Skip to content

Commit 2406a30

Browse files
Jiufei XueMiklos Szeredi
authored andcommitted
ovl: implement async IO routines
A performance regression was observed since linux v4.19 with aio test using fio with iodepth 128 on overlayfs. The queue depth of the device was always 1 which is unexpected. After investigation, it was found that commit 16914e6 ("ovl: add ovl_read_iter()") and commit 2a92e07 ("ovl: add ovl_write_iter()") resulted in vfs_iter_{read,write} being called on underlying filesystem, which always results in syncronous IO. Implement async IO for stacked reading and writing. This resolves the performance regresion. This is implemented by allocating a new kiocb for submitting the AIO request on the underlying filesystem. When the request is completed, the new kiocb is freed and the completion callback is called on the original iocb. Signed-off-by: Jiufei Xue <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 5dcdc43 commit 2406a30

File tree

3 files changed

+110
-15
lines changed

3 files changed

+110
-15
lines changed

fs/overlayfs/file.c

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
#include <linux/uaccess.h>
1212
#include "overlayfs.h"
1313

14+
struct ovl_aio_req {
15+
struct kiocb iocb;
16+
struct kiocb *orig_iocb;
17+
struct fd fd;
18+
};
19+
20+
static struct kmem_cache *ovl_aio_request_cachep;
21+
1422
static char ovl_whatisit(struct inode *inode, struct inode *realinode)
1523
{
1624
if (realinode != ovl_inode_upper(inode))
@@ -225,6 +233,33 @@ static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
225233
return flags;
226234
}
227235

236+
static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
237+
{
238+
struct kiocb *iocb = &aio_req->iocb;
239+
struct kiocb *orig_iocb = aio_req->orig_iocb;
240+
241+
if (iocb->ki_flags & IOCB_WRITE) {
242+
struct inode *inode = file_inode(orig_iocb->ki_filp);
243+
244+
file_end_write(iocb->ki_filp);
245+
ovl_copyattr(ovl_inode_real(inode), inode);
246+
}
247+
248+
orig_iocb->ki_pos = iocb->ki_pos;
249+
fdput(aio_req->fd);
250+
kmem_cache_free(ovl_aio_request_cachep, aio_req);
251+
}
252+
253+
static void ovl_aio_rw_complete(struct kiocb *iocb, long res, long res2)
254+
{
255+
struct ovl_aio_req *aio_req = container_of(iocb,
256+
struct ovl_aio_req, iocb);
257+
struct kiocb *orig_iocb = aio_req->orig_iocb;
258+
259+
ovl_aio_cleanup_handler(aio_req);
260+
orig_iocb->ki_complete(orig_iocb, res, res2);
261+
}
262+
228263
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
229264
{
230265
struct file *file = iocb->ki_filp;
@@ -240,10 +275,28 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
240275
return ret;
241276

242277
old_cred = ovl_override_creds(file_inode(file)->i_sb);
243-
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
244-
ovl_iocb_to_rwf(iocb));
278+
if (is_sync_kiocb(iocb)) {
279+
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
280+
ovl_iocb_to_rwf(iocb));
281+
} else {
282+
struct ovl_aio_req *aio_req;
283+
284+
ret = -ENOMEM;
285+
aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
286+
if (!aio_req)
287+
goto out;
288+
289+
aio_req->fd = real;
290+
real.flags = 0;
291+
aio_req->orig_iocb = iocb;
292+
kiocb_clone(&aio_req->iocb, iocb, real.file);
293+
aio_req->iocb.ki_complete = ovl_aio_rw_complete;
294+
ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter);
295+
if (ret != -EIOCBQUEUED)
296+
ovl_aio_cleanup_handler(aio_req);
297+
}
298+
out:
245299
revert_creds(old_cred);
246-
247300
ovl_file_accessed(file);
248301

249302
fdput(real);
@@ -274,15 +327,33 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
274327
goto out_unlock;
275328

276329
old_cred = ovl_override_creds(file_inode(file)->i_sb);
277-
file_start_write(real.file);
278-
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
279-
ovl_iocb_to_rwf(iocb));
280-
file_end_write(real.file);
330+
if (is_sync_kiocb(iocb)) {
331+
file_start_write(real.file);
332+
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
333+
ovl_iocb_to_rwf(iocb));
334+
file_end_write(real.file);
335+
/* Update size */
336+
ovl_copyattr(ovl_inode_real(inode), inode);
337+
} else {
338+
struct ovl_aio_req *aio_req;
339+
340+
ret = -ENOMEM;
341+
aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL);
342+
if (!aio_req)
343+
goto out;
344+
345+
file_start_write(real.file);
346+
aio_req->fd = real;
347+
real.flags = 0;
348+
aio_req->orig_iocb = iocb;
349+
kiocb_clone(&aio_req->iocb, iocb, real.file);
350+
aio_req->iocb.ki_complete = ovl_aio_rw_complete;
351+
ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
352+
if (ret != -EIOCBQUEUED)
353+
ovl_aio_cleanup_handler(aio_req);
354+
}
355+
out:
281356
revert_creds(old_cred);
282-
283-
/* Update size */
284-
ovl_copyattr(ovl_inode_real(inode), inode);
285-
286357
fdput(real);
287358

288359
out_unlock:
@@ -651,3 +722,19 @@ const struct file_operations ovl_file_operations = {
651722
.copy_file_range = ovl_copy_file_range,
652723
.remap_file_range = ovl_remap_file_range,
653724
};
725+
726+
int __init ovl_aio_request_cache_init(void)
727+
{
728+
ovl_aio_request_cachep = kmem_cache_create("ovl_aio_req",
729+
sizeof(struct ovl_aio_req),
730+
0, SLAB_HWCACHE_ALIGN, NULL);
731+
if (!ovl_aio_request_cachep)
732+
return -ENOMEM;
733+
734+
return 0;
735+
}
736+
737+
void ovl_aio_request_cache_destroy(void)
738+
{
739+
kmem_cache_destroy(ovl_aio_request_cachep);
740+
}

fs/overlayfs/overlayfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
450450

451451
/* file.c */
452452
extern const struct file_operations ovl_file_operations;
453+
int __init ovl_aio_request_cache_init(void);
454+
void ovl_aio_request_cache_destroy(void);
453455

454456
/* copy_up.c */
455457
int ovl_copy_up(struct dentry *dentry);

fs/overlayfs/super.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,9 +1770,15 @@ static int __init ovl_init(void)
17701770
if (ovl_inode_cachep == NULL)
17711771
return -ENOMEM;
17721772

1773-
err = register_filesystem(&ovl_fs_type);
1774-
if (err)
1775-
kmem_cache_destroy(ovl_inode_cachep);
1773+
err = ovl_aio_request_cache_init();
1774+
if (!err) {
1775+
err = register_filesystem(&ovl_fs_type);
1776+
if (!err)
1777+
return 0;
1778+
1779+
ovl_aio_request_cache_destroy();
1780+
}
1781+
kmem_cache_destroy(ovl_inode_cachep);
17761782

17771783
return err;
17781784
}
@@ -1787,7 +1793,7 @@ static void __exit ovl_exit(void)
17871793
*/
17881794
rcu_barrier();
17891795
kmem_cache_destroy(ovl_inode_cachep);
1790-
1796+
ovl_aio_request_cache_destroy();
17911797
}
17921798

17931799
module_init(ovl_init);

0 commit comments

Comments
 (0)