日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux rt patch 强实时,Linux RT(2)-硬实时Linux(RT-Preempt Patch)的中断线程化

發布時間:2023/12/20 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux rt patch 强实时,Linux RT(2)-硬实时Linux(RT-Preempt Patch)的中断线程化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

特別聲明:本系列文章LiAnLab.org著作權所有,轉載請注明出處。by? @宋寶華Barry

底半部:線程化IRQ

線程化中斷的支持在2009年已經進入Linux官方內核,詳見Thomas Gleixner的patch:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=3aa551c9b4c40018f0e261a178e3d25478dc04a9

該patch提供一個能力,驅動可以通過

int request_threaded_irq(unsigned int irq, irq_handler_t handler,

irq_handler_t thread_fn, unsigned long irqflags,

const char *devname, void *dev_id)

申請一個線程化的IRQ,kernel會為中斷的底半部創建一個名字為irq/%d-%s的線程,%d對應著中斷號。其中頂半部(硬中斷)handler在做完必要的處理工作之后,會返回IRQ_WAKE_THREAD,之后kernel會喚醒irq/%d-%s線程,而該kernel線程會調用thread_fn函數,因此,該線程成為底半部。在后續維護的過程中,筆者曾參與進一步完善該功能的討論,后續patch包括nested、oneshot等的支持,詳見patch:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=399b5da29b9f851eb7b96e2882097127f003e87c

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=70aedd24d20e75198f5a0b11750faabbb56924e2

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b25c340c195447afb1860da580fe2a85a6b652c5

該機制目前在kernel中使用已經十分廣泛,可以認為是繼softirq(含tasklet)和workqueue之后的又一大中斷底半部方式。

頂半部:強制線程化

在使能Linux RT-Preempt后,默認情況下會強制透過request_irq()申請的IRQ的頂半部函數在線程中執行,我們都知道request_irq的原型為:

static inline int __must_check

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

const char *name, void *dev)

{

return request_threaded_irq(irq, handler, NULL, flags, name, dev);

}

這意味著通過request_irq()申請的IRQ,在沒有Rt-Preepmt的情況下,kernel并不會為其創建irq線程,因為它在最終調用request_threaded_irq()的時候傳遞的thread_fn為NULL。

如果使能了RT-Preempt Patch的情況下,其中的genirq-force-threading.patch會強制ARM使用threaded irq:

Index: linux-stable/arch/arm/Kconfig

===================================================================

--- linux-stable.orig/arch/arm/Kconfig

+++ linux-stable/arch/arm/Kconfig

@@ -40,6 +40,7 @@ config ARM

select GENERIC_IRQ_SHOW

select ARCH_WANT_IPC_PARSE_VERSION

select HARDIRQS_SW_RESEND

+ select IRQ_FORCED_THREADING

select CPU_PM if (SUSPEND || CPU_IDLE)

select GENERIC_PCI_IOMAP

select HAVE_BPF_JIT

在RT-Preempt Patch中,會針對使能了IRQ_FORCED_THREADING的情況,對這一原先沒有線程化IRQ的case進行強制線程化,代碼見__setup_irq():

887 static int

888 __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)

889 {

890 ...

903

904 /*

905 * Check whether the interrupt nests into another interrupt

906 * thread.

907 */

908 nested = irq_settings_is_nested_thread(desc);

909 if (nested) {

910 ...

920 } else {

921 if (irq_settings_can_thread(desc))

922 irq_setup_forced_threading(new);

923 }

925 /*

926 * Create a handler thread when a thread function is supplied

927 * and the interrupt does not nest into another interrupt

928 * thread.

929 */

930 if (new->thread_fn && !nested) {

931 struct task_struct *t;

932

933 t = kthread_create(irq_thread, new, "irq/%d-%s", irq,

934 new->name);

935 ...

939 /*

940 * We keep the reference to the task struct even if

941 * the thread dies to avoid that the interrupt code

942 * references an already freed task_struct.

943 */

944 get_task_struct(t);

945 new->thread = t;

946 }

我們重點看一下其中的921行:

867 static void irq_setup_forced_threading(struct irqaction *new)

868 {

869 if (!force_irqthreads)

870 return;

871 if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))

872 return;

873

874 new->flags |= IRQF_ONESHOT;

875

876 if (!new->thread_fn) {

877 set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);

878 new->thread_fn = new->handler;

879 new->handler = irq_default_primary_handler;

880 }

881 }

第878行和879行,強制將原先的handler復制給thread_fn,而又強制把原來的handler變更為irq_default_primary_handler(),而這個函數,其實神馬都不做,只是直接返回IRQ_WAKE_THREAD:

613 /*

614 * Default primary interrupt handler for threaded interrupts. Is

615 * assigned as primary handler when request_threaded_irq is called

616 * with handler == NULL. Useful for oneshot interrupts.

617 */

618 static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)

619 {

620 return IRQ_WAKE_THREAD;

621 }

第874的IRQF_ONESHOT就用到了我們前面說的oneshot功能。

所以,RT-Preempt實際上是把原先的頂半部底半部化了,而現在偽造了一個假的頂半部,它只是直接返回一個IRQ_WAKE_THREAD標記而已。

我們來看一下一個中斷發生后,Linux RT-Preempt處理的全過程,首先是會跳到

arch/arm/kernel/entry-armv.S

arch/arm/include/asm/entry-macro-multi.S

中的匯編入口,再進入arm/kernel/irq.c下的asm_do_IRQ 、handle_IRQ,之后generic的handle_irq_event_percpu()被調用:

133 handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)

134 {

135 irqreturn_t retval = IRQ_NONE;

136 unsigned int flags = 0, irq = desc->irq_data.irq;

137

138 do {

139 irqreturn_t res;

140

141 trace_irq_handler_entry(irq, action);

142 res = action->handler(irq, action->dev_id);

143 trace_irq_handler_exit(irq, action, res);

144

145 if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interruptsn",

146 irq, action->handler))

147 local_irq_disable();

148

149 switch (res) {

150 case IRQ_WAKE_THREAD:

151 /*

152 * Catch drivers which return WAKE_THREAD but

153 * did not set up a thread function

154 */

155 if (unlikely(!action->thread_fn)) {

156 warn_no_thread(irq, action);

157 break;

158 }

159

160 irq_wake_thread(desc, action);

161

162 /* Fall through to add to randomness */

163 case IRQ_HANDLED:

164 flags |= action->flags;

165 break;

166

167 default:

我們關注其中的第142行,本質上是調用irq_default_primary_handler(),接到150行,由于 irq_default_primary_handler()返回了IRQ_WAKE_THREAD,因此,generic的中斷處理流程會執行 irq_wake_thread(desc, action);去喚醒前面的irq/%d-%s線程,該線程的代碼是

789 static int irq_thread(void *data)

790 {

791 static const struct sched_param param = {

792 .sched_priority = MAX_USER_RT_PRIO/2,

793 };

794 struct irqaction *action = data;

795 struct irq_desc *desc = irq_to_desc(action->irq);

796 irqreturn_t (*handler_fn)(struct irq_desc *desc,

797 struct irqaction *action);

798

799 if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,

800 &action->thread_flags))

801 handler_fn = irq_forced_thread_fn;

802 else

803 handler_fn = irq_thread_fn;

804

805 sched_setscheduler(current, SCHED_FIFO, ?m);

806 current->irq_thread = 1;

807

808 while (!irq_wait_for_interrupt(action)) {

809 irqreturn_t action_ret;

810

811 irq_thread_check_affinity(desc, action);

812

813 action_ret = handler_fn(desc, action);

814 if (!noirqdebug)

815 note_interrupt(action->irq, desc, action_ret);

816

817 wake_threads_waitq(desc);

818 }

819

820 /*

821 * This is the regular exit path. __fr

其中的813行會調用最終的被賦值給thread_fn的原來的handler,這樣原來的中斷頂半部就整個在irq_thread里面執行了,實現了所謂的頂半部的線程化。

繞開頂半部線程化

當然,在使能了RT-Preempt的情況之下,我們仍然可以繞開頂半部線程化的過程,避免前面的強勢變更,只需要申請中斷的時候設置IRQ_NOTHREAD標志,如其中的patch:

Subject: arm: Mark pmu interupt IRQF_NO_THREAD

From: Thomas Gleixner

Date: Wed, 16 Mar 2011 14:45:31 +0100

PMU interrupt must not be threaded. Remove IRQF_DISABLED while at it

as we run all handlers with interrupts disabled anyway.

Signed-off-by: Thomas Gleixner

---

arch/arm/kernel/perf_event.c | 2 +-

1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-stable/arch/arm/kernel/perf_event.c

===================================================================

--- linux-stable.orig/arch/arm/kernel/perf_event.c

+++ linux-stable/arch/arm/kernel/perf_event.c

@@ -430,7 +430,7 @@ armpmu_reserve_hardware(struct arm_pmu *

}

err = request_irq(irq, handle_irq,

- IRQF_DISABLED | IRQF_NOBALANCING,

+ IRQF_NOBALANCING | IRQF_NO_THREAD,

"arm-pmu", armpmu);

if (err) {

r_err("unable to request IRQ%d for ARM PMU countersn",

總結

以上是生活随笔為你收集整理的linux rt patch 强实时,Linux RT(2)-硬实时Linux(RT-Preempt Patch)的中断线程化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。