Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Apr 2012 00:29:05 +0000 (17:29 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Apr 2012 00:29:05 +0000 (17:29 -0700)
Pull fuse updates from Miklos Szeredi.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: use flexible array in fuse.h
  fuse: allow nanosecond granularity
  fuse: O_DIRECT support for files
  fuse: fix nlink after unlink

1  2 
fs/fuse/file.c
fs/fuse/inode.c

diff --combined fs/fuse/file.c
@@@ -194,10 -194,6 +194,6 @@@ int fuse_open_common(struct inode *inod
        struct fuse_conn *fc = get_fuse_conn(inode);
        int err;
  
-       /* VFS checks this, but only _after_ ->open() */
-       if (file->f_flags & O_DIRECT)
-               return -EINVAL;
        err = generic_file_open(inode, file);
        if (err)
                return err;
@@@ -932,17 -928,23 +928,23 @@@ static ssize_t fuse_file_aio_write(stru
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
        size_t count = 0;
+       size_t ocount = 0;
        ssize_t written = 0;
+       ssize_t written_buffered = 0;
        struct inode *inode = mapping->host;
        ssize_t err;
        struct iov_iter i;
+       loff_t endbyte = 0;
  
        WARN_ON(iocb->ki_pos != pos);
  
-       err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
+       ocount = 0;
+       err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
        if (err)
                return err;
  
+       count = ocount;
        mutex_lock(&inode->i_mutex);
        vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
  
  
        file_update_time(file);
  
-       iov_iter_init(&i, iov, nr_segs, count, 0);
-       written = fuse_perform_write(file, mapping, &i, pos);
-       if (written >= 0)
-               iocb->ki_pos = pos + written;
+       if (file->f_flags & O_DIRECT) {
+               written = generic_file_direct_write(iocb, iov, &nr_segs,
+                                                   pos, &iocb->ki_pos,
+                                                   count, ocount);
+               if (written < 0 || written == count)
+                       goto out;
+               pos += written;
+               count -= written;
  
+               iov_iter_init(&i, iov, nr_segs, count, written);
+               written_buffered = fuse_perform_write(file, mapping, &i, pos);
+               if (written_buffered < 0) {
+                       err = written_buffered;
+                       goto out;
+               }
+               endbyte = pos + written_buffered - 1;
+               err = filemap_write_and_wait_range(file->f_mapping, pos,
+                                                  endbyte);
+               if (err)
+                       goto out;
+               invalidate_mapping_pages(file->f_mapping,
+                                        pos >> PAGE_CACHE_SHIFT,
+                                        endbyte >> PAGE_CACHE_SHIFT);
+               written += written_buffered;
+               iocb->ki_pos = pos + written_buffered;
+       } else {
+               iov_iter_init(&i, iov, nr_segs, count, 0);
+               written = fuse_perform_write(file, mapping, &i, pos);
+               if (written >= 0)
+                       iocb->ki_pos = pos + written;
+       }
  out:
        current->backing_dev_info = NULL;
        mutex_unlock(&inode->i_mutex);
@@@ -1101,30 -1133,41 +1133,41 @@@ static ssize_t fuse_direct_read(struct 
        return res;
  }
  
- static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
-                                size_t count, loff_t *ppos)
+ static ssize_t __fuse_direct_write(struct file *file, const char __user *buf,
+                                  size_t count, loff_t *ppos)
  {
        struct inode *inode = file->f_path.dentry->d_inode;
        ssize_t res;
  
-       if (is_bad_inode(inode))
-               return -EIO;
-       /* Don't allow parallel writes to the same file */
-       mutex_lock(&inode->i_mutex);
        res = generic_write_checks(file, ppos, &count, 0);
        if (!res) {
                res = fuse_direct_io(file, buf, count, ppos, 1);
                if (res > 0)
                        fuse_write_update_size(inode, *ppos);
        }
-       mutex_unlock(&inode->i_mutex);
  
        fuse_invalidate_attr(inode);
  
        return res;
  }
  
+ static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+ {
+       struct inode *inode = file->f_path.dentry->d_inode;
+       ssize_t res;
+       if (is_bad_inode(inode))
+               return -EIO;
+       /* Don't allow parallel writes to the same file */
+       mutex_lock(&inode->i_mutex);
+       res = __fuse_direct_write(file, buf, count, ppos);
+       mutex_unlock(&inode->i_mutex);
+       return res;
+ }
  static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req)
  {
        __free_page(req->pages[0]);
@@@ -1887,11 -1930,11 +1930,11 @@@ long fuse_do_ioctl(struct file *file, u
                    in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
                        goto out;
  
 -              vaddr = kmap_atomic(pages[0], KM_USER0);
 +              vaddr = kmap_atomic(pages[0]);
                err = fuse_copy_ioctl_iovec(fc, iov_page, vaddr,
                                            transferred, in_iovs + out_iovs,
                                            (flags & FUSE_IOCTL_COMPAT) != 0);
 -              kunmap_atomic(vaddr, KM_USER0);
 +              kunmap_atomic(vaddr);
                if (err)
                        goto out;
  
@@@ -2077,6 -2120,57 +2120,57 @@@ int fuse_notify_poll_wakeup(struct fuse
        return 0;
  }
  
+ static ssize_t fuse_loop_dio(struct file *filp, const struct iovec *iov,
+                            unsigned long nr_segs, loff_t *ppos, int rw)
+ {
+       const struct iovec *vector = iov;
+       ssize_t ret = 0;
+       while (nr_segs > 0) {
+               void __user *base;
+               size_t len;
+               ssize_t nr;
+               base = vector->iov_base;
+               len = vector->iov_len;
+               vector++;
+               nr_segs--;
+               if (rw == WRITE)
+                       nr = __fuse_direct_write(filp, base, len, ppos);
+               else
+                       nr = fuse_direct_read(filp, base, len, ppos);
+               if (nr < 0) {
+                       if (!ret)
+                               ret = nr;
+                       break;
+               }
+               ret += nr;
+               if (nr != len)
+                       break;
+       }
+       return ret;
+ }
+ static ssize_t
+ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+                       loff_t offset, unsigned long nr_segs)
+ {
+       ssize_t ret = 0;
+       struct file *file = NULL;
+       loff_t pos = 0;
+       file = iocb->ki_filp;
+       pos = offset;
+       ret = fuse_loop_dio(file, iov, nr_segs, &pos, rw);
+       return ret;
+ }
  static const struct file_operations fuse_file_operations = {
        .llseek         = fuse_file_llseek,
        .read           = do_sync_read,
@@@ -2120,6 -2214,7 +2214,7 @@@ static const struct address_space_opera
        .readpages      = fuse_readpages,
        .set_page_dirty = __set_page_dirty_nobuffers,
        .bmap           = fuse_bmap,
+       .direct_IO      = fuse_direct_IO,
  };
  
  void fuse_init_file_inode(struct inode *inode)
diff --combined fs/fuse/inode.c
@@@ -947,6 -947,7 +947,7 @@@ static int fuse_fill_super(struct super
        sb->s_magic = FUSE_SUPER_MAGIC;
        sb->s_op = &fuse_super_operations;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
+       sb->s_time_gran = 1;
        sb->s_export_op = &fuse_export_operations;
  
        file = fget(d.fd);
  
        err = -ENOMEM;
        root = fuse_get_root_inode(sb, d.rootmode);
 -      if (!root)
 +      root_dentry = d_make_root(root);
 +      if (!root_dentry)
                goto err_put_conn;
 -
 -      root_dentry = d_alloc_root(root);
 -      if (!root_dentry) {
 -              iput(root);
 -              goto err_put_conn;
 -      }
        /* only now - we want root dentry with NULL ->d_op */
        sb->s_d_op = &fuse_dentry_operations;