Skip to content

Commit 92a714d

Browse files
committed
netfs: Fix interaction between write-streaming and cachefiles culling
An issue can occur between write-streaming (storing dirty data in partial non-uptodate pages) and a cachefiles object being culled to make space. The problem occurs because the cache object is only marked in use while there are files open using it. Once it has been released, it can be culled and the cookie marked disabled. At this point, a streaming write is permitted to occur (if the cache is active, we require pages to be prefetched and cached), but the cache can become active again before this gets flushed out - and then two effects can occur: (1) The cache may be asked to write out a region that's less than its DIO block size (assumed by cachefiles to be PAGE_SIZE) - and this causes one of two debugging statements to be emitted. (2) netfs_how_to_modify() gets confused because it sees a page that isn't allowed to be non-uptodate being uptodate and tries to prefetch it - leading to a warning that PG_fscache is set twice. Fix this by the following means: (1) Add a netfs_inode flag to disallow write-streaming to an inode and set it if we ever do local caching of that inode. It remains set for the lifetime of that inode - even if the cookie becomes disabled. (2) If the no-write-streaming flag is set, then make netfs_how_to_modify() always want to prefetch instead. (3) If netfs_how_to_modify() decides it wants to prefetch a folio, but that folio has write-streamed data in it, then it requires the folio be flushed first. (4) Export a counter of the number of times we wanted to prefetch a non-uptodate page, but found it had write-streamed data in it. (5) Export a counter of the number of times we cancelled a write to the cache because it didn't DIO align and remove the debug statements. Reported-by: Marc Dionne <marc.dionne@auristor.com> Signed-off-by: David Howells <dhowells@redhat.com> cc: Jeff Layton <jlayton@kernel.org> cc: linux-cachefs@redhat.com cc: linux-erofs@lists.ozlabs.org cc: linux-fsdevel@vger.kernel.org cc: linux-mm@kvack.org
1 parent 4088e38 commit 92a714d

File tree

7 files changed

+41
-15
lines changed

7 files changed

+41
-15
lines changed

fs/cachefiles/io.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,12 +528,12 @@ int __cachefiles_prepare_write(struct cachefiles_object *object,
528528

529529
/* Round to DIO size */
530530
start = round_down(*_start, PAGE_SIZE);
531-
if (start != *_start) {
532-
kleave(" = -ENOBUFS [down]");
533-
return -ENOBUFS;
534-
}
535-
if (*_len > upper_len) {
536-
kleave(" = -ENOBUFS [up]");
531+
if (start != *_start || *_len > upper_len) {
532+
/* Probably asked to cache a streaming write written into the
533+
* pagecache when the cookie was temporarily out of service to
534+
* culling.
535+
*/
536+
fscache_count_dio_misfit();
537537
return -ENOBUFS;
538538
}
539539

fs/netfs/buffered_write.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,19 @@ static enum netfs_how_to_modify netfs_how_to_modify(struct netfs_inode *ctx,
8080
return NETFS_WHOLE_FOLIO_MODIFY;
8181

8282
if (file->f_mode & FMODE_READ)
83-
return NETFS_JUST_PREFETCH;
84-
85-
if (netfs_is_cache_enabled(ctx))
86-
return NETFS_JUST_PREFETCH;
83+
goto no_write_streaming;
84+
if (test_bit(NETFS_ICTX_NO_WRITE_STREAMING, &ctx->flags))
85+
goto no_write_streaming;
86+
87+
if (netfs_is_cache_enabled(ctx)) {
88+
/* We don't want to get a streaming write on a file that loses
89+
* caching service temporarily because the backing store got
90+
* culled.
91+
*/
92+
if (!test_bit(NETFS_ICTX_NO_WRITE_STREAMING, &ctx->flags))
93+
set_bit(NETFS_ICTX_NO_WRITE_STREAMING, &ctx->flags);
94+
goto no_write_streaming;
95+
}
8796

8897
if (!finfo)
8998
return NETFS_STREAMING_WRITE;
@@ -95,6 +104,13 @@ static enum netfs_how_to_modify netfs_how_to_modify(struct netfs_inode *ctx,
95104
if (offset == finfo->dirty_offset + finfo->dirty_len)
96105
return NETFS_STREAMING_WRITE_CONT;
97106
return NETFS_FLUSH_CONTENT;
107+
108+
no_write_streaming:
109+
if (finfo) {
110+
netfs_stat(&netfs_n_wh_wstream_conflict);
111+
return NETFS_FLUSH_CONTENT;
112+
}
113+
return NETFS_JUST_PREFETCH;
98114
}
99115

100116
/*

fs/netfs/fscache_stats.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,15 @@ atomic_t fscache_n_no_create_space;
4848
EXPORT_SYMBOL(fscache_n_no_create_space);
4949
atomic_t fscache_n_culled;
5050
EXPORT_SYMBOL(fscache_n_culled);
51+
atomic_t fscache_n_dio_misfit;
52+
EXPORT_SYMBOL(fscache_n_dio_misfit);
5153

5254
/*
5355
* display the general statistics
5456
*/
5557
int fscache_stats_show(struct seq_file *m)
5658
{
57-
seq_puts(m, "FS-Cache statistics\n");
59+
seq_puts(m, "-- FS-Cache statistics --\n");
5860
seq_printf(m, "Cookies: n=%d v=%d vcol=%u voom=%u\n",
5961
atomic_read(&fscache_n_cookies),
6062
atomic_read(&fscache_n_volumes),
@@ -93,8 +95,9 @@ int fscache_stats_show(struct seq_file *m)
9395
atomic_read(&fscache_n_no_create_space),
9496
atomic_read(&fscache_n_culled));
9597

96-
seq_printf(m, "IO : rd=%u wr=%u\n",
98+
seq_printf(m, "IO : rd=%u wr=%u mis=%u\n",
9799
atomic_read(&fscache_n_read),
98-
atomic_read(&fscache_n_write));
100+
atomic_read(&fscache_n_write),
101+
atomic_read(&fscache_n_dio_misfit));
99102
return 0;
100103
}

fs/netfs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ extern atomic_t netfs_n_rh_write_begin;
123123
extern atomic_t netfs_n_rh_write_done;
124124
extern atomic_t netfs_n_rh_write_failed;
125125
extern atomic_t netfs_n_rh_write_zskip;
126+
extern atomic_t netfs_n_wh_wstream_conflict;
126127
extern atomic_t netfs_n_wh_upload;
127128
extern atomic_t netfs_n_wh_upload_done;
128129
extern atomic_t netfs_n_wh_upload_failed;

fs/netfs/stats.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ atomic_t netfs_n_rh_write_begin;
2929
atomic_t netfs_n_rh_write_done;
3030
atomic_t netfs_n_rh_write_failed;
3131
atomic_t netfs_n_rh_write_zskip;
32+
atomic_t netfs_n_wh_wstream_conflict;
3233
atomic_t netfs_n_wh_upload;
3334
atomic_t netfs_n_wh_upload_done;
3435
atomic_t netfs_n_wh_upload_failed;
@@ -66,9 +67,10 @@ int netfs_stats_show(struct seq_file *m, void *v)
6667
atomic_read(&netfs_n_wh_write),
6768
atomic_read(&netfs_n_wh_write_done),
6869
atomic_read(&netfs_n_wh_write_failed));
69-
seq_printf(m, "Netfs : rr=%u sr=%u\n",
70+
seq_printf(m, "Netfs : rr=%u sr=%u wsc=%u\n",
7071
atomic_read(&netfs_n_rh_rreq),
71-
atomic_read(&netfs_n_rh_sreq));
72+
atomic_read(&netfs_n_rh_sreq),
73+
atomic_read(&netfs_n_wh_wstream_conflict));
7274
return fscache_stats_show(m);
7375
}
7476
EXPORT_SYMBOL(netfs_stats_show);

include/linux/fscache-cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,17 +189,20 @@ extern atomic_t fscache_n_write;
189189
extern atomic_t fscache_n_no_write_space;
190190
extern atomic_t fscache_n_no_create_space;
191191
extern atomic_t fscache_n_culled;
192+
extern atomic_t fscache_n_dio_misfit;
192193
#define fscache_count_read() atomic_inc(&fscache_n_read)
193194
#define fscache_count_write() atomic_inc(&fscache_n_write)
194195
#define fscache_count_no_write_space() atomic_inc(&fscache_n_no_write_space)
195196
#define fscache_count_no_create_space() atomic_inc(&fscache_n_no_create_space)
196197
#define fscache_count_culled() atomic_inc(&fscache_n_culled)
198+
#define fscache_count_dio_misfit() atomic_inc(&fscache_n_dio_misfit)
197199
#else
198200
#define fscache_count_read() do {} while(0)
199201
#define fscache_count_write() do {} while(0)
200202
#define fscache_count_no_write_space() do {} while(0)
201203
#define fscache_count_no_create_space() do {} while(0)
202204
#define fscache_count_culled() do {} while(0)
205+
#define fscache_count_dio_misfit() do {} while(0)
203206
#endif
204207

205208
#endif /* _LINUX_FSCACHE_CACHE_H */

include/linux/netfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ struct netfs_inode {
142142
#define NETFS_ICTX_ODIRECT 0 /* The file has DIO in progress */
143143
#define NETFS_ICTX_UNBUFFERED 1 /* I/O should not use the pagecache */
144144
#define NETFS_ICTX_WRITETHROUGH 2 /* Write-through caching */
145+
#define NETFS_ICTX_NO_WRITE_STREAMING 3 /* Don't engage in write-streaming */
145146
};
146147

147148
/*

0 commit comments

Comments
 (0)