2006-06-27 13:54:51 +04:00
/*
* lib / plist . c
*
* Descending - priority - sorted double - linked list
*
* ( C ) 2002 - 2003 Intel Corp
* Inaky Perez - Gonzalez < inaky . perez - gonzalez @ intel . com > .
*
* 2001 - 2005 ( c ) MontaVista Software , Inc .
* Daniel Walker < dwalker @ mvista . com >
*
* ( C ) 2005 Thomas Gleixner < tglx @ linutronix . de >
*
* Simplifications of the original code by
* Oleg Nesterov < oleg @ tv - sign . ru >
*
* Licensed under the FSF ' s GNU Public License v2 or later .
*
* Based on simple lists ( include / linux / list . h ) .
*
* This file contains the add / del functions which are considered to
* be too large to inline . See include / linux / plist . h for further
* information .
*/
# include <linux/plist.h>
# include <linux/spinlock.h>
# ifdef CONFIG_DEBUG_PI_LIST
2010-12-21 12:55:18 +03:00
static struct plist_head test_head ;
2006-06-27 13:54:51 +04:00
static void plist_check_prev_next ( struct list_head * t , struct list_head * p ,
struct list_head * n )
{
2008-07-26 06:45:39 +04:00
WARN ( n - > prev ! = p | | p - > next ! = n ,
" top: %p, n: %p, p: %p \n "
" prev: %p, n: %p, p: %p \n "
" next: %p, n: %p, p: %p \n " ,
t , t - > next , t - > prev ,
p , p - > next , p - > prev ,
n , n - > next , n - > prev ) ;
2006-06-27 13:54:51 +04:00
}
static void plist_check_list ( struct list_head * top )
{
struct list_head * prev = top , * next = top - > next ;
plist_check_prev_next ( top , prev , next ) ;
while ( next ! = top ) {
prev = next ;
next = prev - > next ;
plist_check_prev_next ( top , prev , next ) ;
}
}
static void plist_check_head ( struct plist_head * head )
{
2010-12-21 12:55:14 +03:00
if ( ! plist_head_empty ( head ) )
plist_check_list ( & plist_first ( head ) - > prio_list ) ;
2006-06-27 13:54:51 +04:00
plist_check_list ( & head - > node_list ) ;
}
# else
# define plist_check_head(h) do { } while (0)
# endif
/**
* plist_add - add @ node to @ head
*
* @ node : & struct plist_node pointer
* @ head : & struct plist_head pointer
*/
void plist_add ( struct plist_node * node , struct plist_head * head )
{
2010-12-21 12:55:14 +03:00
struct plist_node * first , * iter , * prev = NULL ;
struct list_head * node_next = & head - > node_list ;
2006-06-27 13:54:51 +04:00
plist_check_head ( head ) ;
WARN_ON ( ! plist_node_empty ( node ) ) ;
2010-12-21 12:55:14 +03:00
WARN_ON ( ! list_empty ( & node - > prio_list ) ) ;
2006-06-27 13:54:51 +04:00
2010-12-21 12:55:14 +03:00
if ( plist_head_empty ( head ) )
goto ins_node ;
first = iter = plist_first ( head ) ;
do {
if ( node - > prio < iter - > prio ) {
node_next = & iter - > node_list ;
break ;
2006-06-27 13:54:51 +04:00
}
2010-12-21 12:55:14 +03:00
prev = iter ;
iter = list_entry ( iter - > prio_list . next ,
struct plist_node , prio_list ) ;
} while ( iter ! = first ) ;
if ( ! prev | | prev - > prio ! = node - > prio )
list_add_tail ( & node - > prio_list , & iter - > prio_list ) ;
ins_node :
list_add_tail ( & node - > node_list , node_next ) ;
2006-06-27 13:54:51 +04:00
plist_check_head ( head ) ;
}
/**
* plist_del - Remove a @ node from plist .
*
* @ node : & struct plist_node pointer - entry to be removed
* @ head : & struct plist_head pointer - list head
*/
void plist_del ( struct plist_node * node , struct plist_head * head )
{
plist_check_head ( head ) ;
2010-12-21 12:55:14 +03:00
if ( ! list_empty ( & node - > prio_list ) ) {
if ( node - > node_list . next ! = & head - > node_list ) {
struct plist_node * next ;
next = list_entry ( node - > node_list . next ,
struct plist_node , node_list ) ;
2006-06-27 13:54:51 +04:00
2010-12-21 12:55:14 +03:00
/* add the next plist_node into prio_list */
if ( list_empty ( & next - > prio_list ) )
list_add ( & next - > prio_list , & node - > prio_list ) ;
}
list_del_init ( & node - > prio_list ) ;
2006-06-27 13:54:51 +04:00
}
2010-12-21 12:55:14 +03:00
list_del_init ( & node - > node_list ) ;
2006-06-27 13:54:51 +04:00
plist_check_head ( head ) ;
}
2010-12-21 12:55:18 +03:00
# ifdef CONFIG_DEBUG_PI_LIST
# include <linux/sched.h>
# include <linux/module.h>
# include <linux/init.h>
static struct plist_node __initdata test_node [ 241 ] ;
static void __init plist_test_check ( int nr_expect )
{
struct plist_node * first , * prio_pos , * node_pos ;
if ( plist_head_empty ( & test_head ) ) {
BUG_ON ( nr_expect ! = 0 ) ;
return ;
}
prio_pos = first = plist_first ( & test_head ) ;
plist_for_each ( node_pos , & test_head ) {
if ( nr_expect - - < 0 )
break ;
if ( node_pos = = first )
continue ;
if ( node_pos - > prio = = prio_pos - > prio ) {
BUG_ON ( ! list_empty ( & node_pos - > prio_list ) ) ;
continue ;
}
BUG_ON ( prio_pos - > prio > node_pos - > prio ) ;
BUG_ON ( prio_pos - > prio_list . next ! = & node_pos - > prio_list ) ;
prio_pos = node_pos ;
}
BUG_ON ( nr_expect ! = 0 ) ;
BUG_ON ( prio_pos - > prio_list . next ! = & first - > prio_list ) ;
}
static int __init plist_test ( void )
{
int nr_expect = 0 , i , loop ;
unsigned int r = local_clock ( ) ;
printk ( KERN_INFO " start plist test \n " ) ;
2011-07-08 04:27:59 +04:00
plist_head_init ( & test_head ) ;
2010-12-21 12:55:18 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( test_node ) ; i + + )
plist_node_init ( test_node + i , 0 ) ;
for ( loop = 0 ; loop < 1000 ; loop + + ) {
r = r * 193939 % 47629 ;
i = r % ARRAY_SIZE ( test_node ) ;
if ( plist_node_empty ( test_node + i ) ) {
r = r * 193939 % 47629 ;
test_node [ i ] . prio = r % 99 ;
plist_add ( test_node + i , & test_head ) ;
nr_expect + + ;
} else {
plist_del ( test_node + i , & test_head ) ;
nr_expect - - ;
}
plist_test_check ( nr_expect ) ;
}
for ( i = 0 ; i < ARRAY_SIZE ( test_node ) ; i + + ) {
if ( plist_node_empty ( test_node + i ) )
continue ;
plist_del ( test_node + i , & test_head ) ;
nr_expect - - ;
plist_test_check ( nr_expect ) ;
}
printk ( KERN_INFO " end plist test \n " ) ;
return 0 ;
}
module_init ( plist_test ) ;
# endif