Merge tag 'acpi-5.1-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[sfrench/cifs-2.6.git] / net / sunrpc / svc_xprt.c
index 4eb8fbf2508dc767e7a35c00a885a46146ee7e35..61530b1b7754e53bbc08e54031da2bf9d94090b4 100644 (file)
@@ -357,15 +357,29 @@ static void svc_xprt_release_slot(struct svc_rqst *rqstp)
        struct svc_xprt *xprt = rqstp->rq_xprt;
        if (test_and_clear_bit(RQ_DATA, &rqstp->rq_flags)) {
                atomic_dec(&xprt->xpt_nr_rqsts);
+               smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */
                svc_xprt_enqueue(xprt);
        }
 }
 
-static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt)
+static bool svc_xprt_ready(struct svc_xprt *xprt)
 {
-       if (xprt->xpt_flags & ((1<<XPT_CONN)|(1<<XPT_CLOSE)))
+       unsigned long xpt_flags;
+
+       /*
+        * If another cpu has recently updated xpt_flags,
+        * sk_sock->flags, xpt_reserved, or xpt_nr_rqsts, we need to
+        * know about it; otherwise it's possible that both that cpu and
+        * this one could call svc_xprt_enqueue() without either
+        * svc_xprt_enqueue() recognizing that the conditions below
+        * are satisfied, and we could stall indefinitely:
+        */
+       smp_rmb();
+       xpt_flags = READ_ONCE(xprt->xpt_flags);
+
+       if (xpt_flags & (BIT(XPT_CONN) | BIT(XPT_CLOSE)))
                return true;
-       if (xprt->xpt_flags & ((1<<XPT_DATA)|(1<<XPT_DEFERRED))) {
+       if (xpt_flags & (BIT(XPT_DATA) | BIT(XPT_DEFERRED))) {
                if (xprt->xpt_ops->xpo_has_wspace(xprt) &&
                    svc_xprt_slots_in_range(xprt))
                        return true;
@@ -381,7 +395,7 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt)
        struct svc_rqst *rqstp = NULL;
        int cpu;
 
-       if (!svc_xprt_has_something_to_do(xprt))
+       if (!svc_xprt_ready(xprt))
                return;
 
        /* Mark transport as busy. It will remain in this state until
@@ -475,7 +489,7 @@ void svc_reserve(struct svc_rqst *rqstp, int space)
        if (xprt && space < rqstp->rq_reserved) {
                atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved);
                rqstp->rq_reserved = space;
-
+               smp_wmb(); /* See smp_rmb() in svc_xprt_ready() */
                svc_xprt_enqueue(xprt);
        }
 }