Description
Problem Description
The current implementation of HWSPINLOCK is flacky because it doesn't involve a standard spinlock in conjunction with HW. Currently, it is possible to be preempted by another task while holding a hwspinlock, locking other clusters while we sleep. The HW is also not protected in SMP context against multiple thread trying to acquire the ownership of the same spinlock at the same time. This may result in a double lock, or double release of the lock. Depending on the hardware, this could simply inverse the working of the spinlock.
The framework is also quite bare-bones. There are no devicetree helpers to go along, and the devicetree bindings are also missing the standard hwlocks
&co properties.
Finally, hwspinlocks should not be accessed in userspace. Because hwspinlock are used to protect shared hw resources, only drivers should be consumers. If a user application needs a synchronization mechanism between clusters, it should use higher level APIs like MBOX, IPM or IPC.
Syscalls are also not generated properly right-now because of a missing call to zephyr_syscall_header()
in CMakeLists.txt
Proposed Change (Summary)
I would propose the following solutions
- Remove the syscalls for hwspinlocks.
- Include a regular spinlock, embedded in the hwspinlock. It should be invisible to the user.
- Streamline the API with regular spinlocks
- Provide a
hwspinlock_dt_spec
structure, similar to MBOX. It will hold the hwspinlock device, id and inner zephyr spinlock - Provide an api similar to regular spinlock.
- Provide a
- Provide devicetree helpers
- Add standard
hwlocks
hwlock-names
and#hwlock-cells
properties in the devicetree bindings - Provide devicetree helper macro to extract the hwspinlock controller and ID, similar to how MBOX does it.
- Add standard
Proposed Change (Detailed)
Syscalls removal
As stated previously, hwspinlocks should not be accessed in userspace. They must be reserved to driver implementation in kernel space to protect shared hardware resources.
- Replace the separate
__syscall
andz_impl_
function with direct implementation - Remove the
hwspinlock_handlers.c
file
API similar to zephyr spinlocks
- Replace
with
int hwspinlock_trylock(...);
int hw_spin_trylock(..., k_spinlock_key_t *key);
- Replace
with
void hwspinlock_lock(...);
k_spinlock_key_t hw_spin_lock(...);
- Replace
with
void hwspinlock_unlock(...);
void hw_spin_unlock(..., k_spinlock_key_t key);
The user will call lock or trylock and get a spinlock key, and use the same key to unlock the hwspinlock
Embed a spinlock
Create a struct hwspinlock_context
to hold the required spinlock. This struct will be passed to the lock
/trylock
/unlock
APIs so the framework can manage the embedded spinlock directly.
- When calling
hw_spin_lock
/hw_spin_trylock
, the framework first callsk_spin_lock
/k_spin_trylock
with the embedded spinlock, and upon success, continues with the hwspinlock api calls. - When calling
hw_spin_unlock
, we first call the hwspinlock api, then callk_spin_unlock
to release the lock on the inner spinlock.
Use a typedef hwspinlock_ctx_t
to hide the inner working of the context. It will just be an opaque type to the user, that he will pass around the API calls.
Create a struct hwspinlock_dt_spec
to hold the device pointer, hwspinlock id and context.
struct hwspinlock_dt_spec {
const struct device *dev;
uint32_t id;
hwspinlock_ctx_t ctx;
};
Add helper functions suffixed with _dt
to use this struct, as done in the MBOX framework.
Devicetree helpers
Add the linux standard properties in dts/bindings
hwlocks
: phandle-array, contain the list of hwspinlockhwlock-names
: string-array, names accompanying the phandles#hwlock-cells
: int, used by the hwspinlock controller to defines the fields of the phandles. Standardize theid
field, which will be used in devicetree helper macros.
Add multiple helper macros to extract those information from the devicetree
DT_HWSPINLOCK_CTRL_BY_IDX(node_id, idx)
: get the hwspinlock controller from a phandle at indexDT_HWSPINLOCK_CTRL_BY_NAME(node_id, name)
: get the hwspinlock controller from a phandle by its name inhwlock-names
propertyDT_HWSPINLOCK_ID_BY_IDX(node_id, idx)
: get the hwspinlock id from a phandle at indexDT_HWSPINLOCK_ID_BY_NAME(node_id, name)
: get the hwspinlock id from a phandle by its name inhwlock-names
property
Also include helpers for hwspinlock_dt_spec
initialization
HWSPINLOCK_DT_SPEC_GET_BY_IDX(node_id, idx)
HWSPINLOCK_DT_SPEC_GET_BY_NAME(node_id, idx)
HWSPINLOCK_DT_SPEC_GET(node_id, idx)
: same asHWSPINLOCK_DT_SPEC_GET_BY_IDX(node_id, 0)
HWSPINLOCK_DT_SPEC_INST_GET_BY_IDX(inst, idx)
HWSPINLOCK_DT_SPEC_INST_GET_BY_NAME(inst, idx)
HWSPINLOCK_DT_SPEC_INST_GET(inst, idx)
: same asHWSPINLOCK_DT_SPEC_INST_GET_BY_IDX(node_id, 0)
Dependencies
Only the HWSPINLOCK framework should is impacted.
Concerns and Unresolved Questions
No response
Alternatives Considered
No response
Metadata
Metadata
Assignees
Type
Projects
Status