ovl: implement lazy lookup of lowerdata in data-only layers
authorAmir Goldstein <amir73il@gmail.com>
Thu, 27 Apr 2023 10:39:09 +0000 (13:39 +0300)
committerAmir Goldstein <amir73il@gmail.com>
Mon, 19 Jun 2023 11:01:14 +0000 (14:01 +0300)
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>
fs/overlayfs/copy_up.c
fs/overlayfs/file.c
fs/overlayfs/namei.c
fs/overlayfs/overlayfs.h
fs/overlayfs/ovl_entry.h
fs/overlayfs/super.c
fs/overlayfs/util.c

index 95dce240ba17a4b952bf4c899819be7185cad72f..568f743a5584a5c666277d9121f8bc7768ed46c0 100644 (file)
@@ -1073,6 +1073,15 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
        if (WARN_ON(disconnected && d_is_dir(dentry)))
                return -EIO;
 
+       /*
+        * We may not need lowerdata if we are only doing metacopy up, but it is
+        * not very important to optimize this case, so do lazy lowerdata lookup
+        * before any copy up, so we can do it before taking ovl_inode_lock().
+        */
+       err = ovl_maybe_lookup_lowerdata(dentry);
+       if (err)
+               return err;
+
        old_cred = ovl_override_creds(dentry->d_sb);
        while (!err) {
                struct dentry *next;
index 951683a66ff65ec0d9a218a61c91f036a759f840..39737c2aaa845d486d08d9e45e813c3ec3ee7a20 100644 (file)
@@ -107,15 +107,21 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
 {
        struct dentry *dentry = file_dentry(file);
        struct path realpath;
+       int err;
 
        real->flags = 0;
        real->file = file->private_data;
 
-       if (allow_meta)
+       if (allow_meta) {
                ovl_path_real(dentry, &realpath);
-       else
+       } else {
+               /* lazy lookup of lowerdata */
+               err = ovl_maybe_lookup_lowerdata(dentry);
+               if (err)
+                       return err;
+
                ovl_path_realdata(dentry, &realpath);
-       /* TODO: lazy lookup of lowerdata */
+       }
        if (!realpath.dentry)
                return -EIO;
 
@@ -153,6 +159,11 @@ static int ovl_open(struct inode *inode, struct file *file)
        struct path realpath;
        int err;
 
+       /* lazy lookup of lowerdata */
+       err = ovl_maybe_lookup_lowerdata(dentry);
+       if (err)
+               return err;
+
        err = ovl_maybe_copy_up(dentry, file->f_flags);
        if (err)
                return err;
@@ -161,7 +172,6 @@ static int ovl_open(struct inode *inode, struct file *file)
        file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 
        ovl_path_realdata(dentry, &realpath);
-       /* TODO: lazy lookup of lowerdata */
        if (!realpath.dentry)
                return -EIO;
 
index 605108f0dbe934bda1278d499afa4b7846e84f04..b3b40bc9a5ab5dd1b3b05d30ecfe084b0bc6ec50 100644 (file)
@@ -889,6 +889,52 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
        return err;
 }
 
+/* Lazy lookup of lowerdata */
+int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
+{
+       struct inode *inode = d_inode(dentry);
+       const char *redirect = ovl_lowerdata_redirect(inode);
+       struct ovl_path datapath = {};
+       const struct cred *old_cred;
+       int err;
+
+       if (!redirect || ovl_dentry_lowerdata(dentry))
+               return 0;
+
+       if (redirect[0] != '/')
+               return -EIO;
+
+       err = ovl_inode_lock_interruptible(inode);
+       if (err)
+               return err;
+
+       err = 0;
+       /* Someone got here before us? */
+       if (ovl_dentry_lowerdata(dentry))
+               goto out;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       err = ovl_lookup_data_layers(dentry, redirect, &datapath);
+       revert_creds(old_cred);
+       if (err)
+               goto out_err;
+
+       err = ovl_dentry_set_lowerdata(dentry, &datapath);
+       if (err)
+               goto out_err;
+
+out:
+       ovl_inode_unlock(inode);
+       dput(datapath.dentry);
+
+       return err;
+
+out_err:
+       pr_warn_ratelimited("lazy lowerdata lookup failed (%pd2, err=%i)\n",
+                           dentry, err);
+       goto out;
+}
+
 struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags)
 {
@@ -1072,14 +1118,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                }
        }
 
-       /* Lookup absolute redirect from lower metacopy in data-only layers */
+       /* Defer lookup of lowerdata in data-only layers to first access */
        if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
-               err = ovl_lookup_data_layers(dentry, d.redirect,
-                                            &stack[ctr]);
-               if (!err) {
-                       d.metacopy = false;
-                       ctr++;
-               }
+               d.metacopy = false;
+               ctr++;
        }
 
        /*
index 5cc03b8d7b25f2449dd63c3fb3ce998822d458f8..fcac4e2c56ab3ceb3a0458e9a4c541975617e998 100644 (file)
@@ -396,6 +396,7 @@ enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path);
 struct dentry *ovl_dentry_upper(struct dentry *dentry);
 struct dentry *ovl_dentry_lower(struct dentry *dentry);
 struct dentry *ovl_dentry_lowerdata(struct dentry *dentry);
+int ovl_dentry_set_lowerdata(struct dentry *dentry, struct ovl_path *datapath);
 const struct ovl_layer *ovl_i_layer_lower(struct inode *inode);
 const struct ovl_layer *ovl_layer_lower(struct dentry *dentry);
 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);
 struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
                                struct dentry *origin, bool verify);
 int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
+int ovl_maybe_lookup_lowerdata(struct dentry *dentry);
 struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags);
 bool ovl_lower_positive(struct dentry *dentry);
index 513c2c499e4169b3c12069a1dd6d1f1044e68e79..c6c7d09b494ebeefc71edd2e26831da5326101d6 100644 (file)
@@ -146,7 +146,7 @@ static inline struct dentry *ovl_lowerdata_dentry(struct ovl_entry *oe)
 {
        struct ovl_path *lowerdata = ovl_lowerdata(oe);
 
-       return lowerdata ? lowerdata->dentry : NULL;
+       return lowerdata ? READ_ONCE(lowerdata->dentry) : NULL;
 }
 
 /* private information held for every overlayfs dentry */
index f3e5b43c4106d58dc6258ffde94c7a8df01b7d08..14a2ebdc8126d90ee03ab8f434cc8768a8948b45 100644 (file)
@@ -82,13 +82,14 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
                return real;
 
        /*
-        * XXX: We may need lazy lookup of lowerdata for !inode case to return
+        * Best effort lazy lookup of lowerdata for !inode case to return
         * the real lowerdata dentry.  The only current caller of d_real() with
         * NULL inode is d_real_inode() from 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.
         */
+       ovl_maybe_lookup_lowerdata(dentry);
        lower = ovl_dentry_lowerdata(dentry);
        if (!lower)
                goto bug;
index 23c0224779beddb64cf46705b2f3618cfa40e5f7..939e4d586ec2b6c6f0cc78bad0791a6609d6d6c0 100644 (file)
@@ -229,8 +229,14 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path)
        struct dentry *lowerdata_dentry = ovl_lowerdata_dentry(oe);
 
        if (lowerdata_dentry) {
-               path->mnt = lowerdata->layer->mnt;
                path->dentry = lowerdata_dentry;
+               /*
+                * Pairs with smp_wmb() in ovl_dentry_set_lowerdata().
+                * Make sure that if lowerdata->dentry is visible, then
+                * datapath->layer is visible as well.
+                */
+               smp_rmb();
+               path->mnt = READ_ONCE(lowerdata->layer)->mnt;
        } else {
                *path = (struct path) { };
        }
@@ -292,6 +298,29 @@ struct dentry *ovl_dentry_lowerdata(struct dentry *dentry)
        return ovl_lowerdata_dentry(OVL_E(dentry));
 }
 
+int ovl_dentry_set_lowerdata(struct dentry *dentry, struct ovl_path *datapath)
+{
+       struct ovl_entry *oe = OVL_E(dentry);
+       struct ovl_path *lowerdata = ovl_lowerdata(oe);
+       struct dentry *datadentry = datapath->dentry;
+
+       if (WARN_ON_ONCE(ovl_numlower(oe) <= 1))
+               return -EIO;
+
+       WRITE_ONCE(lowerdata->layer, datapath->layer);
+       /*
+        * Pairs with smp_rmb() in ovl_path_lowerdata().
+        * Make sure that if lowerdata->dentry is visible, then
+        * lowerdata->layer is visible as well.
+        */
+       smp_wmb();
+       WRITE_ONCE(lowerdata->dentry, dget(datadentry));
+
+       ovl_dentry_update_reval(dentry, datadentry);
+
+       return 0;
+}
+
 struct dentry *ovl_dentry_real(struct dentry *dentry)
 {
        return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry);