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

genetlink: register family ops as array

Instead of using a linked list, use an array. This reduces
the data size needed by the users of genetlink, for example
in wireless (net/wireless/nl80211.c) on 64-bit it frees up
over 1K of data space.

Remove the attempted sending of CTRL_CMD_NEWOPS ctrl event
since genl_ctrl_event(CTRL_CMD_NEWOPS, ...) only returns
-EINVAL anyway, therefore no such event could ever be sent.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3686ec5e
...@@ -39,9 +39,10 @@ struct genl_info; ...@@ -39,9 +39,10 @@ struct genl_info;
* @post_doit: called after an operation's doit callback, it may * @post_doit: called after an operation's doit callback, it may
* undo operations done by pre_doit, for example release locks * undo operations done by pre_doit, for example release locks
* @attrbuf: buffer to store parsed attributes * @attrbuf: buffer to store parsed attributes
* @ops_list: list of all assigned operations
* @family_list: family list * @family_list: family list
* @mcast_groups: multicast groups list * @mcast_groups: multicast groups list
* @ops: the operations supported by this family (private)
* @n_ops: number of operations supported by this family (private)
*/ */
struct genl_family { struct genl_family {
unsigned int id; unsigned int id;
...@@ -58,7 +59,8 @@ struct genl_family { ...@@ -58,7 +59,8 @@ struct genl_family {
struct sk_buff *skb, struct sk_buff *skb,
struct genl_info *info); struct genl_info *info);
struct nlattr ** attrbuf; /* private */ struct nlattr ** attrbuf; /* private */
struct list_head ops_list; /* private */ struct genl_ops * ops; /* private */
unsigned int n_ops; /* private */
struct list_head family_list; /* private */ struct list_head family_list; /* private */
struct list_head mcast_groups; /* private */ struct list_head mcast_groups; /* private */
struct module *module; struct module *module;
...@@ -119,7 +121,6 @@ struct genl_ops { ...@@ -119,7 +121,6 @@ struct genl_ops {
int (*dumpit)(struct sk_buff *skb, int (*dumpit)(struct sk_buff *skb,
struct netlink_callback *cb); struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb); int (*done)(struct netlink_callback *cb);
struct list_head ops_list;
}; };
int __genl_register_family(struct genl_family *family); int __genl_register_family(struct genl_family *family);
......
...@@ -108,11 +108,11 @@ static struct genl_family *genl_family_find_byname(char *name) ...@@ -108,11 +108,11 @@ static struct genl_family *genl_family_find_byname(char *name)
static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
{ {
struct genl_ops *ops; int i;
list_for_each_entry(ops, &family->ops_list, ops_list) for (i = 0; i < family->n_ops; i++)
if (ops->cmd == cmd) if (family->ops[i].cmd == cmd)
return ops; return &family->ops[i];
return NULL; return NULL;
} }
...@@ -283,33 +283,30 @@ static void genl_unregister_mc_groups(struct genl_family *family) ...@@ -283,33 +283,30 @@ static void genl_unregister_mc_groups(struct genl_family *family)
__genl_unregister_mc_group(family, grp); __genl_unregister_mc_group(family, grp);
} }
static int genl_register_ops(struct genl_family *family, struct genl_ops *ops) static int genl_validate_add_ops(struct genl_family *family, struct genl_ops *ops,
unsigned int n_ops)
{ {
int err = -EINVAL; int i, j;
if (ops->dumpit == NULL && ops->doit == NULL) for (i = 0; i < n_ops; i++) {
goto errout; if (ops[i].dumpit == NULL && ops[i].doit == NULL)
return -EINVAL;
if (genl_get_cmd(ops->cmd, family)) { for (j = i + 1; j < n_ops; j++)
err = -EEXIST; if (ops[i].cmd == ops[j].cmd)
goto errout; return -EINVAL;
if (ops[i].dumpit)
ops[i].flags |= GENL_CMD_CAP_DUMP;
if (ops[i].doit)
ops[i].flags |= GENL_CMD_CAP_DO;
if (ops[i].policy)
ops[i].flags |= GENL_CMD_CAP_HASPOL;
} }
if (ops->dumpit) /* family is not registered yet, so no locking needed */
ops->flags |= GENL_CMD_CAP_DUMP; family->ops = ops;
if (ops->doit) family->n_ops = n_ops;
ops->flags |= GENL_CMD_CAP_DO;
if (ops->policy)
ops->flags |= GENL_CMD_CAP_HASPOL;
genl_lock_all();
list_add_tail(&ops->ops_list, &family->ops_list);
genl_unlock_all();
genl_ctrl_event(CTRL_CMD_NEWOPS, ops); return 0;
err = 0;
errout:
return err;
} }
/** /**
...@@ -333,7 +330,6 @@ int __genl_register_family(struct genl_family *family) ...@@ -333,7 +330,6 @@ int __genl_register_family(struct genl_family *family)
if (family->id > GENL_MAX_ID) if (family->id > GENL_MAX_ID)
goto errout; goto errout;
INIT_LIST_HEAD(&family->ops_list);
INIT_LIST_HEAD(&family->mcast_groups); INIT_LIST_HEAD(&family->mcast_groups);
genl_lock_all(); genl_lock_all();
...@@ -405,21 +401,13 @@ EXPORT_SYMBOL(__genl_register_family); ...@@ -405,21 +401,13 @@ EXPORT_SYMBOL(__genl_register_family);
int __genl_register_family_with_ops(struct genl_family *family, int __genl_register_family_with_ops(struct genl_family *family,
struct genl_ops *ops, size_t n_ops) struct genl_ops *ops, size_t n_ops)
{ {
int err, i; int err;
err = __genl_register_family(family); err = genl_validate_add_ops(family, ops, n_ops);
if (err) if (err)
return err; return err;
for (i = 0; i < n_ops; ++i, ++ops) { return __genl_register_family(family);
err = genl_register_ops(family, ops);
if (err)
goto err_out;
}
return 0;
err_out:
genl_unregister_family(family);
return err;
} }
EXPORT_SYMBOL(__genl_register_family_with_ops); EXPORT_SYMBOL(__genl_register_family_with_ops);
...@@ -444,7 +432,7 @@ int genl_unregister_family(struct genl_family *family) ...@@ -444,7 +432,7 @@ int genl_unregister_family(struct genl_family *family)
continue; continue;
list_del(&rc->family_list); list_del(&rc->family_list);
INIT_LIST_HEAD(&family->ops_list); family->n_ops = 0;
genl_unlock_all(); genl_unlock_all();
kfree(family->attrbuf); kfree(family->attrbuf);
...@@ -671,19 +659,19 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq, ...@@ -671,19 +659,19 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,
nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))
goto nla_put_failure; goto nla_put_failure;
if (!list_empty(&family->ops_list)) { if (family->n_ops) {
struct nlattr *nla_ops; struct nlattr *nla_ops;
struct genl_ops *ops; int i;
int idx = 1;
nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS); nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
if (nla_ops == NULL) if (nla_ops == NULL)
goto nla_put_failure; goto nla_put_failure;
list_for_each_entry(ops, &family->ops_list, ops_list) { for (i = 0; i < family->n_ops; i++) {
struct nlattr *nest; struct nlattr *nest;
struct genl_ops *ops = &family->ops[i];
nest = nla_nest_start(skb, idx++); nest = nla_nest_start(skb, i + 1);
if (nest == NULL) if (nest == NULL)
goto nla_put_failure; goto nla_put_failure;
......
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