1*aded9cb8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
277ba89c5SIngo Molnar /*
377ba89c5SIngo Molnar * lib/plist.c
477ba89c5SIngo Molnar *
577ba89c5SIngo Molnar * Descending-priority-sorted double-linked list
677ba89c5SIngo Molnar *
777ba89c5SIngo Molnar * (C) 2002-2003 Intel Corp
877ba89c5SIngo Molnar * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>.
977ba89c5SIngo Molnar *
1077ba89c5SIngo Molnar * 2001-2005 (c) MontaVista Software, Inc.
1177ba89c5SIngo Molnar * Daniel Walker <dwalker@mvista.com>
1277ba89c5SIngo Molnar *
1377ba89c5SIngo Molnar * (C) 2005 Thomas Gleixner <tglx@linutronix.de>
1477ba89c5SIngo Molnar *
1577ba89c5SIngo Molnar * Simplifications of the original code by
1677ba89c5SIngo Molnar * Oleg Nesterov <oleg@tv-sign.ru>
1777ba89c5SIngo Molnar *
1877ba89c5SIngo Molnar * Based on simple lists (include/linux/list.h).
1977ba89c5SIngo Molnar *
2077ba89c5SIngo Molnar * This file contains the add / del functions which are considered to
2177ba89c5SIngo Molnar * be too large to inline. See include/linux/plist.h for further
2277ba89c5SIngo Molnar * information.
2377ba89c5SIngo Molnar */
2477ba89c5SIngo Molnar
2550af5eadSPaul Gortmaker #include <linux/bug.h>
2677ba89c5SIngo Molnar #include <linux/plist.h>
2777ba89c5SIngo Molnar
288e18faeaSDavidlohr Bueso #ifdef CONFIG_DEBUG_PLIST
2977ba89c5SIngo Molnar
306d55da53SLai Jiangshan static struct plist_head test_head;
316d55da53SLai Jiangshan
plist_check_prev_next(struct list_head * t,struct list_head * p,struct list_head * n)3277ba89c5SIngo Molnar static void plist_check_prev_next(struct list_head *t, struct list_head *p,
3377ba89c5SIngo Molnar struct list_head *n)
3477ba89c5SIngo Molnar {
355cd2b459SArjan van de Ven WARN(n->prev != p || p->next != n,
365cd2b459SArjan van de Ven "top: %p, n: %p, p: %p\n"
375cd2b459SArjan van de Ven "prev: %p, n: %p, p: %p\n"
385cd2b459SArjan van de Ven "next: %p, n: %p, p: %p\n",
395cd2b459SArjan van de Ven t, t->next, t->prev,
405cd2b459SArjan van de Ven p, p->next, p->prev,
415cd2b459SArjan van de Ven n, n->next, n->prev);
4277ba89c5SIngo Molnar }
4377ba89c5SIngo Molnar
plist_check_list(struct list_head * top)4477ba89c5SIngo Molnar static void plist_check_list(struct list_head *top)
4577ba89c5SIngo Molnar {
4677ba89c5SIngo Molnar struct list_head *prev = top, *next = top->next;
4777ba89c5SIngo Molnar
4877ba89c5SIngo Molnar plist_check_prev_next(top, prev, next);
4977ba89c5SIngo Molnar while (next != top) {
5077ba89c5SIngo Molnar prev = next;
5177ba89c5SIngo Molnar next = prev->next;
5277ba89c5SIngo Molnar plist_check_prev_next(top, prev, next);
5377ba89c5SIngo Molnar }
5477ba89c5SIngo Molnar }
5577ba89c5SIngo Molnar
plist_check_head(struct plist_head * head)5677ba89c5SIngo Molnar static void plist_check_head(struct plist_head *head)
5777ba89c5SIngo Molnar {
58bf6a9b83SLai Jiangshan if (!plist_head_empty(head))
59bf6a9b83SLai Jiangshan plist_check_list(&plist_first(head)->prio_list);
6077ba89c5SIngo Molnar plist_check_list(&head->node_list);
6177ba89c5SIngo Molnar }
6277ba89c5SIngo Molnar
6377ba89c5SIngo Molnar #else
6477ba89c5SIngo Molnar # define plist_check_head(h) do { } while (0)
6577ba89c5SIngo Molnar #endif
6677ba89c5SIngo Molnar
6777ba89c5SIngo Molnar /**
6877ba89c5SIngo Molnar * plist_add - add @node to @head
6977ba89c5SIngo Molnar *
7077ba89c5SIngo Molnar * @node: &struct plist_node pointer
7177ba89c5SIngo Molnar * @head: &struct plist_head pointer
7277ba89c5SIngo Molnar */
plist_add(struct plist_node * node,struct plist_head * head)7377ba89c5SIngo Molnar void plist_add(struct plist_node *node, struct plist_head *head)
7477ba89c5SIngo Molnar {
75bf6a9b83SLai Jiangshan struct plist_node *first, *iter, *prev = NULL;
76bf6a9b83SLai Jiangshan struct list_head *node_next = &head->node_list;
7777ba89c5SIngo Molnar
7877ba89c5SIngo Molnar plist_check_head(head);
7977ba89c5SIngo Molnar WARN_ON(!plist_node_empty(node));
80bf6a9b83SLai Jiangshan WARN_ON(!list_empty(&node->prio_list));
8177ba89c5SIngo Molnar
82bf6a9b83SLai Jiangshan if (plist_head_empty(head))
83bf6a9b83SLai Jiangshan goto ins_node;
84bf6a9b83SLai Jiangshan
85bf6a9b83SLai Jiangshan first = iter = plist_first(head);
86bf6a9b83SLai Jiangshan
87bf6a9b83SLai Jiangshan do {
88bf6a9b83SLai Jiangshan if (node->prio < iter->prio) {
89bf6a9b83SLai Jiangshan node_next = &iter->node_list;
90bf6a9b83SLai Jiangshan break;
9177ba89c5SIngo Molnar }
9277ba89c5SIngo Molnar
93bf6a9b83SLai Jiangshan prev = iter;
94bf6a9b83SLai Jiangshan iter = list_entry(iter->prio_list.next,
95bf6a9b83SLai Jiangshan struct plist_node, prio_list);
96bf6a9b83SLai Jiangshan } while (iter != first);
97bf6a9b83SLai Jiangshan
98bf6a9b83SLai Jiangshan if (!prev || prev->prio != node->prio)
99bf6a9b83SLai Jiangshan list_add_tail(&node->prio_list, &iter->prio_list);
100bf6a9b83SLai Jiangshan ins_node:
101bf6a9b83SLai Jiangshan list_add_tail(&node->node_list, node_next);
10277ba89c5SIngo Molnar
10377ba89c5SIngo Molnar plist_check_head(head);
10477ba89c5SIngo Molnar }
10577ba89c5SIngo Molnar
10677ba89c5SIngo Molnar /**
10777ba89c5SIngo Molnar * plist_del - Remove a @node from plist.
10877ba89c5SIngo Molnar *
10977ba89c5SIngo Molnar * @node: &struct plist_node pointer - entry to be removed
11077ba89c5SIngo Molnar * @head: &struct plist_head pointer - list head
11177ba89c5SIngo Molnar */
plist_del(struct plist_node * node,struct plist_head * head)11277ba89c5SIngo Molnar void plist_del(struct plist_node *node, struct plist_head *head)
11377ba89c5SIngo Molnar {
11477ba89c5SIngo Molnar plist_check_head(head);
11577ba89c5SIngo Molnar
116bf6a9b83SLai Jiangshan if (!list_empty(&node->prio_list)) {
117bf6a9b83SLai Jiangshan if (node->node_list.next != &head->node_list) {
118bf6a9b83SLai Jiangshan struct plist_node *next;
11977ba89c5SIngo Molnar
120bf6a9b83SLai Jiangshan next = list_entry(node->node_list.next,
121bf6a9b83SLai Jiangshan struct plist_node, node_list);
122bf6a9b83SLai Jiangshan
123bf6a9b83SLai Jiangshan /* add the next plist_node into prio_list */
124bf6a9b83SLai Jiangshan if (list_empty(&next->prio_list))
125bf6a9b83SLai Jiangshan list_add(&next->prio_list, &node->prio_list);
126bf6a9b83SLai Jiangshan }
127bf6a9b83SLai Jiangshan list_del_init(&node->prio_list);
12877ba89c5SIngo Molnar }
12977ba89c5SIngo Molnar
130bf6a9b83SLai Jiangshan list_del_init(&node->node_list);
13177ba89c5SIngo Molnar
13277ba89c5SIngo Molnar plist_check_head(head);
13377ba89c5SIngo Molnar }
1346d55da53SLai Jiangshan
135a75f232cSDan Streetman /**
136a75f232cSDan Streetman * plist_requeue - Requeue @node at end of same-prio entries.
137a75f232cSDan Streetman *
138a75f232cSDan Streetman * This is essentially an optimized plist_del() followed by
139a75f232cSDan Streetman * plist_add(). It moves an entry already in the plist to
140a75f232cSDan Streetman * after any other same-priority entries.
141a75f232cSDan Streetman *
142a75f232cSDan Streetman * @node: &struct plist_node pointer - entry to be moved
143a75f232cSDan Streetman * @head: &struct plist_head pointer - list head
144a75f232cSDan Streetman */
plist_requeue(struct plist_node * node,struct plist_head * head)145a75f232cSDan Streetman void plist_requeue(struct plist_node *node, struct plist_head *head)
146a75f232cSDan Streetman {
147a75f232cSDan Streetman struct plist_node *iter;
148a75f232cSDan Streetman struct list_head *node_next = &head->node_list;
149a75f232cSDan Streetman
150a75f232cSDan Streetman plist_check_head(head);
151a75f232cSDan Streetman BUG_ON(plist_head_empty(head));
152a75f232cSDan Streetman BUG_ON(plist_node_empty(node));
153a75f232cSDan Streetman
154a75f232cSDan Streetman if (node == plist_last(head))
155a75f232cSDan Streetman return;
156a75f232cSDan Streetman
157a75f232cSDan Streetman iter = plist_next(node);
158a75f232cSDan Streetman
159a75f232cSDan Streetman if (node->prio != iter->prio)
160a75f232cSDan Streetman return;
161a75f232cSDan Streetman
162a75f232cSDan Streetman plist_del(node, head);
163a75f232cSDan Streetman
164a75f232cSDan Streetman plist_for_each_continue(iter, head) {
165a75f232cSDan Streetman if (node->prio != iter->prio) {
166a75f232cSDan Streetman node_next = &iter->node_list;
167a75f232cSDan Streetman break;
168a75f232cSDan Streetman }
169a75f232cSDan Streetman }
170a75f232cSDan Streetman list_add_tail(&node->node_list, node_next);
171a75f232cSDan Streetman
172a75f232cSDan Streetman plist_check_head(head);
173a75f232cSDan Streetman }
174a75f232cSDan Streetman
1758e18faeaSDavidlohr Bueso #ifdef CONFIG_DEBUG_PLIST
1766d55da53SLai Jiangshan #include <linux/sched.h>
177e6017571SIngo Molnar #include <linux/sched/clock.h>
1786d55da53SLai Jiangshan #include <linux/module.h>
1796d55da53SLai Jiangshan #include <linux/init.h>
1806d55da53SLai Jiangshan
1816d55da53SLai Jiangshan static struct plist_node __initdata test_node[241];
1826d55da53SLai Jiangshan
plist_test_check(int nr_expect)1836d55da53SLai Jiangshan static void __init plist_test_check(int nr_expect)
1846d55da53SLai Jiangshan {
1856d55da53SLai Jiangshan struct plist_node *first, *prio_pos, *node_pos;
1866d55da53SLai Jiangshan
1876d55da53SLai Jiangshan if (plist_head_empty(&test_head)) {
1886d55da53SLai Jiangshan BUG_ON(nr_expect != 0);
1896d55da53SLai Jiangshan return;
1906d55da53SLai Jiangshan }
1916d55da53SLai Jiangshan
1926d55da53SLai Jiangshan prio_pos = first = plist_first(&test_head);
1936d55da53SLai Jiangshan plist_for_each(node_pos, &test_head) {
1946d55da53SLai Jiangshan if (nr_expect-- < 0)
1956d55da53SLai Jiangshan break;
1966d55da53SLai Jiangshan if (node_pos == first)
1976d55da53SLai Jiangshan continue;
1986d55da53SLai Jiangshan if (node_pos->prio == prio_pos->prio) {
1996d55da53SLai Jiangshan BUG_ON(!list_empty(&node_pos->prio_list));
2006d55da53SLai Jiangshan continue;
2016d55da53SLai Jiangshan }
2026d55da53SLai Jiangshan
2036d55da53SLai Jiangshan BUG_ON(prio_pos->prio > node_pos->prio);
2046d55da53SLai Jiangshan BUG_ON(prio_pos->prio_list.next != &node_pos->prio_list);
2056d55da53SLai Jiangshan prio_pos = node_pos;
2066d55da53SLai Jiangshan }
2076d55da53SLai Jiangshan
2086d55da53SLai Jiangshan BUG_ON(nr_expect != 0);
2096d55da53SLai Jiangshan BUG_ON(prio_pos->prio_list.next != &first->prio_list);
2106d55da53SLai Jiangshan }
2116d55da53SLai Jiangshan
plist_test_requeue(struct plist_node * node)212a75f232cSDan Streetman static void __init plist_test_requeue(struct plist_node *node)
213a75f232cSDan Streetman {
214a75f232cSDan Streetman plist_requeue(node, &test_head);
215a75f232cSDan Streetman
216a75f232cSDan Streetman if (node != plist_last(&test_head))
217a75f232cSDan Streetman BUG_ON(node->prio == plist_next(node)->prio);
218a75f232cSDan Streetman }
219a75f232cSDan Streetman
plist_test(void)2206d55da53SLai Jiangshan static int __init plist_test(void)
2216d55da53SLai Jiangshan {
2226d55da53SLai Jiangshan int nr_expect = 0, i, loop;
2236d55da53SLai Jiangshan unsigned int r = local_clock();
2246d55da53SLai Jiangshan
22518120627SDan Streetman printk(KERN_DEBUG "start plist test\n");
226732375c6SDima Zavin plist_head_init(&test_head);
2276d55da53SLai Jiangshan for (i = 0; i < ARRAY_SIZE(test_node); i++)
2286d55da53SLai Jiangshan plist_node_init(test_node + i, 0);
2296d55da53SLai Jiangshan
2306d55da53SLai Jiangshan for (loop = 0; loop < 1000; loop++) {
2316d55da53SLai Jiangshan r = r * 193939 % 47629;
2326d55da53SLai Jiangshan i = r % ARRAY_SIZE(test_node);
2336d55da53SLai Jiangshan if (plist_node_empty(test_node + i)) {
2346d55da53SLai Jiangshan r = r * 193939 % 47629;
2356d55da53SLai Jiangshan test_node[i].prio = r % 99;
2366d55da53SLai Jiangshan plist_add(test_node + i, &test_head);
2376d55da53SLai Jiangshan nr_expect++;
2386d55da53SLai Jiangshan } else {
2396d55da53SLai Jiangshan plist_del(test_node + i, &test_head);
2406d55da53SLai Jiangshan nr_expect--;
2416d55da53SLai Jiangshan }
2426d55da53SLai Jiangshan plist_test_check(nr_expect);
243a75f232cSDan Streetman if (!plist_node_empty(test_node + i)) {
244a75f232cSDan Streetman plist_test_requeue(test_node + i);
245a75f232cSDan Streetman plist_test_check(nr_expect);
246a75f232cSDan Streetman }
2476d55da53SLai Jiangshan }
2486d55da53SLai Jiangshan
2496d55da53SLai Jiangshan for (i = 0; i < ARRAY_SIZE(test_node); i++) {
2506d55da53SLai Jiangshan if (plist_node_empty(test_node + i))
2516d55da53SLai Jiangshan continue;
2526d55da53SLai Jiangshan plist_del(test_node + i, &test_head);
2536d55da53SLai Jiangshan nr_expect--;
2546d55da53SLai Jiangshan plist_test_check(nr_expect);
2556d55da53SLai Jiangshan }
2566d55da53SLai Jiangshan
25718120627SDan Streetman printk(KERN_DEBUG "end plist test\n");
2586d55da53SLai Jiangshan return 0;
2596d55da53SLai Jiangshan }
2606d55da53SLai Jiangshan
2616d55da53SLai Jiangshan module_init(plist_test);
2626d55da53SLai Jiangshan
2636d55da53SLai Jiangshan #endif
264