162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Trond Myklebust 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * I/O and data path helper functionality. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/bitops.h> 1162306a36Sopenharmony_ci#include <linux/rwsem.h> 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/nfs_fs.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "internal.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Call with exclusively locked inode->i_rwsem */ 1862306a36Sopenharmony_cistatic void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci if (test_bit(NFS_INO_ODIRECT, &nfsi->flags)) { 2162306a36Sopenharmony_ci clear_bit(NFS_INO_ODIRECT, &nfsi->flags); 2262306a36Sopenharmony_ci inode_dio_wait(inode); 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/** 2762306a36Sopenharmony_ci * nfs_start_io_read - declare the file is being used for buffered reads 2862306a36Sopenharmony_ci * @inode: file inode 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Declare that a buffered read operation is about to start, and ensure 3162306a36Sopenharmony_ci * that we block all direct I/O. 3262306a36Sopenharmony_ci * On exit, the function ensures that the NFS_INO_ODIRECT flag is unset, 3362306a36Sopenharmony_ci * and holds a shared lock on inode->i_rwsem to ensure that the flag 3462306a36Sopenharmony_ci * cannot be changed. 3562306a36Sopenharmony_ci * In practice, this means that buffered read operations are allowed to 3662306a36Sopenharmony_ci * execute in parallel, thanks to the shared lock, whereas direct I/O 3762306a36Sopenharmony_ci * operations need to wait to grab an exclusive lock in order to set 3862306a36Sopenharmony_ci * NFS_INO_ODIRECT. 3962306a36Sopenharmony_ci * Note that buffered writes and truncates both take a write lock on 4062306a36Sopenharmony_ci * inode->i_rwsem, meaning that those are serialised w.r.t. the reads. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_civoid 4362306a36Sopenharmony_cinfs_start_io_read(struct inode *inode) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 4662306a36Sopenharmony_ci /* Be an optimist! */ 4762306a36Sopenharmony_ci down_read(&inode->i_rwsem); 4862306a36Sopenharmony_ci if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0) 4962306a36Sopenharmony_ci return; 5062306a36Sopenharmony_ci up_read(&inode->i_rwsem); 5162306a36Sopenharmony_ci /* Slow path.... */ 5262306a36Sopenharmony_ci down_write(&inode->i_rwsem); 5362306a36Sopenharmony_ci nfs_block_o_direct(nfsi, inode); 5462306a36Sopenharmony_ci downgrade_write(&inode->i_rwsem); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/** 5862306a36Sopenharmony_ci * nfs_end_io_read - declare that the buffered read operation is done 5962306a36Sopenharmony_ci * @inode: file inode 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Declare that a buffered read operation is done, and release the shared 6262306a36Sopenharmony_ci * lock on inode->i_rwsem. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_civoid 6562306a36Sopenharmony_cinfs_end_io_read(struct inode *inode) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci up_read(&inode->i_rwsem); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/** 7162306a36Sopenharmony_ci * nfs_start_io_write - declare the file is being used for buffered writes 7262306a36Sopenharmony_ci * @inode: file inode 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Declare that a buffered read operation is about to start, and ensure 7562306a36Sopenharmony_ci * that we block all direct I/O. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_civoid 7862306a36Sopenharmony_cinfs_start_io_write(struct inode *inode) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci down_write(&inode->i_rwsem); 8162306a36Sopenharmony_ci nfs_block_o_direct(NFS_I(inode), inode); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * nfs_end_io_write - declare that the buffered write operation is done 8662306a36Sopenharmony_ci * @inode: file inode 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * Declare that a buffered write operation is done, and release the 8962306a36Sopenharmony_ci * lock on inode->i_rwsem. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_civoid 9262306a36Sopenharmony_cinfs_end_io_write(struct inode *inode) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci up_write(&inode->i_rwsem); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Call with exclusively locked inode->i_rwsem */ 9862306a36Sopenharmony_cistatic void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (!test_bit(NFS_INO_ODIRECT, &nfsi->flags)) { 10162306a36Sopenharmony_ci set_bit(NFS_INO_ODIRECT, &nfsi->flags); 10262306a36Sopenharmony_ci nfs_sync_mapping(inode->i_mapping); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/** 10762306a36Sopenharmony_ci * nfs_start_io_direct - declare the file is being used for direct i/o 10862306a36Sopenharmony_ci * @inode: file inode 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * Declare that a direct I/O operation is about to start, and ensure 11162306a36Sopenharmony_ci * that we block all buffered I/O. 11262306a36Sopenharmony_ci * On exit, the function ensures that the NFS_INO_ODIRECT flag is set, 11362306a36Sopenharmony_ci * and holds a shared lock on inode->i_rwsem to ensure that the flag 11462306a36Sopenharmony_ci * cannot be changed. 11562306a36Sopenharmony_ci * In practice, this means that direct I/O operations are allowed to 11662306a36Sopenharmony_ci * execute in parallel, thanks to the shared lock, whereas buffered I/O 11762306a36Sopenharmony_ci * operations need to wait to grab an exclusive lock in order to clear 11862306a36Sopenharmony_ci * NFS_INO_ODIRECT. 11962306a36Sopenharmony_ci * Note that buffered writes and truncates both take a write lock on 12062306a36Sopenharmony_ci * inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_civoid 12362306a36Sopenharmony_cinfs_start_io_direct(struct inode *inode) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct nfs_inode *nfsi = NFS_I(inode); 12662306a36Sopenharmony_ci /* Be an optimist! */ 12762306a36Sopenharmony_ci down_read(&inode->i_rwsem); 12862306a36Sopenharmony_ci if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) != 0) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci up_read(&inode->i_rwsem); 13162306a36Sopenharmony_ci /* Slow path.... */ 13262306a36Sopenharmony_ci down_write(&inode->i_rwsem); 13362306a36Sopenharmony_ci nfs_block_buffered(nfsi, inode); 13462306a36Sopenharmony_ci downgrade_write(&inode->i_rwsem); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * nfs_end_io_direct - declare that the direct i/o operation is done 13962306a36Sopenharmony_ci * @inode: file inode 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Declare that a direct I/O operation is done, and release the shared 14262306a36Sopenharmony_ci * lock on inode->i_rwsem. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_civoid 14562306a36Sopenharmony_cinfs_end_io_direct(struct inode *inode) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci up_read(&inode->i_rwsem); 14862306a36Sopenharmony_ci} 149