Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[deliverable/linux.git] / fs / nfs / pnfs.c
index 0fbe734cc38cb8d27ba2c8efaf985a676f4d0cd8..70806cae0d36bf71ca704d5d1ab7bc128d1455e9 100644 (file)
@@ -259,7 +259,7 @@ pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo)
  * is required.
  * Note that caller must hold inode->i_lock.
  */
-static int
+int
 pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
                struct list_head *lseg_list)
 {
@@ -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)
@@ -486,15 +489,6 @@ pnfs_lseg_range_intersecting(const struct pnfs_layout_range *l1,
               (end2 == NFS4_MAX_UINT64 || end2 > start1);
 }
 
-static bool
-should_free_lseg(const struct pnfs_layout_range *lseg_range,
-                const struct pnfs_layout_range *recall_range)
-{
-       return (recall_range->iomode == IOMODE_ANY ||
-               lseg_range->iomode == recall_range->iomode) &&
-              pnfs_lseg_range_intersecting(lseg_range, recall_range);
-}
-
 static bool pnfs_lseg_dec_and_remove_zero(struct pnfs_layout_segment *lseg,
                struct list_head *tmp_list)
 {
@@ -533,6 +527,27 @@ static bool pnfs_seqid_is_newer(u32 s1, u32 s2)
        return (s32)(s1 - s2) > 0;
 }
 
+static bool
+pnfs_should_free_range(const struct pnfs_layout_range *lseg_range,
+                const struct pnfs_layout_range *recall_range)
+{
+       return (recall_range->iomode == IOMODE_ANY ||
+               lseg_range->iomode == recall_range->iomode) &&
+              pnfs_lseg_range_intersecting(lseg_range, recall_range);
+}
+
+static bool
+pnfs_match_lseg_recall(const struct pnfs_layout_segment *lseg,
+               const struct pnfs_layout_range *recall_range,
+               u32 seq)
+{
+       if (seq != 0 && pnfs_seqid_is_newer(lseg->pls_seq, seq))
+               return false;
+       if (recall_range == NULL)
+               return true;
+       return pnfs_should_free_range(&lseg->pls_range, recall_range);
+}
+
 /**
  * pnfs_mark_matching_lsegs_invalid - tear down lsegs or mark them for later
  * @lo: layout header containing the lsegs
@@ -562,10 +577,7 @@ pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
        if (list_empty(&lo->plh_segs))
                return 0;
        list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
-               if (!recall_range ||
-                   should_free_lseg(&lseg->pls_range, recall_range)) {
-                       if (seq && pnfs_seqid_is_newer(lseg->pls_seq, seq))
-                               continue;
+               if (pnfs_match_lseg_recall(lseg, recall_range, seq)) {
                        dprintk("%s: freeing lseg %p iomode %d seq %u"
                                "offset %llu length %llu\n", __func__,
                                lseg, lseg->pls_range.iomode, lseg->pls_seq,
@@ -761,24 +773,25 @@ void
 pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new,
                        bool update_barrier)
 {
-       u32 oldseq, newseq, new_barrier;
-       int empty = list_empty(&lo->plh_segs);
+       u32 oldseq, newseq, new_barrier = 0;
+       bool invalid = !pnfs_layout_is_valid(lo);
 
        oldseq = be32_to_cpu(lo->plh_stateid.seqid);
        newseq = be32_to_cpu(new->seqid);
-       if (empty || pnfs_seqid_is_newer(newseq, oldseq)) {
+       if (invalid || pnfs_seqid_is_newer(newseq, oldseq)) {
                nfs4_stateid_copy(&lo->plh_stateid, new);
-               if (update_barrier) {
-                       new_barrier = be32_to_cpu(new->seqid);
-               } else {
-                       /* Because of wraparound, we want to keep the barrier
-                        * "close" to the current seqids.
-                        */
-                       new_barrier = newseq - atomic_read(&lo->plh_outstanding);
-               }
-               if (empty || pnfs_seqid_is_newer(new_barrier, lo->plh_barrier))
-                       lo->plh_barrier = new_barrier;
+               /*
+                * Because of wraparound, we want to keep the barrier
+                * "close" to the current seqids.
+                */
+               new_barrier = newseq - atomic_read(&lo->plh_outstanding);
        }
+       if (update_barrier)
+               new_barrier = be32_to_cpu(new->seqid);
+       else if (new_barrier == 0)
+               return;
+       if (invalid || pnfs_seqid_is_newer(new_barrier, lo->plh_barrier))
+               lo->plh_barrier = new_barrier;
 }
 
 static bool
@@ -873,15 +886,37 @@ void pnfs_clear_layoutreturn_waitbit(struct pnfs_layout_hdr *lo)
        rpc_wake_up(&NFS_SERVER(lo->plh_inode)->roc_rpcwaitq);
 }
 
+static void
+pnfs_clear_layoutreturn_info(struct pnfs_layout_hdr *lo)
+{
+       lo->plh_return_iomode = 0;
+       lo->plh_return_seq = 0;
+       clear_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags);
+}
+
 static bool
-pnfs_prepare_layoutreturn(struct pnfs_layout_hdr *lo)
+pnfs_prepare_layoutreturn(struct pnfs_layout_hdr *lo,
+               nfs4_stateid *stateid,
+               enum pnfs_iomode *iomode)
 {
        if (test_and_set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
                return false;
-       lo->plh_return_iomode = 0;
-       lo->plh_return_seq = 0;
        pnfs_get_layout_hdr(lo);
-       clear_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags);
+       if (test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags)) {
+               if (stateid != NULL) {
+                       nfs4_stateid_copy(stateid, &lo->plh_stateid);
+                       if (lo->plh_return_seq != 0)
+                               stateid->seqid = cpu_to_be32(lo->plh_return_seq);
+               }
+               if (iomode != NULL)
+                       *iomode = lo->plh_return_iomode;
+               pnfs_clear_layoutreturn_info(lo);
+               return true;
+       }
+       if (stateid != NULL)
+               nfs4_stateid_copy(stateid, &lo->plh_stateid);
+       if (iomode != NULL)
+               *iomode = IOMODE_ANY;
        return true;
 }
 
@@ -949,10 +984,7 @@ static void pnfs_layoutreturn_before_put_layout_hdr(struct pnfs_layout_hdr *lo)
                enum pnfs_iomode iomode;
                bool send;
 
-               nfs4_stateid_copy(&stateid, &lo->plh_stateid);
-               stateid.seqid = cpu_to_be32(lo->plh_return_seq);
-               iomode = lo->plh_return_iomode;
-               send = pnfs_prepare_layoutreturn(lo);
+               send = pnfs_prepare_layoutreturn(lo, &stateid, &iomode);
                spin_unlock(&inode->i_lock);
                if (send) {
                        /* Send an async layoutreturn so we dont deadlock */
@@ -989,7 +1021,6 @@ _pnfs_return_layout(struct inode *ino)
                dprintk("NFS: %s no layout to return\n", __func__);
                goto out;
        }
-       nfs4_stateid_copy(&stateid, &nfsi->layout->plh_stateid);
        /* Reference matched in nfs4_layoutreturn_release */
        pnfs_get_layout_hdr(lo);
        empty = list_empty(&lo->plh_segs);
@@ -1012,8 +1043,7 @@ _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);
+       send = pnfs_prepare_layoutreturn(lo, &stateid, NULL);
        spin_unlock(&ino->i_lock);
        pnfs_free_lseg_list(&tmp_list);
        if (send)
@@ -1080,11 +1110,10 @@ bool pnfs_roc(struct inode *ino)
                        goto out_noroc;
        }
 
-       nfs4_stateid_copy(&stateid, &lo->plh_stateid);
        /* always send layoutreturn if being marked so */
-       if (test_and_clear_bit(NFS_LAYOUT_RETURN_REQUESTED,
-                                  &lo->plh_flags))
-               layoutreturn = pnfs_prepare_layoutreturn(lo);
+       if (test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags))
+               layoutreturn = pnfs_prepare_layoutreturn(lo,
+                               &stateid, NULL);
 
        list_for_each_entry_safe(lseg, tmp, &lo->plh_segs, pls_list)
                /* If we are sending layoutreturn, invalidate all valid lsegs */
@@ -1132,7 +1161,6 @@ void pnfs_roc_set_barrier(struct inode *ino, u32 barrier)
 
        spin_lock(&ino->i_lock);
        lo = NFS_I(ino)->layout;
-       pnfs_mark_layout_returned_if_empty(lo);
        if (pnfs_seqid_is_newer(barrier, lo->plh_barrier))
                lo->plh_barrier = barrier;
        spin_unlock(&ino->i_lock);
@@ -1505,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))) {
@@ -1645,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);
@@ -1735,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)) {
@@ -1758,16 +1795,19 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
                 * inode invalid, and don't bother validating the stateid
                 * sequence number.
                 */
-               pnfs_mark_matching_lsegs_invalid(lo, &free_me, NULL, 0);
+               pnfs_mark_layout_stateid_invalid(lo, &free_me);
 
                nfs4_stateid_copy(&lo->plh_stateid, &res->stateid);
                lo->plh_barrier = be32_to_cpu(res->stateid.seqid);
        }
 
-       clear_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
-
        pnfs_get_lseg(lseg);
        pnfs_layout_insert_lseg(lo, lseg, &free_me);
+       if (!pnfs_layout_is_valid(lo)) {
+               pnfs_clear_layoutreturn_info(lo);
+               clear_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
+       }
+
 
        if (res->return_on_close)
                set_bit(NFS_LSEG_ROC, &lseg->pls_flags);
@@ -1787,14 +1827,14 @@ static void
 pnfs_set_plh_return_info(struct pnfs_layout_hdr *lo, enum pnfs_iomode iomode,
                         u32 seq)
 {
-       if (lo->plh_return_iomode == iomode)
-               return;
-       if (lo->plh_return_iomode != 0)
+       if (lo->plh_return_iomode != 0 && lo->plh_return_iomode != iomode)
                iomode = IOMODE_ANY;
        lo->plh_return_iomode = iomode;
        set_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags);
-       if (!lo->plh_return_seq || pnfs_seqid_is_newer(seq, lo->plh_return_seq))
+       if (seq != 0) {
+               WARN_ON_ONCE(lo->plh_return_seq != 0 && lo->plh_return_seq != seq);
                lo->plh_return_seq = seq;
+       }
 }
 
 /**
@@ -1824,7 +1864,7 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
        assert_spin_locked(&lo->plh_inode->i_lock);
 
        list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
-               if (should_free_lseg(&lseg->pls_range, return_range)) {
+               if (pnfs_match_lseg_recall(lseg, return_range, seq)) {
                        dprintk("%s: marking lseg %p iomode %d "
                                "offset %llu length %llu\n", __func__,
                                lseg, lseg->pls_range.iomode,
@@ -1855,19 +1895,17 @@ void pnfs_error_mark_layout_for_return(struct inode *inode,
        bool return_now = false;
 
        spin_lock(&inode->i_lock);
-       pnfs_set_plh_return_info(lo, range.iomode, lseg->pls_seq);
+       pnfs_set_plh_return_info(lo, range.iomode, 0);
        /*
         * mark all matching lsegs so that we are sure to have no live
         * segments at hand when sending layoutreturn. See pnfs_put_lseg()
         * for how it works.
         */
-       if (!pnfs_mark_matching_lsegs_return(lo, &free_me,
-                                               &range, lseg->pls_seq)) {
+       if (!pnfs_mark_matching_lsegs_return(lo, &free_me, &range, 0)) {
                nfs4_stateid stateid;
-               enum pnfs_iomode iomode = lo->plh_return_iomode;
+               enum pnfs_iomode iomode;
 
-               nfs4_stateid_copy(&stateid, &lo->plh_stateid);
-               return_now = pnfs_prepare_layoutreturn(lo);
+               return_now = pnfs_prepare_layoutreturn(lo, &stateid, &iomode);
                spin_unlock(&inode->i_lock);
                if (return_now)
                        pnfs_send_layoutreturn(lo, &stateid, iomode, false);
@@ -2382,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) {
This page took 0.029223 seconds and 5 git commands to generate.