Skip to content

Commit 2bf8e5a

Browse files
anakryikoPeter Zijlstra
authored andcommitted
uprobes: allow put_uprobe() from non-sleepable softirq context
Currently put_uprobe() might trigger mutex_lock()/mutex_unlock(), which makes it unsuitable to be called from more restricted context like softirq. Let's make put_uprobe() agnostic to the context in which it is called, and use work queue to defer the mutex-protected clean up steps. RB tree removal step is also moved into work-deferred callback to avoid potential deadlock between softirq-based timer callback, added in the next patch, and the rest of uprobe code. We can rework locking altogher as a follow up, but that's significantly more tricky, so warrants its own patch set. For now, we need to make sure that changes in the next patch that add timer thread work correctly with existing approach, while concentrating on SRCU + timeout logic. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 9e9af8b commit 2bf8e5a

File tree

1 file changed

+16
-4
lines changed

1 file changed

+16
-4
lines changed

kernel/events/uprobes.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/shmem_fs.h>
2828
#include <linux/khugepaged.h>
2929
#include <linux/rcupdate_trace.h>
30+
#include <linux/workqueue.h>
3031

3132
#include <linux/uprobes.h>
3233

@@ -61,7 +62,10 @@ struct uprobe {
6162
struct list_head pending_list;
6263
struct list_head consumers;
6364
struct inode *inode; /* Also hold a ref to inode */
64-
struct rcu_head rcu;
65+
union {
66+
struct rcu_head rcu;
67+
struct work_struct work;
68+
};
6569
loff_t offset;
6670
loff_t ref_ctr_offset;
6771
unsigned long flags; /* "unsigned long" so bitops work */
@@ -625,10 +629,9 @@ static void uprobe_free_rcu(struct rcu_head *rcu)
625629
kfree(uprobe);
626630
}
627631

628-
static void put_uprobe(struct uprobe *uprobe)
632+
static void uprobe_free_deferred(struct work_struct *work)
629633
{
630-
if (!refcount_dec_and_test(&uprobe->ref))
631-
return;
634+
struct uprobe *uprobe = container_of(work, struct uprobe, work);
632635

633636
write_lock(&uprobes_treelock);
634637

@@ -652,6 +655,15 @@ static void put_uprobe(struct uprobe *uprobe)
652655
call_rcu_tasks_trace(&uprobe->rcu, uprobe_free_rcu);
653656
}
654657

658+
static void put_uprobe(struct uprobe *uprobe)
659+
{
660+
if (!refcount_dec_and_test(&uprobe->ref))
661+
return;
662+
663+
INIT_WORK(&uprobe->work, uprobe_free_deferred);
664+
schedule_work(&uprobe->work);
665+
}
666+
655667
static __always_inline
656668
int uprobe_cmp(const struct inode *l_inode, const loff_t l_offset,
657669
const struct uprobe *r)

0 commit comments

Comments
 (0)