Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e052be6 authored by Cong Wang's avatar Cong Wang Committed by David S. Miller
Browse files

net_sched: destroy proto tp when all filters are gone



Kernel automatically creates a tp for each
(kind, protocol, priority) tuple, which has handle 0,
when we add a new filter, but it still is left there
after we remove our own, unless we don't specify the
handle (literally means all the filters under
the tuple). For example this one is left:

  # tc filter show dev eth0
  filter parent 8001: protocol arp pref 49152 basic

The user-space is hard to clean up these for kernel
because filters like u32 are organized in a complex way.
So kernel is responsible to remove it after all filters
are gone.  Each type of filter has its own way to
store the filters, so each type has to provide its
way to check if all filters are gone.

Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarCong Wang <cwang@twopensource.com>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Acked-by: default avatarJamal Hadi <Salim&lt;jhs@mojatatu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fc6c6c2b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -213,7 +213,7 @@ struct tcf_proto_ops {
					    const struct tcf_proto *,
					    struct tcf_result *);
	int			(*init)(struct tcf_proto*);
	void			(*destroy)(struct tcf_proto*);
	bool			(*destroy)(struct tcf_proto*, bool);

	unsigned long		(*get)(struct tcf_proto*, u32 handle);
	int			(*change)(struct net *net, struct sk_buff *,
@@ -399,7 +399,7 @@ struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
				const struct Qdisc_ops *ops, u32 parentid);
void __qdisc_calculate_pkt_len(struct sk_buff *skb,
			       const struct qdisc_size_table *stab);
void tcf_destroy(struct tcf_proto *tp);
bool tcf_destroy(struct tcf_proto *tp, bool force);
void tcf_destroy_chain(struct tcf_proto __rcu **fl);

/* Reset all TX qdiscs greater then index of a device.  */
+10 −4
Original line number Diff line number Diff line
@@ -286,7 +286,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
			RCU_INIT_POINTER(*back, next);

			tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
			tcf_destroy(tp);
			tcf_destroy(tp, true);
			err = 0;
			goto errout;
		}
@@ -301,14 +301,20 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
			err = -EEXIST;
			if (n->nlmsg_flags & NLM_F_EXCL) {
				if (tp_created)
					tcf_destroy(tp);
					tcf_destroy(tp, true);
				goto errout;
			}
			break;
		case RTM_DELTFILTER:
			err = tp->ops->delete(tp, fh);
			if (err == 0)
			if (err == 0) {
				tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
				if (tcf_destroy(tp, false)) {
					struct tcf_proto *next = rtnl_dereference(tp->next);

					RCU_INIT_POINTER(*back, next);
				}
			}
			goto errout;
		case RTM_GETTFILTER:
			err = tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
@@ -329,7 +335,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
		tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
	} else {
		if (tp_created)
			tcf_destroy(tp);
			tcf_destroy(tp, true);
	}

errout:
+5 −1
Original line number Diff line number Diff line
@@ -96,11 +96,14 @@ static void basic_delete_filter(struct rcu_head *head)
	kfree(f);
}

static void basic_destroy(struct tcf_proto *tp)
static bool basic_destroy(struct tcf_proto *tp, bool force)
{
	struct basic_head *head = rtnl_dereference(tp->root);
	struct basic_filter *f, *n;

	if (!force && !list_empty(&head->flist))
		return false;

	list_for_each_entry_safe(f, n, &head->flist, link) {
		list_del_rcu(&f->link);
		tcf_unbind_filter(tp, &f->res);
@@ -108,6 +111,7 @@ static void basic_destroy(struct tcf_proto *tp)
	}
	RCU_INIT_POINTER(tp->root, NULL);
	kfree_rcu(head, rcu);
	return true;
}

static int basic_delete(struct tcf_proto *tp, unsigned long arg)
+5 −1
Original line number Diff line number Diff line
@@ -137,11 +137,14 @@ static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg)
	return 0;
}

static void cls_bpf_destroy(struct tcf_proto *tp)
static bool cls_bpf_destroy(struct tcf_proto *tp, bool force)
{
	struct cls_bpf_head *head = rtnl_dereference(tp->root);
	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_del_rcu(&prog->link);
		tcf_unbind_filter(tp, &prog->res);
@@ -150,6 +153,7 @@ static void cls_bpf_destroy(struct tcf_proto *tp)

	RCU_INIT_POINTER(tp->root, NULL);
	kfree_rcu(head, rcu);
	return true;
}

static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
+5 −1
Original line number Diff line number Diff line
@@ -143,14 +143,18 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
	return err;
}

static void cls_cgroup_destroy(struct tcf_proto *tp)
static bool cls_cgroup_destroy(struct tcf_proto *tp, bool force)
{
	struct cls_cgroup_head *head = rtnl_dereference(tp->root);

	if (!force)
		return false;

	if (head) {
		RCU_INIT_POINTER(tp->root, NULL);
		call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
	}
	return true;
}

static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg)
Loading