|
@@ -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);
|
|
@@ -1746,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)) {
|
|
@@ -1769,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);
|
|
@@ -1798,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;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1835,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,
|
|
@@ -1866,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);
|