14fcb4290SMatthew Sakai /* SPDX-License-Identifier: GPL-2.0-only */
24fcb4290SMatthew Sakai /*
34fcb4290SMatthew Sakai * Copyright 2023 Red Hat
44fcb4290SMatthew Sakai */
54fcb4290SMatthew Sakai
64fcb4290SMatthew Sakai #ifndef VDO_WAIT_QUEUE_H
74fcb4290SMatthew Sakai #define VDO_WAIT_QUEUE_H
84fcb4290SMatthew Sakai
94fcb4290SMatthew Sakai #include <linux/compiler.h>
104fcb4290SMatthew Sakai #include <linux/types.h>
114fcb4290SMatthew Sakai
124fcb4290SMatthew Sakai /**
13d6e260ccSMike Snitzer * A vdo_wait_queue is a circular singly linked list of entries waiting to be notified
14d6e260ccSMike Snitzer * of a change in a condition. Keeping a circular list allows the vdo_wait_queue
15d6e260ccSMike Snitzer * structure to simply be a pointer to the tail (newest) entry, supporting
16d6e260ccSMike Snitzer * constant-time enqueue and dequeue operations. A null pointer is an empty waitq.
174fcb4290SMatthew Sakai *
18d6e260ccSMike Snitzer * An empty waitq:
19d6e260ccSMike Snitzer * waitq0.last_waiter -> NULL
204fcb4290SMatthew Sakai *
21d6e260ccSMike Snitzer * A singleton waitq:
22d6e260ccSMike Snitzer * waitq1.last_waiter -> entry1 -> entry1 -> [...]
234fcb4290SMatthew Sakai *
24d6e260ccSMike Snitzer * A three-element waitq:
25d6e260ccSMike Snitzer * waitq2.last_waiter -> entry3 -> entry1 -> entry2 -> entry3 -> [...]
264fcb4290SMatthew Sakai *
27d6e260ccSMike Snitzer * linux/wait.h's wait_queue_head is _not_ used because vdo_wait_queue's
28d6e260ccSMike Snitzer * interface is much less complex (doesn't need locking, priorities or timers).
29d6e260ccSMike Snitzer * Made possible by vdo's thread-based resource allocation and locking; and
30d6e260ccSMike Snitzer * the polling nature of vdo_wait_queue consumers.
31d6e260ccSMike Snitzer *
32d6e260ccSMike Snitzer * FIXME: could be made to use a linux/list.h's list_head but its extra barriers
33d6e260ccSMike Snitzer * really aren't needed. Nor is a doubly linked list, but vdo_wait_queue could
34d6e260ccSMike Snitzer * make use of __list_del_clearprev() -- but that would compromise the ability
35d6e260ccSMike Snitzer * to make full use of linux's list interface.
364fcb4290SMatthew Sakai */
374fcb4290SMatthew Sakai
38d6e260ccSMike Snitzer struct vdo_waiter;
394fcb4290SMatthew Sakai
40d6e260ccSMike Snitzer struct vdo_wait_queue {
414fcb4290SMatthew Sakai /* The tail of the queue, the last (most recently added) entry */
42d6e260ccSMike Snitzer struct vdo_waiter *last_waiter;
434fcb4290SMatthew Sakai /* The number of waiters currently in the queue */
44d6e260ccSMike Snitzer size_t length;
454fcb4290SMatthew Sakai };
464fcb4290SMatthew Sakai
474fcb4290SMatthew Sakai /**
48d6e260ccSMike Snitzer * vdo_waiter_callback_fn - Callback type that will be called to resume processing
49d6e260ccSMike Snitzer * of a waiter after it has been removed from its wait queue.
504fcb4290SMatthew Sakai */
51d6e260ccSMike Snitzer typedef void (*vdo_waiter_callback_fn)(struct vdo_waiter *waiter, void *context);
524fcb4290SMatthew Sakai
534fcb4290SMatthew Sakai /**
54d6e260ccSMike Snitzer * vdo_waiter_match_fn - Method type for waiter matching methods.
554fcb4290SMatthew Sakai *
56d6e260ccSMike Snitzer * Returns false if the waiter does not match.
574fcb4290SMatthew Sakai */
58d6e260ccSMike Snitzer typedef bool (*vdo_waiter_match_fn)(struct vdo_waiter *waiter, void *context);
594fcb4290SMatthew Sakai
60d6e260ccSMike Snitzer /* The structure for entries in a vdo_wait_queue. */
61d6e260ccSMike Snitzer struct vdo_waiter {
624fcb4290SMatthew Sakai /*
63d6e260ccSMike Snitzer * The next waiter in the waitq. If this entry is the last waiter, then this
64d6e260ccSMike Snitzer * is actually a pointer back to the head of the waitq.
654fcb4290SMatthew Sakai */
66d6e260ccSMike Snitzer struct vdo_waiter *next_waiter;
674fcb4290SMatthew Sakai
68d6e260ccSMike Snitzer /* Optional waiter-specific callback to invoke when dequeuing this waiter. */
69d6e260ccSMike Snitzer vdo_waiter_callback_fn callback;
704fcb4290SMatthew Sakai };
714fcb4290SMatthew Sakai
724fcb4290SMatthew Sakai /**
73d6e260ccSMike Snitzer * vdo_waiter_is_waiting() - Check whether a waiter is waiting.
744fcb4290SMatthew Sakai * @waiter: The waiter to check.
754fcb4290SMatthew Sakai *
76d6e260ccSMike Snitzer * Return: true if the waiter is on some vdo_wait_queue.
774fcb4290SMatthew Sakai */
vdo_waiter_is_waiting(struct vdo_waiter * waiter)78d6e260ccSMike Snitzer static inline bool vdo_waiter_is_waiting(struct vdo_waiter *waiter)
794fcb4290SMatthew Sakai {
804fcb4290SMatthew Sakai return (waiter->next_waiter != NULL);
814fcb4290SMatthew Sakai }
824fcb4290SMatthew Sakai
834fcb4290SMatthew Sakai /**
84d6e260ccSMike Snitzer * vdo_waitq_init() - Initialize a vdo_wait_queue.
85d6e260ccSMike Snitzer * @waitq: The vdo_wait_queue to initialize.
864fcb4290SMatthew Sakai */
vdo_waitq_init(struct vdo_wait_queue * waitq)87d6e260ccSMike Snitzer static inline void vdo_waitq_init(struct vdo_wait_queue *waitq)
884fcb4290SMatthew Sakai {
89d6e260ccSMike Snitzer *waitq = (struct vdo_wait_queue) {
904fcb4290SMatthew Sakai .last_waiter = NULL,
91d6e260ccSMike Snitzer .length = 0,
924fcb4290SMatthew Sakai };
934fcb4290SMatthew Sakai }
944fcb4290SMatthew Sakai
954fcb4290SMatthew Sakai /**
96d6e260ccSMike Snitzer * vdo_waitq_has_waiters() - Check whether a vdo_wait_queue has any entries waiting.
97d6e260ccSMike Snitzer * @waitq: The vdo_wait_queue to query.
984fcb4290SMatthew Sakai *
99d6e260ccSMike Snitzer * Return: true if there are any waiters in the waitq.
1004fcb4290SMatthew Sakai */
vdo_waitq_has_waiters(const struct vdo_wait_queue * waitq)101d6e260ccSMike Snitzer static inline bool __must_check vdo_waitq_has_waiters(const struct vdo_wait_queue *waitq)
1024fcb4290SMatthew Sakai {
103d6e260ccSMike Snitzer return (waitq->last_waiter != NULL);
1044fcb4290SMatthew Sakai }
1054fcb4290SMatthew Sakai
106d6e260ccSMike Snitzer void vdo_waitq_enqueue_waiter(struct vdo_wait_queue *waitq,
107d6e260ccSMike Snitzer struct vdo_waiter *waiter);
1084fcb4290SMatthew Sakai
109*a4bba246SMike Snitzer struct vdo_waiter *vdo_waitq_dequeue_waiter(struct vdo_wait_queue *waitq);
110*a4bba246SMike Snitzer
111d6e260ccSMike Snitzer void vdo_waitq_notify_all_waiters(struct vdo_wait_queue *waitq,
112d6e260ccSMike Snitzer vdo_waiter_callback_fn callback, void *context);
1134fcb4290SMatthew Sakai
114d6e260ccSMike Snitzer bool vdo_waitq_notify_next_waiter(struct vdo_wait_queue *waitq,
115d6e260ccSMike Snitzer vdo_waiter_callback_fn callback, void *context);
1164fcb4290SMatthew Sakai
117d6e260ccSMike Snitzer void vdo_waitq_transfer_all_waiters(struct vdo_wait_queue *from_waitq,
118d6e260ccSMike Snitzer struct vdo_wait_queue *to_waitq);
1194fcb4290SMatthew Sakai
120d6e260ccSMike Snitzer struct vdo_waiter *vdo_waitq_get_first_waiter(const struct vdo_wait_queue *waitq);
1214fcb4290SMatthew Sakai
122d6e260ccSMike Snitzer void vdo_waitq_dequeue_matching_waiters(struct vdo_wait_queue *waitq,
123d6e260ccSMike Snitzer vdo_waiter_match_fn waiter_match,
124d6e260ccSMike Snitzer void *match_context,
125d6e260ccSMike Snitzer struct vdo_wait_queue *matched_waitq);
1264fcb4290SMatthew Sakai
1274fcb4290SMatthew Sakai /**
128d6e260ccSMike Snitzer * vdo_waitq_num_waiters() - Return the number of waiters in a vdo_wait_queue.
129d6e260ccSMike Snitzer * @waitq: The vdo_wait_queue to query.
1304fcb4290SMatthew Sakai *
131d6e260ccSMike Snitzer * Return: The number of waiters in the waitq.
1324fcb4290SMatthew Sakai */
vdo_waitq_num_waiters(const struct vdo_wait_queue * waitq)133d6e260ccSMike Snitzer static inline size_t __must_check vdo_waitq_num_waiters(const struct vdo_wait_queue *waitq)
1344fcb4290SMatthew Sakai {
135d6e260ccSMike Snitzer return waitq->length;
1364fcb4290SMatthew Sakai }
1374fcb4290SMatthew Sakai
1384fcb4290SMatthew Sakai #endif /* VDO_WAIT_QUEUE_H */
139