Skip to content

Commit 2ff913a

Browse files
anakryikoIngo Molnar
authored andcommitted
uprobes: Simplify session consumer tracking
In practice, each return_instance will typically contain either zero or one return_consumer, depending on whether it has any uprobe session consumer attached or not. It's highly unlikely that more than one uprobe session consumers will be attached to any given uprobe, so there is no need to optimize for that case. But the way we currently do memory allocation and accounting is by pre-allocating the space for 4 session consumers in contiguous block of memory next to struct return_instance fixed part. This is unnecessarily wasteful. This patch changes this to keep struct return_instance fixed-sized with one pre-allocated return_consumer, while (in a highly unlikely scenario) allowing for more session consumers in a separate dynamically allocated and reallocated array. We also simplify accounting a bit by not maintaining a separate temporary capacity for consumers array, and, instead, relying on krealloc() to be a no-op if underlying memory can accommodate a slightly bigger allocation (but again, it's very uncommon scenario to even have to do this reallocation). All this gets rid of ri_size(), simplifies push_consumer() and removes confusing ri->consumers_cnt re-assignment, while containing this singular preallocated consumer logic contained within a few simple preexisting helpers. Having fixed-sized struct return_instance simplifies and speeds up return_instance reuse that we ultimately add later in this patch set, see follow up patches. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Cc: Masami Hiramatsu <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Oleg Nesterov <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent e0925f2 commit 2ff913a

File tree

2 files changed

+45
-37
lines changed

2 files changed

+45
-37
lines changed

include/linux/uprobes.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,18 @@ struct return_instance {
154154
unsigned long stack; /* stack pointer */
155155
unsigned long orig_ret_vaddr; /* original return address */
156156
bool chained; /* true, if instance is nested */
157-
int consumers_cnt;
157+
int cons_cnt; /* total number of session consumers */
158158

159159
struct return_instance *next; /* keep as stack */
160160
struct rcu_head rcu;
161161

162-
struct return_consumer consumers[] __counted_by(consumers_cnt);
162+
/* singular pre-allocated return_consumer instance for common case */
163+
struct return_consumer consumer;
164+
/*
165+
* extra return_consumer instances for rare cases of multiple session consumers,
166+
* contains (cons_cnt - 1) elements
167+
*/
168+
struct return_consumer *extra_consumers;
163169
} ____cacheline_aligned;
164170

165171
enum rp_check {

kernel/events/uprobes.c

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,7 @@ static struct return_instance *free_ret_instance(struct return_instance *ri, boo
18991899
hprobe_finalize(&ri->hprobe, hstate);
19001900
}
19011901

1902+
kfree(ri->extra_consumers);
19021903
kfree_rcu(ri, rcu);
19031904
return next;
19041905
}
@@ -1974,32 +1975,34 @@ static struct uprobe_task *get_utask(void)
19741975
return current->utask;
19751976
}
19761977

1977-
static size_t ri_size(int consumers_cnt)
1978-
{
1979-
struct return_instance *ri;
1980-
1981-
return sizeof(*ri) + sizeof(ri->consumers[0]) * consumers_cnt;
1982-
}
1983-
1984-
#define DEF_CNT 4
1985-
19861978
static struct return_instance *alloc_return_instance(void)
19871979
{
19881980
struct return_instance *ri;
19891981

1990-
ri = kzalloc(ri_size(DEF_CNT), GFP_KERNEL);
1982+
ri = kzalloc(sizeof(*ri), GFP_KERNEL);
19911983
if (!ri)
19921984
return ZERO_SIZE_PTR;
19931985

1994-
ri->consumers_cnt = DEF_CNT;
19951986
return ri;
19961987
}
19971988

19981989
static struct return_instance *dup_return_instance(struct return_instance *old)
19991990
{
2000-
size_t size = ri_size(old->consumers_cnt);
1991+
struct return_instance *ri;
1992+
1993+
ri = kmemdup(old, sizeof(*ri), GFP_KERNEL);
1994+
1995+
if (unlikely(old->cons_cnt > 1)) {
1996+
ri->extra_consumers = kmemdup(old->extra_consumers,
1997+
sizeof(ri->extra_consumers[0]) * (old->cons_cnt - 1),
1998+
GFP_KERNEL);
1999+
if (!ri->extra_consumers) {
2000+
kfree(ri);
2001+
return NULL;
2002+
}
2003+
}
20012004

2002-
return kmemdup(old, size, GFP_KERNEL);
2005+
return ri;
20032006
}
20042007

20052008
static int dup_utask(struct task_struct *t, struct uprobe_task *o_utask)
@@ -2369,40 +2372,46 @@ static struct uprobe *find_active_uprobe_rcu(unsigned long bp_vaddr, int *is_swb
23692372
return uprobe;
23702373
}
23712374

2372-
static struct return_instance*
2373-
push_consumer(struct return_instance *ri, int idx, __u64 id, __u64 cookie)
2375+
static struct return_instance *push_consumer(struct return_instance *ri, __u64 id, __u64 cookie)
23742376
{
2377+
struct return_consumer *ric;
2378+
23752379
if (unlikely(ri == ZERO_SIZE_PTR))
23762380
return ri;
23772381

2378-
if (unlikely(idx >= ri->consumers_cnt)) {
2379-
struct return_instance *old_ri = ri;
2380-
2381-
ri->consumers_cnt += DEF_CNT;
2382-
ri = krealloc(old_ri, ri_size(old_ri->consumers_cnt), GFP_KERNEL);
2383-
if (!ri) {
2384-
kfree(old_ri);
2382+
if (unlikely(ri->cons_cnt > 0)) {
2383+
ric = krealloc(ri->extra_consumers, sizeof(*ric) * ri->cons_cnt, GFP_KERNEL);
2384+
if (!ric) {
2385+
kfree(ri->extra_consumers);
2386+
kfree_rcu(ri, rcu);
23852387
return ZERO_SIZE_PTR;
23862388
}
2389+
ri->extra_consumers = ric;
23872390
}
23882391

2389-
ri->consumers[idx].id = id;
2390-
ri->consumers[idx].cookie = cookie;
2392+
ric = likely(ri->cons_cnt == 0) ? &ri->consumer : &ri->extra_consumers[ri->cons_cnt - 1];
2393+
ric->id = id;
2394+
ric->cookie = cookie;
2395+
2396+
ri->cons_cnt++;
23912397
return ri;
23922398
}
23932399

23942400
static struct return_consumer *
23952401
return_consumer_find(struct return_instance *ri, int *iter, int id)
23962402
{
23972403
struct return_consumer *ric;
2398-
int idx = *iter;
2404+
int idx;
23992405

2400-
for (ric = &ri->consumers[idx]; idx < ri->consumers_cnt; idx++, ric++) {
2406+
for (idx = *iter; idx < ri->cons_cnt; idx++)
2407+
{
2408+
ric = likely(idx == 0) ? &ri->consumer : &ri->extra_consumers[idx - 1];
24012409
if (ric->id == id) {
24022410
*iter = idx + 1;
24032411
return ric;
24042412
}
24052413
}
2414+
24062415
return NULL;
24072416
}
24082417

@@ -2416,7 +2425,6 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
24162425
struct uprobe_consumer *uc;
24172426
bool has_consumers = false, remove = true;
24182427
struct return_instance *ri = NULL;
2419-
int push_idx = 0;
24202428

24212429
current->utask->auprobe = &uprobe->arch;
24222430

@@ -2441,18 +2449,12 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs)
24412449
ri = alloc_return_instance();
24422450

24432451
if (session)
2444-
ri = push_consumer(ri, push_idx++, uc->id, cookie);
2452+
ri = push_consumer(ri, uc->id, cookie);
24452453
}
24462454
current->utask->auprobe = NULL;
24472455

2448-
if (!ZERO_OR_NULL_PTR(ri)) {
2449-
/*
2450-
* The push_idx value has the final number of return consumers,
2451-
* and ri->consumers_cnt has number of allocated consumers.
2452-
*/
2453-
ri->consumers_cnt = push_idx;
2456+
if (!ZERO_OR_NULL_PTR(ri))
24542457
prepare_uretprobe(uprobe, regs, ri);
2455-
}
24562458

24572459
if (remove && has_consumers) {
24582460
down_read(&uprobe->register_rwsem);

0 commit comments

Comments
 (0)