Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 276394
b: refs/heads/master
c: dc440d1
h: refs/heads/master
v: v3
  • Loading branch information
Ingo Molnar committed Dec 5, 2011
1 parent aba1318 commit 97d97ea
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 26 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d3d9acf646679c1981032b0985b386d12fccc60c
refs/heads/master: dc440d10e1668b15fe704a23adb9b53fbbb24a44
29 changes: 18 additions & 11 deletions trunk/arch/x86/kernel/cpu/perf_event_amd_ibs.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@ static int force_ibs_eilvt_setup(void)
goto out;
}

pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset);
pr_err(FW_BUG "workaround enabled for IBS LVT offset\n");
pr_info("IBS: LVT offset %d assigned\n", offset);

return 0;
out:
Expand Down Expand Up @@ -265,19 +264,23 @@ perf_ibs_cpu_notifier(struct notifier_block *self, unsigned long action, void *h
static __init int amd_ibs_init(void)
{
u32 caps;
int ret;
int ret = -EINVAL;

caps = __get_ibs_caps();
if (!caps)
return -ENODEV; /* ibs not supported by the cpu */

if (!ibs_eilvt_valid()) {
ret = force_ibs_eilvt_setup();
if (ret) {
pr_err("Failed to setup IBS, %d\n", ret);
return ret;
}
}
/*
* Force LVT offset assignment for family 10h: The offsets are
* not assigned by the BIOS for this family, so the OS is
* responsible for doing it. If the OS assignment fails, fall
* back to BIOS settings and try to setup this.
*/
if (boot_cpu_data.x86 == 0x10)
force_ibs_eilvt_setup();

if (!ibs_eilvt_valid())
goto out;

get_online_cpus();
ibs_caps = caps;
Expand All @@ -287,7 +290,11 @@ static __init int amd_ibs_init(void)
smp_call_function(setup_APIC_ibs, NULL, 1);
put_online_cpus();

return perf_event_ibs_init();
ret = perf_event_ibs_init();
out:
if (ret)
pr_err("Failed to setup IBS, %d\n", ret);
return ret;
}

/* Since we need the pci subsystem to init ibs we can't do this earlier: */
Expand Down
8 changes: 8 additions & 0 deletions trunk/arch/x86/kernel/cpu/perf_event_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,13 @@ static void intel_clovertown_quirks(void)
x86_pmu.pebs_constraints = NULL;
}

static void intel_sandybridge_quirks(void)
{
printk(KERN_WARNING "PEBS disabled due to CPU errata.\n");
x86_pmu.pebs = 0;
x86_pmu.pebs_constraints = NULL;
}

__init int intel_pmu_init(void)
{
union cpuid10_edx edx;
Expand Down Expand Up @@ -1694,6 +1701,7 @@ __init int intel_pmu_init(void)
break;

case 42: /* SandyBridge */
x86_pmu.quirks = intel_sandybridge_quirks;
case 45: /* SandyBridge, "Romely-EP" */
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ struct perf_event {
int mmap_locked;
struct user_struct *mmap_user;
struct ring_buffer *rb;
struct list_head rb_entry;

/* poll related */
wait_queue_head_t waitq;
Expand Down
86 changes: 84 additions & 2 deletions trunk/kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx,
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)
Expand Down Expand Up @@ -3191,12 +3194,33 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
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;
Expand Down Expand Up @@ -3497,6 +3521,49 @@ static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
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;
Expand All @@ -3522,9 +3589,19 @@ static struct ring_buffer *ring_buffer_get(struct perf_event *event)

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);
}

Expand All @@ -3547,6 +3624,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
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);
Expand Down Expand Up @@ -3701,7 +3779,7 @@ static const struct file_operations perf_fops = {

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);
Expand Down Expand Up @@ -5823,6 +5901,8 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
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);

Expand Down Expand Up @@ -6029,6 +6109,8 @@ perf_event_set_output(struct perf_event *event, struct perf_event *output_event)

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);
Expand Down
3 changes: 3 additions & 0 deletions trunk/kernel/events/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct ring_buffer {
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];
Expand Down
3 changes: 3 additions & 0 deletions trunk/kernel/events/ring_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags)
rb->writable = 1;

atomic_set(&rb->refcount, 1);

INIT_LIST_HEAD(&rb->event_list);
spin_lock_init(&rb->event_lock);
}

#ifndef CONFIG_PERF_USE_VMALLOC
Expand Down
10 changes: 10 additions & 0 deletions trunk/tools/perf/util/evsel.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ int __perf_evsel__sample_size(u64 sample_type)
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)
{
Expand Down
10 changes: 0 additions & 10 deletions trunk/tools/perf/util/hist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1211,13 +1211,3 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)

return ret;
}

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);
}
2 changes: 0 additions & 2 deletions trunk/tools/perf/util/hist.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +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);
Expand Down
4 changes: 4 additions & 0 deletions trunk/tools/perf/util/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,10 @@ int perf_session__cpu_bitmap(struct perf_session *session,
}

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];
Expand Down

0 comments on commit 97d97ea

Please sign in to comment.