[PATCH v2] nfs: fix dio deadlock when O_DIRECT flag is flipped

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Running xfstest generic/036, we hit VM_BUG_ON() in nfs_direct_IO().
036 toggles O_DIRECT flag while IO is going on. We cannot simply remove
the VM_BUG_ON() there because we'll have a deadlock in the code path.
inode->i_mutex is taken when calling into ->direct_IO.
And nfs_file_direct_write() would grab inode->i_mutex again.

nfs_file_write() and generic_file_write_iter() checks for O_DIRECT twice,
and it creates a race window if user space is playing with O_DIRECT flag.
Fix it by calling generic_perform_write() instead, so that nfs_direct_IO()
is only invoked in swap on nfs case.

Suggested-by: Christoph Hellwig <hch@xxxxxxxxxxxxx>
Signed-off-by: Peng Tao <tao.peng@xxxxxxxxxxxxxxx>
---
 fs/nfs/file.c | 41 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 40 insertions(+), 1 deletion(-)

diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 2ab6f00..e98604a 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -662,6 +662,45 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode)
 	return 0;
 }
 
+static ssize_t nfs_file_buffer_write(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	ssize_t result = 0;
+	size_t count = iov_iter_count(from);
+	loff_t pos = iocb->ki_pos;
+	int ret;
+
+	mutex_lock(&inode->i_mutex);
+	/* We can write back this queue in page reclaim */
+	current->backing_dev_info = mapping->backing_dev_info;
+	ret = generic_write_checks(file, &pos, &count, 0);
+	if (ret)
+		goto out;
+
+	if (!count)
+		goto out;
+
+	iov_iter_truncate(from, count);
+	ret = file_remove_suid(file);
+	if (ret)
+		goto out;
+
+	ret = file_update_time(file);
+	if (ret)
+		goto out;
+
+	result = generic_perform_write(file, from, pos);
+	if (likely(result >= 0))
+		iocb->ki_pos = pos + result;
+
+out:
+	current->backing_dev_info = NULL;
+	mutex_unlock(&inode->i_mutex);
+	return result ? result : ret;
+}
+
 ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
 {
 	struct file *file = iocb->ki_filp;
@@ -697,7 +736,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
 	if (!count)
 		goto out;
 
-	result = generic_file_write_iter(iocb, from);
+	result = nfs_file_buffer_write(iocb, from);
 	if (result > 0)
 		written = result;
 
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux