<kernel v5.4>
Interrupts -10- (irq partition)
PPI는 per-cpu 인터럽트들은 irq번호가 동일하다. 그런데 클러스터마다 인터럽트 트리거 타입 설정 및 인터럽트 처리 방법이 다른 경우가 발생하여 이를 처리하기 위해 클러스터 단위로 파티션을 나누어 처리하였다.
- 파티션으로 나눈 인터럽트는 chaind irq 처리 방법을 사용한다.
- GIC v3 드라이버에서 적용하였다.
- 참고: irqchip: Add per-cpu interrupt partitioning library (2016, v4.7-rc1)
per-cpu 인터럽트(PPI)
다음 그림은 PPI에 해당하는 per-cpu 인터럽트와 SPI에 해당하는 일반 인터럽트가 처리될 때의 흐름을 보여준다.
- 1개의 per-cpu 인터럽트는 cpu 수 만큼 라인을 가지며 각 cpu에 라우팅되고, 하나의 irq 디스크립터에서 처리된다.
다음 그림은 GIC v3 드라이버가 PPI 및 SPI에서 수신된 인터럽트를 처리할 때 사용하는 도메인과 irq_chip의 operations 함수들을 보여준다.
IRQ Partition
다음 그림은 per-cpu 인터럽트를 처리할 때 PPI용 irq 아래에 하위 파티션 도메인 및 irq 디스크립터들을 구성하고, 호출하는 과정을 보여준다.
다음 그림은 per-cpu 인터럽트를 처리 하기 위해 생성된 파티션 디스크립터의 내부 구조를 보여준다.
- GIC v3 드라이버는 초기화 시 모든 PPI 수 만큼 파티션 디스크립터를 생성한다. 그러나 실제 운영은 파티션 요청한 디바이스에 한하여 운영된다.
다음은 디바이스 트리에서 일반 PPI를 사용하는 디바이스와 irq 파티션을 사용하는 디바이스의 구성을 보여준다.
- irq 파티션을 사용하려면 인터럽트 컨트롤러 노드내에 ppi-partitions을 구성해야 한다.
- big/little에 사용하는 pmu 디바이스가 각각의 칩에서 별도의 인터럽트 핸들러를 동작시키기위해 irq 파티션을 사용한다.
- 디바이스가 irq 파티션을 사용하려면 interrupts 속성에서 4개 이상의 인자가 필요하고 4 번째 인자는 파티션을 가리키는 phandle을 사용해야 한다. (irq 파티션을 사용하지 않을 때 4 번째 인자는 0이다)
per-cpu 인터럽트를 파티션으로 나눈 인터럽트는 cascaded(chained) irq 연결에 의해 파티션마다 별도의 처리가 가능하다.
다음은 QEMU/KVM를 사용한 Guest OS에서 big/little 코어 각각 1나씩 irq-partition을 만든 경우이다.
GICv3: 256 SPIs implemented GICv3: 0 Extended SPIs implemented GICv3: Distributor has no Range Selector support GICv3: 16 PPIs implemented GICv3: no VLPI support, no direct LPI support GICv3: CPU0: found redistributor 0 region 0:0x00000000080a0000 GICv3: GIC: PPI partition interrupt-partition-0[0] { /cpus/cpu@0[0] } GICv3: GIC: PPI partition interrupt-partition-1[1] { /cpus/cpu@1[1] }
arm-pmu 디바이스가 파티션되어 운영되는 모습을 알 수 있다.
- 하위 파티션 도메인에서 생성된 hwirq는 순서대로 0부터 시작한다.
- “GICv3-23” 출력된 메시지는 INTID#23 이라는 의미이며, PPI#7과 동일하다.
$ cat /proc/interrupts CPU0 CPU1 12: 1822 1678 GICv3 27 Level arch_timer 47: 1527 0 GICv3 78 Edge virtio0 48: 17 0 GICv3 79 Edge virtio1 50: 0 0 GICv3 34 Level rtc-pl031 51: 41 0 GICv3 33 Level uart-pl011 52: 0 0 GICv3-23 0 Level arm-pmu 53: 0 0 GICv3-23 1 Level arm-pmu 54: 0 0 9030000.pl061 3 Edge GPIO Key Poweroff IPI0: 854 798 Rescheduling interrupts IPI1: 8 800 Function call interrupts IPI2: 0 0 CPU stop interrupts IPI3: 0 0 CPU stop (for crash dump) interrupts IPI4: 0 0 Timer broadcast interrupts IPI5: 0 0 IRQ work interrupts IPI6: 0 0 CPU wake-up interrupts
PPI 파티션들 초기화
gic_populate_ppi_partitions()
drivers/irqchip/irq-gic-v3.c -1/2-
/* Create all possible partitions at boot time */ static void __init gic_populate_ppi_partitions(struct device_node *gic_node) { struct device_node *parts_node, *child_part; int part_idx = 0, i; int nr_parts; struct partition_affinity *parts; parts_node = of_get_child_by_name(gic_node, "ppi-partitions"); if (!parts_node) return; gic_data.ppi_descs = kcalloc(gic_data.ppi_nr, sizeof(*gic_data.ppi_descs), GFP_KERNEL); if (!gic_data.ppi_descs) return; nr_parts = of_get_child_count(parts_node); if (!nr_parts) goto out_put_node; parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); if (WARN_ON(!parts)) goto out_put_node; for_each_child_of_node(parts_node, child_part) { struct partition_affinity *part; int n; part = &parts[part_idx]; part->partition_id = of_node_to_fwnode(child_part); pr_info("GIC: PPI partition %pOFn[%d] { ", child_part, part_idx); n = of_property_count_elems_of_size(child_part, "affinity", sizeof(u32)); WARN_ON(n <= 0); for (i = 0; i < n; i++) { int err, cpu; u32 cpu_phandle; struct device_node *cpu_node; err = of_property_read_u32_index(child_part, "affinity", i, &cpu_phandle); if (WARN_ON(err)) continue; cpu_node = of_find_node_by_phandle(cpu_phandle); if (WARN_ON(!cpu_node)) continue; cpu = of_cpu_node_to_id(cpu_node); if (WARN_ON(cpu < 0)) continue; pr_cont("%pOF[%d] ", cpu_node, cpu); cpumask_set_cpu(cpu, &part->mask); } pr_cont("}\n"); part_idx++; }
PPI를 위한 irq 파티션을 생성한다.
- 코드 라인 9~11에서 디바이스 트리에서 ppi-partitions 노드를 찾을 수 없으면 함수를 빠져나간다.
- 코드 라인 13~15에서 ppi 수만큼 partition_desc 구조체를 할당한다.
- 코드 라인 17~20에서 child 노드 수를 읽어와 nr_parts에 대입한다.
- 코드 라인 22~24에서 파티션 수(nr_parts)만큼 partition_affinity 구조체를 할당하고 디바이스 트리의 파티션 정보를 출력한다.
- 다음은 두 개의 파티션을 사용하고 있는 rock960 보드의 관련 커널 로그를 출력한 예이다.
- “GIC: PPI partition interrupt-partition-0[0] { /cpus/cpu@0[0] /cpus/cpu@1[1] /cpus/cpu@2[2] /cpus/cpu@3[3] }”
- “GIC: PPI partition interrupt-partition-1[1] { /cpus/cpu@100[4] /cpus/cpu@101[5] }”
- 다음은 두 개의 파티션을 사용하고 있는 rock960 보드의 관련 커널 로그를 출력한 예이다.
- 코드 라인 26~66에서 파티션 수만큼 순회하며 디바이스 트리의 “affinity” 속성에 포함된 cpu 노드의 cpu 번호를 partition_affinity 구조체에 설정한다.
drivers/irqchip/irq-gic-v3.c -2/2-
for (i = 0; i < gic_data.ppi_nr; i++) { unsigned int irq; struct partition_desc *desc; struct irq_fwspec ppi_fwspec = { .fwnode = gic_data.fwnode, .param_count = 3, .param = { [0] = GIC_IRQ_TYPE_PARTITION, [1] = i, [2] = IRQ_TYPE_NONE, }, }; irq = irq_create_fwspec_mapping(&ppi_fwspec); if (WARN_ON(!irq)) continue; desc = partition_create_desc(gic_data.fwnode, parts, nr_parts, irq, &partition_domain_ops); if (WARN_ON(!desc)) continue; gic_data.ppi_descs[i] = desc; } out_put_node: of_node_put(parts_node); }
- 코드 라인 1~23에서 ppi 수 만큼 순회하며 클러스터 단위의 파티션 별로 구성되어 irq를 나누어 처리하기 위해 irq 도메인 및 irq chip 등을 파티션 구성한다.
- 참고로 3개의 인자로 생성하여 매핑한 PPI 파티션 디스크립터들을 생성하지만, 하위 파티션용 도메인과 하위 irq 디스크립터가 생성되지는 않는다.
파티션 디스크립터 생성
partition_create_desc()
drivers/irqchip/irq-partition-percpu.c
struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode, struct partition_affinity *parts, int nr_parts, int chained_irq, const struct irq_domain_ops *ops) { struct partition_desc *desc; struct irq_domain *d; BUG_ON(!ops->select || !ops->translate); desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (!desc) return NULL; desc->ops = *ops; desc->ops.free = partition_domain_free; desc->ops.alloc = partition_domain_alloc; d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc); if (!d) goto out; desc->domain = d; desc->bitmap = kcalloc(BITS_TO_LONGS(nr_parts), sizeof(long), GFP_KERNEL); if (WARN_ON(!desc->bitmap)) goto out; desc->chained_desc = irq_to_desc(chained_irq); desc->nr_parts = nr_parts; desc->parts = parts; return desc; out: if (d) irq_domain_remove(d); kfree(desc); return NULL; }
하나의 ppi에 해당하는 파티션 디스크립터를 할당하고 구성한다.
- 코드 라인 12~18에서 파티션 디스크립터(partition_desc 구조체)를 할당하고 인자로 전달받은 @ops와 파티션용 도메인 할당/해제 함수를 지정한다.
- partition_domain_alloc() & partition_domain_free()
- 코드 라인 20~23에서 파티션 수 만큼 리니어 irq를 관리하는 도메인을 생성한다.
- 코드 라인 25~28에서 파티션을 관리하기 위한 비트맵을 할당한다.
- 코드 라인 30~32에서 파티션 디스크립터에 @chined_irq와 파티션 수 및 할당한 파티션 affinity를 지정한다.
- 코드 라인 34에서 할당하여 구성한 파티션 디스크립터를 반환한다.
파티션용 irq 도메인 할당
다음 파티션용 도메인이 생성되려면 디바이스 트리에서 디바이스가 interrupts 속성에서 파티션을 지정해야 한다.
- 예) interrupts = <1 7 4 &ppi_cluster0>;
partition_domain_alloc()
drivers/irqchip/irq-partition-percpu.c
static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { int ret; irq_hw_number_t hwirq; unsigned int type; struct irq_fwspec *fwspec = arg; struct partition_desc *part; BUG_ON(nr_irqs != 1); ret = domain->ops->translate(domain, fwspec, &hwirq, &type); if (ret) return ret; part = domain->host_data; set_bit(hwirq, part->bitmap); irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc), partition_handle_irq, part); irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask); irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part, handle_percpu_devid_irq, NULL, NULL); irq_set_status_flags(virq, IRQ_NOAUTOEN); return 0; }
하위 파티션용 @virq를 파티션 도메인의 매치된 hwirq를 설정하고, 상위 도메인에 체인 연결한다.
- 코드 라인 11~13에서 fwspec 인자를 가지고 요청한 하위 irq 도메인의 (*translate) 후크 함수를 호출하여 hwirq 및 타입을 알아온다.
- 파티션용 hwirq의 시작번호는 0번부터 시작한다.
- 코드 라인 15에서 하위 파티션 @domain의 host_data에 저장된 파티션 디스크립터를 알아온다.
- 코드 라인 17에서 해당 파티션에 대한 비트맵을 설정한다.
- 코드 라인 18~19에서 해당 파티션에 대한 chained 핸들러에 partition_handle_irq() 함수를 지정한다.
- 파티션에 대한 상위 irq 디스크립터는 part->chained_desc 이다.
- 코드 라인 20에서 하위 @virq에 해당하는 irq 디스크립터의 percpu 기능을 설정한다.
- 코드 라인 21~22에서 파티션 @domain의 하위 @virq에 관련 hwriq 및 파티션용 irq_chip 정보, 파티션 디스크립터 및 percpu 인터럽트 핸들러들을 설정한다.
- 코드 라인 23에서 하위 @virq에 해당하는 디스크립터의 플래그에 IRQ_NOAUTOEN 플래그를 설정한다.
irq_set_percpu_devid_partition()
kernel/irq/irqdesc.c
int irq_set_percpu_devid_partition(unsigned int irq, const struct cpumask *affinity) { struct irq_desc *desc = irq_to_desc(irq); if (!desc) return -EINVAL; if (desc->percpu_enabled) return -EINVAL; desc->percpu_enabled = kzalloc(sizeof(*desc->percpu_enabled), GFP_KERNEL); if (!desc->percpu_enabled) return -ENOMEM; if (affinity) desc->percpu_affinity = affinity; else desc->percpu_affinity = cpu_possible_mask; irq_set_percpu_devid_flags(irq); return 0; }
@irq에 해당하는 irq 디스크립터에 percpu 기능을 설정한다.
- 코드 라인 4~10에서 @irq 번호에 해당하는 irq 디스크립터에서 percpu_enabled가 이미 설정된 경우 -EINVAL 에러를 반환한다.
- 코드 라인 12~15에서 percpu_enabled에 cpu 비트마스크를 할당한다.
- 코드 라인 17~20에서 irq 디스크립터에 percpu용 affinity를 설정한다. 인자로 받은 @affinity가 주어진 경우 이 값을 사용하고, 주어지지 않은 경우 모든 possible cpu를 지정한다.
- 코드 라인 22에서 irq 디스크립터에 percpu 기능 활성화와 관련된 플래그들을 설정한다.
irq_set_percpu_devid_flags()
include/linux/irq.h
static inline void irq_set_percpu_devid_flags(unsigned int irq) { irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_PER_CPU | IRQ_NOTHREAD | IRQ_NOPROBE | IRQ_PER_CPU_DEVID); }
@irq 번호에 해당하는 디스크립터에 percpu 기능 활성화와 관련된 플래그들을 설정한다.
파티션용 인터럽트 핸들러
partition_handle_irq()
drivers/irqchip/irq-partition-percpu.c
static void partition_handle_irq(struct irq_desc *desc) { struct partition_desc *part = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); int cpu = smp_processor_id(); int hwirq; chained_irq_enter(chip, desc); for_each_set_bit(hwirq, part->bitmap, part->nr_parts) { if (partition_check_cpu(part, cpu, hwirq)) break; } if (unlikely(hwirq == part->nr_parts)) { handle_bad_irq(desc); } else { unsigned int irq; irq = irq_find_mapping(part->domain, hwirq); generic_handle_irq(irq); } chained_irq_exit(chip, desc); }
상위 irq에 등록된 파티션용 핸들러이다. irq 디스크립터(@desc)를 사용하여 해당 파티션을 찾고 그 파티션에 등록된 irq 디스크립터의 핸들러 함수를 호출하여 수행한다.
- 코드 라인 3에서 irq 디스크립터의 핸들러 데이터에 저장된 파티션 디스크립터를 알아온다.
- 코드 라인 4에서 irq 디스크립터의 irq 칩을 알아온다.
- 코드 라인 8에서 chained irq의 시작을 선언한다.
- 코드 라인 10~13에서 파티션 디스크립터의 비트맵을 순회하며 설정된 비트로부터 hwirq를 알아온다.
- 두 개의 파티션이 사용되는 경우 hwirq=0~1이다.
- 코드 라인 15~21에서 파티션 디스크립터에 소속된 파티션 도메인에서 hwirq에 해당하는 irq를 찾아 해당 핸들러 함수를 호출한다.
- PPI 인터럽트 핸들러인 handle_percpu_devid_irq() 함수가 호출된다.
- 코드 라인 23에서 chained irq의 끝을 선언한다.
partition_check_cpu()
drivers/irqchip/irq-partition-percpu.c
static bool partition_check_cpu(struct partition_desc *part, unsigned int cpu, unsigned int hwirq) { return cpumask_test_cpu(cpu, &part->parts[hwirq].mask); }
파티션 디스크립터의 @hwirq에 해당하는 파티션에 요청한 @cpu가 포함되어 있는지 여부를 반환한다.
파티션용 irq_chip Operations
partition_irq_chip
drivers/irqchip/irq-partition-percpu.c
static struct irq_chip partition_irq_chip = { .irq_mask = partition_irq_mask, .irq_unmask = partition_irq_unmask, .irq_set_type = partition_irq_set_type, .irq_get_irqchip_state = partition_irq_get_irqchip_state, .irq_set_irqchip_state = partition_irq_set_irqchip_state, .irq_print_chip = partition_irq_print_chip, };
하위 irq 디스크립터에 연동되어 사용되는 irq 칩에 대한 operations 함수이다.
partition_irq_mask()
drivers/irqchip/irq-partition-percpu.c
static void partition_irq_mask(struct irq_data *d) { struct partition_desc *part = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && chip->irq_mask) chip->irq_mask(data); }
하위 irq 디스크립터 @d에 연동된 상위 chained-irq 디스크립터를 사용해 irq mask를 수행한다.
partition_irq_unmask()
drivers/irqchip/irq-partition-percpu.c
static void partition_irq_unmask(struct irq_data *d) { struct partition_desc *part = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && chip->irq_unmask) chip->irq_unmask(data); }
하위 irq 디스크립터 @d에 연동된 상위 chained-irq 디스크립터를 사용해 irq unmask를 수행한다.
partition_irq_set_type()
drivers/irqchip/irq-partition-percpu.c
static int partition_irq_set_type(struct irq_data *d, unsigned int type) { struct partition_desc *part = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); if (chip->irq_set_type) return chip->irq_set_type(data, type); return -EINVAL; }
하위 irq 디스크립터 @d에 연동된 상위 chained-irq 디스크립터를 사용해 irq type을 지정한다.
partition_irq_get_irqchip_state()
drivers/irqchip/irq-partition-percpu.c
static int partition_irq_get_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool *val) { struct partition_desc *part = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && chip->irq_get_irqchip_state) return chip->irq_get_irqchip_state(data, which, val); return -EINVAL; }
하위 irq 디스크립터 @d에 연동된 상위 chained-irq 디스크립터를 사용해 irqchip state를 알아온다.
partition_irq_set_irqchip_state()
drivers/irqchip/irq-partition-percpu.c
static int partition_irq_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool val) { struct partition_desc *part = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); if (partition_check_cpu(part, smp_processor_id(), d->hwirq) && chip->irq_set_irqchip_state) return chip->irq_set_irqchip_state(data, which, val); return -EINVAL; }
하위 irq 디스크립터 @d에 연동된 상위 chained-irq 디스크립터를 사용해 irqchip state를 설정한다.
partition_irq_print_chip()
drivers/irqchip/irq-partition-percpu.c
static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct partition_desc *part = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_desc_get_chip(part->chained_desc); struct irq_data *data = irq_desc_get_irq_data(part->chained_desc); seq_printf(p, " %5s-%lu", chip->name, data->hwirq); }
하위 irq 디스크립터 @d에 연동된 상위 chained-irq 디스크립터이 사용중인 irq chip 명과 hwirq를 출력한다.
- 예) “GICv3-23”
- INTID#23 -> PPI#7
GIC v3 인터럽트 컨트롤러
partition_domain_ops
drivers/irqchip/irq-gic-v3.c
static const struct irq_domain_ops partition_domain_ops = { .translate = partition_domain_translate, .select = gic_irq_domain_select, };
파티션 도메인용 opeartions이다.
- 하위 도메인으로 사용된다.
파티션용 irq 도메인 변환
partition_domain_translate()
drivers/irqchip/irq-partition-percpu.c
static int partition_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { struct device_node *np; int ret; if (!gic_data.ppi_descs) return -ENOMEM; np = of_find_node_by_phandle(fwspec->param[3]); if (WARN_ON(!np)) return -EINVAL; ret = partition_translate_id(gic_data.ppi_descs[fwspec->param[1]], of_node_to_fwnode(np)); if (ret < 0) return ret; *hwirq = ret; *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; return 0; }
파티션 도메인(@d)에사 @fwspec로 irq 디스크립터를 찾아 해당 hwirq 및 type을 알아온다. 성공 시 0을 반환한다.
- 코드 라인 9~10에서 ppi용 파티션 디스크립터들이 만들어지지 않은 경우 함수를 빠져나간다.
- 코드 라인 12~14에서 @fwspec으로 전달받은 4번째 인자에 phandle 해당하는 노드를 찾아온다.
- 찾은 디바이스 노드가 파티션을 위한 클러스터 노드이다.
- 코드 라인 16~19에서 @fwspec의 두 번째 인자인 PPI hwirq에 대응하는 파티션 디스크립터와 클러스터 노드의 fwnode를 사용하여 hwirq를 알아온다.
- 코드 라인 21~24에서 출력 인자 @hwirq에 알아온 hwirq를 대입하고, 출력 인자 @type에는 irq 타입 정보를 담아 정상 0 값을 반환한다.
partition_translate_id()
drivers/irqchip/irq-partition-percpu.c
int partition_translate_id(struct partition_desc *desc, void *partition_id) { struct partition_affinity *part = NULL; int i; for (i = 0; i < desc->nr_parts; i++) { if (desc->parts[i].partition_id == partition_id) { part = &desc->parts[i]; break; } } if (WARN_ON(!part)) { pr_err("Failed to find partition\n"); return -EINVAL; } return i; }
파티션 디스크립터와 @partition_id(fwnode)로 hwirq를 찾아온다. 찾지못하는 경우 nr_parts 값이 반환된다.
파티션용 irq 도메인 선택
gic_irq_domain_select()
drivers/irqchip/irq-gic-v3.c
static int gic_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, enum irq_domain_bus_token bus_token) { /* Not for us */ if (fwspec->fwnode != d->fwnode) return 0; /* If this is not DT, then we have a single domain */ if (!is_of_node(fwspec->fwnode)) return 1; /* * If this is a PPI and we have a 4th (non-null) parameter, * then we need to match the partition domain. */ if (fwspec->param_count >= 4 && fwspec->param[0] == 1 && fwspec->param[3] != 0 && gic_data.ppi_descs) return d == partition_get_domain(gic_data.ppi_descs[fwspec->param[1]]); return d == gic_data.domain; }
@fwspec에 해당하는 irq 도메인을 선택한다. 입력 인자로 요청한 irq 도메인(@d)과 동일한 경우 1을 반환한다.
- 코드 라인 6~7에서 인자로 받은 @fwspec->fwnode와 인자로 받은 irq 도메인의 fwnode가 서로 다른 경우 실패 값 0을 반환한다.
- 코드 라인 10~11에서 디바이스 트리 노드가 아닌 경우 싱글 도메인만 있으므로 1을 반환한다.
- 코드 라인 17~20에서 PPI이고 4개 이상의 파라미터를 가졌으며 파티션 도메인을 가진 경우 파티션 디스크립터에 등록한 irq 도메인과 인자로 받은 irq 도메인이 동일한지 여부를 반환한다.
- 반드시 param_count가 4 이상이며 4번째 파라미터에는 클러스터 phandle 값이 있어야 한다.
- 코드 라인 22에서 파티션 도메인이 아닌 일반 PPI/SPI의 경우 인자로 요청한 irq 도메인(@d)과 gic 기본 도메인이 동일하지 여부를 반환한다.
partition_get_domain()
drivers/irqchip/irq-partition-percpu.c
struct irq_domain *partition_get_domain(struct partition_desc *dsc) { if (dsc) return dsc->domain; return NULL; }
파티션 디스크립터에 소속된 irq 도메인을 반환한다.
구조체
partition_desc 구조체
drivers/irqchip/irq-partition-percpu.c
struct partition_desc { int nr_parts; struct partition_affinity *parts; struct irq_domain *domain; struct irq_desc *chained_desc; unsigned long *bitmap; struct irq_domain_ops ops; };
파티션 디스크립터이다.
- nr_parts
- 구성된 파티션 수이다.
- 클러스터가 4개인 경우 nr_parts=4이다.
- *parts
- 파티션 수만큼 cpu 구성정보를 담는 partition_affinity 구조체들을 가리킨다.
- *domain
- 파티션에 소속된 파티션 도메인을 가리킨다.
- *chained_desc
- 상위 chained irq (PPI irq)를 가리킨다.
- *bitmap
- 운용되는 파티션에 대한 비트맵이다.
- ops
- 파티션에서 사용할 파티션 도메인용 operations이다.
partition_affinity 구조체
include/linux/irqchip/irq-partition-percpu.h
struct partition_affinity { cpumask_t mask; void *partition_id; };
파티션이 사용할 cpu 구성정보를 담는다.
- mask
- 파티션에 포함된 cpu 마스크
- *partition_id
- 디바이스 트리의 파티션 노드에 해당하는 fwnode_handle
참고
- 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