Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2018 Netronome Systems, Inc.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "nfp_mutex.h"
7 : :
8 : : #include <sched.h>
9 : :
10 : : #include "nfp_logs.h"
11 : : #include "nfp_target.h"
12 : :
13 : : /*
14 : : * If you need more than 65536 recursive locks, please
15 : : * rethink your code.
16 : : */
17 : : #define MUTEX_DEPTH_MAX 0xffff
18 : :
19 : : struct nfp_cpp_mutex {
20 : : struct nfp_cpp *cpp;
21 : : uint8_t target;
22 : : uint16_t depth;
23 : : uint64_t address;
24 : : uint32_t key;
25 : : uint32_t usage;
26 : : struct nfp_cpp_mutex *prev, *next;
27 : : };
28 : :
29 : : static inline uint32_t
30 : : nfp_mutex_locked(uint16_t interface)
31 : : {
32 : 0 : return (uint32_t)interface << 16 | 0x000f;
33 : : }
34 : :
35 : : static inline uint32_t
36 : : nfp_mutex_unlocked(uint16_t interface)
37 : : {
38 : 0 : return (uint32_t)interface << 16 | 0x0000;
39 : : }
40 : :
41 : : static inline uint16_t
42 : : nfp_mutex_owner(uint32_t val)
43 : : {
44 : 0 : return (val >> 16) & 0xffff;
45 : : }
46 : :
47 : : static inline bool
48 : : nfp_mutex_is_locked(uint32_t val)
49 : : {
50 : : return (val & 0xffff) == 0x000f;
51 : : }
52 : :
53 : : static inline bool
54 : : nfp_mutex_is_unlocked(uint32_t val)
55 : : {
56 : 0 : return (val & 0xffff) == 0;
57 : : }
58 : :
59 : : static int
60 : : nfp_cpp_mutex_validate(uint16_t interface,
61 : : int *target,
62 : : uint64_t address)
63 : : {
64 : : /* Not permitted on invalid interfaces */
65 [ # # # # ]: 0 : if (NFP_CPP_INTERFACE_TYPE_of(interface) == NFP_CPP_INTERFACE_TYPE_INVALID)
66 : : return -EINVAL;
67 : :
68 : : /* Address must be 64-bit aligned */
69 [ # # # # : 0 : if ((address & 7) != 0)
# # ]
70 : : return -EINVAL;
71 : :
72 [ # # # # : 0 : if (*target != NFP_CPP_TARGET_MU)
# # ]
73 : : return -EINVAL;
74 : :
75 : : return 0;
76 : : }
77 : :
78 : : /**
79 : : * Initialize a mutex location
80 : : *
81 : : * The CPP target:address must point to a 64-bit aligned location, and
82 : : * will initialize 64 bits of data at the location.
83 : : *
84 : : * This creates the initial mutex state, as locked by this
85 : : * nfp_cpp_interface().
86 : : *
87 : : * This function should only be called when setting up
88 : : * the initial lock state upon boot-up of the system.
89 : : *
90 : : * @param cpp
91 : : * NFP CPP handle
92 : : * @param target
93 : : * NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU)
94 : : * @param address
95 : : * Offset into the address space of the NFP CPP target ID
96 : : * @param key
97 : : * Unique 32-bit value for this mutex
98 : : *
99 : : * @return
100 : : * 0 on success, or negative value on failure
101 : : */
102 : : int
103 : 0 : nfp_cpp_mutex_init(struct nfp_cpp *cpp,
104 : : int target,
105 : : uint64_t address,
106 : : uint32_t key)
107 : : {
108 : : int err;
109 : 0 : uint32_t muw = NFP_CPP_ID(target, 4, 0); /* atomic_write */
110 : 0 : uint16_t interface = nfp_cpp_interface(cpp);
111 : :
112 : : err = nfp_cpp_mutex_validate(interface, &target, address);
113 : : if (err < 0)
114 : : return err;
115 : :
116 : 0 : err = nfp_cpp_writel(cpp, muw, address + 4, key);
117 [ # # ]: 0 : if (err < 0)
118 : : return err;
119 : :
120 : 0 : err = nfp_cpp_writel(cpp, muw, address, nfp_mutex_locked(interface));
121 : : if (err < 0)
122 : : return err;
123 : :
124 : : return 0;
125 : : }
126 : :
127 : : /**
128 : : * Create a mutex handle from an address controlled by a MU Atomic engine
129 : : *
130 : : * The CPP target:address must point to a 64-bit aligned location, and
131 : : * reserve 64 bits of data at the location for use by the handle.
132 : : *
133 : : * Only target/address pairs that point to entities that support the
134 : : * MU Atomic Engine are supported.
135 : : *
136 : : * @param cpp
137 : : * NFP CPP handle
138 : : * @param target
139 : : * NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU)
140 : : * @param address
141 : : * Offset into the address space of the NFP CPP target ID
142 : : * @param key
143 : : * 32-bit unique key (must match the key at this location)
144 : : *
145 : : * @return
146 : : * A non-NULL struct nfp_cpp_mutex * on success, NULL on failure.
147 : : */
148 : : struct nfp_cpp_mutex *
149 : 0 : nfp_cpp_mutex_alloc(struct nfp_cpp *cpp,
150 : : int target,
151 : : uint64_t address,
152 : : uint32_t key)
153 : : {
154 : : int err;
155 : : uint32_t tmp;
156 : : struct nfp_cpp_mutex *mutex;
157 : 0 : uint32_t mur = NFP_CPP_ID(target, 3, 0); /* atomic_read */
158 : 0 : uint16_t interface = nfp_cpp_interface(cpp);
159 : :
160 : : err = nfp_cpp_mutex_validate(interface, &target, address);
161 : : if (err < 0)
162 : : return NULL;
163 : :
164 : 0 : err = nfp_cpp_readl(cpp, mur, address + 4, &tmp);
165 [ # # ]: 0 : if (err < 0)
166 : : return NULL;
167 : :
168 [ # # ]: 0 : if (tmp != key)
169 : : return NULL;
170 : :
171 : 0 : mutex = calloc(sizeof(*mutex), 1);
172 [ # # ]: 0 : if (mutex == NULL)
173 : : return NULL;
174 : :
175 : 0 : mutex->cpp = cpp;
176 : 0 : mutex->target = target;
177 : 0 : mutex->address = address;
178 : 0 : mutex->key = key;
179 : 0 : mutex->depth = 0;
180 : :
181 : 0 : return mutex;
182 : : }
183 : :
184 : : /**
185 : : * Free a mutex handle - does not alter the lock state
186 : : *
187 : : * @param mutex
188 : : * NFP CPP Mutex handle
189 : : */
190 : : void
191 : 0 : nfp_cpp_mutex_free(struct nfp_cpp_mutex *mutex)
192 : : {
193 : 0 : free(mutex);
194 : 0 : }
195 : :
196 : : /**
197 : : * Lock a mutex handle, using the NFP MU Atomic Engine
198 : : *
199 : : * @param mutex
200 : : * NFP CPP Mutex handle
201 : : *
202 : : * @return
203 : : * 0 on success, or negative value on failure.
204 : : */
205 : : int
206 : 0 : nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex)
207 : : {
208 : : int err;
209 : 0 : time_t warn_at = time(NULL) + 15;
210 : :
211 [ # # ]: 0 : while ((err = nfp_cpp_mutex_trylock(mutex)) != 0) {
212 : : /* If err != -EBUSY, then the lock was damaged */
213 [ # # ]: 0 : if (err < 0 && err != -EBUSY)
214 : 0 : return err;
215 : :
216 [ # # ]: 0 : if (time(NULL) >= warn_at) {
217 : 0 : PMD_DRV_LOG(WARNING, "Waiting for NFP mutex...");
218 : 0 : warn_at = time(NULL) + 60;
219 : : }
220 : :
221 : 0 : sched_yield();
222 : : }
223 : :
224 : : return 0;
225 : : }
226 : :
227 : : /**
228 : : * Unlock a mutex handle, using the NFP MU Atomic Engine
229 : : *
230 : : * @param mutex
231 : : * NFP CPP Mutex handle
232 : : *
233 : : * @return
234 : : * 0 on success, or negative value on failure
235 : : */
236 : : int
237 : 0 : nfp_cpp_mutex_unlock(struct nfp_cpp_mutex *mutex)
238 : : {
239 : : int err;
240 : : uint32_t key;
241 : : uint32_t value;
242 : 0 : struct nfp_cpp *cpp = mutex->cpp;
243 : 0 : uint16_t interface = nfp_cpp_interface(cpp);
244 : 0 : uint32_t muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */
245 : 0 : uint32_t mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */
246 : :
247 [ # # ]: 0 : if (mutex->depth > 1) {
248 : 0 : mutex->depth--;
249 : 0 : return 0;
250 : : }
251 : :
252 : 0 : err = nfp_cpp_readl(mutex->cpp, mur, mutex->address + 4, &key);
253 [ # # ]: 0 : if (err < 0)
254 : : return err;
255 : :
256 [ # # ]: 0 : if (key != mutex->key)
257 : : return -EPERM;
258 : :
259 : 0 : err = nfp_cpp_readl(mutex->cpp, mur, mutex->address, &value);
260 [ # # ]: 0 : if (err < 0)
261 : : return err;
262 : :
263 [ # # ]: 0 : if (value != nfp_mutex_locked(interface))
264 : : return -EACCES;
265 : :
266 : 0 : err = nfp_cpp_writel(cpp, muw, mutex->address,
267 : : nfp_mutex_unlocked(interface));
268 [ # # ]: 0 : if (err < 0)
269 : : return err;
270 : :
271 : 0 : mutex->depth = 0;
272 : :
273 : 0 : return 0;
274 : : }
275 : :
276 : : /**
277 : : * Attempt to lock a mutex handle, using the NFP MU Atomic Engine
278 : : *
279 : : * Valid lock states:
280 : : * 0x....0000 - Unlocked
281 : : * 0x....000f - Locked
282 : : *
283 : : * @param mutex
284 : : * NFP CPP Mutex handle
285 : : *
286 : : * @return
287 : : * 0 if the lock succeeded, negative value on failure.
288 : : */
289 : : int
290 : 0 : nfp_cpp_mutex_trylock(struct nfp_cpp_mutex *mutex)
291 : : {
292 : : int err;
293 : : uint32_t key;
294 : : uint32_t tmp;
295 : : uint32_t value;
296 : 0 : struct nfp_cpp *cpp = mutex->cpp;
297 : 0 : uint32_t mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */
298 : 0 : uint32_t muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */
299 : 0 : uint32_t mus = NFP_CPP_ID(mutex->target, 5, 3); /* test_set_imm */
300 : :
301 [ # # ]: 0 : if (mutex->depth > 0) {
302 [ # # ]: 0 : if (mutex->depth == MUTEX_DEPTH_MAX)
303 : : return -E2BIG;
304 : :
305 : 0 : mutex->depth++;
306 : 0 : return 0;
307 : : }
308 : :
309 : : /* Verify that the lock marker is not damaged */
310 : 0 : err = nfp_cpp_readl(cpp, mur, mutex->address + 4, &key);
311 [ # # ]: 0 : if (err < 0)
312 : : return err;
313 : :
314 [ # # ]: 0 : if (key != mutex->key)
315 : : return -EPERM;
316 : :
317 : : /*
318 : : * Compare against the unlocked state, and if true,
319 : : * write the interface id into the top 16 bits, and
320 : : * mark as locked.
321 : : */
322 : 0 : value = nfp_mutex_locked(nfp_cpp_interface(cpp));
323 : :
324 : : /*
325 : : * We use test_set_imm here, as it implies a read
326 : : * of the current state, and sets the bits in the
327 : : * bytemask of the command to 1s. Since the mutex
328 : : * is guaranteed to be 64-bit aligned, the bytemask
329 : : * of this 32-bit command is ensured to be 8'b00001111,
330 : : * which implies that the lower 4 bits will be set to
331 : : * ones regardless of the initial state.
332 : : *
333 : : * Since this is a 'Readback' operation, with no Pull
334 : : * data, we can treat this as a normal Push (read)
335 : : * atomic, which returns the original value.
336 : : */
337 : 0 : err = nfp_cpp_readl(cpp, mus, mutex->address, &tmp);
338 [ # # ]: 0 : if (err < 0)
339 : : return err;
340 : :
341 : : /* Was it unlocked? */
342 [ # # ]: 0 : if (nfp_mutex_is_unlocked(tmp)) {
343 : : /*
344 : : * The read value can only be 0x....0000 in the unlocked state.
345 : : * If there was another contending for this lock, then
346 : : * the lock state would be 0x....000f
347 : : *
348 : : * Write our owner ID into the lock.
349 : : * While not strictly necessary, this helps with
350 : : * debug and bookkeeping.
351 : : */
352 : 0 : err = nfp_cpp_writel(cpp, muw, mutex->address, value);
353 [ # # ]: 0 : if (err < 0)
354 : : return err;
355 : :
356 : 0 : mutex->depth = 1;
357 : 0 : return 0;
358 : : }
359 : :
360 : : /* Already locked by us? Success! */
361 [ # # ]: 0 : if (tmp == value) {
362 : 0 : mutex->depth = 1;
363 : 0 : return 0;
364 : : }
365 : :
366 [ # # ]: 0 : return nfp_mutex_is_locked(tmp) ? -EBUSY : -EINVAL;
367 : : }
368 : :
369 : : /**
370 : : * Release lock if held by local system.
371 : : * Extreme care is advised, call only when no local lock users can exist.
372 : : *
373 : : * @param cpp
374 : : * NFP CPP handle
375 : : * @param target
376 : : * NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU)
377 : : * @param address
378 : : * Offset into the address space of the NFP CPP target ID
379 : : *
380 : : * @return
381 : : * - (0) if the lock was OK
382 : : * - (1) if locked by us
383 : : * - (-errno) on invalid mutex
384 : : */
385 : : int
386 : 0 : nfp_cpp_mutex_reclaim(struct nfp_cpp *cpp,
387 : : int target,
388 : : uint64_t address)
389 : : {
390 : : int err;
391 : : uint32_t tmp;
392 : 0 : uint16_t interface = nfp_cpp_interface(cpp);
393 : 0 : const uint32_t mur = NFP_CPP_ID(target, 3, 0); /* atomic_read */
394 [ # # ]: 0 : const uint32_t muw = NFP_CPP_ID(target, 4, 0); /* atomic_write */
395 : :
396 : : err = nfp_cpp_mutex_validate(interface, &target, address);
397 : : if (err != 0)
398 : : return err;
399 : :
400 : : /* Check lock */
401 : 0 : err = nfp_cpp_readl(cpp, mur, address, &tmp);
402 [ # # ]: 0 : if (err < 0)
403 : : return err;
404 : :
405 [ # # # # ]: 0 : if (nfp_mutex_is_unlocked(tmp) || nfp_mutex_owner(tmp) != interface)
406 : : return 0;
407 : :
408 : : /* Bust the lock */
409 : 0 : err = nfp_cpp_writel(cpp, muw, address, nfp_mutex_unlocked(interface));
410 [ # # ]: 0 : if (err < 0)
411 : 0 : return err;
412 : :
413 : : return 1;
414 : : }
|