On Tue, Jan 09, 2018 at 02:26:02PM +0100, Peter Zijlstra wrote:

> OK, so I'm running on an IVB-EP with PTI enabled. I insta triggered a
> lockdep splat

---
Subject: perf: Fix lock inversion between perf,trace,cpuhp
From: Peter Zijlstra <pet...@infradead.org>
Date: Tue Jan 9 13:10:30 CET 2018

Lockdep gifted us:

        perf_trace_init()
#0        mutex_lock(&event_mutex)
          perf_trace_event_init()
            perf_trace_event_reg()
              tp_event->class->reg() := tracepoint_probe_register
#1              mutex_lock(&tracepoints_mutex)
                  trace_point_add_func()
#2                  static_key_enable()


#2      do_cpu_up()
          perf_event_init_cpu()
#3          mutex_lock(&pmus_lock)
#4          mutex_lock(&ctx->mutex)


        perf_event_task_disable()
          mutex_lock(&current->perf_event_mutex)
#4        ctx = perf_event_ctx_lock()
#5        perf_event_for_each_child()


        do_exit()
          task_work_run()
            __fput()
              perf_release()
                perf_event_release_kernel()
#4                mutex_lock(&ctx->mutex)
#5                mutex_lock(&event->child_mutex)
                  free_event()
                    _free_event()
                      event->destroy() := perf_trace_destroy
#0                      mutex_lock(&event_mutex);


Fix that by moving the free_event() out from under the locks.

Cc: Thomas Gleixner <t...@linutronix.de>
Cc: Steven Rostedt (VMware) <rost...@goodmis.org>
Signed-off-by: Peter Zijlstra (Intel) <pet...@infradead.org>
---
 kernel/events/core.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1231,6 +1231,10 @@ static void put_ctx(struct perf_event_co
  *           perf_event_context::lock
  *         perf_event::mmap_mutex
  *         mmap_sem
+ *
+ *    cpu_hotplug_lock
+ *      pmus_lock
+ *       cpuctx->mutex / perf_event_context::mutex
  */
 static struct perf_event_context *
 perf_event_ctx_lock_nested(struct perf_event *event, int nesting)
@@ -4196,6 +4200,7 @@ int perf_event_release_kernel(struct per
 {
        struct perf_event_context *ctx = event->ctx;
        struct perf_event *child, *tmp;
+       LIST_HEAD(free_list);
 
        /*
         * If we got here through err_file: fput(event_file); we will not have
@@ -4268,8 +4273,7 @@ int perf_event_release_kernel(struct per
                                               struct perf_event, child_list);
                if (tmp == child) {
                        perf_remove_from_context(child, DETACH_GROUP);
-                       list_del(&child->child_list);
-                       free_event(child);
+                       list_move(&child->child_list, &free_list);
                        /*
                         * This matches the refcount bump in inherit_event();
                         * this can't be the last reference.
@@ -4284,6 +4288,11 @@ int perf_event_release_kernel(struct per
        }
        mutex_unlock(&event->child_mutex);
 
+       list_for_each_entry_safe(child, tmp, &free_list, child_list) {
+               list_del(&child->child_list);
+               free_event(child);
+       }
+
 no_ctx:
        put_event(event); /* Must be the 'last' reference */
        return 0;

Reply via email to