-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PATCH] pi-futex: add plist implementation
Add the priority-sorted list (plist) implementation. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
- Loading branch information
Ingo Molnar
authored and
Linus Torvalds
committed
Jun 28, 2006
1 parent
8eb94f8
commit 77ba89c
Showing
4 changed files
with
372 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
/* | ||
* 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 is a priority-sorted list of nodes; each node has a | ||
* priority from INT_MIN (highest) to INT_MAX (lowest). | ||
* | ||
* Addition is O(K), removal is O(1), change of priority of a node is | ||
* O(K) and K is the number of RT priority levels used in the system. | ||
* (1 <= K <= 99) | ||
* | ||
* This list is really a list of lists: | ||
* | ||
* - The tier 1 list is the prio_list, different priority nodes. | ||
* | ||
* - The tier 2 list is the node_list, serialized nodes. | ||
* | ||
* Simple ASCII art explanation: | ||
* | ||
* |HEAD | | ||
* | | | ||
* |prio_list.prev|<------------------------------------| | ||
* |prio_list.next|<->|pl|<->|pl|<--------------->|pl|<-| | ||
* |10 | |10| |21| |21| |21| |40| (prio) | ||
* | | | | | | | | | | | | | ||
* | | | | | | | | | | | | | ||
* |node_list.next|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-| | ||
* |node_list.prev|<------------------------------------| | ||
* | ||
* The nodes on the prio_list list are sorted by priority to simplify | ||
* the insertion of new nodes. There are no nodes with duplicate | ||
* priorites on the list. | ||
* | ||
* The nodes on the node_list is ordered by priority and can contain | ||
* entries which have the same priority. Those entries are ordered | ||
* FIFO | ||
* | ||
* Addition means: look for the prio_list node in the prio_list | ||
* for the priority of the node and insert it before the node_list | ||
* entry of the next prio_list node. If it is the first node of | ||
* that priority, add it to the prio_list in the right position and | ||
* insert it into the serialized node_list list | ||
* | ||
* Removal means remove it from the node_list and remove it from | ||
* the prio_list if the node_list list_head is non empty. In case | ||
* of removal from the prio_list it must be checked whether other | ||
* entries of the same priority are on the list or not. If there | ||
* is another entry of the same priority then this entry has to | ||
* replace the removed entry on the prio_list. If the entry which | ||
* is removed is the only entry of this priority then a simple | ||
* remove from both list is sufficient. | ||
* | ||
* INT_MIN is the highest priority, 0 is the medium highest, INT_MAX | ||
* is lowest priority. | ||
* | ||
* No locking is done, up to the caller. | ||
* | ||
*/ | ||
#ifndef _LINUX_PLIST_H_ | ||
#define _LINUX_PLIST_H_ | ||
|
||
#include <linux/list.h> | ||
#include <linux/spinlock_types.h> | ||
|
||
struct plist_head { | ||
struct list_head prio_list; | ||
struct list_head node_list; | ||
#ifdef CONFIG_DEBUG_PI_LIST | ||
spinlock_t *lock; | ||
#endif | ||
}; | ||
|
||
struct plist_node { | ||
int prio; | ||
struct plist_head plist; | ||
}; | ||
|
||
#ifdef CONFIG_DEBUG_PI_LIST | ||
# define PLIST_HEAD_LOCK_INIT(_lock) .lock = _lock | ||
#else | ||
# define PLIST_HEAD_LOCK_INIT(_lock) | ||
#endif | ||
|
||
/** | ||
* #PLIST_HEAD_INIT - static struct plist_head initializer | ||
* | ||
* @head: struct plist_head variable name | ||
*/ | ||
#define PLIST_HEAD_INIT(head, _lock) \ | ||
{ \ | ||
.prio_list = LIST_HEAD_INIT((head).prio_list), \ | ||
.node_list = LIST_HEAD_INIT((head).node_list), \ | ||
PLIST_HEAD_LOCK_INIT(&(_lock)) \ | ||
} | ||
|
||
/** | ||
* #PLIST_NODE_INIT - static struct plist_node initializer | ||
* | ||
* @node: struct plist_node variable name | ||
* @__prio: initial node priority | ||
*/ | ||
#define PLIST_NODE_INIT(node, __prio) \ | ||
{ \ | ||
.prio = (__prio), \ | ||
.plist = PLIST_HEAD_INIT((node).plist, NULL), \ | ||
} | ||
|
||
/** | ||
* plist_head_init - dynamic struct plist_head initializer | ||
* | ||
* @head: &struct plist_head pointer | ||
*/ | ||
static inline void | ||
plist_head_init(struct plist_head *head, spinlock_t *lock) | ||
{ | ||
INIT_LIST_HEAD(&head->prio_list); | ||
INIT_LIST_HEAD(&head->node_list); | ||
#ifdef CONFIG_DEBUG_PI_LIST | ||
head->lock = lock; | ||
#endif | ||
} | ||
|
||
/** | ||
* plist_node_init - Dynamic struct plist_node initializer | ||
* | ||
* @node: &struct plist_node pointer | ||
* @prio: initial node priority | ||
*/ | ||
static inline void plist_node_init(struct plist_node *node, int prio) | ||
{ | ||
node->prio = prio; | ||
plist_head_init(&node->plist, NULL); | ||
} | ||
|
||
extern void plist_add(struct plist_node *node, struct plist_head *head); | ||
extern void plist_del(struct plist_node *node, struct plist_head *head); | ||
|
||
/** | ||
* plist_for_each - iterate over the plist | ||
* | ||
* @pos1: the type * to use as a loop counter. | ||
* @head: the head for your list. | ||
*/ | ||
#define plist_for_each(pos, head) \ | ||
list_for_each_entry(pos, &(head)->node_list, plist.node_list) | ||
|
||
/** | ||
* plist_for_each_entry_safe - iterate over a plist of given type safe | ||
* against removal of list entry | ||
* | ||
* @pos1: the type * to use as a loop counter. | ||
* @n1: another type * to use as temporary storage | ||
* @head: the head for your list. | ||
*/ | ||
#define plist_for_each_safe(pos, n, head) \ | ||
list_for_each_entry_safe(pos, n, &(head)->node_list, plist.node_list) | ||
|
||
/** | ||
* plist_for_each_entry - iterate over list of given type | ||
* | ||
* @pos: the type * to use as a loop counter. | ||
* @head: the head for your list. | ||
* @member: the name of the list_struct within the struct. | ||
*/ | ||
#define plist_for_each_entry(pos, head, mem) \ | ||
list_for_each_entry(pos, &(head)->node_list, mem.plist.node_list) | ||
|
||
/** | ||
* plist_for_each_entry_safe - iterate over list of given type safe against | ||
* removal of list entry | ||
* | ||
* @pos: the type * to use as a loop counter. | ||
* @n: another type * to use as temporary storage | ||
* @head: the head for your list. | ||
* @m: the name of the list_struct within the struct. | ||
*/ | ||
#define plist_for_each_entry_safe(pos, n, head, m) \ | ||
list_for_each_entry_safe(pos, n, &(head)->node_list, m.plist.node_list) | ||
|
||
/** | ||
* plist_head_empty - return !0 if a plist_head is empty | ||
* | ||
* @head: &struct plist_head pointer | ||
*/ | ||
static inline int plist_head_empty(const struct plist_head *head) | ||
{ | ||
return list_empty(&head->node_list); | ||
} | ||
|
||
/** | ||
* plist_node_empty - return !0 if plist_node is not on a list | ||
* | ||
* @node: &struct plist_node pointer | ||
*/ | ||
static inline int plist_node_empty(const struct plist_node *node) | ||
{ | ||
return plist_head_empty(&node->plist); | ||
} | ||
|
||
/* All functions below assume the plist_head is not empty. */ | ||
|
||
/** | ||
* plist_first_entry - get the struct for the first entry | ||
* | ||
* @ptr: the &struct plist_head pointer. | ||
* @type: the type of the struct this is embedded in. | ||
* @member: the name of the list_struct within the struct. | ||
*/ | ||
#ifdef CONFIG_DEBUG_PI_LIST | ||
# define plist_first_entry(head, type, member) \ | ||
({ \ | ||
WARN_ON(plist_head_empty(head)); \ | ||
container_of(plist_first(head), type, member); \ | ||
}) | ||
#else | ||
# define plist_first_entry(head, type, member) \ | ||
container_of(plist_first(head), type, member) | ||
#endif | ||
|
||
/** | ||
* plist_first - return the first node (and thus, highest priority) | ||
* | ||
* @head: the &struct plist_head pointer | ||
* | ||
* Assumes the plist is _not_ empty. | ||
*/ | ||
static inline struct plist_node* plist_first(const struct plist_head *head) | ||
{ | ||
return list_entry(head->node_list.next, | ||
struct plist_node, plist.node_list); | ||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* 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 | ||
|
||
static void plist_check_prev_next(struct list_head *t, struct list_head *p, | ||
struct list_head *n) | ||
{ | ||
if (n->prev != p || p->next != n) { | ||
printk("top: %p, n: %p, p: %p\n", t, t->next, t->prev); | ||
printk("prev: %p, n: %p, p: %p\n", p, p->next, p->prev); | ||
printk("next: %p, n: %p, p: %p\n", n, n->next, n->prev); | ||
WARN_ON(1); | ||
} | ||
} | ||
|
||
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) | ||
{ | ||
WARN_ON(!head->lock); | ||
if (head->lock) | ||
WARN_ON_SMP(!spin_is_locked(head->lock)); | ||
plist_check_list(&head->prio_list); | ||
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) | ||
{ | ||
struct plist_node *iter; | ||
|
||
plist_check_head(head); | ||
WARN_ON(!plist_node_empty(node)); | ||
|
||
list_for_each_entry(iter, &head->prio_list, plist.prio_list) { | ||
if (node->prio < iter->prio) | ||
goto lt_prio; | ||
else if (node->prio == iter->prio) { | ||
iter = list_entry(iter->plist.prio_list.next, | ||
struct plist_node, plist.prio_list); | ||
goto eq_prio; | ||
} | ||
} | ||
|
||
lt_prio: | ||
list_add_tail(&node->plist.prio_list, &iter->plist.prio_list); | ||
eq_prio: | ||
list_add_tail(&node->plist.node_list, &iter->plist.node_list); | ||
|
||
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); | ||
|
||
if (!list_empty(&node->plist.prio_list)) { | ||
struct plist_node *next = plist_first(&node->plist); | ||
|
||
list_move_tail(&next->plist.prio_list, &node->plist.prio_list); | ||
list_del_init(&node->plist.prio_list); | ||
} | ||
|
||
list_del_init(&node->plist.node_list); | ||
|
||
plist_check_head(head); | ||
} |