ethdev: fix max Rx packet length
[dpdk.git] / doc / guides / sample_app_ug / performance_thread.rst
1 ..  SPDX-License-Identifier: BSD-3-Clause
2     Copyright(c) 2015 Intel Corporation.
3
4 Performance Thread Sample Application
5 =====================================
6
7 The performance thread sample application is a derivative of the standard L3
8 forwarding application that demonstrates different threading models.
9
10 Overview
11 --------
12 For a general description of the L3 forwarding applications capabilities
13 please refer to the documentation of the standard application in
14 :doc:`l3_forward`.
15
16 The performance thread sample application differs from the standard L3
17 forwarding example in that it divides the TX and RX processing between
18 different threads, and makes it possible to assign individual threads to
19 different cores.
20
21 Three threading models are considered:
22
23 #. When there is one EAL thread per physical core.
24 #. When there are multiple EAL threads per physical core.
25 #. When there are multiple lightweight threads per EAL thread.
26
27 Since DPDK release 2.0 it is possible to launch applications using the
28 ``--lcores`` EAL parameter, specifying cpu-sets for a physical core. With the
29 performance thread sample application its is now also possible to assign
30 individual RX and TX functions to different cores.
31
32 As an alternative to dividing the L3 forwarding work between different EAL
33 threads the performance thread sample introduces the possibility to run the
34 application threads as lightweight threads (L-threads) within one or
35 more EAL threads.
36
37 In order to facilitate this threading model the example includes a primitive
38 cooperative scheduler (L-thread) subsystem. More details of the L-thread
39 subsystem can be found in :ref:`lthread_subsystem`.
40
41 **Note:** Whilst theoretically possible it is not anticipated that multiple
42 L-thread schedulers would be run on the same physical core, this mode of
43 operation should not be expected to yield useful performance and is considered
44 invalid.
45
46 Compiling the Application
47 -------------------------
48
49 To compile the sample application see :doc:`compiling`.
50
51 The application is located in the `performance-thread/l3fwd-thread` sub-directory.
52
53 Running the Application
54 -----------------------
55
56 The application has a number of command line options::
57
58     ./<build_dir>/examples/dpdk-l3fwd-thread [EAL options] --
59         -p PORTMASK [-P]
60         --rx(port,queue,lcore,thread)[,(port,queue,lcore,thread)]
61         --tx(lcore,thread)[,(lcore,thread)]
62         [--max-pkt-len PKTLEN]  [--no-numa]
63         [--hash-entry-num] [--ipv6] [--no-lthreads] [--stat-lcore lcore]
64         [--parse-ptype]
65
66 Where:
67
68 * ``-p PORTMASK``: Hexadecimal bitmask of ports to configure.
69
70 * ``-P``: optional, sets all ports to promiscuous mode so that packets are
71   accepted regardless of the packet's Ethernet MAC destination address.
72   Without this option, only packets with the Ethernet MAC destination address
73   set to the Ethernet address of the port are accepted.
74
75 * ``--rx (port,queue,lcore,thread)[,(port,queue,lcore,thread)]``: the list of
76   NIC RX ports and queues handled by the RX lcores and threads. The parameters
77   are explained below.
78
79 * ``--tx (lcore,thread)[,(lcore,thread)]``: the list of TX threads identifying
80   the lcore the thread runs on, and the id of RX thread with which it is
81   associated. The parameters are explained below.
82
83 * ``--max-pkt-len``: optional, maximum packet length in decimal (64-9600).
84
85 * ``--no-numa``: optional, disables numa awareness.
86
87 * ``--hash-entry-num``: optional, specifies the hash entry number in hex to be
88   setup.
89
90 * ``--ipv6``: optional, set it if running ipv6 packets.
91
92 * ``--no-lthreads``: optional, disables l-thread model and uses EAL threading
93   model. See below.
94
95 * ``--stat-lcore``: optional, run CPU load stats collector on the specified
96   lcore.
97
98 * ``--parse-ptype:`` optional, set to use software to analyze packet type.
99   Without this option, hardware will check the packet type.
100
101 The parameters of the ``--rx`` and ``--tx`` options are:
102
103 * ``--rx`` parameters
104
105    .. _table_l3fwd_rx_parameters:
106
107    +--------+------------------------------------------------------+
108    | port   | RX port                                              |
109    +--------+------------------------------------------------------+
110    | queue  | RX queue that will be read on the specified RX port  |
111    +--------+------------------------------------------------------+
112    | lcore  | Core to use for the thread                           |
113    +--------+------------------------------------------------------+
114    | thread | Thread id (continuously from 0 to N)                 |
115    +--------+------------------------------------------------------+
116
117
118 * ``--tx`` parameters
119
120    .. _table_l3fwd_tx_parameters:
121
122    +--------+------------------------------------------------------+
123    | lcore  | Core to use for L3 route match and transmit          |
124    +--------+------------------------------------------------------+
125    | thread | Id of RX thread to be associated with this TX thread |
126    +--------+------------------------------------------------------+
127
128 The ``l3fwd-thread`` application allows you to start packet processing in two
129 threading models: L-Threads (default) and EAL Threads (when the
130 ``--no-lthreads`` parameter is used). For consistency all parameters are used
131 in the same way for both models.
132
133
134 Running with L-threads
135 ~~~~~~~~~~~~~~~~~~~~~~
136
137 When the L-thread model is used (default option), lcore and thread parameters
138 in ``--rx/--tx`` are used to affinitize threads to the selected scheduler.
139
140 For example, the following places every l-thread on different lcores::
141
142    dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
143                 --rx="(0,0,0,0)(1,0,1,1)" \
144                 --tx="(2,0)(3,1)"
145
146 The following places RX l-threads on lcore 0 and TX l-threads on lcore 1 and 2
147 and so on::
148
149    dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
150                 --rx="(0,0,0,0)(1,0,0,1)" \
151                 --tx="(1,0)(2,1)"
152
153
154 Running with EAL threads
155 ~~~~~~~~~~~~~~~~~~~~~~~~
156
157 When the ``--no-lthreads`` parameter is used, the L-threading model is turned
158 off and EAL threads are used for all processing. EAL threads are enumerated in
159 the same way as L-threads, but the ``--lcores`` EAL parameter is used to
160 affinitize threads to the selected cpu-set (scheduler). Thus it is possible to
161 place every RX and TX thread on different lcores.
162
163 For example, the following places every EAL thread on different lcores::
164
165    dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
166                 --rx="(0,0,0,0)(1,0,1,1)" \
167                 --tx="(2,0)(3,1)" \
168                 --no-lthreads
169
170
171 To affinitize two or more EAL threads to one cpu-set, the EAL ``--lcores``
172 parameter is used.
173
174 The following places RX EAL threads on lcore 0 and TX EAL threads on lcore 1
175 and 2 and so on::
176
177    dpdk-l3fwd-thread -l 0-7 -n 2 --lcores="(0,1)@0,(2,3)@1" -- -P -p 3 \
178                 --rx="(0,0,0,0)(1,0,1,1)" \
179                 --tx="(2,0)(3,1)" \
180                 --no-lthreads
181
182
183 Examples
184 ~~~~~~~~
185
186 For selected scenarios the command line configuration of the application for L-threads
187 and its corresponding EAL threads command line can be realized as follows:
188
189 a) Start every thread on different scheduler (1:1)::
190
191       dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
192                    --rx="(0,0,0,0)(1,0,1,1)" \
193                    --tx="(2,0)(3,1)"
194
195    EAL thread equivalent::
196
197       dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
198                    --rx="(0,0,0,0)(1,0,1,1)" \
199                    --tx="(2,0)(3,1)" \
200                    --no-lthreads
201
202 b) Start all threads on one core (N:1).
203
204    Start 4 L-threads on lcore 0::
205
206       dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
207                    --rx="(0,0,0,0)(1,0,0,1)" \
208                    --tx="(0,0)(0,1)"
209
210    Start 4 EAL threads on cpu-set 0::
211
212       dpdk-l3fwd-thread -l 0-7 -n 2 --lcores="(0-3)@0" -- -P -p 3 \
213                    --rx="(0,0,0,0)(1,0,0,1)" \
214                    --tx="(2,0)(3,1)" \
215                    --no-lthreads
216
217 c) Start threads on different cores (N:M).
218
219    Start 2 L-threads for RX on lcore 0, and 2 L-threads for TX on lcore 1::
220
221       dpdk-l3fwd-thread -l 0-7 -n 2 -- -P -p 3 \
222                    --rx="(0,0,0,0)(1,0,0,1)" \
223                    --tx="(1,0)(1,1)"
224
225    Start 2 EAL threads for RX on cpu-set 0, and 2 EAL threads for TX on
226    cpu-set 1::
227
228       dpdk-l3fwd-thread -l 0-7 -n 2 --lcores="(0-1)@0,(2-3)@1" -- -P -p 3 \
229                    --rx="(0,0,0,0)(1,0,1,1)" \
230                    --tx="(2,0)(3,1)" \
231                    --no-lthreads
232
233 Explanation
234 -----------
235
236 To a great extent the sample application differs little from the standard L3
237 forwarding application, and readers are advised to familiarize themselves with
238 the material covered in the :doc:`l3_forward` documentation before proceeding.
239
240 The following explanation is focused on the way threading is handled in the
241 performance thread example.
242
243
244 Mode of operation with EAL threads
245 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
246
247 The performance thread sample application has split the RX and TX functionality
248 into two different threads, and the RX and TX threads are
249 interconnected via software rings. With respect to these rings the RX threads
250 are producers and the TX threads are consumers.
251
252 On initialization the TX and RX threads are started according to the command
253 line parameters.
254
255 The RX threads poll the network interface queues and post received packets to a
256 TX thread via a corresponding software ring.
257
258 The TX threads poll software rings, perform the L3 forwarding hash/LPM match,
259 and assemble packet bursts before performing burst transmit on the network
260 interface.
261
262 As with the standard L3 forward application, burst draining of residual packets
263 is performed periodically with the period calculated from elapsed time using
264 the timestamps counter.
265
266 The diagram below illustrates a case with two RX threads and three TX threads.
267
268 .. _figure_performance_thread_1:
269
270 .. figure:: img/performance_thread_1.*
271
272
273 Mode of operation with L-threads
274 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275
276 Like the EAL thread configuration the application has split the RX and TX
277 functionality into different threads, and the pairs of RX and TX threads are
278 interconnected via software rings.
279
280 On initialization an L-thread scheduler is started on every EAL thread. On all
281 but the main EAL thread only a dummy L-thread is initially started.
282 The L-thread started on the main EAL thread then spawns other L-threads on
283 different L-thread schedulers according the command line parameters.
284
285 The RX threads poll the network interface queues and post received packets
286 to a TX thread via the corresponding software ring.
287
288 The ring interface is augmented by means of an L-thread condition variable that
289 enables the TX thread to be suspended when the TX ring is empty. The RX thread
290 signals the condition whenever it posts to the TX ring, causing the TX thread
291 to be resumed.
292
293 Additionally the TX L-thread spawns a worker L-thread to take care of
294 polling the software rings, whilst it handles burst draining of the transmit
295 buffer.
296
297 The worker threads poll the software rings, perform L3 route lookup and
298 assemble packet bursts. If the TX ring is empty the worker thread suspends
299 itself by waiting on the condition variable associated with the ring.
300
301 Burst draining of residual packets, less than the burst size, is performed by
302 the TX thread which sleeps (using an L-thread sleep function) and resumes
303 periodically to flush the TX buffer.
304
305 This design means that L-threads that have no work, can yield the CPU to other
306 L-threads and avoid having to constantly poll the software rings.
307
308 The diagram below illustrates a case with two RX threads and three TX functions
309 (each comprising a thread that processes forwarding and a thread that
310 periodically drains the output buffer of residual packets).
311
312 .. _figure_performance_thread_2:
313
314 .. figure:: img/performance_thread_2.*
315
316
317 CPU load statistics
318 ~~~~~~~~~~~~~~~~~~~
319
320 It is possible to display statistics showing estimated CPU load on each core.
321 The statistics indicate the percentage of CPU time spent: processing
322 received packets (forwarding), polling queues/rings (waiting for work),
323 and doing any other processing (context switch and other overhead).
324
325 When enabled statistics are gathered by having the application threads set and
326 clear flags when they enter and exit pertinent code sections. The flags are
327 then sampled in real time by a statistics collector thread running on another
328 core. This thread displays the data in real time on the console.
329
330 This feature is enabled by designating a statistics collector core, using the
331 ``--stat-lcore`` parameter.
332
333
334 .. _lthread_subsystem:
335
336 The L-thread subsystem
337 ----------------------
338
339 The L-thread subsystem resides in the examples/performance-thread/common
340 directory and is built and linked automatically when building the
341 ``l3fwd-thread`` example.
342
343 The subsystem provides a simple cooperative scheduler to enable arbitrary
344 functions to run as cooperative threads within a single EAL thread.
345 The subsystem provides a pthread like API that is intended to assist in
346 reuse of legacy code written for POSIX pthreads.
347
348 The following sections provide some detail on the features, constraints,
349 performance and porting considerations when using L-threads.
350
351
352 .. _comparison_between_lthreads_and_pthreads:
353
354 Comparison between L-threads and POSIX pthreads
355 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
356
357 The fundamental difference between the L-thread and pthread models is the
358 way in which threads are scheduled. The simplest way to think about this is to
359 consider the case of a processor with a single CPU. To run multiple threads
360 on a single CPU, the scheduler must frequently switch between the threads,
361 in order that each thread is able to make timely progress.
362 This is the basis of any multitasking operating system.
363
364 This section explores the differences between the pthread model and the
365 L-thread model as implemented in the provided L-thread subsystem. If needed a
366 theoretical discussion of preemptive vs cooperative multi-threading can be
367 found in any good text on operating system design.
368
369
370 Scheduling and context switching
371 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
372
373 The POSIX pthread library provides an application programming interface to
374 create and synchronize threads. Scheduling policy is determined by the host OS,
375 and may be configurable. The OS may use sophisticated rules to determine which
376 thread should be run next, threads may suspend themselves or make other threads
377 ready, and the scheduler may employ a time slice giving each thread a maximum
378 time quantum after which it will be preempted in favor of another thread that
379 is ready to run. To complicate matters further threads may be assigned
380 different scheduling priorities.
381
382 By contrast the L-thread subsystem is considerably simpler. Logically the
383 L-thread scheduler performs the same multiplexing function for L-threads
384 within a single pthread as the OS scheduler does for pthreads within an
385 application process. The L-thread scheduler is simply the main loop of a
386 pthread, and in so far as the host OS is concerned it is a regular pthread
387 just like any other. The host OS is oblivious about the existence of and
388 not at all involved in the scheduling of L-threads.
389
390 The other and most significant difference between the two models is that
391 L-threads are scheduled cooperatively. L-threads cannot not preempt each
392 other, nor can the L-thread scheduler preempt a running L-thread (i.e.
393 there is no time slicing). The consequence is that programs implemented with
394 L-threads must possess frequent rescheduling points, meaning that they must
395 explicitly and of their own volition return to the scheduler at frequent
396 intervals, in order to allow other L-threads an opportunity to proceed.
397
398 In both models switching between threads requires that the current CPU
399 context is saved and a new context (belonging to the next thread ready to run)
400 is restored. With pthreads this context switching is handled transparently
401 and the set of CPU registers that must be preserved between context switches
402 is as per an interrupt handler.
403
404 An L-thread context switch is achieved by the thread itself making a function
405 call to the L-thread scheduler. Thus it is only necessary to preserve the
406 callee registers. The caller is responsible to save and restore any other
407 registers it is using before a function call, and restore them on return,
408 and this is handled by the compiler. For ``X86_64`` on both Linux and BSD the
409 System V calling convention is used, this defines registers RSP, RBP, and
410 R12-R15 as callee-save registers (for more detailed discussion a good reference
411 is `X86 Calling Conventions <https://en.wikipedia.org/wiki/X86_calling_conventions>`_).
412
413 Taking advantage of this, and due to the absence of preemption, an L-thread
414 context switch is achieved with less than 20 load/store instructions.
415
416 The scheduling policy for L-threads is fixed, there is no prioritization of
417 L-threads, all L-threads are equal and scheduling is based on a FIFO
418 ready queue.
419
420 An L-thread is a struct containing the CPU context of the thread
421 (saved on context switch) and other useful items. The ready queue contains
422 pointers to threads that are ready to run. The L-thread scheduler is a simple
423 loop that polls the ready queue, reads from it the next thread ready to run,
424 which it resumes by saving the current context (the current position in the
425 scheduler loop) and restoring the context of the next thread from its thread
426 struct. Thus an L-thread is always resumed at the last place it yielded.
427
428 A well behaved L-thread will call the context switch regularly (at least once
429 in its main loop) thus returning to the scheduler's own main loop. Yielding
430 inserts the current thread at the back of the ready queue, and the process of
431 servicing the ready queue is repeated, thus the system runs by flipping back
432 and forth the between L-threads and scheduler loop.
433
434 In the case of pthreads, the preemptive scheduling, time slicing, and support
435 for thread prioritization means that progress is normally possible for any
436 thread that is ready to run. This comes at the price of a relatively heavier
437 context switch and scheduling overhead.
438
439 With L-threads the progress of any particular thread is determined by the
440 frequency of rescheduling opportunities in the other L-threads. This means that
441 an errant L-thread monopolizing the CPU might cause scheduling of other threads
442 to be stalled. Due to the lower cost of context switching, however, voluntary
443 rescheduling to ensure progress of other threads, if managed sensibly, is not
444 a prohibitive overhead, and overall performance can exceed that of an
445 application using pthreads.
446
447
448 Mutual exclusion
449 ^^^^^^^^^^^^^^^^
450
451 With pthreads preemption means that threads that share data must observe
452 some form of mutual exclusion protocol.
453
454 The fact that L-threads cannot preempt each other means that in many cases
455 mutual exclusion devices can be completely avoided.
456
457 Locking to protect shared data can be a significant bottleneck in
458 multi-threaded applications so a carefully designed cooperatively scheduled
459 program can enjoy significant performance advantages.
460
461 So far we have considered only the simplistic case of a single core CPU,
462 when multiple CPUs are considered things are somewhat more complex.
463
464 First of all it is inevitable that there must be multiple L-thread schedulers,
465 one running on each EAL thread. So long as these schedulers remain isolated
466 from each other the above assertions about the potential advantages of
467 cooperative scheduling hold true.
468
469 A configuration with isolated cooperative schedulers is less flexible than the
470 pthread model where threads can be affinitized to run on any CPU. With isolated
471 schedulers scaling of applications to utilize fewer or more CPUs according to
472 system demand is very difficult to achieve.
473
474 The L-thread subsystem makes it possible for L-threads to migrate between
475 schedulers running on different CPUs. Needless to say if the migration means
476 that threads that share data end up running on different CPUs then this will
477 introduce the need for some kind of mutual exclusion system.
478
479 Of course ``rte_ring`` software rings can always be used to interconnect
480 threads running on different cores, however to protect other kinds of shared
481 data structures, lock free constructs or else explicit locking will be
482 required. This is a consideration for the application design.
483
484 In support of this extended functionality, the L-thread subsystem implements
485 thread safe mutexes and condition variables.
486
487 The cost of affinitizing and of condition variable signaling is significantly
488 lower than the equivalent pthread operations, and so applications using these
489 features will see a performance benefit.
490
491
492 Thread local storage
493 ^^^^^^^^^^^^^^^^^^^^
494
495 As with applications written for pthreads an application written for L-threads
496 can take advantage of thread local storage, in this case local to an L-thread.
497 An application may save and retrieve a single pointer to application data in
498 the L-thread struct.
499
500 For legacy and backward compatibility reasons two alternative methods are also
501 offered, the first is modeled directly on the pthread get/set specific APIs,
502 the second approach is modeled on the ``RTE_PER_LCORE`` macros, whereby
503 ``PER_LTHREAD`` macros are introduced, in both cases the storage is local to
504 the L-thread.
505
506
507 .. _constraints_and_performance_implications:
508
509 Constraints and performance implications when using L-threads
510 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
511
512
513 .. _API_compatibility:
514
515 API compatibility
516 ^^^^^^^^^^^^^^^^^
517
518 The L-thread subsystem provides a set of functions that are logically equivalent
519 to the corresponding functions offered by the POSIX pthread library, however not
520 all pthread functions have a corresponding L-thread equivalent, and not all
521 features available to pthreads are implemented for L-threads.
522
523 The pthread library offers considerable flexibility via programmable attributes
524 that can be associated with threads, mutexes, and condition variables.
525
526 By contrast the L-thread subsystem has fixed functionality, the scheduler policy
527 cannot be varied, and L-threads cannot be prioritized. There are no variable
528 attributes associated with any L-thread objects. L-threads, mutexes and
529 conditional variables, all have fixed functionality. (Note: reserved parameters
530 are included in the APIs to facilitate possible future support for attributes).
531
532 The table below lists the pthread and equivalent L-thread APIs with notes on
533 differences and/or constraints. Where there is no L-thread entry in the table,
534 then the L-thread subsystem provides no equivalent function.
535
536 .. _table_lthread_pthread:
537
538 .. table:: Pthread and equivalent L-thread APIs.
539
540    +----------------------------+------------------------+-------------------+
541    | **Pthread function**       | **L-thread function**  | **Notes**         |
542    +============================+========================+===================+
543    | pthread_barrier_destroy    |                        |                   |
544    +----------------------------+------------------------+-------------------+
545    | pthread_barrier_init       |                        |                   |
546    +----------------------------+------------------------+-------------------+
547    | pthread_barrier_wait       |                        |                   |
548    +----------------------------+------------------------+-------------------+
549    | pthread_cond_broadcast     | lthread_cond_broadcast | See note 1        |
550    +----------------------------+------------------------+-------------------+
551    | pthread_cond_destroy       | lthread_cond_destroy   |                   |
552    +----------------------------+------------------------+-------------------+
553    | pthread_cond_init          | lthread_cond_init      |                   |
554    +----------------------------+------------------------+-------------------+
555    | pthread_cond_signal        | lthread_cond_signal    | See note 1        |
556    +----------------------------+------------------------+-------------------+
557    | pthread_cond_timedwait     |                        |                   |
558    +----------------------------+------------------------+-------------------+
559    | pthread_cond_wait          | lthread_cond_wait      | See note 5        |
560    +----------------------------+------------------------+-------------------+
561    | pthread_create             | lthread_create         | See notes 2, 3    |
562    +----------------------------+------------------------+-------------------+
563    | pthread_detach             | lthread_detach         | See note 4        |
564    +----------------------------+------------------------+-------------------+
565    | pthread_equal              |                        |                   |
566    +----------------------------+------------------------+-------------------+
567    | pthread_exit               | lthread_exit           |                   |
568    +----------------------------+------------------------+-------------------+
569    | pthread_getspecific        | lthread_getspecific    |                   |
570    +----------------------------+------------------------+-------------------+
571    | pthread_getcpuclockid      |                        |                   |
572    +----------------------------+------------------------+-------------------+
573    | pthread_join               | lthread_join           |                   |
574    +----------------------------+------------------------+-------------------+
575    | pthread_key_create         | lthread_key_create     |                   |
576    +----------------------------+------------------------+-------------------+
577    | pthread_key_delete         | lthread_key_delete     |                   |
578    +----------------------------+------------------------+-------------------+
579    | pthread_mutex_destroy      | lthread_mutex_destroy  |                   |
580    +----------------------------+------------------------+-------------------+
581    | pthread_mutex_init         | lthread_mutex_init     |                   |
582    +----------------------------+------------------------+-------------------+
583    | pthread_mutex_lock         | lthread_mutex_lock     | See note 6        |
584    +----------------------------+------------------------+-------------------+
585    | pthread_mutex_trylock      | lthread_mutex_trylock  | See note 6        |
586    +----------------------------+------------------------+-------------------+
587    | pthread_mutex_timedlock    |                        |                   |
588    +----------------------------+------------------------+-------------------+
589    | pthread_mutex_unlock       | lthread_mutex_unlock   |                   |
590    +----------------------------+------------------------+-------------------+
591    | pthread_once               |                        |                   |
592    +----------------------------+------------------------+-------------------+
593    | pthread_rwlock_destroy     |                        |                   |
594    +----------------------------+------------------------+-------------------+
595    | pthread_rwlock_init        |                        |                   |
596    +----------------------------+------------------------+-------------------+
597    | pthread_rwlock_rdlock      |                        |                   |
598    +----------------------------+------------------------+-------------------+
599    | pthread_rwlock_timedrdlock |                        |                   |
600    +----------------------------+------------------------+-------------------+
601    | pthread_rwlock_timedwrlock |                        |                   |
602    +----------------------------+------------------------+-------------------+
603    | pthread_rwlock_tryrdlock   |                        |                   |
604    +----------------------------+------------------------+-------------------+
605    | pthread_rwlock_trywrlock   |                        |                   |
606    +----------------------------+------------------------+-------------------+
607    | pthread_rwlock_unlock      |                        |                   |
608    +----------------------------+------------------------+-------------------+
609    | pthread_rwlock_wrlock      |                        |                   |
610    +----------------------------+------------------------+-------------------+
611    | pthread_self               | lthread_current        |                   |
612    +----------------------------+------------------------+-------------------+
613    | pthread_setspecific        | lthread_setspecific    |                   |
614    +----------------------------+------------------------+-------------------+
615    | pthread_spin_init          |                        | See note 10       |
616    +----------------------------+------------------------+-------------------+
617    | pthread_spin_destroy       |                        | See note 10       |
618    +----------------------------+------------------------+-------------------+
619    | pthread_spin_lock          |                        | See note 10       |
620    +----------------------------+------------------------+-------------------+
621    | pthread_spin_trylock       |                        | See note 10       |
622    +----------------------------+------------------------+-------------------+
623    | pthread_spin_unlock        |                        | See note 10       |
624    +----------------------------+------------------------+-------------------+
625    | pthread_cancel             | lthread_cancel         |                   |
626    +----------------------------+------------------------+-------------------+
627    | pthread_setcancelstate     |                        |                   |
628    +----------------------------+------------------------+-------------------+
629    | pthread_setcanceltype      |                        |                   |
630    +----------------------------+------------------------+-------------------+
631    | pthread_testcancel         |                        |                   |
632    +----------------------------+------------------------+-------------------+
633    | pthread_getschedparam      |                        |                   |
634    +----------------------------+------------------------+-------------------+
635    | pthread_setschedparam      |                        |                   |
636    +----------------------------+------------------------+-------------------+
637    | pthread_yield              | lthread_yield          | See note 7        |
638    +----------------------------+------------------------+-------------------+
639    | pthread_setaffinity_np     | lthread_set_affinity   | See notes 2, 3, 8 |
640    +----------------------------+------------------------+-------------------+
641    |                            | lthread_sleep          | See note 9        |
642    +----------------------------+------------------------+-------------------+
643    |                            | lthread_sleep_clks     | See note 9        |
644    +----------------------------+------------------------+-------------------+
645
646
647 **Note 1**:
648
649 Neither lthread signal nor broadcast may be called concurrently by L-threads
650 running on different schedulers, although multiple L-threads running in the
651 same scheduler may freely perform signal or broadcast operations. L-threads
652 running on the same or different schedulers may always safely wait on a
653 condition variable.
654
655
656 **Note 2**:
657
658 Pthread attributes may be used to affinitize a pthread with a cpu-set. The
659 L-thread subsystem does not support a cpu-set. An L-thread may be affinitized
660 only with a single CPU at any time.
661
662
663 **Note 3**:
664
665 If an L-thread is intended to run on a different NUMA node than the node that
666 creates the thread then, when calling ``lthread_create()`` it is advantageous
667 to specify the destination core as a parameter of ``lthread_create()``. See
668 :ref:`memory_allocation_and_NUMA_awareness` for details.
669
670
671 **Note 4**:
672
673 An L-thread can only detach itself, and cannot detach other L-threads.
674
675
676 **Note 5**:
677
678 A wait operation on a pthread condition variable is always associated with and
679 protected by a mutex which must be owned by the thread at the time it invokes
680 ``pthread_wait()``. By contrast L-thread condition variables are thread safe
681 (for waiters) and do not use an associated mutex. Multiple L-threads (including
682 L-threads running on other schedulers) can safely wait on a L-thread condition
683 variable. As a consequence the performance of an L-thread condition variables
684 is typically an order of magnitude faster than its pthread counterpart.
685
686
687 **Note 6**:
688
689 Recursive locking is not supported with L-threads, attempts to take a lock
690 recursively will be detected and rejected.
691
692
693 **Note 7**:
694
695 ``lthread_yield()`` will save the current context, insert the current thread
696 to the back of the ready queue, and resume the next ready thread. Yielding
697 increases ready queue backlog, see :ref:`ready_queue_backlog` for more details
698 about the implications of this.
699
700
701 N.B. The context switch time as measured from immediately before the call to
702 ``lthread_yield()`` to the point at which the next ready thread is resumed,
703 can be an order of magnitude faster that the same measurement for
704 pthread_yield.
705
706
707 **Note 8**:
708
709 ``lthread_set_affinity()`` is similar to a yield apart from the fact that the
710 yielding thread is inserted into a peer ready queue of another scheduler.
711 The peer ready queue is actually a separate thread safe queue, which means that
712 threads appearing in the peer ready queue can jump any backlog in the local
713 ready queue on the destination scheduler.
714
715 The context switch time as measured from the time just before the call to
716 ``lthread_set_affinity()`` to just after the same thread is resumed on the new
717 scheduler can be orders of magnitude faster than the same measurement for
718 ``pthread_setaffinity_np()``.
719
720
721 **Note 9**:
722
723 Although there is no ``pthread_sleep()`` function, ``lthread_sleep()`` and
724 ``lthread_sleep_clks()`` can be used wherever ``sleep()``, ``usleep()`` or
725 ``nanosleep()`` might ordinarily be used. The L-thread sleep functions suspend
726 the current thread, start an ``rte_timer`` and resume the thread when the
727 timer matures. The ``rte_timer_manage()`` entry point is called on every pass
728 of the scheduler loop. This means that the worst case jitter on timer expiry
729 is determined by the longest period between context switches of any running
730 L-threads.
731
732 In a synthetic test with many threads sleeping and resuming then the measured
733 jitter is typically orders of magnitude lower than the same measurement made
734 for ``nanosleep()``.
735
736
737 **Note 10**:
738
739 Spin locks are not provided because they are problematical in a cooperative
740 environment, see :ref:`porting_locks_and_spinlocks` for a more detailed
741 discussion on how to avoid spin locks.
742
743
744 .. _Thread_local_storage_performance:
745
746 Thread local storage
747 ^^^^^^^^^^^^^^^^^^^^
748
749 Of the three L-thread local storage options the simplest and most efficient is
750 storing a single application data pointer in the L-thread struct.
751
752 The ``PER_LTHREAD`` macros involve a run time computation to obtain the address
753 of the variable being saved/retrieved and also require that the accesses are
754 de-referenced  via a pointer. This means that code that has used
755 ``RTE_PER_LCORE`` macros being ported to L-threads might need some slight
756 adjustment (see :ref:`porting_thread_local_storage` for hints about porting
757 code that makes use of thread local storage).
758
759 The get/set specific APIs are consistent with their pthread counterparts both
760 in use and in performance.
761
762
763 .. _memory_allocation_and_NUMA_awareness:
764
765 Memory allocation and NUMA awareness
766 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
767
768 All memory allocation is from DPDK huge pages, and is NUMA aware. Each
769 scheduler maintains its own caches of objects: lthreads, their stacks, TLS,
770 mutexes and condition variables. These caches are implemented as unbounded lock
771 free MPSC queues. When objects are created they are always allocated from the
772 caches on the local core (current EAL thread).
773
774 If an L-thread has been affinitized to a different scheduler, then it can
775 always safely free resources to the caches from which they originated (because
776 the caches are MPSC queues).
777
778 If the L-thread has been affinitized to a different NUMA node then the memory
779 resources associated with it may incur longer access latency.
780
781 The commonly used pattern of setting affinity on entry to a thread after it has
782 started, means that memory allocation for both the stack and TLS will have been
783 made from caches on the NUMA node on which the threads creator is running.
784 This has the side effect that access latency will be sub-optimal after
785 affinitizing.
786
787 This side effect can be mitigated to some extent (although not completely) by
788 specifying the destination CPU as a parameter of ``lthread_create()`` this
789 causes the L-thread's stack and TLS to be allocated when it is first scheduled
790 on the destination scheduler, if the destination is a on another NUMA node it
791 results in a more optimal memory allocation.
792
793 Note that the lthread struct itself remains allocated from memory on the
794 creating node, this is unavoidable because an L-thread is known everywhere by
795 the address of this struct.
796
797
798 .. _object_cache_sizing:
799
800 Object cache sizing
801 ^^^^^^^^^^^^^^^^^^^
802
803 The per lcore object caches pre-allocate objects in bulk whenever a request to
804 allocate an object finds a cache empty. By default 100 objects are
805 pre-allocated, this is defined by ``LTHREAD_PREALLOC`` in the public API
806 header file lthread_api.h. This means that the caches constantly grow to meet
807 system demand.
808
809 In the present implementation there is no mechanism to reduce the cache sizes
810 if system demand reduces. Thus the caches will remain at their maximum extent
811 indefinitely.
812
813 A consequence of the bulk pre-allocation of objects is that every 100 (default
814 value) additional new object create operations results in a call to
815 ``rte_malloc()``. For creation of objects such as L-threads, which trigger the
816 allocation of even more objects (i.e. their stacks and TLS) then this can
817 cause outliers in scheduling performance.
818
819 If this is a problem the simplest mitigation strategy is to dimension the
820 system, by setting the bulk object pre-allocation size to some large number
821 that you do not expect to be exceeded. This means the caches will be populated
822 once only, the very first time a thread is created.
823
824
825 .. _Ready_queue_backlog:
826
827 Ready queue backlog
828 ^^^^^^^^^^^^^^^^^^^
829
830 One of the more subtle performance considerations is managing the ready queue
831 backlog. The fewer threads that are waiting in the ready queue then the faster
832 any particular thread will get serviced.
833
834 In a naive L-thread application with N L-threads simply looping and yielding,
835 this backlog will always be equal to the number of L-threads, thus the cost of
836 a yield to a particular L-thread will be N times the context switch time.
837
838 This side effect can be mitigated by arranging for threads to be suspended and
839 wait to be resumed, rather than polling for work by constantly yielding.
840 Blocking on a mutex or condition variable or even more obviously having a
841 thread sleep if it has a low frequency workload are all mechanisms by which a
842 thread can be excluded from the ready queue until it really does need to be
843 run. This can have a significant positive impact on performance.
844
845
846 .. _Initialization_and_shutdown_dependencies:
847
848 Initialization, shutdown and dependencies
849 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
850
851 The L-thread subsystem depends on DPDK for huge page allocation and depends on
852 the ``rte_timer subsystem``. The DPDK EAL initialization and
853 ``rte_timer_subsystem_init()`` **MUST** be completed before the L-thread sub
854 system can be used.
855
856 Thereafter initialization of the L-thread subsystem is largely transparent to
857 the application. Constructor functions ensure that global variables are properly
858 initialized. Other than global variables each scheduler is initialized
859 independently the first time that an L-thread is created by a particular EAL
860 thread.
861
862 If the schedulers are to be run as isolated and independent schedulers, with
863 no intention that L-threads running on different schedulers will migrate between
864 schedulers or synchronize with L-threads running on other schedulers, then
865 initialization consists simply of creating an L-thread, and then running the
866 L-thread scheduler.
867
868 If there will be interaction between L-threads running on different schedulers,
869 then it is important that the starting of schedulers on different EAL threads
870 is synchronized.
871
872 To achieve this an additional initialization step is necessary, this is simply
873 to set the number of schedulers by calling the API function
874 ``lthread_num_schedulers_set(n)``, where ``n`` is the number of EAL threads
875 that will run L-thread schedulers. Setting the number of schedulers to a
876 number greater than 0 will cause all schedulers to wait until the others have
877 started before beginning to schedule L-threads.
878
879 The L-thread scheduler is started by calling the function ``lthread_run()``
880 and should be called from the EAL thread and thus become the main loop of the
881 EAL thread.
882
883 The function ``lthread_run()``, will not return until all threads running on
884 the scheduler have exited, and the scheduler has been explicitly stopped by
885 calling ``lthread_scheduler_shutdown(lcore)`` or
886 ``lthread_scheduler_shutdown_all()``.
887
888 All these function do is tell the scheduler that it can exit when there are no
889 longer any running L-threads, neither function forces any running L-thread to
890 terminate. Any desired application shutdown behavior must be designed and
891 built into the application to ensure that L-threads complete in a timely
892 manner.
893
894 **Important Note:** It is assumed when the scheduler exits that the application
895 is terminating for good, the scheduler does not free resources before exiting
896 and running the scheduler a subsequent time will result in undefined behavior.
897
898
899 .. _porting_legacy_code_to_run_on_lthreads:
900
901 Porting legacy code to run on L-threads
902 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
903
904 Legacy code originally written for a pthread environment may be ported to
905 L-threads if the considerations about differences in scheduling policy, and
906 constraints discussed in the previous sections can be accommodated.
907
908 This section looks in more detail at some of the issues that may have to be
909 resolved when porting code.
910
911
912 .. _pthread_API_compatibility:
913
914 pthread API compatibility
915 ^^^^^^^^^^^^^^^^^^^^^^^^^
916
917 The first step is to establish exactly which pthread APIs the legacy
918 application uses, and to understand the requirements of those APIs. If there
919 are corresponding L-lthread APIs, and where the default pthread functionality
920 is used by the application then, notwithstanding the other issues discussed
921 here, it should be feasible to run the application with L-threads. If the
922 legacy code modifies the default behavior using attributes then if may be
923 necessary to make some adjustments to eliminate those requirements.
924
925
926 .. _blocking_system_calls:
927
928 Blocking system API calls
929 ^^^^^^^^^^^^^^^^^^^^^^^^^
930
931 It is important to understand what other system services the application may be
932 using, bearing in mind that in a cooperatively scheduled environment a thread
933 cannot block without stalling the scheduler and with it all other cooperative
934 threads. Any kind of blocking system call, for example file or socket IO, is a
935 potential problem, a good tool to analyze the application for this purpose is
936 the ``strace`` utility.
937
938 There are many strategies to resolve these kind of issues, each with it
939 merits. Possible solutions include:
940
941 * Adopting a polled mode of the system API concerned (if available).
942
943 * Arranging for another core to perform the function and synchronizing with
944   that core via constructs that will not block the L-thread.
945
946 * Affinitizing the thread to another scheduler devoted (as a matter of policy)
947   to handling threads wishing to make blocking calls, and then back again when
948   finished.
949
950
951 .. _porting_locks_and_spinlocks:
952
953 Locks and spinlocks
954 ^^^^^^^^^^^^^^^^^^^
955
956 Locks and spinlocks are another source of blocking behavior that for the same
957 reasons as system calls will need to be addressed.
958
959 If the application design ensures that the contending L-threads will always
960 run on the same scheduler then it its probably safe to remove locks and spin
961 locks completely.
962
963 The only exception to the above rule is if for some reason the
964 code performs any kind of context switch whilst holding the lock
965 (e.g. yield, sleep, or block on a different lock, or on a condition variable).
966 This will need to determined before deciding to eliminate a lock.
967
968 If a lock cannot be eliminated then an L-thread mutex can be substituted for
969 either kind of lock.
970
971 An L-thread blocking on an L-thread mutex will be suspended and will cause
972 another ready L-thread to be resumed, thus not blocking the scheduler. When
973 default behavior is required, it can be used as a direct replacement for a
974 pthread mutex lock.
975
976 Spin locks are typically used when lock contention is likely to be rare and
977 where the period during which the lock may be held is relatively short.
978 When the contending L-threads are running on the same scheduler then an
979 L-thread blocking on a spin lock will enter an infinite loop stopping the
980 scheduler completely (see :ref:`porting_infinite_loops` below).
981
982 If the application design ensures that contending L-threads will always run
983 on different schedulers then it might be reasonable to leave a short spin lock
984 that rarely experiences contention in place.
985
986 If after all considerations it appears that a spin lock can neither be
987 eliminated completely, replaced with an L-thread mutex, or left in place as
988 is, then an alternative is to loop on a flag, with a call to
989 ``lthread_yield()`` inside the loop (n.b. if the contending L-threads might
990 ever run on different schedulers the flag will need to be manipulated
991 atomically).
992
993 Spinning and yielding is the least preferred solution since it introduces
994 ready queue backlog (see also :ref:`ready_queue_backlog`).
995
996
997 .. _porting_sleeps_and_delays:
998
999 Sleeps and delays
1000 ^^^^^^^^^^^^^^^^^
1001
1002 Yet another kind of blocking behavior (albeit momentary) are delay functions
1003 like ``sleep()``, ``usleep()``, ``nanosleep()`` etc. All will have the
1004 consequence of stalling the L-thread scheduler and unless the delay is very
1005 short (e.g. a very short nanosleep) calls to these functions will need to be
1006 eliminated.
1007
1008 The simplest mitigation strategy is to use the L-thread sleep API functions,
1009 of which two variants exist, ``lthread_sleep()`` and ``lthread_sleep_clks()``.
1010 These functions start an rte_timer against the L-thread, suspend the L-thread
1011 and cause another ready L-thread to be resumed. The suspended L-thread is
1012 resumed when the rte_timer matures.
1013
1014
1015 .. _porting_infinite_loops:
1016
1017 Infinite loops
1018 ^^^^^^^^^^^^^^
1019
1020 Some applications have threads with loops that contain no inherent
1021 rescheduling opportunity, and rely solely on the OS time slicing to share
1022 the CPU. In a cooperative environment this will stop everything dead. These
1023 kind of loops are not hard to identify, in a debug session you will find the
1024 debugger is always stopping in the same loop.
1025
1026 The simplest solution to this kind of problem is to insert an explicit
1027 ``lthread_yield()`` or ``lthread_sleep()`` into the loop. Another solution
1028 might be to include the function performed by the loop into the execution path
1029 of some other loop that does in fact yield, if this is possible.
1030
1031
1032 .. _porting_thread_local_storage:
1033
1034 Thread local storage
1035 ^^^^^^^^^^^^^^^^^^^^
1036
1037 If the application uses thread local storage, the use case should be
1038 studied carefully.
1039
1040 In a legacy pthread application either or both the ``__thread`` prefix, or the
1041 pthread set/get specific APIs may have been used to define storage local to a
1042 pthread.
1043
1044 In some applications it may be a reasonable assumption that the data could
1045 or in fact most likely should be placed in L-thread local storage.
1046
1047 If the application (like many DPDK applications) has assumed a certain
1048 relationship between a pthread and the CPU to which it is affinitized, there
1049 is a risk that thread local storage may have been used to save some data items
1050 that are correctly logically associated with the CPU, and others items which
1051 relate to application context for the thread. Only a good understanding of the
1052 application will reveal such cases.
1053
1054 If the application requires an that an L-thread is to be able to move between
1055 schedulers then care should be taken to separate these kinds of data, into per
1056 lcore, and per L-thread storage. In this way a migrating thread will bring with
1057 it the local data it needs, and pick up the new logical core specific values
1058 from pthread local storage at its new home.
1059
1060
1061 .. _pthread_shim:
1062
1063 Pthread shim
1064 ~~~~~~~~~~~~
1065
1066 A convenient way to get something working with legacy code can be to use a
1067 shim that adapts pthread API calls to the corresponding L-thread ones.
1068 This approach will not mitigate any of the porting considerations mentioned
1069 in the previous sections, but it will reduce the amount of code churn that
1070 would otherwise been involved. It is a reasonable approach to evaluate
1071 L-threads, before investing effort in porting to the native L-thread APIs.
1072
1073
1074 Overview
1075 ^^^^^^^^
1076 The L-thread subsystem includes an example pthread shim. This is a partial
1077 implementation but does contain the API stubs needed to get basic applications
1078 running. There is a simple "hello world" application that demonstrates the
1079 use of the pthread shim.
1080
1081 A subtlety of working with a shim is that the application will still need
1082 to make use of the genuine pthread library functions, at the very least in
1083 order to create the EAL threads in which the L-thread schedulers will run.
1084 This is the case with DPDK initialization, and exit.
1085
1086 To deal with the initialization and shutdown scenarios, the shim is capable of
1087 switching on or off its adaptor functionality, an application can control this
1088 behavior by the calling the function ``pt_override_set()``. The default state
1089 is disabled.
1090
1091 The pthread shim uses the dynamic linker loader and saves the loaded addresses
1092 of the genuine pthread API functions in an internal table, when the shim
1093 functionality is enabled it performs the adaptor function, when disabled it
1094 invokes the genuine pthread function.
1095
1096 The function ``pthread_exit()`` has additional special handling. The standard
1097 system header file pthread.h declares ``pthread_exit()`` with
1098 ``__rte_noreturn`` this is an optimization that is possible because
1099 the pthread is terminating and this enables the compiler to omit the normal
1100 handling of stack and protection of registers since the function is not
1101 expected to return, and in fact the thread is being destroyed. These
1102 optimizations are applied in both the callee and the caller of the
1103 ``pthread_exit()`` function.
1104
1105 In our cooperative scheduling environment this behavior is inadmissible. The
1106 pthread is the L-thread scheduler thread, and, although an L-thread is
1107 terminating, there must be a return to the scheduler in order that the system
1108 can continue to run. Further, returning from a function with attribute
1109 ``noreturn`` is invalid and may result in undefined behavior.
1110
1111 The solution is to redefine the ``pthread_exit`` function with a macro,
1112 causing it to be mapped to a stub function in the shim that does not have the
1113 ``noreturn`` attribute. This macro is defined in the file
1114 ``pthread_shim.h``. The stub function is otherwise no different than any of
1115 the other stub functions in the shim, and will switch between the real
1116 ``pthread_exit()`` function or the ``lthread_exit()`` function as
1117 required. The only difference is that the mapping to the stub by macro
1118 substitution.
1119
1120 A consequence of this is that the file ``pthread_shim.h`` must be included in
1121 legacy code wishing to make use of the shim. It also means that dynamic
1122 linkage of a pre-compiled binary that did not include pthread_shim.h is not be
1123 supported.
1124
1125 Given the requirements for porting legacy code outlined in
1126 :ref:`porting_legacy_code_to_run_on_lthreads` most applications will require at
1127 least some minimal adjustment and recompilation to run on L-threads so
1128 pre-compiled binaries are unlikely to be met in practice.
1129
1130 In summary the shim approach adds some overhead but can be a useful tool to help
1131 establish the feasibility of a code reuse project. It is also a fairly
1132 straightforward task to extend the shim if necessary.
1133
1134 **Note:** Bearing in mind the preceding discussions about the impact of making
1135 blocking calls then switching the shim in and out on the fly to invoke any
1136 pthread API this might block is something that should typically be avoided.
1137
1138
1139 Building and running the pthread shim
1140 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1141
1142 The shim example application is located in the sample application
1143 in the performance-thread folder
1144
1145 To build and run the pthread shim example
1146
1147 #. Build the application:
1148
1149    To compile the sample application see :doc:`compiling`.
1150
1151 #. To run the pthread_shim example
1152
1153    .. code-block:: console
1154
1155        dpdk-pthread-shim -c core_mask -n number_of_channels
1156
1157 .. _lthread_diagnostics:
1158
1159 L-thread Diagnostics
1160 ~~~~~~~~~~~~~~~~~~~~
1161
1162 When debugging you must take account of the fact that the L-threads are run in
1163 a single pthread. The current scheduler is defined by
1164 ``RTE_PER_LCORE(this_sched)``, and the current lthread is stored at
1165 ``RTE_PER_LCORE(this_sched)->current_lthread``. Thus on a breakpoint in a GDB
1166 session the current lthread can be obtained by displaying the pthread local
1167 variable ``per_lcore_this_sched->current_lthread``.
1168
1169 Another useful diagnostic feature is the possibility to trace significant
1170 events in the life of an L-thread, this feature is enabled by changing the
1171 value of LTHREAD_DIAG from 0 to 1 in the file ``lthread_diag_api.h``.
1172
1173 Tracing of events can be individually masked, and the mask may be programmed
1174 at run time. An unmasked event results in a callback that provides information
1175 about the event. The default callback simply prints trace information. The
1176 default mask is 0 (all events off) the mask can be modified by calling the
1177 function ``lthread_diagnostic_set_mask()``.
1178
1179 It is possible register a user callback function to implement more
1180 sophisticated diagnostic functions.
1181 Object creation events (lthread, mutex, and condition variable) accept, and
1182 store in the created object, a user supplied reference value returned by the
1183 callback function.
1184
1185 The lthread reference value is passed back in all subsequent event callbacks,
1186 the mutex and APIs are provided to retrieve the reference value from
1187 mutexes and condition variables. This enables a user to monitor, count, or
1188 filter for specific events, on specific objects, for example to monitor for a
1189 specific thread signaling a specific condition variable, or to monitor
1190 on all timer events, the possibilities and combinations are endless.
1191
1192 The callback function can be set by calling the function
1193 ``lthread_diagnostic_enable()`` supplying a callback function pointer and an
1194 event mask.
1195
1196 Setting ``LTHREAD_DIAG`` also enables counting of statistics about cache and
1197 queue usage, and these statistics can be displayed by calling the function
1198 ``lthread_diag_stats_display()``. This function also performs a consistency
1199 check on the caches and queues. The function should only be called from the
1200 main EAL thread after all worker threads have stopped and returned to the C
1201 main program, otherwise the consistency check will fail.