3.3.1 Cơ chế bật và vô hiệu hóa ngắt trong Linux
3.3.1.1 Ba lớp kiểm soát ngắt
Khi một ngắt vật lý được kích hoạt từ phần cứng, hệ thống phải vượt qua ba lớp kiểm soát trước khi trình xử lý ngắt (IRQ handler) được thực thi.
3.3.1.2 Lớp đầu tiên: Thanh ghi IMR của thiết bị
Lớp đầu tiên là thanh ghi Interrupt Mask Register (IMR) trên chính thiết bị phần cứng. Ví dụ với bộ điều khiển SPI, bit SPI_IMR[4] bằng 0 nghĩa là ngắt "RX FIFO FULL" bị vô hiệu hóa, và bằng 1 nghĩa là được bật. Trình điều khiển thiết bị (driver) chịu trách nhiệm quản lý các bit này theo nhu cầu.
writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR);
3.3.1.3 Lớp thứ hai: Bit bật ngắt trên bộ điều khiển ngắt
Lớp thứ hai liên quan đến bit bật/ngắt trên bộ điều khiển ngắt trung tâm (ví dụ GICv3). Mỗi IRQ có một bit tương ứng trong thanh ghi GICD_ISENABLER (bật) hoặc GICD_ICENABLER (tắt).
Nhân Linux cung cấp các hàm như:
void disable_irq(unsigned int irq);
void disable_irq_nosync(unsigned int irq);
void enable_irq(unsigned int irq);
Khi gọi disable_irq(), nhân không lập tức ghi vào thanh ghi phần cứng. Thay vào đó, nó đánh dấu IRQ là IRQD_IRQ_DISABLED. Chỉ khi ngắt thực sự xảy ra, trình xử lý mới kiểm tra cờ này và gọi irq_mask() để tắt ngắt ở phần cứng — cụ thể là ghi vào GICD_ICENABLER qua hàm gic_mask_irq().
Cách làm này giúp tránh thao tác không cần thiết với phần cứng nếu ngắt không bao giờ xảy ra giữa lúc gọi disable_irq() và enable_irq().
3.3.1.4 Lớp thứ ba: Cờ che ngắt trên CPU
Lớp cuối cùng là cờ che ngắt ở mức lõi CPU. Trên kiến trúc ARM64, thanh ghi DAIF điều khiển việc cho phép hay chặn các ngoại lệ (bao gồm ngắt IRQ).
Hệ thống cung cấp hai cặp API để thao tác với cờ này:
local_irq_disable()/local_irq_enable(): Tắt/mở ngắt một cách trực tiếp.local_irq_save(flags)/local_irq_restore(flags): Lưu trạng thái hiện tại trước khi tắt, và khôi phục nguyên trạng sau đó.
Ví dụ triển khai trên ARM64:
static inline void arch_local_irq_disable(void)
{
asm volatile("msr daifset, #2" ::: "memory");
}
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags;
asm volatile(
"mrs %0, daif\n"
"msr daifset, #2"
: "=r"(flags)
:
: "memory"
);
return flags;
}
static inline void arch_local_irq_restore(unsigned long flags)
{
asm volatile("msr daif, %0" :: "r"(flags) : "memory");
}
Sử dụng local_irq_save/restore đảm bảo tính an toàn khi lồng ghép — trạng thái ngắt ban đầu luôn được giữ nguyên sau khi thoát khỏi vùng critical section.