Skip to content

Commit

Permalink
ovl: punt write aio completion to workqueue
Browse files Browse the repository at this point in the history
We want to protect concurrent updates of ovl inode size and mtime
(i.e. ovl_copyattr()) from aio completion context.

Punt write aio completion to a workqueue so that we can protect
ovl_copyattr() with a spinlock.

Export sb_init_dio_done_wq(), so that overlayfs can use its own
dio workqueue to punt aio completions.

Suggested-by: Jens Axboe <[email protected]>
Link: https://lore.kernel.org/r/[email protected]/
Signed-off-by: Amir Goldstein <[email protected]>
  • Loading branch information
amir73il committed Oct 30, 2023
1 parent 5f034d3 commit 389a4a4
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
42 changes: 41 additions & 1 deletion fs/overlayfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
#include <linux/fs.h>
#include "overlayfs.h"

#include "../internal.h" /* for sb_init_dio_done_wq */

struct ovl_aio_req {
struct kiocb iocb;
refcount_t ref;
struct kiocb *orig_iocb;
/* used for aio completion */
struct work_struct work;
long res;
};

static struct kmem_cache *ovl_aio_request_cachep;
Expand Down Expand Up @@ -305,6 +310,37 @@ static void ovl_aio_rw_complete(struct kiocb *iocb, long res)
orig_iocb->ki_complete(orig_iocb, res);
}

static void ovl_aio_complete_work(struct work_struct *work)
{
struct ovl_aio_req *aio_req = container_of(work,
struct ovl_aio_req, work);

ovl_aio_rw_complete(&aio_req->iocb, aio_req->res);
}

static void ovl_aio_queue_completion(struct kiocb *iocb, long res)
{
struct ovl_aio_req *aio_req = container_of(iocb,
struct ovl_aio_req, iocb);
struct kiocb *orig_iocb = aio_req->orig_iocb;

/*
* Punt to a work queue to serialize updates of mtime/size.
*/
aio_req->res = res;
INIT_WORK(&aio_req->work, ovl_aio_complete_work);
queue_work(file_inode(orig_iocb->ki_filp)->i_sb->s_dio_done_wq,
&aio_req->work);
}

static int ovl_init_aio_done_wq(struct super_block *sb)
{
if (sb->s_dio_done_wq)
return 0;

return sb_init_dio_done_wq(sb);
}

static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
Expand Down Expand Up @@ -404,6 +440,10 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
} else {
struct ovl_aio_req *aio_req;

ret = ovl_init_aio_done_wq(inode->i_sb);
if (ret)
goto out;

ret = -ENOMEM;
aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
if (!aio_req)
Expand All @@ -412,7 +452,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
aio_req->orig_iocb = iocb;
kiocb_clone(&aio_req->iocb, iocb, get_file(real.file));
aio_req->iocb.ki_flags = ifl;
aio_req->iocb.ki_complete = ovl_aio_rw_complete;
aio_req->iocb.ki_complete = ovl_aio_queue_completion;
refcount_set(&aio_req->ref, 2);
kiocb_start_write(&aio_req->iocb);
ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
Expand Down
1 change: 1 addition & 0 deletions fs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2157,3 +2157,4 @@ int sb_init_dio_done_wq(struct super_block *sb)
destroy_workqueue(wq);
return 0;
}
EXPORT_SYMBOL_GPL(sb_init_dio_done_wq);

0 comments on commit 389a4a4

Please sign in to comment.