| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Light-weight single-linked queue. | 
 |  * | 
 |  * Entries are enqueued to the head of an llist, with no blocking. | 
 |  * This can happen in any context. | 
 |  * | 
 |  * Entries are dequeued using a spinlock to protect against multiple | 
 |  * access.  The llist is staged in reverse order, and refreshed | 
 |  * from the llist when it exhausts. | 
 |  * | 
 |  * 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/rcupdate.h> | 
 | #include <linux/lwq.h> | 
 |  | 
 | struct llist_node *__lwq_dequeue(struct lwq *q) | 
 | { | 
 | 	struct llist_node *this; | 
 |  | 
 | 	if (lwq_empty(q)) | 
 | 		return NULL; | 
 | 	spin_lock(&q->lock); | 
 | 	this = q->ready; | 
 | 	if (!this && !llist_empty(&q->new)) { | 
 | 		/* ensure queue doesn't appear transiently lwq_empty */ | 
 | 		smp_store_release(&q->ready, (void *)1); | 
 | 		this = llist_reverse_order(llist_del_all(&q->new)); | 
 | 		if (!this) | 
 | 			q->ready = NULL; | 
 | 	} | 
 | 	if (this) | 
 | 		q->ready = llist_next(this); | 
 | 	spin_unlock(&q->lock); | 
 | 	return this; | 
 | } | 
 | EXPORT_SYMBOL_GPL(__lwq_dequeue); | 
 |  | 
 | /** | 
 |  * lwq_dequeue_all - dequeue all currently enqueued objects | 
 |  * @q:	the queue to dequeue from | 
 |  * | 
 |  * Remove and return a linked list of llist_nodes of all the objects that were | 
 |  * in the queue. The first on the list will be the object that was least | 
 |  * recently enqueued. | 
 |  */ | 
 | struct llist_node *lwq_dequeue_all(struct lwq *q) | 
 | { | 
 | 	struct llist_node *r, *t, **ep; | 
 |  | 
 | 	if (lwq_empty(q)) | 
 | 		return NULL; | 
 |  | 
 | 	spin_lock(&q->lock); | 
 | 	r = q->ready; | 
 | 	q->ready = NULL; | 
 | 	t = llist_del_all(&q->new); | 
 | 	spin_unlock(&q->lock); | 
 | 	ep = &r; | 
 | 	while (*ep) | 
 | 		ep = &(*ep)->next; | 
 | 	*ep = llist_reverse_order(t); | 
 | 	return r; | 
 | } | 
 | EXPORT_SYMBOL_GPL(lwq_dequeue_all); | 
 |  | 
 | #if IS_ENABLED(CONFIG_LWQ_TEST) | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/wait_bit.h> | 
 | #include <linux/kthread.h> | 
 | #include <linux/delay.h> | 
 | struct tnode { | 
 | 	struct lwq_node n; | 
 | 	int i; | 
 | 	int c; | 
 | }; | 
 |  | 
 | static int lwq_exercise(void *qv) | 
 | { | 
 | 	struct lwq *q = qv; | 
 | 	int cnt; | 
 | 	struct tnode *t; | 
 |  | 
 | 	for (cnt = 0; cnt < 10000; cnt++) { | 
 | 		wait_var_event(q, (t = lwq_dequeue(q, struct tnode, n)) != NULL); | 
 | 		t->c++; | 
 | 		if (lwq_enqueue(&t->n, q)) | 
 | 			wake_up_var(q); | 
 | 	} | 
 | 	while (!kthread_should_stop()) | 
 | 		schedule_timeout_idle(1); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int lwq_test(void) | 
 | { | 
 | 	int i; | 
 | 	struct lwq q; | 
 | 	struct llist_node *l, **t1, *t2; | 
 | 	struct tnode *t; | 
 | 	struct task_struct *threads[8]; | 
 |  | 
 | 	printk(KERN_INFO "testing lwq....\n"); | 
 | 	lwq_init(&q); | 
 | 	printk(KERN_INFO " lwq: run some threads\n"); | 
 | 	for (i = 0; i < ARRAY_SIZE(threads); i++) | 
 | 		threads[i] = kthread_run(lwq_exercise, &q, "lwq-test-%d", i); | 
 | 	for (i = 0; i < 100; i++) { | 
 | 		t = kmalloc(sizeof(*t), GFP_KERNEL); | 
 | 		if (!t) | 
 | 			break; | 
 | 		t->i = i; | 
 | 		t->c = 0; | 
 | 		if (lwq_enqueue(&t->n, &q)) | 
 | 			wake_up_var(&q); | 
 | 	} | 
 | 	/* wait for threads to exit */ | 
 | 	for (i = 0; i < ARRAY_SIZE(threads); i++) | 
 | 		if (!IS_ERR_OR_NULL(threads[i])) | 
 | 			kthread_stop(threads[i]); | 
 | 	printk(KERN_INFO " lwq: dequeue first 50:"); | 
 | 	for (i = 0; i < 50 ; i++) { | 
 | 		if (i && (i % 10) == 0) { | 
 | 			printk(KERN_CONT "\n"); | 
 | 			printk(KERN_INFO " lwq: ... "); | 
 | 		} | 
 | 		t = lwq_dequeue(&q, struct tnode, n); | 
 | 		if (t) | 
 | 			printk(KERN_CONT " %d(%d)", t->i, t->c); | 
 | 		kfree(t); | 
 | 	} | 
 | 	printk(KERN_CONT "\n"); | 
 | 	l = lwq_dequeue_all(&q); | 
 | 	printk(KERN_INFO " lwq: delete the multiples of 3 (test lwq_for_each_safe())\n"); | 
 | 	lwq_for_each_safe(t, t1, t2, &l, n) { | 
 | 		if ((t->i % 3) == 0) { | 
 | 			t->i = -1; | 
 | 			kfree(t); | 
 | 			t = NULL; | 
 | 		} | 
 | 	} | 
 | 	if (l) | 
 | 		lwq_enqueue_batch(l, &q); | 
 | 	printk(KERN_INFO " lwq: dequeue remaining:"); | 
 | 	while ((t = lwq_dequeue(&q, struct tnode, n)) != NULL) { | 
 | 		printk(KERN_CONT " %d", t->i); | 
 | 		kfree(t); | 
 | 	} | 
 | 	printk(KERN_CONT "\n"); | 
 | 	return 0; | 
 | } | 
 |  | 
 | module_init(lwq_test); | 
 | #endif /* CONFIG_LWQ_TEST*/ |