pnfs: only tear down lsegs that precede seqid in LAYOUTRETURN args
[linux-2.6-block.git] / fs / nfs / pnfs.c
index 3ad6bd377d35e4f3264b55487d777e13dad6783c..21ff53fe7a9e949f05de67fde8ac3128bc781ad7 100644 (file)
@@ -270,7 +270,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
        };
 
        set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
-       return pnfs_mark_matching_lsegs_invalid(lo, lseg_list, &range);
+       return pnfs_mark_matching_lsegs_invalid(lo, lseg_list, &range, 0);
 }
 
 static int
@@ -308,7 +308,7 @@ pnfs_layout_io_set_failed(struct pnfs_layout_hdr *lo, u32 iomode)
 
        spin_lock(&inode->i_lock);
        pnfs_layout_set_fail_bit(lo, pnfs_iomode_to_fail_bit(iomode));
-       pnfs_mark_matching_lsegs_invalid(lo, &head, &range);
+       pnfs_mark_matching_lsegs_invalid(lo, &head, &range, 0);
        spin_unlock(&inode->i_lock);
        pnfs_free_lseg_list(&head);
        dprintk("%s Setting layout IOMODE_%s fail bit\n", __func__,
@@ -522,13 +522,35 @@ static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
        return rv;
 }
 
-/* Returns count of number of matching invalid lsegs remaining in list
- * after call.
+/*
+ * Compare 2 layout stateid sequence ids, to see which is newer,
+ * taking into account wraparound issues.
+ */
+static bool pnfs_seqid_is_newer(u32 s1, u32 s2)
+{
+       return (s32)(s1 - s2) > 0;
+}
+
+/**
+ * pnfs_mark_matching_lsegs_invalid - tear down lsegs or mark them for later
+ * @lo: layout header containing the lsegs
+ * @tmp_list: list head where doomed lsegs should go
+ * @recall_range: optional recall range argument to match (may be NULL)
+ * @seq: only invalidate lsegs obtained prior to this sequence (may be 0)
+ *
+ * Walk the list of lsegs in the layout header, and tear down any that should
+ * be destroyed. If "recall_range" is specified then the segment must match
+ * that range. If "seq" is non-zero, then only match segments that were handed
+ * out at or before that sequence.
+ *
+ * Returns number of matching invalid lsegs remaining in list after scanning
+ * it and purging them.
  */
 int
 pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
                            struct list_head *tmp_list,
-                           const struct pnfs_layout_range *recall_range)
+                           const struct pnfs_layout_range *recall_range,
+                           u32 seq)
 {
        struct pnfs_layout_segment *lseg, *next;
        int remaining = 0;
@@ -540,10 +562,12 @@ pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
        list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
                if (!recall_range ||
                    should_free_lseg(&lseg->pls_range, recall_range)) {
-                       dprintk("%s: freeing lseg %p iomode %d "
+                       if (seq && pnfs_seqid_is_newer(lseg->pls_seq, seq))
+                               continue;
+                       dprintk("%s: freeing lseg %p iomode %d seq %u"
                                "offset %llu length %llu\n", __func__,
-                               lseg, lseg->pls_range.iomode, lseg->pls_range.offset,
-                               lseg->pls_range.length);
+                               lseg, lseg->pls_range.iomode, lseg->pls_seq,
+                               lseg->pls_range.offset, lseg->pls_range.length);
                        if (!mark_lseg_invalid(lseg, tmp_list))
                                remaining++;
                }
@@ -730,15 +754,6 @@ pnfs_destroy_all_layouts(struct nfs_client *clp)
        pnfs_destroy_layouts_byclid(clp, false);
 }
 
-/*
- * Compare 2 layout stateid sequence ids, to see which is newer,
- * taking into account wraparound issues.
- */
-static bool pnfs_seqid_is_newer(u32 s1, u32 s2)
-{
-       return (s32)(s1 - s2) > 0;
-}
-
 /* update lo->plh_stateid with new if is more recent */
 void
 pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new,
@@ -1014,7 +1029,7 @@ _pnfs_return_layout(struct inode *ino)
        pnfs_get_layout_hdr(lo);
        empty = list_empty(&lo->plh_segs);
        pnfs_clear_layoutcommit(ino, &tmp_list);
-       pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL);
+       pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL, 0);
 
        if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
                struct pnfs_layout_range range = {
@@ -1721,7 +1736,7 @@ 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);
+               pnfs_mark_matching_lsegs_invalid(lo, &free_me, NULL, 0);
 
                nfs4_stateid_copy(&lo->plh_stateid, &res->stateid);
                lo->plh_barrier = be32_to_cpu(res->stateid.seqid);
@@ -1775,7 +1790,8 @@ pnfs_set_plh_return_info(struct pnfs_layout_hdr *lo, enum pnfs_iomode iomode,
 int
 pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
                                struct list_head *tmp_list,
-                               const struct pnfs_layout_range *return_range)
+                               const struct pnfs_layout_range *return_range,
+                               u32 seq)
 {
        struct pnfs_layout_segment *lseg, *next;
        int remaining = 0;
@@ -1798,8 +1814,11 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
                                continue;
                        remaining++;
                        set_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags);
-                       pnfs_set_plh_return_info(lo, return_range->iomode, lseg->pls_seq);
                }
+
+       if (remaining)
+               pnfs_set_plh_return_info(lo, return_range->iomode, seq);
+
        return remaining;
 }
 
@@ -1822,7 +1841,8 @@ void pnfs_error_mark_layout_for_return(struct inode *inode,
         * segments at hand when sending layoutreturn. See pnfs_put_lseg()
         * for how it works.
         */
-       if (!pnfs_mark_matching_lsegs_return(lo, &free_me, &range)) {
+       if (!pnfs_mark_matching_lsegs_return(lo, &free_me,
+                                               &range, lseg->pls_seq)) {
                nfs4_stateid stateid;
                enum pnfs_iomode iomode = lo->plh_return_iomode;