Skip to contentSkip to conversation

bpf: add two helpers to read perf event enabled/running time

PATCH Superseded Yonghong Song 2 participants 7 messages Sep 19, 2017

Subsystems (10)

v4 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v4 cover bpf: add two helpers to read perf event enabled/running time

Hardware pmu counters are limited resources. When there are more pmu based perf events opened than available counters, kernel will multiplex these events so each event gets certain percentage (but not 100%) of the pmu time. In case that multiplexing happens, the number of samples or counter value will not reflect the case compared to no multiplexing. This makes comparison between different runs difficult.

Typically, the number of samples or counter value should be normalized before comparing to other experiments. The typical normalization is done like:
normalized_num_samples = num_samples * time_enabled / time_running normalized_counter_value = counter_value * time_enabled / time_running
where time_enabled is the time enabled for event and time_running is the time running for event since last normalization.

This patch set implements two helper functions.
The helper bpf_perf_event_read_value reads counter/time_enabled/time_running for perf event array map. The helper bpf_perf_prog_read_value read counter/time_enabled/time_running for bpf prog with type BPF_PROG_TYPE_PERF_EVENT.

Changelogs:
v3->v4:
. fix a build failure
v2->v3:
. counters should be read in order to read enabled/running time. This is to
prevent that counters and enabled/running time may be read separately.
v1->v2:
. reading enabled/running time should be together with reading counters
which contains the logic to ensure the result is valid.

Yonghong Song (4):
bpf: add helper bpf_perf_event_read_value for perf event array map
bpf: add a test case for helper bpf_perf_event_read_value
bpf: add helper bpf_perf_prog_read_value
bpf: add a test case for helper bpf_perf_prog_read_value

 include/linux/perf_event.h                |  7 +++-
 include/uapi/linux/bpf.h                  | 26 +++++++++++-
 kernel/bpf/arraymap.c                     |  2 +-
 kernel/bpf/verifier.c                     |  4 +-
 kernel/events/core.c                      | 16 ++++++--
 kernel/trace/bpf_trace.c                  | 67 +++++++++++++++++++++++++++++--
 samples/bpf/trace_event_kern.c            | 10 +++++
 samples/bpf/trace_event_user.c            | 13 +++---
 samples/bpf/tracex6_kern.c                | 26 ++++++++++++
 samples/bpf/tracex6_user.c                | 13 +++++-
 tools/include/uapi/linux/bpf.h            |  4 +-
 tools/testing/selftests/bpf/bpf_helpers.h |  6 +++
 12 files changed, 175 insertions(+), 19 deletions(-)
Signature · 1 line
2.9.5
#1 lore raw

PATCH v4 1/4 bpf: add helper bpf_perf_event_read_value for perf event array map

Hardware pmu counters are limited resources. When there are more pmu based perf events opened than available counters, kernel will multiplex these events so each event gets certain percentage (but not 100%) of the pmu time. In case that multiplexing happens, the number of samples or counter value will not reflect the case compared to no multiplexing. This makes comparison between different runs difficult.

Typically, the number of samples or counter value should be normalized before comparing to other experiments. The typical normalization is done like:
normalized_num_samples = num_samples * time_enabled / time_running normalized_counter_value = counter_value * time_enabled / time_running
where time_enabled is the time enabled for event and time_running is the time running for event since last normalization.

This patch adds helper bpf_perf_event_read_value for kprobed based perf event array map, to read perf counter and enabled/running time. The enabled/running time is accumulated since the perf event open. To achieve scaling factor between two bpf invocations, users can can use cpu_id as the key (which is typical for perf array usage model) to remember the previous value and do the calculation inside the bpf program.

---
 include/linux/perf_event.h |  6 ++++--
 include/uapi/linux/bpf.h   | 18 +++++++++++++++++-
 kernel/bpf/arraymap.c      |  2 +-
 kernel/bpf/verifier.c      |  4 +++-
 kernel/events/core.c       | 15 ++++++++++++---
 kernel/trace/bpf_trace.c   | 44 ++++++++++++++++++++++++++++++++++++++++----
 6 files changed, 77 insertions(+), 12 deletions(-)
include/linux/perf_event.h +4 -2
index 8e22f24..21d8c12 100644
@@ -884,7 +884,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr,
 				void *context);
 extern void perf_pmu_migrate_context(struct pmu *pmu,
 				int src_cpu, int dst_cpu);
-int perf_event_read_local(struct perf_event *event, u64 *value);
+int perf_event_read_local(struct perf_event *event, u64 *value,
+			  u64 *enabled, u64 *running);
 extern u64 perf_event_read_value(struct perf_event *event,
 				 u64 *enabled, u64 *running);
 
@@ -1286,7 +1287,8 @@ static inline const struct perf_event_attr *perf_event_attrs(struct perf_event *
 {
 	return ERR_PTR(-EINVAL);
 }
-static inline int perf_event_read_local(struct perf_event *event, u64 *value)
+static inline int perf_event_read_local(struct perf_event *event, u64 *value,
+					u64 *enabled, u64 *running)
 {
 	return -EINVAL;
 }
include/uapi/linux/bpf.h +17 -1
index 43ab5c4..2c68b9e 100644
@@ -582,6 +582,14 @@ union bpf_attr {
  *	@map: pointer to sockmap to update
  *	@key: key to insert/update sock in map
  *	@flags: same flags as map update elem
+ *
+ * int bpf_perf_event_read_value(map, flags, buf, buf_size)
+ *     read perf event counter value and perf event enabled/running time
+ *     @map: pointer to perf_event_array map
+ *     @flags: index of event in the map or bitmask flags
+ *     @buf: buf to fill
+ *     @buf_size: size of the buf
+ *     Return: 0 on success or negative error code
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -638,6 +646,7 @@ union bpf_attr {
 	FN(redirect_map),		\
 	FN(sk_redirect_map),		\
 	FN(sock_map_update),		\
+	FN(perf_event_read_value),		\
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -681,7 +690,8 @@ enum bpf_func_id {
 #define BPF_F_ZERO_CSUM_TX		(1ULL << 1)
 #define BPF_F_DONT_FRAGMENT		(1ULL << 2)
 
-/* BPF_FUNC_perf_event_output and BPF_FUNC_perf_event_read flags. */
+/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
+ * BPF_FUNC_perf_event_read_value flags. */
 #define BPF_F_INDEX_MASK		0xffffffffULL
 #define BPF_F_CURRENT_CPU		BPF_F_INDEX_MASK
 /* BPF_FUNC_perf_event_output for sk_buff input context. */
@@ -864,4 +874,10 @@ enum {
 #define TCP_BPF_IW		1001	/* Set TCP initial congestion window */
 #define TCP_BPF_SNDCWND_CLAMP	1002	/* Set sndcwnd_clamp */
 
+struct bpf_perf_event_value {
+	__u64 counter;
+	__u64 enabled;
+	__u64 running;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
kernel/bpf/arraymap.c +1 -1
index 98c0f00..68d8666 100644
@@ -492,7 +492,7 @@ static void *perf_event_fd_array_get_ptr(struct bpf_map *map,
 
 	ee = ERR_PTR(-EOPNOTSUPP);
 	event = perf_file->private_data;
-	if (perf_event_read_local(event, &value) == -EOPNOTSUPP)
+	if (perf_event_read_local(event, &value, NULL, NULL) == -EOPNOTSUPP)
 		goto err_out;
 
 	ee = bpf_event_entry_gen(perf_file, map_file);
kernel/bpf/verifier.c +3 -1
index 799b245..1bf9d7b 100644
@@ -1494,7 +1494,8 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id)
 		break;
 	case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
 		if (func_id != BPF_FUNC_perf_event_read &&
-		    func_id != BPF_FUNC_perf_event_output)
+		    func_id != BPF_FUNC_perf_event_output &&
+		    func_id != BPF_FUNC_perf_event_read_value)
 			goto error;
 		break;
 	case BPF_MAP_TYPE_STACK_TRACE:
@@ -1537,6 +1538,7 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id)
 		break;
 	case BPF_FUNC_perf_event_read:
 	case BPF_FUNC_perf_event_output:
+	case BPF_FUNC_perf_event_read_value:
 		if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
 			goto error;
 		break;
kernel/events/core.c +12 -3
index 3e691b7..2d5bbe5 100644
@@ -3684,10 +3684,12 @@ static inline u64 perf_event_count(struct perf_event *event)
  *     will not be local and we cannot read them atomically
  *   - must not have a pmu::count method
  */
-int perf_event_read_local(struct perf_event *event, u64 *value)
+int perf_event_read_local(struct perf_event *event, u64 *value,
+			  u64 *enabled, u64 *running)
 {
 	unsigned long flags;
 	int ret = 0;
+	u64 now;
 
 	/*
 	 * Disabling interrupts avoids all counter scheduling (context
@@ -3718,14 +3720,21 @@ int perf_event_read_local(struct perf_event *event, u64 *value)
 		goto out;
 	}
 
+	now = event->shadow_ctx_time + perf_clock();
+	if (enabled)
+		*enabled = now - event->tstamp_enabled;
 	/*
 	 * If the event is currently on this CPU, its either a per-task event,
 	 * or local to this CPU. Furthermore it means its ACTIVE (otherwise
 	 * oncpu == -1).
 	 */
-	if (event->oncpu == smp_processor_id())
+	if (event->oncpu == smp_processor_id()) {
 		event->pmu->read(event);
-
+		if (running)
+			*running = now - event->tstamp_running;
+	} else if (running) {
+		*running = event->total_time_running;
+	}
 	*value = local64_read(&event->count);
 out:
 	local_irq_restore(flags);
kernel/trace/bpf_trace.c +40 -4

78 lines not on this page · raw message

Signature · 1 line
2.9.5
maintainer
#2 lore raw

Re: [PATCH net-next v4 1/4] bpf: add helper bpf_perf_event_read_value for perf event array map

On 09/19/2017 09:04 AM, Yonghong Song wrote:
[...]

Just some minor things, but a bit scattered.

Quoted 20 lines
include/uapi/linux/bpf.h +8 -0
index 43ab5c4..2c68b9e 100644
@@ -582,6 +582,14 @@ union bpf_attr {
   *	@map: pointer to sockmap to update
   *	@key: key to insert/update sock in map
   *	@flags: same flags as map update elem
+ *
+ * int bpf_perf_event_read_value(map, flags, buf, buf_size)
+ *     read perf event counter value and perf event enabled/running time
+ *     @map: pointer to perf_event_array map
+ *     @flags: index of event in the map or bitmask flags
+ *     @buf: buf to fill
+ *     @buf_size: size of the buf
+ *     Return: 0 on success or negative error code
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\

include/uapi/linux/bpf.h union bpf_attr { · line 649

Quoted 4 lines
include/uapi/linux/bpf.h +1 -0
@@ -638,6 +646,7 @@ union bpf_attr {
  	FN(redirect_map),		\
  	FN(sk_redirect_map),		\
  	FN(sock_map_update),		\
+	FN(perf_event_read_value),		\

Nit: can you indent the '\' so it aligns with the rest

include/uapi/linux/bpf.h enum bpf_func_id { · line 694

Quoted 9 lines
include/uapi/linux/bpf.h +2 -1
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
@@ -681,7 +690,8 @@ enum bpf_func_id {
  #define BPF_F_ZERO_CSUM_TX		(1ULL << 1)
  #define BPF_F_DONT_FRAGMENT		(1ULL << 2)
 
-/* BPF_FUNC_perf_event_output and BPF_FUNC_perf_event_read flags. */
+/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
+ * BPF_FUNC_perf_event_read_value flags. */

Nit: comment style

Quoted 13 lines
include/uapi/linux/bpf.h +6 -0
  #define BPF_F_INDEX_MASK		0xffffffffULL
  #define BPF_F_CURRENT_CPU		BPF_F_INDEX_MASK
  /* BPF_FUNC_perf_event_output for sk_buff input context. */
@@ -864,4 +874,10 @@ enum {
  #define TCP_BPF_IW		1001	/* Set TCP initial congestion window */
  #define TCP_BPF_SNDCWND_CLAMP	1002	/* Set sndcwnd_clamp */
 
+struct bpf_perf_event_value {
+	__u64 counter;
+	__u64 enabled;
+	__u64 running;
+};
+

[...]

Quoted 47 lines
kernel/events/core.c +12 -3
index 3e691b7..2d5bbe5 100644
@@ -3684,10 +3684,12 @@ static inline u64 perf_event_count(struct perf_event *event)
   *     will not be local and we cannot read them atomically
   *   - must not have a pmu::count method
   */
-int perf_event_read_local(struct perf_event *event, u64 *value)
+int perf_event_read_local(struct perf_event *event, u64 *value,
+			  u64 *enabled, u64 *running)
  {
  	unsigned long flags;
  	int ret = 0;
+	u64 now;
 
  	/*
  	 * Disabling interrupts avoids all counter scheduling (context
@@ -3718,14 +3720,21 @@ int perf_event_read_local(struct perf_event *event, u64 *value)
  		goto out;
  	}
 
+	now = event->shadow_ctx_time + perf_clock();
+	if (enabled)
+		*enabled = now - event->tstamp_enabled;
  	/*
  	 * If the event is currently on this CPU, its either a per-task event,
  	 * or local to this CPU. Furthermore it means its ACTIVE (otherwise
  	 * oncpu == -1).
  	 */
-	if (event->oncpu == smp_processor_id())
+	if (event->oncpu == smp_processor_id()) {
  		event->pmu->read(event);
-
+		if (running)
+			*running = now - event->tstamp_running;
+	} else if (running) {
+		*running = event->total_time_running;
+	}
  	*value = local64_read(&event->count);
  out:
  	local_irq_restore(flags);
kernel/trace/bpf_trace.c +0 -0
index dc498b6..39ce5d9 100644

kernel/trace/bpf_trace.c const struct bpf_func_proto *bpf_get_trace_printk_proto(void) · line 260

Quoted 8 lines
kernel/trace/bpf_trace.c +3 -2
@@ -255,13 +255,13 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void)
  	return &bpf_trace_printk_proto;
  }
 
-BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags)
-{
+static __always_inline int
+get_map_perf_counter(struct bpf_map *map, u64 flags,
+		u64 *value, u64 *enabled, u64 *running) {

Nit: coding style

Quoted 9 lines
kernel/trace/bpf_trace.c +0 -1
  	struct bpf_array *array = container_of(map, struct bpf_array, map);
  	unsigned int cpu = smp_processor_id();
  	u64 index = flags & BPF_F_INDEX_MASK;
  	struct bpf_event_entry *ee;
-	u64 value = 0;
  	int err;
 
  	if (unlikely(flags & ~(BPF_F_INDEX_MASK)))

kernel/trace/bpf_trace.c BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · line 279

Quoted 6 lines
kernel/trace/bpf_trace.c +2 -1
@@ -275,7 +275,17 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags)
  	if (!ee)
  		return -ENOENT;
 
-	err = perf_event_read_local(ee->event, &value);
+	err = perf_event_read_local(ee->event, value, enabled, running);
+	return err;

err can be removed entirely then.

  return perf_event_read_local(ee->event, value, enabled, running);

kernel/trace/bpf_trace.c BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · line 282

Quoted 3 lines
kernel/trace/bpf_trace.c +3 -0
+}
+
+

Nit: Two newlines slipped in.

kernel/trace/bpf_trace.c BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · line 296

Quoted 12 lines
kernel/trace/bpf_trace.c +6 -0
+BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags)
+{
+	u64 value = 0;
+	int err;
+
+	err = get_map_perf_counter(map, flags, &value, NULL, NULL);
  	/*
  	 * this api is ugly since we miss [-22..-2] range of valid
  	 * counter values, but that's uapi
@@ -285,6 +295,20 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags)
  	return value;
  }

Can you also move the bpf_perf_event_read_proto definition right underneath here as we have with all other helpers?

kernel/trace/bpf_trace.c BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · line 299

Quoted 2 lines
kernel/trace/bpf_trace.c +2 -0
+BPF_CALL_4(bpf_perf_event_read_value, struct bpf_map *, map, u64, flags,
+	struct bpf_perf_event_value *, buf, u32, size)

Nit: indent

kernel/trace/bpf_trace.c BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · line 306

Quoted 7 lines
kernel/trace/bpf_trace.c +7 -0
+{
+	int err;
+
+	if (unlikely(size != sizeof(struct bpf_perf_event_value)))
+		return -EINVAL;
+	err = get_map_perf_counter(map, flags, &buf->counter, &buf->enabled,
+                            &buf->running);

^ This is only indented with spaces.

kernel/trace/bpf_trace.c BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · line 309

Quoted 3 lines
kernel/trace/bpf_trace.c +3 -0
+	if (err)
+		return err;
+	return 0;

Also here err can be removed entirely, make it
less convoluted:

BPF_CALL_4(bpf_perf_event_read_value, struct bpf_map *, map, u64, flags,
struct bpf_perf_event_value *, eval, u32, size)

{
       if (unlikely(size != sizeof(struct bpf_perf_event_value)))
               return -EINVAL;

       return get_map_perf_counter(map, flags, &eval->counter, &eval->enabled,
                                   &eval->running);
}
Quoted 6 lines
kernel/trace/bpf_trace.c +2 -0
+}
+
  static const struct bpf_func_proto bpf_perf_event_read_proto = {
  	.func		= bpf_perf_event_read,
  	.gpl_only	= true,

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_perf_event_read_proto = { · line 326

Quoted 10 lines
kernel/trace/bpf_trace.c +7 -0
@@ -293,6 +317,16 @@ static const struct bpf_func_proto bpf_perf_event_read_proto = {
  	.arg2_type	= ARG_ANYTHING,
  };
 
+static const struct bpf_func_proto bpf_perf_event_read_value_proto = {
+	.func		= bpf_perf_event_read_value,
+	.gpl_only	= true,
+	.ret_type	= RET_INTEGER,
+	.arg1_type	= ARG_CONST_MAP_PTR,
+	.arg2_type	= ARG_ANYTHING,
+	.arg3_type	= ARG_PTR_TO_UNINIT_MEM,

If you do that, then error paths need to memset the region, e.g. see bpf_skb_get_tunnel_opt() and bpf_skb_get_tunnel_key() helpers which operate similarly.

Quoted 16 lines
kernel/trace/bpf_trace.c +5 -0
+	.arg4_type	= ARG_CONST_SIZE,
+};
+
  static DEFINE_PER_CPU(struct perf_sample_data, bpf_sd);
 
  static __always_inline u64
@@ -499,6 +533,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
  		return &bpf_perf_event_output_proto;
  	case BPF_FUNC_get_stackid:
  		return &bpf_get_stackid_proto;
+	case BPF_FUNC_perf_event_read_value:
+		return &bpf_perf_event_read_value_proto;
  	default:
  		return tracing_func_proto(func_id);
  	}
#3 lore raw

PATCH v4 2/4 bpf: add a test case for helper bpf_perf_event_read_value

In reply to Yonghong Song

The bpf sample program tracex6 is enhanced to use the new helper to read enabled/running time as well.

---
 samples/bpf/tracex6_kern.c                | 26 ++++++++++++++++++++++++++
 samples/bpf/tracex6_user.c                | 13 ++++++++++++-
 tools/include/uapi/linux/bpf.h            |  3 ++-
 tools/testing/selftests/bpf/bpf_helpers.h |  3 +++
 4 files changed, 43 insertions(+), 2 deletions(-)
samples/bpf/tracex6_kern.c +26 -0
index e7d1803..46c557a 100644
@@ -15,6 +15,12 @@ struct bpf_map_def SEC("maps") values = {
 	.value_size = sizeof(u64),
 	.max_entries = 64,
 };
+struct bpf_map_def SEC("maps") values2 = {
+	.type = BPF_MAP_TYPE_HASH,
+	.key_size = sizeof(int),
+	.value_size = sizeof(struct bpf_perf_event_value),
+	.max_entries = 64,
+};
 
 SEC("kprobe/htab_map_get_next_key")
 int bpf_prog1(struct pt_regs *ctx)
@@ -37,5 +43,25 @@ int bpf_prog1(struct pt_regs *ctx)
 	return 0;
 }
 
+SEC("kprobe/htab_map_lookup_elem")
+int bpf_prog2(struct pt_regs *ctx)
+{
+	u32 key = bpf_get_smp_processor_id();
+	struct bpf_perf_event_value *val, buf;
+	int error;
+
+	error = bpf_perf_event_read_value(&counters, key, &buf, sizeof(buf));
+	if (error)
+		return 0;
+
+	val = bpf_map_lookup_elem(&values2, &key);
+	if (val)
+		*val = buf;
+	else
+		bpf_map_update_elem(&values2, &key, &buf, BPF_NOEXIST);
+
+	return 0;
+}
+
 char _license[] SEC("license") = "GPL";
 u32 _version SEC("version") = LINUX_VERSION_CODE;
samples/bpf/tracex6_user.c +12 -1
index a05a99a..3341a96 100644
@@ -22,6 +22,7 @@
 
 static void check_on_cpu(int cpu, struct perf_event_attr *attr)
 {
+	struct bpf_perf_event_value value2;
 	int pmu_fd, error = 0;
 	cpu_set_t set;
 	__u64 value;
@@ -46,8 +47,18 @@ static void check_on_cpu(int cpu, struct perf_event_attr *attr)
 		fprintf(stderr, "Value missing for CPU %d\n", cpu);
 		error = 1;
 		goto on_exit;
+	} else {
+		fprintf(stderr, "CPU %d: %llu\n", cpu, value);
+	}
+	/* The above bpf_map_lookup_elem should trigger the second kprobe */
+	if (bpf_map_lookup_elem(map_fd[2], &cpu, &value2)) {
+		fprintf(stderr, "Value2 missing for CPU %d\n", cpu);
+		error = 1;
+		goto on_exit;
+	} else {
+		fprintf(stderr, "CPU %d: counter: %llu, enabled: %llu, running: %llu\n", cpu,
+			value2.counter, value2.enabled, value2.running);
 	}
-	fprintf(stderr, "CPU %d: %llu\n", cpu, value);
 
 on_exit:
 	assert(bpf_map_delete_elem(map_fd[0], &cpu) == 0 || error);
tools/include/uapi/linux/bpf.h +2 -1
index 461811e..79eb529 100644
@@ -632,7 +632,8 @@ union bpf_attr {
 	FN(skb_adjust_room),		\
 	FN(redirect_map),		\
 	FN(sk_redirect_map),		\
-	FN(sock_map_update),
+	FN(sock_map_update),		\
+	FN(perf_event_read_value),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
tools/testing/selftests/bpf/bpf_helpers.h +3 -0
index 36fb916..c866682 100644
@@ -70,6 +70,9 @@ static int (*bpf_sk_redirect_map)(void *map, int key, int flags) =
 static int (*bpf_sock_map_update)(void *map, void *key, void *value,
 				  unsigned long long flags) =
 	(void *) BPF_FUNC_sock_map_update;
+static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,
+				       void *buf, unsigned int buf_size) =
+	(void *) BPF_FUNC_perf_event_read_value;
 
 
 /* llvm builtin functions that eBPF C program may use to
Signature · 1 line
2.9.5
#4 lore raw

PATCH v4 3/4 bpf: add helper bpf_perf_prog_read_value

In reply to Yonghong Song

This patch adds helper bpf_perf_prog_read_cvalue for perf event based bpf programs, to read event counter and enabled/running time.
The enabled/running time is accumulated since the perf event open.

The typical use case for perf event based bpf program is to attach itself to a single event. In such cases, if it is desirable to get scaling factor between two bpf invocations, users can can save the time values in a map, and use the value from the map and the current value to calculate the scaling factor.

---
 include/linux/perf_event.h |  1 +
 include/uapi/linux/bpf.h   |  8 ++++++++
 kernel/events/core.c       |  1 +
 kernel/trace/bpf_trace.c   | 23 +++++++++++++++++++++++
 4 files changed, 33 insertions(+)
include/linux/perf_event.h +1 -0
index 21d8c12..79b18a2 100644
@@ -806,6 +806,7 @@ struct perf_output_handle {
 struct bpf_perf_event_data_kern {
 	struct pt_regs *regs;
 	struct perf_sample_data *data;
+	struct perf_event *event;
 };
 
 #ifdef CONFIG_CGROUP_PERF
include/uapi/linux/bpf.h +8 -0
index 2c68b9e..ba77022 100644
@@ -590,6 +590,13 @@ union bpf_attr {
  *     @buf: buf to fill
  *     @buf_size: size of the buf
  *     Return: 0 on success or negative error code
+ *
+ * int bpf_perf_prog_read_value(ctx, buf, buf_size)
+ *     read perf prog attached perf event counter and enabled/running time
+ *     @ctx: pointer to ctx
+ *     @buf: buf to fill
+ *     @buf_size: size of the buf
+ *     Return : 0 on success or negative error code
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -647,6 +654,7 @@ union bpf_attr {
 	FN(sk_redirect_map),		\
 	FN(sock_map_update),		\
 	FN(perf_event_read_value),		\
+	FN(perf_prog_read_value),		\
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
kernel/events/core.c +1 -0
index 2d5bbe5..d039086 100644
@@ -8081,6 +8081,7 @@ static void bpf_overflow_handler(struct perf_event *event,
 	struct bpf_perf_event_data_kern ctx = {
 		.data = data,
 		.regs = regs,
+		.event = event,
 	};
 	int ret = 0;
 
kernel/trace/bpf_trace.c +23 -0
index 39ce5d9..596b5c9 100644
@@ -603,6 +603,18 @@ BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map,
 			       flags, 0, 0);
 }
 
+BPF_CALL_3(bpf_perf_prog_read_value_tp, void *, ctx, struct bpf_perf_event_value *,
+	buf, u32, size)
+{
+	struct bpf_perf_event_data_kern *kctx = (struct bpf_perf_event_data_kern *)ctx;
+
+	if (size != sizeof(struct bpf_perf_event_value))
+		return -EINVAL;
+
+	return perf_event_read_local(kctx->event, &buf->counter, &buf->enabled,
+				     &buf->running);
+}
+
 static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
 	.func		= bpf_get_stackid_tp,
 	.gpl_only	= true,
@@ -612,6 +624,15 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
 	.arg3_type	= ARG_ANYTHING,
 };
 
+static const struct bpf_func_proto bpf_perf_prog_read_value_proto_tp = {
+         .func           = bpf_perf_prog_read_value_tp,
+         .gpl_only       = true,
+         .ret_type       = RET_INTEGER,
+         .arg1_type      = ARG_PTR_TO_CTX,
+         .arg2_type      = ARG_PTR_TO_UNINIT_MEM,
+         .arg3_type      = ARG_CONST_SIZE,
+};
+
 static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
 {
 	switch (func_id) {
@@ -619,6 +640,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
 		return &bpf_perf_event_output_proto_tp;
 	case BPF_FUNC_get_stackid:
 		return &bpf_get_stackid_proto_tp;
+	case BPF_FUNC_perf_prog_read_value:
+		return &bpf_perf_prog_read_value_proto_tp;
 	default:
 		return tracing_func_proto(func_id);
 	}
Signature · 1 line
2.9.5
maintainer
#5 lore raw

Re: [PATCH net-next v4 3/4] bpf: add helper bpf_perf_prog_read_value

On 09/19/2017 09:04 AM, Yonghong Song wrote:
[...]

Quoted 20 lines
  #ifdef CONFIG_CGROUP_PERF
include/uapi/linux/bpf.h +7 -0
index 2c68b9e..ba77022 100644
@@ -590,6 +590,13 @@ union bpf_attr {
   *     @buf: buf to fill
   *     @buf_size: size of the buf
   *     Return: 0 on success or negative error code
+ *
+ * int bpf_perf_prog_read_value(ctx, buf, buf_size)
+ *     read perf prog attached perf event counter and enabled/running time
+ *     @ctx: pointer to ctx
+ *     @buf: buf to fill
+ *     @buf_size: size of the buf
+ *     Return : 0 on success or negative error code
   */
  #define __BPF_FUNC_MAPPER(FN)		\
  	FN(unspec),			\

include/uapi/linux/bpf.h union bpf_attr { · line 657

Quoted 4 lines
include/uapi/linux/bpf.h +1 -0
@@ -647,6 +654,7 @@ union bpf_attr {
  	FN(sk_redirect_map),		\
  	FN(sock_map_update),		\
  	FN(perf_event_read_value),		\
+	FN(perf_prog_read_value),		\

(Same here.)

Quoted 20 lines
include/uapi/linux/bpf.h +0 -0
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
kernel/events/core.c +1 -0
index 2d5bbe5..d039086 100644
@@ -8081,6 +8081,7 @@ static void bpf_overflow_handler(struct perf_event *event,
  	struct bpf_perf_event_data_kern ctx = {
  		.data = data,
  		.regs = regs,
+		.event = event,
  	};
  	int ret = 0;
 
kernel/trace/bpf_trace.c +0 -0
index 39ce5d9..596b5c9 100644

kernel/trace/bpf_trace.c BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map, · line 607

Quoted 5 lines
kernel/trace/bpf_trace.c +2 -0
@@ -603,6 +603,18 @@ BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map,
  			       flags, 0, 0);
  }
 
+BPF_CALL_3(bpf_perf_prog_read_value_tp, void *, ctx, struct bpf_perf_event_value *,
+	buf, u32, size)

Nit: indent

kernel/trace/bpf_trace.c BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map, · line 609

Quoted 2 lines
kernel/trace/bpf_trace.c +2 -0
+{
+	struct bpf_perf_event_data_kern *kctx = (struct bpf_perf_event_data_kern *)ctx;

Why having the arg as void * and have this detour instead of having struct bpf_perf_event_data_kern * right in the helper signature as argument?

kernel/trace/bpf_trace.c BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map, · line 611

Quoted 1 line
kernel/trace/bpf_trace.c +1 -0
+	if (size != sizeof(struct bpf_perf_event_value))

unlikely()

kernel/trace/bpf_trace.c BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map, · line 616

Quoted 5 lines
kernel/trace/bpf_trace.c +5 -0
+		return -EINVAL;
+
+	return perf_event_read_local(kctx->event, &buf->counter, &buf->enabled,
+				     &buf->running);
+}

bpf_perf_prog_read_value_proto_tp would go right underneath here, and bpf_get_stackid_proto_tp below the previous helper above.

kernel/trace/bpf_trace.c static const struct bpf_func_proto bpf_get_stackid_proto_tp = { · line 632

Quoted 13 lines
kernel/trace/bpf_trace.c +6 -0
  static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
  	.func		= bpf_get_stackid_tp,
  	.gpl_only	= true,
@@ -612,6 +624,15 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
  	.arg3_type	= ARG_ANYTHING,
  };
 
+static const struct bpf_func_proto bpf_perf_prog_read_value_proto_tp = {
+         .func           = bpf_perf_prog_read_value_tp,
+         .gpl_only       = true,
+         .ret_type       = RET_INTEGER,
+         .arg1_type      = ARG_PTR_TO_CTX,
+         .arg2_type      = ARG_PTR_TO_UNINIT_MEM,

Same on error path.

Quoted 16 lines
kernel/trace/bpf_trace.c +5 -0
+         .arg3_type      = ARG_CONST_SIZE,
+};
+
  static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
  {
  	switch (func_id) {
@@ -619,6 +640,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
  		return &bpf_perf_event_output_proto_tp;
  	case BPF_FUNC_get_stackid:
  		return &bpf_get_stackid_proto_tp;
+	case BPF_FUNC_perf_prog_read_value:
+		return &bpf_perf_prog_read_value_proto_tp;
  	default:
  		return tracing_func_proto(func_id);
  	}
#6 lore raw

PATCH v4 4/4 bpf: add a test case for helper bpf_perf_prog_read_value

In reply to Yonghong Song

The bpf sample program trace_event is enhanced to use the new helper to print out enabled/running time.

---
 samples/bpf/trace_event_kern.c            | 10 ++++++++++
 samples/bpf/trace_event_user.c            | 13 ++++++++-----
 tools/include/uapi/linux/bpf.h            |  3 ++-
 tools/testing/selftests/bpf/bpf_helpers.h |  3 +++
 4 files changed, 23 insertions(+), 6 deletions(-)
samples/bpf/trace_event_kern.c +10 -0
index 41b6115..a77a583d 100644
@@ -37,10 +37,14 @@ struct bpf_map_def SEC("maps") stackmap = {
 SEC("perf_event")
 int bpf_prog1(struct bpf_perf_event_data *ctx)
 {
+	char time_fmt1[] = "Time Enabled: %llu, Time Running: %llu";
+	char time_fmt2[] = "Get Time Failed, ErrCode: %d";
 	char fmt[] = "CPU-%d period %lld ip %llx";
 	u32 cpu = bpf_get_smp_processor_id();
+	struct bpf_perf_event_value value_buf;
 	struct key_t key;
 	u64 *val, one = 1;
+	int ret;
 
 	if (ctx->sample_period < 10000)
 		/* ignore warmup */
@@ -54,6 +58,12 @@ int bpf_prog1(struct bpf_perf_event_data *ctx)
 		return 0;
 	}
 
+	ret = bpf_perf_prog_read_value(ctx, (void *)&value_buf, sizeof(struct bpf_perf_event_value));
+	if (!ret)
+	  bpf_trace_printk(time_fmt1, sizeof(time_fmt1), value_buf.enabled, value_buf.running);
+	else
+	  bpf_trace_printk(time_fmt2, sizeof(time_fmt2), ret);
+
 	val = bpf_map_lookup_elem(&counts, &key);
 	if (val)
 		(*val)++;
samples/bpf/trace_event_user.c +8 -5
index 7bd827b..bf4f1b6 100644
@@ -127,6 +127,9 @@ static void test_perf_event_all_cpu(struct perf_event_attr *attr)
 	int *pmu_fd = malloc(nr_cpus * sizeof(int));
 	int i, error = 0;
 
+	/* system wide perf event, no need to inherit */
+	attr->inherit = 0;
+
 	/* open perf_event on all cpus */
 	for (i = 0; i < nr_cpus; i++) {
 		pmu_fd[i] = sys_perf_event_open(attr, -1, i, -1, 0);
@@ -154,6 +157,11 @@ static void test_perf_event_task(struct perf_event_attr *attr)
 {
 	int pmu_fd;
 
+	/* per task perf event, enable inherit so the "dd ..." command can be traced properly.
+	 * Enabling inherit will cause bpf_perf_prog_read_time helper failure.
+	 */
+	attr->inherit = 1;
+
 	/* open task bound event */
 	pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0);
 	if (pmu_fd < 0) {
@@ -175,14 +183,12 @@ static void test_bpf_perf_event(void)
 		.freq = 1,
 		.type = PERF_TYPE_HARDWARE,
 		.config = PERF_COUNT_HW_CPU_CYCLES,
-		.inherit = 1,
 	};
 	struct perf_event_attr attr_type_sw = {
 		.sample_freq = SAMPLE_FREQ,
 		.freq = 1,
 		.type = PERF_TYPE_SOFTWARE,
 		.config = PERF_COUNT_SW_CPU_CLOCK,
-		.inherit = 1,
 	};
 	struct perf_event_attr attr_hw_cache_l1d = {
 		.sample_freq = SAMPLE_FREQ,
@@ -192,7 +198,6 @@ static void test_bpf_perf_event(void)
 			PERF_COUNT_HW_CACHE_L1D |
 			(PERF_COUNT_HW_CACHE_OP_READ << 8) |
 			(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
-		.inherit = 1,
 	};
 	struct perf_event_attr attr_hw_cache_branch_miss = {
 		.sample_freq = SAMPLE_FREQ,
@@ -202,7 +207,6 @@ static void test_bpf_perf_event(void)
 			PERF_COUNT_HW_CACHE_BPU |
 			(PERF_COUNT_HW_CACHE_OP_READ << 8) |
 			(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
-		.inherit = 1,
 	};
 	struct perf_event_attr attr_type_raw = {
 		.sample_freq = SAMPLE_FREQ,
@@ -210,7 +214,6 @@ static void test_bpf_perf_event(void)
 		.type = PERF_TYPE_RAW,
 		/* Intel Instruction Retired */
 		.config = 0xc0,
-		.inherit = 1,
 	};
 
 	printf("Test HW_CPU_CYCLES\n");
tools/include/uapi/linux/bpf.h +2 -1
index 79eb529..fa1be2c 100644
@@ -633,7 +633,8 @@ union bpf_attr {
 	FN(redirect_map),		\
 	FN(sk_redirect_map),		\
 	FN(sock_map_update),		\
-	FN(perf_event_read_value),
+	FN(perf_event_read_value),		\
+	FN(perf_prog_read_value),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
tools/testing/selftests/bpf/bpf_helpers.h +3 -0
index c866682..892d785 100644
@@ -73,6 +73,9 @@ static int (*bpf_sock_map_update)(void *map, void *key, void *value,
 static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,
 				       void *buf, unsigned int buf_size) =
 	(void *) BPF_FUNC_perf_event_read_value;
+static int (*bpf_perf_prog_read_value)(void *ctx, void *buf,
+				      unsigned int buf_size) =
+	(void *) BPF_FUNC_perf_prog_read_value;
 
 
 /* llvm builtin functions that eBPF C program may use to
Signature · 1 line
2.9.5