From: Johannes Berg <[email protected]>

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.

Signed-off-by: Johannes Berg <[email protected]>
---
 include/net/netlink.h   | 62 +++++++++++++++++++++++++++++++++++++++--
 net/netlink/genetlink.c |  6 ++--
 net/netlink/policy.c    | 57 +++++++++++++++++++++++++++++--------
 3 files changed, 106 insertions(+), 19 deletions(-)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 4be0ad237e57..a929759a03f5 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -1937,12 +1937,68 @@ void nla_get_range_signed(const struct nla_policy *pt,
 
 struct netlink_policy_dump_state;
 
-struct netlink_policy_dump_state *
-netlink_policy_dump_start(const struct nla_policy *policy,
-                         unsigned int maxtype);
+/**
+ * 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);
+
+/**
+ * netlink_policy_dump_get_policy_idx - retrieve policy index
+ * @state: the policy dump state
+ * @policy: the policy to find
+ * @maxattr: 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
 
 #endif
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 537472342781..42777749d4d8 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -1164,10 +1164,8 @@ static int ctrl_dumppolicy_start(struct netlink_callback 
*cb)
        if (!op.policy)
                return -ENODATA;
 
-       ctx->state = netlink_policy_dump_start(op.policy, op.maxattr);
-       if (IS_ERR(ctx->state))
-               return PTR_ERR(ctx->state);
-       return 0;
+       return netlink_policy_dump_add_policy(&ctx->state, op.policy,
+                                             op.maxattr);
 }
 
 static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
diff --git a/net/netlink/policy.c b/net/netlink/policy.c
index 7bc8f81ecc43..d930064cbaf3 100644
--- a/net/netlink/policy.c
+++ b/net/netlink/policy.c
@@ -77,28 +77,41 @@ static unsigned int get_policy_idx(struct 
netlink_policy_dump_state *state,
        return -1;
 }
 
-struct netlink_policy_dump_state *
-netlink_policy_dump_start(const struct nla_policy *policy,
-                         unsigned int maxtype)
+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;
+}
+
+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 (!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 ERR_PTR(-ENOMEM);
-       state->n_alloc = INITIAL_POLICIES_ALLOC;
-
        err = add_policy(&state, policy, maxtype);
        if (err)
-               return ERR_PTR(err);
+               return err;
 
        for (policy_idx = 0;
             policy_idx < state->n_alloc && state->policies[policy_idx].policy;
@@ -118,7 +131,7 @@ netlink_policy_dump_start(const struct nla_policy *policy,
                                                 policy[type].nested_policy,
                                                 policy[type].len);
                                if (err)
-                                       return ERR_PTR(err);
+                                       return err;
                                break;
                        default:
                                break;
@@ -126,7 +139,27 @@ netlink_policy_dump_start(const struct nla_policy *policy,
                }
        }
 
-       return state;
+       *pstate = state;
+       return 0;
+}
+
+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(1);
+       return 0;
 }
 
 static bool
-- 
2.26.2

Reply via email to