diff options
author | Catalin Boie <util@deuroconsult.ro> | 2005-03-31 04:36:08 -0800 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-03-31 04:36:08 -0800 |
commit | 824391a1a90c63ee28e62eb1e9c49981b103a975 (patch) | |
tree | 31b0cb7fa92487f23dc72940b1d31188ec9d466b | |
parent | e6762bc7b42787313b20f23342a83e76ba570761 (diff) | |
download | history-824391a1a90c63ee28e62eb1e9c49981b103a975.tar.gz |
[PKT_SCHED]: Fix deadlock in sch_api.c
While qdisc_create() is holding the rtnl_sem, it may try
to load modules which in turn may try to register devices
(teql is one such case), and it will then hang trying to
retake the rtnl_sem.
Signed-off-by: Catalin(ux aka Dino) BOIE <catab@umbrella.ro>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/sched/sch_api.c | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 10714716d4c7bf..4323a74eea30bc 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -406,11 +406,29 @@ qdisc_create(struct net_device *dev, u32 handle, struct rtattr **tca, int *errp) ops = qdisc_lookup_ops(kind); #ifdef CONFIG_KMOD - if (ops==NULL && tca[TCA_KIND-1] != NULL) { + if (ops == NULL && kind != NULL) { char name[IFNAMSIZ]; if (rtattr_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) { + /* We dropped the RTNL semaphore in order to + * perform the module load. So, even if we + * succeeded in loading the module we have to + * tell the caller to replay the request. We + * indicate this using -EAGAIN. + * We replay the request because the device may + * go away in the mean time. + */ + rtnl_unlock(); request_module("sch_%s", name); + rtnl_lock(); ops = qdisc_lookup_ops(kind); + if (ops != NULL) { + /* We will try again qdisc_lookup_ops, + * so don't keep a reference. + */ + module_put(ops->owner); + err = -EAGAIN; + goto err_out; + } } } #endif @@ -606,14 +624,20 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { - struct tcmsg *tcm = NLMSG_DATA(n); - struct rtattr **tca = arg; + struct tcmsg *tcm; + struct rtattr **tca; struct net_device *dev; - u32 clid = tcm->tcm_parent; - struct Qdisc *q = NULL; - struct Qdisc *p = NULL; + u32 clid; + struct Qdisc *q, *p; int err; +replay: + /* Reinit, just in case something touches this. */ + tcm = NLMSG_DATA(n); + tca = arg; + clid = tcm->tcm_parent; + q = p = NULL; + if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL) return -ENODEV; @@ -707,8 +731,11 @@ create_n_graft: q = qdisc_create(dev, tcm->tcm_parent, tca, &err); else q = qdisc_create(dev, tcm->tcm_handle, tca, &err); - if (q == NULL) + if (q == NULL) { + if (err == -EAGAIN) + goto replay; return err; + } graft: if (1) { |