summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVivek Goyal <vgoyal@redhat.com>2019-10-15 13:46:26 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2019-10-21 09:57:08 -0400
commita9bfd9dd3417561d06c81de04f6d6c1e0c9b3d44 (patch)
tree1e1991a2446f1a3a253d82983a2c6f430f5d7fd2
parentc17ea009610366146ec409fd6dc277e0f2510b10 (diff)
virtiofs: Retry request submission from worker context
If regular request queue gets full, currently we sleep for a bit and retrying submission in submitter's context. This assumes submitter is not holding any spin lock. But this assumption is not true for background requests. For background requests, we are called with fc->bg_lock held. This can lead to deadlock where one thread is trying submission with fc->bg_lock held while request completion thread has called fuse_request_end() which tries to acquire fc->bg_lock and gets blocked. As request completion thread gets blocked, it does not make further progress and that means queue does not get empty and submitter can't submit more requests. To solve this issue, retry submission with the help of a worker, instead of retrying in submitter's context. We already do this for hiprio/forget requests. Reported-by: Chirantan Ekbote <chirantan@chromium.org> Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/virtio_fs.c61
1 files changed, 52 insertions, 9 deletions
diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c
index 3ea613d5e34f..2de8fc0d6a24 100644
--- a/fs/fuse/virtio_fs.c
+++ b/fs/fuse/virtio_fs.c
@@ -55,6 +55,9 @@ struct virtio_fs_forget {
55 struct list_head list; 55 struct list_head list;
56}; 56};
57 57
58static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
59 struct fuse_req *req, bool in_flight);
60
58static inline struct virtio_fs_vq *vq_to_fsvq(struct virtqueue *vq) 61static inline struct virtio_fs_vq *vq_to_fsvq(struct virtqueue *vq)
59{ 62{
60 struct virtio_fs *fs = vq->vdev->priv; 63 struct virtio_fs *fs = vq->vdev->priv;
@@ -260,6 +263,7 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
260 struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq, 263 struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
261 dispatch_work.work); 264 dispatch_work.work);
262 struct fuse_conn *fc = fsvq->fud->fc; 265 struct fuse_conn *fc = fsvq->fud->fc;
266 int ret;
263 267
264 pr_debug("virtio-fs: worker %s called.\n", __func__); 268 pr_debug("virtio-fs: worker %s called.\n", __func__);
265 while (1) { 269 while (1) {
@@ -268,13 +272,45 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
268 list); 272 list);
269 if (!req) { 273 if (!req) {
270 spin_unlock(&fsvq->lock); 274 spin_unlock(&fsvq->lock);
271 return; 275 break;
272 } 276 }
273 277
274 list_del_init(&req->list); 278 list_del_init(&req->list);
275 spin_unlock(&fsvq->lock); 279 spin_unlock(&fsvq->lock);
276 fuse_request_end(fc, req); 280 fuse_request_end(fc, req);
277 } 281 }
282
283 /* Dispatch pending requests */
284 while (1) {
285 spin_lock(&fsvq->lock);
286 req = list_first_entry_or_null(&fsvq->queued_reqs,
287 struct fuse_req, list);
288 if (!req) {
289 spin_unlock(&fsvq->lock);
290 return;
291 }
292 list_del_init(&req->list);
293 spin_unlock(&fsvq->lock);
294
295 ret = virtio_fs_enqueue_req(fsvq, req, true);
296 if (ret < 0) {
297 if (ret == -ENOMEM || ret == -ENOSPC) {
298 spin_lock(&fsvq->lock);
299 list_add_tail(&req->list, &fsvq->queued_reqs);
300 schedule_delayed_work(&fsvq->dispatch_work,
301 msecs_to_jiffies(1));
302 spin_unlock(&fsvq->lock);
303 return;
304 }
305 req->out.h.error = ret;
306 spin_lock(&fsvq->lock);
307 dec_in_flight_req(fsvq);
308 spin_unlock(&fsvq->lock);
309 pr_err("virtio-fs: virtio_fs_enqueue_req() failed %d\n",
310 ret);
311 fuse_request_end(fc, req);
312 }
313 }
278} 314}
279 315
280static void virtio_fs_hiprio_dispatch_work(struct work_struct *work) 316static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
@@ -837,7 +873,7 @@ static unsigned int sg_init_fuse_args(struct scatterlist *sg,
837 873
838/* Add a request to a virtqueue and kick the device */ 874/* Add a request to a virtqueue and kick the device */
839static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq, 875static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
840 struct fuse_req *req) 876 struct fuse_req *req, bool in_flight)
841{ 877{
842 /* requests need at least 4 elements */ 878 /* requests need at least 4 elements */
843 struct scatterlist *stack_sgs[6]; 879 struct scatterlist *stack_sgs[6];
@@ -917,7 +953,8 @@ static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
917 /* matches barrier in request_wait_answer() */ 953 /* matches barrier in request_wait_answer() */
918 smp_mb__after_atomic(); 954 smp_mb__after_atomic();
919 955
920 inc_in_flight_req(fsvq); 956 if (!in_flight)
957 inc_in_flight_req(fsvq);
921 notify = virtqueue_kick_prepare(vq); 958 notify = virtqueue_kick_prepare(vq);
922 959
923 spin_unlock(&fsvq->lock); 960 spin_unlock(&fsvq->lock);
@@ -963,15 +1000,21 @@ __releases(fiq->lock)
963 req->in.h.nodeid, req->in.h.len, 1000 req->in.h.nodeid, req->in.h.len,
964 fuse_len_args(req->args->out_numargs, req->args->out_args)); 1001 fuse_len_args(req->args->out_numargs, req->args->out_args));
965 1002
966retry:
967 fsvq = &fs->vqs[queue_id]; 1003 fsvq = &fs->vqs[queue_id];
968 ret = virtio_fs_enqueue_req(fsvq, req); 1004 ret = virtio_fs_enqueue_req(fsvq, req, false);
969 if (ret < 0) { 1005 if (ret < 0) {
970 if (ret == -ENOMEM || ret == -ENOSPC) { 1006 if (ret == -ENOMEM || ret == -ENOSPC) {
971 /* Virtqueue full. Retry submission */ 1007 /*
972 /* TODO use completion instead of timeout */ 1008 * Virtqueue full. Retry submission from worker
973 usleep_range(20, 30); 1009 * context as we might be holding fc->bg_lock.
974 goto retry; 1010 */
1011 spin_lock(&fsvq->lock);
1012 list_add_tail(&req->list, &fsvq->queued_reqs);
1013 inc_in_flight_req(fsvq);
1014 schedule_delayed_work(&fsvq->dispatch_work,
1015 msecs_to_jiffies(1));
1016 spin_unlock(&fsvq->lock);
1017 return;
975 } 1018 }
976 req->out.h.error = ret; 1019 req->out.h.error = ret;
977 pr_err("virtio-fs: virtio_fs_enqueue_req() failed %d\n", ret); 1020 pr_err("virtio-fs: virtio_fs_enqueue_req() failed %d\n", ret);