irq.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,229 行 · 第 1/2 页
C
1,229 行
retval = setup_irq(irq, action); if (retval) kfree(action); return retval;}EXPORT_SYMBOL(request_irq);/** * free_irq - free an interrupt * @irq: Interrupt line to free * @dev_id: Device identity to free * * Remove an interrupt handler. The handler is removed and if the * interrupt line is no longer in use by any driver it is disabled. * On a shared IRQ the caller must ensure the interrupt is disabled * on the card it drives before calling this function. The function * does not return until any executing interrupts for this IRQ * have completed. * * This function must not be called from interrupt context. */void free_irq(unsigned int irq, void *dev_id){ irq_desc_t *desc; struct irqaction **p; unsigned long flags; if (irq >= NR_IRQS) return; desc = irq_descp(irq); spin_lock_irqsave(&desc->lock,flags); p = &desc->action; for (;;) { struct irqaction * action = *p; if (action) { struct irqaction **pp = p; p = &action->next; if (action->dev_id != dev_id) continue; /* Found it - now remove it from the list of entries */ *pp = action->next; if (!desc->action) { desc->status |= IRQ_DISABLED; desc->handler->shutdown(irq); } spin_unlock_irqrestore(&desc->lock,flags); /* Wait to make sure it's not being used on another CPU */ synchronize_irq(irq); kfree(action); return; } printk(KERN_ERR "Trying to free free IRQ%d\n",irq); spin_unlock_irqrestore(&desc->lock,flags); return; }}EXPORT_SYMBOL(free_irq);/* * IRQ autodetection code.. * * This depends on the fact that any interrupt that * comes in on to an unassigned handler will get stuck * with "IRQ_WAITING" cleared and the interrupt * disabled. */static DECLARE_MUTEX(probe_sem);/** * probe_irq_on - begin an interrupt autodetect * * Commence probing for an interrupt. The interrupts are scanned * and a mask of potential interrupt lines is returned. * */unsigned long probe_irq_on(void){ unsigned int i; irq_desc_t *desc; unsigned long val; unsigned long delay; down(&probe_sem); /* * something may have generated an irq long ago and we want to * flush such a longstanding irq before considering it as spurious. */ for (i = NR_IRQS-1; i > 0; i--) { desc = irq_descp(i); spin_lock_irq(&desc->lock); if (!desc->action) desc->handler->startup(i); spin_unlock_irq(&desc->lock); } /* Wait for longstanding interrupts to trigger. */ for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) /* about 20ms delay */ barrier(); /* * enable any unassigned irqs * (we must startup again here because if a longstanding irq * happened in the previous stage, it may have masked itself) */ for (i = NR_IRQS-1; i > 0; i--) { desc = irq_descp(i); spin_lock_irq(&desc->lock); if (!desc->action) { desc->status |= IRQ_AUTODETECT | IRQ_WAITING; if (desc->handler->startup(i)) desc->status |= IRQ_PENDING; } spin_unlock_irq(&desc->lock); } /* * Wait for spurious interrupts to trigger */ for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) /* about 100ms delay */ barrier(); /* * Now filter out any obviously spurious interrupts */ val = 0; for (i = 0; i < NR_IRQS; i++) { irq_desc_t *desc = irq_descp(i); unsigned int status; spin_lock_irq(&desc->lock); status = desc->status; if (status & IRQ_AUTODETECT) { /* It triggered already - consider it spurious. */ if (!(status & IRQ_WAITING)) { desc->status = status & ~IRQ_AUTODETECT; desc->handler->shutdown(i); } else if (i < 32) val |= 1 << i; } spin_unlock_irq(&desc->lock); } return val;}EXPORT_SYMBOL(probe_irq_on);/** * probe_irq_mask - scan a bitmap of interrupt lines * @val: mask of interrupts to consider * * Scan the ISA bus interrupt lines and return a bitmap of * active interrupts. The interrupt probe logic state is then * returned to its previous value. * * Note: we need to scan all the irq's even though we will * only return ISA irq numbers - just so that we reset them * all to a known state. */unsigned int probe_irq_mask(unsigned long val){ int i; unsigned int mask; mask = 0; for (i = 0; i < 16; i++) { irq_desc_t *desc = irq_descp(i); unsigned int status; spin_lock_irq(&desc->lock); status = desc->status; if (status & IRQ_AUTODETECT) { if (!(status & IRQ_WAITING)) mask |= 1 << i; desc->status = status & ~IRQ_AUTODETECT; desc->handler->shutdown(i); } spin_unlock_irq(&desc->lock); } up(&probe_sem); return mask & val;}EXPORT_SYMBOL(probe_irq_mask);/** * probe_irq_off - end an interrupt autodetect * @val: mask of potential interrupts (unused) * * Scans the unused interrupt lines and returns the line which * appears to have triggered the interrupt. If no interrupt was * found then zero is returned. If more than one interrupt is * found then minus the first candidate is returned to indicate * their is doubt. * * The interrupt probe logic state is returned to its previous * value. * * BUGS: When used in a module (which arguably shouldn't happen) * nothing prevents two IRQ probe callers from overlapping. The * results of this are non-optimal. */int probe_irq_off(unsigned long val){ int i, irq_found, nr_irqs; nr_irqs = 0; irq_found = 0; for (i = 0; i < NR_IRQS; i++) { irq_desc_t *desc = irq_descp(i); unsigned int status; spin_lock_irq(&desc->lock); status = desc->status; if (status & IRQ_AUTODETECT) { if (!(status & IRQ_WAITING)) { if (!nr_irqs) irq_found = i; nr_irqs++; } desc->status = status & ~IRQ_AUTODETECT; desc->handler->shutdown(i); } spin_unlock_irq(&desc->lock); } up(&probe_sem); if (nr_irqs > 1) irq_found = -irq_found; return irq_found;}EXPORT_SYMBOL(probe_irq_off);int setup_irq(unsigned int irq, struct irqaction * new){ int shared = 0; unsigned long flags; struct irqaction *old, **p; irq_desc_t *desc = irq_descp(irq); if (desc->handler == &no_irq_type) return -ENOSYS; /* * Some drivers like serial.c use request_irq() heavily, * so we have to be careful not to interfere with a * running system. */ if (new->flags & SA_SAMPLE_RANDOM) { /* * This function might sleep, we want to call it first, * outside of the atomic block. * Yes, this might clear the entropy pool if the wrong * driver is attempted to be loaded, without actually * installing a new handler, but is this really a problem, * only the sysadmin is able to do this. */ rand_initialize_irq(irq); } if (new->flags & SA_PERCPU_IRQ) { desc->status |= IRQ_PER_CPU; desc->handler = &irq_type_ia64_lsapic; } /* * The following block of code has to be executed atomically */ spin_lock_irqsave(&desc->lock,flags); p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { spin_unlock_irqrestore(&desc->lock,flags); return -EBUSY; } /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = new; if (!shared) { desc->depth = 0; desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS); desc->handler->startup(irq); } spin_unlock_irqrestore(&desc->lock,flags); register_irq_proc(irq); return 0;}static struct proc_dir_entry * root_irq_dir;static struct proc_dir_entry * irq_dir [NR_IRQS];#ifdef CONFIG_SMPstatic struct proc_dir_entry * smp_affinity_entry [NR_IRQS];static cpumask_t irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL };static char irq_redir [NR_IRQS]; // = { [0 ... NR_IRQS-1] = 1 };void set_irq_affinity_info (unsigned int irq, int hwid, int redir){ cpumask_t mask = CPU_MASK_NONE; cpu_set(cpu_logical_id(hwid), mask); if (irq < NR_IRQS) { irq_affinity[irq] = mask; irq_redir[irq] = (char) (redir & 0xff); }}static int irq_affinity_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data){ int len = sprintf(page, "%s", irq_redir[(long)data] ? "r " : ""); len += cpumask_scnprintf(page+len, count, irq_affinity[(long)data]); if (count - len < 2) return -EINVAL; len += sprintf(page + len, "\n"); return len;}static int irq_affinity_write_proc (struct file *file, const char *buffer, unsigned long count, void *data){ unsigned int irq = (unsigned long) data; int full_count = count, err; cpumask_t new_value, tmp;# define R_PREFIX_LEN 16 char rbuf[R_PREFIX_LEN]; int rlen; int prelen; irq_desc_t *desc = irq_descp(irq); unsigned long flags; int redir = 0; if (!desc->handler->set_affinity) return -EIO; /* * If string being written starts with a prefix of 'r' or 'R' * and some limited number of spaces, set IA64_IRQ_REDIRECTED. * If more than (R_PREFIX_LEN - 2) spaces are passed, they won't * all be trimmed as part of prelen, the untrimmed spaces will * cause the hex parsing to fail, and this write() syscall will * fail with EINVAL. */ if (!count) return -EINVAL; rlen = min(sizeof(rbuf)-1, count); if (copy_from_user(rbuf, buffer, rlen)) return -EFAULT; rbuf[rlen] = 0; prelen = 0; if (tolower(*rbuf) == 'r') { prelen = strspn(rbuf, "Rr "); redir++; } err = cpumask_parse(buffer+prelen, count-prelen, new_value); if (err) return err; /* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ cpus_and(tmp, new_value, cpu_online_map); if (cpus_empty(tmp)) return -EINVAL; spin_lock_irqsave(&desc->lock, flags); pending_irq_cpumask[irq] = new_value; if (redir) set_bit(irq, pending_irq_redir); else clear_bit(irq, pending_irq_redir); spin_unlock_irqrestore(&desc->lock, flags); return full_count;}void move_irq(int irq){ /* note - we hold desc->lock */ cpumask_t tmp; irq_desc_t *desc = irq_descp(irq); int redir = test_bit(irq, pending_irq_redir); if (!cpus_empty(pending_irq_cpumask[irq])) { cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); if (unlikely(!cpus_empty(tmp))) { desc->handler->set_affinity(irq | (redir ? IA64_IRQ_REDIRECTED : 0), pending_irq_cpumask[irq]); } cpus_clear(pending_irq_cpumask[irq]); }}#endif /* CONFIG_SMP */#ifdef CONFIG_HOTPLUG_CPUunsigned int vectors_in_migration[NR_IRQS];/* * Since cpu_online_map is already updated, we just need to check for * affinity that has zeros */static void migrate_irqs(void){ cpumask_t mask; irq_desc_t *desc; int irq, new_cpu; for (irq=0; irq < NR_IRQS; irq++) { desc = irq_descp(irq); /* * No handling for now. * TBD: Implement a disable function so we can now * tell CPU not to respond to these local intr sources. * such as ITV,CPEI,MCA etc. */ if (desc->status == IRQ_PER_CPU) continue; cpus_and(mask, irq_affinity[irq], cpu_online_map); if (any_online_cpu(mask) == NR_CPUS) { /* * Save it for phase 2 processing */ vectors_in_migration[irq] = irq; new_cpu = any_online_cpu(cpu_online_map); mask = cpumask_of_cpu(new_cpu); /* * Al three are essential, currently WARN_ON.. maybe panic? */ if (desc->handler && desc->handler->disable && desc->handler->enable && desc->handler->set_affinity) { desc->handler->disable(irq); desc->handler->set_affinity(irq, mask); desc->handler->enable(irq); } else { WARN_ON((!(desc->handler) || !(desc->handler->disable) || !(desc->handler->enable) || !(desc->handler->set_affinity))); } } }}void fixup_irqs(void){ unsigned int irq; extern void ia64_process_pending_intr(void); ia64_set_itv(1<<16); /* * Phase 1: Locate irq's bound to this cpu and * relocate them for cpu removal. */ migrate_irqs(); /* * Phase 2: Perform interrupt processing for all entries reported in * local APIC. */ ia64_process_pending_intr(); /* * Phase 3: Now handle any interrupts not captured in local APIC. * This is to account for cases that device interrupted during the time the * rte was being disabled and re-programmed. */ for (irq=0; irq < NR_IRQS; irq++) { if (vectors_in_migration[irq]) { vectors_in_migration[irq]=0; do_IRQ(irq, NULL); } } /* * Now let processor die. We do irq disable and max_xtp() to * ensure there is no more interrupts routed to this processor. * But the local timer interrupt can have 1 pending which we * take care in timer_interrupt(). */ max_xtp(); local_irq_disable();}#endifstatic int prof_cpu_mask_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data){ int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); if (count - len < 2) return -EINVAL; len += sprintf(page + len, "\n"); return len;}static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, unsigned long count, void *data){ cpumask_t *mask = (cpumask_t *)data; unsigned long full_count = count, err; cpumask_t new_value; err = cpumask_parse(buffer, count, new_value); if (err) return err; *mask = new_value; return full_count;}#define MAX_NAMELEN 10static void register_irq_proc (unsigned int irq){ char name [MAX_NAMELEN]; if (!root_irq_dir || (irq_descp(irq)->handler == &no_irq_type) || irq_dir[irq]) return; memset(name, 0, MAX_NAMELEN); sprintf(name, "%d", irq); /* create /proc/irq/1234 */ irq_dir[irq] = proc_mkdir(name, root_irq_dir);#ifdef CONFIG_SMP { struct proc_dir_entry *entry; /* create /proc/irq/1234/smp_affinity */ entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); if (entry) { entry->nlink = 1; entry->data = (void *)(long)irq; entry->read_proc = irq_affinity_read_proc; entry->write_proc = irq_affinity_write_proc; } smp_affinity_entry[irq] = entry; }#endif}cpumask_t prof_cpu_mask = CPU_MASK_ALL;void init_irq_proc (void){ struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); if (!entry) return; entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; entry->read_proc = prof_cpu_mask_read_proc; entry->write_proc = prof_cpu_mask_write_proc; /* * Create entries for all existing IRQs. */ for (i = 0; i < NR_IRQS; i++) { if (irq_descp(i)->handler == &no_irq_type) continue; register_irq_proc(i); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?