• request_irqとdevm_request_irqの違い

    A thumbnail image

    IRQは、interrupt requestの略です。Linuxのドライバで使われるrequest_irqdevm_request_irqの違いを自分なりにまとめます。
    カーネルは、5.12.0-rc2を参考にしています。

    request_irq

    /**
     * request_irq - Add a handler for an interrupt line
     * @irq:	 The interrupt line to allocate
     * @handler: Function to be called when the IRQ occurs.
     *		     Primary handler for threaded interrupts
     *		     If NULL, the default primary handler is installed
     * @flags:	 Handling flags
     * @name:	 Name of the device generating this interrupt
     * @dev:	 A cookie passed to the handler function
     *
     * This call allocates an interrupt and establishes a handler; see
     * the documentation for request_threaded_irq() for details.
     */
    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
    • irq:割り込み線(Interrupt line)
    • handler:割り込みハンドラ
    • flags:割り込みのタイプを決定するフラグ
    • name:割り込みを発生させるデバイス名
    • dev:割り込みハンドラに渡されるcookie(どのデバイスからの割り込みかを区別するために使われます。)

    この関数は、割り込み線にデバイスを割り当て、割り込みを有効にします。 include/linux/interrupt.hで定義されています。  

    flagsを指定することで、エッジ割り込みやレベル割り込みなどの割り込みの種類の設定ができます。また、一つの割り込み線に対して複数のデバイスを割り当てられます。 flagsには、以下のものを指定できます。これもinclude/linux/interrupt.hで定義されています。

    /*
     * These correspond to the IORESOURCE_IRQ_* defines in
     * linux/ioport.h to select the interrupt line behaviour.  When
     * requesting an interrupt without specifying a IRQF_TRIGGER, the
     * setting should be assumed to be "as already configured", which
     * may be as per machine or firmware initialisation.
     */
    #define IRQF_TRIGGER_NONE	0x00000000
    #define IRQF_TRIGGER_RISING	0x00000001
    #define IRQF_TRIGGER_FALLING	0x00000002
    #define IRQF_TRIGGER_HIGH	0x00000004
    #define IRQF_TRIGGER_LOW	0x00000008
    #define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
    				 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
    #define IRQF_TRIGGER_PROBE	0x00000010
    
    /*
     * These flags used only by the kernel as part of the
     * irq handling routines.
     *
     * IRQF_SHARED - allow sharing the irq among several devices
     * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
     * IRQF_TIMER - Flag to mark this interrupt as timer interrupt
     * IRQF_PERCPU - Interrupt is per cpu
     * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
     * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
     *                registered first in a shared interrupt is considered for
     *                performance reasons)
     * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
     *                Used by threaded interrupts which need to keep the
     *                irq line disabled until the threaded handler has been run.
     * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend.  Does not guarantee
     *                   that this interrupt will wake the system from a suspended
     *                   state.  See Documentation/power/suspend-and-interrupts.rst
     * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
     * IRQF_NO_THREAD - Interrupt cannot be threaded
     * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
     *                resume time.
     * IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this
     *                interrupt handler after suspending interrupts. For system
     *                wakeup devices users need to implement wakeup detection in
     *                their interrupt handlers.
     */

    request_irqは、request_threaded_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_threaded_irqは、kernel/irq/manage.cで定義されています。

    /**
     *	request_threaded_irq - allocate an interrupt line
     *	@irq: Interrupt line to allocate
     *	@handler: Function to be called when the IRQ occurs.
     *		  Primary handler for threaded interrupts
     *		  If NULL and thread_fn != NULL the default
     *		  primary handler is installed
     *	@thread_fn: Function called from the irq handler thread
     *		    If NULL, no irq thread is created
     *	@irqflags: Interrupt type flags
     *	@devname: An ascii name for the claiming device
     *	@dev_id: A cookie passed back to the handler function
     *
     *	This call allocates interrupt resources and enables the
     *	interrupt line and IRQ handling. From the point this
     *	call is made your handler function may be invoked. Since
     *	your handler function must clear any interrupt the board
     *	raises, you must take care both to initialise your hardware
     *	and to set up the interrupt handler in the right order.
     *
     *	If you want to set up a threaded irq handler for your device
     *	then you need to supply @handler and @thread_fn. @handler is
     *	still called in hard interrupt context and has to check
     *	whether the interrupt originates from the device. If yes it
     *	needs to disable the interrupt on the device and return
     *	IRQ_WAKE_THREAD which will wake up the handler thread and run
     *	@thread_fn. This split handler design is necessary to support
     *	shared interrupts.
     *
     *	Dev_id must be globally unique. Normally the address of the
     *	device data structure is used as the cookie. Since the handler
     *	receives this value it makes sense to use it.
     *
     *	If your interrupt is shared you must pass a non NULL dev_id
     *	as this is required when freeing the interrupt.
     *
     *	Flags:
     *
     *	IRQF_SHARED		Interrupt is shared
     *	IRQF_TRIGGER_*		Specify active edge(s) or level
     *
     */
    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:割り込み線(Interrupt line)
    • handler:割り込みハンドラ(スレッド化された割り込みの優先ハンドラです。handler==NULL, thread_fn!=NULLの場合、デフォルト優先ハンドラが使用されます。)
    • thread_fn:割り込みハンドラスレッド(irq handler thread)から呼ばれる関数(引数がNULLの場合、割り込みハンドラスレッドは作成されません。)
    • irqflags:割り込みのタイプを決定するフラグ
    • devname:割り込み要求するデバイスの名前
    • dev_id:割り込みハンドラに渡されるcookie(どのデバイスからの割り込みかを区別するために使われます。)

    devm_request_irq

    int devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
    • dev:割り込みを要求するためのデバイス
    • irq:割り込み線(Interrupt line)
    • handler:割り込みハンドラ
    • irqflags:割り込みのタイプを決定するフラグ
    • devname:割り込みを発生させるデバイス名
    • dev_id:割り込みハンドラに渡されるcookie(どのデバイスからの割り込みかを区別するために使われます。)

    include/linux/interrupt.hに定義されています。
    devm_request_irqは、devm_request_threaded_irqを次のようにラップしています。

    static inline int __must_check
    devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
    		 unsigned long irqflags, const char *devname, void *dev_id)
    {
    	return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
    					 devname, dev_id);
    }

    devm_request_threaded_irqは、kernel/irq/devres.cに定義されています。

    /**
     *	devm_request_threaded_irq - allocate an interrupt line for a managed device
     *	@dev: device to request interrupt for
     *	@irq: Interrupt line to allocate
     *	@handler: Function to be called when the IRQ occurs
     *	@thread_fn: function to be called in a threaded interrupt context. NULL
     *		    for devices which handle everything in @handler
     *	@irqflags: Interrupt type flags
     *	@devname: An ascii name for the claiming device, dev_name(dev) if NULL
     *	@dev_id: A cookie passed back to the handler function
     *
     *	Except for the extra @dev argument, this function takes the
     *	same arguments and performs the same function as
     *	request_threaded_irq().  IRQs requested with this function will be
     *	automatically freed on driver detach.
     *
     *	If an IRQ allocated with this function needs to be freed
     *	separately, devm_free_irq() must be used.
     */
    int devm_request_threaded_irq(struct device *dev, unsigned int irq,
    			                  irq_handler_t handler, irq_handler_t thread_fn,
    			                  unsigned long irqflags, const char *devname, void *dev_id)

    • dev:割り込みを要求するためのデバイス
    • irq:割り込み線(Interrupt line)
    • handler:割り込みハンドラ
    • irqflags:割り込みのタイプを決定するフラグ
    • devname:割り込みを発生させるデバイス名
    • dev_id:割り込みハンドラに渡されるcookie(どのデバイスからの割り込みかを区別するために使われます。)

    この関数を使って割り込み登録されると、ドライバのデタッチ時に自動的に解放されます。 また、この関数で割り当てられたIRQを個別に開放する必要がある場合は、devm_free_irqを使用する必要があります。 この関数の内部では、request_threaded_irqが呼ばれています。

    request_irqとdevm_request_irqの違い

    同じ機能

    request_threaded_irqを使って、割り込み登録を行っています。

    異なる機能

    devm_request_threaded_irqは、リソース管理を行っています。
    devm_が付く関数はリソース管理が行われます。そういった関数は、Devres: Managed Device Resourceとして定義されています。ドライバの初期化に失敗した場合やデバイスが外された場合でも、リソース管理を行うことで領域の解放を保障できます。これによって、リークの心配などがなくなります。また、低レベルのドライバはdevresを使うことでよりシンプルに実装できます。

    割り込みについての補足

    コンピュータにおいて、割り込み要求(IRQ:Interrupt request)はハードウェア(H/W)の信号をプロセッサに送ります。これによって、実行中のプログラムを一時的に停止し、特別なプログラムの実行を許可し、実行します。この特別なプログラムを割り込みハンドラ(interrupt handler)といいます。H/W割り込みは、モデム、ネットワークカード、キー入力、マウス動作などのイベントを処理するために使用されます。
    割り込み要求可能なH/Wは、割り込み線(Interrupt line or IRQ line)と呼ばれる線を持っています。割り込み線は、通常「IRQ0, IRQ1, …」という形式で識別されます。
    例えば、プログラマブル割り込みコントローラのIntel 8529ファミリは8本の割り込み線を持ち、IRQ0~IRQ7として参照されます。

    参考文献

    comments powered by Disqus