summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Jung Bauermann <bauerman@linux.vnet.ibm.com>2017-06-29 18:55:33 -0300
committerMichael Ellerman <mpe@ellerman.id.au>2017-07-02 20:40:30 +1000
commit41f577eb012f33d9af9c9fcee6a68a499562f875 (patch)
tree7e32d574bd59169a6daabd5d3e5a93ebad36f9b7
parent36c8fb2c616d9373758b155d9723774353067a87 (diff)
downloadlinux-41f577eb012f33d9af9c9fcee6a68a499562f875.tar.bz2
powerpc/perf/hv-24x7: Properly iterate through results
hv-24x7.h has a comment mentioning that result_buffer->results can't be indexed as a normal array because it may contain results of variable sizes, so fix the loop in h_24x7_event_commit_txn to take the variation into account when iterating through results. Another problem in that loop is that it sets h24x7hw->events[i] to NULL. This assumes that only the i'th result maps to the i'th request, but that is not guaranteed to be true. We need to leave the event in the array so that we don't dereference a NULL pointer in case more than one result maps to one request. We still assume that each result has only one result element, so warn if that assumption is violated. Reviewed-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/perf/hv-24x7.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c
index 141de0f708f7..8b1a3e849d23 100644
--- a/arch/powerpc/perf/hv-24x7.c
+++ b/arch/powerpc/perf/hv-24x7.c
@@ -1144,7 +1144,9 @@ static int add_event_to_24x7_request(struct perf_event *event,
static unsigned long single_24x7_request(struct perf_event *event, u64 *count)
{
+ u16 num_elements;
unsigned long ret;
+ struct hv_24x7_result *result;
struct hv_24x7_request_buffer *request_buffer;
struct hv_24x7_data_result_buffer *result_buffer;
@@ -1166,8 +1168,14 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count)
goto out;
}
+ result = result_buffer->results;
+
+ /* This code assumes that a result has only one element. */
+ num_elements = be16_to_cpu(result->num_elements_returned);
+ WARN_ON_ONCE(num_elements != 1);
+
/* process result from hcall */
- *count = be64_to_cpu(result_buffer->results[0].elements[0].element_data[0]);
+ *count = be64_to_cpu(result->elements[0].element_data[0]);
out:
put_cpu_var(hv_24x7_reqb);
@@ -1400,8 +1408,7 @@ static int h_24x7_event_commit_txn(struct pmu *pmu)
{
struct hv_24x7_request_buffer *request_buffer;
struct hv_24x7_data_result_buffer *result_buffer;
- struct hv_24x7_result *resb;
- struct perf_event *event;
+ struct hv_24x7_result *res, *next_res;
u64 count;
int i, ret, txn_flags;
struct hv_24x7_hw *h24x7hw;
@@ -1428,13 +1435,20 @@ static int h_24x7_event_commit_txn(struct pmu *pmu)
h24x7hw = &get_cpu_var(hv_24x7_hw);
- /* Update event counts from hcall */
- for (i = 0; i < request_buffer->num_requests; i++) {
- resb = &result_buffer->results[i];
- count = be64_to_cpu(resb->elements[0].element_data[0]);
- event = h24x7hw->events[i];
- h24x7hw->events[i] = NULL;
+ /* Go through results in the result buffer to update event counts. */
+ for (i = 0, res = result_buffer->results;
+ i < result_buffer->num_results; i++, res = next_res) {
+ struct perf_event *event = h24x7hw->events[res->result_ix];
+ u16 num_elements = be16_to_cpu(res->num_elements_returned);
+ u16 data_size = be16_to_cpu(res->result_element_data_size);
+
+ /* This code assumes that a result has only one element. */
+ WARN_ON_ONCE(num_elements != 1);
+
+ count = be64_to_cpu(res->elements[0].element_data[0]);
update_event_count(event, count);
+
+ next_res = (void *) res->elements[0].element_data + data_size;
}
put_cpu_var(hv_24x7_hw);