Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Benjamin Coddington and committed by
Trond Myklebust
08cb5b0f d2c23c00

+27 -8
+17 -8
fs/nfs/pnfs.c
··· 2094 2094 } 2095 2095 EXPORT_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 + */ 2101 + static void 2102 + pnfs_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 + 2097 2110 void 2098 2111 pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) 2099 2112 { 2100 2113 u64 rd_size = req->wb_bytes; 2101 2114 2102 2115 pnfs_generic_pg_check_layout(pgio); 2116 + pnfs_generic_pg_check_range(pgio, req); 2103 2117 if (pgio->pg_lseg == NULL) { 2104 2118 if (pgio->pg_dreq == NULL) 2105 2119 rd_size = i_size_read(pgio->pg_inode) - req_offset(req); ··· 2145 2131 struct nfs_page *req, u64 wb_size) 2146 2132 { 2147 2133 pnfs_generic_pg_check_layout(pgio); 2134 + pnfs_generic_pg_check_range(pgio, req); 2148 2135 if (pgio->pg_lseg == NULL) { 2149 2136 pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, 2150 2137 req->wb_context, ··· 2206 2191 seg_end = pnfs_end_offset(pgio->pg_lseg->pls_range.offset, 2207 2192 pgio->pg_lseg->pls_range.length); 2208 2193 req_start = req_offset(req); 2209 - WARN_ON_ONCE(req_start >= seg_end); 2194 + 2210 2195 /* start of request is past the last byte of this segment */ 2211 - if (req_start >= seg_end) { 2212 - /* reference the new lseg */ 2213 - if (pgio->pg_ops->pg_cleanup) 2214 - pgio->pg_ops->pg_cleanup(pgio); 2215 - if (pgio->pg_ops->pg_init) 2216 - pgio->pg_ops->pg_init(pgio, req); 2196 + if (req_start >= seg_end) 2217 2197 return 0; 2218 - } 2219 2198 2220 2199 /* adjust 'size' iff there are fewer bytes left in the 2221 2200 * segment than what nfs_generic_pg_test returned */
+10
fs/nfs/pnfs.h
··· 593 593 return pnfs_is_range_intersecting(l1->offset, end1, l2->offset, end2); 594 594 } 595 595 596 + static inline bool 597 + pnfs_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 + 596 606 extern unsigned int layoutstats_timer; 597 607 598 608 #ifdef NFS_DEBUG