X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fsched%2Fcls_api.c;h=fe85d5588b46eaabea6a22d8a01568e93f6ff2c3;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=71c85ad84ec82db8d275e8e1aef516cab9da0930;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 71c85ad84..fe85d5588 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include #include @@ -36,6 +36,7 @@ #include #include #include +#include #if 0 /* control */ #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) @@ -48,19 +49,22 @@ static struct tcf_proto_ops *tcf_proto_base; /* Protects list of registered TC modules. It is pure SMP lock. */ -static rwlock_t cls_mod_lock = RW_LOCK_UNLOCKED; +static DEFINE_RWLOCK(cls_mod_lock); /* Find classifier type by string name */ -struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind) +static struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind) { struct tcf_proto_ops *t = NULL; if (kind) { read_lock(&cls_mod_lock); for (t = tcf_proto_base; t; t = t->next) { - if (rtattr_strcmp(kind, t->kind) == 0) + if (rtattr_strcmp(kind, t->kind) == 0) { + if (!try_module_get(t->owner)) + t = NULL; break; + } } read_unlock(&cls_mod_lock); } @@ -126,22 +130,31 @@ static __inline__ u32 tcf_auto_prio(struct tcf_proto *tp) static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { - struct rtattr **tca = arg; - struct tcmsg *t = NLMSG_DATA(n); - u32 protocol = TC_H_MIN(t->tcm_info); - u32 prio = TC_H_MAJ(t->tcm_info); - u32 nprio = prio; - u32 parent = t->tcm_parent; + struct rtattr **tca; + struct tcmsg *t; + u32 protocol; + u32 prio; + u32 nprio; + u32 parent; struct net_device *dev; struct Qdisc *q; struct tcf_proto **back, **chain; - struct tcf_proto *tp = NULL; + struct tcf_proto *tp; struct tcf_proto_ops *tp_ops; struct Qdisc_class_ops *cops; - unsigned long cl = 0; + unsigned long cl; unsigned long fh; int err; +replay: + tca = arg; + t = NLMSG_DATA(n); + protocol = TC_H_MIN(t->tcm_info); + prio = TC_H_MAJ(t->tcm_info); + nprio = prio; + parent = t->tcm_parent; + cl = 0; + if (prio == 0) { /* If no priority is given, user wants we allocated it. */ if (n->nlmsg_type != RTM_NEWTFILTER || !(n->nlmsg_flags&NLM_F_CREATE)) @@ -207,19 +220,31 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg) err = -ENOBUFS; if ((tp = kmalloc(sizeof(*tp), GFP_KERNEL)) == NULL) goto errout; + err = -EINVAL; tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND-1]); + if (tp_ops == NULL) { #ifdef CONFIG_KMOD - if (tp_ops==NULL && tca[TCA_KIND-1] != NULL) { struct rtattr *kind = tca[TCA_KIND-1]; + char name[IFNAMSIZ]; - if (RTA_PAYLOAD(kind) <= IFNAMSIZ) { - request_module("cls_%s", (char*)RTA_DATA(kind)); + if (kind != NULL && + rtattr_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) { + rtnl_unlock(); + request_module("cls_%s", name); + rtnl_lock(); tp_ops = tcf_proto_lookup_ops(kind); + /* We dropped the RTNL semaphore in order to + * perform the module load. So, even if we + * succeeded in loading the module we have to + * replay the request. We indicate this using + * -EAGAIN. + */ + if (tp_ops != NULL) { + module_put(tp_ops->owner); + err = -EAGAIN; + } } - } #endif - if (tp_ops == NULL) { - err = -EINVAL; kfree(tp); goto errout; } @@ -230,11 +255,6 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg) tp->q = q; tp->classify = tp_ops->classify; tp->classid = parent; - err = -EBUSY; - if (!try_module_get(tp_ops->owner)) { - kfree(tp); - goto errout; - } if ((err = tp_ops->init(tp)) != 0) { module_put(tp_ops->owner); kfree(tp); @@ -294,22 +314,12 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg) errout: if (cl) cops->put(q, cl); + if (err == -EAGAIN) + /* Replay the request. */ + goto replay; return err; } -unsigned long tcf_set_class(struct tcf_proto *tp, unsigned long *clp, - unsigned long cl) -{ - unsigned long old_cl; - - tcf_tree_lock(tp); - old_cl = __cls_set_class(clp, cl); - tcf_tree_unlock(tp); - - return old_cl; -} - - static int tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, unsigned long fh, u32 pid, u32 seq, unsigned flags, int event) @@ -453,6 +463,156 @@ out: return skb->len; } +void +tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) +{ +#ifdef CONFIG_NET_CLS_ACT + if (exts->action) { + tcf_action_destroy(exts->action, TCA_ACT_UNBIND); + exts->action = NULL; + } +#elif defined CONFIG_NET_CLS_POLICE + if (exts->police) { + tcf_police_release(exts->police, TCA_ACT_UNBIND); + exts->police = NULL; + } +#endif +} + + +int +tcf_exts_validate(struct tcf_proto *tp, struct rtattr **tb, + struct rtattr *rate_tlv, struct tcf_exts *exts, + struct tcf_ext_map *map) +{ + memset(exts, 0, sizeof(*exts)); + +#ifdef CONFIG_NET_CLS_ACT + { + int err; + struct tc_action *act; + + if (map->police && tb[map->police-1]) { + act = tcf_action_init_1(tb[map->police-1], rate_tlv, "police", + TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err); + if (act == NULL) + return err; + + act->type = TCA_OLD_COMPAT; + exts->action = act; + } else if (map->action && tb[map->action-1]) { + act = tcf_action_init(tb[map->action-1], rate_tlv, NULL, + TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err); + if (act == NULL) + return err; + + exts->action = act; + } + } +#elif defined CONFIG_NET_CLS_POLICE + if (map->police && tb[map->police-1]) { + struct tcf_police *p; + + p = tcf_police_locate(tb[map->police-1], rate_tlv); + if (p == NULL) + return -EINVAL; + + exts->police = p; + } else if (map->action && tb[map->action-1]) + return -EOPNOTSUPP; +#else + if ((map->action && tb[map->action-1]) || + (map->police && tb[map->police-1])) + return -EOPNOTSUPP; +#endif + + return 0; +} + +void +tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, + struct tcf_exts *src) +{ +#ifdef CONFIG_NET_CLS_ACT + if (src->action) { + struct tc_action *act; + tcf_tree_lock(tp); + act = xchg(&dst->action, src->action); + tcf_tree_unlock(tp); + if (act) + tcf_action_destroy(act, TCA_ACT_UNBIND); + } +#elif defined CONFIG_NET_CLS_POLICE + if (src->police) { + struct tcf_police *p; + tcf_tree_lock(tp); + p = xchg(&dst->police, src->police); + tcf_tree_unlock(tp); + if (p) + tcf_police_release(p, TCA_ACT_UNBIND); + } +#endif +} + +int +tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, + struct tcf_ext_map *map) +{ +#ifdef CONFIG_NET_CLS_ACT + if (map->action && exts->action) { + /* + * again for backward compatible mode - we want + * to work with both old and new modes of entering + * tc data even if iproute2 was newer - jhs + */ + struct rtattr * p_rta = (struct rtattr*) skb->tail; + + if (exts->action->type != TCA_OLD_COMPAT) { + RTA_PUT(skb, map->action, 0, NULL); + if (tcf_action_dump(skb, exts->action, 0, 0) < 0) + goto rtattr_failure; + p_rta->rta_len = skb->tail - (u8*)p_rta; + } else if (map->police) { + RTA_PUT(skb, map->police, 0, NULL); + if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0) + goto rtattr_failure; + p_rta->rta_len = skb->tail - (u8*)p_rta; + } + } +#elif defined CONFIG_NET_CLS_POLICE + if (map->police && exts->police) { + struct rtattr * p_rta = (struct rtattr*) skb->tail; + + RTA_PUT(skb, map->police, 0, NULL); + + if (tcf_police_dump(skb, exts->police) < 0) + goto rtattr_failure; + + p_rta->rta_len = skb->tail - (u8*)p_rta; + } +#endif + return 0; +rtattr_failure: __attribute__ ((unused)) + return -1; +} + +int +tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, + struct tcf_ext_map *map) +{ +#ifdef CONFIG_NET_CLS_ACT + if (exts->action) + if (tcf_action_copy_stats(skb, exts->action) < 0) + goto rtattr_failure; +#elif defined CONFIG_NET_CLS_POLICE + if (exts->police) + if (tcf_police_dump_stats(skb, exts->police) < 0) + goto rtattr_failure; +#endif + return 0; +rtattr_failure: __attribute__ ((unused)) + return -1; +} static int __init tc_filter_init(void) { @@ -475,4 +635,8 @@ subsys_initcall(tc_filter_init); EXPORT_SYMBOL(register_tcf_proto_ops); EXPORT_SYMBOL(unregister_tcf_proto_ops); -EXPORT_SYMBOL(tcf_set_class); +EXPORT_SYMBOL(tcf_exts_validate); +EXPORT_SYMBOL(tcf_exts_destroy); +EXPORT_SYMBOL(tcf_exts_change); +EXPORT_SYMBOL(tcf_exts_dump); +EXPORT_SYMBOL(tcf_exts_dump_stats);