Commit d03727b2 authored by Olga Kornievskaia's avatar Olga Kornievskaia Committed by Anna Schumaker

NFSv4 fix CLOSE not waiting for direct IO compeletion

Figuring out the root case for the REMOVE/CLOSE race and
suggesting the solution was done by Neil Brown.

Currently what happens is that direct IO calls hold a reference
on the open context which is decremented as an asynchronous task
in the nfs_direct_complete(). Before reference is decremented,
control is returned to the application which is free to close the
file. When close is being processed, it decrements its reference
on the open_context but since directIO still holds one, it doesn't
sent a close on the wire. It returns control to the application
which is free to do other operations. For instance, it can delete a
file. Direct IO is finally releasing its reference and triggering
an asynchronous close. Which races with the REMOVE. On the server,
REMOVE can be processed before the CLOSE, failing the REMOVE with
EACCES as the file is still opened.
Signed-off-by: default avatarOlga Kornievskaia <kolga@netapp.com>
Suggested-by: default avatarNeil Brown <neilb@suse.com>
CC: stable@vger.kernel.org
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 8b040137
...@@ -267,8 +267,6 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq) ...@@ -267,8 +267,6 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
{ {
struct inode *inode = dreq->inode; struct inode *inode = dreq->inode;
inode_dio_end(inode);
if (dreq->iocb) { if (dreq->iocb) {
long res = (long) dreq->error; long res = (long) dreq->error;
if (dreq->count != 0) { if (dreq->count != 0) {
...@@ -280,7 +278,10 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq) ...@@ -280,7 +278,10 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
complete(&dreq->completion); complete(&dreq->completion);
igrab(inode);
nfs_direct_req_release(dreq); nfs_direct_req_release(dreq);
inode_dio_end(inode);
iput(inode);
} }
static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
...@@ -410,8 +411,10 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, ...@@ -410,8 +411,10 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
* generic layer handle the completion. * generic layer handle the completion.
*/ */
if (requested_bytes == 0) { if (requested_bytes == 0) {
inode_dio_end(inode); igrab(inode);
nfs_direct_req_release(dreq); nfs_direct_req_release(dreq);
inode_dio_end(inode);
iput(inode);
return result < 0 ? result : -EIO; return result < 0 ? result : -EIO;
} }
...@@ -864,8 +867,10 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, ...@@ -864,8 +867,10 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
* generic layer handle the completion. * generic layer handle the completion.
*/ */
if (requested_bytes == 0) { if (requested_bytes == 0) {
inode_dio_end(inode); igrab(inode);
nfs_direct_req_release(dreq); nfs_direct_req_release(dreq);
inode_dio_end(inode);
iput(inode);
return result < 0 ? result : -EIO; return result < 0 ? result : -EIO;
} }
......
...@@ -83,6 +83,7 @@ nfs_file_release(struct inode *inode, struct file *filp) ...@@ -83,6 +83,7 @@ nfs_file_release(struct inode *inode, struct file *filp)
dprintk("NFS: release(%pD2)\n", filp); dprintk("NFS: release(%pD2)\n", filp);
nfs_inc_stats(inode, NFSIOS_VFSRELEASE); nfs_inc_stats(inode, NFSIOS_VFSRELEASE);
inode_dio_wait(inode);
nfs_file_clear_open_context(filp); nfs_file_clear_open_context(filp);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment