From patchwork Mon Jul 11 03:37:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Kent X-Patchwork-Id: 12912772 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B8C46C433EF for ; Mon, 11 Jul 2022 03:37:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229682AbiGKDhq (ORCPT ); Sun, 10 Jul 2022 23:37:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38524 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229450AbiGKDho (ORCPT ); Sun, 10 Jul 2022 23:37:44 -0400 Received: from smtp01.aussiebb.com.au (smtp01.aussiebb.com.au [IPv6:2403:5800:3:25::1001]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4656762D3; Sun, 10 Jul 2022 20:37:43 -0700 (PDT) Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp01.aussiebb.com.au (Postfix) with ESMTP id DD50D10052F; Mon, 11 Jul 2022 13:37:41 +1000 (AEST) X-Virus-Scanned: Debian amavisd-new at smtp01.aussiebb.com.au Received: from smtp01.aussiebb.com.au ([127.0.0.1]) by localhost (smtp01.aussiebb.com.au [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id mhwLkaRdPbHE; Mon, 11 Jul 2022 13:37:41 +1000 (AEST) Received: by smtp01.aussiebb.com.au (Postfix, from userid 116) id CAB3910052E; Mon, 11 Jul 2022 13:37:41 +1000 (AEST) Received: from donald.themaw.net (180-150-90-198.b4965a.per.nbn.aussiebb.net [180.150.90.198]) by smtp01.aussiebb.com.au (Postfix) with ESMTP id 2647710051D; Mon, 11 Jul 2022 13:37:41 +1000 (AEST) Subject: [PATCH 1/3] vfs: track count of child mounts From: Ian Kent To: Al Viro Cc: Andrew Morton , David Howells , Miklos Szeredi , linux-fsdevel , Kernel Mailing List Date: Mon, 11 Jul 2022 11:37:40 +0800 Message-ID: <165751066075.210556.17270883735094115327.stgit@donald.themaw.net> In-Reply-To: <165751053430.210556.5634228273667507299.stgit@donald.themaw.net> References: <165751053430.210556.5634228273667507299.stgit@donald.themaw.net> User-Agent: StGit/1.1 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org While the total reference count of a mount is mostly all that's needed the reference count corresponding to the mounts only is occassionally also needed (for example, autofs checking if a tree of mounts can be expired). To make this reference count avaialble with minimal changes add a counter to track the number of child mounts under a given mount. This count can then be used to calculate the mounts only reference count. Signed-off-by: Ian Kent --- fs/mount.h | 1 + fs/namespace.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/fs/mount.h b/fs/mount.h index 0b6e08cf8afb..3f0f62912463 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -52,6 +52,7 @@ struct mount { int mnt_writers; #endif struct list_head mnt_mounts; /* list of children, anchored here */ + unsigned int mnt_mounts_cnt; /* count of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ struct list_head mnt_instance; /* mount instance on sb->s_mounts */ const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ diff --git a/fs/namespace.c b/fs/namespace.c index e6a7e769d25d..3c1ee5b5bb69 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -882,6 +882,8 @@ static struct mountpoint *unhash_mnt(struct mount *mnt) struct mountpoint *mp; mnt->mnt_parent = mnt; mnt->mnt_mountpoint = mnt->mnt.mnt_root; + if (!list_empty(&mnt->mnt_child)) + mnt->mnt_parent->mnt_mounts_cnt--; list_del_init(&mnt->mnt_child); hlist_del_init_rcu(&mnt->mnt_hash); hlist_del_init(&mnt->mnt_mp_list); @@ -918,6 +920,7 @@ static void __attach_mnt(struct mount *mnt, struct mount *parent) hlist_add_head_rcu(&mnt->mnt_hash, m_hash(&parent->mnt, mnt->mnt_mountpoint)); list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); + parent->mnt_mounts_cnt++; } /* @@ -936,6 +939,8 @@ void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct m struct mountpoint *old_mp = mnt->mnt_mp; struct mount *old_parent = mnt->mnt_parent; + if (!list_empty(&mnt->mnt_child)) + mnt->mnt_parent->mnt_mounts_cnt--; list_del_init(&mnt->mnt_child); hlist_del_init(&mnt->mnt_mp_list); hlist_del_init_rcu(&mnt->mnt_hash); @@ -1562,6 +1567,8 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) /* Hide the mounts from mnt_mounts */ list_for_each_entry(p, &tmp_list, mnt_list) { + if (!list_empty(&p->mnt_child)) + p->mnt_parent->mnt_mounts_cnt--; list_del_init(&p->mnt_child); } @@ -1590,6 +1597,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) if (!disconnect) { /* Don't forget about p */ list_add_tail(&p->mnt_child, &p->mnt_parent->mnt_mounts); + p->mnt_parent->mnt_mounts_cnt++; } else { umount_mnt(p); } From patchwork Mon Jul 11 03:37:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Kent X-Patchwork-Id: 12912773 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20655C433EF for ; Mon, 11 Jul 2022 03:37:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229697AbiGKDh5 (ORCPT ); Sun, 10 Jul 2022 23:37:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38862 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229450AbiGKDhz (ORCPT ); Sun, 10 Jul 2022 23:37:55 -0400 Received: from smtp01.aussiebb.com.au (smtp01.aussiebb.com.au [IPv6:2403:5800:3:25::1001]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E8E511055F; Sun, 10 Jul 2022 20:37:48 -0700 (PDT) Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp01.aussiebb.com.au (Postfix) with ESMTP id 6C0A410051D; Mon, 11 Jul 2022 13:37:47 +1000 (AEST) X-Virus-Scanned: Debian amavisd-new at smtp01.aussiebb.com.au Received: from smtp01.aussiebb.com.au ([127.0.0.1]) by localhost (smtp01.aussiebb.com.au [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id PYpA9ReO_2bm; Mon, 11 Jul 2022 13:37:47 +1000 (AEST) Received: by smtp01.aussiebb.com.au (Postfix, from userid 116) id 60D90100518; Mon, 11 Jul 2022 13:37:47 +1000 (AEST) Received: from donald.themaw.net (180-150-90-198.b4965a.per.nbn.aussiebb.net [180.150.90.198]) by smtp01.aussiebb.com.au (Postfix) with ESMTP id DDE96100507; Mon, 11 Jul 2022 13:37:46 +1000 (AEST) Subject: [PATCH 2/3] vfs: add propagate_mount_tree_busy() helper From: Ian Kent To: Al Viro Cc: Andrew Morton , David Howells , Miklos Szeredi , linux-fsdevel , Kernel Mailing List Date: Mon, 11 Jul 2022 11:37:46 +0800 Message-ID: <165751066658.210556.1326573473015621909.stgit@donald.themaw.net> In-Reply-To: <165751053430.210556.5634228273667507299.stgit@donald.themaw.net> References: <165751053430.210556.5634228273667507299.stgit@donald.themaw.net> User-Agent: StGit/1.1 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Now that child mounts are tracked the expire checks need to be able to use this to check if a mount is in use. Currently when checking a mount for expiration may_umount_tree() checks only if the passed in mount is in use. This leads to false positive callbacks to the automount daemon to umount the mount which fail if any propagated mounts are in use. To avoid these unnecessary callbacks may_umount_tree() needs to check propagated mounts in a similar way to may_umount(). Add a helper that can do this. Signed-off-by: Ian Kent --- fs/pnode.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/pnode.h | 3 +++ 2 files changed, 64 insertions(+) diff --git a/fs/pnode.c b/fs/pnode.c index 1106137c747a..e2a906db4324 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -404,6 +404,67 @@ int propagate_mount_busy(struct mount *mnt, int refcnt) return 0; } +static int do_mount_in_use_check(struct mount *mnt, int cnt) +{ + struct mount *topper; + + /* Is there exactly one mount on the child that covers + * it completely? + */ + topper = find_topper(mnt); + if (topper) { + int topper_cnt = topper->mnt_mounts_cnt + 1; + + /* Open file or pwd within singular mount? */ + if (do_refcount_check(topper, topper_cnt)) + return 1; + /* Account for singular mount on parent */ + cnt += 1; + } + + if (do_refcount_check(mnt, cnt)) + return 1; + + return 0; +} + +/* + * Check if the mount tree at 'mnt' is in use or any of its + * propogated mounts are in use. + * @mnt: the mount to be checked + * @adjust: caller holds an additional reference to mount + * Check if mnt or any of its propogated mounts have a reference + * count greater than the minimum reference count (ie. are in use). + */ +int propagate_mount_tree_busy(struct mount *mnt, unsigned int flags) +{ + struct mount *parent = mnt->mnt_parent; + struct mount *m, *child; + unsigned int referenced = flags & TREE_BUSY_REFERENCED; + int cnt; + + /* Check for an elevated refcount on the passed in mount. + * If adjust is true the caller holds a reference to the + * passed in mount. + */ + cnt = mnt->mnt_mounts_cnt + (referenced ? 2 : 1); + if (do_mount_in_use_check(mnt, cnt)) + return 1; + + for (m = propagation_next(parent, parent); m; + m = propagation_next(m, parent)) { + child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); + if (!child) + continue; + + cnt = child->mnt_mounts_cnt + 1; + + if (do_mount_in_use_check(child, cnt)) + return 1; + } + return 0; +} + /* * Clear MNT_LOCKED when it can be shown to be safe. * diff --git a/fs/pnode.h b/fs/pnode.h index 988f1aa9b02a..d7b9dddb257b 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -30,6 +30,8 @@ #define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) +#define TREE_BUSY_REFERENCED 0x01 + static inline void set_mnt_shared(struct mount *mnt) { mnt->mnt.mnt_flags &= ~MNT_SHARED_MASK; @@ -41,6 +43,7 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *, struct hlist_head *); int propagate_umount(struct list_head *); int propagate_mount_busy(struct mount *, int); +int propagate_mount_tree_busy(struct mount *, unsigned int); void propagate_mount_unlock(struct mount *); void mnt_release_group_id(struct mount *); int get_dominating_id(struct mount *mnt, const struct path *root); From patchwork Mon Jul 11 03:37:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Kent X-Patchwork-Id: 12912774 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0C4E7C43334 for ; Mon, 11 Jul 2022 03:38:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229718AbiGKDiE (ORCPT ); Sun, 10 Jul 2022 23:38:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39010 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229693AbiGKDh6 (ORCPT ); Sun, 10 Jul 2022 23:37:58 -0400 Received: from smtp01.aussiebb.com.au (smtp01.aussiebb.com.au [121.200.0.92]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2075518B09; Sun, 10 Jul 2022 20:37:56 -0700 (PDT) Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp01.aussiebb.com.au (Postfix) with ESMTP id 9908910052F; Mon, 11 Jul 2022 13:37:53 +1000 (AEST) X-Virus-Scanned: Debian amavisd-new at smtp01.aussiebb.com.au Received: from smtp01.aussiebb.com.au ([127.0.0.1]) by localhost (smtp01.aussiebb.com.au [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2jiGe7T7hu11; Mon, 11 Jul 2022 13:37:53 +1000 (AEST) Received: by smtp01.aussiebb.com.au (Postfix, from userid 116) id 8EF2D100507; Mon, 11 Jul 2022 13:37:53 +1000 (AEST) Received: from donald.themaw.net (180-150-90-198.b4965a.per.nbn.aussiebb.net [180.150.90.198]) by smtp01.aussiebb.com.au (Postfix) with ESMTP id D0BDF100507; Mon, 11 Jul 2022 13:37:52 +1000 (AEST) Subject: [PATCH 3/3] vfs: make may_umount_tree() mount namespace aware From: Ian Kent To: Al Viro Cc: Andrew Morton , David Howells , Miklos Szeredi , linux-fsdevel , Kernel Mailing List Date: Mon, 11 Jul 2022 11:37:52 +0800 Message-ID: <165751067234.210556.2619133379044425664.stgit@donald.themaw.net> In-Reply-To: <165751053430.210556.5634228273667507299.stgit@donald.themaw.net> References: <165751053430.210556.5634228273667507299.stgit@donald.themaw.net> User-Agent: StGit/1.1 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Change may_umount_tree() (and the associated autofs code) to use the propagate_mount_tree_busy() helper so it also checks if propagated mounts are busy. This avoids unnecessary umount requests being sent to the automount daemon when a mount in another mount namespace is in use when the expire check is done. Signed-off-by: Ian Kent --- fs/autofs/expire.c | 14 ++++++++++++-- fs/namespace.c | 32 ++++++++++++++++++++------------ fs/pnode.h | 2 -- include/linux/mount.h | 5 ++++- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c index 038b3d2d9f57..1352c454cf1d 100644 --- a/fs/autofs/expire.c +++ b/fs/autofs/expire.c @@ -31,10 +31,13 @@ static int autofs_mount_busy(struct vfsmount *mnt, { struct dentry *top = dentry; struct path path = {.mnt = mnt, .dentry = dentry}; + unsigned int flags; int status = 1; pr_debug("dentry %p %pd\n", dentry, dentry); + /* A reference to the mount is held. */ + flags = TREE_BUSY_REFERENCED; path_get(&path); if (!follow_down_one(&path)) @@ -55,7 +58,7 @@ static int autofs_mount_busy(struct vfsmount *mnt, } /* Update the expiry counter if fs is busy */ - if (!may_umount_tree(path.mnt)) { + if (!may_umount_tree(path.mnt, flags)) { struct autofs_info *ino; ino = autofs_dentry_ino(top); @@ -152,14 +155,21 @@ static int autofs_direct_busy(struct vfsmount *mnt, unsigned long timeout, unsigned int how) { + unsigned int flags; + pr_debug("top %p %pd\n", top, top); /* Forced expire, user space handles busy mounts */ if (how & AUTOFS_EXP_FORCED) return 0; + /* A mounted direct mount will have an open file handle + * associated with it so we need TREE_BUSY_REFERENCED. + */ + flags = TREE_BUSY_REFERENCED; + /* If it's busy update the expiry counters */ - if (!may_umount_tree(mnt)) { + if (!may_umount_tree(mnt, flags)) { struct autofs_info *ino; ino = autofs_dentry_ino(top); diff --git a/fs/namespace.c b/fs/namespace.c index 3c1ee5b5bb69..bdcb55e821f4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1431,28 +1431,36 @@ void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor) * open files, pwds, chroots or sub mounts that are * busy. */ -int may_umount_tree(struct vfsmount *m) +int may_umount_tree(struct vfsmount *m, unsigned int flags) { struct mount *mnt = real_mount(m); - int actual_refs = 0; - int minimum_refs = 0; struct mount *p; + int ret = 1; + BUG_ON(!m); - /* write lock needed for mnt_get_count */ + down_read(&namespace_sem); lock_mount_hash(); - for (p = mnt; p; p = next_mnt(p, mnt)) { - actual_refs += mnt_get_count(p); - minimum_refs += 2; + if (propagate_mount_tree_busy(mnt, flags)) { + ret = 0; + goto out; } + /* Only the passed in mount will have a reference held by + * the caller. + */ + flags &= ~TREE_BUSY_REFERENCED; + for (p = next_mnt(mnt, mnt); p; p = next_mnt(p, mnt)) { + if (propagate_mount_tree_busy(p, flags)) { + ret = 0; + break; + } + } +out: unlock_mount_hash(); + up_read(&namespace_sem); - if (actual_refs > minimum_refs) - return 0; - - return 1; + return ret; } - EXPORT_SYMBOL(may_umount_tree); /** diff --git a/fs/pnode.h b/fs/pnode.h index d7b9dddb257b..12c3ab5962a0 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -30,8 +30,6 @@ #define CL_COPY_ALL (CL_COPY_UNBINDABLE | CL_COPY_MNT_NS_FILE) -#define TREE_BUSY_REFERENCED 0x01 - static inline void set_mnt_shared(struct mount *mnt) { mnt->mnt.mnt_flags &= ~MNT_SHARED_MASK; diff --git a/include/linux/mount.h b/include/linux/mount.h index 55a4abaf6715..c21d74ea3d85 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -112,7 +112,10 @@ extern bool our_mnt(struct vfsmount *mnt); extern struct vfsmount *kern_mount(struct file_system_type *); extern void kern_unmount(struct vfsmount *mnt); -extern int may_umount_tree(struct vfsmount *); + +#define TREE_BUSY_REFERENCED 0x01 + +extern int may_umount_tree(struct vfsmount *m, unsigned int flags); extern int may_umount(struct vfsmount *); extern long do_mount(const char *, const char __user *, const char *, unsigned long, void *);