aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.c
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2017-09-09 12:28:01 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2017-09-09 12:28:01 -0400
commit137da553dba62dfc64fb8f4ccb5be769acbf615e (patch)
tree18e8bb3b882b37c585e0b498784fe627da90481a /fs/nfs/write.c
parent196639ebbe63a037fe9a80669140bd292d8bcd80 (diff)
NFS: nfs_lock_and_join_requests and nfs_scan_commit_list can deadlock
Since the commit list is not ordered, it is possible for nfs_scan_commit_list to hold a request that nfs_lock_and_join_requests() is waiting for, while at the same time trying to grab a request that nfs_lock_and_join_requests already holds. Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r--fs/nfs/write.c15
1 files changed, 11 insertions, 4 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index ae26775b5448..c3f627b08ec6 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1028,21 +1028,28 @@ int
1028nfs_scan_commit_list(struct list_head *src, struct list_head *dst, 1028nfs_scan_commit_list(struct list_head *src, struct list_head *dst,
1029 struct nfs_commit_info *cinfo, int max) 1029 struct nfs_commit_info *cinfo, int max)
1030{ 1030{
1031 struct nfs_page *req; 1031 struct nfs_page *req, *tmp;
1032 int ret = 0; 1032 int ret = 0;
1033 1033
1034 while(!list_empty(src)) { 1034restart:
1035 req = list_first_entry(src, struct nfs_page, wb_list); 1035 list_for_each_entry_safe(req, tmp, src, wb_list) {
1036 kref_get(&req->wb_kref); 1036 kref_get(&req->wb_kref);
1037 if (!nfs_lock_request(req)) { 1037 if (!nfs_lock_request(req)) {
1038 int status; 1038 int status;
1039
1040 /* Prevent deadlock with nfs_lock_and_join_requests */
1041 if (!list_empty(dst)) {
1042 nfs_release_request(req);
1043 continue;
1044 }
1045 /* Ensure we make progress to prevent livelock */
1039 mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); 1046 mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
1040 status = nfs_wait_on_request(req); 1047 status = nfs_wait_on_request(req);
1041 nfs_release_request(req); 1048 nfs_release_request(req);
1042 mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); 1049 mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
1043 if (status < 0) 1050 if (status < 0)
1044 break; 1051 break;
1045 continue; 1052 goto restart;
1046 } 1053 }
1047 nfs_request_remove_commit_list(req, cinfo); 1054 nfs_request_remove_commit_list(req, cinfo);
1048 nfs_list_add_request(req, dst); 1055 nfs_list_add_request(req, dst);