Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+91 -7
+11 -2
fs/mount.h
··· 8 8 struct mnt_namespace { 9 9 struct ns_common ns; 10 10 struct mount * root; 11 - struct rb_root mounts; /* Protected by namespace_sem */ 11 + struct { 12 + struct rb_root mounts; /* Protected by namespace_sem */ 13 + struct rb_node *mnt_last_node; /* last (rightmost) mount in the rbtree */ 14 + struct rb_node *mnt_first_node; /* first (leftmost) mount in the rbtree */ 15 + }; 12 16 struct user_namespace *user_ns; 13 17 struct ucounts *ucounts; 14 18 u64 seq; /* Sequence number to prevent loops */ ··· 158 154 159 155 static inline void move_from_ns(struct mount *mnt, struct list_head *dt_list) 160 156 { 157 + struct mnt_namespace *ns = mnt->mnt_ns; 161 158 WARN_ON(!mnt_ns_attached(mnt)); 162 - rb_erase(&mnt->mnt_node, &mnt->mnt_ns->mounts); 159 + if (ns->mnt_last_node == &mnt->mnt_node) 160 + ns->mnt_last_node = rb_prev(&mnt->mnt_node); 161 + if (ns->mnt_first_node == &mnt->mnt_node) 162 + ns->mnt_first_node = rb_next(&mnt->mnt_node); 163 + rb_erase(&mnt->mnt_node, &ns->mounts); 163 164 RB_CLEAR_NODE(&mnt->mnt_node); 164 165 list_add_tail(&mnt->mnt_list, dt_list); 165 166 }
+13 -4
fs/namespace.c
··· 1155 1155 { 1156 1156 struct rb_node **link = &ns->mounts.rb_node; 1157 1157 struct rb_node *parent = NULL; 1158 + bool mnt_first_node = true, mnt_last_node = true; 1158 1159 1159 1160 WARN_ON(mnt_ns_attached(mnt)); 1160 1161 mnt->mnt_ns = ns; 1161 1162 while (*link) { 1162 1163 parent = *link; 1163 - if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique) 1164 + if (mnt->mnt_id_unique < node_to_mount(parent)->mnt_id_unique) { 1164 1165 link = &parent->rb_left; 1165 - else 1166 + mnt_last_node = false; 1167 + } else { 1166 1168 link = &parent->rb_right; 1169 + mnt_first_node = false; 1170 + } 1167 1171 } 1172 + 1173 + if (mnt_last_node) 1174 + ns->mnt_last_node = &mnt->mnt_node; 1175 + if (mnt_first_node) 1176 + ns->mnt_first_node = &mnt->mnt_node; 1168 1177 rb_link_node(&mnt->mnt_node, parent, link); 1169 1178 rb_insert_color(&mnt->mnt_node, &ns->mounts); 1170 1179 } ··· 5572 5563 5573 5564 if (!last_mnt_id) { 5574 5565 if (reverse) 5575 - first = node_to_mount(rb_last(&ns->mounts)); 5566 + first = node_to_mount(ns->mnt_last_node); 5576 5567 else 5577 - first = node_to_mount(rb_first(&ns->mounts)); 5568 + first = node_to_mount(ns->mnt_first_node); 5578 5569 } else { 5579 5570 if (reverse) 5580 5571 first = mnt_find_id_at_reverse(ns, last_mnt_id - 1);
+1 -1
tools/testing/selftests/filesystems/statmount/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-or-later 2 2 3 3 CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) 4 - TEST_GEN_PROGS := statmount_test statmount_test_ns 4 + TEST_GEN_PROGS := statmount_test statmount_test_ns listmount_test 5 5 6 6 include ../../lib.mk
+66
tools/testing/selftests/filesystems/statmount/listmount_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + // Copyright (c) 2024 Christian Brauner <brauner@kernel.org> 3 + 4 + #define _GNU_SOURCE 5 + #include <fcntl.h> 6 + #include <sched.h> 7 + #include <stdio.h> 8 + #include <string.h> 9 + #include <sys/stat.h> 10 + #include <sys/mount.h> 11 + #include <unistd.h> 12 + 13 + #include "statmount.h" 14 + #include "../../kselftest_harness.h" 15 + 16 + #ifndef LISTMOUNT_REVERSE 17 + #define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */ 18 + #endif 19 + 20 + #define LISTMNT_BUFFER 10 21 + 22 + /* Check that all mount ids are in increasing order. */ 23 + TEST(listmount_forward) 24 + { 25 + uint64_t list[LISTMNT_BUFFER], last_mnt_id = 0; 26 + 27 + for (;;) { 28 + ssize_t nr_mounts; 29 + 30 + nr_mounts = listmount(LSMT_ROOT, 0, last_mnt_id, 31 + list, LISTMNT_BUFFER, 0); 32 + ASSERT_GE(nr_mounts, 0); 33 + if (nr_mounts == 0) 34 + break; 35 + 36 + for (size_t cur = 0; cur < nr_mounts; cur++) { 37 + if (cur < nr_mounts - 1) 38 + ASSERT_LT(list[cur], list[cur + 1]); 39 + last_mnt_id = list[cur]; 40 + } 41 + } 42 + } 43 + 44 + /* Check that all mount ids are in decreasing order. */ 45 + TEST(listmount_backward) 46 + { 47 + uint64_t list[LISTMNT_BUFFER], last_mnt_id = 0; 48 + 49 + for (;;) { 50 + ssize_t nr_mounts; 51 + 52 + nr_mounts = listmount(LSMT_ROOT, 0, last_mnt_id, 53 + list, LISTMNT_BUFFER, LISTMOUNT_REVERSE); 54 + ASSERT_GE(nr_mounts, 0); 55 + if (nr_mounts == 0) 56 + break; 57 + 58 + for (size_t cur = 0; cur < nr_mounts; cur++) { 59 + if (cur < nr_mounts - 1) 60 + ASSERT_GT(list[cur], list[cur + 1]); 61 + last_mnt_id = list[cur]; 62 + } 63 + } 64 + } 65 + 66 + TEST_HARNESS_MAIN