205 lines · 6.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright 2023 Red Hat */ #include "wait-queue.h" #include <linux/device-mapper.h> #include "permassert.h" #include "status-codes.h" /** * vdo_waitq_enqueue_waiter() - Add a waiter to the tail end of a waitq. * @waitq: The vdo_wait_queue to which to add the waiter. * @waiter: The waiter to add to the waitq. * * The waiter must not already be waiting in a waitq. */void vdo_waitq_enqueue_waiter(struct vdo_wait_queue *waitq, struct vdo_waiter *waiter){	BUG_ON(waiter->next_waiter != NULL); 	if (waitq->last_waiter == NULL) {		/*		 * The waitq is empty, so form the initial circular list by self-linking the		 * initial waiter.		 */		waiter->next_waiter = waiter;	} else {		/* Splice the new waiter in at the end of the waitq. */		waiter->next_waiter = waitq->last_waiter->next_waiter;		waitq->last_waiter->next_waiter = waiter;	} 	/* In both cases, the waiter we added to the list becomes the last waiter. */	waitq->last_waiter = waiter;	waitq->length += 1;} /** * vdo_waitq_transfer_all_waiters() - Transfer all waiters from one waitq to *                                    a second waitq, emptying the first waitq. * @from_waitq: The waitq containing the waiters to move. * @to_waitq: The waitq that will receive the waiters from the first waitq. */void vdo_waitq_transfer_all_waiters(struct vdo_wait_queue *from_waitq,				    struct vdo_wait_queue *to_waitq){	/* If the source waitq is empty, there's nothing to do. */	if (!vdo_waitq_has_waiters(from_waitq))		return; 	if (vdo_waitq_has_waiters(to_waitq)) {		/*		 * Both are non-empty. Splice the two circular lists together		 * by swapping the next (head) pointers in the list tails.		 */		struct vdo_waiter *from_head = from_waitq->last_waiter->next_waiter;		struct vdo_waiter *to_head = to_waitq->last_waiter->next_waiter; 		to_waitq->last_waiter->next_waiter = from_head;		from_waitq->last_waiter->next_waiter = to_head;	} 	to_waitq->last_waiter = from_waitq->last_waiter;	to_waitq->length += from_waitq->length;	vdo_waitq_init(from_waitq);} /** * vdo_waitq_notify_all_waiters() - Notify all the entries waiting in a waitq. * @waitq: The vdo_wait_queue containing the waiters to notify. * @callback: The function to call to notify each waiter, or NULL to invoke the callback field *            registered in each waiter. * @context: The context to pass to the callback function. * * Notifies all the entries waiting in a waitq to continue execution by invoking a callback * function on each of them in turn. The waitq is copied and emptied before invoking any callbacks, * and only the waiters that were in the waitq at the start of the call will be notified. */void vdo_waitq_notify_all_waiters(struct vdo_wait_queue *waitq,				  vdo_waiter_callback_fn callback, void *context){	/*	 * Copy and empty the waitq first, avoiding the possibility of an infinite	 * loop if entries are returned to the waitq by the callback function.	 */	struct vdo_wait_queue waiters; 	vdo_waitq_init(&waiters);	vdo_waitq_transfer_all_waiters(waitq, &waiters); 	/* Drain the copied waitq, invoking the callback on every entry. */	while (vdo_waitq_has_waiters(&waiters))		vdo_waitq_notify_next_waiter(&waiters, callback, context);} /** * vdo_waitq_get_first_waiter() - Return the waiter that is at the head end of a waitq. * @waitq: The vdo_wait_queue from which to get the first waiter. * * Return: The first (oldest) waiter in the waitq, or NULL if the waitq is empty. */struct vdo_waiter *vdo_waitq_get_first_waiter(const struct vdo_wait_queue *waitq){	struct vdo_waiter *last_waiter = waitq->last_waiter; 	if (last_waiter == NULL) {		/* There are no waiters, so we're done. */		return NULL;	} 	/* The waitq is circular, so the last entry links to the head of the waitq. */	return last_waiter->next_waiter;} /** * vdo_waitq_dequeue_matching_waiters() - Remove all waiters that match based on the specified *                                        matching method and append them to a vdo_wait_queue. * @waitq: The vdo_wait_queue to process. * @waiter_match: The method to determine matching. * @match_context: Contextual info for the match method. * @matched_waitq: A wait_waitq to store matches. */void vdo_waitq_dequeue_matching_waiters(struct vdo_wait_queue *waitq,					vdo_waiter_match_fn waiter_match,					void *match_context,					struct vdo_wait_queue *matched_waitq){	struct vdo_wait_queue iteration_waitq; 	vdo_waitq_init(&iteration_waitq);	vdo_waitq_transfer_all_waiters(waitq, &iteration_waitq); 	while (vdo_waitq_has_waiters(&iteration_waitq)) {		struct vdo_waiter *waiter = vdo_waitq_dequeue_waiter(&iteration_waitq); 		vdo_waitq_enqueue_waiter((waiter_match(waiter, match_context) ?					  matched_waitq : waitq), waiter);	}} /** * vdo_waitq_dequeue_waiter() - Remove the first (oldest) waiter from a waitq. * @waitq: The vdo_wait_queue from which to remove the first entry. * * The caller will be responsible for waking the waiter by continuing its * execution appropriately. * * Return: The first (oldest) waiter in the waitq, or NULL if the waitq is empty. */struct vdo_waiter *vdo_waitq_dequeue_waiter(struct vdo_wait_queue *waitq){	struct vdo_waiter *first_waiter = vdo_waitq_get_first_waiter(waitq);	struct vdo_waiter *last_waiter = waitq->last_waiter; 	if (first_waiter == NULL)		return NULL; 	if (first_waiter == last_waiter) {		/* The waitq has a single entry, so empty it by nulling the tail. */		waitq->last_waiter = NULL;	} else {		/*		 * The waitq has multiple waiters, so splice the first waiter out		 * of the circular waitq.		 */		last_waiter->next_waiter = first_waiter->next_waiter;	} 	/* The waiter is no longer in a waitq. */	first_waiter->next_waiter = NULL;	waitq->length -= 1; 	return first_waiter;} /** * vdo_waitq_notify_next_waiter() - Notify the next entry waiting in a waitq. * @waitq: The vdo_wait_queue containing the waiter to notify. * @callback: The function to call to notify the waiter, or NULL to invoke the callback field *            registered in the waiter. * @context: The context to pass to the callback function. * * Notifies the next entry waiting in a waitq to continue execution by invoking a callback function * on it after removing it from the waitq. * * Return: true if there was a waiter in the waitq. */bool vdo_waitq_notify_next_waiter(struct vdo_wait_queue *waitq,				  vdo_waiter_callback_fn callback, void *context){	struct vdo_waiter *waiter = vdo_waitq_dequeue_waiter(waitq); 	if (waiter == NULL)		return false; 	if (callback == NULL)		callback = waiter->callback;	callback(waiter, context); 	return true;}