netlink: rework policy dump to support multiple policies
[sfrench/cifs-2.6.git] / net / netlink / policy.c
index 753b265acec5744022aa9956585feae495a34cc4..cf23c015172190c2b704a10260ba6348656ad54f 100644 (file)
@@ -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);