Skip to content

Commit 49866ce

Browse files
dhowellsbrauner
authored andcommitted
netfs: Add support for caching single monolithic objects such as AFS dirs
Add support for caching the content of a file that contains a single monolithic object that must be read/written with a single I/O operation, such as an AFS directory. Signed-off-by: David Howells <[email protected]> Link: https://lore.kernel.org/r/[email protected] cc: Jeff Layton <[email protected]> cc: Marc Dionne <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent e61bfaa commit 49866ce

File tree

12 files changed

+478
-14
lines changed

12 files changed

+478
-14
lines changed

fs/netfs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ netfs-y := \
1313
read_collect.o \
1414
read_pgpriv2.o \
1515
read_retry.o \
16+
read_single.o \
1617
rolling_buffer.o \
1718
write_collect.o \
1819
write_issue.o \

fs/netfs/buffered_read.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,17 @@ static enum netfs_io_source netfs_cache_prepare_read(struct netfs_io_request *rr
137137
loff_t i_size)
138138
{
139139
struct netfs_cache_resources *cres = &rreq->cache_resources;
140+
enum netfs_io_source source;
140141

141142
if (!cres->ops)
142143
return NETFS_DOWNLOAD_FROM_SERVER;
143-
return cres->ops->prepare_read(subreq, i_size);
144+
source = cres->ops->prepare_read(subreq, i_size);
145+
trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
146+
return source;
147+
144148
}
145149

146-
static void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error,
147-
bool was_async)
150+
void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error, bool was_async)
148151
{
149152
struct netfs_io_subrequest *subreq = priv;
150153

@@ -213,6 +216,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
213216
unsigned long long zp = umin(ictx->zero_point, rreq->i_size);
214217
size_t len = subreq->len;
215218

219+
if (unlikely(rreq->origin == NETFS_READ_SINGLE))
220+
zp = rreq->i_size;
216221
if (subreq->start >= zp) {
217222
subreq->source = source = NETFS_FILL_WITH_ZEROES;
218223
goto fill_with_zeroes;

fs/netfs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
/*
2424
* buffered_read.c
2525
*/
26+
void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error, bool was_async);
2627
int netfs_prefetch_for_write(struct file *file, struct folio *folio,
2728
size_t offset, size_t len);
2829

@@ -110,6 +111,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq);
110111
extern atomic_t netfs_n_rh_dio_read;
111112
extern atomic_t netfs_n_rh_readahead;
112113
extern atomic_t netfs_n_rh_read_folio;
114+
extern atomic_t netfs_n_rh_read_single;
113115
extern atomic_t netfs_n_rh_rreq;
114116
extern atomic_t netfs_n_rh_sreq;
115117
extern atomic_t netfs_n_rh_download;

fs/netfs/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ static const char *netfs_origins[nr__netfs_io_origin] = {
3737
[NETFS_READAHEAD] = "RA",
3838
[NETFS_READPAGE] = "RP",
3939
[NETFS_READ_GAPS] = "RG",
40+
[NETFS_READ_SINGLE] = "R1",
4041
[NETFS_READ_FOR_WRITE] = "RW",
4142
[NETFS_DIO_READ] = "DR",
4243
[NETFS_WRITEBACK] = "WB",
44+
[NETFS_WRITEBACK_SINGLE] = "W1",
4345
[NETFS_WRITETHROUGH] = "WT",
4446
[NETFS_UNBUFFERED_WRITE] = "UW",
4547
[NETFS_DIO_WRITE] = "DW",

fs/netfs/objects.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
5454
if (origin == NETFS_READAHEAD ||
5555
origin == NETFS_READPAGE ||
5656
origin == NETFS_READ_GAPS ||
57+
origin == NETFS_READ_SINGLE ||
5758
origin == NETFS_READ_FOR_WRITE ||
5859
origin == NETFS_DIO_READ)
5960
INIT_WORK(&rreq->work, NULL);
@@ -196,6 +197,7 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq
196197
case NETFS_READAHEAD:
197198
case NETFS_READPAGE:
198199
case NETFS_READ_GAPS:
200+
case NETFS_READ_SINGLE:
199201
case NETFS_READ_FOR_WRITE:
200202
case NETFS_DIO_READ:
201203
INIT_WORK(&subreq->work, netfs_read_subreq_termination_worker);

fs/netfs/read_collect.c

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,39 @@ static void netfs_rreq_assess_dio(struct netfs_io_request *rreq)
361361
inode_dio_end(rreq->inode);
362362
}
363363

364+
/*
365+
* Do processing after reading a monolithic single object.
366+
*/
367+
static void netfs_rreq_assess_single(struct netfs_io_request *rreq)
368+
{
369+
struct netfs_io_subrequest *subreq;
370+
struct netfs_io_stream *stream = &rreq->io_streams[0];
371+
372+
subreq = list_first_entry_or_null(&stream->subrequests,
373+
struct netfs_io_subrequest, rreq_link);
374+
if (subreq) {
375+
if (test_bit(NETFS_SREQ_FAILED, &subreq->flags))
376+
rreq->error = subreq->error;
377+
else
378+
rreq->transferred = subreq->transferred;
379+
380+
if (!rreq->error && subreq->source == NETFS_DOWNLOAD_FROM_SERVER &&
381+
fscache_resources_valid(&rreq->cache_resources)) {
382+
trace_netfs_rreq(rreq, netfs_rreq_trace_dirty);
383+
netfs_single_mark_inode_dirty(rreq->inode);
384+
}
385+
}
386+
387+
if (rreq->iocb) {
388+
rreq->iocb->ki_pos += rreq->transferred;
389+
if (rreq->iocb->ki_complete)
390+
rreq->iocb->ki_complete(
391+
rreq->iocb, rreq->error ? rreq->error : rreq->transferred);
392+
}
393+
if (rreq->netfs_ops->done)
394+
rreq->netfs_ops->done(rreq);
395+
}
396+
364397
/*
365398
* Assess the state of a read request and decide what to do next.
366399
*
@@ -378,9 +411,17 @@ void netfs_rreq_terminated(struct netfs_io_request *rreq)
378411
return;
379412
}
380413

381-
if (rreq->origin == NETFS_DIO_READ ||
382-
rreq->origin == NETFS_READ_GAPS)
414+
switch (rreq->origin) {
415+
case NETFS_DIO_READ:
416+
case NETFS_READ_GAPS:
383417
netfs_rreq_assess_dio(rreq);
418+
break;
419+
case NETFS_READ_SINGLE:
420+
netfs_rreq_assess_single(rreq);
421+
break;
422+
default:
423+
break;
424+
}
384425
task_io_account_read(rreq->transferred);
385426

386427
trace_netfs_rreq(rreq, netfs_rreq_trace_wake_ip);

fs/netfs/read_single.c

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/* Single, monolithic object support (e.g. AFS directory).
3+
*
4+
* Copyright (C) 2024 Red Hat, Inc. All Rights Reserved.
5+
* Written by David Howells ([email protected])
6+
*/
7+
8+
#include <linux/export.h>
9+
#include <linux/fs.h>
10+
#include <linux/mm.h>
11+
#include <linux/pagemap.h>
12+
#include <linux/slab.h>
13+
#include <linux/uio.h>
14+
#include <linux/sched/mm.h>
15+
#include <linux/task_io_accounting_ops.h>
16+
#include <linux/netfs.h>
17+
#include "internal.h"
18+
19+
/**
20+
* netfs_single_mark_inode_dirty - Mark a single, monolithic object inode dirty
21+
* @inode: The inode to mark
22+
*
23+
* Mark an inode that contains a single, monolithic object as dirty so that its
24+
* writepages op will get called. If set, the SINGLE_NO_UPLOAD flag indicates
25+
* that the object will only be written to the cache and not uploaded (e.g. AFS
26+
* directory contents).
27+
*/
28+
void netfs_single_mark_inode_dirty(struct inode *inode)
29+
{
30+
struct netfs_inode *ictx = netfs_inode(inode);
31+
bool cache_only = test_bit(NETFS_ICTX_SINGLE_NO_UPLOAD, &ictx->flags);
32+
bool caching = fscache_cookie_enabled(netfs_i_cookie(netfs_inode(inode)));
33+
34+
if (cache_only && !caching)
35+
return;
36+
37+
mark_inode_dirty(inode);
38+
39+
if (caching && !(inode->i_state & I_PINNING_NETFS_WB)) {
40+
bool need_use = false;
41+
42+
spin_lock(&inode->i_lock);
43+
if (!(inode->i_state & I_PINNING_NETFS_WB)) {
44+
inode->i_state |= I_PINNING_NETFS_WB;
45+
need_use = true;
46+
}
47+
spin_unlock(&inode->i_lock);
48+
49+
if (need_use)
50+
fscache_use_cookie(netfs_i_cookie(ictx), true);
51+
}
52+
53+
}
54+
EXPORT_SYMBOL(netfs_single_mark_inode_dirty);
55+
56+
static int netfs_single_begin_cache_read(struct netfs_io_request *rreq, struct netfs_inode *ctx)
57+
{
58+
return fscache_begin_read_operation(&rreq->cache_resources, netfs_i_cookie(ctx));
59+
}
60+
61+
static void netfs_single_cache_prepare_read(struct netfs_io_request *rreq,
62+
struct netfs_io_subrequest *subreq)
63+
{
64+
struct netfs_cache_resources *cres = &rreq->cache_resources;
65+
66+
if (!cres->ops) {
67+
subreq->source = NETFS_DOWNLOAD_FROM_SERVER;
68+
return;
69+
}
70+
subreq->source = cres->ops->prepare_read(subreq, rreq->i_size);
71+
trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
72+
73+
}
74+
75+
static void netfs_single_read_cache(struct netfs_io_request *rreq,
76+
struct netfs_io_subrequest *subreq)
77+
{
78+
struct netfs_cache_resources *cres = &rreq->cache_resources;
79+
80+
netfs_stat(&netfs_n_rh_read);
81+
cres->ops->read(cres, subreq->start, &subreq->io_iter, NETFS_READ_HOLE_FAIL,
82+
netfs_cache_read_terminated, subreq);
83+
}
84+
85+
/*
86+
* Perform a read to a buffer from the cache or the server. Only a single
87+
* subreq is permitted as the object must be fetched in a single transaction.
88+
*/
89+
static int netfs_single_dispatch_read(struct netfs_io_request *rreq)
90+
{
91+
struct netfs_io_subrequest *subreq;
92+
int ret = 0;
93+
94+
atomic_set(&rreq->nr_outstanding, 1);
95+
96+
subreq = netfs_alloc_subrequest(rreq);
97+
if (!subreq) {
98+
ret = -ENOMEM;
99+
goto out;
100+
}
101+
102+
subreq->source = NETFS_DOWNLOAD_FROM_SERVER;
103+
subreq->start = 0;
104+
subreq->len = rreq->len;
105+
subreq->io_iter = rreq->buffer.iter;
106+
107+
atomic_inc(&rreq->nr_outstanding);
108+
109+
spin_lock_bh(&rreq->lock);
110+
list_add_tail(&subreq->rreq_link, &rreq->subrequests);
111+
trace_netfs_sreq(subreq, netfs_sreq_trace_added);
112+
spin_unlock_bh(&rreq->lock);
113+
114+
netfs_single_cache_prepare_read(rreq, subreq);
115+
switch (subreq->source) {
116+
case NETFS_DOWNLOAD_FROM_SERVER:
117+
netfs_stat(&netfs_n_rh_download);
118+
if (rreq->netfs_ops->prepare_read) {
119+
ret = rreq->netfs_ops->prepare_read(subreq);
120+
if (ret < 0)
121+
goto cancel;
122+
}
123+
124+
rreq->netfs_ops->issue_read(subreq);
125+
rreq->submitted += subreq->len;
126+
break;
127+
case NETFS_READ_FROM_CACHE:
128+
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
129+
netfs_single_read_cache(rreq, subreq);
130+
rreq->submitted += subreq->len;
131+
ret = 0;
132+
break;
133+
default:
134+
pr_warn("Unexpected single-read source %u\n", subreq->source);
135+
WARN_ON_ONCE(true);
136+
ret = -EIO;
137+
break;
138+
}
139+
140+
out:
141+
if (atomic_dec_and_test(&rreq->nr_outstanding))
142+
netfs_rreq_terminated(rreq);
143+
return ret;
144+
cancel:
145+
atomic_dec(&rreq->nr_outstanding);
146+
netfs_put_subrequest(subreq, false, netfs_sreq_trace_put_cancel);
147+
goto out;
148+
}
149+
150+
/**
151+
* netfs_read_single - Synchronously read a single blob of pages.
152+
* @inode: The inode to read from.
153+
* @file: The file we're using to read or NULL.
154+
* @iter: The buffer we're reading into.
155+
*
156+
* Fulfil a read request for a single monolithic object by drawing data from
157+
* the cache if possible, or the netfs if not. The buffer may be larger than
158+
* the file content; unused beyond the EOF will be zero-filled. The content
159+
* will be read with a single I/O request (though this may be retried).
160+
*
161+
* The calling netfs must initialise a netfs context contiguous to the vfs
162+
* inode before calling this.
163+
*
164+
* This is usable whether or not caching is enabled. If caching is enabled,
165+
* the data will be stored as a single object into the cache.
166+
*/
167+
ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_iter *iter)
168+
{
169+
struct netfs_io_request *rreq;
170+
struct netfs_inode *ictx = netfs_inode(inode);
171+
ssize_t ret;
172+
173+
rreq = netfs_alloc_request(inode->i_mapping, file, 0, iov_iter_count(iter),
174+
NETFS_READ_SINGLE);
175+
if (IS_ERR(rreq))
176+
return PTR_ERR(rreq);
177+
178+
ret = netfs_single_begin_cache_read(rreq, ictx);
179+
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
180+
goto cleanup_free;
181+
182+
netfs_stat(&netfs_n_rh_read_single);
183+
trace_netfs_read(rreq, 0, rreq->len, netfs_read_trace_read_single);
184+
185+
rreq->buffer.iter = *iter;
186+
netfs_single_dispatch_read(rreq);
187+
188+
trace_netfs_rreq(rreq, netfs_rreq_trace_wait_ip);
189+
wait_on_bit(&rreq->flags, NETFS_RREQ_IN_PROGRESS,
190+
TASK_UNINTERRUPTIBLE);
191+
192+
ret = rreq->error;
193+
if (ret == 0)
194+
ret = rreq->transferred;
195+
netfs_put_request(rreq, true, netfs_rreq_trace_put_return);
196+
return ret;
197+
198+
cleanup_free:
199+
netfs_put_request(rreq, false, netfs_rreq_trace_put_failed);
200+
return ret;
201+
}
202+
EXPORT_SYMBOL(netfs_read_single);

fs/netfs/stats.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
atomic_t netfs_n_rh_dio_read;
1313
atomic_t netfs_n_rh_readahead;
1414
atomic_t netfs_n_rh_read_folio;
15+
atomic_t netfs_n_rh_read_single;
1516
atomic_t netfs_n_rh_rreq;
1617
atomic_t netfs_n_rh_sreq;
1718
atomic_t netfs_n_rh_download;
@@ -46,10 +47,11 @@ atomic_t netfs_n_folioq;
4647

4748
int netfs_stats_show(struct seq_file *m, void *v)
4849
{
49-
seq_printf(m, "Reads : DR=%u RA=%u RF=%u WB=%u WBZ=%u\n",
50+
seq_printf(m, "Reads : DR=%u RA=%u RF=%u RS=%u WB=%u WBZ=%u\n",
5051
atomic_read(&netfs_n_rh_dio_read),
5152
atomic_read(&netfs_n_rh_readahead),
5253
atomic_read(&netfs_n_rh_read_folio),
54+
atomic_read(&netfs_n_rh_read_single),
5355
atomic_read(&netfs_n_rh_write_begin),
5456
atomic_read(&netfs_n_rh_write_zskip));
5557
seq_printf(m, "Writes : BW=%u WT=%u DW=%u WP=%u 2C=%u\n",

fs/netfs/write_collect.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#define HIT_PENDING 0x01 /* A front op was still pending */
1818
#define NEED_REASSESS 0x02 /* Need to loop round and reassess */
1919
#define MADE_PROGRESS 0x04 /* Made progress cleaning up a stream or the folio set */
20-
#define BUFFERED 0x08 /* The pagecache needs cleaning up */
20+
#define NEED_UNLOCK 0x08 /* The pagecache needs unlocking */
2121
#define NEED_RETRY 0x10 /* A front op requests retrying */
2222
#define SAW_FAILURE 0x20 /* One stream or hit a permanent failure */
2323

@@ -179,7 +179,7 @@ static void netfs_collect_write_results(struct netfs_io_request *wreq)
179179
if (wreq->origin == NETFS_WRITEBACK ||
180180
wreq->origin == NETFS_WRITETHROUGH ||
181181
wreq->origin == NETFS_PGPRIV2_COPY_TO_CACHE)
182-
notes = BUFFERED;
182+
notes = NEED_UNLOCK;
183183
else
184184
notes = 0;
185185

@@ -276,7 +276,7 @@ static void netfs_collect_write_results(struct netfs_io_request *wreq)
276276
trace_netfs_collect_state(wreq, wreq->collected_to, notes);
277277

278278
/* Unlock any folios that we have now finished with. */
279-
if (notes & BUFFERED) {
279+
if (notes & NEED_UNLOCK) {
280280
if (wreq->cleaned_to < wreq->collected_to)
281281
netfs_writeback_unlock_folios(wreq, &notes);
282282
} else {

0 commit comments

Comments
 (0)