/*
* Hypervisor supplied "24x7" performance counter support
*
* Author: Cody P Schafer <cody@linux.vnet.ibm.com>
* Copyright 2014 IBM Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#define pr_fmt(fmt) "hv-24x7: " fmt
#include <linux/perf_event.h>
#include <linux/rbtree.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/firmware.h>
#include <asm/hvcall.h>
#include <asm/io.h>
#include <linux/byteorder/generic.h>
#include "hv-24x7.h"
#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) {
#define DOMAIN(n, v, x, c) \
case HV_PERF_DOMAIN_##n: \
/* fall through */
#include "hv-24x7-domains.h"
#undef DOMAIN
return true;
default:
return false;
}
}
static bool is_physical_domain(unsigned domain)
{
switch (domain) {
#define DOMAIN(n, v, x, c) \
case HV_PERF_DOMAIN_##n: \
return c;
#include "hv-24x7-domains.h"
#undef DOMAIN
default:
return false;
}
}
static bool catalog_entry_domain_is_valid(unsigned domain)
{
return is_physical_domain(domain);
}
/*
* TODO: Merging events:
* - Think of the hcall as an interface to a 4d array of counters:
* - x = domains
* - y = indexes in the domain (core, chip, vcpu, node, etc)
* - z = offset into the counter space
* - w = lpars (guest vms, "logical partitions")
* - A single request is: x,y,y_last,z,z_last,w,w_last
* - this means we can retrieve a rectangle of counters in y,z for a single x.
*
* - Things to consider (ignoring w):
* - input cost_per_request = 16
* - output cost_per_result(ys,zs) = 8 + 8 * ys + ys * zs
* - limited number of requests per hcall (must fit into 4K bytes)
* - 4k = 16 [buffer header] - 16 [request size] * request_count
* - 255 requests per hcall
* - sometimes it will be more efficient to read extra data and discard
*/
/*
* Example usage:
* perf stat -e 'hv_24x7/domain=2,offset=8,vcpu=0,lpar=0xffffffff/'
*/
/* u3 0-6, one of HV_24X7_PERF_DOMAIN */
EVENT_DEFINE_RANGE_FORMAT(domain, config, 0, 3);
/* u16 */
EVENT_DEFINE_RANGE_FORMAT(core, config, 16, 31);
EVENT_DEFINE_RANGE_FORMAT(vcpu, config, 16, 31);
/* u32, see "data_offset" */
EVENT_DEFINE_RANGE_FORMAT(offset, config, 32, 63);
/* u16 */
EVENT_DEFINE_RANGE_FORMAT(lpar, config1, 0, 15);
EVENT_DEFINE_RANGE(reserved1, config, 4, 15);
EVENT_DEFINE_RANGE(reserved2, config1, 16, 63);
EVENT_DEFINE_RANGE(reserved3, config2, 0, 63);
static struct attribute *format_attrs[] = {
&format_attr_domain.attr,
&format_attr_offset.attr,
&format_attr_core.attr,
&format_attr_vcpu.attr,
&format_attr_lpar.attr,
NULL,
};
static struct attribute_group format_group = {
.name = "format",
.attrs = format_attrs,
};
static struct attribute_group event_group = {
.name = "events",
/* .attrs is set in init */
};
static struct attribute_group event_desc_group = {
.name = "event_descs",
/* .attrs is set in init */
};
static struct attribute_group event_long_desc_group =