| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #ifndef LWQ_H |
| #define LWQ_H |
| /* |
| * Light-weight single-linked queue built from llist |
| * |
| * Entries can be enqueued from any context with no locking. |
| * Entries can be dequeued from process context with integrated locking. |
| * |
| * This is particularly suitable when work items are queued in |
| * BH or IRQ context, and where work items are handled one at a time |
| * by dedicated threads. |
| */ |
| #include <linux/container_of.h> |
| #include <linux/spinlock.h> |
| #include <linux/llist.h> |
| |
| struct lwq_node { |
| struct llist_node node; |
| }; |
| |
| struct lwq { |
| spinlock_t lock; |
| struct llist_node *ready; /* entries to be dequeued */ |
| struct llist_head new; /* entries being enqueued */ |
| }; |
| |
| /** |
| * lwq_init - initialise a lwq |
| * @q: the lwq object |
| */ |
| static inline void lwq_init(struct lwq *q) |
| { |
| spin_lock_init(&q->lock); |
| q->ready = NULL; |
| init_llist_head(&q->new); |
| } |
| |
| /** |
| * lwq_empty - test if lwq contains any entry |
| * @q: the lwq object |
| * |
| * This empty test contains an acquire barrier so that if a wakeup |
| * is sent when lwq_dequeue returns true, it is safe to go to sleep after |
| * a test on lwq_empty(). |
| */ |
| static inline bool lwq_empty(struct lwq *q) |
| { |
| /* acquire ensures ordering wrt lwq_enqueue() */ |
| return smp_load_acquire(&q->ready) == NULL && llist_empty(&q->new); |
| } |
| |
| struct llist_node *__lwq_dequeue(struct lwq *q); |
| /** |
| * lwq_dequeue - dequeue first (oldest) entry from lwq |
| * @q: the queue to dequeue from |
| * @type: the type of object to return |
| * @member: them member in returned object which is an lwq_node. |
| * |
| * Remove a single object from the lwq and return it. This will take |
| * a spinlock and so must always be called in the same context, typcially |
| * process contet. |
| */ |
| #define lwq_dequeue(q, type, member) \ |
| ({ struct llist_node *_n = __lwq_dequeue(q); \ |
| _n ? container_of(_n, type, member.node) : NULL; }) |
| |
| struct llist_node *lwq_dequeue_all(struct lwq *q); |
| |
| /** |
| * lwq_for_each_safe - iterate over detached queue allowing deletion |
| * @_n: iterator variable |
| * @_t1: temporary struct llist_node ** |
| * @_t2: temporary struct llist_node * |
| * @_l: address of llist_node pointer from lwq_dequeue_all() |
| * @_member: member in _n where lwq_node is found. |
| * |
| * Iterate over members in a dequeued list. If the iterator variable |
| * is set to NULL, the iterator removes that entry from the queue. |
| */ |
| #define lwq_for_each_safe(_n, _t1, _t2, _l, _member) \ |
| for (_t1 = (_l); \ |
| *(_t1) ? (_n = container_of(*(_t1), typeof(*(_n)), _member.node),\ |
| _t2 = ((*_t1)->next), \ |
| true) \ |
| : false; \ |
| (_n) ? (_t1 = &(_n)->_member.node.next, 0) \ |
| : ((*(_t1) = (_t2)), 0)) |
| |
| /** |
| * lwq_enqueue - add a new item to the end of the queue |
| * @n - the lwq_node embedded in the item to be added |
| * @q - the lwq to append to. |
| * |
| * No locking is needed to append to the queue so this can |
| * be called from any context. |
| * Return %true is the list may have previously been empty. |
| */ |
| static inline bool lwq_enqueue(struct lwq_node *n, struct lwq *q) |
| { |
| /* acquire enqures ordering wrt lwq_dequeue */ |
| return llist_add(&n->node, &q->new) && |
| smp_load_acquire(&q->ready) == NULL; |
| } |
| |
| /** |
| * lwq_enqueue_batch - add a list of new items to the end of the queue |
| * @n - the lwq_node embedded in the first item to be added |
| * @q - the lwq to append to. |
| * |
| * No locking is needed to append to the queue so this can |
| * be called from any context. |
| * Return %true is the list may have previously been empty. |
| */ |
| static inline bool lwq_enqueue_batch(struct llist_node *n, struct lwq *q) |
| { |
| struct llist_node *e = n; |
| |
| /* acquire enqures ordering wrt lwq_dequeue */ |
| return llist_add_batch(llist_reverse_order(n), e, &q->new) && |
| smp_load_acquire(&q->ready) == NULL; |
| } |
| #endif /* LWQ_H */ |