1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2023 Red Hat
6 #include "wait-queue.h"
8 #include <linux/device-mapper.h>
10 #include "permassert.h"
12 #include "status-codes.h"
15 * vdo_waitq_enqueue_waiter() - Add a waiter to the tail end of a waitq.
16 * @waitq: The vdo_wait_queue to which to add the waiter.
17 * @waiter: The waiter to add to the waitq.
19 * The waiter must not already be waiting in a waitq.
21 void vdo_waitq_enqueue_waiter(struct vdo_wait_queue
*waitq
, struct vdo_waiter
*waiter
)
23 BUG_ON(waiter
->next_waiter
!= NULL
);
25 if (waitq
->last_waiter
== NULL
) {
27 * The waitq is empty, so form the initial circular list by self-linking the
30 waiter
->next_waiter
= waiter
;
32 /* Splice the new waiter in at the end of the waitq. */
33 waiter
->next_waiter
= waitq
->last_waiter
->next_waiter
;
34 waitq
->last_waiter
->next_waiter
= waiter
;
37 /* In both cases, the waiter we added to the ring becomes the last waiter. */
38 waitq
->last_waiter
= waiter
;
43 * vdo_waitq_transfer_all_waiters() - Transfer all waiters from one waitq to
44 * a second waitq, emptying the first waitq.
45 * @from_waitq: The waitq containing the waiters to move.
46 * @to_waitq: The waitq that will receive the waiters from the first waitq.
48 void vdo_waitq_transfer_all_waiters(struct vdo_wait_queue
*from_waitq
,
49 struct vdo_wait_queue
*to_waitq
)
51 /* If the source waitq is empty, there's nothing to do. */
52 if (!vdo_waitq_has_waiters(from_waitq
))
55 if (vdo_waitq_has_waiters(to_waitq
)) {
57 * Both are non-empty. Splice the two circular lists together
58 * by swapping the next (head) pointers in the list tails.
60 struct vdo_waiter
*from_head
= from_waitq
->last_waiter
->next_waiter
;
61 struct vdo_waiter
*to_head
= to_waitq
->last_waiter
->next_waiter
;
63 to_waitq
->last_waiter
->next_waiter
= from_head
;
64 from_waitq
->last_waiter
->next_waiter
= to_head
;
67 to_waitq
->last_waiter
= from_waitq
->last_waiter
;
68 to_waitq
->length
+= from_waitq
->length
;
69 vdo_waitq_init(from_waitq
);
73 * vdo_waitq_notify_all_waiters() - Notify all the entries waiting in a waitq.
74 * @waitq: The vdo_wait_queue containing the waiters to notify.
75 * @callback: The function to call to notify each waiter, or NULL to invoke the callback field
76 * registered in each waiter.
77 * @context: The context to pass to the callback function.
79 * Notifies all the entries waiting in a waitq to continue execution by invoking a callback
80 * function on each of them in turn. The waitq is copied and emptied before invoking any callbacks,
81 * and only the waiters that were in the waitq at the start of the call will be notified.
83 void vdo_waitq_notify_all_waiters(struct vdo_wait_queue
*waitq
,
84 vdo_waiter_callback_fn callback
, void *context
)
87 * Copy and empty the waitq first, avoiding the possibility of an infinite
88 * loop if entries are returned to the waitq by the callback function.
90 struct vdo_wait_queue waiters
;
92 vdo_waitq_init(&waiters
);
93 vdo_waitq_transfer_all_waiters(waitq
, &waiters
);
95 /* Drain the copied waitq, invoking the callback on every entry. */
96 while (vdo_waitq_has_waiters(&waiters
))
97 vdo_waitq_notify_next_waiter(&waiters
, callback
, context
);
101 * vdo_waitq_get_first_waiter() - Return the waiter that is at the head end of a waitq.
102 * @waitq: The vdo_wait_queue from which to get the first waiter.
104 * Return: The first (oldest) waiter in the waitq, or NULL if the waitq is empty.
106 struct vdo_waiter
*vdo_waitq_get_first_waiter(const struct vdo_wait_queue
*waitq
)
108 struct vdo_waiter
*last_waiter
= waitq
->last_waiter
;
110 if (last_waiter
== NULL
) {
111 /* There are no waiters, so we're done. */
115 /* The waitq is circular, so the last entry links to the head of the waitq. */
116 return last_waiter
->next_waiter
;
120 * vdo_waitq_dequeue_matching_waiters() - Remove all waiters that match based on the specified
121 * matching method and append them to a vdo_wait_queue.
122 * @waitq: The vdo_wait_queue to process.
123 * @waiter_match: The method to determine matching.
124 * @match_context: Contextual info for the match method.
125 * @matched_waitq: A wait_waitq to store matches.
127 void vdo_waitq_dequeue_matching_waiters(struct vdo_wait_queue
*waitq
,
128 vdo_waiter_match_fn waiter_match
,
130 struct vdo_wait_queue
*matched_waitq
)
132 struct vdo_wait_queue iteration_waitq
;
134 vdo_waitq_init(&iteration_waitq
);
135 vdo_waitq_transfer_all_waiters(waitq
, &iteration_waitq
);
137 while (vdo_waitq_has_waiters(&iteration_waitq
)) {
138 struct vdo_waiter
*waiter
= vdo_waitq_dequeue_waiter(&iteration_waitq
);
140 vdo_waitq_enqueue_waiter((waiter_match(waiter
, match_context
) ?
141 matched_waitq
: waitq
), waiter
);
146 * vdo_waitq_dequeue_waiter() - Remove the first (oldest) waiter from a waitq.
147 * @waitq: The vdo_wait_queue from which to remove the first entry.
149 * The caller will be responsible for waking the waiter by continuing its
150 * execution appropriately.
152 * Return: The first (oldest) waiter in the waitq, or NULL if the waitq is empty.
154 struct vdo_waiter
*vdo_waitq_dequeue_waiter(struct vdo_wait_queue
*waitq
)
156 struct vdo_waiter
*first_waiter
= vdo_waitq_get_first_waiter(waitq
);
157 struct vdo_waiter
*last_waiter
= waitq
->last_waiter
;
159 if (first_waiter
== NULL
)
162 if (first_waiter
== last_waiter
) {
163 /* The waitq has a single entry, so empty it by nulling the tail. */
164 waitq
->last_waiter
= NULL
;
167 * The waitq has multiple waiters, so splice the first waiter out
168 * of the circular waitq.
170 last_waiter
->next_waiter
= first_waiter
->next_waiter
;
173 /* The waiter is no longer in a waitq. */
174 first_waiter
->next_waiter
= NULL
;
181 * vdo_waitq_notify_next_waiter() - Notify the next entry waiting in a waitq.
182 * @waitq: The vdo_wait_queue containing the waiter to notify.
183 * @callback: The function to call to notify the waiter, or NULL to invoke the callback field
184 * registered in the waiter.
185 * @context: The context to pass to the callback function.
187 * Notifies the next entry waiting in a waitq to continue execution by invoking a callback function
188 * on it after removing it from the waitq.
190 * Return: true if there was a waiter in the waitq.
192 bool vdo_waitq_notify_next_waiter(struct vdo_wait_queue
*waitq
,
193 vdo_waiter_callback_fn callback
, void *context
)
195 struct vdo_waiter
*waiter
= vdo_waitq_dequeue_waiter(waitq
);
200 if (callback
== NULL
)
201 callback
= waiter
->callback
;
202 callback(waiter
, context
);