fuse fixes for 6.9-rc5

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCZiJcTAAKCRDh3BK/laaZ
 PK1QAP9u/S7GYKDj0k58xOVAof2x/q0puHWXoObRma+bPmeoeQEA2+K+vlnTJHub
 kLRURaTCzGyFfL+CB/JQ4Kv4tDF5qQc=
 =Eoob
 -----END PGP SIGNATURE-----

Merge tag 'fuse-fixes-6.9-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse fixes from Miklos Szeredi:

 - Fix two bugs in the new passthrough mode

 - Fix a statx bug introduced in v6.6

 - Fix code documentation

* tag 'fuse-fixes-6.9-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  cuse: add kernel-doc comments to cuse_process_init_reply()
  fuse: fix leaked ENOSYS error on first statx call
  fuse: fix parallel dio write on file open in passthrough mode
  fuse: fix wrong ff->iomode state changes from parallel dio write
This commit is contained in:
Linus Torvalds 2024-04-19 13:16:10 -07:00
commit daa757767d
6 changed files with 58 additions and 27 deletions

View file

@ -310,6 +310,10 @@ struct cuse_init_args {
/** /**
* cuse_process_init_reply - finish initializing CUSE channel * cuse_process_init_reply - finish initializing CUSE channel
* *
* @fm: The fuse mount information containing the CUSE connection.
* @args: The arguments passed to the init reply.
* @error: The error code signifying if any error occurred during the process.
*
* This function creates the character device and sets up all the * This function creates the character device and sets up all the
* required data structures for it. Please read the comment at the * required data structures for it. Please read the comment at the
* top of this file for high level overview. * top of this file for high level overview.

View file

@ -1321,6 +1321,7 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
err = fuse_do_statx(inode, file, stat); err = fuse_do_statx(inode, file, stat);
if (err == -ENOSYS) { if (err == -ENOSYS) {
fc->no_statx = 1; fc->no_statx = 1;
err = 0;
goto retry; goto retry;
} }
} else { } else {

View file

@ -1362,7 +1362,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
bool *exclusive) bool *exclusive)
{ {
struct inode *inode = file_inode(iocb->ki_filp); struct inode *inode = file_inode(iocb->ki_filp);
struct fuse_file *ff = iocb->ki_filp->private_data; struct fuse_inode *fi = get_fuse_inode(inode);
*exclusive = fuse_dio_wr_exclusive_lock(iocb, from); *exclusive = fuse_dio_wr_exclusive_lock(iocb, from);
if (*exclusive) { if (*exclusive) {
@ -1377,7 +1377,7 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
* have raced, so check it again. * have raced, so check it again.
*/ */
if (fuse_io_past_eof(iocb, from) || if (fuse_io_past_eof(iocb, from) ||
fuse_file_uncached_io_start(inode, ff, NULL) != 0) { fuse_inode_uncached_io_start(fi, NULL) != 0) {
inode_unlock_shared(inode); inode_unlock_shared(inode);
inode_lock(inode); inode_lock(inode);
*exclusive = true; *exclusive = true;
@ -1388,13 +1388,13 @@ static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive) static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
{ {
struct inode *inode = file_inode(iocb->ki_filp); struct inode *inode = file_inode(iocb->ki_filp);
struct fuse_file *ff = iocb->ki_filp->private_data; struct fuse_inode *fi = get_fuse_inode(inode);
if (exclusive) { if (exclusive) {
inode_unlock(inode); inode_unlock(inode);
} else { } else {
/* Allow opens in caching mode after last parallel dio end */ /* Allow opens in caching mode after last parallel dio end */
fuse_file_uncached_io_end(inode, ff); fuse_inode_uncached_io_end(fi);
inode_unlock_shared(inode); inode_unlock_shared(inode);
} }
} }
@ -2574,8 +2574,10 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
* First mmap of direct_io file enters caching inode io mode. * First mmap of direct_io file enters caching inode io mode.
* Also waits for parallel dio writers to go into serial mode * Also waits for parallel dio writers to go into serial mode
* (exclusive instead of shared lock). * (exclusive instead of shared lock).
* After first mmap, the inode stays in caching io mode until
* the direct_io file release.
*/ */
rc = fuse_file_cached_io_start(inode, ff); rc = fuse_file_cached_io_open(inode, ff);
if (rc) if (rc)
return rc; return rc;
} }

View file

@ -1394,9 +1394,10 @@ int fuse_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry, struct fileattr *fa); struct dentry *dentry, struct fileattr *fa);
/* iomode.c */ /* iomode.c */
int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff); int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff);
int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struct fuse_backing *fb); int fuse_inode_uncached_io_start(struct fuse_inode *fi,
void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff); struct fuse_backing *fb);
void fuse_inode_uncached_io_end(struct fuse_inode *fi);
int fuse_file_io_open(struct file *file, struct inode *inode); int fuse_file_io_open(struct file *file, struct inode *inode);
void fuse_file_io_release(struct fuse_file *ff, struct inode *inode); void fuse_file_io_release(struct fuse_file *ff, struct inode *inode);

View file

@ -175,6 +175,7 @@ static void fuse_evict_inode(struct inode *inode)
} }
} }
if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) { if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
WARN_ON(fi->iocachectr != 0);
WARN_ON(!list_empty(&fi->write_files)); WARN_ON(!list_empty(&fi->write_files));
WARN_ON(!list_empty(&fi->queued_writes)); WARN_ON(!list_empty(&fi->queued_writes));
} }

View file

@ -21,12 +21,13 @@ static inline bool fuse_is_io_cache_wait(struct fuse_inode *fi)
} }
/* /*
* Start cached io mode. * Called on cached file open() and on first mmap() of direct_io file.
* Takes cached_io inode mode reference to be dropped on file release.
* *
* Blocks new parallel dio writes and waits for the in-progress parallel dio * Blocks new parallel dio writes and waits for the in-progress parallel dio
* writes to complete. * writes to complete.
*/ */
int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff) int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff)
{ {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
@ -67,10 +68,9 @@ int fuse_file_cached_io_start(struct inode *inode, struct fuse_file *ff)
return 0; return 0;
} }
static void fuse_file_cached_io_end(struct inode *inode, struct fuse_file *ff) static void fuse_file_cached_io_release(struct fuse_file *ff,
struct fuse_inode *fi)
{ {
struct fuse_inode *fi = get_fuse_inode(inode);
spin_lock(&fi->lock); spin_lock(&fi->lock);
WARN_ON(fi->iocachectr <= 0); WARN_ON(fi->iocachectr <= 0);
WARN_ON(ff->iomode != IOM_CACHED); WARN_ON(ff->iomode != IOM_CACHED);
@ -82,16 +82,15 @@ static void fuse_file_cached_io_end(struct inode *inode, struct fuse_file *ff)
} }
/* Start strictly uncached io mode where cache access is not allowed */ /* Start strictly uncached io mode where cache access is not allowed */
int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struct fuse_backing *fb) int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb)
{ {
struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_backing *oldfb; struct fuse_backing *oldfb;
int err = 0; int err = 0;
spin_lock(&fi->lock); spin_lock(&fi->lock);
/* deny conflicting backing files on same fuse inode */ /* deny conflicting backing files on same fuse inode */
oldfb = fuse_inode_backing(fi); oldfb = fuse_inode_backing(fi);
if (oldfb && oldfb != fb) { if (fb && oldfb && oldfb != fb) {
err = -EBUSY; err = -EBUSY;
goto unlock; goto unlock;
} }
@ -99,12 +98,10 @@ int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struc
err = -ETXTBSY; err = -ETXTBSY;
goto unlock; goto unlock;
} }
WARN_ON(ff->iomode != IOM_NONE);
fi->iocachectr--; fi->iocachectr--;
ff->iomode = IOM_UNCACHED;
/* fuse inode holds a single refcount of backing file */ /* fuse inode holds a single refcount of backing file */
if (!oldfb) { if (fb && !oldfb) {
oldfb = fuse_inode_backing_set(fi, fb); oldfb = fuse_inode_backing_set(fi, fb);
WARN_ON_ONCE(oldfb != NULL); WARN_ON_ONCE(oldfb != NULL);
} else { } else {
@ -115,15 +112,29 @@ int fuse_file_uncached_io_start(struct inode *inode, struct fuse_file *ff, struc
return err; return err;
} }
void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff) /* Takes uncached_io inode mode reference to be dropped on file release */
static int fuse_file_uncached_io_open(struct inode *inode,
struct fuse_file *ff,
struct fuse_backing *fb)
{ {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
int err;
err = fuse_inode_uncached_io_start(fi, fb);
if (err)
return err;
WARN_ON(ff->iomode != IOM_NONE);
ff->iomode = IOM_UNCACHED;
return 0;
}
void fuse_inode_uncached_io_end(struct fuse_inode *fi)
{
struct fuse_backing *oldfb = NULL; struct fuse_backing *oldfb = NULL;
spin_lock(&fi->lock); spin_lock(&fi->lock);
WARN_ON(fi->iocachectr >= 0); WARN_ON(fi->iocachectr >= 0);
WARN_ON(ff->iomode != IOM_UNCACHED);
ff->iomode = IOM_NONE;
fi->iocachectr++; fi->iocachectr++;
if (!fi->iocachectr) { if (!fi->iocachectr) {
wake_up(&fi->direct_io_waitq); wake_up(&fi->direct_io_waitq);
@ -134,6 +145,15 @@ void fuse_file_uncached_io_end(struct inode *inode, struct fuse_file *ff)
fuse_backing_put(oldfb); fuse_backing_put(oldfb);
} }
/* Drop uncached_io reference from passthrough open */
static void fuse_file_uncached_io_release(struct fuse_file *ff,
struct fuse_inode *fi)
{
WARN_ON(ff->iomode != IOM_UNCACHED);
ff->iomode = IOM_NONE;
fuse_inode_uncached_io_end(fi);
}
/* /*
* Open flags that are allowed in combination with FOPEN_PASSTHROUGH. * Open flags that are allowed in combination with FOPEN_PASSTHROUGH.
* A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write * A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write
@ -163,7 +183,7 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
return PTR_ERR(fb); return PTR_ERR(fb);
/* First passthrough file open denies caching inode io mode */ /* First passthrough file open denies caching inode io mode */
err = fuse_file_uncached_io_start(inode, ff, fb); err = fuse_file_uncached_io_open(inode, ff, fb);
if (!err) if (!err)
return 0; return 0;
@ -216,7 +236,7 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
if (ff->open_flags & FOPEN_PASSTHROUGH) if (ff->open_flags & FOPEN_PASSTHROUGH)
err = fuse_file_passthrough_open(inode, file); err = fuse_file_passthrough_open(inode, file);
else else
err = fuse_file_cached_io_start(inode, ff); err = fuse_file_cached_io_open(inode, ff);
if (err) if (err)
goto fail; goto fail;
@ -236,8 +256,10 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
/* No more pending io and no new io possible to inode via open/mmapped file */ /* No more pending io and no new io possible to inode via open/mmapped file */
void fuse_file_io_release(struct fuse_file *ff, struct inode *inode) void fuse_file_io_release(struct fuse_file *ff, struct inode *inode)
{ {
struct fuse_inode *fi = get_fuse_inode(inode);
/* /*
* Last parallel dio close allows caching inode io mode. * Last passthrough file close allows caching inode io mode.
* Last caching file close exits caching inode io mode. * Last caching file close exits caching inode io mode.
*/ */
switch (ff->iomode) { switch (ff->iomode) {
@ -245,10 +267,10 @@ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode)
/* Nothing to do */ /* Nothing to do */
break; break;
case IOM_UNCACHED: case IOM_UNCACHED:
fuse_file_uncached_io_end(inode, ff); fuse_file_uncached_io_release(ff, fi);
break; break;
case IOM_CACHED: case IOM_CACHED:
fuse_file_cached_io_end(inode, ff); fuse_file_cached_io_release(ff, fi);
break; break;
} }
} }