18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Trond Myklebust
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * I/O and data path helper functionality.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/bitops.h>
118c2ecf20Sopenharmony_ci#include <linux/rwsem.h>
128c2ecf20Sopenharmony_ci#include <linux/fs.h>
138c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "internal.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Call with exclusively locked inode->i_rwsem */
188c2ecf20Sopenharmony_cistatic void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	if (test_bit(NFS_INO_ODIRECT, &nfsi->flags)) {
218c2ecf20Sopenharmony_ci		clear_bit(NFS_INO_ODIRECT, &nfsi->flags);
228c2ecf20Sopenharmony_ci		inode_dio_wait(inode);
238c2ecf20Sopenharmony_ci	}
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * nfs_start_io_read - declare the file is being used for buffered reads
288c2ecf20Sopenharmony_ci * @inode: file inode
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Declare that a buffered read operation is about to start, and ensure
318c2ecf20Sopenharmony_ci * that we block all direct I/O.
328c2ecf20Sopenharmony_ci * On exit, the function ensures that the NFS_INO_ODIRECT flag is unset,
338c2ecf20Sopenharmony_ci * and holds a shared lock on inode->i_rwsem to ensure that the flag
348c2ecf20Sopenharmony_ci * cannot be changed.
358c2ecf20Sopenharmony_ci * In practice, this means that buffered read operations are allowed to
368c2ecf20Sopenharmony_ci * execute in parallel, thanks to the shared lock, whereas direct I/O
378c2ecf20Sopenharmony_ci * operations need to wait to grab an exclusive lock in order to set
388c2ecf20Sopenharmony_ci * NFS_INO_ODIRECT.
398c2ecf20Sopenharmony_ci * Note that buffered writes and truncates both take a write lock on
408c2ecf20Sopenharmony_ci * inode->i_rwsem, meaning that those are serialised w.r.t. the reads.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_civoid
438c2ecf20Sopenharmony_cinfs_start_io_read(struct inode *inode)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
468c2ecf20Sopenharmony_ci	/* Be an optimist! */
478c2ecf20Sopenharmony_ci	down_read(&inode->i_rwsem);
488c2ecf20Sopenharmony_ci	if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0)
498c2ecf20Sopenharmony_ci		return;
508c2ecf20Sopenharmony_ci	up_read(&inode->i_rwsem);
518c2ecf20Sopenharmony_ci	/* Slow path.... */
528c2ecf20Sopenharmony_ci	down_write(&inode->i_rwsem);
538c2ecf20Sopenharmony_ci	nfs_block_o_direct(nfsi, inode);
548c2ecf20Sopenharmony_ci	downgrade_write(&inode->i_rwsem);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/**
588c2ecf20Sopenharmony_ci * nfs_end_io_read - declare that the buffered read operation is done
598c2ecf20Sopenharmony_ci * @inode: file inode
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * Declare that a buffered read operation is done, and release the shared
628c2ecf20Sopenharmony_ci * lock on inode->i_rwsem.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_civoid
658c2ecf20Sopenharmony_cinfs_end_io_read(struct inode *inode)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	up_read(&inode->i_rwsem);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/**
718c2ecf20Sopenharmony_ci * nfs_start_io_write - declare the file is being used for buffered writes
728c2ecf20Sopenharmony_ci * @inode: file inode
738c2ecf20Sopenharmony_ci *
748c2ecf20Sopenharmony_ci * Declare that a buffered read operation is about to start, and ensure
758c2ecf20Sopenharmony_ci * that we block all direct I/O.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_civoid
788c2ecf20Sopenharmony_cinfs_start_io_write(struct inode *inode)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	down_write(&inode->i_rwsem);
818c2ecf20Sopenharmony_ci	nfs_block_o_direct(NFS_I(inode), inode);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/**
858c2ecf20Sopenharmony_ci * nfs_end_io_write - declare that the buffered write operation is done
868c2ecf20Sopenharmony_ci * @inode: file inode
878c2ecf20Sopenharmony_ci *
888c2ecf20Sopenharmony_ci * Declare that a buffered write operation is done, and release the
898c2ecf20Sopenharmony_ci * lock on inode->i_rwsem.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_civoid
928c2ecf20Sopenharmony_cinfs_end_io_write(struct inode *inode)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	up_write(&inode->i_rwsem);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* Call with exclusively locked inode->i_rwsem */
988c2ecf20Sopenharmony_cistatic void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	if (!test_bit(NFS_INO_ODIRECT, &nfsi->flags)) {
1018c2ecf20Sopenharmony_ci		set_bit(NFS_INO_ODIRECT, &nfsi->flags);
1028c2ecf20Sopenharmony_ci		nfs_sync_mapping(inode->i_mapping);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/**
1078c2ecf20Sopenharmony_ci * nfs_end_io_direct - declare the file is being used for direct i/o
1088c2ecf20Sopenharmony_ci * @inode: file inode
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * Declare that a direct I/O operation is about to start, and ensure
1118c2ecf20Sopenharmony_ci * that we block all buffered I/O.
1128c2ecf20Sopenharmony_ci * On exit, the function ensures that the NFS_INO_ODIRECT flag is set,
1138c2ecf20Sopenharmony_ci * and holds a shared lock on inode->i_rwsem to ensure that the flag
1148c2ecf20Sopenharmony_ci * cannot be changed.
1158c2ecf20Sopenharmony_ci * In practice, this means that direct I/O operations are allowed to
1168c2ecf20Sopenharmony_ci * execute in parallel, thanks to the shared lock, whereas buffered I/O
1178c2ecf20Sopenharmony_ci * operations need to wait to grab an exclusive lock in order to clear
1188c2ecf20Sopenharmony_ci * NFS_INO_ODIRECT.
1198c2ecf20Sopenharmony_ci * Note that buffered writes and truncates both take a write lock on
1208c2ecf20Sopenharmony_ci * inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT.
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_civoid
1238c2ecf20Sopenharmony_cinfs_start_io_direct(struct inode *inode)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct nfs_inode *nfsi = NFS_I(inode);
1268c2ecf20Sopenharmony_ci	/* Be an optimist! */
1278c2ecf20Sopenharmony_ci	down_read(&inode->i_rwsem);
1288c2ecf20Sopenharmony_ci	if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) != 0)
1298c2ecf20Sopenharmony_ci		return;
1308c2ecf20Sopenharmony_ci	up_read(&inode->i_rwsem);
1318c2ecf20Sopenharmony_ci	/* Slow path.... */
1328c2ecf20Sopenharmony_ci	down_write(&inode->i_rwsem);
1338c2ecf20Sopenharmony_ci	nfs_block_buffered(nfsi, inode);
1348c2ecf20Sopenharmony_ci	downgrade_write(&inode->i_rwsem);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/**
1388c2ecf20Sopenharmony_ci * nfs_end_io_direct - declare that the direct i/o operation is done
1398c2ecf20Sopenharmony_ci * @inode: file inode
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * Declare that a direct I/O operation is done, and release the shared
1428c2ecf20Sopenharmony_ci * lock on inode->i_rwsem.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_civoid
1458c2ecf20Sopenharmony_cinfs_end_io_direct(struct inode *inode)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	up_read(&inode->i_rwsem);
1488c2ecf20Sopenharmony_ci}
149