硬件自旋锁框架

简介

硬件自旋锁模块为异构处理器之间以及未在单个共享操作系统下运行的处理器之间的同步和互斥提供硬件辅助。

例如,OMAP4 具有双 Cortex-A9、双 Cortex-M3 和一个 C64x+ DSP,每个都运行不同的操作系统(主 A9 通常运行 Linux,而从处理器 M3 和 DSP 运行某种 RTOS)。

通用的 hwspinlock 框架允许平台无关的驱动程序使用 hwspinlock 设备来访问在远程处理器之间共享的数据结构,否则它们没有其他机制来完成同步和互斥操作。

例如,对于处理器间通信,这是必要的:在 OMAP4 上,计算密集型多媒体任务由主机卸载到远程 M3 和/或 C64x+ 从处理器(通过名为 Syslink 的 IPC 子系统)。

为了实现快速的基于消息的通信,需要最小的内核支持来将来自远程处理器的消息传递到适当的用户进程。

这种通信基于在远程处理器之间共享的简单数据结构,并且对其的访问使用 hwspinlock 模块进行同步(远程处理器直接将新消息放入此共享数据结构中)。

通用的 hwspinlock 接口使得拥有通用的、平台无关的驱动程序成为可能。

用户 API

struct hwspinlock *hwspin_lock_request_specific(unsigned int id);

分配特定的 hwspinlock id 并返回其地址,如果该 hwspinlock 已经被使用,则返回 NULL。 通常,板级代码会调用此函数,以便为预定义目的保留特定的 hwspinlock 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);

释放先前分配的 hwspinlock;成功时返回 0,失败时返回相应的错误代码(例如,如果 hwspinlock 已经空闲,则返回 -EINVAL)。

应从进程上下文中调用(可能休眠)。

int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id);

验证 hwspinlock 的所有者后,释放先前获取的 hwspinlock;成功时返回 0,失败时返回相应的错误代码(例如,如果未为特定 hwspinlock 定义 bust 操作,则返回 -EOPNOTSUPP)。

应从进程上下文中调用(可能休眠)。

int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout);

锁定先前分配的 hwspinlock,并具有超时限制(以毫秒为单位指定)。如果 hwspinlock 已经被占用,该函数将忙循环等待其释放,但在超时时间过后放弃。 在成功从此函数返回后,抢占将被禁用,因此调用者不得休眠,并建议尽快释放 hwspinlock,以最大程度地减少远程内核在硬件互连上的轮询。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 在超时 msecs 后仍然繁忙,则返回 -ETIMEDOUT)。该函数永远不会休眠。

int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int timeout);

锁定先前分配的 hwspinlock,并具有超时限制(以毫秒为单位指定)。如果 hwspinlock 已经被占用,该函数将忙循环等待其释放,但在超时时间过后放弃。 成功从此函数返回后,抢占和本地中断将被禁用,因此调用者不得休眠,并建议尽快释放 hwspinlock。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 在超时 msecs 后仍然繁忙,则返回 -ETIMEDOUT)。该函数永远不会休眠。

int hwspin_lock_timeout_irqsave(struct hwspinlock *hwlock, unsigned int to,
                                unsigned long *flags);

锁定先前分配的 hwspinlock,并具有超时限制(以毫秒为单位指定)。如果 hwspinlock 已经被占用,该函数将忙循环等待其释放,但在超时时间过后放弃。 成功从此函数返回后,抢占将被禁用,本地中断将被禁用,并且其先前的状态将保存在给定的标志占位符中。 调用者不得休眠,并建议尽快释放 hwspinlock。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 在超时 msecs 后仍然繁忙,则返回 -ETIMEDOUT)。

该函数永远不会休眠。

int hwspin_lock_timeout_raw(struct hwspinlock *hwlock, unsigned int timeout);

锁定先前分配的 hwspinlock,并具有超时限制(以毫秒为单位指定)。如果 hwspinlock 已经被占用,该函数将忙循环等待其释放,但在超时时间过后放弃。

注意:用户必须使用互斥锁或自旋锁保护获取硬件锁的例程,以避免死锁,这将允许用户在硬件锁下执行一些耗时或可休眠的操作。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 在超时 msecs 后仍然繁忙,则返回 -ETIMEDOUT)。

该函数永远不会休眠。

int hwspin_lock_timeout_in_atomic(struct hwspinlock *hwlock, unsigned int to);

锁定先前分配的 hwspinlock,并具有超时限制(以毫秒为单位指定)。如果 hwspinlock 已经被占用,该函数将忙循环等待其释放,但在超时时间过后放弃。

该函数应仅从原子上下文中调用,并且超时值不得超过几毫秒。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 在超时 msecs 后仍然繁忙,则返回 -ETIMEDOUT)。

该函数永远不会休眠。

int hwspin_trylock(struct hwspinlock *hwlock);

尝试锁定先前分配的 hwspinlock,但如果已经被占用,则立即失败。

在成功从此函数返回后,抢占将被禁用,因此调用者不得休眠,并建议尽快释放 hwspinlock,以最大程度地减少远程内核在硬件互连上的轮询。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 已经被占用,则返回 -EBUSY)。该函数永远不会休眠。

int hwspin_trylock_irq(struct hwspinlock *hwlock);

尝试锁定先前分配的 hwspinlock,但如果已经被占用,则立即失败。

在成功从此函数返回后,抢占和本地中断将被禁用,因此调用者不得休眠,并建议尽快释放 hwspinlock。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 已经被占用,则返回 -EBUSY)。

该函数永远不会休眠。

int hwspin_trylock_irqsave(struct hwspinlock *hwlock, unsigned long *flags);

尝试锁定先前分配的 hwspinlock,但如果已经被占用,则立即失败。

在成功从此函数返回后,抢占将被禁用,本地中断将被禁用,并且其先前的状态将保存在给定的标志占位符中。 调用者不得休眠,并建议尽快释放 hwspinlock。

成功时返回 0,否则返回相应的错误代码(最值得注意的是,如果 hwspinlock 已经被占用,则返回 -EBUSY)。该函数永远不会休眠。

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。这样做被认为是错误(没有针对此的保护)。成功从此函数返回后,将重新启用抢占,并且本地中断的状态将恢复为保存在给定标志中的状态。该函数永远不会休眠。

void hwspin_unlock_raw(struct hwspinlock *hwlock);

解锁先前锁定的 hwspinlock。

调用者**永远不应**解锁已经解锁的 hwspinlock。这样做被认为是错误(没有针对此的保护)。该函数永远不会休眠。

void hwspin_unlock_in_atomic(struct hwspinlock *hwlock);

解锁先前锁定的 hwspinlock。

调用者**永远不应**解锁已经解锁的 hwspinlock。这样做被认为是错误(没有针对此的保护)。该函数永远不会休眠。

典型用法

#include <linux/hwspinlock.h>
#include <linux/err.h>

int hwspinlock_example(void)
{
        struct hwspinlock *hwlock;
        int ret;

        /*
        * assign a specific hwspinlock id - this should be called early
        * by board init code.
        */
        hwlock = hwspin_lock_request_specific(PREDEFINED_LOCK_ID);
        if (!hwlock)
                ...

        /* try to take it, but don't spin on it */
        ret = hwspin_trylock(hwlock);
        if (!ret) {
                pr_info("lock is already taken\n");
                return -EBUSY;
        }

        /*
        * we took the lock, do our thing now, but do NOT sleep
        */

        /* release the lock */
        hwspin_unlock(hwlock);

        /* free the lock */
        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);

从底层平台特定的实现中调用,以便注册新的 hwspinlock 设备(通常是大量锁的集合)。 应从进程上下文中调用(此函数可能休眠)。

成功时返回 0,失败时返回相应的错误代码。

int hwspin_lock_unregister(struct hwspinlock_device *bank);

从底层供应商特定的实现中调用,以便注销 hwspinlock 设备(通常是大量锁的集合)。

应从进程上下文中调用(此函数可能休眠)。

成功时返回 hwspinlock 的地址,错误时返回 NULL(例如,如果 hwspinlock 仍在被使用)。

重要结构体

struct hwspinlock_device 是一个设备,通常包含硬件锁的集合。它由底层的 hwspinlock 实现使用 hwspin_lock_register() API 注册。

/**
* struct hwspinlock_device - a device which usually spans numerous hwspinlocks
* @dev: underlying device, will be used to invoke runtime PM api
* @ops: platform-specific hwspinlock handlers
* @base_id: id index of the first lock in this device
* @num_locks: number of locks in this device
* @lock: dynamically allocated array of '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 - this struct represents a single hwspinlock instance
* @bank: the hwspinlock_device structure which owns this lock
* @lock: initialized and used by hwspinlock core
* @priv: private data, owned by the underlying platform-specific hwspinlock drv
*/
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() 调用之间的时间延迟。它**不能**休眠。