Skip to content

Commit e58bc92

Browse files
committed
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs updates from Miklos Szeredi: "Because copy up can take a long time, serialized copy ups could be a big performance bottleneck. This update allows concurrent copy up of regular files eliminating this potential problem. There are also minor fixes" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: drop CAP_SYS_RESOURCE from saved mounter's credentials ovl: properly implement sync_filesystem() ovl: concurrent copy up of regular files ovl: introduce copy up waitqueue ovl: copy up regular file using O_TMPFILE ovl: rearrange code in ovl_copy_up_locked() ovl: check if upperdir fs supports O_TMPFILE
2 parents 590dce2 + 51f8f3c commit e58bc92

File tree

5 files changed

+148
-27
lines changed

5 files changed

+148
-27
lines changed

fs/overlayfs/copy_up.c

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/fdtable.h>
2222
#include <linux/ratelimit.h>
2323
#include "overlayfs.h"
24+
#include "ovl_entry.h"
2425

2526
#define OVL_COPY_UP_CHUNK_SIZE (1 << 20)
2627

@@ -233,12 +234,14 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
233234

234235
static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
235236
struct dentry *dentry, struct path *lowerpath,
236-
struct kstat *stat, const char *link)
237+
struct kstat *stat, const char *link,
238+
struct kstat *pstat, bool tmpfile)
237239
{
238240
struct inode *wdir = workdir->d_inode;
239241
struct inode *udir = upperdir->d_inode;
240242
struct dentry *newdentry = NULL;
241243
struct dentry *upper = NULL;
244+
struct dentry *temp = NULL;
242245
int err;
243246
const struct cred *old_creds = NULL;
244247
struct cred *new_creds = NULL;
@@ -249,25 +252,30 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
249252
.link = link
250253
};
251254

252-
newdentry = ovl_lookup_temp(workdir, dentry);
253-
err = PTR_ERR(newdentry);
254-
if (IS_ERR(newdentry))
255-
goto out;
256-
257255
upper = lookup_one_len(dentry->d_name.name, upperdir,
258256
dentry->d_name.len);
259257
err = PTR_ERR(upper);
260258
if (IS_ERR(upper))
261-
goto out1;
259+
goto out;
262260

263261
err = security_inode_copy_up(dentry, &new_creds);
264262
if (err < 0)
265-
goto out2;
263+
goto out1;
266264

267265
if (new_creds)
268266
old_creds = override_creds(new_creds);
269267

270-
err = ovl_create_real(wdir, newdentry, &cattr, NULL, true);
268+
if (tmpfile)
269+
temp = ovl_do_tmpfile(upperdir, stat->mode);
270+
else
271+
temp = ovl_lookup_temp(workdir, dentry);
272+
err = PTR_ERR(temp);
273+
if (IS_ERR(temp))
274+
goto out1;
275+
276+
err = 0;
277+
if (!tmpfile)
278+
err = ovl_create_real(wdir, temp, &cattr, NULL, true);
271279

272280
if (new_creds) {
273281
revert_creds(old_creds);
@@ -282,39 +290,55 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
282290

283291
ovl_path_upper(dentry, &upperpath);
284292
BUG_ON(upperpath.dentry != NULL);
285-
upperpath.dentry = newdentry;
293+
upperpath.dentry = temp;
294+
295+
if (tmpfile) {
296+
inode_unlock(udir);
297+
err = ovl_copy_up_data(lowerpath, &upperpath,
298+
stat->size);
299+
inode_lock_nested(udir, I_MUTEX_PARENT);
300+
} else {
301+
err = ovl_copy_up_data(lowerpath, &upperpath,
302+
stat->size);
303+
}
286304

287-
err = ovl_copy_up_data(lowerpath, &upperpath, stat->size);
288305
if (err)
289306
goto out_cleanup;
290307
}
291308

292-
err = ovl_copy_xattr(lowerpath->dentry, newdentry);
309+
err = ovl_copy_xattr(lowerpath->dentry, temp);
293310
if (err)
294311
goto out_cleanup;
295312

296-
inode_lock(newdentry->d_inode);
297-
err = ovl_set_attr(newdentry, stat);
298-
inode_unlock(newdentry->d_inode);
313+
inode_lock(temp->d_inode);
314+
err = ovl_set_attr(temp, stat);
315+
inode_unlock(temp->d_inode);
299316
if (err)
300317
goto out_cleanup;
301318

302-
err = ovl_do_rename(wdir, newdentry, udir, upper, 0);
319+
if (tmpfile)
320+
err = ovl_do_link(temp, udir, upper, true);
321+
else
322+
err = ovl_do_rename(wdir, temp, udir, upper, 0);
303323
if (err)
304324
goto out_cleanup;
305325

326+
newdentry = dget(tmpfile ? upper : temp);
306327
ovl_dentry_update(dentry, newdentry);
307328
ovl_inode_update(d_inode(dentry), d_inode(newdentry));
308-
newdentry = NULL;
329+
330+
/* Restore timestamps on parent (best effort) */
331+
ovl_set_timestamps(upperdir, pstat);
309332
out2:
310-
dput(upper);
333+
dput(temp);
311334
out1:
312-
dput(newdentry);
335+
dput(upper);
313336
out:
314337
return err;
315338

316339
out_cleanup:
317-
ovl_cleanup(wdir, newdentry);
340+
if (!tmpfile)
341+
ovl_cleanup(wdir, temp);
318342
goto out2;
319343
}
320344

@@ -338,6 +362,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
338362
struct dentry *lowerdentry = lowerpath->dentry;
339363
struct dentry *upperdir;
340364
const char *link = NULL;
365+
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
341366

342367
if (WARN_ON(!workdir))
343368
return -EROFS;
@@ -358,6 +383,25 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
358383
return PTR_ERR(link);
359384
}
360385

386+
/* Should we copyup with O_TMPFILE or with workdir? */
387+
if (S_ISREG(stat->mode) && ofs->tmpfile) {
388+
err = ovl_copy_up_start(dentry);
389+
/* err < 0: interrupted, err > 0: raced with another copy-up */
390+
if (unlikely(err)) {
391+
pr_debug("ovl_copy_up_start(%pd2) = %i\n", dentry, err);
392+
if (err > 0)
393+
err = 0;
394+
goto out_done;
395+
}
396+
397+
inode_lock_nested(upperdir->d_inode, I_MUTEX_PARENT);
398+
err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath,
399+
stat, link, &pstat, true);
400+
inode_unlock(upperdir->d_inode);
401+
ovl_copy_up_end(dentry);
402+
goto out_done;
403+
}
404+
361405
err = -EIO;
362406
if (lock_rename(workdir, upperdir) != NULL) {
363407
pr_err("overlayfs: failed to lock workdir+upperdir\n");
@@ -370,13 +414,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
370414
}
371415

372416
err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath,
373-
stat, link);
374-
if (!err) {
375-
/* Restore timestamps on parent (best effort) */
376-
ovl_set_timestamps(upperdir, &pstat);
377-
}
417+
stat, link, &pstat, false);
378418
out_unlock:
379419
unlock_rename(workdir, upperdir);
420+
out_done:
380421
do_delayed_call(&done);
381422

382423
return err;

fs/overlayfs/overlayfs.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry)
127127
return err;
128128
}
129129

130+
static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode)
131+
{
132+
struct dentry *ret = vfs_tmpfile(dentry, mode, 0);
133+
int err = IS_ERR(ret) ? PTR_ERR(ret) : 0;
134+
135+
pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err);
136+
return ret;
137+
}
138+
130139
static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
131140
{
132141
unsigned long x = (unsigned long) READ_ONCE(inode->i_private);
@@ -169,6 +178,8 @@ void ovl_dentry_version_inc(struct dentry *dentry);
169178
u64 ovl_dentry_version_get(struct dentry *dentry);
170179
bool ovl_is_whiteout(struct dentry *dentry);
171180
struct file *ovl_path_open(struct path *path, int flags);
181+
int ovl_copy_up_start(struct dentry *dentry);
182+
void ovl_copy_up_end(struct dentry *dentry);
172183

173184
/* namei.c */
174185
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);

fs/overlayfs/ovl_entry.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct ovl_fs {
2727
struct ovl_config config;
2828
/* creds of process who forced instantiation of super block */
2929
const struct cred *creator_cred;
30+
bool tmpfile;
31+
wait_queue_head_t copyup_wq;
3032
};
3133

3234
/* private information held for every overlayfs dentry */
@@ -38,6 +40,7 @@ struct ovl_entry {
3840
u64 version;
3941
const char *redirect;
4042
bool opaque;
43+
bool copying;
4144
};
4245
struct rcu_head rcu;
4346
};

fs/overlayfs/super.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ static void ovl_put_super(struct super_block *sb)
161161
kfree(ufs);
162162
}
163163

164+
static int ovl_sync_fs(struct super_block *sb, int wait)
165+
{
166+
struct ovl_fs *ufs = sb->s_fs_info;
167+
struct super_block *upper_sb;
168+
int ret;
169+
170+
if (!ufs->upper_mnt)
171+
return 0;
172+
upper_sb = ufs->upper_mnt->mnt_sb;
173+
if (!upper_sb->s_op->sync_fs)
174+
return 0;
175+
176+
/* real inodes have already been synced by sync_filesystem(ovl_sb) */
177+
down_read(&upper_sb->s_umount);
178+
ret = upper_sb->s_op->sync_fs(upper_sb, wait);
179+
up_read(&upper_sb->s_umount);
180+
return ret;
181+
}
182+
164183
/**
165184
* ovl_statfs
166185
* @sb: The overlayfs super block
@@ -223,6 +242,7 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
223242

224243
static const struct super_operations ovl_super_operations = {
225244
.put_super = ovl_put_super,
245+
.sync_fs = ovl_sync_fs,
226246
.statfs = ovl_statfs,
227247
.show_options = ovl_show_options,
228248
.remount_fs = ovl_remount,
@@ -702,13 +722,15 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
702722
unsigned int stacklen = 0;
703723
unsigned int i;
704724
bool remote = false;
725+
struct cred *cred;
705726
int err;
706727

707728
err = -ENOMEM;
708729
ufs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
709730
if (!ufs)
710731
goto out;
711732

733+
init_waitqueue_head(&ufs->copyup_wq);
712734
ufs->config.redirect_dir = ovl_redirect_dir_def;
713735
err = ovl_parse_opt((char *) data, &ufs->config);
714736
if (err)
@@ -826,6 +848,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
826848
* creation of workdir in previous step.
827849
*/
828850
if (ufs->workdir) {
851+
struct dentry *temp;
852+
829853
err = ovl_check_d_type_supported(&workpath);
830854
if (err < 0)
831855
goto out_put_workdir;
@@ -837,6 +861,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
837861
*/
838862
if (!err)
839863
pr_warn("overlayfs: upper fs needs to support d_type.\n");
864+
865+
/* Check if upper/work fs supports O_TMPFILE */
866+
temp = ovl_do_tmpfile(ufs->workdir, S_IFREG | 0);
867+
ufs->tmpfile = !IS_ERR(temp);
868+
if (ufs->tmpfile)
869+
dput(temp);
870+
else
871+
pr_warn("overlayfs: upper fs does not support tmpfile.\n");
840872
}
841873
}
842874

@@ -871,10 +903,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
871903
else
872904
sb->s_d_op = &ovl_dentry_operations;
873905

874-
ufs->creator_cred = prepare_creds();
875-
if (!ufs->creator_cred)
906+
ufs->creator_cred = cred = prepare_creds();
907+
if (!cred)
876908
goto out_put_lower_mnt;
877909

910+
/* Never override disk quota limits or use reserved space */
911+
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
912+
878913
err = -ENOMEM;
879914
oe = ovl_alloc_entry(numlower);
880915
if (!oe)

fs/overlayfs/util.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/slab.h>
1313
#include <linux/cred.h>
1414
#include <linux/xattr.h>
15+
#include <linux/sched/signal.h>
1516
#include "overlayfs.h"
1617
#include "ovl_entry.h"
1718

@@ -264,3 +265,33 @@ struct file *ovl_path_open(struct path *path, int flags)
264265
{
265266
return dentry_open(path, flags | O_NOATIME, current_cred());
266267
}
268+
269+
int ovl_copy_up_start(struct dentry *dentry)
270+
{
271+
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
272+
struct ovl_entry *oe = dentry->d_fsdata;
273+
int err;
274+
275+
spin_lock(&ofs->copyup_wq.lock);
276+
err = wait_event_interruptible_locked(ofs->copyup_wq, !oe->copying);
277+
if (!err) {
278+
if (oe->__upperdentry)
279+
err = 1; /* Already copied up */
280+
else
281+
oe->copying = true;
282+
}
283+
spin_unlock(&ofs->copyup_wq.lock);
284+
285+
return err;
286+
}
287+
288+
void ovl_copy_up_end(struct dentry *dentry)
289+
{
290+
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
291+
struct ovl_entry *oe = dentry->d_fsdata;
292+
293+
spin_lock(&ofs->copyup_wq.lock);
294+
oe->copying = false;
295+
wake_up_locked(&ofs->copyup_wq);
296+
spin_unlock(&ofs->copyup_wq.lock);
297+
}

0 commit comments

Comments
 (0)