]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'perf/urgent' into perf/core
authorIngo Molnar <mingo@elte.hu>
Tue, 6 Dec 2011 05:42:35 +0000 (06:42 +0100)
committerIngo Molnar <mingo@elte.hu>
Tue, 6 Dec 2011 05:43:49 +0000 (06:43 +0100)
Merge reason: Add these cherry-picked commits so that future changes
              on perf/core don't conflict.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
1  2 
kernel/events/core.c
kernel/events/internal.h
kernel/trace/trace_events_filter.c
tools/perf/util/evsel.c
tools/perf/util/hist.h
tools/perf/util/session.c

diff --combined kernel/events/core.c
index 924338bb489ce1f43a400e75a2c5f5df94491cb4,600c1629b64db7c3fdb88c1e4c6d08412118ed1b..a355ffb0b28f8974c42fb3797b706da040a43789
@@@ -185,6 -185,9 +185,9 @@@ static void cpu_ctx_sched_in(struct per
  static void update_context_time(struct perf_event_context *ctx);
  static u64 perf_event_time(struct perf_event *event);
  
+ static void ring_buffer_attach(struct perf_event *event,
+                              struct ring_buffer *rb);
  void __weak perf_event_print_debug(void)      { }
  
  extern __weak const char *perf_pmu_name(void)
@@@ -1322,7 -1325,6 +1325,7 @@@ retry
        }
        raw_spin_unlock_irq(&ctx->lock);
  }
 +EXPORT_SYMBOL_GPL(perf_event_disable);
  
  static void perf_set_shadow_time(struct perf_event *event,
                                 struct perf_event_context *ctx,
@@@ -1807,7 -1809,6 +1810,7 @@@ retry
  out:
        raw_spin_unlock_irq(&ctx->lock);
  }
 +EXPORT_SYMBOL_GPL(perf_event_enable);
  
  int perf_event_refresh(struct perf_event *event, int refresh)
  {
@@@ -2175,7 -2176,8 +2178,8 @@@ static void perf_event_context_sched_in
  
        perf_event_sched_in(cpuctx, ctx, task);
  
-       cpuctx->task_ctx = ctx;
+       if (ctx->nr_events)
+               cpuctx->task_ctx = ctx;
  
        perf_pmu_enable(ctx->pmu);
        perf_ctx_unlock(cpuctx, ctx);
@@@ -2571,6 -2573,215 +2575,6 @@@ static u64 perf_event_read(struct perf_
        return perf_event_count(event);
  }
  
 -/*
 - * Callchain support
 - */
 -
 -struct callchain_cpus_entries {
 -      struct rcu_head                 rcu_head;
 -      struct perf_callchain_entry     *cpu_entries[0];
 -};
 -
 -static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
 -static atomic_t nr_callchain_events;
 -static DEFINE_MUTEX(callchain_mutex);
 -struct callchain_cpus_entries *callchain_cpus_entries;
 -
 -
 -__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
 -                                struct pt_regs *regs)
 -{
 -}
 -
 -__weak void perf_callchain_user(struct perf_callchain_entry *entry,
 -                              struct pt_regs *regs)
 -{
 -}
 -
 -static void release_callchain_buffers_rcu(struct rcu_head *head)
 -{
 -      struct callchain_cpus_entries *entries;
 -      int cpu;
 -
 -      entries = container_of(head, struct callchain_cpus_entries, rcu_head);
 -
 -      for_each_possible_cpu(cpu)
 -              kfree(entries->cpu_entries[cpu]);
 -
 -      kfree(entries);
 -}
 -
 -static void release_callchain_buffers(void)
 -{
 -      struct callchain_cpus_entries *entries;
 -
 -      entries = callchain_cpus_entries;
 -      rcu_assign_pointer(callchain_cpus_entries, NULL);
 -      call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
 -}
 -
 -static int alloc_callchain_buffers(void)
 -{
 -      int cpu;
 -      int size;
 -      struct callchain_cpus_entries *entries;
 -
 -      /*
 -       * We can't use the percpu allocation API for data that can be
 -       * accessed from NMI. Use a temporary manual per cpu allocation
 -       * until that gets sorted out.
 -       */
 -      size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
 -
 -      entries = kzalloc(size, GFP_KERNEL);
 -      if (!entries)
 -              return -ENOMEM;
 -
 -      size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
 -
 -      for_each_possible_cpu(cpu) {
 -              entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
 -                                                       cpu_to_node(cpu));
 -              if (!entries->cpu_entries[cpu])
 -                      goto fail;
 -      }
 -
 -      rcu_assign_pointer(callchain_cpus_entries, entries);
 -
 -      return 0;
 -
 -fail:
 -      for_each_possible_cpu(cpu)
 -              kfree(entries->cpu_entries[cpu]);
 -      kfree(entries);
 -
 -      return -ENOMEM;
 -}
 -
 -static int get_callchain_buffers(void)
 -{
 -      int err = 0;
 -      int count;
 -
 -      mutex_lock(&callchain_mutex);
 -
 -      count = atomic_inc_return(&nr_callchain_events);
 -      if (WARN_ON_ONCE(count < 1)) {
 -              err = -EINVAL;
 -              goto exit;
 -      }
 -
 -      if (count > 1) {
 -              /* If the allocation failed, give up */
 -              if (!callchain_cpus_entries)
 -                      err = -ENOMEM;
 -              goto exit;
 -      }
 -
 -      err = alloc_callchain_buffers();
 -      if (err)
 -              release_callchain_buffers();
 -exit:
 -      mutex_unlock(&callchain_mutex);
 -
 -      return err;
 -}
 -
 -static void put_callchain_buffers(void)
 -{
 -      if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
 -              release_callchain_buffers();
 -              mutex_unlock(&callchain_mutex);
 -      }
 -}
 -
 -static int get_recursion_context(int *recursion)
 -{
 -      int rctx;
 -
 -      if (in_nmi())
 -              rctx = 3;
 -      else if (in_irq())
 -              rctx = 2;
 -      else if (in_softirq())
 -              rctx = 1;
 -      else
 -              rctx = 0;
 -
 -      if (recursion[rctx])
 -              return -1;
 -
 -      recursion[rctx]++;
 -      barrier();
 -
 -      return rctx;
 -}
 -
 -static inline void put_recursion_context(int *recursion, int rctx)
 -{
 -      barrier();
 -      recursion[rctx]--;
 -}
 -
 -static struct perf_callchain_entry *get_callchain_entry(int *rctx)
 -{
 -      int cpu;
 -      struct callchain_cpus_entries *entries;
 -
 -      *rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
 -      if (*rctx == -1)
 -              return NULL;
 -
 -      entries = rcu_dereference(callchain_cpus_entries);
 -      if (!entries)
 -              return NULL;
 -
 -      cpu = smp_processor_id();
 -
 -      return &entries->cpu_entries[cpu][*rctx];
 -}
 -
 -static void
 -put_callchain_entry(int rctx)
 -{
 -      put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
 -}
 -
 -static struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
 -{
 -      int rctx;
 -      struct perf_callchain_entry *entry;
 -
 -
 -      entry = get_callchain_entry(&rctx);
 -      if (rctx == -1)
 -              return NULL;
 -
 -      if (!entry)
 -              goto exit_put;
 -
 -      entry->nr = 0;
 -
 -      if (!user_mode(regs)) {
 -              perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
 -              perf_callchain_kernel(entry, regs);
 -              if (current->mm)
 -                      regs = task_pt_regs(current);
 -              else
 -                      regs = NULL;
 -      }
 -
 -      if (regs) {
 -              perf_callchain_store(entry, PERF_CONTEXT_USER);
 -              perf_callchain_user(entry, regs);
 -      }
 -
 -exit_put:
 -      put_callchain_entry(rctx);
 -
 -      return entry;
 -}
 -
  /*
   * Initialize the perf_event context in a task_struct:
   */
@@@ -2983,12 -3194,33 +2987,33 @@@ static unsigned int perf_poll(struct fi
        struct ring_buffer *rb;
        unsigned int events = POLL_HUP;
  
+       /*
+        * Race between perf_event_set_output() and perf_poll(): perf_poll()
+        * grabs the rb reference but perf_event_set_output() overrides it.
+        * Here is the timeline for two threads T1, T2:
+        * t0: T1, rb = rcu_dereference(event->rb)
+        * t1: T2, old_rb = event->rb
+        * t2: T2, event->rb = new rb
+        * t3: T2, ring_buffer_detach(old_rb)
+        * t4: T1, ring_buffer_attach(rb1)
+        * t5: T1, poll_wait(event->waitq)
+        *
+        * To avoid this problem, we grab mmap_mutex in perf_poll()
+        * thereby ensuring that the assignment of the new ring buffer
+        * and the detachment of the old buffer appear atomic to perf_poll()
+        */
+       mutex_lock(&event->mmap_mutex);
        rcu_read_lock();
        rb = rcu_dereference(event->rb);
-       if (rb)
+       if (rb) {
+               ring_buffer_attach(event, rb);
                events = atomic_xchg(&rb->poll, 0);
+       }
        rcu_read_unlock();
  
+       mutex_unlock(&event->mmap_mutex);
        poll_wait(file, &event->waitq, wait);
  
        return events;
@@@ -3289,6 -3521,49 +3314,49 @@@ unlock
        return ret;
  }
  
+ static void ring_buffer_attach(struct perf_event *event,
+                              struct ring_buffer *rb)
+ {
+       unsigned long flags;
+       if (!list_empty(&event->rb_entry))
+               return;
+       spin_lock_irqsave(&rb->event_lock, flags);
+       if (!list_empty(&event->rb_entry))
+               goto unlock;
+       list_add(&event->rb_entry, &rb->event_list);
+ unlock:
+       spin_unlock_irqrestore(&rb->event_lock, flags);
+ }
+ static void ring_buffer_detach(struct perf_event *event,
+                              struct ring_buffer *rb)
+ {
+       unsigned long flags;
+       if (list_empty(&event->rb_entry))
+               return;
+       spin_lock_irqsave(&rb->event_lock, flags);
+       list_del_init(&event->rb_entry);
+       wake_up_all(&event->waitq);
+       spin_unlock_irqrestore(&rb->event_lock, flags);
+ }
+ static void ring_buffer_wakeup(struct perf_event *event)
+ {
+       struct ring_buffer *rb;
+       rcu_read_lock();
+       rb = rcu_dereference(event->rb);
+       list_for_each_entry_rcu(event, &rb->event_list, rb_entry) {
+               wake_up_all(&event->waitq);
+       }
+       rcu_read_unlock();
+ }
  static void rb_free_rcu(struct rcu_head *rcu_head)
  {
        struct ring_buffer *rb;
@@@ -3314,9 -3589,19 +3382,19 @@@ static struct ring_buffer *ring_buffer_
  
  static void ring_buffer_put(struct ring_buffer *rb)
  {
+       struct perf_event *event, *n;
+       unsigned long flags;
        if (!atomic_dec_and_test(&rb->refcount))
                return;
  
+       spin_lock_irqsave(&rb->event_lock, flags);
+       list_for_each_entry_safe(event, n, &rb->event_list, rb_entry) {
+               list_del_init(&event->rb_entry);
+               wake_up_all(&event->waitq);
+       }
+       spin_unlock_irqrestore(&rb->event_lock, flags);
        call_rcu(&rb->rcu_head, rb_free_rcu);
  }
  
@@@ -3339,6 -3624,7 +3417,7 @@@ static void perf_mmap_close(struct vm_a
                atomic_long_sub((size >> PAGE_SHIFT) + 1, &user->locked_vm);
                vma->vm_mm->pinned_vm -= event->mmap_locked;
                rcu_assign_pointer(event->rb, NULL);
+               ring_buffer_detach(event, rb);
                mutex_unlock(&event->mmap_mutex);
  
                ring_buffer_put(rb);
@@@ -3493,7 -3779,7 +3572,7 @@@ static const struct file_operations per
  
  void perf_event_wakeup(struct perf_event *event)
  {
-       wake_up_all(&event->waitq);
+       ring_buffer_wakeup(event);
  
        if (event->pending_kill) {
                kill_fasync(&event->fasync, SIGIO, event->pending_kill);
@@@ -4530,6 -4816,7 +4609,6 @@@ static void perf_swevent_overflow(struc
        struct hw_perf_event *hwc = &event->hw;
        int throttle = 0;
  
 -      data->period = event->hw.last_period;
        if (!overflow)
                overflow = perf_swevent_set_period(event);
  
@@@ -4563,12 -4850,6 +4642,12 @@@ static void perf_swevent_event(struct p
        if (!is_sampling_event(event))
                return;
  
 +      if ((event->attr.sample_type & PERF_SAMPLE_PERIOD) && !event->attr.freq) {
 +              data->period = nr;
 +              return perf_swevent_overflow(event, 1, data, regs);
 +      } else
 +              data->period = event->hw.last_period;
 +
        if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq)
                return perf_swevent_overflow(event, 1, data, regs);
  
@@@ -5620,6 -5901,8 +5699,8 @@@ perf_event_alloc(struct perf_event_att
        INIT_LIST_HEAD(&event->group_entry);
        INIT_LIST_HEAD(&event->event_entry);
        INIT_LIST_HEAD(&event->sibling_list);
+       INIT_LIST_HEAD(&event->rb_entry);
        init_waitqueue_head(&event->waitq);
        init_irq_work(&event->pending, perf_pending_event);
  
@@@ -5826,6 -6109,8 +5907,8 @@@ set
  
        old_rb = event->rb;
        rcu_assign_pointer(event->rb, rb);
+       if (old_rb)
+               ring_buffer_detach(event, old_rb);
        ret = 0;
  unlock:
        mutex_unlock(&event->mmap_mutex);
diff --combined kernel/events/internal.h
index be4a43f6de4fbd371953ade2f46e4325b9efcceb,64568a699375f105232eb588963da8707f926295..b0b107f90afc983a0a7abd4b2f0f9519670f2275
@@@ -1,10 -1,6 +1,10 @@@
  #ifndef _KERNEL_EVENTS_INTERNAL_H
  #define _KERNEL_EVENTS_INTERNAL_H
  
 +#include <linux/hardirq.h>
 +
 +/* Buffer handling */
 +
  #define RING_BUFFER_WRITABLE          0x01
  
  struct ring_buffer {
@@@ -26,6 -22,9 +26,9 @@@
        local_t                         lost;           /* nr records lost   */
  
        long                            watermark;      /* wakeup watermark  */
+       /* poll crap */
+       spinlock_t                      event_lock;
+       struct list_head                event_list;
  
        struct perf_event_mmap_page     *user_page;
        void                            *data_pages[0];
@@@ -68,7 -67,7 +71,7 @@@ static inline int page_order(struct rin
  }
  #endif
  
 -static unsigned long perf_data_size(struct ring_buffer *rb)
 +static inline unsigned long perf_data_size(struct ring_buffer *rb)
  {
        return rb->nr_pages << (PAGE_SHIFT + page_order(rb));
  }
@@@ -97,37 -96,4 +100,37 @@@ __output_copy(struct perf_output_handl
        } while (len);
  }
  
 +/* Callchain handling */
 +extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
 +extern int get_callchain_buffers(void);
 +extern void put_callchain_buffers(void);
 +
 +static inline int get_recursion_context(int *recursion)
 +{
 +      int rctx;
 +
 +      if (in_nmi())
 +              rctx = 3;
 +      else if (in_irq())
 +              rctx = 2;
 +      else if (in_softirq())
 +              rctx = 1;
 +      else
 +              rctx = 0;
 +
 +      if (recursion[rctx])
 +              return -1;
 +
 +      recursion[rctx]++;
 +      barrier();
 +
 +      return rctx;
 +}
 +
 +static inline void put_recursion_context(int *recursion, int rctx)
 +{
 +      barrier();
 +      recursion[rctx]--;
 +}
 +
  #endif /* _KERNEL_EVENTS_INTERNAL_H */
index fdc6d22d406b65505ac51241401c4f7276b3392c,95dc31efd6dd503dbd169a159dac4c5048f5a568..f04cc3136bd370b6fbf9007cb7067ec1476892d4
  #include "trace.h"
  #include "trace_output.h"
  
 +#define DEFAULT_SYS_FILTER_MESSAGE                                    \
 +      "### global filter ###\n"                                       \
 +      "# Use this to set filters for multiple events.\n"              \
 +      "# Only events with the given fields will be affected.\n"       \
 +      "# If no events are modified, an error message will be displayed here"
 +
  enum filter_op_ids
  {
        OP_OR,
@@@ -652,7 -646,7 +652,7 @@@ void print_subsystem_event_filter(struc
        if (filter && filter->filter_string)
                trace_seq_printf(s, "%s\n", filter->filter_string);
        else
 -              trace_seq_printf(s, "none\n");
 +              trace_seq_printf(s, DEFAULT_SYS_FILTER_MESSAGE "\n");
        mutex_unlock(&event_mutex);
  }
  
@@@ -1697,7 -1691,7 +1697,7 @@@ static int replace_system_preds(struct 
                 * replace the filter for the call.
                 */
                filter = call->filter;
-               call->filter = filter_item->filter;
+               rcu_assign_pointer(call->filter, filter_item->filter);
                filter_item->filter = filter;
  
                fail = false;
@@@ -1752,7 -1746,7 +1752,7 @@@ int apply_event_filter(struct ftrace_ev
                filter = call->filter;
                if (!filter)
                        goto out_unlock;
-               call->filter = NULL;
+               RCU_INIT_POINTER(call->filter, NULL);
                /* Make sure the filter is not being used */
                synchronize_sched();
                __free_filter(filter);
@@@ -1793,7 -1787,7 +1793,7 @@@ out
         * string
         */
        tmp = call->filter;
-       call->filter = filter;
+       rcu_assign_pointer(call->filter, filter);
        if (tmp) {
                /* Make sure the call is done with the filter */
                synchronize_sched();
@@@ -1844,10 -1838,7 +1844,10 @@@ int apply_subsystem_event_filter(struc
        if (!filter)
                goto out;
  
 -      replace_filter_string(filter, filter_string);
 +      /* System filters just show a default message */
 +      kfree(filter->filter_string);
 +      filter->filter_string = NULL;
 +
        /*
         * No event actually uses the system filter
         * we can free it without synchronize_sched().
  
        parse_init(ps, filter_ops, filter_string);
        err = filter_parse(ps);
 -      if (err) {
 -              append_filter_err(ps, system->filter);
 -              goto out;
 -      }
 +      if (err)
 +              goto err_filter;
  
        err = replace_system_preds(system, ps, filter_string);
        if (err)
 -              append_filter_err(ps, system->filter);
 +              goto err_filter;
  
  out:
        filter_opstack_clear(ps);
@@@ -1872,11 -1865,6 +1872,11 @@@ out_unlock
        mutex_unlock(&event_mutex);
  
        return err;
 +
 +err_filter:
 +      replace_filter_string(filter, filter_string);
 +      append_filter_err(ps, system->filter);
 +      goto out;
  }
  
  #ifdef CONFIG_PERF_EVENTS
diff --combined tools/perf/util/evsel.c
index b38eaa34b28e2fe8b9ffef125f6bab915b35ce3e,d7915d4e77cb629e4560d499ac2c1c902ecce5db..ee68d6944e61883ca62660b1071e30ff3e4ddd06
@@@ -34,6 -34,16 +34,16 @@@ int __perf_evsel__sample_size(u64 sampl
        return size;
  }
  
+ static void hists__init(struct hists *hists)
+ {
+       memset(hists, 0, sizeof(*hists));
+       hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
+       hists->entries_in = &hists->entries_in_array[0];
+       hists->entries_collapsed = RB_ROOT;
+       hists->entries = RB_ROOT;
+       pthread_mutex_init(&hists->lock, NULL);
+ }
  void perf_evsel__init(struct perf_evsel *evsel,
                      struct perf_event_attr *attr, int idx)
  {
@@@ -53,76 -63,6 +63,76 @@@ struct perf_evsel *perf_evsel__new(stru
        return evsel;
  }
  
 +void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
 +{
 +      struct perf_event_attr *attr = &evsel->attr;
 +      int track = !evsel->idx; /* only the first counter needs these */
 +
 +      attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0;
 +      attr->inherit       = !opts->no_inherit;
 +      attr->read_format   = PERF_FORMAT_TOTAL_TIME_ENABLED |
 +                            PERF_FORMAT_TOTAL_TIME_RUNNING |
 +                            PERF_FORMAT_ID;
 +
 +      attr->sample_type  |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
 +
 +      /*
 +       * We default some events to a 1 default interval. But keep
 +       * it a weak assumption overridable by the user.
 +       */
 +      if (!attr->sample_period || (opts->user_freq != UINT_MAX &&
 +                                   opts->user_interval != ULLONG_MAX)) {
 +              if (opts->freq) {
 +                      attr->sample_type       |= PERF_SAMPLE_PERIOD;
 +                      attr->freq              = 1;
 +                      attr->sample_freq       = opts->freq;
 +              } else {
 +                      attr->sample_period = opts->default_interval;
 +              }
 +      }
 +
 +      if (opts->no_samples)
 +              attr->sample_freq = 0;
 +
 +      if (opts->inherit_stat)
 +              attr->inherit_stat = 1;
 +
 +      if (opts->sample_address) {
 +              attr->sample_type       |= PERF_SAMPLE_ADDR;
 +              attr->mmap_data = track;
 +      }
 +
 +      if (opts->call_graph)
 +              attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
 +
 +      if (opts->system_wide)
 +              attr->sample_type       |= PERF_SAMPLE_CPU;
 +
 +      if (opts->sample_id_all_avail &&
 +          (opts->sample_time || opts->system_wide ||
 +           !opts->no_inherit || opts->cpu_list))
 +              attr->sample_type       |= PERF_SAMPLE_TIME;
 +
 +      if (opts->raw_samples) {
 +              attr->sample_type       |= PERF_SAMPLE_TIME;
 +              attr->sample_type       |= PERF_SAMPLE_RAW;
 +              attr->sample_type       |= PERF_SAMPLE_CPU;
 +      }
 +
 +      if (opts->no_delay) {
 +              attr->watermark = 0;
 +              attr->wakeup_events = 1;
 +      }
 +
 +      attr->mmap = track;
 +      attr->comm = track;
 +
 +      if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) {
 +              attr->disabled = 1;
 +              attr->enable_on_exec = 1;
 +      }
 +}
 +
  int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
  {
        int cpu, thread;
diff --combined tools/perf/util/hist.h
index 6676d558b2a76e3497f2cb34536fdd49269630d9,89289c8e935e78973a906fb96edd164973d20ac9..ff6f9d56ea4192531241474bf07e09a78dcf5695
@@@ -63,8 -63,6 +63,6 @@@ struct hists 
        struct callchain_cursor callchain_cursor;
  };
  
- void hists__init(struct hists *hists);
  struct hist_entry *__hists__add_entry(struct hists *self,
                                      struct addr_location *al,
                                      struct symbol *parent, u64 period);
@@@ -119,6 -117,7 +117,6 @@@ int perf_evlist__tui_browse_hists(struc
  
  static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
                                           int evidx __used,
 -                                         int nr_events __used,
                                           void(*timer)(void *arg) __used,
                                           void *arg __used,
                                           int delay_secs __used)
  #define K_RIGHT -2
  #else
  #include "ui/keysyms.h"
 -int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
 +int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
                             void(*timer)(void *arg), void *arg, int delay_secs);
  
  int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
index 7d159088c4acc20114904c76e6974b95c4e15351,0f4555ce90635a767f4a609b917905b5f58bdd0a..d9318d8a9ba1aabd00520c29119e36e5fb5e677f
@@@ -10,7 -10,6 +10,7 @@@
  #include "evlist.h"
  #include "evsel.h"
  #include "session.h"
 +#include "tool.h"
  #include "sort.h"
  #include "util.h"
  #include "cpumap.h"
@@@ -79,13 -78,39 +79,13 @@@ out_close
        return -1;
  }
  
 -static void perf_session__id_header_size(struct perf_session *session)
 -{
 -       struct perf_sample *data;
 -       u64 sample_type = session->sample_type;
 -       u16 size = 0;
 -
 -      if (!session->sample_id_all)
 -              goto out;
 -
 -       if (sample_type & PERF_SAMPLE_TID)
 -               size += sizeof(data->tid) * 2;
 -
 -       if (sample_type & PERF_SAMPLE_TIME)
 -               size += sizeof(data->time);
 -
 -       if (sample_type & PERF_SAMPLE_ID)
 -               size += sizeof(data->id);
 -
 -       if (sample_type & PERF_SAMPLE_STREAM_ID)
 -               size += sizeof(data->stream_id);
 -
 -       if (sample_type & PERF_SAMPLE_CPU)
 -               size += sizeof(data->cpu) * 2;
 -out:
 -       session->id_hdr_size = size;
 -}
 -
  void perf_session__update_sample_type(struct perf_session *self)
  {
        self->sample_type = perf_evlist__sample_type(self->evlist);
        self->sample_size = __perf_evsel__sample_size(self->sample_type);
        self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
 -      perf_session__id_header_size(self);
 +      self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
 +      self->host_machine.id_hdr_size = self->id_hdr_size;
  }
  
  int perf_session__create_kernel_maps(struct perf_session *self)
@@@ -105,7 -130,7 +105,7 @@@ static void perf_session__destroy_kerne
  
  struct perf_session *perf_session__new(const char *filename, int mode,
                                       bool force, bool repipe,
 -                                     struct perf_event_ops *ops)
 +                                     struct perf_tool *tool)
  {
        size_t len = filename ? strlen(filename) + 1 : 0;
        struct perf_session *self = zalloc(sizeof(*self) + len);
                goto out;
  
        memcpy(self->filename, filename, len);
 -      self->threads = RB_ROOT;
 -      INIT_LIST_HEAD(&self->dead_threads);
 -      self->last_match = NULL;
        /*
         * On 64bit we can mmap the data file in one go. No need for tiny mmap
         * slices. On 32bit we use 32MB.
                        goto out_delete;
        }
  
 -      if (ops && ops->ordering_requires_timestamps &&
 -          ops->ordered_samples && !self->sample_id_all) {
 +      if (tool && tool->ordering_requires_timestamps &&
 +          tool->ordered_samples && !self->sample_id_all) {
                dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
 -              ops->ordered_samples = false;
 +              tool->ordered_samples = false;
        }
  
  out:
@@@ -156,22 -184,17 +156,22 @@@ out_delete
        return NULL;
  }
  
 -static void perf_session__delete_dead_threads(struct perf_session *self)
 +static void machine__delete_dead_threads(struct machine *machine)
  {
        struct thread *n, *t;
  
 -      list_for_each_entry_safe(t, n, &self->dead_threads, node) {
 +      list_for_each_entry_safe(t, n, &machine->dead_threads, node) {
                list_del(&t->node);
                thread__delete(t);
        }
  }
  
 -static void perf_session__delete_threads(struct perf_session *self)
 +static void perf_session__delete_dead_threads(struct perf_session *session)
 +{
 +      machine__delete_dead_threads(&session->host_machine);
 +}
 +
 +static void machine__delete_threads(struct machine *self)
  {
        struct rb_node *nd = rb_first(&self->threads);
  
        }
  }
  
 +static void perf_session__delete_threads(struct perf_session *session)
 +{
 +      machine__delete_threads(&session->host_machine);
 +}
 +
  void perf_session__delete(struct perf_session *self)
  {
        perf_session__destroy_kernel_maps(self);
        free(self);
  }
  
 -void perf_session__remove_thread(struct perf_session *self, struct thread *th)
 +void machine__remove_thread(struct machine *self, struct thread *th)
  {
        self->last_match = NULL;
        rb_erase(&th->rb_node, &self->threads);
@@@ -218,16 -236,16 +218,16 @@@ static bool symbol__match_parent_regex(
        return 0;
  }
  
 -int perf_session__resolve_callchain(struct perf_session *self,
 -                                  struct thread *thread,
 -                                  struct ip_callchain *chain,
 -                                  struct symbol **parent)
 +int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
 +                             struct thread *thread,
 +                             struct ip_callchain *chain,
 +                             struct symbol **parent)
  {
        u8 cpumode = PERF_RECORD_MISC_USER;
        unsigned int i;
        int err;
  
 -      callchain_cursor_reset(&self->callchain_cursor);
 +      callchain_cursor_reset(&evsel->hists.callchain_cursor);
  
        for (i = 0; i < chain->nr; i++) {
                u64 ip;
  
                al.filtered = false;
                thread__find_addr_location(thread, self, cpumode,
 -                              MAP__FUNCTION, thread->pid, ip, &al, NULL);
 +                                         MAP__FUNCTION, ip, &al, NULL);
                if (al.sym != NULL) {
                        if (sort__has_parent && !*parent &&
                            symbol__match_parent_regex(al.sym))
                                break;
                }
  
 -              err = callchain_cursor_append(&self->callchain_cursor,
 +              err = callchain_cursor_append(&evsel->hists.callchain_cursor,
                                              ip, al.map, al.sym);
                if (err)
                        return err;
        return 0;
  }
  
 -static int process_event_synth_stub(union perf_event *event __used,
 -                                  struct perf_session *session __used)
 +static int process_event_synth_tracing_data_stub(union perf_event *event __used,
 +                                               struct perf_session *session __used)
 +{
 +      dump_printf(": unhandled!\n");
 +      return 0;
 +}
 +
 +static int process_event_synth_attr_stub(union perf_event *event __used,
 +                                       struct perf_evlist **pevlist __used)
  {
        dump_printf(": unhandled!\n");
        return 0;
  }
  
 -static int process_event_sample_stub(union perf_event *event __used,
 +static int process_event_sample_stub(struct perf_tool *tool __used,
 +                                   union perf_event *event __used,
                                     struct perf_sample *sample __used,
                                     struct perf_evsel *evsel __used,
 -                                   struct perf_session *session __used)
 +                                   struct machine *machine __used)
  {
        dump_printf(": unhandled!\n");
        return 0;
  }
  
 -static int process_event_stub(union perf_event *event __used,
 +static int process_event_stub(struct perf_tool *tool __used,
 +                            union perf_event *event __used,
                              struct perf_sample *sample __used,
 -                            struct perf_session *session __used)
 +                            struct machine *machine __used)
 +{
 +      dump_printf(": unhandled!\n");
 +      return 0;
 +}
 +
 +static int process_finished_round_stub(struct perf_tool *tool __used,
 +                                     union perf_event *event __used,
 +                                     struct perf_session *perf_session __used)
  {
        dump_printf(": unhandled!\n");
        return 0;
  }
  
 -static int process_finished_round_stub(union perf_event *event __used,
 -                                     struct perf_session *session __used,
 -                                     struct perf_event_ops *ops __used)
 +static int process_event_type_stub(struct perf_tool *tool __used,
 +                                 union perf_event *event __used)
  {
        dump_printf(": unhandled!\n");
        return 0;
  }
  
 -static int process_finished_round(union perf_event *event,
 -                                struct perf_session *session,
 -                                struct perf_event_ops *ops);
 +static int process_finished_round(struct perf_tool *tool,
 +                                union perf_event *event,
 +                                struct perf_session *session);
  
 -static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
 +static void perf_tool__fill_defaults(struct perf_tool *tool)
  {
 -      if (handler->sample == NULL)
 -              handler->sample = process_event_sample_stub;
 -      if (handler->mmap == NULL)
 -              handler->mmap = process_event_stub;
 -      if (handler->comm == NULL)
 -              handler->comm = process_event_stub;
 -      if (handler->fork == NULL)
 -              handler->fork = process_event_stub;
 -      if (handler->exit == NULL)
 -              handler->exit = process_event_stub;
 -      if (handler->lost == NULL)
 -              handler->lost = perf_event__process_lost;
 -      if (handler->read == NULL)
 -              handler->read = process_event_stub;
 -      if (handler->throttle == NULL)
 -              handler->throttle = process_event_stub;
 -      if (handler->unthrottle == NULL)
 -              handler->unthrottle = process_event_stub;
 -      if (handler->attr == NULL)
 -              handler->attr = process_event_synth_stub;
 -      if (handler->event_type == NULL)
 -              handler->event_type = process_event_synth_stub;
 -      if (handler->tracing_data == NULL)
 -              handler->tracing_data = process_event_synth_stub;
 -      if (handler->build_id == NULL)
 -              handler->build_id = process_event_synth_stub;
 -      if (handler->finished_round == NULL) {
 -              if (handler->ordered_samples)
 -                      handler->finished_round = process_finished_round;
 +      if (tool->sample == NULL)
 +              tool->sample = process_event_sample_stub;
 +      if (tool->mmap == NULL)
 +              tool->mmap = process_event_stub;
 +      if (tool->comm == NULL)
 +              tool->comm = process_event_stub;
 +      if (tool->fork == NULL)
 +              tool->fork = process_event_stub;
 +      if (tool->exit == NULL)
 +              tool->exit = process_event_stub;
 +      if (tool->lost == NULL)
 +              tool->lost = perf_event__process_lost;
 +      if (tool->read == NULL)
 +              tool->read = process_event_sample_stub;
 +      if (tool->throttle == NULL)
 +              tool->throttle = process_event_stub;
 +      if (tool->unthrottle == NULL)
 +              tool->unthrottle = process_event_stub;
 +      if (tool->attr == NULL)
 +              tool->attr = process_event_synth_attr_stub;
 +      if (tool->event_type == NULL)
 +              tool->event_type = process_event_type_stub;
 +      if (tool->tracing_data == NULL)
 +              tool->tracing_data = process_event_synth_tracing_data_stub;
 +      if (tool->build_id == NULL)
 +              tool->build_id = process_finished_round_stub;
 +      if (tool->finished_round == NULL) {
 +              if (tool->ordered_samples)
 +                      tool->finished_round = process_finished_round;
                else
 -                      handler->finished_round = process_finished_round_stub;
 +                      tool->finished_round = process_finished_round_stub;
        }
  }
  
@@@ -488,11 -490,11 +488,11 @@@ static void perf_session_free_sample_bu
  static int perf_session_deliver_event(struct perf_session *session,
                                      union perf_event *event,
                                      struct perf_sample *sample,
 -                                    struct perf_event_ops *ops,
 +                                    struct perf_tool *tool,
                                      u64 file_offset);
  
  static void flush_sample_queue(struct perf_session *s,
 -                             struct perf_event_ops *ops)
 +                             struct perf_tool *tool)
  {
        struct ordered_samples *os = &s->ordered_samples;
        struct list_head *head = &os->samples;
        unsigned idx = 0, progress_next = os->nr_samples / 16;
        int ret;
  
 -      if (!ops->ordered_samples || !limit)
 +      if (!tool->ordered_samples || !limit)
                return;
  
        list_for_each_entry_safe(iter, tmp, head, list) {
                if (ret)
                        pr_err("Can't parse sample, err = %d\n", ret);
                else
 -                      perf_session_deliver_event(s, iter->event, &sample, ops,
 +                      perf_session_deliver_event(s, iter->event, &sample, tool,
                                                   iter->file_offset);
  
                os->last_flush = iter->timestamp;
   *      Flush every events below timestamp 7
   *      etc...
   */
 -static int process_finished_round(union perf_event *event __used,
 -                                struct perf_session *session,
 -                                struct perf_event_ops *ops)
 +static int process_finished_round(struct perf_tool *tool,
 +                                union perf_event *event __used,
 +                                struct perf_session *session)
  {
 -      flush_sample_queue(session, ops);
 +      flush_sample_queue(session, tool);
        session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
  
        return 0;
@@@ -735,26 -737,13 +735,26 @@@ static void dump_sample(struct perf_ses
                callchain__printf(sample);
  }
  
 +static struct machine *
 +      perf_session__find_machine_for_cpumode(struct perf_session *session,
 +                                             union perf_event *event)
 +{
 +      const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 +
 +      if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest)
 +              return perf_session__find_machine(session, event->ip.pid);
 +
 +      return perf_session__find_host_machine(session);
 +}
 +
  static int perf_session_deliver_event(struct perf_session *session,
                                      union perf_event *event,
                                      struct perf_sample *sample,
 -                                    struct perf_event_ops *ops,
 +                                    struct perf_tool *tool,
                                      u64 file_offset)
  {
        struct perf_evsel *evsel;
 +      struct machine *machine;
  
        dump_event(session, event, file_offset, sample);
  
                hists__inc_nr_events(&evsel->hists, event->header.type);
        }
  
 +      machine = perf_session__find_machine_for_cpumode(session, event);
 +
        switch (event->header.type) {
        case PERF_RECORD_SAMPLE:
                dump_sample(session, event, sample);
                        ++session->hists.stats.nr_unknown_id;
                        return -1;
                }
 -              return ops->sample(event, sample, evsel, session);
 +              return tool->sample(tool, event, sample, evsel, machine);
        case PERF_RECORD_MMAP:
 -              return ops->mmap(event, sample, session);
 +              return tool->mmap(tool, event, sample, machine);
        case PERF_RECORD_COMM:
 -              return ops->comm(event, sample, session);
 +              return tool->comm(tool, event, sample, machine);
        case PERF_RECORD_FORK:
 -              return ops->fork(event, sample, session);
 +              return tool->fork(tool, event, sample, machine);
        case PERF_RECORD_EXIT:
 -              return ops->exit(event, sample, session);
 +              return tool->exit(tool, event, sample, machine);
        case PERF_RECORD_LOST:
 -              return ops->lost(event, sample, session);
 +              if (tool->lost == perf_event__process_lost)
 +                      session->hists.stats.total_lost += event->lost.lost;
 +              return tool->lost(tool, event, sample, machine);
        case PERF_RECORD_READ:
 -              return ops->read(event, sample, session);
 +              return tool->read(tool, event, sample, evsel, machine);
        case PERF_RECORD_THROTTLE:
 -              return ops->throttle(event, sample, session);
 +              return tool->throttle(tool, event, sample, machine);
        case PERF_RECORD_UNTHROTTLE:
 -              return ops->unthrottle(event, sample, session);
 +              return tool->unthrottle(tool, event, sample, machine);
        default:
                ++session->hists.stats.nr_unknown_events;
                return -1;
@@@ -827,29 -812,24 +827,29 @@@ static int perf_session__preprocess_sam
  }
  
  static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
 -                                          struct perf_event_ops *ops, u64 file_offset)
 +                                          struct perf_tool *tool, u64 file_offset)
  {
 +      int err;
 +
        dump_event(session, event, file_offset, NULL);
  
        /* These events are processed right away */
        switch (event->header.type) {
        case PERF_RECORD_HEADER_ATTR:
 -              return ops->attr(event, session);
 +              err = tool->attr(event, &session->evlist);
 +              if (err == 0)
 +                      perf_session__update_sample_type(session);
 +              return err;
        case PERF_RECORD_HEADER_EVENT_TYPE:
 -              return ops->event_type(event, session);
 +              return tool->event_type(tool, event);
        case PERF_RECORD_HEADER_TRACING_DATA:
                /* setup for reading amidst mmap */
                lseek(session->fd, file_offset, SEEK_SET);
 -              return ops->tracing_data(event, session);
 +              return tool->tracing_data(event, session);
        case PERF_RECORD_HEADER_BUILD_ID:
 -              return ops->build_id(event, session);
 +              return tool->build_id(tool, event, session);
        case PERF_RECORD_FINISHED_ROUND:
 -              return ops->finished_round(event, session, ops);
 +              return tool->finished_round(tool, event, session);
        default:
                return -EINVAL;
        }
  
  static int perf_session__process_event(struct perf_session *session,
                                       union perf_event *event,
 -                                     struct perf_event_ops *ops,
 +                                     struct perf_tool *tool,
                                       u64 file_offset)
  {
        struct perf_sample sample;
        hists__inc_nr_events(&session->hists, event->header.type);
  
        if (event->header.type >= PERF_RECORD_USER_TYPE_START)
 -              return perf_session__process_user_event(session, event, ops, file_offset);
 +              return perf_session__process_user_event(session, event, tool, file_offset);
  
        /*
         * For all kernel events we get the sample data
        if (perf_session__preprocess_sample(session, event, &sample))
                return 0;
  
 -      if (ops->ordered_samples) {
 +      if (tool->ordered_samples) {
                ret = perf_session_queue_event(session, event, &sample,
                                               file_offset);
                if (ret != -ETIME)
                        return ret;
        }
  
 -      return perf_session_deliver_event(session, event, &sample, ops,
 +      return perf_session_deliver_event(session, event, &sample, tool,
                                          file_offset);
  }
  
@@@ -904,11 -884,6 +904,11 @@@ void perf_event_header__bswap(struct pe
        self->size = bswap_16(self->size);
  }
  
 +struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
 +{
 +      return machine__findnew_thread(&session->host_machine, pid);
 +}
 +
  static struct thread *perf_session__register_idle_thread(struct perf_session *self)
  {
        struct thread *thread = perf_session__findnew(self, 0);
  }
  
  static void perf_session__warn_about_errors(const struct perf_session *session,
 -                                          const struct perf_event_ops *ops)
 +                                          const struct perf_tool *tool)
  {
 -      if (ops->lost == perf_event__process_lost &&
 +      if (tool->lost == perf_event__process_lost &&
            session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) {
                ui__warning("Processed %d events and lost %d chunks!\n\n"
                            "Check IO/CPU overload!\n\n",
  volatile int session_done;
  
  static int __perf_session__process_pipe_events(struct perf_session *self,
 -                                             struct perf_event_ops *ops)
 +                                             struct perf_tool *tool)
  {
        union perf_event event;
        uint32_t size;
        int err;
        void *p;
  
 -      perf_event_ops__fill_defaults(ops);
 +      perf_tool__fill_defaults(tool);
  
        head = 0;
  more:
        }
  
        if (size == 0 ||
 -          (skip = perf_session__process_event(self, &event, ops, head)) < 0) {
 +          (skip = perf_session__process_event(self, &event, tool, head)) < 0) {
                dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
                            head, event.header.size, event.header.type);
                /*
  done:
        err = 0;
  out_err:
 -      perf_session__warn_about_errors(self, ops);
 +      perf_session__warn_about_errors(self, tool);
        perf_session_free_sample_buffers(self);
        return err;
  }
@@@ -1059,7 -1034,7 +1059,7 @@@ fetch_mmaped_event(struct perf_session 
  
  int __perf_session__process_events(struct perf_session *session,
                                   u64 data_offset, u64 data_size,
 -                                 u64 file_size, struct perf_event_ops *ops)
 +                                 u64 file_size, struct perf_tool *tool)
  {
        u64 head, page_offset, file_offset, file_pos, progress_next;
        int err, mmap_prot, mmap_flags, map_idx = 0;
        union perf_event *event;
        uint32_t size;
  
 -      perf_event_ops__fill_defaults(ops);
 +      perf_tool__fill_defaults(tool);
  
        page_size = sysconf(_SC_PAGESIZE);
  
@@@ -1123,7 -1098,7 +1123,7 @@@ more
        size = event->header.size;
  
        if (size == 0 ||
 -          perf_session__process_event(session, event, ops, file_pos) < 0) {
 +          perf_session__process_event(session, event, tool, file_pos) < 0) {
                dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
                            file_offset + head, event->header.size,
                            event->header.type);
        err = 0;
        /* do the final flush for ordered samples */
        session->ordered_samples.next_flush = ULLONG_MAX;
 -      flush_sample_queue(session, ops);
 +      flush_sample_queue(session, tool);
  out_err:
 -      perf_session__warn_about_errors(session, ops);
 +      perf_session__warn_about_errors(session, tool);
        perf_session_free_sample_buffers(session);
        return err;
  }
  
  int perf_session__process_events(struct perf_session *self,
 -                               struct perf_event_ops *ops)
 +                               struct perf_tool *tool)
  {
        int err;
  
                err = __perf_session__process_events(self,
                                                     self->header.data_offset,
                                                     self->header.data_size,
 -                                                   self->size, ops);
 +                                                   self->size, tool);
        else
 -              err = __perf_session__process_pipe_events(self, ops);
 +              err = __perf_session__process_pipe_events(self, tool);
  
        return err;
  }
@@@ -1188,8 -1163,9 +1188,8 @@@ bool perf_session__has_traces(struct pe
        return true;
  }
  
 -int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
 -                                           const char *symbol_name,
 -                                           u64 addr)
 +int maps__set_kallsyms_ref_reloc_sym(struct map **maps,
 +                                   const char *symbol_name, u64 addr)
  {
        char *bracket;
        enum map_type i;
@@@ -1248,27 -1224,6 +1248,27 @@@ size_t perf_session__fprintf_nr_events(
        return ret;
  }
  
 +size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
 +{
 +      /*
 +       * FIXME: Here we have to actually print all the machines in this
 +       * session, not just the host...
 +       */
 +      return machine__fprintf(&session->host_machine, fp);
 +}
 +
 +void perf_session__remove_thread(struct perf_session *session,
 +                               struct thread *th)
 +{
 +      /*
 +       * FIXME: This one makes no sense, we need to remove the thread from
 +       * the machine it belongs to, perf_session can have many machines, so
 +       * doing it always on ->host_machine is wrong.  Fix when auditing all
 +       * the 'perf kvm' code.
 +       */
 +      machine__remove_thread(&session->host_machine, th);
 +}
 +
  struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
                                              unsigned int type)
  {
        return NULL;
  }
  
 -void perf_session__print_ip(union perf_event *event,
 -                          struct perf_sample *sample,
 -                          struct perf_session *session,
 -                          int print_sym, int print_dso)
 +void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
 +                        struct machine *machine, struct perf_evsel *evsel,
 +                        int print_sym, int print_dso)
  {
        struct addr_location al;
        const char *symname, *dsoname;
 -      struct callchain_cursor *cursor = &session->callchain_cursor;
 +      struct callchain_cursor *cursor = &evsel->hists.callchain_cursor;
        struct callchain_cursor_node *node;
  
 -      if (perf_event__preprocess_sample(event, session, &al, sample,
 +      if (perf_event__preprocess_sample(event, machine, &al, sample,
                                          NULL) < 0) {
                error("problem processing %d event, skipping it.\n",
                        event->header.type);
  
        if (symbol_conf.use_callchain && sample->callchain) {
  
 -              if (perf_session__resolve_callchain(session, al.thread,
 +              if (machine__resolve_callchain(machine, evsel, al.thread,
                                                sample->callchain, NULL) != 0) {
                        if (verbose)
                                error("Failed to resolve callchain. Skipping\n");
@@@ -1377,6 -1333,10 +1377,10 @@@ int perf_session__cpu_bitmap(struct per
        }
  
        map = cpu_map__new(cpu_list);
+       if (map == NULL) {
+               pr_err("Invalid cpu_list\n");
+               return -1;
+       }
  
        for (i = 0; i < map->nr; i++) {
                int cpu = map->map[i];