NFSv4: Fix a sleep in atomic context in nfs4_callback_sequence()
authorTrond Myklebust <trondmy@gmail.com>
Tue, 14 Aug 2018 21:55:56 +0000 (17:55 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 15 Aug 2018 16:05:15 +0000 (12:05 -0400)
We must drop the lock before we can sleep in referring_call_exists().

Reported-by: Jia-Ju Bai <baijiaju1990@gmail.com>
Fixes: 045d2a6d076a ("NFSv4.1: Delay callback processing...")
Cc: stable@vger.kernel.org # v4.9+
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/callback_proc.c

index cd73364..fa515d5 100644 (file)
@@ -442,11 +442,14 @@ validate_seqid(const struct nfs4_slot_table *tbl, const struct nfs4_slot *slot,
  * a match.  If the slot is in use and the sequence numbers match, the
  * client is still waiting for a response to the original request.
  */
-static bool referring_call_exists(struct nfs_client *clp,
+static int referring_call_exists(struct nfs_client *clp,
                                  uint32_t nrclists,
-                                 struct referring_call_list *rclists)
+                                 struct referring_call_list *rclists,
+                                 spinlock_t *lock)
+       __releases(lock)
+       __acquires(lock)
 {
-       bool status = false;
+       int status = 0;
        int i, j;
        struct nfs4_session *session;
        struct nfs4_slot_table *tbl;
@@ -469,8 +472,10 @@ static bool referring_call_exists(struct nfs_client *clp,
 
                for (j = 0; j < rclist->rcl_nrefcalls; j++) {
                        ref = &rclist->rcl_refcalls[j];
+                       spin_unlock(lock);
                        status = nfs4_slot_wait_on_seqid(tbl, ref->rc_slotid,
                                        ref->rc_sequenceid, HZ >> 1) < 0;
+                       spin_lock(lock);
                        if (status)
                                goto out;
                }
@@ -547,7 +552,8 @@ __be32 nfs4_callback_sequence(void *argp, void *resp,
         * related callback was received before the response to the original
         * call.
         */
-       if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
+       if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists,
+                               &tbl->slot_tbl_lock) < 0) {
                status = htonl(NFS4ERR_DELAY);
                goto out_unlock;
        }