return kzalloc(sizeof(struct fuse_forget_link), GFP_KERNEL_ACCOUNT);
 }
 
+static struct fuse_submount_lookup *fuse_alloc_submount_lookup(void)
+{
+       struct fuse_submount_lookup *sl;
+
+       sl = kzalloc(sizeof(struct fuse_submount_lookup), GFP_KERNEL_ACCOUNT);
+       if (!sl)
+               return NULL;
+       sl->forget = fuse_alloc_forget();
+       if (!sl->forget)
+               goto out_free;
+
+       return sl;
+
+out_free:
+       kfree(sl);
+       return NULL;
+}
+
 static struct inode *fuse_alloc_inode(struct super_block *sb)
 {
        struct fuse_inode *fi;
        fi->attr_version = 0;
        fi->orig_ino = 0;
        fi->state = 0;
+       fi->submount_lookup = NULL;
        mutex_init(&fi->mutex);
        spin_lock_init(&fi->lock);
        fi->forget = fuse_alloc_forget();
        kmem_cache_free(fuse_inode_cachep, fi);
 }
 
+static void fuse_cleanup_submount_lookup(struct fuse_conn *fc,
+                                        struct fuse_submount_lookup *sl)
+{
+       if (!refcount_dec_and_test(&sl->count))
+               return;
+
+       fuse_queue_forget(fc, sl->forget, sl->nodeid, 1);
+       sl->forget = NULL;
+       kfree(sl);
+}
+
 static void fuse_evict_inode(struct inode *inode)
 {
        struct fuse_inode *fi = get_fuse_inode(inode);
                                          fi->nlookup);
                        fi->forget = NULL;
                }
+
+               if (fi->submount_lookup) {
+                       fuse_cleanup_submount_lookup(fc, fi->submount_lookup);
+                       fi->submount_lookup = NULL;
+               }
        }
        if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
                WARN_ON(!list_empty(&fi->write_files));
                fuse_dax_dontcache(inode, attr->flags);
 }
 
+static void fuse_init_submount_lookup(struct fuse_submount_lookup *sl,
+                                     u64 nodeid)
+{
+       sl->nodeid = nodeid;
+       refcount_set(&sl->count, 1);
+}
+
 static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
                            struct fuse_conn *fc)
 {
         */
        if (fc->auto_submounts && (attr->flags & FUSE_ATTR_SUBMOUNT) &&
            S_ISDIR(attr->mode)) {
+               struct fuse_inode *fi;
+
                inode = new_inode(sb);
                if (!inode)
                        return NULL;
 
                fuse_init_inode(inode, attr, fc);
-               get_fuse_inode(inode)->nodeid = nodeid;
+               fi = get_fuse_inode(inode);
+               fi->nodeid = nodeid;
+               fi->submount_lookup = fuse_alloc_submount_lookup();
+               if (!fi->submount_lookup) {
+                       iput(inode);
+                       return NULL;
+               }
+               /* Sets nlookup = 1 on fi->submount_lookup->nlookup */
+               fuse_init_submount_lookup(fi->submount_lookup, nodeid);
                inode->i_flags |= S_AUTOMOUNT;
                goto done;
        }
                iput(inode);
                goto retry;
        }
-done:
        fi = get_fuse_inode(inode);
        spin_lock(&fi->lock);
        fi->nlookup++;
        spin_unlock(&fi->lock);
+done:
        fuse_change_attributes(inode, attr, NULL, attr_valid, attr_version);
 
        return inode;
        struct super_block *parent_sb = parent_fi->inode.i_sb;
        struct fuse_attr root_attr;
        struct inode *root;
+       struct fuse_submount_lookup *sl;
+       struct fuse_inode *fi;
 
        fuse_sb_defaults(sb);
        fm->sb = sb;
         * its nlookup should not be incremented.  fuse_iget() does
         * that, though, so undo it here.
         */
-       get_fuse_inode(root)->nlookup--;
+       fi = get_fuse_inode(root);
+       fi->nlookup--;
+
        sb->s_d_op = &fuse_dentry_operations;
        sb->s_root = d_make_root(root);
        if (!sb->s_root)
                return -ENOMEM;
 
+       /*
+        * Grab the parent's submount_lookup pointer and take a
+        * reference on the shared nlookup from the parent.  This is to
+        * prevent the last forget for this nodeid from getting
+        * triggered until all users have finished with it.
+        */
+       sl = parent_fi->submount_lookup;
+       WARN_ON(!sl);
+       if (sl) {
+               refcount_inc(&sl->count);
+               fi->submount_lookup = sl;
+       }
+
        return 0;
 }