early_irq_init()

 

IRQ 번호를 관리하는 두 가지 방법

CONFIG_SPARSE_IRQ 커널 옵션을 사용유무에 따라 IRQ 번호를 관리하는 방법이 두 가지로 나뉜다.

  • Sparse IRQ
    • 커널 옵션을 사용하는 경우 필요한 IRQ 번호에 대한 irq_desc 구조체를 동적으로 할당하고 Radix Tree를 사용하여 관리한다.
  • Flat IRQ
    • 커널 옵션을 사용하지 않는 경우 max IRQ 번호만큼 irq_dest 구조체 배열을 컴파일 시 정적으로 할당하여 사용한다.

 

early_irq_init()

1) Sparse IRQ

kernel/irq/irqdesc.c

#ifdef CONFIG_SPARSE_IRQ
int __init early_irq_init(void)
{
        int i, initcnt, node = first_online_node;
        struct irq_desc *desc;

        init_irq_default_affinity();

        /* Let arch update nr_irqs and return the nr of preallocated irqs */
        initcnt = arch_probe_nr_irqs();
        printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt);

        if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))
                nr_irqs = IRQ_BITMAP_BITS;

        if (WARN_ON(initcnt > IRQ_BITMAP_BITS))
                initcnt = IRQ_BITMAP_BITS;

        if (initcnt > nr_irqs)
                nr_irqs = initcnt;

        for (i = 0; i < initcnt; i++) {
                desc = alloc_desc(i, node, NULL);
                set_bit(i, allocated_irqs);
                irq_insert_desc(i, desc);
        }
        return arch_early_irq_init();
}
#endif

irq_desc를 관리할 수 있는 radix 트리를 구성하고 초기화한다.

  • init_irq_default_affinity();
    • 전역 irq_default_affinity 변수에 cpu 비트맵 매스크를 할당하고 모두 1로 설정한다.
  • initcnt = arch_probe_nr_irqs();
    • 사용할 최대 irq 갯수를 알아온다.
  • if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS)) nr_irqs = IRQ_BITMAP_BITS;
    • 전역 nr_irqs가 IRQ_BITMAP_BITS를 초과하지 않도록 조정한다.
      • IRQ_BITMAP_BITS는 CONFIG_SPARSE_IRQ에서 NR_IRQS+8192개이고 그렇지 않은 경우 NR_IRQS이다.
  • if (WARN_ON(initcnt > IRQ_BITMAP_BITS)) initcnt = IRQ_BITMAP_BITS;
    • initcnt가 IRQ_BITMAP_BITS를 초과하지 않도록 조정한다.
  • if (initcnt > nr_irqs) nr_irqs = initcnt;
    • initcnt가 nr_irqs를 초과하는 경우 nr_irqs에 initcnt를 대입한다.
  • for (i = 0; i < initcnt; i++) {
    • initcnt 수 만큼 루프를 돌며
  • desc = alloc_desc(i, node, NULL);
    • irq_desc 구조체를 할당받는다.
  • set_bit(i, allocated_irqs);
    • 전역 allocated_irqs 비트맵의 i번 cpu를 1로 설정한다.
  • irq_insert_desc(i, desc);
    • 전역 irq_desc_tree에 i를 키로 추가하고 할당받은 irq_desc를 가리키게 한다.
  • return arch_early_irq_init();
    • arch_early_irq_init() 함수가 있는 아키텍처 코드를 수행한다.
      • 현재 x86, ia64 아키텍처에서 제공된다.

 

다음 그림은 sparse하게 irq descriptor가 관리되는 모습을 보여준다.

early_irq_init-1

 

2) Flat IRQ

#else /* !CONFIG_SPARSE_IRQ */
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
 [0 ... NR_IRQS-1] = {
 .handle_irq = handle_bad_irq,
 .depth = 1,
 .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
 }
};

int __init early_irq_init(void)
{
        int count, i, node = first_online_node;
        struct irq_desc *desc;

        init_irq_default_affinity();

        printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);

        desc = irq_desc;
        count = ARRAY_SIZE(irq_desc);

        for (i = 0; i < count; i++) {
                desc[i].kstat_irqs = alloc_percpu(unsigned int);
                alloc_masks(&desc[i], GFP_KERNEL, node);
                raw_spin_lock_init(&desc[i].lock);
                lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
                desc_set_defaults(i, &desc[i], node, NULL);
        }
        return arch_early_irq_init();
}
#endif

irq_desc를 관리하는 전역 irq_desc[] 배열을 초기화한다.

  • init_irq_default_affinity();
    • 전역 irq_default_affinity 변수에 cpu 비트맵 매스크를 할당하고 모두 1로 설정한다.
  • for (i = 0; i < count; i++) {
    • count(irq_desc[] 배열의 인덱스 수) 만큼 루프를 돌며
  • desc[i].kstat_irqs = alloc_percpu(unsigned int);
    • irq_desc[i].kstat_irqs에 per-cpu int 타입을 할당한다.
  • alloc_masks(&desc[i], GFP_KERNEL, node);
    • irq descriptor에서 사용하는 cpu 비트맵 마스크를 할당받는다.
  • desc_set_defaults(i, &desc[i], node, NULL);
    • irq descriptor를 초기화한다.
  • return arch_early_irq_init();
    • arch_early_irq_init() 함수가 있는 아키텍처 코드를 수행한다.
      • 현재 x86, ia64 아키텍처에서 제공된다.

 

다음 그림은 flat하게 irq descriptor가 관리되는 모습을 보여준다.

early_irq_init-2

 

init_irq_default_affinity()

kernel/irq/irqdesc.c

static void __init init_irq_default_affinity(void)
{
        alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);
        cpumask_setall(irq_default_affinity);
}

전역 irq_default_affinity 변수에 cpu 비트맵 매스크를 할당하고 모두 1로 설정한다.

 

arch_probe_nr_irqs()

arch/arm/kernel/irq.c

int __init arch_probe_nr_irqs(void)
{
        nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
        return nr_irqs;
}

아키텍처의 머신 디스크립터에서 제공되는 최대 IRQ 갯수를 알아온다. 만일 설정되어 있지 않은 경우 NR_IRQS 값으로 정한다.

 

NR_IRQS

irq 최대 갯수

  • CONFIG_SPARSE_IRQ 커널 옵션 사용 유무에 따라
    • 사용하는 경우
      • NR_IRQ_LEGACY(16) 값을 사용한다.
    • 사용하지 않는 경우
      • mach/irqs.h에서 지정된 값을 사용한다.
      • rpi2:
        • arch/arm/mach-bcm2709/include/mach/irqs.h
        • NR_IRQS = 608 (hard irq(128) + fiq irq(128) + gpio irq(160) + spare irq(64) + free irq(128))

 

alloc_desc()

kernel/irq/irqdesc.c

static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
{
        struct irq_desc *desc;
        gfp_t gfp = GFP_KERNEL;

        desc = kzalloc_node(sizeof(*desc), gfp, node);
        if (!desc)
                return NULL;
        /* allocate based on nr_cpu_ids */
        desc->kstat_irqs = alloc_percpu(unsigned int);
        if (!desc->kstat_irqs)
                goto err_desc;

        if (alloc_masks(desc, gfp, node))
                goto err_kstat;

        raw_spin_lock_init(&desc->lock);
        lockdep_set_class(&desc->lock, &irq_desc_lock_class);

        desc_set_defaults(irq, desc, node, owner);

        return desc;

err_kstat:
        free_percpu(desc->kstat_irqs);
err_desc:
        kfree(desc);
        return NULL;
}

irq_desc 구조체를 할당받은 후 초기화한다.

  • 요청 irq 번호용으로 irq_desc  구조체를 할당 받고
  • 그 멤버 per-cpu 변수 kstat_irqs에 int형으로 per-cpu 할당을 받아 연결한다.
  • irq descriptor에서 사용하는 cpu 비트맵 마스크를 할당받는다.
  • 해당 irq descriptor를 초기화하고 owner 모듈을 지정한다.

 

다음 그림은 하나의 irq descriptor가 할당되고 초기화되는 모습을 보여준다.

alloc_desc-1

 

alloc_masks()

kernel/irq/irqdesc.c

static int alloc_masks(struct irq_desc *desc, gfp_t gfp, int node)
{
        if (!zalloc_cpumask_var_node(&desc->irq_data.affinity, gfp, node))
                return -ENOMEM;

#ifdef CONFIG_GENERIC_PENDING_IRQ
        if (!zalloc_cpumask_var_node(&desc->pending_mask, gfp, node)) {
                free_cpumask_var(desc->irq_data.affinity);
                return -ENOMEM;
        }
#endif  
        return 0;
}

irq descriptor에서 사용하는 cpu 비트맵 마스크를 할당받는다.

  • irq descriptor의 irq_data.affinity에 cpu 비트맵을 할당받는다.
  • 만일 CONFIG_GENERIC_PENDING_IRQ 커널 옵션을 사용하는 경우 irq descriptor의 pending_mask에도 cpu 비트맵을 할당받는다.

 

desc_set_defaults()

kernel/irq/irqdesc.c

static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
                struct module *owner)
{
        int cpu;

        desc->irq_data.irq = irq;
        desc->irq_data.chip = &no_irq_chip;
        desc->irq_data.chip_data = NULL;
        desc->irq_data.handler_data = NULL;
        desc->irq_data.msi_desc = NULL;
        irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS);
        irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
        desc->handle_irq = handle_bad_irq;
        desc->depth = 1;
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;
        desc->name = NULL;
        desc->owner = owner;
        for_each_possible_cpu(cpu)
                *per_cpu_ptr(desc->kstat_irqs, cpu) = 0;
        desc_smp_init(desc, node);
}

해당 irq descriptor를 초기화하고 owner 모듈을 지정한다.

  • irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS);
    • irq descriptor의 status_use_accessors의 모든 비트를 클리어한 후 _IRQ_DEFAULT_INIT_FLAGS 비트들을 설정한다.설정한다.
      • _IRQ_DEFAULT_INIT_FLAGS
        • arm: IRQ_NOPROBE(0x400, bit 10)
        • arm: IRQ_NOREQUEST(0x800, bit 11)
  • irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
    • irq_data의 state_use_accessors에 IRQD_IRQ_DISABLED 비트를 설정한다.
      • IRQD_IRQ_DISABLED
        • arm: 0x10000 (bit 16)
  • desc->handle_irq = handle_bad_irq;
    • handle_irq가 지정되지 않을 때 동작하게 하기 위해 에러 처리용 핸들러 함수인 handle_bad_irq() 함수를 지정해둔다.
  • desc_smp_init(desc, node);
    • irq descriptor에서 사용하는 cpu 비트맵을 irq_default_affinity 비트맵 내용으로 초기화한다.

 

irq_settings_clr_and_set()

kernel/irq/settings.h

static inline void
irq_settings_clr_and_set(struct irq_desc *desc, u32 clr, u32 set)
{       
        desc->status_use_accessors &= ~(clr & _IRQF_MODIFY_MASK);
        desc->status_use_accessors |= (set & _IRQF_MODIFY_MASK);
}

irq descriptor의 status_use_accessors에서 인수 clr로 요청한 비트를 clear하고 인수 set으로 요청한 비트를 설정한다.

 

irqd_set()

kernel/irq/internals.h

static inline void irqd_set(struct irq_data *d, unsigned int mask)
{
        d->state_use_accessors |= mask;
}

irq_data의 state_use_accessors의 인수 mask 비트를 설정한다.

 

desc_smp_init()

kernel/irq/irqdesc.c

static void desc_smp_init(struct irq_desc *desc, int node)
{
        desc->irq_data.node = node;
        cpumask_copy(desc->irq_data.affinity, irq_default_affinity);
#ifdef CONFIG_GENERIC_PENDING_IRQ
        cpumask_clear(desc->pending_mask);
#endif
}

irq descriptor에서 사용하는 cpu 비트맵을 irq_default_affinity 비트맵 내용으로 초기화한다.

  • cpumask_copy(desc->irq_data.affinity, irq_default_affinity);
    • irq descriptor의 irq_data.affinity 비트맵에 irq_default_affinity 비트맵을 복사한다.
  • cpumask_clear(desc->pending_mask);
    • irq descriptor의 pending_mask 비트맵을 모두 clear한다.

 

irq_insert_desc()

kernel/irq/irqdesc.c

static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
{
        radix_tree_insert(&irq_desc_tree, irq, desc);
}

전역 irq_desc_tree에 요청 irq를 키로 추가하고 irq descriptor를 가리키게 한다.

 

구조체

irq_desc 구조체

include/linux/irqdesc.h

/**
 * struct irq_desc - interrupt descriptor
 * @irq_data:           per irq and chip data passed down to chip functions
 * @kstat_irqs:         irq stats per cpu
 * @handle_irq:         highlevel irq-events handler
 * @preflow_handler:    handler called before the flow handler (currently used by sparc)
 * @action:             the irq action chain
 * @status:             status information
 * @core_internal_state__do_not_mess_with_it: core internal status information
 * @depth:              disable-depth, for nested irq_disable() calls
 * @wake_depth:         enable depth, for multiple irq_set_irq_wake() callers
 * @irq_count:          stats field to detect stalled irqs
 * @last_unhandled:     aging timer for unhandled count
 * @irqs_unhandled:     stats field for spurious unhandled interrupts
 * @threads_handled:    stats field for deferred spurious detection of threaded handlers
 * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
 * @lock:               locking for SMP
 * @affinity_hint:      hint to user space for preferred irq affinity
 * @affinity_notify:    context for notification of affinity changes
 * @pending_mask:       pending rebalanced interrupts
 * @threads_oneshot:    bitfield to handle shared oneshot threads
 * @threads_active:     number of irqaction threads currently running
 * @wait_for_threads:   wait queue for sync_irq to wait for threaded handlers
 * @nr_actions:         number of installed actions on this descriptor
 * @no_suspend_depth:   number of irqactions on a irq descriptor with
 *                      IRQF_NO_SUSPEND set
 * @force_resume_depth: number of irqactions on a irq descriptor with
 *                      IRQF_FORCE_RESUME set
 * @dir:                /proc/irq/ procfs entry
 * @name:               flow handler name for /proc/interrupts output
 */
struct irq_desc {
        struct irq_data         irq_data;
        unsigned int __percpu   *kstat_irqs;
        irq_flow_handler_t      handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
        irq_preflow_handler_t   preflow_handler;
#endif
        struct irqaction        *action;        /* IRQ action list */
        unsigned int            status_use_accessors;
        unsigned int            core_internal_state__do_not_mess_with_it;
        unsigned int            depth;          /* nested irq disables */
        unsigned int            wake_depth;     /* nested wake enables */
        unsigned int            irq_count;      /* For detecting broken IRQs */
        unsigned long           last_unhandled; /* Aging timer for unhandled count */
        unsigned int            irqs_unhandled;
        atomic_t                threads_handled;
        int                     threads_handled_last;
        raw_spinlock_t          lock;
        struct cpumask          *percpu_enabled;
#ifdef CONFIG_SMP
        const struct cpumask    *affinity_hint;
        struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
        cpumask_var_t           pending_mask;
#endif
#endif
        unsigned long           threads_oneshot;
        atomic_t                threads_active;
        wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PM_SLEEP
        unsigned int            nr_actions;
        unsigned int            no_suspend_depth;
        unsigned int            cond_suspend_depth;
        unsigned int            force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
        struct proc_dir_entry   *dir;
#endif
        int                     parent_irq;
        struct module           *owner;
        const char              *name;
} ____cacheline_internodealigned_in_smp;

 

irq_data 구조체

include/linux/irq.h

/**
 * struct irq_data - per irq and irq chip data passed down to chip functions
 * @mask:               precomputed bitmask for accessing the chip registers
 * @irq:                interrupt number
 * @hwirq:              hardware interrupt number, local to the interrupt domain
 * @node:               node index useful for balancing
 * @state_use_accessors: status information for irq chip functions.
 *                      Use accessor functions to deal with it
 * @chip:               low level interrupt hardware access
 * @domain:             Interrupt translation domain; responsible for mapping
 *                      between hwirq number and linux irq number.
 * @parent_data:        pointer to parent struct irq_data to support hierarchy
 *                      irq_domain
 * @handler_data:       per-IRQ data for the irq_chip methods
 * @chip_data:          platform-specific per-chip private data for the chip
 *                      methods, to allow shared chip implementations
 * @msi_desc:           MSI descriptor
 * @affinity:           IRQ affinity on SMP
 *
 * The fields here need to overlay the ones in irq_desc until we
 * cleaned up the direct references and switched everything over to
 * irq_data.
 */
struct irq_data {
        u32                     mask;
        unsigned int            irq;
        unsigned long           hwirq;
        unsigned int            node;
        unsigned int            state_use_accessors;
        struct irq_chip         *chip;
        struct irq_domain       *domain;
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY
        struct irq_data         *parent_data;
#endif
        void                    *handler_data;
        void                    *chip_data;
        struct msi_desc         *msi_desc;
        cpumask_var_t           affinity;
};

 

irq_chip 구조체

include/linux/irq.h

/**
 * struct irq_chip - hardware interrupt chip descriptor
 *
 * @name:               name for /proc/interrupts
 * @irq_startup:        start up the interrupt (defaults to ->enable if NULL)
 * @irq_shutdown:       shut down the interrupt (defaults to ->disable if NULL)
 * @irq_enable:         enable the interrupt (defaults to chip->unmask if NULL)
 * @irq_disable:        disable the interrupt
 * @irq_ack:            start of a new interrupt
 * @irq_mask:           mask an interrupt source
 * @irq_mask_ack:       ack and mask an interrupt source
 * @irq_unmask:         unmask an interrupt source
 * @irq_eoi:            end of interrupt
 * @irq_set_affinity:   set the CPU affinity on SMP machines
 * @irq_retrigger:      resend an IRQ to the CPU
 * @irq_set_type:       set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
 * @irq_set_wake:       enable/disable power-management wake-on of an IRQ
 * @irq_bus_lock:       function to lock access to slow bus (i2c) chips
 * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
 * @irq_cpu_online:     configure an interrupt source for a secondary CPU
 * @irq_cpu_offline:    un-configure an interrupt source for a secondary CPU
 * @irq_suspend:        function called from core code on suspend once per chip
 * @irq_resume:         function called from core code on resume once per chip
 * @irq_pm_shutdown:    function called from core code on shutdown once per chip
 * @irq_calc_mask:      Optional function to set irq_data.mask for special cases
 * @irq_print_chip:     optional to print special chip info in show_interrupts
 * @irq_request_resources:      optional to request resources before calling
 *                              any other callback related to this irq
 * @irq_release_resources:      optional to release resources acquired with
 *                              irq_request_resources
 * @irq_compose_msi_msg:        optional to compose message content for MSI
 * @irq_write_msi_msg:  optional to write message content for MSI
 * @flags:              chip specific flags
 */
struct irq_chip {
        const char      *name;
        unsigned int    (*irq_startup)(struct irq_data *data);
        void            (*irq_shutdown)(struct irq_data *data);
        void            (*irq_enable)(struct irq_data *data);
        void            (*irq_disable)(struct irq_data *data);

        void            (*irq_ack)(struct irq_data *data);
        void            (*irq_mask)(struct irq_data *data);
        void            (*irq_mask_ack)(struct irq_data *data);
        void            (*irq_unmask)(struct irq_data *data);
        void            (*irq_eoi)(struct irq_data *data);

        int             (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
        int             (*irq_retrigger)(struct irq_data *data);
        int             (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
        int             (*irq_set_wake)(struct irq_data *data, unsigned int on);

        void            (*irq_bus_lock)(struct irq_data *data);
        void            (*irq_bus_sync_unlock)(struct irq_data *data);

        void            (*irq_cpu_online)(struct irq_data *data);
        void            (*irq_cpu_offline)(struct irq_data *data);

        void            (*irq_suspend)(struct irq_data *data);
        void            (*irq_resume)(struct irq_data *data);
        void            (*irq_pm_shutdown)(struct irq_data *data);

        void            (*irq_calc_mask)(struct irq_data *data);

        void            (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
        int             (*irq_request_resources)(struct irq_data *data);
        void            (*irq_release_resources)(struct irq_data *data);

        void            (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
        void            (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);

        unsigned long   flags;
};

 

irq_domain 구조체

include/linux/irqdomain.h

/**
 * struct irq_domain - Hardware interrupt number translation object
 * @link: Element in global irq_domain list.
 * @name: Name of interrupt domain
 * @ops: pointer to irq_domain methods
 * @host_data: private data pointer for use by owner.  Not touched by irq_domain
 *             core code.
 * @flags: host per irq_domain flags
 *
 * Optional elements
 * @of_node: Pointer to device tree nodes associated with the irq_domain. Used
 *           when decoding device tree interrupt specifiers.
 * @gc: Pointer to a list of generic chips. There is a helper function for
 *      setting up one or more generic chips for interrupt controllers
 *      drivers using the generic chip library which uses this pointer.
 * @parent: Pointer to parent irq_domain to support hierarchy irq_domains
 *
 * Revmap data, used internally by irq_domain
 * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
 *                         support direct mapping
 * @revmap_size: Size of the linear map table @linear_revmap[]
 * @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map
 * @linear_revmap: Linear table of hwirq->virq reverse mappings
 */
struct irq_domain {
        struct list_head link;
        const char *name;
        const struct irq_domain_ops *ops;
        void *host_data;
        unsigned int flags;

        /* Optional data */
        struct device_node *of_node;
        struct irq_domain_chip_generic *gc;
#ifdef  CONFIG_IRQ_DOMAIN_HIERARCHY
        struct irq_domain *parent;
#endif

        /* reverse map data. The linear map gets appended to the irq_domain */
        irq_hw_number_t hwirq_max;
        unsigned int revmap_direct_max_irq;
        unsigned int revmap_size;
        struct radix_tree_root revmap_tree;
        unsigned int linear_revmap[];
};

 

irq_affinity_notify 구조체

include/linux/interrupt.h

/**
 * struct irq_affinity_notify - context for notification of IRQ affinity changes
 * @irq:                Interrupt to which notification applies
 * @kref:               Reference count, for internal use
 * @work:               Work item, for internal use
 * @notify:             Function to be called on change.  This will be
 *                      called in process context.
 * @release:            Function to be called on release.  This will be
 *                      called in process context.  Once registered, the
 *                      structure must only be freed when this function is
 *                      called or later.
 */
struct irq_affinity_notify {
        unsigned int irq;
        struct kref kref;
        struct work_struct work;
        void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask);
        void (*release)(struct kref *ref);
};

 

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.