MSI 向量必須連續?
前言
MSI 物理條件,MSI 中斷產生的邏輯是RC初始化的時候,由軟件將配置寫入到 EP 的 2 個寄存器中,這兩個寄存器一個指示的是地址 Message Address,一個指示的是數據 Message Data。當 EP 試圖觸發中斷的時候,將會發起一個 MW 事務,往對應的 Message Address 寫入 Message Data 的值。
引入問題
MSI 可以配置多達 32 個中斷,那么如果是一個相同的 MW 事務,往對應的 Message Address 寫入 Message Data 的值,而 Message Data 的值一直是 RC 初始化時配置的值,那么 RC 怎么可以區分不同的中斷呢?
分析問題
上面的問題,寫入同樣的 Message Data 肯定是不能解決的。所以 EP 寫的 Data 肯定是變化的,這樣 RC 才能區分不同的中斷。所以修改代碼進行驗證。
驗證步驟
- 硬件 CPU: FT E2000 PCIe: NVME 硬盤 (CPU 使用的 ITS 的中斷方式來處理 MSI 中斷)
- 修改代碼邏輯
__pci_write_msi_msg
為最后的將 msi_msg 寫入 EP 配置空間的操作函數。
- 修改1:struct msi_msg *msg 傳入的數據結構里面 Message Address 是 ITS 的某個寄存器的地址,Message Data 是
its_get_event_id
獲取的 id 。申請一個非緩存的內存空間__phys_addr
來冒充這個 Message Address。隨機選取了 Message Data 為 0xF0 來觀察變化。
// 分配非緩存空間noncache_memory = kmalloc(ALLOC_SIZE, GFP_KERNEL | __GFP_HIGHMEM);if (!noncache_memory) {printk(KERN_ERR "Failed to allocate non-cacheable memory\n");return -ENOMEM;} __phys_addr = virt_to_phys(noncache_memory);printk(KERN_INFO "Non-cacheable memory allocated. Physical Address: %pa\n", &__phys_addr);
- 修改 2:創建一個定時器,來定時查詢這個
__phys_addr
的值有沒有被修改
#define ALLOC_SIZE 128
static void* noncache_memory;
static phys_addr_t __phys_addr;
static void __iomem *its_virtual_address;#include <linux/timer.h>
struct irq_data * irq_data_save[128] = {0};//irq_chip_retrigger_hierarchy
// 內核線程函數
static struct timer_list my_timer;#define DATA_MSI 0xF0static void my_kernel_thread(struct timer_list *timer) {int *paddr = noncache_memory;if (*paddr != 0) {printk("msi data change :0x%x irq_data:%px", *paddr, (void*)irq_data_save[97 + (*paddr-(DATA_MSI))]);irq_chip_retrigger_hierarchy(irq_data_save[97 + (*paddr-DATA_MSI)]);*paddr = 0;}// 重新設置定時器,以實現循環定時mod_timer(timer, jiffies + msecs_to_jiffies(1));
}
代碼中使用 irq_chip_retrigger_hierarchy
來觸發 ITS 的中斷。97 是寫死了,是人為查看系統中 MSI 中斷的號起始。
實驗結果
nvme 可以正常使用,CPU 是 2 核心,系統中創建了 3 個 MSI 的中斷號,97 98 99 一共 3 個中斷。
通過觀察打印,可以分析得出,EP試圖觸發中斷時,會往剛剛修改的 __phys_addr
的這個內存地址寫入數據,有 0xF0, 0xF1, 0xF2 。