📄 iosapic.c
字号:
*/static unsigned intiosapic_startup_level_irq (unsigned int irq){ unmask_irq(irq); return 0;}static voidiosapic_end_level_irq (unsigned int irq){ ia64_vector vec = irq_to_vector(irq); struct iosapic_rte_info *rte; move_irq(irq); list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) iosapic_eoi(rte->addr, vec);}#define iosapic_shutdown_level_irq mask_irq#define iosapic_enable_level_irq unmask_irq#define iosapic_disable_level_irq mask_irq#define iosapic_ack_level_irq nopstruct hw_interrupt_type irq_type_iosapic_level = { .typename = "IO-SAPIC-level", .startup = iosapic_startup_level_irq, .shutdown = iosapic_shutdown_level_irq, .enable = iosapic_enable_level_irq, .disable = iosapic_disable_level_irq, .ack = iosapic_ack_level_irq, .end = iosapic_end_level_irq, .set_affinity = iosapic_set_affinity};/* * Handlers for edge-triggered interrupts. */static unsigned intiosapic_startup_edge_irq (unsigned int irq){ unmask_irq(irq); /* * IOSAPIC simply drops interrupts pended while the * corresponding pin was masked, so we can't know if an * interrupt is pending already. Let's hope not... */ return 0;}static voidiosapic_ack_edge_irq (unsigned int irq){ irq_desc_t *idesc = irq_descp(irq); move_irq(irq); /* * Once we have recorded IRQ_PENDING already, we can mask the * interrupt for real. This prevents IRQ storms from unhandled * devices. */ if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED)) mask_irq(irq);}#define iosapic_enable_edge_irq unmask_irq#define iosapic_disable_edge_irq nop#define iosapic_end_edge_irq nopstruct hw_interrupt_type irq_type_iosapic_edge = { .typename = "IO-SAPIC-edge", .startup = iosapic_startup_edge_irq, .shutdown = iosapic_disable_edge_irq, .enable = iosapic_enable_edge_irq, .disable = iosapic_disable_edge_irq, .ack = iosapic_ack_edge_irq, .end = iosapic_end_edge_irq, .set_affinity = iosapic_set_affinity};unsigned intiosapic_version (char __iomem *addr){ /* * IOSAPIC Version Register return 32 bit structure like: * { * unsigned int version : 8; * unsigned int reserved1 : 8; * unsigned int max_redir : 8; * unsigned int reserved2 : 8; * } */ return iosapic_read(addr, IOSAPIC_VERSION);}static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol){ int i, vector = -1, min_count = -1; struct iosapic_intr_info *info; /* * shared vectors for edge-triggered interrupts are not * supported yet */ if (trigger == IOSAPIC_EDGE) return -1; for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) { info = &iosapic_intr_info[i]; if (info->trigger == trigger && info->polarity == pol && (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) { if (min_count == -1 || info->count < min_count) { vector = i; min_count = info->count; } } } return vector;}/* * if the given vector is already owned by other, * assign a new vector for the other and make the vector available */static void __initiosapic_reassign_vector (int vector){ int new_vector; if (!list_empty(&iosapic_intr_info[vector].rtes)) { new_vector = assign_irq_vector(AUTO_ASSIGN); if (new_vector < 0) panic("%s: out of interrupt vectors!\n", __FUNCTION__); printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], sizeof(struct iosapic_intr_info)); INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes); list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes); memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); iosapic_intr_info[vector].low32 = IOSAPIC_MASK; INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); }}static struct iosapic_rte_info *iosapic_alloc_rte (void){ int i; struct iosapic_rte_info *rte; int preallocated = 0; if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) { rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES); if (!rte) return NULL; for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++) list_add(&rte->rte_list, &free_rte_list); } if (!list_empty(&free_rte_list)) { rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list); list_del(&rte->rte_list); preallocated++; } else { rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC); if (!rte) return NULL; } memset(rte, 0, sizeof(struct iosapic_rte_info)); if (preallocated) rte->flags |= RTE_PREALLOCATED; return rte;}static void iosapic_free_rte (struct iosapic_rte_info *rte){ if (rte->flags & RTE_PREALLOCATED) list_add_tail(&rte->rte_list, &free_rte_list); else kfree(rte);}static inline int vector_is_shared (int vector){ return (iosapic_intr_info[vector].count > 1);}static intregister_intr (unsigned int gsi, int vector, unsigned char delivery, unsigned long polarity, unsigned long trigger){ irq_desc_t *idesc; struct hw_interrupt_type *irq_type; int rte_index; int index; unsigned long gsi_base; void __iomem *iosapic_address; struct iosapic_rte_info *rte; index = find_iosapic(gsi); if (index < 0) { printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi); return -ENODEV; } iosapic_address = iosapic_lists[index].addr; gsi_base = iosapic_lists[index].gsi_base; rte = gsi_vector_to_rte(gsi, vector); if (!rte) { rte = iosapic_alloc_rte(); if (!rte) { printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__); return -ENOMEM; } rte_index = gsi - gsi_base; rte->rte_index = rte_index; rte->addr = iosapic_address; rte->gsi_base = gsi_base; rte->refcnt++; list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rtes); iosapic_intr_info[vector].count++; iosapic_lists[index].rtes_inuse++; } else if (vector_is_shared(vector)) { struct iosapic_intr_info *info = &iosapic_intr_info[vector]; if (info->trigger != trigger || info->polarity != polarity) { printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__); return -EINVAL; } } iosapic_intr_info[vector].polarity = polarity; iosapic_intr_info[vector].dmode = delivery; iosapic_intr_info[vector].trigger = trigger; if (trigger == IOSAPIC_EDGE) irq_type = &irq_type_iosapic_edge; else irq_type = &irq_type_iosapic_level; idesc = irq_descp(vector); if (idesc->handler != irq_type) { if (idesc->handler != &no_irq_type) printk(KERN_WARNING "%s: changing vector %d from %s to %s\n", __FUNCTION__, vector, idesc->handler->typename, irq_type->typename); idesc->handler = irq_type; } return 0;}static unsigned intget_target_cpu (unsigned int gsi, int vector){#ifdef CONFIG_SMP static int cpu = -1; /* * In case of vector shared by multiple RTEs, all RTEs that * share the vector need to use the same destination CPU. */ if (!list_empty(&iosapic_intr_info[vector].rtes)) return iosapic_intr_info[vector].dest; /* * If the platform supports redirection via XTP, let it * distribute interrupts. */ if (smp_int_redirect & SMP_IRQ_REDIRECTION) return cpu_physical_id(smp_processor_id()); /* * Some interrupts (ACPI SCI, for instance) are registered * before the BSP is marked as online. */ if (!cpu_online(smp_processor_id())) return cpu_physical_id(smp_processor_id());#ifdef CONFIG_NUMA { int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0; cpumask_t cpu_mask; iosapic_index = find_iosapic(gsi); if (iosapic_index < 0 || iosapic_lists[iosapic_index].node == MAX_NUMNODES) goto skip_numa_setup; cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node); for_each_cpu_mask(numa_cpu, cpu_mask) { if (!cpu_online(numa_cpu)) cpu_clear(numa_cpu, cpu_mask); } num_cpus = cpus_weight(cpu_mask); if (!num_cpus) goto skip_numa_setup; /* Use vector assigment to distribute across cpus in node */ cpu_index = vector % num_cpus; for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++) numa_cpu = next_cpu(numa_cpu, cpu_mask); if (numa_cpu != NR_CPUS) return cpu_physical_id(numa_cpu); }skip_numa_setup:#endif /* * Otherwise, round-robin interrupt vectors across all the * processors. (It'd be nice if we could be smarter in the * case of NUMA.) */ do { if (++cpu >= NR_CPUS) cpu = 0; } while (!cpu_online(cpu)); return cpu_physical_id(cpu);#else return cpu_physical_id(smp_processor_id());#endif}/* * ACPI can describe IOSAPIC interrupts via static tables and namespace * methods. This provides an interface to register those interrupts and * program the IOSAPIC RTE. */intiosapic_register_intr (unsigned int gsi, unsigned long polarity, unsigned long trigger){ int vector, mask = 1, err; unsigned int dest; unsigned long flags; struct iosapic_rte_info *rte; u32 low32;again: /* * If this GSI has already been registered (i.e., it's a * shared interrupt, or we lost a race to register it), * don't touch the RTE. */ spin_lock_irqsave(&iosapic_lock, flags); { vector = gsi_to_vector(gsi); if (vector > 0) { rte = gsi_vector_to_rte(gsi, vector); rte->refcnt++; spin_unlock_irqrestore(&iosapic_lock, flags); return vector; } } spin_unlock_irqrestore(&iosapic_lock, flags); /* If vector is running out, we try to find a sharable vector */ vector = assign_irq_vector(AUTO_ASSIGN); if (vector < 0) { vector = iosapic_find_sharable_vector(trigger, polarity); if (vector < 0) return -ENOSPC; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -