linux AArch64中断下半部之软中断softirq

  • 1 软中断的概念
  • 2 软中断执行的时机
    • 2.1 软中断执行时机
    • 2.2 irq_exit
    • 2.3 invoke_softirq
  • 3 软中断关键函数和数据结构
    • 3.1 软中断的类型
    • 3.2 注册软中断
      • 3.2.1 软中断注册函数定义
      • 3.2.2 软中断注册样例
    • 3.3 触发软中断
      • 3.3.1 触发软中断处理的接口
      • 3.3.2 触发软中断处理的示例
    • 3.4 软中断处理函数`__do_softirq`
  • 4 tasklet
    • 4.1 tasklet数据结构
    • 4.2 声明一个tasklet
      • 4.2.1 DECLARE_TASKLET
      • 4.2.2 tasklet_init
      • 4.2.3 tasklet_init使用样例
    • 4.3 调度一个tasklet
    • 4.4 如何在驱动里面定义一个tasklet

1 软中断的概念

软中断是一种软件实现的机制,而非硬件实现的中断。软中断属于中断上下文,当软中断在执行时,task无法打断软中断执行。

  • 软中断的类型是静态定义的,内核不建议新增加软中断类型
  • 软中断的回调函数是在开中断的情况下执行的
  • 软中断的执行点:在硬中断处理函数返回之前irq_exit()
  • 软中断属于中断上下文,软中断可以抢占进程上下文
  • 同一类型的软中断可以在多个处理器上并行执行
  • tasklet属于一种特殊的软中断,相同的tasklet在整个系统上只有一个可以执行,但是不同的tasklet可以同时在不同的处理器上运行(tasklet
    is running only on one CPU simultaneously,different tasklets
    may be run simultaneously on different CPUs.)

2 软中断执行的时机

2.1 软中断执行时机

中断处理函数执行完成,返回中断中断现场之前(irq_exit)会去检查:

  • 检查当前是否处于中断上下文in_interrupt()
  • 检查系统是否有待处理的软中断local_softirq_pending()
  • 调用invoke_softirq()去处理软中断
    如果当前不处于中断上下文并且有待处理的软中断,则会调用invoke_softirq()去处理软中断。

2.2 irq_exit

代码路径:kernel/softirq.c

/** Exit an interrupt context. Process softirqs if needed and possible:*/
void irq_exit(void)
{#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLEDlocal_irq_disable();
#elseWARN_ON_ONCE(!irqs_disabled());
#endifaccount_irq_exit_time(current);preempt_count_sub(HARDIRQ_OFFSET);if (!in_interrupt() && local_softirq_pending())invoke_softirq();tick_irq_exit();rcu_irq_exit();trace_hardirq_exit(); /* must be last! */
}

2.3 invoke_softirq

代码路径:kernel/softirq.c

static inline void invoke_softirq(void)
{if (!force_irqthreads) {#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK/** We can safely execute softirq on the current stack if* it is the irq stack, because it should be near empty* at this stage.*/__do_softirq();
#else/** Otherwise, irq_exit() is called on the task stack that can* be potentially deep already. So call softirq in its own stack* to prevent from any overrun.*/do_softirq_own_stack();
#endif} else {wakeup_softirqd();}
}

3 软中断关键函数和数据结构

3.1 软中断的类型

代码路径:include/linux/interrupt.h

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ highfrequency threaded job scheduling. For almost all the purposestasklets are more than enough. F.e. all serial device BHs etal. should be converted to tasklets, not to softirqs.*/enum
{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,BLOCK_IOPOLL_SOFTIRQ,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on thenumbering. Sigh! */RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */NR_SOFTIRQS
};

3.2 注册软中断

代码路径:kernel/softirq.c

3.2.1 软中断注册函数定义

void open_softirq(int nr, void (*action)(struct softirq_action *))
{softirq_vec[nr].action = action;
}

3.2.2 软中断注册样例

示例代码路径:net/core/dev.c

/**      Initialize the DEV module. At boot time this walks the device list and*      unhooks any devices that fail to initialise (normally hardware not*      present) and leaves us with a valid list of present and active devices.**//**       This is called single threaded during boot, so no need*       to take the rtnl semaphore.*/
static int __init net_dev_init(void)
{...open_softirq(NET_TX_SOFTIRQ, net_tx_action);open_softirq(NET_RX_SOFTIRQ, net_rx_action);...
}

3.3 触发软中断

触发软中断其实是在中断中通过调用raise_softirq_irqoff或者raise_softirq设置_softirq_pending位图,当从异常返回执行到irq_exit()函数时,会检查_softirq_pending是否有设置的软中断待处理。

3.3.1 触发软中断处理的接口

代码路径:kernel/softirq.c

/** This function must run with irqs disabled!*/
inline void raise_softirq_irqoff(unsigned int nr)
{__raise_softirq_irqoff(nr);/** If we're in an interrupt or softirq, we're done* (this also catches softirq-disabled code). We will* actually run the softirq once we return from* the irq or softirq.** Otherwise we wake up ksoftirqd to make sure we* schedule the softirq soon.*/if (!in_interrupt())wakeup_softirqd();
}void raise_softirq(unsigned int nr)
{unsigned long flags;local_irq_save(flags);raise_softirq_irqoff(nr);local_irq_restore(flags);
}

3.3.2 触发软中断处理的示例

在触发软中断处理时,通常会使用raise_softirq_irqoffraise_softirq函数

   6     51  block/blk-softirq.c <<<unknown>>>raise_softirq_irqoff(BLOCK_SOFTIRQ);7     94  block/blk-softirq.c <<<unknown>>>raise_softirq_irqoff(BLOCK_SOFTIRQ);8    148  block/blk-softirq.c <<<unknown>>>raise_softirq_irqoff(BLOCK_SOFTIRQ);12    784  drivers/irqchip/irq-gic.c <<<unknown>>>static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)13   1157  drivers/irqchip/irq-gic.c <<<unknown>>>set_smp_cross_call(gic_raise_softirq);14    279  drivers/irqchip/irq-hip04.c <<<unknown>>>static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)16   2288  drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_linux.c <<<unknown>>>* This function will essentially invoke __raise_softirq_irqoff(NET_RX_SOFTIRQ)17    131  drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/include/linuxver.h <<<unknown>>>cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)18    123  drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/wl_iw.c <<<unknown>>>cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)19   1025  drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_lb.c <<<unknown>>>* This function will essentially invoke __raise_softirq_irqoff(NET_RX_SOFTIRQ)20   1042  drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dhd_linux_lb.c <<<unknown>>>raise_softirq(NET_RX_SOFTIRQ);

3.4 软中断处理函数__do_softirq

__do_softirq函数需要重点关注一下几个部分:

  • pending = local_softirq_pending();获取有哪些软中断被置位
  • h = softirq_vec;
  • h += softirq_bit - 1;
  • h->action(h);
  • 当中断不满足跳出条件时,会一直执行goto restart去执行软中断处理函数;软中断退出的条件如下所示:
    • time_before(jiffies, end) /* end = jiffies + MAX_SOFTIRQ_TIME 软中断允许的最长占用时间为2s */
    • !need_resched() /* 检查TIF_NEED_RESCHED*/
    • –max_restart /* max_restart = MAX_SOFTIRQ_RESTART; 调度次数最多为MAX_SOFTIRQ_RESTART */
  • 当退出软中断时还有需要处理的软中断则会通过调用wakeup_softirqd()函数去唤醒softirqd线程去处理剩余的软中断。
asmlinkage __visible void __softirq_entry __do_softirq(void)
{unsigned long end = jiffies + MAX_SOFTIRQ_TIME;unsigned long old_flags = current->flags;int max_restart = MAX_SOFTIRQ_RESTART;struct softirq_action *h;bool in_hardirq;__u32 pending;int softirq_bit;/** Mask out PF_MEMALLOC s current task context is borrowed for the* softirq. A softirq handled such as network RX might set PF_MEMALLOC* again if the socket is related to swap*/current->flags &= ~PF_MEMALLOC;pending = local_softirq_pending();account_irq_enter_time(current);__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);in_hardirq = lockdep_softirq_start();restart:/* Reset the pending bitmask before enabling irqs */set_softirq_pending(0);local_irq_enable();h = softirq_vec;while ((softirq_bit = ffs(pending))) {unsigned int vec_nr;int prev_count;h += softirq_bit - 1;vec_nr = h - softirq_vec;prev_count = preempt_count();kstat_incr_softirqs_this_cpu(vec_nr);trace_softirq_entry(vec_nr);h->action(h);trace_softirq_exit(vec_nr);if (unlikely(prev_count != preempt_count())) {pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",vec_nr, softirq_to_name[vec_nr], h->action,prev_count, preempt_count());preempt_count_set(prev_count);}h++;pending >>= softirq_bit;}rcu_bh_qs();local_irq_disable();pending = local_softirq_pending();if (pending) {   /* 存在软中断需要处理 */if (time_before(jiffies, end) && !need_resched() &&  /* 软中断调度时间最长为2s, 当前系统不需要调度并且最大执行次数没有超过MAX_SOFTIRQ_RESTART的限制 */--max_restart)goto restart;   /* 继续处理剩余的软中断 */wakeup_softirqd();  /* 剩余尚未来的及处理的软中断通过唤醒softirqd线程去处理 */}lockdep_softirq_end(in_hardirq);account_irq_exit_time(current);__local_bh_enable(SOFTIRQ_OFFSET);WARN_ON_ONCE(in_interrupt());tsk_restore_flags(current, old_flags, PF_MEMALLOC);
}

4 tasklet

tasklet是一种特殊的软中断TASKLET_SOFTIRQ

4.1 tasklet数据结构

代码路径:include/linux/interrupt.h

/* Tasklets --- multithreaded analogue of BHs.Main feature differing them of generic softirqs: taskletis running only on one CPU simultaneously.Main feature differing them of BHs: different taskletsmay be run simultaneously on different CPUs.Properties:* If tasklet_schedule() is called, then tasklet is guaranteedto be executed on some cpu at least once after this.* If the tasklet is already scheduled, but its execution is still notstarted, it will be executed only once.* If this tasklet is already running on another CPU (or schedule is calledfrom tasklet itself), it is rescheduled for later.* Tasklet is strictly serialized wrt itself, but notwrt another tasklets. If client needs some intertask synchronization,he makes it with spinlocks.*/struct tasklet_struct
{struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
};

4.2 声明一个tasklet

4.2.1 DECLARE_TASKLET

#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

4.2.2 tasklet_init

void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
{t->next = NULL;t->state = 0;atomic_set(&t->count, 0);t->func = func;t->data = data;
}
EXPORT_SYMBOL(tasklet_init);

4.2.3 tasklet_init使用样例

   1   1843  drivers/atm/eni.c <<<unknown>>>tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev);2   2043  drivers/atm/fore200e.c <<<unknown>>>tasklet_init(&fore200e->tx_tasklet, fore200e_tx_tasklet, (unsigned long)fore200e);3   2044  drivers/atm/fore200e.c <<<unknown>>>tasklet_init(&fore200e->rx_tasklet, fore200e_rx_tasklet, (unsigned long)fore200e);4    386  drivers/atm/he.c <<<unknown>>>tasklet_init(&he_dev->tasklet, he_tasklet, (unsigned long) he_dev);5   1304  drivers/atm/solos-pci.c <<<unknown>>>tasklet_init(&card->tlet, solos_bh, (unsigned long)card);6    903  drivers/block/umem.c <<<unknown>>>tasklet_init(&card->tasklet, process_page, (unsigned long)card);7    986  drivers/block/xsysace.c <<<unknown>>>tasklet_init(&ace->fsm_tasklet, ace_fsm_tasklet, (unsigned long)ace);8   2820  drivers/char/ipmi/ipmi_msghandler.c <<<unknown>>>tasklet_init(&intf->recv_tasklet,9    838  drivers/char/mmtimer.c <<<unknown>>>tasklet_init(&timers[node].tasklet, mmtimer_tasklet,10   1198  drivers/crypto/amcc/crypto4xx_core.c <<<unknown>>>tasklet_init(&core_dev->tasklet, crypto4xx_bh_tasklet_cb,11   1357  drivers/crypto/atmel-aes.c <<<unknown>>>tasklet_init(&aes_dd->done_task, atmel_aes_done_task,12   1359  drivers/crypto/atmel-aes.c <<<unknown>>>tasklet_init(&aes_dd->queue_task, atmel_aes_queue_task,13   1370  drivers/crypto/atmel-sha.c <<<unknown>>>tasklet_init(&sha_dd->done_task, atmel_sha_done_task,14   1378  drivers/crypto/atmel-tdes.c <<<unknown>>>tasklet_init(&tdes_dd->done_task, atmel_tdes_done_task,15   1380  drivers/crypto/atmel-tdes.c <<<unknown>>>tasklet_init(&tdes_dd->queue_task, atmel_tdes_queue_task,16    587  drivers/crypto/bfin_crc.c <<<unknown>>>tasklet_init(&crc->done_task, bfin_crypto_crc_done_task, (unsigned long)crc);

4.3 调度一个tasklet

__tasklet_schedule会通过调用raise_softirq_irqoff(TASKLET_SOFTIRQ);去设置_softirq_pending,

void __tasklet_schedule(struct tasklet_struct *t)
{unsigned long flags;local_irq_save(flags);t->next = NULL;*__this_cpu_read(tasklet_vec.tail) = t;__this_cpu_write(tasklet_vec.tail, &(t->next));raise_softirq_irqoff(TASKLET_SOFTIRQ);local_irq_restore(flags);
}
EXPORT_SYMBOL(__tasklet_schedule);

4.4 如何在驱动里面定义一个tasklet

  • 首先要初始化一个tasklet,可以通过DECLARE_TASKLET或者tasklet_init去定义一个tasklet
  • 实现tasklet的处理函数
  • 调度自己的tasklet

linux AArch64中断下半部之软中断softirq相关推荐

  1. Android之linux基础教学之七 中断下半部之软中断

    本文章假设读者知道什么叫做中断,并阅读过相关汇编代码,明白中断的处理流程.本文章介绍怎么使用软中断来执行下半部.   大小: 41.9 KB 大小: 71 KB 查看图片附件

  2. linux中断机制--理解中断上半部/下半部、软中断、tasklet、工作队列(可调度、可睡眠)

    1. 中断vs轮询 Linux 内核需要对连接到计算机上的所有硬件设备进行管理,毫无疑问这是它的份内事.如果要管理这些设备,首先得和它们互相通信才行,一般有两种方案可实现这种功能: 中断(interr ...

  3. Linux内核深入理解中断和异常(7):中断下半部:Softirq, Tasklets and Workqueues

    Linux内核深入理解中断和异常(7):中断下半部:Softirq, Tasklets and Workqueues rtoax 2021年3月 0x00-0x1f architecture-defi ...

  4. 【linux kernel】 中断处理-中断下半部【转】

    转自:http://www.cnblogs.com/embedded-tzp/p/4453987.html 欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地 ...

  5. linux中断下半部

    linux中断下半部 6.1下半部 下半部的任务就是执行与中断处理密切相关但中断处理程序本身不执行的工作.对于在上半部和下半部之间划分工作,尽管不存在某种严格的规则,但还是有一些提示可供借鉴:(1)如 ...

  6. Linux 硬中断和软中断

    微信公众号:嵌入式Linux中文站 Table of Contents 概述 (1) 硬中断 (2) 软中断 (3) 中断嵌套 (4) 软中断指令 (5)硬中断和软中断的区别 开关 (1) 硬中断的开 ...

  7. Linux内核中断系统结构——软中断

    在 Linux异常(中断)处理体系结构 这篇文章,我们详细描写了内核如何进行中断(异常)向量表的初始化.如何初始化硬件中断(IRQ)的操作. 在这篇文章中,我们将重心放在软件中断上.也就是 CPU 本 ...

  8. linux设备驱动归纳总结(六):3.中断下半部之tasklet

    CU首页 ┊ fh265>>博客 微博 相册 个人中心 好友 消息 [退出] ┊ 随便看看 公告:缅怀Dennis Ritchie活动开赛啦! 小白的博客--提升自已,分享别人 xiaob ...

  9. Linux 中断原理之软中断

    linux软中断实现原理 原创文章,转载请标明出处. 什么是软中断 软中断,顾名思义软件触发的中断.但这个解释又很容易被误解为"通过软件指令触发的(硬)中断".其实这里说的软中断只 ...

最新文章

  1. 《2020城市大脑全球标准研究报告》全文正式开放申领
  2. 编写nios-shell时想到的问题-回车vs换行
  3. 深度解密Go语言之基于信号的抢占式调度
  4. 1909升级卡64_苹果最新系统 macOS Catalina 10.15正式版更新,有哪些升级注意事项
  5. SCDPM 2012R2之保护SQL SERVER
  6. 产品工作中/阅读中的涓滴意念
  7. 配置gem5-gpu docker版
  8. 7-1 宿舍谁最高? (20 分)
  9. 做一个消息自动回复,但是回复内容可以在网页上面输入,用input接收,错了,别人有新增选项,本身就是在页面进行新增,页面维护...
  10. 安卓开发(简单打开前置摄像头并显示)
  11. VVC系列(三)xCompressCTU、xCompressCU和xCheckModeSplit解析
  12. [数据结构]——浅谈红黑树原理与简易实现
  13. 史上最全! 全球 22 种开源商业收入模式
  14. nyoj 239 月老的难题 【二分匹配之匈牙利】
  15. 双稳态电路的两个稳定状态是什么_晶振电路中选择电容的方式有哪些?
  16. 计算机网络之应用层Tips
  17. Quality-Estimation2 (翻译质量评价-在BERT模型后面加上Bi-LSTM进行fine-tuning)
  18. 若今生长剑浣花,生死无涯
  19. 博弈论——2.4古诺模型练习题
  20. 什么是节流和防抖?有什么区别?为什么做这个处理?

热门文章

  1. 221、无线电梯监控如何安装
  2. 微软模拟飞行2020服务器连不上打不开,X-Plane 10 Flight Simulator无法连接服务器如何解决...
  3. Oracle 11G OCP 1Z0-053 475
  4. 使用 getprop 命令查看 android 系统允许进程分配的内存大小
  5. H265打包成RTP
  6. 装修 —— 刮腻子和刷乳胶漆的区别
  7. 前端开发——VSCode
  8. 【学术相关】博士生补助差距有多大?从每月76元到年收入10万
  9. arctime必须要java_下载Arctime字幕软件 | Arctime字幕软件
  10. 用 MSN 照片转卡通 服务 制作了自己的签名照片