vserver 2.0 rc7
[linux-2.6.git] / net / sched / sch_api.c
index 1071471..07977f8 100644 (file)
@@ -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) {
@@ -1262,6 +1289,7 @@ static int __init pktsched_init(void)
 
 subsys_initcall(pktsched_init);
 
+EXPORT_SYMBOL(qdisc_lookup);
 EXPORT_SYMBOL(qdisc_get_rtab);
 EXPORT_SYMBOL(qdisc_put_rtab);
 EXPORT_SYMBOL(register_qdisc);