⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 io_apic.c

📁 自己根据lkd和情境分析
💻 C
📖 第 1 页 / 共 3 页
字号:
		spin_unlock_irqrestore(&ioapic_lock, flags);		/*		 * Sanity check		 */		spin_lock_irqsave(&ioapic_lock, flags);		*(int *)&reg_00 = io_apic_read(apic, 0);		spin_unlock_irqrestore(&ioapic_lock, flags);		if (reg_00.ID != mp_ioapics[apic].mpc_apicid)			panic("could not set ID!\n");		else			printk(" ok.\n");	}}/* * There is a nasty bug in some older SMP boards, their mptable lies * about the timer IRQ. We do the following to work around the situation: * *	- timer IRQ defaults to IO-APIC IRQ *	- if this function detects that timer IRQs are defunct, then we fall *	  back to ISA timer IRQs */static int __init timer_irq_works(void){	unsigned int t1 = jiffies;	sti();	/* Let ten ticks pass... */	mdelay((10 * 1000) / HZ);	/*	 * Expect a few ticks at least, to be sure some possible	 * glue logic does not lock up after one or two first	 * ticks in a non-ExtINT mode.  Also the local APIC	 * might have cached one ExtINT interrupt.  Finally, at	 * least one tick may be lost due to delays.	 */	if (jiffies - t1 > 4)		return 1;	return 0;}/* * In the SMP+IOAPIC case it might happen that there are an unspecified * number of pending IRQ events unhandled. These cases are very rare, * so we 'resend' these IRQs via IPIs, to the same CPU. It's much * better to do it this way as thus we do not have to be aware of * 'pending' interrupts in the IRQ path, except at this point. *//* * Edge triggered needs to resend any interrupt * that was delayed but this is now handled in the device * independent code. */#define enable_edge_ioapic_irq unmask_IO_APIC_irqstatic void disable_edge_ioapic_irq (unsigned int irq) { /* nothing */ }/* * Starting up a edge-triggered IO-APIC interrupt is * nasty - we need to make sure that we get the edge. * If it is already asserted for some reason, we need * return 1 to indicate that is was pending. * * This is not complete - we should be able to fake * an edge even if it isn't on the 8259A... */static unsigned int startup_edge_ioapic_irq(unsigned int irq){	int was_pending = 0;	unsigned long flags;	spin_lock_irqsave(&ioapic_lock, flags);	if (irq < 16) {		disable_8259A_irq(irq);		if (i8259A_irq_pending(irq))			was_pending = 1;	}	__unmask_IO_APIC_irq(irq);	spin_unlock_irqrestore(&ioapic_lock, flags);	return was_pending;}#define shutdown_edge_ioapic_irq	disable_edge_ioapic_irq/* * Once we have recorded IRQ_PENDING already, we can mask the * interrupt for real. This prevents IRQ storms from unhandled * devices. */static void ack_edge_ioapic_irq(unsigned int irq){	if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED))					== (IRQ_PENDING | IRQ_DISABLED))		mask_IO_APIC_irq(irq);	ack_APIC_irq();}static void end_edge_ioapic_irq (unsigned int i) { /* nothing */ }/* * Level triggered interrupts can just be masked, * and shutting down and starting up the interrupt * is the same as enabling and disabling them -- except * with a startup need to return a "was pending" value. * * Level triggered interrupts are special because we * do not touch any IO-APIC register while handling * them. We ack the APIC in the end-IRQ handler, not * in the start-IRQ-handler. Protection against reentrance * from the same interrupt is still provided, both by the * generic IRQ layer and by the fact that an unacked local * APIC does not accept IRQs. */static unsigned int startup_level_ioapic_irq (unsigned int irq){	unmask_IO_APIC_irq(irq);	return 0; /* don't check for pending */}#define shutdown_level_ioapic_irq	mask_IO_APIC_irq#define enable_level_ioapic_irq		unmask_IO_APIC_irq#define disable_level_ioapic_irq	mask_IO_APIC_irqstatic void end_level_ioapic_irq (unsigned int irq){	unsigned long v;	int i;/* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various * chipsets).  Under certain conditions a level-triggered interrupt is * erroneously delivered as edge-triggered one but the respective IRR * bit gets set nevertheless.  As a result the I/O unit expects an EOI * message but it will never arrive and further interrupts are blocked * from the source.  The exact reason is so far unknown, but the * phenomenon was observed when two consecutive interrupt requests * from a given source get delivered to the same CPU and the source is * temporarily disabled in between. * * A workaround is to simulate an EOI message manually.  We achieve it * by setting the trigger mode to edge and then to level when the edge * trigger mode gets detected in the TMR of a local APIC for a * level-triggered interrupt.  We mask the source for the time of the * operation to prevent an edge-triggered interrupt escaping meanwhile. * The idea is from Manfred Spraul.  --macro */	i = IO_APIC_VECTOR(irq);	v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1));	ack_APIC_irq();	if (!(v & (1 << (i & 0x1f)))) {#ifdef APIC_LOCKUP_DEBUG		struct irq_pin_list *entry;#endif#ifdef APIC_MISMATCH_DEBUG		atomic_inc(&irq_mis_count);#endif		spin_lock(&ioapic_lock);		__mask_and_edge_IO_APIC_irq(irq);#ifdef APIC_LOCKUP_DEBUG		for (entry = irq_2_pin + irq;;) {			unsigned int reg;			if (entry->pin == -1)				break;			reg = io_apic_read(entry->apic, 0x10 + entry->pin * 2);			if (reg & 0x00004000)				printk(KERN_CRIT "Aieee!!!  Remote IRR"					" still set after unlock!\n");			if (!entry->next)				break;			entry = irq_2_pin + entry->next;		}#endif		__unmask_and_level_IO_APIC_irq(irq);		spin_unlock(&ioapic_lock);	}}static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ }static void set_ioapic_affinity (unsigned int irq, unsigned long mask){	unsigned long flags;	/*	 * Only the first 8 bits are valid.	 */	mask = mask << 24;	spin_lock_irqsave(&ioapic_lock, flags);	__DO_ACTION(1, = mask, )	spin_unlock_irqrestore(&ioapic_lock, flags);}/* * Level and edge triggered IO-APIC interrupts need different handling, * so we use two separate IRQ descriptors. Edge triggered IRQs can be * handled with the level-triggered descriptor, but that one has slightly * more overhead. Level-triggered interrupts cannot be handled with the * edge-triggered handler, without risking IRQ storms and other ugly * races. */static struct hw_interrupt_type ioapic_edge_irq_type = {	"IO-APIC-edge",	startup_edge_ioapic_irq,	shutdown_edge_ioapic_irq,	enable_edge_ioapic_irq,	disable_edge_ioapic_irq,	ack_edge_ioapic_irq,	end_edge_ioapic_irq,	set_ioapic_affinity,};static struct hw_interrupt_type ioapic_level_irq_type = {	"IO-APIC-level",	startup_level_ioapic_irq,	shutdown_level_ioapic_irq,	enable_level_ioapic_irq,	disable_level_ioapic_irq,	mask_and_ack_level_ioapic_irq,	end_level_ioapic_irq,	set_ioapic_affinity,};static inline void init_IO_APIC_traps(void){	int irq;	/*	 * NOTE! The local APIC isn't very good at handling	 * multiple interrupts at the same interrupt level.	 * As the interrupt level is determined by taking the	 * vector number and shifting that right by 4, we	 * want to spread these out a bit so that they don't	 * all fall in the same interrupt level.	 *	 * Also, we've got to be careful not to trash gate	 * 0x80, because int 0x80 is hm, kind of importantish. ;)	 */	for (irq = 0; irq < NR_IRQS ; irq++) {		if (IO_APIC_IRQ(irq) && !IO_APIC_VECTOR(irq)) {			/*			 * Hmm.. We don't have an entry for this,			 * so default to an old-fashioned 8259			 * interrupt if we can..			 */			if (irq < 16)				make_8259A_irq(irq);			else				/* Strange. Oh, well.. */				irq_desc[irq].handler = &no_irq_type;		}	}}static void enable_lapic_irq (unsigned int irq){	unsigned long v;	v = apic_read(APIC_LVT0);	apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED);}static void disable_lapic_irq (unsigned int irq){	unsigned long v;	v = apic_read(APIC_LVT0);	apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);}static void ack_lapic_irq (unsigned int irq){	ack_APIC_irq();}static void end_lapic_irq (unsigned int i) { /* nothing */ }static struct hw_interrupt_type lapic_irq_type = {	"local-APIC-edge",	NULL, /* startup_irq() not used for IRQ0 */	NULL, /* shutdown_irq() not used for IRQ0 */	enable_lapic_irq,	disable_lapic_irq,	ack_lapic_irq,	end_lapic_irq};static void enable_NMI_through_LVT0 (void * dummy){	unsigned int v, ver;	ver = apic_read(APIC_LVR);	ver = GET_APIC_VERSION(ver);	v = APIC_DM_NMI;			/* unmask and set to NMI */	if (!APIC_INTEGRATED(ver))		/* 82489DX */		v |= APIC_LVT_LEVEL_TRIGGER;	apic_write_around(APIC_LVT0, v);}static void setup_nmi (void){	/* 	 * Dirty trick to enable the NMI watchdog ...	 * We put the 8259A master into AEOI mode and	 * unmask on all local APICs LVT0 as NMI.	 *	 * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire')	 * is from Maciej W. Rozycki - so we do not have to EOI from	 * the NMI handler or the timer interrupt.	 */ 	printk(KERN_INFO "activating NMI Watchdog ...");	smp_call_function(enable_NMI_through_LVT0, NULL, 1, 1);	enable_NMI_through_LVT0(NULL);	printk(" done.\n");}/* * This looks a bit hackish but it's about the only one way of sending * a few INTA cycles to 8259As and any associated glue logic.  ICR does * not support the ExtINT mode, unfortunately.  We need to send these * cycles as some i82489DX-based boards have glue logic that keeps the * 8259A interrupt line asserted until INTA.  --macro */static inline void unlock_ExtINT_logic(void){	int pin, i;	struct IO_APIC_route_entry entry0, entry1;	unsigned char save_control, save_freq_select;	unsigned long flags;	pin = find_isa_irq_pin(8, mp_INT);	if (pin == -1)		return;	spin_lock_irqsave(&ioapic_lock, flags);	*(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin);	*(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin);	spin_unlock_irqrestore(&ioapic_lock, flags);	clear_IO_APIC_pin(0, pin);	memset(&entry1, 0, sizeof(entry1));	entry1.dest_mode = 0;			/* physical delivery */	entry1.mask = 0;			/* unmask IRQ now */	entry1.dest.physical.physical_dest = hard_smp_processor_id();	entry1.delivery_mode = dest_ExtINT;	entry1.polarity = entry0.polarity;	entry1.trigger = 0;	entry1.vector = 0;	spin_lock_irqsave(&ioapic_lock, flags);	io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1));	io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0));	spin_unlock_irqrestore(&ioapic_lock, flags);	save_control = CMOS_READ(RTC_CONTROL);	save_freq_select = CMOS_READ(RTC_FREQ_SELECT);	CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6,		   RTC_FREQ_SELECT);	CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL);	i = 100;	while (i-- > 0) {		mdelay(10);		if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF)			i -= 10;	}	CMOS_WRITE(save_control, RTC_CONTROL);	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);	clear_IO_APIC_pin(0, pin);	spin_lock_irqsave(&ioapic_lock, flags);	io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1));	io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0));	spin_unlock_irqrestore(&ioapic_lock, flags);}/* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ * is so screwy.  Thanks to Brian Perkins for testing/hacking this beast * fanatically on his truly buggy board. */static inline void check_timer(void){	extern int timer_ack;	int pin1, pin2;	int vector;	/*	 * get/set the timer IRQ vector:	 */	disable_8259A_irq(0);	vector = assign_irq_vector(0);	set_intr_gate(vector, interrupt[0]);	/*	 * Subtle, code in do_timer_interrupt() expects an AEOI	 * mode for the 8259A whenever interrupts are routed	 * through I/O APICs.  Also IRQ0 has to be enabled in	 * the 8259A which implies the virtual wire has to be	 * disabled in the local APIC.	 */	apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);	init_8259A(1);	timer_ack = 1;	enable_8259A_irq(0);	pin1 = find_isa_irq_pin(0, mp_INT);	pin2 = find_isa_irq_pin(0, mp_ExtINT);	printk(KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2);	if (pin1 != -1) {		/*		 * Ok, does IRQ0 through the IOAPIC work?		 */		unmask_IO_APIC_irq(0);		if (timer_irq_works()) {			if (nmi_watchdog == NMI_IO_APIC) {				disable_8259A_irq(0);				setup_nmi();				enable_8259A_irq(0);				check_nmi_watchdog();			}			return;		}		clear_IO_APIC_pin(0, pin1);		printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n");	}	printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... ");	if (pin2 != -1) {		printk("\n..... (found pin %d) ...", pin2);		/*		 * legacy devices should be connected to IO APIC #0		 */		setup_ExtINT_IRQ0_pin(pin2, vector);		if (timer_irq_works()) {			printk("works.\n");			if (nmi_watchdog == NMI_IO_APIC) {				setup_nmi();				check_nmi_watchdog();			}			return;		}		/*		 * Cleanup, just in case ...		 */		clear_IO_APIC_pin(0, pin2);	}	printk(" failed.\n");	if (nmi_watchdog) {		printk(KERN_WARNING "timer doesnt work through the IO-APIC - disabling NMI Watchdog!\n");		nmi_watchdog = 0;	}	printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");	disable_8259A_irq(0);	irq_desc[0].handler = &lapic_irq_type;	apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector);	/* Fixed mode */	enable_8259A_irq(0);	if (timer_irq_works()) {		printk(" works.\n");		return;	}	apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);	printk(" failed.\n");	printk(KERN_INFO "...trying to set up timer as ExtINT IRQ...");	init_8259A(0);	make_8259A_irq(0);	apic_write_around(APIC_LVT0, APIC_DM_EXTINT);	unlock_ExtINT_logic();	if (timer_irq_works()) {		printk(" works.\n");		return;	}	printk(" failed :(.\n");	panic("IO-APIC + timer doesn't work! pester mingo@redhat.com");}/* * * IRQ's that are handled by the old PIC in all cases: * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. *   Linux doesn't really care, as it's not actually used *   for any interrupt handling anyway. * - There used to be IRQ13 here as well, but all *   MPS-compliant must not use it for FPU coupling and we *   want to use exception 16 anyway.  And there are *   systems who connect it to an I/O APIC for other uses. *   Thus we don't mark it special any longer. * * Additionally, something is definitely wrong with irq9 * on PIIX4 boards. */#define PIC_IRQS	(1<<2)void __init setup_IO_APIC(void){	enable_IO_APIC();	io_apic_irqs = ~PIC_IRQS;	printk("ENABLING IO-APIC IRQs\n");	/*	 * Set up the IO-APIC IRQ routing table by parsing the MP-BIOS	 * mptable:	 */	setup_ioapic_ids_from_mpc();	sync_Arb_IDs();	setup_IO_APIC_irqs();	init_IO_APIC_traps();	check_timer();	print_IO_APIC();}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -