| 1 | /* @title: Locked list */ |
| 2 | #pragma once |
| 3 | #include <enforce.h> |
| 4 | #include <structures/list.h> |
| 5 | #include <sync/spinlock.h> |
| 6 | |
| 7 | /* Doubly linked list with a lock and counter for elements */ |
| 8 | |
| 9 | struct locked_list { |
| 10 | struct spinlock lock; |
| 11 | struct list_head list TSA_GUARDED_BY(lock); |
| 12 | size_t num_elems : 63; |
| 13 | bool lock_high : 1; |
| 14 | }; |
| 15 | |
| 16 | static inline enum irql locked_list_lock(struct locked_list *ll) |
| 17 | TSA_ACQUIRES_SPIN(&ll->lock) { |
| 18 | if (ll->lock_high) { |
| 19 | return spin_lock_high(&ll->lock); |
| 20 | } else { |
| 21 | return spin_lock(&ll->lock); |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | static inline void locked_list_unlock(struct locked_list *ll, enum irql irql) |
| 26 | TSA_RELEASES_SPIN(&ll->lock) { |
| 27 | spin_unlock(&ll->lock, irql); |
| 28 | } |
| 29 | |
| 30 | #define LOCKED_LIST_INIT(ll, irq_disable) \ |
| 31 | (struct locked_list) { \ |
| 32 | .list = LIST_HEAD_INIT(ll.list), .lock = SPINLOCK_INIT, \ |
| 33 | .num_elems = ATOMIC_VAR_INIT(0), .lock_high = irq_disable \ |
| 34 | } |
| 35 | |
| 36 | #define LOCKED_LIST_DEFINE(name, irq_disable) \ |
| 37 | struct locked_list name = LOCKED_LIST_INIT(name, irq_disable) |
| 38 | |
| 39 | #define LOCKED_LIST_INIT_IRQ_DISABLE true |
| 40 | #define LOCKED_LIST_INIT_NORMAL false |
| 41 | #define LOCKED_LIST_DO(ll, action) \ |
| 42 | enum irql __macro_irql = locked_list_lock(ll); \ |
| 43 | action; \ |
| 44 | locked_list_unlock(ll, __macro_irql); |
| 45 | |
| 46 | static inline bool locked_list_empty(struct locked_list *ll) { |
| 47 | LOCKED_LIST_DO(ll, bool empty = list_head_empty(&ll->list)); |
| 48 | return empty; |
| 49 | } |
| 50 | |
| 51 | static inline void locked_list_add(struct locked_list *ll, |
| 52 | struct list_node *lh) { |
| 53 | LOCKED_LIST_DO(ll, list_head_push_front(&ll->list, lh); ll->num_elems++;); |
| 54 | } |
| 55 | |
| 56 | static inline void locked_list_del(struct locked_list *ll, |
| 57 | struct list_node *lh) { |
| 58 | LOCKED_LIST_DO(ll, list_node_delete_init(lh); ll->num_elems--;); |
| 59 | } |
| 60 | |
| 61 | static inline void locked_list_del_locked(struct locked_list *ll, |
| 62 | struct list_node *lh) { |
| 63 | enforce(spinlock_locked(&ll->lock)); |
| 64 | list_node_delete_init(n: lh); |
| 65 | ll->num_elems--; |
| 66 | } |
| 67 | |
| 68 | static inline struct list_node *locked_list_pop_front(struct locked_list *ll) { |
| 69 | LOCKED_LIST_DO(ll, struct list_node *ret = list_head_pop_front(&ll->list)); |
| 70 | return ret; |
| 71 | } |
| 72 | |
| 73 | static inline size_t locked_list_num_elems(struct locked_list *ll) { |
| 74 | LOCKED_LIST_DO(ll, size_t ret = ll->num_elems); |
| 75 | return ret; |
| 76 | } |
| 77 | |
| 78 | static inline void locked_list_init(struct locked_list *ll, bool irq_disable) { |
| 79 | tsa_unpublished(&ll->lock); |
| 80 | list_head_init(h: &ll->list); |
| 81 | spinlock_init(&ll->lock); |
| 82 | ll->num_elems = 0; |
| 83 | ll->lock_high = irq_disable; |
| 84 | } |
| 85 | |