Commit 7f98bfdb authored by Linus Torvalds's avatar Linus Torvalds

Import 2.1.65

parent 3e213f64
...@@ -451,6 +451,17 @@ S: 1123 North Oak Park Avenue ...@@ -451,6 +451,17 @@ S: 1123 North Oak Park Avenue
S: Oak Park, Illinois 60302 S: Oak Park, Illinois 60302
S: USA S: USA
N: Jim Freeman
E: jfree@caldera.com
W: http://www.sovereign.org/
D: Initial GPL'd Frame Relay driver
D: Dynamic PPP devices
D: Sundry modularizations (PPP, IPX, ...) and fixes
S: Caldera, Inc.
S: 240 West Center St.
S: Orem, Utah 84059-1920
S: USA
N: Bob Frey N: Bob Frey
E: bobf@advansys.com E: bobf@advansys.com
D: AdvanSys SCSI driver D: AdvanSys SCSI driver
......
VERSION = 2 VERSION = 2
PATCHLEVEL = 1 PATCHLEVEL = 1
SUBLEVEL = 64 SUBLEVEL = 65
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
......
...@@ -349,6 +349,10 @@ ENTRY(page_fault) ...@@ -349,6 +349,10 @@ ENTRY(page_fault)
pushl $ SYMBOL_NAME(do_page_fault) pushl $ SYMBOL_NAME(do_page_fault)
jmp error_code jmp error_code
ENTRY(page_fault_f00f)
pushl $ SYMBOL_NAME(do_page_fault_f00f)
jmp error_code
ENTRY(spurious_interrupt_bug) ENTRY(spurious_interrupt_bug)
pushl $0 pushl $0
pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
......
...@@ -103,6 +103,7 @@ asmlinkage void segment_not_present(void); ...@@ -103,6 +103,7 @@ asmlinkage void segment_not_present(void);
asmlinkage void stack_segment(void); asmlinkage void stack_segment(void);
asmlinkage void general_protection(void); asmlinkage void general_protection(void);
asmlinkage void page_fault(void); asmlinkage void page_fault(void);
asmlinkage void page_fault_f00f(void);
asmlinkage void coprocessor_error(void); asmlinkage void coprocessor_error(void);
asmlinkage void reserved(void); asmlinkage void reserved(void);
asmlinkage void alignment_check(void); asmlinkage void alignment_check(void);
...@@ -417,6 +418,14 @@ __initfunc(void trap_init_f00f_bug(void)) ...@@ -417,6 +418,14 @@ __initfunc(void trap_init_f00f_bug(void))
{ {
unsigned long page; unsigned long page;
/*
* We use a special page fault handler, to actually detect
* 'bounced' traps/exceptions #0-6. This new page fault
* handler is a few tens of cycles slower than the 'normal'
* one.
*/
set_trap_gate(14,&page_fault_f00f);
/* /*
* Allocate a new page in virtual address space, * Allocate a new page in virtual address space,
* and move the IDT to have entry #7 starting at * and move the IDT to have entry #7 starting at
...@@ -433,6 +442,7 @@ __initfunc(void trap_init_f00f_bug(void)) ...@@ -433,6 +442,7 @@ __initfunc(void trap_init_f00f_bug(void))
*/ */
idt = (struct desc_struct *)(page - 7*8); idt = (struct desc_struct *)(page - 7*8);
__asm__ __volatile__("lidt %0": "=m" (idt_descr)); __asm__ __volatile__("lidt %0": "=m" (idt_descr));
} }
......
...@@ -74,15 +74,6 @@ int __verify_write(const void * addr, unsigned long size) ...@@ -74,15 +74,6 @@ int __verify_write(const void * addr, unsigned long size)
return 0; return 0;
} }
asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
asmlinkage void do_debug (struct pt_regs *, unsigned long);
asmlinkage void do_nmi (struct pt_regs *, unsigned long);
asmlinkage void do_int3 (struct pt_regs *, unsigned long);
asmlinkage void do_overflow (struct pt_regs *, unsigned long);
asmlinkage void do_bounds (struct pt_regs *, unsigned long);
asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
extern int pentium_f00f_bug;
/* /*
* This routine handles page faults. It determines the address, * This routine handles page faults. It determines the address,
...@@ -94,45 +85,17 @@ extern int pentium_f00f_bug; ...@@ -94,45 +85,17 @@ extern int pentium_f00f_bug;
* bit 1 == 0 means read, 1 means write * bit 1 == 0 means read, 1 means write
* bit 2 == 0 means kernel, 1 means user-mode * bit 2 == 0 means kernel, 1 means user-mode
*/ */
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) static void __do_page_fault(struct pt_regs *regs, unsigned long error_code,
unsigned long address)
{ {
struct task_struct *tsk; struct task_struct *tsk;
struct mm_struct *mm; struct mm_struct *mm;
struct vm_area_struct * vma; struct vm_area_struct * vma;
unsigned long address;
unsigned long page; unsigned long page;
unsigned long fixup; unsigned long fixup;
int write; int write;
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
/*
* Pentium F0 0F C7 C8 bug workaround. Do this first,
* to make sure we don't have locking problems with
* asynchronous traps (ie NMI).
*/
if ( !(error_code & 7) && pentium_f00f_bug ) {
unsigned long nr;
nr = (address - (unsigned long) idt) >> 3;
if (nr < 7) {
static void (*handler[])(struct pt_regs *, unsigned long) = {
do_divide_error, /* 0 - divide overflow */
do_debug, /* 1 - debug trap */
do_nmi, /* 2 - NMI */
do_int3, /* 3 - int 3 */
do_overflow, /* 4 - overflow */
do_bounds, /* 5 - bound range */
do_invalid_op }; /* 6 - invalid opcode */
handler[nr](regs, 0);
return;
}
}
lock_kernel(); lock_kernel();
tsk = current; tsk = current;
mm = tsk->mm; mm = tsk->mm;
...@@ -253,3 +216,65 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) ...@@ -253,3 +216,65 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
out: out:
unlock_kernel(); unlock_kernel();
} }
/*
* One of these two functions is the real page fault handler, which one depends
* on wether the CPU has the F00F bug:
*/
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
unsigned long address;
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
__do_page_fault(regs, error_code, address);
}
asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
asmlinkage void do_debug (struct pt_regs *, unsigned long);
asmlinkage void do_nmi (struct pt_regs *, unsigned long);
asmlinkage void do_int3 (struct pt_regs *, unsigned long);
asmlinkage void do_overflow (struct pt_regs *, unsigned long);
asmlinkage void do_bounds (struct pt_regs *, unsigned long);
asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
extern int pentium_f00f_bug;
asmlinkage void do_page_fault_f00f(struct pt_regs *regs, unsigned long error_code)
{
unsigned long address;
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
/*
* Pentium F0 0F C7 C8 bug workaround. Do this first,
* to make sure we don't have locking problems with
* asynchronous traps (ie NMI).
*/
if ( !(error_code & 5) && pentium_f00f_bug ) {
unsigned long nr;
nr = (address - (unsigned long) idt) >> 3;
if (nr < 7) {
static void (*handler[])(struct pt_regs *, unsigned long) = {
do_divide_error, /* 0 - divide overflow */
do_debug, /* 1 - debug trap */
do_nmi, /* 2 - NMI */
do_int3, /* 3 - int 3 */
do_overflow, /* 4 - overflow */
do_bounds, /* 5 - bound range */
do_invalid_op }; /* 6 - invalid opcode */
if (nr == 3 || nr == 4) regs->eip++;
handler[nr](regs, 0);
return;
}
}
__do_page_fault(regs, error_code, address);
}
...@@ -4,7 +4,7 @@ FAQ list: ...@@ -4,7 +4,7 @@ FAQ list:
========= =========
A FAQ list may be found in the fdutils package (see below), and also A FAQ list may be found in the fdutils package (see below), and also
at http://www.club.innet.lu/~year3160/floppy/FAQ.html at http://poboxes.com/Alain.Knaff/floppy/FAQ.html
Lilo config options (Thinkpad users, read this) Lilo config options (Thinkpad users, read this)
......
...@@ -3274,7 +3274,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ...@@ -3274,7 +3274,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
return 0; return 0;
case BLKRAGET: case BLKRAGET:
return put_user(read_ahead[MAJOR(inode->i_rdev)], return put_user(read_ahead[MAJOR(inode->i_rdev)],
(int *) param); (long *) param);
case BLKFLSBUF: case BLKFLSBUF:
if(!suser()) return -EACCES; if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev); fsync_dev(inode->i_rdev);
...@@ -3283,7 +3283,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ...@@ -3283,7 +3283,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
case BLKGETSIZE: case BLKGETSIZE:
ECALL(get_floppy_geometry(drive, type, &g)); ECALL(get_floppy_geometry(drive, type, &g));
return put_user(g->size, (int *) param); return put_user(g->size, (long *) param);
/* BLKRRPART is not defined as floppies don't have /* BLKRRPART is not defined as floppies don't have
* partition tables */ * partition tables */
} }
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
*/ */
static const char *version = static const char *version =
"eepro100.c:v0.34 8/30/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n"; "eepro100.c:v0.36 10/20/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
/* A few user-configurable values that apply to all boards. /* A few user-configurable values that apply to all boards.
First set are undocumented and spelled per Intel recommendations. */ First set are undocumented and spelled per Intel recommendations. */
...@@ -27,13 +27,16 @@ static const char *version = ...@@ -27,13 +27,16 @@ static const char *version =
static int congenb = 0; /* Enable congestion control in the DP83840. */ static int congenb = 0; /* Enable congestion control in the DP83840. */
static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */ static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */ /* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */ static int txdmacount = 128;
static int rxdmacount = 0;
/* If defined use the copy-only-tiny-buffer scheme for higher performance. /* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
The value sets the copy breakpoint. Lower uses more memory, but is Lower values use more memory, but are faster. */
faster. */ static int rx_copybreak = 200;
#define SKBUFF_RX_COPYBREAK 256
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
#include <linux/config.h> #include <linux/config.h>
#ifdef MODULE #ifdef MODULE
...@@ -161,7 +164,7 @@ also has simplified Tx and Rx buffer modes. This driver uses the "flexible" ...@@ -161,7 +164,7 @@ also has simplified Tx and Rx buffer modes. This driver uses the "flexible"
Tx mode, but in a simplified lower-overhead manner: it associates only a Tx mode, but in a simplified lower-overhead manner: it associates only a
single buffer descriptor with each frame descriptor. single buffer descriptor with each frame descriptor.
Despite the extra space overhead in each recieve skbuff, the driver must use Despite the extra space overhead in each receive skbuff, the driver must use
the simplified Rx buffer mode to assure that only a single data buffer is the simplified Rx buffer mode to assure that only a single data buffer is
associated with each RxFD. The driver implements this by reserving space associated with each RxFD. The driver implements this by reserving space
for the Rx descriptor at the head of each Rx skbuff for the Rx descriptor at the head of each Rx skbuff
...@@ -273,9 +276,6 @@ having to sign an Intel NDA when I'm helping Intel sell their own product! ...@@ -273,9 +276,6 @@ having to sign an Intel NDA when I'm helping Intel sell their own product!
/* Time in jiffies before concluding the transmitter is hung. */ /* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT ((400*HZ)/1000) #define TX_TIMEOUT ((400*HZ)/1000)
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
#define INTR_WORK 16
/* How to wait for the command unit to accept a command. /* How to wait for the command unit to accept a command.
Typically this takes 0 ticks. */ Typically this takes 0 ticks. */
static inline void wait_for_cmd_done(int cmd_ioaddr) static inline void wait_for_cmd_done(int cmd_ioaddr)
...@@ -450,6 +450,9 @@ static int speedo_rx(struct device *dev); ...@@ -450,6 +450,9 @@ static int speedo_rx(struct device *dev);
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
static int speedo_close(struct device *dev); static int speedo_close(struct device *dev);
static struct enet_statistics *speedo_get_stats(struct device *dev); static struct enet_statistics *speedo_get_stats(struct device *dev);
#ifdef HAVE_PRIVATE_IOCTL
static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd);
#endif
static void set_rx_mode(struct device *dev); static void set_rx_mode(struct device *dev);
...@@ -458,24 +461,21 @@ static void set_rx_mode(struct device *dev); ...@@ -458,24 +461,21 @@ static void set_rx_mode(struct device *dev);
/* 'options' is used to pass a transceiver override or full-duplex flag /* 'options' is used to pass a transceiver override or full-duplex flag
e.g. "options=16" for FD, "options=32" for 100mbps-only. */ e.g. "options=16" for FD, "options=32" for 100mbps-only. */
static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
#ifdef MODULE #ifdef MODULE
static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int debug = -1; /* The debug level */ static int debug = -1; /* The debug level */
#endif
/* A list of all installed Speedo devices, for removing the driver module. */ /* A list of all installed Speedo devices, for removing the driver module. */
static struct device *root_speedo_dev = NULL; static struct device *root_speedo_dev = NULL;
#endif
int eepro100_init(struct device *dev) int eepro100_init(struct device *dev)
{ {
int cards_found = 0; int cards_found = 0;
if (pcibios_present()) { if (pcibios_present()) {
int pci_index; static int pci_index = 0;
for (pci_index = 0; pci_index < 8; pci_index++) { for (; pci_index < 8; pci_index++) {
unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency; unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) #if (LINUX_VERSION_CODE >= VERSION(1,3,44))
int pci_ioaddr; int pci_ioaddr;
...@@ -539,6 +539,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -539,6 +539,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
{ {
static int did_version = 0; /* Already printed version info. */ static int did_version = 0; /* Already printed version info. */
struct speedo_private *sp; struct speedo_private *sp;
char *product;
int i; int i;
u16 eeprom[0x40]; u16 eeprom[0x40];
...@@ -558,7 +559,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -558,7 +559,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
u16 sum = 0; u16 sum = 0;
int j; int j;
for (j = 0, i = 0; i < 0x40; i++) { for (j = 0, i = 0; i < 0x40; i++) {
unsigned short value = read_eeprom(ioaddr, i); u16 value = read_eeprom(ioaddr, i);
eeprom[i] = value; eeprom[i] = value;
sum += value; sum += value;
if (i < 3) { if (i < 3) {
...@@ -579,8 +580,13 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -579,8 +580,13 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
action. */ action. */
outl(0, ioaddr + SCBPort); outl(0, ioaddr + SCBPort);
printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ", if (eeprom[3] & 0x0100)
dev->name, ioaddr); product = "OEM i82557/i82558 10/100 Ethernet";
else
product = "Intel EtherExpress Pro 10/100";
printk(KERN_INFO "%s: %s at %#3x, ", dev->name, product, ioaddr);
for (i = 0; i < 5; i++) for (i = 0; i < 5; i++)
printk("%2.2X:", dev->dev_addr[i]); printk("%2.2X:", dev->dev_addr[i]);
printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq); printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
...@@ -592,7 +598,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -592,7 +598,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
{ {
const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"}; const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
/* The self-test results must be paragraph aligned. */ /* The self-test results must be paragraph aligned. */
int str[6], *volatile self_test_results; s32 str[6], *volatile self_test_results;
int boguscnt = 16000; /* Timeout for set-test. */ int boguscnt = 16000; /* Timeout for set-test. */
if (eeprom[3] & 0x03) if (eeprom[3] & 0x03)
printk(KERN_INFO " Receiver lock-up bug exists -- enabling" printk(KERN_INFO " Receiver lock-up bug exists -- enabling"
...@@ -638,7 +644,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -638,7 +644,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
} }
/* Perform a system self-test. */ /* Perform a system self-test. */
self_test_results = (int*) ((((int) str) + 15) & ~0xf); self_test_results = (s32*) ((((long) str) + 15) & ~0xf);
self_test_results[0] = 0; self_test_results[0] = 0;
self_test_results[1] = -1; self_test_results[1] = -1;
outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort); outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
...@@ -669,6 +675,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -669,6 +675,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
} }
#endif /* kernel_bloat */ #endif /* kernel_bloat */
outl(0, ioaddr + SCBPort);
/* We do a request_region() only to register /proc/ioports info. */ /* We do a request_region() only to register /proc/ioports info. */
request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet"); request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
...@@ -679,10 +687,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -679,10 +687,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL); dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
sp = dev->priv; sp = dev->priv;
memset(sp, 0, sizeof(*sp)); memset(sp, 0, sizeof(*sp));
#ifdef MODULE
sp->next_module = root_speedo_dev; sp->next_module = root_speedo_dev;
root_speedo_dev = dev; root_speedo_dev = dev;
#endif
if (card_idx >= 0) { if (card_idx >= 0) {
if (full_duplex[card_idx] >= 0) if (full_duplex[card_idx] >= 0)
...@@ -695,10 +701,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -695,10 +701,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
sp->phy[1] = eeprom[7]; sp->phy[1] = eeprom[7];
sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1; sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
printk(KERN_INFO " Operating in %s duplex mode.\n",
sp->full_duplex ? "full" : "half");
if (sp->rx_bug) if (sp->rx_bug)
printk(KERN_INFO " Reciever lock-up workaround activated.\n"); printk(KERN_INFO " Receiver lock-up workaround activated.\n");
/* The Speedo-specific entries in the device structure. */ /* The Speedo-specific entries in the device structure. */
dev->open = &speedo_open; dev->open = &speedo_open;
...@@ -708,6 +712,9 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -708,6 +712,9 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
#ifdef NEW_MULTICAST #ifdef NEW_MULTICAST
dev->set_multicast_list = &set_rx_mode; dev->set_multicast_list = &set_rx_mode;
#endif #endif
#ifdef HAVE_PRIVATE_IOCTL
dev->do_ioctl = &speedo_ioctl;
#endif
return; return;
} }
...@@ -726,7 +733,11 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, ...@@ -726,7 +733,11 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
/* Delay between EEPROM clock transitions. /* Delay between EEPROM clock transitions.
This is a "nasty" timing loop, but PC compatible machines are defined This is a "nasty" timing loop, but PC compatible machines are defined
to delay an ISA compatible period for the SLOW_DOWN_IO macro. */ to delay an ISA compatible period for the SLOW_DOWN_IO macro. */
#ifdef _LINUX_DELAY_H
#define eeprom_delay(nanosec) udelay(1);
#else
#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) #define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
#endif
/* The EEPROM commands include the alway-set leading bit. */ /* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << 6) #define EE_WRITE_CMD (5 << 6)
...@@ -830,11 +841,13 @@ speedo_open(struct device *dev) ...@@ -830,11 +841,13 @@ speedo_open(struct device *dev)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
/* Load the statistics block address. */ /* Load the statistics block address. */
wait_for_cmd_done(ioaddr + SCBCmd);
outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd); outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
sp->lstats.done_marker = 0; sp->lstats.done_marker = 0;
speedo_init_rx_ring(dev); speedo_init_rx_ring(dev);
wait_for_cmd_done(ioaddr + SCBCmd);
outl(0, ioaddr + SCBPointer); outl(0, ioaddr + SCBPointer);
outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd); outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
...@@ -845,8 +858,8 @@ speedo_open(struct device *dev) ...@@ -845,8 +858,8 @@ speedo_open(struct device *dev)
/* Fill the first command with our physical address. */ /* Fill the first command with our physical address. */
{ {
unsigned short *eaddrs = (unsigned short *)dev->dev_addr; u16 *eaddrs = (u16 *)dev->dev_addr;
unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr); u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr);
/* Avoid a bug(?!) here by marking the command already completed. */ /* Avoid a bug(?!) here by marking the command already completed. */
sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000; sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
...@@ -860,6 +873,7 @@ speedo_open(struct device *dev) ...@@ -860,6 +873,7 @@ speedo_open(struct device *dev)
sp->dirty_tx = 0; sp->dirty_tx = 0;
sp->tx_full = 0; sp->tx_full = 0;
wait_for_cmd_done(ioaddr + SCBCmd);
outl(0, ioaddr + SCBPointer); outl(0, ioaddr + SCBPointer);
outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd); outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
...@@ -896,6 +910,7 @@ speedo_open(struct device *dev) ...@@ -896,6 +910,7 @@ speedo_open(struct device *dev)
sp->timer.function = &speedo_timer; /* timer handler */ sp->timer.function = &speedo_timer; /* timer handler */
add_timer(&sp->timer); add_timer(&sp->timer);
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_DUMPSTATS, ioaddr + SCBCmd); outw(CU_DUMPSTATS, ioaddr + SCBCmd);
return 0; return 0;
} }
...@@ -1079,6 +1094,7 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev) ...@@ -1079,6 +1094,7 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev)
sp->last_cmd->command &= ~(CmdSuspend | CmdIntr); sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
/* Trigger the command unit resume. */ /* Trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd);
restore_flags(flags); restore_flags(flags);
} }
...@@ -1100,7 +1116,7 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ...@@ -1100,7 +1116,7 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{ {
struct device *dev = (struct device *)dev_instance; struct device *dev = (struct device *)dev_instance;
struct speedo_private *sp; struct speedo_private *sp;
int ioaddr, boguscnt = INTR_WORK; int ioaddr, boguscnt = max_interrupt_work;
unsigned short status; unsigned short status;
#ifndef final_version #ifndef final_version
...@@ -1169,7 +1185,7 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) ...@@ -1169,7 +1185,7 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
int status = sp->tx_ring[entry].status; int status = sp->tx_ring[entry].status;
if (speedo_debug > 5) if (speedo_debug > 5)
printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n", printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
entry, status); entry, status);
if ((status & 0x8000) == 0) if ((status & 0x8000) == 0)
break; /* It still hasn't been processed. */ break; /* It still hasn't been processed. */
...@@ -1256,13 +1272,13 @@ speedo_rx(struct device *dev) ...@@ -1256,13 +1272,13 @@ speedo_rx(struct device *dev)
dev->name, status); dev->name, status);
} else { } else {
/* Malloc up new buffer, compatible with net-2e. */ /* Malloc up new buffer, compatible with net-2e. */
short pkt_len = sp->rx_ringp[entry]->count & 0x3fff; int pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
struct sk_buff *skb; struct sk_buff *skb;
int rx_in_place = 0; int rx_in_place = 0;
/* Check if the packet is long enough to just accept without /* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */ copying to a properly sized skbuff. */
if (pkt_len > SKBUFF_RX_COPYBREAK) { if (pkt_len > rx_copybreak) {
struct sk_buff *newskb; struct sk_buff *newskb;
char *temp; char *temp;
...@@ -1339,8 +1355,14 @@ speedo_rx(struct device *dev) ...@@ -1339,8 +1355,14 @@ speedo_rx(struct device *dev)
#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) #if (LINUX_VERSION_CODE >= VERSION(1,3,44))
if (! rx_in_place) { if (! rx_in_place) {
skb_reserve(skb, 2); /* 16 byte align the data fields */ skb_reserve(skb, 2); /* 16 byte align the data fields */
#if defined(__i386) && notyet
/* Packet is in one chunk -- we can copy + cksum. */
eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
pkt_len, 0);
#else
memcpy(skb_put(skb, pkt_len), memcpy(skb_put(skb, pkt_len),
bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len); bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
#endif
} }
skb->protocol = eth_type_trans(skb, dev); skb->protocol = eth_type_trans(skb, dev);
#else #else
...@@ -1478,12 +1500,39 @@ speedo_get_stats(struct device *dev) ...@@ -1478,12 +1500,39 @@ speedo_get_stats(struct device *dev)
sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs; sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
sp->stats.rx_length_errors += sp->lstats.rx_runt_errs; sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
sp->lstats.done_marker = 0x0000; sp->lstats.done_marker = 0x0000;
if (dev->start) if (dev->start) {
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_DUMPSTATS, ioaddr + SCBCmd); outw(CU_DUMPSTATS, ioaddr + SCBCmd);
} }
}
return &sp->stats; return &sp->stats;
} }
#ifdef HAVE_PRIVATE_IOCTL
static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
struct speedo_private *sp = (struct speedo_private *)dev->priv;
int ioaddr = dev->base_addr;
u16 *data = (u16 *)&rq->ifr_data;
int phy = sp->phy[0] & 0x1f;
switch(cmd) {
case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
data[0] = phy;
case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
data[3] = mdio_read(ioaddr, data[0], data[1]);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
if (!suser())
return -EPERM;
mdio_write(ioaddr, data[0], data[1], data[2]);
return 0;
default:
return -EOPNOTSUPP;
}
}
#endif /* HAVE_PRIVATE_IOCTL */
/* Set or clear the multicast filter for this adaptor. /* Set or clear the multicast filter for this adaptor.
This is very ugly with Intel chips -- we usually have to execute an This is very ugly with Intel chips -- we usually have to execute an
entire configuration command, plus process a multicast command. entire configuration command, plus process a multicast command.
...@@ -1542,6 +1591,7 @@ set_rx_mode(struct device *dev) ...@@ -1542,6 +1591,7 @@ set_rx_mode(struct device *dev)
virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
sp->last_cmd->command &= ~CmdSuspend; sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */ /* Immediately trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = &sp->config_cmd; sp->last_cmd = &sp->config_cmd;
restore_flags(flags); restore_flags(flags);
...@@ -1557,8 +1607,7 @@ set_rx_mode(struct device *dev) ...@@ -1557,8 +1607,7 @@ set_rx_mode(struct device *dev)
if (new_rx_mode == 0 && dev->mc_count < 3) { if (new_rx_mode == 0 && dev->mc_count < 3) {
/* The simple case of 0-2 multicast list entries occurs often, and /* The simple case of 0-2 multicast list entries occurs often, and
fits within one tx_ring[] entry. */ fits within one tx_ring[] entry. */
u16 *setup_params; u16 *setup_params, *eaddrs;
unsigned short *eaddrs;
struct dev_mc_list *mclist; struct dev_mc_list *mclist;
save_flags(flags); save_flags(flags);
...@@ -1569,12 +1618,12 @@ set_rx_mode(struct device *dev) ...@@ -1569,12 +1618,12 @@ set_rx_mode(struct device *dev)
sp->tx_ring[entry].link = sp->tx_ring[entry].link =
virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */ sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr; setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
*setup_params++ = dev->mc_count*6; *setup_params++ = dev->mc_count*6;
/* Fill in the multicast addresses. */ /* Fill in the multicast addresses. */
for (i = 0, mclist = dev->mc_list; i < dev->mc_count; for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
i++, mclist = mclist->next) { i++, mclist = mclist->next) {
eaddrs = (unsigned short *)mclist->dmi_addr; eaddrs = (u16 *)mclist->dmi_addr;
*setup_params++ = *eaddrs++; *setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++; *setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++; *setup_params++ = *eaddrs++;
...@@ -1582,15 +1631,16 @@ set_rx_mode(struct device *dev) ...@@ -1582,15 +1631,16 @@ set_rx_mode(struct device *dev)
sp->last_cmd->command &= ~CmdSuspend; sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */ /* Immediately trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
restore_flags(flags); restore_flags(flags);
} else if (new_rx_mode == 0) { } else if (new_rx_mode == 0) {
/* This does not work correctly, but why not? */ /* This does not work correctly, but why not? */
struct dev_mc_list *mclist; struct dev_mc_list *mclist;
unsigned short *eaddrs; u16 *eaddrs;
struct descriptor *mc_setup_frm = sp->mc_setup_frm; struct descriptor *mc_setup_frm = sp->mc_setup_frm;
u16 *setup_params = (short *)mc_setup_frm->params; u16 *setup_params = (u16 *)mc_setup_frm->params;
int i; int i;
if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
...@@ -1616,12 +1666,12 @@ set_rx_mode(struct device *dev) ...@@ -1616,12 +1666,12 @@ set_rx_mode(struct device *dev)
mc_setup_frm->status = 0; mc_setup_frm->status = 0;
mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList; mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
/* Link set below. */ /* Link set below. */
setup_params = (short *)mc_setup_frm->params; setup_params = (u16 *)mc_setup_frm->params;
*setup_params++ = dev->mc_count*6; *setup_params++ = dev->mc_count*6;
/* Fill in the multicast addresses. */ /* Fill in the multicast addresses. */
for (i = 0, mclist = dev->mc_list; i < dev->mc_count; for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
i++, mclist = mclist->next) { i++, mclist = mclist->next) {
eaddrs = (unsigned short *)mclist->dmi_addr; eaddrs = (u16 *)mclist->dmi_addr;
*setup_params++ = *eaddrs++; *setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++; *setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++; *setup_params++ = *eaddrs++;
...@@ -1647,6 +1697,7 @@ set_rx_mode(struct device *dev) ...@@ -1647,6 +1697,7 @@ set_rx_mode(struct device *dev)
sp->last_cmd->command &= ~CmdSuspend; sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */ /* Immediately trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = mc_setup_frm; sp->last_cmd = mc_setup_frm;
restore_flags(flags); restore_flags(flags);
...@@ -1663,6 +1714,21 @@ set_rx_mode(struct device *dev) ...@@ -1663,6 +1714,21 @@ set_rx_mode(struct device *dev)
char kernel_version[] = UTS_RELEASE; char kernel_version[] = UTS_RELEASE;
#endif #endif
#if LINUX_VERSION_CODE > 0x20118
MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
MODULE_PARM(debug, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(congenb, "i");
MODULE_PARM(txfifo, "i");
MODULE_PARM(rxfifo, "i");
MODULE_PARM(txdmacount, "i");
MODULE_PARM(rxdmacount, "i");
MODULE_PARM(rx_copybreak, "i");
MODULE_PARM(max_interrupt_work, "i");
#endif
int int
init_module(void) init_module(void)
{ {
......
...@@ -100,7 +100,7 @@ static char *PPA_MODE_STRING[] = ...@@ -100,7 +100,7 @@ static char *PPA_MODE_STRING[] =
int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */ int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
/* other options */ /* other options */
#define PPA_CAN_QUEUE 0 /* use "queueing" interface */ #define PPA_CAN_QUEUE 1 /* use "queueing" interface */
#define PPA_BURST_SIZE 512 /* data burst size */ #define PPA_BURST_SIZE 512 /* data burst size */
#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */ #define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */ #define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
/* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define /* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define
* the following line. * the following line.
*/ */
#undef HAS_LOWLEVEL_H #define HAS_LOWLEVEL_H
/* if your system doesn't support patch manager (OSS 3.7 or newer), /* if your system doesn't support patch manager (OSS 3.7 or newer),
* define the following line. * define the following line.
......
...@@ -327,7 +327,7 @@ sound_mmap (struct file *file, struct vm_area_struct *vma) ...@@ -327,7 +327,7 @@ sound_mmap (struct file *file, struct vm_area_struct *vma)
vma->vm_page_prot)) vma->vm_page_prot))
return -EAGAIN; return -EAGAIN;
vma->vm_dentry = file->f_dentry; vma->vm_dentry = dget(file->f_dentry);
dmap->mapping_flags |= DMA_MAP_MAPPED; dmap->mapping_flags |= DMA_MAP_MAPPED;
......
...@@ -118,6 +118,11 @@ void dput(struct dentry *dentry) ...@@ -118,6 +118,11 @@ void dput(struct dentry *dentry)
} }
list_add(&dentry->d_lru, &dentry_unused); list_add(&dentry->d_lru, &dentry_unused);
dentry_stat.nr_unused++; dentry_stat.nr_unused++;
/*
* Update the timestamp
*/
dentry->d_reftime = jiffies;
out: out:
if (count >= 0) { if (count >= 0) {
dentry->d_count = count; dentry->d_count = count;
...@@ -135,15 +140,12 @@ void dput(struct dentry *dentry) ...@@ -135,15 +140,12 @@ void dput(struct dentry *dentry)
* Try to invalidate the dentry if it turns out to be * Try to invalidate the dentry if it turns out to be
* possible. If there are other users of the dentry we * possible. If there are other users of the dentry we
* can't invalidate it. * can't invalidate it.
*
* We should probably try to see if we can invalidate
* any unused children - right now we refuse to invalidate
* too much. That would require a better child list
* data structure, though.
*/ */
int d_invalidate(struct dentry * dentry) int d_invalidate(struct dentry * dentry)
{ {
/* We might want to do a partial shrink_dcache here */ /* Check whether to do a partial shrink_dcache */
if (dentry->d_count > 1 && !list_empty(&dentry->d_subdirs))
shrink_dcache_parent(dentry);
if (dentry->d_count != 1) if (dentry->d_count != 1)
return -EBUSY; return -EBUSY;
...@@ -152,22 +154,26 @@ int d_invalidate(struct dentry * dentry) ...@@ -152,22 +154,26 @@ int d_invalidate(struct dentry * dentry)
} }
/* /*
* Selects less valuable dentries to be pruned when * Select less valuable dentries to be pruned when we need
* we need inodes or memory. The selected dentries * inodes or memory. The selected dentries are moved to the
* are moved to the old end of the list where * old end of the list where prune_dcache() can find them.
* prune_dcache() can find them. *
* Negative dentries are included in the selection so that
* they don't accumulate at the end of the list. The count
* returned is the total number of dentries selected, which
* may be much larger than the requested number of inodes.
*/ */
int select_dcache(int count, int page_count) int select_dcache(int inode_count, int page_count)
{ {
struct list_head *tail = &dentry_unused; struct list_head *next, *tail = &dentry_unused;
struct list_head *next = dentry_unused.prev; int found = 0, forward = 0, young = 8;
int forward = 0, young = 0, depth = dentry_stat.nr_unused >> 1; int depth = dentry_stat.nr_unused >> 1;
int found = 0, pages = 0; unsigned long min_value = 0, max_value = 4;
#ifdef DCACHE_DEBUG if (page_count)
printk("select_dcache: %d unused, count=%d, pages=%d\n", max_value = -1;
dentry_stat.nr_unused, count, page_count);
#endif next = tail->prev;
while (next != &dentry_unused && depth--) { while (next != &dentry_unused && depth--) {
struct list_head *tmp = next; struct list_head *tmp = next;
struct dentry *dentry = list_entry(tmp, struct dentry, d_lru); struct dentry *dentry = list_entry(tmp, struct dentry, d_lru);
...@@ -184,56 +190,57 @@ dentry_stat.nr_unused, count, page_count); ...@@ -184,56 +190,57 @@ dentry_stat.nr_unused, count, page_count);
continue; continue;
} }
/* /*
* Select dentries based on the page cache count ... * Check the dentry's age to see whether to change direction.
* should factor in number of uses as well.
*/ */
if (inode) { if (!forward) {
if (inode->i_state) int age = (jiffies - dentry->d_reftime) / HZ;
continue;
value = inode->i_nrpages;
}
/*
* Consider various exemptions ...
*/
if (!page_count) {
if (!inode)
continue;
if (value >= 3)
continue;
} else if (!forward) {
if (inode) {
int age = CURRENT_TIME - inode->i_atime;
if (age < dentry_stat.age_limit) { if (age < dentry_stat.age_limit) {
if (++young > 8) { if (!--young) {
forward = 1; forward = 1;
next = dentry_unused.next; next = dentry_unused.next;
/*
* Update the limits -- we don't want
* files with too few or too many pages.
*/
if (page_count) {
min_value = 3;
max_value = 15;
}
#ifdef DCACHE_DEBUG #ifdef DCACHE_DEBUG
printk("select_dcache: age=%d, pages=%d, scanning forward\n", age, pages); printk("select_dcache: %s/%s age=%d, scanning forward\n",
dentry->d_parent->d_name.name, dentry->d_name.name, age);
#endif #endif
} }
continue; continue;
} }
} }
} else {
/* /*
* If we're scanning from the front, don't take * Select dentries based on the page cache count ...
* files with only a trivial amount of memory. * should factor in number of uses as well. We take
* all negative dentries so that they don't accumulate.
* (We skip inodes that aren't immediately available.)
*/ */
if (value < 3 || value > 15) if (inode) {
value = inode->i_nrpages;
if (value >= max_value || value < min_value)
continue;
if (inode->i_state || inode->i_count > 1)
continue; continue;
} }
/* /*
* Move the dentry behind the tail * Move the selected dentries behind the tail.
*/ */
if (tmp != tail->prev) { if (tmp != tail->prev) {
list_del(tmp); list_del(tmp);
list_add(tmp, tail->prev); list_add(tmp, tail->prev);
} }
tail = tmp; tail = tmp;
pages += value; found++;
if (++found >= count) if (inode && --inode_count <= 0)
break; break;
if (page_count && pages >= page_count) if (page_count && (page_count -= value) <= 0)
break; break;
} }
return found; return found;
...@@ -430,7 +437,7 @@ void check_dcache_memory() ...@@ -430,7 +437,7 @@ void check_dcache_memory()
if (goal) { if (goal) {
if (goal > 50) if (goal > 50)
goal = 50; goal = 50;
count = select_dcache(128, goal); count = select_dcache(32, goal);
#ifdef DCACHE_DEBUG #ifdef DCACHE_DEBUG
printk("check_dcache_memory: goal=%d, count=%d\n", goal, count); printk("check_dcache_memory: goal=%d, count=%d\n", goal, count);
#endif #endif
...@@ -453,7 +460,7 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) ...@@ -453,7 +460,7 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
* Prune the dcache if there are too many unused dentries. * Prune the dcache if there are too many unused dentries.
*/ */
if (dentry_stat.nr_unused > 3*(nr_inodes >> 1)) { if (dentry_stat.nr_unused > 3*(nr_inodes >> 1)) {
#ifdef DCACHE_PARANOIA #ifdef DCACHE_DEBUG
printk("d_alloc: %d unused, pruning dcache\n", dentry_stat.nr_unused); printk("d_alloc: %d unused, pruning dcache\n", dentry_stat.nr_unused);
#endif #endif
prune_dcache(8); prune_dcache(8);
...@@ -579,30 +586,33 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name) ...@@ -579,30 +586,33 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
int d_validate(struct dentry *dentry, struct dentry *dparent, int d_validate(struct dentry *dentry, struct dentry *dparent,
unsigned int hash, unsigned int len) unsigned int hash, unsigned int len)
{ {
struct list_head *base = d_hash(dparent, hash); struct list_head *base, *lhp;
struct list_head *lhp = base; int valid = 1;
if (dentry != dparent) {
base = d_hash(dparent, hash);
lhp = base;
while ((lhp = lhp->next) != base) { while ((lhp = lhp->next) != base) {
if (dentry == list_entry(lhp, struct dentry, d_hash)) if (dentry == list_entry(lhp, struct dentry, d_hash))
goto found_it; goto out;
} }
} else {
/* Special case, local mount points don't live in the hashes. /*
* So if we exhausted the chain, search the super blocks. * Special case: local mount points don't live in
* the hashes, so we search the super blocks.
*/ */
if (dentry && dentry == dparent) { struct super_block *sb = super_blocks + 0;
struct super_block *sb;
for (sb = super_blocks + 0; sb < super_blocks + NR_SUPER; sb++) { for (; sb < super_blocks + NR_SUPER; sb++) {
if (!sb->s_dev)
continue;
if (sb->s_root == dentry) if (sb->s_root == dentry)
goto found_it; goto out;
} }
} }
return 0; valid = 0;
found_it: out:
return (dentry->d_parent == dparent) && return valid;
(dentry->d_name.hash == hash) &&
(dentry->d_name.len == len);
} }
/* /*
......
...@@ -358,33 +358,30 @@ static int free_inodes(int goal) ...@@ -358,33 +358,30 @@ static int free_inodes(int goal)
*/ */
static void try_to_free_inodes(int goal) static void try_to_free_inodes(int goal)
{ {
int retried = 0, found; int retry = 1, found;
/* /*
* Check whether to preshrink the dcache ... * Check whether to preshrink the dcache ...
*/ */
if (inodes_stat.preshrink) { if (inodes_stat.preshrink)
spin_unlock(&inode_lock); goto preshrink;
select_dcache(goal, 0);
prune_dcache(goal);
spin_lock(&inode_lock);
}
repeat:
found = free_inodes(goal);
retry = 0;
do {
if (free_inodes(goal))
break;
/* /*
* If we didn't free any inodes, do a limited * If we didn't free any inodes, do a limited
* pruning of the dcache to help the next time. * pruning of the dcache to help the next time.
*/ */
if (!found) { preshrink:
spin_unlock(&inode_lock); spin_unlock(&inode_lock);
select_dcache(goal, 0); found = select_dcache(goal, 0);
prune_dcache(goal); if (found < goal)
found = goal;
prune_dcache(found);
spin_lock(&inode_lock); spin_lock(&inode_lock);
if (inodes_stat.preshrink && !retried++) } while (retry--);
goto repeat;
}
} }
/* /*
...@@ -440,11 +437,11 @@ static struct inode * grow_inodes(void) ...@@ -440,11 +437,11 @@ static struct inode * grow_inodes(void)
* If the allocation failed, do an extensive pruning of * If the allocation failed, do an extensive pruning of
* the dcache and then try again to free some inodes. * the dcache and then try again to free some inodes.
*/ */
prune_dcache(128); prune_dcache(inodes_stat.nr_inodes >> 2);
inodes_stat.preshrink = 1; inodes_stat.preshrink = 1;
spin_lock(&inode_lock); spin_lock(&inode_lock);
free_inodes(128); free_inodes(inodes_stat.nr_inodes >> 2);
{ {
struct list_head *tmp = inode_unused.next; struct list_head *tmp = inode_unused.next;
if (tmp != &inode_unused) { if (tmp != &inode_unused) {
......
...@@ -32,10 +32,16 @@ static struct semaphore nlm_file_sema = MUTEX; ...@@ -32,10 +32,16 @@ static struct semaphore nlm_file_sema = MUTEX;
* Lookup file info. If it doesn't exist, create a file info struct * Lookup file info. If it doesn't exist, create a file info struct
* and open a (VFS) file for the given inode. * and open a (VFS) file for the given inode.
* *
* The NFS filehandle must have been validated prior to this call,
* as we assume that the dentry pointer is valid.
*
* FIXME: * FIXME:
* Note that we open the file O_RDONLY even when creating write locks. * Note that we open the file O_RDONLY even when creating write locks.
* This is not quite right, but for now, we assume the client performs * This is not quite right, but for now, we assume the client performs
* the proper R/W checking. * the proper R/W checking.
*
* The dentry in the FH may not be validated .. can we call this with
* the full svc_fh?
*/ */
u32 u32
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
...@@ -43,21 +49,24 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, ...@@ -43,21 +49,24 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
{ {
struct nlm_file *file; struct nlm_file *file;
struct knfs_fh *fh = (struct knfs_fh *) f; struct knfs_fh *fh = (struct knfs_fh *) f;
unsigned int hash = FILE_HASH(fh->fh_dhash); struct dentry *dentry = fh->fh_dcookie;
unsigned int hash = FILE_HASH(dentry->d_name.hash);
u32 nfserr; u32 nfserr;
dprintk("lockd: nlm_file_lookup(%p)\n", fh->fh_dentry); dprintk("lockd: nlm_file_lookup(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
/* Lock file table */ /* Lock file table */
down(&nlm_file_sema); down(&nlm_file_sema);
for (file = nlm_files[hash]; file; file = file->f_next) { for (file = nlm_files[hash]; file; file = file->f_next) {
if (file->f_handle.fh_dentry == fh->fh_dentry if (file->f_handle.fh_dcookie == dentry
&& !memcmp(&file->f_handle, fh, sizeof(*fh))) && !memcmp(&file->f_handle, fh, sizeof(*fh)))
goto found; goto found;
} }
dprintk("lockd: creating file for %p\n", fh->fh_dentry); dprintk("lockd: creating file for %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) { if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) {
up(&nlm_file_sema); up(&nlm_file_sema);
return nlm_lck_denied_nolocks; return nlm_lck_denied_nolocks;
......
...@@ -235,9 +235,11 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) ...@@ -235,9 +235,11 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
result = ERR_PTR(-ENOMEM); result = ERR_PTR(-ENOMEM);
if (dentry) { if (dentry) {
int error = dir->i_op->lookup(dir, dentry); int error = dir->i_op->lookup(dir, dentry);
result = ERR_PTR(error);
if (!error)
result = dentry; result = dentry;
if (error) {
dput(dentry);
result = ERR_PTR(error);
}
} }
} }
up(&dir->i_sem); up(&dir->i_sem);
......
...@@ -179,27 +179,24 @@ exp_export(struct nfsctl_export *nxp) ...@@ -179,27 +179,24 @@ exp_export(struct nfsctl_export *nxp)
} }
/* Look up the dentry */ /* Look up the dentry */
dentry = lookup_dentry(nxp->ex_path, NULL, 0);
if (IS_ERR(dentry)) {
err = -EINVAL; err = -EINVAL;
dentry = lookup_dentry(nxp->ex_path, NULL, 0);
if (IS_ERR(dentry))
goto finish; goto finish;
}
inode = dentry->d_inode;
if(!inode) {
err = -ENOENT; err = -ENOENT;
inode = dentry->d_inode;
if(!inode)
goto finish; goto finish;
} err = -EINVAL;
if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) { if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) {
/* I'm just being paranoid... */ /* I'm just being paranoid... */
err = -EINVAL;
goto finish; goto finish;
} }
/* We currently export only dirs. */ /* We currently export only dirs. */
if (!S_ISDIR(inode->i_mode)) {
err = -ENOTDIR; err = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto finish; goto finish;
}
/* If this is a sub-export, must be root of FS */ /* If this is a sub-export, must be root of FS */
if ((parent = exp_parent(clp, dev)) != NULL) { if ((parent = exp_parent(clp, dev)) != NULL) {
...@@ -211,10 +208,9 @@ exp_export(struct nfsctl_export *nxp) ...@@ -211,10 +208,9 @@ exp_export(struct nfsctl_export *nxp)
} }
} }
if (!(exp = kmalloc(sizeof(*exp), GFP_USER))) {
err = -ENOMEM; err = -ENOMEM;
if (!(exp = kmalloc(sizeof(*exp), GFP_USER)))
goto finish; goto finish;
}
dprintk("nfsd: created export entry %p for client %p\n", exp, clp); dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
strcpy(exp->ex_path, nxp->ex_path); strcpy(exp->ex_path, nxp->ex_path);
......
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <linux/net.h> #include <linux/net.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/nfs.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/malloc.h> #include <linux/malloc.h>
#include <linux/nfs.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h> #include <linux/nfsd/cache.h>
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
extern void nfsd_fh_init(void);
extern long sys_call_table[]; extern long sys_call_table[];
static int nfsctl_svc(struct nfsctl_svc *data); static int nfsctl_svc(struct nfsctl_svc *data);
...@@ -64,6 +65,7 @@ nfsd_init(void) ...@@ -64,6 +65,7 @@ nfsd_init(void)
nfsd_export_init(); /* Exports table */ nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */ nfsd_lockd_init(); /* lockd->nfsd callbacks */
nfsd_racache_init(); /* Readahead param cache */ nfsd_racache_init(); /* Readahead param cache */
nfsd_fh_init(); /* FH table */
initialized = 1; initialized = 1;
} }
...@@ -141,31 +143,33 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) ...@@ -141,31 +143,33 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
union nfsctl_res * res = NULL; union nfsctl_res * res = NULL;
int err; int err;
MOD_INC_USE_COUNT;
lock_kernel (); lock_kernel ();
if (!initialized) if (!initialized)
nfsd_init(); nfsd_init();
if (!suser()) {
err = -EPERM; err = -EPERM;
if (!suser()) {
goto done; goto done;
} }
err = -EFAULT;
if (!access_ok(VERIFY_READ, argp, sizeof(*argp)) if (!access_ok(VERIFY_READ, argp, sizeof(*argp))
|| (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp)))) { || (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp)))) {
err = -EFAULT;
goto done; goto done;
} }
err = -ENOMEM; /* ??? */
if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) || if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
(resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) { (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
err = -ENOMEM; /* ??? */
goto done; goto done;
} }
err = -EINVAL;
copy_from_user(arg, argp, sizeof(*argp)); copy_from_user(arg, argp, sizeof(*argp));
if (arg->ca_version != NFSCTL_VERSION) { if (arg->ca_version != NFSCTL_VERSION) {
printk(KERN_WARNING "nfsd: incompatible version in syscall.\n"); printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
err = -EINVAL;
goto done; goto done;
} }
MOD_INC_USE_COUNT;
switch(cmd) { switch(cmd) {
case NFSCTL_SVC: case NFSCTL_SVC:
err = nfsctl_svc(&arg->ca_svc); err = nfsctl_svc(&arg->ca_svc);
...@@ -193,7 +197,6 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) ...@@ -193,7 +197,6 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
default: default:
err = -EINVAL; err = -EINVAL;
} }
MOD_DEC_USE_COUNT;
if (!err && resp) if (!err && resp)
copy_to_user(resp, res, sizeof(*resp)); copy_to_user(resp, res, sizeof(*resp));
...@@ -205,6 +208,7 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp) ...@@ -205,6 +208,7 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
kfree(res); kfree(res);
unlock_kernel (); unlock_kernel ();
MOD_DEC_USE_COUNT;
return err; return err;
} }
...@@ -224,7 +228,6 @@ int ...@@ -224,7 +228,6 @@ int
init_module(void) init_module(void)
{ {
printk("Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); printk("Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
nfsd_init();
do_nfsservctl = handle_sys_nfsservctl; do_nfsservctl = handle_sys_nfsservctl;
return 0; return 0;
} }
...@@ -242,6 +245,7 @@ cleanup_module(void) ...@@ -242,6 +245,7 @@ cleanup_module(void)
do_nfsservctl = NULL; do_nfsservctl = NULL;
nfsd_export_shutdown(); nfsd_export_shutdown();
nfsd_cache_shutdown(); nfsd_cache_shutdown();
nfsd_fh_free();
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
nfsd_stat_shutdown(); nfsd_stat_shutdown();
#endif #endif
......
...@@ -7,18 +7,727 @@ ...@@ -7,18 +7,727 @@
*/ */
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/dcache.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#define NFSDDBG_FACILITY NFSDDBG_FH #define NFSDDBG_FACILITY NFSDDBG_FH
#define NFSD_PARANOIA 1
/* #define NFSD_DEBUG_VERBOSE 1 */
extern unsigned long num_physpages;
#define NFSD_FILE_CACHE 0
#define NFSD_DIR_CACHE 1
struct fh_entry {
struct dentry * dentry;
unsigned long reftime;
ino_t ino;
dev_t dev;
};
#define NFSD_MAXFH PAGE_SIZE/sizeof(struct fh_entry)
static struct fh_entry filetable[NFSD_MAXFH];
static struct fh_entry dirstable[NFSD_MAXFH];
static int nfsd_nr_verified = 0;
static int nfsd_nr_put = 0;
static unsigned long nfsd_next_expire = 0;
static int add_to_fhcache(struct dentry *, int);
static int nfsd_d_validate(struct dentry *);
static LIST_HEAD(fixup_head);
static LIST_HEAD(path_inuse);
static int nfsd_nr_paths = 0;
#define NFSD_MAX_PATHS 500
struct nfsd_fixup {
struct list_head lru;
struct dentry *dentry;
unsigned long reftime;
ino_t ino;
dev_t dev;
};
struct nfsd_path {
struct list_head lru;
unsigned long reftime;
int users;
ino_t ino;
dev_t dev;
char name[1];
};
/*
* Copy a dentry's path into the specified buffer.
*/
static int copy_path(char *buffer, struct dentry *dentry, int namelen)
{
char *p, *b = buffer;
int result = 0, totlen = 0, len;
while (1) {
struct dentry *parent;
dentry = dentry->d_covers;
parent = dentry->d_parent;
len = dentry->d_name.len;
p = (char *) dentry->d_name.name + len;
totlen += len;
if (totlen > namelen)
goto out;
while (len--)
*b++ = *(--p);
if (dentry == parent)
break;
dentry = parent;
totlen++;
if (totlen > namelen)
goto out;
*b++ = '/';
}
*b = 0;
/*
* Now reverse in place ...
*/
p = buffer;
while (p < b) {
char c = *(--b);
*b = *p;
*p++ = c;
}
result = 1;
out:
return result;
}
/*
* Add a dentry's path to the path cache.
*/
static int add_to_path_cache(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct dentry *this;
struct nfsd_path *new;
int len, result = 0;
#ifdef NFSD_DEBUG_VERBOSE
printk("add_to_path_cache: cacheing %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
/*
* Get the length of the full pathname.
*/
restart:
len = 0;
this = dentry;
while (1) {
struct dentry *parent;
this = this->d_covers;
parent = this->d_parent;
len += this->d_name.len;
if (this == parent)
break;
this = parent;
len++;
}
/*
* Allocate a structure to hold the path.
*/
new = kmalloc(sizeof(struct nfsd_path) + len, GFP_KERNEL);
if (new) {
new->users = 0;
new->reftime = jiffies;
new->ino = inode->i_ino;
new->dev = inode->i_dev;
result = copy_path(new->name, dentry, len);
if (!result)
goto retry;
list_add(&new->lru, &path_inuse);
nfsd_nr_paths++;
#ifdef NFSD_DEBUG_VERBOSE
printk("add_to_path_cache: added %s, paths=%d\n", new->name, nfsd_nr_paths);
#endif
}
return result;
/*
* If the dentry's path length changed, just try again ...
*/
retry:
kfree(new);
printk("add_to_path_cache: path length changed, retrying\n");
goto restart;
}
/*
* Search for a path entry for the specified (dev, inode).
*/
struct nfsd_path *get_path_entry(dev_t dev, ino_t ino)
{
struct nfsd_path *pe;
struct list_head *tmp;
for (tmp = path_inuse.next; tmp != &path_inuse; tmp = tmp->next) {
pe = list_entry(tmp, struct nfsd_path, lru);
if (pe->ino != ino)
continue;
if (pe->dev != dev)
continue;
list_del(tmp);
list_add(tmp, &path_inuse);
pe->users++;
pe->reftime = jiffies;
#ifdef NFSD_PARANOIA
printk("get_path_entry: found %s for %s/%ld\n", pe->name, kdevname(dev), ino);
#endif
return pe;
}
return NULL;
}
static void put_path(struct nfsd_path *pe)
{
pe->users--;
}
static void free_path_entry(struct nfsd_path *pe)
{
if (pe->users)
printk("free_path_entry: %s in use, users=%d\n",
pe->name, pe->users);
list_del(&pe->lru);
kfree(pe);
nfsd_nr_paths--;
}
struct nfsd_getdents_callback {
struct nfsd_dirent *dirent;
int found;
int checked;
};
struct nfsd_dirent {
ino_t ino;
int len;
char name[256];
};
/*
* A custom filldir function to search for the specified inode.
*/
static int filldir_ino(void * __buf, const char * name, int len,
off_t pos, ino_t ino)
{
struct nfsd_getdents_callback *buf = __buf;
struct nfsd_dirent *dirent = buf->dirent;
int result = 0;
buf->checked++;
if (ino == dirent->ino) {
buf->found = 1;
dirent->len = len;
memcpy(dirent->name, name, len);
dirent->name[len] = 0;
result = -1;
}
return result;
}
/*
* Search a directory for the specified inode number.
*/
static int search_dir(struct dentry *parent, ino_t ino,
struct nfsd_dirent *dirent)
{
struct inode *inode = parent->d_inode;
int error;
struct file file;
struct nfsd_getdents_callback buffer;
error = -ENOTDIR;
if (!inode || !S_ISDIR(inode->i_mode))
goto out;
error = -EINVAL;
if (!inode->i_op || !inode->i_op->default_file_ops)
goto out;
/*
* Open the directory ...
*/
error = init_private_file(&file, parent, FMODE_READ);
if (error)
goto out;
error = -EINVAL;
if (!file.f_op->readdir)
goto out_close;
/*
* Initialize the callback buffer.
*/
dirent->ino = ino;
buffer.dirent = dirent;
buffer.found = 0;
while (1) {
buffer.checked = 0;
error = file.f_op->readdir(&file, &buffer, filldir_ino);
if (error < 0)
break;
error = 0;
if (buffer.found) {
#ifdef NFSD_DEBUG_VERBOSE
printk("search_dir: found %s\n", dirent->name);
#endif
break;
}
error = -ENOENT;
if (!buffer.checked)
break;
}
out_close:
if (file.f_op->release)
file.f_op->release(inode, &file);
out:
return error;
}
/*
* Find an entry in the cache matching the given dentry pointer.
*/
static struct fh_entry *find_fhe(struct dentry *dentry, int cache,
struct fh_entry **empty)
{
struct fh_entry *fhe;
int i, found = (empty == NULL) ? 1 : 0;
fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
if (fhe->dentry == dentry) {
fhe->reftime = jiffies;
return fhe;
}
if (!found && !fhe->dentry) {
found = 1;
*empty = fhe;
}
}
return NULL;
}
/*
* Expire a cache entry.
*/
static void expire_fhe(struct fh_entry *empty, int cache)
{
struct dentry *dentry = empty->dentry;
#ifdef NFSD_DEBUG_VERBOSE
printk("expire_fhe: expiring %s/%s, d_count=%d, ino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
#endif
empty->dentry = NULL; /* no dentry */
/*
* Add the parent to the dir cache before releasing the dentry.
*/
if (dentry != dentry->d_parent) {
struct dentry *parent = dget(dentry->d_parent);
if (add_to_fhcache(parent, NFSD_DIR_CACHE))
nfsd_nr_verified++;
else
dput(parent);
}
/*
* If we're expiring a directory, copy its path.
*/
if (cache == NFSD_DIR_CACHE) {
add_to_path_cache(dentry);
}
dput(dentry);
nfsd_nr_put++;
}
/*
* Look for an empty slot, or select one to expire.
*/
static void expire_slot(int cache)
{
struct fh_entry *fhe, *empty = NULL;
unsigned long oldest = -1;
int i;
fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
if (!fhe->dentry)
goto out;
if (fhe->reftime < oldest) {
oldest = fhe->reftime;
empty = fhe;
}
}
if (empty)
expire_fhe(empty, cache);
out:
return;
}
/*
* Expire any cache entries older than a certain age.
*/
static void expire_old(int cache, int age)
{
struct fh_entry *fhe;
int i;
#ifdef NFSD_DEBUG_VERBOSE
printk("expire_old: expiring %s older than %d\n",
(cache == NFSD_FILE_CACHE) ? "file" : "dir", age);
#endif
fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
if (!fhe->dentry)
continue;
if ((jiffies - fhe->reftime) > age)
expire_fhe(fhe, cache);
}
/*
* Trim the path cache ...
*/
while (nfsd_nr_paths > NFSD_MAX_PATHS) {
struct nfsd_path *pe;
pe = list_entry(path_inuse.prev, struct nfsd_path, lru);
if (pe->users)
break;
free_path_entry(pe);
}
}
/*
* Add a dentry to the file or dir cache.
*
* Note: As NFS filehandles must have an inode, we don't accept
* negative dentries.
*/
static int add_to_fhcache(struct dentry *dentry, int cache)
{
struct fh_entry *fhe, *empty = NULL;
struct inode *inode = dentry->d_inode;
if (!inode) {
#ifdef NFSD_PARANOIA
printk("add_to_fhcache: %s/%s rejected, no inode!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
return 0;
}
repeat:
fhe = find_fhe(dentry, cache, &empty);
if (fhe) {
return 0;
}
/*
* Not found ... make a new entry.
*/
if (empty) {
empty->dentry = dentry;
empty->reftime = jiffies;
empty->ino = inode->i_ino;
empty->dev = inode->i_dev;
return 1;
}
expire_slot(cache);
goto repeat;
}
/*
* Find an entry in the dir cache for the specified inode number.
*/
static struct fh_entry *find_fhe_by_ino(dev_t dev, ino_t ino)
{
struct fh_entry * fhe = &dirstable[0];
int i;
for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
if (fhe->ino == ino && fhe->dev == dev) {
fhe->reftime = jiffies;
return fhe;
}
}
return NULL;
}
/*
* Find the (directory) dentry with the specified (dev, inode) number.
* Note: this leaves the dentry in the cache.
*/
static struct dentry *find_dentry_by_ino(dev_t dev, ino_t ino)
{
struct fh_entry *fhe;
struct nfsd_path *pe;
struct dentry * dentry;
#ifdef NFSD_DEBUG_VERBOSE
printk("find_dentry_by_ino: looking for inode %ld\n", ino);
#endif
fhe = find_fhe_by_ino(dev, ino);
if (fhe) {
dentry = dget(fhe->dentry);
goto out;
}
/*
* Search the path cache ...
*/
dentry = NULL;
pe = get_path_entry(dev, ino);
if (pe) {
struct dentry *res;
res = lookup_dentry(pe->name, NULL, 0);
if (!IS_ERR(res)) {
struct inode *inode = res->d_inode;
if (inode && inode->i_ino == ino &&
inode->i_dev == dev) {
dentry = res;
#ifdef NFSD_PARANOIA
printk("find_dentry_by_ino: found %s/%s, ino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, ino);
#endif
if (add_to_fhcache(dentry, NFSD_DIR_CACHE)) {
dget(dentry);
nfsd_nr_verified++;
}
} else {
dput(res);
}
} else {
#ifdef NFSD_PARANOIA
printk("find_dentry_by_ino: %s lookup failed\n", pe->name);
#endif
}
put_path(pe);
}
out:
return dentry;
}
/*
* Look for an entry in the file cache matching the dentry pointer,
* and verify that the (dev, inode) numbers are correct. If found,
* the entry is removed from the cache.
*/
static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh)
{
struct fh_entry * fhe;
fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL);
if (fhe) {
struct dentry *parent, *dentry = fhe->dentry;
struct inode *inode = dentry->d_inode;
if (!inode) {
#ifdef NFSD_PARANOIA
printk("find_dentry_in_fhcache: %s/%s has no inode!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
goto out;
}
if (inode->i_ino != fh->fh_ino || inode->i_dev != fh->fh_dev) {
#ifdef NFSD_PARANOIA
printk("find_dentry_in_fhcache: %s/%s mismatch, ino=%ld, fh_ino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, fh->fh_ino);
#endif
goto out;
}
fhe->dentry = NULL;
fhe->ino = 0;
fhe->dev = 0;
nfsd_nr_put++;
/*
* Make sure the parent is in the dir cache ...
*/
parent = dget(dentry->d_parent);
if (add_to_fhcache(parent, NFSD_DIR_CACHE))
nfsd_nr_verified++;
else
dput(parent);
return dentry;
}
out:
return NULL;
}
/*
* Look for an entry in the parent directory with the specified
* inode number.
*/
static struct dentry *lookup_by_inode(struct dentry *parent, ino_t ino)
{
struct dentry *dentry;
int error;
struct nfsd_dirent dirent;
/*
* Search the directory for the inode number.
*/
error = search_dir(parent, ino, &dirent);
if (error) {
#ifdef NFSD_PARANOIA
printk("lookup_by_inode: ino %ld not found in %s\n", ino, parent->d_name.name);
#endif
goto no_entry;
}
dentry = lookup_dentry(dirent.name, dget(parent), 0);
if (!IS_ERR(dentry)) {
if (dentry->d_inode && dentry->d_inode->i_ino == ino)
goto out;
#ifdef NFSD_PARANOIA
printk("lookup_by_inode: %s/%s inode mismatch??\n",
parent->d_name.name, dentry->d_name.name);
#endif
dput(dentry);
} else {
#ifdef NFSD_PARANOIA
printk("lookup_by_inode: %s lookup failed, error=%ld\n",
dirent.name, PTR_ERR(dentry));
#endif
}
no_entry:
dentry = NULL;
out:
return dentry;
}
/*
* The is the basic lookup mechanism for turning an NFS filehandle
* into a dentry. There are several levels to the search:
* (1) Look for the dentry pointer the short-term fhcache,
* and verify that it has the correct inode number.
*
* (2) Try to validate the dentry pointer in the filehandle,
* and verify that it has the correct inode number.
*
* (3) Search for the parent dentry in the dir cache, and then
* look for the name matching the inode number.
*
* (4) The most general case ... search the whole volume for the inode.
*
* If successful, we return a dentry with the use count incremented.
*/
static struct dentry *
find_fh_dentry(struct knfs_fh *fh)
{
struct dentry *dentry, *parent;
/*
* Stage 1: Look for the dentry in the short-term fhcache.
*/
dentry = find_dentry_in_fhcache(fh);
if (dentry) {
#ifdef NFSD_DEBUG_VERBOSE
printk("find_fh_dentry: found %s/%s, d_count=%d, ino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,fh->fh_ino);
#endif
goto out;
}
/*
* Stage 2: Attempt to validate the dentry in the filehandle.
*/
dentry = fh->fh_dcookie;
if (nfsd_d_validate(dentry)) {
struct inode * dir = dentry->d_parent->d_inode;
if (dir->i_ino == fh->fh_dirino && dir->i_dev == fh->fh_dev) {
struct inode * inode = dentry->d_inode;
/*
* NFS filehandles must always have an inode,
* so we won't accept a negative dentry.
*/
if (!inode) {
#ifdef NFSD_PARANOIA
printk("find_fh_dentry: %s/%s negative, can't match %ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, fh->fh_ino);
#endif
} else if (inode->i_ino == fh->fh_ino &&
inode->i_dev == fh->fh_dev) {
dget(dentry);
#ifdef NFSD_DEBUG_VERBOSE
printk("find_fh_dentry: validated %s/%s, ino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino);
#endif
goto out;
} else {
#ifdef NFSD_PARANOIA
printk("find_fh_dentry: %s/%s mismatch, ino=%ld, fh_ino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, fh->fh_ino);
#endif
}
} else {
#ifdef NFSD_PARANOIA
printk("find_fh_dentry: %s/%s mismatch, parent ino=%ld, dirino=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dir->i_ino, fh->fh_dirino);
#endif
}
}
/*
* Stage 3: Look for the parent dentry in the fhcache ...
*/
parent = find_dentry_by_ino(fh->fh_dev, fh->fh_dirino);
if (parent) {
/*
* ... then search for the inode in the parent directory.
*/
dentry = lookup_by_inode(parent, fh->fh_ino);
dput(parent);
if (dentry)
goto out;
}
/*
* Stage 4: Search the whole volume.
*/
#ifdef NFSD_PARANOIA
printk("find_fh_dentry: %s, %ld/%ld not found -- need full search!\n",
kdevname(fh->fh_dev), fh->fh_dirino, fh->fh_ino);
#endif
dentry = NULL;
out:
/*
* Perform any needed housekeeping ...
* N.B. move this into one of the daemons ...
*/
if (jiffies >= nfsd_next_expire) {
expire_old(NFSD_FILE_CACHE, 5*HZ);
expire_old(NFSD_DIR_CACHE , 60*HZ);
nfsd_next_expire = jiffies + 5*HZ;
}
return dentry;
}
/* /*
* Perform sanity checks on the dentry in a client's file handle. * Perform sanity checks on the dentry in a client's file handle.
*
* Note that the filehandle dentry may need to be freed even after
* an error return.
*/ */
u32 u32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
...@@ -27,50 +736,53 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) ...@@ -27,50 +736,53 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
struct knfs_fh *fh = &fhp->fh_handle; struct knfs_fh *fh = &fhp->fh_handle;
u32 error = 0;
if(fhp->fh_dverified) if(fhp->fh_dverified)
return 0; goto out;
dprintk("nfsd: fh_lookup(exp %x/%ld fh %p)\n", dprintk("nfsd: fh_lookup(exp %x/%ld fh %p)\n",
fh->fh_xdev, fh->fh_xino, fh->fh_dentry); fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
/* Look up the export entry. */ /*
* Look up the export entry.
* N.B. We need to lock this while in use ...
*/
error = nfserr_stale;
exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino); exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
if (!exp) { if (!exp) /* export entry revoked */
/* nfsdstats.fhstale++; */ goto out;
return nfserr_stale; /* export entry revoked */
}
/* Check if the request originated from a secure port. */ /* Check if the request originated from a secure port. */
error = nfserr_perm;
if (!rqstp->rq_secure && EX_SECURE(exp)) { if (!rqstp->rq_secure && EX_SECURE(exp)) {
printk(KERN_WARNING printk(KERN_WARNING
"nfsd: request from insecure port (%08lx:%d)!\n", "nfsd: request from insecure port (%08lx:%d)!\n",
ntohl(rqstp->rq_addr.sin_addr.s_addr), ntohl(rqstp->rq_addr.sin_addr.s_addr),
ntohs(rqstp->rq_addr.sin_port)); ntohs(rqstp->rq_addr.sin_port));
return nfserr_perm; goto out;
} }
/* Set user creds if we haven't done so already. */ /* Set user creds if we haven't done so already. */
nfsd_setuser(rqstp, exp); nfsd_setuser(rqstp, exp);
dentry = fh->fh_dentry; /*
* Look up the dentry using the NFS fh.
if(!d_validate(dentry, fh->fh_dparent, fh->fh_dhash, fh->fh_dlen) ||
!(inode = dentry->d_inode) ||
!inode->i_nlink) {
/* Currently we cannot tell the difference between
* a bogus pointer and a true unlink between fh
* uses. But who cares about accurate error reporting
* to buggy/malicious clients... -DaveM
*/ */
error = nfserr_stale;
/* nfsdstats.fhstale++; */ dentry = find_fh_dentry(fh);
return nfserr_stale; if (!dentry)
} goto out;
/*
dget(dentry); * Note: it's possible that the returned dentry won't be the
fhp->fh_dverified = 1; * one in the filehandle. We can correct the FH for our use,
* but unfortunately the client will keep sending the broken
* one. Hopefully the lookup will keep patching things up..
*/
fhp->fh_dentry = dentry;
fhp->fh_export = exp; fhp->fh_export = exp;
fhp->fh_dverified = 1;
nfsd_nr_verified++;
/* Type check. The correct error return for type mismatches /* Type check. The correct error return for type mismatches
* does not seem to be generally agreed upon. SunOS seems to * does not seem to be generally agreed upon. SunOS seems to
...@@ -78,33 +790,189 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) ...@@ -78,33 +790,189 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
* spec says this is incorrect (implementation notes for the * spec says this is incorrect (implementation notes for the
* write call). * write call).
*/ */
if (type > 0 && (inode->i_mode & S_IFMT) != type) inode = dentry->d_inode;
return (type == S_IFDIR)? nfserr_notdir : nfserr_isdir; if (type > 0 && (inode->i_mode & S_IFMT) != type) {
if (type < 0 && (inode->i_mode & S_IFMT) == -type) error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir; goto out;
}
if (type < 0 && (inode->i_mode & S_IFMT) == -type) {
error = (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir;
goto out;
}
/* Finally, check access permissions. */ /* Finally, check access permissions. */
return nfsd_permission(fhp->fh_export, dentry, access); error = nfsd_permission(fhp->fh_export, dentry, access);
out:
return error;
} }
/* /*
* Compose file handle for NFS reply. * Compose a filehandle for an NFS reply.
*
* Note that when first composed, the dentry may not yet have
* an inode. In this case a call to fh_update should be made
* before the fh goes out on the wire ...
*/ */
void void
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
{ {
struct inode * inode = dentry->d_inode;
dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n", dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n",
exp->ex_dev, exp->ex_ino, dentry); exp->ex_dev, exp->ex_ino, dentry);
fh_init(fhp); /* initialize empty fh */ /*
fhp->fh_handle.fh_dentry = dentry; * N.B. We shouldn't need to init the fh -- the call to fh_compose
fhp->fh_handle.fh_dparent = dentry->d_parent; * may not be done on error paths, but the cleanup must call fh_put.
fhp->fh_handle.fh_dhash = dentry->d_name.hash; * Fix this soon!
fhp->fh_handle.fh_dlen = dentry->d_name.len; */
fh_init(fhp);
fhp->fh_handle.fh_dcookie = dentry;
if (inode) {
fhp->fh_handle.fh_ino = inode->i_ino;
}
fhp->fh_handle.fh_dirino = dentry->d_parent->d_inode->i_ino;
fhp->fh_handle.fh_dev = dentry->d_parent->d_inode->i_dev;
fhp->fh_handle.fh_xdev = exp->ex_dev; fhp->fh_handle.fh_xdev = exp->ex_dev;
fhp->fh_handle.fh_xino = exp->ex_ino; fhp->fh_handle.fh_xino = exp->ex_ino;
fhp->fh_dentry = dentry; /* our internal copy */
fhp->fh_export = exp; fhp->fh_export = exp;
/* We stuck it there, we know it's good. */ /* We stuck it there, we know it's good. */
fhp->fh_dverified = 1; fhp->fh_dverified = 1;
nfsd_nr_verified++;
}
/*
* Update filehandle information after changing a dentry.
*/
void
fh_update(struct svc_fh *fhp)
{
struct dentry *dentry;
struct inode *inode;
if (!fhp->fh_dverified) {
printk("fh_update: fh not verified!\n");
goto out;
}
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
if (!inode) {
printk("fh_update: %s/%s still negative!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
}
fhp->fh_handle.fh_ino = inode->i_ino;
out:
return;
}
/*
* Release a filehandle. If the filehandle carries a dentry count,
* we add the dentry to the short-term cache rather than release it.
*/
void
fh_put(struct svc_fh *fhp)
{
if (fhp->fh_dverified) {
struct dentry * dentry = fhp->fh_dentry;
fh_unlock(fhp);
fhp->fh_dverified = 0;
if (!dentry->d_count) {
printk("fh_put: %s/%s has d_count 0!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
return;
}
if (!dentry->d_inode || !add_to_fhcache(dentry, 0)) {
dput(dentry);
nfsd_nr_put++;
}
}
}
/*
* Verify that the FH dentry is still a valid dentry pointer.
* After making some preliminary checks, we ask VFS to verify
* that it is indeed a dentry.
*/
static int nfsd_d_validate(struct dentry *dentry)
{
unsigned long dent_addr = (unsigned long) dentry;
unsigned long min_addr = PAGE_OFFSET;
unsigned long max_addr = min_addr + (num_physpages << PAGE_SHIFT);
unsigned long align_mask = 0x1F;
unsigned int len;
int valid = 0;
if (dent_addr < min_addr)
goto bad_addr;
if (dent_addr > max_addr - sizeof(struct dentry))
goto bad_addr;
if ((dent_addr & ~align_mask) != dent_addr)
goto bad_addr;
/*
* Looks safe enough to dereference ...
*/
len = dentry->d_name.len;
if (len > NFS_MAXNAMLEN)
goto bad_length;
valid = d_validate(dentry, dentry->d_parent, dentry->d_name.hash, len);
out:
return valid;
bad_addr:
printk("nfsd_d_validate: invalid address %lx\n", dent_addr);
goto out;
bad_length:
printk("nfsd_d_validate: invalid length %d\n", len);
goto out;
}
/*
* Free the dentry and path caches.
*/
void nfsd_fh_free(void)
{
struct fh_entry *fhe;
struct list_head *tmp;
int i, pass = 2;
fhe = &filetable[0];
while (pass--) {
for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
struct dentry *dentry = fhe->dentry;
if (!dentry)
continue;
fhe->dentry = NULL;
dput(dentry);
nfsd_nr_put++;
}
fhe = &dirstable[0];
}
i = 0;
while ((tmp = path_inuse.next) != &path_inuse) {
struct nfsd_path *pe = list_entry(tmp, struct nfsd_path, lru);
free_path_entry(pe);
i++;
}
printk("nfsd_fh_free: %d paths freed\n", i);
printk("nfsd_fh_free: verified %d, put %d\n",
nfsd_nr_verified, nfsd_nr_put);
}
void
nfsd_fh_init(void)
{
memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
INIT_LIST_HEAD(&path_inuse);
printk("nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH);
} }
...@@ -78,6 +78,8 @@ nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, ...@@ -78,6 +78,8 @@ nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
/* /*
* Look up a path name component * Look up a path name component
* Note: the dentry in the resp->fh may be negative if the file
* doesn't exist yet.
* N.B. After this call resp->fh needs an fh_put * N.B. After this call resp->fh needs an fh_put
*/ */
static int static int
...@@ -88,9 +90,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp, ...@@ -88,9 +90,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name); dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
nfserr = nfsd_lookup(rqstp, &argp->fh, nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
argp->name,
argp->len,
&resp->fh); &resp->fh);
fh_put(&argp->fh); fh_put(&argp->fh);
...@@ -209,11 +209,10 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, ...@@ -209,11 +209,10 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC); nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
if (nfserr) if (nfserr)
goto done; /* must fh_put dirfhp even on error */ goto done; /* must fh_put dirfhp even on error */
dirp = dirfhp->fh_handle.fh_dentry->d_inode; dirp = dirfhp->fh_dentry->d_inode;
/* Check for MAY_WRITE separately. */ /* Check for MAY_WRITE separately. */
nfserr = nfsd_permission(dirfhp->fh_export, nfserr = nfsd_permission(dirfhp->fh_export, dirfhp->fh_dentry,
dirfhp->fh_handle.fh_dentry,
MAY_WRITE); MAY_WRITE);
if (nfserr == nfserr_rofs) { if (nfserr == nfserr_rofs) {
rdonly = 1; /* Non-fatal error for echo > /dev/null */ rdonly = 1; /* Non-fatal error for echo > /dev/null */
...@@ -224,7 +223,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, ...@@ -224,7 +223,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp); exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
if (newfhp->fh_dverified) if (newfhp->fh_dverified)
inode = newfhp->fh_handle.fh_dentry->d_inode; inode = newfhp->fh_dentry->d_inode;
/* Get rid of this soon... */ /* Get rid of this soon... */
if (exists && !inode) { if (exists && !inode) {
......
...@@ -363,7 +363,7 @@ int ...@@ -363,7 +363,7 @@ int
nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p, nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
struct nfsd_attrstat *resp) struct nfsd_attrstat *resp)
{ {
if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode))) if (!(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
return 0; return 0;
return xdr_ressize_check(rqstp, p); return xdr_ressize_check(rqstp, p);
} }
...@@ -373,7 +373,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p, ...@@ -373,7 +373,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
struct nfsd_diropres *resp) struct nfsd_diropres *resp)
{ {
if (!(p = encode_fh(p, &resp->fh)) if (!(p = encode_fh(p, &resp->fh))
|| !(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode))) || !(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
return 0; return 0;
return xdr_ressize_check(rqstp, p); return xdr_ressize_check(rqstp, p);
} }
...@@ -391,7 +391,7 @@ int ...@@ -391,7 +391,7 @@ int
nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
struct nfsd_readres *resp) struct nfsd_readres *resp)
{ {
if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode))) if (!(p = encode_fattr(rqstp, p, resp->fh.fh_dentry->d_inode)))
return 0; return 0;
*p++ = htonl(resp->count); *p++ = htonl(resp->count);
p += XDR_QUADLEN(resp->count); p += XDR_QUADLEN(resp->count);
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#endif #endif
extern void fh_update(struct svc_fh*);
#define NFSDDBG_FACILITY NFSDDBG_FILEOP #define NFSDDBG_FACILITY NFSDDBG_FILEOP
/* Open mode for nfsd_open */ /* Open mode for nfsd_open */
...@@ -108,10 +110,11 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, ...@@ -108,10 +110,11 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name); dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
/* Obtain dentry and export. */ /* Obtain dentry and export. */
if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0) err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP);
return err; if (err)
goto out;
dparent = fhp->fh_handle.fh_dentry; dparent = fhp->fh_dentry;
exp = fhp->fh_export; exp = fhp->fh_export;
/* Fast path... */ /* Fast path... */
...@@ -121,11 +124,16 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, ...@@ -121,11 +124,16 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
!nfsd_iscovered(dparent, exp)) { !nfsd_iscovered(dparent, exp)) {
struct dentry *dchild; struct dentry *dchild;
dchild = lookup_dentry(name, dget(dparent), 1); /* Lookup the name, but don't follow links */
dchild = lookup_dentry(name, dget(dparent), 0);
err = PTR_ERR(dchild); err = PTR_ERR(dchild);
if (IS_ERR(dchild)) if (IS_ERR(dchild))
return nfserrno(-err); return nfserrno(-err);
/*
* Note: we compose the filehandle now, but as the
* dentry may be negative, it may need to be updated.
*/
fh_compose(resfh, exp, dchild); fh_compose(resfh, exp, dchild);
return (dchild->d_inode ? 0 : nfserr_noent); return (dchild->d_inode ? 0 : nfserr_noent);
} }
...@@ -135,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, ...@@ -135,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
return nfserr_noent; return nfserr_noent;
if (nfsd_iscovered(dparent, exp)) if (nfsd_iscovered(dparent, exp))
return nfserr_acces; return nfserr_acces;
out:
return err; return err;
} }
...@@ -158,10 +167,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) ...@@ -158,10 +167,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
ftype = S_IFREG; ftype = S_IFREG;
/* Get inode */ /* Get inode */
if ((err = fh_verify(rqstp, fhp, ftype, accmode)) != 0) err = fh_verify(rqstp, fhp, ftype, accmode);
return err; if (err)
goto out;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
/* The size case is special... */ /* The size case is special... */
...@@ -169,7 +179,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) ...@@ -169,7 +179,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (iap->ia_size < inode->i_size) { if (iap->ia_size < inode->i_size) {
err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC); err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
if (err != 0) if (err != 0)
return err; goto out;
} }
if ((err = get_write_access(inode)) != 0) if ((err = get_write_access(inode)) != 0)
return nfserrno(-err); return nfserrno(-err);
...@@ -211,8 +221,9 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) ...@@ -211,8 +221,9 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (EX_ISSYNC(fhp->fh_export)) if (EX_ISSYNC(fhp->fh_export))
write_inode_now(inode); write_inode_now(inode);
} }
err = 0;
return 0; out:
return err;
} }
/* /*
...@@ -230,20 +241,23 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -230,20 +241,23 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
access = wflag? MAY_WRITE : MAY_READ; access = wflag? MAY_WRITE : MAY_READ;
if ((err = fh_verify(rqstp, fhp, type, access)) != 0) if ((err = fh_verify(rqstp, fhp, type, access)) != 0)
return err; goto out;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
/* Disallow access to files with the append-only bit set or /* Disallow access to files with the append-only bit set or
* with mandatory locking enabled */ * with mandatory locking enabled */
err = nfserr_perm;
if (IS_APPEND(inode) || IS_ISMNDLK(inode)) if (IS_APPEND(inode) || IS_ISMNDLK(inode))
return nfserr_perm; goto out;
if (!inode->i_op || !inode->i_op->default_file_ops) if (!inode->i_op || !inode->i_op->default_file_ops)
return nfserr_perm; goto out;
if (wflag && (err = get_write_access(inode)) != 0) if (wflag && (err = get_write_access(inode)) != 0) {
return nfserrno(-err); err = nfserrno(-err);
goto out;
}
memset(filp, 0, sizeof(*filp)); memset(filp, 0, sizeof(*filp));
filp->f_op = inode->i_op->default_file_ops; filp->f_op = inode->i_op->default_file_ops;
...@@ -252,6 +266,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -252,6 +266,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
filp->f_mode = wflag? FMODE_WRITE : FMODE_READ; filp->f_mode = wflag? FMODE_WRITE : FMODE_READ;
filp->f_dentry = dentry; filp->f_dentry = dentry;
err = 0;
if (filp->f_op->open) { if (filp->f_op->open) {
err = filp->f_op->open(inode, filp); err = filp->f_op->open(inode, filp);
if (err) { if (err) {
...@@ -262,11 +277,11 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -262,11 +277,11 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
* is really on callers stack frame. -DaveM * is really on callers stack frame. -DaveM
*/ */
filp->f_count--; filp->f_count--;
return nfserrno(-err); err = nfserrno(-err);
} }
} }
out:
return 0; return err;
} }
/* /*
...@@ -281,7 +296,8 @@ nfsd_close(struct file *filp) ...@@ -281,7 +296,8 @@ nfsd_close(struct file *filp)
if (!inode->i_count) if (!inode->i_count)
printk(KERN_WARNING "nfsd: inode count == 0!\n"); printk(KERN_WARNING "nfsd: inode count == 0!\n");
if (!dentry->d_count) if (!dentry->d_count)
printk(KERN_WARNING "nfsd: wheee, dentry count == 0!\n"); printk(KERN_WARNING "nfsd: wheee, %s/%s d_count == 0!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
if (filp->f_op && filp->f_op->release) if (filp->f_op && filp->f_op->release)
filp->f_op->release(inode, filp); filp->f_op->release(inode, filp);
if (filp->f_mode & FMODE_WRITE) if (filp->f_mode & FMODE_WRITE)
...@@ -299,6 +315,9 @@ nfsd_sync(struct inode *inode, struct file *filp) ...@@ -299,6 +315,9 @@ nfsd_sync(struct inode *inode, struct file *filp)
/* /*
* Obtain the readahead parameters for the given file * Obtain the readahead parameters for the given file
*
* N.B. is raparm cache for a file cleared when the file closes??
* (dentry might be reused later.)
*/ */
static inline struct raparms * static inline struct raparms *
nfsd_get_raparms(struct dentry *dentry) nfsd_get_raparms(struct dentry *dentry)
...@@ -506,39 +525,53 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -506,39 +525,53 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct inode *dirp; struct inode *dirp;
int err; int err;
err = nfserr_perm;
if (!flen) if (!flen)
return nfserr_perm; goto out;
if (!(iap->ia_valid & ATTR_MODE)) if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0; iap->ia_mode = 0;
if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
return err; if (err)
goto out;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
dirp = dentry->d_inode; dirp = dentry->d_inode;
/* Get all the sanity checks out of the way before we lock the parent. */ /* Get all the sanity checks out of the way before we lock the parent. */
if(!dirp->i_op || !dirp->i_op->lookup) { err = nfserr_notdir;
return nfserrno(-ENOTDIR); if(!dirp->i_op || !dirp->i_op->lookup)
} else if(type == S_IFREG) { goto out;
err = nfserr_perm;
if (type == S_IFREG) {
if(!dirp->i_op->create) if(!dirp->i_op->create)
return nfserr_perm; goto out;
} else if(type == S_IFDIR) { } else if(type == S_IFDIR) {
if(!dirp->i_op->mkdir) if(!dirp->i_op->mkdir)
return nfserr_perm; goto out;
} else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) { } else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
if(!dirp->i_op->mknod) if(!dirp->i_op->mknod)
return nfserr_perm; goto out;
} else { } else {
return nfserr_perm; goto out;
} }
if(!resfhp->fh_dverified) { /*
* The response filehandle may have been setup already ...
*/
if (!resfhp->fh_dverified) {
dchild = lookup_dentry(fname, dget(dentry), 0); dchild = lookup_dentry(fname, dget(dentry), 0);
err = PTR_ERR(dchild); err = PTR_ERR(dchild);
if(IS_ERR(dchild)) if(IS_ERR(dchild))
return nfserrno(-err); return nfserrno(-err);
} else } else
dchild = resfhp->fh_handle.fh_dentry; dchild = resfhp->fh_dentry;
/*
* Make sure the child dentry is still negative ...
*/
if (dchild->d_inode) {
printk("nfsd_create: dentry %s/%s not negative!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
}
/* Looks good, lock the directory. */ /* Looks good, lock the directory. */
fh_lock(fhp); fh_lock(fhp);
...@@ -562,9 +595,15 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -562,9 +595,15 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (EX_ISSYNC(fhp->fh_export)) if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp); write_inode_now(dirp);
/* If needed, assemble the file handle for the newly created file. */ /*
if(!resfhp->fh_dverified) * Assemble the file handle for the newly created file,
* or update the filehandle to get the new inode info.
*/
if (!resfhp->fh_dverified) {
fh_compose(resfhp, fhp->fh_export, dchild); fh_compose(resfhp, fhp->fh_export, dchild);
} else {
fh_update(resfhp);
}
/* Set file attributes. Mode has already been set and /* Set file attributes. Mode has already been set and
* setting uid/gid works only for root. Irix appears to * setting uid/gid works only for root. Irix appears to
...@@ -574,7 +613,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -574,7 +613,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = 0; err = 0;
if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
err = nfsd_setattr(rqstp, resfhp, iap); err = nfsd_setattr(rqstp, resfhp, iap);
out:
return err; return err;
} }
...@@ -597,7 +636,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) ...@@ -597,7 +636,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
if ((err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0) if ((err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
return err; return err;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
if ((err = get_write_access(inode)) != 0) if ((err = get_write_access(inode)) != 0)
...@@ -635,7 +674,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) ...@@ -635,7 +674,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0) if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
return err; return err;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
if (!inode->i_op || !inode->i_op->readlink) if (!inode->i_op || !inode->i_op->readlink)
...@@ -673,7 +712,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -673,7 +712,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0) if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
return err; return err;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
dirp = dentry->d_inode; dirp = dentry->d_inode;
if (nfsd_iscovered(dentry, fhp->fh_export) || if (nfsd_iscovered(dentry, fhp->fh_export) ||
...@@ -720,7 +759,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, ...@@ -720,7 +759,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
(err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0) (err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
return err; return err;
ddir = ffhp->fh_handle.fh_dentry; ddir = ffhp->fh_dentry;
dirp = ddir->d_inode; dirp = ddir->d_inode;
dnew = lookup_dentry(fname, dget(ddir), 1); dnew = lookup_dentry(fname, dget(ddir), 1);
...@@ -731,7 +770,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, ...@@ -731,7 +770,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = -EEXIST; err = -EEXIST;
if (dnew->d_inode) if (dnew->d_inode)
goto dput_and_out; goto dput_and_out;
dest = tfhp->fh_handle.fh_dentry->d_inode; dest = tfhp->fh_dentry->d_inode;
err = -EPERM; err = -EPERM;
if (!len) if (!len)
...@@ -794,10 +833,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, ...@@ -794,10 +833,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
|| (err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0) || (err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
return err; return err;
fdentry = ffhp->fh_handle.fh_dentry; fdentry = ffhp->fh_dentry;
fdir = fdentry->d_inode; fdir = fdentry->d_inode;
tdentry = tfhp->fh_handle.fh_dentry; tdentry = tfhp->fh_dentry;
tdir = tdentry->d_inode; tdir = tdentry->d_inode;
if (!flen || (fname[0] == '.' && if (!flen || (fname[0] == '.' &&
...@@ -816,6 +855,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, ...@@ -816,6 +855,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (IS_ERR(ndentry)) if (IS_ERR(ndentry))
goto out_dput_old; goto out_dput_old;
/* N.B. check this ... problems in locking?? */
nfsd_double_down(&tdir->i_sem, &fdir->i_sem); nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
err = -EXDEV; err = -EXDEV;
if (fdir->i_dev != tdir->i_dev) if (fdir->i_dev != tdir->i_dev)
...@@ -856,7 +896,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -856,7 +896,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0) if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
return err; return err;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
dirp = dentry->d_inode; dirp = dentry->d_inode;
rdentry = lookup_dentry(fname, dget(dentry), 0); rdentry = lookup_dentry(fname, dget(dentry), 0);
...@@ -975,20 +1015,24 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat) ...@@ -975,20 +1015,24 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
unsigned long oldfs; unsigned long oldfs;
int err; int err;
if ((err = fh_verify(rqstp, fhp, 0, MAY_NOP)) != 0) err = fh_verify(rqstp, fhp, 0, MAY_NOP);
return err; if (err)
dentry = fhp->fh_handle.fh_dentry; goto out;
dentry = fhp->fh_dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
err = nfserr_io;
if (!(sb = inode->i_sb) || !sb->s_op->statfs) if (!(sb = inode->i_sb) || !sb->s_op->statfs)
return nfserr_io; goto out;
oldfs = get_fs(); oldfs = get_fs();
set_fs (KERNEL_DS); set_fs (KERNEL_DS);
sb->s_op->statfs(sb, stat, sizeof(*stat)); sb->s_op->statfs(sb, stat, sizeof(*stat));
set_fs (oldfs); set_fs (oldfs);
err = 0;
return 0; out:
return err;
} }
/* /*
......
...@@ -328,6 +328,17 @@ asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct ...@@ -328,6 +328,17 @@ asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct
zero_fd_set(n, &fds->res_out); zero_fd_set(n, &fds->res_out);
zero_fd_set(n, &fds->res_ex); zero_fd_set(n, &fds->res_ex);
error = do_select(n, fds, wait); error = do_select(n, fds, wait);
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
unsigned long timeout = current->timeout - jiffies - 1;
unsigned long sec = 0, usec = 0;
if ((long) timeout > 0) {
sec = timeout / HZ;
usec = timeout % HZ;
usec *= (1000000/HZ);
}
put_user(sec, &tvp->tv_sec);
put_user(usec, &tvp->tv_usec);
}
current->timeout = 0; current->timeout = 0;
if (error < 0) if (error < 0)
goto out; goto out;
......
...@@ -14,7 +14,7 @@ static inline void clear_active_bhs(unsigned long x) ...@@ -14,7 +14,7 @@ static inline void clear_active_bhs(unsigned long x)
unsigned long temp; unsigned long temp;
__asm__ __volatile__( __asm__ __volatile__(
"1: ldq_l %0,%1\n" "1: ldq_l %0,%1\n"
" and %0,%2,%0\n" " bic %0,%2,%0\n"
" stq_c %0,%1\n" " stq_c %0,%1\n"
" beq %0,2f\n" " beq %0,2f\n"
".section .text2,\"ax\"\n" ".section .text2,\"ax\"\n"
......
...@@ -27,12 +27,12 @@ ...@@ -27,12 +27,12 @@
* ino/dev of the exported inode. * ino/dev of the exported inode.
*/ */
struct nfs_fhbase { struct nfs_fhbase {
struct dentry *fb_dentry; struct dentry * fb_dentry; /* dentry cookie */
struct dentry *fb_dparent; ino_t fb_ino; /* our inode number */
unsigned int fb_dhash; ino_t fb_dirino; /* dir inode number */
unsigned int fb_dlen; dev_t fb_dev; /* our device */
ino_t fb_xino;
dev_t fb_xdev; dev_t fb_xdev;
ino_t fb_xino;
}; };
#define NFS_FH_PADDING (NFS_FHSIZE - sizeof(struct nfs_fhbase)) #define NFS_FH_PADDING (NFS_FHSIZE - sizeof(struct nfs_fhbase))
...@@ -41,13 +41,12 @@ struct knfs_fh { ...@@ -41,13 +41,12 @@ struct knfs_fh {
__u8 fh_cookie[NFS_FH_PADDING]; __u8 fh_cookie[NFS_FH_PADDING];
}; };
#define fh_dentry fh_base.fb_dentry #define fh_dcookie fh_base.fb_dentry
#define fh_dparent fh_base.fb_dparent #define fh_ino fh_base.fb_ino
#define fh_dhash fh_base.fb_dhash #define fh_dirino fh_base.fb_dirino
#define fh_dlen fh_base.fb_dlen #define fh_dev fh_base.fb_dev
#define fh_xino fh_base.fb_xino
#define fh_xdev fh_base.fb_xdev #define fh_xdev fh_base.fb_xdev
#define fh_xino fh_base.fb_xino
#ifdef __KERNEL__ #ifdef __KERNEL__
...@@ -57,6 +56,7 @@ struct knfs_fh { ...@@ -57,6 +56,7 @@ struct knfs_fh {
*/ */
typedef struct svc_fh { typedef struct svc_fh {
struct knfs_fh fh_handle; /* FH data */ struct knfs_fh fh_handle; /* FH data */
struct dentry * fh_dentry; /* validated dentry */
struct svc_export * fh_export; /* export pointer */ struct svc_export * fh_export; /* export pointer */
size_t fh_pre_size; /* size before operation */ size_t fh_pre_size; /* size before operation */
time_t fh_pre_mtime; /* mtime before oper */ time_t fh_pre_mtime; /* mtime before oper */
...@@ -69,17 +69,26 @@ typedef struct svc_fh { ...@@ -69,17 +69,26 @@ typedef struct svc_fh {
/* /*
* Shorthand for dprintk()'s * Shorthand for dprintk()'s
*/ */
#define SVCFH_DENTRY(f) ((f)->fh_handle.fh_dentry) #define SVCFH_DENTRY(f) ((f)->fh_dentry)
/* /*
* Function prototypes * Function prototypes
*/ */
u32 fh_verify(struct svc_rqst *, struct svc_fh *, int, int); u32 fh_verify(struct svc_rqst *, struct svc_fh *, int, int);
void fh_compose(struct svc_fh *, struct svc_export *, struct dentry *); void fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
void fh_put(struct svc_fh *);
void nfsd_fh_init(void);
void nfsd_fh_free(void);
static __inline__ struct svc_fh * static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, struct svc_fh *src) fh_copy(struct svc_fh *dst, struct svc_fh *src)
{ {
if (src->fh_dverified) {
struct dentry *dentry = src->fh_dentry;
printk("fh_copy: copying %s/%s, already verified!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
}
*dst = *src; *dst = *src;
return dst; return dst;
} }
...@@ -97,7 +106,7 @@ fh_init(struct svc_fh *fhp) ...@@ -97,7 +106,7 @@ fh_init(struct svc_fh *fhp)
static inline void static inline void
fh_lock(struct svc_fh *fhp) fh_lock(struct svc_fh *fhp)
{ {
struct inode *inode = fhp->fh_handle.fh_dentry->d_inode; struct inode *inode = fhp->fh_dentry->d_inode;
/* /*
dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n", dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
...@@ -117,7 +126,7 @@ fh_lock(struct svc_fh *fhp) ...@@ -117,7 +126,7 @@ fh_lock(struct svc_fh *fhp)
static inline void static inline void
fh_unlock(struct svc_fh *fhp) fh_unlock(struct svc_fh *fhp)
{ {
struct inode *inode = fhp->fh_handle.fh_dentry->d_inode; struct inode *inode = fhp->fh_dentry->d_inode;
if (fhp->fh_locked) { if (fhp->fh_locked) {
if (!fhp->fh_post_version) if (!fhp->fh_post_version)
...@@ -130,17 +139,7 @@ fh_unlock(struct svc_fh *fhp) ...@@ -130,17 +139,7 @@ fh_unlock(struct svc_fh *fhp)
/* /*
* Release an inode * Release an inode
*/ */
#ifndef NFSD_DEBUG #if 0
static inline void
fh_put(struct svc_fh *fhp)
{
if (fhp->fh_dverified) {
fh_unlock(fhp);
dput(fhp->fh_handle.fh_dentry);
fhp->fh_dverified = 0;
}
}
#else
#define fh_put(fhp) __fh_put(fhp, __FILE__, __LINE__) #define fh_put(fhp) __fh_put(fhp, __FILE__, __LINE__)
static inline void static inline void
...@@ -151,7 +150,7 @@ __fh_put(struct svc_fh *fhp, char *file, int line) ...@@ -151,7 +150,7 @@ __fh_put(struct svc_fh *fhp, char *file, int line)
if (!fhp->fh_dverified) if (!fhp->fh_dverified)
return; return;
dentry = fhp->fh_handle.fh_dentry; dentry = fhp->fh_dentry;
if (!dentry->d_count) { if (!dentry->d_count) {
printk("nfsd: trying to free free dentry in %s:%d\n" printk("nfsd: trying to free free dentry in %s:%d\n"
" file %s/%s\n", " file %s/%s\n",
...@@ -159,14 +158,12 @@ __fh_put(struct svc_fh *fhp, char *file, int line) ...@@ -159,14 +158,12 @@ __fh_put(struct svc_fh *fhp, char *file, int line)
dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name);
} else { } else {
fh_unlock(fhp); fh_unlock(fhp);
dput(dentry);
fhp->fh_dverified = 0; fhp->fh_dverified = 0;
dput(dentry);
} }
} }
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* NFSD_FH_H */ #endif /* NFSD_FH_H */
...@@ -766,7 +766,6 @@ svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp) ...@@ -766,7 +766,6 @@ svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp)
} }
} }
printk("svc_recv: svsk=%p, use count=%d\n", svsk, svsk->sk_inuse);
dprintk("svc: server %p servicing socket %p\n", rqstp, svsk); dprintk("svc: server %p servicing socket %p\n", rqstp, svsk);
len = svsk->sk_recvfrom(rqstp); len = svsk->sk_recvfrom(rqstp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment