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:
Родитель
899b07c578
Коммит
04a351a62b
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
const struct nla_policy *policy,
|
||||
unsigned int maxtype)
|
||||
/**
|
||||
* 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,8 +240,9 @@ send_attribute:
|
|||
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,
|
||||
pt->len)) ||
|
||||
netlink_policy_dump_get_policy_idx(state,
|
||||
pt->nested_policy,
|
||||
pt->len)) ||
|
||||
nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
|
||||
pt->len)))
|
||||
goto nla_put_failure;
|
||||
|
@ -309,6 +365,12 @@ nla_put_failure:
|
|||
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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче