1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2023 Red Hat
6 #include "funnel-queue.h"
9 #include "memory-alloc.h"
10 #include "permassert.h"
12 int vdo_make_funnel_queue(struct funnel_queue
**queue_ptr
)
15 struct funnel_queue
*queue
;
17 result
= vdo_allocate(1, struct funnel_queue
, "funnel queue", &queue
);
18 if (result
!= VDO_SUCCESS
)
22 * Initialize the stub entry and put it in the queue, establishing the invariant that
23 * queue->newest and queue->oldest are never null.
25 queue
->stub
.next
= NULL
;
26 queue
->newest
= &queue
->stub
;
27 queue
->oldest
= &queue
->stub
;
33 void vdo_free_funnel_queue(struct funnel_queue
*queue
)
38 static struct funnel_queue_entry
*get_oldest(struct funnel_queue
*queue
)
41 * Barrier requirements: We need a read barrier between reading a "next" field pointer
42 * value and reading anything it points to. There's an accompanying barrier in
43 * vdo_funnel_queue_put() between its caller setting up the entry and making it visible.
45 struct funnel_queue_entry
*oldest
= queue
->oldest
;
46 struct funnel_queue_entry
*next
= READ_ONCE(oldest
->next
);
48 if (oldest
== &queue
->stub
) {
50 * When the oldest entry is the stub and it has no successor, the queue is
56 * The stub entry has a successor, so the stub can be dequeued and ignored without
57 * breaking the queue invariants.
60 queue
->oldest
= oldest
;
61 next
= READ_ONCE(oldest
->next
);
65 * We have a non-stub candidate to dequeue. If it lacks a successor, we'll need to put the
66 * stub entry back on the queue first.
69 struct funnel_queue_entry
*newest
= READ_ONCE(queue
->newest
);
71 if (oldest
!= newest
) {
73 * Another thread has already swung queue->newest atomically, but not yet
74 * assigned previous->next. The queue is really still empty.
80 * Put the stub entry back on the queue, ensuring a successor will eventually be
83 vdo_funnel_queue_put(queue
, &queue
->stub
);
85 /* Check again for a successor. */
86 next
= READ_ONCE(oldest
->next
);
89 * We lost a race with a producer who swapped queue->newest before we did,
90 * but who hasn't yet updated previous->next. Try again later.
100 * Poll a queue, removing the oldest entry if the queue is not empty. This function must only be
101 * called from a single consumer thread.
103 struct funnel_queue_entry
*vdo_funnel_queue_poll(struct funnel_queue
*queue
)
105 struct funnel_queue_entry
*oldest
= get_oldest(queue
);
111 * Dequeue the oldest entry and return it. Only one consumer thread may call this function,
112 * so no locking, atomic operations, or fences are needed; queue->oldest is owned by the
113 * consumer and oldest->next is never used by a producer thread after it is swung from NULL
116 queue
->oldest
= READ_ONCE(oldest
->next
);
118 * Make sure the caller sees the proper stored data for this entry. Since we've already
119 * fetched the entry pointer we stored in "queue->oldest", this also ensures that on entry
120 * to the next call we'll properly see the dependent data.
124 * If "oldest" is a very light-weight work item, we'll be looking for the next one very
125 * soon, so prefetch it now.
127 uds_prefetch_address(queue
->oldest
, true);
128 WRITE_ONCE(oldest
->next
, NULL
);
133 * Check whether the funnel queue is empty or not. If the queue is in a transition state with one
134 * or more entries being added such that the list view is incomplete, this function will report the
137 bool vdo_is_funnel_queue_empty(struct funnel_queue
*queue
)
139 return get_oldest(queue
) == NULL
;
143 * Check whether the funnel queue is idle or not. If the queue has entries available to be
144 * retrieved, it is not idle. If the queue is in a transition state with one or more entries being
145 * added such that the list view is incomplete, it may not be possible to retrieve an entry with
146 * the vdo_funnel_queue_poll() function, but the queue will not be considered idle.
148 bool vdo_is_funnel_queue_idle(struct funnel_queue
*queue
)
151 * Oldest is not the stub, so there's another entry, though if next is NULL we can't
154 if (queue
->oldest
!= &queue
->stub
)
158 * Oldest is the stub, but newest has been updated by _put(); either there's another,
159 * retrievable entry in the list, or the list is officially empty but in the intermediate
160 * state of having an entry added.
162 * Whether anything is retrievable depends on whether stub.next has been updated and become
163 * visible to us, but for idleness we don't care. And due to memory ordering in _put(), the
164 * update to newest would be visible to us at the same time or sooner.
166 if (READ_ONCE(queue
->newest
) != &queue
->stub
)