]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
kprobes: Use guard for rcu_read_lock
authorMasami Hiramatsu (Google) <mhiramat@kernel.org>
Mon, 9 Dec 2024 02:41:38 +0000 (11:41 +0900)
committerMasami Hiramatsu (Google) <mhiramat@kernel.org>
Fri, 10 Jan 2025 00:00:13 +0000 (09:00 +0900)
Use guard(rcu) for rcu_read_lock so that it can remove unneeded
gotos and make it more structured.

Link: https://lore.kernel.org/all/173371209846.480397.3852648910271029695.stgit@devnote2/
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
kernel/kprobes.c

index 004eb83265208926c1ec36010aecfcfb90cb9950..a24587e8f91aa74a285bbbb8db8f4c49e87a2701 100644 (file)
@@ -144,30 +144,26 @@ kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
 
        /* Since the slot array is not protected by rcu, we need a mutex */
        guard(mutex)(&c->mutex);
- retry:
-       rcu_read_lock();
-       list_for_each_entry_rcu(kip, &c->pages, list) {
-               if (kip->nused < slots_per_page(c)) {
-                       int i;
-
-                       for (i = 0; i < slots_per_page(c); i++) {
-                               if (kip->slot_used[i] == SLOT_CLEAN) {
-                                       kip->slot_used[i] = SLOT_USED;
-                                       kip->nused++;
-                                       rcu_read_unlock();
-                                       return kip->insns + (i * c->insn_size);
+       do {
+               guard(rcu)();
+               list_for_each_entry_rcu(kip, &c->pages, list) {
+                       if (kip->nused < slots_per_page(c)) {
+                               int i;
+
+                               for (i = 0; i < slots_per_page(c); i++) {
+                                       if (kip->slot_used[i] == SLOT_CLEAN) {
+                                               kip->slot_used[i] = SLOT_USED;
+                                               kip->nused++;
+                                               return kip->insns + (i * c->insn_size);
+                                       }
                                }
+                               /* kip->nused is broken. Fix it. */
+                               kip->nused = slots_per_page(c);
+                               WARN_ON(1);
                        }
-                       /* kip->nused is broken. Fix it. */
-                       kip->nused = slots_per_page(c);
-                       WARN_ON(1);
                }
-       }
-       rcu_read_unlock();
-
        /* If there are any garbage slots, collect it and try again. */
-       if (c->nr_garbage && collect_garbage_slots(c) == 0)
-               goto retry;
+       } while (c->nr_garbage && collect_garbage_slots(c) == 0);
 
        /* All out of space.  Need to allocate a new page. */
        kip = kmalloc(struct_size(kip, slot_used, slots_per_page(c)), GFP_KERNEL);
@@ -246,25 +242,35 @@ static int collect_garbage_slots(struct kprobe_insn_cache *c)
        return 0;
 }
 
-void __free_insn_slot(struct kprobe_insn_cache *c,
-                     kprobe_opcode_t *slot, int dirty)
+static long __find_insn_page(struct kprobe_insn_cache *c,
+       kprobe_opcode_t *slot, struct kprobe_insn_page **pkip)
 {
-       struct kprobe_insn_page *kip;
+       struct kprobe_insn_page *kip = NULL;
        long idx;
 
-       guard(mutex)(&c->mutex);
-       rcu_read_lock();
+       guard(rcu)();
        list_for_each_entry_rcu(kip, &c->pages, list) {
                idx = ((long)slot - (long)kip->insns) /
                        (c->insn_size * sizeof(kprobe_opcode_t));
-               if (idx >= 0 && idx < slots_per_page(c))
-                       goto out;
+               if (idx >= 0 && idx < slots_per_page(c)) {
+                       *pkip = kip;
+                       return idx;
+               }
        }
        /* Could not find this slot. */
        WARN_ON(1);
-       kip = NULL;
-out:
-       rcu_read_unlock();
+       *pkip = NULL;
+       return -1;
+}
+
+void __free_insn_slot(struct kprobe_insn_cache *c,
+                     kprobe_opcode_t *slot, int dirty)
+{
+       struct kprobe_insn_page *kip = NULL;
+       long idx;
+
+       guard(mutex)(&c->mutex);
+       idx = __find_insn_page(c, slot, &kip);
        /* Mark and sweep: this may sleep */
        if (kip) {
                /* Check double free */