Skip to content

Commit bb737bb

Browse files
rhvgoyalMiklos Szeredi
authored andcommitted
virtiofs: schedule blocking async replies in separate worker
In virtiofs (unlike in regular fuse) processing of async replies is serialized. This can result in a deadlock in rare corner cases when there's a circular dependency between the completion of two or more async replies. Such a deadlock can be reproduced with xfstests:generic/503 if TEST_DIR == SCRATCH_MNT (which is a misconfiguration): - Process A is waiting for page lock in worker thread context and blocked (virtio_fs_requests_done_work()). - Process B is holding page lock and waiting for pending writes to finish (fuse_wait_on_page_writeback()). - Write requests are waiting in virtqueue and can't complete because worker thread is blocked on page lock (process A). Fix this by creating a unique work_struct for each async reply that can block (O_DIRECT read). Fixes: a62a8ef ("virtio-fs: add virtiofs filesystem") Signed-off-by: Vivek Goyal <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent ae83d0b commit bb737bb

File tree

3 files changed

+73
-35
lines changed

3 files changed

+73
-35
lines changed

fs/fuse/file.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ static ssize_t fuse_async_req_send(struct fuse_conn *fc,
712712
spin_unlock(&io->lock);
713713

714714
ia->ap.args.end = fuse_aio_complete_req;
715+
ia->ap.args.may_block = io->should_dirty;
715716
err = fuse_simple_background(fc, &ia->ap.args, GFP_KERNEL);
716717
if (err)
717718
fuse_aio_complete_req(fc, &ia->ap.args, err);

fs/fuse/fuse_i.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ struct fuse_args {
249249
bool out_argvar:1;
250250
bool page_zeroing:1;
251251
bool page_replace:1;
252+
bool may_block:1;
252253
struct fuse_in_arg in_args[3];
253254
struct fuse_arg out_args[2];
254255
void (*end)(struct fuse_conn *fc, struct fuse_args *args, int error);

fs/fuse/virtio_fs.c

Lines changed: 71 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ struct virtio_fs_forget {
6060
struct virtio_fs_forget_req req;
6161
};
6262

63+
struct virtio_fs_req_work {
64+
struct fuse_req *req;
65+
struct virtio_fs_vq *fsvq;
66+
struct work_struct done_work;
67+
};
68+
6369
static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
6470
struct fuse_req *req, bool in_flight);
6571

@@ -485,19 +491,67 @@ static void copy_args_from_argbuf(struct fuse_args *args, struct fuse_req *req)
485491
}
486492

487493
/* Work function for request completion */
494+
static void virtio_fs_request_complete(struct fuse_req *req,
495+
struct virtio_fs_vq *fsvq)
496+
{
497+
struct fuse_pqueue *fpq = &fsvq->fud->pq;
498+
struct fuse_conn *fc = fsvq->fud->fc;
499+
struct fuse_args *args;
500+
struct fuse_args_pages *ap;
501+
unsigned int len, i, thislen;
502+
struct page *page;
503+
504+
/*
505+
* TODO verify that server properly follows FUSE protocol
506+
* (oh.uniq, oh.len)
507+
*/
508+
args = req->args;
509+
copy_args_from_argbuf(args, req);
510+
511+
if (args->out_pages && args->page_zeroing) {
512+
len = args->out_args[args->out_numargs - 1].size;
513+
ap = container_of(args, typeof(*ap), args);
514+
for (i = 0; i < ap->num_pages; i++) {
515+
thislen = ap->descs[i].length;
516+
if (len < thislen) {
517+
WARN_ON(ap->descs[i].offset);
518+
page = ap->pages[i];
519+
zero_user_segment(page, len, thislen);
520+
len = 0;
521+
} else {
522+
len -= thislen;
523+
}
524+
}
525+
}
526+
527+
spin_lock(&fpq->lock);
528+
clear_bit(FR_SENT, &req->flags);
529+
spin_unlock(&fpq->lock);
530+
531+
fuse_request_end(fc, req);
532+
spin_lock(&fsvq->lock);
533+
dec_in_flight_req(fsvq);
534+
spin_unlock(&fsvq->lock);
535+
}
536+
537+
static void virtio_fs_complete_req_work(struct work_struct *work)
538+
{
539+
struct virtio_fs_req_work *w =
540+
container_of(work, typeof(*w), done_work);
541+
542+
virtio_fs_request_complete(w->req, w->fsvq);
543+
kfree(w);
544+
}
545+
488546
static void virtio_fs_requests_done_work(struct work_struct *work)
489547
{
490548
struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
491549
done_work);
492550
struct fuse_pqueue *fpq = &fsvq->fud->pq;
493-
struct fuse_conn *fc = fsvq->fud->fc;
494551
struct virtqueue *vq = fsvq->vq;
495552
struct fuse_req *req;
496-
struct fuse_args_pages *ap;
497553
struct fuse_req *next;
498-
struct fuse_args *args;
499-
unsigned int len, i, thislen;
500-
struct page *page;
554+
unsigned int len;
501555
LIST_HEAD(reqs);
502556

503557
/* Collect completed requests off the virtqueue */
@@ -515,38 +569,20 @@ static void virtio_fs_requests_done_work(struct work_struct *work)
515569

516570
/* End requests */
517571
list_for_each_entry_safe(req, next, &reqs, list) {
518-
/*
519-
* TODO verify that server properly follows FUSE protocol
520-
* (oh.uniq, oh.len)
521-
*/
522-
args = req->args;
523-
copy_args_from_argbuf(args, req);
524-
525-
if (args->out_pages && args->page_zeroing) {
526-
len = args->out_args[args->out_numargs - 1].size;
527-
ap = container_of(args, typeof(*ap), args);
528-
for (i = 0; i < ap->num_pages; i++) {
529-
thislen = ap->descs[i].length;
530-
if (len < thislen) {
531-
WARN_ON(ap->descs[i].offset);
532-
page = ap->pages[i];
533-
zero_user_segment(page, len, thislen);
534-
len = 0;
535-
} else {
536-
len -= thislen;
537-
}
538-
}
539-
}
540-
541-
spin_lock(&fpq->lock);
542-
clear_bit(FR_SENT, &req->flags);
543572
list_del_init(&req->list);
544-
spin_unlock(&fpq->lock);
545573

546-
fuse_request_end(fc, req);
547-
spin_lock(&fsvq->lock);
548-
dec_in_flight_req(fsvq);
549-
spin_unlock(&fsvq->lock);
574+
/* blocking async request completes in a worker context */
575+
if (req->args->may_block) {
576+
struct virtio_fs_req_work *w;
577+
578+
w = kzalloc(sizeof(*w), GFP_NOFS | __GFP_NOFAIL);
579+
INIT_WORK(&w->done_work, virtio_fs_complete_req_work);
580+
w->fsvq = fsvq;
581+
w->req = req;
582+
schedule_work(&w->done_work);
583+
} else {
584+
virtio_fs_request_complete(req, fsvq);
585+
}
550586
}
551587
}
552588

0 commit comments

Comments
 (0)