mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
fuse update for 5.16
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCYYk8VgAKCRDh3BK/laaZ PIUGAP4yYrlK574xnrdfZgwmTEx03/6ze0ZOA/J82mwdyxV8NgD/W8wyheHyXOJR Mnk2eQj4avgwctHrjmyzH3jFFcT1Sgw= =nrFv -----END PGP SIGNATURE----- Merge tag 'fuse-update-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Fix a possible of deadlock in case inode writeback is in progress during dentry reclaim - Fix a crash in case of page stealing - Selectively invalidate cached attributes, possibly improving performance - Allow filesystems to disable data flushing from ->flush() - Misc fixes and cleanups * tag 'fuse-update-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (23 commits) fuse: fix page stealing virtiofs: use strscpy for copying the queue name fuse: add FOPEN_NOFLUSH fuse: only update necessary attributes fuse: take cache_mask into account in getattr fuse: add cache_mask fuse: move reverting attributes to fuse_change_attributes() fuse: simplify local variables holding writeback cache state fuse: cleanup code conditional on fc->writeback_cache fuse: fix attr version comparison in fuse_read_update_size() fuse: always invalidate attributes after writes fuse: rename fuse_write_update_size() fuse: don't bump attr_version in cached write fuse: selective attribute invalidation fuse: don't increment nlink in link() fuse: decrement nlink on overwriting rename fuse: simplify __fuse_write_file_get() fuse: move fuse_invalidate_attr() into fuse_update_ctime() fuse: delete redundant code fuse: use kmap_local_page() ...
This commit is contained in:
commit
cdd39b0539
@ -732,11 +732,8 @@ static ssize_t fuse_dax_direct_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
ssize_t ret;
|
||||
|
||||
ret = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_write_update_size(inode, iocb->ki_pos);
|
||||
fuse_write_update_attr(inode, iocb->ki_pos, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -756,7 +756,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
|
||||
{
|
||||
unsigned ncpy = min(*size, cs->len);
|
||||
if (val) {
|
||||
void *pgaddr = kmap_atomic(cs->pg);
|
||||
void *pgaddr = kmap_local_page(cs->pg);
|
||||
void *buf = pgaddr + cs->offset;
|
||||
|
||||
if (cs->write)
|
||||
@ -764,7 +764,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
|
||||
else
|
||||
memcpy(*val, buf, ncpy);
|
||||
|
||||
kunmap_atomic(pgaddr);
|
||||
kunmap_local(pgaddr);
|
||||
*val += ncpy;
|
||||
}
|
||||
*size -= ncpy;
|
||||
@ -847,6 +847,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
||||
|
||||
replace_page_cache_page(oldpage, newpage);
|
||||
|
||||
/*
|
||||
* Release while we have extra ref on stolen page. Otherwise
|
||||
* anon_pipe_buf_release() might think the page can be reused.
|
||||
*/
|
||||
pipe_buf_release(cs->pipe, buf);
|
||||
|
||||
get_page(newpage);
|
||||
|
||||
if (!(buf->flags & PIPE_BUF_FLAG_LRU))
|
||||
@ -949,10 +955,10 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
|
||||
}
|
||||
}
|
||||
if (page) {
|
||||
void *mapaddr = kmap_atomic(page);
|
||||
void *mapaddr = kmap_local_page(page);
|
||||
void *buf = mapaddr + offset;
|
||||
offset += fuse_copy_do(cs, &buf, &count);
|
||||
kunmap_atomic(mapaddr);
|
||||
kunmap_local(mapaddr);
|
||||
} else
|
||||
offset += fuse_copy_do(cs, NULL, &count);
|
||||
}
|
||||
@ -1591,7 +1597,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
|
||||
end = outarg.offset + outarg.size;
|
||||
if (end > file_size) {
|
||||
file_size = end;
|
||||
fuse_write_update_size(inode, file_size);
|
||||
fuse_write_update_attr(inode, file_size, outarg.size);
|
||||
}
|
||||
|
||||
num = outarg.size;
|
||||
@ -2031,8 +2037,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
|
||||
|
||||
pipe_lock(pipe);
|
||||
out_free:
|
||||
for (idx = 0; idx < nbuf; idx++)
|
||||
pipe_buf_release(pipe, &bufs[idx]);
|
||||
for (idx = 0; idx < nbuf; idx++) {
|
||||
struct pipe_buffer *buf = &bufs[idx];
|
||||
|
||||
if (buf->ops)
|
||||
pipe_buf_release(pipe, buf);
|
||||
}
|
||||
pipe_unlock(pipe);
|
||||
|
||||
kvfree(bufs);
|
||||
|
128
fs/fuse/dir.c
128
fs/fuse/dir.c
@ -116,7 +116,7 @@ u64 entry_attr_timeout(struct fuse_entry_out *o)
|
||||
return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
|
||||
}
|
||||
|
||||
static void fuse_invalidate_attr_mask(struct inode *inode, u32 mask)
|
||||
void fuse_invalidate_attr_mask(struct inode *inode, u32 mask)
|
||||
{
|
||||
set_mask_bits(&get_fuse_inode(inode)->inval_mask, 0, mask);
|
||||
}
|
||||
@ -738,14 +738,51 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
return create_new_entry(fm, &args, dir, entry, S_IFLNK);
|
||||
}
|
||||
|
||||
void fuse_update_ctime(struct inode *inode)
|
||||
void fuse_flush_time_update(struct inode *inode)
|
||||
{
|
||||
int err = sync_inode_metadata(inode, 1);
|
||||
|
||||
mapping_set_error(inode->i_mapping, err);
|
||||
}
|
||||
|
||||
static void fuse_update_ctime_in_cache(struct inode *inode)
|
||||
{
|
||||
if (!IS_NOCMTIME(inode)) {
|
||||
inode->i_ctime = current_time(inode);
|
||||
mark_inode_dirty_sync(inode);
|
||||
fuse_flush_time_update(inode);
|
||||
}
|
||||
}
|
||||
|
||||
void fuse_update_ctime(struct inode *inode)
|
||||
{
|
||||
fuse_invalidate_attr_mask(inode, STATX_CTIME);
|
||||
fuse_update_ctime_in_cache(inode);
|
||||
}
|
||||
|
||||
static void fuse_entry_unlinked(struct dentry *entry)
|
||||
{
|
||||
struct inode *inode = d_inode(entry);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
fi->attr_version = atomic64_inc_return(&fc->attr_version);
|
||||
/*
|
||||
* If i_nlink == 0 then unlink doesn't make sense, yet this can
|
||||
* happen if userspace filesystem is careless. It would be
|
||||
* difficult to enforce correct nlink usage so just ignore this
|
||||
* condition here
|
||||
*/
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
clear_nlink(inode);
|
||||
else if (inode->i_nlink > 0)
|
||||
drop_nlink(inode);
|
||||
spin_unlock(&fi->lock);
|
||||
fuse_invalidate_entry_cache(entry);
|
||||
fuse_update_ctime(inode);
|
||||
}
|
||||
|
||||
static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
||||
{
|
||||
int err;
|
||||
@ -762,24 +799,8 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
||||
args.in_args[0].value = entry->d_name.name;
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (!err) {
|
||||
struct inode *inode = d_inode(entry);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
|
||||
/*
|
||||
* If i_nlink == 0 then unlink doesn't make sense, yet this can
|
||||
* happen if userspace filesystem is careless. It would be
|
||||
* difficult to enforce correct nlink usage so just ignore this
|
||||
* condition here
|
||||
*/
|
||||
if (inode->i_nlink > 0)
|
||||
drop_nlink(inode);
|
||||
spin_unlock(&fi->lock);
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_dir_changed(dir);
|
||||
fuse_invalidate_entry_cache(entry);
|
||||
fuse_update_ctime(inode);
|
||||
fuse_entry_unlinked(entry);
|
||||
} else if (err == -EINTR)
|
||||
fuse_invalidate_entry(entry);
|
||||
return err;
|
||||
@ -801,9 +822,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
||||
args.in_args[0].value = entry->d_name.name;
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (!err) {
|
||||
clear_nlink(d_inode(entry));
|
||||
fuse_dir_changed(dir);
|
||||
fuse_invalidate_entry_cache(entry);
|
||||
fuse_entry_unlinked(entry);
|
||||
} else if (err == -EINTR)
|
||||
fuse_invalidate_entry(entry);
|
||||
return err;
|
||||
@ -833,24 +853,18 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (!err) {
|
||||
/* ctime changes */
|
||||
fuse_invalidate_attr(d_inode(oldent));
|
||||
fuse_update_ctime(d_inode(oldent));
|
||||
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
fuse_invalidate_attr(d_inode(newent));
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
fuse_update_ctime(d_inode(newent));
|
||||
}
|
||||
|
||||
fuse_dir_changed(olddir);
|
||||
if (olddir != newdir)
|
||||
fuse_dir_changed(newdir);
|
||||
|
||||
/* newent will end up negative */
|
||||
if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) {
|
||||
fuse_invalidate_attr(d_inode(newent));
|
||||
fuse_invalidate_entry_cache(newent);
|
||||
fuse_update_ctime(d_inode(newent));
|
||||
}
|
||||
if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent))
|
||||
fuse_entry_unlinked(newent);
|
||||
} else if (err == -EINTR) {
|
||||
/* If request was interrupted, DEITY only knows if the
|
||||
rename actually took place. If the invalidation
|
||||
@ -916,25 +930,11 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||
args.in_args[1].size = newent->d_name.len + 1;
|
||||
args.in_args[1].value = newent->d_name.name;
|
||||
err = create_new_entry(fm, &args, newdir, newent, inode->i_mode);
|
||||
/* Contrary to "normal" filesystems it can happen that link
|
||||
makes two "logical" inodes point to the same "physical"
|
||||
inode. We invalidate the attributes of the old one, so it
|
||||
will reflect changes in the backing inode (link count,
|
||||
etc.)
|
||||
*/
|
||||
if (!err) {
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
if (!err)
|
||||
fuse_update_ctime_in_cache(inode);
|
||||
else if (err == -EINTR)
|
||||
fuse_invalidate_attr(inode);
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
|
||||
if (likely(inode->i_nlink < UINT_MAX))
|
||||
inc_nlink(inode);
|
||||
spin_unlock(&fi->lock);
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_update_ctime(inode);
|
||||
} else if (err == -EINTR) {
|
||||
fuse_invalidate_attr(inode);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -944,15 +944,6 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
|
||||
unsigned int blkbits;
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
|
||||
/* see the comment in fuse_change_attributes() */
|
||||
if (fc->writeback_cache && S_ISREG(inode->i_mode)) {
|
||||
attr->size = i_size_read(inode);
|
||||
attr->mtime = inode->i_mtime.tv_sec;
|
||||
attr->mtimensec = inode->i_mtime.tv_nsec;
|
||||
attr->ctime = inode->i_ctime.tv_sec;
|
||||
attr->ctimensec = inode->i_ctime.tv_nsec;
|
||||
}
|
||||
|
||||
stat->dev = inode->i_sb->s_dev;
|
||||
stat->ino = attr->ino;
|
||||
stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
|
||||
@ -1030,12 +1021,14 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
int err = 0;
|
||||
bool sync;
|
||||
u32 inval_mask = READ_ONCE(fi->inval_mask);
|
||||
u32 cache_mask = fuse_get_cache_mask(inode);
|
||||
|
||||
if (flags & AT_STATX_FORCE_SYNC)
|
||||
sync = true;
|
||||
else if (flags & AT_STATX_DONT_SYNC)
|
||||
sync = false;
|
||||
else if (request_mask & READ_ONCE(fi->inval_mask))
|
||||
else if (request_mask & inval_mask & ~cache_mask)
|
||||
sync = true;
|
||||
else
|
||||
sync = time_before64(fi->i_time, get_jiffies_64());
|
||||
@ -1052,11 +1045,9 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
|
||||
return err;
|
||||
}
|
||||
|
||||
int fuse_update_attributes(struct inode *inode, struct file *file)
|
||||
int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask)
|
||||
{
|
||||
/* Do *not* need to get atime for internal purposes */
|
||||
return fuse_update_get_attr(inode, file, NULL,
|
||||
STATX_BASIC_STATS & ~STATX_ATIME, 0);
|
||||
return fuse_update_get_attr(inode, file, NULL, mask, 0);
|
||||
}
|
||||
|
||||
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
|
||||
@ -1071,7 +1062,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
|
||||
if (!parent)
|
||||
return -ENOENT;
|
||||
|
||||
inode_lock(parent);
|
||||
inode_lock_nested(parent, I_MUTEX_PARENT);
|
||||
if (!S_ISDIR(parent->i_mode))
|
||||
goto unlock;
|
||||
|
||||
@ -1561,10 +1552,10 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
struct fuse_setattr_in inarg;
|
||||
struct fuse_attr_out outarg;
|
||||
bool is_truncate = false;
|
||||
bool is_wb = fc->writeback_cache;
|
||||
bool is_wb = fc->writeback_cache && S_ISREG(inode->i_mode);
|
||||
loff_t oldsize;
|
||||
int err;
|
||||
bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode);
|
||||
bool trust_local_cmtime = is_wb;
|
||||
bool fault_blocked = false;
|
||||
|
||||
if (!fc->default_permissions)
|
||||
@ -1608,7 +1599,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
}
|
||||
|
||||
/* Flush dirty data/metadata before non-truncate SETATTR */
|
||||
if (is_wb && S_ISREG(inode->i_mode) &&
|
||||
if (is_wb &&
|
||||
attr->ia_valid &
|
||||
(ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET |
|
||||
ATTR_TIMES_SET)) {
|
||||
@ -1676,10 +1667,11 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
}
|
||||
|
||||
fuse_change_attributes_common(inode, &outarg.attr,
|
||||
attr_timeout(&outarg));
|
||||
attr_timeout(&outarg),
|
||||
fuse_get_cache_mask(inode));
|
||||
oldsize = inode->i_size;
|
||||
/* see the comment in fuse_change_attributes() */
|
||||
if (!is_wb || is_truncate || !S_ISREG(inode->i_mode))
|
||||
if (!is_wb || is_truncate)
|
||||
i_size_write(inode, outarg.attr.size);
|
||||
|
||||
if (is_truncate) {
|
||||
|
106
fs/fuse/file.c
106
fs/fuse/file.c
@ -211,9 +211,8 @@ void fuse_finish_open(struct inode *inode, struct file *file)
|
||||
i_size_write(inode, 0);
|
||||
spin_unlock(&fi->lock);
|
||||
truncate_pagecache(inode, 0);
|
||||
fuse_invalidate_attr(inode);
|
||||
if (fc->writeback_cache)
|
||||
file_update_time(file);
|
||||
file_update_time(file);
|
||||
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
|
||||
} else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
|
||||
invalidate_inode_pages2(inode->i_mapping);
|
||||
}
|
||||
@ -339,12 +338,6 @@ static int fuse_open(struct inode *inode, struct file *file)
|
||||
|
||||
static int fuse_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
|
||||
/* see fuse_vma_close() for !writeback_cache case */
|
||||
if (fc->writeback_cache)
|
||||
write_inode_now(inode, 1);
|
||||
|
||||
fuse_release_common(file, false);
|
||||
|
||||
/* return value is ignored by VFS */
|
||||
@ -483,6 +476,9 @@ static int fuse_flush(struct file *file, fl_owner_t id)
|
||||
if (fuse_is_bad(inode))
|
||||
return -EIO;
|
||||
|
||||
if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
|
||||
return 0;
|
||||
|
||||
err = write_inode_now(inode, 1);
|
||||
if (err)
|
||||
return err;
|
||||
@ -521,7 +517,7 @@ inval_attr_out:
|
||||
* enabled, i_blocks from cached attr may not be accurate.
|
||||
*/
|
||||
if (!err && fm->fc->writeback_cache)
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -793,7 +789,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size,
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
if (attr_ver == fi->attr_version && size < inode->i_size &&
|
||||
if (attr_ver >= fi->attr_version && size < inode->i_size &&
|
||||
!test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
|
||||
fi->attr_version = atomic64_inc_return(&fc->attr_version);
|
||||
i_size_write(inode, size);
|
||||
@ -1003,7 +999,7 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
if (fc->auto_inval_data ||
|
||||
(iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) {
|
||||
int err;
|
||||
err = fuse_update_attributes(inode, iocb->ki_filp);
|
||||
err = fuse_update_attributes(inode, iocb->ki_filp, STATX_SIZE);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1072,7 +1068,7 @@ static ssize_t fuse_send_write(struct fuse_io_args *ia, loff_t pos,
|
||||
return err ?: ia->write.out.size;
|
||||
}
|
||||
|
||||
bool fuse_write_update_size(struct inode *inode, loff_t pos)
|
||||
bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
@ -1080,12 +1076,14 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos)
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
fi->attr_version = atomic64_inc_return(&fc->attr_version);
|
||||
if (pos > inode->i_size) {
|
||||
if (written > 0 && pos > inode->i_size) {
|
||||
i_size_write(inode, pos);
|
||||
ret = true;
|
||||
}
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1268,11 +1266,8 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,
|
||||
kfree(ap->pages);
|
||||
} while (!err && iov_iter_count(ii));
|
||||
|
||||
if (res > 0)
|
||||
fuse_write_update_size(inode, pos);
|
||||
|
||||
fuse_write_update_attr(inode, pos, res);
|
||||
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
||||
fuse_invalidate_attr(inode);
|
||||
|
||||
return res > 0 ? res : err;
|
||||
}
|
||||
@ -1290,7 +1285,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
|
||||
if (fc->writeback_cache) {
|
||||
/* Update size (EOF optimization) and mode (SUID clearing) */
|
||||
err = fuse_update_attributes(mapping->host, file);
|
||||
err = fuse_update_attributes(mapping->host, file,
|
||||
STATX_SIZE | STATX_MODE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1451,7 +1447,6 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
|
||||
if (!ia)
|
||||
return -ENOMEM;
|
||||
|
||||
ia->io = io;
|
||||
if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) {
|
||||
if (!write)
|
||||
inode_lock(inode);
|
||||
@ -1561,11 +1556,9 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
} else {
|
||||
res = fuse_direct_io(&io, from, &iocb->ki_pos,
|
||||
FUSE_DIO_WRITE);
|
||||
fuse_write_update_attr(inode, iocb->ki_pos, res);
|
||||
}
|
||||
}
|
||||
fuse_invalidate_attr(inode);
|
||||
if (res > 0)
|
||||
fuse_write_update_size(inode, iocb->ki_pos);
|
||||
inode_unlock(inode);
|
||||
|
||||
return res;
|
||||
@ -1776,7 +1769,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
|
||||
* is enabled, we trust local ctime/mtime.
|
||||
*/
|
||||
if (!fc->writeback_cache)
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODIFY);
|
||||
spin_lock(&fi->lock);
|
||||
rb_erase(&wpa->writepages_entry, &fi->writepages);
|
||||
while (wpa->next) {
|
||||
@ -1822,14 +1815,13 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
|
||||
|
||||
static struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi)
|
||||
{
|
||||
struct fuse_file *ff = NULL;
|
||||
struct fuse_file *ff;
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
if (!list_empty(&fi->write_files)) {
|
||||
ff = list_entry(fi->write_files.next, struct fuse_file,
|
||||
write_entry);
|
||||
ff = list_first_entry_or_null(&fi->write_files, struct fuse_file,
|
||||
write_entry);
|
||||
if (ff)
|
||||
fuse_file_get(ff);
|
||||
}
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
return ff;
|
||||
@ -1848,6 +1840,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
struct fuse_file *ff;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Inode is always written before the last reference is dropped and
|
||||
* hence this should not be reached from reclaim.
|
||||
*
|
||||
* Writing back the inode from reclaim can deadlock if the request
|
||||
* processing itself needs an allocation. Allocations triggering
|
||||
* reclaim while serving a request can't be prevented, because it can
|
||||
* involve any number of unrelated userspace processes.
|
||||
*/
|
||||
WARN_ON(wbc->for_reclaim);
|
||||
|
||||
ff = __fuse_write_file_get(fi);
|
||||
err = fuse_flush_times(inode, ff);
|
||||
if (ff)
|
||||
@ -2306,15 +2309,18 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
|
||||
if (!copied)
|
||||
goto unlock;
|
||||
|
||||
pos += copied;
|
||||
if (!PageUptodate(page)) {
|
||||
/* Zero any unwritten bytes at the end of the page */
|
||||
size_t endoff = (pos + copied) & ~PAGE_MASK;
|
||||
size_t endoff = pos & ~PAGE_MASK;
|
||||
if (endoff)
|
||||
zero_user_segment(page, endoff, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
|
||||
fuse_write_update_size(inode, pos + copied);
|
||||
if (pos > inode->i_size)
|
||||
i_size_write(inode, pos);
|
||||
|
||||
set_page_dirty(page);
|
||||
|
||||
unlock:
|
||||
@ -2340,12 +2346,15 @@ static int fuse_launder_page(struct page *page)
|
||||
}
|
||||
|
||||
/*
|
||||
* Write back dirty pages now, because there may not be any suitable
|
||||
* open files later
|
||||
* Write back dirty data/metadata now (there may not be any suitable
|
||||
* open files later for data)
|
||||
*/
|
||||
static void fuse_vma_close(struct vm_area_struct *vma)
|
||||
{
|
||||
filemap_write_and_wait(vma->vm_file->f_mapping);
|
||||
int err;
|
||||
|
||||
err = write_inode_now(vma->vm_file->f_mapping->host, 1);
|
||||
mapping_set_error(vma->vm_file->f_mapping, err);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2628,7 +2637,7 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
|
||||
return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes);
|
||||
|
||||
fallback:
|
||||
err = fuse_update_attributes(inode, file);
|
||||
err = fuse_update_attributes(inode, file, STATX_SIZE);
|
||||
if (!err)
|
||||
return generic_file_llseek(file, offset, whence);
|
||||
else
|
||||
@ -2648,7 +2657,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
|
||||
break;
|
||||
case SEEK_END:
|
||||
inode_lock(inode);
|
||||
retval = fuse_update_attributes(inode, file);
|
||||
retval = fuse_update_attributes(inode, file, STATX_SIZE);
|
||||
if (!retval)
|
||||
retval = generic_file_llseek(file, offset, whence);
|
||||
inode_unlock(inode);
|
||||
@ -2869,7 +2878,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
|
||||
if (iov_iter_rw(iter) == WRITE) {
|
||||
ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE);
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
|
||||
} else {
|
||||
ret = __fuse_direct_read(io, iter, &pos);
|
||||
}
|
||||
@ -2891,9 +2900,8 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
kref_put(&io->refcnt, fuse_io_release);
|
||||
|
||||
if (iov_iter_rw(iter) == WRITE) {
|
||||
if (ret > 0)
|
||||
fuse_write_update_size(inode, pos);
|
||||
else if (ret < 0 && offset + count > i_size)
|
||||
fuse_write_update_attr(inode, pos, ret);
|
||||
if (ret < 0 && offset + count > i_size)
|
||||
fuse_do_truncate(file);
|
||||
}
|
||||
|
||||
@ -2981,16 +2989,14 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
||||
|
||||
/* we could have extended the file */
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
|
||||
bool changed = fuse_write_update_size(inode, offset + length);
|
||||
|
||||
if (changed && fm->fc->writeback_cache)
|
||||
if (fuse_write_update_attr(inode, offset + length, length))
|
||||
file_update_time(file);
|
||||
}
|
||||
|
||||
if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
|
||||
truncate_pagecache_range(inode, offset, offset + length - 1);
|
||||
|
||||
fuse_invalidate_attr(inode);
|
||||
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
|
||||
|
||||
out:
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE))
|
||||
@ -3002,6 +3008,8 @@ out:
|
||||
if (lock_inode)
|
||||
inode_unlock(inode);
|
||||
|
||||
fuse_flush_time_update(inode);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -3096,12 +3104,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
|
||||
ALIGN_DOWN(pos_out, PAGE_SIZE),
|
||||
ALIGN(pos_out + outarg.size, PAGE_SIZE) - 1);
|
||||
|
||||
if (fc->writeback_cache) {
|
||||
fuse_write_update_size(inode_out, pos_out + outarg.size);
|
||||
file_update_time(file_out);
|
||||
}
|
||||
|
||||
fuse_invalidate_attr(inode_out);
|
||||
file_update_time(file_out);
|
||||
fuse_write_update_attr(inode_out, pos_out + outarg.size, outarg.size);
|
||||
|
||||
err = outarg.size;
|
||||
out:
|
||||
@ -3111,6 +3115,8 @@ out:
|
||||
inode_unlock(inode_out);
|
||||
file_accessed(file_in);
|
||||
|
||||
fuse_flush_time_update(inode_out);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1031,7 +1031,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
u64 attr_valid, u64 attr_version);
|
||||
|
||||
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
u64 attr_valid);
|
||||
u64 attr_valid, u32 cache_mask);
|
||||
|
||||
u32 fuse_get_cache_mask(struct inode *inode);
|
||||
|
||||
/**
|
||||
* Initialize the client device
|
||||
@ -1065,7 +1067,15 @@ void fuse_wait_aborted(struct fuse_conn *fc);
|
||||
/**
|
||||
* Invalidate inode attributes
|
||||
*/
|
||||
|
||||
/* Attributes possibly changed on data modification */
|
||||
#define FUSE_STATX_MODIFY (STATX_MTIME | STATX_CTIME | STATX_BLOCKS)
|
||||
|
||||
/* Attributes possibly changed on data and/or size modification */
|
||||
#define FUSE_STATX_MODSIZE (FUSE_STATX_MODIFY | STATX_SIZE)
|
||||
|
||||
void fuse_invalidate_attr(struct inode *inode);
|
||||
void fuse_invalidate_attr_mask(struct inode *inode, u32 mask);
|
||||
|
||||
void fuse_invalidate_entry_cache(struct dentry *entry);
|
||||
|
||||
@ -1148,9 +1158,10 @@ int fuse_allow_current_process(struct fuse_conn *fc);
|
||||
|
||||
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
|
||||
|
||||
void fuse_flush_time_update(struct inode *inode);
|
||||
void fuse_update_ctime(struct inode *inode);
|
||||
|
||||
int fuse_update_attributes(struct inode *inode, struct file *file);
|
||||
int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask);
|
||||
|
||||
void fuse_flush_writepages(struct inode *inode);
|
||||
|
||||
@ -1208,7 +1219,7 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd,
|
||||
__poll_t fuse_file_poll(struct file *file, poll_table *wait);
|
||||
int fuse_dev_release(struct inode *inode, struct file *file);
|
||||
|
||||
bool fuse_write_update_size(struct inode *inode, loff_t pos);
|
||||
bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written);
|
||||
|
||||
int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
|
||||
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
|
||||
|
@ -118,6 +118,9 @@ static void fuse_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
|
||||
/* Will write inode on close/munmap and in all other dirtiers */
|
||||
WARN_ON(inode->i_state & I_DIRTY_INODE);
|
||||
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
clear_inode(inode);
|
||||
if (inode->i_sb->s_flags & SB_ACTIVE) {
|
||||
@ -161,7 +164,7 @@ static ino_t fuse_squash_ino(u64 ino64)
|
||||
}
|
||||
|
||||
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
u64 attr_valid)
|
||||
u64 attr_valid, u32 cache_mask)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
@ -181,9 +184,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
inode->i_atime.tv_sec = attr->atime;
|
||||
inode->i_atime.tv_nsec = attr->atimensec;
|
||||
/* mtime from server may be stale due to local buffered write */
|
||||
if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) {
|
||||
if (!(cache_mask & STATX_MTIME)) {
|
||||
inode->i_mtime.tv_sec = attr->mtime;
|
||||
inode->i_mtime.tv_nsec = attr->mtimensec;
|
||||
}
|
||||
if (!(cache_mask & STATX_CTIME)) {
|
||||
inode->i_ctime.tv_sec = attr->ctime;
|
||||
inode->i_ctime.tv_nsec = attr->ctimensec;
|
||||
}
|
||||
@ -215,16 +220,44 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
inode->i_flags &= ~S_NOSEC;
|
||||
}
|
||||
|
||||
u32 fuse_get_cache_mask(struct inode *inode)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
|
||||
if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
return STATX_MTIME | STATX_CTIME | STATX_SIZE;
|
||||
}
|
||||
|
||||
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
u64 attr_valid, u64 attr_version)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
bool is_wb = fc->writeback_cache;
|
||||
u32 cache_mask;
|
||||
loff_t oldsize;
|
||||
struct timespec64 old_mtime;
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
/*
|
||||
* In case of writeback_cache enabled, writes update mtime, ctime and
|
||||
* may update i_size. In these cases trust the cached value in the
|
||||
* inode.
|
||||
*/
|
||||
cache_mask = fuse_get_cache_mask(inode);
|
||||
if (cache_mask & STATX_SIZE)
|
||||
attr->size = i_size_read(inode);
|
||||
|
||||
if (cache_mask & STATX_MTIME) {
|
||||
attr->mtime = inode->i_mtime.tv_sec;
|
||||
attr->mtimensec = inode->i_mtime.tv_nsec;
|
||||
}
|
||||
if (cache_mask & STATX_CTIME) {
|
||||
attr->ctime = inode->i_ctime.tv_sec;
|
||||
attr->ctimensec = inode->i_ctime.tv_nsec;
|
||||
}
|
||||
|
||||
if ((attr_version != 0 && fi->attr_version > attr_version) ||
|
||||
test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
|
||||
spin_unlock(&fi->lock);
|
||||
@ -232,7 +265,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
}
|
||||
|
||||
old_mtime = inode->i_mtime;
|
||||
fuse_change_attributes_common(inode, attr, attr_valid);
|
||||
fuse_change_attributes_common(inode, attr, attr_valid, cache_mask);
|
||||
|
||||
oldsize = inode->i_size;
|
||||
/*
|
||||
@ -240,11 +273,11 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
* extend local i_size without keeping userspace server in sync. So,
|
||||
* attr->size coming from server can be stale. We cannot trust it.
|
||||
*/
|
||||
if (!is_wb || !S_ISREG(inode->i_mode))
|
||||
if (!(cache_mask & STATX_SIZE))
|
||||
i_size_write(inode, attr->size);
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
if (!is_wb && S_ISREG(inode->i_mode)) {
|
||||
if (!cache_mask && S_ISREG(inode->i_mode)) {
|
||||
bool inval = false;
|
||||
|
||||
if (oldsize != attr->size) {
|
||||
|
@ -286,11 +286,11 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
||||
in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
|
||||
goto out;
|
||||
|
||||
vaddr = kmap_atomic(ap.pages[0]);
|
||||
vaddr = kmap_local_page(ap.pages[0]);
|
||||
err = fuse_copy_ioctl_iovec(fm->fc, iov_page, vaddr,
|
||||
transferred, in_iovs + out_iovs,
|
||||
(flags & FUSE_IOCTL_COMPAT) != 0);
|
||||
kunmap_atomic(vaddr);
|
||||
kunmap_local(vaddr);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -76,11 +76,11 @@ static void fuse_add_dirent_to_cache(struct file *file,
|
||||
WARN_ON(fi->rdc.pos != pos))
|
||||
goto unlock;
|
||||
|
||||
addr = kmap_atomic(page);
|
||||
addr = kmap_local_page(page);
|
||||
if (!offset)
|
||||
clear_page(addr);
|
||||
memcpy(addr + offset, dirent, reclen);
|
||||
kunmap_atomic(addr);
|
||||
kunmap_local(addr);
|
||||
fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen;
|
||||
fi->rdc.pos = dirent->off;
|
||||
unlock:
|
||||
@ -454,7 +454,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
|
||||
* cache; both cases require an up-to-date mtime value.
|
||||
*/
|
||||
if (!ctx->pos && fc->auto_inval_data) {
|
||||
int err = fuse_update_attributes(inode, file);
|
||||
int err = fuse_update_attributes(inode, file, STATX_MTIME);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -649,7 +649,7 @@ static void virtio_fs_vq_done(struct virtqueue *vq)
|
||||
static void virtio_fs_init_vq(struct virtio_fs_vq *fsvq, char *name,
|
||||
int vq_type)
|
||||
{
|
||||
strncpy(fsvq->name, name, VQ_NAME_LEN);
|
||||
strscpy(fsvq->name, name, VQ_NAME_LEN);
|
||||
spin_lock_init(&fsvq->lock);
|
||||
INIT_LIST_HEAD(&fsvq->queued_reqs);
|
||||
INIT_LIST_HEAD(&fsvq->end_reqs);
|
||||
|
@ -42,10 +42,9 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
||||
fm->fc->no_setxattr = 1;
|
||||
err = -EOPNOTSUPP;
|
||||
}
|
||||
if (!err) {
|
||||
fuse_invalidate_attr(inode);
|
||||
if (!err)
|
||||
fuse_update_ctime(inode);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -173,10 +172,9 @@ int fuse_removexattr(struct inode *inode, const char *name)
|
||||
fm->fc->no_removexattr = 1;
|
||||
err = -EOPNOTSUPP;
|
||||
}
|
||||
if (!err) {
|
||||
fuse_invalidate_attr(inode);
|
||||
if (!err)
|
||||
fuse_update_ctime(inode);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,9 @@
|
||||
*
|
||||
* 7.34
|
||||
* - add FUSE_SYNCFS
|
||||
*
|
||||
* 7.35
|
||||
* - add FOPEN_NOFLUSH
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@ -219,7 +222,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 34
|
||||
#define FUSE_KERNEL_MINOR_VERSION 35
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@ -290,12 +293,14 @@ struct fuse_file_lock {
|
||||
* FOPEN_NONSEEKABLE: the file is not seekable
|
||||
* FOPEN_CACHE_DIR: allow caching this directory
|
||||
* FOPEN_STREAM: the file is stream-like (no file position at all)
|
||||
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
|
||||
*/
|
||||
#define FOPEN_DIRECT_IO (1 << 0)
|
||||
#define FOPEN_KEEP_CACHE (1 << 1)
|
||||
#define FOPEN_NONSEEKABLE (1 << 2)
|
||||
#define FOPEN_CACHE_DIR (1 << 3)
|
||||
#define FOPEN_STREAM (1 << 4)
|
||||
#define FOPEN_NOFLUSH (1 << 5)
|
||||
|
||||
/**
|
||||
* INIT request/reply flags
|
||||
|
Loading…
Reference in New Issue
Block a user