]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/perf/hv-24x7.c
Merge branch 'ras-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / arch / powerpc / perf / hv-24x7.c
index df956295c2a7ab3ede23e2a06e650d39943a1b17..9f9dfda9ed2c13a927f6c863759a2e780a5e8941 100644 (file)
@@ -142,6 +142,15 @@ static struct attribute_group event_long_desc_group = {
 
 static struct kmem_cache *hv_page_cache;
 
+DEFINE_PER_CPU(int, hv_24x7_txn_flags);
+DEFINE_PER_CPU(int, hv_24x7_txn_err);
+
+struct hv_24x7_hw {
+       struct perf_event *events[255];
+};
+
+DEFINE_PER_CPU(struct hv_24x7_hw, hv_24x7_hw);
+
 /*
  * 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
@@ -416,7 +425,7 @@ out_val:
 }
 
 static struct attribute *event_to_desc_attr(struct hv_24x7_event_data *event,
-                               int nonce)
+                                           int nonce)
 {
        int nl, dl;
        char *name = event_name(event, &nl);
@@ -444,7 +453,7 @@ event_to_long_desc_attr(struct hv_24x7_event_data *event, int nonce)
 }
 
 static ssize_t event_data_to_attrs(unsigned ix, struct attribute **attrs,
-               struct hv_24x7_event_data *event, int nonce)
+                                  struct hv_24x7_event_data *event, int nonce)
 {
        unsigned i;
 
@@ -512,7 +521,7 @@ static int memord(const void *d1, size_t s1, const void *d2, size_t s2)
 }
 
 static int ev_uniq_ord(const void *v1, size_t s1, unsigned d1, const void *v2,
-                                       size_t s2, unsigned d2)
+                      size_t s2, unsigned d2)
 {
        int r = memord(v1, s1, v2, s2);
 
@@ -526,7 +535,7 @@ static int ev_uniq_ord(const void *v1, size_t s1, unsigned d1, const void *v2,
 }
 
 static int event_uniq_add(struct rb_root *root, const char *name, int nl,
-                               unsigned domain)
+                         unsigned domain)
 {
        struct rb_node **new = &(root->rb_node), *parent = NULL;
        struct event_uniq *data;
@@ -650,8 +659,8 @@ static ssize_t catalog_event_len_validate(struct hv_24x7_event_data *event,
 #define MAX_4K (SIZE_MAX / 4096)
 
 static int create_events_from_catalog(struct attribute ***events_,
-               struct attribute ***event_descs_,
-               struct attribute ***event_long_descs_)
+                                     struct attribute ***event_descs_,
+                                     struct attribute ***event_long_descs_)
 {
        unsigned long hret;
        size_t catalog_len, catalog_page_len, event_entry_count,
@@ -1008,8 +1017,8 @@ static const struct attribute_group *attr_groups[] = {
 };
 
 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_data_result_buffer *result_buffer,
+                          unsigned long ret)
 {
        struct hv_24x7_request *req;
 
@@ -1026,7 +1035,7 @@ static void log_24x7_hcall(struct hv_24x7_request_buffer *request_buffer,
  * 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)
+                             struct hv_24x7_data_result_buffer *result_buffer)
 {
 
        memset(request_buffer, 0, 4096);
@@ -1041,7 +1050,7 @@ static void init_24x7_request(struct hv_24x7_request_buffer *request_buffer,
  * 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)
+                            struct hv_24x7_data_result_buffer *result_buffer)
 {
        unsigned long ret;
 
@@ -1104,7 +1113,6 @@ 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;
 
        BUILD_BUG_ON(sizeof(*request_buffer) > 4096);
        BUILD_BUG_ON(sizeof(*result_buffer) > 4096);
@@ -1125,8 +1133,7 @@ static unsigned long single_24x7_request(struct perf_event *event, u64 *count)
        }
 
        /* process result from hcall */
-       resb = &result_buffer->results[0];
-       *count = be64_to_cpu(resb->elements[0].element_data[0]);
+       *count = be64_to_cpu(result_buffer->results[0].elements[0].element_data[0]);
 
 out:
        put_cpu_var(hv_24x7_reqb);
@@ -1233,9 +1240,48 @@ static void update_event_count(struct perf_event *event, u64 now)
 static void h_24x7_event_read(struct perf_event *event)
 {
        u64 now;
+       struct hv_24x7_request_buffer *request_buffer;
+       struct hv_24x7_hw *h24x7hw;
+       int txn_flags;
+
+       txn_flags = __this_cpu_read(hv_24x7_txn_flags);
+
+       /*
+        * If in a READ transaction, add this counter to the list of
+        * counters to read during the next HCALL (i.e commit_txn()).
+        * If not in a READ transaction, go ahead and make the HCALL
+        * to read this counter by itself.
+        */
 
-       now = h_24x7_get_value(event);
-       update_event_count(event, now);
+       if (txn_flags & PERF_PMU_TXN_READ) {
+               int i;
+               int ret;
+
+               if (__this_cpu_read(hv_24x7_txn_err))
+                       return;
+
+               request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
+
+               ret = add_event_to_24x7_request(event, request_buffer);
+               if (ret) {
+                       __this_cpu_write(hv_24x7_txn_err, ret);
+               } else {
+                       /*
+                        * Assoicate the event with the HCALL request index,
+                        * so ->commit_txn() can quickly find/update count.
+                        */
+                       i = request_buffer->num_requests - 1;
+
+                       h24x7hw = &get_cpu_var(hv_24x7_hw);
+                       h24x7hw->events[i] = event;
+                       put_cpu_var(h24x7hw);
+               }
+
+               put_cpu_var(hv_24x7_reqb);
+       } else {
+               now = h_24x7_get_value(event);
+               update_event_count(event, now);
+       }
 }
 
 static void h_24x7_event_start(struct perf_event *event, int flags)
@@ -1257,6 +1303,117 @@ static int h_24x7_event_add(struct perf_event *event, int flags)
        return 0;
 }
 
+/*
+ * 24x7 counters only support READ transactions. They are
+ * always counting and dont need/support ADD transactions.
+ * Cache the flags, but otherwise ignore transactions that
+ * are not PERF_PMU_TXN_READ.
+ */
+static void h_24x7_event_start_txn(struct pmu *pmu, unsigned int flags)
+{
+       struct hv_24x7_request_buffer *request_buffer;
+       struct hv_24x7_data_result_buffer *result_buffer;
+
+       /* We should not be called if we are already in a txn */
+       WARN_ON_ONCE(__this_cpu_read(hv_24x7_txn_flags));
+
+       __this_cpu_write(hv_24x7_txn_flags, flags);
+       if (flags & ~PERF_PMU_TXN_READ)
+               return;
+
+       request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
+       result_buffer = (void *)get_cpu_var(hv_24x7_resb);
+
+       init_24x7_request(request_buffer, result_buffer);
+
+       put_cpu_var(hv_24x7_resb);
+       put_cpu_var(hv_24x7_reqb);
+}
+
+/*
+ * Clean up transaction state.
+ *
+ * NOTE: Ignore state of request and result buffers for now.
+ *      We will initialize them during the next read/txn.
+ */
+static void reset_txn(void)
+{
+       __this_cpu_write(hv_24x7_txn_flags, 0);
+       __this_cpu_write(hv_24x7_txn_err, 0);
+}
+
+/*
+ * 24x7 counters only support READ transactions. They are always counting
+ * and dont need/support ADD transactions. Clear ->txn_flags but otherwise
+ * ignore transactions that are not of type PERF_PMU_TXN_READ.
+ *
+ * For READ transactions, submit all pending 24x7 requests (i.e requests
+ * that were queued by h_24x7_event_read()), to the hypervisor and update
+ * the event counts.
+ */
+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;
+       u64 count;
+       int i, ret, txn_flags;
+       struct hv_24x7_hw *h24x7hw;
+
+       txn_flags = __this_cpu_read(hv_24x7_txn_flags);
+       WARN_ON_ONCE(!txn_flags);
+
+       ret = 0;
+       if (txn_flags & ~PERF_PMU_TXN_READ)
+               goto out;
+
+       ret = __this_cpu_read(hv_24x7_txn_err);
+       if (ret)
+               goto out;
+
+       request_buffer = (void *)get_cpu_var(hv_24x7_reqb);
+       result_buffer = (void *)get_cpu_var(hv_24x7_resb);
+
+       ret = make_24x7_request(request_buffer, result_buffer);
+       if (ret) {
+               log_24x7_hcall(request_buffer, result_buffer, ret);
+               goto put_reqb;
+       }
+
+       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;
+               update_event_count(event, count);
+       }
+
+       put_cpu_var(hv_24x7_hw);
+
+put_reqb:
+       put_cpu_var(hv_24x7_resb);
+       put_cpu_var(hv_24x7_reqb);
+out:
+       reset_txn();
+       return ret;
+}
+
+/*
+ * 24x7 counters only support READ transactions. They are always counting
+ * and dont need/support ADD transactions. However, regardless of type
+ * of transaction, all we need to do is cleanup, so we don't have to check
+ * the type of transaction.
+ */
+static void h_24x7_event_cancel_txn(struct pmu *pmu)
+{
+       WARN_ON_ONCE(!__this_cpu_read(hv_24x7_txn_flags));
+       reset_txn();
+}
+
 static struct pmu h_24x7_pmu = {
        .task_ctx_nr = perf_invalid_context,
 
@@ -1268,6 +1425,9 @@ static struct pmu h_24x7_pmu = {
        .start       = h_24x7_event_start,
        .stop        = h_24x7_event_stop,
        .read        = h_24x7_event_read,
+       .start_txn   = h_24x7_event_start_txn,
+       .commit_txn  = h_24x7_event_commit_txn,
+       .cancel_txn  = h_24x7_event_cancel_txn,
 };
 
 static int hv_24x7_init(void)