aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Coddington <bcodding@redhat.com>2017-05-22 20:20:23 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2017-05-24 07:55:02 -0400
commit08cb5b0f058a325fcb5305e33f572ff6d6dfa289 (patch)
tree6badaa45f966068d482294770ecab7b5dccfc3e8
parentd2c23c0075d7091bf749411bd2ee757cf4ec356c (diff)
pnfs: Fix the check for requests in range of layout segment
It's possible and acceptable for NFS to attempt to add requests beyond the range of the current pgio->pg_lseg, a case which should be caught and limited by the pg_test operation. However, the current handling of this case replaces pgio->pg_lseg with a new layout segment (after a WARN) within that pg_test operation. That will cause all the previously added requests to be submitted with this new layout segment, which may not be valid for those requests. Fix this problem by only returning zero for the number of bytes to coalesce from pg_test for this case which allows any previously added requests to complete on the current layout segment. The check for requests starting out of range of the layout segment moves to pg_init, so that the replacement of pgio->pg_lseg will be done when the next request is added. Signed-off-by: Benjamin Coddington <bcodding@redhat.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
-rw-r--r--fs/nfs/pnfs.c25
-rw-r--r--fs/nfs/pnfs.h10
2 files changed, 27 insertions, 8 deletions
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index adc6ec28d4b5..c383d0913b54 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -2094,12 +2094,26 @@ pnfs_generic_pg_check_layout(struct nfs_pageio_descriptor *pgio)
2094} 2094}
2095EXPORT_SYMBOL_GPL(pnfs_generic_pg_check_layout); 2095EXPORT_SYMBOL_GPL(pnfs_generic_pg_check_layout);
2096 2096
2097/*
2098 * Check for any intersection between the request and the pgio->pg_lseg,
2099 * and if none, put this pgio->pg_lseg away.
2100 */
2101static void
2102pnfs_generic_pg_check_range(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
2103{
2104 if (pgio->pg_lseg && !pnfs_lseg_request_intersecting(pgio->pg_lseg, req)) {
2105 pnfs_put_lseg(pgio->pg_lseg);
2106 pgio->pg_lseg = NULL;
2107 }
2108}
2109
2097void 2110void
2098pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) 2111pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
2099{ 2112{
2100 u64 rd_size = req->wb_bytes; 2113 u64 rd_size = req->wb_bytes;
2101 2114
2102 pnfs_generic_pg_check_layout(pgio); 2115 pnfs_generic_pg_check_layout(pgio);
2116 pnfs_generic_pg_check_range(pgio, req);
2103 if (pgio->pg_lseg == NULL) { 2117 if (pgio->pg_lseg == NULL) {
2104 if (pgio->pg_dreq == NULL) 2118 if (pgio->pg_dreq == NULL)
2105 rd_size = i_size_read(pgio->pg_inode) - req_offset(req); 2119 rd_size = i_size_read(pgio->pg_inode) - req_offset(req);
@@ -2131,6 +2145,7 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio,
2131 struct nfs_page *req, u64 wb_size) 2145 struct nfs_page *req, u64 wb_size)
2132{ 2146{
2133 pnfs_generic_pg_check_layout(pgio); 2147 pnfs_generic_pg_check_layout(pgio);
2148 pnfs_generic_pg_check_range(pgio, req);
2134 if (pgio->pg_lseg == NULL) { 2149 if (pgio->pg_lseg == NULL) {
2135 pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, 2150 pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
2136 req->wb_context, 2151 req->wb_context,
@@ -2191,16 +2206,10 @@ pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio,
2191 seg_end = pnfs_end_offset(pgio->pg_lseg->pls_range.offset, 2206 seg_end = pnfs_end_offset(pgio->pg_lseg->pls_range.offset,
2192 pgio->pg_lseg->pls_range.length); 2207 pgio->pg_lseg->pls_range.length);
2193 req_start = req_offset(req); 2208 req_start = req_offset(req);
2194 WARN_ON_ONCE(req_start >= seg_end); 2209
2195 /* start of request is past the last byte of this segment */ 2210 /* start of request is past the last byte of this segment */
2196 if (req_start >= seg_end) { 2211 if (req_start >= seg_end)
2197 /* reference the new lseg */
2198 if (pgio->pg_ops->pg_cleanup)
2199 pgio->pg_ops->pg_cleanup(pgio);
2200 if (pgio->pg_ops->pg_init)
2201 pgio->pg_ops->pg_init(pgio, req);
2202 return 0; 2212 return 0;
2203 }
2204 2213
2205 /* adjust 'size' iff there are fewer bytes left in the 2214 /* adjust 'size' iff there are fewer bytes left in the
2206 * segment than what nfs_generic_pg_test returned */ 2215 * segment than what nfs_generic_pg_test returned */
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 2d05b756a8d6..99731e3e332f 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -593,6 +593,16 @@ pnfs_lseg_range_intersecting(const struct pnfs_layout_range *l1,
593 return pnfs_is_range_intersecting(l1->offset, end1, l2->offset, end2); 593 return pnfs_is_range_intersecting(l1->offset, end1, l2->offset, end2);
594} 594}
595 595
596static inline bool
597pnfs_lseg_request_intersecting(struct pnfs_layout_segment *lseg, struct nfs_page *req)
598{
599 u64 seg_last = pnfs_end_offset(lseg->pls_range.offset, lseg->pls_range.length);
600 u64 req_last = req_offset(req) + req->wb_bytes;
601
602 return pnfs_is_range_intersecting(lseg->pls_range.offset, seg_last,
603 req_offset(req), req_last);
604}
605
596extern unsigned int layoutstats_timer; 606extern unsigned int layoutstats_timer;
597 607
598#ifdef NFS_DEBUG 608#ifdef NFS_DEBUG