18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2017 Advanced Micro Devices, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#ifndef DRM_SCHEDULER_SPSC_QUEUE_H_
258c2ecf20Sopenharmony_ci#define DRM_SCHEDULER_SPSC_QUEUE_H_
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/atomic.h>
288c2ecf20Sopenharmony_ci#include <linux/preempt.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/** SPSC lockless queue */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct spsc_node {
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* Stores spsc_node* */
358c2ecf20Sopenharmony_ci	struct spsc_node *next;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct spsc_queue {
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	 struct spsc_node *head;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* atomic pointer to struct spsc_node* */
438c2ecf20Sopenharmony_ci	atomic_long_t tail;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	atomic_t job_count;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic inline void spsc_queue_init(struct spsc_queue *queue)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	queue->head = NULL;
518c2ecf20Sopenharmony_ci	atomic_long_set(&queue->tail, (long)&queue->head);
528c2ecf20Sopenharmony_ci	atomic_set(&queue->job_count, 0);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic inline struct spsc_node *spsc_queue_peek(struct spsc_queue *queue)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	return queue->head;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline int spsc_queue_count(struct spsc_queue *queue)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	return atomic_read(&queue->job_count);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic inline bool spsc_queue_push(struct spsc_queue *queue, struct spsc_node *node)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct spsc_node **tail;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	node->next = NULL;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	preempt_disable();
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	tail = (struct spsc_node **)atomic_long_xchg(&queue->tail, (long)&node->next);
748c2ecf20Sopenharmony_ci	WRITE_ONCE(*tail, node);
758c2ecf20Sopenharmony_ci	atomic_inc(&queue->job_count);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/*
788c2ecf20Sopenharmony_ci	 * In case of first element verify new node will be visible to the consumer
798c2ecf20Sopenharmony_ci	 * thread when we ping the kernel thread that there is new work to do.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	smp_wmb();
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	preempt_enable();
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return tail == &queue->head;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic inline struct spsc_node *spsc_queue_pop(struct spsc_queue *queue)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct spsc_node *next, *node;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* Verify reading from memory and not the cache */
948c2ecf20Sopenharmony_ci	smp_rmb();
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	node = READ_ONCE(queue->head);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!node)
998c2ecf20Sopenharmony_ci		return NULL;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	next = READ_ONCE(node->next);
1028c2ecf20Sopenharmony_ci	WRITE_ONCE(queue->head, next);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (unlikely(!next)) {
1058c2ecf20Sopenharmony_ci		/* slowpath for the last element in the queue */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		if (atomic_long_cmpxchg(&queue->tail,
1088c2ecf20Sopenharmony_ci				(long)&node->next, (long) &queue->head) != (long)&node->next) {
1098c2ecf20Sopenharmony_ci			/* Updating tail failed wait for new next to appear */
1108c2ecf20Sopenharmony_ci			do {
1118c2ecf20Sopenharmony_ci				smp_rmb();
1128c2ecf20Sopenharmony_ci			} while (unlikely(!(queue->head = READ_ONCE(node->next))));
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	atomic_dec(&queue->job_count);
1178c2ecf20Sopenharmony_ci	return node;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#endif /* DRM_SCHEDULER_SPSC_QUEUE_H_ */
123