diff options
Diffstat (limited to 'net/sched/act_api.c')
-rw-r--r-- | net/sched/act_api.c | 321 |
1 files changed, 183 insertions, 138 deletions
diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f2e9ed34a963..a306974e2fb4 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -70,11 +70,11 @@ static void free_tcf(struct rcu_head *head) kfree(p); } -static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *p) +static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p) { - spin_lock_bh(&hinfo->lock); - hlist_del(&p->tcfa_head); - spin_unlock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); + idr_remove_ext(&idrinfo->action_idr, p->tcfa_index); + spin_unlock_bh(&idrinfo->lock); gen_kill_estimator(&p->tcfa_rate_est); /* * gen_estimator est_timer() might access p->tcfa_lock @@ -83,7 +83,7 @@ static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *p) call_rcu(&p->tcfa_rcu, free_tcf); } -int __tcf_hash_release(struct tc_action *p, bool bind, bool strict) +int __tcf_idr_release(struct tc_action *p, bool bind, bool strict) { int ret = 0; @@ -97,55 +97,64 @@ int __tcf_hash_release(struct tc_action *p, bool bind, bool strict) if (p->tcfa_bindcnt <= 0 && p->tcfa_refcnt <= 0) { if (p->ops->cleanup) p->ops->cleanup(p, bind); - tcf_hash_destroy(p->hinfo, p); + tcf_idr_remove(p->idrinfo, p); ret = ACT_P_DELETED; } } return ret; } -EXPORT_SYMBOL(__tcf_hash_release); +EXPORT_SYMBOL(__tcf_idr_release); -static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, +static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, struct netlink_callback *cb) { - int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; + int err = 0, index = -1, s_i = 0, n_i = 0; + u32 act_flags = cb->args[2]; + unsigned long jiffy_since = cb->args[3]; struct nlattr *nest; + struct idr *idr = &idrinfo->action_idr; + struct tc_action *p; + unsigned long id = 1; - spin_lock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); s_i = cb->args[0]; - for (i = 0; i < (hinfo->hmask + 1); i++) { - struct hlist_head *head; - struct tc_action *p; - - head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; - - hlist_for_each_entry_rcu(p, head, tcfa_head) { - index++; - if (index < s_i) - continue; - - nest = nla_nest_start(skb, n_i); - if (nest == NULL) - goto nla_put_failure; - err = tcf_action_dump_1(skb, p, 0, 0); - if (err < 0) { - index--; - nlmsg_trim(skb, nest); - goto done; - } - nla_nest_end(skb, nest); - n_i++; - if (n_i >= TCA_ACT_MAX_PRIO) - goto done; + idr_for_each_entry_ext(idr, p, id) { + index++; + if (index < s_i) + continue; + + if (jiffy_since && + time_after(jiffy_since, + (unsigned long)p->tcfa_tm.lastuse)) + continue; + + nest = nla_nest_start(skb, n_i); + if (!nest) + goto nla_put_failure; + err = tcf_action_dump_1(skb, p, 0, 0); + if (err < 0) { + index--; + nlmsg_trim(skb, nest); + goto done; } + nla_nest_end(skb, nest); + n_i++; + if (!(act_flags & TCA_FLAG_LARGE_DUMP_ON) && + n_i >= TCA_ACT_MAX_PRIO) + goto done; } done: - spin_unlock_bh(&hinfo->lock); - if (n_i) - cb->args[0] += n_i; + if (index >= 0) + cb->args[0] = index + 1; + + spin_unlock_bh(&idrinfo->lock); + if (n_i) { + if (act_flags & TCA_FLAG_LARGE_DUMP_ON) + cb->args[1] = n_i; + } return n_i; nla_put_failure: @@ -153,31 +162,29 @@ nla_put_failure: goto done; } -static int tcf_del_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, +static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, const struct tc_action_ops *ops) { struct nlattr *nest; - int i = 0, n_i = 0; + int n_i = 0; int ret = -EINVAL; + struct idr *idr = &idrinfo->action_idr; + struct tc_action *p; + unsigned long id = 1; nest = nla_nest_start(skb, 0); if (nest == NULL) goto nla_put_failure; if (nla_put_string(skb, TCA_KIND, ops->kind)) goto nla_put_failure; - for (i = 0; i < (hinfo->hmask + 1); i++) { - struct hlist_head *head; - struct hlist_node *n; - struct tc_action *p; - - head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; - hlist_for_each_entry_safe(p, n, head, tcfa_head) { - ret = __tcf_hash_release(p, false, true); - if (ret == ACT_P_DELETED) { - module_put(p->ops->owner); - n_i++; - } else if (ret < 0) - goto nla_put_failure; + + idr_for_each_entry_ext(idr, p, id) { + ret = __tcf_idr_release(p, false, true); + if (ret == ACT_P_DELETED) { + module_put(p->ops->owner); + n_i++; + } else if (ret < 0) { + goto nla_put_failure; } } if (nla_put_u32(skb, TCA_FCNT, n_i)) @@ -194,12 +201,12 @@ int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb, struct netlink_callback *cb, int type, const struct tc_action_ops *ops) { - struct tcf_hashinfo *hinfo = tn->hinfo; + struct tcf_idrinfo *idrinfo = tn->idrinfo; if (type == RTM_DELACTION) { - return tcf_del_walker(hinfo, skb, ops); + return tcf_del_walker(idrinfo, skb, ops); } else if (type == RTM_GETACTION) { - return tcf_dump_walker(hinfo, skb, cb); + return tcf_dump_walker(idrinfo, skb, cb); } else { WARN(1, "tcf_generic_walker: unknown action %d\n", type); return -EINVAL; @@ -207,40 +214,21 @@ int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb, } EXPORT_SYMBOL(tcf_generic_walker); -static struct tc_action *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) +static struct tc_action *tcf_idr_lookup(u32 index, struct tcf_idrinfo *idrinfo) { struct tc_action *p = NULL; - struct hlist_head *head; - spin_lock_bh(&hinfo->lock); - head = &hinfo->htab[tcf_hash(index, hinfo->hmask)]; - hlist_for_each_entry_rcu(p, head, tcfa_head) - if (p->tcfa_index == index) - break; - spin_unlock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); + p = idr_find_ext(&idrinfo->action_idr, index); + spin_unlock_bh(&idrinfo->lock); return p; } -u32 tcf_hash_new_index(struct tc_action_net *tn) -{ - struct tcf_hashinfo *hinfo = tn->hinfo; - u32 val = hinfo->index; - - do { - if (++val == 0) - val = 1; - } while (tcf_hash_lookup(val, hinfo)); - - hinfo->index = val; - return val; -} -EXPORT_SYMBOL(tcf_hash_new_index); - -int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index) +int tcf_idr_search(struct tc_action_net *tn, struct tc_action **a, u32 index) { - struct tcf_hashinfo *hinfo = tn->hinfo; - struct tc_action *p = tcf_hash_lookup(index, hinfo); + struct tcf_idrinfo *idrinfo = tn->idrinfo; + struct tc_action *p = tcf_idr_lookup(index, idrinfo); if (p) { *a = p; @@ -248,15 +236,15 @@ int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index) } return 0; } -EXPORT_SYMBOL(tcf_hash_search); +EXPORT_SYMBOL(tcf_idr_search); -bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a, - int bind) +bool tcf_idr_check(struct tc_action_net *tn, u32 index, struct tc_action **a, + int bind) { - struct tcf_hashinfo *hinfo = tn->hinfo; - struct tc_action *p = NULL; + struct tcf_idrinfo *idrinfo = tn->idrinfo; + struct tc_action *p = tcf_idr_lookup(index, idrinfo); - if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) { + if (index && p) { if (bind) p->tcfa_bindcnt++; p->tcfa_refcnt++; @@ -265,23 +253,25 @@ bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a, } return false; } -EXPORT_SYMBOL(tcf_hash_check); +EXPORT_SYMBOL(tcf_idr_check); -void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est) +void tcf_idr_cleanup(struct tc_action *a, struct nlattr *est) { if (est) gen_kill_estimator(&a->tcfa_rate_est); call_rcu(&a->tcfa_rcu, free_tcf); } -EXPORT_SYMBOL(tcf_hash_cleanup); +EXPORT_SYMBOL(tcf_idr_cleanup); -int tcf_hash_create(struct tc_action_net *tn, u32 index, struct nlattr *est, - struct tc_action **a, const struct tc_action_ops *ops, - int bind, bool cpustats) +int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est, + struct tc_action **a, const struct tc_action_ops *ops, + int bind, bool cpustats) { struct tc_action *p = kzalloc(ops->size, GFP_KERNEL); - struct tcf_hashinfo *hinfo = tn->hinfo; + struct tcf_idrinfo *idrinfo = tn->idrinfo; + struct idr *idr = &idrinfo->action_idr; int err = -ENOMEM; + unsigned long idr_index; if (unlikely(!p)) return -ENOMEM; @@ -304,8 +294,32 @@ err2: } } spin_lock_init(&p->tcfa_lock); - INIT_HLIST_NODE(&p->tcfa_head); - p->tcfa_index = index ? index : tcf_hash_new_index(tn); + /* user doesn't specify an index */ + if (!index) { + idr_preload(GFP_KERNEL); + spin_lock_bh(&idrinfo->lock); + err = idr_alloc_ext(idr, NULL, &idr_index, 1, 0, + GFP_ATOMIC); + spin_unlock_bh(&idrinfo->lock); + idr_preload_end(); + if (err) { +err3: + free_percpu(p->cpu_qstats); + goto err2; + } + p->tcfa_index = idr_index; + } else { + idr_preload(GFP_KERNEL); + spin_lock_bh(&idrinfo->lock); + err = idr_alloc_ext(idr, NULL, NULL, index, index + 1, + GFP_ATOMIC); + spin_unlock_bh(&idrinfo->lock); + idr_preload_end(); + if (err) + goto err3; + p->tcfa_index = index; + } + p->tcfa_tm.install = jiffies; p->tcfa_tm.lastuse = jiffies; p->tcfa_tm.firstuse = 0; @@ -314,52 +328,46 @@ err2: &p->tcfa_rate_est, &p->tcfa_lock, NULL, est); if (err) { - free_percpu(p->cpu_qstats); - goto err2; + goto err3; } } - p->hinfo = hinfo; + p->idrinfo = idrinfo; p->ops = ops; INIT_LIST_HEAD(&p->list); *a = p; return 0; } -EXPORT_SYMBOL(tcf_hash_create); +EXPORT_SYMBOL(tcf_idr_create); -void tcf_hash_insert(struct tc_action_net *tn, struct tc_action *a) +void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a) { - struct tcf_hashinfo *hinfo = tn->hinfo; - unsigned int h = tcf_hash(a->tcfa_index, hinfo->hmask); + struct tcf_idrinfo *idrinfo = tn->idrinfo; - spin_lock_bh(&hinfo->lock); - hlist_add_head(&a->tcfa_head, &hinfo->htab[h]); - spin_unlock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); + idr_replace_ext(&idrinfo->action_idr, a, a->tcfa_index); + spin_unlock_bh(&idrinfo->lock); } -EXPORT_SYMBOL(tcf_hash_insert); +EXPORT_SYMBOL(tcf_idr_insert); -void tcf_hashinfo_destroy(const struct tc_action_ops *ops, - struct tcf_hashinfo *hinfo) +void tcf_idrinfo_destroy(const struct tc_action_ops *ops, + struct tcf_idrinfo *idrinfo) { - int i; - - for (i = 0; i < hinfo->hmask + 1; i++) { - struct tc_action *p; - struct hlist_node *n; - - hlist_for_each_entry_safe(p, n, &hinfo->htab[i], tcfa_head) { - int ret; + struct idr *idr = &idrinfo->action_idr; + struct tc_action *p; + int ret; + unsigned long id = 1; - ret = __tcf_hash_release(p, false, true); - if (ret == ACT_P_DELETED) - module_put(ops->owner); - else if (ret < 0) - return; - } + idr_for_each_entry_ext(idr, p, id) { + ret = __tcf_idr_release(p, false, true); + if (ret == ACT_P_DELETED) + module_put(ops->owner); + else if (ret < 0) + return; } - kfree(hinfo->htab); + idr_destroy(&idrinfo->action_idr); } -EXPORT_SYMBOL(tcf_hashinfo_destroy); +EXPORT_SYMBOL(tcf_idrinfo_destroy); static LIST_HEAD(act_base); static DEFINE_RWLOCK(act_mod_lock); @@ -460,9 +468,10 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind) int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, int nr_actions, struct tcf_result *res) { - int ret = -1, i; u32 jmp_prgcnt = 0; u32 jmp_ttl = TCA_ACT_MAX_PRIO; /*matches actions per filter */ + int i; + int ret = TC_ACT_OK; if (skb_skip_tc_classify(skb)) return TC_ACT_OK; @@ -510,7 +519,7 @@ int tcf_action_destroy(struct list_head *actions, int bind) int ret = 0; list_for_each_entry_safe(a, tmp, actions, list) { - ret = __tcf_hash_release(a, bind, true); + ret = __tcf_idr_release(a, bind, true); if (ret == ACT_P_DELETED) module_put(a->ops->owner); else if (ret < 0) @@ -1068,11 +1077,18 @@ static int tcf_action_add(struct net *net, struct nlattr *nla, return tcf_add_notify(net, n, &actions, portid); } +static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON; +static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = { + [TCA_ROOT_FLAGS] = { .type = NLA_BITFIELD32, + .validation_data = &tcaa_root_flags_allowed }, + [TCA_ROOT_TIME_DELTA] = { .type = NLA_U32 }, +}; + static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); - struct nlattr *tca[TCA_ACT_MAX + 1]; + struct nlattr *tca[TCA_ROOT_MAX + 1]; u32 portid = skb ? NETLINK_CB(skb).portid : 0; int ret = 0, ovr = 0; @@ -1080,7 +1096,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, !netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; - ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL, + ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ROOT_MAX, NULL, extack); if (ret < 0) return ret; @@ -1121,16 +1137,12 @@ replay: return ret; } -static struct nlattr *find_dump_kind(const struct nlmsghdr *n) +static struct nlattr *find_dump_kind(struct nlattr **nla) { struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1]; struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; - struct nlattr *nla[TCAA_MAX + 1]; struct nlattr *kind; - if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, - NULL, NULL) < 0) - return NULL; tb1 = nla[TCA_ACT_TAB]; if (tb1 == NULL) return NULL; @@ -1157,8 +1169,20 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) struct tc_action_ops *a_o; int ret = 0; struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh); - struct nlattr *kind = find_dump_kind(cb->nlh); + struct nlattr *tb[TCA_ROOT_MAX + 1]; + struct nlattr *count_attr = NULL; + unsigned long jiffy_since = 0; + struct nlattr *kind = NULL; + struct nla_bitfield32 bf; + u32 msecs_since = 0; + u32 act_count = 0; + + ret = nlmsg_parse(cb->nlh, sizeof(struct tcamsg), tb, TCA_ROOT_MAX, + tcaa_policy, NULL); + if (ret < 0) + return ret; + kind = find_dump_kind(tb); if (kind == NULL) { pr_info("tc_dump_action: action bad kind\n"); return 0; @@ -1168,14 +1192,32 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) if (a_o == NULL) return 0; + cb->args[2] = 0; + if (tb[TCA_ROOT_FLAGS]) { + bf = nla_get_bitfield32(tb[TCA_ROOT_FLAGS]); + cb->args[2] = bf.value; + } + + if (tb[TCA_ROOT_TIME_DELTA]) { + msecs_since = nla_get_u32(tb[TCA_ROOT_TIME_DELTA]); + } + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, sizeof(*t), 0); if (!nlh) goto out_module_put; + + if (msecs_since) + jiffy_since = jiffies - msecs_to_jiffies(msecs_since); + t = nlmsg_data(nlh); t->tca_family = AF_UNSPEC; t->tca__pad1 = 0; t->tca__pad2 = 0; + cb->args[3] = jiffy_since; + count_attr = nla_reserve(skb, TCA_ROOT_COUNT, sizeof(u32)); + if (!count_attr) + goto out_module_put; nest = nla_nest_start(skb, TCA_ACT_TAB); if (nest == NULL) @@ -1188,6 +1230,9 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) if (ret > 0) { nla_nest_end(skb, nest); ret = skb->len; + act_count = cb->args[1]; + memcpy(nla_data(count_attr), &act_count, sizeof(u32)); + cb->args[1] = 0; } else nlmsg_trim(skb, b); @@ -1205,10 +1250,10 @@ out_module_put: static int __init tc_action_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, - NULL); + 0); return 0; } |