aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/perf/hv-24x7.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/perf/hv-24x7.c')
-rw-r--r--arch/powerpc/perf/hv-24x7.c233
1 files changed, 137 insertions, 96 deletions
diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c
index 9f9dfda9ed2c..2da41b78cb6d 100644
--- a/arch/powerpc/perf/hv-24x7.c
+++ b/arch/powerpc/perf/hv-24x7.c
@@ -27,20 +27,6 @@
#include "hv-24x7-catalog.h"
#include "hv-common.h"
-static const char *event_domain_suffix(unsigned domain)
-{
- switch (domain) {
-#define DOMAIN(n, v, x, c) \
- case HV_PERF_DOMAIN_##n: \
- return "__" #n;
-#include "hv-24x7-domains.h"
-#undef DOMAIN
- default:
- WARN(1, "unknown domain %d\n", domain);
- return "__UNKNOWN_DOMAIN_SUFFIX";
- }
-}
-
static bool domain_is_valid(unsigned domain)
{
switch (domain) {
@@ -68,6 +54,24 @@ static bool is_physical_domain(unsigned domain)
}
}
+static const char *domain_name(unsigned domain)
+{
+ if (!domain_is_valid(domain))
+ return NULL;
+
+ switch (domain) {
+ case HV_PERF_DOMAIN_PHYS_CHIP: return "Physical Chip";
+ case HV_PERF_DOMAIN_PHYS_CORE: return "Physical Core";
+ case HV_PERF_DOMAIN_VCPU_HOME_CORE: return "VCPU Home Core";
+ case HV_PERF_DOMAIN_VCPU_HOME_CHIP: return "VCPU Home Chip";
+ case HV_PERF_DOMAIN_VCPU_HOME_NODE: return "VCPU Home Node";
+ case HV_PERF_DOMAIN_VCPU_REMOTE_NODE: return "VCPU Remote Node";
+ }
+
+ WARN_ON_ONCE(domain);
+ return NULL;
+}
+
static bool catalog_entry_domain_is_valid(unsigned domain)
{
return is_physical_domain(domain);
@@ -101,6 +105,7 @@ static bool catalog_entry_domain_is_valid(unsigned domain)
EVENT_DEFINE_RANGE_FORMAT(domain, config, 0, 3);
/* u16 */
EVENT_DEFINE_RANGE_FORMAT(core, config, 16, 31);
+EVENT_DEFINE_RANGE_FORMAT(chip, config, 16, 31);
EVENT_DEFINE_RANGE_FORMAT(vcpu, config, 16, 31);
/* u32, see "data_offset" */
EVENT_DEFINE_RANGE_FORMAT(offset, config, 32, 63);
@@ -115,6 +120,7 @@ static struct attribute *format_attrs[] = {
&format_attr_domain.attr,
&format_attr_offset.attr,
&format_attr_core.attr,
+ &format_attr_chip.attr,
&format_attr_vcpu.attr,
&format_attr_lpar.attr,
NULL,
@@ -274,32 +280,70 @@ static unsigned long h_get_24x7_catalog_page(char page[],
version, index);
}
-static unsigned core_domains[] = {
- HV_PERF_DOMAIN_PHYS_CORE,
- HV_PERF_DOMAIN_VCPU_HOME_CORE,
- HV_PERF_DOMAIN_VCPU_HOME_CHIP,
- HV_PERF_DOMAIN_VCPU_HOME_NODE,
- HV_PERF_DOMAIN_VCPU_REMOTE_NODE,
-};
-/* chip event data always yeilds a single event, core yeilds multiple */
-#define MAX_EVENTS_PER_EVENT_DATA ARRAY_SIZE(core_domains)
-
+/*
+ * Each event we find in the catalog, will have a sysfs entry. Format the
+ * data for this sysfs entry based on the event's domain.
+ *
+ * Events belonging to the Chip domain can only be monitored in that domain.
+ * i.e the domain for these events is a fixed/knwon value.
+ *
+ * Events belonging to the Core domain can be monitored either in the physical
+ * core or in one of the virtual CPU domains. So the domain value for these
+ * events must be specified by the user (i.e is a required parameter). Format
+ * the Core events with 'domain=?' so the perf-tool can error check required
+ * parameters.
+ *
+ * NOTE: For the Core domain events, rather than making domain a required
+ * parameter we could default it to PHYS_CORE and allowe users to
+ * override the domain to one of the VCPU domains.
+ *
+ * However, this can make the interface a little inconsistent.
+ *
+ * If we set domain=2 (PHYS_CHIP) and allow user to override this field
+ * the user may be tempted to also modify the "offset=x" field in which
+ * can lead to confusing usage. Consider the HPM_PCYC (offset=0x18) and
+ * HPM_INST (offset=0x20) events. With:
+ *
+ * perf stat -e hv_24x7/HPM_PCYC,offset=0x20/
+ *
+ * we end up monitoring HPM_INST, while the command line has HPM_PCYC.
+ *
+ * By not assigning a default value to the domain for the Core events,
+ * we can have simple guidelines:
+ *
+ * - Specifying values for parameters with "=?" is required.
+ *
+ * - Specifying (i.e overriding) values for other parameters
+ * is undefined.
+ */
static char *event_fmt(struct hv_24x7_event_data *event, unsigned domain)
{
const char *sindex;
const char *lpar;
+ const char *domain_str;
+ char buf[8];
- if (is_physical_domain(domain)) {
+ switch (domain) {
+ case HV_PERF_DOMAIN_PHYS_CHIP:
+ snprintf(buf, sizeof(buf), "%d", domain);
+ domain_str = buf;
+ lpar = "0x0";
+ sindex = "chip";
+ break;
+ case HV_PERF_DOMAIN_PHYS_CORE:
+ domain_str = "?";
lpar = "0x0";
sindex = "core";
- } else {
+ break;
+ default:
+ domain_str = "?";
lpar = "?";
sindex = "vcpu";
}
return kasprintf(GFP_KERNEL,
- "domain=0x%x,offset=0x%x,%s=?,lpar=%s",
- domain,
+ "domain=%s,offset=0x%x,%s=?,lpar=%s",
+ domain_str,
be16_to_cpu(event->event_counter_offs) +
be16_to_cpu(event->event_group_record_offs),
sindex,
@@ -339,6 +383,15 @@ static struct attribute *device_str_attr_create_(char *name, char *str)
return &attr->attr.attr;
}
+/*
+ * Allocate and initialize strings representing event attributes.
+ *
+ * NOTE: The strings allocated here are never destroyed and continue to
+ * exist till shutdown. This is to allow us to create as many events
+ * from the catalog as possible, even if we encounter errors with some.
+ * In case of changes to error paths in future, these may need to be
+ * freed by the caller.
+ */
static struct attribute *device_str_attr_create(char *name, int name_max,
int name_nonce,
char *str, size_t str_max)
@@ -370,16 +423,6 @@ out_s:
return NULL;
}
-static void device_str_attr_destroy(struct attribute *attr)
-{
- struct dev_ext_attribute *d;
-
- d = container_of(attr, struct dev_ext_attribute, attr.attr);
- kfree(d->var);
- kfree(d->attr.attr.name);
- kfree(d);
-}
-
static struct attribute *event_to_attr(unsigned ix,
struct hv_24x7_event_data *event,
unsigned domain,
@@ -387,7 +430,6 @@ static struct attribute *event_to_attr(unsigned ix,
{
int event_name_len;
char *ev_name, *a_ev_name, *val;
- const char *ev_suffix;
struct attribute *attr;
if (!domain_is_valid(domain)) {
@@ -400,14 +442,13 @@ static struct attribute *event_to_attr(unsigned ix,
if (!val)
return NULL;
- ev_suffix = event_domain_suffix(domain);
ev_name = event_name(event, &event_name_len);
if (!nonce)
- a_ev_name = kasprintf(GFP_KERNEL, "%.*s%s",
- (int)event_name_len, ev_name, ev_suffix);
+ a_ev_name = kasprintf(GFP_KERNEL, "%.*s",
+ (int)event_name_len, ev_name);
else
- a_ev_name = kasprintf(GFP_KERNEL, "%.*s%s__%d",
- (int)event_name_len, ev_name, ev_suffix, nonce);
+ a_ev_name = kasprintf(GFP_KERNEL, "%.*s__%d",
+ (int)event_name_len, ev_name, nonce);
if (!a_ev_name)
goto out_val;
@@ -452,53 +493,14 @@ event_to_long_desc_attr(struct hv_24x7_event_data *event, int nonce)
return device_str_attr_create(name, nl, nonce, desc, dl);
}
-static ssize_t event_data_to_attrs(unsigned ix, struct attribute **attrs,
+static int event_data_to_attrs(unsigned ix, struct attribute **attrs,
struct hv_24x7_event_data *event, int nonce)
{
- unsigned i;
-
- switch (event->domain) {
- case HV_PERF_DOMAIN_PHYS_CHIP:
- *attrs = event_to_attr(ix, event, event->domain, nonce);
- return 1;
- case HV_PERF_DOMAIN_PHYS_CORE:
- for (i = 0; i < ARRAY_SIZE(core_domains); i++) {
- attrs[i] = event_to_attr(ix, event, core_domains[i],
- nonce);
- if (!attrs[i]) {
- pr_warn("catalog event %u: individual attr %u "
- "creation failure\n", ix, i);
- for (; i; i--)
- device_str_attr_destroy(attrs[i - 1]);
- return -1;
- }
- }
- return i;
- default:
- pr_warn("catalog event %u: domain %u is not allowed in the "
- "catalog\n", ix, event->domain);
+ *attrs = event_to_attr(ix, event, event->domain, nonce);
+ if (!*attrs)
return -1;
- }
-}
-static size_t event_to_attr_ct(struct hv_24x7_event_data *event)
-{
- switch (event->domain) {
- case HV_PERF_DOMAIN_PHYS_CHIP:
- return 1;
- case HV_PERF_DOMAIN_PHYS_CORE:
- return ARRAY_SIZE(core_domains);
- default:
- return 0;
- }
-}
-
-static unsigned long vmalloc_to_phys(void *v)
-{
- struct page *p = vmalloc_to_page(v);
-
- BUG_ON(!p);
- return page_to_phys(p) + offset_in_page(v);
+ return 0;
}
/* */
@@ -726,9 +728,8 @@ static int create_events_from_catalog(struct attribute ***events_,
goto e_free;
}
- if (SIZE_MAX / MAX_EVENTS_PER_EVENT_DATA - 1 < event_entry_count) {
- pr_err("event_entry_count %zu is invalid\n",
- event_entry_count);
+ if (SIZE_MAX - 1 < event_entry_count) {
+ pr_err("event_entry_count %zu is invalid\n", event_entry_count);
ret = -EIO;
goto e_free;
}
@@ -801,7 +802,7 @@ static int create_events_from_catalog(struct attribute ***events_,
continue;
}
- attr_max += event_to_attr_ct(event);
+ attr_max++;
}
event_idx_last = event_idx;
@@ -851,12 +852,12 @@ static int create_events_from_catalog(struct attribute ***events_,
nonce = event_uniq_add(&ev_uniq, name, nl, event->domain);
ct = event_data_to_attrs(event_idx, events + event_attr_ct,
event, nonce);
- if (ct <= 0) {
+ if (ct < 0) {
pr_warn("event %zu (%.*s) creation failure, skipping\n",
event_idx, nl, name);
junk_events++;
} else {
- event_attr_ct += ct;
+ event_attr_ct++;
event_descs[desc_ct] = event_to_desc_attr(event, nonce);
if (event_descs[desc_ct])
desc_ct++;
@@ -961,6 +962,27 @@ e_free:
return ret;
}
+static ssize_t domains_show(struct device *dev, struct device_attribute *attr,
+ char *page)
+{
+ int d, n, count = 0;
+ const char *str;
+
+ for (d = 0; d < HV_PERF_DOMAIN_MAX; d++) {
+ str = domain_name(d);
+ if (!str)
+ continue;
+
+ n = sprintf(page, "%d: %s\n", d, str);
+ if (n < 0)
+ break;
+
+ count += n;
+ page += n;
+ }
+ return count;
+}
+
#define PAGE_0_ATTR(_name, _fmt, _expr) \
static ssize_t _name##_show(struct device *dev, \
struct device_attribute *dev_attr, \
@@ -989,6 +1011,7 @@ PAGE_0_ATTR(catalog_version, "%lld\n",
PAGE_0_ATTR(catalog_len, "%lld\n",
(unsigned long long)be32_to_cpu(page_0->length) * 4096);
static BIN_ATTR_RO(catalog, 0/* real length varies */);
+static DEVICE_ATTR_RO(domains);
static struct bin_attribute *if_bin_attrs[] = {
&bin_attr_catalog,
@@ -998,6 +1021,7 @@ static struct bin_attribute *if_bin_attrs[] = {
static struct attribute *if_attrs[] = {
&dev_attr_catalog_len.attr,
&dev_attr_catalog_version.attr,
+ &dev_attr_domains.attr,
NULL,
};
@@ -1089,10 +1113,16 @@ static int add_event_to_24x7_request(struct perf_event *event,
return -EINVAL;
}
- if (is_physical_domain(event_get_domain(event)))
+ switch (event_get_domain(event)) {
+ case HV_PERF_DOMAIN_PHYS_CHIP:
+ idx = event_get_chip(event);
+ break;
+ case HV_PERF_DOMAIN_PHYS_CORE:
idx = event_get_core(event);
- else
+ break;
+ default:
idx = event_get_vcpu(event);
+ }
i = request_buffer->num_requests++;
req = &request_buffer->requests[i];
@@ -1208,11 +1238,12 @@ static int h_24x7_event_init(struct perf_event *event)
return -EACCES;
}
- /* see if the event complains */
+ /* Get the initial value of the counter for this event */
if (single_24x7_request(event, &ct)) {
pr_devel("test hcall failed\n");
return -EIO;
}
+ (void)local64_xchg(&event->hw.prev_count, ct);
return 0;
}
@@ -1275,6 +1306,16 @@ static void h_24x7_event_read(struct perf_event *event)
h24x7hw = &get_cpu_var(hv_24x7_hw);
h24x7hw->events[i] = event;
put_cpu_var(h24x7hw);
+ /*
+ * Clear the event count so we can compute the _change_
+ * in the 24x7 raw counter value at the end of the txn.
+ *
+ * Note that we could alternatively read the 24x7 value
+ * now and save its value in event->hw.prev_count. But
+ * that would require issuing a hcall, which would then
+ * defeat the purpose of using the txn interface.
+ */
+ local64_set(&event->count, 0);
}
put_cpu_var(hv_24x7_reqb);