📄 iosapic.c
字号:
.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)) {#ifdef XEN rte = xmalloc_bytes(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES);#else rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES);#endif 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; } spin_lock_irqsave(&irq_descp(vector)->lock, flags); spin_lock(&iosapic_lock); { if (gsi_to_vector(gsi) > 0) { if (list_empty(&iosapic_intr_info[vector].rtes)) free_irq_vector(vector); spin_unlock(&iosapic_lock); spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); goto again; } dest = get_target_cpu(gsi, vector); err = register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger); if (err < 0) { spin_unlock(&iosapic_lock); spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); return err; } /* * If the vector is shared and already unmasked for * other interrupt sources, don't mask it. */ low32 = iosapic_intr_info[vector].low32; if (vector_is_shared(vector) && !(low32 & IOSAPIC_MASK)) mask = 0; set_rte(gsi, vector, dest, mask); } spin_unlock(&iosapic_lock); spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), cpu_logical_id(dest), dest, vector); return vector;}voidiosapic_unregister_intr (unsigned int gsi){ unsigned long flags; int irq, vector, index; irq_desc_t *idesc; u32 low32; unsigned long trigger, polarity; unsigned int dest; struct iosapic_rte_info *rte; /* * If the irq associated with the gsi is not found, * iosapic_unregister_intr() is unbalanced. We need to check * this again after getting locks. */ irq = gsi_to_irq(gsi); if (irq < 0) { printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); WARN_ON(1); return; } vector = irq_to_vector(irq); idesc = irq_descp(irq); spin_lock_irqsave(&idesc->lock, flags); spin_lock(&iosapic_lock); { if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) { printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); WARN_ON(1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -