硬件自旋锁框架
简介
硬件自旋锁模块为异构处理器和不在单一共享操作系统下运行的处理器之间的同步和互斥提供硬件辅助。
例如,OMAP4具有双核Cortex-A9、双核Cortex-M3和一个C64x+ DSP,每个处理器运行不同的操作系统(主处理器A9通常运行Linux,从处理器M3和DSP运行某种RTOS)。
通用的硬件自旋锁框架允许平台无关的驱动程序使用硬件自旋锁设备来访问在远程处理器之间共享的数据结构,否则没有其他机制可以实现同步和互斥操作。
例如,在OMAP4上,CPU密集型多媒体任务通过IPC子系统Syslink从主机卸载到远程M3和/或C64x+从处理器。
为了实现基于消息的快速通信,需要最小的内核支持,以便将从远程处理器到达的消息传递给适当的用户进程。
这种通信基于远程处理器之间共享的简单数据结构,并且使用硬件自旋锁模块进行同步(远程处理器直接将新消息放入这个共享数据结构)。
通用的硬件自旋锁接口使得可以拥有通用的、平台无关的驱动程序。
用户API
struct hwspinlock *hwspin_lock_request(void);
动态分配一个硬件自旋锁并返回其地址,如果没有未使用的硬件自旋锁可用,则返回NULL。使用此API的用户通常希望在可以用于实现同步之前将锁的ID通知给远程核心。
应该在进程上下文中调用(可能会休眠)。
struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
分配特定的硬件自旋锁ID并返回其地址,如果该硬件自旋锁已在使用中,则返回NULL。通常,板级代码将调用此函数以预留特定的硬件自旋锁ID以供预定义目的使用。
应该在进程上下文中调用(可能会休眠)。
int of_hwspin_lock_get_id(struct device_node *np, int index);
检索基于OF phandle的特定锁的全局锁ID。此函数为hwspinlock模块的DT用户提供了一种获取特定hwspinlock的全局锁ID的方法,以便可以使用常规的hwspin_lock_request_specific() API进行请求。
该函数在成功时返回锁ID号,如果hwspinlock设备尚未向核心注册,则返回-EPROBE_DEFER,或其他错误值。
应该在进程上下文中调用(可能会休眠)。
int hwspin_lock_free(struct hwspinlock *hwlock);
释放先前分配的硬件自旋锁;成功时返回0,失败时返回适当的错误代码(例如,如果硬件自旋锁已释放,则返回-EINVAL)。
应该在进程上下文中调用(可能会休眠)。
int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout);
使用超时限制(以毫秒为单位)锁定先前分配的硬件自旋锁。如果硬件自旋锁已被占用,函数将忙等待直到其被释放,但在超时后放弃。从此函数成功返回后,抢占将被禁用,因此调用者不能休眠,并建议尽快释放硬件自旋锁,以最小化远程核心对硬件互连的轮询。
成功时返回0,否则返回适当的错误代码(尤其是如果硬件自旋锁在超时后仍然忙碌,则返回-ETIMEDOUT)。该函数永远不会休眠。
int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int timeout);
锁定先前分配的 hwspinlock,并设置超时限制(以毫秒为单位)。如果 hwspinlock 已被占用,函数将忙等待直到其释放,但在超时时间到达时放弃等待。成功返回后,禁用抢占和本地中断,因此调用者不能睡眠,并建议尽快释放 hwspinlock。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 在超时时间后仍然忙碌,则返回 -ETIMEDOUT)。该函数不会睡眠。
int hwspin_lock_timeout_irqsave(struct hwspinlock *hwlock, unsigned int to, unsigned long *flags);
锁定先前分配的 hwspinlock,并设置超时限制(以毫秒为单位)。如果 hwspinlock 已被占用,函数将忙等待直到其释放,但在超时时间到达时放弃等待。成功返回后,禁用抢占,禁用本地中断,并将其先前状态保存在给定的 flags 占位符中。调用者不能睡眠,并建议尽快释放 hwspinlock。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 在超时时间后仍然忙碌,则返回 -ETIMEDOUT)。
该函数不会睡眠。
int hwspin_lock_timeout_raw(struct hwspinlock *hwlock, unsigned int timeout);
锁定先前分配的 hwspinlock,并设置超时限制(以毫秒为单位)。如果 hwspinlock 已被占用,函数将忙等待直到其释放,但在超时时间到达时放弃等待。
注意:用户必须使用互斥锁或自旋锁保护获取硬件锁的例程,以避免死锁,这将允许用户在硬件锁下执行一些耗时或可睡眠的操作。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 在超时时间后仍然忙碌,则返回 -ETIMEDOUT)。
该函数不会睡眠。
int hwspin_lock_timeout_in_atomic(struct hwspinlock *hwlock, unsigned int to);
锁定先前分配的 hwspinlock,并设置超时限制(以毫秒为单位)。如果 hwspinlock 已被占用,函数将忙等待直到其释放,但在超时时间到达时放弃等待。
此函数只能从原子上下文中调用,超时值不得超过几毫秒。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 在超时时间后仍然忙碌,则返回 -ETIMEDOUT)。
该函数不会睡眠。
int hwspin_trylock(struct hwspinlock *hwlock);
尝试锁定先前分配的 hwspinlock,如果已被占用则立即失败。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 已被占用,则返回 -EBUSY)。该函数不会睡眠。
int hwspin_trylock_irq(struct hwspinlock *hwlock);
尝试锁定先前分配的 hwspinlock,如果已被占用则立即失败。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 已被占用,则返回 -EBUSY)。
该函数不会睡眠。
int hwspin_trylock_irqsave(struct hwspinlock *hwlock, unsigned long *flags);
尝试锁定先前分配的 hwspinlock,如果已被占用则立即失败。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 已被占用,则返回 -EBUSY)。禁用抢占,禁用本地中断,并将其先前状态保存在给定的 flags 占位符中。调用者不能睡眠,并建议尽快释放 hwspinlock。
该函数不会睡眠。
int hwspin_trylock_raw(struct hwspinlock *hwlock);
尝试锁定先前分配的 hwspinlock,如果已被占用则立即失败。
注意:用户必须使用互斥锁或自旋锁保护获取硬件锁的例程,以避免死锁,这将允许用户在硬件锁下执行一些耗时或可睡眠的操作。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 已被占用,则返回 -EBUSY)。该函数不会睡眠。
int hwspin_trylock_in_atomic(struct hwspinlock *hwlock);
尝试锁定先前分配的 hwspinlock,如果已被占用则立即失败。
此函数只能从原子上下文中调用。
成功返回 0,否则返回适当的错误代码(特别是如果 hwspinlock 已被占用,则返回 -EBUSY)。该函数不会睡眠。
void hwspin_unlock(struct hwspinlock *hwlock);
解锁先前锁定的 hwspinlock。始终成功,并且可以从任何上下文中调用(该函数不会睡眠)。
注意:代码不应解锁已解锁的 hwspinlock(没有对此进行保护)。
void hwspin_unlock_irq(struct hwspinlock *hwlock);
解锁先前锁定的 hwspinlock,并启用本地中断。调用者不应解锁已解锁的 hwspinlock。
这样做被视为错误(没有对此进行保护)。成功返回后,启用抢占和本地中断。该函数不会睡眠。
void hwspin_unlock_irqrestore(struct hwspinlock *hwlock, unsigned long *flags);
解锁先前锁定的 hwspinlock。
调用者不应解锁已解锁的 hwspinlock。这样做被视为错误(没有对此进行保护)。成功返回后,重新启用抢占,并将本地中断的状态恢复为在给定的 flags 中保存的状态。该函数不会睡眠。
void hwspin_unlock_raw(struct hwspinlock *hwlock);
解锁先前锁定的 hwspinlock。
调用者不应解锁已解锁的 hwspinlock。这样做被视为错误(没有对此进行保护)。该函数不会睡眠。
void hwspin_unlock_in_atomic(struct hwspinlock *hwlock);
解锁先前锁定的 hwspinlock。
调用者不应解锁已解锁的 hwspinlock。这样做被视为错误(没有对此进行保护)。该函数不会睡眠。
int hwspin_lock_get_id(struct hwspinlock *hwlock);
检索给定 hwspinlock 的 id 号码。当 hwspinlock 被动态分配时,需要此信息:在与要同步的远程任务实现互斥之前,应将 id 号码传递给远程任务。
返回 hwspinlock 的 id 号码,如果 hwlock 为 null,则返回 -EINVAL。
典型用法
#include <linux/hwspinlock.h> #include <linux/err.h> int hwspinlock_example1(void) { struct hwspinlock *hwlock; int ret; /* 动态分配一个硬件自旋锁 */ hwlock = hwspin_lock_request(); if (!hwlock) ... id = hwspin_lock_get_id(hwlock); /* 可能需要将id传递给远程处理器 */ /* 获取锁,如果已经被占用则自旋1秒钟 */ ret = hwspin_lock_timeout(hwlock, 1000); if (ret) ... /* * 获取到锁后,进行相应操作,但不要睡眠 */ /* 释放锁 */ hwspin_unlock(hwlock); /* 释放锁资源 */ ret = hwspin_lock_free(hwlock); if (ret) ... return ret; } int hwspinlock_example2(void) { struct hwspinlock *hwlock; int ret; /* * 分配一个特定的硬件自旋锁id - 这应该在板级初始化代码中尽早调用。 */ hwlock = hwspin_lock_request_specific(PREDEFINED_LOCK_ID); if (!hwlock) ... /* 尝试获取锁,但不要自旋 */ ret = hwspin_trylock(hwlock); if (!ret) { pr_info("锁已经被占用\n"); return -EBUSY; } /* * 获取到锁后,进行相应操作,但不要睡眠 */ /* 释放锁 */ hwspin_unlock(hwlock); /* 释放锁资源 */ ret = hwspin_lock_free(hwlock); if (ret) ... return ret; } ## 实现者的API int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, const struct hwspinlock_ops *ops, int base_id, int num_locks); 从底层特定平台的实现中调用,以注册一个新的硬件自旋锁设备(通常是多个锁的集合)。应该在进程上下文中调用(此函数可能会睡眠)。 成功返回0,失败返回适当的错误代码。 int hwspin_lock_unregister(struct hwspinlock_device *bank); 从底层特定供应商的实现中调用,以注销一个硬件自旋锁设备(通常是多个锁的集合)。 应该在进程上下文中调用(此函数可能会睡眠)。 成功返回hwspinlock的地址,错误返回NULL(例如,如果hwspinlock仍在使用中)。 ## 重要的结构体 struct hwspinlock_device是一个通常包含多个硬件锁的设备。它由底层的hwspinlock实现使用hwspin_lock_register() API进行注册。 ```c /** * struct hwspinlock_device - 通常包含多个hwspinlock的设备 * @dev: 底层设备,将用于调用运行时PM API * @ops: 特定平台的hwspinlock处理程序 * @base_id: 此设备中第一个锁的id索引 * @num_locks: 此设备中的锁数量 * @lock: 动态分配的 'struct hwspinlock' 数组 */ struct hwspinlock_device { struct device *dev; const struct hwspinlock_ops *ops; int base_id; int num_locks; struct hwspinlock lock[0]; };
struct hwspinlock_device包含一个hwspinlock结构的数组,每个结构表示一个单独的硬件锁:
/** * struct hwspinlock - 此结构表示一个单独的hwspinlock实例 * @bank: 拥有此锁的hwspinlock_device结构 * @lock: 由hwspinlock核心初始化和使用 * @priv: 由底层特定平台的hwspinlock驱动程序拥有的私有数据 */ struct hwspinlock { struct hwspinlock_device *bank; spinlock_t lock; void *priv; };
在注册一组锁时,hwspinlock驱动程序只需要设置锁的priv成员。其余成员由hwspinlock核心自行设置和初始化。
实现回调函数
在'struct hwspinlock_ops'中定义了三个可能的回调函数:
struct hwspinlock_ops { int (*trylock)(struct hwspinlock *lock); void (*unlock)(struct hwspinlock *lock); void (*relax)(struct hwspinlock *lock); };
前两个回调函数是必需的:
->trylock()回调函数应该尝试一次获取锁,并在失败时返回0,在成功时返回1。此回调函数不得睡眠。
->unlock()回调函数释放锁。它总是成功的,它也不得睡眠。
->relax()回调函数是可选的。在自旋等待锁时,hwspinlock核心会调用它,底层实现可以使用它来在两次连续调用->trylock()之间强制延迟。它不得睡眠。