硬件自旋锁框架

简介

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

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

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

例如,这对于处理器间通信(IPC)是必要的:在 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 未定义强制释放操作,则返回 -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 已经被占用,该函数将忙等待其释放,但在超时后放弃。

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

成功时返回 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,如果它已被占用则立即失败。

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

成功时返回 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。

这样做被认为是一个 bug(对此没有保护机制)。从该函数成功返回后,抢占和本地中断将被启用。该函数绝不会休眠。

void
hwspin_unlock_irqrestore(struct hwspinlock *hwlock, unsigned long *flags);

解锁先前锁定的 hwspinlock。

调用者绝不应该解锁一个已经处于解锁状态的 hwspinlock。这样做被认为是一个 bug(对此没有保护机制)。从该函数成功返回后,抢占将被重新启用,并且本地中断的状态将恢复到保存在给定 flags 中的状态。该函数绝不会休眠。

void hwspin_unlock_raw(struct hwspinlock *hwlock);

解锁先前锁定的 hwspinlock。

调用者绝不应该解锁一个已经处于解锁状态的 hwspinlock。这样做被认为是一个 bug(对此没有保护机制)。该函数绝不会休眠。

void hwspin_unlock_in_atomic(struct hwspinlock *hwlock);

解锁先前锁定的 hwspinlock。

调用者绝不应该解锁一个已经处于解锁状态的 hwspinlock。这样做被认为是一个 bug(对此没有保护机制)。该函数绝不会休眠。

典型用法

#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() 之间产生延迟。它不得休眠。