Skip to content

Commit 4009e56

Browse files
committed
lib: sys: queued_operation: add API for managing a queue of operations
Full asynchronous support for APIs such as bus transactions generally require managing operations from unrelated clients. This API provides a data structure and functions to manage those operations generically, leaving the service to provide only the service-specific operation description and implementation. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
1 parent 6270c67 commit 4009e56

File tree

9 files changed

+1119
-10
lines changed

9 files changed

+1119
-10
lines changed

doc/reference/kernel/other/resource_mgmt.rst

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@ complexity of properly managing multiple consumers of a device in a
1010
multithreaded system, especially when transitions may be asynchronous,
1111
suggests that a shared implementation is desirable.
1212

13+
Zephyr provides managers for several coordination policies. These
14+
managers are embedded into services that use them for specific
15+
functions.
16+
1317
.. contents::
1418
:local:
1519
:depth: 2
1620

21+
.. _resource_mgmt_onoff:
1722

18-
On-Off Services
19-
***************
23+
On-Off Manager
24+
**************
2025

21-
An on-off service supports an arbitrary number of clients of a service
26+
An on-off manager supports an arbitrary number of clients of a service
2227
which has a binary state. Example applications are power rails, clocks,
2328
and binary device power management.
2429

25-
The service has the following properties:
30+
The manager has the following properties:
2631

2732
* The stable states are off, on, and error. The service always begins
2833
in the off state. The service may also be in a transition to a given
@@ -56,15 +61,15 @@ Requests are reference counted, but not tracked. That means clients are
5661
responsible for recording whether their requests were accepted, and for
5762
initiating a release only if they have previously successfully completed
5863
a request. Improper use of the API can cause an active client to be
59-
shut out, and the service does not maintain a record of specific clients
64+
shut out, and the manager does not maintain a record of specific clients
6065
that have been granted a request.
6166

6267
Failures in executing a transition are recorded and inhibit further
63-
requests or releases until the service is reset. Pending requests are
68+
requests or releases until the manager is reset. Pending requests are
6469
notified (and cancelled) when errors are discovered.
6570

66-
Transition operation completion notifications are provided through any
67-
of the following mechanisms:
71+
Transition operation completion notifications are provided through the
72+
standard :ref:`async_notification`, supporting these methods:
6873

6974
* Signal: A pointer to a :c:type:`struct k_poll_signal` is provided, and
7075
the signal is raised when the transition completes. The operation
@@ -79,5 +84,63 @@ Synchronous transition may be implemented by a caller based on its
7984
context, for example by using :cpp:func:`k_poll()` to wait until the
8085
completion is signalled.
8186

82-
.. doxygengroup:: resource_mgmt_apis
87+
.. doxygengroup:: resource_mgmt_onoff_apis
88+
:project: Zephyr
89+
90+
.. _resource_mgmt_queued_operation:
91+
92+
Queued Operation Manager
93+
************************
94+
95+
While :ref:`resource_mgmt_onoff` supports a shared resource that must be
96+
available as long as any user still depends on it, the queued operation
97+
manager provides serialized exclusive access to a resource that executes
98+
operations asynchronously. This can be used to support (for example)
99+
ADC sampling for different sensors, or groups of bus transactions.
100+
Clients submit a operation request that is processed when the device
101+
becomes available, with clients being notified of the completion of the
102+
operation though the standard :ref:`async_notification`.
103+
104+
As with the on-off manager, the queued resource manager is a generic
105+
infrastructure tool that should be used by a extending service, such as
106+
an I2C bus controller or an ADC. The manager has the following
107+
characteristics:
108+
109+
* The stable states are idle and processing. The manager always begins
110+
in the idle state.
111+
* The core client operations are submit (add an operation) and cancel
112+
(remove an operation before it starts).
113+
* Ownership of the operation object transitions from the client to the
114+
manager when a queue request is accepted, and is returned to the
115+
client when the manager notifies the client of operation completion.
116+
* The core client event is completion. Manager state changes only as a
117+
side effect from submitting or completing an operation.
118+
* The service transitions from idle to processing when an operation is
119+
submitted.
120+
* The service transitions from processing to idle when notification of
121+
the last operation has completed and there are no queued operations.
122+
* The manager selects the next operation to process when notification of
123+
completion has itself completed. In particular, changes to the set of
124+
pending operations that are made during a completion callback affect
125+
the next operation to execute.
126+
* Each submitted operation includes a priority that orders execution by
127+
first-come-first-served within priority.
128+
* Operations are asynchronous, with completion notification through the
129+
:ref:`async_notification`. The operations and notifications are run
130+
in a context that is service-specific. This may be one or more
131+
dedicated threads, or work queues. Notifications may come from
132+
interrupt handlers. Note that for some services certain operations
133+
may complete before the submit request has returned to its caller.
134+
135+
The generic infrastructure holds the active operation and a queue of
136+
pending operations. A service extension shall provide functions that:
137+
138+
* check that a request is well-formed, i.e. can be added to the queue;
139+
* receive notification that a new operation is to be processed, or that
140+
no operations are available (allowing the service to enter a
141+
power-down mode);
142+
* translate a generic completion callback into a service-specific
143+
callback.
144+
145+
.. doxygengroup:: resource_mgmt_queued_operation_apis
83146
:project: Zephyr

include/sys/onoff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extern "C" {
1616
#endif
1717

1818
/**
19-
* @defgroup resource_mgmt_apis Resource Management APIs
19+
* @defgroup resource_mgmt_onoff_apis On-Off Service APIs
2020
* @ingroup kernel_apis
2121
* @{
2222
*/

include/sys/queued_operation.h

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* Copyright (c) 2019 Peter Bigot Consulting, LLC
3+
* Copyright (c) 2020 Nordic Semiconductor ASA
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#ifndef ZEPHYR_INCLUDE_SYS_QUEUED_OPERATION_H_
9+
#define ZEPHYR_INCLUDE_SYS_QUEUED_OPERATION_H_
10+
11+
#include <kernel.h>
12+
#include <zephyr/types.h>
13+
#include <sys/async_notify.h>
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
/* Forward declaration */
20+
struct queued_operation;
21+
struct queued_operation_manager;
22+
23+
/**
24+
* @defgroup resource_mgmt_queued_operation_apis Queued Operation APIs
25+
* @ingroup kernel_apis
26+
* @{
27+
*/
28+
29+
/** @internal */
30+
#define QUEUED_OPERATION_PRIORITY_POS ASYNC_NOTIFY_EXTENSION_POS
31+
/** @internal */
32+
#define QUEUED_OPERATION_PRIORITY_MASK 0xFF
33+
34+
/**
35+
* @brief Identify the region of async_notify flags available for
36+
* containing services.
37+
*
38+
* Bits of the flags field of the async_notify structure contained
39+
* within the queued_operation structure at and above this position
40+
* may be used by extensions to the async_notify structure.
41+
*
42+
* These bits are intended for use by containing service
43+
* implementations to record client-specific information. The bits
44+
* are cleared by async_notify_validate(). Use of these does not
45+
* imply that the flags field becomes public API.
46+
*/
47+
#define QUEUED_OPERATION_EXTENSION_POS (8 + ASYNC_NOTIFY_EXTENSION_POS)
48+
49+
/**
50+
* @brief Base object providing state for an operation.
51+
*
52+
* Instances of this should be members of a service-specific structure
53+
* that provides the operation parameters.
54+
*/
55+
struct queued_operation {
56+
/* Links the operation into the operation queue. */
57+
sys_snode_t node;
58+
59+
/* Notification configuration. */
60+
struct async_notify notify;
61+
};
62+
63+
/**
64+
* @ brief Table of functions used by a queued operation manager.
65+
*/
66+
struct queued_operation_functions {
67+
/**
68+
* @brief Function used to verify an operation is well-defined.
69+
*
70+
* When provided this function is invoked by
71+
* queued_operation_submit() to verify that the operation
72+
* definition meets the expectations of the service. The
73+
* operation is acceptable only if a non-negative value is
74+
* returned.
75+
*
76+
* If not provided queued_operation_submit() will assume
77+
* service-specific expectations are trivially satisfied, and
78+
* will reject the operation only if the notification
79+
* configuration is unacceptable.
80+
*
81+
* @param mgr the service that supports queued operations.
82+
*
83+
* @param op the operation being considered for suitability.
84+
*
85+
* @return the value to be returned from queued_operation_submit().
86+
*/
87+
int (*validate)(struct queued_operation_manager *mgr,
88+
struct queued_operation *op);
89+
90+
/**
91+
* @brief Function to transform a generic notification
92+
* callback to its service-specific form.
93+
*
94+
* The implementation should cast cb to the proper signature
95+
* for the service, and invoke the cast pointer with the
96+
* appropriate arguments.
97+
*
98+
* @param mgr the service that supports queued operations.
99+
*
100+
* @param op the operation that has been completed.
101+
*
102+
* @param cb the generic callback to invoke.
103+
*/
104+
void (*callback)(struct queued_operation_manager *mgr,
105+
struct queued_operation *op,
106+
async_notify_generic_callback cb);
107+
108+
/**
109+
* @brief Function used to inform the manager of a new operation.
110+
*
111+
* This is called as a side effect of
112+
* queued_operation_submit() or queued_operation_finalize() to
113+
* tell the service that a new operation needs to be
114+
* processed, or that there are no operations left to process.
115+
* The function will not be invoked while an operation is in
116+
* progress.
117+
*
118+
* @param mgr the service that supports queued operations.
119+
*
120+
* @param op the operation that should be initiated. A null
121+
* pointer is passed if there are no pending operations.
122+
*/
123+
void (*process)(struct queued_operation_manager *mgr,
124+
struct queued_operation *op);
125+
};
126+
127+
/**
128+
* @brief State associated with a manager instance.
129+
*/
130+
struct queued_operation_manager {
131+
/* Links the operation into the operation queue. */
132+
sys_slist_t operations;
133+
134+
/* Pointer to the functions that support the manager. */
135+
const struct queued_operation_functions *vtable;
136+
137+
/* Lock controlling access to other fields. */
138+
struct k_spinlock lock;
139+
140+
/* The operation that is being processed. */
141+
struct queued_operation *current;
142+
143+
/* A flag indicating that the last operation has been
144+
* completed and the callback notification is being
145+
* executed.
146+
*/
147+
bool finalizing;
148+
};
149+
150+
#define QUEUED_OPERATION_MANAGER_INITIALIZER(_vtable) { \
151+
.vtable = _vtable, \
152+
}
153+
154+
/**
155+
* @brief Submit an operation to be processed when the service is
156+
* available.
157+
*
158+
* During the call to this function the service process function will
159+
* have been invoked at least once, either providing another operation
160+
* or indicating that no operations are pending.
161+
*
162+
* @param mgr a generic pointer to the service instance
163+
* @param op a generic pointer to an operation to be performed
164+
* @param priority the priority of the operation relative to other
165+
* operations. Numerically lower values are higher priority. Values
166+
* outside the range of a signed 8-bit integer will be rejected.
167+
*
168+
* @retval -ENOTSUP if callback notification is requested and the
169+
* service does not provide a callback translation. This may also be
170+
* returned due to service-specific validation.
171+
*
172+
* @retval -EINVAL if the passed priority is out of the range of
173+
* supported priorities. This may also be returned due to
174+
* service-specific validation.
175+
*
176+
* @return A negative value if the operation was rejected by the
177+
* service or due to other configuration errors. A non-negative value
178+
* indicates the operation has been accepted for processing and
179+
* completion notification will be provided.
180+
*/
181+
int queued_operation_submit(struct queued_operation_manager *mgr,
182+
struct queued_operation *op,
183+
int priority);
184+
185+
/**
186+
* @brief Helper to extract the result from a queued operation.
187+
*
188+
* This forwards to :cpp:func:`async_notify_fetch_result()`.
189+
*/
190+
static inline int queued_operation_fetch_result(struct queued_operation *op,
191+
int *result)
192+
{
193+
return async_notify_fetch_result(&op->notify, result);
194+
}
195+
196+
/**
197+
* @brief Attempt to cancel a queued operation.
198+
*
199+
* Successful cancellation issues a completion notification with
200+
* result -ECANCELED for the submitted operation before this function
201+
* returns.
202+
*
203+
* @retval 0 if successfully cancelled.
204+
* @retval -EINPROGRESS if op is currently being executed, so cannot
205+
* be cancelled.
206+
* @retval -EINVAL if op is neither being executed nor in the queue of
207+
* pending operations
208+
*/
209+
int queued_operation_cancel(struct queued_operation_manager *mgr,
210+
struct queued_operation *op);
211+
212+
/**
213+
* @brief Send the completion notification for a queued operation.
214+
*
215+
* This function must be invoked by services that support queued
216+
* operations when the operation provided to them through the process
217+
* function have been completed. It is not intended to be invoked by
218+
* users of a service.
219+
*
220+
* During the call to this function the service process function will
221+
* be invoked at least once, either providing another operation or
222+
* indicating that no operations are pending.
223+
*
224+
* @param mgr a generic pointer to the service instance
225+
* @param op a generic pointer to the now completed operation
226+
* @param res the result of the operation, as with
227+
* async_notify_finalize().
228+
*/
229+
void queued_operation_finalize(struct queued_operation_manager *mgr,
230+
struct queued_operation *op,
231+
int res);
232+
233+
/** @} */
234+
235+
#ifdef __cplusplus
236+
}
237+
#endif
238+
239+
#endif /* ZEPHYR_INCLUDE_SYS_ASYNCNOTIFY_H_ */

lib/os/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ zephyr_sources(
1414
mempool.c
1515
printk.c
1616
onoff.c
17+
queued_operation.c
1718
rb.c
1819
sem.c
1920
thread_entry.c

0 commit comments

Comments
 (0)