<kernel v5.4>
IRQ 디스크립터
IRQ 디스크립터를 관리하는 두 가지 방법
CONFIG_SPARSE_IRQ 커널 옵션을 사용유무에 따라 IRQ 번호를 관리하는 방법이 두 가지로 나뉜다.
- Sparse IRQ
- 커널 옵션을 사용하는 경우 필요한 IRQ 번호에 대한 irq_desc 구조체를 동적으로 할당하고 Radix Tree를 사용하여 관리한다.
- arm64 시스템은 기본적으로 CONFIG_SPARSE_IRQ 커널 옵션을 구성하여 사용한다.
- 관련 함수
- irq_alloc_desc*()
- irq_free_desc*()
- Flat IRQ
- 커널 옵션을 사용하지 않는 경우 max IRQ 번호만큼 irq_dest 구조체 배열을 컴파일 시 정적으로 할당하여 사용한다.
NR_IRQS vs nr_irqs
NR_IRQS는 컴파일 타임에 지정되는 irq 수이고, nr_irqs는 런타임에 결정되어 사용되는 irq 수이다.
- NR_CPUS vs nr_cpus와 동일하게 사용된다.
early irq 초기화
1) Sparse IRQ
early_irq_init() – Sparse
kernel/irq/irqdesc.c
#ifdef CONFIG_SPARSE_IRQ int __init early_irq_init(void) { int i, initcnt, node = first_online_node; struct irq_desc *desc; init_irq_default_affinity(); /* Let arch update nr_irqs and return the nr of preallocated irqs */ initcnt = arch_probe_nr_irqs(); printk(KERN_INFO "NR_IRQS: %d, nr_irqs: %d, preallocated irqs: %d\n", NR_IRQS, nr_irqs, initcnt); if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS)) nr_irqs = IRQ_BITMAP_BITS; if (WARN_ON(initcnt > IRQ_BITMAP_BITS)) initcnt = IRQ_BITMAP_BITS; if (initcnt > nr_irqs) nr_irqs = initcnt; for (i = 0; i < initcnt; i++) { desc = alloc_desc(i, node, 0, NULL, NULL); set_bit(i, allocated_irqs); irq_insert_desc(i, desc); } return arch_early_irq_init(); } #endif
irq 디스크립터를 sparse하게 관리할 수 있는 radix 트리를 구성하고 초기화한다.
- 코드 라인 7에서 모든 cpu를 대상으로 사용하도록 전역 irq_default_affinity 변수에 cpu 비트맵 매스크를 할당하고 모두 1로 설정한다.
- 코드 라인 10에서 사용할 최대 irq 갯수를 알아온다.
- 코드 라인 11~12에서 컴파일 때 지정한 최대 NR_IRQS 값과 재조정된 nr_irqs 값, 그리고 아키텍처에 따라 미리 할당해야 할 irq 수 등을 알아와 로그 정보로 출력한다.
- 예) “NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0”
- 코드 라인 14~15에서 전역 nr_irqs가 IRQ_BITMAP_BITS를 초과하지 않도록 제한한다.
- IRQ_BITMAP_BITS
- sparse 옵션에서 NR_IRQS+8192개
- flat 옵션에서 NR_IRQS
- IRQ_BITMAP_BITS
- 코드 라인 17~18에서 initcnt가 IRQ_BITMAP_BITS를 초과하지 않도록 제한한다.
- 코드 라인 20~21에서 nr_irqs를 아키텍처에 따라 미리 할당해야 할 irq 수로 제한한다.
- 코드 라인 23~27에서 아키텍처에 따라 미리 할당해야 할 수(initcnt) 수 만큼 순회하며 irq 디스크립터를 할당한다. 전역 allocated_irqs 비트마스크의 irq 번호(0번부터 시작)에 해당하는 비트를 설정하고, 전역 irq_desc_tree에 추가한다.
- 코드 라인 28에서 아키텍처별로 early irq 초기화 루틴을 수행한다.
- 현재 x86, ia64 아키텍처에서 제공된다.
다음 그림은 sparse하게 irq 디스크립터가 관리되는 모습을 보여준다.
2) Flat IRQ
early_irq_init() – Flat
kernel/irq/irqdesc.c
#else /* !CONFIG_SPARSE_IRQ */ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { [0 ... NR_IRQS-1] = { .handle_irq = handle_bad_irq, .depth = 1, .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock), } }; int __init early_irq_init(void) { int count, i, node = first_online_node; struct irq_desc *desc; init_irq_default_affinity(); printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS); desc = irq_desc; count = ARRAY_SIZE(irq_desc); for (i = 0; i < count; i++) { desc[i].kstat_irqs = alloc_percpu(unsigned int); alloc_masks(&desc[i], node); raw_spin_lock_init(&desc[i].lock); lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); mutex_init(&desc[i].request_mutex); desc_set_defaults(i, &desc[i], node, NULL); } return arch_early_irq_init(); } #endif
irq 디스크립터를 flat하게 관리하도록 전역 irq_desc[] 배열을 초기화한다.
- 코드 라인 15에서 모든 cpu를 대상으로 사용하도록 전역 irq_default_affinity 변수에 cpu 비트맵 매스크를 할당하고 모두 1로 설정한다.
- 코드 라인 17에서 컴파일 때 지정한 최대 NR_IRQS 값을 로그 정보로 출력한다.
- 예) “NR_IRQS: 64”
- 코드 라인 19~29에서 NR_IRQS 수만큼 순회하며 irq 디스크립터를 초기화한다.
- 코드 라인 30에서 아키텍처별로 early irq 초기화 루틴을 수행한다.
- 현재 x86, ia64 아키텍처에서 제공된다.
다음 그림은 flat하게 irq 디스크립터가 관리되는 모습을 보여준다.
irq affinity
“irqaffinity=” 커널 파라미터 설정
irq_affinity_setup()
kernel/irq/irqdesc.c
static int __init irq_affinity_setup(char *str) { alloc_bootmem_cpumask_var(&irq_default_affinity); cpulist_parse(str, irq_default_affinity); /* * Set at least the boot cpu. We don't want to end up with * bugreports caused by random comandline masks */ cpumask_set_cpu(smp_processor_id(), irq_default_affinity); return 1; } __setup("irqaffinity=", irq_affinity_setup);
“irqaffinity=<cpu ranges>” 커널 파라미터를 설정하여 irq를 처리할 수 있는 cpu를 지정하며, 이외에 기본적으로 boot cpu도 포함시킨다.
- 예) “irqaffinity=0-1,6-7”
디폴트 irq affinity 초기화
init_irq_default_affinity()
kernel/irq/irqdesc.c
static void __init init_irq_default_affinity(void) { if (!cpumask_available(irq_default_affinity)) zalloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); if (cpumask_empty(irq_default_affinity)) cpumask_setall(irq_default_affinity); }
“irqaffinity=” 커널 파라미터가 설정되지 않은 경우 디폴트로 모든 cpu에서 동작하도록 제한하지 않는다.
- 코드 라인 3~4에서 할당 방식의 cpu 비트맵 마스크인 경우 비트맵 마스크의 메모리를 할당한다.
- 코드 라인 5~6에서 디폴트 affinity가 설정되지 않은 경우에 한하여 모든 cpu에서 동작하도록 모두 1로 설정한다.
이 루틴은 해당 시스템의 인터럽트 컨트롤러를 초기화하고 각각의 인터럽트 번호에 따른 핸들러들을 준비하는 과정이다. 인터럽트 컨트롤러는 시스템마다 다른 각각의 하드웨어를 사용하므로 분석을하고자 하는 시스템의 하드웨어에 대한 지식이 필요한다. 따라서 각 시스템에서 사용하는 인터럽트 컨틀롤러의 데이터 시트를 참고하기 바란다.
리눅스 커널에서 구현 방법은 크게 다음과 같이 두 가지로 나뉜다.
- 아키텍처 전용 머신 코드를 수행하여 인터럽트 컨트롤러를 초기화하고 핸들러를 구성하는 방법
- arch/arm/mach-로 시작하는 디렉토리
- 대부분의 임베디드 시스템에서 처음 사용된 방식으로 곧장 각 머신의 인터럽트 컨트롤러 설정 등이 구현되어 있다.
-
- rpi 및 rp2 예)
- 내부 인터럽트 초기화 함수는 Device Tree를 사용한다.
- 커널 v4.4 부터는 지원되지 않는다.
- rpi 및 rp2 예)
-
- Device Tree에 설정된 내용을 분석하여 해당 드라이버를 구동하고 인터럽트 컨트롤러 하드웨어를 초기화하고 핸들러를 구성하는 방법
- kernel/chip.c (헬퍼) -> drivers/irqchip 디렉토리의 각 인터럽트 컨트롤러 드라이버
- irq_domain 등을 사용하여 리눅스 irq와 hw irq의 매핑을 지원하는 구조로 복잡해졌지만 점차 시스템들이 이 방향으로 구성하고 있다.
- arm64도 Device Tree 구성을 읽어 인터럽트 초기화 함수들을 호출한다.
- rpi 및 rpi2 예)
- 커널 v4.4 부터 지원하기 시작하였다.
- kernel/chip.c (헬퍼) -> drivers/irqchip 디렉토리의 각 인터럽트 컨트롤러 드라이버
VMAP Stack 지원
64비트 시스템의 경우 vmalloc 공간이 충분하기 때문에 cpu마다 stack 공간으로 vmalloc 공간을 사용할 수 있도록 지원한다.
- 현재 x86, s390, arm64 아키텍처에 적용되어 있다.
- 참고: arm64: add basic VMAP_STACK support (2017, v4.14-rc1)
IRQ 초기화
init_IRQ() – ARM64
arch/arm64/kernel/irq.c
void __init init_IRQ(void) { init_irq_stacks(); irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); if (system_uses_irq_prio_masking()) { /* * Now that we have a stack for our IRQ handler, set * the PMR/PSR pair to a consistent state. */ WARN_ON(read_sysreg(daif) & PSR_A_BIT); local_daif_restore(DAIF_PROCCTX_NOIRQ); } }
인터럽트 처리 핸들러를 위해 인터럽트 컨트롤러의 준비 및 필요한 설정 수행한다.
- 코드 라인 3에서 per-cpu irq용 스택을 할당하여 준비한다.
- 코드 라인 4~6에서 인터럽트 컨트롤러를 초기화하기 위한 irqchip 초기화를 수행한다.
- 참고: Interrupts -2- (irq chip) | 문c
- 코드 라인 8~15에서 시스템이 irq priority 마스킹을 사용할 수 있는 경우 irq를 블럭한다. 단 Pesudo-NMI는 허용한다.
init_irq_stacks()
arch/arm64/kernel/irq.c
#ifdef CONFIG_VMAP_STACK static void init_irq_stacks(void) { int cpu; unsigned long *p; for_each_possible_cpu(cpu) { p = arch_alloc_vmap_stack(IRQ_STACK_SIZE, cpu_to_node(cpu)); per_cpu(irq_stack_ptr, cpu) = p; } } #else /* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */ DEFINE_PER_CPU_ALIGNED(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack); static void init_irq_stacks(void) { int cpu; for_each_possible_cpu(cpu) per_cpu(irq_stack_ptr, cpu) = per_cpu(irq_stack, cpu); } #endif
per-cpu irq 스택을 생성한다.
- 디폴트로 사용되는 CONFIG_VMAP_STACK 커널 옵션은 스택을 vmalloc 공간에 매핑하여 사용할 수 있게 한다.
init_IRQ() – ARM32
arch/arm/kernel/irq.c
void __init init_IRQ(void) { int ret; if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq) irqchip_init(); else machine_desc->init_irq(); if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) && (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) { if (!outer_cache.write_sec) outer_cache.write_sec = machine_desc->l2c_write_sec; ret = l2x0_of_init(machine_desc->l2c_aux_val, machine_desc->l2c_aux_mask); if (ret) pr_err("L2C: failed to init: %d\n", ret); } uniphier_cache_init(); }
인터럽트 처리 핸들러를 위해 인터럽트 컨트롤러의 준비 및 필요한 설정 수행한다. 시스템 구성에 따라 머신 디스크립터 또는 DTB 둘 중 하나의 방법을 사용하여 초기화 한다.
- 코드 라인 5~8에서 시스템이 Device Tree를 지원하면서 머신 디스크립터에서 init_irq 후크가 설정되어 있지 않으면 Device Tree를 위해 irqchip_init() 함수를 호출하고, 그렇지 않은 경우 머신 디스크립터에 준비한 init_irq 후크에 설정된 콜백 함수를 호출한다.
- rpi2 예)
- 머신의 init_irq에 등록한 bcm2709_init_irq() 함수를 호출한다.
- 참고: A new generic IRQ layer | LWN.net
- rpi2 예)
- 코드 라인 10~18에서 outer 캐시를 가진 시스템에 대해 초기화를 수행한다.
- DTB를 사용할 수 있는 커널에서 L2X0 캐시 컨트롤러를 사용하는 시스템이고 머신에 l2c_aux_mask 또는 l2_aux_val이 설정된 경우 outer 캐시에 대한 콜백함수를 준비하고 초기화 함수를 호출한다.
- 코드 라인 20에서 UniPhier outer 캐시 컨트롤러를 사용하는 시스템을 초기화한다.
arch/arm64/kernel/irq.c – arm64 참고
void __init init_IRQ(void) { irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); }
arm64에서는 DTB 만을 지원하므로 irqchip_init() 함수를 곧바로 호출한다.
인터럽트 컨트롤러 초기화
시스템에 따라 다양한 종류의 인터럽트 컨트롤러를 사용하는데 rpi2에서 사용하는 bcm2709용 인터럽트 컨트롤러 초기화 함수를 호출하기로 한다.
- Device Tree 미 사용시 (CONFIG_OF=n)
- rpi
- arch/arm/mach-bcm2708/bcm2708.c – bcm2708_init_irq()
- 내부 초기화 함수에서는 device tree를 사용한다.
- arch/arm/mach-bcm2708/bcm2708.c – bcm2708_init_irq()
- rp2
- arch/arm/mach-bcm2709/bcm2709.c – bcm2709_init_irq()
- 내부 초기화 함수에서는 device tree를 사용한다.
- arch/arm/mach-bcm2709/bcm2709.c – bcm2709_init_irq()
- rpi
- Device Tree 사용 시
- of_irq_init() 함수를 통해 Device Tree에 있는 인터럽트 컨트롤러 정보를 읽어오고 해당 드라이버의 초기화 함수들을 호출한다.
- GIC
- drivers/irqchip/irq-gic.c – gic_of_init()
- Exynos Combiner
- drivers/irqchip/exynos-combiner.c – combiner_of_init()
- rpi
- drivers/irqchip/irq-bcm2835.c – armctrl_of_init()
- rpi2
- 두 개의 드라이버를 사용한다. (커널 v4.4 부터 적용)
- drivers/irqchip/irq-bcm2835.c – bcm2836_armctrl_of_init()
- drivers/irqchip/irq-bcm2836.c – bcm2836_arm_irqchip_l1_intc_of_init()
- 두 개의 드라이버를 사용한다. (커널 v4.4 부터 적용)
IRQ 디스크립터 할당
irq_alloc_descs()
include/linux/irq.h
#define irq_alloc_descs(irq, from, cnt, node) \ __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL)
요청 @irq 번호부터 @cnt 수 만큼 irq 디스크립터를 @node에 할당한다. 요청한 @irq 번호가 지정되지 않은 값(-1)이면 @from 번호부터 검색하여 배정한다. 그리고 현재 모듈을 irq 디스크립터의 owner로 설정한다.
irq_alloc_descs_from()
include/linux/irq.h
#define irq_alloc_descs_from(from, cnt, node) \ irq_alloc_descs(-1, from, cnt, node)
새로운 irq 번호로, @from에서 검색하여 @cnt 수 만큼 irq 디스크립터를 @node에 할당한다. 그리고 현재 모듈을 irq 디스크립터의 owner로 설정한다.
__irq_alloc_descs()
kernel/irq/irqdesc.c
/** * irq_alloc_descs - allocate and initialize a range of irq descriptors * @irq: Allocate for specific irq number if irq >= 0 * @from: Start the search from this irq number * @cnt: Number of consecutive irqs to allocate. * @node: Preferred node on which the irq descriptor should be allocated * @owner: Owning module (can be NULL) * @affinity: Optional pointer to an affinity mask array of size @cnt which * hints where the irq descriptors should be allocated and which * default affinities to use * * Returns the first irq number or error code */
int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, struct module *owner, const struct irq_affinity_desc *affinity) { int start, ret; if (!cnt) return -EINVAL; if (irq >= 0) { if (from > irq) return -EINVAL; from = irq; } else { /* * For interrupts which are freely allocated the * architecture can force a lower bound to the @from * argument. x86 uses this to exclude the GSI space. */ from = arch_dynirq_lower_bound(from); } mutex_lock(&sparse_irq_lock); start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS, from, cnt, 0); ret = -EEXIST; if (irq >=0 && start != irq) goto unlock; if (start + cnt > nr_irqs) { ret = irq_expand_nr_irqs(start + cnt); if (ret) goto unlock; } ret = alloc_descs(start, cnt, node, affinity, owner); unlock: mutex_unlock(&sparse_irq_lock); return ret; } EXPORT_SYMBOL_GPL(__irq_alloc_descs);
요청 @irq 번호부터 @cnt 수 만큼 irq 디스크립터를 @node에 할당한다. 요청한 @irq 번호가 지정되지 않은 값(-1)이면 @from 번호부터 검색하여 배정한다. 그리고 현재 모듈을 irq 디스크립터의 owner로 설정한다.
- 코드 라인 7~8에서 @cnt가 지정되지 않은 경우 -EINVAL 에러로 반환한다.
- 코드 라인 10~13에서 @irq 번호(0번 이상)를 지정한 경우 검색 시작할 번호 @from에 @irq 번호를 대입한다. 단 @from이 고정 irq 번호보다 큰 경우 -EINVAL 에러로 반환한다.
- 코드 라인 14~21에서 @irq 번호(0번 이상)를 지정하지 않은 경우 새 irq 번호를 발부받기 위해 @from 부터 시작하도록 한다. 단 아키텍처에 따라 from 값이 바뀔 수 있다.
- x86 아키텍처에서는 GSI 공간을 피해야 한다.
- 코드 라인 25~26에서 인터럽트 할당 비트맵에서 @from 비트부터 연속된 @cnt 수 만큼의 할당되지 않은 비트를 찾는다.
- 참고: Bitmap Operations | 문c
- 예) 0b0000_0000_1111_0000_1111_0000
- from=5, cnt=2 -> start=8
- from=5, cnt=5 -> start=16
- 코드 라인 28~29에서 @irq 번호를 지정하였고, 찾은 시작 irq 번호와 다른 경우 -EEXIST 에러를 반환한다.
- 코드 라인 31~35에서 할당할 공간이 부족한 경우 irq 공간을 필요한 수 만큼 확장한다. 확장이 불가능한 경우 -EEXIST 에러를 반환한다.
- 코드 라인 36~39에서start irq 디스크립터 부터 @cnt 수 만큼 @node에 할당한다. 그리고 모듈 owner를 설정하고 start irq 번호를 반환한다.
alloc_descs() – FLAT IRQ 용
kernel/irq/irqdesc.c
static inline int alloc_descs(unsigned int start, unsigned int cnt, int node, const struct irq_affinity_desc *affinity, struct module *owner) { u32 i; for (i = 0; i < cnt; i++) { struct irq_desc *desc = irq_to_desc(start + i); desc->owner = owner; } bitmap_set(allocated_irqs, start, cnt); return start; }
flat irq의 경우 irq 디스크립터가 이미 준비되어 있으므로 별도로 생성할 필요 없고, 사용할 @start irq 번호부터 @cnt 수 만큼 할당 비트들만을 설정한다. 반환하는 값은 @start 값과 동일하다.
alloc_descs() – SPARSE IRQ 용
kernel/irq/irqdesc.c
static int alloc_descs(unsigned int start, unsigned int cnt, int node, const struct irq_affinity_desc *affinity, struct module *owner) { struct irq_desc *desc; int i; /* Validate affinity mask(s) */ if (affinity) { for (i = 0; i < cnt; i++) { if (cpumask_empty(&affinity[i].mask)) return -EINVAL; } } for (i = 0; i < cnt; i++) { const struct cpumask *mask = NULL; unsigned int flags = 0; if (affinity) { if (affinity->is_managed) { flags = IRQD_AFFINITY_MANAGED | IRQD_MANAGED_SHUTDOWN; } mask = &affinity->mask; node = cpu_to_node(cpumask_first(mask)); affinity++; } desc = alloc_desc(start + i, node, flags, mask, owner); if (!desc) goto err; irq_insert_desc(start + i, desc); irq_sysfs_add(start + i, desc); irq_add_debugfs_entry(start + i, desc); } bitmap_set(allocated_irqs, start, cnt); return start; err: for (i--; i >= 0; i--) free_desc(start + i); return -ENOMEM; }
@start irq 디스크립터 부터 @cnt 수 만큼 irq 디스크립터를 @node에 할당한다. 그리고 모듈 owner를 설정한 후 radix tree에 추가한다. 반환 값은 @start irq 번호이다.
- 코드 라인 9~14에서 @affinity가 지정된 경우 @cnt 수만큼 @affinity[i].mask가 모두 설정되어 있는지 확인한다. 하나라도 설정되지 않은 경우 -EINVAL 에러를 반환한다.
- 코드 라인 16에서 에서 인터럽트 수인 @cnt 수만큼 반복한다.
- 코드 라인 20~28에서 @affinity 정보가 있는 경우 인자로 받은 @node 대신 affinity[i].mask에 지정된 첫 cpu에 대한 노드를 사용한다.
- affinity++하여 다음 구조체를 선택한다.
- 코드 라인 30~32에서 @start+i 번호의 irq 디스크립터를 @node에 할당한다. 그리고 모듈 owner를 설정한다.
- 코드 라인 33에서 할당한 irq 디스크립터를 radix tree에 추가한다.
- 코드 라인 34에서 할당한 irq 디스크립터를 sysfs에 추가한다.
- 코드 라인 35에서 할당한 riq 디스크립터를 debugfs에 추가한다.
- 코드 라인 37에서 전역 allocated_irqs 비트맵에 할당된 인터럽트 번호에 해당하는 비트들을 설정한다.
- 코드 라인 38에서 성공 하였으므로 @start irq 번호를 반환한다.
다음 그림은 하나의 irq descriptor가 할당되고 초기화되는 모습을 보여준다.
alloc_masks()
kernel/irq/irqdesc.c
static int alloc_masks(struct irq_desc *desc, int node) { if (!zalloc_cpumask_var_node(&desc->irq_common_data.affinity, GFP_KERNEL, node)) return -ENOMEM; #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK if (!zalloc_cpumask_var_node(&desc->irq_common_data.effective_affinity, GFP_KERNEL, node)) { free_cpumask_var(desc->irq_common_data.affinity); return -ENOMEM; } #endif #ifdef CONFIG_GENERIC_PENDING_IRQ if (!zalloc_cpumask_var_node(&desc->pending_mask, GFP_KERNEL, node)) { #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK free_cpumask_var(desc->irq_common_data.effective_affinity); #endif free_cpumask_var(desc->irq_common_data.affinity); return -ENOMEM; } #endif return 0; }
irq 디스크립터에서 사용하는 여러 cpu 비트맵 마스크를 할당받는다.
desc_set_defaults()
kernel/irq/irqdesc.c
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node, const struct cpumask *affinity, struct module *owner) { int cpu; desc->irq_common_data.handler_data = NULL; desc->irq_common_data.msi_desc = NULL; desc->irq_data.common = &desc->irq_common_data; desc->irq_data.irq = irq; desc->irq_data.chip = &no_irq_chip; desc->irq_data.chip_data = NULL; irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS); irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED); irqd_set(&desc->irq_data, IRQD_IRQ_MASKED); desc->handle_irq = handle_bad_irq; desc->depth = 1; desc->irq_count = 0; desc->irqs_unhandled = 0; desc->tot_count = 0; desc->name = NULL; desc->owner = owner; for_each_possible_cpu(cpu) *per_cpu_ptr(desc->kstat_irqs, cpu) = 0; desc_smp_init(desc, node, affinity); }
요청한 @irq 번호에 해당하는 irq 디스크립터 @desc를 디폴트 값으로 초기화하고, @affinity와 @owner를 지정한다.
- 코드 라인 6에서 인터럽트 핸들러 데이터를 null로 초기화한다.
- 코드 라인 7에서 msi 디스크립터가 없는 상태로 초기화한다.
- 코드 라인 9에서 irq 디스크립터의 하이라키를 표현하는 irq_data의 가장 상위에서 irq 디스크립터에 있는 irq 공통 데이터 구조체를 가리킨다.
- 코드 라인 10에서 irq 디스크립터에 인자로 받은 @irq 번호를 지정한다.
- 코드 라인 11~12에서 irq를 처리할 irqchip과 chip_data는 지정되지 않은 상태로 초기화한다.
- 지정되지 않은 경우 no_irq_chip() 함수를 가리킨다.
- 코드 라인 13에서 irq 디스크립터의 플래그를 모드 제거하고, 디폴트 초기화 플래그를 설정한다.
- arm32: IRQ_NOPROBE | IRQ_NOREQUEST
- arm64: 없음
- 코드 라인 14~15에서 irq 디스크립터에 disabled, masked 플래그들을 설정한다.
- 코드 라인 16에서 인터럽트 핸들러 함수를 지정하지 않은 상태로 초기화한다.
- 지정되지 않은 경우 handle_bad_irq() 함수를 가리킨다.
- 코드 라인 17~24에서 irq 디스크립터의 각종 값들을 초기화한다.
- 코드 라인 25에서 irq 디스크립터에 @affinity를 적용한다. 지정되지 않은 경우 전역 irq_default_affinity 값을 적용한다.
irq_settings_clr_and_set()
kernel/irq/settings.h
static inline void irq_settings_clr_and_set(struct irq_desc *desc, u32 clr, u32 set) { desc->status_use_accessors &= ~(clr & _IRQF_MODIFY_MASK); desc->status_use_accessors |= (set & _IRQF_MODIFY_MASK); }
irq 디스크립터의 status_use_accessors에서 인수 @clr로 요청한 비트들을 clear하고 인수 @set으로 요청한 비트들을 설정한다.
irqd_set()
kernel/irq/internals.h
static inline void irqd_set(struct irq_data *d, unsigned int mask) { d->state_use_accessors |= mask; }
irq 디스크립터의 status_use_accessors에서 인수 @mask로 요청한 비트들을 설정한다.
desc_smp_init()
kernel/irq/irqdesc.c
static void desc_smp_init(struct irq_desc *desc, int node, const struct cpumask *affinity) { if (!affinity) affinity = irq_default_affinity; cpumask_copy(desc->irq_common_data.affinity, affinity); #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_clear(desc->pending_mask); #endif #ifdef CONFIG_NUMA desc->irq_common_data.node = node; #endif }
요청한 irq 디스크립터 @desc에서 사용 가능한 cpu들을 지정하기 위해 @affinity 비트마스크를 지정한다. @affinity가 null인 경우 디폴트 affinity가 사용된다.
irq_insert_desc()
kernel/irq/irqdesc.c
static void irq_insert_desc(unsigned int irq, struct irq_desc *desc) { radix_tree_insert(&irq_desc_tree, irq, desc); }
전역 irq_desc_tree에 요청 @irq 번호를 키로 irq 디스크립터 @desc를 추가한다.
Local IRQ 제어
- local
- 현재 cpu
- raw
- 커널 옵션에 따른 논리적인 구현의 차이
- arch
- 아키텍처 의존적인 구현의 차이
Local IRQ Disable
local_irq_disable()
include/linux/irqflags.h
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
현재 CPU의 인터럽트를 disable
raw_local_irq_disable()
include/linux/irqflags.h
#define raw_local_irq_disable() arch_local_irq_disable()
arch_local_irq_disable() – ARM32
arch/arm/include/asm/irqflags.h
static inline void arch_local_irq_disable(void) { asm volatile( " cpsid i @ arch_local_irq_disable" : : : "memory", "cc"); }
arch_local_irq_disable() – ARM64
arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_disable(void) { if (system_has_prio_mask_debugging()) { u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1); WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF); } asm volatile(ALTERNATIVE( "msr daifset, #2 // arch_local_irq_disable", __msr_s(SYS_ICC_PMR_EL1, "%0"), ARM64_HAS_IRQ_PRIO_MASKING) : : "r" ((unsigned long) GIC_PRIO_IRQOFF) : "memory"); }
- Pesudo-NMI를 지원하는 경우 Priority Mask 레지스터를 사용하여 디폴트 irq만 disable하고, nmi에 사용하는 irq는 통과시키는 기법을 사용한다.
- Pesudo-NMI를 지원하지 않는 경우 PSTATE.I 플래그를 설정하여 irq를 disable 한다.
Local IRQ Enable
local_irq_enable()
include/linux/irqflags.h”
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)
현재 CPU의 인터럽트를 enable
raw_local_irq_enaable()
include/linux/irqflags.h
#define raw_local_irq_enable() arch_local_irq_enable()
arch_local_irq_enable() – ARM32
arch/arm/include/asm/irqflags.h
static inline void arch_local_irq_enable(void) { asm volatile( " cpsie i @ arch_local_irq_enable" : : : "memory", "cc"); }
arch_local_irq_enable() – ARM64
arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_enable(void) { if (system_has_prio_mask_debugging()) { u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1); WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF); } asm volatile(ALTERNATIVE( "msr daifclr, #2 // arch_local_irq_enable\n" "nop", __msr_s(SYS_ICC_PMR_EL1, "%0") "dsb sy", ARM64_HAS_IRQ_PRIO_MASKING) : : "r" ((unsigned long) GIC_PRIO_IRQON) : "memory"); }
- Pesudo-NMI를 지원하는 경우 Priority Mask 레지스터를 사용하여 디폴트 irq와 nmi에 사용하는 irq 모두 통과시킨다.
- Pesudo-NMI를 지원하지 않는 경우 PSTATE.I 플래그를 클리어하여 irq를 enable 한다.
Local IRQ 백업
local_irq_save()
include/linux/irqflags.h
#define local_irq_save(flags) \ do { \ raw_local_irq_save(flags); \ } while (0)
현재 CPU의 irq 상태를 백업하고 disable 한다.
raw_local_irq_save()
include/linux/irqflags.h
#define raw_local_irq_save(flags) \ do { \ typecheck(unsigned long, flags); \ flags = arch_local_irq_save(); \ } while (0)
현재 CPU의 cpsr값을 변수에 저장한다.
arch_local_irq_save() – ARM32
arch/arm/include/asm/irqflags.h
static inline unsigned long arch_local_irq_save(void) { unsigned long flags; asm volatile( " mrs %0, " IRQMASK_REG_NAME_R " @ arch_local_irq_save\n" " cpsid i" : "=r" (flags) : : "memory", "cc"); return flags; }
arch_local_irq_save() – ARM64
arch/arm64/include/asm/irqflags.h
static inline unsigned long arch_local_irq_save(void) { unsigned long flags; flags = arch_local_save_flags(); /* * There are too many states with IRQs disabled, just keep the current * state if interrupts are already disabled/masked. */ if (!arch_irqs_disabled_flags(flags)) arch_local_irq_disable(); return flags; }
arch_local_irq_save_flags() – ARM64
arch/arm64/include/asm/irqflags.h
static inline unsigned long arch_local_save_flags(void) { unsigned long flags; asm volatile(ALTERNATIVE( "mrs %0, daif", __mrs_s("%0", SYS_ICC_PMR_EL1), ARM64_HAS_IRQ_PRIO_MASKING) : "=&r" (flags) : : "memory"); return flags; }
Local IRQ 복구
local_irq_restore()
include/linux/irqflags.h
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)
현재 cpu에서 백업해둔 irq 상태를 복구한다.
raw_local_irq_restore()
#define raw_local_irq_restore(flags) \ do { \ typecheck(unsigned long, flags); \ arch_local_irq_restore(flags); \ } while (0)
변수에 저장된 cpsr값을 읽어 현재 CPU의 cpsr_c 부분을 변경(현재 CPU의 인터럽트 상태를 저장되었던 상태로 되돌림)
arch_local_irq_restore() – ARM32
arch/arm/include/asm/irqflags.h
static inline void arch_local_irq_restore(unsigned long flags) { asm volatile( " msr " IRQMASK_REG_NAME_W ", %0 : : "r" (flags) : "memory", "cc"); } #define IRQMASK_REG_NAME_W "cpsr_c"
arch_local_irq_restore() – ARM64
arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_restore(unsigned long flags) { asm volatile(ALTERNATIVE( "msr daif, %0\n" "nop", __msr_s(SYS_ICC_PMR_EL1, "%0") "dsb sy", ARM64_HAS_IRQ_PRIO_MASKING) : : "r" (flags) : "memory"); }
APIs
irq 디스크립터 할당 & 해제
- irq_alloc_descs()
- irq_alloc_descs_from()
- __irq_alloc_descs()
- alloc_descs()
- irq_free_descs()
- irq_alloc_hwirqs()
- irq_free_hwirqs()
irq 핸들러
- generic_handle_irq()
- generic_handle_irq_desc()
- __handle_domain_irq()
- __irq_set_preflow_handler()
기타
- irq_data_to_desc()
- irq_desc_get_irq()
- irq_desc_get_irq_data()
- irq_desc_get_chip()
- irq_desc_get_chip_data()
- irq_desc_get_handler_data()
- irq_desc_has_action()
- irq_has_action()
- irq_set_handler_locked()
- irq_set_chip_handler_name_locked()
- irq_balancing_disabled()
- irq_is_percpu()
- irq_is_percpu_devid()
- irq_get_percpu_devid_partition()
구조체 및 데이터
IRQ 디스크립터
include/linux/irqdesc.h
/** * struct irq_desc - interrupt descriptor * @irq_common_data: per irq and chip data passed down to chip functions * @kstat_irqs: irq stats per cpu * @handle_irq: highlevel irq-events handler * @preflow_handler: handler called before the flow handler (currently used by sparc) * @action: the irq action chain * @status: status information * @core_internal_state__do_not_mess_with_it: core internal status information * @depth: disable-depth, for nested irq_disable() calls * @wake_depth: enable depth, for multiple irq_set_irq_wake() callers * @tot_count: stats field for non-percpu irqs * @irq_count: stats field to detect stalled irqs * @last_unhandled: aging timer for unhandled count * @irqs_unhandled: stats field for spurious unhandled interrupts * @threads_handled: stats field for deferred spurious detection of threaded handlers * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers * @lock: locking for SMP * @affinity_hint: hint to user space for preferred irq affinity * @affinity_notify: context for notification of affinity changes * @pending_mask: pending rebalanced interrupts * @threads_oneshot: bitfield to handle shared oneshot threads * @threads_active: number of irqaction threads currently running * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers * @nr_actions: number of installed actions on this descriptor * @no_suspend_depth: number of irqactions on a irq descriptor with * IRQF_NO_SUSPEND set * @force_resume_depth: number of irqactions on a irq descriptor with * IRQF_FORCE_RESUME set * @rcu: rcu head for delayed free * @kobj: kobject used to represent this struct in sysfs * @request_mutex: mutex to protect request/free before locking desc->lock * @dir: /proc/irq/ procfs entry * @debugfs_file: dentry for the debugfs file * @name: flow handler name for /proc/interrupts output */
struct irq_desc { struct irq_common_data irq_common_data; struct irq_data irq_data; unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; #ifdef CONFIG_IRQ_PREFLOW_FASTEOI irq_preflow_handler_t preflow_handler; #endif struct irqaction *action; /* IRQ action list */ unsigned int status_use_accessors; unsigned int core_internal_state__do_not_mess_with_it; unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ unsigned int tot_count; unsigned int irq_count; /* For detecting broken IRQs */ unsigned long last_unhandled; /* Aging timer for unhandled count */ unsigned int irqs_unhandled; atomic_t threads_handled; int threads_handled_last; raw_spinlock_t lock; struct cpumask *percpu_enabled; const struct cpumask *percpu_affinity; #ifdef CONFIG_SMP const struct cpumask *affinity_hint; struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_var_t pending_mask; #endif #endif unsigned long threads_oneshot; atomic_t threads_active; wait_queue_head_t wait_for_threads; #ifdef CONFIG_PM_SLEEP unsigned int nr_actions; unsigned int no_suspend_depth; unsigned int cond_suspend_depth; unsigned int force_resume_depth; #endif #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; #endif #ifdef CONFIG_GENERIC_IRQ_DEBUGFS struct dentry *debugfs_file; const char *dev_name; #endif #ifdef CONFIG_SPARSE_IRQ struct rcu_head rcu; struct kobject kobj; #endif struct mutex request_mutex; int parent_irq; struct module *owner; const char *name; } ____cacheline_internodealigned_in_smp;
한 개의 irq 디스크립터이다.
- irq_common_data
- irq 공통 정보
- 아래 irq_data 같이 부모 관계로 연결되는 곳에서도 공통으로 사용되는 정보이다.
- irq_data
- chip 기능에 전달되는 irq와 chip 데이터
- 이 구조체들은 하이라키 도메인 구성된 경우 부모 관계로 연결된다.
- *kstat_irqs
- cpu 별 irq 통계 카운터
- handle_irq
- 하이 레벨 irq 이벤트 핸들러
- preflow_handler
- preflow 핸들러
- *action
- irq action 체인으로 유저 디바이스가 요청한 인터럽트 핸들러가 추가되는 리스트이다.
- status_use_accessors
- irq 상태 정보
- core_internal_state__do_not_mess_with_it
- 코어 내부 상태 정보
- irqd->state으로 표현( state가 매크로)
- depth
- 네스트된 irq_disable() 호출을 위한 disable depth
- wake_depth
- 다중 irq_set_irq_wake() 호출을 위한 enable depth
- irq_count
- stalled irq들을 감지하기 위한 상태 필드
- last_unhandled
- 언핸들드 카운트를 위한 aging 타이머
- irqs_unhandled
- 가짜 언핸들된 인터럽트들을 위한 상태 필드
- threads_handled
- 스레드 핸들러의 연기된 가짜 감지를 위한 상태 필드
- threads_handled_last
- 스레드 핸들러의 연기된 가짜 감지를 위한 비교 필드
- lock
- SMP lock
- *percpu_enabled
- cpu 비트마스크
- *percpu_affinity
- cpu 제한된 경우 cpu affinity를 표현한 비트마스크
- *affinity_hint
- 권장 irq affinity를 위한 user space 힌트
- 참고: genirq: Add CPU mask affinity hint
- *affinity_notify
- affnity가 변경 시 통지를 위한 context
- pending_mask
- 지연된 리밸런스된 인터럽트들
- threads_oneshot
- 공유 oneshot 스레드들을 다룰 비트필드
- 참고: genirq: Add oneshot support
- threads_active
- 현재 동작중인 irqaction 스레드의 수
- wait_for_threads
- 스레드된 핸들러를 위해 기다릴 sync_irq를 위한 대기큐
- nr_actions
- 이 irq 디스크립터에 설치된 액션 수
- no_suspend_depth
- IRQF_NO_SUSPEND 셋을 가진 irq 디스크립터의 irqactions 수
- cond_suspend_depth
- force_resume_depth
- IRQF_FORCE_RESUME 셋을 가진 irq 디스크립터의 irqactions 수
- dir
- /proc/irq/에 표시될 procfs 엔트리
- default로 irq 숫자 (예: /proc/irq/83)
- *debugfs_file
- 디버그용 (/sys/fs/debug)
- *dev_name
- 디버그용
- rcu
- kobj
- request_mutex
- parent_irq
- cascade 연결 시 부모 irq 번호
- *owner
- 모듈을 가리킨다.
- name
- /proc/interrupts에 출력될 flow 핸들러 이름
- 예) uart-pl011
irq_data 구조체
include/linux/irq.h
/** * struct irq_data - per irq chip data passed down to chip functions * @mask: precomputed bitmask for accessing the chip registers * @irq: interrupt number * @hwirq: hardware interrupt number, local to the interrupt domain * @common: point to data shared by all irqchips * @chip: low level interrupt hardware access * @domain: Interrupt translation domain; responsible for mapping * between hwirq number and linux irq number. * @parent_data: pointer to parent struct irq_data to support hierarchy * irq_domain * @chip_data: platform-specific per-chip private data for the chip * methods, to allow shared chip implementations */
struct irq_data { u32 mask; unsigned int irq; unsigned long hwirq; struct irq_common_data *common; struct irq_chip *chip; struct irq_domain *domain; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY struct irq_data *parent_data; #endif void *chip_data; };
이 구조체는 irqchip들에 제공될 정보들이다. irq 디스크립터에 기본 내장되어 있고, 하이라키 도메인을 구성하는 경우 추가 생성되어 부모 관계로 연결된다.
- mask
- chip 레지스터들에 접근하기 위한 미리 계산된 비트마스크
- irq
- 리눅스가 관리하는 인터럽트 번호 (virq)
- hwirq
- 하드웨어 인터럽트 번호
- *common
- irq 디스크립터에 있는 공통 정보 구조체를 가리킨다.
- *chip
- 로우 레벨 인터럽트 컨트롤러 하드웨어 제어를 담당하는 irq_chip 구조체를 가리킨다.
- *domain
- irq 도메인을 가리킨다. (hwirq -> irq 변환)
- *parent_data
- 하이라키 irq 도메인을 지원하기 위한 부모 irq_data를 가리킨다.
- chip_data
- chip 메소드와 공유 chip 구현을 허락하기 위한 플랫폼 specific per-chip private 데이터
irq_common_data 구조체
include/linux/irq.h
/** * struct irq_common_data - per irq data shared by all irqchips * @state_use_accessors: status information for irq chip functions. * Use accessor functions to deal with it * @node: node index useful for balancing * @handler_data: per-IRQ data for the irq_chip methods * @affinity: IRQ affinity on SMP. If this is an IPI * related irq, then this is the mask of the * CPUs to which an IPI can be sent. * @effective_affinity: The effective IRQ affinity on SMP as some irq * chips do not allow multi CPU destinations. * A subset of @affinity. * @msi_desc: MSI descriptor * @ipi_offset: Offset of first IPI target cpu in @affinity. Optional. */
struct irq_common_data { unsigned int __private state_use_accessors; #ifdef CONFIG_NUMA unsigned int node; #endif void *handler_data; struct msi_desc *msi_desc; cpumask_var_t affinity; #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK cpumask_var_t effective_affinity; #endif #ifdef CONFIG_GENERIC_IRQ_IPI unsigned int ipi_offset; #endif };
하이라키 irq 도메인을 구성하는 irq data들이 사용하는 irq 공통 정보이다.
- state_use_accessors
- irq chip 기능들에 제공할 irq 디스크립터의 accessor 정보
- node
- 노드 인덱스
- *handler_data
- irq_chip 메소드를 위한 per-IRQ 데이터
- *msi_desc
- MSI 디스크립터를 가리킨다.
- affinity
- SMP에서의 IRQ affinity
- effective_affinity
- ipi_offset
IRQ Data 상태
include/linux/irq.h
/* * Bit masks for irq_common_data.state_use_accessors * * IRQD_TRIGGER_MASK - Mask for the trigger type bits * IRQD_SETAFFINITY_PENDING - Affinity setting is pending * IRQD_ACTIVATED - Interrupt has already been activated * IRQD_NO_BALANCING - Balancing disabled for this IRQ * IRQD_PER_CPU - Interrupt is per cpu * IRQD_AFFINITY_SET - Interrupt affinity was set * IRQD_LEVEL - Interrupt is level triggered * IRQD_WAKEUP_STATE - Interrupt is configured for wakeup * from suspend * IRQD_MOVE_PCNTXT - Interrupt can be moved in process * context * IRQD_IRQ_DISABLED - Disabled state of the interrupt * IRQD_IRQ_MASKED - Masked state of the interrupt * IRQD_IRQ_INPROGRESS - In progress state of the interrupt * IRQD_WAKEUP_ARMED - Wakeup mode armed * IRQD_FORWARDED_TO_VCPU - The interrupt is forwarded to a VCPU * IRQD_AFFINITY_MANAGED - Affinity is auto-managed by the kernel * IRQD_IRQ_STARTED - Startup state of the interrupt * IRQD_MANAGED_SHUTDOWN - Interrupt was shutdown due to empty affinity * mask. Applies only to affinity managed irqs. * IRQD_SINGLE_TARGET - IRQ allows only a single affinity target * IRQD_DEFAULT_TRIGGER_SET - Expected trigger already been set * IRQD_CAN_RESERVE - Can use reservation mode */
enum { IRQD_TRIGGER_MASK = 0xf, IRQD_SETAFFINITY_PENDING = (1 << 8), IRQD_ACTIVATED = (1 << 9), IRQD_NO_BALANCING = (1 << 10), IRQD_PER_CPU = (1 << 11), IRQD_AFFINITY_SET = (1 << 12), IRQD_LEVEL = (1 << 13), IRQD_WAKEUP_STATE = (1 << 14), IRQD_MOVE_PCNTXT = (1 << 15), IRQD_IRQ_DISABLED = (1 << 16), IRQD_IRQ_MASKED = (1 << 17), IRQD_IRQ_INPROGRESS = (1 << 18), IRQD_WAKEUP_ARMED = (1 << 19), IRQD_FORWARDED_TO_VCPU = (1 << 20), IRQD_AFFINITY_MANAGED = (1 << 21), IRQD_IRQ_STARTED = (1 << 22), IRQD_MANAGED_SHUTDOWN = (1 << 23), IRQD_SINGLE_TARGET = (1 << 24), IRQD_DEFAULT_TRIGGER_SET = (1 << 25), IRQD_CAN_RESERVE = (1 << 26), };
irq_common_data.state_use_accessors에서 사용되는 플래그들이다.
- IRQD_TRIGGER_MASK
- 트리거 타입 마스크 (4비트)
- IRQD_SETAFFINITY_PENDING
- affinity 설정이 지연되는 중
- IRQD_NO_BALANCING
- no 밸런싱
- IRQD_PER_CPU
- 코어별 전용 인터럽트 배정되는 경우 per-cpu 인터럽트로 구성
- IRQD_AFFINITY_SET
- affnity 설정된 경우
- IRQD_LEVEL
- 레벨 트리거 설정된 경우 (그렇지 않으면 edge 트리거)
- IRQD_WAKERUP_STATE
- suspend된 작업을 깨워 동작시키는 인터럽트 형태로 설정
- IRQD_MOVE_PCNTXT
- 인터럽트가 process context로 이동된 경우
- IRQD_IRQ_DISABLED
- 인터럽트를 disable한 상태
- IRQD_IRQ_MASKED
- 인터럽트의 masked 상태
- IRQD_IRQ_INPROGRESS
- 인터럽트가 진행중인 상태
- IRQD_WAKERUP_ARMED
- 꺠우는 모드 armed
- IRQD_FORWARDED_TO_VCPU
- 가상화를 담당하는 vCPU 인터페이스로 전달 가능
- IRQD_IRQ_STARTED
- 인터럽트가 서비스 시작된 상태
- IRQD_MANAGED_SHUTDOWN
- affinity가 지정되지 않아 인터럽트가 shutdown된 상태
- IRQD_SINGLE_TARGET
- 하나의 cpu(affinity target)만 지정 가능한 상태
- IRQD_DEFAULT_TRIGGER_SET
- 트리거가 설정된 상태
- IRQD_CAN_RESERVE
- 예약 모드 사용 가능한 상태
참고
- Interrupts -1- (Interrupt Controller) | 문c
- Interrupts -2- (irq chip) | 문c
- Interrupts -3- (irq domain) | 문c
- Interrupts -4- (Top-Half & Bottom-Half) | 문c
- Interrupts -5- (Softirq) | 문c
- Interrupts -6- (IPI Cross-call) | 문c
- Interrupts -7- (Workqueue 1) | 문c
- Interrupts -8- (Workqueue 2) | 문c
- Interrupts -9- (GIC v3 Driver) | 문c
- Interrupts -10- (irq partition) | 문c
- Interrupts -11- (RPI2 IC Driver) | 문c
- Interrupts -12- (irq desc) | 문c – 현재 글