[커널 18차] 71주차
2022.10.01 22:34
gic interrupt 초기화 및 등록 진행중
git : https://github.com/iamroot18/5.10/commit/a6199cc3f2e939767614dbf07299cb1be7584b14
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 453918eb7390..bb37a8c98091 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -806,6 +806,10 @@ EXPORT_SYMBOL_GPL(device_get_named_child_node);
*
* Returns the fwnode handle.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - ref up
+ */
struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
{
if (!fwnode_has_op(fwnode, get))
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c
index a610821c8ff2..42ca5d389b18 100644
--- a/drivers/irqchip/irq-gic-common.c
+++ b/drivers/irqchip/irq-gic-common.c
@@ -12,6 +12,10 @@
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+/*
+ * IAMROOT, 2022.10.01:
+ * - @quirks(ex gic_quirks)를 돌며 init 처리. 에라타같은것.
+ */
void gic_enable_of_quirks(const struct device_node *np,
const struct gic_quirk *quirks, void *data)
{
@@ -24,6 +28,10 @@ void gic_enable_of_quirks(const struct device_node *np,
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - gic_enable_of_quirks랑 비슷한데 iddr이 일치하는지만 한번더 따진것.
+ */
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
void *data)
{
@@ -38,6 +46,14 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * @return 0 = IRQ_SET_MASK_OK
+ *
+ * -0b00 Corresponding interrupt is level-sensitive.
+ * 0b10 Corresponding interrupt is edge-triggered.
+ * - level인지, edge인지에 따라 @irq에 해당하는 bit 자리를 찾아서 set한다.
+ */
int gic_configure_irq(unsigned int irq, unsigned int type,
void __iomem *base, void (*sync_access)(void))
{
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index fd4e9a37fea6..5e373fc12db8 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -59,10 +59,22 @@ struct gic_chip_data {
};
static struct gic_chip_data gic_data __read_mostly;
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - gic_init_bases에서 hyp_mode가 안켜져있으면 disable시킨다.
+ * eoi를 drop / deactivate의 두개의 기능으로 나누는 것에 대한 지원여부
+ */
static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
#define GIC_ID_NR (1U << GICD_TYPER_ID_BITS(gic_data.rdists.gicd_typer))
#define GIC_LINE_NR min(GICD_TYPER_SPIS(gic_data.rdists.gicd_typer), 1020U)
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - SPIs 확장. Extended SPIs
+ * 1020개 이상 확장 시킬수있는것인지.
+ */
#define GIC_ESPI_NR GICD_TYPER_ESPIS(gic_data.rdists.gicd_typer)
/*
@@ -130,6 +142,10 @@ static DEFINE_PER_CPU(bool, has_rss);
#define MPIDR_RS(mpidr) (((mpidr) & 0xF0UL) >> 4)
#define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
+/*
+ * IAMROOT, 2022.10.01:
+ * - sgi_base = rd_base + 64k
+ */
#define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K)
/* Our default, arbitrary priority value. Linux only uses one anyway. */
@@ -145,6 +161,10 @@ enum gic_intid_range {
__INVALID_RANGE__
};
+/*
+ * IAMROOT, 2022.10.01:
+ * - @hwirq로 어떤 gic인지 알아온다.
+ */
static enum gic_intid_range __get_intid_range(irq_hw_number_t hwirq)
{
switch (hwirq) {
@@ -175,6 +195,10 @@ static inline unsigned int gic_irq(struct irq_data *d)
return d->hwirq;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - rdist(redirect distribute)에 있는 interrupt인지 확인
+ */
static inline bool gic_irq_in_rdist(struct irq_data *d)
{
switch (get_intid_range(d)) {
@@ -206,6 +230,10 @@ static inline void __iomem *gic_dist_base(struct irq_data *d)
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - rwp가 풀릴때까지 기다린다. max 1초
+ */
static void gic_do_wait_for_rwp(void __iomem *base)
{
u32 count = 1000000; /* 1s! */
@@ -222,12 +250,20 @@ static void gic_do_wait_for_rwp(void __iomem *base)
}
/* Wait for completion of a distributor change */
+/*
+ * IAMROOT, 2022.10.01:
+ * - rwp wait.
+ */
static void gic_dist_wait_for_rwp(void)
{
gic_do_wait_for_rwp(gic_data.dist_base);
}
/* Wait for completion of a redistributor change */
+/*
+ * IAMROOT, 2022.10.01:
+ * - rd base에서 rwp wait.
+ */
static void gic_redist_wait_for_rwp(void)
{
gic_do_wait_for_rwp(gic_data_rdist_rd_base());
@@ -284,6 +320,12 @@ static void gic_enable_redist(bool enable)
/*
* Routines to disable, enable, EOI and route interrupts
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * @index hwirq
+ * @offset base register offset
+ * @return base register offset
+ */
static u32 convert_offset_index(struct irq_data *d, u32 offset, u32 *index)
{
switch (get_intid_range(d)) {
@@ -334,6 +376,10 @@ static u32 convert_offset_index(struct irq_data *d, u32 offset, u32 *index)
return offset;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - offset에 해당하는 bit가 set이면 return 1, 없으면 0
+ */
static int gic_peek_irq(struct irq_data *d, u32 offset)
{
void __iomem *base;
@@ -350,13 +396,26 @@ static int gic_peek_irq(struct irq_data *d, u32 offset)
return !!(readl_relaxed(base + offset + (index / 32) * 4) & mask);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @d에 해당하는 irq bit를 set하고 rwp wait를 한다.
+ */
static void gic_poke_irq(struct irq_data *d, u32 offset)
{
void (*rwp_wait)(void);
void __iomem *base;
u32 index, mask;
+/*
+ * IAMROOT, 2022.10.01:
+ * - index에 hwirq 번호가 가져와지고,
+ */
offset = convert_offset_index(d, offset, &index);
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - 가져온 hwirq번호로 irq bit를 찾아 mask에 기록한다.
+ */
mask = 1 << (index % 32);
if (gic_irq_in_rdist(d)) {
@@ -371,6 +430,11 @@ static void gic_poke_irq(struct irq_data *d, u32 offset)
rwp_wait();
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - Interrupt Clear-Enable Registers
+ * clear 요청. interrupt를 disable한다.
+ */
static void gic_mask_irq(struct irq_data *d)
{
gic_poke_irq(d, GICD_ICENABLER);
@@ -391,6 +455,11 @@ static void gic_eoimode1_mask_irq(struct irq_data *d)
gic_poke_irq(d, GICD_ICACTIVER);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - Interrupt Set-Enable Registers
+ * Set 요청. interrupt를 enable한다.
+ */
static void gic_unmask_irq(struct irq_data *d)
{
gic_poke_irq(d, GICD_ISENABLER);
@@ -570,6 +639,10 @@ static void gic_eoimode1_eoi_irq(struct irq_data *d)
gic_write_dir(gic_irq(d));
}
+/*
+ * IAMROOT, 2022.10.01:
+ * @return 0 = IRQ_SET_MASK_OK
+ */
static int gic_set_type(struct irq_data *d, unsigned int type)
{
enum gic_intid_range range;
@@ -581,10 +654,18 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
range = get_intid_range(d);
+/*
+ * IAMROOT, 2022.10.01:
+ * - sgi는 항상 EDGE_RISING이여야 한다. 맞다면 IRQ_SET_MASK_OK를 return.
+ */
/* Interrupt configuration for SGIs can't be changed */
if (range == SGI_RANGE)
return type != IRQ_TYPE_EDGE_RISING ? -EINVAL : 0;
+/*
+ * IAMROOT, 2022.10.01:
+ * - spi는 IRQ_TYPE_LEVEL_HIGH이거나 EDGE_RISING이여야 한다.
+ */
/* SPIs have restrictions on the supported types */
if ((range == SPI_RANGE || range == ESPI_RANGE) &&
type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
@@ -1231,6 +1312,11 @@ static void __init gic_smp_init(void)
set_smp_ipi_range(base_sgi, 8);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @d에 해당하는 gic register를 가져와 @mask_val의 cpu중 하나를 골라서
+ * mpidr을 가져와 gic register에 기록한다.
+ */
static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
{
@@ -1240,6 +1326,10 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
int enabled;
u64 val;
+/*
+ * IAMROOT, 2022.10.01:
+ * - mask_val중에 한개 선택한다.
+ */
if (force)
cpu = cpumask_first(mask_val);
else
@@ -1248,11 +1338,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
if (cpu >= nr_cpu_ids)
return -EINVAL;
+/*
+ * IAMROOT, 2022.10.01:
+ * - spi류들만 가능하다. per cpu류들은 안된다.
+ */
if (gic_irq_in_rdist(d))
return -EINVAL;
/* If interrupt was enabled, disable it first */
enabled = gic_peek_irq(d, GICD_ISENABLER);
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - enabled되있있으면 잠깐 mask해놓는다.
+ */
if (enabled)
gic_mask_irq(d);
@@ -1260,12 +1359,21 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
reg = gic_dist_base(d) + offset + (index * 8);
val = gic_mpidr_to_affinity(cpu_logical_map(cpu));
+/*
+ * IAMROOT, 2022.10.01:
+ * - 해당 irq에 대한 gic register를 가져와서 cpu mpdir을 write한다
+ * - *reg = val
+ */
gic_write_irouter(val, reg);
/*
* If the interrupt was enabled, enabled it again. Otherwise,
* just wait for the distributor to have digested our changes.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - mask해놨으면 다시 unmask한다. 그게 아니면 wait rwp
+ */
if (enabled)
gic_unmask_irq(d);
else
@@ -1357,6 +1465,10 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
struct irq_chip *chip = &gic_chip;
struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));
+/*
+ * IAMROOT, 2022.10.01:
+ * - supports_deactivate_key를 지원하면 eoimode1로 한다.
+ */
if (static_branch_likely(&supports_deactivate_key))
chip = &gic_eoimode1_chip;
@@ -1364,6 +1476,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
case SGI_RANGE:
case PPI_RANGE:
case EPPI_RANGE:
+/*
+ * IAMROOT, 2022.10.01:
+ * - 위 irq들은 cpu별로 하는것이므로 per cpu로 설정해줘야한다.
+ * - flow handler(handle_percpu_devid_irq())를 per cpu방식으로 설정한다.
+ */
irq_set_percpu_devid(irq);
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
@@ -1393,17 +1510,42 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
return 0;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @fwspec 정보로 @d를 통해서 @hwirq, @type을 알아온다.
+ */
static int gic_irq_domain_translate(struct irq_domain *d,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
+/*
+ * IAMROOT, 2022.10.01:
+ * - 1개의 인자 + 16미만의 번호. coner case로 변환 없이 그대로 반환한다.
+ */
if (fwspec->param_count == 1 && fwspec->param[0] < 16) {
*hwirq = fwspec->param[0];
*type = IRQ_TYPE_EDGE_RISING;
return 0;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - __get_intid_range() 참고
+ * - dt에서 왓으면 인자는 3개정도 와야된다.
+ * param[0] param[1] (irq nr)
+ * 0 => spi라는 의미. spi. +32
+ * 1 => ppi ppi. +16
+ * 2 => ESPI ESPI. +ESPI_BASE_INTID
+ * 3 => EPPI EPPI. +EPPI_BASE_INTID
+ * GIC_IRQ_TYPE_LPI LPI. 그대로 사용.
+ * GIC_IRQ_TYPE_PARTITION PMU처리를 위해 한개의 계층을 또 만드는 개념.
+ * 16이상이면 +EPPI_BASE_INTID - 16
+ * 16미만이면 +16
+ *
+ * param[2] = irq line trigger 속성.
+ * - SGI는 여기에 진입할 일이 없다.
+ */
if (is_of_node(fwspec->fwnode)) {
if (fwspec->param_count < 3)
return -EINVAL;
@@ -1446,6 +1588,12 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return 0;
}
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - fwnode는 param이 2개여야한다.
+ * 0번은 hwirq, 1번은 type 고정
+ */
if (is_fwnode_irqchip(fwspec->fwnode)) {
if(fwspec->param_count != 2)
return -EINVAL;
@@ -1460,6 +1608,10 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return -EINVAL;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @
+ */
static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
@@ -1468,10 +1620,18 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int type = IRQ_TYPE_NONE;
struct irq_fwspec *fwspec = arg;
+/*
+ * IAMROOT, 2022.10.01:
+ * - base가 되는 hwirq를 알아온다.
+ */
ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
if (ret)
return ret;
+/*
+ * IAMROOT, 2022.10.01:
+ * - @virq와 hwirq로 nr_irqs만큼 mapping을 해준다.
+ */
for (i = 0; i < nr_irqs; i++) {
ret = gic_irq_domain_map(domain, virq + i, hwirq + i);
if (ret)
@@ -1544,6 +1704,10 @@ static int gic_irq_domain_select(struct irq_domain *d,
return d == partition_get_domain(gic_data.ppi_descs[ppi_idx]);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - tree
+ */
static const struct irq_domain_ops gic_irq_domain_ops = {
.translate = gic_irq_domain_translate,
.alloc = gic_irq_domain_alloc,
@@ -1725,6 +1889,10 @@ static void gic_enable_nmi_support(void)
gic_chip.flags |= IRQCHIP_SUPPORTS_NMI;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * -
+ */
static int __init gic_init_bases(void __iomem *dist_base,
struct redist_region *rdist_regs,
u32 nr_redist_regions,
@@ -1755,6 +1923,13 @@ static int __init gic_init_bases(void __iomem *dist_base,
gic_enable_quirks(readl_relaxed(gic_data.dist_base + GICD_IIDR),
gic_quirks, &gic_data);
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - SPIs(Shared Peripherals Interrupts)
+ * core에 공용으로 사용될 수 있는 IRQ 들로 INTID32 ~ 1019까지 사용한다.
+ * GIC에서 사용하는 클럭과 연동된다
+ */
pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);
@@ -1818,6 +1993,10 @@ static int __init gic_init_bases(void __iomem *dist_base,
return err;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - GICD_PIDR2 register의 GIC_PIDR2_ARCH를 읽어서 v3 or v4인지 확인한다.
+ */
static int __init gic_validate_dist_version(void __iomem *dist_base)
{
u32 reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
@@ -1949,6 +2128,33 @@ static void __init gic_of_setup_kvm_info(struct device_node *node)
vgic_set_kvm_info(&gic_v3_kvm_info);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - index 0 : dist_base register
+ * index 1 ~ nr_redist_regions + 1 : redist register 0 ~ nr_redist_regions의
+ * register
+ * - #redistributors-regions : nr_redist_regions개수
+ * - ex)
+ * gic: interrupt-controller@4d000000 {
+ * compatible = "arm,gic-v3";
+ * #interrupt-cells = <3>;
+ * #address-cells = <2>;
+ * #size-cells = <2>;
+ * ranges;
+ * interrupt-controller;
+ * #redistributor-regions = <4>;
+ * redistributor-stride = <0x0 0x40000>;
+ * reg = <0x0 0x4d000000 0x0 0x10000>, // GICD
+ * <0x0 0x4d100000 0x0 0x400000>, // p0 GICR node 0
+ * <0x0 0x6d100000 0x0 0x400000>, // p0 GICR node 1
+ * <0x400 0x4d100000 0x0 0x400000>, // p1 GICR node 2
+ * <0x400 0x6d100000 0x0 0x400000>, // p1 GICR node 3
+ * <0x0 0xfe000000 0x0 0x10000>, // GICC
+ * <0x0 0xfe010000 0x0 0x10000>, // GICH
+ * <0x0 0xfe020000 0x0 0x10000>; // GICV
+ * interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ * ...
+ */
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *dist_base;
@@ -1957,6 +2163,10 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
u32 nr_redist_regions;
int err, i;
+/*
+ * IAMROOT, 2022.10.01:
+ * - 0 index에 대한 register의 가상 address를 dist_base로 가져온다.
+ */
dist_base = of_iomap(node, 0);
if (!dist_base) {
pr_err("%pOF: unable to map gic dist registers\n", node);
@@ -1969,9 +2179,18 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
goto out_unmap_dist;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - redistributor-regions가 없으면 1. 있으면 읽어온 값으로.
+ */
if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
nr_redist_regions = 1;
+/*
+ * IAMROOT, 2022.10.01:
+ * - 읽어온 redist_regions의 개수만큼 할당한다.
+ * (nr_redist_regions * sizeof(*rdist_regs))
+ */
rdist_regs = kcalloc(nr_redist_regions, sizeof(*rdist_regs),
GFP_KERNEL);
if (!rdist_regs) {
@@ -1979,6 +2198,11 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
goto out_unmap_dist;
}
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - index 1번 ~ nr_redist_regions + 1까지 읽으면서 redist register를 초기화한다.
+ */
for (i = 0; i < nr_redist_regions; i++) {
struct resource res;
int ret;
@@ -1993,6 +2217,14 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
rdist_regs[i].phys_base = res.start;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - redistributor-stride
+ * - 없으면 0로 초기화.
+ * redist_base의 간격을 64배수간격으로 조정
+ * - ex) redistributor-stride = <0x0 0x40000>
+ * 256k 간격으로 조정.
+ */
if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
redist_stride = 0;
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 5f22c9d65e57..d87aa17cde6e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -1116,12 +1116,22 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
return 0;
}
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - linear
+ */
static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
.translate = gic_irq_domain_translate,
.alloc = gic_irq_domain_alloc,
.free = irq_domain_free_irqs_top,
};
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - Legacy
+ */
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.unmap = gic_irq_domain_unmap,
diff --git a/include/linux/cache.h b/include/linux/cache.h
index d742c57eaee5..980243ab3342 100644
--- a/include/linux/cache.h
+++ b/include/linux/cache.h
@@ -74,6 +74,11 @@
#if !defined(____cacheline_internodealigned_in_smp)
#if defined(CONFIG_SMP)
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - l1 cache단위로 정렬. 기본 64byte
+ */
#define ____cacheline_internodealigned_in_smp \
__attribute__((__aligned__(1 << (INTERNODE_CACHE_SHIFT))))
#else
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index eb36a107e9d4..2bdecc1c458a 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -42,6 +42,10 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
# define __force __attribute__((force))
# define __nocast __attribute__((nocast))
# define __safe __attribute__((safe))
+/*
+ * IAMROOT, 2022.10.01:
+ * - 순수 pointer 전달금지.
+ */
# define __private __attribute__((noderef))
# define ACCESS_PRIVATE(p, member) (*((typeof((p)->member) __force *) &(p)->member))
#else /* __CHECKER__ */
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index e49dfc5fcc68..f5dfddde9fef 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -186,6 +186,10 @@ static inline void fwnode_init(struct fwnode_handle *fwnode,
INIT_LIST_HEAD(&fwnode->suppliers);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - fwnode의 init / deinit여부를 저장.
+ */
static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode,
bool initialized)
{
diff --git a/include/linux/irq.h b/include/linux/irq.h
index c8293c817646..a456b05de4a2 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -95,6 +95,14 @@ enum {
IRQ_NO_BALANCING = (1 << 13),
IRQ_MOVE_PCNTXT = (1 << 14),
IRQ_NESTED_THREAD = (1 << 15),
+/*
+ * IAMROOT, 2022.10.01:
+ * hardirq, threaded irq의 2가지로 처리한다.
+ * - threaded irq
+ * 보통 irq가 발생하면 hardirq로 처리를 하는데(isr에서 직접처리)
+ * rt kernel에서는 flag등을 set하고 bottom half에서 후에 처리하는 식을 사용한다.
+ * 이것을 threaded irq라고 부른다. 여기서는 threaded를 사용하지 말라는것.
+ */
IRQ_NOTHREAD = (1 << 16),
IRQ_PER_CPU_DEVID = (1 << 17),
IRQ_IS_POLLED = (1 << 18),
@@ -145,6 +153,10 @@ struct irq_domain;
* @ipi_offset: Offset of first IPI target cpu in @affinity. Optional.
*/
struct irq_common_data {
+/*
+ * IAMROOT, 2022.10.01:
+ * - irqd에 대한 state. IRQ_TYPE_SENSE_MASK에 대한 값등이 올수있다.
+ */
unsigned int __private state_use_accessors;
#ifdef CONFIG_NUMA
unsigned int node;
@@ -174,6 +186,12 @@ struct irq_common_data {
* @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - 계층구조를 지원할려고 irq_desc에서 irq_data만 빼놓았다.
+ * - chip으로 해당 irq의 소속 hw와 연결된다.
+ * - domain을 통해서 해당 irq가 소속된 domain(tree, linear...)과 연결된다.
+ */
struct irq_data {
u32 mask;
unsigned int irq;
@@ -223,6 +241,10 @@ struct irq_data {
* irqchip have flag IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND set.
*/
enum {
+/*
+ * IAMROOT, 2022.10.01:
+ * - IRQ_TYPE_SENSE_MASK
+ */
IRQD_TRIGGER_MASK = 0xf,
IRQD_SETAFFINITY_PENDING = (1 << 8),
IRQD_ACTIVATED = (1 << 9),
@@ -281,6 +303,10 @@ static inline bool irqd_trigger_type_was_set(struct irq_data *d)
return __irqd_to_state(d) & IRQD_DEFAULT_TRIGGER_SET;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - IRQ_TYPE_SENSE_MASK
+ */
static inline u32 irqd_get_trigger_type(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_TRIGGER_MASK;
@@ -756,6 +782,10 @@ irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set);
+/*
+ * IAMROOT, 2022.10.01:
+ * - @irq의 irq_desc에 @set bits를 set한다.
+ */
static inline void irq_set_status_flags(unsigned int irq, unsigned long set)
{
irq_modify_status(irq, 0, set);
@@ -794,6 +824,20 @@ static inline void irq_set_nested_thread(unsigned int irq, bool nest)
irq_clear_status_flags(irq, IRQ_NESTED_THREAD);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @irq의 irq_desc에 flag들을 추가한다.
+ * - IRQ_NOAUTOEN
+ * 수동으로 enable하겠다는것.
+ * - IRQ_PER_CPU
+ * per cpu로 만들라는 것.
+ * - IRQ_NOTHREAD
+ * threaded를 쓰지 말라는것.
+ * - IRQ_NOPROBE
+ * 수동으로 probe하겠다는것.
+ * - IRQ_PER_CPU_DEVID
+ * per cpu id
+ */
static inline void irq_set_percpu_devid_flags(unsigned int irq)
{
irq_set_status_flags(irq,
@@ -875,6 +919,11 @@ static inline int irq_data_get_node(struct irq_data *d)
return irq_common_data_get_node(d->common);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * @return affinity cpu mask
+ * - irq_data 설정시 별다른 Affinity 요청이 없었으면 possible cpu로 되있을 것이다.
+ */
static inline struct cpumask *irq_get_affinity_mask(int irq)
{
struct irq_data *d = irq_get_irq_data(irq);
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index 81cbf85f73de..96bf0396310f 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -27,6 +27,10 @@
#define GICD_ISACTIVER 0x0300
#define GICD_ICACTIVER 0x0380
#define GICD_IPRIORITYR 0x0400
+/*
+ * IAMROOT, 2022.10.01:
+ * - Interrupt Configuration Registers
+ */
#define GICD_ICFGR 0x0C00
#define GICD_IGRPMODR 0x0D00
#define GICD_NSACR 0x0E00
@@ -55,6 +59,11 @@
#define GICD_CPENDSGIR 0x0F10
#define GICD_SPENDSGIR 0x0F20
+/*
+ * IAMROOT, 2022.10.01:
+ * - Register Write Pending.
+ * Read only. Indicates whether a register write is in progress or not
+ */
#define GICD_CTLR_RWP (1U << 31)
#define GICD_CTLR_nASSGIreq (1U << 8)
#define GICD_CTLR_DS (1U << 6)
@@ -86,6 +95,12 @@
#define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
#define GICD_TYPER_NUM_LPIS(typer) ((((typer) >> 11) & 0x1f) + 1)
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - If the value of this field is N, the maximum SPI INTID is 32(N+1) minus 1.
+ * For example, 00011 specifies that the maximum SPI INTID in is 127
+ */
#define GICD_TYPER_SPIS(typer) ((((typer) & 0x1f) + 1) * 32)
#define GICD_TYPER_ESPIS(typer) \
(((typer) & GICD_TYPER_ESPI) ? GICD_TYPER_SPIS((typer) >> 27) : 0)
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 59aea39785bf..5ee9736f4ae3 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -52,8 +52,17 @@ struct pt_regs;
* @debugfs_file: dentry for the debugfs file
* @name: flow handler name for /proc/interrupts output
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - 한개의 irq번호에 대해 관리하는 자료구조.
+ * - 한개이상의 hwirq를 irq_data를 통해서 관리한다.
+ */
struct irq_desc {
struct irq_common_data irq_common_data;
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq_data를 통해서 계층구조가 형성될수있다.
+ */
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 9ee238ad29ce..3d23be490cdd 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -118,6 +118,11 @@ struct irq_domain_ops {
unsigned int nr_irqs);
int (*activate)(struct irq_domain *d, struct irq_data *irqd, bool reserve);
void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq로 desc를 찾는목적.
+ */
int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
unsigned long *out_hwirq, unsigned int *out_type);
#endif
@@ -160,6 +165,10 @@ struct irq_domain {
const char *name;
const struct irq_domain_ops *ops;
void *host_data;
+/*
+ * IAMROOT, 2022.10.01:
+ * - IRQ_DOMAIN_NAME_ALLOCATED등
+ */
unsigned int flags;
unsigned int mapcount;
@@ -173,9 +182,36 @@ struct irq_domain {
/* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max;
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - linear일때 size가 정해진다. tree, nomap일때는 0
+ */
unsigned int revmap_size;
struct radix_tree_root revmap_tree;
struct mutex revmap_mutex;
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - linear reverse mapping. radix tree
+ * - hwirq로 virq를 찾을려고하는 배열.
+ * -
+ * +------+
+ * hwirq 0 -> | | -> virqA1
+ * hwirq 1 -> | hw1 | -> virqA2
+ * hwirq 2 -> | | -> virqA3
+ * ... .. ..
+ * hwirq n -> | | -> virqAn
+ * +------+
+ *
+ * +------+
+ * hwirq 0 -> | | -> virqB1
+ * hwirq 1 -> | hw2 | -> virqB2
+ * hwirq 2 -> | | -> virqB3
+ * ... .. ..
+ * hwirq n -> | | -> virqBn
+ * +------+
+ */
struct irq_data __rcu *revmap[];
};
@@ -288,6 +324,10 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
extern const struct fwnode_operations irqchip_fwnode_ops;
+/*
+ * IAMROOT, 2022.10.01:
+ * - acpi + 기타(kvm등) 에서 왔는지를 확인한다.
+ */
static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode)
{
return fwnode && fwnode->ops == &irqchip_fwnode_ops;
@@ -375,6 +415,12 @@ static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *
return __irq_domain_add(fwnode, size, size, 0, ops, host_data);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * @ops ex)gic_irq_domain_ops
+ *
+ * irq domain을 tree로 생성한다.
+ */
static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode,
const struct irq_domain_ops *ops,
void *host_data)
diff --git a/include/linux/of.h b/include/linux/of.h
index 248038057536..b31894f0f987 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -213,6 +213,10 @@ extern raw_spinlock_t devtree_lock;
#ifdef CONFIG_OF
void of_core_init(void);
+/*
+ * IAMROOT, 2022.10.01:
+ * - dt에서 불러왔는지 확인.
+ */
static inline bool is_of_node(const struct fwnode_handle *fwnode)
{
return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &of_fwnode_ops;
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index a98bcfc4be7b..735c342ec9b9 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -155,6 +155,10 @@ int irq_set_chip_data(unsigned int irq, void *data)
}
EXPORT_SYMBOL(irq_set_chip_data);
+/*
+ * IAMROOT, 2022.10.01:
+ * - virq로 desc를 가져와서 irq_data를 가져온다.
+ */
struct irq_data *irq_get_irq_data(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
@@ -190,6 +194,12 @@ enum {
};
#ifdef CONFIG_SMP
+/*
+ * IAMROOT, 2022.10.01:
+ * @return IRQ_STARTUP_NORMAL : affinity managed가 아니면 그냥 normal로 return.
+ * IRQ_STARTUP_MANAGED : 모든 parent까지 activate 성공.
+ * IRQ_STARTUP_ABORT : ativate 실패
+ */
static int
__irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
{
@@ -200,6 +210,11 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
irqd_clr_managed_shutdown(d);
+/*
+ * prifri, 2022.10.01:
+ * - online cpu중에 aff와 겹치는게 없다면 return IRQ_STARTUP_ABORT.
+ * @force 요청이 있다면 warning print를 해준다.
+ */
if (cpumask_any_and(aff, cpu_online_mask) >= nr_cpu_ids) {
/*
* Catch code which fiddles with enable_irq() on a managed
@@ -207,6 +222,13 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
* installment or irq auto probing should not happen on
* managed irqs either.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - papago
+ * 관리되고 잠재적으로 종료되는 IRQ에서 enable_irq()를 사용하는 코드를 잡아라.
+ * 체인 인터럽트 설치 또는 irq 자동 프로빙은 관리되는 irq에서도 발생하지 않아야
+ * 합니다.
+ */
if (WARN_ON_ONCE(force))
return IRQ_STARTUP_ABORT;
/*
@@ -215,6 +237,13 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
* state and let the cpu hotplug mechanism start it up once
* a CPU in the mask becomes available.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - papago
+ * 인터럽트가 요청되었지만 해당 선호도 마스크에 온라인 CPU가 없습니다.
+ * 관리 종료 상태로 전환하고 마스크의 CPU를 사용할 수 있게 되면 CPU 핫플러그
+ * 메커니즘이 이를 시작하도록 합니다.
+ */
return IRQ_STARTUP_ABORT;
}
/*
@@ -233,6 +262,13 @@ __irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force)
}
#endif
+/*
+ * IAMROOT, 2022.10.01:
+ * - startup이 있으면 startup -> clr disable, clr mask.
+ * 그게 아니면 irq_enable
+ * - 주로 gpio control등을 수행한다. ex)npcmgpio_irq_startup
+ * - set bit IRQD_IRQ_STARTED
+ */
static int __irq_startup(struct irq_desc *desc)
{
struct irq_data *d = irq_desc_get_irq_data(desc);
@@ -252,6 +288,10 @@ static int __irq_startup(struct irq_desc *desc)
return ret;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - 진행중
+ */
int irq_startup(struct irq_desc *desc, bool resend, bool force)
{
struct irq_data *d = irq_desc_get_irq_data(desc);
@@ -260,11 +300,21 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force)
desc->depth = 0;
+/*
+ * IAMROOT, 2022.10.01:
+ * - 이미 started면 enable.
+ */
if (irqd_is_started(d)) {
irq_enable(desc);
} else {
switch (__irq_startup_managed(desc, aff, force)) {
case IRQ_STARTUP_NORMAL:
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - IRQCHIP_AFFINITY_PRE_STARTUP이 있으면 먼저 irq_setup_affinity를 먼저 수행하고
+ * 그게 아니면 __irq_startup후에 수행한다.
+ */
if (d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP)
irq_setup_affinity(desc);
ret = __irq_startup(desc);
@@ -295,8 +345,17 @@ int irq_activate(struct irq_desc *desc)
return 0;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * -
+ */
int irq_activate_and_startup(struct irq_desc *desc, bool resend)
{
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - 이미 activate 상태라면 warning
+ */
if (WARN_ON(irq_activate(desc)))
return 0;
return irq_startup(desc, resend, IRQ_START_FORCE);
@@ -332,21 +391,45 @@ void irq_shutdown_and_deactivate(struct irq_desc *desc)
irq_domain_deactivate_irq(&desc->irq_data);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq_enable.
+ * enable엔 unmask도 포함된다.
+ */
void irq_enable(struct irq_desc *desc)
{
+/*
+ * IAMROOT, 2022.10.01:
+ * - disable 상태가 아니면 unmask.
+ */
if (!irqd_irq_disabled(&desc->irq_data)) {
unmask_irq(desc);
} else {
+/*
+ * IAMROOT, 2022.10.01:
+ * - disable 상태라면 irq_enable이 필요한 hw일 경우
+ * disable clr -> irq_enable -> irq_state_clr_masked로 진행한다.
+ */
irq_state_clr_disabled(desc);
if (desc->irq_data.chip->irq_enable) {
desc->irq_data.chip->irq_enable(&desc->irq_data);
irq_state_clr_masked(desc);
} else {
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq_enable이 없다면 unmask만 해주면되는듯
+ * irq_unmask + irq_state_clr_masked
+ */
unmask_irq(desc);
}
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq disable
+ * disable엔 unmask도 포함된다.
+ */
static void __irq_disable(struct irq_desc *desc, bool mask)
{
if (irqd_irq_disabled(&desc->irq_data)) {
@@ -418,17 +501,39 @@ static inline void mask_ack_irq(struct irq_desc *desc)
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - controller에 있는 irq_mask를 막는다.
+ * ex) gic_mask_irq
+ */
void mask_irq(struct irq_desc *desc)
{
+/*
+ * IAMROOT, 2022.10.01:
+ * - @desc의 irqd에 이미 mask되있다고 하면 return.
+ */
if (irqd_irq_masked(&desc->irq_data))
return;
if (desc->irq_data.chip->irq_mask) {
+/*
+ * IAMROOT, 2022.10.01:
+ * - regiset 조작.
+ */
desc->irq_data.chip->irq_mask(&desc->irq_data);
+/*
+ * IAMROOT, 2022.10.01:
+ * - @desc의 irqd에 mask set.
+ */
irq_state_set_masked(desc);
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq_unmask + irq_state_clr_masked 수행
+ * - unmask가 되있다면 안한다. unmask수행
+ */
void unmask_irq(struct irq_desc *desc)
{
if (!irqd_irq_masked(&desc->irq_data))
@@ -989,6 +1094,10 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
* cannot enable/startup the interrupt at this point.
*/
while (irq_data) {
+/*
+ * IAMROOT, 2022.10.01:
+ * - chip 설정이 안된 irq_data를 찾는다.
+ */
if (irq_data->chip != &no_irq_chip)
break;
/*
@@ -1015,9 +1124,14 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
desc->action = NULL;
desc->depth = 1;
}
+
desc->handle_irq = handle;
desc->name = name;
+/*
+ * IAMROOT, 2022.10.01:
+ * - install이 됬고 계층구조에서 child로 요청된경우.
+ */
if (handle != handle_bad_irq && is_chained) {
unsigned int type = irqd_get_trigger_type(&desc->irq_data);
@@ -1034,6 +1148,10 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
desc->handle_irq = handle;
}
+/*
+ * prifri, 2022.10.01:
+ * - bit flag set.
+ */
irq_settings_set_noprobe(desc);
irq_settings_set_norequest(desc);
irq_settings_set_nothread(desc);
@@ -1042,6 +1160,10 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * -
+ */
void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
@@ -1083,6 +1205,10 @@ irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
}
EXPORT_SYMBOL_GPL(irq_set_chip_and_handler_name);
+/*
+ * IAMROOT, 2022.10.01:
+ * - @irq에 대한 irq_desc를 구해와 flag를 @clr, @set한다.
+ */
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
{
unsigned long flags, trigger, tmp;
diff --git a/kernel/irq/dummychip.c b/kernel/irq/dummychip.c
index 7fe6cffe7d0d..e1d2616315cf 100644
--- a/kernel/irq/dummychip.c
+++ b/kernel/irq/dummychip.c
@@ -36,6 +36,10 @@ static unsigned int noop_ret(struct irq_data *data)
/*
* Generic no controller implementation
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - NULL irq_chip
+ */
struct irq_chip no_irq_chip = {
.name = "none",
.irq_startup = noop_ret,
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index 221d80c31e94..dba6cc4203c8 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -28,6 +28,10 @@ void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
*
* Handles spurious and unhandled IRQ's. It also prints a debugmessage.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - invalid handle이 등록될려할때 대신 등록되는것.
+ */
void handle_bad_irq(struct irq_desc *desc)
{
unsigned int irq = irq_desc_get_irq(desc);
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 54363527feea..0d894c6044bd 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -140,6 +140,10 @@ static inline int irq_setup_affinity(struct irq_desc *desc) { return 0; }
#endif
/* Inline functions for support of irq chips on slow busses */
+/*
+ * IAMROOT, 2022.10.01:
+ * - callback irq_bus_lock으로 lock을 건다.(mutex, pm resume등 기타)
+ */
static inline void chip_bus_lock(struct irq_desc *desc)
{
if (unlikely(desc->irq_data.chip->irq_bus_lock))
@@ -166,6 +170,10 @@ __irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
unsigned int check);
void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus);
+/*
+ * IAMROOT, 2022.10.01:
+ * - spinlock + buslock
+ */
static inline struct irq_desc *
irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check)
{
@@ -240,6 +248,10 @@ static inline void irq_state_set_disabled(struct irq_desc *desc)
irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq disable
+ */
static inline void irq_state_set_masked(struct irq_desc *desc)
{
irqd_set(&desc->irq_data, IRQD_IRQ_MASKED);
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 4e3c29bb603c..ef0107a65db1 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -133,6 +133,10 @@ EXPORT_SYMBOL_GPL(nr_irqs);
static DEFINE_MUTEX(sparse_irq_lock);
static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
+/*
+ * IAMROOT, 2022.10.01:
+ * - arm64는 sparse.
+ */
#ifdef CONFIG_SPARSE_IRQ
static void irq_kobj_release(struct kobject *kobj);
@@ -348,6 +352,10 @@ static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
radix_tree_insert(&irq_desc_tree, irq, desc);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - sparse에선 irq desc를 radix tree에서 찾는다.
+ */
struct irq_desc *irq_to_desc(unsigned int irq)
{
return radix_tree_lookup(&irq_desc_tree, irq);
@@ -835,6 +843,19 @@ unsigned int irq_get_next_irq(unsigned int offset)
return find_next_bit(allocated_irqs, nr_irqs, offset);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - spinlock을 건다.
+ * 이때 @bus가 true이면 chip_bus_lock도 건다.
+ * - check
+ * -- percpu인지 검사한다.
+ *
+ * _IRQ_DESC_CHECK + _IRQ_DESC_PERCPU
+ * per_cpu_devid가 없으면 NULL
+ * _IRQ_DESC_CHECK
+ * per_cpu_devid가 있으면 NULL
+ *
+ */
struct irq_desc *
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
unsigned int check)
@@ -867,6 +888,11 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus)
chip_bus_sync_unlock(desc);
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - irq에 대한 cpu affinity를 정한다. @affinity가 NULL로 오면 possible로 고정한다.
+ * - percpu로 사용하고, 수동 enable, 수동 probe로 flag를 set한다.
+ */
int irq_set_percpu_devid_partition(unsigned int irq,
const struct cpumask *affinity)
{
@@ -878,11 +904,19 @@ int irq_set_percpu_devid_partition(unsigned int irq,
if (desc->percpu_enabled)
return -EINVAL;
+/*
+ * IAMROOT, 2022.10.01:
+ * - cpumask alloc
+ */
desc->percpu_enabled = kzalloc(sizeof(*desc->percpu_enabled), GFP_KERNEL);
if (!desc->percpu_enabled)
return -ENOMEM;
+/*
+ * IAMROOT, 2022.10.01:
+ * - NULL이면 cpu_possible_mask로 한다.
+ */
if (affinity)
desc->percpu_affinity = affinity;
else
@@ -892,6 +926,10 @@ int irq_set_percpu_devid_partition(unsigned int irq,
return 0;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @irq의 irq_desc에 percpu방식으로 운영을 하고 모든 cpu로 affinity를 한다.
+ */
int irq_set_percpu_devid(unsigned int irq)
{
return irq_set_percpu_devid_partition(irq, NULL);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 4d8fc65cf38f..aefa4775831a 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -136,6 +136,28 @@ EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);
* Allocates and initializes an irq_domain structure.
* Returns pointer to IRQ domain, or NULL on failure.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - legacy
+ * legacy irq_domain_create_legacy
+ * - simple
+ * irq_domain_create_simple
+ * - linear
+ * irq_domain_create_linear
+ * - nomap
+ * irq_domain_add_nomap
+ * - tree
+ * irq_domain_create_tree
+ *
+ * size hwirq_max direct_max (mapping)
+ * nomap | 0 max_irq max_irq
+ * linear | size size 0
+ * tree | 0 ~0 0
+ * simple | size size 0 yes
+ * legacy | size + first_hwirq size + first_hwirq 0 yes
+ *
+ * - ops example : gic_irq_domain_ops
+ */
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
@@ -146,16 +168,31 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
static atomic_t unknown_domains;
+/*
+ * IAMROOT, 2022.10.01:
+ * - size && direct_max 인 경우는 없다.
+ * - nomap인 경우만 direct_max가 존재한다.
+ */
if (WARN_ON((size && direct_max) ||
(!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max)))
return NULL;
+/*
+ * IAMROOT, 2022.10.01:
+ * - linear mapping 배열을 size만큼 만든다.
+ * reverse map. struct irq_data __rcu
+ *
+ */
domain = kzalloc_node(struct_size(domain, revmap, size),
GFP_KERNEL, of_node_to_nid(to_of_node(fwnode)));
if (!domain)
return NULL;
if (is_fwnode_irqchip(fwnode)) {
+/*
+ * IAMROOT, 2022.10.01:
+ * - dt에서 왔으면 type에 따라 name, fwnode, flags를 초기화한다.
+ */
fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
switch (fwid->type) {
@@ -169,6 +206,10 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
}
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
break;
+/*
+ * IAMROOT, 2022.10.01:
+ * - IRQCHIP_FWNODE_REAL
+ */
default:
domain->fwnode = fwnode;
domain->name = fwid->name;
@@ -176,6 +217,11 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
}
} else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) ||
is_software_node(fwnode)) {
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - 이경우엔 이름을 %pfw형식으로 만들어서 넣는다.
+ */
char *name;
/*
@@ -196,6 +242,10 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - 이렇게 했는데도 이름이 없으면 unknown으로 만든다.
+ */
if (!domain->name) {
if (fwnode)
pr_err("Invalid fwnode type for irqdomain\n");
@@ -218,6 +268,10 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
domain->host_data = host_data;
domain->hwirq_max = hwirq_max;
+/*
+ * IAMROOT, 2022.10.01:
+ * - direct_max는 nomap만 해당된다.
+ */
if (direct_max) {
size = direct_max;
domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP;
@@ -1296,6 +1350,12 @@ static int irq_domain_alloc_irq_data(struct irq_domain *domain,
* @domain: domain to match
* @virq: IRQ number to get irq_data
*/
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - child부터 시작해서 parent에 거슬러 올라가며 domain이 같은 irq_data를
+ * return 한다.
+ */
struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
unsigned int virq)
{
@@ -1318,6 +1378,11 @@ EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
* @chip: The associated interrupt chip
* @chip_data: The associated chip data
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - virq를 가진 irq_data 계층에서 @domain과 일치하는 irq_data를 가져오고,
+ * 해당 irq_data에 @hwirq, @chip, @chip_data를 넣는다.
+ */
int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq, struct irq_chip *chip,
void *chip_data)
@@ -1346,6 +1411,10 @@ EXPORT_SYMBOL_GPL(irq_domain_set_hwirq_and_chip);
* @handler_data: The interrupt flow handler data
* @handler_name: The interrupt handler name
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * -
+ */
void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq, struct irq_chip *chip,
void *chip_data, irq_flow_handler_t handler,
@@ -1749,6 +1818,10 @@ static void __irq_domain_deactivate_irq(struct irq_data *irq_data)
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - parent부터 activate. gic v3 its같은경우엔 actiate ops가 존재한다.
+ */
static int __irq_domain_activate_irq(struct irq_data *irqd, bool reserve)
{
int ret = 0;
@@ -1756,12 +1829,26 @@ static int __irq_domain_activate_irq(struct irq_data *irqd, bool reserve)
if (irqd && irqd->domain) {
struct irq_domain *domain = irqd->domain;
+/*
+ * IAMROOT, 2022.10.01:
+ * - parent 재귀.
+ */
if (irqd->parent_data)
ret = __irq_domain_activate_irq(irqd->parent_data,
reserve);
+/*
+ * IAMROOT, 2022.10.01:
+ * - parent에서 성공했으면 수행한다.
+ * - activate가 있으면 activate.
+ * ops ex) its_domain_ops
+ */
if (!ret && domain->ops->activate) {
ret = domain->ops->activate(domain, irqd, reserve);
/* Rollback in case of error */
+/*
+ * IAMROOT, 2022.10.01:
+ * - 하나라도 실패하면 다시 타고올라가면서 deactivate.
+ */
if (ret && irqd->parent_data)
__irq_domain_deactivate_irq(irqd->parent_data);
}
@@ -1778,6 +1865,11 @@ static int __irq_domain_activate_irq(struct irq_data *irqd, bool reserve)
* This is the second step to call domain_ops->activate to program interrupt
* controllers, so the interrupt could actually get delivered.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - activate 안되있면 activate 수행. actiavte는 gic v3 its.
+ * activate가 정상적으로 수행되면 irq_data에 activate bit를 set한다.
+ */
int irq_domain_activate_irq(struct irq_data *irq_data, bool reserve)
{
int ret = 0;
@@ -1805,6 +1897,13 @@ void irq_domain_deactivate_irq(struct irq_data *irq_data)
}
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - 계층구조로 만들 필요가 있는지를 표시
+ * - ex)
+ * hw1 --> hw2
+ * \-> hw3
+ */
static void irq_domain_check_hierarchy(struct irq_domain *domain)
{
/* Hierarchy irq_domains must implement callback alloc() */
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 27667e82ecc9..cd1a6d244df9 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -143,6 +143,10 @@ EXPORT_SYMBOL(synchronize_irq);
#ifdef CONFIG_SMP
cpumask_var_t irq_default_affinity;
+/*
+ * IAMROOT, 2022.10.01:
+ * - set affinity가 가능한지 확인한다.
+ */
static bool __irq_can_set_affinity(struct irq_desc *desc)
{
if (!desc || !irqd_can_balance(&desc->irq_data) ||
@@ -185,6 +189,17 @@ bool irq_can_set_affinity_usr(unsigned int irq)
* set_cpus_allowed_ptr() here as we hold desc->lock and this
* code can be called from hard interrupt context.
*/
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - papago
+ * 방금 IRQTF_AFFINITY를 설정하고 선호도 설정을 인터럽트 스레드 자체에
+ * 위임했습니다. desc->lock을 유지하고 이 코드는 하드 인터럽트 컨텍스트에서
+ * 호출할 수 있으므로 여기에서 set_cpus_allowed_ptr()을 호출할 수 없습니다.
+ *
+ * - action handler들의 thread_flags에 IRQTF_AFFINITY를 set한다.
+ * notify irq threads
+ */
void irq_set_thread_affinity(struct irq_desc *desc)
{
struct irqaction *action;
@@ -195,6 +210,10 @@ void irq_set_thread_affinity(struct irq_desc *desc)
}
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
+/*
+ * IAMROOT, 2022.10.01:
+ * - affinity cpumask가 비어있다면 print를 한번 출력해준다.
+ */
static void irq_validate_effective_affinity(struct irq_data *data)
{
const struct cpumask *m = irq_data_get_effective_affinity_mask(data);
@@ -217,6 +236,10 @@ static inline void irq_init_effective_affinity(struct irq_data *data,
const struct cpumask *mask) { }
#endif
+/*
+ * IAMROOT, 2022.10.01:
+ * - @mask에 해당하는 cpu중에 하나를 @data irq의 gic register에 set한다.
+ */
int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
bool force)
{
@@ -246,6 +269,25 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
* housekeeping CPU which belongs to the affinity mask comes
* online.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - papago
+ * 이것이 관리되는 인터럽트이고 하우스키핑이 활성화된 경우 요청된 선호도
+ * 마스크가 하우스키핑 CPU와 교차하는지 확인합니다. 그렇다면 마스크에서 격리된
+ * CPU를 제거하고 하우스키핑 CPU만 유지합니다. 이렇게 하면 선호도 설정자가
+ * 인터럽트를 격리된 CPU로 라우팅하여 하우스키핑 CPU에서 제출된 I/O가 격리된
+ * CPU에서 인터럽트를 일으키는 것을 방지합니다.
+ *
+ * 마스크가 교차하지 않거나 온라인 CPU를 포함하지 않는 경우 요청된 마스크를
+ * 유지합니다. 격리된 대상 CPU는 I/O 작업이 직접 제출된 경우에만 인터럽트를
+ * 수신합니다.
+ *
+ * 선호도 마스크의 모든 하우스키핑 CPU가 오프라인인 경우, 선호도 마스크에 속한
+ * 하우스키핑 CPU가 온라인 상태가 되면 인터럽트가 CPU 핫플러그 코드에 의해
+ * 마이그레이션됩니다.
+ *
+ * - interrupt 용 housekeeping cpu가 있는지 한번더 and해서 set affinity를 한다.
+ */
if (irqd_affinity_is_managed(data) &&
housekeeping_enabled(HK_FLAG_MANAGED_IRQ)) {
const struct cpumask *hk_mask, *prog_mask;
@@ -257,6 +299,14 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
raw_spin_lock(&tmp_mask_lock);
cpumask_and(&tmp_mask, mask, hk_mask);
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - housekeeping cpu가 전체 대상이 되서 쓸수있는 cpu가 없다. 그냥 들어온 인자로
+ * 사용한다.
+ * 그게 아니라면 housekeeping cpu까지 고려한 mask를 사용한다.
+ * (online cpu + node cpu + affinity cpu + housekeeping cpu)
+ */
if (!cpumask_intersects(&tmp_mask, cpu_online_mask))
prog_mask = mask;
else
@@ -266,6 +316,13 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
} else {
ret = chip->irq_set_affinity(data, mask, force);
}
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - NOCOPY가 아니면 copy를 한번해준다.
+ * 그후 다시 읽어 cpumask를 한번 검사해주고 irq thread한테 변경됬다는것을
+ * 알려준다.
+ */
switch (ret) {
case IRQ_SET_MASK_OK:
case IRQ_SET_MASK_OK_DONE:
@@ -575,10 +632,19 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
}
EXPORT_SYMBOL_GPL(irq_set_affinity_notifier);
+/*
+ * IAMROOT, 2022.10.01:
+ * - ALPHA를 제외한 모든 머신이 not define
+ */
#ifndef CONFIG_AUTO_IRQ_AFFINITY
/*
* Generic version of the affinity autoselector.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - @desc에 해당하는 irq, cpumask, irq gic register에 cpumask중 하나를 골라
+ * mpidr를 write한다.
+ */
int irq_setup_affinity(struct irq_desc *desc)
{
struct cpumask *set = irq_default_affinity;
@@ -595,6 +661,11 @@ int irq_setup_affinity(struct irq_desc *desc)
* Preserve the managed affinity setting and a userspace affinity
* setup, but make sure that one of the targets is online.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - affinity managed가 되있으면 irqd에 IRQD_AFFINITY_SET을 한다.
+ * online cpu와 affinity중에 겹치는게 있으면
+ */
if (irqd_affinity_is_managed(&desc->irq_data) ||
irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) {
if (cpumask_intersects(desc->irq_common_data.affinity,
@@ -605,9 +676,19 @@ int irq_setup_affinity(struct irq_desc *desc)
}
cpumask_and(&mask, cpu_online_mask, set);
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - 겹치는게 없으면 online으로 .
+ */
if (cpumask_empty(&mask))
cpumask_copy(&mask, cpu_online_mask);
+/*
+ * IAMROOT, 2022.10.01:
+ * - 결국에 affinity cpu, online cpu 와, @desc node, cpu가 전부 겹치는 mask를
+ * 구한다.
+ */
if (node != NUMA_NO_NODE) {
const struct cpumask *nodemask = cpumask_of_node(node);
@@ -615,6 +696,11 @@ int irq_setup_affinity(struct irq_desc *desc)
if (cpumask_intersects(&mask, nodemask))
cpumask_and(&mask, &mask, nodemask);
}
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - 위 조건에 해당하는 mask로 sete affinity를 수행한다.
+ */
ret = irq_do_set_affinity(&desc->irq_data, &mask, false);
raw_spin_unlock(&mask_lock);
return ret;
@@ -936,6 +1022,11 @@ int can_request_irq(unsigned int irq, unsigned long irqflags)
return canrequest;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - @desc의 irq에 tigger @flags를 설정한다.
+ * IRQCHIP_SET_TYPE_MASKED요청이 있다면 잠깐 mask를 한후 설정후 unmask한다.
+ */
int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
{
struct irq_chip *chip = desc->irq_data.chip;
@@ -952,13 +1043,28 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
return 0;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - IRQCHIP_SET_TYPE_MASKED
+ * chip.irq_set_type을 호출하기전에 controller interrupt를 막아야된다는 요청.
+ * 해당 요청이 있으면 gic의 해당 interrupt막는다.
+ */
if (chip->flags & IRQCHIP_SET_TYPE_MASKED) {
if (!irqd_irq_masked(&desc->irq_data))
mask_irq(desc);
+/*
+ * IAMROOT, 2022.10.01:
+ * - 해당 irq가 이미 enable상태라면, 아래 설정이 끝나고나서
+ * unmask을 해야한다.
+ */
if (!irqd_irq_disabled(&desc->irq_data))
unmask = 1;
}
+/*
+ * IAMROOT, 2022.10.01:
+ * - ex)gic_set_type
+ */
/* Mask all flags except trigger mode */
flags &= IRQ_TYPE_SENSE_MASK;
ret = chip->irq_set_type(&desc->irq_data, flags);
@@ -966,11 +1072,26 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
switch (ret) {
case IRQ_SET_MASK_OK:
case IRQ_SET_MASK_OK_DONE:
+/*
+ * IAMROOT, 2022.10.01:
+ * - trigger bits들을 clear후 flags로 set한다.
+ */
irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK);
irqd_set(&desc->irq_data, flags);
fallthrough;
case IRQ_SET_MASK_OK_NOCOPY:
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - read trriger_type <- irq_data state --+
+ * write trriger_type -> irq_desc state |
+ * clear IRQD_LEVEL -> irq_data state |
+ * clear IRQD_LEVEL -> irq_desc state |
+ * if (IRQ_TYPE_LEVEL_MASK & trriger_type) <-+
+ * write IRQD_LEVEL -> irq_desc state
+ * write IRQD_LEVEL -> irq_data state
+ */
flags = irqd_get_trigger_type(&desc->irq_data);
irq_settings_set_trigger_mask(desc, flags);
irqd_clear(&desc->irq_data, IRQD_LEVEL);
@@ -986,6 +1107,11 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
pr_err("Setting trigger mode %lu for irq %u failed (%pS)\n",
flags, irq_desc_get_irq(desc), chip->irq_set_type);
}
+
+/*
+ * IAMROOT, 2022.10.01:
+ * - unmask 요청이 있었으면 unmask.
+ */
if (unmask)
unmask_irq(desc);
return ret;
diff --git a/lib/cpumask.c b/lib/cpumask.c
index 9c89e3b213ce..2d56bc31bdd2 100644
--- a/lib/cpumask.c
+++ b/lib/cpumask.c
@@ -31,6 +31,10 @@ EXPORT_SYMBOL(cpumask_next);
*
* Returns >= nr_cpu_ids if no further cpus set in both.
*/
+/*
+ * IAMROOT, 2022.10.01:
+ * - 겹치는게 없으면 return >= nr_cpu_ids
+ */
int cpumask_next_and(int n, const struct cpumask *src1p,
const struct cpumask *src2p)
{
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
공지 | [공지] 스터디 정리 노트 공간입니다. | woos | 2016.05.14 | 623 |
165 | [커널 19차] 27 주차 | Min | 2022.11.22 | 82 |
164 | [커널 18차] 78주차 | kkr | 2022.11.19 | 186 |
163 | [커널 19차] 25 ~ 26 주차 | Min | 2022.11.14 | 71 |
162 | [커널 18차] 76-77주차 | kkr | 2022.11.12 | 385 |
161 | [커널 19차] 24주차 | Min | 2022.10.31 | 108 |
160 | [커널 17차] 112주차 | ㅇㅇㅇ | 2022.10.30 | 81 |
159 | [커널 18차] 75주차 | kkr | 2022.10.29 | 40 |
158 | [커널 17차] 107 ~ 111주차 | ㅇㅇㅇ | 2022.10.23 | 71 |
157 | [커널 19차] 22주차 | Min | 2022.10.17 | 76 |
156 | [커널 18차] 73주차 | kkr | 2022.10.15 | 44 |
155 | [커널 18차] 72주차 | kkr | 2022.10.09 | 182 |
» | [커널 18차] 71주차 | kkr | 2022.10.01 | 74 |
153 | [커널 18차] 70주차 | kkr | 2022.09.24 | 76 |
152 | [커널 18차] 69주차 | kkr | 2022.09.22 | 57 |
151 | [커널 17차] 105~106주차 | ㅇㅇㅇ | 2022.09.18 | 50 |
150 | [커널 17차] 104주차 | ㅇㅇㅇ | 2022.09.04 | 87 |
149 | [커널 18차] 67주차 | kkr | 2022.09.03 | 138 |
148 | [커널 17차] 103주차 | ㅇㅇㅇ | 2022.08.28 | 35 |
147 | [커널 18차] 66주차 | kkr | 2022.08.27 | 75 |
146 | [커널 17차] 101~102주차 | ㅇㅇㅇ | 2022.08.21 | 45 |
.