Merge patch series "fs: tweak mntns iteration"
Christian Brauner <brauner@kernel.org> says: Make finding the last or first mount to start iterating the mount namespace from an O(1) operation and add selftests for iterating the mount table starting from the first and last mount. * patches from https://lore.kernel.org/r/20241215-vfs-6-14-mount-work-v1-0-fd55922c4af8@kernel.org: selftests: add listmount() iteration tests fs: cache first and last mount fs: kill MNT_ONRB Link: https://lore.kernel.org/r/20241215-vfs-6-14-mount-work-v1-0-fd55922c4af8@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>pull/1129/head
commit
87fc11ae7a
13
fs/mount.h
13
fs/mount.h
|
|
@ -8,7 +8,11 @@
|
|||
struct mnt_namespace {
|
||||
struct ns_common ns;
|
||||
struct mount * root;
|
||||
struct rb_root mounts; /* Protected by namespace_sem */
|
||||
struct {
|
||||
struct rb_root mounts; /* Protected by namespace_sem */
|
||||
struct rb_node *mnt_last_node; /* last (rightmost) mount in the rbtree */
|
||||
struct rb_node *mnt_first_node; /* first (leftmost) mount in the rbtree */
|
||||
};
|
||||
struct user_namespace *user_ns;
|
||||
struct ucounts *ucounts;
|
||||
u64 seq; /* Sequence number to prevent loops */
|
||||
|
|
@ -154,8 +158,13 @@ static inline bool mnt_ns_attached(const struct mount *mnt)
|
|||
|
||||
static inline void move_from_ns(struct mount *mnt, struct list_head *dt_list)
|
||||
{
|
||||
struct mnt_namespace *ns = mnt->mnt_ns;
|
||||
WARN_ON(!mnt_ns_attached(mnt));
|
||||
rb_erase(&mnt->mnt_node, &mnt->mnt_ns->mounts);
|
||||
if (ns->mnt_last_node == &mnt->mnt_node)
|
||||
ns->mnt_last_node = rb_prev(&mnt->mnt_node);
|
||||
if (ns->mnt_first_node == &mnt->mnt_node)
|
||||
ns->mnt_first_node = rb_next(&mnt->mnt_node);
|
||||
rb_erase(&mnt->mnt_node, &ns->mounts);
|
||||
RB_CLEAR_NODE(&mnt->mnt_node);
|
||||
list_add_tail(&mnt->mnt_list, dt_list);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1155,16 +1155,25 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
|
|||
{
|
||||
struct rb_node **link = &ns->mounts.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
bool mnt_first_node = true, mnt_last_node = true;
|
||||
|
||||
WARN_ON(mnt_ns_attached(mnt));
|
||||
mnt->mnt_ns = ns;
|
||||
while (*link) {
|
||||
parent = *link;
|
||||
if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique)
|
||||
if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique) {
|
||||
link = &parent->rb_left;
|
||||
else
|
||||
mnt_last_node = false;
|
||||
} else {
|
||||
link = &parent->rb_right;
|
||||
mnt_first_node = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mnt_last_node)
|
||||
ns->mnt_last_node = &mnt->mnt_node;
|
||||
if (mnt_first_node)
|
||||
ns->mnt_first_node = &mnt->mnt_node;
|
||||
rb_link_node(&mnt->mnt_node, parent, link);
|
||||
rb_insert_color(&mnt->mnt_node, &ns->mounts);
|
||||
}
|
||||
|
|
@ -5563,9 +5572,9 @@ static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
|
|||
|
||||
if (!last_mnt_id) {
|
||||
if (reverse)
|
||||
first = node_to_mount(rb_last(&ns->mounts));
|
||||
first = node_to_mount(ns->mnt_last_node);
|
||||
else
|
||||
first = node_to_mount(rb_first(&ns->mounts));
|
||||
first = node_to_mount(ns->mnt_first_node);
|
||||
} else {
|
||||
if (reverse)
|
||||
first = mnt_find_id_at_reverse(ns, last_mnt_id - 1);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES)
|
||||
TEST_GEN_PROGS := statmount_test statmount_test_ns
|
||||
TEST_GEN_PROGS := statmount_test statmount_test_ns listmount_test
|
||||
|
||||
include ../../lib.mk
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (c) 2024 Christian Brauner <brauner@kernel.org>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "statmount.h"
|
||||
#include "../../kselftest_harness.h"
|
||||
|
||||
#ifndef LISTMOUNT_REVERSE
|
||||
#define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */
|
||||
#endif
|
||||
|
||||
#define LISTMNT_BUFFER 10
|
||||
|
||||
/* Check that all mount ids are in increasing order. */
|
||||
TEST(listmount_forward)
|
||||
{
|
||||
uint64_t list[LISTMNT_BUFFER], last_mnt_id = 0;
|
||||
|
||||
for (;;) {
|
||||
ssize_t nr_mounts;
|
||||
|
||||
nr_mounts = listmount(LSMT_ROOT, 0, last_mnt_id,
|
||||
list, LISTMNT_BUFFER, 0);
|
||||
ASSERT_GE(nr_mounts, 0);
|
||||
if (nr_mounts == 0)
|
||||
break;
|
||||
|
||||
for (size_t cur = 0; cur < nr_mounts; cur++) {
|
||||
if (cur < nr_mounts - 1)
|
||||
ASSERT_LT(list[cur], list[cur + 1]);
|
||||
last_mnt_id = list[cur];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that all mount ids are in decreasing order. */
|
||||
TEST(listmount_backward)
|
||||
{
|
||||
uint64_t list[LISTMNT_BUFFER], last_mnt_id = 0;
|
||||
|
||||
for (;;) {
|
||||
ssize_t nr_mounts;
|
||||
|
||||
nr_mounts = listmount(LSMT_ROOT, 0, last_mnt_id,
|
||||
list, LISTMNT_BUFFER, LISTMOUNT_REVERSE);
|
||||
ASSERT_GE(nr_mounts, 0);
|
||||
if (nr_mounts == 0)
|
||||
break;
|
||||
|
||||
for (size_t cur = 0; cur < nr_mounts; cur++) {
|
||||
if (cur < nr_mounts - 1)
|
||||
ASSERT_GT(list[cur], list[cur + 1]);
|
||||
last_mnt_id = list[cur];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
Loading…
Reference in New Issue