Skip to content

Commit 42dd69a

Browse files
committed
ovl: implement lazy lookup of lowerdata in data-only layers
Defer lookup of lowerdata in the data-only layers to first data access or before copy up. We perform lowerdata lookup before copy up even if copy up is metadata only copy up. We can further optimize this lookup later if needed. We do best effort lazy lookup of lowerdata for d_real_inode(), because this interface does not expect errors. The only current in-tree caller of d_real_inode() is trace_uprobe and this caller is likely going to be followed reading from the file, before placing uprobes on offset within the file, so lowerdata should be available when setting the uprobe. Tested-by: kernel test robot <oliver.sang@intel.com> Reviewed-by: Alexander Larsson <alexl@redhat.com> Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 4166564 commit 42dd69a

7 files changed

Lines changed: 107 additions & 14 deletions

File tree

fs/overlayfs/copy_up.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,15 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
10731073
if (WARN_ON(disconnected && d_is_dir(dentry)))
10741074
return -EIO;
10751075

1076+
/*
1077+
* We may not need lowerdata if we are only doing metacopy up, but it is
1078+
* not very important to optimize this case, so do lazy lowerdata lookup
1079+
* before any copy up, so we can do it before taking ovl_inode_lock().
1080+
*/
1081+
err = ovl_maybe_lookup_lowerdata(dentry);
1082+
if (err)
1083+
return err;
1084+
10761085
old_cred = ovl_override_creds(dentry->d_sb);
10771086
while (!err) {
10781087
struct dentry *next;

fs/overlayfs/file.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,21 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
107107
{
108108
struct dentry *dentry = file_dentry(file);
109109
struct path realpath;
110+
int err;
110111

111112
real->flags = 0;
112113
real->file = file->private_data;
113114

114-
if (allow_meta)
115+
if (allow_meta) {
115116
ovl_path_real(dentry, &realpath);
116-
else
117+
} else {
118+
/* lazy lookup of lowerdata */
119+
err = ovl_maybe_lookup_lowerdata(dentry);
120+
if (err)
121+
return err;
122+
117123
ovl_path_realdata(dentry, &realpath);
118-
/* TODO: lazy lookup of lowerdata */
124+
}
119125
if (!realpath.dentry)
120126
return -EIO;
121127

@@ -153,6 +159,11 @@ static int ovl_open(struct inode *inode, struct file *file)
153159
struct path realpath;
154160
int err;
155161

162+
/* lazy lookup of lowerdata */
163+
err = ovl_maybe_lookup_lowerdata(dentry);
164+
if (err)
165+
return err;
166+
156167
err = ovl_maybe_copy_up(dentry, file->f_flags);
157168
if (err)
158169
return err;
@@ -161,7 +172,6 @@ static int ovl_open(struct inode *inode, struct file *file)
161172
file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
162173

163174
ovl_path_realdata(dentry, &realpath);
164-
/* TODO: lazy lookup of lowerdata */
165175
if (!realpath.dentry)
166176
return -EIO;
167177

fs/overlayfs/namei.c

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,52 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
889889
return err;
890890
}
891891

892+
/* Lazy lookup of lowerdata */
893+
int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
894+
{
895+
struct inode *inode = d_inode(dentry);
896+
const char *redirect = ovl_lowerdata_redirect(inode);
897+
struct ovl_path datapath = {};
898+
const struct cred *old_cred;
899+
int err;
900+
901+
if (!redirect || ovl_dentry_lowerdata(dentry))
902+
return 0;
903+
904+
if (redirect[0] != '/')
905+
return -EIO;
906+
907+
err = ovl_inode_lock_interruptible(inode);
908+
if (err)
909+
return err;
910+
911+
err = 0;
912+
/* Someone got here before us? */
913+
if (ovl_dentry_lowerdata(dentry))
914+
goto out;
915+
916+
old_cred = ovl_override_creds(dentry->d_sb);
917+
err = ovl_lookup_data_layers(dentry, redirect, &datapath);
918+
revert_creds(old_cred);
919+
if (err)
920+
goto out_err;
921+
922+
err = ovl_dentry_set_lowerdata(dentry, &datapath);
923+
if (err)
924+
goto out_err;
925+
926+
out:
927+
ovl_inode_unlock(inode);
928+
dput(datapath.dentry);
929+
930+
return err;
931+
932+
out_err:
933+
pr_warn_ratelimited("lazy lowerdata lookup failed (%pd2, err=%i)\n",
934+
dentry, err);
935+
goto out;
936+
}
937+
892938
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
893939
unsigned int flags)
894940
{
@@ -1072,14 +1118,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10721118
}
10731119
}
10741120

1075-
/* Lookup absolute redirect from lower metacopy in data-only layers */
1121+
/* Defer lookup of lowerdata in data-only layers to first access */
10761122
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
1077-
err = ovl_lookup_data_layers(dentry, d.redirect,
1078-
&stack[ctr]);
1079-
if (!err) {
1080-
d.metacopy = false;
1081-
ctr++;
1082-
}
1123+
d.metacopy = false;
1124+
ctr++;
10831125
}
10841126

10851127
/*

fs/overlayfs/overlayfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path);
396396
struct dentry *ovl_dentry_upper(struct dentry *dentry);
397397
struct dentry *ovl_dentry_lower(struct dentry *dentry);
398398
struct dentry *ovl_dentry_lowerdata(struct dentry *dentry);
399+
int ovl_dentry_set_lowerdata(struct dentry *dentry, struct ovl_path *datapath);
399400
const struct ovl_layer *ovl_i_layer_lower(struct inode *inode);
400401
const struct ovl_layer *ovl_layer_lower(struct dentry *dentry);
401402
struct dentry *ovl_dentry_real(struct dentry *dentry);
@@ -558,6 +559,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh);
558559
struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
559560
struct dentry *origin, bool verify);
560561
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
562+
int ovl_maybe_lookup_lowerdata(struct dentry *dentry);
561563
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
562564
unsigned int flags);
563565
bool ovl_lower_positive(struct dentry *dentry);

fs/overlayfs/ovl_entry.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ static inline struct dentry *ovl_lowerdata_dentry(struct ovl_entry *oe)
146146
{
147147
struct ovl_path *lowerdata = ovl_lowerdata(oe);
148148

149-
return lowerdata ? lowerdata->dentry : NULL;
149+
return lowerdata ? READ_ONCE(lowerdata->dentry) : NULL;
150150
}
151151

152152
/* private information held for every overlayfs dentry */

fs/overlayfs/super.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
8282
return real;
8383

8484
/*
85-
* XXX: We may need lazy lookup of lowerdata for !inode case to return
85+
* Best effort lazy lookup of lowerdata for !inode case to return
8686
* the real lowerdata dentry. The only current caller of d_real() with
8787
* NULL inode is d_real_inode() from trace_uprobe and this caller is
8888
* likely going to be followed reading from the file, before placing
8989
* uprobes on offset within the file, so lowerdata should be available
9090
* when setting the uprobe.
9191
*/
92+
ovl_maybe_lookup_lowerdata(dentry);
9293
lower = ovl_dentry_lowerdata(dentry);
9394
if (!lower)
9495
goto bug;

fs/overlayfs/util.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,14 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path)
229229
struct dentry *lowerdata_dentry = ovl_lowerdata_dentry(oe);
230230

231231
if (lowerdata_dentry) {
232-
path->mnt = lowerdata->layer->mnt;
233232
path->dentry = lowerdata_dentry;
233+
/*
234+
* Pairs with smp_wmb() in ovl_dentry_set_lowerdata().
235+
* Make sure that if lowerdata->dentry is visible, then
236+
* datapath->layer is visible as well.
237+
*/
238+
smp_rmb();
239+
path->mnt = READ_ONCE(lowerdata->layer)->mnt;
234240
} else {
235241
*path = (struct path) { };
236242
}
@@ -292,6 +298,29 @@ struct dentry *ovl_dentry_lowerdata(struct dentry *dentry)
292298
return ovl_lowerdata_dentry(OVL_E(dentry));
293299
}
294300

301+
int ovl_dentry_set_lowerdata(struct dentry *dentry, struct ovl_path *datapath)
302+
{
303+
struct ovl_entry *oe = OVL_E(dentry);
304+
struct ovl_path *lowerdata = ovl_lowerdata(oe);
305+
struct dentry *datadentry = datapath->dentry;
306+
307+
if (WARN_ON_ONCE(ovl_numlower(oe) <= 1))
308+
return -EIO;
309+
310+
WRITE_ONCE(lowerdata->layer, datapath->layer);
311+
/*
312+
* Pairs with smp_rmb() in ovl_path_lowerdata().
313+
* Make sure that if lowerdata->dentry is visible, then
314+
* lowerdata->layer is visible as well.
315+
*/
316+
smp_wmb();
317+
WRITE_ONCE(lowerdata->dentry, dget(datadentry));
318+
319+
ovl_dentry_update_reval(dentry, datadentry);
320+
321+
return 0;
322+
}
323+
295324
struct dentry *ovl_dentry_real(struct dentry *dentry)
296325
{
297326
return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry);

0 commit comments

Comments
 (0)