pNFS: Handle NFS4ERR_OLD_STATEID correctly in LAYOUTSTAT calls
[linux-2.6-block.git] / fs / nfs / pnfs.c
index 49e952968edee1d797fc4520d458ff2c87be236f..bf98f1b2595fb12a7198dac3d4d261cf067cdbeb 100644 (file)
@@ -334,14 +334,17 @@ pnfs_layout_io_test_failed(struct pnfs_layout_hdr *lo, u32 iomode)
 }
 
 static void
-init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg)
+pnfs_init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg,
+               const struct pnfs_layout_range *range,
+               const nfs4_stateid *stateid)
 {
        INIT_LIST_HEAD(&lseg->pls_list);
        INIT_LIST_HEAD(&lseg->pls_lc_list);
        atomic_set(&lseg->pls_refcount, 1);
-       smp_mb();
        set_bit(NFS_LSEG_VALID, &lseg->pls_flags);
        lseg->pls_layout = lo;
+       lseg->pls_range = *range;
+       lseg->pls_seq = be32_to_cpu(stateid->seqid);
 }
 
 static void pnfs_free_lseg(struct pnfs_layout_segment *lseg)
@@ -1040,7 +1043,6 @@ _pnfs_return_layout(struct inode *ino)
                goto out_put_layout_hdr;
        }
 
-       set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
        send = pnfs_prepare_layoutreturn(lo, &stateid, NULL);
        spin_unlock(&ino->i_lock);
        pnfs_free_lseg_list(&tmp_list);
@@ -1531,7 +1533,7 @@ pnfs_update_layout(struct inode *ino,
        struct pnfs_layout_segment *lseg = NULL;
        nfs4_stateid stateid;
        long timeout = 0;
-       unsigned long giveup = jiffies + rpc_get_timeout(server->client);
+       unsigned long giveup = jiffies + (clp->cl_lease_time << 1);
        bool first;
 
        if (!pnfs_enabled_sb(NFS_SERVER(ino))) {
@@ -1671,33 +1673,44 @@ 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:
+               case -EBUSY:
                        if (time_after(jiffies, giveup))
                                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;
+                       break;
+               case -ERECALLCONFLICT:
+                       /* Huh? We hold no layouts, how is there a recall? */
+                       if (first) {
+                               lseg = NULL;
+                               break;
                        }
+                       /* Destroy the existing layout and start over */
+                       if (time_after(jiffies, giveup))
+                               pnfs_destroy_layout(NFS_I(ino));
                        /* Fallthrough */
+               case -EAGAIN:
+                       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);
@@ -1761,9 +1774,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
                return lseg;
        }
 
-       init_lseg(lo, lseg);
-       lseg->pls_range = res->range;
-       lseg->pls_seq = be32_to_cpu(res->stateid.seqid);
+       pnfs_init_lseg(lo, lseg, &res->range, &res->stateid);
 
        spin_lock(&ino->i_lock);
        if (pnfs_layoutgets_blocked(lo)) {
@@ -2409,7 +2420,10 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync)
        nfs_fattr_init(&data->fattr);
        data->args.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask;
        data->res.fattr = &data->fattr;
-       data->args.lastbytewritten = end_pos - 1;
+       if (end_pos != 0)
+               data->args.lastbytewritten = end_pos - 1;
+       else
+               data->args.lastbytewritten = U64_MAX;
        data->res.server = NFS_SERVER(inode);
 
        if (ld->prepare_layoutcommit) {
@@ -2496,7 +2510,6 @@ pnfs_report_layoutstat(struct inode *inode, gfp_t gfp_flags)
 
        data->args.fh = NFS_FH(inode);
        data->args.inode = inode;
-       nfs4_stateid_copy(&data->args.stateid, &hdr->plh_stateid);
        status = ld->prepare_layoutstats(&data->args);
        if (status)
                goto out_free;