1/* @title: Lock Validation */
2#pragma once
3#include <atomic.h>
4#include <compiler/core.h>
5#include <sch/irql.h>
6#include <stdbool.h>
7#include <stddef.h>
8#include <stdint.h>
9
10struct lock_chk_class;
11struct lock_chk_lock;
12
13enum lock_chk_flags : uint8_t {
14 LOCK_UNCHKD = 0,
15 LOCK_CHKD_ORDER = 1 << 0,
16 LOCK_CHKD_THREAD = 1 << 1,
17 LOCK_CHKD_FULL = LOCK_CHKD_ORDER | LOCK_CHKD_THREAD,
18};
19
20enum lock_chk_type : uint8_t {
21 LOCK_CHK_TYPE_SPIN,
22 LOCK_CHK_TYPE_QSPIN,
23 LOCK_CHK_TYPE_MUTEX,
24 LOCK_CHK_TYPE_MUTEX_SIMPLE,
25 LOCK_CHK_TYPE_RWLOCK,
26};
27
28enum lock_chk_mode : uint8_t {
29 LOCK_CHK_MODE_IGNORED,
30 LOCK_CHK_MODE_SHARED,
31 LOCK_CHK_MODE_EXCLUSIVE,
32};
33
34enum lock_chk_engine_state : uint8_t {
35 LOCK_CHK_INACTIVE,
36 LOCK_CHK_ACTIVE,
37 LOCK_CHK_DEGRADED,
38};
39
40#define LOCK_OP_IRQ_MASK 0x3
41#define LOCK_OP_KIND_MASK 0x18
42
43enum lock_op_flags : uint8_t {
44 LOCK_OP_IRQ_NONE = 0,
45 LOCK_OP_IRQ_DISPATCH = 1,
46 LOCK_OP_IRQ_HIGH = 2,
47 LOCK_OP_RAW = 1 << 2,
48
49 /* These are represented NOT as a single bit
50 * so a missing KIND can trip an assertion
51 * instead of assuming a state */
52 LOCK_OP_KIND_BLOCKING = 1 << 3,
53 LOCK_OP_KIND_TRY = 2 << 3,
54};
55
56struct lock_chk_site {
57 const char *file;
58 const char *func;
59 uint32_t line;
60};
61
62struct lock_chk_class {
63 const char *name;
64 const char *file;
65 uint32_t line;
66};
67
68#ifdef DEBUG_LOCK_CHK
69
70#define LOCK_CHK_SITE_HERE() \
71 ({ \
72 static const struct lock_chk_site __site = { \
73 .file = __RELFILE__, \
74 .func = __func__, \
75 .line = __LINE__, \
76 }; \
77 &__site; \
78 })
79
80#define LOCK_CHK_CLASS(id) (&__lock_chk_class_##id)
81
82#define LOCK_CHK_CLASS_DEFINE(id) \
83 extern const struct lock_chk_class __lock_chk_class_##id; \
84 const struct lock_chk_class __lock_chk_class_##id = { \
85 .name = #id, \
86 .file = __RELFILE__, \
87 .line = __LINE__, \
88 }
89
90#define LOCK_CHK_CLASS_DEFINE_LOCAL(id) \
91 static const struct lock_chk_class __lock_chk_class_##id = { \
92 .name = #id, \
93 .file = __RELFILE__, \
94 .line = __LINE__, \
95 }
96
97#define LOCK_CHK_CLASS_EXTERN(id) \
98 extern const struct lock_chk_class __lock_chk_class_##id
99
100#else /* !defined(DEBUG_LOCK_CHK) */
101
102#define LOCK_CHK_SITE_HERE() ((const struct lock_chk_site *) NULL)
103#define LOCK_CHK_CLASS(id) ((const struct lock_chk_class *) NULL)
104#define LOCK_CHK_CLASS_DEFINE(id)
105#define LOCK_CHK_CLASS_DEFINE_LOCAL(id)
106#define LOCK_CHK_CLASS_EXTERN(id)
107
108#endif /* DEBUG_LOCK_CHK */
109
110/*
111 * Prototypes
112 */
113#ifdef DEBUG_LOCK_CHK
114
115void lock_debug_spin_classify(atomic(enum lock_op_flags) * usage,
116 enum lock_op_flags requested,
117 struct lock_chk_lock *lock,
118 const struct lock_chk_site *site);
119bool lock_debug_spin_push(struct lock_chk_lock *lock, enum irql prev_irql,
120 const struct lock_chk_site *site);
121void lock_debug_spin_validate_top(struct lock_chk_lock *lock,
122 enum irql prev_irql,
123 const struct lock_chk_site *site);
124void lock_debug_spin_pop(struct lock_chk_lock *lock);
125
126void lock_chk_note_use(struct lock_chk_lock *lock, enum lock_op_flags flags);
127
128void lock_chk_assert_schedulable(const struct lock_chk_site *site);
129
130#else /* !defined(DEBUG_LOCK_CHK) */
131
132static inline void lock_chk_note_lock_use(struct lock_chk_lock *lock,
133 bool manages_irql,
134 bool raw_operation) {
135 cc_ignore(lock, manages_irql, raw_operation);
136}
137
138static inline void
139lock_chk_assert_schedulable(const struct lock_chk_site *site) {
140 cc_ignore(site);
141}
142
143#endif /* DEBUG_LOCK_CHK */
144
145void lock_chk_init(void);
146bool lock_chk_tracking_active(void);
147