Pavel Emelyanov <xemul@xxxxxxxxxxxxx> writes: > Make fuse think that when writeback is on the inode's i_size is alway > up-to-date and not update it with the value received from the > userspace. This is done because the page cache code may update i_size > without letting the FS know. Similar rule applies to i_mtime. Except it's even more tricky, since you have to flush the mtime together with the data (the flush should not update the modification time). And we have other operations which also change the mtime, and those also need to be updated. We should probably look at what NFS is doing. > This assumption implies fixing the previously introduced short-read > helper -- when a short read occurs the 'hole' is filled with zeroes. > > Signed-off-by: Pavel Emelyanov <xemul@xxxxxxxxxx> > --- > fs/fuse/dir.c | 10 ++++++---- > fs/fuse/file.c | 23 +++++++++++++++++++++-- > fs/fuse/inode.c | 6 ++++-- > 3 files changed, 31 insertions(+), 8 deletions(-) > > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > index 334e0b1..032fb20 100644 > --- a/fs/fuse/dir.c > +++ b/fs/fuse/dir.c > @@ -790,7 +790,7 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, > stat->mtime.tv_nsec = attr->mtimensec; > stat->ctime.tv_sec = attr->ctime; > stat->ctime.tv_nsec = attr->ctimensec; > - stat->size = attr->size; > + stat->size = i_size_read(inode); > stat->blocks = attr->blocks; > > if (attr->blksize != 0) > @@ -1350,7 +1350,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, > struct fuse_req *req; > struct fuse_setattr_in inarg; > struct fuse_attr_out outarg; > - bool is_truncate = false; > + bool is_truncate = false, is_wb = fc->writeback_cache; > loff_t oldsize; > int err; > > @@ -1423,7 +1423,8 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, > fuse_change_attributes_common(inode, &outarg.attr, > attr_timeout(&outarg)); > oldsize = inode->i_size; > - i_size_write(inode, outarg.attr.size); > + if (!is_wb || is_truncate || !S_ISREG(inode->i_mode)) > + i_size_write(inode, outarg.attr.size); > > if (is_truncate) { > /* NOTE: this may release/reacquire fc->lock */ > @@ -1435,7 +1436,8 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, > * Only call invalidate_inode_pages2() after removing > * FUSE_NOWRITE, otherwise fuse_launder_page() would deadlock. > */ > - if (S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { > + if ((is_truncate || !is_wb) && > + S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { > truncate_pagecache(inode, oldsize, outarg.attr.size); > invalidate_inode_pages2(inode->i_mapping); > } > diff --git a/fs/fuse/file.c b/fs/fuse/file.c > index 47f0f2e..9bc1390 100644 > --- a/fs/fuse/file.c > +++ b/fs/fuse/file.c > @@ -541,9 +541,28 @@ static void fuse_read_update_size(struct inode *inode, loff_t size, > static void fuse_short_read(struct fuse_req *req, struct inode *inode, u64 attr_ver) > { > size_t num_read = req->out.args[0].size; > + struct fuse_conn *fc = get_fuse_conn(inode); > + > + if (fc->writeback_cache) { > + /* > + * A hole in a file. Some data after the hole are in page cache. > + */ > + int i; > + size_t off = num_read & (PAGE_CACHE_SIZE - 1); > + > + for (i = num_read >> PAGE_CACHE_SHIFT; i < req->num_pages; i++) { > + struct page *page = req->pages[i]; > + void *mapaddr = kmap_atomic(page, KM_USER0); > > - loff_t pos = page_offset(req->pages[0]) + num_read; > - fuse_read_update_size(inode, pos, attr_ver); > + memset(mapaddr + off, 0, PAGE_CACHE_SIZE - off); > + > + kunmap_atomic(mapaddr, KM_USER0); > + off = 0; > + } > + } else { > + loff_t pos = page_offset(req->pages[0]) + num_read; > + fuse_read_update_size(inode, pos, attr_ver); > + } > } > > static int fuse_readpage(struct file *file, struct page *page) > diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c > index 1cd6165..88c577f 100644 > --- a/fs/fuse/inode.c > +++ b/fs/fuse/inode.c > @@ -196,6 +196,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, > { > struct fuse_conn *fc = get_fuse_conn(inode); > struct fuse_inode *fi = get_fuse_inode(inode); > + bool is_wb = fc->writeback_cache; > loff_t oldsize; > > spin_lock(&fc->lock); > @@ -207,10 +208,11 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, > fuse_change_attributes_common(inode, attr, attr_valid); > > oldsize = inode->i_size; > - i_size_write(inode, attr->size); > + if (!is_wb || !S_ISREG(inode->i_mode)) > + i_size_write(inode, attr->size); > spin_unlock(&fc->lock); > > - if (S_ISREG(inode->i_mode) && oldsize != attr->size) { > + if (!is_wb && S_ISREG(inode->i_mode) && oldsize != attr->size) { > truncate_pagecache(inode, oldsize, attr->size); > invalidate_inode_pages2(inode->i_mapping); > } -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html