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

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
...@@ -1937,9 +1937,12 @@ void nla_get_range_signed(const struct nla_policy *pt, ...@@ -1937,9 +1937,12 @@ void nla_get_range_signed(const struct nla_policy *pt,
struct netlink_policy_dump_state; struct netlink_policy_dump_state;
int netlink_policy_dump_start(const struct nla_policy *policy, int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
unsigned int maxtype, const struct nla_policy *policy,
struct netlink_policy_dump_state **state); 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); bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state);
int netlink_policy_dump_write(struct sk_buff *skb, int netlink_policy_dump_write(struct sk_buff *skb,
struct netlink_policy_dump_state *state); struct netlink_policy_dump_state *state);
......
...@@ -1150,7 +1150,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) ...@@ -1150,7 +1150,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb)
if (!rt->policy) if (!rt->policy)
return -ENODATA; 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) static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
......
...@@ -63,44 +63,85 @@ static int add_policy(struct netlink_policy_dump_state **statep, ...@@ -63,44 +63,85 @@ static int add_policy(struct netlink_policy_dump_state **statep,
return 0; return 0;
} }
static unsigned int get_policy_idx(struct netlink_policy_dump_state *state, /**
const struct nla_policy *policy, * netlink_policy_dump_get_policy_idx - retrieve policy index
unsigned int maxtype) * @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; unsigned int i;
if (WARN_ON(!policy || !maxtype))
return 0;
for (i = 0; i < state->n_alloc; i++) { for (i = 0; i < state->n_alloc; i++) {
if (state->policies[i].policy == policy && if (state->policies[i].policy == policy &&
state->policies[i].maxtype == maxtype) state->policies[i].maxtype == maxtype)
return i; return i;
} }
WARN_ON_ONCE(1); WARN_ON(1);
return -1; return 0;
} }
int netlink_policy_dump_start(const struct nla_policy *policy, static struct netlink_policy_dump_state *alloc_state(void)
unsigned int maxtype,
struct netlink_policy_dump_state **statep)
{ {
struct netlink_policy_dump_state *state; 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; unsigned int policy_idx;
int err; int err;
if (*statep) if (!state) {
return 0; state = alloc_state();
if (IS_ERR(state))
return PTR_ERR(state);
}
/* /*
* walk the policies and nested ones first, and build * walk the policies and nested ones first, and build
* a linear list of them. * 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); err = add_policy(&state, policy, maxtype);
if (err) if (err)
return err; return err;
...@@ -131,8 +172,7 @@ int netlink_policy_dump_start(const struct nla_policy *policy, ...@@ -131,8 +172,7 @@ int netlink_policy_dump_start(const struct nla_policy *policy,
} }
} }
*statep = state; *pstate = state;
return 0; return 0;
} }
...@@ -143,11 +183,26 @@ netlink_policy_dump_finished(struct netlink_policy_dump_state *state) ...@@ -143,11 +183,26 @@ netlink_policy_dump_finished(struct netlink_policy_dump_state *state)
!state->policies[state->policy_idx].policy; !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) bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
{ {
return !netlink_policy_dump_finished(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, int netlink_policy_dump_write(struct sk_buff *skb,
struct netlink_policy_dump_state *state) struct netlink_policy_dump_state *state)
{ {
...@@ -185,8 +240,9 @@ int netlink_policy_dump_write(struct sk_buff *skb, ...@@ -185,8 +240,9 @@ int netlink_policy_dump_write(struct sk_buff *skb,
type = NL_ATTR_TYPE_NESTED_ARRAY; type = NL_ATTR_TYPE_NESTED_ARRAY;
if (pt->nested_policy && pt->len && if (pt->nested_policy && pt->len &&
(nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, (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->len)) || pt->nested_policy,
pt->len)) ||
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
pt->len))) pt->len)))
goto nla_put_failure; goto nla_put_failure;
...@@ -309,6 +365,12 @@ int netlink_policy_dump_write(struct sk_buff *skb, ...@@ -309,6 +365,12 @@ int netlink_policy_dump_write(struct sk_buff *skb,
return -ENOBUFS; 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) void netlink_policy_dump_free(struct netlink_policy_dump_state *state)
{ {
kfree(state); kfree(state);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment