aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/act_api.c45
-rw-r--r--net/sched/cls_api.c30
-rw-r--r--net/sched/cls_u32.c4
-rw-r--r--net/sched/sch_api.c112
-rw-r--r--net/sched/sch_generic.c15
5 files changed, 121 insertions, 85 deletions
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index d8e0171d9a4b..019045174fc3 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -668,7 +668,8 @@ nlmsg_failure:
}
static int
-act_get_notify(u32 pid, struct nlmsghdr *n, struct tc_action *a, int event)
+act_get_notify(struct net *net, u32 pid, struct nlmsghdr *n,
+ struct tc_action *a, int event)
{
struct sk_buff *skb;
@@ -680,7 +681,7 @@ act_get_notify(u32 pid, struct nlmsghdr *n, struct tc_action *a, int event)
return -EINVAL;
}
- return rtnl_unicast(skb, &init_net, pid);
+ return rtnl_unicast(skb, net, pid);
}
static struct tc_action *
@@ -750,7 +751,8 @@ static struct tc_action *create_a(int i)
return act;
}
-static int tca_action_flush(struct nlattr *nla, struct nlmsghdr *n, u32 pid)
+static int tca_action_flush(struct net *net, struct nlattr *nla,
+ struct nlmsghdr *n, u32 pid)
{
struct sk_buff *skb;
unsigned char *b;
@@ -809,7 +811,7 @@ static int tca_action_flush(struct nlattr *nla, struct nlmsghdr *n, u32 pid)
nlh->nlmsg_flags |= NLM_F_ROOT;
module_put(a->ops->owner);
kfree(a);
- err = rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
+ err = rtnetlink_send(skb, net, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
if (err > 0)
return 0;
@@ -826,7 +828,8 @@ noflush_out:
}
static int
-tca_action_gd(struct nlattr *nla, struct nlmsghdr *n, u32 pid, int event)
+tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
+ u32 pid, int event)
{
int i, ret;
struct nlattr *tb[TCA_ACT_MAX_PRIO+1];
@@ -838,7 +841,7 @@ tca_action_gd(struct nlattr *nla, struct nlmsghdr *n, u32 pid, int event)
if (event == RTM_DELACTION && n->nlmsg_flags&NLM_F_ROOT) {
if (tb[1] != NULL)
- return tca_action_flush(tb[1], n, pid);
+ return tca_action_flush(net, tb[1], n, pid);
else
return -EINVAL;
}
@@ -859,7 +862,7 @@ tca_action_gd(struct nlattr *nla, struct nlmsghdr *n, u32 pid, int event)
}
if (event == RTM_GETACTION)
- ret = act_get_notify(pid, n, head, event);
+ ret = act_get_notify(net, pid, n, head, event);
else { /* delete */
struct sk_buff *skb;
@@ -878,7 +881,7 @@ tca_action_gd(struct nlattr *nla, struct nlmsghdr *n, u32 pid, int event)
/* now do the delete */
tcf_action_destroy(head, 0);
- ret = rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC,
+ ret = rtnetlink_send(skb, net, pid, RTNLGRP_TC,
n->nlmsg_flags&NLM_F_ECHO);
if (ret > 0)
return 0;
@@ -889,8 +892,8 @@ err:
return ret;
}
-static int tcf_add_notify(struct tc_action *a, u32 pid, u32 seq, int event,
- u16 flags)
+static int tcf_add_notify(struct net *net, struct tc_action *a,
+ u32 pid, u32 seq, int event, u16 flags)
{
struct tcamsg *t;
struct nlmsghdr *nlh;
@@ -923,7 +926,7 @@ static int tcf_add_notify(struct tc_action *a, u32 pid, u32 seq, int event,
nlh->nlmsg_len = skb_tail_pointer(skb) - b;
NETLINK_CB(skb).dst_group = RTNLGRP_TC;
- err = rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC, flags&NLM_F_ECHO);
+ err = rtnetlink_send(skb, net, pid, RTNLGRP_TC, flags&NLM_F_ECHO);
if (err > 0)
err = 0;
return err;
@@ -936,7 +939,8 @@ nlmsg_failure:
static int
-tcf_action_add(struct nlattr *nla, struct nlmsghdr *n, u32 pid, int ovr)
+tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
+ u32 pid, int ovr)
{
int ret = 0;
struct tc_action *act;
@@ -954,7 +958,7 @@ tcf_action_add(struct nlattr *nla, struct nlmsghdr *n, u32 pid, int ovr)
/* dump then free all the actions after update; inserted policy
* stays intact
* */
- ret = tcf_add_notify(act, pid, seq, RTM_NEWACTION, n->nlmsg_flags);
+ ret = tcf_add_notify(net, act, pid, seq, RTM_NEWACTION, n->nlmsg_flags);
for (a = act; a; a = act) {
act = a->next;
kfree(a);
@@ -970,9 +974,6 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
u32 pid = skb ? NETLINK_CB(skb).pid : 0;
int ret = 0, ovr = 0;
- if (!net_eq(net, &init_net))
- return -EINVAL;
-
ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
if (ret < 0)
return ret;
@@ -995,15 +996,17 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (n->nlmsg_flags&NLM_F_REPLACE)
ovr = 1;
replay:
- ret = tcf_action_add(tca[TCA_ACT_TAB], n, pid, ovr);
+ ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, pid, ovr);
if (ret == -EAGAIN)
goto replay;
break;
case RTM_DELACTION:
- ret = tca_action_gd(tca[TCA_ACT_TAB], n, pid, RTM_DELACTION);
+ ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
+ pid, RTM_DELACTION);
break;
case RTM_GETACTION:
- ret = tca_action_gd(tca[TCA_ACT_TAB], n, pid, RTM_GETACTION);
+ ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
+ pid, RTM_GETACTION);
break;
default:
BUG();
@@ -1043,7 +1046,6 @@ find_dump_kind(const struct nlmsghdr *n)
static int
tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = sock_net(skb->sk);
struct nlmsghdr *nlh;
unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;
@@ -1053,9 +1055,6 @@ tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh);
struct nlattr *kind = find_dump_kind(cb->nlh);
- if (!net_eq(net, &init_net))
- return 0;
-
if (kind == NULL) {
printk("tc_dump_action: action bad kind\n");
return 0;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index f082b27ff46d..5fd0c28ef79a 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -99,8 +99,9 @@ out:
}
EXPORT_SYMBOL(unregister_tcf_proto_ops);
-static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr *n,
- struct tcf_proto *tp, unsigned long fh, int event);
+static int tfilter_notify(struct net *net, struct sk_buff *oskb,
+ struct nlmsghdr *n, struct tcf_proto *tp,
+ unsigned long fh, int event);
/* Select new prio value from the range, managed by kernel. */
@@ -138,9 +139,6 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
int err;
int tp_created = 0;
- if (!net_eq(net, &init_net))
- return -EINVAL;
-
replay:
t = NLMSG_DATA(n);
protocol = TC_H_MIN(t->tcm_info);
@@ -159,7 +157,7 @@ replay:
/* Find head of filter chain. */
/* Find link */
- dev = __dev_get_by_index(&init_net, t->tcm_ifindex);
+ dev = __dev_get_by_index(net, t->tcm_ifindex);
if (dev == NULL)
return -ENODEV;
@@ -283,7 +281,7 @@ replay:
*back = tp->next;
spin_unlock_bh(root_lock);
- tfilter_notify(skb, n, tp, fh, RTM_DELTFILTER);
+ tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
tcf_destroy(tp);
err = 0;
goto errout;
@@ -306,10 +304,10 @@ replay:
case RTM_DELTFILTER:
err = tp->ops->delete(tp, fh);
if (err == 0)
- tfilter_notify(skb, n, tp, fh, RTM_DELTFILTER);
+ tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
goto errout;
case RTM_GETTFILTER:
- err = tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);
+ err = tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
goto errout;
default:
err = -EINVAL;
@@ -325,7 +323,7 @@ replay:
*back = tp;
spin_unlock_bh(root_lock);
}
- tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);
+ tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
} else {
if (tp_created)
tcf_destroy(tp);
@@ -371,8 +369,9 @@ nla_put_failure:
return -1;
}
-static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr *n,
- struct tcf_proto *tp, unsigned long fh, int event)
+static int tfilter_notify(struct net *net, struct sk_buff *oskb,
+ struct nlmsghdr *n, struct tcf_proto *tp,
+ unsigned long fh, int event)
{
struct sk_buff *skb;
u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
@@ -386,7 +385,7 @@ static int tfilter_notify(struct sk_buff *oskb, struct nlmsghdr *n,
return -EINVAL;
}
- return rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC,
+ return rtnetlink_send(skb, net, pid, RTNLGRP_TC,
n->nlmsg_flags & NLM_F_ECHO);
}
@@ -419,12 +418,9 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
const struct Qdisc_class_ops *cops;
struct tcf_dump_args arg;
- if (!net_eq(net, &init_net))
- return 0;
-
if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
return skb->len;
- if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
+ if ((dev = __dev_get_by_index(net, tcm->tcm_ifindex)) == NULL)
return skb->len;
if (!tcm->tcm_parent)
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 17c5dfc67320..593eac056e8d 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -773,10 +773,10 @@ static int __init init_u32(void)
printk(" Performance counters on\n");
#endif
#ifdef CONFIG_NET_CLS_IND
- printk(" input device check on \n");
+ printk(" input device check on\n");
#endif
#ifdef CONFIG_NET_CLS_ACT
- printk(" Actions configured \n");
+ printk(" Actions configured\n");
#endif
return register_tcf_proto_ops(&cls_u32_ops);
}
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 145268ca57cf..9839b26674f4 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -35,10 +35,12 @@
#include <net/netlink.h>
#include <net/pkt_sched.h>
-static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n, u32 clid,
+static int qdisc_notify(struct net *net, struct sk_buff *oskb,
+ struct nlmsghdr *n, u32 clid,
struct Qdisc *old, struct Qdisc *new);
-static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,
- struct Qdisc *q, unsigned long cl, int event);
+static int tclass_notify(struct net *net, struct sk_buff *oskb,
+ struct nlmsghdr *n, struct Qdisc *q,
+ unsigned long cl, int event);
/*
@@ -639,11 +641,12 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
}
EXPORT_SYMBOL(qdisc_tree_decrease_qlen);
-static void notify_and_destroy(struct sk_buff *skb, struct nlmsghdr *n, u32 clid,
+static void notify_and_destroy(struct net *net, struct sk_buff *skb,
+ struct nlmsghdr *n, u32 clid,
struct Qdisc *old, struct Qdisc *new)
{
if (new || old)
- qdisc_notify(skb, n, clid, old, new);
+ qdisc_notify(net, skb, n, clid, old, new);
if (old)
qdisc_destroy(old);
@@ -663,6 +666,7 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
struct Qdisc *new, struct Qdisc *old)
{
struct Qdisc *q = old;
+ struct net *net = dev_net(dev);
int err = 0;
if (parent == NULL) {
@@ -699,12 +703,13 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
}
if (!ingress) {
- notify_and_destroy(skb, n, classid, dev->qdisc, new);
+ notify_and_destroy(net, skb, n, classid,
+ dev->qdisc, new);
if (new && !new->ops->attach)
atomic_inc(&new->refcnt);
dev->qdisc = new ? : &noop_qdisc;
} else {
- notify_and_destroy(skb, n, classid, old, new);
+ notify_and_destroy(net, skb, n, classid, old, new);
}
if (dev->flags & IFF_UP)
@@ -722,7 +727,7 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
err = -ENOENT;
}
if (!err)
- notify_and_destroy(skb, n, classid, old, new);
+ notify_and_destroy(net, skb, n, classid, old, new);
}
return err;
}
@@ -948,10 +953,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
struct Qdisc *p = NULL;
int err;
- if (!net_eq(net, &init_net))
- return -EINVAL;
-
- if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
+ if ((dev = __dev_get_by_index(net, tcm->tcm_ifindex)) == NULL)
return -ENODEV;
err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
@@ -991,7 +993,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if ((err = qdisc_graft(dev, p, skb, n, clid, NULL, q)) != 0)
return err;
} else {
- qdisc_notify(skb, n, clid, NULL, q);
+ qdisc_notify(net, skb, n, clid, NULL, q);
}
return 0;
}
@@ -1010,16 +1012,13 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
struct Qdisc *q, *p;
int err;
- if (!net_eq(net, &init_net))
- return -EINVAL;
-
replay:
/* Reinit, just in case something touches this. */
tcm = NLMSG_DATA(n);
clid = tcm->tcm_parent;
q = p = NULL;
- if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
+ if ((dev = __dev_get_by_index(net, tcm->tcm_ifindex)) == NULL)
return -ENODEV;
err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
@@ -1106,7 +1105,7 @@ replay:
return -EINVAL;
err = qdisc_change(q, tca);
if (err == 0)
- qdisc_notify(skb, n, clid, NULL, q);
+ qdisc_notify(net, skb, n, clid, NULL, q);
return err;
create_n_graft:
@@ -1196,8 +1195,9 @@ nla_put_failure:
return -1;
}
-static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n,
- u32 clid, struct Qdisc *old, struct Qdisc *new)
+static int qdisc_notify(struct net *net, struct sk_buff *oskb,
+ struct nlmsghdr *n, u32 clid,
+ struct Qdisc *old, struct Qdisc *new)
{
struct sk_buff *skb;
u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
@@ -1216,7 +1216,7 @@ static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n,
}
if (skb->len)
- return rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
+ return rtnetlink_send(skb, net, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
err_out:
kfree_skb(skb);
@@ -1275,15 +1275,12 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
int s_idx, s_q_idx;
struct net_device *dev;
- if (!net_eq(net, &init_net))
- return 0;
-
s_idx = cb->args[0];
s_q_idx = q_idx = cb->args[1];
rcu_read_lock();
idx = 0;
- for_each_netdev_rcu(&init_net, dev) {
+ for_each_netdev_rcu(net, dev) {
struct netdev_queue *dev_queue;
if (idx < s_idx)
@@ -1335,10 +1332,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
u32 qid = TC_H_MAJ(clid);
int err;
- if (!net_eq(net, &init_net))
- return -EINVAL;
-
- if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
+ if ((dev = __dev_get_by_index(net, tcm->tcm_ifindex)) == NULL)
return -ENODEV;
err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
@@ -1419,10 +1413,10 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (cops->delete)
err = cops->delete(q, cl);
if (err == 0)
- tclass_notify(skb, n, q, cl, RTM_DELTCLASS);
+ tclass_notify(net, skb, n, q, cl, RTM_DELTCLASS);
goto out;
case RTM_GETTCLASS:
- err = tclass_notify(skb, n, q, cl, RTM_NEWTCLASS);
+ err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS);
goto out;
default:
err = -EINVAL;
@@ -1435,7 +1429,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
if (cops->change)
err = cops->change(q, clid, pid, tca, &new_cl);
if (err == 0)
- tclass_notify(skb, n, q, new_cl, RTM_NEWTCLASS);
+ tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS);
out:
if (cl)
@@ -1487,8 +1481,9 @@ nla_put_failure:
return -1;
}
-static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,
- struct Qdisc *q, unsigned long cl, int event)
+static int tclass_notify(struct net *net, struct sk_buff *oskb,
+ struct nlmsghdr *n, struct Qdisc *q,
+ unsigned long cl, int event)
{
struct sk_buff *skb;
u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
@@ -1502,7 +1497,7 @@ static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,
return -EINVAL;
}
- return rtnetlink_send(skb, &init_net, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
+ return rtnetlink_send(skb, net, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
}
struct qdisc_dump_args
@@ -1577,12 +1572,9 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb)
struct net_device *dev;
int t, s_t;
- if (!net_eq(net, &init_net))
- return 0;
-
if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm)))
return 0;
- if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL)
+ if ((dev = dev_get_by_index(net, tcm->tcm_ifindex)) == NULL)
return 0;
s_t = cb->args[0];
@@ -1692,7 +1684,7 @@ static int psched_show(struct seq_file *seq, void *v)
static int psched_open(struct inode *inode, struct file *file)
{
- return single_open(file, psched_show, PDE(inode)->data);
+ return single_open(file, psched_show, NULL);
}
static const struct file_operations psched_fops = {
@@ -1702,15 +1694,53 @@ static const struct file_operations psched_fops = {
.llseek = seq_lseek,
.release = single_release,
};
+
+static int __net_init psched_net_init(struct net *net)
+{
+ struct proc_dir_entry *e;
+
+ e = proc_net_fops_create(net, "psched", 0, &psched_fops);
+ if (e == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __net_exit psched_net_exit(struct net *net)
+{
+ proc_net_remove(net, "psched");
+}
+#else
+static int __net_init psched_net_init(struct net *net)
+{
+ return 0;
+}
+
+static void __net_exit psched_net_exit(struct net *net)
+{
+}
#endif
+static struct pernet_operations psched_net_ops = {
+ .init = psched_net_init,
+ .exit = psched_net_exit,
+};
+
static int __init pktsched_init(void)
{
+ int err;
+
+ err = register_pernet_subsys(&psched_net_ops);
+ if (err) {
+ printk(KERN_ERR "pktsched_init: "
+ "cannot initialize per netns operations\n");
+ return err;
+ }
+
register_qdisc(&pfifo_qdisc_ops);
register_qdisc(&bfifo_qdisc_ops);
register_qdisc(&pfifo_head_drop_qdisc_ops);
register_qdisc(&mq_qdisc_ops);
- proc_net_fops_create(&init_net, "psched", 0, &psched_fops);
rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL);
rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL);
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index ff4dd53eeff0..aeddabfb8e4e 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -529,7 +529,7 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
unsigned int size;
int err = -ENOBUFS;
- /* ensure that the Qdisc and the private data are 32-byte aligned */
+ /* ensure that the Qdisc and the private data are 64-byte aligned */
size = QDISC_ALIGN(sizeof(*sch));
size += ops->priv_size + (QDISC_ALIGNTO - 1);
@@ -591,6 +591,13 @@ void qdisc_reset(struct Qdisc *qdisc)
}
EXPORT_SYMBOL(qdisc_reset);
+static void qdisc_rcu_free(struct rcu_head *head)
+{
+ struct Qdisc *qdisc = container_of(head, struct Qdisc, rcu_head);
+
+ kfree((char *) qdisc - qdisc->padded);
+}
+
void qdisc_destroy(struct Qdisc *qdisc)
{
const struct Qdisc_ops *ops = qdisc->ops;
@@ -614,7 +621,11 @@ void qdisc_destroy(struct Qdisc *qdisc)
dev_put(qdisc_dev(qdisc));
kfree_skb(qdisc->gso_skb);
- kfree((char *) qdisc - qdisc->padded);
+ /*
+ * gen_estimator est_timer() might access qdisc->q.lock,
+ * wait a RCU grace period before freeing qdisc.
+ */
+ call_rcu(&qdisc->rcu_head, qdisc_rcu_free);
}
EXPORT_SYMBOL(qdisc_destroy);