Skip to content

Commit 8b58924

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: lookup in inode cache first when decoding lower file handle
When decoding a lower file handle, we need to check if lower file was copied up and indexed and if it has a whiteout index, we need to check if this is an unlinked but open non-dir before returning -ESTALE. To find out if this is an unlinked but open non-dir we need to lookup an overlay inode in inode cache by lower inode and that requires decoding the lower file handle before looking in inode cache. Before this change, if the lower inode turned out to be a directory, we may have paid an expensive cost to reconnect that lower directory for nothing. After this change, we start by decoding a disconnected lower dentry and using the lower inode for looking up an overlay inode in inode cache. If we find overlay inode and dentry in cache, we avoid the index lookup overhead. If we don't find an overlay inode and dentry in cache, then we only need to decode a connected lower dentry in case the lower dentry is a non-indexed directory. The xfstests group overlay/exportfs tests decoding overlayfs file handles after drop_caches with different states of the file at encode and decode time. Overall the tests in the group call ovl_lower_fh_to_d() 89 times to decode a lower file handle. Before this change, the tests called ovl_get_index_fh() 75 times and reconnect_one() 61 times. After this change, the tests call ovl_get_index_fh() 70 times and reconnect_one() 59 times. The 2 cases where reconnect_one() was avoided are cases where a non-upper directory file handle was encoded, then the directory removed and then file handle was decoded. To demonstrate the affect on decoding file handles with hot inode/dentry cache, the drop_caches call in the tests was disabled. Without drop_caches, there are no reconnect_one() calls at all before or after the change. Before the change, there are 75 calls to ovl_get_index_fh(), exactly as the case with drop_caches. After the change, there are only 10 calls to ovl_get_index_fh(). Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 8a22efa commit 8b58924

File tree

1 file changed

+33
-25
lines changed

1 file changed

+33
-25
lines changed

fs/overlayfs/export.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -703,25 +703,39 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
703703
struct ovl_path *stack = &origin;
704704
struct dentry *dentry = NULL;
705705
struct dentry *index = NULL;
706-
struct inode *inode = NULL;
707-
bool is_deleted = false;
706+
struct inode *inode;
708707
int err;
709708

710-
/* First lookup indexed upper by fh */
709+
/* First lookup overlay inode in inode cache by origin fh */
710+
err = ovl_check_origin_fh(ofs, fh, false, NULL, &stack);
711+
if (err)
712+
return ERR_PTR(err);
713+
714+
if (!d_is_dir(origin.dentry) ||
715+
!(origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
716+
inode = ovl_lookup_inode(sb, origin.dentry, false);
717+
err = PTR_ERR(inode);
718+
if (IS_ERR(inode))
719+
goto out_err;
720+
if (inode) {
721+
dentry = d_find_any_alias(inode);
722+
iput(inode);
723+
if (dentry)
724+
goto out;
725+
}
726+
}
727+
728+
/* Then lookup indexed upper/whiteout by origin fh */
711729
if (ofs->indexdir) {
712730
index = ovl_get_index_fh(ofs, fh);
713731
err = PTR_ERR(index);
714732
if (IS_ERR(index)) {
715-
if (err != -ESTALE)
716-
return ERR_PTR(err);
717-
718-
/* Found a whiteout index - treat as deleted inode */
719-
is_deleted = true;
720733
index = NULL;
734+
goto out_err;
721735
}
722736
}
723737

724-
/* Then try to get upper dir by index */
738+
/* Then try to get a connected upper dir by index */
725739
if (index && d_is_dir(index)) {
726740
struct dentry *upper = ovl_index_upper(ofs, index);
727741

@@ -734,32 +748,26 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
734748
goto out;
735749
}
736750

737-
/* Then lookup origin by fh */
738-
err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
739-
if (err) {
740-
goto out_err;
741-
} else if (index) {
742-
err = ovl_verify_origin(index, origin.dentry, false);
751+
/* Otherwise, get a connected non-upper dir or disconnected non-dir */
752+
if (d_is_dir(origin.dentry) &&
753+
(origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
754+
dput(origin.dentry);
755+
origin.dentry = NULL;
756+
err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
743757
if (err)
744758
goto out_err;
745-
} else if (is_deleted) {
746-
/* Lookup deleted non-dir by origin inode */
747-
if (!d_is_dir(origin.dentry))
748-
inode = ovl_lookup_inode(sb, origin.dentry, false);
749-
err = -ESTALE;
750-
if (!inode || atomic_read(&inode->i_count) == 1)
759+
}
760+
if (index) {
761+
err = ovl_verify_origin(index, origin.dentry, false);
762+
if (err)
751763
goto out_err;
752-
753-
/* Deleted but still open? */
754-
index = dget(ovl_i_dentry_upper(inode));
755764
}
756765

757766
dentry = ovl_get_dentry(sb, NULL, &origin, index);
758767

759768
out:
760769
dput(origin.dentry);
761770
dput(index);
762-
iput(inode);
763771
return dentry;
764772

765773
out_err:

0 commit comments

Comments
 (0)