Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2016 Trond Myklebust
0004  * Copyright (c) 2019 Jeff Layton
0005  *
0006  * I/O and data path helper functionality.
0007  *
0008  * Heavily borrowed from equivalent code in fs/nfs/io.c
0009  */
0010 
0011 #include <linux/ceph/ceph_debug.h>
0012 
0013 #include <linux/types.h>
0014 #include <linux/kernel.h>
0015 #include <linux/rwsem.h>
0016 #include <linux/fs.h>
0017 
0018 #include "super.h"
0019 #include "io.h"
0020 
0021 /* Call with exclusively locked inode->i_rwsem */
0022 static void ceph_block_o_direct(struct ceph_inode_info *ci, struct inode *inode)
0023 {
0024     lockdep_assert_held_write(&inode->i_rwsem);
0025 
0026     if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT) {
0027         spin_lock(&ci->i_ceph_lock);
0028         ci->i_ceph_flags &= ~CEPH_I_ODIRECT;
0029         spin_unlock(&ci->i_ceph_lock);
0030         inode_dio_wait(inode);
0031     }
0032 }
0033 
0034 /**
0035  * ceph_start_io_read - declare the file is being used for buffered reads
0036  * @inode: file inode
0037  *
0038  * Declare that a buffered read operation is about to start, and ensure
0039  * that we block all direct I/O.
0040  * On exit, the function ensures that the CEPH_I_ODIRECT flag is unset,
0041  * and holds a shared lock on inode->i_rwsem to ensure that the flag
0042  * cannot be changed.
0043  * In practice, this means that buffered read operations are allowed to
0044  * execute in parallel, thanks to the shared lock, whereas direct I/O
0045  * operations need to wait to grab an exclusive lock in order to set
0046  * CEPH_I_ODIRECT.
0047  * Note that buffered writes and truncates both take a write lock on
0048  * inode->i_rwsem, meaning that those are serialised w.r.t. the reads.
0049  */
0050 void
0051 ceph_start_io_read(struct inode *inode)
0052 {
0053     struct ceph_inode_info *ci = ceph_inode(inode);
0054 
0055     /* Be an optimist! */
0056     down_read(&inode->i_rwsem);
0057     if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT))
0058         return;
0059     up_read(&inode->i_rwsem);
0060     /* Slow path.... */
0061     down_write(&inode->i_rwsem);
0062     ceph_block_o_direct(ci, inode);
0063     downgrade_write(&inode->i_rwsem);
0064 }
0065 
0066 /**
0067  * ceph_end_io_read - declare that the buffered read operation is done
0068  * @inode: file inode
0069  *
0070  * Declare that a buffered read operation is done, and release the shared
0071  * lock on inode->i_rwsem.
0072  */
0073 void
0074 ceph_end_io_read(struct inode *inode)
0075 {
0076     up_read(&inode->i_rwsem);
0077 }
0078 
0079 /**
0080  * ceph_start_io_write - declare the file is being used for buffered writes
0081  * @inode: file inode
0082  *
0083  * Declare that a buffered write operation is about to start, and ensure
0084  * that we block all direct I/O.
0085  */
0086 void
0087 ceph_start_io_write(struct inode *inode)
0088 {
0089     down_write(&inode->i_rwsem);
0090     ceph_block_o_direct(ceph_inode(inode), inode);
0091 }
0092 
0093 /**
0094  * ceph_end_io_write - declare that the buffered write operation is done
0095  * @inode: file inode
0096  *
0097  * Declare that a buffered write operation is done, and release the
0098  * lock on inode->i_rwsem.
0099  */
0100 void
0101 ceph_end_io_write(struct inode *inode)
0102 {
0103     up_write(&inode->i_rwsem);
0104 }
0105 
0106 /* Call with exclusively locked inode->i_rwsem */
0107 static void ceph_block_buffered(struct ceph_inode_info *ci, struct inode *inode)
0108 {
0109     lockdep_assert_held_write(&inode->i_rwsem);
0110 
0111     if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT)) {
0112         spin_lock(&ci->i_ceph_lock);
0113         ci->i_ceph_flags |= CEPH_I_ODIRECT;
0114         spin_unlock(&ci->i_ceph_lock);
0115         /* FIXME: unmap_mapping_range? */
0116         filemap_write_and_wait(inode->i_mapping);
0117     }
0118 }
0119 
0120 /**
0121  * ceph_start_io_direct - declare the file is being used for direct i/o
0122  * @inode: file inode
0123  *
0124  * Declare that a direct I/O operation is about to start, and ensure
0125  * that we block all buffered I/O.
0126  * On exit, the function ensures that the CEPH_I_ODIRECT flag is set,
0127  * and holds a shared lock on inode->i_rwsem to ensure that the flag
0128  * cannot be changed.
0129  * In practice, this means that direct I/O operations are allowed to
0130  * execute in parallel, thanks to the shared lock, whereas buffered I/O
0131  * operations need to wait to grab an exclusive lock in order to clear
0132  * CEPH_I_ODIRECT.
0133  * Note that buffered writes and truncates both take a write lock on
0134  * inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT.
0135  */
0136 void
0137 ceph_start_io_direct(struct inode *inode)
0138 {
0139     struct ceph_inode_info *ci = ceph_inode(inode);
0140 
0141     /* Be an optimist! */
0142     down_read(&inode->i_rwsem);
0143     if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT)
0144         return;
0145     up_read(&inode->i_rwsem);
0146     /* Slow path.... */
0147     down_write(&inode->i_rwsem);
0148     ceph_block_buffered(ci, inode);
0149     downgrade_write(&inode->i_rwsem);
0150 }
0151 
0152 /**
0153  * ceph_end_io_direct - declare that the direct i/o operation is done
0154  * @inode: file inode
0155  *
0156  * Declare that a direct I/O operation is done, and release the shared
0157  * lock on inode->i_rwsem.
0158  */
0159 void
0160 ceph_end_io_direct(struct inode *inode)
0161 {
0162     up_read(&inode->i_rwsem);
0163 }