// SPDX-License-Identifier: GPL-2.01/*2* Copyright (c) 2016 Trond Myklebust3* Copyright (c) 2019 Jeff Layton4*5* I/O and data path helper functionality.6*7* Heavily borrowed from equivalent code in fs/nfs/io.c8*/910#include <linux/ceph/ceph_debug.h>1112#include <linux/types.h>13#include <linux/kernel.h>14#include <linux/rwsem.h>15#include <linux/fs.h>1617#include "super.h"18#include "io.h"1920/* Call with exclusively locked inode->i_rwsem */21static void ceph_block_o_direct(struct ceph_inode_info *ci, struct inode *inode)22{23bool is_odirect;2425lockdep_assert_held_write(&inode->i_rwsem);2627spin_lock(&ci->i_ceph_lock);28/* ensure that bit state is consistent */29smp_mb__before_atomic();30is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;31if (is_odirect) {32clear_bit(CEPH_I_ODIRECT_BIT, &ci->i_ceph_flags);33/* ensure modified bit is visible */34smp_mb__after_atomic();35}36spin_unlock(&ci->i_ceph_lock);3738if (is_odirect)39inode_dio_wait(inode);40}4142/**43* ceph_start_io_read - declare the file is being used for buffered reads44* @inode: file inode45*46* Declare that a buffered read operation is about to start, and ensure47* that we block all direct I/O.48* On exit, the function ensures that the CEPH_I_ODIRECT flag is unset,49* and holds a shared lock on inode->i_rwsem to ensure that the flag50* cannot be changed.51* In practice, this means that buffered read operations are allowed to52* execute in parallel, thanks to the shared lock, whereas direct I/O53* operations need to wait to grab an exclusive lock in order to set54* CEPH_I_ODIRECT.55* Note that buffered writes and truncates both take a write lock on56* inode->i_rwsem, meaning that those are serialised w.r.t. the reads.57*/58int ceph_start_io_read(struct inode *inode)59{60struct ceph_inode_info *ci = ceph_inode(inode);61bool is_odirect;62int err;6364/* Be an optimist! */65err = down_read_killable(&inode->i_rwsem);66if (err)67return err;6869spin_lock(&ci->i_ceph_lock);70/* ensure that bit state is consistent */71smp_mb__before_atomic();72is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;73spin_unlock(&ci->i_ceph_lock);74if (!is_odirect)75return 0;76up_read(&inode->i_rwsem);7778/* Slow path.... */79err = down_write_killable(&inode->i_rwsem);80if (err)81return err;8283ceph_block_o_direct(ci, inode);84downgrade_write(&inode->i_rwsem);8586return 0;87}8889/**90* ceph_end_io_read - declare that the buffered read operation is done91* @inode: file inode92*93* Declare that a buffered read operation is done, and release the shared94* lock on inode->i_rwsem.95*/96void97ceph_end_io_read(struct inode *inode)98{99up_read(&inode->i_rwsem);100}101102/**103* ceph_start_io_write - declare the file is being used for buffered writes104* @inode: file inode105*106* Declare that a buffered write operation is about to start, and ensure107* that we block all direct I/O.108*/109int ceph_start_io_write(struct inode *inode)110{111int err = down_write_killable(&inode->i_rwsem);112if (!err)113ceph_block_o_direct(ceph_inode(inode), inode);114return err;115}116117/**118* ceph_end_io_write - declare that the buffered write operation is done119* @inode: file inode120*121* Declare that a buffered write operation is done, and release the122* lock on inode->i_rwsem.123*/124void125ceph_end_io_write(struct inode *inode)126{127up_write(&inode->i_rwsem);128}129130/* Call with exclusively locked inode->i_rwsem */131static void ceph_block_buffered(struct ceph_inode_info *ci, struct inode *inode)132{133bool is_odirect;134135lockdep_assert_held_write(&inode->i_rwsem);136137spin_lock(&ci->i_ceph_lock);138/* ensure that bit state is consistent */139smp_mb__before_atomic();140is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;141if (!is_odirect) {142set_bit(CEPH_I_ODIRECT_BIT, &ci->i_ceph_flags);143/* ensure modified bit is visible */144smp_mb__after_atomic();145}146spin_unlock(&ci->i_ceph_lock);147148if (!is_odirect) {149/* FIXME: unmap_mapping_range? */150filemap_write_and_wait(inode->i_mapping);151}152}153154/**155* ceph_start_io_direct - declare the file is being used for direct i/o156* @inode: file inode157*158* Declare that a direct I/O operation is about to start, and ensure159* that we block all buffered I/O.160* On exit, the function ensures that the CEPH_I_ODIRECT flag is set,161* and holds a shared lock on inode->i_rwsem to ensure that the flag162* cannot be changed.163* In practice, this means that direct I/O operations are allowed to164* execute in parallel, thanks to the shared lock, whereas buffered I/O165* operations need to wait to grab an exclusive lock in order to clear166* CEPH_I_ODIRECT.167* Note that buffered writes and truncates both take a write lock on168* inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT.169*/170int ceph_start_io_direct(struct inode *inode)171{172struct ceph_inode_info *ci = ceph_inode(inode);173bool is_odirect;174int err;175176/* Be an optimist! */177err = down_read_killable(&inode->i_rwsem);178if (err)179return err;180181spin_lock(&ci->i_ceph_lock);182/* ensure that bit state is consistent */183smp_mb__before_atomic();184is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;185spin_unlock(&ci->i_ceph_lock);186if (is_odirect)187return 0;188up_read(&inode->i_rwsem);189190/* Slow path.... */191err = down_write_killable(&inode->i_rwsem);192if (err)193return err;194195ceph_block_buffered(ci, inode);196downgrade_write(&inode->i_rwsem);197198return 0;199}200201/**202* ceph_end_io_direct - declare that the direct i/o operation is done203* @inode: file inode204*205* Declare that a direct I/O operation is done, and release the shared206* lock on inode->i_rwsem.207*/208void209ceph_end_io_direct(struct inode *inode)210{211up_read(&inode->i_rwsem);212}213214215