xref: /linux/Documentation/driver-api/surface_aggregator/internal.rst (revision b83deaa741558babf4b8d51d34f6637ccfff1b26)
1.. SPDX-License-Identifier: GPL-2.0+
2
3.. |ssh_ptl| replace:: :c:type:`struct ssh_ptl <ssh_ptl>`
4.. |ssh_ptl_submit| replace:: :c:func:`ssh_ptl_submit`
5.. |ssh_ptl_cancel| replace:: :c:func:`ssh_ptl_cancel`
6.. |ssh_ptl_shutdown| replace:: :c:func:`ssh_ptl_shutdown`
7.. |ssh_ptl_rx_rcvbuf| replace:: :c:func:`ssh_ptl_rx_rcvbuf`
8.. |ssh_rtl| replace:: :c:type:`struct ssh_rtl <ssh_rtl>`
9.. |ssh_rtl_submit| replace:: :c:func:`ssh_rtl_submit`
10.. |ssh_rtl_cancel| replace:: :c:func:`ssh_rtl_cancel`
11.. |ssh_rtl_shutdown| replace:: :c:func:`ssh_rtl_shutdown`
12.. |ssh_packet| replace:: :c:type:`struct ssh_packet <ssh_packet>`
13.. |ssh_packet_get| replace:: :c:func:`ssh_packet_get`
14.. |ssh_packet_put| replace:: :c:func:`ssh_packet_put`
15.. |ssh_packet_ops| replace:: :c:type:`struct ssh_packet_ops <ssh_packet_ops>`
16.. |ssh_packet_base_priority| replace:: :c:type:`enum ssh_packet_base_priority <ssh_packet_base_priority>`
17.. |ssh_packet_flags| replace:: :c:type:`enum ssh_packet_flags <ssh_packet_flags>`
18.. |SSH_PACKET_PRIORITY| replace:: :c:func:`SSH_PACKET_PRIORITY`
19.. |ssh_frame| replace:: :c:type:`struct ssh_frame <ssh_frame>`
20.. |ssh_command| replace:: :c:type:`struct ssh_command <ssh_command>`
21.. |ssh_request| replace:: :c:type:`struct ssh_request <ssh_request>`
22.. |ssh_request_get| replace:: :c:func:`ssh_request_get`
23.. |ssh_request_put| replace:: :c:func:`ssh_request_put`
24.. |ssh_request_ops| replace:: :c:type:`struct ssh_request_ops <ssh_request_ops>`
25.. |ssh_request_init| replace:: :c:func:`ssh_request_init`
26.. |ssh_request_flags| replace:: :c:type:`enum ssh_request_flags <ssh_request_flags>`
27.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
28.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
29.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
30.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
31.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
32.. |ssam_request_sync| replace:: :c:type:`struct ssam_request_sync <ssam_request_sync>`
33.. |ssam_event_registry| replace:: :c:type:`struct ssam_event_registry <ssam_event_registry>`
34.. |ssam_event_id| replace:: :c:type:`struct ssam_event_id <ssam_event_id>`
35.. |ssam_nf| replace:: :c:type:`struct ssam_nf <ssam_nf>`
36.. |ssam_nf_refcount_inc| replace:: :c:func:`ssam_nf_refcount_inc`
37.. |ssam_nf_refcount_dec| replace:: :c:func:`ssam_nf_refcount_dec`
38.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
39.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
40.. |ssam_cplt| replace:: :c:type:`struct ssam_cplt <ssam_cplt>`
41.. |ssam_event_queue| replace:: :c:type:`struct ssam_event_queue <ssam_event_queue>`
42.. |ssam_request_sync_submit| replace:: :c:func:`ssam_request_sync_submit`
43
44=====================
45Core Driver Internals
46=====================
47
48Architectural overview of the Surface System Aggregator Module (SSAM) core
49and Surface Serial Hub (SSH) driver. For the API documentation, refer to:
50
51.. toctree::
52   :maxdepth: 2
53
54   internal-api
55
56
57Overview
58========
59
60The SSAM core implementation is structured in layers, somewhat following the
61SSH protocol structure:
62
63Lower-level packet transport is implemented in the *packet transport layer
64(PTL)*, directly building on top of the serial device (serdev)
65infrastructure of the kernel. As the name indicates, this layer deals with
66the packet transport logic and handles things like packet validation, packet
67acknowledgment (ACKing), packet (retransmission) timeouts, and relaying
68packet payloads to higher-level layers.
69
70Above this sits the *request transport layer (RTL)*. This layer is centered
71around command-type packet payloads, i.e. requests (sent from host to EC),
72responses of the EC to those requests, and events (sent from EC to host).
73It, specifically, distinguishes events from request responses, matches
74responses to their corresponding requests, and implements request timeouts.
75
76The *controller* layer is building on top of this and essentially decides
77how request responses and, especially, events are dealt with. It provides an
78event notifier system, handles event activation/deactivation, provides a
79workqueue for event and asynchronous request completion, and also manages
80the message counters required for building command messages (``SEQ``,
81``RQID``). This layer basically provides a fundamental interface to the SAM
82EC for use in other kernel drivers.
83
84While the controller layer already provides an interface for other kernel
85drivers, the client *bus* extends this interface to provide support for
86native SSAM devices, i.e. devices that are not defined in ACPI and not
87implemented as platform devices, via |ssam_device| and |ssam_device_driver|
88simplify management of client devices and client drivers.
89
90Refer to Documentation/driver-api/surface_aggregator/client.rst for
91documentation regarding the client device/driver API and interface options
92for other kernel drivers. It is recommended to familiarize oneself with
93that chapter and the Documentation/driver-api/surface_aggregator/ssh.rst
94before continuing with the architectural overview below.
95
96
97Packet Transport Layer
98======================
99
100The packet transport layer is represented via |ssh_ptl| and is structured
101around the following key concepts:
102
103Packets
104-------
105
106Packets are the fundamental transmission unit of the SSH protocol. They are
107managed by the packet transport layer, which is essentially the lowest layer
108of the driver and is built upon by other components of the SSAM core.
109Packets to be transmitted by the SSAM core are represented via |ssh_packet|
110(in contrast, packets received by the core do not have any specific
111structure and are managed entirely via the raw |ssh_frame|).
112
113This structure contains the required fields to manage the packet inside the
114transport layer, as well as a reference to the buffer containing the data to
115be transmitted (i.e. the message wrapped in |ssh_frame|). Most notably, it
116contains an internal reference count, which is used for managing its
117lifetime (accessible via |ssh_packet_get| and |ssh_packet_put|). When this
118counter reaches zero, the ``release()`` callback provided to the packet via
119its |ssh_packet_ops| reference is executed, which may then deallocate the
120packet or its enclosing structure (e.g. |ssh_request|).
121
122In addition to the ``release`` callback, the |ssh_packet_ops| reference also
123provides a ``complete()`` callback, which is run once the packet has been
124completed and provides the status of this completion, i.e. zero on success
125or a negative errno value in case of an error. Once the packet has been
126submitted to the packet transport layer, the ``complete()`` callback is
127always guaranteed to be executed before the ``release()`` callback, i.e. the
128packet will always be completed, either successfully, with an error, or due
129to cancellation, before it will be released.
130
131The state of a packet is managed via its ``state`` flags
132(|ssh_packet_flags|), which also contains the packet type. In particular,
133the following bits are noteworthy:
134
135* ``SSH_PACKET_SF_LOCKED_BIT``: This bit is set when completion, either
136  through error or success, is imminent. It indicates that no further
137  references of the packet should be taken and any existing references
138  should be dropped as soon as possible. The process setting this bit is
139  responsible for removing any references to this packet from the packet
140  queue and pending set.
141
142* ``SSH_PACKET_SF_COMPLETED_BIT``: This bit is set by the process running the
143  ``complete()`` callback and is used to ensure that this callback only runs
144  once.
145
146* ``SSH_PACKET_SF_QUEUED_BIT``: This bit is set when the packet is queued on
147  the packet queue and cleared when it is dequeued.
148
149* ``SSH_PACKET_SF_PENDING_BIT``: This bit is set when the packet is added to
150  the pending set and cleared when it is removed from it.
151
152Packet Queue
153------------
154
155The packet queue is the first of the two fundamental collections in the
156packet transport layer. It is a priority queue, with priority of the
157respective packets based on the packet type (major) and number of tries
158(minor). See |SSH_PACKET_PRIORITY| for more details on the priority value.
159
160All packets to be transmitted by the transport layer must be submitted to
161this queue via |ssh_ptl_submit|. Note that this includes control packets
162sent by the transport layer itself. Internally, data packets can be
163re-submitted to this queue due to timeouts or NAK packets sent by the EC.
164
165Pending Set
166-----------
167
168The pending set is the second of the two fundamental collections in the
169packet transport layer. It stores references to packets that have already
170been transmitted, but wait for acknowledgment (e.g. the corresponding ACK
171packet) by the EC.
172
173Note that a packet may both be pending and queued if it has been
174re-submitted due to a packet acknowledgment timeout or NAK. On such a
175re-submission, packets are not removed from the pending set.
176
177Transmitter Thread
178------------------
179
180The transmitter thread is responsible for most of the actual work regarding
181packet transmission. In each iteration, it (waits for and) checks if the
182next packet on the queue (if any) can be transmitted and, if so, removes it
183from the queue and increments its counter for the number of transmission
184attempts, i.e. tries. If the packet is sequenced, i.e. requires an ACK by
185the EC, the packet is added to the pending set. Next, the packet's data is
186submitted to the serdev subsystem. In case of an error or timeout during
187this submission, the packet is completed by the transmitter thread with the
188status value of the callback set accordingly. In case the packet is
189unsequenced, i.e. does not require an ACK by the EC, the packet is completed
190with success on the transmitter thread.
191
192Transmission of sequenced packets is limited by the number of concurrently
193pending packets, i.e. a limit on how many packets may be waiting for an ACK
194from the EC in parallel. This limit is currently set to one (see
195Documentation/driver-api/surface_aggregator/ssh.rst for the reasoning behind
196this). Control packets (i.e. ACK and NAK) can always be transmitted.
197
198Receiver Thread
199---------------
200
201Any data received from the EC is put into a FIFO buffer for further
202processing. This processing happens on the receiver thread. The receiver
203thread parses and validates the received message into its |ssh_frame| and
204corresponding payload. It prepares and submits the necessary ACK (and on
205validation error or invalid data NAK) packets for the received messages.
206
207This thread also handles further processing, such as matching ACK messages
208to the corresponding pending packet (via sequence ID) and completing it, as
209well as initiating re-submission of all currently pending packets on
210receival of a NAK message (re-submission in case of a NAK is similar to
211re-submission due to timeout, see below for more details on that). Note that
212the successful completion of a sequenced packet will always run on the
213receiver thread (whereas any failure-indicating completion will run on the
214process where the failure occurred).
215
216Any payload data is forwarded via a callback to the next upper layer, i.e.
217the request transport layer.
218
219Timeout Reaper
220--------------
221
222The packet acknowledgment timeout is a per-packet timeout for sequenced
223packets, started when the respective packet begins (re-)transmission (i.e.
224this timeout is armed once per transmission attempt on the transmitter
225thread). It is used to trigger re-submission or, when the number of tries
226has been exceeded, cancellation of the packet in question.
227
228This timeout is handled via a dedicated reaper task, which is essentially a
229work item (re-)scheduled to run when the next packet is set to time out. The
230work item then checks the set of pending packets for any packets that have
231exceeded the timeout and, if there are any remaining packets, re-schedules
232itself to the next appropriate point in time.
233
234If a timeout has been detected by the reaper, the packet will either be
235re-submitted if it still has some remaining tries left, or completed with
236``-ETIMEDOUT`` as status if not. Note that re-submission, in this case and
237triggered by receival of a NAK, means that the packet is added to the queue
238with a now incremented number of tries, yielding a higher priority. The
239timeout for the packet will be disabled until the next transmission attempt
240and the packet remains on the pending set.
241
242Note that due to transmission and packet acknowledgment timeouts, the packet
243transport layer is always guaranteed to make progress, if only through
244timing out packets, and will never fully block.
245
246Concurrency and Locking
247-----------------------
248
249There are two main locks in the packet transport layer: One guarding access
250to the packet queue and one guarding access to the pending set. These
251collections may only be accessed and modified under the respective lock. If
252access to both collections is needed, the pending lock must be acquired
253before the queue lock to avoid deadlocks.
254
255In addition to guarding the collections, after initial packet submission
256certain packet fields may only be accessed under one of the locks.
257Specifically, the packet priority must only be accessed while holding the
258queue lock and the packet timestamp must only be accessed while holding the
259pending lock.
260
261Other parts of the packet transport layer are guarded independently. State
262flags are managed by atomic bit operations and, if necessary, memory
263barriers. Modifications to the timeout reaper work item and expiration date
264are guarded by their own lock.
265
266The reference of the packet to the packet transport layer (``ptl``) is
267somewhat special. It is either set when the upper layer request is submitted
268or, if there is none, when the packet is first submitted. After it is set,
269it will not change its value. Functions that may run concurrently with
270submission, i.e. cancellation, can not rely on the ``ptl`` reference to be
271set. Access to it in these functions is guarded by ``READ_ONCE()``, whereas
272setting ``ptl`` is equally guarded with ``WRITE_ONCE()`` for symmetry.
273
274Some packet fields may be read outside of the respective locks guarding
275them, specifically priority and state for tracing. In those cases, proper
276access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such
277read-only access is only allowed when stale values are not critical.
278
279With respect to the interface for higher layers, packet submission
280(|ssh_ptl_submit|), packet cancellation (|ssh_ptl_cancel|), data receival
281(|ssh_ptl_rx_rcvbuf|), and layer shutdown (|ssh_ptl_shutdown|) may always be
282executed concurrently with respect to each other. Note that packet
283submission may not run concurrently with itself for the same packet.
284Equally, shutdown and data receival may also not run concurrently with
285themselves (but may run concurrently with each other).
286
287
288Request Transport Layer
289=======================
290
291The request transport layer is represented via |ssh_rtl| and builds on top
292of the packet transport layer. It deals with requests, i.e. SSH packets sent
293by the host containing a |ssh_command| as frame payload. This layer
294separates responses to requests from events, which are also sent by the EC
295via a |ssh_command| payload. While responses are handled in this layer,
296events are relayed to the next upper layer, i.e. the controller layer, via
297the corresponding callback. The request transport layer is structured around
298the following key concepts:
299
300Request
301-------
302
303Requests are packets with a command-type payload, sent from host to EC to
304query data from or trigger an action on it (or both simultaneously). They
305are represented by |ssh_request|, wrapping the underlying |ssh_packet|
306storing its message data (i.e. SSH frame with command payload). Note that
307all top-level representations, e.g. |ssam_request_sync| are built upon this
308struct.
309
310As |ssh_request| extends |ssh_packet|, its lifetime is also managed by the
311reference counter inside the packet struct (which can be accessed via
312|ssh_request_get| and |ssh_request_put|). Once the counter reaches zero, the
313``release()`` callback of the |ssh_request_ops| reference of the request is
314called.
315
316Requests can have an optional response that is equally sent via a SSH
317message with command-type payload (from EC to host). The party constructing
318the request must know if a response is expected and mark this in the request
319flags provided to |ssh_request_init|, so that the request transport layer
320can wait for this response.
321
322Similar to |ssh_packet|, |ssh_request| also has a ``complete()`` callback
323provided via its request ops reference and is guaranteed to be completed
324before it is released once it has been submitted to the request transport
325layer via |ssh_rtl_submit|. For a request without a response, successful
326completion will occur once the underlying packet has been successfully
327transmitted by the packet transport layer (i.e. from within the packet
328completion callback). For a request with response, successful completion
329will occur once the response has been received and matched to the request
330via its request ID (which happens on the packet layer's data-received
331callback running on the receiver thread). If the request is completed with
332an error, the status value will be set to the corresponding (negative) errno
333value.
334
335The state of a request is again managed via its ``state`` flags
336(|ssh_request_flags|), which also encode the request type. In particular,
337the following bits are noteworthy:
338
339* ``SSH_REQUEST_SF_LOCKED_BIT``: This bit is set when completion, either
340  through error or success, is imminent. It indicates that no further
341  references of the request should be taken and any existing references
342  should be dropped as soon as possible. The process setting this bit is
343  responsible for removing any references to this request from the request
344  queue and pending set.
345
346* ``SSH_REQUEST_SF_COMPLETED_BIT``: This bit is set by the process running the
347  ``complete()`` callback and is used to ensure that this callback only runs
348  once.
349
350* ``SSH_REQUEST_SF_QUEUED_BIT``: This bit is set when the request is queued on
351  the request queue and cleared when it is dequeued.
352
353* ``SSH_REQUEST_SF_PENDING_BIT``: This bit is set when the request is added to
354  the pending set and cleared when it is removed from it.
355
356Request Queue
357-------------
358
359The request queue is the first of the two fundamental collections in the
360request transport layer. In contrast to the packet queue of the packet
361transport layer, it is not a priority queue and the simple first come first
362serve principle applies.
363
364All requests to be transmitted by the request transport layer must be
365submitted to this queue via |ssh_rtl_submit|. Once submitted, requests may
366not be re-submitted, and will not be re-submitted automatically on timeout.
367Instead, the request is completed with a timeout error. If desired, the
368caller can create and submit a new request for another try, but it must not
369submit the same request again.
370
371Pending Set
372-----------
373
374The pending set is the second of the two fundamental collections in the
375request transport layer. This collection stores references to all pending
376requests, i.e. requests awaiting a response from the EC (similar to what the
377pending set of the packet transport layer does for packets).
378
379Transmitter Task
380----------------
381
382The transmitter task is scheduled when a new request is available for
383transmission. It checks if the next request on the request queue can be
384transmitted and, if so, submits its underlying packet to the packet
385transport layer. This check ensures that only a limited number of
386requests can be pending, i.e. waiting for a response, at the same time. If
387the request requires a response, the request is added to the pending set
388before its packet is submitted.
389
390Packet Completion Callback
391--------------------------
392
393The packet completion callback is executed once the underlying packet of a
394request has been completed. In case of an error completion, the
395corresponding request is completed with the error value provided in this
396callback.
397
398On successful packet completion, further processing depends on the request.
399If the request expects a response, it is marked as transmitted and the
400request timeout is started. If the request does not expect a response, it is
401completed with success.
402
403Data-Received Callback
404----------------------
405
406The data received callback notifies the request transport layer of data
407being received by the underlying packet transport layer via a data-type
408frame. In general, this is expected to be a command-type payload.
409
410If the request ID of the command is one of the request IDs reserved for
411events (one to ``SSH_NUM_EVENTS``, inclusively), it is forwarded to the
412event callback registered in the request transport layer. If the request ID
413indicates a response to a request, the respective request is looked up in
414the pending set and, if found and marked as transmitted, completed with
415success.
416
417Timeout Reaper
418--------------
419
420The request-response-timeout is a per-request timeout for requests expecting
421a response. It is used to ensure that a request does not wait indefinitely
422on a response from the EC and is started after the underlying packet has
423been successfully completed.
424
425This timeout is, similar to the packet acknowledgment timeout on the packet
426transport layer, handled via a dedicated reaper task. This task is
427essentially a work-item (re-)scheduled to run when the next request is set
428to time out. The work item then scans the set of pending requests for any
429requests that have timed out and completes them with ``-ETIMEDOUT`` as
430status. Requests will not be re-submitted automatically. Instead, the issuer
431of the request must construct and submit a new request, if so desired.
432
433Note that this timeout, in combination with packet transmission and
434acknowledgment timeouts, guarantees that the request layer will always make
435progress, even if only through timing out packets, and never fully block.
436
437Concurrency and Locking
438-----------------------
439
440Similar to the packet transport layer, there are two main locks in the
441request transport layer: One guarding access to the request queue and one
442guarding access to the pending set. These collections may only be accessed
443and modified under the respective lock.
444
445Other parts of the request transport layer are guarded independently. State
446flags are (again) managed by atomic bit operations and, if necessary, memory
447barriers. Modifications to the timeout reaper work item and expiration date
448are guarded by their own lock.
449
450Some request fields may be read outside of the respective locks guarding
451them, specifically the state for tracing. In those cases, proper access is
452ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``. Such read-only
453access is only allowed when stale values are not critical.
454
455With respect to the interface for higher layers, request submission
456(|ssh_rtl_submit|), request cancellation (|ssh_rtl_cancel|), and layer
457shutdown (|ssh_rtl_shutdown|) may always be executed concurrently with
458respect to each other. Note that request submission may not run concurrently
459with itself for the same request (and also may only be called once per
460request). Equally, shutdown may also not run concurrently with itself.
461
462
463Controller Layer
464================
465
466The controller layer extends on the request transport layer to provide an
467easy-to-use interface for client drivers. It is represented by
468|ssam_controller| and the SSH driver. While the lower level transport layers
469take care of transmitting and handling packets and requests, the controller
470layer takes on more of a management role. Specifically, it handles device
471initialization, power management, and event handling, including event
472delivery and registration via the (event) completion system (|ssam_cplt|).
473
474Event Registration
475------------------
476
477In general, an event (or rather a class of events) has to be explicitly
478requested by the host before the EC will send it (HID input events seem to
479be the exception). This is done via an event-enable request (similarly,
480events should be disabled via an event-disable request once no longer
481desired).
482
483The specific request used to enable (or disable) an event is given via an
484event registry, i.e. the governing authority of this event (so to speak),
485represented by |ssam_event_registry|. As parameters to this request, the
486target category and, depending on the event registry, instance ID of the
487event to be enabled must be provided. This (optional) instance ID must be
488zero if the registry does not use it. Together, target category and instance
489ID form the event ID, represented by |ssam_event_id|. In short, both, event
490registry and event ID, are required to uniquely identify a respective class
491of events.
492
493Note that a further *request ID* parameter must be provided for the
494enable-event request. This parameter does not influence the class of events
495being enabled, but instead is set as the request ID (RQID) on each event of
496this class sent by the EC. It is used to identify events (as a limited
497number of request IDs is reserved for use in events only, specifically one
498to ``SSH_NUM_EVENTS`` inclusively) and also map events to their specific
499class. Currently, the controller always sets this parameter to the target
500category specified in |ssam_event_id|.
501
502As multiple client drivers may rely on the same (or overlapping) classes of
503events and enable/disable calls are strictly binary (i.e. on/off), the
504controller has to manage access to these events. It does so via reference
505counting, storing the counter inside an RB-tree based mapping with event
506registry and ID as key (there is no known list of valid event registry and
507event ID combinations). See |ssam_nf|, |ssam_nf_refcount_inc|, and
508|ssam_nf_refcount_dec| for details.
509
510This management is done together with notifier registration (described in
511the next section) via the top-level |ssam_notifier_register| and
512|ssam_notifier_unregister| functions.
513
514Event Delivery
515--------------
516
517To receive events, a client driver has to register an event notifier via
518|ssam_notifier_register|. This increments the reference counter for that
519specific class of events (as detailed in the previous section), enables the
520class on the EC (if it has not been enabled already), and installs the
521provided notifier callback.
522
523Notifier callbacks are stored in lists, with one (RCU) list per target
524category (provided via the event ID; NB: there is a fixed known number of
525target categories). There is no known association from the combination of
526event registry and event ID to the command data (target ID, target category,
527command ID, and instance ID) that can be provided by an event class, apart
528from target category and instance ID given via the event ID.
529
530Note that due to the way notifiers are (or rather have to be) stored, client
531drivers may receive events that they have not requested and need to account
532for them. Specifically, they will, by default, receive all events from the
533same target category. To simplify dealing with this, filtering of events by
534target ID (provided via the event registry) and instance ID (provided via
535the event ID) can be requested when registering a notifier. This filtering
536is applied when iterating over the notifiers at the time they are executed.
537
538All notifier callbacks are executed on a dedicated workqueue, the so-called
539completion workqueue. After an event has been received via the callback
540installed in the request layer (running on the receiver thread of the packet
541transport layer), it will be put on its respective event queue
542(|ssam_event_queue|). From this event queue the completion work item of that
543queue (running on the completion workqueue) will pick up the event and
544execute the notifier callback. This is done to avoid blocking on the
545receiver thread.
546
547There is one event queue per combination of target ID and target category.
548This is done to ensure that notifier callbacks are executed in sequence for
549events of the same target ID and target category. Callbacks can be executed
550in parallel for events with a different combination of target ID and target
551category.
552
553Concurrency and Locking
554-----------------------
555
556Most of the concurrency related safety guarantees of the controller are
557provided by the lower-level request transport layer. In addition to this,
558event (un-)registration is guarded by its own lock.
559
560Access to the controller state is guarded by the state lock. This lock is a
561read/write semaphore. The reader part can be used to ensure that the state
562does not change while functions depending on the state to stay the same
563(e.g. |ssam_notifier_register|, |ssam_notifier_unregister|,
564|ssam_request_sync_submit|, and derivatives) are executed and this guarantee
565is not already provided otherwise (e.g. through |ssam_client_bind| or
566|ssam_client_link|). The writer part guards any transitions that will change
567the state, i.e. initialization, destruction, suspension, and resumption.
568
569The controller state may be accessed (read-only) outside the state lock for
570smoke-testing against invalid API usage (e.g. in |ssam_request_sync_submit|).
571Note that such checks are not supposed to (and will not) protect against all
572invalid usages, but rather aim to help catch them. In those cases, proper
573variable access is ensured by employing ``WRITE_ONCE()`` and ``READ_ONCE()``.
574
575Assuming any preconditions on the state not changing have been satisfied,
576all non-initialization and non-shutdown functions may run concurrently with
577each other. This includes |ssam_notifier_register|, |ssam_notifier_unregister|,
578|ssam_request_sync_submit|, as well as all functions building on top of those.
579