Interrupts -10- (irq partition)

<kernel v5.4>

Interrupts -10- (irq partition)

PPI는 per-cpu 인터럽트들은 irq번호가 동일하다. 그런데 클러스터마다 인터럽트 트리거 타입 설정 및 인터럽트 처리 방법이 다른 경우가 발생하여 이를 처리하기 위해 클러스터 단위로 파티션을 나누어 처리하였다.

 

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] }”
  • 코드 라인 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

 

참고

 

댓글 남기기