Skip to content

Commit 9e736cf

Browse files
committed
Merge tag 'netfs-fixes-20210621' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull netfs fixes from David Howells: "This contains patches to fix netfs_write_begin() and afs_write_end() in the following ways: (1) In netfs_write_begin(), extract the decision about whether to skip a page out to its own helper and have that clear around the region to be written, but not clear that region. This requires the filesystem to patch it up afterwards if the hole doesn't get completely filled. (2) Use offset_in_thp() in (1) rather than manually calculating the offset into the page. (3) Due to (1), afs_write_end() now needs to handle short data write into the page by generic_perform_write(). I've adopted an analogous approach to ceph of just returning 0 in this case and letting the caller go round again. It also adds a note that (in the future) the len parameter may extend beyond the page allocated. This is because the page allocation is deferred to write_begin() and that gets to decide what size of THP to allocate." Jeff Layton points out: "The netfs fix in particular fixes a data corruption bug in cephfs" * tag 'netfs-fixes-20210621' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: netfs: fix test for whether we can skip read when writing beyond EOF afs: Fix afs_write_end() to handle short writes
2 parents c13e302 + 827a746 commit 9e736cf

File tree

2 files changed

+45
-15
lines changed

2 files changed

+45
-15
lines changed

fs/afs/write.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ int afs_write_end(struct file *file, struct address_space *mapping,
118118
_enter("{%llx:%llu},{%lx}",
119119
vnode->fid.vid, vnode->fid.vnode, page->index);
120120

121+
if (!PageUptodate(page)) {
122+
if (copied < len) {
123+
copied = 0;
124+
goto out;
125+
}
126+
127+
SetPageUptodate(page);
128+
}
129+
121130
if (copied == 0)
122131
goto out;
123132

@@ -132,8 +141,6 @@ int afs_write_end(struct file *file, struct address_space *mapping,
132141
write_sequnlock(&vnode->cb_lock);
133142
}
134143

135-
ASSERT(PageUptodate(page));
136-
137144
if (PagePrivate(page)) {
138145
priv = page_private(page);
139146
f = afs_page_dirty_from(page, priv);

fs/netfs/read_helper.c

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,20 +1011,50 @@ int netfs_readpage(struct file *file,
10111011
}
10121012
EXPORT_SYMBOL(netfs_readpage);
10131013

1014-
static void netfs_clear_thp(struct page *page)
1014+
/**
1015+
* netfs_skip_page_read - prep a page for writing without reading first
1016+
* @page: page being prepared
1017+
* @pos: starting position for the write
1018+
* @len: length of write
1019+
*
1020+
* In some cases, write_begin doesn't need to read at all:
1021+
* - full page write
1022+
* - write that lies in a page that is completely beyond EOF
1023+
* - write that covers the the page from start to EOF or beyond it
1024+
*
1025+
* If any of these criteria are met, then zero out the unwritten parts
1026+
* of the page and return true. Otherwise, return false.
1027+
*/
1028+
static bool netfs_skip_page_read(struct page *page, loff_t pos, size_t len)
10151029
{
1016-
unsigned int i;
1030+
struct inode *inode = page->mapping->host;
1031+
loff_t i_size = i_size_read(inode);
1032+
size_t offset = offset_in_thp(page, pos);
1033+
1034+
/* Full page write */
1035+
if (offset == 0 && len >= thp_size(page))
1036+
return true;
1037+
1038+
/* pos beyond last page in the file */
1039+
if (pos - offset >= i_size)
1040+
goto zero_out;
1041+
1042+
/* Write that covers from the start of the page to EOF or beyond */
1043+
if (offset == 0 && (pos + len) >= i_size)
1044+
goto zero_out;
10171045

1018-
for (i = 0; i < thp_nr_pages(page); i++)
1019-
clear_highpage(page + i);
1046+
return false;
1047+
zero_out:
1048+
zero_user_segments(page, 0, offset, offset + len, thp_size(page));
1049+
return true;
10201050
}
10211051

10221052
/**
10231053
* netfs_write_begin - Helper to prepare for writing
10241054
* @file: The file to read from
10251055
* @mapping: The mapping to read from
10261056
* @pos: File position at which the write will begin
1027-
* @len: The length of the write in this page
1057+
* @len: The length of the write (may extend beyond the end of the page chosen)
10281058
* @flags: AOP_* flags
10291059
* @_page: Where to put the resultant page
10301060
* @_fsdata: Place for the netfs to store a cookie
@@ -1061,8 +1091,6 @@ int netfs_write_begin(struct file *file, struct address_space *mapping,
10611091
struct inode *inode = file_inode(file);
10621092
unsigned int debug_index = 0;
10631093
pgoff_t index = pos >> PAGE_SHIFT;
1064-
int pos_in_page = pos & ~PAGE_MASK;
1065-
loff_t size;
10661094
int ret;
10671095

10681096
DEFINE_READAHEAD(ractl, file, NULL, mapping, index);
@@ -1090,13 +1118,8 @@ int netfs_write_begin(struct file *file, struct address_space *mapping,
10901118
* within the cache granule containing the EOF, in which case we need
10911119
* to preload the granule.
10921120
*/
1093-
size = i_size_read(inode);
10941121
if (!ops->is_cache_enabled(inode) &&
1095-
((pos_in_page == 0 && len == thp_size(page)) ||
1096-
(pos >= size) ||
1097-
(pos_in_page == 0 && (pos + len) >= size))) {
1098-
netfs_clear_thp(page);
1099-
SetPageUptodate(page);
1122+
netfs_skip_page_read(page, pos, len)) {
11001123
netfs_stat(&netfs_n_rh_write_zskip);
11011124
goto have_page_no_wait;
11021125
}

0 commit comments

Comments
 (0)