aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-11-14 11:14:44 -0500
committerDavid S. Miller <davem@davemloft.net>2013-11-14 17:10:41 -0500
commitd91824c08fbcb265ec930d863fa905e8daa836a4 (patch)
tree2f3c2f27b68f46e1dc5e1fc3139cd52942f818b2
parent3686ec5e84977eddc796903177e7e0a122585c11 (diff)
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: Johannes Berg <johannes.berg@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/genetlink.h7
-rw-r--r--net/netlink/genetlink.c78
2 files changed, 37 insertions, 48 deletions
diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index 617d718524b0..d4802af1a8b3 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -39,9 +39,10 @@ struct genl_info;
39 * @post_doit: called after an operation's doit callback, it may 39 * @post_doit: called after an operation's doit callback, it may
40 * undo operations done by pre_doit, for example release locks 40 * undo operations done by pre_doit, for example release locks
41 * @attrbuf: buffer to store parsed attributes 41 * @attrbuf: buffer to store parsed attributes
42 * @ops_list: list of all assigned operations
43 * @family_list: family list 42 * @family_list: family list
44 * @mcast_groups: multicast groups list 43 * @mcast_groups: multicast groups list
44 * @ops: the operations supported by this family (private)
45 * @n_ops: number of operations supported by this family (private)
45 */ 46 */
46struct genl_family { 47struct genl_family {
47 unsigned int id; 48 unsigned int id;
@@ -58,7 +59,8 @@ struct genl_family {
58 struct sk_buff *skb, 59 struct sk_buff *skb,
59 struct genl_info *info); 60 struct genl_info *info);
60 struct nlattr ** attrbuf; /* private */ 61 struct nlattr ** attrbuf; /* private */
61 struct list_head ops_list; /* private */ 62 struct genl_ops * ops; /* private */
63 unsigned int n_ops; /* private */
62 struct list_head family_list; /* private */ 64 struct list_head family_list; /* private */
63 struct list_head mcast_groups; /* private */ 65 struct list_head mcast_groups; /* private */
64 struct module *module; 66 struct module *module;
@@ -119,7 +121,6 @@ struct genl_ops {
119 int (*dumpit)(struct sk_buff *skb, 121 int (*dumpit)(struct sk_buff *skb,
120 struct netlink_callback *cb); 122 struct netlink_callback *cb);
121 int (*done)(struct netlink_callback *cb); 123 int (*done)(struct netlink_callback *cb);
122 struct list_head ops_list;
123}; 124};
124 125
125int __genl_register_family(struct genl_family *family); 126int __genl_register_family(struct genl_family *family);
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index fbccb45e8cc1..d8273b057763 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -108,11 +108,11 @@ static struct genl_family *genl_family_find_byname(char *name)
108 108
109static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family) 109static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
110{ 110{
111 struct genl_ops *ops; 111 int i;
112 112
113 list_for_each_entry(ops, &family->ops_list, ops_list) 113 for (i = 0; i < family->n_ops; i++)
114 if (ops->cmd == cmd) 114 if (family->ops[i].cmd == cmd)
115 return ops; 115 return &family->ops[i];
116 116
117 return NULL; 117 return NULL;
118} 118}
@@ -283,33 +283,30 @@ static void genl_unregister_mc_groups(struct genl_family *family)
283 __genl_unregister_mc_group(family, grp); 283 __genl_unregister_mc_group(family, grp);
284} 284}
285 285
286static int genl_register_ops(struct genl_family *family, struct genl_ops *ops) 286static int genl_validate_add_ops(struct genl_family *family, struct genl_ops *ops,
287 unsigned int n_ops)
287{ 288{
288 int err = -EINVAL; 289 int i, j;
289 290
290 if (ops->dumpit == NULL && ops->doit == NULL) 291 for (i = 0; i < n_ops; i++) {
291 goto errout; 292 if (ops[i].dumpit == NULL && ops[i].doit == NULL)
292 293 return -EINVAL;
293 if (genl_get_cmd(ops->cmd, family)) { 294 for (j = i + 1; j < n_ops; j++)
294 err = -EEXIST; 295 if (ops[i].cmd == ops[j].cmd)
295 goto errout; 296 return -EINVAL;
297 if (ops[i].dumpit)
298 ops[i].flags |= GENL_CMD_CAP_DUMP;
299 if (ops[i].doit)
300 ops[i].flags |= GENL_CMD_CAP_DO;
301 if (ops[i].policy)
302 ops[i].flags |= GENL_CMD_CAP_HASPOL;
296 } 303 }
297 304
298 if (ops->dumpit) 305 /* family is not registered yet, so no locking needed */
299 ops->flags |= GENL_CMD_CAP_DUMP; 306 family->ops = ops;
300 if (ops->doit) 307 family->n_ops = n_ops;
301 ops->flags |= GENL_CMD_CAP_DO;
302 if (ops->policy)
303 ops->flags |= GENL_CMD_CAP_HASPOL;
304
305 genl_lock_all();
306 list_add_tail(&ops->ops_list, &family->ops_list);
307 genl_unlock_all();
308 308
309 genl_ctrl_event(CTRL_CMD_NEWOPS, ops); 309 return 0;
310 err = 0;
311errout:
312 return err;
313} 310}
314 311
315/** 312/**
@@ -333,7 +330,6 @@ int __genl_register_family(struct genl_family *family)
333 if (family->id > GENL_MAX_ID) 330 if (family->id > GENL_MAX_ID)
334 goto errout; 331 goto errout;
335 332
336 INIT_LIST_HEAD(&family->ops_list);
337 INIT_LIST_HEAD(&family->mcast_groups); 333 INIT_LIST_HEAD(&family->mcast_groups);
338 334
339 genl_lock_all(); 335 genl_lock_all();
@@ -405,21 +401,13 @@ EXPORT_SYMBOL(__genl_register_family);
405int __genl_register_family_with_ops(struct genl_family *family, 401int __genl_register_family_with_ops(struct genl_family *family,
406 struct genl_ops *ops, size_t n_ops) 402 struct genl_ops *ops, size_t n_ops)
407{ 403{
408 int err, i; 404 int err;
409 405
410 err = __genl_register_family(family); 406 err = genl_validate_add_ops(family, ops, n_ops);
411 if (err) 407 if (err)
412 return err; 408 return err;
413 409
414 for (i = 0; i < n_ops; ++i, ++ops) { 410 return __genl_register_family(family);
415 err = genl_register_ops(family, ops);
416 if (err)
417 goto err_out;
418 }
419 return 0;
420err_out:
421 genl_unregister_family(family);
422 return err;
423} 411}
424EXPORT_SYMBOL(__genl_register_family_with_ops); 412EXPORT_SYMBOL(__genl_register_family_with_ops);
425 413
@@ -444,7 +432,7 @@ int genl_unregister_family(struct genl_family *family)
444 continue; 432 continue;
445 433
446 list_del(&rc->family_list); 434 list_del(&rc->family_list);
447 INIT_LIST_HEAD(&family->ops_list); 435 family->n_ops = 0;
448 genl_unlock_all(); 436 genl_unlock_all();
449 437
450 kfree(family->attrbuf); 438 kfree(family->attrbuf);
@@ -671,19 +659,19 @@ static int ctrl_fill_info(struct genl_family *family, u32 portid, u32 seq,
671 nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) 659 nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr))
672 goto nla_put_failure; 660 goto nla_put_failure;
673 661
674 if (!list_empty(&family->ops_list)) { 662 if (family->n_ops) {
675 struct nlattr *nla_ops; 663 struct nlattr *nla_ops;
676 struct genl_ops *ops; 664 int i;
677 int idx = 1;
678 665
679 nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS); 666 nla_ops = nla_nest_start(skb, CTRL_ATTR_OPS);
680 if (nla_ops == NULL) 667 if (nla_ops == NULL)
681 goto nla_put_failure; 668 goto nla_put_failure;
682 669
683 list_for_each_entry(ops, &family->ops_list, ops_list) { 670 for (i = 0; i < family->n_ops; i++) {
684 struct nlattr *nest; 671 struct nlattr *nest;
672 struct genl_ops *ops = &family->ops[i];
685 673
686 nest = nla_nest_start(skb, idx++); 674 nest = nla_nest_start(skb, i + 1);
687 if (nest == NULL) 675 if (nest == NULL)
688 goto nla_put_failure; 676 goto nla_put_failure;
689 677