at master 4.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2016 Trond Myklebust 4 * 5 * I/O and data path helper functionality. 6 */ 7 8#include <linux/types.h> 9#include <linux/kernel.h> 10#include <linux/bitops.h> 11#include <linux/rwsem.h> 12#include <linux/fs.h> 13#include <linux/nfs_fs.h> 14 15#include "internal.h" 16 17/** 18 * nfs_start_io_read - declare the file is being used for buffered reads 19 * @inode: file inode 20 * 21 * Declare that a buffered read operation is about to start, and ensure 22 * that we block all direct I/O. 23 * On exit, the function ensures that the NFS_INO_ODIRECT flag is unset, 24 * and holds a shared lock on inode->i_rwsem to ensure that the flag 25 * cannot be changed. 26 * In practice, this means that buffered read operations are allowed to 27 * execute in parallel, thanks to the shared lock, whereas direct I/O 28 * operations need to wait to grab an exclusive lock in order to set 29 * NFS_INO_ODIRECT. 30 * Note that buffered writes and truncates both take a write lock on 31 * inode->i_rwsem, meaning that those are serialised w.r.t. the reads. 32 */ 33int 34nfs_start_io_read(struct inode *inode) 35{ 36 struct nfs_inode *nfsi = NFS_I(inode); 37 int err; 38 39 /* Be an optimist! */ 40 err = down_read_killable(&inode->i_rwsem); 41 if (err) 42 return err; 43 if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0) 44 return 0; 45 up_read(&inode->i_rwsem); 46 47 /* Slow path.... */ 48 err = down_write_killable(&inode->i_rwsem); 49 if (err) 50 return err; 51 nfs_file_block_o_direct(nfsi); 52 downgrade_write(&inode->i_rwsem); 53 54 return 0; 55} 56 57/** 58 * nfs_end_io_read - declare that the buffered read operation is done 59 * @inode: file inode 60 * 61 * Declare that a buffered read operation is done, and release the shared 62 * lock on inode->i_rwsem. 63 */ 64void 65nfs_end_io_read(struct inode *inode) 66{ 67 up_read(&inode->i_rwsem); 68} 69 70/** 71 * nfs_start_io_write - declare the file is being used for buffered writes 72 * @inode: file inode 73 * 74 * Declare that a buffered read operation is about to start, and ensure 75 * that we block all direct I/O. 76 */ 77int 78nfs_start_io_write(struct inode *inode) 79{ 80 int err; 81 82 err = down_write_killable(&inode->i_rwsem); 83 if (!err) 84 nfs_file_block_o_direct(NFS_I(inode)); 85 return err; 86} 87 88/** 89 * nfs_end_io_write - declare that the buffered write operation is done 90 * @inode: file inode 91 * 92 * Declare that a buffered write operation is done, and release the 93 * lock on inode->i_rwsem. 94 */ 95void 96nfs_end_io_write(struct inode *inode) 97{ 98 up_write(&inode->i_rwsem); 99} 100 101/* Call with exclusively locked inode->i_rwsem */ 102static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode) 103{ 104 if (!test_bit(NFS_INO_ODIRECT, &nfsi->flags)) { 105 set_bit(NFS_INO_ODIRECT, &nfsi->flags); 106 nfs_sync_mapping(inode->i_mapping); 107 } 108} 109 110/** 111 * nfs_start_io_direct - declare the file is being used for direct i/o 112 * @inode: file inode 113 * 114 * Declare that a direct I/O operation is about to start, and ensure 115 * that we block all buffered I/O. 116 * On exit, the function ensures that the NFS_INO_ODIRECT flag is set, 117 * and holds a shared lock on inode->i_rwsem to ensure that the flag 118 * cannot be changed. 119 * In practice, this means that direct I/O operations are allowed to 120 * execute in parallel, thanks to the shared lock, whereas buffered I/O 121 * operations need to wait to grab an exclusive lock in order to clear 122 * NFS_INO_ODIRECT. 123 * Note that buffered writes and truncates both take a write lock on 124 * inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT. 125 */ 126int 127nfs_start_io_direct(struct inode *inode) 128{ 129 struct nfs_inode *nfsi = NFS_I(inode); 130 int err; 131 132 /* Be an optimist! */ 133 err = down_read_killable(&inode->i_rwsem); 134 if (err) 135 return err; 136 if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) != 0) 137 return 0; 138 up_read(&inode->i_rwsem); 139 140 /* Slow path.... */ 141 err = down_write_killable(&inode->i_rwsem); 142 if (err) 143 return err; 144 nfs_block_buffered(nfsi, inode); 145 downgrade_write(&inode->i_rwsem); 146 147 return 0; 148} 149 150/** 151 * nfs_end_io_direct - declare that the direct i/o operation is done 152 * @inode: file inode 153 * 154 * Declare that a direct I/O operation is done, and release the shared 155 * lock on inode->i_rwsem. 156 */ 157void 158nfs_end_io_direct(struct inode *inode) 159{ 160 up_read(&inode->i_rwsem); 161}