硬中断--数据结构

liaocj 2024-12-03 09:02:56
Categories: Tags:
 struct irqaction {
         irq_handler_t           handler; // request_irq 初始化
         void                    *dev_id;
         void __percpu           *percpu_dev_id;
         struct irqaction        *next;
         irq_handler_t           thread_fn;
         struct task_struct      *thread;
         struct irqaction        *secondary;
         unsigned int            irq;
         unsigned int            flags;
         unsigned long           thread_flags;
         unsigned long           thread_mask;
         const char              *name;
         struct proc_dir_entry   *dir;
 } ____cacheline_internodealigned_in_smp;

struct irq_desc {
        struct irq_common_data  irq_common_data;
        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;

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);

        int             (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
        int             (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);

        int             (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);

        unsigned long   flags;
};


/**
 * struct irq_domain_ops - Methods for irq_domain objects
 * @match: Match an interrupt controller device node to a host, returns
 *         1 on a match
 * match是判断一个指定的 interrupt controller(node 参数)是否和一个irq domain 匹配(d 参数),
 *  如果匹配的话, 返回 1. 实际上, 内核中很少定义这个 callback 函数(!!!),
 * 实际上struct irq_domain中有一个of_node指向了对应的 interrupt controller的device node,
 * 因此, 如果不提供该函数, 那么default 的匹配函数其实就是判断 irq domain的of_node 成员是否等于传入的node 参数.
 *
 * @map: Create or update a mapping between a virtual irq number and a hw
 *       irq number. This is called only once for a given mapping.
 * @unmap: Dispose of such a mapping
 * @xlate: Given a device tree node and interrupt specifier, decode
 *         the hardware irq number and linux irq type value.
 *
 * Functions below are provided by the driver and called whenever a new mapping
 * is created or an old mapping is disposed. The driver can then proceed to
 * whatever internal data structures management is required. It also needs
 * to setup the irq_desc when returning from map().
 *
 *在 __irq_domain_add 中将ops 传递给domain
 */
struct irq_domain_ops {
    int (*match)(struct irq_domain *d, struct device_node *node,
             enum irq_domain_bus_token bus_token);
    int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec,
              enum irq_domain_bus_token bus_token);
    int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
    void (*unmap)(struct irq_domain *d, unsigned int virq);
    int (*xlate)(struct irq_domain *d, struct device_node *node,
             const u32 *intspec, unsigned int intsize,
             unsigned long *out_hwirq, unsigned int *out_type);
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
    /* extended V2 interfaces to support hierarchy irq_domains */
    int (*alloc)(struct irq_domain *d, unsigned int virq,
             unsigned int nr_irqs, void *arg);
    void (*free)(struct irq_domain *d, unsigned int virq,
             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);
    int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
             unsigned long *out_hwirq, unsigned int *out_type);
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
    void (*debug_show)(struct seq_file *m, struct irq_domain *d,
               struct irq_data *irqd, int ind);
#endif
};

struct irq_domain {
    struct list_head link; // 常规操作,所有的 irq_domain 连接到全局链表上
    const char *name;
    const struct irq_domain_ops *ops; // 这个应该是不同的控制器操作函数不同
    void *host_data;
    unsigned int flags;
    unsigned int mapcount; // 管理的中断源数量

    /* Optional data */
    struct fwnode_handle *fwnode; // 从 DTS 或 ACPI 获取的
    enum irq_domain_bus_token bus_token;
    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_size; // 线性映射的大小
    struct radix_tree_root revmap_tree; // 基数树映射的根节点
    struct mutex revmap_mutex;
    struct irq_data __rcu *revmap[]; //  线性映射用到的查找表
};

alt text

图的左侧紫色部分,主要在中断控制器驱动中进行初始化设置,包括各个结构中函数指针的指向等,其中 struct irq_chip 用于对中断控制器的硬件操作,struct irq_domain 与中断控制器对应,完成的工作是硬件中断号到 irq 的映射;
alt text