History

perf/core: Add ::write_backward attribute to perf event

This patch introduces 'write_backward' bit to perf_event_attr, which controls the direction of a ring buffer. After set, the corresponding ring buffer is written from end to beginning. This feature is design to support reading from overwritable ring buffer. Ring buffer can be created by mapping a perf event fd. Kernel puts event records into ring buffer, user tooling like perf fetch them from address returned by mmap(). To prevent racing between kernel and tooling, they communicate to each other through 'head' and 'tail' pointers. Kernel maintains 'head' pointer, points it to the next free area (tail of the last record). Tooling maintains 'tail' pointer, points it to the tail of last consumed record (record has already been fetched). Kernel determines the available space in a ring buffer using these two pointers to avoid overwrite unfetched records. By mapping without 'PROT_WRITE', an overwritable ring buffer is created. Different from normal ring buffer, tooling is unable to maintain 'tail' pointer because writing is forbidden. Therefore, for this type of ring buffers, kernel overwrite old records unconditionally, works like flight recorder. This feature would be useful if reading from overwritable ring buffer were as easy as reading from normal ring buffer. However, there's an obscure problem. The following figure demonstrates a full overwritable ring buffer. In this figure, the 'head' pointer points to the end of last record, and a long record 'E' is pending. For a normal ring buffer, a 'tail' pointer would have pointed to position (X), so kernel knows there's no more space in the ring buffer. However, for an overwritable ring buffer, kernel ignore the 'tail' pointer. (X) head . | . V +------+-------+----------+------+---+ |A....A|B.....B|C........C|D....D| | +------+-------+----------+------+---+ Record 'A' is overwritten by event 'E': head | V +--+---+-------+----------+------+---+ |.E|..A|B.....B|C........C|D....D|E..| +--+---+-------+----------+------+---+ Now tooling decides to read from this ring buffer. However, none of these two natural positions, 'head' and the start of this ring buffer, are pointing to the head of a record. Even the full ring buffer can be accessed by tooling, it is unable to find a position to start decoding. The first attempt tries to solve this problem AFAIK can be found from [1]. It makes kernel to maintain 'tail' pointer: updates it when ring buffer is half full. However, this approach introduces overhead to fast path. Test result shows a 1% overhead [2]. In addition, this method utilizes no more tham 50% records. Another attempt can be found from [3], which allows putting the size of an event at the end of each record. This approach allows tooling to find records in a backward manner from 'head' pointer by reading size of a record from its tail. However, because of alignment requirement, it needs 8 bytes to record the size of a record, which is a huge waste. Its performance is also not good, because more data need to be written. This approach also introduces some extra branch instructions to fast path. 'write_backward' is a better solution to this problem. Following figure demonstrates the state of the overwritable ring buffer when 'write_backward' is set before overwriting: head | V +---+------+----------+-------+------+ | |D....D|C........C|B.....B|A....A| +---+------+----------+-------+------+ and after overwriting: head | V +---+------+----------+-------+---+--+ |..E|D....D|C........C|B.....B|A..|E.| +---+------+----------+-------+---+--+ In each situation, 'head' points to the beginning of the newest record. From this record, tooling can iterate over the full ring buffer and fetch records one by one. The only limitation that needs to be considered is back-to-back reading. Due to the non-deterministic of user programs, it is impossible to ensure the ring buffer keeps stable during reading. Consider an extreme situation: tooling is scheduled out after reading record 'D', then a burst of events come, eat up the whole ring buffer (one or multiple rounds). When the tooling process comes back, reading after 'D' is incorrect now. To prevent this problem, we need to find a way to ensure the ring buffer is stable during reading. ioctl(PERF_EVENT_IOC_PAUSE_OUTPUT) is suggested because its overhead is lower than ioctl(PERF_EVENT_IOC_ENABLE). By carefully verifying 'header' pointer, reader can avoid pausing the ring-buffer. For example: /* A union of all possible events */ union perf_event event; p = head = perf_mmap__read_head(); while (true) { /* copy header of next event */ fetch(&event.header, p, sizeof(event.header)); /* read 'head' pointer */ head = perf_mmap__read_head(); /* check overwritten: is the header good? */ if (!verify(sizeof(event.header), p, head)) break; /* copy the whole event */ fetch(&event, p, event.header.size); /* read 'head' pointer again */ head = perf_mmap__read_head(); /* is the whole event good? */ if (!verify(event.header.size, p, head)) break; p += event.header.size; } However, the overhead is high because: a) In-place decoding is not safe. Copying-verifying-decoding is required. b) Fetching 'head' pointer requires additional synchronization. (From Alexei Starovoitov: Even when this trick works, pause is needed for more than stability of reading. When we collect the events into overwrite buffer we're waiting for some other trigger (like all cpu utilization spike or just one cpu running and all others are idle) and when it happens the buffer has valuable info from the past. At this point new events are no longer interesting and buffer should be paused, events read and unpaused until next trigger comes.) This patch utilizes event's default overflow_handler introduced previously. perf_event_output_backward() is created as the default overflow handler for backward ring buffers. To avoid extra overhead to fast path, original perf_event_output() becomes __perf_event_output() and marked '__always_inline'. In theory, there's no extra overhead introduced to fast path. Performance testing: Calling 3000000 times of 'close(-1)', use gettimeofday() to check duration. Use 'perf record -o /dev/null -e raw_syscalls:*' to capture system calls. In ns. Testing environment: CPU : Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz Kernel : v4.5.0 MEAN STDVAR BASE 800214.950 2853.083 PRE1 2253846.700 9997.014 PRE2 2257495.540 8516.293 POST 2250896.100 8933.921 Where 'BASE' is pure performance without capturing. 'PRE1' is test result of pure 'v4.5.0' kernel. 'PRE2' is test result before this patch. 'POST' is test result after this patch. See [4] for the detailed experimental setup. Considering the stdvar, this patch doesn't introduce performance overhead to the fast path. [1] http://lkml.iu.edu/hypermail/linux/kernel/1304.1/04584.html [2] http://lkml.iu.edu/hypermail/linux/kernel/1307.1/00535.html [3] http://lkml.iu.edu/hypermail/linux/kernel/1512.0/01265.html [4] http://lkml.kernel.org/g/56F89DCD.1040202@huawei.com Signed-off-by: Wang Nan <wangnan0@huawei.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Alexei Starovoitov <ast@kernel.org> Cc: <acme@kernel.org> Cc: <pi3orama@163.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Brendan Gregg <brendan.d.gregg@gmail.com> Cc: He Kuang <hekuang@huawei.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Cc: Zefan Li <lizefan@huawei.com> Link: http://lkml.kernel.org/r/1459865478-53413-1-git-send-email-wangnan0@huawei.com [ Fixed the changelog some more. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>

Wang Nan committed 10 years ago

Commit 9ecda411 parent f21d5ad4 files · +85 −10Browse files

Unified · Split

4 files
  1. include/linux/perf_event.h +25 −3
  2. include/uapi/linux/perf_event.h +2 −1
  3. kernel/events/core.c +43 −5
  4. kernel/events/ring_buffer.c +15 −1
include/linux/perf_event.h+25−3
@@ -834,14 +834,24 @@ extern int perf_event_overflow(struct perf_event *event, · 833 unmodified lines
834 struct perf_sample_data *data,834 struct perf_sample_data *data,
835 struct pt_regs *regs);835 struct pt_regs *regs);
836 836
837extern void perf_event_output_forward(struct perf_event *event,
838 struct perf_sample_data *data,
839 struct pt_regs *regs);
840extern void perf_event_output_backward(struct perf_event *event,
841 struct perf_sample_data *data,
842 struct pt_regs *regs);
837extern void perf_event_output(struct perf_event *event,843extern void perf_event_output(struct perf_event *event,
838 struct perf_sample_data *data,844 struct perf_sample_data *data,
839 struct pt_regs *regs);845 struct pt_regs *regs);
840 846
841static inline bool847static inline bool
842is_default_overflow_handler(struct perf_event *event)848is_default_overflow_handler(struct perf_event *event)
843{849{
844 return (event->overflow_handler == perf_event_output);850 if (likely(event->overflow_handler == perf_event_output_forward))
851 return true;
852 if (unlikely(event->overflow_handler == perf_event_output_backward))
853 return true;
854 return false;
845}855}
846 856
847extern void857extern void
@@ -1051,8 +1061,20 @@ static inline bool has_aux(struct perf_event *event) · 203 unmodified lines
1051 return event->pmu->setup_aux;1061 return event->pmu->setup_aux;
1052}1062}
1053 1063
1064static inline bool is_write_backward(struct perf_event *event)
1065{
1066 return !!event->attr.write_backward;
1067}
1068
1054extern int perf_output_begin(struct perf_output_handle *handle,1069extern int perf_output_begin(struct perf_output_handle *handle,
1055 struct perf_event *event, unsigned int size);1070 struct perf_event *event, unsigned int size);
1071extern int perf_output_begin_forward(struct perf_output_handle *handle,
1072 struct perf_event *event,
1073 unsigned int size);
1074extern int perf_output_begin_backward(struct perf_output_handle *handle,
1075 struct perf_event *event,
1076 unsigned int size);
1077
1056extern void perf_output_end(struct perf_output_handle *handle);1078extern void perf_output_end(struct perf_output_handle *handle);
1057extern unsigned int perf_output_copy(struct perf_output_handle *handle,1079extern unsigned int perf_output_copy(struct perf_output_handle *handle,
1058 const void *buf, unsigned int len);1080 const void *buf, unsigned int len);
include/uapi/linux/perf_event.h+2−1
@@ -340,7 +340,8 @@ struct perf_event_attr { · 339 unmodified lines
340 comm_exec : 1, /* flag comm events that are due to an exec */340 comm_exec : 1, /* flag comm events that are due to an exec */
341 use_clockid : 1, /* use @clockid for time fields */341 use_clockid : 1, /* use @clockid for time fields */
342 context_switch : 1, /* context switch data */342 context_switch : 1, /* context switch data */
343 __reserved_1 : 37;343 write_backward : 1, /* Write ring buffer from end to beginning */
344 __reserved_1 : 36;
344 345
345 union {346 union {
346 __u32 wakeup_events; /* wakeup every n events */347 __u32 wakeup_events; /* wakeup every n events */
kernel/events/core.c+43−5
@@ -5694,9 +5694,13 @@ void perf_prepare_sample(struct perf_event_header *header, · 5693 unmodified lines
5694 }5694 }
5695}5695}
5696 5696
5697void perf_event_output(struct perf_event *event,5697static void __always_inline
5698 struct perf_sample_data *data,5698__perf_event_output(struct perf_event *event,
5699 struct pt_regs *regs)5699 struct perf_sample_data *data,
5700 struct pt_regs *regs,
5701 int (*output_begin)(struct perf_output_handle *,
5702 struct perf_event *,
5703 unsigned int))
5700{5704{
5701 struct perf_output_handle handle;5705 struct perf_output_handle handle;
5702 struct perf_event_header header;5706 struct perf_event_header header;
@@ -5706,7 +5710,7 @@ void perf_event_output(struct perf_event *event, · 3 unmodified lines
5706 5710
5707 perf_prepare_sample(&header, data, event, regs);5711 perf_prepare_sample(&header, data, event, regs);
5708 5712
5709 if (perf_output_begin(&handle, event, header.size))5713 if (output_begin(&handle, event, header.size))
5710 goto exit;5714 goto exit;
5711 5715
5712 perf_output_sample(&handle, &header, data, event);5716 perf_output_sample(&handle, &header, data, event);
@@ -5717,6 +5721,30 @@ exit: · 4 unmodified lines
5717 rcu_read_unlock();5721 rcu_read_unlock();
5718}5722}
5719 5723
5724void
5725perf_event_output_forward(struct perf_event *event,
5726 struct perf_sample_data *data,
5727 struct pt_regs *regs)
5728{
5729 __perf_event_output(event, data, regs, perf_output_begin_forward);
5730}
5731
5732void
5733perf_event_output_backward(struct perf_event *event,
5734 struct perf_sample_data *data,
5735 struct pt_regs *regs)
5736{
5737 __perf_event_output(event, data, regs, perf_output_begin_backward);
5738}
5739
5740void
5741perf_event_output(struct perf_event *event,
5742 struct perf_sample_data *data,
5743 struct pt_regs *regs)
5744{
5745 __perf_event_output(event, data, regs, perf_output_begin);
5746}
5747
5720/*5748/*
5721 * read event_id5749 * read event_id
5722 */5750 */
@@ -8153,8 +8181,11 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, · 2430 unmodified lines
8153 if (overflow_handler) {8181 if (overflow_handler) {
8154 event->overflow_handler = overflow_handler;8182 event->overflow_handler = overflow_handler;
8155 event->overflow_handler_context = context;8183 event->overflow_handler_context = context;
8184 } else if (is_write_backward(event)){
8185 event->overflow_handler = perf_event_output_backward;
8186 event->overflow_handler_context = NULL;
8156 } else {8187 } else {
8157 event->overflow_handler = perf_event_output;8188 event->overflow_handler = perf_event_output_forward;
8158 event->overflow_handler_context = NULL;8189 event->overflow_handler_context = NULL;
8159 }8190 }
8160 8191
@@ -8388,6 +8419,13 @@ perf_event_set_output(struct perf_event *event, struct perf_event *output_event) · 227 unmodified lines
8388 if (output_event->clock != event->clock)8419 if (output_event->clock != event->clock)
8389 goto out;8420 goto out;
8390 8421
8422 /*
8423 * Either writing ring buffer from beginning or from end.
8424 * Mixing is not allowed.
8425 */
8426 if (is_write_backward(output_event) != is_write_backward(event))
8427 goto out;
8428
8391 /*8429 /*
8392 * If both events generate aux data, they must be on the same PMU8430 * If both events generate aux data, they must be on the same PMU
8393 */8431 */
kernel/events/ring_buffer.c+15−1
@@ -230,10 +230,24 @@ out: · 229 unmodified lines
230 return -ENOSPC;230 return -ENOSPC;
231}231}
232 232
233int perf_output_begin_forward(struct perf_output_handle *handle,
234 struct perf_event *event, unsigned int size)
235{
236 return __perf_output_begin(handle, event, size, false);
237}
238
239int perf_output_begin_backward(struct perf_output_handle *handle,
240 struct perf_event *event, unsigned int size)
241{
242 return __perf_output_begin(handle, event, size, true);
243}
244
233int perf_output_begin(struct perf_output_handle *handle,245int perf_output_begin(struct perf_output_handle *handle,
234 struct perf_event *event, unsigned int size)246 struct perf_event *event, unsigned int size)
235{247{
236 return __perf_output_begin(handle, event, size, false);248
249 return __perf_output_begin(handle, event, size,
250 unlikely(is_write_backward(event)));
237}251}
238 252
239unsigned int perf_output_copy(struct perf_output_handle *handle,253unsigned int perf_output_copy(struct perf_output_handle *handle,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t