<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





