xref: /openbmc/linux/include/drm/spsc_queue.h (revision 552c69b36ebd966186573b9c7a286b390935cce1)
1*1b1f42d8SLucas Stach /*
2*1b1f42d8SLucas Stach  * Copyright 2017 Advanced Micro Devices, Inc.
3*1b1f42d8SLucas Stach  *
4*1b1f42d8SLucas Stach  * Permission is hereby granted, free of charge, to any person obtaining a
5*1b1f42d8SLucas Stach  * copy of this software and associated documentation files (the "Software"),
6*1b1f42d8SLucas Stach  * to deal in the Software without restriction, including without limitation
7*1b1f42d8SLucas Stach  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*1b1f42d8SLucas Stach  * and/or sell copies of the Software, and to permit persons to whom the
9*1b1f42d8SLucas Stach  * Software is furnished to do so, subject to the following conditions:
10*1b1f42d8SLucas Stach  *
11*1b1f42d8SLucas Stach  * The above copyright notice and this permission notice shall be included in
12*1b1f42d8SLucas Stach  * all copies or substantial portions of the Software.
13*1b1f42d8SLucas Stach  *
14*1b1f42d8SLucas Stach  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*1b1f42d8SLucas Stach  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*1b1f42d8SLucas Stach  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17*1b1f42d8SLucas Stach  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*1b1f42d8SLucas Stach  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*1b1f42d8SLucas Stach  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*1b1f42d8SLucas Stach  * OTHER DEALINGS IN THE SOFTWARE.
21*1b1f42d8SLucas Stach  *
22*1b1f42d8SLucas Stach  */
23*1b1f42d8SLucas Stach 
24*1b1f42d8SLucas Stach #ifndef DRM_SCHEDULER_SPSC_QUEUE_H_
25*1b1f42d8SLucas Stach #define DRM_SCHEDULER_SPSC_QUEUE_H_
26*1b1f42d8SLucas Stach 
27*1b1f42d8SLucas Stach #include <linux/atomic.h>
28*1b1f42d8SLucas Stach #include <linux/preempt.h>
29*1b1f42d8SLucas Stach 
30*1b1f42d8SLucas Stach /** SPSC lockless queue */
31*1b1f42d8SLucas Stach 
32*1b1f42d8SLucas Stach struct spsc_node {
33*1b1f42d8SLucas Stach 
34*1b1f42d8SLucas Stach 	/* Stores spsc_node* */
35*1b1f42d8SLucas Stach 	struct spsc_node *next;
36*1b1f42d8SLucas Stach };
37*1b1f42d8SLucas Stach 
38*1b1f42d8SLucas Stach struct spsc_queue {
39*1b1f42d8SLucas Stach 
40*1b1f42d8SLucas Stach 	 struct spsc_node *head;
41*1b1f42d8SLucas Stach 
42*1b1f42d8SLucas Stach 	/* atomic pointer to struct spsc_node* */
43*1b1f42d8SLucas Stach 	atomic_long_t tail;
44*1b1f42d8SLucas Stach 
45*1b1f42d8SLucas Stach 	atomic_t job_count;
46*1b1f42d8SLucas Stach };
47*1b1f42d8SLucas Stach 
spsc_queue_init(struct spsc_queue * queue)48*1b1f42d8SLucas Stach static inline void spsc_queue_init(struct spsc_queue *queue)
49*1b1f42d8SLucas Stach {
50*1b1f42d8SLucas Stach 	queue->head = NULL;
51*1b1f42d8SLucas Stach 	atomic_long_set(&queue->tail, (long)&queue->head);
52*1b1f42d8SLucas Stach 	atomic_set(&queue->job_count, 0);
53*1b1f42d8SLucas Stach }
54*1b1f42d8SLucas Stach 
spsc_queue_peek(struct spsc_queue * queue)55*1b1f42d8SLucas Stach static inline struct spsc_node *spsc_queue_peek(struct spsc_queue *queue)
56*1b1f42d8SLucas Stach {
57*1b1f42d8SLucas Stach 	return queue->head;
58*1b1f42d8SLucas Stach }
59*1b1f42d8SLucas Stach 
spsc_queue_count(struct spsc_queue * queue)60*1b1f42d8SLucas Stach static inline int spsc_queue_count(struct spsc_queue *queue)
61*1b1f42d8SLucas Stach {
62*1b1f42d8SLucas Stach 	return atomic_read(&queue->job_count);
63*1b1f42d8SLucas Stach }
64*1b1f42d8SLucas Stach 
spsc_queue_push(struct spsc_queue * queue,struct spsc_node * node)65*1b1f42d8SLucas Stach static inline bool spsc_queue_push(struct spsc_queue *queue, struct spsc_node *node)
66*1b1f42d8SLucas Stach {
67*1b1f42d8SLucas Stach 	struct spsc_node **tail;
68*1b1f42d8SLucas Stach 
69*1b1f42d8SLucas Stach 	node->next = NULL;
70*1b1f42d8SLucas Stach 
71*1b1f42d8SLucas Stach 	preempt_disable();
72*1b1f42d8SLucas Stach 
73*1b1f42d8SLucas Stach 	tail = (struct spsc_node **)atomic_long_xchg(&queue->tail, (long)&node->next);
74*1b1f42d8SLucas Stach 	WRITE_ONCE(*tail, node);
75*1b1f42d8SLucas Stach 	atomic_inc(&queue->job_count);
76*1b1f42d8SLucas Stach 
77*1b1f42d8SLucas Stach 	/*
78*1b1f42d8SLucas Stach 	 * In case of first element verify new node will be visible to the consumer
79*1b1f42d8SLucas Stach 	 * thread when we ping the kernel thread that there is new work to do.
80*1b1f42d8SLucas Stach 	 */
81*1b1f42d8SLucas Stach 	smp_wmb();
82*1b1f42d8SLucas Stach 
83*1b1f42d8SLucas Stach 	preempt_enable();
84*1b1f42d8SLucas Stach 
85*1b1f42d8SLucas Stach 	return tail == &queue->head;
86*1b1f42d8SLucas Stach }
87*1b1f42d8SLucas Stach 
88*1b1f42d8SLucas Stach 
spsc_queue_pop(struct spsc_queue * queue)89*1b1f42d8SLucas Stach static inline struct spsc_node *spsc_queue_pop(struct spsc_queue *queue)
90*1b1f42d8SLucas Stach {
91*1b1f42d8SLucas Stach 	struct spsc_node *next, *node;
92*1b1f42d8SLucas Stach 
93*1b1f42d8SLucas Stach 	/* Verify reading from memory and not the cache */
94*1b1f42d8SLucas Stach 	smp_rmb();
95*1b1f42d8SLucas Stach 
96*1b1f42d8SLucas Stach 	node = READ_ONCE(queue->head);
97*1b1f42d8SLucas Stach 
98*1b1f42d8SLucas Stach 	if (!node)
99*1b1f42d8SLucas Stach 		return NULL;
100*1b1f42d8SLucas Stach 
101*1b1f42d8SLucas Stach 	next = READ_ONCE(node->next);
102*1b1f42d8SLucas Stach 	WRITE_ONCE(queue->head, next);
103*1b1f42d8SLucas Stach 
104*1b1f42d8SLucas Stach 	if (unlikely(!next)) {
105*1b1f42d8SLucas Stach 		/* slowpath for the last element in the queue */
106*1b1f42d8SLucas Stach 
107*1b1f42d8SLucas Stach 		if (atomic_long_cmpxchg(&queue->tail,
108*1b1f42d8SLucas Stach 				(long)&node->next, (long) &queue->head) != (long)&node->next) {
109*1b1f42d8SLucas Stach 			/* Updating tail failed wait for new next to appear */
110*1b1f42d8SLucas Stach 			do {
111*1b1f42d8SLucas Stach 				smp_rmb();
112*1b1f42d8SLucas Stach 			} while (unlikely(!(queue->head = READ_ONCE(node->next))));
113*1b1f42d8SLucas Stach 		}
114*1b1f42d8SLucas Stach 	}
115*1b1f42d8SLucas Stach 
116*1b1f42d8SLucas Stach 	atomic_dec(&queue->job_count);
117*1b1f42d8SLucas Stach 	return node;
118*1b1f42d8SLucas Stach }
119*1b1f42d8SLucas Stach 
120*1b1f42d8SLucas Stach 
121*1b1f42d8SLucas Stach 
122*1b1f42d8SLucas Stach #endif /* DRM_SCHEDULER_SPSC_QUEUE_H_ */
123