diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/MAINTAINERS linuxppc64_2_4/MAINTAINERS --- ../w3.wed/linux-2.4.8-ac5/MAINTAINERS Wed Aug 15 12:11:29 2001 +++ linuxppc64_2_4/MAINTAINERS Thu Aug 16 08:58:42 2001 @@ -870,7 +870,7 @@ P: Dwayne McConnell M: dwayne@austin.ibm.com W: http://linuxppc64.org -L: linuxppc64-dev@lists.linuxppc.porg +L: linuxppc64-dev@lists.linuxppc.org S: Supported LOGICAL VOLUME MANAGER diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/Makefile linuxppc64_2_4/Makefile --- ../w3.wed/linux-2.4.8-ac5/Makefile Wed Aug 15 12:11:29 2001 +++ linuxppc64_2_4/Makefile Thu Aug 16 08:58:42 2001 @@ -10,11 +10,13 @@ # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, -# then ARCH is assigned, getting whatever value it gets normally, and +# then ARCH is assigned, getting whatever value it gets normally, and # SUBARCH is subsequently ignored. SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) -ARCH := $(SUBARCH) +#ARCH := $(SUBARCH) + +ARCH := ppc64 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ @@ -27,7 +29,7 @@ HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -CROSS_COMPILE = +CROSS_COMPILE = /usr/local/ppc64-current/powerpc-elf64/bin/ # # Include the make variables (CC, etc...) @@ -97,7 +99,7 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ - -fomit-frame-pointer -fno-strict-aliasing -fno-common + -fomit-frame-pointer -fno-strict-aliasing -fno-common AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/HvLpConfig.c linuxppc64_2_4/arch/ppc64/kernel/HvLpConfig.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/HvLpConfig.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/HvLpConfig.c Mon Aug 13 10:25:01 2001 @@ -0,0 +1,28 @@ +/* + * HvLpConfig.c + * Copyright (C) 2001 Kyle A. Lucke, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HVLPCONFIG_H +#include +#endif + +HvLpIndex HvLpConfig_getLpIndex_outline(void) +{ + return HvLpConfig_getLpIndex(); +} + diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/Makefile linuxppc64_2_4/arch/ppc64/kernel/Makefile --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/Makefile Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/Makefile Tue Aug 14 23:13:12 2001 @@ -26,12 +26,12 @@ ioctl32.o ptrace32.o signal32.o open_pic.o xics.o \ pmc.o mf_proc.o proc_pmc.o iSeries_setup.o \ ItLpQueue.o hvCall.o mf.o viopath.o HvLpEvent.o \ - iSeries_proc.o HvCall.o flight_recorder.o + iSeries_proc.o HvCall.o flight_recorder.o HvLpConfig.o obj-$(CONFIG_PCI) += pci.o ifeq ($(CONFIG_PPC_ISERIES),y) -obj-$(CONFIG_PCI) += iSeries_dma.o iSeries_rtc.o +obj-$(CONFIG_PCI) += iSeries_dma.o iSeries_rtc.o proc_pcifr.o else obj-$(CONFIG_PCI) += pci_dma.o proc_pcifr.o endif diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/entry.S linuxppc64_2_4/arch/ppc64/kernel/entry.S --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/entry.S Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/entry.S Wed Aug 15 09:12:56 2001 @@ -306,10 +306,10 @@ mtmsrd r6 /* Update machine state */ ld r6,_MSR(r1) - mtspr SRR0,r7 - mtspr SRR1,r6 ld r2,GPR2(r1) ld r1,GPR1(r1) + mtspr SRR0,r7 + mtspr SRR1,r6 /* sync required to force memory operations on this processor */ /* to complete before the current thread gives up control. */ @@ -334,33 +334,20 @@ ld r5,_MSR(r1) andi. r5,r5,MSR_EE beq 2f - -_GLOBAL(lost_irq_ret) -3: LOADBASE(r4,ppc_n_lost_interrupts) - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 - bne- 1f /* do_IRQ if lost interrupts */ +irq_recheck: + mfmsr r5 + andi. r5,r5,MSR_EE + bne 4f /* - * Check for pending I/O events (iSeries) - * If no I/O events pending then CR0 = "eq" and r4 = 0 - * (kills registers r5 and r6) + * Check for pending interrupts (iSeries) */ - CHECKLPQUEUE(r4,r5,r6) - beq+ 4f /* skip do_IRQ if no events */ -1: + CHECKANYINT(r4,r5) + beq+ 4f /* skip do_IRQ if no interrupts */ + addi r3,r1,STACK_FRAME_OVERHEAD bl .do_IRQ - b 3b /* loop back and handle more */ + b irq_recheck /* loop back and handle more */ 4: - /* - * Check for lost decrementer interrupts. - * (If decrementer popped while we were in the hypervisor) - */ - CHECKDECR(r4,r5) - beq+ 5f - addi r3,r1,STACK_FRAME_OVERHEAD - bl .timer_interrupt -5: LOADADDR(r4,irq_stat) #ifdef CONFIG_SMP /* get processor # */ @@ -388,7 +375,6 @@ /* NEED_RESCHED is a volatile long (64-bits) */ mfspr r4,SPRG3 /* current task's PACA */ ld r4,PACACURRENT(r4) /* Get 'current' */ - ld r3,NEED_RESCHED(r4) cmpi 0,r3,0 /* check need_resched flag */ beq+ 7f @@ -423,17 +409,24 @@ rldicl r0,r0,48,1 rldicl r0,r0,16,0 /* clear MSR_EE */ mtmsrd r0 /* Update machine state */ + + ld r0,_MSR(r1) + andi. r3,r0,MSR_EE + beq+ 1f + + CHECKANYINT(r4,r3) + bne- irq_recheck +1: /* if returning to user mode, set new sprg3 and save kernel SP */ ld r0,_MSR(r1) andi. r0,r0,MSR_PR beq+ 1f addi r0,r1,INT_FRAME_SIZE /* size of frame */ mfspr r4,SPRG3 /* current task's PACA */ - ld r4,PACACURRENT(r4) /* Get 'current' */ - std r0,THREAD+KSP(r4) /* save kernel stack pointer */ - mfspr r2,SPRG3 /* Get Paca */ - std r1,PACAKSAVE(r2) /* save exception stack pointer */ + ld r2,PACACURRENT(r4) /* Get 'current' */ + std r0,THREAD+KSP(r2) /* save kernel stack pointer */ + std r1,PACAKSAVE(r4) /* save exception stack pointer */ 1: ld r0,_MSR(r1) mtspr SRR1,r0 diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/head.S linuxppc64_2_4/arch/ppc64/kernel/head.S --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/head.S Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/head.S Tue Aug 14 23:40:22 2001 @@ -26,6 +26,8 @@ * */ +#define SECONDARY_PROCESSORS + #include "ppc_asm.h" #include "ppc_defs.h" #include @@ -326,22 +328,32 @@ .globl SystemReset_Iseries SystemReset_Iseries: - mfspr r3,SPRG3 /* Get Paca address */ - lhz r24,PACAPACAINDEX(r3) /* Get processor # */ + mfspr 25,SPRG3 /* Get Paca address */ + lhz r24,PACAPACAINDEX(r25) /* Get processor # */ cmpi 0,r24,0 /* Are we processor 0? */ beq .__start_initialization_iSeries /* Start up the first processor */ mfspr r4,CTRLF li r5,RUNLATCH /* Turn off the run light */ andc r4,r4,r5 mtspr CTRLT,r4 + 1: HMT_LOW #ifdef CONFIG_SMP - lbz r23,PACAPROCSTART(r3) /* Test if this processor + lbz r23,PACAPROCSTART(r25) /* Test if this processor * should start */ + sync + LOADADDR(r3,current_set) + sldi r28,r24,4 /* get current_set[cpu#] */ + ldx r3,r3,r28 + addi r1,r3,TASK_UNION_SIZE + subi r1,r1,STACK_FRAME_OVERHEAD + cmpi 0,r23,0 beq iseries_secondary_smp_loop /* Loop until told to go */ +#ifdef SECONDARY_PROCESSORS bne .__secondary_start /* Loop until told to go */ +#endif iseries_secondary_smp_loop: /* Let the Hypervisor know we are alive */ /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ @@ -354,13 +366,13 @@ which are running on multi-threaded machines. */ lis r3,0x8000 rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ - addi r3,r3,18 /* r3 = -x8000000000000012 which is "yield" */ + addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */ li r4,0 /* "yield timed" */ li r5,-1 /* "yield forever" */ #endif /* CONFIG_SMP */ li r0,-1 /* r0=-1 indicates a Hypervisor call */ sc /* Invoke the hypervisor via a system call */ - mfspr r3,SPRG3 /* Put r3 back */ + mfspr r25,SPRG3 /* Put r25 back ???? */ b 1b /* If SMP not configured, secondaries * loop forever */ @@ -368,7 +380,6 @@ STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException ) STD_EXCEPTION_COMMON( 0x200, MachineCheck, .MachineCheckException ) - STD_EXCEPTION_COMMON( 0x500, HardwareInterrupt, .do_IRQ ) STD_EXCEPTION_COMMON( 0x900, Decrementer, .timer_interrupt ) STD_EXCEPTION_COMMON( 0xa00, Trap_0a, .UnknownException ) STD_EXCEPTION_COMMON( 0xb00, Trap_0b, .UnknownException ) @@ -376,8 +387,21 @@ STD_EXCEPTION_COMMON( 0xe00, Trap_0e, .UnknownException ) STD_EXCEPTION_COMMON( 0xf00, PerformanceMonitor, .PerformanceMonitorException ) +/* r20 is in SPRG2, + r21 is in the PACA +*/ .globl DataAccess_common DataAccess_common: + mfcr r20 + mfspr r21,DAR + srdi r21,r21,60 + cmpi 0,r21,0xf + bne 3f + + /* Segment faulted on a bolted segment. Go off and map that segment. */ + b .do_stab_bolted + +3: mtcr r20 EXCEPTION_PROLOG_COMMON mfspr r20,DSISR std r20,_DSISR(r21) @@ -520,6 +544,17 @@ .llong .do_page_fault .llong .ret_from_except + .globl HardwareInterrupt_common +HardwareInterrupt_common: + EXCEPTION_PROLOG_COMMON +HardwareInterrupt_entry: + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + li r6,0x500 + bl .transfer_to_handler + .llong .do_IRQ + .llong .ret_from_except + .globl Alignment_common Alignment_common: EXCEPTION_PROLOG_COMMON @@ -559,6 +594,11 @@ .globl SystemCall_common SystemCall_common: EXCEPTION_PROLOG_COMMON + cmpi 0,r0,0x5555 /* Special syscall to handle pending */ + bne+ 1f /* interrupts */ + andi. r6,r23,MSR_PR /* Only allowed from kernel */ + beq+ HardwareInterrupt_entry +1: std r3,ORIG_GPR3(r21) SET_REG_TO_CONST(r20, MSR_KERNEL) rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ @@ -612,6 +652,124 @@ ld r21,GPR21(r21) rfid +/* orig r20 is in SPRG2, + orig r21 is in the PACA + r20 contains CCR + + r22 needs to be saved + r1 needs to be saved + CCR needs to be saved +*/ +_GLOBAL(do_stab_bolted) + mfsprg r21,3 + std r22,PACAR22(r21) + std r1,PACAR1(r21) + stw r20,PACACCR(r21) + mfspr r21,DAR + + /* (((ea >> 28) & 0x1fff) << 15) | (ea >> 60) */ + rldicl r20,r21,36,32 /* Permits a full 32b of ESID */ + rldicr r20,r20,15,48 + rldicl r21,r21,4,60 + or r20,r20,r21 + + li r21,9 /* VSID_RANDOMIZER */ + rldicr r21,r21,32,31 + oris r21,r21,58231 + ori r21,r21,39831 + + mulld r20,r20,r21 + clrldi r20,r20,28 /* r20 = vsid */ + + mfsprg r21,3 + ld r21,PACASTABVIRT(r21) + + /* Hash to the primary group */ + mfspr r22,DAR + rldicl r22,r22,36,59 + rldicr r22,r22,7,56 + or r21,r21,r22 /* r21 = first ste of the group */ + + /* Search the primary group for a free entry */ + li r22,0 +1: + ld r1,0(r21) /* Test valid bit of the current ste */ + rldicl r1,r1,57,63 + cmpwi r1,0 + bne 2f + ld r1,8(r21) /* Get the current vsid part of the ste */ + rldimi r1,r20,12,0 /* Insert the new vsid value */ + std r1,8(r21) /* Put new entry back into the stab */ + eieio /* Order vsid update */ + ld r1,0(r21) /* Get the esid part of the ste */ + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,28 /* Permits a full 36b of ESID */ + rldimi r1,r20,28,0 /* Insert the new esid value */ + ori r1,r1,144 /* Turn on valid and kp */ + std r1,0(r21) /* Put new entry back into the stab */ + sync /* Order the update */ + b 3f +2: + addi r22,r22,1 + addi r21,r21,16 + cmpldi r22,7 + ble 1b + + /* Stick for only searching the primary group for now. */ + /* At least for now, we use a very simple random castout scheme */ + /* Use the TB as a random number ; OR in 1 to avoid entry 0 */ + mftb r22 + andi. r22,r22,7 + ori r22,r22,1 + sldi r22,r22,4 + + /* r21 currently points to and ste one past the group of interest */ + /* make it point to the randomly selected entry */ + subi r21,r21,128 + ori r21,r21,r22 /* r21 is the entry to invalidate */ + + isync /* mark the entry invalid */ + ld r1,0(r21) + li r22,-129 + and r1,r1,r22 + std r1,0(r21) + sync + + ld r1,8(r21) + rldimi r1,r20,12,0 + std r1,8(r21) + eieio + + ld r1,0(r21) /* Get the esid part of the ste */ + mr r22,r1 + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,32 /* Permits a full 32b of ESID */ + rldimi r1,r20,28,0 /* Insert the new esid value */ + ori r1,r1,144 /* Turn on valid and kp */ + std r1,0(r21) /* Put new entry back into the stab */ + + rldicl r22,r22,36,28 + rldicr r22,r22,28,35 + slbie r22 + sync + +3: + /* All done -- return from exception. */ + mfsprg r20,3 /* Load the PACA pointer */ + + ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */ + ld r21,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */ + mtspr SRR1,r21 + mtspr SRR0,r22 + + lwz r21,PACACCR(r20) /* Restore the clobbered regs */ + mtcr r21 + ld r1, PACAR1(r20) + ld r21,PACAR21(r20) + ld r22,PACAR22(r20) + mfspr r20,SPRG2 + rfid + _GLOBAL(do_stab_SI) mflr r21 /* Save LR in r21 */ /* r21 restored later from r1 */ @@ -696,7 +854,6 @@ addi r24,r1,STACK_FRAME_OVERHEAD std r24,THREAD+PT_REGS(r22) 2: - /* * Since we store 'current' in the PACA now, we don't need to * set it here. When r2 was used as 'current' it had to be @@ -733,27 +890,37 @@ * At entry, r3 = this processor's number (in Linux terms, not hardware). */ _GLOBAL(pseries_secondary_smp_init) + + /* turn on 64-bit mode */ + bl .enable_64b_mode + isync + /* Set up a Paca value for this processor. */ LOADADDR(r24, xPaca) /* Get base vaddr of Paca array */ mulli r25,r3,PACA_SIZE /* Calculate vaddr of right Paca */ add r25,r25,r24 /* for this processor. */ - SET_REG_TO_CONST(r26,KERNELBASE) /* Calculate raddr of the Paca */ - sub r26,r25,r26 - mtspr SPRG3,r25 /* Save vaddr of Paca in SPRG3 */ mr r24,r3 /* __secondary_start needs cpu# */ 1: HMT_LOW - lbz r23,PACAPROCSTART(r26) /* Test if this processor should */ + lbz r23,PACAPROCSTART(r25) /* Test if this processor should */ /* start. */ + sync + LOADADDR(r3,current_set) + addi r3,r3,8 + sldi r28,r24,4 /* get current_set[cpu#] */ + ldx r1,r3,r28 + cmpi 0,r23,0 #ifdef CONFIG_SMP - bne .__secondary_start /* Loop until told to go. */ +#ifdef SECONDARY_PROCESSORS + bne .__secondary_start #endif - b 1b - +#endif + b 1b /* Loop until told to go */ + _GLOBAL(__start_initialization_iSeries) LOADADDR(r1,init_task_union) @@ -1071,68 +1238,57 @@ * On entry the following are set: * r24 = cpu# (in Linux terms) * r25 = Paca virtual address - * r26 = Paca physical address * SPRG3 = Paca virtual address */ _GLOBAL(__secondary_start) - /* turn on 64 bit mode */ - bl .enable_64b_mode - isync - - /* get our current offset. */ - SET_REG_TO_CONST(r27, KERNELBASE) HMT_MEDIUM /* Set thread priority to MEDIUM */ - /* get a virtual pointer to current */ - LOADADDR(r3,current_set) - sub r3,r3,r27 - addi r3,r3,8 - sldi r28,r24,4 /* get current_set[cpu#] */ - ldx r1,r3,r28 - /* set up the TOC (virtual address) */ LOADADDR(r2,__toc_start) addi r2,r2,0x4000 addi r2,r2,0x4000 - std r2,PACATOC(r26) + std r2,PACATOC(r25) li r6,0 - std r6,PACAKSAVE(r26) + std r6,PACAKSAVE(r25) #ifndef CONFIG_PPC_ISERIES /* Initialize the page table pointer register. */ - LOADADDR(r6,_SDR1) - sub r6,r6,r27 + LOADADDR(r6,_SDR1) ld r6,0(r6) /* get the value of _SDR1 */ mtspr SDR1,r6 /* set the htab location */ #endif /* Initialize the segment table subsystem. */ - ld r4,PACASTABREAL(r26) /* get raddr of segment table */ - ori r3,r4,1 /* turn on valid bit */ - mtasr r3 /* set the stab location */ + ld r4,PACASTABVIRT(r25) /* get addr of segment table */ li r3,0 /* 0 -> include esid 0xC00000000 */ - slbia + std r3,0(r1) /* Zero the stack frame pointer */ bl .stab_initialize - /* get a virtual pointer to current */ - SET_REG_TO_CONST(r27, KERNELBASE) LOADADDR(r3,current_set) - sub r3,r3,r27 sldi r28,r24,4 /* get current_set[cpu#] */ ldx r6,r3,r28 + std r6,PACACURRENT(r25) addi r1,r6,TASK_UNION_SIZE subi r1,r1,STACK_FRAME_OVERHEAD - addi r3,r3,8 - ld r3,0(r3) + ld r3,PACASTABREAL(r25) /* get raddr of segment table */ + ori r4,r3,1 /* turn on valid bit */ + +#ifdef CONFIG_PPC_ISERIES + li r0,-1 /* hypervisor call */ + li r3,1 + sldi r3,r3,63 /* 0x8000000000000000 */ + ori r3,r3,4 /* 0x8000000000000004 */ + sc /* HvCall_setASR */ +#else + mtasr r4 /* set the stab location */ +#endif + slbia + li r7,0 - std r7,0(r3) mtlr r7 - mfspr r4,SPRG3 - std r6,PACACURRENT(r4) - /* enable MMU and jump to start_secondary */ LOADADDR(r3,.start_secondary) SET_REG_TO_CONST(r4, MSR_KERNEL) @@ -1356,6 +1512,10 @@ bolted_dir: .space 4096 +/* 4096 * 31 bytes of storage */ + .globl stab_array +stab_array: + .space 131072 /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/htab.c linuxppc64_2_4/arch/ppc64/kernel/htab.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/htab.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/htab.c Wed Aug 15 16:43:31 2001 @@ -82,6 +82,22 @@ #define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) #define RELOC(x) (*PTRRELOC(&(x))) +extern unsigned long htab_size( unsigned long ); + +static inline void +create_pte_mapping(unsigned long start, unsigned long end, + unsigned long mode, unsigned long mask) +{ + unsigned long addr, offset = reloc_offset(); + HTAB *_htab_data = PTRRELOC(&htab_data); + HPTE *htab = (HPTE *)__v2a(_htab_data->htab); + + for (addr=start; addr < end ;addr+=0x1000) { + unsigned long vsid = get_kernel_vsid(addr); + unsigned long va = (vsid << 28) | (addr & 0xfffffff); + make_pte(htab, va, (unsigned long)__v2a(addr), mode, mask); + } +} void htab_initialize(void) @@ -125,11 +141,11 @@ * (addr & KERNELBASE) == 0 (ie they are disjoint). * We also assume that the va is <= 64 bits. */ - create_pte_mapping(_stext, __start_naca, mode_ro, mask); - create_pte_mapping(__start_naca, __end_stab, mode_rw, mask); - create_pte_mapping(__end_stab, _etext, mode_ro, mask); - create_pte_mapping(_etext, RELOC(klimit), mode_rw, mask); - create_pte_mapping(__a2v(table), __a2v(table+htab_size_bytes), mode_rw, mask); + create_pte_mapping((unsigned long)_stext, (unsigned long)__start_naca, mode_ro, mask); + create_pte_mapping((unsigned long)__start_naca, (unsigned long)__end_stab, mode_rw, mask); + create_pte_mapping((unsigned long)__end_stab, (unsigned long)_etext, mode_ro, mask); + create_pte_mapping((unsigned long)_etext, RELOC(klimit), mode_rw, mask); + create_pte_mapping((unsigned long)__a2v(table), (unsigned long)__a2v(table+htab_size_bytes), mode_rw, mask); } static inline unsigned long hpt_hash( unsigned long vpn ) @@ -318,6 +334,69 @@ return 0; } +static unsigned long get_hpte0( unsigned long slot ) +{ + unsigned long dword0; + + if ( _machine == _MACH_iSeries ) { + HPTE hpte; + HvCallHpt_get( &hpte, slot ); + dword0 = hpte.dw0.dword0; + } + else { + HPTE * hptep = htab_data.htab + slot; + dword0 = hptep->dw0.dword0; + } + + return dword0; +} + +long find_hpte( unsigned long vpn ) +{ + HPTE hpte; + long slot; + + if ( _machine == _MACH_iSeries ) { + slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { + if ( slot < 0 ) { + slot &= 0x7fffffffffffffff; + slot = -slot; + } + } + else + slot = -1; + } + else { + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + unsigned long hash; + unsigned long i,j; + + hash = hpt_hash( vpn ); + for ( j=0; j<2; ++j ) { + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( i=0; i> 11 ) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == j ) ) { + /* HPTE matches */ + if ( j ) + slot = -slot; + return slot; + } + ++slot; + } + hash = ~hash; + } + slot = -1; + } + return slot; +} + /* This function is called by btmalloc to bolt an entry in the hpt */ void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, pte_t * ptep, unsigned hpteflags, unsigned bolted ) @@ -474,23 +553,6 @@ 1 ) ); } -static unsigned long get_hpte0( unsigned long slot ) -{ - unsigned long dword0; - - if ( _machine == _MACH_iSeries ) { - HPTE hpte; - HvCallHpt_get( &hpte, slot ); - dword0 = hpte.dw0.dword0; - } - else { - HPTE * hptep = htab_data.htab + slot; - dword0 = hptep->dw0.dword0; - } - - return dword0; -} - static void updateHptePP( long slot, unsigned long newpp, unsigned long va ) { if ( _machine == _MACH_iSeries ) { @@ -568,7 +630,7 @@ pte_val(old_pte) = ((__pa(ea)&PAGE_MASK)<<(PTE_SHIFT-PAGE_SHIFT)) | _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED | _PAGE_RW | _PAGE_COHERENT | - _PAGE_DIRTY | _PAGE_HASHPTE; + _PAGE_DIRTY | _PAGE_HPTENOIX; spin_lock( &hash_table_lock ); break; case VMALLOC_REGION_ID: @@ -669,6 +731,24 @@ access |= _PAGE_PRESENT; if ( 0 == ( access & ~(pte_val(old_pte)) ) ) { + /* + * Check if pte might have an hpte, but we have + * no slot information + */ + if ( pte_val(old_pte) & _PAGE_HPTENOIX ) { + unsigned long slot; + pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; + slot = find_hpte( vpn ); + if ( slot != -1 ) { + if ( slot < 0 ) { + pte_val(old_pte) |= _PAGE_SECONDARY; + slot = -slot; + } + pte_val(old_pte) |= ((slot << 12) & _PAGE_GROUP_IX) | _PAGE_HASHPTE; + + } + } + /* User has appropriate access rights. */ new_pte = old_pte; /* If the attempted access was a store */ @@ -848,6 +928,25 @@ /* HPTE matches */ invalidate_hpte( slot ); } + else { + unsigned k; + /* Temporarily lets check for the hpte in all possible slots */ + for ( secondary = 0; secondary < 2; ++secondary ) { + hash = hpt_hash( vpn ); + if ( secondary ) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( k=0; k<8; ++k ) { + hpte_dw0.d = get_hpte0( slot+k ); + if ( ( hpte_dw0.h.avpn == (vpn >> 11) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == secondary ) ) { + while (1) ; + } + } + } + + } spin_unlock_irqrestore( &hash_table_lock, flags ); } @@ -1042,20 +1141,5 @@ pteg_count >>= 3; } return (pteg_count << 7); /* pteg_count*128B/PTEG */ -} - -static inline void -create_pte_mapping(unsigned long start, unsigned long end, - unsigned long mode, unsigned long mask) -{ - unsigned long addr, offset = reloc_offset(); - HTAB *_htab_data = PTRRELOC(&htab_data); - HPTE *htab = (HPTE *)__v2a(_htab_data->htab); - - for (addr=start; addr < end ;addr+=0x1000) { - unsigned long vsid = get_kernel_vsid(addr); - unsigned long va = (vsid << 28) | (addr & 0xfffffff); - make_pte(htab, va, __v2a(addr), mode, mask); - } } diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/iSeries_setup.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/iSeries_setup.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c Wed Aug 15 08:10:23 2001 @@ -92,7 +92,7 @@ */ if ( naca->xRamDisk ) { - initrd_start = __va(naca->xRamDisk); + initrd_start = (unsigned long)__va(naca->xRamDisk); initrd_end = initrd_start + naca->xRamDiskSize * PAGE_SIZE; initrd_below_start_ok = 1; // ramdisk in kernel space ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); @@ -243,7 +243,7 @@ loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr); loadAreaSize = itLpNaca.xLoadAreaChunks; - + /* Only add the pages already mapped here. * Otherwise we might add the hpt pages * The rest of the pages of the load area @@ -256,7 +256,7 @@ loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; printk( "Mapping load area - physical addr = 0000000000000000\n" - " absolute addr = %016x\n", + " absolute addr = %016lx\n", chunk_to_addr(loadAreaFirstChunk) ); printk( "Load area size %dK\n", loadAreaSize*256 ); @@ -275,7 +275,7 @@ hptSizeChunks = hptSizePages >> (msChunks.chunk_shift-PAGE_SHIFT); hptLastChunk = hptFirstChunk + hptSizeChunks - 1; - printk( "HPT absolute addr = %016x, size = %dK\n", + printk( "HPT absolute addr = %016lx, size = %dK\n", chunk_to_addr(hptFirstChunk), hptSizeChunks*256 ); /* Fill in the htab_data structure */ @@ -357,6 +357,11 @@ * nextPhysChunk */ naca->physicalMemorySize = chunk_to_addr(nextPhysChunk); + + lmb_init(); + lmb_add( 0, naca->physicalMemorySize ); + lmb_reserve( 0, __pa(klimit)); + } /* @@ -502,6 +507,77 @@ return (len); } +#ifdef CONFIG_SMP +void iSeries_spin_lock(spinlock_t *lock) +{ + int i; + for (;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if ( lock->lock == 0 ) { + HMT_medium(); + if ( __spin_trylock(lock) ) + return; + } + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +void iSeries_read_lock(__rwlock_t *rw) +{ + int i; + for (;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if ( rw->lock >= 0 ) { + HMT_medium(); + if ( __read_trylock(rw) ) + return; + } + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +void iSeries_write_lock(__rwlock_t *rw) +{ + int i; + for (;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if ( rw->lock == 0 ) { + HMT_medium(); + if ( __write_trylock(rw) ) + return; + } + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +void iSeries_brlock_spin( spinlock_t *lock ) +{ + int i; + for(;;) { + for (i=1024; i>0; --i) { + HMT_low(); + if (lock->lock == 0) { + HMT_medium(); + return; + } + + } + HMT_medium(); + HvCallCfg_getLps(); + } +} + +#endif /* CONFIG_SMP */ + int iSeries_get_cpuinfo(char *buffer) { int len = 0; @@ -605,7 +681,7 @@ * Set the RTC in the virtual service processor * This requires flowing LpEvents to the primary partition */ -int __init +int iSeries_set_rtc_time(unsigned long time) { mf_setRtcTime(time); @@ -616,7 +692,7 @@ * Get the RTC from the virtual service processor * This requires flowing LpEvents to the primary partition */ -unsigned long __init +unsigned long iSeries_get_rtc_time(void) { unsigned long time; diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/idle.c linuxppc64_2_4/arch/ppc64/kernel/idle.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/idle.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/idle.c Wed Aug 15 08:37:55 2001 @@ -31,41 +31,91 @@ #include #include -static void power_save(void) +#include +#include +#include + +unsigned long maxYieldTime = 0; +unsigned long minYieldTime = 0xffffffffffffffffUL; + +static void yield_shared_processor(void) { - /* Implement me */ ; + struct Paca *paca; + unsigned long tb; + unsigned long yieldTime; + + /* Turn off the run light */ + unsigned long CTRL; + CTRL = mfspr(CTRLF); + CTRL &= ~RUNLATCH; + mtspr(CTRLT, CTRL); + + HMT_low(); + + paca = (struct Paca *)mfspr(SPRG3); + HvCall_setEnabledInterrupts( HvCall_MaskIPI | + HvCall_MaskLpEvent | + HvCall_MaskLpProd | + HvCall_MaskTimeout ); + + __cli(); + __sti(); + tb = get_tb(); + /* Compute future tb value when yield should expire */ + HvCall_yieldProcessor( HvCall_YieldTimed, tb+tb_ticks_per_jiffy ); + + yieldTime = get_tb() - tb; + if ( yieldTime > maxYieldTime ) + maxYieldTime = yieldTime; + + if ( yieldTime < minYieldTime ) + minYieldTime = yieldTime; + + /* + * disable/enable will force any pending interrupts + * to be seen. + */ + __cli(); + /* + * The decrementer stops during the yield. Just force + * a fake decrementer now and the timer interrupt code + * will straighten it all out. We have to do this + * while disabled so we don't do it between where it is + * checked and where it is reset. + */ + + paca->xLpPaca.xIntDword.xFields.xDecrInt = 1; + __sti(); } int idled(void) { - int do_power_save = 0; + struct Paca *paca; + long oldval; /* endless loop with no priority at all */ current->nice = 20; current->counter = -100; init_idle(); - for (;;) { -#ifdef CONFIG_SMP - int oldval; + paca = (struct Paca *)mfspr(SPRG3); - if (!do_power_save) { - /* - * Deal with another CPU just having chosen a thread to - * run here: - */ + for (;;) { + + if ( paca->xLpPaca.xSharedProc ) { + if ( !current->need_resched ) + yield_shared_processor(); + } + else { + /* Avoid an IPI by setting need_resched */ oldval = xchg(¤t->need_resched, -1); - if (!oldval) { while(current->need_resched == -1) - ; /* Do Nothing */ + HMT_low(); } } -#endif - if (do_power_save && !current->need_resched) - power_save(); - if (current->need_resched) { + HMT_medium(); schedule(); check_pgt_cache(); } @@ -82,3 +132,5 @@ idled(); return 0; } + + diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/irq.c linuxppc64_2_4/arch/ppc64/kernel/irq.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/irq.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/irq.c Mon Aug 13 10:40:21 2001 @@ -526,7 +526,8 @@ { int cpu = smp_processor_id(); int irq; - unsigned long flags; + struct Paca * paca; + struct ItLpQueue * lpq; /* if(cpu) udbg_printf("Entering do_IRQ\n"); */ @@ -556,23 +557,23 @@ } /* if on iSeries partition */ else { + paca = (struct Paca *)mfspr(SPRG3); #ifdef CONFIG_SMP - if ( xPaca[cpu].xLpPacaPtr->xIpiCnt ) { - xPaca[cpu].xLpPacaPtr->xIpiCnt = 0; + if ( paca->xLpPaca.xIntDword.xFields.xIpiCnt ) { + paca->xLpPaca.xIntDword.xFields.xIpiCnt = 0; iSeries_smp_message_recv( regs ); } #endif /* CONFIG_SMP */ - __no_use_save_flags( &flags ); - __no_use_cli(); - lpEvent_count += ItLpQueue_process( &xItLpQueue, regs ); - __no_lpq_restore_flags( flags ); + lpq = paca->lpQueuePtr; + if ( lpq ) + lpEvent_count += ItLpQueue_process( lpq, regs ); } irq_exit(cpu); if ( _machine == _MACH_iSeries ) { - if ( xPaca[cpu].xLpPacaPtr->xDecrInt ) { - xPaca[cpu].xLpPacaPtr->xDecrInt = 0; + if ( paca->xLpPaca.xIntDword.xFields.xDecrInt ) { + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; /* Signal a fake decrementer interrupt */ timer_interrupt( regs ); } diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/lmb.c linuxppc64_2_4/arch/ppc64/kernel/lmb.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/lmb.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/lmb.c Wed Aug 15 15:36:41 2001 @@ -21,7 +21,8 @@ extern unsigned long klimit; extern unsigned long reloc_offset(void); -static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long); + +static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long, unsigned long); struct lmb lmb = { 0, @@ -39,7 +40,9 @@ rgn->region[r1].size += rgn->region[r2].size; for(i=r2; i < rgn->cnt-1 ;i++) { rgn->region[i].base = rgn->region[i+1].base; + rgn->region[i].physbase = rgn->region[i+1].physbase; rgn->region[i].size = rgn->region[i+1].size; + rgn->region[i].type = rgn->region[i+1].type; } rgn->cnt--; } @@ -57,11 +60,13 @@ */ _lmb->memory.region[0].base = 0; _lmb->memory.region[0].size = 0; + _lmb->memory.region[0].type = LMB_MEMORY_AREA; _lmb->memory.cnt = 1; /* Ditto. */ _lmb->reserved.region[0].base = 0; _lmb->reserved.region[0].size = 0; + _lmb->reserved.region[0].type = LMB_MEMORY_AREA; _lmb->reserved.cnt = 1; } @@ -69,14 +74,20 @@ void lmb_analyze(void) { - unsigned long i; - unsigned long size_mask = 0; + unsigned long i, physbase = 0; unsigned long total_size = 0; + unsigned long size_mask = 0; unsigned long offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); for(i=0; i < _lmb->memory.cnt ;i++) { unsigned long lmb_size = _lmb->memory.region[i].size; +#ifdef CONFIG_MSCHUNKS + _lmb->memory.region[i].physbase = physbase; + physbase += lmb_size; +#else + _lmb->memory.region[i].physbase = _lmb->memory.region[i].base; +#endif total_size += lmb_size; size_mask |= lmb_size; } @@ -92,7 +103,20 @@ struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_rgn = &(_lmb->memory); - return lmb_add_region(_rgn, base, size); + return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA); + +} + +/* This routine called with relocation disabled. */ +long +lmb_add_io(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_rgn = &(_lmb->memory); + + return lmb_add_region(_rgn, base, size, LMB_IO_AREA); + } long @@ -102,20 +126,27 @@ struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_rgn = &(_lmb->reserved); - return lmb_add_region(_rgn, base, size); + return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA); } /* This routine called with relocation disabled. */ static long -lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size) +lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size, + unsigned long type) { unsigned long i, coalesced = 0; + long adjacent; /* First try and coalesce this LMB with another. */ for(i=0; i < rgn->cnt ;i++) { unsigned long rgnbase = rgn->region[i].base; unsigned long rgnsize = rgn->region[i].size; - long adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); + unsigned long rgntype = rgn->region[i].type; + + if ( rgntype != type ) + continue; + + adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); if ( adjacent > 0 ) { rgn->region[i].base -= size; rgn->region[i].physbase -= size; @@ -147,10 +178,12 @@ rgn->region[i+1].base = rgn->region[i].base; rgn->region[i+1].physbase = rgn->region[i].physbase; rgn->region[i+1].size = rgn->region[i].size; + rgn->region[i+1].type = rgn->region[i].type; } else { rgn->region[i+1].base = base; rgn->region[i+1].physbase = lmb_abs_to_phys(base); rgn->region[i+1].size = size; + rgn->region[i+1].type = type; break; } } @@ -181,15 +214,19 @@ { long i, j; unsigned long base; - unsigned long lmbbase, lmbsize; unsigned long offset = reloc_offset(); struct lmb *_lmb = PTRRELOC(&lmb); struct lmb_region *_mem = &(_lmb->memory); struct lmb_region *_rsv = &(_lmb->reserved); for(i=_mem->cnt-1; i >= 0 ;i--) { - lmbbase = _mem->region[i].base; - lmbsize = _mem->region[i].size; + unsigned long lmbbase = _mem->region[i].base; + unsigned long lmbsize = _mem->region[i].size; + unsigned long lmbtype = _mem->region[i].type; + + if ( lmbtype != LMB_MEMORY_AREA ) + continue; + base = _ALIGN_DOWN(lmbbase+lmbsize-size, align); while ( (lmbbase <= base) && @@ -201,16 +238,28 @@ break; } - if ( i < 0 ) { + if ( i < 0 ) return 0; - } - lmb_add_region(_rsv, base, size); + lmb_add_region(_rsv, base, size, LMB_MEMORY_AREA); return base; } unsigned long +lmb_phys_mem_size(void) +{ + unsigned long offset = reloc_offset(); + struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb_region *_mem = &(_lmb->memory); + unsigned long idx = _mem->cnt-1; + unsigned long lastbase = _mem->region[idx].physbase; + unsigned long lastsize = _mem->region[idx].size; + + return (lastbase + lastsize); +} + +unsigned long lmb_end_of_DRAM(void) { unsigned long offset = reloc_offset(); @@ -248,4 +297,46 @@ return pa; } +void +lmb_dump(char *str) +{ + unsigned long i; + + udbg_printf("\nlmb_dump: %s\n", str); + udbg_printf(" debug = %s\n", + (lmb.debug) ? "TRUE" : "FALSE"); + udbg_printf(" memory.cnt = %d\n", + lmb.memory.cnt); + udbg_printf(" memory.size = 0x%lx\n", + lmb.memory.size); + udbg_printf(" memory.lcd_size = 0x%lx\n", + lmb.memory.lcd_size); + for(i=0; i < lmb.memory.cnt ;i++) { + udbg_printf(" memory.region[%d].base = 0x%lx\n", + i, lmb.memory.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + lmb.memory.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + lmb.memory.region[i].size); + udbg_printf(" .type = 0x%lx\n", + lmb.memory.region[i].type); + } + udbg_printf("\n"); + udbg_printf(" reserved.cnt = %d\n", + lmb.reserved.cnt); + udbg_printf(" reserved.size = 0x%lx\n", + lmb.reserved.size); + udbg_printf(" reserved.lcd_size = 0x%lx\n", + lmb.reserved.lcd_size); + for(i=0; i < lmb.reserved.cnt ;i++) { + udbg_printf(" reserved.region[%d].base = 0x%lx\n", + i, lmb.reserved.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + lmb.reserved.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + lmb.reserved.region[i].size); + udbg_printf(" .type = 0x%lx\n", + lmb.reserved.region[i].type); + } +} diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/mf.c linuxppc64_2_4/arch/ppc64/kernel/mf.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/mf.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/mf.c Wed Aug 15 08:38:20 2001 @@ -1019,45 +1019,6 @@ int mf_setRtcTime(unsigned long time) { -/* - char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; - int rc = 0; - struct rtc_time tm; - u16 year; - u8 day, mon, hour, min, sec, y1, y2; - - to_tm(time, &tm); - - year = tm.tm_year; - y1 = (year >> 8) & 0x00FF; - y2 = year & 0x00FF; - - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(y1); - BIN_TO_BCD(y2); - - year = y1 * 100 + y2; - day = tm.tm_mday; - mon = tm.tm_mon; - hour = tm.tm_hour; - min = tm.tm_min; - sec = tm.tm_sec; - - memcpy(ceTime + 4, &year, 2); - memcpy(ceTime + 6, &sec, 1); - memcpy(ceTime + 7, &min, 1); - memcpy(ceTime + 8, &hour, 1); - memcpy(ceTime + 10, &day, 1); - memcpy(ceTime + 11, &mon, 1); - - rc = signalCEMsg( ceTime, NULL ); - - return rc; -*/ struct rtc_time tm; to_tm(time, &tm); @@ -1067,7 +1028,7 @@ struct RtcTimeData { - volatile int xDone; + struct semaphore *xSemaphore; struct CeMsgData xCeMsg; int xRc; }; @@ -1079,7 +1040,7 @@ memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg)); rtc->xRc = 0; - rtc->xDone = 1; + up(rtc->xSemaphore); } static unsigned long lastsec = 1; @@ -1129,14 +1090,16 @@ int mf_getRtc( struct rtc_time * tm ) { - struct CeMsgCompleteData ceComplete; struct RtcTimeData rtcData; int rc = 0; + DECLARE_MUTEX_LOCKED(Semaphore); memset(&ceComplete, 0, sizeof(ceComplete)); memset(&rtcData, 0, sizeof(rtcData)); + rtcData.xSemaphore = &Semaphore; + ceComplete.xHdlr = &getRtcTimeComplete; ceComplete.xToken = (void *)&rtcData; @@ -1144,13 +1107,22 @@ if ( rc == 0 ) { - while( rtcData.xDone != 1 ) - { - udelay(10); - } + down(&Semaphore); if ( rtcData.xRc == 0) { + if ( ( rtcData.xCeMsg.xCEMsg[2] == 0xa9 ) || + ( rtcData.xCeMsg.xCEMsg[2] == 0xaf ) ) { + /* TOD clock is not set */ + tm->tm_sec = 1; + tm->tm_min = 1; + tm->tm_hour = 1; + tm->tm_mday = 10; + tm->tm_mon = 8; + tm->tm_year = 71; + mf_setRtc( tm ); + } + { u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4)); u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8)); u8 year = (dataWord1 >> 16 ) & 0x000000FF; @@ -1176,7 +1148,7 @@ tm->tm_mday = day; tm->tm_mon = mon; tm->tm_year = year; - + } } else { @@ -1189,6 +1161,10 @@ tm->tm_year = 52; } + tm->tm_wday = 0; + tm->tm_yday = 0; + tm->tm_isdst = 0; + } return rc; diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/misc.S linuxppc64_2_4/arch/ppc64/kernel/misc.S --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/misc.S Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/misc.S Tue Aug 14 23:24:17 2001 @@ -101,24 +101,22 @@ /* are we enabling interrupts? */ rlwinm. r0,r3,0,16,16 beq 1f - /* if so, check if there are any lost interrupts */ - LOADBASE(r7,ppc_n_lost_interrupts) - lwz r7,ppc_n_lost_interrupts@l(r7) - cmpi 0,r7,0 /* lost interrupts to process first? */ - bne- .do_lost_interrupts - /* Check for lost decrementer interrupts. - * (If decrementer popped while we were in the hypervisor) - * (calls timer_interrupt if so) + /* Check pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) */ - CHECKDECR(r4,r5) - bne- .do_fake_decrementer - /* Check for pending I/O Events. If no I/O events pending, - * then CR0 = "eq" and r4 == 0 - * (kills registers r5 and r6) + CHECKANYINT(r4,r5) + beq+ 1f + + /* + * Handle pending interrupts in interrupt context */ - CHECKLPQUEUE(r4,r5,r6) - bne- .do_lost_interrupts -1: sync + mtmsrd r3 + li r0,0x5555 + sc + blr +1: + sync mtmsrd r3 isync blr @@ -150,23 +148,22 @@ _GLOBAL(__no_use_sti) mfmsr r3 /* Get current state */ ori r3,r3,MSR_EE /* Turn on 'EE' bit */ - LOADBASE(r4,ppc_n_lost_interrupts) - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 /* lost interrupts to process first? */ - bne- .do_lost_interrupts - /* Check for lost decrementer interrupts. - * (If decrementer popped while we were in the hypervisor) - * (calls timer_interrupt if so) + /* Check for pending interrupts + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) */ - CHECKDECR(r4,r5) - bne- .do_fake_decrementer - /* Check for pending I/O events. If no I/O events pending, - * then CR0 = "eq" and R4 = 0 - * (kills registers r5 and r6) + CHECKANYINT(r4,r5) + beq+ 1f + + /* + * Handle pending interrupts in interrupt context */ - CHECKLPQUEUE(r4,r5,r6) - bne- .do_lost_interrupts + mtmsrd r3 + li r0,0x5555 + sc + blr +1: sync /* Some chip revs have problems here... */ mtmsrd r3 /* Update machine state */ blr @@ -175,21 +172,17 @@ * We were about to enable interrupts but we have to simulate * some interrupts that were lost by enable_irq first. */ -_GLOBAL(do_lost_interrupts) +_GLOBAL(do_fake_interrupt) mflr r0 std r0,16(r1) std r3,-8(r1) stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) 1: bl .fake_interrupt - LOADBASE(r4,ppc_n_lost_interrupts) - lwz r4,ppc_n_lost_interrupts@l(r4) - cmpi 0,r4,0 - bne- 1b - /* Check for pending I/O events. If no I/O events pending, - * then CR0 = "eq" and R4 = 0 - * (kills registers r5 and r6) + /* Check for pending interrupt + * A decrementer, IPI or PMC interrupt may have occurred + * while we were in the hypervisor (which enables) */ - CHECKLPQUEUE(r4,r5,r6) + CHECKANYINT(r4,r5) bne- 1b addi r1,r1,STACK_FRAME_OVERHEAD+16 ld r3,-8(r1) @@ -198,6 +191,7 @@ mtmsrd r3 blr +#if 0 _GLOBAL(do_fake_decrementer) mflr r0 std r0,16(r1) @@ -212,6 +206,7 @@ bne- .do_lost_interrupts mtmsrd r3 blr +#endif /* * complement mask on the msr then "or" some values on. @@ -252,7 +247,6 @@ /* * Write any modified data cache blocks out to memory * and invalidate the corresponding instruction cache blocks. - * This is a no-op on the 601. * * flush_icache_range(unsigned long start, unsigned long stop) * diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/mk_defs.c linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/mk_defs.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c Mon Aug 13 10:16:24 2001 @@ -93,6 +93,7 @@ DEFINE(PACAR1, offsetof(struct Paca, xR1)); DEFINE(PACAR21, offsetof(struct Paca, xR21)); DEFINE(PACAR22, offsetof(struct Paca, xR22)); + DEFINE(PACACCR, offsetof(struct Paca, xCCR)); DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr)); DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); DEFINE(PACATOC, offsetof(struct Paca, xTOC)); @@ -100,8 +101,9 @@ DEFINE(LPREGSAV, offsetof(struct Paca, xRegSav)); DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); - DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xDecrInt)); - DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIpiCnt)); + DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xIntDword.xAnyInt)); + DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xIntDword.xFields.xDecrInt)); + DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIntDword.xFields.xIpiCnt)); DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr)); DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending)); DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags)); diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/pacaData.c linuxppc64_2_4/arch/ppc64/kernel/pacaData.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/pacaData.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pacaData.c Wed Aug 15 08:46:27 2001 @@ -39,8 +39,7 @@ 0, /* Kernel stack addr save */ \ (number), /* Paca Index */ \ 0, /* HW Proc Number */ \ - (start), /* Processor start */ \ - {0,0,0}, /* Resv */ \ + 0, /* CCR Save */ \ 0, /* MSR Save */ \ 0, /* LR Save */ \ 0, /* Pointer to thread */ \ @@ -52,10 +51,15 @@ 0, /* R1 Save */ \ (lpq), /* &xItLpQueue, */ \ {0,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, SPIN_LOCK_UNLOCKED, 0}, /*xRtas */ \ - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, /* Resv */ \ + (start), /* Processor start */ \ + {0,0,0,0,0,0,0}, /* Resv */ \ + 0, /* next_jiffy_update_tb */ \ + {0,0,0,0,0,0,0,0,0,0,0,0,0}, /* Resv */ \ { /* LpPaca */ \ xDesc: 0xd397d781, /* "LpPa" */ \ xSize: sizeof(struct ItLpPaca), /* */ \ + xFPRegsInUse: 1, \ + xDynProcStatus: 2, \ xEndOfQuantum: 0xffffffffffffffff /* */ \ }, \ { /* LpRegSave */ \ @@ -66,7 +70,7 @@ struct Paca xPaca[maxPacas] = { #ifdef CONFIG_PPC_ISERIES - PACAINITDATA( 0, 1, &xItLpQueue, 0x5000, 0xc000000000005000), + PACAINITDATA( 0, 1, &xItLpQueue, 0, 0xc000000000005000), #else PACAINITDATA( 0, 1, 0, 0x5000, 0xc000000000005000), #endif diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/pmc.c linuxppc64_2_4/arch/ppc64/kernel/pmc.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/pmc.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc.c Tue Aug 14 14:55:53 2001 @@ -22,10 +22,7 @@ * End Change Activity */ -#ifndef _PMC_PROC_H -#include -#endif - +#include #include #include #include diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/pmc_proc.c linuxppc64_2_4/arch/ppc64/kernel/pmc_proc.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/pmc_proc.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc_proc.c Wed Dec 31 18:00:00 1969 @@ -1,650 +0,0 @@ -/* - * pmc_proc.c - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -/* Change Activity: */ -/* End Change Activity */ - -#ifndef _PMC_PROC_H -#include -#endif - -#include -#include -#include -#include - -#define MMCR0 795 -#define MMCR1 798 -#define MMCRA 786 -#define PMC1 787 -#define PMC2 788 -#define PMC3 789 -#define PMC4 790 -#define PMC5 791 -#define PMC6 792 -#define PMC7 793 -#define PMC8 794 - -static int proc_pmc_control_mode = 0; -#define PMC_CONTROL_CPI 1 -#define PMC_CONTROL_TLB 2 - -static struct proc_dir_entry *pmc_proc_root = NULL; - -int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); - -int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_mmcr0( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_mmcr1( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_mmcra( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc1( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc2( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc3( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc4( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc5( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc6( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc7( char *page, char **start, off_t off, int count, int *eof, void *data); -int proc_pmc_get_pmc8( char *page, char **start, off_t off, int count, int *eof, void *data); - -int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); -int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); - -void pmc_proc_init(struct proc_dir_entry *iSeries_proc) -{ - struct proc_dir_entry *ent = NULL; - - ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_get_lpevents; - ent->write_proc = proc_reset_lpevents; - - pmc_proc_root = proc_mkdir("pmc", iSeries_proc); - if (!pmc_proc_root) return; - - ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_control; - ent->write_proc = proc_pmc_set_control; - - ent = create_proc_entry("mmcr0", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_mmcr0; - ent->write_proc = proc_pmc_set_mmcr0; - - ent = create_proc_entry("mmcr1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_mmcr1; - ent->write_proc = proc_pmc_set_mmcr1; - - ent = create_proc_entry("mmcra", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_mmcra; - ent->write_proc = proc_pmc_set_mmcra; - - ent = create_proc_entry("pmc1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc1; - ent->write_proc = proc_pmc_set_pmc1; - - ent = create_proc_entry("pmc2", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc2; - ent->write_proc = proc_pmc_set_pmc2; - - ent = create_proc_entry("pmc3", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc3; - ent->write_proc = proc_pmc_set_pmc3; - - ent = create_proc_entry("pmc4", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc4; - ent->write_proc = proc_pmc_set_pmc4; - - ent = create_proc_entry("pmc5", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc5; - ent->write_proc = proc_pmc_set_pmc5; - - ent = create_proc_entry("pmc6", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc6; - ent->write_proc = proc_pmc_set_pmc6; - - ent = create_proc_entry("pmc7", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc7; - ent->write_proc = proc_pmc_set_pmc7; - - ent = create_proc_entry("pmc8", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)0; - ent->read_proc = proc_pmc_get_pmc8; - ent->write_proc = proc_pmc_set_pmc8; - - -} - -static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) -{ - if ( len <= off+count) - *eof = 1; - *start = page+off; - len -= off; - if ( len > count ) - len = count; - if ( len < 0 ) - len = 0; - return len; -} - -static char * lpEventTypes[9] = { - "Hypervisor\t\t", - "Machine Facilities\t", - "Session Manager\t", - "SPD I/O\t\t", - "Virtual Bus\t\t", - "PCI I/O\t\t", - "RIO I/O\t\t", - "Virtual Lan\t\t", - "Virtual I/O\t\t" - }; - - -int proc_get_lpevents -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - unsigned i; - int len = 0; - - len += sprintf( page+len, "LpEventQueue 0\n" ); - len += sprintf( page+len, " events processed:\t%lu\n", - (unsigned long)xItLpQueue.xLpIntCount ); - for (i=0; i<9; ++i) { - len += sprintf( page+len, " %s %10lu\n", - lpEventTypes[i], - (unsigned long)xItLpQueue.xLpIntCountByType[i] ); - } - return pmc_calc_metrics( page, start, off, count, eof, len ); - -} - -int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - return count; -} - -int proc_pmc_get_control -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = 0; - - if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { - unsigned long mach_cycles = mfspr( PMC5 ); - unsigned long inst_complete = mfspr( PMC4 ); - unsigned long inst_dispatch = mfspr( PMC3 ); - unsigned long thread_active_run = mfspr( PMC1 ); - unsigned long thread_active = mfspr( PMC2 ); - unsigned long cpi = 0; - unsigned long cpithou = 0; - unsigned long remain; - - if ( inst_complete ) { - cpi = thread_active_run / inst_complete; - remain = thread_active_run % inst_complete; - if ( inst_complete > 1000000 ) - cpithou = remain / ( inst_complete / 1000 ); - else - cpithou = ( remain * 1000 ) / inst_complete; - } - len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); - len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); - len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); - - len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); - len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); - len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); - - len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); - len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); - - } - else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { - len += sprintf( page+len, "PMC TLB Mode\n" ); - len += sprintf( page+len, "I-miss count : %12lu\n", mfspr( PMC1 ) ); - len += sprintf( page+len, "I-miss latency : %12lu\n", mfspr( PMC2 ) ); - len += sprintf( page+len, "D-miss count : %12lu\n", mfspr( PMC3 ) ); - len += sprintf( page+len, "D-miss latency : %12lu\n", mfspr( PMC4 ) ); - len += sprintf( page+len, "IERAT miss count : %12lu\n", mfspr( PMC5 ) ); - len += sprintf( page+len, "D-reference count : %12lu\n", mfspr( PMC6 ) ); - len += sprintf( page+len, "miss PTEs searched : %12lu\n", mfspr( PMC7 ) ); - len += sprintf( page+len, "miss >8 PTEs searched : %12lu\n", mfspr( PMC8 ) ); - } - /* IMPLEMENT ME */ - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_mmcr0 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(MMCR0) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_mmcr1 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(MMCR1) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_mmcra -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(MMCRA) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc1 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC1) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc2 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC2) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc3 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC3) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc4 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC4) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc5 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC5) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc6 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC6) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc7 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC7) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -int proc_pmc_get_pmc8 -(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = sprintf( page, "0x%08lx", mfspr(PMC8) ); - return pmc_calc_metrics( page, start, off, count, eof, len ); -} - -unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) -{ - const char * p; - char b0, b1; - unsigned v, multiplier, mult, i; - unsigned long val; - multiplier = 10; - p = buf; - if ( count >= 3 ) { - b0 = buf[0]; - b1 = buf[1]; - if ( ( b0 == '0' ) && - ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { - p = buf + 2; - count -= 2; - multiplier = 16; - } - - } - val = 0; - for ( i=0; i= '0' ) && ( b0 <= '9' ) ) - v = b0 - '0'; - else if ( multiplier == 16 ) { - if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) - v = b0 - 'a' + 10; - else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) - v = b0 - 'A' + 10; - else - mult = 1; - } - else - mult = 1; - val *= mult; - val += v; - } - - return val; - -} - -static inline void proc_pmc_stop(void) -{ - /* Freeze all counters, leave everything else alone */ - mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); -} - -static inline void proc_pmc_start(void) -{ - /* Unfreeze all counters, leave everything else alone */ - mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); - -} - -static inline void proc_pmc_reset(void) -{ - /* Clear all the PMCs to zeros - * Assume a "stop" has already frozen the counters - * Clear all the PMCs - */ - mtspr( PMC1, 0 ); - mtspr( PMC2, 0 ); - mtspr( PMC3, 0 ); - mtspr( PMC4, 0 ); - mtspr( PMC5, 0 ); - mtspr( PMC6, 0 ); - mtspr( PMC7, 0 ); - mtspr( PMC8, 0 ); - -} - -static inline void proc_pmc_cpi(void) -{ - /* Configure the PMC registers to count cycles and instructions */ - /* so we can compute cpi */ - /* - * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) - * MMCR0[6] = 1 Freeze counters when any overflow - * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles - * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles - * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched - * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed - * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles - * - */ - - proc_pmc_control_mode = PMC_CONTROL_CPI; - - // Indicate to hypervisor that we are using the PMCs - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; - - // Freeze all counters - mtspr( MMCR0, 0x80000000 ); - mtspr( MMCR1, 0x00000000 ); - - // Clear all the PMCs - mtspr( PMC1, 0 ); - mtspr( PMC2, 0 ); - mtspr( PMC3, 0 ); - mtspr( PMC4, 0 ); - mtspr( PMC5, 0 ); - mtspr( PMC6, 0 ); - mtspr( PMC7, 0 ); - mtspr( PMC8, 0 ); - - /* Freeze counters in Wait State (CTRL[31]=0) */ - mtspr( MMCRA, 0x00000002 ); - - /* PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 */ - mtspr( MMCR1, 0x38cc0000 ); - - mb(); - - /* PMC1<-0x01, PMC2<-0x05 - * Start all counters - */ - mtspr( MMCR0, 0x02000045 ); - -} - -static inline void proc_pmc_tlb(void) -{ - /* Configure the PMC registers to count tlb misses */ - /* - * MMCR0[6] = 1 Freeze counters when any overflow - * MMCR0[19:25] = 0x55 Group count - * PMC1 counts I misses - * PMC2 counts I miss duration (latency) - * PMC3 counts D misses - * PMC4 counts D miss duration (latency) - * PMC5 counts IERAT misses - * PMC6 counts D references (including PMC7) - * PMC7 counts miss PTEs searched - * PMC8 counts miss >8 PTEs searched - * - */ - - proc_pmc_control_mode = PMC_CONTROL_TLB; - - /* Indicate to hypervisor that we are using the PMCs */ - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; - - /* Freeze all counters */ - mtspr( MMCR0, 0x80000000 ); - mtspr( MMCR1, 0x00000000 ); - - /* Clear all the PMCs */ - mtspr( PMC1, 0 ); - mtspr( PMC2, 0 ); - mtspr( PMC3, 0 ); - mtspr( PMC4, 0 ); - mtspr( PMC5, 0 ); - mtspr( PMC6, 0 ); - mtspr( PMC7, 0 ); - mtspr( PMC8, 0 ); - - mtspr( MMCRA, 0x00000000 ); - - mb(); - - /* PMC1<-0x55 - * Start all counters - */ - mtspr( MMCR0, 0x02001540 ); - -} - -int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - if ( ! strncmp( buffer, "stop", 4 ) ) - proc_pmc_stop(); - else if ( ! strncmp( buffer, "start", 5 ) ) - proc_pmc_start(); - else if ( ! strncmp( buffer, "reset", 5 ) ) - proc_pmc_reset(); - else if ( ! strncmp( buffer, "cpi", 3 ) ) - proc_pmc_cpi(); - else if ( ! strncmp( buffer, "tlb", 3 ) ) - proc_pmc_tlb(); - - /* IMPLEMENT ME */ - return count; -} - -int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - v = v & ~0x04000000; /* Don't allow interrupts for now */ - if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; - else - ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 0; - mtspr( MMCR0, v ); - - return count; -} - -int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( MMCR1, v ); - - return count; -} - -int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - v = v & ~0x00008000; /* Don't allow interrupts for now */ - mtspr( MMCRA, v ); - - return count; -} - - -int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC1, v ); - - return count; -} - -int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC2, v ); - - return count; -} - -int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC3, v ); - - return count; -} - -int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC4, v ); - - return count; -} - -int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC5, v ); - - return count; -} - -int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC6, v ); - - return count; -} - -int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC7, v ); - - return count; -} - -int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) -{ - unsigned long v; - v = proc_pmc_conv_int( buffer, count ); - mtspr( PMC8, v ); - - return count; -} - - diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/ppc_asm.h linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/ppc_asm.h Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h Mon Aug 13 10:14:50 2001 @@ -72,6 +72,10 @@ stb ra,PACALPPACA+LPPACADECRINT(rb); /* Clear DECR int flag */\ 99: +#define CHECKANYINT(ra,rb) \ + mfspr rb,SPRG3; /* Get Paca address */\ + ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get pending interrupt flags */\ + cmpldi 0,ra,0; /* Macros to adjust thread priority for Iseries hardware multithreading */ #define HMT_LOW or 1,1,1 diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/ppc_defs.h linuxppc64_2_4/arch/ppc64/kernel/ppc_defs.h --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/ppc_defs.h Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_defs.h Wed Dec 31 18:00:00 1969 @@ -1,116 +0,0 @@ -/* The source for this file is automatically generated. Do not edit! */ -#define STATE 0 -#define NEXT_TASK 136 -#define COUNTER 64 -#define PROCESSOR 100 -#define SIGPENDING 16 -#define THREAD 1448 -#define MM 88 -#define ACTIVE_MM 152 -#define TASK_STRUCT_SIZE 1904 -#define KSP 0 -#define PACA 24 -#define PACA_SIZE 2080 -#define RTAS_ARGS_SIZE 88 -#define ITLPPACA_SIZE 640 -#define ITLPREGSAVE_SIZE 1024 -#define DCACHEL1LINESIZE 42 -#define DCACHEL1LOGLINESIZE 44 -#define DCACHEL1LINESPERPAGE 46 -#define ICACHEL1LINESIZE 48 -#define ICACHEL1LOGLINESIZE 50 -#define ICACHEL1LINESPERPAGE 52 -#define PHYSICALMEMORYSIZE 56 -#define PACAPACAINDEX 48 -#define PACAPROCSTART 52 -#define PACAKSAVE 40 -#define PACATHREAD 72 -#define PACACURRENT 16 -#define PACASAVEDMSR 56 -#define PACASAVEDLR 64 -#define PACASTABREAL 80 -#define PACASTABVIRT 88 -#define PACAR1 120 -#define PACAR21 24 -#define PACAR22 32 -#define PACALPQUEUE 128 -#define PACALPPACA 376 -#define PACATOC 112 -#define LPPACA 376 -#define LPREGSAV 1016 -#define LPPACASRR0 144 -#define LPPACASRR1 152 -#define LPPACADECRINT 132 -#define LPPACAIPIINT 131 -#define LPQCUREVENTPTR 16 -#define LPQOVERFLOW 0 -#define LPEVENTFLAGS 0 -#define PROMENTRY 0 -#define PROMARGS 32 -#define RTASBASE 8 -#define RTASENTRY 0 -#define RTASSIZE 16 -#define PGDIR 24 -#define LAST_SYSCALL 32 -#define PT_REGS 8 -#define PT_TRACESYS 2 -#define TASK_FLAGS 8 -#define TASK_PTRACE 48 -#define TASK_PERSONALITY 184 -#define NEED_RESCHED 40 -#define THREAD_FPR0 52 -#define THREAD_FPSCR 320 -#define THREAD_FLAGS 40 -#define PPC_FLAG_32BIT 1 -#define TASK_UNION_SIZE 16384 -#define STACK_FRAME_OVERHEAD 112 -#define INT_FRAME_SIZE 752 -#define GPR0 112 -#define GPR1 120 -#define GPR2 128 -#define GPR3 136 -#define GPR4 144 -#define GPR5 152 -#define GPR6 160 -#define GPR7 168 -#define GPR8 176 -#define GPR9 184 -#define GPR10 192 -#define GPR11 200 -#define GPR12 208 -#define GPR13 216 -#define GPR14 224 -#define GPR15 232 -#define GPR16 240 -#define GPR17 248 -#define GPR18 256 -#define GPR19 264 -#define GPR20 272 -#define GPR21 280 -#define GPR22 288 -#define GPR23 296 -#define GPR24 304 -#define GPR25 312 -#define GPR26 320 -#define GPR27 328 -#define GPR28 336 -#define GPR29 344 -#define GPR30 352 -#define GPR31 360 -#define _NIP 368 -#define _MSR 376 -#define _CTR 392 -#define _LINK 400 -#define _CCR 416 -#define _MQ 424 -#define _XER 408 -#define _DAR 440 -#define _DSISR 448 -#define _DEAR 440 -#define _ESR 448 -#define ORIG_GPR3 384 -#define RESULT 456 -#define TRAP 432 -#define CLONE_VM 256 -#define IRQ_CPUSTAT_SHIFT 7 -#define SOFTIRQ_PENDING 0 diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/proc_pmc.c linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/proc_pmc.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c Tue Aug 14 14:55:53 2001 @@ -25,10 +25,7 @@ * End Change Activity */ -#ifndef _PMC_PROC_H -#include -#endif - +#include #include #include #include diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/prom.c linuxppc64_2_4/arch/ppc64/kernel/prom.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/prom.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/prom.c Wed Aug 15 15:36:50 2001 @@ -425,7 +425,7 @@ if ( (_naca->processorCount = num_cpus) < 1 ) PROM_BUG(); - _naca->physicalMemorySize = _lmb->memory.size; + _naca->physicalMemorySize = lmb_phys_mem_size(); /* * Hardcode to GP size. I am not sure where to get this info @@ -491,9 +491,20 @@ unsigned long i, offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); union lmb_reg_property reg; - unsigned long lmb_base, lmb_size; + unsigned long mem_size, lmb_base, lmb_size; unsigned long num_regs, bytes_per_reg = (_prom->encode_phys_size*2)/8; +#if 1 + /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ + unsigned long io_base = 3UL<<30; + unsigned long io_size = 1UL<<30; + unsigned long have_630 = 1; /* assume we have a 630 */ + +#else + unsigned long io_base = ; + unsigned long io_size = ; +#endif + lmb_init(); for (node = 0; prom_next_node(&node); ) { @@ -516,11 +527,23 @@ lmb_size = reg.addr64[i].size; } + if ( lmb_addrs_overlap(lmb_base,lmb_size, + io_base,io_size) ) { + /* If we really have dram here, then we don't + * have a 630! -Peter + */ + have_630 = 0; + } if ( lmb_add(lmb_base, lmb_size) < 0 ) prom_print(RELOC("Too many LMB's, discarding this one...\n")); + else + mem_size =+ lmb_size; } } + + if ( have_630 && lmb_addrs_overlap(0,mem_size,io_base,io_size) ) + lmb_add_io(io_base, io_size); lmb_analyze(); diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/smp.c linuxppc64_2_4/arch/ppc64/kernel/smp.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/smp.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/smp.c Tue Aug 14 23:18:46 2001 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include "open_pic.h" @@ -71,6 +72,8 @@ volatile unsigned long __initdata tb_sync_flag = 0; volatile unsigned long __initdata tb_offset = 0; +extern unsigned char stab_array[]; + int start_secondary(void *); extern int cpu_idle(void *unused); void smp_call_function_interrupt(void); @@ -93,7 +96,8 @@ static void smp_xics_message_pass(int target, int msg, unsigned long data, int wait); static int smp_xics_probe(void); -/* static void smp_xics_setup_cpu(int cpu_nr); */ +void xics_setup_cpu(void); +void xics_cause_IPI(int cpu); /* * XICS only has a single IPI, so encode the messages per CPU @@ -187,8 +191,10 @@ np = 0; for (i=0; i < maxPacas; ++i) { lpPaca = xPaca[i].xLpPacaPtr; - if ( lpPaca->xDynProcStatus < 2 ) + if ( lpPaca->xDynProcStatus < 2 ) { ++np; + last_jiffy_stamp(i) = last_jiffy_stamp(0); + } } smp_tb_synchronized = 1; @@ -472,19 +478,26 @@ /* Wait for response */ timeout = 8000000; while (atomic_read(&data.started) != cpus) { + HMT_low(); if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", smp_processor_id(), atomic_read(&data.started)); +#ifdef CONFIG_XMON xmon(0); +#endif goto out; } barrier(); udelay(1); +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif } if (wait) { timeout = 1000000; while (atomic_read(&data.finished) != cpus) { + HMT_low(); if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); @@ -492,11 +505,15 @@ } barrier(); udelay(1); +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif } } ret = 0; out: + HMT_medium(); spin_unlock_bh(&call_lock); return ret; } @@ -528,6 +545,7 @@ int i, cpu_nr; struct task_struct *p; unsigned long sp; + unsigned long arpn; printk("Entering SMP Mode...\n"); PPCDBG(PPCDBG_SMP, "smp_boot_cpus: start. NR_CPUS = 0x%lx\n", NR_CPUS); @@ -560,11 +578,12 @@ * Other processor's tables are created and * initialized here. */ - paca->xStab_data.virt = - (unsigned long)btmalloc(PAGE_SIZE); - paca->xStab_data.real = ___pa(paca->xStab_data.virt); + + paca->xStab_data.virt = (unsigned long)&stab_array[PAGE_SIZE * (i-1)]; + arpn = physRpn_to_absRpn(___pa(paca->xStab_data.virt) >> 12); + paca->xStab_data.real = arpn << 12; paca->xHwProcNum = smp_hw_index[i]; - memset(paca->xStab_data.virt, 0, PAGE_SIZE); + memset((void *)paca->xStab_data.virt, 0, PAGE_SIZE); } PPCDBG(PPCDBG_SMP, "\tProcessor %d, stab virt = 0x%lx, stab real = 0x%lx\n", @@ -596,12 +615,17 @@ /* Probe arch for CPUs */ cpu_nr = smp_ops->probe(); + printk("Probe found %d CPUs\n", cpu_nr); + /* * only check for cpus we know exist. We keep the callin map * with cpus at the bottom -- Cort */ if (cpu_nr > max_cpus) cpu_nr = max_cpus; + + printk("Waiting for %d CPUs\n", cpu_nr-1); + for ( i = 1 ; i < cpu_nr; i++ ) { int c; struct pt_regs regs; @@ -640,7 +664,7 @@ sp = ((unsigned long)p) + sizeof(union task_union) - STACK_FRAME_OVERHEAD; PPCDBG(PPCDBG_SMP, "\tstack pointer virt = 0x%lx\n", sp); - current_set[i].sp_real = ___pa(sp); + current_set[i].sp_real = (void *)___pa(sp); PPCDBG(PPCDBG_SMP,"\tstack pointer real = 0x%lx\n", current_set[i].sp_real); @@ -652,8 +676,12 @@ * use this value that I found through experimentation. * -- Cort */ - for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) + for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) { +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif udelay(100); + } if ( cpu_callin_map[i] ) { @@ -705,8 +733,12 @@ */ cpu_callin_map[cpu] = 1; - while(!smp_commenced) + while(!smp_commenced) { +#ifdef CONFIG_PPC_ISERIES + HvCallCfg_getLps(); +#endif barrier(); + } __sti(); } diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/stab.c linuxppc64_2_4/arch/ppc64/kernel/stab.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/stab.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/stab.c Fri Aug 10 15:18:54 2001 @@ -44,8 +44,12 @@ if ( skip_esid_c ) esid += (REGION_STRIDE >> SID_SHIFT); - last_esid = GET_ESID(KERNELBASE) + - ((REGION_COUNT - 2) * (REGION_STRIDE >> SID_SHIFT)); + /* + * Put entries into the STAB for the first segment of the 4 kernel + * regions (0xC - 0xF). These are left in the table during flush. + * All other kernel segments (including bolted) are demand faulted. + */ + last_esid = GET_ESID(KERNELBASE) + (3 * (REGION_STRIDE >> SID_SHIFT)); for (; esid <= last_esid; esid += (REGION_STRIDE >> SID_SHIFT)) { /* Map the esid to an EA & find the vsid we need. */ @@ -116,9 +120,10 @@ castout_ste = ste + (castout_entry - 8); } - if(REGION_ID((castout_ste->dw0.dw0.esid) << SID_SHIFT) == - USER_REGION_ID) { - /* Found a non-kernel entry to castout */ + if((((castout_ste->dw0.dw0.esid) >> 32) == 0) || + (((castout_ste->dw0.dw0.esid) & 0xffffffff) > 0)) { + /* Found an entry to castout. It is either a user */ + /* region, or a secondary kernel segment. */ break; } @@ -295,10 +300,7 @@ if(!__is_processor(PV_POWER4)) { unsigned long entry; - STE *ste = stab; - - /* Never flush the first four entries (kernel segments) */ - ste += (REGION_COUNT-1); + STE *ste; /* Force previous translations to complete. DRENG */ __asm__ __volatile__ ("isync" : : : "memory"); @@ -311,10 +313,12 @@ PMC_SW_PROCESSOR(stab_invalidations); } } else { - /* Invalidate all entries except the four - * kernel segments. - */ - for(entry = REGION_COUNT - 1; + /* Invalidate all entries. */ + ste = stab; + + /* Never flush the first four entries. */ + ste += 4; + for(entry = 4; entry < (PAGE_SIZE / sizeof(STE)); entry++, ste++) { ste->dw0.dw0.v = 0; diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/time.c linuxppc64_2_4/arch/ppc64/kernel/time.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/kernel/time.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/time.c Wed Aug 15 09:25:00 2001 @@ -79,13 +79,14 @@ extern rwlock_t xtime_lock; extern int piranha_simulator; -unsigned long tb_ticks_per_jiffy; -unsigned long tb_ticks_per_usec; +unsigned long tb_ticks_per_jiffy = 5000000; /* initial sane values */ +unsigned long tb_ticks_per_usec = 500; unsigned long tb_to_us; unsigned long tb_last_stamp; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; extern unsigned long wall_jiffies; +extern unsigned long lpEvent_count; static long time_offset = 0; @@ -152,6 +153,9 @@ unsigned long cpu = smp_processor_id(); unsigned long jiffy_stamp = last_jiffy_stamp(cpu); + struct Paca * paca; + struct ItLpQueue * lpq; + irq_enter(cpu); if (!user_mode(regs)) @@ -203,6 +207,12 @@ if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) ppc_md.heartbeat(); + + paca = (struct Paca *)mfspr(SPRG3); + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + lpq = paca->lpQueuePtr; + if ( lpq ) + lpEvent_count += ItLpQueue_process( lpq, regs ); irq_exit(cpu); return 1; /* lets ret_from_int know we can do checks */ diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/mm/btmalloc.c linuxppc64_2_4/arch/ppc64/mm/btmalloc.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/mm/btmalloc.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/mm/btmalloc.c Mon Aug 13 14:00:44 2001 @@ -16,13 +16,7 @@ #include extern pgd_t *bolted_pgd; -extern struct vmalloc_struct btmalloc_ops; -extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, - struct vmalloc_struct *v); extern pte_t *find_linux_pte(pgd_t *pgdir, unsigned long ea); -extern void build_valid_hpte(unsigned long vsid, unsigned long ea, - unsigned long pa, pte_t *ptep, unsigned hpteflags, - unsigned bolted ); /* * This lock guarantees that only one task will be attempting @@ -30,186 +24,406 @@ */ static spinlock_t bolted_pte_lock = SPIN_LOCK_UNLOCKED; -pmd_t *pmd_alloc_b(pgd_t *pgd, unsigned long address) +/* This lock protects the btmlist and serializes the removal + * of entries from the bolted page table + */ +rwlock_t btmlist_lock = RW_LOCK_UNLOCKED; + +struct vm_struct * btmlist = NULL; + +void* get_free_bolted_pages(int gfp_mask, unsigned long order) { - pmd_t *pmd; void *page; - unsigned long ea, pa, vsid; - struct vm_struct *area; - if (!pgd_present(*pgd)) { - spin_unlock(&bolted_pte_lock); - page = (void *)__get_free_page(GFP_KERNEL); - spin_lock(&bolted_pte_lock); - if (!page) - return NULL; - - /* - * Because we dropped the lock, we should re-check the - * entry, as somebody else could have populated it.. - */ - if (pgd_present(*pgd)) { - __free_pages(page, 0); - goto out; - } - - clear_page(page); - /* allocate space in the bolted address range */ - area = __get_vm_area(PAGE_SIZE, 0, &btmalloc_ops); - ea = (unsigned long)area->addr; - pa = __pa(page); - vsid = get_kernel_vsid(ea); - build_valid_hpte(vsid, ea, pa, NULL, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1); - pmd = (pmd_t *)ea; - pgd_set(pgd, pmd); - } -out: - return pmd_offset(pgd, address); + page = btmalloc(PAGE_SIZE * (1 << order)); + return(page); } -pte_t *pte_alloc_b(pmd_t *pmd, unsigned long address) +static int local_free_bolted_pages(void* page, unsigned long num) { - pte_t *pte; - void *page; - struct vm_struct *area; - unsigned long ea, pa, vsid; + int rc = 0; + u32 cntr = 0; + char* pageAddr = (char*)page; + pte_t * ptep, pte; - if (!pmd_present(*pmd)) { - spin_unlock(&bolted_pte_lock); - page = (void *)__get_free_page(GFP_KERNEL); - spin_lock(&bolted_pte_lock); - if (!page) - return NULL; - - /* - * Because we dropped the lock, we should re-check the - * entry, as somebody else could have populated it.. - */ - if (pmd_present(*pmd)) { - __free_pages(page, 0); - goto out; - } +/* Need to find the memmap array entry for the first real page of the range + * (this is where the reference count is maintained). - clear_page(page); - /* allocate space in the bolted address range */ - area = __get_vm_area(PAGE_SIZE, 0, &btmalloc_ops); - ea = (unsigned long)area->addr; - pa = __pa(page); - vsid = get_kernel_vsid(ea); - build_valid_hpte(vsid, ea, pa, NULL, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1); - pte = (pte_t *)ea; - pmd_set(pmd, pte); + * It is assumed that the btmlist_lock is held to prevent changes + * to the bolted area page table while we search it + */ + + + ptep = find_linux_pte( bolted_pgd, (unsigned long)pageAddr ); + + if ( !ptep ) { + write_unlock(&btmlist_lock); + panic("free_bolted_pages - bolted page is NOT in the bolted page table"); + } + + /* find_linux_pte has assured us that the pte is PRESENT */ + + /* Check the page reference count on the first page of the range + * to insure that we only free the range when the last reference is freed. + */ + if ( page_count(pte_page(*ptep)) == 1 ) { + /* we are the last user of this range of pages - actually free the pages. */ + for (cntr=0; cntrcount)); +/* put_page_testzero(pte_page(pte)); */ + } + + return rc; } -static unsigned long btmalloc_start(void) +struct vm_struct *get_btm_area(unsigned long size, unsigned long flags); +long btmallocread(char *buf, char *addr, unsigned long count); + +extern void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, + pte_t * ptep, unsigned hpteflags, unsigned bolted ); + +void* btmalloc (unsigned long size) { - return BTMALLOC_START; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + + unsigned long ea, eaddr; + struct vm_struct *area; + unsigned long pa, vsid, pg_count, page; + + size = PAGE_ALIGN(size); + if (!size || (size >> PAGE_SHIFT) > num_physpages) { + BUG(); + return NULL; + } + + /* Get a virtual address region in the bolted space */ + area = get_btm_area(size, 0); + if (!area) { + BUG(); + return NULL; + } + + ea = (unsigned long) area->addr; + pg_count = (size >> PAGE_SHIFT); + +/* printk("btmalloc: allocated range %016lx for %ld pages\n", ea, pg_count ); */ + + + /* Create a Linux page table entry and an HPTE for each page */ + for(page = 0; page < pg_count; page++) { + + pa = get_free_page(GFP_KERNEL) - PAGE_OFFSET; + + eaddr = ea + (page * PAGE_SIZE); + + /* We need a lock here to avoid multiple tasks doing + * pmd_alloc_b and/or pte_alloc_b simultaneously + */ + spin_lock(&bolted_pte_lock); + /* Get a pointer to the linux page table entry for this page + * allocating pmd or pte pages along the way as needed + */ + pgdp = pgd_offset_b(eaddr); + pmdp = pmd_alloc_b(pgdp, eaddr); + ptep = pte_alloc_b(pmdp, eaddr); + /* At this point we are done allocating any new pmd/pte pages + * We own the pte (ptep) by nature of owning the eaddr so we + * no longer need the lock + */ + spin_unlock(&bolted_pte_lock); + + /* Clear any old hpte and set the new linux pte */ + set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, PAGE_KERNEL)); + + PPCDBG(PPCDBG_MM, "In btmalloc: ea = %lx, pa = %lx\n", eaddr, pa); + +/* printk("btmalloc: set_pte at ptep=%016lx, pa=%016lx\n", (unsigned long)ptep, pa ); */ + + vsid = get_kernel_vsid(eaddr); + +/* printk("btmalloc: build_valid_hpte: ea=%016lx, pa=%016lx, vsid=%016lx, ptep=%016lx\n", + eaddr, pa, vsid, (unsigned long)ptep ); +*/ + build_valid_hpte( vsid, eaddr, pa, ptep, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1 ); + } + + return (void*)ea; } -static unsigned long btmalloc_end(void) +/* + * get_btm_area + * + * Get a virtual region in the bolted space + * + * This function is also called when allocating pages + * for pmd's/pte's for the bolted page table itself. + * + */ + +struct vm_struct *get_btm_area(unsigned long size, unsigned long flags) { - return BTMALLOC_END; + unsigned long addr; + struct vm_struct **p, *tmp, *area; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + + write_lock(&btmlist_lock); + + addr = BTMALLOC_START; + for (p = &btmlist; (tmp = *p) ; p = &tmp->next) { + if (size + addr < (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if (addr > BTMALLOC_END-size) { + write_unlock(&btmlist_lock); + kfree(area); + return NULL; + } + } + + area->flags = flags; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&btmlist_lock); + return area; } -static int btmalloc_area_pages(unsigned long address, unsigned long size, - int gfp_mask, pgprot_t prot); -static void btfree_area_pages(unsigned long address, unsigned long size); +/* + * Free a range of bolted pages that were allocated with btmalloc + */ -struct vmalloc_struct btmalloc_ops = { - lock: RW_LOCK_UNLOCKED, - start: btmalloc_start, - end: btmalloc_end, - alloc_area_pages: btmalloc_area_pages, - free_area_pages: btfree_area_pages, -}; +char btfree_dummy; -static void btfree_area_pages(unsigned long address, unsigned long size) -{ - pte_t *ptep; - pte_t pte; - unsigned long num; +extern void * _get_SP(void); - num = (size >> PAGE_SHIFT); +void btfree(void * addr) { - while(num--) { - ptep = find_linux_pte(bolted_pgd, address); - if (!ptep) { - write_unlock(&btmalloc_ops.lock); - panic("btfree_area_pages - page being freed (%016lx) is not bolted", address); - } - pte = *ptep; - pte_clear(ptep); - flush_hash_page( 0, address, pte ); - __free_pages(pte_page(pte), 0); - address += PAGE_SIZE; + struct vm_struct **p, *tmp; + unsigned long size; + + if ( !addr ) { + udbg_printf("btfree - bad address, returning w/o freeing - bad address=%p\n", addr); + return; } -} -static int btmalloc_area_pages(unsigned long address, unsigned long size, - int gfp_mask, pgprot_t prot) -{ - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - unsigned long eaddr; - unsigned long pa, vsid, pg_count, page; - - pg_count = (size >> PAGE_SHIFT); + if ( ( PAGE_SIZE-1 ) & (unsigned long)addr ) { + udbg_printf("btfree - trying to btfree() bad address=%p\n", addr); + printk(KERN_ERR "Trying to btfree() bad address (%p)\n", addr); + return; + } - /* Create a Linux page table entry and an HPTE for each page */ - for(page = 0; page < pg_count; page++) { - pa = get_free_page(GFP_KERNEL) - PAGE_OFFSET; - eaddr = address + (page * PAGE_SIZE); + size = 0; - /* - * We need a lock here to avoid multiple tasks doing - * pmd_alloc_b and/or pte_alloc_b simultaneously - */ - spin_lock(&bolted_pte_lock); + write_lock( &btmlist_lock ); - /* - * Get a pointer to the linux page table entry for this page - * allocating pmd or pte pages along the way as needed - */ - pgdp = pgd_offset_b(eaddr); - pmdp = pmd_alloc_b(pgdp, eaddr); - ptep = pte_alloc_b(pmdp, eaddr); + /* Scan the bolted memory list for an entry matching + * the address to be freed, get the size (in bytes) + * and free the entry. The list lock is not dropped + * until the page table entries are removed. + */ + for ( p = &btmlist; (tmp = *p); p = &tmp->next ) { + if ( tmp->addr == addr ) { + size = tmp->size; +/* *p = tmp->next; + kfree(tmp); + */ + break; + } + } - /* - * At this point we are done allocating any new pmd/pte pages - * We own the pte (ptep) by nature of owning the eaddr so we - * no longer need the lock - */ - spin_unlock(&bolted_pte_lock); + /* If no entry found, it is an error */ + if ( !size ) { + write_unlock(&btmlist_lock); +/* udbg_printf("btfree - trying to btfree() bad address=%p\n", addr ); */ + printk(KERN_ERR "Trying to btfree() bad address (%p)\n", addr ); + print_backtrace( _get_SP() ); + btfree_dummy = *((char *)0); // Force dump of registers here + return; + } - /* Clear any old hpte and set the new linux pte */ - set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, PAGE_KERNEL)); + /* Free up the bolted pages and remove the page table entries */ + if ( local_free_bolted_pages( addr, size >> PAGE_SHIFT ) ) { + /* if we actually freed the pages (not just decremented use count) + * then free the list entry + */ + *p = tmp->next; + kfree(tmp); + } - vsid = get_kernel_vsid(eaddr); + write_unlock(&btmlist_lock); +} - build_valid_hpte(vsid, eaddr, pa, ptep, - _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1); +long btmallocread(char *buf, char *addr, unsigned long count) { + struct vm_struct *tmp; + char *vaddr, *buf_start = buf; + unsigned long n; + + /* Don't allow overflow */ + if ((unsigned long) addr + count < count) + count = -(unsigned long) addr; + + read_lock(&btmlist_lock); + for (tmp = btmlist; tmp; tmp = tmp->next) { + vaddr = (char *) tmp->addr; + if (addr >= vaddr + tmp->size - PAGE_SIZE) + continue; + while (addr < vaddr) { + if (count == 0) + goto finished; + *buf = '\0'; + buf++; + addr++; + count--; + } + n = vaddr + tmp->size - PAGE_SIZE - addr; + do { + if (count == 0) + goto finished; + *buf = *addr; + buf++; + addr++; + count--; + } while (--n > 0); } +finished: + read_unlock(&btmlist_lock); + return buf - buf_start; +} + + +pmd_t *pmd_alloc_b(pgd_t *pgd, unsigned long address) +{ + pmd_t *pmd; + void *page; + unsigned long ea, pa, vsid; + struct vm_struct *area; + + address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1); + if (pgd_none(*pgd)) { + // allocate space in the bolted address range + area = get_btm_area( 4096, 0 ); + ea = (unsigned long)area->addr; + page = (void *)__get_free_page(GFP_KERNEL); + clear_page(page); + pa = ((unsigned long)page - PAGE_OFFSET ); + vsid = get_kernel_vsid( ea ); - return 0; +/* printk("pmd_alloc_b: building hpte for bolted page table pmd\n"); +printk(" ea = %016lx\n", ea ); + printk(" pa = %016lx\n", pa ); + printk(" vsid = %016lx\n", vsid ); + printk(" pgd = %016lx\n", (unsigned long)pgd ); +*/ + build_valid_hpte( vsid, ea, pa, NULL, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1 ); + pmd = (pmd_t *)ea; + pgd_set(pgd, pmd); + return pmd + address; + } + if (pgd_bad(*pgd)) { + __bad_pmd(pgd); + return NULL; + } + return (pmd_t *) pgd_page(*pgd) + address; } -void btfree(void *addr) +pte_t *pte_alloc_b(pmd_t *pmd, unsigned long address) { - __vfree(addr, &btmalloc_ops); + pte_t *pte; + void *page; + struct vm_struct *area; + unsigned long ea, pa, vsid; + + address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); + if (pmd_none(*pmd)) { + // allocate space in the bolted address range + area = get_btm_area( 4096, 0 ); + ea = (unsigned long)area->addr; + page = (void *)__get_free_page(GFP_KERNEL); + clear_page(page); + pa = ((unsigned long)page - PAGE_OFFSET ); + vsid = get_kernel_vsid( ea ); + +/* printk("pte_alloc_b: building hpte for bolted page table pte\n"); + printk(" ea = %016lx\n", ea ); + printk(" pa = %016lx\n", pa ); + printk(" vsid = %016lx\n", vsid ); + printk(" pmd = %016lx\n", (unsigned long)pmd ); +*/ + + build_valid_hpte( vsid, ea, pa, NULL, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, 1 ); + pte = (pte_t *)ea; + pmd_set(pmd, pte); + return pte + address; + } + if (pmd_bad(*pmd)) { + __bad_pte(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + address; } -void *btmalloc(unsigned long size) +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t *empty_bad_page_table; + +pte_t * __bad_pagetable(void) { - return ___vmalloc(size, 0, 0, &btmalloc_ops); + clear_page(empty_bad_page_table); + return empty_bad_page_table; } -long btread(char *buf, char *addr, unsigned long count) +void *empty_bad_page; + +pte_t __bad_page(void) { - return __vread(buf, addr, count, &btmalloc_ops); + clear_page(empty_bad_page); + return pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED)); } diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/arch/ppc64/mm/init.c linuxppc64_2_4/arch/ppc64/mm/init.c --- ../w3.wed/linux-2.4.8-ac5/arch/ppc64/mm/init.c Wed Aug 15 12:11:30 2001 +++ linuxppc64_2_4/arch/ppc64/mm/init.c Wed Aug 15 17:01:37 2001 @@ -277,8 +277,8 @@ ea = (unsigned long)(area->addr); } else { - ea = ioremap_bot; - ioremap_bot += size; + ea = ioremap_bot; + ioremap_bot += size; } if ((flags & _PAGE_PRESENT) == 0) @@ -329,33 +329,33 @@ pte_t *ptep; unsigned long vsid; - if(mem_init_done) { - spin_lock(&init_mm.page_table_lock); - pgdp = pgd_offset_i(ea); - pmdp = pmd_alloc(&init_mm, pgdp, ea); - ptep = pte_alloc(&init_mm, pmdp, ea); - set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); - spin_unlock(&init_mm.page_table_lock); - } else { - /* If the mm subsystem is not fully up, we cannot create a linux - * page table entry for this mapping. Simply bolt an entry in the - * hardware page table. - */ - vsid = get_kernel_vsid(ea); - make_pte(htab_data.htab, - (vsid << 28) | (ea & 0xFFFFFFF), // va (NOT the ea) - pa, - _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX, - htab_data.htab_hash_mask); - } + if (mem_init_done) { + spin_lock(&init_mm.page_table_lock); + pgdp = pgd_offset_i(ea); + pmdp = pmd_alloc(&init_mm, pgdp, ea); + ptep = pte_alloc(&init_mm, pmdp, ea); + set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); + spin_unlock(&init_mm.page_table_lock); + } else { + /* If the mm subsystem is not fully up, we cannot create a + * linux page table entry for this mapping. Simply bolt an + * entry in the hardware page table. + */ + vsid = get_kernel_vsid(ea); + make_pte(htab_data.htab, + (vsid << 28) | (ea & 0xFFFFFFF), // va (NOT the ea) + pa, + _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX, + htab_data.htab_hash_mask); + } } void local_flush_tlb_all(void) { -/* - * Implemented to just flush the vmalloc area. vmalloc is the only user of flush_tlb_all - */ + /* Implemented to just flush the vmalloc area. + * vmalloc is the only user of flush_tlb_all. + */ local_flush_tlb_range( NULL, VMALLOC_START, VMALLOC_END ); } @@ -367,7 +367,7 @@ for ( mp = mm->mmap; mp != NULL; mp = mp->vm_next ) local_flush_tlb_range( mm, mp->vm_start, mp->vm_end ); } - else + else /* MIKEC: It is not clear why this is needed */ local_flush_tlb_range( mm, USER_START, USER_END ); } @@ -428,10 +428,10 @@ unsigned long context; if ( start >= end ) - return; + panic("flush_tlb_range: start (%016lx) greater than end (%016lx)\n", start, end ); if ( REGION_ID(start) != REGION_ID(end) ) - panic("flush_tlb_range start (%016lx) and end (%016lx) not in same region\n", start, end ); + panic("flush_tlb_range: start (%016lx) and end (%016lx) not in same region\n", start, end ); context = 0; @@ -450,7 +450,7 @@ context = mm->context; break; default: - return; + panic("flush_tlb_range: invalid region for start (%016lx) and end (%016lx)\n", start, end); } @@ -577,7 +577,7 @@ */ bootmap_pages = bootmem_bootmap_pages(total_pages); - start = __a2p(lmb_alloc(bootmap_pages< +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an IDE drive + * + * If we are using our own majors, we only support 3 partitions per physical + * disk....so with minor numbers 0-255 we get a maximum of 64 disks. If we + * are emulating IDE, we get 16 partitions per disk, with a maximum of 16 + * disks + ***************************************************************************/ +#ifdef CONFIG_VIODASD_IDE +#define MAJOR_NR IDE0_MAJOR +#define PARTITION_SHIFT 6 +#define do_viodasd_request do_hd_request +static int numdsk = 16; +static int viodasd_max_disk = 16; +#else +#define MAJOR_NR VIODASD_MAJOR +#define PARTITION_SHIFT 2 +static int numdsk = 64; +static int viodasd_max_disk = 64; +#endif + +#define VIODASD_VERS "1.01" +#define LOCAL_END_REQUEST + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +MODULE_DESCRIPTION("iSeries Virtual DASD"); +MODULE_AUTHOR("Dave Boutcher"); + +/*************************************************************************** + * In a perfect world we will perform better if we get page-aligned I/O + * requests, in multiples of pages. At least peg our block size fo the + * actual page size. + ***************************************************************************/ +static int blksize = HVPAGESIZE; /* in bytes */ + +static DECLARE_WAIT_QUEUE_HEAD(viodasd_wait); +struct viodasd_waitevent { + struct semaphore *sem; + int rc; + int changed; /* Used only for check_change */ +}; + +/*************************************************************************** + * All our disk-related global structures + ***************************************************************************/ +static struct hd_struct *viodasd_partitions; +static int *viodasd_sizes; +static int *viodasd_sectsizes; +static int *viodasd_maxsectors; +extern struct gendisk viodasd_gendsk; + +/*************************************************************************** + * Figure out the biggest I/O request (in sectors) we can accept + ***************************************************************************/ +#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA) + +/*************************************************************************** + * Keep some statistics on what's happening for the PROC file system + ***************************************************************************/ +static struct { + long tot; + long nobh; + long ntce[VIOMAXBLOCKDMA]; +} viod_stats[64][2]; + +/*************************************************************************** + * Number of disk I/O requests we've sent to OS/400 + ***************************************************************************/ +static int numReqOut; + +/*************************************************************************** + * Our internal printk macro + ***************************************************************************/ +#define err_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERR: " fmt, __FILE__, __LINE__ , ## args) + +/*************************************************************************** + * This is our internal structure for keeping track of disk devices + ***************************************************************************/ +struct viodasd_device { + int useCount; + u16 cylinders; + u16 tracks; + u16 sectors; + u16 bytesPerSector; + u64 size; + int readOnly; +} *viodasd_devices; + +/*************************************************************************** + * When we get a disk I/O request we take it off the general request queue + * and put it here. + ***************************************************************************/ +LIST_HEAD(reqlist); + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + int j; + +#if defined(MODULE) + len += sprintf(buf+len,"viod Module opened %d times. Major number %d\n", + MOD_IN_USE, + MAJOR_NR); +#endif + len += sprintf(buf+len,"viod %d devices\n", + numdsk); + + for (i=0; i<16; i++) + { + if (viod_stats[i][0].tot || viod_stats[i][1].tot) + { + len += sprintf(buf+len,"DISK %2.2d: rd %-10.10ld wr %-10.10ld (no buffer list rd %-10.10ld wr %-10.10ld\n", + i, viod_stats[i][0].tot,viod_stats[i][1].tot,viod_stats[i][0].nobh,viod_stats[i][1].nobh); + + len += sprintf(buf+len,"rd DMA: "); + + for (j=0; jnlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viodasd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viodasd", iSeries_proc); +} + +/*************************************************************************** + * End a request + ***************************************************************************/ +static void viodasd_end_request(struct request *req, int uptodate) { + + if (end_that_request_first(req, uptodate, DEVICE_NAME)) + return; + + end_that_request_last(req); +} + +/*************************************************************************** + * This rebuilds the partition information for a single disk device + ***************************************************************************/ +static int viodasd_revalidate(kdev_t dev) +{ + int i; + int device_no = DEVICE_NR(dev); + int part0 = (device_no << PARTITION_SHIFT); + int npart = (1<=0;i--) + { + minor = part0 +i; + + if (viodasd_partitions[minor].nr_sects != 0) + { + devp = MKDEV(MAJOR_NR, minor); + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); + } + + viodasd_partitions[minor].start_sect = 0; + viodasd_partitions[minor].nr_sects = 0; + } + + grok_partitions(&viodasd_gendsk, device_no, npart, viodasd_devices[device_no].size>>9); + + return 0; +} + +/*************************************************************************** + * This is the actual open code. It gets called from the external + * open entry point, as well as from the init code when we're figuring + * out what disks we have + ***************************************************************************/ +static int internal_open(int device_no) +{ + int i; + struct viodasd_waitevent we; + + HvLpEvent_Rc hvrc; + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code */ + if (we.rc != 0) + { + err_printk("bad rc opening disk: %d\n",(int)we.rc); + return we.rc; + } + + /* If this is the first open of this device, update the device information */ + /* If this is NOT the first open, assume that it isn't changing */ + if (viodasd_devices[device_no].useCount == 0) + { + if (viodasd_devices[device_no].size > 0) + { + /* divide by 512 */ + u64 tmpint = viodasd_devices[device_no].size >> 9; + viodasd_partitions[device_no << PARTITION_SHIFT].nr_sects = tmpint; + /* Now the value divided by 1024 */ + tmpint = tmpint >> 1; + viodasd_sizes[device_no << PARTITION_SHIFT] = tmpint; + + for (i = (device_no << PARTITION_SHIFT); + i < ((device_no+1) << PARTITION_SHIFT); + i++) + viodasd_sectsizes[i] = viodasd_devices[device_no].bytesPerSector; + + } + } + else + { + /* If the size of the device changed, wierd things are happening! */ + if (viodasd_sizes[device_no<> 10) + { + err_printk("disk size change (%dK to %dK) for device %d\n", + viodasd_sizes[device_no<> 10, + device_no); + } + } + + /* Bump the use count */ + viodasd_devices[device_no].useCount++; + + return 0; +} + +/*************************************************************************** + * This is the actual release code. It gets called from the external + * release entry point, as well as from the init code when we're figuring + * out what disks we have + ***************************************************************************/ +static int internal_release(int device_no) +{ + + /* Send the event to OS/400. We DON'T expect a response */ + HvLpEvent_Rc hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + viodasd_devices[device_no].useCount--; + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + return 0; +} + +/*************************************************************************** + * External open entry point. + ***************************************************************************/ +static int viodasd_open(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) + { + err_printk("no inode provided in open\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on open\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + err_printk("Invalid minor device number %d in open\n",device_no); + return -ENODEV; + } + + /* Call the actual open code */ + if (internal_open(device_no) == 0) + { + if (fil && fil->f_mode) + { + if (fil->f_mode & 2) + { + if (viodasd_devices[device_no].readOnly) { + internal_release( device_no ); + return -EROFS; + } + } + } + MOD_INC_USE_COUNT; + return 0; + } + else + { + return -EIO; + } +} + +/*************************************************************************** + * External release entry point. + ***************************************************************************/ +static int viodasd_release(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) + { + err_printk("no inode provided in release\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on release\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + return -ENODEV; + } + + /* Just to be paranoid, sync the device */ + fsync_dev(ino->i_rdev); + + /* Call the actual release code */ + internal_release(device_no); + + MOD_DEC_USE_COUNT; + return 0; +} + +/*************************************************************************** + * External ioctl entry point. + ***************************************************************************/ +static int viodasd_ioctl(struct inode *ino, struct file *fil, unsigned int cmd, unsigned long arg) +{ + int device_no; + int err; + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Sanity checks */ + if (!ino) + { + err_printk("no inode provided in ioctl\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) + { + err_printk("Wierd error...wrong major number on ioctl\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) + { + err_printk("Invalid minor device number %d in ioctl\n",device_no); + return -ENODEV; + } + + switch(cmd) { + case BLKGETSIZE: + /* return the device size in sectors */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (err) return err; + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].nr_sects, (long *)arg); + return 0; + + case FDFLUSH: + case BLKFLSBUF: + if (!suser()) return -EACCES; + fsync_dev(ino->i_rdev); + invalidate_buffers(ino->i_rdev); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockflush, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&Semaphore, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on sync signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + return 0; + + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (err) return err; + put_user(read_ahead[MAJOR_NR], (long *)arg); + return 0; + + case BLKRASET: + if (!suser()) return -EACCES; + if (arg > 0x00ff) return -EINVAL; + read_ahead[MAJOR_NR] = arg; + return 0; + + case BLKRRPART: + viodasd_revalidate(ino->i_rdev); + return 0; + + case HDIO_GETGEO: + { + unsigned char sectors; + unsigned char heads; + unsigned short cylinders; + + struct hd_geometry *geo = (struct hd_geometry *)arg; + if (geo == NULL) return -EINVAL; + + err = verify_area(VERIFY_WRITE, geo, sizeof(*geo)); + if (err) return err; + + sectors = viodasd_devices[device_no].sectors; + if (sectors == 0) + sectors = 32; + + heads = viodasd_devices[device_no].tracks; + if (heads == 0) + heads = 64; + + cylinders = viodasd_devices[device_no].cylinders; + if (cylinders == 0) + cylinders = viodasd_partitions[MINOR(ino->i_rdev)].nr_sects / (sectors * heads); + + put_user(sectors , &geo->sectors ); + put_user(heads , &geo->heads ); + put_user(cylinders, &geo->cylinders); + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].start_sect,(long *)&geo->start); + + return 0; + } + +#define PRTIOC(x) case x: err_printk("got unsupported FD ioctl " #x "\n"); \ + return -EINVAL; + + PRTIOC(FDCLRPRM); + PRTIOC(FDSETPRM); + PRTIOC(FDDEFPRM); + PRTIOC(FDGETPRM); + PRTIOC(FDMSGON); + PRTIOC(FDMSGOFF); + PRTIOC(FDFMTBEG); + PRTIOC(FDFMTTRK); + PRTIOC(FDFMTEND); + PRTIOC(FDSETEMSGTRESH); + PRTIOC(FDSETMAXERRS); + PRTIOC(FDGETMAXERRS); + PRTIOC(FDGETDRVTYP); + PRTIOC(FDSETDRVPRM); + PRTIOC(FDGETDRVPRM); + PRTIOC(FDGETDRVSTAT); + PRTIOC(FDPOLLDRVSTAT); + PRTIOC(FDRESET); + PRTIOC(FDGETFDCSTAT); + PRTIOC(FDWERRORCLR); + PRTIOC(FDWERRORGET); + PRTIOC(FDRAWCMD); + PRTIOC(FDEJECT); + PRTIOC(FDTWADDLE); + + } + + return -EINVAL; +} + +/*************************************************************************** + * Send an actual I/O request to OS/400 + ***************************************************************************/ +static int send_request(struct request *req) +{ + u64 sect_size; + u64 start; + u64 len; + int direction; + int nsg; + u16 viocmd; + HvLpEvent_Rc hvrc; + struct vioblocklpevent *bevent = (struct vioblocklpevent *)vio_block_event_buffer; + struct scatterlist sg[VIOMAXBLOCKDMA]; + struct buffer_head *bh; + int sgindex; + int device_no = DEVICE_NR(req->rq_dev); + int statindex; + + /* Note that this SHOULD always be 512...but lets be architecturally correct */ + sect_size = hardsect_size[MAJOR_NR][device_no]; + + /* Figure out teh starting sector and length */ + start = (req->sector + viodasd_partitions[MINOR(req->rq_dev)].start_sect) * sect_size; + len = req->nr_sectors * sect_size; + + /* More paranoia checks */ + if ((req->sector + req->nr_sectors) > + (viodasd_partitions[MINOR(req->rq_dev)].start_sect + + viodasd_partitions[MINOR(req->rq_dev)].nr_sects)) + { + err_printk("Invalid request offset & length\n"); + err_printk("req->sector: %ld, req->nr_sectors: %ld\n", req->sector, req->nr_sectors); + err_printk("RQ_DEV: %d, minor: %d\n", req->rq_dev, MINOR(req->rq_dev)); + return -1; + } + + if (req->cmd == READ) + { + direction = PCI_DMA_FROMDEVICE; + viocmd = viomajorsubtype_blockio | vioblockread; + statindex = 0; + } + else + { + direction = PCI_DMA_TODEVICE; + viocmd = viomajorsubtype_blockio | vioblockwrite; + statindex = 1; + } + + /* Update totals */ + viod_stats[device_no][statindex].tot++; + + /* Now build the scatter-gather list */ + memset(&sg, 0x00, sizeof(sg)); + sgindex = 0; + + /* See if this is a swap I/O (without a bh pointer) or a regular I/O */ + if (req->bh) + { + /* OK...this loop takes buffers from the request and adds them to the SG + until we're done, or until we hit a maximum. If we hit a maximum we'll + just finish this request later */ + bh = req->bh; + while ((bh) && (sgindex < VIOMAXBLOCKDMA)) + { + sg[sgindex].address = bh->b_data; + sg[sgindex].length = bh->b_size; + + sgindex++; + bh = bh->b_reqnext; + } + nsg = pci_map_sg(NULL, sg, sgindex, direction); + if ((nsg == 0) || (sg[0].dma_length == 0) || (sg[0].dma_address == 0xFFFFFFFF)) + { + err_printk("error getting sg tces\n"); + return -1; + } + + } + else + { + /* Update stats */ + viod_stats[device_no][statindex].nobh++; + + sg[0].dma_address = pci_map_single(NULL, req->buffer, + len, + direction); + if (sg[0].dma_address == 0xFFFFFFFF) + { + err_printk("error allocating tce for address %p len %ld\n", + req->buffer, + (long)len); + return -1; + } + sg[0].dma_length = len; + nsg = 1; + } + + /* Update stats */ + viod_stats[device_no][statindex].ntce[sgindex]++; +#if 0 + /* This optimization handles a single DMA block */ + if (sgindex == 1) + { + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | viocmd, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)req->buffer, + VIOVERSION << 16, + ((u64)device_no << 48), + start, + ((u64)sg[0].dma_address)<<32, + sg[0].dma_length); + } + else +#endif + { + /* Now build up the actual request. Note that we store the pointer */ + /* to the request buffer in the correlation token so we can match */ + /* this response up later */ + memset(bevent,0x00,sizeof(struct vioblocklpevent)); + bevent->event.xFlags.xValid = 1; + bevent->event.xFlags.xFunction = HvLpEvent_Function_Int; + bevent->event.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + bevent->event.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + bevent->event.xType = HvLpEvent_Type_VirtualIo; + bevent->event.xSubtype = viocmd; + bevent->event.xSourceLp = HvLpConfig_getLpIndex(); + bevent->event.xTargetLp = viopath_hostLp; +#if 0 + bevent->event.xSizeMinus1 = offsetof(struct vioblocklpevent, + u.rwData.dmaInfo) + + (sizeof(bevent->u.rwData.dmaInfo[0])*(sgindex))-1; +#endif + bevent->event.xSizeMinus1 = sizeof(struct vioblocklpevent)-1; + bevent->event.xSourceInstanceId = viopath_sourceinst(viopath_hostLp); + bevent->event.xTargetInstanceId = viopath_targetinst(viopath_hostLp); + bevent->event.xCorrelationToken = (u64)(unsigned long)req->buffer; + bevent->mVersion = VIOVERSION; + bevent->mDisk = device_no; + bevent->u.rwData.mOffset = start; + + /* Copy just the dma information from the sg list into the request */ + for (sgindex = 0; sgindex < nsg; sgindex++) + { + bevent->u.rwData.dmaInfo[sgindex].mToken = sg[sgindex].dma_address; + bevent->u.rwData.dmaInfo[sgindex].mLen = sg[sgindex].dma_length; + } + + /* Send the request */ + hvrc = HvCallEvent_signalLpEvent(&bevent->event); + } + + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + return -1; + } + else + { + /* If the request was successful, bump the number of outstanding */ + numReqOut++; + } + return 0; +} + +/*************************************************************************** + * This is the external request processing routine + ***************************************************************************/ +static void do_viodasd_request(request_queue_t *q) +{ + int device_no; + struct request *req; + for (;;) { + + INIT_REQUEST; + + device_no = CURRENT_DEV; + if (device_no > numdsk) + { + err_printk("Invalid device # %d\n",CURRENT_DEV); + viodasd_end_request(CURRENT, 0); + continue; + } + + if (viodasd_gendsk.sizes == NULL) + { + err_printk("Ouch! viodasd_gendsk.sizes is NULL\n"); + viodasd_end_request(CURRENT, 0); + continue; + } + + /* If the queue is plugged, don't dequeue anything right now */ + if ((q) && (q->plugged)) + { + return; + } + + /* If we already have the maximum number of requests outstanding to OS/400 + just bail out. We'll come back later */ + if (numReqOut >= VIOMAXREQ) + return; + + /* get the current request, then dequeue it from the queue */ + req = CURRENT; + blkdev_dequeue_request(req); + + /* Try sending the request */ + if (send_request(req) == 0) + { + list_add_tail(&req->queue, &reqlist); + } + else + { + viodasd_end_request(req, 0); + } + } +} + +/*************************************************************************** + * Check for changed disks + ***************************************************************************/ +static int viodasd_check_change(kdev_t dev) +{ +#if 0 + struct viodasd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = DEVICE_NR(dev); +#endif + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + return 0; +#if 0 + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | vioblockcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) + { + err_printk("bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +#endif +} + +/*************************************************************************** + * Our file operations table + ***************************************************************************/ +static struct block_device_operations viodasd_fops = { + open: viodasd_open, + release: viodasd_release, + ioctl: viodasd_ioctl, + check_media_change: viodasd_check_change, + revalidate: viodasd_revalidate +}; + +/*************************************************************************** + * Our gendisk table + ***************************************************************************/ +struct gendisk viodasd_gendsk = { + 0, /* major - fill in later */ + "viodasd", + PARTITION_SHIFT, + 1<xFlags.xFunction == HvLpEvent_Function_Int) + { + err_printk("Yikes! got an int in viodasd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) + { + + /* Handle a response to an open request. We get all the disk information + * in the response, so update it. The correlation token contains a pointer to + * a waitevent structure that has a semaphore in it. update the return code + * in the waitevent structure and post the semaphore to wake up the guy who + * sent the request */ + case vioblockopen: + pwe = (struct viodasd_waitevent *)(unsigned long)event->xCorrelationToken; + pwe->rc = event->xRc; + if (event->xRc == HvLpEvent_Rc_Good) + { + viodasd_devices[bevent->mDisk].size = bevent->u.openData.mDiskLen; + viodasd_devices[bevent->mDisk].cylinders = bevent->u.openData.mCylinders; + viodasd_devices[bevent->mDisk].tracks = bevent->u.openData.mTracks; + viodasd_devices[bevent->mDisk].sectors = bevent->u.openData.mSectors; + viodasd_devices[bevent->mDisk].bytesPerSector = bevent->u.openData.mBytesPerSector; + viodasd_devices[bevent->mDisk].readOnly = bevent->mFlags & vioblockflags_ro; + + if (viodasd_max_disk != bevent->u.openData.mMaxDisks) + { + viodasd_max_disk = bevent->u.openData.mMaxDisks; + } + } + up(pwe->sem); + break; + + case vioblockclose: + break; + + /* For read and write requests, decrement the number of outstanding requests, + * Free the DMA buffers we allocated, and find the matching request by + * using the buffer pointer we stored in the correlation token. + */ + case vioblockread: + case vioblockwrite: + + /* Decrement the number of outstanding requests */ + numReqOut--; + + /* Free the DMA buffers */ + i = 0; + nsect = 0; + memset(sg, 0x00, sizeof(sg)); + + maxsg = (((bevent->event.xSizeMinus1 + 1) - + offsetof(struct vioblocklpevent, + u.rwData.dmaInfo)) / + sizeof(bevent->u.rwData.dmaInfo[0])); + + + while ((iu.rwData.dmaInfo[i].mLen > 0) && + (i < VIOMAXBLOCKDMA)) + { + sg[i].dma_address = bevent->u.rwData.dmaInfo[i].mToken; + sg[i].dma_length = bevent->u.rwData.dmaInfo[i].mLen; + nsect += bevent->u.rwData.dmaInfo[i].mLen; + i++; + } + + pci_unmap_sg(NULL, + sg, + i, + (bevent->event.xSubtype == (viomajorsubtype_blockio | vioblockread)) ? + PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + + + /* Since this is running in interrupt mode, we need to make sure we're not + * stepping on any global I/O operations + */ + spin_lock_irqsave(&io_request_lock, flags); + + /* Now find the matching request in OUR list (remember we moved the request + * from the global list to our list when we got it) + */ + req = blkdev_entry_to_request(reqlist.next); + while ((&req->queue != &reqlist) && + ((u64)(unsigned long)req->buffer != bevent->event.xCorrelationToken)) + req = blkdev_entry_to_request(req->queue.next); + + if (&req->queue == &reqlist) + { + err_printk("Yikes! Could not find matching buffer %p in reqlist\n", + req->buffer); + break; + } + + /* Remove the request from our list */ + list_del(&req->queue); + + /* Calculate the number of sectors from the length in bytes */ + nsect = nsect >> 9; + if (!req->bh) { + if (event->xRc != HvLpEvent_Rc_Good) + { + err_printk("rw error %d:%d\n",event->xRc,bevent->mSubTypeRc); + viodasd_end_request(req,0); + } + else + { + if (nsect != req->current_nr_sectors) + { + err_printk("Yikes...non bh i/o # sect doesn't match!!!\n"); + } + viodasd_end_request(req, 1); + } + } + else { + while ((nsect > 0) && (req->bh)) + { + nsect -= req->current_nr_sectors; + viodasd_end_request(req, 1); + } + if (nsect) + { + err_printk("Yikes...sectors left over on a request!!!\n"); + } + + /* If the original request could not handle all the buffers, re-send + * the request + */ + if (req->bh) + { + if (send_request(req) == 0) + { + list_add_tail(&req->queue, &reqlist); + } + else + { + viodasd_end_request(req, 0); + } + } + + } + + /* Finally, send more requests */ + do_viodasd_request(NULL); + + spin_unlock_irqrestore(&io_request_lock, flags); + break; + + case vioblockflush: + up((void*)(unsigned long)event->xCorrelationToken); + break; + + case vioblockcheck: + pwe = (struct viodasd_waitevent *)(unsigned long)event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->u.check.changed; + up(pwe->sem); + break; + + default: + err_printk("invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/*************************************************************************** + * This routine tries to clean up anything we allocated/registered + ***************************************************************************/ +static void cleanup2(void) +{ + int i; + +#define CLEANIT(x) if (x) {kfree(x); x=NULL;} + + for (i=0; i + * (C) Copyright 2000 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *************************************************************************** + * This routine provides access to CD ROM drives owned and managed by an + * OS/400 partition running on the same box as this Linux partition. + * + * All operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * include/asm-ppc/iSeries/vio.h + * + * This device driver can either use it's own major number, or it can + * pretend to be an AZTECH drive. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new CD major number... + * or you can call this a really ugly hack. Your choice. + * + */ +#include +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an AZTECH drive + ***************************************************************************/ +#ifdef CONFIG_VIOCD_AZTECH +#define MAJOR_NR AZTECH_CDROM_MAJOR +#define do_viocd_request do_aztcd_request +#else +#define MAJOR_NR VIOCD_MAJOR +#endif + +#define VIOCD_VERS "1.02" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +/*************************************************************************** + * Our internal printk macro + ***************************************************************************/ +#define err_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERR: " fmt, __FILE__, __LINE__ , ## args) + +/*************************************************************************** + * Should probably make this a module parameter....sigh + ***************************************************************************/ +#define VIOCD_MAX_CD 8 +int viocd_blocksizes[VIOCD_MAX_CD]; + +/*************************************************************************** + * This is the structure we use to exchange info between driver and interrupt + * handler + ***************************************************************************/ +struct viocd_waitevent { + struct semaphore *sem; + int rc; + int changed; +}; + +/*************************************************************************** + * These are our internal structures for keeping track of devices + ***************************************************************************/ +static int viocd_numdev; +static struct { + char rsrcname[10]; + char type[4]; + char model[3]; +} viocd_unitinfo[VIOCD_MAX_CD]; + +static struct { + u32 useCount; + u32 blocksize; + u32 mediasize; +} viocd_diskinfo[VIOCD_MAX_CD]; + +static struct cdrom_device_info viocd_info[VIOCD_MAX_CD]; + +/*************************************************************************** + *************************************************************************** + * CODE STARTS HERE + *************************************************************************** + ***************************************************************************/ + +/*************************************************************************** + * Get info on CD devices from OS/400 + ***************************************************************************/ +static void get_viocd_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + DECLARE_MUTEX_LOCKED(Semaphore); + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return; + + memset(viocd_unitinfo, 0x00, sizeof(viocd_unitinfo)); + + dmaaddr = pci_map_single(NULL, &viocd_unitinfo, + sizeof(viocd_unitinfo), + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + err_printk("error allocating tce\n"); + return; + } + + we.sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdgetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + dmaaddr, + 0, + sizeof(viocd_unitinfo), + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + return; + } + + down(&Semaphore); + + if (we.rc) + { + err_printk("bad rc %d on getinfo\n",we.rc); + return; + } + + + for (i=0; + ((i < VIOCD_MAX_CD) && (viocd_unitinfo[i].rsrcname[0])); + i++) + { + viocd_numdev++; + } +} + +/*************************************************************************** + * Open a device + ***************************************************************************/ +static int viocd_open(struct cdrom_device_info * cdi, int purpose) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + if (device_no >= viocd_numdev) + return -ENODEV; + + we.sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + down(&Semaphore); + + if (we.rc) + return -EIO; + + if (viocd_diskinfo[device_no].useCount == 0) + { + if (viocd_diskinfo[device_no].blocksize > 0) + { + viocd_blocksizes[device_no] = viocd_diskinfo[device_no].blocksize; + } + } + return 0; +} + +/*************************************************************************** + * Release a device + ***************************************************************************/ +static void viocd_release(struct cdrom_device_info * cdi) +{ + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return; + + if (device_no >= viocd_numdev) + return; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return; + } +} + +/*************************************************************************** + * Do a request + ***************************************************************************/ +static int rwreq; +static void do_viocd_request(request_queue_t *q) +{ + int device_no; + long start; + long len; + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + for (;;) { + if (rwreq > 0) return; + + INIT_REQUEST; + + if (CURRENT->cmd != READ) + { + err_printk("Invalid write request for CD\n"); + end_request(0); + continue; + } + + device_no = CURRENT_DEV; + if (device_no > viocd_numdev) + { + err_printk("Invalid device # %d\n",CURRENT_DEV); + end_request(0); + continue; + } + + if (rwreq > 0) + { + err_printk("multiple rw req!\n"); + end_request(0); + continue; + } + + start = CURRENT->sector * 512; + len = CURRENT->current_nr_sectors * 512; + + dmaaddr = pci_map_single(NULL, CURRENT->buffer, + len, + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + err_printk("error allocating tce for address %p len %ld\n", + CURRENT->buffer, + len); + end_request(0); + continue; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdread, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)device_no << 48) | + dmaaddr, + start, + len, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + err_printk("hv error on op %d\n",(int)hvrc); + end_request(0); + } + else + { + rwreq++; + return; + } + } +} + +/*************************************************************************** + * Check if the CD changed + ***************************************************************************/ +static int viocd_media_changed(struct cdrom_device_info * cdi, int disc_nr) +{ + struct viocd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = MINOR(cdi->dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) + { + err_printk("Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&we, + VIOVERSION << 16, + ((u64)device_no << 48), + 0, 0, 0); + + if (hvrc != 0) + { + err_printk("bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) + { + err_printk("bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +/*************************************************************************** + * This routine handles incoming CD LP events + ***************************************************************************/ +static void vioHandleCDEvent(struct HvLpEvent *event) +{ + struct viocdlpevent *bevent = (struct viocdlpevent *)event; + struct viocd_waitevent *pwe; + + if (event == NULL) + { + /* Notification that a partition went away! */ + return; + } + + // First, we should NEVER get an int here...only acks + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + err_printk("Yikes! got an int in viocd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) + { + case viocdgetinfo: + case viocdopen: + pwe = (struct viocd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + viocd_diskinfo[bevent->mDisk].blocksize = bevent->mBlockSize; + viocd_diskinfo[bevent->mDisk].mediasize = bevent->mMediaSize; + + up(pwe->sem); + break; + + case viocdclose: + break; + + case viocdread: + rwreq--; + pci_unmap_single(NULL, + bevent->mToken, + bevent->mLen, + PCI_DMA_FROMDEVICE); + + if (event->xRc == HvLpEvent_Rc_Good) + { + end_request(1); + } + else + { + err_printk("rw error %d:%d\n",event->xRc,bevent->mSubTypeRc); + end_request(0); + } + do_viocd_request(NULL); + break; + + case viocdcheck: + pwe = (struct viocd_waitevent *)(u32)event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->mFlags; + up(pwe->sem); + break; + + default: + err_printk("invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/*************************************************************************** + * Our file operations table + ***************************************************************************/ +static struct cdrom_device_ops viocd_dops = { + open: viocd_open, + release: viocd_release, + media_changed: viocd_media_changed, + capability: CDC_CD_R +}; + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + +#if defined(MODULE) + len += sprintf(buf+len,"viocd Module opened %d times. Major number %d\n", + MOD_IN_USE, + MAJOR_NR); +#endif + *eof = 1; + return len; +} + +/*************************************************************************** + * Handle writes to our proc file system + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + printk("viocd: in proc_write, got %ld bytes starting with %c\n", + count, buffer[0]); + return count; +} + + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void viocd_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("viocd", S_IFREG|S_IRUSR, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viocd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocd", iSeries_proc); +} + +/*************************************************************************** + * Initialize the whole device driver. Handle module and non-module + * versions + ***************************************************************************/ +__init int viocd_init(void) +{ + int i; + int rc; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + rc = viopath_open(viopath_hostLp, viomajorsubtype_cdio); + if (rc) + { + err_printk("error opening path to host partition %d\n",viopath_hostLp); + return rc; + } + + /* + * Initialize our request handler + */ + rwreq = 0; + vio_setCDHandler(vioHandleCDEvent); + + memset(viocd_unitinfo, 0x00, sizeof(viocd_unitinfo)); + memset(viocd_diskinfo, 0x00, sizeof(viocd_diskinfo)); + + get_viocd_info(); + + if (viocd_numdev == 0) + { + viopath_close(viopath_hostLp, viomajorsubtype_cdio); + return 0; + } + + printk("%s: iSeries Virtual CD vers %s, major %d, max disks %d, hosting partition %d\n", + DEVICE_NAME,VIOCD_VERS,MAJOR_NR,VIOCD_MAX_CD,viopath_hostLp); + + if (devfs_register_blkdev(MAJOR_NR, "viocd", &cdrom_fops) != 0) + { + printk("Unable to get major %d for viocd CD-ROM\n", + MAJOR_NR); + return -EIO; + } + + blksize_size[MAJOR_NR] = viocd_blocksizes; + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + read_ahead[MAJOR_NR] = 4; + + memset(&viocd_info,0x00,sizeof(viocd_info)); + for (i=0; iprocess_char_map) || tty->lnext) { + if (!test_bit(c, tty->process_char_map) || tty->lnext) { finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { @@ -659,7 +659,7 @@ handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, &tty->read_flags); + set_bit(tty->read_head, tty->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; @@ -811,38 +811,38 @@ memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', &tty->process_char_map); + set_bit('\r', tty->process_char_map); if (I_INLCR(tty)) - set_bit('\n', &tty->process_char_map); + set_bit('\n', tty->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), &tty->process_char_map); - set_bit(KILL_CHAR(tty), &tty->process_char_map); - set_bit(EOF_CHAR(tty), &tty->process_char_map); - set_bit('\n', &tty->process_char_map); - set_bit(EOL_CHAR(tty), &tty->process_char_map); + set_bit(ERASE_CHAR(tty), tty->process_char_map); + set_bit(KILL_CHAR(tty), tty->process_char_map); + set_bit(EOF_CHAR(tty), tty->process_char_map); + set_bit('\n', tty->process_char_map); + set_bit(EOL_CHAR(tty), tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(LNEXT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(EOL2_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), &tty->process_char_map); - set_bit(STOP_CHAR(tty), &tty->process_char_map); + set_bit(START_CHAR(tty), tty->process_char_map); + set_bit(STOP_CHAR(tty), tty->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), &tty->process_char_map); - set_bit(QUIT_CHAR(tty), &tty->process_char_map); - set_bit(SUSP_CHAR(tty), &tty->process_char_map); + set_bit(INTR_CHAR(tty), tty->process_char_map); + set_bit(QUIT_CHAR(tty), tty->process_char_map); + set_bit(SUSP_CHAR(tty), tty->process_char_map); } - clear_bit(__DISABLED_CHAR, &tty->process_char_map); + clear_bit(__DISABLED_CHAR, tty->process_char_map); sti(); tty->raw = 0; tty->real_raw = 0; @@ -1058,7 +1058,7 @@ int eol; eol = test_and_clear_bit(tty->read_tail, - &tty->read_flags); + tty->read_flags); c = tty->read_buf[tty->read_tail]; spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/char/tty_io.c linuxppc64_2_4/drivers/char/tty_io.c --- ../w3.wed/linux-2.4.8-ac5/drivers/char/tty_io.c Wed Aug 15 12:11:31 2001 +++ linuxppc64_2_4/drivers/char/tty_io.c Fri Aug 10 13:08:57 2001 @@ -2237,6 +2237,11 @@ * set up the console device so that later boot sequences can * inform about problems etc.. */ + +#ifdef CONFIG_VIOCONS + viocons_init(); +#endif + #ifdef CONFIG_VT con_init(); #endif @@ -2334,6 +2339,10 @@ /* console calls tty_register_driver() before kmalloc() works. * Thus, we can't devfs_register() then. Do so now, instead. */ +#ifdef CONFIG_VIOCONS + viocons_init2(); +#endif + #ifdef CONFIG_VT con_init_devfs(); #endif diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/char/viocons.c linuxppc64_2_4/drivers/char/viocons.c --- ../w3.wed/linux-2.4.8-ac5/drivers/char/viocons.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/char/viocons.c Thu Jul 19 14:02:56 2001 @@ -0,0 +1,1407 @@ +/* + * drivers/char/viocons.c + * + * iSeries Virtual Terminal + * + * Author: Dave Boutcher + * (C) Copyright 2000 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif + +#ifndef _HVLPEVENT_H +#include +#endif + +#ifndef _HVCALLEVENT_H +#include "asm/iSeries/HvCallEvent.h" +#endif +#ifndef _HVLPCONFIG_H +#include "asm/iSeries/HvLpConfig.h" +#endif +#include "asm/iSeries/HvCall.h" +#ifndef _ISERIES_PROC_H +#include +#endif + +/*************************************************************************** + * Check that the tty_driver_data actually points to our stuff + ***************************************************************************/ +#define VIOTTY_PARANOIA_CHECK 1 +#define VIOTTY_MAGIC (0x0DCB) + +static int debug; + +static DECLARE_WAIT_QUEUE_HEAD(viocons_wait_queue); + +static int viotty_major = 229; + +#define VTTY_PORTS 10 +#define VIOTTY_SERIAL_START 65 + +static u64 sndMsgSeq[VTTY_PORTS]; +static u64 sndMsgAck[VTTY_PORTS]; + +static spinlock_t consolelock; + +/*************************************************************************** + * When we get writes faster than we can send it to the partition, + * buffer the data here. There is one set of buffers for each virtual + * port. + ***************************************************************************/ +/* Note that bufferUsed is a bit map of used buffers. + * It had better have enough bits to hold NUM_BUF + * the bitops assume it is a multiple of unsigned long + */ +#define NUM_BUF (8) +#define OVERFLOW_SIZE VIOCHAR_MAX_DATA + +static struct overflowBuffers { + unsigned long bufferUsed; + u8 *buffer[NUM_BUF]; + int bufferBytes[NUM_BUF]; + int curbuf; + int bufferOverflow; + int overflowMessage; +} overflow[VTTY_PORTS]; + +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp); + +static struct tty_driver viotty_driver; +static struct tty_driver viottyS_driver; +static int viotty_refcount; + +static struct tty_struct *viotty_table[VTTY_PORTS]; +static struct tty_struct *viottyS_table[VTTY_PORTS]; +static struct termios *viotty_termios[VTTY_PORTS]; +static struct termios *viottyS_termios[VTTY_PORTS]; +static struct termios *viotty_termios_locked[VTTY_PORTS]; +static struct termios *viottyS_termios_locked[VTTY_PORTS]; + +void hvlog(char *fmt, ...) +{ + int i; + static char buf[256]; + va_list args; + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + HvCall_writeLogBuffer(buf, i); + HvCall_writeLogBuffer("\r",1); + +} + +/*************************************************************************** + * Our port information. We store a pointer to one entry in the + * tty_driver_data + ***************************************************************************/ +static struct port_info_tag { + int magic; + struct tty_struct *tty; + HvLpIndex lp; + u8 vcons; + u8 port; +} port_info[VTTY_PORTS]; + +/*************************************************************************** + * Make sure we're pointing to a valid port_info structure. Shamelessly + * plagerized from serial.c + ***************************************************************************/ +static inline int viotty_paranoia_check(struct port_info_tag *pi, + kdev_t device, const char *routine) +{ +#ifdef VIOTTY_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for port_info struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null port_info for (%s) in %s\n"; + + if (!pi) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (pi->magic != VIOTTY_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/*************************************************************************** + * Handle reads from the proc file system. Right now we just dump the + * state of the first TTY + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + struct tty_struct *tty = viotty_table[0]; + struct termios *termios; + if (tty == NULL) + { + len += sprintf(buf+len,"no tty\n"); + *eof = 1; + return len; + } + + len += sprintf(buf+len,"tty info: COOK_OUT %ld COOK_IN %ld, NO_WRITE_SPLIT %ld\n", + tty->flags & TTY_HW_COOK_OUT, + tty->flags & TTY_HW_COOK_IN, + tty->flags & TTY_NO_WRITE_SPLIT); + + termios = tty->termios; + if (termios == NULL) + { + len += sprintf(buf+len,"no termios\n"); + *eof = 1; + return len; + } +len+= sprintf(buf+len,"INTR_CHAR %2.2x\n",INTR_CHAR(tty)); +len+= sprintf(buf+len,"QUIT_CHAR %2.2x\n",QUIT_CHAR(tty)); +len+= sprintf(buf+len,"ERASE_CHAR %2.2x\n",ERASE_CHAR(tty)); +len+= sprintf(buf+len,"KILL_CHAR %2.2x\n",KILL_CHAR(tty)); +len+= sprintf(buf+len,"EOF_CHAR %2.2x\n",EOF_CHAR(tty)); +len+= sprintf(buf+len,"TIME_CHAR %2.2x\n",TIME_CHAR(tty)); +len+= sprintf(buf+len,"MIN_CHAR %2.2x\n",MIN_CHAR(tty)); +len+= sprintf(buf+len,"SWTC_CHAR %2.2x\n",SWTC_CHAR(tty)); +len+= sprintf(buf+len,"START_CHAR %2.2x\n",START_CHAR(tty)); +len+= sprintf(buf+len,"STOP_CHAR %2.2x\n",STOP_CHAR(tty)); +len+= sprintf(buf+len,"SUSP_CHAR %2.2x\n",SUSP_CHAR(tty)); +len+= sprintf(buf+len,"EOL_CHAR %2.2x\n",EOL_CHAR(tty)); +len+= sprintf(buf+len,"REPRINT_CHAR %2.2x\n",REPRINT_CHAR(tty)); +len+= sprintf(buf+len,"DISCARD_CHAR %2.2x\n",DISCARD_CHAR(tty)); +len+= sprintf(buf+len,"WERASE_CHAR %2.2x\n",WERASE_CHAR(tty)); +len+= sprintf(buf+len,"LNEXT_CHAR %2.2x\n",LNEXT_CHAR(tty)); +len+= sprintf(buf+len,"EOL2_CHAR %2.2x\n",EOL2_CHAR(tty)); + +len+= sprintf(buf+len,"I_IGNBRK %4.4x\n",I_IGNBRK(tty)); +len+= sprintf(buf+len,"I_BRKINT %4.4x\n",I_BRKINT(tty)); +len+= sprintf(buf+len,"I_IGNPAR %4.4x\n",I_IGNPAR(tty)); +len+= sprintf(buf+len,"I_PARMRK %4.4x\n",I_PARMRK(tty)); +len+= sprintf(buf+len,"I_INPCK %4.4x\n",I_INPCK(tty)); +len+= sprintf(buf+len,"I_ISTRIP %4.4x\n",I_ISTRIP(tty)); +len+= sprintf(buf+len,"I_INLCR %4.4x\n",I_INLCR(tty)); +len+= sprintf(buf+len,"I_IGNCR %4.4x\n",I_IGNCR(tty)); +len+= sprintf(buf+len,"I_ICRNL %4.4x\n",I_ICRNL(tty)); +len+= sprintf(buf+len,"I_IUCLC %4.4x\n",I_IUCLC(tty)); +len+= sprintf(buf+len,"I_IXON %4.4x\n",I_IXON(tty)); +len+= sprintf(buf+len,"I_IXANY %4.4x\n",I_IXANY(tty)); +len+= sprintf(buf+len,"I_IXOFF %4.4x\n",I_IXOFF(tty)); +len+= sprintf(buf+len,"I_IMAXBEL %4.4x\n",I_IMAXBEL(tty)); + +len+= sprintf(buf+len,"O_OPOST %4.4x\n",O_OPOST(tty)); +len+= sprintf(buf+len,"O_OLCUC %4.4x\n",O_OLCUC(tty)); +len+= sprintf(buf+len,"O_ONLCR %4.4x\n",O_ONLCR(tty)); +len+= sprintf(buf+len,"O_OCRNL %4.4x\n",O_OCRNL(tty)); +len+= sprintf(buf+len,"O_ONOCR %4.4x\n",O_ONOCR(tty)); +len+= sprintf(buf+len,"O_ONLRET %4.4x\n",O_ONLRET(tty)); +len+= sprintf(buf+len,"O_OFILL %4.4x\n",O_OFILL(tty)); +len+= sprintf(buf+len,"O_OFDEL %4.4x\n",O_OFDEL(tty)); +len+= sprintf(buf+len,"O_NLDLY %4.4x\n",O_NLDLY(tty)); +len+= sprintf(buf+len,"O_CRDLY %4.4x\n",O_CRDLY(tty)); +len+= sprintf(buf+len,"O_TABDLY %4.4x\n",O_TABDLY(tty)); +len+= sprintf(buf+len,"O_BSDLY %4.4x\n",O_BSDLY(tty)); +len+= sprintf(buf+len,"O_VTDLY %4.4x\n",O_VTDLY(tty)); +len+= sprintf(buf+len,"O_FFDLY %4.4x\n",O_FFDLY(tty)); + +len+= sprintf(buf+len,"C_BAUD %4.4x\n",C_BAUD(tty)); +len+= sprintf(buf+len,"C_CSIZE %4.4x\n",C_CSIZE(tty)); +len+= sprintf(buf+len,"C_CSTOPB %4.4x\n",C_CSTOPB(tty)); +len+= sprintf(buf+len,"C_CREAD %4.4x\n",C_CREAD(tty)); +len+= sprintf(buf+len,"C_PARENB %4.4x\n",C_PARENB(tty)); +len+= sprintf(buf+len,"C_PARODD %4.4x\n",C_PARODD(tty)); +len+= sprintf(buf+len,"C_HUPCL %4.4x\n",C_HUPCL(tty)); +len+= sprintf(buf+len,"C_CLOCAL %4.4x\n",C_CLOCAL(tty)); +len+= sprintf(buf+len,"C_CRTSCTS %4.4x\n",C_CRTSCTS(tty)); + +len+= sprintf(buf+len,"L_ISIG %4.4x\n",L_ISIG(tty)); +len+= sprintf(buf+len,"L_ICANON %4.4x\n",L_ICANON(tty)); +len+= sprintf(buf+len,"L_XCASE %4.4x\n",L_XCASE(tty)); +len+= sprintf(buf+len,"L_ECHO %4.4x\n",L_ECHO(tty)); +len+= sprintf(buf+len,"L_ECHOE %4.4x\n",L_ECHOE(tty)); +len+= sprintf(buf+len,"L_ECHOK %4.4x\n",L_ECHOK(tty)); +len+= sprintf(buf+len,"L_ECHONL %4.4x\n",L_ECHONL(tty)); +len+= sprintf(buf+len,"L_NOFLSH %4.4x\n",L_NOFLSH(tty)); +len+= sprintf(buf+len,"L_TOSTOP %4.4x\n",L_TOSTOP(tty)); +len+= sprintf(buf+len,"L_ECHOCTL %4.4x\n",L_ECHOCTL(tty)); +len+= sprintf(buf+len,"L_ECHOPRT %4.4x\n",L_ECHOPRT(tty)); +len+= sprintf(buf+len,"L_ECHOKE %4.4x\n",L_ECHOKE(tty)); +len+= sprintf(buf+len,"L_FLUSHO %4.4x\n",L_FLUSHO(tty)); +len+= sprintf(buf+len,"L_PENDIN %4.4x\n",L_PENDIN(tty)); +len+= sprintf(buf+len,"L_IEXTEN %4.4x\n",L_IEXTEN(tty)); + + *eof = 1; + return len; +} + +/*************************************************************************** + * Handle writes to our proc file system. Right now just turns on and off + * our debug flag + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + if (count) + { + if (buffer[0] == '1') + { + printk("viocons: debugging on\n"); + debug = 1; + } + else + { + printk("viocons: debugging off\n"); + debug = 0; + } + } + return count; +} + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void viocons_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("viocons", S_IFREG|S_IRUSR, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/*************************************************************************** + * clean up our proc file system entries + ***************************************************************************/ +void viocons_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocons", iSeries_proc); +} + +/*************************************************************************** + * Add data to our pending-send buffers + ***************************************************************************/ +static int bufferAdd(u8 port, const char *buf, size_t len, int userFlag) +{ + size_t bleft = len; + size_t curlen; + char *cbuf = (char *)buf; + int nextbuf; + struct overflowBuffers *pov = &overflow[port]; + while (bleft > 0) + { + /* + * If there is no space left in the current buffer, we have + * filled everything up, so return. If we filled the previous + * buffer we would already have moved to the next one. + */ + if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) + { + hvlog("buffer %d full. no more space\n",pov->curbuf); + pov->bufferOverflow++; + pov->overflowMessage = 1; + return len - bleft; + } + + /* + * Turn on the "used" bit for this buffer. If it's already on, that's + * fine. + */ + set_bit(pov->curbuf,&pov->bufferUsed); + + /* + * See if this buffer has been allocated. If not, allocate it + */ + if (pov->buffer[pov->curbuf] == NULL) + pov->buffer[pov->curbuf] = + kmalloc(OVERFLOW_SIZE, GFP_ATOMIC); + + /* + * Figure out how much we can copy into this buffer + */ + if (bleft < (OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf])) + curlen = bleft; + else + curlen = OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf]; + + /* + * Copy the data into the buffer + */ + if (userFlag) + copy_from_user(pov->buffer[pov->curbuf]+ + pov->bufferBytes[pov->curbuf],cbuf,curlen); + else + memcpy(pov->buffer[pov->curbuf]+ + pov->bufferBytes[pov->curbuf],cbuf,curlen); + + pov->bufferBytes[pov->curbuf] += curlen; + cbuf += curlen; + bleft -= curlen; + + /* + * Now see if we've filled this buffer + */ + if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) + { + nextbuf = (pov->curbuf+1)%NUM_BUF; + + /* + * Move to the next buffer if it hasn't been used yet + */ + if (test_bit(nextbuf,&pov->bufferUsed) == 0) + { + pov->curbuf = nextbuf; + } + } + } + return len; +} + +/*************************************************************************** + * Send pending data + ***************************************************************************/ +void sendBuffers(u8 port, HvLpIndex lp) +{ + HvLpEvent_Rc hvrc; + int nextbuf; + struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; + int flags; + struct overflowBuffers *pov = &overflow[port]; + + spin_lock_irqsave(&consolelock, + flags); + + if (pov->bufferUsed == 0) + { + hvlog("in sendbuffers, but no buffers used\n"); + spin_unlock_irqrestore(&consolelock, + flags); + + return; + } + + /* + * curbuf points to the buffer we're filling. We want to start sending AFTER + * this one. + */ + nextbuf = (pov->curbuf + 1) % NUM_BUF; + + /* + * Loop until we find a buffer with the bufferUsed bit on + */ + while (test_bit(nextbuf,&pov->bufferUsed) == 0) + nextbuf = (nextbuf + 1) % NUM_BUF; + + initDataEvent(viochar, lp); + + /* + * While we have buffers with data, and our send window is open, send them + */ + while ((test_bit(nextbuf, &pov->bufferUsed)) && + ((sndMsgSeq[port] - sndMsgAck[port]) immediateDataLen = pov->bufferBytes[nextbuf]; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + viochar->event.xSizeMinus1 = offsetof(struct viocharlpevent, immediateData) + + viochar->immediateDataLen; + + memcpy(viochar->immediateData,pov->buffer[nextbuf],viochar->immediateDataLen); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + { + /* + * MUST unlock the spinlock before doing a printk + */ + spin_unlock_irqrestore(&consolelock, + flags); + + printk("viocons: error sending event! %d\n",(int)hvrc); + return; + } + + /* + * clear the bufferUsed bit, zero the number of bytes in this buffer, + * and move to the next buffer + */ + clear_bit(nextbuf, &pov->bufferUsed); + pov->bufferBytes[nextbuf] = 0; + nextbuf = (nextbuf + 1) % NUM_BUF; + } + + + /* + * If we have emptied all the buffers, start at 0 again. + * this will re-use any allocated buffers + */ + if (pov->bufferUsed == 0) + { + pov->curbuf = 0; + + if (pov->overflowMessage) + pov->overflowMessage = 0; + + if (port_info[port].tty) + { + if ((port_info[port].tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + (port_info[port].tty->ldisc.write_wakeup)) + (port_info[port].tty->ldisc.write_wakeup) (port_info[port].tty); + wake_up_interruptible(&port_info[port].tty->write_wait); + } + } + + spin_unlock_irqrestore(&consolelock, + flags); + +} + +/*************************************************************************** + * Our internal writer. Gets called both from the console device and + * the tty device. the tty pointer will be NULL if called from the console. + ***************************************************************************/ +static int internal_write(struct tty_struct * tty, const char *buf, size_t len, int userFlag) +{ + HvLpEvent_Rc hvrc; + size_t bleft = len; + size_t curlen; + const char *curbuf = buf; + struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; + unsigned long flags; + struct port_info_tag * pi = NULL; + HvLpIndex lp; + u8 port; + + if (tty) + { + pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_internal_write")) + return -ENODEV; + + lp = pi->lp; + port = pi->port; + } + else + { + /* + * If this is the console device, use the lp from the first port entry + */ + port = 0; + lp = port_info[0].lp; + } + + /* + * Always put console output in the hypervisor console log + */ + if (port == 0) + HvCall_writeLogBuffer(buf, len); + + /* + * If the path to this LP is closed, don't bother doing anything more. + * just dump the data on the floor + */ + if (!viopath_isactive(lp)) + return len; + + /* + * If there is already data queued for this port, send it + */ + if (overflow[port].bufferUsed) + sendBuffers(port, lp); + + spin_lock_irqsave(&consolelock, + flags); + + initDataEvent(viochar, lp); + + /* Got the lock, don't cause console output */ + while ((bleft > 0) && + (overflow[port].bufferUsed == 0) && + ((sndMsgSeq[port] - sndMsgAck[port]) VIOCHAR_MAX_DATA) + curlen = VIOCHAR_MAX_DATA; + else + curlen = bleft; + + viochar->immediateDataLen = curlen; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + + if (userFlag) + copy_from_user(viochar->immediateData,curbuf,curlen); + else + memcpy(viochar->immediateData,curbuf,curlen); + + viochar->event.xSizeMinus1 = offsetof(struct viocharlpevent, immediateData) + + curlen; + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + { + /* + * MUST unlock the spinlock before doing a printk + */ + spin_unlock_irqrestore(&consolelock, + flags); + + printk("viocons: error sending event! %d\n",(int)hvrc); + return len - bleft; + } + + curbuf += curlen; + bleft -= curlen; + } + + /* + * If we didn't send it all, buffer it + */ + if (bleft > 0) + { + bleft -= bufferAdd(port, curbuf,bleft, userFlag); + } + spin_unlock_irqrestore(&consolelock, + flags); + + return len - bleft; +} + +/*************************************************************************** + * Initialize the common fields in a charLpEvent + ***************************************************************************/ +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp) +{ + memset(viochar, 0x00, sizeof(struct viocharlpevent)); + + viochar->event.xFlags.xValid = 1; + viochar->event.xFlags.xFunction = HvLpEvent_Function_Int; + viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck; + viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck; + viochar->event.xType = HvLpEvent_Type_VirtualIo; + viochar->event.xSubtype = viomajorsubtype_chario | viochardata; + viochar->event.xSourceLp = HvLpConfig_getLpIndex(); + viochar->event.xTargetLp = lp; + viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent); + viochar->event.xSourceInstanceId = viopath_sourceinst(lp); + viochar->event.xTargetInstanceId = viopath_targetinst(lp); +} + + +/*************************************************************************** + * console device write + ***************************************************************************/ +static void viocons_write(struct console *co, const char *s, + unsigned count) +{ + /*** Ryan S. Arnold : ryanarn@us.ibm.com *** 05/03/2001 *********************** + * This parser will ensure that all single instances of either \n or \r are + * matched into carriage return/line feed combinations. It also allows for + * instances where there already exist \n\r combinations as well as the + * reverse, \r\n combinations. + */ + + int index; + char charptr[1]; + int foundcr; + int slicebegin; + int sliceend; + + foundcr = 0; + slicebegin = 0; + sliceend = 0; + + for ( index = 0; index < count; index++ ) + { + if( !foundcr && s[index] == 0x0a ) + { + if ( (slicebegin - sliceend > 0) && sliceend < count ) + { + internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); + slicebegin = sliceend; + } + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + if( foundcr && s[index] != 0x0a ) + { + if ( ( index - 2 ) >= 0 ) + { + if ( s[index - 2] != 0x0a ) + { + internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); + slicebegin = sliceend; + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } + } + } + sliceend++; + + if( s[index] == 0x0d ) + foundcr = 1; + else + foundcr = 0; + } + + internal_write(NULL, &s[slicebegin], sliceend - slicebegin, 0 ); + + if ( count > 1) + { + if ( foundcr == 1 && s[count-1] != 0x0a ) + { + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } + else if ( s[count-1] == 0x0a && s[count-2] != 0x0d ) + { + + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + } +} + +/*************************************************************************** + * Work out a the device associate with this console + ***************************************************************************/ +static kdev_t viocons_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index + viotty_driver.minor_start); +} + +/*************************************************************************** + * console device read method + ***************************************************************************/ +static int viocons_read( struct console *co, const char *s, + unsigned count ) +{ + printk("In viocons_read\n"); + // Implement me + interruptible_sleep_on( &viocons_wait_queue ); + return 0; +} + +/*************************************************************************** + * console device wait until a key is pressed + ***************************************************************************/ +static int viocons_wait_key( struct console *co ) +{ + printk("In viocons_wait_key\n"); + // Implement me + interruptible_sleep_on( &viocons_wait_queue ); + return 0; +} + +/*************************************************************************** + * Do console device setup + ***************************************************************************/ +static int __init viocons_setup(struct console *co, char *options) +{ + printk("viocons: in viocons_setup\n"); + spin_lock_init(&consolelock); + return 0; +} + +/*************************************************************************** + * console device I/O methods + ***************************************************************************/ +static struct console viocons = { + name: "ttyS", + write: viocons_write, + read: viocons_read, + device: viocons_device, + wait_key: viocons_wait_key, + setup: viocons_setup, + flags: CON_PRINTBUFFER, +}; + + +/*************************************************************************** + * TTY Open method + ***************************************************************************/ +static int viotty_open(struct tty_struct *tty, struct file * filp) +{ + int port; + unsigned long flags; + MOD_INC_USE_COUNT; + port = MINOR(tty->device) - tty->driver.minor_start; + + if (port >= VIOTTY_SERIAL_START) + port -= VIOTTY_SERIAL_START; + + if ((port < 0) || (port >= VTTY_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + spin_lock_irqsave(&consolelock, + flags); + + /* + * If some other TTY is already connected here, reject the open + */ + if ((port_info[port].tty) && + (port_info[port].tty != tty)) + { + spin_unlock_irqrestore(&consolelock, + flags); + MOD_DEC_USE_COUNT; + printk("viocons: attempt to open device twice from different ttys\n"); + return -EBUSY; + } + tty->driver_data = &port_info[port]; + port_info[port].tty = tty; + spin_unlock_irqrestore(&consolelock, + flags); + + return 0; +} + +/*************************************************************************** + * TTY Close method + ***************************************************************************/ +static void viotty_close(struct tty_struct *tty, struct file * filp) +{ + unsigned long flags; + struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_close")) + return; + + spin_lock_irqsave(&consolelock, + flags); + if (tty->count == 1) + { + pi->tty = NULL; + } + + spin_unlock_irqrestore(&consolelock, + flags); + + MOD_DEC_USE_COUNT; +} + +/*************************************************************************** + * TTY Write method + ***************************************************************************/ +static int viotty_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + return internal_write(tty, buf, count, from_user); +} + +/*************************************************************************** + * TTY put_char method + ***************************************************************************/ +static void viotty_put_char(struct tty_struct *tty, unsigned char ch) +{ + internal_write(tty, &ch, 1, 0); +} + +/*************************************************************************** + * TTY flush_chars method + ***************************************************************************/ +static void viotty_flush_chars(struct tty_struct *tty) +{ +} + +/*************************************************************************** + * TTY write_room method + ***************************************************************************/ +static int viotty_write_room(struct tty_struct *tty) +{ + int i; + int room = 0; + struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_sendbuffers")) + return 0; + + // If no buffers are used, return the max size + if (overflow[pi->port].bufferUsed == 0) + return VIOCHAR_MAX_DATA * NUM_BUF; + + for (i=0; ((iport].bufferBytes[i]); + } + + if (room > VIOCHAR_MAX_DATA) + return VIOCHAR_MAX_DATA; + else + return room; +} + +/*************************************************************************** + * TTY chars_in_buffer_room method + ***************************************************************************/ +static int viotty_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static void viotty_flush_buffer(struct tty_struct *tty) +{ +} + +static int viotty_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) + { + /* the ioctls below read/set the flags usually shown in the leds */ + /* don't use them - they will go away without warning */ + case KDGETLED: + case KDGKBLED: + return put_user(0, (char*)arg); + + case KDSKBLED: + return 0; + } + + return n_tty_ioctl(tty,file,cmd,arg); +} + +static void viotty_throttle(struct tty_struct * tty) +{ +} + +static void viotty_unthrottle(struct tty_struct * tty) +{ +} + +static void viotty_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ +} + +static void viotty_stop(struct tty_struct *tty) +{ +} + +static void viotty_start(struct tty_struct *tty) +{ +} + +static void viotty_hangup(struct tty_struct *tty) +{ +} + +static void viotty_break(struct tty_struct *tty, int break_state) +{ +} + +static void viotty_send_xchar(struct tty_struct *tty, char ch) +{ +} + +static void viotty_wait_until_sent(struct tty_struct *tty, int timeout) +{ +} + +/*************************************************************************** + * Handle an open charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleOpenEvent(struct HvLpEvent *event) +{ + unsigned long flags; + u8 eventRc; + u16 eventSubtypeRc; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + if (event->xRc == HvLpEvent_Rc_Good) + { + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + port_info[port].lp = event->xTargetLp; + + spin_unlock_irqrestore(&consolelock, + flags); + + if(event->xCorrelationToken != 0) + { + unsigned long semptr = event->xCorrelationToken; + up((struct semaphore *)semptr); + } + else + printk("viocons: wierd...got open ack without semaphore\n"); + } + else + { + /* This had better require an ack, otherwise complain + */ + if (event->xFlags.xAckInd != HvLpEvent_AckInd_DoAck) + { + printk("viocons: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) + { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + + /* If this is tty is already connected to a different + partition, fail */ + else if ((port_info[port].lp != HvLpIndexInvalid) && + (port_info[port].lp != event->xSourceLp)) + { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + else + { + port_info[port].lp = event->xSourceLp; + eventRc = HvLpEvent_Rc_Good; + eventSubtypeRc = viorc_good; + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + spin_unlock_irqrestore(&consolelock, + flags); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); + } +} + +/*************************************************************************** + * Handle a close open charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleCloseEvent(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + if (port >= VTTY_PORTS) + return; + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, + flags); + /* Got the lock, don't cause console output */ + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, + flags); + printk("viocons: console close from %d\n", event->xSourceLp); + } + else + { + printk("viocons: got unexpected close ack\n"); + } +} + +/*************************************************************************** + * Handle a config charLpEvent. Could be either interrupt or ack + ***************************************************************************/ +static void vioHandleConfig( struct HvLpEvent *event ) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + int len; + + len = cevent->immediateDataLen; + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); + + if ( cevent->immediateData[0] == 0x01 ) + { + printk("viocons: window resized to %d: %d: %d: %d\n", + cevent->immediateData[1], + cevent->immediateData[2], + cevent->immediateData[3], + cevent->immediateData[4]); + } + else + { + printk("viocons: unknown config event\n"); + } + return; +} + +/*************************************************************************** + * Handle a data charLpEvent. + ***************************************************************************/ +static void vioHandleData(struct HvLpEvent *event) +{ + struct tty_struct *tty; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + struct port_info_tag * pi; + int len; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) + { + printk("viocons: data on invalid virtual device %d\n",port); + return; + } + + tty = port_info[port].tty; + + if (tty == NULL) + { + printk("viocons: no tty for virtual device %d\n",port); + return; + } + + if (tty->magic != TTY_MAGIC) + { + printk("viocons: tty bad magic\n"); + return; + } + + /* + * Just to be paranoid, make sure the tty points back to this port + */ + pi = (struct port_info_tag *)tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "vioHandleData")) + return; + + len = cevent->immediateDataLen; + + if (len == 0) + return; + + /* + * Log port 0 data to the hypervisor log + */ + if (port == 0) + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + len > TTY_FLIPBUF_SIZE) + { + len = TTY_FLIPBUF_SIZE - tty->flip.count; + printk("viocons: flip buffer overflow!\n"); + } + + memcpy (tty->flip.char_buf_ptr, + cevent->immediateData, + len); + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, len); + + /* Update the kernel buffer end */ + tty->flip.count += len; + tty->flip.char_buf_ptr += len; + + tty->flip.flag_buf_ptr += len; + + tty_flip_buffer_push (tty); +} + +/*************************************************************************** + * Handle an ack charLpEvent. + ***************************************************************************/ +static void vioHandleAck(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + unsigned long flags; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) + { + printk("viocons: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, + flags); + sndMsgAck[port] = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, + flags); + + if (overflow[port].bufferUsed) + sendBuffers(port, port_info[port].lp); +} + +/*************************************************************************** + * Handle charLpEvents and route to the appropriate routine + ***************************************************************************/ +static void vioHandleCharEvent(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) + { + return; + } + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) + { + case viocharopen: + vioHandleOpenEvent(event); + break; + case viocharclose: + vioHandleCloseEvent(event); + break; + case viochardata: + vioHandleData(event); + break; + case viocharack: + vioHandleAck(event); + break; + case viocharconfig: + vioHandleConfig(event); + break; + default: + } +} + +/*************************************************************************** + * Send an open event + ***************************************************************************/ +static int viocons_sendOpen(HvLpIndex remoteLp, u8 port, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario | viocharopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(remoteLp), + viopath_targetinst(remoteLp), + (u64)(unsigned long)sem, + VIOVERSION << 16, + ((u64)port << 48), + 0, 0, 0); + +} + +int __init viocons_init2(void) +{ +// extern char saved_command_line[]; + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + + /* + * Now open to the primary LP + */ + printk("viocons: init2 - open path to primary\n"); + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario); + if (rc) + { + printk("viocons: error on viopath_open to primary %d\n",rc); + } + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) + { + printk("viocons: init2 - open path to hosting (%d) (%d)\n", + viopath_hostLp, HvLpConfig_getPrimaryLpIndex()); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario); + if (rc) + { + printk("viocons: error on viopath_open to hostlp %d\n",rc); + } + } + + vio_setCharHandler(vioHandleCharEvent); + + printk("viocons major is %d\n",TTY_MAJOR); + + /* First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + if ((viopath_isactive(viopath_hostLp)) && + (viocons_sendOpen(viopath_hostLp, 0, &Semaphore) == 0)) + { + printk("viocons: init2 - open cons to hosting\n"); + down(&Semaphore); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (viocons_sendOpen(HvLpConfig_getPrimaryLpIndex(), 0, &Semaphore) == 0)) + { + printk("viocons: init2 - open cons to primary\n"); + down(&Semaphore); + } + + printk("viocons: init2 - initializing tty\n"); + + /* Initialize the tty_driver structure */ + memset(&viotty_driver, 0, sizeof(struct tty_driver)); + viotty_driver.magic = TTY_DRIVER_MAGIC; + viotty_driver.driver_name = "vioconsole"; +#if defined(CONFIG_DEVFS_FS) + viotty_driver.name = "tty%d"; +#else + viotty_driver.name = "tty"; +#endif + viotty_driver.major = TTY_MAJOR; + viotty_driver.minor_start = 1; + viotty_driver.name_base = 1; + viotty_driver.num = VTTY_PORTS; + viotty_driver.type = TTY_DRIVER_TYPE_CONSOLE; + viotty_driver.subtype = 1; + viotty_driver.init_termios = tty_std_termios; + viotty_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + viotty_driver.refcount = &viotty_refcount; + viotty_driver.table = viotty_table; + viotty_driver.termios = viotty_termios; + viotty_driver.termios_locked = viotty_termios_locked; + + viotty_driver.open = viotty_open; + viotty_driver.close = viotty_close; + viotty_driver.write = viotty_write; + viotty_driver.put_char = viotty_put_char; + viotty_driver.flush_chars = viotty_flush_chars; + viotty_driver.write_room = viotty_write_room; + viotty_driver.chars_in_buffer = viotty_chars_in_buffer; + viotty_driver.flush_buffer = viotty_flush_buffer; + viotty_driver.ioctl = viotty_ioctl; + viotty_driver.throttle = viotty_throttle; + viotty_driver.unthrottle = viotty_unthrottle; + viotty_driver.set_termios = viotty_set_termios; + viotty_driver.stop = viotty_stop; + viotty_driver.start = viotty_start; + viotty_driver.hangup = viotty_hangup; + viotty_driver.break_ctl = viotty_break; + viotty_driver.send_xchar = viotty_send_xchar; + viotty_driver.wait_until_sent = viotty_wait_until_sent; + + viottyS_driver = viotty_driver; +#if defined(CONFIG_DEVFS_FS) + viottyS_driver.name = "ttyS%d"; +#else + viottyS_driver.name = "ttyS"; +#endif + viottyS_driver.major = TTY_MAJOR; + viottyS_driver.minor_start = VIOTTY_SERIAL_START; + viottyS_driver.type = TTY_DRIVER_TYPE_SERIAL; + viottyS_driver.table = viottyS_table; + viottyS_driver.termios = viottyS_termios; + viottyS_driver.termios_locked = viottyS_termios_locked; + + if (tty_register_driver(&viotty_driver)) + { + printk("Couldn't register viotty driver\n"); + } + + if (tty_register_driver(&viottyS_driver)) + { + printk("Couldn't register viottyS driver\n"); + } + + // Now create the vcs and vcsa devfs entries so mingetty works +#if defined(CONFIG_DEVFS_FS) + { + struct tty_driver temp_driver = viotty_driver; + int i; + + temp_driver.name = "vcs%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + + temp_driver.name = "vcsa%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + + // For compatibility with some earlier code only! + // This will go away!!! + temp_driver.name = "viocons/%d"; + temp_driver.name_base = 0; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs (&temp_driver, + 0, + i + temp_driver.minor_start); + } +#endif + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viocons_proc_init); + + return 0; +} + +void __init viocons_init(void) +{ + int i; + printk("viocons registering console\n"); + + memset(&port_info, 0x00, sizeof(port_info)); + for (i=0; i + * (C) Copyright 2000 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include "asm/iSeries/vio.h" +#endif + +#ifndef _HVLPEVENT_H +#include +#endif + +#ifndef _HVCALLEVENT_H +#include "asm/iSeries/HvCallEvent.h" +#endif +#ifndef _HVLPCONFIG_H +#include "asm/iSeries/HvLpConfig.h" +#endif + +static DECLARE_WAIT_QUEUE_HEAD(viotape_wait_queue); + +static int viotape_major = 101; +static int viotape_numdev = 0; + +static u64 sndMsgSeq; +static u64 sndMsgAck; +static u64 rcvMsgSeq; +static u64 rcvMsgAck; + +#define VIOTAPE_MAX_TAPE 8 + +static struct { + char rsrcname[10]; + char type[4]; + char model[3]; +} viotape_unitinfo[VIOTAPE_MAX_TAPE]; + +static struct mtget viomtget[VIOTAPE_MAX_TAPE]; + +static struct semaphore reqSem; + +struct opStruct { + void *buffer; + dma_addr_t dmaaddr; + size_t count; + int rc; + struct semaphore *sem; + struct opStruct *free; +}; + +static spinlock_t opStructListLock; +static struct opStruct *opStructList; + +static struct opStruct *getOpStruct(void) +{ + struct opStruct *newOpStruct; + spin_lock(&opStructListLock); + + if (opStructList == NULL) + { + newOpStruct = kmalloc(sizeof(struct opStruct), GFP_KERNEL); + + } + else + { + newOpStruct = opStructList; + opStructList = opStructList->free; + } + + if (newOpStruct) + memset(newOpStruct,0x00,sizeof(struct opStruct)); + + spin_unlock(&opStructListLock); + + return newOpStruct; +} + +static void freeOpStruct(struct opStruct *opStruct) +{ + spin_lock(&opStructListLock); + opStruct->free = opStructList; + opStructList = opStruct; + spin_unlock(&opStructListLock); +} + +static void get_viotape_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + DECLARE_MUTEX_LOCKED(Semaphore); + + memset(viotape_unitinfo, 0x00, sizeof(viotape_unitinfo)); + + dmaaddr = pci_map_single(NULL, &viotape_unitinfo, + sizeof(viotape_unitinfo), + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) + { + printk("viotape error allocating tce\n"); + return; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapegetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)&Semaphore, + VIOVERSION << 16, + dmaaddr, + sizeof(viotape_unitinfo), + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + } + + down(&Semaphore); + + for (i=0; + ((i < VIOTAPE_MAX_TAPE) && (viotape_unitinfo[i].rsrcname[0])); + i++) + { + viotape_numdev++; + } +} + +static long long viotap_llseek(struct file *file, long long offset, int origin) +{ + return -EINVAL; /* not supported */ +} + +static ssize_t viotap_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + DECLARE_MUTEX_LOCKED(Semaphore); + + if (op == NULL) + return -ENOMEM; + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) + { + if (down_trylock(&reqSem)) + { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } + else + { + down(&reqSem); + } + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(NULL, + count, + &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) + { + printk("viotape error allocating dma buffer for len %d\n", + count); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + /* Copy the data into the buffer */ + err = copy_from_user( op->buffer, (const void *) buf, count); + if (err) + { + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + if (noblock) + { + op->sem = NULL; + } + else + { + op->sem = &Semaphore; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapewrite, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)dev << 48) | + op->dmaaddr, + count, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + if (noblock) + return count; + + down(&Semaphore); + + err = op->rc; + /* Free the buffer */ + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + if (err) + return -EIO; + else + return 0; +} + +/** + * viotap_read: + */ +static ssize_t viotap_read(struct file *file, char *buf, size_t count, loff_t *ptr) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + DECLARE_MUTEX_LOCKED(Semaphore); + + if (op == NULL) + return -ENOMEM; + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) + { + if (down_trylock(&reqSem)) + { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } + else + { + down(&reqSem); + } + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(NULL, + count, + &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) + { + printk("viotape error allocating dma buffer for len %d\n", + count); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotaperead, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)dev << 48) | + op->dmaaddr, + count, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + down(&Semaphore); + + if (op->rc == 0) + { + /* Copy the data into the buffer */ + err = copy_to_user( buf, op->buffer, count); + if (err) + { + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + } + + err = op->rc; + /* Free the buffer */ + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + if (err) + return -EIO; + else + return 0; +} + +/** + * viotap_ioctl: + */ +static int viotap_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + HvLpEvent_Rc hvrc; + int err; + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + struct opStruct *op = getOpStruct(); + if (op == NULL) + return -ENOMEM; + + down(&reqSem); + + switch (cmd) + { + case MTIOCTOP: + { + struct mtop mtc; + u32 myOp; + + if (copy_from_user((char *) &mtc, (char *) arg, sizeof(struct mtop))) + { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + switch(mtc.mt_op) + { + case MTRESET: myOp = VIOTAPOP_RESET; break; + case MTFSF: myOp = VIOTAPOP_FSF; break; + case MTBSF: myOp = VIOTAPOP_BSF; break; + case MTFSR: myOp = VIOTAPOP_FSR; break; + case MTBSR: myOp = VIOTAPOP_BSR; break; + case MTWEOF: myOp = VIOTAPOP_WEOF; break; + case MTREW: myOp = VIOTAPOP_REW; break; + case MTNOP: myOp = VIOTAPOP_NOP; break; + case MTEOM: myOp = VIOTAPOP_EOM; break; + case MTERASE: myOp = VIOTAPOP_ERASE; break; + case MTSETBLK: myOp = VIOTAPOP_SETBLK; break; + case MTSETDENSITY: myOp = VIOTAPOP_SETDENSITY; break; + case MTTELL: myOp = VIOTAPOP_GETPOS; break; + case MTSEEK: myOp = VIOTAPOP_SETPOS; break; + case MTSETPART: myOp = VIOTAPOP_SETPART; break; + default: + return -EIO; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)dev << 48) | myOp, + ((u64)mtc.mt_count) << 32, + 0, 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + if (op->rc) + { + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + else + { + freeOpStruct(op); + up(&reqSem); + return 0; + } + break; + } + + case MTIOCGET: + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapegetstatus , + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)dev << 48), + 0, 0, 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viotape hv error on op %d\n",(int)hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + up(&reqSem); + if (op->rc) + { + freeOpStruct(op); + return -EIO; + } + else + { + freeOpStruct(op); + err = copy_to_user((void *)arg, &viomtget[dev], sizeof(viomtget[0])); + if (err) + { + freeOpStruct(op); + return -EFAULT; + } + return 0; + } + break; + case MTIOCPOS: + printk("Got an MTIOCPOS\n"); + default: + return -ENOSYS; + } + return 0; +} + +/** + * viotap_open: + */ + +static int viotap_open(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct opStruct *op = getOpStruct(); + if (op == NULL) + return -ENOMEM; + + if (dev >= viotape_numdev) + { + freeOpStruct(op); + return -ENODEV; + } + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(u32)op, + VIOVERSION << 16, + ((u64)dev << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + printk("viotape bad rc on signalLpEvent %d\n",(int)hvrc); + freeOpStruct(op); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) + { + freeOpStruct(op); + return -EIO; + } + else + { + freeOpStruct(op); + return 0; + } +} + +/** + * viotap_close: + */ + +static int viotap_release(struct inode *inode, struct file *file) +{ + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + + if (dev >= viotape_numdev) + return -EINVAL; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | viotapeclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64)dev << 48), + 0, 0, 0); + + + if (hvrc != 0) + { + printk("viotape bad rc on signalLpEvent %d\n",(int)hvrc); + return -EIO; + } + return 0; +} + +struct file_operations viotap_fops = { + owner: THIS_MODULE, + llseek: viotap_llseek, + read: viotap_read, + write: viotap_write, + ioctl: viotap_ioctl, + open: viotap_open, + release: viotap_release, +}; + +static void vioHandleTapeEvent(struct HvLpEvent *event) +{ + int tapeminor; + struct opStruct *op; + + if (event == NULL) + { + /* Notification that a partition went away! */ + if (!viopath_isactive(viopath_hostLp)) + { + /* TODO! Clean up */ + } + return; + } + tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (tapeminor) + { + case viotapegetinfo: + case viotapeopen: + up((void*)(u32)event->xCorrelationToken); + break; + case viotapeclose: + break; + case viotaperead: + case viotapewrite: + op = (struct opStruct *)(u32)event->xCorrelationToken; + op->rc = event->xRc; + + if (op->sem) + { + up(op->sem); + } + else + { + pci_free_consistent(NULL, op->count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + } + case viotapeop: + case viotapegetpos: + case viotapesetpos: + case viotapegetstatus: + default: + } +} + + +int __init viotap_init(void) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + char tapename[32]; + int i; + + sndMsgSeq = sndMsgAck = 0; + rcvMsgSeq = rcvMsgAck = 0; + opStructList = NULL; + spin_lock_init(&opStructListLock); + + sema_init(&reqSem, VIOTAPE_MAXREQ); + + /* + * Open to our hosing lp + */ + if (viopath_hostLp == HvLpIndexInvalid) + return -1; + + printk("viotape: init - open path to hosting (%d)\n", + viopath_hostLp); + + rc = viopath_open(viopath_hostLp, viomajorsubtype_tape); + if (rc) + { + printk("viotape: error on viopath_open to hostlp %d\n",rc); + } + + vio_setTapeHandler(vioHandleTapeEvent); + + printk("viotape major is %d\n",viotape_major); + + get_viotape_info(); + + if (devfs_register_chrdev(viotape_major, "viotape", &viotap_fops)) + { + printk("Error registering viotape device\n"); + return -1; + } + + for (i=0; i= 0x20400 -static struct pci_device_id acenic_pci_tbl[] __initdata = { - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - /* - * Farallon used the DEC vendor ID on their cards incorrectly. - */ - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { } -}; -MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); -#endif - #ifndef wmb #define wmb() mb() @@ -521,230 +497,188 @@ "acenic.c: v0.81 04/20/2001 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static struct net_device *root_dev; - -static int probed __initdata = 0; - - -int __devinit acenic_probe (ACE_PROBE_ARG) +static int __devinit acenic_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { -#ifdef NEW_NETINIT struct net_device *dev; -#endif - struct ace_private *ap; - struct pci_dev *pdev = NULL; int boards_found = 0; int version_disp; - if (probed) - return -ENODEV; - probed++; - if (!pci_present()) /* is PCI support present? */ return -ENODEV; version_disp = 0; - while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) { + dev = init_etherdev(NULL, sizeof(struct ace_private)); - if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) && - ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) || - (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&& - !((pdev->vendor == PCI_VENDOR_ID_3COM) && - (pdev->device == PCI_DEVICE_ID_3COM_3C985)) && - !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) && - ((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) || - (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) && - /* - * Farallon used the DEC vendor ID on their cards by - * mistake for a while - */ - !((pdev->vendor == PCI_VENDOR_ID_DEC) && - (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) && - !((pdev->vendor == PCI_VENDOR_ID_SGI) && - (pdev->device == PCI_DEVICE_ID_SGI_ACENIC))) - continue; - - dev = init_etherdev(NULL, sizeof(struct ace_private)); - - if (dev == NULL) { - printk(KERN_ERR "acenic: Unable to allocate " - "net_device structure!\n"); - break; - } + if (dev == NULL) { + printk(KERN_ERR "acenic: Unable to allocate " + "net_device structure!\n"); + return -ENODEV; + } - SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(dev); - if (!dev->priv) - dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); - if (!dev->priv) { - printk(KERN_ERR "acenic: Unable to allocate memory\n"); - return -ENOMEM; - } - - ap = dev->priv; - ap->pdev = pdev; - - dev->irq = pdev->irq; - dev->open = &ace_open; - dev->hard_start_xmit = &ace_start_xmit; - dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; - if (1) { - static void ace_watchdog(struct net_device *dev); - dev->tx_timeout = &ace_watchdog; - dev->watchdog_timeo = 5*HZ; - } - dev->stop = &ace_close; - dev->get_stats = &ace_get_stats; - dev->set_multicast_list = &ace_set_multicast_list; - dev->do_ioctl = &ace_ioctl; - dev->set_mac_address = &ace_set_mac_addr; - dev->change_mtu = &ace_change_mtu; + if (!dev->priv) + dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); + if (!dev->priv) { + printk(KERN_ERR "acenic: Unable to allocate memory\n"); + return -ENOMEM; + } - /* display version info if adapter is found */ - if (!version_disp) - { - /* set display flag to TRUE so that */ - /* we only display this string ONCE */ - version_disp = 1; - printk(version); - } + ap = dev->priv; + ap->pdev = pdev; - /* - * Enable master mode before we start playing with the - * pci_command word since pci_set_master() will modify - * it. - */ - pci_set_master(pdev); + dev->irq = pdev->irq; + dev->open = &ace_open; + dev->hard_start_xmit = &ace_start_xmit; + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; + if (1) { + static void ace_watchdog(struct net_device *dev); + dev->tx_timeout = &ace_watchdog; + dev->watchdog_timeo = 5*HZ; + } + dev->stop = &ace_close; + dev->get_stats = &ace_get_stats; + dev->set_multicast_list = &ace_set_multicast_list; + dev->do_ioctl = &ace_ioctl; + dev->set_mac_address = &ace_set_mac_addr; + dev->change_mtu = &ace_change_mtu; - pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); + /* display version info if adapter is found */ + if (!version_disp) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + version_disp = 1; + printk(version); + } - /* OpenFirmware on Mac's does not set this - DOH.. */ - if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { - printk(KERN_INFO "%s: Enabling PCI Memory Mapped " - "access - was not enabled by BIOS/Firmware\n", - dev->name); - ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; - pci_write_config_word(ap->pdev, PCI_COMMAND, - ap->pci_command); - wmb(); - } + /* + * Enable master mode before we start playing with the + * pci_command word since pci_set_master() will modify + * it. + */ + pci_set_master(pdev); - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, - &ap->pci_latency); - if (ap->pci_latency <= 0x40) { - ap->pci_latency = 0x40; - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, - ap->pci_latency); - } + pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); - /* - * Remap the regs into kernel space - this is abuse of - * dev->base_addr since it was means for I/O port - * addresses but who gives a damn. - */ - dev->base_addr = pci_resource_start(pdev, 0); - ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); - if (!ap->regs) { - printk(KERN_ERR "%s: Unable to map I/O register, " - "AceNIC %i will be disabled.\n", - dev->name, boards_found); - break; - } + /* OpenFirmware on Mac's does not set this - DOH.. */ + if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { + printk(KERN_INFO "%s: Enabling PCI Memory Mapped " + "access - was not enabled by BIOS/Firmware\n", + dev->name); + ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; + pci_write_config_word(ap->pdev, PCI_COMMAND, + ap->pci_command); + wmb(); + } - switch(pdev->vendor) { - case PCI_VENDOR_ID_ALTEON: - strncpy(ap->name, "AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); - break; - case PCI_VENDOR_ID_3COM: - strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); - break; - case PCI_VENDOR_ID_NETGEAR: - strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: NetGear GA620 ", dev->name); - break; - case PCI_VENDOR_ID_DEC: - if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { - strncpy(ap->name, "Farallon PN9000-SX " - "Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Farallon PN9000-SX ", - dev->name); - break; - } - case PCI_VENDOR_ID_SGI: - strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: SGI AceNIC ", dev->name); - break; - default: - strncpy(ap->name, "Unknown AceNIC based Gigabit " - "Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, + &ap->pci_latency); + if (ap->pci_latency <= 0x40) { + ap->pci_latency = 0x40; + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + ap->pci_latency); + } + + /* + * Remap the regs into kernel space - this is abuse of + * dev->base_addr since it was means for I/O port + * addresses but who gives a damn. + */ + dev->base_addr = pci_resource_start(pdev, 0); + ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); + if (!ap->regs) { + printk(KERN_ERR "%s: Unable to map I/O register, " + "AceNIC %i will be disabled.\n", + dev->name, boards_found); + return -ENODEV; + } + + switch(pdev->vendor) { + case PCI_VENDOR_ID_ALTEON: + strncpy(ap->name, "AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); + break; + case PCI_VENDOR_ID_3COM: + strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); + break; + case PCI_VENDOR_ID_NETGEAR: + strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: NetGear GA620 ", dev->name); + break; + case PCI_VENDOR_ID_DEC: + if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { + strncpy(ap->name, "Farallon PN9000-SX " + "Gigabit Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Farallon PN9000-SX ", + dev->name); break; } - ap->name [sizeof (ap->name) - 1] = '\0'; - printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); + case PCI_VENDOR_ID_SGI: + strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: SGI AceNIC ", dev->name); + break; + default: + strncpy(ap->name, "Unknown AceNIC based Gigabit " + "Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + break; + } + ap->name [sizeof (ap->name) - 1] = '\0'; + printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); #ifdef __sparc__ - printk("irq %s\n", __irq_itoa(dev->irq)); + printk("irq %s\n", __irq_itoa(dev->irq)); #else - printk("irq %i\n", dev->irq); + printk("irq %i\n", dev->irq); #endif #ifdef CONFIG_ACENIC_OMIT_TIGON_I - if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { - printk(KERN_ERR "%s: Driver compiled without Tigon I" - " support - NIC disabled\n", dev->name); - ace_init_cleanup(dev); - kfree(dev); - continue; - } + if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { + printk(KERN_ERR "%s: Driver compiled without Tigon I" + " support - NIC disabled\n", dev->name); + ace_init_cleanup(dev); + kfree(dev); + return -ENODEV; + } #endif - if (ace_allocate_descriptors(dev)) { - /* - * ace_allocate_descriptors() calls - * ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + if (ace_allocate_descriptors(dev)) { + /* + * ace_allocate_descriptors() calls + * ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; + } #ifdef MODULE - if (boards_found >= ACE_MAX_MOD_PARMS) - ap->board_idx = BOARD_IDX_OVERFLOW; - else - ap->board_idx = boards_found; + if (boards_found >= ACE_MAX_MOD_PARMS) + ap->board_idx = BOARD_IDX_OVERFLOW; + else + ap->board_idx = boards_found; #else - ap->board_idx = BOARD_IDX_STATIC; + ap->board_idx = BOARD_IDX_STATIC; #endif - if (ace_init(dev)) { - /* - * ace_init() calls ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + pdev->driver_data = dev; - boards_found++; + if (ace_init(dev)) { + /* + * ace_init() calls ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; } - /* - * If we're at this point we're going through ace_probe() for - * the first time. Return success (0) if we've initialized 1 - * or more boards. Otherwise, return failure (-ENODEV). - */ - - if (boards_found > 0) - return 0; - else - return -ENODEV; + return 0; } @@ -759,136 +693,95 @@ MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); #endif - -static void __exit ace_module_cleanup(void) +static void __devexit acenic_remove_one(struct pci_dev *pdev) { + struct net_device *dev = pdev->driver_data; struct ace_private *ap; struct ace_regs *regs; - struct net_device *next; short i; - while (root_dev) { - ap = root_dev->priv; - next = ap->next; - - regs = ap->regs; + ap = dev->priv; + regs = ap->regs; - writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); - if (ap->version >= 2) - writel(readl(®s->CpuBCtrl) | CPU_HALT, - ®s->CpuBCtrl); - /* - * This clears any pending interrupts - */ - writel(1, ®s->Mb0Lo); + writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); + if (ap->version >= 2) + writel(readl(®s->CpuBCtrl) | CPU_HALT, + ®s->CpuBCtrl); + /* + * This clears any pending interrupts + */ + writel(1, ®s->Mb0Lo); - /* - * Make sure no other CPUs are processing interrupts - * on the card before the buffers are being released. - * Otherwise one might experience some `interesting' - * effects. - * - * Then release the RX buffers - jumbo buffers were - * already released in ace_close(). - */ - synchronize_irq(); + /* + * Make sure no other CPUs are processing interrupts + * on the card before the buffers are being released. + * Otherwise one might experience some `interesting' + * effects. + * + * Then release the RX buffers - jumbo buffers were + * already released in ace_close(). + */ + synchronize_irq(); - for (i = 0; i < RX_STD_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; + for (i = 0; i < RX_STD_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; - if (skb) { + if (skb) { #ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + dma_addr_t mapping; - mapping = ap->skb->rx_std_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_STD_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); + mapping = ap->skb->rx_std_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_STD_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - ap->rx_std_ring[i].size = 0; - ap->skb->rx_std_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } + ap->rx_std_ring[i].size = 0; + ap->skb->rx_std_skbuff[i].skb = NULL; + dev_kfree_skb(skb); } - if (ap->version >= 2) { - for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - - if (skb) { -#ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + } + if (ap->version >= 2) { + for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - mapping = ap->skb->rx_mini_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_MINI_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); -#endif - ap->rx_mini_ring[i].size = 0; - ap->skb->rx_mini_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } - } - } - for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; if (skb) { #ifndef DUMMY_PCI_UNMAP dma_addr_t mapping; - mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + mapping = ap->skb->rx_mini_skbuff[i].mapping; pci_unmap_single(ap->pdev, mapping, - ACE_JUMBO_BUFSIZE - (2 + 16), + ACE_MINI_BUFSIZE - (2 + 16), PCI_DMA_FROMDEVICE); #endif - - ap->rx_jumbo_ring[i].size = 0; - ap->skb->rx_jumbo_skbuff[i].skb = NULL; + ap->rx_mini_ring[i].size = 0; + ap->skb->rx_mini_skbuff[i].skb = NULL; dev_kfree_skb(skb); } } - - ace_init_cleanup(root_dev); - kfree(root_dev); - root_dev = next; } -} - - -int __init ace_module_init(void) -{ - int status; - - root_dev = NULL; + for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; + if (skb) { +#ifndef DUMMY_PCI_UNMAP + dma_addr_t mapping; -#ifdef NEW_NETINIT - status = acenic_probe(); -#else - status = acenic_probe(NULL); + mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_JUMBO_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - return status; -} + ap->rx_jumbo_ring[i].size = 0; + ap->skb->rx_jumbo_skbuff[i].skb = NULL; + dev_kfree_skb(skb); + } + } -#if (LINUX_VERSION_CODE < 0x02032a) -#ifdef MODULE -int init_module(void) -{ - return ace_module_init(); + ace_init_cleanup(dev); + kfree(dev); } - -void cleanup_module(void) -{ - ace_module_cleanup(); -} -#endif -#else -module_init(ace_module_init); -module_exit(ace_module_cleanup); -#endif - - static void ace_free_descriptors(struct net_device *dev) { struct ace_private *ap = dev->priv; @@ -1324,13 +1217,6 @@ goto init_error; } - /* - * Register the device here to be able to catch allocated - * interrupt handlers in case the firmware doesn't come up. - */ - ap->next = root_dev; - root_dev = dev; - #ifdef INDEX_DEBUG spin_lock_init(&ap->debug_lock); ap->last_tx = TX_RING_ENTRIES - 1; @@ -3338,3 +3224,45 @@ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ + +static struct pci_device_id acenic_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + /* + * Farallon used the DEC vendor ID on their cards incorrectly. + */ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { } +}; +MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); + +static struct pci_driver acenic_driver = { + name: "acenic", + id_table: acenic_pci_tbl, + probe: acenic_init_one, + remove: acenic_remove_one, +}; + +static int __init acenic_init_module(void) +{ + return pci_module_init(&acenic_driver); +} + +static void __exit acenic_cleanup_module(void) +{ + pci_unregister_driver(&acenic_driver); +} + +module_init(acenic_init_module); +module_exit(acenic_cleanup_module); diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/net/pcnet32.c linuxppc64_2_4/drivers/net/pcnet32.c --- ../w3.wed/linux-2.4.8-ac5/drivers/net/pcnet32.c Wed Aug 15 12:11:32 2001 +++ linuxppc64_2_4/drivers/net/pcnet32.c Thu Aug 16 08:50:26 2001 @@ -294,7 +294,7 @@ static int pcnet32_probe_vlbus(int cards_found); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); -static int pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *); +static int pcnet32_probe1(unsigned long, unsigned int, int, int, struct pci_dev *); static int pcnet32_open(struct net_device *); static int pcnet32_init_ring(struct net_device *); static int pcnet32_start_xmit(struct sk_buff *, struct net_device *); @@ -317,7 +317,7 @@ const char *name; u16 vendor_id, device_id, svid, sdid, flags; int io_size; - int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *); + int (*probe1) (unsigned long, unsigned int, int, int, struct pci_dev *); }; @@ -440,7 +440,9 @@ static int __init pcnet32_probe_vlbus(int cards_found) { unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0; +#ifndef __powerpc__ unsigned int irq_line = 0; // FIXME dev ? dev->irq : 0; +#endif int *port; printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found); @@ -508,7 +510,7 @@ * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit -pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) +pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, int card_idx, struct pci_dev *pdev) { struct pcnet32_private *lp; struct resource *res; @@ -527,12 +529,13 @@ pcnet32_dwio_reset(ioaddr); pcnet32_wio_reset(ioaddr); - /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) { - a = &pcnet32_wio; + /* Important to do the check for dwio mode first. */ + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { + a = &pcnet32_dwio; } else { - if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { - a = &pcnet32_dwio; + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && + pcnet32_wio_check(ioaddr)) { + a = &pcnet32_wio; } else return -ENODEV; } @@ -643,19 +646,31 @@ for (i = 0; i < 6; i++) { promaddr[i] = inb(ioaddr + i); } + printk("csraddr: "); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] ); + printk("\npromaddr: "); + for (i = 0; i < 6; i++) + printk(" %2.2x", promaddr[i] ); + printk("\n"); if( memcmp( promaddr, dev->dev_addr, 6) ) - { - printk(" warning PROM address does not match CSR address\n"); -#if defined(__i386__) - printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); - memcpy(dev->dev_addr, promaddr, 6); -#endif - } + printk(" warning: PROM address does not match CSR address\n"); + if( !is_valid_ether_addr(dev->dev_addr) ) { + printk("bad csr addr\n"); + if (!is_valid_ether_addr(promaddr)) { + printk("bad promaddr\n"); + /* if neither ethernet address is not valid, force to 00:00:00:00:00:00 */ + for (i = 0; i < 6; i++) + dev->dev_addr[i]=0; + } + else + { + printk(" warning: Using PROM address\n"); + for (i = 0; i < 6; i++) + dev->dev_addr[i]=promaddr[i]; + } + } } - /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ - if( !is_valid_ether_addr(dev->dev_addr) ) - for (i = 0; i < 6; i++) - dev->dev_addr[i]=0; for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] ); @@ -885,7 +900,7 @@ lp->init_block.filter[1] = 0x00000000; if (pcnet32_init_ring(dev)) return -ENOMEM; - + /* Re-initialize the PCNET32, and start it when done. */ lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff); lp->a.write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16); @@ -967,7 +982,7 @@ } skb_reserve (rx_skbuff, 2); } - lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, rx_skbuff->len, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]); lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); lp->rx_ring[i].status = le16_to_cpu(0x8000); @@ -1301,7 +1316,8 @@ skb_put (skb, pkt_len); lp->rx_skbuff[entry] = newskb; newskb->dev = dev; - lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]); rx_in_place = 1; } else @@ -1387,7 +1403,7 @@ for (i = 0; i < RX_RING_SIZE; i++) { lp->rx_ring[i].status = 0; if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], lp->rx_skbuff[i]->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); dev_kfree_skb(lp->rx_skbuff[i]); } lp->rx_skbuff[i] = NULL; diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/net/veth-proc.c linuxppc64_2_4/drivers/net/veth-proc.c --- ../w3.wed/linux-2.4.8-ac5/drivers/net/veth-proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth-proc.c Fri May 4 17:12:47 2001 @@ -0,0 +1,73 @@ +/* File veth-proc.c created by Kyle A. Lucke on Wed Oct 18 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_H +#include "veth.h" +#endif + + +static struct proc_dir_entry * veth_proc_root = NULL; + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + long i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + long vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lp%ld", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%ld", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } +} + diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/net/veth.c linuxppc64_2_4/drivers/net/veth.c --- ../w3.wed/linux-2.4.8-ac5/drivers/net/veth.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth.c Mon Aug 13 11:20:53 2001 @@ -0,0 +1,1719 @@ +/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/**************************************************************************/ +/* */ +/* IBM eServer iSeries Virtual Ethernet Device Driver */ +/* Copyright (C) 2001 Kyle A. Lucke (klucke@raleigh.ibm.com), IBM Corp. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ +/* USA */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with iSeries LPAR Linux. It utilizes low-level message passing*/ +/* provided by the hypervisor to enable an ethernet-like network device */ +/* that can be used to enable inter-partition communications on the same */ +/* physical iSeries. */ +/* */ +/* The iSeries LPAR hypervisor has currently defined the ability for a */ +/* partition to communicate on up to 16 different virtual ethernets, all */ +/* dynamically configurable, at least for an OS/400 partition. The */ +/* dynamic nature is not supported for Linux yet. */ +/* */ +/* Each virtual ethernet a given Linux partition participates in will */ +/* cause a network device with the form vethXX to be created, where XX is */ +/* a decimal number from 0 to 15, corresponding to the virtual ethernet */ +/* the given netdevice talks on. This is slightly different from the */ +/* standard eth0, eth1, etc. way of naming network devices, but without */ +/* this little naming convention, it would not be as easy to configure */ +/* the tcpip interfaces to a given veth device, and if the partition */ +/* was configured to use a new virtual ethernet at some point, the devices*/ +/* would most likely get renumbered. */ +/* */ +/* This driver (and others like it on other partitions) is responsible */ +/* routing packets to and from other partitions. The MAC addresses used */ +/* by the virtual ethernets contain meaning, and should not be modified. */ +/* Doing so could disable the ability of your Linux partition to */ +/* communicate with the other OS/400 partitions on your physical iSeries. */ +/* Similarly, setting the MAC address to something other than the */ +/* "virtual burned-in" address is not allowed, for the same reason. */ +/* */ +/* Notes: */ +/* */ +/* 1. Although there is the capability to talk on multiple shared */ +/* ethernets to communicate to the same partition, each shared */ +/* ethernet to a given partition X will use a finite, shared amount */ +/* of hypervisor messages to do the communication. So having 2 shared */ +/* ethernets to the same remote partition DOES NOT double the */ +/* available bandwidth. Each of the 2 shared ethernets will share the */ +/* same bandwidth available to another. */ +/* */ +/* 2. It is allowed to have a virtual ethernet that does not communicate */ +/* with any other partition. It won't do anything, but it's allowed. */ +/* */ +/* 3. There is no "loopback" mode for a virtual ethernet device. If you */ +/* send a packet to your own mac address, it will just be dropped, you */ +/* won't get it on the receive side. Such a thing could be done, */ +/* but my default driver DOES NOT do so. */ +/* */ +/* 4. Multicast addressing is implemented via broadcasting the multicast */ +/* frames to other partitions. It is the responsibility of the */ +/* receiving partition to filter the addresses desired. */ +/* */ +/* 5. This module utilizes several different bottom half handlers for */ +/* non-high-use path function (setup, error handling, etc.). Multiple */ +/* bottom halves were used because only one would not keep up to the */ +/* much faster iSeries device drivers this Linux driver is talking to. */ +/* All hi-priority work (receiving frames, handling frame acks) is done*/ +/* in the interrupt handler for maximum performance. */ +/* */ +/* Tunable parameters: */ +/* */ +/* VethBuffersToAllocate: This compile time option defaults to 120. It can*/ +/* be safely changed to something greater or less than the default. It */ +/* controls how much memory Linux will allocate per remote partition it is*/ +/* communicating with. The user can play with this to see how it affects */ +/* performance, packets dropped, etc. Without trying to understand the */ +/* complete driver, it can be thought of as the maximum number of packets */ +/* outstanding to a remote partition at a time. */ +/* */ +/**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VETH_H +#include "veth.h" +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _ISERIES_PROC_H +#include +#endif +#include +#include + + +#define veth_printk(fmt, args...) \ +printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + +#define veth_error_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args) + +static const char __initdata *version = +"v0.9 02/15/2001 Kyle Lucke, klucke@raleigh.ibm.com, klucke@us.ibm.com\n"; + +static int probed __initdata = 0; +#define VethBuffersToAllocate 120 + +static struct VethFabricMgr *mFabricMgr = NULL; +static struct proc_dir_entry * veth_proc_root = NULL; + +DECLARE_MUTEX_LOCKED(VethProcSemaphore); + +static int veth_open(struct net_device *dev); +static int veth_close(struct net_device *dev); +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static void veth_handleEvent(struct HvLpEvent *, struct pt_regs *); +static void veth_handleAck(struct HvLpEvent *); +static void veth_handleInt(struct HvLpEvent *); +static void veth_openConnections(void); +static void veth_openConnection(u8, int lockMe); +static void veth_closeConnection(u8, int lockMe); +static void veth_intFinishOpeningConnections(void *, int number); +static void veth_finishOpeningConnections(void *); +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *); +static int veth_multicast_wanted(struct VethPort *port, u64 dest); +static void veth_set_multicast_list(struct net_device *dev); + +static void veth_sendCap(struct VethLpConnection *); +static void veth_sendMonitor(struct VethLpConnection *); +static void veth_takeCap(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeCapAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeMonitorAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_msgsInit(struct VethLpConnection *connection); +static void veth_recycleMsg(struct VethLpConnection *, u16); +static void veth_capBh(struct VethLpConnection *); +static void veth_capAckBh(struct VethLpConnection *); +static void veth_monitorAckBh(struct VethLpConnection *); +static void veth_takeFrames(struct VethLpConnection *, struct VethLpEvent *); +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev); +static struct net_device_stats *veth_get_stats(struct net_device *dev); +static void veth_intFinishMsgsInit(void *, int); +static void veth_finishMsgsInit(struct VethLpConnection *connection); +static void veth_intFinishCapBh(void *, int); +static void veth_finishCapBh(struct VethLpConnection *connection); +static void veth_finishCapBhLocked(struct VethLpConnection *connection); +static void veth_finishSendCap(struct VethLpConnection *connection); +static void veth_timedAck(unsigned long connectionPtr); +#ifdef MODULE +static void veth_waitForEnd(void); +#endif +static void veth_failMe(struct VethLpConnection *connection); + +int __init veth_probe(void) +{ + struct net_device *dev= NULL; + struct VethPort *port = NULL; + int vlansFound = 0; + int displayVersion = 0; + + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + if (probed) + return -ENODEV; + probed = 1; + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + vlansFound++; + + dev = init_vethdev(NULL, sizeof(struct VethPort), vlanIndex); + + if (dev == NULL) { + veth_error_printk("Unable to allocate net_device structure!\n"); + break; + } + + if (!dev->priv) + dev->priv = kmalloc(sizeof(struct VethPort), GFP_KERNEL); + if (!dev->priv) { + veth_error_printk("Unable to allocate memory\n"); + return -ENOMEM; + } + + veth_printk("Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev); + port = mFabricMgr->mPorts[vlanIndex] = (struct VethPort *)dev->priv; + memset(port, 0, sizeof(struct VethPort)); + rwlock_init(&(port->mMcastGate)); + mFabricMgr->mPorts[vlanIndex]->mDev = dev; + + dev->dev_addr[0] = 0x02; + dev->dev_addr[1] = 0x01; + dev->dev_addr[2] = 0xFF; + dev->dev_addr[3] = vlanIndex; + dev->dev_addr[4] = 0xFF; + dev->dev_addr[5] = HvLpConfig_getLpIndex_outline(); + dev->mtu = 9000; + + memcpy(&(port->mMyAddress), dev->dev_addr, 6); + + dev->open = &veth_open; + dev->hard_start_xmit = &veth_start_xmit; + dev->stop = &veth_close; + dev->get_stats = veth_get_stats; + dev->set_multicast_list = &veth_set_multicast_list; + dev->do_ioctl = &veth_ioctl; + + /* display version info if adapter is found */ + if (!displayVersion) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + displayVersion = 1; + veth_printk("%s", version); + } + + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + if (vlansFound > 0) + return 0; + else + return -ENODEV; +} + +#ifdef MODULE +MODULE_AUTHOR("Kyle Lucke , "); +MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); + +DECLARE_MUTEX_LOCKED(VethModuleBhDone); +int VethModuleReopen = 1; + +void veth_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + char name[10] = ""; + sprintf(name, "lp%d", i); + remove_proc_entry(name, veth_proc_root); + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + remove_proc_entry(name, veth_proc_root); + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + remove_proc_entry("veth", iSeries_proc); + + up(&VethProcSemaphore); +} + +void veth_waitForEnd(void) +{ + up(&VethModuleBhDone); +} + +void __exit veth_module_cleanup(void) +{ + int i; + struct VethFabricMgr *myFm = mFabricMgr; + struct tq_struct myBottomHalf; + struct net_device *thisOne = NULL; + + VethModuleReopen = 0; + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + veth_closeConnection(i, 1); + } + + myBottomHalf.routine = (void *)(void *)veth_waitForEnd; + + queue_task(&myBottomHalf, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + down(&VethModuleBhDone); + + HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); + + mb(); + mFabricMgr = NULL; + mb(); + + down(&VethProcSemaphore); + + iSeries_proc_callback(&veth_proc_delete); + + down(&VethProcSemaphore); + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + if (myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs > 0) + { + mf_deallocateLpEvents(myFm->mConnection[i].mRemoteLp, + HvLpEvent_Type_VirtualLan, + myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs, + NULL, + NULL); + } + + if (myFm->mConnection[i].mMsgs != NULL) + { + kfree(myFm->mConnection[i].mMsgs); + } + } + + for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) + { + if (myFm->mPorts[i] != NULL) + { + thisOne = myFm->mPorts[i]->mDev; + myFm->mPorts[i] = NULL; + + mb(); + + if (thisOne != NULL) + { + veth_printk("Unregistering %s (veth=%d)\n", thisOne->name, i); + unregister_netdev(thisOne); + } + } + } + + kfree(myFm); +} + +module_exit(veth_module_cleanup); +#endif + + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lpar%d", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)(long)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)(long)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + up(&VethProcSemaphore); +} + +int __init veth_module_init(void) +{ + int status; + int i; + + mFabricMgr = kmalloc(sizeof(struct VethFabricMgr), GFP_KERNEL); + memset(mFabricMgr, 0, sizeof(struct VethFabricMgr)); + veth_printk("Initializing veth module, fabric mgr (address=%p)\n", mFabricMgr); + + mFabricMgr->mEyecatcher = 0x56455448464D4752ULL; + mFabricMgr->mThisLp = HvLpConfig_getLpIndex_outline(); + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + mFabricMgr->mConnection[i].mEyecatcher = 0x564554484C50434EULL; + veth_failMe(mFabricMgr->mConnection+i); + spin_lock_init(&mFabricMgr->mConnection[i].mAckGate); + spin_lock_init(&mFabricMgr->mConnection[i].mStatusGate); + } + + status = veth_probe(); + + if (status == 0) + { + veth_openConnections(); + } + + iSeries_proc_callback(&veth_proc_init); + + return status; +} + +module_init(veth_module_init); + +static void veth_failMe(struct VethLpConnection *connection) +{ + connection->mConnectionStatus.mSentCap = 0; + connection->mConnectionStatus.mCapAcked = 0; + connection->mConnectionStatus.mGotCap = 0; + connection->mConnectionStatus.mGotCapAcked = 0; + connection->mConnectionStatus.mSentMonitor = 0; + connection->mConnectionStatus.mFailed = 1; +} + +static int veth_open(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + memset(&port->mStats, 0, sizeof(port->mStats)); + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + + return 0; +} + +static int veth_close(struct net_device *dev) +{ + netif_stop_queue(dev); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct net_device_stats *veth_get_stats(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + return(&port->mStats); +} + + +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned char *frame = skb->data; + HvLpIndex remoteLp = frame[5]; + int i = 0; + int clone = 0; + + if (mFabricMgr == NULL) + { + veth_error_printk("NULL fabric manager with active ports!\n"); + netif_stop_queue(dev); + return 1; + } + + mb(); + + if ((*frame & 0x01) != 0x01) /* broadcast or multicast */ + { + if ((remoteLp != mFabricMgr->mThisLp) && + (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, remoteLp))) + veth_pTransmit(skb, remoteLp, dev); + } + else + { + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != mFabricMgr->mThisLp) + { + if (clone) + skb = skb_clone(skb, GFP_ATOMIC); + else + clone = 1; + + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + /* the ack handles deleting the skb */ + veth_pTransmit(skb, i, dev); + } + } + } + } + + return 0; +} + +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev) +{ + struct VethLpConnection *connection = mFabricMgr->mConnection + remoteLp; + HvLpEvent_Rc returnCode; + + if (connection->mConnectionStatus.mFailed != 1) + { + struct VethMsg *msg = NULL; + VETHSTACKPOP(&(connection->mMsgStack), msg); + + if (msg != NULL) + { + if ((skb->len > 14) && + (skb->len <= 9018)) + { + dma_addr_t dma_addr = pci_map_single(NULL, + skb->data, + skb->len, + PCI_DMA_TODEVICE); + + if (dma_addr != -1) + { + msg->mSkb = skb; + msg->mEvent.mSendData.mAddress[0] = dma_addr; + msg->mEvent.mSendData.mLength[0] = skb->len; + msg->mEvent.mSendData.mEofMask = 0xFFFFFFFFUL; + + test_and_set_bit(0, &(msg->mInUse)); + + returnCode = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFrames, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + msg->mIndex, + msg->mEvent.mFpData.mData1, + msg->mEvent.mFpData.mData2, + msg->mEvent.mFpData.mData3, + msg->mEvent.mFpData.mData4, + msg->mEvent.mFpData.mData5); + } + else + { + returnCode = -1; /* Bad return code */ + } + + if (returnCode != HvLpEvent_Rc_Good) + { + struct VethPort *port = (struct VethPort *)dev->priv; + + if (msg->mEvent.mSendData.mAddress[0]) + { + pci_unmap_single(NULL, dma_addr, skb->len, PCI_DMA_TODEVICE); + } + + dev_kfree_skb_irq(skb); + + msg->mSkb = NULL; + memset(&(msg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&(connection->mMsgStack), msg); + port->mStats.tx_dropped++; + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_packets++; + port->mStats.tx_bytes += skb->len; + } + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } +} + +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + + return -EOPNOTSUPP; +} + +static void veth_set_multicast_list(struct net_device *dev) +{ + char *addrs; + struct VethPort *port = (struct VethPort *)dev->priv; + u64 newAddress = 0; + unsigned long flags; + + write_lock_irqsave(&port->mMcastGate, flags); + + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + port->mPromiscuous = 1; + } else { + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) { + port->mAllMcast = 1; + } else { + int i; + /* Update table */ + port->mNumAddrs = 0; + + for (i = 0; ((i < dev->mc_count) && (i < 12)); i++) { /* for each address in the list */ + addrs = dmi->dmi_addr; + dmi = dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + memcpy(&newAddress, addrs, 6); + newAddress &= 0xFFFFFFFFFFFF0000; + + port->mMcasts[port->mNumAddrs] = newAddress; + mb(); + port->mNumAddrs = port->mNumAddrs + 1; + } + } + } + } + + write_unlock_irqrestore(&port->mMcastGate, flags); +} + + +static void veth_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + veth_handleAck(event); + } + else if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + veth_handleInt(event); + } +} + +static void veth_handleAck(struct HvLpEvent *event) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xTargetLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCapAck(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + veth_takeMonitorAck(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown ack type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_handleInt(struct HvLpEvent *event) +{ + int i=0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xSourceLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCap(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + /* do nothing... this'll hang out here til we're dead, and the hypervisor will return it for us. */ + break; + } + case VethEventTypeFramesAck: + { + for (i=0; i < VethMaxFramesMsgsAcked; ++i) + { + u16 msg = vethEvent->mDerivedData.mFramesAckData.mToken[i]; + veth_recycleMsg(connection, msg); + } + break; + } + case VethEventTypeFrames: + { + veth_takeFrames(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown interrupt type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_openConnections() +{ + int i=0; + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handleEvent); + + /* Now I need to run through the active lps and open connections to the ones I'm supposed to + open to. */ + + for (i=HvMaxArchitectedLps-1; i >=0; --i) + { + if (i != mFabricMgr->mThisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + veth_openConnection(i, 1); + } + else + { + veth_closeConnection(i, 1); + } + } + } +} + +static void veth_intFinishOpeningConnections(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberAllocated = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishOpeningConnections(void *parm) +{ + unsigned long flags; + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishOpeningConnectionsLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *connection) +{ + if (connection->mNumberAllocated >= 2) + { + connection->mConnectionStatus.mCapMonAlloced = 1; + veth_sendCap(connection); + } + else + { + veth_error_printk("Couldn't allocate base msgs for lpar %d, only got %d\n", connection->mRemoteLp, connection->mNumberAllocated); + veth_failMe(connection); + } +} + +static void veth_openConnection(u8 remoteLp, int lockMe) +{ + unsigned long flags; + unsigned long flags2; + HvLpInstanceId source; + HvLpInstanceId target; + u64 i = 0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + + memset(&connection->mCapBhTq, 0, sizeof(connection->mCapBhTq)); + connection->mCapBhTq.routine = (void *)(void *)veth_capBh; + + memset(&connection->mCapAckBhTq, 0, sizeof(connection->mCapAckBhTq)); + connection->mCapAckBhTq.routine = (void *)(void *)veth_capAckBh; + + memset(&connection->mMonitorAckBhTq, 0, sizeof(connection->mMonitorAckBhTq)); + connection->mMonitorAckBhTq.routine = (void *)(void *)veth_monitorAckBh; + + memset(&connection->mAllocBhTq, 0, sizeof(connection->mAllocBhTq)); + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + connection->mRemoteLp = remoteLp; + + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + + /* clean up non-acked msgs */ + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + + connection->mConnectionStatus.mOpen = 1; + + source = connection->mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + target = connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + + if (connection->mConnectionStatus.mCapMonAlloced != 1) + { + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + mf_allocateLpEvents(remoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + 2, + &veth_intFinishOpeningConnections, + connection); + } + else + { + veth_finishOpeningConnectionsLocked(connection); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_closeConnection(u8 remoteLp, int lockMe) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + unsigned long flags; + unsigned long flags2; + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (connection->mConnectionStatus.mOpen == 1) + { + HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + connection->mConnectionStatus.mOpen = 0; + veth_failMe(connection); + + /* reset ack data */ + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + } + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_msgsInit(struct VethLpConnection *connection) +{ + connection->mAllocBhTq.routine = (void *)(void *)veth_finishMsgsInit; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + &veth_intFinishMsgsInit, + connection); +} + +static void veth_intFinishMsgsInit(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberRcvMsgs = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_intFinishCapBh(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + if (number > 0) + connection->mNumberLpAcksAlloced += number; + + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishMsgsInit(struct VethLpConnection *connection) +{ + int i=0; + unsigned int numberGotten = 0; + u64 amountOfHeapToGet = connection->mMyCap.mUnionData.mFields.mNumberBuffers * sizeof(struct VethMsg); + char *msgs = NULL; + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (connection->mNumberRcvMsgs >= connection->mMyCap.mUnionData.mFields.mNumberBuffers) + { + msgs = kmalloc(amountOfHeapToGet, GFP_ATOMIC); + + connection->mMsgs = (struct VethMsg *)msgs; + + if (msgs != NULL) + { + memset(msgs, 0, amountOfHeapToGet); + + for (i=0; i < connection->mMyCap.mUnionData.mFields.mNumberBuffers; ++i) + { + connection->mMsgs[i].mIndex = i; + ++numberGotten; + VETHSTACKPUSH(&(connection->mMsgStack), (connection->mMsgs+i)); + } + if (numberGotten > 0) + { + connection->mNumMsgs = numberGotten; + } + } + else + { + kfree(msgs); + connection->mMsgs = NULL; + } + } + + connection->mMyCap.mUnionData.mFields.mNumberBuffers = connection->mNumMsgs; + + if (connection->mNumMsgs < 10) + connection->mMyCap.mUnionData.mFields.mThreshold = 1; + else if (connection->mNumMsgs < 20) + connection->mMyCap.mUnionData.mFields.mThreshold = 4; + else if (connection->mNumMsgs < 40) + connection->mMyCap.mUnionData.mFields.mThreshold = 10; + else + connection->mMyCap.mUnionData.mFields.mThreshold = 20; + + connection->mMyCap.mUnionData.mFields.mTimer = VethAckTimeoutUsec; + + veth_finishSendCap(connection); + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_sendCap(struct VethLpConnection *connection) +{ + if (connection->mMsgs == NULL) + { + connection->mMyCap.mUnionData.mFields.mNumberBuffers = VethBuffersToAllocate; + veth_msgsInit(connection); + } + else + { + veth_finishSendCap(connection); + } +} + +static void veth_finishSendCap(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeCap, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mMyCap.mUnionData.mNoFields.mReserved1, + connection->mMyCap.mUnionData.mNoFields.mReserved2, + connection->mMyCap.mUnionData.mNoFields.mReserved3, + connection->mMyCap.mUnionData.mNoFields.mReserved4, + connection->mMyCap.mUnionData.mNoFields.mReserved5); + + if ((returnCode == HvLpEvent_Rc_PartitionDead) || + (returnCode == HvLpEvent_Rc_PathClosed)) + { + connection->mConnectionStatus.mSentCap = 0; + } + else if (returnCode != HvLpEvent_Rc_Good) + { + veth_error_printk("Couldn't send cap to lpar %d, rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } + else + { + connection->mConnectionStatus.mSentCap = 1; + } +} + +static void veth_takeCap(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapBhPending))) + { + connection->mCapBhTq.data = connection; + memcpy(&connection->mCapEvent, event, sizeof(connection->mCapEvent)); + queue_task(&connection->mCapBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities from lpar %d while already processing one\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +static void veth_takeCapAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapAckBhPending))) + { + connection->mCapAckBhTq.data = connection; + memcpy(&connection->mCapAckEvent, event, sizeof(connection->mCapAckEvent)); + queue_task(&connection->mCapAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_takeMonitorAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mMonitorAckBhPending))) + { + connection->mMonitorAckBhTq.data = connection; + memcpy(&connection->mMonitorAckEvent, event, sizeof(connection->mMonitorAckEvent)); + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a monitor ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_recycleMsg(struct VethLpConnection *connection, u16 msg) +{ + if (msg < connection->mNumMsgs) + { + struct VethMsg *myMsg = connection->mMsgs + msg; + if (test_and_clear_bit(0, &(myMsg->mInUse))) + { + pci_unmap_single(NULL, + myMsg->mEvent.mSendData.mAddress[0], + myMsg->mEvent.mSendData.mLength[0], + PCI_DMA_TODEVICE); + dev_kfree_skb_irq(myMsg->mSkb); + + myMsg->mSkb = NULL; + memset(&(myMsg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&connection->mMsgStack, myMsg); + } + else + { + if (connection->mConnectionStatus.mOpen) + { + veth_error_printk("Received a frames ack for msg %d from lpar %d while not outstanding\n", msg, connection->mRemoteLp); + } + } + } +} + +static void veth_capBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + unsigned long flags; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + u64 numAcks = 0; + spin_lock_irqsave(&connection->mStatusGate, flags); + connection->mConnectionStatus.mGotCap = 1; + + memcpy(remoteCap, &(event->mDerivedData.mCapabilitiesData), sizeof(connection->mRemoteCap)); + + if ((remoteCap->mUnionData.mFields.mNumberBuffers <= VethMaxFramesMsgs) && + (remoteCap->mUnionData.mFields.mNumberBuffers != 0) && + (remoteCap->mUnionData.mFields.mThreshold <= VethMaxFramesMsgsAcked) && + (remoteCap->mUnionData.mFields.mThreshold != 0)) + { + numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + if (connection->mNumberLpAcksAlloced < numAcks) + { + numAcks = numAcks - connection->mNumberLpAcksAlloced; + connection->mAllocBhTq.routine = (void *)(void *)veth_finishCapBh; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + numAcks, + &veth_intFinishCapBh, + connection); + } + else + veth_finishCapBhLocked(connection); + } + else + { + veth_error_printk("Received incompatible capabilities from lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_InvalidSubtypeData; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } + + clear_bit(0,&(connection->mCapBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_capAckBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapAckEvent; + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (event->mBaseEvent.xRc == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mCapAcked = 1; + + if ((connection->mConnectionStatus.mGotCap == 1) && + (connection->mConnectionStatus.mGotCapAcked == 1)) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Bad rc(%d) from lpar %d on capabilities\n", event->mBaseEvent.xRc, connection->mRemoteLp); + veth_failMe(connection); + } + + clear_bit(0,&(connection->mCapAckBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_monitorAckBh(struct VethLpConnection *connection) +{ + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + veth_failMe(connection); + + veth_printk("Monitor ack returned for lpar %d\n", connection->mRemoteLp); + del_timer(&connection->mAckTimer); + + if (connection->mConnectionStatus.mOpen) + { + veth_closeConnection(connection->mRemoteLp, 0); + + udelay(100); + + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { +#ifdef MODULE + if (VethModuleReopen) +#endif + veth_openConnection(connection->mRemoteLp, 0); +#ifdef MODULE + else + { + int i=0; + + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + } +#endif + clear_bit(0,&(connection->mMonitorAckBhPending)); + } + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_takeFrames(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + int i; + struct VethPort *port = NULL; + + for (i=0; i < VethMaxFramesPerMsg; ++i) + { + u16 length = event->mDerivedData.mSendData.mLength[i]; + u64 address = event->mDerivedData.mSendData.mAddress[i]; + if ((address != 0) && + (length <= 9018) && + (length > 14)) + { + struct sk_buff *skb = alloc_skb(event->mDerivedData.mSendData.mLength[i], GFP_ATOMIC); + + if (skb != NULL) + { + dma_addr_t toAddress = -1; + HvLpDma_Rc returnCode = HvLpDma_Rc_Good; + toAddress = pci_map_single(NULL, skb->data, + length, + PCI_DMA_FROMDEVICE); + + if (toAddress != -1) + { + returnCode = HvCallEvent_dmaSingle(HvLpEvent_Type_VirtualLan, + event->mBaseEvent.xSourceLp, + HvLpDma_Direction_RemoteToLocal, + connection->mSourceInst, + connection->mTargetInst, + HvLpDma_AddressType_TceIndex, + HvLpDma_AddressType_TceIndex, + toAddress, + address, + length); + + if (returnCode == HvLpDma_Rc_Good) + { + HvLpVirtualLanIndex vlan = skb->data[9]; + u64 dest = *((u64 *)skb->data) & 0xFFFFFFFFFFFF0000; + port = mFabricMgr->mPorts[vlan]; + + if (((vlan < HvMaxArchitectedVirtualLans) && + (port != NULL)) && + ((dest == port->mMyAddress) || /* it's for me */ + (dest == 0xFFFFFFFFFFFF0000) || /* it's a broadcast */ + (veth_multicast_wanted(port, dest)) || /* it's one of my multicasts */ + (port->mPromiscuous == 1))) /* I'm promiscuous */ + { + skb_put(skb, length); + skb->dev = mFabricMgr->mPorts[vlan]->mDev; + skb->protocol = eth_type_trans(skb, mFabricMgr->mPorts[vlan]->mDev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); /* send it up */ + port->mStats.rx_packets++; + port->mStats.rx_bytes += length; + + } + else + { + dev_kfree_skb_irq(skb); + } + } + else + { + dev_kfree_skb_irq(skb); + } + + pci_unmap_single(NULL, + toAddress, + length, + PCI_DMA_FROMDEVICE); + } + else + { + dev_kfree_skb_irq(skb); + } + } + } + } + /* Ack it */ + + { + unsigned long flags; + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks < VethMaxFramesMsgsAcked) + { + connection->mEventData.mAckData.mToken[connection->mNumAcks] = event->mBaseEvent.xCorrelationToken; + ++connection->mNumAcks; + + if (connection->mNumAcks == connection->mRemoteCap.mUnionData.mFields.mThreshold) + { + HvLpEvent_Rc rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d\n", rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + } +} + +static void veth_timedAck(unsigned long connectionPtr) +{ + unsigned long flags; + HvLpEvent_Rc rc; + struct VethLpConnection *connection = (struct VethLpConnection *) connectionPtr; + /* Ack all the events */ + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks > 0) + { + rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d!\n", rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + + /* Reschedule the timer */ + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); +} + +static int veth_multicast_wanted(struct VethPort *port, u64 thatAddr) +{ + int returnParm = 0; + int i; + unsigned long flags; + + if ((*((char *)&thatAddr) & 0x01) != 1) + return 0; + + read_lock_irqsave(&port->mMcastGate, flags); + if (port->mAllMcast) + return 1; + + for (i=0; i < port->mNumAddrs; ++i) + { + u64 thisAddr = port->mMcasts[i]; + + if (thisAddr == thatAddr) + { + returnParm = 1; + break; + } + } + read_unlock_irqrestore(&port->mMcastGate, flags); + + return returnParm; +} + +static void veth_sendMonitor(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeMonitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + connection->mSourceInst, + connection->mTargetInst, + 0, 0, 0, 0, 0, 0); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mSentMonitor = 1; + connection->mConnectionStatus.mFailed = 0; + + /* Start the ACK timer */ + init_timer(&connection->mAckTimer); + connection->mAckTimer.function = veth_timedAck; + connection->mAckTimer.data = (unsigned long) connection; + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); + + } + else + { + veth_error_printk("Monitor send to lpar %d failed with rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } +} + +static void veth_finishCapBh(struct VethLpConnection *connection) +{ + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishCapBhLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishCapBhLocked(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + int numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + /* Convert timer to jiffies */ + if (connection->mMyCap.mUnionData.mFields.mTimer) + connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000; + else + connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000; + + if (connection->mNumberLpAcksAlloced >= numAcks) + { + HvLpEvent_Rc returnCode = HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mGotCapAcked = 1; + + if (connection->mConnectionStatus.mSentCap != 1) + { + connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(connection->mRemoteLp, HvLpEvent_Type_VirtualLan); + + veth_sendCap(connection); + } + else if (connection->mConnectionStatus.mCapAcked == 1) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Failed to ack remote cap for lpar %d with rc %Lx\n", connection->mRemoteLp, returnCode); + veth_failMe(connection); + } + } + else + { + veth_error_printk("Couldn't allocate all the frames ack events for lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +int proc_veth_dump_connection +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int whichConnection = (int) (long)data; + int len = 0; + struct VethLpConnection *connection = NULL; + + if ((whichConnection < 0) || (whichConnection > HvMaxArchitectedLps) || (mFabricMgr == NULL)) + { + veth_error_printk("Got bad data from /proc file system\n"); + len = sprintf(page, "ERROR\n"); + } + else + { + int thereWasStuffBefore = 0; + connection = &(mFabricMgr->mConnection[whichConnection]); + + out += sprintf(out, "Remote Lp:\t%d\n", connection->mRemoteLp); + out += sprintf(out, "Source Inst:\t%04X\n", connection->mSourceInst); + out += sprintf(out, "Target Inst:\t%04X\n", connection->mTargetInst); + out += sprintf(out, "Num Msgs:\t%d\n", connection->mNumMsgs); + out += sprintf(out, "Num Lp Acks:\t%d\n", connection->mNumberLpAcksAlloced); + out += sprintf(out, "Num Acks:\t%d\n", connection->mNumAcks); + + if (connection->mConnectionStatus.mOpen) + { + out += sprintf(out, "mConnectionStatus.mCapMonAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapMonAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mBaseMsgsAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "BaseMsgsAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentMonitor) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentMonitor"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mPopulatedRings) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "PopulatedRings"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mFailed) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "Failed"); + thereWasStuffBefore = 1; + } + + if (thereWasStuffBefore) + out += sprintf(out, ">"); + + out += sprintf(out, "\n"); + + out += sprintf(out, "Capabilities (System:):\n"); + out += sprintf(out, "\tLocal:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mMyCap.mUnionData.mFields.mVersion, + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + connection->mMyCap.mUnionData.mFields.mThreshold, + connection->mMyCap.mUnionData.mFields.mTimer); + out += sprintf(out, "\tRemote:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mRemoteCap.mUnionData.mFields.mVersion, + connection->mRemoteCap.mUnionData.mFields.mNumberBuffers, + connection->mRemoteCap.mUnionData.mFields.mThreshold, + connection->mRemoteCap.mUnionData.mFields.mTimer); + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_veth_dump_port +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int whichPort = (int) (long)data; + int len = 0; + struct VethPort *port = NULL; + + if ((whichPort < 0) || (whichPort > HvMaxArchitectedVirtualLans) || (mFabricMgr == NULL)) + len = sprintf(page, "Virtual ethernet is not configured.\n"); + else + { + int i=0; + u32 *myAddr; + u16 *myEndAddr; + port = mFabricMgr->mPorts[whichPort]; + + if (port != NULL) + { + myAddr = (u32 *)&(port->mMyAddress); + myEndAddr = (u16 *)(myAddr + 1); + out += sprintf(out, "Net device:\t%p\n", port->mDev); + out += sprintf(out, "Address:\t%08X%04X\n", myAddr[0], myEndAddr[0]); + out += sprintf(out, "Promiscuous:\t%d\n", port->mPromiscuous); + out += sprintf(out, "All multicast:\t%d\n", port->mAllMcast); + out += sprintf(out, "Number multicast:\t%d\n", port->mNumAddrs); + + for (i=0; i < port->mNumAddrs; ++i) + { + u32 *multi = (u32 *)&(port->mMcasts[i]); + u16 *multiEnd = (u16 *)(multi + 1); + out += sprintf(out, " %08X%04X\n", multi[0], multiEnd[0]); + } + } + else + { + out += sprintf(page, "veth%d is not configured.\n", whichPort); + } + + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + + diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/net/veth.h linuxppc64_2_4/drivers/net/veth.h --- ../w3.wed/linux-2.4.8-ac5/drivers/net/veth.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth.h Fri May 4 17:12:47 2001 @@ -0,0 +1,255 @@ +/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_H +#define _VETH_H + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#include + +#define VethEventNumTypes (4) +#define VethEventTypeCap (0) +#define VethEventTypeFrames (1) +#define VethEventTypeMonitor (2) +#define VethEventTypeFramesAck (3) + +#define VethMaxFramesMsgsAcked (20) +#define VethMaxFramesMsgs (0xFFFF) +#define VethMaxFramesPerMsg (6) +#define VethAckTimeoutUsec (1000000) + +#define VETHSTACKTYPE(T) struct VethStack##T +#define VETHSTACK(T) \ +VETHSTACKTYPE(T) \ +{ \ +struct T *head; \ +spinlock_t lock; \ +} +#define VETHSTACKCTOR(s) do { (s)->head = NULL; spin_lock_init(&(s)->lock); } while(0) +#define VETHSTACKPUSH(s, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p)->next = (s)->head; \ +(s)->head = (p); \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHSTACKPOP(s,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p) = (s)->head; \ +if ((s)->head != NULL) \ +{ \ +(s)->head = (s)->head->next; \ +} \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHQUEUE(T) \ +struct VethQueue##T \ +{ \ +T *head; \ +T *tail; \ +spinlock_t lock; \ +} +#define VETHQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; spin_lock_init(&(q)->lock); } while(0) +#define VETHQUEUEENQ(q, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +#define VETHQUEUEDEQ(q,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +struct VethFramesData +{ + u32 mAddress[6]; + u16 mLength[6]; + u32 mEofMask; +}; + +struct VethFramesAckData +{ + u16 mToken[VethMaxFramesMsgsAcked]; +}; + +struct VethCapData +{ + union + { + struct Fields + { + u8 mVersion; + u8 mReserved1; + u16 mNumberBuffers; + u16 mThreshold; + u16 mReserved2; + u32 mTimer; + u32 mReserved3; + u64 mReserved4; + u64 mReserved5; + u64 mReserved6; + } mFields; + struct NoFields + { + u64 mReserved1; + u64 mReserved2; + u64 mReserved3; + u64 mReserved4; + u64 mReserved5; + } mNoFields; + } mUnionData; +}; + +struct VethFastPathData +{ + u64 mData1; + u64 mData2; + u64 mData3; + u64 mData4; + u64 mData5; +}; + +struct VethLpEvent +{ + struct HvLpEvent mBaseEvent; + union { + struct VethFramesData mSendData; + struct VethCapData mCapabilitiesData; + struct VethFramesAckData mFramesAckData; + struct VethFastPathData mFastPathData; + } mDerivedData; + +}; + +struct VethMsg +{ + struct VethMsg *next; + union { + struct VethFramesData mSendData; + struct VethFastPathData mFpData; + } mEvent; + int mIndex; + unsigned long mInUse; + struct sk_buff *mSkb; +}; + + +struct VethControlBlock +{ + struct net_device *mDev; + struct VethControlBlock *mNext; + HvLpVirtualLanIndex mVlanId; +}; + +struct VethLpConnection +{ + u64 mEyecatcher; + HvLpIndex mRemoteLp; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + u32 mNumMsgs; + struct VethMsg *mMsgs; + int mNumberRcvMsgs; + int mNumberLpAcksAlloced; + union + { + struct VethFramesAckData mAckData; + struct VethFastPathData mFpData; + } mEventData; + spinlock_t mAckGate; + u32 mNumAcks; + spinlock_t mStatusGate; + struct + { + u64 mOpen : 1; + u64 mCapMonAlloced : 1; + u64 mBaseMsgsAlloced : 1; + u64 mSentCap : 1; + u64 mCapAcked : 1; + u64 mGotCap : 1; + u64 mGotCapAcked : 1; + u64 mSentMonitor : 1; + u64 mPopulatedRings : 1; + u64 mReserved : 54; + u64 mFailed : 1; + } mConnectionStatus; + struct VethCapData mMyCap; + struct VethCapData mRemoteCap; + unsigned long mCapAckBhPending; + struct tq_struct mCapAckBhTq; + struct VethLpEvent mCapAckEvent; + unsigned long mCapBhPending; + struct tq_struct mCapBhTq; + struct VethLpEvent mCapEvent; + unsigned long mMonitorAckBhPending; + struct tq_struct mMonitorAckBhTq; + struct VethLpEvent mMonitorAckEvent; + unsigned long mAllocBhPending; + struct tq_struct mAllocBhTq; + int mNumberAllocated; + struct timer_list mAckTimer; + u32 mTimeout; + VETHSTACK(VethMsg) mMsgStack; +}; +#define HVMAXARCHITECTEDVIRTUALLANS 16 +struct VethPort +{ + struct net_device *mDev; + struct net_device_stats mStats; + int mLock; + u64 mMyAddress; + int mPromiscuous; + int mAllMcast; + rwlock_t mMcastGate; + int mNumAddrs; + u64 mMcasts[12]; +}; + +struct VethFabricMgr +{ + u64 mEyecatcher; + HvLpIndex mThisLp; + struct VethLpConnection mConnection[HVMAXARCHITECTEDLPS]; + spinlock_t mPortListGate; + u64 mNumPorts; + struct VethPort *mPorts[HVMAXARCHITECTEDVIRTUALLANS]; +}; + +int proc_veth_dump_connection(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_veth_dump_port(char *page, char **start, off_t off, int count, int *eof, void *data); + +#endif /* _VETH_H */ diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/pci/pci.c linuxppc64_2_4/drivers/pci/pci.c --- ../w3.wed/linux-2.4.8-ac5/drivers/pci/pci.c Wed Aug 15 12:11:33 2001 +++ linuxppc64_2_4/drivers/pci/pci.c Thu Aug 16 08:50:27 2001 @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $ + * * * PCI Bus Services, see include/linux/pci.h for further explanation. * @@ -874,6 +874,14 @@ u32 l, sz; struct resource *res; + /************************************************************ + * Check for architecture dependant call to read the BARs. + ************************************************************/ + if( dev->bus->ops->pci_read_bases != NULL) { + dev->bus->ops->pci_read_bases(dev, howmany, rom); + return; + } + for(pos=0; posresource[pos]; @@ -1155,6 +1163,14 @@ static void pci_read_irq(struct pci_dev *dev) { unsigned char irq; + + /************************************************************ + * Check for architecture dependant call to read and set irg + ************************************************************/ + if(dev->bus->ops->pci_read_irq != NULL) { + dev->bus->ops->pci_read_irq(dev); + return; + } pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); if (irq) @@ -1176,7 +1192,17 @@ { u32 class; - sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + /* sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); */ + + + /******************************************************************** + * Prefix the bus number with the phb number for large(>256 bus) systems. + ********************************************************************/ + sprintf(dev->slot_name, "%4x%02x:%02x.%1x", + ( (dev->bus->number>>8) &0xFF0000), + ( dev->bus->number&0x0000FF), + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); @@ -1223,6 +1249,14 @@ printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n", dev->slot_name, class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; + } + + + /********************************************************************* + * Give the architure code a chance to fix up/tweak the devices. + *********************************************************************/ + if(dev->bus->ops->pci_fixup_registers != NULL) { + dev->bus->ops->pci_fixup_registers(dev); } /* We found a fine healthy device, go go go... */ diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/scsi/osst.c linuxppc64_2_4/drivers/scsi/osst.c --- ../w3.wed/linux-2.4.8-ac5/drivers/scsi/osst.c Thu Jul 19 23:18:15 2001 +++ linuxppc64_2_4/drivers/scsi/osst.c Fri Aug 10 13:09:19 2001 @@ -31,6 +31,7 @@ #define OSST_FW_NEED_POLL_MAX 10704 /*(108D)*/ #define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7) +#include #include #include diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/scsi/scsi_scan.c linuxppc64_2_4/drivers/scsi/scsi_scan.c --- ../w3.wed/linux-2.4.8-ac5/drivers/scsi/scsi_scan.c Thu Jul 5 13:28:17 2001 +++ linuxppc64_2_4/drivers/scsi/scsi_scan.c Tue Aug 14 14:55:53 2001 @@ -301,8 +301,9 @@ scsi_initialize_queue(SDpnt, shpnt); SDpnt->request_queue.queuedata = (void *) SDpnt; /* Make sure we have something that is valid for DMA purposes */ - scsi_result = ((!shpnt->unchecked_isa_dma) - ? &scsi_result0[0] : kmalloc(512, GFP_DMA)); + + scsi_result = kmalloc(512, (shpnt->unchecked_isa_dma)?GFP_KERNEL:GFP_DMA); + } if (scsi_result == NULL) { diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/scsi/sr_ioctl.c linuxppc64_2_4/drivers/scsi/sr_ioctl.c --- ../w3.wed/linux-2.4.8-ac5/drivers/scsi/sr_ioctl.c Thu Jul 5 13:28:17 2001 +++ linuxppc64_2_4/drivers/scsi/sr_ioctl.c Wed Jun 27 13:43:42 2001 @@ -107,7 +107,6 @@ if (!scsi_block_when_processing_errors(SDev)) return -ENODEV; - scsi_wait_req(SRpnt, (void *) sr_cmd, (void *) buffer, buflength, IOCTL_TIMEOUT, IOCTL_RETRIES); @@ -334,7 +333,12 @@ { u_char sr_cmd[10]; int result, target = MINOR(cdi->dev); - unsigned char buffer[32]; + unsigned char *buffer = scsi_malloc(512); + + if (buffer == NULL) { + printk("SCSI DMA pool exhausted."); + return -ENOMEM; + } memset(sr_cmd, 0, sizeof(sr_cmd)); @@ -407,6 +411,7 @@ return -EINVAL; } + scsi_free(buffer, 512); #if 0 if (result) printk("DEBUG: sr_audio: result for ioctl %x: %x\n", cmd, result); diff -uNr --exclude=CVS ../w3.wed/linux-2.4.8-ac5/drivers/video/offb.c linuxppc64_2_4/drivers/video/offb.c --- ../w3.wed/linux-2.4.8-ac5/drivers/video/offb.c Wed Aug 15 12:11:34 2001 +++ linuxppc64_2_4/drivers/video/offb.c Wed Aug 1 15:55:58 2001 @@ -32,7 +32,9 @@ #endif #include #include +#ifndef CONFIG_PPC64 #include +#endif #include