Loading...
Searching...
No Matches
lock_core.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#ifndef _PICO_LOCK_CORE_H
8#define _PICO_LOCK_CORE_H
9
10#include "pico.h"
11#include "pico/time.h"
12#include "hardware/sync.h"
13
40
41// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_LOCK_CORE, Enable/disable assertions in the lock core, type=bool, default=0, group=pico_sync
42#ifndef PARAM_ASSERTIONS_ENABLED_LOCK_CORE
43#define PARAM_ASSERTIONS_ENABLED_LOCK_CORE 0
44#endif
45
46// PICO_CONFIG: PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND, Enable workaround to preserve low power waits in synchronization primitives where an exclusive access sets the calling core's own event, type=bool, default=1 when using software spin locks on such a platform, advanced=true, group=pico_sync
47#ifndef PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND
48#ifdef PICO_SYNC_RP2350_SPIN_LOCK_WORKAROUND
49#define PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND PICO_SYNC_RP2350_SPIN_LOCK_WORKAROUND
50#else
51#define PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND (PICO_USE_SW_SPIN_LOCKS && PICO_EXCLUSIVE_ACCESS_SETS_OWN_EVENT)
52#endif
53#endif
54
62
69struct lock_core {
70 spin_lock_t *spin_lock;
71
72 // note any lock members in containing structures need not be volatile;
73 // they are protected by memory/compiler barriers when gaining and release spin locks
74};
75
76typedef struct lock_core lock_core_t;
77
87void lock_init(lock_core_t *core, uint lock_num);
88
89#ifndef lock_owner_id_t
96#define lock_owner_id_t int8_t
97#endif
98
99#ifndef LOCK_INVALID_OWNER_ID
103#define LOCK_INVALID_OWNER_ID ((lock_owner_id_t)-1)
104#endif
105
106#ifndef lock_get_caller_owner_id
112#define lock_get_caller_owner_id() ((lock_owner_id_t)get_core_num())
113#ifndef lock_is_owner_id_valid
114#define lock_is_owner_id_valid(id) ((id)>=0)
115#endif
116#endif
117
118#ifndef lock_is_owner_id_valid
119#define lock_is_owner_id_valid(id) ((id) != LOCK_INVALID_OWNER_ID)
120#endif
121
122#ifdef lock_internal_spin_unlock_with_wait
123#define LOCK_INTERNAL_SPIN_UNLOCK_WITH_WAIT_OVERRIDDEN 1
124#else
145#if !PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND
146#define lock_internal_spin_unlock_with_wait(lock, save) spin_unlock((lock)->spin_lock, save), __wfe()
147#else
148extern volatile uint8_t lock_internal_notify_count;
149// Note the ordering here matters. The event register is a single bit, with multiple sources: our
150// own spin_unlock, an SEV from a notifier, an SEV from some other unrelated code, or (on Arm) an
151// exception entry + return on this core. We must drain the event left by the spin_unlock *before*
152// deciding whether we still need to wait for a second event from a notifier.
153//
154// The first (draining) __wfe() is inside the lock's IRQ critical section. Return-from-interrupt
155// generates an event on Armv8-M, but not on Hazard3 v1.0, so IRQs can swallow events on RISC-V.
156// This first __wfe() always completes promptly, because spin_unlock_unsafe() generates an event.
157//
158// With the drain first, a notify occurring after _notify_count is sampled either: (a) bumps the
159// count before we test it, so we skip the second __wfe(), or (b) happens after the test, in which
160// case its SEV sets the event register and the second __wfe() returns immediately.
161//
162// (Also note: the increment cannot happen between the _notify_count and the spin_unlock_unsafe()
163// because the increment must hold the lock *we are initially holding*.)
164//
165// See comment on `lock_internal_notify_count` declaration in lock_core.c for background details
166// on interactions between events and exclusives on RP2350.
167#define lock_internal_spin_unlock_with_wait(lock, save) ({ \
168 uint8_t _notify_count = lock_internal_notify_count; \
169 spin_unlock_unsafe((lock)->spin_lock); \
170 __wfe(); /* consume event from unlock, without interruption */ \
171 restore_interrupts_from_disabled(save); \
172 if (_notify_count == lock_internal_notify_count) __wfe(); \
173 })
174#endif
175#endif
176
177#ifdef lock_internal_spin_unlock_with_notify
178#define LOCK_INTERNAL_SPIN_UNLOCK_WITH_NOTIFY_OVERRIDDEN 1
179#else
205#if !PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND
206#define lock_internal_spin_unlock_with_notify(lock, save) spin_unlock((lock)->spin_lock, save), __sev()
207#else
208// note that spin_lock_blocking() already posts an event to the current core: ldaexb/strex on Arm,
209// amoor.w.aq on RISC-V, so creates + retires a reservation. (spin_unlock() doesn't as it's just a
210// release-ordered store, but the point is a lock+unlock will always cause a core-local event.)
211#define lock_internal_spin_unlock_with_notify(lock, save) ({ \
212 lock_internal_notify_count++; \
213 spin_unlock((lock)->spin_lock, save); \
214 __sev(); \
215 })
216#endif
217#endif
218
219#ifdef lock_internal_spin_unlock_with_best_effort_wait_or_timeout
220#define LOCK_INTERNAL_SPIN_UNLOCK_WITH_BEST_EFFORT_WAIT_OR_TIMEOUT_OVERRIDDEN 1
221#else
244#if !PICO_SYNC_EXCLUSIVE_ACCESS_EVENT_WORKAROUND
245#define lock_internal_spin_unlock_with_best_effort_wait_or_timeout(lock, save, until) ({ \
246 spin_unlock((lock)->spin_lock, save); \
247 best_effort_wfe_or_timeout(until); \
248})
249#else
250// see the comment on lock_internal_spin_unlock_with_wait above for why the event left by the
251// spin_unlock must be drained before the notify count is tested
252#define lock_internal_spin_unlock_with_best_effort_wait_or_timeout(lock, save, until) ({ \
253 uint8_t _notify_count = lock_internal_notify_count; \
254 spin_unlock_unsafe((lock)->spin_lock); \
255 __wfe(); /* consume event from spin_unlock_unsafe() without interruption */ \
256 restore_interrupts_from_disabled(save); \
257 _notify_count == lock_internal_notify_count ? best_effort_wfe_or_timeout(until) \
258 : time_reached(until); \
259})
260#endif
261#endif
262
284#ifndef LOCK_INTERNAL_SPIN_UNLOCK_WITH_NOTIFY_WAKES_ALL
285#if !(LOCK_INTERNAL_SPIN_UNLOCK_WITH_WAIT_OVERRIDDEN | LOCK_INTERNAL_SPIN_UNLOCK_WITH_NOTIFY_OVERRIDDEN | LOCK_INTERNAL_SPIN_UNLOCK_WITH_BEST_EFFORT_WAIT_OR_TIMEOUT_OVERRIDDEN)
286#define LOCK_INTERNAL_SPIN_UNLOCK_WITH_NOTIFY_WAKES_ALL 1
287#else
288#define LOCK_INTERNAL_SPIN_UNLOCK_WITH_NOTIFY_WAKES_ALL 0
289#endif
290#endif
291
316#if LOCK_INTERNAL_SPIN_UNLOCK_WITH_NOTIFY_WAKES_ALL
317#define lock_internal_spin_unlock_maybe_notify(lock, save, others_may_proceed) spin_unlock((lock)->spin_lock, save)
318#else
319#define lock_internal_spin_unlock_maybe_notify(lock, save, others_may_proceed) ({ \
320 if (others_may_proceed) { \
321 lock_internal_spin_unlock_with_notify(lock, save); \
322 } else { \
323 spin_unlock((lock)->spin_lock, save); \
324 } \
325})
326#endif
327
328#ifndef sync_internal_yield_until_before
341#define sync_internal_yield_until_before(until) ((void)0)
342#endif
343
344#endif
void lock_init(lock_core_t *core, uint lock_num)
Initialise a lock structure.
Definition lock_core.c:44
Core state shared by all lock primitives.
Definition lock_core.h:69
spin_lock_t * spin_lock
Spin lock protecting this lock's state.
Definition lock_core.h:70