OSDN Git Service

perf kwork: Add IRQ trace BPF support
authorYang Jihong <yangjihong1@huawei.com>
Sat, 9 Jul 2022 01:50:31 +0000 (09:50 +0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 26 Jul 2022 19:31:54 +0000 (16:31 -0300)
Implements irq trace bpf function.

Test cases:
Trace irq without filter:

  # perf kwork -k irq rep -b
  Starting trace, Hit <Ctrl+C> to stop and report
  ^C
    Kwork Name                     | Cpu  | Total Runtime | Count     | Max runtime   | Max runtime start   | Max runtime end     |
   --------------------------------------------------------------------------------------------------------------------------------
    virtio0-requests:25            | 0000 |     31.026 ms |       285 |      1.493 ms |     110326.049963 s |     110326.051456 s |
    eth0:10                        | 0002 |      7.875 ms |        96 |      1.429 ms |     110313.916835 s |     110313.918264 s |
    ata_piix:14                    | 0002 |      2.510 ms |        28 |      0.396 ms |     110331.367987 s |     110331.368383 s |
   --------------------------------------------------------------------------------------------------------------------------------

Trace irq with cpu filter:

  # perf kwork -k irq rep -b -C 0
  Starting trace, Hit <Ctrl+C> to stop and report
  ^C
    Kwork Name                     | Cpu  | Total Runtime | Count     | Max runtime   | Max runtime start   | Max runtime end     |
   --------------------------------------------------------------------------------------------------------------------------------
    virtio0-requests:25            | 0000 |     34.288 ms |       282 |      2.061 ms |     110358.078968 s |     110358.081029 s |
   --------------------------------------------------------------------------------------------------------------------------------

Trace irq with name filter:

  # perf kwork -k irq rep -b -n eth0
  Starting trace, Hit <Ctrl+C> to stop and report
  ^C
    Kwork Name                     | Cpu  | Total Runtime | Count     | Max runtime   | Max runtime start   | Max runtime end     |
   --------------------------------------------------------------------------------------------------------------------------------
    eth0:10                        | 0002 |      2.184 ms |        21 |      0.572 ms |     110386.541699 s |     110386.542271 s |
   --------------------------------------------------------------------------------------------------------------------------------

Trace irq with summary:

  # perf kwork -k irq rep -b -S
  Starting trace, Hit <Ctrl+C> to stop and report
  ^C
    Kwork Name                     | Cpu  | Total Runtime | Count     | Max runtime   | Max runtime start   | Max runtime end     |
   --------------------------------------------------------------------------------------------------------------------------------
    virtio0-requests:25            | 0000 |     42.923 ms |       285 |      1.181 ms |     110418.128867 s |     110418.130049 s |
    eth0:10                        | 0002 |      2.085 ms |        20 |      0.668 ms |     110416.002935 s |     110416.003603 s |
    ata_piix:14                    | 0002 |      0.970 ms |         4 |      0.656 ms |     110424.034482 s |     110424.035138 s |
   --------------------------------------------------------------------------------------------------------------------------------
    Total count            :       309
    Total runtime   (msec) :    45.977 (0.003% load average)
    Total time span (msec) : 17017.655
   --------------------------------------------------------------------------------------------------------------------------------

Committer testing:

  # perf kwork -k irq rep -b
  Starting trace, Hit <Ctrl+C> to stop and report
  ^C
    Kwork Name                     | Cpu  | Total Runtime | Count     | Max runtime   | Max runtime start   | Max runtime end     |
   --------------------------------------------------------------------------------------------------------------------------------
    nvme0q20:145                   | 0019 |      0.570 ms |        28 |      0.064 ms |      26966.635102 s |      26966.635167 s |
    amdgpu:162                     | 0002 |      0.568 ms |        29 |      0.068 ms |      26966.644346 s |      26966.644414 s |
    nvme0q4:129                    | 0003 |      0.565 ms |        31 |      0.037 ms |      26966.614830 s |      26966.614866 s |
    nvme0q16:141                   | 0015 |      0.205 ms |        66 |      0.012 ms |      26967.145161 s |      26967.145174 s |
    nvme0q29:154                   | 0028 |      0.154 ms |        44 |      0.014 ms |      26967.078970 s |      26967.078984 s |
    nvme0q10:135                   | 0009 |      0.134 ms |        43 |      0.011 ms |      26967.132093 s |      26967.132104 s |
    nvme0q2:127                    | 0001 |      0.132 ms |        26 |      0.011 ms |      26966.883584 s |      26966.883595 s |
    nvme0q25:150                   | 0024 |      0.127 ms |        32 |      0.014 ms |      26966.631419 s |      26966.631433 s |
    nvme0q14:139                   | 0013 |      0.110 ms |        21 |      0.017 ms |      26966.760843 s |      26966.760861 s |
    nvme0q30:155                   | 0029 |      0.102 ms |        30 |      0.022 ms |      26966.677171 s |      26966.677193 s |
    nvme0q13:138                   | 0012 |      0.088 ms |        20 |      0.015 ms |      26966.738733 s |      26966.738748 s |
    nvme0q6:131                    | 0005 |      0.087 ms |        13 |      0.020 ms |      26966.648445 s |      26966.648465 s |
    nvme0q28:153                   | 0027 |      0.066 ms |        12 |      0.015 ms |      26966.771431 s |      26966.771447 s |
    nvme0q26:151                   | 0025 |      0.060 ms |        13 |      0.012 ms |      26966.704266 s |      26966.704278 s |
    nvme0q21:146                   | 0020 |      0.054 ms |        20 |      0.011 ms |      26967.322082 s |      26967.322094 s |
    nvme0q1:126                    | 0000 |      0.046 ms |        11 |      0.013 ms |      26966.859754 s |      26966.859767 s |
    nvme0q17:142                   | 0016 |      0.046 ms |        10 |      0.011 ms |      26967.114513 s |      26967.114524 s |
    xhci_hcd:74                    | 0015 |      0.041 ms |         3 |      0.016 ms |      26967.086004 s |      26967.086020 s |
    nvme0q8:133                    | 0007 |      0.039 ms |        12 |      0.008 ms |      26966.712056 s |      26966.712063 s |
    nvme0q32:157                   | 0031 |      0.036 ms |        10 |      0.014 ms |      26966.627054 s |      26966.627068 s |
    nvme0q9:134                    | 0008 |      0.036 ms |        11 |      0.011 ms |      26967.258452 s |      26967.258462 s |
    nvme0q7:132                    | 0006 |      0.024 ms |         3 |      0.014 ms |      26966.767404 s |      26966.767418 s |
    nvme0q11:136                   | 0010 |      0.023 ms |         5 |      0.006 ms |      26966.935455 s |      26966.935461 s |
    nvme0q31:156                   | 0030 |      0.018 ms |         5 |      0.006 ms |      26966.627517 s |      26966.627524 s |
    nvme0q12:137                   | 0011 |      0.015 ms |         2 |      0.014 ms |      26966.799588 s |      26966.799602 s |
    enp5s0-rx-0:164                | 0006 |      0.009 ms |         2 |      0.005 ms |      26966.742024 s |      26966.742028 s |
    enp5s0-rx-1:165                | 0007 |      0.006 ms |         2 |      0.004 ms |      26966.939486 s |      26966.939490 s |
    enp5s0-tx-0:166                | 0008 |      0.005 ms |         1 |      0.005 ms |      26966.939484 s |      26966.939489 s |
    enp5s0-tx-1:167                | 0009 |      0.005 ms |         1 |      0.005 ms |      26966.939484 s |      26966.939489 s |
   --------------------------------------------------------------------------------------------------------------------------------

  #t

Signed-off-by: Yang Jihong <yangjihong1@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Clarke <pc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220709015033.38326-16-yangjihong1@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/bpf_kwork.c
tools/perf/util/bpf_skel/kwork_trace.bpf.c

index 433bfad..a7af3ca 100644 (file)
@@ -62,9 +62,45 @@ void perf_kwork__trace_finish(void)
        skel->bss->enabled = 0;
 }
 
+static int get_work_name_from_map(struct work_key *key, char **ret_name)
+{
+       char name[MAX_KWORKNAME] = { 0 };
+       int fd = bpf_map__fd(skel->maps.perf_kwork_names);
+
+       *ret_name = NULL;
+
+       if (fd < 0) {
+               pr_debug("Invalid names map fd\n");
+               return 0;
+       }
+
+       if ((bpf_map_lookup_elem(fd, key, name) == 0) && (strlen(name) != 0)) {
+               *ret_name = strdup(name);
+               if (*ret_name == NULL) {
+                       pr_err("Failed to copy work name\n");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static void irq_load_prepare(struct perf_kwork *kwork)
+{
+       if (kwork->report == KWORK_REPORT_RUNTIME) {
+               bpf_program__set_autoload(skel->progs.report_irq_handler_entry, true);
+               bpf_program__set_autoload(skel->progs.report_irq_handler_exit, true);
+       }
+}
+
+static struct kwork_class_bpf kwork_irq_bpf = {
+       .load_prepare  = irq_load_prepare,
+       .get_work_name = get_work_name_from_map,
+};
+
 static struct kwork_class_bpf *
 kwork_class_bpf_supported_list[KWORK_CLASS_MAX] = {
-       [KWORK_CLASS_IRQ]       = NULL,
+       [KWORK_CLASS_IRQ]       = &kwork_irq_bpf,
        [KWORK_CLASS_SOFTIRQ]   = NULL,
        [KWORK_CLASS_WORKQUEUE] = NULL,
 };
index 36112be..1925407 100644 (file)
@@ -71,4 +71,154 @@ int enabled = 0;
 int has_cpu_filter = 0;
 int has_name_filter = 0;
 
+static __always_inline int local_strncmp(const char *s1,
+                                        unsigned int sz, const char *s2)
+{
+       int ret = 0;
+       unsigned int i;
+
+       for (i = 0; i < sz; i++) {
+               ret = (unsigned char)s1[i] - (unsigned char)s2[i];
+               if (ret || !s1[i] || !s2[i])
+                       break;
+       }
+
+       return ret;
+}
+
+static __always_inline int trace_event_match(struct work_key *key, char *name)
+{
+       __u8 *cpu_val;
+       char *name_val;
+       __u32 zero = 0;
+       __u32 cpu = bpf_get_smp_processor_id();
+
+       if (!enabled)
+               return 0;
+
+       if (has_cpu_filter) {
+               cpu_val = bpf_map_lookup_elem(&perf_kwork_cpu_filter, &cpu);
+               if (!cpu_val)
+                       return 0;
+       }
+
+       if (has_name_filter && (name != NULL)) {
+               name_val = bpf_map_lookup_elem(&perf_kwork_name_filter, &zero);
+               if (name_val &&
+                   (local_strncmp(name_val, MAX_KWORKNAME, name) != 0)) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static __always_inline void do_update_time(void *map, struct work_key *key,
+                                          __u64 time_start, __u64 time_end)
+{
+       struct report_data zero, *data;
+       __s64 delta = time_end - time_start;
+
+       if (delta < 0)
+               return;
+
+       data = bpf_map_lookup_elem(map, key);
+       if (!data) {
+               __builtin_memset(&zero, 0, sizeof(zero));
+               bpf_map_update_elem(map, key, &zero, BPF_NOEXIST);
+               data = bpf_map_lookup_elem(map, key);
+               if (!data)
+                       return;
+       }
+
+       if ((delta > data->max_time) ||
+           (data->max_time == 0)) {
+               data->max_time       = delta;
+               data->max_time_start = time_start;
+               data->max_time_end   = time_end;
+       }
+
+       data->total_time += delta;
+       data->nr++;
+}
+
+static __always_inline void do_update_timestart(void *map, struct work_key *key)
+{
+       __u64 ts = bpf_ktime_get_ns();
+
+       bpf_map_update_elem(map, key, &ts, BPF_ANY);
+}
+
+static __always_inline void do_update_timeend(void *report_map, void *time_map,
+                                             struct work_key *key)
+{
+       __u64 *time = bpf_map_lookup_elem(time_map, key);
+
+       if (time) {
+               bpf_map_delete_elem(time_map, key);
+               do_update_time(report_map, key, *time, bpf_ktime_get_ns());
+       }
+}
+
+static __always_inline void do_update_name(void *map,
+                                          struct work_key *key, char *name)
+{
+       if (!bpf_map_lookup_elem(map, key))
+               bpf_map_update_elem(map, key, name, BPF_ANY);
+}
+
+static __always_inline int update_timestart_and_name(void *time_map,
+                                                    void *names_map,
+                                                    struct work_key *key,
+                                                    char *name)
+{
+       if (!trace_event_match(key, name))
+               return 0;
+
+       do_update_timestart(time_map, key);
+       do_update_name(names_map, key, name);
+
+       return 0;
+}
+
+static __always_inline int update_timeend(void *report_map,
+                                         void *time_map, struct work_key *key)
+{
+       if (!trace_event_match(key, NULL))
+               return 0;
+
+       do_update_timeend(report_map, time_map, key);
+
+       return 0;
+}
+
+SEC("tracepoint/irq/irq_handler_entry")
+int report_irq_handler_entry(struct trace_event_raw_irq_handler_entry *ctx)
+{
+       char name[MAX_KWORKNAME];
+       struct work_key key = {
+               .type = KWORK_CLASS_IRQ,
+               .cpu  = bpf_get_smp_processor_id(),
+               .id   = (__u64)ctx->irq,
+       };
+       void *name_addr = (void *)ctx + (ctx->__data_loc_name & 0xffff);
+
+       bpf_probe_read_kernel_str(name, sizeof(name), name_addr);
+
+       return update_timestart_and_name(&perf_kwork_time,
+                                        &perf_kwork_names, &key, name);
+}
+
+SEC("tracepoint/irq/irq_handler_exit")
+int report_irq_handler_exit(struct trace_event_raw_irq_handler_exit *ctx)
+{
+       struct work_key key = {
+               .type = KWORK_CLASS_IRQ,
+               .cpu  = bpf_get_smp_processor_id(),
+               .id   = (__u64)ctx->irq,
+       };
+
+       return update_timeend(&perf_kwork_report, &perf_kwork_time, &key);
+}
+
 char LICENSE[] SEC("license") = "Dual BSD/GPL";