diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ccc2dd45aeec71..fffef155ceb586 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1476,14 +1476,25 @@ static ssize_t fuse_send_write(struct fuse_io_args *ia, loff_t pos, * i_size still at the value read means nothing landed in the gap. When it * moved, the mover zeroed its own gap before publishing, and only what lies * past the new size is left to do. + * + * Under DLM with the writeback cache, i_size is only this node's view and the + * server's side of the gap need not be a hole: the folio may have been read + * from the server with another node's bytes past it. Zeroing would send + * zeros over them at writeback. Only a shared writable mapping can put bytes + * past EOF that need zeroing, so skip unless the inode has had one. */ void fuse_zero_eof_gap(struct inode *inode, loff_t to) { + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); struct folio *folio; loff_t from, end; bool done; + if (fc->dlm && fc->writeback_cache && + !test_bit(FUSE_I_SHARED_WRITE_MMAP, &fi->state)) + return; + do { from = i_size_read(inode); if (from >= to || !(from & (PAGE_SIZE - 1))) @@ -3192,9 +3203,16 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, /* * Check if the start of this folio comes after the end of file, * in which case the readpage can be optimized away. + * + * Not under DLM: there i_size is only this node's view. Another + * node extending the file does not update it, so a folio "past EOF" + * here may hold that node's bytes on the server. Zero-filling it + * would send zeros over them once writeback covers the whole folio. + * The page-aligned write grant this write holds makes the server's + * copy current, so read it. */ fsize = i_size_read(mapping->host); - if (fsize <= folio_pos(folio)) { + if (!fc->dlm && fsize <= folio_pos(folio)) { size_t off = offset_in_folio(folio, pos); if (off) folio_zero_segment(folio, 0, off); @@ -3254,11 +3272,9 @@ static int fuse_write_end(struct file *file, struct address_space *mapping, * than by an unlocked read-modify-write two concurrent extenders could * lose an update to. * - * Growing i_size just behind the write cursor, rather than claiming - * the whole extension up front, also keeps fuse_write_begin()'s - * beyond-EOF optimisation effective: folios wholly past EOF are zeroed - * locally instead of sending the server a read-modify-write READ for - * data that does not exist yet. + * This i_size is not what fuse_write_begin() trusts to skip its + * read-modify-write READ: under DLM it reads every partial folio, + * because another node may have written past this node's i_size. */ if (pos > inode->i_size) { struct fuse_inode *fi = get_fuse_inode(inode); @@ -3961,8 +3977,10 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) return rc; } - if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) + if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) { + set_bit(FUSE_I_SHARED_WRITE_MMAP, &get_fuse_inode(inode)->state); fuse_link_write_file(file); + } file_accessed(file); vma->vm_ops = &fuse_file_vm_ops; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 99eda51ba1bc0e..4d68ce8a3d17fd 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -313,6 +313,14 @@ enum { * See fuse_reverse_inval_inode()/fuse_file_io_open(). */ FUSE_I_FORCE_DIO, + /* + * Has had a shared writable mapping. Only such a mapping can leave + * bytes past EOF in the last folio, so under DLM fuse_zero_eof_gap() + * zeroes them only when this is set. Set in fuse_file_mmap() and + * never cleared for the life of the inode: the bytes can outlive the + * mapping in the page cache. + */ + FUSE_I_SHARED_WRITE_MMAP, }; struct fuse_conn;