]> Git Repo - J-linux.git/commitdiff
Merge tag 'fuse-update-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszered...
authorLinus Torvalds <[email protected]>
Tue, 9 Nov 2021 18:46:32 +0000 (10:46 -0800)
committerLinus Torvalds <[email protected]>
Tue, 9 Nov 2021 18:46:32 +0000 (10:46 -0800)
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()
  ...

1  2 
fs/fuse/file.c

diff --combined fs/fuse/file.c
index 34b6d0650e668b3a5ade49178c2516df92146531,b7f1a164e18a9eb9ce59ea8a94061808fb360779..9d6c5f6361f7d907cefafa60ee0f14c2450c5165
@@@ -211,9 -211,8 +211,8 @@@ void fuse_finish_open(struct inode *ino
                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 +338,6 @@@ static int fuse_open(struct inode *inod
  
  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 +476,9 @@@ static int fuse_flush(struct file *file
        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 +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;
  }
  
@@@ -687,7 -683,7 +683,7 @@@ static void fuse_aio_complete(struct fu
                        spin_unlock(&fi->lock);
                }
  
 -              io->iocb->ki_complete(io->iocb, res, 0);
 +              io->iocb->ki_complete(io->iocb, res);
        }
  
        kref_put(&io->refcnt, fuse_io_release);
@@@ -793,7 -789,7 +789,7 @@@ static void fuse_read_update_size(struc
        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 +999,7 @@@ static ssize_t fuse_cache_read_iter(str
        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 +1068,7 @@@ static ssize_t fuse_send_write(struct f
        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);
  
        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;
  }
  
@@@ -1164,7 -1162,7 +1162,7 @@@ static ssize_t fuse_fill_write_pages(st
  
   again:
                err = -EFAULT;
 -              if (iov_iter_fault_in_readable(ii, bytes))
 +              if (fault_in_iov_iter_readable(ii, bytes))
                        break;
  
                err = -ENOMEM;
@@@ -1268,11 -1266,8 +1266,8 @@@ static ssize_t fuse_perform_write(struc
                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 +1285,8 @@@ static ssize_t fuse_cache_write_iter(st
  
        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 +1447,6 @@@ ssize_t fuse_direct_io(struct fuse_io_p
        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 +1556,9 @@@ static ssize_t fuse_direct_write_iter(s
                } 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 +1769,7 @@@ static void fuse_writepage_end(struct f
         * 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) {
  
  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 +1840,17 @@@ int fuse_write_inode(struct inode *inod
        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 +2309,18 @@@ static int fuse_write_end(struct file *
        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 +2346,15 @@@ static int fuse_launder_page(struct pag
  }
  
  /*
-  * 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 +2637,7 @@@ static loff_t fuse_lseek(struct file *f
        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 +2657,7 @@@ static loff_t fuse_file_llseek(struct f
                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 +2878,7 @@@ fuse_direct_IO(struct kiocb *iocb, stru
  
        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);
        }
        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 +2989,14 @@@ static long fuse_file_fallocate(struct 
  
        /* 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))
        if (lock_inode)
                inode_unlock(inode);
  
+       fuse_flush_time_update(inode);
        return err;
  }
  
@@@ -3096,12 -3104,8 +3104,8 @@@ static ssize_t __fuse_copy_file_range(s
                                   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:
        inode_unlock(inode_out);
        file_accessed(file_in);
  
+       fuse_flush_time_update(inode_out);
        return err;
  }
  
This page took 0.075332 seconds and 4 git commands to generate.