From 145264e2129a5e2fca9bc75f666e80424020f9ea Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:38 -0700 Subject: powerpc/perf/hv-24x7: Modify definition of request and result buffers The parameters to the 24x7 HCALL have variable number of elements in them. Set the minimum number of such elements to 1 rather than 0 and eliminate the temporary structures. This would enable us to submit multiple counter requests and process multiple results from a single HCALL (in a follow on patch). Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 77 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 40 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 9445a824819e..408e6e9ff449 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -142,6 +142,15 @@ static struct attribute_group event_long_desc_group = { static struct kmem_cache *hv_page_cache; +/* + * request_buffer and result_buffer are not required to be 4k aligned, + * but are not allowed to cross any 4k boundary. Aligning them to 4k is + * the simplest way to ensure that. + */ +#define H24x7_DATA_BUFFER_SIZE 4096 +DEFINE_PER_CPU(char, hv_24x7_reqb[H24x7_DATA_BUFFER_SIZE]) __aligned(4096); +DEFINE_PER_CPU(char, hv_24x7_resb[H24x7_DATA_BUFFER_SIZE]) __aligned(4096); + static char *event_name(struct hv_24x7_event_data *ev, int *len) { *len = be16_to_cpu(ev->event_name_len) - 2; @@ -976,31 +985,16 @@ static const struct attribute_group *attr_groups[] = { NULL, }; -DEFINE_PER_CPU(char, hv_24x7_reqb[4096]) __aligned(4096); -DEFINE_PER_CPU(char, hv_24x7_resb[4096]) __aligned(4096); - static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, - u16 lpar, u64 *res, + u16 lpar, u64 *count, bool success_expected) { unsigned long ret; - /* - * request_buffer and result_buffer are not required to be 4k aligned, - * but are not allowed to cross any 4k boundary. Aligning them to 4k is - * the simplest way to ensure that. - */ - struct reqb { - struct hv_24x7_request_buffer buf; - struct hv_24x7_request req; - } __packed *request_buffer; - - struct { - struct hv_24x7_data_result_buffer buf; - struct hv_24x7_result res; - struct hv_24x7_result_element elem; - __be64 result; - } __packed *result_buffer; + struct hv_24x7_request_buffer *request_buffer; + struct hv_24x7_data_result_buffer *result_buffer; + struct hv_24x7_result *resb; + struct hv_24x7_request *req; BUILD_BUG_ON(sizeof(*request_buffer) > 4096); BUILD_BUG_ON(sizeof(*result_buffer) > 4096); @@ -1011,38 +1005,41 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, memset(request_buffer, 0, 4096); memset(result_buffer, 0, 4096); - *request_buffer = (struct reqb) { - .buf = { - .interface_version = HV_24X7_IF_VERSION_CURRENT, - .num_requests = 1, - }, - .req = { - .performance_domain = domain, - .data_size = cpu_to_be16(8), - .data_offset = cpu_to_be32(offset), - .starting_lpar_ix = cpu_to_be16(lpar), - .max_num_lpars = cpu_to_be16(1), - .starting_ix = cpu_to_be16(ix), - .max_ix = cpu_to_be16(1), - } - }; + request_buffer->interface_version = HV_24X7_IF_VERSION_CURRENT; + request_buffer->num_requests = 1; + + req = &request_buffer->requests[0]; + req->performance_domain = domain; + req->data_size = cpu_to_be16(8); + req->data_offset = cpu_to_be32(offset); + req->starting_lpar_ix = cpu_to_be16(lpar), + req->max_num_lpars = cpu_to_be16(1); + req->starting_ix = cpu_to_be16(ix); + req->max_ix = cpu_to_be16(1); + + /* + * NOTE: Due to variable number of array elements in request and + * result buffer(s), sizeof() is not reliable. Use the actual + * allocated buffer size, H24x7_DATA_BUFFER_SIZE. + */ ret = plpar_hcall_norets(H_GET_24X7_DATA, - virt_to_phys(request_buffer), sizeof(*request_buffer), - virt_to_phys(result_buffer), sizeof(*result_buffer)); + virt_to_phys(request_buffer), H24x7_DATA_BUFFER_SIZE, + virt_to_phys(result_buffer), H24x7_DATA_BUFFER_SIZE); if (ret) { if (success_expected) pr_err_ratelimited("hcall failed: %d %#x %#x %d => " "0x%lx (%ld) detail=0x%x failing ix=%x\n", domain, offset, ix, lpar, ret, ret, - result_buffer->buf.detailed_rc, - result_buffer->buf.failing_request_ix); + result_buffer->detailed_rc, + result_buffer->failing_request_ix); goto out; } - *res = be64_to_cpu(result_buffer->result); + resb = &result_buffer->results[0]; + *count = be64_to_cpu(resb->elements[0].element_data[0]); out: return ret; } -- cgit v1.2.3 From f2b1237c732d3f3ce18e85c84782a4d1f3b5ba08 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:39 -0700 Subject: powerpc/perf/hv-24x7: Remove unnecessary parameter Remove the 'success_expected' parameter and log the message unconditionally. Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 408e6e9ff449..c185dcfe6271 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -986,8 +986,7 @@ static const struct attribute_group *attr_groups[] = { }; static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, - u16 lpar, u64 *count, - bool success_expected) + u16 lpar, u64 *count) { unsigned long ret; @@ -1028,8 +1027,7 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, virt_to_phys(result_buffer), H24x7_DATA_BUFFER_SIZE); if (ret) { - if (success_expected) - pr_err_ratelimited("hcall failed: %d %#x %#x %d => " + pr_err_ratelimited("hcall failed: %d %#x %#x %d => " "0x%lx (%ld) detail=0x%x failing ix=%x\n", domain, offset, ix, lpar, ret, ret, result_buffer->detailed_rc, @@ -1044,8 +1042,7 @@ out: return ret; } -static unsigned long event_24x7_request(struct perf_event *event, u64 *res, - bool success_expected) +static unsigned long event_24x7_request(struct perf_event *event, u64 *res) { u16 idx; unsigned domain = event_get_domain(event); @@ -1059,8 +1056,7 @@ static unsigned long event_24x7_request(struct perf_event *event, u64 *res, event_get_offset(event), idx, event_get_lpar(event), - res, - success_expected); + res); } static int h_24x7_event_init(struct perf_event *event) @@ -1130,7 +1126,7 @@ static int h_24x7_event_init(struct perf_event *event) } /* see if the event complains */ - if (event_24x7_request(event, &ct, false)) { + if (event_24x7_request(event, &ct)) { pr_devel("test hcall failed\n"); return -EIO; } @@ -1142,7 +1138,7 @@ static u64 h_24x7_get_value(struct perf_event *event) { unsigned long ret; u64 ct; - ret = event_24x7_request(event, &ct, true); + ret = event_24x7_request(event, &ct); if (ret) /* We checked this in event init, shouldn't fail here... */ return 0; -- cgit v1.2.3 From 7aabe0cec2bcfcb08a4e706ec7c794883cbbcf6f Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:40 -0700 Subject: powerpc/perf/hv-24x7: Use pr_devel() to log message Use pr_devel_ratelimited() to log error message when the 24x7 HCALL fails. Since users specify events by their sysfs name, the HCALL should succeed. Any errors reported by the HCALL would be of interest to the developer, rather than the user/administrator. Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index c185dcfe6271..87c99056ed54 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1027,7 +1027,7 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, virt_to_phys(result_buffer), H24x7_DATA_BUFFER_SIZE); if (ret) { - pr_err_ratelimited("hcall failed: %d %#x %#x %d => " + pr_devel_ratelimited("hcall failed: %d %#x %#x %d => " "0x%lx (%ld) detail=0x%x failing ix=%x\n", domain, offset, ix, lpar, ret, ret, result_buffer->detailed_rc, -- cgit v1.2.3 From 8079876497653381c233445374465c3da6450766 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:41 -0700 Subject: powerpc/perf/hv-24x7: Drop event_24x7_request() The function event_24x7_request() is essentially a wrapper to the function single_24x7_request() and can be dropped to simplify code. Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 87c99056ed54..f509f3bd7d2a 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -985,9 +985,9 @@ static const struct attribute_group *attr_groups[] = { NULL, }; -static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, - u16 lpar, u64 *count) +static unsigned long single_24x7_request(struct perf_event *event, u64 *count) { + u16 idx; unsigned long ret; struct hv_24x7_request_buffer *request_buffer; @@ -1004,17 +1004,22 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, memset(request_buffer, 0, 4096); memset(result_buffer, 0, 4096); + if (is_physical_domain(event_get_domain(event))) + idx = event_get_core(event); + else + idx = event_get_vcpu(event); + request_buffer->interface_version = HV_24X7_IF_VERSION_CURRENT; request_buffer->num_requests = 1; req = &request_buffer->requests[0]; - req->performance_domain = domain; + req->performance_domain = event_get_domain(event); req->data_size = cpu_to_be16(8); - req->data_offset = cpu_to_be32(offset); - req->starting_lpar_ix = cpu_to_be16(lpar), + req->data_offset = cpu_to_be32(event_get_offset(event)); + req->starting_lpar_ix = cpu_to_be16(event_get_lpar(event)), req->max_num_lpars = cpu_to_be16(1); - req->starting_ix = cpu_to_be16(ix); + req->starting_ix = cpu_to_be16(idx); req->max_ix = cpu_to_be16(1); /* @@ -1029,7 +1034,8 @@ static unsigned long single_24x7_request(u8 domain, u32 offset, u16 ix, if (ret) { pr_devel_ratelimited("hcall failed: %d %#x %#x %d => " "0x%lx (%ld) detail=0x%x failing ix=%x\n", - domain, offset, ix, lpar, ret, ret, + req->performance_domain, req->data_offset, + idx, req->starting_lpar_ix, ret, ret, result_buffer->detailed_rc, result_buffer->failing_request_ix); goto out; @@ -1042,22 +1048,6 @@ out: return ret; } -static unsigned long event_24x7_request(struct perf_event *event, u64 *res) -{ - u16 idx; - unsigned domain = event_get_domain(event); - - if (is_physical_domain(domain)) - idx = event_get_core(event); - else - idx = event_get_vcpu(event); - - return single_24x7_request(event_get_domain(event), - event_get_offset(event), - idx, - event_get_lpar(event), - res); -} static int h_24x7_event_init(struct perf_event *event) { @@ -1126,7 +1116,7 @@ static int h_24x7_event_init(struct perf_event *event) } /* see if the event complains */ - if (event_24x7_request(event, &ct)) { + if (single_24x7_request(event, &ct)) { pr_devel("test hcall failed\n"); return -EIO; } @@ -1138,7 +1128,7 @@ static u64 h_24x7_get_value(struct perf_event *event) { unsigned long ret; u64 ct; - ret = event_24x7_request(event, &ct); + ret = single_24x7_request(event, &ct); if (ret) /* We checked this in event init, shouldn't fail here... */ return 0; -- cgit v1.2.3 From f954825dd9c47ffaa0e6ff86e16a2355ae8a81d7 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:42 -0700 Subject: powerpc/perf/hv-24x7: Move debug prints to separate function To simplify/cleanup code, move the rather long printk() to a separate function. Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index f509f3bd7d2a..a58a1dfb15e2 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -985,6 +985,21 @@ static const struct attribute_group *attr_groups[] = { NULL, }; +static void log_24x7_hcall(struct hv_24x7_request_buffer *request_buffer, + struct hv_24x7_data_result_buffer *result_buffer, + unsigned long ret) +{ + struct hv_24x7_request *req; + + req = &request_buffer->requests[0]; + pr_notice_ratelimited("hcall failed: [%d %#x %#x %d] => " + "ret 0x%lx (%ld) detail=0x%x failing ix=%x\n", + req->performance_domain, req->data_offset, + req->starting_ix, req->starting_lpar_ix, ret, ret, + result_buffer->detailed_rc, + result_buffer->failing_request_ix); +} + static unsigned long single_24x7_request(struct perf_event *event, u64 *count) { u16 idx; @@ -1032,12 +1047,7 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count) virt_to_phys(result_buffer), H24x7_DATA_BUFFER_SIZE); if (ret) { - pr_devel_ratelimited("hcall failed: %d %#x %#x %d => " - "0x%lx (%ld) detail=0x%x failing ix=%x\n", - req->performance_domain, req->data_offset, - idx, req->starting_lpar_ix, ret, ret, - result_buffer->detailed_rc, - result_buffer->failing_request_ix); + log_24x7_hcall(request_buffer, result_buffer, ret); goto out; } -- cgit v1.2.3 From 33ba14c0d8ff64d51adb543f689cde7a9a227cd8 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:43 -0700 Subject: powerpc/perf/hv-24x7: Rename hv_24x7_event_update For consistency with the pmu operation ->read() and with other pmus, rename hv_24x7_event_update() to hv_24x7_event_read(). Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index a58a1dfb15e2..e78b1272aa0e 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1146,7 +1146,7 @@ static u64 h_24x7_get_value(struct perf_event *event) return ct; } -static void h_24x7_event_update(struct perf_event *event) +static void h_24x7_event_read(struct perf_event *event) { s64 prev; u64 now; @@ -1163,7 +1163,7 @@ static void h_24x7_event_start(struct perf_event *event, int flags) static void h_24x7_event_stop(struct perf_event *event, int flags) { - h_24x7_event_update(event); + h_24x7_event_read(event); } static int h_24x7_event_add(struct perf_event *event, int flags) @@ -1184,7 +1184,7 @@ static struct pmu h_24x7_pmu = { .del = h_24x7_event_stop, .start = h_24x7_event_start, .stop = h_24x7_event_stop, - .read = h_24x7_event_update, + .read = h_24x7_event_read, }; static int hv_24x7_init(void) -- cgit v1.2.3 From e3ee15dc5d19d060012d0a89b085d56e50d40a2b Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:44 -0700 Subject: powerpc/perf/hv-24x7: Define add_event_to_24x7_request() Move code that maps a perf_event to a 24x7 request buffer into a separate function, add_event_to_24x7_request(). Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 59 ++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 17 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index e78b1272aa0e..fe74221a35ff 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1000,15 +1000,52 @@ static void log_24x7_hcall(struct hv_24x7_request_buffer *request_buffer, result_buffer->failing_request_ix); } -static unsigned long single_24x7_request(struct perf_event *event, u64 *count) +/* + * Add the given @event to the next slot in the 24x7 request_buffer. + * + * Note that H_GET_24X7_DATA hcall allows reading several counters' + * values in a single HCALL. We expect the caller to add events to the + * request buffer one by one, make the HCALL and process the results. + */ +static int add_event_to_24x7_request(struct perf_event *event, + struct hv_24x7_request_buffer *request_buffer) { u16 idx; + int i; + struct hv_24x7_request *req; + + if (request_buffer->num_requests > 254) { + pr_devel("Too many requests for 24x7 HCALL %d\n", + request_buffer->num_requests); + return -EINVAL; + } + + if (is_physical_domain(event_get_domain(event))) + idx = event_get_core(event); + else + idx = event_get_vcpu(event); + + i = request_buffer->num_requests++; + req = &request_buffer->requests[i]; + + req->performance_domain = event_get_domain(event); + req->data_size = cpu_to_be16(8); + req->data_offset = cpu_to_be32(event_get_offset(event)); + req->starting_lpar_ix = cpu_to_be16(event_get_lpar(event)), + req->max_num_lpars = cpu_to_be16(1); + req->starting_ix = cpu_to_be16(idx); + req->max_ix = cpu_to_be16(1); + + return 0; +} + +static unsigned long single_24x7_request(struct perf_event *event, u64 *count) +{ unsigned long ret; struct hv_24x7_request_buffer *request_buffer; struct hv_24x7_data_result_buffer *result_buffer; struct hv_24x7_result *resb; - struct hv_24x7_request *req; BUILD_BUG_ON(sizeof(*request_buffer) > 4096); BUILD_BUG_ON(sizeof(*result_buffer) > 4096); @@ -1019,23 +1056,11 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count) memset(request_buffer, 0, 4096); memset(result_buffer, 0, 4096); - if (is_physical_domain(event_get_domain(event))) - idx = event_get_core(event); - else - idx = event_get_vcpu(event); - request_buffer->interface_version = HV_24X7_IF_VERSION_CURRENT; - request_buffer->num_requests = 1; - - req = &request_buffer->requests[0]; - req->performance_domain = event_get_domain(event); - req->data_size = cpu_to_be16(8); - req->data_offset = cpu_to_be32(event_get_offset(event)); - req->starting_lpar_ix = cpu_to_be16(event_get_lpar(event)), - req->max_num_lpars = cpu_to_be16(1); - req->starting_ix = cpu_to_be16(idx); - req->max_ix = cpu_to_be16(1); + ret = add_event_to_24x7_request(event, request_buffer); + if (ret) + return ret; /* * NOTE: Due to variable number of array elements in request and -- cgit v1.2.3 From 3ca4ea71cb27b4de781dd86f06e322ee11c82975 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:45 -0700 Subject: powerpc/perf/hv-24x7: Whitespace cleanup Fix minor whitespace damages. Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index fe74221a35ff..676fb2f93825 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -161,6 +161,7 @@ static char *event_desc(struct hv_24x7_event_data *ev, int *len) { unsigned nl = be16_to_cpu(ev->event_name_len); __be16 *desc_len = (__be16 *)(ev->remainder + nl - 2); + *len = be16_to_cpu(*desc_len) - 2; return (char *)ev->remainder + nl; } @@ -171,6 +172,7 @@ static char *event_long_desc(struct hv_24x7_event_data *ev, int *len) __be16 *desc_len_ = (__be16 *)(ev->remainder + nl - 2); unsigned desc_len = be16_to_cpu(*desc_len_); __be16 *long_desc_len = (__be16 *)(ev->remainder + nl + desc_len - 2); + *len = be16_to_cpu(*long_desc_len) - 2; return (char *)ev->remainder + nl + desc_len; } @@ -248,14 +250,12 @@ static unsigned long h_get_24x7_catalog_page_(unsigned long phys_4096, unsigned long index) { pr_devel("h_get_24x7_catalog_page(0x%lx, %lu, %lu)", - phys_4096, - version, - index); + phys_4096, version, index); + WARN_ON(!IS_ALIGNED(phys_4096, 4096)); + return plpar_hcall_norets(H_GET_24X7_CATALOG_PAGE, - phys_4096, - version, - index); + phys_4096, version, index); } static unsigned long h_get_24x7_catalog_page(char page[], @@ -309,6 +309,7 @@ static ssize_t device_show_string(struct device *dev, struct dev_ext_attribute *d; d = container_of(attr, struct dev_ext_attribute, attr); + return sprintf(buf, "%s\n", (char *)d->var); } @@ -323,6 +324,7 @@ static struct attribute *device_str_attr_create_(char *name, char *str) attr->attr.attr.name = name; attr->attr.attr.mode = 0444; attr->attr.show = device_show_string; + return &attr->attr.attr; } @@ -396,7 +398,6 @@ static struct attribute *event_to_attr(unsigned ix, a_ev_name = kasprintf(GFP_KERNEL, "%.*s%s__%d", (int)event_name_len, ev_name, ev_suffix, nonce); - if (!a_ev_name) goto out_val; @@ -881,6 +882,7 @@ static ssize_t catalog_read(struct file *filp, struct kobject *kobj, uint64_t catalog_version_num = 0; void *page = kmem_cache_alloc(hv_page_cache, GFP_USER); struct hv_24x7_catalog_page_0 *page_0 = page; + if (!page) return -ENOMEM; @@ -1077,7 +1079,6 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count) } resb = &result_buffer->results[0]; - *count = be64_to_cpu(resb->elements[0].element_data[0]); out: return ret; @@ -1175,6 +1176,7 @@ static void h_24x7_event_read(struct perf_event *event) { s64 prev; u64 now; + now = h_24x7_get_value(event); prev = local64_xchg(&event->hw.prev_count, now); local64_add(now - prev, &event->count); -- cgit v1.2.3 From 529ce8c9ddd00bd1ee77727f7a83c02dff0684f8 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:46 -0700 Subject: powerpc/perf/hv-24x7: Define update_event_count() Move the code to update an event count into a new function, update_event_count(). Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 676fb2f93825..cf820269bfb6 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1172,16 +1172,22 @@ static u64 h_24x7_get_value(struct perf_event *event) return ct; } -static void h_24x7_event_read(struct perf_event *event) +static void update_event_count(struct perf_event *event, u64 now) { s64 prev; - u64 now; - now = h_24x7_get_value(event); prev = local64_xchg(&event->hw.prev_count, now); local64_add(now - prev, &event->count); } +static void h_24x7_event_read(struct perf_event *event) +{ + u64 now; + + now = h_24x7_get_value(event); + update_event_count(event, now); +} + static void h_24x7_event_start(struct perf_event *event, int flags) { if (flags & PERF_EF_RELOAD) -- cgit v1.2.3 From aeab199d84c2616ffc5e29aa4831bd8a660bde96 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Mon, 30 Mar 2015 18:53:47 -0700 Subject: powerpc/perf/hv-24x7: Break up single_24x7_request Break up the function single_24x7_request() into smaller functions. This would later enable us to "prepare" a multi-event request buffer and then submit a single hcall for several events. Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 56 +++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 14 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index cf820269bfb6..46be032f60ad 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1002,6 +1002,44 @@ static void log_24x7_hcall(struct hv_24x7_request_buffer *request_buffer, result_buffer->failing_request_ix); } +/* + * Start the process for a new H_GET_24x7_DATA hcall. + */ +static void init_24x7_request(struct hv_24x7_request_buffer *request_buffer, + struct hv_24x7_data_result_buffer *result_buffer) +{ + + memset(request_buffer, 0, 4096); + memset(result_buffer, 0, 4096); + + request_buffer->interface_version = HV_24X7_IF_VERSION_CURRENT; + /* memset above set request_buffer->num_requests to 0 */ +} + +/* + * Commit (i.e perform) the H_GET_24x7_DATA hcall using the data collected + * by 'init_24x7_request()' and 'add_event_to_24x7_request()'. + */ +static int make_24x7_request(struct hv_24x7_request_buffer *request_buffer, + struct hv_24x7_data_result_buffer *result_buffer) +{ + unsigned long ret; + + /* + * NOTE: Due to variable number of array elements in request and + * result buffer(s), sizeof() is not reliable. Use the actual + * allocated buffer size, H24x7_DATA_BUFFER_SIZE. + */ + ret = plpar_hcall_norets(H_GET_24X7_DATA, + virt_to_phys(request_buffer), H24x7_DATA_BUFFER_SIZE, + virt_to_phys(result_buffer), H24x7_DATA_BUFFER_SIZE); + + if (ret) + log_24x7_hcall(request_buffer, result_buffer, ret); + + return ret; +} + /* * Add the given @event to the next slot in the 24x7 request_buffer. * @@ -1044,7 +1082,6 @@ static int add_event_to_24x7_request(struct perf_event *event, static unsigned long single_24x7_request(struct perf_event *event, u64 *count) { unsigned long ret; - struct hv_24x7_request_buffer *request_buffer; struct hv_24x7_data_result_buffer *result_buffer; struct hv_24x7_result *resb; @@ -1055,31 +1092,22 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count) request_buffer = (void *)get_cpu_var(hv_24x7_reqb); result_buffer = (void *)get_cpu_var(hv_24x7_resb); - memset(request_buffer, 0, 4096); - memset(result_buffer, 0, 4096); - - request_buffer->interface_version = HV_24X7_IF_VERSION_CURRENT; + init_24x7_request(request_buffer, result_buffer); ret = add_event_to_24x7_request(event, request_buffer); if (ret) return ret; - /* - * NOTE: Due to variable number of array elements in request and - * result buffer(s), sizeof() is not reliable. Use the actual - * allocated buffer size, H24x7_DATA_BUFFER_SIZE. - */ - ret = plpar_hcall_norets(H_GET_24X7_DATA, - virt_to_phys(request_buffer), H24x7_DATA_BUFFER_SIZE, - virt_to_phys(result_buffer), H24x7_DATA_BUFFER_SIZE); - + ret = make_24x7_request(request_buffer, result_buffer); if (ret) { log_24x7_hcall(request_buffer, result_buffer, ret); goto out; } + /* process result from hcall */ resb = &result_buffer->results[0]; *count = be64_to_cpu(resb->elements[0].element_data[0]); + out: return ret; } -- cgit v1.2.3 From b816ce67fcb57f9d51681cb3d26fd6598b4351ed Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 17 Feb 2015 14:14:36 -0500 Subject: powerpc/perf/hv-24x7: Add missing put_cpu_var() Add missing put_cpu_var() for 24x7 requests. This went missing in commit f34b6c7 (3.18-rc3). Signed-off-by: Sukadev Bhattiprolu Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 46be032f60ad..ead8878ca62b 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1096,7 +1096,7 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count) ret = add_event_to_24x7_request(event, request_buffer); if (ret) - return ret; + goto out; ret = make_24x7_request(request_buffer, result_buffer); if (ret) { @@ -1109,6 +1109,8 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count) *count = be64_to_cpu(resb->elements[0].element_data[0]); out: + put_cpu_var(hv_24x7_reqb); + put_cpu_var(hv_24x7_resb); return ret; } -- cgit v1.2.3 From 7debc970ae7a5573ed43a1dfa242fd1a5390d21a Mon Sep 17 00:00:00 2001 From: Li Zhong Date: Mon, 13 Apr 2015 15:48:37 +0800 Subject: powerpc/perf/hv-24x7: Fail 24x7 initcall if create_events_from_catalog() fails As Michael pointed out, create_events_from_catalog() fails when we either have: - a kernel bug - some sort of hypervisor misconfiguration - ENOMEM In all the above cases, we can also fail 24x7 initcall. For hypervisor errors, EIO is used so there is something reported in dmesg. Signed-off-by: Li Zhong Signed-off-by: Michael Ellerman --- arch/powerpc/perf/hv-24x7.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'arch/powerpc/perf/hv-24x7.c') diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index ead8878ca62b..ff413615a87c 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -647,7 +647,7 @@ static ssize_t catalog_event_len_validate(struct hv_24x7_event_data *event, #define MAX_4K (SIZE_MAX / 4096) -static void create_events_from_catalog(struct attribute ***events_, +static int create_events_from_catalog(struct attribute ***events_, struct attribute ***event_descs_, struct attribute ***event_long_descs_) { @@ -665,19 +665,25 @@ static void create_events_from_catalog(struct attribute ***events_, void *event_data, *end; struct hv_24x7_event_data *event; struct rb_root ev_uniq = RB_ROOT; + int ret = 0; - if (!page) + if (!page) { + ret = -ENOMEM; goto e_out; + } hret = h_get_24x7_catalog_page(page, 0, 0); - if (hret) + if (hret) { + ret = -EIO; goto e_free; + } catalog_version_num = be64_to_cpu(page_0->version); catalog_page_len = be32_to_cpu(page_0->length); if (MAX_4K < catalog_page_len) { pr_err("invalid page count: %zu\n", catalog_page_len); + ret = -EIO; goto e_free; } @@ -696,6 +702,7 @@ static void create_events_from_catalog(struct attribute ***events_, || (MAX_4K - event_data_offs < event_data_len)) { pr_err("invalid event data offs %zu and/or len %zu\n", event_data_offs, event_data_len); + ret = -EIO; goto e_free; } @@ -704,12 +711,14 @@ static void create_events_from_catalog(struct attribute ***events_, event_data_offs, event_data_offs + event_data_len, catalog_page_len); + ret = -EIO; goto e_free; } if (SIZE_MAX / MAX_EVENTS_PER_EVENT_DATA - 1 < event_entry_count) { pr_err("event_entry_count %zu is invalid\n", event_entry_count); + ret = -EIO; goto e_free; } @@ -722,6 +731,7 @@ static void create_events_from_catalog(struct attribute ***events_, event_data = vmalloc(event_data_bytes); if (!event_data) { pr_err("could not allocate event data\n"); + ret = -ENOMEM; goto e_free; } @@ -741,6 +751,7 @@ static void create_events_from_catalog(struct attribute ***events_, if (hret) { pr_err("failed to get event data in page %zu\n", i + event_data_offs); + ret = -EIO; goto e_event_data; } } @@ -788,18 +799,24 @@ static void create_events_from_catalog(struct attribute ***events_, event_idx_last, event_entry_count, junk_events); events = kmalloc_array(attr_max + 1, sizeof(*events), GFP_KERNEL); - if (!events) + if (!events) { + ret = -ENOMEM; goto e_event_data; + } event_descs = kmalloc_array(event_idx + 1, sizeof(*event_descs), GFP_KERNEL); - if (!event_descs) + if (!event_descs) { + ret = -ENOMEM; goto e_event_attrs; + } event_long_descs = kmalloc_array(event_idx + 1, sizeof(*event_long_descs), GFP_KERNEL); - if (!event_long_descs) + if (!event_long_descs) { + ret = -ENOMEM; goto e_event_descs; + } /* Iterate over the catalog filling in the attribute vector */ for (junk_events = 0, event_attr_ct = 0, desc_ct = 0, long_desc_ct = 0, @@ -853,7 +870,7 @@ static void create_events_from_catalog(struct attribute ***events_, *events_ = events; *event_descs_ = event_descs; *event_long_descs_ = event_long_descs; - return; + return 0; e_event_descs: kfree(event_descs); @@ -867,6 +884,7 @@ e_out: *events_ = NULL; *event_descs_ = NULL; *event_long_descs_ = NULL; + return ret; } static ssize_t catalog_read(struct file *filp, struct kobject *kobj, @@ -1275,10 +1293,13 @@ static int hv_24x7_init(void) /* sampling not supported */ h_24x7_pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT; - create_events_from_catalog(&event_group.attrs, + r = create_events_from_catalog(&event_group.attrs, &event_desc_group.attrs, &event_long_desc_group.attrs); + if (r) + return r; + r = perf_pmu_register(&h_24x7_pmu, h_24x7_pmu.name, -1); if (r) return r; -- cgit v1.2.3