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: Johannes Berg <johannes.berg@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
899b07c578
commit
04a351a62b
|
@ -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)
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*statep = state;
|
*pstate = state;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 @@ send_attribute:
|
||||||
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 @@ nla_put_failure:
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue