From bb5ddc17721bb9a6df50014d916445bd7b9503f0 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Sun, 18 Sep 2022 13:12:49 +0200 Subject: [PATCH] add hash, plist, etc (untested) --- c/Makefile | 8 +- c/include/bits.h | 39 +++-- c/include/bug.h | 70 +++++++++ c/include/container-of.h | 30 ++++ c/include/debug.h | 1 + c/include/hash.h | 101 +++++++++++++ c/include/hashtable.h | 204 ++++++++++++++++++++++++++ c/include/likely.h | 18 +++ c/include/list.h | 9 +- c/include/plist.h | 301 +++++++++++++++++++++++++++++++++++++++ c/plist.c | 173 ++++++++++++++++++++++ 11 files changed, 935 insertions(+), 19 deletions(-) create mode 100644 c/include/bug.h create mode 100644 c/include/container-of.h create mode 100644 c/include/hash.h create mode 100644 c/include/hashtable.h create mode 100644 c/include/likely.h create mode 100644 c/include/plist.h create mode 100644 c/plist.c diff --git a/c/Makefile b/c/Makefile index 2936020..56e4580 100644 --- a/c/Makefile +++ b/c/Makefile @@ -13,7 +13,7 @@ SHELL := /bin/bash CC := gcc -CFLAGS += -std=gnu99 +CFLAGS += -std=gnu11 CFLAGS += -O2 CFLAGS += -g CFLAGS += -Wall @@ -46,7 +46,7 @@ LDLIB := -l$(LIB) export LD_LIBRARY_PATH = $(LIBDIR) -.PHONY: all libs clean dirs +.PHONY: all libs clean dirs bear all: libs @@ -84,3 +84,7 @@ $(OBJDIR)/%.o: %.c .c.s: @echo generating $@ @$(CC) -S -fverbose-asm $(CFLAGS) -I $(INCDIR) $< -o $@ + +bear: clean + @echo building ccls language server compilation database + @bear -- make diff --git a/c/include/bits.h b/c/include/bits.h index 6614848..ef26e7c 100644 --- a/c/include/bits.h +++ b/c/include/bits.h @@ -55,7 +55,7 @@ typedef signed char schar; /* count trailing zeroes : 00101000 -> 3 * ^^^ */ -static inline int ctz64(u64 n) +static __always_inline int ctz64(u64 n) { # if __has_builtin(__builtin_ctzl) # ifdef DEBUG_BITS @@ -77,7 +77,7 @@ static inline int ctz64(u64 n) # endif } -static inline int ctz32(u32 n) +static __always_inline int ctz32(u32 n) { # if __has_builtin(__builtin_ctz) # ifdef DEBUG_BITS @@ -99,10 +99,10 @@ static inline int ctz32(u32 n) # endif } -/* count leading zeroes : 00101000 -> 2 - * ^^ +/* clz - count leading zeroes : 00101000 -> 2 + * ^^ */ -static inline int clz64(u64 n) +static __always_inline int clz64(u64 n) { # if __has_builtin(__builtin_clzl) # ifdef DEBUG_BITS @@ -126,7 +126,7 @@ static inline int clz64(u64 n) # endif } -static inline int clz32(u32 n) +static __always_inline int clz32(u32 n) { # if __has_builtin(__builtin_clz) # ifdef DEBUG_BITS @@ -149,10 +149,27 @@ static inline int clz32(u32 n) # endif } +/* fls - find last set : 00101000 -> 6 + * ^ + */ +static __always_inline int fls64(u64 n) +{ + if (!n) + return 0; + return 64 - clz64(n); +} + +static __always_inline int fls32(u32 n) +{ + if (!n) + return 0; + return 32 - clz32(n); +} + /* find first set : 00101000 -> 4 * ^ */ -static inline uint ffs64(u64 n) +static __always_inline uint ffs64(u64 n) { # if __has_builtin(__builtin_ffsl) # ifdef DEBUG_BITS @@ -176,7 +193,7 @@ static inline uint ffs64(u64 n) # endif } -static inline uint ffs32(u32 n) +static __always_inline uint ffs32(u32 n) { # if __has_builtin(__builtin_ffs) # ifdef DEBUG_BITS @@ -203,7 +220,7 @@ static inline uint ffs32(u32 n) /* count set bits: 10101000 -> 3 * ^ ^ ^ */ -static inline int popcount64(u64 n) +static __always_inline int popcount64(u64 n) { # if __has_builtin(__builtin_popcountl) # ifdef DEBUG_BITS @@ -224,7 +241,7 @@ static inline int popcount64(u64 n) # endif } -static inline int popcount32(u32 n) +static __always_inline int popcount32(u32 n) { # if __has_builtin(__builtin_popcount) # ifdef DEBUG_BITS @@ -245,6 +262,8 @@ static inline int popcount32(u32 n) # endif } + + /** bit_for_each - iterate over an u64/u32 bits * @pos: an int used as current bit * @tmp: a temp u64/u32 used as temporary storage diff --git a/c/include/bug.h b/c/include/bug.h new file mode 100644 index 0000000..2c4566f --- /dev/null +++ b/c/include/bug.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BR_BUG_H +#define _BR_BUG_H + +#include +#include +#include +#include "likely.h" +#include "debug.h" + +/* inspired by Linux kernel's */ + +#define panic() exit(0xff) + +/* + * Don't use BUG() or BUG_ON() unless there's really no way out; one + * example might be detecting data structure corruption in the middle + * of an operation that can't be backed out of. If the (sub)system + * can somehow continue operating, perhaps with reduced functionality, + * it's probably not BUG-worthy. + * + * If you're tempted to BUG(), think again: is completely giving up + * really the *only* solution? There are usually better options, where + * users don't need to reboot ASAP and can mostly shut down cleanly. + */ +#define BUG() do { \ + fprintf(stderr, "BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ + panic(); \ + } while (0) + +#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0) + +/* + * WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report + * significant kernel issues that need prompt attention if they should ever + * appear at runtime. + * + * Do not use these macros when checking for invalid external inputs + * (e.g. invalid system call arguments, or invalid data coming from + * network/devices), and on transient conditions like ENOMEM or EAGAIN. + * These macros should be used for recoverable kernel issues only. + * For invalid external inputs, transient conditions, etc use + * pr_err[_once/_ratelimited]() followed by dump_stack(), if necessary. + * Do not include "BUG"/"WARNING" in format strings manually to make these + * conditions distinguishable from kernel issues. + * + * Use the versions with printk format strings to provide better diagnostics. + */ +#define __WARN() do { \ + fprintf(stderr, "WARNING: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ + } while (0) +#define __WARN_printf(arg...) do { \ + vfprintf(stderr, arg); \ + } while (0) + +#define WARN_ON(condition) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + __WARN(); \ + unlikely(__ret_warn_on); \ + }) + +#define WARN(condition, format...) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + __WARN_printf(format); \ + unlikely(__ret_warn_on); \ + }) + +#endif /* _BR_BUG_H */ diff --git a/c/include/container-of.h b/c/include/container-of.h new file mode 100644 index 0000000..1c7f54f --- /dev/null +++ b/c/include/container-of.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* adaptation of Linux kernel's + */ +#ifndef _BR_CONTAINER_OF_H +#define _BR_CONTAINER_OF_H + +/* Are two types/vars the same type (ignoring qualifiers)? */ +#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) + +/** + * typeof_member - + */ +#define typeof_member(T, m) typeof(((T*)0)->m) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + void *__mptr = (void *)(ptr); \ + _Static_assert(__same_type(*(ptr), ((type *)0)->member) || \ + __same_type(*(ptr), void), \ + "pointer type mismatch in container_of()"); \ + ((type *)(__mptr - offsetof(type, member))); }) + +#endif /* BR_CONTAINER_OF_H */ diff --git a/c/include/debug.h b/c/include/debug.h index 087304c..fcceefd 100644 --- a/c/include/debug.h +++ b/c/include/debug.h @@ -21,6 +21,7 @@ #define _unused __attribute__((__unused__)) #define _printf __attribute__ ((format (printf, 6, 7))) + #ifdef DEBUG_DEBUG void debug_init(u32 level); void debug_level_set(u32 level); diff --git a/c/include/hash.h b/c/include/hash.h new file mode 100644 index 0000000..5ce4330 --- /dev/null +++ b/c/include/hash.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _BR_HASH_H +#define _BR_HASH_H +/* adaptation of Linux kernel's + */ + +/* Fast hashing routine for ints, longs and pointers. + (C) 2002 Nadia Yvette Chambers, IBM */ + +#include +#include +#include "bits.h" +/* + * The "GOLDEN_RATIO_PRIME" is used in ifs/btrfs/brtfs_inode.h and + * fs/inode.c. It's not actually prime any more (the previous primes + * were actively bad for hashing), but the name remains. + */ +#if __BITS_PER_LONG == 32 +#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32 +#define hash_long(val, bits) hash_32(val, bits) +#elif __BITS_PER_LONG == 64 +#define hash_long(val, bits) hash_64(val, bits) +#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_64 +#else +#error Wordsize not 32 or 64 +#endif + +/* + * This hash multiplies the input by a large odd number and takes the + * high bits. Since multiplication propagates changes to the most + * significant end only, it is essential that the high bits of the + * product be used for the hash value. + * + * Chuck Lever verified the effectiveness of this technique: + * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf + * + * Although a random odd number will do, it turns out that the golden + * ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice + * properties. (See Knuth vol 3, section 6.4, exercise 9.) + * + * These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2, + * which is very slightly easier to multiply by and makes no + * difference to the hash distribution. + */ +#define GOLDEN_RATIO_32 0x61C88647 +#define GOLDEN_RATIO_64 0x61C8864680B583EBull + +/* + * The _generic versions exist only so lib/test_hash.c can compare + * the arch-optimized versions with the generic. + * + * Note that if you change these, any that aren't updated + * to match need to have their HAVE_ARCH_* define values updated so the + * self-test will not false-positive. + */ +#ifndef HAVE_ARCH__HASH_32 +#define __hash_32 __hash_32_generic +#endif +static inline u32 __hash_32_generic(u32 val) +{ + return val * GOLDEN_RATIO_32; +} + +static inline u32 hash_32(u32 val, unsigned int bits) +{ + /* High bits are more random, so use them. */ + return __hash_32(val) >> (32 - bits); +} + +#ifndef HAVE_ARCH_HASH_64 +#define hash_64 hash_64_generic +#endif +static __always_inline u32 hash_64_generic(u64 val, unsigned int bits) +{ +#if __BITS_PER_LONG == 64 + /* 64x64-bit multiply is efficient on all 64-bit processors */ + return val * GOLDEN_RATIO_64 >> (64 - bits); +#else + /* Hash 64 bits using only 32x32-bit multiply. */ + return hash_32((u32)val ^ __hash_32(val >> 32), bits); +#endif +} + +static inline u32 hash_ptr(const void *ptr, unsigned int bits) +{ + return hash_long((unsigned long)ptr, bits); +} + +/* This really should be called fold32_ptr; it does no hashing to speak of. */ +static inline u32 hash32_ptr(const void *ptr) +{ + unsigned long val = (unsigned long)ptr; + +#if __BITS_PER_LONG == 64 + val ^= (val >> 32); +#endif + return (u32)val; +} + +#endif /* _BR_HASH_H */ diff --git a/c/include/hashtable.h b/c/include/hashtable.h new file mode 100644 index 0000000..9393583 --- /dev/null +++ b/c/include/hashtable.h @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* adaptation of Linux kernel's + */ + + +/* + * Statically sized hash table implementation + * (C) 2012 Sasha Levin + */ + +#ifndef _LINUX_HASHTABLE_H +#define _LINUX_HASHTABLE_H + +#include "list.h" +#include +#include +#include "hash.h" +//#include + +#define DEFINE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DEFINE_READ_MOSTLY_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] __read_mostly = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DECLARE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] + +#define HASH_SIZE(name) (ARRAY_SIZE(name)) +#define HASH_BITS(name) ilog2(HASH_SIZE(name)) + +/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ +#define hash_min(val, bits) \ + (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) + +static inline void __hash_init(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + INIT_HLIST_HEAD(&ht[i]); +} + +/** + * hash_init - initialize a hash table + * @hashtable: hashtable to be initialized + * + * Calculates the size of the hashtable from the given parameter, otherwise + * same as hash_init_size. + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_add - add an object to a hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add(hashtable, node, key) \ + hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_add_rcu - add an object to a rcu enabled hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add_rcu(hashtable, node, key) \ + hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_hashed - check whether an object is in any hashtable + * @node: the &struct hlist_node of the object to be checked + */ +static inline bool hash_hashed(struct hlist_node *node) +{ + return !hlist_unhashed(node); +} + +static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + if (!hlist_empty(&ht[i])) + return false; + + return true; +} + +/** + * hash_empty - check whether a hashtable is empty + * @hashtable: hashtable to check + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_del - remove an object from a hashtable + * @node: &struct hlist_node of the object to remove + */ +static inline void hash_del(struct hlist_node *node) +{ + hlist_del_init(node); +} + +/** + * hash_for_each - iterate over a hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each(name, bkt, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry(obj, &name[bkt], member) + +/** + * hash_for_each_rcu - iterate over a rcu enabled hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_rcu(name, bkt, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_rcu(obj, &name[bkt], member) + +/** + * hash_for_each_safe - iterate over a hashtable safe against removal of + * hash entry + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @tmp: a &struct hlist_node used for temporary storage + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_safe(name, bkt, tmp, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_safe(obj, tmp, &name[bkt], member) + +/** + * hash_for_each_possible - iterate over all possible objects hashing to the + * same bucket + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible(name, obj, member, key) \ + hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member) + +/** + * hash_for_each_possible_rcu - iterate over all possible objects hashing to the + * same bucket in an rcu enabled hashtable + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_rcu(name, obj, member, key, cond...) \ + hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\ + member, ## cond) + +/** + * hash_for_each_possible_rcu_notrace - iterate over all possible objects hashing + * to the same bucket in an rcu enabled hashtable in a rcu enabled hashtable + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + * + * This is the same as hash_for_each_possible_rcu() except that it does + * not do any RCU debugging or tracing. + */ +#define hash_for_each_possible_rcu_notrace(name, obj, member, key) \ + hlist_for_each_entry_rcu_notrace(obj, \ + &name[hash_min(key, HASH_BITS(name))], member) + +/** + * hash_for_each_possible_safe - iterate over all possible objects hashing to the + * same bucket safe against removals + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @tmp: a &struct hlist_node used for temporary storage + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ + hlist_for_each_entry_safe(obj, tmp,\ + &name[hash_min(key, HASH_BITS(name))], member) + + +#endif diff --git a/c/include/likely.h b/c/include/likely.h new file mode 100644 index 0000000..a5d151d --- /dev/null +++ b/c/include/likely.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* taken from Kernel's - * Main change is that I don't use READ_ONCE and WRITE_ONCE - * See https://www.kernel.org/doc/Documentation/memory-barriers.txt + * */ #ifndef __BR_LIST_H @@ -11,6 +10,7 @@ #include #include #include "rwonce.h" +#include "container-of.h" /************ originally in */ struct list_head { @@ -33,11 +33,6 @@ struct hlist_node { #define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) #define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA) -/************ originally in */ -#define container_of(ptr, type, member) ({ \ - void *__mptr = (void *)(ptr); \ - ((type *)(__mptr - offsetof(type, member))); }) - /* * Circular doubly linked list implementation. * diff --git a/c/include/plist.h b/c/include/plist.h new file mode 100644 index 0000000..df0e88a --- /dev/null +++ b/c/include/plist.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Descending-priority-sorted double-linked list + * + * (C) 2002-2003 Intel Corp + * Inaky Perez-Gonzalez . + * + * 2001-2005 (c) MontaVista Software, Inc. + * Daniel Walker + * + * (C) 2005 Thomas Gleixner + * + * Simplifications of the original code by + * Oleg Nesterov + * + * 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: + * + * pl:prio_list (only for plist_node) + * nl:node_list + * HEAD| NODE(S) + * | + * ||------------------------------------| + * ||->|pl|<->|pl|<--------------->|pl|<-| + * | |10| |21| |21| |21| |40| (prio) + * | | | | | | | | | | | + * | | | | | | | | | | | + * |->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-| + * |-------------------------------------------| + * + * 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 are 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 "container-of.h" +#include "list.h" +//#include + +// #include + +struct plist_head { + struct list_head node_list; +}; + +struct plist_node { + int prio; + struct list_head prio_list; + struct list_head node_list; +}; + +/** + * PLIST_HEAD_INIT - static struct plist_head initializer + * @head: struct plist_head variable name + */ +#define PLIST_HEAD_INIT(head) \ + { \ + .node_list = LIST_HEAD_INIT((head).node_list) \ + } + +/** + * PLIST_HEAD - declare and init plist_head + * @head: name for struct plist_head variable + */ +#define PLIST_HEAD(head) \ + struct plist_head head = PLIST_HEAD_INIT(head) + +/** + * 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), \ + .prio_list = LIST_HEAD_INIT((node).prio_list), \ + .node_list = LIST_HEAD_INIT((node).node_list), \ + } + +/** + * plist_head_init - dynamic struct plist_head initializer + * @head: &struct plist_head pointer + */ +static inline void +plist_head_init(struct plist_head *head) +{ + INIT_LIST_HEAD(&head->node_list); +} + +/** + * 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; + INIT_LIST_HEAD(&node->prio_list); + INIT_LIST_HEAD(&node->node_list); +} + +extern void plist_add(struct plist_node *node, struct plist_head *head); +extern void plist_del(struct plist_node *node, struct plist_head *head); + +extern void plist_requeue(struct plist_node *node, struct plist_head *head); + +/** + * plist_for_each - iterate over the plist + * @pos: 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, node_list) + +/** + * plist_for_each_continue - continue iteration over the plist + * @pos: the type * to use as a loop cursor + * @head: the head for your list + * + * Continue to iterate over plist, continuing after the current position. + */ +#define plist_for_each_continue(pos, head) \ + list_for_each_entry_continue(pos, &(head)->node_list, node_list) + +/** + * plist_for_each_safe - iterate safely over a plist of given type + * @pos: the type * to use as a loop counter + * @n: another type * to use as temporary storage + * @head: the head for your list + * + * Iterate over a plist of given type, safe against removal of list entry. + */ +#define plist_for_each_safe(pos, n, head) \ + list_for_each_entry_safe(pos, n, &(head)->node_list, 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 + * @mem: the name of the list_head within the struct + */ +#define plist_for_each_entry(pos, head, mem) \ + list_for_each_entry(pos, &(head)->node_list, mem.node_list) + +/** + * plist_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor + * @head: the head for your list + * @m: the name of the list_head within the struct + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define plist_for_each_entry_continue(pos, head, m) \ + list_for_each_entry_continue(pos, &(head)->node_list, m.node_list) + +/** + * plist_for_each_entry_safe - iterate safely over list of given type + * @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_head within the struct + * + * Iterate over list of given type, safe against removal of list entry. + */ +#define plist_for_each_entry_safe(pos, n, head, m) \ + list_for_each_entry_safe(pos, n, &(head)->node_list, m.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 list_empty(&node->node_list); +} + +/* All functions below assume the plist_head is not empty. */ + +/** + * plist_first_entry - get the struct for the first entry + * @head: the &struct plist_head pointer + * @type: the type of the struct this is embedded in + * @member: the name of the list_head within the struct + */ +#ifdef CONFIG_DEBUG_PLIST +# 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_last_entry - get the struct for the last entry + * @head: the &struct plist_head pointer + * @type: the type of the struct this is embedded in + * @member: the name of the list_head within the struct + */ +#ifdef CONFIG_DEBUG_PLIST +# define plist_last_entry(head, type, member) \ + ({ \ + WARN_ON(plist_head_empty(head)); \ + container_of(plist_last(head), type, member); \ + }) +#else +# define plist_last_entry(head, type, member) \ + container_of(plist_last(head), type, member) +#endif + +/** + * plist_next - get the next entry in list + * @pos: the type * to cursor + */ +#define plist_next(pos) \ + list_next_entry(pos, node_list) + +/** + * plist_prev - get the prev entry in list + * @pos: the type * to cursor + */ +#define plist_prev(pos) \ + list_prev_entry(pos, node_list) + +/** + * 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, node_list); +} + +/** + * plist_last - return the last node (and thus, lowest priority) + * @head: the &struct plist_head pointer + * + * Assumes the plist is _not_ empty. + */ +static inline struct plist_node *plist_last(const struct plist_head *head) +{ + return list_entry(head->node_list.prev, + struct plist_node, node_list); +} + +#endif diff --git a/c/plist.c b/c/plist.c new file mode 100644 index 0000000..72e1fb1 --- /dev/null +++ b/c/plist.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * adapted from Linux kernel lib/plist.c + * + * Descending-priority-sorted double-linked list + * + * (C) 2002-2003 Intel Corp + * Inaky Perez-Gonzalez . + * + * 2001-2005 (c) MontaVista Software, Inc. + * Daniel Walker + * + * (C) 2005 Thomas Gleixner + * + * Simplifications of the original code by + * Oleg Nesterov + * + * 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 "plist.h" +#include "bug.h" + +#ifdef DEBUG_PLIST + +static struct plist_head test_head; + +static void plist_check_prev_next(struct list_head *t, struct list_head *p, + struct list_head *n) +{ + 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); +} + +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) +{ + if (!plist_head_empty(head)) + plist_check_list(&plist_first(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 *first, *iter, *prev = NULL; + struct list_head *node_next = &head->node_list; + + plist_check_head(head); + WARN_ON(!plist_node_empty(node)); + WARN_ON(!list_empty(&node->prio_list)); + + 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; + } + + 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); + + 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->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); + + /* 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); + } + + list_del_init(&node->node_list); + + plist_check_head(head); +} + +/** + * plist_requeue - Requeue @node at end of same-prio entries. + * + * This is essentially an optimized plist_del() followed by + * plist_add(). It moves an entry already in the plist to + * after any other same-priority entries. + * + * @node: &struct plist_node pointer - entry to be moved + * @head: &struct plist_head pointer - list head + */ +void plist_requeue(struct plist_node *node, struct plist_head *head) +{ + struct plist_node *iter; + struct list_head *node_next = &head->node_list; + + plist_check_head(head); + BUG_ON(plist_head_empty(head)); + BUG_ON(plist_node_empty(node)); + + if (node == plist_last(head)) + return; + + iter = plist_next(node); + + if (node->prio != iter->prio) + return; + + plist_del(node, head); + + plist_for_each_continue(iter, head) { + if (node->prio != iter->prio) { + node_next = &iter->node_list; + break; + } + } + list_add_tail(&node->node_list, node_next); + + plist_check_head(head); +}