Skip to content

Commit c86243b

Browse files
rhvgoyalMiklos Szeredi
authored andcommitted
ovl: provide a mount option "volatile"
Container folks are complaining that dnf/yum issues too many sync while installing packages and this slows down the image build. Build requirement is such that they don't care if a node goes down while build was still going on. In that case, they will simply throw away unfinished layer and start new build. So they don't care about syncing intermediate state to the disk and hence don't want to pay the price associated with sync. So they are asking for mount options where they can disable sync on overlay mount point. They primarily seem to have two use cases. - For building images, they will mount overlay with nosync and then sync upper layer after unmounting overlay and reuse upper as lower for next layer. - For running containers, they don't seem to care about syncing upper layer because if node goes down, they will simply throw away upper layer and create a fresh one. So this patch provides a mount option "volatile" which disables all forms of sync. Now it is caller's responsibility to throw away upper if system crashes or shuts down and start fresh. With "volatile", I am seeing roughly 20% speed up in my VM where I am just installing emacs in an image. Installation time drops from 31 seconds to 25 seconds when nosync option is used. This is for the case of building on top of an image where all packages are already cached. That way I take out the network operations latency out of the measurement. Giuseppe is also looking to cut down on number of iops done on the disk. He is complaining that often in cloud their VMs are throttled if they cross the limit. This option can help them where they reduce number of iops (by cutting down on frequent sync and writebacks). Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com> Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 235ce9e commit c86243b

6 files changed

Lines changed: 116 additions & 8 deletions

File tree

Documentation/filesystems/overlayfs.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,25 @@ Note: the mount options index=off,nfs_export=on are conflicting for a
564564
read-write mount and will result in an error.
565565

566566

567+
Volatile mount
568+
--------------
569+
570+
This is enabled with the "volatile" mount option. Volatile mounts are not
571+
guaranteed to survive a crash. It is strongly recommended that volatile
572+
mounts are only used if data written to the overlay can be recreated
573+
without significant effort.
574+
575+
The advantage of mounting with the "volatile" option is that all forms of
576+
sync calls to the upper filesystem are omitted.
577+
578+
When overlay is mounted with "volatile" option, the directory
579+
"$workdir/work/incompat/volatile" is created. During next mount, overlay
580+
checks for this directory and refuses to mount if present. This is a strong
581+
indicator that user should throw away upper and work directories and create
582+
fresh one. In very limited cases where the user knows that the system has
583+
not crashed and contents of upperdir are intact, The "volatile" directory
584+
can be removed.
585+
567586
Testsuite
568587
---------
569588

fs/overlayfs/copy_up.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
128128
return error;
129129
}
130130

131-
static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
131+
static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
132+
struct path *new, loff_t len)
132133
{
133134
struct file *old_file;
134135
struct file *new_file;
@@ -218,7 +219,7 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
218219
len -= bytes;
219220
}
220221
out:
221-
if (!error)
222+
if (!error && ovl_should_sync(ofs))
222223
error = vfs_fsync(new_file, 0);
223224
fput(new_file);
224225
out_fput:
@@ -484,6 +485,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
484485

485486
static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
486487
{
488+
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
487489
int err;
488490

489491
/*
@@ -499,7 +501,8 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
499501
upperpath.dentry = temp;
500502

501503
ovl_path_lowerdata(c->dentry, &datapath);
502-
err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
504+
err = ovl_copy_up_data(ofs, &datapath, &upperpath,
505+
c->stat.size);
503506
if (err)
504507
return err;
505508
}
@@ -784,6 +787,7 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
784787
/* Copy up data of an inode which was copied up metadata only in the past. */
785788
static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
786789
{
790+
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
787791
struct path upperpath, datapath;
788792
int err;
789793
char *capability = NULL;
@@ -804,7 +808,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
804808
goto out;
805809
}
806810

807-
err = ovl_copy_up_data(&datapath, &upperpath, c->stat.size);
811+
err = ovl_copy_up_data(ofs, &datapath, &upperpath, c->stat.size);
808812
if (err)
809813
goto out_free;
810814

fs/overlayfs/file.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
331331
struct fd real;
332332
const struct cred *old_cred;
333333
ssize_t ret;
334+
int ifl = iocb->ki_flags;
334335

335336
if (!iov_iter_count(iter))
336337
return 0;
@@ -346,11 +347,14 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
346347
if (ret)
347348
goto out_unlock;
348349

350+
if (!ovl_should_sync(OVL_FS(inode->i_sb)))
351+
ifl &= ~(IOCB_DSYNC | IOCB_SYNC);
352+
349353
old_cred = ovl_override_creds(file_inode(file)->i_sb);
350354
if (is_sync_kiocb(iocb)) {
351355
file_start_write(real.file);
352356
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
353-
ovl_iocb_to_rwf(iocb->ki_flags));
357+
ovl_iocb_to_rwf(ifl));
354358
file_end_write(real.file);
355359
/* Update size */
356360
ovl_copyattr(ovl_inode_real(inode), inode);
@@ -370,6 +374,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
370374
real.flags = 0;
371375
aio_req->orig_iocb = iocb;
372376
kiocb_clone(&aio_req->iocb, iocb, real.file);
377+
aio_req->iocb.ki_flags = ifl;
373378
aio_req->iocb.ki_complete = ovl_aio_rw_complete;
374379
ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
375380
if (ret != -EIOCBQUEUED)
@@ -433,6 +438,9 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
433438
const struct cred *old_cred;
434439
int ret;
435440

441+
if (!ovl_should_sync(OVL_FS(file_inode(file)->i_sb)))
442+
return 0;
443+
436444
ret = ovl_real_fdget_meta(file, &real, !datasync);
437445
if (ret)
438446
return ret;

fs/overlayfs/ovl_entry.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct ovl_config {
1717
bool nfs_export;
1818
int xino;
1919
bool metacopy;
20+
bool ovl_volatile;
2021
};
2122

2223
struct ovl_sb {
@@ -90,6 +91,11 @@ static inline struct ovl_fs *OVL_FS(struct super_block *sb)
9091
return (struct ovl_fs *)sb->s_fs_info;
9192
}
9293

94+
static inline bool ovl_should_sync(struct ovl_fs *ofs)
95+
{
96+
return !ofs->config.ovl_volatile;
97+
}
98+
9399
/* private information held for every overlayfs dentry */
94100
struct ovl_entry {
95101
union {

fs/overlayfs/readdir.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,9 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
863863
if (!OVL_TYPE_UPPER(ovl_path_type(dentry)))
864864
return 0;
865865

866+
if (!ovl_should_sync(OVL_FS(dentry->d_sb)))
867+
return 0;
868+
866869
/*
867870
* Need to check if we started out being a lower dir, but got copied up
868871
*/

fs/overlayfs/super.c

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ static int ovl_sync_fs(struct super_block *sb, int wait)
264264
if (!ovl_upper_mnt(ofs))
265265
return 0;
266266

267+
if (!ovl_should_sync(ofs))
268+
return 0;
267269
/*
268270
* Not called for sync(2) call or an emergency sync (SB_I_SKIP_SYNC).
269271
* All the super blocks will be iterated, including upper_sb.
@@ -362,6 +364,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
362364
if (ofs->config.metacopy != ovl_metacopy_def)
363365
seq_printf(m, ",metacopy=%s",
364366
ofs->config.metacopy ? "on" : "off");
367+
if (ofs->config.ovl_volatile)
368+
seq_puts(m, ",volatile");
365369
return 0;
366370
}
367371

@@ -376,9 +380,11 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
376380

377381
if (*flags & SB_RDONLY && !sb_rdonly(sb)) {
378382
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
379-
down_read(&upper_sb->s_umount);
380-
ret = sync_filesystem(upper_sb);
381-
up_read(&upper_sb->s_umount);
383+
if (ovl_should_sync(ofs)) {
384+
down_read(&upper_sb->s_umount);
385+
ret = sync_filesystem(upper_sb);
386+
up_read(&upper_sb->s_umount);
387+
}
382388
}
383389

384390
return ret;
@@ -411,6 +417,7 @@ enum {
411417
OPT_XINO_AUTO,
412418
OPT_METACOPY_ON,
413419
OPT_METACOPY_OFF,
420+
OPT_VOLATILE,
414421
OPT_ERR,
415422
};
416423

@@ -429,6 +436,7 @@ static const match_table_t ovl_tokens = {
429436
{OPT_XINO_AUTO, "xino=auto"},
430437
{OPT_METACOPY_ON, "metacopy=on"},
431438
{OPT_METACOPY_OFF, "metacopy=off"},
439+
{OPT_VOLATILE, "volatile"},
432440
{OPT_ERR, NULL}
433441
};
434442

@@ -573,6 +581,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
573581
metacopy_opt = true;
574582
break;
575583

584+
case OPT_VOLATILE:
585+
config->ovl_volatile = true;
586+
break;
587+
576588
default:
577589
pr_err("unrecognized mount option \"%s\" or missing value\n",
578590
p);
@@ -595,6 +607,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
595607
config->index = false;
596608
}
597609

610+
if (!config->upperdir && config->ovl_volatile) {
611+
pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n");
612+
config->ovl_volatile = false;
613+
}
614+
598615
err = ovl_parse_redirect_mode(config, config->redirect_mode);
599616
if (err)
600617
return err;
@@ -1203,6 +1220,45 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
12031220
return err;
12041221
}
12051222

1223+
static struct dentry *ovl_lookup_or_create(struct dentry *parent,
1224+
const char *name, umode_t mode)
1225+
{
1226+
size_t len = strlen(name);
1227+
struct dentry *child;
1228+
1229+
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
1230+
child = lookup_one_len(name, parent, len);
1231+
if (!IS_ERR(child) && !child->d_inode)
1232+
child = ovl_create_real(parent->d_inode, child,
1233+
OVL_CATTR(mode));
1234+
inode_unlock(parent->d_inode);
1235+
dput(parent);
1236+
1237+
return child;
1238+
}
1239+
1240+
/*
1241+
* Creates $workdir/work/incompat/volatile/dirty file if it is not already
1242+
* present.
1243+
*/
1244+
static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
1245+
{
1246+
unsigned int ctr;
1247+
struct dentry *d = dget(ofs->workbasedir);
1248+
static const char *const volatile_path[] = {
1249+
OVL_WORKDIR_NAME, "incompat", "volatile", "dirty"
1250+
};
1251+
const char *const *name = volatile_path;
1252+
1253+
for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) {
1254+
d = ovl_lookup_or_create(d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
1255+
if (IS_ERR(d))
1256+
return PTR_ERR(d);
1257+
}
1258+
dput(d);
1259+
return 0;
1260+
}
1261+
12061262
static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
12071263
struct path *workpath)
12081264
{
@@ -1286,6 +1342,18 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
12861342
goto out;
12871343
}
12881344

1345+
/*
1346+
* For volatile mount, create a incompat/volatile/dirty file to keep
1347+
* track of it.
1348+
*/
1349+
if (ofs->config.ovl_volatile) {
1350+
err = ovl_create_volatile_dirty(ofs);
1351+
if (err < 0) {
1352+
pr_err("Failed to create volatile/dirty file.\n");
1353+
goto out;
1354+
}
1355+
}
1356+
12891357
/* Check if upper/work fs supports file handles */
12901358
fh_type = ovl_can_decode_fh(ofs->workdir->d_sb);
12911359
if (ofs->config.index && !fh_type) {

0 commit comments

Comments
 (0)