diff -u --recursive --new-file v2.4.5/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.4.5/linux/Documentation/Configure.help Fri May 25 18:28:53 2001 +++ linux/Documentation/Configure.help Mon Jun 4 16:02:56 2001 @@ -10709,9 +10709,10 @@ USB Handspring Visor Driver CONFIG_USB_SERIAL_VISOR - Say Y here if you want to connect to your HandSpring Visor through - its USB docking station. See http://usbvisor.sourceforge.net for - more information on using this driver. + Say Y here if you want to connect to your HandSpring Visor, Palm m500 + or m505 through its USB docking station. + See http://usbvisor.sourceforge.net for more information on using this + driver. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -11128,12 +11129,13 @@ Microtek USB scanner support CONFIG_USB_MICROTEK - Say Y here if you want support for the Microtek X6USB and possibly - some other scanners by that vendor. The scanner will appear as a - scsi generic device to the rest of the system. - A patched version of SANE is necessary to use the - scanner. It's available at - http://fachschaft.cup.uni-muenchen.de/~neukum/scanner.html + Say Y here if you want support for the Microtek X6USB and + possibly the Phantom 336CX, Phantom C6 and ScanMaker V6U(S)L. + Support for anything but the X6 is experimetal. + Please report failures and successes. + The scanner will appear as a scsi generic device to the rest + of the system. Scsi support is required for this driver to compile + and work. SANE 1.0.4 or newer is needed to make use of your scanner. This driver can be compiled as a module. USB Bluetooth support diff -u --recursive --new-file v2.4.5/linux/Makefile linux/Makefile --- v2.4.5/linux/Makefile Fri May 25 18:28:53 2001 +++ linux/Makefile Fri May 25 18:29:03 2001 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 -SUBLEVEL = 5 -EXTRAVERSION = +SUBLEVEL = 6 +EXTRAVERSION =-pre1 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -u --recursive --new-file v2.4.5/linux/arch/alpha/kernel/core_tsunami.c linux/arch/alpha/kernel/core_tsunami.c --- v2.4.5/linux/arch/alpha/kernel/core_tsunami.c Fri May 25 18:28:53 2001 +++ linux/arch/alpha/kernel/core_tsunami.c Sun Jun 3 16:45:55 2001 @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -21,6 +20,8 @@ #include #include #undef __EXTERN_INLINE + +#include #include "proto.h" #include "pci_impl.h" diff -u --recursive --new-file v2.4.5/linux/arch/alpha/kernel/sys_dp264.c linux/arch/alpha/kernel/sys_dp264.c --- v2.4.5/linux/arch/alpha/kernel/sys_dp264.c Fri May 25 18:28:53 2001 +++ linux/arch/alpha/kernel/sys_dp264.c Sun Jun 3 16:34:41 2001 @@ -16,18 +16,15 @@ #include #include -#define __EXTERN_INLINE inline -#include -#include -#undef __EXTERN_INLINE - #include #include #include #include #include #include +#include #include +#include #include #include "proto.h" diff -u --recursive --new-file v2.4.5/linux/arch/alpha/kernel/sys_rawhide.c linux/arch/alpha/kernel/sys_rawhide.c --- v2.4.5/linux/arch/alpha/kernel/sys_rawhide.c Fri Mar 2 11:12:07 2001 +++ linux/arch/alpha/kernel/sys_rawhide.c Sun Jun 3 16:51:30 2001 @@ -59,10 +59,11 @@ irq -= 16; hose = irq / 24; irq -= hose * 24; + mask = 1 << irq; spin_lock(&rawhide_irq_lock); - mask = cached_irq_masks[hose] |= 1 << irq; - mask |= hose_irq_masks[hose]; + mask |= cached_irq_masks[hose]; + cached_irq_masks[hose] = mask; rawhide_update_irq_hw(hose, mask); spin_unlock(&rawhide_irq_lock); } @@ -75,14 +76,37 @@ irq -= 16; hose = irq / 24; irq -= hose * 24; + mask = ~(1 << irq) | hose_irq_masks[hose]; spin_lock(&rawhide_irq_lock); - mask = cached_irq_masks[hose] &= ~(1 << irq); - mask |= hose_irq_masks[hose]; + mask &= cached_irq_masks[hose]; + cached_irq_masks[hose] = mask; rawhide_update_irq_hw(hose, mask); spin_unlock(&rawhide_irq_lock); } +static void +rawhide_mask_and_ack_irq(unsigned int irq) +{ + unsigned int mask, mask1, hose; + + irq -= 16; + hose = irq / 24; + irq -= hose * 24; + mask1 = 1 << irq; + mask = ~mask1 | hose_irq_masks[hose]; + + spin_lock(&rawhide_irq_lock); + + mask &= cached_irq_masks[hose]; + cached_irq_masks[hose] = mask; + rawhide_update_irq_hw(hose, mask); + + /* Clear the interrupt. */ + *(vuip)MCPCIA_INT_REQ(MCPCIA_HOSE2MID(hose)) = mask1; + + spin_unlock(&rawhide_irq_lock); +} static unsigned int rawhide_startup_irq(unsigned int irq) @@ -104,7 +128,7 @@ shutdown: rawhide_disable_irq, enable: rawhide_enable_irq, disable: rawhide_disable_irq, - ack: rawhide_disable_irq, + ack: rawhide_mask_and_ack_irq, end: rawhide_end_irq, }; @@ -145,8 +169,12 @@ mcpcia_init_hoses(); for (hose = hose_head; hose; hose = hose->next) { - int h = hose->index; - rawhide_update_irq_hw(h, hose_irq_masks[h]); + unsigned int h = hose->index; + unsigned int mask = hose_irq_masks[h]; + + cached_irq_masks[h] = mask; + *(vuip)MCPCIA_INT_MASK0(MCPCIA_HOSE2MID(h)) = mask; + *(vuip)MCPCIA_INT_MASK1(MCPCIA_HOSE2MID(h)) = 0; } for (i = 16; i < 128; ++i) { diff -u --recursive --new-file v2.4.5/linux/arch/cris/drivers/ide.c linux/arch/cris/drivers/ide.c --- v2.4.5/linux/arch/cris/drivers/ide.c Fri Apr 6 10:42:55 2001 +++ linux/arch/cris/drivers/ide.c Sun Jun 3 21:17:47 2001 @@ -1,4 +1,4 @@ -/* $Id: ide.c,v 1.9 2001/03/01 13:11:18 bjornw Exp $ +/* $Id: ide.c,v 1.16 2001/04/05 08:30:07 matsfg Exp $ * * Etrax specific IDE functions, like init and PIO-mode setting etc. * Almost the entire ide.c is used for the rest of the Etrax ATA driver. @@ -8,6 +8,28 @@ * Mikael Starvik (pio setup stuff) * * $Log: ide.c,v $ + * Revision 1.16 2001/04/05 08:30:07 matsfg + * Corrected cse1 and csp0 reset. + * + * Revision 1.15 2001/04/04 14:34:06 bjornw + * Re-instated code that mysteriously disappeared during review updates. + * + * Revision 1.14 2001/04/04 13:45:12 matsfg + * Calls REG_SHADOW_SET for cse1 reset so only the resetbit is affected + * + * Revision 1.13 2001/04/04 13:26:40 matsfg + * memmapping is done in init.c + * + * Revision 1.12 2001/04/04 11:37:56 markusl + * Updated according to review remarks + * + * Revision 1.11 2001/03/29 12:49:14 matsfg + * Changed check for ata_tot_size from >= to >. + * Sets sw_len to 0 if size is exactly 65536. + * + * Revision 1.10 2001/03/16 09:39:30 matsfg + * Support for reset on port CSP0 + * * Revision 1.9 2001/03/01 13:11:18 bjornw * 100 -> HZ * @@ -158,6 +180,10 @@ #define ATA_PIO0_STROBE 19 #define ATA_PIO0_HOLD 4 +static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); +static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, + void *buffer, unsigned int length); + /* * good_dma_drives() lists the model names (from "hdparm -i") * of drives which do not support mword2 DMA but which are @@ -174,7 +200,7 @@ unsigned long flags; pio = 4; - //pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + /* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */ save_flags(flags); cli(); @@ -226,10 +252,6 @@ restore_flags(flags); } -static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); /* defined below */ -static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, - void *buffer, unsigned int length); /* defined below */ - void __init init_e100_ide (void) { @@ -277,26 +299,23 @@ *R_GEN_CONFIG = genconfig_shadow; #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET -#ifndef CONFIG_CRIS_LOW_MAP - /* remap the I/O-mapped reset-bit from CSE1 to something inside our kernel space */ - reset_addr = (unsigned long *)ioremap((unsigned long)(MEM_CSE1_START | - MEM_NON_CACHEABLE), 16); - *reset_addr = 0; -#else - /* LOW_MAP, can't do the ioremap, but it's already mapped straight over */ - reset_addr = (unsigned long *)(MEM_CSE1_START | MEM_NON_CACHEABLE); - *reset_addr = 0; + init_ioremap(); + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0); #endif + +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + init_ioremap(); + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0); #endif /* wait some */ - - dummy = 1; - dummy = 2; - dummy = 3; + udelay(25); #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET - *reset_addr = 1 << 16; + REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET + REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 1); #endif #ifdef CONFIG_ETRAX_IDE_G27_RESET *R_PORT_G_DATA = 0; /* de-assert bus-reset */ @@ -349,7 +368,6 @@ e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { ide_ioreg_t data_reg = IDE_DATA_REG; - unsigned long status; D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n", data_reg, buffer, bytecount)); @@ -376,7 +394,7 @@ /* initiate a multi word dma read using PIO handshaking */ - *R_ATA_TRANSFER_CNT = bytecount >> 1; + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); *R_ATA_CTRL_DATA = data_reg | IO_STATE(R_ATA_CTRL_DATA, rw, read) | @@ -390,35 +408,38 @@ LED_DISK_READ(1); WAIT_DMA(3); LED_DISK_READ(0); - + #if 0 - /* old polled transfer code */ - - /* initiate a multi word read */ - - *R_ATA_TRANSFER_CNT = wcount << 1; - - *R_ATA_CTRL_DATA = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, read) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - /* svinto has a latency until the busy bit actually is set */ - - nop(); nop(); - nop(); nop(); - nop(); nop(); - nop(); nop(); - nop(); nop(); - - /* unit should be busy during multi transfer */ - while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { - while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) - status = *R_ATA_STATUS_DATA; - *ptr++ = (unsigned short)(status & 0xffff); - } + /* old polled transfer code + * this should be moved into a new function that can do polled + * transfers if DMA is not available + */ + + /* initiate a multi word read */ + + *R_ATA_TRANSFER_CNT = wcount << 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* svinto has a latency until the busy bit actually is set */ + + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + + /* unit should be busy during multi transfer */ + while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { + while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) + status = *R_ATA_STATUS_DATA; + *ptr++ = (unsigned short)(status & 0xffff); + } #endif } @@ -426,8 +447,6 @@ e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) { ide_ioreg_t data_reg = IDE_DATA_REG; - unsigned short *ptr = (unsigned short *)buffer; - unsigned long ctrl; D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n", data_reg, buffer, bytecount)); @@ -454,7 +473,7 @@ /* initiate a multi word dma write using PIO handshaking */ - *R_ATA_TRANSFER_CNT = bytecount >> 1; + *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); *R_ATA_CTRL_DATA = data_reg | IO_STATE(R_ATA_CTRL_DATA, rw, write) | @@ -470,40 +489,42 @@ LED_DISK_WRITE(0); #if 0 - /* old polled write code */ + /* old polled write code - see comment in input_bytes */ - while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ + /* wait for busy flag */ + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); - /* initiate a multi word write */ + /* initiate a multi word write */ - *R_ATA_TRANSFER_CNT = bytecount >> 1; + *R_ATA_TRANSFER_CNT = bytecount >> 1; - ctrl = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, write) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - LED_DISK_WRITE(1); - - /* Etrax will set busy = 1 until the multi pio transfer has finished - * and tr_rdy = 1 after each succesful word transfer. - * When the last byte has been transferred Etrax will first set tr_tdy = 1 - * and then busy = 0 (not in the same cycle). If we read busy before it - * has been set to 0 we will think that we should transfer more bytes - * and then tr_rdy would be 0 forever. This is solved by checking busy - * in the inner loop. - */ - - do { - *R_ATA_CTRL_DATA = ctrl | *ptr++; - while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && - (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); - } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + ctrl = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + /* Etrax will set busy = 1 until the multi pio transfer has finished + * and tr_rdy = 1 after each succesful word transfer. + * When the last byte has been transferred Etrax will first set tr_tdy = 1 + * and then busy = 0 (not in the same cycle). If we read busy before it + * has been set to 0 we will think that we should transfer more bytes + * and then tr_rdy would be 0 forever. This is solved by checking busy + * in the inner loop. + */ + + do { + *R_ATA_CTRL_DATA = ctrl | *ptr++; + while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && + (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); + } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + LED_DISK_WRITE(0); +#endif - LED_DISK_WRITE(0); -#endif } /* @@ -604,7 +625,7 @@ those blocks that were actually set-up for transfer. */ - if(ata_tot_size + size >= 131072) { + if(ata_tot_size + size > 131072) { printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, size); return 1; } @@ -625,7 +646,12 @@ addr += 65536; } /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ - ata_descrs[count].sw_len = size; + if(size == 65536) { + ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ + } + else { + ata_descrs[count].sw_len = size; + } ata_descrs[count].ctrl = 0; ata_descrs[count].buf = addr; ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); @@ -793,9 +819,11 @@ /* initiate a multi word dma read using DMA handshaking */ - *R_ATA_TRANSFER_CNT = ata_tot_size >> 1; + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); - *R_ATA_CTRL_DATA = IDE_DATA_REG | + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | IO_STATE(R_ATA_CTRL_DATA, rw, read) | IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | @@ -834,9 +862,11 @@ /* initiate a multi word dma write using DMA handshaking */ - *R_ATA_TRANSFER_CNT = ata_tot_size >> 1; + *R_ATA_TRANSFER_CNT = + IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); - *R_ATA_CTRL_DATA = IDE_DATA_REG | + *R_ATA_CTRL_DATA = + IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | IO_STATE(R_ATA_CTRL_DATA, rw, write) | IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | diff -u --recursive --new-file v2.4.5/linux/arch/i386/kernel/apic.c linux/arch/i386/kernel/apic.c --- v2.4.5/linux/arch/i386/kernel/apic.c Tue Dec 5 12:43:48 2000 +++ linux/arch/i386/kernel/apic.c Mon Jun 4 15:57:49 2001 @@ -728,6 +728,9 @@ irq_enter(cpu, 0); smp_local_timer_interrupt(regs); irq_exit(cpu, 0); + + if (softirq_pending(cpu)) + do_softirq(); } /* diff -u --recursive --new-file v2.4.5/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.4.5/linux/arch/i386/kernel/entry.S Wed Nov 8 17:09:50 2000 +++ linux/arch/i386/kernel/entry.S Mon Jun 4 15:57:49 2001 @@ -133,6 +133,18 @@ movl $-8192, reg; \ andl %esp, reg +#ifdef CONFIG_SMP +#define CHECK_SOFTIRQ \ + movl processor(%ebx),%eax; \ + shll $CONFIG_X86_L1_CACHE_SHIFT,%eax; \ + movl SYMBOL_NAME(irq_stat)(,%eax),%ecx; \ + testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx +#else +#define CHECK_SOFTIRQ \ + movl SYMBOL_NAME(irq_stat),%ecx; \ + testl SYMBOL_NAME(irq_stat)+4,%ecx +#endif + ENTRY(lcall7) pushfl # We get a different stack layout with call gates, pushl %eax # which has to be cleaned up later.. @@ -203,18 +215,9 @@ call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value ENTRY(ret_from_sys_call) -#ifdef CONFIG_SMP - movl processor(%ebx),%eax - shll $CONFIG_X86_L1_CACHE_SHIFT,%eax - movl SYMBOL_NAME(irq_stat)(,%eax),%ecx # softirq_active - testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx # softirq_mask -#else - movl SYMBOL_NAME(irq_stat),%ecx # softirq_active - testl SYMBOL_NAME(irq_stat)+4,%ecx # softirq_mask -#endif - jne handle_softirq - -ret_with_reschedule: + cli + CHECK_SOFTIRQ + jne handle_softirq cmpl $0,need_resched(%ebx) jne reschedule cmpl $0,sigpending(%ebx) @@ -230,6 +233,13 @@ jne v86_signal_return xorl %edx,%edx call SYMBOL_NAME(do_signal) +#ifdef CONFIG_SMP + GET_CURRENT(%ebx) +#endif + cli + CHECK_SOFTIRQ + je restore_all + call SYMBOL_NAME(do_softirq) jmp restore_all ALIGN @@ -238,6 +248,13 @@ movl %eax,%esp xorl %edx,%edx call SYMBOL_NAME(do_signal) +#ifdef CONFIG_SMP + GET_CURRENT(%ebx) +#endif + cli + CHECK_SOFTIRQ + je restore_all + call SYMBOL_NAME(do_softirq) jmp restore_all ALIGN @@ -258,24 +275,21 @@ ALIGN ret_from_exception: -#ifdef CONFIG_SMP - GET_CURRENT(%ebx) - movl processor(%ebx),%eax - shll $CONFIG_X86_L1_CACHE_SHIFT,%eax - movl SYMBOL_NAME(irq_stat)(,%eax),%ecx # softirq_active - testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx # softirq_mask -#else - movl SYMBOL_NAME(irq_stat),%ecx # softirq_active - testl SYMBOL_NAME(irq_stat)+4,%ecx # softirq_mask -#endif - jne handle_softirq + cli + CHECK_SOFTIRQ + jne handle_softirq + cmpl $0,need_resched(%ebx) + jne reschedule + cmpl $0,sigpending(%ebx) + jne signal_return + jmp restore_all ENTRY(ret_from_intr) GET_CURRENT(%ebx) movl EFLAGS(%esp),%eax # mix EFLAGS and CS movb CS(%esp),%al testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? - jne ret_with_reschedule + jne ret_from_sys_call jmp restore_all ALIGN diff -u --recursive --new-file v2.4.5/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c --- v2.4.5/linux/arch/i386/kernel/io_apic.c Fri Feb 9 11:28:31 2001 +++ linux/arch/i386/kernel/io_apic.c Mon Jun 4 15:57:21 2001 @@ -255,10 +255,16 @@ */ static int pin_2_irq(int idx, int apic, int pin); -int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) +int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) { int apic, i, best_guess = -1; + Dprintk("querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n", + bus, slot, pin); + if (mp_bus_id_to_pci_bus[bus] == -1) { + printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus); + return -1; + } for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; @@ -269,14 +275,14 @@ if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) && !mp_irqs[i].mpc_irqtype && - (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) && + (bus == lbus) && (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); if (!(apic || IO_APIC_IRQ(irq))) continue; - if (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3)) + if (pin == (mp_irqs[i].mpc_srcbusirq & 3)) return irq; /* * Use the first all-but-pin matching entry as a @@ -728,9 +734,11 @@ printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)®_01); printk(KERN_DEBUG "....... : max redirection entries: %04X\n", reg_01.entries); if ( (reg_01.entries != 0x0f) && /* older (Neptune) boards */ + (reg_01.entries != 0x11) && (reg_01.entries != 0x17) && /* typical ISA+PCI boards */ (reg_01.entries != 0x1b) && /* Compaq Proliant boards */ (reg_01.entries != 0x1f) && /* dual Xeon boards */ + (reg_01.entries != 0x20) && (reg_01.entries != 0x22) && /* bigger Xeon boards */ (reg_01.entries != 0x2E) && (reg_01.entries != 0x3F) diff -u --recursive --new-file v2.4.5/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v2.4.5/linux/arch/i386/kernel/irq.c Fri Feb 9 11:29:44 2001 +++ linux/arch/i386/kernel/irq.c Mon Jun 4 15:57:49 2001 @@ -623,7 +623,7 @@ desc->handler->end(irq); spin_unlock(&desc->lock); - if (softirq_active(cpu) & softirq_mask(cpu)) + if (softirq_pending(cpu)) do_softirq(); return 1; } diff -u --recursive --new-file v2.4.5/linux/arch/i386/kernel/mpparse.c linux/arch/i386/kernel/mpparse.c --- v2.4.5/linux/arch/i386/kernel/mpparse.c Tue Nov 14 21:25:34 2000 +++ linux/arch/i386/kernel/mpparse.c Mon Jun 4 15:57:21 2001 @@ -36,7 +36,7 @@ */ int apic_version [MAX_APICS]; int mp_bus_id_to_type [MAX_MP_BUSSES]; -int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, }; +int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; int mp_current_pci_id; int pic_mode; unsigned long mp_lapic_addr; diff -u --recursive --new-file v2.4.5/linux/arch/i386/kernel/pci-irq.c linux/arch/i386/kernel/pci-irq.c --- v2.4.5/linux/arch/i386/kernel/pci-irq.c Fri May 25 18:28:53 2001 +++ linux/arch/i386/kernel/pci-irq.c Mon Jun 4 15:57:21 2001 @@ -656,10 +656,12 @@ if (pin) { pin--; /* interrupt pins are numbered starting from 1 */ irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); -/* - * Will be removed completely if things work out well with fuzzy parsing - */ -#if 0 + /* + * Busses behind bridges are typically not listed in the MP-table. + * In this case we have to look up the IRQ based on the parent bus, + * parent slot, and pin number. The SMP code detects such bridged + * busses itself so we should get into this branch reliably. + */ if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ struct pci_dev * bridge = dev->bus->self; @@ -670,7 +672,6 @@ printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); } -#endif if (irq >= 0) { printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); diff -u --recursive --new-file v2.4.5/linux/drivers/char/rio/rioroute.c linux/drivers/char/rio/rioroute.c --- v2.4.5/linux/drivers/char/rio/rioroute.c Fri Feb 9 11:30:23 2001 +++ linux/drivers/char/rio/rioroute.c Sun Jun 3 16:46:34 2001 @@ -657,6 +657,7 @@ */ if (PortP->TxStart == 0) { rio_dprintk (RIO_DEBUG_ROUTE, "Tx pkts not set up yet\n"); + rio_spin_unlock_irqrestore(&PortP->portSem, flags); break; } diff -u --recursive --new-file v2.4.5/linux/drivers/i2o/i2o_lan.c linux/drivers/i2o/i2o_lan.c --- v2.4.5/linux/drivers/i2o/i2o_lan.c Fri May 25 18:28:54 2001 +++ linux/drivers/i2o/i2o_lan.c Sun Jun 3 21:12:45 2001 @@ -984,7 +984,7 @@ __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext __raw_writel(1 << 30 | 1 << 3, msg+3); // TransmitControlWord // bit 30: reply as soon as transmission attempt is complete - // bit 3: Supress CRC generation + // bit 3: Suppress CRC generation __raw_writel(0xD5000000 | skb->len, msg+4); // MAC hdr included __raw_writel((u32)skb, msg+5); // TransactionContext __raw_writel(virt_to_bus(skb->data), msg+6); diff -u --recursive --new-file v2.4.5/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c --- v2.4.5/linux/drivers/scsi/scsi_ioctl.c Fri Apr 27 13:59:19 2001 +++ linux/drivers/scsi/scsi_ioctl.c Sun Jun 3 15:24:03 2001 @@ -312,17 +312,17 @@ /* * If there was an error condition, pass the info back to the user. */ + result = SRpnt->sr_result; if (SRpnt->sr_result) { int sb_len = sizeof(SRpnt->sr_sense_buffer); sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len; if (copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len)) - return -EFAULT; - } else + result = -EFAULT; + } else { if (copy_to_user(cmd_in, buf, outlen)) - return -EFAULT; - - result = SRpnt->sr_result; + result = -EFAULT; + } SDpnt = SRpnt->sr_device; scsi_release_request(SRpnt); diff -u --recursive --new-file v2.4.5/linux/drivers/usb/Config.in linux/drivers/usb/Config.in --- v2.4.5/linux/drivers/usb/Config.in Fri May 25 18:28:55 2001 +++ linux/drivers/usb/Config.in Mon Jun 4 16:02:56 2001 @@ -53,7 +53,7 @@ dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB - dep_tristate ' Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI $CONFIG_EXPERIMENTAL + dep_tristate ' Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI comment 'USB Multimedia devices' dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV diff -u --recursive --new-file v2.4.5/linux/drivers/usb/bluetooth.c linux/drivers/usb/bluetooth.c --- v2.4.5/linux/drivers/usb/bluetooth.c Fri May 25 18:28:55 2001 +++ linux/drivers/usb/bluetooth.c Mon Jun 4 16:04:08 2001 @@ -1,11 +1,20 @@ /* - * bluetooth.c Version 0.8 + * bluetooth.c Version 0.10 * * Copyright (c) 2000 Greg Kroah-Hartman * Copyright (c) 2000 Mark Douglas Corner * * USB Bluetooth driver, based on the Bluetooth Spec version 1.0B * + * (2001/05/28) Version 0.10 gkh + * - Fixed problem with using data from userspace in the bluetooth_write + * function as found by the CHECKER project. + * - Added a buffer to the write_urb_pool which reduces the number of + * buffers being created and destroyed for ever write. Also cleans + * up the logic a bit. + * - Added a buffer to the control_urb_pool which fixes a memory leak + * when the device is removed from the system. + * * (08/04/2001) gb * Identify version on module load. * @@ -97,7 +106,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.8" +#define DRIVER_VERSION "v0.10" #define DRIVER_AUTHOR "Greg Kroah-Hartman, Mark Douglas Corner" #define DRIVER_DESC "USB Bluetooth driver" @@ -260,7 +269,7 @@ } -static int bluetooth_ctrl_msg (struct usb_bluetooth *bluetooth, int request, int value, void *buf, int len) +static int bluetooth_ctrl_msg (struct usb_bluetooth *bluetooth, int request, int value, const unsigned char *buf, int len) { struct urb *urb = NULL; devrequest *dr = NULL; @@ -282,11 +291,23 @@ return -ENOMEM; } - /* free up the last buffer that this urb used */ - if (urb->transfer_buffer != NULL) { - kfree(urb->transfer_buffer); - urb->transfer_buffer = NULL; + /* keep increasing the urb transfer buffer to fit the size of the message */ + if (urb->transfer_buffer == NULL) { + urb->transfer_buffer = kmalloc (len, GFP_KERNEL); + if (urb->transfer_buffer == NULL) { + err (__FUNCTION__" - out of memory"); + return -ENOMEM; + } + } + if (urb->transfer_buffer_length < len) { + kfree (urb->transfer_buffer); + urb->transfer_buffer = kmalloc (len, GFP_KERNEL); + if (urb->transfer_buffer == NULL) { + err (__FUNCTION__" - out of memory"); + return -ENOMEM; + } } + memcpy (urb->transfer_buffer, buf, len); dr->requesttype = BLUETOOTH_CONTROL_REQUEST_TYPE; dr->request = request; @@ -295,14 +316,14 @@ dr->length = cpu_to_le16p(&len); FILL_CONTROL_URB (urb, bluetooth->dev, usb_sndctrlpipe(bluetooth->dev, 0), - (unsigned char*)dr, buf, len, bluetooth_ctrl_callback, bluetooth); + (unsigned char*)dr, urb->transfer_buffer, len, bluetooth_ctrl_callback, bluetooth); /* send it down the pipe */ status = usb_submit_urb(urb); if (status) dbg(__FUNCTION__ " - usb_submit_urb(control) failed with status = %d", status); - return 0; + return status; } @@ -401,12 +422,13 @@ { struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__); struct urb *urb = NULL; - unsigned char *new_buffer; + unsigned char *temp_buffer = NULL; + const unsigned char *current_buffer; const unsigned char *current_position; - int status; int bytes_sent; int buffer_size; int i; + int retval = 0; if (!bluetooth) { return -ENODEV; @@ -436,38 +458,39 @@ printk ("\n"); #endif - switch (*buf) { + if (from_user) { + temp_buffer = kmalloc (count, GFP_KERNEL); + if (temp_buffer == NULL) { + err (__FUNCTION__ "- out of memory."); + retval = -ENOMEM; + goto exit; + } + copy_from_user (temp_buffer, buf, count); + current_buffer = temp_buffer; + } else { + current_buffer = buf; + } + + switch (*current_buffer) { /* First byte indicates the type of packet */ case CMD_PKT: /* dbg(__FUNCTION__ "- Send cmd_pkt len:%d", count);*/ if (in_interrupt()){ printk("cmd_pkt from interrupt!\n"); - return count; - } - - new_buffer = kmalloc (count-1, GFP_KERNEL); - - if (!new_buffer) { - err (__FUNCTION__ "- out of memory."); - return -ENOMEM; + retval = count; + goto exit; } - if (from_user) - copy_from_user (new_buffer, buf+1, count-1); - else - memcpy (new_buffer, buf+1, count-1); - - if (bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, new_buffer, count-1) != 0) { - kfree (new_buffer); - return 0; + retval = bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, ¤t_buffer[1], count-1); + if (retval) { + goto exit; } - - /* need to free new_buffer somehow... FIXME */ - return count; + retval = count; + break; case ACL_PKT: - current_position = buf; + current_position = current_buffer; ++current_position; --count; bytes_sent = 0; @@ -484,37 +507,25 @@ } if (urb == NULL) { dbg (__FUNCTION__ " - no free urbs"); - return bytes_sent; + retval = bytes_sent; + goto exit; } - /* free up the last buffer that this urb used */ - if (urb->transfer_buffer != NULL) { - kfree(urb->transfer_buffer); - urb->transfer_buffer = NULL; - } buffer_size = MIN (count, bluetooth->bulk_out_buffer_size); - - new_buffer = kmalloc (buffer_size, GFP_KERNEL); - if (new_buffer == NULL) { - err(__FUNCTION__" no more kernel memory..."); - return bytes_sent; - } - - if (from_user) - copy_from_user(new_buffer, current_position, buffer_size); - else - memcpy (new_buffer, current_position, buffer_size); + memcpy (urb->transfer_buffer, current_position, buffer_size); /* build up our urb */ FILL_BULK_URB (urb, bluetooth->dev, usb_sndbulkpipe(bluetooth->dev, bluetooth->bulk_out_endpointAddress), - new_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth); + urb->transfer_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth); urb->transfer_flags |= USB_QUEUE_BULK; /* send it down the pipe */ - status = usb_submit_urb(urb); - if (status) - dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status); + retval = usb_submit_urb(urb); + if (retval) { + dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with error = %d", retval); + goto exit; + } #ifdef BTBUGGYHARDWARE /* A workaround for the stalled data bug */ /* May or may not be needed...*/ @@ -527,13 +538,20 @@ count -= buffer_size; } - return bytes_sent + 1; + retval = bytes_sent + 1; + break; default : dbg(__FUNCTION__" - unsupported (at this time) write type"); + retval = -EINVAL; + break; } - return 0; +exit: + if (temp_buffer != NULL) + kfree (temp_buffer); + + return retval; } @@ -1114,7 +1132,11 @@ err("No free urbs available"); goto probe_error; } - urb->transfer_buffer = NULL; + urb->transfer_buffer = kmalloc (bluetooth->bulk_out_buffer_size, GFP_KERNEL); + if (urb->transfer_buffer == NULL) { + err("out of memory"); + goto probe_error; + } bluetooth->write_urb_pool[i] = urb; } @@ -1156,11 +1178,17 @@ if (bluetooth->interrupt_in_buffer) kfree (bluetooth->interrupt_in_buffer); for (i = 0; i < NUM_BULK_URBS; ++i) - if (bluetooth->write_urb_pool[i]) + if (bluetooth->write_urb_pool[i]) { + if (bluetooth->write_urb_pool[i]->transfer_buffer) + kfree (bluetooth->write_urb_pool[i]->transfer_buffer); usb_free_urb (bluetooth->write_urb_pool[i]); + } for (i = 0; i < NUM_CONTROL_URBS; ++i) - if (bluetooth->control_urb_pool[i]) + if (bluetooth->control_urb_pool[i]) { + if (bluetooth->control_urb_pool[i]->transfer_buffer) + kfree (bluetooth->control_urb_pool[i]->transfer_buffer); usb_free_urb (bluetooth->control_urb_pool[i]); + } bluetooth_table[minor] = NULL; diff -u --recursive --new-file v2.4.5/linux/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- v2.4.5/linux/drivers/usb/ov511.c Fri May 25 18:28:55 2001 +++ linux/drivers/usb/ov511.c Mon Jun 4 16:02:56 2001 @@ -337,7 +337,7 @@ /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ - out += sprintf (out, "driver_version : %s\n", version); + out += sprintf (out, "driver_version : %s\n", DRIVER_VERSION); out += sprintf (out, "custom_id : %d\n", ov511->customid); out += sprintf (out, "model : %s\n", ov511->desc ? clist[ov511->desc].description : "unknown"); diff -u --recursive --new-file v2.4.5/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c --- v2.4.5/linux/drivers/usb/pegasus.c Fri May 25 18:28:55 2001 +++ linux/drivers/usb/pegasus.c Mon Jun 4 16:02:56 2001 @@ -127,8 +127,16 @@ static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { int ret; + unsigned char *buffer; DECLARE_WAITQUEUE(wait, current); + buffer = kmalloc(size,GFP_KERNEL); + if (!buffer) { + err("unable to allocate memory for configuration descriptors"); + return 0; + } + memcpy(buffer,data,size); + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP; interruptible_sleep_on( &pegasus->ctrl_wait ); @@ -143,7 +151,7 @@ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, - data, size, ctrl_callback, pegasus ); + buffer, size, ctrl_callback, pegasus ); add_wait_queue( &pegasus->ctrl_wait, &wait ); set_current_state( TASK_INTERRUPTIBLE ); @@ -157,6 +165,8 @@ schedule(); remove_wait_queue( &pegasus->ctrl_wait, &wait ); out: + memcpy(data,buffer,size); + kfree(buffer); return ret; } @@ -164,8 +174,16 @@ static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { int ret; + unsigned char *buffer; DECLARE_WAITQUEUE(wait, current); + buffer = kmalloc(size, GFP_KERNEL); + if (!buffer) { + err("unable to allocate memory for configuration descriptors"); + return 0; + } + memcpy(buffer, data, size); + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP ; interruptible_sleep_on( &pegasus->ctrl_wait ); @@ -180,7 +198,7 @@ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, - data, size, ctrl_callback, pegasus ); + buffer, size, ctrl_callback, pegasus ); add_wait_queue( &pegasus->ctrl_wait, &wait ); set_current_state( TASK_INTERRUPTIBLE ); @@ -188,12 +206,14 @@ if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { err( __FUNCTION__ " BAD CTRL %d", ret); + kfree(buffer); return ret; } schedule(); remove_wait_queue( &pegasus->ctrl_wait, &wait ); + kfree(buffer); return ret; } @@ -201,9 +221,17 @@ static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ) { int ret; + unsigned char *buffer; __u16 dat = data; DECLARE_WAITQUEUE(wait, current); + buffer = kmalloc(1, GFP_KERNEL); + if (!buffer) { + err("unable to allocate memory for configuration descriptors"); + return 0; + } + memcpy(buffer, &data, 1); + while ( pegasus->flags & ETH_REGS_CHANGED ) { pegasus->flags |= CTRL_URB_SLEEP; interruptible_sleep_on( &pegasus->ctrl_wait ); @@ -218,7 +246,7 @@ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, - &data, 1, ctrl_callback, pegasus ); + buffer, 1, ctrl_callback, pegasus ); add_wait_queue( &pegasus->ctrl_wait, &wait ); set_current_state( TASK_INTERRUPTIBLE ); @@ -226,12 +254,14 @@ if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { err( __FUNCTION__ " BAD CTRL %d", ret); + kfree(buffer); return ret; } schedule(); remove_wait_queue( &pegasus->ctrl_wait, &wait ); + kfree(buffer); return ret; } diff -u --recursive --new-file v2.4.5/linux/drivers/usb/serial/io_edgeport.c linux/drivers/usb/serial/io_edgeport.c --- v2.4.5/linux/drivers/usb/serial/io_edgeport.c Fri May 25 18:28:55 2001 +++ linux/drivers/usb/serial/io_edgeport.c Mon Jun 4 16:02:56 2001 @@ -944,7 +944,7 @@ } if (status) { - dbg(__FUNCTION__" - nonzero write bulk status received: %d", urb->status); + dbg(__FUNCTION__" - nonzero write bulk status received: %d", status); return; } diff -u --recursive --new-file v2.4.5/linux/drivers/usb/serial/visor.c linux/drivers/usb/serial/visor.c --- v2.4.5/linux/drivers/usb/serial/visor.c Fri May 25 18:28:55 2001 +++ linux/drivers/usb/serial/visor.c Mon Jun 4 16:02:56 2001 @@ -1,7 +1,7 @@ /* * USB HandSpring Visor driver * - * Copyright (C) 1999, 2000 + * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/28/2000) gkh + * Added initial support for the Palm m500 and Palm m505 devices. + * * (04/08/2001) gb * Identify version on module load. * @@ -109,7 +112,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.0.0" +#define DRIVER_VERSION "v1.1" #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB HandSpring Visor driver" @@ -131,8 +134,26 @@ static void visor_read_bulk_callback (struct urb *urb); +static __devinitdata struct usb_device_id visor_id_table [] = { + { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id palm_m500_id_table [] = { + { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, + { } /* Terminating entry */ +}; + +static __devinitdata struct usb_device_id palm_m505_id_table [] = { + { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, + { } /* Terminating entry */ +}; + + static __devinitdata struct usb_device_id id_table [] = { { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { } /* Terminating entry */ }; @@ -143,7 +164,33 @@ /* All of the device info needed for the Handspring Visor */ struct usb_serial_device_type handspring_device = { name: "Handspring Visor", - id_table: id_table, + id_table: visor_id_table, + needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 0, + num_bulk_in: 2, + num_bulk_out: 2, + num_ports: 2, + open: visor_open, + close: visor_close, + throttle: visor_throttle, + unthrottle: visor_unthrottle, + startup: visor_startup, + shutdown: visor_shutdown, + ioctl: visor_ioctl, + set_termios: visor_set_termios, + write: visor_write, + write_room: visor_write_room, + chars_in_buffer: visor_chars_in_buffer, + write_bulk_callback: visor_write_bulk_callback, + read_bulk_callback: visor_read_bulk_callback, +}; + +/* device info for the Palm M500 */ +struct usb_serial_device_type palm_m500_device = { + name: "Palm M500", + id_table: palm_m500_id_table, needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ @@ -166,6 +213,31 @@ read_bulk_callback: visor_read_bulk_callback, }; +/* device info for the Palm M505 */ +struct usb_serial_device_type palm_m505_device = { + name: "Palm M505", + id_table: palm_m505_id_table, + needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 0, + num_bulk_in: 2, + num_bulk_out: 2, + num_ports: 2, + open: visor_open, + close: visor_close, + throttle: visor_throttle, + unthrottle: visor_unthrottle, + startup: visor_startup, + shutdown: visor_shutdown, + ioctl: visor_ioctl, + set_termios: visor_set_termios, + write: visor_write, + write_room: visor_write_room, + chars_in_buffer: visor_chars_in_buffer, + write_bulk_callback: visor_write_bulk_callback, + read_bulk_callback: visor_read_bulk_callback, +}; #define NUM_URBS 24 #define URB_TRANSFER_BUFFER_SIZE 768 @@ -496,6 +568,10 @@ return -ENOMEM; } + /* force debugging on for the palm devices for now */ + if (serial->dev->descriptor.idVendor == PALM_VENDOR_ID) + debug = 1; + dbg(__FUNCTION__); dbg(__FUNCTION__ " - Set config to 1"); @@ -537,6 +613,28 @@ } } + if (serial->dev->descriptor.idVendor == PALM_VENDOR_ID) { + /* Palm USB Hack */ + response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), + PALM_GET_SOME_UNKNOWN_INFORMATION, + 0xc2, 0x0000, 0x0000, transfer_buffer, + 0x14, 300); + if (response < 0) { + err(__FUNCTION__ " - error getting first unknown palm command"); + } else { + usb_serial_debug_data (__FILE__, __FUNCTION__, 0x14, transfer_buffer); + } + response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), + PALM_GET_SOME_UNKNOWN_INFORMATION, + 0xc2, 0x0000, 0x0000, transfer_buffer, + 0x14, 300); + if (response < 0) { + err(__FUNCTION__ " - error getting second unknown palm command"); + } else { + usb_serial_debug_data (__FILE__, __FUNCTION__, 0x14, transfer_buffer); + } + } + /* ask for the number of bytes available, but ignore the response as it is broken */ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE, 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300); @@ -645,6 +743,8 @@ int i; usb_serial_register (&handspring_device); + usb_serial_register (&palm_m500_device); + usb_serial_register (&palm_m505_device); /* create our write urb pool and transfer buffers */ spin_lock_init (&write_urb_pool_lock); @@ -677,6 +777,8 @@ unsigned long flags; usb_serial_deregister (&handspring_device); + usb_serial_deregister (&palm_m500_device); + usb_serial_deregister (&palm_m505_device); spin_lock_irqsave (&write_urb_pool_lock, flags); diff -u --recursive --new-file v2.4.5/linux/drivers/usb/serial/visor.h linux/drivers/usb/serial/visor.h --- v2.4.5/linux/drivers/usb/serial/visor.h Wed Apr 18 11:49:12 2001 +++ linux/drivers/usb/serial/visor.h Mon Jun 4 16:02:56 2001 @@ -1,7 +1,7 @@ /* * USB HandSpring Visor driver * - * Copyright (C) 1999, 2000 + * Copyright (C) 1999 - 2001 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,10 @@ #define HANDSPRING_VENDOR_ID 0x082d #define HANDSPRING_VISOR_ID 0x0100 +#define PALM_VENDOR_ID 0x0830 +#define PALM_M500_ID 0x0001 +#define PALM_M505_ID 0x0002 + /**************************************************************************** * Handspring Visor Vendor specific request codes (bRequest values) * A big thank you to Handspring for providing the following information. @@ -69,6 +73,13 @@ #define VISOR_FUNCTION_HOTSYNC 0x02 #define VISOR_FUNCTION_CONSOLE 0x03 #define VISOR_FUNCTION_REMOTE_FILE_SYS 0x04 + + +/**************************************************************************** + * PALM_GET_SOME_UNKNOWN_INFORMATION is sent by the host during enumeration to + * get some information from the M series devices, that is currently unknown. + ****************************************************************************/ +#define PALM_GET_SOME_UNKNOWN_INFORMATION 0x04 #endif diff -u --recursive --new-file v2.4.5/linux/drivers/usb/usb.c linux/drivers/usb/usb.c --- v2.4.5/linux/drivers/usb/usb.c Fri May 25 18:28:55 2001 +++ linux/drivers/usb/usb.c Mon Jun 4 16:02:56 2001 @@ -449,7 +449,7 @@ */ list_del(&bus->bus_list); - usbdevfs_remove_bus(bus); + usbdevfs_remove_bus(bus); clear_bit(bus->busnum, busmap.busmap); @@ -1869,6 +1869,7 @@ { int result; __u16 status; + unsigned char *buffer; int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7); /* @@ -1883,9 +1884,19 @@ if (result < 0) return result; + buffer = kmalloc(sizeof(status), GFP_KERNEL); + if (!buffer) { + err("unable to allocate memory for configuration descriptors"); + return -ENOMEM; + } + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp, - &status, sizeof(status), HZ * SET_TIMEOUT); + buffer, sizeof(status), HZ * SET_TIMEOUT); + + memcpy(&status, buffer, sizeof(status)); + kfree(buffer); + if (result < 0) return result; @@ -1970,10 +1981,9 @@ { int result; unsigned int cfgno, length; - unsigned char buffer[8]; + unsigned char *buffer; unsigned char *bigbuffer; - struct usb_config_descriptor *desc = - (struct usb_config_descriptor *)buffer; + struct usb_config_descriptor *desc; if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) { warn("too many configurations"); @@ -2002,6 +2012,13 @@ return -ENOMEM; } + buffer = kmalloc(8, GFP_KERNEL); + if (!buffer) { + err("unable to allocate memory for configuration descriptors"); + return -ENOMEM; + } + desc = (struct usb_config_descriptor *)buffer; + for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { /* We grab the first 8 bytes so we know how long the whole */ /* configuration is */ @@ -2052,8 +2069,10 @@ } } + kfree(buffer); return 0; err: + kfree(buffer); dev->descriptor.bNumConfigurations = cfgno; return result; } diff -u --recursive --new-file v2.4.5/linux/drivers/video/matrox/matroxfb_base.c linux/drivers/video/matrox/matroxfb_base.c --- v2.4.5/linux/drivers/video/matrox/matroxfb_base.c Sun Feb 4 10:05:30 2001 +++ linux/drivers/video/matrox/matroxfb_base.c Sat May 26 01:11:43 2001 @@ -2483,7 +2483,7 @@ return 0; } -static int __init initialized = 0; +static int initialized __initdata = 0; int __init matroxfb_init(void) { diff -u --recursive --new-file v2.4.5/linux/fs/buffer.c linux/fs/buffer.c --- v2.4.5/linux/fs/buffer.c Fri May 25 18:28:55 2001 +++ linux/fs/buffer.c Mon Jun 4 17:56:50 2001 @@ -61,7 +61,7 @@ #define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9]) #define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512) -#define NR_RESERVED (2*MAX_BUF_PER_PAGE) +#define NR_RESERVED (10*MAX_BUF_PER_PAGE) #define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this number of unused buffer heads */ @@ -1348,11 +1348,9 @@ */ run_task_queue(&tq_disk); - /* - * Set our state for sleeping, then check again for buffer heads. - * This ensures we won't miss a wake_up from an interrupt. - */ - wait_event(buffer_wait, nr_unused_buffer_heads >= MAX_BUF_PER_PAGE); + current->policy |= SCHED_YIELD; + __set_current_state(TASK_RUNNING); + schedule(); goto try_again; } diff -u --recursive --new-file v2.4.5/linux/fs/ext2/dir.c linux/fs/ext2/dir.c --- v2.4.5/linux/fs/ext2/dir.c Fri Dec 8 17:35:54 2000 +++ linux/fs/ext2/dir.c Sun Jun 3 17:42:16 2001 @@ -16,167 +16,567 @@ * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 + * + * All code that works with directory layout had been switched to pagecache + * and moved here. AV */ #include #include +#include -static unsigned char ext2_filetype_table[] = { - DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK -}; +typedef struct ext2_dir_entry_2 ext2_dirent; -static int ext2_readdir(struct file *, void *, filldir_t); +/* + * ext2 uses block-sized chunks. Arguably, sector-sized ones would be + * more robust, but we have what we have + */ +static inline unsigned ext2_chunk_size(struct inode *inode) +{ + return inode->i_sb->s_blocksize; +} -struct file_operations ext2_dir_operations = { - read: generic_read_dir, - readdir: ext2_readdir, - ioctl: ext2_ioctl, - fsync: ext2_sync_file, +static inline void ext2_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); +} + +static inline unsigned long dir_pages(struct inode *inode) +{ + return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; +} + +static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to) +{ + struct inode *dir = (struct inode *)page->mapping->host; + int err = 0; + dir->i_version = ++event; + page->mapping->a_ops->commit_write(NULL, page, from, to); + if (IS_SYNC(dir)) + err = waitfor_one_page(page); + return err; +} + +static void ext2_check_page(struct page *page) +{ + struct inode *dir = (struct inode *)page->mapping->host; + struct super_block *sb = dir->i_sb; + unsigned chunk_size = ext2_chunk_size(dir); + char *kaddr = (char*)page_address(page); + u32 max_inumber = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count); + unsigned offs, rec_len; + unsigned limit = PAGE_CACHE_SIZE; + ext2_dirent *p; + char *error; + + if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { + limit = dir->i_size & ~PAGE_CACHE_MASK; + if (limit & (chunk_size - 1)) + goto Ebadsize; + for (offs = limit; offsrec_len = cpu_to_le16(chunk_size); + } + if (!limit) + goto out; + } + for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { + p = (ext2_dirent *)(kaddr + offs); + rec_len = le16_to_cpu(p->rec_len); + + if (rec_len < EXT2_DIR_REC_LEN(1)) + goto Eshort; + if (rec_len & 3) + goto Ealign; + if (rec_len < EXT2_DIR_REC_LEN(p->name_len)) + goto Enamelen; + if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) + goto Espan; + if (le32_to_cpu(p->inode) > max_inumber) + goto Einumber; + } + if (offs != limit) + goto Eend; +out: + SetPageChecked(page); + return; + + /* Too bad, we had an error */ + +Ebadsize: + ext2_error(sb, "ext2_check_page", + "size of directory #%lu is not a multiple of chunk size", + dir->i_ino + ); + goto fail; +Eshort: + error = "rec_len is smaller than minimal"; + goto bad_entry; +Ealign: + error = "unaligned directory entry"; + goto bad_entry; +Enamelen: + error = "rec_len is too small for name_len"; + goto bad_entry; +Espan: + error = "directory entry across blocks"; + goto bad_entry; +Einumber: + error = "inode out of bounds"; +bad_entry: + ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - " + "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", + dir->i_ino, error, (page->index<inode), + rec_len, p->name_len); + goto fail; +Eend: + p = (ext2_dirent *)(kaddr + offs); + ext2_error (sb, "ext2_check_page", + "entry in directory #%lu spans the page boundary" + "offset=%lu, inode=%lu", + dir->i_ino, (page->index<inode)); +fail: + SetPageChecked(page); + SetPageError(page); +} + +static struct page * ext2_get_page(struct inode *dir, unsigned long n) +{ + struct address_space *mapping = dir->i_mapping; + struct page *page = read_cache_page(mapping, n, + (filler_t*)mapping->a_ops->readpage, NULL); + if (!IS_ERR(page)) { + wait_on_page(page); + kmap(page); + if (!Page_Uptodate(page)) + goto fail; + if (!PageChecked(page)) + ext2_check_page(page); + if (PageError(page)) + goto fail; + } + return page; + +fail: + ext2_put_page(page); + return ERR_PTR(-EIO); +} + +/* + * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. + * + * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller. + */ +static inline int ext2_match (int len, const char * const name, + struct ext2_dir_entry_2 * de) +{ + if (len != de->name_len) + return 0; + if (!de->inode) + return 0; + return !memcmp(name, de->name, len); +} + +/* + * p is at least 6 bytes before the end of page + */ +static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) +{ + return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len)); +} + +static inline unsigned +ext2_validate_entry(char *base, unsigned offset, unsigned mask) +{ + ext2_dirent *de = (ext2_dirent*)(base + offset); + ext2_dirent *p = (ext2_dirent*)(base + (offset&mask)); + while ((char*)p < (char*)de) + p = ext2_next_entry(p); + return (char *)p - base; +} + +static unsigned char ext2_filetype_table[EXT2_FT_MAX] = { + [EXT2_FT_UNKNOWN] DT_UNKNOWN, + [EXT2_FT_REG_FILE] DT_REG, + [EXT2_FT_DIR] DT_DIR, + [EXT2_FT_CHRDEV] DT_CHR, + [EXT2_FT_BLKDEV] DT_BLK, + [EXT2_FT_FIFO] DT_FIFO, + [EXT2_FT_SOCK] DT_SOCK, + [EXT2_FT_SYMLINK] DT_LNK, }; -int ext2_check_dir_entry (const char * function, struct inode * dir, - struct ext2_dir_entry_2 * de, - struct buffer_head * bh, - unsigned long offset) -{ - const char * error_msg = NULL; - - if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1)) - error_msg = "rec_len is smaller than minimal"; - else if (le16_to_cpu(de->rec_len) % 4 != 0) - error_msg = "rec_len % 4 != 0"; - else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(de->name_len)) - error_msg = "rec_len is too small for name_len"; - else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) > - dir->i_sb->s_blocksize) - error_msg = "directory entry across blocks"; - else if (dir && le32_to_cpu(de->inode) > le32_to_cpu(dir->i_sb->u.ext2_sb.s_es->s_inodes_count)) - error_msg = "inode out of bounds"; - - if (error_msg != NULL) - ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - " - "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", - dir->i_ino, error_msg, offset, - (unsigned long) le32_to_cpu(de->inode), - le16_to_cpu(de->rec_len), de->name_len); - return error_msg == NULL ? 1 : 0; -} - -static int ext2_readdir(struct file * filp, - void * dirent, filldir_t filldir) -{ - int error = 0; - unsigned long offset, blk; - int i, num, stored; - struct buffer_head * bh, * tmp, * bha[16]; - struct ext2_dir_entry_2 * de; - struct super_block * sb; - int err; - struct inode *inode = filp->f_dentry->d_inode; +#define S_SHIFT 12 +static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = { + [S_IFREG >> S_SHIFT] EXT2_FT_REG_FILE, + [S_IFDIR >> S_SHIFT] EXT2_FT_DIR, + [S_IFCHR >> S_SHIFT] EXT2_FT_CHRDEV, + [S_IFBLK >> S_SHIFT] EXT2_FT_BLKDEV, + [S_IFIFO >> S_SHIFT] EXT2_FT_FIFO, + [S_IFSOCK >> S_SHIFT] EXT2_FT_SOCK, + [S_IFLNK >> S_SHIFT] EXT2_FT_SYMLINK, +}; + +static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode) +{ + mode_t mode = inode->i_mode; + if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; + else + de->file_type = 0; +} - sb = inode->i_sb; +static int +ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) +{ + loff_t pos = filp->f_pos; + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + unsigned offset = pos & ~PAGE_CACHE_MASK; + unsigned long n = pos >> PAGE_CACHE_SHIFT; + unsigned long npages = dir_pages(inode); + unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); + unsigned char *types = NULL; + int need_revalidate = (filp->f_version != inode->i_version); + + if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) + goto done; + + if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) + types = ext2_filetype_table; + + for ( ; n < npages; n++, offset = 0) { + char *kaddr, *limit; + ext2_dirent *de; + struct page *page = ext2_get_page(inode, n); - stored = 0; - bh = NULL; - offset = filp->f_pos & (sb->s_blocksize - 1); - - while (!error && !stored && filp->f_pos < inode->i_size) { - blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb); - bh = ext2_bread (inode, blk, 0, &err); - if (!bh) { - ext2_error (sb, "ext2_readdir", - "directory #%lu contains a hole at offset %lu", - inode->i_ino, (unsigned long)filp->f_pos); - filp->f_pos += sb->s_blocksize - offset; + if (IS_ERR(page)) continue; + kaddr = (char *)page_address(page); + if (need_revalidate) { + offset = ext2_validate_entry(kaddr, offset, chunk_mask); + need_revalidate = 0; } + de = (ext2_dirent *)(kaddr+offset); + limit = kaddr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1); + for ( ;(char*)de <= limit; de = ext2_next_entry(de)) + if (de->inode) { + int over; + unsigned char d_type = DT_UNKNOWN; - /* - * Do the readahead - */ - if (!offset) { - for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0; - i > 0; i--) { - tmp = ext2_getblk (inode, ++blk, 0, &err); - if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) - bha[num++] = tmp; - else - brelse (tmp); - } - if (num) { - ll_rw_block (READA, num, bha); - for (i = 0; i < num; i++) - brelse (bha[i]); - } - } - -revalidate: - /* If the dir block has changed since the last call to - * readdir(2), then we might be pointing to an invalid - * dirent right now. Scan from the start of the block - * to make sure. */ - if (filp->f_version != inode->i_version) { - for (i = 0; i < sb->s_blocksize && i < offset; ) { - de = (struct ext2_dir_entry_2 *) - (bh->b_data + i); - /* It's too expensive to do a full - * dirent test each time round this - * loop, but we do have to test at - * least that it is non-zero. A - * failure will be detected in the - * dirent test below. */ - if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1)) - break; - i += le16_to_cpu(de->rec_len); + if (types && de->file_type < EXT2_FT_MAX) + d_type = types[de->file_type]; + + offset = (char *)de - kaddr; + over = filldir(dirent, de->name, de->name_len, + (n<inode), d_type); + if (over) { + ext2_put_page(page); + goto done; + } } - offset = i; - filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) - | offset; - filp->f_version = inode->i_version; + ext2_put_page(page); + } + +done: + filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + filp->f_version = inode->i_version; + UPDATE_ATIME(inode); + return 0; +} + +/* + * ext2_find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the page in which the entry was found, and the entry itself + * (as a parameter - res_dir). Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. + */ +struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, + struct dentry *dentry, struct page ** res_page) +{ + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; + unsigned reclen = EXT2_DIR_REC_LEN(namelen); + unsigned long n; + unsigned long npages = dir_pages(dir); + struct page *page = NULL; + ext2_dirent * de; + + /* OFFSET_CACHE */ + *res_page = NULL; + + for (n = 0; n < npages; n++) { + char *kaddr; + page = ext2_get_page(dir, n); + if (IS_ERR(page)) + continue; + + kaddr = (char*)page_address(page); + de = (ext2_dirent *) kaddr; + kaddr += PAGE_CACHE_SIZE - reclen; + for ( ; (char *) de <= kaddr ; de = ext2_next_entry(de)) + if (ext2_match (namelen, name, de)) + goto found; + ext2_put_page(page); + } + return NULL; + +found: + *res_page = page; + return de; +} + +struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) +{ + struct page *page = ext2_get_page(dir, 0); + ext2_dirent *de = NULL; + + if (!IS_ERR(page)) { + de = ext2_next_entry((ext2_dirent *) page_address(page)); + *p = page; + } + return de; +} + +ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry) +{ + ino_t res = 0; + struct ext2_dir_entry_2 * de; + struct page *page; + + de = ext2_find_entry (dir, dentry, &page); + if (de) { + res = le32_to_cpu(de->inode); + kunmap(page); + page_cache_release(page); + } + return res; +} + +/* Releases the page */ +void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, + struct page *page, struct inode *inode) +{ + unsigned from = (char *)de-(char*)page_address(page); + unsigned to = from + le16_to_cpu(de->rec_len); + int err; + + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + BUG(); + de->inode = cpu_to_le32(inode->i_ino); + ext2_set_de_type (de, inode); + err = ext2_commit_chunk(page, from, to); + UnlockPage(page); + ext2_put_page(page); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); +} + +/* + * Parent is locked. + */ +int ext2_add_link (struct dentry *dentry, struct inode *inode) +{ + struct inode *dir = dentry->d_parent->d_inode; + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; + unsigned reclen = EXT2_DIR_REC_LEN(namelen); + unsigned short rec_len, name_len; + struct page *page = NULL; + ext2_dirent * de; + unsigned long npages = dir_pages(dir); + unsigned long n; + char *kaddr; + unsigned from, to; + int err; + + /* We take care of directory expansion in the same loop */ + for (n = 0; n <= npages; n++) { + page = ext2_get_page(dir, n); + err = PTR_ERR(page); + if (IS_ERR(page)) + goto out; + kaddr = (char*)page_address(page); + de = (ext2_dirent *)kaddr; + kaddr += PAGE_CACHE_SIZE - reclen; + while ((char *)de <= kaddr) { + err = -EEXIST; + if (ext2_match (namelen, name, de)) + goto out_page; + name_len = EXT2_DIR_REC_LEN(de->name_len); + rec_len = le16_to_cpu(de->rec_len); + if (!de->inode && rec_len >= reclen) + goto got_it; + if (rec_len >= name_len + reclen) + goto got_it; + de = (ext2_dirent *) ((char *) de + rec_len); } - - while (!error && filp->f_pos < inode->i_size - && offset < sb->s_blocksize) { - de = (struct ext2_dir_entry_2 *) (bh->b_data + offset); - if (!ext2_check_dir_entry ("ext2_readdir", inode, de, - bh, offset)) { - /* On error, skip the f_pos to the - next block. */ - filp->f_pos = (filp->f_pos | (sb->s_blocksize - 1)) - + 1; - brelse (bh); - return stored; - } - offset += le16_to_cpu(de->rec_len); - if (le32_to_cpu(de->inode)) { - /* We might block in the next section - * if the data destination is - * currently swapped out. So, use a - * version stamp to detect whether or - * not the directory has been modified - * during the copy operation. - */ - unsigned long version = filp->f_version; - unsigned char d_type = DT_UNKNOWN; + ext2_put_page(page); + } + BUG(); + return -EINVAL; - if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE) - && de->file_type < EXT2_FT_MAX) - d_type = ext2_filetype_table[de->file_type]; - error = filldir(dirent, de->name, - de->name_len, - filp->f_pos, le32_to_cpu(de->inode), - d_type); - if (error) - break; - if (version != filp->f_version) - goto revalidate; - stored ++; +got_it: + from = (char*)de - (char*)page_address(page); + to = from + rec_len; + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + goto out_unlock; + if (de->inode) { + ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); + de1->rec_len = cpu_to_le16(rec_len - name_len); + de->rec_len = cpu_to_le16(name_len); + de = de1; + } + de->name_len = namelen; + memcpy (de->name, name, namelen); + de->inode = cpu_to_le32(inode->i_ino); + ext2_set_de_type (de, inode); + err = ext2_commit_chunk(page, from, to); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); + /* OFFSET_CACHE */ +out_unlock: + UnlockPage(page); +out_page: + ext2_put_page(page); +out: + return err; +} + +/* + * ext2_delete_entry deletes a directory entry by merging it with the + * previous entry. Page is up-to-date. Releases the page. + */ +int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = (struct inode*)mapping->host; + char *kaddr = (char*)page_address(page); + unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); + unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len); + ext2_dirent * pde = NULL; + ext2_dirent * de = (ext2_dirent *) (kaddr + from); + int err; + + while ((char*)de < (char*)dir) { + pde = de; + de = ext2_next_entry(de); + } + if (pde) + from = (char*)pde - (char*)page_address(page); + lock_page(page); + err = mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + BUG(); + if (pde) + pde->rec_len = cpu_to_le16(to-from); + dir->inode = 0; + err = ext2_commit_chunk(page, from, to); + UnlockPage(page); + ext2_put_page(page); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); + return err; +} + +/* + * Set the first fragment of directory. + */ +int ext2_make_empty(struct inode *inode, struct inode *parent) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page = grab_cache_page(mapping, 0); + unsigned chunk_size = ext2_chunk_size(inode); + struct ext2_dir_entry_2 * de; + char *base; + int err; + + if (!page) + return -ENOMEM; + err = mapping->a_ops->prepare_write(NULL, page, 0, chunk_size); + if (err) + goto fail; + + base = (char*)page_address(page); + + de = (struct ext2_dir_entry_2 *) base; + de->name_len = 1; + de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1)); + memcpy (de->name, ".\0\0", 4); + de->inode = cpu_to_le32(inode->i_ino); + ext2_set_de_type (de, inode); + + de = (struct ext2_dir_entry_2 *) (base + EXT2_DIR_REC_LEN(1)); + de->name_len = 2; + de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1)); + de->inode = cpu_to_le32(parent->i_ino); + memcpy (de->name, "..\0", 4); + ext2_set_de_type (de, inode); + + err = ext2_commit_chunk(page, 0, chunk_size); +fail: + UnlockPage(page); + page_cache_release(page); + return err; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +int ext2_empty_dir (struct inode * inode) +{ + struct page *page = NULL; + unsigned long i, npages = dir_pages(inode); + + for (i = 0; i < npages; i++) { + char *kaddr; + ext2_dirent * de; + page = ext2_get_page(inode, i); + + if (IS_ERR(page)) + continue; + + kaddr = (char *)page_address(page); + de = (ext2_dirent *)kaddr; + kaddr += PAGE_CACHE_SIZE-EXT2_DIR_REC_LEN(1); + + while ((char *)de <= kaddr) { + if (de->inode != 0) { + /* check for . and .. */ + if (de->name[0] != '.') + goto not_empty; + if (de->name_len > 2) + goto not_empty; + if (de->name_len < 2) { + if (de->inode != + cpu_to_le32(inode->i_ino)) + goto not_empty; + } else if (de->name[1] != '.') + goto not_empty; } - filp->f_pos += le16_to_cpu(de->rec_len); + de = ext2_next_entry(de); } - offset = 0; - brelse (bh); + ext2_put_page(page); } - UPDATE_ATIME(inode); + return 1; + +not_empty: + ext2_put_page(page); return 0; } + +struct file_operations ext2_dir_operations = { + read: generic_read_dir, + readdir: ext2_readdir, + fsync: ext2_sync_file, +}; diff -u --recursive --new-file v2.4.5/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c --- v2.4.5/linux/fs/ext2/ialloc.c Fri Mar 23 12:15:07 2001 +++ linux/fs/ext2/ialloc.c Sun Jun 3 17:22:17 2001 @@ -199,10 +199,15 @@ lock_super (sb); es = sb->u.ext2_sb.s_es; - if (ino < EXT2_FIRST_INO(sb) || + is_directory = S_ISDIR(inode->i_mode); + + /* Do this BEFORE marking the inode not in use or returning an error */ + clear_inode (inode); + + if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { - ext2_error (sb, "free_inode", - "reserved inode or nonexistent inode"); + ext2_error (sb, "ext2_free_inode", + "reserved or nonexistent inode %lu", ino); goto error_return; } block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); @@ -210,13 +215,8 @@ bitmap_nr = load_inode_bitmap (sb, block_group); if (bitmap_nr < 0) goto error_return; - - bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; - is_directory = S_ISDIR(inode->i_mode); - - /* Do this BEFORE marking the inode not in use */ - clear_inode (inode); + bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; /* Ok, now we can actually update the inode bitmaps.. */ if (!ext2_clear_bit (bit, bh->b_data)) diff -u --recursive --new-file v2.4.5/linux/fs/ext2/inode.c linux/fs/ext2/inode.c --- v2.4.5/linux/fs/ext2/inode.c Thu Apr 26 10:44:10 2001 +++ linux/fs/ext2/inode.c Sun Jun 3 17:42:16 2001 @@ -230,11 +230,11 @@ * or when it reads all @depth-1 indirect blocks successfully and finds * the whole chain, all way to the data (returns %NULL, *err == 0). */ -static inline Indirect *ext2_get_branch(struct inode *inode, - int depth, - int *offsets, - Indirect chain[4], - int *err) +static Indirect *ext2_get_branch(struct inode *inode, + int depth, + int *offsets, + Indirect chain[4], + int *err) { kdev_t dev = inode->i_dev; int size = inode->i_sb->s_blocksize; @@ -574,82 +574,6 @@ goto reread; } -struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err) -{ - struct buffer_head dummy; - int error; - - dummy.b_state = 0; - dummy.b_blocknr = -1000; - error = ext2_get_block(inode, block, &dummy, create); - *err = error; - if (!error && buffer_mapped(&dummy)) { - struct buffer_head *bh; - bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); - if (buffer_new(&dummy)) { - lock_buffer(bh); - memset(bh->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - unlock_buffer(bh); - mark_buffer_dirty_inode(bh, inode); - } - return bh; - } - return NULL; -} - -struct buffer_head * ext2_bread (struct inode * inode, int block, - int create, int *err) -{ - struct buffer_head * bh; - int prev_blocks; - - prev_blocks = inode->i_blocks; - - bh = ext2_getblk (inode, block, create, err); - if (!bh) - return bh; - - /* - * If the inode has grown, and this is a directory, then perform - * preallocation of a few more blocks to try to keep directory - * fragmentation down. - */ - if (create && - S_ISDIR(inode->i_mode) && - inode->i_blocks > prev_blocks && - EXT2_HAS_COMPAT_FEATURE(inode->i_sb, - EXT2_FEATURE_COMPAT_DIR_PREALLOC)) { - int i; - struct buffer_head *tmp_bh; - - for (i = 1; - i < EXT2_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks; - i++) { - /* - * ext2_getblk will zero out the contents of the - * directory for us - */ - tmp_bh = ext2_getblk(inode, block+i, create, err); - if (!tmp_bh) { - brelse (bh); - return 0; - } - brelse (tmp_bh); - } - } - - if (buffer_uptodate(bh)) - return bh; - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); - if (buffer_uptodate(bh)) - return bh; - brelse (bh); - *err = -EIO; - return NULL; -} - static int ext2_writepage(struct page *page) { return block_write_full_page(page,ext2_get_block); @@ -1067,6 +991,7 @@ } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; + inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { if (!inode->i_blocks) inode->i_op = &ext2_fast_symlink_inode_operations; diff -u --recursive --new-file v2.4.5/linux/fs/ext2/namei.c linux/fs/ext2/namei.c --- v2.4.5/linux/fs/ext2/namei.c Thu Feb 15 12:32:21 2001 +++ linux/fs/ext2/namei.c Sun Jun 3 17:42:16 2001 @@ -1,5 +1,18 @@ /* - * linux/fs/ext2/namei.c + * linux/fs/ext2/namei.c + * + * Rewrite to pagecache. Almost all code had been changed, so blame me + * if the things go wrong. Please, send bug reports to viro@math.psu.edu + * + * Stuff here is basically a glue between the VFS and generic UNIXish + * filesystem that keeps everything in pagecache. All knowledge of the + * directory layout is in fs/ext2/dir.c - it turned out to be easily separatable + * and it's easier to debug that way. In principle we might want to + * generalize that a bit and turn it into a library. Or not. + * + * The only non-static object here is ext2_dir_inode_operations. + * + * TODO: get rid of kmap() use, add readahead. * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) @@ -14,344 +27,63 @@ * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 - * Directory entry file type support and forward compatibility hooks - * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998 */ #include #include -#include -#include - - +#include /* - * define how far ahead to read directories while searching them. + * Couple of helper functions - make the code slightly cleaner. */ -#define NAMEI_RA_CHUNKS 2 -#define NAMEI_RA_BLOCKS 4 -#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) -/* - * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. - * - * `len <= EXT2_NAME_LEN' is guaranteed by caller. - * `de != NULL' is guaranteed by caller. - */ -static inline int ext2_match (int len, const char * const name, - struct ext2_dir_entry_2 * de) +static inline void ext2_inc_count(struct inode *inode) { - if (len != de->name_len) - return 0; - if (!de->inode) - return 0; - return !memcmp(name, de->name, len); + inode->i_nlink++; + mark_inode_dirty(inode); } -/* - * ext2_find_entry() - * - * finds an entry in the specified directory with the wanted name. It - * returns the cache buffer in which the entry was found, and the entry - * itself (as a parameter - res_dir). It does NOT read the inode of the - * entry - you'll have to do that yourself if you want to. - */ -static struct buffer_head * ext2_find_entry (struct inode * dir, - const char * const name, int namelen, - struct ext2_dir_entry_2 ** res_dir) -{ - struct super_block * sb; - struct buffer_head * bh_use[NAMEI_RA_SIZE]; - struct buffer_head * bh_read[NAMEI_RA_SIZE]; - unsigned long offset; - int block, toread, i, err; - - *res_dir = NULL; - sb = dir->i_sb; - - if (namelen > EXT2_NAME_LEN) - return NULL; - - memset (bh_use, 0, sizeof (bh_use)); - toread = 0; - for (block = 0; block < NAMEI_RA_SIZE; ++block) { - struct buffer_head * bh; - - if ((block << EXT2_BLOCK_SIZE_BITS (sb)) >= dir->i_size) - break; - bh = ext2_getblk (dir, block, 0, &err); - bh_use[block] = bh; - if (bh && !buffer_uptodate(bh)) - bh_read[toread++] = bh; - } - - for (block = 0, offset = 0; offset < dir->i_size; block++) { - struct buffer_head * bh; - struct ext2_dir_entry_2 * de; - char * dlimit; - - if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { - ll_rw_block (READ, toread, bh_read); - toread = 0; - } - bh = bh_use[block % NAMEI_RA_SIZE]; - if (!bh) { -#if 0 - ext2_error (sb, "ext2_find_entry", - "directory #%lu contains a hole at offset %lu", - dir->i_ino, offset); -#endif - offset += sb->s_blocksize; - continue; - } - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - /* - * read error: all bets are off - */ - break; - } - - de = (struct ext2_dir_entry_2 *) bh->b_data; - dlimit = bh->b_data + sb->s_blocksize; - while ((char *) de < dlimit) { - /* this code is executed quadratically often */ - /* do minimal checking `by hand' */ - int de_len; - - if ((char *) de + namelen <= dlimit && - ext2_match (namelen, name, de)) { - /* found a match - - just to be sure, do a full check */ - if (!ext2_check_dir_entry("ext2_find_entry", - dir, de, bh, offset)) - goto failure; - for (i = 0; i < NAMEI_RA_SIZE; ++i) { - if (bh_use[i] != bh) - brelse (bh_use[i]); - } - *res_dir = de; - return bh; - } - /* prevent looping on a bad block */ - de_len = le16_to_cpu(de->rec_len); - if (de_len <= 0) - goto failure; - offset += de_len; - de = (struct ext2_dir_entry_2 *) - ((char *) de + de_len); - } +static inline void ext2_dec_count(struct inode *inode) +{ + inode->i_nlink--; + mark_inode_dirty(inode); +} - brelse (bh); - if (((block + NAMEI_RA_SIZE) << EXT2_BLOCK_SIZE_BITS (sb)) >= - dir->i_size) - bh = NULL; - else - bh = ext2_getblk (dir, block + NAMEI_RA_SIZE, 0, &err); - bh_use[block % NAMEI_RA_SIZE] = bh; - if (bh && !buffer_uptodate(bh)) - bh_read[toread++] = bh; +static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode) +{ + int err = ext2_add_link(dentry, inode); + if (!err) { + d_instantiate(dentry, inode); + return 0; } - -failure: - for (i = 0; i < NAMEI_RA_SIZE; ++i) - brelse (bh_use[i]); - return NULL; + ext2_dec_count(inode); + iput(inode); + return err; } +/* + * Methods themselves. + */ + static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry) { struct inode * inode; - struct ext2_dir_entry_2 * de; - struct buffer_head * bh; - + ino_t ino; + if (dentry->d_name.len > EXT2_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); + ino = ext2_inode_by_name(dir, dentry); inode = NULL; - if (bh) { - unsigned long ino = le32_to_cpu(de->inode); - brelse (bh); + if (ino) { inode = iget(dir->i_sb, ino); - - if (!inode) + if (!inode) return ERR_PTR(-EACCES); } d_add(dentry, inode); return NULL; } -#define S_SHIFT 12 -static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = { - [S_IFREG >> S_SHIFT] EXT2_FT_REG_FILE, - [S_IFDIR >> S_SHIFT] EXT2_FT_DIR, - [S_IFCHR >> S_SHIFT] EXT2_FT_CHRDEV, - [S_IFBLK >> S_SHIFT] EXT2_FT_BLKDEV, - [S_IFIFO >> S_SHIFT] EXT2_FT_FIFO, - [S_IFSOCK >> S_SHIFT] EXT2_FT_SOCK, - [S_IFLNK >> S_SHIFT] EXT2_FT_SYMLINK, -}; - -static inline void ext2_set_de_type(struct super_block *sb, - struct ext2_dir_entry_2 *de, - umode_t mode) { - if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; -} - -/* - * ext2_add_entry() - * - * adds a file entry to the specified directory. - */ -int ext2_add_entry (struct inode * dir, const char * name, int namelen, - struct inode *inode) -{ - unsigned long offset; - unsigned short rec_len; - struct buffer_head * bh; - struct ext2_dir_entry_2 * de, * de1; - struct super_block * sb; - int retval; - - sb = dir->i_sb; - - if (!namelen) - return -EINVAL; - bh = ext2_bread (dir, 0, 0, &retval); - if (!bh) - return retval; - rec_len = EXT2_DIR_REC_LEN(namelen); - offset = 0; - de = (struct ext2_dir_entry_2 *) bh->b_data; - while (1) { - if ((char *)de >= sb->s_blocksize + bh->b_data) { - brelse (bh); - bh = NULL; - bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &retval); - if (!bh) - return retval; - if (dir->i_size <= offset) { - if (dir->i_size == 0) { - brelse(bh); - return -ENOENT; - } - - ext2_debug ("creating next block\n"); - - de = (struct ext2_dir_entry_2 *) bh->b_data; - de->inode = 0; - de->rec_len = le16_to_cpu(sb->s_blocksize); - dir->i_size = offset + sb->s_blocksize; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - } else { - - ext2_debug ("skipping to next block\n"); - - de = (struct ext2_dir_entry_2 *) bh->b_data; - } - } - if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh, - offset)) { - brelse (bh); - return -ENOENT; - } - if (ext2_match (namelen, name, de)) { - brelse (bh); - return -EEXIST; - } - if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) || - (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) { - offset += le16_to_cpu(de->rec_len); - if (le32_to_cpu(de->inode)) { - de1 = (struct ext2_dir_entry_2 *) ((char *) de + - EXT2_DIR_REC_LEN(de->name_len)); - de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) - - EXT2_DIR_REC_LEN(de->name_len)); - de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); - de = de1; - } - de->file_type = EXT2_FT_UNKNOWN; - if (inode) { - de->inode = cpu_to_le32(inode->i_ino); - ext2_set_de_type(dir->i_sb, de, inode->i_mode); - } else - de->inode = 0; - de->name_len = namelen; - memcpy (de->name, name, namelen); - /* - * XXX shouldn't update any times until successful - * completion of syscall, but too many callers depend - * on this. - * - * XXX similarly, too many callers depend on - * ext2_new_inode() setting the times, but error - * recovery deletes the inode, so the worst that can - * happen is that the times are slightly out of date - * and/or different from the directory change time. - */ - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - dir->i_version = ++event; - mark_buffer_dirty_inode(bh, dir); - if (IS_SYNC(dir)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } - brelse(bh); - return 0; - } - offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - } - brelse (bh); - return -ENOSPC; -} - -/* - * ext2_delete_entry deletes a directory entry by merging it with the - * previous entry - */ -static int ext2_delete_entry (struct inode * dir, - struct ext2_dir_entry_2 * de_del, - struct buffer_head * bh) -{ - struct ext2_dir_entry_2 * de, * pde; - int i; - - i = 0; - pde = NULL; - de = (struct ext2_dir_entry_2 *) bh->b_data; - while (i < bh->b_size) { - if (!ext2_check_dir_entry ("ext2_delete_entry", NULL, - de, bh, i)) - return -EIO; - if (de == de_del) { - if (pde) - pde->rec_len = - cpu_to_le16(le16_to_cpu(pde->rec_len) + - le16_to_cpu(de->rec_len)); - else - de->inode = 0; - dir->i_version = ++event; - mark_buffer_dirty_inode(bh, dir); - if (IS_SYNC(dir)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } - return 0; - } - i += le16_to_cpu(de->rec_len); - pde = de; - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - } - return -ENOENT; -} - /* * By the time this is called, we already have created * the directory cache entry for the new file, but it @@ -364,454 +96,245 @@ { struct inode * inode = ext2_new_inode (dir, mode); int err = PTR_ERR(inode); - if (IS_ERR(inode)) - return err; - - inode->i_op = &ext2_file_inode_operations; - inode->i_fop = &ext2_file_operations; - inode->i_mapping->a_ops = &ext2_aops; - inode->i_mode = mode; - mark_inode_dirty(inode); - err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, - inode); - if (err) { - inode->i_nlink--; + if (!IS_ERR(inode)) { + inode->i_op = &ext2_file_inode_operations; + inode->i_fop = &ext2_file_operations; + inode->i_mapping->a_ops = &ext2_aops; mark_inode_dirty(inode); - iput (inode); - return err; + err = ext2_add_nondir(dentry, inode); } - d_instantiate(dentry, inode); - return 0; + return err; } static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) { struct inode * inode = ext2_new_inode (dir, mode); int err = PTR_ERR(inode); - - if (IS_ERR(inode)) - return err; - - inode->i_uid = current->fsuid; - init_special_inode(inode, mode, rdev); - err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, - inode); - if (err) - goto out_no_entry; - mark_inode_dirty(inode); - d_instantiate(dentry, inode); - return 0; - -out_no_entry: - inode->i_nlink--; - mark_inode_dirty(inode); - iput(inode); + if (!IS_ERR(inode)) { + init_special_inode(inode, mode, rdev); + mark_inode_dirty(inode); + err = ext2_add_nondir(dentry, inode); + } return err; } -static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) +static int ext2_symlink (struct inode * dir, struct dentry * dentry, + const char * symname) { + struct super_block * sb = dir->i_sb; + int err = -ENAMETOOLONG; + unsigned l = strlen(symname)+1; struct inode * inode; - struct buffer_head * dir_block; - struct ext2_dir_entry_2 * de; - int err; - if (dir->i_nlink >= EXT2_LINK_MAX) - return -EMLINK; + if (l > sb->s_blocksize) + goto out; - inode = ext2_new_inode (dir, S_IFDIR); + inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO); err = PTR_ERR(inode); if (IS_ERR(inode)) - return err; + goto out; - inode->i_op = &ext2_dir_inode_operations; - inode->i_fop = &ext2_dir_operations; - inode->i_size = inode->i_sb->s_blocksize; - inode->i_blocks = 0; - dir_block = ext2_bread (inode, 0, 1, &err); - if (!dir_block) { - inode->i_nlink--; /* is this nlink == 0? */ - mark_inode_dirty(inode); - iput (inode); - return err; + if (l > sizeof (inode->u.ext2_i.i_data)) { + /* slow symlink */ + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ext2_aops; + err = block_symlink(inode, symname, l); + if (err) + goto out_fail; + } else { + /* fast symlink */ + inode->i_op = &ext2_fast_symlink_inode_operations; + memcpy((char*)&inode->u.ext2_i.i_data,symname,l); + inode->i_size = l-1; } - de = (struct ext2_dir_entry_2 *) dir_block->b_data; - de->inode = cpu_to_le32(inode->i_ino); - de->name_len = 1; - de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); - strcpy (de->name, "."); - ext2_set_de_type(dir->i_sb, de, S_IFDIR); - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - de->inode = cpu_to_le32(dir->i_ino); - de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1)); - de->name_len = 2; - strcpy (de->name, ".."); - ext2_set_de_type(dir->i_sb, de, S_IFDIR); - inode->i_nlink = 2; - mark_buffer_dirty_inode(dir_block, dir); - brelse (dir_block); - inode->i_mode = S_IFDIR | mode; - if (dir->i_mode & S_ISGID) - inode->i_mode |= S_ISGID; mark_inode_dirty(inode); - err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, - inode); - if (err) - goto out_no_entry; - dir->i_nlink++; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - d_instantiate(dentry, inode); - return 0; -out_no_entry: - inode->i_nlink = 0; - mark_inode_dirty(inode); - iput (inode); + err = ext2_add_nondir(dentry, inode); +out: return err; -} -/* - * routine to check that the specified directory is empty (for rmdir) - */ -static int empty_dir (struct inode * inode) -{ - unsigned long offset; - struct buffer_head * bh; - struct ext2_dir_entry_2 * de, * de1; - struct super_block * sb; - int err; - - sb = inode->i_sb; - if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) || - !(bh = ext2_bread (inode, 0, 0, &err))) { - ext2_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no data block", - inode->i_ino); - return 1; - } - de = (struct ext2_dir_entry_2 *) bh->b_data; - de1 = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) || - strcmp (".", de->name) || strcmp ("..", de1->name)) { - ext2_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no `.' or `..'", - inode->i_ino); - brelse (bh); - return 1; - } - offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); - de = (struct ext2_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len)); - while (offset < inode->i_size ) { - if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { - brelse (bh); - bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err); - if (!bh) { -#if 0 - ext2_error (sb, "empty_dir", - "directory #%lu contains a hole at offset %lu", - inode->i_ino, offset); -#endif - offset += sb->s_blocksize; - continue; - } - de = (struct ext2_dir_entry_2 *) bh->b_data; - } - if (!ext2_check_dir_entry ("empty_dir", inode, de, bh, - offset)) { - brelse (bh); - return 1; - } - if (le32_to_cpu(de->inode)) { - brelse (bh); - return 0; - } - offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - } - brelse (bh); - return 1; +out_fail: + ext2_dec_count(inode); + iput (inode); + goto out; } -static int ext2_rmdir (struct inode * dir, struct dentry *dentry) +static int ext2_link (struct dentry * old_dentry, struct inode * dir, + struct dentry *dentry) { - int retval; - struct inode * inode; - struct buffer_head * bh; - struct ext2_dir_entry_2 * de; + struct inode *inode = old_dentry->d_inode; - retval = -ENOENT; - bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); - if (!bh) - goto end_rmdir; - - inode = dentry->d_inode; - DQUOT_INIT(inode); - - retval = -EIO; - if (le32_to_cpu(de->inode) != inode->i_ino) - goto end_rmdir; - - retval = -ENOTEMPTY; - if (!empty_dir (inode)) - goto end_rmdir; - - retval = ext2_delete_entry(dir, de, bh); - if (retval) - goto end_rmdir; - if (inode->i_nlink != 2) - ext2_warning (inode->i_sb, "ext2_rmdir", - "empty directory has nlink!=2 (%d)", - inode->i_nlink); - inode->i_version = ++event; - inode->i_nlink = 0; - inode->i_size = 0; - mark_inode_dirty(inode); - dir->i_nlink--; - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - -end_rmdir: - brelse (bh); - return retval; -} + if (S_ISDIR(inode->i_mode)) + return -EPERM; -static int ext2_unlink(struct inode * dir, struct dentry *dentry) -{ - int retval; - struct inode * inode; - struct buffer_head * bh; - struct ext2_dir_entry_2 * de; + if (inode->i_nlink >= EXT2_LINK_MAX) + return -EMLINK; - retval = -ENOENT; - bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); - if (!bh) - goto end_unlink; - - inode = dentry->d_inode; - DQUOT_INIT(inode); - - retval = -EIO; - if (le32_to_cpu(de->inode) != inode->i_ino) - goto end_unlink; - - if (!inode->i_nlink) { - ext2_warning (inode->i_sb, "ext2_unlink", - "Deleting nonexistent file (%lu), %d", - inode->i_ino, inode->i_nlink); - inode->i_nlink = 1; - } - retval = ext2_delete_entry(dir, de, bh); - if (retval) - goto end_unlink; - dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - inode->i_nlink--; - mark_inode_dirty(inode); - inode->i_ctime = dir->i_ctime; - retval = 0; + inode->i_ctime = CURRENT_TIME; + ext2_inc_count(inode); + atomic_inc(&inode->i_count); -end_unlink: - brelse (bh); - return retval; + return ext2_add_nondir(dentry, inode); } -static int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname) +static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; - int l, err; + int err = -EMLINK; - l = strlen(symname)+1; - if (l > dir->i_sb->s_blocksize) - return -ENAMETOOLONG; + if (dir->i_nlink >= EXT2_LINK_MAX) + goto out; + + ext2_inc_count(dir); - inode = ext2_new_inode (dir, S_IFLNK); + inode = ext2_new_inode (dir, S_IFDIR | mode); err = PTR_ERR(inode); if (IS_ERR(inode)) - return err; + goto out_dir; - inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &ext2_dir_inode_operations; + inode->i_fop = &ext2_dir_operations; + inode->i_mapping->a_ops = &ext2_aops; - if (l > sizeof (inode->u.ext2_i.i_data)) { - inode->i_op = &page_symlink_inode_operations; - inode->i_mapping->a_ops = &ext2_aops; - err = block_symlink(inode, symname, l); - if (err) - goto out_no_entry; - } else { - inode->i_op = &ext2_fast_symlink_inode_operations; - memcpy((char*)&inode->u.ext2_i.i_data,symname,l); - inode->i_size = l-1; - } - mark_inode_dirty(inode); + ext2_inc_count(inode); - err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, - inode); + err = ext2_make_empty(inode, dir); if (err) - goto out_no_entry; - d_instantiate(dentry, inode); - return 0; + goto out_fail; -out_no_entry: - inode->i_nlink--; - mark_inode_dirty(inode); - iput (inode); + err = ext2_add_link(dentry, inode); + if (err) + goto out_fail; + + d_instantiate(dentry, inode); +out: return err; + +out_fail: + ext2_dec_count(inode); + ext2_dec_count(inode); + iput(inode); +out_dir: + ext2_dec_count(dir); + goto out; } -static int ext2_link (struct dentry * old_dentry, - struct inode * dir, struct dentry *dentry) +static int ext2_unlink(struct inode * dir, struct dentry *dentry) { - struct inode *inode = old_dentry->d_inode; - int err; + struct inode * inode = dentry->d_inode; + struct ext2_dir_entry_2 * de; + struct page * page; + int err = -ENOENT; - if (S_ISDIR(inode->i_mode)) - return -EPERM; + de = ext2_find_entry (dir, dentry, &page); + if (!de) + goto out; - if (inode->i_nlink >= EXT2_LINK_MAX) - return -EMLINK; - - err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, - inode); + err = ext2_delete_entry (de, page); if (err) - return err; + goto out; - inode->i_nlink++; - inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - atomic_inc(&inode->i_count); - d_instantiate(dentry, inode); - return 0; + inode->i_ctime = dir->i_ctime; + ext2_dec_count(inode); + err = 0; +out: + return err; } -#define PARENT_INO(buffer) \ - ((struct ext2_dir_entry_2 *) ((char *) buffer + \ - le16_to_cpu(((struct ext2_dir_entry_2 *) buffer)->rec_len)))->inode - -/* - * Anybody can rename anything with this: the permission checks are left to the - * higher-level routines. - */ -static int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, - struct inode * new_dir,struct dentry *new_dentry) +static int ext2_rmdir (struct inode * dir, struct dentry *dentry) { - struct inode * old_inode, * new_inode; - struct buffer_head * old_bh, * new_bh, * dir_bh; - struct ext2_dir_entry_2 * old_de, * new_de; - int retval; - - old_bh = new_bh = dir_bh = NULL; - - old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); - /* - * Check for inode number is _not_ due to possible IO errors. - * We might rmdir the source, keep it as pwd of some process - * and merrily kill the link to whatever was created under the - * same name. Goodbye sticky bit ;-< - */ - old_inode = old_dentry->d_inode; - retval = -ENOENT; - if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino) - goto end_rename; - - new_inode = new_dentry->d_inode; - new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, - new_dentry->d_name.len, &new_de); - if (new_bh) { - if (!new_inode) { - brelse (new_bh); - new_bh = NULL; - } else { - DQUOT_INIT(new_inode); + struct inode * inode = dentry->d_inode; + int err = -ENOTEMPTY; + + if (ext2_empty_dir(inode)) { + err = ext2_unlink(dir, dentry); + if (!err) { + inode->i_size = 0; + ext2_dec_count(inode); + ext2_dec_count(dir); } } + return err; +} + +static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry ) +{ + struct inode * old_inode = old_dentry->d_inode; + struct inode * new_inode = new_dentry->d_inode; + struct page * dir_page = NULL; + struct ext2_dir_entry_2 * dir_de = NULL; + struct page * old_page; + struct ext2_dir_entry_2 * old_de; + int err = -ENOENT; + + old_de = ext2_find_entry (old_dir, old_dentry, &old_page); + if (!old_de) + goto out; + if (S_ISDIR(old_inode->i_mode)) { - if (new_inode) { - retval = -ENOTEMPTY; - if (!empty_dir (new_inode)) - goto end_rename; - } - retval = -EIO; - dir_bh = ext2_bread (old_inode, 0, 0, &retval); - if (!dir_bh) - goto end_rename; - if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) - goto end_rename; - retval = -EMLINK; - if (!new_inode && new_dir!=old_dir && - new_dir->i_nlink >= EXT2_LINK_MAX) - goto end_rename; + err = -EIO; + dir_de = ext2_dotdot(old_inode, &dir_page); + if (!dir_de) + goto out_old; } - if (!new_bh) { - retval = ext2_add_entry (new_dir, new_dentry->d_name.name, - new_dentry->d_name.len, - old_inode); - if (retval) - goto end_rename; - } else { - new_de->inode = le32_to_cpu(old_inode->i_ino); - if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - new_de->file_type = old_de->file_type; - new_dir->i_version = ++event; - mark_buffer_dirty_inode(new_bh, new_dir); - if (IS_SYNC(new_dir)) { - ll_rw_block (WRITE, 1, &new_bh); - wait_on_buffer (new_bh); - } - brelse(new_bh); - new_bh = NULL; - } - - /* - * Like most other Unix systems, set the ctime for inodes on a - * rename. - */ - old_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(old_inode); - - /* - * ok, that's it - */ - ext2_delete_entry(old_dir, old_de, old_bh); if (new_inode) { - new_inode->i_nlink--; + struct page *new_page; + struct ext2_dir_entry_2 *new_de; + + err = -ENOTEMPTY; + if (dir_de && !ext2_empty_dir (new_inode)) + goto out_dir; + + err = -ENOENT; + new_de = ext2_find_entry (new_dir, new_dentry, &new_page); + if (!new_de) + goto out_dir; + ext2_inc_count(old_inode); + ext2_set_link(new_dir, new_de, new_page, old_inode); new_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(new_inode); - } - old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(old_dir); - if (dir_bh) { - PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); - mark_buffer_dirty_inode(dir_bh, old_inode); - old_dir->i_nlink--; - mark_inode_dirty(old_dir); - if (new_inode) { + if (dir_de) new_inode->i_nlink--; - mark_inode_dirty(new_inode); - } else { - new_dir->i_nlink++; - new_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(new_dir); - } + ext2_dec_count(new_inode); + } else { + if (dir_de) { + err = -EMLINK; + if (new_dir->i_nlink >= EXT2_LINK_MAX) + goto out_dir; + } + ext2_inc_count(old_inode); + err = ext2_add_link(new_dentry, old_inode); + if (err) { + ext2_dec_count(old_inode); + goto out_dir; + } + if (dir_de) + ext2_inc_count(new_dir); + } + + ext2_delete_entry (old_de, old_page); + ext2_dec_count(old_inode); + + if (dir_de) { + ext2_set_link(old_inode, dir_de, dir_page, new_dir); + ext2_dec_count(old_dir); } + return 0; - retval = 0; -end_rename: - brelse (dir_bh); - brelse (old_bh); - brelse (new_bh); - return retval; +out_dir: + if (dir_de) { + kunmap(dir_page); + page_cache_release(dir_page); + } +out_old: + kunmap(old_page); + page_cache_release(old_page); +out: + return err; } -/* - * directories can handle most operations... - */ struct inode_operations ext2_dir_inode_operations = { create: ext2_create, lookup: ext2_lookup, diff -u --recursive --new-file v2.4.5/linux/fs/lockd/mon.c linux/fs/lockd/mon.c --- v2.4.5/linux/fs/lockd/mon.c Mon Oct 16 12:58:51 2000 +++ linux/fs/lockd/mon.c Sun Jun 3 19:07:28 2001 @@ -146,7 +146,7 @@ u32 addr = ntohl(argp->addr); dprintk("nsm: xdr_encode_mon(%08x, %d, %d, %d)\n", - htonl(argp->addr), htonl(argp->proc), + htonl(argp->addr), htonl(argp->prog), htonl(argp->vers), htonl(argp->proc)); /* diff -u --recursive --new-file v2.4.5/linux/fs/super.c linux/fs/super.c --- v2.4.5/linux/fs/super.c Fri May 25 18:28:55 2001 +++ linux/fs/super.c Sun Jun 3 15:24:35 2001 @@ -873,6 +873,7 @@ } spin_unlock(&dcache_lock); down_write(&sb->s_umount); + lock_kernel(); sb->s_root = NULL; /* Need to clean after the sucker */ if (fs->fs_flags & FS_LITTER) @@ -901,6 +902,7 @@ put_filesystem(fs); sb->s_type = NULL; unlock_super(sb); + unlock_kernel(); up_write(&sb->s_umount); if (bdev) { blkdev_put(bdev, BDEV_FS); diff -u --recursive --new-file v2.4.5/linux/include/asm-i386/softirq.h linux/include/asm-i386/softirq.h --- v2.4.5/linux/include/asm-i386/softirq.h Fri Apr 27 15:48:26 2001 +++ linux/include/asm-i386/softirq.h Mon Jun 4 19:06:59 2001 @@ -4,11 +4,14 @@ #include #include -#define cpu_bh_disable(cpu) do { local_bh_count(cpu)++; barrier(); } while (0) -#define cpu_bh_enable(cpu) do { barrier(); local_bh_count(cpu)--; } while (0) +#define __cpu_bh_enable(cpu) \ + do { barrier(); local_bh_count(cpu)--; } while (0) +#define cpu_bh_disable(cpu) \ + do { local_bh_count(cpu)++; barrier(); } while (0) #define local_bh_disable() cpu_bh_disable(smp_processor_id()) -#define local_bh_enable() cpu_bh_enable(smp_processor_id()) +#define __local_bh_enable() __cpu_bh_enable(smp_processor_id()) +#define local_bh_enable() do { if (!--local_bh_count(smp_processor_id()) && softirq_pending(smp_processor_id())) { do_softirq(); __sti(); } } while (0) #define in_softirq() (local_bh_count(smp_processor_id()) != 0) diff -u --recursive --new-file v2.4.5/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- v2.4.5/linux/include/linux/ext2_fs.h Fri Apr 27 15:48:20 2001 +++ linux/include/linux/ext2_fs.h Mon Jun 4 19:06:52 2001 @@ -498,16 +498,17 @@ * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ -#define EXT2_FT_UNKNOWN 0 -#define EXT2_FT_REG_FILE 1 -#define EXT2_FT_DIR 2 -#define EXT2_FT_CHRDEV 3 -#define EXT2_FT_BLKDEV 4 -#define EXT2_FT_FIFO 5 -#define EXT2_FT_SOCK 6 -#define EXT2_FT_SYMLINK 7 - -#define EXT2_FT_MAX 8 +enum { + EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, + EXT2_FT_DIR, + EXT2_FT_CHRDEV, + EXT2_FT_BLKDEV, + EXT2_FT_FIFO, + EXT2_FT_SOCK, + EXT2_FT_SYMLINK, + EXT2_FT_MAX +}; /* * EXT2_DIR_PAD defines the directory entries boundaries @@ -552,9 +553,6 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned); /* dir.c */ -extern int ext2_check_dir_entry (const char *, struct inode *, - struct ext2_dir_entry_2 *, struct buffer_head *, - unsigned long); /* file.c */ extern int ext2_read (struct inode *, struct file *, char *, int); @@ -613,6 +611,14 @@ /* dir.c */ extern struct file_operations ext2_dir_operations; +extern int ext2_add_link (struct dentry *, struct inode *); +extern ino_t ext2_inode_by_name(struct inode *, struct dentry *); +extern int ext2_make_empty(struct inode *, struct inode *); +extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **); +extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *); +extern int ext2_empty_dir (struct inode *); +extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **); +extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *); /* file.c */ extern struct inode_operations ext2_file_inode_operations; diff -u --recursive --new-file v2.4.5/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v2.4.5/linux/include/linux/interrupt.h Fri Apr 27 15:48:27 2001 +++ linux/include/linux/interrupt.h Mon Jun 4 19:07:00 2001 @@ -74,20 +74,15 @@ asmlinkage void do_softirq(void); extern void open_softirq(int nr, void (*action)(struct softirq_action*), void *data); +/* Locally cached atomic variables are cheaper than cli/sti */ static inline void __cpu_raise_softirq(int cpu, int nr) { - softirq_active(cpu) |= (1<state)) -#define tasklet_unlock_wait(t) while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); } #define tasklet_unlock(t) clear_bit(TASKLET_STATE_RUN, &(t)->state) -#else -#define tasklet_trylock(t) 1 -#define tasklet_unlock_wait(t) do { } while (0) -#define tasklet_unlock(t) do { } while (0) -#endif - -static inline void tasklet_schedule(struct tasklet_struct *t) -{ - if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { - int cpu = smp_processor_id(); - unsigned long flags; - - local_irq_save(flags); - t->next = tasklet_vec[cpu].list; - tasklet_vec[cpu].list = t; - __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ); - local_irq_restore(flags); - } -} - -static inline void tasklet_hi_schedule(struct tasklet_struct *t) -{ - if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { - int cpu = smp_processor_id(); - unsigned long flags; - - local_irq_save(flags); - t->next = tasklet_hi_vec[cpu].list; - tasklet_hi_vec[cpu].list = t; - __cpu_raise_softirq(cpu, HI_SOFTIRQ); - local_irq_restore(flags); - } -} +#define tasklet_unlock_wait(t) while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); } +extern void tasklet_schedule(struct tasklet_struct *t); +extern void tasklet_hi_schedule(struct tasklet_struct *t); static inline void tasklet_disable_nosync(struct tasklet_struct *t) { @@ -196,7 +159,14 @@ static inline void tasklet_enable(struct tasklet_struct *t) { - atomic_dec(&t->count); + if (atomic_dec_and_test(&t->count)) + tasklet_schedule(t); +} + +static inline void tasklet_hi_enable(struct tasklet_struct *t) +{ + if (atomic_dec_and_test(&t->count)) + tasklet_hi_schedule(t); } extern void tasklet_kill(struct tasklet_struct *t); diff -u --recursive --new-file v2.4.5/linux/include/linux/irq_cpustat.h linux/include/linux/irq_cpustat.h --- v2.4.5/linux/include/linux/irq_cpustat.h Fri Apr 27 15:48:20 2001 +++ linux/include/linux/irq_cpustat.h Mon Jun 4 19:06:52 2001 @@ -34,4 +34,7 @@ /* arch dependent irq_stat fields */ #define nmi_count(cpu) __IRQ_STAT((cpu), __nmi_count) /* i386, ia64 */ +#define softirq_pending(cpu) \ + ((softirq_active(cpu) & softirq_mask(cpu))) + #endif /* __irq_cpustat_h */ diff -u --recursive --new-file v2.4.5/linux/include/linux/mm.h linux/include/linux/mm.h --- v2.4.5/linux/include/linux/mm.h Fri May 25 18:28:55 2001 +++ linux/include/linux/mm.h Mon Jun 4 19:07:13 2001 @@ -167,6 +167,7 @@ #define PG_skip 10 #define PG_inactive_clean 11 #define PG_highmem 12 +#define PG_checked 13 /* kill me in 2.5.. */ /* bits 21-29 unused */ #define PG_arch_1 30 #define PG_reserved 31 @@ -181,6 +182,8 @@ #define PageLocked(page) test_bit(PG_locked, &(page)->flags) #define LockPage(page) set_bit(PG_locked, &(page)->flags) #define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags) +#define PageChecked(page) test_bit(PG_checked, &(page)->flags) +#define SetPageChecked(page) set_bit(PG_checked, &(page)->flags) extern void __set_page_dirty(struct page *); @@ -337,10 +340,10 @@ * can allocate highmem pages, the *get*page*() variants return * virtual kernel addresses to the allocated page(s). */ -extern struct page * FASTCALL(__alloc_pages(zonelist_t *zonelist, unsigned long order)); +extern struct page * FASTCALL(_alloc_pages(unsigned int gfp_mask, unsigned long order)); +extern struct page * FASTCALL(__alloc_pages(unsigned int gfp_mask, unsigned long order, zonelist_t *zonelist)); extern struct page * alloc_pages_node(int nid, int gfp_mask, unsigned long order); -#ifndef CONFIG_DISCONTIGMEM static inline struct page * alloc_pages(int gfp_mask, unsigned long order) { /* @@ -348,11 +351,8 @@ */ if (order >= MAX_ORDER) return NULL; - return __alloc_pages(contig_page_data.node_zonelists+(gfp_mask), order); + return _alloc_pages(gfp_mask, order); } -#else /* !CONFIG_DISCONTIGMEM */ -extern struct page * alloc_pages(int gfp_mask, unsigned long order); -#endif /* !CONFIG_DISCONTIGMEM */ #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) @@ -471,18 +471,17 @@ /* * GFP bitmasks.. */ -#define __GFP_WAIT 0x01 -#define __GFP_HIGH 0x02 -#define __GFP_IO 0x04 -#define __GFP_DMA 0x08 -#ifdef CONFIG_HIGHMEM -#define __GFP_HIGHMEM 0x10 -#else -#define __GFP_HIGHMEM 0x0 /* noop */ -#endif - +/* Zone modifiers in GFP_ZONEMASK (see linux/mmzone.h - low four bits) */ +#define __GFP_DMA 0x01 +#define __GFP_HIGHMEM 0x02 + +/* Action modifiers - doesn't change the zoning */ +#define __GFP_WAIT 0x10 +#define __GFP_HIGH 0x20 +#define __GFP_IO 0x40 +#define __GFP_BUFFER 0x80 -#define GFP_BUFFER (__GFP_HIGH | __GFP_WAIT) +#define GFP_BUFFER (__GFP_HIGH | __GFP_WAIT | __GFP_BUFFER) #define GFP_ATOMIC (__GFP_HIGH) #define GFP_USER ( __GFP_WAIT | __GFP_IO) #define GFP_HIGHUSER ( __GFP_WAIT | __GFP_IO | __GFP_HIGHMEM) diff -u --recursive --new-file v2.4.5/linux/include/linux/mmzone.h linux/include/linux/mmzone.h --- v2.4.5/linux/include/linux/mmzone.h Fri May 25 18:28:55 2001 +++ linux/include/linux/mmzone.h Mon Jun 4 19:06:54 2001 @@ -78,10 +78,9 @@ */ typedef struct zonelist_struct { zone_t * zones [MAX_NR_ZONES+1]; // NULL delimited - int gfp_mask; } zonelist_t; -#define NR_GFPINDEX 0x20 +#define GFP_ZONEMASK 0x0f /* * The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM @@ -97,7 +96,7 @@ struct bootmem_data; typedef struct pglist_data { zone_t node_zones[MAX_NR_ZONES]; - zonelist_t node_zonelists[NR_GFPINDEX]; + zonelist_t node_zonelists[GFP_ZONEMASK+1]; struct page *node_mem_map; unsigned long *valid_addr_bitmap; struct bootmem_data *bdata; diff -u --recursive --new-file v2.4.5/linux/include/linux/slab.h linux/include/linux/slab.h --- v2.4.5/linux/include/linux/slab.h Fri Apr 27 15:48:37 2001 +++ linux/include/linux/slab.h Mon Jun 4 19:07:21 2001 @@ -22,7 +22,7 @@ #define SLAB_NFS GFP_NFS #define SLAB_DMA GFP_DMA -#define SLAB_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO) +#define SLAB_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_BUFFER) #define SLAB_NO_GROW 0x00001000UL /* don't grow a cache */ /* flags to pass to kmem_cache_create(). diff -u --recursive --new-file v2.4.5/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.4.5/linux/kernel/ksyms.c Fri May 25 18:28:55 2001 +++ linux/kernel/ksyms.c Mon Jun 4 18:56:55 2001 @@ -90,17 +90,13 @@ EXPORT_SYMBOL(exit_sighand); /* internal kernel memory management */ +EXPORT_SYMBOL(_alloc_pages); EXPORT_SYMBOL(__alloc_pages); EXPORT_SYMBOL(alloc_pages_node); EXPORT_SYMBOL(__get_free_pages); EXPORT_SYMBOL(get_zeroed_page); EXPORT_SYMBOL(__free_pages); EXPORT_SYMBOL(free_pages); -#ifndef CONFIG_DISCONTIGMEM -EXPORT_SYMBOL(contig_page_data); -#else -EXPORT_SYMBOL(alloc_pages); -#endif EXPORT_SYMBOL(num_physpages); EXPORT_SYMBOL(kmem_find_general_cachep); EXPORT_SYMBOL(kmem_cache_create); diff -u --recursive --new-file v2.4.5/linux/kernel/sched.c linux/kernel/sched.c --- v2.4.5/linux/kernel/sched.c Fri Apr 20 18:26:16 2001 +++ linux/kernel/sched.c Mon Jun 4 15:57:49 2001 @@ -544,7 +544,7 @@ release_kernel_lock(prev, this_cpu); /* Do "administrative" work here while we don't hold any locks */ - if (softirq_active(this_cpu) & softirq_mask(this_cpu)) + if (softirq_pending(this_cpu)) goto handle_softirq; handle_softirq_back: diff -u --recursive --new-file v2.4.5/linux/kernel/softirq.c linux/kernel/softirq.c --- v2.4.5/linux/kernel/softirq.c Fri Dec 29 14:07:24 2000 +++ linux/kernel/softirq.c Mon Jun 4 15:57:49 2001 @@ -52,12 +52,12 @@ int cpu = smp_processor_id(); __u32 active, mask; + local_irq_disable(); if (in_interrupt()) - return; + goto out; local_bh_disable(); - local_irq_disable(); mask = softirq_mask(cpu); active = softirq_active(cpu) & mask; @@ -71,7 +71,6 @@ local_irq_enable(); h = softirq_vec; - mask &= ~active; do { if (active & 1) @@ -82,12 +81,13 @@ local_irq_disable(); - active = softirq_active(cpu); - if ((active &= mask) != 0) + active = softirq_active(cpu) & mask; + if (active) goto retry; } - local_bh_enable(); + __local_bh_enable(); +out: /* Leave with locally disabled hard irqs. It is critical to close * window for infinite recursion, while we help local bh count, @@ -121,6 +121,45 @@ struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned; +void tasklet_schedule(struct tasklet_struct *t) +{ + unsigned long flags; + int cpu; + + cpu = smp_processor_id(); + local_irq_save(flags); + /* + * If nobody is running it then add it to this CPU's + * tasklet queue. + */ + if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state) && + tasklet_trylock(t)) { + t->next = tasklet_vec[cpu].list; + tasklet_vec[cpu].list = t; + __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ); + tasklet_unlock(t); + } + local_irq_restore(flags); +} + +void tasklet_hi_schedule(struct tasklet_struct *t) +{ + unsigned long flags; + int cpu; + + cpu = smp_processor_id(); + local_irq_save(flags); + + if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state) && + tasklet_trylock(t)) { + t->next = tasklet_hi_vec[cpu].list; + tasklet_hi_vec[cpu].list = t; + __cpu_raise_softirq(cpu, HI_SOFTIRQ); + tasklet_unlock(t); + } + local_irq_restore(flags); +} + static void tasklet_action(struct softirq_action *a) { int cpu = smp_processor_id(); @@ -129,37 +168,37 @@ local_irq_disable(); list = tasklet_vec[cpu].list; tasklet_vec[cpu].list = NULL; - local_irq_enable(); - while (list != NULL) { + while (list) { struct tasklet_struct *t = list; list = list->next; - if (tasklet_trylock(t)) { - if (atomic_read(&t->count) == 0) { - clear_bit(TASKLET_STATE_SCHED, &t->state); - - t->func(t->data); - /* - * talklet_trylock() uses test_and_set_bit that imply - * an mb when it returns zero, thus we need the explicit - * mb only here: while closing the critical section. - */ -#ifdef CONFIG_SMP - smp_mb__before_clear_bit(); -#endif - tasklet_unlock(t); - continue; - } - tasklet_unlock(t); + /* + * A tasklet is only added to the queue while it's + * locked, so no other CPU can have this tasklet + * pending: + */ + if (!tasklet_trylock(t)) + BUG(); +repeat: + if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) + BUG(); + if (!atomic_read(&t->count)) { + local_irq_enable(); + t->func(t->data); + local_irq_disable(); + /* + * One more run if the tasklet got reactivated: + */ + if (test_bit(TASKLET_STATE_SCHED, &t->state)) + goto repeat; } - local_irq_disable(); - t->next = tasklet_vec[cpu].list; - tasklet_vec[cpu].list = t; - __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ); - local_irq_enable(); + tasklet_unlock(t); + if (test_bit(TASKLET_STATE_SCHED, &t->state)) + tasklet_schedule(t); } + local_irq_enable(); } @@ -174,39 +213,40 @@ local_irq_disable(); list = tasklet_hi_vec[cpu].list; tasklet_hi_vec[cpu].list = NULL; - local_irq_enable(); - while (list != NULL) { + while (list) { struct tasklet_struct *t = list; list = list->next; - if (tasklet_trylock(t)) { - if (atomic_read(&t->count) == 0) { - clear_bit(TASKLET_STATE_SCHED, &t->state); - - t->func(t->data); - tasklet_unlock(t); - continue; - } - tasklet_unlock(t); + if (!tasklet_trylock(t)) + BUG(); +repeat: + if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) + BUG(); + if (!atomic_read(&t->count)) { + local_irq_enable(); + t->func(t->data); + local_irq_disable(); + if (test_bit(TASKLET_STATE_SCHED, &t->state)) + goto repeat; } - local_irq_disable(); - t->next = tasklet_hi_vec[cpu].list; - tasklet_hi_vec[cpu].list = t; - __cpu_raise_softirq(cpu, HI_SOFTIRQ); - local_irq_enable(); + tasklet_unlock(t); + if (test_bit(TASKLET_STATE_SCHED, &t->state)) + tasklet_hi_schedule(t); } + local_irq_enable(); } void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data) { - t->func = func; - t->data = data; + t->next = NULL; t->state = 0; atomic_set(&t->count, 0); + t->func = func; + t->data = data; } void tasklet_kill(struct tasklet_struct *t) diff -u --recursive --new-file v2.4.5/linux/mm/filemap.c linux/mm/filemap.c --- v2.4.5/linux/mm/filemap.c Fri May 25 18:28:55 2001 +++ linux/mm/filemap.c Sun Jun 3 17:42:16 2001 @@ -529,7 +529,7 @@ if (PageLocked(page)) BUG(); - flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_dirty) | (1 << PG_referenced) | (1 << PG_arch_1)); + flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_dirty) | (1 << PG_referenced) | (1 << PG_arch_1) | (1 << PG_checked)); page->flags = flags | (1 << PG_locked); page_cache_get(page); page->index = offset; diff -u --recursive --new-file v2.4.5/linux/mm/numa.c linux/mm/numa.c --- v2.4.5/linux/mm/numa.c Mon Oct 16 15:25:45 2000 +++ linux/mm/numa.c Mon Jun 4 19:04:01 2001 @@ -34,7 +34,7 @@ struct page * alloc_pages_node(int nid, int gfp_mask, unsigned long order) { #ifdef CONFIG_NUMA - return __alloc_pages(NODE_DATA(nid)->node_zonelists + gfp_mask, order); + return __alloc_pages(gfp_mask, order, NODE_DATA(nid)->node_zonelists + (gfp_mask & GFP_ZONEMASK)); #else return alloc_pages(gfp_mask, order); #endif @@ -85,14 +85,14 @@ static struct page * alloc_pages_pgdat(pg_data_t *pgdat, int gfp_mask, unsigned long order) { - return __alloc_pages(pgdat->node_zonelists + gfp_mask, order); + return __alloc_pages(gfp_mask, order, pgdat->node_zonelists + (gfp_mask & GFP_ZONEMASK)); } /* * This can be refined. Currently, tries to do round robin, instead * should do concentratic circle search, starting from current node. */ -struct page * alloc_pages(int gfp_mask, unsigned long order) +struct page * _alloc_pages(int gfp_mask, unsigned long order) { struct page *ret = 0; pg_data_t *start, *temp; diff -u --recursive --new-file v2.4.5/linux/mm/page_alloc.c linux/mm/page_alloc.c --- v2.4.5/linux/mm/page_alloc.c Fri May 25 18:28:55 2001 +++ linux/mm/page_alloc.c Mon Jun 4 19:04:42 2001 @@ -268,15 +268,21 @@ return NULL; } +#ifndef CONFIG_DISCONTIGMEM +struct page *_alloc_pages(unsigned int gfp_mask, unsigned long order) +{ + return __alloc_pages(gfp_mask, order, + contig_page_data.node_zonelists+(gfp_mask & GFP_ZONEMASK)); +} +#endif /* * This is the 'heart' of the zoned buddy allocator: */ -struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order) +struct page * __alloc_pages(unsigned int gfp_mask, unsigned long order, zonelist_t *zonelist) { zone_t **zone; int direct_reclaim = 0; - unsigned int gfp_mask = zonelist->gfp_mask; struct page * page; /* @@ -428,18 +434,26 @@ } /* * When we arrive here, we are really tight on memory. + * Since kswapd didn't succeed in freeing pages for us, + * we try to help it. * - * We try to free pages ourselves by: - * - shrinking the i/d caches. - * - reclaiming unused memory from the slab caches. - * - swapping/syncing pages to disk (done by page_launder) - * - moving clean pages from the inactive dirty list to - * the inactive clean list. (done by page_launder) + * Single page allocs loop until the allocation succeeds. + * Multi-page allocs can fail due to memory fragmentation; + * in that case we bail out to prevent infinite loops and + * hanging device drivers ... + * + * Another issue are GFP_BUFFER allocations; because they + * do not have __GFP_IO set it's possible we cannot make + * any progress freeing pages, in that case it's better + * to give up than to deadlock the kernel looping here. */ if (gfp_mask & __GFP_WAIT) { memory_pressure++; - try_to_free_pages(gfp_mask); - goto try_again; + if (!order || free_shortage()) { + int progress = try_to_free_pages(gfp_mask); + if (progress || gfp_mask & __GFP_IO) + goto try_again; + } } } @@ -671,14 +685,13 @@ { int i, j, k; - for (i = 0; i < NR_GFPINDEX; i++) { + for (i = 0; i <= GFP_ZONEMASK; i++) { zonelist_t *zonelist; zone_t *zone; zonelist = pgdat->node_zonelists + i; memset(zonelist, 0, sizeof(*zonelist)); - zonelist->gfp_mask = i; j = 0; k = ZONE_NORMAL; if (i & __GFP_HIGHMEM) diff -u --recursive --new-file v2.4.5/linux/mm/vmscan.c linux/mm/vmscan.c --- v2.4.5/linux/mm/vmscan.c Fri May 25 18:28:55 2001 +++ linux/mm/vmscan.c Mon Jun 4 19:15:13 2001 @@ -428,19 +428,14 @@ * go out to Matthew Dillon. */ #define MAX_LAUNDER (4 * (1 << page_cluster)) +#define CAN_DO_IO (gfp_mask & __GFP_IO) +#define CAN_DO_BUFFERS (gfp_mask & __GFP_BUFFER) int page_launder(int gfp_mask, int sync) { int launder_loop, maxscan, cleaned_pages, maxlaunder; - int can_get_io_locks; struct list_head * page_lru; struct page * page; - /* - * We can only grab the IO locks (eg. for flushing dirty - * buffers to disk) if __GFP_IO is set. - */ - can_get_io_locks = gfp_mask & __GFP_IO; - launder_loop = 0; maxlaunder = 0; cleaned_pages = 0; @@ -463,6 +458,7 @@ /* Page is or was in use? Move it to the active list. */ if (PageReferenced(page) || page->age > 0 || + page->zone->free_pages > page->zone->pages_high || (!page->buffers && page_count(page) > 1) || page_ramdisk(page)) { del_page_from_inactive_dirty_list(page); @@ -491,7 +487,7 @@ goto page_active; /* First time through? Move it to the back of the list */ - if (!launder_loop) { + if (!launder_loop || !CAN_DO_IO) { list_del(page_lru); list_add(page_lru, &inactive_dirty_list); UnlockPage(page); @@ -621,7 +617,7 @@ * loads, flush out the dirty pages before we have to wait on * IO. */ - if (can_get_io_locks && !launder_loop && free_shortage()) { + if ((CAN_DO_IO || CAN_DO_BUFFERS) && !launder_loop && free_shortage()) { launder_loop = 1; /* If we cleaned pages, never do synchronous IO. */ if (cleaned_pages) @@ -655,24 +651,10 @@ /* * When we are background aging, we try to increase the page aging - * information in the system. When we have too many inactive pages - * we don't do background aging since having all pages on the - * inactive list decreases aging information. - * - * Since not all active pages have to be on the active list, we round - * nr_active_pages up to num_physpages/2, if needed. + * information in the system. */ - if (!target) { - int inactive = nr_free_pages() + nr_inactive_clean_pages() + - nr_inactive_dirty_pages; - int active = MAX(nr_active_pages, num_physpages / 2); - if (active > 10 * inactive) - maxscan = nr_active_pages >> 4; - else if (active > 3 * inactive) - maxscan = nr_active_pages >> 8; - else - return 0; - } + if (!target) + maxscan = nr_active_pages >> 4; /* Take the lock while messing with the list... */ spin_lock(&pagemap_lru_lock); diff -u --recursive --new-file v2.4.5/linux/net/core/dev.c linux/net/core/dev.c --- v2.4.5/linux/net/core/dev.c Fri May 25 18:28:55 2001 +++ linux/net/core/dev.c Mon Jun 4 15:57:49 2001 @@ -1278,7 +1278,7 @@ ret = pt->func(skb, skb->dev, pt); - tasklet_enable(bh_task_vec+TIMER_BH); + tasklet_hi_enable(bh_task_vec+TIMER_BH); spin_unlock(&net_bh_lock); return ret; }