ChangeSet 1.2325, 2005/03/31 20:36:08-08:00, [EMAIL PROTECTED]

        [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 <[EMAIL PROTECTED]>
        Signed-off-by: David S. Miller <[EMAIL PROTECTED]>



 sch_api.c |   41 ++++++++++++++++++++++++++++++++++-------
 1 files changed, 34 insertions(+), 7 deletions(-)


diff -Nru a/net/sched/sch_api.c b/net/sched/sch_api.c
--- a/net/sched/sch_api.c       2005-04-01 09:09:15 -08:00
+++ b/net/sched/sch_api.c       2005-04-01 09:09:15 -08:00
@@ -406,11 +406,29 @@
 
        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_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 @@
                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) {
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to