--- /dev/null
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE
+
+#include <elf.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include "event.h"
+#include "lib.h"
+#include "utils.h"
+
+/*
+ * Test that per-event excludes work.
+ */
+
+static int per_event_excludes(void)
+{
+       struct event *e, events[4];
+       char *platform;
+       int i;
+
+       platform = (char *)get_auxv_entry(AT_BASE_PLATFORM);
+       FAIL_IF(!platform);
+       SKIP_IF(strcmp(platform, "power8") != 0);
+
+       /*
+        * We need to create the events disabled, otherwise the running/enabled
+        * counts don't match up.
+        */
+       e = &events[0];
+       event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
+                       PERF_TYPE_HARDWARE, "instructions");
+       e->attr.disabled = 1;
+
+       e = &events[1];
+       event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
+                       PERF_TYPE_HARDWARE, "instructions(k)");
+       e->attr.disabled = 1;
+       e->attr.exclude_user = 1;
+       e->attr.exclude_hv = 1;
+
+       e = &events[2];
+       event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
+                       PERF_TYPE_HARDWARE, "instructions(h)");
+       e->attr.disabled = 1;
+       e->attr.exclude_user = 1;
+       e->attr.exclude_kernel = 1;
+
+       e = &events[3];
+       event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
+                       PERF_TYPE_HARDWARE, "instructions(u)");
+       e->attr.disabled = 1;
+       e->attr.exclude_hv = 1;
+       e->attr.exclude_kernel = 1;
+
+       FAIL_IF(event_open(&events[0]));
+
+       /*
+        * The open here will fail if we don't have per event exclude support,
+        * because the second event has an incompatible set of exclude settings
+        * and we're asking for the events to be in a group.
+        */
+       for (i = 1; i < 4; i++)
+               FAIL_IF(event_open_with_group(&events[i], events[0].fd));
+
+       /*
+        * Even though the above will fail without per-event excludes we keep
+        * testing in order to be thorough.
+        */
+       prctl(PR_TASK_PERF_EVENTS_ENABLE);
+
+       /* Spin for a while */
+       for (i = 0; i < INT_MAX; i++)
+               asm volatile("" : : : "memory");
+
+       prctl(PR_TASK_PERF_EVENTS_DISABLE);
+
+       for (i = 0; i < 4; i++) {
+               FAIL_IF(event_read(&events[i]));
+               event_report(&events[i]);
+       }
+
+       /*
+        * We should see that all events have enabled == running. That
+        * shows that they were all on the PMU at once.
+        */
+       for (i = 0; i < 4; i++)
+               FAIL_IF(events[i].result.running != events[i].result.enabled);
+
+       /*
+        * We can also check that the result for instructions is >= all the
+        * other counts. That's because it is counting all instructions while
+        * the others are counting a subset.
+        */
+       for (i = 1; i < 4; i++)
+               FAIL_IF(events[0].result.value < events[i].result.value);
+
+       for (i = 0; i < 4; i++)
+               event_close(&events[i]);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(per_event_excludes, "per_event_excludes");
+}