← Documents Documentation/filesystems/relay.rst GitHub 원문 ↗

Linux 6.18.37 · Filesystems

relay interface (formerly relayfs)

Relay channel의 buffer 모델, 파일·커널 API, overwrite 모드와 callback 계약의 전문 번역입니다.

Source pathDocumentation/filesystems/relay.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

relay.rst:1-491

Relay는 고속 kernel-to-user 데이터 전달을 위해 CPU별 원형 buffer와 host filesystem의 relay file을 연결한다. 메시지는 sub-buffer를 가로질러 쪼개지지 않으며 padding과 소비 상태를 경계 단위로 관리한다.

No-overwrite는 새 event 유실을, overwrite flight-recorder 모드는 오래된 데이터 유실을 선택한다. `subbuf_start()`가 모드와 header 예약을 결정하고, `read()`는 padding 제거와 소비를 자동화하지만 `mmap()`보다 overhead가 크다.

Global buffer, `relay_reserve()`, overwrite 모드는 편리한 대신 locking이나 소비 통지의 기본 가정을 바꾼다. 따라서 callback 계약과 호출 context를 정확히 지키는 것이 데이터 순서와 손실 특성을 보장하는 핵심이다.

Relay channel 운용
`relay_open()`과 file callback으로 channel 생성`relay_write()` 또는 `relay_reserve()`로 기록`subbuf_start()`에서 경계·padding·모드 처리Userspace가 `mmap()`·`read()`로 소비`relay_flush()` 후 `relay_close()`

생성·쓰기·소비·종료의 핵심 API 흐름이다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ==================================
4 relay interface (formerly relayfs)
5 ==================================
6
7 The relay interface provides a means for kernel applications to
8 efficiently log and transfer large quantities of data from the kernel
9 to userspace via user-defined 'relay channels'.
10
11 A 'relay channel' is a kernel->user data relay mechanism implemented
12 as a set of per-cpu kernel buffers ('channel buffers'), each
13 represented as a regular file ('relay file') in user space. Kernel
14 clients write into the channel buffers using efficient write
15 functions; these automatically log into the current cpu's channel
16 buffer. User space applications mmap() or read() from the relay files
17 and retrieve the data as it becomes available. The relay files
18 themselves are files created in a host filesystem, e.g. debugfs, and
19 are associated with the channel buffers using the API described below.
20
21 The format of the data logged into the channel buffers is completely
22 up to the kernel client; the relay interface does however provide
23 hooks which allow kernel clients to impose some structure on the
24 buffer data. The relay interface doesn't implement any form of data
25 filtering - this also is left to the kernel client. The purpose is to
26 keep things as simple as possible.
27
28 This document provides an overview of the relay interface API. The
29 details of the function parameters are documented along with the
30 functions in the relay interface code - please see that for details.
31
32 Semantics
33 =========
34
35 Each relay channel has one buffer per CPU; each buffer has one or more
36 sub-buffers. Messages are written to the first sub-buffer until it is
37 too full to contain a new message, in which case it is written to
38 the next (if available). Messages are never split across sub-buffers.
39 At this point, userspace can be notified so it empties the first
40 sub-buffer, while the kernel continues writing to the next.
41
42 When notified that a sub-buffer is full, the kernel knows how many
43 bytes of it are padding, i.e., unused space occurring because a complete
44 message couldn't fit into a sub-buffer. Userspace can use this
45 knowledge to copy only valid data.
46
47 After copying it, userspace can notify the kernel that a sub-buffer
48 has been consumed.
49
50 A relay channel can operate in a mode where it will overwrite data not
51 yet collected by userspace, and not wait for it to be consumed.
52
53 The relay channel itself does not provide for communication of such
54 data between userspace and kernel, allowing the kernel side to remain
55 simple and not impose a single interface on userspace. It does
56 provide a set of examples and a separate helper though, described
57 below.
58
59 The read() interface both removes padding and internally consumes the
60 read sub-buffers; thus in cases where read(2) is being used to drain
61 the channel buffers, special-purpose communication between kernel and
62 user isn't necessary for basic operation.
63
64 One of the major goals of the relay interface is to provide a low
65 overhead mechanism for conveying kernel data to userspace. While the
66 read() interface is easy to use, it's not as efficient as the mmap()
67 approach; the example code attempts to make the tradeoff between the
68 two approaches as small as possible.
69
70 klog and relay-apps example code
71 ================================
72
73 The relay interface itself is ready to use, but to make things easier,
74 a couple of simple utility functions and a set of examples are provided.
75
76 The relay-apps example tarball, available on the relay sourceforge
77 site, contains a set of self-contained examples, each consisting of a
78 pair of .c files containing boilerplate code for each of the user and
79 kernel sides of a relay application. When combined these two sets of
80 boilerplate code provide glue to easily stream data to disk, without
81 having to bother with mundane housekeeping chores.
82
83 The 'klog debugging functions' patch (klog.patch in the relay-apps
84 tarball) provides a couple of high-level logging functions to the
85 kernel which allow writing formatted text or raw data to a channel,
86 regardless of whether a channel to write into exists or not, or even
87 whether the relay interface is compiled into the kernel or not. These
88 functions allow you to put unconditional 'trace' statements anywhere
89 in the kernel or kernel modules; only when there is a 'klog handler'
90 registered will data actually be logged (see the klog and kleak
91 examples for details).
92
93 It is of course possible to use the relay interface from scratch,
94 i.e., without using any of the relay-apps example code or klog, but
95 you'll have to implement communication between userspace and kernel,
96 allowing both to convey the state of buffers (full, empty, amount of
97 padding). The read() interface both removes padding and internally
98 consumes the read sub-buffers; thus in cases where read(2) is being
99 used to drain the channel buffers, special-purpose communication
100 between kernel and user isn't necessary for basic operation. Things
101 such as buffer-full conditions would still need to be communicated via
102 some channel though.
103
104 klog and the relay-apps examples can be found in the relay-apps
105 tarball on http://relayfs.sourceforge.net
106
107 The relay interface user space API
108 ==================================
109
110 The relay interface implements basic file operations for user space
111 access to relay channel buffer data. Here are the file operations
112 that are available and some comments regarding their behavior:
113
114 =========== ============================================================
115 open() enables user to open an _existing_ channel buffer.
116
117 mmap() results in channel buffer being mapped into the caller's
118 memory space. Note that you can't do a partial mmap - you
119 must map the entire file, which is NRBUF * SUBBUFSIZE.
120
121 read() read the contents of a channel buffer. The bytes read are
122 'consumed' by the reader, i.e., they won't be available
123 again to subsequent reads. If the channel is being used
124 in no-overwrite mode (the default), it can be read at any
125 time even if there's an active kernel writer. If the
126 channel is being used in overwrite mode and there are
127 active channel writers, results may be unpredictable -
128 users should make sure that all logging to the channel has
129 ended before using read() with overwrite mode. Sub-buffer
130 padding is automatically removed and will not be seen by
131 the reader.
132
133 sendfile() transfer data from a channel buffer to an output file
134 descriptor. Sub-buffer padding is automatically removed
135 and will not be seen by the reader.
136
137 poll() POLLIN/POLLRDNORM/POLLERR supported. User applications are
138 notified when sub-buffer boundaries are crossed.
139
140 close() decrements the channel buffer's refcount. When the refcount
141 reaches 0, i.e., when no process or kernel client has the
142 buffer open, the channel buffer is freed.
143 =========== ============================================================
144
145 In order for a user application to make use of relay files, the
146 host filesystem must be mounted. For example::
147
148 mount -t debugfs debugfs /sys/kernel/debug
149
150 .. Note::
151
152 The host filesystem doesn't need to be mounted for kernel
153 clients to create or use channels - it only needs to be
154 mounted when user space applications need access to the buffer
155 data.
156
157
158 The relay interface kernel API
159 ==============================
160
161 Here's a summary of the API the relay interface provides to in-kernel clients:
162
163 TBD(curr. line MT:/API/)
164 channel management functions::
165
166 relay_open(base_filename, parent, subbuf_size, n_subbufs,
167 callbacks, private_data)
168 relay_close(chan)
169 relay_flush(chan)
170 relay_reset(chan)
171
172 channel management typically called on instigation of userspace::
173
174 relay_subbufs_consumed(chan, cpu, subbufs_consumed)
175
176 write functions::
177
178 relay_write(chan, data, length)
179 __relay_write(chan, data, length)
180 relay_reserve(chan, length)
181
182 callbacks::
183
184 subbuf_start(buf, subbuf, prev_subbuf, prev_padding)
185 buf_mapped(buf, filp)
186 buf_unmapped(buf, filp)
187 create_buf_file(filename, parent, mode, buf, is_global)
188 remove_buf_file(dentry)
189
190 helper functions::
191
192 relay_buf_full(buf)
193 subbuf_start_reserve(buf, length)
194
195
196 Creating a channel
197 ------------------
198
199 relay_open() is used to create a channel, along with its per-cpu
200 channel buffers. Each channel buffer will have an associated file
201 created for it in the host filesystem, which can be and mmapped or
202 read from in user space. The files are named basename0...basenameN-1
203 where N is the number of online cpus, and by default will be created
204 in the root of the filesystem (if the parent param is NULL). If you
205 want a directory structure to contain your relay files, you should
206 create it using the host filesystem's directory creation function,
207 e.g. debugfs_create_dir(), and pass the parent directory to
208 relay_open(). Users are responsible for cleaning up any directory
209 structure they create, when the channel is closed - again the host
210 filesystem's directory removal functions should be used for that,
211 e.g. debugfs_remove().
212
213 In order for a channel to be created and the host filesystem's files
214 associated with its channel buffers, the user must provide definitions
215 for two callback functions, create_buf_file() and remove_buf_file().
216 create_buf_file() is called once for each per-cpu buffer from
217 relay_open() and allows the user to create the file which will be used
218 to represent the corresponding channel buffer. The callback should
219 return the dentry of the file created to represent the channel buffer.
220 remove_buf_file() must also be defined; it's responsible for deleting
221 the file(s) created in create_buf_file() and is called during
222 relay_close().
223
224 Here are some typical definitions for these callbacks, in this case
225 using debugfs::
226
227 /*
228 * create_buf_file() callback. Creates relay file in debugfs.
229 */
230 static struct dentry *create_buf_file_handler(const char *filename,
231 struct dentry *parent,
232 umode_t mode,
233 struct rchan_buf *buf,
234 int *is_global)
235 {
236 return debugfs_create_file(filename, mode, parent, buf,
237 &relay_file_operations);
238 }
239
240 /*
241 * remove_buf_file() callback. Removes relay file from debugfs.
242 */
243 static int remove_buf_file_handler(struct dentry *dentry)
244 {
245 debugfs_remove(dentry);
246
247 return 0;
248 }
249
250 /*
251 * relay interface callbacks
252 */
253 static struct rchan_callbacks relay_callbacks =
254 {
255 .create_buf_file = create_buf_file_handler,
256 .remove_buf_file = remove_buf_file_handler,
257 };
258
259 And an example relay_open() invocation using them::
260
261 chan = relay_open("cpu", NULL, SUBBUF_SIZE, N_SUBBUFS, &relay_callbacks, NULL);
262
263 If the create_buf_file() callback fails, or isn't defined, channel
264 creation and thus relay_open() will fail.
265
266 The total size of each per-cpu buffer is calculated by multiplying the
267 number of sub-buffers by the sub-buffer size passed into relay_open().
268 The idea behind sub-buffers is that they're basically an extension of
269 double-buffering to N buffers, and they also allow applications to
270 easily implement random-access-on-buffer-boundary schemes, which can
271 be important for some high-volume applications. The number and size
272 of sub-buffers is completely dependent on the application and even for
273 the same application, different conditions will warrant different
274 values for these parameters at different times. Typically, the right
275 values to use are best decided after some experimentation; in general,
276 though, it's safe to assume that having only 1 sub-buffer is a bad
277 idea - you're guaranteed to either overwrite data or lose events
278 depending on the channel mode being used.
279
280 The create_buf_file() implementation can also be defined in such a way
281 as to allow the creation of a single 'global' buffer instead of the
282 default per-cpu set. This can be useful for applications interested
283 mainly in seeing the relative ordering of system-wide events without
284 the need to bother with saving explicit timestamps for the purpose of
285 merging/sorting per-cpu files in a postprocessing step.
286
287 To have relay_open() create a global buffer, the create_buf_file()
288 implementation should set the value of the is_global outparam to a
289 non-zero value in addition to creating the file that will be used to
290 represent the single buffer. In the case of a global buffer,
291 create_buf_file() and remove_buf_file() will be called only once. The
292 normal channel-writing functions, e.g. relay_write(), can still be
293 used - writes from any cpu will transparently end up in the global
294 buffer - but since it is a global buffer, callers should make sure
295 they use the proper locking for such a buffer, either by wrapping
296 writes in a spinlock, or by copying a write function from relay.h and
297 creating a local version that internally does the proper locking.
298
299 The private_data passed into relay_open() allows clients to associate
300 user-defined data with a channel, and is immediately available
301 (including in create_buf_file()) via chan->private_data or
302 buf->chan->private_data.
303
304 Channel 'modes'
305 ---------------
306
307 relay channels can be used in either of two modes - 'overwrite' or
308 'no-overwrite'. The mode is entirely determined by the implementation
309 of the subbuf_start() callback, as described below. The default if no
310 subbuf_start() callback is defined is 'no-overwrite' mode. If the
311 default mode suits your needs, and you plan to use the read()
312 interface to retrieve channel data, you can ignore the details of this
313 section, as it pertains mainly to mmap() implementations.
314
315 In 'overwrite' mode, also known as 'flight recorder' mode, writes
316 continuously cycle around the buffer and will never fail, but will
317 unconditionally overwrite old data regardless of whether it's actually
318 been consumed. In no-overwrite mode, writes will fail, i.e., data will
319 be lost, if the number of unconsumed sub-buffers equals the total
320 number of sub-buffers in the channel. It should be clear that if
321 there is no consumer or if the consumer can't consume sub-buffers fast
322 enough, data will be lost in either case; the only difference is
323 whether data is lost from the beginning or the end of a buffer.
324
325 As explained above, a relay channel is made of up one or more
326 per-cpu channel buffers, each implemented as a circular buffer
327 subdivided into one or more sub-buffers. Messages are written into
328 the current sub-buffer of the channel's current per-cpu buffer via the
329 write functions described below. Whenever a message can't fit into
330 the current sub-buffer, because there's no room left for it, the
331 client is notified via the subbuf_start() callback that a switch to a
332 new sub-buffer is about to occur. The client uses this callback to 1)
333 initialize the next sub-buffer if appropriate 2) finalize the previous
334 sub-buffer if appropriate and 3) return a boolean value indicating
335 whether or not to actually move on to the next sub-buffer.
336
337 To implement 'no-overwrite' mode, the userspace client provides
338 an implementation of the subbuf_start() callback something like the
339 following::
340
341 static int subbuf_start(struct rchan_buf *buf,
342 void *subbuf,
343 void *prev_subbuf,
344 unsigned int prev_padding)
345 {
346 if (prev_subbuf)
347 *((unsigned *)prev_subbuf) = prev_padding;
348
349 if (relay_buf_full(buf))
350 return 0;
351
352 subbuf_start_reserve(buf, sizeof(unsigned int));
353
354 return 1;
355 }
356
357 If the current buffer is full, i.e., all sub-buffers remain unconsumed,
358 the callback returns 0 to indicate that the buffer switch should not
359 occur yet, i.e., until the consumer has had a chance to read the
360 current set of ready sub-buffers. For the relay_buf_full() function
361 to make sense, the consumer is responsible for notifying the relay
362 interface when sub-buffers have been consumed via
363 relay_subbufs_consumed(). Any subsequent attempts to write into the
364 buffer will again invoke the subbuf_start() callback with the same
365 parameters; only when the consumer has consumed one or more of the
366 ready sub-buffers will relay_buf_full() return 0, in which case the
367 buffer switch can continue.
368
369 The implementation of the subbuf_start() callback for 'overwrite' mode
370 would be very similar::
371
372 static int subbuf_start(struct rchan_buf *buf,
373 void *subbuf,
374 void *prev_subbuf,
375 size_t prev_padding)
376 {
377 if (prev_subbuf)
378 *((unsigned *)prev_subbuf) = prev_padding;
379
380 subbuf_start_reserve(buf, sizeof(unsigned int));
381
382 return 1;
383 }
384
385 In this case, the relay_buf_full() check is meaningless and the
386 callback always returns 1, causing the buffer switch to occur
387 unconditionally. It's also meaningless for the client to use the
388 relay_subbufs_consumed() function in this mode, as it's never
389 consulted.
390
391 The default subbuf_start() implementation, used if the client doesn't
392 define any callbacks, or doesn't define the subbuf_start() callback,
393 implements the simplest possible 'no-overwrite' mode, i.e., it does
394 nothing but return 0.
395
396 Header information can be reserved at the beginning of each sub-buffer
397 by calling the subbuf_start_reserve() helper function from within the
398 subbuf_start() callback. This reserved area can be used to store
399 whatever information the client wants. In the example above, room is
400 reserved in each sub-buffer to store the padding count for that
401 sub-buffer. This is filled in for the previous sub-buffer in the
402 subbuf_start() implementation; the padding value for the previous
403 sub-buffer is passed into the subbuf_start() callback along with a
404 pointer to the previous sub-buffer, since the padding value isn't
405 known until a sub-buffer is filled. The subbuf_start() callback is
406 also called for the first sub-buffer when the channel is opened, to
407 give the client a chance to reserve space in it. In this case the
408 previous sub-buffer pointer passed into the callback will be NULL, so
409 the client should check the value of the prev_subbuf pointer before
410 writing into the previous sub-buffer.
411
412 Writing to a channel
413 --------------------
414
415 Kernel clients write data into the current cpu's channel buffer using
416 relay_write() or __relay_write(). relay_write() is the main logging
417 function - it uses local_irqsave() to protect the buffer and should be
418 used if you might be logging from interrupt context. If you know
419 you'll never be logging from interrupt context, you can use
420 __relay_write(), which only disables preemption. These functions
421 don't return a value, so you can't determine whether or not they
422 failed - the assumption is that you wouldn't want to check a return
423 value in the fast logging path anyway, and that they'll always succeed
424 unless the buffer is full and no-overwrite mode is being used, in
425 which case you can detect a failed write in the subbuf_start()
426 callback by calling the relay_buf_full() helper function.
427
428 relay_reserve() is used to reserve a slot in a channel buffer which
429 can be written to later. This would typically be used in applications
430 that need to write directly into a channel buffer without having to
431 stage data in a temporary buffer beforehand. Because the actual write
432 may not happen immediately after the slot is reserved, applications
433 using relay_reserve() can keep a count of the number of bytes actually
434 written, either in space reserved in the sub-buffers themselves or as
435 a separate array. See the 'reserve' example in the relay-apps tarball
436 at http://relayfs.sourceforge.net for an example of how this can be
437 done. Because the write is under control of the client and is
438 separated from the reserve, relay_reserve() doesn't protect the buffer
439 at all - it's up to the client to provide the appropriate
440 synchronization when using relay_reserve().
441
442 Closing a channel
443 -----------------
444
445 The client calls relay_close() when it's finished using the channel.
446 The channel and its associated buffers are destroyed when there are no
447 longer any references to any of the channel buffers. relay_flush()
448 forces a sub-buffer switch on all the channel buffers, and can be used
449 to finalize and process the last sub-buffers before the channel is
450 closed.
451
452 Misc
453 ----
454
455 Some applications may want to keep a channel around and re-use it
456 rather than open and close a new channel for each use. relay_reset()
457 can be used for this purpose - it resets a channel to its initial
458 state without reallocating channel buffer memory or destroying
459 existing mappings. It should however only be called when it's safe to
460 do so, i.e., when the channel isn't currently being written to.
461
462 Finally, there are a couple of utility callbacks that can be used for
463 different purposes. buf_mapped() is called whenever a channel buffer
464 is mmapped from user space and buf_unmapped() is called when it's
465 unmapped. The client can use this notification to trigger actions
466 within the kernel application, such as enabling/disabling logging to
467 the channel.
468
469
470 Resources
471 =========
472
473 For news, example code, mailing list, etc. see the relay interface homepage:
474
475 http://relayfs.sourceforge.net
476
477
478 Credits
479 =======
480
481 The ideas and specs for the relay interface came about as a result of
482 discussions on tracing involving the following:
483
484 Michel Dagenais <michel.dagenais@polymtl.ca>
485 Richard Moore <richardj_moore@uk.ibm.com>
486 Bob Wisniewski <bob@watson.ibm.com>
487 Karim Yaghmour <karim@opersys.com>
488 Tom Zanussi <zanussi@us.ibm.com>
489
490 Also thanks to Hubertus Franke for a lot of useful suggestions and bug
491 reports.
492

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Relay channel 개요

1-31

Relay 인터페이스는 커널 애플리케이션이 사용자가 정의한 relay channel을 통해 많은 데이터를 효율적으로 기록하고 userspace로 전송하게 한다. 과거 이름은 relayfs였다.

Relay channel은 CPU마다 하나씩 있는 커널 channel buffer 집합으로 구현한 kernel-to-user 데이터 전달 장치다. 각 channel buffer는 userspace에서 일반 파일인 relay file 하나로 보인다.

커널 클라이언트가 효율적인 쓰기 함수로 기록하면 현재 CPU의 channel buffer가 자동 선택된다. Userspace는 relay file을 `mmap()`하거나 `read()`해 데이터가 준비되는 대로 가져온다. Relay file은 debugfs 같은 host filesystem에 생성되고 아래 API로 channel buffer와 연결한다.

Channel buffer에 기록하는 데이터 형식은 전적으로 커널 클라이언트가 정한다. Relay는 클라이언트가 buffer 데이터에 구조를 부여할 hook을 제공하지만 filtering은 구현하지 않는다. 단순성을 유지하기 위해 필터링도 커널 클라이언트의 책임이다.

이 문서는 relay API의 개요이며 각 함수 인자의 상세 계약은 relay 인터페이스 소스의 함수 문서를 참고해야 한다.

Relay 데이터 경로
커널 클라이언트가 relay write API 호출현재 CPU의 channel buffer 선택sub-buffer에 완전한 메시지 기록debugfs 등의 relay file로 노출userspace가 `mmap()` 또는 `read()`로 수집

현재 CPU의 커널 버퍼와 host filesystem 파일을 통해 userspace로 흐른다.

.. SPDX-License-Identifier: GPL-2.0

==================================
relay interface (formerly relayfs)
==================================

The relay interface provides a means for kernel applications to
efficiently log and transfer large quantities of data from the kernel
to userspace via user-defined 'relay channels'.

A 'relay channel' is a kernel->user data relay mechanism implemented
as a set of per-cpu kernel buffers ('channel buffers'), each
represented as a regular file ('relay file') in user space.  Kernel
clients write into the channel buffers using efficient write
functions; these automatically log into the current cpu's channel
buffer.  User space applications mmap() or read() from the relay files
and retrieve the data as it becomes available.  The relay files
themselves are files created in a host filesystem, e.g. debugfs, and
are associated with the channel buffers using the API described below.

The format of the data logged into the channel buffers is completely
up to the kernel client; the relay interface does however provide
hooks which allow kernel clients to impose some structure on the
buffer data.  The relay interface doesn't implement any form of data
filtering - this also is left to the kernel client.  The purpose is to
keep things as simple as possible.

This document provides an overview of the relay interface API.  The
details of the function parameters are documented along with the
functions in the relay interface code - please see that for details.

Sub-buffer 전환과 소비 의미

32-69

각 relay channel은 CPU마다 buffer 하나를 가지며 각 buffer는 하나 이상의 sub-buffer로 나뉜다. 첫 sub-buffer가 새 메시지 전체를 담을 수 없을 만큼 차면 다음 사용 가능한 sub-buffer에 쓴다. 메시지는 sub-buffer 경계를 가로질러 나뉘지 않는다.

전환 시 userspace에 첫 sub-buffer를 비우라고 알릴 수 있고 커널은 다음 sub-buffer에 계속 쓴다. 가득 찬 sub-buffer에는 완전한 메시지가 들어가지 않아 남은 padding byte 수가 알려지므로 userspace는 유효 데이터만 복사할 수 있다. 복사 뒤에는 해당 sub-buffer를 소비했다고 커널에 알릴 수 있다.

채널은 userspace가 아직 수집하지 않은 데이터를 기다리지 않고 덮어쓰는 overwrite 모드로도 동작할 수 있다.

Relay channel 자체는 padding·소비 상태를 kernel과 userspace 사이에 전달하는 단일 통신 규약을 강제하지 않는다. 커널 쪽을 단순하게 유지하기 위해 이 통신은 클라이언트가 정하고, 별도 helper와 예제를 제공한다.

`read()` 인터페이스는 padding을 제거하고 읽은 sub-buffer를 내부적으로 소비 처리한다. 따라서 `read(2)`로 channel buffer를 비우는 기본 동작에는 별도의 kernel-userspace 상태 통신이 필요 없다.

Relay의 주요 목표는 낮은 overhead다. `read()`는 사용하기 쉽지만 `mmap()` 방식만큼 효율적이지 않으며 예제 코드는 둘의 성능 차이를 줄이려 한다.

Sub-buffer 상태
상태커널 동작Userspace 동작
기록 중현재 sub-buffer에 완전한 메시지 추가대기 또는 mmap 관찰
메시지 미수용메시지를 쪼개지 않고 다음 sub-buffer로 전환이전 sub-buffer 수집
Padding사용하지 않은 byte 수 전달유효 데이터만 복사
Consumed재사용 가능한 sub-buffer로 인식`relay_subbufs_consumed()` 또는 `read()` 내부 처리

생산자와 소비자가 경계·padding·소비 완료를 기준으로 협력한다.

Semantics
=========

Each relay channel has one buffer per CPU; each buffer has one or more
sub-buffers.  Messages are written to the first sub-buffer until it is
too full to contain a new message, in which case it is written to
the next (if available).  Messages are never split across sub-buffers.
At this point, userspace can be notified so it empties the first
sub-buffer, while the kernel continues writing to the next.

When notified that a sub-buffer is full, the kernel knows how many
bytes of it are padding, i.e., unused space occurring because a complete
message couldn't fit into a sub-buffer.  Userspace can use this
knowledge to copy only valid data.

After copying it, userspace can notify the kernel that a sub-buffer
has been consumed.

A relay channel can operate in a mode where it will overwrite data not
yet collected by userspace, and not wait for it to be consumed.

The relay channel itself does not provide for communication of such
data between userspace and kernel, allowing the kernel side to remain
simple and not impose a single interface on userspace.  It does
provide a set of examples and a separate helper though, described
below.

The read() interface both removes padding and internally consumes the
read sub-buffers; thus in cases where read(2) is being used to drain
the channel buffers, special-purpose communication between kernel and
user isn't necessary for basic operation.

One of the major goals of the relay interface is to provide a low
overhead mechanism for conveying kernel data to userspace.  While the
read() interface is easy to use, it's not as efficient as the mmap()
approach; the example code attempts to make the tradeoff between the
two approaches as small as possible.

klog와 relay-apps 예제

70-106

Relay 인터페이스는 바로 사용할 수 있지만, 단순한 utility 함수와 예제 집합도 제공한다. Relay SourceForge 사이트의 relay-apps tarball에는 userspace와 kernel 쪽 boilerplate C 파일 한 쌍으로 된 자체 완결 예제가 들어 있다. 두 파일을 조합하면 반복적인 상태 관리 코드를 직접 쓰지 않고 데이터를 디스크로 스트리밍할 수 있다.

Relay-apps의 `klog.patch`가 제공하는 klog debugging 함수는 대상 channel이 존재하는지, relay가 커널에 컴파일됐는지와 무관하게 formatted text나 raw data를 쓰는 고수준 API다. 커널이나 모듈 어디에나 무조건 trace 문을 둘 수 있고 `klog handler`가 등록된 경우에만 실제로 기록한다. 자세한 사용은 klog와 kleak 예제를 참고한다.

예제나 klog 없이 relay를 직접 사용할 수도 있다. 이 경우 buffer의 full·empty·padding 양을 양쪽에 전달하는 kernel-userspace 통신을 구현해야 한다.

`read()`는 padding 제거와 sub-buffer 소비를 내부 처리하므로 기본 배출에는 전용 통신이 필요 없지만 buffer-full 같은 상태는 여전히 어떤 channel로 알려야 한다.

Klog와 relay-apps 예제는 `http://relayfs.sourceforge.net`의 relay-apps tarball에서 찾을 수 있다.

Relay 사용 수준
방식제공 기능클라이언트 책임
klog채널 존재와 구성 여부에 독립적인 trace APIhandler 등록과 로그 정책
relay-appskernel/userspace boilerplate와 디스크 스트리밍 glue예제에 맞춘 데이터 형식
직접 relay API최소 channel·buffer primitivefull·empty·padding 통신 전체
`read()` 기반padding 제거와 소비 자동화buffer-full 등 추가 상태 통신

편의 계층을 사용할수록 상태 통신과 반복 코드가 줄어든다.

klog and relay-apps example code
================================

The relay interface itself is ready to use, but to make things easier,
a couple of simple utility functions and a set of examples are provided.

The relay-apps example tarball, available on the relay sourceforge
site, contains a set of self-contained examples, each consisting of a
pair of .c files containing boilerplate code for each of the user and
kernel sides of a relay application.  When combined these two sets of
boilerplate code provide glue to easily stream data to disk, without
having to bother with mundane housekeeping chores.

The 'klog debugging functions' patch (klog.patch in the relay-apps
tarball) provides a couple of high-level logging functions to the
kernel which allow writing formatted text or raw data to a channel,
regardless of whether a channel to write into exists or not, or even
whether the relay interface is compiled into the kernel or not.  These
functions allow you to put unconditional 'trace' statements anywhere
in the kernel or kernel modules; only when there is a 'klog handler'
registered will data actually be logged (see the klog and kleak
examples for details).

It is of course possible to use the relay interface from scratch,
i.e., without using any of the relay-apps example code or klog, but
you'll have to implement communication between userspace and kernel,
allowing both to convey the state of buffers (full, empty, amount of
padding).  The read() interface both removes padding and internally
consumes the read sub-buffers; thus in cases where read(2) is being
used to drain the channel buffers, special-purpose communication
between kernel and user isn't necessary for basic operation.  Things
such as buffer-full conditions would still need to be communicated via
some channel though.

klog and the relay-apps examples can be found in the relay-apps
tarball on http://relayfs.sourceforge.net

Userspace 파일 연산 API

107-157

Relay 인터페이스는 userspace가 channel buffer 데이터에 접근할 기본 파일 연산을 구현한다.

Relay file 연산
연산동작과 제약
`open()`이미 존재하는 channel buffer를 연다
`mmap()`buffer 전체를 호출자 주소 공간에 mapping; 부분 mmap은 불가하며 크기는 `NRBUF * SUBBUFSIZE`
`read()`읽은 byte를 소비해 다음 read에서 다시 보이지 않게 하고 padding 자동 제거
`sendfile()`channel buffer를 출력 file descriptor로 전송하며 padding 자동 제거
`poll()``POLLIN`, `POLLRDNORM`, `POLLERR` 지원; sub-buffer 경계 통과 때 알림
`close()`buffer refcount 감소; process와 kernel client 참조가 모두 0이면 buffer 해제

파일 전체 mapping, 소비 의미, 모드별 제약을 포함한 계약이다.

기본 no-overwrite 모드에서는 active kernel writer가 있어도 언제든 `read()`할 수 있다. Overwrite 모드에서 writer가 활동 중이면 결과를 예측할 수 없으므로 모든 channel logging이 끝난 뒤 읽어야 한다.

Userspace에서 relay file을 사용하려면 host filesystem을 마운트해야 한다. Debugfs를 쓰는 대표 명령은 다음과 같다.

mount -t debugfs debugfs /sys/kernel/debug

커널 클라이언트가 channel을 생성하거나 사용하는 데 host filesystem 마운트가 필요한 것은 아니다. Userspace가 buffer 데이터에 접근할 때만 마운트돼 있어야 한다.

Relay file 수명
커널이 channel과 buffer 생성Callback이 host filesystem용 dentry 준비Userspace 접근 전에 debugfs 등 마운트`open()` 후 `mmap()`·`read()`·`poll()`마지막 참조의 `close()`에서 buffer 해제 가능

Kernel channel 생성과 userspace 접근 가능 시점은 host filesystem 마운트 여부에 따라 분리된다.

The relay interface user space API
==================================

The relay interface implements basic file operations for user space
access to relay channel buffer data.  Here are the file operations
that are available and some comments regarding their behavior:

=========== ============================================================
open()            enables user to open an _existing_ channel buffer.

mmap()      results in channel buffer being mapped into the caller's
            memory space. Note that you can't do a partial mmap - you
            must map the entire file, which is NRBUF * SUBBUFSIZE.

read()      read the contents of a channel buffer.  The bytes read are
            'consumed' by the reader, i.e., they won't be available
            again to subsequent reads.  If the channel is being used
            in no-overwrite mode (the default), it can be read at any
            time even if there's an active kernel writer.  If the
            channel is being used in overwrite mode and there are
            active channel writers, results may be unpredictable -
            users should make sure that all logging to the channel has
            ended before using read() with overwrite mode.  Sub-buffer
            padding is automatically removed and will not be seen by
            the reader.

sendfile()  transfer data from a channel buffer to an output file
            descriptor. Sub-buffer padding is automatically removed
            and will not be seen by the reader.

poll()      POLLIN/POLLRDNORM/POLLERR supported.  User applications are
            notified when sub-buffer boundaries are crossed.

close()     decrements the channel buffer's refcount.  When the refcount
            reaches 0, i.e., when no process or kernel client has the
            buffer open, the channel buffer is freed.
=========== ============================================================

In order for a user application to make use of relay files, the
host filesystem must be mounted.  For example::

        mount -t debugfs debugfs /sys/kernel/debug

.. Note::

        The host filesystem doesn't need to be mounted for kernel
        clients to create or use channels - it only needs to be
        mounted when user space applications need access to the buffer
        data.

커널 API 분류

158-195

In-kernel 클라이언트가 사용하는 relay API는 channel 관리, userspace 소비 통지, 쓰기, callback, helper의 다섯 묶음으로 나뉜다.

Relay 커널 API
분류API
Channel 관리`relay_open()`, `relay_close()`, `relay_flush()`, `relay_reset()`
소비 통지`relay_subbufs_consumed(chan, cpu, subbufs_consumed)`
쓰기`relay_write()`, `__relay_write()`, `relay_reserve()`
Callback`subbuf_start()`, `buf_mapped()`, `buf_unmapped()`, `create_buf_file()`, `remove_buf_file()`
Helper`relay_buf_full()`, `subbuf_start_reserve()`

호출 목적별 함수와 callback을 그대로 보존한다.

`relay_open()`은 이름·부모 dentry·sub-buffer 크기와 개수·callback 집합·private data를 받아 channel을 만든다. `relay_close()`는 닫고, `relay_flush()`는 마지막 데이터 처리를 위해 전환을 강제하며, `relay_reset()`은 할당과 mapping을 유지한 채 초기 상태로 되돌린다.

`relay_subbufs_consumed()`는 userspace가 CPU별로 소비한 sub-buffer 수를 알린다. 쓰기 API는 현재 CPU buffer를 사용하며 callback은 경계 전환과 파일 수명, mapping 수명을 다룬다.

원문의 `TBD(curr. line MT:/API/)` 표시는 API 상세 문서 연결이 미완성이던 상태를 그대로 나타낸다.

The relay interface kernel API
==============================

Here's a summary of the API the relay interface provides to in-kernel clients:

TBD(curr. line MT:/API/)
  channel management functions::

    relay_open(base_filename, parent, subbuf_size, n_subbufs,
               callbacks, private_data)
    relay_close(chan)
    relay_flush(chan)
    relay_reset(chan)

  channel management typically called on instigation of userspace::

    relay_subbufs_consumed(chan, cpu, subbufs_consumed)

  write functions::

    relay_write(chan, data, length)
    __relay_write(chan, data, length)
    relay_reserve(chan, length)

  callbacks::

    subbuf_start(buf, subbuf, prev_subbuf, prev_padding)
    buf_mapped(buf, filp)
    buf_unmapped(buf, filp)
    create_buf_file(filename, parent, mode, buf, is_global)
    remove_buf_file(dentry)

  helper functions::

    relay_buf_full(buf)
    subbuf_start_reserve(buf, length)

Channel 생성과 파일 callback

196-303

`relay_open()`은 channel과 CPU별 channel buffer를 만든다. 각 buffer에는 host filesystem 파일이 연결되며 userspace가 mmap하거나 읽을 수 있다. 온라인 CPU 수가 `N`이면 기본 파일명은 `basename0`부터 `basenameN-1`이다.

`parent`가 `NULL`이면 host filesystem 루트에 파일을 만든다. 별도 디렉터리가 필요하면 `debugfs_create_dir()` 같은 host filesystem 함수를 호출해 부모를 만들고 `relay_open()`에 넘긴다. Channel을 닫을 때 클라이언트가 `debugfs_remove()` 같은 함수로 자신이 만든 디렉터리 구조를 정리해야 한다.

Channel 생성에는 `create_buf_file()`과 `remove_buf_file()` callback이 필수다. 전자는 `relay_open()`이 CPU별 buffer마다 호출해 표시 파일을 만들고 해당 dentry를 반환한다. 후자는 `relay_close()` 중 전자가 만든 파일을 삭제한다.

static struct dentry *create_buf_file_handler(const char *filename,
                                        struct dentry *parent,
                                        umode_t mode,
                                        struct rchan_buf *buf,
                                        int *is_global)
{
        return debugfs_create_file(filename, mode, parent, buf,
                                   &relay_file_operations);
}

static int remove_buf_file_handler(struct dentry *dentry)
{
        debugfs_remove(dentry);
        return 0;
}

static struct rchan_callbacks relay_callbacks = {
        .create_buf_file = create_buf_file_handler,
        .remove_buf_file = remove_buf_file_handler,
};
chan = relay_open("cpu", NULL, SUBBUF_SIZE, N_SUBBUFS, &relay_callbacks, NULL);

`create_buf_file()` callback이 실패하거나 정의되지 않으면 channel 생성과 `relay_open()`이 실패한다.

CPU별 buffer 전체 크기는 `n_subbufs * subbuf_size`다. Sub-buffer는 double buffering을 N개 buffer로 확장한 구조이며, 높은 처리량 애플리케이션이 buffer 경계 단위 random access를 구현하는 데도 유용하다. 적절한 개수와 크기는 workload에 따라 실험으로 정해야 한다.

Sub-buffer 하나만 두는 것은 좋지 않다. Channel 모드에 따라 기존 데이터를 덮어쓰거나 새 event를 잃는 일이 보장되기 때문이다.

`create_buf_file()`은 기본 CPU별 집합 대신 global buffer 하나를 만들 수도 있다. 시스템 전체 event의 상대 순서를 보고 싶고 CPU별 파일을 timestamp로 병합·정렬하고 싶지 않은 애플리케이션에 유용하다. 이 경우 callback은 파일을 만들면서 `is_global` 출력 인자를 0이 아닌 값으로 설정한다.

Global buffer에서는 `create_buf_file()`과 `remove_buf_file()`이 한 번씩만 호출되고 보통의 `relay_write()`도 사용할 수 있다. 모든 CPU 쓰기가 같은 buffer로 들어가므로 호출자가 spinlock으로 감싸거나 `relay.h` 쓰기 함수를 복사해 내부 locking을 추가한 로컬 함수를 만들어야 한다.

`relay_open()`의 `private_data`는 channel과 사용자 정의 데이터를 연결한다. `create_buf_file()` 실행 시점부터 `chan->private_data` 또는 `buf->chan->private_data`로 즉시 접근할 수 있다.

Per-CPU와 global buffer
구성장점주의점
Per-CPUCPU별 쓰기 경합 최소화후처리에서 timestamp 기반 병합 필요
Global시스템 전체 상대 순서 직접 관찰spinlock 등 명시적 locking 필수

파일 수와 event ordering 대신 동시 쓰기 동기화 책임이 달라진다.

Creating a channel
------------------

relay_open() is used to create a channel, along with its per-cpu
channel buffers.  Each channel buffer will have an associated file
created for it in the host filesystem, which can be and mmapped or
read from in user space.  The files are named basename0...basenameN-1
where N is the number of online cpus, and by default will be created
in the root of the filesystem (if the parent param is NULL).  If you
want a directory structure to contain your relay files, you should
create it using the host filesystem's directory creation function,
e.g. debugfs_create_dir(), and pass the parent directory to
relay_open().  Users are responsible for cleaning up any directory
structure they create, when the channel is closed - again the host
filesystem's directory removal functions should be used for that,
e.g. debugfs_remove().

In order for a channel to be created and the host filesystem's files
associated with its channel buffers, the user must provide definitions
for two callback functions, create_buf_file() and remove_buf_file().
create_buf_file() is called once for each per-cpu buffer from
relay_open() and allows the user to create the file which will be used
to represent the corresponding channel buffer.  The callback should
return the dentry of the file created to represent the channel buffer.
remove_buf_file() must also be defined; it's responsible for deleting
the file(s) created in create_buf_file() and is called during
relay_close().

Here are some typical definitions for these callbacks, in this case
using debugfs::

    /*
    * create_buf_file() callback.  Creates relay file in debugfs.
    */
    static struct dentry *create_buf_file_handler(const char *filename,
                                                struct dentry *parent,
                                                umode_t mode,
                                                struct rchan_buf *buf,
                                                int *is_global)
    {
            return debugfs_create_file(filename, mode, parent, buf,
                                    &relay_file_operations);
    }

    /*
    * remove_buf_file() callback.  Removes relay file from debugfs.
    */
    static int remove_buf_file_handler(struct dentry *dentry)
    {
            debugfs_remove(dentry);

            return 0;
    }

    /*
    * relay interface callbacks
    */
    static struct rchan_callbacks relay_callbacks =
    {
            .create_buf_file = create_buf_file_handler,
            .remove_buf_file = remove_buf_file_handler,
    };

And an example relay_open() invocation using them::

  chan = relay_open("cpu", NULL, SUBBUF_SIZE, N_SUBBUFS, &relay_callbacks, NULL);

If the create_buf_file() callback fails, or isn't defined, channel
creation and thus relay_open() will fail.

The total size of each per-cpu buffer is calculated by multiplying the
number of sub-buffers by the sub-buffer size passed into relay_open().
The idea behind sub-buffers is that they're basically an extension of
double-buffering to N buffers, and they also allow applications to
easily implement random-access-on-buffer-boundary schemes, which can
be important for some high-volume applications.  The number and size
of sub-buffers is completely dependent on the application and even for
the same application, different conditions will warrant different
values for these parameters at different times.  Typically, the right
values to use are best decided after some experimentation; in general,
though, it's safe to assume that having only 1 sub-buffer is a bad
idea - you're guaranteed to either overwrite data or lose events
depending on the channel mode being used.

The create_buf_file() implementation can also be defined in such a way
as to allow the creation of a single 'global' buffer instead of the
default per-cpu set.  This can be useful for applications interested
mainly in seeing the relative ordering of system-wide events without
the need to bother with saving explicit timestamps for the purpose of
merging/sorting per-cpu files in a postprocessing step.

To have relay_open() create a global buffer, the create_buf_file()
implementation should set the value of the is_global outparam to a
non-zero value in addition to creating the file that will be used to
represent the single buffer.  In the case of a global buffer,
create_buf_file() and remove_buf_file() will be called only once.  The
normal channel-writing functions, e.g. relay_write(), can still be
used - writes from any cpu will transparently end up in the global
buffer - but since it is a global buffer, callers should make sure
they use the proper locking for such a buffer, either by wrapping
writes in a spinlock, or by copying a write function from relay.h and
creating a local version that internally does the proper locking.

The private_data passed into relay_open() allows clients to associate
user-defined data with a channel, and is immediately available
(including in create_buf_file()) via chan->private_data or
buf->chan->private_data.

Overwrite·no-overwrite 모드

304-411

Relay channel의 overwrite와 no-overwrite 모드는 `subbuf_start()` callback 구현으로 완전히 결정된다. Callback을 정의하지 않으면 기본은 no-overwrite다. 기본 모드와 `read()`를 사용한다면 이 절의 mmap 중심 세부사항을 대부분 신경 쓰지 않아도 된다.

Overwrite, 즉 flight recorder 모드에서는 쓰기가 원형 buffer를 계속 순환해 실패하지 않지만 소비되지 않은 옛 데이터도 무조건 덮어쓴다. No-overwrite 모드에서는 미소비 sub-buffer 수가 전체 개수와 같으면 쓰기가 실패해 새 데이터가 유실된다.

소비자가 없거나 충분히 빠르지 않으면 어느 모드든 데이터가 유실된다. 차이는 buffer의 시작 쪽 옛 데이터를 잃는지, 끝 쪽 새 event를 잃는지다.

현재 메시지가 sub-buffer에 맞지 않으면 새 sub-buffer 전환 직전에 `subbuf_start()`를 호출한다. 클라이언트는 다음 sub-buffer 초기화, 이전 sub-buffer 마무리, 실제 전환 허용 여부의 boolean 반환을 수행한다.

static int subbuf_start(struct rchan_buf *buf,
                        void *subbuf,
                        void *prev_subbuf,
                        unsigned int prev_padding)
{
        if (prev_subbuf)
                *((unsigned *)prev_subbuf) = prev_padding;

        if (relay_buf_full(buf))
                return 0;

        subbuf_start_reserve(buf, sizeof(unsigned int));
        return 1;
}

No-overwrite callback은 이전 sub-buffer가 있으면 그 header에 `prev_padding`을 기록한다. 모든 sub-buffer가 미소비 상태면 `relay_buf_full()`이 참이므로 0을 반환해 전환을 중지한다. 소비자가 `relay_subbufs_consumed()`로 하나 이상 소비했다고 알린 뒤 재시도할 때만 전환이 진행된다.

쓰기가 다시 시도되면 같은 인자로 `subbuf_start()`가 재호출된다. 준비된 sub-buffer가 소비되면 `relay_buf_full()`이 0을 반환하고 callback이 1을 반환할 수 있다.

static int subbuf_start(struct rchan_buf *buf,
                        void *subbuf,
                        void *prev_subbuf,
                        size_t prev_padding)
{
        if (prev_subbuf)
                *((unsigned *)prev_subbuf) = prev_padding;

        subbuf_start_reserve(buf, sizeof(unsigned int));
        return 1;
}

Overwrite callback에서는 full 검사가 의미 없고 항상 1을 반환해 무조건 전환한다. 이 모드는 소비 상태를 참조하지 않으므로 `relay_subbufs_consumed()`를 호출할 의미도 없다.

클라이언트 callback이 전혀 없거나 `subbuf_start()`만 없을 때 쓰는 기본 구현은 가장 단순한 no-overwrite 모드로, 아무 작업 없이 0만 반환한다.

`subbuf_start_reserve()`는 각 sub-buffer 시작에 header 공간을 예약한다. 예제에서는 padding count를 저장할 `unsigned int` 공간을 예약하고, 값이 sub-buffer를 채운 뒤에야 확정되므로 다음 전환 callback에서 `prev_subbuf`에 기록한다.

Channel을 열 때 첫 sub-buffer에도 callback을 호출해 header를 예약할 기회를 준다. 이때 이전 sub-buffer가 없으므로 `prev_subbuf == NULL`이며, 이전 buffer에 쓰기 전 반드시 포인터를 검사해야 한다.

Relay channel 모드
모드Buffer full 동작유실 위치소비 통지
no-overwrite전환 거부, 새 write 실패새 event`relay_subbufs_consumed()` 필요
overwrite항상 전환해 이전 내용 덮어씀가장 오래된 데이터사용하지 않음

소비자가 느릴 때 어느 데이터를 보존할지 선택한다.

Channel 'modes'
---------------

relay channels can be used in either of two modes - 'overwrite' or
'no-overwrite'.  The mode is entirely determined by the implementation
of the subbuf_start() callback, as described below.  The default if no
subbuf_start() callback is defined is 'no-overwrite' mode.  If the
default mode suits your needs, and you plan to use the read()
interface to retrieve channel data, you can ignore the details of this
section, as it pertains mainly to mmap() implementations.

In 'overwrite' mode, also known as 'flight recorder' mode, writes
continuously cycle around the buffer and will never fail, but will
unconditionally overwrite old data regardless of whether it's actually
been consumed.  In no-overwrite mode, writes will fail, i.e., data will
be lost, if the number of unconsumed sub-buffers equals the total
number of sub-buffers in the channel.  It should be clear that if
there is no consumer or if the consumer can't consume sub-buffers fast
enough, data will be lost in either case; the only difference is
whether data is lost from the beginning or the end of a buffer.

As explained above, a relay channel is made of up one or more
per-cpu channel buffers, each implemented as a circular buffer
subdivided into one or more sub-buffers.  Messages are written into
the current sub-buffer of the channel's current per-cpu buffer via the
write functions described below.  Whenever a message can't fit into
the current sub-buffer, because there's no room left for it, the
client is notified via the subbuf_start() callback that a switch to a
new sub-buffer is about to occur.  The client uses this callback to 1)
initialize the next sub-buffer if appropriate 2) finalize the previous
sub-buffer if appropriate and 3) return a boolean value indicating
whether or not to actually move on to the next sub-buffer.

To implement 'no-overwrite' mode, the userspace client provides
an implementation of the subbuf_start() callback something like the
following::

    static int subbuf_start(struct rchan_buf *buf,
                            void *subbuf,
                            void *prev_subbuf,
                            unsigned int prev_padding)
    {
            if (prev_subbuf)
                    *((unsigned *)prev_subbuf) = prev_padding;

            if (relay_buf_full(buf))
                    return 0;

            subbuf_start_reserve(buf, sizeof(unsigned int));

            return 1;
    }

If the current buffer is full, i.e., all sub-buffers remain unconsumed,
the callback returns 0 to indicate that the buffer switch should not
occur yet, i.e., until the consumer has had a chance to read the
current set of ready sub-buffers.  For the relay_buf_full() function
to make sense, the consumer is responsible for notifying the relay
interface when sub-buffers have been consumed via
relay_subbufs_consumed().  Any subsequent attempts to write into the
buffer will again invoke the subbuf_start() callback with the same
parameters; only when the consumer has consumed one or more of the
ready sub-buffers will relay_buf_full() return 0, in which case the
buffer switch can continue.

The implementation of the subbuf_start() callback for 'overwrite' mode
would be very similar::

    static int subbuf_start(struct rchan_buf *buf,
                            void *subbuf,
                            void *prev_subbuf,
                            size_t prev_padding)
    {
            if (prev_subbuf)
                    *((unsigned *)prev_subbuf) = prev_padding;

            subbuf_start_reserve(buf, sizeof(unsigned int));

            return 1;
    }

In this case, the relay_buf_full() check is meaningless and the
callback always returns 1, causing the buffer switch to occur
unconditionally.  It's also meaningless for the client to use the
relay_subbufs_consumed() function in this mode, as it's never
consulted.

The default subbuf_start() implementation, used if the client doesn't
define any callbacks, or doesn't define the subbuf_start() callback,
implements the simplest possible 'no-overwrite' mode, i.e., it does
nothing but return 0.

Header information can be reserved at the beginning of each sub-buffer
by calling the subbuf_start_reserve() helper function from within the
subbuf_start() callback.  This reserved area can be used to store
whatever information the client wants.  In the example above, room is
reserved in each sub-buffer to store the padding count for that
sub-buffer.  This is filled in for the previous sub-buffer in the
subbuf_start() implementation; the padding value for the previous
sub-buffer is passed into the subbuf_start() callback along with a
pointer to the previous sub-buffer, since the padding value isn't
known until a sub-buffer is filled.  The subbuf_start() callback is
also called for the first sub-buffer when the channel is opened, to
give the client a chance to reserve space in it.  In this case the
previous sub-buffer pointer passed into the callback will be NULL, so
the client should check the value of the prev_subbuf pointer before
writing into the previous sub-buffer.

쓰기·예약·종료 API

412-451

커널 클라이언트는 `relay_write()` 또는 `__relay_write()`로 현재 CPU의 channel buffer에 쓴다. `relay_write()`는 `local_irqsave()`로 buffer를 보호하므로 interrupt context에서 기록할 가능성이 있으면 사용한다. Interrupt context에서 절대 쓰지 않는다면 preemption만 비활성화하는 `__relay_write()`를 사용할 수 있다.

두 함수는 반환값이 없다. 빠른 logging 경로에서 실패값을 검사하지 않는다는 설계이며, no-overwrite 모드에서 buffer가 가득 찬 경우를 제외하면 항상 성공한다고 가정한다. 이 실패는 `subbuf_start()`에서 `relay_buf_full()`을 호출해 감지할 수 있다.

`relay_reserve()`는 나중에 직접 쓸 channel buffer slot을 예약한다. 임시 buffer에 데이터를 staging하지 않고 바로 기록하려는 애플리케이션에 적합하다. 예약 직후 실제 쓰기가 일어나지 않을 수 있으므로, sub-buffer 예약 영역이나 별도 배열에 실제로 쓴 byte 수를 기록할 수 있다.

예약과 쓰기가 분리되고 실제 쓰기가 클라이언트 통제 아래 있으므로 `relay_reserve()`는 buffer를 전혀 보호하지 않는다. 적절한 동기화는 클라이언트 책임이다. Relay-apps tarball의 `reserve` 예제가 구현 방식을 보여 준다.

사용을 마치면 `relay_close()`를 호출한다. 어느 channel buffer에도 참조가 남지 않을 때 channel과 관련 buffer가 파괴된다. 닫기 전 `relay_flush()`로 모든 channel buffer의 sub-buffer 전환을 강제하면 마지막 sub-buffer를 마무리하고 처리할 수 있다.

쓰기 API 보호 수준
API내부 보호사용 조건
`relay_write()``local_irqsave()`Interrupt context 가능
`__relay_write()`Preemption 비활성Interrupt context 없음 보장
`relay_reserve()`보호 없음클라이언트가 locking과 실제 byte 수 관리

호출 context와 직접 기록 여부에 맞춰 동기화 수준을 선택한다.

Writing to a channel
--------------------

Kernel clients write data into the current cpu's channel buffer using
relay_write() or __relay_write().  relay_write() is the main logging
function - it uses local_irqsave() to protect the buffer and should be
used if you might be logging from interrupt context.  If you know
you'll never be logging from interrupt context, you can use
__relay_write(), which only disables preemption.  These functions
don't return a value, so you can't determine whether or not they
failed - the assumption is that you wouldn't want to check a return
value in the fast logging path anyway, and that they'll always succeed
unless the buffer is full and no-overwrite mode is being used, in
which case you can detect a failed write in the subbuf_start()
callback by calling the relay_buf_full() helper function.

relay_reserve() is used to reserve a slot in a channel buffer which
can be written to later.  This would typically be used in applications
that need to write directly into a channel buffer without having to
stage data in a temporary buffer beforehand.  Because the actual write
may not happen immediately after the slot is reserved, applications
using relay_reserve() can keep a count of the number of bytes actually
written, either in space reserved in the sub-buffers themselves or as
a separate array.  See the 'reserve' example in the relay-apps tarball
at http://relayfs.sourceforge.net for an example of how this can be
done.  Because the write is under control of the client and is
separated from the reserve, relay_reserve() doesn't protect the buffer
at all - it's up to the client to provide the appropriate
synchronization when using relay_reserve().

Closing a channel
-----------------

The client calls relay_close() when it's finished using the channel.
The channel and its associated buffers are destroyed when there are no
longer any references to any of the channel buffers.  relay_flush()
forces a sub-buffer switch on all the channel buffers, and can be used
to finalize and process the last sub-buffers before the channel is
closed.

Reset·mapping callback·자료와 기여자

452-491

애플리케이션이 사용할 때마다 channel을 열고 닫지 않고 재사용하려면 `relay_reset()`을 쓸 수 있다. Channel buffer 메모리를 다시 할당하거나 기존 mapping을 파괴하지 않고 초기 상태로 되돌린다. 현재 channel에 쓰는 중이 아닐 때만 호출해야 안전하다.

`buf_mapped()`는 userspace가 channel buffer를 mmap할 때, `buf_unmapped()`는 mapping을 해제할 때 호출된다. 커널 클라이언트는 이 알림을 channel logging 활성화·비활성화 같은 동작의 trigger로 사용할 수 있다.

뉴스·예제 코드·메일링 리스트 등 자료는 relay 인터페이스 홈페이지 `http://relayfs.sourceforge.net`에서 제공한다.

Relay의 아이디어와 명세는 tracing 논의에서 나왔다. 원문은 Michel Dagenais, Richard Moore, Bob Wisniewski, Karim Yaghmour, Tom Zanussi를 기여자로 기록하며, 유용한 제안과 bug report를 제공한 Hubertus Franke에게도 감사를 전한다.

재사용 가능한 channel 수명
Channel open·buffer와 mapping 생성`buf_mapped()`로 logging 활성화 가능기록 중단과 안전 상태 확인`relay_reset()`으로 초기 상태 복원`buf_unmapped()` 또는 마지막 close에서 정리

Mapping과 buffer 할당을 유지하면서 기록 세션만 다시 시작할 수 있다.

Misc
----

Some applications may want to keep a channel around and re-use it
rather than open and close a new channel for each use.  relay_reset()
can be used for this purpose - it resets a channel to its initial
state without reallocating channel buffer memory or destroying
existing mappings.  It should however only be called when it's safe to
do so, i.e., when the channel isn't currently being written to.

Finally, there are a couple of utility callbacks that can be used for
different purposes.  buf_mapped() is called whenever a channel buffer
is mmapped from user space and buf_unmapped() is called when it's
unmapped.  The client can use this notification to trigger actions
within the kernel application, such as enabling/disabling logging to
the channel.


Resources
=========

For news, example code, mailing list, etc. see the relay interface homepage:

    http://relayfs.sourceforge.net


Credits
=======

The ideas and specs for the relay interface came about as a result of
discussions on tracing involving the following:

Michel Dagenais                <michel.dagenais@polymtl.ca>
Richard Moore                <richardj_moore@uk.ibm.com>
Bob Wisniewski                <bob@watson.ibm.com>
Karim Yaghmour                <karim@opersys.com>
Tom Zanussi                <zanussi@us.ibm.com>

Also thanks to Hubertus Franke for a lot of useful suggestions and bug
reports.