diff options
| author | Ingo Molnar <mingo@elte.hu> | 2006-06-27 02:54:51 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-27 17:32:46 -0700 | 
| commit | 77ba89c5cf28d5d98a3cae17f67a3e42b102cc25 (patch) | |
| tree | d487b536522574ab183cc600b62008c60db85b59 | |
| parent | 8eb94f80dd2da5977c35cd094f0802c1501a12cd (diff) | |
| download | olio-linux-3.10-77ba89c5cf28d5d98a3cae17f67a3e42b102cc25.tar.xz olio-linux-3.10-77ba89c5cf28d5d98a3cae17f67a3e42b102cc25.zip  | |
[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>
| -rw-r--r-- | include/linux/plist.h | 247 | ||||
| -rw-r--r-- | lib/Kconfig | 6 | ||||
| -rw-r--r-- | lib/Makefile | 1 | ||||
| -rw-r--r-- | lib/plist.c | 118 | 
4 files changed, 372 insertions, 0 deletions
diff --git a/include/linux/plist.h b/include/linux/plist.h new file mode 100644 index 00000000000..3404faef542 --- /dev/null +++ b/include/linux/plist.h @@ -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 diff --git a/lib/Kconfig b/lib/Kconfig index 3de93357f5a..f6299342b88 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -86,4 +86,10 @@ config TEXTSEARCH_BM  config TEXTSEARCH_FSM  	tristate +# +# plist support is select#ed if needed +# +config PLIST +	boolean +  endmenu diff --git a/lib/Makefile b/lib/Makefile index 79358ad1f11..10c13c9d782 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -25,6 +25,7 @@ lib-$(CONFIG_SEMAPHORE_SLEEPERS) += semaphore-sleepers.o  lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o  lib-$(CONFIG_GENERIC_HWEIGHT) += hweight.o  obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o +obj-$(CONFIG_PLIST) += plist.o  obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o  ifneq ($(CONFIG_HAVE_DEC_LOCK),y) diff --git a/lib/plist.c b/lib/plist.c new file mode 100644 index 00000000000..3074a02272f --- /dev/null +++ b/lib/plist.c @@ -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); +}  |