Add parlib/common.h
[akaros.git] / user / parlib / include / mcs.h
1 #ifndef PARLIB_MCS_H
2 #define PARLIB_MCS_H
3
4 #include <parlib/vcore.h>
5 #include <parlib/arch/arch.h>
6
7 __BEGIN_DECLS
8
9 #define MCS_LOCK_INIT {0}
10 #define MCS_QNODE_INIT {0, 0}
11
12 typedef struct mcs_lock_qnode
13 {
14         struct mcs_lock_qnode *next;
15         int locked;
16 }__attribute__((aligned(ARCH_CL_SIZE))) mcs_lock_qnode_t;
17
18 typedef struct mcs_lock
19 {
20         mcs_lock_qnode_t* lock;
21 } mcs_lock_t;
22
23 typedef struct
24 {
25         volatile int myflags[2][LOG2_MAX_VCORES];
26         volatile int* partnerflags[2][LOG2_MAX_VCORES];
27         int parity;
28         int sense;
29         char pad[ARCH_CL_SIZE];
30 } mcs_dissem_flags_t;
31
32 typedef struct
33 {
34         size_t nprocs;
35         mcs_dissem_flags_t* allnodes;
36         size_t logp;
37 } mcs_barrier_t;
38
39 int mcs_barrier_init(mcs_barrier_t* b, size_t nprocs);
40 void mcs_barrier_wait(mcs_barrier_t* b, size_t vcoreid);
41
42 void mcs_lock_init(struct mcs_lock *lock);
43 /* Caller needs to alloc (and zero) their own qnode to spin on.  The memory
44  * should be on a cacheline that is 'per-thread'.  This could be on the stack,
45  * in a thread control block, etc. */
46 void mcs_lock_lock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
47 void mcs_lock_unlock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
48 void mcs_lock_unlock_cas(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
49 /* If you lock the lock from vcore context, you must use these. */
50 void mcs_lock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
51 void mcs_unlock_notifsafe(struct mcs_lock *lock, struct mcs_lock_qnode *qnode);
52
53 /* Preemption detection and recovering MCS locks.
54  *
55  * The basic idea is that when spinning, vcores make sure someone else is
56  * making progress that will lead to them not spinning.  Usually, it'll be to
57  * make sure the lock holder (if known) is running.  If we don't know the lock
58  * holder, we ensure the end of whatever chain we can see is running, which
59  * will make sure its predecessor runs, which will eventually unjam the system.
60  * */
61
62 /* Old style.  Has trouble getting out of 'preempt/change-to storms' under
63  * heavy contention and with preemption. */
64 struct mcs_pdro_qnode
65 {
66         struct mcs_pdro_qnode *next;
67         int locked;
68         uint32_t vcoreid;
69 }__attribute__((aligned(ARCH_CL_SIZE)));
70
71 struct mcs_pdro_lock
72 {
73         struct mcs_pdro_qnode *lock;
74 };
75
76 #define MCSPDRO_LOCK_INIT {0}
77 #define MCSPDRO_QNODE_INIT {0, 0, 0}
78
79 void mcs_pdro_init(struct mcs_pdro_lock *lock);
80 void mcs_pdro_fini(struct mcs_pdro_lock *lock);
81 void mcs_pdro_lock(struct mcs_pdro_lock *lock, struct mcs_pdro_qnode *qnode);
82 void mcs_pdro_unlock(struct mcs_pdro_lock *lock, struct mcs_pdro_qnode *qnode);
83
84 /* Only call these if you have notifs disabled and know your vcore's qnode.
85  * Mostly used for debugging, benchmarks, or critical code. */
86 void __mcs_pdro_lock(struct mcs_pdro_lock *lock, struct mcs_pdro_qnode *qnode);
87 void __mcs_pdro_unlock(struct mcs_pdro_lock *lock, struct mcs_pdro_qnode *qnode);
88 void __mcs_pdro_unlock_no_cas(struct mcs_pdro_lock *lock,
89                              struct mcs_pdro_qnode *qnode);
90
91 /* New style: under heavy contention with preemption, they won't enter the
92  * 'preempt/change_to storm' that can happen to PDRs, at the cost of some
93  * performance.  This is the default. */
94 struct mcs_pdr_qnode
95 {
96         struct mcs_pdr_qnode *next;
97         int locked;
98 }__attribute__((aligned(ARCH_CL_SIZE)));
99
100 struct mcs_pdr_lock
101 {
102         struct mcs_pdr_qnode *lock __attribute__((aligned(ARCH_CL_SIZE)));
103         uint32_t lockholder_vcoreid __attribute__((aligned(ARCH_CL_SIZE)));
104         struct mcs_pdr_qnode *qnodes __attribute__((aligned(ARCH_CL_SIZE)));
105 };
106
107 #define MCSPDR_NO_LOCKHOLDER ((uint32_t)-1)
108
109 void mcs_pdr_init(struct mcs_pdr_lock *lock);
110 void mcs_pdr_fini(struct mcs_pdr_lock *lock);
111 void mcs_pdr_lock(struct mcs_pdr_lock *lock);
112 void mcs_pdr_unlock(struct mcs_pdr_lock *lock);
113
114 __END_DECLS
115
116 #endif /* PARLIB_MCS_H */