硬體自旋鎖框架

簡介

硬體自旋鎖模組為異構處理器之間以及未在單個共享作業系統下執行的處理器之間的同步和互斥提供硬體輔助。

例如,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() 呼叫之間的時間延遲。它**不能**休眠。