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

Configure Feed

Select the types of activity you want to include in your feed.

ucount: use RCU for ucounts lookups

The ucounts element is looked up under ucounts_lock. This can be
optimized by using RCU for a lockless lookup and return and element if the
reference can be obtained.

Replace hlist_head with hlist_nulls_head which is RCU compatible. Let
find_ucounts() search for the required item within a RCU section and
return the item if a reference could be obtained. This means
alloc_ucounts() will always return an element (unless the memory
allocation failed). Let put_ucounts() RCU free the element if the
reference counter dropped to zero.

Link: https://lkml.kernel.org/r/20250203150525.456525-4-bigeasy@linutronix.de
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Joel Fernandes <joel@joelfernandes.org>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Lai jiangshan <jiangshanlai@gmail.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Mengen Sun <mengensun@tencent.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: "Uladzislau Rezki (Sony)" <urezki@gmail.com>
Cc: YueHong Wu <yuehongwu@tencent.com>
Cc: Zqiang <qiang.zhang1211@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Sebastian Andrzej Siewior and committed by
Andrew Morton
5f01a22c 328152e6

+43 -36
+3 -1
include/linux/user_namespace.h
··· 5 5 #include <linux/kref.h> 6 6 #include <linux/nsproxy.h> 7 7 #include <linux/ns_common.h> 8 + #include <linux/rculist_nulls.h> 8 9 #include <linux/sched.h> 9 10 #include <linux/workqueue.h> 10 11 #include <linux/rwsem.h> ··· 116 115 } __randomize_layout; 117 116 118 117 struct ucounts { 119 - struct hlist_node node; 118 + struct hlist_nulls_node node; 120 119 struct user_namespace *ns; 121 120 kuid_t uid; 121 + struct rcu_head rcu; 122 122 atomic_t count; 123 123 atomic_long_t ucount[UCOUNT_COUNTS]; 124 124 atomic_long_t rlimit[UCOUNT_RLIMIT_COUNTS];
+40 -35
kernel/ucount.c
··· 15 15 }; 16 16 17 17 #define UCOUNTS_HASHTABLE_BITS 10 18 - static struct hlist_head ucounts_hashtable[(1 << UCOUNTS_HASHTABLE_BITS)]; 18 + #define UCOUNTS_HASHTABLE_ENTRIES (1 << UCOUNTS_HASHTABLE_BITS) 19 + static struct hlist_nulls_head ucounts_hashtable[UCOUNTS_HASHTABLE_ENTRIES] = { 20 + [0 ... UCOUNTS_HASHTABLE_ENTRIES - 1] = HLIST_NULLS_HEAD_INIT(0) 21 + }; 19 22 static DEFINE_SPINLOCK(ucounts_lock); 20 23 21 24 #define ucounts_hashfn(ns, uid) \ ··· 26 23 UCOUNTS_HASHTABLE_BITS) 27 24 #define ucounts_hashentry(ns, uid) \ 28 25 (ucounts_hashtable + ucounts_hashfn(ns, uid)) 29 - 30 26 31 27 #ifdef CONFIG_SYSCTL 32 28 static struct ctl_table_set * ··· 129 127 #endif 130 128 } 131 129 132 - static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid, struct hlist_head *hashent) 130 + static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid, 131 + struct hlist_nulls_head *hashent) 133 132 { 134 133 struct ucounts *ucounts; 134 + struct hlist_nulls_node *pos; 135 135 136 - hlist_for_each_entry(ucounts, hashent, node) { 137 - if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns)) 138 - return ucounts; 136 + guard(rcu)(); 137 + hlist_nulls_for_each_entry_rcu(ucounts, pos, hashent, node) { 138 + if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns)) { 139 + if (atomic_inc_not_zero(&ucounts->count)) 140 + return ucounts; 141 + } 139 142 } 140 143 return NULL; 141 144 } 142 145 143 146 static void hlist_add_ucounts(struct ucounts *ucounts) 144 147 { 145 - struct hlist_head *hashent = ucounts_hashentry(ucounts->ns, ucounts->uid); 148 + struct hlist_nulls_head *hashent = ucounts_hashentry(ucounts->ns, ucounts->uid); 149 + 146 150 spin_lock_irq(&ucounts_lock); 147 - hlist_add_head(&ucounts->node, hashent); 151 + hlist_nulls_add_head_rcu(&ucounts->node, hashent); 148 152 spin_unlock_irq(&ucounts_lock); 149 153 } 150 154 ··· 163 155 164 156 struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid) 165 157 { 166 - struct hlist_head *hashent = ucounts_hashentry(ns, uid); 167 - struct ucounts *ucounts, *new = NULL; 158 + struct hlist_nulls_head *hashent = ucounts_hashentry(ns, uid); 159 + struct ucounts *ucounts, *new; 160 + 161 + ucounts = find_ucounts(ns, uid, hashent); 162 + if (ucounts) 163 + return ucounts; 164 + 165 + new = kzalloc(sizeof(*new), GFP_KERNEL); 166 + if (!new) 167 + return NULL; 168 + 169 + new->ns = ns; 170 + new->uid = uid; 171 + atomic_set(&new->count, 1); 168 172 169 173 spin_lock_irq(&ucounts_lock); 170 174 ucounts = find_ucounts(ns, uid, hashent); 171 - if (!ucounts) { 175 + if (ucounts) { 172 176 spin_unlock_irq(&ucounts_lock); 173 - 174 - new = kzalloc(sizeof(*new), GFP_KERNEL); 175 - if (!new) 176 - return NULL; 177 - 178 - new->ns = ns; 179 - new->uid = uid; 180 - atomic_set(&new->count, 1); 181 - 182 - spin_lock_irq(&ucounts_lock); 183 - ucounts = find_ucounts(ns, uid, hashent); 184 - if (!ucounts) { 185 - hlist_add_head(&new->node, hashent); 186 - get_user_ns(new->ns); 187 - spin_unlock_irq(&ucounts_lock); 188 - return new; 189 - } 177 + kfree(new); 178 + return ucounts; 190 179 } 191 - if (!atomic_inc_not_zero(&ucounts->count)) 192 - ucounts = NULL; 193 - spin_unlock_irq(&ucounts_lock); 194 - kfree(new); 195 180 196 - return ucounts; 181 + hlist_nulls_add_head_rcu(&new->node, hashent); 182 + get_user_ns(new->ns); 183 + spin_unlock_irq(&ucounts_lock); 184 + return new; 197 185 } 198 186 199 187 void put_ucounts(struct ucounts *ucounts) ··· 197 193 unsigned long flags; 198 194 199 195 if (atomic_dec_and_lock_irqsave(&ucounts->count, &ucounts_lock, flags)) { 200 - hlist_del_init(&ucounts->node); 196 + hlist_nulls_del_rcu(&ucounts->node); 201 197 spin_unlock_irqrestore(&ucounts_lock, flags); 198 + 202 199 put_user_ns(ucounts->ns); 203 - kfree(ucounts); 200 + kfree_rcu(ucounts, rcu); 204 201 } 205 202 } 206 203