struct dentry *upperdir = ovl_dentry_upper(parent);
        struct inode *udir = d_inode(upperdir);
 
+       err = ovl_set_nlink_lower(dentry);
+       if (err)
+               return err;
+
        inode_lock_nested(udir, I_MUTEX_PARENT);
        upper = lookup_one_len(dentry->d_name.name, upperdir,
                               dentry->d_name.len);
                        ovl_dentry_set_upper_alias(dentry);
        }
        inode_unlock(udir);
+       ovl_set_nlink_upper(dentry);
 
        return err;
 }
 
                    struct dentry *new)
 {
        int err;
+       bool locked = false;
        struct inode *inode;
 
        err = ovl_want_write(old);
        if (err)
                goto out_drop_write;
 
+       err = ovl_nlink_start(old, &locked);
+       if (err)
+               goto out_drop_write;
+
        inode = d_inode(old);
        ihold(inode);
 
        if (err)
                iput(inode);
 
+       ovl_nlink_end(old, locked);
 out_drop_write:
        ovl_drop_write(old);
 out:
 
 static int ovl_do_remove(struct dentry *dentry, bool is_dir)
 {
-       enum ovl_path_type type;
        int err;
+       bool locked = false;
        const struct cred *old_cred;
 
        err = ovl_want_write(dentry);
        if (err)
                goto out_drop_write;
 
-       type = ovl_path_type(dentry);
+       err = ovl_nlink_start(dentry, &locked);
+       if (err)
+               goto out_drop_write;
 
        old_cred = ovl_override_creds(dentry->d_sb);
        if (!ovl_lower_positive(dentry))
                else
                        drop_nlink(dentry->d_inode);
        }
+       ovl_nlink_end(dentry, locked);
 out_drop_write:
        ovl_drop_write(dentry);
 out:
                      unsigned int flags)
 {
        int err;
+       bool locked = false;
        struct dentry *old_upperdir;
        struct dentry *new_upperdir;
        struct dentry *olddentry;
                err = ovl_copy_up(new);
                if (err)
                        goto out_drop_write;
+       } else {
+               err = ovl_nlink_start(new, &locked);
+               if (err)
+                       goto out_drop_write;
        }
 
        old_cred = ovl_override_creds(old->d_sb);
        unlock_rename(new_upperdir, old_upperdir);
 out_revert_creds:
        revert_creds(old_cred);
+       ovl_nlink_end(new, locked);
 out_drop_write:
        ovl_drop_write(old);
 out:
 
 #include <linux/cred.h>
 #include <linux/xattr.h>
 #include <linux/posix_acl.h>
+#include <linux/ratelimit.h>
 #include "overlayfs.h"
 
 int ovl_setattr(struct dentry *dentry, struct iattr *attr)
        if (is_dir && OVL_TYPE_MERGE(type))
                stat->nlink = 1;
 
+       /*
+        * Return the overlay inode nlinks for indexed upper inodes.
+        * Overlay inode nlink counts the union of the upper hardlinks
+        * and non-covered lower hardlinks. It does not include the upper
+        * index hardlink.
+        */
+       if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry)))
+               stat->nlink = dentry->d_inode->i_nlink;
+
 out:
        revert_creds(old_cred);
 
        }
 }
 
+/*
+ * With inodes index enabled, an overlay inode nlink counts the union of upper
+ * hardlinks and non-covered lower hardlinks. During the lifetime of a non-pure
+ * upper inode, the following nlink modifying operations can happen:
+ *
+ * 1. Lower hardlink copy up
+ * 2. Upper hardlink created, unlinked or renamed over
+ * 3. Lower hardlink whiteout or renamed over
+ *
+ * For the first, copy up case, the union nlink does not change, whether the
+ * operation succeeds or fails, but the upper inode nlink may change.
+ * Therefore, before copy up, we store the union nlink value relative to the
+ * lower inode nlink in the index inode xattr trusted.overlay.nlink.
+ *
+ * For the second, upper hardlink case, the union nlink should be incremented
+ * or decremented IFF the operation succeeds, aligned with nlink change of the
+ * upper inode. Therefore, before link/unlink/rename, we store the union nlink
+ * value relative to the upper inode nlink in the index inode.
+ *
+ * For the last, lower cover up case, we simplify things by preceding the
+ * whiteout or cover up with copy up. This makes sure that there is an index
+ * upper inode where the nlink xattr can be stored before the copied up upper
+ * entry is unlink.
+ */
+#define OVL_NLINK_ADD_UPPER    (1 << 0)
+
+/*
+ * On-disk format for indexed nlink:
+ *
+ * nlink relative to the upper inode - "U[+-]NUM"
+ * nlink relative to the lower inode - "L[+-]NUM"
+ */
+
+static int ovl_set_nlink_common(struct dentry *dentry,
+                               struct dentry *realdentry, const char *format)
+{
+       struct inode *inode = d_inode(dentry);
+       struct inode *realinode = d_inode(realdentry);
+       char buf[13];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), format,
+                      (int) (inode->i_nlink - realinode->i_nlink));
+
+       return ovl_do_setxattr(ovl_dentry_upper(dentry),
+                              OVL_XATTR_NLINK, buf, len, 0);
+}
+
+int ovl_set_nlink_upper(struct dentry *dentry)
+{
+       return ovl_set_nlink_common(dentry, ovl_dentry_upper(dentry), "U%+i");
+}
+
+int ovl_set_nlink_lower(struct dentry *dentry)
+{
+       return ovl_set_nlink_common(dentry, ovl_dentry_lower(dentry), "L%+i");
+}
+
+static unsigned int ovl_get_nlink(struct dentry *lowerdentry,
+                                 struct dentry *upperdentry,
+                                 unsigned int fallback)
+{
+       int nlink_diff;
+       int nlink;
+       char buf[13];
+       int err;
+
+       if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
+               return fallback;
+
+       err = vfs_getxattr(upperdentry, OVL_XATTR_NLINK, &buf, sizeof(buf) - 1);
+       if (err < 0)
+               goto fail;
+
+       buf[err] = '\0';
+       if ((buf[0] != 'L' && buf[0] != 'U') ||
+           (buf[1] != '+' && buf[1] != '-'))
+               goto fail;
+
+       err = kstrtoint(buf + 1, 10, &nlink_diff);
+       if (err < 0)
+               goto fail;
+
+       nlink = d_inode(buf[0] == 'L' ? lowerdentry : upperdentry)->i_nlink;
+       nlink += nlink_diff;
+
+       if (nlink <= 0)
+               goto fail;
+
+       return nlink;
+
+fail:
+       pr_warn_ratelimited("overlayfs: failed to get index nlink (%pd2, err=%i)\n",
+                           upperdentry, err);
+       return fallback;
+}
+
 struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
 {
        struct inode *inode;
        if (!S_ISDIR(realinode->i_mode) &&
            (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
                struct inode *key = d_inode(lowerdentry ?: upperdentry);
+               unsigned int nlink;
 
                inode = iget5_locked(dentry->d_sb, (unsigned long) key,
                                     ovl_inode_test, ovl_inode_set, key);
                        goto out;
                }
 
-               set_nlink(inode, realinode->i_nlink);
+               nlink = ovl_get_nlink(lowerdentry, upperdentry,
+                                     realinode->i_nlink);
+               set_nlink(inode, nlink);
        } else {
                inode = new_inode(dentry->d_sb);
                if (!inode)
 
 #define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
 #define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
 #define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
+#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
 
 enum ovl_flag {
        OVL_IMPURE,
 bool ovl_test_flag(unsigned long flag, struct inode *inode);
 bool ovl_inuse_trylock(struct dentry *dentry);
 void ovl_inuse_unlock(struct dentry *dentry);
+int ovl_nlink_start(struct dentry *dentry, bool *locked);
+void ovl_nlink_end(struct dentry *dentry, bool locked);
 
 static inline bool ovl_is_impuredir(struct dentry *dentry)
 {
                         struct path *lowerstack, unsigned int numlower);
 
 /* inode.c */
+int ovl_set_nlink_upper(struct dentry *dentry);
+int ovl_set_nlink_lower(struct dentry *dentry);
 int ovl_setattr(struct dentry *dentry, struct iattr *attr);
 int ovl_getattr(const struct path *path, struct kstat *stat,
                u32 request_mask, unsigned int flags);
 
                spin_unlock(&inode->i_lock);
        }
 }
+
+/*
+ * Operations that change overlay inode and upper inode nlink need to be
+ * synchronized with copy up for persistent nlink accounting.
+ */
+int ovl_nlink_start(struct dentry *dentry, bool *locked)
+{
+       struct ovl_inode *oi = OVL_I(d_inode(dentry));
+       const struct cred *old_cred;
+       int err;
+
+       if (!d_inode(dentry) || d_is_dir(dentry))
+               return 0;
+
+       /*
+        * With inodes index is enabled, we store the union overlay nlink
+        * in an xattr on the index inode. When whiting out lower hardlinks
+        * we need to decrement the overlay persistent nlink, but before the
+        * first copy up, we have no upper index inode to store the xattr.
+        *
+        * As a workaround, before whiteout/rename over of a lower hardlink,
+        * copy up to create the upper index. Creating the upper index will
+        * initialize the overlay nlink, so it could be dropped if unlink
+        * or rename succeeds.
+        *
+        * TODO: implement metadata only index copy up when called with
+        *       ovl_copy_up_flags(dentry, O_PATH).
+        */
+       if (ovl_indexdir(dentry->d_sb) && !ovl_dentry_has_upper_alias(dentry) &&
+           d_inode(ovl_dentry_lower(dentry))->i_nlink > 1) {
+               err = ovl_copy_up(dentry);
+               if (err)
+                       return err;
+       }
+
+       err = mutex_lock_interruptible(&oi->lock);
+       if (err)
+               return err;
+
+       if (!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
+               goto out;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       /*
+        * The overlay inode nlink should be incremented/decremented IFF the
+        * upper operation succeeds, along with nlink change of upper inode.
+        * Therefore, before link/unlink/rename, we store the union nlink
+        * value relative to the upper inode nlink in an upper inode xattr.
+        */
+       err = ovl_set_nlink_upper(dentry);
+       revert_creds(old_cred);
+
+out:
+       if (err)
+               mutex_unlock(&oi->lock);
+       else
+               *locked = true;
+
+       return err;
+}
+
+void ovl_nlink_end(struct dentry *dentry, bool locked)
+{
+       if (locked)
+               mutex_unlock(&OVL_I(d_inode(dentry))->lock);
+}