Skip to content

Commit 7f84a7b

Browse files
committed
netfs: Make netfs_read_folio() handle streaming-write pages
netfs_read_folio() needs to handle partially-valid pages that are marked dirty, but not uptodate in the event that someone tries to read a page was used to cache data by a streaming write. In such a case, make netfs_read_folio() set up a bvec iterator that points to the parts of the folio that need filling and to a sink page for the data that should be discarded and use that instead of i_pages as the iterator to be written to. This requires netfs_rreq_unlock_folios() to convert the page into a normal dirty uptodate page, getting rid of the partial write record and bumping the group pointer over to folio->private. Signed-off-by: David Howells <[email protected]> Reviewed-by: Jeff Layton <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected]
1 parent c38f4e9 commit 7f84a7b

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

fs/netfs/buffered_read.c

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
1717
{
1818
struct netfs_io_subrequest *subreq;
19+
struct netfs_folio *finfo;
1920
struct folio *folio;
2021
pgoff_t start_page = rreq->start / PAGE_SIZE;
2122
pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
@@ -87,6 +88,15 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
8788

8889
if (!pg_failed) {
8990
flush_dcache_folio(folio);
91+
finfo = netfs_folio_info(folio);
92+
if (finfo) {
93+
trace_netfs_folio(folio, netfs_folio_trace_filled_gaps);
94+
if (finfo->netfs_group)
95+
folio_change_private(folio, finfo->netfs_group);
96+
else
97+
folio_detach_private(folio);
98+
kfree(finfo);
99+
}
90100
folio_mark_uptodate(folio);
91101
}
92102

@@ -239,6 +249,7 @@ int netfs_read_folio(struct file *file, struct folio *folio)
239249
struct address_space *mapping = folio_file_mapping(folio);
240250
struct netfs_io_request *rreq;
241251
struct netfs_inode *ctx = netfs_inode(mapping->host);
252+
struct folio *sink = NULL;
242253
int ret;
243254

244255
_enter("%lx", folio_index(folio));
@@ -259,12 +270,56 @@ int netfs_read_folio(struct file *file, struct folio *folio)
259270
trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_readpage);
260271

261272
/* Set up the output buffer */
262-
iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
263-
rreq->start, rreq->len);
273+
if (folio_test_dirty(folio)) {
274+
/* Handle someone trying to read from an unflushed streaming
275+
* write. We fiddle the buffer so that a gap at the beginning
276+
* and/or a gap at the end get copied to, but the middle is
277+
* discarded.
278+
*/
279+
struct netfs_folio *finfo = netfs_folio_info(folio);
280+
struct bio_vec *bvec;
281+
unsigned int from = finfo->dirty_offset;
282+
unsigned int to = from + finfo->dirty_len;
283+
unsigned int off = 0, i = 0;
284+
size_t flen = folio_size(folio);
285+
size_t nr_bvec = flen / PAGE_SIZE + 2;
286+
size_t part;
287+
288+
ret = -ENOMEM;
289+
bvec = kmalloc_array(nr_bvec, sizeof(*bvec), GFP_KERNEL);
290+
if (!bvec)
291+
goto discard;
292+
293+
sink = folio_alloc(GFP_KERNEL, 0);
294+
if (!sink)
295+
goto discard;
296+
297+
trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
298+
299+
rreq->direct_bv = bvec;
300+
rreq->direct_bv_count = nr_bvec;
301+
if (from > 0) {
302+
bvec_set_folio(&bvec[i++], folio, from, 0);
303+
off = from;
304+
}
305+
while (off < to) {
306+
part = min_t(size_t, to - off, PAGE_SIZE);
307+
bvec_set_folio(&bvec[i++], sink, part, 0);
308+
off += part;
309+
}
310+
if (to < flen)
311+
bvec_set_folio(&bvec[i++], folio, flen - to, to);
312+
iov_iter_bvec(&rreq->iter, ITER_DEST, bvec, i, rreq->len);
313+
} else {
314+
iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
315+
rreq->start, rreq->len);
316+
}
264317

265318
ret = netfs_begin_read(rreq, true);
319+
if (sink)
320+
folio_put(sink);
266321
netfs_put_request(rreq, false, netfs_rreq_trace_put_return);
267-
return ret;
322+
return ret < 0 ? ret : 0;
268323

269324
discard:
270325
netfs_put_request(rreq, false, netfs_rreq_trace_put_discard);

include/trace/events/netfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,11 @@
118118
EM(netfs_folio_trace_clear_g, "clear-g") \
119119
EM(netfs_folio_trace_copy_to_cache, "copy") \
120120
EM(netfs_folio_trace_end_copy, "end-copy") \
121+
EM(netfs_folio_trace_filled_gaps, "filled-gaps") \
121122
EM(netfs_folio_trace_kill, "kill") \
122123
EM(netfs_folio_trace_mkwrite, "mkwrite") \
123124
EM(netfs_folio_trace_mkwrite_plus, "mkwrite+") \
125+
EM(netfs_folio_trace_read_gaps, "read-gaps") \
124126
EM(netfs_folio_trace_redirty, "redirty") \
125127
EM(netfs_folio_trace_redirtied, "redirtied") \
126128
EM(netfs_folio_trace_store, "store") \

0 commit comments

Comments
 (0)