硬件自旋锁框架

简介

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

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

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

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

为了实现快速的基于消息的通信,需要最少的内核支持来将从远程处理器到达的消息传递给相应的用户进程。

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

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

用户 API

struct hwspinlock *hwspin_lock_request(void);

动态分配一个 hwspinlock 并返回其地址,如果没有可用的未使用 hwspinlock,则返回 NULL。 此 API 的用户通常希望在可以使用锁来实现同步之前,将锁的 ID 传达给远程核心。

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

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 在超时毫秒后仍然忙碌,则返回 -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,但如果它已被占用,则立即失败。

在此函数成功返回后,抢占被禁用,因此调用者不得睡眠,并建议尽快释放 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,但如果它已被占用,则立即失败。

在此函数成功返回后,抢占被禁用,本地中断被禁用,并且它们的先前状态保存在给定的 flags 占位符处。 调用者不得睡眠,并建议尽快释放 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。 这样做被认为是错误(没有针对这种情况的保护)。 在此函数成功返回后,抢占被重新启用,并且本地中断的状态恢复为保存在给定的 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 时,这是必要的:在使用它与远程 CPU 实现互斥之前,应将 ID 号传递给我们要与之同步的远程任务。

返回 hwspinlock ID 号,如果 hwlock 为 null,则返回 -EINVAL。

典型用法

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

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

        /* dynamically assign a hwspinlock */
        hwlock = hwspin_lock_request();
        if (!hwlock)
                ...

        id = hwspin_lock_get_id(hwlock);
        /* probably need to communicate id to a remote processor now */

        /* take the lock, spin for 1 sec if it's already taken */
        ret = hwspin_lock_timeout(hwlock, 1000);
        if (ret)
                ...

        /*
        * 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;
}

int hwspinlock_example2(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];
};

结构体 `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()` 之间产生延迟。它**不能**睡眠。