]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/overlayfs/file.c
ovl: fix lockdep warning for async write
[linux.git] / fs / overlayfs / file.c
index e235a635d9ec7eb90c83ce3eab4e4eeb7631a961..87c362f65448b956f5ca425ac40f728dae408dab 100644 (file)
@@ -9,8 +9,19 @@
 #include <linux/xattr.h>
 #include <linux/uio.h>
 #include <linux/uaccess.h>
+#include <linux/splice.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
 #include "overlayfs.h"
 
+struct ovl_aio_req {
+       struct kiocb iocb;
+       struct kiocb *orig_iocb;
+       struct fd fd;
+};
+
+static struct kmem_cache *ovl_aio_request_cachep;
+
 static char ovl_whatisit(struct inode *inode, struct inode *realinode)
 {
        if (realinode != ovl_inode_upper(inode))
@@ -146,7 +157,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
        struct inode *inode = file_inode(file);
        struct fd real;
        const struct cred *old_cred;
-       ssize_t ret;
+       loff_t ret;
 
        /*
         * The two special cases below do not need to involve real fs,
@@ -171,7 +182,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
         * limitations that are more strict than ->s_maxbytes for specific
         * files, so we use the real file to perform seeks.
         */
-       inode_lock(inode);
+       ovl_inode_lock(inode);
        real.file->f_pos = file->f_pos;
 
        old_cred = ovl_override_creds(inode->i_sb);
@@ -179,7 +190,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
        revert_creds(old_cred);
 
        file->f_pos = real.file->f_pos;
-       inode_unlock(inode);
+       ovl_inode_unlock(inode);
 
        fdput(real);
 
@@ -225,6 +236,36 @@ static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
        return flags;
 }
 
+static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
+{
+       struct kiocb *iocb = &aio_req->iocb;
+       struct kiocb *orig_iocb = aio_req->orig_iocb;
+
+       if (iocb->ki_flags & IOCB_WRITE) {
+               struct inode *inode = file_inode(orig_iocb->ki_filp);
+
+               /* Actually acquired in ovl_write_iter() */
+               __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb,
+                                     SB_FREEZE_WRITE);
+               file_end_write(iocb->ki_filp);
+               ovl_copyattr(ovl_inode_real(inode), inode);
+       }
+
+       orig_iocb->ki_pos = iocb->ki_pos;
+       fdput(aio_req->fd);
+       kmem_cache_free(ovl_aio_request_cachep, aio_req);
+}
+
+static void ovl_aio_rw_complete(struct kiocb *iocb, long res, long res2)
+{
+       struct ovl_aio_req *aio_req = container_of(iocb,
+                                                  struct ovl_aio_req, iocb);
+       struct kiocb *orig_iocb = aio_req->orig_iocb;
+
+       ovl_aio_cleanup_handler(aio_req);
+       orig_iocb->ki_complete(orig_iocb, res, res2);
+}
+
 static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
        struct file *file = iocb->ki_filp;
@@ -240,10 +281,28 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
                return ret;
 
        old_cred = ovl_override_creds(file_inode(file)->i_sb);
-       ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
-                           ovl_iocb_to_rwf(iocb));
+       if (is_sync_kiocb(iocb)) {
+               ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
+                                   ovl_iocb_to_rwf(iocb));
+       } else {
+               struct ovl_aio_req *aio_req;
+
+               ret = -ENOMEM;
+               aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
+               if (!aio_req)
+                       goto out;
+
+               aio_req->fd = real;
+               real.flags = 0;
+               aio_req->orig_iocb = iocb;
+               kiocb_clone(&aio_req->iocb, iocb, real.file);
+               aio_req->iocb.ki_complete = ovl_aio_rw_complete;
+               ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter);
+               if (ret != -EIOCBQUEUED)
+                       ovl_aio_cleanup_handler(aio_req);
+       }
+out:
        revert_creds(old_cred);
-
        ovl_file_accessed(file);
 
        fdput(real);
@@ -274,15 +333,36 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
                goto out_unlock;
 
        old_cred = ovl_override_creds(file_inode(file)->i_sb);
-       file_start_write(real.file);
-       ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
-                            ovl_iocb_to_rwf(iocb));
-       file_end_write(real.file);
+       if (is_sync_kiocb(iocb)) {
+               file_start_write(real.file);
+               ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
+                                    ovl_iocb_to_rwf(iocb));
+               file_end_write(real.file);
+               /* Update size */
+               ovl_copyattr(ovl_inode_real(inode), inode);
+       } else {
+               struct ovl_aio_req *aio_req;
+
+               ret = -ENOMEM;
+               aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
+               if (!aio_req)
+                       goto out;
+
+               file_start_write(real.file);
+               /* Pacify lockdep, same trick as done in aio_write() */
+               __sb_writers_release(file_inode(real.file)->i_sb,
+                                    SB_FREEZE_WRITE);
+               aio_req->fd = real;
+               real.flags = 0;
+               aio_req->orig_iocb = iocb;
+               kiocb_clone(&aio_req->iocb, iocb, real.file);
+               aio_req->iocb.ki_complete = ovl_aio_rw_complete;
+               ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
+               if (ret != -EIOCBQUEUED)
+                       ovl_aio_cleanup_handler(aio_req);
+       }
+out:
        revert_creds(old_cred);
-
-       /* Update size */
-       ovl_copyattr(ovl_inode_real(inode), inode);
-
        fdput(real);
 
 out_unlock:
@@ -291,6 +371,48 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
        return ret;
 }
 
+static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
+                        struct pipe_inode_info *pipe, size_t len,
+                        unsigned int flags)
+{
+       ssize_t ret;
+       struct fd real;
+       const struct cred *old_cred;
+
+       ret = ovl_real_fdget(in, &real);
+       if (ret)
+               return ret;
+
+       old_cred = ovl_override_creds(file_inode(in)->i_sb);
+       ret = generic_file_splice_read(real.file, ppos, pipe, len, flags);
+       revert_creds(old_cred);
+
+       ovl_file_accessed(in);
+       fdput(real);
+       return ret;
+}
+
+static ssize_t
+ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
+                         loff_t *ppos, size_t len, unsigned int flags)
+{
+       struct fd real;
+       const struct cred *old_cred;
+       ssize_t ret;
+
+       ret = ovl_real_fdget(out, &real);
+       if (ret)
+               return ret;
+
+       old_cred = ovl_override_creds(file_inode(out)->i_sb);
+       ret = iter_file_splice_write(pipe, real.file, ppos, len, flags);
+       revert_creds(old_cred);
+
+       ovl_file_accessed(out);
+       fdput(real);
+       return ret;
+}
+
 static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 {
        struct fd real;
@@ -647,7 +769,25 @@ const struct file_operations ovl_file_operations = {
        .fadvise        = ovl_fadvise,
        .unlocked_ioctl = ovl_ioctl,
        .compat_ioctl   = ovl_compat_ioctl,
+       .splice_read    = ovl_splice_read,
+       .splice_write   = ovl_splice_write,
 
        .copy_file_range        = ovl_copy_file_range,
        .remap_file_range       = ovl_remap_file_range,
 };
+
+int __init ovl_aio_request_cache_init(void)
+{
+       ovl_aio_request_cachep = kmem_cache_create("ovl_aio_req",
+                                                  sizeof(struct ovl_aio_req),
+                                                  0, SLAB_HWCACHE_ALIGN, NULL);
+       if (!ovl_aio_request_cachep)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void ovl_aio_request_cache_destroy(void)
+{
+       kmem_cache_destroy(ovl_aio_request_cachep);
+}