Commit 04a351a6 authored by Johannes Berg's avatar Johannes Berg Committed by David S. Miller
Browse files

netlink: rework policy dump to support multiple policies



Rework the policy dump code a bit to support adding multiple
policies to a single dump, in order to e.g. support per-op
policies in generic netlink.

v2:
 - move kernel-doc to implementation [Jakub]
 - squash the first patch to not flip-flop on the prototype
   [Jakub]
 - merge netlink_policy_dump_get_policy_idx() with the old
   get_policy_idx() we already had
 - rebase without Jakub's patch to have per-op dump

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 899b07c5
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -1937,9 +1937,12 @@ void nla_get_range_signed(const struct nla_policy *pt,

struct netlink_policy_dump_state;

int netlink_policy_dump_start(const struct nla_policy *policy,
			      unsigned int maxtype,
			      struct netlink_policy_dump_state **state);
int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
				   const struct nla_policy *policy,
				   unsigned int maxtype);
int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
				       const struct nla_policy *policy,
				       unsigned int maxtype);
bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state);
int netlink_policy_dump_write(struct sk_buff *skb,
			      struct netlink_policy_dump_state *state);
+2 −1
Original line number Diff line number Diff line
@@ -1150,7 +1150,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb)
	if (!rt->policy)
		return -ENODATA;

	return netlink_policy_dump_start(rt->policy, rt->maxattr, &ctx->state);
	return netlink_policy_dump_add_policy(&ctx->state, rt->policy,
					      rt->maxattr);
}

static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
+82 −20
Original line number Diff line number Diff line
@@ -63,44 +63,85 @@ static int add_policy(struct netlink_policy_dump_state **statep,
	return 0;
}

static unsigned int get_policy_idx(struct netlink_policy_dump_state *state,
/**
 * netlink_policy_dump_get_policy_idx - retrieve policy index
 * @state: the policy dump state
 * @policy: the policy to find
 * @maxtype: the policy's maxattr
 *
 * Returns: the index of the given policy in the dump state
 *
 * Call this to find a policy index when you've added multiple and e.g.
 * need to tell userspace which command has which policy (by index).
 *
 * Note: this will WARN and return 0 if the policy isn't found, which
 *	 means it wasn't added in the first place, which would be an
 *	 internal consistency bug.
 */
int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
				       const struct nla_policy *policy,
				       unsigned int maxtype)
{
	unsigned int i;

	if (WARN_ON(!policy || !maxtype))
                return 0;

	for (i = 0; i < state->n_alloc; i++) {
		if (state->policies[i].policy == policy &&
		    state->policies[i].maxtype == maxtype)
			return i;
	}

	WARN_ON_ONCE(1);
	return -1;
	WARN_ON(1);
	return 0;
}

int netlink_policy_dump_start(const struct nla_policy *policy,
			      unsigned int maxtype,
			      struct netlink_policy_dump_state **statep)
static struct netlink_policy_dump_state *alloc_state(void)
{
	struct netlink_policy_dump_state *state;

	state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
			GFP_KERNEL);
	if (!state)
		return ERR_PTR(-ENOMEM);
	state->n_alloc = INITIAL_POLICIES_ALLOC;

	return state;
}

/**
 * netlink_policy_dump_add_policy - add a policy to the dump
 * @pstate: state to add to, may be reallocated, must be %NULL the first time
 * @policy: the new policy to add to the dump
 * @maxtype: the new policy's max attr type
 *
 * Returns: 0 on success, a negative error code otherwise.
 *
 * Call this to allocate a policy dump state, and to add policies to it. This
 * should be called from the dump start() callback.
 *
 * Note: on failures, any previously allocated state is freed.
 */
int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
				   const struct nla_policy *policy,
				   unsigned int maxtype)
{
	struct netlink_policy_dump_state *state = *pstate;
	unsigned int policy_idx;
	int err;

	if (*statep)
		return 0;
	if (!state) {
		state = alloc_state();
		if (IS_ERR(state))
			return PTR_ERR(state);
	}

	/*
	 * walk the policies and nested ones first, and build
	 * a linear list of them.
	 */

	state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
			GFP_KERNEL);
	if (!state)
		return -ENOMEM;
	state->n_alloc = INITIAL_POLICIES_ALLOC;

	err = add_policy(&state, policy, maxtype);
	if (err)
		return err;
@@ -131,8 +172,7 @@ int netlink_policy_dump_start(const struct nla_policy *policy,
		}
	}

	*statep = state;

	*pstate = state;
	return 0;
}

@@ -143,11 +183,26 @@ netlink_policy_dump_finished(struct netlink_policy_dump_state *state)
	       !state->policies[state->policy_idx].policy;
}

/**
 * netlink_policy_dump_loop - dumping loop indicator
 * @state: the policy dump state
 *
 * Returns: %true if the dump continues, %false otherwise
 *
 * Note: this frees the dump state when finishing
 */
bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
{
	return !netlink_policy_dump_finished(state);
}

/**
 * netlink_policy_dump_write - write current policy dump attributes
 * @skb: the message skb to write to
 * @state: the policy dump state
 *
 * Returns: 0 on success, an error code otherwise
 */
int netlink_policy_dump_write(struct sk_buff *skb,
			      struct netlink_policy_dump_state *state)
{
@@ -185,7 +240,8 @@ int netlink_policy_dump_write(struct sk_buff *skb,
			type = NL_ATTR_TYPE_NESTED_ARRAY;
		if (pt->nested_policy && pt->len &&
		    (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
				 get_policy_idx(state, pt->nested_policy,
				 netlink_policy_dump_get_policy_idx(state,
								    pt->nested_policy,
								    pt->len)) ||
		     nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
				 pt->len)))
@@ -309,6 +365,12 @@ int netlink_policy_dump_write(struct sk_buff *skb,
	return -ENOBUFS;
}

/**
 * netlink_policy_dump_free - free policy dump state
 * @state: the policy dump state to free
 *
 * Call this from the done() method to ensure dump state is freed.
 */
void netlink_policy_dump_free(struct netlink_policy_dump_state *state)
{
	kfree(state);