aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCatalin Boie <util@deuroconsult.ro>2005-03-31 04:36:08 -0800
committerDavid S. Miller <davem@sunset.davemloft.net>2005-03-31 04:36:08 -0800
commit824391a1a90c63ee28e62eb1e9c49981b103a975 (patch)
tree31b0cb7fa92487f23dc72940b1d31188ec9d466b
parente6762bc7b42787313b20f23342a83e76ba570761 (diff)
downloadhistory-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.c41
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) {