3.4.4 __ipipe_init_early之再论虚拟中断

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

3.4.4 __ipipe_init_early之再论虚拟中断

       根据《3.4.1.2 IPIPE对Linux中断号的改造》的分析,IPIPE引入的虚拟中断virtual interrupt的概念,其中前10个虚拟中断本质上是利用SGI实现的IPI中断。IPIPE在原来Linux原生的7个NR_IPI的基础上,多定义了3个IPIPE_OOB_IPI_NR。虚拟中断virtual interrupt总共是64个呢,其余的怎么用呢?

        在__ipipe_init_early的结尾,初始化了两个virtual interrupt: __ipipe_printk_virq和__ipipe_work_virq。

        它们的virtual interrupt编号是多少?

        上一节分析,virtual interrupt编号前10个已经分配出去了,即1024~1033。__ipipe_printk_virq和__ipipe_work_virq通过ipipe_alloc_virq()得到的virtual interrupt编号是1034和1035.

        它们是如何触发的?

        这两个virtual interrupt,都不是经过SGI触发的IPI中断,而是直接在软件层面触发。接下来对它们进行分析。

        (1)__ipipe_printk_virq

       __ipipe_printk_virq用来干啥?它涉及的代码集中在kernel/printk/printk.c,涉及的commit信息是:printk: ipipe: defer vprintk() output,它最重的修改就是对printk进行hack,让head domain调用printk函数时,实际是把打印内容通过__ipipe_printk_virq送给root domain去打印,避免影响影响head domain的实时性。

       正因为如此,root domain需要设置中断响应程序__ipipe_flush_printk来响应来自head domain的__ipipe_printk_virq中断。反过来思考,head domain是发起__ipipe_printk_virq中断的一方,是不需要设置中断响应程序来处理__ipipe_printk_virq中断的哦。

       那head domain是如何发起__ipipe_printk_virq中断的? 如果是head domain调用printk函数,核心调用关系如下。

printk <kernel/printk/printk.c>				
 ->	do_vprintk <kernel/printk/printk.c>			
	 ->	__ipipe_log_printk  <kernel/printk/printk.c>		
		 ->	ipipe_raise_irq <kernel/ipipe/core.c>	
			 ->	__ipipe_dispatch_irq <kernel/ipipe/core.c>

       最终调用的是函数__ipipe_dispatch_irq,它之前不都是在硬件中断的调用栈中,怎么会出现在这里呢?非常巧妙,IPIPE可以通过调用ipipe_raise_irq函数,来模拟virtual interrupt的触发,并调用函数__ipipe_dispatch_irq来处理此virtual interrupt。在函数__ipipe_dispatch_irq中,会把__ipipe_printk_virq虚拟中断记录到root domain的interrupt log。直到interrupt log被回放,在root domain中调用虚拟中断响应程序__ipipe_flush_printk来完成打印操作。

       (2) __ipipe_work_virq

       上面的虚拟中断__ipipe_printk_virq是一个专用的虚拟中断,而__ipipe_work_virq是一个通用的虚拟中断。可以通过触发虚拟中断__ipipe_work_virq,给root domain传递任意的工作项struct ipipe_work_header,定义如下。

include/linux/ipipe.h:

struct ipipe_work_header {
	size_t size;
	void (*handler)(struct ipipe_work_header *work);
};

       如何发起工作项,它的核心调用堆栈如下:

ipipe_post_work_root <include/linux/ipipe.h>				
 ->	__ipipe_post_work_root <kernel/ipipe/core.c>			
	 ->	ipipe_post_irq_root <kernel/ipipe/core.c>		
		 ->	__ipipe_set_irq_pending(&ipipe_root, irq)  <include/linux/ipipe.h>	

       最后一步,是调用__ipipe_set_irq_pending(&ipipe_root, irq)把__ipipe_work_virq虚拟中断记录到root domain的interrupt log,这不是取代了__ipipe_dispatch_irq的工作嘛,非常简单粗暴了。直到interrupt log被回放,在root domain中调用虚拟中断响应程序__ipipe_do_work来完成工作项。

       在IPIPE Patch中,并没有运用__ipipe_work_virq的实例。后来在Xenomai patch中找到了一处实例。

       至此,对两个虚拟中断的分析结束了。

       结合这两个实例,再反过来阅读Documentation/ipipe.rst中关于虚拟中断的章节,感觉理解的更加透彻了!特别是,不仅可以利用ipipe_post_irq_root把任务发到in-band即root domain中,还可以利用ipipe_post_irq_head把任务发到out-of-band即head domain中,非常巧妙!

Virtual/Synthetic interrupt vectors
-----------------------------------

.. _synthetic:
.. _virtual:
The pipeline introduces an additional type of interrupts, which are
purely software-originated, with no hardware involvement. These IRQs
can be triggered by any kernel code. So-called virtual IRQs are
inherently per-CPU events.

Because the common pipeline flow_ applies to virtual interrupts, it
is possible to attach them to out-of-band and/or in-band handlers,
just like device interrupts.

.. NOTE:: virtual interrupts and regular softirqs differ in essence:
          the latter only exist in the in-band context, and therefore
          cannot trigger out-of-band activities.

Virtual interrupt vectors are allocated by a call to
:c:func:`ipipe_alloc_virq`, and conversely released with
:c:func:`ipipe_free_virq`.

For instance, a virtual interrupt can be used for triggering an
in-band activity on the root stage from the head stage as follows::

  #include <linux/ipipe.h>

  static void virq_handler(unsigned int virq, void *cookie)
  {
        do_in_band_work();
  }

  void install_virq(void)
  {
     unsigned int virq;
     ...
     virq = ipipe_alloc_virq();
     ...
     ipipe_request_irq(ipipe_root_domain, virq, virq_handler,
		       handler_arg, NULL);
  }

An out-of-band handler can schedule the execution of
:c:func:`virq_handler` like this::

  ipipe_post_irq_root(virq);

Conversely, a virtual interrupt can be handled from the out-of-band
context::

  static void virq_oob_handler(unsigned int virq, void *cookie)
  {
        do_oob_work();
  }

  void install_virq(void)
  {
     unsigned int virq;
     ...
     virq = ipipe_alloc_virq();
     ...
     ipipe_request_irq(ipipe_head_domain, virq, virq_oob_handler,
		       handler_arg, NULL);
  }

Any in-band code can trigger the immediate execution of
:c:func:`virq_oob_handler` on the head stage as follows::

  ipipe_post_irq_head(virq);

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值