Skip to content

Commit 04b0162

Browse files
author
Peter Zijlstra
committed
perf/uprobe: split uprobe_unregister()
With uprobe_unregister() having grown a synchronize_srcu(), it becomes fairly slow to call. Esp. since both users of this API call it in a loop. Peel off the sync_srcu() and do it once, after the loop. We also need to add uprobe_unregister_sync() into uprobe_register()'s error handling path, as we need to be careful about returning to the caller before we have a guarantee that partially attached consumer won't be called anymore. This is an unlikely slow path and this should be totally fine to be slow in the case of a failed attach. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: "Peter Zijlstra (Intel)" <[email protected]> Co-developed-by: Andrii Nakryiko <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Oleg Nesterov <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent cc01bd0 commit 04b0162

File tree

5 files changed

+32
-11
lines changed

5 files changed

+32
-11
lines changed

include/linux/uprobes.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs);
115115
extern int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
116116
extern struct uprobe *uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset, struct uprobe_consumer *uc);
117117
extern int uprobe_apply(struct uprobe *uprobe, struct uprobe_consumer *uc, bool);
118-
extern void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc);
118+
extern void uprobe_unregister_nosync(struct uprobe *uprobe, struct uprobe_consumer *uc);
119+
extern void uprobe_unregister_sync(void);
119120
extern int uprobe_mmap(struct vm_area_struct *vma);
120121
extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end);
121122
extern void uprobe_start_dup_mmap(void);
@@ -164,7 +165,10 @@ uprobe_apply(struct uprobe* uprobe, struct uprobe_consumer *uc, bool add)
164165
return -ENOSYS;
165166
}
166167
static inline void
167-
uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
168+
uprobe_unregister_nosync(struct uprobe *uprobe, struct uprobe_consumer *uc)
169+
{
170+
}
171+
static inline void uprobe_unregister_sync(void)
168172
{
169173
}
170174
static inline int uprobe_mmap(struct vm_area_struct *vma)

kernel/events/uprobes.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,11 +1105,11 @@ register_for_each_vma(struct uprobe *uprobe, struct uprobe_consumer *new)
11051105
}
11061106

11071107
/**
1108-
* uprobe_unregister - unregister an already registered probe.
1108+
* uprobe_unregister_nosync - unregister an already registered probe.
11091109
* @uprobe: uprobe to remove
11101110
* @uc: identify which probe if multiple probes are colocated.
11111111
*/
1112-
void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
1112+
void uprobe_unregister_nosync(struct uprobe *uprobe, struct uprobe_consumer *uc)
11131113
{
11141114
int err;
11151115

@@ -1121,12 +1121,15 @@ void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
11211121
/* TODO : cant unregister? schedule a worker thread */
11221122
if (unlikely(err)) {
11231123
uprobe_warn(current, "unregister, leaking uprobe");
1124-
goto out_sync;
1124+
return;
11251125
}
11261126

11271127
put_uprobe(uprobe);
1128+
}
1129+
EXPORT_SYMBOL_GPL(uprobe_unregister_nosync);
11281130

1129-
out_sync:
1131+
void uprobe_unregister_sync(void)
1132+
{
11301133
/*
11311134
* Now that handler_chain() and handle_uretprobe_chain() iterate over
11321135
* uprobe->consumers list under RCU protection without holding
@@ -1138,7 +1141,7 @@ void uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)
11381141
*/
11391142
synchronize_srcu(&uprobes_srcu);
11401143
}
1141-
EXPORT_SYMBOL_GPL(uprobe_unregister);
1144+
EXPORT_SYMBOL_GPL(uprobe_unregister_sync);
11421145

11431146
/**
11441147
* uprobe_register - register a probe
@@ -1196,7 +1199,13 @@ struct uprobe *uprobe_register(struct inode *inode,
11961199
up_write(&uprobe->register_rwsem);
11971200

11981201
if (ret) {
1199-
uprobe_unregister(uprobe, uc);
1202+
uprobe_unregister_nosync(uprobe, uc);
1203+
/*
1204+
* Registration might have partially succeeded, so we can have
1205+
* this consumer being called right at this time. We need to
1206+
* sync here. It's ok, it's unlikely slow path.
1207+
*/
1208+
uprobe_unregister_sync();
12001209
return ERR_PTR(ret);
12011210
}
12021211

kernel/trace/bpf_trace.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3184,7 +3184,10 @@ static void bpf_uprobe_unregister(struct bpf_uprobe *uprobes, u32 cnt)
31843184
u32 i;
31853185

31863186
for (i = 0; i < cnt; i++)
3187-
uprobe_unregister(uprobes[i].uprobe, &uprobes[i].consumer);
3187+
uprobe_unregister_nosync(uprobes[i].uprobe, &uprobes[i].consumer);
3188+
3189+
if (cnt)
3190+
uprobe_unregister_sync();
31883191
}
31893192

31903193
static void bpf_uprobe_multi_link_release(struct bpf_link *link)

kernel/trace/trace_uprobe.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,7 @@ static int trace_uprobe_enable(struct trace_uprobe *tu, filter_func_t filter)
10971097
static void __probe_event_disable(struct trace_probe *tp)
10981098
{
10991099
struct trace_uprobe *tu;
1100+
bool sync = false;
11001101

11011102
tu = container_of(tp, struct trace_uprobe, tp);
11021103
WARN_ON(!uprobe_filter_is_empty(tu->tp.event->filter));
@@ -1105,9 +1106,12 @@ static void __probe_event_disable(struct trace_probe *tp)
11051106
if (!tu->uprobe)
11061107
continue;
11071108

1108-
uprobe_unregister(tu->uprobe, &tu->consumer);
1109+
uprobe_unregister_nosync(tu->uprobe, &tu->consumer);
1110+
sync = true;
11091111
tu->uprobe = NULL;
11101112
}
1113+
if (sync)
1114+
uprobe_unregister_sync();
11111115
}
11121116

11131117
static int probe_event_enable(struct trace_event_call *call,

tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,8 @@ static void testmod_unregister_uprobe(void)
475475
mutex_lock(&testmod_uprobe_mutex);
476476

477477
if (uprobe.uprobe) {
478-
uprobe_unregister(uprobe.uprobe, &uprobe.consumer);
478+
uprobe_unregister_nosync(uprobe.uprobe, &uprobe.consumer);
479+
uprobe_unregister_sync();
479480
path_put(&uprobe.path);
480481
uprobe.uprobe = NULL;
481482
}

0 commit comments

Comments
 (0)