Commit dfb05553 authored by David S. Miller's avatar David S. Miller

Merge branch 'tc-filter-cleanup-destroy-delete'

Cong Wang says:

====================
net_sched: clean up tc filter destroy and delete logic

The first patch fixes a potenial race condition, the second one
is pure cleanup.
====================
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b1d9fc41 43920538
...@@ -204,14 +204,14 @@ struct tcf_proto_ops { ...@@ -204,14 +204,14 @@ struct tcf_proto_ops {
const struct tcf_proto *, const struct tcf_proto *,
struct tcf_result *); struct tcf_result *);
int (*init)(struct tcf_proto*); int (*init)(struct tcf_proto*);
bool (*destroy)(struct tcf_proto*, bool); void (*destroy)(struct tcf_proto*);
unsigned long (*get)(struct tcf_proto*, u32 handle); unsigned long (*get)(struct tcf_proto*, u32 handle);
int (*change)(struct net *net, struct sk_buff *, int (*change)(struct net *net, struct sk_buff *,
struct tcf_proto*, unsigned long, struct tcf_proto*, unsigned long,
u32 handle, struct nlattr **, u32 handle, struct nlattr **,
unsigned long *, bool); unsigned long *, bool);
int (*delete)(struct tcf_proto*, unsigned long); int (*delete)(struct tcf_proto*, unsigned long, bool*);
void (*walk)(struct tcf_proto*, struct tcf_walker *arg); void (*walk)(struct tcf_proto*, struct tcf_walker *arg);
/* rtnetlink specific */ /* rtnetlink specific */
......
...@@ -178,14 +178,11 @@ static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol, ...@@ -178,14 +178,11 @@ static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
return ERR_PTR(err); return ERR_PTR(err);
} }
static bool tcf_proto_destroy(struct tcf_proto *tp, bool force) static void tcf_proto_destroy(struct tcf_proto *tp)
{ {
if (tp->ops->destroy(tp, force)) { tp->ops->destroy(tp);
module_put(tp->ops->owner); module_put(tp->ops->owner);
kfree_rcu(tp, rcu); kfree_rcu(tp, rcu);
return true;
}
return false;
} }
void tcf_destroy_chain(struct tcf_proto __rcu **fl) void tcf_destroy_chain(struct tcf_proto __rcu **fl)
...@@ -194,7 +191,7 @@ void tcf_destroy_chain(struct tcf_proto __rcu **fl) ...@@ -194,7 +191,7 @@ void tcf_destroy_chain(struct tcf_proto __rcu **fl)
while ((tp = rtnl_dereference(*fl)) != NULL) { while ((tp = rtnl_dereference(*fl)) != NULL) {
RCU_INIT_POINTER(*fl, tp->next); RCU_INIT_POINTER(*fl, tp->next);
tcf_proto_destroy(tp, true); tcf_proto_destroy(tp);
} }
} }
EXPORT_SYMBOL(tcf_destroy_chain); EXPORT_SYMBOL(tcf_destroy_chain);
...@@ -361,7 +358,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -361,7 +358,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
RCU_INIT_POINTER(*back, next); RCU_INIT_POINTER(*back, next);
tfilter_notify(net, skb, n, tp, fh, tfilter_notify(net, skb, n, tp, fh,
RTM_DELTFILTER, false); RTM_DELTFILTER, false);
tcf_proto_destroy(tp, true); tcf_proto_destroy(tp);
err = 0; err = 0;
goto errout; goto errout;
} }
...@@ -372,24 +369,28 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -372,24 +369,28 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
goto errout; goto errout;
} }
} else { } else {
bool last;
switch (n->nlmsg_type) { switch (n->nlmsg_type) {
case RTM_NEWTFILTER: case RTM_NEWTFILTER:
if (n->nlmsg_flags & NLM_F_EXCL) { if (n->nlmsg_flags & NLM_F_EXCL) {
if (tp_created) if (tp_created)
tcf_proto_destroy(tp, true); tcf_proto_destroy(tp);
err = -EEXIST; err = -EEXIST;
goto errout; goto errout;
} }
break; break;
case RTM_DELTFILTER: case RTM_DELTFILTER:
err = tp->ops->delete(tp, fh); err = tp->ops->delete(tp, fh, &last);
if (err) if (err)
goto errout; goto errout;
next = rtnl_dereference(tp->next); next = rtnl_dereference(tp->next);
tfilter_notify(net, skb, n, tp, t->tcm_handle, tfilter_notify(net, skb, n, tp, t->tcm_handle,
RTM_DELTFILTER, false); RTM_DELTFILTER, false);
if (tcf_proto_destroy(tp, false)) if (last) {
RCU_INIT_POINTER(*back, next); RCU_INIT_POINTER(*back, next);
tcf_proto_destroy(tp);
}
goto errout; goto errout;
case RTM_GETTFILTER: case RTM_GETTFILTER:
err = tfilter_notify(net, skb, n, tp, fh, err = tfilter_notify(net, skb, n, tp, fh,
...@@ -411,7 +412,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -411,7 +412,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false); tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
} else { } else {
if (tp_created) if (tp_created)
tcf_proto_destroy(tp, true); tcf_proto_destroy(tp);
} }
errout: errout:
......
...@@ -93,30 +93,28 @@ static void basic_delete_filter(struct rcu_head *head) ...@@ -93,30 +93,28 @@ static void basic_delete_filter(struct rcu_head *head)
kfree(f); kfree(f);
} }
static bool basic_destroy(struct tcf_proto *tp, bool force) static void basic_destroy(struct tcf_proto *tp)
{ {
struct basic_head *head = rtnl_dereference(tp->root); struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *f, *n; struct basic_filter *f, *n;
if (!force && !list_empty(&head->flist))
return false;
list_for_each_entry_safe(f, n, &head->flist, link) { list_for_each_entry_safe(f, n, &head->flist, link) {
list_del_rcu(&f->link); list_del_rcu(&f->link);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, basic_delete_filter); call_rcu(&f->rcu, basic_delete_filter);
} }
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
return true;
} }
static int basic_delete(struct tcf_proto *tp, unsigned long arg) static int basic_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *f = (struct basic_filter *) arg; struct basic_filter *f = (struct basic_filter *) arg;
list_del_rcu(&f->link); list_del_rcu(&f->link);
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, basic_delete_filter); call_rcu(&f->rcu, basic_delete_filter);
*last = list_empty(&head->flist);
return 0; return 0;
} }
......
...@@ -274,25 +274,24 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) ...@@ -274,25 +274,24 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog)
call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu);
} }
static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct cls_bpf_head *head = rtnl_dereference(tp->root);
__cls_bpf_delete(tp, (struct cls_bpf_prog *) arg); __cls_bpf_delete(tp, (struct cls_bpf_prog *) arg);
*last = list_empty(&head->plist);
return 0; return 0;
} }
static bool cls_bpf_destroy(struct tcf_proto *tp, bool force) static void cls_bpf_destroy(struct tcf_proto *tp)
{ {
struct cls_bpf_head *head = rtnl_dereference(tp->root); struct cls_bpf_head *head = rtnl_dereference(tp->root);
struct cls_bpf_prog *prog, *tmp; struct cls_bpf_prog *prog, *tmp;
if (!force && !list_empty(&head->plist))
return false;
list_for_each_entry_safe(prog, tmp, &head->plist, link) list_for_each_entry_safe(prog, tmp, &head->plist, link)
__cls_bpf_delete(tp, prog); __cls_bpf_delete(tp, prog);
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
return true;
} }
static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
......
...@@ -131,20 +131,16 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, ...@@ -131,20 +131,16 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
return err; return err;
} }
static bool cls_cgroup_destroy(struct tcf_proto *tp, bool force) static void cls_cgroup_destroy(struct tcf_proto *tp)
{ {
struct cls_cgroup_head *head = rtnl_dereference(tp->root); struct cls_cgroup_head *head = rtnl_dereference(tp->root);
if (!force)
return false;
/* Head can still be NULL due to cls_cgroup_init(). */ /* Head can still be NULL due to cls_cgroup_init(). */
if (head) if (head)
call_rcu(&head->rcu, cls_cgroup_destroy_rcu); call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
return true;
} }
static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg) static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -562,12 +562,14 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, ...@@ -562,12 +562,14 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
return err; return err;
} }
static int flow_delete(struct tcf_proto *tp, unsigned long arg) static int flow_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct flow_head *head = rtnl_dereference(tp->root);
struct flow_filter *f = (struct flow_filter *)arg; struct flow_filter *f = (struct flow_filter *)arg;
list_del_rcu(&f->list); list_del_rcu(&f->list);
call_rcu(&f->rcu, flow_destroy_filter); call_rcu(&f->rcu, flow_destroy_filter);
*last = list_empty(&head->filters);
return 0; return 0;
} }
...@@ -583,20 +585,16 @@ static int flow_init(struct tcf_proto *tp) ...@@ -583,20 +585,16 @@ static int flow_init(struct tcf_proto *tp)
return 0; return 0;
} }
static bool flow_destroy(struct tcf_proto *tp, bool force) static void flow_destroy(struct tcf_proto *tp)
{ {
struct flow_head *head = rtnl_dereference(tp->root); struct flow_head *head = rtnl_dereference(tp->root);
struct flow_filter *f, *next; struct flow_filter *f, *next;
if (!force && !list_empty(&head->filters))
return false;
list_for_each_entry_safe(f, next, &head->filters, list) { list_for_each_entry_safe(f, next, &head->filters, list) {
list_del_rcu(&f->list); list_del_rcu(&f->list);
call_rcu(&f->rcu, flow_destroy_filter); call_rcu(&f->rcu, flow_destroy_filter);
} }
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
return true;
} }
static unsigned long flow_get(struct tcf_proto *tp, u32 handle) static unsigned long flow_get(struct tcf_proto *tp, u32 handle)
......
...@@ -328,21 +328,16 @@ static void fl_destroy_rcu(struct rcu_head *rcu) ...@@ -328,21 +328,16 @@ static void fl_destroy_rcu(struct rcu_head *rcu)
schedule_work(&head->work); schedule_work(&head->work);
} }
static bool fl_destroy(struct tcf_proto *tp, bool force) static void fl_destroy(struct tcf_proto *tp)
{ {
struct cls_fl_head *head = rtnl_dereference(tp->root); struct cls_fl_head *head = rtnl_dereference(tp->root);
struct cls_fl_filter *f, *next; struct cls_fl_filter *f, *next;
if (!force && !list_empty(&head->filters))
return false;
list_for_each_entry_safe(f, next, &head->filters, list) list_for_each_entry_safe(f, next, &head->filters, list)
__fl_delete(tp, f); __fl_delete(tp, f);
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
call_rcu(&head->rcu, fl_destroy_rcu); call_rcu(&head->rcu, fl_destroy_rcu);
return true;
} }
static unsigned long fl_get(struct tcf_proto *tp, u32 handle) static unsigned long fl_get(struct tcf_proto *tp, u32 handle)
...@@ -947,7 +942,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, ...@@ -947,7 +942,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
return err; return err;
} }
static int fl_delete(struct tcf_proto *tp, unsigned long arg) static int fl_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct cls_fl_head *head = rtnl_dereference(tp->root); struct cls_fl_head *head = rtnl_dereference(tp->root);
struct cls_fl_filter *f = (struct cls_fl_filter *) arg; struct cls_fl_filter *f = (struct cls_fl_filter *) arg;
...@@ -956,6 +951,7 @@ static int fl_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -956,6 +951,7 @@ static int fl_delete(struct tcf_proto *tp, unsigned long arg)
rhashtable_remove_fast(&head->ht, &f->ht_node, rhashtable_remove_fast(&head->ht, &f->ht_node,
head->ht_params); head->ht_params);
__fl_delete(tp, f); __fl_delete(tp, f);
*last = list_empty(&head->filters);
return 0; return 0;
} }
......
...@@ -127,20 +127,14 @@ static void fw_delete_filter(struct rcu_head *head) ...@@ -127,20 +127,14 @@ static void fw_delete_filter(struct rcu_head *head)
kfree(f); kfree(f);
} }
static bool fw_destroy(struct tcf_proto *tp, bool force) static void fw_destroy(struct tcf_proto *tp)
{ {
struct fw_head *head = rtnl_dereference(tp->root); struct fw_head *head = rtnl_dereference(tp->root);
struct fw_filter *f; struct fw_filter *f;
int h; int h;
if (head == NULL) if (head == NULL)
return true; return;
if (!force) {
for (h = 0; h < HTSIZE; h++)
if (rcu_access_pointer(head->ht[h]))
return false;
}
for (h = 0; h < HTSIZE; h++) { for (h = 0; h < HTSIZE; h++) {
while ((f = rtnl_dereference(head->ht[h])) != NULL) { while ((f = rtnl_dereference(head->ht[h])) != NULL) {
...@@ -150,17 +144,17 @@ static bool fw_destroy(struct tcf_proto *tp, bool force) ...@@ -150,17 +144,17 @@ static bool fw_destroy(struct tcf_proto *tp, bool force)
call_rcu(&f->rcu, fw_delete_filter); call_rcu(&f->rcu, fw_delete_filter);
} }
} }
RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
return true;
} }
static int fw_delete(struct tcf_proto *tp, unsigned long arg) static int fw_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct fw_head *head = rtnl_dereference(tp->root); struct fw_head *head = rtnl_dereference(tp->root);
struct fw_filter *f = (struct fw_filter *)arg; struct fw_filter *f = (struct fw_filter *)arg;
struct fw_filter __rcu **fp; struct fw_filter __rcu **fp;
struct fw_filter *pfp; struct fw_filter *pfp;
int ret = -EINVAL;
int h;
if (head == NULL || f == NULL) if (head == NULL || f == NULL)
goto out; goto out;
...@@ -173,11 +167,21 @@ static int fw_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -173,11 +167,21 @@ static int fw_delete(struct tcf_proto *tp, unsigned long arg)
RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
tcf_unbind_filter(tp, &f->res); tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, fw_delete_filter); call_rcu(&f->rcu, fw_delete_filter);
return 0; ret = 0;
break;
} }
} }
*last = true;
for (h = 0; h < HTSIZE; h++) {
if (rcu_access_pointer(head->ht[h])) {
*last = false;
break;
}
}
out: out:
return -EINVAL; return ret;
} }
static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = {
......
...@@ -90,19 +90,18 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, ...@@ -90,19 +90,18 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp,
&offload); &offload);
} }
static bool mall_destroy(struct tcf_proto *tp, bool force) static void mall_destroy(struct tcf_proto *tp)
{ {
struct cls_mall_head *head = rtnl_dereference(tp->root); struct cls_mall_head *head = rtnl_dereference(tp->root);
struct net_device *dev = tp->q->dev_queue->dev; struct net_device *dev = tp->q->dev_queue->dev;
if (!head) if (!head)
return true; return;
if (tc_should_offload(dev, tp, head->flags)) if (tc_should_offload(dev, tp, head->flags))
mall_destroy_hw_filter(tp, head, (unsigned long) head); mall_destroy_hw_filter(tp, head, (unsigned long) head);
call_rcu(&head->rcu, mall_destroy_rcu); call_rcu(&head->rcu, mall_destroy_rcu);
return true;
} }
static unsigned long mall_get(struct tcf_proto *tp, u32 handle) static unsigned long mall_get(struct tcf_proto *tp, u32 handle)
...@@ -216,7 +215,7 @@ static int mall_change(struct net *net, struct sk_buff *in_skb, ...@@ -216,7 +215,7 @@ static int mall_change(struct net *net, struct sk_buff *in_skb,
return err; return err;
} }
static int mall_delete(struct tcf_proto *tp, unsigned long arg) static int mall_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -140,8 +140,6 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, ...@@ -140,8 +140,6 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
goto failure; goto failure;
id = dst->tclassid; id = dst->tclassid;
if (head == NULL)
goto old_method;
iif = inet_iif(skb); iif = inet_iif(skb);
...@@ -194,15 +192,6 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, ...@@ -194,15 +192,6 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
route4_set_fastmap(head, id, iif, ROUTE4_FAILURE); route4_set_fastmap(head, id, iif, ROUTE4_FAILURE);
failure: failure:
return -1; return -1;
old_method:
if (id && (TC_H_MAJ(id) == 0 ||
!(TC_H_MAJ(id^tp->q->handle)))) {
res->classid = id;
res->class = 0;
return 0;
}
return -1;
} }
static inline u32 to_hash(u32 id) static inline u32 to_hash(u32 id)
...@@ -234,9 +223,6 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle) ...@@ -234,9 +223,6 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
struct route4_filter *f; struct route4_filter *f;
unsigned int h1, h2; unsigned int h1, h2;
if (!head)
return 0;
h1 = to_hash(handle); h1 = to_hash(handle);
if (h1 > 256) if (h1 > 256)
return 0; return 0;
...@@ -276,20 +262,13 @@ static void route4_delete_filter(struct rcu_head *head) ...@@ -276,20 +262,13 @@ static void route4_delete_filter(struct rcu_head *head)
kfree(f); kfree(f);
} }
static bool route4_destroy(struct tcf_proto *tp, bool force) static void route4_destroy(struct tcf_proto *tp)
{ {
struct route4_head *head = rtnl_dereference(tp->root); struct route4_head *head = rtnl_dereference(tp->root);
int h1, h2; int h1, h2;
if (head == NULL) if (head == NULL)
return true; return;
if (!force) {
for (h1 = 0; h1 <= 256; h1++) {
if (rcu_access_pointer(head->table[h1]))
return false;
}
}
for (h1 = 0; h1 <= 256; h1++) { for (h1 = 0; h1 <= 256; h1++) {
struct route4_bucket *b; struct route4_bucket *b;
...@@ -312,12 +291,10 @@ static bool route4_destroy(struct tcf_proto *tp, bool force) ...@@ -312,12 +291,10 @@ static bool route4_destroy(struct tcf_proto *tp, bool force)
kfree_rcu(b, rcu); kfree_rcu(b, rcu);
} }
} }
RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu); kfree_rcu(head, rcu);
return true;
} }
static int route4_delete(struct tcf_proto *tp, unsigned long arg) static int route4_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct route4_head *head = rtnl_dereference(tp->root); struct route4_head *head = rtnl_dereference(tp->root);
struct route4_filter *f = (struct route4_filter *)arg; struct route4_filter *f = (struct route4_filter *)arg;
...@@ -325,7 +302,7 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -325,7 +302,7 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg)
struct route4_filter *nf; struct route4_filter *nf;
struct route4_bucket *b; struct route4_bucket *b;
unsigned int h = 0; unsigned int h = 0;
int i; int i, h1;
if (!head || !f) if (!head || !f)
return -EINVAL; return -EINVAL;
...@@ -356,16 +333,25 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -356,16 +333,25 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg)
rt = rtnl_dereference(b->ht[i]); rt = rtnl_dereference(b->ht[i]);
if (rt) if (rt)
return 0; goto out;
} }
/* OK, session has no flows */ /* OK, session has no flows */
RCU_INIT_POINTER(head->table[to_hash(h)], NULL); RCU_INIT_POINTER(head->table[to_hash(h)], NULL);
kfree_rcu(b, rcu); kfree_rcu(b, rcu);
break;
}
}
return 0; out:
*last = true;
for (h1 = 0; h1 <= 256; h1++) {
if (rcu_access_pointer(head->table[h1])) {
*last = false;
break;
} }
} }
return 0; return 0;
} }
......
...@@ -152,8 +152,6 @@ static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp, ...@@ -152,8 +152,6 @@ static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
return -1; return -1;
nhptr = ip_hdr(skb); nhptr = ip_hdr(skb);
#endif #endif
if (unlikely(!head))
return -1;
restart: restart:
#if RSVP_DST_LEN == 4 #if RSVP_DST_LEN == 4
...@@ -302,22 +300,13 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) ...@@ -302,22 +300,13 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
call_rcu(&f->rcu, rsvp_delete_filter_rcu); call_rcu(&f->rcu, rsvp_delete_filter_rcu);
} }
static bool rsvp_destroy(struct tcf_proto *tp, bool force) static void rsvp_destroy(struct tcf_proto *tp)
{ {
struct rsvp_head *data = rtnl_dereference(tp->root); struct rsvp_head *data = rtnl_dereference(tp->root);
int h1, h2; int h1, h2;
if (data == NULL) if (data == NULL)
return true; return;
if (!force) {
for (h1 = 0; h1 < 256; h1++) {
if (rcu_access_pointer(data->ht[h1]))
return false;
}
}
RCU_INIT_POINTER(tp->root, NULL);
for (h1 = 0; h1 < 256; h1++) { for (h1 = 0; h1 < 256; h1++) {
struct rsvp_session *s; struct rsvp_session *s;
...@@ -337,10 +326,9 @@ static bool rsvp_destroy(struct tcf_proto *tp, bool force) ...@@ -337,10 +326,9 @@ static bool rsvp_destroy(struct tcf_proto *tp, bool force)
} }
} }
kfree_rcu(data, rcu); kfree_rcu(data, rcu);
return true;
} }
static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct rsvp_head *head = rtnl_dereference(tp->root); struct rsvp_head *head = rtnl_dereference(tp->root);
struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg; struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg;
...@@ -348,7 +336,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -348,7 +336,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
unsigned int h = f->handle; unsigned int h = f->handle;
struct rsvp_session __rcu **sp; struct rsvp_session __rcu **sp;
struct rsvp_session *nsp, *s = f->sess; struct rsvp_session *nsp, *s = f->sess;
int i; int i, h1;
fp = &s->ht[(h >> 8) & 0xFF]; fp = &s->ht[(h >> 8) & 0xFF];
for (nfp = rtnl_dereference(*fp); nfp; for (nfp = rtnl_dereference(*fp); nfp;
...@@ -361,7 +349,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -361,7 +349,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
for (i = 0; i <= 16; i++) for (i = 0; i <= 16; i++)
if (s->ht[i]) if (s->ht[i])
return 0; goto out;
/* OK, session has no flows */ /* OK, session has no flows */
sp = &head->ht[h & 0xFF]; sp = &head->ht[h & 0xFF];
...@@ -370,13 +358,23 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -370,13 +358,23 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg)
if (nsp == s) { if (nsp == s) {
RCU_INIT_POINTER(*sp, s->next); RCU_INIT_POINTER(*sp, s->next);
kfree_rcu(s, rcu); kfree_rcu(s, rcu);
return 0; goto out;
} }
} }
return 0; break;
}
}
out:
*last = true;
for (h1 = 0; h1 < 256; h1++) {
if (rcu_access_pointer(head->ht[h1])) {
*last = false;
break;
} }
} }
return 0; return 0;
} }
......
...@@ -150,7 +150,7 @@ static void tcindex_destroy_fexts(struct rcu_head *head) ...@@ -150,7 +150,7 @@ static void tcindex_destroy_fexts(struct rcu_head *head)
kfree(f); kfree(f);
} }
static int tcindex_delete(struct tcf_proto *tp, unsigned long arg) static int tcindex_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct tcindex_data *p = rtnl_dereference(tp->root); struct tcindex_data *p = rtnl_dereference(tp->root);
struct tcindex_filter_result *r = (struct tcindex_filter_result *) arg; struct tcindex_filter_result *r = (struct tcindex_filter_result *) arg;
...@@ -186,6 +186,8 @@ static int tcindex_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -186,6 +186,8 @@ static int tcindex_delete(struct tcf_proto *tp, unsigned long arg)
call_rcu(&f->rcu, tcindex_destroy_fexts); call_rcu(&f->rcu, tcindex_destroy_fexts);
else else
call_rcu(&r->rcu, tcindex_destroy_rexts); call_rcu(&r->rcu, tcindex_destroy_rexts);
*last = false;
return 0; return 0;
} }
...@@ -193,7 +195,9 @@ static int tcindex_destroy_element(struct tcf_proto *tp, ...@@ -193,7 +195,9 @@ static int tcindex_destroy_element(struct tcf_proto *tp,
unsigned long arg, unsigned long arg,
struct tcf_walker *walker) struct tcf_walker *walker)
{ {
return tcindex_delete(tp, arg); bool last;
return tcindex_delete(tp, arg, &last);
} }
static void __tcindex_destroy(struct rcu_head *head) static void __tcindex_destroy(struct rcu_head *head)
...@@ -529,14 +533,11 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker) ...@@ -529,14 +533,11 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker)
} }
} }
static bool tcindex_destroy(struct tcf_proto *tp, bool force) static void tcindex_destroy(struct tcf_proto *tp)
{ {
struct tcindex_data *p = rtnl_dereference(tp->root); struct tcindex_data *p = rtnl_dereference(tp->root);
struct tcf_walker walker; struct tcf_walker walker;
if (!force)
return false;
pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p);
walker.count = 0; walker.count = 0;
walker.skip = 0; walker.skip = 0;
...@@ -544,7 +545,6 @@ static bool tcindex_destroy(struct tcf_proto *tp, bool force) ...@@ -544,7 +545,6 @@ static bool tcindex_destroy(struct tcf_proto *tp, bool force)
tcindex_walk(tp, &walker); tcindex_walk(tp, &walker);
call_rcu(&p->rcu, __tcindex_destroy); call_rcu(&p->rcu, __tcindex_destroy);
return true;
} }
......
...@@ -585,37 +585,13 @@ static bool ht_empty(struct tc_u_hnode *ht) ...@@ -585,37 +585,13 @@ static bool ht_empty(struct tc_u_hnode *ht)
return true; return true;
} }
static bool u32_destroy(struct tcf_proto *tp, bool force) static void u32_destroy(struct tcf_proto *tp)
{ {
struct tc_u_common *tp_c = tp->data; struct tc_u_common *tp_c = tp->data;
struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
WARN_ON(root_ht == NULL); WARN_ON(root_ht == NULL);
if (!force) {
if (root_ht) {
if (root_ht->refcnt > 1)
return false;
if (root_ht->refcnt == 1) {
if (!ht_empty(root_ht))
return false;
}
}
if (tp_c->refcnt > 1)
return false;
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))
return false;
}
}
if (root_ht && --root_ht->refcnt == 0) if (root_ht && --root_ht->refcnt == 0)
u32_destroy_hnode(tp, root_ht); u32_destroy_hnode(tp, root_ht);
...@@ -640,20 +616,22 @@ static bool u32_destroy(struct tcf_proto *tp, bool force) ...@@ -640,20 +616,22 @@ static bool u32_destroy(struct tcf_proto *tp, bool force)
} }
tp->data = NULL; tp->data = NULL;
return true;
} }
static int u32_delete(struct tcf_proto *tp, unsigned long arg) static int u32_delete(struct tcf_proto *tp, unsigned long arg, bool *last)
{ {
struct tc_u_hnode *ht = (struct tc_u_hnode *)arg; struct tc_u_hnode *ht = (struct tc_u_hnode *)arg;
struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
struct tc_u_common *tp_c = tp->data;
int ret = 0;
if (ht == NULL) if (ht == NULL)
return 0; goto out;
if (TC_U32_KEY(ht->handle)) { if (TC_U32_KEY(ht->handle)) {
u32_remove_hw_knode(tp, ht->handle); u32_remove_hw_knode(tp, ht->handle);
return u32_delete_key(tp, (struct tc_u_knode *)ht); ret = u32_delete_key(tp, (struct tc_u_knode *)ht);
goto out;
} }
if (root_ht == ht) if (root_ht == ht)
...@@ -666,7 +644,40 @@ static int u32_delete(struct tcf_proto *tp, unsigned long arg) ...@@ -666,7 +644,40 @@ static int u32_delete(struct tcf_proto *tp, unsigned long arg)
return -EBUSY; return -EBUSY;
} }
return 0; out:
*last = true;
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;
} }
#define NR_U32_NODE (1<<12) #define NR_U32_NODE (1<<12)
......
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