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
S: Oak Park, Illinois 60302
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
E: bobf@advansys.com
D: AdvanSys SCSI driver
......
VERSION = 2
PATCHLEVEL = 1
SUBLEVEL = 64
SUBLEVEL = 65
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
......
......@@ -349,6 +349,10 @@ ENTRY(page_fault)
pushl $ SYMBOL_NAME(do_page_fault)
jmp error_code
ENTRY(page_fault_f00f)
pushl $ SYMBOL_NAME(do_page_fault_f00f)
jmp error_code
ENTRY(spurious_interrupt_bug)
pushl $0
pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
......
......@@ -103,6 +103,7 @@ asmlinkage void segment_not_present(void);
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
asmlinkage void page_fault_f00f(void);
asmlinkage void coprocessor_error(void);
asmlinkage void reserved(void);
asmlinkage void alignment_check(void);
......@@ -417,6 +418,14 @@ __initfunc(void trap_init_f00f_bug(void))
{
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,
* and move the IDT to have entry #7 starting at
......@@ -433,6 +442,7 @@ __initfunc(void trap_init_f00f_bug(void))
*/
idt = (struct desc_struct *)(page - 7*8);
__asm__ __volatile__("lidt %0": "=m" (idt_descr));
}
......
......@@ -74,15 +74,6 @@ int __verify_write(const void * addr, unsigned long size)
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,
......@@ -94,45 +85,17 @@ extern int pentium_f00f_bug;
* bit 1 == 0 means read, 1 means write
* 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 mm_struct *mm;
struct vm_area_struct * vma;
unsigned long address;
unsigned long page;
unsigned long fixup;
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();
tsk = current;
mm = tsk->mm;
......@@ -253,3 +216,65 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
out:
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:
=========
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)
......
......@@ -3274,7 +3274,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
return 0;
case BLKRAGET:
return put_user(read_ahead[MAJOR(inode->i_rdev)],
(int *) param);
(long *) param);
case BLKFLSBUF:
if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
......@@ -3283,7 +3283,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
case BLKGETSIZE:
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
* partition tables */
}
......
......@@ -19,7 +19,7 @@
*/
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.
First set are undocumented and spelled per Intel recommendations. */
......@@ -27,13 +27,16 @@ static const char *version =
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 rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */
static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
static int txdmacount = 128;
static int rxdmacount = 0;
/* If defined use the copy-only-tiny-buffer scheme for higher performance.
The value sets the copy breakpoint. Lower uses more memory, but is
faster. */
#define SKBUFF_RX_COPYBREAK 256
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
Lower values use more memory, but are faster. */
static int rx_copybreak = 200;
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
#include <linux/config.h>
#ifdef MODULE
......@@ -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
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
associated with each RxFD. The driver implements this by reserving space
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!
/* Time in jiffies before concluding the transmitter is hung. */
#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.
Typically this takes 0 ticks. */
static inline void wait_for_cmd_done(int cmd_ioaddr)
......@@ -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 int speedo_close(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);
......@@ -458,24 +461,21 @@ static void set_rx_mode(struct device *dev);
/* 'options' is used to pass a transceiver override or full-duplex flag
e.g. "options=16" for FD, "options=32" for 100mbps-only. */
static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
#ifdef MODULE
static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int debug = -1; /* The debug level */
#endif
/* A list of all installed Speedo devices, for removing the driver module. */
static struct device *root_speedo_dev = NULL;
#endif
int eepro100_init(struct device *dev)
{
int cards_found = 0;
if (pcibios_present()) {
int pci_index;
for (pci_index = 0; pci_index < 8; pci_index++) {
static int pci_index = 0;
for (; pci_index < 8; pci_index++) {
unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
int pci_ioaddr;
......@@ -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. */
struct speedo_private *sp;
char *product;
int i;
u16 eeprom[0x40];
......@@ -558,7 +559,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
u16 sum = 0;
int j;
for (j = 0, i = 0; i < 0x40; i++) {
unsigned short value = read_eeprom(ioaddr, i);
u16 value = read_eeprom(ioaddr, i);
eeprom[i] = value;
sum += value;
if (i < 3) {
......@@ -579,8 +580,13 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
action. */
outl(0, ioaddr + SCBPort);
printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
dev->name, ioaddr);
if (eeprom[3] & 0x0100)
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++)
printk("%2.2X:", dev->dev_addr[i]);
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,
{
const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
/* 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. */
if (eeprom[3] & 0x03)
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,
}
/* 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[1] = -1;
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,
}
#endif /* kernel_bloat */
outl(0, ioaddr + SCBPort);
/* We do a request_region() only to register /proc/ioports info. */
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,
dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
sp = dev->priv;
memset(sp, 0, sizeof(*sp));
#ifdef MODULE
sp->next_module = root_speedo_dev;
root_speedo_dev = dev;
#endif
if (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,
sp->phy[1] = eeprom[7];
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)
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. */
dev->open = &speedo_open;
......@@ -708,6 +712,9 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
#ifdef NEW_MULTICAST
dev->set_multicast_list = &set_rx_mode;
#endif
#ifdef HAVE_PRIVATE_IOCTL
dev->do_ioctl = &speedo_ioctl;
#endif
return;
}
......@@ -726,7 +733,11 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
/* Delay between EEPROM clock transitions.
This is a "nasty" timing loop, but PC compatible machines are defined
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)
#endif
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << 6)
......@@ -830,11 +841,13 @@ speedo_open(struct device *dev)
MOD_INC_USE_COUNT;
/* Load the statistics block address. */
wait_for_cmd_done(ioaddr + SCBCmd);
outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
sp->lstats.done_marker = 0;
speedo_init_rx_ring(dev);
wait_for_cmd_done(ioaddr + SCBCmd);
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
......@@ -845,8 +858,8 @@ speedo_open(struct device *dev)
/* Fill the first command with our physical address. */
{
unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
u16 *eaddrs = (u16 *)dev->dev_addr;
u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr);
/* Avoid a bug(?!) here by marking the command already completed. */
sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
......@@ -860,6 +873,7 @@ speedo_open(struct device *dev)
sp->dirty_tx = 0;
sp->tx_full = 0;
wait_for_cmd_done(ioaddr + SCBCmd);
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
......@@ -896,6 +910,7 @@ speedo_open(struct device *dev)
sp->timer.function = &speedo_timer; /* timer handler */
add_timer(&sp->timer);
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_DUMPSTATS, ioaddr + SCBCmd);
return 0;
}
......@@ -1079,6 +1094,7 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev)
sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
/* Trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
restore_flags(flags);
}
......@@ -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 speedo_private *sp;
int ioaddr, boguscnt = INTR_WORK;
int ioaddr, boguscnt = max_interrupt_work;
unsigned short status;
#ifndef final_version
......@@ -1169,7 +1185,7 @@ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
int status = sp->tx_ring[entry].status;
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);
if ((status & 0x8000) == 0)
break; /* It still hasn't been processed. */
......@@ -1256,13 +1272,13 @@ speedo_rx(struct device *dev)
dev->name, status);
} else {
/* 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;
int rx_in_place = 0;
/* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */
if (pkt_len > SKBUFF_RX_COPYBREAK) {
if (pkt_len > rx_copybreak) {
struct sk_buff *newskb;
char *temp;
......@@ -1339,8 +1355,14 @@ speedo_rx(struct device *dev)
#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
if (! rx_in_place) {
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),
bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
#endif
}
skb->protocol = eth_type_trans(skb, dev);
#else
......@@ -1478,12 +1500,39 @@ speedo_get_stats(struct device *dev)
sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
sp->lstats.done_marker = 0x0000;
if (dev->start)
if (dev->start) {
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_DUMPSTATS, ioaddr + SCBCmd);
}
}
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.
This is very ugly with Intel chips -- we usually have to execute an
entire configuration command, plus process a multicast command.
......@@ -1542,6 +1591,7 @@ set_rx_mode(struct device *dev)
virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = &sp->config_cmd;
restore_flags(flags);
......@@ -1557,8 +1607,7 @@ set_rx_mode(struct device *dev)
if (new_rx_mode == 0 && dev->mc_count < 3) {
/* The simple case of 0-2 multicast list entries occurs often, and
fits within one tx_ring[] entry. */
u16 *setup_params;
unsigned short *eaddrs;
u16 *setup_params, *eaddrs;
struct dev_mc_list *mclist;
save_flags(flags);
......@@ -1569,12 +1618,12 @@ set_rx_mode(struct device *dev)
sp->tx_ring[entry].link =
virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
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;
/* Fill in the multicast addresses. */
for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
i++, mclist = mclist->next) {
eaddrs = (unsigned short *)mclist->dmi_addr;
eaddrs = (u16 *)mclist->dmi_addr;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
......@@ -1582,15 +1631,16 @@ set_rx_mode(struct device *dev)
sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
restore_flags(flags);
} else if (new_rx_mode == 0) {
/* This does not work correctly, but why not? */
struct dev_mc_list *mclist;
unsigned short *eaddrs;
u16 *eaddrs;
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;
if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
......@@ -1616,12 +1666,12 @@ set_rx_mode(struct device *dev)
mc_setup_frm->status = 0;
mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
/* Link set below. */
setup_params = (short *)mc_setup_frm->params;
setup_params = (u16 *)mc_setup_frm->params;
*setup_params++ = dev->mc_count*6;
/* Fill in the multicast addresses. */
for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
i++, mclist = mclist->next) {
eaddrs = (unsigned short *)mclist->dmi_addr;
eaddrs = (u16 *)mclist->dmi_addr;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
......@@ -1647,6 +1697,7 @@ set_rx_mode(struct device *dev)
sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = mc_setup_frm;
restore_flags(flags);
......@@ -1663,6 +1714,21 @@ set_rx_mode(struct device *dev)
char kernel_version[] = UTS_RELEASE;
#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
init_module(void)
{
......
......@@ -100,7 +100,7 @@ static char *PPA_MODE_STRING[] =
int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
/* 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_SELECT_TMO 5000 /* how long to wait for target ? */
#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */
......
......@@ -37,7 +37,7 @@
/* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define
* the following line.
*/
#undef HAS_LOWLEVEL_H
#define HAS_LOWLEVEL_H
/* if your system doesn't support patch manager (OSS 3.7 or newer),
* define the following line.
......
......@@ -327,7 +327,7 @@ sound_mmap (struct file *file, struct vm_area_struct *vma)
vma->vm_page_prot))
return -EAGAIN;
vma->vm_dentry = file->f_dentry;
vma->vm_dentry = dget(file->f_dentry);
dmap->mapping_flags |= DMA_MAP_MAPPED;
......
......@@ -118,6 +118,11 @@ void dput(struct dentry *dentry)
}
list_add(&dentry->d_lru, &dentry_unused);
dentry_stat.nr_unused++;
/*
* Update the timestamp
*/
dentry->d_reftime = jiffies;
out:
if (count >= 0) {
dentry->d_count = count;
......@@ -135,15 +140,12 @@ void dput(struct dentry *dentry)
* Try to invalidate the dentry if it turns out to be
* possible. If there are other users of the dentry we
* 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)
{
/* 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)
return -EBUSY;
......@@ -152,27 +154,31 @@ int d_invalidate(struct dentry * dentry)
}
/*
* Selects less valuable dentries to be pruned when
* we need inodes or memory. The selected dentries
* are moved to the old end of the list where
* prune_dcache() can find them.
* Select less valuable dentries to be pruned when we need
* inodes or memory. The selected dentries are moved to the
* old end of the list where 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 = dentry_unused.prev;
int forward = 0, young = 0, depth = dentry_stat.nr_unused >> 1;
int found = 0, pages = 0;
struct list_head *next, *tail = &dentry_unused;
int found = 0, forward = 0, young = 8;
int depth = dentry_stat.nr_unused >> 1;
unsigned long min_value = 0, max_value = 4;
#ifdef DCACHE_DEBUG
printk("select_dcache: %d unused, count=%d, pages=%d\n",
dentry_stat.nr_unused, count, page_count);
#endif
if (page_count)
max_value = -1;
next = tail->prev;
while (next != &dentry_unused && depth--) {
struct list_head *tmp = next;
struct dentry *dentry = list_entry(tmp, struct dentry, d_lru);
struct inode *inode = dentry->d_inode;
unsigned long value = 0;
unsigned long value = 0;
next = tmp->prev;
if (forward)
......@@ -184,56 +190,57 @@ dentry_stat.nr_unused, count, page_count);
continue;
}
/*
* Select dentries based on the page cache count ...
* should factor in number of uses as well.
*/
if (inode) {
if (inode->i_state)
continue;
value = inode->i_nrpages;
}
/*
* Consider various exemptions ...
* Check the dentry's age to see whether to change direction.
*/
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 (++young > 8) {
forward = 1;
next = dentry_unused.next;
if (!forward) {
int age = (jiffies - dentry->d_reftime) / HZ;
if (age < dentry_stat.age_limit) {
if (!--young) {
forward = 1;
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
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
}
continue;
}
continue;
}
} else {
/*
* If we're scanning from the front, don't take
* files with only a trivial amount of memory.
*/
if (value < 3 || value > 15)
}
/*
* Select dentries based on the page cache count ...
* 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 (inode) {
value = inode->i_nrpages;
if (value >= max_value || value < min_value)
continue;
if (inode->i_state || inode->i_count > 1)
continue;
}
/*
* Move the dentry behind the tail
* Move the selected dentries behind the tail.
*/
if (tmp != tail->prev) {
list_del(tmp);
list_add(tmp, tail->prev);
}
tail = tmp;
pages += value;
if (++found >= count)
found++;
if (inode && --inode_count <= 0)
break;
if (page_count && pages >= page_count)
if (page_count && (page_count -= value) <= 0)
break;
}
return found;
......@@ -430,7 +437,7 @@ void check_dcache_memory()
if (goal) {
if (goal > 50)
goal = 50;
count = select_dcache(128, goal);
count = select_dcache(32, goal);
#ifdef DCACHE_DEBUG
printk("check_dcache_memory: goal=%d, count=%d\n", goal, count);
#endif
......@@ -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.
*/
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);
#endif
prune_dcache(8);
......@@ -579,30 +586,33 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
int d_validate(struct dentry *dentry, struct dentry *dparent,
unsigned int hash, unsigned int len)
{
struct list_head *base = d_hash(dparent, hash);
struct list_head *lhp = base;
while ((lhp = lhp->next) != base) {
if (dentry == list_entry(lhp, struct dentry, d_hash))
goto found_it;
}
/* Special case, local mount points don't live in the hashes.
* So if we exhausted the chain, search the super blocks.
*/
if (dentry && dentry == dparent) {
struct super_block *sb;
struct list_head *base, *lhp;
int valid = 1;
if (dentry != dparent) {
base = d_hash(dparent, hash);
lhp = base;
while ((lhp = lhp->next) != base) {
if (dentry == list_entry(lhp, struct dentry, d_hash))
goto out;
}
} else {
/*
* Special case: local mount points don't live in
* the hashes, so we search the super blocks.
*/
struct super_block *sb = super_blocks + 0;
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)
goto found_it;
goto out;
}
}
return 0;
found_it:
return (dentry->d_parent == dparent) &&
(dentry->d_name.hash == hash) &&
(dentry->d_name.len == len);
valid = 0;
out:
return valid;
}
/*
......
......@@ -358,33 +358,30 @@ static int 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 ...
*/
if (inodes_stat.preshrink) {
spin_unlock(&inode_lock);
select_dcache(goal, 0);
prune_dcache(goal);
spin_lock(&inode_lock);
}
if (inodes_stat.preshrink)
goto preshrink;
repeat:
found = free_inodes(goal);
/*
* If we didn't free any inodes, do a limited
* pruning of the dcache to help the next time.
*/
if (!found) {
retry = 0;
do {
if (free_inodes(goal))
break;
/*
* If we didn't free any inodes, do a limited
* pruning of the dcache to help the next time.
*/
preshrink:
spin_unlock(&inode_lock);
select_dcache(goal, 0);
prune_dcache(goal);
found = select_dcache(goal, 0);
if (found < goal)
found = goal;
prune_dcache(found);
spin_lock(&inode_lock);
if (inodes_stat.preshrink && !retried++)
goto repeat;
}
} while (retry--);
}
/*
......@@ -440,11 +437,11 @@ static struct inode * grow_inodes(void)
* If the allocation failed, do an extensive pruning of
* the dcache and then try again to free some inodes.
*/
prune_dcache(128);
prune_dcache(inodes_stat.nr_inodes >> 2);
inodes_stat.preshrink = 1;
spin_lock(&inode_lock);
free_inodes(128);
free_inodes(inodes_stat.nr_inodes >> 2);
{
struct list_head *tmp = inode_unused.next;
if (tmp != &inode_unused) {
......
......@@ -32,10 +32,16 @@ static struct semaphore nlm_file_sema = MUTEX;
* Lookup file info. If it doesn't exist, create a file info struct
* 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:
* 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
* the proper R/W checking.
*
* The dentry in the FH may not be validated .. can we call this with
* the full svc_fh?
*/
u32
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 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;
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 */
down(&nlm_file_sema);
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)))
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))) {
up(&nlm_file_sema);
return nlm_lck_denied_nolocks;
......
......@@ -235,9 +235,11 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
result = ERR_PTR(-ENOMEM);
if (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);
......
......@@ -179,27 +179,24 @@ exp_export(struct nfsctl_export *nxp)
}
/* Look up the dentry */
err = -EINVAL;
dentry = lookup_dentry(nxp->ex_path, NULL, 0);
if (IS_ERR(dentry)) {
err = -EINVAL;
if (IS_ERR(dentry))
goto finish;
}
err = -ENOENT;
inode = dentry->d_inode;
if(!inode) {
err = -ENOENT;
if(!inode)
goto finish;
}
err = -EINVAL;
if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) {
/* I'm just being paranoid... */
err = -EINVAL;
goto finish;
}
/* We currently export only dirs. */
if (!S_ISDIR(inode->i_mode)) {
err = -ENOTDIR;
err = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto finish;
}
/* If this is a sub-export, must be root of FS */
if ((parent = exp_parent(clp, dev)) != NULL) {
......@@ -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;
}
dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
strcpy(exp->ex_path, nxp->ex_path);
......
......@@ -17,11 +17,11 @@
#include <linux/fcntl.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/nfs.h>
#include <linux/version.h>
#include <linux/unistd.h>
#include <linux/malloc.h>
#include <linux/nfs.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h>
......@@ -38,6 +38,7 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
extern void nfsd_fh_init(void);
extern long sys_call_table[];
static int nfsctl_svc(struct nfsctl_svc *data);
......@@ -64,6 +65,7 @@ nfsd_init(void)
nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */
nfsd_racache_init(); /* Readahead param cache */
nfsd_fh_init(); /* FH table */
initialized = 1;
}
......@@ -141,31 +143,33 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
union nfsctl_res * res = NULL;
int err;
MOD_INC_USE_COUNT;
lock_kernel ();
if (!initialized)
nfsd_init();
err = -EPERM;
if (!suser()) {
err = -EPERM;
goto done;
}
err = -EFAULT;
if (!access_ok(VERIFY_READ, argp, sizeof(*argp))
|| (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp)))) {
err = -EFAULT;
goto done;
}
err = -ENOMEM; /* ??? */
if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
(resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
err = -ENOMEM; /* ??? */
goto done;
}
err = -EINVAL;
copy_from_user(arg, argp, sizeof(*argp));
if (arg->ca_version != NFSCTL_VERSION) {
printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
err = -EINVAL;
goto done;
}
MOD_INC_USE_COUNT;
switch(cmd) {
case NFSCTL_SVC:
err = nfsctl_svc(&arg->ca_svc);
......@@ -193,7 +197,6 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
default:
err = -EINVAL;
}
MOD_DEC_USE_COUNT;
if (!err && resp)
copy_to_user(resp, res, sizeof(*resp));
......@@ -205,6 +208,7 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
kfree(res);
unlock_kernel ();
MOD_DEC_USE_COUNT;
return err;
}
......@@ -224,7 +228,6 @@ int
init_module(void)
{
printk("Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
nfsd_init();
do_nfsservctl = handle_sys_nfsservctl;
return 0;
}
......@@ -242,6 +245,7 @@ cleanup_module(void)
do_nfsservctl = NULL;
nfsd_export_shutdown();
nfsd_cache_shutdown();
nfsd_fh_free();
#ifdef CONFIG_PROC_FS
nfsd_stat_shutdown();
#endif
......
......@@ -7,18 +7,727 @@
*/
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/fs.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/dcache.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#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.
*
* Note that the filehandle dentry may need to be freed even after
* an error return.
*/
u32
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 inode *inode;
struct knfs_fh *fh = &fhp->fh_handle;
u32 error = 0;
if(fhp->fh_dverified)
return 0;
goto out;
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);
if (!exp) {
/* nfsdstats.fhstale++; */
return nfserr_stale; /* export entry revoked */
}
if (!exp) /* export entry revoked */
goto out;
/* Check if the request originated from a secure port. */
error = nfserr_perm;
if (!rqstp->rq_secure && EX_SECURE(exp)) {
printk(KERN_WARNING
"nfsd: request from insecure port (%08lx:%d)!\n",
ntohl(rqstp->rq_addr.sin_addr.s_addr),
ntohs(rqstp->rq_addr.sin_port));
return nfserr_perm;
goto out;
}
/* Set user creds if we haven't done so already. */
nfsd_setuser(rqstp, exp);
dentry = fh->fh_dentry;
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
*/
/* nfsdstats.fhstale++; */
return nfserr_stale;
}
dget(dentry);
fhp->fh_dverified = 1;
/*
* Look up the dentry using the NFS fh.
*/
error = nfserr_stale;
dentry = find_fh_dentry(fh);
if (!dentry)
goto out;
/*
* Note: it's possible that the returned dentry won't be the
* 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_dverified = 1;
nfsd_nr_verified++;
/* Type check. The correct error return for type mismatches
* 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)
* spec says this is incorrect (implementation notes for the
* write call).
*/
if (type > 0 && (inode->i_mode & S_IFMT) != type)
return (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
if (type < 0 && (inode->i_mode & S_IFMT) == -type)
return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir;
inode = dentry->d_inode;
if (type > 0 && (inode->i_mode & S_IFMT) != type) {
error = (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. */
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
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",
exp->ex_dev, exp->ex_ino, dentry);
fh_init(fhp); /* initialize empty fh */
fhp->fh_handle.fh_dentry = dentry;
fhp->fh_handle.fh_dparent = dentry->d_parent;
fhp->fh_handle.fh_dhash = dentry->d_name.hash;
fhp->fh_handle.fh_dlen = dentry->d_name.len;
/*
* N.B. We shouldn't need to init the fh -- the call to fh_compose
* may not be done on error paths, but the cleanup must call fh_put.
* Fix this soon!
*/
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_xino = exp->ex_ino;
fhp->fh_dentry = dentry; /* our internal copy */
fhp->fh_export = exp;
/* We stuck it there, we know it's good. */
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,
/*
* 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
*/
static int
......@@ -88,10 +90,8 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
nfserr = nfsd_lookup(rqstp, &argp->fh,
argp->name,
argp->len,
&resp->fh);
nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
&resp->fh);
fh_put(&argp->fh);
RETURN(nfserr);
......@@ -209,11 +209,10 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
if (nfserr)
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. */
nfserr = nfsd_permission(dirfhp->fh_export,
dirfhp->fh_handle.fh_dentry,
nfserr = nfsd_permission(dirfhp->fh_export, dirfhp->fh_dentry,
MAY_WRITE);
if (nfserr == nfserr_rofs) {
rdonly = 1; /* Non-fatal error for echo > /dev/null */
......@@ -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);
if (newfhp->fh_dverified)
inode = newfhp->fh_handle.fh_dentry->d_inode;
inode = newfhp->fh_dentry->d_inode;
/* Get rid of this soon... */
if (exists && !inode) {
......
......@@ -363,7 +363,7 @@ int
nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
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 xdr_ressize_check(rqstp, p);
}
......@@ -373,7 +373,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
struct nfsd_diropres *resp)
{
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 xdr_ressize_check(rqstp, p);
}
......@@ -391,7 +391,7 @@ int
nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
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;
*p++ = htonl(resp->count);
p += XDR_QUADLEN(resp->count);
......
......@@ -36,6 +36,8 @@
#include <asm/uaccess.h>
#endif
extern void fh_update(struct svc_fh*);
#define NFSDDBG_FACILITY NFSDDBG_FILEOP
/* Open mode for nfsd_open */
......@@ -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);
/* Obtain dentry and export. */
if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
return err;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP);
if (err)
goto out;
dparent = fhp->fh_handle.fh_dentry;
dparent = fhp->fh_dentry;
exp = fhp->fh_export;
/* Fast path... */
......@@ -121,11 +124,16 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
!nfsd_iscovered(dparent, exp)) {
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);
if (IS_ERR(dchild))
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);
return (dchild->d_inode ? 0 : nfserr_noent);
}
......@@ -135,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
return nfserr_noent;
if (nfsd_iscovered(dparent, exp))
return nfserr_acces;
out:
return err;
}
......@@ -158,10 +167,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
ftype = S_IFREG;
/* Get inode */
if ((err = fh_verify(rqstp, fhp, ftype, accmode)) != 0)
return err;
err = fh_verify(rqstp, fhp, ftype, accmode);
if (err)
goto out;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
/* The size case is special... */
......@@ -169,7 +179,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (iap->ia_size < inode->i_size) {
err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
if (err != 0)
return err;
goto out;
}
if ((err = get_write_access(inode)) != 0)
return nfserrno(-err);
......@@ -211,8 +221,9 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(inode);
}
return 0;
err = 0;
out:
return err;
}
/*
......@@ -230,20 +241,23 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
access = wflag? MAY_WRITE : MAY_READ;
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;
/* Disallow access to files with the append-only bit set or
* with mandatory locking enabled */
err = nfserr_perm;
if (IS_APPEND(inode) || IS_ISMNDLK(inode))
return nfserr_perm;
goto out;
if (!inode->i_op || !inode->i_op->default_file_ops)
return nfserr_perm;
goto out;
if (wflag && (err = get_write_access(inode)) != 0)
return nfserrno(-err);
if (wflag && (err = get_write_access(inode)) != 0) {
err = nfserrno(-err);
goto out;
}
memset(filp, 0, sizeof(*filp));
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,
filp->f_mode = wflag? FMODE_WRITE : FMODE_READ;
filp->f_dentry = dentry;
err = 0;
if (filp->f_op->open) {
err = filp->f_op->open(inode, filp);
if (err) {
......@@ -262,11 +277,11 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
* is really on callers stack frame. -DaveM
*/
filp->f_count--;
return nfserrno(-err);
err = nfserrno(-err);
}
}
return 0;
out:
return err;
}
/*
......@@ -281,7 +296,8 @@ nfsd_close(struct file *filp)
if (!inode->i_count)
printk(KERN_WARNING "nfsd: inode count == 0!\n");
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)
filp->f_op->release(inode, filp);
if (filp->f_mode & FMODE_WRITE)
......@@ -299,6 +315,9 @@ nfsd_sync(struct inode *inode, struct file *filp)
/*
* 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 *
nfsd_get_raparms(struct dentry *dentry)
......@@ -506,39 +525,53 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct inode *dirp;
int err;
err = nfserr_perm;
if (!flen)
return nfserr_perm;
goto out;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
return err;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
if (err)
goto out;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
/* Get all the sanity checks out of the way before we lock the parent. */
if(!dirp->i_op || !dirp->i_op->lookup) {
return nfserrno(-ENOTDIR);
} else if(type == S_IFREG) {
err = nfserr_notdir;
if(!dirp->i_op || !dirp->i_op->lookup)
goto out;
err = nfserr_perm;
if (type == S_IFREG) {
if(!dirp->i_op->create)
return nfserr_perm;
goto out;
} else if(type == S_IFDIR) {
if(!dirp->i_op->mkdir)
return nfserr_perm;
goto out;
} else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
if(!dirp->i_op->mknod)
return nfserr_perm;
goto out;
} 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);
err = PTR_ERR(dchild);
if(IS_ERR(dchild))
return nfserrno(-err);
} 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. */
fh_lock(fhp);
......@@ -562,9 +595,15 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (EX_ISSYNC(fhp->fh_export))
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);
} else {
fh_update(resfhp);
}
/* Set file attributes. Mode has already been set and
* setting uid/gid works only for root. Irix appears to
......@@ -574,7 +613,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = 0;
if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
err = nfsd_setattr(rqstp, resfhp, iap);
out:
return err;
}
......@@ -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)
return err;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
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)
if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
return err;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
if (!inode->i_op || !inode->i_op->readlink)
......@@ -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)
return err;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
if (nfsd_iscovered(dentry, fhp->fh_export) ||
......@@ -720,7 +759,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
(err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
return err;
ddir = ffhp->fh_handle.fh_dentry;
ddir = ffhp->fh_dentry;
dirp = ddir->d_inode;
dnew = lookup_dentry(fname, dget(ddir), 1);
......@@ -731,7 +770,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = -EEXIST;
if (dnew->d_inode)
goto dput_and_out;
dest = tfhp->fh_handle.fh_dentry->d_inode;
dest = tfhp->fh_dentry->d_inode;
err = -EPERM;
if (!len)
......@@ -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)
return err;
fdentry = ffhp->fh_handle.fh_dentry;
fdentry = ffhp->fh_dentry;
fdir = fdentry->d_inode;
tdentry = tfhp->fh_handle.fh_dentry;
tdentry = tfhp->fh_dentry;
tdir = tdentry->d_inode;
if (!flen || (fname[0] == '.' &&
......@@ -816,6 +855,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (IS_ERR(ndentry))
goto out_dput_old;
/* N.B. check this ... problems in locking?? */
nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
err = -EXDEV;
if (fdir->i_dev != tdir->i_dev)
......@@ -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)
return err;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
rdentry = lookup_dentry(fname, dget(dentry), 0);
......@@ -975,20 +1015,24 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
unsigned long oldfs;
int err;
if ((err = fh_verify(rqstp, fhp, 0, MAY_NOP)) != 0)
return err;
dentry = fhp->fh_handle.fh_dentry;
err = fh_verify(rqstp, fhp, 0, MAY_NOP);
if (err)
goto out;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
err = nfserr_io;
if (!(sb = inode->i_sb) || !sb->s_op->statfs)
return nfserr_io;
goto out;
oldfs = get_fs();
set_fs (KERNEL_DS);
sb->s_op->statfs(sb, stat, sizeof(*stat));
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
zero_fd_set(n, &fds->res_out);
zero_fd_set(n, &fds->res_ex);
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;
if (error < 0)
goto out;
......
......@@ -14,7 +14,7 @@ static inline void clear_active_bhs(unsigned long x)
unsigned long temp;
__asm__ __volatile__(
"1: ldq_l %0,%1\n"
" and %0,%2,%0\n"
" bic %0,%2,%0\n"
" stq_c %0,%1\n"
" beq %0,2f\n"
".section .text2,\"ax\"\n"
......
......@@ -27,12 +27,12 @@
* ino/dev of the exported inode.
*/
struct nfs_fhbase {
struct dentry *fb_dentry;
struct dentry *fb_dparent;
unsigned int fb_dhash;
unsigned int fb_dlen;
ino_t fb_xino;
struct dentry * fb_dentry; /* dentry cookie */
ino_t fb_ino; /* our inode number */
ino_t fb_dirino; /* dir inode number */
dev_t fb_dev; /* our device */
dev_t fb_xdev;
ino_t fb_xino;
};
#define NFS_FH_PADDING (NFS_FHSIZE - sizeof(struct nfs_fhbase))
......@@ -41,13 +41,12 @@ struct knfs_fh {
__u8 fh_cookie[NFS_FH_PADDING];
};
#define fh_dentry fh_base.fb_dentry
#define fh_dparent fh_base.fb_dparent
#define fh_dhash fh_base.fb_dhash
#define fh_dlen fh_base.fb_dlen
#define fh_xino fh_base.fb_xino
#define fh_dcookie fh_base.fb_dentry
#define fh_ino fh_base.fb_ino
#define fh_dirino fh_base.fb_dirino
#define fh_dev fh_base.fb_dev
#define fh_xdev fh_base.fb_xdev
#define fh_xino fh_base.fb_xino
#ifdef __KERNEL__
......@@ -57,6 +56,7 @@ struct knfs_fh {
*/
typedef struct svc_fh {
struct knfs_fh fh_handle; /* FH data */
struct dentry * fh_dentry; /* validated dentry */
struct svc_export * fh_export; /* export pointer */
size_t fh_pre_size; /* size before operation */
time_t fh_pre_mtime; /* mtime before oper */
......@@ -69,17 +69,26 @@ typedef struct svc_fh {
/*
* Shorthand for dprintk()'s
*/
#define SVCFH_DENTRY(f) ((f)->fh_handle.fh_dentry)
#define SVCFH_DENTRY(f) ((f)->fh_dentry)
/*
* Function prototypes
*/
u32 fh_verify(struct svc_rqst *, struct svc_fh *, int, int);
void fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
u32 fh_verify(struct svc_rqst *, struct svc_fh *, int, int);
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 *
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;
return dst;
}
......@@ -97,7 +106,7 @@ fh_init(struct svc_fh *fhp)
static inline void
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",
......@@ -117,7 +126,7 @@ fh_lock(struct svc_fh *fhp)
static inline void
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_post_version)
......@@ -130,17 +139,7 @@ fh_unlock(struct svc_fh *fhp)
/*
* Release an inode
*/
#ifndef NFSD_DEBUG
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
#if 0
#define fh_put(fhp) __fh_put(fhp, __FILE__, __LINE__)
static inline void
......@@ -151,7 +150,7 @@ __fh_put(struct svc_fh *fhp, char *file, int line)
if (!fhp->fh_dverified)
return;
dentry = fhp->fh_handle.fh_dentry;
dentry = fhp->fh_dentry;
if (!dentry->d_count) {
printk("nfsd: trying to free free dentry in %s:%d\n"
" file %s/%s\n",
......@@ -159,14 +158,12 @@ __fh_put(struct svc_fh *fhp, char *file, int line)
dentry->d_parent->d_name.name, dentry->d_name.name);
} else {
fh_unlock(fhp);
dput(dentry);
fhp->fh_dverified = 0;
dput(dentry);
}
}
#endif
#endif /* __KERNEL__ */
#endif /* NFSD_FH_H */
......@@ -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);
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