diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 0d9de353f42bc536237f359f6841632aa5a872ba..9f5a47338e59bcf676f1ada5c796d76639475494 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -316,6 +316,29 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
 	return err;
 }
 
+static int ovl_link_up(struct dentry *parent, struct dentry *dentry)
+{
+	int err;
+	struct dentry *upper;
+	struct dentry *upperdir = ovl_dentry_upper(parent);
+	struct inode *udir = d_inode(upperdir);
+
+	inode_lock_nested(udir, I_MUTEX_PARENT);
+	upper = lookup_one_len(dentry->d_name.name, upperdir,
+			       dentry->d_name.len);
+	err = PTR_ERR(upper);
+	if (!IS_ERR(upper)) {
+		err = ovl_do_link(ovl_dentry_upper(dentry), udir, upper, true);
+		dput(upper);
+
+		if (!err)
+			ovl_dentry_set_upper_alias(dentry);
+	}
+	inode_unlock(udir);
+
+	return err;
+}
+
 struct ovl_copy_up_ctx {
 	struct dentry *parent;
 	struct dentry *dentry;
@@ -323,9 +346,11 @@ struct ovl_copy_up_ctx {
 	struct kstat stat;
 	struct kstat pstat;
 	const char *link;
-	struct dentry *upperdir;
+	struct dentry *destdir;
+	struct qstr destname;
 	struct dentry *workdir;
 	bool tmpfile;
+	bool origin;
 };
 
 static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
@@ -333,10 +358,9 @@ static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
 {
 	int err;
 	struct dentry *upper;
-	struct inode *udir = d_inode(c->upperdir);
+	struct inode *udir = d_inode(c->destdir);
 
-	upper = lookup_one_len(c->dentry->d_name.name, c->upperdir,
-			       c->dentry->d_name.len);
+	upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len);
 	if (IS_ERR(upper))
 		return PTR_ERR(upper);
 
@@ -345,11 +369,8 @@ static int ovl_install_temp(struct ovl_copy_up_ctx *c, struct dentry *temp,
 	else
 		err = ovl_do_rename(d_inode(c->workdir), temp, udir, upper, 0);
 
-	/* Restore timestamps on parent (best effort) */
-	if (!err) {
-		ovl_set_timestamps(c->upperdir, &c->pstat);
+	if (!err)
 		*newdentry = dget(c->tmpfile ? upper : temp);
-	}
 	dput(upper);
 
 	return err;
@@ -439,7 +460,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
 	 * Don't set origin when we are breaking the association with a lower
 	 * hard link.
 	 */
-	if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1) {
+	if (c->origin) {
 		err = ovl_set_origin(c->dentry, c->lowerpath.dentry, temp);
 		if (err)
 			return err;
@@ -450,7 +471,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
 
 static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
 {
-	struct inode *udir = c->upperdir->d_inode;
+	struct inode *udir = c->destdir->d_inode;
 	struct dentry *newdentry = NULL;
 	struct dentry *temp = NULL;
 	int err;
@@ -473,7 +494,6 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c)
 	if (err)
 		goto out_cleanup;
 
-	ovl_dentry_set_upper_alias(c->dentry);
 	ovl_inode_update(d_inode(c->dentry), newdentry);
 out:
 	dput(temp);
@@ -498,24 +518,57 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
 {
 	int err;
 	struct ovl_fs *ofs = c->dentry->d_sb->s_fs_info;
+	bool indexed = false;
 
-	/* Mark parent "impure" because it may now contain non-pure upper */
-	err = ovl_set_impure(c->parent, c->upperdir);
-	if (err)
-		return err;
+	if (ovl_indexdir(c->dentry->d_sb) && !S_ISDIR(c->stat.mode) &&
+	    c->stat.nlink > 1)
+		indexed = true;
+
+	if (S_ISDIR(c->stat.mode) || c->stat.nlink == 1 || indexed)
+		c->origin = true;
+
+	if (indexed) {
+		c->destdir = ovl_indexdir(c->dentry->d_sb);
+		err = ovl_get_index_name(c->lowerpath.dentry, &c->destname);
+		if (err)
+			return err;
+	} else {
+		/*
+		 * Mark parent "impure" because it may now contain non-pure
+		 * upper
+		 */
+		err = ovl_set_impure(c->parent, c->destdir);
+		if (err)
+			return err;
+	}
 
 	/* Should we copyup with O_TMPFILE or with workdir? */
 	if (S_ISREG(c->stat.mode) && ofs->tmpfile) {
 		c->tmpfile = true;
-		return  ovl_copy_up_locked(c);
+		err = ovl_copy_up_locked(c);
+	} else {
+		err = -EIO;
+		if (lock_rename(c->workdir, c->destdir) != NULL) {
+			pr_err("overlayfs: failed to lock workdir+upperdir\n");
+		} else {
+			err = ovl_copy_up_locked(c);
+			unlock_rename(c->workdir, c->destdir);
+		}
 	}
 
-	err = -EIO;
-	if (lock_rename(c->workdir, c->upperdir) != NULL) {
-		pr_err("overlayfs: failed to lock workdir+upperdir\n");
-	} else {
-		err = ovl_copy_up_locked(c);
-		unlock_rename(c->workdir, c->upperdir);
+	if (indexed) {
+		if (!err)
+			ovl_set_flag(OVL_INDEX, d_inode(c->dentry));
+		kfree(c->destname.name);
+	} else if (!err) {
+		struct inode *udir = d_inode(c->destdir);
+
+		/* Restore timestamps on parent (best effort) */
+		inode_lock(udir);
+		ovl_set_timestamps(c->destdir, &c->pstat);
+		inode_unlock(udir);
+
+		ovl_dentry_set_upper_alias(c->dentry);
 	}
 
 	return err;
@@ -543,7 +596,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
 		return err;
 
 	ovl_path_upper(parent, &parentpath);
-	ctx.upperdir = parentpath.dentry;
+	ctx.destdir = parentpath.dentry;
+	ctx.destname = dentry->d_name;
 
 	err = vfs_getattr(&parentpath, &ctx.pstat,
 			  STATX_ATIME | STATX_MTIME, AT_STATX_SYNC_AS_STAT);
@@ -567,7 +621,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
 		if (err > 0)
 			err = 0;
 	} else {
-		err = ovl_do_copy_up(&ctx);
+		if (!ovl_dentry_upper(dentry))
+			err = ovl_do_copy_up(&ctx);
+		if (!err && !ovl_dentry_has_upper_alias(dentry))
+			err = ovl_link_up(parent, dentry);
 		ovl_copy_up_end(dentry);
 	}
 	do_delayed_call(&done);
@@ -583,9 +640,22 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
 	while (!err) {
 		struct dentry *next;
 		struct dentry *parent;
-		enum ovl_path_type type = ovl_path_type(dentry);
 
-		if (OVL_TYPE_UPPER(type))
+		/*
+		 * Check if copy-up has happened as well as for upper alias (in
+		 * case of hard links) is there.
+		 *
+		 * Both checks are lockless:
+		 *  - false negatives: will recheck under oi->lock
+		 *  - false positives:
+		 *    + ovl_dentry_upper() uses memory barriers to ensure the
+		 *      upper dentry is up-to-date
+		 *    + ovl_dentry_has_upper_alias() relies on locking of
+		 *      upper parent i_rwsem to prevent reordering copy-up
+		 *      with rename.
+		 */
+		if (ovl_dentry_upper(dentry) &&
+		    ovl_dentry_has_upper_alias(dentry))
 			break;
 
 		next = dget(dentry);
@@ -593,8 +663,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
 		for (;;) {
 			parent = dget_parent(next);
 
-			type = ovl_path_type(parent);
-			if (OVL_TYPE_UPPER(type))
+			if (ovl_dentry_upper(parent))
 				break;
 
 			dput(next);
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index d9fe07defca33e742c9100a46e240f3d860093ff..44d262a0a77edcfa742a15792966861e53cf027d 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -305,13 +305,13 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
 	return acl;
 }
 
-static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
-				  struct dentry *realdentry)
+static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
 {
-	if (OVL_TYPE_UPPER(type))
+	if (ovl_dentry_upper(dentry) &&
+	    ovl_dentry_has_upper_alias(dentry))
 		return false;
 
-	if (special_file(realdentry->d_inode->i_mode))
+	if (special_file(d_inode(dentry)->i_mode))
 		return false;
 
 	if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC))
@@ -323,11 +323,8 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
 int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
 {
 	int err = 0;
-	struct path realpath;
-	enum ovl_path_type type;
 
-	type = ovl_path_real(dentry, &realpath);
-	if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
+	if (ovl_open_need_copy_up(dentry, file_flags)) {
 		err = ovl_want_write(dentry);
 		if (!err) {
 			err = ovl_copy_up_flags(dentry, file_flags);
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 38fa75228c66e21d229695c5cd5df8730b6bc4d1..a290be449b8b69cb42f7a2a718a66ee939b37b94 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -302,7 +302,7 @@ int ovl_copy_up_start(struct dentry *dentry)
 	int err;
 
 	err = mutex_lock_interruptible(&oi->lock);
-	if (!err && ovl_dentry_upper(dentry)) {
+	if (!err && ovl_dentry_has_upper_alias(dentry)) {
 		err = 1; /* Already copied up */
 		mutex_unlock(&oi->lock);
 	}