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>
This commit is contained in:
parent
196639ebbe
commit
137da553db
|
@ -91,22 +91,28 @@ static int
|
|||
pnfs_generic_transfer_commit_list(struct list_head *src, struct list_head *dst,
|
||||
struct nfs_commit_info *cinfo, int max)
|
||||
{
|
||||
struct nfs_page *req;
|
||||
struct nfs_page *req, *tmp;
|
||||
int ret = 0;
|
||||
|
||||
while(!list_empty(src)) {
|
||||
req = list_first_entry(src, struct nfs_page, wb_list);
|
||||
|
||||
restart:
|
||||
list_for_each_entry_safe(req, tmp, src, wb_list) {
|
||||
kref_get(&req->wb_kref);
|
||||
if (!nfs_lock_request(req)) {
|
||||
int status;
|
||||
|
||||
/* Prevent deadlock with nfs_lock_and_join_requests */
|
||||
if (!list_empty(dst)) {
|
||||
nfs_release_request(req);
|
||||
continue;
|
||||
}
|
||||
/* Ensure we make progress to prevent livelock */
|
||||
mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
|
||||
status = nfs_wait_on_request(req);
|
||||
nfs_release_request(req);
|
||||
mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
|
||||
if (status < 0)
|
||||
break;
|
||||
continue;
|
||||
goto restart;
|
||||
}
|
||||
nfs_request_remove_commit_list(req, cinfo);
|
||||
clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
|
||||
|
|
|
@ -1028,21 +1028,28 @@ int
|
|||
nfs_scan_commit_list(struct list_head *src, struct list_head *dst,
|
||||
struct nfs_commit_info *cinfo, int max)
|
||||
{
|
||||
struct nfs_page *req;
|
||||
struct nfs_page *req, *tmp;
|
||||
int ret = 0;
|
||||
|
||||
while(!list_empty(src)) {
|
||||
req = list_first_entry(src, struct nfs_page, wb_list);
|
||||
restart:
|
||||
list_for_each_entry_safe(req, tmp, src, wb_list) {
|
||||
kref_get(&req->wb_kref);
|
||||
if (!nfs_lock_request(req)) {
|
||||
int status;
|
||||
|
||||
/* Prevent deadlock with nfs_lock_and_join_requests */
|
||||
if (!list_empty(dst)) {
|
||||
nfs_release_request(req);
|
||||
continue;
|
||||
}
|
||||
/* Ensure we make progress to prevent livelock */
|
||||
mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
|
||||
status = nfs_wait_on_request(req);
|
||||
nfs_release_request(req);
|
||||
mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
|
||||
if (status < 0)
|
||||
break;
|
||||
continue;
|
||||
goto restart;
|
||||
}
|
||||
nfs_request_remove_commit_list(req, cinfo);
|
||||
nfs_list_add_request(req, dst);
|
||||
|
|
Loading…
Reference in New Issue