1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * This file implements the core controller locking logic and mechanisms. See
18 * the Locking section in the theory statement for more background and
19 * information.
20 */
21
22 #include "i2cnex.h"
23
24 void
i2c_txn_free(i2c_txn_t * txn)25 i2c_txn_free(i2c_txn_t *txn)
26 {
27 if (txn == NULL) {
28 return;
29 }
30
31 VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_UNLOCKED);
32 i2c_ctrl_t *ctrl = txn->txn_ctrl;
33
34 mutex_enter(&ctrl->ic_txn_lock);
35 list_remove(&ctrl->ic_txns, txn);
36 mutex_exit(&ctrl->ic_txn_lock);
37
38 cv_destroy(&txn->txn_cv);
39 kmem_free(txn, sizeof (i2c_txn_t));
40 }
41
42 i2c_txn_t *
i2c_txn_alloc(i2c_ctrl_t * ctrl,i2c_txn_tag_t tag,const void * debug)43 i2c_txn_alloc(i2c_ctrl_t *ctrl, i2c_txn_tag_t tag, const void *debug)
44 {
45 i2c_txn_t *txn = kmem_zalloc(sizeof (i2c_txn_t), KM_SLEEP);
46
47 txn->txn_tag = tag;
48 txn->txn_debug = debug;
49 txn->txn_alloc_kthread = (uintptr_t)curthread;
50 txn->txn_ctrl = ctrl;
51 cv_init(&txn->txn_cv, NULL, CV_DRIVER, NULL);
52
53 mutex_enter(&ctrl->ic_txn_lock);
54 list_insert_tail(&ctrl->ic_txns, txn);
55 mutex_exit(&ctrl->ic_txn_lock);
56
57 return (txn);
58 }
59
60
61 bool
i2c_txn_held(i2c_txn_t * txn)62 i2c_txn_held(i2c_txn_t *txn)
63 {
64 if (txn == NULL) {
65 return (false);
66 }
67
68 i2c_ctrl_t *ctrl = txn->txn_ctrl;
69 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
70
71 mutex_enter(&lock->cl_mutex);
72 bool ret = lock->cl_owner == txn;
73 mutex_exit(&lock->cl_mutex);
74
75 return (ret);
76 }
77
78 static void
i2c_txn_lock_acquire(i2c_ctrl_lock_t * lock,i2c_txn_t * txn)79 i2c_txn_lock_acquire(i2c_ctrl_lock_t *lock, i2c_txn_t *txn)
80 {
81 VERIFY3P(lock->cl_owner, ==, NULL);
82 VERIFY3P(txn->txn_state, ==, I2C_TXN_STATE_UNLOCKED);
83 VERIFY3U(list_link_active(&txn->txn_wait_link), ==, 0);
84 VERIFY3P(&txn->txn_ctrl->ic_lock, ==, lock);
85
86 txn->txn_state = I2C_TXN_STATE_ACQUIRED;
87 txn->txn_last_change = gethrtime();
88 txn->txn_acq_kthread = (uintptr_t)curthread;
89 txn->txn_acq_pid = curproc->p_pid;
90
91 lock->cl_owner = txn;
92 lock->cl_nlocks++;
93 }
94
95 static void
i2c_txn_lock_wakeup(i2c_ctrl_t * ctrl)96 i2c_txn_lock_wakeup(i2c_ctrl_t *ctrl)
97 {
98 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
99
100 VERIFY(MUTEX_HELD(&lock->cl_mutex));
101 VERIFY3P(lock->cl_owner, ==, NULL);
102
103 /*
104 * If there is no one next in line, then we're done. Otherwise we need
105 * to reset that callers state to unlocked and then allow them to
106 * acquire it. After that we will need to signal them.
107 */
108 i2c_txn_t *next = list_remove_head(&lock->cl_waiters);
109 if (next == NULL)
110 return;
111
112 /*
113 * As we're about to acquire this, we're going to note that we're no
114 * longer blocked.
115 */
116 next->txn_state = I2C_TXN_STATE_UNLOCKED;
117 next->txn_last_change = gethrtime();
118 i2c_txn_lock_acquire(lock, next);
119 cv_signal(&next->txn_cv);
120 }
121
122 /*
123 * Our txn was interrupted due to a signal. However, other activity could have
124 * still been going on and we cannot guarantee that the caller didn't actually
125 * acquire this and then raced with receiving a signal compared to the
126 * cv_signal(). If they did acquire it, then we treat this as a case where we
127 * need to go ahead and wake up the next person.
128 */
129 static void
i2c_txn_lock_signal(i2c_ctrl_t * ctrl,i2c_txn_t * txn)130 i2c_txn_lock_signal(i2c_ctrl_t *ctrl, i2c_txn_t *txn)
131 {
132 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
133
134 /*
135 * We don't believe that we should be able to enter here with the lock
136 * unlocked. Either we were blocked waiting for this or the next person
137 * woke us up because we had acquired the controller. There should be no
138 * other state.
139 */
140 VERIFY3U(txn->txn_state, !=, I2C_TXN_STATE_UNLOCKED);
141 VERIFY(MUTEX_HELD(&lock->cl_mutex));
142
143 /*
144 * As we're changing the state of the this node, we're going to update
145 * the txn's change time now as well as setting the error that'll get
146 * propagated back.
147 */
148 txn->txn_last_change = gethrtime();
149 txn->txn_err = I2C_CORE_E_LOCK_WAIT_SIGNAL;
150 lock->cl_nsig++;
151
152 if (txn->txn_state == I2C_TXN_STATE_BLOCKED) {
153 VERIFY3U(lock->cl_owner, !=, txn);
154 VERIFY3U(list_link_active(&txn->txn_wait_link), !=, 0);
155 list_remove(&lock->cl_waiters, txn);
156 txn->txn_state = I2C_TXN_STATE_UNLOCKED;
157
158 lock->cl_nsig_block++;
159 return;
160 }
161
162 /*
163 * This caller had actually managed to acquire this and now has to give
164 * it up. We treat this as an immediate unlock request.
165 */
166 lock->cl_nsig_acq++;
167 i2c_txn_ctrl_unlock(txn);
168 }
169
170 void
i2c_txn_ctrl_unlock(i2c_txn_t * txn)171 i2c_txn_ctrl_unlock(i2c_txn_t *txn)
172 {
173 i2c_ctrl_t *ctrl = txn->txn_ctrl;
174 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
175
176 mutex_enter(&lock->cl_mutex);
177 VERIFY3P(lock->cl_owner, ==, txn);
178 VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
179
180 txn->txn_last_change = gethrtime();
181 txn->txn_state = I2C_TXN_STATE_UNLOCKED;
182
183 /*
184 * Before we proceed to wake anyone up, check to see if we have anything
185 * in our stack. If so, then basically we're in the recursive nexus case
186 * and we need to resume them as the rightful owner without waking
187 * anyone up.
188 */
189 lock->cl_owner = list_remove_head(&lock->cl_stack);
190 if (lock->cl_owner == NULL) {
191 i2c_txn_lock_wakeup(ctrl);
192 }
193 mutex_exit(&lock->cl_mutex);
194 }
195
196 i2c_errno_t
i2c_txn_ctrl_lock(i2c_txn_t * txn,bool block)197 i2c_txn_ctrl_lock(i2c_txn_t *txn, bool block)
198 {
199 i2c_ctrl_t *ctrl = txn->txn_ctrl;
200 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
201 hrtime_t sleep_time;
202 i2c_errno_t ret;
203
204 mutex_enter(&lock->cl_mutex);
205 /*
206 * First check if this controller is currently held. If it is not, then
207 * this is easy, we get it right away. Note, this implies that there are
208 * no waiters as part of our invariants.
209 */
210 VERIFY3P(lock->cl_owner, !=, txn);
211 if (lock->cl_owner == NULL) {
212 VERIFY3U(list_is_empty(&lock->cl_waiters), !=, 0);
213 i2c_txn_lock_acquire(lock, txn);
214 mutex_exit(&lock->cl_mutex);
215 return (I2C_CORE_E_OK);
216 }
217
218 /*
219 * Now, we must see if this is a nexus related operation and we're in
220 * the nexus thread. If so, lock inheritance is basically allowed. That
221 * means that we're allowed to basically "obtain" the lock. This should
222 * only really happen during a device's attach / detach operation.
223 */
224 if (lock->cl_nexus_thr == (uintptr_t)curthread) {
225 list_insert_head(&lock->cl_stack, lock->cl_owner);
226 lock->cl_owner = NULL;
227 i2c_txn_lock_acquire(lock, txn);
228 lock->cl_nstack++;
229 mutex_exit(&lock->cl_mutex);
230 return (I2C_CORE_E_OK);
231 }
232
233 if (!block) {
234 mutex_exit(&lock->cl_mutex);
235 return (I2C_CORE_E_LOCK_WOULD_BLOCK);
236 }
237
238 VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_UNLOCKED);
239 txn->txn_state = I2C_TXN_STATE_BLOCKED;
240 sleep_time = gethrtime();
241 list_insert_tail(&lock->cl_waiters, txn);
242
243 while (txn->txn_state == I2C_TXN_STATE_BLOCKED) {
244 /*
245 * Block until we receive a signal. Signals to interrupt us
246 * trump any other action that could occur. It's important to
247 * note that we could raise with being signaled and handed the
248 * controller. In that case, we defer to the signal.
249 */
250 if (cv_wait_sig(&txn->txn_cv, &lock->cl_mutex) == 0) {
251 i2c_txn_lock_signal(ctrl, txn);
252 break;
253 }
254 }
255
256 VERIFY3S(txn->txn_last_change, !=, sleep_time);
257 VERIFY3U(list_link_active(&txn->txn_wait_link), ==, 0);
258 if (txn->txn_state == I2C_TXN_STATE_UNLOCKED) {
259 ret = txn->txn_err;
260 } else {
261 VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
262 VERIFY3P(lock->cl_owner, ==, txn);
263 ret = I2C_CORE_E_OK;
264 }
265
266 mutex_exit(&lock->cl_mutex);
267 return (ret);
268 }
269
270 void
i2c_txn_nexus_op_end(i2c_txn_t * txn)271 i2c_txn_nexus_op_end(i2c_txn_t *txn)
272 {
273 i2c_ctrl_t *ctrl = txn->txn_ctrl;
274 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
275
276 mutex_enter(&lock->cl_mutex);
277 VERIFY3P(lock->cl_owner, ==, txn);
278 VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
279 VERIFY3U(lock->cl_nexus_thr, ==, curthread);
280
281 /*
282 * It is possible for us to have multiple layers of nexus stacking. For
283 * example, a user is doing a device create, which in turn calls a bus
284 * config operation. If this has happened, then we'll have a non-empty
285 * stack.
286 */
287 if (list_is_empty(&lock->cl_stack) != 0) {
288 lock->cl_nexus_thr = 0;
289 }
290 mutex_exit(&lock->cl_mutex);
291 }
292
293 void
i2c_txn_nexus_op_begin(i2c_txn_t * txn)294 i2c_txn_nexus_op_begin(i2c_txn_t *txn)
295 {
296 i2c_ctrl_t *ctrl = txn->txn_ctrl;
297 i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
298
299 mutex_enter(&lock->cl_mutex);
300 VERIFY3P(lock->cl_owner, ==, txn);
301 VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
302 VERIFY0(lock->cl_nexus_thr);
303 if (lock->cl_nexus_thr != 0) {
304 VERIFY3U(lock->cl_nexus_thr, ==, curthread);
305 }
306 lock->cl_nexus_thr = (uintptr_t)curthread;
307 lock->cl_nnexus++;
308 mutex_exit(&lock->cl_mutex);
309 }
310