ocfs2: Provide a splice-read wrapper
authorDavid Howells <dhowells@redhat.com>
Mon, 22 May 2023 13:50:09 +0000 (14:50 +0100)
committerJens Axboe <axboe@kernel.dk>
Wed, 24 May 2023 14:42:16 +0000 (08:42 -0600)
Provide a splice_read wrapper for ocfs2.  This emits trace lines and does
an atime lock/update before calling filemap_splice_read().  Splicing from
direct I/O is handled by the caller.

A couple of new tracepoints are added for this purpose.

Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
cc: Christoph Hellwig <hch@lst.de>
cc: Al Viro <viro@zeniv.linux.org.uk>
cc: Jens Axboe <axboe@kernel.dk>
cc: Mark Fasheh <mark@fasheh.com>
cc: Joel Becker <jlbec@evilplan.org>
cc: ocfs2-devel@oss.oracle.com
cc: linux-fsdevel@vger.kernel.org
cc: linux-block@vger.kernel.org
cc: linux-mm@kvack.org
Link: https://lore.kernel.org/r/20230522135018.2742245-23-dhowells@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/ocfs2/file.c
fs/ocfs2/ocfs2_trace.h

index efb09de..86add13 100644 (file)
@@ -2552,7 +2552,7 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb,
         *
         * Take and drop the meta data lock to update inode fields
         * like i_size. This allows the checks down below
-        * generic_file_read_iter() a chance of actually working.
+        * copy_splice_read() a chance of actually working.
         */
        ret = ocfs2_inode_lock_atime(inode, filp->f_path.mnt, &lock_level,
                                     !nowait);
@@ -2581,6 +2581,43 @@ bail:
        return ret;
 }
 
+static ssize_t ocfs2_file_splice_read(struct file *in, loff_t *ppos,
+                                     struct pipe_inode_info *pipe,
+                                     size_t len, unsigned int flags)
+{
+       struct inode *inode = file_inode(in);
+       ssize_t ret = 0;
+       int lock_level = 0;
+
+       trace_ocfs2_file_splice_read(inode, in, in->f_path.dentry,
+                                    (unsigned long long)OCFS2_I(inode)->ip_blkno,
+                                    in->f_path.dentry->d_name.len,
+                                    in->f_path.dentry->d_name.name,
+                                    flags);
+
+       /*
+        * We're fine letting folks race truncates and extending writes with
+        * read across the cluster, just like they can locally.  Hence no
+        * rw_lock during read.
+        *
+        * Take and drop the meta data lock to update inode fields like i_size.
+        * This allows the checks down below filemap_splice_read() a chance of
+        * actually working.
+        */
+       ret = ocfs2_inode_lock_atime(inode, in->f_path.mnt, &lock_level, 1);
+       if (ret < 0) {
+               if (ret != -EAGAIN)
+                       mlog_errno(ret);
+               goto bail;
+       }
+       ocfs2_inode_unlock(inode, lock_level);
+
+       ret = filemap_splice_read(in, ppos, pipe, len, flags);
+       trace_filemap_splice_read_ret(ret);
+bail:
+       return ret;
+}
+
 /* Refer generic_file_llseek_unlocked() */
 static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int whence)
 {
@@ -2744,7 +2781,7 @@ const struct file_operations ocfs2_fops = {
 #endif
        .lock           = ocfs2_lock,
        .flock          = ocfs2_flock,
-       .splice_read    = generic_file_splice_read,
+       .splice_read    = ocfs2_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .fallocate      = ocfs2_fallocate,
        .remap_file_range = ocfs2_remap_file_range,
index dc4bce1..b8c3d17 100644 (file)
@@ -1319,6 +1319,8 @@ DEFINE_OCFS2_FILE_OPS(ocfs2_file_splice_write);
 
 DEFINE_OCFS2_FILE_OPS(ocfs2_file_read_iter);
 
+DEFINE_OCFS2_FILE_OPS(ocfs2_file_splice_read);
+
 DEFINE_OCFS2_ULL_ULL_ULL_EVENT(ocfs2_truncate_file);
 
 DEFINE_OCFS2_ULL_ULL_EVENT(ocfs2_truncate_file_error);
@@ -1470,6 +1472,7 @@ TRACE_EVENT(ocfs2_prepare_inode_for_write,
 );
 
 DEFINE_OCFS2_INT_EVENT(generic_file_read_iter_ret);
+DEFINE_OCFS2_INT_EVENT(filemap_splice_read_ret);
 
 /* End of trace events for fs/ocfs2/file.c. */