Commit 3f2122bb authored by David S. Miller's avatar David S. Miller

Merge branch 'net-sched-cls_u32-Various-improvements'

Jamal Hadi Salim says:

====================
net: sched: cls_u32 Various improvements

Various improvements from Al.

Changes from version 1: Add missing commit
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 67db8c3e a0305986
...@@ -68,7 +68,6 @@ struct tc_u_knode { ...@@ -68,7 +68,6 @@ struct tc_u_knode {
u32 mask; u32 mask;
u32 __percpu *pcpu_success; u32 __percpu *pcpu_success;
#endif #endif
struct tcf_proto *tp;
struct rcu_work rwork; struct rcu_work rwork;
/* The 'sel' field MUST be the last field in structure to allow for /* The 'sel' field MUST be the last field in structure to allow for
* tc_u32_keys allocated at end of structure. * tc_u32_keys allocated at end of structure.
...@@ -80,10 +79,10 @@ struct tc_u_hnode { ...@@ -80,10 +79,10 @@ struct tc_u_hnode {
struct tc_u_hnode __rcu *next; struct tc_u_hnode __rcu *next;
u32 handle; u32 handle;
u32 prio; u32 prio;
struct tc_u_common *tp_c;
int refcnt; int refcnt;
unsigned int divisor; unsigned int divisor;
struct idr handle_idr; struct idr handle_idr;
bool is_root;
struct rcu_head rcu; struct rcu_head rcu;
u32 flags; u32 flags;
/* The 'ht' field MUST be the last field in structure to allow for /* The 'ht' field MUST be the last field in structure to allow for
...@@ -98,7 +97,7 @@ struct tc_u_common { ...@@ -98,7 +97,7 @@ struct tc_u_common {
int refcnt; int refcnt;
struct idr handle_idr; struct idr handle_idr;
struct hlist_node hnode; struct hlist_node hnode;
struct rcu_head rcu; long knodes;
}; };
static inline unsigned int u32_hash_fold(__be32 key, static inline unsigned int u32_hash_fold(__be32 key,
...@@ -344,19 +343,16 @@ static void *tc_u_common_ptr(const struct tcf_proto *tp) ...@@ -344,19 +343,16 @@ static void *tc_u_common_ptr(const struct tcf_proto *tp)
return block->q; return block->q;
} }
static unsigned int tc_u_hash(const struct tcf_proto *tp) static struct hlist_head *tc_u_hash(void *key)
{ {
return hash_ptr(tc_u_common_ptr(tp), U32_HASH_SHIFT); return tc_u_common_hash + hash_ptr(key, U32_HASH_SHIFT);
} }
static struct tc_u_common *tc_u_common_find(const struct tcf_proto *tp) static struct tc_u_common *tc_u_common_find(void *key)
{ {
struct tc_u_common *tc; struct tc_u_common *tc;
unsigned int h; hlist_for_each_entry(tc, tc_u_hash(key), hnode) {
if (tc->ptr == key)
h = tc_u_hash(tp);
hlist_for_each_entry(tc, &tc_u_common_hash[h], hnode) {
if (tc->ptr == tc_u_common_ptr(tp))
return tc; return tc;
} }
return NULL; return NULL;
...@@ -365,10 +361,8 @@ static struct tc_u_common *tc_u_common_find(const struct tcf_proto *tp) ...@@ -365,10 +361,8 @@ static struct tc_u_common *tc_u_common_find(const struct tcf_proto *tp)
static int u32_init(struct tcf_proto *tp) static int u32_init(struct tcf_proto *tp)
{ {
struct tc_u_hnode *root_ht; struct tc_u_hnode *root_ht;
struct tc_u_common *tp_c; void *key = tc_u_common_ptr(tp);
unsigned int h; struct tc_u_common *tp_c = tc_u_common_find(key);
tp_c = tc_u_common_find(tp);
root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL); root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL);
if (root_ht == NULL) if (root_ht == NULL)
...@@ -377,6 +371,7 @@ static int u32_init(struct tcf_proto *tp) ...@@ -377,6 +371,7 @@ static int u32_init(struct tcf_proto *tp)
root_ht->refcnt++; root_ht->refcnt++;
root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : 0x80000000; root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : 0x80000000;
root_ht->prio = tp->prio; root_ht->prio = tp->prio;
root_ht->is_root = true;
idr_init(&root_ht->handle_idr); idr_init(&root_ht->handle_idr);
if (tp_c == NULL) { if (tp_c == NULL) {
...@@ -385,26 +380,23 @@ static int u32_init(struct tcf_proto *tp) ...@@ -385,26 +380,23 @@ static int u32_init(struct tcf_proto *tp)
kfree(root_ht); kfree(root_ht);
return -ENOBUFS; return -ENOBUFS;
} }
tp_c->ptr = tc_u_common_ptr(tp); tp_c->ptr = key;
INIT_HLIST_NODE(&tp_c->hnode); INIT_HLIST_NODE(&tp_c->hnode);
idr_init(&tp_c->handle_idr); idr_init(&tp_c->handle_idr);
h = tc_u_hash(tp); hlist_add_head(&tp_c->hnode, tc_u_hash(key));
hlist_add_head(&tp_c->hnode, &tc_u_common_hash[h]);
} }
tp_c->refcnt++; tp_c->refcnt++;
RCU_INIT_POINTER(root_ht->next, tp_c->hlist); RCU_INIT_POINTER(root_ht->next, tp_c->hlist);
rcu_assign_pointer(tp_c->hlist, root_ht); rcu_assign_pointer(tp_c->hlist, root_ht);
root_ht->tp_c = tp_c;
rcu_assign_pointer(tp->root, root_ht); rcu_assign_pointer(tp->root, root_ht);
tp->data = tp_c; tp->data = tp_c;
return 0; return 0;
} }
static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n, static int u32_destroy_key(struct tc_u_knode *n, bool free_pf)
bool free_pf)
{ {
struct tc_u_hnode *ht = rtnl_dereference(n->ht_down); struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
...@@ -438,7 +430,7 @@ static void u32_delete_key_work(struct work_struct *work) ...@@ -438,7 +430,7 @@ static void u32_delete_key_work(struct work_struct *work)
struct tc_u_knode, struct tc_u_knode,
rwork); rwork);
rtnl_lock(); rtnl_lock();
u32_destroy_key(key->tp, key, false); u32_destroy_key(key, false);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -455,12 +447,13 @@ static void u32_delete_key_freepf_work(struct work_struct *work) ...@@ -455,12 +447,13 @@ static void u32_delete_key_freepf_work(struct work_struct *work)
struct tc_u_knode, struct tc_u_knode,
rwork); rwork);
rtnl_lock(); rtnl_lock();
u32_destroy_key(key->tp, key, true); u32_destroy_key(key, true);
rtnl_unlock(); rtnl_unlock();
} }
static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
{ {
struct tc_u_common *tp_c = tp->data;
struct tc_u_knode __rcu **kp; struct tc_u_knode __rcu **kp;
struct tc_u_knode *pkp; struct tc_u_knode *pkp;
struct tc_u_hnode *ht = rtnl_dereference(key->ht_up); struct tc_u_hnode *ht = rtnl_dereference(key->ht_up);
...@@ -471,6 +464,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) ...@@ -471,6 +464,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
kp = &pkp->next, pkp = rtnl_dereference(*kp)) { kp = &pkp->next, pkp = rtnl_dereference(*kp)) {
if (pkp == key) { if (pkp == key) {
RCU_INIT_POINTER(*kp, key->next); RCU_INIT_POINTER(*kp, key->next);
tp_c->knodes--;
tcf_unbind_filter(tp, &key->res); tcf_unbind_filter(tp, &key->res);
idr_remove(&ht->handle_idr, key->handle); idr_remove(&ht->handle_idr, key->handle);
...@@ -585,6 +579,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, ...@@ -585,6 +579,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht, static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_u_common *tp_c = tp->data;
struct tc_u_knode *n; struct tc_u_knode *n;
unsigned int h; unsigned int h;
...@@ -592,13 +587,14 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht, ...@@ -592,13 +587,14 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
while ((n = rtnl_dereference(ht->ht[h])) != NULL) { while ((n = rtnl_dereference(ht->ht[h])) != NULL) {
RCU_INIT_POINTER(ht->ht[h], RCU_INIT_POINTER(ht->ht[h],
rtnl_dereference(n->next)); rtnl_dereference(n->next));
tp_c->knodes--;
tcf_unbind_filter(tp, &n->res); tcf_unbind_filter(tp, &n->res);
u32_remove_hw_knode(tp, n, extack); u32_remove_hw_knode(tp, n, extack);
idr_remove(&ht->handle_idr, n->handle); idr_remove(&ht->handle_idr, n->handle);
if (tcf_exts_get_net(&n->exts)) if (tcf_exts_get_net(&n->exts))
tcf_queue_work(&n->rwork, u32_delete_key_freepf_work); tcf_queue_work(&n->rwork, u32_delete_key_freepf_work);
else else
u32_destroy_key(n->tp, n, true); u32_destroy_key(n, true);
} }
} }
} }
...@@ -631,17 +627,6 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht, ...@@ -631,17 +627,6 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
return -ENOENT; return -ENOENT;
} }
static bool ht_empty(struct tc_u_hnode *ht)
{
unsigned int h;
for (h = 0; h <= ht->divisor; h++)
if (rcu_access_pointer(ht->ht[h]))
return false;
return true;
}
static void u32_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack) static void u32_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
{ {
struct tc_u_common *tp_c = tp->data; struct tc_u_common *tp_c = tp->data;
...@@ -679,20 +664,16 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last, ...@@ -679,20 +664,16 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct tc_u_hnode *ht = arg; struct tc_u_hnode *ht = arg;
struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
struct tc_u_common *tp_c = tp->data; struct tc_u_common *tp_c = tp->data;
int ret = 0; int ret = 0;
if (ht == NULL)
goto out;
if (TC_U32_KEY(ht->handle)) { if (TC_U32_KEY(ht->handle)) {
u32_remove_hw_knode(tp, (struct tc_u_knode *)ht, extack); u32_remove_hw_knode(tp, (struct tc_u_knode *)ht, extack);
ret = u32_delete_key(tp, (struct tc_u_knode *)ht); ret = u32_delete_key(tp, (struct tc_u_knode *)ht);
goto out; goto out;
} }
if (root_ht == ht) { if (ht->is_root) {
NL_SET_ERR_MSG_MOD(extack, "Not allowed to delete root node"); NL_SET_ERR_MSG_MOD(extack, "Not allowed to delete root node");
return -EINVAL; return -EINVAL;
} }
...@@ -706,38 +687,7 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last, ...@@ -706,38 +687,7 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
} }
out: out:
*last = true; *last = tp_c->refcnt == 1 && tp_c->knodes == 0;
if (root_ht) {
if (root_ht->refcnt > 1) {
*last = false;
goto ret;
}
if (root_ht->refcnt == 1) {
if (!ht_empty(root_ht)) {
*last = false;
goto ret;
}
}
}
if (tp_c->refcnt > 1) {
*last = false;
goto ret;
}
if (tp_c->refcnt == 1) {
struct tc_u_hnode *ht;
for (ht = rtnl_dereference(tp_c->hlist);
ht;
ht = rtnl_dereference(ht->next))
if (!ht_empty(ht)) {
*last = false;
break;
}
}
ret:
return ret; return ret;
} }
...@@ -768,7 +718,7 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { ...@@ -768,7 +718,7 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
}; };
static int u32_set_parms(struct net *net, struct tcf_proto *tp, static int u32_set_parms(struct net *net, struct tcf_proto *tp,
unsigned long base, struct tc_u_hnode *ht, unsigned long base,
struct tc_u_knode *n, struct nlattr **tb, struct tc_u_knode *n, struct nlattr **tb,
struct nlattr *est, bool ovr, struct nlattr *est, bool ovr,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
...@@ -789,12 +739,16 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, ...@@ -789,12 +739,16 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
} }
if (handle) { if (handle) {
ht_down = u32_lookup_ht(ht->tp_c, handle); ht_down = u32_lookup_ht(tp->data, handle);
if (!ht_down) { if (!ht_down) {
NL_SET_ERR_MSG_MOD(extack, "Link hash table not found"); NL_SET_ERR_MSG_MOD(extack, "Link hash table not found");
return -EINVAL; return -EINVAL;
} }
if (ht_down->is_root) {
NL_SET_ERR_MSG_MOD(extack, "Not linking to root node");
return -EINVAL;
}
ht_down->refcnt++; ht_down->refcnt++;
} }
...@@ -891,7 +845,6 @@ static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp, ...@@ -891,7 +845,6 @@ static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp,
/* Similarly success statistics must be moved as pointers */ /* Similarly success statistics must be moved as pointers */
new->pcpu_success = n->pcpu_success; new->pcpu_success = n->pcpu_success;
#endif #endif
new->tp = tp;
memcpy(&new->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key)); memcpy(&new->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key));
if (tcf_exts_init(&new->exts, TCA_U32_ACT, TCA_U32_POLICE)) { if (tcf_exts_init(&new->exts, TCA_U32_ACT, TCA_U32_POLICE)) {
...@@ -960,18 +913,17 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -960,18 +913,17 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
err = u32_set_parms(net, tp, base, err = u32_set_parms(net, tp, base, new, tb,
rtnl_dereference(n->ht_up), new, tb,
tca[TCA_RATE], ovr, extack); tca[TCA_RATE], ovr, extack);
if (err) { if (err) {
u32_destroy_key(tp, new, false); u32_destroy_key(new, false);
return err; return err;
} }
err = u32_replace_hw_knode(tp, new, flags, extack); err = u32_replace_hw_knode(tp, new, flags, extack);
if (err) { if (err) {
u32_destroy_key(tp, new, false); u32_destroy_key(new, false);
return err; return err;
} }
...@@ -988,7 +940,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -988,7 +940,11 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
if (tb[TCA_U32_DIVISOR]) { if (tb[TCA_U32_DIVISOR]) {
unsigned int divisor = nla_get_u32(tb[TCA_U32_DIVISOR]); unsigned int divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
if (--divisor > 0x100) { if (!is_power_of_2(divisor)) {
NL_SET_ERR_MSG_MOD(extack, "Divisor is not a power of 2");
return -EINVAL;
}
if (divisor-- > 0x100) {
NL_SET_ERR_MSG_MOD(extack, "Exceeded maximum 256 hash buckets"); NL_SET_ERR_MSG_MOD(extack, "Exceeded maximum 256 hash buckets");
return -EINVAL; return -EINVAL;
} }
...@@ -1013,7 +969,6 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -1013,7 +969,6 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
return err; return err;
} }
} }
ht->tp_c = tp_c;
ht->refcnt = 1; ht->refcnt = 1;
ht->divisor = divisor; ht->divisor = divisor;
ht->handle = handle; ht->handle = handle;
...@@ -1103,7 +1058,6 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -1103,7 +1058,6 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
n->handle = handle; n->handle = handle;
n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
n->flags = flags; n->flags = flags;
n->tp = tp;
err = tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE); err = tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE);
if (err < 0) if (err < 0)
...@@ -1125,7 +1079,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -1125,7 +1079,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
} }
#endif #endif
err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr, err = u32_set_parms(net, tp, base, n, tb, tca[TCA_RATE], ovr,
extack); extack);
if (err == 0) { if (err == 0) {
struct tc_u_knode __rcu **ins; struct tc_u_knode __rcu **ins;
...@@ -1146,6 +1100,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, ...@@ -1146,6 +1100,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
RCU_INIT_POINTER(n->next, pins); RCU_INIT_POINTER(n->next, pins);
rcu_assign_pointer(*ins, n); rcu_assign_pointer(*ins, n);
tp_c->knodes++;
*arg = n; *arg = n;
return 0; return 0;
} }
......
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