]> Pileus Git - ~andy/linux/blobdiff - net/sunrpc/svc_xprt.c
Merge tag v3.9-rc1 into for-3.9/upstream-fixes
[~andy/linux] / net / sunrpc / svc_xprt.c
index 11a33c87484873a96af1004aa81b566556ea2381..80a6640f329bab991859e032fda658a871e65ca5 100644 (file)
@@ -955,21 +955,24 @@ void svc_close_xprt(struct svc_xprt *xprt)
 }
 EXPORT_SYMBOL_GPL(svc_close_xprt);
 
-static void svc_close_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net)
+static int svc_close_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net)
 {
        struct svc_xprt *xprt;
+       int ret = 0;
 
        spin_lock(&serv->sv_lock);
        list_for_each_entry(xprt, xprt_list, xpt_list) {
                if (xprt->xpt_net != net)
                        continue;
+               ret++;
                set_bit(XPT_CLOSE, &xprt->xpt_flags);
-               set_bit(XPT_BUSY, &xprt->xpt_flags);
+               svc_xprt_enqueue(xprt);
        }
        spin_unlock(&serv->sv_lock);
+       return ret;
 }
 
-static void svc_clear_pools(struct svc_serv *serv, struct net *net)
+static struct svc_xprt *svc_dequeue_net(struct svc_serv *serv, struct net *net)
 {
        struct svc_pool *pool;
        struct svc_xprt *xprt;
@@ -984,42 +987,46 @@ static void svc_clear_pools(struct svc_serv *serv, struct net *net)
                        if (xprt->xpt_net != net)
                                continue;
                        list_del_init(&xprt->xpt_ready);
+                       spin_unlock_bh(&pool->sp_lock);
+                       return xprt;
                }
                spin_unlock_bh(&pool->sp_lock);
        }
+       return NULL;
 }
 
-static void svc_clear_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net)
+static void svc_clean_up_xprts(struct svc_serv *serv, struct net *net)
 {
        struct svc_xprt *xprt;
-       struct svc_xprt *tmp;
-       LIST_HEAD(victims);
-
-       spin_lock(&serv->sv_lock);
-       list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) {
-               if (xprt->xpt_net != net)
-                       continue;
-               list_move(&xprt->xpt_list, &victims);
-       }
-       spin_unlock(&serv->sv_lock);
 
-       list_for_each_entry_safe(xprt, tmp, &victims, xpt_list)
+       while ((xprt = svc_dequeue_net(serv, net))) {
+               set_bit(XPT_CLOSE, &xprt->xpt_flags);
                svc_delete_xprt(xprt);
+       }
 }
 
+/*
+ * Server threads may still be running (especially in the case where the
+ * service is still running in other network namespaces).
+ *
+ * So we shut down sockets the same way we would on a running server, by
+ * setting XPT_CLOSE, enqueuing, and letting a thread pick it up to do
+ * the close.  In the case there are no such other threads,
+ * threads running, svc_clean_up_xprts() does a simple version of a
+ * server's main event loop, and in the case where there are other
+ * threads, we may need to wait a little while and then check again to
+ * see if they're done.
+ */
 void svc_close_net(struct svc_serv *serv, struct net *net)
 {
-       svc_close_list(serv, &serv->sv_tempsocks, net);
-       svc_close_list(serv, &serv->sv_permsocks, net);
+       int delay = 0;
 
-       svc_clear_pools(serv, net);
-       /*
-        * At this point the sp_sockets lists will stay empty, since
-        * svc_xprt_enqueue will not add new entries without taking the
-        * sp_lock and checking XPT_BUSY.
-        */
-       svc_clear_list(serv, &serv->sv_tempsocks, net);
-       svc_clear_list(serv, &serv->sv_permsocks, net);
+       while (svc_close_list(serv, &serv->sv_permsocks, net) +
+              svc_close_list(serv, &serv->sv_tempsocks, net)) {
+
+               svc_clean_up_xprts(serv, net);
+               msleep(delay++);
+       }
 }
 
 /*