diff options
author | Vivek Goyal <vgoyal@redhat.com> | 2019-10-15 13:46:22 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2019-10-21 09:57:07 -0400 |
commit | 51fecdd2555b3e0e05a78d30093c638d164a32f9 (patch) | |
tree | 2760e95ff51c29ca24d74a8ba2c4a32e396621ce | |
parent | 6c26f71759a6efc04b888dd2c1cc4f1cac38cdf0 (diff) |
virtiofs: Do not end request in submission context
Submission context can hold some locks which end request code tries to hold
again and deadlock can occur. For example, fc->bg_lock. If a background
request is being submitted, it might hold fc->bg_lock and if we could not
submit request (because device went away) and tried to end request, then
deadlock happens. During testing, I also got a warning from deadlock
detection code.
So put requests on a list and end requests from a worker thread.
I got following warning from deadlock detector.
[ 603.137138] WARNING: possible recursive locking detected
[ 603.137142] --------------------------------------------
[ 603.137144] blogbench/2036 is trying to acquire lock:
[ 603.137149] 00000000f0f51107 (&(&fc->bg_lock)->rlock){+.+.}, at: fuse_request_end+0xdf/0x1c0 [fuse]
[ 603.140701]
[ 603.140701] but task is already holding lock:
[ 603.140703] 00000000f0f51107 (&(&fc->bg_lock)->rlock){+.+.}, at: fuse_simple_background+0x92/0x1d0 [fuse]
[ 603.140713]
[ 603.140713] other info that might help us debug this:
[ 603.140714] Possible unsafe locking scenario:
[ 603.140714]
[ 603.140715] CPU0
[ 603.140716] ----
[ 603.140716] lock(&(&fc->bg_lock)->rlock);
[ 603.140718] lock(&(&fc->bg_lock)->rlock);
[ 603.140719]
[ 603.140719] *** DEADLOCK ***
Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r-- | fs/fuse/virtio_fs.c | 37 |
1 files changed, 33 insertions, 4 deletions
diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c index e22a0c003c3d..7ea58606cc1d 100644 --- a/fs/fuse/virtio_fs.c +++ b/fs/fuse/virtio_fs.c | |||
@@ -30,6 +30,7 @@ struct virtio_fs_vq { | |||
30 | struct virtqueue *vq; /* protected by ->lock */ | 30 | struct virtqueue *vq; /* protected by ->lock */ |
31 | struct work_struct done_work; | 31 | struct work_struct done_work; |
32 | struct list_head queued_reqs; | 32 | struct list_head queued_reqs; |
33 | struct list_head end_reqs; /* End these requests */ | ||
33 | struct delayed_work dispatch_work; | 34 | struct delayed_work dispatch_work; |
34 | struct fuse_dev *fud; | 35 | struct fuse_dev *fud; |
35 | bool connected; | 36 | bool connected; |
@@ -259,8 +260,27 @@ static void virtio_fs_hiprio_done_work(struct work_struct *work) | |||
259 | spin_unlock(&fsvq->lock); | 260 | spin_unlock(&fsvq->lock); |
260 | } | 261 | } |
261 | 262 | ||
262 | static void virtio_fs_dummy_dispatch_work(struct work_struct *work) | 263 | static void virtio_fs_request_dispatch_work(struct work_struct *work) |
263 | { | 264 | { |
265 | struct fuse_req *req; | ||
266 | struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq, | ||
267 | dispatch_work.work); | ||
268 | struct fuse_conn *fc = fsvq->fud->fc; | ||
269 | |||
270 | pr_debug("virtio-fs: worker %s called.\n", __func__); | ||
271 | while (1) { | ||
272 | spin_lock(&fsvq->lock); | ||
273 | req = list_first_entry_or_null(&fsvq->end_reqs, struct fuse_req, | ||
274 | list); | ||
275 | if (!req) { | ||
276 | spin_unlock(&fsvq->lock); | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | list_del_init(&req->list); | ||
281 | spin_unlock(&fsvq->lock); | ||
282 | fuse_request_end(fc, req); | ||
283 | } | ||
264 | } | 284 | } |
265 | 285 | ||
266 | static void virtio_fs_hiprio_dispatch_work(struct work_struct *work) | 286 | static void virtio_fs_hiprio_dispatch_work(struct work_struct *work) |
@@ -502,6 +522,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev, | |||
502 | names[VQ_HIPRIO] = fs->vqs[VQ_HIPRIO].name; | 522 | names[VQ_HIPRIO] = fs->vqs[VQ_HIPRIO].name; |
503 | INIT_WORK(&fs->vqs[VQ_HIPRIO].done_work, virtio_fs_hiprio_done_work); | 523 | INIT_WORK(&fs->vqs[VQ_HIPRIO].done_work, virtio_fs_hiprio_done_work); |
504 | INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].queued_reqs); | 524 | INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].queued_reqs); |
525 | INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].end_reqs); | ||
505 | INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work, | 526 | INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work, |
506 | virtio_fs_hiprio_dispatch_work); | 527 | virtio_fs_hiprio_dispatch_work); |
507 | spin_lock_init(&fs->vqs[VQ_HIPRIO].lock); | 528 | spin_lock_init(&fs->vqs[VQ_HIPRIO].lock); |
@@ -511,8 +532,9 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev, | |||
511 | spin_lock_init(&fs->vqs[i].lock); | 532 | spin_lock_init(&fs->vqs[i].lock); |
512 | INIT_WORK(&fs->vqs[i].done_work, virtio_fs_requests_done_work); | 533 | INIT_WORK(&fs->vqs[i].done_work, virtio_fs_requests_done_work); |
513 | INIT_DELAYED_WORK(&fs->vqs[i].dispatch_work, | 534 | INIT_DELAYED_WORK(&fs->vqs[i].dispatch_work, |
514 | virtio_fs_dummy_dispatch_work); | 535 | virtio_fs_request_dispatch_work); |
515 | INIT_LIST_HEAD(&fs->vqs[i].queued_reqs); | 536 | INIT_LIST_HEAD(&fs->vqs[i].queued_reqs); |
537 | INIT_LIST_HEAD(&fs->vqs[i].end_reqs); | ||
516 | snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name), | 538 | snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name), |
517 | "requests.%u", i - VQ_REQUEST); | 539 | "requests.%u", i - VQ_REQUEST); |
518 | callbacks[i] = virtio_fs_vq_done; | 540 | callbacks[i] = virtio_fs_vq_done; |
@@ -918,6 +940,7 @@ __releases(fiq->lock) | |||
918 | struct fuse_conn *fc; | 940 | struct fuse_conn *fc; |
919 | struct fuse_req *req; | 941 | struct fuse_req *req; |
920 | struct fuse_pqueue *fpq; | 942 | struct fuse_pqueue *fpq; |
943 | struct virtio_fs_vq *fsvq; | ||
921 | int ret; | 944 | int ret; |
922 | 945 | ||
923 | WARN_ON(list_empty(&fiq->pending)); | 946 | WARN_ON(list_empty(&fiq->pending)); |
@@ -951,7 +974,8 @@ __releases(fiq->lock) | |||
951 | smp_mb__after_atomic(); | 974 | smp_mb__after_atomic(); |
952 | 975 | ||
953 | retry: | 976 | retry: |
954 | ret = virtio_fs_enqueue_req(&fs->vqs[queue_id], req); | 977 | fsvq = &fs->vqs[queue_id]; |
978 | ret = virtio_fs_enqueue_req(fsvq, req); | ||
955 | if (ret < 0) { | 979 | if (ret < 0) { |
956 | if (ret == -ENOMEM || ret == -ENOSPC) { | 980 | if (ret == -ENOMEM || ret == -ENOSPC) { |
957 | /* Virtqueue full. Retry submission */ | 981 | /* Virtqueue full. Retry submission */ |
@@ -965,7 +989,12 @@ retry: | |||
965 | clear_bit(FR_SENT, &req->flags); | 989 | clear_bit(FR_SENT, &req->flags); |
966 | list_del_init(&req->list); | 990 | list_del_init(&req->list); |
967 | spin_unlock(&fpq->lock); | 991 | spin_unlock(&fpq->lock); |
968 | fuse_request_end(fc, req); | 992 | |
993 | /* Can't end request in submission context. Use a worker */ | ||
994 | spin_lock(&fsvq->lock); | ||
995 | list_add_tail(&req->list, &fsvq->end_reqs); | ||
996 | schedule_delayed_work(&fsvq->dispatch_work, 0); | ||
997 | spin_unlock(&fsvq->lock); | ||
969 | return; | 998 | return; |
970 | } | 999 | } |
971 | } | 1000 | } |