Skip to content

Commit 4f5d5ef

Browse files
Sebastian Andrzej Siewiorakpm00
Sebastian Andrzej Siewior
authored andcommitted
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/[email protected] Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Reviewed-by: Paul E. McKenney <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Boqun Feng <[email protected]> Cc: Joel Fernandes <[email protected]> Cc: Josh Triplett <[email protected]> Cc: Lai jiangshan <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Mengen Sun <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: "Uladzislau Rezki (Sony)" <[email protected]> Cc: YueHong Wu <[email protected]> Cc: Zqiang <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 2538edd commit 4f5d5ef

File tree

2 files changed

+43
-36
lines changed

2 files changed

+43
-36
lines changed

include/linux/user_namespace.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <linux/kref.h>
66
#include <linux/nsproxy.h>
77
#include <linux/ns_common.h>
8+
#include <linux/rculist_nulls.h>
89
#include <linux/sched.h>
910
#include <linux/workqueue.h>
1011
#include <linux/rwsem.h>
@@ -115,9 +116,10 @@ struct user_namespace {
115116
} __randomize_layout;
116117

117118
struct ucounts {
118-
struct hlist_node node;
119+
struct hlist_nulls_node node;
119120
struct user_namespace *ns;
120121
kuid_t uid;
122+
struct rcu_head rcu;
121123
atomic_t count;
122124
atomic_long_t ucount[UCOUNT_COUNTS];
123125
atomic_long_t rlimit[UCOUNT_RLIMIT_COUNTS];

kernel/ucount.c

+40-35
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ struct ucounts init_ucounts = {
1515
};
1616

1717
#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+
};
1922
static DEFINE_SPINLOCK(ucounts_lock);
2023

2124
#define ucounts_hashfn(ns, uid) \
@@ -24,7 +27,6 @@ static DEFINE_SPINLOCK(ucounts_lock);
2427
#define ucounts_hashentry(ns, uid) \
2528
(ucounts_hashtable + ucounts_hashfn(ns, uid))
2629

27-
2830
#ifdef CONFIG_SYSCTL
2931
static struct ctl_table_set *
3032
set_lookup(struct ctl_table_root *root)
@@ -127,22 +129,28 @@ void retire_userns_sysctls(struct user_namespace *ns)
127129
#endif
128130
}
129131

130-
static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid, struct hlist_head *hashent)
132+
static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid,
133+
struct hlist_nulls_head *hashent)
131134
{
132135
struct ucounts *ucounts;
136+
struct hlist_nulls_node *pos;
133137

134-
hlist_for_each_entry(ucounts, hashent, node) {
135-
if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns))
136-
return ucounts;
138+
guard(rcu)();
139+
hlist_nulls_for_each_entry_rcu(ucounts, pos, hashent, node) {
140+
if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns)) {
141+
if (atomic_inc_not_zero(&ucounts->count))
142+
return ucounts;
143+
}
137144
}
138145
return NULL;
139146
}
140147

141148
static void hlist_add_ucounts(struct ucounts *ucounts)
142149
{
143-
struct hlist_head *hashent = ucounts_hashentry(ucounts->ns, ucounts->uid);
150+
struct hlist_nulls_head *hashent = ucounts_hashentry(ucounts->ns, ucounts->uid);
151+
144152
spin_lock_irq(&ucounts_lock);
145-
hlist_add_head(&ucounts->node, hashent);
153+
hlist_nulls_add_head_rcu(&ucounts->node, hashent);
146154
spin_unlock_irq(&ucounts_lock);
147155
}
148156

@@ -155,48 +163,45 @@ struct ucounts *get_ucounts(struct ucounts *ucounts)
155163

156164
struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid)
157165
{
158-
struct hlist_head *hashent = ucounts_hashentry(ns, uid);
159-
struct ucounts *ucounts, *new = NULL;
166+
struct hlist_nulls_head *hashent = ucounts_hashentry(ns, uid);
167+
struct ucounts *ucounts, *new;
168+
169+
ucounts = find_ucounts(ns, uid, hashent);
170+
if (ucounts)
171+
return ucounts;
172+
173+
new = kzalloc(sizeof(*new), GFP_KERNEL);
174+
if (!new)
175+
return NULL;
176+
177+
new->ns = ns;
178+
new->uid = uid;
179+
atomic_set(&new->count, 1);
160180

161181
spin_lock_irq(&ucounts_lock);
162182
ucounts = find_ucounts(ns, uid, hashent);
163-
if (!ucounts) {
183+
if (ucounts) {
164184
spin_unlock_irq(&ucounts_lock);
165-
166-
new = kzalloc(sizeof(*new), GFP_KERNEL);
167-
if (!new)
168-
return NULL;
169-
170-
new->ns = ns;
171-
new->uid = uid;
172-
atomic_set(&new->count, 1);
173-
174-
spin_lock_irq(&ucounts_lock);
175-
ucounts = find_ucounts(ns, uid, hashent);
176-
if (!ucounts) {
177-
hlist_add_head(&new->node, hashent);
178-
get_user_ns(new->ns);
179-
spin_unlock_irq(&ucounts_lock);
180-
return new;
181-
}
185+
kfree(new);
186+
return ucounts;
182187
}
183-
if (!atomic_inc_not_zero(&ucounts->count))
184-
ucounts = NULL;
185-
spin_unlock_irq(&ucounts_lock);
186-
kfree(new);
187188

188-
return ucounts;
189+
hlist_nulls_add_head_rcu(&new->node, hashent);
190+
get_user_ns(new->ns);
191+
spin_unlock_irq(&ucounts_lock);
192+
return new;
189193
}
190194

191195
void put_ucounts(struct ucounts *ucounts)
192196
{
193197
unsigned long flags;
194198

195199
if (atomic_dec_and_lock_irqsave(&ucounts->count, &ucounts_lock, flags)) {
196-
hlist_del_init(&ucounts->node);
200+
hlist_nulls_del_rcu(&ucounts->node);
197201
spin_unlock_irqrestore(&ucounts_lock, flags);
202+
198203
put_user_ns(ucounts->ns);
199-
kfree(ucounts);
204+
kfree_rcu(ucounts, rcu);
200205
}
201206
}
202207

0 commit comments

Comments
 (0)