Skip to content

Commit 63c14ad

Browse files
committed
lib: 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 d59fa6e commit 63c14ad

File tree

8 files changed

+1802
-0
lines changed

8 files changed

+1802
-0
lines changed

doc/reference/resource_management/index.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,61 @@ state has already been reached.
144144

145145
.. doxygengroup:: resource_mgmt_onoff_apis
146146
:project: Zephyr
147+
148+
.. _resource_mgmt_queued_operation:
149+
150+
Queued Operation Manager
151+
************************
152+
153+
While :ref:`resource_mgmt_onoff` supports a shared resource that must be
154+
available as long as any user still depends on it, the queued operation
155+
manager provides serialized exclusive access to a resource that executes
156+
operations asynchronously. This can be used to support (for example)
157+
ADC sampling for different sensors, or groups of bus transactions.
158+
Clients submit a operation request that is processed when the device
159+
becomes available, with clients being notified of the completion of the
160+
operation though the standard :ref:`async_notification`.
161+
162+
As with the on-off manager, the queued resource manager is a generic
163+
infrastructure tool that should be used by a extending service, such as
164+
an I2C bus controller or an ADC. The manager has the following
165+
characteristics:
166+
167+
* The stable states are idle and processing. The manager always begins
168+
in the idle state.
169+
* The core client operations are submit (add an operation) and cancel
170+
(remove an operation before it starts).
171+
* Ownership of the operation object transitions from the client to the
172+
manager when a queue request is accepted, and is returned to the
173+
client when the manager notifies the client of operation completion.
174+
* The core client event is completion. Manager state changes only as a
175+
side effect from submitting or completing an operation.
176+
* The service transitions from idle to processing when an operation is
177+
submitted.
178+
* The service transitions from processing to idle when notification of
179+
the last operation has completed and there are no queued operations.
180+
* The manager selects the next operation to process when notification of
181+
completion has itself completed. In particular, changes to the set of
182+
pending operations that are made during a completion callback affect
183+
the next operation to execute.
184+
* Each submitted operation includes a priority that orders execution by
185+
first-come-first-served within priority.
186+
* Operations are asynchronous, with completion notification through the
187+
:ref:`async_notification`. The operations and notifications are run
188+
in a context that is service-specific. This may be one or more
189+
dedicated threads, or work queues. Notifications may come from
190+
interrupt handlers. Note that for some services certain operations
191+
may complete before the submit request has returned to its caller.
192+
193+
The generic infrastructure holds the active operation and a queue of
194+
pending operations. A service extension shall provide functions that:
195+
196+
* check that a request is well-formed, i.e. can be added to the queue;
197+
* receive notification that a new operation is to be processed, or that
198+
no operations are available (allowing the service to enter a
199+
power-down mode);
200+
* translate a generic completion callback into a service-specific
201+
callback.
202+
203+
.. doxygengroup:: resource_mgmt_queued_operation_apis
204+
:project: Zephyr

include/sys/queued_operation.h

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
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/notify.h>
14+
#include <sys/onoff.h>
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
/* Forward declaration */
21+
struct queued_operation;
22+
struct queued_operation_manager;
23+
24+
/**
25+
* @defgroup resource_mgmt_queued_operation_apis Queued Operation APIs
26+
* @ingroup kernel_apis
27+
* @{
28+
*/
29+
30+
/** @internal */
31+
#define QUEUED_OPERATION_PRIORITY_POS SYS_NOTIFY_EXTENSION_POS
32+
/** @internal */
33+
#define QUEUED_OPERATION_PRIORITY_BITS 8U
34+
/** @internal */
35+
#define QUEUED_OPERATION_PRIORITY_MASK BIT_MASK(QUEUED_OPERATION_PRIORITY_BITS)
36+
37+
/**
38+
* @brief Special priority value to indicate operation should be
39+
* placed last in current queue.
40+
*
41+
* This is like providing the lowest priority but uses a constant-time
42+
* insertion and is FIFO.
43+
*/
44+
#define QUEUED_OPERATION_PRIORITY_APPEND \
45+
((int)QUEUED_OPERATION_PRIORITY_MASK + 1)
46+
47+
/**
48+
* @brief Special priority value to indicate operation should be
49+
* placed first in the current queue.
50+
*
51+
* This is like providing the highest priority but uses a
52+
* constant-time insertion and is LIFO.
53+
*/
54+
#define QUEUED_OPERATION_PRIORITY_PREPEND \
55+
((int)QUEUED_OPERATION_PRIORITY_MASK + 2)
56+
57+
/**
58+
* @brief Identify the region of sys_notify flags available for
59+
* containing services.
60+
*
61+
* Bits of the flags field of the sys_notify structure contained
62+
* within the queued_operation structure at and above this position
63+
* may be used by extensions to the sys_notify structure.
64+
*
65+
* These bits are intended for use by containing service
66+
* implementations to record client-specific information. The bits
67+
* are cleared by sys_notify_validate(). Use of these does not
68+
* imply that the flags field becomes public API.
69+
*/
70+
#define QUEUED_OPERATION_EXTENSION_POS \
71+
(QUEUED_OPERATION_PRIORITY_POS + QUEUED_OPERATION_PRIORITY_BITS)
72+
73+
/**
74+
* @brief Base object providing state for an operation.
75+
*
76+
* Instances of this should be members of a service-specific structure
77+
* that provides the operation parameters.
78+
*/
79+
struct queued_operation {
80+
/** @internal
81+
*
82+
* Links the operation into the operation queue.
83+
*/
84+
sys_snode_t node;
85+
86+
/**
87+
* @brief Notification configuration.
88+
*
89+
* This must be initialized using sys_notify_init_callback()
90+
* or its sibling functions before an operation can be passed
91+
* to queued_operation_submit().
92+
*
93+
* The queued operation manager provides specific error codes
94+
* for failures identified at the manager level:
95+
* * -ENODEV indicates a failure in an onoff service.
96+
*/
97+
struct sys_notify notify;
98+
};
99+
100+
/**
101+
* @ brief Table of functions used by a queued operation manager.
102+
*/
103+
struct queued_operation_functions {
104+
/**
105+
* @brief Function used to verify an operation is well-defined.
106+
*
107+
* When provided this function is invoked by
108+
* queued_operation_submit() to verify that the operation
109+
* definition meets the expectations of the service. The
110+
* operation is acceptable only if a non-negative value is
111+
* returned.
112+
*
113+
* If not provided queued_operation_submit() will assume
114+
* service-specific expectations are trivially satisfied, and
115+
* will reject the operation only if sys_notify_validate()
116+
* fails. Because that validation is limited services should
117+
* at a minimum verify that the extension bits have the
118+
* expected value (zero, when none are being used).
119+
*
120+
* @note The validate function must be isr-ok and not sleep.
121+
*
122+
* @param mgr the service that supports queued operations.
123+
*
124+
* @param op the operation being considered for suitability.
125+
*
126+
* @return the value to be returned from queued_operation_submit().
127+
*/
128+
int (*validate)(struct queued_operation_manager *mgr,
129+
struct queued_operation *op);
130+
131+
/**
132+
* @brief Function to transform a generic notification
133+
* callback to its service-specific form.
134+
*
135+
* The implementation should cast cb to the proper signature
136+
* for the service, and invoke the cast pointer with the
137+
* appropriate arguments.
138+
*
139+
* @note The callback function must be isr-ok and not sleep.
140+
*
141+
* @param mgr the service that supports queued operations.
142+
*
143+
* @param op the operation that has been completed.
144+
*
145+
* @param cb the generic callback to invoke.
146+
*/
147+
void (*callback)(struct queued_operation_manager *mgr,
148+
struct queued_operation *op,
149+
sys_notify_generic_callback cb);
150+
151+
/**
152+
* @brief Function used to inform the manager of a new operation.
153+
*
154+
* This function can be called as a side effect of
155+
* queued_operation_submit() or queued_operation_finalize() to
156+
* tell the service that a new operation needs to be
157+
* processed.
158+
*
159+
* Be aware that if processing is entirely
160+
* synchronous--meaning queued_operation_finalize() can be
161+
* invoked during process()--then the process() function will
162+
* be invoked recursively, possibly with another operation.
163+
* This can cause unbounded stack growth, and requires that
164+
* process() be re-entrant. Generally the process() function
165+
* should itself be async, with finalization done after
166+
* process() returns.
167+
*
168+
* @note The process function must be isr-ok and not sleep.
169+
*
170+
* @param mgr the service that supports queued operations.
171+
*
172+
* @param op the operation that should be initiated. A null
173+
* pointer is passed if there are no pending operations.
174+
*/
175+
void (*process)(struct queued_operation_manager *mgr,
176+
struct queued_operation *op);
177+
};
178+
179+
/**
180+
* @brief State associated with a manager instance.
181+
*/
182+
struct queued_operation_manager {
183+
/* Links the operation into the operation queue. */
184+
sys_slist_t operations;
185+
186+
/* Pointer to the functions that support the manager. */
187+
const struct queued_operation_functions *vtable;
188+
189+
/* Pointer to an on-off service supporting this service. NULL
190+
* if service is always-on.
191+
*/
192+
struct onoff_manager *onoff;
193+
194+
/* The state of on-off service requests. */
195+
struct onoff_client onoff_client;
196+
197+
/* Lock controlling access to other fields. */
198+
struct k_spinlock lock;
199+
200+
/* The operation that is being processed. */
201+
struct queued_operation *current;
202+
203+
/* Information about the internal state of the manager. */
204+
u32_t volatile state;
205+
};
206+
207+
#define QUEUED_OPERATION_MANAGER_INITIALIZER(_vtable, _onoff) { \
208+
.vtable = _vtable, \
209+
.onoff = _onoff, \
210+
}
211+
212+
/**
213+
* @brief Submit an operation to be processed when the service is
214+
* available.
215+
*
216+
* The service process function will be invoked during this call if
217+
* the service is available.
218+
*
219+
* @param mgr a generic pointer to the service instance
220+
*
221+
* @param op a generic pointer to an operation to be performed. The
222+
* notify field in the provided operation must have been initialized
223+
* before being submitted, even if the operation description is being
224+
* re-used. This may be done directly with sys_notify API or by
225+
* wrapping it in a service-specific operation init function.
226+
*
227+
* @param priority the priority of the operation relative to other
228+
* operations. Numerically lower values are higher priority. Values
229+
* outside the range of a signed 8-bit integer will be rejected,
230+
* except for named priorities like QUEUED_OPERATION_PRIORITY_APPEND.
231+
*
232+
* @retval -ENOTSUP if callback notification is requested and the
233+
* service does not provide a callback translation. This may also be
234+
* returned due to service-specific validation.
235+
*
236+
* @retval -EINVAL if the passed priority is out of the range of
237+
* supported priorities. This may also be returned due to
238+
* service-specific validation.
239+
*
240+
* @return A negative value if the operation was rejected by service
241+
* validation or due to other configuration errors. A non-negative
242+
* value indicates the operation has been accepted for processing and
243+
* completion notification will be provided.
244+
*/
245+
int queued_operation_submit(struct queued_operation_manager *mgr,
246+
struct queued_operation *op,
247+
int priority);
248+
249+
/**
250+
* @brief Helper to extract the result from a queued operation.
251+
*
252+
* This forwards to sys_notify_fetch_result().
253+
*/
254+
static inline int queued_operation_fetch_result(const struct queued_operation *op,
255+
int *result)
256+
{
257+
return sys_notify_fetch_result(&op->notify, result);
258+
}
259+
260+
/**
261+
* @brief Attempt to cancel a queued operation.
262+
*
263+
* Successful cancellation issues a completion notification with
264+
* result -ECANCELED for the submitted operation before this function
265+
* returns.
266+
*
267+
* @retval 0 if successfully cancelled.
268+
* @retval -EINPROGRESS if op is currently being executed, so cannot
269+
* be cancelled.
270+
* @retval -EINVAL if op is neither being executed nor in the queue of
271+
* pending operations
272+
*/
273+
int queued_operation_cancel(struct queued_operation_manager *mgr,
274+
struct queued_operation *op);
275+
276+
/**
277+
* @brief Send the completion notification for a queued operation.
278+
*
279+
* This function must be invoked by services that support queued
280+
* operations when the operation provided to them through the process
281+
* function have been completed. It is not intended to be invoked by
282+
* users of a service.
283+
*
284+
* @param mgr a generic pointer to the service instance
285+
* @param res the result of the operation, as with
286+
* sys_notify_finalize().
287+
*/
288+
void queued_operation_finalize(struct queued_operation_manager *mgr,
289+
int res);
290+
291+
/** @} */
292+
293+
#ifdef __cplusplus
294+
}
295+
#endif
296+
297+
#endif /* ZEPHYR_INCLUDE_SYS_ASYNCNOTIFY_H_ */

lib/os/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ zephyr_sources(
1515
printk.c
1616
onoff.c
1717
onoff_notifier.c
18+
queued_operation.c
1819
rb.c
1920
sem.c
2021
thread_entry.c

0 commit comments

Comments
 (0)