Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 13 Oct 2014 14:06:09 +0000 (16:06 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 13 Oct 2014 14:06:09 +0000 (16:06 +0200)
Pull perf fixes from Ingo Molnar:
 "Two leftover fixes from the v3.17 cycle - these will be forwarded to
  stable as well, if they prove problem-free in wider testing as well"

[ Side note: the "fix perf bug in fork()" fix had also come in through
  Andrew's patch-bomb   - Linus ]

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  perf: Fix perf bug in fork()
  perf: Fix unclone_ctx() vs. locking

1  2 
kernel/events/core.c

diff --combined kernel/events/core.c
index 385f11d941059f94a0f7bc988855f68998a1f7aa,658f232af04c7668c09deec72f1b13f0e6434a91..094df8c0742daccba897abc504dd70b6b9ee253f
@@@ -47,8 -47,6 +47,8 @@@
  
  #include <asm/irq_regs.h>
  
 +static struct workqueue_struct *perf_wq;
 +
  struct remote_function_call {
        struct task_struct      *p;
        int                     (*func)(void *info);
@@@ -122,13 -120,6 +122,13 @@@ static int cpu_function_call(int cpu, i
        return data.ret;
  }
  
 +#define EVENT_OWNER_KERNEL ((void *) -1)
 +
 +static bool is_kernel_event(struct perf_event *event)
 +{
 +      return event->owner == EVENT_OWNER_KERNEL;
 +}
 +
  #define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\
                       PERF_FLAG_FD_OUTPUT  |\
                       PERF_FLAG_PID_CGROUP |\
@@@ -401,9 -392,14 +401,9 @@@ perf_cgroup_match(struct perf_event *ev
                                    event->cgrp->css.cgroup);
  }
  
 -static inline void perf_put_cgroup(struct perf_event *event)
 -{
 -      css_put(&event->cgrp->css);
 -}
 -
  static inline void perf_detach_cgroup(struct perf_event *event)
  {
 -      perf_put_cgroup(event);
 +      css_put(&event->cgrp->css);
        event->cgrp = NULL;
  }
  
@@@ -906,13 -902,23 +906,23 @@@ static void put_ctx(struct perf_event_c
        }
  }
  
- static void unclone_ctx(struct perf_event_context *ctx)
+ /*
+  * This must be done under the ctx->lock, such as to serialize against
+  * context_equiv(), therefore we cannot call put_ctx() since that might end up
+  * calling scheduler related locks and ctx->lock nests inside those.
+  */
+ static __must_check struct perf_event_context *
+ unclone_ctx(struct perf_event_context *ctx)
  {
-       if (ctx->parent_ctx) {
-               put_ctx(ctx->parent_ctx);
+       struct perf_event_context *parent_ctx = ctx->parent_ctx;
+       lockdep_assert_held(&ctx->lock);
+       if (parent_ctx)
                ctx->parent_ctx = NULL;
-       }
        ctx->generation++;
+       return parent_ctx;
  }
  
  static u32 perf_event_pid(struct perf_event *event, struct task_struct *p)
                perf_event__header_size(tmp);
  }
  
 +/*
 + * User event without the task.
 + */
 +static bool is_orphaned_event(struct perf_event *event)
 +{
 +      return event && !is_kernel_event(event) && !event->owner;
 +}
 +
 +/*
 + * Event has a parent but parent's task finished and it's
 + * alive only because of children holding refference.
 + */
 +static bool is_orphaned_child(struct perf_event *event)
 +{
 +      return is_orphaned_event(event->parent);
 +}
 +
 +static void orphans_remove_work(struct work_struct *work);
 +
 +static void schedule_orphans_remove(struct perf_event_context *ctx)
 +{
 +      if (!ctx->task || ctx->orphans_remove_sched || !perf_wq)
 +              return;
 +
 +      if (queue_delayed_work(perf_wq, &ctx->orphans_remove, 1)) {
 +              get_ctx(ctx);
 +              ctx->orphans_remove_sched = true;
 +      }
 +}
 +
 +static int __init perf_workqueue_init(void)
 +{
 +      perf_wq = create_singlethread_workqueue("perf");
 +      WARN(!perf_wq, "failed to create perf workqueue\n");
 +      return perf_wq ? 0 : -1;
 +}
 +
 +core_initcall(perf_workqueue_init);
 +
  static inline int
  event_filter_match(struct perf_event *event)
  {
@@@ -1467,9 -1434,6 +1477,9 @@@ event_sched_out(struct perf_event *even
        if (event->attr.exclusive || !cpuctx->active_oncpu)
                cpuctx->exclusive = 0;
  
 +      if (is_orphaned_child(event))
 +              schedule_orphans_remove(ctx);
 +
        perf_pmu_enable(event->pmu);
  }
  
@@@ -1777,9 -1741,6 +1787,9 @@@ event_sched_in(struct perf_event *event
        if (event->attr.exclusive)
                cpuctx->exclusive = 1;
  
 +      if (is_orphaned_child(event))
 +              schedule_orphans_remove(ctx);
 +
  out:
        perf_pmu_enable(event->pmu);
  
@@@ -2259,6 -2220,9 +2269,9 @@@ static void ctx_sched_out(struct perf_e
  static int context_equiv(struct perf_event_context *ctx1,
                         struct perf_event_context *ctx2)
  {
+       lockdep_assert_held(&ctx1->lock);
+       lockdep_assert_held(&ctx2->lock);
        /* Pinning disables the swap optimization */
        if (ctx1->pin_count || ctx2->pin_count)
                return 0;
@@@ -2380,7 -2344,7 +2393,7 @@@ static void perf_event_context_sched_ou
        next_parent = rcu_dereference(next_ctx->parent_ctx);
  
        /* If neither context have a parent context; they cannot be clones. */
 -      if (!parent || !next_parent)
 +      if (!parent && !next_parent)
                goto unlock;
  
        if (next_parent == ctx || next_ctx == parent || next_parent == parent) {
@@@ -2992,6 -2956,7 +3005,7 @@@ static int event_enable_on_exec(struct 
   */
  static void perf_event_enable_on_exec(struct perf_event_context *ctx)
  {
+       struct perf_event_context *clone_ctx = NULL;
        struct perf_event *event;
        unsigned long flags;
        int enabled = 0;
         * Unclone this context if we enabled any event.
         */
        if (enabled)
-               unclone_ctx(ctx);
+               clone_ctx = unclone_ctx(ctx);
  
        raw_spin_unlock(&ctx->lock);
  
        perf_event_context_sched_in(ctx, ctx->task);
  out:
        local_irq_restore(flags);
+       if (clone_ctx)
+               put_ctx(clone_ctx);
  }
  
  void perf_event_exec(void)
@@@ -3127,7 -3095,6 +3144,7 @@@ static void __perf_event_init_context(s
        INIT_LIST_HEAD(&ctx->flexible_groups);
        INIT_LIST_HEAD(&ctx->event_list);
        atomic_set(&ctx->refcount, 1);
 +      INIT_DELAYED_WORK(&ctx->orphans_remove, orphans_remove_work);
  }
  
  static struct perf_event_context *
@@@ -3185,7 -3152,7 +3202,7 @@@ errout
  static struct perf_event_context *
  find_get_context(struct pmu *pmu, struct task_struct *task, int cpu)
  {
-       struct perf_event_context *ctx;
+       struct perf_event_context *ctx, *clone_ctx = NULL;
        struct perf_cpu_context *cpuctx;
        unsigned long flags;
        int ctxn, err;
  retry:
        ctx = perf_lock_task_context(task, ctxn, &flags);
        if (ctx) {
-               unclone_ctx(ctx);
+               clone_ctx = unclone_ctx(ctx);
                ++ctx->pin_count;
                raw_spin_unlock_irqrestore(&ctx->lock, flags);
+               if (clone_ctx)
+                       put_ctx(clone_ctx);
        } else {
                ctx = alloc_perf_context(pmu, task);
                err = -ENOMEM;
@@@ -3373,12 -3343,16 +3393,12 @@@ static void free_event(struct perf_even
  }
  
  /*
 - * Called when the last reference to the file is gone.
 + * Remove user event from the owner task.
   */
 -static void put_event(struct perf_event *event)
 +static void perf_remove_from_owner(struct perf_event *event)
  {
 -      struct perf_event_context *ctx = event->ctx;
        struct task_struct *owner;
  
 -      if (!atomic_long_dec_and_test(&event->refcount))
 -              return;
 -
        rcu_read_lock();
        owner = ACCESS_ONCE(event->owner);
        /*
                mutex_unlock(&owner->perf_event_mutex);
                put_task_struct(owner);
        }
 +}
 +
 +/*
 + * Called when the last reference to the file is gone.
 + */
 +static void put_event(struct perf_event *event)
 +{
 +      struct perf_event_context *ctx = event->ctx;
 +
 +      if (!atomic_long_dec_and_test(&event->refcount))
 +              return;
 +
 +      if (!is_kernel_event(event))
 +              perf_remove_from_owner(event);
  
        WARN_ON_ONCE(ctx->parent_ctx);
        /*
@@@ -3459,42 -3419,6 +3479,42 @@@ static int perf_release(struct inode *i
        return 0;
  }
  
 +/*
 + * Remove all orphanes events from the context.
 + */
 +static void orphans_remove_work(struct work_struct *work)
 +{
 +      struct perf_event_context *ctx;
 +      struct perf_event *event, *tmp;
 +
 +      ctx = container_of(work, struct perf_event_context,
 +                         orphans_remove.work);
 +
 +      mutex_lock(&ctx->mutex);
 +      list_for_each_entry_safe(event, tmp, &ctx->event_list, event_entry) {
 +              struct perf_event *parent_event = event->parent;
 +
 +              if (!is_orphaned_child(event))
 +                      continue;
 +
 +              perf_remove_from_context(event, true);
 +
 +              mutex_lock(&parent_event->child_mutex);
 +              list_del_init(&event->child_list);
 +              mutex_unlock(&parent_event->child_mutex);
 +
 +              free_event(event);
 +              put_event(parent_event);
 +      }
 +
 +      raw_spin_lock_irq(&ctx->lock);
 +      ctx->orphans_remove_sched = false;
 +      raw_spin_unlock_irq(&ctx->lock);
 +      mutex_unlock(&ctx->mutex);
 +
 +      put_ctx(ctx);
 +}
 +
  u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
  {
        struct perf_event *child;
@@@ -3592,19 -3516,6 +3612,19 @@@ static int perf_event_read_one(struct p
        return n * sizeof(u64);
  }
  
 +static bool is_event_hup(struct perf_event *event)
 +{
 +      bool no_children;
 +
 +      if (event->state != PERF_EVENT_STATE_EXIT)
 +              return false;
 +
 +      mutex_lock(&event->child_mutex);
 +      no_children = list_empty(&event->child_list);
 +      mutex_unlock(&event->child_mutex);
 +      return no_children;
 +}
 +
  /*
   * Read the performance event - simple non blocking version for now
   */
@@@ -3646,12 -3557,7 +3666,12 @@@ static unsigned int perf_poll(struct fi
  {
        struct perf_event *event = file->private_data;
        struct ring_buffer *rb;
 -      unsigned int events = POLL_HUP;
 +      unsigned int events = POLLHUP;
 +
 +      poll_wait(file, &event->waitq, wait);
 +
 +      if (is_event_hup(event))
 +              return events;
  
        /*
         * Pin the event->rb by taking event->mmap_mutex; otherwise
        if (rb)
                events = atomic_xchg(&rb->poll, 0);
        mutex_unlock(&event->mmap_mutex);
 -
 -      poll_wait(file, &event->waitq, wait);
 -
        return events;
  }
  
@@@ -5925,7 -5834,7 +5945,7 @@@ static void swevent_hlist_release(struc
        if (!hlist)
                return;
  
 -      rcu_assign_pointer(swhash->swevent_hlist, NULL);
 +      RCU_INIT_POINTER(swhash->swevent_hlist, NULL);
        kfree_rcu(hlist, rcu_head);
  }
  
@@@ -7508,9 -7417,6 +7528,9 @@@ perf_event_create_kernel_counter(struc
                goto err;
        }
  
 +      /* Mark owner so we could distinguish it from user events. */
 +      event->owner = EVENT_OWNER_KERNEL;
 +
        account_event(event);
  
        ctx = find_get_context(event->pmu, task, cpu);
@@@ -7597,12 -7503,6 +7617,12 @@@ static void sync_child_event(struct per
        list_del_init(&child_event->child_list);
        mutex_unlock(&parent_event->child_mutex);
  
 +      /*
 +       * Make sure user/parent get notified, that we just
 +       * lost one event.
 +       */
 +      perf_event_wakeup(parent_event);
 +
        /*
         * Release the parent event, if this was the last
         * reference to it.
@@@ -7637,16 -7537,13 +7657,16 @@@ __perf_event_exit_task(struct perf_even
        if (child_event->parent) {
                sync_child_event(child_event, child);
                free_event(child_event);
 +      } else {
 +              child_event->state = PERF_EVENT_STATE_EXIT;
 +              perf_event_wakeup(child_event);
        }
  }
  
  static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
  {
        struct perf_event *child_event, *next;
-       struct perf_event_context *child_ctx, *parent_ctx;
+       struct perf_event_context *child_ctx, *clone_ctx = NULL;
        unsigned long flags;
  
        if (likely(!child->perf_event_ctxp[ctxn])) {
        task_ctx_sched_out(child_ctx);
        child->perf_event_ctxp[ctxn] = NULL;
  
-       /*
-        * In order to avoid freeing: child_ctx->parent_ctx->task
-        * under perf_event_context::lock, grab another reference.
-        */
-       parent_ctx = child_ctx->parent_ctx;
-       if (parent_ctx)
-               get_ctx(parent_ctx);
        /*
         * If this context is a clone; unclone it so it can't get
         * swapped to another process while we're removing all
         * the events from it.
         */
-       unclone_ctx(child_ctx);
+       clone_ctx = unclone_ctx(child_ctx);
        update_context_time(child_ctx);
        raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
  
-       /*
-        * Now that we no longer hold perf_event_context::lock, drop
-        * our extra child_ctx->parent_ctx reference.
-        */
-       if (parent_ctx)
-               put_ctx(parent_ctx);
+       if (clone_ctx)
+               put_ctx(clone_ctx);
  
        /*
         * Report the task dead after unscheduling the events so that we
@@@ -7823,7 -7708,6 +7831,7 @@@ inherit_event(struct perf_event *parent
              struct perf_event *group_leader,
              struct perf_event_context *child_ctx)
  {
 +      enum perf_event_active_state parent_state = parent_event->state;
        struct perf_event *child_event;
        unsigned long flags;
  
        if (IS_ERR(child_event))
                return child_event;
  
 -      if (!atomic_long_inc_not_zero(&parent_event->refcount)) {
 +      if (is_orphaned_event(parent_event) ||
 +          !atomic_long_inc_not_zero(&parent_event->refcount)) {
                free_event(child_event);
                return NULL;
        }
         * not its attr.disabled bit.  We hold the parent's mutex,
         * so we won't race with perf_event_{en, dis}able_family.
         */
 -      if (parent_event->state >= PERF_EVENT_STATE_INACTIVE)
 +      if (parent_state >= PERF_EVENT_STATE_INACTIVE)
                child_event->state = PERF_EVENT_STATE_INACTIVE;
        else
                child_event->state = PERF_EVENT_STATE_OFF;