pNFS: Fix post-layoutget error handling in pnfs_update_layout()
authorTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 14 Jul 2016 22:34:12 +0000 (18:34 -0400)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Tue, 19 Jul 2016 20:22:47 +0000 (16:22 -0400)
The non-retry error path is currently broken and ends up releasing the
reference to the layout twice. It also can end up clearing the
NFS_LAYOUT_FIRST_LAYOUTGET flag twice, causing a race.

In addition, the retry path will fail to decrement the plh_outstanding
counter.

Fixes: 183d9e7b112aa ("pnfs: rework LAYOUTGET retry handling")
Cc: stable@vger.kernel.org # 4.7
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
fs/nfs/pnfs.c

index 0fbe734cc38cb8d27ba2c8efaf985a676f4d0cd8..563f131c9abe953be046ed4350cc3fd56864e332 100644 (file)
@@ -1645,6 +1645,7 @@ lookup_again:
        lseg = send_layoutget(lo, ctx, &stateid, &arg, &timeout, gfp_flags);
        trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
                                 PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET);
+       atomic_dec(&lo->plh_outstanding);
        if (IS_ERR(lseg)) {
                switch(PTR_ERR(lseg)) {
                case -ERECALLCONFLICT:
@@ -1652,26 +1653,26 @@ lookup_again:
                                lseg = NULL;
                        /* Fallthrough */
                case -EAGAIN:
-                       pnfs_put_layout_hdr(lo);
-                       if (first)
-                               pnfs_clear_first_layoutget(lo);
-                       if (lseg) {
-                               trace_pnfs_update_layout(ino, pos, count,
-                                       iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY);
-                               goto lookup_again;
-                       }
-                       /* Fallthrough */
+                       break;
                default:
                        if (!nfs_error_is_fatal(PTR_ERR(lseg))) {
                                pnfs_layout_clear_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
                                lseg = NULL;
                        }
+                       goto out_put_layout_hdr;
+               }
+               if (lseg) {
+                       if (first)
+                               pnfs_clear_first_layoutget(lo);
+                       trace_pnfs_update_layout(ino, pos, count,
+                               iomode, lo, lseg, PNFS_UPDATE_LAYOUT_RETRY);
+                       pnfs_put_layout_hdr(lo);
+                       goto lookup_again;
                }
        } else {
                pnfs_layout_clear_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
        }
 
-       atomic_dec(&lo->plh_outstanding);
 out_put_layout_hdr:
        if (first)
                pnfs_clear_first_layoutget(lo);