Skip to content

Commit cce6bfa

Browse files
dhowellsbrauner
authored andcommitted
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that data exists on the server, it will make a 'streaming write' whereby it stores data in a folio that is marked dirty, but not uptodate. When it does this, it attaches a record to folio->private to track the dirty region. When truncate() or fallocate() wants to invalidate part of such a folio, it will call into ->invalidate_folio(), specifying the part of the folio that is to be invalidated. netfs_invalidate_folio(), on behalf of the filesystem, must then determine how to trim the streaming write record. In a couple of cases, however, it does this incorrectly (the reduce-length and move-start cases are switched over and don't, in any case, calculate the value correctly). Fix this by making the logic tree more obvious and fixing the cases. Fixes: 9ebff83 ("netfs: Prep to use folio->private for write grouping and streaming write") Signed-off-by: David Howells <[email protected]> Link: https://lore.kernel.org/r/[email protected] cc: Matthew Wilcox (Oracle) <[email protected]> cc: Pankaj Raghav <[email protected]> cc: Jeff Layton <[email protected]> cc: Marc Dionne <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 7dfc8f0 commit cce6bfa

File tree

1 file changed

+35
-15
lines changed

1 file changed

+35
-15
lines changed

fs/netfs/misc.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,20 @@ EXPORT_SYMBOL(netfs_clear_inode_writeback);
9797
void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)
9898
{
9999
struct netfs_folio *finfo;
100+
struct netfs_inode *ctx = netfs_inode(folio_inode(folio));
100101
size_t flen = folio_size(folio);
101102

102103
_enter("{%lx},%zx,%zx", folio->index, offset, length);
103104

105+
if (offset == 0 && length == flen) {
106+
unsigned long long i_size = i_size_read(&ctx->inode);
107+
unsigned long long fpos = folio_pos(folio), end;
108+
109+
end = umin(fpos + flen, i_size);
110+
if (fpos < i_size && end > ctx->zero_point)
111+
ctx->zero_point = end;
112+
}
113+
104114
folio_wait_private_2(folio); /* [DEPRECATED] */
105115

106116
if (!folio_test_private(folio))
@@ -115,18 +125,34 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)
115125
/* We have a partially uptodate page from a streaming write. */
116126
unsigned int fstart = finfo->dirty_offset;
117127
unsigned int fend = fstart + finfo->dirty_len;
118-
unsigned int end = offset + length;
128+
unsigned int iend = offset + length;
119129

120130
if (offset >= fend)
121131
return;
122-
if (end <= fstart)
132+
if (iend <= fstart)
133+
return;
134+
135+
/* The invalidation region overlaps the data. If the region
136+
* covers the start of the data, we either move along the start
137+
* or just erase the data entirely.
138+
*/
139+
if (offset <= fstart) {
140+
if (iend >= fend)
141+
goto erase_completely;
142+
/* Move the start of the data. */
143+
finfo->dirty_len = fend - iend;
144+
finfo->dirty_offset = offset;
145+
return;
146+
}
147+
148+
/* Reduce the length of the data if the invalidation region
149+
* covers the tail part.
150+
*/
151+
if (iend >= fend) {
152+
finfo->dirty_len = offset - fstart;
123153
return;
124-
if (offset <= fstart && end >= fend)
125-
goto erase_completely;
126-
if (offset <= fstart && end > fstart)
127-
goto reduce_len;
128-
if (offset > fstart && end >= fend)
129-
goto move_start;
154+
}
155+
130156
/* A partial write was split. The caller has already zeroed
131157
* it, so just absorb the hole.
132158
*/
@@ -139,12 +165,6 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)
139165
folio_clear_uptodate(folio);
140166
kfree(finfo);
141167
return;
142-
reduce_len:
143-
finfo->dirty_len = offset + length - finfo->dirty_offset;
144-
return;
145-
move_start:
146-
finfo->dirty_len -= offset - finfo->dirty_offset;
147-
finfo->dirty_offset = offset;
148168
}
149169
EXPORT_SYMBOL(netfs_invalidate_folio);
150170

@@ -164,7 +184,7 @@ bool netfs_release_folio(struct folio *folio, gfp_t gfp)
164184
if (folio_test_dirty(folio))
165185
return false;
166186

167-
end = folio_pos(folio) + folio_size(folio);
187+
end = umin(folio_pos(folio) + folio_size(folio), i_size_read(&ctx->inode));
168188
if (end > ctx->zero_point)
169189
ctx->zero_point = end;
170190

0 commit comments

Comments
 (0)