xref: /linux/fs/nfs/io.c (revision 7aac71907bdea16e2754a782b9d9155449a9d49d)
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  */
33 int
nfs_start_io_read(struct inode * inode)34 nfs_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  */
64 void
nfs_end_io_read(struct inode * inode)65 nfs_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  */
77 int
nfs_start_io_write(struct inode * inode)78 nfs_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  */
95 void
nfs_end_io_write(struct inode * inode)96 nfs_end_io_write(struct inode *inode)
97 {
98 	up_write(&inode->i_rwsem);
99 }
100 
101 /* Call with exclusively locked inode->i_rwsem */
nfs_block_buffered(struct nfs_inode * nfsi,struct inode * inode)102 static 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  */
126 int
nfs_start_io_direct(struct inode * inode)127 nfs_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  */
157 void
nfs_end_io_direct(struct inode * inode)158 nfs_end_io_direct(struct inode *inode)
159 {
160 	up_read(&inode->i_rwsem);
161 }
162