18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Hypervisor supplied "24x7" performance counter support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Cody P Schafer <cody@linux.vnet.ibm.com>
68c2ecf20Sopenharmony_ci * Copyright 2014 IBM Corporation.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "hv-24x7: " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
128c2ecf20Sopenharmony_ci#include <linux/rbtree.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/cputhreads.h>
188c2ecf20Sopenharmony_ci#include <asm/firmware.h>
198c2ecf20Sopenharmony_ci#include <asm/hvcall.h>
208c2ecf20Sopenharmony_ci#include <asm/io.h>
218c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/rtas.h>
248c2ecf20Sopenharmony_ci#include "hv-24x7.h"
258c2ecf20Sopenharmony_ci#include "hv-24x7-catalog.h"
268c2ecf20Sopenharmony_ci#include "hv-common.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Version of the 24x7 hypervisor API that we should use in this machine. */
298c2ecf20Sopenharmony_cistatic int interface_version;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* Whether we have to aggregate result data for some domains. */
328c2ecf20Sopenharmony_cistatic bool aggregate_result_elements;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic cpumask_t hv_24x7_cpumask;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic bool domain_is_valid(unsigned domain)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	switch (domain) {
398c2ecf20Sopenharmony_ci#define DOMAIN(n, v, x, c)		\
408c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_##n:	\
418c2ecf20Sopenharmony_ci		/* fall through */
428c2ecf20Sopenharmony_ci#include "hv-24x7-domains.h"
438c2ecf20Sopenharmony_ci#undef DOMAIN
448c2ecf20Sopenharmony_ci		return true;
458c2ecf20Sopenharmony_ci	default:
468c2ecf20Sopenharmony_ci		return false;
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic bool is_physical_domain(unsigned domain)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	switch (domain) {
538c2ecf20Sopenharmony_ci#define DOMAIN(n, v, x, c)		\
548c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_##n:	\
558c2ecf20Sopenharmony_ci		return c;
568c2ecf20Sopenharmony_ci#include "hv-24x7-domains.h"
578c2ecf20Sopenharmony_ci#undef DOMAIN
588c2ecf20Sopenharmony_ci	default:
598c2ecf20Sopenharmony_ci		return false;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * The Processor Module Information system parameter allows transferring
658c2ecf20Sopenharmony_ci * of certain processor module information from the platform to the OS.
668c2ecf20Sopenharmony_ci * Refer PAPR+ document to get parameter token value as '43'.
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define PROCESSOR_MODULE_INFO   43
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic u32 phys_sockets;	/* Physical sockets */
728c2ecf20Sopenharmony_cistatic u32 phys_chipspersocket;	/* Physical chips per socket*/
738c2ecf20Sopenharmony_cistatic u32 phys_coresperchip; /* Physical cores per chip */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * read_24x7_sys_info()
778c2ecf20Sopenharmony_ci * Retrieve the number of sockets and chips per socket and cores per
788c2ecf20Sopenharmony_ci * chip details through the get-system-parameter rtas call.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_civoid read_24x7_sys_info(void)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	const s32 token = rtas_token("ibm,get-system-parameter");
838c2ecf20Sopenharmony_ci	int call_status;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * Making system parameter: chips and sockets and cores per chip
878c2ecf20Sopenharmony_ci	 * default to 1.
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	phys_sockets = 1;
908c2ecf20Sopenharmony_ci	phys_chipspersocket = 1;
918c2ecf20Sopenharmony_ci	phys_coresperchip = 1;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	do {
948c2ecf20Sopenharmony_ci		spin_lock(&rtas_data_buf_lock);
958c2ecf20Sopenharmony_ci		call_status = rtas_call(token, 3, 1, NULL, PROCESSOR_MODULE_INFO,
968c2ecf20Sopenharmony_ci					__pa(rtas_data_buf), RTAS_DATA_BUF_SIZE);
978c2ecf20Sopenharmony_ci		if (call_status == 0) {
988c2ecf20Sopenharmony_ci			int ntypes = be16_to_cpup((__be16 *)&rtas_data_buf[2]);
998c2ecf20Sopenharmony_ci			int len = be16_to_cpup((__be16 *)&rtas_data_buf[0]);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci			if (len >= 8 && ntypes != 0) {
1028c2ecf20Sopenharmony_ci				phys_sockets = be16_to_cpup((__be16 *)&rtas_data_buf[4]);
1038c2ecf20Sopenharmony_ci				phys_chipspersocket = be16_to_cpup((__be16 *)&rtas_data_buf[6]);
1048c2ecf20Sopenharmony_ci				phys_coresperchip = be16_to_cpup((__be16 *)&rtas_data_buf[8]);
1058c2ecf20Sopenharmony_ci			}
1068c2ecf20Sopenharmony_ci		}
1078c2ecf20Sopenharmony_ci		spin_unlock(&rtas_data_buf_lock);
1088c2ecf20Sopenharmony_ci	} while (rtas_busy_delay(call_status));
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (call_status != 0) {
1118c2ecf20Sopenharmony_ci		pr_err("Error calling get-system-parameter %d\n",
1128c2ecf20Sopenharmony_ci		       call_status);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* Domains for which more than one result element are returned for each event. */
1178c2ecf20Sopenharmony_cistatic bool domain_needs_aggregation(unsigned int domain)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return aggregate_result_elements &&
1208c2ecf20Sopenharmony_ci			(domain == HV_PERF_DOMAIN_PHYS_CORE ||
1218c2ecf20Sopenharmony_ci			 (domain >= HV_PERF_DOMAIN_VCPU_HOME_CORE &&
1228c2ecf20Sopenharmony_ci			  domain <= HV_PERF_DOMAIN_VCPU_REMOTE_NODE));
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic const char *domain_name(unsigned domain)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	if (!domain_is_valid(domain))
1288c2ecf20Sopenharmony_ci		return NULL;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	switch (domain) {
1318c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_PHYS_CHIP:		return "Physical Chip";
1328c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_PHYS_CORE:		return "Physical Core";
1338c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_VCPU_HOME_CORE:	return "VCPU Home Core";
1348c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_VCPU_HOME_CHIP:	return "VCPU Home Chip";
1358c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_VCPU_HOME_NODE:	return "VCPU Home Node";
1368c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_VCPU_REMOTE_NODE:	return "VCPU Remote Node";
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	WARN_ON_ONCE(domain);
1408c2ecf20Sopenharmony_ci	return NULL;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic bool catalog_entry_domain_is_valid(unsigned domain)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	/* POWER8 doesn't support virtual domains. */
1468c2ecf20Sopenharmony_ci	if (interface_version == 1)
1478c2ecf20Sopenharmony_ci		return is_physical_domain(domain);
1488c2ecf20Sopenharmony_ci	else
1498c2ecf20Sopenharmony_ci		return domain_is_valid(domain);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * TODO: Merging events:
1548c2ecf20Sopenharmony_ci * - Think of the hcall as an interface to a 4d array of counters:
1558c2ecf20Sopenharmony_ci *   - x = domains
1568c2ecf20Sopenharmony_ci *   - y = indexes in the domain (core, chip, vcpu, node, etc)
1578c2ecf20Sopenharmony_ci *   - z = offset into the counter space
1588c2ecf20Sopenharmony_ci *   - w = lpars (guest vms, "logical partitions")
1598c2ecf20Sopenharmony_ci * - A single request is: x,y,y_last,z,z_last,w,w_last
1608c2ecf20Sopenharmony_ci *   - this means we can retrieve a rectangle of counters in y,z for a single x.
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * - Things to consider (ignoring w):
1638c2ecf20Sopenharmony_ci *   - input  cost_per_request = 16
1648c2ecf20Sopenharmony_ci *   - output cost_per_result(ys,zs)  = 8 + 8 * ys + ys * zs
1658c2ecf20Sopenharmony_ci *   - limited number of requests per hcall (must fit into 4K bytes)
1668c2ecf20Sopenharmony_ci *     - 4k = 16 [buffer header] - 16 [request size] * request_count
1678c2ecf20Sopenharmony_ci *     - 255 requests per hcall
1688c2ecf20Sopenharmony_ci *   - sometimes it will be more efficient to read extra data and discard
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/*
1728c2ecf20Sopenharmony_ci * Example usage:
1738c2ecf20Sopenharmony_ci *  perf stat -e 'hv_24x7/domain=2,offset=8,vcpu=0,lpar=0xffffffff/'
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/* u3 0-6, one of HV_24X7_PERF_DOMAIN */
1778c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(domain, config, 0, 3);
1788c2ecf20Sopenharmony_ci/* u16 */
1798c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(core, config, 16, 31);
1808c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(chip, config, 16, 31);
1818c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(vcpu, config, 16, 31);
1828c2ecf20Sopenharmony_ci/* u32, see "data_offset" */
1838c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(offset, config, 32, 63);
1848c2ecf20Sopenharmony_ci/* u16 */
1858c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE_FORMAT(lpar, config1, 0, 15);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE(reserved1, config,   4, 15);
1888c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE(reserved2, config1, 16, 63);
1898c2ecf20Sopenharmony_ciEVENT_DEFINE_RANGE(reserved3, config2,  0, 63);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic struct attribute *format_attrs[] = {
1928c2ecf20Sopenharmony_ci	&format_attr_domain.attr,
1938c2ecf20Sopenharmony_ci	&format_attr_offset.attr,
1948c2ecf20Sopenharmony_ci	&format_attr_core.attr,
1958c2ecf20Sopenharmony_ci	&format_attr_chip.attr,
1968c2ecf20Sopenharmony_ci	&format_attr_vcpu.attr,
1978c2ecf20Sopenharmony_ci	&format_attr_lpar.attr,
1988c2ecf20Sopenharmony_ci	NULL,
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic struct attribute_group format_group = {
2028c2ecf20Sopenharmony_ci	.name = "format",
2038c2ecf20Sopenharmony_ci	.attrs = format_attrs,
2048c2ecf20Sopenharmony_ci};
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic struct attribute_group event_group = {
2078c2ecf20Sopenharmony_ci	.name = "events",
2088c2ecf20Sopenharmony_ci	/* .attrs is set in init */
2098c2ecf20Sopenharmony_ci};
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic struct attribute_group event_desc_group = {
2128c2ecf20Sopenharmony_ci	.name = "event_descs",
2138c2ecf20Sopenharmony_ci	/* .attrs is set in init */
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic struct attribute_group event_long_desc_group = {
2178c2ecf20Sopenharmony_ci	.name = "event_long_descs",
2188c2ecf20Sopenharmony_ci	/* .attrs is set in init */
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic struct kmem_cache *hv_page_cache;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciDEFINE_PER_CPU(int, hv_24x7_txn_flags);
2248c2ecf20Sopenharmony_ciDEFINE_PER_CPU(int, hv_24x7_txn_err);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistruct hv_24x7_hw {
2278c2ecf20Sopenharmony_ci	struct perf_event *events[255];
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ciDEFINE_PER_CPU(struct hv_24x7_hw, hv_24x7_hw);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/*
2338c2ecf20Sopenharmony_ci * request_buffer and result_buffer are not required to be 4k aligned,
2348c2ecf20Sopenharmony_ci * but are not allowed to cross any 4k boundary. Aligning them to 4k is
2358c2ecf20Sopenharmony_ci * the simplest way to ensure that.
2368c2ecf20Sopenharmony_ci */
2378c2ecf20Sopenharmony_ci#define H24x7_DATA_BUFFER_SIZE	4096
2388c2ecf20Sopenharmony_ciDEFINE_PER_CPU(char, hv_24x7_reqb[H24x7_DATA_BUFFER_SIZE]) __aligned(4096);
2398c2ecf20Sopenharmony_ciDEFINE_PER_CPU(char, hv_24x7_resb[H24x7_DATA_BUFFER_SIZE]) __aligned(4096);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic unsigned int max_num_requests(int interface_version)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	return (H24x7_DATA_BUFFER_SIZE - sizeof(struct hv_24x7_request_buffer))
2448c2ecf20Sopenharmony_ci		/ H24x7_REQUEST_SIZE(interface_version);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic char *event_name(struct hv_24x7_event_data *ev, int *len)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	*len = be16_to_cpu(ev->event_name_len) - 2;
2508c2ecf20Sopenharmony_ci	return (char *)ev->remainder;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic char *event_desc(struct hv_24x7_event_data *ev, int *len)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	unsigned nl = be16_to_cpu(ev->event_name_len);
2568c2ecf20Sopenharmony_ci	__be16 *desc_len = (__be16 *)(ev->remainder + nl - 2);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	*len = be16_to_cpu(*desc_len) - 2;
2598c2ecf20Sopenharmony_ci	return (char *)ev->remainder + nl;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic char *event_long_desc(struct hv_24x7_event_data *ev, int *len)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	unsigned nl = be16_to_cpu(ev->event_name_len);
2658c2ecf20Sopenharmony_ci	__be16 *desc_len_ = (__be16 *)(ev->remainder + nl - 2);
2668c2ecf20Sopenharmony_ci	unsigned desc_len = be16_to_cpu(*desc_len_);
2678c2ecf20Sopenharmony_ci	__be16 *long_desc_len = (__be16 *)(ev->remainder + nl + desc_len - 2);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	*len = be16_to_cpu(*long_desc_len) - 2;
2708c2ecf20Sopenharmony_ci	return (char *)ev->remainder + nl + desc_len;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic bool event_fixed_portion_is_within(struct hv_24x7_event_data *ev,
2748c2ecf20Sopenharmony_ci					  void *end)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	void *start = ev;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return (start + offsetof(struct hv_24x7_event_data, remainder)) < end;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/*
2828c2ecf20Sopenharmony_ci * Things we don't check:
2838c2ecf20Sopenharmony_ci *  - padding for desc, name, and long/detailed desc is required to be '\0'
2848c2ecf20Sopenharmony_ci *    bytes.
2858c2ecf20Sopenharmony_ci *
2868c2ecf20Sopenharmony_ci *  Return NULL if we pass end,
2878c2ecf20Sopenharmony_ci *  Otherwise return the address of the byte just following the event.
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic void *event_end(struct hv_24x7_event_data *ev, void *end)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	void *start = ev;
2928c2ecf20Sopenharmony_ci	__be16 *dl_, *ldl_;
2938c2ecf20Sopenharmony_ci	unsigned dl, ldl;
2948c2ecf20Sopenharmony_ci	unsigned nl = be16_to_cpu(ev->event_name_len);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (nl < 2) {
2978c2ecf20Sopenharmony_ci		pr_debug("%s: name length too short: %d", __func__, nl);
2988c2ecf20Sopenharmony_ci		return NULL;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (start + nl > end) {
3028c2ecf20Sopenharmony_ci		pr_debug("%s: start=%p + nl=%u > end=%p",
3038c2ecf20Sopenharmony_ci				__func__, start, nl, end);
3048c2ecf20Sopenharmony_ci		return NULL;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	dl_ = (__be16 *)(ev->remainder + nl - 2);
3088c2ecf20Sopenharmony_ci	if (!IS_ALIGNED((uintptr_t)dl_, 2))
3098c2ecf20Sopenharmony_ci		pr_warn("desc len not aligned %p", dl_);
3108c2ecf20Sopenharmony_ci	dl = be16_to_cpu(*dl_);
3118c2ecf20Sopenharmony_ci	if (dl < 2) {
3128c2ecf20Sopenharmony_ci		pr_debug("%s: desc len too short: %d", __func__, dl);
3138c2ecf20Sopenharmony_ci		return NULL;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (start + nl + dl > end) {
3178c2ecf20Sopenharmony_ci		pr_debug("%s: (start=%p + nl=%u + dl=%u)=%p > end=%p",
3188c2ecf20Sopenharmony_ci				__func__, start, nl, dl, start + nl + dl, end);
3198c2ecf20Sopenharmony_ci		return NULL;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	ldl_ = (__be16 *)(ev->remainder + nl + dl - 2);
3238c2ecf20Sopenharmony_ci	if (!IS_ALIGNED((uintptr_t)ldl_, 2))
3248c2ecf20Sopenharmony_ci		pr_warn("long desc len not aligned %p", ldl_);
3258c2ecf20Sopenharmony_ci	ldl = be16_to_cpu(*ldl_);
3268c2ecf20Sopenharmony_ci	if (ldl < 2) {
3278c2ecf20Sopenharmony_ci		pr_debug("%s: long desc len too short (ldl=%u)",
3288c2ecf20Sopenharmony_ci				__func__, ldl);
3298c2ecf20Sopenharmony_ci		return NULL;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (start + nl + dl + ldl > end) {
3338c2ecf20Sopenharmony_ci		pr_debug("%s: start=%p + nl=%u + dl=%u + ldl=%u > end=%p",
3348c2ecf20Sopenharmony_ci				__func__, start, nl, dl, ldl, end);
3358c2ecf20Sopenharmony_ci		return NULL;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return start + nl + dl + ldl;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic long h_get_24x7_catalog_page_(unsigned long phys_4096,
3428c2ecf20Sopenharmony_ci				     unsigned long version, unsigned long index)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	pr_devel("h_get_24x7_catalog_page(0x%lx, %lu, %lu)",
3458c2ecf20Sopenharmony_ci			phys_4096, version, index);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	WARN_ON(!IS_ALIGNED(phys_4096, 4096));
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return plpar_hcall_norets(H_GET_24X7_CATALOG_PAGE,
3508c2ecf20Sopenharmony_ci			phys_4096, version, index);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic long h_get_24x7_catalog_page(char page[], u64 version, u32 index)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	return h_get_24x7_catalog_page_(virt_to_phys(page),
3568c2ecf20Sopenharmony_ci					version, index);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci/*
3608c2ecf20Sopenharmony_ci * Each event we find in the catalog, will have a sysfs entry. Format the
3618c2ecf20Sopenharmony_ci * data for this sysfs entry based on the event's domain.
3628c2ecf20Sopenharmony_ci *
3638c2ecf20Sopenharmony_ci * Events belonging to the Chip domain can only be monitored in that domain.
3648c2ecf20Sopenharmony_ci * i.e the domain for these events is a fixed/knwon value.
3658c2ecf20Sopenharmony_ci *
3668c2ecf20Sopenharmony_ci * Events belonging to the Core domain can be monitored either in the physical
3678c2ecf20Sopenharmony_ci * core or in one of the virtual CPU domains. So the domain value for these
3688c2ecf20Sopenharmony_ci * events must be specified by the user (i.e is a required parameter). Format
3698c2ecf20Sopenharmony_ci * the Core events with 'domain=?' so the perf-tool can error check required
3708c2ecf20Sopenharmony_ci * parameters.
3718c2ecf20Sopenharmony_ci *
3728c2ecf20Sopenharmony_ci * NOTE: For the Core domain events, rather than making domain a required
3738c2ecf20Sopenharmony_ci *	 parameter we could default it to PHYS_CORE and allowe users to
3748c2ecf20Sopenharmony_ci *	 override the domain to one of the VCPU domains.
3758c2ecf20Sopenharmony_ci *
3768c2ecf20Sopenharmony_ci *	 However, this can make the interface a little inconsistent.
3778c2ecf20Sopenharmony_ci *
3788c2ecf20Sopenharmony_ci *	 If we set domain=2 (PHYS_CHIP) and allow user to override this field
3798c2ecf20Sopenharmony_ci *	 the user may be tempted to also modify the "offset=x" field in which
3808c2ecf20Sopenharmony_ci *	 can lead to confusing usage. Consider the HPM_PCYC (offset=0x18) and
3818c2ecf20Sopenharmony_ci *	 HPM_INST (offset=0x20) events. With:
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci *		perf stat -e hv_24x7/HPM_PCYC,offset=0x20/
3848c2ecf20Sopenharmony_ci *
3858c2ecf20Sopenharmony_ci *	we end up monitoring HPM_INST, while the command line has HPM_PCYC.
3868c2ecf20Sopenharmony_ci *
3878c2ecf20Sopenharmony_ci *	By not assigning a default value to the domain for the Core events,
3888c2ecf20Sopenharmony_ci *	we can have simple guidelines:
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci *		- Specifying values for parameters with "=?" is required.
3918c2ecf20Sopenharmony_ci *
3928c2ecf20Sopenharmony_ci *		- Specifying (i.e overriding) values for other parameters
3938c2ecf20Sopenharmony_ci *		  is undefined.
3948c2ecf20Sopenharmony_ci */
3958c2ecf20Sopenharmony_cistatic char *event_fmt(struct hv_24x7_event_data *event, unsigned domain)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	const char *sindex;
3988c2ecf20Sopenharmony_ci	const char *lpar;
3998c2ecf20Sopenharmony_ci	const char *domain_str;
4008c2ecf20Sopenharmony_ci	char buf[8];
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	switch (domain) {
4038c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_PHYS_CHIP:
4048c2ecf20Sopenharmony_ci		snprintf(buf, sizeof(buf), "%d", domain);
4058c2ecf20Sopenharmony_ci		domain_str = buf;
4068c2ecf20Sopenharmony_ci		lpar = "0x0";
4078c2ecf20Sopenharmony_ci		sindex = "chip";
4088c2ecf20Sopenharmony_ci		break;
4098c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_PHYS_CORE:
4108c2ecf20Sopenharmony_ci		domain_str = "?";
4118c2ecf20Sopenharmony_ci		lpar = "0x0";
4128c2ecf20Sopenharmony_ci		sindex = "core";
4138c2ecf20Sopenharmony_ci		break;
4148c2ecf20Sopenharmony_ci	default:
4158c2ecf20Sopenharmony_ci		domain_str = "?";
4168c2ecf20Sopenharmony_ci		lpar = "?";
4178c2ecf20Sopenharmony_ci		sindex = "vcpu";
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL,
4218c2ecf20Sopenharmony_ci			"domain=%s,offset=0x%x,%s=?,lpar=%s",
4228c2ecf20Sopenharmony_ci			domain_str,
4238c2ecf20Sopenharmony_ci			be16_to_cpu(event->event_counter_offs) +
4248c2ecf20Sopenharmony_ci				be16_to_cpu(event->event_group_record_offs),
4258c2ecf20Sopenharmony_ci			sindex,
4268c2ecf20Sopenharmony_ci			lpar);
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci/* Avoid trusting fw to NUL terminate strings */
4308c2ecf20Sopenharmony_cistatic char *memdup_to_str(char *maybe_str, int max_len, gfp_t gfp)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	return kasprintf(gfp, "%.*s", max_len, maybe_str);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic ssize_t device_show_string(struct device *dev,
4368c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct dev_ext_attribute *d;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	d = container_of(attr, struct dev_ext_attribute, attr);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", (char *)d->var);
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic ssize_t cpumask_show(struct device *dev,
4468c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buf)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	return cpumap_print_to_pagebuf(true, buf, &hv_24x7_cpumask);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic ssize_t sockets_show(struct device *dev,
4528c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buf)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", phys_sockets);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic ssize_t chipspersocket_show(struct device *dev,
4588c2ecf20Sopenharmony_ci				   struct device_attribute *attr, char *buf)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", phys_chipspersocket);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic ssize_t coresperchip_show(struct device *dev,
4648c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", phys_coresperchip);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic struct attribute *device_str_attr_create_(char *name, char *str)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct dev_ext_attribute *attr = kzalloc(sizeof(*attr), GFP_KERNEL);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (!attr)
4748c2ecf20Sopenharmony_ci		return NULL;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	sysfs_attr_init(&attr->attr.attr);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	attr->var = str;
4798c2ecf20Sopenharmony_ci	attr->attr.attr.name = name;
4808c2ecf20Sopenharmony_ci	attr->attr.attr.mode = 0444;
4818c2ecf20Sopenharmony_ci	attr->attr.show = device_show_string;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	return &attr->attr.attr;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/*
4878c2ecf20Sopenharmony_ci * Allocate and initialize strings representing event attributes.
4888c2ecf20Sopenharmony_ci *
4898c2ecf20Sopenharmony_ci * NOTE: The strings allocated here are never destroyed and continue to
4908c2ecf20Sopenharmony_ci *	 exist till shutdown. This is to allow us to create as many events
4918c2ecf20Sopenharmony_ci *	 from the catalog as possible, even if we encounter errors with some.
4928c2ecf20Sopenharmony_ci *	 In case of changes to error paths in future, these may need to be
4938c2ecf20Sopenharmony_ci *	 freed by the caller.
4948c2ecf20Sopenharmony_ci */
4958c2ecf20Sopenharmony_cistatic struct attribute *device_str_attr_create(char *name, int name_max,
4968c2ecf20Sopenharmony_ci						int name_nonce,
4978c2ecf20Sopenharmony_ci						char *str, size_t str_max)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	char *n;
5008c2ecf20Sopenharmony_ci	char *s = memdup_to_str(str, str_max, GFP_KERNEL);
5018c2ecf20Sopenharmony_ci	struct attribute *a;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (!s)
5048c2ecf20Sopenharmony_ci		return NULL;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (!name_nonce)
5078c2ecf20Sopenharmony_ci		n = kasprintf(GFP_KERNEL, "%.*s", name_max, name);
5088c2ecf20Sopenharmony_ci	else
5098c2ecf20Sopenharmony_ci		n = kasprintf(GFP_KERNEL, "%.*s__%d", name_max, name,
5108c2ecf20Sopenharmony_ci					name_nonce);
5118c2ecf20Sopenharmony_ci	if (!n)
5128c2ecf20Sopenharmony_ci		goto out_s;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	a = device_str_attr_create_(n, s);
5158c2ecf20Sopenharmony_ci	if (!a)
5168c2ecf20Sopenharmony_ci		goto out_n;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return a;
5198c2ecf20Sopenharmony_ciout_n:
5208c2ecf20Sopenharmony_ci	kfree(n);
5218c2ecf20Sopenharmony_ciout_s:
5228c2ecf20Sopenharmony_ci	kfree(s);
5238c2ecf20Sopenharmony_ci	return NULL;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic struct attribute *event_to_attr(unsigned ix,
5278c2ecf20Sopenharmony_ci				       struct hv_24x7_event_data *event,
5288c2ecf20Sopenharmony_ci				       unsigned domain,
5298c2ecf20Sopenharmony_ci				       int nonce)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	int event_name_len;
5328c2ecf20Sopenharmony_ci	char *ev_name, *a_ev_name, *val;
5338c2ecf20Sopenharmony_ci	struct attribute *attr;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (!domain_is_valid(domain)) {
5368c2ecf20Sopenharmony_ci		pr_warn("catalog event %u has invalid domain %u\n",
5378c2ecf20Sopenharmony_ci				ix, domain);
5388c2ecf20Sopenharmony_ci		return NULL;
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	val = event_fmt(event, domain);
5428c2ecf20Sopenharmony_ci	if (!val)
5438c2ecf20Sopenharmony_ci		return NULL;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	ev_name = event_name(event, &event_name_len);
5468c2ecf20Sopenharmony_ci	if (!nonce)
5478c2ecf20Sopenharmony_ci		a_ev_name = kasprintf(GFP_KERNEL, "%.*s",
5488c2ecf20Sopenharmony_ci				(int)event_name_len, ev_name);
5498c2ecf20Sopenharmony_ci	else
5508c2ecf20Sopenharmony_ci		a_ev_name = kasprintf(GFP_KERNEL, "%.*s__%d",
5518c2ecf20Sopenharmony_ci				(int)event_name_len, ev_name, nonce);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (!a_ev_name)
5548c2ecf20Sopenharmony_ci		goto out_val;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	attr = device_str_attr_create_(a_ev_name, val);
5578c2ecf20Sopenharmony_ci	if (!attr)
5588c2ecf20Sopenharmony_ci		goto out_name;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	return attr;
5618c2ecf20Sopenharmony_ciout_name:
5628c2ecf20Sopenharmony_ci	kfree(a_ev_name);
5638c2ecf20Sopenharmony_ciout_val:
5648c2ecf20Sopenharmony_ci	kfree(val);
5658c2ecf20Sopenharmony_ci	return NULL;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic struct attribute *event_to_desc_attr(struct hv_24x7_event_data *event,
5698c2ecf20Sopenharmony_ci					    int nonce)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	int nl, dl;
5728c2ecf20Sopenharmony_ci	char *name = event_name(event, &nl);
5738c2ecf20Sopenharmony_ci	char *desc = event_desc(event, &dl);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/* If there isn't a description, don't create the sysfs file */
5768c2ecf20Sopenharmony_ci	if (!dl)
5778c2ecf20Sopenharmony_ci		return NULL;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return device_str_attr_create(name, nl, nonce, desc, dl);
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic struct attribute *
5838c2ecf20Sopenharmony_cievent_to_long_desc_attr(struct hv_24x7_event_data *event, int nonce)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	int nl, dl;
5868c2ecf20Sopenharmony_ci	char *name = event_name(event, &nl);
5878c2ecf20Sopenharmony_ci	char *desc = event_long_desc(event, &dl);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* If there isn't a description, don't create the sysfs file */
5908c2ecf20Sopenharmony_ci	if (!dl)
5918c2ecf20Sopenharmony_ci		return NULL;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return device_str_attr_create(name, nl, nonce, desc, dl);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int event_data_to_attrs(unsigned ix, struct attribute **attrs,
5978c2ecf20Sopenharmony_ci				   struct hv_24x7_event_data *event, int nonce)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	*attrs = event_to_attr(ix, event, event->domain, nonce);
6008c2ecf20Sopenharmony_ci	if (!*attrs)
6018c2ecf20Sopenharmony_ci		return -1;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci/* */
6078c2ecf20Sopenharmony_cistruct event_uniq {
6088c2ecf20Sopenharmony_ci	struct rb_node node;
6098c2ecf20Sopenharmony_ci	const char *name;
6108c2ecf20Sopenharmony_ci	int nl;
6118c2ecf20Sopenharmony_ci	unsigned ct;
6128c2ecf20Sopenharmony_ci	unsigned domain;
6138c2ecf20Sopenharmony_ci};
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int memord(const void *d1, size_t s1, const void *d2, size_t s2)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	if (s1 < s2)
6188c2ecf20Sopenharmony_ci		return 1;
6198c2ecf20Sopenharmony_ci	if (s1 > s2)
6208c2ecf20Sopenharmony_ci		return -1;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return memcmp(d1, d2, s1);
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int ev_uniq_ord(const void *v1, size_t s1, unsigned d1, const void *v2,
6268c2ecf20Sopenharmony_ci		       size_t s2, unsigned d2)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	int r = memord(v1, s1, v2, s2);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (r)
6318c2ecf20Sopenharmony_ci		return r;
6328c2ecf20Sopenharmony_ci	if (d1 > d2)
6338c2ecf20Sopenharmony_ci		return 1;
6348c2ecf20Sopenharmony_ci	if (d2 > d1)
6358c2ecf20Sopenharmony_ci		return -1;
6368c2ecf20Sopenharmony_ci	return 0;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic int event_uniq_add(struct rb_root *root, const char *name, int nl,
6408c2ecf20Sopenharmony_ci			  unsigned domain)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	struct rb_node **new = &(root->rb_node), *parent = NULL;
6438c2ecf20Sopenharmony_ci	struct event_uniq *data;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	/* Figure out where to put new node */
6468c2ecf20Sopenharmony_ci	while (*new) {
6478c2ecf20Sopenharmony_ci		struct event_uniq *it;
6488c2ecf20Sopenharmony_ci		int result;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		it = rb_entry(*new, struct event_uniq, node);
6518c2ecf20Sopenharmony_ci		result = ev_uniq_ord(name, nl, domain, it->name, it->nl,
6528c2ecf20Sopenharmony_ci					it->domain);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci		parent = *new;
6558c2ecf20Sopenharmony_ci		if (result < 0)
6568c2ecf20Sopenharmony_ci			new = &((*new)->rb_left);
6578c2ecf20Sopenharmony_ci		else if (result > 0)
6588c2ecf20Sopenharmony_ci			new = &((*new)->rb_right);
6598c2ecf20Sopenharmony_ci		else {
6608c2ecf20Sopenharmony_ci			it->ct++;
6618c2ecf20Sopenharmony_ci			pr_info("found a duplicate event %.*s, ct=%u\n", nl,
6628c2ecf20Sopenharmony_ci						name, it->ct);
6638c2ecf20Sopenharmony_ci			return it->ct;
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	data = kmalloc(sizeof(*data), GFP_KERNEL);
6688c2ecf20Sopenharmony_ci	if (!data)
6698c2ecf20Sopenharmony_ci		return -ENOMEM;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	*data = (struct event_uniq) {
6728c2ecf20Sopenharmony_ci		.name = name,
6738c2ecf20Sopenharmony_ci		.nl = nl,
6748c2ecf20Sopenharmony_ci		.ct = 0,
6758c2ecf20Sopenharmony_ci		.domain = domain,
6768c2ecf20Sopenharmony_ci	};
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* Add new node and rebalance tree. */
6798c2ecf20Sopenharmony_ci	rb_link_node(&data->node, parent, new);
6808c2ecf20Sopenharmony_ci	rb_insert_color(&data->node, root);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	/* data->ct */
6838c2ecf20Sopenharmony_ci	return 0;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic void event_uniq_destroy(struct rb_root *root)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	/*
6898c2ecf20Sopenharmony_ci	 * the strings we point to are in the giant block of memory filled by
6908c2ecf20Sopenharmony_ci	 * the catalog, and are freed separately.
6918c2ecf20Sopenharmony_ci	 */
6928c2ecf20Sopenharmony_ci	struct event_uniq *pos, *n;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	rbtree_postorder_for_each_entry_safe(pos, n, root, node)
6958c2ecf20Sopenharmony_ci		kfree(pos);
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/*
7008c2ecf20Sopenharmony_ci * ensure the event structure's sizes are self consistent and don't cause us to
7018c2ecf20Sopenharmony_ci * read outside of the event
7028c2ecf20Sopenharmony_ci *
7038c2ecf20Sopenharmony_ci * On success, return the event length in bytes.
7048c2ecf20Sopenharmony_ci * Otherwise, return -1 (and print as appropriate).
7058c2ecf20Sopenharmony_ci */
7068c2ecf20Sopenharmony_cistatic ssize_t catalog_event_len_validate(struct hv_24x7_event_data *event,
7078c2ecf20Sopenharmony_ci					  size_t event_idx,
7088c2ecf20Sopenharmony_ci					  size_t event_data_bytes,
7098c2ecf20Sopenharmony_ci					  size_t event_entry_count,
7108c2ecf20Sopenharmony_ci					  size_t offset, void *end)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	ssize_t ev_len;
7138c2ecf20Sopenharmony_ci	void *ev_end, *calc_ev_end;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (offset >= event_data_bytes)
7168c2ecf20Sopenharmony_ci		return -1;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (event_idx >= event_entry_count) {
7198c2ecf20Sopenharmony_ci		pr_devel("catalog event data has %zu bytes of padding after last event\n",
7208c2ecf20Sopenharmony_ci				event_data_bytes - offset);
7218c2ecf20Sopenharmony_ci		return -1;
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (!event_fixed_portion_is_within(event, end)) {
7258c2ecf20Sopenharmony_ci		pr_warn("event %zu fixed portion is not within range\n",
7268c2ecf20Sopenharmony_ci				event_idx);
7278c2ecf20Sopenharmony_ci		return -1;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	ev_len = be16_to_cpu(event->length);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	if (ev_len % 16)
7338c2ecf20Sopenharmony_ci		pr_info("event %zu has length %zu not divisible by 16: event=%pK\n",
7348c2ecf20Sopenharmony_ci				event_idx, ev_len, event);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	ev_end = (__u8 *)event + ev_len;
7378c2ecf20Sopenharmony_ci	if (ev_end > end) {
7388c2ecf20Sopenharmony_ci		pr_warn("event %zu has .length=%zu, ends after buffer end: ev_end=%pK > end=%pK, offset=%zu\n",
7398c2ecf20Sopenharmony_ci				event_idx, ev_len, ev_end, end,
7408c2ecf20Sopenharmony_ci				offset);
7418c2ecf20Sopenharmony_ci		return -1;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	calc_ev_end = event_end(event, end);
7458c2ecf20Sopenharmony_ci	if (!calc_ev_end) {
7468c2ecf20Sopenharmony_ci		pr_warn("event %zu has a calculated length which exceeds buffer length %zu: event=%pK end=%pK, offset=%zu\n",
7478c2ecf20Sopenharmony_ci			event_idx, event_data_bytes, event, end,
7488c2ecf20Sopenharmony_ci			offset);
7498c2ecf20Sopenharmony_ci		return -1;
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (calc_ev_end > ev_end) {
7538c2ecf20Sopenharmony_ci		pr_warn("event %zu exceeds it's own length: event=%pK, end=%pK, offset=%zu, calc_ev_end=%pK\n",
7548c2ecf20Sopenharmony_ci			event_idx, event, ev_end, offset, calc_ev_end);
7558c2ecf20Sopenharmony_ci		return -1;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	return ev_len;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci#define MAX_4K (SIZE_MAX / 4096)
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic int create_events_from_catalog(struct attribute ***events_,
7648c2ecf20Sopenharmony_ci				      struct attribute ***event_descs_,
7658c2ecf20Sopenharmony_ci				      struct attribute ***event_long_descs_)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	long hret;
7688c2ecf20Sopenharmony_ci	size_t catalog_len, catalog_page_len, event_entry_count,
7698c2ecf20Sopenharmony_ci	       event_data_len, event_data_offs,
7708c2ecf20Sopenharmony_ci	       event_data_bytes, junk_events, event_idx, event_attr_ct, i,
7718c2ecf20Sopenharmony_ci	       attr_max, event_idx_last, desc_ct, long_desc_ct;
7728c2ecf20Sopenharmony_ci	ssize_t ct, ev_len;
7738c2ecf20Sopenharmony_ci	uint64_t catalog_version_num;
7748c2ecf20Sopenharmony_ci	struct attribute **events, **event_descs, **event_long_descs;
7758c2ecf20Sopenharmony_ci	struct hv_24x7_catalog_page_0 *page_0 =
7768c2ecf20Sopenharmony_ci		kmem_cache_alloc(hv_page_cache, GFP_KERNEL);
7778c2ecf20Sopenharmony_ci	void *page = page_0;
7788c2ecf20Sopenharmony_ci	void *event_data, *end;
7798c2ecf20Sopenharmony_ci	struct hv_24x7_event_data *event;
7808c2ecf20Sopenharmony_ci	struct rb_root ev_uniq = RB_ROOT;
7818c2ecf20Sopenharmony_ci	int ret = 0;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (!page) {
7848c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7858c2ecf20Sopenharmony_ci		goto e_out;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	hret = h_get_24x7_catalog_page(page, 0, 0);
7898c2ecf20Sopenharmony_ci	if (hret) {
7908c2ecf20Sopenharmony_ci		ret = -EIO;
7918c2ecf20Sopenharmony_ci		goto e_free;
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	catalog_version_num = be64_to_cpu(page_0->version);
7958c2ecf20Sopenharmony_ci	catalog_page_len = be32_to_cpu(page_0->length);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	if (MAX_4K < catalog_page_len) {
7988c2ecf20Sopenharmony_ci		pr_err("invalid page count: %zu\n", catalog_page_len);
7998c2ecf20Sopenharmony_ci		ret = -EIO;
8008c2ecf20Sopenharmony_ci		goto e_free;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	catalog_len = catalog_page_len * 4096;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	event_entry_count = be16_to_cpu(page_0->event_entry_count);
8068c2ecf20Sopenharmony_ci	event_data_offs   = be16_to_cpu(page_0->event_data_offs);
8078c2ecf20Sopenharmony_ci	event_data_len    = be16_to_cpu(page_0->event_data_len);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	pr_devel("cv %llu cl %zu eec %zu edo %zu edl %zu\n",
8108c2ecf20Sopenharmony_ci			catalog_version_num, catalog_len,
8118c2ecf20Sopenharmony_ci			event_entry_count, event_data_offs, event_data_len);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if ((MAX_4K < event_data_len)
8148c2ecf20Sopenharmony_ci			|| (MAX_4K < event_data_offs)
8158c2ecf20Sopenharmony_ci			|| (MAX_4K - event_data_offs < event_data_len)) {
8168c2ecf20Sopenharmony_ci		pr_err("invalid event data offs %zu and/or len %zu\n",
8178c2ecf20Sopenharmony_ci				event_data_offs, event_data_len);
8188c2ecf20Sopenharmony_ci		ret = -EIO;
8198c2ecf20Sopenharmony_ci		goto e_free;
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if ((event_data_offs + event_data_len) > catalog_page_len) {
8238c2ecf20Sopenharmony_ci		pr_err("event data %zu-%zu does not fit inside catalog 0-%zu\n",
8248c2ecf20Sopenharmony_ci				event_data_offs,
8258c2ecf20Sopenharmony_ci				event_data_offs + event_data_len,
8268c2ecf20Sopenharmony_ci				catalog_page_len);
8278c2ecf20Sopenharmony_ci		ret = -EIO;
8288c2ecf20Sopenharmony_ci		goto e_free;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	if (SIZE_MAX - 1 < event_entry_count) {
8328c2ecf20Sopenharmony_ci		pr_err("event_entry_count %zu is invalid\n", event_entry_count);
8338c2ecf20Sopenharmony_ci		ret = -EIO;
8348c2ecf20Sopenharmony_ci		goto e_free;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	event_data_bytes = event_data_len * 4096;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/*
8408c2ecf20Sopenharmony_ci	 * event data can span several pages, events can cross between these
8418c2ecf20Sopenharmony_ci	 * pages. Use vmalloc to make this easier.
8428c2ecf20Sopenharmony_ci	 */
8438c2ecf20Sopenharmony_ci	event_data = vmalloc(event_data_bytes);
8448c2ecf20Sopenharmony_ci	if (!event_data) {
8458c2ecf20Sopenharmony_ci		pr_err("could not allocate event data\n");
8468c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8478c2ecf20Sopenharmony_ci		goto e_free;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	end = event_data + event_data_bytes;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/*
8538c2ecf20Sopenharmony_ci	 * using vmalloc_to_phys() like this only works if PAGE_SIZE is
8548c2ecf20Sopenharmony_ci	 * divisible by 4096
8558c2ecf20Sopenharmony_ci	 */
8568c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PAGE_SIZE % 4096);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	for (i = 0; i < event_data_len; i++) {
8598c2ecf20Sopenharmony_ci		hret = h_get_24x7_catalog_page_(
8608c2ecf20Sopenharmony_ci				vmalloc_to_phys(event_data + i * 4096),
8618c2ecf20Sopenharmony_ci				catalog_version_num,
8628c2ecf20Sopenharmony_ci				i + event_data_offs);
8638c2ecf20Sopenharmony_ci		if (hret) {
8648c2ecf20Sopenharmony_ci			pr_err("Failed to get event data in page %zu: rc=%ld\n",
8658c2ecf20Sopenharmony_ci			       i + event_data_offs, hret);
8668c2ecf20Sopenharmony_ci			ret = -EIO;
8678c2ecf20Sopenharmony_ci			goto e_event_data;
8688c2ecf20Sopenharmony_ci		}
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/*
8728c2ecf20Sopenharmony_ci	 * scan the catalog to determine the number of attributes we need, and
8738c2ecf20Sopenharmony_ci	 * verify it at the same time.
8748c2ecf20Sopenharmony_ci	 */
8758c2ecf20Sopenharmony_ci	for (junk_events = 0, event = event_data, event_idx = 0, attr_max = 0;
8768c2ecf20Sopenharmony_ci	     ;
8778c2ecf20Sopenharmony_ci	     event_idx++, event = (void *)event + ev_len) {
8788c2ecf20Sopenharmony_ci		size_t offset = (void *)event - (void *)event_data;
8798c2ecf20Sopenharmony_ci		char *name;
8808c2ecf20Sopenharmony_ci		int nl;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		ev_len = catalog_event_len_validate(event, event_idx,
8838c2ecf20Sopenharmony_ci						    event_data_bytes,
8848c2ecf20Sopenharmony_ci						    event_entry_count,
8858c2ecf20Sopenharmony_ci						    offset, end);
8868c2ecf20Sopenharmony_ci		if (ev_len < 0)
8878c2ecf20Sopenharmony_ci			break;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		name = event_name(event, &nl);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci		if (event->event_group_record_len == 0) {
8928c2ecf20Sopenharmony_ci			pr_devel("invalid event %zu (%.*s): group_record_len == 0, skipping\n",
8938c2ecf20Sopenharmony_ci					event_idx, nl, name);
8948c2ecf20Sopenharmony_ci			junk_events++;
8958c2ecf20Sopenharmony_ci			continue;
8968c2ecf20Sopenharmony_ci		}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		if (!catalog_entry_domain_is_valid(event->domain)) {
8998c2ecf20Sopenharmony_ci			pr_info("event %zu (%.*s) has invalid domain %d\n",
9008c2ecf20Sopenharmony_ci					event_idx, nl, name, event->domain);
9018c2ecf20Sopenharmony_ci			junk_events++;
9028c2ecf20Sopenharmony_ci			continue;
9038c2ecf20Sopenharmony_ci		}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci		attr_max++;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	event_idx_last = event_idx;
9098c2ecf20Sopenharmony_ci	if (event_idx_last != event_entry_count)
9108c2ecf20Sopenharmony_ci		pr_warn("event buffer ended before listed # of events were parsed (got %zu, wanted %zu, junk %zu)\n",
9118c2ecf20Sopenharmony_ci				event_idx_last, event_entry_count, junk_events);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	events = kmalloc_array(attr_max + 1, sizeof(*events), GFP_KERNEL);
9148c2ecf20Sopenharmony_ci	if (!events) {
9158c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9168c2ecf20Sopenharmony_ci		goto e_event_data;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	event_descs = kmalloc_array(event_idx + 1, sizeof(*event_descs),
9208c2ecf20Sopenharmony_ci				GFP_KERNEL);
9218c2ecf20Sopenharmony_ci	if (!event_descs) {
9228c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9238c2ecf20Sopenharmony_ci		goto e_event_attrs;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	event_long_descs = kmalloc_array(event_idx + 1,
9278c2ecf20Sopenharmony_ci			sizeof(*event_long_descs), GFP_KERNEL);
9288c2ecf20Sopenharmony_ci	if (!event_long_descs) {
9298c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9308c2ecf20Sopenharmony_ci		goto e_event_descs;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	/* Iterate over the catalog filling in the attribute vector */
9348c2ecf20Sopenharmony_ci	for (junk_events = 0, event_attr_ct = 0, desc_ct = 0, long_desc_ct = 0,
9358c2ecf20Sopenharmony_ci				event = event_data, event_idx = 0;
9368c2ecf20Sopenharmony_ci			event_idx < event_idx_last;
9378c2ecf20Sopenharmony_ci			event_idx++, ev_len = be16_to_cpu(event->length),
9388c2ecf20Sopenharmony_ci				event = (void *)event + ev_len) {
9398c2ecf20Sopenharmony_ci		char *name;
9408c2ecf20Sopenharmony_ci		int nl;
9418c2ecf20Sopenharmony_ci		int nonce;
9428c2ecf20Sopenharmony_ci		/*
9438c2ecf20Sopenharmony_ci		 * these are the only "bad" events that are intermixed and that
9448c2ecf20Sopenharmony_ci		 * we can ignore without issue. make sure to skip them here
9458c2ecf20Sopenharmony_ci		 */
9468c2ecf20Sopenharmony_ci		if (event->event_group_record_len == 0)
9478c2ecf20Sopenharmony_ci			continue;
9488c2ecf20Sopenharmony_ci		if (!catalog_entry_domain_is_valid(event->domain))
9498c2ecf20Sopenharmony_ci			continue;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		name  = event_name(event, &nl);
9528c2ecf20Sopenharmony_ci		nonce = event_uniq_add(&ev_uniq, name, nl, event->domain);
9538c2ecf20Sopenharmony_ci		ct    = event_data_to_attrs(event_idx, events + event_attr_ct,
9548c2ecf20Sopenharmony_ci					    event, nonce);
9558c2ecf20Sopenharmony_ci		if (ct < 0) {
9568c2ecf20Sopenharmony_ci			pr_warn("event %zu (%.*s) creation failure, skipping\n",
9578c2ecf20Sopenharmony_ci				event_idx, nl, name);
9588c2ecf20Sopenharmony_ci			junk_events++;
9598c2ecf20Sopenharmony_ci		} else {
9608c2ecf20Sopenharmony_ci			event_attr_ct++;
9618c2ecf20Sopenharmony_ci			event_descs[desc_ct] = event_to_desc_attr(event, nonce);
9628c2ecf20Sopenharmony_ci			if (event_descs[desc_ct])
9638c2ecf20Sopenharmony_ci				desc_ct++;
9648c2ecf20Sopenharmony_ci			event_long_descs[long_desc_ct] =
9658c2ecf20Sopenharmony_ci					event_to_long_desc_attr(event, nonce);
9668c2ecf20Sopenharmony_ci			if (event_long_descs[long_desc_ct])
9678c2ecf20Sopenharmony_ci				long_desc_ct++;
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	pr_info("read %zu catalog entries, created %zu event attrs (%zu failures), %zu descs\n",
9728c2ecf20Sopenharmony_ci			event_idx, event_attr_ct, junk_events, desc_ct);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	events[event_attr_ct] = NULL;
9758c2ecf20Sopenharmony_ci	event_descs[desc_ct] = NULL;
9768c2ecf20Sopenharmony_ci	event_long_descs[long_desc_ct] = NULL;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	event_uniq_destroy(&ev_uniq);
9798c2ecf20Sopenharmony_ci	vfree(event_data);
9808c2ecf20Sopenharmony_ci	kmem_cache_free(hv_page_cache, page);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	*events_ = events;
9838c2ecf20Sopenharmony_ci	*event_descs_ = event_descs;
9848c2ecf20Sopenharmony_ci	*event_long_descs_ = event_long_descs;
9858c2ecf20Sopenharmony_ci	return 0;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cie_event_descs:
9888c2ecf20Sopenharmony_ci	kfree(event_descs);
9898c2ecf20Sopenharmony_cie_event_attrs:
9908c2ecf20Sopenharmony_ci	kfree(events);
9918c2ecf20Sopenharmony_cie_event_data:
9928c2ecf20Sopenharmony_ci	vfree(event_data);
9938c2ecf20Sopenharmony_cie_free:
9948c2ecf20Sopenharmony_ci	kmem_cache_free(hv_page_cache, page);
9958c2ecf20Sopenharmony_cie_out:
9968c2ecf20Sopenharmony_ci	*events_ = NULL;
9978c2ecf20Sopenharmony_ci	*event_descs_ = NULL;
9988c2ecf20Sopenharmony_ci	*event_long_descs_ = NULL;
9998c2ecf20Sopenharmony_ci	return ret;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic ssize_t catalog_read(struct file *filp, struct kobject *kobj,
10038c2ecf20Sopenharmony_ci			    struct bin_attribute *bin_attr, char *buf,
10048c2ecf20Sopenharmony_ci			    loff_t offset, size_t count)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	long hret;
10078c2ecf20Sopenharmony_ci	ssize_t ret = 0;
10088c2ecf20Sopenharmony_ci	size_t catalog_len = 0, catalog_page_len = 0;
10098c2ecf20Sopenharmony_ci	loff_t page_offset = 0;
10108c2ecf20Sopenharmony_ci	loff_t offset_in_page;
10118c2ecf20Sopenharmony_ci	size_t copy_len;
10128c2ecf20Sopenharmony_ci	uint64_t catalog_version_num = 0;
10138c2ecf20Sopenharmony_ci	void *page = kmem_cache_alloc(hv_page_cache, GFP_USER);
10148c2ecf20Sopenharmony_ci	struct hv_24x7_catalog_page_0 *page_0 = page;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (!page)
10178c2ecf20Sopenharmony_ci		return -ENOMEM;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	hret = h_get_24x7_catalog_page(page, 0, 0);
10208c2ecf20Sopenharmony_ci	if (hret) {
10218c2ecf20Sopenharmony_ci		ret = -EIO;
10228c2ecf20Sopenharmony_ci		goto e_free;
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	catalog_version_num = be64_to_cpu(page_0->version);
10268c2ecf20Sopenharmony_ci	catalog_page_len = be32_to_cpu(page_0->length);
10278c2ecf20Sopenharmony_ci	catalog_len = catalog_page_len * 4096;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	page_offset = offset / 4096;
10308c2ecf20Sopenharmony_ci	offset_in_page = offset % 4096;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	if (page_offset >= catalog_page_len)
10338c2ecf20Sopenharmony_ci		goto e_free;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (page_offset != 0) {
10368c2ecf20Sopenharmony_ci		hret = h_get_24x7_catalog_page(page, catalog_version_num,
10378c2ecf20Sopenharmony_ci					       page_offset);
10388c2ecf20Sopenharmony_ci		if (hret) {
10398c2ecf20Sopenharmony_ci			ret = -EIO;
10408c2ecf20Sopenharmony_ci			goto e_free;
10418c2ecf20Sopenharmony_ci		}
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	copy_len = 4096 - offset_in_page;
10458c2ecf20Sopenharmony_ci	if (copy_len > count)
10468c2ecf20Sopenharmony_ci		copy_len = count;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	memcpy(buf, page+offset_in_page, copy_len);
10498c2ecf20Sopenharmony_ci	ret = copy_len;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_cie_free:
10528c2ecf20Sopenharmony_ci	if (hret)
10538c2ecf20Sopenharmony_ci		pr_err("h_get_24x7_catalog_page(ver=%lld, page=%lld) failed:"
10548c2ecf20Sopenharmony_ci		       " rc=%ld\n",
10558c2ecf20Sopenharmony_ci		       catalog_version_num, page_offset, hret);
10568c2ecf20Sopenharmony_ci	kmem_cache_free(hv_page_cache, page);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	pr_devel("catalog_read: offset=%lld(%lld) count=%zu "
10598c2ecf20Sopenharmony_ci			"catalog_len=%zu(%zu) => %zd\n", offset, page_offset,
10608c2ecf20Sopenharmony_ci			count, catalog_len, catalog_page_len, ret);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	return ret;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic ssize_t domains_show(struct device *dev, struct device_attribute *attr,
10668c2ecf20Sopenharmony_ci			    char *page)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	int d, n, count = 0;
10698c2ecf20Sopenharmony_ci	const char *str;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	for (d = 0; d < HV_PERF_DOMAIN_MAX; d++) {
10728c2ecf20Sopenharmony_ci		str = domain_name(d);
10738c2ecf20Sopenharmony_ci		if (!str)
10748c2ecf20Sopenharmony_ci			continue;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci		n = sprintf(page, "%d: %s\n", d, str);
10778c2ecf20Sopenharmony_ci		if (n < 0)
10788c2ecf20Sopenharmony_ci			break;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci		count += n;
10818c2ecf20Sopenharmony_ci		page += n;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci	return count;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci#define PAGE_0_ATTR(_name, _fmt, _expr)				\
10878c2ecf20Sopenharmony_cistatic ssize_t _name##_show(struct device *dev,			\
10888c2ecf20Sopenharmony_ci			    struct device_attribute *dev_attr,	\
10898c2ecf20Sopenharmony_ci			    char *buf)				\
10908c2ecf20Sopenharmony_ci{								\
10918c2ecf20Sopenharmony_ci	long hret;						\
10928c2ecf20Sopenharmony_ci	ssize_t ret = 0;					\
10938c2ecf20Sopenharmony_ci	void *page = kmem_cache_alloc(hv_page_cache, GFP_USER);	\
10948c2ecf20Sopenharmony_ci	struct hv_24x7_catalog_page_0 *page_0 = page;		\
10958c2ecf20Sopenharmony_ci	if (!page)						\
10968c2ecf20Sopenharmony_ci		return -ENOMEM;					\
10978c2ecf20Sopenharmony_ci	hret = h_get_24x7_catalog_page(page, 0, 0);		\
10988c2ecf20Sopenharmony_ci	if (hret) {						\
10998c2ecf20Sopenharmony_ci		ret = -EIO;					\
11008c2ecf20Sopenharmony_ci		goto e_free;					\
11018c2ecf20Sopenharmony_ci	}							\
11028c2ecf20Sopenharmony_ci	ret = sprintf(buf, _fmt, _expr);			\
11038c2ecf20Sopenharmony_cie_free:								\
11048c2ecf20Sopenharmony_ci	kmem_cache_free(hv_page_cache, page);			\
11058c2ecf20Sopenharmony_ci	return ret;						\
11068c2ecf20Sopenharmony_ci}								\
11078c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(_name)
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ciPAGE_0_ATTR(catalog_version, "%lld\n",
11108c2ecf20Sopenharmony_ci		(unsigned long long)be64_to_cpu(page_0->version));
11118c2ecf20Sopenharmony_ciPAGE_0_ATTR(catalog_len, "%lld\n",
11128c2ecf20Sopenharmony_ci		(unsigned long long)be32_to_cpu(page_0->length) * 4096);
11138c2ecf20Sopenharmony_cistatic BIN_ATTR_RO(catalog, 0/* real length varies */);
11148c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(domains);
11158c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(sockets);
11168c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(chipspersocket);
11178c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(coresperchip);
11188c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpumask);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic struct bin_attribute *if_bin_attrs[] = {
11218c2ecf20Sopenharmony_ci	&bin_attr_catalog,
11228c2ecf20Sopenharmony_ci	NULL,
11238c2ecf20Sopenharmony_ci};
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cistatic struct attribute *cpumask_attrs[] = {
11268c2ecf20Sopenharmony_ci	&dev_attr_cpumask.attr,
11278c2ecf20Sopenharmony_ci	NULL,
11288c2ecf20Sopenharmony_ci};
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic struct attribute_group cpumask_attr_group = {
11318c2ecf20Sopenharmony_ci	.attrs = cpumask_attrs,
11328c2ecf20Sopenharmony_ci};
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cistatic struct attribute *if_attrs[] = {
11358c2ecf20Sopenharmony_ci	&dev_attr_catalog_len.attr,
11368c2ecf20Sopenharmony_ci	&dev_attr_catalog_version.attr,
11378c2ecf20Sopenharmony_ci	&dev_attr_domains.attr,
11388c2ecf20Sopenharmony_ci	&dev_attr_sockets.attr,
11398c2ecf20Sopenharmony_ci	&dev_attr_chipspersocket.attr,
11408c2ecf20Sopenharmony_ci	&dev_attr_coresperchip.attr,
11418c2ecf20Sopenharmony_ci	NULL,
11428c2ecf20Sopenharmony_ci};
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic struct attribute_group if_group = {
11458c2ecf20Sopenharmony_ci	.name = "interface",
11468c2ecf20Sopenharmony_ci	.bin_attrs = if_bin_attrs,
11478c2ecf20Sopenharmony_ci	.attrs = if_attrs,
11488c2ecf20Sopenharmony_ci};
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic const struct attribute_group *attr_groups[] = {
11518c2ecf20Sopenharmony_ci	&format_group,
11528c2ecf20Sopenharmony_ci	&event_group,
11538c2ecf20Sopenharmony_ci	&event_desc_group,
11548c2ecf20Sopenharmony_ci	&event_long_desc_group,
11558c2ecf20Sopenharmony_ci	&if_group,
11568c2ecf20Sopenharmony_ci	&cpumask_attr_group,
11578c2ecf20Sopenharmony_ci	NULL,
11588c2ecf20Sopenharmony_ci};
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci/*
11618c2ecf20Sopenharmony_ci * Start the process for a new H_GET_24x7_DATA hcall.
11628c2ecf20Sopenharmony_ci */
11638c2ecf20Sopenharmony_cistatic void init_24x7_request(struct hv_24x7_request_buffer *request_buffer,
11648c2ecf20Sopenharmony_ci			      struct hv_24x7_data_result_buffer *result_buffer)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	memset(request_buffer, 0, H24x7_DATA_BUFFER_SIZE);
11688c2ecf20Sopenharmony_ci	memset(result_buffer, 0, H24x7_DATA_BUFFER_SIZE);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	request_buffer->interface_version = interface_version;
11718c2ecf20Sopenharmony_ci	/* memset above set request_buffer->num_requests to 0 */
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci/*
11758c2ecf20Sopenharmony_ci * Commit (i.e perform) the H_GET_24x7_DATA hcall using the data collected
11768c2ecf20Sopenharmony_ci * by 'init_24x7_request()' and 'add_event_to_24x7_request()'.
11778c2ecf20Sopenharmony_ci */
11788c2ecf20Sopenharmony_cistatic int make_24x7_request(struct hv_24x7_request_buffer *request_buffer,
11798c2ecf20Sopenharmony_ci			     struct hv_24x7_data_result_buffer *result_buffer)
11808c2ecf20Sopenharmony_ci{
11818c2ecf20Sopenharmony_ci	long ret;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/*
11848c2ecf20Sopenharmony_ci	 * NOTE: Due to variable number of array elements in request and
11858c2ecf20Sopenharmony_ci	 *	 result buffer(s), sizeof() is not reliable. Use the actual
11868c2ecf20Sopenharmony_ci	 *	 allocated buffer size, H24x7_DATA_BUFFER_SIZE.
11878c2ecf20Sopenharmony_ci	 */
11888c2ecf20Sopenharmony_ci	ret = plpar_hcall_norets(H_GET_24X7_DATA,
11898c2ecf20Sopenharmony_ci			virt_to_phys(request_buffer), H24x7_DATA_BUFFER_SIZE,
11908c2ecf20Sopenharmony_ci			virt_to_phys(result_buffer),  H24x7_DATA_BUFFER_SIZE);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	if (ret) {
11938c2ecf20Sopenharmony_ci		struct hv_24x7_request *req;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		req = request_buffer->requests;
11968c2ecf20Sopenharmony_ci		pr_notice_ratelimited("hcall failed: [%d %#x %#x %d] => ret 0x%lx (%ld) detail=0x%x failing ix=%x\n",
11978c2ecf20Sopenharmony_ci				      req->performance_domain, req->data_offset,
11988c2ecf20Sopenharmony_ci				      req->starting_ix, req->starting_lpar_ix,
11998c2ecf20Sopenharmony_ci				      ret, ret, result_buffer->detailed_rc,
12008c2ecf20Sopenharmony_ci				      result_buffer->failing_request_ix);
12018c2ecf20Sopenharmony_ci		return -EIO;
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	return 0;
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci/*
12088c2ecf20Sopenharmony_ci * Add the given @event to the next slot in the 24x7 request_buffer.
12098c2ecf20Sopenharmony_ci *
12108c2ecf20Sopenharmony_ci * Note that H_GET_24X7_DATA hcall allows reading several counters'
12118c2ecf20Sopenharmony_ci * values in a single HCALL. We expect the caller to add events to the
12128c2ecf20Sopenharmony_ci * request buffer one by one, make the HCALL and process the results.
12138c2ecf20Sopenharmony_ci */
12148c2ecf20Sopenharmony_cistatic int add_event_to_24x7_request(struct perf_event *event,
12158c2ecf20Sopenharmony_ci				struct hv_24x7_request_buffer *request_buffer)
12168c2ecf20Sopenharmony_ci{
12178c2ecf20Sopenharmony_ci	u16 idx;
12188c2ecf20Sopenharmony_ci	int i;
12198c2ecf20Sopenharmony_ci	size_t req_size;
12208c2ecf20Sopenharmony_ci	struct hv_24x7_request *req;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	if (request_buffer->num_requests >=
12238c2ecf20Sopenharmony_ci	    max_num_requests(request_buffer->interface_version)) {
12248c2ecf20Sopenharmony_ci		pr_devel("Too many requests for 24x7 HCALL %d\n",
12258c2ecf20Sopenharmony_ci				request_buffer->num_requests);
12268c2ecf20Sopenharmony_ci		return -EINVAL;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	switch (event_get_domain(event)) {
12308c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_PHYS_CHIP:
12318c2ecf20Sopenharmony_ci		idx = event_get_chip(event);
12328c2ecf20Sopenharmony_ci		break;
12338c2ecf20Sopenharmony_ci	case HV_PERF_DOMAIN_PHYS_CORE:
12348c2ecf20Sopenharmony_ci		idx = event_get_core(event);
12358c2ecf20Sopenharmony_ci		break;
12368c2ecf20Sopenharmony_ci	default:
12378c2ecf20Sopenharmony_ci		idx = event_get_vcpu(event);
12388c2ecf20Sopenharmony_ci	}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	req_size = H24x7_REQUEST_SIZE(request_buffer->interface_version);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	i = request_buffer->num_requests++;
12438c2ecf20Sopenharmony_ci	req = (void *) request_buffer->requests + i * req_size;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	req->performance_domain = event_get_domain(event);
12468c2ecf20Sopenharmony_ci	req->data_size = cpu_to_be16(8);
12478c2ecf20Sopenharmony_ci	req->data_offset = cpu_to_be32(event_get_offset(event));
12488c2ecf20Sopenharmony_ci	req->starting_lpar_ix = cpu_to_be16(event_get_lpar(event));
12498c2ecf20Sopenharmony_ci	req->max_num_lpars = cpu_to_be16(1);
12508c2ecf20Sopenharmony_ci	req->starting_ix = cpu_to_be16(idx);
12518c2ecf20Sopenharmony_ci	req->max_ix = cpu_to_be16(1);
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	if (request_buffer->interface_version > 1) {
12548c2ecf20Sopenharmony_ci		if (domain_needs_aggregation(req->performance_domain))
12558c2ecf20Sopenharmony_ci			req->max_num_thread_groups = -1;
12568c2ecf20Sopenharmony_ci		else if (req->performance_domain != HV_PERF_DOMAIN_PHYS_CHIP) {
12578c2ecf20Sopenharmony_ci			req->starting_thread_group_ix = idx % 2;
12588c2ecf20Sopenharmony_ci			req->max_num_thread_groups = 1;
12598c2ecf20Sopenharmony_ci		}
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	return 0;
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci/**
12668c2ecf20Sopenharmony_ci * get_count_from_result - get event count from all result elements in result
12678c2ecf20Sopenharmony_ci *
12688c2ecf20Sopenharmony_ci * If the event corresponding to this result needs aggregation of the result
12698c2ecf20Sopenharmony_ci * element values, then this function does that.
12708c2ecf20Sopenharmony_ci *
12718c2ecf20Sopenharmony_ci * @event:	Event associated with @res.
12728c2ecf20Sopenharmony_ci * @resb:	Result buffer containing @res.
12738c2ecf20Sopenharmony_ci * @res:	Result to work on.
12748c2ecf20Sopenharmony_ci * @countp:	Output variable containing the event count.
12758c2ecf20Sopenharmony_ci * @next:	Optional output variable pointing to the next result in @resb.
12768c2ecf20Sopenharmony_ci */
12778c2ecf20Sopenharmony_cistatic int get_count_from_result(struct perf_event *event,
12788c2ecf20Sopenharmony_ci				 struct hv_24x7_data_result_buffer *resb,
12798c2ecf20Sopenharmony_ci				 struct hv_24x7_result *res, u64 *countp,
12808c2ecf20Sopenharmony_ci				 struct hv_24x7_result **next)
12818c2ecf20Sopenharmony_ci{
12828c2ecf20Sopenharmony_ci	u16 num_elements = be16_to_cpu(res->num_elements_returned);
12838c2ecf20Sopenharmony_ci	u16 data_size = be16_to_cpu(res->result_element_data_size);
12848c2ecf20Sopenharmony_ci	unsigned int data_offset;
12858c2ecf20Sopenharmony_ci	void *element_data;
12868c2ecf20Sopenharmony_ci	int i;
12878c2ecf20Sopenharmony_ci	u64 count;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/*
12908c2ecf20Sopenharmony_ci	 * We can bail out early if the result is empty.
12918c2ecf20Sopenharmony_ci	 */
12928c2ecf20Sopenharmony_ci	if (!num_elements) {
12938c2ecf20Sopenharmony_ci		pr_debug("Result of request %hhu is empty, nothing to do\n",
12948c2ecf20Sopenharmony_ci			 res->result_ix);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci		if (next)
12978c2ecf20Sopenharmony_ci			*next = (struct hv_24x7_result *) res->elements;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci		return -ENODATA;
13008c2ecf20Sopenharmony_ci	}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	/*
13038c2ecf20Sopenharmony_ci	 * Since we always specify 1 as the maximum for the smallest resource
13048c2ecf20Sopenharmony_ci	 * we're requesting, there should to be only one element per result.
13058c2ecf20Sopenharmony_ci	 * Except when an event needs aggregation, in which case there are more.
13068c2ecf20Sopenharmony_ci	 */
13078c2ecf20Sopenharmony_ci	if (num_elements != 1 &&
13088c2ecf20Sopenharmony_ci	    !domain_needs_aggregation(event_get_domain(event))) {
13098c2ecf20Sopenharmony_ci		pr_err("Error: result of request %hhu has %hu elements\n",
13108c2ecf20Sopenharmony_ci		       res->result_ix, num_elements);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci		return -EIO;
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	if (data_size != sizeof(u64)) {
13168c2ecf20Sopenharmony_ci		pr_debug("Error: result of request %hhu has data of %hu bytes\n",
13178c2ecf20Sopenharmony_ci			 res->result_ix, data_size);
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci		return -ENOTSUPP;
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	if (resb->interface_version == 1)
13238c2ecf20Sopenharmony_ci		data_offset = offsetof(struct hv_24x7_result_element_v1,
13248c2ecf20Sopenharmony_ci				       element_data);
13258c2ecf20Sopenharmony_ci	else
13268c2ecf20Sopenharmony_ci		data_offset = offsetof(struct hv_24x7_result_element_v2,
13278c2ecf20Sopenharmony_ci				       element_data);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	/* Go through the result elements in the result. */
13308c2ecf20Sopenharmony_ci	for (i = count = 0, element_data = res->elements + data_offset;
13318c2ecf20Sopenharmony_ci	     i < num_elements;
13328c2ecf20Sopenharmony_ci	     i++, element_data += data_size + data_offset)
13338c2ecf20Sopenharmony_ci		count += be64_to_cpu(*((u64 *) element_data));
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	*countp = count;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	/* The next result is after the last result element. */
13388c2ecf20Sopenharmony_ci	if (next)
13398c2ecf20Sopenharmony_ci		*next = element_data - data_offset;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	return 0;
13428c2ecf20Sopenharmony_ci}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cistatic int single_24x7_request(struct perf_event *event, u64 *count)
13458c2ecf20Sopenharmony_ci{
13468c2ecf20Sopenharmony_ci	int ret;
13478c2ecf20Sopenharmony_ci	struct hv_24x7_request_buffer *request_buffer;
13488c2ecf20Sopenharmony_ci	struct hv_24x7_data_result_buffer *result_buffer;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*request_buffer) > 4096);
13518c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*result_buffer) > 4096);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
13548c2ecf20Sopenharmony_ci	result_buffer = (void *)get_cpu_var(hv_24x7_resb);
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	init_24x7_request(request_buffer, result_buffer);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	ret = add_event_to_24x7_request(event, request_buffer);
13598c2ecf20Sopenharmony_ci	if (ret)
13608c2ecf20Sopenharmony_ci		goto out;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	ret = make_24x7_request(request_buffer, result_buffer);
13638c2ecf20Sopenharmony_ci	if (ret)
13648c2ecf20Sopenharmony_ci		goto out;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* process result from hcall */
13678c2ecf20Sopenharmony_ci	ret = get_count_from_result(event, result_buffer,
13688c2ecf20Sopenharmony_ci				    result_buffer->results, count, NULL);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ciout:
13718c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_reqb);
13728c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_resb);
13738c2ecf20Sopenharmony_ci	return ret;
13748c2ecf20Sopenharmony_ci}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_cistatic int h_24x7_event_init(struct perf_event *event)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	struct hv_perf_caps caps;
13808c2ecf20Sopenharmony_ci	unsigned domain;
13818c2ecf20Sopenharmony_ci	unsigned long hret;
13828c2ecf20Sopenharmony_ci	u64 ct;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/* Not our event */
13858c2ecf20Sopenharmony_ci	if (event->attr.type != event->pmu->type)
13868c2ecf20Sopenharmony_ci		return -ENOENT;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	/* Unused areas must be 0 */
13898c2ecf20Sopenharmony_ci	if (event_get_reserved1(event) ||
13908c2ecf20Sopenharmony_ci	    event_get_reserved2(event) ||
13918c2ecf20Sopenharmony_ci	    event_get_reserved3(event)) {
13928c2ecf20Sopenharmony_ci		pr_devel("reserved set when forbidden 0x%llx(0x%llx) 0x%llx(0x%llx) 0x%llx(0x%llx)\n",
13938c2ecf20Sopenharmony_ci				event->attr.config,
13948c2ecf20Sopenharmony_ci				event_get_reserved1(event),
13958c2ecf20Sopenharmony_ci				event->attr.config1,
13968c2ecf20Sopenharmony_ci				event_get_reserved2(event),
13978c2ecf20Sopenharmony_ci				event->attr.config2,
13988c2ecf20Sopenharmony_ci				event_get_reserved3(event));
13998c2ecf20Sopenharmony_ci		return -EINVAL;
14008c2ecf20Sopenharmony_ci	}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	/* no branch sampling */
14038c2ecf20Sopenharmony_ci	if (has_branch_stack(event))
14048c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	/* offset must be 8 byte aligned */
14078c2ecf20Sopenharmony_ci	if (event_get_offset(event) % 8) {
14088c2ecf20Sopenharmony_ci		pr_devel("bad alignment\n");
14098c2ecf20Sopenharmony_ci		return -EINVAL;
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	domain = event_get_domain(event);
14138c2ecf20Sopenharmony_ci	if (domain  == 0 || domain >= HV_PERF_DOMAIN_MAX) {
14148c2ecf20Sopenharmony_ci		pr_devel("invalid domain %d\n", domain);
14158c2ecf20Sopenharmony_ci		return -EINVAL;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	hret = hv_perf_caps_get(&caps);
14198c2ecf20Sopenharmony_ci	if (hret) {
14208c2ecf20Sopenharmony_ci		pr_devel("could not get capabilities: rc=%ld\n", hret);
14218c2ecf20Sopenharmony_ci		return -EIO;
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	/* Physical domains & other lpars require extra capabilities */
14258c2ecf20Sopenharmony_ci	if (!caps.collect_privileged && (is_physical_domain(domain) ||
14268c2ecf20Sopenharmony_ci		(event_get_lpar(event) != event_get_lpar_max()))) {
14278c2ecf20Sopenharmony_ci		pr_devel("hv permissions disallow: is_physical_domain:%d, lpar=0x%llx\n",
14288c2ecf20Sopenharmony_ci				is_physical_domain(domain),
14298c2ecf20Sopenharmony_ci				event_get_lpar(event));
14308c2ecf20Sopenharmony_ci		return -EACCES;
14318c2ecf20Sopenharmony_ci	}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	/* Get the initial value of the counter for this event */
14348c2ecf20Sopenharmony_ci	if (single_24x7_request(event, &ct)) {
14358c2ecf20Sopenharmony_ci		pr_devel("test hcall failed\n");
14368c2ecf20Sopenharmony_ci		return -EIO;
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci	(void)local64_xchg(&event->hw.prev_count, ct);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	return 0;
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic u64 h_24x7_get_value(struct perf_event *event)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	u64 ct;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	if (single_24x7_request(event, &ct))
14488c2ecf20Sopenharmony_ci		/* We checked this in event init, shouldn't fail here... */
14498c2ecf20Sopenharmony_ci		return 0;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	return ct;
14528c2ecf20Sopenharmony_ci}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cistatic void update_event_count(struct perf_event *event, u64 now)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	s64 prev;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	prev = local64_xchg(&event->hw.prev_count, now);
14598c2ecf20Sopenharmony_ci	local64_add(now - prev, &event->count);
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_cistatic void h_24x7_event_read(struct perf_event *event)
14638c2ecf20Sopenharmony_ci{
14648c2ecf20Sopenharmony_ci	u64 now;
14658c2ecf20Sopenharmony_ci	struct hv_24x7_request_buffer *request_buffer;
14668c2ecf20Sopenharmony_ci	struct hv_24x7_hw *h24x7hw;
14678c2ecf20Sopenharmony_ci	int txn_flags;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	txn_flags = __this_cpu_read(hv_24x7_txn_flags);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	/*
14728c2ecf20Sopenharmony_ci	 * If in a READ transaction, add this counter to the list of
14738c2ecf20Sopenharmony_ci	 * counters to read during the next HCALL (i.e commit_txn()).
14748c2ecf20Sopenharmony_ci	 * If not in a READ transaction, go ahead and make the HCALL
14758c2ecf20Sopenharmony_ci	 * to read this counter by itself.
14768c2ecf20Sopenharmony_ci	 */
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if (txn_flags & PERF_PMU_TXN_READ) {
14798c2ecf20Sopenharmony_ci		int i;
14808c2ecf20Sopenharmony_ci		int ret;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci		if (__this_cpu_read(hv_24x7_txn_err))
14838c2ecf20Sopenharmony_ci			return;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci		request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci		ret = add_event_to_24x7_request(event, request_buffer);
14888c2ecf20Sopenharmony_ci		if (ret) {
14898c2ecf20Sopenharmony_ci			__this_cpu_write(hv_24x7_txn_err, ret);
14908c2ecf20Sopenharmony_ci		} else {
14918c2ecf20Sopenharmony_ci			/*
14928c2ecf20Sopenharmony_ci			 * Associate the event with the HCALL request index,
14938c2ecf20Sopenharmony_ci			 * so ->commit_txn() can quickly find/update count.
14948c2ecf20Sopenharmony_ci			 */
14958c2ecf20Sopenharmony_ci			i = request_buffer->num_requests - 1;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci			h24x7hw = &get_cpu_var(hv_24x7_hw);
14988c2ecf20Sopenharmony_ci			h24x7hw->events[i] = event;
14998c2ecf20Sopenharmony_ci			put_cpu_var(h24x7hw);
15008c2ecf20Sopenharmony_ci		}
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci		put_cpu_var(hv_24x7_reqb);
15038c2ecf20Sopenharmony_ci	} else {
15048c2ecf20Sopenharmony_ci		now = h_24x7_get_value(event);
15058c2ecf20Sopenharmony_ci		update_event_count(event, now);
15068c2ecf20Sopenharmony_ci	}
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_cistatic void h_24x7_event_start(struct perf_event *event, int flags)
15108c2ecf20Sopenharmony_ci{
15118c2ecf20Sopenharmony_ci	if (flags & PERF_EF_RELOAD)
15128c2ecf20Sopenharmony_ci		local64_set(&event->hw.prev_count, h_24x7_get_value(event));
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_cistatic void h_24x7_event_stop(struct perf_event *event, int flags)
15168c2ecf20Sopenharmony_ci{
15178c2ecf20Sopenharmony_ci	h_24x7_event_read(event);
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_cistatic int h_24x7_event_add(struct perf_event *event, int flags)
15218c2ecf20Sopenharmony_ci{
15228c2ecf20Sopenharmony_ci	if (flags & PERF_EF_START)
15238c2ecf20Sopenharmony_ci		h_24x7_event_start(event, flags);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	return 0;
15268c2ecf20Sopenharmony_ci}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci/*
15298c2ecf20Sopenharmony_ci * 24x7 counters only support READ transactions. They are
15308c2ecf20Sopenharmony_ci * always counting and dont need/support ADD transactions.
15318c2ecf20Sopenharmony_ci * Cache the flags, but otherwise ignore transactions that
15328c2ecf20Sopenharmony_ci * are not PERF_PMU_TXN_READ.
15338c2ecf20Sopenharmony_ci */
15348c2ecf20Sopenharmony_cistatic void h_24x7_event_start_txn(struct pmu *pmu, unsigned int flags)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci	struct hv_24x7_request_buffer *request_buffer;
15378c2ecf20Sopenharmony_ci	struct hv_24x7_data_result_buffer *result_buffer;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	/* We should not be called if we are already in a txn */
15408c2ecf20Sopenharmony_ci	WARN_ON_ONCE(__this_cpu_read(hv_24x7_txn_flags));
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	__this_cpu_write(hv_24x7_txn_flags, flags);
15438c2ecf20Sopenharmony_ci	if (flags & ~PERF_PMU_TXN_READ)
15448c2ecf20Sopenharmony_ci		return;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
15478c2ecf20Sopenharmony_ci	result_buffer = (void *)get_cpu_var(hv_24x7_resb);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	init_24x7_request(request_buffer, result_buffer);
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_resb);
15528c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_reqb);
15538c2ecf20Sopenharmony_ci}
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci/*
15568c2ecf20Sopenharmony_ci * Clean up transaction state.
15578c2ecf20Sopenharmony_ci *
15588c2ecf20Sopenharmony_ci * NOTE: Ignore state of request and result buffers for now.
15598c2ecf20Sopenharmony_ci *	 We will initialize them during the next read/txn.
15608c2ecf20Sopenharmony_ci */
15618c2ecf20Sopenharmony_cistatic void reset_txn(void)
15628c2ecf20Sopenharmony_ci{
15638c2ecf20Sopenharmony_ci	__this_cpu_write(hv_24x7_txn_flags, 0);
15648c2ecf20Sopenharmony_ci	__this_cpu_write(hv_24x7_txn_err, 0);
15658c2ecf20Sopenharmony_ci}
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci/*
15688c2ecf20Sopenharmony_ci * 24x7 counters only support READ transactions. They are always counting
15698c2ecf20Sopenharmony_ci * and dont need/support ADD transactions. Clear ->txn_flags but otherwise
15708c2ecf20Sopenharmony_ci * ignore transactions that are not of type PERF_PMU_TXN_READ.
15718c2ecf20Sopenharmony_ci *
15728c2ecf20Sopenharmony_ci * For READ transactions, submit all pending 24x7 requests (i.e requests
15738c2ecf20Sopenharmony_ci * that were queued by h_24x7_event_read()), to the hypervisor and update
15748c2ecf20Sopenharmony_ci * the event counts.
15758c2ecf20Sopenharmony_ci */
15768c2ecf20Sopenharmony_cistatic int h_24x7_event_commit_txn(struct pmu *pmu)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	struct hv_24x7_request_buffer *request_buffer;
15798c2ecf20Sopenharmony_ci	struct hv_24x7_data_result_buffer *result_buffer;
15808c2ecf20Sopenharmony_ci	struct hv_24x7_result *res, *next_res;
15818c2ecf20Sopenharmony_ci	u64 count;
15828c2ecf20Sopenharmony_ci	int i, ret, txn_flags;
15838c2ecf20Sopenharmony_ci	struct hv_24x7_hw *h24x7hw;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	txn_flags = __this_cpu_read(hv_24x7_txn_flags);
15868c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!txn_flags);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	ret = 0;
15898c2ecf20Sopenharmony_ci	if (txn_flags & ~PERF_PMU_TXN_READ)
15908c2ecf20Sopenharmony_ci		goto out;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	ret = __this_cpu_read(hv_24x7_txn_err);
15938c2ecf20Sopenharmony_ci	if (ret)
15948c2ecf20Sopenharmony_ci		goto out;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
15978c2ecf20Sopenharmony_ci	result_buffer = (void *)get_cpu_var(hv_24x7_resb);
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	ret = make_24x7_request(request_buffer, result_buffer);
16008c2ecf20Sopenharmony_ci	if (ret)
16018c2ecf20Sopenharmony_ci		goto put_reqb;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	h24x7hw = &get_cpu_var(hv_24x7_hw);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	/* Go through results in the result buffer to update event counts. */
16068c2ecf20Sopenharmony_ci	for (i = 0, res = result_buffer->results;
16078c2ecf20Sopenharmony_ci	     i < result_buffer->num_results; i++, res = next_res) {
16088c2ecf20Sopenharmony_ci		struct perf_event *event = h24x7hw->events[res->result_ix];
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci		ret = get_count_from_result(event, result_buffer, res, &count,
16118c2ecf20Sopenharmony_ci					    &next_res);
16128c2ecf20Sopenharmony_ci		if (ret)
16138c2ecf20Sopenharmony_ci			break;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci		update_event_count(event, count);
16168c2ecf20Sopenharmony_ci	}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_hw);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ciput_reqb:
16218c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_resb);
16228c2ecf20Sopenharmony_ci	put_cpu_var(hv_24x7_reqb);
16238c2ecf20Sopenharmony_ciout:
16248c2ecf20Sopenharmony_ci	reset_txn();
16258c2ecf20Sopenharmony_ci	return ret;
16268c2ecf20Sopenharmony_ci}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci/*
16298c2ecf20Sopenharmony_ci * 24x7 counters only support READ transactions. They are always counting
16308c2ecf20Sopenharmony_ci * and dont need/support ADD transactions. However, regardless of type
16318c2ecf20Sopenharmony_ci * of transaction, all we need to do is cleanup, so we don't have to check
16328c2ecf20Sopenharmony_ci * the type of transaction.
16338c2ecf20Sopenharmony_ci */
16348c2ecf20Sopenharmony_cistatic void h_24x7_event_cancel_txn(struct pmu *pmu)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!__this_cpu_read(hv_24x7_txn_flags));
16378c2ecf20Sopenharmony_ci	reset_txn();
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic struct pmu h_24x7_pmu = {
16418c2ecf20Sopenharmony_ci	.task_ctx_nr = perf_invalid_context,
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	.name = "hv_24x7",
16448c2ecf20Sopenharmony_ci	.attr_groups = attr_groups,
16458c2ecf20Sopenharmony_ci	.event_init  = h_24x7_event_init,
16468c2ecf20Sopenharmony_ci	.add         = h_24x7_event_add,
16478c2ecf20Sopenharmony_ci	.del         = h_24x7_event_stop,
16488c2ecf20Sopenharmony_ci	.start       = h_24x7_event_start,
16498c2ecf20Sopenharmony_ci	.stop        = h_24x7_event_stop,
16508c2ecf20Sopenharmony_ci	.read        = h_24x7_event_read,
16518c2ecf20Sopenharmony_ci	.start_txn   = h_24x7_event_start_txn,
16528c2ecf20Sopenharmony_ci	.commit_txn  = h_24x7_event_commit_txn,
16538c2ecf20Sopenharmony_ci	.cancel_txn  = h_24x7_event_cancel_txn,
16548c2ecf20Sopenharmony_ci	.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
16558c2ecf20Sopenharmony_ci};
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_cistatic int ppc_hv_24x7_cpu_online(unsigned int cpu)
16588c2ecf20Sopenharmony_ci{
16598c2ecf20Sopenharmony_ci	if (cpumask_empty(&hv_24x7_cpumask))
16608c2ecf20Sopenharmony_ci		cpumask_set_cpu(cpu, &hv_24x7_cpumask);
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	return 0;
16638c2ecf20Sopenharmony_ci}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_cistatic int ppc_hv_24x7_cpu_offline(unsigned int cpu)
16668c2ecf20Sopenharmony_ci{
16678c2ecf20Sopenharmony_ci	int target;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	/* Check if exiting cpu is used for collecting 24x7 events */
16708c2ecf20Sopenharmony_ci	if (!cpumask_test_and_clear_cpu(cpu, &hv_24x7_cpumask))
16718c2ecf20Sopenharmony_ci		return 0;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	/* Find a new cpu to collect 24x7 events */
16748c2ecf20Sopenharmony_ci	target = cpumask_last(cpu_active_mask);
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	if (target < 0 || target >= nr_cpu_ids) {
16778c2ecf20Sopenharmony_ci		pr_err("hv_24x7: CPU hotplug init failed\n");
16788c2ecf20Sopenharmony_ci		return -1;
16798c2ecf20Sopenharmony_ci	}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	/* Migrate 24x7 events to the new target */
16828c2ecf20Sopenharmony_ci	cpumask_set_cpu(target, &hv_24x7_cpumask);
16838c2ecf20Sopenharmony_ci	perf_pmu_migrate_context(&h_24x7_pmu, cpu, target);
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	return 0;
16868c2ecf20Sopenharmony_ci}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_cistatic int hv_24x7_cpu_hotplug_init(void)
16898c2ecf20Sopenharmony_ci{
16908c2ecf20Sopenharmony_ci	return cpuhp_setup_state(CPUHP_AP_PERF_POWERPC_HV_24x7_ONLINE,
16918c2ecf20Sopenharmony_ci			  "perf/powerpc/hv_24x7:online",
16928c2ecf20Sopenharmony_ci			  ppc_hv_24x7_cpu_online,
16938c2ecf20Sopenharmony_ci			  ppc_hv_24x7_cpu_offline);
16948c2ecf20Sopenharmony_ci}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_cistatic int hv_24x7_init(void)
16978c2ecf20Sopenharmony_ci{
16988c2ecf20Sopenharmony_ci	int r;
16998c2ecf20Sopenharmony_ci	unsigned long hret;
17008c2ecf20Sopenharmony_ci	struct hv_perf_caps caps;
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_LPAR)) {
17038c2ecf20Sopenharmony_ci		pr_debug("not a virtualized system, not enabling\n");
17048c2ecf20Sopenharmony_ci		return -ENODEV;
17058c2ecf20Sopenharmony_ci	} else if (!cur_cpu_spec->oprofile_cpu_type)
17068c2ecf20Sopenharmony_ci		return -ENODEV;
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	/* POWER8 only supports v1, while POWER9 only supports v2. */
17098c2ecf20Sopenharmony_ci	if (!strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power8"))
17108c2ecf20Sopenharmony_ci		interface_version = 1;
17118c2ecf20Sopenharmony_ci	else {
17128c2ecf20Sopenharmony_ci		interface_version = 2;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci		/* SMT8 in POWER9 needs to aggregate result elements. */
17158c2ecf20Sopenharmony_ci		if (threads_per_core == 8)
17168c2ecf20Sopenharmony_ci			aggregate_result_elements = true;
17178c2ecf20Sopenharmony_ci	}
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	hret = hv_perf_caps_get(&caps);
17208c2ecf20Sopenharmony_ci	if (hret) {
17218c2ecf20Sopenharmony_ci		pr_debug("could not obtain capabilities, not enabling, rc=%ld\n",
17228c2ecf20Sopenharmony_ci				hret);
17238c2ecf20Sopenharmony_ci		return -ENODEV;
17248c2ecf20Sopenharmony_ci	}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	hv_page_cache = kmem_cache_create("hv-page-4096", 4096, 4096, 0, NULL);
17278c2ecf20Sopenharmony_ci	if (!hv_page_cache)
17288c2ecf20Sopenharmony_ci		return -ENOMEM;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	/* sampling not supported */
17318c2ecf20Sopenharmony_ci	h_24x7_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	r = create_events_from_catalog(&event_group.attrs,
17348c2ecf20Sopenharmony_ci				   &event_desc_group.attrs,
17358c2ecf20Sopenharmony_ci				   &event_long_desc_group.attrs);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	if (r)
17388c2ecf20Sopenharmony_ci		return r;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	/* init cpuhotplug */
17418c2ecf20Sopenharmony_ci	r = hv_24x7_cpu_hotplug_init();
17428c2ecf20Sopenharmony_ci	if (r)
17438c2ecf20Sopenharmony_ci		return r;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	r = perf_pmu_register(&h_24x7_pmu, h_24x7_pmu.name, -1);
17468c2ecf20Sopenharmony_ci	if (r)
17478c2ecf20Sopenharmony_ci		return r;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	read_24x7_sys_info();
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	return 0;
17528c2ecf20Sopenharmony_ci}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_cidevice_initcall(hv_24x7_init);
1755