From c7dded697a3ab90c4353748811d0960eef170063 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:19:00 -0500 Subject: [PATCH] Import 2.2.10pre1 --- CREDITS | 9 +- Documentation/Configure.help | 3 + Documentation/sound/CMI8330 | 54 +- Documentation/svga.txt | 10 +- Makefile | 2 +- arch/alpha/config.in | 7 +- arch/alpha/kernel/alpha_ksyms.c | 5 +- arch/alpha/kernel/core_cia.c | 6 +- arch/alpha/kernel/fpreg.c | 6 +- arch/alpha/kernel/head.S | 36 +- arch/alpha/kernel/irq.c | 10 +- arch/alpha/kernel/process.c | 45 +- arch/alpha/kernel/proto.h | 4 +- arch/alpha/kernel/setup.c | 10 +- arch/alpha/kernel/signal.c | 6 + arch/alpha/kernel/smp.c | 1239 +++++++++++++------------ arch/alpha/kernel/sys_dp264.c | 162 +++- arch/alpha/kernel/time.c | 93 +- arch/alpha/kernel/traps.c | 21 +- arch/alpha/mm/init.c | 20 - arch/i386/boot/video.S | 13 +- arch/i386/kernel/mtrr.c | 141 ++- arch/i386/kernel/setup.c | 5 + arch/sparc/kernel/sys_sunos.c | 4 +- arch/sparc64/kernel/sys_sparc32.c | 104 ++- arch/sparc64/kernel/sys_sunos32.c | 4 +- arch/sparc64/solaris/fs.c | 26 +- drivers/block/ide.c | 16 +- drivers/char/cyclades.c | 357 ++++--- drivers/misc/parport_pc.c | 5 +- drivers/net/smc-ultra.c | 2 +- drivers/scsi/README.st | 11 +- drivers/scsi/scsi.c | 11 + drivers/scsi/sr_ioctl.c | 4 +- drivers/scsi/st.c | 6 +- drivers/scsi/st.h | 1 - drivers/scsi/sym53c8xx.c | 2 + drivers/sound/es1370.c | 53 +- drivers/video/fbcon.c | 1 + drivers/video/mdacon.c | 2 +- fs/adfs/dir.c | 3 - fs/adfs/namei.c | 6 - fs/autofs/dir.c | 2 - fs/autofs/root.c | 3 - fs/block_dev.c | 2 + fs/ext2/namei.c | 3 +- fs/ext2/truncate.c | 3 +- fs/hfs/dir_cap.c | 4 - fs/hfs/dir_dbl.c | 4 - fs/hfs/dir_nat.c | 4 - fs/minix/namei.c | 63 +- fs/ncpfs/dir.c | 19 +- fs/ncpfs/file.c | 33 +- fs/ncpfs/inode.c | 5 +- fs/ncpfs/ioctl.c | 42 +- fs/ncpfs/mmap.c | 39 +- fs/ncpfs/ncplib_kernel.c | 87 +- fs/ncpfs/ncplib_kernel.h | 16 +- fs/ncpfs/sock.c | 32 +- fs/select.c | 6 +- fs/smbfs/inode.c | 44 +- fs/super.c | 23 +- fs/sysv/namei.c | 73 +- include/asm-alpha/atomic.h | 2 + include/asm-alpha/bitops.h | 13 +- include/asm-alpha/init.h | 2 +- include/asm-alpha/irq.h | 4 + include/asm-alpha/mmu_context.h | 3 +- include/asm-alpha/smp.h | 15 +- include/asm-alpha/spinlock.h | 22 +- include/asm-alpha/system.h | 16 + include/asm-arm/arch-ebsa285/irq.h | 1 + include/asm-arm/arch-ebsa285/memory.h | 2 + include/linux/cyclades.h | 3 +- include/linux/ncp.h | 3 - include/linux/ncp_fs.h | 6 +- include/linux/ncp_fs_sb.h | 3 +- include/linux/netdevice.h | 2 +- net/core/filter.c | 2 +- net/ipv4/tcp.c | 1 + net/ipv4/tcp_input.c | 100 +- net/ipv4/tcp_output.c | 7 +- net/ipv4/tcp_timer.c | 6 +- net/ipv6/tcp_ipv6.c | 2 +- net/netlink/af_netlink.c | 2 +- 85 files changed, 1910 insertions(+), 1339 deletions(-) diff --git a/CREDITS b/CREDITS index 52021ec29bcc..8995afa42d00 100644 --- a/CREDITS +++ b/CREDITS @@ -656,7 +656,7 @@ N: Richard E. Gooch E: rgooch@atnf.csiro.au D: parent process death signal to children D: prctl() syscall -D: /proc/mtrr support to manipulate MTRRs on Pentium Pro's +D: /proc/mtrr support to manipulate MTRRs on Intel P6 family S: CSIRO Australia Telescope National Facility S: P.O. Box 76, Epping S: New South Wales, 2121 @@ -754,6 +754,13 @@ S: 77 Clarence Mews S: London SE16 1GD S: United Kingdom +N: Bart Hartgers +E: bart@etpmod.phys.tue.nl +D: MTRR emulation with Centaur MCRs +S: Gen Stedmanstraat 212 +S: 5623 HZ Eindhoven +S: The Netherlands + N: Kai Harrekilde-Petersen E: khp@dolphinics.no D: Original author of the ftape-HOWTO, i82078 fdc detection code. diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 3e09463a601e..da76a5930f3b 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -8684,6 +8684,9 @@ CONFIG_MTRR The AMD K6-2 (stepping 8 and above) and K6-3 processors have two MTRRs. These are supported. + + The Centaur C6 (WinChip) has 8 MCRs, allowing write-combining. These + are supported. Saying Y here also fixes a problem with buggy SMP BIOSes which only set the MTRRs for the boot CPU and not the secondary CPUs. This can diff --git a/Documentation/sound/CMI8330 b/Documentation/sound/CMI8330 index 43b1c5a72b5d..fca15f6069e6 100644 --- a/Documentation/sound/CMI8330 +++ b/Documentation/sound/CMI8330 @@ -1,46 +1,48 @@ -How to enable CMI 8330 soundchip on Linux +How to enable CMI 8330 (SOUNDPRO) soundchip on Linux ------------------------------------------ Stefan Laudat -Hello folks, - - The CMI8330 soundchip is a very small chip found on many recent - motherboards. In order to use it you just have to use a proper - isapnp.conf and a little bit of patience. +[Note: The CMI 8338 is unrelated and right now unsupported] + - Of course you will have to compile kernel sound support as module, - as shown below: + In order to use CMI8330 under Linux you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience. I use isapnp 1.17, but +you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/. + + Of course you will have to compile kernel sound support as module, as shown below: CONFIG_SOUND=m CONFIG_SOUND_OSS=m CONFIG_SOUND_SB=m CONFIG_SOUND_ADLIB=m CONFIG_SOUND_MPU401=m -# Just for fun :) +# Mikro$chaft sound system (kinda useful here ;)) CONFIG_SOUND_MSS=m The /etc/isapnp.conf file will be: + (READPORT 0x0203) (ISOLATE PRESERVE) (IDENTIFY *) (VERBOSITY 2) (CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING (VERIFYLD N) + + # WSS (CONFIGURE CMI0001/16777472 (LD 0 (IO 0 (SIZE 8) (BASE 0x0530)) (IO 1 (SIZE 8) (BASE 0x0388)) -(INT 0 (IRQ 5 (MODE +E))) +(INT 0 (IRQ 7 (MODE +E))) (DMA 0 (CHANNEL 0)) (NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}") (ACT Y) )) -# Control device ? +# MPU (CONFIGURE CMI0001/16777472 (LD 1 (IO 0 (SIZE 2) (BASE 0x0330)) @@ -57,10 +59,11 @@ CONFIG_SOUND_MSS=m (ACT Y) )) -# SB... +# SoundBlaster + (CONFIGURE CMI0001/16777472 (LD 3 (IO 0 (SIZE 16) (BASE 0x0220)) -(INT 0 (IRQ 7 (MODE +E))) +(INT 0 (IRQ 5 (MODE +E))) (DMA 0 (CHANNEL 1)) (DMA 1 (CHANNEL 5)) (NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}") @@ -74,13 +77,22 @@ CONFIG_SOUND_MSS=m The module sequence is trivial: -/sbin/modprobe sound -# You need to load the ad1848 module first. That matters, otherwise the -# chip falls into soundblaster compatibility and you won't get it back out -/sbin/insmod ad1848 io=0x530 dma=0 irq=5 soundpro=1 +/sbin/insmod soundcore +/sbin/insmod sound /sbin/insmod uart401 -/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 -/sbin/insmod mpu401 io=0x330 -/sbin/insmod opl3 io=0x388 +# insert this first +/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1 +# The sb module is an alternative to the ad1848 (Microsoft Sound System) +# Anyhow, this is full duplex and has MIDI +/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330 + + - The soundchip is now fully initialized. Enjoy it. +Alma Chao suggests the following /etc/conf.modules: + +alias sound ad1848 +alias synth0 opl3 +options ad1848 io=0x530 irq=7 dma=0 soundpro=1 +options opl3 io=0x388 + + diff --git a/Documentation/svga.txt b/Documentation/svga.txt index 22d82294b222..cd66ec836e4f 100644 --- a/Documentation/svga.txt +++ b/Documentation/svga.txt @@ -1,5 +1,5 @@ - Video Mode Selection Support 2.11 - (c) 1995--1997 Martin Mares, + Video Mode Selection Support 2.13 + (c) 1995--1999 Martin Mares, -------------------------------------------------------------------------------- 1. Intro @@ -9,6 +9,11 @@ allows the use of various special video modes supported by the video BIOS. Due to usage of the BIOS, the selection is limited to boot time (before the kernel decompression starts) and works only on 80X86 machines. + ** Short intro for the impatient: Just use vga=ask for the first time, + ** enter `scan' on the video mode prompt, pick the mode you want to use, + ** remember its mode ID (the four-digit hexadecimal number) and then + ** set the vga parameter to this number (converted to decimal first). + The video mode to be used is selected by a kernel parameter which can be specified in the kernel Makefile (the SVGA_MODE=... line) or by the "vga=..." option of LILO (or some other boot loader you use) or by the "vidmode" utility @@ -268,3 +273,4 @@ force setting of the correct mode. - Removed the doc section describing adding of new probing functions as I try to get rid of _all_ hardware probing here. 2.12 (25-May-98)- Added support for VESA frame buffer graphics. +2.13 (14-May-99)- Minor documentation fixes. diff --git a/Makefile b/Makefile index 488fdac0d14d..188946de5881 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 9 +SUBLEVEL = 10 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff --git a/arch/alpha/config.in b/arch/alpha/config.in index d632cdbb9919..4a0da0cbcb30 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -142,6 +142,7 @@ fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \ + -o "$CONFIG_ALPHA_TAKARA" = "y" -o "$CONFIG_ALPHA_EB164" = "y" \ -o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \ -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \ -o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ @@ -166,7 +167,11 @@ then define_bool CONFIG_ALPHA_AVANTI y fi -bool 'Symmetric multi-processing support' CONFIG_SMP +if [ "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_RAWHIDE" = "y" \ + -o "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_GENERIC" = "y" ] +then + bool 'Symmetric multi-processing support' CONFIG_SMP +fi if [ "$CONFIG_PCI" = "y" ]; then bool 'PCI quirks' CONFIG_PCI_QUIRKS diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 65975c168f55..cba493d09b1f 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -52,6 +52,7 @@ EXPORT_SYMBOL(local_bh_count); EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); EXPORT_SYMBOL(screen_info); EXPORT_SYMBOL(perf_irq); @@ -170,8 +171,8 @@ EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); #if DEBUG_SPINLOCK EXPORT_SYMBOL(spin_unlock); -EXPORT_SYMBOL(spin_lock); -EXPORT_SYMBOL(spin_trylock); +EXPORT_SYMBOL(debug_spin_lock); +EXPORT_SYMBOL(debug_spin_trylock); #endif #if DEBUG_RWLOCK EXPORT_SYMBOL(write_lock); diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c index e0f52cb8eb21..eef31582638c 100644 --- a/arch/alpha/kernel/core_cia.c +++ b/arch/alpha/kernel/core_cia.c @@ -598,7 +598,7 @@ cia_pci_clr_err(void) { CIA_jd = *(vuip)CIA_IOC_CIA_ERR; DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); - *(vuip)CIA_IOC_CIA_ERR = 0x0180; + *(vuip)CIA_IOC_CIA_ERR = CIA_jd; mb(); return 0; } @@ -698,6 +698,10 @@ cia_machine_check(unsigned long vector, unsigned long la_ptr, reason = buf; break; } + mb(); + mb(); /* magic */ + draina(); + cia_pci_clr_err(); wrmces(rdmces()); /* reset machine check pending flag */ mb(); diff --git a/arch/alpha/kernel/fpreg.c b/arch/alpha/kernel/fpreg.c index 6da94c0cbb61..0bd8b9f67dfc 100644 --- a/arch/alpha/kernel/fpreg.c +++ b/arch/alpha/kernel/fpreg.c @@ -1,10 +1,10 @@ /* - * kernel/fpreg.c + * arch/alpha/kernel/fpreg.c * * (C) Copyright 1998 Linus Torvalds */ -#ifdef __alpha_cix__ +#if defined(__alpha_cix__) || defined(__alpha_fix__) #define STT(reg,val) asm volatile ("ftoit $f"#reg",%0" : "=r"(val)); #else #define STT(reg,val) asm volatile ("stt $f"#reg",%0" : "=m"(val)); @@ -52,7 +52,7 @@ alpha_read_fp_reg (unsigned long reg) return val; } -#ifdef __alpha_cix__ +#if defined(__alpha_cix__) || defined(__alpha_fix__) #define LDT(reg,val) asm volatile ("itoft %0,$f"#reg : : "r"(val)); #else #define LDT(reg,val) asm volatile ("ldt $f"#reg",%0" : : "m"(val)); diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S index 8ba50462bc92..3fcbdbcda71e 100644 --- a/arch/alpha/kernel/head.S +++ b/arch/alpha/kernel/head.S @@ -32,24 +32,26 @@ __start: #ifdef __SMP__ .align 3 - .globl __start_cpu - .ent __start_cpu - /* On entry here from SRM console, the HWPCB of this processor - has been loaded, and $27 contains the task pointer */ -__start_cpu: - .prologue 0 - /* First order of business, load the GP */ - br $26,1f -1: ldgp $29,0($26) - /* We need to get current loaded up with our first task... */ - mov $27,$8 - /* Set FEN */ - lda $16,1($31) - call_pal PAL_wrfen - /* ... and then we can start the processor. */ - jsr $26,start_secondary + .globl __smp_callin + .ent __smp_callin + /* On entry here from SRM console, the HWPCB of the per-cpu + slot for this processor has been loaded. We've arranged + for the UNIQUE value for this process to contain the PCBB + of the target idle task. */ +__smp_callin: + .prologue 1 + ldgp $29,0($27) # First order of business, load the GP. + + call_pal PAL_rduniq # Grab the target PCBB. + mov $0,$16 # Install it. + call_pal PAL_swpctx + + lda $8,0x3fff # Find "current". + bic $30,$8,$8 + + jsr $26,smp_callin call_pal PAL_halt - .end __start_cpu + .end __smp_callin #endif /* __SMP__ */ .align 3 diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index e1a53ab5763b..d53f7ffeaae9 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -192,7 +192,7 @@ unmask_irq(unsigned long irq) } void -disable_irq(unsigned int irq_nr) +disable_irq_nosync(unsigned int irq_nr) { unsigned long flags; @@ -201,6 +201,14 @@ disable_irq(unsigned int irq_nr) restore_flags(flags); } +void +disable_irq(unsigned int irq_nr) +{ + /* This works non-SMP, and SMP until we write code to distribute + interrupts to more that cpu 0. */ + disable_irq_nosync(irq_nr); +} + void enable_irq(unsigned int irq_nr) { diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 93d8db60276a..93fb02b9774f 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -75,33 +75,46 @@ sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2, return 0; } -static void __attribute__((noreturn)) -do_cpu_idle(void) +#ifdef __SMP__ +void +cpu_idle(void *unused) { /* An endless idle loop with no priority at all. */ current->priority = 0; + current->counter = -100; + while (1) { - check_pgt_cache(); - run_task_queue(&tq_scheduler); - current->counter = 0; - schedule(); + /* FIXME -- EV6 and LCA45 know how to power down + the CPU. */ + + /* Although we are an idle CPU, we do not want to + get into the scheduler unnecessarily. */ + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } } } - -#ifdef __SMP__ -void -cpu_idle(void *unused) -{ - do_cpu_idle(); -} #endif asmlinkage int sys_idle(void) { - if (current->pid == 0) - do_cpu_idle(); - return -EPERM; + if (current->pid != 0) + return -EPERM; + + /* An endless idle loop with no priority at all. */ + current->priority = 0; + current->counter = -100; + init_idle(); + + while (1) { + /* FIXME -- EV6 and LCA45 know how to power down + the CPU. */ + + schedule(); + check_pgt_cache(); + } } void diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 8a0efe52d6af..f7e54a982c56 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -151,6 +151,8 @@ extern unsigned long srm_hae; extern void setup_smp(void); extern int smp_info(char *buffer); extern void handle_ipi(struct pt_regs *); +extern void smp_percpu_timer_interrupt(struct pt_regs *); +extern int smp_boot_cpuid; /* bios32.c */ extern void reset_for_srm(void); @@ -178,7 +180,7 @@ extern unsigned long alpha_read_fp_reg (unsigned long reg); extern void wrmces(unsigned long mces); extern void cserve_ena(unsigned long); extern void cserve_dis(unsigned long); -extern void __start_cpu(unsigned long); +extern void __smp_callin(void); /* entry.S */ extern void entArith(void); diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 80b4454e1820..a808a8149a12 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -106,6 +106,7 @@ WEAK(alcor_mv); WEAK(alphabook1_mv); WEAK(avanti_mv); WEAK(cabriolet_mv); +WEAK(clipper_mv); WEAK(dp264_mv); WEAK(eb164_mv); WEAK(eb64p_mv); @@ -330,6 +331,10 @@ find_end_memory(void) /* Round it up to an even number of pages. */ high = (high + PAGE_SIZE) & (PAGE_MASK*2); + + /* Enforce maximum of 2GB even if there is more. Blah. */ + if (high > 0x80000000UL) + high = 0x80000000UL; return PAGE_OFFSET + high; } @@ -448,11 +453,11 @@ get_sysvec(long type, long variation, long cpu) static struct alpha_machine_vector *tsunami_vecs[] __initlocaldata = { NULL, - &dp264_mv, /* dp164 */ + &dp264_mv, /* dp264 */ &dp264_mv, /* warhol */ &dp264_mv, /* windjammer */ &monet_mv, /* monet */ - &dp264_mv, /* clipper */ + &clipper_mv, /* clipper */ &dp264_mv, /* goldrush */ &webbrick_mv, /* webbrick */ &dp264_mv, /* catamaran */ @@ -537,6 +542,7 @@ get_sysvec_byname(const char *name) &alphabook1_mv, &avanti_mv, &cabriolet_mv, + &clipper_mv, &dp264_mv, &eb164_mv, &eb64p_mv, diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 99bd367850d6..34ac7754ba48 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -24,6 +24,12 @@ #include #include +#include "proto.h" + + +#include "proto.h" + + #define DEBUG_SIG 0 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index aa1eaf363e48..f01c0e55d632 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,8 @@ #include #include "proto.h" +#include "irq.h" + #define DEBUG_SMP 0 #if DEBUG_SMP @@ -37,62 +40,44 @@ #define DBGS(args) #endif -struct ipi_msg_flush_tb_struct { - volatile unsigned int flush_tb_mask; - union { - struct mm_struct * flush_mm; - struct vm_area_struct * flush_vma; - } p; - unsigned long flush_addr; - unsigned long flush_end; -}; - -static struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; -static spinlock_t flush_tb_lock = SPIN_LOCK_UNLOCKED; - +/* A collection of per-processor data. */ struct cpuinfo_alpha cpu_data[NR_CPUS]; -spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; -spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; - -unsigned int boot_cpu_id = 0; -static int smp_activated = 0; +/* A collection of single bit ipi messages. */ +static struct { + unsigned long bits __cacheline_aligned; +} ipi_data[NR_CPUS]; -int smp_found_config = 0; /* Have we found an SMP box */ -static int max_cpus = -1; +enum ipi_message_type { + IPI_RESCHEDULE, + IPI_CALL_FUNC, + IPI_CPU_STOP, +}; -unsigned int cpu_present_map = 0; +spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED; -int smp_num_cpus = 1; -int smp_num_probed = 0; /* Internal processor count */ +/* Set to a secondary's cpuid when it comes online. */ +static unsigned long smp_secondary_alive; -int smp_threads_ready = 0; -volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; -volatile unsigned long smp_spinning[NR_CPUS] = { 0, }; +unsigned long cpu_present_mask; /* Which cpus ids came online. */ +static int max_cpus = -1; /* Command-line limitation. */ +int smp_boot_cpuid; /* Which processor we booted from. */ +int smp_num_probed; /* Internal processor count */ +int smp_num_cpus = 1; /* Number that came online. */ +int smp_threads_ready; /* True once the per process idle is forked. */ cycles_t cacheflush_time; -unsigned int prof_multiplier[NR_CPUS]; -unsigned int prof_counter[NR_CPUS]; - -volatile int ipi_bits[NR_CPUS] __cacheline_aligned; - -unsigned long boot_cpu_palrev; - -volatile int smp_commenced = 0; -volatile int smp_processors_ready = 0; - -volatile int cpu_number_map[NR_CPUS]; -volatile int cpu_logical_map[NR_CPUS]; +int cpu_number_map[NR_CPUS]; +int __cpu_logical_map[NR_CPUS]; extern void calibrate_delay(void); -extern struct thread_struct * original_pcb_ptr; - -static void smp_setup_percpu_timer(void); -static void secondary_cpu_start(int, struct task_struct *); -static void send_cpu_msg(char *, int); +extern asmlinkage void entInt(void); -/* Process bootcommand SMP options, like "nosmp" and "maxcpus=" */ + +/* + * Process bootcommand SMP options, like "nosmp" and "maxcpus=". + */ void __init smp_setup(char *str, int *ints) { @@ -102,100 +87,87 @@ smp_setup(char *str, int *ints) max_cpus = 0; } -static void __init -smp_store_cpu_info(int id) +/* + * Called by both boot and secondaries to move global data into + * per-processor storage. + */ +static inline void __init +smp_store_cpu_info(int cpuid) { - /* This is it on Alpha, so far. */ - cpu_data[id].loops_per_sec = loops_per_sec; + cpu_data[cpuid].loops_per_sec = loops_per_sec; } -void __init -smp_commence(void) +/* + * Ideally sets up per-cpu profiling hooks. Doesn't do much now... + */ +static inline void __init +smp_setup_percpu_timer(int cpuid) { - /* Lets the callin's below out of their loop. */ - mb(); - smp_commenced = 1; + cpu_data[cpuid].prof_counter = 1; + cpu_data[cpuid].prof_multiplier = 1; + +#ifdef NOT_YET_PROFILING + load_profile_irq(mid_xlate[cpu], lvl14_resolution); + if (cpu == smp_boot_cpuid) + enable_pil_irq(14); +#endif } +/* + * Where secondaries begin a life of C. + */ void __init smp_callin(void) { int cpuid = hard_smp_processor_id(); DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state)); -#ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); -#endif -#if 0 - set_irq_udt(mid_xlate[boot_cpu_id]); -#endif + + /* Turn on machine checks. */ + wrmces(7); + + /* Set trap vectors. */ + trap_init(); + + /* Set interrupt vector. */ + wrent(entInt, 0); + + /* Setup the scheduler for this processor. */ + init_idle(); /* Get our local ticker going. */ - smp_setup_percpu_timer(); + smp_setup_percpu_timer(cpuid); -#if 0 + /* Must have completely accurate bogos. */ + __sti(); calibrate_delay(); -#endif smp_store_cpu_info(cpuid); -#ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); -#endif /* Allow master to continue. */ - set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]); -#ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); -#endif - -#ifdef NOT_YET - while(!task[cpuid] || current_set[cpuid] != task[cpuid]) - barrier(); -#endif - -#ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); -#endif -#if 0 - __sti(); -#endif -} + wmb(); + smp_secondary_alive = cpuid; -asmlinkage int __init -start_secondary(void *unused) -{ - extern asmlinkage void entInt(void); - extern void paging_init_secondary(void); + /* Wait for the go code. */ + while (!smp_threads_ready) + barrier(); - wrmces(7); - paging_init_secondary(); - trap_init(); - wrent(entInt, 0); + printk(KERN_INFO "SMP: commencing CPU %d current %p\n", + cpuid, current); - smp_callin(); - while (!smp_commenced) - barrier(); -#if 1 - printk("start_secondary: commencing CPU %d current %p\n", - hard_smp_processor_id(), current); -#endif + /* Do nothing. */ cpu_idle(NULL); } + +/* + * Rough estimation for SMP scheduling, this is the number of cycles it + * takes for a fully memory-limited process to flush the SMP-local cache. + * + * We are not told how much cache there is, so we have to guess. + */ static void __init smp_tune_scheduling (void) { - /* - * Rough estimation for SMP scheduling, this is the number of - * cycles it takes for a fully memory-limited process to flush - * the SMP-local cache. - * - * We are not told how much cache there is, so we have to guess. - */ - struct percpu_struct *cpu; unsigned long on_chip_cache; unsigned long freq; @@ -231,259 +203,159 @@ smp_tune_scheduling (void) cacheflush_time = freq / 1024 * on_chip_cache / 5000; } - /* - * Cycle through the processors sending START msgs to boot each. + * Send a message to a secondary's console. "START" is one such + * interesting message. ;-) */ -void __init -smp_boot_cpus(void) +static void +send_secondary_console_msg(char *str, int cpuid) { - int cpucount = 0; - int i, first, prev; - - printk("Entering SMP Mode.\n"); - -#if 0 - __sti(); -#endif - - for(i=0; i < NR_CPUS; i++) { - cpu_number_map[i] = -1; - cpu_logical_map[i] = -1; - prof_counter[i] = 1; - prof_multiplier[i] = 1; - ipi_bits[i] = 0; - } - - cpu_number_map[boot_cpu_id] = 0; - cpu_logical_map[0] = boot_cpu_id; - current->processor = boot_cpu_id; /* ??? */ - - smp_store_cpu_info(boot_cpu_id); - smp_tune_scheduling(); -#ifdef NOT_YET - printk("CPU%d: ", boot_cpu_id); - print_cpu_info(&cpu_data[boot_cpu_id]); - set_irq_udt(mid_xlate[boot_cpu_id]); -#endif - smp_setup_percpu_timer(); -#ifdef HUH - local_flush_cache_all(); -#endif - if (smp_num_probed == 1) - return; /* Not an MP box. */ - -#if NOT_YET - /* - * If SMP should be disabled, then really disable it! - */ - if (!max_cpus) - { - smp_found_config = 0; - printk(KERN_INFO "SMP mode deactivated.\n"); - } -#endif - - for (i = 0; i < NR_CPUS; i++) { + struct percpu_struct *cpu; + register char *cp1, *cp2; + unsigned long cpumask; + size_t len; + long timeout; - if (i == boot_cpu_id) - continue; + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + cpuid * hwrpb->processor_size); - if (cpu_present_map & (1 << i)) { - struct task_struct *idle; - int timeout; + cpumask = (1L << cpuid); + if (hwrpb->txrdy & cpumask) + goto delay1; + ready1: - /* Cook up an idler for this guy. */ - kernel_thread(start_secondary, NULL, CLONE_PID); - idle = task[++cpucount]; - if (!idle) - panic("No idle process for CPU %d", i); - idle->processor = i; + cp2 = str; + len = strlen(cp2); + *(unsigned int *)&cpu->ipc_buffer[0] = len; + cp1 = (char *) &cpu->ipc_buffer[1]; + memcpy(cp1, cp2, len); - DBGS(("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n", - i, idle->state, idle->flags)); + /* atomic test and set */ + wmb(); + set_bit(cpuid, &hwrpb->rxrdy); - /* whirrr, whirrr, whirrrrrrrrr... */ -#ifdef HUH - local_flush_cache_all(); -#endif - secondary_cpu_start(i, idle); + if (hwrpb->txrdy & cpumask) + goto delay2; + ready2: + return; - /* wheee... it's going... wait for 5 secs...*/ - for (timeout = 0; timeout < 50000; timeout++) { - if (cpu_callin_map[i]) - break; - udelay(100); - } - if (cpu_callin_map[i]) { - /* Another "Red Snapper". */ - cpu_number_map[i] = cpucount; - cpu_logical_map[cpucount] = i; - } else { - cpucount--; - printk("smp_boot_cpus: Processor %d" - " is stuck 0x%lx.\n", i, idle->flags); - } - } - if (!(cpu_callin_map[i])) { - cpu_present_map &= ~(1 << i); - cpu_number_map[i] = -1; - } - } -#ifdef HUH - local_flush_cache_all(); -#endif - if (cpucount == 0) { - printk("smp_boot_cpus: ERROR - only one Processor found.\n"); - cpu_present_map = (1 << smp_processor_id()); - } else { - unsigned long bogosum = 0; - for (i = 0; i < NR_CPUS; i++) { - if (cpu_present_map & (1 << i)) - bogosum += cpu_data[i].loops_per_sec; - } - printk("smp_boot_cpus: Total of %d Processors activated" - " (%lu.%02lu BogoMIPS).\n", - cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); - smp_activated = 1; - smp_num_cpus = cpucount + 1; +delay1: + /* Wait one second. Note that jiffies aren't ticking yet. */ + for (timeout = 100000; timeout > 0; --timeout) { + if (!(hwrpb->txrdy & cpumask)) + goto ready1; + udelay(10); + barrier(); } + goto timeout; - /* Setup CPU list for IRQ distribution scheme. */ - first = prev = -1; - for (i = 0; i < NR_CPUS; i++) { - if (cpu_present_map & (1 << i)) { - if (first == -1) - first = i; - if (prev != -1) - cpu_data[i].next = i; - prev = i; - } +delay2: + /* Wait one second. */ + for (timeout = 100000; timeout > 0; --timeout) { + if (!(hwrpb->txrdy & cpumask)) + goto ready2; + udelay(10); + barrier(); } - cpu_data[prev].next = first; + goto timeout; - /* Ok, they are spinning and ready to go. */ - smp_processors_ready = 1; +timeout: + printk("Processor %x not ready\n", cpuid); + return; } -static void __init -smp_setup_percpu_timer(void) +/* + * A secondary console wants to send a message. Receive it. + */ +static void +recv_secondary_console_msg(void) { - int cpu = smp_processor_id(); - - prof_counter[cpu] = prof_multiplier[cpu] = 1; -#ifdef NOT_YET - load_profile_irq(mid_xlate[cpu], lvl14_resolution); - if (cpu == boot_cpu_id) - enable_pil_irq(14); -#endif -} - -extern void update_one_process(struct task_struct *p, unsigned long ticks, - unsigned long user, unsigned long system, - int cpu); + int mycpu, i, cnt; + unsigned long txrdy = hwrpb->txrdy; + char *cp1, *cp2, buf[80]; + struct percpu_struct *cpu; -void -smp_percpu_timer_interrupt(struct pt_regs *regs) -{ - int cpu = smp_processor_id(); + DBGS(("recv_secondary_console_msg: TXRDY 0x%lx.\n", txrdy)); -#ifdef NOT_YET - clear_profile_irq(mid_xlate[cpu]); - if(!user_mode(regs)) - alpha_do_profile(regs->pc); -#endif + mycpu = hard_smp_processor_id(); - if (!--prof_counter[cpu]) { - int user = user_mode(regs); - if (current->pid) { - update_one_process(current, 1, user, !user, cpu); + for (i = 0; i < NR_CPUS; i++) { + if (!(txrdy & (1L << i))) + continue; - if (--current->counter < 0) { - current->counter = 0; - current->need_resched = 1; - } + DBGS(("recv_secondary_console_msg: " + "TXRDY contains CPU %d.\n", i)); - spin_lock(&ticker_lock); - if (user) { - if (current->priority < DEF_PRIORITY) { - kstat.cpu_nice++; - kstat.per_cpu_nice[cpu]++; - } else { - kstat.cpu_user++; - kstat.per_cpu_user[cpu]++; - } - } else { - kstat.cpu_system++; - kstat.per_cpu_system[cpu]++; - } - spin_unlock(&ticker_lock); - } - prof_counter[cpu] = prof_multiplier[cpu]; - } -} + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + i * hwrpb->processor_size); -int __init -setup_profiling_timer(unsigned int multiplier) -{ -#ifdef NOT_YET - int i; - unsigned long flags; + printk(KERN_INFO "recv_secondary_console_msg: on %d from %d" + " HALT_REASON 0x%lx FLAGS 0x%lx\n", + mycpu, i, cpu->halt_reason, cpu->flags); - /* Prevent level14 ticker IRQ flooding. */ - if((!multiplier) || (lvl14_resolution / multiplier) < 500) - return -EINVAL; + cnt = cpu->ipc_buffer[0] >> 32; + if (cnt <= 0 || cnt >= 80) + strcpy(buf, "<<< BOGUS MSG >>>"); + else { + cp1 = (char *) &cpu->ipc_buffer[11]; + cp2 = buf; + strcpy(cp2, cp1); + + while ((cp2 = strchr(cp2, '\r')) != 0) { + *cp2 = ' '; + if (cp2[1] == '\n') + cp2[1] = ' '; + } + } - save_and_cli(flags); - for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1 << i)) { - load_profile_irq(mid_xlate[i], lvl14_resolution / multip -lier); - prof_multiplier[i] = multiplier; - } + printk(KERN_INFO "recv_secondary_console_msg: on %d " + "message is '%s'\n", mycpu, buf); } - restore_flags(flags); - - return 0; -#endif - return -EINVAL; -} - -/* Only broken Intel needs this, thus it should not even be - referenced globally. */ - -void __init -initialize_secondary(void) -{ + hwrpb->txrdy = 0; } -static void __init +/* + * Convince the console to have a secondary cpu begin execution. + */ +static int __init secondary_cpu_start(int cpuid, struct task_struct *idle) { struct percpu_struct *cpu; - int timeout; + struct pcb_struct *hwpcb; + long timeout; cpu = (struct percpu_struct *) ((char*)hwrpb + hwrpb->processor_offset + cpuid * hwrpb->processor_size); - - /* Set context to idle thread this CPU will use when running - assumption is that the idle thread is all set to go... ??? */ - memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct)); - cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */ - - DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n", - cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb)); + hwpcb = (struct pcb_struct *) cpu->hwpcb; + + /* Initialize the CPU's HWPCB to something just good enough for + us to get started. Immediately after starting, we'll swpctx + to the target idle task's tss. Reuse the stack in the mean + time. Precalculate the target PCBB. */ + hwpcb->ksp = (unsigned long) idle + sizeof(union task_union) - 16; + hwpcb->usp = 0; + hwpcb->ptbr = idle->tss.ptbr; + hwpcb->pcc = 0; + hwpcb->asn = 0; + hwpcb->unique = virt_to_phys(&idle->tss); + hwpcb->flags = idle->tss.pal_flags; + hwpcb->res1 = hwpcb->res2 = 0; + + DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx UNIQUE 0x%lx\n", + hwpcb->ksp, hwpcb->ptbr, hwrpb->vptb, hwcpb->unique)); DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", cpuid, idle->state, idle->tss.pal_flags)); /* Setup HWRPB fields that SRM uses to activate secondary CPU */ - hwrpb->CPU_restart = __start_cpu; - hwrpb->CPU_restart_data = (unsigned long) idle; + hwrpb->CPU_restart = __smp_callin; + hwrpb->CPU_restart_data = (unsigned long) __smp_callin; /* Recalculate and update the HWRPB checksum */ hwrpb_update_checksum(hwrpb); @@ -495,99 +367,97 @@ secondary_cpu_start(int cpuid, struct task_struct *idle) /* SRM III 3.4.1.3 */ cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */ cpu->flags &= ~1; /* turn off Bootstrap In Progress */ - mb(); + wmb(); - send_cpu_msg("START\r\n", cpuid); + send_secondary_console_msg("START\r\n", cpuid); - /* now, we wait... */ - for (timeout = 10000; !(cpu->flags & 1); timeout--) { - if (timeout <= 0) { - printk("Processor %d failed to start\n", cpuid); - /* needed for pset_info to work */ -#if 0 - ipc_processor_enable(cpu_to_processor(cpunum)); -#endif - return; - } - mdelay(1); + /* Wait 1 second for an ACK from the console. Note that jiffies + aren't ticking yet. */ + for (timeout = 100000; timeout > 0; timeout--) { + if (cpu->flags & 1) + goto started; + udelay(10); barrier(); } + printk(KERN_ERR "SMP: Processor %d failed to start.\n", cpuid); + return -1; + +started: DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); + return 0; } -static void -send_cpu_msg(char *str, int cpuid) +/* + * Bring one cpu online. + */ +static int __init +smp_boot_one_cpu(int cpuid, int cpunum) { - struct percpu_struct *cpu; - register char *cp1, *cp2; - unsigned long cpumask; - size_t len; - int timeout; - - cpu = (struct percpu_struct *) - ((char*)hwrpb - + hwrpb->processor_offset - + cpuid * hwrpb->processor_size); - - cpumask = (1L << cpuid); - if (hwrpb->txrdy & cpumask) - goto delay1; - ready1: - - cp2 = str; - len = strlen(cp2); - *(unsigned int *)&cpu->ipc_buffer[0] = len; - cp1 = (char *) &cpu->ipc_buffer[1]; - memcpy(cp1, cp2, len); - - /* atomic test and set */ - set_bit(cpuid, &hwrpb->rxrdy); - - if (hwrpb->txrdy & cpumask) - goto delay2; - ready2: - return; - -delay1: - for (timeout = 10000; timeout > 0; --timeout) { - if (!(hwrpb->txrdy & cpumask)) - goto ready1; - udelay(100); + struct task_struct *idle; + long timeout; + + /* Cook up an idler for this guy. Note that the address we give + to kernel_thread is irrelevant -- it's going to start where + HWRPB.CPU_restart says to start. But this gets all the other + task-y sort of data structures set up like we wish. */ + kernel_thread((void *)__smp_callin, NULL, CLONE_PID|CLONE_VM); + idle = task[cpunum]; + if (!idle) + panic("No idle process for CPU %d", cpuid); + idle->processor = cpuid; + + /* Schedule the first task manually. */ + /* ??? Ingo, what is this? */ + idle->has_cpu = 1; + + DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n", + cpuid, idle->state, idle->flags)); + + /* The secondary will change this once it is happy. Note that + secondary_cpu_start contains the necessary memory barrier. */ + smp_secondary_alive = -1; + + /* Whirrr, whirrr, whirrrrrrrrr... */ + if (secondary_cpu_start(cpuid, idle)) + return -1; + + /* We've been acked by the console; wait one second for the task + to start up for real. Note that jiffies aren't ticking yet. */ + for (timeout = 0; timeout < 100000; timeout++) { + if (smp_secondary_alive != -1) + goto alive; + udelay(10); barrier(); } - goto timeout; -delay2: - for (timeout = 10000; timeout > 0; --timeout) { - if (!(hwrpb->txrdy & cpumask)) - goto ready2; - udelay(100); - barrier(); - } - goto timeout; + printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid); + return -1; -timeout: - printk("Processor %x not ready\n", cpuid); - return; +alive: + /* Another "Red Snapper". */ + cpu_number_map[cpuid] = cpunum; + __cpu_logical_map[cpunum] = cpuid; + return 0; } /* - * setup_smp() - * - * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined + * Called from setup_arch. Detect an SMP system and which processors + * are present. */ void __init setup_smp(void) { struct percpu_struct *cpubase, *cpu; int i; - - boot_cpu_id = hard_smp_processor_id(); - if (boot_cpu_id != 0) { - printk("setup_smp: boot_cpu_id != 0 (%d).\n", boot_cpu_id); + + smp_boot_cpuid = hard_smp_processor_id(); + if (smp_boot_cpuid != 0) { + printk(KERN_WARNING "SMP: Booting off cpu %d instead of 0?\n", + smp_boot_cpuid); } if (hwrpb->nr_processors > 1) { + int boot_cpu_palrev; DBGS(("setup_smp: nr_processors %ld\n", hwrpb->nr_processors)); @@ -601,10 +471,9 @@ setup_smp(void) ((char *)cpubase + i*hwrpb->processor_size); if ((cpu->flags & 0x1cc) == 0x1cc) { smp_num_probed++; - /* assume here that "whami" == index */ - cpu_present_map |= (1 << i); - if (i != boot_cpu_id) - cpu->pal_revision = boot_cpu_palrev; + /* Assume here that "whami" == index */ + cpu_present_mask |= (1L << i); + cpu->pal_revision = boot_cpu_palrev; } DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", @@ -614,76 +483,249 @@ setup_smp(void) } } else { smp_num_probed = 1; - cpu_present_map = (1 << boot_cpu_id); + cpu_present_mask = (1L << smp_boot_cpuid); } - printk("setup_smp: %d CPUs probed, cpu_present_map 0x%x," - " boot_cpu_id %d\n", - smp_num_probed, cpu_present_map, boot_cpu_id); + + printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_mask = %lx\n", + smp_num_probed, cpu_present_mask); } -static void -secondary_console_message(void) +/* + * Called by smp_init bring all the secondaries online and hold them. + */ +void __init +smp_boot_cpus(void) { - int mycpu, i, cnt; - unsigned long txrdy = hwrpb->txrdy; - char *cp1, *cp2, buf[80]; - struct percpu_struct *cpu; + int cpu_count, i; + unsigned long bogosum; - DBGS(("secondary_console_message: TXRDY 0x%lx.\n", txrdy)); + /* Take care of some initial bookkeeping. */ + memset(cpu_number_map, -1, sizeof(cpu_number_map)); + memset(__cpu_logical_map, -1, sizeof(__cpu_logical_map)); + memset(ipi_data, 0, sizeof(ipi_data)); - mycpu = hard_smp_processor_id(); + cpu_number_map[smp_boot_cpuid] = 0; + __cpu_logical_map[0] = smp_boot_cpuid; + current->processor = smp_boot_cpuid; + + smp_store_cpu_info(smp_boot_cpuid); + smp_tune_scheduling(); + smp_setup_percpu_timer(smp_boot_cpuid); + + init_idle(); + + /* Nothing to do on a UP box, or when told not to. */ + if (smp_num_probed == 1 || max_cpus == 0) { + printk(KERN_INFO "SMP mode deactivated.\n"); + return; + } + printk(KERN_INFO "SMP starting up secondaries.\n"); + + cpu_count = 1; for (i = 0; i < NR_CPUS; i++) { - if (!(txrdy & (1L << i))) + if (i == smp_boot_cpuid) continue; - DBGS(("secondary_console_message: " - "TXRDY contains CPU %d.\n", i)); + if (((cpu_present_mask >> i) & 1) == 0) + continue; - cpu = (struct percpu_struct *) - ((char*)hwrpb - + hwrpb->processor_offset - + i * hwrpb->processor_size); + if (smp_boot_one_cpu(i, cpu_count)) + continue; - printk("secondary_console_message: on %d from %d" - " HALT_REASON 0x%lx FLAGS 0x%lx\n", - mycpu, i, cpu->halt_reason, cpu->flags); + cpu_count++; + } - cnt = cpu->ipc_buffer[0] >> 32; - if (cnt <= 0 || cnt >= 80) - strcpy(buf, "<<< BOGUS MSG >>>"); - else { - cp1 = (char *) &cpu->ipc_buffer[11]; - cp2 = buf; - strcpy(cp2, cp1); - - while ((cp2 = strchr(cp2, '\r')) != 0) { - *cp2 = ' '; - if (cp2[1] == '\n') - cp2[1] = ' '; - } - } + if (cpu_count == 1) { + printk(KERN_ERR "SMP: Only one lonely processor alive.\n"); + return; + } + + bogosum = 0; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_mask & (1L << i)) + bogosum += cpu_data[i].loops_per_sec; + } + printk(KERN_INFO "SMP: Total of %d processors activated " + "(%lu.%02lu BogoMIPS).\n", + cpu_count, (bogosum + 2500) / 500000, + ((bogosum + 2500) / 5000) % 100); + + smp_num_cpus = cpu_count; +} + +/* + * Called by smp_init to release the blocking online cpus once they + * are all started. + */ +void __init +smp_commence(void) +{ + /* smp_init sets smp_threads_ready -- that's enough. */ + mb(); +} + +/* + * Only broken Intel needs this, thus it should not even be + * referenced globally. + */ - printk("secondary_console_message: on %d message is '%s'\n", - mycpu, buf); +void __init +initialize_secondary(void) +{ +} + + +extern void update_one_process(struct task_struct *p, unsigned long ticks, + unsigned long user, unsigned long system, + int cpu); + +void +smp_percpu_timer_interrupt(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + int user = user_mode(regs); + struct cpuinfo_alpha *data = &cpu_data[cpu]; + +#ifdef NOT_YET_PROFILING + clear_profile_irq(mid_xlate[cpu]); + if (!user) + alpha_do_profile(regs->pc); +#endif + + if (!--data->prof_counter) { + /* We need to make like a normal interrupt -- otherwise + timer interrupts ignore the global interrupt lock, + which would be a Bad Thing. */ + irq_enter(cpu, TIMER_IRQ); + + update_one_process(current, 1, user, !user, cpu); + if (current->pid) { + if (--current->counter < 0) { + current->counter = 0; + current->need_resched = 1; + } + + if (user) { + if (current->priority < DEF_PRIORITY) { + kstat.cpu_nice++; + kstat.per_cpu_nice[cpu]++; + } else { + kstat.cpu_user++; + kstat.per_cpu_user[cpu]++; + } + } else { + kstat.cpu_system++; + kstat.per_cpu_system[cpu]++; + } + } + + data->prof_counter = data->prof_multiplier; + irq_exit(cpu, TIMER_IRQ); } +} - hwrpb->txrdy = 0; +int __init +setup_profiling_timer(unsigned int multiplier) +{ +#ifdef NOT_YET_PROFILING + int i; + unsigned long flags; + + /* Prevent level14 ticker IRQ flooding. */ + if((!multiplier) || (lvl14_resolution / multiplier) < 500) + return -EINVAL; + + save_and_cli(flags); + for (i = 0; i < NR_CPUS; i++) { + if (cpu_present_mask & (1L << i)) { + load_profile_irq(mid_xlate[i], + lvl14_resolution / multiplier); + prof_multiplier[i] = multiplier; + } + } + restore_flags(flags); + + return 0; +#else + return -EINVAL; +#endif } -enum ipi_message_type { - IPI_TLB_ALL, - IPI_TLB_MM, - IPI_TLB_PAGE, - IPI_RESCHEDULE, - IPI_CPU_STOP + +static void +send_ipi_message(unsigned long to_whom, enum ipi_message_type operation) +{ + long i, j; + + /* Reduce the number of memory barriers by doing two loops, + one to set the bits, one to invoke the interrupts. */ + + mb(); /* Order out-of-band data and bit setting. */ + + for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { + if (to_whom & j) + set_bit(operation, &ipi_data[i].bits); + } + + mb(); /* Order bit setting and interrupt. */ + + for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { + if (to_whom & j) + wripir(i); + } +} + +/* Structure and data for smp_call_function. This is designed to + minimize static memory requirements. Plus it looks cleaner. */ + +struct smp_call_struct { + void (*func) (void *info); + void *info; + long wait; + atomic_t unstarted_count; + atomic_t unfinished_count; }; +static struct smp_call_struct *smp_call_function_data; + +/* Atomicly drop data into a shared pointer. The pointer is free if + it is initially locked. If retry, spin until free. */ + +static inline int +pointer_lock (void *lock, void *data, int retry) +{ + void *old, *tmp; + + mb(); +again: + /* Compare and swap with zero. */ + asm volatile ( + "1: ldq_l %0,%1\n" + " mov %3,%2\n" + " bne %0,2f\n" + " stq_c %2,%1\n" + " beq %2,1b\n" + "2:" + : "=&r"(old), "=m"(*(void **)lock), "=&r"(tmp) + : "r"(data) + : "memory"); + + if (old == 0) + return 0; + if (! retry) + return -EBUSY; + + while (*(void **)lock) + schedule(); + goto again; +} + void handle_ipi(struct pt_regs *regs) { int this_cpu = smp_processor_id(); - volatile int * pending_ipis = &ipi_bits[this_cpu]; + unsigned long *pending_ipis = &ipi_data[this_cpu].bits; unsigned long ops; DBGS(("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n", @@ -699,190 +741,189 @@ handle_ipi(struct pt_regs *regs) ops &= ~which; which = ffz(~which); - if (which < IPI_RESCHEDULE) { - if (which == IPI_TLB_ALL) - tbia(); - else if (which == IPI_TLB_MM) { - struct mm_struct * mm; - mm = ipi_msg_flush_tb.p.flush_mm; - if (mm == current->mm) - flush_tlb_current(mm); - } - else /* IPI_TLB_PAGE */ { - struct vm_area_struct * vma; - struct mm_struct * mm; - unsigned long addr; - - vma = ipi_msg_flush_tb.p.flush_vma; - mm = vma->vm_mm; - addr = ipi_msg_flush_tb.flush_addr; - - if (mm == current->mm) - flush_tlb_current_page(mm, vma, addr); - } - clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); - } - else if (which == IPI_RESCHEDULE) { + if (which == IPI_RESCHEDULE) { /* Reschedule callback. Everything to be done is done by the interrupt return path. */ } + else if (which == IPI_CALL_FUNC) { + struct smp_call_struct *data; + void (*func)(void *info); + void *info; + int wait; + + data = smp_call_function_data; + func = data->func; + info = data->info; + wait = data->wait; + + /* Notify the sending CPU that the data has been + received, and execution is about to begin. */ + mb(); + atomic_dec (&data->unstarted_count); + + /* At this point the structure may be gone unless + wait is true. */ + (*func)(info); + + /* Notify the sending CPU that the task is done. */ + mb(); + if (wait) atomic_dec (&data->unfinished_count); + } else if (which == IPI_CPU_STOP) { halt(); } else { - printk(KERN_CRIT "unknown_ipi() on CPU %d: %lu\n", + printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); } } while (ops); + mb(); /* Order data access and bit testing. */ } cpu_data[this_cpu].ipi_count++; if (hwrpb->txrdy) - secondary_console_message(); + recv_secondary_console_msg(); } -static void -send_ipi_message(unsigned long to_whom, enum ipi_message_type operation) +void +smp_send_reschedule(int cpu) { - long i, j; - - /* Reduce the number of memory barriers by doing two loops, - one to set the bits, one to invoke the interrupts. */ - - mb(); /* Order out-of-band data and bit setting. */ - - for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { - if (to_whom & j) - set_bit(operation, &ipi_bits[i]); - } - - mb(); /* Order bit setting and interrupt. */ + send_ipi_message(1L << cpu, IPI_RESCHEDULE); +} - for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { - if (to_whom & j) - wripir(i); - } +void +smp_send_stop(void) +{ + unsigned long to_whom = cpu_present_mask ^ (1L << smp_processor_id()); + send_ipi_message(to_whom, IPI_CPU_STOP); } +/* + * Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * If true, keep retrying until ready. + * If true, wait until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. + * + * Does not return until remote CPUs are nearly ready to execute + * or are or have executed. + */ + int -smp_info(char *buffer) +smp_call_function (void (*func) (void *info), void *info, int retry, int wait) { - long i; - unsigned long sum = 0; - for (i = 0; i < NR_CPUS; i++) - sum += cpu_data[i].ipi_count; + unsigned long to_whom = cpu_present_mask ^ (1L << smp_processor_id()); + struct smp_call_struct data; + long timeout; + + data.func = func; + data.info = info; + data.wait = wait; + atomic_set(&data.unstarted_count, smp_num_cpus - 1); + atomic_set(&data.unfinished_count, smp_num_cpus - 1); + + /* Aquire the smp_call_function_data mutex. */ + if (pointer_lock(&smp_call_function_data, &data, retry)) + return -EBUSY; + + /* Send a message to all other CPUs. */ + send_ipi_message(to_whom, IPI_CALL_FUNC); + + /* Wait for a minimal response. */ + timeout = jiffies + HZ; + while (atomic_read (&data.unstarted_count) > 0 + && time_before (jiffies, timeout)) + barrier(); - return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n", - smp_num_probed, smp_num_cpus, cpu_present_map, sum); -} + /* We either got one or timed out -- clear the lock. */ + mb(); + smp_call_function_data = 0; + if (atomic_read (&data.unstarted_count) > 0) + return -ETIMEDOUT; + + /* Wait for a complete response, if needed. */ + if (wait) { + while (atomic_read (&data.unfinished_count) > 0) + barrier(); + } -void -smp_send_reschedule(int cpu) -{ - send_ipi_message(1 << cpu, IPI_RESCHEDULE); + return 0; } -void -smp_send_stop(void) +static void +ipi_flush_tlb_all(void *ignored) { - unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); - send_ipi_message(to_whom, IPI_CPU_STOP); + tbia(); } void flush_tlb_all(void) { - unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); - long timeout = 1000000; - - spin_lock(&flush_tb_lock); - - ipi_msg_flush_tb.flush_tb_mask = to_whom; - send_ipi_message(to_whom, IPI_TLB_ALL); tbia(); - while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { - udelay(1); - barrier(); - } - - if (timeout == 0) { - printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n", - smp_processor_id(), - ipi_msg_flush_tb.flush_tb_mask); - ipi_msg_flush_tb.flush_tb_mask = 0; + /* Although we don't have any data to pass, we do want to + synchronize with the other processors. */ + if (smp_call_function(ipi_flush_tlb_all, NULL, 1, 1)) { + printk(KERN_CRIT "flush_tlb_all: timed out\n"); } +} - spin_unlock(&flush_tb_lock); +static void +ipi_flush_tlb_mm(void *x) +{ + struct mm_struct *mm = (struct mm_struct *) x; + if (mm == current->mm) + flush_tlb_current(mm); } void flush_tlb_mm(struct mm_struct *mm) { - unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); - long timeout = 1000000; - - spin_lock(&flush_tb_lock); - - ipi_msg_flush_tb.flush_tb_mask = to_whom; - ipi_msg_flush_tb.p.flush_mm = mm; - send_ipi_message(to_whom, IPI_TLB_MM); - - if (mm != current->mm) - flush_tlb_other(mm); - else + if (mm == current->mm) flush_tlb_current(mm); + else + flush_tlb_other(mm); - while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { - udelay(1); - barrier(); + if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) { + printk(KERN_CRIT "flush_tlb_mm: timed out\n"); } +} - if (timeout == 0) { - printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n", - smp_processor_id(), - ipi_msg_flush_tb.flush_tb_mask); - ipi_msg_flush_tb.flush_tb_mask = 0; - } +struct flush_tlb_page_struct { + struct vm_area_struct *vma; + struct mm_struct *mm; + unsigned long addr; +}; - spin_unlock(&flush_tb_lock); +static void +ipi_flush_tlb_page(void *x) +{ + struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x; + if (data->mm == current->mm) + flush_tlb_current_page(data->mm, data->vma, data->addr); } void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { - int cpu = smp_processor_id(); - unsigned long to_whom = cpu_present_map ^ (1 << cpu); - struct mm_struct * mm = vma->vm_mm; - int timeout = 1000000; - - spin_lock(&flush_tb_lock); + struct flush_tlb_page_struct data; + struct mm_struct *mm = vma->vm_mm; - ipi_msg_flush_tb.flush_tb_mask = to_whom; - ipi_msg_flush_tb.p.flush_vma = vma; - ipi_msg_flush_tb.flush_addr = addr; - send_ipi_message(to_whom, IPI_TLB_PAGE); + data.vma = vma; + data.mm = mm; + data.addr = addr; - if (mm != current->mm) - flush_tlb_other(mm); - else + if (mm == current->mm) flush_tlb_current_page(mm, vma, addr); - - while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { - udelay(1); - barrier(); - } - - if (timeout == 0) { - printk("flush_tlb_page: STUCK on CPU %d mask 0x%x\n", - smp_processor_id(), - ipi_msg_flush_tb.flush_tb_mask); - ipi_msg_flush_tb.flush_tb_mask = 0; + else + flush_tlb_other(mm); + + if (smp_call_function(ipi_flush_tlb_page, &data, 1, 1)) { + printk(KERN_CRIT "flush_tlb_page: timed out\n"); } - - spin_unlock(&flush_tb_lock); } void @@ -892,6 +933,20 @@ flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) flush_tlb_mm(mm); } + +int +smp_info(char *buffer) +{ + long i; + unsigned long sum = 0; + for (i = 0; i < NR_CPUS; i++) + sum += cpu_data[i].ipi_count; + + return sprintf(buffer, "CPUs probed %d active %d map 0x%lx IPIs %ld\n", + smp_num_probed, smp_num_cpus, cpu_present_mask, sum); +} + + #if DEBUG_SPINLOCK #ifdef MANAGE_SPINLOCK_IPL @@ -932,17 +987,16 @@ void spin_lock(spinlock_t * lock) { long tmp; - long stuck = 1<<27; + long stuck; void *inline_pc = __builtin_return_address(0); unsigned long started = jiffies; int printed = 0; int cpu = smp_processor_id(); long old_ipl = spinlock_raise_ipl(lock); + stuck = 1L << 28; try_again: - stuck = 0x10000000; /* was 4G, now 256M */ - /* Use sub-sections to put the actual loop at the end of this object file's text section so as to perfect branch prediction. */ @@ -961,19 +1015,16 @@ spin_lock(spinlock_t * lock) " blbs %0,2b\n" " br 1b\n" ".previous" - : "=r" (tmp), - "=m" (__dummy_lock(lock)), - "=r" (stuck) - : "2" (stuck)); + : "=r" (tmp), "=m" (__dummy_lock(lock)), "=r" (stuck) + : "1" (__dummy_lock(lock)), "2" (stuck)); if (stuck < 0) { - if (!printed) { - printk("spinlock stuck at %p(%d) owner %s at %p\n", - inline_pc, cpu, lock->task->comm, - lock->previous); - printed = 1; - } - stuck = 1<<30; + printk(KERN_WARNING + "spinlock stuck at %p(%d) owner %s at %p(%d) st %ld\n", + inline_pc, cpu, lock->task->comm, lock->previous, + lock->task->processor, lock->task->state); + stuck = 1L << 36; + printed = 1; goto try_again; } @@ -984,7 +1035,7 @@ spin_lock(spinlock_t * lock) lock->task = current; if (printed) { - printk("spinlock grabbed at %p(%d) %ld ticks\n", + printk(KERN_WARNING "spinlock grabbed at %p(%d) %ld ticks\n", inline_pc, cpu, jiffies - started); } } @@ -1006,7 +1057,7 @@ spin_trylock(spinlock_t * lock) return ret; } #endif /* DEBUG_SPINLOCK */ - + #if DEBUG_RWLOCK void write_lock(rwlock_t * lock) { @@ -1038,18 +1089,17 @@ void write_lock(rwlock_t * lock) " blt %1,8b\n" " br 1b\n" ".previous" - : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy) - , "=&r" (stuck_lock), "=&r" (stuck_reader) - : "0" (__dummy_lock(lock)) - , "3" (stuck_lock), "4" (stuck_reader) - ); + : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy), + "=&r" (stuck_lock), "=&r" (stuck_reader) + : "0" (__dummy_lock(lock)), "3" (stuck_lock), "4" (stuck_reader)); if (stuck_lock < 0) { - printk("write_lock stuck at %p\n", inline_pc); + printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc); goto try_again; } if (stuck_reader < 0) { - printk("write_lock stuck on readers at %p\n", inline_pc); + printk(KERN_WARNING "write_lock stuck on readers at %p\n", + inline_pc); goto try_again; } } @@ -1079,11 +1129,10 @@ void read_lock(rwlock_t * lock) " br 1b\n" ".previous" : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock) - : "0" (__dummy_lock(lock)), "2" (stuck_lock) - ); + : "0" (__dummy_lock(lock)), "2" (stuck_lock)); if (stuck_lock < 0) { - printk("read_lock stuck at %p\n", inline_pc); + printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc); goto try_again; } } diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index c464c37ec68c..4b4cf656733e 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -65,6 +65,33 @@ dp264_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) outb(mask, 0x21); /* ISA PIC1 */ } +static void +clipper_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) +{ + if (irq >= 16) { + volatile unsigned long *csr; + + if (TSUNAMI_bootcpu < 2) + if (!TSUNAMI_bootcpu) + csr = &TSUNAMI_cchip->dim0.csr; + else + csr = &TSUNAMI_cchip->dim1.csr; + else + if (TSUNAMI_bootcpu == 2) + csr = &TSUNAMI_cchip->dim2.csr; + else + csr = &TSUNAMI_cchip->dim3.csr; + + *csr = (~mask >> 16) | (1UL << 55); /* master ISA enable */ + mb(); + *csr; + } + else if (irq >= 8) + outb(mask >> 8, 0xA1); /* ISA PIC2 */ + else + outb(mask, 0x21); /* ISA PIC1 */ +} + static void dp264_device_interrupt(unsigned long vector, struct pt_regs * regs) { @@ -105,11 +132,16 @@ dp264_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) ack = irq = (vector - 0x800) >> 4; /* - * The DP264 SRM console reports PCI interrupts with a vector - * 0x100 *higher* than one might expect, as PCI IRQ 0 (ie bit 0) - * shows up as IRQ 16, etc, etc. We adjust it down by 16 to have - * it line up with the actual bit numbers from the DIM registers, - * which is how we manage the interrupts/mask. Sigh... + * The EV6 machines SRM console reports PCI interrupts with a vector + * calculated by: + * + * 0x900 + (0x10 * DRIR-bit) + * + * So bit 16 shows up as IRQ 32, etc, etc. + * + * On DP264/BRICK/MONET, we adjust it down by 16 because at least + * that many of the low order bits of the DRIR are not used, and + * so we don't count them. */ if (irq >= 32) ack = irq = irq - 16; @@ -117,11 +149,32 @@ dp264_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) handle_irq(irq, ack, regs); } +static void +clipper_srm_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + int irq, ack; + + ack = irq = (vector - 0x800) >> 4; + + /* + * The EV6 machines SRM console reports PCI interrupts with a vector + * calculated by: + * + * 0x900 + (0x10 * DRIR-bit) + * + * So bit 16 shows up as IRQ 32, etc, etc. + * + * CLIPPER uses bits 8-47 for PCI interrupts, so we do not need + * to scale down the vector reported, we just use it. + * + * Eg IRQ 24 is DRIR bit 8, etc, etc + */ + handle_irq(irq, ack, regs); +} + static void __init dp264_init_irq(void) { - volatile unsigned long *csr; - outb(0, DMA1_RESET_REG); outb(0, DMA2_RESET_REG); outb(DMA_MODE_CASCADE, DMA2_MODE_REG); @@ -130,23 +183,26 @@ dp264_init_irq(void) if (alpha_using_srm) alpha_mv.device_interrupt = dp264_srm_device_interrupt; - if (TSUNAMI_bootcpu < 2) - if (!TSUNAMI_bootcpu) - csr = &TSUNAMI_cchip->dim0.csr; - else - csr = &TSUNAMI_cchip->dim1.csr; - else - if (TSUNAMI_bootcpu == 2) - csr = &TSUNAMI_cchip->dim2.csr; - else - csr = &TSUNAMI_cchip->dim3.csr; - - /* Note invert on MASK bits. */ - *csr = ~(alpha_irq_mask); - mb(); - *csr; + dp264_update_irq_hw(16, alpha_irq_mask, 0); + + enable_irq(55); /* Enable ISA interrupt controller. */ + enable_irq(2); +} + +static void __init +clipper_init_irq(void) +{ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(DMA_MODE_CASCADE, DMA2_MODE_REG); + outb(0, DMA2_MASK_REG); + + if (alpha_using_srm) + alpha_mv.device_interrupt = clipper_srm_device_interrupt; + + clipper_update_irq_hw(16, alpha_irq_mask, 0); - enable_irq(55); /* Enable CYPRESS interrupt controller (ISA). */ + enable_irq(55); /* Enable ISA interrupt controller. */ enable_irq(2); } @@ -221,7 +277,7 @@ dp264_map_irq(struct pci_dev *dev, int slot, int pin) const long min_idsel = 5, max_idsel = 10, irqs_per_slot = 5; int irq = COMMON_TABLE_LOOKUP; - if (irq >= 0) + if (irq > 0) irq += 16 * dev2hose(dev); return irq; @@ -300,10 +356,10 @@ webbrick_map_irq(struct pci_dev *dev, int slot, int pin) { 30, 30, 30, 30, 30}, /* IdSel 11 21143 #2 */ { -1, -1, -1, -1, -1}, /* IdSel 12 unused */ { -1, -1, -1, -1, -1}, /* IdSel 13 unused */ - { 47, 47, 46, 45, 44}, /* IdSel 14 slot 0 */ + { 35, 35, 34, 33, 32}, /* IdSel 14 slot 0 */ { 39, 39, 38, 37, 36}, /* IdSel 15 slot 1 */ { 43, 43, 42, 41, 40}, /* IdSel 16 slot 2 */ - { 35, 35, 34, 33, 32}, /* IdSel 17 slot 3 */ + { 47, 47, 46, 45, 44}, /* IdSel 17 slot 3 */ }; const long min_idsel = 7, max_idsel = 17, irqs_per_slot = 5; int irq = COMMON_TABLE_LOOKUP; @@ -311,6 +367,28 @@ webbrick_map_irq(struct pci_dev *dev, int slot, int pin) return irq; } +static int __init +clipper_map_irq(struct pci_dev *dev, int slot, int pin) +{ + static char irq_tab[7][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ + { 16+ 8, 16+ 8, 16+ 9, 16+10, 16+11}, /* IdSel 1 slot 1 */ + { 16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 2 slot 2 */ + { 16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 3 slot 3 */ + { 16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 4 slot 4 */ + { 16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 5 slot 5 */ + { 16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 6 slot 6 */ + { -1, -1, -1, -1, -1} /* IdSel 7 ISA Bridge */ + }; + const long min_idsel = 1, max_idsel = 7, irqs_per_slot = 5; + int irq = COMMON_TABLE_LOOKUP; + + if (irq > 0) + irq += 16 * dev2hose(dev); + + return irq; +} + static void __init dp264_pci_fixup(void) { @@ -336,6 +414,13 @@ webbrick_pci_fixup(void) SMC669_Init(0); } +static void __init +clipper_pci_fixup(void) +{ + layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); + common_pci_fixup(clipper_map_irq, common_swizzle); +} + /* * The System Vectors @@ -407,5 +492,26 @@ struct alpha_machine_vector webbrick_mv __initmv = { pci_fixup: webbrick_pci_fixup, kill_arch: generic_kill_arch, }; -/* No alpha_mv alias for webbrick, since we compile it in unconditionally - with DP264; setup_arch knows how to cope. */ +struct alpha_machine_vector clipper_mv __initmv = { + vector_name: "Clipper", + DO_EV6_MMU, + DO_DEFAULT_RTC, + DO_TSUNAMI_IO, + DO_TSUNAMI_BUS, + machine_check: tsunami_machine_check, + max_dma_address: ALPHA_MAX_DMA_ADDRESS, + + nr_irqs: 64, + irq_probe_mask: _PROBE_MASK(64), + update_irq_hw: clipper_update_irq_hw, + ack_irq: generic_ack_irq, + device_interrupt: dp264_device_interrupt, + + init_arch: tsunami_init_arch, + init_irq: clipper_init_irq, + init_pit: generic_init_pit, + pci_fixup: clipper_pci_fixup, + kill_arch: generic_kill_arch, +}; +/* No alpha_mv alias for webbrick/monet/clipper, since we compile them + in unconditionally with DP264; setup_arch knows how to cope. */ diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index a84378926472..e75c37383649 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -42,6 +42,12 @@ #include "proto.h" #include "irq.h" +extern rwlock_t xtime_lock; +extern volatile unsigned long lost_ticks; /*kernel/sched.c*/ + +extern rwlock_t xtime_lock; +extern volatile unsigned long lost_ticks; /*kernel/sched.c*/ + static int set_rtc_mmss(unsigned long); @@ -86,15 +92,15 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) long nticks; #ifdef __SMP__ - extern void smp_percpu_timer_interrupt(struct pt_regs *); - extern unsigned int boot_cpu_id; - /* when SMP, do this for *all* CPUs, - but only do the rest for the boot CPU */ + /* When SMP, do this for *all* CPUs, but only do the rest for + the boot CPU. */ smp_percpu_timer_interrupt(regs); - if (smp_processor_id() != boot_cpu_id) - return; + if (smp_processor_id() != smp_boot_cpuid) + return; #endif + write_lock(&xtime_lock); + /* * Calculate how many ticks have passed since the last update, * including any previous partial leftover. Save any resulting @@ -124,6 +130,8 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) int tmp = set_rtc_mmss(xtime.tv_sec); state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0); } + + write_unlock(&xtime_lock); } /* @@ -226,7 +234,8 @@ time_init(void) { void (*irq_handler)(int, void *, struct pt_regs *); unsigned int year, mon, day, hour, min, sec, cc1, cc2; - unsigned long cycle_freq, diff, one_percent; + unsigned long cycle_freq, one_percent; + long diff; /* * The Linux interpretation of the CMOS clock register contents: @@ -242,7 +251,7 @@ time_init(void) if (!est_cycle_freq) { /* Sometimes the hwrpb->cycle_freq value is bogus. - Go another round to check up on it and see. */ + Go another round to check up on it and see. */ do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); cc2 = rpcc(); @@ -279,8 +288,7 @@ time_init(void) mon = CMOS_READ(RTC_MONTH); year = CMOS_READ(RTC_YEAR); - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(sec); BCD_TO_BIN(min); BCD_TO_BIN(hour); @@ -328,18 +336,24 @@ time_init(void) void do_gettimeofday(struct timeval *tv) { - unsigned long flags, delta_cycles, delta_usec; - unsigned long sec, usec; - __u32 now; - extern volatile unsigned long lost_ticks; /*kernel/sched.c*/ + unsigned long sec, usec, lost, flags; + unsigned long delta_cycles, delta_usec, partial_tick; - now = rpcc(); - save_and_cli(flags); + read_lock_irqsave(&xtime_lock, flags); + + delta_cycles = rpcc() - state.last_time; sec = xtime.tv_sec; usec = xtime.tv_usec; - delta_cycles = now - state.last_time; - restore_flags(flags); + partial_tick = state.partial_tick; + lost = lost_ticks; + + read_unlock_irqrestore(&xtime_lock, flags); +#ifdef __SMP__ + /* Until and unless we figure out how to get cpu cycle counters + in sync and keep them there, we can't use the rpcc tricks. */ + delta_usec = lost * (1000000 / HZ); +#else /* * usec = cycles * ticks_per_cycle * 2**48 * 1e6 / (2**48 * ticks) * = cycles * (s_t_p_c) * 1e6 / (2**48 * ticks) @@ -354,13 +368,10 @@ do_gettimeofday(struct timeval *tv) */ delta_usec = (delta_cycles * state.scaled_ticks_per_cycle - + state.partial_tick - + (lost_ticks << FIX_SHIFT) ) * 15625; + + partial_tick + + (lost << FIX_SHIFT)) * 15625; delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; - - /* the 'lost_tics' term above implements this: - * delta_usec += lost_ticks * (1000000 / HZ); - */ +#endif usec += delta_usec; if (usec >= 1000000) { @@ -375,13 +386,41 @@ do_gettimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv) { - cli(); - xtime = *tv; + unsigned long delta_usec; + long sec, usec; + + write_lock_irq(&xtime_lock); + + /* The offset that is added into time in do_gettimeofday above + must be subtracted out here to keep a coherent view of the + time. Without this, a full-tick error is possible. */ + +#ifdef __SMP__ + delta_usec = lost_ticks * (1000000 / HZ); +#else + delta_usec = rpcc() - state.last_time; + delta_usec = (delta_usec * state.scaled_ticks_per_cycle + + state.partial_tick + + (lost_ticks << FIX_SHIFT)) * 15625; + delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2; +#endif + + sec = tv->tv_sec; + usec = tv->tv_usec; + usec -= delta_usec; + if (usec < 0) { + usec += 1000000; + sec -= 1; + } + + xtime.tv_sec = sec; + xtime.tv_usec = usec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + + write_unlock_irq(&xtime_lock); } diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 2548f0914588..1b490b9c1fda 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -1,5 +1,5 @@ /* - * kernel/traps.c + * arch/alpha/kernel/traps.c * * (C) Copyright 1994 Linus Torvalds */ @@ -95,6 +95,9 @@ die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) { if (regs->ps & 8) return; +#ifdef __SMP__ + printk("CPU %d ", hard_smp_processor_id()); +#endif printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); dik_show_regs(regs, r9_15); dik_show_code((unsigned int *)regs->pc); @@ -128,8 +131,8 @@ do_entArith(unsigned long summary, unsigned long write_mask, if (summary & 1) { /* Software-completion summary bit is set, so try to emulate the instruction. */ - if (implver() == IMPLVER_EV6) { - /* Whee! EV6 has precice exceptions. */ + if (!amask(AMASK_PRECISE_TRAP)) { + /* 21264 (except pass 1) has precise exceptions. */ if (alpha_fp_emul(regs.pc - 4)) return; } else { @@ -138,14 +141,12 @@ do_entArith(unsigned long summary, unsigned long write_mask, } } - lock_kernel(); #if 0 printk("%s: arithmetic trap at %016lx: %02lx %016lx\n", current->comm, regs.pc, summary, write_mask); #endif die_if_kernel("Arithmetic fault", ®s, 0, 0); send_sig(SIGFPE, current, 1); - unlock_kernel(); } asmlinkage void @@ -235,10 +236,8 @@ do_entDbg(unsigned long type, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { - lock_kernel(); die_if_kernel("Instruction fault", ®s, type, 0); force_sig(SIGILL, current); - unlock_kernel(); } @@ -453,10 +452,8 @@ got_exception: unsigned long newpc; newpc = fixup_exception(una_reg, fixup, pc); - lock_kernel(); printk("Forwarding unaligned exception at %lx (%lx)\n", pc, newpc); - unlock_kernel(); (®s)->pc = newpc; return; @@ -610,11 +607,9 @@ do_entUnaUser(void * va, unsigned long opcode, cnt = 0; } if (++cnt < 5) { - lock_kernel(); printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n", current->comm, current->pid, regs->pc - 4, va, opcode, reg); - unlock_kernel(); } last_time = jiffies; } @@ -868,16 +863,12 @@ do_entUnaUser(void * va, unsigned long opcode, give_sigsegv: regs->pc -= 4; /* make pc point to faulting insn */ - lock_kernel(); send_sig(SIGSEGV, current, 1); - unlock_kernel(); return; give_sigbus: regs->pc -= 4; - lock_kernel(); send_sig(SIGBUS, current, 1); - unlock_kernel(); return; } diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 2e67deb8aa85..fc5a964bbbbe 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -256,26 +256,6 @@ paging_init(unsigned long start_mem, unsigned long end_mem) return start_mem; } -#ifdef __SMP__ -/* - * paging_init_secondary(), called ONLY by secondary CPUs, - * sets up current->tss contents appropriately and does a load_PCB. - * note that current should be pointing at the idle thread task struct - * for this CPU. - */ -void -paging_init_secondary(void) -{ - current->tss.ptbr = init_task.tss.ptbr; - current->tss.pal_flags = 1; - current->tss.flags = 0; - load_PCB(¤t->tss); - tbia(); - - return; -} -#endif /* __SMP__ */ - #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM) void srm_paging_stop (void) diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S index 879f0aa306a9..22e626d44ea5 100644 --- a/arch/i386/boot/video.S +++ b/arch/i386/boot/video.S @@ -1,14 +1,19 @@ ! -! Display adapter & video mode setup, version 2.12 (25-May-98) +! Display adapter & video mode setup, version 2.13 (14-May-99) ! -! Copyright (C) 1995 -- 1998 Martin Mares +! Copyright (C) 1995 -- 1999 Martin Mares ! Based on the original setup.S code (C) Linus Torvalds and Mats Anderson ! +! For further information, look at Documentation/svga.txt. +! #include /* for CONFIG_VIDEO_* */ ! Enable autodetection of SVGA adapters and modes. If you really need this -! feature, drop me a mail as I think of removing it some day... +! feature, drop me a mail as I think of removing it some day. You can +! always enter `scan' to get the video mode table and then use the real +! video mode numbers (those 4-digit hexadecimal numbers, NOT the menu +! item numbers) which don't rely on any autodetection. #undef CONFIG_VIDEO_SVGA ! Enable autodetection of VESA modes @@ -1939,7 +1944,7 @@ unknt: .ascii "Unknown mode ID. Try again." badmdt: .ascii "You passed an undefined mode number." db 0x0d, 0x0a, 0 vesaer: .ascii "Error: Scanning of VESA modes failed. Please " - .ascii "report to ." + .ascii "report to ." db 0x0d, 0x0a, 0 old_name: .ascii "CGA/MDA/HGA" db 0 diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 0d71d8bb50fb..0d6c177919c9 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -1,6 +1,6 @@ /* Generic MTRR (Memory Type Range Register) driver. - Copyright (C) 1997-1998 Richard Gooch + Copyright (C) 1997-1999 Richard Gooch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -196,6 +196,11 @@ 19990310 Richard Gooch Support K6-II/III based on Alan Cox's patches. v1.34 + 19990511 Bart Hartgers + Support Centaur C6 MCR's. + 19990512 Richard Gooch + Minor cleanups. + v1.35 */ #include #include @@ -232,7 +237,7 @@ #include #include "irq.h" -#define MTRR_VERSION "1.34 (19990310)" +#define MTRR_VERSION "1.35 (19990512)" #define TRUE 1 #define FALSE 0 @@ -313,8 +318,13 @@ static void set_mtrr_prepare (struct set_mtrr_context *ctxt) /* Disable interrupts locally */ __save_flags (ctxt->flags); __cli (); - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return; - + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_AMD: + case X86_VENDOR_CENTAUR: + return; + /*break;*/ + } /* Save value of CR4 and clear Page Global Enable (bit 7) */ if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) asm volatile ("movl %%cr4, %0\n\t" @@ -352,12 +362,14 @@ static void set_mtrr_done (struct set_mtrr_context *ctxt) { unsigned long tmp; - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + case X86_VENDOR_CENTAUR: __restore_flags (ctxt->flags); return; + /*break;*/ } - /* Flush caches and TLBs */ asm volatile ("wbinvd" : : : "memory" ); @@ -399,7 +411,9 @@ static unsigned int get_num_var_ranges (void) return (config & 0xff); /*break;*/ case X86_VENDOR_CYRIX: - /* Cyrix have 8 ARRs */ + /* Cyrix have 8 ARRs */ + case X86_VENDOR_CENTAUR: + /* and Centaur has 8 MCR's */ return 8; /*break;*/ case X86_VENDOR_AMD: @@ -422,6 +436,7 @@ static int have_wrcomb (void) /*break;*/ case X86_VENDOR_CYRIX: case X86_VENDOR_AMD: + case X86_VENDOR_CENTAUR: return 1; /*break;*/ } @@ -450,7 +465,6 @@ static void intel_get_mtrr (unsigned int reg, unsigned long *base, /* Clean up mask_lo so it gives the real address mask. */ mask_lo = (mask_lo & 0xfffff000UL); - /* This works correctly if size is a power of two, i.e. a contiguous range. */ *size = ~(mask_lo - 1); @@ -480,7 +494,6 @@ static void cyrix_get_arr (unsigned int reg, unsigned long *base, /* Enable interrupts if it was enabled previously */ __restore_flags (flags); - shift = ((unsigned char *) base)[1] & 0x0f; *base &= 0xfffff000UL; @@ -550,6 +563,20 @@ static void amd_get_mtrr (unsigned int reg, unsigned long *base, return; } /* End Function amd_get_mtrr */ +static struct +{ + unsigned long high; + unsigned long low; +} centaur_mcr[8]; + +static void centaur_get_mcr (unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type *type) +{ + *base = centaur_mcr[reg].high & 0xfffff000; + *size = (~(centaur_mcr[reg].low & 0xfffff000))+1; + *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ +} /* End Function centaur_get_mcr */ + static void (*get_mtrr) (unsigned int reg, unsigned long *base, unsigned long *size, mtrr_type *type) = NULL; @@ -647,11 +674,10 @@ static void amd_set_mtrr_up (unsigned int reg, unsigned long base, else /* Set the register to the base (already shifted for us), the type (off by one) and an inverted bitmask of the size - The size is the only odd bit. We are fed say 512K We invert this and we get 111 1111 1111 1011 but if you subtract one and invert you get the desired - 111 1111 1111 1100 mask + 111 1111 1111 1100 mask */ *(reg ? &high : &low)=(((~(size-1))>>15)&0x0001FFFC)|base|(type+1); /* @@ -663,10 +689,36 @@ static void amd_set_mtrr_up (unsigned int reg, unsigned long base, if (do_safe) set_mtrr_done (&ctxt); } /* End Function amd_set_mtrr_up */ + +static void centaur_set_mcr_up (unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type, + int do_safe) +{ + struct set_mtrr_context ctxt; + unsigned long low, high; + + if (do_safe) set_mtrr_prepare( &ctxt ); + if (size == 0) + { + /* Disable */ + high = low = 0; + } + else + { + high = base & 0xfffff000; /* base works on 4K pages... */ + low = ((~(size-1))&0xfffff000); + low |= 0x1f; /* only support write-combining... */ + } + centaur_mcr[reg].high = high; + centaur_mcr[reg].low = low; + wrmsr (0x110 + reg, low, high); + if (do_safe) set_mtrr_done( &ctxt ); +} /* End Function centaur_set_mtrr_up */ + static void (*set_mtrr_up) (unsigned int reg, unsigned long base, unsigned long size, mtrr_type type, int do_safe) = NULL; - + #ifdef __SMP__ struct mtrr_var_range @@ -694,23 +746,21 @@ __initfunc(static int set_mtrr_var_range_testing (unsigned int index, { unsigned int lo, hi; int changed = FALSE; - - rdmsr(MTRRphysBase_MSR(index), lo, hi); + rdmsr(MTRRphysBase_MSR(index), lo, hi); if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) { - wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); changed = TRUE; } - rdmsr(MTRRphysMask_MSR(index), lo, hi); + rdmsr(MTRRphysMask_MSR(index), lo, hi); if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) { - wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); + wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); changed = TRUE; } - return changed; } /* End Function set_mtrr_var_range_testing */ @@ -723,7 +773,6 @@ __initfunc(static void get_fixed_ranges(mtrr_type *frs)) for (i = 0; i < 2; i++) rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); - for (i = 0; i < 8; i++) rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); } /* End Function get_fixed_ranges */ @@ -777,14 +826,13 @@ __initfunc(static void get_mtrr_state(struct mtrr_state *state)) unsigned long lo, dummy; nvrs = state->num_var_ranges = get_num_var_ranges(); - vrs = state->var_ranges + vrs = state->var_ranges = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); if (vrs == NULL) nvrs = state->num_var_ranges = 0; for (i = 0; i < nvrs; i++) get_mtrr_var_range (i, &vrs[i]); - get_fixed_ranges (state->fixed_ranges); rdmsr (MTRRdefType_MSR, lo, dummy); @@ -818,7 +866,6 @@ __initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, if ( set_fixed_ranges_testing(state->fixed_ranges) ) change_mask |= MTRR_CHANGE_MASK_FIXED; - /* Set_mtrr_restore restores the old value of MTRRdefType, so to set it we fiddle with the saved value */ if ((ctxt->deftype_lo & 0xff) != state->def_type @@ -831,7 +878,7 @@ __initfunc(static unsigned long set_mtrr_state (struct mtrr_state *state, return change_mask; } /* End Function set_mtrr_state */ - + static atomic_t undone_count; static volatile int wait_barrier_execute = FALSE; static volatile int wait_barrier_cache_enable = FALSE; @@ -1025,13 +1072,22 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, } /* Fall through */ case X86_VENDOR_CYRIX: + case X86_VENDOR_CENTAUR: if ( (base & 0xfff) || (size & 0xfff) ) { printk ("mtrr: size and base must be multiples of 4 kiB\n"); printk ("mtrr: size: %lx base: %lx\n", size, base); return -EINVAL; } - if (base + size < 0x100000) + if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) + { + if (type != MTRR_TYPE_WRCOMB) + { + printk ("mtrr: only write-combining is supported\n"); + return -EINVAL; + } + } + else if (base + size < 0x100000) { printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n", base, size); @@ -1050,7 +1106,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, } break; case X86_VENDOR_AMD: - /* Apply the K6 block alignment and size rules + /* Apply the K6 block alignment and size rules In order o Uncached or gathering only o 128K or bigger block @@ -1572,6 +1628,30 @@ __initfunc(static void cyrix_arr_init(void)) if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n"); } /* End Function cyrix_arr_init */ +__initfunc(static void centaur_mcr_init (void)) +{ + unsigned i; + struct set_mtrr_context ctxt; + + set_mtrr_prepare (&ctxt); + /* Unfortunately, MCR's are read-only, so there is no way to + * find out what the bios might have done. + */ + /* Clear all MCR's. + * This way we are sure that the centaur_mcr array contains the actual + * values. The disadvantage is that any BIOS tweaks are thus undone. + */ + for (i = 0; i < 8; ++i) + { + centaur_mcr[i].high = 0; + centaur_mcr[i].low = 0; + wrmsr (0x110 + i , 0, 0); + } + /* Throw the main write-combining switch... */ + wrmsr (0x120, 0x01f0001f, 0); + set_mtrr_done (&ctxt); +} /* End Function centaur_mcr_init */ + __initfunc(static void mtrr_setup (void)) { printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); @@ -1582,7 +1662,6 @@ __initfunc(static void mtrr_setup (void)) set_mtrr_up = intel_set_mtrr_up; break; case X86_VENDOR_CYRIX: - printk ("mtrr: Using Cyrix style ARRs\n"); get_mtrr = cyrix_get_arr; set_mtrr_up = cyrix_set_arr_up; get_free_region = cyrix_get_free_region; @@ -1591,6 +1670,10 @@ __initfunc(static void mtrr_setup (void)) get_mtrr = amd_get_mtrr; set_mtrr_up = amd_set_mtrr_up; break; + case X86_VENDOR_CENTAUR: + get_mtrr = centaur_get_mcr; + set_mtrr_up = centaur_set_mcr_up; + break; } } /* End Function mtrr_setup */ @@ -1611,6 +1694,9 @@ __initfunc(void mtrr_init_boot_cpu (void)) case X86_VENDOR_CYRIX: cyrix_arr_init (); break; + case X86_VENDOR_CENTAUR: + centaur_mcr_init (); + break; } } /* End Function mtrr_init_boot_cpu */ @@ -1675,6 +1761,9 @@ __initfunc(int mtrr_init(void)) case X86_VENDOR_CYRIX: cyrix_arr_init (); break; + case X86_VENDOR_CENTAUR: + centaur_mcr_init (); + break; } # endif /* !__SMP__ */ diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index af6df1065f31..be2e6e992fc5 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -9,6 +9,9 @@ * Force Cyrix 6x86(MX) and M II processors to report MTRR capability * and fix against Cyrix "coma bug" by * Zoltan Boszormenyi February 1999. + * + * Force Centaur C6 processors to report MTRR capability. + * Bart Hartgers , May 199. */ /* @@ -861,6 +864,8 @@ __initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) /* lv|=(1<<6); - may help too if the board can cope */ printk("now 0x%X", lv); wrmsr(0x107, lv, hv); + /* Emulate MTRRs using Centaur's MCR. */ + c->x86_capability |= X86_FEATURE_MTRR; } printk("\n"); } diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 086a473e392d..312a370a5d04 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.94 1998/10/12 06:15:04 jj Exp $ +/* $Id: sys_sunos.c,v 1.97 1999/05/24 19:40:39 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -1198,7 +1198,7 @@ asmlinkage int sunos_readv(unsigned long fd, const struct iovec * vector, long c lock_kernel(); ret = check_nonblock(sys_readv(fd,vector,count),fd); - lock_kernel(); + unlock_kernel(); return ret; } diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index a7f85ca5856a..0aec81915897 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.107 1999/03/05 13:21:02 davem Exp $ +/* $Id: sys_sparc32.c,v 1.108 1999/05/16 10:50:32 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -2363,6 +2363,94 @@ static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) __scm_destroy(scm); } +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) < ((unsigned long)kmsg->msg_control)) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) { struct socket *sock; @@ -2455,6 +2543,14 @@ asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int use if(scm.fp) __scm_destroy(&scm); } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + /* Wheee... */ if(sock->passcred) put_cmsg32(&kern_msg, @@ -2471,9 +2567,9 @@ asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int use if(uaddr != NULL && err >= 0) err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); if(cmsg_ptr != 0 && err >= 0) { - u32 ucmsg_ptr = ((u32)(unsigned long)kern_msg.msg_control); - err = __put_user(ucmsg_ptr, &user_msg->msg_control); - err |= __put_user(kern_msg.msg_controllen, &user_msg->msg_controllen); + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); } if(err >= 0) err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 82701cc9e489..0cce0a094362 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.22 1998/10/26 20:01:13 davem Exp $ +/* $Id: sys_sunos32.c,v 1.25 1999/05/24 19:40:44 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -1347,7 +1347,7 @@ asmlinkage int sunos_readv(u32 fd, u32 vector, s32 count) lock_kernel(); ret = check_nonblock(sys32_readv(fd, vector, count), fd); - lock_kernel(); + unlock_kernel(); return ret; } diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c index f0921ab9f3b9..c926f3a942e8 100644 --- a/arch/sparc64/solaris/fs.c +++ b/arch/sparc64/solaris/fs.c @@ -410,7 +410,11 @@ static int report_statvfs(struct inode *inode, u32 buf) mm_segment_t old_fs = get_fs(); int error; struct sol_statvfs *ss = (struct sol_statvfs *)A(buf); - + + if (!inode->i_sb) + return -ENODEV; + if (!inode->i_sb->s_op->statfs) + return -ENOSYS; set_fs (KERNEL_DS); error = inode->i_sb->s_op->statfs(inode->i_sb, &s, sizeof(struct statfs)); set_fs (old_fs); @@ -448,6 +452,10 @@ static int report_statvfs64(struct inode *inode, u32 buf) int error; struct sol_statvfs64 *ss = (struct sol_statvfs64 *)A(buf); + if (!inode->i_sb) + return -ENODEV; + if (!inode->i_sb->s_op->statfs) + return -ENOSYS; set_fs (KERNEL_DS); error = inode->i_sb->s_op->statfs(inode->i_sb, &s, sizeof(struct statfs)); set_fs (old_fs); @@ -489,9 +497,7 @@ asmlinkage int solaris_statvfs(u32 path, u32 buf) if (!IS_ERR(dentry)) { struct inode * inode = dentry->d_inode; - error = -ENOSYS; - if (inode->i_sb->s_op->statfs) - error = report_statvfs(inode, buf); + error = report_statvfs(inode, buf); dput(dentry); } unlock_kernel(); @@ -515,10 +521,6 @@ asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf) error = -ENOENT; else if (!(inode = dentry->d_inode)) error = -ENOENT; - else if (!inode->i_sb) - error = -ENODEV; - else if (!inode->i_sb->s_op->statfs) - error = -ENOSYS; else error = report_statvfs(inode, buf); fput(file); @@ -538,9 +540,7 @@ asmlinkage int solaris_statvfs64(u32 path, u32 buf) if (!IS_ERR(dentry)) { struct inode * inode = dentry->d_inode; - error = -ENOSYS; - if (inode->i_sb->s_op->statfs) - error = report_statvfs64(inode, buf); + error = report_statvfs64(inode, buf); dput(dentry); } unlock_kernel(); @@ -564,10 +564,6 @@ asmlinkage int solaris_fstatvfs64(unsigned int fd, u32 buf) error = -ENOENT; else if (!(inode = dentry->d_inode)) error = -ENOENT; - else if (!inode->i_sb) - error = -ENODEV; - else if (!inode->i_sb->s_op->statfs) - error = -ENOSYS; else error = report_statvfs64(inode, buf); fput(file); diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 7646c303044c..9cda5ccf7d60 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -926,6 +926,7 @@ static inline void do_special (ide_drive_t *drive) int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout) { byte stat; + int i; unsigned long flags; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ @@ -942,9 +943,18 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou } __restore_flags(flags); /* local CPU only */ } - udelay(1); /* allow status to settle, then read it again */ - if (OK_STAT((stat = GET_STAT()), good, bad)) - return 0; + /* + * Allow status to settle, then read it again. + * A few rare drives vastly violate the 400ns spec here, + * so we'll wait up to 10usec for a "good" status + * rather than expensively fail things immediately. + * This fix courtesy of Matthew Faupel & Niccolo Rigacci. + */ + for (i = 0; i < 10; i++) { + udelay(1); + if (OK_STAT((stat = GET_STAT()), good, bad)) + return 0; + } ide_error(drive, "status error", stat); return 1; } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 93ab0faa3f48..4e56a19a6995 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,7 +1,7 @@ #define BLOCKMOVE #define Z_WAKE static char rcsid[] = -"$Revision: 2.2.2.1 $$Date: 1999/04/08 16:17:43 $"; +"$Revision: 2.2.2.2 $$Date: 1999/05/21 17:18:15 $"; /* * linux/drivers/char/cyclades.c @@ -21,7 +21,7 @@ static char rcsid[] = * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, * and then fixed as suggested by Michael K. Johnson 12/12/92. * - * This version does not support shared irq's. + * This version supports shared IRQ's (only for PCI boards). * * This module exports the following rs232 io functions: * int cy_init(void); @@ -31,6 +31,17 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.2.2.2 1999/05/14 17:18:15 ivan + * /proc entry location changed to /proc/tty/driver/cyclades; + * Added support to shared IRQ's (only for PCI boards); + * Added support for Cobalt Qube2 systems; + * IRQ [de]allocation scheme revisited; + * BREAK implementation changed in order to make use of the 'break_ctl' + * TTY facility; + * Fixed typo in TTY structure field 'driver_name'; + * Included a PCI bridge reset and EEPROM reload in the board + * initialization code (for both Y and Z series). + * * Revision 2.2.2.1 1999/04/08 16:17:43 ivan * Fixed a bug in cy_wait_until_sent that was preventing the port to be * closed properly after a SIGINT; @@ -536,7 +547,7 @@ static char rcsid[] = #undef CY_16Y_HACK #undef CY_ENABLE_MONITORING #undef CY_PCI_DEBUG -#define CY_PROC +#undef CY_PROC #if 0 #define PAUSE __asm__("nop"); @@ -600,6 +611,14 @@ static char rcsid[] = #include #include +#ifdef CONFIG_COBALT_27 +#include +#include + +#define CACHED_TO_UNCACHED(x) (((unsigned long)(x) & \ + (unsigned long)0x1fffffff) + KSEG1) +#endif + #define cy_put_user put_user static unsigned long cy_get_user(unsigned long *addr) @@ -638,6 +657,7 @@ static DECLARE_TASK_QUEUE(tq_cyclades); static struct tty_driver cy_serial_driver, cy_callout_driver; static int serial_refcount; +#ifndef CONFIG_COBALT_27 static volatile int cy_irq_triggered; static volatile int cy_triggered; static int cy_wild_int_mask; @@ -665,6 +685,8 @@ static unsigned char *cy_isa_addresses[] = { }; #define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*)) +#endif /* CONFIG_COBALT_27 */ + /* This is the per-card data structure containing address, irq, number of channels, etc. This driver supports a maximum of NR_CARDS cards. */ @@ -681,11 +703,6 @@ static struct tty_struct *serial_table[NR_PORTS]; static struct termios *serial_termios[NR_PORTS]; static struct termios *serial_termios_locked[NR_PORTS]; -/* This is the per-irq data structure, - it maps an irq to the corresponding card */ - -static struct cyclades_card *IRQ_cards[NR_IRQS]; - /* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, @@ -790,7 +807,9 @@ static unsigned short cy_pci_dev_id[] = { static void cy_start(struct tty_struct *); static void set_line_char(struct cyclades_port *); +#ifndef CONFIG_COBALT_27 static void cy_probe(int, void *, struct pt_regs *); +#endif /* CONFIG_COBALT_27 */ static void cyz_poll(unsigned long); #ifdef CYCLOM_SHOW_STATUS static void show_status(int); @@ -959,6 +978,8 @@ cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index) return(0); } /* cyy_issue_cmd */ +#ifndef CONFIG_COBALT_27 /* ISA interrupt detection code */ + static int probe_ready; /* @@ -1149,6 +1170,8 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs) return; } /* cy_probe */ +#endif /* CONFIG_COBALT_27 */ + /* The real interrupt service routine is called whenever the card wants its hand held--chars received, out buffer empty, modem change, etc. @@ -1172,9 +1195,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) int mdm_change; int mdm_status; - if((cinfo = IRQ_cards[irq]) == 0){ + if((cinfo = (struct cyclades_card *)dev_id) == 0){ #ifdef CY_DEBUG_INTERRUPTS -printk("cy_interrupt: spurious interrupt %d\n\r", irq); + printk("cy_interrupt: spurious interrupt %d\n\r", irq); #endif return; /* spurious interrupt */ } @@ -1206,7 +1229,7 @@ printk("cy_interrupt: spurious interrupt %d\n\r", irq); } if (status & CySRReceive) { /* reception interrupt */ #ifdef CY_DEBUG_INTERRUPTS -printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); + printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); #endif /* determine the channel & change to that context */ save_xir = (u_char) cy_readb(base_addr+(CyRIR<x_char = 0; } - if (info->x_break){ - /* The Cirrus chip requires the "Embedded - Transmit Commands" of start break, delay, - and end break sequences to be sent. The - duration of the break is given in TICs, - which runs at HZ (typically 100) and the - PPR runs at 200 Hz, so the delay is - duration * 200/HZ, and thus a break can - run from 1/100 sec to about 5/4 sec. - For CD1400 J or later, replace the 200 Hz - by 500 Hz. - */ - /* start break */ - cy_writeb((u_long)base_addr + (CyTDR<chip_rev >= CD1400_REV_J ) { - /* It is a CD1400 rev. J or later */ - cy_writeb((u_long)base_addr + (CyTDR<x_break*500/HZ); - } else { - cy_writeb((u_long)base_addr + (CyTDR<x_break*200/HZ); + if (info->breakon || info->breakoff) { + if (info->breakon) { + cy_writeb((u_long)base_addr + (CyTDR<breakon = 0; + char_count -= 2; + } + if (info->breakoff) { + cy_writeb((u_long)base_addr + (CyTDR<breakoff = 0; + char_count -= 2; } - /* finish break */ - cy_writeb((u_long)base_addr + (CyTDR<x_break = 0; } while (char_count-- > 0){ @@ -1871,12 +1876,6 @@ cyz_poll(unsigned long arg) info->last_active = jiffies; info->jiffies[2] = jiffies; } - if (info->x_break){ - printk("cyc cyz_poll shouldn't see x_break\n"); - info->x_break = 0; - info->last_active = jiffies; - info->jiffies[2] = jiffies; - } #ifdef BLOCKMOVE while(0 < (small_count = cy_min((tx_bufsize - tx_put), @@ -1946,26 +1945,35 @@ static int startup(struct cyclades_port * info) { unsigned long flags; + int retval = 0; unsigned char *base_addr; int card,chip,channel,index; + unsigned long page; + + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); if (info->flags & ASYNC_INITIALIZED){ - return 0; + free_page(page); + goto errout; } if (!info->type){ if (info->tty){ set_bit(TTY_IO_ERROR, &info->tty->flags); } - return 0; - } - if (!info->xmit_buf){ - info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); - if (!info->xmit_buf){ - return -ENOMEM; - } + free_page(page); + goto errout; } + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; + set_line_char(info); card = info->card; @@ -1982,39 +1990,40 @@ startup(struct cyclades_port * info) card, chip, channel, (long)base_addr);/**/ #endif - save_flags(flags); cli(); - cy_writeb((ulong)base_addr+(CyCAR<default_timeout - ? info->default_timeout - : 0x02)); /* 10ms rx timeout */ + cy_writeb((ulong)base_addr+(CyRTPR<default_timeout + ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); - cy_writeb((ulong)base_addr+(CyCAR<flags |= ASYNC_INITIALIZED; + cy_writeb((u_long)base_addr+(CySRER<flags |= ASYNC_INITIALIZED; + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + info->breakon = info->breakoff = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; - if (info->tty){ - clear_bit(TTY_IO_ERROR, &info->tty->flags); - } - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; restore_flags(flags); + } else { struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; @@ -2022,6 +2031,8 @@ startup(struct cyclades_port * info) struct CH_CTRL *ch_ctrl; int retval; + restore_flags(flags); + base_addr = (unsigned char*) (cy_card[card].base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); @@ -2074,7 +2085,7 @@ startup(struct cyclades_port * info) clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - + info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); info->idle_stats.in_use = info->idle_stats.recv_idle = @@ -2085,6 +2096,10 @@ startup(struct cyclades_port * info) printk(" cyc startup done\n"); #endif return 0; + +errout: + restore_flags(flags); + return retval; } /* startup */ @@ -3763,36 +3778,62 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, return 0; } /* set_modem_info */ +/* + * cy_break() --- routine which turns the break handling on or off + */ static void -send_break( struct cyclades_port * info, int duration) +cy_break(struct tty_struct *tty, int break_state) { + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + if (serial_paranoia_check(info, tty->device, "cy_break")) + return; + + save_flags(flags); cli(); if (!IS_CYC_Z(cy_card[info->card])) { /* Let the transmit ISR take care of this (since it requires stuffing characters into the output stream). */ - info->x_break = duration; - if (!info->xmit_cnt ) { - start_xmit(info); + if (break_state == -1) { + if (!info->breakon) { + info->breakon = 1; + if (!info->xmit_cnt ) { + start_xmit(info); + } + } + } else { + if (!info->breakoff) { + info->breakoff = 1; + if (!info->xmit_cnt ) { + start_xmit(info); + } + } } } else { - /* For the moment we ignore the duration parameter!!! - A better implementation will use C_CM_SET_BREAK - and C_CM_CLR_BREAK with the appropriate delay. - */ -#if 1 -// this appears to wedge the output data stream -int retval; - retval = cyz_issue_cmd(&cy_card[info->card], + int retval; + + if (break_state == -1) { + retval = cyz_issue_cmd(&cy_card[info->card], (info->line) - (cy_card[info->card].first_line), - C_CM_SENDBRK, 0L); - if (retval != 0){ - printk("cyc:send_break retval at %d was %x\n", - __LINE__, retval); + C_CM_SET_BREAK, 0L); + if (retval != 0) { + printk("cyc:cy_break (set) retval at %d was %x\n", + __LINE__, retval); + } + } else { + retval = cyz_issue_cmd(&cy_card[info->card], + (info->line) - (cy_card[info->card].first_line), + C_CM_CLR_BREAK, 0L); + if (retval != 0) { + printk("cyc:cy_break (clr) retval at %d was %x\n", + __LINE__, retval); + } } -#endif } -} /* send_break */ + restore_flags(flags); + +} /* cy_break */ static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) @@ -4026,21 +4067,6 @@ cy_ioctl(struct tty_struct *tty, struct file * file, case CYGETWAIT: ret_val = info->closing_wait / (HZ/100); break; - case TCSBRK: /* SVID version: non-zero arg --> no break */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; - tty_wait_until_sent(tty,0); - if (!arg) - send_break(info, HZ/4); /* 1/4 second */ - break; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; - tty_wait_until_sent(tty,0); - send_break(info, arg ? arg*(HZ/10) : HZ/4); - break; case TIOCMGET: ret_val = get_modem_info(info, (unsigned int *) arg); break; @@ -4091,7 +4117,13 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios) tty->stopped = 0; cy_start(tty); } -#ifdef tytso_patch_94Nov25_1726 +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait); @@ -4100,16 +4132,6 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios) return; } /* cy_set_termios */ - -/* - * void (*set_ldisc)(struct tty_struct *tty); - * - * This routine allows the tty driver to be notified when the - * device's termios settings have changed. - * - */ - - /* This routine is called by the upper-layer tty layer to signal that incoming characters should be throttled because the input buffers are close to full. @@ -4467,6 +4489,7 @@ cyy_init_card(volatile ucchar *true_base_addr,int index)) return chip_number; } /* cyy_init_card */ +#ifndef CONFIG_COBALT_27 /* * --------------------------------------------------------------------- * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. @@ -4530,7 +4553,7 @@ cy_detect_isa(void)) /* allocate IRQ */ if(request_irq(cy_isa_irq, cyy_interrupt, - SA_INTERRUPT, "cyclomY", NULL)) + SA_INTERRUPT, "Cyclom-Y", &cy_card[j])) { printk("Cyclom-Y/ISA found at 0x%lx ", (unsigned long) cy_isa_address); @@ -4546,7 +4569,6 @@ cy_detect_isa(void)) cy_card[j].bus_index = 0; cy_card[j].first_line = cy_next_channel; cy_card[j].num_chips = cy_isa_nchan/4; - IRQ_cards[cy_isa_irq] = &cy_card[j]; nboard++; /* print message */ @@ -4561,6 +4583,20 @@ cy_detect_isa(void)) return(nboard); } /* cy_detect_isa */ +#endif /* CONFIG_COBALT_27 */ + +static void plx_init(uclong addr, uclong initctl) +{ + /* Reset PLX */ + cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x40000000); + udelay(100L); + cy_writel(addr + initctl, cy_readl(addr + initctl) & ~0x40000000); + + /* Reload Config. Registers from EEPROM */ + cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x20000000); + udelay(100L); + cy_writel(addr + initctl, cy_readl(addr + initctl) & ~0x20000000); +} /* * --------------------------------------------------------------------- @@ -4621,6 +4657,12 @@ cy_detect_pci(void)) cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK; cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; + if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) { + printk(" Warning: PCI I/O bit incorrectly set. " + "Ignoring it...\n"); + cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK; + } + #if defined(__alpha__) if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", @@ -4673,7 +4715,7 @@ cy_detect_pci(void)) /* allocate IRQ */ if(request_irq(cy_pci_irq, cyy_interrupt, - SA_INTERRUPT, "cyclomY", NULL)) + SA_SHIRQ, "Cyclom-Y", &cy_card[j])) { printk("Cyclom-Y/PCI found at 0x%lx ", (ulong) cy_pci_addr2); @@ -4689,13 +4731,14 @@ cy_detect_pci(void)) cy_card[j].bus_index = 1; cy_card[j].first_line = cy_next_channel; cy_card[j].num_chips = cy_pci_nchan/4; - IRQ_cards[cy_pci_irq] = &cy_card[j]; /* enable interrupts in the PCI interface */ plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f; switch (plx_ver) { case PLX_9050: + plx_init(cy_pci_addr0, 0x50); + cy_writew(cy_pci_addr0+0x4c, cy_readw(cy_pci_addr0+0x4c)|0x0040); break; @@ -4704,6 +4747,8 @@ cy_detect_pci(void)) case PLX_9080: default: /* Old boards, use PLX_9060 */ + plx_init(cy_pci_addr0, 0x6c); + cy_writew(cy_pci_addr0+0x68, cy_readw(cy_pci_addr0+0x68)|0x0900); break; @@ -4742,9 +4787,18 @@ cy_detect_pci(void)) #if !defined(__alpha__) cy_pci_addr0 = (ulong)ioremap(cy_pci_addr0, CyPCI_Zctl); #endif + + plx_init(cy_pci_addr0, 0x6c); + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0); cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; + + if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) { + printk(" Warning: PCI I/O bit incorrectly set. " + "Ignoring it...\n"); + cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK; + } if (mailbox == ZE_V1) { #if !defined(__alpha__) cy_pci_addr2 = (ulong)ioremap(cy_pci_addr2, CyPCI_Ze_win); @@ -4821,7 +4875,7 @@ cy_detect_pci(void)) /* allocate IRQ only if board has an IRQ */ if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { if(request_irq(cy_pci_irq,cyz_interrupt, - SA_INTERRUPT,"cyclomZ",NULL)) + SA_SHIRQ,"Cyclades-Z",&cy_card[j])) { printk("Could not allocate IRQ%d ", cy_pci_irq); @@ -4839,7 +4893,6 @@ cy_detect_pci(void)) cy_card[j].bus_index = 1; cy_card[j].first_line = cy_next_channel; cy_card[j].num_chips = -1; - IRQ_cards[cy_pci_irq] = &cy_card[j]; /* print message */ /* don't report IRQ if board is no IRQ */ @@ -4905,7 +4958,7 @@ cy_detect_pci(void)) /* allocate IRQ only if board has an IRQ */ if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { if(request_irq(cy_pci_irq,cyz_interrupt, - SA_INTERRUPT,"cyclomZ",NULL)) + SA_SHIRQ,"Cyclades-Z",&cy_card[j])) { printk("Could not allocate IRQ%d ", cy_pci_irq); @@ -4922,7 +4975,6 @@ cy_detect_pci(void)) cy_card[j].bus_index = 1; cy_card[j].first_line = cy_next_channel; cy_card[j].num_chips = -1; - IRQ_cards[cy_pci_irq] = &cy_card[j]; /* print message */ /* don't report IRQ if board is no IRQ */ @@ -4971,7 +5023,6 @@ show_version(void) __DATE__, __TIME__); } /* show_version */ -#ifdef CY_PROC static int cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, int *eof, void *data) @@ -5028,7 +5079,6 @@ done: len = 0; return len; } -#endif /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -5062,13 +5112,15 @@ cy_init(void)) struct proc_dir_entry *ent; #endif + init_bh(CYCLADES_BH, do_cyclades_bh); + show_version(); /* Initialize the tty_driver structure */ memset(&cy_serial_driver, 0, sizeof(struct tty_driver)); cy_serial_driver.magic = TTY_DRIVER_MAGIC; - cy_serial_driver.name = "cyclades"; + cy_serial_driver.driver_name = "cyclades"; cy_serial_driver.name = "ttyC"; cy_serial_driver.major = CYCLADES_MAJOR; cy_serial_driver.minor_start = 0; @@ -5083,6 +5135,7 @@ cy_init(void)) cy_serial_driver.table = serial_table; cy_serial_driver.termios = serial_termios; cy_serial_driver.termios_locked = serial_termios_locked; + cy_serial_driver.open = cy_open; cy_serial_driver.close = cy_close; cy_serial_driver.write = cy_write; @@ -5098,7 +5151,9 @@ cy_init(void)) cy_serial_driver.stop = cy_stop; cy_serial_driver.start = cy_start; cy_serial_driver.hangup = cy_hangup; + cy_serial_driver.break_ctl = cy_break; cy_serial_driver.wait_until_sent = cy_wait_until_sent; + cy_serial_driver.read_proc = cyclades_get_proc_info; /* * The callout device is just like normal device except for @@ -5117,12 +5172,6 @@ cy_init(void)) if (tty_register_driver(&cy_callout_driver)) panic("Couldn't register Cyclades callout driver\n"); - init_bh(CYCLADES_BH, do_cyclades_bh); - - for (i = 0; i < NR_IRQS; i++) { - IRQ_cards[i] = 0; - } - for (i = 0; i < NR_CARDS; i++) { /* base_addr=0 indicates board not found */ cy_card[i].base_addr = 0; @@ -5135,9 +5184,11 @@ cy_init(void)) availability of cy_card and cy_port data structures and updating the cy_next_channel. */ +#ifndef CONFIG_COBALT_27 /* look for isa boards */ cy_isa_nboard = cy_detect_isa(); - +#endif /* CONFIG_COBALT_27 */ + /* look for pci boards */ cy_pci_nboard = cy_detect_pci(); @@ -5323,6 +5374,7 @@ void cleanup_module(void) { int i; + int e1, e2; unsigned long flags; if (cyz_timeron){ @@ -5333,11 +5385,12 @@ cleanup_module(void) save_flags(flags); cli(); remove_bh(CYCLADES_BH); - free_page((unsigned long)tmp_buf); - if (tty_unregister_driver(&cy_callout_driver)) - printk("Couldn't unregister Cyclades callout driver\n"); - if (tty_unregister_driver(&cy_serial_driver)) - printk("Couldn't unregister Cyclades serial driver\n"); + if ((e1 = tty_unregister_driver(&cy_serial_driver))) + printk("cyc: failed to unregister Cyclades serial driver(%d)\n", + e1); + if ((e2 = tty_unregister_driver(&cy_callout_driver))) + printk("cyc: failed to unregister Cyclades callout driver (%d)\n", + e2); restore_flags(flags); @@ -5345,9 +5398,13 @@ cleanup_module(void) if (cy_card[i].base_addr != 0 && cy_card[i].irq) { - free_irq(cy_card[i].irq,NULL); + free_irq(cy_card[i].irq, &cy_card[i]); } } + if (tmp_buf) { + free_page((unsigned long) tmp_buf); + tmp_buf = NULL; + } #ifdef CY_PROC remove_proc_entry("cyclades", 0); #endif @@ -5358,6 +5415,7 @@ cleanup_module(void) void cy_setup(char *str, int *ints) { +#ifndef CONFIG_COBALT_27 int i, j; for (i = 0 ; i < NR_ISA_ADDRS ; i++) { @@ -5368,6 +5426,7 @@ cy_setup(char *str, int *ints) cy_isa_addresses[i++] = (unsigned char *)(ints[j]); } } +#endif /* CONFIG_COBALT_27 */ } /* cy_setup */ #endif diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c index 8279c075b06e..33f873e7d2de 100644 --- a/drivers/misc/parport_pc.c +++ b/drivers/misc/parport_pc.c @@ -832,8 +832,11 @@ static int probe_one_port(unsigned long int base, int irq, int dma) * Put the ECP detected port in the more SPP like mode. */ parport_pc_write_econtrol(p, 0x0); - parport_pc_write_control(p, 0xc); + parport_pc_write_control(p, 0x8); parport_pc_write_data(p, 0); + udelay (50); + parport_pc_write_control(p, 0xc); + udelay (50); if (parport_probe_hook) (*parport_probe_hook)(p); diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c index 560af8eee800..ea3712c7e9da 100644 --- a/drivers/net/smc-ultra.c +++ b/drivers/net/smc-ultra.c @@ -483,9 +483,9 @@ cleanup_module(void) /* NB: ultra_close_card() does free_irq + irq2dev */ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; kfree(dev->priv); - dev->priv = NULL; release_region(ioaddr, ULTRA_IO_EXTENT); unregister_netdev(dev); + dev->priv = NULL; } } } diff --git a/drivers/scsi/README.st b/drivers/scsi/README.st index da8099937ccf..8537cf9bbf0e 100644 --- a/drivers/scsi/README.st +++ b/drivers/scsi/README.st @@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver. The driver is currently maintained by Kai M{kisara (email Kai.Makisara@metla.fi) -Last modified: Sun Jan 17 10:57:41 1999 by makisara@home +Last modified: Sun Apr 18 13:24:43 1999 by makisara@home BASICS @@ -349,15 +349,6 @@ MTIOCGET Returns some status information. is set if there is no tape in the drive. GMT_EOD means either end of recorded data or end of tape. GMT_EOT means end of tape. -The following ioctls use the structure mtlocation that contains both -the block number and the partition number. These ioctls are available -only for SCSI-2 tape drives and the block number is the -device-independent logical block number defined by the standard. - -MTGETLOC Returns the current block and partition number. -MTSETLOC Sets the tape to the block and partition specified by the - arguments. - MISCELLANEOUS COMPILE OPTIONS diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index d9647d6dc228..c25ff66025df 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -109,6 +109,7 @@ static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1 #define BLIST_SINGLELUN 0x10 #define BLIST_NOTQ 0x20 #define BLIST_SPARSELUN 0x40 +#define BLIST_MAX5LUN 0x80 /* * Data declarations. @@ -273,6 +274,7 @@ static struct dev_info device_list[] = {"INSITE","I325VM","*", BLIST_KEY}, {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN}, +{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN}, {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN}, @@ -932,6 +934,15 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun, *max_dev_lun = 8; return 1; } + + /* + * REGAL CDC-4X: avoid hang after LUN 4 + */ + if (bflags & BLIST_MAX5LUN) { + *max_dev_lun = 5; + return 1; + } + /* * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1 diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index b01ab7f1c029..541c10ada671 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -122,11 +122,9 @@ retry: if (!quiet) printk(KERN_ERR "sr%d: CDROM (ioctl) reports ILLEGAL " "REQUEST.\n", target); - if ((SCpnt->sense_buffer[12] == 0x20 || - SCpnt->sense_buffer[12] == 0x24) && + if (SCpnt->sense_buffer[12] == 0x20 && SCpnt->sense_buffer[13] == 0x00) { /* sense: Invalid command operation code */ - /* or Invalid field in cdb */ err = -EDRIVE_CANT_DO_THIS; } else { err = -EINVAL; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index c53e2e7908a2..bbef41553ce9 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -11,7 +11,7 @@ Copyright 1992 - 1999 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Sun Mar 7 09:03:17 1999 by makisara@home + Last modified: Tue May 18 09:29:52 1999 by makisara@home Some small formal changes - aeb, 950809 */ @@ -164,8 +164,6 @@ st_chk_result(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen); if (driver_byte(result) & DRIVER_SENSE) print_sense("st", SCpnt); - else - printk("\n"); } else #endif @@ -289,6 +287,7 @@ st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes, } else bp = (STp->buffer)->b_data; + SCpnt->cmd_len = 0; SCpnt->request.sem = &(STp->sem); SCpnt->request.rq_status = RQ_SCSI_BUSY; SCpnt->request.rq_dev = STp->devt; @@ -3380,7 +3379,6 @@ static int st_attach(Scsi_Device * SDp){ tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i); tpnt->dirty = 0; - tpnt->waiting = NULL; tpnt->in_use = 0; tpnt->drv_buffer = 1; /* Try buffering if no mode sense */ tpnt->restr_dma = (SDp->host)->unchecked_isa_dma; diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 2146e2b01ec8..2a033a8eefd6 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -65,7 +65,6 @@ typedef struct { typedef struct { kdev_t devt; unsigned capacity; - struct wait_queue * waiting; Scsi_Device* device; struct semaphore sem; ST_buffer * buffer; diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c index 7ad601b1ab07..1579fc23ad39 100644 --- a/drivers/scsi/sym53c8xx.c +++ b/drivers/scsi/sym53c8xx.c @@ -572,6 +572,8 @@ spinlock_t sym53c8xx_lock; #define remap_pci_mem(base, size) ((u_long) __va(base)) #define unmap_pci_mem(vaddr, size) #define pcivtobus(p) ((p) & pci_dvma_mask) +#elif defined(__alpha__) +#define pcivtobus(p) ((p) & 0xfffffffful) #else /* __sparc__ */ #define pcivtobus(p) (p) diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c index 1ce70dc7b28f..3cee2d23a9b9 100644 --- a/drivers/sound/es1370.c +++ b/drivers/sound/es1370.c @@ -33,8 +33,8 @@ * to make the card a four channel one: use dsp to output two * channels to LINE and dac to output the other two channels to * SPKR. Set the mixer to only output synth to SPKR. - * micz it looks like this changes the MIC input impedance. I don't know - * any detail though. + * micbias sets the +5V bias to the mic if using an electretmic. + * * * Note: sync mode is not yet supported (i.e. running dsp and dac from the same * clock source) @@ -92,6 +92,12 @@ * Alpha fixes reported by Peter Jones * Note: joystick address handling might still be wrong on archs * other than i386 + * 10.05.99 0.21 Added support for an electret mic for SB PCI64 + * to the Linux kernel sound driver. This mod also straighten + * out the question marks around the mic impedance setting + * (micz). From Kim.Berts@fisub.mail.abb.com + * 11.05.99 0.22 Implemented the IMIX call to mute recording monitor. + * Guenter Geiger * * some important things missing in Ensoniq documentation: * @@ -107,8 +113,8 @@ * The card uses a 22.5792 MHz crystal. * The LINEIN jack may be converted to an AOUT jack by * setting pin 47 (XCTL0) of the ES1370 to high. - * Pin 48 (XCTL1) of the ES1370 presumably changes the input impedance of the - * MIC jack. + * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic + * * */ @@ -190,7 +196,7 @@ static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; #define DAC2_DIVTOSR(x) (1411200/((x)+2)) #define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ -#define CTRL_XCTL1 0x40000000 /* ? mic impedance */ +#define CTRL_XCTL1 0x40000000 /* electret mic bias */ #define CTRL_OPEN 0x20000000 /* no function, can be read and written */ #define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ #define CTRL_SH_PCLKDIV 16 @@ -301,6 +307,7 @@ struct es1370_state { unsigned int recsrc; unsigned int modcnt; unsigned short micpreamp; + unsigned int imix; } mix; /* wave stuff */ @@ -839,7 +846,8 @@ static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long a return put_user(s->mix.recsrc, (int *)arg); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ - for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + val = SOUND_MASK_IMIX; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].avail) val |= 1 << i; return put_user(val, (int *)arg); @@ -858,6 +866,9 @@ static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long a case SOUND_MIXER_CAPS: return put_user(0, (int *)arg); + + case SOUND_MIXER_IMIX: + return put_user(s->mix.imix, (int *)arg); default: i = _IOC_NR(cmd); @@ -870,6 +881,14 @@ static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long a return -EINVAL; s->mix.modcnt++; switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_IMIX: + if (arg == 0) + return -EFAULT; + get_user_ret(s->mix.imix,(int *)arg, -EFAULT); + val = s->mix.recsrc; + /* fall through */ + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ get_user_ret(val, (int *)arg, -EFAULT); for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { @@ -886,7 +905,10 @@ static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long a wrcodec(s, 0x13, j & 0xaa); wrcodec(s, 0x14, (j >> 8) & 0x17); wrcodec(s, 0x15, (j >> 8) & 0x0f); - i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30; + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; + if (!s->mix.imix) { + i &= 0xff60; /* mute record and line monitor */ + } wrcodec(s, 0x10, i); wrcodec(s, 0x11, i >> 8); return 0; @@ -2262,7 +2284,7 @@ static int joystick[NR_DEVICE] = { 1, 0, }; static int joystick[NR_DEVICE] = { 0, }; #endif static int lineout[NR_DEVICE] = { 0, }; -static int micz[NR_DEVICE] = { 0, }; +static int micbias[NR_DEVICE] = { 0, }; /* --------------------------------------------------------------------- */ @@ -2295,7 +2317,7 @@ __initfunc(int init_es1370(void)) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.20 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.22 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2328,8 +2350,10 @@ __initfunc(int init_es1370(void)) goto err_irq; } /* initialize codec registers */ - s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); - if (joystick[index]) { + /* note: setting CTRL_SERR_DIS is reported to break + * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ + s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); + if (joystick[index]) { if (check_region(0x200, JOY_EXTENT)) printk(KERN_ERR "es1370: io port 0x200 in use\n"); else @@ -2337,7 +2361,7 @@ __initfunc(int init_es1370(void)) } if (lineout[index]) s->ctrl |= CTRL_XCTL0; - if (micz[index]) + if (micbias[index]) s->ctrl |= CTRL_XCTL1; s->sctrl = 0; printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n" @@ -2361,6 +2385,7 @@ __initfunc(int init_es1370(void)) wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ wrcodec(s, 0x18, 0); /* recording source is mixer */ wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ + s->mix.imix = 1; fs = get_fs(); set_fs(KERNEL_DS); val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; @@ -2403,8 +2428,8 @@ MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); -MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i"); -MODULE_PARM_DESC(micz, "changes (??) the microphone impedance"); +MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c index b3e56f3228ac..77f4c3e101a1 100644 --- a/drivers/video/fbcon.c +++ b/drivers/video/fbcon.c @@ -2300,3 +2300,4 @@ struct display_switch fbcon_dummy = { EXPORT_SYMBOL(fb_display); EXPORT_SYMBOL(fbcon_redraw_bmove); EXPORT_SYMBOL(fbcon_dummy); +EXPORT_SYMBOL(fb_con); diff --git a/drivers/video/mdacon.c b/drivers/video/mdacon.c index 90dc8568581c..ae32da083dca 100644 --- a/drivers/video/mdacon.c +++ b/drivers/video/mdacon.c @@ -597,7 +597,7 @@ __initfunc(void mda_console_init(void)) if (mda_first_vc > mda_last_vc) return; - take_over_console(&mda_con, mda_first_vc, mda_last_vc, 0); + take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0); } #ifdef MODULE diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 7c2d2259f6c5..738bb40b896a 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -138,9 +138,6 @@ int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp) struct super_block *sb; int i, size; - if (!inode) - return 0; - sb = inode->i_sb; size = 2048 >> sb->s_blocksize_bits; diff --git a/fs/adfs/namei.c b/fs/adfs/namei.c index df3b5e457add..4e41c0975c17 100644 --- a/fs/adfs/namei.c +++ b/fs/adfs/namei.c @@ -46,9 +46,6 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name unsigned long parent_object_id, dir_object_id; int buffers, pos; - if (!S_ISDIR(dir->i_mode)) - return 0; - sb = dir->i_sb; if (adfs_inode_validate (dir)) { @@ -57,9 +54,6 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name return 0; } - if (namelen > ADFS_NAME_LEN) - return 0; - if (!(buffers = adfs_dir_read (dir, bh))) { adfs_error (sb, "adfs_find_entry", "unable to read directory"); return 0; diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index d6944e889d4f..425df6577342 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -16,8 +16,6 @@ static int autofs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode=filp->f_dentry->d_inode; - if (!inode || !S_ISDIR(inode->i_mode)) - return -ENOTDIR; switch((unsigned long) filp->f_pos) { diff --git a/fs/autofs/root.c b/fs/autofs/root.c index c0caee9df565..c1b57ec6e969 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -72,9 +72,6 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi struct inode * inode = filp->f_dentry->d_inode; off_t onr, nr; - if (!inode || !S_ISDIR(inode->i_mode)) - return -ENOTDIR; - sbi = autofs_sbi(inode->i_sb); dirhash = &sbi->dirhash; nr = filp->f_pos; diff --git a/fs/block_dev.c b/fs/block_dev.c index 11b5d02d2c84..13b3f534debc 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -273,6 +273,8 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) if (++bhe == &buflist[NBUF]) bhe = buflist; } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); + if (bhe == bhb && !blocks) + break; } while (left > 0); /* Release the read-ahead blocks */ diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 4a8c4a7d6a52..6a7fb92f7329 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -869,7 +869,8 @@ int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX) + if (!new_inode && new_dir!=old_dir && + new_dir->i_nlink >= EXT2_LINK_MAX) goto end_rename; } if (!new_bh) { diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index b393fd28aec7..eb253809c66d 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -407,7 +407,8 @@ void ext2_truncate (struct inode * inode) break; if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) ext2_sync_inode (inode); - current->counter = 0; + run_task_queue(&tq_disk); + current->policy |= SCHED_YIELD; schedule (); } /* diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index 0ab81d966332..7711ef2594ff 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -237,10 +237,6 @@ static int cap_readdir(struct file * filp, struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - return -EBADF; - } - entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_CAP_RDIR); diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 80e990627351..66d60fe8a81e 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -202,10 +202,6 @@ static int dbl_readdir(struct file * filp, struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - return -EBADF; - } - entry = HFS_I(dir)->entry; if (filp->f_pos == 0) { diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index 5cff9d814263..b5f7c9f5c47c 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -225,10 +225,6 @@ static int nat_readdir(struct file * filp, struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - return -EBADF; - } - entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_NAT_HDIR); diff --git a/fs/minix/namei.c b/fs/minix/namei.c index e6d680ecf3e0..8a855438d264 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -45,8 +45,6 @@ static struct buffer_head * minix_find_entry(struct inode * dir, struct minix_dir_entry *de; *res_dir = NULL; - if (!dir->i_sb) - return NULL; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE @@ -161,8 +159,6 @@ static int minix_add_entry(struct inode * dir, *res_buf = NULL; *res_dir = NULL; - if (!dir || !dir->i_sb) - return -ENOENT; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE @@ -342,8 +338,6 @@ static int empty_dir(struct inode * inode) struct minix_dir_entry * de; struct minix_sb_info * info; - if (!inode || !inode->i_sb) - return 1; info = &inode->i_sb->u.minix_sb; block = 0; bh = NULL; @@ -442,26 +436,12 @@ int minix_unlink(struct inode * dir, struct dentry *dentry) struct buffer_head * bh; struct minix_dir_entry * de; -repeat: retval = -ENOENT; - inode = NULL; + inode = dentry->d_inode; bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); - if (!bh) + if (!bh || de->inode != inode->i_ino) goto end_unlink; - inode = dentry->d_inode; - - retval = -EPERM; - if (de->inode != inode->i_ino) { - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } - if (de->inode != inode->i_ino) { - retval = -ENOENT; - goto end_unlink; - } if (!inode->i_nlink) { printk("Deleting nonexistent file (%s:%lu), %d\n", kdevname(inode->i_dev), @@ -562,12 +542,6 @@ int minix_link(struct dentry * old_dentry, struct inode * dir, (((struct minix_dir_entry *) ((buffer)+info->s_dirsize))->inode) /* - * rename uses retrying to avoid race-conditions: at least they should be minimal. - * it tries to allocate all the blocks, then sanity-checks, and if the sanity- - * checks fail, it tries to restart itself again. Very practical - no changes - * are done until we know everything works ok.. and then all the changes can be - * done in one fell swoop when we have claimed all the buffers needed. - * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ @@ -581,24 +555,15 @@ int minix_rename(struct inode * old_dir, struct dentry *old_dentry, int retval; info = &old_dir->i_sb->u.minix_sb; - goto start_up; -try_again: - brelse(old_bh); - brelse(new_bh); - brelse(dir_bh); - current->counter = 0; - schedule(); -start_up: - old_inode = new_inode = NULL; - old_bh = new_bh = dir_bh = NULL; + new_bh = dir_bh = NULL; + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; old_bh = minix_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; - if (!old_bh) + if (!old_bh || old_de->inode != old_inode->i_ino) goto end_rename; - old_inode = old_dentry->d_inode; retval = -EPERM; - new_inode = new_dentry->d_inode; new_bh = minix_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { @@ -620,7 +585,8 @@ start_up: if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= info->s_link_max) + if (!new_inode && new_dir != old_dir && + new_dir->i_nlink >= info->s_link_max) goto end_rename; } if (!new_bh) { @@ -631,22 +597,15 @@ start_up: if (retval) goto end_rename; } -/* sanity checking before doing the rename - avoid races */ - if (new_inode && (new_de->inode != new_inode->i_ino)) - goto try_again; - if (new_de->inode && !new_inode) - goto try_again; - if (old_de->inode != old_inode->i_ino) - goto try_again; /* ok, that's it */ - old_de->inode = 0; new_de->inode = old_inode->i_ino; + old_de->inode = 0; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(old_dir); old_dir->i_version = ++event; + mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(new_dir); new_dir->i_version = ++event; + mark_inode_dirty(new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index cacc0d5c5bb2..9f862c47ab72 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -38,8 +38,7 @@ static int c_size; static int c_seen_eof; static int c_last_returned_index; static struct ncp_dirent *c_entry = NULL; -static int c_lock = 0; -static struct wait_queue *c_wait = NULL; +static struct semaphore c_sem = MUTEX; static int ncp_read_volume_list(struct ncp_server *, int, int, struct ncp_dirent *); @@ -230,15 +229,12 @@ static inline int ncp_is_server_root(struct inode *inode) static inline void ncp_lock_dircache(void) { - while (c_lock) - sleep_on(&c_wait); - c_lock = 1; + down(&c_sem); } static inline void ncp_unlock_dircache(void) { - c_lock = 0; - wake_up(&c_wait); + up(&c_sem); } @@ -354,16 +350,7 @@ ncp_lookup_validate(struct dentry * dentry, int flags) int len = dentry->d_name.len; struct ncpfs_inode_info finfo; __u8 __name[dentry->d_name.len + 1]; - - if (!dentry->d_inode) { - DPRINTK(KERN_DEBUG "ncp_lookup_validate: called with dentry->d_inode already NULL.\n"); - return 0; - } - if (!dir || !S_ISDIR(dir->i_mode)) { - printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n"); - goto finished; - } server = NCP_SERVER(dir); if (!ncp_conn_valid(server)) diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 50d91a2b290c..d42ae0a5b645 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -100,6 +100,8 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) size_t already_read = 0; off_t pos; int bufsize, error; + void* freepage; + int freelen; DPRINTK(KERN_DEBUG "ncp_file_read: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -135,16 +137,24 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) bufsize = NCP_SERVER(inode)->buffer_size; + error = -EIO; + freelen = ncp_read_bounce_size(bufsize); + freepage = kmalloc(freelen, GFP_NFS); + if (!freepage) + goto out; + error = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { int read_this_time; int to_read = min(bufsize - (pos % bufsize), count - already_read); - error = ncp_read(NCP_SERVER(inode), + error = ncp_read_bounce(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_read, buf, &read_this_time); + pos, to_read, buf, &read_this_time, + freepage, freelen); if (error) { + kfree(freepage); error = -EIO; /* This is not exact, i know.. */ goto out; } @@ -156,6 +166,7 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) break; } } + kfree(freepage); file->f_pos = pos; @@ -177,6 +188,7 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) size_t already_written = 0; off_t pos; int bufsize, errno; + void* bouncebuffer; DPRINTK(KERN_DEBUG "ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -210,14 +222,23 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) already_written = 0; + bouncebuffer = kmalloc(bufsize, GFP_NFS); + if (!bouncebuffer) + return -EIO; /* -ENOMEM */ while (already_written < count) { int written_this_time; int to_write = min(bufsize - (pos % bufsize), count - already_written); - if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_write, buf, &written_this_time) != 0) { - return -EIO; + if (copy_from_user(bouncebuffer, buf, to_write)) { + errno = -EFAULT; + break; + } + if (ncp_write_kernel(NCP_SERVER(inode), + NCP_FINFO(inode)->file_handle, + pos, to_write, buf, &written_this_time) != 0) { + errno = -EIO; + break; } pos += written_this_time; buf += written_this_time; @@ -227,7 +248,7 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) break; } } - + kfree(bouncebuffer); inode->i_mtime = inode->i_atime = CURRENT_TIME; file->f_pos = pos; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 1afee6c7e98c..0d216ae8bcdc 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -346,11 +346,12 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) GFP_KERNEL); if (server == NULL) goto out_no_server; + memset(server, 0, sizeof(*server)); NCP_SBP(sb) = server; server->ncp_filp = ncp_filp; server->lock = 0; - server->wait = NULL; + sema_init(&server->sem, 1); server->packet = NULL; server->buffer_size = 0; server->conn_status = 0; @@ -687,7 +688,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) if ((result = ncp_make_open(inode, O_RDWR)) < 0) { return -EACCES; } - ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, + ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 8ada3752bb72..a7f767e38acc 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -33,6 +33,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, int result; struct ncp_ioctl_request request; struct ncp_fs_info info; + char* bouncebuffer; #ifdef NCP_IOC_GETMOUNTUID_INT /* remove after ncpfs-2.0.13/2.2.0 gets released */ @@ -57,12 +58,9 @@ int ncp_ioctl(struct inode *inode, struct file *filp, && (current->uid != server->m.mounted_uid)) { return -EACCES; } - if ((result = verify_area(VERIFY_READ, (char *) arg, - sizeof(request))) != 0) { - return result; - } - copy_from_user(&request, (struct ncp_ioctl_request *) arg, - sizeof(request)); + if (copy_from_user(&request, (struct ncp_ioctl_request *) arg, + sizeof(request))) + return -EFAULT; if ((request.function > 255) || (request.size > @@ -73,6 +71,13 @@ int ncp_ioctl(struct inode *inode, struct file *filp, NCP_PACKET_SIZE)) != 0) { return result; } + bouncebuffer = kmalloc(NCP_PACKET_SIZE, GFP_NFS); + if (!bouncebuffer) + return -ENOMEM; + if (copy_from_user(bouncebuffer, request.data, request.size)) { + kfree(bouncebuffer); + return -EFAULT; + } ncp_lock_server(server); /* FIXME: We hack around in the server's structures @@ -80,17 +85,22 @@ int ncp_ioctl(struct inode *inode, struct file *filp, server->has_subfunction = 0; server->current_size = request.size; - copy_from_user(server->packet, request.data, request.size); - - ncp_request(server, request.function); - - DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n", - server->reply_size); - copy_to_user(request.data, server->packet, server->reply_size); - + memcpy(server->packet, bouncebuffer, request.size); + + result = ncp_request2(server, request.function, + bouncebuffer, NCP_PACKET_SIZE); + if (result < 0) + result = -EIO; + else + result = server->reply_size; ncp_unlock_server(server); - - return server->reply_size; + DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n", + result); + if (result >= 0) + if (copy_to_user(request.data, bouncebuffer, result)) + result = -EFAULT; + kfree(bouncebuffer); + return result; case NCP_IOC_CONN_LOGGED_IN: diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 6b321e6c984a..c9254f4f67a7 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -37,11 +37,10 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; unsigned long page; - unsigned int clear; - unsigned long tmp; + unsigned int already_read; + unsigned int count; int bufsize; int pos; - mm_segment_t fs; page = __get_free_page(GFP_KERNEL); if (!page) @@ -49,35 +48,24 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, address &= PAGE_MASK; pos = address - area->vm_start + area->vm_offset; - clear = 0; + count = PAGE_SIZE; if (address + PAGE_SIZE > area->vm_end) { - clear = address + PAGE_SIZE - area->vm_end; + count = area->vm_end - address; } /* what we can read in one go */ bufsize = NCP_SERVER(inode)->buffer_size; - fs = get_fs(); - set_fs(get_ds()); - - if (ncp_make_open(inode, O_RDONLY) < 0) { - clear = PAGE_SIZE; - } else { - int already_read = 0; - int count = PAGE_SIZE - clear; - int to_read; - + already_read = 0; + if (ncp_make_open(inode, O_RDONLY) >= 0) { while (already_read < count) { int read_this_time; + int to_read; - if ((pos % bufsize) != 0) { - to_read = bufsize - (pos % bufsize); - } else { - to_read = bufsize; - } + to_read = bufsize - (pos % bufsize); to_read = min(to_read, count - already_read); - if (ncp_read(NCP_SERVER(inode), + if (ncp_read_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, (char *) (page + already_read), @@ -94,12 +82,9 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, } - set_fs(fs); - - tmp = page + PAGE_SIZE; - while (clear--) { - *(char *) --tmp = 0; - } + if (already_read < PAGE_SIZE) + memset((char*)(page + already_read), 0, + PAGE_SIZE - already_read); return page; } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index bb034a4e469a..8fc6913566ee 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -754,7 +754,7 @@ int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, /* We have to transfer to/from user space */ int -ncp_read(struct ncp_server *server, const char *file_id, +ncp_read_kernel(struct ncp_server *server, const char *file_id, __u32 offset, __u16 to_read, char *target, int *bytes_read) { char *source; @@ -772,18 +772,27 @@ ncp_read(struct ncp_server *server, const char *file_id, *bytes_read = ntohs(ncp_reply_word(server, 0)); source = ncp_reply_data(server, 2 + (offset & 1)); - result = -EFAULT; - if (!copy_to_user(target, source, *bytes_read)) - result = 0; + memcpy(target, source, *bytes_read); out: ncp_unlock_server(server); return result; } +/* There is a problem... egrep and some other silly tools do: + x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, , 32768); + read(, x, 32768); + Now copying read result by copy_to_user causes pagefault. This pagefault + could not be handled because of server was locked due to read. So we have + to use temporary buffer. So ncp_unlock_server must be done before + copy_to_user (and for write, copy_from_user must be done before + ncp_init_request... same applies for send raw packet ioctl). Because of + file is normally read in bigger chunks, caller provides kmalloced + (vmalloced) chunk of memory with size >= to_read... + */ int -ncp_write(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_write, - const char *source, int *bytes_written) +ncp_read_bounce(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_read, char *target, int *bytes_read, + void* bounce, __u32 bufsize) { int result; @@ -791,46 +800,47 @@ ncp_write(struct ncp_server *server, const char *file_id, ncp_add_byte(server, 0); ncp_add_mem(server, file_id, 6); ncp_add_dword(server, htonl(offset)); - ncp_add_word(server, htons(to_write)); - ncp_add_mem_fromfs(server, source, to_write); - - if ((result = ncp_request(server, 73)) != 0) - goto out; - *bytes_written = to_write; - result = 0; -out: + ncp_add_word(server, htons(to_read)); + result = ncp_request2(server, 72, bounce, bufsize); ncp_unlock_server(server); + if (!result) { + int len = be16_to_cpu(get_unaligned((__u16*)((char*)bounce + + sizeof(struct ncp_reply_header)))); + result = -EIO; + if (len <= to_read) { + char* source; + + source = (char*)bounce + + sizeof(struct ncp_reply_header) + 2 + + (offset & 1); + *bytes_read = len; + result = 0; + if (copy_to_user(target, source, len)) + result = -EFAULT; + } + } return result; } -#ifdef CONFIG_NCPFS_EXTRAS -int -ncp_read_kernel(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, char *target, int *bytes_read) { - int error; - mm_segment_t old_fs; - - old_fs = get_fs(); - set_fs(get_ds()); - error = ncp_read(server, file_id, offset, to_read, target, bytes_read); - set_fs(old_fs); - return error; -} - int ncp_write_kernel(struct ncp_server *server, const char *file_id, __u32 offset, __u16 to_write, - const char *source, int *bytes_written) { - int error; - mm_segment_t old_fs; + const char *source, int *bytes_written) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 0); + ncp_add_mem(server, file_id, 6); + ncp_add_dword(server, htonl(offset)); + ncp_add_word(server, htons(to_write)); + ncp_add_mem(server, source, to_write); - old_fs = get_fs(); - set_fs(get_ds()); - error = ncp_write(server, file_id, offset, to_write, source, bytes_written); - set_fs(old_fs); - return error; + if ((result = ncp_request(server, 73)) == 0) + *bytes_written = to_write; + ncp_unlock_server(server); + return result; } -#endif #ifdef CONFIG_NCPFS_IOCTL_LOCKING int @@ -877,3 +887,4 @@ ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id, } #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ + diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index cc1df189647a..057b068b80eb 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -32,20 +32,24 @@ #include #include +#define NCP_MIN_SYMLINK_SIZE 8 +#define NCP_MAX_SYMLINK_SIZE 512 + int ncp_negotiate_buffersize(struct ncp_server *, int, int *); int ncp_negotiate_size_and_options(struct ncp_server *server, int size, int options, int *ret_size, int *ret_options); int ncp_get_volume_info_with_number(struct ncp_server *, int, struct ncp_volume_info *); int ncp_close_file(struct ncp_server *, const char *); -int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *); -int ncp_write(struct ncp_server *, const char *, __u32, __u16, - const char *, int *); -#ifdef CONFIG_NCPFS_EXTRAS -int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *); +static inline int ncp_read_bounce_size(__u32 size) { + return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8; +}; +int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16, + char *, int *, void* bounce, __u32 bouncelen); +int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, + char *, int *); int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, const char *, int *); -#endif int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, struct nw_info_struct *target); diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index 865fc68a323a..0a293ca85388 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -83,7 +83,8 @@ static int _send(struct socket *sock, const void *buff, int len) #define NCP_SLACK_SPACE 1024 -static int do_ncp_rpc_call(struct ncp_server *server, int size) +static int do_ncp_rpc_call(struct ncp_server *server, int size, + struct ncp_reply_header* reply_buf, int max_reply_size) { struct file *file; struct inode *inode; @@ -276,7 +277,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) * we have the correct reply, so read into the correct place and * return it */ - result = _recv(sock, (void *) start, server->packet_size, MSG_DONTWAIT); + result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT); if (result < 0) { printk(KERN_WARNING "NCP: notice message: result=%d\n", result); } else if (result < sizeof(struct ncp_reply_header)) { @@ -299,7 +300,8 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) * We need the server to be locked here, so check! */ -static int ncp_do_request(struct ncp_server *server, int size) +static int ncp_do_request(struct ncp_server *server, int size, + void* reply, int max_reply_size) { int result; @@ -316,7 +318,7 @@ static int ncp_do_request(struct ncp_server *server, int size) sign_packet(server, &size); } #endif /* CONFIG_NCPFS_PACKET_SIGNING */ - result = do_ncp_rpc_call(server, size); + result = do_ncp_rpc_call(server, size, reply, max_reply_size); DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result); @@ -332,10 +334,11 @@ static int ncp_do_request(struct ncp_server *server, int size) * received. It assumes that server->current_size contains the ncp * request size */ -int ncp_request(struct ncp_server *server, int function) +int ncp_request2(struct ncp_server *server, int function, + void* rpl, int size) { struct ncp_request_header *h; - struct ncp_reply_header *reply; + struct ncp_reply_header* reply = rpl; int request_size = server->current_size - sizeof(struct ncp_request_header); int result; @@ -357,12 +360,11 @@ int ncp_request(struct ncp_server *server, int function) h->task = 2; /* (current->pid) & 0xff; */ h->function = function; - result = ncp_do_request(server, request_size + sizeof(*h)); + result = ncp_do_request(server, request_size + sizeof(*h), reply, size); if (result < 0) { DPRINTK(KERN_WARNING "ncp_request_error: %d\n", result); goto out; } - reply = (struct ncp_reply_header *) (server->packet); server->completion = reply->completion_code; server->conn_status = reply->connection_state; server->reply_size = result; @@ -393,7 +395,7 @@ int ncp_connect(struct ncp_server *server) h->task = 2; /* see above */ h->function = 0; - result = ncp_do_request(server, sizeof(*h)); + result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); if (result < 0) goto out; server->sequence = 0; @@ -417,7 +419,7 @@ int ncp_disconnect(struct ncp_server *server) h->task = 2; /* see above */ h->function = 0; - return ncp_do_request(server, sizeof(*h)); + return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); } void ncp_lock_server(struct ncp_server *server) @@ -428,16 +430,18 @@ void ncp_lock_server(struct ncp_server *server) DPRINTK(KERN_WARNING "ncpfs: server locked!!!\n"); } #endif - while (server->lock) - sleep_on(&server->wait); + down(&server->sem); + if (server->lock) + printk(KERN_WARNING "ncp_lock_server: was locked!\n"); server->lock = 1; } void ncp_unlock_server(struct ncp_server *server) { - if (server->lock != 1) { + if (!server->lock) { printk(KERN_WARNING "ncp_unlock_server: was not locked!\n"); + return; } server->lock = 0; - wake_up(&server->wait); + up(&server->sem); } diff --git a/fs/select.c b/fs/select.c index a89425503784..1ba9d9be440c 100644 --- a/fs/select.c +++ b/fs/select.c @@ -268,8 +268,12 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) } ret = -EINVAL; - if (n < 0 || n > KFDS_NR) + if (n < 0) goto out_nofds; + + if (n > KFDS_NR) + n = KFDS_NR; + /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), * since we used fdset we need to allocate memory in units of diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 1a278911ab55..f93de5ffbacf 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -36,6 +36,7 @@ static void smb_put_inode(struct inode *); static void smb_delete_inode(struct inode *); static void smb_put_super(struct super_block *); static int smb_statfs(struct super_block *, struct statfs *, int); +static void smb_set_inode_attr(struct inode *, struct smb_fattr *); static struct super_operations smb_sops = { @@ -67,9 +68,7 @@ smb_invent_inos(unsigned long n) return ino; } -static struct smb_fattr *read_fattr = NULL; -static struct semaphore read_semaphore = MUTEX; - +/* We are always generating a new inode here */ struct inode * smb_iget(struct super_block *sb, struct smb_fattr *fattr) { @@ -77,11 +76,19 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr) pr_debug("smb_iget: %p\n", fattr); - down(&read_semaphore); - read_fattr = fattr; - result = iget(sb, fattr->f_ino); - read_fattr = NULL; - up(&read_semaphore); + result = get_empty_inode(); + result->i_sb = sb; + result->i_dev = sb->s_dev; + result->i_ino = fattr->f_ino; + memset(&(result->u.smbfs_i), 0, sizeof(result->u.smbfs_i)); + smb_set_inode_attr(result, fattr); + if (S_ISREG(result->i_mode)) + result->i_op = &smb_file_inode_operations; + else if (S_ISDIR(result->i_mode)) + result->i_op = &smb_dir_inode_operations; + else + result->i_op = NULL; + insert_inode_hash(result); return result; } @@ -147,24 +154,9 @@ smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) static void smb_read_inode(struct inode *inode) { - pr_debug("smb_iget: %p\n", read_fattr); - - if (!read_fattr || inode->i_ino != read_fattr->f_ino) - { - printk("smb_read_inode called from invalid point\n"); - return; - } - - inode->i_dev = inode->i_sb->s_dev; - memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i)); - smb_set_inode_attr(inode, read_fattr); - - if (S_ISREG(inode->i_mode)) - inode->i_op = &smb_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) - inode->i_op = &smb_dir_inode_operations; - else - inode->i_op = NULL; + /* Now it can be called only by NFS */ + printk("smb_read_inode called from invalid point\n"); + return; } /* diff --git a/fs/super.c b/fs/super.c index 690807a26079..558aa20736c5 100644 --- a/fs/super.c +++ b/fs/super.c @@ -952,16 +952,19 @@ static int do_remount(const char *dir,int flags,char *data) if (!IS_ERR(dentry)) { struct super_block * sb = dentry->d_inode->i_sb; - retval = -EINVAL; - if (dentry == sb->s_root) { - /* - * Shrink the dcache and sync the device. - */ - shrink_dcache_sb(sb); - fsync_dev(sb->s_dev); - if (flags & MS_RDONLY) - acct_auto_close(sb->s_dev); - retval = do_remount_sb(sb, flags, data); + retval = -ENODEV; + if (sb) { + retval = -EINVAL; + if (dentry == sb->s_root) { + /* + * Shrink the dcache and sync the device. + */ + shrink_dcache_sb(sb); + fsync_dev(sb->s_dev); + if (flags & MS_RDONLY) + acct_auto_close(sb->s_dev); + retval = do_remount_sb(sb, flags, data); + } } dput(dentry); } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 8cea266a8ee5..f1e2910e0a6d 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -144,8 +144,6 @@ static int sysv_add_entry(struct inode * dir, *res_buf = NULL; *res_dir = NULL; - if (!dir) - return -ENOENT; sb = dir->i_sb; if (namelen > SYSV_NAMELEN) { if (sb->sv_truncate) @@ -334,8 +332,6 @@ static int empty_dir(struct inode * inode) struct buffer_head * bh; struct sysv_dir_entry * de; - if (!inode) - return 1; block = 0; bh = NULL; pos = offset = 2*SYSV_DIRSIZE; @@ -391,22 +387,16 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry) struct buffer_head * bh; struct sysv_dir_entry * de; - inode = NULL; - bh = sysv_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); + inode = dentry->d_inode; + bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); retval = -ENOENT; - if (!bh) + if (!bh || de->inode != inode->i_ino) goto end_rmdir; - inode = dentry->d_inode; if (!empty_dir(inode)) { retval = -ENOTEMPTY; goto end_rmdir; } - if (de->inode != inode->i_ino) { - retval = -ENOENT; - goto end_rmdir; - } if (!list_empty(&dentry->d_hash)) { retval = -EBUSY; goto end_rmdir; @@ -416,9 +406,9 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry) de->inode = 0; mark_buffer_dirty(bh, 1); inode->i_nlink=0; - mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); mark_inode_dirty(dir); d_delete(dentry); retval = 0; @@ -434,26 +424,11 @@ int sysv_unlink(struct inode * dir, struct dentry * dentry) struct buffer_head * bh; struct sysv_dir_entry * de; -repeat: retval = -ENOENT; - inode = NULL; - bh = sysv_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); - if (!bh) - goto end_unlink; inode = dentry->d_inode; - - retval = -EPERM; - if (de->inode != inode->i_ino) { - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } - if (de->inode != inode->i_ino) { - retval = -ENOENT; + bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); + if (!bh || de->inode != inode->i_ino) goto end_unlink; - } if (!inode->i_nlink) { printk("Deleting nonexistent file (%s:%lu), %d\n", kdevname(inode->i_dev), inode->i_ino, inode->i_nlink); @@ -572,12 +547,6 @@ int sysv_link(struct dentry * old_dentry, struct inode * dir, (((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode) /* - * rename uses retrying to avoid race-conditions: at least they should be minimal. - * it tries to allocate all the blocks, then sanity-checks, and if the sanity- - * checks fail, it tries to restart itself again. Very practical - no changes - * are done until we know everything works ok.. and then all the changes can be - * done in one fell swoop when we have claimed all the buffers needed. - * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ @@ -589,24 +558,15 @@ int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, struct sysv_dir_entry * old_de, * new_de; int retval; - goto start_up; -try_again: - brelse(old_bh); - brelse(new_bh); - brelse(dir_bh); - current->counter = 0; - schedule(); -start_up: - old_inode = new_inode = NULL; - old_bh = new_bh = dir_bh = NULL; + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + new_bh = dir_bh = NULL; old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; - if (!old_bh) + if (!old_bh || old_de->inode != old_inode->i_ino) goto end_rename; - old_inode = old_dentry->d_inode; /* don't cross mnt-points */ retval = -EPERM; - new_inode = new_dentry->d_inode; new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { @@ -628,7 +588,8 @@ start_up: if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max) + if (!new_inode && new_dir != old_dir && + new_dir->i_nlink >= new_dir->i_sb->sv_link_max) goto end_rename; } if (!new_bh) { @@ -637,16 +598,8 @@ start_up: if (retval) goto end_rename; } -/* sanity checking before doing the rename - avoid races */ - if (new_inode && (new_de->inode != new_inode->i_ino)) - goto try_again; - if (new_de->inode && !new_inode) - goto try_again; - if (old_de->inode != old_inode->i_ino) - goto try_again; -/* ok, that's it */ - old_de->inode = 0; new_de->inode = old_inode->i_ino; + old_de->inode = 0; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; diff --git a/include/asm-alpha/atomic.h b/include/asm-alpha/atomic.h index 2dccf35217ec..67b74d3568d2 100644 --- a/include/asm-alpha/atomic.h +++ b/include/asm-alpha/atomic.h @@ -75,6 +75,7 @@ extern __inline__ long atomic_add_return(int i, atomic_t * v) " mov %0,%2\n" " stl_c %0,%1\n" " beq %0,2f\n" + " mb\n" ".section .text2,\"ax\"\n" "2: br 1b\n" ".previous" @@ -92,6 +93,7 @@ extern __inline__ long atomic_sub_return(int i, atomic_t * v) " mov %0,%2\n" " stl_c %0,%1\n" " beq %0,2f\n" + " mb\n" ".section .text2,\"ax\"\n" "2: br 1b\n" ".previous" diff --git a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h index b7c805511f82..c9e7e7aee9e8 100644 --- a/include/asm-alpha/bitops.h +++ b/include/asm-alpha/bitops.h @@ -90,6 +90,7 @@ extern __inline__ unsigned long test_and_set_bit(unsigned long nr, " xor %0,%3,%0\n" " stl_c %0,%1\n" " beq %0,3f\n" + " mb\n" "2:\n" ".section .text2,\"ax\"\n" "3: br 1b\n" @@ -114,6 +115,7 @@ extern __inline__ unsigned long test_and_clear_bit(unsigned long nr, " xor %0,%3,%0\n" " stl_c %0,%1\n" " beq %0,3f\n" + " mb\n" "2:\n" ".section .text2,\"ax\"\n" "3: br 1b\n" @@ -137,6 +139,7 @@ extern __inline__ unsigned long test_and_change_bit(unsigned long nr, " xor %0,%3,%0\n" " stl_c %0,%1\n" " beq %0,3f\n" + " mb\n" ".section .text2,\"ax\"\n" "3: br 1b\n" ".previous" @@ -172,7 +175,10 @@ extern inline unsigned long ffz_b(unsigned long x) extern inline unsigned long ffz(unsigned long word) { -#ifdef __alpha_cix__ +#if 0 && defined(__alpha_cix__) + /* Swine architects -- a year after they publish v3 of the + handbook, in the 21264 data sheet they quietly change CIX + to FIX and remove the spiffy counting instructions. */ /* Whee. EV6 can calculate it directly. */ unsigned long result; __asm__("ctlz %1,%0" : "=r"(result) : "r"(~word)); @@ -208,7 +214,10 @@ extern inline int ffs(int word) * of bits set) of a N-bit word */ -#ifdef __alpha_cix__ +#if 0 && defined(__alpha_cix__) +/* Swine architects -- a year after they publish v3 of the handbook, in + the 21264 data sheet they quietly change CIX to FIX and remove the + spiffy counting instructions. */ /* Whee. EV6 can calculate it directly. */ extern __inline__ unsigned long hweight64(unsigned long w) { diff --git a/include/asm-alpha/init.h b/include/asm-alpha/init.h index f4a08c9f2099..a85501cbbbbb 100644 --- a/include/asm-alpha/init.h +++ b/include/asm-alpha/init.h @@ -12,6 +12,6 @@ #define __FINIT .previous #define __INITDATA .section .data.init,"a" -#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES))) +#define __cacheline_aligned __attribute__((__aligned__(32))) #endif diff --git a/include/asm-alpha/irq.h b/include/asm-alpha/irq.h index fc9e8019a20d..ad1c917a36b6 100644 --- a/include/asm-alpha/irq.h +++ b/include/asm-alpha/irq.h @@ -92,8 +92,12 @@ static __inline__ int irq_cannonicalize(int irq) } extern void disable_irq(unsigned int); +extern void disable_irq_nosync(unsigned int); extern void enable_irq(unsigned int); +extern void irq_enter(int cpu, int irq); +extern void irq_exit(int cpu, int irq); + struct pt_regs; extern void (*perf_irq)(unsigned long, struct pt_regs *); diff --git a/include/asm-alpha/mmu_context.h b/include/asm-alpha/mmu_context.h index 03aa3c6d2861..aeec2c041b12 100644 --- a/include/asm-alpha/mmu_context.h +++ b/include/asm-alpha/mmu_context.h @@ -107,7 +107,8 @@ __EXTERN_INLINE void ev5_get_mmu_context(struct task_struct *p) if (mm) { unsigned long asn = asn_cache; - /* Check if our ASN is of an older version and thus invalid */ + /* Check if our ASN is of an older version, + or on a different CPU, and thus invalid */ if ((mm->context ^ asn) & ~HARDWARE_ASN_MASK) get_new_mmu_context(p, mm); } diff --git a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h index 04be8487db52..d53142bb11fd 100644 --- a/include/asm-alpha/smp.h +++ b/include/asm-alpha/smp.h @@ -4,22 +4,30 @@ #ifdef __SMP__ #include +#include #include struct cpuinfo_alpha { unsigned long loops_per_sec; - unsigned int next; unsigned long *pgd_cache; unsigned long *pte_cache; unsigned long pgtable_cache_sz; unsigned long ipi_count; -} __attribute__((aligned(32))); + unsigned long prof_multiplier; + unsigned long prof_counter; +} __cacheline_aligned; extern struct cpuinfo_alpha cpu_data[NR_CPUS]; #define PROC_CHANGE_PENALTY 20 -extern __volatile__ int cpu_number_map[NR_CPUS]; +/* Map from cpu id to sequential logical cpu number. This will only + not be idempotent when cpus failed to come on-line. */ +extern int cpu_number_map[NR_CPUS]; + +/* The reverse map from sequential logical cpu number to cpu id. */ +extern int __cpu_logical_map[NR_CPUS]; +#define cpu_logical_map(cpu) __cpu_logical_map[cpu] /* HACK: Cabrio WHAMI return value is bogus if more than 8 bits used.. :-( */ @@ -35,7 +43,6 @@ static __inline__ unsigned char hard_smp_processor_id(void) } #define smp_processor_id() (current->processor) -#define cpu_logical_map(cpu) (cpu) #endif /* __SMP__ */ diff --git a/include/asm-alpha/spinlock.h b/include/asm-alpha/spinlock.h index b5fe62ddf488..0a87b8af95a3 100644 --- a/include/asm-alpha/spinlock.h +++ b/include/asm-alpha/spinlock.h @@ -79,19 +79,20 @@ */ typedef struct { - volatile unsigned int lock; + volatile unsigned int lock /*__attribute__((aligned(32))) */; #if DEBUG_SPINLOCK - char debug_state, target_ipl, saved_ipl, on_cpu; + int on_cpu; + int line_no; void *previous; struct task_struct * task; + const char *base_file; #endif } spinlock_t; #if DEBUG_SPINLOCK -#define SPIN_LOCK_UNLOCKED (spinlock_t) {0, 1, 0, 0, 0, 0} +#define SPIN_LOCK_UNLOCKED (spinlock_t) {0, -1, 0, 0, 0, 0} #define spin_lock_init(x) \ - ((x)->lock = 0, (x)->target_ipl = 0, (x)->debug_state = 1, \ - (x)->previous = 0, (x)->task = 0) + ((x)->lock = 0, (x)->on_cpu = -1, (x)->previous = 0, (x)->task = 0) #else #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } #define spin_lock_init(x) ((x)->lock = 0) @@ -105,8 +106,11 @@ typedef struct { unsigned long a[100]; } __dummy_lock_t; #if DEBUG_SPINLOCK extern void spin_unlock(spinlock_t * lock); -extern void spin_lock(spinlock_t * lock); -extern int spin_trylock(spinlock_t * lock); +extern void debug_spin_lock(spinlock_t * lock, const char *, int); +extern int debug_spin_trylock(spinlock_t * lock, const char *, int); + +#define spin_lock(LOCK) debug_spin_lock(LOCK, __BASE_FILE__, __LINE__) +#define spin_trylock(LOCK) debug_spin_trylock(LOCK, __BASE_FILE__, __LINE__) #define spin_lock_own(LOCK, LOCATION) \ do { \ @@ -161,7 +165,9 @@ static inline void spin_lock(spinlock_t * lock) /***********************************************************/ -typedef struct { volatile int write_lock:1, read_counter:31; } rwlock_t; +typedef struct { + volatile int write_lock:1, read_counter:31; +} /*__attribute__((aligned(32)))*/ rwlock_t; #define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } diff --git a/include/asm-alpha/system.h b/include/asm-alpha/system.h index b37be73b2bea..2e8b4e0a1794 100644 --- a/include/asm-alpha/system.h +++ b/include/asm-alpha/system.h @@ -147,6 +147,20 @@ enum implver_enum { #endif #endif +enum amask_enum { + AMASK_BWX = (1UL << 0), + AMASK_FIX = (1UL << 1), + AMASK_MAX = (1UL << 8), + AMASK_PRECISE_TRAP = (1UL << 9), +}; + +enum amask_enum { + AMASK_BWX = (1UL << 0), + AMASK_FIX = (1UL << 1), + AMASK_MAX = (1UL << 8), + AMASK_PRECISE_TRAP = (1UL << 9), +}; + #define amask(mask) \ ({ unsigned long __amask, __input = (mask); \ __asm__ ("amask %1,%0" : "=r"(__amask) : "rI"(__input)); \ @@ -282,6 +296,7 @@ extern __inline__ unsigned long xchg_u32(volatile int *m, unsigned long val) " bis $31,%3,%1\n" " stl_c %1,%2\n" " beq %1,2f\n" + " mb\n" ".section .text2,\"ax\"\n" "2: br 1b\n" ".previous" @@ -300,6 +315,7 @@ extern __inline__ unsigned long xchg_u64(volatile long * m, unsigned long val) " bis $31,%3,%1\n" " stq_c %1,%2\n" " beq %1,2f\n" + " mb\n" ".section .text2,\"ax\"\n" "2: br 1b\n" ".previous" diff --git a/include/asm-arm/arch-ebsa285/irq.h b/include/asm-arm/arch-ebsa285/irq.h index d8f0ab21d8a0..ca1a55cdb4ae 100644 --- a/include/asm-arm/arch-ebsa285/irq.h +++ b/include/asm-arm/arch-ebsa285/irq.h @@ -10,6 +10,7 @@ * 26-Jan-1999 PJB Don't use IACK on CATS * 16-Mar-1999 RMK Added autodetect of ISA PICs */ +#include #include #include #include diff --git a/include/asm-arm/arch-ebsa285/memory.h b/include/asm-arm/arch-ebsa285/memory.h index 745750e3be21..a03cea639672 100644 --- a/include/asm-arm/arch-ebsa285/memory.h +++ b/include/asm-arm/arch-ebsa285/memory.h @@ -15,6 +15,8 @@ #ifndef __ASM_ARCH_MMU_H #define __ASM_ARCH_MMU_H +#include + #if defined(CONFIG_HOST_FOOTBRIDGE) /* diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 0f373ff5be09..9aed9f2d8f45 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -557,7 +557,8 @@ struct cyclades_port { unsigned long event; unsigned long last_active; int count; /* # of fd on device */ - int x_break; + int breakon; + int breakoff; int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ long pgrp; /* pgrp of opening process */ diff --git a/include/linux/ncp.h b/include/linux/ncp.h index 666ffff0fa59..229618db0652 100644 --- a/include/linux/ncp.h +++ b/include/linux/ncp.h @@ -72,9 +72,6 @@ struct ncp_volume_info { #define aDELETEINHIBIT (ntohl(1L<<(18-8))) #define aDONTCOMPRESS (nothl(1L<<(27-24))) -#define NCP_MIN_SYMLINK_SIZE 8 -#define NCP_MAX_SYMLINK_SIZE 512 - #define AR_READ (ntohs(0x0100)) #define AR_WRITE (ntohs(0x0200)) #define AR_EXCLUSIVE (ntohs(0x2000)) diff --git a/include/linux/ncp_fs.h b/include/linux/ncp_fs.h index b30fd061067e..9c5df53425d4 100644 --- a/include/linux/ncp_fs.h +++ b/include/linux/ncp_fs.h @@ -248,7 +248,11 @@ void ncp_date_unix2dos(int unix_date, __u16 * time, __u16 * date); int ncp_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* linux/fs/ncpfs/sock.c */ -int ncp_request(struct ncp_server *server, int function); +int ncp_request2(struct ncp_server *server, int function, + void* reply, int max_reply_size); +static int inline ncp_request(struct ncp_server *server, int function) { + return ncp_request2(server, function, server->packet, server->packet_size); +} int ncp_connect(struct ncp_server *server); int ncp_disconnect(struct ncp_server *server); void ncp_lock_server(struct ncp_server *server); diff --git a/include/linux/ncp_fs_sb.h b/include/linux/ncp_fs_sb.h index adf1eef660e6..43f902bed362 100644 --- a/include/linux/ncp_fs_sb.h +++ b/include/linux/ncp_fs_sb.h @@ -8,6 +8,7 @@ #ifndef _NCP_FS_SB #define _NCP_FS_SB +#include #include #include @@ -44,7 +45,7 @@ struct ncp_server { receive replies */ int lock; /* To prevent mismatch in protocols. */ - struct wait_queue *wait; + struct semaphore sem; int current_size; /* for packet preparation */ int has_subfunction; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 18e7fde28694..f2ef43b29349 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -405,7 +405,7 @@ extern __inline__ void dev_unlock_list(void) extern __inline__ void dev_lock_wait(void) { while (atomic_read(&dev_lockct)) { - current->counter = 0; + current->policy |= SCHED_YIELD; schedule(); } } diff --git a/net/core/filter.c b/net/core/filter.c index cc1ed83cd832..8e1ffb628e8d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -106,7 +106,7 @@ int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen) continue; case BPF_ALU|BPF_MUL|BPF_K: - A *= X; + A *= fentry->k; continue; case BPF_ALU|BPF_DIV|BPF_X: diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8c1c9f9be0a7..8beb05c81d73 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -896,6 +896,7 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg) err = -ERESTARTSYS; goto do_interrupted; } + tcp_push_pending_frames(sk, tp); wait_for_tcp_memory(sk); /* If SACK's were formed or PMTU events happened, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4a607a7492f9..9b4fecd6f1f8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.164 1999/05/08 21:09:52 davem Exp $ + * Version: $Id: tcp_input.c,v 1.165 1999/05/14 23:10:08 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -748,7 +748,6 @@ static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp, static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp) { struct sk_buff *skb = skb_peek(&sk->write_queue); - __u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when); /* Some data was ACK'd, if still retransmitting (due to a * timeout), resend more of the retransmit queue. The @@ -758,6 +757,9 @@ static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp) tcp_xmit_retransmit_queue(sk); tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } else { + __u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when); + if ((__s32)when < 0) + when = 1; tcp_reset_xmit_timer(sk, TIME_RETRANS, when); } } @@ -785,8 +787,6 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, if (after(ack, tp->snd_nxt) || before(ack, tp->snd_una)) goto uninteresting_ack; - dst_confirm(sk->dst_cache); - /* If there is data set flag 1 */ if (len != th->doff*4) { flag |= FLAG_DATA; @@ -882,6 +882,24 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, /* Clear any aborted fast retransmit starts. */ tp->dup_acks = 0; } + /* It is not a brain fart, I thought a bit now. 8) + * + * Forward progress is indicated, if: + * 1. the ack acknowledges new data. + * 2. or the ack is duplicate, but it is caused by new segment + * arrival. This case is filtered by: + * - it contains no data, syn or fin. + * - it does not update window. + * 3. or new SACK. It is difficult to check, so that we ignore it. + * + * Forward progress is also indicated by arrival new data, + * which was caused by window open from our side. This case is more + * difficult and it is made (alas, incorrectly) in tcp_data_queue(). + * --ANK (990513) + */ + if (ack != tp->snd_una || (flag == 0 && !th->fin)) + dst_confirm(sk->dst_cache); + /* Remember the highest ack received. */ tp->snd_una = ack; return 1; @@ -2067,21 +2085,81 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * not be in line code. [AC] */ if(th->ack) { - tp->snd_wl1 = TCP_SKB_CB(skb)->seq; - - /* We got an ack, but it's not a good ack. */ - if(!tcp_ack(sk,th, TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(skb)->ack_seq, len)) + /* rfc793: + * "If the state is SYN-SENT then + * first check the ACK bit + * If the ACK bit is set + * If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send + * a reset (unless the RST bit is set, if so drop + * the segment and return)" + * + * I cite this place to emphasize one essential + * detail, this check is different of one + * in established state: SND.UNA <= SEG.ACK <= SND.NXT. + * SEG_ACK == SND.UNA == ISS is invalid in SYN-SENT, + * because we have no previous data sent before SYN. + * --ANK(990513) + * + * We do not send data with SYN, so that RFC-correct + * test reduces to: + */ + if (sk->zapped || + TCP_SKB_CB(skb)->ack_seq != tp->snd_nxt) return 1; - if(th->rst) { + /* Now ACK is acceptable. + * + * "If the RST bit is set + * If the ACK was acceptable then signal the user "error: + * connection reset", drop the segment, enter CLOSED state, + * delete TCB, and return." + */ + + if (th->rst) { tcp_reset(sk); goto discard; } - if(!th->syn) + /* rfc793: + * "fifth, if neither of the SYN or RST bits is set then + * drop the segment and return." + * + * See note below! + * --ANK(990513) + */ + + if (!th->syn) goto discard; + /* rfc793: + * "If the SYN bit is on ... + * are acceptable then ... + * (our SYN has been ACKed), change the connection + * state to ESTABLISHED..." + * + * Do you see? SYN-less ACKs in SYN-SENT state are + * completely ignored. + * + * The bug causing stalled SYN-SENT sockets + * was here: tcp_ack advanced snd_una and canceled + * retransmit timer, so that bare ACK received + * in SYN-SENT state (even with invalid ack==ISS, + * because tcp_ack check is too weak for SYN-SENT) + * causes moving socket to invalid semi-SYN-SENT, + * semi-ESTABLISHED state and connection hangs. + * + * There exist buggy stacks, which really send + * such ACKs: f.e. 202.226.91.94 (okigate.oki.co.jp) + * Actually, if this host did not try to get something + * from ftp.inr.ac.ru I'd never find this bug 8) + * + * --ANK (990514) + */ + + tp->snd_wl1 = TCP_SKB_CB(skb)->seq; + tcp_ack(sk,th, TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(skb)->ack_seq, len); + /* Ok.. it's good. Set up sequence numbers and * move to established. */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9a096f0f307a..0aa868a667c0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.108 1999/05/08 21:48:59 davem Exp $ + * Version: $Id: tcp_output.c,v 1.109 1999/05/14 23:10:13 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -240,6 +240,11 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) /* Rechecksum original buffer. */ skb->csum = csum_partial(skb->data, skb->len, 0); + /* Looks stupid, but our code really uses when of + * skbs, which it never sent before. --ANK + */ + TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when; + /* Link BUFF into the send queue. */ __skb_append(skb, buff); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index ad6ccace95c6..123676fa54d6 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.62 1999/05/08 21:09:55 davem Exp $ + * Version: $Id: tcp_timer.c,v 1.63 1999/05/15 23:02:21 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -542,10 +542,6 @@ static void tcp_syn_recv_timer(unsigned long data) continue; } - if ((long)(now - conn->expires) <= 0) - break; - - tcp_synq_unlink(tp, conn, prev); if (conn->retrans >= sysctl_tcp_retries1) { #ifdef TCP_DEBUG diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f1ef74de8a4d..2c99e7fb6eac 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -551,7 +551,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, failure: dst_release(xchg(&sk->dst_cache, NULL)); - memcpy(&np->daddr, 0, sizeof(struct in6_addr)); + memset(&np->daddr, 0, sizeof(struct in6_addr)); sk->daddr = 0; return err; } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 9247bf99cadb..3c3445222e3a 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -203,7 +203,7 @@ static int netlink_release(struct socket *sock, struct socket *peer) */ while (netlink_locked(sk)) { - current->counter = 0; + current->policy |= SCHED_YIELD; schedule(); } -- 2.39.5