diff -u --recursive --new-file v2.1.31/linux/CREDITS linux/CREDITS
--- v2.1.31/linux/CREDITS	Thu Mar 27 14:39:58 1997
+++ linux/CREDITS	Sat Apr  5 12:14:20 1997
@@ -632,6 +632,13 @@
 S: D-69126 Heidelberg
 S: Germany
 
+N: Christopher Horn
+E: chorn@warwick.net
+D: Miscellaneous sysctl hacks
+S: 36 Mudtown Road
+S: Wantage, NJ 07461
+S: USA
+
 N: Miguel de Icaza Amozurrutia
 E: miguel@nuclecu.unam.mx
 D: Linux/SPARC team, Midnight Commander maintainer
diff -u --recursive --new-file v2.1.31/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.31/linux/Documentation/Changes	Fri Apr  4 08:52:16 1997
+++ linux/Documentation/Changes	Fri Apr  4 09:34:31 1997
@@ -47,6 +47,7 @@
 - Kbd                    0.91
 - Loadlin                1.6a
 - Sh-utils               1.16
+- Ncpfs                  2.1.1
 
 Upgrade notes
 *************
diff -u --recursive --new-file v2.1.31/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.31/linux/Documentation/Configure.help	Fri Apr  4 08:52:16 1997
+++ linux/Documentation/Configure.help	Sat Apr  5 12:14:20 1997
@@ -703,6 +703,18 @@
   or with the program info ("man info"). Saying Y here enlarges
   your kernel by about 7kB. Just say Y.
 
+Sysctl support
+CONFIG_SYSCTL
+  The sysctl interface provides a means of dynamically changing certain
+  kernel parameters and variables on the fly without requiring a
+  recompile of the kernel or reboot of the system. The primary interface
+  consists of a system call, but if the /proc filesystem is enabled, a
+  tree of modifiable sysctl entries will be generated beneath the 
+  /proc/sys directory. Note that enabling this option will enlarge the 
+  kernel by at least 8kB. As it is generally a good thing, you probably
+  want to say Y here unless building a kernel for install/rescue disks
+  or your system is very limited in memory.
+
 Kernel support for ELF binaries
 CONFIG_BINFMT_ELF
   ELF (Executable and Linkable Format) is a format for libraries and
diff -u --recursive --new-file v2.1.31/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v2.1.31/linux/Documentation/ioctl-number.txt	Sun Feb  2 05:18:29 1997
+++ linux/Documentation/ioctl-number.txt	Thu Apr  3 15:00:40 1997
@@ -1,5 +1,5 @@
 Ioctl Numbers
-16 Dec 1996
+12 Feb 1997
 Michael Chastain
 <mec@shout.net>
 
@@ -108,3 +108,5 @@
 0x90	00	linux/sbpcd.h
 0x99	00-0F	537-Addinboard driver	in development, e-mail contact:
 					b.kohl@ipn-b.comlink.apc.org
+0xA0	all	Small Device Project	in development, e-mail contact:
+					khollis@northwest.com
diff -u --recursive --new-file v2.1.31/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.31/linux/MAINTAINERS	Fri Apr  4 08:52:16 1997
+++ linux/MAINTAINERS	Fri Apr  4 09:34:31 1997
@@ -249,7 +249,7 @@
 
 NCP FILESYSTEM:
 P:	Volker Lendecke
-M:	lendecke@namu01.Num.Math.Uni-Goettingen.de
+M:	lendecke@Math.Uni-Goettingen.de
 L:	linware@sh.cvut.cz
 S:	Maintained
 
@@ -301,9 +301,9 @@
 
 SMB FILESYSTEM:
 P:	Volker Lendecke
-M:	lendecke@namu01.Num.Math.Uni-Goettingen.de
+M:	lendecke@Math.Uni-Goettingen.de
 L:	samba@listproc.anu.edu.au
-S:	Odd Fixes
+S:	Maintained
 
 SMP: (except SPARC)
 P:	Alan Cox
diff -u --recursive --new-file v2.1.31/linux/Makefile linux/Makefile
--- v2.1.31/linux/Makefile	Fri Apr  4 08:52:16 1997
+++ linux/Makefile	Fri Apr  4 11:56:59 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 31
+SUBLEVEL = 32
 
 ARCH = i386
 
@@ -114,8 +114,9 @@
 # Include the make variables (CC, etc...)
 #
 
-ARCHIVES	=kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o net/network.a
+CORE_FILES	=kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o
 FILESYSTEMS	=fs/filesystems.a
+NETWORKS	=net/network.a
 DRIVERS		=drivers/block/block.a \
 		 drivers/char/char.a
 LIBS		=$(TOPDIR)/lib/lib.a
@@ -173,8 +174,9 @@
 
 vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
 	$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
-		$(ARCHIVES) \
+		$(CORE_FILES) \
 		$(FILESYSTEMS) \
+		$(NETWORKS) \
 		$(DRIVERS) \
 		$(LIBS) \
 		-o vmlinux
diff -u --recursive --new-file v2.1.31/linux/arch/alpha/Makefile linux/arch/alpha/Makefile
--- v2.1.31/linux/arch/alpha/Makefile	Thu Feb  6 04:42:34 1997
+++ linux/arch/alpha/Makefile	Fri Apr  4 12:02:16 1997
@@ -30,7 +30,7 @@
 
 SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib \
 	arch/alpha/math-emu
-ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES)
+CORE_FILES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(CORE_FILES)
 LIBS := $(TOPDIR)/arch/alpha/math-emu/math-emu.a \
 	$(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
 
diff -u --recursive --new-file v2.1.31/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.31/linux/arch/alpha/config.in	Fri Apr  4 08:52:16 1997
+++ linux/arch/alpha/config.in	Sat Apr  5 12:14:20 1997
@@ -91,8 +91,10 @@
 fi
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
 endmenu
 
 source drivers/block/Config.in
diff -u --recursive --new-file v2.1.31/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.1.31/linux/arch/alpha/defconfig	Fri Apr  4 08:52:16 1997
+++ linux/arch/alpha/defconfig	Sat Apr  5 12:14:20 1997
@@ -39,6 +39,7 @@
 CONFIG_TGA_CONSOLE=y
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+CONFIG_SYSCTL=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 
diff -u --recursive --new-file v2.1.31/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.31/linux/arch/alpha/kernel/entry.S	Thu Mar 27 14:39:59 1997
+++ linux/arch/alpha/kernel/entry.S	Thu Apr  3 09:59:57 1997
@@ -22,7 +22,7 @@
 /*
  * stack offsets
  */
-#define SP_OFF		160
+#define SP_OFF		184
 
 #define SWITCH_STACK_SIZE 320
 
@@ -47,9 +47,11 @@
  * regs 9-15 preserved by C code
  * regs 16-18 saved by PAL-code
  * regs 29-30 saved and set up by PAL-code
+ * JRP - Save regs 16-18 in a special area of the stack, so that
+ * the palcode-provided values are available to the signal handler.
  */
 #define SAVE_ALL			\
-	subq	$30,160,$30;		\
+	subq	$30,184,$30;		\
 	stq	$0,0($30);		\
 	stq	$1,8($30);		\
 	stq	$2,16($30);		\
@@ -71,7 +73,10 @@
 	stq	$26,128($30);		\
 	stq	$27,136($30);		\
 	stq	$28,144($30);		\
-	stq	$2,152($30)
+	stq	$2,152($30);		\
+	stq	$16,160($30);		\
+	stq	$17,168($30);		\
+	stq	$18,176($30)
 
 #define RESTORE_ALL			\
 	lda	$19,hae;		\
@@ -108,7 +113,7 @@
 	ldq	$26,128($30);		\
 	ldq	$27,136($30);		\
 	ldq	$28,144($30);		\
-	addq	$30,160,$30
+	addq	$30,184,$30
 
 .text
 .set noat
diff -u --recursive --new-file v2.1.31/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c
--- v2.1.31/linux/arch/alpha/kernel/signal.c	Sun Jan 26 04:26:39 1997
+++ linux/arch/alpha/kernel/signal.c	Thu Apr  3 10:01:31 1997
@@ -218,6 +218,9 @@
 	put_user(regs->gp , sc->sc_regs+29);
 	for (i = 0; i < 31; i++)
 		put_user(sw->fp[i], sc->sc_fpregs+i);
+	put_user(regs->trap_a0, &sc->sc_traparg_a0);
+	put_user(regs->trap_a1, &sc->sc_traparg_a1);
+	put_user(regs->trap_a2, &sc->sc_traparg_a2);
 
 	/*
 	 * The following is:
diff -u --recursive --new-file v2.1.31/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v2.1.31/linux/arch/i386/Makefile	Sun Jan 26 02:07:04 1997
+++ linux/arch/i386/Makefile	Fri Apr  4 12:01:53 1997
@@ -70,7 +70,7 @@
 HEAD := arch/i386/kernel/head.o
 
 SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib
-ARCHIVES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(ARCHIVES)
+CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES)
 LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a
 
 ifdef CONFIG_MATH_EMULATION
diff -u --recursive --new-file v2.1.31/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.31/linux/arch/i386/config.in	Wed Jan 15 18:46:06 1997
+++ linux/arch/i386/config.in	Sat Apr  5 12:14:20 1997
@@ -31,6 +31,7 @@
 fi
 bool 'MCA support' CONFIG_MCA
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --recursive --new-file v2.1.31/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.31/linux/arch/i386/defconfig	Fri Apr  4 08:52:16 1997
+++ linux/arch/i386/defconfig	Sat Apr  5 12:14:20 1997
@@ -22,6 +22,7 @@
 CONFIG_PCI=y
 # CONFIG_MCA is not set
 CONFIG_SYSVIPC=y
+CONFIG_SYSCTL=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 # CONFIG_M386 is not set
@@ -191,6 +192,8 @@
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
 # CONFIG_ROOT_NFS is not set
 # CONFIG_SMB_FS is not set
 CONFIG_ISO9660_FS=y
diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.31/linux/arch/i386/kernel/irq.c	Fri Apr  4 08:52:17 1997
+++ linux/arch/i386/kernel/irq.c	Fri Apr  4 10:52:19 1997
@@ -40,6 +40,8 @@
 static unsigned char cache_21 = 0xff;
 static unsigned char cache_A1 = 0xff;
 
+unsigned int local_irq_count[NR_CPUS];
+
 #ifdef __SMP_PROF__
 static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},};
 #endif
@@ -329,7 +331,6 @@
 unsigned char global_irq_holder = NO_PROC_ID;
 unsigned volatile int global_irq_lock;
 unsigned volatile int global_irq_count;
-unsigned int local_irq_count[NR_CPUS];
 
 #define irq_active(cpu) \
 	(global_irq_count != local_irq_count[cpu])
@@ -353,11 +354,11 @@
 static unsigned long previous_irqholder;
 
 #undef INIT_STUCK
-#define INIT_STUCK 10000000
+#define INIT_STUCK 100000000
 
 #undef STUCK
 #define STUCK \
-if (!--stuck) {printk("wait_on_irq stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", where, previous_irqholder, local_count, global_irq_count); stuck = INIT_STUCK;}
+if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, global_irq_count); stuck = INIT_STUCK; }
 
 static inline void wait_on_irq(int cpu, unsigned long where)
 {
@@ -393,6 +394,28 @@
 	}
 }
 
+/*
+ * This is called when we want to synchronize with
+ * interrupts. We may for example tell a device to
+ * stop sending interrupts: but to make sure there
+ * are no interrupts that are executing on another
+ * CPU we need to call this function.
+ *
+ * On UP this is a no-op.
+ */
+void synchronize_irq(void)
+{
+	int cpu = smp_processor_id();
+	int local_count = local_irq_count[cpu];
+
+	/* Do we need to wait? */
+	if (local_count != global_irq_count) {
+		/* The stupid way to do this */
+		cli();
+		sti();
+	}
+}
+
 #undef INIT_STUCK
 #define INIT_STUCK 10000000
 
@@ -436,7 +459,7 @@
 	int cpu = smp_processor_id();
 	unsigned long where;
 
-	__asm__("movl 12(%%esp),%0":"=r" (where));
+	__asm__("movl 16(%%esp),%0":"=r" (where));
 	__cli();
 	get_irqlock(cpu, where);
 }
diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.31/linux/arch/i386/kernel/smp.c	Thu Mar 27 14:39:59 1997
+++ linux/arch/i386/kernel/smp.c	Fri Apr  4 12:37:26 1997
@@ -1232,8 +1232,10 @@
 void smp_flush_tlb(void)
 {
 	unsigned long flags;
-	if(smp_activated && smp_processor_id()!=active_kernel_processor)
-		panic("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor);
+	if(smp_activated && smp_processor_id()!=active_kernel_processor) {
+		printk("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor);
+		*(char *)0=0;
+	}
 /*	printk("SMI-");*/
 
 	/*
diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.1.31/linux/arch/i386/kernel/time.c	Fri Apr  4 08:52:17 1997
+++ linux/arch/i386/kernel/time.c	Thu Apr  3 10:13:39 1997
@@ -454,7 +454,8 @@
 	  )*60 + sec; /* finally seconds */
 }
 
-static unsigned long get_cmos_time(void)
+/* not static: needed by APM */
+unsigned long get_cmos_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
 	int i;
diff -u --recursive --new-file v2.1.31/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.31/linux/arch/i386/kernel/traps.c	Thu Mar 27 14:39:59 1997
+++ linux/arch/i386/kernel/traps.c	Thu Apr  3 10:35:53 1997
@@ -114,7 +114,7 @@
 #define VMALLOC_OFFSET (8*1024*1024)
 #define MODULE_RANGE (8*1024*1024)
 
-/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err)
+static void show_regs(struct pt_regs *regs)
 {
 	int i;
 	unsigned long esp;
@@ -130,8 +130,6 @@
 		esp = regs->esp;
 		ss = regs->xss & 0xffff;
 	}
-	console_verbose();
-	printk("%s: %04lx\n", str, err & 0xffff);
 	printk("CPU:    %d\n", smp_processor_id());
 	printk("EIP:    %04x:[<%08lx>]\nEFLAGS: %08lx\n", 0xffff & regs->xcs,regs->eip,regs->eflags);
 	printk("eax: %08lx   ebx: %08lx   ecx: %08lx   edx: %08lx\n",
@@ -182,6 +180,13 @@
 	for(i=0;i<20;i++)
 		printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip)));
 	printk("\n");
+}	
+
+/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err)
+{
+	console_verbose();
+	printk("%s: %04lx\n", str, err & 0xffff);
+	show_regs(regs);
 	do_exit(SIGSEGV);
 }
 
@@ -231,6 +236,7 @@
 
 asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
 {
+	printk("NMI\n"); show_regs(regs);
 #ifdef CONFIG_SMP_NMI_INVAL
 	smp_flush_tlb_rcv();
 #else
diff -u --recursive --new-file v2.1.31/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.31/linux/arch/m68k/config.in	Fri Apr  4 08:52:17 1997
+++ linux/arch/m68k/config.in	Sat Apr  5 12:14:20 1997
@@ -49,6 +49,7 @@
 
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_AMIGA" = "y" ]; then
diff -u --recursive --new-file v2.1.31/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.1.31/linux/arch/m68k/defconfig	Fri Apr  4 08:52:17 1997
+++ linux/arch/m68k/defconfig	Sat Apr  5 12:14:20 1997
@@ -38,6 +38,7 @@
 #
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+CONFIG_SYSCTL=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_ZORRO=y
diff -u --recursive --new-file v2.1.31/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.1.31/linux/arch/mips/config.in	Sat May  4 23:05:58 1996
+++ linux/arch/mips/config.in	Sat Apr  5 12:14:20 1997
@@ -50,6 +50,7 @@
 #  fi
 #fi
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 endmenu
 
 mainmenu_option next_comment
diff -u --recursive --new-file v2.1.31/linux/arch/mips/defconfig linux/arch/mips/defconfig
--- v2.1.31/linux/arch/mips/defconfig	Fri Apr  4 08:52:17 1997
+++ linux/arch/mips/defconfig	Sat Apr  5 12:14:20 1997
@@ -26,6 +26,7 @@
 CONFIG_CPU_LITTLE_ENDIAN=y
 # CONFIG_NET is not set
 # CONFIG_SYSVIPC is not set
+CONFIG_SYSCTL=y
 
 #
 # Loadable module support
diff -u --recursive --new-file v2.1.31/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.31/linux/arch/ppc/config.in	Wed Jan 15 18:46:06 1997
+++ linux/arch/ppc/config.in	Sat Apr  5 12:14:20 1997
@@ -30,6 +30,7 @@
   fi
 fi
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
diff -u --recursive --new-file v2.1.31/linux/arch/sparc/Makefile linux/arch/sparc/Makefile
--- v2.1.31/linux/arch/sparc/Makefile	Mon Mar 17 14:54:20 1997
+++ linux/arch/sparc/Makefile	Fri Apr  4 12:02:44 1997
@@ -26,14 +26,14 @@
 SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm \
 	arch/sparc/prom
 
-ARCHIVES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(ARCHIVES)
+CORE_FILES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(CORE_FILES)
 
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/prom/promlib.a \
 	$(TOPDIR)/arch/sparc/lib/lib.a
 
 ifdef CONFIG_AP1000
 SUBDIRS := $(SUBDIRS) arch/sparc/ap1000 mpp
-ARCHIVES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o $(TOPDIR)/mpp/mpplib.o $(ARCHIVES) 
+CORE_FILES := $(TOPDIR)/arch/sparc/ap1000/ap1000lib.o $(TOPDIR)/mpp/mpplib.o $(CORE_FILES) 
 DRIVERS := $(DRIVERS) drivers/ap1000/ap1000.a
 CFLAGS := $(CFLAGS) -D__MPP__=1
 endif
diff -u --recursive --new-file v2.1.31/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.31/linux/arch/sparc/config.in	Fri Apr  4 08:52:17 1997
+++ linux/arch/sparc/config.in	Sat Apr  5 12:14:20 1997
@@ -50,6 +50,7 @@
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --recursive --new-file v2.1.31/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.31/linux/arch/sparc/defconfig	Fri Apr  4 08:52:17 1997
+++ linux/arch/sparc/defconfig	Sat Apr  5 12:14:20 1997
@@ -59,6 +59,7 @@
 CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+CONFIG_SYSCTL=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_BINFMT_JAVA=m
diff -u --recursive --new-file v2.1.31/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.1.31/linux/arch/sparc64/Makefile	Thu Mar 27 14:40:00 1997
+++ linux/arch/sparc64/Makefile	Fri Apr  4 12:03:15 1997
@@ -32,7 +32,7 @@
 SUBDIRS := $(SUBDIRS) arch/sparc64/kernel arch/sparc64/lib arch/sparc64/mm \
 	arch/sparc64/prom
 
-ARCHIVES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(ARCHIVES)
+CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES)
 
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \
 	$(TOPDIR)/arch/sparc64/lib/lib.a
diff -u --recursive --new-file v2.1.31/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.31/linux/arch/sparc64/config.in	Fri Apr  4 08:52:17 1997
+++ linux/arch/sparc64/config.in	Sat Apr  5 12:14:20 1997
@@ -49,6 +49,7 @@
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+bool 'Sysctl support' CONFIG_SYSCTL
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --recursive --new-file v2.1.31/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.31/linux/arch/sparc64/defconfig	Fri Apr  4 08:52:17 1997
+++ linux/arch/sparc64/defconfig	Sat Apr  5 12:14:20 1997
@@ -59,6 +59,7 @@
 CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+CONFIG_SYSCTL=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_BINFMT_JAVA=m
diff -u --recursive --new-file v2.1.31/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.1.31/linux/drivers/block/genhd.c	Fri Apr  4 08:52:17 1997
+++ linux/drivers/block/genhd.c	Thu Apr  3 10:23:56 1997
@@ -24,9 +24,7 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/string.h>
-#ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/blk.h>
-#endif
 
 #include <asm/system.h>
 
@@ -109,6 +107,52 @@
 		SYS_IND(p) == LINUX_EXTENDED_PARTITION);
 }
 
+static unsigned int get_ptable_blocksize(kdev_t dev)
+{
+  int ret = 1024;
+
+  /*
+   * See whether the low-level driver has given us a minumum blocksize.
+   * If so, check to see whether it is larger than the default of 1024.
+   */
+  if (!blksize_size[MAJOR(dev)])
+    {
+      return ret;
+    }
+
+  /*
+   * Check for certain special power of two sizes that we allow.
+   * With anything larger than 1024, we must force the blocksize up to
+   * the natural blocksize for the device so that we don't have to try
+   * and read partial sectors.  Anything smaller should be just fine.
+   */
+  switch( blksize_size[MAJOR(dev)][MINOR(dev)] )
+    {
+    case 2048:
+      ret = 2048;
+      break;
+    case 4096:
+      ret = 4096;
+      break;
+    case 8192:
+      ret = 8192;
+      break;
+    case 1024:
+    case 512:
+    case 256:
+    case 0:
+      /*
+       * These are all OK.
+       */
+      break;
+    default:
+      panic("Strange blocksize for partition table\n");
+    }
+
+  return ret;
+
+}
+
 #ifdef CONFIG_MSDOS_PARTITION
 /*
  * Create devices for each logical partition in an extended partition.
@@ -138,7 +182,7 @@
 	while (1) {
 		if ((current_minor & mask) == 0)
 			return;
-		if (!(bh = bread(dev,0,1024)))
+		if (!(bh = bread(dev,0,get_ptable_blocksize(dev))))
 			return;
 	  /*
 	   * This block is from a device that we're about to stomp on.
@@ -222,7 +266,7 @@
 	struct bsd_partition *p;
 	int mask = (1 << hd->minor_shift) - 1;
 
-	if (!(bh = bread(dev,0,1024)))
+	if (!(bh = bread(dev,0,get_ptable_blocksize(dev))))
 		return;
 	bh->b_state = 0;
 	l = (struct bsd_disklabel *) (bh->b_data+512);
@@ -259,7 +303,7 @@
 
 read_mbr:
 #endif
-	if (!(bh = bread(dev,0,1024))) {
+	if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
 		printk(" unable to read partition table\n");
 		return -1;
 	}
@@ -438,7 +482,7 @@
 	struct d_partition * partition;
 #define DISKLABELMAGIC (0x82564557UL)
 
-	if (!(bh = bread(dev,0,1024))) {
+	if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
 		printk("unable to read partition table\n");
 		return -1;
 	}
@@ -501,7 +545,7 @@
 	unsigned long spc;
 #define SUN_LABEL_MAGIC          0xDABE
 
-	if(!(bh = bread(dev, 0, 1024))) {
+	if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) {
 		printk("Dev %s: unable to read partition table\n",
 		       kdevname(dev));
 		return -1;
diff -u --recursive --new-file v2.1.31/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.31/linux/drivers/block/ide-cd.h	Wed Dec 11 06:45:54 1996
+++ linux/drivers/block/ide-cd.h	Fri Apr  4 08:49:31 1997
@@ -15,15 +15,6 @@
 #endif
 
 
-/* Turn this on to have the driver print out the meanings of the
-   ATAPI error codes.  This will use up additional kernel-space
-   memory, though. */
-
-#ifndef VERBOSE_IDE_CD_ERRORS
-#define VERBOSE_IDE_CD_ERRORS 0
-#endif
-
-
 /* Turning this on will remove code to work around various nonstandard
    ATAPI implementations.  If you know your drive follows the standard,
    this will give you a slightly smaller kernel. */
diff -u --recursive --new-file v2.1.31/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.31/linux/drivers/char/lp.c	Fri Apr  4 08:52:19 1997
+++ linux/drivers/char/lp.c	Thu Apr  3 19:15:44 1997
@@ -117,6 +117,8 @@
 	struct lp_stats *stats;
 
 	do {
+	    if(need_resched)
+		schedule();
 	    if ((status = LP_S(minor)) & LP_PBUSY) {
 		if (!LP_CAREFUL_READY(minor, status))
 			return 0;
diff -u --recursive --new-file v2.1.31/linux/drivers/char/vga.c linux/drivers/char/vga.c
--- v2.1.31/linux/drivers/char/vga.c	Sun Jan 26 02:07:17 1997
+++ linux/drivers/char/vga.c	Thu Apr  3 10:55:14 1997
@@ -115,14 +115,10 @@
 void
 __set_origin(unsigned short offset)
 {
-	unsigned long flags;
-
 	clear_selection();
 
-	save_flags(flags); cli();
 	__origin = offset;
 	write_vga(12, offset);
-	restore_flags(flags);
 }
 
 /*
@@ -140,18 +136,14 @@
 void
 set_cursor(int currcons)
 {
-	unsigned long flags;
-
 	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
 		return;
 	if (__real_origin != __origin)
 		__set_origin(__real_origin);
-	save_flags(flags); cli();
 	if (deccm) {
 		write_vga(14, (pos - video_mem_base)>>1);
 	} else
 		hide_cursor();
-	restore_flags(flags);
 }
 
 unsigned long
diff -u --recursive --new-file v2.1.31/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- v2.1.31/linux/drivers/net/cs89x0.c	Fri Apr  4 08:52:20 1997
+++ linux/drivers/net/cs89x0.c	Fri Apr  4 14:02:05 1997
@@ -758,7 +758,7 @@
 		short ioaddr = dev->base_addr;
 		unsigned long flags;
 
-		if (net_debug > 3)printk("%s: sent %ld byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+		if (net_debug > 3)printk("%s: sent %d byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
 
 		/* keep the upload from being interrupted, since we
                    ask the chip to start transmitting before the
diff -u --recursive --new-file v2.1.31/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.31/linux/drivers/net/de4x5.c	Fri Apr  4 08:52:20 1997
+++ linux/drivers/net/de4x5.c	Thu Apr  3 16:37:22 1997
@@ -1359,8 +1359,9 @@
 	
     if (dev->interrupt)
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
-	
+
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+    synchronize_irq();
     dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
@@ -1389,13 +1390,11 @@
 		   dev->name, sts);
 	    return;
 	}
-if (sts & (STS_AIS | STS_UNF | STS_SE | STS_LNF | STS_RWT | STS_RU | STS_TJT))
-	printk("STS=%08x\n",sts);
     }
 
     /* Load the TX ring with any locally stored packets */
     if (!set_bit(0, (void *)&lp->cache.lock)) {
-	while (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
+	if (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
 	    de4x5_queue_pkt(de4x5_get_cache(dev), dev);
 	}
 	lp->cache.lock = 0;
diff -u --recursive --new-file v2.1.31/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.1.31/linux/drivers/net/shaper.c	Fri Apr  4 08:52:21 1997
+++ linux/drivers/net/shaper.c	Fri Apr  4 11:58:44 1997
@@ -614,7 +614,7 @@
 	if(err<0)
 		return err;
 	printk(SHAPER_BANNER);	
-	if (register_netdev(dev) != 0)
+	if (register_netdev(&dev_shape) != 0)
 		return -EIO;
 	printk("Traffic shaper initialised.\n");
 	return 0;
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c
--- v2.1.31/linux/drivers/scsi/AM53C974.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/scsi/AM53C974.c	Fri Apr  4 14:03:29 1997
@@ -117,6 +117,343 @@
 
 #define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) )
 
+
+#include <scsi/scsicam.h>
+
+/***************************************************************************************
+* Default setting of the controller's SCSI id. Edit and uncomment this only if your    *
+* BIOS does not correctly initialize the controller's SCSI id.                         *
+* If you don't get a warning during boot, it is correctly initialized.                 *
+****************************************************************************************/
+/* #define AM53C974_SCSI_ID 7 */
+
+/***************************************************************************************
+* Default settings for sync. negotiation enable, transfer rate and sync. offset.       *
+* These settings can be replaced by LILO overrides (append) with the following syntax:          *
+* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset                          *
+* Sync. negotiation is disabled by default and will be enabled for those targets which *
+* are specified in the LILO override                                                   *
+****************************************************************************************/
+#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
+#define DEFAULT_RATE			 5 /* MHz, min: 3; max: 10 */
+#define DEFAULT_SYNC_OFFSET		 0 /* bytes, min: 0; max: 15; use 0 for async. mode */
+
+/***************************************************************************************
+* If defined, don't allow targets to disconnect during commands.  This will reduce     *
+* performance, but may be worthwhile if you suspect the driver of corrupting data when *
+* a disconnect happens.                                                                *
+***************************************************************************************/
+#define AM53C974_PROHIBIT_DISCONNECT
+
+/* --------------------- don't edit below here  --------------------- */
+
+#define AM53C974_DRIVER_REVISION_MAJOR 0
+#define AM53C974_DRIVER_REVISION_MINOR 5
+#define SEPARATOR_LINE  \
+"--------------------------------------------------------------------------\n"
+
+/* debug control */
+/* #define AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_MSG */
+/* #define AM53C974_DEBUG_KEYWAIT */
+/* #define AM53C974_DEBUG_INIT */
+/* #define AM53C974_DEBUG_QUEUE */
+/* #define AM53C974_DEBUG_INFO */
+/* #define AM53C974_DEBUG_LINKED */
+/* #define VERBOSE_AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_INTR */
+/* #define AM53C974_DEB_RESEL */
+#define AM53C974_DEBUG_ABORT
+/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
+
+/* special options/constants */
+#define DEF_CLK                 40   /* chip clock freq. in MHz */
+#define MIN_PERIOD               4   /* for negotiation: min. number of clocks per cycle */
+#define MAX_PERIOD              13   /* for negotiation: max. number of clocks per cycle */
+#define MAX_OFFSET              15   /* for negotiation: max. offset (0=async) */
+
+#define DEF_SCSI_TIMEOUT        245  /* STIMREG value, 40 Mhz */
+#define DEF_STP                 8    /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
+#define DEF_SOF_RAD             0    /* REQ/ACK deassertion delay */
+#define DEF_SOF_RAA             0    /* REQ/ACK assertion delay */
+#define DEF_ETM                 0    /* CNTLREG1, ext. timing mode */
+#define DEF_PERE                1    /* CNTLREG1, parity error reporting */
+#define DEF_CLKF                0    /* CLKFREG,  0=40 Mhz */
+#define DEF_ENF                 1    /* CNTLREG2, enable features */
+#define DEF_ADIDCHK             0    /* CNTLREG3, additional ID check */
+#define DEF_FASTSCSI            1    /* CNTLREG3, fast SCSI */
+#define DEF_FASTCLK             1    /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
+#define DEF_GLITCH              1    /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
+#define DEF_PWD                 0    /* CNTLREG4, reduced power feature */
+#define DEF_RAE                 0    /* CNTLREG4, RAE active negation on REQ, ACK only */
+#define DEF_RADE                1    /* 1CNTLREG4, active negation on REQ, ACK and data */
+
+/*** PCI block ***/
+/* standard registers are defined in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_AMD
+#define PCI_VENDOR_ID_AMD	0x1022
+#define PCI_DEVICE_ID_AMD_SCSI  0x2020
+#endif
+#define PCI_BASE_MASK           0xFFFFFFE0
+#define PCI_COMMAND_PERREN      0x40
+#define PCI_SCRATCH_REG_0	0x40	/* 16 bits */
+#define PCI_SCRATCH_REG_1	0x42	/* 16 bits */
+#define PCI_SCRATCH_REG_2	0x44	/* 16 bits */
+#define PCI_SCRATCH_REG_3	0x46	/* 16 bits */
+#define PCI_SCRATCH_REG_4	0x48	/* 16 bits */
+#define PCI_SCRATCH_REG_5	0x4A	/* 16 bits */
+#define PCI_SCRATCH_REG_6	0x4C	/* 16 bits */
+#define PCI_SCRATCH_REG_7	0x4E	/* 16 bits */
+
+/*** SCSI block ***/
+#define CTCLREG		    	0x00	/* r	current transf. count, low byte    */
+#define CTCMREG		   	0x04	/* r 	current transf. count, middle byte */
+#define CTCHREG		    	0x38	/* r	current transf. count, high byte   */
+#define STCLREG		    	0x00	/* w	start transf. count, low byte      */
+#define STCMREG		    	0x04	/* w	start transf. count, middle byte   */
+#define STCHREG		    	0x38	/* w 	start transf. count, high byte     */
+#define FFREG		    	0x08	/* rw	SCSI FIFO reg.			   */
+#define STIMREG		    	0x14	/* w	SCSI timeout reg.		   */
+
+#define SDIDREG		    	0x10	/* w	SCSI destination ID reg.	   */
+#define SDIREG_MASK		0x07	/* mask					   */
+
+#define STPREG		    	0x18	/* w	synchronous transf. period reg.	   */
+#define STPREG_STP		0x1F	/* synchr. transfer period		   */
+
+#define CLKFREG		    	0x24	/* w	clock factor reg.		   */
+#define CLKFREG_MASK		0x07	/* mask					   */
+
+#define CMDREG		    	0x0C	/* rw	SCSI command reg.		   */
+#define CMDREG_DMA         	0x80    /* set DMA mode (set together with opcodes below) */
+#define CMDREG_IT          	0x10    /* information transfer 		   */
+#define CMDREG_ICCS		0x11	/* initiator command complete steps 	   */
+#define CMDREG_MA		0x12	/* message accepted 			   */
+#define CMDREG_TPB		0x98	/* transfer pad bytes, DMA mode only 	   */
+#define CMDREG_SATN		0x1A	/* set ATN 				   */
+#define CMDREG_RATN		0x1B	/* reset ATN 				   */
+#define CMDREG_SOAS		0x41	/* select without ATN steps 		   */
+#define CMDREG_SAS		0x42	/* select with ATN steps (1 msg byte)	   */
+#define CMDREG_SASS		0x43	/* select with ATN and stop steps 	   */
+#define CMDREG_ESR		0x44	/* enable selection/reselection 	   */
+#define CMDREG_DSR		0x45	/* disable selection/reselection 	   */
+#define CMDREG_SA3S		0x46	/* select with ATN 3 steps  (3 msg bytes)  */
+#define CMDREG_NOP		0x00	/* no operation 			   */
+#define CMDREG_CFIFO		0x01	/* clear FIFO 				   */
+#define CMDREG_RDEV		0x02	/* reset device 			   */
+#define CMDREG_RBUS		0x03	/* reset SCSI bus 			   */
+
+#define STATREG		    	0x10	/* r 	SCSI status reg.		   */
+#define STATREG_INT		0x80	/* SCSI interrupt condition detected	   */
+#define STATREG_IOE		0x40	/* SCSI illegal operation error detected   */
+#define STATREG_PE		0x20	/* SCSI parity error detected		   */
+#define STATREG_CTZ		0x10	/* CTC reg decremented to zero		   */
+#define STATREG_MSG		0x04	/* SCSI MSG phase (latched?)		   */
+#define STATREG_CD		0x02	/* SCSI C/D phase (latched?)		   */
+#define STATREG_IO		0x01	/* SCSI I/O phase (latched?)		   */
+#define STATREG_PHASE           0x07    /* SCSI phase mask 			   */
+
+#define INSTREG		    	0x14	/* r	interrupt status reg.		   */
+#define INSTREG_SRST		0x80	/* SCSI reset detected			   */
+#define INSTREG_ICMD		0x40	/* SCSI invalid command detected	   */
+#define INSTREG_DIS		0x20	/* target disconnected or sel/resel timeout*/
+#define INSTREG_SR		0x10	/* device on bus has service request       */
+#define INSTREG_SO		0x08	/* successful operation			   */
+#define INSTREG_RESEL		0x04	/* device reselected as initiator	   */
+
+#define ISREG		    	0x18	/* r	internal state reg.		   */
+#define ISREG_SOF		0x08	/* synchronous offset flag (act. low)	   */
+#define ISREG_IS		0x07	/* status of intermediate op.		   */
+#define ISREG_OK_NO_STOP        0x04    /* selection successful                    */
+#define ISREG_OK_STOP           0x01    /* selection successful                    */
+
+#define CFIREG		    	0x1C	/* r	current FIFO/internal state reg.   */
+#define CFIREG_IS		0xE0	/* status of intermediate op.		   */
+#define CFIREG_CF		0x1F	/* number of bytes in SCSI FIFO		   */
+
+#define SOFREG		    	0x1C	/* w	synchr. offset reg.		   */
+#define SOFREG_RAD		0xC0	/* REQ/ACK deassertion delay (sync.)	   */
+#define SOFREG_RAA		0x30	/* REQ/ACK assertion delay (sync.)	   */
+#define SOFREG_SO		0x0F	/* synch. offset (sync.)		   */
+
+#define CNTLREG1	    	0x20	/* rw	control register one		   */
+#define CNTLREG1_ETM		0x80	/* set extended timing mode		   */
+#define CNTLREG1_DISR		0x40	/* disable interrupt on SCSI reset	   */
+#define CNTLREG1_PERE		0x10	/* enable parity error reporting	   */
+#define CNTLREG1_SID		0x07	/* host adapter SCSI ID			   */
+
+#define CNTLREG2	    	0x2C	/* rw	control register two		   */
+#define CNTLREG2_ENF		0x40	/* enable features			   */
+
+#define CNTLREG3	    	0x30	/* rw	control register three		   */
+#define CNTLREG3_ADIDCHK	0x80	/* additional ID check			   */
+#define CNTLREG3_FASTSCSI	0x10	/* fast SCSI				   */
+#define CNTLREG3_FASTCLK	0x08	/* fast SCSI clocking			   */
+
+#define CNTLREG4	    	0x34	/* rw	control register four		   */
+#define CNTLREG4_GLITCH		0xC0	/* glitch eater				   */
+#define CNTLREG4_PWD		0x20	/* reduced power feature		   */
+#define CNTLREG4_RAE		0x08	/* write only, active negot. ctrl.	   */
+#define CNTLREG4_RADE		0x04	/* active negot. ctrl.			   */
+#define CNTLREG4_RES		0x10	/* reserved bit, must be 1		   */
+
+/*** DMA block ***/
+#define DMACMD		    	0x40	/* rw	command				   */
+#define DMACMD_DIR		0x80	/* transfer direction (1=read from device) */
+#define DMACMD_INTE_D		0x40	/* DMA transfer interrupt enable 	   */
+#define DMACMD_INTE_P		0x20	/* page transfer interrupt enable 	   */
+#define DMACMD_MDL		0x10	/* map to memory descriptor list 	   */
+#define DMACMD_DIAG		0x04	/* diagnostics, set to 0		   */
+#define DMACMD_IDLE 		0x00	/* idle cmd			 	   */
+#define DMACMD_BLAST		0x01	/* flush FIFO to memory		 	   */
+#define DMACMD_ABORT		0x02	/* terminate DMA		 	   */
+#define DMACMD_START		0x03	/* start DMA			 	   */
+
+#define DMASTATUS	      	0x54	/* r	status register			   */
+#define DMASTATUS_BCMPLT	0x20	/* BLAST complete			   */
+#define DMASTATUS_SCSIINT	0x10	/* SCSI interrupt pending		   */
+#define DMASTATUS_DONE		0x08	/* DMA transfer terminated		   */
+#define DMASTATUS_ABORT		0x04	/* DMA transfer aborted			   */
+#define DMASTATUS_ERROR		0x02	/* DMA transfer error			   */
+#define DMASTATUS_PWDN		0x02	/* power down indicator			   */
+
+#define DMASTC		    	0x44	/* rw	starting transfer count		   */
+#define DMASPA		    	0x48	/* rw	starting physical address	   */
+#define DMAWBC		    	0x4C	/* r	working byte counter		   */
+#define DMAWAC		    	0x50	/* r	working address counter		   */
+#define DMASMDLA	    	0x58	/* rw	starting MDL address		   */
+#define DMAWMAC		    	0x5C	/* r	working MDL counter		   */
+
+/*** SCSI phases ***/
+#define PHASE_MSGIN             0x07
+#define PHASE_MSGOUT            0x06
+#define PHASE_RES_1             0x05
+#define PHASE_RES_0             0x04
+#define PHASE_STATIN            0x03
+#define PHASE_CMDOUT            0x02
+#define PHASE_DATAIN            0x01
+#define PHASE_DATAOUT           0x00
+
+
+#define AM53C974_local_declare()	unsigned long io_port
+#define AM53C974_setio(instance)	io_port = instance->io_port
+#define AM53C974_read_8(addr)           inb(io_port + (addr))
+#define AM53C974_write_8(addr,x)        outb((x), io_port + (addr))
+#define AM53C974_read_16(addr)          inw(io_port + (addr))
+#define AM53C974_write_16(addr,x)       outw((x), io_port + (addr))
+#define AM53C974_read_32(addr)          inl(io_port + (addr))
+#define AM53C974_write_32(addr,x)       outl((x), io_port + (addr))
+
+#define AM53C974_poll_int()             { do { statreg = AM53C974_read_8(STATREG); } \
+                                             while (!(statreg & STATREG_INT)) ; \
+                                          AM53C974_read_8(INSTREG) ; } /* clear int */
+#define AM53C974_cfifo()		(AM53C974_read_8(CFIREG) & CFIREG_CF)
+
+/* These are "special" values for the tag parameter passed to AM53C974_select. */
+#define TAG_NEXT	-1 	/* Use next free tag */
+#define TAG_NONE	-2	/* Establish I_T_L nexus instead of I_T_L_Q
+				 * even on SCSI-II devices */
+
+/************ LILO overrides *************/
+typedef struct _override_t {
+    int host_scsi_id;			/* SCSI id of the bus controller */
+    int target_scsi_id;                 /* SCSI id of target */
+    int max_rate;			/* max. transfer rate */
+    int max_offset;	                /* max. sync. offset, 0 = asynchronous */
+    } override_t;
+
+/************ PCI stuff *************/
+#define AM53C974_PCIREG_OPEN()                    outb(0xF1, 0xCF8); outb(0, 0xCFA)
+#define AM53C974_PCIREG_CLOSE()                   outb(0, 0xCF8)
+#define AM53C974_PCIREG_READ_BYTE(instance,a)     ( inb((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_WORD(instance,a)     ( inw((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_DWORD(instance,a)    ( inl((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a)  ( outb((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_WORD(instance,x,a)  ( outw((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) )
+
+typedef struct _pci_config_t {
+    /* start of official PCI config space header */
+    union {
+        unsigned int device_vendor;
+	struct {
+	  unsigned short vendor;
+	  unsigned short device;
+ 	  } dv;
+        } dv_id;
+#define _device_vendor dv_id.device_vendor
+#define _vendor dv_id.dv.vendor
+#define _device dv_id.dv.device
+    union {
+        unsigned int status_command;
+	struct {
+	  unsigned short command;
+	  unsigned short status;
+	  } sc;
+        } stat_cmd;
+#define _status_command stat_cmd.status_command
+#define _command stat_cmd.sc.command
+#define _status  stat_cmd.sc.status
+    union {
+        unsigned int class_revision;
+	struct {
+	    unsigned char rev_id;
+	    unsigned char prog_if;
+	    unsigned char sub_class;
+	    unsigned char base_class;
+	} cr;
+    } class_rev;
+#define _class_revision class_rev.class_revision
+#define _rev_id     class_rev.cr.rev_id
+#define _prog_if    class_rev.cr.prog_if
+#define _sub_class  class_rev.cr.sub_class
+#define _base_class class_rev.cr.base_class
+    union {
+        unsigned int bist_header_latency_cache;
+	struct {
+	    unsigned char cache_line_size;
+	    unsigned char latency_timer;
+	    unsigned char header_type;
+	    unsigned char bist;
+	} bhlc;
+    } bhlc;
+#define _bist_header_latency_cache bhlc.bist_header_latency_cache
+#define _cache_line_size bhlc.bhlc.cache_line_size
+#define _latency_timer   bhlc.bhlc.latency_timer
+#define _header_type     bhlc.bhlc.header_type
+#define _bist            bhlc.bhlc.bist
+    unsigned int _base0;
+    unsigned int _base1;
+    unsigned int _base2;
+    unsigned int _base3;
+    unsigned int _base4;
+    unsigned int _base5;
+    unsigned int rsvd1;
+    unsigned int rsvd2;
+    unsigned int _baserom;
+    unsigned int rsvd3;
+    unsigned int rsvd4;
+    union {
+        unsigned int max_min_ipin_iline;
+	struct {
+	    unsigned char int_line;
+	    unsigned char int_pin;
+	    unsigned char min_gnt;
+	    unsigned char max_lat;
+	} mmii;
+    } mmii;
+#define _max_min_ipin_iline mmii.max_min_ipin_iline
+#define _int_line mmii.mmii.int_line
+#define _int_pin  mmii.mmii.int_pin
+#define _min_gnt  mmii.mmii.min_gnt
+#define _max_lat  mmii.mmii.max_lat
+    /* end of official PCI config space header */
+    unsigned short _ioaddr; /* config type 1 - private I/O addr    */
+    unsigned int _pcibus;  /* config type 2 - private bus id      */
+    unsigned int _cardnum; /* config type 2 - private card number */
+} pci_config_t;
+
+
 #ifdef AM53C974_DEBUG
 static void AM53C974_print_pci(struct Scsi_Host *instance);
 static void AM53C974_print_phase(struct Scsi_Host *instance);
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h
--- v2.1.31/linux/drivers/scsi/AM53C974.h	Mon Jan 27 00:42:54 1997
+++ linux/drivers/scsi/AM53C974.h	Fri Apr  4 14:03:29 1997
@@ -27,222 +27,6 @@
 #ifndef AM53C974_H
 #define AM53C974_H
 
-#include <scsi/scsicam.h>
-
-/***************************************************************************************
-* Default setting of the controller's SCSI id. Edit and uncomment this only if your    *
-* BIOS does not correctly initialize the controller's SCSI id.                         *
-* If you don't get a warning during boot, it is correctly initialized.                 *
-****************************************************************************************/
-/* #define AM53C974_SCSI_ID 7 */
-
-/***************************************************************************************
-* Default settings for sync. negotiation enable, transfer rate and sync. offset.       *
-* These settings can be replaced by LILO overrides (append) with the following syntax:          *
-* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset                          *
-* Sync. negotiation is disabled by default and will be enabled for those targets which *
-* are specified in the LILO override                                                   *
-****************************************************************************************/
-#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
-#define DEFAULT_RATE			 5 /* MHz, min: 3; max: 10 */
-#define DEFAULT_SYNC_OFFSET		 0 /* bytes, min: 0; max: 15; use 0 for async. mode */
-
-/***************************************************************************************
-* If defined, don't allow targets to disconnect during commands.  This will reduce     *
-* performance, but may be worthwhile if you suspect the driver of corrupting data when *
-* a disconnect happens.                                                                *
-***************************************************************************************/
-#define AM53C974_PROHIBIT_DISCONNECT
-
-/* --------------------- don't edit below here  --------------------- */
-
-#define AM53C974_DRIVER_REVISION_MAJOR 0
-#define AM53C974_DRIVER_REVISION_MINOR 5
-#define SEPARATOR_LINE  \
-"--------------------------------------------------------------------------\n"
-
-/* debug control */
-/* #define AM53C974_DEBUG */
-/* #define AM53C974_DEBUG_MSG */
-/* #define AM53C974_DEBUG_KEYWAIT */
-/* #define AM53C974_DEBUG_INIT */
-/* #define AM53C974_DEBUG_QUEUE */
-/* #define AM53C974_DEBUG_INFO */
-/* #define AM53C974_DEBUG_LINKED */
-/* #define VERBOSE_AM53C974_DEBUG */
-/* #define AM53C974_DEBUG_INTR */
-/* #define AM53C974_DEB_RESEL */
-#define AM53C974_DEBUG_ABORT
-/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
-
-/* special options/constants */
-#define DEF_CLK                 40   /* chip clock freq. in MHz */
-#define MIN_PERIOD               4   /* for negotiation: min. number of clocks per cycle */
-#define MAX_PERIOD              13   /* for negotiation: max. number of clocks per cycle */
-#define MAX_OFFSET              15   /* for negotiation: max. offset (0=async) */
-
-#define DEF_SCSI_TIMEOUT        245  /* STIMREG value, 40 Mhz */
-#define DEF_STP                 8    /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
-#define DEF_SOF_RAD             0    /* REQ/ACK deassertion delay */
-#define DEF_SOF_RAA             0    /* REQ/ACK assertion delay */
-#define DEF_ETM                 0    /* CNTLREG1, ext. timing mode */
-#define DEF_PERE                1    /* CNTLREG1, parity error reporting */
-#define DEF_CLKF                0    /* CLKFREG,  0=40 Mhz */
-#define DEF_ENF                 1    /* CNTLREG2, enable features */
-#define DEF_ADIDCHK             0    /* CNTLREG3, additional ID check */
-#define DEF_FASTSCSI            1    /* CNTLREG3, fast SCSI */
-#define DEF_FASTCLK             1    /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
-#define DEF_GLITCH              1    /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
-#define DEF_PWD                 0    /* CNTLREG4, reduced power feature */
-#define DEF_RAE                 0    /* CNTLREG4, RAE active negation on REQ, ACK only */
-#define DEF_RADE                1    /* 1CNTLREG4, active negation on REQ, ACK and data */
-
-/*** PCI block ***/
-/* standard registers are defined in <linux/pci.h> */
-#ifndef PCI_VENDOR_ID_AMD
-#define PCI_VENDOR_ID_AMD	0x1022
-#define PCI_DEVICE_ID_AMD_SCSI  0x2020
-#endif
-#define PCI_BASE_MASK           0xFFFFFFE0
-#define PCI_COMMAND_PERREN      0x40
-#define PCI_SCRATCH_REG_0	0x40	/* 16 bits */
-#define PCI_SCRATCH_REG_1	0x42	/* 16 bits */
-#define PCI_SCRATCH_REG_2	0x44	/* 16 bits */
-#define PCI_SCRATCH_REG_3	0x46	/* 16 bits */
-#define PCI_SCRATCH_REG_4	0x48	/* 16 bits */
-#define PCI_SCRATCH_REG_5	0x4A	/* 16 bits */
-#define PCI_SCRATCH_REG_6	0x4C	/* 16 bits */
-#define PCI_SCRATCH_REG_7	0x4E	/* 16 bits */
-
-/*** SCSI block ***/
-#define CTCLREG		    	0x00	/* r	current transf. count, low byte    */
-#define CTCMREG		   	0x04	/* r 	current transf. count, middle byte */
-#define CTCHREG		    	0x38	/* r	current transf. count, high byte   */
-#define STCLREG		    	0x00	/* w	start transf. count, low byte      */
-#define STCMREG		    	0x04	/* w	start transf. count, middle byte   */
-#define STCHREG		    	0x38	/* w 	start transf. count, high byte     */
-#define FFREG		    	0x08	/* rw	SCSI FIFO reg.			   */
-#define STIMREG		    	0x14	/* w	SCSI timeout reg.		   */
-
-#define SDIDREG		    	0x10	/* w	SCSI destination ID reg.	   */
-#define SDIREG_MASK		0x07	/* mask					   */
-
-#define STPREG		    	0x18	/* w	synchronous transf. period reg.	   */
-#define STPREG_STP		0x1F	/* synchr. transfer period		   */
-
-#define CLKFREG		    	0x24	/* w	clock factor reg.		   */
-#define CLKFREG_MASK		0x07	/* mask					   */
-
-#define CMDREG		    	0x0C	/* rw	SCSI command reg.		   */
-#define CMDREG_DMA         	0x80    /* set DMA mode (set together with opcodes below) */
-#define CMDREG_IT          	0x10    /* information transfer 		   */
-#define CMDREG_ICCS		0x11	/* initiator command complete steps 	   */
-#define CMDREG_MA		0x12	/* message accepted 			   */
-#define CMDREG_TPB		0x98	/* transfer pad bytes, DMA mode only 	   */
-#define CMDREG_SATN		0x1A	/* set ATN 				   */
-#define CMDREG_RATN		0x1B	/* reset ATN 				   */
-#define CMDREG_SOAS		0x41	/* select without ATN steps 		   */
-#define CMDREG_SAS		0x42	/* select with ATN steps (1 msg byte)	   */
-#define CMDREG_SASS		0x43	/* select with ATN and stop steps 	   */
-#define CMDREG_ESR		0x44	/* enable selection/reselection 	   */
-#define CMDREG_DSR		0x45	/* disable selection/reselection 	   */
-#define CMDREG_SA3S		0x46	/* select with ATN 3 steps  (3 msg bytes)  */
-#define CMDREG_NOP		0x00	/* no operation 			   */
-#define CMDREG_CFIFO		0x01	/* clear FIFO 				   */
-#define CMDREG_RDEV		0x02	/* reset device 			   */
-#define CMDREG_RBUS		0x03	/* reset SCSI bus 			   */
-
-#define STATREG		    	0x10	/* r 	SCSI status reg.		   */
-#define STATREG_INT		0x80	/* SCSI interrupt condition detected	   */
-#define STATREG_IOE		0x40	/* SCSI illegal operation error detected   */
-#define STATREG_PE		0x20	/* SCSI parity error detected		   */
-#define STATREG_CTZ		0x10	/* CTC reg decremented to zero		   */
-#define STATREG_MSG		0x04	/* SCSI MSG phase (latched?)		   */
-#define STATREG_CD		0x02	/* SCSI C/D phase (latched?)		   */
-#define STATREG_IO		0x01	/* SCSI I/O phase (latched?)		   */
-#define STATREG_PHASE           0x07    /* SCSI phase mask 			   */
-
-#define INSTREG		    	0x14	/* r	interrupt status reg.		   */
-#define INSTREG_SRST		0x80	/* SCSI reset detected			   */
-#define INSTREG_ICMD		0x40	/* SCSI invalid command detected	   */
-#define INSTREG_DIS		0x20	/* target disconnected or sel/resel timeout*/
-#define INSTREG_SR		0x10	/* device on bus has service request       */
-#define INSTREG_SO		0x08	/* successful operation			   */
-#define INSTREG_RESEL		0x04	/* device reselected as initiator	   */
-
-#define ISREG		    	0x18	/* r	internal state reg.		   */
-#define ISREG_SOF		0x08	/* synchronous offset flag (act. low)	   */
-#define ISREG_IS		0x07	/* status of intermediate op.		   */
-#define ISREG_OK_NO_STOP        0x04    /* selection successful                    */
-#define ISREG_OK_STOP           0x01    /* selection successful                    */
-
-#define CFIREG		    	0x1C	/* r	current FIFO/internal state reg.   */
-#define CFIREG_IS		0xE0	/* status of intermediate op.		   */
-#define CFIREG_CF		0x1F	/* number of bytes in SCSI FIFO		   */
-
-#define SOFREG		    	0x1C	/* w	synchr. offset reg.		   */
-#define SOFREG_RAD		0xC0	/* REQ/ACK deassertion delay (sync.)	   */
-#define SOFREG_RAA		0x30	/* REQ/ACK assertion delay (sync.)	   */
-#define SOFREG_SO		0x0F	/* synch. offset (sync.)		   */
-
-#define CNTLREG1	    	0x20	/* rw	control register one		   */
-#define CNTLREG1_ETM		0x80	/* set extended timing mode		   */
-#define CNTLREG1_DISR		0x40	/* disable interrupt on SCSI reset	   */
-#define CNTLREG1_PERE		0x10	/* enable parity error reporting	   */
-#define CNTLREG1_SID		0x07	/* host adapter SCSI ID			   */
-
-#define CNTLREG2	    	0x2C	/* rw	control register two		   */
-#define CNTLREG2_ENF		0x40	/* enable features			   */
-
-#define CNTLREG3	    	0x30	/* rw	control register three		   */
-#define CNTLREG3_ADIDCHK	0x80	/* additional ID check			   */
-#define CNTLREG3_FASTSCSI	0x10	/* fast SCSI				   */
-#define CNTLREG3_FASTCLK	0x08	/* fast SCSI clocking			   */
-
-#define CNTLREG4	    	0x34	/* rw	control register four		   */
-#define CNTLREG4_GLITCH		0xC0	/* glitch eater				   */
-#define CNTLREG4_PWD		0x20	/* reduced power feature		   */
-#define CNTLREG4_RAE		0x08	/* write only, active negot. ctrl.	   */
-#define CNTLREG4_RADE		0x04	/* active negot. ctrl.			   */
-#define CNTLREG4_RES		0x10	/* reserved bit, must be 1		   */
-
-/*** DMA block ***/
-#define DMACMD		    	0x40	/* rw	command				   */
-#define DMACMD_DIR		0x80	/* transfer direction (1=read from device) */
-#define DMACMD_INTE_D		0x40	/* DMA transfer interrupt enable 	   */
-#define DMACMD_INTE_P		0x20	/* page transfer interrupt enable 	   */
-#define DMACMD_MDL		0x10	/* map to memory descriptor list 	   */
-#define DMACMD_DIAG		0x04	/* diagnostics, set to 0		   */
-#define DMACMD_IDLE 		0x00	/* idle cmd			 	   */
-#define DMACMD_BLAST		0x01	/* flush FIFO to memory		 	   */
-#define DMACMD_ABORT		0x02	/* terminate DMA		 	   */
-#define DMACMD_START		0x03	/* start DMA			 	   */
-
-#define DMASTATUS	      	0x54	/* r	status register			   */
-#define DMASTATUS_BCMPLT	0x20	/* BLAST complete			   */
-#define DMASTATUS_SCSIINT	0x10	/* SCSI interrupt pending		   */
-#define DMASTATUS_DONE		0x08	/* DMA transfer terminated		   */
-#define DMASTATUS_ABORT		0x04	/* DMA transfer aborted			   */
-#define DMASTATUS_ERROR		0x02	/* DMA transfer error			   */
-#define DMASTATUS_PWDN		0x02	/* power down indicator			   */
-
-#define DMASTC		    	0x44	/* rw	starting transfer count		   */
-#define DMASPA		    	0x48	/* rw	starting physical address	   */
-#define DMAWBC		    	0x4C	/* r	working byte counter		   */
-#define DMAWAC		    	0x50	/* r	working address counter		   */
-#define DMASMDLA	    	0x58	/* rw	starting MDL address		   */
-#define DMAWMAC		    	0x5C	/* r	working MDL counter		   */
-
-/*** SCSI phases ***/
-#define PHASE_MSGIN             0x07
-#define PHASE_MSGOUT            0x06
-#define PHASE_RES_1             0x05
-#define PHASE_RES_0             0x04
-#define PHASE_STATIN            0x03
-#define PHASE_CMDOUT            0x02
-#define PHASE_DATAIN            0x01
-#define PHASE_DATAOUT           0x00
-
 struct AM53C974_hostdata {
     volatile unsigned       in_reset:1;          /* flag, says bus reset pending */
     volatile unsigned       aborted:1;           /* flag, says aborted */
@@ -301,122 +85,5 @@
 int AM53C974_abort(Scsi_Cmnd *cmd);
 int AM53C974_reset (Scsi_Cmnd *cmd);
 
-#define AM53C974_local_declare()	unsigned long io_port
-#define AM53C974_setio(instance)	io_port = instance->io_port
-#define AM53C974_read_8(addr)           inb(io_port + (addr))
-#define AM53C974_write_8(addr,x)        outb((x), io_port + (addr))
-#define AM53C974_read_16(addr)          inw(io_port + (addr))
-#define AM53C974_write_16(addr,x)       outw((x), io_port + (addr))
-#define AM53C974_read_32(addr)          inl(io_port + (addr))
-#define AM53C974_write_32(addr,x)       outl((x), io_port + (addr))
-
-#define AM53C974_poll_int()             { do { statreg = AM53C974_read_8(STATREG); } \
-                                             while (!(statreg & STATREG_INT)) ; \
-                                          AM53C974_read_8(INSTREG) ; } /* clear int */
-#define AM53C974_cfifo()		(AM53C974_read_8(CFIREG) & CFIREG_CF)
-
-/* These are "special" values for the tag parameter passed to AM53C974_select. */
-#define TAG_NEXT	-1 	/* Use next free tag */
-#define TAG_NONE	-2	/* Establish I_T_L nexus instead of I_T_L_Q
-				 * even on SCSI-II devices */
-
-/************ LILO overrides *************/
-typedef struct _override_t {
-    int host_scsi_id;			/* SCSI id of the bus controller */
-    int target_scsi_id;                 /* SCSI id of target */
-    int max_rate;			/* max. transfer rate */
-    int max_offset;	                /* max. sync. offset, 0 = asynchronous */
-    } override_t;
-
-/************ PCI stuff *************/
-#define AM53C974_PCIREG_OPEN()                    outb(0xF1, 0xCF8); outb(0, 0xCFA)
-#define AM53C974_PCIREG_CLOSE()                   outb(0, 0xCF8)
-#define AM53C974_PCIREG_READ_BYTE(instance,a)     ( inb((a) + (instance)->io_port) )
-#define AM53C974_PCIREG_READ_WORD(instance,a)     ( inw((a) + (instance)->io_port) )
-#define AM53C974_PCIREG_READ_DWORD(instance,a)    ( inl((a) + (instance)->io_port) )
-#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a)  ( outb((x), (a) + (instance)->io_port) )
-#define AM53C974_PCIREG_WRITE_WORD(instance,x,a)  ( outw((x), (a) + (instance)->io_port) )
-#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) )
-
-typedef struct _pci_config_t {
-    /* start of official PCI config space header */
-    union {
-        unsigned int device_vendor;
-	struct {
-	  unsigned short vendor;
-	  unsigned short device;
- 	  } dv;
-        } dv_id;
-#define _device_vendor dv_id.device_vendor
-#define _vendor dv_id.dv.vendor
-#define _device dv_id.dv.device
-    union {
-        unsigned int status_command;
-	struct {
-	  unsigned short command;
-	  unsigned short status;
-	  } sc;
-        } stat_cmd;
-#define _status_command stat_cmd.status_command
-#define _command stat_cmd.sc.command
-#define _status  stat_cmd.sc.status
-    union {
-        unsigned int class_revision;
-	struct {
-	    unsigned char rev_id;
-	    unsigned char prog_if;
-	    unsigned char sub_class;
-	    unsigned char base_class;
-	} cr;
-    } class_rev;
-#define _class_revision class_rev.class_revision
-#define _rev_id     class_rev.cr.rev_id
-#define _prog_if    class_rev.cr.prog_if
-#define _sub_class  class_rev.cr.sub_class
-#define _base_class class_rev.cr.base_class
-    union {
-        unsigned int bist_header_latency_cache;
-	struct {
-	    unsigned char cache_line_size;
-	    unsigned char latency_timer;
-	    unsigned char header_type;
-	    unsigned char bist;
-	} bhlc;
-    } bhlc;
-#define _bist_header_latency_cache bhlc.bist_header_latency_cache
-#define _cache_line_size bhlc.bhlc.cache_line_size
-#define _latency_timer   bhlc.bhlc.latency_timer
-#define _header_type     bhlc.bhlc.header_type
-#define _bist            bhlc.bhlc.bist
-    unsigned int _base0;
-    unsigned int _base1;
-    unsigned int _base2;
-    unsigned int _base3;
-    unsigned int _base4;
-    unsigned int _base5;
-    unsigned int rsvd1;
-    unsigned int rsvd2;
-    unsigned int _baserom;
-    unsigned int rsvd3;
-    unsigned int rsvd4;
-    union {
-        unsigned int max_min_ipin_iline;
-	struct {
-	    unsigned char int_line;
-	    unsigned char int_pin;
-	    unsigned char min_gnt;
-	    unsigned char max_lat;
-	} mmii;
-    } mmii;
-#define _max_min_ipin_iline mmii.max_min_ipin_iline
-#define _int_line mmii.mmii.int_line
-#define _int_pin  mmii.mmii.int_pin
-#define _min_gnt  mmii.mmii.min_gnt
-#define _max_lat  mmii.mmii.max_lat
-    /* end of official PCI config space header */
-    unsigned short _ioaddr; /* config type 1 - private I/O addr    */
-    unsigned int _pcibus;  /* config type 2 - private bus id      */
-    unsigned int _cardnum; /* config type 2 - private card number */
-} pci_config_t;
-
 #endif /* AM53C974_H */
+
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/eata_pio_proc.c linux/drivers/scsi/eata_pio_proc.c
--- v2.1.31/linux/drivers/scsi/eata_pio_proc.c	Thu Apr 11 23:49:41 1996
+++ linux/drivers/scsi/eata_pio_proc.c	Fri Apr  4 14:03:29 1997
@@ -1,19 +1,3 @@
-
-#define MAX_SCSI_DEVICE_CODE 10
-const char *const pio_scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-
 /*
  * eata_set_info
  * buffer : pointer to the data that has been written to the hostfile
@@ -147,4 +131,3 @@
  * tab-width: 8
  * End:
  */
-
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c
--- v2.1.31/linux/drivers/scsi/g_NCR5380.c	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/g_NCR5380.c	Fri Apr  4 14:03:29 1997
@@ -331,6 +331,10 @@
     int blocks = len / 128;
     int start = 0;
     int bl;
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+    int i;
+#endif 
+
     NCR5380_local_declare();
 
     NCR5380_setup(instance);
@@ -605,21 +609,6 @@
     return len-start;
 }
 
-const char *const private_scsi_device_types[] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-#define MAX_SCSI_DEVICE_CODE sizeof(private_scsi_device_types)/sizeof(char*)
-
 int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
 {
     int len = 0;
@@ -630,7 +619,8 @@
 	Scsi_Device *dev;
     Scsi_Cmnd *ptr;
     struct NCR5380_hostdata *hostdata;
-
+    extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
+    
     cli();
 
     for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr=scsi_ptr->next)
@@ -673,7 +663,7 @@
 	    long tr = hostdata->time_read[dev->id] / HZ;
 	    long tw = hostdata->time_write[dev->id] / HZ;
 
-	    PRINTP("  T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? private_scsi_device_types[(int)dev->type] : "Unknown");
+	    PRINTP("  T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int)dev->type] : "Unknown");
 	    for (i=0; i<8; i++)
 		if (dev->vendor[i] >= 0x20)
 		    *(buffer+(len++)) = dev->vendor[i];
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.31/linux/drivers/scsi/scsi.c	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/scsi.c	Fri Apr  4 14:03:29 1997
@@ -110,7 +110,6 @@
 #define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
 			  || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
 
-#define MAX_SCSI_DEVICE_CODE 10
 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
 {
     "Direct-Access    ",
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.1.31/linux/drivers/scsi/scsi.h	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/scsi.h	Fri Apr  4 14:03:29 1997
@@ -34,6 +34,7 @@
 # define FALSE 0
 #endif
 
+#define MAX_SCSI_DEVICE_CODE 10
 
 extern void scsi_make_blocked_list(void);
 extern volatile int in_scan_scsis;
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v2.1.31/linux/drivers/scsi/scsi_proc.c	Sat Nov 11 03:11:19 1995
+++ linux/drivers/scsi/scsi_proc.c	Fri Apr  4 14:03:29 1997
@@ -239,25 +239,12 @@
     return(cmdIndex);
 }
 
-#define MAX_SCSI_DEVICE_CODE 10
-const char *const scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
-{
-    "Direct-Access    ",
-    "Sequential-Access",
-    "Printer          ",
-    "Processor        ",
-    "WORM             ",
-    "CD-ROM           ",
-    "Scanner          ",
-    "Optical Device   ",
-    "Medium Changer   ",
-    "Communications   "
-};
-
 void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
 {	    
+  
     int x, y = *size;
-    
+    extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
+        
     y = sprintf(buffer + len, 
 		    "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n  Vendor: ",
 		    scd->host->host_no, scd->channel, scd->id, scd->lun);
@@ -285,7 +272,7 @@
     
     y += sprintf(buffer + len + y, "  Type:   %s ",
 		     scd->type < MAX_SCSI_DEVICE_CODE ? 
-		     scsi_dev_types[(int)scd->type] : "Unknown          " );
+		     scsi_device_types[(int)scd->type] : "Unknown          " );
     y += sprintf(buffer + len + y, "               ANSI"
 		     " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
     if (scd->scsi_level == 2)
@@ -315,3 +302,4 @@
  * tab-width: 8
  * End:
  */
+
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.1.31/linux/drivers/scsi/sd.c	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/sd.c	Thu Apr  3 10:23:56 1997
@@ -60,7 +60,7 @@
  */
 
 #define SD_TIMEOUT (15 * HZ)
-#define SD_MOD_TIMEOUT (15 * HZ)
+#define SD_MOD_TIMEOUT (75 * HZ)
 
 #define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
 				SC->device->type != TYPE_MOD)
@@ -254,6 +254,11 @@
 	    error_sector <<= 1;
 	    if (block_sectors < 2) block_sectors = 2;
 	  }
+        else if (sector_size == 2048)
+	  {
+	    error_sector <<= 2;
+	    if (block_sectors < 4) block_sectors = 4;
+	  }
 	else if (sector_size == 256)
 	  error_sector >>= 1;
 	error_sector -= sd[MINOR(SCpnt->request.rq_dev)].start_sect;
@@ -634,6 +639,13 @@
 	    goto repeat;
 	}
 
+     if (rscsi_disks[dev].sector_size == 2048)
+ 	if((block & 3) || (SCpnt->request.nr_sectors & 3)) {
+ 	    printk("sd.c:Bad block number requested");
+ 	    SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ 	    goto repeat;
+ 	}
+     
     switch (SCpnt->request.cmd)
     {
     case WRITE :
@@ -900,6 +912,13 @@
 
     cmd[1] = (SCpnt->lun << 5) & 0xe0;
 
+     if (rscsi_disks[dev].sector_size == 2048){
+ 	if(block & 3) panic("sd.c:Bad block number requested");
+ 	if(this_count & 3) panic("sd.c:Bad block number requested");
+ 	block = block >> 2;
+ 	this_count = this_count >> 2;
+     }
+     
     if (rscsi_disks[dev].sector_size == 1024){
 	if(block & 1) panic("sd.c:Bad block number requested");
 	if(this_count & 1) panic("sd.c:Bad block number requested");
@@ -1201,6 +1220,7 @@
 
 	if (rscsi_disks[i].sector_size != 512 &&
 	    rscsi_disks[i].sector_size != 1024 &&
+	    rscsi_disks[i].sector_size != 2048 &&
 	    rscsi_disks[i].sector_size != 256)
 	{
 	    printk ("sd%c : unsupported sector size %d.\n",
@@ -1215,6 +1235,22 @@
 		return i;
 	    }
 	}
+
+        if( rscsi_disks[i].sector_size == 2048 )
+          {
+            int m;
+
+            /*
+             * We must fix the sd_blocksizes and sd_hardsizes
+             * to allow us to read the partition tables.
+             * The disk reading code does not allow for reading
+             * of partial sectors.
+             */
+            for (m=i<<4; m<((i+1)<<4); m++)
+              {
+                sd_blocksizes[m] = 2048;
+              }
+          }
     {
 	/*
 	 * The msdos fs needs to know the hardware sector size
@@ -1238,6 +1274,8 @@
 		i+'a', hard_sector, rscsi_disks[i].capacity,
                 mb, sz_quot, sz_rem);
     }
+	if(rscsi_disks[i].sector_size == 2048)
+	    rscsi_disks[i].capacity <<= 2;  /* Change into 512 byte sectors */
 	if(rscsi_disks[i].sector_size == 1024)
 	    rscsi_disks[i].capacity <<= 1;  /* Change into 512 byte sectors */
 	if(rscsi_disks[i].sector_size == 256)
@@ -1341,10 +1379,12 @@
     sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
 					    sizeof(int), GFP_ATOMIC);
 
-    for(i=0;i<(sd_template.dev_max << 4);i++){
-	sd_blocksizes[i] = 1024;
-	sd_hardsizes[i] = 512;
-    }
+     for(i=0;i<(sd_template.dev_max << 4);i++)
+       {
+         sd_blocksizes[i] = 1024;
+         sd_hardsizes[i] = 512;
+       }
+ 
     blksize_size[MAJOR_NR] = sd_blocksizes;
     hardsect_size[MAJOR_NR] = sd_hardsizes;
     sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
@@ -1477,9 +1517,15 @@
 	gdev->part[minor].nr_sects = 0;
         /*
          * Reset the blocksize for everything so that we can read
-         * the partition table.
+         * the partition table.  Technically we will determine the
+         * correct block size when we revalidate, but we do this just
+         * to make sure that everything remains consistent.
          */
         blksize_size[MAJOR_NR][minor] = 1024;
+        if( rscsi_disks[target].sector_size == 2048 )
+          blksize_size[MAJOR_NR][minor] = 2048;
+        else
+          blksize_size[MAJOR_NR][minor] = 1024;
     }
 
 #ifdef MAYBE_REINIT
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v2.1.31/linux/drivers/scsi/seagate.c	Thu Mar 27 14:40:05 1997
+++ linux/drivers/scsi/seagate.c	Fri Apr  4 14:03:30 1997
@@ -127,6 +127,103 @@
                                            broken! */
 #endif
 
+
+/*
+	Thanks to Brian Antoine for the example code in his Messy-Loss ST-01
+		driver, and Mitsugu Suzuki for information on the ST-01
+		SCSI host.
+*/
+
+/*
+	CONTROL defines
+*/
+
+#define CMD_RST 		0x01
+#define CMD_SEL 		0x02
+#define CMD_BSY 		0x04
+#define CMD_ATTN    		0x08
+#define CMD_START_ARB		0x10
+#define CMD_EN_PARITY		0x20
+#define CMD_INTR		0x40
+#define CMD_DRVR_ENABLE		0x80
+
+/*
+	STATUS
+*/
+#ifdef SWAPSTAT
+	#define STAT_MSG		0x08
+	#define STAT_CD			0x02
+#else
+	#define STAT_MSG		0x02
+	#define STAT_CD			0x08
+#endif
+
+#define STAT_BSY		0x01
+#define STAT_IO			0x04
+#define STAT_REQ		0x10
+#define STAT_SEL		0x20
+#define STAT_PARITY		0x40
+#define STAT_ARB_CMPL		0x80
+
+/* 
+	REQUESTS
+*/
+
+#define REQ_MASK (STAT_CD |  STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+extern volatile int seagate_st0x_timeout;
+
+#ifdef PARITY
+	#define BASE_CMD CMD_EN_PARITY
+#else
+	#define BASE_CMD  0
+#endif
+
+/*
+	Debugging code
+*/
+
+#define PHASE_BUS_FREE 1
+#define PHASE_ARBITRATION 2
+#define PHASE_SELECTION 4
+#define PHASE_DATAIN 8 
+#define PHASE_DATAOUT 0x10
+#define PHASE_CMDOUT 0x20
+#define PHASE_MSGIN 0x40
+#define PHASE_MSGOUT 0x80
+#define PHASE_STATUSIN 0x100
+#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN)
+#define PRINT_COMMAND 0x200
+#define PHASE_EXIT 0x400
+#define PHASE_RESELECT 0x800
+#define DEBUG_FAST 0x1000
+#define DEBUG_SG   0x2000
+#define DEBUG_LINKED	0x4000
+#define DEBUG_BORKEN	0x8000
+
+/* 
+ *	Control options - these are timeouts specified in .01 seconds.
+ */
+
+/* 30, 20 work */
+#define ST0X_BUS_FREE_DELAY 25
+#define ST0X_SELECTION_DELAY 25
+
+#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::)
+	
+#define SEAGATE 1	/* these determine the type of the controller */
+#define FD	2
+
+#define ST0X_ID_STR	"Seagate ST-01/ST-02"
+#define FD_ID_STR	"TMC-8XX/TMC-950"
+
+
 static int internal_command (unsigned char target, unsigned char lun,
                              const void *cmnd,
                              void *buff, int bufflen, int reselect);
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/seagate.h linux/drivers/scsi/seagate.h
--- v2.1.31/linux/drivers/scsi/seagate.h	Fri Jan 24 09:35:51 1997
+++ linux/drivers/scsi/seagate.h	Fri Apr  4 14:03:30 1997
@@ -35,102 +35,6 @@
 			 seagate_st0x_queue_command, seagate_st0x_abort, \
 			 seagate_st0x_reset, NULL, seagate_st0x_biosparam, \
 			 1, 7, SG_ALL, 1, 0, 0, DISABLE_CLUSTERING}
-#endif
-
-/*
-	Thanks to Brian Antoine for the example code in his Messy-Loss ST-01
-		driver, and Mitsugu Suzuki for information on the ST-01
-		SCSI host.
-*/
-
-/*
-	CONTROL defines
-*/
-
-#define CMD_RST 		0x01
-#define CMD_SEL 		0x02
-#define CMD_BSY 		0x04
-#define CMD_ATTN    		0x08
-#define CMD_START_ARB		0x10
-#define CMD_EN_PARITY		0x20
-#define CMD_INTR		0x40
-#define CMD_DRVR_ENABLE		0x80
-
-/*
-	STATUS
-*/
-#ifdef SWAPSTAT
-	#define STAT_MSG		0x08
-	#define STAT_CD			0x02
-#else
-	#define STAT_MSG		0x02
-	#define STAT_CD			0x08
-#endif
-
-#define STAT_BSY		0x01
-#define STAT_IO			0x04
-#define STAT_REQ		0x10
-#define STAT_SEL		0x20
-#define STAT_PARITY		0x40
-#define STAT_ARB_CMPL		0x80
-
-/* 
-	REQUESTS
-*/
-
-#define REQ_MASK (STAT_CD |  STAT_IO | STAT_MSG)
-#define REQ_DATAOUT 0
-#define REQ_DATAIN STAT_IO
-#define REQ_CMDOUT STAT_CD
-#define REQ_STATIN (STAT_CD | STAT_IO)
-#define REQ_MSGOUT (STAT_MSG | STAT_CD)
-#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
-
-extern volatile int seagate_st0x_timeout;
-
-#ifdef PARITY
-	#define BASE_CMD CMD_EN_PARITY
-#else
-	#define BASE_CMD  0
-#endif
-
-/*
-	Debugging code
-*/
-
-#define PHASE_BUS_FREE 1
-#define PHASE_ARBITRATION 2
-#define PHASE_SELECTION 4
-#define PHASE_DATAIN 8 
-#define PHASE_DATAOUT 0x10
-#define PHASE_CMDOUT 0x20
-#define PHASE_MSGIN 0x40
-#define PHASE_MSGOUT 0x80
-#define PHASE_STATUSIN 0x100
-#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN)
-#define PRINT_COMMAND 0x200
-#define PHASE_EXIT 0x400
-#define PHASE_RESELECT 0x800
-#define DEBUG_FAST 0x1000
-#define DEBUG_SG   0x2000
-#define DEBUG_LINKED	0x4000
-#define DEBUG_BORKEN	0x8000
-
-/* 
- *	Control options - these are timeouts specified in .01 seconds.
- */
-
-/* 30, 20 work */
-#define ST0X_BUS_FREE_DELAY 25
-#define ST0X_SELECTION_DELAY 25
-
-#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::)
-	
-#define SEAGATE 1	/* these determine the type of the controller */
-#define FD	2
-
-#define ST0X_ID_STR	"Seagate ST-01/ST-02"
-#define FD_ID_STR	"TMC-8XX/TMC-950"
-
-#endif
+#endif /* ASM */
 
+#endif /* _SEAGATE_H */
diff -u --recursive --new-file v2.1.31/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.1.31/linux/drivers/scsi/sr.c	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/sr.c	Thu Apr  3 10:23:56 1997
@@ -60,9 +60,10 @@
                                            sr_finish, sr_attach, sr_detach};
 
 Scsi_CD * scsi_CDs = NULL;
-static int * sr_sizes;
+static int * sr_sizes = NULL;
 
-static int * sr_blocksizes;
+static int * sr_blocksizes = NULL;
+static int * sr_hardsizes = NULL;              /* Hardware sector size */
 
 static int sr_open(struct cdrom_device_info*, int);
 void get_sectorsize(int);
@@ -121,7 +122,8 @@
 	retval = scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,
                             SCSI_IOCTL_TEST_UNIT_READY, 0);
 
-	if(retval){
+	if(retval)
+        {
                 /* Unable to test, unit probably not ready.  This usually
 		 * means there is no disc in the drive.  Mark as changed,
 		 * and we will figure it out later once the drive is
@@ -140,7 +142,18 @@
 #ifdef CONFIG_BLK_DEV_SR_VENDOR
                 sr_cd_check(cdi);
 #endif
+
+                 /* 
+                  * If the disk changed, the capacity will now be different,
+                  * so we force a re-read of this information 
+                  * Force 2048 for the sector size so that filesystems won't
+                  * be trying to use something that is too small if the disc
+                  * has changed.
+                  */
                 scsi_CDs[MINOR(cdi->dev)].needs_sector_size = 1;
+
+                scsi_CDs[MINOR(cdi->dev)].sector_size = 
+                  sr_hardsizes[MINOR(cdi->dev)] = 2048;
         }
 	return retval;
 }
@@ -894,6 +907,12 @@
 			scsi_CDs[i].capacity = 0;
 			scsi_CDs[i].needs_sector_size = 1;
 	}
+
+        /*
+         * Add this so that we have the ability to correctly gauge
+         * what the device is capable of.
+         */
+        sr_hardsizes[i] = scsi_CDs[i].sector_size;
 	scsi_CDs[i].needs_sector_size = 0;
 	sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
     };
@@ -928,8 +947,16 @@
 
     sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
 					 sizeof(int), GFP_ATOMIC);
+    sr_hardsizes =  (int *) scsi_init_malloc(sr_template.dev_max * 
+                                         sizeof(int), GFP_ATOMIC);
+
+    /*
+     * These are good guesses for the time being.
+     */
     for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
+    for(i=0;i<sr_template.dev_max;i++) sr_hardsizes[i] = 2048;
     blksize_size[MAJOR_NR] = sr_blocksizes;
+    hardsect_size[MAJOR_NR] = sr_hardsizes;
     return 0;
 }
 
@@ -1023,7 +1050,13 @@
 		       * sizeof(Scsi_CD));
 
 	scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
+        sr_sizes = NULL;
+
 	scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
+        sr_blocksizes = NULL;
+
+	scsi_init_free((char *) sr_hardsizes,  sr_template.dev_max * sizeof(int));
+        sr_hardsizes = NULL;
     }
 
     blksize_size[MAJOR_NR] = NULL;
diff -u --recursive --new-file v2.1.31/linux/fs/Config.in linux/fs/Config.in
--- v2.1.31/linux/fs/Config.in	Fri Apr  4 08:52:23 1997
+++ linux/fs/Config.in	Fri Apr  4 12:14:24 1997
@@ -18,6 +18,8 @@
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
   if [ "$CONFIG_NFS_FS" = "y" ]; then
+    define_bool CONFIG_SUNRPC y
+    define_bool CONFIG_LOCKD y
     bool '   Root file system on NFS' CONFIG_ROOT_NFS
     if [ "$CONFIG_ROOT_NFS" = "y" ]; then
       bool '      BOOTP support' CONFIG_RNFS_BOOTP
diff -u --recursive --new-file v2.1.31/linux/fs/Makefile linux/fs/Makefile
--- v2.1.31/linux/fs/Makefile	Fri Apr  4 08:52:23 1997
+++ linux/fs/Makefile	Fri Apr  4 12:11:25 1997
@@ -17,7 +17,7 @@
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext2 fat msdos vfat proc isofs nfs umsdos \
-		hpfs sysv smbfs ncpfs ufs affs romfs autofs
+		hpfs sysv smbfs ncpfs ufs affs romfs autofs lockd nfsd
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -89,6 +89,23 @@
   endif
 endif
 
+ifeq ($(CONFIG_NFSD),y)
+CONFIG_LOCKD := y
+SUB_DIRS += nfsd
+else
+  ifeq ($(CONFIG_NFSD),m)
+  MOD_SUB_DIRS += nfsd
+  endif
+endif
+
+ifeq ($(CONFIG_LOCKD),y)
+SUB_DIRS += lockd
+else
+  ifeq ($(CONFIG_LOCKD),m)
+  MOD_SUB_DIRS := lockd $(MOD_SUB_DIRS)
+  endif
+endif
+
 ifeq ($(CONFIG_UMSDOS_FS),y)
 SUB_DIRS += umsdos
 else
@@ -184,6 +201,15 @@
   M_OBJS += binfmt_java.o
   endif
 endif
+
+ifeq ($(CONFIG_BINFMT_EM86),y)
+BINFMTS += binfmt_em86.o
+else
+  ifeq ($(CONFIG_BINFMT_EM86),m)
+  M_OBJS += binfmt_em86.o
+  endif
+endif
+
 
 # binfmt_script is always there
 BINFMTS += binfmt_script.o
diff -u --recursive --new-file v2.1.31/linux/fs/autofs/dir.c linux/fs/autofs/dir.c
--- v2.1.31/linux/fs/autofs/dir.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/autofs/dir.c	Fri Apr  4 08:34:38 1997
@@ -10,7 +10,6 @@
  *
  * ------------------------------------------------------------------------- */
 
-#include <linux/modversions.h>
 #include <linux/auto_fs.h>
 
 static int autofs_dir_readdir(struct inode *inode, struct file *filp,
diff -u --recursive --new-file v2.1.31/linux/fs/autofs/dirhash.c linux/fs/autofs/dirhash.c
--- v2.1.31/linux/fs/autofs/dirhash.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/autofs/dirhash.c	Fri Apr  4 08:34:38 1997
@@ -10,7 +10,6 @@
  *
  * ------------------------------------------------------------------------- */
 
-#include <linux/modversions.h>
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/auto_fs.h>
diff -u --recursive --new-file v2.1.31/linux/fs/autofs/inode.c linux/fs/autofs/inode.c
--- v2.1.31/linux/fs/autofs/inode.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/autofs/inode.c	Fri Apr  4 08:34:38 1997
@@ -10,7 +10,6 @@
  *
  * ------------------------------------------------------------------------- */
 
-#include <linux/modversions.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/file.h>
@@ -143,6 +142,7 @@
         sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
 	if ( !sbi ) {
 		s->s_dev = 0;
+		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
 	DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
@@ -208,7 +208,7 @@
         struct statfs tmp;
 
         tmp.f_type = AUTOFS_SUPER_MAGIC;
-        tmp.f_bsize = PAGE_SIZE/sizeof(long);
+        tmp.f_bsize = 1024;
         tmp.f_blocks = 0;
         tmp.f_bfree = 0;
         tmp.f_bavail = 0;
diff -u --recursive --new-file v2.1.31/linux/fs/autofs/root.c linux/fs/autofs/root.c
--- v2.1.31/linux/fs/autofs/root.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/autofs/root.c	Fri Apr  4 08:34:38 1997
@@ -10,7 +10,6 @@
  *
  * ------------------------------------------------------------------------- */
 
-#include <linux/modversions.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <linux/malloc.h>
diff -u --recursive --new-file v2.1.31/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c
--- v2.1.31/linux/fs/autofs/symlink.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/autofs/symlink.c	Fri Apr  4 08:34:38 1997
@@ -10,7 +10,6 @@
  *
  * ------------------------------------------------------------------------- */
 
-#include <linux/modversions.h>
 #include <linux/string.h>
 #include <linux/sched.h>
 #include <linux/auto_fs.h>
diff -u --recursive --new-file v2.1.31/linux/fs/autofs/waitq.c linux/fs/autofs/waitq.c
--- v2.1.31/linux/fs/autofs/waitq.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/autofs/waitq.c	Fri Apr  4 08:34:38 1997
@@ -10,8 +10,9 @@
  *
  * ------------------------------------------------------------------------- */
 
-#include <linux/modversions.h>
 #include <linux/malloc.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
 #include <linux/auto_fs.h>
 
 /* We make this a static variable rather than a part of the superblock; it
@@ -40,6 +41,7 @@
 static int autofs_write(struct file *file, const void *addr, int bytes)
 {
 	unsigned short fs;
+	unsigned long old_signal;
 	const char *data = (const char *)addr;
 	int written;
 
@@ -49,9 +51,17 @@
 	fs = get_fs();
 	set_fs(KERNEL_DS);
 
+	old_signal = current->signal;
+
 	while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) {
 		data += written;
 		bytes -= written;
+	}
+
+	if ( written == -EPIPE && !(old_signal & (1 << (SIGPIPE-1))) ) {
+		/* Keep the currently executing process from receiving a
+		   SIGPIPE unless it was already supposed to get one */
+		current->signal &= ~(1 << (SIGPIPE-1));
 	}
 	set_fs(fs);
 
diff -u --recursive --new-file v2.1.31/linux/fs/binfmt_em86.c linux/fs/binfmt_em86.c
--- v2.1.31/linux/fs/binfmt_em86.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/binfmt_em86.c	Thu Apr  3 09:56:23 1997
@@ -0,0 +1,120 @@
+/*
+ *  linux/fs/binfmt_em86.c
+ *
+ *  Based on linux/fs/binfmt_script.c
+ *  Copyright (C) 1996  Martin von Löwis
+ *  original #!-checking implemented by tytso.
+ *
+ *  em86 changes Copyright (C) 1997  Jim Paradis
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+#include <linux/elf.h>
+
+#define EM86_INTERP	"/usr/bin/em86"
+#define EM86_I_NAME	"em86"
+
+static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+	char *cp, *interp, *i_name, *i_arg;
+	int retval;
+	struct elfhdr	elf_ex;
+
+	/* Make sure this is a Linux/Intel ELF executable... */
+	elf_ex = *((struct elfhdr *)bprm->buf);
+	
+        if (elf_ex.e_ident[0] != 0x7f ||
+            strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
+                return  -ENOEXEC;
+        }
+
+
+        /* First of all, some simple consistency checks */
+        if ((elf_ex.e_type != ET_EXEC &&
+            elf_ex.e_type != ET_DYN) ||
+           (!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) ||
+           (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
+            !bprm->inode->i_op->default_file_ops->mmap)){
+                return -ENOEXEC;
+        }
+
+	bprm->sh_bang++;	/* Well, the bang-shell is implicit... */
+	iput(bprm->inode);
+	bprm->dont_iput = 1;
+
+	/* Unlike in the script case, we don't have to do any hairy
+	 * parsing to find our interpreter... it's hardcoded!
+	 */
+	interp = EM86_INTERP;
+	i_name = EM86_I_NAME;
+	i_arg = NULL;		/* We reserve the right to add an arg later */
+
+	/*
+	 * Splice in (1) the interpreter's name for argv[0]
+	 *           (2) (optional) argument to interpreter
+	 *           (3) filename of emulated file (replace argv[0])
+	 *
+	 * This is done in reverse order, because of how the
+	 * user environment and arguments are stored.
+	 */
+	remove_arg_zero(bprm);
+	bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+	bprm->argc++;
+	if (i_arg) {
+		bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2);
+		bprm->argc++;
+	}
+	bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+	bprm->argc++;
+	if (!bprm->p) 
+		return -E2BIG;
+	/*
+	 * OK, now restart the process with the interpreter's inode.
+	 * Note that we use open_namei() as the name is now in kernel
+	 * space, and we don't need to copy it.
+	 */
+	retval = open_namei(interp, 0, 0, &bprm->inode, NULL);
+	if (retval)
+		return retval;
+	bprm->dont_iput=0;
+	retval=prepare_binprm(bprm);
+	if(retval<0)
+		return retval;
+	return search_binary_handler(bprm,regs);
+}
+
+static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+	int retval;
+	MOD_INC_USE_COUNT;
+	retval = do_load_em86(bprm,regs);
+	MOD_DEC_USE_COUNT;
+	return retval;
+}
+
+struct linux_binfmt em86_format = {
+#ifndef MODULE
+	NULL, 0, load_em86, NULL, NULL
+#else
+	NULL, &mod_use_count_, load_em86, NULL, NULL
+#endif
+};
+
+int init_em86_binfmt(void) {
+	return register_binfmt(&em86_format);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_em86_binfmt();
+}
+
+void cleanup_module( void) {
+	unregister_binfmt(&em86_format);
+}
+#endif
diff -u --recursive --new-file v2.1.31/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.31/linux/fs/buffer.c	Thu Mar 27 14:40:06 1997
+++ linux/fs/buffer.c	Fri Apr  4 08:46:15 1997
@@ -29,6 +29,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/vmalloc.h>
+#include <linux/blkdev.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -498,6 +499,31 @@
 	}
 }
 
+unsigned int get_hardblocksize(kdev_t dev)
+{
+  int blksize = 0;
+
+  /*
+   * Get the hard sector size for the given device.  If we don't know
+   * what it is, return 0.
+   */
+
+  if (hardsect_size[MAJOR(dev)] != NULL)
+    {
+      blksize = hardsect_size[MAJOR(dev)][MINOR(dev)];
+      if (blksize != 0)
+	{
+	  return blksize;
+	}
+  }
+
+  /*
+   * We don't know what the hardware sector size for this device is.
+   * Return 0 indicating that we don't know.
+   */
+  return 0;
+}
+
 void set_blocksize(kdev_t dev, int size)
 {
 	extern int *blksize_size[];
@@ -979,17 +1005,15 @@
 static inline void recover_reusable_buffer_heads(void)
 {
 	if (reuse_list) {
-		struct buffer_head *bh;
-		unsigned long flags;
+		struct buffer_head *head;
+
+		head = xchg(&reuse_list, NULL);
 	
-		save_flags(flags);
 		do {
-			cli();
-			bh = reuse_list;
-			reuse_list = bh->b_next_free;
-			restore_flags(flags);
+			struct buffer_head *bh = head;
+			head = head->b_next_free;
 			put_unused_buffer_head(bh);
-		} while (reuse_list);
+		} while (head);
 	}
 }
 
diff -u --recursive --new-file v2.1.31/linux/fs/exec.c linux/fs/exec.c
--- v2.1.31/linux/fs/exec.c	Sun Jan 26 02:10:10 1997
+++ linux/fs/exec.c	Thu Apr  3 09:56:23 1997
@@ -78,6 +78,11 @@
 #ifdef CONFIG_BINFMT_JAVA
 	init_java_binfmt();
 #endif
+
+#ifdef CONFIG_BINFMT_EM86
+	init_em86_binfmt();
+#endif
+
 	/* This cannot be configured out of the kernel */
 	init_script_binfmt();
 }
diff -u --recursive --new-file v2.1.31/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v2.1.31/linux/fs/ext2/super.c	Fri Dec 27 02:03:25 1996
+++ linux/fs/ext2/super.c	Thu Apr  3 10:23:56 1997
@@ -32,6 +32,7 @@
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/blkdev.h>
 
 static char error_buf[1024];
 
@@ -377,10 +378,26 @@
 	unsigned short resuid = EXT2_DEF_RESUID;
 	unsigned short resgid = EXT2_DEF_RESGID;
 	unsigned long logic_sb_block = 1;
+	unsigned long offset = 0;
 	kdev_t dev = sb->s_dev;
+	int blocksize = BLOCK_SIZE;
+	int hblock;
 	int db_count;
 	int i, j;
 
+	/*
+	 * See what the current blocksize for the device is, and
+	 * use that as the blocksize.  Otherwise (or if the blocksize
+	 * is smaller than the default) use the default.
+	 * This is important for devices that have a hardware
+	 * sectorsize that is larger than the default.
+	 */
+	blocksize = get_hardblocksize(dev);
+	if( blocksize == 0 || blocksize < BLOCK_SIZE )
+	  {
+	    blocksize = BLOCK_SIZE;
+	  }
+
 	sb->u.ext2_sb.s_mount_opt = 0;
 	set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
 	if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
@@ -391,8 +408,19 @@
 
 	MOD_INC_USE_COUNT;
 	lock_super (sb);
-	set_blocksize (dev, BLOCK_SIZE);
-	if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) {
+	set_blocksize (dev, blocksize);
+
+	/*
+	 * If the superblock doesn't start on a sector boundary,
+	 * calculate the offset.  FIXME(eric) this doesn't make sense
+	 * that we would have to do this.
+	 */
+	if (blocksize != BLOCK_SIZE) {
+		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
+		offset = (sb_block*BLOCK_SIZE) % blocksize;
+	}
+
+	if (!(bh = bread (dev, logic_sb_block, blocksize))) {
 		sb->s_dev = 0;
 		unlock_super (sb);
 		printk ("EXT2-fs: unable to read superblock\n");
@@ -403,7 +431,7 @@
 	 * Note: s_es must be initialized s_es as soon as possible because
 	 * some ext2 macro-instructions depend on its value
 	 */
-	es = (struct ext2_super_block *) bh->b_data;
+	es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
 	sb->u.ext2_sb.s_es = es;
 	sb->s_magic = le16_to_cpu(es->s_magic);
 	if (sb->s_magic != EXT2_SUPER_MAGIC) {
@@ -438,7 +466,17 @@
 	if (sb->s_blocksize != BLOCK_SIZE &&
 	    (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||
 	     sb->s_blocksize == 4096)) {
-		unsigned long offset;
+		/*
+		 * Make sure the blocksize for the filesystem is larger
+		 * than the hardware sectorsize for the machine.
+		 */
+		hblock = get_hardblocksize(dev);
+		if(    (hblock != 0)
+		    && (sb->s_blocksize < hblock) )
+		{
+			printk("EXT2-fs: blocksize too small for device.\n");
+			goto failed_mount;
+		}
 
 		brelse (bh);
 		set_blocksize (dev, sb->s_blocksize);
diff -u --recursive --new-file v2.1.31/linux/fs/fat/buffer.c linux/fs/fat/buffer.c
--- v2.1.31/linux/fs/fat/buffer.c	Thu May  9 21:54:52 1996
+++ linux/fs/fat/buffer.c	Thu Apr  3 10:23:56 1997
@@ -16,13 +16,18 @@
 {
 	struct buffer_head *ret = NULL;
 
-	/* Note that the blocksize is 512 or 1024, but the first read
-	   is always of size 1024. Doing readahead may be counterproductive
+	/* Note that the blocksize is 512, 1024 or 2048, but the first read
+	   is always of size 1024 (or 2048). Doing readahead may be counterproductive
 	   or just plain wrong. */
 	if (sb->s_blocksize == 512) {
 		ret = bread (sb->s_dev,block,512);
 	} else {
-		struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
+		struct buffer_head *real;
+		if (sb->s_blocksize == 1024){
+			real = bread (sb->s_dev,block>>1,1024);
+		}else{
+			real = bread (sb->s_dev,block>>2,2048);
+		}
 
 		if (real != NULL){
 			ret = (struct buffer_head *)
@@ -59,7 +64,11 @@
 				*/
 				memset (ret,0,sizeof(*ret));
 				ret->b_data = real->b_data;
-				if (block & 1) ret->b_data += 512;
+				if (sb->s_blocksize == 2048) {
+					if (block & 3) ret->b_data += (block & 3) << 9;
+				}else{
+					if (block & 1) ret->b_data += 512;
+				}
 				ret->b_next = real;
 			}else{
 				brelse (real);
diff -u --recursive --new-file v2.1.31/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.1.31/linux/fs/fat/inode.c	Mon Dec 30 06:45:36 1996
+++ linux/fs/fat/inode.c	Thu Apr  3 10:23:56 1997
@@ -172,8 +172,8 @@
 			*blksize = simple_strtoul(value,&value,0);
 			if (*value)
 				return 0;
-			if (*blksize != 512 && *blksize != 1024){
-				printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
+			if (*blksize != 512 && *blksize != 1024 && *blksize != 2048){
+				printk ("MSDOS FS: Invalid blocksize (512, 1024 or 2048)\n");
 			}
 		}
 		else if (!strcmp(this_char,"sys_immutable")) {
@@ -205,16 +205,26 @@
 		}
 	}
 	if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
-		|| (blksize != 512 && blksize != 1024)) {
+		|| (blksize != 512 && blksize != 1024 && blksize != 2048)) 
+	{
 		sb->s_dev = 0;
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
 	cache_init();
 	lock_super(sb);
-	/* The first read is always 1024 bytes */
-	sb->s_blocksize = 1024;
-	set_blocksize(sb->s_dev, 1024);
+	if( blksize > 1024 )
+	  {
+	    /* Force the superblock to a larger size here. */
+	    sb->s_blocksize = blksize;
+	    set_blocksize(sb->s_dev, blksize);
+	  }
+	else
+	  {
+	    /* The first read is always 1024 bytes */
+	    sb->s_blocksize = 1024;
+	    set_blocksize(sb->s_dev, 1024);
+	  }
 	bh = fat_bread(sb, 0);
 	unlock_super(sb);
 	if (bh == NULL || !fat_is_uptodate(sb,bh)) {
@@ -285,7 +295,7 @@
 				/* the misfit with buffer cache and cluster */
 				/* because clusters (DOS) are often aligned */
 				/* on odd sectors. */
-	sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
+	sb->s_blocksize_bits = blksize == 512 ? 9 : (blksize == 1024 ? 10 : 11);
 	if (error || debug) {
 		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
 		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
@@ -451,7 +461,7 @@
 		       !is_exec(raw_entry->ext)))
 		    	? S_IRUGO|S_IWUGO : S_IRWXUGO)
 		    & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
-		inode->i_op = (sb->s_blocksize == 1024)
+		inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048)
 			? &fat_file_inode_operations_1024
 			: &fat_file_inode_operations;
 		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
diff -u --recursive --new-file v2.1.31/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.31/linux/fs/filesystems.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/filesystems.c	Fri Apr  4 12:14:09 1997
@@ -79,6 +79,10 @@
 	init_proc_fs();
 #endif
 
+#ifdef CONFIG_LOCKD
+	nlmxdr_init();
+#endif
+
 #ifdef CONFIG_NFS_FS
 	init_nfs_fs();
 #endif
diff -u --recursive --new-file v2.1.31/linux/fs/isofs/dir.c linux/fs/isofs/dir.c
--- v2.1.31/linux/fs/isofs/dir.c	Sun Jan 26 02:07:44 1997
+++ linux/fs/isofs/dir.c	Thu Apr  3 10:23:56 1997
@@ -131,28 +131,12 @@
 		return 0;
 
 	while (filp->f_pos < inode->i_size) {
-		int de_len, next_offset;
+		int de_len;
 #ifdef DEBUG
 		printk("Block, offset, f_pos: %x %x %x\n",
 		       block, offset, filp->f_pos);
 	        printk("inode->i_size = %x\n",inode->i_size);
 #endif
-		/* Next directory_record on next CDROM sector */
-		if (offset >= bufsize) {
-#ifdef DEBUG
-		        printk("offset >= bufsize\n");
-#endif
-			brelse(bh);
-			offset = 0;
-			block = isofs_bmap(inode, (filp->f_pos) >> bufbits);
-			if (!block)
-				return 0;
-			bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size);
-			if (!bh)
-				return 0;
-			continue;
-		}
-
 		de = (struct iso_directory_record *) (bh->b_data + offset);
 		inode_number = (block << bufbits) + (offset & (bufsize - 1));
 
@@ -166,7 +150,7 @@
 		   CDROM sector.  If we are at the end of the directory, we
 		   kick out of the while loop. */
 
-		if (de_len == 0) {
+		if ((de_len == 0) || (offset == bufsize) ) {
 			brelse(bh);
 			filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1))
 				       + ISOFS_BLOCK_SIZE);
@@ -180,40 +164,18 @@
 			continue;
 		}
 
-		/* Make sure that the entire directory record is in the
-		   current bh block.
-		   If not, put the two halves together in "tmpde" */
-		next_offset = offset + de_len;
-		if (next_offset > bufsize) {
-#ifdef DEBUG
-		        printk("next_offset (%x) > bufsize (%x)\n",next_offset,bufsize);
-#endif
-			next_offset &= (bufsize - 1);
-		        memcpy(tmpde, de, bufsize - offset);
-			brelse(bh);
-			block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits);
-			if (!block)
-		        {
-				return 0;
-			}
-		  
-			bh = breada(inode->i_dev, block, bufsize, 
-				    filp->f_pos, 
-				    inode->i_size);
-			if (!bh)
-		        {
-#ifdef DEBUG
-                 		printk("!bh block=%ld, bufsize=%ld\n",block,bufsize); 
- 				printk("filp->f_pos = %ld\n",filp->f_pos);
-				printk("inode->i_size = %ld\n", inode->i_size);
-#endif
-				return 0;
-			}
-		  
-			memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset);
-			de = tmpde;
+		offset +=  de_len;
+		if (offset > bufsize) {
+			/*
+			 * This would only normally happen if we had
+			 * a buggy cdrom image.  All directory
+			 * entries should terminate with a null size
+			 * or end exactly at the end of the sector.
+			 */
+		        printk("next_offset (%x) > bufsize (%x)\n",
+			       offset,bufsize);
+			break;
 		}
-		offset = next_offset;
 
 		/* Handle the case of the '.' directory */
 		if (de->name_len[0] == 1 && de->name[0] == 0) {
diff -u --recursive --new-file v2.1.31/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.31/linux/fs/isofs/inode.c	Fri Jan  3 01:33:26 1997
+++ linux/fs/isofs/inode.c	Thu Apr  3 10:23:56 1997
@@ -86,7 +86,10 @@
 	popt->check = 's';		/* default: strict */
 	popt->conversion = 'b';		/* default: no conversion */
 	popt->blocksize = 1024;
-	popt->mode = S_IRUGO;
+	popt->mode = S_IRUGO | S_IXUGO; /* r-x for all.  The disc could
+					   be shared with DOS machines so
+					   virtually anything could be
+					   a valid executable. */
 	popt->gid = 0;
 	popt->uid = 0;
 	if (!options) return 1;
@@ -227,23 +230,22 @@
 struct super_block *isofs_read_super(struct super_block *s,void *data,
 				     int silent)
 {
-	struct buffer_head *bh=NULL;
-	int iso_blknum;
-	unsigned int blocksize_bits;
-	int high_sierra;
-	kdev_t dev = s->s_dev;
-	unsigned int vol_desc_start;
-	int orig_zonesize;
-
-	struct iso_volume_descriptor *vdp;
-	struct hs_volume_descriptor *hdp;
-
-	struct iso_primary_descriptor *pri = NULL;
-	struct hs_primary_descriptor *h_pri = NULL;
+	struct buffer_head	      * bh = NULL;
+	unsigned int			blocksize;
+	unsigned int			blocksize_bits;
+	kdev_t				dev = s->s_dev;
+	struct hs_volume_descriptor   * hdp;
+	struct hs_primary_descriptor  * h_pri = NULL;
+	int				high_sierra;
+	int				iso_blknum;
+	struct iso9660_options		opt;
+	int				orig_zonesize;
+	struct iso_primary_descriptor * pri = NULL;
+	struct iso_directory_record   * rootp;
+	struct iso_volume_descriptor  * vdp;
+	unsigned int			vol_desc_start;
 
-	struct iso_directory_record *rootp;
 
-	struct iso9660_options opt;
 
 	MOD_INC_USE_COUNT;
 
@@ -265,6 +267,23 @@
 	printk("uid = %d\n", opt.uid);
 #endif
 
+ 	/*
+ 	 * First of all, get the hardware blocksize for this device.
+ 	 * If we don't know what it is, or the hardware blocksize is
+ 	 * larger than the blocksize the user specified, then use
+ 	 * that value.
+ 	 */
+ 	blocksize = get_hardblocksize(dev);
+ 	if(    (blocksize != 0)
+ 	    && (blocksize > opt.blocksize) )
+ 	  {
+ 	    /*
+ 	     * Force the blocksize we are going to use to be the
+ 	     * hardware blocksize.
+ 	     */
+ 	    opt.blocksize = blocksize;
+ 	  }
+ 
 	blocksize_bits = 0;
 	{
 	  int i = opt.blocksize;
@@ -273,6 +292,7 @@
 	    i >>=1;
 	  }
 	}
+
 	set_blocksize(dev, opt.blocksize);
 
 	lock_super(s);
@@ -362,6 +382,24 @@
 	/* RDE: convert log zone size to bit shift */
 
 	orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
+
+	/*
+	 * If the zone size is smaller than the hardware sector size,
+	 * this is a fatal error.  This would occur if the
+	 * disc drive had sectors that were 2048 bytes, but the filesystem
+	 * had blocks that were 512 bytes (which should only very rarely
+	 * happen.
+	 */
+	if(    (blocksize != 0)
+	    && (orig_zonesize < blocksize) )
+	  {
+	      printk("Logical zone size(%ld) < hardware blocksize(%ld)\n", 
+		     orig_zonesize, blocksize);
+	      goto out;
+
+	  }
+
+
 	switch (s -> u.isofs_sb.s_log_zone_size)
 	  { case  512: s -> u.isofs_sb.s_log_zone_size =  9; break;
 	    case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
@@ -403,8 +441,20 @@
 	 * Force the blocksize to 512 for 512 byte sectors.  The file
 	 * read primitives really get it wrong in a bad way if we don't
 	 * do this.
+	 *
+	 * Note - we should never be setting the blocksize to something
+	 * less than the hardware sector size for the device.  If we
+	 * do, we would end up having to read larger buffers and split
+	 * out portions to satisfy requests.
+	 *
+	 * Note2- the idea here is that we want to deal with the optimal
+	 * zonesize in the filesystem.  If we have it set to something less,
+	 * then we have horrible problems with trying to piece together
+	 * bits of adjacent blocks in order to properly read directory
+	 * entries.  By forcing the blocksize in this way, we ensure
+	 * that we will never be required to do this.
 	 */
-	if( orig_zonesize < opt.blocksize )
+	if( orig_zonesize != opt.blocksize )
 	  {
 	    opt.blocksize = orig_zonesize;
 	    blocksize_bits = 0;
@@ -482,6 +532,34 @@
 		printk("_isofs_bmap: block<0");
 		return 0;
 	}
+
+	/*
+	 * If we are beyond the end of this file, don't give out any
+	 * blocks.
+	 */
+	if( (block << ISOFS_BUFFER_BITS(inode)) >= inode->i_size )
+	  {
+	    off_t	max_legal_read_offset;
+
+	    /*
+	     * If we are *way* beyond the end of the file, print a message.
+	     * Access beyond the end of the file up to the next page boundary
+	     * is normal, however because of the way the page cache works.
+	     * In this case, we just return 0 so that we can properly fill
+	     * the page with useless information without generating any
+	     * I/O errors.
+	     */
+	    max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) 
+	      & ~(PAGE_SIZE - 1);
+	    if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
+	      {
+
+		printk("_isofs_bmap: block>= EOF(%d, %d)", block, 
+		       inode->i_size);
+	      }
+	    return 0;
+	  }
+
 	return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
 }
 
@@ -502,7 +580,6 @@
 	struct buffer_head * bh;
 	struct iso_directory_record * raw_inode;
 	unsigned char *pnt = NULL;
-	void *cpnt = NULL;
 	int high_sierra;
 	int block;
 	int volume_seq_no ;
@@ -519,30 +596,6 @@
 	raw_inode = ((struct iso_directory_record *) pnt);
 	high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 
-	if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
-	        int frag1, offset;
-
-		offset = (inode->i_ino & (bufsize - 1));
-		frag1 = bufsize - offset;
-	        cpnt = kmalloc(*pnt,GFP_KERNEL);
-		if (cpnt == NULL) {
-			printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
-			brelse(bh);
-			goto fail;
-		}
-		memcpy(cpnt, bh->b_data + offset, frag1);
-		brelse(bh);
-		if (!(bh = bread(inode->i_dev,++block, bufsize))) {
-			kfree(cpnt);
-			printk("unable to read i-node block");
-			goto fail;
-		}
-		offset += *pnt - bufsize;
-		memcpy((char *)cpnt+frag1, bh->b_data, offset);
-		pnt = ((unsigned char *) cpnt);
-		raw_inode = ((struct iso_directory_record *) pnt);
-	}
-
 	if (raw_inode->flags[-high_sierra] & 2) {
 		inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
 		inode->i_nlink = 1; /* Set to 1.  We know there are 2, but
@@ -682,10 +735,6 @@
 	  else if (S_ISFIFO(inode->i_mode))
 	    init_fifo(inode);
 	}
-	if (cpnt) {
-		kfree (cpnt);
-		cpnt = NULL;
-	}
 	return;
       fail:
 	/* With a data error we return this information */
@@ -725,8 +774,6 @@
 	unsigned char bufbits = ISOFS_BUFFER_BITS(parent);
 	unsigned int block,offset;
 	int parent_dir, inode_number;
-	int old_offset;
-	void * cpnt = NULL;
 	int result;
 	int directory_size;
 	struct buffer_head * bh;
@@ -786,7 +833,7 @@
 		   CDROM sector.  If we are at the end of the directory, we
 		   kick out of the while loop. */
 
-		if (*((unsigned char *) de) == 0)
+ 		if ((*((unsigned char *) de) == 0) || (offset == bufsize) )
 		{
 			brelse(bh);
 			offset = 0;
@@ -813,31 +860,12 @@
 		   bh block.  If not, we malloc a buffer, and put the two
 		   halves together, so that we can cleanly read the block.  */
 
-		old_offset = offset;
 		offset += *((unsigned char *) de);
 
-		if (offset >= bufsize)
+		if (offset > bufsize)
 		{
- 		        unsigned int frag1;
- 			frag1 = bufsize - old_offset;
- 			cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL);
-			if (!cpnt) return -1;
- 			memcpy(cpnt, bh->b_data + old_offset, frag1);
- 			de = (struct iso_directory_record *) ((char *)cpnt);
-			brelse(bh);
-			offset -= bufsize;
- 			directory_size -= bufsize;
-			if(directory_size < 0)
-			{
-				printk("Directory size < 0\n");
-				return -1;
-			}
-			block++;
-			if(!(bh = bread(parent->i_dev,block,bufsize))) {
- 			        kfree(cpnt);
-				return -1;
-			}
- 			memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ 			printk("Directory overrun\n");
+ 			goto out;
 		}
 
 		if (find_rock_ridge_relocation(de, parent) == extent){
@@ -845,20 +873,12 @@
 			goto out;
 		}
 
-		if (cpnt) {
-			kfree(cpnt);
-			cpnt = NULL;
-		}
 	}
 
 	/* We go here for any condition we cannot handle.
 	   We also drop through to here at the end of the directory. */
 
  out:
-	if (cpnt) {
-	        kfree(cpnt);
-		cpnt = NULL;
-	}
 	brelse(bh);
 #ifdef DEBUG
 	printk("Resultant Inode %d\n",result);
diff -u --recursive --new-file v2.1.31/linux/fs/isofs/rock.c linux/fs/isofs/rock.c
--- v2.1.31/linux/fs/isofs/rock.c	Thu Jan  2 02:11:30 1997
+++ linux/fs/isofs/rock.c	Thu Apr  3 10:23:56 1997
@@ -203,6 +203,17 @@
 	break;
       case SIG('N','M'):
 	if (truncate) break;
+        /*
+	 * If the flags are 2 or 4, this indicates '.' or '..'.
+	 * We don't want to do anything with this, because it
+	 * screws up the code that calls us.  We don't really
+	 * care anyways, since we can just use the non-RR
+	 * name.
+	 */
+	if (rr->u.NM.flags & 6) {
+	  break;
+	}
+
 	if (rr->u.NM.flags & ~1) {
 	  printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
 	  break;
diff -u --recursive --new-file v2.1.31/linux/fs/isofs/util.c linux/fs/isofs/util.c
--- v2.1.31/linux/fs/isofs/util.c	Tue May 28 21:26:48 1996
+++ linux/fs/isofs/util.c	Thu Apr  3 10:23:56 1997
@@ -84,10 +84,16 @@
 	return (isonum_731 (p));
 }
 
-/* We have to convert from a MM/DD/YY format to the unix ctime format.  We have to
-   take into account leap years and all of that good stuff.  Unfortunately, the kernel
-   does not have the information on hand to take into account daylight savings time,
-   so there will be cases (roughly half the time) where the dates are off by one hour. */
+/* 
+ * We have to convert from a MM/DD/YY format to the unix ctime format.
+ * We have to take into account leap years and all of that good stuff.
+ * Unfortunately, the kernel does not have the information on hand to
+ * take into account daylight savings time, but it shouldn't matter.
+ * The time stored should be localtime (with or without DST in effect),
+ * and the timezone offset should hold the offset required to get back
+ * to GMT.  Thus  we should always be correct.
+ */
+
 int iso_date(char * p, int flag)
 {
 	int year, month, day, hour ,minute, second, tz;
@@ -121,9 +127,33 @@
 		if (tz & 0x80)
 			tz |= (-1 << 8);
 		
-		/* timezone offset is unreliable on some disks */
-		if (-48 <= tz && tz <= 52)
-			crtime += tz * 15 * 60;
+		/* 
+		 * The timezone offset is unreliable on some disks,
+		 * so we make a sanity check.  In no case is it ever
+		 * more than 13 hours from GMT, which is 52*15min.
+		 * The time is always stored in localtime with the
+		 * timezone offset being what get added to GMT to
+		 * get to localtime.  Thus we need to subtract the offset
+		 * to get to true GMT, which is what we store the time
+		 * as internally.  On the local system, the user may set
+		 * their timezone any way they wish, of course, so GMT
+		 * gets converted back to localtime on the receiving
+		 * system.
+		 *
+		 * NOTE: mkisofs in versions prior to mkisofs-1.10 had
+		 * the sign wrong on the timezone offset.  This has now
+		 * been corrected there too, but if you are getting screwy
+		 * results this may be the explaination.  If enough people
+		 * complain, a user configuration option could be added
+		 * to add the timezone offset in with the wrong sign
+		 * for 'compatibility' with older discs, but I cannot see how
+		 * it will matter that much.
+		 *
+		 * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann)
+		 * for pointing out the sign error.
+		 */
+		if (-52 <= tz && tz <= 52)
+			crtime -= tz * 15 * 60;
 	}
 	return crtime;
 }		
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/Makefile linux/fs/lockd/Makefile
--- v2.1.31/linux/fs/lockd/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/Makefile	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux lock manager stuff
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := lockd.o
+O_OBJS   := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
+	    svcproc.o svcsubs.o mon.o xdr.o
+OX_OBJS  := lockd_syms.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/clntlock.c linux/fs/lockd/clntlock.c
--- v2.1.31/linux/fs/lockd/clntlock.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/clntlock.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,203 @@
+/*
+ * linux/fs/lockd/clntlock.c
+ *
+ * Lock handling for the client side NLM implementation
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+
+#define NLMDBG_FACILITY		NLMDBG_CIENT
+
+/*
+ * Local function prototypes
+ */
+static int			reclaimer(void *ptr);
+
+/*
+ * The following functions handle blocking and granting from the
+ * client perspective.
+ */
+
+/*
+ * This is the representation of a blocked client lock.
+ */
+struct nlm_wait {
+	struct nlm_wait *	b_next;		/* linked list */
+	struct wait_queue *	b_wait;		/* where to wait on */
+	struct nlm_host *	b_host;
+	struct file_lock *	b_lock;		/* local file lock */
+	unsigned short		b_reclaim;	/* got to reclaim lock */
+	u32			b_status;	/* grant callback status */
+};
+
+static struct nlm_wait *	nlm_blocked = NULL;
+
+/*
+ * Block on a lock
+ */
+int
+nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
+{
+	struct nlm_wait	block, **head;
+	int		err;
+	u32		pstate;
+
+	block.b_host   = host;
+	block.b_lock   = fl;
+	block.b_wait   = NULL;
+	block.b_status = NLM_LCK_BLOCKED;
+	block.b_next   = nlm_blocked;
+	nlm_blocked    = &block;
+
+	/* Remember pseudo nsm state */
+	pstate = host->h_state;
+
+	/* Go to sleep waiting for GRANT callback. Some servers seem
+	 * to lose callbacks, however, so we're going to poll from
+	 * time to time just to make sure.
+	 *
+	 * For now, the retry frequency is pretty high; normally 
+	 * a 1 minute timeout would do. See the comment before
+	 * nlmclnt_lock for an explanation.
+	 */
+	current->timeout = jiffies + 30 * HZ;
+	interruptible_sleep_on(&block.b_wait);
+
+	for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
+		if (*head == &block) {
+			*head = block.b_next;
+			break;
+		}
+	}
+
+	if (!signalled()) {
+		*statp = block.b_status;
+		return 0;
+	}
+
+	/* Okay, we were interrupted. Cancel the pending request
+	 * unless the server has rebooted.
+	 */
+	if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
+		printk(KERN_NOTICE
+			"lockd: CANCEL call failed (errno %d)\n", -err);
+
+	return -ERESTARTSYS;
+}
+
+/*
+ * The server lockd has called us back to tell us the lock was granted
+ */
+u32
+nlmclnt_grant(struct nlm_lock *lock)
+{
+	struct nlm_wait	*block;
+
+	/*
+	 * Look up blocked request based on arguments. 
+	 * Warning: must not use cookie to match it!
+	 */
+	for (block = nlm_blocked; block; block = block->b_next) {
+		if (nlm_compare_locks(block->b_lock, &lock->fl))
+			break;
+	}
+
+	/* Ooops, no blocked request found. */
+	if (block == NULL)
+		return nlm_lck_denied;
+
+	/* Alright, we found the lock. Set the return status and
+	 * wake up the caller.
+	 */
+	block->b_status = NLM_LCK_GRANTED;
+	wake_up(&block->b_wait);
+
+	return nlm_granted;
+}
+
+/*
+ * The following procedures deal with the recovery of locks after a
+ * server crash.
+ */
+
+/*
+ * Reclaim all locks on server host. We do this by spawning a separate
+ * reclaimer thread.
+ * FIXME: should bump MOD_USE_COUNT while reclaiming
+ */
+void
+nlmclnt_recovery(struct nlm_host *host, u32 newstate)
+{
+	if (!host->h_reclaiming++) {
+		if (host->h_nsmstate == newstate)
+			return;
+		printk(KERN_WARNING
+			"lockd: Uh-oh! Interfering reclaims for host %s",
+			host->h_name);
+		host->h_monitored = 0;
+		host->h_nsmstate = newstate;
+		host->h_state++;
+		nlm_release_host(host);
+	} else {
+		host->h_monitored = 0;
+		host->h_nsmstate = newstate;
+		host->h_state++;
+		host->h_count++;
+		kernel_thread(reclaimer, host, 0);
+	}
+}
+
+static int
+reclaimer(void *ptr)
+{
+	struct nlm_host	  *host = (struct nlm_host *) ptr;
+	struct nlm_wait	  *block;
+	struct file_lock  *fl;
+	struct inode	  *inode;
+
+	/* This one ensures that our parent doesn't terminate while the
+	 * reclaim is in progress */
+	lockd_up();
+
+	/* First, reclaim all locks that have been granted previously. */
+	do {
+		for (fl = file_lock_table; fl; fl = fl->fl_next) {
+			inode = fl->fl_file->f_inode;
+			if (inode->i_sb->s_magic == NFS_SUPER_MAGIC
+			 && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr)
+			 && fl->fl_u.nfs_fl.state != host->h_state
+			 && (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) {
+				fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED;
+				nlmclnt_reclaim(host, fl);
+				break;
+			}
+		}
+	} while (fl);
+
+	host->h_reclaiming = 0;
+	wake_up(&host->h_gracewait);
+
+	/* Now, wake up all processes that sleep on a blocked lock */
+	for (block = nlm_blocked; block; block = block->b_next) {
+		if (block->b_host == host) {
+			block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
+			wake_up(&block->b_wait);
+		}
+	}
+
+	/* Release host handle after use */
+	nlm_release_host(host);
+	lockd_down();
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c
--- v2.1.31/linux/fs/lockd/clntproc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/clntproc.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,539 @@
+/*
+ * linux/fs/lockd/clntproc.c
+ *
+ * RPC procedures for the client side NLM implementation
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/nfs_fs.h>
+#include <linux/utsname.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY		NLMDBG_CLIENT
+
+static int	nlmclnt_test(struct nlm_rqst *, struct file_lock *);
+static int	nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
+static int	nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
+static void	nlmclnt_unlock_callback(struct rpc_task *);
+static void	nlmclnt_cancel_callback(struct rpc_task *);
+static int	nlm_stat_to_errno(u32 stat);
+
+/*
+ * Cookie counter for NLM requests
+ */
+static u32	nlm_cookie = 0x1234;
+
+/*
+ * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
+ */
+static inline void
+nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
+{
+	struct nlm_args	*argp = &req->a_args;
+	struct nlm_lock	*lock = &argp->lock;
+
+	memset(argp, 0, sizeof(*argp));
+	argp->cookie  = nlm_cookie++;
+	argp->state   = nsm_local_state;
+	lock->fh      = *NFS_FH(fl->fl_file->f_inode);
+	lock->caller  = system_utsname.nodename;
+	lock->oh.data = req->a_owner;
+	lock->oh.len  = sprintf(req->a_owner, "%d@%s",
+				current->pid, system_utsname.nodename);
+	lock->fl      = *fl;
+}
+
+/*
+ * Initialize arguments for GRANTED call
+ */
+int
+nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
+{
+	struct nlm_args	*argp = &call->a_args;
+	struct nlm_lock	*alock = &argp->lock;
+	void		*data = NULL;
+
+	if (lock->oh.len > NLMCLNT_OHSIZE
+	 && !(data = kmalloc(lock->oh.len, GFP_KERNEL)))
+		return 0;
+
+	argp->cookie  = nlm_cookie++;
+	argp->lock    = *lock;
+	alock->caller = system_utsname.nodename;
+	if (data)
+		alock->oh.data = (u8 *) data;
+	else
+		alock->oh.data = call->a_owner;
+	memcpy(alock->oh.data, lock->oh.data, lock->oh.len);
+	return 1;
+}
+
+void
+nlmclnt_freegrantargs(struct nlm_rqst *call)
+{
+	kfree(call->a_args.lock.caller);
+}
+
+/*
+ * This is the main entry point for the NLM client.
+ */
+int
+nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
+{
+	struct nfs_server	*nfssrv = NFS_SERVER(inode);
+	struct nlm_host		*host;
+	struct nlm_rqst		reqst, *call = &reqst;
+	unsigned long		oldmask;
+	int			status;
+
+	/* Always use NLM version 1 over UDP for now... */
+	if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), IPPROTO_UDP, 1)))
+		return -ENOLCK;
+
+	/* Create RPC client handle if not there, and copy soft
+	 * and intr flags from NFS client. */
+	if (host->h_rpcclnt == NULL) {
+		struct rpc_clnt	*clnt;
+
+		/* Bind an rpc client to this host handle (does not
+		 * perform a portmapper lookup) */
+		if (!(clnt = nlm_bind_host(host))) {
+			status = -ENOLCK;
+			goto done;
+		}
+		clnt->cl_softrtry = nfssrv->client->cl_softrtry;
+		clnt->cl_intr     = nfssrv->client->cl_intr;
+		clnt->cl_chatty   = nfssrv->client->cl_chatty;
+	}
+
+	/* Keep the old signal mask */
+	oldmask = current->blocked;
+
+	/* If we're cleaning up locks because the process is exiting,
+	 * perform the RPC call asynchronously. */
+	if (cmd == F_SETLK && fl->fl_type == F_UNLCK
+	 && (current->flags & PF_EXITING)) {
+		current->blocked = ~0UL;	/* Mask all signals */
+		call = nlmclnt_alloc_call();
+		call->a_flags = RPC_TASK_ASYNC;
+	} else {
+		call->a_flags = 0;
+	}
+	call->a_host = host;
+
+	/* Set up the argument struct */
+	nlmclnt_setlockargs(call, fl);
+
+	if (cmd == F_GETLK) {
+		status = nlmclnt_test(call, fl);
+	} else if (cmd == F_SETLK && fl->fl_type == F_UNLCK) {
+		status = nlmclnt_unlock(call, fl);
+	} else if (cmd == F_SETLK || cmd == F_SETLKW) {
+		call->a_args.block = (cmd == F_SETLKW)? 1 : 0;
+		status = nlmclnt_lock(call, fl);
+	} else {
+		status = -EINVAL;
+	}
+
+	if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
+		rpc_free(call);
+
+	current->blocked = oldmask;
+
+done:
+	dprintk("lockd: clnt proc returns %d\n", status);
+	nlm_release_host(host);
+	return status;
+}
+
+/*
+ * Wait while server is in grace period
+ */
+static inline int
+nlmclnt_grace_wait(struct nlm_host *host)
+{
+	if (!host->h_reclaiming)
+		current->timeout = 10 * HZ;
+	interruptible_sleep_on(&host->h_gracewait);
+	return signalled()? -ERESTARTSYS : 0;
+}
+
+/*
+ * Allocate an NLM RPC call struct
+ */
+struct nlm_rqst *
+nlmclnt_alloc_call(void)
+{
+	struct nlm_rqst	*call;
+
+	while (!signalled()) {
+		call = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
+					sizeof(struct nlm_rqst));
+		if (call)
+			return call;
+		current->timeout = 5 * HZ;
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+	}
+	return NULL;
+}
+
+/*
+ * Generic NLM call
+ */
+int
+nlmclnt_call(struct nlm_rqst *req, u32 proc)
+{
+	struct nlm_host	*host = req->a_host;
+	struct rpc_clnt	*clnt;
+	struct nlm_args	*argp = &req->a_args;
+	struct nlm_res	*resp = &req->a_res;
+	int		status;
+
+	dprintk("lockd: call procedure %s on %s\n",
+			nlm_procname(proc), host->h_name);
+
+	do {
+		if (host->h_reclaiming && !argp->reclaim) {
+			interruptible_sleep_on(&host->h_gracewait);
+			continue;
+		}
+
+		/* If we have no RPC client yet, create one. */
+		if ((clnt = nlm_bind_host(host)) == NULL)
+			return -ENOLCK;
+
+		/* Perform the RPC call. If an error occurs, try again */
+		if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) {
+			dprintk("lockd: rpc_call returned error %d\n", -status);
+			if (status == -ERESTARTSYS)
+				return status;
+			nlm_rebind_host(host);
+		} else
+		if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
+			dprintk("lockd: server in grace period\n");
+			if (argp->reclaim) {
+				printk(KERN_WARNING
+				     "lockd: spurious grace period reject?!\n");
+				return -ENOLCK;
+			}
+		} else {
+			dprintk("lockd: server returns status %d\n", resp->status);
+			return 0;	/* Okay, call complete */
+		}
+
+		/* Back off a little and try again */
+		current->timeout = jiffies + 15 * HZ;
+		interruptible_sleep_on(&host->h_gracewait);
+	} while (!signalled());
+
+	return -ERESTARTSYS;
+}
+
+/*
+ * Generic NLM call, async version.
+ */
+int
+nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+	struct nlm_host	*host = req->a_host;
+	struct rpc_clnt	*clnt;
+	struct nlm_args	*argp = &req->a_args;
+	struct nlm_res	*resp = &req->a_res;
+	int		status;
+
+	dprintk("lockd: call procedure %s on %s (async)\n",
+			nlm_procname(proc), host->h_name);
+
+	/* If we have no RPC client yet, create one. */
+	if ((clnt = nlm_bind_host(host)) == NULL)
+		return -ENOLCK;
+
+        /* bootstrap and kick off the async RPC call */
+        status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC,
+					callback, req);
+
+	/* If the async call is proceeding, increment host refcount */
+        if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC))
+                host->h_count++;
+	return status;
+}
+
+/*
+ * TEST for the presence of a conflicting lock
+ */
+static int
+nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
+{
+	int	status;
+
+	if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0)
+		return status;
+
+	status = req->a_res.status;
+	if (status == NLM_LCK_GRANTED) {
+		fl->fl_type = F_UNLCK;
+	} if (status == NLM_LCK_DENIED) {
+		/*
+		 * Report the conflicting lock back to the application.
+		 * FIXME: Is it OK to report the pid back as well?
+		 */
+		memcpy(fl, &req->a_res.lock.fl, sizeof(*fl));
+		/* fl->fl_pid = 0; */
+	} else {
+		return nlm_stat_to_errno(req->a_res.status);
+	}
+
+	return 0;
+}
+
+/*
+ * LOCK: Try to create a lock
+ *
+ *			Programmer Harassment Alert
+ *
+ * When given a blocking lock request in a sync RPC call, the HPUX lockd
+ * will faithfully return LCK_BLOCKED but never cares to notify us when
+ * the lock could be granted. This way, our local process could hang
+ * around forever waiting for the callback.
+ *
+ *  Solution A:	Implement busy-waiting
+ *  Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
+ *
+ * For now I am implementing solution A, because I hate the idea of
+ * re-implementing lockd for a third time in two months. The async
+ * calls shouldn't be too hard to do, however.
+ *
+ * This is one of the lovely things about standards in the NFS area:
+ * they're so soft and squishy you can't really blame HP for doing this.
+ */
+static int
+nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
+{
+	struct nlm_host	*host = req->a_host;
+	struct nlm_res	*resp = &req->a_res;
+	int		status;
+
+	if (!host->h_monitored && nsm_monitor(host) < 0) {
+		printk(KERN_NOTICE "lockd: failed to monitor %s", host->h_name);
+		return -ENOLCK;
+	}
+
+	while (1) {
+		if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
+			if (resp->status != NLM_LCK_BLOCKED)
+				break;
+			status = nlmclnt_block(host, fl, &resp->status);
+		}
+		if (status < 0)
+			return status;
+	}
+
+	if (resp->status == NLM_LCK_GRANTED) {
+		fl->fl_u.nfs_fl.state = host->h_state;
+		fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
+	}
+
+	return nlm_stat_to_errno(resp->status);
+}
+
+/*
+ * RECLAIM: Try to reclaim a lock
+ */
+int
+nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
+{
+	struct nlm_rqst reqst, *req;
+	int		status;
+
+	req = &reqst;
+	req->a_host  = host;
+	req->a_flags = 0;
+
+	/* Set up the argument struct */
+	nlmclnt_setlockargs(req, fl);
+	req->a_args.reclaim = 1;
+
+	if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
+	 && req->a_res.status == NLM_LCK_GRANTED)
+		return 0;
+
+	printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
+				"(errno %d, status %d)\n", fl->fl_pid,
+				status, req->a_res.status);
+
+	/*
+	 * FIXME: This is a serious failure. We can
+	 *
+	 *  a.	Ignore the problem
+	 *  b.	Send the owning process some signal (Linux doesn't have
+	 *	SIGLOST, though...)
+	 *  c.	Retry the operation
+	 *
+	 * Until someone comes up with a simple implementation
+	 * for b or c, I'll choose option a.
+	 */
+
+	return -ENOLCK;
+}
+
+/*
+ * UNLOCK: remove an existing lock
+ */
+static int
+nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
+{
+	struct nlm_res	*resp = &req->a_res;
+	int		status;
+
+	/* Clean the GRANTED flag now so the lock doesn't get
+	 * reclaimed while we're stuck in the unlock call. */
+	fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
+
+	if (req->a_flags & RPC_TASK_ASYNC) {
+		return nlmclnt_async_call(req, NLMPROC_UNLOCK,
+					nlmclnt_unlock_callback);
+	}
+
+	if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0)
+		return status;
+
+	if (resp->status == NLM_LCK_GRANTED)
+		return 0;
+
+	if (resp->status != NLM_LCK_DENIED_NOLOCKS)
+		printk("lockd: unexpected unlock status: %d\n", resp->status);
+
+	/* What to do now? I'm out of my depth... */
+
+	return -ENOLCK;
+}
+
+static void
+nlmclnt_unlock_callback(struct rpc_task *task)
+{
+	struct nlm_rqst	*req = (struct nlm_rqst *) task->tk_calldata;
+	int		status = req->a_res.status;
+
+	if (RPC_ASSASSINATED(task))
+		goto die;
+
+	if (task->tk_status < 0) {
+		dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
+		nlm_rebind_host(req->a_host);
+		rpc_restart_call(task);
+		return;
+	}
+	if (status != NLM_LCK_GRANTED
+	 && status != NLM_LCK_DENIED_GRACE_PERIOD) {
+		printk("lockd: unexpected unlock status: %d\n", status);
+	}
+
+die:
+	rpc_release_task(task);
+}
+
+/*
+ * Cancel a blocked lock request.
+ * We always use an async RPC call for this in order not to hang a
+ * process that has been Ctrl-C'ed.
+ */
+int
+nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
+{
+	struct nlm_rqst	*req;
+	unsigned long	oldmask = current->blocked;
+	int		status;
+
+	/* Block all signals while setting up call */
+	current->blocked = ~0UL;
+
+	do {
+		req = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
+							sizeof(*req));
+	} while (req == NULL);
+	req->a_host  = host;
+	req->a_flags = RPC_TASK_ASYNC;
+
+	nlmclnt_setlockargs(req, fl);
+
+	status = nlmclnt_async_call(req, NLMPROC_CANCEL,
+					nlmclnt_cancel_callback);
+	if (status < 0)
+		rpc_free(req);
+
+	current->blocked = oldmask;
+	return status;
+}
+
+static void
+nlmclnt_cancel_callback(struct rpc_task *task)
+{
+	struct nlm_rqst	*req = (struct nlm_rqst *) task->tk_calldata;
+
+	if (RPC_ASSASSINATED(task))
+		goto die;
+
+	if (task->tk_status < 0) {
+		dprintk("lockd: CANCEL call error %d, retrying.\n",
+					task->tk_status);
+		goto retry_cancel;
+	}
+
+	dprintk("lockd: cancel status %d (task %d)\n",
+			req->a_res.status, task->tk_pid);
+
+	switch (req->a_res.status) {
+	case NLM_LCK_GRANTED:
+	case NLM_LCK_DENIED_GRACE_PERIOD:
+		/* Everything's good */
+		break;
+	case NLM_LCK_DENIED_NOLOCKS:
+		dprintk("lockd: CANCEL failed (server has no locks)");
+		goto retry_cancel;
+	default:
+		printk(KERN_NOTICE "lockd: weird return %d for CANCEL call",
+			req->a_res.status);
+	}
+
+die:
+	rpc_release_task(task);
+	nlm_release_host(req->a_host);
+	kfree(req);
+	return;
+
+retry_cancel:
+	nlm_rebind_host(req->a_host);
+	rpc_restart_call(task);
+	rpc_delay(task, 30 * HZ);
+	return;
+}
+
+/*
+ * Convert an NLM status code to a generic kernel errno
+ */
+static int
+nlm_stat_to_errno(u32 status)
+{
+	switch(status) {
+	case NLM_LCK_GRANTED:
+		return 0;
+	case NLM_LCK_DENIED:
+		return -EAGAIN;
+	case NLM_LCK_DENIED_NOLOCKS:
+	case NLM_LCK_DENIED_GRACE_PERIOD:
+		return -ENOLCK;
+	case NLM_LCK_BLOCKED:
+		printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
+		return -ENOLCK;
+	}
+	printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
+	return -ENOLCK;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/host.c linux/fs/lockd/host.c
--- v2.1.31/linux/fs/lockd/host.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/host.c	Fri Apr  4 12:23:40 1997
@@ -0,0 +1,323 @@
+/*
+ * linux/fs/lockd/host.c
+ *
+ * Management for NLM peer hosts. The nlm_host struct is shared
+ * between client and server implementation. The only reason to
+ * do so is to reduce code bloat.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_HOSTCACHE
+#define NLM_HOST_MAX		64
+#define NLM_HOST_NRHASH		32
+#define NLM_ADDRHASH(addr)	(ntohl(addr) & (NLM_HOST_NRHASH-1))
+#define NLM_PTRHASH(ptr)	((((u32) ptr) / 32) & (NLM_HOST_NRHASH-1))
+#define NLM_HOST_REBIND		(60 * HZ)
+#define NLM_HOST_EXPIRE		((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
+#define NLM_HOST_COLLECT	((nrhosts > NLM_HOST_MAX)? 120 * HZ :  60 * HZ)
+#define NLM_HOST_ADDR(sv)	(&(sv)->s_nlmclnt->cl_xprt->addr)
+
+static struct nlm_host *	nlm_hosts[NLM_HOST_NRHASH];
+static unsigned long		next_gc = 0;
+static int			nrhosts = 0;
+static struct semaphore		nlm_host_sema = MUTEX;
+
+
+static void			nlm_gc_hosts(void);
+
+/*
+ * Find an NLM server handle in the cache. If there is none, create it.
+ */
+struct nlm_host *
+nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version)
+{
+	return nlm_lookup_host(NULL, sin, proto, version);
+}
+
+/*
+ * Find an NLM client handle in the cache. If there is none, create it.
+ */
+struct nlm_host *
+nlmsvc_lookup_host(struct svc_rqst *rqstp)
+{
+	return nlm_lookup_host(rqstp->rq_client, &rqstp->rq_addr, 0, 0);
+}
+
+/*
+ * Match the given host against client/address
+ */
+static inline int
+nlm_match_host(struct nlm_host *host, struct svc_client *clnt,
+					struct sockaddr_in *sin)
+{
+	if (clnt)
+		return host->h_exportent == clnt;
+	return nlm_cmp_addr(&host->h_addr, sin);
+}
+
+/*
+ * Common host lookup routine for server & client
+ */
+struct nlm_host *
+nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin,
+					int proto, int version)
+{
+	struct nlm_host	*host, **hp;
+	u32		addr;
+	int		hash;
+
+	if (!clnt && !sin) {
+		printk(KERN_NOTICE "lockd: no clnt or addr in lookup_host!\n");
+		return NULL;
+	}
+
+	dprintk("lockd: nlm_lookup_host(%08lx, p=%d, v=%d)\n",
+			sin? ntohl(sin->sin_addr.s_addr) : 0, proto, version);
+
+	if (clnt)
+		hash = NLM_PTRHASH(clnt);
+	else
+		hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
+
+	/* Lock hash table */
+	down(&nlm_host_sema);
+
+	if (next_gc < jiffies)
+		nlm_gc_hosts();
+
+	for (hp = &nlm_hosts[hash]; (host = *hp); hp = &host->h_next) {
+		if (host->h_version != version || host->h_proto != proto)
+			continue;
+
+		if (nlm_match_host(host, clnt, sin)) {
+			if (hp != nlm_hosts + hash) {
+				*hp = host->h_next;
+				host->h_next = nlm_hosts[hash];
+				nlm_hosts[hash] = host;
+			}
+			host->h_expires = jiffies + NLM_HOST_EXPIRE;
+			host->h_count++;
+			up(&nlm_host_sema);
+			return host;
+		}
+	}
+
+	/* special hack for nlmsvc_invalidate_client */
+	if (sin == NULL)
+		goto nohost;
+
+	/* Ooops, no host found, create it */
+	dprintk("lockd: creating host entry\n");
+
+	if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL)))
+		goto nohost;
+	memset(host, 0, sizeof(*host));
+
+	addr = sin->sin_addr.s_addr;
+	sprintf(host->h_name, "%d.%d.%d.%d",
+			(unsigned char) (ntohl(addr) >> 24),
+			(unsigned char) (ntohl(addr) >> 16),
+			(unsigned char) (ntohl(addr) >>  8),
+			(unsigned char) (ntohl(addr) >>  0));
+
+	host->h_addr       = *sin;
+	host->h_addr.sin_port = 0;	/* ouch! */
+	host->h_version    = version;
+	host->h_proto      = proto;
+	host->h_authflavor = RPC_AUTH_NULL;
+	host->h_rpcclnt    = NULL;
+	host->h_sema	   = MUTEX;
+	host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+	host->h_expires    = jiffies + NLM_HOST_EXPIRE;
+	host->h_count      = 1;
+	host->h_state      = 0;			/* pseudo NSM state */
+	host->h_nsmstate   = 0;			/* real NSM state */
+	host->h_exportent  = clnt;
+
+	host->h_next       = nlm_hosts[hash];
+	nlm_hosts[hash]    = host;
+
+	if (++nrhosts > NLM_HOST_MAX)
+		next_gc = 0;
+
+nohost:
+	up(&nlm_host_sema);
+	return host;
+}
+
+/*
+ * Create the NLM RPC client for an NLM peer
+ */
+struct rpc_clnt *
+nlm_bind_host(struct nlm_host *host)
+{
+	struct rpc_clnt	*clnt;
+	struct rpc_xprt	*xprt;
+
+	dprintk("lockd: nlm_bind_host(%08lx)\n",
+			ntohl(host->h_addr.sin_addr.s_addr));
+
+	/* Lock host handle */
+	down(&host->h_sema);
+
+	/* If we've already created an RPC client, check whether
+	 * RPC rebind is required */
+	if ((clnt = host->h_rpcclnt) != NULL) {
+		if (host->h_nextrebind < jiffies) {
+			clnt->cl_port = 0;
+			host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+			dprintk("lockd: next rebind in %ld jiffies\n",
+					host->h_nextrebind - jiffies);
+		}
+	} else {
+		uid_t		saved_euid = current->euid;
+
+		/* Create RPC socket as root user so we get a priv port */
+		current->euid = 0;
+		xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL);
+		current->euid = saved_euid;
+		if (xprt == NULL)
+			goto forgetit;
+
+		xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
+
+		clnt = rpc_create_client(xprt, host->h_name, &nlm_program,
+					host->h_version, host->h_authflavor);
+		if (clnt == NULL) {
+			xprt_destroy(xprt);
+			goto forgetit;
+		}
+		clnt->cl_autobind = 1;	/* turn on pmap queries */
+		xprt->nocong = 1;	/* No congestion control for NLM */
+
+		host->h_rpcclnt = clnt;
+	}
+
+	up(&host->h_sema);
+	return clnt;
+
+forgetit:
+	printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
+	up(&host->h_sema);
+	return NULL;
+}
+
+/*
+ * Force a portmap lookup of the remote lockd port
+ */
+void
+nlm_rebind_host(struct nlm_host *host)
+{
+	dprintk("lockd: rebind host %s\n", host->h_name);
+	if (host->h_rpcclnt && host->h_nextrebind < jiffies) {
+		host->h_rpcclnt->cl_port = 0;
+		host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+	}
+}
+
+/*
+ * Release NLM host after use
+ */
+void
+nlm_release_host(struct nlm_host *host)
+{
+	dprintk("lockd: release host %s\n", host->h_name);
+	host->h_count -= 1;
+}
+
+/*
+ * Shut down the hosts module.
+ * Note that this routine is called only at server shutdown time.
+ */
+void
+nlm_shutdown_hosts(void)
+{
+	struct nlm_host	*host;
+	int		i;
+
+	dprintk("lockd: shutting down host module\n");
+	down(&nlm_host_sema);
+
+	/* First, make all hosts eligible for gc */
+	dprintk("lockd: nuking all hosts...\n");
+	for (i = 0; i < NLM_HOST_NRHASH; i++) {
+		for (host = nlm_hosts[i]; host; host = host->h_next)
+			host->h_expires = 0;
+	}
+
+	/* Then, perform a garbage collection pass */
+	nlm_gc_hosts();
+	up(&nlm_host_sema);
+
+	/* complain if any hosts are left */
+	if (nrhosts) {
+		printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
+		dprintk("lockd: %d hosts left:\n", nrhosts);
+		for (i = 0; i < NLM_HOST_NRHASH; i++) {
+			for (host = nlm_hosts[i]; host; host = host->h_next) {
+				dprintk("       %s (cnt %d use %d exp %ld)\n",
+					host->h_name, host->h_count,
+					host->h_inuse, host->h_expires);
+			}
+		}
+	}
+}
+
+/*
+ * Garbage collect any unused NLM hosts.
+ * This GC combines reference counting for async operations with
+ * mark & sweep for resources held by remote clients.
+ */
+static void
+nlm_gc_hosts(void)
+{
+	struct nlm_host	**q, *host;
+	struct rpc_clnt	*clnt;
+	int		i;
+
+	dprintk("lockd: host garbage collection\n");
+	for (i = 0; i < NLM_HOST_NRHASH; i++) {
+		for (host = nlm_hosts[i]; host; host = host->h_next)
+			host->h_inuse = 0;
+	}
+
+	/* Mark all hosts that hold locks, blocks or shares */
+	nlmsvc_mark_resources();
+
+	for (i = 0; i < NLM_HOST_NRHASH; i++) {
+		q = &nlm_hosts[i];
+		while ((host = *q) != NULL) {
+			if (host->h_count || host->h_inuse
+			 || host->h_expires >= jiffies) {
+				q = &host->h_next;
+				continue;
+			}
+			dprintk("lockd: delete host %s\n", host->h_name);
+			*q = host->h_next;
+			if ((clnt = host->h_rpcclnt) != NULL) {
+				if (clnt->cl_users) {
+					printk(KERN_WARNING
+						"lockd: active RPC handle\n");
+					clnt->cl_dead = 1;
+				} else {
+					rpc_destroy_client(host->h_rpcclnt);
+				}
+			}
+			kfree(host);
+			nrhosts--;
+		}
+	}
+
+	next_gc = jiffies + NLM_HOST_COLLECT;
+}
+
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/lockd_syms.c linux/fs/lockd/lockd_syms.c
--- v2.1.31/linux/fs/lockd/lockd_syms.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/lockd_syms.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,42 @@
+/*
+ * linux/fs/lockd/lockd_syms.c
+ *
+ * Symbols exported by the lockd module.
+ *
+ * Authors:	Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MODULES
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/uio.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+
+/* Start/stop the daemon */
+EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL(lockd_down);
+
+/* NFS client entry */
+EXPORT_SYMBOL(nlmclnt_proc);
+
+/* NFS server entry points/hooks */
+EXPORT_SYMBOL(nlmsvc_invalidate_client);
+EXPORT_SYMBOL(nlmsvc_ops);
+
+/* Configuration at insmod time */
+EXPORT_SYMBOL(nlmsvc_grace_period);
+EXPORT_SYMBOL(nlmsvc_timeout);
+
+#endif /* CONFIG_MODULES */
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/mon.c linux/fs/lockd/mon.c
--- v2.1.31/linux/fs/lockd/mon.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/mon.c	Fri Apr  4 12:21:59 1997
@@ -0,0 +1,228 @@
+/*
+ * linux/fs/lockd/mon.c
+ *
+ * The kernel statd client.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/kernel.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_MONITOR
+
+static struct rpc_clnt *	nsm_create(void);
+
+extern struct rpc_program	nsm_program;
+
+/*
+ * Local NSM state
+ */
+u32				nsm_local_state = 0;
+
+/*
+ * Common procedure for SM_MON/SM_UNMON calls
+ */
+static int
+nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc)
+{
+	struct rpc_clnt	*clnt;
+	struct nsm_args	args;
+	struct nsm_res	res;
+	int		status;
+
+	dprintk("lockd: nsm_%s(%s)\n", what, host->h_name);
+	if (!(clnt = nsm_create()))
+		return -EACCES;
+
+	args.addr = host->h_addr.sin_addr.s_addr;
+	args.prog = NLM_PROGRAM;
+	args.vers = 1;
+	args.proc = NLMPROC_NSM_NOTIFY;
+
+	if ((status = rpc_call(clnt, proc, &args, &res, 0)) < 0)
+		return status;
+
+	if (res.status != 0) {
+		printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name);
+		return -EACCES;
+	}
+
+	nsm_local_state = res.state;
+	return 0;
+}
+
+/*
+ * Set up monitoring of a remote host
+ */
+int
+nsm_monitor(struct nlm_host *host)
+{
+	int		status;
+
+	if ((status = nsm_mon_unmon(host, "monitor", SM_MON)) >= 0)
+		host->h_monitored = 1;
+	return status;
+}
+
+/*
+ * Cease to monitor remote host
+ */
+int
+nsm_unmonitor(struct nlm_host *host)
+{
+	int		status;
+
+	if ((status = nsm_mon_unmon(host, "unmonitor", SM_UNMON)) >= 0)
+		host->h_monitored = 0;
+	return status;
+}
+
+/*
+ * Create NSM client for the local host
+ */
+static struct rpc_clnt *
+nsm_create(void)
+{
+	struct sockaddr_in	sin;
+	struct rpc_xprt		*xprt;
+	struct rpc_clnt		*clnt;
+
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	sin.sin_port = 0;
+
+	if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL)))
+		return NULL;
+
+	clnt = rpc_create_client(xprt, "localhost",
+				&nsm_program, SM_VERSION,
+				RPC_AUTH_NULL);
+	if (!clnt) {
+		xprt_destroy(xprt);
+	} else {
+		clnt->cl_softrtry = 1;
+		clnt->cl_chatty   = 1;
+		clnt->cl_oneshot  = 1;
+	}
+	return clnt;
+}
+
+/*
+ * XDR functions for NSM.
+ */
+static int
+xdr_error(struct rpc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return -EACCES;
+}
+
+static int
+xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp)
+{
+	char	buffer[20];
+	u32	addr = ntohl(argp->addr);
+
+	dprintk("nsm: xdr_encode_mon(%08lx, %ld, %ld, %ld)\n",
+			htonl(argp->addr), htonl(argp->proc),
+			htonl(argp->vers), htonl(argp->proc));
+
+	/*
+	 * Use the dotted-quad IP address of the remote host as
+	 * identifier. Linux statd always looks up the canonical
+	 * hostname first for whatever remote hostname it receives,
+	 * so this works alright.
+	 */
+	sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
+				 	(addr>>8) & 0xff,  (addr) & 0xff);
+	if (!(p = xdr_encode_string(p, buffer))
+	 || !(p = xdr_encode_string(p, system_utsname.nodename)))
+		return -EIO;
+	*p++ = htonl(argp->prog);
+	*p++ = htonl(argp->vers);
+	*p++ = htonl(argp->proc);
+
+	/* This is the private part. Needed only for SM_MON call */
+	if (rqstp->rq_task->tk_proc == SM_MON) {
+		*p++ = argp->addr;
+		*p++ = 0;
+		*p++ = 0;
+		*p++ = 0;
+	}
+
+	rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
+	return 0;
+}
+
+static int
+xdr_decode_stat_res(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
+{
+	resp->status = ntohl(*p++);
+	resp->state = ntohl(*p++);
+	dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
+			resp->status, resp->state);
+	return 0;
+}
+
+static int
+xdr_decode_stat(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
+{
+	resp->status = ntohl(*p++);
+	return 0;
+}
+
+#define SM_my_name_sz	(1+XDR_QUADLEN(SM_MAXSTRLEN))
+#define SM_my_id_sz	(3+1+SM_my_name_sz)
+#define SM_mon_id_sz	(1+XDR_QUADLEN(20)+SM_my_id_sz)
+#define SM_mon_sz	(SM_mon_id_sz+4)
+
+static struct rpc_procinfo	nsm_procedures[] = {
+        { "sm_null",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+        { "sm_stat",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+        { "sm_mon",
+		(kxdrproc_t) xdr_encode_mon,
+		(kxdrproc_t) xdr_decode_stat_res, SM_mon_sz, 2 },
+        { "sm_unmon",
+		(kxdrproc_t) xdr_encode_mon,
+		(kxdrproc_t) xdr_decode_stat, SM_mon_id_sz, 1 },
+        { "sm_unmon_all",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+        { "sm_simu_crash",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+        { "sm_notify",
+		(kxdrproc_t) xdr_error,
+		(kxdrproc_t) xdr_error, 0, 0 },
+};
+
+static struct rpc_version	nsm_version1 = {
+	1, 
+	sizeof(nsm_procedures)/sizeof(nsm_procedures[0]),
+	nsm_procedures
+};
+
+static struct rpc_version *	nsm_version[] = {
+	NULL,
+	&nsm_version1,
+};
+
+static struct rpc_stat		nsm_stats;
+
+struct rpc_program		nsm_program = {
+	"statd",
+	SM_PROGRAM,
+	sizeof(nsm_version)/sizeof(nsm_version[0]),
+	nsm_version,
+	&nsm_stats
+};
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svc.c linux/fs/lockd/svc.c
--- v2.1.31/linux/fs/lockd/svc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/svc.c	Fri Apr  4 12:47:07 1997
@@ -0,0 +1,284 @@
+/*
+ * linux/fs/lockd/svc.c
+ *
+ * This is the central lockd service.
+ *
+ * FIXME: Separate the lockd NFS server functionality from the lockd NFS
+ * 	  client functionality. Oh why didn't Sun create two separate
+ *	  services in the first place?
+ *
+ * Authors:	Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/nfs.h>
+#include <linux/in.h>
+#include <linux/uio.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/lockd/lockd.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_SVC
+#define LOCKD_BUFSIZE		(1024 + NLMSSVC_XDRSIZE)
+#define BLOCKABLE_SIGS		(~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _S(sig)			(1 << ((sig) - 1))
+
+extern struct svc_program	nlmsvc_program;
+struct nlmsvc_binding *		nlmsvc_ops = NULL;
+static int			nlmsvc_sema = 0;
+static int			nlmsvc_pid = 0;
+unsigned long			nlmsvc_grace_period = 0;
+unsigned long			nlmsvc_timeout = 0;
+
+/*
+ * Currently the following can be set only at insmod time.
+ * Ideally, they would be accessible through the sysctl interface.
+ */
+unsigned long			nlm_grace_period = 0;
+unsigned long			nlm_timeout = LOCKD_DFLT_TIMEO;
+
+/*
+ * This is the lockd kernel thread
+ */
+static void
+lockd(struct svc_rqst *rqstp)
+{
+	struct svc_serv	*serv = rqstp->rq_server;
+	sigset_t	oldsigmask;
+	int		err = 0;
+
+	lock_kernel();
+	/* Lock module and set up kernel thread */
+	MOD_INC_USE_COUNT;
+	/* exit_files(current); */
+	exit_mm(current);
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "lockd");
+
+	/* kick rpciod */
+	rpciod_up();
+
+	dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
+
+	if (!nlm_timeout)
+		nlm_timeout = LOCKD_DFLT_TIMEO;
+
+#ifdef RPC_DEBUG
+	nlmsvc_grace_period = 10 * HZ;
+#else
+	if (nlm_grace_period) {
+		nlmsvc_grace_period += (1 + nlm_grace_period / nlm_timeout)
+						* nlm_timeout * HZ;
+	} else {
+		nlmsvc_grace_period += 5 * nlm_timeout * HZ;
+	}
+#endif
+
+	nlmsvc_grace_period += jiffies;
+	nlmsvc_timeout = nlm_timeout * HZ;
+	nlmsvc_pid = current->pid;
+
+	/*
+	 * The main request loop. We don't terminate until the last
+	 * NFS mount or NFS daemon has gone away, and we've been sent a
+	 * signal.
+	 */
+	while (nlmsvc_sema || !signalled()) {
+		if (signalled())
+			current->signal = 0;
+
+		/*
+		 * Retry any blocked locks that have been notified by
+		 * the VFS. Don't do this during grace period.
+		 * (Theoretically, there shouldn't even be blocked locks
+		 * during grace period).
+		 */
+		if (!nlmsvc_grace_period) {
+			current->timeout = nlmsvc_retry_blocked();
+		} else if (nlmsvc_grace_period < jiffies)
+			nlmsvc_grace_period = 0;
+
+		/*
+		 * Find a socket with data available and call its
+		 * recvfrom routine.
+		 */
+		if ((err = svc_recv(serv, rqstp)) == -EAGAIN)
+			continue;
+		if (err < 0) {
+			if (err != -EINTR)
+				printk(KERN_WARNING
+					"lockd: terminating on error %d\n",
+					-err);
+			break;
+		}
+
+		dprintk("lockd: request from %08lx\n",
+				ntohl(rqstp->rq_addr.sin_addr.s_addr));
+
+		/*
+		 * Look up the NFS client handle. The handle is needed for
+		 * all but the GRANTED callback RPCs.
+		 */
+		if (nlmsvc_ops) {
+			nlmsvc_ops->exp_readlock();
+			rqstp->rq_client =
+				nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
+		} else {
+			rqstp->rq_client = NULL;
+		}
+
+		/* Process request with all signals blocked.  */
+		oldsigmask = current->blocked;
+		current->blocked = BLOCKABLE_SIGS;
+		svc_process(serv, rqstp);
+		current->blocked = oldsigmask;
+
+		/* Unlock export hash tables */
+		if (nlmsvc_ops)
+			nlmsvc_ops->exp_unlock();
+	}
+
+	nlm_shutdown_hosts();
+
+	/* Exit the RPC thread */
+	svc_exit_thread(rqstp);
+
+	/* release rpciod */
+	rpciod_down();
+
+	/* Release module */
+	MOD_DEC_USE_COUNT;
+	nlmsvc_pid = 0;
+}
+
+/*
+ * Make a socket for lockd
+ * FIXME: Move this to net/sunrpc/svc.c so that we can share this with nfsd.
+ */
+static int
+lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
+{
+	struct sockaddr_in	sin;
+
+	dprintk("lockd: creating socket proto = %d\n", protocol);
+	sin.sin_family      = AF_INET;
+	sin.sin_addr.s_addr = INADDR_ANY;
+	sin.sin_port        = htons(port);
+	return svc_create_socket(serv, protocol, &sin);
+}
+
+int
+lockd_up(void)
+{
+	struct svc_serv *	serv;
+	int			error;
+
+	if (nlmsvc_pid || nlmsvc_sema++)
+		return 0;
+
+	dprintk("lockd: creating service\n");
+	if ((serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE)) == NULL)
+		return -ENOMEM;
+
+	if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0
+	 || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) {
+		svc_destroy(serv);
+		return error;
+	}
+
+	if ((error = svc_create_thread(lockd, serv)) < 0)
+		nlmsvc_sema--;
+
+	/* Release server */
+	svc_destroy(serv);
+	return 0;
+}
+
+void
+lockd_down(void)
+{
+	if (!nlmsvc_pid || --nlmsvc_sema > 0)
+		return;
+
+	kill_proc(nlmsvc_pid, SIGKILL, 1);
+	nlmsvc_sema = 0;
+	nlmsvc_pid = 0;
+}
+
+#ifdef MODULE
+/* New module support in 2.1.18 */
+#if LINUX_VERSION_CODE >= 0x020112
+  EXPORT_NO_SYMBOLS;
+  MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+  MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
+  MODULE_PARM(nlm_grace_period, "10-240l");
+  MODULE_PARM(nlm_timeout, "3-20l");
+#endif
+int
+init_module(void)
+{
+	nlmxdr_init();
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	/* FIXME: delete all NLM clients */
+	nlm_shutdown_hosts();
+}
+#endif
+
+/*
+ * Define NLM program and procedures
+ */
+static struct svc_version	nlmsvc_version1 = {
+	1, 16, nlmsvc_procedures, NULL
+};
+static struct svc_version	nlmsvc_version3 = {
+	3, 24, nlmsvc_procedures, NULL
+};
+#ifdef CONFIG_NFSD_NFS3
+static struct svc_version	nlmsvc_version4 = {
+	4, 24, nlmsvc_procedures4, NULL
+};
+#endif
+static struct svc_version *	nlmsvc_version[] = {
+	NULL,
+	&nlmsvc_version1,
+	NULL,
+	&nlmsvc_version3,
+#ifdef CONFIG_NFSD_NFS3
+	&nlmsvc_version4,
+#endif
+};
+
+static struct svc_stat		nlmsvc_stats;
+
+#define NLM_NRVERS	(sizeof(nlmsvc_version)/sizeof(nlmsvc_version[0]))
+struct svc_program		nlmsvc_program = {
+	NLM_PROGRAM,		/* program number */
+	1, NLM_NRVERS-1,	/* version range */
+	NLM_NRVERS,		/* number of entries in nlmsvc_version */
+	nlmsvc_version,		/* version table */
+	"lockd",		/* service name */
+	&nlmsvc_stats,		/* stats table */
+};
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svclock.c linux/fs/lockd/svclock.c
--- v2.1.31/linux/fs/lockd/svclock.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/svclock.c	Fri Apr  4 12:21:03 1997
@@ -0,0 +1,620 @@
+/*
+ * linux/fs/lockd/svclock.c
+ *
+ * Handling of server-side locks, mostly of the blocked variety.
+ * This is the ugliest part of lockd because we tread on very thin ice.
+ * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
+ * IMNSHO introducing the grant callback into the NLM protocol was one
+ * of the worst ideas Sun ever had. Except maybe for the idea of doing
+ * NFS file locking at all.
+ *
+ * I'm trying hard to avoid race conditions by protecting most accesses
+ * to a file's list of blocked locks through a semaphore. The global
+ * list of blocked locks is not protected in this fashion however.
+ * Therefore, some functions (such as the RPC callback for the async grant
+ * call) move blocked locks towards the head of the list *while some other
+ * process might be traversing it*. This should not be a problem in
+ * practice, because this will only cause functions traversing the list
+ * to visit some blocks twice.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/nlm.h>
+#include <linux/lockd/lockd.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_SVCLOCK
+
+static void	nlmsvc_insert_block(struct nlm_block *block, unsigned long);
+static int	nlmsvc_remove_block(struct nlm_block *block);
+static void	nlmsvc_grant_callback(struct rpc_task *task);
+static void	nlmsvc_notify_blocked(struct file_lock *);
+
+/*
+ * The list of blocked locks to retry
+ */
+static struct nlm_block *	nlm_blocked = NULL;
+
+/*
+ * Insert a blocked lock into the global list
+ */
+static void
+nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+{
+	struct nlm_block **bp, *b;
+
+	dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
+	if (block->b_queued)
+		nlmsvc_remove_block(block);
+	for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next)
+		if (when < b->b_when)
+			break;
+
+	block->b_queued = 1;
+	block->b_when = when;
+	block->b_next = b;
+	*bp = block;
+}
+
+/*
+ * Remove a block from the global list
+ */
+static int
+nlmsvc_remove_block(struct nlm_block *block)
+{
+	struct nlm_block **bp, *b;
+
+	if (!block->b_queued)
+		return 1;
+	for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next) {
+		if (b == block) {
+			*bp = block->b_next;
+			block->b_queued = 0;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Find a block for a given lock and optionally remove it from
+ * the list.
+ */
+static struct nlm_block *
+nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove)
+{
+	struct nlm_block	**head, *block;
+	struct file_lock	*fl;
+
+	dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %ld-%ld ty=%d\n",
+				file, lock->fl.fl_pid, lock->fl.fl_start,
+				lock->fl.fl_end, lock->fl.fl_type);
+	for (head = &nlm_blocked; (block = *head); head = &block->b_next) {
+		fl = &block->b_call.a_args.lock.fl;
+		dprintk("       check f=%p pd=%d %ld-%ld ty=%d\n",
+				block->b_file, fl->fl_pid, fl->fl_start,
+				fl->fl_end, fl->fl_type);
+		if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
+			if (remove)
+				*head = block->b_next;
+			return block;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Find a block with a given NLM cookie.
+ */
+static inline struct nlm_block *
+nlmsvc_find_block(u32 cookie)
+{
+	struct nlm_block *block;
+
+	for (block = nlm_blocked; block; block = block->b_next) {
+		if (block->b_call.a_args.cookie == cookie)
+			break;
+	}
+
+	return block;
+}
+
+/*
+ * Create a block and initialize it.
+ *
+ * Note: we explicitly set the cookie of the grant reply to that of
+ * the blocked lock request. The spec explicitly mentions that the client
+ * should _not_ rely on the callback containing the same cookie as the
+ * request, but (as I found out later) that's because some implementations
+ * do just this. Never mind the standards comittees, they support our
+ * logging industries.
+ */
+static inline struct nlm_block *
+nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
+				struct nlm_lock *lock, u32 cookie)
+{
+	struct nlm_block	*block;
+	struct nlm_host		*host;
+	struct nlm_rqst		*call;
+
+	/* Create host handle for callback */
+	host = nlmclnt_lookup_host(&rqstp->rq_addr,
+				rqstp->rq_prot, rqstp->rq_vers);
+	if (host == NULL)
+		return NULL;
+
+	/* Allocate memory for block, and initialize arguments */
+	if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
+		goto failed;
+	memset(block, 0, sizeof(*block));
+
+	/* Set notifier function for VFS, and init args */
+	lock->fl.fl_notify = nlmsvc_notify_blocked;
+	if (!nlmclnt_setgrantargs(&block->b_call, lock)) {
+		kfree(block);
+		goto failed;
+	}
+	block->b_call.a_args.cookie = cookie;	/* see above */
+
+	dprintk("lockd: created block %p...\n", block);
+
+	/* Create and initialize the block */
+	block->b_daemon = rqstp->rq_server;
+	block->b_host   = host;
+	block->b_file   = file;
+
+	/* Add to file's list of blocks */
+	block->b_fnext  = file->f_blocks;
+	file->f_blocks  = block;
+
+	/* Set up RPC arguments for callback */
+	call = &block->b_call;
+	call->a_host    = host;
+	call->a_flags   = RPC_TASK_ASYNC;
+
+	return block;
+
+failed:
+	nlm_release_host(host);
+	return NULL;
+}
+
+/*
+ * Delete a block. If the lock was cancelled or the grant callback
+ * failed, unlock is set to 1.
+ * It is the caller's responsibility to check whether the file
+ * can be closed hereafter.
+ */
+static void
+nlmsvc_delete_block(struct nlm_block *block, int unlock)
+{
+	struct file_lock	*fl = &block->b_call.a_args.lock.fl;
+	struct nlm_file		*file = block->b_file;
+	struct nlm_block	**bp;
+
+	dprintk("lockd: deleting block %p...\n", block);
+
+	/* Remove block from list */
+	nlmsvc_remove_block(block);
+
+	/* If granted, unlock it, else remove from inode block list */
+	if (unlock && block->b_granted) {
+		dprintk("lockd: deleting granted lock\n");
+		fl->fl_type = F_UNLCK;
+		posix_lock_file(&block->b_file->f_file, fl, 0);
+		block->b_granted = 0;
+	} else {
+		dprintk("lockd: unblocking blocked lock\n");
+		posix_unblock_lock(fl);
+	}
+
+	/* If the block is in the middle of a GRANT callback,
+	 * don't kill it yet. */
+	if (block->b_incall) {
+		nlmsvc_insert_block(block, NLM_NEVER);
+		block->b_done = 1;
+		return;
+	}
+
+	/* Remove block from file's list of blocks */
+	for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) {
+		if (*bp == block) {
+			*bp = block->b_fnext;
+			break;
+		}
+	}
+
+	if (block->b_host)
+		nlm_release_host(block->b_host);
+	nlmclnt_freegrantargs(&block->b_call);
+	kfree(block);
+}
+
+/*
+ * Loop over all blocks and perform the action specified.
+ * (NLM_ACT_CHECK handled by nlmsvc_inspect_file).
+ */
+int
+nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action)
+{
+	struct nlm_block	*block, *next;
+
+	down(&file->f_sema);
+	for (block = file->f_blocks; block; block = next) {
+		next = block->b_fnext;
+		if (action == NLM_ACT_MARK)
+			block->b_host->h_inuse = 1;
+		else if (action == NLM_ACT_UNLOCK) {
+			if (host == NULL || host == block->b_host)
+				nlmsvc_delete_block(block, 1);
+		}
+	}
+	up(&file->f_sema);
+	return 0;
+}
+
+/*
+ * Attempt to establish a lock, and if it can't be granted, block it
+ * if required.
+ */
+u32
+nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
+			struct nlm_lock *lock, int wait, u32 cookie)
+{
+	struct file_lock	*conflock;
+	struct nlm_block	*block;
+	int			error;
+
+	dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n",
+				file->f_file.f_inode->i_dev,
+				file->f_file.f_inode->i_ino,
+				lock->fl.fl_type, lock->fl.fl_pid,
+				lock->fl.fl_start,
+				lock->fl.fl_end,
+				wait);
+
+	/* Lock file against concurrent access */
+	down(&file->f_sema);
+
+	/* Get existing block (in case client is busy-waiting) */
+	block = nlmsvc_lookup_block(file, lock, 0);
+
+	lock->fl.fl_flags |= FL_LOCKD;
+
+again:
+	if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
+		error = posix_lock_file(&file->f_file, &lock->fl, 0);
+
+		if (block)
+			nlmsvc_delete_block(block, 0);
+		up(&file->f_sema);
+
+		dprintk("lockd: posix_lock_file returned %d\n", -error);
+		switch(-error) {
+		case 0:
+			return nlm_granted;
+		case EDEADLK:			/* no applicable NLM status */
+		case EAGAIN:
+			return nlm_lck_denied;
+		default:			/* includes ENOLCK */
+			return nlm_lck_denied_nolocks;
+		}
+	}
+
+	if (!wait) {
+		up(&file->f_sema);
+		return nlm_lck_denied;
+	}
+
+	/* If we don't have a block, create and initialize it. Then
+	 * retry because we may have slept in kmalloc. */
+	if (block == NULL) {
+		dprintk("lockd: blocking on this lock (allocating).\n");
+		if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
+			return nlm_lck_denied_nolocks;
+		goto again;
+	}
+
+	/* Append to list of blocked */
+	nlmsvc_insert_block(block, NLM_NEVER);
+
+	/* Now add block to block list of the conflicting lock */
+	dprintk("lockd: blocking on this lock.\n");
+	posix_block_lock(conflock, &block->b_call.a_args.lock.fl);
+
+	up(&file->f_sema);
+	return nlm_lck_blocked;
+}
+
+/*
+ * Test for presence of a conflicting lock.
+ */
+u32
+nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
+				       struct nlm_lock *conflock)
+{
+	struct file_lock	*fl;
+
+	dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %ld-%ld)\n",
+				file->f_file.f_inode->i_dev,
+				file->f_file.f_inode->i_ino,
+				lock->fl.fl_type,
+				lock->fl.fl_start,
+				lock->fl.fl_end);
+
+	if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+		dprintk("lockd: conflicting lock(ty=%d, %ld-%ld)\n",
+				fl->fl_type, fl->fl_start, fl->fl_end);
+		conflock->caller = "somehost";	/* FIXME */
+		conflock->oh.len = 0;		/* don't return OH info */
+		conflock->fl = *fl;
+		return nlm_lck_denied;
+	}
+
+	return nlm_granted;
+}
+
+/*
+ * Remove a lock.
+ * This implies a CANCEL call: We send a GRANT_MSG, the client replies
+ * with a GRANT_RES call which gets lost, and calls UNLOCK immediately
+ * afterwards. In this case the block will still be there, and hence
+ * must be removed.
+ */
+u32
+nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
+{
+	int	error;
+
+	dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %ld-%ld)\n",
+				file->f_file.f_inode->i_dev,
+				file->f_file.f_inode->i_ino,
+				lock->fl.fl_pid,
+				lock->fl.fl_start,
+				lock->fl.fl_end);
+
+	/* First, cancel any lock that might be there */
+	nlmsvc_cancel_blocked(file, lock);
+
+	lock->fl.fl_type = F_UNLCK;
+	error = posix_lock_file(&file->f_file, &lock->fl, 0);
+
+	return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
+}
+
+/*
+ * Cancel a previously blocked request.
+ *
+ * A cancel request always overrides any grant that may currently
+ * be in progress.
+ * The calling procedure must check whether the file can be closed.
+ */
+u32
+nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
+{
+	struct nlm_block	*block;
+
+	dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %ld-%ld)\n",
+				file->f_file.f_inode->i_dev,
+				file->f_file.f_inode->i_ino,
+				lock->fl.fl_pid,
+				lock->fl.fl_start,
+				lock->fl.fl_end);
+
+	down(&file->f_sema);
+	if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL)
+		nlmsvc_delete_block(block, 1);
+	up(&file->f_sema);
+	return nlm_granted;
+}
+
+/*
+ * Unblock a blocked lock request. This is a callback invoked from the
+ * VFS layer when a lock on which we blocked is removed.
+ *
+ * This function doesn't grant the blocked lock instantly, but rather moves
+ * the block to the head of nlm_blocked where it can be picked up by lockd.
+ */
+static void
+nlmsvc_notify_blocked(struct file_lock *fl)
+{
+	struct nlm_block	**bp, *block;
+
+	dprintk("lockd: VFS unblock notification for block %p\n", fl);
+	posix_unblock_lock(fl);
+	for (bp = &nlm_blocked; (block = *bp); bp = &block->b_next) {
+		if (&block->b_call.a_args.lock.fl == fl) {
+			svc_wake_up(block->b_daemon);
+			nlmsvc_insert_block(block, 0);
+			return;
+		}
+	}
+
+	printk(KERN_WARNING "lockd: notification for unknown block!\n");
+}
+
+/*
+ * Try to claim a lock that was previously blocked.
+ *
+ * Note that we use both the RPC_GRANTED_MSG call _and_ an async
+ * RPC thread when notifying the client. This seems like overkill...
+ * Here's why:
+ *  -	we don't want to use a synchronous RPC thread, otherwise
+ *	we might find ourselves hanging on a dead portmapper.
+ *  -	Some lockd implementations (e.g. HP) don't react to
+ *	RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
+ */
+static void
+nlmsvc_grant_blocked(struct nlm_block *block)
+{
+	struct nlm_file		*file = block->b_file;
+	struct nlm_lock		*lock = &block->b_call.a_args.lock;
+	struct file_lock	*conflock;
+	int			error;
+
+	dprintk("lockd: grant blocked lock %p\n", block);
+
+	/* First thing is lock the file */
+	down(&file->f_sema);
+
+	/* Unlink block request from list */
+	nlmsvc_remove_block(block);
+
+	/* If b_granted is true this means we've been here before.
+	 * Just retry the grant callback, possibly refreshing the RPC
+	 * binding */
+	if (block->b_granted) {
+		nlm_rebind_host(block->b_host);
+		goto callback;
+	}
+
+	/* Try the lock operation again */
+	if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+		/* Bummer, we blocked again */
+		dprintk("lockd: lock still blocked\n");
+		nlmsvc_insert_block(block, NLM_NEVER);
+		posix_block_lock(conflock, &lock->fl);
+		up(&file->f_sema);
+		return;
+	}
+
+	/* Alright, no conflicting lock. Now lock it for real. If the
+	 * following yields an error, this is most probably due to low
+	 * memory. Retry the lock in a few seconds.
+	 */
+	if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) {
+		printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
+				-error, __FUNCTION__);
+		nlmsvc_insert_block(block, jiffies + 10 * HZ);
+		up(&file->f_sema);
+		return;
+	}
+
+callback:
+	/* Lock was granted by VFS. */
+	dprintk("lockd: GRANTing blocked lock.\n");
+	block->b_granted = 1;
+	block->b_incall  = 1;
+
+	/* Schedule next grant callback in 30 seconds */
+	nlmsvc_insert_block(block, jiffies + 30 * HZ);
+
+	/* Call the client */
+	nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
+						nlmsvc_grant_callback);
+	up(&file->f_sema);
+}
+
+/*
+ * This is the callback from the RPC layer when the NLM_GRANTED_MSG
+ * RPC call has succeeded or timed out.
+ * Like all RPC callbacks, it is invoked by the rpciod process, so it
+ * better not sleep. Therefore, we put the blocked lock on the nlm_blocked
+ * chain once more in order to have it removed by lockd itself (which can
+ * then sleep on the file semaphore without disrupting e.g. the nfs client).
+ */
+static void
+nlmsvc_grant_callback(struct rpc_task *task)
+{
+	struct nlm_rqst		*call = (struct nlm_rqst *) task->tk_calldata;
+	struct nlm_block	*block;
+	unsigned long		timeout;
+
+	dprintk("lockd: GRANT_MSG RPC callback\n");
+	if (!(block = nlmsvc_find_block(call->a_args.cookie))) {
+		dprintk("lockd: no block for cookie %x\n", call->a_args.cookie);
+		return;
+	}
+
+	/* Technically, we should down the file semaphore here. Since we
+	 * move the block towards the head of the queue only, no harm
+	 * can be done, though. */
+	if (task->tk_status < 0) {
+		/* RPC error: Re-insert for retransmission */
+		timeout = jiffies + 10 * HZ;
+	} else if (block->b_done) {
+		/* Block already removed, kill it for real */
+		timeout = 0;
+	} else {
+		/* Call was successful, now wait for client callback */
+		timeout = jiffies + 60 * HZ;
+	}
+	nlmsvc_insert_block(block, timeout);
+	svc_wake_up(block->b_daemon);
+	block->b_incall = 0;
+
+	nlm_release_host(call->a_host);
+	rpc_release_task(task);
+}
+
+/*
+ * We received a GRANT_RES callback. Try to find the corresponding
+ * block.
+ */
+void
+nlmsvc_grant_reply(u32 cookie, u32 status)
+{
+	struct nlm_block	*block;
+	struct nlm_file		*file;
+
+	if (!(block = nlmsvc_find_block(cookie)))
+		return;
+	file = block->b_file;
+
+	file->f_count++;
+	down(&file->f_sema);
+	if ((block = nlmsvc_find_block(cookie)) != NULL) {
+		if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
+			/* Try again in a couple of seconds */
+			nlmsvc_insert_block(block, jiffies + 10 * HZ);
+			block = NULL;
+		} else {
+			/* Lock is now held by client, or has been rejected.
+			 * In both cases, the block should be removed. */
+			file->f_count++;
+			up(&file->f_sema);
+			if (status == NLM_LCK_GRANTED)
+				nlmsvc_delete_block(block, 0);
+			else
+				nlmsvc_delete_block(block, 1);
+		}
+	}
+	if (!block)
+		up(&file->f_sema);
+	nlm_release_file(file);
+}
+
+/*
+ * Retry all blocked locks that have been notified. This is where lockd
+ * picks up locks that can be granted, or grant notifications that must
+ * be retransmitted.
+ */
+unsigned long
+nlmsvc_retry_blocked(void)
+{
+	struct nlm_block	*block;
+
+	dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
+			nlm_blocked,
+			nlm_blocked? nlm_blocked->b_when : 0);
+	while ((block = nlm_blocked) && block->b_when < jiffies) {
+		dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
+			block, block->b_when, block->b_done);
+		if (block->b_done)
+			nlmsvc_delete_block(block, 0);
+		else
+			nlmsvc_grant_blocked(block);
+	}
+
+	if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
+		return block->b_when;
+	return 0;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svcproc.c linux/fs/lockd/svcproc.c
--- v2.1.31/linux/fs/lockd/svcproc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/svcproc.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,551 @@
+/*
+ * linux/fs/lockd/svcproc.c
+ *
+ * Lockd server procedures. We don't implement the NLM_*_RES 
+ * procedures because we don't use the async procedures.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY		NLMDBG_CLIENT
+
+static u32	nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *);
+static void	nlmsvc_callback_exit(struct rpc_task *);
+
+/*
+ * Obtain client and file from arguments
+ */
+static u32
+nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
+			struct nlm_host **hostp, struct nlm_file **filp)
+{
+	struct nlm_host		*host = NULL;
+	struct nlm_file		*file = NULL;
+	struct nlm_lock		*lock = &argp->lock;
+	u32			error;
+
+	/* nfsd callbacks must have been installed for this procedure */
+	if (!nlmsvc_ops)
+		return nlm_lck_denied_nolocks;
+
+	/* Obtain handle for client host */
+	if (rqstp->rq_client == NULL) {
+		printk(KERN_NOTICE
+			"lockd: unauthenticated request from (%08lx:%d)\n",
+			ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			ntohs(rqstp->rq_addr.sin_port));
+		return nlm_lck_denied_nolocks;
+	}
+
+	/* Obtain host handle */
+	if (!(host = nlmsvc_lookup_host(rqstp))
+	 || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0))
+		goto no_locks;
+	*hostp = host;
+
+	/* Obtain file pointer. Not used by FREE_ALL call. */
+	if (filp != NULL) {
+		if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+			goto no_locks;
+		*filp = file;
+
+		/* Set up the missing parts of the file_lock structure */
+		lock->fl.fl_file  = &file->f_file;
+		lock->fl.fl_owner = host;
+	}
+
+	return 0;
+
+no_locks:
+	if (host)
+		nlm_release_host(host);
+	return nlm_lck_denied_nolocks;
+}
+
+/*
+ * NULL: Test for presence of service
+ */
+static int
+nlmsvc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+	dprintk("lockd: NULL          called\n");
+	return rpc_success;
+}
+
+/*
+ * TEST: Check for conflicting lock
+ */
+static int
+nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
+				         struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: TEST          called\n");
+	resp->cookie = argp->cookie;
+
+	/* Don't accept test requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now check for conflicting locks */
+	resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+
+	dprintk("lockd: TEST          status %ld\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+static int
+nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				         struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: LOCK          called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period && !argp->reclaim) {
+		resp->status = nlm_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+#if 0
+	/* If supplied state doesn't match current state, we assume it's
+	 * an old request that time-warped somehow. Any error return would
+	 * do in this case because it's irrelevant anyway.
+	 *
+	 * NB: We don't retrieve the remote host's state yet.
+	 */
+	if (host->h_nsmstate && host->h_nsmstate != argp->state) {
+		resp->status = nlm_lck_denied_nolocks;
+	} else
+#endif
+
+	/* Now try to lock the file */
+	resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
+					argp->block, argp->cookie);
+
+	dprintk("lockd: LOCK          status %ld\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+static int
+nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
+				           struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: CANCEL        called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Try to cancel request. */
+	resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+
+	dprintk("lockd: CANCEL        status %ld\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * UNLOCK: release a lock
+ */
+static int
+nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				           struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: UNLOCK        called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to remove the lock */
+	resp->status = nlmsvc_unlock(file, &argp->lock);
+
+	dprintk("lockd: UNLOCK        status %ld\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * GRANTED: A server calls us to tell that a process' lock request
+ * was granted
+ */
+static int
+nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	resp->cookie = argp->cookie;
+
+	dprintk("lockd: GRANTED       called\n");
+	resp->status = nlmclnt_grant(&argp->lock);
+	dprintk("lockd: GRANTED       status %ld\n", ntohl(resp->status));
+	return rpc_success;
+}
+
+/*
+ * `Async' versions of the above service routines. They aren't really,
+ * because we send the callback before the reply proper. I hope this
+ * doesn't break any clients.
+ */
+static int
+nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void	     *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: TEST_MSG      called\n");
+
+	if ((stat = nlmsvc_proc_test(rqstp, argp, &res)) == 0)
+		stat = nlmsvc_callback(rqstp, NLMPROC_TEST_RES, &res);
+	return stat;
+}
+
+static int
+nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void	     *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: LOCK_MSG      called\n");
+
+	if ((stat = nlmsvc_proc_lock(rqstp, argp, &res)) == 0)
+		stat = nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, &res);
+	return stat;
+}
+
+static int
+nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+					       void	       *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: CANCEL_MSG    called\n");
+
+	if ((stat = nlmsvc_proc_cancel(rqstp, argp, &res)) == 0)
+		stat = nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, &res);
+	return stat;
+}
+
+static int
+nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+                                               void            *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: UNLOCK_MSG    called\n");
+
+	if ((stat = nlmsvc_proc_unlock(rqstp, argp, &res)) == 0)
+		stat = nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, &res);
+	return stat;
+}
+
+static int
+nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+                                                void            *resp)
+{
+	struct nlm_res	res;
+	u32		stat;
+
+	dprintk("lockd: GRANTED_MSG   called\n");
+
+	if ((stat = nlmsvc_proc_granted(rqstp, argp, &res)) == 0)
+		stat = nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, &res);
+	return stat;
+}
+
+/*
+ * SHARE: create a DOS share or alter existing share.
+ */
+static int
+nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
+				          struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: SHARE         called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept new lock requests during grace period */
+	if (nlmsvc_grace_period && !argp->reclaim) {
+		resp->status = nlm_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to create the share */
+	resp->status = nlmsvc_share_file(host, file, argp);
+
+	dprintk("lockd: SHARE         status %ld\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * UNSHARE: Release a DOS share.
+ */
+static int
+nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_file	*file;
+
+	dprintk("lockd: UNSHARE       called\n");
+
+	resp->cookie = argp->cookie;
+
+	/* Don't accept requests during grace period */
+	if (nlmsvc_grace_period) {
+		resp->status = nlm_lck_denied_grace_period;
+		return rpc_success;
+	}
+
+	/* Obtain client and file */
+	if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+		return rpc_success;
+
+	/* Now try to lock the file */
+	resp->status = nlmsvc_unshare_file(host, file, argp);
+
+	dprintk("lockd: UNSHARE       status %ld\n", ntohl(resp->status));
+	nlm_release_host(host);
+	nlm_release_file(file);
+	return rpc_success;
+}
+
+/*
+ * NM_LOCK: Create an unmonitored lock
+ */
+static int
+nlmsvc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+				            struct nlm_res  *resp)
+{
+	dprintk("lockd: NM_LOCK       called\n");
+
+	argp->monitor = 0;		/* just clean the monitor flag */
+	return nlmsvc_proc_lock(rqstp, argp, resp);
+}
+
+/*
+ * FREE_ALL: Release all locks and shares held by client
+ */
+static int
+nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
+					     void            *resp)
+{
+	struct nlm_host	*host;
+
+	/* Obtain client */
+	if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL))
+		return rpc_success;
+
+	nlmsvc_free_host_resources(host);
+	nlm_release_host(host);
+	return rpc_success;
+}
+
+/*
+ * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+ */
+static int
+nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
+					      void	        *resp)
+{
+	struct sockaddr_in	saddr = rqstp->rq_addr;
+	struct nlm_host		*host;
+
+	dprintk("lockd: SM_NOTIFY     called\n");
+	if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+	 || ntohs(saddr.sin_port) >= 1024) {
+		printk(KERN_WARNING
+			"lockd: rejected NSM callback from %08lx:%d\n",
+			ntohl(rqstp->rq_addr.sin_addr.s_addr),
+			ntohs(rqstp->rq_addr.sin_port));
+		return rpc_system_err;
+	}
+
+	/* Obtain the host pointer for this NFS server and try to
+	 * reclaim all locks we hold on this server.
+	 */
+	saddr.sin_addr.s_addr = argp->addr;	
+	if ((host = nlm_lookup_host(NULL, &saddr, IPPROTO_UDP, 1)) != NULL) {
+		nlmclnt_recovery(host, argp->state);
+		nlm_release_host(host);
+	}
+
+	/* If we run on an NFS server, delete all locks held by the client */
+	if (nlmsvc_ops != NULL) {
+		struct svc_client	*clnt;
+		saddr.sin_addr.s_addr = argp->addr;	
+		if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL 
+		 && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) {
+			nlmsvc_free_host_resources(host);
+		}
+		nlm_release_host(host);
+	}
+
+	return rpc_success;
+}
+
+/*
+ * This is the generic lockd callback for async RPC calls
+ */
+static u32
+nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
+{
+	struct nlm_host	*host;
+	struct nlm_rqst	*call;
+
+	if (!(call = nlmclnt_alloc_call()))
+		return rpc_system_err;
+
+	host = nlmclnt_lookup_host(&rqstp->rq_addr,
+				rqstp->rq_prot, rqstp->rq_vers);
+	if (!host) {
+		kfree(call);
+		return rpc_system_err;
+	}
+
+	call->a_flags = RPC_TASK_ASYNC;
+	call->a_host  = host;
+	memcpy(&call->a_args, resp, sizeof(*resp));
+
+	if (nlmclnt_async_call(call, proc, nlmsvc_callback_exit) < 0)
+		return rpc_system_err;
+
+	return rpc_success;
+}
+
+static void
+nlmsvc_callback_exit(struct rpc_task *task)
+{
+	struct nlm_rqst	*call = (struct nlm_rqst *) task->tk_calldata;
+
+	if (task->tk_status < 0) {
+		dprintk("lockd: %4d callback failed (errno = %d)\n",
+					task->tk_pid, -task->tk_status);
+	}
+	nlm_release_host(call->a_host);
+	rpc_release_task(task);
+	kfree(call);
+}
+
+/*
+ * NLM Server procedures.
+ */
+#define nlmsvc_proc_none	NULL
+#define nlmsvc_encode_norep	NULL
+#define nlmsvc_decode_norep	NULL
+#define nlmsvc_decode_testres	NULL
+#define nlmsvc_proc_test_res	NULL
+#define nlmsvc_proc_lock_res	NULL
+#define nlmsvc_proc_cancel_res	NULL
+#define nlmsvc_proc_unlock_res	NULL
+#define nlmsvc_proc_granted_res	NULL
+struct nlm_void			{ int dummy; };
+
+#define PROC(name, xargt, xrest, argt, rest)	\
+ { (svc_procfunc) nlmsvc_proc_##name,	\
+   (kxdrproc_t) nlmsvc_decode_##xargt,	\
+   (kxdrproc_t) nlmsvc_encode_##xrest,	\
+   NULL,				\
+   sizeof(struct nlm_##argt),		\
+   sizeof(struct nlm_##rest),		\
+   0,					\
+   0					\
+ }
+struct svc_procedure		nlmsvc_procedures[] = {
+  PROC(null,		void,		void,		void,	void),
+  PROC(test,		testargs,	testres,	args,	res),
+  PROC(lock,		lockargs,	res,		args,	res),
+  PROC(cancel,		cancargs,	res,		args,	res),
+  PROC(unlock,		unlockargs,	res,		args,	res),
+  PROC(granted,		testargs,	res,		args,	res),
+  PROC(test_msg,	testargs,	norep,		args,	void),
+  PROC(lock_msg,	lockargs,	norep,		args,	void),
+  PROC(cancel_msg,	cancargs,	norep,		args,	void),
+  PROC(unlock_msg,	unlockargs,	norep,		args,	void),
+  PROC(granted_msg,	testargs,	norep,		args,	void),
+  PROC(test_res,	testres,	norep,		res,	void),
+  PROC(lock_res,	res,		norep,		res,	void),
+  PROC(cancel_res,	res,		norep,		res,	void),
+  PROC(unlock_res,	res,		norep,		res,	void),
+  PROC(granted_res,	res,		norep,		res,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(none,		void,		void,		void,	void),
+  PROC(share,		shareargs,	shareres,	args,	res),
+  PROC(unshare,		shareargs,	shareres,	args,	res),
+  PROC(nm_lock,		lockargs,	res,		args,	res),
+  PROC(free_all,	notify,		void,		args,	void),
+
+  /* statd callback */
+  PROC(sm_notify,	reboot,		void,		reboot,	void),
+};
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svcshare.c linux/fs/lockd/svcshare.c
--- v2.1.31/linux/fs/lockd/svcshare.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/svcshare.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,111 @@
+/*
+ * linux/fs/lockd/svcshare.c
+ *
+ * Management of DOS shares.
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+
+static inline int
+nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh)
+{
+	return share->s_owner.len == oh->len
+	    && !memcmp(share->s_owner.data, oh->data, oh->len);
+}
+
+u32
+nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
+			struct nlm_args *argp)
+{
+	struct nlm_share	*share;
+	struct xdr_netobj	*oh = &argp->lock.oh;
+	u8			*ohdata;
+
+	for (share = file->f_shares; share; share = share->s_next) {
+		if (share->s_host == host && nlm_cmp_owner(share, oh))
+			goto update;
+		if ((argp->fsm_access & share->s_mode)
+		 || (argp->fsm_mode   & share->s_access ))
+			return nlm_lck_denied;
+	}
+
+	share = (struct nlm_share *) kmalloc(sizeof(*share) + oh->len,
+						GFP_KERNEL);
+	if (share == NULL)
+		return nlm_lck_denied_nolocks;
+
+	/* Copy owner handle */
+	ohdata = (u8 *) (share + 1);
+	memcpy(ohdata, oh->data, oh->len);
+
+	share->s_file	    = file;
+	share->s_host       = host;
+	share->s_owner.data = ohdata;
+	share->s_owner.len  = oh->len;
+	share->s_next       = file->f_shares;
+	file->f_shares      = share;
+	file->f_count	   += 1;
+
+update:
+	share->s_access = argp->fsm_access;
+	share->s_mode   = argp->fsm_mode;
+	return nlm_granted;
+}
+
+/*
+ * Delete a share.
+ */
+u32
+nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
+			struct nlm_args *argp)
+{
+	struct nlm_share	*share, **shpp;
+	struct xdr_netobj	*oh = &argp->lock.oh;
+
+	for (shpp = &file->f_shares; (share = *shpp); shpp = &share->s_next) {
+		if (share->s_host == host && nlm_cmp_owner(share, oh)) {
+			*shpp = share->s_next;
+			kfree(share);
+			return nlm_granted;
+		}
+	}
+
+	/* X/Open spec says return success even if there was no
+	 * corresponding share. */
+	return nlm_granted;
+}
+
+/*
+ * Traverse all shares for a given file (and host).
+ * NLM_ACT_CHECK is handled by nlmsvc_inspect_file.
+ */
+int
+nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action)
+{
+	struct nlm_share	*share, **shpp;
+
+	shpp = &file->f_shares;
+	while ((share = *shpp) !=  NULL) {
+		if (action == NLM_ACT_MARK)
+			share->s_host->h_inuse = 1;
+		else if (action == NLM_ACT_UNLOCK) {
+			if (host == NULL || host == share->s_host) {
+				*shpp = share->s_next;
+				kfree(share);
+				continue;
+			}
+		}
+		shpp = &share->s_next;
+	}
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c
--- v2.1.31/linux/fs/lockd/svcsubs.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/svcsubs.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,278 @@
+/*
+ * linux/fs/lockd/svcsubs.c
+ *
+ * Various support routines for the NLM server.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/export.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY		NLMDBG_SVCSUBS
+
+
+/*
+ * Global file hash table
+ */
+#define FILE_NRHASH		32
+#define FILE_HASH(dev, ino)	(((dev) + (ino)) & FILE_NRHASH)
+static struct nlm_file *	nlm_files[FILE_NRHASH];
+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.
+ *
+ * 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.
+ */
+u32
+nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
+					struct nfs_fh *f)
+{
+	struct nlm_file	*file;
+	struct knfs_fh	*fh = (struct knfs_fh *) f;
+	unsigned int	hash = FILE_HASH(fh->fh_dev, fh->fh_ino);
+	u32		nfserr;
+
+	dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino);
+
+	/* Lock file table */
+	down(&nlm_file_sema);
+
+	for (file = nlm_files[hash]; file; file = file->f_next) {
+		if (file->f_handle.fh_ino == fh->fh_ino
+		 && !memcmp(&file->f_handle, fh, sizeof(*fh)))
+			goto found;
+	}
+
+	dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino);
+	if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) {
+		up(&nlm_file_sema);
+		return nlm_lck_denied_nolocks;
+	}
+
+	memset(file, 0, sizeof(*file));
+	file->f_handle = *fh;
+	file->f_sema   = MUTEX;
+
+	/* Open the file. Note that this must not sleep for too long, else
+	 * we would lock up lockd:-) So no NFS re-exports, folks. */
+	if ((nfserr = nlmsvc_ops->fopen(rqstp, fh, &file->f_file)) != 0) {
+		dprintk("lockd: open failed (nfserr %ld)\n", ntohl(nfserr));
+		kfree(file);
+		up(&nlm_file_sema);
+		return nlm_lck_denied;
+	}
+
+	file->f_next = nlm_files[hash];
+	nlm_files[hash] = file;
+
+found:
+	dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
+	*result = file;
+	up(&nlm_file_sema);
+	file->f_count++;
+	return 0;
+}
+
+/*
+ * Delete a file after having released all locks, blocks and shares
+ */
+static inline void
+nlm_delete_file(struct nlm_file *file)
+{
+	struct inode	*inode = nlmsvc_file_inode(file);
+	struct nlm_file	**fp, *f;
+
+	dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino);
+	fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino);
+	while ((f = *fp) != NULL) {
+		if (f == file) {
+			*fp = file->f_next;
+			nlmsvc_ops->fclose(&file->f_file);
+			kfree(file);
+			return;
+		}
+		fp = &file->f_next;
+	}
+
+	printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
+}
+
+/*
+ * Loop over all locks on the given file and perform the specified
+ * action.
+ */
+static int
+nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action)
+{
+	struct inode	 *inode = nlmsvc_file_inode(file);
+	struct file_lock *fl;
+	struct nlm_host	 *lockhost;
+
+again:
+	file->f_locks = 0;
+	for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+		if (!(fl->fl_flags & FL_LOCKD))
+			continue;
+
+		/* update current lock count */
+		file->f_locks++;
+		lockhost = (struct nlm_host *) fl->fl_owner;
+		if (action == NLM_ACT_MARK)
+			lockhost->h_inuse = 1;
+		else if (action == NLM_ACT_CHECK)
+			return 1;
+		else if (action == NLM_ACT_UNLOCK) {
+			struct file_lock lock = *fl;
+
+			if (host && lockhost != host)
+				continue;
+
+			lock.fl_type  = F_UNLCK;
+			lock.fl_start = 0;
+			lock.fl_end   = NLM_OFFSET_MAX;
+			if (posix_lock_file(&file->f_file, &lock, 0) < 0) {
+				printk("lockd: unlock failure in %s:%d\n",
+						__FILE__, __LINE__);
+				return 1;
+			}
+			goto again;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Operate on a single file
+ */
+static inline int
+nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action)
+{
+	if (action == NLM_ACT_CHECK) {
+		/* Fast path for mark and sweep garbage collection */
+		if (file->f_count || file->f_blocks || file->f_shares)
+			return 1;
+	} else {
+		if (nlmsvc_traverse_blocks(host, file, action)
+		 || nlmsvc_traverse_shares(host, file, action))
+			return 1;
+	}
+	return nlm_traverse_locks(host, file, action);
+}
+
+/*
+ * Loop over all files in the file table.
+ */
+static int
+nlm_traverse_files(struct nlm_host *host, int action)
+{
+	struct nlm_file	*file, **fp;
+	int		i;
+
+	down(&nlm_file_sema);
+	for (i = 0; i < FILE_NRHASH; i++) {
+		fp = nlm_files + i;
+		while ((file = *fp) != NULL) {
+			/* Traverse locks, blocks and shares of this file
+			 * and update file->f_locks count */
+			if (nlm_inspect_file(host, file, action)) {
+				up(&nlm_file_sema);
+				return 1;
+			}
+
+			/* No more references to this file. Let go of it. */
+			if (!file->f_blocks && !file->f_locks
+			 && !file->f_shares && !file->f_count) {
+				*fp = file->f_next;
+				nlmsvc_ops->fclose(&file->f_file);
+				kfree(file);
+			} else {
+				fp = &file->f_next;
+			}
+		}
+	}
+	up(&nlm_file_sema);
+	return 0;
+}
+
+/*
+ * Release file. If there are no more remote locks on this file,
+ * close it and free the handle.
+ *
+ * Note that we can't do proper reference counting without major
+ * contortions because the code in fs/locks.c creates, deletes and
+ * splits locks without notification. Our only way is to walk the
+ * entire lock list each time we remove a lock.
+ */
+void
+nlm_release_file(struct nlm_file *file)
+{
+	dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
+				file, file->f_count);
+
+	/* Lock file table */
+	down(&nlm_file_sema);
+
+	/* If there are no more locks etc, delete the file */
+	if (--(file->f_count) == 0
+	 && !nlm_inspect_file(NULL, file, NLM_ACT_CHECK))
+		nlm_delete_file(file);
+
+	up(&nlm_file_sema);
+	return;
+}
+
+/*
+ * Mark all hosts that still hold resources
+ */
+void
+nlmsvc_mark_resources(void)
+{
+	dprintk("lockd: nlmsvc_mark_resources\n");
+
+	nlm_traverse_files(NULL, NLM_ACT_MARK);
+}
+
+/*
+ * Release all resources held by the given client
+ */
+void
+nlmsvc_free_host_resources(struct nlm_host *host)
+{
+	dprintk("lockd: nlmsvc_free_host_resources\n");
+
+	if (nlm_traverse_files(host, NLM_ACT_UNLOCK))
+		printk(KERN_WARNING
+			"lockd: couldn't remove all locks held by %s",
+			host->h_name);
+}
+
+/*
+ * Delete a client when the nfsd entry is removed.
+ */
+void
+nlmsvc_invalidate_client(struct svc_client *clnt)
+{
+	struct nlm_host	*host;
+
+	if ((host = nlm_lookup_host(clnt, NULL, 0, 0)) != NULL) {
+		dprintk("lockd: invalidating client for %s\n", host->h_name);
+		nlmsvc_free_host_resources(host);
+		host->h_expires = 0;
+		nlm_release_host(host);
+	}
+}
diff -u --recursive --new-file v2.1.31/linux/fs/lockd/xdr.c linux/fs/lockd/xdr.c
--- v2.1.31/linux/fs/lockd/xdr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/lockd/xdr.c	Fri Apr  4 11:06:05 1997
@@ -0,0 +1,605 @@
+/*
+ * linux/fs/lockd/xdr.c
+ *
+ * XDR support for lockd and the lock client.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/utsname.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY		NLMDBG_XDR
+#define NLM_MAXSTRLEN		1024
+
+#define QUADLEN(len)		(((len) + 3) >> 2)
+
+
+u32	nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks,
+	nlm_lck_blocked, nlm_lck_denied_grace_period;
+
+
+typedef struct nlm_args	nlm_args;
+
+/*
+ * Initialization of NFS status variables
+ */
+void
+nlmxdr_init(void)
+{
+	static int	inited = 0;
+
+	if (inited)
+		return;
+
+	nlm_granted = htonl(NLM_LCK_GRANTED);
+	nlm_lck_denied = htonl(NLM_LCK_DENIED);
+	nlm_lck_denied_nolocks = htonl(NLM_LCK_DENIED_NOLOCKS);
+	nlm_lck_blocked = htonl(NLM_LCK_BLOCKED);
+	nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
+
+	inited = 1;
+}
+
+/*
+ * XDR functions for basic NLM types
+ */
+static inline u32 *
+nlm_decode_cookie(u32 *p, u32 *c)
+{
+	unsigned int	len;
+
+	if ((len = ntohl(*p++)) == 4) {
+		*c = ntohl(*p++);
+	} else if (len == 0) {		/* hockeypux brain damage */
+		*c = 0;
+	} else {
+		printk(KERN_NOTICE
+			"lockd: bad cookie size %d (should be 4)\n", len);
+		return NULL;
+	}
+	return p;
+}
+
+static inline u32 *
+nlm_encode_cookie(u32 *p, u32 c)
+{
+	*p++ = htonl(sizeof(c));
+	*p++ = htonl(c);
+	return p;
+}
+
+static inline u32 *
+nlm_decode_fh(u32 *p, struct nfs_fh *f)
+{
+	unsigned int	len;
+
+	if ((len = ntohl(*p++)) != sizeof(*f)) {
+		printk(KERN_NOTICE
+			"lockd: bad fhandle size %x (should be %d)\n",
+			len, sizeof(*f));
+		return NULL;
+	}
+	memcpy(f, p, sizeof(*f));
+	return p + XDR_QUADLEN(sizeof(*f));
+}
+
+static inline u32 *
+nlm_encode_fh(u32 *p, struct nfs_fh *f)
+{
+	*p++ = htonl(sizeof(*f));
+	memcpy(p, f, sizeof(*f));
+	return p + XDR_QUADLEN(sizeof(*f));
+}
+
+/*
+ * Encode and decode owner handle
+ */
+static inline u32 *
+nlm_decode_oh(u32 *p, struct xdr_netobj *oh)
+{
+	return xdr_decode_netobj(p, oh);
+}
+
+static inline u32 *
+nlm_encode_oh(u32 *p, struct xdr_netobj *oh)
+{
+	return xdr_encode_netobj(p, oh);
+}
+
+static inline u32 *
+nlm_decode_lock(u32 *p, struct nlm_lock *lock)
+{
+	struct file_lock	*fl = &lock->fl;
+	int			len;
+
+	if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))
+	 || !(p = nlm_decode_fh(p, &lock->fh))
+	 || !(p = nlm_decode_oh(p, &lock->oh)))
+		return NULL;
+
+	memset(fl, 0, sizeof(*fl));
+	fl->fl_owner = current;
+	fl->fl_pid   = ntohl(*p++);
+	fl->fl_flags = FL_POSIX;
+	fl->fl_type  = F_RDLCK;		/* as good as anything else */
+	fl->fl_start = ntohl(*p++);
+	len = ntohl(*p++);
+	if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
+		fl->fl_end = NLM_OFFSET_MAX;
+	return p;
+}
+
+/*
+ * Encode a lock as part of an NLM call
+ */
+static u32 *
+nlm_encode_lock(u32 *p, struct nlm_lock *lock)
+{
+	struct file_lock	*fl = &lock->fl;
+
+	if (!(p = xdr_encode_string(p, lock->caller))
+	 || !(p = nlm_encode_fh(p, &lock->fh))
+	 || !(p = nlm_encode_oh(p, &lock->oh)))
+		return NULL;
+
+	*p++ = htonl(fl->fl_pid);
+	*p++ = htonl(lock->fl.fl_start);
+	if (lock->fl.fl_end == NLM_OFFSET_MAX)
+		*p++ = xdr_zero;
+	else
+		*p++ = htonl(lock->fl.fl_end - lock->fl.fl_start + 1);
+
+	return p;
+}
+
+/*
+ * Encode result of a TEST/TEST_MSG call
+ */
+static u32 *
+nlm_encode_testres(u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_encode_cookie(p, resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+
+	if (resp->status == nlm_lck_denied) {
+		struct file_lock	*fl = &resp->lock.fl;
+
+		*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
+		*p++ = htonl(fl->fl_pid);
+
+		/* Encode owner handle. */
+		if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
+			return 0;
+
+		*p++ = htonl(fl->fl_start);
+		if (fl->fl_end == NLM_OFFSET_MAX)
+			*p++ = xdr_zero;
+		else
+			*p++ = htonl(fl->fl_end - fl->fl_start + 1);
+	}
+
+	return p;
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_argbuf;
+
+	return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_resbuf;
+
+	buf->len = p - buf->base;
+	return (buf->len <= buf->buflen);
+}
+
+/*
+ * First, the server side XDR functions
+ */
+int
+nlmsvc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+		return 0;
+
+	exclusive = ntohl(*p++);
+	if (!(p = nlm_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_encode_testres(p, resp)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+		return 0;
+	argp->block  = ntohl(*p++);
+	exclusive    = ntohl(*p++);
+	if (!(p = nlm_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+	argp->reclaim = ntohl(*p++);
+	argp->state   = ntohl(*p++);
+	argp->monitor = 1;		/* monitor client by default */
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	u32	exclusive;
+
+	if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+		return 0;
+	argp->block = ntohl(*p++);
+	exclusive = ntohl(*p++);
+	if (!(p = nlm_decode_lock(p, &argp->lock)))
+		return 0;
+	if (exclusive)
+		argp->lock.fl.fl_type = F_WRLCK;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	if (!(p = nlm_decode_cookie(p, &argp->cookie))
+	 || !(p = nlm_decode_lock(p, &argp->lock)))
+		return 0;
+	argp->lock.fl.fl_type = F_UNLCK;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+	int		len;
+
+	memset(lock, 0, sizeof(*lock));
+	lock->fl.fl_pid = ~(u32) 0;
+
+	if (!(p = nlm_decode_cookie(p, &argp->cookie))
+	 || !(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))
+	 || !(p = nlm_decode_fh(p, &lock->fh))
+	 || !(p = nlm_decode_oh(p, &lock->oh)))
+		return 0;
+	argp->fsm_mode = ntohl(*p++);
+	argp->fsm_access = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_encode_cookie(p, resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+	*p++ = xdr_zero;		/* sequence argument */
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_encode_cookie(p, resp->cookie)))
+		return 0;
+	*p++ = resp->status;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+	int		len;
+
+	if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)))
+		return 0;
+	argp->state = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp)
+{
+	if (!(p = xdr_decode_string(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
+		return 0;
+	argp->state = ntohl(*p++);
+	argp->addr = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+		return 0;
+	resp->status = ntohl(*p++);
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Now, the client side XDR functions
+ */
+static int
+nlmclt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+	return 0;
+}
+
+static int
+nlmclt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm_encode_cookie(p, argp->cookie)))
+		return -EIO;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+		return -EIO;
+	resp->status = ntohl(*p++);
+	if (resp->status == NLM_LCK_DENIED) {
+		struct file_lock	*fl = &resp->lock.fl;
+		u32			excl, len;
+
+		memset(&resp->lock, 0, sizeof(resp->lock));
+		excl = ntohl(*p++);
+		fl->fl_pid = ntohl(*p++);
+		if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
+			return -EIO;
+
+		fl->fl_flags = FL_POSIX;
+		fl->fl_type  = excl? F_WRLCK : F_RDLCK;
+		fl->fl_start = ntohl(*p++);
+		len = ntohl(*p++);
+		if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
+			fl->fl_end = NLM_OFFSET_MAX;
+	}
+	return 0;
+}
+
+
+static int
+nlmclt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm_encode_cookie(p, argp->cookie)))
+		return -EIO;
+	*p++ = argp->block? xdr_one : xdr_zero;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm_encode_lock(p, lock)))
+		return -EIO;
+	*p++ = argp->reclaim? xdr_one : xdr_zero;
+	*p++ = htonl(argp->state);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm_encode_cookie(p, argp->cookie)))
+		return -EIO;
+	*p++ = argp->block? xdr_one : xdr_zero;
+	*p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+	if (!(p = nlm_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+	struct nlm_lock	*lock = &argp->lock;
+
+	if (!(p = nlm_encode_cookie(p, argp->cookie)))
+		return -EIO;
+	if (!(p = nlm_encode_lock(p, lock)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_encode_cookie(p, resp->cookie)))
+		return -EIO;
+	*p++ = resp->status;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_encode_testres(p, resp)))
+		return -EIO;
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+	if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+		return -EIO;
+	resp->status = ntohl(*p++);
+	return 0;
+}
+
+/*
+ * Buffer requirements for NLM
+ */
+#define NLM_void_sz		0
+#define NLM_cookie_sz		2
+#define NLM_caller_sz		1+QUADLEN(sizeof(system_utsname.nodename))
+#define NLM_netobj_sz		1+QUADLEN(XDR_MAX_NETOBJ)
+/* #define NLM_owner_sz		1+QUADLEN(NLM_MAXOWNER) */
+#define NLM_fhandle_sz		1+QUADLEN(NFS_FHSIZE)
+#define NLM_lock_sz		3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz
+#define NLM_holder_sz		4+NLM_netobj_sz
+
+#define NLM_testargs_sz		NLM_cookie_sz+1+NLM_lock_sz
+#define NLM_lockargs_sz		NLM_cookie_sz+4+NLM_lock_sz
+#define NLM_cancargs_sz		NLM_cookie_sz+2+NLM_lock_sz
+#define NLM_unlockargs_sz	NLM_cookie_sz+NLM_lock_sz
+
+#define NLM_testres_sz		NLM_cookie_sz+1+NLM_holder_sz
+#define NLM_res_sz		NLM_cookie_sz+1
+#define NLM_norep_sz		0
+
+#ifndef MAX
+# define MAX(a, b)		(((a) > (b))? (a) : (b))
+#endif
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlmclt_decode_norep	NULL
+
+#define PROC(proc, argtype, restype)	\
+    { "nlm_" #proc,						\
+      (kxdrproc_t) nlmclt_encode_##argtype,			\
+      (kxdrproc_t) nlmclt_decode_##restype,			\
+      MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2		\
+    }
+
+static struct rpc_procinfo	nlm_procedures[] = {
+    PROC(null,		void,		void),
+    PROC(test,		testargs,	testres),
+    PROC(lock,		lockargs,	res),
+    PROC(canc,		cancargs,	res),
+    PROC(unlock,	unlockargs,	res),
+    PROC(granted,	testargs,	res),
+    PROC(test_msg,	testargs,	norep),
+    PROC(lock_msg,	lockargs,	norep),
+    PROC(canc_msg,	cancargs,	norep),
+    PROC(unlock_msg,	unlockargs,	norep),
+    PROC(granted_msg,	testargs,	norep),
+    PROC(test_res,	testres,	norep),
+    PROC(lock_res,	res,		norep),
+    PROC(canc_res,	res,		norep),
+    PROC(unlock_res,	res,		norep),
+    PROC(granted_res,	res,		norep),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+#ifdef NLMCLNT_SUPPORT_SHARES
+    PROC(share,		shareargs,	shareres),
+    PROC(unshare,	shareargs,	shareres),
+    PROC(nm_lock,	lockargs,	res),
+    PROC(free_all,	notify,		void),
+#else
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+    PROC(undef,		void,		void),
+#endif
+};
+
+static struct rpc_version	nlm_version1 = {
+	1, 16, nlm_procedures,
+};
+
+static struct rpc_version	nlm_version3 = {
+	3, 24, nlm_procedures,
+};
+
+static struct rpc_version *	nlm_versions[] = {
+	NULL,
+	&nlm_version1,
+	NULL,
+	&nlm_version3,
+};
+
+static struct rpc_stat		nlm_stats;
+
+struct rpc_program		nlm_program = {
+	"lockd",
+	NLM_PROGRAM,
+	sizeof(nlm_versions) / sizeof(nlm_versions[0]),
+	nlm_versions,
+	&nlm_stats,
+};
+
+#ifdef LOCKD_DEBUG
+char *
+nlm_procname(u32 proc)
+{
+	if (proc < sizeof(nlm_procedures)/sizeof(nlm_procedures[0]))
+		return nlm_procedures[proc].p_procname;
+	return "unknown";
+}
+#endif
+
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/Makefile linux/fs/nfs/Makefile
--- v2.1.31/linux/fs/nfs/Makefile	Sat Mar 30 14:05:22 1996
+++ linux/fs/nfs/Makefile	Fri Apr  4 11:05:59 1997
@@ -8,11 +8,11 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := nfs.o
-O_OBJS   := proc.o sock.o rpcsock.o inode.o file.o bio.o \
-	    nfsiod.o dir.o symlink.o
+O_OBJS   := inode.o file.o read.o write.o dir.o symlink.o proc.o \
+	    nfs2xdr.o
 
 ifdef CONFIG_ROOT_NFS
-O_OBJS += nfsroot.o
+  O_OBJS += nfsroot.o
 endif
 
 M_OBJS   := $(O_TARGET)
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/README linux/fs/nfs/README
--- v2.1.31/linux/fs/nfs/README	Thu Apr 25 05:04:50 1996
+++ linux/fs/nfs/README	Wed Dec 31 16:00:00 1969
@@ -1,114 +0,0 @@
-
-
-    This is an NFS client for Linux that supports async RPC calls for
-    read-ahead (and hopefully soon, write-back) on regular files. 
-
-    The implementation uses a straightforward nfsiod scheme.  After
-    trying out a number of different concepts, I finally got back to
-    this concept, because everything else either didn't work or gave me
-    headaches. It's not flashy, but it works without hacking into any
-    other regions of the kernel.
-
-
-    HOW TO USE
-
-    This stuff compiles as a loadable module (I developed it on 1.3.77).
-    Simply type mkmodule, and insmod nfs.o. This will start four nfsiod's
-    at the same time (which will show up under the pseudonym of insmod in
-    ps-style listings).
-
-    Alternatively, you can put it right into the kernel: remove everything
-    from fs/nfs, move the Makefile and all *.c to this directory, and
-    copy all *.h files to include/linux.
-
-    After mounting, you should be able to watch (with tcpdump) several
-    RPC READ calls being placed simultaneously.
-
-
-    HOW IT WORKS
-
-    When a process reads from a file on an NFS volume, the following
-    happens:
-
-     *	nfs_file_read sets file->f_reada if more than 1K is
-    	read at once. It then calls generic_file_read.
-
-     *	generic_file_read requests one ore more pages via
-    	nfs_readpage.
-
-     *	nfs_readpage allocates a request slot with an nfsiod
-    	daemon, fills in the READ request, sends out the
-    	RPC call, kicks the daemon, and returns.
-    	If there's no free biod, nfs_readpage places the
-    	call directly, waiting for the reply (sync readpage).
-
-     *	nfsiod calls nfs_rpc_doio to collect the reply. If the
-    	call was successful, it sets page->uptodate and
-    	wakes up all processes waiting on page->wait;
-
-    This is the rough outline only. There are a few things to note:
-
-     *	Async RPC will not be tried when server->rsize < PAGE_SIZE.
-
-     *	When an error occurs, nfsiod has no way of returning
-    	the error code to the user process. Therefore, it flags
-    	page->error and wakes up all processes waiting on that
-    	page (they usually do so from within generic_readpage).
-
-    	generic_readpage finds that the page is still not
-    	uptodate, and calls nfs_readpage again. This time around,
-    	nfs_readpage notices that page->error is set and
-    	unconditionally does a synchronous RPC call.
-
-    	This area needs a lot of improvement, since read errors
-    	are not that uncommon (e.g. we have to retransmit calls
-    	if the fsuid is different from the ruid in order to
-    	cope with root squashing and stuff like this).
-
-	Retransmits with fsuid/ruid change should be handled by
-	nfsiod, but this doesn't come easily (a more general nfs_call
-	routine that does all this may be useful...)
-
-     *	To save some time on readaheads, we save one data copy
-    	by frobbing the page into the iovec passed to the
-	RPC code so that the networking layer copies the
-    	data into the page directly.
-
-    	This needs to be adjustable (different authentication
-    	flavors; AUTH_NULL versus AUTH_SHORT verifiers).
-
-     *	Currently, a fixed number of nfsiod's is spawned from
-    	within init_nfs_fs. This is problematic when running
-    	as a loadable module, because this will keep insmod's
-    	memory allocated. As a side-effect, you will see the
-    	nfsiod processes listed as several insmod's when doing
-    	a `ps.'
-
-     * 	This NFS client implements server congestion control via
-	Van Jacobson slow start as implemented in 44BSD. I haven't
-	checked how well this behaves, but since Rick Macklem did
-	it this way, it should be okay :-)
-
-
-    WISH LIST
-
-    After giving this thing some testing, I'd like to add some more
-    features:
-
-     *	Some sort of async write handling. True write-back doesn't
-	work with the current kernel (I think), because invalidate_pages
-	kills all pages, regardless of whether they're dirty or not.
-	Besides, this may require special bdflush treatment because
-	write caching on clients is really hairy.
-
-	Alternatively, a write-through scheme might be useful where
-	the client enqueues the request, but leaves collecting the
-	results to nfsiod. Again, we need a way to pass RPC errors
-	back to the application.
-
-     *	Support for different authentication flavors.
-
-     *	/proc/net/nfsclnt (for nfsstat, etc.).
-
-March 29, 1996
-Olaf Kirch <okir@monad.swb.de>
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/bio.c linux/fs/nfs/bio.c
--- v2.1.31/linux/fs/nfs/bio.c	Sat Dec 14 03:33:17 1996
+++ linux/fs/nfs/bio.c	Wed Dec 31 16:00:00 1969
@@ -1,225 +0,0 @@
-/*
- * linux/fs/nfs/bio.c
- *
- * Block I/O for NFS
- *
- * Partial copy of Linus' read cache modifications to fs/nfs/file.c
- * modified for async RPC by okir@monad.swb.de
- *
- * We do an ugly hack here in order to return proper error codes to the
- * user program when a read request failed. This is a huge problem because
- * generic_file_read only checks the return value of inode->i_op->readpage()
- * which is usually 0 for async RPC. To overcome this obstacle, we set
- * the error bit of the page to 1 when an error occurs, and make nfs_readpage
- * transmit requests synchronously when encountering this.
- *
- * Another possible solution to this problem may be to have a cache of recent
- * RPC call results indexed by page pointer, or even a result code field
- * in struct page.
- *
- * June 96: Added retries of RPCs that seem to have failed for a transient
- * reason.
- */
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fcntl.h>
-#include <linux/stat.h>
-#include <linux/mm.h>
-#include <linux/nfs_fs.h>
-#include <linux/nfsiod.h>
-#include <linux/malloc.h>
-#include <linux/pagemap.h>
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
-#undef DEBUG_BIO
-#ifdef DEBUG_BIO
-#define dprintk(args...)	printk(## args)
-#else
-#define dprintk(args...)	/* nothing */
-#endif
-
-static inline int
-do_read_nfs_sync(struct inode * inode, struct page * page)
-{
-	struct nfs_fattr fattr;
-	int		result, refresh = 0;
-	int		count = PAGE_SIZE;
-	int		rsize = NFS_SERVER(inode)->rsize;
-	char		*buf = (char *) page_address(page);
-	unsigned long	pos = page->offset;
-
-	dprintk("NFS: do_read_nfs_sync(%p)\n", page);
-
-	set_bit(PG_locked, &page->flags);
-	clear_bit(PG_error, &page->flags);
-
-	do {
-		if (count < rsize)
-			rsize = count;
-		result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), 
-			pos, rsize, buf, &fattr);
-		dprintk("nfs_proc_read(%s, (%x,%lx), %ld, %d, %p) = %d\n",
-				NFS_SERVER(inode)->hostname,
-				inode->i_dev, inode->i_ino,
-				pos, rsize, buf, result);
-		/*
-		 * Even if we had a partial success we can't mark the page
-		 * cache valid.
-		 */
-		if (result < 0)
-			goto io_error;
-		refresh = 1;
-		count -= result;
-		pos += result;
-		buf += result;
-		if (result < rsize)
-			break;
-	} while (count);
-
-	memset(buf, 0, count);
-	set_bit(PG_uptodate, &page->flags);
-	result = 0;
-
-io_error:
-	if (refresh)
-		nfs_refresh_inode(inode, &fattr);
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
-	return result;
-}
-
-/*
- * This is the function to (re-) transmit an NFS readahead request
- */
-static int
-nfsiod_read_setup(struct nfsiod_req *req)
-{
-	struct inode	*inode = req->rq_inode;
-	struct page	*page = req->rq_page;
-
-	return nfs_proc_read_request(&req->rq_rpcreq,
-			NFS_SERVER(inode), NFS_FH(inode),
-			page->offset, PAGE_SIZE, 
-			(__u32 *) page_address(page));
-}
-
-/*
- * This is the callback from nfsiod telling us whether a reply was
- * received or some error occurred (timeout or socket shutdown).
- */
-static int
-nfsiod_read_result(int result, struct nfsiod_req *req)
-{
-	struct nfs_server *server = NFS_SERVER(req->rq_inode);
-	struct page	*page = req->rq_page;
-	static int	succ = 0, fail = 0;
-	int		i;
-
-	dprintk("BIO: received callback for page %p, result %d\n",
-			page, result);
-
-	if (result >= 0) {
-		struct nfs_fattr	fattr;
-
-		result = nfs_proc_read_reply(&req->rq_rpcreq, &fattr);
-		if (result >= 0) {
-			nfs_refresh_inode(req->rq_inode, &fattr);
-			if (result < PAGE_SIZE)
-				memset((u8 *) page_address(page)+result,
-						0, PAGE_SIZE-result);
-		}
-	} else
-	if (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT)) {
-		/* XXX: Theoretically, we'd have to increment the initial
-		 * timeo here; but I'm not going to bother with this now
-		 * because this old nfsiod stuff will soon die anyway.
-		 */
-		result = -EAGAIN;
-	}
-
-	if (result == -EAGAIN && req->rq_retries--) {
-		dprintk("BIO: retransmitting request.\n");
-		memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
-		while (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0)
-			schedule();
-		current->fsuid = req->rq_fsuid;
-		current->fsgid = req->rq_fsgid;
-		for (i = 0; i < NGROUPS; i++)
-			current->groups[i] = req->rq_groups[i];
-		nfsiod_read_setup(req);
-		return 0;
-	}
-	if (result >= 0) {
-		set_bit(PG_uptodate, &page->flags);
-		succ++;
-	} else {
-		dprintk("BIO: %d successful reads, %d failures\n", succ, fail);
-		set_bit(PG_error, &page->flags);
-		fail++;
-	}
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
-	free_page(page_address(page));
-	return 1;
-}
-
-static inline int
-do_read_nfs_async(struct inode *inode, struct page *page)
-{
-	struct nfsiod_req *req;
-	int		result, i;
-
-	dprintk("NFS: do_read_nfs_async(%p)\n", page);
-
-	set_bit(PG_locked, &page->flags);
-	clear_bit(PG_error, &page->flags);
-
-	if (!(req = nfsiod_reserve(NFS_SERVER(inode))))
-		return -EAGAIN;
-
-	req->rq_retries = 5;
-	req->rq_callback = nfsiod_read_result;
-	req->rq_inode = inode;
-	req->rq_page = page;
-
-	req->rq_fsuid = current->fsuid;
-	req->rq_fsgid = current->fsgid;
-	for (i = 0; i < NGROUPS; i++)
-		req->rq_groups[i] = current->groups[i];
-
-	if ((result = nfsiod_read_setup(req)) >= 0) {
-		page->count++;
-		nfsiod_enqueue(req);
-	} else {
-		dprintk("NFS: deferring async READ request.\n");
-		nfsiod_release(req);
-		clear_bit(PG_locked, &page->flags);
-		wake_up(&page->wait);
-	}
-
-	return result < 0? result : 0;
-}
-
-int
-nfs_readpage(struct inode *inode, struct page *page)
-{
-	unsigned long	address;
-	int		error = -1;
-
-	/* In case we're called from a page fault we want to */
-	/* make sure we're runnable before we schedule.. */
-	current->state = TASK_RUNNING;
-	dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
-	address = page_address(page);
-	page->count++;
-	if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
-		error = do_read_nfs_async(inode, page);
-	if (error < 0)		/* couldn't enqueue */
-		error = do_read_nfs_sync(inode, page);
-	free_page(address);
-	return error;
-}
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.31/linux/fs/nfs/dir.c	Sun Jan 26 02:07:44 1997
+++ linux/fs/nfs/dir.c	Fri Apr  4 11:05:59 1997
@@ -6,19 +6,38 @@
  *  nfs directory handling functions
  *
  * 10 Apr 1996	Added silly rename for unlink	--okir
+ * 28 Sep 1996	Improved directory cache --okir
  */
 
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
-#include <linux/nfs_fs.h>
 #include <linux/fcntl.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/mm.h>
+#include <linux/sunrpc/types.h>
+#include <linux/nfs_fs.h>
 
-#include <asm/uaccess.h>	/* for fs functions */
+#include <asm/segment.h>	/* for fs functions */
+
+/*
+ * Head for a dircache entry. Currently still very simple; when
+ * the cache grows larger, we will need a LRU list.
+ */
+struct nfs_dirent {
+	dev_t			dev;		/* device number */
+	ino_t			ino;		/* inode number */
+	u32			cookie;		/* cooke of first entry */
+	unsigned short		valid  : 1,	/* data is valid */
+				locked : 1;	/* entry locked */
+	unsigned int		size;		/* # of entries */
+	unsigned long		age;		/* last used */
+	unsigned long		mtime;		/* last attr stamp */
+	struct wait_queue *	wait;
+	struct nfs_entry *	entry;
+};
 
 static int nfs_dir_open(struct inode * inode, struct file * file);
 static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long);
@@ -39,7 +58,7 @@
 	nfs_dir_read,		/* read - bad */
 	NULL,			/* write - bad */
 	nfs_readdir,		/* readdir */
-	NULL,			/* poll - default */
+	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	NULL,			/* mmap */
 	nfs_dir_open,		/* open - revalidate */
@@ -64,42 +83,26 @@
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	nfs_revalidate,		/* revalidate */
 };
 
-static inline void revalidate_dir(struct nfs_server * server, struct inode * dir)
-{
-	struct nfs_fattr fattr;
-
-	if (jiffies - NFS_READTIME(dir) < NFS_ATTRTIMEO(dir))
-		return;
-
-	NFS_READTIME(dir) = jiffies;
-	if (nfs_proc_getattr(server, NFS_FH(dir), &fattr) == 0) {
-		nfs_refresh_inode(dir, &fattr);
-		if (fattr.mtime.seconds == NFS_OLDMTIME(dir)) {
-			if ((NFS_ATTRTIMEO(dir) <<= 1) > server->acdirmax)
-				NFS_ATTRTIMEO(dir) = server->acdirmax;
-			return;
-		}
-		NFS_OLDMTIME(dir) = fattr.mtime.seconds;
-	}
-	/* invalidate directory cache here when we _really_ start caching */
-}
-
-static int nfs_dir_open(struct inode * dir, struct file * file)
+static int
+nfs_dir_open(struct inode *dir, struct file *file)
 {
-	revalidate_dir(NFS_SERVER(dir), dir);
-	return 0;
+	dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino);
+	return nfs_revalidate_inode(NFS_SERVER(dir), dir);
 }
 
-static long nfs_dir_read(struct inode *inode, struct file *filp,
-	char *buf, unsigned long count)
+static long
+nfs_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
 {
 	return -EISDIR;
 }
 
-static struct nfs_entry *c_entry = NULL;
+static struct nfs_dirent	dircache[NFS_MAX_DIRCACHE];
 
 /*
  * We need to do caching of directory entries to prevent an
@@ -107,119 +110,222 @@
  * directory is cached.  This seems sufficient for most purposes.
  * Technically, we ought to flush the cache on close but this is
  * not a problem in practice.
+ *
+ * XXX: Do proper directory caching by stuffing data into the
+ * page cache (may require some fiddling for rsize < PAGE_SIZE).
  */
 
-static int nfs_readdir(struct inode *inode, struct file *filp,
-		       void *dirent, filldir_t filldir)
-{
-	static kdev_t c_dev = 0;
-	static int c_ino;
-	static int c_size;
-
-	int result;
-	int i, index = 0;
-	struct nfs_entry *entry;
+static int
+nfs_readdir(struct inode *inode, struct file *filp, void *dirent,
+						filldir_t filldir)
+{
+	static struct wait_queue *readdir_wait = NULL;
+	struct wait_queue	**waitp = NULL;
+	struct nfs_dirent	*cache, *free;
+	struct nfs_entry	*entry;
+	unsigned long		age, dead;
+	u32			cookie;
+	int			ismydir, result;
+	int			i, j, index = 0;
 
+	dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
 	if (!inode || !S_ISDIR(inode->i_mode)) {
 		printk("nfs_readdir: inode is NULL or not a directory\n");
 		return -EBADF;
 	}
 
-	revalidate_dir(NFS_SERVER(inode), inode);
+	if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
+		return result;
 
-	/* initialize cache memory if it hasn't been used before */
+	/*
+	 * Try to find the entry in the cache
+	 */
+again:
+	if (waitp) {
+		interruptible_sleep_on(waitp);
+		if (current->signal & ~current->blocked)
+			return -ERESTARTSYS;
+		waitp = NULL;
+	}
+
+	cookie = filp->f_pos;
+	entry  = NULL;
+	free   = NULL;
+	age    = ~(unsigned long) 0;
+	dead   = jiffies - NFS_ATTRTIMEO(inode);
+
+	for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
+		/*
+		dprintk("NFS: dircache[%d] valid %d locked %d\n",
+					i, cache->valid, cache->locked);
+		 */
+		ismydir = (cache->dev == inode->i_dev
+				&& cache->ino == inode->i_ino);
+		if (cache->locked) {
+			if (!ismydir || cache->cookie != cookie)
+				continue;
+			dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
+			waitp = &cache->wait;
+			goto again;
+		}
 
-	if (c_entry == NULL) {
-		i = sizeof (struct nfs_entry)*NFS_READDIR_CACHE_SIZE;
-		c_entry = (struct nfs_entry *) kmalloc(i, GFP_KERNEL);
-		if (c_entry == NULL) {
-			printk("nfs_readdir: no MEMORY for cache\n");
-			return -ENOMEM;
-		}
-		for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) {
-			c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1,
-				GFP_KERNEL);
-			if (c_entry[i].name == NULL) {
-				printk("nfs_readdir: no MEMORY for cache\n");
-				while (--i>=0)
-					kfree(c_entry[i].name);
-				kfree(c_entry);
-				c_entry = NULL;
-				return -ENOMEM;
-			}
+		if (ismydir && cache->mtime != NFS_OLDMTIME(inode))
+			cache->valid = 0;
+
+		if (!cache->valid || cache->age < dead) {
+			free = cache;
+			age  = 0;
+		} else if (cache->age < age) {
+			free = cache;
+			age  = cache->age;
 		}
-	}
-	entry = NULL;
 
-	/* try to find it in the cache */
+		if (!ismydir || !cache->valid)
+			continue;
 
-	if (inode->i_dev == c_dev && inode->i_ino == c_ino) {
-		for (i = 0; i < c_size; i++) {
-			if (filp->f_pos == c_entry[i].cookie) {
-				if (i == c_size - 1) {
-					if (c_entry[i].eof)
-						return 0;
-				}
-				else
-					entry = c_entry + (index = i + 1);
-				break;
+		if (cache->cookie == cookie && cache->size > 0) {
+			entry = cache->entry + (index = 0);
+			cache->locked = 1;
+			break;
+		}
+		for (j = 0; j < cache->size; j++) {
+			/*
+			dprintk("NFS: examing entry %.*s @%d\n",
+				(int) cache->entry[j].length,
+				cache->entry[j].name,
+				cache->entry[j].cookie);
+			 */
+			if (cache->entry[j].cookie != cookie)
+				continue;
+			if (j < cache->size - 1) {
+				entry = cache->entry + (index = j + 1);
+			} else if (cache->entry[j].eof) {
+				return 0;
 			}
+			break;
+		}
+		if (entry) {
+			dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
+						cache - dircache);
+			cache->locked = 1;
+			break;
 		}
 	}
 
-	/* if we didn't find it in the cache, revert to an nfs call */
-
-	if (!entry) {
-		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
-			filp->f_pos, NFS_READDIR_CACHE_SIZE, c_entry);
-		if (result < 0) {
-			c_dev = 0;
-			return result;
+	/*
+	 * Okay, entry not present in cache, or locked and inaccessible.
+	 * Set up the cache entry and attempt a READDIR call.
+	 */
+	if (entry == NULL) {
+		if ((cache = free) == NULL) {
+			dfprintk(DIRCACHE, "NFS: dircache contention\n");
+			waitp = &readdir_wait;
+			goto again;
 		}
-		if (result > 0) {
-			c_dev = inode->i_dev;
-			c_ino = inode->i_ino;
-			c_size = result;
-			entry = c_entry + (index = 0);
+		dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
+				free - dircache);
+		cache->cookie = cookie;
+		cache->locked = 1;
+		cache->valid  = 0;
+		cache->dev    = inode->i_dev;
+		cache->ino    = inode->i_ino;
+		if (!cache->entry) {
+			cache->entry = (struct nfs_entry *)
+						get_free_page(GFP_KERNEL);
+			if (!cache->entry) {
+				result = -ENOMEM;
+				goto done;
+			}
 		}
-	}
 
-	/* if we found it in the cache or from an nfs call, return results */
-	if (!entry)
-		return 0;
-	while (index < c_size) {
-		int nextpos = entry->cookie;
-		if (filldir(dirent, entry->name, strlen(entry->name), filp->f_pos, entry->fileid) < 0)
-			break;
-		filp->f_pos = nextpos;
-		/* revalidate the cache if we slept in filldir() */
-		if (inode->i_dev != c_dev)
-			break;
-		if (inode->i_ino != c_ino)
+		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+					cookie, PAGE_SIZE, cache->entry);
+		if (result <= 0)
+			goto done;
+		cache->size  = result;
+		cache->valid = 1;
+		entry = cache->entry + (index = 0);
+	}
+	cache->mtime = NFS_OLDMTIME(inode);
+	cache->age = jiffies;
+
+	/*
+	 * Yowza! We have a cache entry...
+	 */
+	while (index < cache->size) {
+		int	nextpos = entry->cookie;
+
+		/*
+		dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
+				(int) entry->length, entry->name, entry->length,
+				(unsigned int) filp->f_pos,
+				entry->fileid, entry->eof);
+		 */
+
+		if (filldir(dirent, entry->name, entry->length, cookie, entry->fileid) < 0)
 			break;
-		if (nextpos != entry->cookie)
+		cookie = nextpos;
+		if (nextpos != entry->cookie) {
+			printk("nfs_readdir: shouldn't happen!\n");
 			break;
+		}
 		index++;
 		entry++;
 	}
-	return 0;
+	filp->f_pos = cookie;
+	result = 0;
+
+	/* XXX: May want to kick async readdir-ahead here. Not too hard
+	 * to do. */
+
+done:
+	dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
+	cache->locked = 0;
+	wake_up(&cache->wait);
+	wake_up(&readdir_wait);
+
+	return result;
 }
 
 /*
- * free cache memory
- * called from cleanup_module
+ * Invalidate dircache entries for inode
  */
+void
+nfs_invalidate_dircache(struct inode *inode)
+{
+	struct nfs_dirent *cache;
+	dev_t		dev = inode->i_dev;
+	ino_t		ino = inode->i_ino;
+	int		i;
+
+	dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, ino);
+	for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
+		if (!cache->locked && cache->dev == dev && cache->ino == ino)
+			cache->valid = 0;	/* brute force */
+	}
+}
 
-void nfs_kfree_cache(void)
+/*
+ * Free directory cache memory
+ * Called from cleanup_module
+ */
+void
+nfs_free_dircache(void)
 {
-	int i;
+	struct nfs_dirent *cache;
+	int		i;
 
-	if (c_entry == NULL)
-		return;
-	for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++)
-		kfree(c_entry[i].name);
-	kfree(c_entry);
-	c_entry = NULL;
+	dfprintk(DIRCACHE, "NFS: freeing dircache\n");
+	for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
+		cache->valid = 0;
+		if (cache->locked) {
+			printk("nfs_kfree_cache: locked entry in dircache!\n");
+			continue;
+		}
+		if (cache->entry)
+			free_page((unsigned long) cache->entry);
+		cache->entry = NULL;
+	}
 }
  
 
@@ -234,15 +340,18 @@
  * Since the cache is not hashed yet, it is a good idea not to make it too
  * large because every lookup looks through the entire cache even
  * though most of them will fail.
+ *
+ * FIXME: The lookup cache should also cache failed lookups. This can
+ * be a considerable win on diskless clients.
  */
 
 static struct nfs_lookup_cache_entry {
-	kdev_t dev;
-	int inode;
-	char filename[NFS_MAXNAMLEN + 1];
-	struct nfs_fh fhandle;
+	kdev_t		dev;
+	ino_t		inode;
+	char		filename[NFS_MAXNAMLEN + 1];
+	struct nfs_fh	fhandle;
 	struct nfs_fattr fattr;
-	int expiration_date;
+	int		expiration_date;
 } nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE];
 
 static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
@@ -269,6 +378,8 @@
 
 	struct nfs_lookup_cache_entry *entry;
 
+	dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n",
+				dir->i_dev, dir->i_ino, filename);
 	if (!nfs_lookup_cache_in_use) {
 		memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache));
 		nfs_lookup_cache_in_use = 1;
@@ -292,6 +403,9 @@
 	static int nfs_lookup_cache_pos = 0;
 	struct nfs_lookup_cache_entry *entry;
 
+	dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n",
+				dir->i_dev, dir->i_ino, filename);
+
 	/* compensate for bug in SGI NFS server */
 	if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1
 	    || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1)
@@ -301,6 +415,7 @@
 		if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE)
 			nfs_lookup_cache_pos = 0;
 	}
+
 	entry->dev = dir->i_dev;
 	entry->inode = dir->i_ino;
 	strcpy(entry->filename, filename);
@@ -314,9 +429,9 @@
 				    const char *filename)
 {
 	struct nfs_lookup_cache_entry *entry;
-	kdev_t dev;
-	int fileid;
-	int i;
+	kdev_t	dev;
+	ino_t	fileid;
+	int	i;
 
 	if (inode) {
 		dev = inode->i_dev;
@@ -328,6 +443,10 @@
 	}
 	else
 		return;
+
+	dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n",
+				dev, fileid);
+
 	for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
 		entry = nfs_lookup_cache + i;
 		if (entry->dev == dev && entry->fattr.fileid == fileid)
@@ -358,6 +477,9 @@
 	char name[len > NFS_MAXNAMLEN? 1 : len+1];
 	int error;
 
+	dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
+				dir->i_dev, dir->i_ino, len, __name);
+
 	*result = NULL;
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_lookup: inode is NULL or not a directory\n");
@@ -399,6 +521,9 @@
 	struct nfs_fh fhandle;
 	int error;
 
+	dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
+				dir->i_dev, dir->i_ino, name);
+
 	*result = NULL;
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_create: inode is NULL or not a directory\n");
@@ -422,6 +547,7 @@
 		return -EACCES;
 	}
 	nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+	nfs_invalidate_dircache(dir);
 	iput(dir);
 	return 0;
 }
@@ -434,6 +560,9 @@
 	struct nfs_fh fhandle;
 	int error;
 
+	dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
+				dir->i_dev, dir->i_ino, name);
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_mknod: inode is NULL or not a directory\n");
 		iput(dir);
@@ -453,12 +582,8 @@
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
 		name, &sattr, &fhandle, &fattr);
 	if (!error)
-	{
 		nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
-		/* The parent dir inode count may have changed ! */
-		nfs_lookup_cache_remove( NULL, dir, NULL);
-	}
-		
+	nfs_invalidate_dircache(dir);
 	iput(dir);
 	return error;
 }
@@ -470,6 +595,9 @@
 	struct nfs_fh fhandle;
 	int error;
 
+	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
+				dir->i_dev, dir->i_ino, name);
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_mkdir: inode is NULL or not a directory\n");
 		iput(dir);
@@ -484,12 +612,9 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
 		name, &sattr, &fhandle, &fattr);
-	if (!error) {
-		if (fattr.fileid == dir->i_ino)
-			printk("Sony NewsOS 4.1R buggy nfs server?\n");
-		else
-			nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
-	}
+	if (!error)
+		nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+	nfs_invalidate_dircache(dir);
 	iput(dir);
 	return error;
 }
@@ -498,6 +623,9 @@
 {
 	int error;
 
+	dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
+				dir->i_dev, dir->i_ino, name);
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_rmdir: inode is NULL or not a directory\n");
 		iput(dir);
@@ -508,7 +636,9 @@
 		return -ENAMETOOLONG;
 	}
 	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name);
-	nfs_lookup_cache_remove(dir, NULL, name);
+	if (!error)
+		nfs_lookup_cache_remove(dir, NULL, name);
+	nfs_invalidate_dircache(dir);
 	iput(dir);
 	return error;
 }
@@ -522,46 +652,78 @@
 	dir->i_count++;
 	if (nfs_lookup(dir, name, len, &inode) < 0)
 		return -EIO;		/* arbitrary */
-	if (inode->i_count == 1 || NFS_RENAMED_DIR(inode)) {
+
+	if (inode->i_count == 1) {
+		iput(inode);
+		return -EIO;
+	}
+	if (NFS_RENAMED_DIR(inode)) {
+		iput(NFS_RENAMED_DIR(inode));
+		NFS_RENAMED_DIR(inode) = NULL;
 		iput(inode);
 		return -EIO;
 	}
-	slen = sprintf(silly, ".nfs%ld", inode->i_ino);
 
+	slen = sprintf(silly, ".nfs%ld", inode->i_ino);
 	if (len == slen && !strncmp(name, silly, len)) {
 		iput(inode);
 		return -EIO;		/* DWIM */
 	}
+
+	dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n",
+				dir->i_dev, dir->i_ino, name);
+
 	ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name,
-					       NFS_FH(dir), silly, 0);
+					       NFS_FH(dir), silly);
 	if (ret >= 0) {
 		nfs_lookup_cache_remove(dir, NULL, name);
 		nfs_lookup_cache_remove(dir, NULL, silly);
 		NFS_RENAMED_DIR(inode) = dir;
 		dir->i_count++;
 	}
+	nfs_invalidate_dircache(dir);
 	iput(inode);
 	return ret;
 }
 
+/*
+ * When releasing the inode, finally remove any unlinked but open files.
+ * Note that we have to clear the set of pending signals temporarily;
+ * otherwise the RPC call will fail.
+ */
 void nfs_sillyrename_cleanup(struct inode *inode)
 {
+	unsigned long	oldsig;
 	struct inode	*dir = NFS_RENAMED_DIR(inode);
 	char		silly[14];
 	int		error, slen;
 
+	dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n",
+				inode->i_dev, inode->i_ino);
+
+	oldsig = current->signal;
+	current->signal = 0;
+
 	slen = sprintf(silly, ".nfs%ld", inode->i_ino);
-	if ((error = nfs_unlink(dir, silly, slen)) < 0) {
-		printk("NFS silly_rename cleanup failed (err = %d)\n",
-					-error);
-	}
+	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly);
+	if (error < 0)
+		printk("NFS: silly_rename cleanup failed (err %d)\n", -error);
+
+	nfs_lookup_cache_remove(dir, NULL, silly);
+	nfs_invalidate_dircache(dir);
 	NFS_RENAMED_DIR(inode) = NULL;
+	iput(dir);
+
+	current->signal |= oldsig;
 }
 
 static int nfs_unlink(struct inode *dir, const char *name, int len)
 {
 	int error;
 
+	dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
+				dir->i_dev, dir->i_ino, name);
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_unlink: inode is NULL or not a directory\n");
 		iput(dir);
@@ -573,8 +735,10 @@
 	}
 	if ((error = nfs_sillyrename(dir, name, len)) < 0) {
 		error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
-		nfs_lookup_cache_remove(dir, NULL, name);
+		if (!error)
+			nfs_lookup_cache_remove(dir, NULL, name);
 	}
+	nfs_invalidate_dircache(dir);
 	iput(dir);
 	return error;
 }
@@ -585,6 +749,9 @@
 	struct nfs_sattr sattr;
 	int error;
 
+	dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
+				dir->i_dev, dir->i_ino, name, symname);
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_symlink: inode is NULL or not a directory\n");
 		iput(dir);
@@ -603,6 +770,7 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
 		name, symname, &sattr);
+	nfs_invalidate_dircache(dir);
 	iput(dir);
 	return error;
 }
@@ -612,6 +780,10 @@
 {
 	int error;
 
+	dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
+				oldinode->i_dev, oldinode->i_ino,
+				dir->i_dev, dir->i_ino, name);
+
 	if (!oldinode) {
 		printk("nfs_link: old inode is NULL\n");
 		iput(oldinode);
@@ -631,19 +803,35 @@
 	}
 	error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode),
 		NFS_FH(dir), name);
-
-	nfs_lookup_cache_remove(dir, oldinode, NULL);
+	if (!error) {
+		nfs_lookup_cache_remove(dir, oldinode, NULL);
+		NFS_READTIME(oldinode) = 0;	/* force getattr */
+	}
+	nfs_invalidate_dircache(dir);
 	iput(oldinode);
 	iput(dir);
 	return error;
 }
 
+/*
+ * RENAME
+ * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
+ * different file handle for the same inode after a rename (e.g. when
+ * moving to a different directory). A fail-safe method to do so would
+ * be to look up old_dir/old_name, create a link to new_dir/new_name and
+ * rename the old file using the silly_rename stuff. This way, the original
+ * file in old_dir will go away when the last process iput()s the inode.
+ */
 static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
 		      struct inode *new_dir, const char *new_name, int new_len,
 		      int must_be_dir)
 {
 	int error;
 
+	dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n",
+				old_dir->i_dev, old_dir->i_ino, old_name,
+				new_dir->i_dev, new_dir->i_ino, new_name);
+
 	if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
 		printk("nfs_rename: old inode is NULL or not a directory\n");
 		iput(old_dir);
@@ -661,13 +849,20 @@
 		iput(new_dir);
 		return -ENAMETOOLONG;
 	}
+
+	/* We don't do rename() with trailing slashes over NFS now. Hmm. */
+	if (must_be_dir)
+		return -EINVAL;
+
 	error = nfs_proc_rename(NFS_SERVER(old_dir),
 		NFS_FH(old_dir), old_name,
-		NFS_FH(new_dir), new_name,
-		must_be_dir);
-
-	nfs_lookup_cache_remove(old_dir, NULL, old_name);
-	nfs_lookup_cache_remove(new_dir, NULL, new_name);
+		NFS_FH(new_dir), new_name);
+	if (!error) {
+		nfs_lookup_cache_remove(old_dir, NULL, old_name);
+		nfs_lookup_cache_remove(new_dir, NULL, new_name);
+	}
+	nfs_invalidate_dircache(old_dir);
+	nfs_invalidate_dircache(new_dir);
 	iput(old_dir);
 	iput(new_dir);
 	return error;
@@ -683,6 +878,9 @@
 {
 	int was_empty;
 
+	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
+				inode->i_dev, inode->i_ino, inode->i_count);
+
 	if (!inode || !fattr) {
 		printk("nfs_refresh_inode: inode or fattr is NULL\n");
 		return;
@@ -698,10 +896,16 @@
 	inode->i_gid = fattr->gid;
 
 	/* Size changed from outside: invalidate caches on next read */
-	if (inode->i_size != fattr->size)
+	if (inode->i_size != fattr->size) {
+		dfprintk(PAGECACHE, "NFS:      cacheinv(%x/%ld)\n",
+					inode->i_dev, inode->i_ino);
 		NFS_CACHEINV(inode);
-	if (NFS_OLDMTIME(inode) != fattr->mtime.seconds)
+	}
+	if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) {
+		dfprintk(PAGECACHE, "NFS:      mtime change on %x/%ld\n",
+					inode->i_dev, inode->i_ino);
 		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+	}
 	inode->i_size = fattr->size;
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		inode->i_rdev = to_kdev_t(fattr->rdev);
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v2.1.31/linux/fs/nfs/file.c	Sun Jan 26 02:07:44 1997
+++ linux/fs/nfs/file.c	Fri Apr  4 11:05:59 1997
@@ -21,30 +21,40 @@
 #include <linux/errno.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
-#include <linux/mm.h>
 #include <linux/nfs_fs.h>
+#include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
+#include <linux/lockd/bind.h>
 
-#include <asm/uaccess.h>
+#include <asm/segment.h>
 #include <asm/system.h>
 
-static int nfs_file_mmap(struct inode *, struct file *, struct vm_area_struct *);
+#define NFSDBG_FACILITY		NFSDBG_FILE
+
+static int  nfs_file_mmap(struct inode *, struct file *,
+					struct vm_area_struct *);
 static long nfs_file_read(struct inode *, struct file *, char *, unsigned long);
-static long nfs_file_write(struct inode *, struct file *, const char *, unsigned long);
-static int nfs_fsync(struct inode *, struct file *);
+static long nfs_file_write(struct inode *, struct file *,
+					const char *, unsigned long);
+static int  nfs_file_close(struct inode *, struct file *);
+static int  nfs_fsync(struct inode *, struct file *);
 
 static struct file_operations nfs_file_operations = {
 	NULL,			/* lseek - default */
 	nfs_file_read,		/* read */
 	nfs_file_write,		/* write */
 	NULL,			/* readdir - bad */
-	NULL,			/* poll - default */
+	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	nfs_file_mmap,		/* mmap */
 	NULL,			/* no special open is needed */
-	NULL,			/* release */
+	nfs_file_close,		/* release */
 	nfs_fsync,		/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL,			/* revalidate */
+	nfs_lock,		/* lock */
 };
 
 struct inode_operations nfs_file_inode_operations = {
@@ -61,97 +71,156 @@
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
 	nfs_readpage,		/* readpage */
-	NULL,			/* writepage */
+	nfs_writepage,		/* writepage */
 	NULL,			/* bmap */
-	NULL			/* truncate */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	nfs_updatepage,		/* updatepage */
+	nfs_revalidate,		/* revalidate */
 };
 
-static inline void revalidate_inode(struct nfs_server * server, struct inode * inode)
+/* Hack for future NFS swap support */
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode)	(0)
+#endif
+
+
+static int
+nfs_file_close(struct inode *inode, struct file *file)
 {
-	struct nfs_fattr fattr;
+	int	status;
 
-	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
-		return;
+	dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
-	NFS_READTIME(inode) = jiffies;
-	if (nfs_proc_getattr(server, NFS_FH(inode), &fattr) == 0) {
-		nfs_refresh_inode(inode, &fattr);
-		if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
-			if ((NFS_ATTRTIMEO(inode) <<= 1) > server->acregmax)
-				NFS_ATTRTIMEO(inode) = server->acregmax;
-			return;
-		}
-		NFS_OLDMTIME(inode) = fattr.mtime.seconds;
-	}
-	invalidate_inode_pages(inode);
+	if ((status = nfs_flush_dirty_pages(inode, 0, 0)) < 0)
+		return status;
+	return nfs_write_error(inode);
 }
 
-
-static long nfs_file_read(struct inode * inode, struct file * file,
-	char * buf, unsigned long count)
+static long
+nfs_file_read(struct inode * inode, struct file * file,
+				char * buf, unsigned long count)
 {
-	revalidate_inode(NFS_SERVER(inode), inode);
+	int	status;
+
+	dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
+			inode->i_dev, inode->i_ino, count,
+			(unsigned long) file->f_pos);
+
+	if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
+		return status;
 	return generic_file_read(inode, file, buf, count);
 }
 
-static int nfs_file_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+static int
+nfs_file_mmap(struct inode * inode, struct file * file,
+				struct vm_area_struct * vma)
 {
-	revalidate_inode(NFS_SERVER(inode), inode);
+	int	status;
+
+	dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+	if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
+		return status;
 	return generic_file_mmap(inode, file, vma);
 }
 
 static int nfs_fsync(struct inode *inode, struct file *file)
 {
-	return 0;
+	dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+	return nfs_flush_dirty_pages(inode, 0, 0);
 }
 
-static long nfs_file_write(struct inode *inode, struct file *file,
-	const char *buf, unsigned long count)
+/* 
+ * Write to a file (through the page cache).
+ */
+static long
+nfs_file_write(struct inode *inode, struct file *file,
+			const char *buf, unsigned long count)
 {
-	int result, written, wsize;
-	struct nfs_fattr fattr;
-	unsigned long pos;
+	int	result;
+
+	dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
+			inode->i_dev, inode->i_ino, inode->i_count,
+			count, (unsigned long) file->f_pos);
 
 	if (!inode) {
 		printk("nfs_file_write: inode = NULL\n");
 		return -EINVAL;
 	}
+	if (IS_SWAPFILE(inode)) {
+		printk("NFS: attempt to write to active swap file!\n");
+		return -EBUSY;
+	}
+	if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
+		return result;
 	if (!S_ISREG(inode->i_mode)) {
 		printk("nfs_file_write: write to non-file, mode %07o\n",
 			inode->i_mode);
 		return -EINVAL;
 	}
-	if (count == 0)
+	if (count <= 0)
 		return 0;
 
-	pos = file->f_pos;
-	if (file->f_flags & O_APPEND)
-		pos = inode->i_size;
-	wsize = NFS_SERVER(inode)->wsize;
-	result = 0;
-	written = 0;
-	while (written < count) {
-		int hunk = count - written;
-		if (hunk >= wsize)
-			hunk = wsize;
-		result = nfs_proc_write(inode,
-			pos, hunk, buf, &fattr);
-		if (result < 0)
-			break;
-		pos += hunk;
-		buf += hunk;
-		written += hunk;
-		if (hunk < wsize)
-			break;
-	}
-	if (!written)
+	/* Return error from previous async call */
+	if ((result = nfs_write_error(inode)) < 0)
 		return result;
-	file->f_pos = pos;
-	if (pos > inode->i_size)
-		inode->i_size = pos;
-	/* Avoid possible Solaris 2.5 nfsd bug */
-	if (inode->i_ino == fattr.fileid)
-		nfs_refresh_inode(inode, &fattr);
-	return written;
+
+	return generic_file_write(inode, file, buf, count);
 }
 
+/*
+ * Lock a (portion of) a file
+ */
+int
+nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl)
+{
+	int	status;
+
+	dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
+			filp->f_inode->i_dev, filp->f_inode->i_ino,
+			fl->fl_type, fl->fl_flags,
+			fl->fl_start, fl->fl_end);
+
+	if (!(inode = filp->f_inode))
+		return -EINVAL;
+
+	/* No mandatory locks over NFS */
+	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return -ENOLCK;
+
+	/* Fake OK code if mounted without NLM support */
+	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
+		return 0;
+
+	/*
+	 * No BSD flocks over NFS allowed.
+	 * Note: we could try to fake a POSIX lock request here by
+	 * using ((u32) filp | 0x80000000) or some such as the pid.
+	 * Not sure whether that would be unique, though, or whether
+	 * that would break in other places.
+	 */
+	if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX)
+		return -ENOLCK;
+
+	/* If unlocking a file region, flush dirty pages (unless we've
+	 * been killed by a signal, that is). */
+	if (cmd == F_SETLK && fl->fl_type == F_UNLCK
+	 && !(current->signal & ~current->blocked)) {
+		status = nfs_flush_dirty_pages(inode,
+			fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 :
+			fl->fl_end - fl->fl_start + 1);
+		if (status < 0)
+			return status;
+	}
+
+	if ((status = nlmclnt_proc(inode, cmd, fl)) < 0)
+		return status;
+
+	/* Here, we could turn off write-back of pages in the
+	 * locked file region */
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.31/linux/fs/nfs/inode.c	Sun Jan 26 02:07:44 1997
+++ linux/fs/nfs/inode.c	Sat Apr  5 12:41:04 1997
@@ -17,25 +17,21 @@
 #include <linux/module.h>
 
 #include <linux/sched.h>
-#include <linux/nfs_fs.h>
-#include <linux/nfsiod.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+#include <linux/lockd/bind.h>
 
 #include <asm/system.h>
-#include <asm/uaccess.h>
-
-/* This is for kernel_thread */
-#define __KERNEL_SYSCALLS__
-#include <linux/unistd.h>
+# include <asm/uaccess.h>
 
-extern int close_fp(struct file *filp);
+#define NFSDBG_FACILITY		NFSDBG_VFS
 
 static int nfs_notify_change(struct inode *, struct iattr *);
 static void nfs_put_inode(struct inode *);
@@ -43,7 +39,7 @@
 static void nfs_read_inode(struct inode *);
 static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz);
 
-static struct super_operations nfs_sops = {
+static struct super_operations nfs_sops = { 
 	nfs_read_inode,		/* read inode */
 	nfs_notify_change,	/* notify change */
 	NULL,			/* write inode */
@@ -54,6 +50,7 @@
 	NULL
 };
 
+
 /*
  * The "read_inode" function doesn't actually do anything:
  * the real data is filled in later in nfs_fhget. Here we
@@ -61,31 +58,36 @@
  * (the latter makes "nfs_refresh_inode" do the right thing
  * wrt pipe inodes)
  */
-static void nfs_read_inode(struct inode * inode)
+static void
+nfs_read_inode(struct inode * inode)
 {
-	int rsize = inode->i_sb->u.nfs_sb.s_server.rsize;
-	int size = inode->i_sb->u.nfs_sb.s_server.wsize;
-
-	if (rsize > size)
-		size = rsize;
-	inode->i_blksize = size;
+	inode->i_blksize = inode->i_sb->s_blocksize;
 	inode->i_mode = 0;
 	inode->i_op = NULL;
 	NFS_CACHEINV(inode);
 }
 
-static void nfs_put_inode(struct inode * inode)
+static void
+nfs_put_inode(struct inode * inode)
 {
+	dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
 	if (NFS_RENAMED_DIR(inode))
 		nfs_sillyrename_cleanup(inode);
 	if (inode->i_pipe)
 		clear_inode(inode);
 }
 
-void nfs_put_super(struct super_block *sb)
+void
+nfs_put_super(struct super_block *sb)
 {
-	close_fp(sb->u.nfs_sb.s_server.file);
-	rpc_closesock(sb->u.nfs_sb.s_server.rsock);
+	struct rpc_clnt	*rpc;
+
+	if ((rpc = sb->u.nfs_sb.s_server.client) != NULL)
+		rpc_shutdown_client(rpc);
+
+	lockd_down();		/* release rpc.lockd */
+	rpciod_down();		/* release rpciod */
 	lock_super(sb);
 	sb->s_dev = 0;
 	unlock_super(sb);
@@ -93,22 +95,50 @@
 }
 
 /*
- * The way this works is that the mount process passes a structure
- * in the data argument which contains an open socket to the NFS
- * server and the root file handle obtained from the server's mount
- * daemon.  We stash these away in the private superblock fields.
- * Later we can add other mount parameters like caching values.
+ * Compute and set NFS server blocksize
  */
-
-struct super_block *nfs_read_super(struct super_block *sb, void *raw_data,
-				   int silent)
+static unsigned int
+nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
 {
-	struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
-	struct nfs_server *server;
-	unsigned int fd;
-	struct file *filp;
+	if (bsize < 1024)
+		bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+	else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
+		bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+
+	/* make sure blocksize is a power of two */
+	if ((bsize & (bsize - 1)) || nrbitsp) {
+		unsigned int	nrbits;
+
+		for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--)
+			;
+		bsize = 1 << nrbits;
+		if (nrbitsp)
+			*nrbitsp = nrbits;
+		if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE)
+			bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+	}
+
+	return bsize;
+}
 
-	kdev_t dev = sb->s_dev;
+/*
+ * The way this works is that the mount process passes a structure
+ * in the data argument which contains the server's IP address
+ * and the root file handle obtained from the server's mount
+ * daemon. We stash these away in the private superblock fields.
+ */
+struct super_block *
+nfs_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+	struct nfs_mount_data	*data = (struct nfs_mount_data *) raw_data;
+	struct sockaddr_in	srvaddr;
+	struct nfs_server	*server;
+	struct rpc_timeout	timeparms;
+	struct rpc_xprt		*xprt;
+	struct rpc_clnt		*clnt;
+	unsigned int		authflavor;
+	int			tcp;
+	kdev_t			dev = sb->s_dev;
 
 	MOD_INC_USE_COUNT;
 	if (!data) {
@@ -117,97 +147,106 @@
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
-	fd = data->fd;
 	if (data->version != NFS_MOUNT_VERSION) {
 		printk("nfs warning: mount version %s than kernel\n",
 			data->version < NFS_MOUNT_VERSION ? "older" : "newer");
+		if (data->version < 2)
+			data->namlen = 0;
+		if (data->version < 3)
+			data->bsize  = 0;
 	}
-	if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) {
-		printk("nfs_read_super: invalid file descriptor\n");
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	if (!S_ISSOCK(filp->f_inode->i_mode)) {
-		printk("nfs_read_super: not a socket\n");
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	filp->f_count++;
+
 	lock_super(sb);
 
-	sb->s_blocksize = 1024; /* XXX */
-	sb->s_blocksize_bits = 10;
-	sb->s_magic = NFS_SUPER_MAGIC;
-	sb->s_dev = dev;
-	sb->s_op = &nfs_sops;
-	server = &sb->u.nfs_sb.s_server;
-	server->file = filp;
-	server->lock = 0;
-	server->wait = NULL;
-	server->flags = data->flags;
-	server->rsize = data->rsize;
-	if (server->rsize <= 0)
-		server->rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	else if (server->rsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
-		server->rsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
-	server->wsize = data->wsize;
-	if (server->wsize <= 0)
-		server->wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
-	else if (server->wsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
-		server->wsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
-	server->timeo = data->timeo*HZ/10;
-	server->retrans = data->retrans;
+	server           = &sb->u.nfs_sb.s_server;
+	sb->s_magic      = NFS_SUPER_MAGIC;
+	sb->s_dev        = dev;
+	sb->s_op         = &nfs_sops;
+	sb->s_blocksize  = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
+	server->rsize    = nfs_block_size(data->rsize, NULL);
+	server->wsize    = nfs_block_size(data->wsize, NULL);
+	server->flags    = data->flags;
 	server->acregmin = data->acregmin*HZ;
 	server->acregmax = data->acregmax*HZ;
 	server->acdirmin = data->acdirmin*HZ;
 	server->acdirmax = data->acdirmax*HZ;
 	strcpy(server->hostname, data->hostname);
+	sb->u.nfs_sb.s_root = data->root;
+
+	/* We now require that the mount process passes the remote address */
+	memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
+	if (srvaddr.sin_addr.s_addr == INADDR_ANY) {
+		printk("NFS: mount program didn't pass remote address!\n");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
 
-	/* Start of JSP NFS patch */
-	/* Check if passed address in data->addr */
-	if (data->addr.sin_addr.s_addr == INADDR_ANY) {  /* No address passed */
-	  if (((struct sockaddr_in *)(&server->toaddr))->sin_addr.s_addr == INADDR_ANY) {
-	    printk("NFS: Error passed unconnected socket and no address\n") ;
-	    MOD_DEC_USE_COUNT;
-	    return NULL ;
-	  } else {
-	    /* Need access to socket internals  JSP */
-	    struct socket *sock;
-	    int dummylen ;
-
-	 /*   printk("NFS: using socket address\n") ;*/
-
-	    sock = &((filp->f_inode)->u.socket_i);
-
-	    /* extract the other end of the socket into server->toaddr */
-	    sock->ops->getname(sock, &(server->toaddr), &dummylen, 1) ;
-	  }
+	/* Which protocol do we use? */
+	tcp   = (data->flags & NFS_MOUNT_TCP);
+
+	/* Initialize timeout values */
+	timeparms.to_initval = data->timeo * HZ / 10;
+	timeparms.to_retries = data->retrans;
+	timeparms.to_maxval  = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
+	timeparms.to_exponential = 1;
+
+	/* Choose authentication flavor */
+	if (data->flags & NFS_MOUNT_SECURE) {
+		authflavor = RPC_AUTH_DES;
+	} else if (data->flags & NFS_MOUNT_KERBEROS) {
+		authflavor = RPC_AUTH_KRB;
 	} else {
-	/*  printk("NFS: copying passed addr to server->toaddr\n") ;*/
-	  memcpy((char *)&(server->toaddr),(char *)(&data->addr),sizeof(server->toaddr));
+		authflavor = RPC_AUTH_UNIX;
 	}
-	/* End of JSP NFS patch */
 
-	if ((server->rsock = rpc_makesock(filp)) == NULL) {
-		printk("NFS: cannot create RPC socket.\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+	/* Now create transport and client */
+	xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
+						&srvaddr, &timeparms);
+	if (xprt == NULL) {
+		printk("NFS: cannot create RPC transport.\n");
+		goto failure;
 	}
 
-	sb->u.nfs_sb.s_root = data->root;
+	clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
+						NFS_VERSION, authflavor);
+	if (clnt == NULL) {
+		printk("NFS: cannot create RPC client.\n");
+		xprt_destroy(xprt);
+		goto failure;
+	}
+
+	clnt->cl_intr     = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
+	clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
+	clnt->cl_chatty   = 1;
+	server->client    = clnt;
+
+	/* Fire up rpciod if not yet running */
+	rpciod_up();
+
+	/* Unlock super block and try to get root fh attributes */
 	unlock_super(sb);
-	if (!(sb->s_mounted = nfs_fhget(sb, &data->root, NULL))) {
-		sb->s_dev = 0;
-		printk("nfs_read_super: get root inode failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+
+	if ((sb->s_mounted = nfs_fhget(sb, &data->root, NULL)) != NULL) {
+		/* We're airborne */
+		lockd_up();
+		return sb;
 	}
-	return sb;
+
+	/* Yargs. It didn't work out. */
+	printk("nfs_read_super: get root inode failed\n");
+	rpc_shutdown_client(server->client);
+	rpciod_down();
+
+failure:
+	MOD_DEC_USE_COUNT;
+	if (sb->s_lock)
+		unlock_super(sb);
+	sb->s_dev = 0;
+	return NULL;
 }
 
-void nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static void
+nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
 	int error;
 	struct nfs_fsinfo res;
@@ -238,9 +277,9 @@
  * We just have to be careful not to subvert iget's special handling
  * of mount points.
  */
-
-struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
-			struct nfs_fattr *fattr)
+struct inode *
+nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
+				  struct nfs_fattr *fattr)
 {
 	struct nfs_fattr newfattr;
 	int error;
@@ -271,39 +310,43 @@
 		*NFS_FH(inode) = *fhandle;
 		nfs_refresh_inode(inode, fattr);
 	}
+	dprintk("NFS: fhget(%x/%ld ct=%d)\n",
+				inode->i_dev, inode->i_ino, inode->i_count);
+
 	return inode;
 }
 
-int nfs_notify_change(struct inode *inode, struct iattr *attr)
+int
+nfs_notify_change(struct inode *inode, struct iattr *attr)
 {
 	struct nfs_sattr sattr;
 	struct nfs_fattr fattr;
 	int error;
 
-	sattr.mode = (unsigned) -1;
-	if (attr->ia_valid & ATTR_MODE)
+	sattr.mode = (u32) -1;
+	if (attr->ia_valid & ATTR_MODE) 
 		sattr.mode = attr->ia_mode;
 
-	sattr.uid = (unsigned) -1;
+	sattr.uid = (u32) -1;
 	if (attr->ia_valid & ATTR_UID)
 		sattr.uid = attr->ia_uid;
 
-	sattr.gid = (unsigned) -1;
+	sattr.gid = (u32) -1;
 	if (attr->ia_valid & ATTR_GID)
 		sattr.gid = attr->ia_gid;
 
 
-	sattr.size = (unsigned) -1;
-	if (attr->ia_valid & ATTR_SIZE)
-		sattr.size = S_ISREG(inode->i_mode) ? attr->ia_size : -1;
+	sattr.size = (u32) -1;
+	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode))
+		sattr.size = attr->ia_size;
 
-	sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
+	sattr.mtime.seconds = sattr.mtime.useconds = (u32) -1;
 	if (attr->ia_valid & ATTR_MTIME) {
 		sattr.mtime.seconds = attr->ia_mtime;
 		sattr.mtime.useconds = 0;
 	}
 
-	sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
+	sattr.atime.seconds = sattr.atime.useconds = (u32) -1;
 	if (attr->ia_valid & ATTR_ATIME) {
 		sattr.atime.seconds = attr->ia_atime;
 		sattr.atime.useconds = 0;
@@ -311,64 +354,117 @@
 
 	error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
 		&sattr, &fattr);
-	if (!error)
+	if (!error) {
+		nfs_truncate_dirty_pages(inode, sattr.size);
 		nfs_refresh_inode(inode, &fattr);
+	}
 	inode->i_dirt = 0;
 	return error;
 }
 
-/* Every kernel module contains stuff like this. */
-
-static struct file_system_type nfs_fs_type = {
-	nfs_read_super, "nfs", 0, NULL
-};
+/*
+ * Externally visible revalidation function
+ */
+int
+nfs_revalidate(struct inode *inode)
+{
+	return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+}
 
 /*
- * Start up an nfsiod process. This is an awful hack, because when running
- * as a module, we will keep insmod's memory. Besides, the current->comm
- * hack won't work in this case
- * The best would be to have a syscall for nfs client control that (among
- * other things) forks biod's.
- * Alternatively, we might want to have the idle task spawn biod's on demand.
+ * This function is called whenever some part of NFS notices that
+ * the cached attributes have to be refreshed.
+ *
+ * This is a bit tricky because we have to make sure all dirty pages
+ * have been sent off to the server before calling invalidate_inode_pages.
+ * To make sure no other process adds more write requests while we try
+ * our best to flush them, we make them sleep during the attribute refresh.
+ *
+ * A very similar scenario holds for the dir cache.
  */
-static int run_nfsiod(void *dummy)
+int
+_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
-	int	ret;
+	struct nfs_fattr fattr;
+	int		 status;
 
-	lock_kernel();
-	MOD_INC_USE_COUNT;
-	exit_mm(current);
-	current->session = 1;
-	current->pgrp = 1;
-	sprintf(current->comm, "nfsiod");
-	ret = nfsiod();
-	MOD_DEC_USE_COUNT;
-	unlock_kernel();
-	return ret;
+	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
+		return 0;
+
+	dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
+			inode->i_dev, inode->i_ino);
+	NFS_READTIME(inode) = jiffies;
+	if ((status = nfs_proc_getattr(server, NFS_FH(inode), &fattr)) < 0)
+		goto done;
+
+	nfs_refresh_inode(inode, &fattr);
+	if (fattr.mtime.seconds != NFS_OLDMTIME(inode)) {
+		if (!S_ISDIR(inode->i_mode)) {
+			/* This sends off all dirty pages off to the server.
+			 * Note that this function must not sleep. */
+			nfs_invalidate_pages(inode);
+			invalidate_inode_pages(inode);
+		} else {
+			nfs_invalidate_dircache(inode);
+		}
+
+		NFS_OLDMTIME(inode)  = fattr.mtime.seconds;
+		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+	} else {
+		/* Update attrtimeo value */
+		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
+			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
+	}
+	status = 0;
+
+done:
+	dfprintk(PAGECACHE,
+		"NFS: inode %x/%ld revalidation complete (status %d).\n",
+				inode->i_dev, inode->i_ino, status);
+	return status;
 }
 
-int init_nfs_fs(void)
+/*
+ * File system information
+ */
+static struct file_system_type nfs_fs_type = {
+	nfs_read_super, "nfs", 0, NULL
+};
+
+/*
+ * Initialize NFS
+ */
+int
+init_nfs_fs(void)
 {
-	/* Fork four biod's */
-	kernel_thread(run_nfsiod, NULL, 0);
-	kernel_thread(run_nfsiod, NULL, 0);
-	kernel_thread(run_nfsiod, NULL, 0);
-	kernel_thread(run_nfsiod, NULL, 0);
+#ifdef CONFIG_PROC_FS
+	rpcstat_register(&nfs_rpcstat);
+#endif
         return register_filesystem(&nfs_fs_type);
 }
 
+/*
+ * Every kernel module contains stuff like this.
+ */
 #ifdef MODULE
+
 EXPORT_NO_SYMBOLS;
+/* Not quite true; I just maintain it */
+MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
 
-int init_module(void)
+int
+init_module(void)
 {
 	return init_nfs_fs();
 }
 
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
+#ifdef CONFIG_PROC_FS
+	rpcstat_unregister(&nfs_rpcstat);
+#endif
 	unregister_filesystem(&nfs_fs_type);
-	nfs_kfree_cache();
+	nfs_free_dircache();
 }
-
 #endif
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- v2.1.31/linux/fs/nfs/nfs2xdr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/nfs2xdr.c	Fri Apr  4 11:05:59 1997
@@ -0,0 +1,649 @@
+/*
+ * linux/fs/nfs/xdr.c
+ *
+ * XDR functions to encode/decode NFS RPC arguments and results.
+ *
+ * Copyright (C) 1992, 1993, 1994  Rick Sladkey
+ * Copyright (C) 1996 Olaf Kirch
+ */
+
+#define NFS_NEED_XDR_TYPES
+
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+
+#define NFSDBG_FACILITY		NFSDBG_XDR
+
+#define QUADLEN(len)		(((len) + 3) >> 2)
+static int			nfs_stat_to_errno(int stat);
+
+/* Mapping from NFS error code to "errno" error code. */
+#define errno_NFSERR_IO		EIO
+
+/*
+ * Declare the space requirements for NFS arguments and replies as
+ * number of 32bit-words
+ */
+#define NFS_fhandle_sz		8
+#define NFS_sattr_sz		8
+#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
+#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
+#define NFS_fattr_sz		17
+#define NFS_info_sz		5
+#define NFS_entry_sz		NFS_filename_sz+3
+
+#define NFS_enc_void_sz		0
+#define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
+#define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
+#define NFS_readargs_sz		NFS_fhandle_sz+3
+#define NFS_writeargs_sz	NFS_fhandle_sz+4
+#define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
+#define NFS_renameargs_sz	NFS_diropargs_sz+NFS_diropargs_sz
+#define NFS_linkargs_sz		NFS_fhandle_sz+NFS_diropargs_sz
+#define NFS_symlinkargs_sz	NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
+#define NFS_readdirargs_sz	NFS_fhandle_sz+2
+
+#define NFS_dec_void_sz		0
+#define NFS_attrstat_sz		1+NFS_fattr_sz
+#define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
+#define NFS_readlinkres_sz	1+NFS_path_sz
+#define NFS_readres_sz		1+NFS_fattr_sz+1
+#define NFS_stat_sz		1
+#define NFS_readdirres_sz	1
+#define NFS_statfsres_sz	1+NFS_info_sz
+
+/*
+ * Common NFS XDR functions as inlines
+ */
+static inline u32 *
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
+{
+	*((struct nfs_fh *) p) = *fhandle;
+	return p + QUADLEN(sizeof(*fhandle));
+}
+
+static inline u32 *
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
+{
+	*fhandle = *((struct nfs_fh *) p);
+	return p + QUADLEN(sizeof(*fhandle));
+}
+
+static inline u32 *
+xdr_decode_string2(u32 *p, char **string, unsigned int *len,
+			unsigned int maxlen)
+{
+	*len = ntohl(*p++);
+	if (*len > maxlen)
+		return NULL;
+	*string = (char *) p;
+	return p + QUADLEN(*len);
+}
+
+static inline u32 *
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
+{
+	fattr->type = (enum nfs_ftype) ntohl(*p++);
+	fattr->mode = ntohl(*p++);
+	fattr->nlink = ntohl(*p++);
+	fattr->uid = ntohl(*p++);
+	fattr->gid = ntohl(*p++);
+	fattr->size = ntohl(*p++);
+	fattr->blocksize = ntohl(*p++);
+	fattr->rdev = ntohl(*p++);
+	fattr->blocks = ntohl(*p++);
+	fattr->fsid = ntohl(*p++);
+	fattr->fileid = ntohl(*p++);
+	fattr->atime.seconds = ntohl(*p++);
+	fattr->atime.useconds = ntohl(*p++);
+	fattr->mtime.seconds = ntohl(*p++);
+	fattr->mtime.useconds = ntohl(*p++);
+	fattr->ctime.seconds = ntohl(*p++);
+	fattr->ctime.useconds = ntohl(*p++);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+{
+	*p++ = htonl(sattr->mode);
+	*p++ = htonl(sattr->uid);
+	*p++ = htonl(sattr->gid);
+	*p++ = htonl(sattr->size);
+	*p++ = htonl(sattr->atime.seconds);
+	*p++ = htonl(sattr->atime.useconds);
+	*p++ = htonl(sattr->mtime.seconds);
+	*p++ = htonl(sattr->mtime.useconds);
+	return p;
+}
+
+/*
+ * NFS encode functions
+ */
+/*
+ * Encode void argument
+ */
+static int
+nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode file handle argument
+ * GETATTR, READLINK, STATFS
+ */
+static int
+nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
+{
+	p = xdr_encode_fhandle(p, fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode SETATTR arguments
+ */
+static int
+nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_sattr(p, args->sattr);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode directory ops argument
+ * LOOKUP, REMOVE, RMDIR
+ */
+static int
+nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Arguments to a READ call. Since we read data directly into the page
+ * cache, we also set up the reply iovec here so that iov[1] points
+ * exactly to the page wewant to fetch.
+ */
+static int
+nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+{
+	struct rpc_auth	*auth = req->rq_task->tk_auth;
+	int		replen, buflen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->offset);
+	*p++ = htonl(args->count);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+#if 1
+	/* set up reply iovec */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	buflen = req->rq_rvec[0].iov_len;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->count;
+	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+	req->rq_rvec[2].iov_len  = buflen - replen;
+	req->rq_rlen = args->count + buflen;
+	req->rq_rnr = 3;
+#else
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+#endif
+
+	return 0;
+}
+
+/*
+ * Decode READ reply
+ */
+static int
+nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+{
+	struct iovec *iov = req->rq_rvec;
+	int	status, count, recvd, hdrlen;
+
+	dprintk("RPC:      readres OK status %lx\n", ntohl(*p));
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	p = xdr_decode_fattr(p, res->fattr);
+
+	count = ntohl(*p++);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	recvd = req->rq_rlen - hdrlen;
+	if (p != iov[2].iov_base) {
+		/* Unexpected reply header size. Punt.
+		 * XXX: Move iovec contents to align data on page
+		 * boundary and adjust RPC header size guess */
+		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (count > recvd) {
+		printk("NFS: server cheating in read reply: "
+			"count %d > recvd %d\n", count, recvd);
+		count = recvd;
+	}
+
+	dprintk("RPC:      readres OK count %d\n", count);
+	if (count < res->count)
+		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+
+	return count;
+}
+
+
+/*
+ * Write arguments. Splice the buffer to be written into the iovec.
+ */
+static int
+nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->offset);
+	*p++ = htonl(args->offset);
+	*p++ = htonl(args->count);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+	req->rq_svec[1].iov_base = (void *) args->buffer;
+	req->rq_svec[1].iov_len = args->count;
+	req->rq_slen += args->count;
+	req->rq_snr = 2;
+
+	return 0;
+}
+
+/*
+ * Encode create arguments
+ * CREATE, MKDIR
+ */
+static int
+nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_sattr(p, args->sattr);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode RENAME arguments
+ */
+static int
+nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode LINK arguments
+ */
+static int
+nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode SYMLINK arguments
+ */
+static int
+nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname);
+	p = xdr_encode_string(p, args->topath);
+	p = xdr_encode_sattr(p, args->sattr);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode arguments to readdir call
+ */
+static int
+nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+{
+	struct rpc_auth	*auth = req->rq_task->tk_auth;
+	int		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->cookie);
+	*p++ = htonl(args->bufsiz);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+	/* set up reply iovec */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
+	/*
+	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
+		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
+	 */
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr = 2;
+
+	/*
+	dprintk("RPC:      readdirargs set up reply vec:\n");
+	dprintk("          rvec[0] = %p/%d\n",
+			req->rq_rvec[0].iov_base,
+			req->rq_rvec[0].iov_len);
+	dprintk("          rvec[1] = %p/%d\n",
+			req->rq_rvec[1].iov_base,
+			req->rq_rvec[1].iov_len);
+	 */
+
+	return 0;
+}
+
+/*
+ * Decode the result of a readdir call. We decode the result in place
+ * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
+ * After decoding, the layout in memory looks like this:
+ *	entry1 entry2 ... entryN <space> stringN ... string2 string1
+ * Note that the strings are not null-terminated so that the entire number
+ * of entries returned by the server should fit into the buffer.
+ */
+static int
+nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
+{
+	struct nfs_entry	*entry;
+	struct iovec		*iov = req->rq_rvec;
+	int			status, nr, len;
+	char			*string;
+	u32			*end;
+
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
+		/* Unexpected reply header size. Punt. */
+		printk("NFS: Odd RPC header size in readdirres reply\n");
+		return -errno_NFSERR_IO;
+	}
+
+	/* Get start and end address of XDR data */
+	p   = (u32 *) iov[1].iov_base;
+	end = (u32 *) ((u8 *) p + iov[1].iov_len);
+
+	/* Get start and end of dirent buffer */
+	entry  = (struct nfs_entry *) res->buffer;
+	string = (char *) res->buffer + res->bufsiz;
+	for (nr = 0; *p++; nr++, entry++) {
+		entry->fileid = ntohl(*p++);
+
+		len = ntohl(*p++);
+		if ((p + QUADLEN(len) + 3) > end) {
+			printk(KERN_NOTICE
+				"NFS: short packet in readdir reply!\n");
+			break;
+		}
+		if (len > NFS_MAXNAMLEN) {
+			printk("NFS: giant filename in readdir (len %x)!\n",
+						len);
+			return -errno_NFSERR_IO;
+		}
+		string -= len;
+		if ((void *) (entry+1) > (void *) string) {
+			printk(KERN_NOTICE "NFS: should not happen in %s!\n",
+					__FUNCTION__);
+			break;
+		}
+
+		entry->name = string;
+		entry->length = len;
+		memmove(string, p, len);
+		p += QUADLEN(len);
+		entry->cookie = ntohl(*p++);
+		entry->eof = !p[0] && p[1];
+	}
+	return nr;
+}
+
+/*
+ * NFS XDR decode functions
+ */
+/*
+ * Decode void reply
+ */
+static int
+nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	return 0;
+}
+
+/*
+ * Decode simple status reply
+ */
+static int
+nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	int	status;
+
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	return status;
+}
+
+/*
+ * Decode attrstat reply
+ * GETATTR, SETATTR, WRITE
+ */
+static int
+nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+	int	status;
+
+	dprintk("RPC:      attrstat status %lx\n", ntohl(*p));
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_fattr(p, fattr);
+	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
+		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
+	return 0;
+}
+
+/*
+ * Decode diropres reply
+ * LOOKUP, CREATE, MKDIR
+ */
+static int
+nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+{
+	int	status;
+
+	dprintk("RPC:      diropres status %lx\n", ntohl(*p));
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	p = xdr_decode_fhandle(p, res->fh);
+	xdr_decode_fattr(p, res->fattr);
+	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
+		res->fattr->type, res->fattr->mode,
+		res->fattr->fsid, res->fattr->fileid);
+	return 0;
+}
+
+/*
+ * Decode READLINK reply
+ */
+static int
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+{
+	int	status;
+
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
+
+	/* Caller takes over the buffer here to avoid extra copy */
+	res->buffer = req->rq_task->tk_buffer;
+	req->rq_task->tk_buffer = NULL;
+	return 0;
+}
+
+/*
+ * Decode STATFS reply
+ */
+static int
+nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	int	status;
+
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	res->tsize = ntohl(*p++);
+	res->bsize = ntohl(*p++);
+	res->blocks = ntohl(*p++);
+	res->bfree = ntohl(*p++);
+	res->bavail = ntohl(*p++);
+	return 0;
+}
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ */
+static struct {
+	int stat;
+	int errno;
+} nfs_errtbl[] = {
+	{ NFS_OK,		0		},
+	{ NFSERR_PERM,		EPERM		},
+	{ NFSERR_NOENT,		ENOENT		},
+	{ NFSERR_IO,		errno_NFSERR_IO	},
+	{ NFSERR_NXIO,		ENXIO		},
+	{ NFSERR_EAGAIN,	EAGAIN		},
+	{ NFSERR_ACCES,		EACCES		},
+	{ NFSERR_EXIST,		EEXIST		},
+	{ NFSERR_NODEV,		ENODEV		},
+	{ NFSERR_NOTDIR,	ENOTDIR		},
+	{ NFSERR_ISDIR,		EISDIR		},
+	{ NFSERR_INVAL,		EINVAL		},
+	{ NFSERR_FBIG,		EFBIG		},
+	{ NFSERR_NOSPC,		ENOSPC		},
+	{ NFSERR_ROFS,		EROFS		},
+	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
+	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
+	{ NFSERR_DQUOT,		EDQUOT		},
+	{ NFSERR_STALE,		ESTALE		},
+#ifdef EWFLUSH
+	{ NFSERR_WFLUSH,	EWFLUSH		},
+#endif
+	{ -1,			EIO		}
+};
+
+static int
+nfs_stat_to_errno(int stat)
+{
+	int i;
+
+	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+		if (nfs_errtbl[i].stat == stat)
+			return nfs_errtbl[i].errno;
+	}
+	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+	return nfs_errtbl[i].errno;
+}
+
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype)	\
+    { "nfs_" #proc,					\
+      (kxdrproc_t) nfs_xdr_##argtype,			\
+      (kxdrproc_t) nfs_xdr_##restype,			\
+      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
+    }
+
+static struct rpc_procinfo	nfs_procedures[18] = {
+    PROC(null,		enc_void,	dec_void),
+    PROC(getattr,	fhandle,	attrstat),
+    PROC(setattr,	sattrargs,	attrstat),
+    PROC(root,		enc_void,	dec_void),
+    PROC(lookup,	diropargs,	diropres),
+    PROC(readlink,	fhandle,	readlinkres),
+    PROC(read,		readargs,	readres),
+    PROC(writecache,	enc_void,	dec_void),
+    PROC(write,		writeargs,	attrstat),
+    PROC(create,	createargs,	diropres),
+    PROC(remove,	diropargs,	stat),
+    PROC(rename,	renameargs,	stat),
+    PROC(link,		linkargs,	stat),
+    PROC(symlink,	symlinkargs,	stat),
+    PROC(mkdir,		createargs,	diropres),
+    PROC(rmdir,		diropargs,	stat),
+    PROC(readdir,	readdirargs,	readdirres),
+    PROC(statfs,	fhandle,	statfsres),
+};
+
+static struct rpc_version	nfs_version2 = {
+	2,
+	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
+	nfs_procedures
+};
+
+static struct rpc_version *	nfs_version[] = {
+	NULL,
+	NULL,
+	&nfs_version2
+};
+
+struct rpc_program	nfs_program = {
+	"nfs",
+	NFS_PROGRAM,
+	sizeof(nfs_version) / sizeof(nfs_version[0]),
+	nfs_version,
+	&nfs_rpcstat,
+};
+
+/*
+ * RPC stats support
+ */
+static int
+nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
+}
+
+static struct proc_dir_entry	proc_nfsclnt = {
+	0, 3, "nfs",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	6, &proc_net_inode_operations,
+	nfs_get_info
+};
+
+struct rpc_stat			nfs_rpcstat = {
+	NULL,			/* next */
+	&proc_nfsclnt,		/* /proc/net directory entry */
+	&nfs_program,		/* RPC program */
+};
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfs3xdr.c linux/fs/nfs/nfs3xdr.c
--- v2.1.31/linux/fs/nfs/nfs3xdr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/nfs3xdr.c	Fri Apr  4 11:05:59 1997
@@ -0,0 +1,669 @@
+/*
+ * linux/fs/nfs/nfs2xdr.c
+ *
+ * XDR functions to encode/decode NFSv3 RPC arguments and results.
+ * Note: this is incomplete!
+ *
+ * Copyright (C) 1996 Olaf Kirch
+ */
+
+#define NFS_NEED_XDR_TYPES
+
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/nfs_fs.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+# define RPC_FACILITY		RPCDBG_NFS
+#endif
+
+#define QUADLEN(len)		(((len) + 3) >> 2)
+static int			nfs_stat_to_errno(int stat);
+
+/* Mapping from NFS error code to "errno" error code. */
+#define errno_NFSERR_IO		EIO
+
+/*
+ * Declare the space requirements for NFS arguments and replies as
+ * number of 32bit-words
+ */
+#define NFS_fhandle_sz		(1+16)
+#define NFS_sattr_sz		8
+#define NFS_filename_sz		1+(NFS_MAXNAMLEN>>2)
+#define NFS_path_sz		1+(NFS_MAXPATHLEN>>2)
+#define NFS_fattr_sz		17
+#define NFS_info_sz		5
+#define NFS_entry_sz		NFS_filename_sz+3
+
+#define NFS_enc_void_sz		0
+#define NFS_diropargs_sz	NFS_fhandle_sz+NFS_filename_sz
+#define NFS_sattrargs_sz	NFS_fhandle_sz+NFS_sattr_sz
+#define NFS_readargs_sz		NFS_fhandle_sz+3
+#define NFS_writeargs_sz	NFS_fhandle_sz+4
+#define NFS_createargs_sz	NFS_diropargs_sz+NFS_sattr_sz
+#define NFS_renameargs_sz	NFS_diropargs_sz+NFS_diropargs_sz
+#define NFS_linkargs_sz		NFS_fhandle_sz+NFS_diropargs_sz
+#define NFS_symlinkargs_sz	NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
+#define NFS_readdirargs_sz	NFS_fhandle_sz+2
+
+#define NFS_dec_void_sz		0
+#define NFS_attrstat_sz		1+NFS_fattr_sz
+#define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
+#define NFS_readlinkres_sz	1+NFS_path_sz
+#define NFS_readres_sz		1+NFS_fattr_sz+1
+#define NFS_stat_sz		1
+#define NFS_readdirres_sz	1
+#define NFS_statfsres_sz	1+NFS_info_sz
+
+/*
+ * Common NFS XDR functions as inlines
+ */
+static inline u32 *
+xdr_encode_fhandle(u32 *p, struct nfs3_fh *fh)
+{
+	*p++ = htonl(fh->size);
+	memcpy(p, fh->data, fh->size);
+	return p + QUADLEN(fh->size);
+}
+
+static inline u32 *
+xdr_decode_fhandle(u32 *p, struct nfs3_fh *fh)
+{
+	if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
+		memcpy(fh->data, p, fh->size);
+		return p + QUADLEN(fh->size);
+	}
+	return NULL;
+}
+
+static inline enum nfs_ftype
+xdr_decode_ftype(u32 type)
+{
+	return (type == NF3FIFO)? NFFIFO : (enum nfs_ftype) type;
+}
+
+static inline u32 *
+xdr_decode_string2(u32 *p, char **string, unsigned int *len,
+			unsigned int maxlen)
+{
+	*len = ntohl(*p++);
+	if (*len > maxlen)
+		return NULL;
+	*string = (char *) p;
+	return p + QUADLEN(*len);
+}
+
+static inline u32 *
+xdr_decode_fattr(u32 *p, struct nfs3_fattr *fattr)
+{
+	fattr->type = xdr_decode_ftype(ntohl(*p++));
+	fattr->mode = ntohl(*p++);
+	fattr->nlink = ntohl(*p++);
+	fattr->uid = ntohl(*p++);
+	fattr->gid = ntohl(*p++);
+	fattr->size = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
+	fattr->used = ((u64) ntohl(*p++) << 32) | ntohl(*p++);
+	fattr->rdev_maj = ntohl(*p++);
+	fattr->rdev_min = ntohl(*p++);
+	fattr->fsid = ntohl(*p++);
+	fattr->fileid = ntohl(*p++);
+	fattr->atime.seconds = ntohl(*p++);
+	fattr->atime.useconds = ntohl(*p++);
+	fattr->mtime.seconds = ntohl(*p++);
+	fattr->mtime.useconds = ntohl(*p++);
+	fattr->ctime.seconds = ntohl(*p++);
+	fattr->ctime.useconds = ntohl(*p++);
+	return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct nfs_sattr *sattr)
+{
+	*p++ = htonl(sattr->mode);
+	*p++ = htonl(sattr->uid);
+	*p++ = htonl(sattr->gid);
+	*p++ = htonl(sattr->size >> 32);
+	*p++ = htonl(sattr->size & 0xFFFFFFFF);
+	*p++ = htonl(sattr->atime.seconds);
+	*p++ = htonl(sattr->atime.useconds);
+	*p++ = htonl(sattr->mtime.seconds);
+	*p++ = htonl(sattr->mtime.useconds);
+	return p;
+}
+
+/*
+ * NFS encode functions
+ */
+/*
+ * Encode void argument
+ */
+static int
+nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode file handle argument
+ * GETATTR, READLINK, STATFS
+ */
+static int
+nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs3_fh *fh)
+{
+	p = xdr_encode_fhandle(p, fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode SETATTR arguments
+ */
+static int
+nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_sattr(p, args->sattr);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode directory ops argument
+ * LOOKUP, REMOVE, RMDIR
+ */
+static int
+nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Arguments to a READ call. Since we read data directly into the page
+ * cache, we also set up the reply iovec here so that iov[1] points
+ * exactly to the page wewant to fetch.
+ */
+static int
+nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+{
+	struct rpc_auth	*auth = req->rq_task->tk_auth;
+	int		replen, buflen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->offset);
+	*p++ = htonl(args->count);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+#if 1
+	/* set up reply iovec */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	buflen = req->rq_rvec[0].iov_len;
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->count;
+	req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+	req->rq_rvec[2].iov_len  = buflen - replen;
+	req->rq_rlen = args->count + buflen;
+	req->rq_rnr = 3;
+#else
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+	req->rq_rvec[0].iov_len  = replen;
+#endif
+
+	return 0;
+}
+
+/*
+ * Decode READ reply
+ */
+static int
+nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+{
+	struct iovec *iov = req->rq_rvec;
+	int	status, count, recvd, hdrlen;
+
+	dprintk("RPC:      readres OK status %lx\n", ntohl(*p));
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	p = xdr_decode_fattr(p, res->fattr);
+
+	count = ntohl(*p++);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	recvd = req->rq_rlen - hdrlen;
+	if (p != iov[2].iov_base) {
+		/* Unexpected reply header size. Punt.
+		 * XXX: Move iovec contents to align data on page
+		 * boundary and adjust RPC header size guess */
+		printk("NFS: Odd RPC header size in read reply: %d\n", hdrlen);
+		return -errno_NFSERR_IO;
+	}
+	if (count > recvd) {
+		printk("NFS: server cheating in read reply: "
+			"count %d > recvd %d\n", count, recvd);
+		count = recvd;
+	}
+
+	dprintk("RPC:      readres OK count %d\n", count);
+	if (count < res->count)
+		memset((u8 *)(iov[1].iov_base+count), 0, res->count-count);
+
+	return count;
+}
+
+
+/*
+ * Write arguments. Splice the buffer to be written into the iovec.
+ */
+static int
+nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->offset);
+	*p++ = htonl(args->offset);
+	*p++ = htonl(args->count);
+	*p++ = htonl(args->count);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+	req->rq_svec[1].iov_base = (void *) args->buffer;
+	req->rq_svec[1].iov_len = args->count;
+	req->rq_slen += args->count;
+	req->rq_snr = 2;
+
+	return 0;
+}
+
+/*
+ * Encode create arguments
+ * CREATE, MKDIR
+ */
+static int
+nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fh);
+	p = xdr_encode_string(p, args->name);
+	p = xdr_encode_sattr(p, args->sattr);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode RENAME arguments
+ */
+static int
+nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode LINK arguments
+ */
+static int
+nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_fhandle(p, args->tofh);
+	p = xdr_encode_string(p, args->toname);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode SYMLINK arguments
+ */
+static int
+nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+{
+	p = xdr_encode_fhandle(p, args->fromfh);
+	p = xdr_encode_string(p, args->fromname);
+	p = xdr_encode_string(p, args->topath);
+	p = xdr_encode_sattr(p, args->sattr);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+/*
+ * Encode arguments to readdir call
+ */
+static int
+nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+{
+	struct rpc_auth	*auth = req->rq_task->tk_auth;
+	int		replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->cookie);
+	*p++ = htonl(args->bufsiz);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+	/* set up reply iovec */
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
+	/*
+	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
+		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
+	 */
+	req->rq_rvec[0].iov_len  = replen;
+	req->rq_rvec[1].iov_base = args->buffer;
+	req->rq_rvec[1].iov_len  = args->bufsiz;
+	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rnr = 2;
+
+	/*
+	dprintk("RPC:      readdirargs set up reply vec:\n");
+	dprintk("          rvec[0] = %p/%d\n",
+			req->rq_rvec[0].iov_base,
+			req->rq_rvec[0].iov_len);
+	dprintk("          rvec[1] = %p/%d\n",
+			req->rq_rvec[1].iov_base,
+			req->rq_rvec[1].iov_len);
+	 */
+
+	return 0;
+}
+
+/*
+ * Decode the result of a readdir call. We decode the result in place
+ * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
+ * After decoding, the layout in memory looks like this:
+ *	entry1 entry2 ... entryN <space> stringN ... string2 string1
+ * Note that the strings are not null-terminated so that the entire number
+ * of entries returned by the server should fit into the buffer.
+ */
+static int
+nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
+{
+	struct nfs_entry	*entry;
+	struct iovec		*iov = req->rq_rvec;
+	int			status, nr, len;
+	char			*string;
+	u32			*end;
+
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
+		/* Unexpected reply header size. Punt. */
+		printk("NFS: Odd RPC header size in readdirres reply\n");
+		return -errno_NFSERR_IO;
+	}
+
+	p = (u32 *) iov[1].iov_base;
+	end = (u32 *) ((u8 *) p + iov[1].iov_len);
+
+	if (p != res->buffer) {
+		printk("NFS: p != res->buffer in %s:%d!!!\n",
+					__FILE__, __LINE__);
+		return -errno_NFSERR_IO;
+	}
+
+	string = (char *) res->buffer + res->bufsiz;
+	entry = (struct nfs_entry *) res->buffer;
+	for (nr = 0; *p++; nr++, entry++) {
+		entry->fileid = ntohl(*p++);
+
+		len = ntohl(*p++);
+		if ((p + QUADLEN(len) + 3) > end) {
+			printk(KERN_NOTICE
+				"NFS: short packet in readdir reply!\n");
+			break;
+		}
+		if (len > NFS_MAXNAMLEN) {
+			printk("NFS: giant filename in readdir (len %x)!\n",
+						len);
+			return -errno_NFSERR_IO;
+		}
+		string -= len;
+		if ((void *) (entry+1) > (void *) string) {
+			dprintk("NFS: shouldnothappen in readdirres_decode!\n");
+			break;	/* should not happen */
+		}
+
+		entry->name = string;
+		entry->length = len;
+		memmove(string, p, len);
+		p += QUADLEN(len);
+		entry->cookie = ntohl(*p++);
+		entry->eof = !p[0] && p[1];
+		/*
+		dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n",
+			len, string, entry->cookie, entry->eof);
+		 */
+	}
+	return nr;
+}
+
+/*
+ * NFS XDR decode functions
+ */
+/*
+ * Decode void reply
+ */
+static int
+nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	return 0;
+}
+
+/*
+ * Decode simple status reply
+ */
+static int
+nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	int	status;
+
+	if ((status = ntohl(*p++)) != 0)
+		status = -nfs_stat_to_errno(status);
+	return status;
+}
+
+/*
+ * Decode attrstat reply
+ * GETATTR, SETATTR, WRITE
+ */
+static int
+nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+	int	status;
+
+	dprintk("RPC:      attrstat status %lx\n", ntohl(*p));
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_fattr(p, fattr);
+	dprintk("RPC:      attrstat OK type %d mode %o dev %x ino %x\n",
+		fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
+	return 0;
+}
+
+/*
+ * Decode diropres reply
+ * LOOKUP, CREATE, MKDIR
+ */
+static int
+nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+{
+	int	status;
+
+	dprintk("RPC:      diropres status %lx\n", ntohl(*p));
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	p = xdr_decode_fhandle(p, res->fh);
+	xdr_decode_fattr(p, res->fattr);
+	dprintk("RPC:      diropres OK type %x mode %o dev %x ino %x\n",
+		res->fattr->type, res->fattr->mode,
+		res->fattr->fsid, res->fattr->fileid);
+	return 0;
+}
+
+/*
+ * Decode READLINK reply
+ */
+static int
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+{
+	int	status;
+
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
+
+	/* Caller takes over the buffer here to avoid extra copy */
+	res->buffer = req->rq_task->tk_buffer;
+	req->rq_task->tk_buffer = NULL;
+	return 0;
+}
+
+/*
+ * Decode STATFS reply
+ */
+static int
+nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+	int	status;
+
+	if ((status = ntohl(*p++)))
+		return -nfs_stat_to_errno(status);
+	res->tsize = ntohl(*p++);
+	res->bsize = ntohl(*p++);
+	res->blocks = ntohl(*p++);
+	res->bfree = ntohl(*p++);
+	res->bavail = ntohl(*p++);
+	return 0;
+}
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ */
+static struct {
+	int stat;
+	int errno;
+} nfs_errtbl[] = {
+	{ NFS_OK,		0		},
+	{ NFSERR_PERM,		EPERM		},
+	{ NFSERR_NOENT,		ENOENT		},
+	{ NFSERR_IO,		errno_NFSERR_IO	},
+	{ NFSERR_NXIO,		ENXIO		},
+	{ NFSERR_EAGAIN,	EAGAIN		},
+	{ NFSERR_ACCES,		EACCES		},
+	{ NFSERR_EXIST,		EEXIST		},
+	{ NFSERR_NODEV,		ENODEV		},
+	{ NFSERR_NOTDIR,	ENOTDIR		},
+	{ NFSERR_ISDIR,		EISDIR		},
+	{ NFSERR_INVAL,		EINVAL		},
+	{ NFSERR_FBIG,		EFBIG		},
+	{ NFSERR_NOSPC,		ENOSPC		},
+	{ NFSERR_ROFS,		EROFS		},
+	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
+	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
+	{ NFSERR_DQUOT,		EDQUOT		},
+	{ NFSERR_STALE,		ESTALE		},
+#ifdef EWFLUSH
+	{ NFSERR_WFLUSH,	EWFLUSH		},
+#endif
+	{ -1,			EIO		}
+};
+
+static int
+nfs_stat_to_errno(int stat)
+{
+	int i;
+
+	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+		if (nfs_errtbl[i].stat == stat)
+			return nfs_errtbl[i].errno;
+	}
+	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+	return nfs_errtbl[i].errno;
+}
+
+#ifndef MAX
+# define MAX(a, b)	(((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype)	\
+    { "nfs_" #proc,					\
+      (kxdrproc_t) nfs_xdr_##argtype,			\
+      (kxdrproc_t) nfs_xdr_##restype,			\
+      MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2	\
+    }
+
+static struct rpc_procinfo	nfs_procedures[18] = {
+    PROC(null,		enc_void,	dec_void),
+    PROC(getattr,	fhandle,	attrstat),
+    PROC(setattr,	sattrargs,	attrstat),
+    PROC(root,		enc_void,	dec_void),
+    PROC(lookup,	diropargs,	diropres),
+    PROC(readlink,	fhandle,	readlinkres),
+    PROC(read,		readargs,	readres),
+    PROC(writecache,	enc_void,	dec_void),
+    PROC(write,		writeargs,	attrstat),
+    PROC(create,	createargs,	diropres),
+    PROC(remove,	diropargs,	stat),
+    PROC(rename,	renameargs,	stat),
+    PROC(link,		linkargs,	stat),
+    PROC(symlink,	symlinkargs,	stat),
+    PROC(mkdir,		createargs,	diropres),
+    PROC(rmdir,		diropargs,	stat),
+    PROC(readdir,	readdirargs,	readdirres),
+    PROC(statfs,	fhandle,	statfsres),
+};
+
+static struct rpc_version	nfs_version2 = {
+	2,
+	sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
+	nfs_procedures
+};
+
+static struct rpc_version *	nfs_version[] = {
+	NULL,
+	NULL,
+	&nfs_version2
+};
+
+struct rpc_program	nfs_program = {
+	"nfs",
+	NFS_PROGRAM,
+	sizeof(nfs_version) / sizeof(nfs_version[0]),
+	nfs_version,
+	&nfs_rpcstat,
+};
+
+/*
+ * RPC stats support
+ */
+static int
+nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
+}
+
+static struct proc_dir_entry	proc_nfsclnt = {
+	0, 3, "nfs",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	6, &proc_net_inode_operations,
+	nfs_get_info
+};
+
+struct rpc_stat			nfs_rpcstat = {
+	NULL,			/* next */
+	&proc_nfsclnt,		/* /proc/net directory entry */
+	&nfs_program,		/* RPC program */
+};
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfsiod.c linux/fs/nfs/nfsiod.c
--- v2.1.31/linux/fs/nfs/nfsiod.c	Sat Jun 29 02:00:46 1996
+++ linux/fs/nfs/nfsiod.c	Wed Dec 31 16:00:00 1969
@@ -1,120 +0,0 @@
-/*
- * linux/fs/nfs/nfsiod.c
- *
- * Async NFS RPC call support.
- *
- * When a process wants to place an asynchronous RPC call, it reserves
- * an nfsiod slot, fills in all necessary fields including the callback
- * handler field, and enqueues the request.
- *
- * This will wake up nfsiod, which calls nfs_rpc_doio to collect the
- * reply. It then dispatches the result to the caller via the callback
- * function, including result value and request pointer. It then re-inserts
- * itself into the free list.
- *
- * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
- */
-
-#include <linux/sched.h>
-#include <linux/nfs_fs.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/rpcsock.h>
-#include <linux/nfsiod.h>
-
-static struct nfsiod_req *	free_list = NULL;
-static int			active = 0;
-
-#undef DEBUG_NFSIOD
-#ifdef DEBUG_NFSIOD
-#define dprintk(args...)	printk(## args)
-#else
-#define dprintk(args...)	/* nothing */
-#endif
-
-
-/*
- * Reserve an nfsiod slot and initialize the request struct
- */
-struct nfsiod_req *
-nfsiod_reserve(struct nfs_server *server)
-{
-	struct nfsiod_req	*req;
-
-	if (!(req = free_list)) {
-		dprintk("BIO: nfsiod_reserve: no free nfsiods\n");
-		return NULL;
-	}
-	free_list = req->rq_next;
-	memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
-
-	if (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) {
-		dprintk("BIO: nfsiod_reserve failed to reserve RPC slot\n");
-		req->rq_next = free_list;
-		free_list = req;
-		return NULL;
-	}
-
-	req->rq_server = server;
-	return req;
-}
-
-void
-nfsiod_release(struct nfsiod_req *req)
-{
-	dprintk("BIO: nfsiod_release called\n");
-	rpc_release(req->rq_server->rsock, &req->rq_rpcreq);
-	memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
-	req->rq_next = free_list;
-	free_list = req;
-}
-
-/*
- * Transmit a request and put it on nfsiod's list of pending requests.
- */
-void
-nfsiod_enqueue(struct nfsiod_req *req)
-{
-	dprintk("BIO: enqueuing request %p\n", &req->rq_rpcreq);
-	wake_up(&req->rq_wait);
-	schedule();
-}
-
-/*
- * This is the main nfsiod loop.
- */
-int
-nfsiod(void)
-{
-	struct nfsiod_req	request, *req = &request;
-	int			result;
-
-	dprintk("BIO: nfsiod %d starting\n", current->pid);
-	while (1) {
-		/* Insert request into free list */
-		memset(req, 0, sizeof(*req));
-		req->rq_next = free_list;
-		free_list = req;
-
-		/* Wait until user enqueues request */
-		dprintk("BIO: before: now %d nfsiod's active\n", active);
-		dprintk("BIO: nfsiod %d waiting\n", current->pid);
-		interruptible_sleep_on(&req->rq_wait);
-
-		if (current->signal & ~current->blocked)
-			break;
-		if (!req->rq_rpcreq.rq_slot)
-			continue;
-		dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n",
-				current->pid);
-		active++;
-		dprintk("BIO: before: now %d nfsiod's active\n", active);
-		do {
-			result = nfs_rpc_doio(req->rq_server,
-						&req->rq_rpcreq, 1);
-		} while (!req->rq_callback(result, req));
-		active--;
-	}
-
-	return 0;
-}
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.1.31/linux/fs/nfs/nfsroot.c	Thu Feb 27 10:57:31 1997
+++ linux/fs/nfs/nfsroot.c	Fri Apr  4 11:05:59 1997
@@ -51,8 +51,6 @@
  *				without giving a path name. Fix BOOTP request
  *				for domainname (domainname is NIS domain, not
  *				DNS domain!). Skip dummy devices for BOOTP.
- *	Jacek Zapala	:	Fixed a bug which prevented server-ip address
- *				from nfsroot parameter from being used.
  *
  */
 
@@ -79,22 +77,20 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#ifdef CONFIG_AX25
 #include <net/ax25.h>	/* For AX25_P_IP */
 #endif
 #include <linux/skbuff.h>
 #include <linux/socket.h>
 #include <linux/route.h>
-#include <linux/ip.h>
 #include <linux/nfs.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/in.h>
-#include <linux/netlink.h>
 #include <net/route.h>
 #include <net/sock.h>
 
-#include <asm/uaccess.h>
+#include <asm/segment.h>
 
 /* Range of privileged ports */
 #define STARTPORT	600
@@ -154,6 +150,8 @@
 /* Yes, we use sys_socket, but there's no include file for it */
 extern asmlinkage int sys_socket(int family, int type, int protocol);
 
+
+
 /***************************************************************************
 
 			Device Handling Subroutines
@@ -172,20 +170,18 @@
 
 	last = &open_base;
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (
-#if !CONFIG_AP1000
-		    dev->type < ARPHRD_SLIP &&
-#endif
+		if (dev->type < ARPHRD_SLIP &&
 		    dev->family == AF_INET &&
 		    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
 		    (0 != strncmp(dev->name, "dummy", 5)) &&
 		    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
-			struct ifreq ifr;
+			/* First up the interface */
 			old_flags = dev->flags;
-			strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
-			ifr.ifr_flags = old_flags | IFF_UP | IFF_RUNNING;
-			if (dev_ioctl(SIOCSIFFLAGS, &ifr) < 0)
+			dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+			if (!(old_flags & IFF_UP) && dev_open(dev)) {
+				dev->flags = old_flags;
 				continue;
+			}
 			openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
 						GFP_ATOMIC);
 			if (openp == NULL)
@@ -226,10 +222,9 @@
 		nextp = openp->next;
 		openp->next = NULL;
 		if (openp->dev != root_dev) {
-			struct ifreq ifr;
-			strncpy(ifr.ifr_name, openp->dev->name, IFNAMSIZ);
-			ifr.ifr_flags = openp->old_flags;
-			dev_ioctl(SIOCSIFFLAGS, &ifr);
+			if (!(openp->old_flags & IFF_UP))
+				dev_close(openp->dev);
+			openp->dev->flags = openp->old_flags;
 		}
 		kfree_s(openp, sizeof(struct open_dev));
 		openp = nextp;
@@ -295,7 +290,7 @@
 	unsigned long sip, tip;
 	unsigned char *sha, *tha;		/* s for "source", t for "target" */
 
-	/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
+	/* If this test doesn't pass, its not IP, or we should ignore it anyway */
 	if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -309,7 +304,7 @@
 
 	/* If it's not ethernet or AX25, delete it. */
 	if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#ifdef CONFIG_AX25
 	   (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
 #endif
 	    rarp->ar_pln != 4) {
@@ -465,23 +460,17 @@
  */
 static int root_add_bootp_route(void)
 {
-	struct {
-		struct nlmsghdr nlh;
-		struct in_rtmsg rtm;
-	} rtreq;
-	int err;
-	unsigned short fs;
-
-	memset(&rtreq, 0, sizeof(rtreq));
-	rtreq.nlh.nlmsg_type = RTMSG_NEWROUTE;
-	rtreq.nlh.nlmsg_len = sizeof(rtreq);
-	rtreq.rtm.rtmsg_ifindex = bootp_dev->ifindex;
-	rtreq.rtm.rtmsg_flags = RTF_UP;
-	fs = get_fs();
-	set_fs(get_ds());
-	err = ip_rt_ioctl(SIOCRTMSG, &rtreq);
-	set_fs(fs);
-	if (err) {
+	struct rtentry route;
+
+	memset(&route, 0, sizeof(route));
+	route.rt_dev = bootp_dev->name;
+	route.rt_mss = bootp_dev->mtu;
+	route.rt_flags = RTF_UP;
+	((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
+	((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
+	((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
+	((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
+	if (ip_rt_new(&route)) {
 		printk(KERN_ERR "BOOTP: Adding of route failed!\n");
 		return -1;
 	}
@@ -495,25 +484,14 @@
  */
 static int root_del_bootp_route(void)
 {
-	struct {
-		struct nlmsghdr nlh;
-		struct in_rtmsg rtm;
-	} rtreq;
-	int err;
-	unsigned short fs;
+	struct rtentry route;
 
 	if (!bootp_have_route)
 		return 0;
-	memset(&rtreq, 0, sizeof(rtreq));
-	rtreq.nlh.nlmsg_type = RTMSG_DELROUTE;
-	rtreq.nlh.nlmsg_len = sizeof(rtreq);
-	rtreq.rtm.rtmsg_ifindex = bootp_dev->ifindex;
-	rtreq.rtm.rtmsg_flags = RTF_UP;
-	fs = get_fs();
-	set_fs(get_ds());
-	err = ip_rt_ioctl(SIOCRTMSG, &rtreq);
-	set_fs(fs);
-	if (err) {
+	memset(&route, 0, sizeof(route));
+	((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
+	((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
+	if (ip_rt_kill(&route)) {
 		printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
 		return -1;
 	}
@@ -598,13 +576,10 @@
 	iov.iov_base = buf;
 	iov.iov_len = size;
 	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
-	msg.msg_flags = 0;
-	result = sock_sendmsg(sock, &msg, size);
+	msg.msg_accrights = NULL;
+	result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
 	set_fs(oldfs);
 	return (result != size);
 }
@@ -625,13 +600,11 @@
 	iov.iov_base = buf;
 	iov.iov_len = size;
 	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
+	msg.msg_accrights = NULL;
 	msg.msg_namelen = 0;
-	result = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT);
+	result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
 	set_fs(oldfs);
 	return result;
 }
@@ -746,9 +719,9 @@
 		return -1;
 
 	/* Bind/connect the sockets */
-	bootp_xmit_sock->sk->broadcast = 1;
-	bootp_xmit_sock->sk->reuse = 1;
-	bootp_recv_sock->sk->reuse = 1;
+	((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
+	((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
+	((struct sock *) bootp_recv_sock->data) -> reuse = 1;
 	if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
 	    root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
 	    root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
@@ -788,9 +761,9 @@
  */
 static void root_do_bootp_ext(u8 *ext)
 {
-#ifdef NFSROOT_BOOTP_DEBUG
 	u8 *c;
 
+#ifdef NFSROOT_BOOTP_DEBUG
 	printk("BOOTP: Got extension %02x",*ext);
 	for(c=ext+2; c<ext+2+ext[1]; c++)
 		printk(" %02x", *c);
@@ -1087,8 +1060,6 @@
 			break;
 		if (*cp == '.' || octets == 3)
 			octets++;
-		if (octets < 4)
-			cp++;
 		cq = cp;
 	}
 	if (octets == 4 && (*cp == ':' || *cp == '\0')) {
@@ -1104,7 +1075,7 @@
 						sizeof(nfs_data.hostname)-1);
 
 	/* Set the name of the directory to mount */
-	if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
+	if (nfs_path[0] == '\0' || !strncmp(name, "default", 7))
 		strncpy(buf, name, NFS_MAXPATHLEN);
 	else
 		strncpy(buf, nfs_path, NFS_MAXPATHLEN);
@@ -1294,42 +1265,15 @@
 		num++;
 	}
 	rarp_serv = server;
-#if CONFIG_AP1000
-	ap_nfs_hook(server.sin_addr.s_addr);
-#endif				
-}
-
-static int root_nfs_add_default_route(struct in_addr gw, struct device *dev)
-{
-	unsigned short fs;
-	int err;
-	struct {
-		struct nlmsghdr nlh;
-		struct in_rtmsg rtm;
-	} rtreq;
-
-	memset(&rtreq, 0, sizeof(rtreq));
-	rtreq.nlh.nlmsg_type = RTMSG_NEWROUTE;
-	rtreq.nlh.nlmsg_len  = sizeof(rtreq);
-
-	rtreq.rtm.rtmsg_ifindex = dev->ifindex;
-	rtreq.rtm.rtmsg_flags = RTF_UP|RTF_GATEWAY;
-	rtreq.rtm.rtmsg_gateway = gw;
-	fs = get_fs();
-	set_fs(get_ds());
-	err = ip_rt_ioctl(SIOCRTMSG, &rtreq);
-	set_fs(fs);
-	return err;
 }
 
+
 /*
  *  Set the interface address and configure a route to the server.
  */
 static int root_nfs_setup(void)
 {
-	unsigned short fs;
-	struct ifreq ifr;
-	int err;
+	struct rtentry route;
 
 	/* Set the default system name in case none was previously found */
 	if (!system_utsname.nodename[0]) {
@@ -1337,22 +1281,16 @@
 		system_utsname.nodename[__NEW_UTS_LEN] = '\0';
 	}
 
-	fs = get_fs();
-	set_fs(get_ds());
-	strncpy(ifr.ifr_name, root_dev->name, IFNAMSIZ);
-	memcpy(&ifr.ifr_addr, &myaddr, sizeof(myaddr));
-	devinet_ioctl(SIOCSIFADDR, &ifr);
-	if (netmask.sin_addr.s_addr != INADDR_NONE &&
-	    netmask.sin_addr.s_addr != root_dev->pa_mask) {
-		memcpy(&ifr.ifr_netmask, &netmask, sizeof(netmask));
-		devinet_ioctl(SIOCSIFNETMASK, &ifr);
-		memcpy(&ifr.ifr_broadaddr, &netmask, sizeof(netmask));
-		((struct sockaddr_in*)&ifr.ifr_broadaddr)->sin_addr.s_addr =
-			root_dev->pa_addr | ~root_dev->pa_mask;
-		devinet_ioctl(SIOCSIFBRDADDR, &ifr);
-	}
-	netmask.sin_addr.s_addr = root_dev->pa_mask;
-	set_fs(fs);
+	/* Set the correct netmask */
+	if (netmask.sin_addr.s_addr == INADDR_NONE)
+		netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
+
+	/* Setup the device correctly */
+	root_dev->family     = myaddr.sin_family;
+	root_dev->pa_addr    = myaddr.sin_addr.s_addr;
+	root_dev->pa_mask    = netmask.sin_addr.s_addr;
+	root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
+	root_dev->pa_dstaddr = 0;
 
 	/*
 	 * Now add a route to the server. If there is no gateway given,
@@ -1362,10 +1300,28 @@
 	 * gatewayed default route. Note that this gives sufficient network
 	 * setup even for full system operation in all common cases.
 	 */
+	memset(&route, 0, sizeof(route));	/* Local subnet route */
+	route.rt_dev = root_dev->name;
+	route.rt_mss = root_dev->mtu;
+	route.rt_flags = RTF_UP;
+	*((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
+	(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
+	*((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
+	if (ip_rt_new(&route)) {
+		printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
+		return -1;
+	}
 
 	if (gateway.sin_addr.s_addr != INADDR_NONE) {	/* Default route */
-		err = root_nfs_add_default_route(gateway.sin_addr, root_dev); 
-		if (err) {
+		(((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
+		(((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
+		*((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
+		route.rt_flags |= RTF_GATEWAY;
+		if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
+			printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
+			return -1;
+		}
+		if (ip_rt_new(&route)) {
 			printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
 			return -1;
 		}
@@ -1600,7 +1556,7 @@
  */
 static int *root_nfs_header(int proc, int program, int version)
 {
-	gid_t groups[] = { 0 };
+	int groups[] = { 0, NOGROUP };
 
 	if (rpc_packet == NULL) {
 		if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
@@ -1608,7 +1564,7 @@
 			return NULL;
 		}
 	}
-	return rpc_header(rpc_packet, proc, program, version, 0, 0, 1, groups);
+	return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
 }
 
 
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v2.1.31/linux/fs/nfs/proc.c	Fri Nov 22 02:33:29 1996
+++ linux/fs/nfs/proc.c	Fri Apr  4 11:05:59 1997
@@ -21,1017 +21,272 @@
  *  it decodes the packet.
  *
  *  Feel free to fix it and mail me the diffs if it worries you.
+ *
+ *  Completely rewritten to support the new RPC call interface;
+ *  rewrote and moved the entire XDR stuff to xdr.c
+ *  --Olaf Kirch June 1996
  */
 
-/*
- * Defining NFS_PROC_DEBUG causes a lookup of a file named
- * "xyzzy" to toggle debugging.  Just cd to an NFS-mounted
- * filesystem and type 'ls xyzzy' to turn on debugging.
- */
-
-#if 0
-#define NFS_PROC_DEBUG
-#endif
+#define NFS_NEED_XDR_TYPES
 
 #include <linux/param.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
-#include <linux/nfs_fs.h>
 #include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
 #include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
 
-#include <asm/uaccess.h>
-
-#ifdef NFS_PROC_DEBUG
-
-static int proc_debug = 0;
-#define PRINTK(format, args...) \
-	do {						\
-		if (proc_debug)				\
-			printk(format , ## args);	\
-	} while (0)
-
-#else /* !NFS_PROC_DEBUG */
-
-#define PRINTK(format, args...) do ; while (0)
-
-#endif /* !NFS_PROC_DEBUG */
-
-/* Mapping from NFS error code to "errno" error code. */
-#define errno_NFSERR_IO EIO
-
-static int *nfs_rpc_header(int *p, int procedure, int ruid);
-static int *nfs_rpc_verify(int *p);
-static int nfs_stat_to_errno(int stat);
-
-/*
- * Our memory allocation and release functions.
- */
- 
-#define NFS_SLACK_SPACE		1024	/* Total overkill */ 
-/* !!! Be careful, this constant is now also used in sock.c...
-   We should easily convert to not using it anymore for most cases... */
-
-static inline int *nfs_rpc_alloc(int size)
-{
-	int *i;
-
-	while (!(i = (int *)kmalloc(size+NFS_SLACK_SPACE,GFP_NFS))) {
-		schedule();
-	}
-	return i;
-}
-
-static inline void nfs_rpc_free(int *p)
-{
-	kfree((void *)p);
-}
+#include <asm/segment.h>
 
 /*
- * Here are a bunch of xdr encode/decode functions that convert
- * between machine dependent and xdr data formats.
+ * If NFS_DEBUG is defined, you can toggle NFS debugging by causing
+ * a lookup of "xyzzy". Just cd to an NFS-mounted filesystem and type
+ * 'ls xyzzy' to turn on debugging.
  */
+#ifdef NFS_DEBUG
+# define NFSDBG_FACILITY	NFSDBG_PROC
+#endif
 
-#define QUADLEN(len) (((len) + 3) >> 2)
-
-static inline int *xdr_encode_fhandle(int *p, struct nfs_fh *fhandle)
-{
-	*((struct nfs_fh *) p) = *fhandle;
-	return p + QUADLEN(sizeof(*fhandle));
-}
-
-static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle)
-{
-	*fhandle = *((struct nfs_fh *) p);
-	return p + QUADLEN(sizeof(*fhandle));
-}
-
-static inline int *xdr_encode_string(int *p, const char *string)
-{
-	int len = strlen(string);
-	int quadlen = QUADLEN(len);
-
-	p[quadlen] = 0;
-	*p++ = htonl(len);
-	memcpy(p, string, len);
-	return p + quadlen;
-}
-
-static inline int *xdr_decode_string(int *p, char *string, unsigned int maxlen)
-{
-	unsigned int len = ntohl(*p++);
-	if (len > maxlen)
-		return NULL;
-	memcpy(string, p, len);
-	string[len] = '\0';
-	return p + QUADLEN(len);
-}
-
-static inline int *xdr_decode_string2(int *p, char **string, unsigned int *len,
-			unsigned int maxlen)
-{
-	*len = ntohl(*p++);
-	if (*len > maxlen)
-		return NULL;
-	*string = (char *) p;
-	return p + QUADLEN(*len);
-}
-
-
-static inline int *xdr_encode_data(int *p, const char *data, int len)
-{
-	int quadlen = QUADLEN(len);
-	
-	p[quadlen] = 0;
-	*p++ = htonl(len);
-	copy_from_user(p, data, len);
-	return p + quadlen;
-}
-
-static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
-{
-	unsigned len = *lenp = ntohl(*p++);
-	if (len > maxlen)
-		return NULL;
-	memcpy(data, p, len);
-	return p + QUADLEN(len);
-}
-
-static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
-{
-	fattr->type = (enum nfs_ftype) ntohl(*p++);
-	fattr->mode = ntohl(*p++);
-	fattr->nlink = ntohl(*p++);
-	fattr->uid = ntohl(*p++);
-	fattr->gid = ntohl(*p++);
-	fattr->size = ntohl(*p++);
-	fattr->blocksize = ntohl(*p++);
-	fattr->rdev = ntohl(*p++);
-	fattr->blocks = ntohl(*p++);
-	fattr->fsid = ntohl(*p++);
-	fattr->fileid = ntohl(*p++);
-	fattr->atime.seconds = ntohl(*p++);
-	fattr->atime.useconds = ntohl(*p++);
-	fattr->mtime.seconds = ntohl(*p++);
-	fattr->mtime.useconds = ntohl(*p++);
-	fattr->ctime.seconds = ntohl(*p++);
-	fattr->ctime.useconds = ntohl(*p++);
-	return p;
-}
-
-static int *xdr_encode_sattr(int *p, struct nfs_sattr *sattr)
-{
-	*p++ = htonl(sattr->mode);
-	*p++ = htonl(sattr->uid);
-	*p++ = htonl(sattr->gid);
-	*p++ = htonl(sattr->size);
-	*p++ = htonl(sattr->atime.seconds);
-	*p++ = htonl(sattr->atime.useconds);
-	*p++ = htonl(sattr->mtime.seconds);
-	*p++ = htonl(sattr->mtime.useconds);
-	return p;
-}
-
-static int *xdr_decode_entry(int *p, struct nfs_entry *entry)
-{
-	entry->fileid = ntohl(*p++);
-	if (!(p = xdr_decode_string(p, entry->name, NFS_MAXNAMLEN)))
-		return NULL;
-	entry->cookie = ntohl(*p++);
-	entry->eof = 0;
-	return p;
-}
-
-static int *xdr_decode_fsinfo(int *p, struct nfs_fsinfo *res)
-{
-	res->tsize = ntohl(*p++);
-	res->bsize = ntohl(*p++);
-	res->blocks = ntohl(*p++);
-	res->bfree = ntohl(*p++);
-	res->bavail = ntohl(*p++);
-	return p;
-}
 
 /*
  * One function for each procedure in the NFS protocol.
  */
-
-int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-		     struct nfs_fattr *fattr)
+int
+nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+			struct nfs_fattr *fattr)
 {
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  getattr\n");
-	if (!(p0 = nfs_rpc_alloc(server->rsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fattr(p, fattr);
-		PRINTK("NFS reply getattr\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply getattr failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	int	status;
+
+	dprintk("NFS call  getattr\n");
+	status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+	dprintk("NFS reply getattr\n");
 	return status;
 }
 
-int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
-		     struct nfs_sattr *sattr, struct nfs_fattr *fattr)
+int
+nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
+			struct nfs_sattr *sattr, struct nfs_fattr *fattr)
 {
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  setattr\n");
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	p = xdr_encode_sattr(p, sattr);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fattr(p, fattr);
-		PRINTK("NFS reply setattr\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply setattr failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	struct nfs_sattrargs	arg = { fhandle, sattr };
+	int	status;
+
+	dprintk("NFS call  setattr\n");
+	status = rpc_call(server->client, NFSPROC_SETATTR, &arg, fattr, 0);
+	dprintk("NFS reply setattr\n");
 	return status;
 }
 
-int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
+int
+nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
 		    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 {
-	int *p, *p0;
-	int status;
-	int ruid = 0;
+	struct nfs_diropargs	arg = { dir, name };
+	struct nfs_diropok	res = { fhandle, fattr };
+	int			status;
 
-	PRINTK("NFS call  lookup %s\n", name);
-#ifdef NFS_PROC_DEBUG
+	dprintk("NFS call  lookup %s\n", name);
+#ifdef RPC_DEBUG
 	if (!strcmp(name, "xyzzy"))
-		proc_debug = 1 - proc_debug;
+		nfs_debug = ~nfs_debug;
 #endif
-	if (!(p0 = nfs_rpc_alloc(server->rsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fhandle(p, fhandle);
-		p = xdr_decode_fattr(p, fattr);
-		PRINTK("NFS reply lookup\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply lookup failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
+	dprintk("NFS reply lookup: %d\n", status);
 	return status;
 }
 
-int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-		int **p0, char **string, unsigned int *len, unsigned int maxlen)
+int
+nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
+			void **p0, char **string, unsigned int *len,
+			unsigned int maxlen)
 {
-	int *p;
-	int status, ruid = 0;
-
-	PRINTK("NFS call  readlink\n");
-	if (!(*p0 = nfs_rpc_alloc(server->rsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(*p0, NFSPROC_READLINK, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	if ((status = nfs_rpc_call(server, *p0, p, server->rsize)) < 0)
-		return status;
-	if (!(p = nfs_rpc_verify(*p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		if (!(p = xdr_decode_string2(p, string, len, maxlen))) {
-			printk("nfs_proc_readlink: giant pathname\n");
-			status = -errno_NFSERR_IO;
-		}
-		else	/* status = 0, */
-			PRINTK("NFS reply readlink\n");
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply readlink failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	return status;
-}
+	struct nfs_readlinkres	res = { string, len, maxlen, NULL };
+	int			status;
 
-int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
-	  int offset, int count, char *data, struct nfs_fattr *fattr)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-	int len;
-
-	PRINTK("NFS call  read %d @ %d\n", count, offset);
-	if (!(p0 = nfs_rpc_alloc(server->rsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_READ, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	*p++ = htonl(offset);
-	*p++ = htonl(count);
-	*p++ = htonl(count); /* traditional, could be any value */
-	if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fattr(p, fattr);
-		if (!(p = xdr_decode_data(p, data, &len, count))) {
-			printk("nfs_proc_read: giant data size\n"); 
-			status = -errno_NFSERR_IO;
-		}
-		else {
-			status = len;
-			PRINTK("NFS reply read %d\n", len);
-		}
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply read failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	dprintk("NFS call  readlink\n");
+	status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0);
+	dprintk("NFS reply readlink: %d\n", status);
+	if (!status)
+		*p0 = res.buffer;
+	else if (res.buffer)
+		kfree(res.buffer);
 	return status;
 }
 
 int
-nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server,
-			struct nfs_fh *fh, unsigned long offset,
-			unsigned long count, __u32 *buf)
-{
-	__u32	*p, *p0;
-	int	len;
-
-	PRINTK("NFS reqst read %ld @ %ld\n", count, offset);
-	if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE)))
-		return -EIO;
-
-	p = nfs_rpc_header(p0, NFSPROC_READ, 0);
-	p = xdr_encode_fhandle(p, fh);
-	*p++ = htonl(offset);
-	*p++ = htonl(count);
-	*p++ = htonl(count); /* traditional, could be any value */
-	req->rq_svec[0].iov_base = p0;
-	req->rq_svec[0].iov_len  = (p - p0) << 2;
-	req->rq_slen = (p - p0) << 2;
-	req->rq_snr = 1;
-
-	len = (6 + 1 + 17 + 1);		/* standard READ reply header */
-	req->rq_rvec[0].iov_base = p0;
-	req->rq_rvec[0].iov_len  = len << 2;
-	req->rq_rvec[1].iov_base = buf;
-	req->rq_rvec[1].iov_len  = count;
-	req->rq_rvec[2].iov_base = p0 + len;		/* spill buffer */
-	req->rq_rvec[2].iov_len  = (NFS_SLACK_SPACE - len) << 2;
-	req->rq_rlen = count + NFS_SLACK_SPACE;
-	req->rq_rnr = 3;
-
-	req->rq_addr = &server->toaddr;
-	req->rq_alen = sizeof(server->toaddr);
-
-	return rpc_transmit(server->rsock, req);
-}
-
-int
-nfs_proc_read_reply(struct rpc_ioreq *req, struct nfs_fattr *fattr)
-{
-	int		status;
-	__u32		*p0, *p;
-	int		count;
-
-	p0 = (__u32 *) req->rq_rvec[0].iov_base;
-
-	if (!(p = nfs_rpc_verify(p0))) {
-		/* Tell the upper layers to retry */
-		status = -EAGAIN;
-		/* status = -errno_NFSERR_IO; */
-	} else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fattr(p, fattr);
-		count = ntohl(*p++);
-		if (p != req->rq_rvec[2].iov_base) {
-			/* unexpected RPC reply header size. punt.
-			 * fixme: move iovec contents to align data
-			 * on page boundary and adjust RPC header size
-			 * guess. */
-			status = -errno_NFSERR_IO;
-			PRINTK("NFS reply read odd header size %d\n",
-					(p - p0) << 2);
-		} else {
-			status = count;
-			PRINTK("NFS reply read %d\n", count);
-		}
-	}
-	else {
-		PRINTK("NFS reply read failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
+			  unsigned long offset, unsigned int count,
+			  void *buffer, struct nfs_fattr *fattr)
+{
+	struct nfs_readargs	arg = { fhandle, offset, count, buffer };
+	struct nfs_readres	res = { fattr, count };
+	int			status;
+
+	dprintk("NFS call  read %d @ %ld\n", count, offset);
+	status = rpc_call(server->client, NFSPROC_READ, &arg, &res,
+			swap? NFS_RPC_SWAPFLAGS : 0);
+	dprintk("NFS reply read: %d\n", status);
 	return status;
 }
 
-int nfs_proc_write(struct inode * inode, int offset,
-		   int count, const char *data, struct nfs_fattr *fattr)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-	void * kdata;	/* address of kernel copy */
-	struct nfs_server * server = NFS_SERVER(inode);
-	struct nfs_fh *fhandle = NFS_FH(inode);
-
-	PRINTK("NFS call  write %d @ %d\n", count, offset);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	*p++ = htonl(offset); /* traditional, could be any value */
-	*p++ = htonl(offset);
-	*p++ = htonl(count); /* traditional, could be any value */
-	kdata = (void *) (p+1);	/* start of data in RPC buffer */
-	p = xdr_encode_data(p, data, count);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		update_vm_cache(inode, offset, kdata, count);
-		p = xdr_decode_fattr(p, fattr);
-		PRINTK("NFS reply write\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply write failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
-	return status;
+int
+nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
+			unsigned long offset, unsigned int count,
+			const void *buffer, struct nfs_fattr *fattr)
+{
+	struct nfs_writeargs	arg = { fhandle, offset, count, buffer };
+	int			status;
+
+	dprintk("NFS call  write %d @ %ld\n", count, offset);
+	status = rpc_call(server->client, NFSPROC_WRITE, &arg, fattr,
+			swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0);
+	dprintk("NFS reply read: %d\n", status);
+	return status < 0? status : count;
 }
 
-int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
-		    const char *name, struct nfs_sattr *sattr,
-		    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  create %s\n", name);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	p = xdr_encode_sattr(p, sattr);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fhandle(p, fhandle);
-		p = xdr_decode_fattr(p, fattr);
-		PRINTK("NFS reply create\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply create failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+int
+nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
+			const char *name, struct nfs_sattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs_createargs	arg = { dir, name, sattr };
+	struct nfs_diropok	res = { fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  create %s\n", name);
+	status = rpc_call(server->client, NFSPROC_CREATE, &arg, &res, 0);
+	dprintk("NFS reply create: %d\n", status);
 	return status;
 }
 
-int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
+int
+nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
 {
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  remove %s\n", name);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		PRINTK("NFS reply remove\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply remove failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
-	return status;
-}
+	struct nfs_diropargs	arg = { dir, name };
+	int			status;
 
-int nfs_proc_rename(struct nfs_server *server,
-		    struct nfs_fh *old_dir, const char *old_name,
-		    struct nfs_fh *new_dir, const char *new_name,
-		    int must_be_dir)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	/*
-	 * Disallow "rename()" with trailing slashes over NFS: getting
-	 * POSIX.1 behaviour is just too unlikely.
-	 */
-	if (must_be_dir)
-		return -EINVAL;
-	PRINTK("NFS call  rename %s -> %s\n", old_name, new_name);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid);
-	p = xdr_encode_fhandle(p, old_dir);
-	p = xdr_encode_string(p, old_name);
-	p = xdr_encode_fhandle(p, new_dir);
-	p = xdr_encode_string(p, new_name);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		PRINTK("NFS reply rename\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply rename failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	dprintk("NFS call  remove %s\n", name);
+	status = rpc_call(server->client, NFSPROC_REMOVE, &arg, NULL, 0);
+	dprintk("NFS reply remove: %d\n", status);
 	return status;
 }
 
-int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
-		  struct nfs_fh *dir, const char *name)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  link %s\n", name);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_LINK, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		PRINTK("NFS reply link\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply link failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+int
+nfs_proc_rename(struct nfs_server *server,
+			struct nfs_fh *old_dir, const char *old_name,
+			struct nfs_fh *new_dir, const char *new_name)
+{
+	struct nfs_renameargs	arg = { old_dir, old_name, new_dir, new_name };
+	int			status;
+
+	dprintk("NFS call  rename %s -> %s\n", old_name, new_name);
+	status = rpc_call(server->client, NFSPROC_RENAME, &arg, NULL, 0);
+	dprintk("NFS reply rename: %d\n", status);
 	return status;
 }
 
-int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
-		     const char *name, const char *path, struct nfs_sattr *sattr)
+int
+nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
+			struct nfs_fh *dir, const char *name)
 {
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  symlink %s -> %s\n", name, path);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	p = xdr_encode_string(p, path);
-	p = xdr_encode_sattr(p, sattr);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		PRINTK("NFS reply symlink\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply symlink failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
-	return status;
-}
+	struct nfs_linkargs	arg = { fhandle, dir, name };
+	int			status;
 
-int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
-		   const char *name, struct nfs_sattr *sattr,
-		   struct nfs_fh *fhandle, struct nfs_fattr *fattr)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  mkdir %s\n", name);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	p = xdr_encode_sattr(p, sattr);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fhandle(p, fhandle);
-		p = xdr_decode_fattr(p, fattr);
-		PRINTK("NFS reply mkdir\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply mkdir failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	dprintk("NFS call  link %s\n", name);
+	status = rpc_call(server->client, NFSPROC_LINK, &arg, NULL, 0);
+	dprintk("NFS reply link: %d\n", status);
 	return status;
 }
 
-int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  rmdir %s\n", name);
-	if (!(p0 = nfs_rpc_alloc(server->wsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid);
-	p = xdr_encode_fhandle(p, dir);
-	p = xdr_encode_string(p, name);
-	if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		PRINTK("NFS reply rmdir\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply rmdir failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+int
+nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
+			const char *name, const char *path,
+			struct nfs_sattr *sattr)
+{
+	struct nfs_symlinkargs	arg = { dir, name, path, sattr };
+	int			status;
+
+	dprintk("NFS call  symlink %s -> %s\n", name, path);
+	status = rpc_call(server->client, NFSPROC_SYMLINK, &arg, NULL, 0);
+	dprintk("NFS reply symlink: %d\n", status);
 	return status;
 }
 
-int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-		     int cookie, int count, struct nfs_entry *entry)
-{
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-	int i;
-	int size;
-	int eof;
-
-	PRINTK("NFS call  readdir %d @ %d\n", count, cookie);
-	size = server->rsize;
-	if (!(p0 = nfs_rpc_alloc(server->rsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	*p++ = htonl(cookie);
-	*p++ = htonl(size);
-	if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		for (i = 0; i < count && *p++; i++) {
-			if (!(p = xdr_decode_entry(p, entry++)))
-				break;
-		}
-		if (!p) {
-			printk("nfs_proc_readdir: giant filename\n");
-			status = -errno_NFSERR_IO;
-		}
-		else {
-			eof = (i == count && !*p++ && *p++)
-			      || (i < count && *p++);
-			if (eof && i)
-				entry[-1].eof = 1;
-			PRINTK("NFS reply readdir %d %s\n", i,
-			       eof ? "eof" : "");
-			status = i;
-		}
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply readdir failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+int
+nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
+			const char *name, struct nfs_sattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+	struct nfs_createargs	arg = { dir, name, sattr };
+	struct nfs_diropok	res = { fhandle, fattr };
+	int			status;
+
+	dprintk("NFS call  mkdir %s\n", name);
+	status = rpc_call(server->client, NFSPROC_MKDIR, &arg, &res, 0);
+	dprintk("NFS reply mkdir: %d\n", status);
 	return status;
 }
 
-int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
-		    struct nfs_fsinfo *res)
+int
+nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
 {
-	int *p, *p0;
-	int status;
-	int ruid = 0;
-
-	PRINTK("NFS call  statfs\n");
-	if (!(p0 = nfs_rpc_alloc(server->rsize)))
-		return -EIO;
-retry:
-	p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid);
-	p = xdr_encode_fhandle(p, fhandle);
-	if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
-		nfs_rpc_free(p0);
-		return status;
-	}
-	if (!(p = nfs_rpc_verify(p0)))
-		status = -errno_NFSERR_IO;
-	else if ((status = ntohl(*p++)) == NFS_OK) {
-		p = xdr_decode_fsinfo(p, res);
-		PRINTK("NFS reply statfs\n");
-		/* status = 0; */
-	}
-	else {
-		if (!ruid && current->fsuid == 0 && current->uid != 0) {
-			ruid = 1;
-			goto retry;
-		}
-		PRINTK("NFS reply statfs failed = %d\n", status);
-		status = -nfs_stat_to_errno(status);
-	}
-	nfs_rpc_free(p0);
+	struct nfs_diropargs	arg = { dir, name };
+	int			status;
+
+	dprintk("NFS call  rmdir %s\n", name);
+	status = rpc_call(server->client, NFSPROC_RMDIR, &arg, NULL, 0);
+	dprintk("NFS reply rmdir: %d\n", status);
 	return status;
 }
 
 /*
- * Here are a few RPC-assist functions.
+ * The READDIR implementation is somewhat hackish - we pass a temporary
+ * buffer to the encode function, which installs it in the receive
+ * iovec. The dirent buffer itself is passed in the result struct.
  */
-
-int *rpc_header(int *p, int procedure, int program, int version,
-					int uid, int gid,
-					int ngroup, gid_t *groups)
-{
-	int *p1;
-	static int xid = 0;
-	unsigned char *sys = (unsigned char *) system_utsname.nodename;
-
-	if (xid == 0) {
-		xid = CURRENT_TIME;
-		xid ^= (sys[3]<<24) | (sys[2]<<16) | (sys[1]<<8) | sys[0];
-	}
-	*p++ = htonl(++xid);
-	*p++ = htonl(RPC_CALL);
-	*p++ = htonl(RPC_VERSION);
-	*p++ = htonl(program);
-	*p++ = htonl(version);
-	*p++ = htonl(procedure);
-	*p++ = htonl(RPC_AUTH_UNIX);
-	p1 = p++;
-	*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
-	p = xdr_encode_string(p, (char *) sys);
-	*p++ = htonl(uid);
-	*p++ = htonl(gid);
-	if (ngroup > 16)
-		ngroup = 16;
-	*p++ = htonl(ngroup);
-	while (ngroup) {
-		*p++ = htonl(*groups);
-		groups++;
-		ngroup--;
-	}
-	*p1 = htonl((p - (p1 + 1)) << 2);
-	*p++ = htonl(RPC_AUTH_NULL);
-	*p++ = htonl(0);
-	return p;
-}
-
-
-static int *nfs_rpc_header(int *p, int procedure, int ruid)
-{
-	return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
-			(ruid ? current->uid : current->fsuid),
-			current->egid, current->ngroups, current->groups);
-}
-
-
-int *rpc_verify(int *p)
+int
+nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
+			u32 cookie, unsigned int size, struct nfs_entry *entry)
 {
-	unsigned int n;
-
-	p++;
-	if ((n = ntohl(*p++)) != RPC_REPLY) {
-		printk("nfs_rpc_verify: not an RPC reply: %x\n", n);
-		return NULL;
-	}
-	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
-		printk("nfs_rpc_verify: RPC call rejected: %d\n", n);
-		return NULL;
-	}
-	switch (n = ntohl(*p++)) {
-	case RPC_AUTH_NULL: case RPC_AUTH_UNIX: case RPC_AUTH_SHORT:
-		break;
-	default:
-		printk("nfs_rpc_verify: bad RPC authentication type: %d\n", n);
-		return NULL;
-	}
-	if ((n = ntohl(*p++)) > 400) {
-		printk("nfs_rpc_verify: giant auth size\n");
-		return NULL;
-	}
-	p += QUADLEN(n);
-	if ((n = ntohl(*p++)) != RPC_SUCCESS) {
-		printk("nfs_rpc_verify: RPC call failed: %d\n", n);
-		return NULL;
+	struct nfs_readdirargs	arg;
+	struct nfs_readdirres	res;
+	void *			buffer;
+	int			status;
+
+	/* First get a temp buffer for the readdir reply */
+	while (!(buffer = (void *) get_free_page(GFP_USER))) {
+		need_resched = 1;
+		schedule();
+		if (signalled())
+			return -ERESTARTSYS;
 	}
-	return p;
-}
 
-
-static int *nfs_rpc_verify(int *p)
-{
-	return rpc_verify(p);
+	arg.fh = fhandle;
+	arg.cookie = cookie;
+	arg.buffer = buffer;
+	arg.bufsiz = server->rsize < PAGE_SIZE? server->rsize : PAGE_SIZE;
+	res.buffer = entry;
+	res.bufsiz = size;
+
+	dprintk("NFS call  readdir %d\n", cookie);
+	status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
+	dprintk("NFS reply readdir: %d\n", status);
+	free_page((unsigned long) buffer);
+	return status;
 }
 
-
-/*
- * We need to translate between nfs status return values and
- * the local errno values which may not be the same.
- */
-
-static struct {
-	int stat;
-	int errno;
-} nfs_errtbl[] = {
-	{ NFS_OK,		0		},
-	{ NFSERR_PERM,		EPERM		},
-	{ NFSERR_NOENT,		ENOENT		},
-	{ NFSERR_IO,		errno_NFSERR_IO	},
-	{ NFSERR_NXIO,		ENXIO		},
-	{ NFSERR_EAGAIN,	EAGAIN		},
-	{ NFSERR_ACCES,		EACCES		},
-	{ NFSERR_EXIST,		EEXIST		},
-	{ NFSERR_NODEV,		ENODEV		},
-	{ NFSERR_NOTDIR,	ENOTDIR		},
-	{ NFSERR_ISDIR,		EISDIR		},
-	{ NFSERR_INVAL,		EINVAL		},
-	{ NFSERR_FBIG,		EFBIG		},
-	{ NFSERR_NOSPC,		ENOSPC		},
-	{ NFSERR_ROFS,		EROFS		},
-	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
-	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
-	{ NFSERR_DQUOT,		EDQUOT		},
-	{ NFSERR_STALE,		ESTALE		},
-#ifdef EWFLUSH
-	{ NFSERR_WFLUSH,	EWFLUSH		},
-#endif
-	{ -1,			EIO		}
-};
-
-static int nfs_stat_to_errno(int stat)
+int
+nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+			struct nfs_fsinfo *info)
 {
-	int i;
+	int	status;
 
-	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-		if (nfs_errtbl[i].stat == stat)
-			return nfs_errtbl[i].errno;
-	}
-	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
-	return nfs_errtbl[i].errno;
+	dprintk("NFS call  statfs\n");
+	status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0);
+	dprintk("NFS reply statfs: %d\n", status);
+	return status;
 }
-
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.1.31/linux/fs/nfs/read.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/read.c	Fri Apr  4 11:05:59 1997
@@ -0,0 +1,232 @@
+/*
+ * linux/fs/nfs/read.c
+ *
+ * Block I/O for NFS
+ *
+ * Partial copy of Linus' read cache modifications to fs/nfs/file.c
+ * modified for async RPC by okir@monad.swb.de
+ *
+ * We do an ugly hack here in order to return proper error codes to the
+ * user program when a read request failed: since generic_file_read
+ * only checks the return value of inode->i_op->readpage() which is always 0
+ * for async RPC, we set the error bit of the page to 1 when an error occurs,
+ * and make nfs_readpage transmit requests synchronously when encountering this.
+ * This is only a small problem, though, since we now retry all operations
+ * within the RPC code when root squashing is suspected.
+ */
+
+#define NFS_NEED_XDR_TYPES
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#define NFSDBG_FACILITY		NFSDBG_PAGECACHE
+
+struct nfs_rreq {
+	struct inode *		ra_inode;	/* inode from which to read */
+	struct page *		ra_page;	/* page to be read */
+	struct nfs_readargs	ra_args;	/* XDR argument struct */
+	struct nfs_readres	ra_res;		/* ... and result struct */
+	struct nfs_fattr	ra_fattr;	/* fattr storage */
+};
+
+/* Hack for future NFS swap support */
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode)	(0)
+#endif
+
+
+/*
+ * Set up the NFS read request struct
+ */
+static inline void
+nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh,
+		  unsigned long offset, void *buffer, unsigned int rsize)
+{
+	req->ra_args.fh     = fh;
+	req->ra_args.offset = offset;
+	req->ra_args.count  = rsize;
+	req->ra_args.buffer = buffer;
+	req->ra_res.fattr   = &req->ra_fattr;
+	req->ra_res.count   = rsize;
+}
+
+
+/*
+ * Read a page synchronously.
+ */
+int
+nfs_readpage_sync(struct inode *inode, struct page *page)
+{
+	struct nfs_rreq	rqst;
+	unsigned long	offset = page->offset;
+	char		*buffer = (char *) page_address(page);
+	int		rsize = NFS_SERVER(inode)->rsize;
+	int		result, refresh = 0;
+	int		count = PAGE_SIZE;
+	int		flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
+
+	dprintk("NFS: nfs_readpage_sync(%p)\n", page);
+	clear_bit(PG_error, &page->flags);
+
+	do {
+		if (count < rsize)
+			rsize = count;
+
+		dprintk("NFS: nfs_proc_read(%s, (%x,%lx), %ld, %d, %p)\n",
+			NFS_SERVER(inode)->hostname, inode->i_dev,
+			inode->i_ino, offset, rsize, buffer);
+
+		/* Set up arguments and perform rpc call */
+		nfs_readreq_setup(&rqst, NFS_FH(inode), offset, buffer, rsize);
+		result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ,
+					&rqst.ra_args, &rqst.ra_res, flags);
+
+		/*
+		 * Even if we had a partial success we can't mark the page
+		 * cache valid.
+		 */
+		if (result < 0) {
+			if (result == -EISDIR)
+				result = -EINVAL;
+			goto io_error;
+		}
+		refresh = 1;
+		count  -= result;
+		offset += result;
+		buffer += result;
+		if (result < rsize)	/* NFSv2ism */
+			break;
+	} while (count);
+
+	memset(buffer, 0, count);
+	set_bit(PG_uptodate, &page->flags);
+	result = 0;
+
+io_error:
+	if (refresh)
+		nfs_refresh_inode(inode, &rqst.ra_fattr);
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
+	return result;
+}
+
+/*
+ * This is the callback from RPC telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static void
+nfs_readpage_result(struct rpc_task *task)
+{
+	struct nfs_rreq	*req = (struct nfs_rreq *) task->tk_calldata;
+	struct page	*page = req->ra_page;
+	int		result = task->tk_status;
+	static int	succ = 0, fail = 0;
+
+	dprintk("NFS: %4d received callback for page %lx, result %d\n",
+			task->tk_pid, page_address(page), result);
+
+	if (result >= 0) {
+		result = req->ra_res.count;
+		if (result < PAGE_SIZE) {
+			memset((char *) page_address(page) + result, 0,
+							PAGE_SIZE - result);
+		}
+		nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
+		set_bit(PG_uptodate, &page->flags);
+		succ++;
+	} else {
+		set_bit(PG_error, &page->flags);
+		fail++;
+		dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
+	}
+	iput(req->ra_inode);
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
+
+	free_page(page_address(page));
+
+	rpc_release_task(task);
+	kfree(req);
+}
+
+static inline int
+nfs_readpage_async(struct inode *inode, struct page *page)
+{
+	struct nfs_rreq	*req;
+	int		result, flags;
+
+	dprintk("NFS: nfs_readpage_async(%p)\n", page);
+	flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+
+	if (NFS_CONGESTED(inode)
+	 || !(req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req)))) {
+		dprintk("NFS: deferring async READ request.\n");
+		return -1;
+	}
+
+	/* Initialize request */
+	nfs_readreq_setup(req, NFS_FH(inode), page->offset,
+				(void *) page_address(page), PAGE_SIZE);
+	req->ra_inode = inode;
+	req->ra_page = page;
+
+	/* Start the async call */
+	dprintk("NFS: executing async READ request.\n");
+	result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ,
+				&req->ra_args, &req->ra_res, flags,
+				nfs_readpage_result, req);
+
+	if (result >= 0) {
+		inode->i_count++;
+		page->count++;
+		return 0;
+	}
+
+	dprintk("NFS: failed to enqueue async READ request.\n");
+	kfree(req);
+	return -1;
+}
+
+/*
+ * Read a page over NFS.
+ * We read the page synchronously in the following cases:
+ *  -	The file is a swap file. Swap-ins are always sync operations,
+ *	so there's no need bothering to make async reads 100% fail-safe.
+ *  -	The NFS rsize is smaller than PAGE_SIZE. We could kludge our way
+ *	around this by creating several consecutive read requests, but
+ *	that's hardly worth it.
+ *  -	The error flag is set for this page. This happens only when a
+ *	previous async read operation failed.
+ *  -	The server is congested.
+ */
+int
+nfs_readpage(struct inode *inode, struct page *page)
+{
+	unsigned long	address;
+	int		error = -1;
+
+	dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
+	set_bit(PG_locked, &page->flags);
+	address = page_address(page);
+	page->count++;
+	if (!IS_SWAPFILE(inode) && !PageError(page)
+	 && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+		error = nfs_readpage_async(inode, page);
+	if (error < 0)		/* couldn't enqueue */
+		error = nfs_readpage_sync(inode, page);
+	if (error < 0 && IS_SWAPFILE(inode))
+		printk("Aiee.. nfs swap-in of page failed!\n");
+	free_page(address);
+	return error;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- v2.1.31/linux/fs/nfs/rpcsock.c	Sat Jan 25 13:46:13 1997
+++ linux/fs/nfs/rpcsock.c	Wed Dec 31 16:00:00 1969
@@ -1,588 +0,0 @@
-/*
- *  linux/fs/nfs/rpcsock.c
- *
- *  This is a generic RPC call interface for datagram sockets that is able
- *  to place several concurrent RPC requests at the same time. It works like
- *  this:
- *
- *  -	When a process places a call, it allocates a request slot if
- *	one is available. Otherwise, it sleeps on the backlog queue
- *	(rpc_reserve).
- *  -	Then, the message is transmitted via rpc_send (exported by name of
- *	rpc_transmit).
- *  -	Finally, the process waits for the call to complete (rpc_doio):
- *	The first process on the receive queue waits for the next RPC packet,
- *	and peeks at the XID. If it finds a matching request, it receives
- *	the datagram on behalf of that process and wakes it up. Otherwise,
- *	the datagram is discarded.
- *  -	If the process having received the datagram was the first one on
- *	the receive queue, it wakes up the next one to listen for replies.
- *  -	It then removes itself from the request queue (rpc_release).
- *	If there are more callers waiting on the backlog queue, they are
- *	woken up, too.
- *
- * Mar 1996:
- *  -	Split up large functions into smaller chunks as per Linus' coding
- *	style. Found an interesting bug this way, too.
- *  -	Added entry points for nfsiod.
- *
- *  Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
- */
-
-#include <linux/types.h>
-#include <linux/malloc.h>
-#include <linux/sched.h>
-#include <linux/nfs_fs.h>
-#include <linux/errno.h>
-#include <linux/socket.h>
-#include <linux/fcntl.h>
-#include <linux/in.h>
-#include <linux/net.h>
-#include <linux/mm.h>
-#include <linux/rpcsock.h>
-#include <linux/poll.h>
-
-#include <linux/udp.h>
-#include <net/sock.h>
-
-#include <asm/uaccess.h>
-
-#define msleep(sec)	{ current->timeout = sec * HZ / 1000; \
-			  current->state = TASK_INTERRUPTIBLE; \
-			  schedule(); \
-			}
-
-#undef DEBUG_RPC
-#ifdef DEBUG_RPC			
-#define dprintk(args...)	printk(## args)
-#else
-#define	dprintk(args...)
-#endif
-
-
-/*
- * Insert new request into wait list. We make sure list is sorted by
- * increasing timeout value.
- */
-static inline void
-rpc_insque(struct rpc_sock *rsock, struct rpc_wait *slot)
-{
-	struct rpc_wait	*next = rsock->pending;
-
-	slot->w_next = next;
-	slot->w_prev = NULL;
-	if (next)
-		next->w_prev = slot;
-	rsock->pending = slot;
-	slot->w_queued = 1;
-
-	dprintk("RPC: inserted %p into queue\n", slot);
-}
-
-/*
- * Remove request from request queue
- */
-static inline void
-rpc_remque(struct rpc_sock *rsock, struct rpc_wait *slot)
-{
-	struct rpc_wait	*prev = slot->w_prev,
-			*next = slot->w_next;
-
-	if (prev != NULL)
-		prev->w_next = next;
-	else
-		rsock->pending = next;
-	if (next != NULL)
-		next->w_prev = prev;
-
-	slot->w_queued = 0;
-	dprintk("RPC: removed %p from queue, head now %p.\n",
-			slot, rsock->pending);
-}
-
-/*
- * Write data to socket.
- */
-static inline int
-rpc_sendmsg(struct rpc_sock *rsock, struct iovec *iov, int nr, int len,
-				struct sockaddr *sap, int salen)
-{
-	struct socket	*sock = rsock->sock;
-	struct msghdr	msg;
-	unsigned long	oldfs;
-	int		result;
-
-	msg.msg_iov	= iov;
-	msg.msg_iovlen	= nr;
-	msg.msg_name	= sap;
-	msg.msg_namelen = salen;
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
-	msg.msg_flags = 0;
-
-	oldfs = get_fs();
-	set_fs(get_ds());
-	result = sock_sendmsg(sock, &msg, len);
-	set_fs(oldfs);
-
-	dprintk("RPC: rpc_sendmsg(iov %p, len %d) = %d\n", iov, len, result);
-	return result;
-}
-/*
- * Read data from socket
- */
-static inline int
-rpc_recvmsg(struct rpc_sock *rsock, struct iovec *iov,
-			int nr, int len, int flags)
-{
-	struct socket	*sock = rsock->sock;
-	struct sockaddr	sa;
-	struct msghdr	msg;
-	unsigned long	oldfs;
-	int		result;
-
-	msg.msg_iov	= iov;
-	msg.msg_iovlen	= nr;
-	msg.msg_name	= &sa;
-	msg.msg_namelen = sizeof(sa);
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
-	msg.msg_flags = 0;
-
-	oldfs = get_fs();
-	set_fs(get_ds());
-	result = sock_recvmsg(sock, &msg, len, flags|MSG_DONTWAIT);
-	set_fs(oldfs);
-
-	dprintk("RPC: rpc_recvmsg(iov %p, len %d) = %d\n", iov, len, result);
-	return result;
-}
-
-/*
- * This code is slightly complicated. Since the networking code does not
- * honor the current->timeout value, we have to poll on the socket.
- */
-static inline int
-rpc_select(struct rpc_sock *rsock)
-{
-	struct poll_table_entry entry;
-	struct file	*file = rsock->file;
-	poll_table	wait_table;
-
-	dprintk("RPC: polling socket...\n");
-	wait_table.nr = 0;
-	wait_table.entry = &entry;
-	current->state = TASK_INTERRUPTIBLE;
-	if (!(file->f_op->poll(file, &wait_table) & POLLIN)) {
-		schedule();
-		remove_wait_queue(entry.wait_address, &entry.wait);
-		current->state = TASK_RUNNING;
-		if (current->signal & ~current->blocked)
-			return -ERESTARTSYS;
-		if (current->timeout == 0)
-			return -ETIMEDOUT;
-	} else if (wait_table.nr)
-		remove_wait_queue(entry.wait_address, &entry.wait);
-	current->state = TASK_RUNNING;
-	dprintk("RPC: ...Okay, there appears to be some data.\n");
-	return 0;
-}
-
-/*
- * Reserve an RPC call slot. nocwait determines whether we wait in case
- * of congestion or not.
- */
-int
-rpc_reserve(struct rpc_sock *rsock, struct rpc_ioreq *req, int nocwait)
-{
-	struct rpc_wait	*slot;
-
-	req->rq_slot = NULL;
-
-	while (!(slot = rsock->free) || rsock->cong >= rsock->cwnd) {
-		if (nocwait) {
-			current->timeout = 0;
-			return -ENOBUFS;
-		}
-		dprintk("RPC: rpc_reserve waiting on backlog\n");
-		interruptible_sleep_on(&rsock->backlog);
-		if (current->timeout == 0)
-			return -ETIMEDOUT;
-		if (current->signal & ~current->blocked)
-			return -ERESTARTSYS;
-		if (rsock->shutdown)
-			return -EIO;
-	}
-
-	rsock->free = slot->w_next;
-	rsock->cong += RPC_CWNDSCALE;	/* bump congestion value */
-
-	slot->w_queued = 0;
-	slot->w_gotit = 0;
-	slot->w_req = req;
-
-	dprintk("RPC: reserved slot %p\n", slot);
-	req->rq_slot = slot;
-	return 0;
-}
-
-/*
- * Release an RPC call slot
- */
-void
-rpc_release(struct rpc_sock *rsock, struct rpc_ioreq *req)
-{
-	struct rpc_wait	*slot = req->rq_slot;
-
-	if (slot != NULL) {
-		dprintk("RPC: release slot %p\n", slot);
-
-		/* Wake up the next receiver */
-		if (slot == rsock->pending && slot->w_next != NULL)
-			wake_up(&slot->w_next->w_wait);
-
-		/* remove slot from queue of pending */
-		if (slot->w_queued)
-			rpc_remque(rsock, slot);
-		slot->w_next = rsock->free;
-		rsock->free = slot;
-
-		/* decrease congestion value */
-		rsock->cong -= RPC_CWNDSCALE;
-		if (rsock->cong < rsock->cwnd && rsock->backlog)
-			wake_up(&rsock->backlog);
-		if (rsock->shutdown)
-			wake_up(&rsock->shutwait);
-
-		req->rq_slot = NULL;
-	}
-}
-
-/*
- * Adjust RPC congestion window
- */
-static void
-rpc_cwnd_adjust(struct rpc_sock *rsock, int timeout)
-{
-	unsigned long	cwnd = rsock->cwnd;
-
-	if (!timeout) {
-		if (rsock->cong >= cwnd) {
-			/* The (cwnd >> 1) term makes sure
-			 * the result gets rounded properly. */
-			cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE +
-					(cwnd >> 1)) / cwnd;
-			if (cwnd > RPC_MAXCWND)
-				cwnd = RPC_MAXCWND;
-		}
-	} else {
-		if ((cwnd >>= 1) < RPC_CWNDSCALE)
-			cwnd = RPC_CWNDSCALE;
-		dprintk("RPC: cwnd decrease %08lx\n", cwnd);
-	}
-	dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx\n",
-			rsock->cong, rsock->cwnd, cwnd);
-
-	rsock->cwnd = cwnd;
-}
-
-static inline void
-rpc_send_check(char *where, u32 *ptr)
-{
-	if (ptr[1] != htonl(RPC_CALL) || ptr[2] != htonl(RPC_VERSION)) {
-		printk("RPC: %s sending evil packet:\n"
-		       "     %08x %08x %08x %08x %08x %08x %08x %08x\n",
-		       where,
-		       ptr[0], ptr[1], ptr[2], ptr[3],
-		       ptr[4], ptr[5], ptr[6], ptr[7]);
-	}
-}
-
-/*
- * Place the actual RPC call.
- * We have to copy the iovec because sendmsg fiddles with its contents.
- */
-static inline int
-rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot)
-{
-	struct rpc_ioreq *req = slot->w_req;
-	struct iovec	iov[UIO_FASTIOV];
-
-	if (rsock->shutdown)
-		return -EIO;
-
-	memcpy(iov, req->rq_svec, req->rq_snr * sizeof(iov[0]));
-	slot->w_xid = *(u32 *)(iov[0].iov_base);
-	if (!slot->w_queued)
-		rpc_insque(rsock, slot);
-
-	dprintk("rpc_send(%p, %x)\n", slot, slot->w_xid);
-	rpc_send_check("rpc_send", (u32 *) req->rq_svec[0].iov_base);
-	return rpc_sendmsg(rsock, iov, req->rq_snr, req->rq_slen,
-				req->rq_addr, req->rq_alen);
-}
-
-/*
- * This is the same as rpc_send but for the functions exported to nfsiod
- */
-int
-rpc_transmit(struct rpc_sock *rsock, struct rpc_ioreq *req)
-{
-	rpc_send_check("rpc_transmit", (u32 *) req->rq_svec[0].iov_base);
-	return rpc_send(rsock, req->rq_slot);
-}
-
-/*
- * Receive and dispatch a single reply
- */
-static inline int
-rpc_grok(struct rpc_sock *rsock)
-{
-	struct rpc_wait	*rovr;
-	struct rpc_ioreq *req;
-	struct iovec	iov[UIO_FASTIOV];
-	u32		xid;
-	int		safe, result;
-
-	iov[0].iov_base = (void *) &xid;
-	iov[0].iov_len  = sizeof(xid);
-	result = rpc_recvmsg(rsock, iov, 1, sizeof(xid), MSG_PEEK);
-
-	if (result < 0) {
-		switch (-result) {
-		case EAGAIN: case ECONNREFUSED:
-			return 0;
-		case ERESTARTSYS:
-			return result;
-		default:
-			dprintk("rpc_grok: recv error = %d\n", result);
-		}
-	}
-	if (result < 4) {
-		printk(KERN_WARNING "RPC: impossible RPC reply size %d\n",
-						result);
-		iov[0].iov_base=(void*)&xid;	/* xid=32bits, which is large enough */
-		iov[0].iov_len=result;
-		rpc_recvmsg(rsock, iov, 1, result, 0);
-		return 0;
-	}
-
-	dprintk("RPC: rpc_grok: got xid %08lx\n", (unsigned long) xid);
-
-	/* Look for the caller */
-	safe = 0;
-	for (rovr = rsock->pending; rovr; rovr = rovr->w_next) {
-		if (rovr->w_xid == xid)
-			break;
-		if (safe++ > RPC_MAXREQS) {
-			printk(KERN_WARNING "RPC: loop in request Q!!\n");
-			rovr = NULL;
-			break;
-		}
-	}
-
-	if (!rovr || rovr->w_gotit) {
-		/* discard dgram */
-		dprintk("RPC: rpc_grok: %s.\n",
-			rovr? "duplicate reply" : "bad XID");
-		iov[0].iov_base = (void *) &xid;
-		iov[0].iov_len  = sizeof(xid);
-		rpc_recvmsg(rsock, iov, 1, sizeof(xid), 0);
-		return 0;
-	}
-	req = rovr->w_req;
-
-	/* Now receive the reply... Copy the iovec first because of 
-	 * memcpy_fromiovec fiddling. */
-	memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
-	result = rpc_recvmsg(rsock, iov, req->rq_rnr, req->rq_rlen, 0);
-	rovr->w_result = result;
-	rovr->w_gotit = 1;
-
-	/* ... and wake up the process */
-	wake_up(&rovr->w_wait);
-
-	return result;
-}
-
-/*
- * Wait for the reply to our call.
- */
-static int
-rpc_recv(struct rpc_sock *rsock, struct rpc_wait *slot)
-{
-	int	result;
-
-	do {
-		/* If we are not the receiver, wait on the sidelines */
-		dprintk("RPC: rpc_recv TP1\n");
-		while (rsock->pending != slot) {
-			if (!slot->w_gotit)
-				interruptible_sleep_on(&slot->w_wait);
-			if (slot->w_gotit)
-				return slot->w_result; /* quite important */
-			if (current->signal & ~current->blocked)
-				return -ERESTARTSYS;
-			if (rsock->shutdown)
-				return -EIO;
-			if (current->timeout == 0)
-				return -ETIMEDOUT;
-		}
-
-		/* Wait for data to arrive */
-		if ((result = rpc_select(rsock)) < 0) {
-			dprintk("RPC: select error = %d\n", result);
-			return result;
-		}
-
-		/* Receive and dispatch */
-		if ((result = rpc_grok(rsock)) < 0)
-			return result;
-	} while (current->timeout && !slot->w_gotit);
-
-	return slot->w_gotit? slot->w_result : -ETIMEDOUT;
-}
-
-/*
- * Generic RPC call routine. This handles retries and timeouts etc pp.
- *
- * If sent is non-null, it assumes the called has already sent out the
- * message, so it won't need to do so unless a timeout occurs.
- */
-int
-rpc_doio(struct rpc_sock *rsock, struct rpc_ioreq *req,
-			struct rpc_timeout *strategy, int sent)
-{
-	struct rpc_wait	*slot;
-	int		result, retries;
-	unsigned long	timeout;
-
-	timeout = strategy->to_initval;
-	retries = 0;
-	slot = req->rq_slot;
-
-	do {
-		dprintk("RPC: rpc_doio: TP1 (req %p)\n", req);
-		current->timeout = jiffies + timeout;
-		if (slot == NULL) {
-			result = rpc_reserve(rsock, req, 0);
-			if (result == -ETIMEDOUT)
-				goto timedout;
-			if (result < 0)
-				break;
-			slot = req->rq_slot;
-			rpc_send_check("rpc_doio",
-				(u32 *) req->rq_svec[0].iov_base);
-			rpc_insque(rsock, slot);
-		}
-
-		/* This check is for loopback NFS. Sometimes replies come
-		 * in before biod has called rpc_doio... */
-		if (slot->w_gotit) {
-			result = slot->w_result;
-			break;
-		}
-
-		dprintk("RPC: rpc_doio: TP2\n");
-		if (sent || (result = rpc_send(rsock, slot)) >= 0) {
-			result = rpc_recv(rsock, slot);
-			sent = 0;
-		}
-
-		if (result != -ETIMEDOUT) {
-			/* dprintk("RPC: rpc_recv returned %d\n", result); */
-			rpc_cwnd_adjust(rsock, 0);
-			break;
-		}
-
-		rpc_cwnd_adjust(rsock, 1);
-
-timedout:
-		dprintk("RPC: rpc_recv returned timeout.\n");
-		if (strategy->to_exponential)
-			timeout <<= 1;
-		else
-			timeout += strategy->to_increment;
-		if (strategy->to_maxval && timeout >= strategy->to_maxval)
-			timeout = strategy->to_maxval;
-		if (strategy->to_retries && ++retries >= strategy->to_retries)
-			break;
-	} while (1);
-
-	dprintk("RPC: rpc_doio: TP3\n");
-	current->timeout = 0;
-	return result;
-}
-
-/*
- */
-int
-rpc_call(struct rpc_sock *rsock, struct rpc_ioreq *req,
-			struct rpc_timeout *strategy)
-{
-	int	result;
-
-	result = rpc_doio(rsock, req, strategy, 0);
-	if (req->rq_slot == NULL)
-		printk(KERN_WARNING "RPC: bad: rq_slot == NULL\n");
-	rpc_release(rsock, req);
-	return result;
-}
-
-struct rpc_sock *
-rpc_makesock(struct file *file)
-{
-	struct rpc_sock	*rsock;
-	struct socket	*sock;
-	struct sock	*sk;
-	struct rpc_wait	*slot;
-	int		i;
-
-	dprintk("RPC: make RPC socket...\n");
-	sock = &file->f_inode->u.socket_i;
-	if (sock->type != SOCK_DGRAM || sock->ops->family != AF_INET) {
-		printk(KERN_WARNING "RPC: only UDP sockets supported\n");
-		return NULL;
-	}
-	sk = sock->sk;
-
-	if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
-		return NULL;
-	memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */
-
-	rsock->sock = sock;
-	rsock->inet = sk;
-	rsock->file = file;
-	rsock->cwnd = RPC_INITCWND;
-
-	dprintk("RPC: slots %p, %p, ...\n", rsock->waiting, rsock->waiting + 1);
-	rsock->free = rsock->waiting;
-	for (i = 0, slot = rsock->waiting; i < RPC_MAXREQS-1; i++, slot++)
-		slot->w_next = slot + 1;
-	slot->w_next = NULL;
-
-	dprintk("RPC: made socket %p\n", rsock);
-	return rsock;
-}
-
-int
-rpc_closesock(struct rpc_sock *rsock)
-{
-	unsigned long	t0 = jiffies;
-
-	rsock->shutdown = 1;
-	while (rsock->pending || waitqueue_active(&rsock->backlog)) {
-		interruptible_sleep_on(&rsock->shutwait);
-		if (current->signal & ~current->blocked)
-			return -EINTR;
-#if 1
-		if (t0 && t0 - jiffies > 60 * HZ) {
-			printk(KERN_WARNING "RPC: hanging in rpc_closesock.\n");
-			t0 = 0;
-		}
-#endif
-	}
-
-	kfree(rsock);
-	return 0;
-}
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/sock.c linux/fs/nfs/sock.c
--- v2.1.31/linux/fs/nfs/sock.c	Mon Oct 28 04:29:26 1996
+++ linux/fs/nfs/sock.c	Wed Dec 31 16:00:00 1969
@@ -1,127 +0,0 @@
-/*
- *  linux/fs/nfs/sock.c
- *
- *  Copyright (C) 1992, 1993  Rick Sladkey
- *
- *  low-level nfs remote procedure call interface
- *
- * FIXES
- *
- * 2/7/94 James Bottomley and Jon Peatfield DAMTP, Cambridge University
- *
- * An xid mismatch no longer causes the request to be trashed.
- *
- * Peter Eriksson - incorrect XID used to confuse Linux
- * Florian La Roche - use the correct max size, if reading a packet and
- *                    also verify, if the whole packet has been read...
- *                    more checks should be done in proc.c...
- *
- */
-
-#include <linux/sched.h>
-#include <linux/nfs_fs.h>
-#include <linux/errno.h>
-#include <linux/socket.h>
-#include <linux/fcntl.h>
-#include <linux/in.h>
-#include <linux/net.h>
-#include <linux/mm.h>
-#include <linux/rpcsock.h>
-
-#include <asm/uaccess.h>
-
-#define _S(nr) (1<<((nr)-1))
-
-/*
- * Place a synchronous call to the NFS server, meaning that the process
- * sleeps in rpc_call until it either receives a reply or a major timeout
- * occurs.
- * This is now merely a front-end to nfs_rpc_doio.
- */
-int
-nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
-{
-	struct rpc_ioreq	req;
-
-	size += 1024;		/* account for NFS slack space. ugly */
-
-	req.rq_addr = &server->toaddr;
-	req.rq_alen = sizeof(server->toaddr);
-	req.rq_slot = NULL;
-
-	req.rq_svec[0].iov_base = start;
-	req.rq_svec[0].iov_len = (end - start) << 2;
-	req.rq_slen = (end - start) << 2;
-	req.rq_snr = 1;
-	req.rq_rvec[0].iov_base = start;
-	req.rq_rvec[0].iov_len = size;
-	req.rq_rlen = size;
-	req.rq_rnr = 1;
-
-	return nfs_rpc_doio(server, &req, 0);
-}
-
-int
-nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *req, int async)
-{
-	struct rpc_timeout	timeout;
-	unsigned long		maxtimeo;
-	unsigned long		oldmask;
-	int			major_timeout_seen, result;
-
-	timeout.to_initval = server->timeo;
-	timeout.to_maxval = NFS_MAX_RPC_TIMEOUT*HZ/10;
-	timeout.to_retries = server->retrans;
-	timeout.to_exponential = 1;
-
-	oldmask = current->blocked;
-	current->blocked |= ~(_S(SIGKILL)
-		| ((server->flags & NFS_MOUNT_INTR)
-		? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL
-			? _S(SIGINT) : 0)
-		| (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL
-			? _S(SIGQUIT) : 0))
-		: 0));
-
-	major_timeout_seen = 0;
-	maxtimeo = timeout.to_maxval;
-
-	do {
-		result = rpc_doio(server->rsock, req, &timeout, async);
-		rpc_release(server->rsock, req);	/* Release slot */
-
-		if (current->signal & ~current->blocked)
-			result = -ERESTARTSYS;
-		if (result == -ETIMEDOUT) {
-			if (async)
-				break;
-			if (server->flags & NFS_MOUNT_SOFT) {
-				printk("NFS server %s not responding, "
-					"timed out.\n", server->hostname);
-				result = -EIO;
-				break;
-			}
-			if (!major_timeout_seen) {
-				printk("NFS server %s not responding, "
-					"still trying.\n", server->hostname);
-				major_timeout_seen = 1;
-			}
-			if ((timeout.to_initval <<= 1) >= maxtimeo) {
-				timeout.to_initval = maxtimeo;
-			}
-		} else if (result < 0 && result != -ERESTARTSYS) {
-			printk("NFS: notice message: result = %d.\n", result);
-		}
-	} while (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT));
-
-	if (result >= 0 && major_timeout_seen)
-		printk("NFS server %s OK.\n", server->hostname);
-	/* 20 is the minimum RPC reply header size */
-	if (result >= 0 && result < 20) {
-		printk("NFS: too small read memory size (%d bytes)\n", result);
-		result = -EIO;
-	}
-
-	current->blocked = oldmask;
-	return result;
-}
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- v2.1.31/linux/fs/nfs/symlink.c	Tue Dec 31 11:19:32 1996
+++ linux/fs/nfs/symlink.c	Sat Apr  5 12:39:31 1997
@@ -48,9 +48,10 @@
 static int nfs_follow_link(struct inode *dir, struct inode *inode,
 			   int flag, int mode, struct inode **res_inode)
 {
-	int error, *mem;
+	int error;
 	unsigned int len;
 	char *res, *res2;
+	void *mem;
 
 	*res_inode = NULL;
 	if (!dir) {
@@ -95,9 +96,12 @@
 
 static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
 {
-	int error, *mem;
+	int error;
 	unsigned int len;
 	char *res;
+	void *mem;
+
+	dfprintk(VFS, "nfs: readlink(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
 	if (!S_ISLNK(inode->i_mode)) {
 		iput(inode);
@@ -108,12 +112,10 @@
 	error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
 		&res, &len, buflen);
 	iput(inode);
-	if (!error) {
-		error = copy_to_user(buffer, res, len);
-		if (!error)
-			error = put_user('\0', buffer + len);
-		if (!error)
-			error = len;
+	if (! error) {
+		copy_to_user(buffer, res, len);
+		put_user('\0', buffer + len);
+		error = len;
 	}
 	kfree(mem);
 	return error;
diff -u --recursive --new-file v2.1.31/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.31/linux/fs/nfs/write.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfs/write.c	Sat Apr  5 12:39:52 1997
@@ -0,0 +1,823 @@
+/*
+ * linux/fs/nfs/write.c
+ *
+ * Writing file data over NFS.
+ *
+ * We do it like this: When a (user) process wishes to write data to an
+ * NFS file, a write request is allocated that contains the RPC task data
+ * plus some info on the page to be written, and added to the inode's
+ * write chain. If the process writes past the end of the page, an async
+ * RPC call to write the page is scheduled immediately; otherwise, the call
+ * is delayed for a few seconds.
+ *
+ * Just like readahead, no async I/O is performed if wsize < PAGE_SIZE.
+ *
+ * Write requests are kept on the inode's writeback list. Each entry in
+ * that list references the page (portion) to be written. When the
+ * cache timeout has expired, the RPC task is woken up, and tries to
+ * lock the page. As soon as it manages to do so, the request is moved
+ * from the writeback list to the writelock list.
+ *
+ * Note: we must make sure never to confuse the inode passed in the
+ * write_page request with the one in page->inode. As far as I understant
+ * it, these are different when doing a swap-out.
+ *
+ * To understand everything that goes one here and in the nfs read code,
+ * one should be aware that a page is locked in exactly one of the following
+ * cases:
+ *
+ *  -	A write request is in progress.
+ *  -	A user process is in generic_file_write/nfs_update_page
+ *  -	A user process is in generic_file_read
+ *
+ * Also note that because of the way pages are invalidated in
+ * nfs_revalidate_inode, the following assertions hold:
+ *
+ *  -	If a page is dirty, there will be no read requests (a page will
+ *	not be re-read unless invalidated by nfs_revalidate_inode).
+ *  -	If the page is not uptodate, there will be no pending write
+ *	requests, and no process will be in nfs_update_page.
+ *
+ * FIXME: Interaction with the vmscan routines is not optimal yet.
+ * Either vmscan must be made nfs-savvy, or we need a different page
+ * reclaim concept that supports something like FS-independent
+ * buffer_heads with a b_ops-> field.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define NFS_NEED_XDR_TYPES
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+#include <asm/uaccess.h>
+
+#define NFSDBG_FACILITY		NFSDBG_PAGECACHE
+
+static void			nfs_wback_lock(struct rpc_task *task);
+static void			nfs_wback_result(struct rpc_task *task);
+
+/*
+ * This struct describes a file region to be written.
+ * It's kind of a pity we have to keep all these lists ourselves, rather
+ * than sticking an extra pointer into struct page.
+ */
+struct nfs_wreq {
+	struct rpc_listitem	wb_list;	/* linked list of req's */
+	struct rpc_task		wb_task;	/* RPC task */
+	struct inode *		wb_inode;	/* inode referenced */
+	struct page *		wb_page;	/* page to be written */
+	unsigned int		wb_offset;	/* offset within page */
+	unsigned int		wb_bytes;	/* dirty range */
+	pid_t			wb_pid;		/* owner process */
+	unsigned short		wb_flags;	/* status flags */
+
+	struct nfs_writeargs *	wb_args;	/* NFS RPC stuff */
+	struct nfs_fattr *	wb_fattr;	/* file attributes */
+};
+#define wb_status		wb_task.tk_status
+
+#define WB_NEXT(req)		((struct nfs_wreq *) ((req)->wb_list.next))
+
+/*
+ * Various flags for wb_flags
+ */
+#define NFS_WRITE_WANTLOCK	0x0001	/* needs to lock page */
+#define NFS_WRITE_LOCKED	0x0002	/* holds lock on page */
+#define NFS_WRITE_CANCELLED	0x0004	/* has been cancelled */
+#define NFS_WRITE_UNCOMMITTED	0x0008	/* written but uncommitted (NFSv3) */
+#define NFS_WRITE_INVALIDATE	0x0010	/* invalidate after write */
+#define NFS_WRITE_INPROGRESS	0x0020	/* RPC call in progress */
+
+#define WB_INPROGRESS(req)	((req)->wb_flags & NFS_WRITE_INPROGRESS)
+#define WB_WANTLOCK(req)	((req)->wb_flags & NFS_WRITE_WANTLOCK)
+#define WB_HAVELOCK(req)	((req)->wb_flags & NFS_WRITE_LOCKED)
+#define WB_CANCELLED(req)	((req)->wb_flags & NFS_WRITE_CANCELLED)
+#define WB_UNCOMMITTED(req)	((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
+#define WB_INVALIDATE(req)	((req)->wb_flags & NFS_WRITE_INVALIDATE)
+
+/*
+ * Cache parameters
+ */
+#define NFS_WRITEBACK_DELAY	(10 * HZ)
+#define NFS_WRITEBACK_MAX	64
+
+/*
+ * Limit number of delayed writes
+ */
+static int			nr_write_requests = 0;
+static struct rpc_wait_queue	write_queue = RPC_INIT_WAITQ("write_chain");
+struct nfs_wreq *		nfs_failed_requests = NULL;
+
+/* Hack for future NFS swap support */
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode)	(0)
+#endif
+
+/*
+ * Unlock a page after writing it
+ */
+static inline void
+nfs_unlock_page(struct page *page)
+{
+	dprintk("NFS:      unlock %ld\n", page->offset);
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
+
+#ifdef CONFIG_NFS_SWAP
+	/* async swap-out support */
+	if (clear_bit(PG_decr_after, &page->flags))
+		atomic_dec(&page->count);
+	if (clear_bit(PG_swap_unlock_after, &page->flags))
+		swap_after_unlock_page(page->swap_unlock_entry);
+#endif
+}
+
+/*
+ * Transfer a page lock to a write request waiting for it.
+ */
+static inline void
+transfer_page_lock(struct nfs_wreq *req)
+{
+	dprintk("NFS:      transfer_page_lock\n");
+
+	req->wb_flags &= ~NFS_WRITE_WANTLOCK;
+	req->wb_flags |= NFS_WRITE_LOCKED;
+	rpc_wake_up_task(&req->wb_task);
+
+	dprintk("nfs:      wake up task %d (flags %x)\n",
+			req->wb_task.tk_pid, req->wb_flags);
+}
+
+/*
+ * Write a page synchronously.
+ * Offset is the data offset within the page.
+ */
+static int
+nfs_writepage_sync(struct inode *inode, struct page *page,
+				unsigned long offset, unsigned int count)
+{
+	struct nfs_fattr fattr;
+	unsigned int	wsize = NFS_SERVER(inode)->wsize;
+	int		result, refresh = 0, written = 0;
+	u8		*buffer;
+
+	dprintk("NFS:      nfs_writepage_sync(%x/%ld %d@%ld)\n",
+				inode->i_dev, inode->i_ino,
+				count, page->offset + offset);
+
+	buffer = (u8 *) page_address(page) + offset;
+	offset += page->offset;
+
+	do {
+		if (count < wsize && !IS_SWAPFILE(inode))
+			wsize = count;
+
+		result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
+					offset, wsize, IS_SWAPFILE(inode),
+					buffer, &fattr);
+
+		if (result < 0) {
+			/* Must mark the page invalid after I/O error */
+			clear_bit(PG_uptodate, &page->flags);
+			goto io_error;
+		}
+		refresh = 1;
+		buffer  += wsize;
+		offset  += wsize;
+		written += wsize;
+		count   -= wsize;
+	} while (count);
+
+io_error:
+	if (refresh) {
+		/* See comments in nfs_wback_result */
+		if (fattr.size < inode->i_size)
+			fattr.size = inode->i_size;
+		/* Solaris 2.5 server seems to send garbled
+		 * fattrs occasionally */
+		if (inode->i_ino == fattr.fileid)
+			nfs_refresh_inode(inode, &fattr);
+	}
+
+	nfs_unlock_page(page);
+	return written? written : result;
+}
+
+/*
+ * Append a writeback request to a list
+ */
+static inline void
+append_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+{
+	dprintk("NFS:      append_write_request(%p, %p)\n", q, wreq);
+	rpc_append_list(q, wreq);
+}
+
+/*
+ * Remove a writeback request from a list
+ */
+static inline void
+remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
+{
+	dprintk("NFS:      remove_write_request(%p, %p)\n", q, wreq);
+	rpc_remove_list(q, wreq);
+}
+
+/*
+ * Find a write request for a given page
+ */
+static inline struct nfs_wreq *
+find_write_request(struct inode *inode, struct page *page)
+{
+	struct nfs_wreq	*head, *req;
+
+	dprintk("NFS:      find_write_request(%x/%ld, %p)\n",
+				inode->i_dev, inode->i_ino, page);
+	if (!(req = head = NFS_WRITEBACK(inode)))
+		return NULL;
+	do {
+		if (req->wb_page == page)
+			return req;
+	} while ((req = WB_NEXT(req)) != head);
+	return NULL;
+}
+
+/*
+ * Find a failed write request by pid
+ */
+static inline struct nfs_wreq *
+find_failed_request(struct inode *inode, pid_t pid)
+{
+	struct nfs_wreq	*head, *req;
+
+	if (!(req = head = nfs_failed_requests))
+		return NULL;
+	do {
+		if (req->wb_inode == inode && req->wb_pid == pid)
+			return req;
+	} while ((req = WB_NEXT(req)) != head);
+	return NULL;
+}
+
+/*
+ * Try to merge adjacent write requests. This works only for requests
+ * issued by the same user.
+ */
+static inline int
+update_write_request(struct nfs_wreq *req, unsigned first, unsigned bytes)
+{
+	unsigned	rqfirst = req->wb_offset,
+			rqlast = rqfirst + req->wb_bytes,
+			last = first + bytes;
+
+	dprintk("nfs:      trying to update write request %p\n", req);
+
+	/* Check the credentials associated with this write request.
+	 * If the buffer is owned by the same user, we can happily
+	 * add our data without risking server permission problems.
+	 * Note that I'm not messing around with RPC root override creds
+	 * here, because they're used by swap requests only which
+	 * always write out full pages. */
+	if (!rpcauth_matchcred(&req->wb_task, req->wb_task.tk_cred)) {
+		dprintk("NFS:      update failed (cred mismatch)\n");
+		return 0;
+	}
+
+	if (first < rqfirst)
+		rqfirst = first;
+	if (rqlast < last)
+		rqlast = last;
+	req->wb_offset = rqfirst;
+	req->wb_bytes  = rqlast - rqfirst;
+
+	return 1;
+}
+
+/*
+ * Create and initialize a writeback request
+ */
+static inline struct nfs_wreq *
+create_write_request(struct inode *inode, struct page *page,
+				unsigned offset, unsigned bytes)
+{
+	struct nfs_wreq *wreq;
+	struct rpc_clnt	*clnt = NFS_CLIENT(inode);
+	struct rpc_task	*task;
+
+	dprintk("NFS:      create_write_request(%x/%ld, %ld+%d)\n",
+				inode->i_dev, inode->i_ino,
+				page->offset + offset, bytes);
+
+	/* FIXME: Enforce hard limit on number of concurrent writes? */
+
+	wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER);
+	if (!wreq)
+		return NULL;
+	memset(wreq, 0, sizeof(*wreq));
+
+	task = &wreq->wb_task;
+	rpc_init_task(task, clnt, nfs_wback_result, 0);
+	task->tk_calldata = wreq;
+	task->tk_action = nfs_wback_lock;
+
+	rpcauth_lookupcred(task);	/* Obtain user creds */
+	if (task->tk_status < 0) {
+		rpc_release_task(task);
+		kfree(wreq);
+		return NULL;
+	}
+
+	/* Put the task on inode's writeback request list. */
+	wreq->wb_inode  = inode;
+	wreq->wb_pid    = current->pid;
+	wreq->wb_page   = page;
+	wreq->wb_offset = offset;
+	wreq->wb_bytes  = bytes;
+	inode->i_count++;
+	page->count++;
+
+	append_write_request(&NFS_WRITEBACK(inode), wreq);
+
+	if (nr_write_requests++ > NFS_WRITEBACK_MAX*3/4)
+		rpc_wake_up_next(&write_queue);
+
+	return wreq;
+}
+
+/*
+ * Schedule a writeback RPC call.
+ * If the server is congested, don't add to our backlog of queued
+ * requests but call it synchronously.
+ * The function returns true if the page has been unlocked as the
+ * consequence of a synchronous write call.
+ *
+ * FIXME: Here we could walk the inode's lock list to see whether the
+ * page we're currently writing to has been write-locked by the caller.
+ * If it is, we could schedule an async write request with a long
+ * delay in order to avoid writing back the page until the lock is
+ * released.
+ */
+static inline int
+schedule_write_request(struct nfs_wreq *req, int sync)
+{
+	struct rpc_task	*task = &req->wb_task;
+	struct inode	*inode = req->wb_inode;
+
+	if (NFS_CONGESTED(inode) || nr_write_requests >= NFS_WRITEBACK_MAX)
+		sync = 1;
+
+	if (sync) {
+		dprintk("NFS: %4d schedule_write_request (sync)\n",
+					task->tk_pid);
+		/* Page is already locked */
+		req->wb_flags |= NFS_WRITE_LOCKED;
+		rpc_execute(task);
+	} else {
+		dprintk("NFS: %4d schedule_write_request (async)\n",
+					task->tk_pid);
+		task->tk_flags |= RPC_TASK_ASYNC;
+		task->tk_timeout = NFS_WRITEBACK_DELAY;
+		rpc_sleep_on(&write_queue, task, NULL, NULL);
+	}
+
+	return sync == 0;
+}
+
+/*
+ * Wait for request to complete
+ * This is almost a copy of __wait_on_page
+ */
+static inline int
+wait_on_write_request(struct nfs_wreq *req)
+{
+	struct wait_queue	wait = { current, NULL };
+	struct page		*page = req->wb_page;
+
+	add_wait_queue(&page->wait, &wait);
+	page->count++;
+repeat:
+	current->state = TASK_INTERRUPTIBLE;
+	if (PageLocked(page)) {
+		schedule();
+		goto repeat;
+	}
+	remove_wait_queue(&page->wait, &wait);
+	current->state = TASK_RUNNING;
+	atomic_dec(&page->count);
+	return signalled()? -ERESTARTSYS : 0;
+}
+
+/*
+ * Write a page to the server. This will be used for NFS swapping only
+ * (for now), and we currently do this synchronously only.
+ */
+int
+nfs_writepage(struct inode *inode, struct page *page)
+{
+	return nfs_writepage_sync(inode, page, 0, PAGE_SIZE);
+}
+
+/*
+ * Update and possibly write a cached page of an NFS file.
+ * The page is already locked when we get here.
+ *
+ * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad
+ * things with a page scheduled for an RPC call (e.g. invalidate it).
+ */
+int
+nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
+			unsigned long offset, unsigned int count, int sync)
+{
+	struct nfs_wreq	*req;
+	int		status = 0, page_locked = 1;
+	u8		*page_addr;
+
+	dprintk("NFS:      nfs_updatepage(%x/%ld %d@%ld, sync=%d)\n",
+				inode->i_dev, inode->i_ino,
+				count, page->offset+offset, sync);
+
+	page_addr = (u8 *) page_address(page);
+
+	/* If wsize is smaller than page size, update and write
+	 * page synchronously.
+	 */
+	if (NFS_SERVER(inode)->wsize < PAGE_SIZE) {
+		copy_from_user(page_addr + offset, buffer, count);
+		return nfs_writepage_sync(inode, page, offset, count);
+	}
+
+	/*
+	 * Try to find a corresponding request on the writeback queue.
+	 * If there is one, we can be sure that this request is not
+	 * yet being processed, because we hold a lock on the page.
+	 *
+	 * If the request was created by us, update it. Otherwise,
+	 * transfer the page lock and flush out the dirty page now.
+	 * After returning, generic_file_write will wait on the
+	 * page and retry the update.
+	 */
+	if ((req = find_write_request(inode, page)) != NULL) {
+		if (update_write_request(req, offset, count)) {
+			copy_from_user(page_addr + offset, buffer, count);
+			goto updated;
+		}
+		dprintk("NFS:      wake up conflicting write request.\n");
+		transfer_page_lock(req);
+		return 0;
+	}
+
+	/* Create the write request. */
+	if (!(req = create_write_request(inode, page, offset, count))) {
+		status = -ENOBUFS;
+		goto done;
+	}
+
+	/* Copy data to page buffer. */
+	copy_from_user(page_addr + offset, buffer, count);
+
+	/* Schedule request */
+	page_locked = schedule_write_request(req, sync);
+
+updated:
+	/*
+	 * If we wrote up to the end of the chunk, transmit request now.
+	 * We should be a bit more intelligent about detecting whether a
+	 * process accesses the file sequentially or not.
+	 */
+	if (page_locked && (offset + count >= PAGE_SIZE || sync))
+		req->wb_flags |= NFS_WRITE_WANTLOCK;
+
+	/* If the page was written synchronously, return any error that
+	 * may have happened; otherwise return the write count. */
+	if (page_locked || (status = nfs_write_error(inode)) >= 0)
+		status = count;
+
+done:
+	/* Unlock page and wake up anyone sleeping on it */
+	if (page_locked) {
+		if (req && WB_WANTLOCK(req)) {
+			transfer_page_lock(req);
+			/* rpc_execute(&req->wb_task); */
+			if (sync) {
+				wait_on_write_request(req);
+				if ((count = nfs_write_error(inode)) < 0)
+					status = count;
+			}
+		} else
+			nfs_unlock_page(page);
+	}
+
+	dprintk("NFS:      nfs_updatepage returns %d (isize %ld)\n",
+						status, inode->i_size);
+	return status;
+}
+
+/*
+ * Flush out a dirty page.
+ */ 
+static inline void
+nfs_flush_request(struct nfs_wreq *req)
+{
+	struct page	*page = req->wb_page;
+
+	dprintk("NFS:      nfs_flush_request(%x/%ld, @%ld)\n",
+				page->inode->i_dev, page->inode->i_ino,
+				page->offset);
+
+	req->wb_flags |= NFS_WRITE_WANTLOCK;
+	if (!set_bit(PG_locked, &page->flags)) {
+		transfer_page_lock(req);
+	} else {
+		printk(KERN_WARNING "NFS oops in %s: can't lock page!\n",
+					__FUNCTION__);
+		rpc_wake_up_task(&req->wb_task);
+	}
+}
+
+/*
+ * Flush writeback requests. See nfs_flush_dirty_pages for details.
+ */
+static struct nfs_wreq *
+nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len,
+						int invalidate)
+{
+	struct nfs_wreq *head, *req, *last = NULL;
+	off_t		rqoffset, rqend, end;
+
+	end = len? offset + len : 0x7fffffffUL;
+
+	req = head = NFS_WRITEBACK(inode);
+	while (req != NULL) {
+		dprintk("NFS: %4d nfs_flush inspect %x/%ld @%ld fl %x\n",
+				req->wb_task.tk_pid,
+				req->wb_inode->i_dev, req->wb_inode->i_ino,
+				req->wb_page->offset, req->wb_flags);
+		if (!WB_INPROGRESS(req)) {
+			rqoffset = req->wb_page->offset + req->wb_offset;
+			rqend    = rqoffset + req->wb_bytes;
+
+			if (rqoffset < end && offset < rqend
+			 && (pid == 0 || req->wb_pid == pid)) {
+				if (!WB_HAVELOCK(req))
+					nfs_flush_request(req);
+				last = req;
+			}
+		}
+		if (invalidate)
+			req->wb_flags |= NFS_WRITE_INVALIDATE;
+		if ((req = WB_NEXT(req)) == head)
+			break;
+	}
+
+	return last;
+}
+
+/*
+ * Cancel all writeback requests, both pending and in process.
+ */
+static void
+nfs_cancel_dirty(struct inode *inode, pid_t pid)
+{
+	struct nfs_wreq *head, *req;
+
+	req = head = NFS_WRITEBACK(inode);
+	while (req != NULL) {
+		if (req->wb_pid == pid) {
+			req->wb_flags |= NFS_WRITE_CANCELLED;
+			rpc_exit(&req->wb_task, 0);
+		}
+		if ((req = WB_NEXT(req)) == head)
+			break;
+	}
+}
+
+/*
+ * Flush out all dirty pages belonging to a certain user process and
+ * maybe wait for the RPC calls to complete.
+ *
+ * Another purpose of this function is sync()ing a file range before a
+ * write lock is released. This is what offset and length are for, even if
+ * this isn't used by the nlm module yet.
+ */
+int
+nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
+{
+	struct nfs_wreq *last = NULL;
+
+	dprintk("NFS:      flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
+				inode->i_dev, inode->i_ino, current->pid,
+				offset, len);
+
+	if (signalled())
+		nfs_cancel_dirty(inode, current->pid);
+
+	while (!signalled()) {
+		/* Flush all pending writes for this pid and file region */
+		last = nfs_flush_pages(inode, current->pid, offset, len, 0);
+		if (last == NULL)
+			break;
+		wait_on_write_request(last);
+	}
+
+	return signalled()? -ERESTARTSYS : 0;
+}
+
+/*
+ * Flush out any pending write requests and flag that they be discarded
+ * after the write is complete.
+ *
+ * This function is called from nfs_revalidate_inode just before it calls
+ * invalidate_inode_pages. After nfs_flush_pages returns, we can be sure
+ * that all dirty pages are locked, so that invalidate_inode_pages does
+ * not throw away any dirty pages.
+ */
+void
+nfs_invalidate_pages(struct inode *inode)
+{
+	dprintk("NFS:      nfs_invalidate_pages(%x/%ld)\n",
+				inode->i_dev, inode->i_ino);
+
+	nfs_flush_pages(inode, 0, 0, 0, 1);
+}
+
+/*
+ * Cancel any pending write requests after a given offset
+ * (called from nfs_notify_change).
+ */
+int
+nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset)
+{
+	struct nfs_wreq *req, *head;
+	unsigned long	rqoffset;
+
+	dprintk("NFS:      truncate_dirty_pages(%x/%ld, %ld)\n",
+				inode->i_dev, inode->i_ino, offset);
+
+	req = head = NFS_WRITEBACK(inode);
+	while (req != NULL) {
+		rqoffset = req->wb_page->offset + req->wb_offset;
+
+		if (rqoffset >= offset) {
+			req->wb_flags |= NFS_WRITE_CANCELLED;
+			rpc_exit(&req->wb_task, 0);
+		} else if (rqoffset + req->wb_bytes >= offset) {
+			req->wb_bytes = offset - rqoffset;
+		}
+		if ((req = WB_NEXT(req)) == head)
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if a previous write operation returned an error
+ */
+int
+nfs_check_error(struct inode *inode)
+{
+	struct nfs_wreq	*req;
+	int		status = 0;
+
+	dprintk("nfs:      checking for write error inode %04x/%ld\n",
+			inode->i_dev, inode->i_ino);
+
+	if (!(req = find_failed_request(inode, current->pid)))
+		return 0;
+
+	dprintk("nfs: write error %d inode %04x/%ld\n",
+			req->wb_task.tk_status, inode->i_dev, inode->i_ino);
+
+	status = req->wb_task.tk_status;
+	remove_write_request(&nfs_failed_requests, req);
+	iput(req->wb_inode);
+	kfree(req);
+	return status;
+}
+
+/*
+ * The following procedures make up the writeback finite state machinery:
+ *
+ * 1.	Try to lock the page if not yet locked by us,
+ *	set up the RPC call info, and pass to the call FSM.
+ */
+static void
+nfs_wback_lock(struct rpc_task *task)
+{
+	struct nfs_wreq	*req = (struct nfs_wreq *) task->tk_calldata;
+	struct page	*page = req->wb_page;
+	struct inode	*inode = req->wb_inode;
+
+	dprintk("NFS: %4d nfs_wback_lock (status %d flags %x)\n",
+			task->tk_pid, task->tk_status, req->wb_flags);
+
+	if (!WB_HAVELOCK(req))
+		req->wb_flags |= NFS_WRITE_WANTLOCK;
+
+	if (WB_WANTLOCK(req) && set_bit(PG_locked, &page->flags)) {
+		dprintk("NFS:      page already locked in writeback_lock!\n");
+		task->tk_timeout = 2 * HZ;
+		rpc_sleep_on(&write_queue, task, NULL, NULL);
+		return;
+	}
+	task->tk_status = 0;
+	req->wb_flags &= ~NFS_WRITE_WANTLOCK;
+	req->wb_flags |=  NFS_WRITE_LOCKED;
+
+	if (req->wb_args == 0) {
+		size_t	size = sizeof(struct nfs_writeargs)
+			     + sizeof(struct nfs_fattr);
+		void	*ptr;
+
+		if (!(ptr = kmalloc(size, GFP_KERNEL))) {
+			task->tk_timeout = HZ;
+			rpc_sleep_on(&write_queue, task, NULL, NULL);
+			return;
+		}
+		req->wb_args = (struct nfs_writeargs *) ptr;
+		req->wb_fattr = (struct nfs_fattr *) (req->wb_args + 1);
+	}
+
+	/* Setup the task struct for a writeback call */
+	req->wb_args->fh = NFS_FH(inode);
+	req->wb_args->offset = page->offset + req->wb_offset;
+	req->wb_args->count  = req->wb_bytes;
+	req->wb_args->buffer = (void *) (page_address(page) + req->wb_offset);
+
+	rpc_call_setup(task, NFSPROC_WRITE, req->wb_args, req->wb_fattr, 0);
+
+	req->wb_flags |= NFS_WRITE_INPROGRESS;
+}
+
+/*
+ * 2.	Collect the result
+ */
+static void
+nfs_wback_result(struct rpc_task *task)
+{
+	struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
+	struct inode	*inode;
+	struct page	*page;
+	int		status;
+
+	dprintk("NFS: %4d nfs_wback_result (status %d)\n",
+				task->tk_pid, task->tk_status);
+
+	inode  = req->wb_inode;
+	page   = req->wb_page;
+	status = task->tk_status;
+
+	/* Remove request from writeback list and wake up tasks
+	 * sleeping on it. */
+	remove_write_request(&NFS_WRITEBACK(inode), req);
+
+	if (status < 0) {
+		/*
+		 * An error occurred. Report the error back to the
+		 * application by adding the failed request to the
+		 * inode's error list.
+		 */
+		if (find_failed_request(inode, req->wb_pid)) {
+			status = 0;
+		} else {
+			dprintk("NFS: %4d saving write failure code\n",
+						task->tk_pid);
+			append_write_request(&nfs_failed_requests, req);
+			inode->i_count++;
+		}
+		clear_bit(PG_uptodate, &page->flags);
+	} else if (!WB_CANCELLED(req)) {
+		/* Update attributes as result of writeback. 
+		 * Beware: when UDP replies arrive out of order, we
+		 * may end up overwriting a previous, bigger file size.
+		 */
+		if (req->wb_fattr->size < inode->i_size)
+			req->wb_fattr->size = inode->i_size;
+		/* possible Solaris 2.5 server bug workaround */
+		if (inode->i_ino == req->wb_fattr->fileid)
+			nfs_refresh_inode(inode, req->wb_fattr);
+	}
+
+	rpc_release_task(task);
+
+	if (WB_INVALIDATE(req))
+		clear_bit(PG_uptodate, &page->flags);
+	if (WB_HAVELOCK(req))
+		nfs_unlock_page(page);
+
+	if (req->wb_args) {
+		kfree(req->wb_args);
+		req->wb_args = 0;
+	}
+	if (status >= 0)
+		kfree(req);
+
+	free_page(page_address(page));
+	iput(inode);
+	nr_write_requests--;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/Makefile linux/fs/nfsd/Makefile
--- v2.1.31/linux/fs/nfsd/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/Makefile	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,20 @@
+#
+# Makefile for the linux nfs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := nfsd.o
+O_OBJS   := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
+	    export.o auth.o lockd.o nfscache.o nfsxdr.o
+
+ifdef CONFIG_PROC_FS
+  O_OBJS += stats.o
+endif
+
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/auth.c linux/fs/nfsd/auth.c
--- v2.1.31/linux/fs/nfsd/auth.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/auth.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,47 @@
+/*
+ * linux/fs/nfsd/auth.c
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/nfsd/nfsd.h>
+
+void
+nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
+{
+	struct svc_cred	*cred = &rqstp->rq_cred;
+	int		i;
+
+	if (rqstp->rq_userset)
+		return;
+
+	if (exp->ex_flags & NFSEXP_ALLSQUASH) {
+		cred->cr_uid = exp->ex_anon_uid;
+		cred->cr_gid = exp->ex_anon_gid;
+		cred->cr_groups[0] = NOGROUP;
+	} else if (exp->ex_flags & NFSEXP_ROOTSQUASH) {
+		if (!cred->cr_uid)
+			cred->cr_uid = exp->ex_anon_uid;
+		if (!cred->cr_gid)
+			cred->cr_gid = exp->ex_anon_gid;
+		for (i = 0; i < NGROUPS; i++)
+			if (!cred->cr_groups[i])
+				cred->cr_groups[i] = exp->ex_anon_gid;
+	}
+
+	if (cred->cr_uid != (uid_t) -1)
+		current->fsuid = cred->cr_uid;
+	else
+		current->fsuid = exp->ex_anon_uid;
+	if (cred->cr_gid != (gid_t) -1)
+		current->fsgid = cred->cr_gid;
+	else
+		current->fsgid = exp->ex_anon_gid;
+	for (i = 0; i < NGROUPS; i++)
+		current->groups[i] = cred->cr_groups[i];
+	rqstp->rq_userset = 1;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.1.31/linux/fs/nfsd/export.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/export.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,677 @@
+/*
+ * linux/fs/nfsd/export.c
+ *
+ * NFS exporting and validation.
+ *
+ * We maintain a list of clients, each of which has a list of
+ * exports. To export an fs to a given client, you first have
+ * to create the client entry with NFSCTL_ADDCLIENT, which
+ * creates a client control block and adds it to the hash
+ * table. Then, you call NFSCTL_EXPORT for each fs.
+ *
+ * You cannot currently read the export information from the
+ * kernel. It would be nice to have a /proc file though.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
+ */
+
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/in.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/syscall.h>
+#include <linux/lockd/bind.h>
+
+typedef struct svc_client	svc_client;
+typedef struct svc_export	svc_export;
+
+static svc_export *	exp_find(svc_client *clp, dev_t dev);
+static svc_export *	exp_parent(svc_client *clp, dev_t dev);
+static void		exp_unexport_all(svc_client *clp);
+static void		exp_do_unexport(svc_export *unexp);
+static svc_client *	exp_getclientbyname(char *name);
+static void		exp_freeclient(svc_client *clp);
+static void		exp_unhashclient(svc_client *clp);
+static int		exp_verify_string(char *cp, int max);
+struct inode *		exp_lnamei(char *pathname, int *errp);
+
+#define NFSDDBG_FACILITY	NFSDDBG_EXPORT
+#define CLIENT_HASHBITS		6
+#define CLIENT_HASHMAX		(1 << CLIENT_HASHBITS)
+#define CLIENT_HASHMASK		(CLIENT_HASHMAX - 1)
+#define CLIENT_HASH(a) \
+		((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
+#define EXPORT_HASH(dev)	((dev) & (NFSCLNT_EXPMAX - 1))
+
+struct svc_clnthash {
+	struct svc_clnthash *	h_next;
+	struct in_addr		h_addr;
+	struct svc_client *	h_client;
+};
+static struct svc_clnthash *	clnt_hash[CLIENT_HASHMAX];
+static svc_client *		clients = NULL;
+static int			initialized = 0;
+
+static int			hash_lock = 0;
+static int			want_lock = 0;
+static int			hash_count = 0;
+static struct wait_queue *	hash_wait = NULL;
+
+#define READLOCK		0
+#define WRITELOCK		1
+
+/*
+ * Find the export entry matching xdev/xino.
+ */
+static inline svc_export *
+exp_find(svc_client *clp, dev_t dev)
+{
+	svc_export *	exp;
+
+	exp = clp->cl_export[EXPORT_HASH(dev)];
+	while (exp && exp->ex_dev != dev)
+		exp = exp->ex_next;
+	return exp;
+}
+
+svc_export *
+exp_get(svc_client *clp, dev_t dev, ino_t ino)
+{
+	svc_export *	exp;
+
+	if (!clp)
+		return NULL;
+	exp = exp_find(clp, dev);
+	return (exp && exp->ex_ino == ino)? exp : NULL;
+}
+
+/*
+ * Find the parent export entry for a given fs. This function is used
+ * only by the export syscall to keep the export tree consistent.
+ * nfsd_parentdev(dev) returns the device on which dev is mounted.
+ */
+static svc_export *
+exp_parent(svc_client *clp, dev_t dev)
+{
+	svc_export	*exp;
+	dev_t		xdev = dev;
+
+	do {
+		exp = exp_find(clp, xdev);
+		if (exp)
+			return exp;
+	} while (nfsd_parentdev(&xdev));
+
+	return NULL;
+}
+
+/*
+ * Export a file system.
+ */
+int
+exp_export(struct nfsctl_export *nxp)
+{
+	svc_client	*clp;
+	svc_export	*exp, *parent;
+	svc_export	**head;
+	struct inode	*inode = NULL;
+	int		i, err;
+	dev_t		dev;
+	ino_t		ino;
+
+	/* Consistency check */
+	if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
+	    !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
+		return -EINVAL;
+
+	dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
+			nxp->ex_client, nxp->ex_path,
+			nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
+	dev = nxp->ex_dev;
+	ino = nxp->ex_ino;
+
+	/* Try to lock the export table for update */
+	if ((err = exp_writelock()) < 0)
+		return err;
+
+	/* Look up client info */
+	if (!(clp = exp_getclientbyname(nxp->ex_client))) {
+		err = -EINVAL;
+		goto finish;
+	}
+
+	/*
+	 * If there's already an export for this file, assume this
+	 * is just a flag update.
+	 */
+	if ((exp = exp_find(clp, dev)) != NULL) {
+		/* Ensure there's only one export per FS. */
+		if (exp->ex_ino != ino) {
+			err = -EPERM;
+		} else {
+			exp->ex_flags    = nxp->ex_flags;
+			exp->ex_anon_uid = nxp->ex_anon_uid;
+			exp->ex_anon_gid = nxp->ex_anon_gid;
+			err = 0;
+		}
+		goto finish;
+	}
+
+	/* Look up the inode */
+	if (!(inode = nfsd_iget(nxp->ex_dev, nxp->ex_ino))) {
+		err = -EINVAL;
+		goto finish;
+	}
+
+	/* We currently export only dirs. */
+	if (!S_ISDIR(inode->i_mode)) {
+		err = -ENOTDIR;
+		goto finish;
+	}
+
+	/* If this is a sub-export, must be root of FS */
+	if ((parent = exp_parent(clp, dev)) != NULL) {
+		struct super_block *sb;
+
+		if ((sb = inode->i_sb) && (inode != sb->s_mounted)) {
+			err = -EINVAL;
+			goto finish;
+		}
+	}
+
+	if (!(exp = kmalloc(sizeof(*exp), GFP_USER))) {
+		err = -ENOMEM;
+		goto finish;
+	}
+	dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
+
+	strcpy(exp->ex_path, nxp->ex_path);
+	exp->ex_client = clp;
+	exp->ex_parent = parent;
+	exp->ex_inode = inode;
+	exp->ex_flags = nxp->ex_flags;
+	exp->ex_dev = dev;
+	exp->ex_ino = ino;
+	exp->ex_anon_uid = nxp->ex_anon_uid;
+	exp->ex_anon_gid = nxp->ex_anon_gid;
+
+	/* Update parent pointers of all exports */
+	if (parent) {
+		for (i = 0; i < NFSCLNT_EXPMAX; i++) {
+			svc_export *temp = clp->cl_export[i];
+
+			while (temp) {
+				if (temp->ex_parent == parent)
+					temp->ex_parent = exp;
+				temp = temp->ex_next;
+			}
+		}
+	}
+
+	head = clp->cl_export + EXPORT_HASH(dev);
+	exp->ex_next = *head;
+	*head = exp;
+
+	err = 0;
+
+finish:
+	/* Release inode */
+	if (err < 0 && inode)
+		iput(inode);
+	/* Unlock hashtable */
+	exp_unlock();
+	return err;
+}
+
+/*
+ * Unexport a file system. The export entry has already
+ * been removed from the client's list of exported fs's.
+ */
+static void
+exp_do_unexport(svc_export *unexp)
+{
+	svc_export	*exp;
+	svc_client	*clp;
+	struct inode	*inode;
+	int		i;
+
+	/* Update parent pointers. */
+	clp = unexp->ex_client;
+	for (i = 0; i < NFSCLNT_EXPMAX; i++) {
+		for (exp = clp->cl_export[i]; exp; exp = exp->ex_next)
+			if (exp->ex_parent == unexp)
+				exp->ex_parent = unexp->ex_parent;
+	}
+
+	inode = unexp->ex_inode;
+	if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
+		printk(KERN_WARNING "nfsd: bad inode in unexport!\n");
+	else
+		iput(unexp->ex_inode);
+
+	kfree(unexp);
+}
+
+/*
+ * Revoke all exports for a given client.
+ * This may look very awkward, but we have to do it this way in order
+ * to avoid race conditions (aka mind the parent pointer).
+ */
+static void
+exp_unexport_all(svc_client *clp)
+{
+	svc_export	*exp;
+	int		i;
+
+	dprintk("unexporting all fs's for clnt %p\n", clp);
+	for (i = 0; i < NFSCLNT_EXPMAX; i++) {
+		exp = clp->cl_export[i];
+		clp->cl_export[i] = NULL;
+		while (exp) {
+			svc_export *next = exp->ex_next;
+			exp_do_unexport(exp);
+			exp = next;
+		}
+	}
+}
+
+/*
+ * unexport syscall.
+ */
+int
+exp_unexport(struct nfsctl_export *nxp)
+{
+	svc_client	*clp;
+	svc_export	**expp, *exp = NULL;
+	int		err;
+
+	/* Consistency check */
+	if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
+		return -EINVAL;
+
+	if ((err = exp_writelock()) < 0)
+		return err;
+
+	err = -EINVAL;
+	if ((clp = exp_getclientbyname(nxp->ex_client)) != NULL) {
+		expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev);
+		while ((exp = *expp) != NULL) {
+			if (exp->ex_dev == nxp->ex_dev) {
+				if (exp->ex_ino != nxp->ex_ino)
+					break;
+				*expp = exp->ex_next;
+				exp_do_unexport(exp);
+				err = 0;
+				break;
+			}
+			expp = &(exp->ex_next);
+		}
+	}
+
+	exp_unlock();
+	return err;
+}
+
+/*
+ * Obtain the root fh on behalf of a client.
+ * This could be done in user space, but I feel that it adds some safety
+ * since its harder to fool a kernel module than a user space program.
+ */
+int
+exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f)
+{
+	struct svc_export	*exp = NULL;
+	struct svc_fh		fh;
+
+	dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
+
+	if (!(exp = exp_get(clp, dev, ino)))
+		return -EPERM;
+	exp->ex_inode->i_count++;
+	fh_compose(&fh, exp, exp->ex_inode);
+	memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
+	fh_put(&fh);
+
+	return 0;
+}
+
+/*
+ * Hashtable locking. Write locks are placed only by user processes
+ * wanting to modify export information.
+ */
+void
+exp_readlock(void)
+{
+	while (hash_lock || want_lock)
+		sleep_on(&hash_wait);
+	hash_count++;
+}
+
+int
+exp_writelock(void)
+{
+	want_lock++;
+	while (hash_count || hash_lock)
+		interruptible_sleep_on(&hash_wait);
+	want_lock--;
+	if (current->signal & ~current->blocked)
+		return -EINTR;
+	hash_lock = 1;
+	return 0;
+}
+
+void
+exp_unlock(void)
+{
+	if (hash_count)
+		hash_count--;
+	else
+		hash_lock = 0;
+	wake_up(&hash_wait);
+}
+
+/*
+ * Find a valid client given an inet address. We always move the most
+ * recently used client to the front of the hash chain to speed up
+ * future lookups.
+ * Locking against other processes is the responsibility of the caller.
+ */
+struct svc_client *
+exp_getclient(struct sockaddr_in *sin)
+{
+	struct svc_clnthash	**hp, **head, *tmp;
+	unsigned long		addr = sin->sin_addr.s_addr;
+
+	if (!initialized)
+		return NULL;
+
+	head = &clnt_hash[CLIENT_HASH(addr)];
+
+	for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
+		if (tmp->h_addr.s_addr == addr) {
+			/* Move client to the front */
+			if (head != hp) {
+				*hp = tmp->h_next;
+				tmp->h_next = *head;
+				*head = tmp;
+			}
+
+			return tmp->h_client;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Find a client given its identifier.
+ */
+static svc_client *
+exp_getclientbyname(char *ident)
+{
+	svc_client *	clp;
+
+	for (clp = clients; clp; clp = clp->cl_next) {
+		if (!strcmp(clp->cl_ident, ident))
+			return clp;
+	}
+	return NULL;
+}
+
+/*
+ * Add or modify a client.
+ * Change requests may involve the list of host addresses. The list of
+ * exports and possibly existing uid maps are left untouched.
+ */
+int
+exp_addclient(struct nfsctl_client *ncp)
+{
+	struct svc_clnthash *	ch[NFSCLNT_ADDRMAX];
+	svc_client *		clp;
+	int			i, err, change = 0, ilen;
+
+	/* First, consistency check. */
+	if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)))
+		return -EINVAL;
+	if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
+		return -EINVAL;
+
+	/* Lock the hashtable */
+	if ((err = exp_writelock()) < 0)
+		return err;
+
+	/* First check if this is a change request for a client. */
+	for (clp = clients; clp; clp = clp->cl_next)
+		if (!strcmp(clp->cl_ident, ncp->cl_ident))
+			break;
+
+	if (clp) {
+		change = 1;
+	} else {
+		if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL))) {
+			exp_unlock();
+			return -ENOMEM;
+		}
+		memset(clp, 0, sizeof(*clp));
+
+		dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
+
+		strcpy(clp->cl_ident, ncp->cl_ident);
+		clp->cl_idlen = ilen;
+	}
+
+	/* Allocate hash buckets */
+	for (i = 0; i < ncp->cl_naddr; i++) {
+		if (!(ch[i] = kmalloc(GFP_KERNEL, sizeof(ch[0])))) {
+			while (i--)
+				kfree(ch[i]);
+			if (!change)
+				kfree(clp);
+			exp_unlock();
+			return -ENOMEM;
+		}
+	}
+
+	/* Copy addresses. */
+	for (i = 0; i < ncp->cl_naddr; i++) {
+		clp->cl_addr[i] = ncp->cl_addrlist[i];
+	}
+	clp->cl_naddr = ncp->cl_naddr;
+
+	/* Remove old client hash entries. */
+	if (change)
+		exp_unhashclient(clp);
+
+	/* Insert client into hashtable. */
+	for (i = 0; i < ncp->cl_naddr; i++) {
+		struct in_addr	addr = clp->cl_addr[i];
+		int		hash;
+
+		hash = CLIENT_HASH(addr.s_addr);
+		ch[i]->h_client = clp;
+		ch[i]->h_addr = addr;
+		ch[i]->h_next = clnt_hash[hash];
+		clnt_hash[hash] = ch[i];
+	}
+
+	if (!change) {
+		clp->cl_next = clients;
+		clients = clp;
+	}
+
+	exp_unlock();
+	return 0;
+}
+
+/*
+ * Delete a client given an identifier.
+ */
+int
+exp_delclient(struct nfsctl_client *ncp)
+{
+	svc_client	**clpp, *clp;
+	int		err;
+
+	if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
+		return -EINVAL;
+
+	/* Lock the hashtable */
+	if ((err = exp_writelock()) < 0)
+		return err;
+
+	for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
+		if (!strcmp(ncp->cl_ident, clp->cl_ident))
+			break;
+
+	if (!clp) {
+		exp_unlock();
+		return -EINVAL;
+	}
+	*clpp = clp->cl_next;
+	exp_freeclient(clp);
+
+	exp_unlock();
+	return 0;
+}
+
+/*
+ * Free a client. The caller has already removed it from the client list.
+ */
+static void
+exp_freeclient(svc_client *clp)
+{
+	exp_unhashclient(clp);
+
+	/* umap_free(&(clp->cl_umap)); */
+	exp_unexport_all(clp);
+	nfsd_lockd_unexport(clp);
+	kfree (clp);
+}
+
+/*
+ * Remove client from hashtable. We first collect all hashtable
+ * entries and free them in one go.
+ * The hash table must be writelocked by the caller.
+ */
+static void
+exp_unhashclient(svc_client *clp)
+{
+	struct svc_clnthash	**hpp, *hp, *ch[NFSCLNT_ADDRMAX];
+	int			i, count, err;
+
+again:
+	err = 0;
+	for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) {
+		hpp = clnt_hash + i;
+		while ((hp = *hpp) && !err) {
+			if (hp->h_client == clp) {
+				*hpp = hp->h_next;
+				ch[count++] = hp;
+				err = (count >= NFSCLNT_ADDRMAX);
+			} else {
+				hpp = &(hp->h_next);
+			}
+		}
+	}
+	if (count != clp->cl_naddr)
+		printk(KERN_WARNING "nfsd: bad address count in freeclient!\n");
+	if (err)
+		goto again;
+	for (i = 0; i < count; i++)
+		kfree (ch[i]);
+}
+
+/*
+ * Lockd is shutting down and tells us to unregister all clients
+ */
+void
+exp_nlmdetach(void)
+{
+	struct svc_client	*clp;
+
+	for (clp = clients; clp; clp = clp->cl_next)
+		nfsd_lockd_unexport(clp);
+}
+
+/*
+ * Verify that string is non-empty and does not exceed max length.
+ */
+static int
+exp_verify_string(char *cp, int max)
+{
+	int	i;
+
+	for (i = 0; i < max; i++)
+		if (!cp[i])
+			return i;
+	cp[i] = 0;
+	printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
+	return 0;
+}
+
+#if 0
+/*
+ * Get the inode associated with a pathname. Used by exp_export.
+ */
+struct inode *
+exp_lnamei(char *pathname, int *errp)
+{
+	struct inode	*inode;
+	unsigned long	oldfs;
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	*errp = lnamei(pathname, &inode);
+	set_fs(oldfs);
+
+	return inode;
+}
+#endif
+
+/*
+ * Initialize the exports module.
+ */
+void
+nfsd_export_init(void)
+{
+	int		i;
+
+	dprintk("nfsd: initializing export module.\n");
+	if (initialized)
+		return;
+	for (i = 0; i < CLIENT_HASHMAX; i++)
+		clnt_hash[i] = NULL;
+	clients = NULL;
+
+	initialized = 1;
+	return;
+}
+
+/*
+ * Shutdown the exports module.
+ */
+void
+nfsd_export_shutdown(void)
+{
+	int	i;
+
+	dprintk("nfsd: shutting down export module.\n");
+	if (!initialized)
+		return;
+	if (exp_writelock() < 0) {
+		printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown");
+		return;
+	}
+	for (i = 0; i < CLIENT_HASHMAX; i++) {
+		while (clnt_hash[i])
+			exp_freeclient(clnt_hash[i]->h_client);
+	}
+	exp_unlock();
+	dprintk("nfsd: export shutdown complete.\n");
+
+	return;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/lockd.c linux/fs/nfsd/lockd.c
--- v2.1.31/linux/fs/nfsd/lockd.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/lockd.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,70 @@
+/*
+ * linux/fs/nfsd/lockd.c
+ *
+ * This file contains all the stubs needed when communicating with lockd.
+ * This level of indirection is necessary so we can run nfsd+lockd without
+ * requiring the nfs client to be compiled in/loaded, and vice versa.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/bind.h>
+
+#define NFSDDBG_FACILITY		NFSDDBG_LOCKD
+
+static u32
+nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp)
+{
+	struct svc_fh	fh;
+	u32		nfserr;
+
+	fh.fh_handle = *f;
+	fh.fh_export = NULL;
+	fh.fh_inode  = NULL;
+
+	nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
+	fh_put(&fh);
+	return nfserr;
+}
+
+static void
+nlm_fclose(struct file *filp)
+{
+	nfsd_close(filp);
+}
+
+struct nlmsvc_binding		nfsd_nlm_ops = {
+	exp_readlock,		/* lock export table for reading */
+	exp_unlock,		/* unlock export table */
+	exp_getclient,		/* look up NFS client */
+	nlm_fopen,		/* open file for locking */
+	nlm_fclose,		/* close file */
+	exp_nlmdetach,		/* lockd shutdown notification */
+};
+
+/*
+ * When removing an NFS client entry, notify lockd that it is gone.
+ * FIXME: We should do the same when unexporting an NFS volume.
+ */
+void
+nfsd_lockd_unexport(struct svc_client *clnt)
+{
+	nlmsvc_invalidate_client(clnt);
+}
+
+void
+nfsd_lockd_init(void)
+{
+	dprintk("nfsd: initializing lockd\n");
+	nlmsvc_ops = &nfsd_nlm_ops;
+}
+
+void
+nfsd_lockd_shutdown(void)
+{
+	nlmsvc_ops = NULL;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfs3proc.c linux/fs/nfsd/nfs3proc.c
--- v2.1.31/linux/fs/nfsd/nfs3proc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfs3proc.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,529 @@
+/*
+ * linux/fs/nfsd/nfs3proc.c
+ *
+ * Process version 3 NFS requests.
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr3.h>
+
+typedef struct svc_rqst	svc_rqst;
+typedef struct svc_buf	svc_buf;
+
+#define NFSDDBG_FACILITY		NFSDDBG_PROC
+
+#define RETURN(st)	{ resp->status = (st); return (st); }
+
+static void
+svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
+{
+	*ptr = buf->buf + nr;
+	*len = buf->buflen - buf->len - nr;
+}
+
+static int
+nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+	return nfs_ok;
+}
+
+/*
+ * Get a file's attributes
+ */
+static int
+nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
+					   struct nfsd3_attrstat *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: GETATTR  %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	resp->fh = argp->fh;
+	nfserr = fh_lookup(rqstp, &resp->fh, 0, MAY_NOP);
+	RETURN(nfserr);
+}
+
+/*
+ * Set a file's attributes
+ */
+static int
+nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp,
+					   struct nfsd3_attrstat  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: SETATTR  %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	resp->fh = argp->fh;
+	nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs);
+	RETURN(nfserr);
+}
+
+/*
+ * Look up a path name component
+ */
+static int
+nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+					  struct nfsd3_lookupres *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: LOOKUP   %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	resp->dirfh = argp->fh;
+	nfserr = nfsd_lookup(rqstp, &resp->dirfh,
+				    argp->name,
+				    argp->len,
+				    &resp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Check file access
+ */
+static int
+nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd_fhandle   *argp,
+					  struct nfsd3_accessres *resp)
+{
+	/* to be done */
+	resp->fh = argp->fh;
+	return nfserr_notsupp;
+}
+
+/*
+ * Read a symlink.
+ */
+static int
+nfsd3_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle     *argp,
+					   struct nfsd3_readlinkres *resp)
+{
+	u32		*path;
+	int		dummy, nfserr;
+
+	dprintk("nfsd: READLINK %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	/* Reserve room for status, post_op_attr, and path length */
+	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 1 + 22 + 1);
+
+	/* Read the symlink. */
+	resp->len = NFS3_MAXPATHLEN;
+	nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
+	RETURN(nfserr);
+}
+
+/*
+ * Read a portion of a file.
+ */
+static int
+nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp,
+				        struct nfsd3_readres  *resp)
+{
+	u32 *	buffer;
+	int	nfserr, avail;
+
+	dprintk("nfsd: READ %x/%ld %lu bytes at %lu\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				(unsigned long) argp->count,
+				(unsigned long) argp->offset);
+
+	/* Obtain buffer pointer for payload.
+	 * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
+	 * + 1 (xdr opaque byte count) = 26
+	 */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 26);
+
+	if ((avail << 2) < argp->count) {
+		printk(KERN_NOTICE
+			"oversized read request from %08lx:%d (%d bytes)\n",
+				ntohl(rqstp->rq_addr.sin_addr.s_addr),
+				ntohs(rqstp->rq_addr.sin_port),
+				argp->count);
+		argp->count = avail;
+	}
+
+	resp->count = argp->count;
+	resp->fh    = argp->fh;
+	nfserr = nfsd_read(rqstp, &resp->fh,
+				  argp->offset,
+				  (char *) buffer,
+				  &resp->count);
+
+	RETURN(nfserr);
+}
+
+/*
+ * Write data to a file
+ */
+static int
+nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
+					 struct nfsd3_writeres  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: WRITE    %x/%ld %d bytes at %ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->len,
+				(unsigned long) argp->offset);
+
+	resp->fh = argp->fh;
+	nfserr = nfsd_write(rqstp, &resp->fh,
+				   argp->offset,
+				   argp->data,
+				   argp->len,
+				   argp->stable);
+	resp->committed = argp->stable;
+	RETURN(nfserr);
+}
+
+/*
+ * With NFSv3, CREATE processing is a lot easier than with NFSv2.
+ * At least in theory; we'll see how it fares in practice when the
+ * first reports about SunOS compatibility problems start to pour in...
+ */
+static int
+nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
+					  struct nfsd3_createres  *resp)
+{
+	svc_fh		*dirfhp, *newfhp = NULL;
+	struct iattr	*attr;
+	int		mode;
+	u32		nfserr;
+
+	dprintk("nfsd: CREATE   %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	dirfhp = fh_copy(&resp->dirfh, &argp->fh);
+	newfhp = fh_init(&resp->fh);
+	attr   = &argp->attrs;
+
+	/* Get the directory inode */
+	nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
+	if (nfserr)
+		RETURN(nfserr);
+
+	/* Unfudge the mode bits */
+	attr->ia_mode &= ~S_IFMT;
+	if (!(attr->ia_valid & ATTR_MODE)) { 
+		attr->ia_valid |= ATTR_MODE;
+		attr->ia_mode = S_IFREG;
+	}
+	mode = attr->ia_mode & ~S_IFMT;
+
+	/* Now create the file and set attributes */
+	nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
+				attr, S_IFREG, 0, newfhp,
+				argp->createmode);
+
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+					  struct nfsd3_attrstat  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: REMOVE   %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	fh_copy(&resp->fh, &argp->fh);
+
+	/* Unlink. -S_IFDIR means file must not be a directory */
+	nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR,
+					argp->name, argp->len);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
+				  	 void		        *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				SVCFH_INO(&argp->ffh),
+				argp->fname,
+				SVCFH_DEV(&argp->tfh),
+				SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
+				    &argp->tfh, argp->tname, argp->tlen);
+	fh_put(&argp->ffh);
+	fh_put(&argp->tfh);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
+				void			    *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				SVCFH_INO(&argp->ffh),
+				SVCFH_DEV(&argp->tfh),
+				SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
+				  &argp->ffh);
+	fh_put(&argp->ffh);
+	fh_put(&argp->tfh);
+	RETURN(nfserr);
+}
+
+static int
+nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
+				          void			  *resp)
+{
+	struct svc_fh	newfh;
+	int		nfserr;
+
+	dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
+				SVCFH_DEV(&argp->ffh),
+				SVCFH_INO(&argp->ffh),
+				argp->fname, argp->tname);
+
+	memset(&newfh, 0, sizeof(newfh));
+
+	/*
+	 * Create the link, look up new file and set attrs.
+	 */
+	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
+						 argp->tname, argp->tlen,
+						 &newfh);
+	if (nfserr)
+		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
+
+	fh_put(&argp->ffh);
+	fh_put(&newfh);
+	RETURN(nfserr);
+}
+
+/*
+ * Make directory. This operation is not idempotent.
+ */
+static int
+nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
+					struct nfsd3_diropres   *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: MKDIR    %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
+				    &argp->attrs, S_IFDIR, 0, &resp->fh);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Remove a directory
+ */
+static int
+nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+				 	void		      *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: RMDIR    %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Read a portion of a directory.
+ */
+static int
+nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
+					  struct nfsd3_readdirres  *resp)
+{
+	u32 *	buffer;
+	int	nfserr, count;
+
+	dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->count, argp->cookie);
+
+	/* Reserve buffer space for status */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
+
+	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
+	 * client read size */
+	if ((count -= 8) > argp->count)
+		count = argp->count;
+
+	/* Read directory and encode entries on the fly */
+	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
+					nfssvc_encode_entry,
+					buffer, &count);
+	resp->count = count;
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Get file system info
+ */
+static int
+nfsd3_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
+					  struct nfsd3_statfsres *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: STATFS   %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * NFSv2 Server procedures.
+ * Only the results of non-idempotent operations are cached.
+ */
+#define nfsd3_proc_none		NULL
+#define nfssvc_encode_void	NULL
+#define nfssvc_decode_void	NULL
+#define nfssvc_release_void	NULL
+struct nfsd3_void { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache)	\
+ { (svc_procfunc) nfsd3_proc_##name,	\
+   (kxdrproc_t) nfssvc_decode_##argt,	\
+   (kxdrproc_t) nfssvc_encode_##rest,	\
+   (kxdrproc_t) nfssvc_release_##relt,	\
+   sizeof(struct nfsd3_##argt),		\
+   sizeof(struct nfsd3_##rest),		\
+   0,					\
+   cache				\
+ }
+struct svc_procedure		nfsd3_procedures2[18] = {
+  PROC(null,	 void,		void,		void,	 RC_NOCACHE),
+  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
+  PROC(setattr,  sattrargs,	attrstat,	fhandle, RC_REPLBUFF),
+  PROC(none,	 void,		void,		void,	 RC_NOCACHE),
+  PROC(lookup,	 diropargs,	diropres,	fhandle, RC_NOCACHE),
+  PROC(readlink, fhandle,	readlinkres,	void,	 RC_NOCACHE),
+  PROC(read,	 readargs,	readres,	fhandle, RC_NOCACHE),
+  PROC(none,	 void,		void,		void,	 RC_NOCACHE),
+  PROC(write,	 writeargs,	attrstat,	fhandle, RC_REPLBUFF),
+  PROC(create,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
+  PROC(remove,	 diropargs,	void,		void,	 RC_REPLSTAT),
+  PROC(rename,	 renameargs,	void,		void,	 RC_REPLSTAT),
+  PROC(link,	 linkargs,	void,		void,	 RC_REPLSTAT),
+  PROC(symlink,	 symlinkargs,	void,		void,	 RC_REPLSTAT),
+  PROC(mkdir,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
+  PROC(rmdir,	 diropargs,	void,		void,	 RC_REPLSTAT),
+  PROC(readdir,	 readdirargs,	readdirres,	void,	 RC_REPLSTAT),
+  PROC(statfs,	 fhandle,	statfsres,	void,	 RC_NOCACHE),
+};
+
+
+/*
+ * Map errnos to NFS errnos.
+ */
+int
+nfserrno (int errno)
+{
+	static struct {
+		int	nfserr;
+		int	syserr;
+	} nfs_errtbl[] = {
+		{ NFS_OK, 0 },
+		{ NFSERR_PERM, EPERM },
+		{ NFSERR_NOENT, ENOENT },
+		{ NFSERR_IO, EIO },
+		{ NFSERR_NXIO, ENXIO },
+		{ NFSERR_ACCES, EACCES },
+		{ NFSERR_EXIST, EEXIST },
+		{ NFSERR_NODEV, ENODEV },
+		{ NFSERR_NOTDIR, ENOTDIR },
+		{ NFSERR_ISDIR, EISDIR },
+		{ NFSERR_INVAL, EINVAL },
+		{ NFSERR_FBIG, EFBIG },
+		{ NFSERR_NOSPC, ENOSPC },
+		{ NFSERR_ROFS, EROFS },
+		{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
+		{ NFSERR_NOTEMPTY, ENOTEMPTY },
+#ifdef EDQUOT
+		{ NFSERR_DQUOT, EDQUOT },
+#endif
+		{ NFSERR_STALE, ESTALE },
+		{ NFSERR_WFLUSH, EIO },
+		{ -1, EIO }
+	};
+	int	i;
+
+	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
+		if (nfs_errtbl[i].syserr == errno)
+			return htonl (nfs_errtbl[i].nfserr);
+	}
+	printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
+	return nfserr_io;
+}
+
+#if 0
+static void
+nfsd3_dump(char *tag, u32 *buf, int len)
+{
+	int	i;
+
+	printk(KERN_NOTICE
+		"nfsd: %s (%d words)\n", tag, len);
+
+	for (i = 0; i < len && i < 32; i += 8)
+		printk(KERN_NOTICE
+			" %08lx %08lx %08lx %08lx"
+			" %08lx %08lx %08lx %08lx\n",
+			buf[i],   buf[i+1], buf[i+2], buf[i+3],
+			buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+}
+#endif
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfs3xdr.c linux/fs/nfsd/nfs3xdr.c
--- v2.1.31/linux/fs/nfsd/nfs3xdr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfs3xdr.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,785 @@
+/*
+ * linux/fs/nfsd/nfs3xdr.c
+ *
+ * XDR support for nfsd/protocol version 3.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs3.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/xdr3.h>
+
+#define NFSDDBG_FACILITY		NFSDDBG_XDR
+
+u32	nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
+	nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
+	nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
+	nfserr_nametoolong, nfserr_dquot, nfserr_stale;
+
+#ifdef NFSD_OPTIMIZE_SPACE
+# define inline
+#endif
+
+/*
+ * Mapping of S_IF* types to NFS file types
+ */
+static u32	nfs3_ftypes[] = {
+	NF3NON,  NF3FIFO, NF3CHR, NF3BAD,
+	NF3DIR,  NF3BAD,  NF3BLK, NF3BAD,
+	NF3REG,  NF3BAD,  NF3LNK, NF3BAD,
+	NF3SOCK, NF3BAD,  NF3LNK, NF3BAD,
+};
+
+/*
+ * Initialization of NFS status variables
+ */
+void
+nfs3xdr_init(void)
+{
+	static int	inited = 0;
+
+	if (inited)
+		return;
+
+	nfs_ok = htonl(NFS_OK);
+	nfserr_perm = htonl(NFSERR_PERM);
+	nfserr_noent = htonl(NFSERR_NOENT);
+	nfserr_io = htonl(NFSERR_IO);
+	nfserr_nxio = htonl(NFSERR_NXIO);
+	nfserr_acces = htonl(NFSERR_ACCES);
+	nfserr_exist = htonl(NFSERR_EXIST);
+	nfserr_nodev = htonl(NFSERR_NODEV);
+	nfserr_notdir = htonl(NFSERR_NOTDIR);
+	nfserr_isdir = htonl(NFSERR_ISDIR);
+	nfserr_fbig = htonl(NFSERR_FBIG);
+	nfserr_nospc = htonl(NFSERR_NOSPC);
+	nfserr_rofs = htonl(NFSERR_ROFS);
+	nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
+	nfserr_dquot = htonl(NFSERR_DQUOT);
+	nfserr_stale = htonl(NFSERR_STALE);
+
+	inited = 1;
+}
+
+/*
+ * XDR functions for basic NFS types
+ */
+static inline u32 *
+enc64(u32 *p, u64 val)
+{
+	*p++ = (val >> 32);
+	*p++ = (val & 0xffffffff);
+	return p;
+}
+
+static inline u32 *
+dec64(u32 *p, u64 *valp)
+{
+	*valp  = ((u64) ntohl(*p++)) << 32;
+	*valp |= ntohl(*p++);
+	return p;
+}
+
+static inline u32 *
+encode_time3(u32 *p, time_t secs)
+{
+	*p++ = htonl((u32) secs); *p++ = 0;
+	return p;
+}
+
+static inline u32 *
+decode_time3(u32 *p, time_t *secp)
+{
+	*secp = ntohl(*p++);
+	return p + 1;
+}
+
+static inline u32 *
+decode_fh(u32 *p, struct svc_fh *fhp)
+{
+	if (*p++ != sizeof(struct knfs_fh))
+		return NULL;
+
+	memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh));
+	fhp->fh_inode  = NULL;
+	fhp->fh_export = NULL;
+
+	return p + (sizeof(struct knfs_fh) >> 2);
+}
+
+static inline u32 *
+encode_fh(u32 *p, struct svc_fh *fhp)
+{
+	*p++ = htonl(sizeof(struct knfs_fh));
+	memcpy(p, &fhp->fh_handle, sizeof(struct knfs_fh));
+	return p + (sizeof(struct knfs_fh) >> 2);
+}
+
+/*
+ * Decode a file name and make sure that the path contains
+ * no slashes or null bytes.
+ */
+static inline u32 *
+decode_filename(u32 *p, char **namp, int *lenp)
+{
+	char		*name;
+	int		i;
+
+	if ((p = xdr_decode_string(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
+		for (i = 0, name = *namp; i < *lenp; i++, name++) {
+			if (*name == '\0' || *name == '/')
+				return NULL;
+		}
+		*name = '\0';
+	}
+
+	return p;
+}
+
+static inline u32 *
+decode_pathname(u32 *p, char **namp, int *lenp)
+{
+	char		*name;
+	int		i;
+
+	if ((p = xdr_decode_string(p, namp, lenp, NFS3_MAXPATHLEN)) != NULL) {
+		for (i = 0, name = *namp; i < *lenp; i++, name++) {
+			if (*name == '\0')
+				return NULL;
+		}
+		*name = '\0';
+	}
+
+	return p;
+}
+
+static inline u32 *
+decode_sattr3(u32 *p, struct iattr *iap)
+{
+	u32	tmp;
+
+	iap->ia_valid = 0;
+
+	if (*p++) {
+		iap->ia_valid |= ATTR_MODE;
+		iap->ia_mode = ntohl(*p++);
+	}
+	if (*p++) {
+		iap->ia_valid |= ATTR_UID;
+		iap->ia_uid = ntohl(*p++);
+	}
+	if (*p++) {
+		iap->ia_valid |= ATTR_GID;
+		iap->ia_gid = ntohl(*p++);
+	}
+	if (*p++) {
+		iap->ia_valid |= ATTR_SIZE;
+		iap->ia_size = ntohl(*p++);
+	}
+	if ((tmp = *p++) == 1) {
+		iap->ia_valid |= ATTR_ATIME;
+	} else if (tmp == 2) {
+		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
+		iap->ia_atime = ntohl(*p++), p++;
+	}
+	if ((tmp = *p++) != 0) {
+		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
+	} else if (tmp == 2) {
+		iap->ia_valid |= ATTR_MTIME;
+		iap->ia_mtime = ntohl(*p++), p++;
+	}
+	return p;
+}
+
+static inline u32 *
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+{
+	if (!inode) {
+		printk("nfsd: NULL inode in %s:%d", __FILE__, __LINE__);
+		return NULL;
+	}
+
+	*p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]);
+	*p++ = htonl((u32) inode->i_mode);
+	*p++ = htonl((u32) inode->i_nlink);
+	*p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
+	*p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
+	if (S_ISLNK(inode->i_mode) && inode->i_size > NFS3_MAXPATHLEN) {
+		p = enc64(p, (u64) NFS3_MAXPATHLEN);
+	} else {
+		p = enc64(p, (u64) inode->i_size);
+	}
+	p = enc64(p, inode->i_blksize * inode->i_blocks);
+	*p++ = htonl((u32) MAJOR(inode->i_rdev));
+	*p++ = htonl((u32) MINOR(inode->i_rdev));
+	p = enc64(p, (u64) inode->i_dev);
+	p = enc64(p, (u64) inode->i_ino);
+	p = encode_time3(p, inode->i_atime);
+	p = encode_time3(p, inode->i_mtime);
+	p = encode_time3(p, inode->i_ctime);
+
+	return p;
+}
+
+/*
+ * Encode post-operation attributes.
+ * The inode may be NULL if the call failed because of a stale file
+ * handle. In this case, no attributes are returned.
+ */
+static u32 *
+encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+{
+	if (inode == NULL) {
+		*p++ = xdr_zero;
+		return p;
+	}
+	return encode_fattr3(rqstp, p, inode);
+}
+
+/*
+ * Enocde weak cache consistency data
+ */
+static u32 *
+encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+	struct inode	*inode = fhp->fh_inode;
+
+	if (fhp->fh_post_version == inode->i_version) {
+		*p++ = xdr_one;
+		p = enc64(p, (u64) fhp->fh_pre_size);
+		p = encode_time3(p, fhp->fh_pre_mtime);
+		p = encode_time3(p, fhp->fh_pre_ctime);
+	} else {
+		*p++ = xdr_zero;
+	}
+	return encode_post_op_attr(rqstp, p, inode);
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_argbuf;
+
+	return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_resbuf;
+
+	buf->len = p - buf->base;
+	dprintk("nfsd: ressize_check p %p base %p len %d\n",
+			p, buf->base, buf->buflen);
+	return (buf->len <= buf->buflen);
+}
+
+/*
+ * XDR decode functions
+ */
+int
+nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+	if (!(p = decode_fh(p, fhp)))
+		return 0;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_sattrargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_sattr3(p, &args->attrs))
+	 || (*p++ && !(p = decode_time3(p, &args->guardtime))))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_diropargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_filename(p, &args->name, &args->len)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_accessargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh)))
+		return 0;
+	args->access = ntohl(*p++);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_readargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = dec64(p, &args->offset))
+	 || !(p = dec64(p, &args->count)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_writeargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = dec64(p, &args->offset))
+	 || !(p = dec64(p, &args->count)))
+		return 0;
+
+	args->stable = ntohl(*p++);
+	args->len = ntohl(*p++);
+	args->data = (char *) p;
+	p += (args->len + 3) >> 2;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_createargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_filename(p, &args->name, &args->len)))
+		return 0;
+
+	switch (args->createmode = ntohl(*p++)) {
+	case 0: case 1:
+		if (!(p = decode_sattr3(p, &args->attrs)))
+			return 0;
+		break;
+	case 2:
+		args->verf = p;
+		p += 2;
+		break;
+	default:
+		return 0;
+	}
+
+	return xdr_argsize_check(rqstp, p);
+}
+int
+nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_createargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_filename(p, &args->name, &args->len))
+	 || !(p = decode_sattr3(p, &args->attrs)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_symlinkargs *args)
+{
+	if (!(p = decode_fh(p, &args->ffh))
+	 || !(p = decode_filename(p, &args->fname, &args->flen))
+	 || !(p = decode_sattr3(p, &args->attrs))
+	 || !(p = decode_pathname(p, &args->tname, &args->tlen)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_mknodargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_filename(p, &args->name, &args->len)))
+		return 0;
+
+	args->ftype = ntohl(*p++);
+
+	if (args->ftype == NF3BLK  || args->ftype == NF3CHR
+	 || args->ftype == NF3SOCK || args->ftype == NF3FIFO) {
+		if (!(p = decode_sattr3(p, &args->attrs)))
+			return 0;
+	}
+
+	if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
+		args->major = ntohl(*p++);
+		args->minor = ntohl(*p++);
+	}
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_renameargs *args)
+{
+	if (!(p = decode_fh(p, &args->ffh))
+	 || !(p = decode_filename(p, &args->fname, &args->flen))
+	 || !(p = decode_fh(p, &args->tfh))
+	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_linkargs *args)
+{
+	if (!(p = decode_fh(p, &args->ffh))
+	 || !(p = decode_fh(p, &args->tfh))
+	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_readdirargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh)))
+		return 0;
+	args->cookie = ntohl(*p++);
+	args->verf   = p; p += 2;
+	args->count  = ntohl(*p++);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_readdirargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh)))
+		return 0;
+	args->cookie   = ntohl(*p++);
+	args->verf     = p; p += 2;
+	args->dircount = ntohl(*p++);
+	args->count    = ntohl(*p++);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_commitargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = dec64(p, &args->offset)))
+		return 0;
+	args->count = ntohl(*p++);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+/* GETATTR */
+int
+nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_attrstat *resp)
+{
+	if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* SETATTR, REMOVE, RMDIR */
+int
+nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_attrstat *resp)
+{
+	if (!(p = encode_wcc_data(rqstp, p, &resp->fh)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* LOOKUP */
+int
+nfs3svc_encode_lookupres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_lookupres *resp)
+{
+	if (resp->status == 0) {
+		p = encode_fh(p, &resp->fh);
+		if (!(p = encode_fattr3(rqstp, p, resp->fh.fh_inode)))
+			return 0;
+	}
+	p = encode_post_op_attr(rqstp, p, resp->dirfh.fh_inode);
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* ACCESS */
+int
+nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_accessres *resp)
+{
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	if (resp->status == 0)
+		*p++ = htonl(resp->access);
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* READLINK */
+int
+nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_readlinkres *resp)
+{
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	if (resp->status == 0) {
+		*p++ = htonl(resp->len);
+		p += XDR_QUADLEN(resp->len);
+	}
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* READ */
+int
+nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_readres *resp)
+{
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	if (resp->status == 0) {
+		*p++ = htonl(resp->count);
+		*p++ = htonl(resp->eof);
+		*p++ = htonl(resp->count);	/* xdr opaque count */
+		p += XDR_QUADLEN(resp->count);
+	}
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* WRITE */
+int
+nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_writeres *resp)
+{
+	p = encode_wcc_data(rqstp, p, &resp->fh);
+	if (resp->status == 0) {
+		*p++ = htonl(resp->count);
+		*p++ = htonl(resp->committed);
+		*p++ = htonl(nfssvc_boot.tv_sec);
+		*p++ = htonl(nfssvc_boot.tv_usec);
+	}
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* CREATE, MKDIR, SYMLINK, MKNOD */
+int
+nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_createres *resp)
+{
+	if (resp->status == 0) {
+		p = encode_fh(p, &resp->fh);
+		p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	}
+	p = encode_wcc_data(rqstp, p, &resp->dirfh);
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* RENAME */
+int
+nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_renameres *resp)
+{
+	p = encode_wcc_data(rqstp, p, &resp->ffh);
+	p = encode_wcc_data(rqstp, p, &resp->tfh);
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* LINK */
+int
+nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_linkres *resp)
+{
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	p = encode_wcc_data(rqstp, p, &resp->tfh);
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* READDIR */
+int
+nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_readdirres *resp)
+{
+	p = encode_post_op_attr(rqstp, p, resp->fh.fh_inode);
+	if (resp->status == 0) {
+		/* stupid readdir cookie */
+		*p++ = ntohl(resp->fh.fh_inode->i_mtime);
+		*p++ = xdr_zero;
+		p = resp->list_end;
+	}
+
+	return xdr_ressize_check(rqstp, p);
+}
+
+#define NFS3_ENTRYPLUS_BAGGAGE	((1 + 20 + 1 + NFS3_FHSIZE) << 2)
+int
+nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
+				int namlen, unsigned long offset, ino_t ino)
+{
+	u32		*p = cd->buffer;
+	int		buflen, slen, elen;
+	struct svc_fh	fh;
+
+	if (offset > ~((u64) 0))
+		return -EINVAL;
+	if (cd->offset)
+		*cd->offset = htonl(offset);
+
+	/* For readdirplus, look up the inode */
+	if (cd->plus && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh))
+		return 0;
+
+	/* truncate filename if too long */
+	if (namlen > NFS3_MAXNAMLEN)
+		namlen = NFS3_MAXNAMLEN;
+
+	slen = XDR_QUADLEN(namlen);
+	elen = slen + (cd->plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
+	if ((buflen = cd->buflen - elen - 4) < 0) {
+		cd->eob = 1;
+		if (cd->plus)
+			fh_put(&fh);
+		return -EINVAL;
+	}
+	*p++ = xdr_one;			/* mark entry present */
+	*p++ = xdr_zero;		/* file id (64 bit) */
+	*p++ = htonl((u32) ino);
+	*p++ = htonl((u32) namlen);	/* name length & name */
+	memcpy(p, name, namlen);
+	p += slen;
+
+	/* throw in readdirplus baggage */
+	if (cd->plus) {
+		p = encode_post_op_attr(cd->rqstp, p, fh.fh_inode);
+		p = encode_fh(p, &fh);
+		fh_put(&fh);
+	}
+
+	cd->offset = p;			/* remember pointer */
+	p = enc64(p, ~(u64) 0);	/* offset of next entry */
+
+	cd->buflen = buflen;
+	cd->buffer = p;
+	return 0;
+}
+
+/* FSSTAT */
+int
+nfs3svc_encode_statfsres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_statfsres *resp)
+{
+	struct statfs	*s = &resp->stats;
+	u64		bs = s->f_bsize;
+
+	*p++ = xdr_zero;	/* no post_op_attr */
+
+	if (resp->status == 0) {
+		p = enc64(p, bs * s->f_blocks);	/* total bytes */
+		p = enc64(p, bs * s->f_bfree);	/* free bytes */
+		p = enc64(p, bs * s->f_bavail);	/* user available bytes */
+		p = enc64(p, s->f_files);	/* total inodes */
+		p = enc64(p, s->f_ffree);	/* free inodes */
+		p = enc64(p, s->f_ffree);	/* user available inodes */
+		*p++ = htonl(resp->invarsec);	/* mean unchanged time */
+	}
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* FSINFO */
+int
+nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_fsinfores *resp)
+{
+	*p++ = xdr_zero;	/* no post_op_attr */
+
+	if (resp->status == 0) {
+		*p++ = htonl(resp->f_rtmax);
+		*p++ = htonl(resp->f_rtpref);
+		*p++ = htonl(resp->f_rtmult);
+		*p++ = htonl(resp->f_wtmax);
+		*p++ = htonl(resp->f_wtpref);
+		*p++ = htonl(resp->f_wtmult);
+		*p++ = htonl(resp->f_dtpref);
+		*p++ = htonl(resp->f_maxfilesize);
+		*p++ = xdr_zero;
+		*p++ = htonl(1000000000 / HZ);
+		*p++ = htonl(resp->f_properties);
+	}
+
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* PATHCONF */
+int
+nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_pathconfres *resp)
+{
+	*p++ = xdr_zero;	/* no post_op_attr */
+
+	if (resp->status == 0) {
+		*p++ = htonl(resp->p_link_max);
+		*p++ = htonl(resp->p_name_max);
+		*p++ = xdr_one;	/* always reject long file names */
+		*p++ = xdr_one;	/* chown restricted */
+		*p++ = htonl(resp->p_case_insensitive);
+		*p++ = htonl(resp->p_case_preserving);
+	}
+
+	return xdr_ressize_check(rqstp, p);
+}
+
+/* COMMIT */
+int
+nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_commitres *resp)
+{
+	p = encode_wcc_data(rqstp, p, &resp->fh);
+	/* Write verifier */
+	if (resp->status == 0) {
+		*p++ = htonl(nfssvc_boot.tv_sec);
+		*p++ = htonl(nfssvc_boot.tv_usec);
+	}
+	return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * XDR release functions
+ */
+int
+nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_fhandle *resp)
+{
+	fh_put(&resp->fh);
+	return 1;
+}
+
+int
+nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd3_fhandle2 *resp)
+{
+	fh_put(&resp->fh1);
+	fh_put(&resp->fh2);
+	return 1;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfscache.c linux/fs/nfsd/nfscache.c
--- v2.1.31/linux/fs/nfsd/nfscache.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfscache.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,321 @@
+/*
+ * linux/fs/nfsd/nfscache.c
+ *
+ * Request reply cache. This is currently a global cache, but this may
+ * change in the future and be a per-client cache.
+ *
+ * This code is heavily inspired by the 44BSD implementation, although
+ * it does things a bit differently.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+
+/* Size of reply cache. Common values are:
+ * 4.3BSD:	128
+ * 4.4BSD:	256
+ * Solaris2:	1024
+ * DEC Unix:	512-4096
+ */
+#define CACHESIZE		1024
+#define HASHSIZE		64
+#define REQHASH(xid)		((((xid) >> 24) ^ (xid)) & (HASHSIZE-1))
+
+struct nfscache_head {
+	struct svc_cacherep *	next;
+	struct svc_cacherep *	prev;
+};
+
+static struct nfscache_head	hash_list[HASHSIZE];
+static struct svc_cacherep *	lru_head;
+static struct svc_cacherep *	lru_tail;
+static struct svc_cacherep	nfscache[CACHESIZE];
+static int			cache_initialized = 0;
+static int			cache_disabled = 1;
+
+static int	nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data);
+
+void
+nfsd_cache_init(void)
+{
+	struct svc_cacherep	*rp;
+	struct nfscache_head	*rh;
+	int			i;
+
+	if (cache_initialized)
+		return;
+
+	for (i = 0, rh = hash_list; i < HASHSIZE; i++, rh++)
+		rh->next = rh->prev = (struct svc_cacherep *) rh;
+
+	for (i = 0, rp = nfscache; i < CACHESIZE; i++, rp++) {
+		rp->c_state = RC_UNUSED;
+		rp->c_type = RC_NOCACHE;
+		rp->c_hash_next =
+		rp->c_hash_prev = rp;
+		rp->c_lru_next = rp + 1;
+		rp->c_lru_prev = rp - 1;
+	}
+	lru_head = nfscache;
+	lru_tail = nfscache + CACHESIZE - 1;
+	lru_head->c_lru_prev = NULL;
+	lru_tail->c_lru_next = NULL;
+
+	cache_initialized = 1;
+	cache_disabled = 0;
+}
+
+void
+nfsd_cache_shutdown(void)
+{
+	struct svc_cacherep	*rp;
+
+	if (!cache_initialized)
+		return;
+
+	for (rp = lru_head; rp; rp = rp->c_lru_next) {
+		if (rp->c_state == RC_DONE && rp->c_type == RC_REPLBUFF)
+			kfree(rp->c_replbuf.buf);
+	}
+
+	cache_initialized = 0;
+	cache_disabled = 1;
+}
+
+/*
+ * Move cache entry to front of LRU list
+ */
+static void
+lru_put_front(struct svc_cacherep *rp)
+{
+	struct svc_cacherep	*prev = rp->c_lru_prev,
+				*next = rp->c_lru_next;
+
+	if (prev)
+		prev->c_lru_next = next;
+	else
+		lru_head = next;
+	if (next)
+		next->c_lru_prev = prev;
+	else
+		lru_tail = prev;
+
+	rp->c_lru_next = lru_head;
+	rp->c_lru_prev = NULL;
+	if (lru_head)
+		lru_head->c_lru_prev = rp;
+	lru_head = rp;
+}
+
+/*
+ * Move a cache entry from one hash list to another
+ */
+static void
+hash_refile(struct svc_cacherep *rp)
+{
+	struct svc_cacherep	*prev = rp->c_hash_prev,
+				*next = rp->c_hash_next;
+	struct nfscache_head	*head = hash_list + REQHASH(rp->c_xid);
+
+	prev->c_hash_next = next;
+	next->c_hash_prev = prev;
+
+	rp->c_hash_next = head->next;
+	rp->c_hash_prev = (struct svc_cacherep *) head;
+	head->next->c_hash_prev = rp;
+	head->next = rp;
+}
+
+/*
+ * Try to find an entry matching the current call in the cache. When none
+ * is found, we grab the oldest unlocked entry off the LRU list.
+ * Note that no operation within the loop may sleep.
+ */
+int
+nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
+{
+	struct svc_cacherep	*rh, *rp;
+	struct svc_client	*clp = rqstp->rq_client;
+	u32			xid = rqstp->rq_xid,
+				proc = rqstp->rq_proc;
+	unsigned long		age;
+
+	rqstp->rq_cacherep = NULL;
+	if (cache_disabled || type == RC_NOCACHE) {
+		nfsdstats.rcnocache++;
+		return RC_DOIT;
+	}
+
+	rp = rh = (struct svc_cacherep *) &hash_list[REQHASH(xid)];
+	while ((rp = rp->c_hash_next) != rh) {
+		if (rp->c_state != RC_UNUSED &&
+		    xid == rp->c_xid && proc == rp->c_proc &&
+		    exp_checkaddr(clp, rp->c_client)) {
+			nfsdstats.rchits++;
+			goto found_entry;
+		}
+	}
+	nfsdstats.rcmisses++;
+
+	/* This loop shouldn't take more than a few iterations normally */
+	{
+	int	safe = 0;
+	for (rp = lru_tail; rp; rp = rp->c_lru_prev) {
+		if (rp->c_state != RC_INPROG)
+			break;
+		if (safe++ > CACHESIZE) {
+			printk("nfsd: loop in repcache LRU list\n");
+			cache_disabled = 1;
+			return RC_DOIT;
+		}
+	}
+	}
+
+	/* This should not happen */
+	if (rp == NULL) {
+		static int	complaints = 0;
+
+		printk(KERN_WARNING "nfsd: all repcache entries locked!\n");
+		if (++complaints > 5) {
+			printk(KERN_WARNING "nfsd: disabling repcache.\n");
+			cache_disabled = 1;
+		}
+		return RC_DOIT;
+	}
+
+	rqstp->rq_cacherep = rp;
+	rp->c_state = RC_INPROG;
+	rp->c_xid = xid;
+	rp->c_proc = proc;
+	rp->c_client = rqstp->rq_addr.sin_addr;
+	hash_refile(rp);
+
+	/* release any buffer */
+	if (rp->c_type == RC_REPLBUFF) {
+		kfree(rp->c_replbuf.buf);
+		rp->c_replbuf.buf = NULL;
+	}
+	rp->c_type = RC_NOCACHE;
+
+	return RC_DOIT;
+
+found_entry:
+	/* We found a matching entry which is either in progress or done. */
+	age = jiffies - rp->c_timestamp;
+	rp->c_timestamp = jiffies;
+	lru_put_front(rp);
+
+	/* Request being processed or excessive rexmits */
+	if (rp->c_state == RC_INPROG || age < RC_DELAY)
+		return RC_DROPIT;
+
+	/* From the hall of fame of impractical attacks:
+	 * Is this a user who tries to snoop on the cache? */
+	if (!rqstp->rq_secure && rp->c_secure)
+		return RC_DOIT;
+
+	/* Compose RPC reply header */
+	switch (rp->c_type) {
+	case RC_NOCACHE:
+		return RC_DOIT;
+	case RC_REPLSTAT:
+		svc_putlong(&rqstp->rq_resbuf, rp->c_replstat);
+		break;
+	case RC_REPLBUFF:
+		if (!nfsd_cache_append(rqstp, &rp->c_replbuf))
+			return RC_DOIT;	/* should not happen */
+		break;
+	default:
+		printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type);
+		rp->c_state = RC_UNUSED;
+		return RC_DOIT;
+	}
+
+	return RC_REPLY;
+}
+
+/*
+ * Update a cache entry. This is called from nfsd_dispatch when
+ * the procedure has been executed and the complete reply is in
+ * rqstp->rq_res.
+ *
+ * We're copying around data here rather than swapping buffers because
+ * the toplevel loop requires max-sized buffers, which would be a waste
+ * of memory for a cache with a max reply size of 100 bytes (diropokres).
+ *
+ * If we should start to use different types of cache entries tailored
+ * specifically for attrstat and fh's, we may save even more space.
+ *
+ * Also note that a cachetype of RC_NOCACHE can legally be passed when
+ * nfsd failed to encode a reply that otherwise would have been cached.
+ * In this case, nfsd_cache_update is called with statp == NULL.
+ */
+void
+nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, u32 *statp)
+{
+	struct svc_cacherep *rp;
+	struct svc_buf	*resp = &rqstp->rq_resbuf, *cachp;
+	int		len;
+
+	if (!(rp = rqstp->rq_cacherep) || cache_disabled)
+		return;
+
+	/* Don't cache excessive amounts of data and XDR failures */
+	if (!statp || (len = resp->buf - statp) > (256 >> 2)) {
+		rp->c_state = RC_UNUSED;
+		return;
+	}
+
+	switch (cachetype) {
+	case RC_REPLSTAT:
+		if (len != 1)
+			printk("nfsd: RC_REPLSTAT/reply len %d!\n",len);
+		rp->c_replstat = *statp;
+		break;
+	case RC_REPLBUFF:
+		cachp = &rp->c_replbuf;
+		cachp->buf = (u32 *) kmalloc(len << 2, GFP_KERNEL);
+		if (!cachp->buf) {
+			rp->c_state = RC_UNUSED;
+			return;
+		}
+		cachp->len = len;
+		memcpy(cachp->buf, statp, len << 2);
+		break;
+	}
+
+	lru_put_front(rp);
+	rp->c_secure = rqstp->rq_secure;
+	rp->c_type = cachetype;
+	rp->c_state = RC_DONE;
+	rp->c_timestamp = jiffies;
+
+	return;
+}
+
+/*
+ * Copy cached reply to current reply buffer. Should always fit.
+ */
+static int
+nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data)
+{
+	struct svc_buf	*resp = &rqstp->rq_resbuf;
+
+	if (resp->len + data->len > resp->buflen) {
+		printk(KERN_WARNING "nfsd: cached reply too large (%d).\n",
+				data->len);
+		return 0;
+	}
+	memcpy(resp->buf, data->buf, data->len);
+	resp->buf += ((data->len + 3) >> 2);
+	resp->len += data->len;
+	return 1;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsctl.c linux/fs/nfsd/nfsctl.c
--- v2.1.31/linux/fs/nfsd/nfsctl.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfsctl.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,238 @@
+/*
+ * linux/fs/nfsd/nfsctl.c
+ *
+ * Syscall interface to knfsd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#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/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/nfsd/syscall.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#else
+# define copy_from_user		memcpy_fromfs
+# define copy_to_user		memcpy_tofs
+# define access_ok		!verify_area
+#endif
+
+extern long sys_call_table[];
+
+static int	nfsctl_svc(struct nfsctl_svc *data);
+static int	nfsctl_addclient(struct nfsctl_client *data);
+static int	nfsctl_delclient(struct nfsctl_client *data);
+static int	nfsctl_export(struct nfsctl_export *data);
+static int	nfsctl_unexport(struct nfsctl_export *data);
+static int	nfsctl_getfh(struct nfsctl_fhparm *, struct knfs_fh *);
+/* static int	nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */
+
+static int	initialized = 0;
+
+/*
+ * Initialize nfsd
+ */
+static void
+nfsd_init(void)
+{
+	nfsd_xdr_init();	/* XDR */
+#ifdef CONFIG_PROC_FS
+	nfsd_stat_init();	/* Statistics */
+#endif
+	nfsd_cache_init();	/* RPC reply cache */
+	nfsd_export_init();	/* Exports table */
+	nfsd_lockd_init();	/* lockd->nfsd callbacks */
+	nfsd_racache_init();	/* Readahead param cache */
+	initialized = 1;
+}
+
+static inline int
+nfsctl_svc(struct nfsctl_svc *data)
+{
+	return nfsd_svc(data->svc_port, data->svc_nthreads);
+}
+
+static inline int
+nfsctl_addclient(struct nfsctl_client *data)
+{
+	return exp_addclient(data);
+}
+
+static inline int
+nfsctl_delclient(struct nfsctl_client *data)
+{
+	return exp_delclient(data);
+}
+
+static inline int
+nfsctl_export(struct nfsctl_export *data)
+{
+	return exp_export(data);
+}
+
+static inline int
+nfsctl_unexport(struct nfsctl_export *data)
+{
+	return exp_unexport(data);
+}
+
+#ifdef notyet
+static inline int
+nfsctl_ugidupdate(nfs_ugidmap *data)
+{
+	return -EINVAL;
+}
+#endif
+
+static inline int
+nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res)
+{
+	struct sockaddr_in	*sin;
+	struct svc_client	*clp;
+	int			err = 0;
+
+	if (data->gf_addr.sa_family != AF_INET)
+		return -EPROTONOSUPPORT;
+	if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS)
+		return -EINVAL;
+	sin = (struct sockaddr_in *)&data->gf_addr;
+
+	exp_readlock();
+	if (!(clp = exp_getclient(sin)))
+		err = -EPERM;
+	else
+		err = exp_rootfh(clp, data->gf_dev, data->gf_ino, res);
+	exp_unlock();
+
+	return err;
+}
+
+int
+asmlinkage sys_nfsservctl(int cmd, struct nfsctl_arg *argp, union nfsctl_res *resp)
+{
+	struct nfsctl_arg *	arg = NULL;
+	union nfsctl_res *	res = NULL;
+	int			err;
+
+	if (!initialized)
+		nfsd_init();
+	if (!suser())
+		return -EPERM;
+	if (!access_ok(VERIFY_READ, argp, sizeof(*argp))
+	 || (resp && !access_ok(VERIFY_WRITE, resp, sizeof(*resp))))
+		return -EFAULT;
+	if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
+	    (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
+		err = -ENOMEM;	/* ??? */
+		goto done;
+	}
+	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);
+		break;
+	case NFSCTL_ADDCLIENT:
+		err = nfsctl_addclient(&arg->ca_client);
+		break;
+	case NFSCTL_DELCLIENT:
+		err = nfsctl_delclient(&arg->ca_client);
+		break;
+	case NFSCTL_EXPORT:
+		err = nfsctl_export(&arg->ca_export);
+		break;
+	case NFSCTL_UNEXPORT:
+		err = nfsctl_unexport(&arg->ca_export);
+		break;
+#ifdef notyet
+	case NFSCTL_UGIDUPDATE:
+		err = nfsctl_ugidupdate(&arg->ca_umap);
+		break;
+#endif
+	case NFSCTL_GETFH:
+		err = nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh);
+		break;
+	default:
+		err = -EINVAL;
+	}
+	MOD_DEC_USE_COUNT;
+
+	if (!err && resp)
+		copy_to_user(resp, res, sizeof(*resp));
+
+done:
+	if (arg)
+		kfree(arg);
+	if (res)
+		kfree(res);
+
+	return err;
+}
+
+#ifdef MODULE
+/* New-style module support since 2.1.18 */
+#if LINUX_VERSION_CODE >= 131346
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+#endif
+
+static unsigned long	old_syscallvec;
+
+/*
+ * Initialize the module
+ */
+int
+init_module(void)
+{
+	printk("Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
+
+	old_syscallvec = sys_call_table[__NR_nfsservctl];
+	sys_call_table[__NR_nfsservctl] = (unsigned long) sys_nfsservctl;
+	nfsd_init();
+	return 0;
+}
+
+/*
+ * Clean up the mess before unloading the module
+ */
+void
+cleanup_module(void)
+{
+	if (MOD_IN_USE) {
+		printk("nfsd: nfsd busy, remove delayed\n");
+		return;
+	}
+	sys_call_table[__NR_nfsservctl] = old_syscallvec;
+	nfsd_export_shutdown();
+	nfsd_cache_shutdown();
+#ifdef CONFIG_PROC_FS
+	nfsd_stat_shutdown();
+#endif
+	nfsd_lockd_shutdown();
+}
+#endif
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.1.31/linux/fs/nfsd/nfsfh.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfsfh.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,124 @@
+/*
+ * linux/fs/nfsd/nfsfh.c
+ *
+ * NFS server filehandle treatment.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+
+#define NFSDDBG_FACILITY		NFSDDBG_FH
+
+/*
+ * Get the inode version number
+ */
+static inline int
+nfsd_iversion(struct inode *inode)
+{
+	if (inode->i_sb->s_magic == EXT2_SUPER_MAGIC)
+		return inode->u.ext2_i.i_version;
+	return 0;
+}
+
+/*
+ * Get the inode given a file handle.
+ */
+u32
+fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+{
+	struct svc_export *exp;
+	struct inode	*inode;
+	struct knfs_fh	*fh = &fhp->fh_handle;
+
+	/* Already checked */
+	if (fhp->fh_inode)
+		return 0;
+
+	dprintk("nfsd: fh_lookup(exp %x/%ld fh %x/%ld)\n",
+			fh->fh_xdev, fh->fh_xino, fh->fh_dev, fh->fh_ino);
+
+	/* Make sure that clients don't cheat */
+	if (fh->fh_dev != fh->fh_xdev) {
+		printk(KERN_NOTICE "nfsd: fh with bad dev fields "
+				"(%x != %x) from %08lx:%d\n",
+				fh->fh_dev, fh->fh_xdev,
+				ntohl(rqstp->rq_addr.sin_addr.s_addr),
+				ntohs(rqstp->rq_addr.sin_port));
+		return nfserr_perm;
+	}
+
+	/* Look up the export entry */
+	exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
+	if (!exp) {
+		/* nfsdstats.fhstale++; */
+		return nfserr_stale; /* export entry revoked */
+	}
+
+	/* Check if the request originated from a secure port. */
+	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;
+	}
+
+	/* Set user creds if we haven't done so already */
+	nfsd_setuser(rqstp, exp);
+
+	/* Get the inode */
+	if (!(inode = nfsd_iget(fh->fh_dev, fh->fh_ino))
+	 || !inode->i_nlink || fh->fh_version != nfsd_iversion(inode)) {
+		if (inode)
+			iput(inode);
+		/* nfsdstats.fhstale++; */
+		return nfserr_stale;	/* unlinked in the meanwhile */
+	}
+
+	/* This is basically what wait_on_inode does */
+	while (inode->i_lock)
+		sleep_on(&inode->i_wait);
+	fhp->fh_inode  = inode;
+	fhp->fh_export = exp;
+
+	/* Type check. The correct error return for type mismatches
+	 * does not seem to be generally agreed upon. SunOS seems to
+	 * use EISDIR if file isn't S_IFREG; a comment in the NFSv3
+	 * 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;
+
+	/* Finally, check access permissions */
+	return nfsd_permission(fhp->fh_export, inode, access);
+}
+
+/*
+ * Compose file handle for NFS reply.
+ */
+void
+fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct inode *inode)
+{
+	dprintk("nfsd: fh_compose(exp %x/%ld fh %x/%ld)\n",
+			exp->ex_dev, exp->ex_ino, inode->i_dev, inode->i_ino);
+
+	fh_init(fhp);			/* initialize empty fh */
+	fhp->fh_inode = inode;
+	fhp->fh_export = exp;
+	fhp->fh_handle.fh_dev = inode->i_dev;
+	fhp->fh_handle.fh_ino = inode->i_ino;
+	fhp->fh_handle.fh_xdev = exp->ex_dev;
+	fhp->fh_handle.fh_xino = exp->ex_ino;
+	fhp->fh_handle.fh_version = nfsd_iversion(inode);
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsproc.c linux/fs/nfsd/nfsproc.c
--- v2.1.31/linux/fs/nfsd/nfsproc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfsproc.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,581 @@
+/*
+ * nfsproc2.c	Process version 2 NFS requests.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+
+typedef struct svc_rqst	svc_rqst;
+typedef struct svc_buf	svc_buf;
+
+#define NFSDDBG_FACILITY		NFSDDBG_PROC
+
+#define sleep(msec)		\
+	{	printk(KERN_NOTICE "nfsd: sleeping %d msecs\n", msec); \
+		current->state = TASK_INTERRUPTIBLE;	\
+		current->timeout = jiffies + msec / 10;	\
+		schedule();	\
+	}
+#define RETURN(st)	return st
+
+static void
+svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
+{
+	*ptr = buf->buf + nr;
+	*len = buf->buflen - buf->len - nr;
+}
+
+static int
+nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+	RETURN(nfs_ok);
+}
+
+/*
+ * Get a file's attributes
+ */
+static int
+nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
+					  struct nfsd_attrstat *resp)
+{
+	dprintk("nfsd: GETATTR  %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	fh_copy(&resp->fh, &argp->fh);
+	RETURN(fh_lookup(rqstp, &resp->fh, 0, MAY_NOP));
+}
+
+/*
+ * Set a file's attributes
+ */
+static int
+nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
+					  struct nfsd_attrstat  *resp)
+{
+	dprintk("nfsd: SETATTR  %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	fh_copy(&resp->fh, &argp->fh);
+	RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
+}
+
+/*
+ * Look up a path name component
+ */
+static int
+nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
+					 struct nfsd_diropres  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: LOOKUP   %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	nfserr = nfsd_lookup(rqstp, &argp->fh,
+				    argp->name,
+				    argp->len,
+				    &resp->fh);
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Read a symlink.
+ */
+static int
+nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle     *argp,
+					   struct nfsd_readlinkres *resp)
+{
+	u32		*path;
+	int		dummy, nfserr;
+
+	dprintk("nfsd: READLINK %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	/* Reserve room for status and path length */
+	svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2);
+
+	/* Read the symlink. */
+	resp->len = NFS_MAXPATHLEN;
+	nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Read a portion of a file.
+ */
+static int
+nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
+				       struct nfsd_readres  *resp)
+{
+	u32 *	buffer;
+	int	nfserr, avail;
+
+	dprintk("nfsd: READ %x/%ld %d bytes at %d\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->count, argp->offset);
+
+	/* Obtain buffer pointer for payload. 19 is 1 word for
+	 * status, 17 words for fattr, and 1 word for the byte count.
+	 */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 19);
+
+	if ((avail << 2) < argp->count) {
+		printk(KERN_NOTICE
+			"oversized read request from %08lx:%d (%d bytes)\n",
+				ntohl(rqstp->rq_addr.sin_addr.s_addr),
+				ntohs(rqstp->rq_addr.sin_port),
+				argp->count);
+		argp->count = avail;
+	}
+
+	resp->count = argp->count;
+	nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
+				  argp->offset,
+				  (char *) buffer,
+				  &resp->count);
+
+	RETURN(nfserr);
+}
+
+/*
+ * Write data to a file
+ */
+static int
+nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
+					struct nfsd_attrstat  *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: WRITE    %x/%ld %d bytes at %d\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->len, argp->offset);
+
+	nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
+				   argp->offset,
+				   argp->data,
+				   argp->len,
+				   0);
+	RETURN(nfserr);
+}
+
+/*
+ * CREATE processing is complicated. The keyword here is `overloaded.'
+ * There's a small race condition here between the check for existence
+ * and the actual create() call, but one could even consider this a
+ * feature because this only happens if someone else creates the file
+ * at the same time.
+ */
+static int
+nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
+					 struct nfsd_diropres   *resp)
+{
+	struct inode	*dirp, *inode = NULL;
+	struct iattr	*attr;
+	svc_fh		*dirfhp, *newfhp = NULL;
+	int		nfserr, type, mode;
+	int		rdonly = 0, exists;
+	dev_t		rdev = NODEV;
+
+	dprintk("nfsd: CREATE   %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	dirfhp = &argp->fh;
+	newfhp = &resp->fh;
+	attr = &argp->attrs;
+
+	/* Get the directory inode */
+	nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
+	if (nfserr)
+		RETURN(nfserr);
+	dirp = dirfhp->fh_inode;
+
+	/* Check for MAY_WRITE separately. */
+	nfserr = nfsd_permission(dirfhp->fh_export, dirp, MAY_WRITE);
+	if (nfserr == nfserr_rofs) {
+		rdonly = 1;	/* Non-fatal error for echo > /dev/null */
+	} else if (nfserr) {
+		fh_put(dirfhp);
+		RETURN(nfserr);
+	}
+
+	/* First, check if the file already exists.  */
+	exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
+	inode = newfhp->fh_inode;
+
+	/* Unfudge the mode bits */
+	if (attr->ia_valid & ATTR_MODE) { 
+		type = attr->ia_mode & S_IFMT;
+		mode = attr->ia_mode & ~S_IFMT;
+		if (!type)	/* HP weirdness */
+			type = S_IFREG;
+	} else if (exists) {
+		type = inode->i_mode & S_IFMT;
+		mode = inode->i_mode & ~S_IFMT;
+	} else {
+		type = S_IFREG;
+		mode = 0;	/* ??? */
+	}
+
+	/* This is for "echo > /dev/null" a la SunOS. Argh. */
+	if (rdonly && (!exists || type == S_IFREG)) {
+		nfserr = nfserr_rofs;
+		goto done;
+	}
+
+	attr->ia_valid |= ATTR_MODE;
+	attr->ia_mode = type | mode;
+
+	/* Special treatment for non-regular files according to the
+	 * gospel of sun micro
+	 */
+	nfserr = 0;
+	if (type != S_IFREG) {
+		int	is_borc = 0;
+		u32	size = attr->ia_size;
+
+		rdev = (dev_t) size;
+		if (type != S_IFBLK && type != S_IFCHR) {
+			rdev = 0;
+		} else if (type == S_IFCHR && size == ~(u32) 0) {
+			/* If you think you've seen the worst, grok this. */
+			attr->ia_mode = S_IFIFO | mode;
+			type = S_IFIFO;
+		} else if (size != rdev) {
+			/* dev got truncated because of 16bit Linux dev_t */
+			nfserr = nfserr_io;	/* or nfserr_inval? */
+			goto done;
+		} else {
+			/* Okay, char or block special */
+			is_borc = 1;
+		}
+
+		/* Make sure the type and device matches */
+		if (exists && (type != (inode->i_mode & S_IFMT)
+		 || (is_borc && inode->i_rdev != rdev))) {
+			nfserr = nfserr_exist;
+			goto done;
+		}
+	}
+	
+	if (!exists) {
+		/* File doesn't exist. Create it and set attrs */
+		nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
+					attr, type, rdev, newfhp);
+	} else if (type == S_IFREG) {
+		/* File already exists. We ignore all attributes except
+		 * size, so that creat() behaves exactly like
+		 * open(..., O_CREAT|O_TRUNC|O_WRONLY).
+		 */
+		if ((attr->ia_valid &= ~(ATTR_SIZE)) != 0)
+			nfserr = nfsd_setattr(rqstp, newfhp, attr);
+	}
+
+done:
+	fh_put(dirfhp);
+	RETURN(nfserr);
+}
+
+static int
+nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
+					 void		       *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: REMOVE   %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	/* Unlink. -SIFDIR means file must not be a directory */
+	nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+static int
+nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp,
+				  	 void		        *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				SVCFH_INO(&argp->ffh),
+				argp->fname,
+				SVCFH_DEV(&argp->tfh),
+				SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
+				    &argp->tfh, argp->tname, argp->tlen);
+	fh_put(&argp->ffh);
+	fh_put(&argp->tfh);
+	RETURN(nfserr);
+}
+
+static int
+nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp,
+				void			    *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
+				SVCFH_DEV(&argp->ffh),
+				SVCFH_INO(&argp->ffh),
+				SVCFH_DEV(&argp->tfh),
+				SVCFH_INO(&argp->tfh),
+				argp->tname);
+
+	nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
+				  &argp->ffh);
+	fh_put(&argp->ffh);
+	fh_put(&argp->tfh);
+	RETURN(nfserr);
+}
+
+static int
+nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
+				          void			  *resp)
+{
+	struct svc_fh	newfh;
+	int		nfserr;
+
+	dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
+				SVCFH_DEV(&argp->ffh),
+				SVCFH_INO(&argp->ffh),
+				argp->fname, argp->tname);
+
+	/*
+	 * Create the link, look up new file and set attrs.
+	 */
+	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
+						 argp->tname, argp->tlen,
+						 &newfh);
+	if (nfserr)
+		nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
+
+	fh_put(&argp->ffh);
+	fh_put(&newfh);
+	RETURN(nfserr);
+}
+
+/*
+ * Make directory. This operation is not idempotent.
+ */
+static int
+nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
+					struct nfsd_diropres   *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: MKDIR    %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
+				    &argp->attrs, S_IFDIR, 0, &resp->fh);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Remove a directory
+ */
+static int
+nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
+				 	void		      *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: RMDIR    %x/%ld %s\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->name);
+
+	nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Read a portion of a directory.
+ */
+static int
+nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
+					  struct nfsd_readdirres  *resp)
+{
+	u32 *	buffer;
+	int	nfserr, count;
+
+	dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh),
+				argp->count, argp->cookie);
+
+	/* Reserve buffer space for status */
+	svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
+
+	/* Make sure we've room for the NULL ptr & eof flag, and shrink to
+	 * client read size */
+	if ((count -= 8) > argp->count)
+		count = argp->count;
+
+	/* Read directory and encode entries on the fly */
+	nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie, 
+					nfssvc_encode_entry,
+					buffer, &count);
+	resp->count = count;
+
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * Get file system info
+ */
+static int
+nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
+					  struct nfsd_statfsres *resp)
+{
+	int	nfserr;
+
+	dprintk("nfsd: STATFS   %x/%ld\n",
+				SVCFH_DEV(&argp->fh),
+				SVCFH_INO(&argp->fh));
+
+	nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+	fh_put(&argp->fh);
+	RETURN(nfserr);
+}
+
+/*
+ * NFSv2 Server procedures.
+ * Only the results of non-idempotent operations are cached.
+ */
+#define nfsd_proc_none		NULL
+#define nfssvc_release_none	NULL
+struct nfsd_void { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache)	\
+ { (svc_procfunc) nfsd_proc_##name,	\
+   (kxdrproc_t) nfssvc_decode_##argt,	\
+   (kxdrproc_t) nfssvc_encode_##rest,	\
+   (kxdrproc_t) nfssvc_release_##relt,	\
+   sizeof(struct nfsd_##argt),		\
+   sizeof(struct nfsd_##rest),		\
+   0,					\
+   cache				\
+ }
+struct svc_procedure		nfsd_procedures2[18] = {
+  PROC(null,	 void,		void,		none,	 RC_NOCACHE),
+  PROC(getattr,	 fhandle,	attrstat,	fhandle, RC_NOCACHE),
+  PROC(setattr,  sattrargs,	attrstat,	fhandle, RC_REPLBUFF),
+  PROC(none,	 void,		void,		none,	 RC_NOCACHE),
+  PROC(lookup,	 diropargs,	diropres,	fhandle, RC_NOCACHE),
+  PROC(readlink, fhandle,	readlinkres,	none,	 RC_NOCACHE),
+  PROC(read,	 readargs,	readres,	fhandle, RC_NOCACHE),
+  PROC(none,	 void,		void,		none,	 RC_NOCACHE),
+  PROC(write,	 writeargs,	attrstat,	fhandle, RC_REPLBUFF),
+  PROC(create,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
+  PROC(remove,	 diropargs,	void,		none,	 RC_REPLSTAT),
+  PROC(rename,	 renameargs,	void,		none,	 RC_REPLSTAT),
+  PROC(link,	 linkargs,	void,		none,	 RC_REPLSTAT),
+  PROC(symlink,	 symlinkargs,	void,		none,	 RC_REPLSTAT),
+  PROC(mkdir,	 createargs,	diropres,	fhandle, RC_REPLBUFF),
+  PROC(rmdir,	 diropargs,	void,		none,	 RC_REPLSTAT),
+  PROC(readdir,	 readdirargs,	readdirres,	none,	 RC_REPLSTAT),
+  PROC(statfs,	 fhandle,	statfsres,	none,	 RC_NOCACHE),
+};
+
+
+/*
+ * Map errnos to NFS errnos.
+ */
+int
+nfserrno (int errno)
+{
+	static struct {
+		int	nfserr;
+		int	syserr;
+	} nfs_errtbl[] = {
+		{ NFS_OK, 0 },
+		{ NFSERR_PERM, EPERM },
+		{ NFSERR_NOENT, ENOENT },
+		{ NFSERR_IO, EIO },
+		{ NFSERR_NXIO, ENXIO },
+		{ NFSERR_ACCES, EACCES },
+		{ NFSERR_EXIST, EEXIST },
+		{ NFSERR_NODEV, ENODEV },
+		{ NFSERR_NOTDIR, ENOTDIR },
+		{ NFSERR_ISDIR, EISDIR },
+		{ NFSERR_INVAL, EINVAL },
+		{ NFSERR_FBIG, EFBIG },
+		{ NFSERR_NOSPC, ENOSPC },
+		{ NFSERR_ROFS, EROFS },
+		{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
+		{ NFSERR_NOTEMPTY, ENOTEMPTY },
+#ifdef EDQUOT
+		{ NFSERR_DQUOT, EDQUOT },
+#endif
+		{ NFSERR_STALE, ESTALE },
+		{ NFSERR_WFLUSH, EIO },
+		{ -1, EIO }
+	};
+	int	i;
+
+	for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
+		if (nfs_errtbl[i].syserr == errno)
+			return htonl (nfs_errtbl[i].nfserr);
+	}
+	printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
+	return nfserr_io;
+}
+
+#if 0
+static void
+nfsd_dump(char *tag, u32 *buf, int len)
+{
+	int	i;
+
+	printk(KERN_NOTICE
+		"nfsd: %s (%d words)\n", tag, len);
+
+	for (i = 0; i < len && i < 32; i += 8)
+		printk(KERN_NOTICE
+			" %08lx %08lx %08lx %08lx"
+			" %08lx %08lx %08lx %08lx\n",
+			buf[i],   buf[i+1], buf[i+2], buf[i+3],
+			buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+}
+#endif
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.1.31/linux/fs/nfsd/nfssvc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfssvc.c	Fri Apr  4 12:48:15 1997
@@ -0,0 +1,245 @@
+/*
+ * linux/fs/nfsd/nfssvc.c
+ *
+ * Central processing for nfsd.
+ *
+ * Authors:	Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/nfs.h>
+#include <linux/in.h>
+#include <linux/uio.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/stats.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/lockd/bind.h>
+
+#define NFSDDBG_FACILITY	NFSDDBG_SVC
+#define NFSD_BUFSIZE		(1024 + NFSSVC_MAXBLKSIZE)
+#define BLOCKABLE_SIGS		(~(_S(SIGKILL - 1) | _S(SIGSTOP - 1)))
+#define _S(sig)			(1 << ((sig) - 1))
+
+extern struct svc_program	nfsd_program;
+static void			nfsd(struct svc_rqst *rqstp);
+struct timeval			nfssvc_boot = { 0, 0 };
+
+/*
+ * Make a socket for nfsd
+ */
+static int
+nfsd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
+{
+	struct sockaddr_in	sin;
+
+	dprintk("nfsd: creating socket proto = %d\n", protocol);
+	sin.sin_family      = AF_INET;
+	sin.sin_addr.s_addr = INADDR_ANY;
+	sin.sin_port        = htons(port);
+	return svc_create_socket(serv, protocol, &sin);
+}
+
+int
+nfsd_svc(unsigned short port, int nrservs)
+{
+	struct svc_serv *	serv;
+	int			error;
+
+	dprintk("nfsd: creating service\n");
+	if (nrservs < 0)
+		return -EINVAL;
+	if (nrservs > NFSD_MAXSERVS)
+		nrservs = NFSD_MAXSERVS;
+
+	serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
+	if (serv == NULL) 
+		return -ENOMEM;
+
+	if ((error = nfsd_makesock(serv, IPPROTO_UDP, port)) < 0
+	 || (error = nfsd_makesock(serv, IPPROTO_TCP, port)) < 0)
+		goto failure;
+
+	while (nrservs--) {
+		error = svc_create_thread(nfsd, serv);
+		if (error < 0)
+			break;
+	}
+
+failure:
+	svc_destroy(serv);		/* Release server */
+	return error;
+}
+
+/*
+ * This is the NFS server kernel thread
+ */
+static void
+nfsd(struct svc_rqst *rqstp)
+{
+	struct svc_serv	*serv = rqstp->rq_server;
+	sigset_t	oldsigmask;
+	int		oldumask, err;
+
+	lock_kernel();
+	/* Lock module and set up kernel thread */
+	MOD_INC_USE_COUNT;
+	exit_mm(current);
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "nfsd");
+
+	oldumask = current->fs->umask;		/* Set umask to 0.  */
+	current->fs->umask = 0;
+	nfssvc_boot = xtime;			/* record boot time */
+	lockd_up();				/* start lockd */
+
+	/*
+	 * The main request loop
+	 */
+	do {
+		/*
+		 * Find a socket with data available and call its
+		 * recvfrom routine.
+		 */
+		while ((err = svc_recv(serv, rqstp)) == -EAGAIN)
+			;
+		if (err < 0)
+			break;
+
+		/* Lock the export hash tables for reading. */
+		exp_readlock();
+
+		/* Validate the client's address. This will also defeat
+		 * port probes on port 2049 by unauthorized clients.
+		 */
+		rqstp->rq_client = exp_getclient(&rqstp->rq_addr);
+		if (!rqstp->rq_client) {
+			printk(KERN_WARNING "nfsd: unauthenticated request "
+				"from (%08lx:%d)\n",
+				ntohl(rqstp->rq_addr.sin_addr.s_addr),
+				ntohs(rqstp->rq_addr.sin_port));
+			svc_drop(rqstp);
+			serv->sv_stats->rpcbadclnt++;
+		} else {
+			/* Process request with all signals blocked.  */
+			oldsigmask = current->blocked;
+			current->blocked = BLOCKABLE_SIGS;
+			svc_process(serv, rqstp);
+			current->blocked = oldsigmask;
+		}
+
+		/* Unlock export hash tables */
+		exp_unlock();
+	} while (err >= 0);
+
+	if (err != -EINTR)
+		printk(KERN_WARNING "nfsd: terminating on error %d\n", -err);
+
+	/* Release lockd */
+	lockd_down();
+
+	/* Destroy the thread */
+	svc_exit_thread(rqstp);
+	current->fs->umask = oldumask;
+
+	/* Release module */
+	MOD_DEC_USE_COUNT;
+}
+
+static int
+nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp)
+{
+	struct svc_procedure	*proc;
+	kxdrproc_t		xdr;
+	u32			nfserr;
+
+	dprintk("nfsd_dispatch: proc %d\n", rqstp->rq_proc);
+	proc = rqstp->rq_procinfo;
+
+	/* Check whether we have this call in the cache. */
+	switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) {
+	case RC_INTR:
+	case RC_DROPIT:
+		return 0;
+	case RC_REPLY:
+		return 1;
+	case RC_DOIT:
+		/* do it */
+	}
+
+	/* Decode arguments */
+	xdr = proc->pc_decode;
+	if (xdr && !xdr(rqstp, rqstp->rq_argbuf.buf, rqstp->rq_argp)) {
+		dprintk("nfsd: failed to decode arguments!\n");
+		nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+		*statp = rpc_garbage_args;
+		return 1;
+	}
+
+	/* Now call the procedure handler, and encode NFS status. */
+	nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
+	if (rqstp->rq_proc != 0)
+		svc_putlong(&rqstp->rq_resbuf, nfserr);
+
+	/* Encode result.
+	 * FIXME: Most NFSv3 calls return wcc data even when the call failed
+	 */
+	xdr = proc->pc_encode;
+	if (!nfserr && xdr
+	 && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
+		/* Failed to encode result. Release cache entry */
+		dprintk("nfsd: failed to encode result!\n");
+		nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+		*statp = rpc_system_err;
+		return 1;
+	}
+
+	/* Store reply in cache. */
+	nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
+	return 1;
+}
+
+static struct svc_version	nfsd_version2 = {
+	2, 18, nfsd_procedures2, nfsd_dispatch
+};
+#ifdef CONFIG_NFSD_NFS3
+static struct svc_version	nfsd_version3 = {
+	3, 23, nfsd_procedures3, nfsd_dispatch
+};
+#endif
+static struct svc_version *	nfsd_version[] = {
+	NULL,
+	NULL,
+	&nfsd_version2,
+#ifdef CONFIG_NFSD_NFS3
+	&nfsd_version3,
+#endif
+};
+
+#define NFSD_NRVERS		(sizeof(nfsd_version)/sizeof(nfsd_version[0]))
+struct svc_program		nfsd_program = {
+	NFS_PROGRAM,		/* program number */
+	2, NFSD_NRVERS-1,	/* version range */
+	NFSD_NRVERS,		/* nr of entries in nfsd_version */
+	nfsd_version,		/* version table */
+	"nfsd",			/* program name */
+	&nfsd_svcstats,		/* version table */
+};
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/nfsxdr.c linux/fs/nfsd/nfsxdr.c
--- v2.1.31/linux/fs/nfsd/nfsxdr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/nfsxdr.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,470 @@
+/*
+ * linux/fs/nfsd/xdr.c
+ *
+ * XDR support for nfsd
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/xdr.h>
+
+#define NFSDDBG_FACILITY		NFSDDBG_XDR
+
+u32	nfs_ok, nfserr_perm, nfserr_noent, nfserr_io, nfserr_nxio,
+	nfserr_acces, nfserr_exist, nfserr_nodev, nfserr_notdir,
+	nfserr_isdir, nfserr_fbig, nfserr_nospc, nfserr_rofs,
+	nfserr_nametoolong, nfserr_dquot, nfserr_stale;
+
+#ifdef NFSD_OPTIMIZE_SPACE
+# define inline
+#endif
+
+/*
+ * Mapping of S_IF* types to NFS file types
+ */
+static u32	nfs_ftypes[] = {
+	NFNON,  NFFIFO, NFCHR, NFBAD,
+	NFDIR,  NFBAD,  NFBLK, NFBAD,
+	NFREG,  NFBAD,  NFLNK, NFBAD,
+	NFSOCK, NFBAD,  NFLNK, NFBAD,
+};
+
+/*
+ * Initialization of NFS status variables
+ */
+void
+nfsd_xdr_init(void)
+{
+	static int	inited = 0;
+
+	if (inited)
+		return;
+
+	nfs_ok = htonl(NFS_OK);
+	nfserr_perm = htonl(NFSERR_PERM);
+	nfserr_noent = htonl(NFSERR_NOENT);
+	nfserr_io = htonl(NFSERR_IO);
+	nfserr_nxio = htonl(NFSERR_NXIO);
+	nfserr_acces = htonl(NFSERR_ACCES);
+	nfserr_exist = htonl(NFSERR_EXIST);
+	nfserr_nodev = htonl(NFSERR_NODEV);
+	nfserr_notdir = htonl(NFSERR_NOTDIR);
+	nfserr_isdir = htonl(NFSERR_ISDIR);
+	nfserr_fbig = htonl(NFSERR_FBIG);
+	nfserr_nospc = htonl(NFSERR_NOSPC);
+	nfserr_rofs = htonl(NFSERR_ROFS);
+	nfserr_nametoolong = htonl(NFSERR_NAMETOOLONG);
+	nfserr_dquot = htonl(NFSERR_DQUOT);
+	nfserr_stale = htonl(NFSERR_STALE);
+
+	inited = 1;
+}
+
+/*
+ * XDR functions for basic NFS types
+ */
+static inline u32 *
+decode_fh(u32 *p, struct svc_fh *fhp)
+{
+	fh_init(fhp);
+	memcpy(&fhp->fh_handle, p, sizeof(struct knfs_fh));
+
+	/* FIXME: Look up export pointer here and verify
+	 * Sun Secure RPC if requested */
+	return p + (sizeof(struct knfs_fh) >> 2);
+}
+
+static inline u32 *
+encode_fh(u32 *p, struct svc_fh *fhp)
+{
+	memcpy(p, &fhp->fh_handle, sizeof(struct knfs_fh));
+	return p + (sizeof(struct knfs_fh) >> 2);
+}
+
+/*
+ * Decode a file name and make sure that the path contains
+ * no slashes or null bytes.
+ */
+static inline u32 *
+decode_filename(u32 *p, char **namp, int *lenp)
+{
+	char		*name;
+	int		i;
+
+	if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) {
+		for (i = 0, name = *namp; i < *lenp; i++, name++) {
+			if (*name == '\0' || *name == '/')
+				return NULL;
+		}
+		*name = '\0';
+	}
+
+	return p;
+}
+
+static inline u32 *
+decode_pathname(u32 *p, char **namp, int *lenp)
+{
+	char		*name;
+	int		i;
+
+	if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
+		for (i = 0, name = *namp; i < *lenp; i++, name++) {
+			if (*name == '\0')
+				return NULL;
+		}
+		*name = '\0';
+	}
+
+	return p;
+}
+
+static inline u32 *
+decode_sattr(u32 *p, struct iattr *iap)
+{
+	u32	tmp, tmp1;
+
+	iap->ia_valid = 0;
+
+	/* Sun client bug compatibility check: some sun clients seem to
+	 * put 0xffff in the mode field when they mean 0xffffffff.
+	 * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah.
+	 */
+	if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) {
+		iap->ia_valid |= ATTR_MODE;
+		iap->ia_mode = tmp;
+	}
+	if ((tmp = ntohl(*p++)) != (u32)-1) {
+		iap->ia_valid |= ATTR_UID;
+		iap->ia_uid = tmp;
+	}
+	if ((tmp = ntohl(*p++)) != (u32)-1) {
+		iap->ia_valid |= ATTR_GID;
+		iap->ia_gid = tmp;
+	}
+	if ((tmp = ntohl(*p++)) != (u32)-1) {
+		iap->ia_valid |= ATTR_SIZE;
+		iap->ia_size = tmp;
+	}
+	tmp  = ntohl(*p++); tmp1 = ntohl(*p++);
+	if (tmp != (u32)-1 && tmp1 != (u32)-1) {
+		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
+		iap->ia_atime = tmp;
+	}
+	tmp  = ntohl(*p++); tmp1 = ntohl(*p++);
+	if (tmp != (u32)-1 && tmp1 != (u32)-1) {
+		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
+		iap->ia_mtime = tmp;
+	}
+	return p;
+}
+
+static inline u32 *
+encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode)
+{
+	if (!inode)
+		return 0;
+	*p++ = htonl(nfs_ftypes[(inode->i_mode & S_IFMT) >> 12]);
+	*p++ = htonl((u32) inode->i_mode);
+	*p++ = htonl((u32) inode->i_nlink);
+	*p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
+	*p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
+	if (S_ISLNK(inode->i_mode) && inode->i_size > NFS_MAXPATHLEN) {
+		*p++ = htonl(NFS_MAXPATHLEN);
+	} else {
+		*p++ = htonl((u32) inode->i_size);
+	}
+	*p++ = htonl((u32) inode->i_blksize);
+	*p++ = htonl((u32) inode->i_rdev);
+	*p++ = htonl((u32) inode->i_blocks);
+	*p++ = htonl((u32) inode->i_dev);
+	*p++ = htonl((u32) inode->i_ino);
+	*p++ = htonl((u32) inode->i_atime);
+	*p++ = 0;
+	*p++ = htonl((u32) inode->i_mtime);
+	*p++ = 0;
+	*p++ = htonl((u32) inode->i_ctime);
+	*p++ = 0;
+
+	return p;
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_argbuf;
+
+	return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+	struct svc_buf	*buf = &rqstp->rq_resbuf;
+
+	buf->len = p - buf->base;
+	dprintk("nfsd: ressize_check p %p base %p len %d\n",
+			p, buf->base, buf->buflen);
+	return (buf->len <= buf->buflen);
+}
+
+/*
+ * XDR decode functions
+ */
+int
+nfssvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+	if (!(p = decode_fh(p, fhp)))
+		return 0;
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_sattrargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_sattr(p, &args->attrs)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_diropargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_filename(p, &args->name, &args->len)))
+		return 0;
+
+	 return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_readargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh)))
+		return 0;
+
+	args->offset    = ntohl(*p++);
+	args->count     = ntohl(*p++);
+	args->totalsize = ntohl(*p++);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_writeargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh)))
+		return 0;
+
+	p++;				/* beginoffset */
+	args->offset = ntohl(*p++);	/* offset */
+	p++;				/* totalcount */
+	args->len = ntohl(*p++);
+	args->data = (char *) p;
+	p += XDR_QUADLEN(args->len);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_createargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh))
+	 || !(p = decode_filename(p, &args->name, &args->len))
+	 || !(p = decode_sattr(p, &args->attrs)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_renameargs *args)
+{
+	if (!(p = decode_fh(p, &args->ffh))
+	 || !(p = decode_filename(p, &args->fname, &args->flen))
+	 || !(p = decode_fh(p, &args->tfh))
+	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_linkargs *args)
+{
+	if (!(p = decode_fh(p, &args->ffh))
+	 || !(p = decode_fh(p, &args->tfh))
+	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_symlinkargs *args)
+{
+	if (!(p = decode_fh(p, &args->ffh))
+	 || !(p = decode_filename(p, &args->fname, &args->flen))
+	 || !(p = decode_pathname(p, &args->tname, &args->tlen))
+	 || !(p = decode_sattr(p, &args->attrs)))
+		return 0;
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_readdirargs *args)
+{
+	if (!(p = decode_fh(p, &args->fh)))
+		return 0;
+	args->cookie = ntohl(*p++);
+	args->count  = ntohl(*p++);
+
+	return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+int
+nfssvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_attrstat *resp)
+{
+	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+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_inode)))
+		return 0;
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_readlinkres *resp)
+{
+	*p++ = htonl(resp->len);
+	p += XDR_QUADLEN(resp->len);
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_readres *resp)
+{
+	if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+		return 0;
+	*p++ = htonl(resp->count);
+	p += XDR_QUADLEN(resp->count);
+
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_readdirres *resp)
+{
+	p += XDR_QUADLEN(resp->count);
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_statfsres *resp)
+{
+	struct statfs	*stat = &resp->stats;
+
+	*p++ = htonl(8 * 1024);		/* max transfer size */
+	*p++ = htonl(stat->f_bsize);
+	*p++ = htonl(stat->f_blocks);
+	*p++ = htonl(stat->f_bfree);
+	*p++ = htonl(stat->f_bavail);
+	return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_entry(struct readdir_cd *cd, const char *name,
+					int namlen, off_t offset, ino_t ino)
+{
+	u32	*p = cd->buffer;
+	int	buflen, slen;
+
+	/*
+	dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
+			namlen, name, offset, ino);
+	 */
+
+	if (offset > ~((u32) 0))
+		return -EINVAL;
+	if (cd->offset)
+		*cd->offset = htonl(offset);
+	if (namlen > NFS2_MAXNAMLEN)
+		namlen = NFS2_MAXNAMLEN;/* truncate filename */
+
+	slen = XDR_QUADLEN(namlen);
+	if ((buflen = cd->buflen - slen - 4) < 0) {
+		cd->eob = 1;
+		return -EINVAL;
+	}
+	*p++ = xdr_one;			/* mark entry present */
+	*p++ = htonl((u32) ino);	/* file id */
+	*p++ = htonl((u32) namlen);	/* name length & name */
+	memcpy(p, name, namlen);
+	p += slen;
+	cd->offset = p;			/* remember pointer */
+	*p++ = ~(u32) 0;		/* offset of next entry */
+
+	cd->buflen = buflen;
+	cd->buffer = p;
+	return 0;
+}
+
+/*
+ * XDR release functions
+ */
+int
+nfssvc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+					struct nfsd_fhandle *resp)
+{
+	fh_put(&resp->fh);
+	return 1;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- v2.1.31/linux/fs/nfsd/stats.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/stats.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,92 @@
+/*
+ * nfsstat.c	procfs-based user access to knfsd statistics
+ *
+ * /proc/net/nfssrv
+ * Format:
+ *	net <packets> <udp> <tcp> <tcpconn>
+ *	rpc <packets> <badfmt> <badclnt>
+ *	auth <flavor> <creds> <upcalls> <badauth> <badverf> <authrej>
+ *	fh  <hits> <misses> <avg_util> <stale> <cksum> <badcksum>
+ *	rc <hits> <misses> <nocache>
+ *	proto <version> <nrprocs>
+ *	<calls> <time_msec>
+ *	... (for each procedure and protocol version)
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/stats.h>
+
+struct nfsd_stats	nfsdstats;
+
+static int		nfsd_get_info(char *, char **, off_t, int, int);
+
+#ifndef PROC_NET_NFSSRV
+# define PROC_NET_NFSSRV	0
+#endif
+
+static struct proc_dir_entry proc_nfssrv = {
+	PROC_NET_NFSSRV, 4, "nfsd",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	6, &proc_net_inode_operations,
+	nfsd_get_info
+};
+
+struct svc_stat		nfsd_svcstats = {
+	NULL, &proc_nfssrv, &nfsd_program,
+};
+
+static int
+nfsd_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int			len;
+
+	len = sprintf(buffer,
+		"rc %d %d %d\n",
+			nfsdstats.rchits,
+			nfsdstats.rcmisses,
+			nfsdstats.rcnocache);
+
+	/*
+	 * Append generic nfsd RPC statistics
+	 */
+	if (offset >= len) {
+		offset -= len;
+		len = svcstat_get_info(&nfsd_svcstats, buffer, start,
+					offset, length);
+#if 0
+	} else if (len < length) {
+		len = svcstat_get_info(&nfsd_svcstats, buffer + len, start,
+					offset - len, length - len);
+#endif
+	}
+
+	if (offset >= len) {
+		*start = buffer;
+		return 0;
+	}
+	*start = buffer + offset;
+	if ((len -= offset) > length)
+		len = length;
+	return len;
+}
+
+void
+nfsd_stat_init(void)
+{
+	svcstat_register(&nfsd_svcstats);
+}
+
+void
+nfsd_stat_shutdown(void)
+{
+	svcstat_unregister(&nfsd_svcstats);
+}
diff -u --recursive --new-file v2.1.31/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.1.31/linux/fs/nfsd/vfs.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/nfsd/vfs.c	Fri Apr  4 11:06:02 1997
@@ -0,0 +1,1095 @@
+/*
+ * linux/fs/nfsd/vfs.c
+ *
+ * File operations used by nfsd. Some of these have been ripped from
+ * other parts of the kernel because they weren't in ksyms.c, others
+ * are partial duplicates with added or changed functionality.
+ *
+ * Note that several functions lock the inode upon which they want
+ * to act, most notably those that create directory entries. The
+ * unlock operation can take place either by calling fh_unlock within
+ * the function directly, or at a later time in fh_put(). So if you
+ * notice code paths that apparently fail to unlock the inode, don't
+ * worry--they have been taken care of.
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/ext2_fs.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <netinet/in.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#endif
+
+#define NFSDDBG_FACILITY		NFSDDBG_FILEOP
+
+/* Symbol not exported */
+static struct super_block *get_super(dev_t dev);
+
+/* Open mode for nfsd_open */
+#define OPEN_READ	0
+#define OPEN_WRITE	1
+
+/* Hack until we have a macro check for mandatory locks. */
+#ifndef IS_ISMNDLK
+#define IS_ISMNDLK(i)	(((i)->i_mode & (S_ISGID|S_ISVTX)) == S_ISGID)
+#endif
+
+/* Check for dir entries '.' and '..' */
+#define isdotent(n, l)	(l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
+
+/*
+ * This is a cache of readahead params that help us choose the proper
+ * readahead strategy. Initially, we set all readahead parameters to 0
+ * and let the VFS handle things.
+ * If you increase the number of cached files very much, you'll need to
+ * add a hash table here.
+ */
+struct raparms {
+	struct raparms *	p_next;
+	unsigned int		p_count;
+	dev_t			p_dev;
+	ino_t			p_ino;
+	unsigned long		p_reada,
+				p_ramax,
+				p_raend,
+				p_ralen,
+				p_rawin;
+};
+
+#define FILECACHE_MAX		(2 * NFSD_MAXSERVS)
+static struct raparms		raparms[FILECACHE_MAX];
+static struct raparms *		raparm_cache = 0;
+
+/*
+ * Deny access to certain file systems
+ */
+static inline int
+fs_off_limits(struct super_block *sb)
+{
+	return !sb || sb->s_magic == NFS_SUPER_MAGIC
+	           || sb->s_magic == PROC_SUPER_MAGIC;
+}
+
+/*
+ * Check whether directory is a mount point
+ */
+static inline int
+nfsd_iscovered(struct inode *inode)
+{
+	return inode->i_mount != NULL;
+}
+
+/*
+ * Look up one component of a pathname.
+ */
+int
+nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
+					int len, struct svc_fh *resfh)
+{
+	struct svc_export	*exp;
+	struct super_block	*sb;
+	struct inode		*dirp, *inode;
+	int			perm, err, dotdot = 0;
+
+	dprintk("nfsd: nfsd_lookup(fh %x/%ld, %s)\n",
+			SVCFH_DEV(fhp), SVCFH_INO(fhp), name);
+
+	/* Obtain inode and export */
+	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
+		return err;
+	dirp = fhp->fh_inode;
+	exp  = fhp->fh_export;
+
+	/* check permissions before traversing mount-points */
+	perm = nfsd_permission(exp, dirp, MAY_EXEC);
+
+	dotdot = (len == 2 && name[0] == '.' && name[1] == '.');
+	if (dotdot) {
+		if (dirp == current->fs->root) {
+			dirp->i_count++;
+			*resfh = *fhp;
+			return 0;
+		}
+
+		if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) {
+			dirp->i_count++;
+			*resfh = *fhp;
+			return 0;
+		}
+	} else if (len == 1 && name[0] == '.') {
+		len = 0;
+	} else if (fs_off_limits(dirp->i_sb)) {
+		/* No lookups on NFS mounts and procfs */
+		return nfserr_noent;
+	} else if (nfsd_iscovered(dirp)) {
+		/* broken NFS client */
+		return nfserr_acces;
+	}
+	if (!dirp->i_op || !dirp->i_op->lookup)
+		return nfserr_notdir;
+ 	if (perm != 0)
+		return perm;
+	if (!len) {
+		dirp->i_count++;
+		*resfh = *fhp;
+		return 0;
+	}
+
+	dirp->i_count++;		/* lookup eats the dirp inode */
+	err = dirp->i_op->lookup(dirp, name, len, &inode);
+
+	if (err)
+		return nfserrno(-err);
+
+	/* Note that lookup() has already done a call to iget() so that
+	 * the inode returned never refers to an inode covered by a mount.
+	 * When this has happened, return the covered inode.
+	 */
+	if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) {
+		iput(inode);
+		inode = sb->s_covered;
+		inode->i_count++;
+	}
+
+	fh_compose(resfh, exp, inode);
+	return 0;
+}
+
+/*
+ * Set various file attributes.
+ */
+int
+nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
+{
+	struct inode	*inode;
+	int		accmode = MAY_SATTR;
+	int		ftype = 0;
+	int		imode;
+	int		err;
+
+	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
+		accmode |= MAY_WRITE;
+	if (iap->ia_valid & ATTR_SIZE)
+		ftype = S_IFREG;
+
+	/* Get inode */
+	if ((err = fh_lookup(rqstp, fhp, ftype, accmode)) != 0)
+		return err;
+
+	fh_lock(fhp);			/* lock inode */
+	inode = fhp->fh_inode;
+
+	/* The size case is special... */
+	if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
+		if (iap->ia_size < inode->i_size) {
+			err = nfsd_permission(fhp->fh_export, inode, MAY_TRUNC);
+			if (err != 0)
+				return err;
+		}
+		if ((err = get_write_access(inode)) != 0)
+			return nfserrno(-err);
+		inode->i_size = iap->ia_size;
+		if (inode->i_op && inode->i_op->truncate)
+			inode->i_op->truncate(inode);
+		inode->i_dirt = 1;
+		put_write_access(inode);
+		iap->ia_valid &= ATTR_SIZE;
+		iap->ia_valid |= ATTR_MTIME;
+		iap->ia_mtime = CURRENT_TIME;
+	}
+
+	imode = inode->i_mode;
+	if (iap->ia_valid & ATTR_MODE) {
+		iap->ia_mode &= S_IALLUGO;
+		imode = iap->ia_mode |= (imode & ~S_IALLUGO);
+	}
+
+	/* Revoke setuid/setgid bit on chown/chgrp */
+	if ((iap->ia_valid & ATTR_UID) && (imode & S_ISUID)
+	 && iap->ia_uid != inode->i_uid) {
+		iap->ia_valid |= ATTR_MODE;
+		iap->ia_mode = imode &= ~S_ISUID;
+	}
+	if ((iap->ia_valid & ATTR_GID) && (imode & S_ISGID)
+	 && iap->ia_gid != inode->i_gid) {
+		iap->ia_valid |= ATTR_MODE;
+		iap->ia_mode = imode &= ~S_ISGID;
+	}
+
+	/* Change the attributes. */
+	if (iap->ia_valid) {
+		iap->ia_valid |= ATTR_CTIME;
+		iap->ia_ctime = CURRENT_TIME;
+		err = nfsd_notify_change(inode, iap);
+		if (err)
+			return nfserrno(-err);
+		if (EX_ISSYNC(fhp->fh_export))
+			nfsd_write_inode(inode);
+	}
+
+	return 0;
+}
+
+/*
+ * Open an existing file or directory.
+ * The wflag argument indicates write access.
+ */
+int
+nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
+			int wflag, struct file *filp)
+{
+	struct inode	*inode;
+	int		access, err;
+
+	access = wflag? MAY_WRITE : MAY_READ;
+	if ((err = fh_lookup(rqstp, fhp, type, access)) != 0)
+		return err;
+	inode = fhp->fh_inode;
+
+	/* Disallow access to files with the append-only bit set or
+	 * with mandatory locking enabled */
+	if (IS_APPEND(inode) || IS_ISMNDLK(inode))
+		return nfserr_perm;
+	if (!inode->i_op || !inode->i_op->default_file_ops)
+		return nfserr_perm;
+
+	if (wflag && (err = get_write_access(inode)) != 0)
+		return nfserrno(-err);
+
+	memset(filp, 0, sizeof(*filp));
+	filp->f_op    = inode->i_op->default_file_ops;
+	filp->f_count = 1;
+	filp->f_flags = wflag? O_WRONLY : O_RDONLY;
+	filp->f_mode  = wflag? FMODE_WRITE : FMODE_READ;
+	filp->f_inode = inode;
+
+	if (filp->f_op->open) {
+		err = filp->f_op->open(inode, filp);
+		if (err) {
+			if (wflag)
+				put_write_access(inode);
+			filp->f_count--;
+			return nfserrno(-err);
+		}
+	}
+
+	inode->i_count++;
+	return 0;
+}
+
+/*
+ * Close a file.
+ */
+void
+nfsd_close(struct file *filp)
+{
+	struct inode	*inode;
+
+	inode = filp->f_inode;
+	if (!inode->i_count)
+		printk(KERN_WARNING "nfsd: inode count == 0!\n");
+	if (filp->f_op && filp->f_op->release)
+		filp->f_op->release(inode, filp);
+	if (filp->f_mode & FMODE_WRITE)
+		put_write_access(inode);
+	iput(inode);
+}
+
+/*
+ * Sync a file
+ */
+void
+nfsd_sync(struct inode *inode, struct file *filp)
+{
+	filp->f_op->fsync(inode, filp);
+}
+
+/*
+ * Obtain the readahead parameters for the given file
+ */
+static inline struct raparms *
+nfsd_get_raparms(dev_t dev, ino_t ino)
+{
+	struct raparms	*ra, **rap, **frap = NULL;
+
+	for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
+		if (ra->p_dev != dev || ra->p_ino != ino) {
+			if (ra->p_count == 0)
+				frap = rap;
+		} else
+			goto found;
+	}
+	if (!frap)
+		return NULL;
+	rap = frap;
+	ra = *frap;
+	memset(ra, 0, sizeof(*ra));
+found:
+	if (rap != &raparm_cache) {
+		*rap = ra->p_next;
+		ra->p_next   = raparm_cache;
+		raparm_cache = ra;
+	}
+	ra->p_count++;
+	return ra;
+}
+
+/*
+ * Read data from a file. count must contain the requested read count
+ * on entry. On return, *count contains the number of bytes actually read.
+ */
+int
+nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
+						unsigned long *count)
+{
+	struct raparms	*ra;
+	struct inode	*inode;
+	struct file	file;
+	unsigned long	oldfs;
+	int		err;
+
+	if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0)
+		return err;
+	inode = file.f_inode;
+	if (!file.f_op->read) {
+		nfsd_close(&file);
+		return nfserr_perm;
+	}
+
+	/* Get readahead parameters */
+	if ((ra = nfsd_get_raparms(inode->i_dev, inode->i_ino)) != NULL) {
+		file.f_reada = ra->p_reada;
+		file.f_ramax = ra->p_ramax;
+		file.f_raend = ra->p_raend;
+		file.f_ralen = ra->p_ralen;
+		file.f_rawin = ra->p_rawin;
+	}
+	file.f_pos = offset;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	err = file.f_op->read(file.f_inode, &file, buf, *count);
+	set_fs(oldfs);
+
+	/* Write back readahead params */
+	if (ra != NULL) {
+		dprintk("nfsd: raparms %ld %ld %ld %ld %ld\n",
+			file.f_reada, file.f_ramax, file.f_raend,
+			file.f_ralen, file.f_rawin);
+		ra->p_reada = file.f_reada;
+		ra->p_ramax = file.f_ramax;
+		ra->p_raend = file.f_raend;
+		ra->p_ralen = file.f_ralen;
+		ra->p_rawin = file.f_rawin;
+		ra->p_count -= 1;
+	}
+
+	nfsd_close(&file);
+
+	if (err < 0)
+		return nfserrno(-err);
+	*count = err;
+	return 0;
+}
+
+/*
+ * Write data to a file.
+ * The stable flag requests synchronous writes.
+ */
+int
+nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
+				char *buf, unsigned long cnt, int stable)
+{
+	struct svc_export	*exp;
+	struct file		file;
+	struct inode		*inode;
+	unsigned long		oldfs;
+	int			err;
+
+	if (!cnt)
+		return 0;
+	if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file)) != 0)
+		return err;
+	if (!file.f_op->write) {
+		nfsd_close(&file);
+		return nfserr_perm;
+	}
+
+	inode = fhp->fh_inode;
+	exp   = fhp->fh_export;
+
+	/*
+	 * Request sync writes if
+	 *  -	the sync export option has been set, or
+	 *  -	the client requested O_SYNC behavior (NFSv3 feature).
+	 * When gathered writes have been configured for this volume,
+	 * flushing the data to disk is handled separately below.
+	 */
+	if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp))
+		file.f_flags |= O_SYNC;
+
+	fh_lock(fhp);			/* lock inode */
+	file.f_pos = offset;		/* set write offset */
+
+	/* Write the data. */
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	err = file.f_op->write(inode, &file, buf, cnt);
+	set_fs(oldfs);
+
+	/* clear setuid/setgid flag after write */
+	if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
+		struct iattr	ia;
+
+		ia.ia_valid = ATTR_MODE;
+		ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
+		nfsd_notify_change(inode, &ia);
+	}
+
+	fh_unlock(fhp);			/* unlock inode */
+
+	if (err >= 0 && stable) {
+		static unsigned long	last_ino = 0;
+		static kdev_t		last_dev = NODEV;
+
+		/*
+		 * Gathered writes: If another process is currently
+		 * writing to the file, there's a high chance
+		 * this is another nfsd (triggered by a bulk write
+		 * from a client's biod). Rather than syncing the
+		 * file with each write request, we sleep for 10 msec.
+		 *
+		 * I don't know if this roughly approximates
+		 * C. Juszak's idea of gathered writes, but it's a
+		 * nice and simple solution (IMHO), and it seems to
+		 * work:-)
+		 */
+		if (EX_WGATHER(exp) && (inode->i_writecount > 1
+		 || (last_ino == inode->i_ino && last_dev == inode->i_dev))) {
+#if 0
+			current->timeout = jiffies + 10 * HZ / 1000;
+			interruptible_sleep_on(&inode->i_wait);
+#else
+			dprintk("nfsd: write defer %d\n", current->pid);
+			need_resched = 1;
+			current->timeout = jiffies + HZ / 100;
+			schedule();
+			dprintk("nfsd: write resume %d\n", current->pid);
+#endif
+		}
+
+		if (inode->i_dirt) {
+			dprintk("nfsd: write sync %d\n", current->pid);
+			nfsd_sync(inode, &file);
+			nfsd_write_inode(inode);
+		}
+		wake_up(&inode->i_wait);
+		last_ino = inode->i_ino;
+		last_dev = inode->i_dev;
+	}
+
+	nfsd_close(&file);
+
+	dprintk("nfsd: write complete\n");
+	return (err < 0)? nfserrno(-err) : 0;
+}
+
+/*
+ * Create a file (regular, directory, device, fifo).
+ * UNIX sockets not yet implemented.
+ */
+int
+nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
+		char *fname, int flen, struct iattr *iap,
+		int type, dev_t rdev, struct svc_fh *resfhp)
+{
+	struct inode	*dirp, *inode = NULL;
+	int		err;
+
+	if (!flen)
+		return nfserr_perm;
+
+	if (!(iap->ia_valid & ATTR_MODE))
+		iap->ia_mode = 0;
+
+	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+		return err;
+
+	fh_lock(fhp);			/* lock directory */
+	dirp = fhp->fh_inode;
+	dirp->i_count++;		/* dirop eats the inode */
+
+	switch (type) {
+	case S_IFREG:
+		if (!dirp->i_op || !dirp->i_op->create)
+			return nfserr_perm;
+		err = dirp->i_op->create(dirp, fname, flen,
+					 iap->ia_mode, &inode);
+		break;
+	case S_IFDIR:
+		if (!dirp->i_op || !dirp->i_op->mkdir)
+			return nfserr_perm;
+		err = dirp->i_op->mkdir(dirp, fname, flen, iap->ia_mode);
+		break;
+	case S_IFCHR:
+	case S_IFBLK:
+	case S_IFIFO:
+		if (!dirp->i_op || !dirp->i_op->mknod)
+			return nfserr_perm;
+		err = dirp->i_op->mknod(dirp, fname, flen, iap->ia_mode, rdev);
+		break;
+	default:
+		iput(dirp);
+		err = -EACCES;
+	}
+
+	fh_unlock(fhp);
+
+	if (err < 0)
+		return nfserrno(-err);
+
+	/*
+	 * If the VFS call doesn't return the inode, look it up now.
+	 */
+	if (inode == NULL) {
+		dirp->i_count++;
+		err = dirp->i_op->lookup(dirp, fname, flen, &inode);
+		if (err < 0)
+			return -nfserrno(err);	/* Huh?! */
+	}
+
+	if (EX_ISSYNC(fhp->fh_export))
+		nfsd_write_inode(dirp);
+
+	/* Assemble the file handle for the newly created file */
+	fh_compose(resfhp, fhp->fh_export, inode);
+
+	/* Set file attributes. Mode has already been set and
+	 * setting uid/gid works only for root. Irix appears to
+	 * send along the gid when it tries to implement setgid
+	 * directories via NFS.
+	 */
+	if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) {
+		if ((err = nfsd_setattr(rqstp, resfhp, iap)) != 0) {
+			fh_put(resfhp);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Truncate a file.
+ * The calling routines must make sure to update the ctime
+ * field and call notify_change.
+ */
+int
+nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
+{
+	struct inode	*inode;
+	int		err;
+
+	if ((err = fh_lookup(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
+		return err;
+
+	fh_lock(fhp);			/* lock inode if not yet locked */
+	inode = fhp->fh_inode;
+
+	if ((err = get_write_access(inode)) != 0)
+		return nfserrno(-err);
+	inode->i_size = size;
+	if (inode->i_op && inode->i_op->truncate)
+		inode->i_op->truncate(inode);
+	inode->i_dirt = 1;
+	put_write_access(inode);
+
+	fh_unlock(fhp);
+
+	return 0;
+}
+
+/*
+ * Read a symlink. On entry, *lenp must contain the maximum path length that
+ * fits into the buffer. On return, it contains the true length.
+ */
+int
+nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
+{
+	struct inode	*inode;
+	unsigned long	oldfs;
+	int		err;
+
+	if ((err = fh_lookup(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
+		return err;
+	inode = fhp->fh_inode;
+
+	if (!inode->i_op || !inode->i_op->readlink)
+		return nfserr_io;
+
+	inode->i_count++;
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	err = inode->i_op->readlink(inode, buf, *lenp);
+	set_fs(oldfs);
+
+	if (err < 0)
+		return nfserrno(-err);
+	*lenp = err;
+
+	return 0;
+}
+
+/*
+ * Create a symlink and look up its inode
+ */
+int
+nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
+				char *fname, int flen,
+				char *path,  int plen,
+				struct svc_fh *resfhp)
+{
+	struct inode	*dirp, *inode;
+	int		err;
+
+	if (!flen || !plen)
+		return nfserr_noent;
+
+	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+		return err;
+
+	dirp = fhp->fh_inode;
+	if (nfsd_iscovered(dirp))
+		return nfserr_perm;
+	if (!dirp->i_op || !dirp->i_op->symlink)
+		return nfserr_perm;
+
+	fh_lock(fhp);			/* lock inode */
+	dirp->i_count++;
+	err = dirp->i_op->symlink(dirp, fname, flen, path);
+	fh_unlock(fhp);			/* unlock inode */
+
+	if (err)
+		return nfserrno(-err);
+
+	if (EX_ISSYNC(fhp->fh_export))
+		nfsd_write_inode(dirp);
+
+	/*
+	 * Okay, now look up the inode of the new symlink.
+	 */
+	dirp->i_count++;		/* lookup eats the dirp inode */
+	err = dirp->i_op->lookup(dirp, fname, flen, &inode);
+	if (err)
+		return nfserrno(-err);
+
+	fh_compose(resfhp, fhp->fh_export, inode);
+	return 0;
+}
+
+/*
+ * Create a hardlink
+ */
+int
+nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
+				char *fname, int len, struct svc_fh *tfhp)
+{
+	struct inode	*dirp, *dest;
+	int		err;
+
+	if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
+	    (err = fh_lookup(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
+		return err;
+	dirp = ffhp->fh_inode;
+	dest = tfhp->fh_inode;
+
+	if (!len)
+		return nfserr_perm;
+	if (nfsd_iscovered(dirp))
+		return nfserr_acces;
+	if (dirp->i_dev != dest->i_dev)
+		return nfserr_acces;	/* FIXME: nxdev for NFSv3 */
+	if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ )
+		return nfserr_perm;
+	if (!dirp->i_op || !dirp->i_op->link)
+		return nfserr_perm;
+
+	fh_lock(ffhp);			/* lock directory inode */
+	dirp->i_count++;
+	err = dirp->i_op->link(dest, dirp, fname, len);
+	fh_unlock(ffhp);		/* unlock inode */
+
+	if (!err && EX_ISSYNC(ffhp->fh_export)) {
+		nfsd_write_inode(dirp);
+		nfsd_write_inode(dest);
+	}
+
+	return err? nfserrno(-err) : 0;
+}
+
+/*
+ * Rename a file
+ */
+int
+nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
+			    struct svc_fh *tfhp, char *tname, int tlen)
+{
+	struct inode	*fdir, *tdir;
+	int		err;
+
+	if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
+	 || (err = fh_lookup(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
+		return err;
+	fdir = ffhp->fh_inode;
+	tdir = tfhp->fh_inode;
+
+	if (!flen || (fname[0] == '.' && 
+	    (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
+	    !tlen || (tname[0] == '.' && 
+	    (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
+		return nfserr_perm;
+
+	if (fdir->i_dev != tdir->i_dev)
+		return nfserr_acces;	/* nfserr_nxdev */
+	if (!fdir->i_op || !fdir->i_op->rename)
+		return nfserr_perm;
+
+	fh_lock(tfhp);			/* lock destination directory */
+	tdir->i_count++;
+	fdir->i_count++;
+	err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen, 0);
+	fh_unlock(tfhp);		/* unlock inode */
+
+	if (!err && EX_ISSYNC(tfhp->fh_export)) {
+		nfsd_write_inode(fdir);
+		nfsd_write_inode(tdir);
+	}
+
+	return err? nfserrno(-err) : 0;
+}
+
+/*
+ * Unlink a file or directory
+ */
+int
+nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
+				char *fname, int flen)
+{
+	struct inode	*dirp;
+	int		err;
+
+	if (!flen || isdotent(fname, flen))
+		return nfserr_acces;
+
+	if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
+		return err;
+
+	fh_lock(fhp);			/* lock inode */
+	dirp = fhp->fh_inode;
+
+	if (type == S_IFDIR) {
+		if (!dirp->i_op || !dirp->i_op->rmdir)
+			return nfserr_notdir;
+		dirp->i_count++;
+		err = dirp->i_op->rmdir(dirp, fname, flen);
+	} else {	/* other than S_IFDIR */
+		if (!dirp->i_op || !dirp->i_op->unlink)
+			return nfserr_perm;
+		dirp->i_count++;
+		err = dirp->i_op->unlink(dirp, fname, flen);
+	}
+
+	fh_unlock(fhp);			/* unlock inode */
+	if (!err && EX_ISSYNC(fhp->fh_export))
+		nfsd_write_inode(dirp);
+
+	return err? nfserrno(-err) : 0;
+}
+
+/*
+ * Read entries from a directory.
+ */
+int
+nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, 
+			encode_dent_fn func, u32 *buffer, int *countp)
+{
+	struct readdir_cd cd;
+	struct file	file;
+	u32		*p;
+	int		oldlen, eof, err;
+
+	if (offset > ~(u32) 0)
+		return 0;
+
+	if ((err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file)) != 0)
+		return nfserrno(-err);
+
+	if (!file.f_op->readdir) {
+		nfsd_close(&file);
+		return nfserr_notdir;
+	}
+	file.f_pos = offset;
+
+	/* Set up the readdir context */
+	memset(&cd, 0, sizeof(cd));
+	cd.rqstp  = rqstp;
+	cd.buffer = buffer;
+	cd.buflen = *countp >> 2;
+
+	/*
+	 * Read the directory entries. This silly loop is necessary because
+	 * readdir() is not guaranteed to fill up the entire buffer, but
+	 * may choose to do less.
+	 */
+	do {
+		oldlen = cd.buflen;
+
+		/*
+		dprintk("nfsd: f_op->readdir(%x/%ld @ %d) buflen = %d (%d)\n",
+			file.f_inode->i_dev, file.f_inode->i_ino,
+			(int) file.f_pos, (int) oldlen, (int) cd.buflen);
+		 */
+		err = file.f_op->readdir(file.f_inode, &file,
+					 &cd, (filldir_t) func);
+
+		if (err < 0) {
+			nfsd_close(&file);
+			return nfserrno(-err);
+		}
+		if (oldlen == cd.buflen)
+			break;
+	} while (oldlen != cd.buflen && !cd.eob);
+
+	/* If we didn't fill the buffer completely, we're at EOF */
+	eof = !cd.eob;
+
+	/* Hewlett Packard ignores the eof flag on READDIR. Some
+	 * fs-specific readdir implementations seem to reset f_pos to 0
+	 * at EOF however, causing an endless loop. */
+	if (cd.offset && !eof)
+		*cd.offset = htonl(file.f_pos);
+
+	/* Close the file */
+	nfsd_close(&file);
+
+	p = cd.buffer;
+	*p++ = 0;			/* no more entries */
+	*p++ = htonl(eof);		/* end of directory */
+	*countp = (caddr_t) p - (caddr_t) buffer;
+
+	dprintk("nfsd: readdir result %d bytes, eof %d offset %ld\n",
+				*countp, eof,
+				cd.offset? ntohl(*cd.offset) : -1);
+	return 0;
+}
+
+/*
+ * Get file system stats
+ */
+int
+nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
+{
+	struct inode		*inode;
+	struct super_block	*sb;
+	unsigned long		oldfs;
+	int			err;
+
+	if ((err = fh_lookup(rqstp, fhp, 0, MAY_NOP)) != 0)
+		return err;
+	inode = fhp->fh_inode;
+
+	if (!(sb = inode->i_sb) || !sb->s_op->statfs)
+		return nfserr_io;
+
+	oldfs = get_fs();
+	set_fs (KERNEL_DS);
+	sb->s_op->statfs(sb, stat, sizeof(*stat));
+	set_fs (oldfs);
+
+	return 0;
+}
+
+/*
+ * Check for a user's access permissions to this inode.
+ */
+int
+nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
+{
+	int		err;
+
+	if (acc == MAY_NOP)
+		return 0;
+
+	/*
+	dprintk("nfsd: permission 0x%x%s%s%s%s%s mode 0%o%s%s%s\n",
+		acc,
+		(acc & MAY_READ)?	" read"  : "",
+		(acc & MAY_WRITE)?	" write" : "",
+		(acc & MAY_EXEC)?	" exec"  : "",
+		(acc & MAY_SATTR)?	" sattr" : "",
+		(acc & MAY_TRUNC)?	" trunc" : "",
+		inode->i_mode,
+		IS_IMMUTABLE(inode)?	" immut" : "",
+		IS_APPEND(inode)?	" append" : "",
+		IS_RDONLY(inode)?	" ro" : "");
+	dprintk("      owner %d/%d user %d/%d\n",
+		inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
+	 */
+
+	if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
+		if (EX_RDONLY(exp) || IS_RDONLY(inode))
+			return nfserr_rofs;
+		if (S_ISDIR(inode->i_mode) && nfsd_iscovered(inode))
+			return nfserr_perm;
+		if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
+			return nfserr_perm;
+	}
+	if ((acc & MAY_TRUNC) && IS_APPEND(inode))
+		return nfserr_perm;
+
+	/*
+	 * The file owner always gets access permission. This is to make
+	 * file access work even when the client has done a fchmod(fd, 0).
+	 *
+	 * However, `cp foo bar' should fail nevertheless when bar is
+	 * readonly. A sensible way to do this might be to reject all
+	 * attempts to truncate a read-only file, because a creat() call
+	 * always implies file truncation.
+	 */
+	if (inode->i_uid == current->fsuid /* && !(acc & MAY_TRUNC) */)
+		return 0;
+
+	err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
+
+	/* Allow read access to binaries even when mode 111 */
+	if (err == -EPERM && S_ISREG(inode->i_mode) && acc == MAY_READ)
+		err = permission(inode, MAY_EXEC);
+
+	return err? nfserrno(-err) : 0;
+}
+
+/*
+ * Look up the inode for a given FH.
+ */
+struct inode *
+nfsd_iget(dev_t dev, ino_t ino)
+{
+	struct super_block	*sb;
+
+	if (!(sb = get_super(dev)) || fs_off_limits(sb))
+		return NULL;
+	return __iget(sb, ino, 0);
+}
+
+/*
+ * Write the inode if dirty (copy of fs/inode.c:write_inode)
+ */
+void
+nfsd_write_inode(struct inode *inode)
+{
+	struct super_block	*sb = inode->i_sb;
+
+	if (!inode->i_dirt)
+		return;
+	while (inode->i_lock) {
+		sleep_on(&inode->i_wait);
+		if (!inode->i_dirt)
+			return;
+	}
+	if (!sb || !sb->s_op || !sb->s_op->write_inode) {
+		inode->i_dirt = 0;
+		return;
+	}
+	inode->i_lock = 1;
+	sb->s_op->write_inode(inode);
+	inode->i_lock = 0;
+	wake_up(&inode->i_wait);
+}
+
+/*
+ * Look up the root inode of the parent fs.
+ * We have to go through iget in order to allow for wait_on_inode.
+ */
+int
+nfsd_parentdev(dev_t* devp)
+{
+	struct super_block	*sb;
+
+	if (!(sb = get_super(*devp)) || !sb->s_covered)
+		return 0;
+	if (*devp == sb->s_covered->i_dev)
+		return 0;
+	*devp = sb->s_covered->i_dev;
+	return 1;
+}
+
+/* Duplicated here from fs/super.c because it's not exported */
+static struct super_block *
+get_super(dev_t dev)
+{
+	struct super_block *s;
+
+	if (!dev)
+		return NULL;
+	s = 0 + super_blocks;
+	while (s < NR_SUPER + super_blocks)
+		if (s->s_dev == dev) {
+			wait_on_super(s);
+			if (s->s_dev == dev)
+				return s;
+			s = 0 + super_blocks;
+		} else
+			s++;
+	return NULL;
+}
+
+/*
+ * This is a copy from fs/inode.c because it wasn't exported.
+ */
+int
+nfsd_notify_change(struct inode *inode, struct iattr *attr)
+{
+	int retval;
+
+	if (inode->i_sb && inode->i_sb->s_op &&
+	    inode->i_sb->s_op->notify_change)
+		return inode->i_sb->s_op->notify_change(inode, attr);
+
+	if ((retval = inode_change_ok(inode, attr)) != 0)
+		return retval;
+
+	inode_setattr(inode, attr);
+	return 0;
+}
+
+/*
+ * Initialize readahead param cache
+ */
+void
+nfsd_racache_init(void)
+{
+	int	i;
+
+	if (raparm_cache)
+		return;
+	memset(raparms, 0, sizeof(raparms));
+	for (i = 0; i < FILECACHE_MAX - 1; i++) {
+		raparms[i].p_next = raparms + i + 1;
+	}
+	raparm_cache = raparms;
+}
diff -u --recursive --new-file v2.1.31/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.31/linux/fs/proc/root.c	Mon Mar 17 14:54:31 1997
+++ linux/fs/proc/root.c	Sat Apr  5 12:14:20 1997
@@ -137,6 +137,7 @@
 };
 #endif
 
+#ifdef CONFIG_SYSCTL
 struct proc_dir_entry proc_sys_root = {
 	PROC_SYS, 3, "sys",			/* inode, name */
 	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,	/* mode, nlink, uid, gid */
@@ -145,6 +146,7 @@
 	NULL,					/* next */
 	NULL, NULL				/* parent, subdir */
 };
+#endif
 
 #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
 
@@ -556,7 +558,9 @@
 	proc_register(&proc_root, &proc_root_self);
 	proc_net = create_proc_entry("net", S_IFDIR, 0);
 	proc_scsi = create_proc_entry("scsi", S_IFDIR, 0);
+#ifdef CONFIG_SYSCTL
 	proc_register(&proc_root, &proc_sys_root);
+#endif
 #ifdef CONFIG_MCA
 	proc_register(&proc_root, &proc_mca);
 #endif
diff -u --recursive --new-file v2.1.31/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v2.1.31/linux/fs/smbfs/sock.c	Thu Mar 27 14:40:06 1997
+++ linux/fs/smbfs/sock.c	Fri Apr  4 09:34:31 1997
@@ -27,7 +27,7 @@
 
 static int
 _recvfrom(struct socket *sock, unsigned char *ubuf, int size,
-	  int noblock, unsigned flags)
+	  unsigned flags)
 {
 	struct iovec iov;
 	struct msghdr msg;
@@ -41,9 +41,6 @@
 	iov.iov_base = ubuf;
 	iov.iov_len = size;
 	
-	if (noblock) {
-		flags |= MSG_DONTWAIT;
-	}
 	memset(&scm, 0,sizeof(scm));
 	size=sock->ops->recvmsg(sock, &msg, size, flags, &scm);
 	if(size>=0)
@@ -52,8 +49,7 @@
 }
 
 static int
-_send(struct socket *sock, const void *buff, int len,
-      int noblock, unsigned flags)
+_send(struct socket *sock, const void *buff, int len)
 {
 	struct iovec iov;
 	struct msghdr msg;
@@ -69,12 +65,8 @@
 	
 	iov.iov_base = (void *)buff;
 	iov.iov_len = len;
-	
 
-	if (noblock) 
-		flags |= MSG_DONTWAIT;
-
-	msg.msg_flags = flags;
+	msg.msg_flags = 0;
 
 	err = scm_send(sock, &msg, &scm);
         if (err < 0)
@@ -98,12 +90,14 @@
 		fs = get_fs();
 		set_fs(get_ds());
 
-		result = _recvfrom(sock, (void *) peek_buf, 1, 1, MSG_PEEK);
+		result = _recvfrom(sock, (void *) peek_buf, 1,
+				   MSG_PEEK | MSG_DONTWAIT);
 
 		while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
 		{
 			/* got SESSION KEEP ALIVE */
-			result = _recvfrom(sock, (void *) peek_buf, 4, 1, 0);
+			result = _recvfrom(sock, (void *) peek_buf, 4,
+					   MSG_DONTWAIT);
 
 			DDPRINTK("smb_data_callback:"
 				 " got SESSION KEEP ALIVE\n");
@@ -112,8 +106,8 @@
 			{
 				break;
 			}
-			result = _recvfrom(sock, (void *) peek_buf,
-					   1, 1, MSG_PEEK);
+			result = _recvfrom(sock, (void *) peek_buf, 1,
+					   MSG_PEEK | MSG_DONTWAIT);
 		}
 		set_fs(fs);
 
@@ -233,7 +227,7 @@
 	{
 		result = _send(sock,
 			       (void *) (source + already_sent),
-			       length - already_sent, 0, 0);
+			       length - already_sent);
 
 		if (result == 0)
 		{
@@ -260,7 +254,7 @@
 	{
 		result = _recvfrom(sock,
 				   (void *) (target + already_read),
-				   length - already_read, 0, 0);
+				   length - already_read, 0);
 
 		if (result == 0)
 		{
@@ -393,7 +387,6 @@
 	int total_data = 0;
 	int total_param = 0;
 	int result;
-	unsigned char *inbuf = server->packet;
 	unsigned char *rcv_buf;
 	int buf_len;
 	int data_len = 0;
@@ -409,8 +402,8 @@
 		*ldata = *lparam = 0;
 		return 0;
 	}
-	total_data = WVAL(inbuf, smb_tdrcnt);
-	total_param = WVAL(inbuf, smb_tprcnt);
+	total_data = WVAL(server->packet, smb_tdrcnt);
+	total_param = WVAL(server->packet, smb_tprcnt);
 
 	DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
 
@@ -435,6 +428,8 @@
 
 	while (1)
 	{
+		unsigned char *inbuf = server->packet;
+
 		if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
 		    > total_param)
 		{
diff -u --recursive --new-file v2.1.31/linux/fs/super.c linux/fs/super.c
--- v2.1.31/linux/fs/super.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/super.c	Fri Apr  4 11:24:18 1997
@@ -325,6 +325,7 @@
 				len += sprintf(buf+len, ",wsize=%d",
 					       nfss->wsize);
 			}
+#if 0
 			if (nfss->timeo != 7*HZ/10) {
 				len += sprintf(buf+len, ",timeo=%d",
 					       nfss->timeo*10/HZ);
@@ -333,6 +334,7 @@
 				len += sprintf(buf+len, ",retrans=%d",
 					       nfss->retrans);
 			}
+#endif
 			if (nfss->acregmin != 3*HZ) {
 				len += sprintf(buf+len, ",acregmin=%d",
 					       nfss->acregmin/HZ);
diff -u --recursive --new-file v2.1.31/linux/include/asm-alpha/ptrace.h linux/include/asm-alpha/ptrace.h
--- v2.1.31/linux/include/asm-alpha/ptrace.h	Thu Dec 21 22:22:06 1995
+++ linux/include/asm-alpha/ptrace.h	Thu Apr  3 09:56:30 1997
@@ -37,6 +37,10 @@
 	unsigned long r27;
 	unsigned long r28;
 	unsigned long hae;
+/* JRP - These are the values provided to a0-a2 by PALcode */
+	unsigned long trap_a0;
+	unsigned long trap_a1;
+	unsigned long trap_a2;
 /* These are saved by PAL-code: */
 	unsigned long ps;
 	unsigned long pc;
diff -u --recursive --new-file v2.1.31/linux/include/asm-i386/bitops.h linux/include/asm-i386/bitops.h
--- v2.1.31/linux/include/asm-i386/bitops.h	Thu Mar 27 14:40:06 1997
+++ linux/include/asm-i386/bitops.h	Fri Apr  4 10:49:42 1997
@@ -30,7 +30,7 @@
 {
 	int oldbit;
 
-	__asm__ __volatile__(LOCK_PREFIX
+	__asm__ __volatile__( LOCK_PREFIX
 		"btsl %2,%1\n\tsbbl %0,%0"
 		:"=r" (oldbit),"=m" (ADDR)
 		:"ir" (nr));
@@ -41,7 +41,7 @@
 {
 	int oldbit;
 
-	__asm__ __volatile__(LOCK_PREFIX
+	__asm__ __volatile__( LOCK_PREFIX
 		"btrl %2,%1\n\tsbbl %0,%0"
 		:"=r" (oldbit),"=m" (ADDR)
 		:"ir" (nr));
@@ -52,7 +52,7 @@
 {
 	int oldbit;
 
-	__asm__ __volatile__(LOCK_PREFIX
+	__asm__ __volatile__( LOCK_PREFIX
 		"btcl %2,%1\n\tsbbl %0,%0"
 		:"=r" (oldbit),"=m" (ADDR)
 		:"ir" (nr));
diff -u --recursive --new-file v2.1.31/linux/include/asm-i386/hardirq.h linux/include/asm-i386/hardirq.h
--- v2.1.31/linux/include/asm-i386/hardirq.h	Thu Mar 27 14:40:06 1997
+++ linux/include/asm-i386/hardirq.h	Thu Apr  3 16:31:02 1997
@@ -12,6 +12,8 @@
 #define hardirq_enter(cpu)	(local_irq_count[cpu]++)
 #define hardirq_exit(cpu)	(local_irq_count[cpu]--)
 
+#define synchronize_irq()	do { } while (0)
+
 #else
 
 extern unsigned char global_irq_holder;
@@ -61,7 +63,9 @@
 	__cli();
 	hardirq_exit(cpu);
 	__sti();
-}	
+}
+
+extern void synchronize_irq(void);
 
 #endif /* __SMP__ */
 
diff -u --recursive --new-file v2.1.31/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.1.31/linux/include/asm-i386/system.h	Thu Mar 27 14:40:06 1997
+++ linux/include/asm-i386/system.h	Fri Apr  4 08:39:01 1997
@@ -187,6 +187,9 @@
 struct __xchg_dummy { unsigned long a[100]; };
 #define __xg(x) ((struct __xchg_dummy *)(x))
 
+/*
+ * Note: no "lock" prefix even on SMP: xchg always implies lock anyway
+ */
 static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
 {
 	switch (size) {
diff -u --recursive --new-file v2.1.31/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.31/linux/include/linux/fs.h	Fri Apr  4 08:52:25 1997
+++ linux/include/linux/fs.h	Fri Apr  4 12:15:40 1997
@@ -667,6 +667,7 @@
 		__bforget(buf);
 }
 extern void set_blocksize(kdev_t dev, int size);
+extern unsigned int get_hardblocksize(kdev_t dev);
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
 extern struct buffer_head * breada(kdev_t dev,int block, int size, 
 				   unsigned int pos, unsigned int filesize);
diff -u --recursive --new-file v2.1.31/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.1.31/linux/include/linux/interrupt.h	Fri Apr  4 08:52:25 1997
+++ linux/include/linux/interrupt.h	Fri Apr  4 11:16:00 1997
@@ -80,9 +80,8 @@
 extern inline void start_bh_atomic(void)
 {
 #ifdef __SMP__
-	cli();
 	atomic_inc(&intr_count);
-	sti();
+	synchronize_irq();
 #else
 	intr_count++;
 	barrier();
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/bind.h linux/include/linux/lockd/bind.h
--- v2.1.31/linux/include/linux/lockd/bind.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/bind.h	Fri Dec 13 08:25:50 1996
@@ -0,0 +1,43 @@
+/*
+ * linux/include/linux/lockd/bind.h
+ *
+ * This is the part of lockd visible to nfsd and the nfs client.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_LOCKD_BIND_H
+#define LINUX_LOCKD_BIND_H
+
+#include <linux/lockd/nlm.h>
+
+/* Dummy declarations */
+struct svc_rqst;
+struct knfs_fh;
+struct svc_client;		/* opaque type */
+
+/*
+ * This is the set of functions for lockd->nfsd communication
+ */
+struct nlmsvc_binding {
+	void			(*exp_readlock)(void);
+	void			(*exp_unlock)(void);
+	struct svc_client *	(*exp_getclient)(struct sockaddr_in *);
+	u32			(*fopen)(struct svc_rqst *,
+						struct knfs_fh *,
+						struct file *);
+	void			(*fclose)(struct file *);
+	void			(*detach)(void);
+};
+
+extern struct nlmsvc_binding *	nlmsvc_ops;
+
+/*
+ * Functions exported by the lockd module
+ */
+extern void	nlmsvc_invalidate_client(struct svc_client *clnt);
+extern int	nlmclnt_proc(struct inode *, int, struct file_lock *);
+extern int	lockd_up(void);
+extern void	lockd_down(void);
+
+#endif /* LINUX_LOCKD_BIND_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/debug.h linux/include/linux/lockd/debug.h
--- v2.1.31/linux/include/linux/lockd/debug.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/debug.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,51 @@
+/*
+ * linux/include/linux/lockd/debug.h
+ *
+ * Debugging stuff.
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_LOCKD_DEBUG_H
+#define LINUX_LOCKD_DEBUG_H
+
+#ifdef __KERNEL__
+
+#include <linux/sunrpc/debug.h>
+
+/*
+ * Enable lockd debugging.
+ * Requires RPC_DEBUG.
+ */
+#ifdef RPC_DEBUG
+# define LOCKD_DEBUG		1
+#endif
+
+#undef ifdebug
+#if defined(RPC_DEBUG) && defined(LOCKD_DEBUG)
+# define ifdebug(flag)		if (nlm_debug & NLMDBG_##flag)
+#else
+# define ifdebug(flag)		if (0)
+#endif
+
+#ifdef LOCKD_DEBUG
+char *		  nlm_procname(u32);
+#endif
+
+#endif /* __KERNEL__ */
+
+/*
+ * Debug flags
+ */
+#define NLMDBG_SVC		0x0001
+#define NLMDBG_CLIENT		0x0002
+#define NLMDBG_CLNTLOCK		0x0004
+#define NLMDBG_SVCLOCK		0x0008
+#define NLMDBG_MONITOR		0x0010
+#define NLMDBG_CLNTSUBS		0x0020
+#define NLMDBG_SVCSUBS		0x0040
+#define NLMDBG_HOSTCACHE	0x0080
+#define NLMDBG_ALL		0x7fff
+
+
+#endif /* LINUX_LOCKD_DEBUG_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/lockd.h linux/include/linux/lockd/lockd.h
--- v2.1.31/linux/include/linux/lockd/lockd.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/lockd.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,196 @@
+/*
+ * linux/include/linux/lockd/lockd.h
+ *
+ * General-purpose lockd include file.
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_LOCKD_LOCKD_H
+#define LINUX_LOCKD_LOCKD_H
+
+#ifdef __KERNEL__
+
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/utsname.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/lockd/bind.h>
+#include <linux/lockd/xdr.h>
+#include <linux/lockd/debug.h>
+
+/*
+ * Version string
+ */
+#define LOCKD_VERSION		"0.4"
+
+/*
+ * Default timeout for RPC calls (seconds)
+ */
+#define LOCKD_DFLT_TIMEO	10
+
+/*
+ * Lockd host handle (used both by the client and server personality).
+ */
+struct nlm_host {
+	struct nlm_host *	h_next;		/* linked list (hash table) */
+	struct sockaddr_in	h_addr;		/* peer address */
+	struct svc_client *	h_exportent;	/* NFS client */
+	struct rpc_clnt	*	h_rpcclnt;	/* RPC client to talk to peer */
+	char			h_name[20];	/* remote hostname */
+	u32			h_version;	/* interface version */
+	unsigned short		h_proto;	/* transport proto */
+	unsigned short		h_authflavor;	/* RPC authentication type */
+	unsigned short		h_reclaiming : 1,
+				h_inuse      : 1,
+				h_monitored  : 1;
+	struct wait_queue *	h_gracewait;	/* wait while reclaiming */
+	u32			h_state;	/* pseudo-state counter */
+	u32			h_nsmstate;	/* true remote NSM state */
+	unsigned int		h_count;	/* reference count */
+	struct semaphore	h_sema;		/* mutex for pmap binding */
+	unsigned long		h_nextrebind;	/* next portmap call */
+	unsigned long		h_expires;	/* eligible for GC */
+};
+
+/*
+ * Memory chunk for NLM client RPC request.
+ */
+#define NLMCLNT_OHSIZE		(sizeof(system_utsname.nodename)+10)
+struct nlm_rqst {
+	unsigned int		a_flags;	/* initial RPC task flags */
+	struct nlm_host *	a_host;		/* host handle */
+	struct nlm_args		a_args;		/* arguments */
+	struct nlm_res		a_res;		/* result */
+	char			a_owner[NLMCLNT_OHSIZE];
+};
+
+/*
+ * This struct describes a file held open by lockd on behalf of
+ * an NFS client.
+ */
+struct nlm_file {
+	struct nlm_file *	f_next;		/* linked list */
+	struct knfs_fh		f_handle;	/* NFS file handle */
+	struct file		f_file;		/* VFS file pointer */
+	struct nlm_share *	f_shares;	/* DOS shares */
+	struct nlm_block *	f_blocks;	/* blocked locks */
+	unsigned int		f_locks;	/* guesstimate # of locks */
+	unsigned int		f_count;	/* reference count */
+	struct semaphore	f_sema;		/* avoid concurrent access */
+};
+
+/*
+ * This is a server block (i.e. a lock requested by some client which
+ * couldn't be granted because of a conflicting lock).
+ */
+#define NLM_NEVER		(~(unsigned long) 0)
+struct nlm_block {
+	struct nlm_block *	b_next;		/* linked list (all blocks) */
+	struct nlm_block *	b_fnext;	/* linked list (per file) */
+	struct nlm_rqst		b_call;		/* RPC args & callback info */
+	struct svc_serv *	b_daemon;	/* NLM service */
+	struct nlm_host *	b_host;		/* host handle for RPC clnt */
+	unsigned long		b_when;		/* next re-xmit */
+	unsigned int		b_id;		/* block id */
+	unsigned char		b_queued;	/* re-queued */
+	unsigned char		b_granted;	/* VFS granted lock */
+	unsigned char		b_incall;	/* doing callback */
+	unsigned char		b_done;		/* callback complete */
+	struct nlm_file *	b_file;		/* file in question */
+};
+
+/*
+ * Valid actions for nlmsvc_traverse_files
+ */
+#define NLM_ACT_CHECK		0		/* check for locks */
+#define NLM_ACT_MARK		1		/* mark & sweep */
+#define NLM_ACT_UNLOCK		2		/* release all locks */
+
+/*
+ * Global variables
+ */
+extern struct rpc_program	nlm_program;
+extern struct svc_procedure	nlmsvc_procedures[];
+extern unsigned long		nlmsvc_grace_period;
+extern unsigned long		nlmsvc_timeout;
+
+/*
+ * Lockd client functions
+ */
+struct nlm_rqst * nlmclnt_alloc_call(void);
+int		  nlmclnt_call(struct nlm_rqst *, u32);
+int		  nlmclnt_async_call(struct nlm_rqst *, u32, rpc_action);
+int		  nlmclnt_block(struct nlm_host *, struct file_lock *, u32 *);
+int		  nlmclnt_cancel(struct nlm_host *, struct file_lock *);
+u32		  nlmclnt_grant(struct nlm_lock *);
+void		  nlmclnt_recovery(struct nlm_host *, u32);
+int		  nlmclnt_reclaim(struct nlm_host *, struct file_lock *);
+int		  nlmclnt_setgrantargs(struct nlm_rqst *, struct nlm_lock *);
+void		  nlmclnt_freegrantargs(struct nlm_rqst *);
+
+/*
+ * Host cache
+ */
+struct nlm_host * nlmclnt_lookup_host(struct sockaddr_in *, int, int);
+struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *);
+struct nlm_host * nlm_lookup_host(struct svc_client *,
+					struct sockaddr_in *, int, int);
+struct rpc_clnt * nlm_bind_host(struct nlm_host *);
+void		  nlm_rebind_host(struct nlm_host *);
+void		  nlm_release_host(struct nlm_host *);
+void		  nlm_shutdown_hosts(void);
+
+/*
+ * Server-side lock handling
+ */
+u32		  nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
+					struct nlm_lock *, int, u32);
+u32		  nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
+u32		  nlmsvc_testlock(struct nlm_file *, struct nlm_lock *,
+					struct nlm_lock *);
+u32		  nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *);
+unsigned long	  nlmsvc_retry_blocked(void);
+int		  nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
+					int action);
+
+/*
+ * File handling for the server personality
+ */
+u32		  nlm_lookup_file(struct svc_rqst *, struct nlm_file **,
+					struct nfs_fh *);
+void		  nlm_release_file(struct nlm_file *);
+void		  nlmsvc_mark_resources(void);
+void		  nlmsvc_free_host_resources(struct nlm_host *);
+
+extern __inline__ struct inode *
+nlmsvc_file_inode(struct nlm_file *file)
+{
+	return file->f_file.f_inode;
+}
+
+/*
+ * Compare two host addresses (needs modifying for ipv6)
+ */
+extern __inline__ int
+nlm_cmp_addr(struct sockaddr_in *sin1, struct sockaddr_in *sin2)
+{
+	return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
+}
+
+/*
+ * Compare two NLM locks.
+ * When the second lock is of type F_UNLCK, this acts like a wildcard.
+ */
+extern __inline__ int
+nlm_compare_locks(struct file_lock *fl1, struct file_lock *fl2)
+{
+	return	fl1->fl_pid   == fl2->fl_pid
+	     && fl1->fl_start == fl2->fl_start
+	     && fl1->fl_end   == fl2->fl_end
+	     &&(fl1->fl_type  == fl2->fl_type || fl2->fl_type == F_UNLCK);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* LINUX_LOCKD_LOCKD_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/nlm.h linux/include/linux/lockd/nlm.h
--- v2.1.31/linux/include/linux/lockd/nlm.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/nlm.h	Fri Dec 13 08:23:14 1996
@@ -0,0 +1,52 @@
+/*
+ * linux/include/linux/lockd/nlm.h
+ *
+ * Declarations for the Network Lock Manager protocol.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_LOCKD_NLM_H
+#define LINUX_LOCKD_NLM_H
+
+/* Maximum file offset in file_lock.fl_end */
+#ifdef OFFSET_MAX
+# define NLM_OFFSET_MAX		OFFSET_MAX
+#else
+# define NLM_OFFSET_MAX		((off_t) 0x7fffffff)
+#endif
+
+/* Return states for NLM */
+enum {
+	NLM_LCK_GRANTED = 0,
+	NLM_LCK_DENIED,
+	NLM_LCK_DENIED_NOLOCKS,
+	NLM_LCK_BLOCKED,
+	NLM_LCK_DENIED_GRACE_PERIOD,
+};
+
+#define NLM_PROGRAM		100021
+
+#define NLMPROC_NULL		0
+#define NLMPROC_TEST		1
+#define NLMPROC_LOCK		2
+#define NLMPROC_CANCEL		3
+#define NLMPROC_UNLOCK		4
+#define NLMPROC_GRANTED		5
+#define NLMPROC_TEST_MSG	6
+#define NLMPROC_LOCK_MSG	7
+#define NLMPROC_CANCEL_MSG	8
+#define NLMPROC_UNLOCK_MSG	9
+#define NLMPROC_GRANTED_MSG	10
+#define NLMPROC_TEST_RES	11
+#define NLMPROC_LOCK_RES	12
+#define NLMPROC_CANCEL_RES	13
+#define NLMPROC_UNLOCK_RES	14
+#define NLMPROC_GRANTED_RES	15
+#define NLMPROC_SHARE		20
+#define NLMPROC_UNSHARE		21
+#define NLMPROC_NM_LOCK		22
+#define NLMPROC_FREE_ALL	23
+#define NLMPROC_NSM_NOTIFY	24		/* statd callback */
+
+#endif /* LINUX_LOCKD_NLM_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/share.h linux/include/linux/lockd/share.h
--- v2.1.31/linux/include/linux/lockd/share.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/share.h	Thu Dec 12 14:22:19 1996
@@ -0,0 +1,30 @@
+/*
+ * linux/include/linux/lockd/share.h
+ *
+ * DOS share management for lockd.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_LOCKD_SHARE_H
+#define LINUX_LOCKD_SHARE_H
+
+/*
+ * DOS share for a specific file
+ */
+struct nlm_share {
+	struct nlm_share *	s_next;		/* linked list */
+	struct nlm_host *	s_host;		/* client host */
+	struct nlm_file *	s_file;		/* shared file */
+	struct xdr_netobj	s_owner;	/* owner handle */
+	u32			s_access;	/* access mode */
+	u32			s_mode;		/* deny mode */
+};
+
+u32	nlmsvc_share_file(struct nlm_host *, struct nlm_file *,
+					       struct nlm_args *);
+u32	nlmsvc_unshare_file(struct nlm_host *, struct nlm_file *,
+					       struct nlm_args *);
+int	nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, int);
+
+#endif /* LINUX_LOCKD_SHARE_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/sm_inter.h linux/include/linux/lockd/sm_inter.h
--- v2.1.31/linux/include/linux/lockd/sm_inter.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/sm_inter.h	Wed Dec  4 02:54:18 1996
@@ -0,0 +1,45 @@
+/*
+ * linux/include/linux/lockd/sm_inter.h
+ *
+ * Declarations for the kernel statd client.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_LOCKD_SM_INTER_H
+#define LINUX_LOCKD_SM_INTER_H
+
+#define SM_PROGRAM	100024
+#define SM_VERSION	1
+#define SM_STAT		1
+#define SM_MON		2
+#define SM_UNMON	3
+#define SM_UNMON_ALL	4
+#define SM_SIMU_CRASH	5
+#define SM_NOTIFY	6
+
+#define SM_MAXSTRLEN	1024
+
+/*
+ * Arguments for all calls to statd
+ */
+struct nsm_args {
+	u32		addr;		/* remote address */
+	u32		prog;		/* RPC callback info */
+	u32		vers;
+	u32		proc;
+};
+
+/*
+ * Result returned by statd
+ */
+struct nsm_res {
+	u32		status;
+	u32		state;
+};
+
+int		nsm_monitor(struct nlm_host *);
+int		nsm_unmonitor(struct nlm_host *);
+extern u32	nsm_local_state;
+
+#endif /* LINUX_LOCKD_SM_INTER_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/lockd/xdr.h linux/include/linux/lockd/xdr.h
--- v2.1.31/linux/include/linux/lockd/xdr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/lockd/xdr.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,86 @@
+/*
+ * linux/include/linux/lockd/xdr.h
+ *
+ * XDR types for the NLM protocol
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LOCKD_XDR_H
+#define LOCKD_XDR_H
+
+#include <linux/fs.h>
+#include <linux/nfs.h>
+#include <linux/sunrpc/xdr.h>
+
+extern u32	nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks,
+		nlm_lck_blocked, nlm_lck_denied_grace_period;
+
+/* Lock info passed via NLM */
+struct nlm_lock {
+	char *			caller;
+	struct nfs_fh		fh;
+	struct xdr_netobj	oh;
+	struct file_lock	fl;
+};
+
+/*
+ * Generic lockd arguments for all but sm_notify
+ */
+struct nlm_args {
+	u32			cookie;
+	struct nlm_lock		lock;
+	u32			block;
+	u32			reclaim;
+	u32			state;
+	u32			monitor;
+	u32			fsm_access;
+	u32			fsm_mode;
+};
+
+/*
+ * Generic lockd result
+ */
+struct nlm_res {
+	u32			cookie;
+	u32			status;
+	struct nlm_lock		lock;
+};
+
+/*
+ * statd callback when client has rebooted
+ */
+struct nlm_reboot {
+	char *		mon;
+	int		len;
+	u32		state;
+	u32		addr;
+};
+
+/*
+ * Contents of statd callback when monitored host rebooted
+ */
+#define NLMSVC_XDRSIZE		sizeof(struct nlm_args)
+
+void	nlmxdr_init(void);
+int	nlmsvc_decode_testargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlmsvc_encode_testres(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlmsvc_decode_lockargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlmsvc_decode_cancargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlmsvc_decode_unlockargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlmsvc_encode_res(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlmsvc_decode_res(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlmsvc_encode_void(struct svc_rqst *, u32 *, void *);
+int	nlmsvc_decode_void(struct svc_rqst *, u32 *, void *);
+int	nlmsvc_decode_shareargs(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlmsvc_encode_shareres(struct svc_rqst *, u32 *, struct nlm_res *);
+int	nlmsvc_decode_notify(struct svc_rqst *, u32 *, struct nlm_args *);
+int	nlmsvc_decode_reboot(struct svc_rqst *, u32 *, struct nlm_reboot *);
+/*
+int	nlmclt_encode_testargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_lockargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_cancargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+int	nlmclt_encode_unlockargs(struct rpc_rqst *, u32 *, struct nlm_args *);
+ */
+
+#endif /* LOCKD_XDR_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs.h linux/include/linux/nfs.h
--- v2.1.31/linux/include/linux/nfs.h	Sun Jan 29 20:51:02 1995
+++ linux/include/linux/nfs.h	Mon Mar  3 01:14:03 1997
@@ -1,65 +1,28 @@
+/*
+ * NFS protocol definitions
+ */
 #ifndef _LINUX_NFS_H
 #define _LINUX_NFS_H
 
-#define NFS_PORT 2049
-#define NFS_MAXDATA 8192
-#define NFS_MAXPATHLEN 1024
-#define NFS_MAXNAMLEN 255
-#define NFS_MAXGROUPS 16
-#define NFS_FHSIZE 32
-#define NFS_COOKIESIZE 4
-#define NFS_FIFO_DEV (-1)
-#define NFSMODE_FMT 0170000
-#define NFSMODE_DIR 0040000
-#define NFSMODE_CHR 0020000
-#define NFSMODE_BLK 0060000
-#define NFSMODE_REG 0100000
-#define NFSMODE_LNK 0120000
-#define NFSMODE_SOCK 0140000
-#define NFSMODE_FIFO 0010000
-
-#ifdef __KERNEL__ /* user programs should get these from the rpc header files */
-
-#define RPC_VERSION 2
-
-enum rpc_auth_flavor {
-	RPC_AUTH_NULL = 0,
-	RPC_AUTH_UNIX = 1,
-	RPC_AUTH_SHORT = 2
-};
-
-enum rpc_msg_type {
-	RPC_CALL = 0,
-	RPC_REPLY = 1
-};
-
-enum rpc_reply_stat {
-	RPC_MSG_ACCEPTED = 0,
-	RPC_MSG_DENIED = 1
-};
-
-enum rpc_accept_stat {
-	RPC_SUCCESS = 0,
-	RPC_PROG_UNAVAIL = 1,
-	RPC_PROG_MISMATCH = 2,
-	RPC_PROC_UNAVAIL = 3,
-	RPC_GARBAGE_ARGS = 4
-};
-
-enum rpc_reject_stat {
-	RPC_MISMATCH = 0,
-	RPC_AUTH_ERROR = 1
-};
-
-enum rpc_auth_stat {
-	RPC_AUTH_BADCRED = 1,
-	RPC_AUTH_REJECTEDCRED = 2,
-	RPC_AUTH_BADVERF = 3,
-	RPC_AUTH_REJECTEDVERF = 4,
-	RPC_AUTH_TOOWEAK = 5
-};
+#include <linux/sunrpc/msg_prot.h>
+
+#define NFS_PORT	2049
+#define NFS_MAXDATA	8192
+#define NFS_MAXPATHLEN	1024
+#define NFS_MAXNAMLEN	255
+#define NFS_MAXGROUPS	16
+#define NFS_FHSIZE	32
+#define NFS_COOKIESIZE	4
+#define NFS_FIFO_DEV	(-1)
+#define NFSMODE_FMT	0170000
+#define NFSMODE_DIR	0040000
+#define NFSMODE_CHR	0020000
+#define NFSMODE_BLK	0060000
+#define NFSMODE_REG	0100000
+#define NFSMODE_LNK	0120000
+#define NFSMODE_SOCK	0140000
+#define NFSMODE_FIFO	0010000
 
-#endif /* __KERNEL__ */
 	
 enum nfs_stat {
 	NFS_OK = 0,
@@ -96,6 +59,10 @@
 	NFFIFO = 8
 };
 
+struct nfs_fh {
+	char			data[NFS_FHSIZE];
+};
+
 #define NFS_PROGRAM		100003
 #define NFS_VERSION		2
 #define NFSPROC_NULL		0
@@ -117,54 +84,140 @@
 #define NFSPROC_READDIR		16
 #define NFSPROC_STATFS		17
 
-struct nfs_fh {
-	char data[NFS_FHSIZE];
-};
+#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+
+extern struct rpc_program	nfs_program;
+extern struct rpc_stat		nfs_rpcstat;
 
 struct nfs_time {
-	u_int seconds;
-	u_int useconds;
+	__u32			seconds;
+	__u32			useconds;
 };
 
 struct nfs_fattr {
-	enum nfs_ftype type;
-	u_int mode;
-	u_int nlink;
-	u_int uid;
-	u_int gid;
-	u_int size;
-	u_int blocksize;
-	u_int rdev;
-	u_int blocks;
-	u_int fsid;
-	u_int fileid;
-	struct nfs_time atime;
-	struct nfs_time mtime;
-	struct nfs_time ctime;
+	enum nfs_ftype		type;
+	__u32			mode;
+	__u32			nlink;
+	__u32			uid;
+	__u32			gid;
+	__u32			size;
+	__u32			blocksize;
+	__u32			rdev;
+	__u32			blocks;
+	__u32			fsid;
+	__u32			fileid;
+	struct nfs_time		atime;
+	struct nfs_time		mtime;
+	struct nfs_time		ctime;
 };
 
 struct nfs_sattr {
-	u_int mode;
-	u_int uid;
-	u_int gid;
-	u_int size;
-	struct nfs_time atime;
-	struct nfs_time mtime;
+	__u32			mode;
+	__u32			uid;
+	__u32			gid;
+	__u32			size;
+	struct nfs_time		atime;
+	struct nfs_time		mtime;
 };
 
 struct nfs_entry {
-	u_int fileid;
-	char *name;
-	int cookie;
-	int eof;
+	__u32			fileid;
+	char *			name;
+	unsigned int		length;
+	__u32			cookie;
+	__u32			eof;
 };
 
 struct nfs_fsinfo {
-	u_int tsize;
-	u_int bsize;
-	u_int blocks;
-	u_int bfree;
-	u_int bavail;
+	__u32			tsize;
+	__u32			bsize;
+	__u32			blocks;
+	__u32			bfree;
+	__u32			bavail;
+};
+
+#ifdef NFS_NEED_XDR_TYPES
+
+struct nfs_sattrargs {
+	struct nfs_fh *		fh;
+	struct nfs_sattr *	sattr;
+};
+
+struct nfs_diropargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+};
+
+struct nfs_readargs {
+	struct nfs_fh *		fh;
+	__u32			offset;
+	__u32			count;
+	void *			buffer;
+};
+
+struct nfs_writeargs {
+	struct nfs_fh *		fh;
+	__u32			offset;
+	__u32			count;
+	const void *		buffer;
+};
+
+struct nfs_createargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	struct nfs_sattr *	sattr;
+};
+
+struct nfs_renameargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+};
+
+struct nfs_linkargs {
+	struct nfs_fh *		fromfh;
+	struct nfs_fh *		tofh;
+	const char *		toname;
 };
+
+struct nfs_symlinkargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	const char *		topath;
+	struct nfs_sattr *	sattr;
+};
+
+struct nfs_readdirargs {
+	struct nfs_fh *		fh;
+	__u32			cookie;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs_diropok {
+	struct nfs_fh *		fh;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs_readres {
+	struct nfs_fattr *	fattr;
+	unsigned int		count;
+};
+
+struct nfs_readlinkres {
+	char **			string;
+	unsigned int *		lenp;
+	unsigned int		maxlen;
+	void *			buffer;
+};
+
+struct nfs_readdirres {
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+#endif /* NFS_NEED_XDR_TYPES */
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs3.h linux/include/linux/nfs3.h
--- v2.1.31/linux/include/linux/nfs3.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfs3.h	Thu Dec 12 16:02:54 1996
@@ -0,0 +1,252 @@
+/*
+ * NFSv3 protocol definitions
+ */
+#ifndef _LINUX_NFS3_H
+#define _LINUX_NFS3_H
+
+#include <linux/sunrpc/msg_prot.h>
+#include <linux/nfs.h>
+
+#define NFS3_PORT		2049
+#define NFS3_MAXDATA		8192
+#define NFS3_MAXPATHLEN		PATH_MAX
+#define NFS3_MAXNAMLEN		NAME_MAX
+#define NFS3_MAXGROUPS		16
+#define NFS3_FHSIZE		NFS_FHSIZE
+#define NFS3_COOKIESIZE		4
+#define NFS3_FIFO_DEV		(-1)
+#define NFS3MODE_FMT		0170000
+#define NFS3MODE_DIR		0040000
+#define NFS3MODE_CHR		0020000
+#define NFS3MODE_BLK		0060000
+#define NFS3MODE_REG		0100000
+#define NFS3MODE_LNK		0120000
+#define NFS3MODE_SOCK		0140000
+#define NFS3MODE_FIFO		0010000
+
+	
+enum nfs3_stat {
+	NFS3_OK			= 0,
+	NFS3ERR_PERM		= 1,
+	NFS3ERR_NOENT		= 2,
+	NFS3ERR_IO		= 5,
+	NFS3ERR_NXIO		= 6,
+	NFS3ERR_EAGAIN		= 11,
+	NFS3ERR_ACCES		= 13,
+	NFS3ERR_EXIST		= 17,
+	NFS3ERR_XDEV		= 18,	/* new in NFSv3 */
+	NFS3ERR_NODEV		= 19,
+	NFS3ERR_NOTDIR		= 20,
+	NFS3ERR_ISDIR		= 21,
+	NFS3ERR_INVAL		= 22,	/* new in NFSv3 */
+	NFS3ERR_FBIG		= 27,
+	NFS3ERR_NOSPC		= 28,
+	NFS3ERR_ROFS		= 30,
+	NFS3ERR_MLINK		= 31,	/* new in NFSv3 */
+	NFS3ERR_NAMETOOLONG	= 63,
+	NFS3ERR_NOTEMPTY	= 66,
+	NFS3ERR_DQUOT		= 69,
+	NFS3ERR_STALE		= 70,
+	NFS3ERR_REMOTE		= 71,	/* new in NFSv3 */
+	NFS3ERR_BADHANDLE	= 10001,/* ditto */
+	NFS3ERR_NOT_SYNC	= 10002,/* ditto */
+	NFS3ERR_BAD_COOKIE	= 10003,/* ditto */
+	NFS3ERR_NOTSUPP		= 10004,/* ditto */
+	NFS3ERR_TOOSMALL	= 10005,/* ditto */
+	NFS3ERR_SERVERFAULT	= 10006,/* ditto */
+	NFS3ERR_BADTYPE		= 10007,/* ditto */
+	NFS3ERR_JUKEBOX		= 10008,/* ditto */
+};
+
+enum nfs3_ftype {
+	NF3NON  = 0,
+	NF3REG  = 1,
+	NF3DIR  = 2,
+	NF3BLK  = 3,
+	NF3CHR  = 4,
+	NF3LNK  = 5,
+	NF3SOCK = 6,
+	NF3FIFO = 7,	/* changed from NFSv2 (was 8) */
+	NF3BAD  = 8
+};
+
+#define NFS3_VERSION		3
+#define NFSPROC_NULL		0
+#define NFSPROC_GETATTR		1
+#define NFSPROC_SETATTR		2
+#define NFSPROC_ROOT		3
+#define NFSPROC_LOOKUP		4
+#define NFSPROC_READLINK	5
+#define NFSPROC_READ		6
+#define NFSPROC_WRITECACHE	7
+#define NFSPROC_WRITE		8
+#define NFSPROC_CREATE		9
+#define NFSPROC_REMOVE		10
+#define NFSPROC_RENAME		11
+#define NFSPROC_LINK		12
+#define NFSPROC_SYMLINK		13
+#define NFSPROC_MKDIR		14
+#define NFSPROC_RMDIR		15
+#define NFSPROC_READDIR		16
+#define NFSPROC_STATFS		17
+
+#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+
+struct nfs3_fh {
+	__u32			size;
+	__u8			data[NFS3_FHSIZE];
+};
+
+struct nfs3_fattr {
+	enum nfs3_ftype		type;
+	__u32			mode;
+	__u32			nlink;
+	__u32			uid;
+	__u32			gid;
+	__u64			size;
+	__u64			used;
+	__u32			rdev_maj;
+	__u32			rdev_min;
+	__u32			fsid;
+	__u32			fileid;
+	struct nfs_time		atime;
+	struct nfs_time		mtime;
+	struct nfs_time		ctime;
+};
+
+struct nfs3_wcc_attr {
+	__u64			size;
+	struct nfs_time		mtime;
+	struct nfs_time		ctime;
+};
+
+struct nfs3_wcc_data {
+	struct nfs3_wcc_attr	before;
+	struct nfs3_wcc_attr	after;
+};
+
+struct nfs3_sattr {
+	__u32			valid;
+	__u32			mode;
+	__u32			uid;
+	__u32			gid;
+	__u64			size;
+	struct nfs_time		atime;
+	struct nfs_time		mtime;
+};
+
+struct nfs3_entry {
+	__u32			fileid;
+	char *			name;
+	unsigned int		length;
+	__u32			cookie;
+	__u32			eof;
+};
+
+struct nfs3_fsinfo {
+	__u32			tsize;
+	__u32			bsize;
+	__u32			blocks;
+	__u32			bfree;
+	__u32			bavail;
+};
+
+#ifdef NFS_NEED_XDR_TYPES
+
+struct nfs3_sattrargs {
+	struct nfs_fh *		fh;
+	struct nfs_sattr *	sattr;
+};
+
+struct nfs3_diropargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+};
+
+struct nfs3_readargs {
+	struct nfs_fh *		fh;
+	__u32			offset;
+	__u32			count;
+	void *			buffer;
+};
+
+struct nfs3_writeargs {
+	struct nfs_fh *		fh;
+	__u32			offset;
+	__u32			count;
+	const void *		buffer;
+};
+
+struct nfs3_createargs {
+	struct nfs_fh *		fh;
+	const char *		name;
+	struct nfs_sattr *	sattr;
+};
+
+struct nfs3_renameargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+};
+
+struct nfs3_linkargs {
+	struct nfs_fh *		fromfh;
+	struct nfs_fh *		tofh;
+	const char *		toname;
+};
+
+struct nfs3_symlinkargs {
+	struct nfs_fh *		fromfh;
+	const char *		fromname;
+	const char *		topath;
+	struct nfs_sattr *	sattr;
+};
+
+struct nfs3_readdirargs {
+	struct nfs_fh *		fh;
+	__u32			cookie;
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+struct nfs3_diropok {
+	struct nfs_fh *		fh;
+	struct nfs_fattr *	fattr;
+};
+
+struct nfs3_readres {
+	struct nfs_fattr *	fattr;
+	unsigned int		count;
+};
+
+struct nfs3_readlinkres {
+	char **			string;
+	unsigned int *		lenp;
+	unsigned int		maxlen;
+	void *			buffer;
+};
+
+struct nfs3_readdirres {
+	void *			buffer;
+	unsigned int		bufsiz;
+};
+
+/*
+ * The following are for NFSv3
+ */
+struct nfs3_fh {
+	__u32			size;
+	__u8			data[NFS3_FHSIZE]
+};
+
+struct nfs3_wcc_attr {
+	__u64			size;
+	struct nfs_time		mtime;
+	struct nfs_time		ctime;
+};
+
+#endif /* NFS_NEED_XDR_TYPES */
+#endif /* __KERNEL__ */
+
+#endif
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.1.31/linux/include/linux/nfs_fs.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/nfs_fs.h	Fri Apr  4 12:15:40 1997
@@ -1,6 +1,3 @@
-#ifndef _LINUX_NFS_FS_H
-#define _LINUX_NFS_FS_H
-
 /*
  *  linux/include/linux/nfs_fs.h
  *
@@ -9,26 +6,39 @@
  *  OS-specific nfs filesystem definitions and declarations
  */
 
-#include <linux/nfs.h>
+#ifndef _LINUX_NFS_FS_H
+#define _LINUX_NFS_FS_H
 
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/nfs.h>
 #include <linux/in.h>
+#include <linux/sunrpc/debug.h>
 #include <linux/nfs_mount.h>
 
 /*
- * The readdir cache size controls how many directory entries are cached.
- * Its size is limited by the number of nfs_entry structures that can fit
- * in one page, currently, the limit is 256 when using 4KB pages.
+ * Enable debugging support for nfs client.
+ * Requires RPC_DEBUG.
  */
+#ifdef RPC_DEBUG
+# define NFS_DEBUG
+#endif
 
-#define NFS_READDIR_CACHE_SIZE		64
+/*
+ * NFS_MAX_DIRCACHE controls the number of simultaneously cached
+ * directory chunks. Each chunk holds the list of nfs_entry's returned
+ * in a single readdir call in a memory region of size PAGE_SIZE.
+ *
+ * Note that at most server->rsize bytes of the cache memory are used.
+ */
+#define NFS_MAX_DIRCACHE		16
 
 #define NFS_MAX_FILE_IO_BUFFER_SIZE	16384
-#define NFS_DEF_FILE_IO_BUFFER_SIZE	1024
+#define NFS_DEF_FILE_IO_BUFFER_SIZE	4096
 
 /*
  * The upper limit on timeouts for the exponential backoff algorithm.
  */
-
 #define NFS_MAX_RPC_TIMEOUT		(6*HZ)
 
 /*
@@ -36,113 +46,168 @@
  * It is better not to make this too large although the optimum
  * depends on a usage and environment.
  */
-
 #define NFS_LOOKUP_CACHE_SIZE		64
 
+/*
+ * superblock magic number for NFS
+ */
 #define NFS_SUPER_MAGIC			0x6969
 
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
+#define NFS_CLIENT(inode)		(NFS_SERVER(inode)->client)
+#define NFS_ADDR(inode)			(RPC_PEERADDR(NFS_CLIENT(inode)))
+#define NFS_CONGESTED(inode)		(RPC_CONGESTED(NFS_CLIENT(inode)))
 #define NFS_FH(inode)			(&(inode)->u.nfs_i.fhandle)
-#define NFS_RENAMED_DIR(inode)		((inode)->u.nfs_i.silly_rename_dir)
+
 #define NFS_READTIME(inode)		((inode)->u.nfs_i.read_cache_jiffies)
 #define NFS_OLDMTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
-#define NFS_ATTRTIMEO(inode)		((inode)->u.nfs_i.attrtimeo)
-#define NFS_MINATTRTIMEO(inode)		(S_ISREG((inode)->i_mode)?	\
-						NFS_SERVER(inode)->acregmin : \
-						NFS_SERVER(inode)->acdirmin)
 #define NFS_CACHEINV(inode) \
 do { \
 	NFS_READTIME(inode) = jiffies - 1000000; \
 	NFS_OLDMTIME(inode) = 0; \
 } while (0)
+#define NFS_ATTRTIMEO(inode)		((inode)->u.nfs_i.attrtimeo)
+#define NFS_MINATTRTIMEO(inode) \
+	(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
+			       : NFS_SERVER(inode)->acregmin)
+#define NFS_MAXATTRTIMEO(inode) \
+	(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
+			       : NFS_SERVER(inode)->acregmax)
 
+#define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
+#define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATE)
 
-#ifdef __KERNEL__
+#define NFS_RENAMED_DIR(inode)		((inode)->u.nfs_i.silly_inode)
+#define NFS_WRITEBACK(inode)		((inode)->u.nfs_i.writeback)
 
-/* linux/fs/nfs/proc.c */
+/*
+ * These are the default flags for swap requests
+ */
+#define NFS_RPC_SWAPFLAGS		(RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
 
+#ifdef __KERNEL__
+
+/*
+ * linux/fs/nfs/proc.c
+ */
 extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			    struct nfs_fattr *fattr);
+			struct nfs_fattr *fattr);
 extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
-			    struct nfs_sattr *sattr, struct nfs_fattr *fattr);
+			struct nfs_sattr *sattr, struct nfs_fattr *fattr);
 extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
-			   const char *name, struct nfs_fh *fhandle,
-			   struct nfs_fattr *fattr);
+			const char *name, struct nfs_fh *fhandle,
+			struct nfs_fattr *fattr);
 extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			int **p0, char **string, unsigned int *len,
+			void **p0, char **string, unsigned int *len,
 			unsigned int maxlen);
 extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
-			 int offset, int count, char *data,
-			 struct nfs_fattr *fattr);
-extern int nfs_proc_write(struct inode * inode, int offset,
-			  int count, const char *data, struct nfs_fattr *fattr);
+			int swap, unsigned long offset, unsigned int count,
+			void *buffer, struct nfs_fattr *fattr);
+extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
+			int swap, unsigned long offset, unsigned int count,
+			const void *buffer, struct nfs_fattr *fattr);
 extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
-			   const char *name, struct nfs_sattr *sattr,
-			   struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+			const char *name, struct nfs_sattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
-			   const char *name);
+			const char *name);
 extern int nfs_proc_rename(struct nfs_server *server,
-			   struct nfs_fh *old_dir, const char *old_name,
-			   struct nfs_fh *new_dir, const char *new_name,
-			   int must_be_dir);
+			struct nfs_fh *old_dir, const char *old_name,
+			struct nfs_fh *new_dir, const char *new_name);
 extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
-			 struct nfs_fh *dir, const char *name);
+			struct nfs_fh *dir, const char *name);
 extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
-			    const char *name, const char *path, struct nfs_sattr *sattr);
+			const char *name, const char *path,
+			struct nfs_sattr *sattr);
 extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
-			  const char *name, struct nfs_sattr *sattr,
-			  struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+			const char *name, struct nfs_sattr *sattr,
+			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
-			  const char *name);
+			const char *name);
 extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			    int cookie, int count, struct nfs_entry *entry);
+			u32 cookie, unsigned int size, struct nfs_entry *entry);
 extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
-			    struct nfs_fsinfo *res);
-extern int nfs_proc_read_request(struct rpc_ioreq *, struct nfs_server *,
-				 struct nfs_fh *, unsigned long offset,
-				 unsigned long count, __u32 *buf);
-extern int nfs_proc_read_reply(struct rpc_ioreq *, struct nfs_fattr *);
-extern int *rpc_header(int *p, int procedure, int program, int version,
-				int uid, int gid, int ngroup, gid_t *groups);
-extern int *rpc_verify(int *p);
-
-/* linux/fs/nfs/sock.c */
-
-extern int nfs_rpc_call(struct nfs_server *server, int *start,
-				int *end, int size);
-extern int nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *,
-				int async);
+			struct nfs_fsinfo *res);
 
-/* linux/fs/nfs/inode.c */
 
+/*
+ * linux/fs/nfs/inode.c
+ */
 extern struct super_block *nfs_read_super(struct super_block *sb, 
 					  void *data,int);
 extern int init_nfs_fs(void);
 extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
 			       struct nfs_fattr *fattr);
 extern void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr);
+extern int nfs_revalidate(struct inode *);
+extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *);
 
-/* linux/fs/nfs/file.c */
-
+/*
+ * linux/fs/nfs/file.c
+ */
 extern struct inode_operations nfs_file_inode_operations;
 
-/* linux/fs/nfs/dir.c */
-
+/*
+ * linux/fs/nfs/dir.c
+ */
 extern struct inode_operations nfs_dir_inode_operations;
 extern void nfs_sillyrename_cleanup(struct inode *);
-extern void nfs_kfree_cache(void);
-
-/* linux/fs/nfs/symlink.c */
+extern void nfs_free_dircache(void);
+extern void nfs_invalidate_dircache(struct inode *);
 
+/*
+ * linux/fs/nfs/symlink.c
+ */
 extern struct inode_operations nfs_symlink_inode_operations;
 
-/* linux/fs/nfs/mmap.c */
+/*
+ * linux/fs/nfs/mmap.c
+ */
+extern int nfs_mmap(struct inode *inode, struct file *file,
+			struct vm_area_struct * vma);
 
-extern int nfs_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma);
+/*
+ * linux/fs/nfs/locks.c
+ */
+extern int nfs_lock(struct inode *inode, struct file *file,
+			int cmd, struct file_lock *fl);
 
-/* linux/fs/nfs/bio.c */
+/*
+ * linux/fs/nfs/write.c
+ */
+extern int  nfs_writepage(struct inode *, struct page *);
+extern int  nfs_check_error(struct inode *);
+extern int  nfs_flush_dirty_pages(struct inode *, off_t, off_t);
+extern int  nfs_truncate_dirty_pages(struct inode *, unsigned long);
+extern void nfs_invalidate_pages(struct inode *);
+extern int  nfs_updatepage(struct inode *, struct page *, const char *,
+			unsigned long, unsigned int, int);
+
+/*
+ * linux/fs/nfs/read.c
+ */
+extern int  nfs_readpage(struct inode *, struct page *);
+extern int  nfs_readpage_sync(struct inode *, struct page *);
 
-extern int nfs_readpage(struct inode *, struct page *);
+/*
+ * inline functions
+ */
+static inline int
+nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+{
+	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
+		return 0;
+	return _nfs_revalidate_inode(server, inode);
+}
+
+extern struct nfs_wreq *	nfs_failed_requests;
+static inline int
+nfs_write_error(struct inode *inode)
+{
+	if (nfs_failed_requests == NULL)
+		return 0;
+	return nfs_check_error(inode);
+}
 
 /* NFS root */
 
@@ -156,5 +221,26 @@
 extern char nfs_root_addrs[];
 
 #endif /* __KERNEL__ */
+
+/*
+ * NFS debug flags
+ */
+#define NFSDBG_VFS		0x0001
+#define NFSDBG_DIRCACHE		0x0002
+#define NFSDBG_LOOKUPCACHE	0x0004
+#define NFSDBG_PAGECACHE	0x0008
+#define NFSDBG_PROC		0x0010
+#define NFSDBG_XDR		0x0020
+#define NFSDBG_FILE		0x0040
+#define NFSDBG_ALL		0xFFFF
+
+#ifdef __KERNEL__
+# undef ifdebug
+# ifdef NFS_DEBUG
+#  define ifdebug(fac)		if (nfs_debug & NFSDBG_##fac)
+# else
+#  define ifdebug(fac)		if (0)
+# endif
+#endif /* __KERNEL */
 
 #endif
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- v2.1.31/linux/include/linux/nfs_fs_i.h	Fri Apr  4 08:52:25 1997
+++ linux/include/linux/nfs_fs_i.h	Fri Apr  4 11:15:59 1997
@@ -8,35 +8,75 @@
  * nfs fs inode data in memory
  */
 struct nfs_inode_info {
-	struct pipe_inode_info pipeinfo;
-	struct nfs_fh fhandle;
+	/*
+	 * This is a place holder so named pipes on NFS filesystems
+	 * work (more or less correctly). This must be first in the
+	 * struct because the data is really accessed via inode->u.pipe_i.
+	 */
+	struct pipe_inode_info	pipeinfo;
+
+	/*
+	 * The file handle
+	 */
+	struct nfs_fh		fhandle;
+
+	/*
+	 * Various flags
+	 */
+	unsigned short		flags;
+
 	/*
 	 * read_cache_jiffies is when we started read-caching this inode,
 	 * and read_cache_mtime is the mtime of the inode at that time.
+	 * attrtimeo is for how long the cached information is assumed
+	 * to be valid. A successful attribute revalidation doubles
+	 * attrtimeo (up to acregmax/acdirmax), a failure resets it to
+	 * acregmin/acdirmin.
+	 *
+	 * We need to revalidate the cached attrs for this inode if
 	 *
-	 * We need to invalidate the cache for this inode if
+	 *	jiffies - read_cache_jiffies > attrtimeo
+	 *
+	 * and invalidate any cached data/flush out any dirty pages if
+	 * we find that
 	 *
-	 *	jiffies - read_cache_jiffies > 30*HZ
-	 * AND
 	 *	mtime != read_cache_mtime
 	 */
-	unsigned long read_cache_jiffies;
-	unsigned long read_cache_mtime;
+	unsigned long		read_cache_jiffies;
+	unsigned long		read_cache_mtime;
+	unsigned long		attrtimeo;
+
 	/*
 	 * This is to support the clandestine rename on unlink.
-	 * Instead of the directory inode, we might as well keep its
-	 * NFS FH, but that requires a kmalloc.
+	 * Instead of the directory inode, we might as well keep
+	 * its NFS FH, but that requires a kmalloc.
 	 */
-	struct inode *silly_rename_dir;
+	struct inode *		silly_inode;
+
 	/*
-	 * attrtimeo defines for how long the cached attributes are valid
+	 * This is the list of dirty unwritten pages.
+	 * NFSv3 will want to add a list for written but uncommitted
+	 * pages.
 	 */
-	unsigned long attrtimeo;
+	struct nfs_wreq *	writeback;
 };
 
+/*
+ * Legal inode flag values
+ */
+#define NFS_INO_REVALIDATE	0x0001		/* revalidating attrs */
+
+/*
+ * NFS lock info
+ */
 struct nfs_lock_info {
 	u32		state;
-	unsigned int	flags;
+	u32		flags;
 };
+
+/*
+ * Lock flag values
+ */
+#define NFS_LCK_GRANTED		0x0001		/* lock has been granted */
 
 #endif
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_fs_sb.h linux/include/linux/nfs_fs_sb.h
--- v2.1.31/linux/include/linux/nfs_fs_sb.h	Thu Dec 12 07:12:17 1996
+++ linux/include/linux/nfs_fs_sb.h	Fri Apr  4 12:15:40 1997
@@ -1,34 +1,32 @@
 #ifndef _NFS_FS_SB
 #define _NFS_FS_SB
 
-#include <linux/rpcsock.h>
 #include <linux/nfs.h>
+#include <linux/in.h>
 
+/*
+ * NFS client parameters
+ * Part of this is duplicated in rpc_clnt and is therefore obsolete.
+ */
 struct nfs_server {
-	struct file *file;
-	struct rpc_sock *rsock;
-	struct sockaddr toaddr ;  /* Added for change to NFS code to use sendto()  1995-06-02  JSP */
-	int lock;
-	struct wait_queue *wait;
-	int flags;
-	int rsize;
-	int wsize;
-	int timeo;
-	int retrans;
-	int acregmin;
-	int acregmax;
-	int acdirmin;
-	int acdirmax;
-	char hostname[256];
+	struct rpc_clnt *	client;		/* RPC client handle */
+	int			flags;		/* various flags */
+	int			rsize;		/* read size */
+	int			wsize;		/* write size */
+	unsigned int		bsize;		/* server block size */
+	unsigned int		acregmin;	/* attr cache timeouts */
+	unsigned int		acregmax;
+	unsigned int		acdirmin;
+	unsigned int		acdirmax;
+	char			hostname[256];	/* remote hostname */
 };
 
 /*
  * nfs super-block data in memory
  */
-
 struct nfs_sb_info {
-	struct nfs_server s_server;
-	struct nfs_fh s_root;
+	struct nfs_server	s_server;
+	struct nfs_fh		s_root;
 };
 
 #endif
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfs_mount.h linux/include/linux/nfs_mount.h
--- v2.1.31/linux/include/linux/nfs_mount.h	Tue Nov 14 06:03:57 1995
+++ linux/include/linux/nfs_mount.h	Sat Feb 22 06:52:10 1997
@@ -16,36 +16,25 @@
  * mount-to-kernel version compatibility.  Some of these aren't used yet
  * but here they are anyway.
  */
-
-#define NFS_NFS_PROGRAM		100003	/* nfsd program number */
-#define NFS_NFS_VERSION		2	/* nfsd version */
-#define NFS_NFS_PORT		2049	/* portnumber on server for nfsd */
-
-#define NFS_MOUNT_PROGRAM	100005	/* mountd program number */
-#define NFS_MOUNT_VERSION	1	/* mountd version */
-#define NFS_MOUNT_PROC		1	/* mount process id */
-#define NFS_MOUNT_PORT		627	/* portnumber on server for mountd */
-
-#define NFS_PMAP_PROGRAM	100000	/* portmap program number */
-#define NFS_PMAP_VERSION	2	/* portmap version */
-#define NFS_PMAP_PROC		3	/* portmap getport id */
-#define NFS_PMAP_PORT		111	/* portnumber on server for portmap */
+#define NFS_MOUNT_VERSION	3
 
 struct nfs_mount_data {
-	int version;			/* 1 */
-	int fd;				/* 1 */
-	struct nfs_fh root;		/* 1 */
-	int flags;			/* 1 */
-	int rsize;			/* 1 */
-	int wsize;			/* 1 */
-	int timeo;			/* 1 */
-	int retrans;			/* 1 */
-	int acregmin;			/* 1 */
-	int acregmax;			/* 1 */
-	int acdirmin;			/* 1 */
-	int acdirmax;			/* 1 */
-	struct sockaddr_in addr;	/* 1 */
-	char hostname[256];		/* 1 */
+	int		version;		/* 1 */
+	int		fd;			/* 1 */
+	struct nfs_fh	root;			/* 1 */
+	int		flags;			/* 1 */
+	int		rsize;			/* 1 */
+	int		wsize;			/* 1 */
+	int		timeo;			/* 1 */
+	int		retrans;		/* 1 */
+	int		acregmin;		/* 1 */
+	int		acregmax;		/* 1 */
+	int		acdirmin;		/* 1 */
+	int		acdirmax;		/* 1 */
+	struct sockaddr_in addr;		/* 1 */
+	char		hostname[256];		/* 1 */
+	int		namlen;			/* 2 */
+	unsigned int	bsize;			/* 3 */
 };
 
 /* bits in the flags field */
@@ -56,5 +45,9 @@
 #define NFS_MOUNT_POSIX		0x0008	/* 1 */
 #define NFS_MOUNT_NOCTO		0x0010	/* 1 */
 #define NFS_MOUNT_NOAC		0x0020	/* 1 */
+#define NFS_MOUNT_TCP		0x0040	/* 2 */
+#define NFS_MOUNT_VER3		0x0080	/* 3 */
+#define NFS_MOUNT_KERBEROS	0x0100	/* 3 */
+#define NFS_MOUNT_NONLM		0x0200	/* 3 */
  
 #endif
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/auth.h linux/include/linux/nfsd/auth.h
--- v2.1.31/linux/include/linux/nfsd/auth.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/auth.h	Mon Oct 28 06:51:25 1996
@@ -0,0 +1,45 @@
+/*
+ * include/linux/nfsd/auth.h
+ *
+ * nfsd-specific authentication stuff.
+ * uid/gid mapping not yet implemented.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_NFSD_AUTH_H
+#define LINUX_NFSD_AUTH_H
+
+#ifdef __KERNEL__
+
+#define nfsd_luid(rq, uid)	((u32)(uid))
+#define nfsd_lgid(rq, gid)	((u32)(gid))
+#define nfsd_ruid(rq, uid)	((u32)(uid))
+#define nfsd_rgid(rq, gid)	((u32)(gid))
+
+/*
+ * Set the current process's fsuid/fsgid etc to those of the NFS
+ * client user
+ */
+void		nfsd_setuser(struct svc_rqst *, struct svc_export *);
+
+#if 0
+/*
+ * These must match the actual size of uid_t and gid_t
+ */
+#define UGID_BITS		(8 * sizeof(uid_t))
+#define UGID_SHIFT		8
+#define UGID_MASK		((1 << UGID_SHIFT) - 1)
+#define UGID_NRENTRIES		((1 << (UGID_BITS - UGID_SHIFT)) + 1)
+#define UGID_NONE		((unsigned short)-1)
+
+typedef struct svc_uidmap {
+	uid_t *			um_ruid[UGID_NRENTRIES];
+	uid_t *			um_luid[UGID_NRENTRIES];
+	gid_t *			um_rgid[UGID_NRENTRIES];
+	gid_t *			um_lgid[UGID_NRENTRIES];
+} svc_uidmap;
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* LINUX_NFSD_AUTH_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/cache.h linux/include/linux/nfsd/cache.h
--- v2.1.31/linux/include/linux/nfsd/cache.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/cache.h	Sun Dec 15 05:44:27 1996
@@ -0,0 +1,79 @@
+/*
+ * include/linux/nfsd/cache.h
+ *
+ * Request reply cache. This was heavily inspired by the
+ * implementation in 4.3BSD/4.4BSD.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef NFSCACHE_H
+#define NFSCACHE_H
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+
+/*
+ * Representation of a reply cache entry. The first two members *must*
+ * be hash_next and hash_prev.
+ */
+struct svc_cacherep {
+	struct svc_cacherep *	c_hash_next;
+	struct svc_cacherep *	c_hash_prev;
+	struct svc_cacherep *	c_lru_next;
+	struct svc_cacherep *	c_lru_prev;
+	unsigned char		c_state,	/* unused, inprog, done */
+				c_type,		/* status, buffer */
+				c_secure : 1;	/* req came from port < 1024 */
+	struct in_addr		c_client;
+	u32			c_xid;
+	u32			c_proc;
+	unsigned long		c_timestamp;
+	union {
+		struct svc_buf	u_buffer;
+		u32		u_status;
+	}			c_u;
+};
+
+#define c_replbuf		c_u.u_buffer
+#define c_replstat		c_u.u_status
+
+/* cache entry states */
+enum {
+	RC_UNUSED,
+	RC_INPROG,
+	RC_DONE
+};
+
+/* return values */
+enum {
+	RC_DROPIT,
+	RC_REPLY,
+	RC_DOIT,
+	RC_INTR
+};
+
+/*
+ * Cache types.
+ * We may want to add more types one day, e.g. for diropres and
+ * attrstat replies. Using cache entries with fixed length instead
+ * of buffer pointers may be more efficient.
+ */
+enum {
+	RC_NOCACHE,
+	RC_REPLSTAT,
+	RC_REPLBUFF,
+};
+
+/*
+ * If requests are retransmitted within this interval, they're dropped.
+ */
+#define RC_DELAY		(HZ/5)
+
+void	nfsd_cache_init(void);
+void	nfsd_cache_shutdown(void);
+int	nfsd_cache_lookup(struct svc_rqst *, int);
+void	nfsd_cache_update(struct svc_rqst *, int, u32 *);
+
+#endif /* __KERNEL__ */
+#endif /* NFSCACHE_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/const.h linux/include/linux/nfsd/const.h
--- v2.1.31/linux/include/linux/nfsd/const.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/const.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,86 @@
+/*
+ * include/linux/nfsd/nfsconst.h
+ *
+ * Various constants related to NFS.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef __NFSCONST_H__
+#define __NFSCONST_H__
+
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+#include <linux/dirent.h>
+#include <linux/fs.h>
+#include <linux/nfs.h>
+
+/*
+ * Maximum protocol version supported by knfsd
+ */
+#define NFSSVC_MAXVERS		3
+
+/*
+ * Maximum blocksize supported by daemon currently at 8K
+ */
+#define NFSSVC_MAXBLKSIZE	8192
+
+#define NFS2_MAXPATHLEN		1024
+#define NFS2_MAXNAMLEN		255
+#define NFS2_FHSIZE		NFS_FHSIZE
+#define NFS2_COOKIESIZE		4
+
+#define NFS3_MAXPATHLEN		PATH_MAX
+#define NFS3_MAXNAMLEN		NAME_MAX
+#define NFS3_FHSIZE		NFS_FHSIZE
+#define NFS3_COOKIEVERFSIZE	8
+#define NFS3_CREATEVERFSIZE	8
+#define NFS3_WRITEVERFSIZE	8
+
+#ifdef __KERNEL__
+
+#ifndef NFS_SUPER_MAGIC
+# define NFS_SUPER_MAGIC	0x6969
+#endif
+
+/*
+ * NFS stats. The good thing with these values is that NFSv3 errors are
+ * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
+ * no-one uses anyway), so we can happily mix code as long as we make sure
+ * no NFSv3 errors are returned to NFSv2 clients.
+ */
+#define NFS_OK			0		/* v2 v3 */
+#define NFSERR_PERM		1		/* v2 v3 */
+#define NFSERR_NOENT		2		/* v2 v3 */
+#define NFSERR_IO		5		/* v2 v3 */
+#define NFSERR_NXIO		6		/* v2 v3 */
+#define NFSERR_ACCES		13		/* v2 v3 */
+#define NFSERR_EXIST		17		/* v2 v3 */
+#define NFSERR_XDEV		18		/*    v3 */
+#define NFSERR_NODEV		19		/* v2 v3 */
+#define NFSERR_NOTDIR		20		/* v2 v3 */
+#define NFSERR_ISDIR		21		/* v2 v3 */
+#define NFSERR_INVAL		22		/*    v3 */
+#define NFSERR_FBIG		27		/* v2 v3 */
+#define NFSERR_NOSPC		28		/* v2 v3 */
+#define NFSERR_ROFS		30		/* v2 v3 */
+#define NFSERR_MLINK		31		/*    v3 */
+#define NFSERR_NAMETOOLONG	63		/* v2 v3 */
+#define NFSERR_NOTEMPTY		66		/* v2 v3 */
+#define NFSERR_DQUOT		69		/* v2 v3 */
+#define NFSERR_STALE		70		/* v2 v3 */
+#define NFSERR_REMOTE		71		/*    v3 */
+#define NFSERR_WFLUSH		99		/* v2    */
+#define NFSERR_BADHANDLE	10001		/*    v3 */
+#define NFSERR_NOT_SYNC		10002		/*    v3 */
+#define NFSERR_BAD_COOKIE	10003		/*    v3 */
+#define NFSERR_NOTSUPP		10004		/*    v3 */
+#define NFSERR_TOOSMALL		10005		/*    v3 */
+#define NFSERR_SERVERFAULT	10006		/*    v3 */
+#define NFSERR_BADTYPE		10007		/*    v3 */
+#define NFSERR_JUKEBOX		10008		/*    v3 */
+
+#endif /* __KERNEL__ */
+
+#endif /* __NFSCONST_H__ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/debug.h linux/include/linux/nfsd/debug.h
--- v2.1.31/linux/include/linux/nfsd/debug.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/debug.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,48 @@
+/*
+ * linux/include/linux/nfsd/debug.h
+ *
+ * Debugging-related stuff for nfsd
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_NFSD_DEBUG_H
+#define LINUX_NFSD_DEBUG_H
+
+#include <linux/sunrpc/debug.h>
+
+/*
+ * Enable debugging for nfsd.
+ * Requires RPC_DEBUG.
+ */
+#ifdef RPC_DEBUG
+# define NFSD_DEBUG		1
+#endif
+
+/*
+ * knfsd debug flags
+ */
+#define NFSDDBG_SOCK		0x0001
+#define NFSDDBG_FH		0x0002
+#define NFSDDBG_EXPORT		0x0004
+#define NFSDDBG_SVC		0x0008
+#define NFSDDBG_PROC		0x0010
+#define NFSDDBG_FILEOP		0x0020
+#define NFSDDBG_AUTH		0x0040
+#define NFSDDBG_REPCACHE	0x0080
+#define NFSDDBG_XDR		0x0100
+#define NFSDDBG_LOCKD		0x0200
+#define NFSDDBG_ALL		0x7FFF
+#define NFSDDBG_NOCHANGE	0xFFFF
+
+
+#ifdef __KERNEL__
+# undef ifdebug
+# ifdef NFSD_DEBUG
+#  define ifdebug(flag)		if (nfsd_debug & NFSDDBG_##flag)
+# else
+#  define ifdebug(flag)		if (0)
+# endif
+#endif /* __KERNEL__ */
+
+#endif /* LINUX_NFSD_DEBUG_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/export.h linux/include/linux/nfsd/export.h
--- v2.1.31/linux/include/linux/nfsd/export.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/export.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,109 @@
+/*
+ * include/linux/nfsd/export.h
+ * 
+ * Public declarations for NFS exports. The definitions for the
+ * syscall interface are in nfsctl.h
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef NFSD_EXPORT_H
+#define NFSD_EXPORT_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+
+/*
+ * Important limits for the exports stuff.
+ */
+#define NFSCLNT_IDMAX		1024
+#define NFSCLNT_ADDRMAX		16
+#define NFSCLNT_KEYMAX		32
+
+/*
+ * Export flags.
+ */
+#define NFSEXP_READONLY		0x0001
+#define NFSEXP_INSECURE_PORT	0x0002
+#define NFSEXP_ROOTSQUASH	0x0004
+#define NFSEXP_ALLSQUASH	0x0008
+#define NFSEXP_ASYNC		0x0010
+#define NFSEXP_GATHERED_WRITES	0x0020
+#define NFSEXP_UIDMAP		0x0040
+#define NFSEXP_KERBEROS		0x0080		/* not available */
+#define NFSEXP_SUNSECURE	0x0100
+#define NFSEXP_CROSSMNT		0x0200		/* not available */
+#define NFSEXP_ALLFLAGS		0x03FF
+
+
+#ifdef __KERNEL__
+
+/* The following are hashtable sizes and must be powers of 2 */
+#define NFSCLNT_EXPMAX		16
+
+struct svc_client {
+	struct svc_client *	cl_next;
+	char			cl_ident[NFSCLNT_IDMAX];
+	int			cl_idlen;
+	int			cl_naddr;
+	struct in_addr		cl_addr[NFSCLNT_ADDRMAX];
+	struct svc_uidmap *	cl_umap;
+	struct svc_export *	cl_export[NFSCLNT_EXPMAX];
+};
+
+struct svc_export {
+	struct svc_export *	ex_next;
+	char			ex_path[NFS_MAXPATHLEN+1];
+	struct svc_export *	ex_parent;
+	struct svc_client *	ex_client;
+	int			ex_flags;
+	struct inode *		ex_inode;
+	dev_t			ex_dev;
+	ino_t			ex_ino;
+	uid_t			ex_anon_uid;
+	gid_t			ex_anon_gid;
+};
+
+#define EX_SECURE(exp)		(!((exp)->ex_flags & NFSEXP_INSECURE_PORT))
+#define EX_ISSYNC(exp)		(!((exp)->ex_flags & NFSEXP_ASYNC))
+#define EX_RDONLY(exp)		((exp)->ex_flags & NFSEXP_READONLY)
+#define EX_CROSSMNT(exp)	((exp)->ex_flags & NFSEXP_CROSSMNT)
+#define EX_SUNSECURE(exp)	((exp)->ex_flags & NFSEXP_SUNSECURE)
+#define EX_WGATHER(exp)		((exp)->ex_flags & NFSEXP_GATHERED_WRITES)
+
+
+/*
+ * Function declarations
+ */
+void			nfsd_export_init(void);
+void			nfsd_export_shutdown(void);
+void			exp_readlock(void);
+int			exp_writelock(void);
+void			exp_unlock(void);
+struct svc_client *	exp_getclient(struct sockaddr_in *sin);
+void			exp_putclient(struct svc_client *clp);
+struct svc_export *	exp_get(struct svc_client *clp, dev_t dev, ino_t ino);
+int			exp_rootfh(struct svc_client *, dev_t, ino_t,
+					struct knfs_fh *);
+int			nfserrno(int errno);
+void			exp_nlmdetach(void);
+
+
+extern __inline__ int
+exp_checkaddr(struct svc_client *clp, struct in_addr addr)
+{
+	struct in_addr	*ap = clp->cl_addr;
+	int		i;
+
+	for (i = clp->cl_naddr; i--; ap++)
+		if (ap->s_addr == addr.s_addr)
+			return 1;
+	return 0;
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* NFSD_EXPORT_H */
+
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/nfsd.h linux/include/linux/nfsd/nfsd.h
--- v2.1.31/linux/include/linux/nfsd/nfsd.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/nfsd.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,169 @@
+/*
+ * linux/include/linux/nfsd/nfsd.h
+ *
+ * Hodge-podge collection of knfsd-related stuff.
+ * I will sort this out later.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_NFSD_NFSD_H
+#define LINUX_NFSD_NFSD_H
+
+#include <linux/types.h>
+#include <linux/unistd.h>
+#include <linux/dirent.h>
+#include <linux/fs.h>
+
+#include <linux/nfsd/debug.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/export.h>
+#include <linux/nfsd/auth.h>
+#include <linux/nfsd/stats.h>
+
+/*
+ * nfsd version
+ */
+#define NFSD_VERSION		"0.4"
+
+/*
+ * Maximum number of nfsd processes
+ */
+#define NFSD_MAXSERVS		16
+
+#ifdef __KERNEL__
+/*
+ * Special flags for nfsd_permission. These must be different from MAY_READ,
+ * MAY_WRITE, and MAY_EXEC.
+ */
+#define MAY_NOP			0
+#define MAY_SATTR		8
+#define MAY_TRUNC		16
+#if (MAY_SATTR | MAY_TRUNC) & (MAY_READ | MAY_WRITE | MAY_EXEC)
+# error "please use a different value for MAY_SATTR or MAY_TRUNC."
+#endif
+#define MAY_CREATE		(MAY_EXEC|MAY_WRITE)
+#define MAY_REMOVE		(MAY_EXEC|MAY_WRITE|MAY_TRUNC)
+
+/*
+ * Callback function for readdir
+ */
+struct readdir_cd {
+	struct svc_rqst *	rqstp;
+	struct svc_fh *		dirfh;
+	u32 *			buffer;
+	int			buflen;
+	u32 *			offset;		/* previous dirent->d_next */
+	char			plus;		/* readdirplus */
+	char			eob;		/* end of buffer */
+	char			dotonly;
+};
+typedef int		(*encode_dent_fn)(struct readdir_cd *, const char *,
+						int, off_t, ino_t);
+
+/*
+ * Procedure table for NFSv2
+ */
+extern struct svc_procedure	nfsd_procedures2[];
+extern struct svc_program	nfsd_program;
+
+/*
+ * Function prototypes.
+ */
+int		nfsd_svc(unsigned short port, int nrservs);
+
+void		nfsd_racache_init(void);
+int		nfsd_lookup(struct svc_rqst *, struct svc_fh *,
+				const char *, int, struct svc_fh *);
+int		nfsd_setattr(struct svc_rqst *, struct svc_fh *,
+				struct iattr *);
+int		nfsd_create(struct svc_rqst *, struct svc_fh *,
+				char *name, int len, struct iattr *attrs,
+				int type, dev_t rdev, struct svc_fh *res);
+int		nfsd_open(struct svc_rqst *, struct svc_fh *, int,
+				int, struct file *);
+void		nfsd_close(struct file *);
+int		nfsd_read(struct svc_rqst *, struct svc_fh *,
+				loff_t, char *, unsigned long *);
+int		nfsd_write(struct svc_rqst *, struct svc_fh *,
+				loff_t, char *, unsigned long, int);
+int		nfsd_readlink(struct svc_rqst *, struct svc_fh *,
+				char *, int *);
+int		nfsd_symlink(struct svc_rqst *, struct svc_fh *,
+				char *name, int len, char *path, int plen,
+				struct svc_fh *res);
+int		nfsd_link(struct svc_rqst *, struct svc_fh *,
+				char *, int, struct svc_fh *);
+int		nfsd_rename(struct svc_rqst *,
+				struct svc_fh *, char *, int,
+				struct svc_fh *, char *, int);
+int		nfsd_remove(struct svc_rqst *,
+				struct svc_fh *, char *, int);
+int		nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type,
+				char *name, int len);
+int		nfsd_truncate(struct svc_rqst *, struct svc_fh *,
+				unsigned long size);
+int		nfsd_readdir(struct svc_rqst *, struct svc_fh *,
+				loff_t, encode_dent_fn,
+				u32 *buffer, int *countp);
+int		nfsd_statfs(struct svc_rqst *, struct svc_fh *,
+				struct statfs *);
+int		nfsd_notify_change(struct inode *, struct iattr *);
+int		nfsd_permission(struct svc_export *, struct inode *, int);
+void		nfsd_write_inode(struct inode *);
+struct inode *	nfsd_iget(dev_t dev, ino_t ino);
+int		nfsd_parentdev(dev_t *devp);
+
+/*
+ * lockd binding
+ */
+void		nfsd_lockd_init(void);
+void		nfsd_lockd_shutdown(void);
+void		nfsd_lockd_unexport(struct svc_client *);
+
+
+#ifndef makedev
+#define makedev(maj, min)	(((maj) << 8) | (min))
+#endif
+
+/*
+ * These variables contain pre-xdr'ed values for faster operation.
+ * FIXME: should be replaced by macros for big-endian machines.
+ */
+extern u32	nfs_ok,
+		nfserr_perm,
+		nfserr_noent,
+		nfserr_io,
+		nfserr_nxio,
+		nfserr_acces,
+		nfserr_exist,
+		nfserr_xdev,
+		nfserr_nodev,
+		nfserr_notdir,
+		nfserr_isdir,
+		nfserr_inval,
+		nfserr_fbig,
+		nfserr_nospc,
+		nfserr_rofs,
+		nfserr_mlink,
+		nfserr_nametoolong,
+		nfserr_dquot,
+		nfserr_stale,
+		nfserr_remote,
+		nfserr_badhandle,
+		nfserr_notsync,
+		nfserr_badcookie,
+		nfserr_notsupp,
+		nfserr_toosmall,
+		nfserr_serverfault,
+		nfserr_badtype,
+		nfserr_jukebox;
+
+/*
+ * Time of server startup
+ */
+extern struct timeval	nfssvc_boot;
+
+#endif /* __KERNEL__ */
+
+#endif /* LINUX_NFSD_NFSD_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- v2.1.31/linux/include/linux/nfsd/nfsfh.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/nfsfh.h	Fri Apr  4 12:19:14 1997
@@ -0,0 +1,170 @@
+/*
+ * include/linux/nfsd/nfsfh.h
+ *
+ * This file describes the layout of the file handles as passed
+ * over the wire.
+ *
+ * Earlier versions of knfsd used to sign file handles using keyed MD5
+ * or SHA. I've removed this code, because it doesn't give you more
+ * security than blocking external access to port 2049 on your firewall.
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef NFSD_FH_H
+#define NFSD_FH_H
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/nfsd/const.h>
+#include <linux/nfsd/debug.h>
+
+/*
+ * This is our NFSv2 file handle.
+ *
+ * The xdev and xino fields are currently used to transport the dev/ino
+ * of the exported inode. The xdev field is redundant, though, because
+ * we do not allow mount point crossing.
+ */
+struct nfs_fhbase {
+	dev_t			fb_dev;
+	dev_t			fb_xdev;
+	ino_t			fb_ino;
+	ino_t			fb_xino;
+	__u32			fb_version;
+};
+
+#define NFS_FH_PADDING		(NFS_FHSIZE - sizeof(struct nfs_fhbase))
+struct knfs_fh {
+	struct nfs_fhbase	fh_base;
+	__u8			fh_cookie[NFS_FH_PADDING];
+};
+
+#define fh_dev			fh_base.fb_dev
+#define fh_xdev			fh_base.fb_xdev
+#define fh_ino			fh_base.fb_ino
+#define fh_xino			fh_base.fb_xino
+#define fh_version		fh_base.fb_version
+
+
+#ifdef __KERNEL__
+
+/*
+ * This is the internal representation of an NFS handle used in knfsd.
+ * pre_mtime/post_version will be used to support wcc_attr's in NFSv3.
+ */
+typedef struct svc_fh {
+	struct knfs_fh		fh_handle;	/* FH data */
+	struct svc_export *	fh_export;	/* export pointer */
+	struct inode *		fh_inode;	/* inode */
+	size_t			fh_pre_size;	/* size before operation */
+	time_t			fh_pre_mtime;	/* mtime before oper */
+	time_t			fh_pre_ctime;	/* ctime before oper */
+	unsigned long		fh_post_version;/* inode version after oper */
+	unsigned char		fh_locked;	/* inode locked by us */
+} svc_fh;
+
+/*
+ * Shorthands for dprintk()'s
+ */
+#define SVCFH_INO(f)		((f)->fh_handle.fh_ino)
+#define SVCFH_DEV(f)		((f)->fh_handle.fh_dev)
+
+/*
+ * Function prototypes
+ */
+u32             fh_lookup(struct svc_rqst *, struct svc_fh *, int, int);
+void            fh_compose(struct svc_fh *, struct svc_export *,
+                                struct inode *);
+
+static __inline__ struct svc_fh *
+fh_copy(struct svc_fh *dst, struct svc_fh *src)
+{
+	*dst = *src;
+	return dst;
+}
+
+static __inline__ struct svc_fh *
+fh_init(struct svc_fh *fhp)
+{
+	memset(fhp, 0, sizeof(*fhp));
+	return fhp;
+}
+
+/*
+ * Lock a file handle/inode
+ */
+static inline void
+fh_lock(struct svc_fh *fhp)
+{
+	struct inode	*inode = fhp->fh_inode;
+
+	/*
+	dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
+			SVCFH_DEV(fhp), SVCFH_INO(fhp), fhp->fh_locked);
+	 */
+	if (!fhp->fh_locked) {
+		down(&inode->i_sem);
+		if (!fhp->fh_pre_mtime)
+			fhp->fh_pre_mtime = inode->i_mtime;
+		fhp->fh_locked = 1;
+	}
+}
+
+/*
+ * Unlock a file handle/inode
+ */
+static inline void
+fh_unlock(struct svc_fh *fhp)
+{
+	struct inode	*inode = fhp->fh_inode;
+
+	if (fhp->fh_locked) {
+		if (!fhp->fh_post_version)
+			fhp->fh_post_version = inode->i_version;
+		fhp->fh_locked = 0;
+		up(&inode->i_sem);
+	}
+}
+
+/*
+ * Release an inode
+ */
+#ifndef NFSD_DEBUG
+static inline void
+fh_put(struct svc_fh *fhp)
+{
+	if (fhp->fh_inode) {
+		fh_unlock(fhp);
+		iput(fhp->fh_inode);
+	}
+}
+#else
+#define fh_put(fhp)	__fh_put(fhp, __FILE__, __LINE__)
+
+static inline void
+__fh_put(struct svc_fh *fhp, char *file, int line)
+{
+	struct inode	*inode;
+
+	if (!(inode = fhp->fh_inode))
+		return;
+
+	if (!inode->i_count) {
+		printk("nfsd: trying to free free inode in %s:%d\n"
+		       "      dev %04x ino %ld, mode %07o\n",
+		       file, line, inode->i_dev,
+		       inode->i_ino, inode->i_mode);
+	} else {
+		fh_unlock(fhp);
+		iput(inode);
+	}
+}
+#endif
+
+
+
+#endif /* __KERNEL__ */
+
+#endif /* NFSD_FH_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/stats.h linux/include/linux/nfsd/stats.h
--- v2.1.31/linux/include/linux/nfsd/stats.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/stats.h	Sun Dec 15 05:45:08 1996
@@ -0,0 +1,27 @@
+/*
+ * linux/include/nfsd/stats.h
+ *
+ * Statistics for NFS server.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_NFSD_STATS_H
+#define LINUX_NFSD_STATS_H
+
+struct nfsd_stats {
+	unsigned int	rchits;		/* repcache hits */
+	unsigned int	rcmisses;	/* repcache hits */
+	unsigned int	rcnocache;	/* uncached reqs */
+};
+
+#ifdef __KERNEL__
+
+extern struct nfsd_stats	nfsdstats;
+extern struct svc_stat		nfsd_svcstats;
+
+void	nfsd_stat_init(void);
+void	nfsd_stat_shutdown(void);
+
+#endif /* __KERNEL__ */
+#endif /* LINUX_NFSD_STATS_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h
--- v2.1.31/linux/include/linux/nfsd/syscall.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/syscall.h	Mon Mar  3 01:14:58 1997
@@ -0,0 +1,127 @@
+/*
+ * include/linux/nfsd/syscall.h
+ *
+ * This file holds all declarations for the knfsd syscall interface.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef NFSD_SYSCALL_H
+#define NFSD_SYSCALL_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/nfsd/const.h>
+#include <linux/nfsd/export.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/auth.h>
+
+/*
+ * Version of the syscall interface
+ */
+#define NFSCTL_VERSION		0x0201
+
+/*
+ * These are the commands understood by nfsctl().
+ */
+#define NFSCTL_SVC		0	/* This is a server process. */
+#define NFSCTL_ADDCLIENT	1	/* Add an NFS client. */
+#define NFSCTL_DELCLIENT	2	/* Remove an NFS client. */
+#define NFSCTL_EXPORT		3	/* export a file system. */
+#define NFSCTL_UNEXPORT		4	/* unexport a file system. */
+#define NFSCTL_UGIDUPDATE	5	/* update a client's uid/gid map. */
+#define NFSCTL_GETFH		6	/* get an fh (used by mountd) */
+
+
+/* SVC */
+struct nfsctl_svc {
+	unsigned short		svc_port;
+	int			svc_nthreads;
+};
+
+/* ADDCLIENT/DELCLIENT */
+struct nfsctl_client {
+	char			cl_ident[NFSCLNT_IDMAX+1];
+	int			cl_naddr;
+	struct in_addr		cl_addrlist[NFSCLNT_ADDRMAX];
+	int			cl_fhkeytype;
+	int			cl_fhkeylen;
+	unsigned char		cl_fhkey[NFSCLNT_KEYMAX];
+};
+
+/* EXPORT/UNEXPORT */
+struct nfsctl_export {
+	char			ex_client[NFSCLNT_IDMAX+1];
+	char			ex_path[NFS_MAXPATHLEN+1];
+	dev_t			ex_dev;
+	ino_t			ex_ino;
+	int			ex_flags;
+	uid_t			ex_anon_uid;
+	gid_t			ex_anon_gid;
+};
+
+/* UGIDUPDATE */
+struct nfsctl_uidmap {
+	char *			ug_ident;
+	uid_t			ug_uidbase;
+	int			ug_uidlen;
+	uid_t *			ug_udimap;
+	uid_t			ug_gidbase;
+	int			ug_gidlen;
+	gid_t *			ug_gdimap;
+};
+
+/* GETFH */
+struct nfsctl_fhparm {
+	struct sockaddr		gf_addr;
+	dev_t			gf_dev;
+	ino_t			gf_ino;
+	int			gf_version;
+};
+
+/*
+ * This is the argument union.
+ */
+struct nfsctl_arg {
+	int			ca_version;	/* safeguard */
+	union {
+		struct nfsctl_svc	u_svc;
+		struct nfsctl_client	u_client;
+		struct nfsctl_export	u_export;
+		struct nfsctl_uidmap	u_umap;
+		struct nfsctl_fhparm	u_getfh;
+		unsigned int		u_debug;
+	} u;
+#define ca_svc		u.u_svc
+#define ca_client	u.u_client
+#define ca_export	u.u_export
+#define ca_umap		u.u_umap
+#define ca_getfh	u.u_getfh
+#define ca_authd	u.u_authd
+#define ca_debug	u.u_debug
+};
+
+union nfsctl_res {
+	struct knfs_fh		cr_getfh;
+	unsigned int		cr_debug;
+};
+
+#ifdef __KERNEL__
+/*
+ * Kernel syscall implementation.
+ */
+#ifdef CONFIG_NFSD
+extern asmlinkage int	sys_nfsservctl(int, struct nfsctl_arg *,
+					    union nfsctl_res *);
+#else
+# define sys_nfsservctl	sys_ni_syscall
+#endif
+extern int		exp_addclient(struct nfsctl_client *ncp);
+extern int		exp_delclient(struct nfsctl_client *ncp);
+extern int		exp_export(struct nfsctl_export *nxp);
+extern int		exp_unexport(struct nfsctl_export *nxp);
+
+#endif /* __KERNEL__ */
+
+#endif /* NFSD_SYSCALL_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/xdr.h linux/include/linux/nfsd/xdr.h
--- v2.1.31/linux/include/linux/nfsd/xdr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/xdr.h	Fri Mar 14 05:04:32 1997
@@ -0,0 +1,158 @@
+/*
+ * linux/inxlude/linux/nfsd/xdr.h
+ *
+ * XDR types for nfsd. This is mainly a typing exercise.
+ */
+
+#ifndef LINUX_NFSD_H
+#define LINUX_NFSD_H
+
+#include <linux/fs.h>
+#include <linux/nfs.h>
+
+struct nfsd_fhandle {
+	struct svc_fh		fh;
+};
+
+struct nfsd_sattrargs {
+	struct svc_fh		fh;
+	struct iattr		attrs;
+};
+
+struct nfsd_diropargs {
+	struct svc_fh		fh;
+	char *			name;
+	int			len;
+};
+
+struct nfsd_readargs {
+	struct svc_fh		fh;
+	__u32			offset;
+	__u32			count;
+	__u32			totalsize;
+};
+
+struct nfsd_writeargs {
+	svc_fh			fh;
+	__u32			beginoffset;
+	__u32			offset;
+	__u32			totalcount;
+	__u8 *			data;
+	int			len;
+};
+
+struct nfsd_createargs {
+	struct svc_fh		fh;
+	char *			name;
+	int			len;
+	struct iattr		attrs;
+};
+
+struct nfsd_renameargs {
+	struct svc_fh		ffh;
+	char *			fname;
+	int			flen;
+	struct svc_fh		tfh;
+	char *			tname;
+	int			tlen;
+};
+
+struct nfsd_linkargs {
+	struct svc_fh		ffh;
+	struct svc_fh		tfh;
+	char *			tname;
+	int			tlen;
+};
+
+struct nfsd_symlinkargs {
+	struct svc_fh		ffh;
+	char *			fname;
+	int			flen;
+	char *			tname;
+	int			tlen;
+	struct iattr		attrs;
+};
+
+struct nfsd_readdirargs {
+	struct svc_fh		fh;
+	__u32			cookie;
+	__u32			count;
+};
+
+struct nfsd_attrstat {
+	struct svc_fh		fh;
+};
+
+struct nfsd_diropres  {
+	struct svc_fh		fh;
+};
+
+struct nfsd_readlinkres {
+	int			len;
+};
+
+struct nfsd_readres {
+	struct svc_fh		fh;
+	unsigned long		count;
+};
+
+struct nfsd_readdirres {
+	int			count;
+};
+
+struct nfsd_statfsres {
+	struct statfs		stats;
+};
+
+/*
+ * Storage requirements for XDR arguments and results.
+ */
+union nfsd_xdrstore {
+	struct nfsd_sattrargs	sattr;
+	struct nfsd_diropargs	dirop;
+	struct nfsd_readargs	read;
+	struct nfsd_writeargs	write;
+	struct nfsd_createargs	create;
+	struct nfsd_renameargs	rename;
+	struct nfsd_linkargs	link;
+	struct nfsd_symlinkargs	symlink;
+	struct nfsd_readdirargs	readdir;
+};
+
+#define NFSSVC_XDRSIZE		sizeof(union nfsd_xdrstore)
+
+void nfsd_xdr_init(void);
+
+int nfssvc_decode_void(struct svc_rqst *, u32 *, void *);
+int nfssvc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *);
+int nfssvc_decode_sattrargs(struct svc_rqst *, u32 *,
+				struct nfsd_sattrargs *);
+int nfssvc_decode_diropargs(struct svc_rqst *, u32 *,
+				struct nfsd_diropargs *);
+int nfssvc_decode_readargs(struct svc_rqst *, u32 *,
+				struct nfsd_readargs *);
+int nfssvc_decode_writeargs(struct svc_rqst *, u32 *,
+				struct nfsd_writeargs *);
+int nfssvc_decode_createargs(struct svc_rqst *, u32 *,
+				struct nfsd_createargs *);
+int nfssvc_decode_renameargs(struct svc_rqst *, u32 *,
+				struct nfsd_renameargs *);
+int nfssvc_decode_linkargs(struct svc_rqst *, u32 *,
+				struct nfsd_linkargs *);
+int nfssvc_decode_symlinkargs(struct svc_rqst *, u32 *,
+				struct nfsd_symlinkargs *);
+int nfssvc_decode_readdirargs(struct svc_rqst *, u32 *,
+				struct nfsd_readdirargs *);
+int nfssvc_encode_void(struct svc_rqst *, u32 *, void *);
+int nfssvc_encode_attrstat(struct svc_rqst *, u32 *, struct nfsd_attrstat *);
+int nfssvc_encode_diropres(struct svc_rqst *, u32 *, struct nfsd_diropres *);
+int nfssvc_encode_readlinkres(struct svc_rqst *, u32 *, struct nfsd_readlinkres *);
+int nfssvc_encode_readres(struct svc_rqst *, u32 *, struct nfsd_readres *);
+int nfssvc_encode_statfsres(struct svc_rqst *, u32 *, struct nfsd_statfsres *);
+int nfssvc_encode_readdirres(struct svc_rqst *, u32 *, struct nfsd_readdirres *);
+int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
+
+int nfssvc_encode_entry(struct readdir_cd *, const char *name,
+				int namlen, off_t offset, ino_t ino);
+
+#endif /* LINUX_NFSD_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/nfsd/xdr3.h linux/include/linux/nfsd/xdr3.h
--- v2.1.31/linux/include/linux/nfsd/xdr3.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/nfsd/xdr3.h	Thu Dec 12 16:35:47 1996
@@ -0,0 +1,268 @@
+/*
+ * linux/include/linux/nfsd/xdr3.h
+ *
+ * XDR types for NFSv3 in nfsd.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef LINUX_NFSD_XDR3_H
+#define LINUX_NFSD_XDR3_H
+
+#include <linux/nfsd/xdr.h>
+
+struct nfsd3_sattrargs {
+	struct svc_fh		fh;
+	struct iattr		attrs;
+	time_t			guardtime;
+};
+
+struct nfsd3_diropargs {
+	struct svc_fh		fh;
+	char *			name;
+	int			len;
+};
+
+struct nfsd3_accessargs {
+	struct svc_fh		fh;
+	unsigned int		access;
+};
+
+struct nfsd3_readargs {
+	struct svc_fh		fh;
+	__u64			offset;
+	__u32			count;
+};
+
+struct nfsd3_writeargs {
+	svc_fh			fh;
+	__u64			offset;
+	__u32			count;
+	int			stable;
+	__u8 *			data;
+	int			len;
+};
+
+struct nfsd3_createargs {
+	struct svc_fh		fh;
+	char *			name;
+	int			len;
+	int			createmode;
+	struct iattr		attrs;
+	__u32 *			verf;
+};
+
+struct nfsd3_mknodargs {
+	struct svc_fh		fh;
+	char *			name;
+	int			len;
+	__u32			ftype;
+	__u32			major, minor;
+	struct iattr		attrs;
+};
+
+struct nfsd3_renameargs {
+	struct svc_fh		ffh;
+	char *			fname;
+	int			flen;
+	struct svc_fh		tfh;
+	char *			tname;
+	int			tlen;
+};
+
+struct nfsd3_linkargs {
+	struct svc_fh		ffh;
+	struct svc_fh		tfh;
+	char *			tname;
+	int			tlen;
+};
+
+struct nfsd3_symlinkargs {
+	struct svc_fh		ffh;
+	char *			fname;
+	int			flen;
+	char *			tname;
+	int			tlen;
+	struct iattr		attrs;
+};
+
+struct nfsd3_readdirargs {
+	struct svc_fh		fh;
+	__u32			cookie;
+	__u32			dircount;
+	__u32			count;
+	__u32 *			verf;
+};
+
+struct nfsd3_commitargs {
+	struct svc_fh		fh;
+	__u64			offset;
+	__u64			count;
+};
+
+struct nfsd3_attrstat {
+	__u32			status;
+	struct svc_fh		fh;
+};
+
+struct nfsd3_lookupres  {
+	__u32			status;
+	struct svc_fh		dirfh;
+	struct svc_fh		fh;
+};
+
+struct nfsd3_accessres {
+	__u32			status;
+	struct svc_fh		fh;
+	__u32			access;
+};
+
+struct nfsd3_readlinkres {
+	__u32			status;
+	struct svc_fh		fh;
+	__u32			len;
+};
+
+struct nfsd3_readres {
+	__u32			status;
+	struct svc_fh		fh;
+	unsigned long		count;
+	int			eof;
+};
+
+struct nfsd3_writeres {
+	__u32			status;
+	struct svc_fh		fh;
+	unsigned long		count;
+	int			committed;
+};
+
+struct nfsd3_createres {
+	__u32			status;
+	struct svc_fh		dirfh;
+	struct svc_fh		fh;
+};
+
+struct nfsd3_renameres {
+	__u32			status;
+	struct svc_fh		ffh;
+	struct svc_fh		tfh;
+};
+
+struct nfsd3_linkres {
+	__u32			status;
+	struct svc_fh		tfh;
+	struct svc_fh		fh;
+};
+
+struct nfsd3_readdirres {
+	__u32			status;
+	struct svc_fh		fh;
+	__u32 *			list_end;
+};
+
+struct nfsd3_statfsres {
+	__u32			status;
+	struct statfs		stats;
+	__u32			invarsec;
+};
+
+struct nfsd3_fsinfores {
+	__u32			status;
+	__u32			f_rtmax;
+	__u32			f_rtpref;
+	__u32			f_rtmult;
+	__u32			f_wtmax;
+	__u32			f_wtpref;
+	__u32			f_wtmult;
+	__u32			f_dtpref;
+	__u64			f_maxfilesize;
+	__u32			f_properties;
+};
+
+struct nfsd3_pathconfres {
+	__u32			status;
+	__u32			p_link_max;
+	__u32			p_name_max;
+	__u32			p_case_insensitive;
+	__u32			p_case_preserving;
+};
+
+struct nfsd3_commitres {
+	__u32			status;
+	struct svc_fh		fh;
+};
+
+/* dummy type for release */
+struct nfsd3_fhandle2 {
+	__u32			dummy;
+	struct svc_fh		fh1;
+	struct svc_fh		fh2;
+};
+
+/*
+ * Storage requirements for XDR arguments and results.
+ */
+union nfsd3_xdrstore {
+	struct nfsd3_sattrargs		sattrargs;
+	struct nfsd3_diropargs		diropargs;
+	struct nfsd3_readargs		readargs;
+	struct nfsd3_writeargs		writeargs;
+	struct nfsd3_createargs		createargs;
+	struct nfsd3_renameargs		renameargs;
+	struct nfsd3_linkargs		linkargs;
+	struct nfsd3_symlinkargs	symlinkargs;
+	struct nfsd3_readdirargs	readdirargs;
+	struct nfsd3_lookupres 		lookupres;
+	struct nfsd3_accessres		accessres;
+	struct nfsd3_readlinkres	readlinkres;
+	struct nfsd3_readres		readres;
+	struct nfsd3_writeres		writeres;
+	struct nfsd3_createres		createres;
+	struct nfsd3_renameres		renameres;
+	struct nfsd3_linkres		linkres;
+	struct nfsd3_readdirres		readdirres;
+	struct nfsd3_statfsres		statfsres;
+	struct nfsd3_fsinfores		fsinfores;
+	struct nfsd3_pathconfres	pathconfres;
+	struct nfsd3_commitres		commitres;
+};
+
+#define NFS3_SVC_XDRSIZE		sizeof(union nfsd3_xdrstore)
+
+void nfsxdr_init(void);
+
+int nfs3svc_decode_fhandle(struct svc_rqst *, u32 *, struct svc_fh *);
+int nfs3svc_decode_sattr3args(struct svc_rqst *, u32 *,
+				struct nfsd3_sattrargs *);
+int nfs3svc_decode_dirop3args(struct svc_rqst *, u32 *,
+				struct nfsd3_diropargs *);
+int nfs3svc_decode_read3args(struct svc_rqst *, u32 *,
+				struct nfsd3_readargs *);
+int nfs3svc_decode_write3args(struct svc_rqst *, u32 *,
+				struct nfsd3_writeargs *);
+int nfs3svc_decode_create3args(struct svc_rqst *, u32 *,
+				struct nfsd3_createargs *);
+int nfs3svc_decode_rename3args(struct svc_rqst *, u32 *,
+				struct nfsd3_renameargs *);
+int nfs3svc_decode_link3args(struct svc_rqst *, u32 *,
+				struct nfsd3_linkargs *);
+int nfs3svc_decode_symlink3args(struct svc_rqst *, u32 *,
+				struct nfsd3_symlinkargs *);
+int nfs3svc_decode_readdir3args(struct svc_rqst *, u32 *,
+				struct nfsd3_readdirargs *);
+int nfs3svc_encode_readlinkres(struct svc_rqst *, u32 *,
+				struct nfsd3_readlinkres *);
+int nfs3svc_encode_readres(struct svc_rqst *, u32 *, struct nfsd3_readres *);
+int nfs3svc_encode_statfsres(struct svc_rqst *, u32 *,
+				struct nfsd3_statfsres *);
+int nfs3svc_encode_readdirres(struct svc_rqst *, u32 *,
+				struct nfsd3_readdirres *);
+int nfs3svc_release_fhandle(struct svc_rqst *, u32 *,
+				struct nfsd_fhandle *);
+int nfs3svc_release_fhandle2(struct svc_rqst *, u32 *,
+				struct nfsd3_fhandle2 *);
+int nfs3svc_encode_entry(struct readdir_cd *, const char *name,
+				int namlen, unsigned long offset, ino_t ino);
+
+#endif /* LINUX_NFSD_XDR3_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/auth.h linux/include/linux/sunrpc/auth.h
--- v2.1.31/linux/include/linux/sunrpc/auth.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/auth.h	Fri Apr  4 12:19:02 1997
@@ -0,0 +1,99 @@
+/*
+ * linux/include/linux/auth.h
+ *
+ * Declarations for the RPC authentication machinery.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_AUTH_H
+#define _LINUX_SUNRPC_AUTH_H
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/sunrpc/sched.h>
+
+
+/*
+ * Client user credentials
+ */
+struct rpc_cred {
+	struct rpc_cred *	cr_next;	/* linked list */
+	unsigned long		cr_expire;	/* when to gc */
+	unsigned short		cr_count;	/* ref count */
+	unsigned short		cr_flags;	/* various flags */
+
+	uid_t			cr_uid;
+
+	/* per-flavor data */
+};
+#define RPCAUTH_CRED_LOCKED	0x0001
+#define RPCAUTH_CRED_UPTODATE	0x0002
+#define RPCAUTH_CRED_DEAD	0x0004
+
+/*
+ * Client authentication handle
+ */
+#define RPC_CREDCACHE_NR	8
+struct rpc_auth {
+	struct rpc_cred *	au_credcache[RPC_CREDCACHE_NR];
+	unsigned long		au_expire;	/* cache expiry interval */
+	unsigned long		au_nextgc;	/* next garbage collection */
+	unsigned int		au_cslack;	/* call cred size estimate */
+	unsigned int		au_rslack;	/* reply verf size guess */
+	unsigned int		au_flags;	/* various flags */
+	struct rpc_authops *	au_ops;		/* operations */
+
+	/* per-flavor data */
+};
+#define RPC_AUTH_PROC_CREDS	0x0010		/* process creds (including
+						 * uid/gid, fs[ug]id, gids)
+						 */
+
+/*
+ * Client authentication ops
+ */
+struct rpc_authops {
+	unsigned int		au_flavor;	/* flavor (RPC_AUTH_*) */
+#ifdef RPC_DEBUG
+	char *			au_name;
+#endif
+	struct rpc_auth *	(*create)(struct rpc_clnt *);
+	void			(*destroy)(struct rpc_auth *);
+
+	struct rpc_cred *	(*crcreate)(struct rpc_task *);
+	void			(*crdestroy)(struct rpc_cred *);
+
+	int			(*crmatch)(struct rpc_task *, struct rpc_cred*);
+	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
+	int			(*crrefresh)(struct rpc_task *);
+	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
+};
+
+extern struct rpc_authops	authunix_ops;
+extern struct rpc_authops	authnull_ops;
+#ifdef CONFIG_SUNRPC_SECURE
+extern struct rpc_authops	authdes_ops;
+#endif
+
+int			rpcauth_register(struct rpc_authops *);
+int			rpcauth_unregister(struct rpc_authops *);
+struct rpc_auth *	rpcauth_create(unsigned int, struct rpc_clnt *);
+void			rpcauth_destroy(struct rpc_auth *);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_task *);
+void			rpcauth_holdcred(struct rpc_task *);
+void			rpcauth_releasecred(struct rpc_task *);
+int			rpcauth_matchcred(struct rpc_task *, struct rpc_cred *);
+u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
+u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
+int			rpcauth_refreshcred(struct rpc_task *);
+void			rpcauth_invalcred(struct rpc_task *);
+int			rpcauth_uptodatecred(struct rpc_task *);
+void			rpcauth_init_credcache(struct rpc_auth *);
+void			rpcauth_free_credcache(struct rpc_auth *);
+void			rpcauth_insert_credcache(struct rpc_auth *,
+						struct rpc_cred *);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SUNRPC_AUTH_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- v2.1.31/linux/include/linux/sunrpc/clnt.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/clnt.h	Fri Apr  4 12:19:02 1997
@@ -0,0 +1,130 @@
+/*
+ *  linux/include/linux/sunrpc/clnt.h
+ *
+ *  Declarations for the high-level RPC client interface
+ *
+ *  Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_CLNT_H
+#define _LINUX_SUNRPC_CLNT_H
+
+#include <linux/sunrpc/msg_prot.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/xdr.h>
+
+/*
+ * This defines an RPC port mapping
+ */
+struct rpc_portmap {
+	__u32			pm_prog;
+	__u32			pm_vers;
+	__u32			pm_prot;
+	__u32			pm_port;
+};
+
+/*
+ * The high-level client handle
+ */
+struct rpc_clnt {
+	struct rpc_xprt *	cl_xprt;	/* transport */
+	struct rpc_procinfo *	cl_procinfo;	/* procedure info */
+	u32			cl_maxproc;	/* max procedure number */
+
+	char *			cl_server;	/* server machine name */
+	char *			cl_protname;	/* protocol name */
+	struct rpc_auth *	cl_auth;	/* authenticator */
+	struct rpc_portmap	cl_pmap;	/* port mapping */
+	struct rpc_stat *	cl_stats;	/* statistics */
+
+	unsigned int		cl_softrtry : 1,/* soft timeouts */
+				cl_intr     : 1,/* interruptible */
+				cl_chatty   : 1,/* be verbose */
+				cl_autobind : 1,/* use getport() */
+				cl_binding  : 1,/* doing a getport() */
+				cl_oneshot  : 1,/* dispose after use */
+				cl_dead     : 1;/* abandoned */
+	unsigned long		cl_hardmax;	/* max hard timeout */
+
+	struct rpc_wait_queue	cl_bindwait;	/* waiting on getport() */
+	unsigned int		cl_users;	/* number of references */
+};
+#define cl_timeout		cl_xprt->timeout
+#define cl_prog			cl_pmap.pm_prog
+#define cl_vers			cl_pmap.pm_vers
+#define cl_port			cl_pmap.pm_port
+#define cl_prot			cl_pmap.pm_prot
+
+/*
+ * General RPC program info
+ */
+#define RPC_MAXVERSION		4
+struct rpc_program {
+	char *			name;		/* protocol name */
+	u32			number;		/* program number */
+	unsigned int		nrvers;		/* number of versions */
+	struct rpc_version **	version;	/* version array */
+	struct rpc_stat *	stats;		/* statistics */
+};
+
+struct rpc_version {
+	u32			number;		/* version number */
+	unsigned int		nrprocs;	/* number of procs */
+	struct rpc_procinfo *	procs;		/* procedure array */
+};
+
+/*
+ * Procedure information
+ */
+struct rpc_procinfo {
+	char *			p_procname;	/* procedure name */
+	kxdrproc_t		p_encode;	/* XDR encode function */
+	kxdrproc_t		p_decode;	/* XDR decode function */
+	unsigned int		p_bufsiz;	/* req. buffer size */
+	unsigned int		p_count;	/* call count */
+};
+
+#define rpcproc_bufsiz(clnt, proc)	((clnt)->cl_procinfo[proc].p_bufsiz)
+#define rpcproc_encode(clnt, proc)	((clnt)->cl_procinfo[proc].p_encode)
+#define rpcproc_decode(clnt, proc)	((clnt)->cl_procinfo[proc].p_decode)
+#define rpcproc_name(clnt, proc)	((clnt)->cl_procinfo[proc].p_procname)
+#define rpcproc_count(clnt, proc)	((clnt)->cl_procinfo[proc].p_count)
+
+#define RPC_CONGESTED(clnt)	(RPCXPRT_CONGESTED((clnt)->cl_xprt))
+#define RPC_PEERADDR(clnt)	(&(clnt)->cl_xprt->addr)
+
+#ifdef __KERNEL__
+
+struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname,
+				struct rpc_program *info,
+				u32 version, int authflavor);
+int		rpc_shutdown_client(struct rpc_clnt *);
+int		rpc_destroy_client(struct rpc_clnt *);
+void		rpc_release_client(struct rpc_clnt *);
+void		rpc_getport(struct rpc_task *, struct rpc_clnt *);
+int		rpc_register(u32, u32, int, unsigned short, int *);
+
+int		rpc_call(struct rpc_clnt *clnt, u32 proc,
+				void *argp, void *resp, int flags);
+int		rpc_call_async(struct rpc_task *task, u32 proc,
+				void *argp, void *resp, int flags);
+void		rpc_call_setup(struct rpc_task *task, u32 proc,
+				void *argp, void *resp, int flags);
+int		rpc_do_call(struct rpc_clnt *clnt, u32 proc,
+				void *argp, void *resp, int flags,
+				rpc_action callback, void *clntdata);
+void		rpc_restart_call(struct rpc_task *);
+#define rpc_call(clnt, proc, argp, resp, flags)	\
+		rpc_do_call(clnt, proc, argp, resp, flags, NULL, NULL)
+
+extern __inline__ void
+rpc_set_timeout(struct rpc_clnt *clnt, unsigned int retr, unsigned long incr)
+{
+	xprt_set_timeout(&clnt->cl_timeout, retr, incr);
+}
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SUNRPC_CLNT_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/debug.h linux/include/linux/sunrpc/debug.h
--- v2.1.31/linux/include/linux/sunrpc/debug.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/debug.h	Fri Apr  4 11:15:59 1997
@@ -0,0 +1,91 @@
+/*
+ * linux/include/linux/sunrpc/debug.h
+ *
+ * Debugging support for sunrpc module
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_DEBUG_H_
+#define _LINUX_SUNRPC_DEBUG_H_
+
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+
+/*
+ * Enable RPC debugging/profiling.
+ */
+#define  RPC_DEBUG
+/* #define  RPC_PROFILE */
+
+/*
+ * RPC debug facilities
+ */
+#define RPCDBG_XPRT		0x0001
+#define RPCDBG_CALL		0x0002
+#define RPCDBG_DEBUG		0x0004
+#define RPCDBG_NFS		0x0008
+#define RPCDBG_AUTH		0x0010
+#define RPCDBG_PMAP		0x0020
+#define RPCDBG_SCHED		0x0040
+#define RPCDBG_SVCSOCK		0x0100
+#define RPCDBG_SVCDSP		0x0200
+#define RPCDBG_MISC		0x0400
+#define RPCDBG_ALL		0x7fff
+
+#ifdef __KERNEL__
+
+/*
+ * Debugging macros etc
+ */
+#ifdef RPC_DEBUG
+extern unsigned int		rpc_debug;
+extern unsigned int		nfs_debug;
+extern unsigned int		nfsd_debug;
+extern unsigned int		nlm_debug;
+#endif
+
+#define dprintk(args...)	dfprintk(FACILITY, ## args)
+
+#undef ifdebug
+#ifdef RPC_DEBUG			
+# define ifdebug(fac)		if (rpc_debug & RPCDBG_##fac)
+# define dfprintk(fac, args...)	do { ifdebug(fac) printk(## args); } while(0)
+# define RPC_IFDEBUG(x)		x
+#else
+# define dfprintk(fac, args...)	do ; while (0)
+# define RPC_IFDEBUG(x)
+#endif
+
+#ifdef RPC_PROFILE
+# define pprintk(args...)	printk(## args)
+#else
+# define pprintk(args...)	do ; while (0)
+#endif
+
+/*
+ * Sysctl interface for RPC debugging
+ */
+#ifdef RPC_DEBUG
+void		rpc_register_sysctl(void);
+void		rpc_unregister_sysctl(void);
+#endif
+
+#endif /* __KERNEL__ */
+
+/*
+ * Declarations for the sysctl debug interface, which allows to read or
+ * change the debug flags for rpc, nfs, nfsd, and lockd. Since the sunrpc
+ * module currently registers its sysctl table dynamically, the sysctl path
+ * for module FOO is <CTL_SUNRPC, CTL_FOODEBUG>.
+ */
+#define CTL_SUNRPC	7249	/* arbitrary and hopefully unused */
+
+enum {
+	CTL_RPCDEBUG = 1,
+	CTL_NFSDEBUG,
+	CTL_NFSDDEBUG,
+	CTL_NLMDEBUG,
+};
+
+#endif /* _LINUX_SUNRPC_DEBUG_H_ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/msg_prot.h linux/include/linux/sunrpc/msg_prot.h
--- v2.1.31/linux/include/linux/sunrpc/msg_prot.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/msg_prot.h	Sun Oct 13 07:58:51 1996
@@ -0,0 +1,62 @@
+/*
+ * linux/include/net/sunrpc/msg_prot.h
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_MSGPROT_H_
+#define _LINUX_SUNRPC_MSGPROT_H_
+
+#ifdef __KERNEL__ /* user programs should get these from the rpc header files */
+
+#define RPC_VERSION 2
+
+enum rpc_auth_flavor {
+	RPC_AUTH_NULL  = 0,
+	RPC_AUTH_UNIX  = 1,
+	RPC_AUTH_SHORT = 2,
+	RPC_AUTH_DES   = 3,
+	RPC_AUTH_KRB   = 4,
+};
+
+enum rpc_msg_type {
+	RPC_CALL = 0,
+	RPC_REPLY = 1
+};
+
+enum rpc_reply_stat {
+	RPC_MSG_ACCEPTED = 0,
+	RPC_MSG_DENIED = 1
+};
+
+enum rpc_accept_stat {
+	RPC_SUCCESS = 0,
+	RPC_PROG_UNAVAIL = 1,
+	RPC_PROG_MISMATCH = 2,
+	RPC_PROC_UNAVAIL = 3,
+	RPC_GARBAGE_ARGS = 4,
+	RPC_SYSTEM_ERR = 5
+};
+
+enum rpc_reject_stat {
+	RPC_MISMATCH = 0,
+	RPC_AUTH_ERROR = 1
+};
+
+enum rpc_auth_stat {
+	RPC_AUTH_OK = 0,
+	RPC_AUTH_BADCRED = 1,
+	RPC_AUTH_REJECTEDCRED = 2,
+	RPC_AUTH_BADVERF = 3,
+	RPC_AUTH_REJECTEDVERF = 4,
+	RPC_AUTH_TOOWEAK = 5
+};
+
+#define RPC_PMAP_PROGRAM	100000
+#define RPC_PMAP_VERSION	2
+#define RPC_PMAP_PORT		111
+
+#define RPC_MAXNETNAMELEN	256
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SUNRPC_MSGPROT_H_ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h
--- v2.1.31/linux/include/linux/sunrpc/sched.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/sched.h	Fri Apr  4 11:18:20 1997
@@ -0,0 +1,170 @@
+/*
+ * linux/include/linux/sunrpc/sched.h
+ *
+ * Scheduling primitives for kernel Sun RPC.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_SCHED_H_
+#define _LINUX_SUNRPC_SCHED_H_
+
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#include <linux/sunrpc/types.h>
+
+/*
+ * Define this if you want to test the fast scheduler for async calls.
+ * This is still experimental and may not work.
+ */
+#undef  CONFIG_RPC_FASTSCHED
+
+/*
+ * This is the RPC task struct
+ */
+struct rpc_task {
+	struct rpc_task *	tk_prev;	/* wait queue links */
+	struct rpc_task *	tk_next;
+#ifdef RPC_DEBUG
+	unsigned long		tk_magic;	/* 0xf00baa */
+#endif
+	struct rpc_task *	tk_next_task;	/* global list of tasks */
+	struct rpc_task *	tk_prev_task;	/* global list of tasks */
+	struct rpc_clnt *	tk_client;	/* RPC client */
+	struct rpc_rqst *	tk_rqstp;	/* RPC request */
+	struct rpc_cred *	tk_cred;	/* RPC credentials */
+	int			tk_status;	/* result of last operation */
+	struct rpc_wait_queue *	tk_rpcwait;	/* RPC wait queue we're on */
+
+	/*
+	 * RPC call state
+	 */
+	u32			tk_proc;	/* procedure number */
+	u32 *			tk_buffer;	/* XDR buffer */
+	void *			tk_argp;	/* argument storage */
+	void *			tk_resp;	/* result storage */
+	u8			tk_garb_retry,
+				tk_cred_retry,
+				tk_suid_retry;
+
+	/*
+	 * callback	to be executed after waking up
+	 * action	next procedure for async tasks
+	 * exit		exit async task and report to caller
+	 */
+	void			(*tk_callback)(struct rpc_task *);
+	void			(*tk_action)(struct rpc_task *);
+	void			(*tk_exit)(struct rpc_task *);
+	void *			tk_calldata;
+
+	/*
+	 * tk_timer is used for async processing by the RPC scheduling
+	 * primitives. You should not access this directly unless
+	 * you have a pathological interest in kernel oopses.
+	 */
+	struct timer_list	tk_timer;	/* kernel timer */
+	struct wait_queue *	tk_wait;	/* sync: sleep on this q */
+	unsigned long		tk_timeout;	/* timeout for rpc_sleep() */
+	unsigned short		tk_flags;	/* misc flags */
+#ifdef RPC_DEBUG
+	unsigned short		tk_pid;		/* debugging aid */
+#endif
+};
+#define tk_auth			tk_client->cl_auth
+#define tk_xprt			tk_client->cl_xprt
+
+typedef void			(*rpc_action)(struct rpc_task *);
+
+/*
+ * RPC task flags
+ */
+#define RPC_TASK_RUNNING	0x0001		/* is running */
+#define RPC_TASK_ASYNC		0x0002		/* is an async task */
+#define RPC_TASK_CALLBACK	0x0004		/* invoke callback */
+#define RPC_TASK_SWAPPER	0x0008		/* is swapping in/out */
+#define RPC_TASK_SETUID		0x0010		/* is setuid process */
+#define RPC_TASK_CHILD		0x0020		/* is child of other task */
+#define RPC_CALL_REALUID	0x0040		/* try using real uid */
+#define RPC_CALL_MAJORSEEN	0x0080		/* major timeout seen */
+#define RPC_TASK_ROOTCREDS	0x0100		/* force root creds */
+#define RPC_TASK_DYNAMIC	0x0200		/* task was kmalloc'ed */
+#define RPC_TASK_KILLED		0x0400		/* task was killed */
+
+#define RPC_IS_RUNNING(t)	((t)->tk_flags & RPC_TASK_RUNNING)
+#define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
+#define RPC_IS_SETUID(t)	((t)->tk_flags & RPC_TASK_SETUID)
+#define RPC_IS_CHILD(t)		((t)->tk_flags & RPC_TASK_CHILD)
+#define RPC_IS_SWAPPER(t)	((t)->tk_flags & RPC_TASK_SWAPPER)
+#define RPC_DO_CALLBACK(t)	((t)->tk_flags & RPC_TASK_CALLBACK)
+#define RPC_DO_ROOTOVERRIDE(t)	((t)->tk_flags & RPC_TASK_ROOTCREDS)
+#define RPC_ASSASSINATED(t)	((t)->tk_flags & RPC_TASK_KILLED)
+
+/*
+ * RPC synchronization objects
+ */
+struct rpc_wait_queue {
+	struct rpc_task *	task;
+#ifdef RPC_DEBUG
+	char *			name;
+#endif
+};
+
+#ifndef RPC_DEBUG
+# define RPC_INIT_WAITQ(name)	((struct rpc_wait_queue) { NULL })
+#else
+# define RPC_INIT_WAITQ(name)	((struct rpc_wait_queue) { NULL, name })
+#endif
+
+/*
+ * Function prototypes
+ */
+struct rpc_task *rpc_new_task(struct rpc_clnt *, rpc_action, int flags);
+struct rpc_task *rpc_new_child(struct rpc_clnt *, struct rpc_task *parent);
+void		rpc_init_task(struct rpc_task *, struct rpc_clnt *,
+					rpc_action exitfunc, int flags);
+void		rpc_release_task(struct rpc_task *);
+void		rpc_killall_tasks(struct rpc_clnt *);
+void		rpc_execute(struct rpc_task *);
+void		rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
+					rpc_action action);
+void		rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
+void		rpc_remove_wait_queue(struct rpc_task *);
+void		rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
+					rpc_action action, rpc_action timer);
+void		rpc_cond_wait(struct rpc_wait_queue *, struct rpc_task *,
+					unsigned char *,
+					rpc_action action, rpc_action timer);
+void		rpc_wake_up_task(struct rpc_task *);
+void		rpc_wake_up(struct rpc_wait_queue *);
+struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
+void		rpc_wake_up_status(struct rpc_wait_queue *, int);
+void		rpc_add_timer(struct rpc_task *, rpc_action);
+void		rpc_del_timer(struct rpc_task *);
+void		rpc_delay(struct rpc_task *, unsigned long);
+void *		rpc_allocate(unsigned int flags, unsigned int);
+void		rpc_free(void *);
+void		rpciod_up(void);
+void		rpciod_down(void);
+
+extern __inline__ void *
+rpc_malloc(struct rpc_task *task, unsigned int size)
+{
+	return rpc_allocate(task->tk_flags, size);
+}
+
+extern __inline__ void
+rpc_exit(struct rpc_task *task, int status)
+{
+	task->tk_status = status;
+	task->tk_action = NULL;
+}
+
+#ifdef RPC_DEBUG
+extern __inline__ char *
+rpc_qname(struct rpc_wait_queue *q)
+{
+	return q->name? q->name : "unknown";
+}
+#endif
+
+#endif /* _LINUX_SUNRPC_SCHED_H_ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/stats.h linux/include/linux/sunrpc/stats.h
--- v2.1.31/linux/include/linux/sunrpc/stats.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/stats.h	Fri Apr  4 12:19:02 1997
@@ -0,0 +1,59 @@
+/*
+ * linux/include/linux/sunrpc/stats.h
+ *
+ * Client statistics collection for SUN RPC
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_STATS_H
+#define _LINUX_SUNRPC_STATS_H
+
+#include <linux/proc_fs.h>
+
+struct rpc_stat {
+	struct rpc_stat *	next;
+	struct proc_dir_entry *	entry;
+	struct rpc_program *	program;
+
+	unsigned int		netcnt,
+				netudpcnt,
+				nettcpcnt,
+				nettcpconn,
+				netreconn;
+	unsigned int		rpccnt,
+				rpcretrans,
+				rpcauthrefresh,
+				rpcgarbage;
+};
+
+struct svc_stat {
+	struct svc_stat *	next;
+	struct proc_dir_entry *	entry;
+	struct svc_program *	program;
+
+	unsigned int		netcnt,
+				netudpcnt,
+				nettcpcnt,
+				nettcpconn;
+	unsigned int		rpccnt,
+				rpcbadfmt,
+				rpcbadauth,
+				rpcbadclnt;
+};
+
+void		rpcstat_init(void);
+void		rpcstat_exit(void);
+
+void		rpcstat_register(struct rpc_stat *);
+void		rpcstat_unregister(struct rpc_stat *);
+int		rpcstat_get_info(struct rpc_stat *, char *, char **,
+					off_t, int);
+void		rpcstat_zero_info(struct rpc_program *);
+void		svcstat_register(struct svc_stat *);
+void		svcstat_unregister(struct svc_stat *);
+int		svcstat_get_info(struct svc_stat *, char *, char **,
+					off_t, int);
+void		svcstat_zero_info(struct svc_program *);
+
+#endif /* _LINUX_SUNRPC_STATS_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/svc.h linux/include/linux/sunrpc/svc.h
--- v2.1.31/linux/include/linux/sunrpc/svc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/svc.h	Fri Apr  4 11:38:27 1997
@@ -0,0 +1,180 @@
+/*
+ * linux/include/linux/sunrpc/svc.h
+ *
+ * RPC server declarations.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+
+#ifndef SUNRPC_SVC_H
+#define SUNRPC_SVC_H
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcauth.h>
+
+/*
+ * RPC service.
+ *
+ * An RPC service is a ``daemon,'' possibly multithreaded, which
+ * receives and processes incoming RPC messages.
+ * It has one or more transport sockets associated with it, and maintains
+ * a list of idle threads waiting for input.
+ *
+ * We currently do not support more than one RPC program per daemon.
+ */
+struct svc_serv {
+	struct svc_rqst *	sv_threads;	/* idle server threads */
+	struct svc_sock *	sv_sockets;	/* pending sockets */
+	struct svc_program *	sv_program;	/* RPC program */
+	struct svc_stat *	sv_stats;	/* RPC statistics */
+	unsigned int		sv_nrthreads;	/* # of server threads */
+	unsigned int		sv_bufsz;	/* datagram buffer size */
+	unsigned int		sv_xdrsize;	/* XDR buffer size */
+
+	struct svc_sock *	sv_allsocks;	/* all sockets */
+
+	char *			sv_name;	/* service name */
+};
+
+/*
+ * Maximum payload size supported by a kernel RPC server.
+ * This is use to determine the max number of pages nfsd is
+ * willing to return in a single READ operation.
+ */
+#define RPCSVC_MAXPAYLOAD	16384u
+
+/*
+ * Buffer to store RPC requests or replies in.
+ * Each server thread has one of these beasts.
+ *
+ * Area points to the allocated memory chunk currently owned by the
+ * buffer. Base points to the buffer containing the request, which is
+ * different from area when directly reading from an sk_buff. buf is
+ * the current read/write position while processing an RPC request.
+ *
+ * The array of iovecs can hold additional data that the server process
+ * may not want to copy into the RPC reply buffer, but pass to the 
+ * network sendmsg routines directly. The prime candidate for this
+ * will of course be NFS READ operations, but one might also want to
+ * do something about READLINK and READDIR. It might be worthwhile
+ * to implement some generic readdir cache in the VFS layer...
+ *
+ * On the receiving end of the RPC server, the iovec may be used to hold
+ * the list of IP fragments once we get to process fragmented UDP
+ * datagrams directly.
+ */
+#define RPCSVC_MAXIOV		((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE + 1)
+struct svc_buf {
+	u32 *			area;	/* allocated memory */
+	u32 *			base;	/* base of RPC datagram */
+	int			buflen;	/* total length of buffer */
+	u32 *			buf;	/* read/write pointer */
+	int			len;	/* current end of buffer */
+
+	/* iovec for zero-copy NFS READs */
+	struct iovec		iov[RPCSVC_MAXIOV];
+	int			nriov;
+};
+#define svc_getlong(argp, val)	{ (val) = *(argp)->buf++; (argp)->len--; }
+#define svc_putlong(resp, val)	{ *(resp)->buf++ = (val); (resp)->len++; }
+
+/*
+ * The context of a single thread, including the request currently being
+ * processed.
+ * NOTE: First two items must be prev/next.
+ */
+struct svc_rqst {
+	struct svc_rqst *	rq_prev;	/* idle list */
+	struct svc_rqst *	rq_next;
+	struct svc_sock *	rq_sock;	/* socket */
+	struct sockaddr_in	rq_addr;	/* peer address */
+	int			rq_addrlen;
+
+	struct svc_serv *	rq_server;	/* RPC service definition */
+	struct svc_procedure *	rq_procinfo;	/* procedure info */
+	struct svc_cred		rq_cred;	/* auth info */
+	struct sk_buff *	rq_skbuff;	/* fast recv inet buffer */
+	struct svc_buf		rq_defbuf;	/* default buffer */
+	struct svc_buf		rq_argbuf;	/* argument buffer */
+	struct svc_buf		rq_resbuf;	/* result buffer */
+	u32			rq_xid;		/* transmission id */
+	u32			rq_prog;	/* program number */
+	u32			rq_vers;	/* program version */
+	u32			rq_proc;	/* procedure number */
+	u32			rq_prot;	/* IP protocol */
+	unsigned short		rq_verfed  : 1,	/* reply has verifier */
+				rq_userset : 1,	/* auth->setuser OK */
+				rq_secure  : 1;	/* secure port */
+
+	void *			rq_argp;	/* decoded arguments */
+	void *			rq_resp;	/* xdr'd results */
+
+	/* Catering to nfsd */
+	struct svc_client *	rq_client;	/* RPC peer info */
+	struct svc_cacherep *	rq_cacherep;	/* cache info */
+
+	struct wait_queue *	rq_wait;	/* synchronozation */
+};
+
+/*
+ * RPC program
+ */
+struct svc_program {
+	u32			pg_prog;	/* program number */
+	unsigned int		pg_lovers;	/* lowest version */
+	unsigned int		pg_hivers;	/* lowest version */
+	unsigned int		pg_nvers;	/* number of versions */
+	struct svc_version **	pg_vers;	/* version array */
+	char *			pg_name;	/* service name */
+	struct svc_stat *	pg_stats;	/* rpc statistics */
+};
+
+/*
+ * RPC program version
+ */
+struct svc_version {
+	u32			vs_vers;	/* version number */
+	u32			vs_nproc;	/* number of procedures */
+	struct svc_procedure *	vs_proc;	/* per-procedure info */
+
+	/* Override dispatch function (e.g. when caching replies).
+	 * A return value of 0 means drop the request. 
+	 * vs_dispatch == NULL means use default dispatcher.
+	 */
+	int			(*vs_dispatch)(struct svc_rqst *, u32 *);
+};
+
+/*
+ * RPC procedure info
+ */
+typedef int	(*svc_procfunc)(struct svc_rqst *, void *argp, void *resp);
+struct svc_procedure {
+	svc_procfunc		pc_func;	/* process the request */
+	kxdrproc_t		pc_decode;	/* XDR decode args */
+	kxdrproc_t		pc_encode;	/* XDR encode result */
+	kxdrproc_t		pc_release;	/* XDR free result */
+	unsigned int		pc_argsize;	/* argument struct size */
+	unsigned int		pc_ressize;	/* result struct size */
+	unsigned int		pc_count;	/* call count */
+	unsigned int		pc_cachetype;	/* cache info (NFS) */
+};
+
+/*
+ * This is the RPC server thread function prototype
+ */
+typedef void		(*svc_thread_fn)(struct svc_rqst *);
+
+/*
+ * Function prototypes.
+ */
+struct svc_serv *  svc_create(struct svc_program *, unsigned int, unsigned int);
+int		   svc_create_thread(svc_thread_fn, struct svc_serv *);
+void		   svc_exit_thread(struct svc_rqst *);
+void		   svc_destroy(struct svc_serv *);
+int		   svc_process(struct svc_serv *, struct svc_rqst *);
+int		   svc_register(struct svc_serv *, int, unsigned short);
+void		   svc_wake_up(struct svc_serv *);
+
+#endif /* SUNRPC_SVC_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/svcauth.h linux/include/linux/sunrpc/svcauth.h
--- v2.1.31/linux/include/linux/sunrpc/svcauth.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/svcauth.h	Mon Mar  3 01:14:02 1997
@@ -0,0 +1,48 @@
+/*
+ * linux/include/linux/sunrpc/svcauth.h
+ *
+ * RPC server-side authentication stuff.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_SVCAUTH_H_
+#define _LINUX_SUNRPC_SVCAUTH_H_
+
+#ifdef __KERNEL__
+
+#include <linux/sunrpc/msg_prot.h>
+
+struct svc_cred {
+	u32			cr_flavor;
+	uid_t			cr_uid;
+	gid_t			cr_gid;
+	gid_t			cr_groups[NGROUPS];
+};
+
+struct svc_rqst;		/* forward decl */
+
+void	svc_authenticate(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+int	svc_auth_register(u32 flavor, void (*)(struct svc_rqst *,u32 *,u32 *));
+void	svc_auth_unregister(u32 flavor);
+
+#if 0
+/*
+ * Decoded AUTH_UNIX data. This is different from what's in the RPC lib.
+ */
+#define NGRPS		16
+struct authunix_parms {
+	u32		aup_stamp;
+	u32		aup_uid;
+	u32		aup_gid;
+	u32		aup_len;
+	u32		aup_gids[NGRPS];
+};
+
+struct svc_authops *	auth_getops(u32 flavor);
+#endif
+
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SUNRPC_SVCAUTH_H_ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/svcsock.h linux/include/linux/sunrpc/svcsock.h
--- v2.1.31/linux/include/linux/sunrpc/svcsock.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/svcsock.h	Fri Apr  4 11:38:32 1997
@@ -0,0 +1,58 @@
+/*
+ * linux/include/linux/sunrpc/svcsock.h
+ *
+ * RPC server socket I/O.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef SUNRPC_SVCSOCK_H
+#define SUNRPC_SVCSOCK_H
+
+#include <linux/sunrpc/svc.h>
+
+/*
+ * RPC server socket.
+ * NOTE: First two items must be prev/next.
+ */
+struct svc_sock {
+	struct svc_sock *	sk_prev;	/* list of ready sockets */
+	struct svc_sock *	sk_next;
+	struct svc_sock *	sk_list;	/* list of all sockets */
+	struct socket *		sk_sock;	/* berkeley socket layer */
+	struct sock *		sk_sk;		/* INET layer */
+
+	struct svc_serv *	sk_server;	/* service for this socket */
+	unsigned char		sk_inuse;	/* use count */
+	unsigned char		sk_busy;	/* enqueued/receiving */
+	unsigned char		sk_conn;	/* conn pending */
+	unsigned char		sk_close;	/* dead or dying */
+	int			sk_data;	/* data pending */
+	unsigned int		sk_temp : 1,	/* temp socket */
+				sk_qued : 1,	/* on serv->sk_sockets */
+				sk_dead : 1;	/* socket closed */
+	int			(*sk_recvfrom)(struct svc_rqst *rqstp);
+	int			(*sk_sendto)(struct svc_rqst *rqstp);
+
+	/* We keep the old state_change and data_ready CB's here */
+	void			(*sk_ostate)(struct sock *);
+	void			(*sk_odata)(struct sock *, int bytes);
+
+	/* private TCP part */
+	unsigned long		sk_reclen;	/* length of record */
+	int			sk_tcplen;	/* current read length */
+
+	/* Debugging */
+	struct svc_rqst *	sk_rqstp;
+};
+
+/*
+ * Function prototypes.
+ */
+int		svc_create_socket(struct svc_serv *, int, struct sockaddr_in *);
+void		svc_delete_socket(struct svc_sock *);
+int		svc_recv(struct svc_serv *, struct svc_rqst *);
+int		svc_send(struct svc_rqst *);
+void		svc_drop(struct svc_rqst *);
+
+#endif /* SUNRPC_SVCSOCK_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/types.h linux/include/linux/sunrpc/types.h
--- v2.1.31/linux/include/linux/sunrpc/types.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/types.h	Fri Apr  4 11:18:20 1997
@@ -0,0 +1,75 @@
+/*
+ * linux/include/linux/sunrpc/types.h
+ *
+ * Generic types and misc stuff for RPC.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_TYPES_H_
+#define _LINUX_SUNRPC_TYPES_H_
+
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#include <linux/sunrpc/debug.h>
+
+/*
+ * These are the RPC list manipulation primitives used everywhere.
+ */
+struct rpc_listitem	{
+	struct rpc_listitem *	prev;
+	struct rpc_listitem *	next;
+};
+
+extern __inline__ void
+__rpc_append_list(struct rpc_listitem **q, struct rpc_listitem *item)
+{
+	struct rpc_listitem	*next, *prev;
+
+	if (!(next = *q)) {
+		*q = item->next = item->prev = item;
+	} else {
+		prev = next->prev;
+		prev->next = item;
+		next->prev = item;
+		item->next = next;
+		item->prev = prev;
+	}
+}
+
+extern __inline__ void
+__rpc_insert_list(struct rpc_listitem **q, struct rpc_listitem *item)
+{
+	__rpc_append_list(q, item);
+	*q = item;
+}
+
+extern __inline__ void
+__rpc_remove_list(struct rpc_listitem **q, struct rpc_listitem *item)
+{
+	struct rpc_listitem	*prev = item->prev,
+				*next = item->next;
+
+	if (item != prev) {
+		next->prev = prev;
+		prev->next = next;
+	} else {
+		next = NULL;
+	}
+	if (*q == item)
+		*q = next;
+}
+
+#define rpc_insert_list(q, i) \
+      __rpc_insert_list((struct rpc_listitem **) q, (struct rpc_listitem *) i)
+#define rpc_append_list(q, i) \
+      __rpc_append_list((struct rpc_listitem **) q, (struct rpc_listitem *) i)
+#define rpc_remove_list(q, i) \
+      __rpc_remove_list((struct rpc_listitem **) q, (struct rpc_listitem *) i)
+
+/*
+ * Shorthands
+ */
+#define signalled()		(current->signal & ~current->blocked)
+
+#endif /* _LINUX_SUNRPC_TYPES_H_ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/xdr.h linux/include/linux/sunrpc/xdr.h
--- v2.1.31/linux/include/linux/sunrpc/xdr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/xdr.h	Fri Apr  4 11:18:20 1997
@@ -0,0 +1,79 @@
+/*
+ * include/linux/sunrpc/xdr.h
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _SUNRPC_XDR_H_
+#define _SUNRPC_XDR_H_
+
+#ifdef __KERNEL__
+
+#include <linux/uio.h>
+
+/*
+ * Buffer adjustment
+ */
+#define XDR_QUADLEN(l)		(((l) + 3) >> 2)
+
+/*
+ * Generic opaque `network object.' At the kernel level, this type
+ * is used only by lockd.
+ */
+#define XDR_MAX_NETOBJ		1024
+struct xdr_netobj {
+	unsigned int		len;
+	u8 *			data;
+};
+
+/*
+ * This is the generic XDR function. rqstp is either a rpc_rqst (client
+ * side) or svc_rqst pointer (server side).
+ * Encode functions always assume there's enough room in the buffer.
+ */
+typedef int	(*kxdrproc_t)(void *rqstp, u32 *data, void *obj);
+
+/*
+ * These variables contain pre-xdr'ed values for faster operation.
+ * FIXME: should be replaced by macros for big-endian machines.
+ */
+extern u32	xdr_zero, xdr_one, xdr_two;
+
+extern u32	rpc_success,
+		rpc_prog_unavail,
+		rpc_prog_mismatch,
+		rpc_proc_unavail,
+		rpc_garbage_args,
+		rpc_system_err;
+
+extern u32	rpc_auth_ok,
+		rpc_autherr_badcred,
+		rpc_autherr_rejectedcred,
+		rpc_autherr_badverf,
+		rpc_autherr_rejectedverf,
+		rpc_autherr_tooweak,
+		rpc_autherr_dropit;
+
+void		xdr_init(void);
+
+/*
+ * Miscellaneous XDR helper functions
+ */
+u32 *	xdr_encode_string(u32 *p, const char *s);
+u32 *	xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen);
+u32 *	xdr_encode_netobj(u32 *p, const struct xdr_netobj *);
+u32 *	xdr_decode_netobj(u32 *p, struct xdr_netobj *);
+u32 *	xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len);
+
+/*
+ * Adjust iovec to reflect end of xdr'ed data (RPC client XDR)
+ */
+static inline int
+xdr_adjust_iovec(struct iovec *iov, u32 *p)
+{
+	return iov->iov_len = ((u8 *) p - (u8 *) iov->iov_base);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _SUNRPC_XDR_H_ */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sunrpc/xprt.h linux/include/linux/sunrpc/xprt.h
--- v2.1.31/linux/include/linux/sunrpc/xprt.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sunrpc/xprt.h	Fri Apr  4 12:19:02 1997
@@ -0,0 +1,195 @@
+/*
+ *  linux/include/linux/sunrpc/clnt_xprt.h
+ *
+ *  Declarations for the RPC transport interface.
+ *
+ *  Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef _LINUX_SUNRPC_XPRT_H
+#define _LINUX_SUNRPC_XPRT_H
+
+#include <linux/uio.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/sunrpc/sched.h>
+
+/*
+ * Maximum number of iov's we use.
+ */
+#define MAX_IOVEC	8
+
+/*
+ * The transport code maintains an estimate on the maximum number of out-
+ * standing RPC requests, using a smoothed version of the congestion
+ * avoidance implemented in 44BSD. This is basically the Van Jacobson
+ * slow start algorithm: If a retransmit occurs, the congestion window is
+ * halved; otherwise, it is incremented by 1/cwnd when
+ *
+ *	-	a reply is received and
+ *	-	a full number of requests are outstanding and
+ *	-	the congestion window hasn't been updated recently.
+ *
+ * Upper procedures may check whether a request would block waiting for
+ * a free RPC slot by using the RPC_CONGESTED() macro.
+ *
+ * Note: on machines with low memory we should probably use a smaller
+ * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment
+ * reassembly will frequently run out of memory.
+ * Come Linux 2.1, we'll handle fragments directly.
+ */
+#define RPC_MAXCONG		16
+#define RPC_MAXREQS		(RPC_MAXCONG + 1)
+#define RPC_CWNDSCALE		256
+#define RPC_MAXCWND		(RPC_MAXCONG * RPC_CWNDSCALE)
+#define RPC_INITCWND		RPC_CWNDSCALE
+#define RPCXPRT_CONGESTED(xprt) \
+	((xprt)->cong >= ((xprt)->nocong? RPC_MAXCWND : (xprt)->cwnd))
+
+/* Default timeout values */
+#define RPC_MAX_UDP_TIMEOUT	(6*HZ)
+#define RPC_MAX_TCP_TIMEOUT	(600*HZ)
+
+/* RPC call and reply header size as number of 32bit words (verifier
+ * size computed separately)
+ */
+#define RPC_CALLHDRSIZE		6
+#define RPC_REPHDRSIZE		4
+
+/*
+ * This describes a timeout strategy
+ */
+struct rpc_timeout {
+	unsigned long		to_current,		/* current timeout */
+				to_initval,		/* initial timeout */
+				to_maxval,		/* max timeout */
+				to_increment,		/* if !exponential */
+				to_resrvval;		/* reserve timeout */
+	short			to_retries;		/* max # of retries */
+	unsigned char		to_exponential;
+};
+
+/*
+ * This is the RPC buffer
+ */
+struct rpc_iov {
+	struct iovec		io_vec[MAX_IOVEC];
+	unsigned int		io_nr;
+	unsigned int		io_len;
+};
+
+/*
+ * This describes a complete RPC request
+ */
+struct rpc_rqst {
+	/*
+	 * This is the user-visible part
+	 */
+	struct rpc_xprt *	rq_xprt;		/* RPC client */
+	struct rpc_timeout	rq_timeout;		/* timeout parms */
+	struct rpc_iov		rq_snd_buf;		/* send buffer */
+	struct rpc_iov		rq_rcv_buf;		/* recv buffer */
+
+	/*
+	 * This is the private part
+	 */
+	struct rpc_task *	rq_task;	/* RPC task data */
+	__u32			rq_xid;		/* request XID */
+	struct rpc_rqst *	rq_next;	/* free list */
+	unsigned char		rq_gotit;	/* reply received */
+	unsigned char		rq_damaged;	/* being received */
+
+	/*
+	 * For authentication (e.g. auth_des)
+	 */
+	u32			rq_creddata[2];
+
+#ifdef RPC_PROFILE
+	unsigned long		rq_xtime;	/* when transmitted */
+#endif
+};
+#define rq_svec			rq_snd_buf.io_vec
+#define rq_snr			rq_snd_buf.io_nr
+#define rq_slen			rq_snd_buf.io_len
+#define rq_rvec			rq_rcv_buf.io_vec
+#define rq_rnr			rq_rcv_buf.io_nr
+#define rq_rlen			rq_rcv_buf.io_len
+
+struct rpc_xprt {
+	struct rpc_xprt *	link;		/* list of all clients */
+
+	struct file *		file;		/* VFS layer */
+	struct socket *		sock;		/* BSD socket layer */
+	struct sock *		inet;		/* INET layer */
+
+	struct rpc_timeout	timeout;	/* timeout parms */
+	struct sockaddr_in	addr;		/* server address */
+	int			prot;		/* IP protocol */
+
+	unsigned long		cong;		/* current congestion */
+	unsigned long		cwnd;		/* congestion window */
+	unsigned long		congtime;	/* hold cwnd until then */
+
+	struct rpc_wait_queue	sending;	/* requests waiting to send */
+	struct rpc_wait_queue	pending;	/* requests in flight */
+	struct rpc_wait_queue	backlog;	/* waiting for slot */
+	struct rpc_wait_queue	reconn;		/* waiting for reconnect */
+	struct rpc_rqst *	free;		/* free slots */
+	struct rpc_rqst		slot[RPC_MAXREQS];
+	unsigned char		connected;	/* TCP: connected */
+	unsigned char		write_space;	/* TCP: can send */
+	unsigned int		shutdown   : 1,	/* being shut down */
+				nocong	   : 1,	/* no congestion control */
+				stream     : 1,	/* TCP */
+				tcp_more   : 1,	/* more record fragments */
+				connecting : 1;	/* being reconnected */
+
+	/*
+	 * State of TCP reply receive stuff
+	 */
+	union {					/* record marker & XID */
+		u32		header[2];
+		u8 *		data[8];
+	}			tcp_recm;
+	struct rpc_rqst *	tcp_rqstp;
+	struct iovec		tcp_iovec[MAX_IOVEC];
+	u32			tcp_total;	/* overall record length */
+	u32			tcp_reclen;	/* fragment length */
+	u32			tcp_offset;	/* fragment offset */
+	u32			tcp_copied;	/* copied to request */
+
+	/*
+	 * TCP send stuff
+	 */
+	struct rpc_iov		snd_buf;	/* send buffer */
+	struct rpc_task *	snd_task;	/* Task blocked in send */
+
+	void			(*old_data_ready)(struct sock *, int);
+	void			(*old_state_change)(struct sock *);
+	void			(*old_write_space)(struct sock *);
+};
+#define tcp_reclen		tcp_recm.header[0]
+#define tcp_xid			tcp_recm.header[1]
+
+#ifdef __KERNEL__
+
+struct rpc_xprt *	xprt_create(struct file *socket,
+					struct sockaddr_in *addr,
+					struct rpc_timeout *toparms);
+struct rpc_xprt *	xprt_create_proto(int proto, struct sockaddr_in *addr,
+					struct rpc_timeout *toparms);
+int			xprt_destroy(struct rpc_xprt *);
+void			xprt_default_timeout(struct rpc_timeout *, int);
+void			xprt_set_timeout(struct rpc_timeout *, unsigned int,
+					unsigned long);
+
+int			xprt_reserve(struct rpc_task *);
+void			xprt_transmit(struct rpc_task *);
+void			xprt_receive(struct rpc_task *);
+int			xprt_adjust_timeout(struct rpc_timeout *);
+void			xprt_release(struct rpc_task *);
+void			xprt_reconnect(struct rpc_task *);
+
+#endif /* __KERNEL__*/
+
+#endif /* _LINUX_SUNRPC_XPRT_H */
diff -u --recursive --new-file v2.1.31/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.31/linux/include/linux/sysctl.h	Thu Mar 27 14:40:11 1997
+++ linux/include/linux/sysctl.h	Sat Apr  5 12:14:20 1997
@@ -28,64 +28,80 @@
 /* For internal pattern-matching use only: */
 #ifdef __KERNEL__
 #define CTL_ANY		-1	/* Matches any name */
-#define CTL_NONE		0
+#define CTL_NONE	0
 #endif
 
-#define CTL_KERN	1	/* General kernel info and control */
-#define CTL_VM		2	/* VM management */
-#define CTL_NET		3	/* Networking */
-#define CTL_PROC	4	/* Process info */
-#define CTL_FS		5	/* Filesystems */
-#define CTL_DEBUG	6	/* Debugging */
-#define CTL_DEV		7	/* Devices */
-#define CTL_MAXID	8
+enum
+{
+	CTL_KERN=1,		/* General kernel info and control */
+	CTL_VM,			/* VM management */
+	CTL_NET,		/* Networking */
+	CTL_PROC,		/* Process info */
+	CTL_FS,			/* Filesystems */
+	CTL_DEBUG,		/* Debugging */
+	CTL_DEV,		/* Devices */
+};
+
 
 /* CTL_KERN names: */
-#define KERN_OSTYPE	1	/* string: system version */
-#define KERN_OSRELEASE	2	/* string: system release */
-#define KERN_OSREV	3	/* int: system revision */
-#define KERN_VERSION	4	/* string: compile time info */
-#define KERN_SECUREMASK 5	/* struct: maximum rights mask */
-#define KERN_PROF 	6	/* table: profiling information */
-#define KERN_NODENAME   7
-#define KERN_DOMAINNAME 8
-#define KERN_NRINODE	9
-#define KERN_MAXINODE	10
-#define KERN_NRFILE	11
-#define KERN_MAXFILE	12
-#define KERN_MAXID	13
-#define KERN_SECURELVL	14	/* int: system security level */
-#define KERN_PANIC	15	/* int: panic timeout */
-#define KERN_REALROOTDEV 16	/* real root device to mount after initrd */
-#define KERN_NFSRNAME	17	/* NFS root name */
-#define KERN_NFSRADDRS	18	/* NFS root addresses */
-#define KERN_JAVA_INTERPRETER 19 /* path to Java(tm) interpreter */
-#define KERN_JAVA_APPLETVIEWER 20 /* path to Java(tm) appletviewer */
-#define KERN_SPARC_REBOOT 21 /* reboot command on Sparc */
+enum
+{
+	KERN_OSTYPE=1,		/* string: system version */
+	KERN_OSRELEASE,		/* string: system release */
+	KERN_OSREV,		/* int: system revision */
+	KERN_VERSION,		/* string: compile time info */
+	KERN_SECUREMASK,	/* struct: maximum rights mask */
+	KERN_PROF,		/* table: profiling information */
+	KERN_NODENAME,
+	KERN_DOMAINNAME,
+	KERN_NRINODE,
+	KERN_MAXINODE,
+	KERN_NRFILE,
+	KERN_MAXFILE,
+	KERN_SECURELVL,		/* int: system security level */
+	KERN_PANIC,		/* int: panic timeout */
+	KERN_REALROOTDEV,	/* real root device to mount after initrd */
+	KERN_NFSRNAME,		/* NFS root name */
+	KERN_NFSRADDRS,		/* NFS root addresses */
+	KERN_JAVA_INTERPRETER,	/* path to Java(tm) interpreter */
+	KERN_JAVA_APPLETVIEWER,	/* path to Java(tm) appletviewer */
+	KERN_SPARC_REBOOT,	/* reboot command on Sparc */
+	KERN_CTLALTDEL,		/* int: allow ctl-alt-del to reboot */
+	KERN_PRINTK,            /* sturct: control printk logging parameters */
+};
+
 
 /* CTL_VM names: */
-#define VM_SWAPCTL	1	/* struct: Set vm swapping control */
-#define VM_KSWAPD	2	/* struct: control background pageout */
-#define VM_FREEPG	3	/* struct: Set free page thresholds */
-#define VM_BDFLUSH	4	/* struct: Control buffer cache flushing */
-#define VM_MAXID	5
-#define VM_OVERCOMMIT_MEMORY 7	/* Turn off the virtual memory safety limit */
+enum
+{
+	VM_SWAPCTL=1,		/* struct: Set vm swapping control */
+	VM_KSWAPD,		/* struct: control background pageout */
+	VM_SWAPOUT,		/* int: Background pageout interval */
+	VM_FREEPG,		/* struct: Set free page thresholds */
+	VM_BDFLUSH,		/* struct: Control buffer cache flushing */
+	VM_OVERCOMMIT_MEMORY,	/* Turn off the virtual memory safety limit */
+};
+
 
 /* CTL_NET names: */
-#define NET_CORE        1
-#define NET_ETHER       2
-#define NET_802         3
-#define NET_UNIX        4
-#define NET_IPV4        5
-#define NET_IPX         6
-#define NET_ATALK       7
-#define NET_NETROM      8
-#define NET_AX25        9
-#define NET_BRIDGE	10
-#define NET_IPV6	11
-#define NET_ROSE	12
-#define NET_X25		13
-#define NET_TR		14
+enum
+{
+	NET_CORE=1,
+	NET_ETHER,
+	NET_802,
+	NET_UNIX,
+	NET_IPV4,
+	NET_IPX,
+	NET_ATALK,
+	NET_NETROM,
+	NET_AX25,
+	NET_BRIDGE,
+	NET_IPV6,
+	NET_ROSE,
+	NET_X25,
+	NET_TR,
+};
+
 
 /* /proc/sys/net/core */
 
@@ -218,7 +234,7 @@
 #ifdef __KERNEL__
 
 extern asmlinkage int sys_sysctl(struct __sysctl_args *);
-extern void init_sysctl(void);
+extern void sysctl_init(void);
 
 typedef struct ctl_table ctl_table;
 
diff -u --recursive --new-file v2.1.31/linux/init/main.c linux/init/main.c
--- v2.1.31/linux/init/main.c	Fri Apr  4 08:52:26 1997
+++ linux/init/main.c	Sat Apr  5 12:14:20 1997
@@ -909,7 +909,9 @@
 #ifdef __SMP__
 	smp_init();
 #endif
+#ifdef CONFIG_SYSCTL
 	sysctl_init();
+#endif
 	/* 
 	 *	We count on the initial thread going ok 
 	 *	Like idlers init is an unlocked kernel thread, which will
diff -u --recursive --new-file v2.1.31/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.31/linux/kernel/ksyms.c	Fri Apr  4 08:52:26 1997
+++ linux/kernel/ksyms.c	Thu Apr  3 10:23:56 1997
@@ -150,6 +150,7 @@
 EXPORT_SYMBOL(permission);
 EXPORT_SYMBOL(inode_setattr);
 EXPORT_SYMBOL(inode_change_ok);
+EXPORT_SYMBOL(get_hardblocksize);
 EXPORT_SYMBOL(set_blocksize);
 EXPORT_SYMBOL(getblk);
 EXPORT_SYMBOL(bread);
diff -u --recursive --new-file v2.1.31/linux/kernel/printk.c linux/kernel/printk.c
--- v2.1.31/linux/kernel/printk.c	Fri Apr  4 08:52:26 1997
+++ linux/kernel/printk.c	Sat Apr  5 12:14:20 1997
@@ -9,6 +9,7 @@
  * to the console.  Added hook for sending the console messages
  * elsewhere, in preparation for a serial line console (someday).
  * Ted Ts'o, 2/11/93.
+ * Modified for sysctl support, 1/8/97, Chris Horn.
  */
 
 #include <stdarg.h>
@@ -40,7 +41,12 @@
 
 unsigned long log_size = 0;
 struct wait_queue * log_wait = NULL;
+
+/* Keep together for sysctl support */
 int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
+int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
+int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
+int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
 
 struct console *console_drivers = NULL;
 static char log_buf[LOG_BUF_LEN];
@@ -141,17 +147,17 @@
 		logged_chars = 0;
 		break;
 	case 6:		/* Disable logging to console */
-		console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
+		console_loglevel = minimum_console_loglevel;
 		break;
 	case 7:		/* Enable logging to console */
-		console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
+		console_loglevel = default_console_loglevel;
 		break;
 	case 8:
 		error = -EINVAL;
 		if (len < 1 || len > 8)
 			goto out;
-		if (len < MINIMUM_CONSOLE_LOGLEVEL)
-			len = MINIMUM_CONSOLE_LOGLEVEL;
+		if (len < minimum_console_loglevel)
+			len = minimum_console_loglevel;
 		console_loglevel = len;
 		error = 0;
 		break;
@@ -190,7 +196,7 @@
 			) {
 				p -= 3;
 				p[0] = '<';
-				p[1] = DEFAULT_MESSAGE_LOGLEVEL + '0';
+				p[1] = default_message_loglevel + '0';
 				p[2] = '>';
 			} else
 				msg += 3;
diff -u --recursive --new-file v2.1.31/linux/kernel/sys.c linux/kernel/sys.c
--- v2.1.31/linux/kernel/sys.c	Fri Apr  4 08:52:26 1997
+++ linux/kernel/sys.c	Fri Apr  4 08:37:59 1997
@@ -893,13 +893,14 @@
 			gid_t *groups = current->groups;
 			do {
 				if (*groups == grp)
-					break;
+					goto out;
 				groups++;
 				i--;
 			} while (i);
 		}
 		return 0;
 	}
+out:
 	return 1;
 }
 
@@ -1138,7 +1139,6 @@
 
 asmlinkage int sys_umask(int mask)
 {
-	/* The xchg() isn't SMP-safe on x86 right now.. */
 	mask = xchg(&current->fs->umask, mask & S_IRWXUGO);
 	return mask;
 }
diff -u --recursive --new-file v2.1.31/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.31/linux/kernel/sysctl.c	Fri Apr  4 08:52:26 1997
+++ linux/kernel/sysctl.c	Sat Apr  5 12:14:20 1997
@@ -7,6 +7,8 @@
  * Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver.
  * Added kernel/java-{interpreter,appletviewer}, 96/5/10, Mike Shaver.
  * Dynamic registration fixes, Stephen Tweedie.
+ * Added kswapd-interval, ctrl-alt-del, printk stuff, 1/8/97, Chris Horn.
+ * Made sysctl support optional via CONFIG_SYSCTL, 1/10/97, Chris Horn.
  */
 
 #include <linux/config.h>
@@ -26,24 +28,43 @@
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_ROOT_NFS
+#include <linux/nfs_fs.h>
+#endif
+
+#ifdef CONFIG_SYSCTL
+
 /* External variables not in a header file. */
 extern int panic_timeout;
+extern int console_loglevel, default_message_loglevel;
+extern int minimum_console_loglevel, default_console_loglevel;
+extern int C_A_D, swapout_interval;
+extern int bdf_prm[], bdflush_min[], bdflush_max[];
+extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
+extern int sysctl_overcommit_memory;
 
-
-#ifdef CONFIG_ROOT_NFS
-#include <linux/nfs_fs.h>
+#ifdef __sparc__
+extern char reboot_command [];
 #endif
 
+static int parse_table(int *, int, void *, size_t *, void *, size_t,
+		       ctl_table *, void **);
+static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
+				    void *, size_t, void **);
+
+
 static ctl_table root_table[];
 static struct ctl_table_header root_table_header = 
 	{root_table, DNODE_SINGLE(&root_table_header)};
 
-static int parse_table(int *, int, void *, size_t *, void *, size_t,
-		       ctl_table *, void **);
-
 static ctl_table kern_table[];
 static ctl_table vm_table[];
 extern ctl_table net_table[];
+static ctl_table proc_table[];
+static ctl_table fs_table[];
+static ctl_table debug_table[];
+static ctl_table dev_table[];
+
 
 /* /proc declarations: */
 
@@ -96,24 +117,16 @@
 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
 #endif
 
-extern int bdf_prm[], bdflush_min[], bdflush_max[];
-
-static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
-				    void *, size_t, void **);
-
-extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
-extern int sysctl_overcommit_memory;
-
-#ifdef __sparc__
-extern char reboot_command [];
-#endif
-
 /* The default sysctl tables: */
 
 static ctl_table root_table[] = {
 	{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
 	{CTL_VM, "vm", NULL, 0, 0555, vm_table},
 	{CTL_NET, "net", NULL, 0, 0555, net_table},
+	{CTL_PROC, "proc", NULL, 0, 0555, proc_table},
+	{CTL_FS, "fs", NULL, 0, 0555, fs_table},
+	{CTL_DEBUG, "debug", NULL, 0, 0555, debug_table},
+        {CTL_DEV, "dev", NULL, 0, 0555, dev_table},
 	{0}
 };
 
@@ -160,6 +173,10 @@
 	{KERN_SPARC_REBOOT, "reboot-cmd", reboot_command,
 	 256, 0644, NULL, &proc_dostring, &sysctl_string },
 #endif
+	{KERN_CTLALTDEL, "ctrl-alt-del", &C_A_D, sizeof(int),
+	 0644, NULL, &proc_dointvec},
+	{KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int),
+	 0644, NULL, &proc_dointvec},
 	{0}
 };
 
@@ -168,6 +185,8 @@
 	 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
 	{VM_KSWAPD, "kswapd", 
 	 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
+	{VM_SWAPOUT, "kswapd-interval",
+	 &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec},
 	{VM_FREEPG, "freepages", 
 	 &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
 	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
@@ -178,6 +197,23 @@
 	{0}
 };
 
+static ctl_table proc_table[] = {
+	{0}
+};
+
+static ctl_table fs_table[] = {
+	{0}
+};
+
+static ctl_table debug_table[] = {
+	{0}
+};
+
+static ctl_table dev_table[] = {
+	{0}
+};  
+
+
 void sysctl_init(void)
 {
 #ifdef CONFIG_PROC_FS
@@ -942,4 +978,59 @@
 			return -EFAULT;
 	return 0;
 }
+
+
+#else /* CONFIG_SYSCTL */
+
+
+extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
+{
+	return -ENOSYS;
+}
+
+int sysctl_string(ctl_table *table, int *name, int nlen,
+		  void *oldval, size_t *oldlenp,
+		  void *newval, size_t newlen, void **context)
+{
+	return -ENOSYS;
+}
+
+int sysctl_intvec(ctl_table *table, int *name, int nlen,
+		void *oldval, size_t *oldlenp,
+		void *newval, size_t newlen, void **context)
+{
+	return -ENOSYS;
+}
+
+int proc_dostring(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	return -ENOSYS;
+}
+
+int proc_dointvec(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	return -ENOSYS;
+}
+
+int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+		    void *buffer, size_t *lenp)
+{
+	return -ENOSYS;
+}
+
+struct ctl_table_header * register_sysctl_table(ctl_table * table, 
+						int insert_at_head)
+{
+	return 0;
+}
+
+void unregister_sysctl_table(struct ctl_table_header * table)
+{
+}
+
+#endif /* CONFIG_SYSCTL */
+
+
 
diff -u --recursive --new-file v2.1.31/linux/net/802/Makefile linux/net/802/Makefile
--- v2.1.31/linux/net/802/Makefile	Sun Jan 26 02:07:50 1997
+++ linux/net/802/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,13 +8,16 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := 802.o
-O_OBJS	  = p8023.o sysctl_net_802.o
+O_OBJS	  = p8023.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_802.o
+endif
 
 ifeq ($(CONFIG_LLC),y)
 SUB_DIRS += transit
 O_OBJS += llc_sendpdu.o llc_utility.o cl2llc.o
 OX_OBJS += llc_macinit.o
-
 endif
 
 ifdef CONFIG_TR
diff -u --recursive --new-file v2.1.31/linux/net/Makefile linux/net/Makefile
--- v2.1.31/linux/net/Makefile	Sun Feb  2 05:18:48 1997
+++ linux/net/Makefile	Sat Apr  5 12:14:21 1997
@@ -9,7 +9,7 @@
 
 MOD_SUB_DIRS := ipv4
 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
-		netrom rose lapb x25 wanrouter #decnet
+		netrom rose lapb x25 wanrouter sunrpc #decnet
 SUB_DIRS     := core ethernet unix
 MOD_LIST_NAME := NET_MISC_MODULES
 
@@ -97,6 +97,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SUNRPC),y)
+SUB_DIRS += sunrpc
+else
+  ifeq ($(CONFIG_SUNRPC),m)
+  MOD_SUB_DIRS += sunrpc
+  endif
+endif
+
 # We must attach netsyms.o to socket.o, as otherwise there is nothing
 # to pull the object file from the archive.
 
@@ -111,9 +119,13 @@
 endif
 
 L_TARGET     := network.a
-L_OBJS	     := $(SOCK) protocols.o sysctl_net.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+L_OBJS	     := $(SOCK) protocols.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
 
 M_OBJS	     :=
+
+ifeq ($(CONFIG_SYSCTL),y)
+L_OBJS += sysctl_net.o
+endif
 
 CONFIG_NETLINK_BUILTIN :=
 CONFIG_NETLINK_MODULE :=
diff -u --recursive --new-file v2.1.31/linux/net/appletalk/Makefile linux/net/appletalk/Makefile
--- v2.1.31/linux/net/appletalk/Makefile	Mon Apr  1 21:43:08 1996
+++ linux/net/appletalk/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,8 +8,12 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := appletalk.o
-O_OBJS	 := aarp.o ddp.o sysctl_net_atalk.o
+O_OBJS	 := aarp.o ddp.o
 M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_atalk.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/ax25/Makefile linux/net/ax25/Makefile
--- v2.1.31/linux/net/ax25/Makefile	Thu Mar 27 14:40:13 1997
+++ linux/net/ax25/Makefile	Sat Apr  5 12:14:21 1997
@@ -11,13 +11,17 @@
 O_TARGET := ax25.o
 O_OBJS	 := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \
 	    ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \
-	    ax25_subr.o ax25_timer.o ax25_uid.o sysctl_net_ax25.o
+	    ax25_subr.o ax25_timer.o ax25_uid.o
 M_OBJS   := $(O_TARGET)
 
 OX_OBJS  += af_ax25.o
 
 ifeq ($(CONFIG_AX25_DAMA_SLAVE),y)
 O_OBJS += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o
+endif
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_ax25.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.31/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.31/linux/net/ax25/af_ax25.c	Thu Mar 27 14:40:13 1997
+++ linux/net/ax25/af_ax25.c	Sat Apr  5 12:14:21 1997
@@ -1797,7 +1797,9 @@
 	ax25_packet_type.type = htons(ETH_P_AX25);
 	dev_add_pack(&ax25_packet_type);
 	register_netdevice_notifier(&ax25_dev_notifier);
+#ifdef CONFIG_SYSCTL
 	ax25_register_sysctl();
+#endif
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_ax25_route);
@@ -1828,8 +1830,9 @@
 	ax25_uid_free();
 	ax25_dev_free();
 
+#ifdef CONFIG_SYSCTL
 	ax25_unregister_sysctl();
-
+#endif
 	unregister_netdevice_notifier(&ax25_dev_notifier);
 
 	ax25_packet_type.type = htons(ETH_P_AX25);
diff -u --recursive --new-file v2.1.31/linux/net/ax25/ax25_dev.c linux/net/ax25/ax25_dev.c
--- v2.1.31/linux/net/ax25/ax25_dev.c	Thu Mar 27 14:40:13 1997
+++ linux/net/ax25/ax25_dev.c	Sat Apr  5 12:14:21 1997
@@ -81,7 +81,9 @@
 		return;
 	}
 
+#ifdef CONFIG_SYSCTL
 	ax25_unregister_sysctl();
+#endif
 
 	memset(ax25_dev, 0x00, sizeof(*ax25_dev));
 
@@ -112,7 +114,9 @@
 	ax25_dev_list  = ax25_dev;
 	restore_flags(flags);
 
+#ifdef CONFIG_SYSCTL
 	ax25_register_sysctl();
+#endif
 }
 
 void ax25_dev_device_down(struct device *dev)
@@ -123,7 +127,9 @@
 	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
 		return;
 
+#ifdef CONFIG_SYSCTL
 	ax25_unregister_sysctl();
+#endif
 
 	save_flags(flags); cli();
 
@@ -142,7 +148,9 @@
 		ax25_dev_list = s->next;
 		restore_flags(flags);
 		kfree_s(ax25_dev, sizeof(ax25_dev));
+#ifdef CONFIG_SYSCTL
 		ax25_register_sysctl();
+#endif
 		return;
 	}
 
@@ -151,7 +159,9 @@
 			s->next = ax25_dev->next;
 			restore_flags(flags);
 			kfree_s(ax25_dev, sizeof(ax25_dev));
+#ifdef CONFIG_SYSCTL
 			ax25_register_sysctl();
+#endif
 			return;
 		}
 
@@ -159,7 +169,9 @@
 	}
 
 	restore_flags(flags);
+#ifdef CONFIG_SYSCTL
 	ax25_register_sysctl();
+#endif
 }
 
 int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
diff -u --recursive --new-file v2.1.31/linux/net/bridge/Makefile linux/net/bridge/Makefile
--- v2.1.31/linux/net/bridge/Makefile	Mon Jun  3 02:42:41 1996
+++ linux/net/bridge/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,8 +8,12 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := bridge.o
-O_OBJS	 := br.o br_tree.o sysctl_net_bridge.o
+O_OBJS	 := br.o br_tree.o
 M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_bridge.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/core/Makefile linux/net/core/Makefile
--- v2.1.31/linux/net/core/Makefile	Thu Dec 12 06:54:23 1996
+++ linux/net/core/Makefile	Sat Apr  5 12:14:21 1997
@@ -9,8 +9,12 @@
 
 O_TARGET := core.o
 
-O_OBJS	:= sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o dst.o scm.o \
+O_OBJS	:= sock.o skbuff.o iovec.o datagram.o dst.o scm.o \
 	neighbour.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_core.o
+endif
 
 ifdef CONFIG_NET
 
diff -u --recursive --new-file v2.1.31/linux/net/ethernet/Makefile linux/net/ethernet/Makefile
--- v2.1.31/linux/net/ethernet/Makefile	Mon Apr  1 21:43:08 1996
+++ linux/net/ethernet/Makefile	Sat Apr  5 12:14:21 1997
@@ -9,7 +9,11 @@
 
 O_TARGET := ethernet.o
 
-OBJS	:= eth.o sysctl_net_ether.o
+OBJS	:= eth.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+OBJS += sysctl_net_ether.o
+endif
 
 ifdef CONFIG_IPX
 OBJ2	:= pe2.o
diff -u --recursive --new-file v2.1.31/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.31/linux/net/ipv4/sysctl_net_ipv4.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/sysctl_net_ipv4.c	Sat Apr  5 12:14:21 1997
@@ -39,6 +39,9 @@
 				void *buffer, size_t *lenp);
 
 struct ipv4_config ipv4_config = { 1, 1, 1, 1, };
+
+#ifdef CONFIG_SYSCTL
+
 struct ipv4_config ipv4_def_router_config = { 0, 1, 1, 1, 1, 1, 1, };
 struct ipv4_config ipv4_def_host_config = { 1, 1, 1, 1, };
 
@@ -121,3 +124,5 @@
          &proc_dointvec},
 	{0}
 };
+
+#endif /* CONFIG_SYSCTL */
diff -u --recursive --new-file v2.1.31/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.31/linux/net/ipv6/ipv6_sockglue.c	Thu Mar 27 14:40:16 1997
+++ linux/net/ipv6/ipv6_sockglue.c	Sat Apr  5 12:14:21 1997
@@ -228,7 +228,7 @@
 	return 0;
 }
 
-#ifdef MODULE
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
 
 /*
  *	sysctl registration functions defined in sysctl_net_ipv6.c
@@ -244,7 +244,7 @@
 
 	dev_add_pack(&ipv6_packet_type);
 
-#ifdef MODULE
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
 	ipv6_sysctl_register();
 #endif
 
@@ -258,9 +258,13 @@
 {
 	unregister_netdevice_notifier(&ipv6_dev_notf);
 	dev_remove_pack(&ipv6_packet_type);
+#ifdef CONFIG_SYSCTL
 	ipv6_sysctl_unregister();	
+#endif
 	ip6_route_cleanup();
 	ndisc_cleanup();
 	addrconf_cleanup();	
 }
 #endif
+
+
diff -u --recursive --new-file v2.1.31/linux/net/ipv6/sysctl_net_ipv6.c linux/net/ipv6/sysctl_net_ipv6.c
--- v2.1.31/linux/net/ipv6/sysctl_net_ipv6.c	Thu Mar 27 14:40:17 1997
+++ linux/net/ipv6/sysctl_net_ipv6.c	Sat Apr  5 12:14:21 1997
@@ -33,6 +33,8 @@
 	30*HZ,				/* rt gc period		*/
 };
 
+#ifdef CONFIG_SYSCTL
+
 int ipv6_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
 			   void *buffer, size_t *lenp)
 {
@@ -140,3 +142,8 @@
 	unregister_sysctl_table(ipv6_sysctl_header);
 }
 #endif	/* MODULE */
+
+#endif /* CONFIG_SYSCTL */
+
+
+
diff -u --recursive --new-file v2.1.31/linux/net/ipx/Makefile linux/net/ipx/Makefile
--- v2.1.31/linux/net/ipx/Makefile	Mon Apr  1 21:43:08 1996
+++ linux/net/ipx/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,8 +8,12 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := ipx.o
-O_OBJS   := af_ipx.o sysctl_net_ipx.o
+O_OBJS   := af_ipx.o
 M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_ipx.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/netrom/Makefile linux/net/netrom/Makefile
--- v2.1.31/linux/net/netrom/Makefile	Sun Nov 10 09:12:58 1996
+++ linux/net/netrom/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,8 +8,12 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := netrom.o
-O_OBJS	 := af_netrom.o sysctl_net_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
+O_OBJS	 := af_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
 M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_netrom.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.31/linux/net/netrom/af_netrom.c	Thu Mar 27 14:40:17 1997
+++ linux/net/netrom/af_netrom.c	Sat Apr  5 12:14:21 1997
@@ -1376,7 +1376,9 @@
 	for (i = 0; i < 4; i++)
 		register_netdev(&dev_nr[i]);
 
+#ifdef CONFIG_SYSCTL
 	nr_register_sysctl();
+#endif
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_nr);
@@ -1411,8 +1413,9 @@
 
 	unregister_netdevice_notifier(&nr_dev_notifier);
 
+#ifdef CONFIG_SYSCTL
 	nr_unregister_sysctl();
-
+#endif
 	sock_unregister(AF_NETROM);
 
 	for (i = 0; i < 4; i++) {
diff -u --recursive --new-file v2.1.31/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.31/linux/net/netsyms.c	Fri Apr  4 08:52:29 1997
+++ linux/net/netsyms.c	Thu Apr  3 19:16:54 1997
@@ -150,10 +150,6 @@
 EXPORT_SYMBOL(scm_detach_fds);
 #endif
 
-#ifdef CONFIG_SMB_FS_MODULE
-EXPORT_SYMBOL(scm_detach_fds);
-#endif
-
 #ifdef CONFIG_INET
 /* Internet layer registration */
 EXPORT_SYMBOL(inet_add_protocol);
@@ -264,7 +260,6 @@
 EXPORT_SYMBOL(register_trdev);
 EXPORT_SYMBOL(unregister_trdev);
 EXPORT_SYMBOL(init_trdev);
-EXPORT_SYMBOL(csum_partial_copy);
 #endif
                   
 #ifdef CONFIG_NET_ALIAS
diff -u --recursive --new-file v2.1.31/linux/net/rose/Makefile linux/net/rose/Makefile
--- v2.1.31/linux/net/rose/Makefile	Sun Nov 10 09:12:58 1996
+++ linux/net/rose/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,8 +8,12 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := rose.o
-O_OBJS	 := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
+O_OBJS	 := af_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
 M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_rose.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.31/linux/net/rose/af_rose.c	Thu Mar 27 14:40:17 1997
+++ linux/net/rose/af_rose.c	Sat Apr  5 12:14:21 1997
@@ -1398,7 +1398,9 @@
 	for (i = 0; i < 6; i++)
 		register_netdev(&dev_rose[i]);
 
+#ifdef CONFIG_SYSCTL
 	rose_register_sysctl();
+#endif
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_rose);
@@ -1436,8 +1438,9 @@
 	if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
 		ax25_listen_release(&rose_callsign, NULL);
 
+#ifdef CONFIG_SYSCTL
 	rose_unregister_sysctl();
-
+#endif
 	unregister_netdevice_notifier(&rose_dev_notifier);
 
 	sock_unregister(AF_ROSE);
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/Makefile linux/net/sunrpc/Makefile
--- v2.1.31/linux/net/sunrpc/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/Makefile	Sat Mar  1 13:41:10 1997
@@ -0,0 +1,23 @@
+#
+# Makefile for Linux kernel SUN RPC
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := sunrpc.o
+O_OBJS   := clnt.o xprt.o sched.o \
+	    auth.o auth_null.o auth_unix.o \
+	    svc.o svcsock.o svcauth.o \
+	    pmap_clnt.o xdr.o sysctl.o
+OX_OBJS  := sunrpc_syms.o
+
+ifeq ($(CONFIG_PROC_FS),y)
+  O_OBJS     += stats.o
+endif
+
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/auth.c linux/net/sunrpc/auth.c
--- v2.1.31/linux/net/sunrpc/auth.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/auth.c	Fri Dec 13 06:53:18 1996
@@ -0,0 +1,295 @@
+/*
+ * linux/fs/nfs/rpcauth.c
+ *
+ * Generic RPC authentication API.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY	RPCDBG_AUTH
+#endif
+
+#define RPC_MAXFLAVOR	8
+
+static struct rpc_authops *	auth_flavors[RPC_MAXFLAVOR] = {
+	&authnull_ops,		/* AUTH_NULL */
+	&authunix_ops,		/* AUTH_UNIX */
+	NULL,			/* others can be loadable modules */
+};
+
+int
+rpcauth_register(struct rpc_authops *ops)
+{
+	unsigned int	flavor;
+
+	if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR)
+		return -EINVAL;
+	if (auth_flavors[flavor] != NULL)
+		return -EPERM;		/* what else? */
+	auth_flavors[flavor] = ops;
+	return 0;
+}
+
+int
+rpcauth_unregister(struct rpc_authops *ops)
+{
+	unsigned int	flavor;
+
+	if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR)
+		return -EINVAL;
+	if (auth_flavors[flavor] != ops)
+		return -EPERM;		/* what else? */
+	auth_flavors[flavor] = NULL;
+	return 0;
+}
+
+struct rpc_auth *
+rpcauth_create(unsigned int flavor, struct rpc_clnt *clnt)
+{
+	struct rpc_authops	*ops;
+
+	if (flavor >= RPC_MAXFLAVOR || !(ops = auth_flavors[flavor]))
+		return NULL;
+	clnt->cl_auth = ops->create(clnt);
+	return clnt->cl_auth;
+}
+
+void
+rpcauth_destroy(struct rpc_auth *auth)
+{
+	auth->au_ops->destroy(auth);
+}
+
+/*
+ * Initialize RPC credential cache
+ */
+void
+rpcauth_init_credcache(struct rpc_auth *auth)
+{
+	memset(auth->au_credcache, 0, sizeof(auth->au_credcache));
+	auth->au_nextgc = jiffies + (auth->au_expire >> 1);
+}
+
+/*
+ * Clear the RPC credential cache
+ */
+void
+rpcauth_free_credcache(struct rpc_auth *auth)
+{
+	struct rpc_cred	**q, *cred;
+	void		(*destroy)(struct rpc_cred *);
+	int		i;
+
+	if (!(destroy = auth->au_ops->crdestroy))
+		destroy = (void (*)(struct rpc_cred *)) rpc_free;
+
+	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
+		q = &auth->au_credcache[i];
+		while ((cred = *q) != NULL) {
+			*q = cred->cr_next;
+			destroy(cred);
+		}
+	}
+}
+
+/*
+ * Remove stale credentials. Avoid sleeping inside the loop.
+ */
+static void
+rpcauth_gc_credcache(struct rpc_auth *auth)
+{
+	struct rpc_cred	**q, *cred, *free = NULL;
+	int		i, safe = 0;
+
+	dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
+	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
+		q = &auth->au_credcache[i];
+		while ((cred = *q) != NULL) {
+			if (++safe > 500) {
+				printk("RPC: rpcauth_gc_credcache looping!\n");
+				break;
+			}
+			if (!cred->cr_count && cred->cr_expire < jiffies) {
+				*q = cred->cr_next;
+				cred->cr_next = free;
+				free = cred;
+				continue;
+			}
+			q = &cred->cr_next;
+		}
+	}
+	while ((cred = free) != NULL) {
+		free = cred->cr_next;
+		rpc_free(cred);
+	}
+	auth->au_nextgc = jiffies + auth->au_expire;
+}
+
+/*
+ * Insert credential into cache
+ */
+inline void
+rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+	int		nr;
+
+	nr = (cred->cr_uid % RPC_CREDCACHE_NR);
+	cred->cr_next = auth->au_credcache[nr];
+	auth->au_credcache[nr] = cred;
+	cred->cr_expire = jiffies + auth->au_expire;
+	cred->cr_count++;
+}
+
+/*
+ * Look up a process' credentials in the authentication cache
+ */
+static struct rpc_cred *
+rpcauth_lookup_credcache(struct rpc_task *task)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+	struct rpc_cred	**q, *cred = NULL;
+	int		nr;
+
+	nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR);
+
+	if (auth->au_nextgc < jiffies)
+		rpcauth_gc_credcache(auth);
+
+	q = &auth->au_credcache[nr];
+	while ((cred = *q) != NULL) {
+		if (auth->au_ops->crmatch(task, cred)) {
+			*q = cred->cr_next;
+			break;
+		}
+		q = &cred->cr_next;
+	}
+
+	if (!cred)
+		cred = auth->au_ops->crcreate(task);
+
+	rpcauth_insert_credcache(auth, cred);
+
+	return (struct rpc_cred *) cred;
+}
+
+/*
+ * Remove cred handle from cache
+ */
+static inline void
+rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+	struct rpc_cred	**q, *cr;
+	int		nr;
+
+	nr = (cred->cr_uid % RPC_CREDCACHE_NR);
+	q = &auth->au_credcache[nr];
+	while ((cr = *q) != NULL) {
+		if (cred == cr) {
+			*q = cred->cr_next;
+			return;
+		}
+		q = &cred->cr_next;
+	}
+}
+
+struct rpc_cred *
+rpcauth_lookupcred(struct rpc_task *task)
+{
+	dprintk("RPC: %4d looking up %s cred\n",
+		task->tk_pid, task->tk_auth->au_ops->au_name);
+	return task->tk_cred = rpcauth_lookup_credcache(task);
+}
+
+int
+rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+
+	dprintk("RPC: %4d matching %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+	return auth->au_ops->crmatch(task, cred);
+}
+
+void
+rpcauth_holdcred(struct rpc_task *task)
+{
+	dprintk("RPC: %4d holding %s cred %p\n",
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
+	if (task->tk_cred)
+		task->tk_cred->cr_count++;
+}
+
+void
+rpcauth_releasecred(struct rpc_task *task)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+	struct rpc_cred	*cred;
+
+	dprintk("RPC: %4d releasing %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+	if ((cred = task->tk_cred) != NULL) {
+		cred->cr_count--;
+		if (cred->cr_flags & RPCAUTH_CRED_DEAD) {
+			rpcauth_remove_credcache(auth, cred);
+			if (!cred->cr_count)
+				auth->au_ops->crdestroy(cred);
+		}
+		task->tk_cred = NULL;
+	}
+}
+
+u32 *
+rpcauth_marshcred(struct rpc_task *task, u32 *p)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+
+	dprintk("RPC: %4d marshaling %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+	return auth->au_ops->crmarshal(task, p,
+				task->tk_flags & RPC_CALL_REALUID);
+}
+
+u32 *
+rpcauth_checkverf(struct rpc_task *task, u32 *p)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+
+	dprintk("RPC: %4d validating %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+	return auth->au_ops->crvalidate(task, p);
+}
+
+int
+rpcauth_refreshcred(struct rpc_task *task)
+{
+	struct rpc_auth	*auth = task->tk_auth;
+
+	dprintk("RPC: %4d refreshing %s cred %p\n",
+		task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+	task->tk_status = auth->au_ops->crrefresh(task);
+	return task->tk_status;
+}
+
+void
+rpcauth_invalcred(struct rpc_task *task)
+{
+	dprintk("RPC: %4d invalidating %s cred %p\n",
+		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
+	if (task->tk_cred)
+		task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+}
+
+int
+rpcauth_uptodatecred(struct rpc_task *task)
+{
+	return !(task->tk_cred) ||
+		(task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
+}
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/auth_null.c linux/net/sunrpc/auth_null.c
--- v2.1.31/linux/net/sunrpc/auth_null.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/auth_null.c	Wed Oct 30 00:39:45 1996
@@ -0,0 +1,131 @@
+/*
+ * linux/net/sunrpc/rpcauth_null.c
+ *
+ * AUTH_NULL authentication. Really :-)
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/utsname.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY	RPCDBG_AUTH
+#endif
+
+static struct rpc_auth *
+nul_create(struct rpc_clnt *clnt)
+{
+	struct rpc_auth	*auth;
+
+	dprintk("RPC: creating NULL authenticator for client %p\n", clnt);
+	if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth))))
+		return NULL;
+	auth->au_cslack = 4;
+	auth->au_rslack = 2;
+	auth->au_ops = &authnull_ops;
+	auth->au_expire = 1800 * HZ;
+	rpcauth_init_credcache(auth);
+
+	return (struct rpc_auth *) auth;
+}
+
+static void
+nul_destroy(struct rpc_auth *auth)
+{
+	dprintk("RPC: destroying NULL authenticator %p\n", auth);
+	rpc_free(auth);
+}
+
+/*
+ * Create NULL creds for current process
+ */
+static struct rpc_cred *
+nul_create_cred(struct rpc_task *task)
+{
+	struct rpc_cred	*cred;
+
+	if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred))))
+		return NULL;
+	cred->cr_count = 0;
+	cred->cr_flags = RPCAUTH_CRED_UPTODATE;
+
+	return cred;
+}
+
+/*
+ * Destroy cred handle.
+ */
+static void
+nul_destroy_cred(struct rpc_cred *cred)
+{
+	rpc_free(cred);
+}
+
+/*
+ * Match cred handle against current process
+ */
+static int
+nul_match(struct rpc_task *task, struct rpc_cred *cred)
+{
+	return 1;
+}
+
+/*
+ * Marshal credential.
+ */
+static u32 *
+nul_marshal(struct rpc_task *task, u32 *p, int ruid)
+{
+	*p++ = htonl(RPC_AUTH_NULL);
+	*p++ = 0;
+	*p++ = htonl(RPC_AUTH_NULL);
+	*p++ = 0;
+
+	return p;
+}
+
+/*
+ * Refresh credential. This is a no-op for AUTH_NULL
+ */
+static int
+nul_refresh(struct rpc_task *task)
+{
+	return task->tk_status = -EACCES;
+}
+
+static u32 *
+nul_validate(struct rpc_task *task, u32 *p)
+{
+	u32		n = ntohl(*p++);
+
+	if (n != RPC_AUTH_NULL) {
+		printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
+		return NULL;
+	}
+	if ((n = ntohl(*p++)) != 0) {
+		printk("RPC: bad verf size: %ld\n", (unsigned long) n);
+		return NULL;
+	}
+
+	return p;
+}
+
+struct rpc_authops	authnull_ops = {
+	RPC_AUTH_NULL,
+#ifdef RPC_DEBUG
+	"NULL",
+#endif
+	nul_create,
+	nul_destroy,
+	nul_create_cred,
+	nul_destroy_cred,
+	nul_match,
+	nul_marshal,
+	nul_refresh,
+	nul_validate
+};
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/auth_unix.c linux/net/sunrpc/auth_unix.c
--- v2.1.31/linux/net/sunrpc/auth_unix.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/auth_unix.c	Wed Oct 30 00:39:45 1996
@@ -0,0 +1,238 @@
+/*
+ * linux/net/sunrpc/rpcauth_unix.c
+ *
+ * UNIX-style authentication; no AUTH_SHORT support
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/utsname.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/auth.h>
+
+struct unx_cred {
+	struct rpc_cred		uc_base;
+	uid_t			uc_fsuid;
+	gid_t			uc_gid, uc_fsgid;
+	gid_t			uc_gids[16];
+};
+#define uc_uid			uc_base.cr_uid
+#define uc_count		uc_base.cr_count
+#define uc_flags		uc_base.cr_flags
+#define uc_expire		uc_base.cr_expire
+
+#define UNX_CRED_EXPIRE		(60 * HZ)
+
+#ifndef DONT_FILLIN_HOSTNAME
+/* # define UNX_MAXNODENAME	(sizeof(system_utsname.nodename)-1) */
+# define UNX_MAXNODENAME	32
+# define UNX_WRITESLACK		(21 + (UNX_MAXNODENAME >> 2))
+#else
+# define UNX_WRITESLACK		20
+#endif
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY	RPCDBG_AUTH
+#endif
+
+static struct rpc_auth *
+unx_create(struct rpc_clnt *clnt)
+{
+	struct rpc_auth	*auth;
+
+	dprintk("RPC: creating UNIX authenticator for client %p\n", clnt);
+	if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth))))
+		return NULL;
+	auth->au_cslack = UNX_WRITESLACK;
+	auth->au_rslack = 2;	/* assume AUTH_NULL verf */
+	auth->au_expire = UNX_CRED_EXPIRE;
+	auth->au_ops = &authunix_ops;
+
+	rpcauth_init_credcache(auth);
+
+	return auth;
+}
+
+static void
+unx_destroy(struct rpc_auth *auth)
+{
+	dprintk("RPC: destroying UNIX authenticator %p\n", auth);
+	rpcauth_free_credcache(auth);
+	rpc_free(auth);
+}
+
+static struct rpc_cred *
+unx_create_cred(struct rpc_task *task)
+{
+	struct unx_cred	*cred;
+	int		i;
+
+	dprintk("RPC:      allocating UNIX cred for uid %d gid %d\n",
+				current->uid, current->gid);
+
+	if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred))))
+		return NULL;
+
+	cred->uc_count = 0;
+	cred->uc_flags = RPCAUTH_CRED_UPTODATE;
+	if (RPC_DO_ROOTOVERRIDE(task)) {
+		cred->uc_uid = cred->uc_fsuid = 0;
+		cred->uc_gid = cred->uc_fsgid = 0;
+		cred->uc_gids[0] = NOGROUP;
+	} else {
+		cred->uc_uid = current->uid;
+		cred->uc_gid = current->gid;
+		cred->uc_fsuid = current->fsuid;
+		cred->uc_fsgid = current->fsgid;
+		for (i = 0; i < 16 && i < NGROUPS; i++)
+			cred->uc_gids[i] = (gid_t) current->groups[i];
+	}
+
+	return (struct rpc_cred *) cred;
+}
+
+struct rpc_cred *
+authunix_fake_cred(struct rpc_task *task, uid_t uid, gid_t gid)
+{
+	struct unx_cred	*cred;
+
+	dprintk("RPC:      allocating fake UNIX cred for uid %d gid %d\n",
+				uid, gid);
+
+	if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred))))
+		return NULL;
+
+	cred->uc_count = 1;
+	cred->uc_flags = RPCAUTH_CRED_DEAD|RPCAUTH_CRED_UPTODATE;
+	cred->uc_uid   = uid;
+	cred->uc_gid   = gid;
+	cred->uc_fsuid = uid;
+	cred->uc_fsgid = gid;
+	cred->uc_gids[0] = (gid_t) NOGROUP;
+
+	return task->tk_cred = (struct rpc_cred *) cred;
+}
+
+static void
+unx_destroy_cred(struct rpc_cred *cred)
+{
+	rpc_free(cred);
+}
+
+/*
+ * Match credentials against current process creds.
+ * The root_override argument takes care of cases where the caller may
+ * request root creds (e.g. for NFS swapping).
+ */
+static int
+unx_match(struct rpc_task * task, struct rpc_cred *rcred)
+{
+	struct unx_cred	*cred = (struct unx_cred *) rcred;
+	int		i;
+
+	if (!RPC_DO_ROOTOVERRIDE(task)) {
+		if (cred->uc_uid != current->uid
+		 || cred->uc_gid != current->gid
+		 || cred->uc_fsuid != current->fsuid
+		 || cred->uc_fsgid != current->fsgid)
+			return 0;
+
+		for (i = 0; i < 16 && i < NGROUPS; i++)
+			if (cred->uc_gids[i] != (gid_t) current->groups[i])
+				return 0;
+		return 1;
+	}
+	return (cred->uc_uid == 0 && cred->uc_fsuid == 0
+	     && cred->uc_gid == 0 && cred->uc_fsgid == 0
+	     && cred->uc_gids[0] == (gid_t) NOGROUP);
+}
+
+/*
+ * Marshal credentials.
+ * Maybe we should keep a cached credential for performance reasons.
+ */
+static u32 *
+unx_marshal(struct rpc_task *task, u32 *p, int ruid)
+{
+	struct unx_cred	*cred = (struct unx_cred *) task->tk_cred;
+	u32		*base, *hold;
+	int		i, n;
+
+	*p++ = htonl(RPC_AUTH_UNIX);
+	base = p++;
+	*p++ = htonl(jiffies/HZ);
+#ifndef DONT_FILLIN_HOSTNAME
+	if ((n = strlen((char *) system_utsname.nodename)) > UNX_MAXNODENAME)
+		n = UNX_MAXNODENAME;
+	*p++ = htonl(n);
+	memcpy(p, system_utsname.nodename, n);
+	p += (n + 3) >> 2;
+#else
+	*p++ = 0;
+#endif
+	if (ruid) {
+		*p++ = htonl((u32) cred->uc_uid);
+		*p++ = htonl((u32) cred->uc_gid);
+	} else {
+		*p++ = htonl((u32) cred->uc_fsuid);
+		*p++ = htonl((u32) cred->uc_fsgid);
+	}
+	hold = p++;
+	for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
+		*p++ = htonl((u32) cred->uc_gids[i]);
+	*hold = htonl(p - hold - 1);		/* gid array length */
+	*base = htonl((p - base - 1) << 2);	/* cred length */
+
+	*p++ = htonl(RPC_AUTH_NULL);
+	*p++ = htonl(0);
+
+	return p;
+}
+
+/*
+ * Refresh credentials. This is a no-op for AUTH_UNIX
+ */
+static int
+unx_refresh(struct rpc_task *task)
+{
+	task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
+	return task->tk_status = -EACCES;
+}
+
+static u32 *
+unx_validate(struct rpc_task *task, u32 *p)
+{
+	u32		n = ntohl(*p++);
+
+	if (n != RPC_AUTH_NULL && n != RPC_AUTH_UNIX && n != RPC_AUTH_SHORT) {
+		printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
+		return NULL;
+	}
+	if ((n = ntohl(*p++)) > 400) {
+		printk("RPC: giant verf size: %ld\n", (unsigned long) n);
+		return NULL;
+	}
+	task->tk_auth->au_rslack = (n >> 2) + 2;
+	p += (n >> 2);
+
+	return p;
+}
+
+struct rpc_authops	authunix_ops = {
+	RPC_AUTH_UNIX,
+#ifdef RPC_DEBUG
+	"UNIX",
+#endif
+	unx_create,
+	unx_destroy,
+	unx_create_cred,
+	unx_destroy_cred,
+	unx_match,
+	unx_marshal,
+	unx_refresh,
+	unx_validate
+};
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.31/linux/net/sunrpc/clnt.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/clnt.c	Thu Mar 20 08:09:20 1997
@@ -0,0 +1,761 @@
+/*
+ *  linux/net/sunrpc/rpcclnt.c
+ *
+ *  This file contains the high-level RPC interface.
+ *  It is modeled as a finite state machine to support both synchronous
+ *  and asynchronous requests.
+ *
+ *  -	RPC header generation and argument serialization.
+ *  -	Credential refresh.
+ *  -	TCP reconnect handling (when finished).
+ *  -	Retry of operation when it is suspected the operation failed because
+ *	of uid squashing on the server, or when the credentials were stale
+ *	and need to be refreshed, or when a packet was damaged in transit.
+ *	This may be have to be moved to the VFS layer.
+ *
+ *  NB: BSD uses a more intelligent approach to guessing when a request
+ *  or reply has been lost by keeping the RTO estimate for each procedure.
+ *  We currently make do with a constant timeout value.
+ *
+ *  Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
+ *  Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+
+#include <linux/sunrpc/clnt.h>
+
+
+#define RPC_SLACK_SPACE		1024	/* total overkill */
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY	RPCDBG_CALL
+#endif
+
+static struct wait_queue *	destroy_wait = NULL;
+
+
+static void	call_bind(struct rpc_task *task);
+static void	call_reserve(struct rpc_task *task);
+static void	call_reserveresult(struct rpc_task *task);
+static void	call_allocate(struct rpc_task *task);
+static void	call_encode(struct rpc_task *task);
+static void	call_decode(struct rpc_task *task);
+static void	call_transmit(struct rpc_task *task);
+static void	call_receive(struct rpc_task *task);
+static void	call_status(struct rpc_task *task);
+static void	call_refresh(struct rpc_task *task);
+static void	call_refreshresult(struct rpc_task *task);
+static void	call_timeout(struct rpc_task *task);
+static void	call_reconnect(struct rpc_task *task);
+static u32 *	call_header(struct rpc_task *task);
+static u32 *	call_verify(struct rpc_task *task);
+
+#define _S(nr)	(1 << ((nr) - 1))
+
+/*
+ * Create an RPC client
+ * FIXME: This should also take a flags argument (as in task->tk_flags).
+ * It's called (among others) from pmap_create_client, which may in
+ * turn be called by an async task. In this case, rpciod should not be
+ * made to sleep too long.
+ */
+struct rpc_clnt *
+rpc_create_client(struct rpc_xprt *xprt, char *servname,
+		  struct rpc_program *program, u32 vers, int flavor)
+{
+	struct rpc_version	*version;
+	struct rpc_clnt		*clnt;
+
+	dprintk("RPC: creating %s client for %s (xprt %p)\n",
+			program->name, servname, xprt);
+
+	if (!xprt)
+		return NULL;
+	if (vers>= program->nrvers || !(version = program->version[vers]))
+		return NULL;
+
+	if (!(clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt)))) {
+		printk("RPC: out of memory in rpc_create_client\n");
+		return NULL;
+	}
+	memset(clnt, 0, sizeof(*clnt));
+
+	clnt->cl_xprt     = xprt;
+	clnt->cl_procinfo = version->procs;
+	clnt->cl_maxproc  = version->nrprocs;
+	clnt->cl_server   = servname;
+	clnt->cl_protname = program->name;
+	clnt->cl_port     = xprt->addr.sin_port;
+	clnt->cl_prog     = program->number;
+	clnt->cl_vers     = version->number;
+	clnt->cl_prot     = IPPROTO_UDP;
+	clnt->cl_stats    = program->stats;
+	clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait");
+
+	if (!clnt->cl_port)
+		clnt->cl_autobind = 1;
+
+	if (!rpcauth_create(flavor, clnt)) {
+		printk("RPC: Couldn't create auth handle (flavor %d)\n",
+					flavor);
+		rpc_free(clnt);
+		return NULL;
+	}
+	return clnt;
+}
+
+/*
+ * Properly shut down an RPC client, terminating all outstanding
+ * requests.
+ */
+int
+rpc_shutdown_client(struct rpc_clnt *clnt)
+{
+	dprintk("RPC: shutting down %s client for %s\n",
+			clnt->cl_protname, clnt->cl_server);
+	while (clnt->cl_users) {
+		dprintk("sigmask %08lx\n", current->signal);
+		dprintk("users %d\n", clnt->cl_users);
+		clnt->cl_dead = 1;
+		rpc_killall_tasks(clnt);
+		sleep_on(&destroy_wait);
+	}
+	return rpc_destroy_client(clnt);
+}
+
+/*
+ * Delete an RPC client
+ */
+int
+rpc_destroy_client(struct rpc_clnt *clnt)
+{
+	dprintk("RPC: destroying %s client for %s\n",
+			clnt->cl_protname, clnt->cl_server);
+
+	rpcauth_destroy(clnt->cl_auth);
+	if (clnt->cl_xprt) {
+		xprt_destroy(clnt->cl_xprt);
+		clnt->cl_xprt = NULL;
+	}
+	rpc_free(clnt);
+	return 0;
+}
+
+/*
+ * Release an RPC client
+ */
+void
+rpc_release_client(struct rpc_clnt *clnt)
+{
+	dprintk("RPC:      rpc_release_client(%p, %d)\n",
+				clnt, clnt->cl_users);
+	if (--(clnt->cl_users) == 0) {
+		wake_up(&destroy_wait);
+		if (clnt->cl_oneshot || clnt->cl_dead)
+			rpc_destroy_client(clnt);
+	}
+	dprintk("RPC:      rpc_release_client done\n");
+}
+
+/*
+ * Default callback for async RPC calls
+ */
+static void
+rpc_default_callback(struct rpc_task *task)
+{
+	rpc_release_task(task);
+}
+
+/*
+ * New rpc_call implementation
+ */
+int
+rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
+				int flags, rpc_action func, void *data)
+{
+	struct rpc_task	my_task, *task = &my_task;
+	unsigned long	oldmask, sigallow = _S(SIGKILL);
+	int		async, status;
+
+	/* Turn off various signals */
+	if (clnt->cl_intr) {
+		struct sigaction *action = current->sig->action;
+		if (action[SIGINT-1].sa_handler == SIG_DFL)
+			sigallow |= _S(SIGINT);
+		if (action[SIGQUIT-1].sa_handler == SIG_DFL)
+			sigallow |= _S(SIGQUIT);
+	}
+	oldmask = current->blocked;
+	current->blocked |= ~sigallow;
+
+	/* Create/initialize a new RPC task */
+	if ((async = (flags & RPC_TASK_ASYNC)) != 0) {
+		if (!func)
+			func = rpc_default_callback;
+		if (!(task = rpc_new_task(clnt, func, flags))) {
+			current->blocked = oldmask;
+			return -ENOMEM;
+		}
+		task->tk_calldata = data;
+	} else {
+		rpc_init_task(task, clnt, NULL, flags);
+	}
+
+	/* Bind the user cred, set up the call info struct and
+	 * execute the task */
+	if (rpcauth_lookupcred(task) != NULL) {
+		rpc_call_setup(task, proc, argp, resp, 0);
+		rpc_execute(task);
+	} else
+		async = 0;
+
+	if (!async) {
+		status = task->tk_status;
+		rpc_release_task(task);
+	} else
+		status = 0;
+
+	current->blocked = oldmask;
+	return status;
+}
+
+
+void
+rpc_call_setup(struct rpc_task *task, u32 proc,
+				void *argp, void *resp, int flags)
+{
+	task->tk_action = call_bind;
+	task->tk_proc   = proc;
+	task->tk_argp   = argp;
+	task->tk_resp   = resp;
+	task->tk_flags |= flags;
+
+	/* Increment call count */
+	rpcproc_count(task->tk_client, proc)++;
+}
+
+/*
+ * Restart an (async) RPC call. Usually called from within the
+ * exit handler.
+ */
+void
+rpc_restart_call(struct rpc_task *task)
+{
+	if (task->tk_flags & RPC_TASK_KILLED) {
+		rpc_release_task(task);
+		return;
+	}
+	task->tk_action = call_bind;
+	rpcproc_count(task->tk_client, task->tk_proc)++;
+}
+
+/*
+ * 0.	Get the server port number if not yet set
+ */
+static void
+call_bind(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+
+	task->tk_action = call_reserve;
+	task->tk_status = 0;
+	if (!clnt->cl_port)
+		rpc_getport(task, clnt);
+}
+
+/*
+ * 1.	Reserve an RPC call slot
+ */
+static void
+call_reserve(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+
+	dprintk("RPC: %4d call_reserve\n", task->tk_pid);
+	if (!clnt->cl_port) {
+		printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n",
+			clnt->cl_protname, clnt->cl_server,
+			clnt->cl_softrtry? "giving up" : "retrying");
+		if (!clnt->cl_softrtry) {
+			rpc_delay(task, 5*HZ);
+			return;
+		}
+		rpc_exit(task, -EIO);
+		return;
+	}
+	if (!rpcauth_uptodatecred(task)) {
+		task->tk_action = call_refresh;
+		return;
+	}
+	task->tk_action  = call_reserveresult;
+	task->tk_timeout = clnt->cl_timeout.to_resrvval;
+	task->tk_status  = 0;
+	clnt->cl_stats->rpccnt++;
+	xprt_reserve(task);
+}
+
+/*
+ * 1b.	Grok the result of xprt_reserve()
+ */
+static void
+call_reserveresult(struct rpc_task *task)
+{
+	dprintk("RPC: %4d call_reserveresult (status %d)\n",
+				task->tk_pid, task->tk_status);
+
+	if (task->tk_status >= 0) {
+		task->tk_action = call_allocate;
+	} else if (task->tk_status == -EAGAIN) {
+		task->tk_timeout = task->tk_client->cl_timeout.to_resrvval;
+		task->tk_status = 0;
+		xprt_reserve(task);
+		return;
+	} else if (task->tk_status == -ETIMEDOUT) {
+		task->tk_action = call_timeout;
+	} else {
+		task->tk_action = NULL;
+	}
+	if (!task->tk_rqstp)
+		rpc_exit(task, -EIO);
+}
+
+/*
+ * 2.	Allocate the buffer. For details, see sched.c:rpc_malloc.
+ *	(Note: buffer memory is freed in rpc_task_release).
+ */
+static void
+call_allocate(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+	unsigned int	bufsiz;
+
+	dprintk("RPC: %4d call_allocate (status %d)\n", 
+				task->tk_pid, task->tk_status);
+	task->tk_action = call_encode;
+	if (task->tk_buffer)
+		return;
+
+	/* FIXME: compute buffer requirements more exactly using
+	 * auth->au_wslack */
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE;
+
+	if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL)
+		return;
+
+	if (!signalled()) {
+		xprt_release(task);
+		task->tk_action = call_reserve;
+		rpc_delay(task, HZ);
+		return;
+	}
+
+	rpc_exit(task, -ERESTARTSYS);
+}
+
+/*
+ * 3.	Encode arguments of an RPC call
+ */
+static void
+call_encode(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_rqst	*req = task->tk_rqstp;
+	unsigned int	bufsiz;
+	kxdrproc_t	encode;
+	int		status;
+	u32		*p;
+
+	dprintk("RPC: %4d call_encode (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	task->tk_action = call_transmit;
+
+	/* Default buffer setup */
+	bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE;
+	req->rq_svec[0].iov_base = task->tk_buffer;
+	req->rq_svec[0].iov_len  = bufsiz;
+	req->rq_slen		 = 0;
+	req->rq_snr		 = 1;
+	req->rq_rvec[0].iov_base = task->tk_buffer;
+	req->rq_rvec[0].iov_len  = bufsiz;
+	req->rq_rlen		 = bufsiz;
+	req->rq_rnr		 = 1;
+
+	if (task->tk_proc > clnt->cl_maxproc) {
+		printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
+			clnt->cl_protname, clnt->cl_vers, task->tk_proc);
+		rpc_exit(task, -EIO);
+		return;
+	}
+
+	/* Encode header and provided arguments */
+	encode = rpcproc_encode(clnt, task->tk_proc);
+	if (!(p = call_header(task))) {
+		rpc_exit(task, -EIO);
+	} else
+	if ((status = encode(req, p, task->tk_argp)) < 0) {
+		printk(KERN_WARNING "%s: can't encode arguments: %d\n",
+				clnt->cl_protname, -status);
+		rpc_exit(task, status);
+	}
+}
+
+/*
+ * 4.	Transmit the RPC request
+ */
+static void
+call_transmit(struct rpc_task *task)
+{
+	dprintk("RPC: %4d call_transmit (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	task->tk_action = call_receive;
+	task->tk_status = 0;
+	xprt_transmit(task);
+}
+
+/*
+ * 5.	Wait for the RPC reply
+ */
+static void
+call_receive(struct rpc_task *task)
+{
+	dprintk("RPC: %4d call_receive (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	/* In case of error, evaluate status */
+	if (task->tk_status < 0) {
+		task->tk_action = call_status;
+		return;
+	}
+
+	/* If we have no decode function, this means we're performing
+	 * a void call (a la lockd message passing). */
+	if (!rpcproc_decode(task->tk_client, task->tk_proc)) {
+		rpc_remove_wait_queue(task); /* remove from xprt_pending */
+		task->tk_action = NULL;
+		return;
+	}
+
+	task->tk_action = call_status;
+	xprt_receive(task);
+}
+
+/*
+ * 6.	Sort out the RPC call status
+ */
+static void
+call_status(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_rqst	*req;
+	int		status = task->tk_status;
+
+	dprintk("RPC: %4d call_status (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	if (status >= 0) {
+		task->tk_action = call_decode;
+	} else if (status == -ETIMEDOUT) {
+		task->tk_action = call_timeout;
+	} else if (status == -EAGAIN) {
+		if (!(req = task->tk_rqstp))
+			task->tk_action = call_reserve;
+		else if (!task->tk_buffer)
+			task->tk_action = call_allocate;
+		else if (req->rq_damaged)
+			task->tk_action = call_encode;
+		else
+			task->tk_action = call_transmit;
+	} else if (status == -ENOTCONN) {
+		task->tk_action = call_reconnect;
+	} else if (status == -ECONNREFUSED && clnt->cl_autobind) {
+		task->tk_action = call_bind;
+		clnt->cl_port = 0;
+	} else {
+		if (clnt->cl_chatty)
+			printk("%s: RPC call returned error %d\n",
+				clnt->cl_protname, -status);
+		task->tk_action = NULL;
+		return;
+	}
+}
+
+/*
+ * 6a.	Handle RPC timeout
+ * 	We do not release the request slot, so we keep using the
+ *	same XID for all retransmits.
+ */
+static void
+call_timeout(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_rqst	*req = task->tk_rqstp;
+
+	if (req) {
+		struct rpc_timeout *to = &req->rq_timeout;
+
+		if (xprt_adjust_timeout(to)) {
+			dprintk("RPC: %4d call_timeout (minor timeo)\n",
+				task->tk_pid);
+			goto minor_timeout;
+		}
+		if ((to->to_initval <<= 1) > to->to_maxval)
+			to->to_initval = to->to_maxval;
+	}
+
+	dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid);
+	if (clnt->cl_softrtry) {
+		if (clnt->cl_chatty && !task->tk_exit)
+			printk("%s: server %s not responding, timed out\n",
+				clnt->cl_protname, clnt->cl_server);
+		rpc_exit(task, -EIO);
+		return;
+	}
+	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
+		printk("%s: server %s not responding, still trying\n",
+			clnt->cl_protname, clnt->cl_server);
+		task->tk_flags |= RPC_CALL_MAJORSEEN;
+	}
+	if (clnt->cl_autobind)
+		clnt->cl_port = 0;
+
+minor_timeout:
+	if (!clnt->cl_port) {
+		task->tk_action = call_bind;
+	} else if (!req) {
+		task->tk_action = call_reserve;
+	} else if (req->rq_damaged) {
+		task->tk_action = call_encode;
+		clnt->cl_stats->rpcretrans++;
+	} else {
+		task->tk_action = call_transmit;
+		clnt->cl_stats->rpcretrans++;
+	}
+	task->tk_status = 0;
+}
+
+/*
+ * 6b.	Reconnect to the RPC server (TCP case)
+ */
+static void
+call_reconnect(struct rpc_task *task)
+{
+	dprintk("RPC: %4d call_reconnect status %d\n",
+				task->tk_pid, task->tk_status);
+	if (task->tk_status == 0) {
+		task->tk_action = call_status;
+		task->tk_status = -EAGAIN;
+		return;
+	}
+	task->tk_client->cl_stats->netreconn++;
+	xprt_reconnect(task);
+}
+
+/*
+ * 7.	Decode the RPC reply
+ */
+static void
+call_decode(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+	struct rpc_rqst	*req = task->tk_rqstp;
+	kxdrproc_t	decode = rpcproc_decode(clnt, task->tk_proc);
+	u32		*p;
+
+	dprintk("RPC: %4d call_decode (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
+		printk("%s: server %s OK\n",
+			clnt->cl_protname, clnt->cl_server);
+		task->tk_flags &= ~RPC_CALL_MAJORSEEN;
+	}
+
+	if (task->tk_status < 12) {
+		printk("%s: too small RPC reply size (%d bytes)\n",
+			clnt->cl_protname, task->tk_status);
+		rpc_exit(task, -EIO);
+		return;
+	}
+
+	/* Verify the RPC header */
+	if (!(p = call_verify(task)))
+		return;
+
+	/*
+	 * The following is an NFS-specific hack to cater for setuid
+	 * processes whose uid is mapped to nobody on the server.
+	 */
+	if (task->tk_client->cl_prog == 100003 && ntohl(*p) == NFSERR_PERM) {
+		if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) {
+			dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
+			task->tk_flags ^= RPC_CALL_REALUID;
+			task->tk_action = call_encode;
+			return;
+		}
+	}
+
+	task->tk_action = NULL;
+	task->tk_status = decode(req, p, task->tk_resp);
+	dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
+					task->tk_status);
+}
+
+/*
+ * 8.	Refresh the credentials if rejected by the server
+ */
+static void
+call_refresh(struct rpc_task *task)
+{
+	dprintk("RPC: %4d call_refresh\n", task->tk_pid);
+
+	xprt_release(task);	/* Must do to obtain new XID */
+	task->tk_action = call_refreshresult;
+	task->tk_status = 0;
+	task->tk_client->cl_stats->rpcauthrefresh++;
+	rpcauth_refreshcred(task);
+}
+
+/*
+ * 8a.	Process the results of a credential refresh
+ */
+static void
+call_refreshresult(struct rpc_task *task)
+{
+	dprintk("RPC: %4d call_refreshresult (status %d)\n", 
+				task->tk_pid, task->tk_status);
+
+	if (task->tk_status < 0) {
+		task->tk_status = -EACCES;
+		task->tk_action = NULL;
+	} else
+		task->tk_action = call_reserve;
+}
+
+/*
+ * Call header serialization
+ */
+static u32 *
+call_header(struct rpc_task *task)
+{
+	struct rpc_clnt *clnt = task->tk_client;
+	struct rpc_xprt *xprt = clnt->cl_xprt;
+	u32		*p = task->tk_buffer;
+
+	/* FIXME: check buffer size? */
+	if (xprt->stream)
+		*p++ = 0;		/* fill in later */
+	*p++ = task->tk_rqstp->rq_xid;	/* XID */
+	*p++ = htonl(RPC_CALL);		/* CALL */
+	*p++ = htonl(RPC_VERSION);	/* RPC version */
+	*p++ = htonl(clnt->cl_prog);	/* program number */
+	*p++ = htonl(clnt->cl_vers);	/* program version */
+	*p++ = htonl(task->tk_proc);	/* procedure */
+	return rpcauth_marshcred(task, p);
+}
+
+/*
+ * Reply header verification
+ */
+static u32 *
+call_verify(struct rpc_task *task)
+{
+	u32	*p = task->tk_buffer, n;
+
+	p += 1;	/* skip XID */
+
+	if ((n = ntohl(*p++)) != RPC_REPLY) {
+		printk("call_verify: not an RPC reply: %x\n", n);
+		goto garbage;
+	}
+	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
+		int	error = -EACCES;
+
+		if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
+			printk("call_verify: RPC call rejected: %x\n", n);
+		} else
+		switch ((n = ntohl(*p++))) {
+		case RPC_AUTH_REJECTEDCRED:
+		case RPC_AUTH_REJECTEDVERF:
+			if (!task->tk_cred_retry--)
+				break;
+			dprintk("RPC: %4d call_verify: retry stale creds\n",
+							task->tk_pid);
+			rpcauth_invalcred(task);
+			task->tk_action = call_refresh;
+			return NULL;
+		case RPC_AUTH_BADCRED:
+		case RPC_AUTH_BADVERF:
+			/* possibly garbled cred/verf? */
+			if (!task->tk_garb_retry--)
+				break;
+			dprintk("RPC: %4d call_verify: retry garbled creds\n",
+							task->tk_pid);
+			task->tk_action = call_encode;
+			return NULL;
+		case RPC_AUTH_TOOWEAK:
+			printk("call_verify: server requires stronger "
+			       "authentication.\n");
+		default:
+			printk("call_verify: unknown auth error: %x\n", n);
+			error = -EIO;
+		}
+		dprintk("RPC: %4d call_verify: call rejected %d\n",
+						task->tk_pid, n);
+		rpc_exit(task, error);
+		return NULL;
+	}
+	if (!(p = rpcauth_checkverf(task, p)))
+		goto garbage;		/* bad verifier, retry */
+	switch ((n = ntohl(*p++))) {
+	case RPC_SUCCESS:
+		return p;
+	case RPC_GARBAGE_ARGS:
+		break;			/* retry */
+	default:
+		printk("call_verify: server accept status: %x\n", n);
+		/* Also retry */
+	}
+
+garbage:
+	dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
+	task->tk_client->cl_stats->rpcgarbage++;
+	if (0 && task->tk_garb_retry--) {
+		task->tk_action = call_encode;
+		return NULL;
+	}
+	rpc_exit(task, -EIO);
+	return NULL;
+}
+
+#ifdef MODULE
+int
+init_module(void)
+{
+#ifdef RPC_DEBUG
+	rpc_register_sysctl();
+#endif
+	rpcstat_init();
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+#ifdef RPC_DEBUG
+	rpc_unregister_sysctl();
+#endif
+	rpcstat_exit();
+}
+#endif
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/pmap_clnt.c linux/net/sunrpc/pmap_clnt.c
--- v2.1.31/linux/net/sunrpc/pmap_clnt.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/pmap_clnt.c	Thu Dec 12 13:37:15 1996
@@ -0,0 +1,238 @@
+/*
+ * linux/net/sunrpc/pmap.c
+ *
+ * Portmapper client.
+ *
+ * FIXME: In a secure environment, we may want to use an authentication
+ * flavor other than AUTH_NULL.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/uio.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY	RPCDBG_PMAP
+#endif
+
+#define PMAP_SET		1
+#define PMAP_UNSET		2
+#define PMAP_GETPORT		3
+
+static struct rpc_clnt *	pmap_create(char *, struct sockaddr_in *, int);
+static void			pmap_getport_done(struct rpc_task *);
+extern struct rpc_program	pmap_program;
+
+/*
+ * Obtain the port for a given RPC service on a given host
+ */
+void
+rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
+{
+	struct rpc_portmap *map = &clnt->cl_pmap;
+	struct sockaddr_in *sap = &clnt->cl_xprt->addr;
+	struct rpc_clnt	*pmap_clnt;
+	struct rpc_task	*child;
+
+	dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n",
+			task->tk_pid, clnt->cl_server,
+			map->pm_prog, map->pm_vers, map->pm_prot);
+
+	if (clnt->cl_binding) {
+		rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0);
+		return;
+	}
+	clnt->cl_binding = 1;
+
+	task->tk_status = 0;
+	if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) {
+		task->tk_status = -EACCES;
+		goto bailout;
+	}
+	if (!(child = rpc_new_child(pmap_clnt, task))) {
+		rpc_destroy_client(pmap_clnt);
+		goto bailout;
+	}
+
+	/* Setup the call info struct */
+	rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
+
+	/* ... and run the child task */
+	rpc_run_child(task, child, pmap_getport_done);
+	return;
+
+bailout:
+	clnt->cl_binding = 0;
+	rpc_wake_up(&clnt->cl_bindwait);
+	task->tk_status = -EIO;
+	task->tk_action = NULL;
+}
+
+static void
+pmap_getport_done(struct rpc_task *task)
+{
+	struct rpc_clnt	*clnt = task->tk_client;
+
+	dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n",
+			task->tk_pid, task->tk_status, clnt->cl_port);
+	if (task->tk_status < 0) {
+		/* Make the calling task exit with an error */
+		task->tk_action = NULL;
+	} else if (clnt->cl_port == 0) {
+		/* Program not registered */
+		task->tk_status = -EACCES;
+		task->tk_action = NULL;
+	} else {
+		/* byte-swap port number first */
+		clnt->cl_port = htons(clnt->cl_port);
+		clnt->cl_xprt->addr.sin_port = clnt->cl_port;
+	}
+	clnt->cl_binding = 0;
+	rpc_wake_up(&clnt->cl_bindwait);
+}
+
+/*
+ * Set or unset a port registration with the local portmapper.
+ * port == 0 means unregister, port != 0 means register.
+ */
+int
+rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+{
+	struct sockaddr_in	sin;
+	struct rpc_portmap	map;
+	struct rpc_clnt		*pmap_clnt;
+	unsigned int		error = 0;
+
+	dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n",
+			prog, vers, prot, port);
+
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	if (!(pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP))) {
+		dprintk("RPC: couldn't create pmap client\n");
+		return -EACCES;
+	}
+
+	map.pm_prog = prog;
+	map.pm_vers = vers;
+	map.pm_prot = prot;
+	map.pm_port = port;
+
+	error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET,
+					&map, okay, 0);
+
+	if (error < 0) {
+		printk(KERN_WARNING
+			"RPC: failed to contact portmap (errno %d).\n",
+			error);
+	}
+	dprintk("RPC: registration status %d/%d\n", error, *okay);
+
+	/* Client deleted automatically because cl_oneshot == 1 */
+	return error;
+}
+
+static struct rpc_clnt *
+pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto)
+{
+	struct rpc_xprt	*xprt;
+	struct rpc_clnt	*clnt;
+
+	/* printk("pmap: create xprt\n"); */
+	if (!(xprt = xprt_create_proto(proto, srvaddr, NULL)))
+		return NULL;
+	xprt->addr.sin_port = htons(RPC_PMAP_PORT);
+
+	/* printk("pmap: create clnt\n"); */
+	clnt = rpc_create_client(xprt, hostname,
+				&pmap_program, RPC_PMAP_VERSION,
+				RPC_AUTH_NULL);
+	if (!clnt) {
+		xprt_destroy(xprt);
+	} else {
+		clnt->cl_softrtry = 1;
+		clnt->cl_chatty   = 1;
+		clnt->cl_oneshot  = 1;
+	}
+	return clnt;
+}
+
+/*
+ * XDR encode/decode functions for PMAP
+ */
+static int
+xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+	return -EIO;
+}
+
+static int
+xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
+{
+	dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n",
+		map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port);
+	*p++ = htonl(map->pm_prog);
+	*p++ = htonl(map->pm_vers);
+	*p++ = htonl(map->pm_prot);
+	*p++ = htonl(map->pm_port);
+
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	return 0;
+}
+
+static int
+xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
+{
+	*portp = (unsigned short) ntohl(*p++);
+	return 0;
+}
+
+static int
+xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
+{
+	*boolp = (unsigned int) ntohl(*p++);
+	return 0;
+}
+
+static struct rpc_procinfo	pmap_procedures[4] = {
+	{ "pmap_null",
+		(kxdrproc_t) xdr_error,	
+		(kxdrproc_t) xdr_error,	0, 0 },
+	{ "pmap_set",
+		(kxdrproc_t) xdr_encode_mapping,	
+		(kxdrproc_t) xdr_decode_bool, 4, 1 },
+	{ "pmap_unset",
+		(kxdrproc_t) xdr_encode_mapping,	
+		(kxdrproc_t) xdr_decode_bool, 4, 1 },
+	{ "pmap_get",
+		(kxdrproc_t) xdr_encode_mapping,
+		(kxdrproc_t) xdr_decode_port, 4, 1 },
+};
+
+static struct rpc_version	pmap_version2 = {
+	2, 4, pmap_procedures
+};
+
+static struct rpc_version *	pmap_version[] = {
+	NULL,
+	NULL,
+	&pmap_version2,
+};
+
+static struct rpc_stat		pmap_stats;
+
+struct rpc_program	pmap_program = {
+	"portmap",
+	RPC_PMAP_PROGRAM,
+	sizeof(pmap_version)/sizeof(pmap_version[0]),
+	pmap_version,
+	&pmap_stats,
+};
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.1.31/linux/net/sunrpc/sched.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/sched.c	Fri Apr  4 12:47:47 1997
@@ -0,0 +1,805 @@
+/*
+ * linux/net/sunrpc/sched.c
+ *
+ * Scheduling for synchronous and asynchronous RPC requests.
+ *
+ * Copyright (C) 1996 Olaf Kirch, <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/unistd.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+#define RPCDBG_FACILITY		RPCDBG_SCHED
+static int			rpc_task_id = 0;
+#endif
+
+#define _S(signo)		(1 << ((signo)-1))
+
+/*
+ * We give RPC the same get_free_pages priority as NFS
+ */
+#define GFP_RPC			GFP_NFS
+
+static void			__rpc_default_timer(struct rpc_task *task);
+static void			rpciod_killall(void);
+
+/*
+ * When an asynchronous RPC task is activated within a bottom half
+ * handler, or while executing another RPC task, it is put on
+ * schedq, and rpciod is woken up.
+ */
+static struct rpc_wait_queue	schedq = RPC_INIT_WAITQ("schedq");
+
+/*
+ * RPC tasks that create another task (e.g. for contacting the portmapper)
+ * will wait on this queue for their child's completion
+ */
+static struct rpc_wait_queue	childq = RPC_INIT_WAITQ("childq");
+
+/*
+ * All RPC tasks are linked into this list
+ */
+static struct rpc_task *	all_tasks = NULL;
+
+/*
+ * rpciod-related stuff
+ */
+static struct wait_queue *	rpciod_idle = NULL;
+static struct wait_queue *	rpciod_killer = NULL;
+static int			rpciod_sema = 0;
+static pid_t			rpciod_pid = 0;
+static int			rpc_inhibit = 0;
+
+/*
+ * This is the last-ditch buffer for NFS swap requests
+ */
+static u32			swap_buffer[PAGE_SIZE >> 2];
+static int			swap_buffer_used = 0;
+
+/*
+ * Add new request to wait queue.
+ *
+ * Swapper tasks always get inserted at the head of the queue.
+ * This should avoid many nasty memory deadlocks and hopefully
+ * improve overall performance.
+ * Everyone else gets appended to the queue to ensure proper FIFO behavior.
+ */
+void
+rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
+{
+	if (task->tk_rpcwait) {
+		if (task->tk_rpcwait != queue)
+			printk(KERN_WARNING "RPC: doubly enqueued task!\n");
+		return;
+	}
+	if (RPC_IS_SWAPPER(task))
+		rpc_insert_list(&queue->task, task);
+	else
+		rpc_append_list(&queue->task, task);
+	task->tk_rpcwait = queue;
+
+	dprintk("RPC: %4d added to queue %p \"%s\"\n",
+				task->tk_pid, queue, rpc_qname(queue));
+}
+
+/*
+ * Remove request from queue
+ */
+void
+rpc_remove_wait_queue(struct rpc_task *task)
+{
+	struct rpc_wait_queue *queue;
+
+	if (!(queue = task->tk_rpcwait))
+		return;
+	rpc_remove_list(&queue->task, task);
+	task->tk_rpcwait = NULL;
+
+	dprintk("RPC: %4d removed from queue %p \"%s\"\n",
+				task->tk_pid, queue, rpc_qname(queue));
+}
+
+/*
+ * Set up a timer for the current task.
+ */
+inline void
+rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+	unsigned long	expires = jiffies + task->tk_timeout;
+
+	dprintk("RPC: %4d setting alarm for %lu ms\n",
+			task->tk_pid, task->tk_timeout * 1000 / HZ);
+	if (!timer)
+		timer = __rpc_default_timer;
+	if (expires < jiffies) {
+		printk("RPC: bad timeout value %ld - setting to 10 sec!\n",
+					task->tk_timeout);
+		expires = jiffies + 10 * HZ;
+	}
+	task->tk_timer.expires  = expires;
+	task->tk_timer.data     = (unsigned long) task;
+	task->tk_timer.function = (void (*)(unsigned long)) timer;
+	task->tk_timer.prev     = NULL;
+	task->tk_timer.next     = NULL;
+	add_timer(&task->tk_timer);
+}
+
+/*
+ * Delete any timer for the current task.
+ * Must be called with interrupts off.
+ */
+inline void
+rpc_del_timer(struct rpc_task *task)
+{
+	if (task->tk_timeout) {
+		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
+		del_timer(&task->tk_timer);
+		task->tk_timeout = 0;
+	}
+}
+
+/*
+ * Make an RPC task runnable.
+ */
+static inline void
+rpc_make_runnable(struct rpc_task *task)
+{
+	if (task->tk_timeout) {
+		printk("RPC: task w/ running timer in rpc_make_runnable!!\n");
+		return;
+	}
+	if (RPC_IS_ASYNC(task)) {
+		rpc_add_wait_queue(&schedq, task);
+		wake_up(&rpciod_idle);
+	} else {
+		wake_up(&task->tk_wait);
+	}
+	task->tk_flags |= RPC_TASK_RUNNING;
+}
+
+/*
+ * Prepare for sleeping on a wait queue.
+ * By always appending tasks to the list we ensure FIFO behavior.
+ * NB: An RPC task will only receive interrupt-driven events as long
+ * as it's on a wait queue.
+ */
+static void
+__rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
+			rpc_action action, rpc_action timer)
+{
+	unsigned long	oldflags;
+
+	dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid,
+				rpc_qname(q), jiffies);
+
+	/*
+	 * Protect the execution below.
+	 */
+	save_flags(oldflags); cli();
+
+	rpc_add_wait_queue(q, task);
+	task->tk_callback = action;
+	if (task->tk_timeout)
+		rpc_add_timer(task, timer);
+	task->tk_flags &= ~RPC_TASK_RUNNING;
+
+	restore_flags(oldflags);
+	return;
+}
+
+void
+rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
+				rpc_action action, rpc_action timer)
+{
+	__rpc_sleep_on(q, task, action, timer);
+}
+
+/*
+ * Wake up a single task -- must be invoked with bottom halves off.
+ *
+ * It would probably suffice to cli/sti the del_timer and remove_wait_queue
+ * operations individually.
+ */
+static void
+__rpc_wake_up(struct rpc_task *task)
+{
+	dprintk("RPC: %4d __rpc_wake_up (now %ld inh %d)\n",
+					task->tk_pid, jiffies, rpc_inhibit);
+
+#ifdef RPC_DEBUG
+	if (task->tk_magic != 0xf00baa) {
+		printk("RPC: attempt to wake up non-existing task!\n");
+		rpc_debug = ~0;
+		return;
+	}
+#endif
+	rpc_del_timer(task);
+	if (task->tk_rpcwait != &schedq)
+		rpc_remove_wait_queue(task);
+	if (!RPC_IS_RUNNING(task)) {
+		rpc_make_runnable(task);
+		task->tk_flags |= RPC_TASK_CALLBACK;
+	}
+	dprintk("RPC:      __rpc_wake_up done\n");
+}
+
+/*
+ * Default timeout handler if none specified by user
+ */
+static void
+__rpc_default_timer(struct rpc_task *task)
+{
+	dprintk("RPC: %d timeout (default timer)\n", task->tk_pid);
+	task->tk_status = -ETIMEDOUT;
+	task->tk_timeout = 0;
+	__rpc_wake_up(task);
+}
+
+/*
+ * Wake up the specified task
+ */
+void
+rpc_wake_up_task(struct rpc_task *task)
+{
+	unsigned long	oldflags;
+
+	save_flags(oldflags); cli();
+	__rpc_wake_up(task);
+	restore_flags(oldflags);
+}
+
+/*
+ * Wake up the next task on the wait queue.
+ */
+struct rpc_task *
+rpc_wake_up_next(struct rpc_wait_queue *queue)
+{
+	unsigned long	oldflags;
+	struct rpc_task	*task;
+
+	dprintk("RPC:      wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue));
+	save_flags(oldflags); cli();
+	if ((task = queue->task) != 0)
+		__rpc_wake_up(task);
+	restore_flags(oldflags);
+
+	return task;
+}
+
+/*
+ * Wake up all tasks on a queue
+ */
+void
+rpc_wake_up(struct rpc_wait_queue *queue)
+{
+	unsigned long	oldflags;
+
+	save_flags(oldflags); cli();
+	while (queue->task)
+		__rpc_wake_up(queue->task);
+	restore_flags(oldflags);
+}
+
+/*
+ * Wake up all tasks on a queue, and set their status value.
+ */
+void
+rpc_wake_up_status(struct rpc_wait_queue *queue, int status)
+{
+	struct rpc_task	*task;
+	unsigned long	oldflags;
+
+	save_flags(oldflags); cli();
+	while ((task = queue->task) != NULL) {
+		task->tk_status = status;
+		__rpc_wake_up(task);
+	}
+	restore_flags(oldflags);
+}
+
+/*
+ * Run a task at a later time
+ */
+static void	__rpc_atrun(struct rpc_task *);
+void
+rpc_delay(struct rpc_task *task, unsigned long delay)
+{
+	static struct rpc_wait_queue	delay_queue;
+
+	task->tk_timeout = delay;
+	rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun);
+}
+
+static void
+__rpc_atrun(struct rpc_task *task)
+{
+	task->tk_status = 0;
+	__rpc_wake_up(task);
+}
+
+/*
+ * This is the RPC `scheduler' (or rather, the finite state machine).
+ */
+static int
+__rpc_execute(struct rpc_task *task)
+{
+	unsigned long	oldflags;
+	int		status = 0;
+
+	dprintk("RPC: %4d rpc_execute flgs %x\n",
+				task->tk_pid, task->tk_flags);
+
+	if (!RPC_IS_RUNNING(task)) {
+		printk("RPC: rpc_execute called for sleeping task!!\n");
+		return 0;
+	}
+
+	while (1) {
+		/*
+		 * Execute any pending callback.
+		 */
+		if (task->tk_flags & RPC_TASK_CALLBACK) {
+			task->tk_flags &= ~RPC_TASK_CALLBACK;
+			if (task->tk_callback) {
+				task->tk_callback(task);
+				task->tk_callback = NULL;
+			}
+		}
+
+		/*
+		 * No handler for next step means exit.
+		 */
+		if (!task->tk_action)
+			break;
+
+		/*
+		 * Perform the next FSM step.
+		 * tk_action may be NULL when the task has been killed
+		 * by someone else.
+		 */
+		if (RPC_IS_RUNNING(task) && task->tk_action)
+			task->tk_action(task);
+
+		/*
+		 * Check whether task is sleeping.
+		 * Note that if the task may go to sleep in tk_action,
+		 * and the RPC reply arrives before we get here, it will
+		 * have state RUNNING, but will still be on schedq.
+		 */
+		save_flags(oldflags); cli();
+		if (RPC_IS_RUNNING(task)) {
+			if (task->tk_rpcwait == &schedq)
+				rpc_remove_wait_queue(task);
+		} else while (!RPC_IS_RUNNING(task)) {
+			if (RPC_IS_ASYNC(task)) {
+				restore_flags(oldflags);
+				return 0;
+			}
+
+			/* sync task: sleep here */
+			dprintk("RPC: %4d sync task going to sleep\n",
+							task->tk_pid);
+			current->timeout = 0;
+			interruptible_sleep_on(&task->tk_wait);
+
+			/* When the task received a signal, remove from
+			 * any queues etc, and make runnable again. */
+			if (signalled())
+				__rpc_wake_up(task);
+
+			dprintk("RPC: %4d sync task resuming\n",
+							task->tk_pid);
+		}
+		restore_flags(oldflags);
+
+		/*
+		 * When a sync task receives a signal, it exits with
+		 * -ERESTARTSYS. In order to catch any callbacks that
+		 * clean up after sleeping on some queue, we don't
+		 * break the loop here, but go around once more.
+		 */
+		if (!RPC_IS_ASYNC(task) && signalled()) {
+			dprintk("RPC: %4d got signal (map %08lx)\n",
+				task->tk_pid,
+				current->signal & ~current->blocked);
+			rpc_exit(task, -ERESTARTSYS);
+		}
+	}
+
+	dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
+	if (task->tk_exit) {
+		status = task->tk_status;
+		task->tk_exit(task);
+	}
+
+	return status;
+}
+
+/*
+ * User-visible entry point to the scheduler.
+ * The recursion protection is for debugging. It should go away once
+ * the code has stabilized.
+ */
+void
+rpc_execute(struct rpc_task *task)
+{
+	static int	executing = 0;
+	int		incr = RPC_IS_ASYNC(task)? 1 : 0;
+
+	if (incr && (executing || rpc_inhibit)) {
+		printk("RPC: rpc_execute called recursively!\n");
+		return;
+	}
+	executing += incr;
+	__rpc_execute(task);
+	executing -= incr;
+}
+
+/*
+ * This is our own little scheduler for async RPC tasks.
+ */
+static void
+__rpc_schedule(void)
+{
+	struct rpc_task	*task;
+	int		count = 0;
+	unsigned long	oldflags;
+
+	dprintk("RPC:      rpc_schedule enter\n");
+	while (1) {
+		save_flags(oldflags); cli();
+		if (!(task = schedq.task))
+			break;
+		rpc_del_timer(task);
+		rpc_remove_wait_queue(task);
+		task->tk_flags |= RPC_TASK_RUNNING;
+		restore_flags(oldflags);
+
+		__rpc_execute(task);
+
+		if (++count >= 200) {
+			count = 0;
+			need_resched = 1;
+		}
+		if (need_resched)
+			schedule();
+	}
+	restore_flags(oldflags);
+	dprintk("RPC:      rpc_schedule leave\n");
+}
+
+/*
+ * Allocate memory for RPC purpose.
+ *
+ * This is yet another tricky issue: For sync requests issued by
+ * a user process, we want to make kmalloc sleep if there isn't
+ * enough memory. Async requests should not sleep too excessively
+ * because that will block rpciod (but that's not dramatic when
+ * it's starved of memory anyway). Finally, swapout requests should
+ * never sleep at all, and should not trigger another swap_out
+ * request through kmalloc which would just increase memory contention.
+ *
+ * I hope the following gets it right, which gives async requests
+ * a slight advantage over sync requests (good for writeback, debatable
+ * for readahead):
+ *
+ *   sync user requests:	GFP_KERNEL
+ *   async requests:		GFP_RPC		(== GFP_NFS)
+ *   swap requests:		GFP_ATOMIC	(or new GFP_SWAPPER)
+ */
+void *
+rpc_allocate(unsigned int flags, unsigned int size)
+{
+	u32	*buffer;
+	int	gfp;
+
+	if (flags & RPC_TASK_SWAPPER)
+		gfp = GFP_ATOMIC;
+	else if (flags & RPC_TASK_ASYNC)
+		gfp = GFP_RPC;
+	else
+		gfp = GFP_KERNEL;
+
+	do {
+		if ((buffer = (u32 *) kmalloc(size, gfp)) != NULL) {
+			dprintk("RPC:      allocated buffer %p\n", buffer);
+			return buffer;
+		}
+		if ((flags & RPC_TASK_SWAPPER) && !swap_buffer_used++) {
+			dprintk("RPC:      used last-ditch swap buffer\n");
+			return swap_buffer;
+		}
+		if (flags & RPC_TASK_ASYNC)
+			return NULL;
+		current->timeout = jiffies + (HZ >> 4);
+		schedule();
+	} while (!signalled());
+
+	return NULL;
+}
+
+void
+rpc_free(void *buffer)
+{
+	if (buffer != swap_buffer) {
+		kfree(buffer);
+		return;
+	}
+	swap_buffer_used = 0;
+}
+
+/*
+ * Creation and deletion of RPC task structures
+ */
+inline void
+rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
+				rpc_action callback, int flags)
+{
+	memset(task, 0, sizeof(*task));
+	task->tk_client = clnt;
+	task->tk_flags  = RPC_TASK_RUNNING | flags;
+	task->tk_exit   = callback;
+	if (current->uid != current->fsuid || current->gid != current->fsgid)
+		task->tk_flags |= RPC_TASK_SETUID;
+
+	/* Initialize retry counters */
+	task->tk_garb_retry = 2;
+	task->tk_cred_retry = 2;
+	task->tk_suid_retry = 1;
+
+	/* Add to global list of all tasks */
+	task->tk_next_task = all_tasks;
+	task->tk_prev_task = NULL;
+	if (all_tasks)
+		all_tasks->tk_prev_task = task;
+	all_tasks = task;
+
+	if (clnt)
+		clnt->cl_users++;
+
+#ifdef RPC_DEBUG
+	task->tk_magic = 0xf00baa;
+	task->tk_pid = rpc_task_id++;
+#endif
+	dprintk("RPC: %4d new task procpid %d\n", task->tk_pid,
+				current->pid);
+}
+
+struct rpc_task *
+rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags)
+{
+	struct rpc_task	*task;
+
+	if (!(task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task))))
+		return NULL;
+
+	rpc_init_task(task, clnt, callback, flags);
+
+	dprintk("RPC: %4d allocated task\n", task->tk_pid);
+	task->tk_flags |= RPC_TASK_DYNAMIC;
+	return task;
+}
+
+void
+rpc_release_task(struct rpc_task *task)
+{
+	struct rpc_task	*next, *prev;
+
+	dprintk("RPC: %4d release task\n", task->tk_pid);
+
+	/* Remove from global task list */
+	prev = task->tk_prev_task;
+	next = task->tk_next_task;
+	if (next)
+		next->tk_prev_task = prev;
+	if (prev)
+		prev->tk_next_task = next;
+	else
+		all_tasks = next;
+
+	/* Release resources */
+	if (task->tk_rqstp)
+		xprt_release(task);
+	if (task->tk_cred)
+		rpcauth_releasecred(task);
+	if (task->tk_buffer) {
+		rpc_free(task->tk_buffer);
+		task->tk_buffer = NULL;
+	}
+	if (task->tk_client) {
+		rpc_release_client(task->tk_client);
+		task->tk_client = NULL;
+	}
+
+#ifdef RPC_DEBUG
+	task->tk_magic = 0;
+#endif
+
+	if (task->tk_flags & RPC_TASK_DYNAMIC) {
+		dprintk("RPC: %4d freeing task\n", task->tk_pid);
+		task->tk_flags &= ~RPC_TASK_DYNAMIC;
+		rpc_free(task);
+	}
+}
+
+/*
+ * Handling of RPC child tasks
+ * We can't simply call wake_up(parent) here, because the
+ * parent task may already have gone away
+ */
+static inline struct rpc_task *
+rpc_find_parent(struct rpc_task *child)
+{
+	struct rpc_task	*temp, *parent;
+
+	parent = (struct rpc_task *) child->tk_calldata;
+	for (temp = childq.task; temp; temp = temp->tk_next) {
+		if (temp == parent)
+			return parent;
+	}
+	return NULL;
+}
+
+static void
+rpc_child_exit(struct rpc_task *child)
+{
+	struct rpc_task	*parent;
+
+	if ((parent = rpc_find_parent(child)) != NULL) {
+		parent->tk_status = child->tk_status;
+		rpc_wake_up_task(parent);
+	}
+	rpc_release_task(child);
+}
+
+struct rpc_task *
+rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent)
+{
+	struct rpc_task	*task;
+
+	if (!(task = rpc_new_task(clnt, NULL, RPC_TASK_ASYNC|RPC_TASK_CHILD))) {
+		parent->tk_status = -ENOMEM;
+		return NULL;
+	}
+	task->tk_exit = rpc_child_exit;
+	task->tk_calldata = parent;
+
+	return task;
+}
+
+void
+rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func)
+{
+	rpc_make_runnable(child);
+	rpc_sleep_on(&childq, task, func, NULL);
+}
+
+/*
+ * Kill all tasks for the given client.
+ * XXX: kill their descendants as well?
+ */
+void
+rpc_killall_tasks(struct rpc_clnt *clnt)
+{
+	struct rpc_task	**q, *rovr;
+
+	dprintk("RPC:      killing all tasks for client %p\n", clnt);
+	rpc_inhibit++;
+	for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) {
+		if (!clnt || rovr->tk_client == clnt) {
+			rovr->tk_flags |= RPC_TASK_KILLED;
+			rpc_exit(rovr, -EIO);
+			rpc_wake_up_task(rovr);
+		}
+	}
+	rpc_inhibit--;
+}
+
+/*
+ * This is the rpciod kernel thread
+ */
+static int
+rpciod(void *ptr)
+{
+	struct wait_queue **assassin = (struct wait_queue **) ptr;
+	unsigned long	oldflags;
+	int		rounds = 0;
+
+	lock_kernel();
+	rpciod_pid = current->pid;
+
+	MOD_INC_USE_COUNT;
+	/* exit_files(current); */
+	exit_mm(current);
+	current->blocked |= ~_S(SIGKILL);
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "rpciod");
+
+	dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid);
+	while (rpciod_sema) {
+		if (signalled()) {
+			if (current->signal & _S(SIGKILL)) {
+				rpciod_killall();
+			} else {
+				printk("rpciod: ignoring signal (%d users)\n",
+					rpciod_sema);
+			}
+			current->signal &= current->blocked;
+		}
+		__rpc_schedule();
+
+		if (++rounds >= 64)	/* safeguard */
+			schedule();
+		save_flags(oldflags); cli();
+		if (!schedq.task) {
+			dprintk("RPC: rpciod back to sleep\n");
+			interruptible_sleep_on(&rpciod_idle);
+			dprintk("RPC: switch to rpciod\n");
+		}
+		restore_flags(oldflags);
+	}
+
+	dprintk("RPC: rpciod shutdown commences\n");
+	if (all_tasks) {
+		printk("rpciod: active tasks at shutdown?!\n");
+		rpciod_killall();
+	}
+
+	rpciod_pid = 0;
+	wake_up(assassin);
+
+	dprintk("RPC: rpciod exiting\n");
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static void
+rpciod_killall(void)
+{
+	while (all_tasks) {
+		unsigned long	oldsig = current->signal;
+
+		current->signal = 0;
+		rpc_killall_tasks(NULL);
+		__rpc_schedule();
+		current->timeout = jiffies + HZ / 100;
+		need_resched = 1;
+		schedule();
+		current->signal = oldsig;
+	}
+}
+
+void
+rpciod_up(void)
+{
+	dprintk("rpciod_up pid %d sema %d\n", rpciod_pid, rpciod_sema);
+	if (!(rpciod_sema++) || !rpciod_pid)
+		kernel_thread(rpciod, &rpciod_killer, 0);
+}
+
+void
+rpciod_down(void)
+{
+	dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_sema);
+	if (--rpciod_sema > 0)
+		return;
+
+	rpciod_sema = 0;
+	kill_proc(rpciod_pid, SIGKILL, 1);
+	while (rpciod_pid) {
+		if (signalled())
+			return;
+		interruptible_sleep_on(&rpciod_killer);
+	}
+}
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/stats.c linux/net/sunrpc/stats.c
--- v2.1.31/linux/net/sunrpc/stats.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/stats.c	Fri Apr  4 12:08:27 1997
@@ -0,0 +1,267 @@
+/*
+ * linux/net/sunrpc/stats.c
+ *
+ * procfs-based user access to RPC statistics
+ *
+ * Everything is complicated by the fact that procfs doesn't pass the
+ * proc_dir_info struct in the call to get_info. We need our own 
+ * inode_ops for /proc/net/rpc (we want to have a write op for zeroing
+ * the current stats, anyway).
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY	RPCDBG_MISC
+
+/*
+ * Generic stats object (same for clnt and svc stats).
+ * Must agree with first two fields of either.
+ */
+struct stats {
+	struct stats *		next;
+	struct proc_dir_entry *	entry;
+};
+
+/* Code disabled until updated to new dynamic /proc code */
+#if 0
+
+static struct stats *		rpc_stats = NULL;
+static struct stats *		svc_stats = NULL;
+
+static struct proc_dir_entry	proc_rpc = {
+	0, 3, "rpc",
+	S_IFDIR | S_IRUGO | S_IXUGO, 1, 0, 0,
+	0, NULL,
+	NULL, NULL,
+	NULL,
+	NULL, NULL,
+};
+
+/*
+ * Register/unregister a stats file
+ */
+static void
+stats_register(struct stats **q, struct stats *p)
+{
+	dprintk("RPC: registering /proc/net/rpc/%s\n",
+				p->entry->name);
+	/* return; */
+	if (p->entry->low_ino)
+		return;
+	p->next = *q;
+	*q = p;
+	proc_register_dynamic(&proc_rpc, p->entry);
+}
+
+static void
+stats_unregister(struct stats **q, struct stats *p)
+{
+	dprintk("RPC: unregistering /proc/net/rpc/%s\n",
+				p->entry->name);
+	/* return; */
+	if (!p->entry->low_ino)
+		return;
+	while (*q) {
+		if (*q == p) {
+			*q = p->next;
+			proc_unregister(&proc_rpc, p->entry->low_ino);
+			return;
+		}
+		q = &((*q)->next);
+	}
+}
+
+/*
+ * Client stats handling
+ */
+void
+rpcstat_register(struct rpc_stat *statp)
+{
+	stats_register(&rpc_stats, (struct stats *) statp);
+}
+
+void
+rpcstat_unregister(struct rpc_stat *statp)
+{
+	stats_unregister(&rpc_stats, (struct stats *) statp);
+}
+
+int
+rpcstat_get_info(struct rpc_stat *statp, char *buffer,
+			char **start, off_t offset, int length)
+{
+	struct rpc_program *prog = statp->program;
+	struct rpc_version *vers;
+	int		len, i, j;
+
+	len = sprintf(buffer,
+		"net %d %d %d %d\n",
+			statp->netcnt,
+			statp->netudpcnt,
+			statp->nettcpcnt,
+			statp->nettcpconn);
+	len += sprintf(buffer + len,
+		"rpc %d %d %d\n",
+			statp->rpccnt,
+			statp->rpcretrans,
+			statp->rpcauthrefresh);
+
+	for (i = 0; i < prog->nrvers; i++) {
+		if (!(vers = prog->version[i]))
+			continue;
+		len += sprintf(buffer + len, "proc%d %d",
+					vers->number, vers->nrprocs);
+		for (j = 0; j < vers->nrprocs; j++)
+			len += sprintf(buffer + len, " %d",
+					vers->procs[j].p_count);
+		buffer[len++] = '\n';
+	}
+
+	if (offset >= len) {
+		*start = buffer;
+		return 0;
+	}
+	*start = buffer + offset;
+	len -= offset;
+	if (len > length)
+		len = length;
+	return len;
+}
+
+/*
+ * Server stats handling
+ */
+void
+svcstat_register(struct svc_stat *statp)
+{
+	stats_register(&svc_stats, (struct stats *) statp);
+}
+
+void
+svcstat_unregister(struct svc_stat *statp)
+{
+	stats_unregister(&svc_stats, (struct stats *) statp);
+}
+
+int
+svcstat_get_info(struct svc_stat *statp, char *buffer,
+			char **start, off_t offset, int length)
+{
+	struct svc_program *prog = statp->program;
+	struct svc_procedure *proc;
+	struct svc_version *vers;
+	int		len, i, j;
+
+	len = sprintf(buffer,
+		"net %d %d %d %d\n",
+			statp->netcnt,
+			statp->netudpcnt,
+			statp->nettcpcnt,
+			statp->nettcpconn);
+	len += sprintf(buffer + len,
+		"rpc %d %d %d %d %d\n",
+			statp->rpccnt,
+			statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt,
+			statp->rpcbadfmt,
+			statp->rpcbadauth,
+			statp->rpcbadclnt);
+
+	for (i = 0; i < prog->pg_nvers; i++) {
+		if (!(vers = prog->pg_vers[i]) || !(proc = vers->vs_proc))
+			continue;
+		len += sprintf(buffer + len, "proc%d %d", i, vers->vs_nproc);
+		for (j = 0; j < vers->vs_nproc; j++, proc++)
+			len += sprintf(buffer + len, " %d", proc->pc_count);
+		buffer[len++] = '\n';
+	}
+
+	if (offset >= len) {
+		*start = buffer;
+		return 0;
+	}
+	*start = buffer + offset;
+	if ((len -= offset) > length)
+		len = length;
+	return len;
+}
+
+/*
+ * Register /proc/net/rpc
+ */
+void
+rpcstat_init(void)
+{
+	dprintk("RPC: registering /proc/net/rpc\n");
+	proc_rpc.ops = proc_net.ops;	/* cheat */
+	proc_register_dynamic(&proc_net, &proc_rpc);
+}
+
+/*
+ * Unregister /proc/net/rpc
+ */
+void
+rpcstat_exit(void)
+{
+	while (rpc_stats)
+		stats_unregister(&rpc_stats, rpc_stats);
+	while (svc_stats)
+		stats_unregister(&svc_stats, svc_stats);
+	dprintk("RPC: unregistering /proc/net/rpc\n");
+	proc_unregister(&proc_net, proc_rpc.low_ino);
+}
+
+#else
+
+/* Various dummy functions */
+
+int
+rpcstat_get_info(struct rpc_stat *statp, char *buffer,
+			char **start, off_t offset, int length)
+{
+	return 0;
+}
+
+int
+svcstat_get_info(struct svc_stat *statp, char *buffer,
+			char **start, off_t offset, int length)
+{
+	return 0;
+}
+
+void
+rpcstat_register(struct rpc_stat *statp)
+{
+}
+
+void
+rpcstat_unregister(struct rpc_stat *statp)
+{
+}
+
+void
+svcstat_register(struct svc_stat *statp)
+{
+}
+
+void
+svcstat_unregister(struct svc_stat *statp)
+{
+}
+
+void
+rpcstat_init(void)
+{
+}
+
+void
+rpcstat_exit(void)
+{
+}
+
+#endif
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c
--- v2.1.31/linux/net/sunrpc/sunrpc_syms.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/sunrpc_syms.c	Fri Mar 14 05:47:22 1997
@@ -0,0 +1,105 @@
+/*
+ * linux/net/sunrpc/sunrpc_syms.c
+ *
+ * Symbols exported by the sunrpc module.
+ *
+ * Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MODULES
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/uio.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/auth.h>
+
+/* RPC scheduler */
+EXPORT_SYMBOL(rpc_allocate);
+EXPORT_SYMBOL(rpc_free);
+EXPORT_SYMBOL(rpc_execute);
+EXPORT_SYMBOL(rpc_init_task);
+EXPORT_SYMBOL(rpc_release_task);
+EXPORT_SYMBOL(rpc_sleep_on);
+EXPORT_SYMBOL(rpc_wake_up_next);
+EXPORT_SYMBOL(rpc_wake_up_task);
+EXPORT_SYMBOL(rpc_new_child);
+EXPORT_SYMBOL(rpc_run_child);
+EXPORT_SYMBOL(rpciod_down);
+EXPORT_SYMBOL(rpciod_up);
+
+/* RPC client functions */
+EXPORT_SYMBOL(rpc_create_client);
+EXPORT_SYMBOL(rpc_destroy_client);
+EXPORT_SYMBOL(rpc_shutdown_client);
+EXPORT_SYMBOL(rpc_do_call);
+EXPORT_SYMBOL(rpc_call_setup);
+EXPORT_SYMBOL(rpc_delay);
+EXPORT_SYMBOL(rpc_restart_call);
+
+/* Client transport */
+EXPORT_SYMBOL(xprt_create_proto);
+EXPORT_SYMBOL(xprt_destroy);
+EXPORT_SYMBOL(xprt_set_timeout);
+
+/* Client credential cache */
+EXPORT_SYMBOL(rpcauth_register);
+EXPORT_SYMBOL(rpcauth_unregister);
+EXPORT_SYMBOL(rpcauth_init_credcache);
+EXPORT_SYMBOL(rpcauth_free_credcache);
+EXPORT_SYMBOL(rpcauth_insert_credcache);
+EXPORT_SYMBOL(rpcauth_lookupcred);
+EXPORT_SYMBOL(rpcauth_matchcred);
+EXPORT_SYMBOL(rpcauth_releasecred);
+
+/* RPC server stuff */
+EXPORT_SYMBOL(svc_create);
+EXPORT_SYMBOL(svc_create_socket);
+EXPORT_SYMBOL(svc_create_thread);
+EXPORT_SYMBOL(svc_exit_thread);
+EXPORT_SYMBOL(svc_destroy);
+EXPORT_SYMBOL(svc_drop);
+EXPORT_SYMBOL(svc_process);
+EXPORT_SYMBOL(svc_recv);
+EXPORT_SYMBOL(svc_wake_up);
+
+/* RPC statistics */
+#ifdef CONFIG_PROC_FS
+EXPORT_SYMBOL(rpcstat_register);
+EXPORT_SYMBOL(rpcstat_unregister);
+EXPORT_SYMBOL(rpcstat_get_info);
+EXPORT_SYMBOL(svcstat_register);
+EXPORT_SYMBOL(svcstat_unregister);
+EXPORT_SYMBOL(svcstat_get_info);
+#endif
+
+/* Generic XDR */
+EXPORT_SYMBOL(xdr_encode_string);
+EXPORT_SYMBOL(xdr_decode_string);
+EXPORT_SYMBOL(xdr_decode_netobj);
+EXPORT_SYMBOL(xdr_encode_netobj);
+EXPORT_SYMBOL(xdr_zero);
+EXPORT_SYMBOL(xdr_one);
+
+/* RPC errors */
+EXPORT_SYMBOL(rpc_success);
+EXPORT_SYMBOL(rpc_garbage_args);
+EXPORT_SYMBOL(rpc_system_err);
+
+/* Debugging symbols */
+EXPORT_SYMBOL(rpc_debug);
+EXPORT_SYMBOL(nfs_debug);
+EXPORT_SYMBOL(nfsd_debug);
+EXPORT_SYMBOL(nlm_debug);
+
+#endif /* CONFIG_MODULES */
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svc.c linux/net/sunrpc/svc.c
--- v2.1.31/linux/net/sunrpc/svc.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/svc.c	Thu Mar 20 07:03:20 1997
@@ -0,0 +1,357 @@
+/*
+ * linux/net/sunrpc/svc.c
+ *
+ * High-level RPC service routines
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/clnt.h>
+
+#define RPCDBG_FACILITY	RPCDBG_SVCDSP
+
+/*
+ * Create an RPC service
+ */
+struct svc_serv *
+svc_create(struct svc_program *prog, unsigned int bufsize, unsigned int xdrsize)
+{
+	struct svc_serv	*serv;
+
+	xdr_init();
+
+	if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL)))
+		return NULL;
+
+	memset(serv, 0, sizeof(*serv));
+	serv->sv_program   = prog;
+	serv->sv_nrthreads = 1;
+	serv->sv_stats     = prog->pg_stats;
+	serv->sv_bufsz	   = bufsize? bufsize : 4096;
+	serv->sv_xdrsize   = xdrsize;
+
+	serv->sv_name      = prog->pg_name;
+
+	/* Remove any stale portmap registrations */
+	svc_register(serv, 0, 0);
+
+	return serv;
+}
+
+/*
+ * Destroy an RPC service
+ */
+void
+svc_destroy(struct svc_serv *serv)
+{
+	struct svc_sock	*svsk;
+
+	dprintk("RPC: svc_destroy(%s, %d)\n",
+				serv->sv_program->pg_name,
+				serv->sv_nrthreads);
+
+	if (--(serv->sv_nrthreads) != 0)
+		return;
+	while ((svsk = serv->sv_allsocks) != NULL)
+		svc_delete_socket(svsk);
+
+	/* Unregister service with the portmapper */
+	svc_register(serv, 0, 0);
+	kfree(serv);
+}
+
+/*
+ * Allocate an RPC server buffer
+ * Later versions may do nifty things by allocating multiple pages
+ * of memory directly and putting them into the bufp->iov.
+ */
+int
+svc_init_buffer(struct svc_buf *bufp, unsigned int size)
+{
+	if (!(bufp->area = (u32 *) kmalloc(size, GFP_KERNEL)))
+		return 0;
+	bufp->base   = bufp->area;
+	bufp->buf    = bufp->area;
+	bufp->len    = 0;
+	bufp->buflen = size >> 2;
+
+	bufp->iov[0].iov_base = bufp->area;
+	bufp->iov[0].iov_len  = size;
+	bufp->nriov = 1;
+
+	return 1;
+}
+
+/*
+ * Release an RPC server buffer
+ */
+void
+svc_release_buffer(struct svc_buf *bufp)
+{
+	kfree(bufp->area);
+	bufp->area = 0;
+}
+
+/*
+ * Create a server thread
+ */
+int
+svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
+{
+	struct svc_rqst	*rqstp = 0;
+	int		error;
+
+	if (!(rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(rqstp, 0, sizeof(*rqstp));
+	if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
+	 || !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
+	 || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) {
+		error = -ENOMEM;
+		goto failure;
+	}
+
+	serv->sv_nrthreads++;
+	if ((error = kernel_thread((int (*)(void *)) func, rqstp, 0)) < 0)
+		goto failure;
+
+	rqstp->rq_server = serv;
+	return 0;
+
+failure:
+	svc_exit_thread(rqstp);
+	return error;
+}
+
+/*
+ * Destroy an RPC server thread
+ */
+void
+svc_exit_thread(struct svc_rqst *rqstp)
+{
+	struct svc_serv	*serv = rqstp->rq_server;
+
+	svc_release_buffer(&rqstp->rq_defbuf);
+	if (rqstp->rq_resp)
+		kfree(rqstp->rq_resp);
+	if (rqstp->rq_argp)
+		kfree(rqstp->rq_argp);
+	kfree(rqstp);
+
+	/* Release the server */
+	svc_destroy(serv);
+}
+
+/*
+ * Register an RPC service with the local portmapper. To
+ * unregister a service, call this routine with proto and port == 0.
+ */
+int
+svc_register(struct svc_serv *serv, int proto, unsigned short port)
+{
+	struct svc_program	*progp;
+	unsigned long		oldsigs = 0;
+	int			i, error = 0, dummy;
+
+	progp = serv->sv_program;
+
+	dprintk("RPC: svc_register(%s, %s, %d)\n",
+		progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port);
+
+	if (!port) {
+		oldsigs = current->signal;
+		current->signal = 0;
+	}
+
+	for (i = 0; i < progp->pg_nvers; i++) {
+		if (progp->pg_vers[i] == NULL)
+			continue;
+		error = rpc_register(progp->pg_prog, i, proto, port, &dummy);
+		if (error < 0)
+			return error;
+		if (port && !dummy) {
+			error = -EACCES;
+			break;
+		}
+	}
+	current->signal |= oldsigs;
+	return error;
+}
+
+/*
+ * Process the RPC request.
+ */
+int
+svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+	struct svc_program	*progp;
+	struct svc_version	*versp = NULL;	/* compiler food */
+	struct svc_procedure	*procp = NULL;
+	struct svc_buf *	argp = &rqstp->rq_argbuf;
+	struct svc_buf *	resp = &rqstp->rq_resbuf;
+	kxdrproc_t		xdr;
+	u32			*bufp, *statp;
+	u32			dir, prog, vers, proc,
+				auth_stat, rpc_stat;
+
+	rpc_stat = rpc_success;
+	bufp = argp->buf;
+
+	if (argp->len < 5)
+		goto dropit;
+
+	dir  = ntohl(*bufp++);
+	vers = ntohl(*bufp++);
+
+	/* First words of reply: */
+	svc_putlong(resp, xdr_one);		/* REPLY */
+	svc_putlong(resp, xdr_zero);		/* ACCEPT */
+
+	if (dir != 0) {		/* direction != CALL */
+		serv->sv_stats->rpcbadfmt++;
+		goto dropit;			/* drop request */
+	}
+	if (vers != 2) {		/* RPC version number */
+		serv->sv_stats->rpcbadfmt++;
+		resp->buf[-1] = xdr_one;	/* REJECT */
+		svc_putlong(resp, xdr_zero);	/* RPC_MISMATCH */
+		svc_putlong(resp, xdr_two);	/* Only RPCv2 supported */
+		svc_putlong(resp, xdr_two);
+		goto error;
+	}
+
+	rqstp->rq_prog = prog = ntohl(*bufp++);	/* program number */
+	rqstp->rq_vers = vers = ntohl(*bufp++);	/* version number */
+	rqstp->rq_proc = proc = ntohl(*bufp++);	/* procedure number */
+
+	argp->buf += 5;
+	argp->len -= 5;
+
+	/*
+	 * Decode auth data, and add verifier to reply buffer.
+	 * We do this before anything else in order to get a decent
+	 * auth verifier.
+	 */
+	svc_authenticate(rqstp, &rpc_stat, &auth_stat);
+
+	if (rpc_stat != rpc_success) {
+		serv->sv_stats->rpcbadfmt++;
+		svc_putlong(resp, rpc_garbage_args);
+		goto error;
+	}
+
+	if (auth_stat != rpc_auth_ok) {
+		dprintk("svc: authentication failed (%ld)\n", ntohl(auth_stat));
+		serv->sv_stats->rpcbadauth++;
+		resp->buf[-1] = xdr_one;	/* REJECT */
+		svc_putlong(resp, xdr_one);	/* AUTH_ERROR */
+		svc_putlong(resp, auth_stat);	/* status */
+		goto error;
+		return svc_send(rqstp);
+	}
+
+	progp = serv->sv_program;
+	if (prog != progp->pg_prog) {
+		dprintk("svc: unknown program %d (me %d)\n", prog, progp->pg_prog);
+		serv->sv_stats->rpcbadfmt++;
+		svc_putlong(resp, rpc_prog_unavail);
+		goto error;
+		return svc_send(rqstp);
+	}
+
+	versp = progp->pg_vers[vers];
+	if (!versp || vers >= progp->pg_nvers) {
+		dprintk("svc: unknown version (%d)\n", vers);
+		serv->sv_stats->rpcbadfmt++;
+		svc_putlong(resp, rpc_prog_mismatch);
+		svc_putlong(resp, htonl(progp->pg_lovers));
+		svc_putlong(resp, htonl(progp->pg_hivers));
+		goto error;
+		return svc_send(rqstp);
+	}
+
+	procp = versp->vs_proc + proc;
+	if (proc >= versp->vs_nproc || !procp->pc_func) {
+		dprintk("svc: unknown procedure (%d)\n", proc);
+		serv->sv_stats->rpcbadfmt++;
+		svc_putlong(resp, rpc_proc_unavail);
+		goto error;
+		return svc_send(rqstp);
+	}
+	rqstp->rq_server   = serv;
+	rqstp->rq_procinfo = procp;
+
+	/* Syntactic check complete */
+	serv->sv_stats->rpccnt++;
+
+	/* Build the reply header. */
+	statp = resp->buf;
+	svc_putlong(resp, rpc_success);		/* RPC_SUCCESS */
+
+	/* Bump per-procedure stats counter */
+	procp->pc_count++;
+
+	/* Initialize storage for argp and resp */
+	memset(rqstp->rq_argp, 0, procp->pc_argsize);
+	memset(rqstp->rq_resp, 0, procp->pc_ressize);
+
+	/* Call the function that processes the request. */
+	if (!versp->vs_dispatch) {
+		/* Decode arguments */
+		xdr = procp->pc_decode;
+		if (xdr && !xdr(rqstp, rqstp->rq_argbuf.buf, rqstp->rq_argp)) {
+			dprintk("svc: failed to decode args\n");
+			serv->sv_stats->rpcbadfmt++;
+			svc_putlong(resp, rpc_garbage_args);
+			goto error;
+		}
+
+		*statp = procp->pc_func(rqstp,
+					rqstp->rq_argp,
+					rqstp->rq_resp);
+
+		/* Encode reply */
+		if (*statp == rpc_success && (xdr = procp->pc_encode)
+		 && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
+			dprintk("svc: failed to encode reply\n");
+			/* serv->sv_stats->rpcsystemerr++; */
+			*statp = rpc_system_err;
+		}
+	} else {
+		dprintk("svc: calling dispatcher\n");
+		if (!versp->vs_dispatch(rqstp, statp))
+			goto dropit;
+	}
+
+	/* Check RPC status result */
+	if (*statp != rpc_success)
+		resp->len = statp + 1 - resp->base;
+
+	/* Release reply info */
+	if (procp->pc_release)
+		procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+
+	if (procp->pc_encode != NULL)
+		return svc_send(rqstp);
+
+dropit:
+	dprintk("svc: svc_process dropit\n");
+	svc_drop(rqstp);
+	return 0;
+
+error:
+	return svc_send(rqstp);
+}
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcauth.c linux/net/sunrpc/svcauth.c
--- v2.1.31/linux/net/sunrpc/svcauth.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/svcauth.c	Fri Dec 20 08:34:32 1996
@@ -0,0 +1,163 @@
+/*
+ * linux/net/sunrpc/svcauth.c
+ *
+ * The generic interface for RPC authentication on the server side.
+ * 
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY	RPCDBG_AUTH
+
+/*
+ * Type of authenticator function
+ */
+typedef void	(*auth_fn_t)(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+
+/*
+ * Builtin auth flavors
+ */
+static void	svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+static void	svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+
+/*
+ * Max number of authentication flavors we support
+ */
+#define RPC_SVCAUTH_MAX	8
+
+/*
+ * Table of authenticators
+ */
+static auth_fn_t	authtab[RPC_SVCAUTH_MAX] = {
+	svcauth_null,
+	svcauth_unix,
+	NULL,
+};
+
+void
+svc_authenticate(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+	u32		flavor;
+	auth_fn_t	func;
+
+	*statp = rpc_success;
+	*authp = rpc_auth_ok;
+
+	svc_getlong(&rqstp->rq_argbuf, flavor);
+	flavor = ntohl(flavor);
+
+	dprintk("svc: svc_authenticate (%d)\n", flavor);
+	if (flavor >= RPC_SVCAUTH_MAX || !(func = authtab[flavor])) {
+		*authp = rpc_autherr_badcred;
+		return;
+	}
+
+	rqstp->rq_cred.cr_flavor = flavor;
+	func(rqstp, statp, authp);
+}
+
+int
+svc_auth_register(u32 flavor, auth_fn_t func)
+{
+	if (flavor >= RPC_SVCAUTH_MAX || authtab[flavor])
+		return -EINVAL;
+	authtab[flavor] = func;
+	return 0;
+}
+
+void
+svc_auth_unregister(u32 flavor)
+{
+	if (flavor < RPC_SVCAUTH_MAX)
+		authtab[flavor] = NULL;
+}
+
+static void
+svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+	struct svc_buf	*argp = &rqstp->rq_argbuf;
+	struct svc_buf	*resp = &rqstp->rq_resbuf;
+
+	if ((argp->len -= 3) < 0) {
+		*statp = rpc_garbage_args;
+		return;
+	}
+	if (*(argp->buf)++ != 0) {	/* we already skipped the flavor */
+		dprintk("svc: bad null cred\n");
+		*authp = rpc_autherr_badcred;
+		return;
+	}
+	if (*(argp->buf)++ != RPC_AUTH_NULL || *(argp->buf)++ != 0) {
+		dprintk("svc: bad null verf\n");
+		*authp = rpc_autherr_badverf;
+		return;
+	}
+
+	/* Signal that mapping to nobody uid/gid is required */
+	rqstp->rq_cred.cr_uid = (uid_t) -1;
+	rqstp->rq_cred.cr_gid = (gid_t) -1;
+	rqstp->rq_cred.cr_groups[0] = NOGROUP;
+
+	/* Put NULL verifier */
+	rqstp->rq_verfed = 1;
+	svc_putlong(resp, RPC_AUTH_NULL);
+	svc_putlong(resp, 0);
+}
+
+static void
+svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+	struct svc_buf	*argp = &rqstp->rq_argbuf;
+	struct svc_buf	*resp = &rqstp->rq_resbuf;
+	struct svc_cred	*cred = &rqstp->rq_cred;
+	u32		*bufp = argp->buf;
+	int		len   = argp->len, slen, i;
+
+	if ((len -= 3) < 0) {
+		*statp = rpc_garbage_args;
+		return;
+	}
+
+	bufp++;					/* length */
+	bufp++;					/* time stamp */
+	slen = (ntohl(*bufp++) + 3) >> 2;	/* machname length */
+	if (slen > 64 || (len -= slen) < 0)
+		goto badcred;
+	bufp += slen;				/* skip machname */
+
+	cred->cr_uid = ntohl(*bufp++);		/* uid */
+	cred->cr_gid = ntohl(*bufp++);		/* gid */
+
+	slen = ntohl(*bufp++);			/* gids length */
+	if (slen > 16 || (len -= slen + 2) < 0)
+		goto badcred;
+	for (i = 0; i < NGROUPS && i < slen; i++)
+		cred->cr_groups[i] = ntohl(*bufp++);
+	if (i < NGROUPS)
+		cred->cr_groups[i] = NOGROUP;
+	bufp += (slen - i);
+
+	if (*bufp++ != RPC_AUTH_NULL || *bufp++ != 0) {
+		*authp = rpc_autherr_badverf;
+		return;
+	}
+
+	argp->buf = bufp;
+	argp->len = len;
+
+	/* Put NULL verifier */
+	rqstp->rq_verfed = 1;
+	svc_putlong(resp, RPC_AUTH_NULL);
+	svc_putlong(resp, 0);
+
+	return;
+
+badcred:
+	*authp = rpc_autherr_badcred;
+}
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcauth_des.c linux/net/sunrpc/svcauth_des.c
--- v2.1.31/linux/net/sunrpc/svcauth_des.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/svcauth_des.c	Fri Jan  3 04:10:28 1997
@@ -0,0 +1,215 @@
+/*
+ * linux/net/sunrpc/svcauth_des.c
+ *
+ * Server-side AUTH_DES handling.
+ * 
+ * Copyright (C) 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY	RPCDBG_AUTH
+
+/*
+ * DES cedential cache.
+ * The cache is indexed by fullname/key to allow for multiple sessions
+ * by the same user from different hosts.
+ * It would be tempting to use the client's IP address rather than the
+ * conversation key as an index, but that could become problematic for
+ * multi-homed hosts that distribute traffic across their interfaces.
+ */
+struct des_cred {
+	struct des_cred *	dc_next;
+	char *			dc_fullname;
+	u32			dc_nickname;
+	des_cblock		dc_key;		/* conversation key */
+	des_cblock		dc_xkey;	/* encrypted conv. key */
+	des_key_schedule	dc_keysched;
+};
+
+#define ADN_FULLNAME		0
+#define ADN_NICKNAME		1
+
+/*
+ * The default slack allowed when checking for replayed credentials
+ * (in milliseconds).
+ */
+#define DES_REPLAY_SLACK	2000
+
+/*
+ * Make sure we don't place more than one call to the key server at
+ * a time.
+ */
+static int			in_keycall = 0;
+
+#define FAIL(err) \
+	{ if (data) put_cred(data);			\
+	  *authp = rpc_autherr_##err;			\
+	  return;					\
+	}
+
+void
+svcauth_des(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+	struct svc_buf	*argp = &rqstp->rq_argbuf;
+	struct svc_buf	*resp = &rqstp->rq_resbuf;
+	struct svc_cred	*cred = &rqstp->rq_cred;
+	struct des_cred	*data = NULL;
+	u32		cryptkey[2];
+	u32		cryptbuf[4];
+	u32		*p = argp->buf;
+	int		len   = argp->len, slen, i;
+
+	*authp = rpc_auth_ok;
+
+	if ((argp->len -= 3) < 0) {
+		*statp = rpc_garbage_args;
+		return;
+	}
+
+	p++;					/* skip length field */
+	namekind = ntohl(*p++);			/* fullname/nickname */
+
+	/* Get the credentials */
+	if (namekind == ADN_NICKNAME) {
+		/* If we can't find the cached session key, initiate a
+		 * new session. */
+		if (!(data = get_cred_bynick(*p++)))
+			FAIL(rejectedcred);
+	} else if (namekind == ADN_FULLNAME) {
+		p = xdr_decode_string(p, &fullname, &len, RPC_MAXNETNAMELEN);
+		if (p == NULL)
+			FAIL(badcred);
+		cryptkey[0] = *p++;		/* get the encrypted key */
+		cryptkey[1] = *p++;
+		cryptbuf[2] = *p++;		/* get the encrypted window */
+	} else {
+		FAIL(badcred);
+	}
+
+	/* If we're just updating the key, silently discard the request. */
+	if (data && data->dc_locked) {
+		*authp = rpc_autherr_dropit;
+		_put_cred(data);	/* release but don't unlock */
+		return;
+	}
+
+	/* Get the verifier flavor and length */
+	if (ntohl(*p++) != RPC_AUTH_DES && ntohl(*p++) != 12)
+		FAIL(badverf);
+
+	cryptbuf[0] = *p++;			/* encrypted time stamp */
+	cryptbuf[1] = *p++;
+	cryptbuf[3] = *p++;			/* 0 or window - 1 */
+
+	if (namekind == ADN_NICKNAME) {
+		status = des_ecb_encrypt((des_block *) cryptbuf,
+					 (des_block *) cryptbuf,
+					 data->dc_keysched, DES_DECRYPT);
+	} else {
+		/* We first have to decrypt the new session key and
+		 * fill in the UNIX creds. */
+		if (!(data = get_cred_byname(rqstp, authp, fullname, cryptkey)))
+			return;
+		status = des_cbc_encrypt((des_cblock *) cryptbuf,
+					 (des_cblock *) cryptbuf, 16,
+					 data->dc_keysched,
+					 (des_cblock *) &ivec,
+					 DES_DECRYPT);
+	}
+	if (status) {
+		printk("svcauth_des: DES decryption failed (status %d)\n",
+				status);
+		FAIL(badverf);
+	}
+
+	/* Now check the whole lot */
+	if (namekind == ADN_FULLNAME) {
+		unsigned long	winverf;
+
+		data->dc_window = ntohl(cryptbuf[2]);
+		winverf = ntohl(cryptbuf[2]);
+		if (window != winverf - 1) {
+			printk("svcauth_des: bad window verifier!\n");
+			FAIL(badverf);
+		}
+	}
+
+	/* XDR the decrypted timestamp */
+	cryptbuf[0] = ntohl(cryptbuf[0]);
+	cryptbuf[1] = ntohl(cryptbuf[1]);
+	if (cryptbuf[1] > 1000000) {
+		dprintk("svcauth_des: bad usec value %u\n", cryptbuf[1]);
+		if (namekind == ADN_NICKNAME)
+			FAIL(rejectedverf);
+		FAIL(badverf);
+	}
+	
+	/*
+	 * Check for replayed credentials. We must allow for reordering
+	 * of requests by the network, and the OS scheduler, hence we
+	 * cannot expect timestamps to be increasing monotonically.
+	 * This opens a small security hole, therefore the replay_slack
+	 * value shouldn't be too large.
+	 */
+	if ((delta = cryptbuf[0] - data->dc_timestamp[0]) <= 0) {
+		switch (delta) {
+		case -1:	
+			delta = -1000000;
+		case 0:
+			delta += cryptbuf[1] - data->dc_timestamp[1];
+			break;
+		default:
+			delta = -1000000;
+		}
+		if (delta < DES_REPLAY_SLACK)
+			FAIL(rejectedverf);
+#ifdef STRICT_REPLAY_CHECKS
+		/* TODO: compare time stamp to last five timestamps cached
+		 * and reject (drop?) request if a match is found. */
+#endif
+	}
+
+	now = xtime;
+	now.tv_secs -= data->dc_window;
+	if (now.tv_secs < cryptbuf[0] ||
+	    (now.tv_secs == cryptbuf[0] && now.tv_usec < cryptbuf[1]))
+		FAIL(rejectedverf);
+
+	/* Okay, we're done. Update the lot */
+	if (namekind == ADN_FULLNAME)
+		data->dc_valid = 1;
+	data->dc_timestamp[0] = cryptbuf[0];
+	data->dc_timestamp[1] = cryptbuf[1];
+
+	put_cred(data);
+	return;
+garbage:
+	*statp = rpc_garbage_args;
+	return;
+}
+
+/*
+ * Call the keyserver to obtain the decrypted conversation key and
+ * UNIX creds. We use a Linux-specific keycall extension that does
+ * both things in one go.
+ */
+static struct des_cred *
+get_cred_byname(struct svc_rqst *rqstp, u32 *authp, char *fullname, u32 *cryptkey)
+{
+	static int	in_keycall = 0;
+	struct des_cred	*cred;
+
+	if (in_keycall) {
+		*authp = rpc_autherr_dropit;
+		return NULL;
+	}
+	in_keycall = 1;
+	in_keycall = 0;
+	return cred;
+}
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- v2.1.31/linux/net/sunrpc/svcsock.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/svcsock.c	Thu Mar 20 07:03:40 1997
@@ -0,0 +1,967 @@
+/*
+ * linux/net/sunrpc/svcsock.c
+ *
+ * These are the RPC server socket internals.
+ *
+ * The server scheduling algorithm does not always distribute the load
+ * evenly when servicing a single client. May need to modify the
+ * svc_sock_enqueue procedure...
+ *
+ * TCP support is largely untested and may be a little slow. The problem
+ * is that we currently do two separate recvfrom's, one for the 4-byte
+ * record length, and the second for the actual record. This could possibly
+ * be improved by always reading a minimum size of around 100 bytes and
+ * tucking any superfluous bytes away in a temporary store. Still, that
+ * leaves write requests out in the rain. An alternative may be to peek at
+ * the first skb in the queue, and if it matches the next TCP sequence
+ * number, to extract the record marker. Yuck.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/udp.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#endif
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/stats.h>
+
+
+#define RPCDBG_FACILITY	RPCDBG_SVCSOCK
+
+
+static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
+					 int *errp, int pmap_reg);
+static void		svc_udp_data_ready(struct sock *, int);
+static int		svc_udp_recvfrom(struct svc_rqst *);
+static int		svc_udp_sendto(struct svc_rqst *);
+
+
+/*
+ * Queue up an idle server thread.
+ */
+static inline void
+svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+	rpc_append_list(&serv->sv_threads, rqstp);
+}
+
+/*
+ * Dequeue an nfsd thread.
+ */
+static inline void
+svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+	rpc_remove_list(&serv->sv_threads, rqstp);
+}
+
+/*
+ * Release an skbuff after use
+ */
+static inline void
+svc_release_skb(struct svc_rqst *rqstp)
+{
+	if (!rqstp->rq_skbuff)
+		return;
+
+	dprintk("svc: releasing skb %p\n", rqstp->rq_skbuff);
+	skb_free_datagram(rqstp->rq_sock->sk_sk, rqstp->rq_skbuff);
+	rqstp->rq_skbuff = NULL;
+}
+
+/*
+ * Queue up a socket with data pending. If there are idle nfsd
+ * processes, wake'em up.
+ * When calling this function, you should make sure it can't be interrupted
+ * by the network bottom half.
+ */
+static inline void
+svc_sock_enqueue(struct svc_sock *svsk)
+{
+	struct svc_rqst	*rqstp;
+	struct svc_serv	*serv;
+
+	if (svsk->sk_busy) {
+		/* Don't enqueue socket while daemon is receiving */
+		dprintk("svc: socket %p not enqueued: busy\n", svsk->sk_sk);
+		return;
+	}
+
+	/* Mark socket as busy. It will remain in this state until the
+	 * server has processed all pending data and put the socket back
+	 * on the idle list
+	 */
+	svsk->sk_busy = 1;
+
+	serv = svsk->sk_server;
+	if ((rqstp = serv->sv_threads) != NULL) {
+		dprintk("svc: socket %p served by daemon %p\n",
+						svsk->sk_sk, rqstp);
+		svc_serv_dequeue(serv, rqstp);
+		rqstp->rq_sock = svsk;
+		svsk->sk_inuse++;
+		wake_up(&rqstp->rq_wait);
+	} else {
+		dprintk("svc: socket %p put into queue\n", svsk->sk_sk);
+		rpc_append_list(&serv->sv_sockets, svsk);
+		svsk->sk_qued = 1;
+	}
+}
+
+/*
+ * Dequeue the first socket.
+ */
+static inline struct svc_sock *
+svc_sock_dequeue(struct svc_serv *serv)
+{
+	struct svc_sock	*svsk;
+
+	disable_bh(NET_BH);
+	if ((svsk = serv->sv_sockets) != NULL)
+		rpc_remove_list(&serv->sv_sockets, svsk);
+	enable_bh(NET_BH);
+
+	if (svsk) {
+		dprintk("svc: socket %p dequeued\n", svsk->sk_sk);
+		svsk->sk_qued = 0;
+	}
+
+	return svsk;
+}
+
+/*
+ * Having read count bytes from a socket, check whether it
+ * needs to be re-enqueued.
+ */
+static inline void
+svc_sock_received(struct svc_sock *svsk, int count)
+{
+	disable_bh(NET_BH);
+	if ((svsk->sk_data -= count) < 0) {
+		printk(KERN_NOTICE "svc: sk_data negative!\n");
+		svsk->sk_data = 0;
+	}
+	svsk->sk_rqstp = NULL; /* XXX */
+	svsk->sk_busy = 0;
+	if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) {
+		dprintk("svc: socket %p re-enqueued after receive\n",
+						svsk->sk_sk);
+		svc_sock_enqueue(svsk);
+	}
+	enable_bh(NET_BH);
+}
+
+/*
+ * Dequeue a new connection.
+ */
+static inline void
+svc_sock_accepted(struct svc_sock *svsk)
+{
+	disable_bh(NET_BH);
+        svsk->sk_busy = 0;
+        svsk->sk_conn--;
+        if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) {
+                dprintk("svc: socket %p re-enqueued after accept\n",
+						svsk->sk_sk);
+                svc_sock_enqueue(svsk);
+        }
+	enable_bh(NET_BH);
+}
+
+/*
+ * Release a socket after use.
+ */
+static inline void
+svc_sock_release(struct svc_rqst *rqstp)
+{
+	struct svc_sock	*svsk = rqstp->rq_sock;
+
+	if (!svsk)
+		return;
+	svc_release_skb(rqstp);
+	rqstp->rq_sock = NULL;
+	if (!--(svsk->sk_inuse) && svsk->sk_dead) {
+		dprintk("svc: releasing dead socket\n");
+		sock_release(svsk->sk_sock);
+		kfree(svsk);
+	}
+}
+
+/*
+ * External function to wake up a server waiting for data
+ */
+void
+svc_wake_up(struct svc_serv *serv)
+{
+	struct svc_rqst	*rqstp;
+
+	if ((rqstp = serv->sv_threads) != NULL) {
+		dprintk("svc: daemon %p woken up.\n", rqstp);
+		/*
+		svc_serv_dequeue(serv, rqstp);
+		rqstp->rq_sock = NULL;
+		 */
+		wake_up(&rqstp->rq_wait);
+	}
+}
+
+/*
+ * Generic sendto routine
+ */
+static int
+svc_sendto(struct svc_rqst *rqstp, struct iovec *iov, int nr)
+{
+	unsigned long	oldfs;
+	struct socket	*sock = rqstp->rq_sock->sk_sock;
+	struct msghdr	msg;
+	int		i, buflen, len;
+
+	for (i = buflen = 0; i < nr; i++)
+		buflen += iov[i].iov_len;
+
+	msg.msg_name    = &rqstp->rq_addr;
+	msg.msg_namelen = sizeof(rqstp->rq_addr);
+	msg.msg_iov     = iov;
+	msg.msg_iovlen  = nr;
+	msg.msg_control = 0;
+
+#if LINUX_VERSION_CODE >= 0x020100
+	msg.msg_flags	= MSG_DONTWAIT;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	len = sock_sendmsg(sock, &msg, buflen);
+	set_fs(oldfs);
+#else
+	msg.msg_flags	= 0;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	len = sock->ops->sendmsg(sock, &msg, buflen, 1, 0);
+	set_fs(oldfs);
+#endif
+
+	dprintk("svc: socket %p sendto([%p %d... ], %d, %d) = %d\n",
+			rqstp->rq_sock,
+			iov[0].iov_base, iov[0].iov_len, nr,
+			buflen, len);
+
+	return len;
+}
+
+/*
+ * Check input queue length
+ */
+static int
+svc_recv_available(struct svc_sock *svsk)
+{
+	unsigned long	oldfs;
+	struct socket	*sock = svsk->sk_sock;
+	int		avail, err;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	err = sock->ops->ioctl(sock, TIOCINQ, (unsigned long) &avail);
+	set_fs(oldfs);
+
+	return (err >= 0)? avail : err;
+}
+
+/*
+ * Generic recvfrom routine.
+ */
+static int
+svc_recvfrom(struct svc_rqst *rqstp, struct iovec *iov, int nr, int buflen)
+{
+	unsigned long	oldfs;
+	struct msghdr	msg;
+	struct socket	*sock;
+	int		len;
+
+	rqstp->rq_addrlen = sizeof(rqstp->rq_addr);
+	sock = rqstp->rq_sock->sk_sock;
+
+	msg.msg_name    = &rqstp->rq_addr;
+	msg.msg_namelen = sizeof(rqstp->rq_addr);
+	msg.msg_iov     = iov;
+	msg.msg_iovlen  = nr;
+	msg.msg_control = 0;
+
+#if LINUX_VERSION_CODE >= 0x020100
+	msg.msg_flags	= MSG_DONTWAIT;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	len = sock_recvmsg(sock, &msg, buflen, MSG_DONTWAIT);
+	set_fs(oldfs);
+#else
+	msg.msg_flags	= 0;
+
+	oldfs = get_fs(); set_fs(KERNEL_DS);
+	len = sock->ops->recvmsg(sock, &msg, buflen, 0, 1, &rqstp->rq_addrlen);
+	set_fs(oldfs);
+#endif
+
+	dprintk("svc: socket %p recvfrom(%p, %d) = %d\n", rqstp->rq_sock,
+				iov[0].iov_base, iov[0].iov_len, len);
+
+	return len;
+}
+
+/*
+ * INET callback when data has been received on the socket.
+ */
+static void
+svc_udp_data_ready(struct sock *sk, int count)
+{
+	struct svc_sock	*svsk;
+
+	dprintk("svc: socket %p data ready (inet %p)\n", sk->user_data, sk);
+
+	svsk = (struct svc_sock *)(sk->user_data);
+	if (!svsk)
+		return;
+	svsk->sk_data = 1;
+	svc_sock_enqueue(svsk);
+}
+
+/*
+ * Receive a datagram from a UDP socket.
+ */
+static int
+svc_udp_recvfrom(struct svc_rqst *rqstp)
+{
+	struct svc_sock	*svsk = rqstp->rq_sock;
+	struct svc_serv	*serv = svsk->sk_server;
+	struct sk_buff	*skb;
+	u32		*data;
+	int		err, len;
+
+	svsk->sk_data = 0;
+	while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) {
+		svc_sock_received(svsk, 0);
+		if (err == -EAGAIN)
+			return err;
+		/* possibly an icmp error */
+		dprintk("svc: recvfrom returned error %d\n", -err);
+	}
+
+	/* There may be more data */
+	svsk->sk_data = 1;
+
+	len  = skb->len - sizeof(struct udphdr);
+	data = (u32 *) (skb->h.raw + sizeof(struct udphdr));
+
+	rqstp->rq_skbuff      = skb;
+	rqstp->rq_argbuf.base = data;
+	rqstp->rq_argbuf.buf  = data;
+	rqstp->rq_argbuf.len  = (len >> 2);
+	/* rqstp->rq_resbuf      = rqstp->rq_defbuf; */
+	rqstp->rq_prot        = IPPROTO_UDP;
+
+	/* Get sender address */
+	rqstp->rq_addr.sin_family = AF_INET;
+	rqstp->rq_addr.sin_port = skb->h.uh->source;
+#if LINUX_VERSION_CODE >= 0x020100
+	rqstp->rq_addr.sin_addr.s_addr = skb->nh.iph->saddr;
+#else
+	rqstp->rq_addr.sin_addr.s_addr = skb->saddr;
+#endif
+
+	if (serv->sv_stats)
+		serv->sv_stats->netudpcnt++;
+
+	/* One down, maybe more to go... */
+	svsk->sk_sk->stamp = skb->stamp;
+	svc_sock_received(svsk, 0);
+
+	return len;
+}
+
+static int
+svc_udp_sendto(struct svc_rqst *rqstp)
+{
+	struct svc_buf	*bufp = &rqstp->rq_resbuf;
+	int		error;
+
+	/* Set up the first element of the reply iovec.
+	 * Any other iovecs that may be in use have been taken
+	 * care of by the server implementation itself.
+	 */
+	/* bufp->base = bufp->area; */
+	bufp->iov[0].iov_base = bufp->base;
+	bufp->iov[0].iov_len  = bufp->len << 2;
+
+	error = svc_sendto(rqstp, bufp->iov, bufp->nriov);
+	if (error == -ECONNREFUSED)
+		/* ICMP error on earlier request. */
+		error = svc_sendto(rqstp, bufp->iov, bufp->nriov);
+	else if (error == -EAGAIN)
+		/* Ignore and wait for re-xmit */
+		error = 0;
+
+	return error;
+}
+
+static int
+svc_udp_init(struct svc_sock *svsk)
+{
+	svsk->sk_sk->data_ready = svc_udp_data_ready;
+	svsk->sk_recvfrom = svc_udp_recvfrom;
+	svsk->sk_sendto = svc_udp_sendto;
+
+	return 0;
+}
+
+/*
+ * A state change on a listening socket means there's a connection
+ * pending.
+ */
+static void
+svc_tcp_state_change1(struct sock *sk)
+{
+	struct svc_sock	*svsk;
+
+	dprintk("svc: socket %p TCP (listen) state change %d\n",
+			sk, sk->state);
+
+	if  (sk->state != TCP_ESTABLISHED) {
+		/* Aborted connection, SYN_RECV or whatever... */
+		return;
+	}
+	if (!(svsk = (struct svc_sock *) sk->user_data)) {
+		printk("svc: socket %p: no user data\n", sk);
+		return;
+	}
+	svsk->sk_conn++;
+	svc_sock_enqueue(svsk);
+}
+
+/*
+ * A state change on a connected socket means it's dying or dead.
+ */
+static void
+svc_tcp_state_change2(struct sock *sk)
+{
+	struct svc_sock	*svsk;
+
+	dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n",
+			sk, sk->state, sk->user_data);
+
+	if (!(svsk = (struct svc_sock *) sk->user_data)) {
+		printk("svc: socket %p: no user data\n", sk);
+		return;
+	}
+	svsk->sk_close = 1;
+	svc_sock_enqueue(svsk);
+}
+
+static void
+svc_tcp_data_ready(struct sock *sk, int count)
+{
+	struct svc_sock *	svsk;
+
+	/* Disconnect signalled through data_ready?!? */
+	if (sk->state != TCP_ESTABLISHED) {
+		svc_tcp_state_change2(sk);
+		return;
+	}
+
+	dprintk("svc: socket %p TCP data ready (svsk %p)\n",
+			sk, sk->user_data);
+	if (!(svsk = (struct svc_sock *)(sk->user_data)))
+		return;
+	svsk->sk_data++;
+	svc_sock_enqueue(svsk);
+}
+
+/*
+ * Accept a TCP connection
+ */
+static void
+svc_tcp_accept(struct svc_sock *svsk)
+{
+	struct sockaddr_in sin;
+	struct svc_serv	*serv = svsk->sk_server;
+	struct socket	*sock = svsk->sk_sock;
+	struct socket	*newsock;
+	struct proto_ops *ops;
+	struct svc_sock	*newsvsk;
+	int		err, slen;
+
+	dprintk("svc: tcp_accept %p sock %p\n", svsk, sock);
+	if (!sock)
+		return;
+
+	if (!(newsock = sock_alloc())) {
+		printk(KERN_WARNING "%s: no more sockets!\n", serv->sv_name);
+		return;
+	}
+	dprintk("svc: tcp_accept %p allocated\n", newsock);
+
+	newsock->type = sock->type;
+	if ((err = sock->ops->dup(newsock, sock)) < 0) {
+		printk(KERN_WARNING "%s: socket dup failed (err %d)!\n",
+					serv->sv_name, -err);
+		goto failed;
+	}
+
+	ops = newsock->ops;
+	if ((err = ops->accept(sock, newsock, O_NONBLOCK)) < 0) {
+		printk(KERN_WARNING "%s: accept failed (err %d)!\n",
+					serv->sv_name, -err);
+		goto failed;		/* aborted connection or whatever */
+	}
+
+	slen = sizeof(sin);
+	err = ops->getname(newsock, (struct sockaddr *) &sin, &slen, 1);
+	if (err < 0) {
+		printk(KERN_WARNING "%s: peername failed (err %d)!\n",
+					serv->sv_name, -err);
+		goto failed;		/* aborted connection or whatever */
+	}
+
+	/* Ideally, we would want to reject connections from unauthorized
+	 * hosts here, but we have no generic client tables. For now,
+	 * we just punt connects from unprivileged ports. */
+	if (ntohs(sin.sin_port) >= 1024) {
+		printk(KERN_WARNING
+			"%s: connect from unprivileged port: %08lx:%d",
+			serv->sv_name, 
+			ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+		goto failed;
+	}
+
+	dprintk("%s: connect from %08lx:%04x\n", serv->sv_name,
+			ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+
+	if (!(newsvsk = svc_setup_socket(serv, newsock, &err, 0)))
+		goto failed;
+
+	/* Precharge. Data may have arrived on the socket before we
+	 * installed the data_ready callback. 
+	 */
+	newsvsk->sk_data = 1;
+	newsvsk->sk_temp = 1;
+	svc_sock_enqueue(newsvsk);
+
+	if (serv->sv_stats)
+		serv->sv_stats->nettcpconn++;
+
+	return;
+
+failed:
+	sock_release(newsock);
+	return;
+}
+
+/*
+ * Receive data from a TCP socket.
+ */
+static int
+svc_tcp_recvfrom(struct svc_rqst *rqstp)
+{
+	struct svc_sock	*svsk = rqstp->rq_sock;
+	struct svc_serv	*serv = svsk->sk_server;
+	struct svc_buf	*bufp = &rqstp->rq_argbuf;
+	int		len, ready;
+
+	dprintk("svc: tcp_recv %p data %d conn %d close %d\n",
+			svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close);
+
+	if (svsk->sk_close) {
+		svc_delete_socket(svsk);
+		return 0;
+	}
+
+	if (svsk->sk_conn) {
+		svc_tcp_accept(svsk);
+		svc_sock_accepted(svsk);
+		return 0;
+	}
+
+	ready = svsk->sk_data;
+
+	/* Receive data. If we haven't got the record length yet, get
+	 * the next four bytes. Otherwise try to gobble up as much as
+	 * possible up to the complete record length.
+	 */
+	if (svsk->sk_tcplen < 4) {
+		unsigned long	want = 4 - svsk->sk_tcplen;
+		struct iovec	iov;
+
+		iov.iov_base = ((u32 *) &svsk->sk_reclen) + svsk->sk_tcplen;
+		iov.iov_len  = want;
+		if ((len = svc_recvfrom(rqstp, &iov, 1, want)) < 0)
+			goto error;
+		svsk->sk_tcplen += len;
+
+		svsk->sk_reclen = ntohl(svsk->sk_reclen);
+		if (!(svsk->sk_reclen & 0x80000000)) {
+			/* FIXME: shutdown socket */
+			printk(KERN_NOTICE "RPC: bad TCP reclen %08lx",
+					(unsigned long) svsk->sk_reclen);
+			return -EIO;
+		}
+		svsk->sk_reclen &= 0x7fffffff;
+		dprintk("svc: TCP record, %ld bytes\n", svsk->sk_reclen);
+	}
+
+	/* Check whether enough data is available */
+	len = svc_recv_available(svsk);
+	if (len < 0)
+		goto error;
+
+	if (len < svsk->sk_reclen) {
+		dprintk("svc: incomplete TCP record (%d of %ld)\n",
+				len, svsk->sk_reclen);
+		svc_sock_received(svsk, ready);
+		len = -EAGAIN;	/* record not complete */
+	}
+
+	/* Frob argbuf */
+	bufp->iov[0].iov_base += 4;
+	bufp->iov[0].iov_len  -= 4;
+
+	/* Now receive data */
+	len = svc_recvfrom(rqstp, bufp->iov, bufp->nriov, svsk->sk_reclen);
+	if (len < 0)
+		goto error;
+
+	dprintk("svc: TCP complete record (%d bytes)\n", len);
+
+	/* Position reply write pointer immediately after
+	 * record length */
+	rqstp->rq_resbuf.buf += 1;
+	rqstp->rq_resbuf.len  = 1;
+
+	rqstp->rq_skbuff      = 0;
+	rqstp->rq_argbuf.buf += 1;
+	rqstp->rq_argbuf.len  = (len >> 2);
+	rqstp->rq_prot	      = IPPROTO_TCP;
+
+	/* Reset TCP read info */
+	svsk->sk_reclen = 0;
+	svsk->sk_tcplen = 0;
+
+	svc_sock_received(svsk, 1);
+	if (serv->sv_stats)
+		serv->sv_stats->nettcpcnt++;
+
+	return len;
+
+error:
+	if (len == -EAGAIN) {
+		dprintk("RPC: TCP recvfrom got EAGAIN\n");
+		svc_sock_received(svsk, ready); /* Clear data ready */
+	} else {
+		printk(KERN_NOTICE "%s: recvfrom returned errno %d\n",
+					svsk->sk_server->sv_name, -len);
+		svc_sock_received(svsk, 0);
+	}
+
+	return len;
+}
+
+/*
+ * Send out data on TCP socket.
+ * FIXME: Make the sendto call non-blocking in order not to hang
+ * a daemon on a a dead client. Requires write queue maintenance.
+ */
+static int
+svc_tcp_sendto(struct svc_rqst *rqstp)
+{
+	struct svc_buf	*bufp = &rqstp->rq_resbuf;
+
+	/* Set up the first element of the reply iovec.
+	 * Any other iovecs that may be in use have been taken
+	 * care of by the server implementation itself.
+	 */
+	bufp->iov[0].iov_base = bufp->base;
+	bufp->iov[0].iov_len  = bufp->len << 2;
+	bufp->base[0] = htonl(0x80000000|((bufp->len << 2) - 4));
+
+	return svc_sendto(rqstp, bufp->iov, bufp->nriov);
+}
+
+static int
+svc_tcp_init(struct svc_sock *svsk)
+{
+	struct sock	*sk = svsk->sk_sk;
+
+	svsk->sk_recvfrom = svc_tcp_recvfrom;
+	svsk->sk_sendto = svc_tcp_sendto;
+
+	if (sk->state == TCP_LISTEN) {
+		dprintk("setting up TCP socket for listening\n");
+		sk->state_change = svc_tcp_state_change1;
+	} else {
+		dprintk("setting up TCP socket for reading\n");
+		sk->state_change = svc_tcp_state_change2;
+		sk->data_ready = svc_tcp_data_ready;
+
+		svsk->sk_reclen = 0;
+		svsk->sk_tcplen = 0;
+	}
+
+	return 0;
+}
+
+/*
+ * Receive the next request on any socket.
+ */
+int
+svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+	struct wait_queue	wait = { current, NULL };
+	struct svc_sock		*svsk;
+	int			len;
+
+	dprintk("svc: server %p waiting for data (to = %ld)\n",
+					rqstp, current->timeout);
+
+again:
+	/* Initialize the buffers */
+	rqstp->rq_argbuf = rqstp->rq_defbuf;
+	rqstp->rq_resbuf = rqstp->rq_defbuf;
+
+	if (signalled())
+		return -EINTR;
+
+	disable_bh(NET_BH);
+	if ((svsk = svc_sock_dequeue(serv)) != NULL) {
+		enable_bh(NET_BH);
+		rqstp->rq_sock = svsk;
+		svsk->sk_inuse++;
+	} else {
+		/* No data pending. Go to sleep */
+		rqstp->rq_sock = NULL;
+		rqstp->rq_wait = NULL;
+		svc_serv_enqueue(serv, rqstp);
+
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&rqstp->rq_wait, &wait);
+		enable_bh(NET_BH);
+		schedule();
+
+		if (!(svsk = rqstp->rq_sock)) {
+			svc_serv_dequeue(serv, rqstp);
+			if (!(svsk = rqstp->rq_sock))
+				return signalled()? -EINTR : -EAGAIN;
+		}
+	}
+
+	dprintk("svc: server %p servicing socket %p\n", rqstp, svsk);
+	len = svsk->sk_recvfrom(rqstp);
+
+	/* No data, incomplete (TCP) read, or accept() */
+	if (len == 0 || len == -EAGAIN) {
+		svc_sock_release(rqstp);
+		goto again;
+	}
+
+	rqstp->rq_secure  = ntohs(rqstp->rq_addr.sin_port) < 1024;
+	rqstp->rq_userset = 0;
+	rqstp->rq_verfed  = 0;
+
+	svc_getlong(&rqstp->rq_argbuf, rqstp->rq_xid);
+	svc_putlong(&rqstp->rq_resbuf, rqstp->rq_xid);
+
+	/* Assume that the reply consists of a single buffer. */
+	rqstp->rq_resbuf.nriov = 1;
+
+	if (serv->sv_stats)
+		serv->sv_stats->netcnt++;
+	return len;
+}
+
+/* 
+ * Drop request
+ */
+void
+svc_drop(struct svc_rqst *rqstp)
+{
+	dprintk("svc: socket %p dropped request\n", rqstp->rq_sock);
+	svc_sock_release(rqstp);
+}
+
+/*
+ * Return reply to client.
+ */
+int
+svc_send(struct svc_rqst *rqstp)
+{
+	struct svc_sock	*svsk;
+	int		len;
+
+	if ((svsk = rqstp->rq_sock) == NULL) {
+		printk(KERN_WARNING "NULL socket pointer in %s:%d\n",
+				__FILE__, __LINE__);
+		return -EFAULT;
+	}
+
+	/* release the receive skb before sending the reply */
+	svc_release_skb(rqstp);
+
+	len = svsk->sk_sendto(rqstp);
+	svc_sock_release(rqstp);
+
+	if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN)
+		return 0;
+	return len;
+}
+
+/*
+ * Initialize socket for RPC use and create svc_sock struct
+ * XXX: May want to setsockopt SO_SNDBUF and SO_RCVBUF.
+ */
+static struct svc_sock *
+svc_setup_socket(struct svc_serv *serv, struct socket *sock,
+					int *errp, int pmap_register)
+{
+	struct svc_sock	*svsk;
+	struct sock	*inet;
+
+	dprintk("svc: svc_setup_socket %p\n", sock);
+	if (!(svsk = kmalloc(sizeof(*svsk), GFP_KERNEL))) {
+		*errp = -ENOMEM;
+		return NULL;
+	}
+	memset(svsk, 0, sizeof(*svsk));
+
+#if LINUX_VERSION_CODE >= 0x020100
+	inet = sock->sk;
+#else
+	inet = (struct sock *) sock->data;
+#endif
+	inet->user_data = svsk;
+	svsk->sk_sock = sock;
+	svsk->sk_sk = inet;
+	svsk->sk_ostate = inet->state_change;
+	svsk->sk_odata = inet->data_ready;
+	svsk->sk_server = serv;
+
+	/* Initialize the socket */
+	if (sock->type == SOCK_DGRAM)
+		*errp = svc_udp_init(svsk);
+	else
+		*errp = svc_tcp_init(svsk);
+if (svsk->sk_sk == NULL)
+	printk(KERN_WARNING "svsk->sk_sk == NULL after svc_prot_init!\n");
+
+	/* Register socket with portmapper */
+	if (*errp >= 0 && pmap_register)
+		*errp = svc_register(serv, inet->protocol,
+					ntohs(inet->dummy_th.source));
+
+	if (*errp < 0) {
+		inet->user_data = NULL;
+		kfree(svsk);
+		return NULL;
+	}
+
+	svsk->sk_list = serv->sv_allsocks;
+	serv->sv_allsocks = svsk;
+
+	dprintk("svc: svc_setup_socket created %p (inet %p)\n",
+				svsk, svsk->sk_sk);
+	return svsk;
+}
+
+/*
+ * Create socket for RPC service.
+ */
+int
+svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin)
+{
+	struct svc_sock	*svsk;
+	struct socket	*sock;
+	int		error;
+	int		type;
+
+	dprintk("svc: svc_create_socket(%s, %d, %08lx:%d)\n",
+				serv->sv_program->pg_name, protocol,
+				ntohl(sin->sin_addr.s_addr),
+				ntohs(sin->sin_port));
+
+	if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP) {
+		printk(KERN_WARNING "svc: only UDP and TCP "
+				"sockets supported\n");
+		return -EINVAL;
+	}
+	type = (protocol == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+
+	if ((error = sock_create(AF_INET, type, protocol, &sock)) < 0)
+		return error;
+
+	if (sin != NULL) {
+		error = sock->ops->bind(sock, (struct sockaddr *) sin,
+						sizeof(*sin));
+		if (error < 0)
+			goto bummer;
+	}
+
+	if (protocol == IPPROTO_TCP) {
+		if ((error = sock->ops->listen(sock, 5)) < 0)
+			goto bummer;
+		sock->flags |= SO_ACCEPTCON;
+	}
+
+	if ((svsk = svc_setup_socket(serv, sock, &error, 1)) != NULL)
+		return 0;
+
+bummer:
+	dprintk("svc: svc_create_socket error = %d\n", -error);
+	sock_release(sock);
+	return error;
+}
+
+/*
+ * Remove a dead socket
+ */
+void
+svc_delete_socket(struct svc_sock *svsk)
+{
+	struct svc_sock	**rsk;
+	struct svc_serv	*serv;
+	struct sock	*sk;
+
+	dprintk("svc: svc_delete_socket(%p)\n", svsk);
+
+	serv = svsk->sk_server;
+	sk = svsk->sk_sk;
+
+	sk->state_change = svsk->sk_ostate;
+	sk->data_ready = svsk->sk_odata;
+
+	for (rsk = &serv->sv_allsocks; *rsk; rsk = &(*rsk)->sk_list) {
+		if (*rsk == svsk)
+			break;
+	}
+	if (!*rsk)
+		return;
+	*rsk = svsk->sk_list;
+
+	if (svsk->sk_qued)
+		rpc_remove_list(&serv->sv_sockets, svsk);
+	svsk->sk_dead = 1;
+
+	if (!svsk->sk_inuse) {
+		sock_release(svsk->sk_sock);
+		kfree(svsk);
+	} else {
+		printk(KERN_NOTICE "svc: server socket destroy delayed\n");
+		svsk->sk_server = NULL;
+	}
+}
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/sysctl.c linux/net/sunrpc/sysctl.c
--- v2.1.31/linux/net/sunrpc/sysctl.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/sysctl.c	Mon Mar  3 00:55:59 1997
@@ -0,0 +1,134 @@
+/*
+ * linux/net/sunrpc/sysctl.c
+ *
+ * Sysctl interface to sunrpc module. This is for debugging only now.
+ *
+ * I would prefer to register the sunrpc table below sys/net, but that's
+ * impossible at the moment.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#else
+# include <linux/mm.h>
+# define copy_from_user		memcpy_fromfs
+# define copy_to_user		memcpy_tofs
+# define access_ok		!verify_area
+#endif
+#include <linux/sunrpc/types.h>
+
+/*
+ * Declare the debug flags here
+ */
+unsigned int	rpc_debug  = 0;
+unsigned int	nfs_debug  = 0;
+unsigned int	nfsd_debug = 0;
+unsigned int	nlm_debug  = 0;
+
+#ifdef RPC_DEBUG
+
+static struct ctl_table_header *sunrpc_table_header = NULL;
+static ctl_table		sunrpc_table[];
+
+void
+rpc_register_sysctl(void)
+{
+	if (sunrpc_table_header)
+		return;
+	sunrpc_table_header = register_sysctl_table(sunrpc_table, 1);
+}
+
+void
+rpc_unregister_sysctl(void)
+{
+	if (!sunrpc_table_header)
+		return;
+	unregister_sysctl_table(sunrpc_table_header);
+}
+
+int
+proc_dodebug(ctl_table *table, int write, struct file *file,
+				void *buffer, size_t *lenp)
+{
+	char		tmpbuf[20], *p, c;
+	unsigned int	value;
+	int		left, len;
+
+	if ((file->f_pos && !write) || !*lenp) {
+		*lenp = 0;
+		return 0;
+	}
+
+	left = *lenp;
+
+	if (write) {
+		if (!access_ok(VERIFY_READ, buffer, left))
+			return -EFAULT;
+		p = (char *) buffer;
+#if LINUX_VERSION_CODE >= 0x020100
+		while (left && __get_user(c, p) >= 0 && isspace(c))
+			left--, p++;
+#else
+		while (left && (c = get_fs_byte(p)) >= 0 && isspace(c))
+			left--, p++;
+#endif
+		if (!left)
+			goto done;
+
+		if (left > sizeof(tmpbuf) - 1)
+			return -EINVAL;
+		copy_from_user(tmpbuf, p, left);
+		tmpbuf[left] = '\0';
+
+		for (p = tmpbuf, value = 0; '0' <= *p && *p <= '9'; p++, left--)
+			value = 10 * value + (*p - '0');
+		if (*p && !isspace(*p))
+			return -EINVAL;
+		while (left && isspace(*p))
+			left--, p++;
+		*(unsigned int *) table->data = value;
+	} else {
+		if (!access_ok(VERIFY_WRITE, buffer, left))
+			return -EFAULT;
+		len = sprintf(tmpbuf, "%d", *(unsigned int *) table->data);
+		if (len > left)
+			len = left;
+		copy_to_user(buffer, tmpbuf, len);
+		if ((left -= len) > 0) {
+			put_user('\n', (char *)buffer + len);
+			left--;
+		}
+	}
+
+done:
+	*lenp -= left;
+	file->f_pos += *lenp;
+	return 0;
+}
+
+#define DIRENTRY(nam1, nam2, child)	\
+	{CTL_##nam1, #nam2, NULL, 0, 0555, child }
+#define DBGENTRY(nam1, nam2)	\
+	{CTL_##nam1##DEBUG, #nam2 "_debug", &nam2##_debug, sizeof(int),\
+	 0644, NULL, &proc_dodebug}
+
+static ctl_table		debug_table[] = {
+	DBGENTRY(RPC,  rpc),
+	DBGENTRY(NFS,  nfs),
+	DBGENTRY(NFSD, nfsd),
+	DBGENTRY(NLM,  nlm),
+	{0}
+};
+
+static ctl_table		sunrpc_table[] = {
+	DIRENTRY(SUNRPC, sunrpc, debug_table),
+	{0}
+};
+
+#endif
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/xdr.c linux/net/sunrpc/xdr.c
--- v2.1.31/linux/net/sunrpc/xdr.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/xdr.c	Thu Dec 19 09:04:19 1996
@@ -0,0 +1,118 @@
+/*
+ * linux/net/sunrpc/xdr.c
+ *
+ * Generic XDR support.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/msg_prot.h>
+
+u32	rpc_success, rpc_prog_unavail, rpc_prog_mismatch, rpc_proc_unavail,
+	rpc_garbage_args, rpc_system_err;
+u32	rpc_auth_ok, rpc_autherr_badcred, rpc_autherr_rejectedcred,
+	rpc_autherr_badverf, rpc_autherr_rejectedverf, rpc_autherr_tooweak;
+u32	xdr_zero, xdr_one, xdr_two;
+
+void
+xdr_init(void)
+{
+	static int	inited = 0;
+
+	if (inited)
+		return;
+
+	xdr_zero = htonl(0);
+	xdr_one = htonl(1);
+	xdr_two = htonl(2);
+
+	rpc_success = htonl(RPC_SUCCESS);
+	rpc_prog_unavail = htonl(RPC_PROG_UNAVAIL);
+	rpc_prog_mismatch = htonl(RPC_PROG_MISMATCH);
+	rpc_proc_unavail = htonl(RPC_PROC_UNAVAIL);
+	rpc_garbage_args = htonl(RPC_GARBAGE_ARGS);
+	rpc_system_err = htonl(RPC_SYSTEM_ERR);
+
+	rpc_auth_ok = htonl(RPC_AUTH_OK);
+	rpc_autherr_badcred = htonl(RPC_AUTH_BADCRED);
+	rpc_autherr_rejectedcred = htonl(RPC_AUTH_REJECTEDCRED);
+	rpc_autherr_badverf = htonl(RPC_AUTH_BADVERF);
+	rpc_autherr_rejectedverf = htonl(RPC_AUTH_REJECTEDVERF);
+	rpc_autherr_tooweak = htonl(RPC_AUTH_TOOWEAK);
+
+	inited = 1;
+}
+
+/*
+ * XDR functions for basic NFS types
+ */
+u32 *
+xdr_encode_netobj(u32 *p, const struct xdr_netobj *obj)
+{
+	unsigned int	quadlen = XDR_QUADLEN(obj->len);
+
+	*p++ = htonl(obj->len);
+	p[quadlen-1] = 0;	/* zero trailing bytes */
+	memcpy(p, obj->data, obj->len);
+	return p + XDR_QUADLEN(obj->len);
+}
+
+u32 *
+xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len)
+{
+	if (ntohl(*p++) != len)
+		return NULL;
+	memcpy(obj, p, len);
+	return p + XDR_QUADLEN(len);
+}
+
+u32 *
+xdr_decode_netobj(u32 *p, struct xdr_netobj *obj)
+{
+	unsigned int	len;
+
+	if ((len = ntohl(*p++)) > XDR_MAX_NETOBJ)
+		return NULL;
+	obj->len  = len;
+	obj->data = (u8 *) p;
+	return p + XDR_QUADLEN(len);
+}
+
+u32 *
+xdr_encode_string(u32 *p, const char *string)
+{
+	int len = strlen(string);
+	int quadlen = XDR_QUADLEN(len);
+
+	p[quadlen] = 0;
+	*p++ = htonl(len);
+	memcpy(p, string, len);
+	return p + quadlen;
+}
+
+u32 *
+xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen)
+{
+	unsigned int	len;
+	char		*string;
+
+	if ((len = ntohl(*p++)) > maxlen)
+		return NULL;
+	if (lenp)
+		*lenp = len;
+	if ((len % 4) != 0) {
+		string = (char *) p;
+	} else {
+		string = (char *) (p - 1);
+		memmove(string, p, len);
+	}
+	string[len] = '\0';
+	*sp = string;
+	return p + XDR_QUADLEN(len);
+}
+
diff -u --recursive --new-file v2.1.31/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.1.31/linux/net/sunrpc/xprt.c	Wed Dec 31 16:00:00 1969
+++ linux/net/sunrpc/xprt.c	Thu Mar 20 07:52:08 1997
@@ -0,0 +1,1367 @@
+/*
+ *  linux/net/sunrpc/xprt.c
+ *
+ *  This is a generic RPC call interface supporting congestion avoidance,
+ *  and asynchronous calls.
+ *
+ *  The interface works like this:
+ *
+ *  -	When a process places a call, it allocates a request slot if
+ *	one is available. Otherwise, it sleeps on the backlog queue
+ *	(xprt_reserve).
+ *  -	Next, the caller puts together the RPC message, stuffs it into
+ *	the request struct, and calls xprt_call().
+ *  -	xprt_call transmits the message and installs the caller on the
+ *	socket's wait list. At the same time, it installs a timer that
+ *	is run after the packet's timeout has expired.
+ *  -	When a packet arrives, the data_ready handler walks the list of
+ *	pending requests for that socket. If a matching XID is found, the
+ *	caller is woken up, and the timer removed.
+ *  -	When no reply arrives within the timeout interval, the timer is
+ *	fired by the kernel and runs xprt_timer(). It either adjusts the
+ *	timeout values (minor timeout) or wakes up the caller with a status
+ *	of -ETIMEDOUT.
+ *  -	When the caller receives a notification from RPC that a reply arrived,
+ *	it should release the RPC slot, and process the reply.
+ *	If the call timed out, it may choose to retry the operation by
+ *	adjusting the initial timeout value, and simply calling rpc_call
+ *	again.
+ *
+ *  Support for async RPC is done through a set of RPC-specific scheduling
+ *  primitives that `transparently' work for processes as well as async
+ *  tasks that rely on callbacks.
+ *
+ *  Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/udp.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <net/sock.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#endif
+
+#define SOCK_HAS_USER_DATA
+
+/*
+ * Local variables
+ */
+#ifndef SOCK_HAS_USER_DATA
+static struct rpc_xprt *	sock_list = NULL;
+#endif
+
+#ifdef RPC_DEBUG
+# undef  RPC_DEBUG_DATA
+# define RPCDBG_FACILITY	RPCDBG_XPRT
+#endif
+
+#ifndef MAX
+# define MAX(a, b)	((a) > (b)? (a) : (b))
+# define MIN(a, b)	((a) < (b)? (a) : (b))
+#endif
+
+/*
+ * Local functions
+ */
+static void	xprt_request_init(struct rpc_task *, struct rpc_xprt *);
+static void	xprt_transmit_status(struct rpc_task *task);
+static void	xprt_receive_status(struct rpc_task *task);
+static void	xprt_reserve_status(struct rpc_task *task);
+static void	xprt_reconn_timeout(struct rpc_task *task);
+static void	xprt_reconn_status(struct rpc_task *task);
+static struct socket *xprt_create_socket(int, struct sockaddr_in *,
+					struct rpc_timeout *);
+
+#ifdef RPC_DEBUG_DATA
+/*
+ * Print the buffer contents (first 128 bytes only--just enough for
+ * diropres return).
+ */
+static void
+xprt_pktdump(char *msg, u32 *packet, unsigned int count)
+{
+	u8	*buf = (u8 *) packet;
+	int	j;
+
+	dprintk("RPC:      %s\n", msg);
+	for (j = 0; j < count && j < 128; j += 4) {
+		if (!(j & 31)) {
+			if (j)
+				dprintk("\n");
+			dprintk("0x%04x ", j);
+		}
+		dprintk("%02x%02x%02x%02x ",
+			buf[j], buf[j+1], buf[j+2], buf[j+3]);
+	}
+	dprintk("\n");
+}
+#else
+static inline void
+xprt_pktdump(char *msg, u32 *packet, unsigned int count)
+{
+	/* NOP */
+}
+#endif
+
+/*
+ * Look up RPC transport given an INET socket
+ */
+static inline struct rpc_xprt *
+xprt_from_sock(struct sock *sk)
+{
+#ifndef SOCK_HAS_USER_DATA
+	struct rpc_xprt		*xprt;
+
+	for (xprt = sock_list; xprt && sk != xprt->inet; xprt = xprt->link)
+		;
+	return xprt;
+#else
+	return (struct rpc_xprt *) sk->user_data;
+#endif
+}
+
+/*
+ * Write data to socket.
+ */
+static inline int
+xprt_sendmsg(struct rpc_xprt *xprt)
+{
+	struct socket	*sock = xprt->sock;
+	struct msghdr	msg;
+	unsigned long	oldfs;
+	int		result;
+
+	xprt_pktdump("packet data:",
+				xprt->snd_buf.io_vec->iov_base,
+				xprt->snd_buf.io_vec->iov_len);
+
+#if LINUX_VERSION_CODE >= 0x020100
+	msg.msg_flags   = MSG_DONTWAIT;
+	msg.msg_iov	= xprt->snd_buf.io_vec;
+	msg.msg_iovlen	= xprt->snd_buf.io_nr;
+	msg.msg_name	= (struct sockaddr *) &xprt->addr;
+	msg.msg_namelen = sizeof(xprt->addr);
+	msg.msg_control = NULL;
+
+	oldfs = get_fs(); set_fs(get_ds());
+	result = sock_sendmsg(sock, &msg, xprt->snd_buf.io_len);
+	set_fs(oldfs);
+#else
+	msg.msg_flags   = 0;
+	msg.msg_iov	= xprt->snd_buf.io_vec;
+	msg.msg_iovlen	= xprt->snd_buf.io_nr;
+	msg.msg_name	= (struct sockaddr *) &xprt->addr;
+	msg.msg_namelen = sizeof(xprt->addr);
+	msg.msg_control = NULL;
+
+	oldfs = get_fs(); set_fs(get_ds());
+	result = sock->ops->sendmsg(sock, &msg, xprt->snd_buf.io_len, 1, 0);
+	set_fs(oldfs);
+#endif
+
+	dprintk("RPC:      xprt_sendmsg(%d) = %d\n",
+				xprt->snd_buf.io_len, result);
+
+	if (result >= 0) {
+		xprt->snd_buf.io_len -= result;
+		return result;
+	}
+
+	switch (result) {
+	case -ECONNREFUSED:
+		/* When the server has died, an ICMP port unreachable message
+		 * prompts ECONNREFUSED.
+		 */
+		break;
+	case -ENOTCONN: case -EPIPE:
+		/* connection broken */
+		break;
+	default:
+		printk(KERN_NOTICE "RPC: sendmsg returned error %d\n", -result);
+		result = 0;
+	}
+	return result;
+}
+
+/*
+ * Read data from socket
+ */
+static inline int
+xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len)
+{
+	struct socket	*sock = xprt->sock;
+	struct sockaddr_in sin;
+	struct msghdr	msg;
+	unsigned long	oldfs;
+	int		result;
+
+#if LINUX_VERSION_CODE >= 0x020100
+	msg.msg_flags   = MSG_DONTWAIT;
+	msg.msg_iov	= iov;
+	msg.msg_iovlen	= nr;
+	msg.msg_name	= &sin;
+	msg.msg_namelen = sizeof(sin);
+	msg.msg_control = NULL;
+
+	oldfs = get_fs(); set_fs(get_ds());
+	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
+	set_fs(oldfs);
+#else
+	int		alen = sizeof(sin);
+	msg.msg_flags   = 0;
+	msg.msg_iov	= iov;
+	msg.msg_iovlen	= nr;
+	msg.msg_name	= &sin;
+	msg.msg_namelen = sizeof(sin);
+	msg.msg_control = NULL;
+
+	oldfs = get_fs(); set_fs(get_ds());
+	result = sock->ops->recvmsg(sock, &msg, len, 1, 0, &alen);
+	set_fs(oldfs);
+#endif
+
+	if (!result && len)
+		result = -EAGAIN;
+
+	dprintk("RPC:      xprt_recvmsg(iov %p, len %d) = %d\n",
+						iov, len, result);
+	return result;
+}
+
+
+/*
+ * Adjust RPC congestion window
+ * We use a time-smoothed congestion estimator to avoid heavy oscillation.
+ */
+static void
+xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
+{
+	unsigned long	cwnd = xprt->cwnd;
+
+	if (xprt->nocong)
+		return;
+	if (result >= 0) {
+		if (xprt->cong < cwnd || jiffies < xprt->congtime)
+			return;
+		/* The (cwnd >> 1) term makes sure
+		 * the result gets rounded properly. */
+		cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd;
+		if (cwnd > RPC_MAXCWND)
+			cwnd = RPC_MAXCWND;
+		else
+			pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd);
+		xprt->congtime = jiffies + ((cwnd * HZ) << 2) / RPC_CWNDSCALE;
+		dprintk("RPC:      cong %08lx, cwnd was %08lx, now %08lx, "
+			"time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
+			(xprt->congtime-jiffies)*1000/HZ);
+	} else if (result == -ETIMEDOUT) {
+		if ((cwnd >>= 1) < RPC_CWNDSCALE)
+			cwnd = RPC_CWNDSCALE;
+		xprt->congtime = jiffies + ((cwnd * HZ) << 3) / RPC_CWNDSCALE;
+		dprintk("RPC:      cong %08lx, cwnd was %08lx, now %08lx, "
+			"time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
+			(xprt->congtime-jiffies)*1000/HZ);
+		pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd);
+	}
+
+	xprt->cwnd = cwnd;
+}
+
+/*
+ * Adjust timeout values etc for next retransmit
+ */
+int
+xprt_adjust_timeout(struct rpc_timeout *to)
+{
+	if (to->to_exponential)
+		to->to_current <<= 1;
+	else
+		to->to_current += to->to_increment;
+	if (to->to_maxval && to->to_current >= to->to_maxval) {
+		to->to_current = to->to_maxval;
+		to->to_retries = 0;
+	}
+	if (!to->to_current) {
+		printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n");
+		to->to_current = 5 * HZ;
+	}
+	pprintk("RPC: %lu %s\n", jiffies,
+			to->to_retries? "retrans" : "timeout");
+	return (to->to_retries)--;
+}
+
+/*
+ * Close down a transport socket
+ */
+static void
+xprt_close(struct rpc_xprt *xprt)
+{
+	struct sock	*sk = xprt->inet;
+
+#ifdef SOCK_HAS_USER_DATA
+	sk->user_data    = NULL;
+#endif
+	sk->data_ready   = xprt->old_data_ready;
+	sk->state_change = xprt->old_state_change;
+	sk->write_space  = xprt->old_write_space;
+
+	if (xprt->file)
+		close_fp(xprt->file);
+	else
+		sock_release(xprt->sock);
+}
+
+/*
+ * Mark a transport as disconnected
+ */
+static void
+xprt_disconnect(struct rpc_xprt *xprt)
+{
+	dprintk("RPC:      disconnected transport %p\n", xprt);
+	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+	rpc_wake_up_status(&xprt->sending, -ENOTCONN);
+	xprt->connected = 0;
+}
+
+/*
+ * Reconnect a broken TCP connection.
+ */
+void
+xprt_reconnect(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_xprt;
+	struct socket	*sock;
+	struct sock	*inet;
+	int		status;
+
+	dprintk("RPC: %4d xprt_reconnect %p connected %d\n",
+				task->tk_pid, xprt, xprt->connected);
+	task->tk_status = 0;
+
+	if (xprt->connecting) {
+		task->tk_timeout = xprt->timeout.to_maxval;
+		rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
+		return;
+	}
+	xprt->connecting = 1;
+
+	/* Create an unconnected socket */
+	if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout)))
+		goto defer;
+
+#if LINUX_VERSION_CODE >= 0x020100
+	inet = sock->sk;
+#else
+	inet = (struct sock *) sock->data;
+#endif
+	inet->data_ready   = xprt->inet->data_ready;
+	inet->state_change = xprt->inet->state_change;
+	inet->write_space  = xprt->inet->write_space;
+#ifdef SOCK_HAS_USER_DATA
+	inet->user_data    = xprt;
+#endif
+
+	dprintk("RPC: %4d closing old socket\n", task->tk_pid);
+	xprt_disconnect(xprt);
+	xprt_close(xprt);
+
+	/* Reset to new socket and default congestion */
+	xprt->sock = sock;
+	xprt->inet = inet;
+	xprt->cwnd = RPC_INITCWND;
+
+	/* Now connect it asynchronously. */
+	dprintk("RPC: %4d connecting new socket\n", task->tk_pid);
+	status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
+				sizeof(xprt->addr), O_NONBLOCK);
+	if (status < 0) {
+		if (status != -EINPROGRESS && status != -EALREADY) {
+			printk("RPC: TCP connect error %d!\n", -status);
+			goto defer;
+		}
+
+		dprintk("RPC: %4d connect status %d connected %d\n",
+				task->tk_pid, status, xprt->connected);
+		task->tk_timeout = 60 * HZ;
+
+		disable_bh(NET_BH);
+		if (!xprt->connected) {
+			rpc_sleep_on(&xprt->reconn, task,
+				xprt_reconn_status, xprt_reconn_timeout);
+			enable_bh(NET_BH);
+			return;
+		}
+		enable_bh(NET_BH);
+	}
+
+	xprt->connecting = 0;
+	rpc_wake_up(&xprt->reconn);
+	return;
+
+defer:
+	task->tk_timeout = 30 * HZ;
+	rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
+	xprt->connecting = 0;
+}
+
+/*
+ * Reconnect status
+ */
+static void
+xprt_reconn_status(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_xprt;
+
+	dprintk("RPC: %4d xprt_reconn_status %d\n",
+				task->tk_pid, task->tk_status);
+	if (!xprt->connected && task->tk_status != -ETIMEDOUT) {
+		task->tk_timeout = 30 * HZ;
+		rpc_sleep_on(&xprt->reconn, task, NULL, xprt_reconn_timeout);
+	}
+}
+
+/*
+ * Reconnect timeout. We just mark the transport as not being in the
+ * process of reconnecting, and leave the rest to the upper layers.
+ */
+static void
+xprt_reconn_timeout(struct rpc_task *task)
+{
+	dprintk("RPC: %4d xprt_reconn_timeout %d\n",
+				task->tk_pid, task->tk_status);
+	task->tk_status = -ENOTCONN;
+	task->tk_xprt->connecting = 0;
+	task->tk_timeout = 0;
+	rpc_wake_up_task(task);
+}
+
+/*
+ * Look up the RPC request corresponding to a reply.
+ */
+static inline struct rpc_rqst *
+xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid)
+{
+	struct rpc_task	*head, *task;
+	struct rpc_rqst	*req;
+	int		safe = 0;
+
+	if ((head = xprt->pending.task) != NULL) {
+		task = head;
+		do {
+			if ((req = task->tk_rqstp) && req->rq_xid == xid)
+				return req;
+			task = task->tk_next;
+			if (++safe > 100) {
+				printk("xprt_lookup_rqst: loop in Q!\n");
+				return NULL;
+			}
+		} while (task != head);
+	}
+	dprintk("RPC:      unknown XID %08x in reply.\n", xid);
+	return NULL;
+}
+
+/*
+ * Complete reply received.
+ * The TCP code relies on us to remove the request from xprt->pending.
+ */
+static inline void
+xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
+{
+	struct rpc_task	*task = req->rq_task;
+
+	req->rq_rlen   = copied;
+	req->rq_gotit  = 1;
+
+	/* Adjust congestion window */
+	xprt_adjust_cwnd(xprt, copied);
+
+#ifdef RPC_PROFILE
+	/* Profile only reads for now */
+	if (copied > 1024) {
+		static unsigned long	nextstat = 0;
+		static unsigned long	pkt_rtt = 0, pkt_len = 0, pkt_cnt = 0;
+
+		pkt_cnt++;
+		pkt_len += req->rq_slen + copied;
+		pkt_rtt += jiffies - req->rq_xtime;
+		if (nextstat < jiffies) {
+			printk("RPC: %lu %ld cwnd\n", jiffies, xprt->cwnd);
+			printk("RPC: %ld %ld %ld %ld stat\n",
+					jiffies, pkt_cnt, pkt_len, pkt_rtt);
+			pkt_rtt = pkt_len = pkt_cnt = 0;
+			nextstat = jiffies + 5 * HZ;
+		}
+	}
+#endif
+
+	/* ... and wake up the process. */
+	dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied);
+	task->tk_status = copied;
+
+	rpc_wake_up_task(task);
+	return;
+}
+
+/*
+ * Input handler for RPC replies. Called from a bottom half and hence
+ * atomic.
+ */
+static inline void
+udp_data_ready(struct sock *sk, int len)
+{
+	struct rpc_task	*task;
+	struct rpc_xprt	*xprt;
+	struct rpc_rqst *rovr;
+	struct sk_buff	*skb;
+	struct iovec	iov[MAX_IOVEC];
+	unsigned long	oldfs;
+	int		err, repsize, copied;
+
+	dprintk("RPC:      udp_data_ready...\n");
+	if (!(xprt = xprt_from_sock(sk)))
+		return;
+	dprintk("RPC:      udp_data_ready client %p\n", xprt);
+
+	if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL)
+		return;
+	repsize = skb->len - 8;	/* don't account for UDP header */
+
+	if (repsize < 4) {
+		printk("RPC: impossible RPC reply size %d!\n", repsize);
+		goto dropit;
+	}
+
+	/* Look up the request corresponding to the given XID */
+	if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8))))
+		goto dropit;
+	task = rovr->rq_task;
+
+	dprintk("RPC: %4d received reply\n", task->tk_pid);
+	xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize);
+
+	if ((copied = rovr->rq_rlen) > repsize)
+		copied = repsize;
+
+	/* Okay, we have it. Copy datagram... */
+	memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0]));
+	oldfs = get_fs(); set_fs(get_ds());
+	skb_copy_datagram_iovec(skb, 8, iov, copied);
+	set_fs(oldfs);
+
+	xprt_complete_rqst(xprt, rovr, copied);
+
+dropit:
+	skb_free_datagram(sk, skb);
+	return;
+}
+
+/*
+ * TCP record receive routine
+ * This is not the most efficient code since we call recvfrom twice--
+ * first receiving the record marker and XID, then the data.
+ * 
+ * The optimal solution would be a RPC support in the TCP layer, which
+ * would gather all data up to the next record marker and then pass us
+ * the list of all TCP segments ready to be copied.
+ */
+static inline int
+tcp_input_record(struct rpc_xprt *xprt)
+{
+	struct rpc_rqst	*req;
+	struct iovec	*iov;
+	struct iovec	riov;
+	u32		offset;
+	int		result, maxcpy, reclen, avail, want;
+
+	dprintk("RPC:      tcp_input_record\n");
+	offset = xprt->tcp_offset;
+	result = -EAGAIN;
+	if (offset < 4 || (!xprt->tcp_more && offset < 8)) {
+		want = (xprt->tcp_more? 4 : 8) - offset;
+		dprintk("RPC:      reading header (%d bytes)\n", want);
+		riov.iov_base = xprt->tcp_recm.data + offset;
+		riov.iov_len  = want;
+		result = xprt_recvmsg(xprt, &riov, 1, want);
+		if (result < 0)
+			goto done;
+		offset += result;
+		if (result < want) {
+			result = -EAGAIN;
+			goto done;
+		}
+
+		/* Get the record length and mask out the more_fragments bit */
+		reclen = ntohl(xprt->tcp_reclen);
+		dprintk("RPC:      reclen %08x\n", reclen);
+		xprt->tcp_more = (reclen & 0x80000000)? 0 : 1;
+		if (!(reclen &= 0x7fffffff)) {
+			printk(KERN_NOTICE "RPC:      empty TCP record.\n");
+			return -ENOTCONN;	/* break connection */
+		}
+		xprt->tcp_total += reclen;
+		xprt->tcp_reclen = reclen;
+
+		dprintk("RPC:      got xid %08x reclen %d morefrags %d\n",
+			xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more);
+		if (!xprt->tcp_copied
+		 && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) {
+			iov = xprt->tcp_iovec;
+			memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
+#if 0
+*(u32 *)iov->iov_base = req->rq_xid;
+#endif
+			iov->iov_base += 4;
+			iov->iov_len  -= 4;
+			xprt->tcp_copied = 4;
+			xprt->tcp_rqstp  = req;
+		}
+	} else {
+		reclen = xprt->tcp_reclen;
+	}
+
+	avail = reclen - (offset - 4);
+	if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid
+	 && req->rq_task->tk_rpcwait == &xprt->pending) {
+		want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+
+		dprintk("RPC: %4d TCP receiving %d bytes\n",
+					req->rq_task->tk_pid, want);
+		result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want);
+		if (result < 0)
+			goto done;
+		xprt->tcp_copied += result;
+		offset += result;
+		avail  -= result;
+		if (result < want) {
+			result = -EAGAIN;
+			goto done;
+		}
+
+		maxcpy = MIN(req->rq_rlen, xprt->tcp_total);
+		if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) {
+			dprintk("RPC: %4d received reply complete\n",
+					req->rq_task->tk_pid);
+			xprt_complete_rqst(xprt, req, xprt->tcp_total);
+			xprt->tcp_copied = 0;
+			xprt->tcp_rqstp  = NULL;
+		}
+		/* Request must be re-encoded before retransmit */
+		req->rq_damaged = 1;
+	}
+
+	/* Skip over any trailing bytes on short reads */
+	while (avail) {
+		static u8	dummy[64];
+
+		want = MIN(avail, sizeof(dummy));
+		riov.iov_base = dummy;
+		riov.iov_len  = want;
+		dprintk("RPC:      TCP skipping %d bytes\n", want);
+		result = xprt_recvmsg(xprt, &riov, 1, want);
+		if (result < 0)
+			goto done;
+		offset += result;
+		avail  -= result;
+		if (result < want) {
+			result = -EAGAIN;
+			goto done;
+		}
+	}
+	if (!xprt->tcp_more)
+		xprt->tcp_total = 0;
+	offset = 0;
+
+done:
+	dprintk("RPC:      tcp_input_record done (off %d total %d copied %d)\n",
+			offset, xprt->tcp_total, xprt->tcp_copied);
+	xprt->tcp_offset = offset;
+	return result;
+}
+
+/*
+ * data_ready callback for TCP.
+ */
+static void
+tcp_data_ready(struct sock *sk, int len)
+{
+	struct rpc_xprt	*xprt;
+	int		result, safe_retry = 0;
+
+	dprintk("RPC:      tcp_data_ready...\n");
+	if (!(xprt = xprt_from_sock(sk)))
+		return;
+	dprintk("RPC:      tcp_data_ready client %p\n", xprt);
+	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
+				sk->state, xprt->connected,
+				sk->dead, sk->zapped);
+
+	do {
+		if (safe_retry++ > 20)
+			return;
+		result = tcp_input_record(xprt);
+	} while (result >= 0);
+
+	switch (result) {
+	case -EAGAIN:
+		return;
+	case -ENOTCONN:
+	case -EPIPE:
+		xprt_disconnect(xprt);
+		return;
+	default:
+		printk("RPC: unexpected error %d from tcp_input_record\n",
+			result);
+	}
+}
+
+static void
+tcp_state_change(struct sock *sk)
+{
+	struct rpc_xprt	*xprt;
+
+	if (!(xprt = xprt_from_sock(sk)))
+		return;
+	dprintk("RPC:      tcp_state_change client %p...\n", xprt);
+	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
+				sk->state, xprt->connected,
+				sk->dead, sk->zapped);
+
+	if (sk->state == TCP_ESTABLISHED && !xprt->connected) {
+		xprt->connected = 1;
+		xprt->connecting = 0;
+		rpc_wake_up(&xprt->reconn);
+	} else if (sk->zapped) {
+		rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+		rpc_wake_up_status(&xprt->sending, -ENOTCONN);
+		rpc_wake_up_status(&xprt->reconn,  -ENOTCONN);
+	}
+}
+
+static void
+tcp_write_space(struct sock *sk)
+{
+	struct rpc_xprt	*xprt;
+
+	if (!(xprt = xprt_from_sock(sk)))
+		return;
+	xprt->write_space = 1;
+	if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
+		rpc_wake_up_task(xprt->snd_task);
+}
+
+/*
+ * RPC receive timeout handler.
+ */
+static void
+xprt_timer(struct rpc_task *task)
+{
+	if (task->tk_rqstp)
+		xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
+
+	dprintk("RPC: %4d xprt_timer (%s request)\n", task->tk_pid,
+			task->tk_rqstp? "pending" : "backlogged");
+
+	task->tk_status  = -ETIMEDOUT;
+	task->tk_timeout = 0;
+	rpc_wake_up_task(task);
+}
+
+/*
+ * (Partly) transmit the RPC packet
+ * Note that task->tk_status is either 0 or negative on return.
+ * Only when the reply is received will the status be set to a
+ * positive value.
+ */
+static inline int
+xprt_transmit_some(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+	struct rpc_rqst	*req = task->tk_rqstp;
+	int		result;
+
+	task->tk_status = 0;
+	if ((result = xprt_sendmsg(xprt)) >= 0) {
+		if (!xprt->snd_buf.io_len || !xprt->stream) {
+			rpc_wake_up_next(&xprt->sending);
+			return req->rq_slen;
+		}
+		result = -EAGAIN;
+	} else if (xprt->stream) {
+		if (result == -ENOTCONN || result == -EPIPE) {
+			xprt_disconnect(xprt);
+			result = -ENOTCONN;
+		}
+	}
+	return task->tk_status = result;
+}
+
+/*
+ * Place the actual RPC call.
+ * We have to copy the iovec because sendmsg fiddles with its contents.
+ */
+void
+xprt_transmit(struct rpc_task *task)
+{
+	struct rpc_timeout *timeo;
+	struct rpc_rqst	*req = task->tk_rqstp;
+	struct rpc_xprt	*xprt = req->rq_xprt;
+
+	dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid, 
+				*(u32 *)(req->rq_svec[0].iov_base));
+
+	if (xprt->shutdown) {
+		task->tk_status = -EIO;
+		return;
+	}
+
+	/* If we're not already in the process of transmitting our call,
+	 * set up everything as needed. */
+	if (xprt->snd_task != task) {
+		/* Write the record marker */
+		if (xprt->stream) {
+			u32	marker;
+
+			if (!xprt->connected) {
+				task->tk_status = -ENOTCONN;
+				return;
+			}
+			marker = htonl(0x80000000|(req->rq_slen-4));
+			*((u32 *) req->rq_svec[0].iov_base) = marker;
+		}
+
+		/* Reset timeout parameters */
+		timeo = &req->rq_timeout;
+		if (timeo->to_retries < 0) {
+			dprintk("RPC: %4d xprt_transmit reset timeo\n",
+						task->tk_pid);
+			timeo->to_retries = xprt->timeout.to_retries;
+			timeo->to_current = timeo->to_initval;
+		}
+
+#ifdef RPC_PROFILE
+		req->rq_xtime = jiffies;
+#endif
+		req->rq_gotit = 0;
+
+		if (xprt->snd_task) {
+			dprintk("RPC: %4d TCP write queue full (task %d)\n",
+					task->tk_pid, xprt->snd_task->tk_pid);
+			rpc_sleep_on(&xprt->sending, task,
+					xprt_transmit_status, NULL);
+			return;
+		}
+		xprt->snd_buf  = req->rq_snd_buf;
+		xprt->snd_task = task;
+	}
+
+	/* For fast networks/servers we have to put the request on
+	 * the pending list now:
+	 */
+	disable_bh(NET_BH);
+	rpc_add_wait_queue(&xprt->pending, task);
+	task->tk_callback = NULL;
+	enable_bh(NET_BH);
+
+	/* Continue transmitting the packet/record. We must be careful
+	 * to cope with writespace callbacks arriving _after_ we have
+	 * called xprt_sendmsg().
+	 */
+	while (1) {
+		xprt->write_space = 0;
+		if (xprt_transmit_some(xprt, task) != -EAGAIN) {
+			dprintk("RPC: %4d xmit complete\n", task->tk_pid);
+			xprt->snd_task = NULL;
+			return;
+		}
+
+		dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
+				task->tk_pid, xprt->snd_buf.io_len,
+				req->rq_slen);
+		task->tk_status = 0;
+		disable_bh(NET_BH);
+		if (!xprt->write_space) {
+			/* Remove from pending */
+			rpc_remove_wait_queue(task);
+			rpc_sleep_on(&xprt->sending, task,
+					xprt_transmit_status, NULL);
+			enable_bh(NET_BH);
+			return;
+		}
+		enable_bh(NET_BH);
+	}
+}
+
+/*
+ * This callback is invoked when the sending task is forced to sleep
+ * because the TCP write buffers are full
+ */
+static void
+xprt_transmit_status(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_client->cl_xprt;
+
+	dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status);
+	if (xprt->snd_task == task) {
+		if (task->tk_status < 0)
+			xprt->snd_task = NULL;
+		xprt_disconnect(xprt);
+	}
+}
+
+/*
+ * Wait for the reply to our call.
+ * When the callback is invoked, the congestion window should have
+ * been updated already.
+ */
+void
+xprt_receive(struct rpc_task *task)
+{
+	struct rpc_rqst	*req = task->tk_rqstp;
+	struct rpc_xprt	*xprt = req->rq_xprt;
+
+	dprintk("RPC: %4d xprt_receive\n", task->tk_pid);
+	if (xprt->connected == 0) {
+		task->tk_status = -ENOTCONN;
+		return;
+	}
+
+	/*
+	 * Wait until rq_gotit goes non-null, or timeout elapsed.
+	 */
+	task->tk_timeout = req->rq_timeout.to_current;
+
+	disable_bh(NET_BH);
+	if (!req->rq_gotit) {
+		rpc_sleep_on(&xprt->pending, task,
+				xprt_receive_status, xprt_timer);
+	}
+	enable_bh(NET_BH);
+
+	dprintk("RPC: %4d xprt_receive returns %d\n",
+				task->tk_pid, task->tk_status);
+}
+
+static void
+xprt_receive_status(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_xprt;
+
+	if (xprt->stream && xprt->tcp_rqstp == task->tk_rqstp)
+		xprt->tcp_rqstp = NULL;
+}
+
+/*
+ * Reserve an RPC call slot.
+ */
+int
+xprt_reserve(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_xprt;
+
+	/* We already have an initialized request. */
+	if (task->tk_rqstp)
+		return 0;
+
+	dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n",
+				task->tk_pid, xprt->cong, xprt->cwnd);
+	if ((!RPCXPRT_CONGESTED(xprt) && xprt->free)) {
+		xprt_reserve_status(task);
+		task->tk_timeout = 0;
+	} else if (!task->tk_timeout) {
+		task->tk_status = -ENOBUFS;
+	} else {
+		dprintk("RPC:      xprt_reserve waiting on backlog\n");
+		rpc_sleep_on(&xprt->backlog, task, xprt_reserve_status, NULL);
+	}
+	dprintk("RPC: %4d xprt_reserve returns %d\n",
+				task->tk_pid, task->tk_status);
+	return task->tk_status;
+}
+
+/*
+ * Reservation callback
+ */
+static void
+xprt_reserve_status(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_xprt;
+	struct rpc_rqst	*req;
+
+	if (xprt->shutdown) {
+		task->tk_status = -EIO;
+	} else if (task->tk_status < 0) {
+		/* NOP */
+	} else if (task->tk_rqstp) {
+		/* We've already been given a request slot: NOP */
+	} else if (!RPCXPRT_CONGESTED(xprt)) {
+		/* OK: There's room for us. Grab a free slot and bump
+		 * congestion value */
+		if (!(req = xprt->free)) {
+			/* printk("RPC: inconsistent free list!\n"); */
+			rpc_debug = ~0;
+			dprintk("RPC: %4d inconsistent free list "
+					"(cong %ld cwnd %ld)\n",
+					task->tk_pid, xprt->cong, xprt->cwnd);
+			goto bummer;
+		}
+		if (req->rq_xid) {
+			printk("RPC: used rqst slot %p on free list!\n", req);
+			goto bummer;
+		}
+		xprt->free     = req->rq_next;
+		xprt->cong    += RPC_CWNDSCALE;
+		task->tk_rqstp = req;
+		req->rq_next   = NULL;
+		xprt_request_init(task, xprt);
+	} else {
+		task->tk_status = -EAGAIN;
+	}
+
+	if (xprt->free && !RPCXPRT_CONGESTED(xprt))
+		rpc_wake_up_next(&xprt->backlog);
+
+	return;
+
+bummer:
+	task->tk_status = -EIO;
+	xprt->free = NULL;
+	return;
+}
+
+/*
+ * Initialize RPC request
+ */
+static void
+xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
+{
+	struct rpc_rqst	*req = task->tk_rqstp;
+	static u32	xid = 0;
+
+	if (!xid)
+		xid = jiffies;
+
+	dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid);
+	task->tk_status = 0;
+	req->rq_gotit	= 0;
+	req->rq_timeout = xprt->timeout;
+	req->rq_task	= task;
+	req->rq_xprt    = xprt;
+	req->rq_xid     = xid++;
+}
+
+/*
+ * Release an RPC call slot
+ */
+void
+xprt_release(struct rpc_task *task)
+{
+	struct rpc_xprt	*xprt = task->tk_xprt;
+	struct rpc_rqst	*req;
+
+	if (!(req = task->tk_rqstp))
+		return;
+	memset(req, 0, sizeof(*req));	/* mark unused */
+
+	dprintk("RPC: %4d release request %p\n", task->tk_pid, req);
+
+	/* remove slot from queue of pending */
+	disable_bh(NET_BH);
+	if (task->tk_rpcwait) {
+		printk("RPC: task of released request still queued!\n");
+#ifdef RPC_DEBUG
+		printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait));
+#endif
+		rpc_del_timer(task);
+		rpc_remove_wait_queue(task);
+	}
+	enable_bh(NET_BH);
+
+	/* Decrease congestion value. If congestion threshold is not yet
+	 * reached, pass on the request slot.
+	 * This looks kind of kludgy, but it guarantees backlogged requests
+	 * are served in order.
+	 */
+	xprt->cong -= RPC_CWNDSCALE;
+	if (!RPCXPRT_CONGESTED(xprt)) {
+		struct rpc_task	*next = rpc_wake_up_next(&xprt->backlog);
+
+		if (next && next->tk_rqstp == 0) {
+			xprt->cong += RPC_CWNDSCALE;
+			next->tk_rqstp = req;
+			xprt_request_init(next, xprt);
+			return;
+		}
+	}
+
+	req->rq_next = xprt->free;
+	xprt->free   = req;
+}
+
+/*
+ * Set default timeout parameters
+ */
+void
+xprt_default_timeout(struct rpc_timeout *to, int proto)
+{
+	if (proto == IPPROTO_UDP)
+		xprt_set_timeout(to, 5,  5 * HZ);
+	else
+		xprt_set_timeout(to, 5, 15 * HZ);
+}
+
+/*
+ * Set constant timeout
+ */
+void
+xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr)
+{
+	to->to_current   = 
+	to->to_initval   = 
+	to->to_increment = incr;
+	to->to_maxval    = incr * retr;
+	to->to_resrvval  = incr * retr;
+	to->to_retries   = retr;
+	to->to_exponential = 0;
+}
+
+/*
+ * Initialize an RPC client
+ */
+static struct rpc_xprt *
+xprt_setup(struct socket *sock, int proto,
+			struct sockaddr_in *ap, struct rpc_timeout *to)
+{
+	struct rpc_xprt	*xprt;
+	struct rpc_rqst	*req;
+	struct sock	*inet;
+	int		i;
+
+	dprintk("RPC:      setting up %s transport...\n",
+				proto == IPPROTO_UDP? "UDP" : "TCP");
+
+#if LINUX_VERSION_CODE >= 0x020100
+	inet = sock->sk;
+#else
+	inet = (struct sock *) sock->data;
+#endif
+
+	if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL)
+		return NULL;
+	memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */
+
+	xprt->file = NULL;
+	xprt->sock = sock;
+	xprt->inet = inet;
+	xprt->addr = *ap;
+	xprt->prot = proto;
+	xprt->stream = (proto == IPPROTO_TCP)? 1 : 0;
+	xprt->cwnd = RPC_INITCWND;
+#ifdef SOCK_HAS_USER_DATA
+	inet->user_data = xprt;
+#else
+	xprt->link = sock_list;
+	sock_list = xprt;
+#endif
+	xprt->old_data_ready = inet->data_ready;
+	xprt->old_state_change = inet->state_change;
+	xprt->old_write_space = inet->write_space;
+	if (proto == IPPROTO_UDP) {
+		inet->data_ready = udp_data_ready;
+	} else {
+		inet->data_ready = tcp_data_ready;
+		inet->state_change = tcp_state_change;
+		inet->write_space = tcp_write_space;
+		xprt->nocong = 1;
+	}
+	xprt->connected = 1;
+
+	/* Set timeout parameters */
+	if (to) {
+		xprt->timeout = *to;
+		xprt->timeout.to_current = to->to_initval;
+		xprt->timeout.to_resrvval = to->to_maxval << 1;
+	} else {
+		xprt_default_timeout(&xprt->timeout, xprt->prot);
+	}
+
+	xprt->pending = RPC_INIT_WAITQ("xprt_pending");
+	xprt->sending = RPC_INIT_WAITQ("xprt_sending");
+	xprt->backlog = RPC_INIT_WAITQ("xprt_backlog");
+	xprt->reconn  = RPC_INIT_WAITQ("xprt_reconn");
+
+	/* initialize free list */
+	for (i = 0, req = xprt->slot; i < RPC_MAXREQS-1; i++, req++)
+		req->rq_next = req + 1;
+	req->rq_next = NULL;
+	xprt->free = xprt->slot;
+
+	dprintk("RPC:      created transport %p\n", xprt);
+	return xprt;
+}
+
+/*
+ * Create and initialize an RPC client given an open file.
+ * This is obsolete now.
+ */
+#if 0
+struct rpc_xprt *
+xprt_create(struct file *file, struct sockaddr_in *ap, struct rpc_timeout *to)
+{
+	struct rpc_xprt	*xprt;
+	struct socket	*sock;
+	int		proto;
+
+	if (!file) {
+		printk("RPC: file == NULL in xprt_create!\n");
+		return NULL;
+	}
+
+	sock = &file->f_inode->u.socket_i;
+	if (sock->ops->family != AF_INET) {
+		printk(KERN_WARNING "RPC: only INET sockets supported\n");
+		return NULL;
+	}
+
+	proto = (sock->type == SOCK_DGRAM)? IPPROTO_UDP : IPPROTO_TCP;
+	if ((xprt = xprt_setup(sock, proto, ap, to)) != NULL) {
+		xprt->file = file;
+		file->f_count++;
+	}
+
+	return xprt;
+}
+#endif
+
+/*
+ * Bind to a reserved port
+ */
+static inline int
+xprt_bindresvport(struct socket *sock)
+{
+	struct sockaddr_in myaddr;
+	int		err, port;
+
+	memset(&myaddr, 0, sizeof(myaddr));
+	myaddr.sin_family = AF_INET;
+	port = 800;
+	do {
+		myaddr.sin_port = htons(port);
+		err = sock->ops->bind(sock, (struct sockaddr *) &myaddr,
+						sizeof(myaddr));
+	} while (err == -EADDRINUSE && --port > 0);
+
+	if (err < 0)
+		printk("RPC: Can't bind to reserved port (%d).\n", -err);
+
+	return err;
+}
+
+/*
+ * Create a client socket given the protocol and peer address.
+ */
+static struct socket *
+xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+{
+	struct socket	*sock;
+	int		type, err;
+
+	dprintk("RPC:      xprt_create_socket(%08lx, %s %d)\n",
+			   sap? ntohl(sap->sin_addr.s_addr) : 0,
+			   (proto == IPPROTO_UDP)? "udp" : "tcp", proto);
+
+	type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+	if ((err = sock_create(AF_INET, type, proto, &sock)) < 0) {
+		printk("RPC: can't create socket (%d).\n", -err);
+		goto failed;
+	}
+
+	/* If the caller has root privs, bind to a reserved port */
+	if (!current->fsuid && xprt_bindresvport(sock) < 0)
+		goto failed;
+
+	if (type == SOCK_STREAM && sap) {
+		err = sock->ops->connect(sock, (struct sockaddr *) sap,
+						sizeof(*sap), 0);
+		if (err < 0) {
+			printk("RPC: TCP connect failed (%d).\n", -err);
+			goto failed;
+		}
+	}
+
+	return sock;
+
+failed:
+	sock_release(sock);
+	return NULL;
+}
+
+/*
+ * Create an RPC client transport given the protocol and peer address.
+ */
+struct rpc_xprt *
+xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+{
+	struct socket	*sock;
+	struct rpc_xprt	*xprt;
+
+	dprintk("RPC:      xprt_create_proto called\n");
+
+	if (!(sock = xprt_create_socket(proto, sap, to)))
+		return NULL;
+
+	if (!(xprt = xprt_setup(sock, proto, sap, to)))
+		sock_release(sock);
+
+	return xprt;
+}
+
+/*
+ * Prepare for transport shutdown.
+ */
+void
+xprt_shutdown(struct rpc_xprt *xprt)
+{
+	xprt->shutdown = 1;
+	rpc_wake_up(&xprt->sending);
+	rpc_wake_up(&xprt->pending);
+	rpc_wake_up(&xprt->backlog);
+	rpc_wake_up(&xprt->reconn);
+}
+
+/*
+ * Destroy an RPC transport, killing off all requests.
+ */
+int
+xprt_destroy(struct rpc_xprt *xprt)
+{
+#ifndef SOCK_HAS_USER_DATA
+	struct rpc_xprt	**q;
+
+	for (q = &sock_list; *q && *q != xprt; q = &((*q)->link))
+		;
+	if (!*q) {
+		printk(KERN_WARNING "xprt_destroy: unknown socket!\n");
+		return -EIO;	/* why is there no EBUGGYSOFTWARE */
+	}
+	*q = xprt->link;
+#endif
+
+	dprintk("RPC:      destroying transport %p\n", xprt);
+	xprt_close(xprt);
+	kfree(xprt);
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.31/linux/net/unix/Makefile linux/net/unix/Makefile
--- v2.1.31/linux/net/unix/Makefile	Mon Apr  1 21:43:08 1996
+++ linux/net/unix/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,7 +8,11 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := unix.o
-O_OBJS	 := af_unix.o garbage.o sysctl_net_unix.o
+O_OBJS	 := af_unix.o garbage.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_unix.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/x25/Makefile linux/net/x25/Makefile
--- v2.1.31/linux/net/x25/Makefile	Wed Dec 18 02:07:56 1996
+++ linux/net/x25/Makefile	Sat Apr  5 12:14:21 1997
@@ -8,8 +8,12 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := x25.o
-O_OBJS	 := af_x25.o sysctl_net_x25.o x25_dev.o x25_in.o x25_link.o x25_out.o x25_route.o x25_subr.o x25_timer.o
+O_OBJS	 := af_x25.o x25_dev.o x25_in.o x25_link.o x25_out.o x25_route.o x25_subr.o x25_timer.o
 M_OBJS   := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_x25.o
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.31/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.31/linux/net/x25/af_x25.c	Thu Mar 27 14:40:18 1997
+++ linux/net/x25/af_x25.c	Sat Apr  5 12:14:21 1997
@@ -1293,7 +1293,9 @@
 
 	printk(KERN_INFO "X.25 for Linux. Version 0.1 for Linux 2.1.15\n");
 
+#ifdef CONFIG_SYSCTL
 	x25_register_sysctl();
+#endif
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_x25);
@@ -1337,7 +1339,9 @@
 	x25_link_free();
 	x25_route_free();
 
+#ifdef CONFIG_SYSCTL
 	x25_unregister_sysctl();
+#endif
 
 	unregister_netdevice_notifier(&x25_dev_notifier);
 
