diff -uwbrN --exclude=config.save linux-2.4.23/arch/alpha/kernel/time.c linux-2.4.23-web100-HSv4/arch/alpha/kernel/time.c
--- linux-2.4.23/arch/alpha/kernel/time.c	2003-06-13 15:51:29.000000000 +0100
+++ linux-2.4.23-web100-HSv4/arch/alpha/kernel/time.c	2004-03-01 01:39:22.000000000 +0000
@@ -382,6 +382,30 @@
 	alpha_mv.init_rtc();
 }
 
+#ifdef CONFIG_WEB100_STATS
+void get_mono_time(__u64 *time)
+{
+#ifdef CONFIG_SMP
+	*time = jiffies * (1000000 / HZ);
+#else
+	unsigned long flags;
+	unsigned long delta_cycles, delta_usec, partial_tick, lost, now;
+
+	read_lock_irqsave(&xtime_lock, flags);
+	delta_cycles = rpcc() - state.last_time;
+	partial_tick = state.partial_tick;
+	now = jiffies;
+	read_unlock_irqrestore(&xtime_lock, flags);
+	
+	delta_usec = (delta_cycles * state.scaled_ticks_per_cycle
+		      + partial_tick) * 15625;
+	delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2;
+	
+	*time = now * (1000000 / HZ) + delta_usec;
+#endif
+}
+#endif
+
 /*
  * Use the cycle counter to estimate an displacement from the last time
  * tick.  Unfortunately the Alpha designers made only the low 32-bits of
diff -uwbrN --exclude=config.save linux-2.4.23/arch/i386/kernel/time.c linux-2.4.23-web100-HSv4/arch/i386/kernel/time.c
--- linux-2.4.23/arch/i386/kernel/time.c	2003-11-28 18:26:19.000000000 +0000
+++ linux-2.4.23-web100-HSv4/arch/i386/kernel/time.c	2004-03-01 01:39:18.000000000 +0000
@@ -422,6 +422,17 @@
 
 #endif
 
+#ifdef CONFIG_WEB100_STATS
+void get_mono_time(__u64 *time)
+{
+	unsigned long flags;
+	
+	read_lock_irqsave(&xtime_lock, flags);
+	*time = (__u64)jiffies * (1000000 / HZ) + do_gettimeoffset();
+	read_unlock_irqrestore(&xtime_lock, flags);
+}
+#endif
+
 /* No-cyclone stubs */
 #ifndef CONFIG_X86_SUMMIT
 int __init cyclone_setup(char *str) 
diff -uwbrN --exclude=config.save linux-2.4.23/arch/ia64/kernel/time.c linux-2.4.23-web100-HSv4/arch/ia64/kernel/time.c
--- linux-2.4.23/arch/ia64/kernel/time.c	2003-08-25 12:44:39.000000000 +0100
+++ linux-2.4.23-web100-HSv4/arch/ia64/kernel/time.c	2004-03-01 01:39:47.000000000 +0000
@@ -81,6 +81,18 @@
 	return (elapsed_cycles*local_cpu_data->usec_per_cyc) >> IA64_USEC_PER_CYC_SHIFT;
 }
 
+#ifdef CONFIG_WEB100_STATS
+void
+get_mono_time(__u64 *time)
+{
+	unsigned long flags;
+
+	read_lock_irqsave(&xtime_lock, flags);
+	*time = (__u64)jiffies * (1000000 / HZ) + gettimeoffset();
+	read_unlock_irqrestore(&xtime_lock, flags);
+}
+#endif
+  
 void
 do_settimeofday (struct timeval *tv)
 {
diff -uwbrN --exclude=config.save linux-2.4.23/arch/ppc/kernel/time.c linux-2.4.23-web100-HSv4/arch/ppc/kernel/time.c
--- linux-2.4.23/arch/ppc/kernel/time.c	2003-08-25 12:44:40.000000000 +0100
+++ linux-2.4.23-web100-HSv4/arch/ppc/kernel/time.c	2004-03-01 01:39:32.000000000 +0000
@@ -426,3 +426,21 @@
 	return mlt;
 }
 
+#ifdef CONFIG_WEB100_STATS
+void get_mono_time(__u64 *time)
+{
+	unsigned delta, lost_ticks;
+	unsigned long flags;
+
+	read_lock_irqsave(&xtime_lock, flags);
+	delta = tb_ticks_since(tb_last_stamp);
+#ifdef CONFIG_SMP
+	if( !smp_tb_synchronized)
+		delta = 0;
+#endif
+	lost_ticks = jiffies - wall_jiffies;
+	read_unlock_irqrestore(&xtime_lock, flags);
+
+	*time = mulhwu(tb_to_us, tb_ticks_per_jiffy * lost_ticks + delta) * (1000000/HZ);
+}
+#endif
diff -uwbrN --exclude=config.save linux-2.4.23/Documentation/web100/locking.txt linux-2.4.23-web100-HSv4/Documentation/web100/locking.txt
--- linux-2.4.23/Documentation/web100/locking.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/Documentation/web100/locking.txt	2004-03-01 01:40:03.000000000 +0000
@@ -0,0 +1,33 @@
+Web100 Locking Model for Linux 2.4
+John Heffner <jheffner@psc.edu>
+August 2, 2001
+
+
+1. Lookup Structures
+
+The connections entries are kept linked together simultaneously in a table
+and in a list.  Only entries in these structures can be looked up.  To
+protect these lookup structures, we have a single global reader-writer
+spinlock, web100_linkage_lock.  Since we grab the lock both from user space
+and in the bottom half, we must do a [read/write]_lock_bh.  As this disables
+the local BH's, this lock should *not* be held for very long.
+
+
+2. Data Integrity
+
+The statistics are protected by the sock's lock.  Any code modifying or
+reading the statistics should hold the sock lock while doing so.  We assume
+that if the socket is gone, the statistics should not be modified, so
+readers need not hold any lock.
+
+
+3. Statistics Destruction
+
+A statistics structure keeps a count of the number of references to it,
+wc_users.  When a lookup is performed, the reference count should be
+incremented (while the linkage lock is held) by calling web100_stats_use. 
+When the reference is no longer needed, decrement the count by calling
+web100_stats_unuse.  The latter function will free the statistics when there
+are no remaining references.  The lookup structures keep one reference.  The
+sock also keeps one, since the sock may be destroyed before it ever enters
+the ESTABLISHED state.
diff -uwbrN --exclude=config.save linux-2.4.23/Documentation/web100/proc_interface.txt linux-2.4.23-web100-HSv4/Documentation/web100/proc_interface.txt
--- linux-2.4.23/Documentation/web100/proc_interface.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/Documentation/web100/proc_interface.txt	2004-03-01 01:40:03.000000000 +0000
@@ -0,0 +1,102 @@
+WEB100 proc interface notes
+===========================
+
+The web100 modifications to the kernel collect information about the
+state of a TCP transfer in a kernel data structure that is linked
+out of the "sock" TCP structure in sock.h.  Please see
+"include/net/web100_stats.h" for the structure definition.
+
+The API for this structure is provided through the /proc interface.
+This document provides a brief description of this interface.  Please
+see fs/proc/web100.c for source code.
+
+First, kernel creates the /proc/web100 directory and the file
+/proc/web100/header at system boot time.
+
+Each new TCP connection is assigned a unique, unchanging number
+(similar to a pid), and its directory name is that number as ASCII
+decimal.  These directories persist for about sixty seconds after the
+connection is terminated (goes into a CLOSED or TIME_WAIT state).  The
+connection stats will not change after the connection is terminated.
+(So a connection whose state variable is TIME_WAIT is not necessarily
+still in TIME_WAIT.)  It should be noted that what is meant by a
+"connection" here is actually one side of a connection.  If a
+connection is created from the local host to the local host, two
+connection ID's will be created.
+
+When writing an application to read from the proc interface, it should be
+taken into consideration that the directories and their files can disappear at
+any time (they do so at an interrupt level).  So if a file open fails on a
+file you just looked up (say, with glob), that's probably normal and the
+program should handle it gracefully.
+
+Another seemingly strange thing that can happen is that stats for multiple
+connections with the same four-tuple can show up.  No more than one of the
+connections may be in any state but CLOSED or TIME_WAIT.  This behavior is
+correct, and should be handled as such.
+
+The algorithms governing the connection numbers are not yet final. 
+Currently, for simplification, it is only possible to have 32768
+connections.
+
+Inside each connection directory is an identical set of files.  One is
+spec-ascii, which contains the connection four-tuple in human-readable
+format.  One can, for example, see all outgoing ssh connections by executing
+"grep ':22$' /proc/web100/*/spec-ascii" from the command prompt.
+
+The remaining files provide access to states of TCP-KIS variables in
+local host byte-order.  Since the number, names, and contents of these
+files can and will change with releases, they are described in a
+header file -- /proc/web100/header.  A file named spec, which contains the
+variables describing the connection's four-tuple, should be present
+for any release.
+
+The header file is in human-readable format as follows:
+	<version>
+	
+	/<filename>
+	<varname> <offset> <type>
+	<varname> <offset> <type>
+	...
+	
+	/<filename>
+	...
+The filename is the name of the file inside each connection directory.  (The
+/ is prepended to make it clear it is a new file, not a new variable in the
+previous file.  There is also an empty line before each filename.)  Each
+file has an arbitrary number of variables, and there are an arbitrary number
+of files.  The type is an integer, and is currently defined something like:
+
+	enum {
+		WEB100_TYPE_INTEGER,
+		WEB100_TYPE_INTEGER32,
+		WEB100_TYPE_IP_ADDRESS,
+		WEB100_TYPE_COUNTER32,
+		WEB100_TYPE_GAUGE32,
+		WEB100_TYPE_UNSIGNED32,
+		WEB100_TYPE_TIME_TICKS,
+		WEB100_TYPE_COUNTER64,
+		WEB100_TYPE_UNSIGNED16
+	};
+
+in the kernel source file fs/proc/web100.c.  These correspond to
+MIB-II types.  (RFC2578)
+
+To read variables, seek to the appropriate offset, then read the appropriate
+amount of data.  (Length is implied by the type.)  Multiple variables may be
+read with a single read, and will be read atomically when doing so. 
+Currently, all variables are readable, but this may not be true in the
+future.
+
+To write variables, seek to the appropriate offset, and write the
+appropriate amount of data.  Only a single variable may be written at one
+time.  If variables must be atomically written, a variable should be used as
+a flag to signal that the write is done, and the kernel code depending on
+the variables should be written to handle this.
+
+See: http://www.web100.org
+Please send coments to prog@web100.org
+
+John Heffner, Matt Mathis, R. Reddy
+August 2000, Jan 2001
+
diff -uwbrN --exclude=config.save linux-2.4.23/Documentation/web100/sysctl.txt linux-2.4.23-web100-HSv4/Documentation/web100/sysctl.txt
--- linux-2.4.23/Documentation/web100/sysctl.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/Documentation/web100/sysctl.txt	2004-03-01 01:40:03.000000000 +0000
@@ -0,0 +1,50 @@
+Web100 sysctl variables
+John Heffner <jheffner@psc.edu>
+October 10, 2002
+
+net.ipv4.WAD_FloydAIMD
+	This value is used for WAD_FloydAIMD by a connection when its KIS
+	variable is 0.  This variable requires that private extenisons be
+	enabled.
+
+net.ipv4.WAD_IFQ
+	This value is used for WAD_IFQ by a connection when its KIS
+	variable is 0.  This variable requires that Net100 extensions be
+	enabled.
+
+net.ipv4.WAD_MaxBurst
+	This value is used for WAD_MaxBurst by a connection when its KIS
+	variable is 0.  This variable requires that Net100 extensions be
+	enabled.
+
+net.ipv4.web100_default_wscale
+	This will be the minimum window scale advertised.
+
+net.ipv4.web100_no_metrics_save
+	When non-zero, TCP metrics will not be saved the the route dest
+	cache.  NOTE: values already in the cache will not be flushed
+	by writing to this variable.  To do so, as root write to
+	net.ipv4.route.flush.  This variable requires that Net100
+	extensions be enabled.
+
+net.ipv4.web100_rbufmode
+	The X_RBufMode KIS variable for each connection is set to this value
+	upon creation of the statistics structure.
+
+net.ipv4.web100_rcvbuf_emu
+	If this is non-zero and RBufMode is 1, then we will set LimRwin
+	when an application does a setsockopt(SO_RCVBUF).
+
+net.ipv4.web100_sbufmode
+	The X_SBufMode KIS variable for each connection is set to this value
+	upon creation of the statistics structure.
+
+net.ipv4.web100_sndbuf_emu
+	If this is non-zero and SBufMode is 1, then we will set LimCwnd
+	when an application does a setsockopt(SO_SNDBUF).
+
+net.ipv4.web100_fperms
+	Sets the file permissions of the files in /proc/web100/*/
+
+net.ipv4.web100_gid
+	Sets the group of the files in /proc/web100/*/
diff -uwbrN --exclude=config.save linux-2.4.23/Documentation/web100/tuning.txt linux-2.4.23-web100-HSv4/Documentation/web100/tuning.txt
--- linux-2.4.23/Documentation/web100/tuning.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/Documentation/web100/tuning.txt	2004-03-01 01:40:03.000000000 +0000
@@ -0,0 +1,36 @@
+One of the primary features of the 2.1 release of the Web100 kernel patch is
+that is contains a new style of TCP buffer management which effectively
+"auto-tunes" both sending and receiving flows.  The algorithms used are very
+similar to those described in
+<http://www.psc.edu/~jheffner/papers/senior_thesis.ps>, and will be further
+described in a future Web100 paper.
+
+Enabling/Disabling Autotuning
+-----------------------------
+System-wide and per-connection controls have been provided for enabling and
+disabling these experimental algorithms.  KIS variables X_SBufMode and
+X_RBufMode are the per-connection controls.  For each, a value of 0 uses the
+classic Linux buffering, and a value of 1 uses the Web100 autotuning. 
+Currently no other values are accepted.  The system-wide sysctl variables
+net.ipv4.web100_sbufmode and net.ipv4.web100_rbufmode are defaults for the
+KIS variables, loaded at connection startup.  Changing the sysctl variables
+will NOT affect currently established connections.
+
+Mis-tuning
+----------
+For diagnostic or demonstration purposes, it may be useful to mis-tune
+connections.  Previously, this was done by writing to the SndbufSet or
+RcvbufSet and then STuneMode or RTuneMode variables.  These are now
+deprecated.  The suggested method of mis-tuning now is to use LimCwnd and
+LimRwin.  These have precise implementation-independent definitions -- they
+are simply clamps on cwnd and rwin.
+
+Support for legacy applications
+------------------------------
+To provide backward compatibility, the old tuning variables do still have
+functionality, though it has been somewhat altered.  SndbufSet and RcvbufSet
+immediately set sndbuf and rcvbuf, respectively.  They also set LimCwnd and
+LimRwin.  The deprecated variables SMaxWinBuf and RMaxWinBuf also have the
+same effects.  The new variables X_Sndbuf and X_Rcvbuf as well as the
+deprecated variables SAppBuf and RAppBuf write to sndbuf and rcvbuf but do
+not set LimCwnd and LimRwin.
diff -uwbrN --exclude=config.save linux-2.4.23/fs/proc/Makefile linux-2.4.23-web100-HSv4/fs/proc/Makefile
--- linux-2.4.23/fs/proc/Makefile	2001-05-09 00:41:32.000000000 +0100
+++ linux-2.4.23-web100-HSv4/fs/proc/Makefile	2004-03-01 01:36:30.000000000 +0000
@@ -18,4 +18,8 @@
 obj-y += proc_devtree.o
 endif
 
+ifeq ($(CONFIG_WEB100_STATS),y)
+obj-y += web100.o
+endif
+
 include $(TOPDIR)/Rules.make
diff -uwbrN --exclude=config.save linux-2.4.23/fs/proc/root.c linux-2.4.23-web100-HSv4/fs/proc/root.c
--- linux-2.4.23/fs/proc/root.c	2002-08-03 01:39:45.000000000 +0100
+++ linux-2.4.23-web100-HSv4/fs/proc/root.c	2004-03-01 01:36:30.000000000 +0000
@@ -68,6 +68,10 @@
 	proc_rtas_init();
 #endif
 	proc_bus = proc_mkdir("bus", 0);
+	
+#ifdef CONFIG_WEB100_STATS
+	proc_web100_init();
+#endif
 }
 
 static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
diff -uwbrN --exclude=config.save linux-2.4.23/fs/proc/web100.c linux-2.4.23-web100-HSv4/fs/proc/web100.c
--- linux-2.4.23/fs/proc/web100.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/fs/proc/web100.c	2004-03-01 01:36:30.000000000 +0000
@@ -0,0 +1,1391 @@
+/*  
+ *  fs/proc/web100.c
+ *  
+ * Copyright (C) 2001 Matt Mathis <mathis@psc.edu>
+ * Copyright (C) 2001 John Heffner <jheffner@psc.edu>
+ *
+ * The Web 100 project.  See http://www.web100.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <net/web100.h>
+#include <linux/init.h>
+#include <linux/sysctl.h>
+
+#define WEB100MIB_BLOCK_SIZE	PAGE_SIZE - 1024
+
+extern __u32 sysctl_wmem_default;
+extern __u32 sysctl_wmem_max;
+
+struct proc_dir_entry *proc_web100_dir;
+static struct proc_dir_entry *proc_web100_header;
+
+
+/*
+ * Web100 variable reading/writing
+ */
+
+enum web100_connection_inos {
+	PROC_CONN_SPEC_ASCII = 1,
+	PROC_CONN_SPEC,
+	PROC_CONN_READ,
+	PROC_CONN_TEST,
+	PROC_CONN_TUNE,
+	PROC_CONN_HIGH_INO		/* Keep at the end */
+};
+
+enum {
+	WEB100_TYPE_INTEGER = 0,
+	WEB100_TYPE_INTEGER32,
+	WEB100_TYPE_INET_ADDRESS_IPV4,
+	WEB100_TYPE_IP_ADDRESS = WEB100_TYPE_INET_ADDRESS_IPV4, /* Depricated */
+	WEB100_TYPE_COUNTER32,
+	WEB100_TYPE_GAUGE32,
+	WEB100_TYPE_UNSIGNED32,
+	WEB100_TYPE_TIME_TICKS,
+	WEB100_TYPE_COUNTER64,
+	WEB100_TYPE_INET_PORT_NUMBER,
+	WEB100_TYPE_UNSIGNED16 = WEB100_TYPE_INET_PORT_NUMBER, /* Depricated */
+	WEB100_TYPE_INET_ADDRESS,
+	WEB100_TYPE_INET_ADDRESS_IPV6,
+};
+
+struct web100_var;
+typedef int (*web100_rwfunc_t)(void *buf, struct web100stats *stats,
+			       struct web100_var *vp);
+
+/* The printed variable description should look something like this (in ASCII):
+ * varname offset type
+ * where offset is the offset into the file.
+ */
+struct web100_var {
+	char *name;
+	__u32 type;
+	int len;
+	
+	web100_rwfunc_t read;
+	unsigned long read_data;	/* read handler-specific data */
+	
+	web100_rwfunc_t write;
+	unsigned long write_data;	/* write handler-specific data */
+	
+	struct web100_var *next;
+};
+
+struct web100_file {
+	int len;
+	char *name;
+	int low_ino;
+	mode_t mode;
+	
+	struct web100_var *first_var;
+};
+
+#define F(name,ino,perm) { sizeof (name) - 1, (name), (ino), (perm), NULL }
+static struct web100_file web100_file_arr[] = {
+	F("spec-ascii", PROC_CONN_SPEC_ASCII, S_IFREG | S_IRUGO),
+	F("spec", PROC_CONN_SPEC, S_IFREG | S_IRUGO),
+	F("read", PROC_CONN_READ, 0),
+	F("test", PROC_CONN_TEST, 0),
+	F("tune", PROC_CONN_TUNE, 0) };
+#undef F
+#define WEB100_FILE_ARR_SIZE	(sizeof (web100_file_arr) / sizeof (struct web100_file))
+
+/* This works only if the array is built in the correct order. */
+static inline struct web100_file *web100_file_lookup(int ino) {
+	return &web100_file_arr[ino - 1];
+}
+
+static void add_var(struct web100_file *file, char *name, int type,
+	web100_rwfunc_t read, unsigned long read_data,
+	web100_rwfunc_t write, unsigned long write_data)
+{
+	struct web100_var *var;
+	
+	/* Again, assuming add_var is only called at init. */
+	if ((var = kmalloc(sizeof (struct web100_var), GFP_KERNEL)) == NULL)
+		panic("No memory available for Web100 var.\n");
+	
+	var->name = name;
+	var->type = type;
+	switch (type) {
+	case WEB100_TYPE_INET_PORT_NUMBER:
+		var->len = 2;
+		break;
+	case WEB100_TYPE_INTEGER:
+	case WEB100_TYPE_INTEGER32:
+	case WEB100_TYPE_COUNTER32:
+	case WEB100_TYPE_GAUGE32:
+	case WEB100_TYPE_UNSIGNED32:
+	case WEB100_TYPE_TIME_TICKS:
+		var->len = 4;
+		break;
+	case WEB100_TYPE_COUNTER64:
+		var->len = 8;
+		break;
+	case WEB100_TYPE_INET_ADDRESS:
+		var->len = 17;
+		break;
+	default:
+		printk("Web100: Warning: Adding variable of unknown type.\n");
+		var->len = 0;
+	}
+	
+	var->read = read;
+	var->read_data = read_data;
+	
+	var->write = write;
+	var->write_data = write_data;
+	
+	var->next = file->first_var;
+	file->first_var = var;
+}
+
+
+/*
+ * proc filesystem routines
+ */
+
+static struct inode *proc_web100_make_inode(struct super_block *sb, int ino)
+{
+	struct inode *inode;
+	
+	inode = new_inode(sb);
+	if (!inode)
+		goto out;
+	
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	inode->i_ino = ino;
+	
+	inode->i_uid = 0;
+	inode->i_gid = 0;
+
+out:
+	return inode;
+}
+
+static inline ino_t ino_from_cid(int cid)
+{
+	return (cid << 8) | 0x80000000;
+}
+
+static inline ino_t ino_from_parts(ino_t dir_ino, __u16 low_ino)
+{
+	return (dir_ino & ~0xff) | low_ino;
+}
+
+static inline int cid_from_ino(ino_t ino)
+{
+	return (ino & 0x7fffff00) >> 8;
+}
+
+static inline int low_from_ino(ino_t ino)
+{
+	return ino & 0xff;
+}
+
+static int connection_file_open(struct inode *inode, struct file *file)
+{
+	int cid = cid_from_ino(inode->i_ino);
+	struct web100stats *stats;
+	
+	read_lock_bh(&web100_linkage_lock);
+	stats = web100stats_lookup(cid);
+	if (stats == NULL || stats->wc_dead) {
+		read_unlock_bh(&web100_linkage_lock);
+		return -ENOENT;
+	}
+	web100_stats_use(stats);
+	read_unlock_bh(&web100_linkage_lock);
+	
+	return 0;
+}
+
+static int connection_file_release(struct inode *inode, struct file *file)
+{
+	int cid = cid_from_ino(inode->i_ino);
+	struct web100stats *stats;
+	
+	read_lock_bh(&web100_linkage_lock);
+	stats = web100stats_lookup(cid);
+	if (stats == NULL) {
+		read_unlock_bh(&web100_linkage_lock);
+		return -ENOENT;
+	}
+	read_unlock_bh(&web100_linkage_lock);
+	web100_stats_unuse(stats);
+	
+	return 0;
+}
+
+/**  /proc/web100/<connection>/<binary variable files>  **/
+static ssize_t connection_file_rw(int read, struct file *file,
+	char *buf, size_t nbytes, loff_t *ppos)
+{
+	int low_ino = low_from_ino(file->f_dentry->d_inode->i_ino);
+	int cid = cid_from_ino(file->f_dentry->d_inode->i_ino);
+	struct web100stats *stats;
+	struct web100_file *fp;
+	struct web100_var *vp;
+	int pos;
+	int n;
+	int err;
+	web100_rwfunc_t rwfunc;
+	char *page;
+	
+	/* We're only going to let them read one page at a time.
+	 * We shouldn't ever read more than a page, anyway, though.
+	 */
+	if (nbytes > PAGE_SIZE)
+		nbytes = PAGE_SIZE;
+	
+	if ((err = verify_area(read ? VERIFY_WRITE : VERIFY_READ, buf, nbytes)) < 0)
+		return err;
+	
+	if ((page = (char *)get_free_page(GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	
+	if (!read) {
+		if (copy_from_user(page, buf, nbytes))
+			return -EFAULT;
+	}
+	
+	fp = web100_file_lookup(low_ino);
+	if (fp == NULL) {
+		printk("Unregistered Web100 file.\n");
+		return 0;
+	}
+	
+	read_lock_bh(&web100_linkage_lock);
+	stats = web100stats_lookup(cid);
+	read_unlock_bh(&web100_linkage_lock);
+	if (stats == NULL)
+		return -ENOENT;
+	
+	lock_sock(stats->wc_sk);
+	
+	/* TODO: seek in constant time, not linear.  -JWH */
+	pos = 0;
+	n = 0;
+	vp = fp->first_var;
+	while (vp && nbytes > n) {
+		if (pos > *ppos) {
+			err = -ESPIPE;
+			goto err_out;
+		}
+		if (pos == *ppos) {
+			if (vp->len > nbytes - n)
+				break;
+			
+			if (read)
+				rwfunc = vp->read;
+			else
+				rwfunc = vp->write;
+			if (rwfunc == NULL) {
+				err = -EACCES;
+				goto err_out;
+			}
+			
+			err = rwfunc(page + n, stats, vp);
+			
+			if (err < 0)
+				goto err_out;
+			n += vp->len;
+			*ppos += vp->len;
+		}
+		pos += vp->len;
+		vp = vp->next;
+	}
+	
+	release_sock(stats->wc_sk);
+	
+	if (read) {
+		if (copy_to_user(buf, page, n))
+			return -EFAULT;
+	}
+	free_page((unsigned long)page);
+	
+	return n;
+
+err_out:
+	release_sock(stats->wc_sk);
+	
+	return err;
+}
+
+static ssize_t connection_file_read(struct file *file,
+	char *buf, size_t nbytes, loff_t *ppos)
+{
+	return connection_file_rw(1, file, buf, nbytes, ppos);
+}
+
+static ssize_t connection_file_write(struct file *file,
+	const char *buf, size_t nbytes, loff_t *ppos)
+{
+	return connection_file_rw(0, file, (char *)buf, nbytes, ppos);
+}
+
+static struct file_operations connection_file_fops = {
+	open:		connection_file_open,
+	release:	connection_file_release,
+	read:		connection_file_read,
+	write:		connection_file_write
+};
+
+
+static size_t v6addr_str(char *dest, short *addr)
+{
+	int start = -1, end = -1;
+	int i, j;
+	int pos;
+
+	/* Find longest subsequence of 0's in addr */
+	for (i = 0; i < 8; i++) {
+		if (addr[i] == 0) {
+			for (j = i + 1; addr[j] == 0 && j < 8; j++);
+			if (j - i > end - start) {
+				end = j;
+				start = i;
+			}
+			i = j;
+		}
+	}
+	if (end - start == 1)
+		start = -1;
+
+	pos = 0;
+	for (i = 0; i < 8; i++) {
+		if (i > 0)
+			pos += sprintf(dest + pos, ":");
+		if (i == start) {
+			pos += sprintf(dest + pos, ":");
+			i += end - start - 1;
+		} else {
+			pos += sprintf(dest + pos, "%hx", ntohs(addr[i]));
+		}
+	}
+
+	return pos;
+}
+
+/**  /proc/web100/<connection>/spec_ascii  **/
+static ssize_t connection_spec_ascii_read(struct file * file, char * buf,
+	size_t nbytes, loff_t *ppos)
+{
+	__u32 local_addr, remote_addr;
+	__u16 local_port, remote_port;
+	int cid;
+	struct web100stats *stats;
+	struct web100directs *vars;
+	char tmpbuf[100];
+	int len = 0;
+	
+	if (*ppos != 0)
+		return 0;
+	
+	cid = cid_from_ino(file->f_dentry->d_parent->d_inode->i_ino);
+	
+	read_lock_bh(&web100_linkage_lock);
+	stats = web100stats_lookup(cid);
+	read_unlock_bh(&web100_linkage_lock);
+	if (stats == NULL)
+		return -ENOENT;
+	vars = &stats->wc_vars;
+	
+	if (vars->LocalAddressType == WC_ADDRTYPE_IPV4) {
+		/* These values should not change while stats are linked.
+		 * We don't need to lock the sock. */
+		local_addr = ntohl(vars->LocalAddress.v4addr);
+		remote_addr = ntohl(vars->RemAddress.v4addr);
+		local_port = vars->LocalPort;
+		remote_port = vars->RemPort;
+		
+		len = sprintf(tmpbuf, "%d.%d.%d.%d:%d %d.%d.%d.%d:%d\n",
+			(local_addr >> 24) & 0xff,
+			(local_addr >> 16) & 0xff,
+			(local_addr >> 8) & 0xff,
+			local_addr & 0xff,
+			local_port,
+			(remote_addr >> 24) & 0xff,
+			(remote_addr >> 16) & 0xff,
+			(remote_addr >> 8) & 0xff,
+			remote_addr & 0xff,
+			remote_port);
+	} else if (vars->LocalAddressType == WC_ADDRTYPE_IPV6) {
+		local_port = vars->LocalPort;
+		remote_port = vars->RemPort;
+		
+		len += v6addr_str(tmpbuf + len, (short *)&vars->LocalAddress.v6addr.addr);
+		len += sprintf(tmpbuf + len, ".%d ", local_port);
+		len += v6addr_str(tmpbuf + len, (short *)&vars->RemAddress.v6addr.addr);
+		len += sprintf(tmpbuf + len, ".%d\n", remote_port);
+	} else {
+		printk(KERN_ERR "connection_spec_ascii_read: LocalAddressType invalid\n");
+		return 0;
+	}
+	
+	len = len > nbytes ? nbytes : len;
+	if (copy_to_user(buf, tmpbuf, len))
+		return -EFAULT;
+	*ppos += len;
+	return len;
+}
+
+static struct file_operations connection_spec_ascii_fops = {
+	open:		connection_file_open,
+	release:	connection_file_release,
+	read:		connection_spec_ascii_read
+};
+
+
+/**  /proc/web100/<connection>/  **/
+static int connection_dir_readdir(struct file *filp,
+	void *dirent, filldir_t filldir)
+{
+	int i;
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct web100_file *p;
+	
+	i = filp->f_pos;
+	switch (i) {
+	case 0:
+		if (filldir(dirent, ".", 1, i, inode->i_ino, DT_DIR) < 0)
+			return 0;
+		i++;
+		filp->f_pos++;
+		/* fall through */
+	case 1:
+		if (filldir(dirent, "..", 2, i, proc_web100_dir->low_ino, DT_DIR) < 0)
+			return 0;
+		i++;
+		filp->f_pos++;
+		/* fall through */
+	default:
+		i -= 2;
+		if (i >= WEB100_FILE_ARR_SIZE)
+			return 1;
+		p = &web100_file_arr[i];
+		while (p->name) {
+			if (filldir(dirent, p->name, p->len, filp->f_pos,
+				    ino_from_parts(inode->i_ino, p->low_ino),
+				    p->mode >> 12) < 0)
+				return 0;
+			filp->f_pos++;
+			p++;
+		}
+	}
+	
+	return 1;
+}
+
+static struct dentry *connection_dir_lookup(struct inode *dir,
+	struct dentry *dentry)
+{
+	struct inode *inode;
+	struct web100_file *p;
+	struct web100stats *stats;
+	uid_t uid;
+	
+	inode = NULL;
+	for (p = &web100_file_arr[0]; p->name; p++) {
+		if (p->len != dentry->d_name.len)
+			continue;
+		if (!memcmp(dentry->d_name.name, p->name, p->len))
+			break;
+	}
+	if (!p->name)
+		return ERR_PTR(-ENOENT);
+	
+	read_lock_bh(&web100_linkage_lock);
+	if ((stats = web100stats_lookup(cid_from_ino(dir->i_ino))) == NULL) {
+		read_unlock_bh(&web100_linkage_lock);
+		printk("connection_dir_lookup: stats == NULL\n");
+		return ERR_PTR(-ENOENT);
+	}
+	uid = sock_i_uid(stats->wc_sk);
+	read_unlock_bh(&web100_linkage_lock);
+	
+	inode = proc_web100_make_inode(dir->i_sb, ino_from_parts(dir->i_ino, p->low_ino));
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+	inode->i_mode = p->mode ? p->mode : S_IFREG | sysctl_web100_fperms;
+	inode->i_uid = uid;
+	inode->i_gid = sysctl_web100_gid;
+	
+	switch (p->low_ino) {
+	case PROC_CONN_SPEC_ASCII:
+		inode->i_fop = &connection_spec_ascii_fops;
+		break;
+	case PROC_CONN_SPEC:
+	case PROC_CONN_READ:
+	case PROC_CONN_TEST:
+	case PROC_CONN_TUNE:
+		inode->i_fop = &connection_file_fops;
+		break;
+	default:
+		printk("Web100: impossible type (%d)\n", p->low_ino);
+		iput(inode);
+		return ERR_PTR(-EINVAL);
+	}
+	
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static struct inode_operations connection_dir_iops = {
+	lookup:		connection_dir_lookup
+};
+
+static struct file_operations connection_dir_fops = {
+	readdir:	connection_dir_readdir
+};
+
+
+/**  /proc/web100/header  **/
+static ssize_t header_read(struct file * file, char * buf,
+	size_t nbytes, loff_t *ppos)
+{
+	int len = 0;
+	loff_t offset;
+	char *tmpbuf;
+	struct web100_file *fp;
+	struct web100_var *vp;
+	int n, tmp;
+	int i;
+	int ret = 0;
+	
+	/* We will assume the variable description list will not change
+	 * after init.  (True at least right now.) Otherwise, we would have
+	 * to have a lock on it.
+	 */
+	
+	if ((tmpbuf = (char *)get_free_page(GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	
+	offset = sprintf(tmpbuf, "%s\n", web100_version_string);
+	
+	for (i = 0; i < WEB100_FILE_ARR_SIZE; i++) {
+		int file_offset = 0;
+		
+		if ((fp = &web100_file_arr[i]) == NULL)
+			continue;
+		
+		if (fp->first_var == NULL)
+			continue;
+		
+		offset += sprintf(tmpbuf + offset, "\n/%s\n", fp->name);
+		
+		vp = fp->first_var;
+		while (vp) {
+			if (offset > WEB100MIB_BLOCK_SIZE) {
+				len += offset;
+				if (*ppos < len) {
+					n = min(offset, min_t(loff_t, nbytes, len - *ppos));
+					if (copy_to_user(buf, tmpbuf + max_t(loff_t, *ppos - len + offset, 0), n))
+						return -EFAULT;
+					buf += n;
+					if (nbytes == n) {
+						*ppos += n;
+						ret = n;
+						goto out;
+					}
+				}
+				offset = 0;
+			}
+			
+			offset += sprintf(tmpbuf + offset, "%s %d %d %d\n",
+					  vp->name, file_offset, vp->type, vp->len);
+			file_offset += vp->len;
+			
+			vp = vp->next;
+		}
+	}
+	len += offset;
+	if (*ppos < len) {
+		n = min(offset, min_t(loff_t, nbytes, len - *ppos));
+		if (copy_to_user(buf, tmpbuf + max_t(loff_t, *ppos - len + offset, 0), n))
+			return -EFAULT;
+		if (nbytes <= len - *ppos) {
+			*ppos += nbytes;
+			ret = nbytes;
+			goto out;
+		} else {
+			tmp = len - *ppos;
+			*ppos = len;
+			ret = tmp;
+			goto out;
+		}
+	}
+	
+out:
+	free_page((unsigned long)tmpbuf);
+	return ret;
+}
+
+static struct file_operations header_file_operations = {
+	read:		header_read
+};
+
+
+/**  /proc/web100/  **/
+#define FIRST_CONNECTION_ENTRY	256
+#define NUMBUF_LEN		11
+
+static int get_connection_list(int pos, int *cids, int max)
+{
+	struct web100stats *stats;
+	int n;
+	
+	pos -= FIRST_CONNECTION_ENTRY;
+	n = 0;
+	
+	read_lock_bh(&web100_linkage_lock);
+	
+	stats = web100stats_first;
+	while (stats && n < max) {
+		if (!stats->wc_dead) {
+			if (pos <= 0)
+				cids[n++] = stats->wc_cid;
+			else
+				pos--;
+		}
+		
+		stats = stats->wc_next;
+	}
+	
+	read_unlock_bh(&web100_linkage_lock);
+	
+	return n;
+}
+
+static int cid_to_str(int cid, char *buf)
+{
+	int len, tmp, i;
+	
+	if (cid == 0) { /* a special case */
+		len = 1;
+	} else {
+		tmp = cid;
+		for (len = 0; len < NUMBUF_LEN - 1 && tmp > 0; len++)
+			tmp /= 10;
+	}
+	
+	for (i = 0; i < len; i++) {
+		buf[len - i - 1] = '0' + (cid % 10);
+		cid /= 10;
+	}
+	buf[len] = '\0';
+	
+	return len;
+}
+
+static int web100_dir_readdir(struct file *filp,
+	void *dirent, filldir_t filldir)
+{
+	int err;
+	unsigned n, i;
+	int *cids;
+	int len;
+	ino_t ino;
+	char name[NUMBUF_LEN];
+	int n_conns;
+	
+	if (filp->f_pos < FIRST_CONNECTION_ENTRY) {
+		if ((err = proc_readdir(filp, dirent, filldir)) < 0)
+			return err;
+		filp->f_pos = FIRST_CONNECTION_ENTRY;
+	}
+	n_conns = WEB100_MAX_CONNS * 2;
+	do {
+		n_conns /= 2;
+		cids = kmalloc(n_conns * sizeof (int), GFP_KERNEL);
+	} while (cids == NULL && n_conns > 0);
+	if (cids == NULL)
+		return -ENOMEM;
+	n = get_connection_list(filp->f_pos, cids, n_conns);
+	
+	for (i = 0; i < n; i++) {
+		ino = ino_from_cid(cids[i]);
+		len = cid_to_str(cids[i], name);
+		if (filldir(dirent, name, len, filp->f_pos,
+			    ino, DT_DIR) < 0) {
+			break;
+		}
+		filp->f_pos++;
+	}
+	
+	kfree(cids);
+	
+	return 0;
+}
+
+static inline struct dentry *web100_dir_dent(void)
+{
+	struct qstr qstr;
+	
+	qstr.name = "web100";
+	qstr.len = 6;
+	qstr.hash = full_name_hash(qstr.name, qstr.len);
+	
+	return d_lookup(proc_mnt->mnt_sb->s_root, &qstr);
+}
+
+void web100_proc_nlink_update(nlink_t nlink)
+{
+	struct dentry *dent;
+	
+	dent = web100_dir_dent();
+	if (dent)
+		dent->d_inode->i_nlink = nlink;
+	dput(dent);
+}
+
+int web100_proc_dointvec_update(ctl_table *ctl, int write, struct file *filp,
+                               void *buffer, size_t *lenp)
+{
+	unsigned n, i;
+	int *cids;
+	int err;
+	struct qstr qstr;
+	struct dentry *web100_dent, *conn_dent, *dent;
+	struct inode *inode;
+	struct web100_file *p;
+	char name[NUMBUF_LEN];
+	
+	if ((err = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0)
+		return err;
+	
+	if ((web100_dent = web100_dir_dent()) == NULL)
+		return 0;
+	
+	if ((cids = kmalloc(WEB100_MAX_CONNS * sizeof (int), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	n = get_connection_list(FIRST_CONNECTION_ENTRY, cids, WEB100_MAX_CONNS);
+	for (i = 0; i < n; i++) {
+		qstr.len = cid_to_str(cids[i], name);
+		qstr.name = name;
+		qstr.hash = full_name_hash(qstr.name, qstr.len);
+		if ((conn_dent = d_lookup(web100_dent, &qstr)) != NULL) {
+			for (p = &web100_file_arr[0]; p->name; p++) {
+				qstr.name = p->name;
+				qstr.len = p->len;
+				qstr.hash = full_name_hash(qstr.name, qstr.len);
+				if ((dent = d_lookup(conn_dent, &qstr)) != NULL) {
+					inode = dent->d_inode;
+					if ((inode->i_mode = p->mode) == 0)
+						inode->i_mode = S_IFREG | sysctl_web100_fperms;
+					inode->i_gid = sysctl_web100_gid;
+					dput(dent);
+				}
+			}
+			dput(conn_dent);
+		}
+	}
+	dput(web100_dent);
+	kfree(cids);
+	printk("web100_proc_dointvec_update: set fperms = %d, gid = %d\n",
+	       sysctl_web100_fperms, sysctl_web100_gid);
+	
+	return 0;
+}
+
+static int web100_proc_connection_revalidate(struct dentry *dentry, int flags)
+{
+	int ret = 1;
+	
+	if (dentry->d_inode == NULL)
+		return 0;
+	read_lock_bh(&web100_linkage_lock);
+	if (web100stats_lookup(cid_from_ino(dentry->d_inode->i_ino)) == NULL) {
+		ret = 0;
+		d_drop(dentry);
+	}
+	read_unlock_bh(&web100_linkage_lock);
+	
+	return ret;
+}
+
+static struct dentry_operations web100_dir_dentry_operations = {
+	d_revalidate:	web100_proc_connection_revalidate
+};
+
+static struct dentry *web100_dir_lookup(struct inode *dir,
+	struct dentry *dentry)
+{
+	char *name;
+	int len;
+	int cid;
+	unsigned c;
+	struct inode *inode;
+	unsigned long ino;
+	struct web100stats *stats;
+	
+	if (proc_lookup(dir, dentry) == NULL)
+		return NULL;
+	
+	cid = 0;
+	name = (char *)(dentry->d_name.name);
+	len = dentry->d_name.len;
+	if (len <= 0)	/* I don't think this can happen */
+		return ERR_PTR(-EINVAL);
+	while (len-- > 0) {
+		c = *name - '0';
+		name++;
+		cid *= 10;
+		cid += c;
+		if (c > 9 || c < 0 || (cid == 0 && len != 0) || cid >= WEB100_MAX_CONNS) {
+			cid = -1;
+			break;
+		}
+	}
+	if (cid < 0)
+		return ERR_PTR(-ENOENT);
+	
+	read_lock_bh(&web100_linkage_lock);
+	stats = web100stats_lookup(cid);
+	if (stats == NULL || stats->wc_dead) {
+		read_unlock_bh(&web100_linkage_lock);
+		return ERR_PTR(-ENOENT);
+	}
+	read_unlock_bh(&web100_linkage_lock);
+	
+	ino = ino_from_cid(cid);
+	inode = proc_web100_make_inode(dir->i_sb, ino);
+	if (inode == NULL)
+		return ERR_PTR(-ENOMEM);
+	inode->i_nlink = 2;
+	inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+	inode->i_flags |= S_IMMUTABLE; /* ? */
+	inode->i_op = &connection_dir_iops;
+	inode->i_fop = &connection_dir_fops;
+	
+	dentry->d_op = &web100_dir_dentry_operations;
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static struct file_operations web100_dir_fops = {
+	readdir:	web100_dir_readdir
+};
+
+static struct inode_operations web100_dir_iops = {
+	lookup:		web100_dir_lookup
+};
+
+
+/*
+ * Read/write handlers
+ */
+
+/* A read handler for reading directly from the stats */
+/* read_data is the byte offset into struct web100stats */
+static int read_stats(void *buf, struct web100stats *stats,
+			   struct web100_var *vp)
+{
+	memcpy(buf, (char *)stats + vp->read_data, vp->len);
+	
+	return 0;
+}
+
+/* A write handler for writing directly to the stats */
+/* write_data is a byte offset into struct web100stats */
+static int write_stats(void *buf, struct web100stats *stats,
+			    struct web100_var *vp)
+{
+	memcpy((char *)stats + vp->read_data, buf, vp->len);
+	
+	return 0;
+}
+
+int read_LimCwnd(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	struct tcp_opt *tp = &stats->wc_sk->tp_pinfo.af_tcp;
+	__u32 tmp = (__u32)(tp->snd_cwnd_clamp * tp->mss_cache);
+
+	memcpy(buf, &tmp, 4);
+
+	return 0;
+}
+
+int write_LimCwnd(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	struct tcp_opt *tp = &stats->wc_sk->tp_pinfo.af_tcp;
+	
+	tp->snd_cwnd_clamp = min(*(__u32 *)buf / tp->mss_cache, 65535U);
+	
+	return 0;
+}
+
+int write_LimRwin(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	__u32 val = *(__u32 *)buf;
+	struct tcp_opt *tp = &stats->wc_sk->tp_pinfo.af_tcp;
+	
+	stats->wc_vars.LimRwin = tp->window_clamp =
+		min(val, 65535U << tp->rcv_wscale);
+	
+	return 0;
+}
+
+extern __u32 sysctl_wmem_default;
+extern __u32 sysctl_rmem_default;
+
+int write_SBufMode(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	__u32 val = *(__u32 *)buf;
+	struct sock *sk = stats->wc_sk;
+	
+	switch (val) {
+	case WC_BUFMODE_OS:
+		sk->userlocks &= ~SOCK_SNDBUF_LOCK;
+		break;
+	case WC_BUFMODE_WEB100:
+		sk->userlocks |= SOCK_SNDBUF_LOCK;
+		sk->sndbuf = sysctl_wmem_default;
+		sk->write_space(sk);
+		break;
+	default:
+		return 1;
+	}
+	stats->wc_vars.X_SBufMode = val;
+	
+	return 0;
+}
+
+int write_RBufMode(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	__u32 val = *(__u32 *)buf;
+	struct sock *sk = stats->wc_sk;
+	
+	switch (val) {
+	case WC_BUFMODE_OS:
+		sk->userlocks &= ~SOCK_RCVBUF_LOCK;
+		stats->wc_vars.LimRwin = sk->tp_pinfo.af_tcp.window_clamp;
+		break;
+	case WC_BUFMODE_WEB100:
+		sk->userlocks |= SOCK_RCVBUF_LOCK;
+		sk->rcvbuf = sysctl_rmem_default;
+		stats->wc_vars.LimRwin = 0xffffffff;
+		break;
+	default:
+		return 1;
+	}
+	stats->wc_vars.X_RBufMode = val;
+	
+	return 0;
+}
+
+/* A read handler for reading directly from the sk */
+/* read_data is a byte offset into the sk */
+static int read_sk(void *buf, struct web100stats *stats,
+			  struct web100_var *vp)
+{
+	/* Fill data with 0's if the connection is gone. */
+	if (stats->wc_sk == NULL)
+		memset(buf, 0, vp->len);
+	else
+		memcpy(buf, (char *)(stats->wc_sk) + vp->read_data, vp->len);
+	
+	return 0;
+}
+
+static int write_sk(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	if (stats->wc_sk == NULL)
+		return 1;
+	else
+		memcpy((char *)(stats->wc_sk) + vp->write_data, buf, vp->len);
+	
+	return 0;
+}
+
+#ifdef HAVE_MONO_TIME
+extern void get_mono_time(__u64 *time);
+#endif
+
+/* clean the clock */
+__u64 web100_mono_time()
+{
+#ifdef HAVE_MONO_TIME
+	__u64 time;
+	get_mono_time(&time);
+	return time;
+#else
+	struct timeval now;
+	static struct timeval before;
+
+	do_gettimeofday(&now);
+
+	/* assure monotonic, no matter what */
+	if ((now.tv_sec > before.tv_sec) ||
+	    ((now.tv_sec == before.tv_sec) && (now.tv_usec > before.tv_usec))) {
+		before = now;
+	} else {
+		before.tv_usec++;
+		if (before.tv_usec >= 1000000) {
+			before.tv_usec -= 1000000;
+			before.tv_sec++;
+		}
+	}
+	
+	return (1000000ULL * (__u64)before.tv_sec + before.tv_usec);
+#endif
+}
+
+/* A read handler to get the low part of the current time in usec */
+static int read_now(void *buf, struct web100stats *stats,
+			  struct web100_var *vp)
+{
+	__u64 val;
+
+	val = web100_mono_time();
+	val -= stats->wc_start_monotime;
+	memcpy(buf, (char *)&val, vp->len);
+
+	return 0;
+}
+
+#ifdef CONFIG_WEB100_NET100
+static int write_mss(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	struct sock *sk = stats->wc_sk;
+	struct tcp_opt *tp;
+	__u32 val = *(__u32 *)buf;
+	
+	if (sk == NULL)
+		return 1;
+	tp = &sk->tp_pinfo.af_tcp;
+	
+	if (val > tp->mss_cache)
+		return 1;
+	if (val < 1)
+		return 1;
+	
+	tp->mss_cache = val;
+	web100_update_mss(tp);
+	
+	return 0;
+}
+#endif
+
+static int write_sndbuf(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	(__u32)(stats->wc_sk->sndbuf) = *(__u32 *)buf;
+	
+	return write_LimCwnd(buf, stats, vp);
+}
+
+static int write_rcvbuf(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	(__u32)(stats->wc_sk->rcvbuf) = *(__u32 *)buf;
+	
+	return write_LimRwin(buf, stats, vp);
+}
+
+static int rw_noop(void *buf, struct web100stats *stats, struct web100_var *vp)
+{
+	return 0;
+}
+
+/*
+ * init
+ */
+
+void __init proc_web100_init(void)
+{
+	/* Set up the proc files. */
+	proc_web100_dir = proc_mkdir("web100", NULL);
+	proc_web100_dir->proc_iops = &web100_dir_iops;
+	proc_web100_dir->proc_fops = &web100_dir_fops;
+	
+	proc_web100_header = create_proc_entry("header", S_IFREG | S_IRUGO,
+					       proc_web100_dir);
+	proc_web100_header->proc_fops = &header_file_operations;
+	
+	/* Set up the contents of the proc files. */
+#define OFFSET_IN(type,var)	((unsigned long)(&(((type *)NULL)->var)))
+#define OFFSET_ST(field) ((unsigned long)(&(((struct web100stats *)NULL)->wc_vars.field)))
+#define OFFSET_SK(field) ((unsigned long)(&(((struct sock *)NULL)->field)))
+
+#define ADD_RO_STATSVAR(ino,name,type)	\
+add_var(web100_file_lookup(ino), #name, type, \
+	read_stats, OFFSET_ST(name), NULL, 0)
+
+#define ADD_RO_STATSRENAME(ino,name,type,var)	\
+add_var(web100_file_lookup(ino), name, type, \
+	read_stats, OFFSET_ST(var), NULL, 0)
+
+#define ADD_RO_STATSVAR_DEP(ino,name,type)	\
+add_var(web100_file_lookup(ino), "_" #name, type, \
+	read_stats, OFFSET_ST(name), NULL, 0)
+
+#define ADD_WO_STATSVAR(ino,name,type)	\
+add_var(web100_file_lookup(ino), #name, type, NULL, 0, \
+	write_stats, OFFSET_ST(name))
+
+#define ADD_WO_STATSVAR_DEP(ino,name,type)	\
+add_var(web100_file_lookup(ino), "_" #name, type, NULL, 0, \
+	write_stats, OFFSET_ST(name))
+
+#define ADD_RW_STATSVAR(ino,name,type)	\
+add_var(web100_file_lookup(ino), #name, type, \
+	read_stats, OFFSET_ST(name), \
+	write_stats, OFFSET_ST(name))
+
+#define ADD_RW_STATSVAR_DEP(ino,name,type)	\
+add_var(web100_file_lookup(ino), "_" #name, type, \
+	read_stats, OFFSET_ST(name), \
+	write_stats, OFFSET_ST(name))
+
+#define ADD_RO_SKVAR(ino,name,type,var) \
+add_var(web100_file_lookup(ino), #name, type, \
+	read_sk, OFFSET_SK(var), NULL, 0)
+
+#define ADD_RW_SKVAR(ino,name,type,var) \
+add_var(web100_file_lookup(ino), #name, type, \
+	read_sk, OFFSET_SK(var), write_sk, OFFSET_SK(var))
+
+#define ADD_NOOP(ino,name,type) \
+add_var(web100_file_lookup(ino), #name, type, \
+	rw_noop, 0, rw_noop, 0)
+
+	/* spec */
+	ADD_RO_STATSVAR(PROC_CONN_SPEC, LocalAddressType, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_SPEC, LocalAddress, WEB100_TYPE_INET_ADDRESS);
+	ADD_RO_STATSVAR(PROC_CONN_SPEC, LocalPort, WEB100_TYPE_INET_PORT_NUMBER);
+	ADD_RO_STATSVAR(PROC_CONN_SPEC, RemAddress, WEB100_TYPE_INET_ADDRESS);
+	ADD_RO_STATSVAR(PROC_CONN_SPEC, RemPort, WEB100_TYPE_INET_PORT_NUMBER);
+	ADD_RO_STATSRENAME(PROC_CONN_SPEC, "_RemoteAddress", WEB100_TYPE_INET_ADDRESS, RemAddress);
+	ADD_RO_STATSRENAME(PROC_CONN_SPEC, "_RemotePort", WEB100_TYPE_INET_PORT_NUMBER, RemPort);
+	
+	/* read */
+	/* STATE */
+	ADD_RO_STATSVAR(PROC_CONN_READ, State, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SACKEnabled, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, TimestampsEnabled, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, NagleEnabled, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, ECNEnabled, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SndWinScale, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RcvWinScale, WEB100_TYPE_INTEGER);
+	
+	/* SYN OPTIONS */
+	ADD_RO_STATSVAR(PROC_CONN_READ, ActiveOpen, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MSSRcvd, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, WinScaleRcvd, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, WinScaleSent, WEB100_TYPE_INTEGER);
+	
+	/* DATA */
+	ADD_RO_STATSVAR(PROC_CONN_READ, PktsOut, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DataPktsOut, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR_DEP(PROC_CONN_READ, AckPktsOut, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DataBytesOut, WEB100_TYPE_COUNTER64);
+	ADD_RO_STATSVAR(PROC_CONN_READ, PktsIn, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DataPktsIn, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR_DEP(PROC_CONN_READ, AckPktsIn, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DataBytesIn, WEB100_TYPE_COUNTER64);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SndUna, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SndNxt, WEB100_TYPE_INTEGER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SndMax, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_snd_una", WEB100_TYPE_COUNTER32, SndUna);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_snd_nxt", WEB100_TYPE_INTEGER32, SndNxt);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_snd_max", WEB100_TYPE_COUNTER32, SndMax);
+	ADD_RO_STATSVAR(PROC_CONN_READ, ThruBytesAcked, WEB100_TYPE_COUNTER64);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_ThruBytesSent", WEB100_TYPE_COUNTER64, ThruBytesAcked);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SndISS, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR_DEP(PROC_CONN_READ, SendWraps, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RcvNxt, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_rcv_nxt", WEB100_TYPE_COUNTER32, RcvNxt);
+	ADD_RO_STATSVAR(PROC_CONN_READ, ThruBytesReceived, WEB100_TYPE_COUNTER64);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RecvISS, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR_DEP(PROC_CONN_READ, RecvWraps, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR_DEP(PROC_CONN_READ, StartTime, WEB100_TYPE_INTEGER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, StartTimeSec, WEB100_TYPE_INTEGER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, StartTimeUsec, WEB100_TYPE_INTEGER32);
+	add_var(web100_file_lookup(PROC_CONN_READ), "Duration", WEB100_TYPE_COUNTER64, read_now, 0, NULL, 0);
+	add_var(web100_file_lookup(PROC_CONN_READ), "_CurrTime", WEB100_TYPE_COUNTER64, read_now, 0, NULL, 0);
+        ADD_RO_STATSVAR(PROC_CONN_READ, Bi, WEB100_TYPE_INTEGER32);
+
+	/* SENDER CONGESTION */
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimTransSender", WEB100_TYPE_COUNTER32, SndLimTrans[WC_SNDLIM_SENDER]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimBytesSender", WEB100_TYPE_COUNTER64, SndLimBytes[WC_SNDLIM_SENDER]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimTimeSender", WEB100_TYPE_COUNTER32, SndLimTime[WC_SNDLIM_SENDER]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimTransCwnd", WEB100_TYPE_COUNTER32, SndLimTrans[WC_SNDLIM_CWND]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimBytesCwnd", WEB100_TYPE_COUNTER64, SndLimBytes[WC_SNDLIM_CWND]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimTimeCwnd", WEB100_TYPE_COUNTER32, SndLimTime[WC_SNDLIM_CWND]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimTransRwin", WEB100_TYPE_COUNTER32, SndLimTrans[WC_SNDLIM_RWIN]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimBytesRwin", WEB100_TYPE_COUNTER64, SndLimBytes[WC_SNDLIM_RWIN]);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "SndLimTimeRwin", WEB100_TYPE_COUNTER32, SndLimTime[WC_SNDLIM_RWIN]);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SlowStart, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CongAvoid, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CongestionSignals, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, OtherReductions, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, X_OtherReductionsCV, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, X_OtherReductionsCM, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CongestionOverCount, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_Recoveries", WEB100_TYPE_COUNTER32, CongestionSignals);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurCwnd, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrentCwnd", WEB100_TYPE_GAUGE32, CurCwnd);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxCwnd, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurSsthresh, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrentSsthresh", WEB100_TYPE_GAUGE32, CurSsthresh);
+	add_var(web100_file_lookup(PROC_CONN_READ), "LimCwnd", WEB100_TYPE_GAUGE32, read_LimCwnd, 0, NULL, 0);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxSsthresh, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MinSsthresh, WEB100_TYPE_GAUGE32);
+
+	/* SENDER PATH MODEL */
+	ADD_RO_STATSVAR(PROC_CONN_READ, FastRetran, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, Timeouts, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SubsequentTimeouts, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurTimeoutCount, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrTimeoutCount", WEB100_TYPE_GAUGE32, CurTimeoutCount);
+	ADD_RO_STATSVAR(PROC_CONN_READ, AbruptTimeouts, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, PktsRetrans, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, BytesRetrans, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DupAcksIn, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SACKsRcvd, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SACKBlocksRcvd, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, PreCongSumCwnd, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_SumCwndAtCong", WEB100_TYPE_COUNTER32, PreCongSumCwnd);
+	ADD_RO_STATSVAR(PROC_CONN_READ, PreCongSumRTT, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR_DEP(PROC_CONN_READ, PreCongCountRTT, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, PostCongSumRTT, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, PostCongCountRTT, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, ECERcvd, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SendStall, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, QuenchRcvd, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RetranThresh, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, NonRecovDA, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, AckAfterFR, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DSACKDups, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SampleRTT, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_SampledRTT", WEB100_TYPE_GAUGE32, SampleRTT);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SmoothedRTT, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RTTVar, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxRTT, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MinRTT, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, SumRTT, WEB100_TYPE_COUNTER64);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CountRTT, WEB100_TYPE_COUNTER32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurRTO, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrentRTO", WEB100_TYPE_GAUGE32, CurRTO);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxRTO, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MinRTO, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurMSS, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrentMSS", WEB100_TYPE_GAUGE32, CurMSS);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxMSS, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MinMSS, WEB100_TYPE_GAUGE32);
+
+	/* SENDER BUFFER */
+#define PROC_CONN_XTEST PROC_CONN_READ	/* lazy */
+	ADD_RO_SKVAR(PROC_CONN_READ, _Sndbuf, WEB100_TYPE_GAUGE32, sndbuf);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurRetxQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurRetranQueue", WEB100_TYPE_GAUGE32, CurRetxQueue);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxRetxQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_MaxRetranQueue", WEB100_TYPE_GAUGE32, MaxRetxQueue);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurAppWQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxAppWQueue, WEB100_TYPE_GAUGE32);
+	
+	/* SENDER BUFFER TUNING - See below */
+
+	/* LOCAL RECEIVER */
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurRwinSent, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrentRwinSent", WEB100_TYPE_GAUGE32, CurRwinSent);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxRwinSent, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MinRwinSent, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, LimRwin, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, DupAcksOut, WEB100_TYPE_COUNTER32);
+	ADD_RO_SKVAR(PROC_CONN_READ, _Rcvbuf, WEB100_TYPE_GAUGE32, rcvbuf);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurReasmQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxReasmQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurAppRQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxAppRQueue, WEB100_TYPE_GAUGE32);
+	ADD_RO_SKVAR(PROC_CONN_XTEST, X_rcv_ssthresh, WEB100_TYPE_GAUGE32, tp_pinfo.af_tcp.rcv_ssthresh);
+	ADD_RO_SKVAR(PROC_CONN_XTEST, X_wnd_clamp, WEB100_TYPE_GAUGE32, tp_pinfo.af_tcp.window_clamp);
+	ADD_RO_STATSVAR(PROC_CONN_XTEST, X_dbg1, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_XTEST, X_dbg2, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_XTEST, X_dbg3, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_XTEST, X_dbg4, WEB100_TYPE_GAUGE32);
+
+	/* OBSERVED RECEIVER */
+	ADD_RO_STATSVAR(PROC_CONN_READ, CurRwinRcvd, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_CurrentRwinRcvd", WEB100_TYPE_GAUGE32, CurRwinRcvd);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MaxRwinRcvd, WEB100_TYPE_GAUGE32);
+	ADD_RO_STATSVAR(PROC_CONN_READ, MinRwinRcvd, WEB100_TYPE_GAUGE32);
+
+	/* CONNECTION ID */
+	ADD_RO_STATSVAR(PROC_CONN_READ, LocalAddressType, WEB100_TYPE_INTEGER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, LocalAddress, WEB100_TYPE_INET_ADDRESS);
+	ADD_RO_STATSVAR(PROC_CONN_READ, LocalPort, WEB100_TYPE_INET_PORT_NUMBER);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RemAddress, WEB100_TYPE_INET_ADDRESS);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_RemoteAddress", WEB100_TYPE_INET_ADDRESS, RemAddress);
+	ADD_RO_STATSVAR(PROC_CONN_READ, RemPort, WEB100_TYPE_INET_PORT_NUMBER);
+	ADD_RO_STATSRENAME(PROC_CONN_READ, "_RemotePort", WEB100_TYPE_INET_PORT_NUMBER, RemPort);
+	
+	ADD_RO_STATSVAR(PROC_CONN_READ, X_RcvRTT, WEB100_TYPE_GAUGE32);
+	
+	/* tune */
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "LimCwnd",
+		WEB100_TYPE_GAUGE32, read_LimCwnd, 0,
+		write_LimCwnd, 0);
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "LimRwin",
+		WEB100_TYPE_GAUGE32, read_stats, OFFSET_ST(LimRwin),
+		write_LimRwin, 0);
+#ifdef CONFIG_WEB100_NET100
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "CurMSS",
+	        WEB100_TYPE_GAUGE32, read_stats, OFFSET_ST(CurMSS),
+	        write_mss, 0);
+#endif
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "X_SBufMode",
+		WEB100_TYPE_INTEGER, read_stats, OFFSET_ST(X_SBufMode),
+		write_SBufMode, 0);
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "X_RBufMode",
+		WEB100_TYPE_INTEGER, read_stats, OFFSET_ST(X_RBufMode),
+		write_RBufMode, 0);
+	
+	ADD_RW_SKVAR(PROC_CONN_TUNE, X_Sndbuf, WEB100_TYPE_GAUGE32, sndbuf);
+	ADD_RW_SKVAR(PROC_CONN_TUNE, X_Rcvbuf, WEB100_TYPE_GAUGE32, rcvbuf);
+	
+	ADD_NOOP(PROC_CONN_TUNE, _STuneMode, WEB100_TYPE_INTEGER);
+	ADD_RO_SKVAR(PROC_CONN_TUNE, _SndbufGet, WEB100_TYPE_GAUGE32, sndbuf);
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "_SndbufSet",
+		WEB100_TYPE_GAUGE32, read_sk, OFFSET_SK(sndbuf),
+		write_sndbuf, 0);
+	ADD_RW_SKVAR(PROC_CONN_TUNE, _SAppBuf, WEB100_TYPE_GAUGE32, sndbuf);
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "_SMaxWinBuf",
+		WEB100_TYPE_GAUGE32, read_sk, OFFSET_SK(sndbuf),
+		write_sndbuf, 0);
+	ADD_NOOP(PROC_CONN_TUNE, _SXtra, WEB100_TYPE_GAUGE32);
+	ADD_NOOP(PROC_CONN_TUNE, _STuneErr, WEB100_TYPE_INTEGER);
+	
+	ADD_NOOP(PROC_CONN_TUNE, _RTuneMode, WEB100_TYPE_INTEGER);
+	ADD_RO_SKVAR(PROC_CONN_TUNE, _RcvbufGet, WEB100_TYPE_GAUGE32, rcvbuf);
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "_RcvbufSet",
+		WEB100_TYPE_GAUGE32, read_sk, OFFSET_SK(rcvbuf),
+		write_rcvbuf, 0);
+	ADD_RW_SKVAR(PROC_CONN_TUNE, _RAppBuf, WEB100_TYPE_GAUGE32, rcvbuf);
+	add_var(web100_file_lookup(PROC_CONN_TUNE), "_RMaxWinBuf",
+		WEB100_TYPE_GAUGE32, read_sk, OFFSET_SK(rcvbuf),
+		write_rcvbuf, 0);
+	ADD_NOOP(PROC_CONN_TUNE, _RXtra, WEB100_TYPE_GAUGE32);
+	ADD_NOOP(PROC_CONN_TUNE, _RTuneErr, WEB100_TYPE_INTEGER);
+
+#ifdef CONFIG_WEB100_NET100
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_IFQ, WEB100_TYPE_GAUGE32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_MaxBurst, WEB100_TYPE_GAUGE32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_MaxSsthresh, WEB100_TYPE_GAUGE32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_FloydAIMD, WEB100_TYPE_GAUGE32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_AI, WEB100_TYPE_GAUGE32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_MD, WEB100_TYPE_GAUGE32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_NoAI, WEB100_TYPE_INTEGER);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_CwndAdjust, WEB100_TYPE_INTEGER32);
+	ADD_RW_STATSVAR(PROC_CONN_TUNE, WAD_Kaicnt, WEB100_TYPE_INTEGER32);
+#endif
+}
diff -uwbrN --exclude=config.save linux-2.4.23/include/asm-alpha/timex.h linux-2.4.23-web100-HSv4/include/asm-alpha/timex.h
--- linux-2.4.23/include/asm-alpha/timex.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.23-web100-HSv4/include/asm-alpha/timex.h	2004-03-01 01:37:00.000000000 +0000
@@ -17,6 +17,10 @@
  * which isn't an evil thing.
  */
 
+#ifdef CONFIG_WEB100_STATS
+#define HAVE_MONO_TIME	1
+#endif
+
 typedef unsigned int cycles_t;
 
 static inline cycles_t get_cycles (void)
diff -uwbrN --exclude=config.save linux-2.4.23/include/asm-i386/timex.h linux-2.4.23-web100-HSv4/include/asm-i386/timex.h
--- linux-2.4.23/include/asm-i386/timex.h	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/asm-i386/timex.h	2004-03-25 00:04:44.000000000 +0000
@@ -20,6 +20,10 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
+#ifdef CONFIG_WEB100_STATS
+#define HAVE_MONO_TIME	1
+#endif
+
 /*
  * Standard way to access the cycle counter on i586+ CPUs.
  * Currently only used on SMP.
diff -uwbrN --exclude=config.save linux-2.4.23/include/asm-ia64/timex.h linux-2.4.23-web100-HSv4/include/asm-ia64/timex.h
--- linux-2.4.23/include/asm-ia64/timex.h	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/asm-ia64/timex.h	2004-03-01 01:37:11.000000000 +0000
@@ -10,6 +10,10 @@
  *			Also removed cacheflush_time as it's entirely unused.
  */
 
+#ifdef CONFIG_WEB100_STATS
+#define HAVE_MONO_TIME 1
+#endif
+
 typedef unsigned long cycles_t;
 
 static inline cycles_t
diff -uwbrN --exclude=config.save linux-2.4.23/include/asm-ppc/timex.h linux-2.4.23-web100-HSv4/include/asm-ppc/timex.h
--- linux-2.4.23/include/asm-ppc/timex.h	2003-06-13 15:51:38.000000000 +0100
+++ linux-2.4.23-web100-HSv4/include/asm-ppc/timex.h	2004-03-01 01:37:05.000000000 +0000
@@ -16,6 +16,10 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
+#ifdef CONFIG_WEB100_STATS
+#define HAVE_MONO_TIME 1
+#endif
+
 typedef unsigned long cycles_t;
 
 /*
diff -uwbrN --exclude=config.save linux-2.4.23/include/linux/netlink.h linux-2.4.23-web100-HSv4/include/linux/netlink.h
--- linux-2.4.23/include/linux/netlink.h	2002-11-28 23:53:15.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/linux/netlink.h	2004-03-25 00:05:41.000000000 +0000
@@ -8,6 +8,9 @@
 #define NETLINK_TCPDIAG		4	/* TCP socket monitoring			*/
 #define NETLINK_NFLOG		5	/* netfilter/iptables ULOG */
 #define NETLINK_ARPD		8
+#ifdef CONFIG_WEB100_STATS
+#define NETLINK_WEB100		10
+#endif
 #define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
 #define NETLINK_IP6_FW		13
 #define NETLINK_DNRTMSG		14	/* DECnet routing messages */
diff -uwbrN --exclude=config.save linux-2.4.23/include/linux/proc_fs.h linux-2.4.23-web100-HSv4/include/linux/proc_fs.h
--- linux-2.4.23/include/linux/proc_fs.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/linux/proc_fs.h	2004-03-25 00:04:49.000000000 +0000
@@ -86,6 +86,10 @@
 extern void proc_root_init(void);
 extern void proc_misc_init(void);
 
+#ifdef CONFIG_WEB100_STATS
+extern void proc_web100_init(void);
+#endif
+
 struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry);
 void proc_pid_delete_inode(struct inode *inode);
 int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir);
diff -uwbrN --exclude=config.save linux-2.4.23/include/linux/skbuff.h linux-2.4.23-web100-HSv4/include/linux/skbuff.h
--- linux-2.4.23/include/linux/skbuff.h	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.23-web100-HSv4/include/linux/skbuff.h	2004-03-25 00:04:54.000000000 +0000
@@ -177,7 +177,7 @@
 	 * want to keep them across layers you have to do a skb_clone()
 	 * first. This is owned by whoever has the skb queued ATM.
 	 */ 
-	char		cb[48];	 
+	char		cb[72];	 
 
 	unsigned int 	len;			/* Length of actual data			*/
  	unsigned int 	data_len;
@@ -566,8 +566,13 @@
 {
 	struct sk_buff *next, *prev, *result;
 
+	if (list==NULL) return NULL;
+	
 	prev = (struct sk_buff *) list;
 	next = prev->next;
+
+	if (next==NULL) return NULL;
+
 	result = NULL;
 	if (next != prev) {
 		result = next;
diff -uwbrN --exclude=config.save linux-2.4.23/include/linux/sysctl.h linux-2.4.23-web100-HSv4/include/linux/sysctl.h
--- linux-2.4.23/include/linux/sysctl.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/linux/sysctl.h	2004-03-25 00:04:46.000000000 +0000
@@ -311,6 +311,29 @@
 	NET_TCP_FRTO=92,
 	NET_TCP_LOW_LATENCY=93,
 	NET_IPV4_IPFRAG_SECRET_INTERVAL=94,
+	NET_TCP_HTCP,
+	NET_TCP_HTCP_MODESWITCH,
+        NET_TCP_HTCP_UNDO,
+        NET_TCP_HTCP_MODERATION,
+        NET_TCP_HTCP_CAP,
+#ifdef CONFIG_WEB100
+	NET_IPV4_WEB100_DEFAULT_WSCALE,
+#endif
+#ifdef CONFIG_WEB100_NET100
+	NET_IPV4_WEB100_NO_METRICS_SAVE,
+	NET_IPV4_WAD_IFQ,
+	NET_IPV4_WAD_MAX_BURST,
+	NET_IPV4_WAD_FLOYD_AIMD,
+	NET_IPV4_WEB100_SCALABLE_TCP,
+#endif
+#ifdef CONFIG_WEB100_STATS
+	NET_IPV4_WEB100_SBUFMODE,
+	NET_IPV4_WEB100_RBUFMODE,
+	NET_IPV4_WEB100_FPERMS,
+	NET_IPV4_WEB100_GID,
+	NET_IPV4_WEB100_SNDBUF_EMU,
+	NET_IPV4_WEB100_RCVBUF_EMU,
+#endif
 };
 
 enum {
@@ -731,6 +754,10 @@
 				  void *, size_t *);
 extern int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int,
 				      struct file *, void *, size_t *);
+#ifdef CONFIG_WEB100_STATS
+extern int web100_proc_dointvec_update(ctl_table *, int, struct file *,
+									   void *, size_t *);
+#endif
 
 extern int do_sysctl (int *name, int nlen,
 		      void *oldval, size_t *oldlenp,
diff -uwbrN --exclude=config.save linux-2.4.23/include/net/sock.h linux-2.4.23-web100-HSv4/include/net/sock.h
--- linux-2.4.23/include/net/sock.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/net/sock.h	2004-03-25 00:05:41.000000000 +0000
@@ -106,6 +106,8 @@
 #include <linux/filter.h>
 #endif
 
+#include <net/web100_stats.h>
+
 #include <asm/atomic.h>
 #include <net/dst.h>
 
@@ -256,6 +258,21 @@
 	__u32	end_seq;
 };
 
+struct sacked_list_details {
+        struct sk_buff   *sacked_list_head;
+        struct sk_buff   *sacked_list_tail;
+        __u16             sacked_list_size;
+        __u8              lock; 
+};
+
+#define SACK_CACHE_SIZE (48)
+struct sack_cache_item {
+        u8 ack_count;
+        u8 num;
+        u32 start_seq[SACK_CACHE_SIZE+1];
+        u32 end_seq[SACK_CACHE_SIZE+1];
+};
+
 struct tcp_opt {
 	int	tcp_header_len;	/* Bytes of tcp header to send		*/
 
@@ -326,24 +343,47 @@
 	__u32	packets_out;	/* Packets which are "in flight"	*/
 	__u32	left_out;	/* Packets which leaved network		*/
 	__u32	retrans_out;	/* Retransmitted packets out		*/
-
+        __u8    retrans_this_ack; /* packets retransmitted in response to current ack */
 
 /*
  *	Slow start and congestion control (see also Nagle, and Karn & Partridge)
  */
  	__u32	snd_ssthresh;	/* Slow start size threshold		*/
  	__u32	snd_cwnd;	/* Sending congestion window		*/
- 	__u16	snd_cwnd_cnt;	/* Linear increase counter		*/
-	__u16	snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */
+	__u32   snd_cwnd_cnt;   /* Linear increase counter      */
+	__u32   snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */
 	__u32	snd_cwnd_used;
 	__u32	snd_cwnd_stamp;
-
+	__u16   snd_ccount; /*number of RTT's since last back-off */
+	__u16   snd_cwnd_cnt2; /* counter used as part of snd_ccount calculation */
+	__u32   snd_minRTT; /* minimum RTT */
+	__u8    snd_decreasenum;  /*current backoff factor <<7 */
+	__u32   snd_maxRTT; /*max RTT */
+	__u32   snd_packetcount; /* number of packets acked since snd_lasttime */
+	__u32   snd_lasttime; 
+	__u32   snd_minB; /* throughput just after a backoff event */
+        __u32   snd_maxB; /* max throughput achieved in current congestion epoch */
+	__u32   snd_oldmaxB;  /* max throughput achieved in previous congestion epoch */
+	__u32   snd_Bi;  /* current achieved throughput */
+	__u32   snd_modecount; 
+        __u32   snd_alpha; /* current cwnd increase rate */
+        __u32   undo_modecount;
+        __u16   undo_ccount;
+        __u32   undo_oldmaxB;
+        __u32   undo_ssthresh;
+        __u32   undo_maxRTT;
 	/* Two commonly used timers in both sender and receiver paths. */
 	unsigned long		timeout;
  	struct timer_list	retransmit_timer;	/* Resend (no ack)	*/
  	struct timer_list	delack_timer;		/* Ack delay 		*/
 
 	struct sk_buff_head	out_of_order_queue; /* Out of order segments go here */
+	struct sacked_list_details sacked_list;
+
+        struct sk_buff *xmit_retransmit_queue_skb_hint;
+        int xmit_retransmit_queue_cnt_hint;
+        struct sk_buff *dupsack_skb_hint;
+        struct sack_cache_item sack_cache; /* cache sack info when under pressure */
 
 	struct tcp_func		*af_specific;	/* Operations which are AF_INET{4,6} specific	*/
 	struct sk_buff		*send_head;	/* Front of stuff to transmit			*/
@@ -432,6 +472,27 @@
 	__u32                   frto_highmark; /* snd_nxt when RTO occurred */
 
 	unsigned long last_synq_overflow; 
+
+	/* CONFIG_WEB100_STATS */
+	/* For storing Web100 protocol-specific instrument data */
+	struct web100stats  *tcp_stats;
+	
+	int         rcv_space;  /* space available for rcv queue */
+	int         rcv_alloc;  /* space used by rcv queue */
+	__u32           rcv_hi_seq; /* highest received valid sequence number */
+	__u32           rcv_prev_tstamp; /* last timestamp sent */
+
+	/* For DRS-style window measurement */
+	__u32           rcv_rtt;
+	__u32           rcv_rtt_seq;
+	unsigned long       rcv_rtt_time;
+	__u32           rcv_winest_seq;
+	unsigned long       rcv_winest_time;
+	
+	/* For timestamps window measurement */
+	__u8            rcv_tswin_pending;  /* Measurement pending */
+	__u32           rcv_tswin_tstamp;   /* time stamp we are waiting to be echoed */
+	__u32           rcv_tswin_seq;      /* rcv_hi_seq when we first sent rcv_tswin_tstamp */
 };
 
  	
@@ -675,7 +736,6 @@
 #endif
 	} protinfo;  		
 
-
 	/* This part is used for the timeout functions. */
 	struct timer_list	timer;		/* This is the sock cleanup timer. */
 	struct timeval		stamp;
@@ -695,6 +755,8 @@
   	int			(*backlog_rcv) (struct sock *sk,
 						struct sk_buff *skb);  
 	void                    (*destruct)(struct sock *sk);
+
+	int         retx_alloc;
 };
 
 /* The per-socket spinlock must be held here. */
@@ -820,6 +882,7 @@
 #define bh_lock_sock(__sk)	spin_lock(&((__sk)->lock.slock))
 #define bh_unlock_sock(__sk)	spin_unlock(&((__sk)->lock.slock))
 
+
 extern struct sock *		sk_alloc(int family, int priority, int zero_it);
 extern void			sk_free(struct sock *sk);
 
diff -uwbrN --exclude=config.save linux-2.4.23/include/net/tcp.h linux-2.4.23-web100-HSv4/include/net/tcp.h
--- linux-2.4.23/include/net/tcp.h	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/include/net/tcp.h	2004-03-25 01:01:21.000000000 +0000
@@ -32,6 +32,10 @@
 #include <net/sock.h>
 #include <net/snmp.h>
 
+#ifdef CONFIG_WEB100_STATS
+#include <net/web100.h>
+#endif
+
 /* This is for all connections with a full identity, no wildcards.
  * New scheme, half the table is for TIME_WAIT, the other half is
  * for the rest.  I'll experiment with dynamic table growth later.
@@ -463,10 +467,36 @@
 extern int sysctl_tcp_tw_reuse;
 extern int sysctl_tcp_frto;
 extern int sysctl_tcp_low_latency;
+extern int sysctl_tcp_htcp;
+extern int sysctl_tcp_htcp_modeswitch;
+extern int sysctl_tcp_htcp_undo;
+extern int sysctl_tcp_htcp_moderation;
+extern int sysctl_tcp_htcp_cap;
+#ifdef CONFIG_WEB100
+extern int sysctl_web100_default_wscale;
+#endif
+#ifdef CONFIG_WEB100_NET100
+extern int sysctl_web100_no_metrics_save;
+extern int sysctl_WAD_IFQ;
+extern int sysctl_WAD_MaxBurst;
+extern int sysctl_WAD_FloydAIMD;
+extern int sysctl_web100_scalable_tcp;
+#endif
+#ifdef CONFIG_WEB100_STATS
+extern int sysctl_web100_sbufmode;
+extern int sysctl_web100_rbufmode;
+extern int sysctl_web100_fperms;
+extern int sysctl_web100_gid;
+extern int sysctl_web100_sndbuf_emu;
+extern int sysctl_web100_rcvbuf_emu;
+#endif
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
 extern int tcp_memory_pressure;
+#ifdef CONFIG_WEB100_STATS
+extern atomic_t tcp_rwin_announced;
+#endif
 
 struct open_request;
 
@@ -892,7 +922,6 @@
 		if (!mod_timer(&tp->delack_timer, tp->ack.timeout))
 			sock_hold(sk);
 		break;
-
 	default:
 		printk(KERN_DEBUG "bug: unknown timer value\n");
 	};
@@ -953,7 +982,10 @@
 {
 	if (skb_queue_len(&tp->out_of_order_queue) == 0 &&
 	    tp->rcv_wnd &&
-	    atomic_read(&sk->rmem_alloc) < sk->rcvbuf &&
+#ifdef CONFIG_WEB100_STATS
+		(sysctl_web100_rbufmode == 1 ||
+		 atomic_read(&sk->rmem_alloc) < sk->rcvbuf) &&
+#endif
 	    !tp->urg_data)
 		tcp_fast_path_on(tp);
 }
@@ -1025,16 +1057,116 @@
 #define TCPCB_EVER_RETRANS	0x80	/* Ever retransmitted frame	*/
 #define TCPCB_RETRANS		(TCPCB_SACKED_RETRANS|TCPCB_EVER_RETRANS)
 
+       __u8        markedlost;
+#define TCPCB_MARKEDLOST 0x01
+
 #define TCPCB_URG		0x20	/* Urgent pointer advenced here	*/
 
 #define TCPCB_AT_TAIL		(TCPCB_URG)
 
 	__u16		urg_ptr;	/* Valid w/URG flags is set.	*/
 	__u32		ack_seq;	/* Sequence number ACK'd	*/
+	
+	__u8        transmitted;
+
+        struct sk_buff *next; /* linked list for sacktag */
+        struct sk_buff *prev;
 };
 
 #define TCP_SKB_CB(__skb)	((struct tcp_skb_cb *)&((__skb)->cb[0]))
 
+#define HTCP_DEBUG 0
+#define HTCP_PRINTK1 if (HTCP_DEBUG>=1) printk
+#define HTCP_PRINTK2 if (HTCP_DEBUG>=2) printk
+
+static inline void  clear_sacktag_hints( struct tcp_opt *tp)
+{
+    tp->xmit_retransmit_queue_skb_hint = NULL; 
+    tp->dupsack_skb_hint = NULL;
+}
+ 
+#define sacked_list_head(tp) (tp->sacked_list).sacked_list_head
+#define sacked_list_tail(tp) (tp->sacked_list).sacked_list_tail
+#define sacked_list_size(tp) (tp->sacked_list).sacked_list_size
+#define sacked_list_lock(tp) (tp->sacked_list).lock
+
+static inline  struct sk_buff* unlink_sacked_list (struct sk_buff *sl, struct tcp_opt *tp)
+{
+        struct sk_buff *next=TCP_SKB_CB(sl)->next;
+
+        BUG_TRAP(sl != NULL);
+        BUG_TRAP(tp != NULL);
+
+        if (sacked_list_lock(tp))
+           printk("unlink_sacked_list(): lock=%u on entry.\n",sacked_list_lock(tp));
+        sacked_list_lock(tp)=1;
+
+        if (TCP_SKB_CB(sl)->prev != NULL)
+                TCP_SKB_CB(TCP_SKB_CB(sl)->prev)->next = TCP_SKB_CB(sl)->next;
+        if (TCP_SKB_CB(sl)->next != NULL)
+                TCP_SKB_CB(TCP_SKB_CB(sl)->next)->prev = TCP_SKB_CB(sl)->prev;
+        if (sacked_list_head(tp) == sl && sacked_list_head(tp) != NULL)
+                sacked_list_head(tp)=TCP_SKB_CB(sl)->next;
+        if (sacked_list_tail(tp) == sl && sacked_list_tail(tp) != NULL)
+                sacked_list_tail(tp)=TCP_SKB_CB(sl)->prev;
+        if (sacked_list_size(tp) == 0) {
+           printk("unlink_sacked_list(): sacked_list_size(tp) < 0\n");
+           sacked_list_head(tp)=NULL;
+           sacked_list_tail(tp)=NULL;
+        } else
+           sacked_list_size(tp)--;
+
+        TCP_SKB_CB(sl)->next = NULL;
+        TCP_SKB_CB(sl)->prev = NULL;
+
+        sacked_list_lock(tp)=0;
+        return next;
+}
+
+static inline void clear_sacked_list (struct tcp_opt *tp)
+{
+        BUG_TRAP(tp != NULL);
+
+        sacked_list_head(tp) = NULL;
+        sacked_list_tail(tp) = NULL;
+        sacked_list_size(tp) = 0;
+        sacked_list_lock(tp)=0;
+}
+
+static inline void purge_sacked_list (struct tcp_opt *tp)
+{
+                struct sk_buff *sl;
+
+                BUG_TRAP(tp != NULL);
+
+                sl = sacked_list_head(tp);
+                while (sl != NULL)
+                   sl = unlink_sacked_list (sl, tp);
+}
+ 
+static inline int add_sacked_list_tail (struct sk_buff *sl, struct tcp_opt *tp)
+{
+
+        if (tp == NULL) printk("add_sacked_list_tail: tp==NULL\n");
+        BUG_TRAP(sl != NULL);
+
+        if (sacked_list_lock(tp))
+           printk("add_sacked_list(): lock=%u on entry.\n",sacked_list_lock(tp));
+        sacked_list_lock(tp)=2;
+
+        TCP_SKB_CB(sl)->prev = sacked_list_tail(tp);
+        TCP_SKB_CB(sl)->next = NULL;
+        if (sacked_list_tail(tp) != NULL)
+                (TCP_SKB_CB(sacked_list_tail(tp)))->next = sl;
+        sacked_list_tail(tp) = sl;
+        if (sacked_list_head(tp) == NULL)
+                sacked_list_head(tp)=sacked_list_tail(tp);
+        sacked_list_size(tp)++;
+
+        sacked_list_lock(tp)=0;
+        return 0;
+}
+
 #define for_retrans_queue(skb, sk, tp) \
 		for (skb = (sk)->write_queue.next;			\
 		     (skb != (tp)->send_head) &&			\
@@ -1058,6 +1190,109 @@
 	return sk->sndbuf - sk->wmem_queued;
 }
 
+static inline void htcp_reset(struct tcp_opt *tp)
+{
+        BUG_TRAP(tp!=NULL);
+
+        tp->undo_ccount=tp->snd_ccount;
+        tp->undo_oldmaxB = tp->snd_oldmaxB;
+        tp->undo_modecount = tp->snd_modecount;
+        tp->undo_maxRTT = tp->snd_maxRTT;
+
+	tp->snd_ccount=0; 
+	tp->snd_cwnd_cnt2=0;
+}
+
+static inline void decreasenum_check(struct tcp_opt *tp)
+{
+	if (tp->snd_decreasenum < 1<<6) tp->snd_decreasenum=1<<6; /* 0.5 */
+	if (tp->snd_decreasenum > 102) tp->snd_decreasenum=102; /* 102>>7 is 0.8 */    
+}
+
+static inline void decreasenum_update(struct tcp_opt *tp)
+{
+     HTCP_PRINTK1("calculate backoff factor to use: maxB=%u, oldmaxB=%u, modecount=%u\n",tp->snd_maxB,tp->snd_oldmaxB, tp->snd_modecount);
+
+     if ( (((5*tp->snd_maxB > 4*tp->snd_oldmaxB)
+           &&(5*tp->snd_maxB < 6*tp->snd_oldmaxB))
+           || sysctl_tcp_htcp_modeswitch==0) && sysctl_tcp_htcp) {
+        tp->snd_modecount++;
+        if (tp->snd_modecount > 1 && tp->snd_minRTT>HZ/100 && tp->snd_maxRTT >0) 
+           tp->snd_decreasenum = (tp->snd_minRTT<<7)/tp->snd_maxRTT;
+        else
+           tp->snd_decreasenum = 1<<6; /* 0.5 */
+     } else {
+        tp->snd_modecount = 0;
+        tp->snd_decreasenum = 1<<6; /* 0.5 */
+     }
+     decreasenum_check(tp);
+     tp->snd_oldmaxB = tp->snd_maxB;
+ 
+    // add slowly fading memory for maxRTT to accommodate routing changes etc
+     if (tp->snd_minRTT > 0 && tp->snd_maxRTT > tp->snd_minRTT)
+        tp->snd_maxRTT = tp->snd_minRTT + ((tp->snd_maxRTT-tp->snd_minRTT)*95)/100;
+}
+
+static inline void undo_htcp_reset(struct tcp_opt *tp)
+{
+        BUG_TRAP(tp!=NULL);
+
+        tp->snd_modecount = tp->undo_modecount;
+        tp->snd_oldmaxB = tp->undo_oldmaxB;
+        tp->snd_ccount = tp->undo_ccount;
+        tp->snd_maxRTT = tp->undo_maxRTT;
+}
+
+static inline void measure_minRTT (struct tcp_opt *tp)
+{
+       /* keep track of minimum RTT seen so far */
+        if (tp->snd_minRTT==0) tp->snd_minRTT=tp->srtt>>3; /* minRTT is initially zero */
+        tp->snd_minRTT=min(tp->snd_minRTT, tp->srtt>>3);
+        tp->snd_minRTT = max(tp->snd_minRTT, (__u32)1); /* safety net to avoid divide by zero */
+
+       /* max RTT */
+        if (tp->ca_state == TCP_CA_Open && tp->snd_ssthresh < 0xFFFF && tp->snd_ccount>3) {
+           if (tp->snd_maxRTT<tp->snd_minRTT) tp->snd_maxRTT = tp->snd_minRTT; 
+           if (tp->srtt>>3 > tp->snd_maxRTT && tp->srtt>>3 <= tp->snd_maxRTT+HZ/50 )  
+              tp->snd_maxRTT=tp->srtt>>3;
+        }
+}
+
+static inline void measure_achieved_throughput(struct tcp_opt *tp)
+{
+     __u32 now = tcp_time_stamp;
+
+     /* achieved throughput calculations */
+     if (tp->ca_state != TCP_CA_Open && tp->ca_state != TCP_CA_Disorder) {
+        tp->snd_packetcount = 0;
+        tp->snd_lasttime=now;
+     }
+
+     if (tp->snd_packetcount >= tp->snd_cwnd-tp->snd_alpha
+                && now - tp->snd_lasttime >= tp->snd_minRTT
+                && tp->snd_minRTT>0) {
+        if (tp->snd_ccount <=3) {
+           // just after backoff
+           tp->snd_Bi = tp->snd_packetcount*HZ/(now-tp->snd_lasttime);
+           tp->snd_minB=tp->snd_Bi;
+           tp->snd_maxB=tp->snd_Bi;
+        } else {
+           tp->snd_Bi=(3*tp->snd_Bi+tp->snd_packetcount*HZ/(now-tp->snd_lasttime))/4;
+           if (tp->snd_Bi > tp->snd_maxB)  tp->snd_maxB = tp->snd_Bi;
+           if (tp->snd_minB>tp->snd_maxB) tp->snd_minB=tp->snd_maxB; //sanity check
+        }
+        tp->snd_packetcount = 0;
+        tp->snd_lasttime = now;
+        HTCP_PRINTK1("Bi=%u, maxB=%u, ccount=%u, cwnd=%u\n", tp->snd_Bi, tp->snd_maxB,tp->snd_ccount, tp->snd_cwnd);
+     }
+
+}
+
+static inline void purge_sack_cache(struct tcp_opt *tp)
+{
+        tp->sack_cache.num=0; tp->sack_cache.ack_count=0;
+}
+
 
 /* This determines how many packets are "in the network" to the best
  * of our knowledge.  In many cases it is conservative, but where
@@ -1085,6 +1320,19 @@
  */
 static inline __u32 tcp_recalc_ssthresh(struct tcp_opt *tp)
 {
+      if (sysctl_tcp_htcp)
+        return max((tp->snd_cwnd*tp->snd_decreasenum)>>7, 2U);
+
+#ifdef CONFIG_WEB100_NET100
+      /* use WAD_MD rather than 0.5 */
+      if (NET100_WAD(tp, WAD_FloydAIMD, sysctl_WAD_FloydAIMD))
+              web100_update_floyd_aimd(tp); /* update AI MD */
+      if (NET100_WAD(tp, WAD_MD, 0)) {
+              __u32 tmp = tp->snd_cwnd - ((tp->snd_cwnd*NET100_WAD(tp, WAD_MD,128))>>8); /* 8b fraction*/
+              return max(tmp, 2U);
+      }
+        /* otherwise do standard stuff ... */
+#endif
 	return max(tp->snd_cwnd >> 1U, 2U);
 }
 
@@ -1096,11 +1344,13 @@
 {
 	if ((1<<tp->ca_state)&(TCPF_CA_CWR|TCPF_CA_Recovery))
 		return tp->snd_ssthresh;
-	else
+	else {
+                tp->undo_ssthresh = tp->snd_cwnd;
 		return max(tp->snd_ssthresh,
 			   ((tp->snd_cwnd >> 1) +
 			    (tp->snd_cwnd >> 2)));
 }
+}
 
 static inline void tcp_sync_left_out(struct tcp_opt *tp)
 {
@@ -1132,14 +1382,17 @@
 /* Set slow start threshould and cwnd not falling to slow start */
 static inline void __tcp_enter_cwr(struct tcp_opt *tp)
 {
+        printk("enter cwr.\n");
+        decreasenum_update(tp); 
 	tp->undo_marker = 0;
 	tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
 	tp->snd_cwnd = min(tp->snd_cwnd,
 			   tcp_packets_in_flight(tp) + 1U);
-	tp->snd_cwnd_cnt = 0;
+	tp->snd_cwnd_cnt = 0; htcp_reset(tp);
 	tp->high_seq = tp->snd_nxt;
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 	TCP_ECN_queue_cwr(tp);
+	WEB100_UPDATE_FUNC(tp, web100_update_congestion(tp, 0));
 }
 
 static inline void tcp_enter_cwr(struct tcp_opt *tp)
@@ -1158,6 +1411,9 @@
  */
 static __inline__ __u32 tcp_max_burst(struct tcp_opt *tp)
 {
+#ifdef CONFIG_WEB100_NET100
+	return (NET100_WAD(tp, WAD_MaxBurst, sysctl_WAD_MaxBurst));
+#endif
 	return 3;
 }
 
@@ -1195,7 +1451,11 @@
 /* This checks if the data bearing packet SKB (usually tp->send_head)
  * should be put on the wire right now.
  */
-static __inline__ int tcp_snd_test(struct tcp_opt *tp, struct sk_buff *skb,
+/* Web100:
+ * Modified to return WC_SNDLIM_NONE when ok, reason if not ok.
+ * The name is changed because we have changed the return value.
+ */
+static __inline__ int tcp_snd_wait(struct tcp_opt *tp, struct sk_buff *skb,
 				   unsigned cur_mss, int nonagle)
 {
 	/*	RFC 1122 - section 4.2.3.4
@@ -1218,7 +1478,16 @@
 	 *	to get new data) and if room at tail of skb is
 	 *	not enough to save something seriously (<32 for now).
 	 */
-
+	if ((tcp_packets_in_flight(tp) >= tp->snd_cwnd) &&
+		!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))
+		return WC_SNDLIM_CWND;
+	if (after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd))
+		return WC_SNDLIM_RWIN;
+	if (!(nonagle == 1 || tp->urg_mode ||
+		  !tcp_nagle_check(tp, skb, cur_mss, nonagle)))
+		return WC_SNDLIM_SENDER;
+	return WC_SNDLIM_NONE;
+#if 0
 	/* Don't be strict about the congestion window for the
 	 * final FIN frame.  -DaveM
 	 */
@@ -1227,6 +1496,7 @@
 		((tcp_packets_in_flight(tp) < tp->snd_cwnd) ||
 		 (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) &&
 		!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd));
+#endif
 }
 
 static __inline__ void tcp_check_probe_timer(struct sock *sk, struct tcp_opt *tp)
@@ -1254,7 +1524,7 @@
 	if (skb) {
 		if (!tcp_skb_is_last(sk, skb))
 			nonagle = 1;
-		if (!tcp_snd_test(tp, skb, cur_mss, nonagle) ||
+		if (tcp_snd_wait(tp, skb, cur_mss, nonagle) != WC_SNDLIM_NONE ||
 		    tcp_write_xmit(sk, nonagle))
 			tcp_check_probe_timer(sk, tp);
 	}
@@ -1272,8 +1542,9 @@
 	struct sk_buff *skb = tp->send_head;
 
 	return (skb &&
-		tcp_snd_test(tp, skb, tcp_current_mss(sk),
-			     tcp_skb_is_last(sk, skb) ? 1 : tp->nonagle));
+		tcp_snd_wait(tp, skb, tcp_current_mss(sk),
+				 tcp_skb_is_last(sk, skb) ? 1 : tp->nonagle)
+		== WC_SNDLIM_NONE);
 }
 
 static __inline__ void tcp_init_wl(struct tcp_opt *tp, u32 ack, u32 seq)
@@ -1372,6 +1643,8 @@
 {
 	int oldstate = sk->state;
 
+	WEB100_VAR_SET(&sk->tp_pinfo.af_tcp, State, web100_state(state));
+
 	switch (state) {
 	case TCP_ESTABLISHED:
 		if (oldstate != TCP_ESTABLISHED)
@@ -1530,6 +1803,10 @@
 		if (*rcv_wscale && sysctl_tcp_app_win && space>=mss &&
 		    space - max((space>>sysctl_tcp_app_win), mss>>*rcv_wscale) < 65536/2)
 			(*rcv_wscale)--;
+#ifdef CONFIG_WEB100
+		(*rcv_wscale) = max((__u8)sysctl_web100_default_wscale,
+					(*rcv_wscale));
+#endif
 	}
 
 	/* Set initial window to value enough for senders,
@@ -1679,12 +1956,14 @@
 
 #define TCP_MEM_QUANTUM	((int)PAGE_SIZE)
 
-static inline void tcp_free_skb(struct sock *sk, struct sk_buff *skb)
+static inline void tcp_uncharge_skb(struct sock *sk, struct sk_buff *skb)
 {
 	sk->tp_pinfo.af_tcp.queue_shrunk = 1;
 	sk->wmem_queued -= skb->truesize;
 	sk->forward_alloc += skb->truesize;
-	__kfree_skb(skb);
+#ifdef CONFIG_WEB100_STATS
+	web100_update_writeq(sk);
+#endif
 }
 
 static inline void tcp_charge_skb(struct sock *sk, struct sk_buff *skb)
@@ -1693,6 +1972,50 @@
 	sk->forward_alloc -= skb->truesize;
 }
 
+#ifdef CONFIG_WEB100_STATS
+extern atomic_t tcp_retx_mem;
+
+static inline void tcp_retx_uncharge_skb(struct sock *sk, struct sk_buff *skb)
+{
+	unsigned len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq;
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
+	tp->tcp_stats->wc_vars.CurRetxQueue -= len;
+	
+	atomic_sub(skb->truesize, &tcp_retx_mem);
+	sk->retx_alloc -= skb->truesize;
+	sock_put(sk);
+}
+
+static inline void tcp_retx_charge_skb(struct sock *sk, struct sk_buff *skb)
+{
+	unsigned len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq;
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct web100directs *vars = &tp->tcp_stats->wc_vars;
+	
+	vars->CurRetxQueue += len;
+	if (vars->MaxRetxQueue < vars->CurRetxQueue)
+		vars->MaxRetxQueue = vars->CurRetxQueue;
+	
+	TCP_SKB_CB(skb)->transmitted = 1;
+	sock_hold(sk);
+	tcp_uncharge_skb(sk, skb);
+	atomic_add(skb->truesize, &tcp_retx_mem);
+	sk->retx_alloc += skb->truesize;
+}
+#endif
+
+static inline void tcp_free_skb(struct sock *sk, struct sk_buff *skb)
+{
+#ifdef CONFIG_WEB100_STATS
+	if (TCP_SKB_CB(skb)->transmitted)
+		tcp_retx_uncharge_skb(sk, skb);
+	else
+#endif
+		tcp_uncharge_skb(sk, skb);
+	__kfree_skb(skb);
+}
+
 extern void __tcp_mem_reclaim(struct sock *sk);
 extern int tcp_mem_schedule(struct sock *sk, int size, int kind);
 
@@ -1712,6 +2035,9 @@
 
 static inline void tcp_moderate_sndbuf(struct sock *sk)
 {
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_sbufmode != 1)
+#endif
 	if (!(sk->userlocks&SOCK_SNDBUF_LOCK)) {
 		sk->sndbuf = min(sk->sndbuf, sk->wmem_queued/2);
 		sk->sndbuf = max(sk->sndbuf, SOCK_MIN_SNDBUF);
@@ -1758,10 +2084,14 @@
 static inline void tcp_writequeue_purge(struct sock *sk)
 {
 	struct sk_buff *skb;
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
+        BUG_TRAP(tp !=NULL);
+        if (tp !=NULL) clear_sacked_list (tp); //purge sacked_list
 	while ((skb = __skb_dequeue(&sk->write_queue)) != NULL)
 		tcp_free_skb(sk, skb);
 	tcp_mem_reclaim(sk);
+	
 }
 
 extern void tcp_rfree(struct sk_buff *skb);
Binary files linux-2.4.23/include/net/.tcp.h.swo and linux-2.4.23-web100-HSv4/include/net/.tcp.h.swo differ
diff -uwbrN --exclude=config.save linux-2.4.23/include/net/web100.h linux-2.4.23-web100-HSv4/include/net/web100.h
--- linux-2.4.23/include/net/web100.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/include/net/web100.h	2004-03-25 00:07:10.000000000 +0000
@@ -0,0 +1,122 @@
+/* 
+ *  include/net/web100.h
+ *  
+ * Copyright (C) 2001 Matt Mathis <mathis@psc.edu>
+ * Copyright (C) 2001 John Heffner <jheffner@psc.edu>
+ *
+ * The Web 100 project.  See http://www.web100.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _WEB100_H
+#define _WEB100_H
+
+#include <net/sock.h>
+
+#define WEB100_MAX_CONNS	(1<<15)
+
+#define WEB100_DELAY_MAX	HZ
+
+/* Netlink */
+#define WC_NL_TYPE_CONNECT	0
+#define WC_NL_TYPE_DISCONNECT	1
+
+struct web100_netlink_msg {
+	int type;
+	int cid;
+};
+
+/* The syntax of this version string is subject to future changes */
+extern char *web100_version_string;
+
+/* Stats structures */
+extern struct web100stats *web100stats_arr[];
+extern struct web100stats *web100stats_first;
+
+/* For locking the creation and destruction of stats structures. */
+extern rwlock_t web100_linkage_lock;
+
+/* For /proc/web100 */
+extern struct web100stats *web100stats_lookup(int cid);
+
+/* For the TCP code */
+extern int  web100_stats_create(struct sock *sk);
+extern void web100_stats_destroy(struct web100stats *stats);
+extern void web100_stats_free(struct web100stats *stats);
+extern void web100_stats_establish(struct sock *sk);
+
+extern void web100_tune_sndbuf_ack(struct sock *sk);
+extern void web100_tune_sndbuf_snd(struct sock *sk);
+extern void web100_tune_rcvbuf(struct sock *sk);
+
+extern void web100_update_snd_nxt(struct tcp_opt *tp);
+extern void web100_update_rtt(struct tcp_opt *tp, unsigned long rtt_sample);
+extern void web100_update_timeout(struct tcp_opt *tp);
+extern void web100_update_mss(struct tcp_opt *tp);
+extern void web100_update_cwnd(struct tcp_opt *tp);
+extern void web100_update_rwin_rcvd(struct tcp_opt *tp);
+extern void web100_update_sndlim(struct tcp_opt *tp, int why);
+extern void web100_update_rcv_nxt(struct tcp_opt *tp);
+extern void web100_update_rwin_sent(struct tcp_opt *tp);
+extern void web100_update_congestion(struct tcp_opt *tp, int why);
+extern void web100_update_segsend(struct tcp_opt *tp, struct sk_buff *skb);
+extern void web100_update_segrecv(struct tcp_opt *tp, struct sk_buff *skb);
+extern void web100_update_rcvbuf(struct sock *sk, int rcvbuf);
+extern void web100_update_writeq(struct sock *sk);
+extern void web100_update_recvq(struct sock *sk);
+extern void web100_update_ofoq(struct sock *sk);
+#ifdef CONFIG_WEB100_NET100
+extern void web100_update_floyd_aimd(struct tcp_opt *tp);
+#endif
+
+extern void web100_stats_init(void);
+
+/* For the IP code */
+extern int web100_delay_output(struct sk_buff *skb, int (*output)(struct sk_buff *));
+
+extern __u64 web100_mono_time(void);
+
+/* You may have to hold web100_linkage_lock here to prevent
+   stats from disappearing. */
+static inline void web100_stats_use(struct web100stats *stats)
+{
+	sock_hold(stats->wc_sk);
+	atomic_inc(&stats->wc_users);
+}
+
+/* You MUST NOT hold web100_linkage_lock here. */
+static inline void web100_stats_unuse(struct web100stats *stats)
+{
+	if (atomic_dec_and_test(&stats->wc_users)) {
+		struct sock *sk = stats->wc_sk;
+		web100_stats_free(stats);
+		sock_put(sk);
+	}
+}
+
+/* A mapping between Linux and Web100 states.  This could easily just
+ * be an array. */
+static inline int web100_state(int state)
+{
+	switch (state) {
+	case TCP_ESTABLISHED:	return WC_STATE_ESTABLISHED;
+	case TCP_SYN_SENT:	return WC_STATE_SYNSENT;
+	case TCP_SYN_RECV:	return WC_STATE_SYNRECEIVED;
+	case TCP_FIN_WAIT1:	return WC_STATE_FINWAIT1;
+	case TCP_FIN_WAIT2:	return WC_STATE_FINWAIT2;
+	case TCP_TIME_WAIT:	return WC_STATE_TIMEWAIT;
+	case TCP_CLOSE:		return WC_STATE_CLOSED;
+	case TCP_CLOSE_WAIT:	return WC_STATE_CLOSEWAIT;
+	case TCP_LAST_ACK:	return WC_STATE_LASTACK;
+	case TCP_LISTEN:	return WC_STATE_LISTEN;
+	case TCP_CLOSING:	return WC_STATE_CLOSING;
+	default:		return 0;
+	}
+}
+
+#endif /* _WEB100_H */
diff -uwbrN --exclude=config.save linux-2.4.23/include/net/web100_stats.h linux-2.4.23-web100-HSv4/include/net/web100_stats.h
--- linux-2.4.23/include/net/web100_stats.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/include/net/web100_stats.h	2004-03-25 00:05:41.000000000 +0000
@@ -0,0 +1,359 @@
+/*
+ * include/net/web100_stats.h
+ *
+ * Copyright (C) 2001 Matt Mathis <mathis@psc.edu>
+ * Copyright (C) 2001 John Heffner <jheffner@psc.edu>
+ * Copyright (C) 2000 Jeff Semke <semke@psc.edu>
+ *
+ * The Web 100 project.  See http://www.web100.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* TODO: make sure that the time duration states below include:
+   Congestion Avoidance, Slow Start, Timeouts, Idle Application, and
+   Window Limited cases */
+/* TODO: Consider adding sysctl variable to enable/disable WC stats updates.
+   Probably should still create stats structures if compiled with WC support,
+   even if sysctl(wc) is turned off.  That would allow the stats to be updated
+   if the sysctl(wc) is turned back on. */
+/* TODO: Add all variables needed to do user-level auto-tuning, including
+   writeable parameters */
+
+
+#ifndef _WEB100_STATS_H
+#define _WEB100_STATS_H
+
+enum wc_sndlim_states {
+	WC_SNDLIM_NONE = -1,
+	WC_SNDLIM_SENDER,
+	WC_SNDLIM_CWND,
+	WC_SNDLIM_RWIN,
+	WC_SNDLIM_STARTUP,
+	WC_SNDLIM_NSTATES	/* Keep at end */
+};
+
+#ifndef CONFIG_WEB100_STATS
+
+#define WEB100_VAR_INC(tp,var)		do {} while (0)
+#define WEB100_VAR_DEC(tp,var)		do {} while (0)
+#define WEB100_VAR_SET(tp,var,val)	do {} while (0)
+#define WEB100_VAR_ADD(tp,var,val)	do {} while (0)
+#define WEB100_UPDATE_FUNC(tp,func)	do {} while (0)
+#define NET100_WAD(tp, var, def)	(def)
+
+#else /* CONFIG_WEB100_STATS */ /* { */
+
+#include <linux/spinlock.h>
+
+#define WEB100_CHECK(tp,expr) \
+	do { if ((tp)->tcp_stats) (expr); } while (0)
+#define WEB100_VAR_INC(tp,var) \
+	WEB100_CHECK(tp, ((tp)->tcp_stats->wc_vars.var)++)
+#define WEB100_VAR_DEC(tp,var) \
+	WEB100_CHECK(tp, ((tp)->tcp_stats->wc_vars.var)--)
+#define WEB100_VAR_ADD(tp,var,val) \
+	WEB100_CHECK(tp, ((tp)->tcp_stats->wc_vars.var) += (val))
+#define WEB100_VAR_SET(tp,var,val) \
+	WEB100_CHECK(tp, ((tp)->tcp_stats->wc_vars.var) = (val))
+#define WEB100_UPDATE_FUNC(tp,func) \
+	WEB100_CHECK(tp, func)
+#ifdef CONFIG_WEB100_NET100
+#define NET100_WAD(tp, var, def) \
+	(((tp)->tcp_stats && (tp)->tcp_stats->wc_vars.var) ? (tp)->tcp_stats->wc_vars.var : (def))
+#else
+#define NET100_WAD(tp, var, def)	(def)
+#endif
+
+/* SMIv2 types - RFC 1902 */
+typedef __s32		INTEGER;
+typedef INTEGER		Integer32;
+typedef __u32		IpAddress;
+typedef __u32		Counter32;
+typedef __u32		Unsigned32;
+typedef Unsigned32	Gauge32;
+typedef __u32		TimeTicks;
+typedef __u64		Counter64;
+typedef __u32		Unsigned16;
+
+/* New inet address types specified in INET-ADDRESS-MIB */
+typedef Unsigned16	InetPortNumber;
+typedef enum {
+	WC_ADDRTYPE_UNKNOWN = 0,
+	WC_ADDRTYPE_IPV4,
+	WC_ADDRTYPE_IPV6,
+	WC_ADDRTYPE_DNS = 16
+} InetAddressType;
+typedef IpAddress	InetAddresIPv4;
+typedef struct {
+	__u8	addr[16];
+	__u8	type;
+} InetAddresIPv6;
+typedef union {
+	InetAddresIPv4	v4addr;
+	InetAddresIPv6	v6addr;
+} InetAddress;
+
+typedef enum {
+	truthValueTrue = 1,
+	truthValueFalse = 2
+} TruthValue;
+
+enum wc_states {
+	WC_STATE_CLOSED = 1,
+	WC_STATE_LISTEN,
+	WC_STATE_SYNSENT,
+	WC_STATE_SYNRECEIVED,
+	WC_STATE_ESTABLISHED,
+	WC_STATE_FINWAIT1,
+	WC_STATE_FINWAIT2,
+	WC_STATE_CLOSEWAIT,
+	WC_STATE_LASTACK,
+	WC_STATE_CLOSING,
+	WC_STATE_TIMEWAIT,
+	WC_STATE_DELETECB
+};
+
+enum wc_stunemodes {
+	WC_STUNEMODE_DEFAULT = 0,	/* OS native */
+	WC_STUNEMODE_SETSOCKOPT,	/* OS native setsockopt() */
+	WC_STUNEMODE_FIXED,		/* Manual via the web100 API */
+	WC_STUNEMODE_AUTO,
+	WC_STUNEMODE_EXP1,
+	WC_STUNEMODE_EXP2
+};
+
+enum wc_rtunemodes {
+	WC_RTUNEMODE_DEFAULT = 0,
+	WC_RTUNEMODE_SETSOCKOPT,
+	WC_RTUNEMODE_FIXED,
+	WC_RTUNEMODE_AUTO,
+	WC_RTUNEMODE_EXP1,
+	WC_RTUNEMODE_EXP2
+};
+
+enum wc_bufmodes {
+	WC_BUFMODE_OS = 0,
+	WC_BUFMODE_WEB100,
+};
+
+enum {
+	WC_SE_BELOW_DATA_WINDOW = 1,
+	WC_SE_ABOVE_DATA_WINDOW,
+	WC_SE_BELOW_ACK_WINDOW,
+	WC_SE_ABOVE_ACK_WINDOW,
+	WC_SE_BELOW_TSW_WINDOW,
+	WC_SE_ABOVE_TSW_WINDOW,
+	WC_SE_DATA_CHECKSUM
+};
+
+
+/*
+ * Variables that can be read and written directly.
+ * 
+ * Should contain most variables from TCP-KIS 0.1.  Commented feilds are
+ * either not implemented or have handlers and do not need struct storage.
+ */
+struct web100directs {
+	/* STATE */
+	INTEGER		State;
+	TruthValue	SACKEnabled;
+	TruthValue	TimestampsEnabled;
+	TruthValue	NagleEnabled;
+	TruthValue	ECNEnabled;
+	Integer32	SndWinScale;
+	Integer32	RcvWinScale;
+	
+	/* SYN OPTIONS */
+	INTEGER		ActiveOpen;
+     /* Gauge32		MSSSent; */
+	Gauge32		MSSRcvd;
+	Integer32	WinScaleRcvd;
+	Integer32	WinScaleSent;
+     /* INTEGER		SACKokSent; */
+     /* INTEGER		SACKokRcvd; */
+     /* INTEGER		TimestampSent; */
+     /* INTEGER		TimestampRcvd; */
+	
+	/* DATA */
+	Counter32	PktsOut;
+	Counter32	DataPktsOut;
+	Counter32	AckPktsOut;		/* DEPRICATED */
+	Counter64	DataBytesOut;
+	Counter32	PktsIn;
+	Counter32	DataPktsIn;
+	Counter32	AckPktsIn;		/* DEPRICATED */
+	Counter64	DataBytesIn;
+     /* Counter32	SoftErrors; */
+     /* INTEGER		SoftErrorReason; */
+	Counter32	SndUna;
+	Counter32	SndNxt;
+	Integer32	SndMax;
+	Counter64	ThruBytesAcked;
+	Counter32	SndISS;			/* SndInitial */
+	Counter32	SendWraps;		/* DEPRICATED */
+	Counter32	RcvNxt;
+	Counter64	ThruBytesReceived;
+	Counter32	RecvISS;		/* RecInitial */
+	Counter32	RecvWraps;		/* DEPRICATED */
+     /* Counter64	Duration; */
+	Integer32	StartTime;		/* DEPRICATED */
+	Integer32	StartTimeSec;
+	Integer32	StartTimeUsec;
+        Integer32       Bi; /* goodput in packets/sec */
+
+	/* SENDER CONGESTION */
+	Counter32	SndLimTrans[WC_SNDLIM_NSTATES];
+	Counter32	SndLimTime[WC_SNDLIM_NSTATES];
+	Counter64	SndLimBytes[WC_SNDLIM_NSTATES];
+	Counter32	SlowStart;
+	Counter32	CongAvoid;
+	Counter32	CongestionSignals;
+	Counter32	OtherReductions;
+	Counter32	X_OtherReductionsCV;
+	Counter32	X_OtherReductionsCM;
+	Counter32	CongestionOverCount;
+	Gauge32		CurCwnd;
+	Gauge32		MaxCwnd;
+     /* Gauge32		LimCwnd; */
+	Gauge32		CurSsthresh;
+	Gauge32		MaxSsthresh;
+	Gauge32		MinSsthresh;
+
+	/* SENDER PATH MODEL */
+	Counter32	FastRetran;
+	Counter32	Timeouts;
+	Counter32	SubsequentTimeouts;
+	Gauge32		CurTimeoutCount;
+	Counter32	AbruptTimeouts;
+	Counter32	PktsRetrans;
+	Counter32	BytesRetrans;
+	Counter32	DupAcksIn;
+	Counter32	SACKsRcvd;
+     	Counter32	SACKBlocksRcvd;
+     	Counter32	PreCongSumCwnd;
+	Counter32	PreCongSumRTT;
+	Counter32	PreCongCountRTT;	/* DEPRICATED */
+	Counter32	PostCongSumRTT;
+	Counter32	PostCongCountRTT;
+     /* Counter32	ECNsignals; */
+	Counter32	ECERcvd;
+	Counter32	SendStall;
+	Counter32	QuenchRcvd;
+	Gauge32		RetranThresh;
+     /* Counter32	SndDupAckEpisodes; */
+     /* Counter64	SumBytesReordered; */
+	Counter32	NonRecovDA;
+	Counter32	AckAfterFR;
+	Counter32	DSACKDups;
+	Gauge32		SampleRTT;
+	Gauge32		SmoothedRTT;
+	Gauge32		RTTVar;
+	Gauge32		MaxRTT;
+	Gauge32		MinRTT;
+	Counter64	SumRTT;
+	Counter32	CountRTT;
+	Gauge32		CurRTO;
+	Gauge32		MaxRTO;
+	Gauge32		MinRTO;
+	Gauge32		CurMSS;
+	Gauge32		MaxMSS;
+	Gauge32		MinMSS;
+
+	/* LOCAL SENDER BUFFER */
+	Gauge32		CurRetxQueue;
+	Gauge32		MaxRetxQueue;
+	Gauge32		CurAppWQueue;
+	Gauge32		MaxAppWQueue;
+
+	/* LOCAL RECEIVER */
+	Gauge32		CurRwinSent;
+	Gauge32		MaxRwinSent;
+	Gauge32		MinRwinSent;
+	Integer32	LimRwin;
+     /* Counter32	DupAckEpisodes; */
+	Counter32	DupAcksOut;
+     /* Counter32	CERcvd; */
+     /* Counter32	ECNSent; */
+     /* Counter32	ECNNonceRcvd; */
+	Gauge32		CurReasmQueue;
+	Gauge32		MaxReasmQueue;
+	Gauge32		CurAppRQueue;
+	Gauge32		MaxAppRQueue;
+	Gauge32		X_rcv_ssthresh;
+	Gauge32		X_wnd_clamp;
+	Gauge32		X_dbg1;
+	Gauge32		X_dbg2;
+	Gauge32		X_dbg3;
+	Gauge32		X_dbg4;
+
+	/* OBSERVED RECEIVER */
+	Gauge32		CurRwinRcvd;
+	Gauge32		MaxRwinRcvd;
+	Gauge32		MinRwinRcvd;
+
+	/* CONNECTION ID */
+	InetAddressType	LocalAddressType;
+	InetAddress	LocalAddress;
+	InetPortNumber	LocalPort;
+     /* InetAddressType	RemAddressType;	*/
+	InetAddress	RemAddress;
+	InetPortNumber	RemPort;
+     /* Integer32	IdId; */
+	
+	Gauge32		X_RcvRTT;
+	
+	INTEGER		X_SBufMode;
+	INTEGER		X_RBufMode;
+	
+#ifdef CONFIG_WEB100_NET100
+	/* support for the NET100 Work Around Deamon (WAD) */
+	Gauge32		WAD_IFQ;
+	Gauge32		WAD_MaxBurst;
+	Gauge32         WAD_MaxSsthresh;
+	Gauge32         WAD_FloydAIMD;
+	Gauge32		WAD_AI;
+	Gauge32		WAD_MD;
+	INTEGER		WAD_NoAI;
+	Integer32	WAD_CwndAdjust;
+	Gauge32		WAD_Kaicnt;
+#endif
+};
+
+struct web100stats {
+	int			wc_cid;
+	
+	struct sock		*wc_sk;
+	
+	atomic_t		wc_users;
+	__u8			wc_dead;
+	
+	struct web100stats	*wc_next;
+	struct web100stats	*wc_prev;
+	
+	struct web100stats	*wc_hash_next;
+	struct web100stats	*wc_hash_prev;
+	
+	struct web100stats	*wc_death_next;
+	
+	int			wc_limstate;
+	__u64			wc_limstate_bytes;
+	struct timeval		wc_limstate_time;
+	
+	__u64			wc_start_monotime;
+	
+	int			wc_lss_k;
+	int			wc_lss_cnt1;
+	int			wc_lss_cnt2;
+	int			wc_flindex;
+	
+	struct web100directs	wc_vars;
+};
+
+#endif /* CONFIG_WEB100_STATS */ /* } */
+
+#endif		/*_WEB100_STATS_H */
diff -uwbrN --exclude=config.save linux-2.4.23/kernel/printk.c linux-2.4.23-web100-HSv4/kernel/printk.c
--- linux-2.4.23/kernel/printk.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/kernel/printk.c	2004-03-01 01:36:44.000000000 +0000
@@ -35,9 +35,9 @@
 #elif defined(CONFIG_ARCH_S390)
 #define LOG_BUF_LEN	(131072)
 #elif defined(CONFIG_SMP)
-#define LOG_BUF_LEN	(32768)
+#define LOG_BUF_LEN	(2097152)
 #else	
-#define LOG_BUF_LEN	(16384)			/* This must be a power of two */
+#define LOG_BUF_LEN	(2097152)			/* This must be a power of two */
 #endif
 #else /* CONFIG_LOG_BUF_SHIFT */
 #define LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
diff -uwbrN --exclude=config.save linux-2.4.23/Makefile linux-2.4.23-web100-HSv4/Makefile
--- linux-2.4.23/Makefile	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/Makefile	2004-03-01 02:20:28.000000000 +0000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 23
-EXTRAVERSION =
+EXTRAVERSION = -web100-HSv4
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -uwbrN --exclude=config.save linux-2.4.23/net/core/dev.c linux-2.4.23-web100-HSv4/net/core/dev.c
--- linux-2.4.23/net/core/dev.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/core/dev.c	2004-03-01 01:57:47.000000000 +0000
@@ -119,7 +119,7 @@
 /* Setting this will sample the queue lengths and thus congestion
  * via a timer instead of as each packet is received.
  */
-#undef OFFLINE_SAMPLE
+#define OFFLINE_SAMPLE
 
 NET_PROFILE_DEFINE(dev_queue_xmit)
 NET_PROFILE_DEFINE(softnet_process)
@@ -1130,7 +1130,7 @@
 			Receiver routines
   =======================================================================*/
 
-int netdev_max_backlog = 300;
+int netdev_max_backlog = 2500;
 int weight_p = 64;            /* old backlog weight */
 /* These numbers are selected based on intuition and some
  * experimentatiom, if you have more scientific way of doing this
@@ -1291,8 +1291,10 @@
 	netdev_rx_stat[this_cpu].total++;
 	if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
 		if (queue->input_pkt_queue.qlen) {
-			if (queue->throttle)
-				goto drop;
+                        // just drop packets on queue overflow
+                        // - disable throttle action
+ 			//if (queue->throttle)
+			//	goto drop;
 
 enqueue:
 			dev_hold(skb->dev);
diff -uwbrN --exclude=config.save linux-2.4.23/net/core/sock.c linux-2.4.23-web100-HSv4/net/core/sock.c
--- linux-2.4.23/net/core/sock.c	2003-06-13 15:51:39.000000000 +0100
+++ linux-2.4.23-web100-HSv4/net/core/sock.c	2004-03-01 01:37:29.000000000 +0000
@@ -231,6 +231,13 @@
 				sk->sndbuf = SOCK_MIN_SNDBUF;
 			else
 				sk->sndbuf = (val * 2);
+#ifdef CONFIG_WEB100_STATS
+			if (sk->protocol == IPPROTO_TCP &&
+			    sysctl_web100_sbufmode && sysctl_web100_sndbuf_emu) {
+				sk->tp_pinfo.af_tcp.snd_cwnd_clamp =
+				  min_t(u32, val / sk->tp_pinfo.af_tcp.mss_cache, 65535U);
+			}
+#endif
 
 			/*
 			 *	Wake up sending tasks if we
@@ -254,6 +261,13 @@
 				sk->rcvbuf = SOCK_MIN_RCVBUF;
 			else
 				sk->rcvbuf = (val * 2);
+#ifdef CONFIG_WEB100_STATS
+			if (sk->protocol == IPPROTO_TCP &&
+			    sysctl_web100_rbufmode && sysctl_web100_rcvbuf_emu) {
+				sk->tp_pinfo.af_tcp.window_clamp =
+				  min(val, 65535 << sk->tp_pinfo.af_tcp.rcv_wscale);
+			}
+#endif
 			break;
 
 		case SO_KEEPALIVE:
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/Config.in linux-2.4.23-web100-HSv4/net/ipv4/Config.in
--- linux-2.4.23/net/ipv4/Config.in	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/ipv4/Config.in	2004-03-01 01:37:30.000000000 +0000
@@ -41,8 +41,19 @@
 bool '  IP: TCP Explicit Congestion Notification support' CONFIG_INET_ECN
 bool '  IP: TCP syncookie support (disabled per default)' CONFIG_SYN_COOKIES
 if [ "$CONFIG_NETFILTER" != "n" ]; then
-   source net/ipv4/netfilter/Config.in
-fi
-if [ "$CONFIG_NETFILTER" != "n" ]; then
    source net/ipv4/ipvs/Config.in
 fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   bool '  Web100 networking enhancements' CONFIG_WEB100
+   if [ "$CONFIG_WEB100" = "y" ]; then
+      bool '    Web100: TCP statistics' CONFIG_WEB100_STATS
+      if [ "$CONFIG_WEB100_STATS" = "y" ]; then
+          int  '    Web100: Default file permissions' CONFIG_WEB100_FPERMS 0666
+          int  '    Web100: Default gid' CONFIG_WEB100_GID 0
+          bool '    Web100: Net100 extensions' CONFIG_WEB100_NET100
+          # Netlink is always enabled now.
+          bool '    Web100: netlink event notification service' CONFIG_WEB100_NETLINK
+      fi
+      int '    Web100: default winscale initial value' CONFIG_WEB100_WINSCALE_VAL 7
+   fi
+fi
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/Makefile linux-2.4.23-web100-HSv4/net/ipv4/Makefile
--- linux-2.4.23/net/ipv4/Makefile	2001-12-21 17:42:05.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/ipv4/Makefile	2004-03-01 01:37:30.000000000 +0000
@@ -25,5 +25,6 @@
 obj-$(CONFIG_NET_IPGRE) += ip_gre.o
 obj-$(CONFIG_SYN_COOKIES) += syncookies.o
 obj-$(CONFIG_IP_PNP) += ipconfig.o
+obj-$(CONFIG_WEB100_STATS) += web100_stats.o
 
 include $(TOPDIR)/Rules.make
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/sysctl_net_ipv4.c linux-2.4.23-web100-HSv4/net/ipv4/sysctl_net_ipv4.c
--- linux-2.4.23/net/ipv4/sysctl_net_ipv4.c	2003-06-13 15:51:39.000000000 +0100
+++ linux-2.4.23-web100-HSv4/net/ipv4/sysctl_net_ipv4.c	2004-03-25 00:00:03.000000000 +0000
@@ -229,6 +229,46 @@
 	{NET_IPV4_IPFRAG_SECRET_INTERVAL, "ipfrag_secret_interval",
 	 &sysctl_ipfrag_secret_interval, sizeof(int), 0644, NULL, &proc_dointvec_jiffies, 
 	 &sysctl_jiffies},
+	{NET_TCP_HTCP, "tcp_htcp",
+	 &sysctl_tcp_htcp, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_TCP_HTCP_MODESWITCH, "tcp_htcp_modeswitch",
+	 &sysctl_tcp_htcp_modeswitch, sizeof(int), 0644, NULL, &proc_dointvec},
+        {NET_TCP_HTCP_UNDO, "tcp_htcp_undo",
+         &sysctl_tcp_htcp_undo, sizeof(int), 0644, NULL, &proc_dointvec},
+        {NET_TCP_HTCP_MODERATION, "tcp_htcp_moderation",
+         &sysctl_tcp_htcp_moderation, sizeof(int), 0644, NULL, &proc_dointvec},
+        {NET_TCP_HTCP_CAP, "tcp_htcp_cap",
+         &sysctl_tcp_htcp_cap, sizeof(int), 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_WEB100
+	{NET_IPV4_WEB100_DEFAULT_WSCALE, "web100_default_wscale",
+	 &sysctl_web100_default_wscale, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_WEB100_NET100
+	{NET_IPV4_WEB100_NO_METRICS_SAVE, "web100_no_metrics_save",
+	 &sysctl_web100_no_metrics_save, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WAD_IFQ, "WAD_IFQ",
+	 &sysctl_WAD_IFQ, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WAD_MAX_BURST, "WAD_MaxBurst",
+	 &sysctl_WAD_MaxBurst, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WAD_FLOYD_AIMD, "WAD_FloydAIMD",
+	 &sysctl_WAD_FloydAIMD, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WEB100_SCALABLE_TCP, "web100_scalable_tcp",
+	 &sysctl_web100_scalable_tcp, sizeof (int), 0644, NULL, &proc_dointvec},
+#endif
+#ifdef CONFIG_WEB100_STATS
+	{NET_IPV4_WEB100_SBUFMODE, "web100_sbufmode",
+	 &sysctl_web100_sbufmode, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WEB100_RBUFMODE, "web100_rbufmode",
+	 &sysctl_web100_rbufmode, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WEB100_FPERMS, "web100_fperms",
+	 &sysctl_web100_fperms, sizeof(int), 0644, NULL, &web100_proc_dointvec_update},
+	{NET_IPV4_WEB100_GID, "web100_gid",
+	 &sysctl_web100_gid, sizeof(int), 0644, NULL, &web100_proc_dointvec_update},
+	{NET_IPV4_WEB100_SNDBUF_EMU, "web100_sndbuf_emu",
+	 &sysctl_web100_sndbuf_emu, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_IPV4_WEB100_RCVBUF_EMU, "web100_rcvbuf_emu",
+	 &sysctl_web100_rcvbuf_emu, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
 	{0}
 };
 
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/tcp.c linux-2.4.23-web100-HSv4/net/ipv4/tcp.c
--- linux-2.4.23/net/ipv4/tcp.c	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.23-web100-HSv4/net/ipv4/tcp.c	2004-03-18 08:57:22.000000000 +0000
@@ -277,6 +277,30 @@
 atomic_t tcp_memory_allocated;	/* Current allocated memory. */
 atomic_t tcp_sockets_allocated;	/* Current number of TCP sockets. */
 
+#ifdef CONFIG_WEB100_STATS
+atomic_t tcp_retx_mem;
+atomic_t tcp_rwin_announced;
+#endif
+
+#ifdef CONFIG_WEB100
+int sysctl_web100_default_wscale = CONFIG_WEB100_WINSCALE_VAL;
+#endif
+#ifdef CONFIG_WEB100_NET100
+int sysctl_web100_no_metrics_save = 0;
+int sysctl_WAD_IFQ = 1;
+int sysctl_WAD_MaxBurst = 3;
+int sysctl_WAD_FloydAIMD = 0;
+int sysctl_web100_scalable_tcp = 0;
+#endif
+#ifdef CONFIG_WEB100_STATS
+int sysctl_web100_sbufmode = 0;
+int sysctl_web100_rbufmode = 0;
+int sysctl_web100_fperms = CONFIG_WEB100_FPERMS;
+int sysctl_web100_gid = CONFIG_WEB100_GID;
+int sysctl_web100_sndbuf_emu = 1;
+int sysctl_web100_rcvbuf_emu = 1;
+#endif
+
 /* Pressure flag: try to collapse.
  * Technical note: it is used by multiple contexts non atomically.
  * All the tcp_mem_schedule() is of this nature: accounting
@@ -681,6 +705,12 @@
 
 static inline int tcp_memory_free(struct sock *sk)
 {
+#ifdef CONFIG_WEB100_STATS
+	return sk->wmem_queued < sk->sndbuf &&
+		   (sysctl_web100_sbufmode != 1 ||
+			sk->wmem_queued / PAGE_SIZE + 1 < sysctl_tcp_mem[0] -
+			 atomic_read(&tcp_memory_allocated));
+#endif
 	return sk->wmem_queued < sk->sndbuf;
 }
 
@@ -921,8 +951,12 @@
 	}
 
 out:
-	if (copied)
+	if (copied) {
 		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
+#ifdef CONFIG_WEB100_STATS
+		web100_update_writeq(sk);
+#endif
+	}
 	return copied;
 
 do_error:
@@ -1171,6 +1205,12 @@
 				__tcp_push_pending_frames(sk, tp, mss_now, 1);
 			} else if (skb == tp->send_head)
 				tcp_push_one(sk, mss_now);
+			
+#if 0
+			/* why is this here? WEB100_XXX */
+			WEB100_UPDATE_FUNC(tp, web100_update_cwnd(tp));
+#endif
+			
 			continue;
 
 wait_for_sndbuf:
@@ -1187,8 +1227,12 @@
 	}
 
 out:
-	if (copied)
+	if (copied) {
 		tcp_push(sk, tp, flags, mss_now, tp->nonagle);
+#ifdef CONFIG_WEB100_STATS
+		web100_update_writeq(sk);
+#endif
+	}
 	TCP_CHECK_TIMER(sk);
 	release_sock(sk);
 	return copied;
@@ -1545,6 +1589,9 @@
 			BUG_TRAP(flags&MSG_PEEK);
 			skb = skb->next;
 		} while (skb != (struct sk_buff *)&sk->receive_queue);
+#ifdef CONFIG_WEB100_STATS
+		web100_update_recvq(sk);
+#endif
 
 		/* Well, if we have backlog, try to process it now yet. */
 
@@ -2282,6 +2329,7 @@
 			err = -EINVAL;
 			break;
 		}
+		WEB100_VAR_SET(tp, NagleEnabled, !val);
 		tp->nonagle = (val == 0) ? 0 : 1;
 		if (val)
 			tcp_push_pending_frames(sk, tp);
@@ -2303,6 +2351,7 @@
 			err = -EINVAL;
 			break;
 		}
+		WEB100_VAR_SET(tp, NagleEnabled, !val);
 		if (val != 0) {
 			tp->nonagle = 2;
 		} else {
@@ -2645,4 +2694,8 @@
 
 	(void) tcp_mib_init();
 	tcpdiag_init();
+	
+#ifdef CONFIG_WEB100_STATS
+	web100_stats_init();
+#endif
 }
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/tcp_input.c linux-2.4.23-web100-HSv4/net/ipv4/tcp_input.c
--- linux-2.4.23/net/ipv4/tcp_input.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/ipv4/tcp_input.c	2004-03-25 01:23:09.000000000 +0000
@@ -61,6 +61,7 @@
  *		Panu Kuhlberg:		Experimental audit of TCP (re)transmission
  *					engine. Lots of bugs are found.
  *		Pasi Sarolahti:		F-RTO for dealing with spurious RTOs
+*       Doug Leith:  Fixes for high-speed operation, plus HTCP implementation.
  */
 
 #include <linux/config.h>
@@ -88,6 +89,11 @@
 int sysctl_tcp_rfc1337 = 0;
 int sysctl_tcp_max_orphans = NR_FILE;
 int sysctl_tcp_frto = 0;
+int sysctl_tcp_htcp=1;
+int sysctl_tcp_htcp_modeswitch=1;
+int sysctl_tcp_htcp_undo=1;
+int sysctl_tcp_htcp_moderation=1;
+int sysctl_tcp_htcp_cap=1;
 
 #define FLAG_DATA		0x01 /* Incoming frame contained data.		*/
 #define FLAG_WIN_UPDATE		0x02 /* Incoming ACK was a window update.	*/
@@ -288,11 +294,20 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int maxwin;
 
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_rbufmode != 1)
+#endif
 	if (!(sk->userlocks&SOCK_RCVBUF_LOCK))
 		tcp_fixup_rcvbuf(sk);
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_sbufmode != 1)
+#endif
 	if (!(sk->userlocks&SOCK_SNDBUF_LOCK))
 		tcp_fixup_sndbuf(sk);
 
+#ifdef CONFIG_WEB100_STATS
+	tp->rcv_space = tp->rcv_wnd;
+#endif
 	maxwin = tcp_full_space(sk);
 
 	if (tp->window_clamp >= maxwin) {
@@ -349,6 +364,108 @@
 	}
 }
 
+#ifdef CONFIG_WEB100_STATS
+/* Receiver "autotuning" code.
+ *
+ * Note that some of these algorithms are based on or similar to
+ * Dynamic Right-Sizing (DRS) by Wu Feng and Mike Fisk of LANL.
+ * <http://www.lanl.gov/radiant/website/pubs/drs/lacsi2001.ps>
+ *
+ * Details on this code can be found at
+ * <http://www.psc.edu/~jheffner/senior_thesis.ps>
+ */
+
+static inline void tcp_rcv_rtt_update(struct tcp_opt *tp, __u32 rcv_rtt, int win_dep)
+{
+	rcv_rtt++;  /* Truncated, round up. */
+	if (tp->rcv_rtt == 0)
+		tp->rcv_rtt = rcv_rtt;  /* First measurement */
+	else if (rcv_rtt < tp->rcv_rtt || win_dep)
+		tp->rcv_rtt = min(tp->rcv_rtt, rcv_rtt);
+	else
+		tp->rcv_rtt = (7 * tp->rcv_rtt + rcv_rtt) / 8;
+	
+	WEB100_VAR_SET(tp, X_RcvRTT, tp->rcv_rtt * 1000000 / HZ);
+}
+
+static inline void tcp_rcv_rtt_measure(struct tcp_opt *tp)
+{
+	if (tp->rcv_rtt_time == 0)
+		goto new_measure;
+	
+	if (before(tp->rcv_nxt, tp->rcv_rtt_seq))
+		return;
+	
+	tcp_rcv_rtt_update(tp, jiffies - tp->rcv_rtt_time, 1);
+	
+new_measure:
+	tp->rcv_rtt_seq = tp->rcv_nxt + tp->rcv_wnd;
+	tp->rcv_rtt_time = jiffies;
+}
+
+static inline void tcp_rcv_win_to_space(struct tcp_opt *tp, int win)
+{
+	win = max(2*win, (int)tp->rcv_space/2);
+	tp->rcv_space = max(win, 6*tp->advmss);
+}
+
+static inline void tcp_rcv_space_adjust(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	int time;
+	
+	if (tp->rcv_winest_time == 0)
+		goto new_measure;
+	
+	time = jiffies - tp->rcv_winest_time;
+	if (time < tp->rcv_rtt || tp->rcv_rtt == 0)
+		return;
+	
+	tcp_rcv_win_to_space(tp, tp->rcv_hi_seq - tp->rcv_winest_seq);
+	
+new_measure:
+	tp->rcv_winest_seq = tp->rcv_hi_seq;
+	tp->rcv_winest_time = jiffies;
+}
+
+/* Conclude a receiver-side window measurement using timestamps */
+static inline void tcp_rcv_tswin_measure(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
+	if (tp->rcv_tswin_pending &&
+		!after(tp->rcv_tswin_tstamp, tp->rcv_tsecr)) {
+		tp->rcv_tswin_pending = 0;
+		tcp_rcv_win_to_space(tp, tp->rcv_hi_seq - tp->rcv_tswin_seq);
+	}
+}
+
+/* Call this every time we get an acceptable segment (checksum ok, in window),
+   and we are in the ESTABLISHED state when it arrives. */
+static void tcp_good_seg_rcvd(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
+	if (TCP_SKB_CB(skb)->end_seq == 0) {
+#if 0
+		printk("tcp_good_seg_rcvd: end_seq == 0\n");
+#endif
+		return;
+	}
+	else if (after(TCP_SKB_CB(skb)->end_seq, tp->rcv_hi_seq))
+		tp->rcv_hi_seq = TCP_SKB_CB(skb)->end_seq;
+	web100_update_recvq(sk);
+	web100_update_ofoq(sk);
+	
+	tcp_rcv_rtt_measure(tp);
+	
+	if (tp->tstamp_ok)
+		tcp_rcv_tswin_measure(sk);
+	else
+		tcp_rcv_space_adjust(sk);
+}
+#endif /* CONFIG_WEB100_STATS */
+
 /* There is something which you must keep in mind when you analyze the
  * behavior of the tp->ato delayed ack timeout interval.  When a
  * connection starts up, we want to ack as quickly as possible.  The
@@ -399,6 +516,7 @@
 
 	if (skb->len >= 128)
 		tcp_grow_window(sk, tp, skb);
+	WEB100_UPDATE_FUNC(tp, web100_update_rcv_nxt(tp));
 }
 
 /* Called to compute a smoothed rtt estimate. The data fed to this
@@ -421,7 +539,7 @@
 	 *	m stands for "measurement".
 	 *
 	 *	On a 1990 paper the rto value is changed to:
-	 *	RTO = rtt + 4 * mdev
+	 *  RTO = rtt +  mdev 
 	 *
 	 * Funny. This algorithm seems to be very broken.
 	 * These formulae increase RTO, when it should be decreased, increase
@@ -514,6 +632,11 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct dst_entry *dst = __sk_dst_get(sk);
 
+#ifdef CONFIG_WEB100_NET100
+	if (sysctl_web100_no_metrics_save)
+		return;
+#endif
+
 	dst_confirm(dst);
 
 	if (dst && (dst->flags&DST_HOST)) {
@@ -637,6 +760,7 @@
 	if (dst->reordering && tp->reordering != dst->reordering) {
 		tp->sack_ok &= ~2;
 		tp->reordering = dst->reordering;
+		WEB100_VAR_SET(tp, RetranThresh, tp->reordering);
 	}
 
 	if (dst->rtt == 0)
@@ -689,6 +813,7 @@
 {
 	if (metric > tp->reordering) {
 		tp->reordering = min(TCP_MAX_REORDERING, metric);
+		WEB100_VAR_SET(tp, RetranThresh, tp->reordering);
 
 		/* This exciting event is worth to be remembered. 8) */
 		if (ts)
@@ -710,6 +835,16 @@
 	}
 }
 
+static inline int tcp_skb_timedout(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	return (tcp_time_stamp - TCP_SKB_CB(skb)->when > tp->rto);
+}
+
+static inline int tcp_head_timedout(struct sock *sk, struct tcp_opt *tp)
+{
+	return tp->packets_out && tcp_skb_timedout(tp, skb_peek(&sk->write_queue));
+}
+
 /* This procedure tags the retransmission queue when SACKs arrive.
  *
  * We have three tag bits: SACKED(S), RETRANS(R) and LOST(L).
@@ -758,6 +893,101 @@
  * Both of these heuristics are not used in Loss state, when we cannot
  * account for retransmits accurately.
  */
+ 
+/* tcp_sacktag_write_queue rewritten to use a avoid a walk of entire write queue every ack.  For large
+ * window sizes, this is too slow.  The new code restricts the walk to only those packets on the write 
+ * queue which have not yet been sacked (these are the only packets that require attention here) 
+ * and so computation scales as o(number of lost packets) rather than o(cwnd)  - DL
+ *
+ * Sack caching used when under pressure - DL  
+ *
+ * Kelly style hints added to dupsack processing and retransmission algorithm -DL
+ */
+
+static int slow_sacktag_write_queue(struct sock *sk, u32 dup_start_seq, u32 dup_end_seq)
+{
+   // Old style full walk of write_queue - costly.
+        struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+        int flag = 0;
+        u8 sacked;
+        int in_sack;
+
+        struct sk_buff *skb;
+
+        if (tp->dupsack_skb_hint && !after(TCP_SKB_CB(tp->dupsack_skb_hint)->seq,dup_start_seq) ) {
+           HTCP_PRINTK1("dupsack fast path ...");
+           skb= tp->dupsack_skb_hint;
+        } else
+           skb=(sk)->write_queue.next;
+
+//      for_retrans_queue(skb, sk,tp) {
+        for (;
+            (skb != (tp)->send_head) &&
+            (skb != (struct sk_buff *)&(sk)->write_queue);
+            skb=skb->next) {
+
+              tp->dupsack_skb_hint = skb;
+
+              if (!before(TCP_SKB_CB(skb)->seq, dup_end_seq))
+                 break;
+
+              sacked = TCP_SKB_CB(skb)->sacked;
+              in_sack = !after(dup_start_seq, TCP_SKB_CB(skb)->seq) &&
+                        !before(dup_end_seq, TCP_SKB_CB(skb)->end_seq);
+
+              /* Account D-SACK for retransmitted packet. */
+              if (in_sack &&
+                  (sacked & TCPCB_RETRANS) &&
+                  after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker)) {
+                      if(tp->undo_retrans) tp->undo_retrans--;
+              }
+
+              /* The frame is ACKed. */
+              if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) {
+                      /* Nothing to do; acked frame is about to be dropped. */
+                      break;
+              }
+
+              if (!in_sack)
+                 continue;
+
+              if (!(sacked&TCPCB_SACKED_ACKED) ) {
+                 if (sacked & TCPCB_SACKED_RETRANS) {
+                    /* If the segment is not tagged as lost,
+                     * we do not clear RETRANS, believing
+                     * that retransmission is still in flight.
+                     */
+
+                    if (sacked & TCPCB_LOST) {
+                       TCP_SKB_CB(skb)->sacked &= ~(TCPCB_LOST|TCPCB_SACKED_RETRANS);
+                       tp->lost_out--;
+                       tp->retrans_out--;
+                       tp->xmit_retransmit_queue_skb_hint=NULL;
+                     }
+                 } else {
+                    if (sacked & TCPCB_LOST) {
+                       TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
+                       tp->lost_out--;
+                       tp->xmit_retransmit_queue_skb_hint=NULL;
+                     }
+                 }
+
+                 TCP_SKB_CB(skb)->markedlost &= ~ TCPCB_MARKEDLOST;
+                 TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
+                 flag |= FLAG_DATA_SACKED;
+                 tp->sacked_out++;
+              }
+
+              if (TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_RETRANS) {
+                      TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
+                      BUG_TRAP(tp->retrans_out!=0);
+                      if(tp->retrans_out) tp->retrans_out--;
+              }
+           }
+
+           return flag;
+}
+
 static int
 tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_una)
 {
@@ -765,105 +995,181 @@
 	unsigned char *ptr = ack_skb->h.raw + TCP_SKB_CB(ack_skb)->sacked;
 	struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2);
 	int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE)>>3;
-	int reord = tp->packets_out;
 	int prior_fackets;
 	u32 lost_retrans = 0;
 	int flag = 0;
-	int i;
+	int i; int j;
+	__u32 start_seq[SACK_CACHE_SIZE+3];
+	__u32 end_seq[SACK_CACHE_SIZE+3];
+        __u32 dup_start_seq;
+        __u32 dup_end_seq;
+	__u32 temp;
+        int dup_sack=0;
+	u32 ack = TCP_SKB_CB(ack_skb)->ack_seq;
+	u8 sacked;
+        int in_sack;
+        struct sk_buff *sl;
+        struct sk_buff *skb;
+        int packet_cnt=0;
+
+        if (tcp_packets_in_flight(tp) <= tp->snd_cwnd)
+           packet_cnt = max(tcp_max_burst(tp), (__u32) tp->sack_cache.ack_count<<1);
+        if (!sysctl_tcp_htcp_moderation) packet_cnt=tp->snd_cwnd; //use original form of moderation
+        tp->retrans_this_ack=0;
+
+	HTCP_PRINTK1("sacktag_write_queue ...");
 
 	if (!tp->sacked_out)
 		tp->fackets_out = 0;
 	prior_fackets = tp->fackets_out;
 
+	WEB100_VAR_INC(tp, SACKsRcvd);
+	WEB100_VAR_ADD(tp, SACKBlocksRcvd, num_sacks);
+
+	/* Eliminate too old ACKs, but take into
+		* account more or less fresh ones, they can
+		* contain valid SACK info.
+		*/
+	if (before(ack, prior_snd_una-tp->max_window)) {
+		HTCP_PRINTK1("old ACK\n");
+		return 0;
+	}
+
 	for (i=0; i<num_sacks; i++, sp++) {
-		struct sk_buff *skb;
-		__u32 start_seq = ntohl(sp->start_seq);
-		__u32 end_seq = ntohl(sp->end_seq);
-		int fack_count = 0;
-		int dup_sack = 0;
+           start_seq[i] = ntohl(sp->start_seq);
+           end_seq[i] = ntohl(sp->end_seq);
+        }
+        for (i=0; i<tp->sack_cache.num; i++) {
+           start_seq[i+num_sacks] = tp->sack_cache.start_seq[i];
+           end_seq[i+num_sacks] = tp->sack_cache.end_seq[i];
+        }	
 
 		/* Check for D-SACK. */
-		if (i == 0) {
-			u32 ack = TCP_SKB_CB(ack_skb)->ack_seq;
-
-			if (before(start_seq, ack)) {
+        dup_start_seq=start_seq[0];
+        dup_end_seq=end_seq[0];
+        if (before(start_seq[0], ack)) {
 				dup_sack = 1;
 				tp->sack_ok |= 4;
 				NET_INC_STATS_BH(TCPDSACKRecv);
 			} else if (num_sacks > 1 &&
-				   !after(end_seq, ntohl(sp[1].end_seq)) &&
-				   !before(start_seq, ntohl(sp[1].start_seq))) {
+                   !after(end_seq[0], end_seq[1]) &&
+                   !before(start_seq[0], start_seq[1])) {
 				dup_sack = 1;
 				tp->sack_ok |= 4;
 				NET_INC_STATS_BH(TCPDSACKOfoRecv);
 			}
 
+        if (dup_sack) {
+                WEB100_VAR_INC(tp, DSACKDups);
+                HTCP_PRINTK1("DSACK...");
+        }
+
+        num_sacks = num_sacks+tp->sack_cache.num;
+
 			/* D-SACK for already forgotten data...
 			 * Do dumb counting. */
 			if (dup_sack &&
-			    !after(end_seq, prior_snd_una) &&
-			    after(end_seq, tp->undo_marker))
+           !after(end_seq[0], prior_snd_una) &&
+            after(end_seq[0], tp->undo_marker))
 				tp->undo_retrans--;
 
-			/* Eliminate too old ACKs, but take into
-			 * account more or less fresh ones, they can
-			 * contain valid SACK info.
-			 */
-			if (before(ack, prior_snd_una-tp->max_window))
-				return 0;
+	for (i=dup_sack; i<num_sacks; i++) {    
+		for (j=i+1; j<num_sacks; j++) {
+		   if (start_seq[i] > start_seq[j]) {
+				temp=start_seq[i]; start_seq[i]=start_seq[j]; start_seq[j]=temp;
+				temp=end_seq[i]; end_seq[i]=end_seq[j]; end_seq[j]=temp;
+		   }
+		}
+	}
+		
+        if (dup_sack) {
+           // for dupsack need to walk sacked packets - costly, but necessary.
+           HTCP_PRINTK1("slow_sacktag_walk ...");
+           flag |= slow_sacktag_write_queue(sk, dup_start_seq, dup_end_seq);       
 		}
 
+        sl= sacked_list_head(tp);
+
+        if (sl == NULL) {
+		skb = (sk)->write_queue.next;
+		if (add_sacked_list_tail (skb, tp)) {
+		   HTCP_PRINTK1("sacktag error: initial sacked_list out of space\n");
+		   return 0; /* error - out of sacked_list space yet sacked_list empty*/
+		}
+		sl = sacked_list_tail(tp);
+	}
+	BUG_TRAP(sl !=NULL);
+	
+	skb = sl; 
+	
+	for (i=dup_sack; i<num_sacks; i++) {
+	
 		/* Event "B" in the comment above. */
-		if (after(end_seq, tp->high_seq))
+		if (after(end_seq[i], tp->high_seq))
 			flag |= FLAG_DATA_LOST;
 
-		for_retrans_queue(skb, sk, tp) {
-			u8 sacked = TCP_SKB_CB(skb)->sacked;
-			int in_sack;
+		while ((skb != (tp)->send_head) && (skb != (struct sk_buff *)&(sk)->write_queue)) {
 
-			/* The retransmission queue is always in order, so
-			 * we can short-circuit the walk early.
-			 */
-			if(!before(TCP_SKB_CB(skb)->seq, end_seq))
+			if (sl == NULL) {
+				/* run off end of sacked_list - do we need to extend it ? */
+				for (skb=skb->next;((skb != (tp)->send_head) && (skb != (struct sk_buff *)&(sk)->write_queue)); skb=skb->next) {
+					BUG_TRAP(skb !=NULL);
+					sacked = TCP_SKB_CB(skb)->sacked;
+					if ( !(sacked&TCPCB_SACKED_ACKED) ) {
+						add_sacked_list_tail (skb, tp);
+						sl = sacked_list_tail(tp);
 				break;
+                                        }
+				} 
+				if (sl == NULL) {
+				   HTCP_PRINTK1("left, return 0.\n");
+				   return 0; /* rest of write_queue in flight is already sacked ! */
+				}
+			}
+                        BUG_TRAP(sl!=NULL);
+			skb = sl;
 
-			fack_count++;
-
-			in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq) &&
-				!before(end_seq, TCP_SKB_CB(skb)->end_seq);
+			if (!before(TCP_SKB_CB(skb)->seq, end_seq[i]))
+				break;
 
-			/* Account D-SACK for retransmitted packet. */
-			if ((dup_sack && in_sack) &&
-			    (sacked & TCPCB_RETRANS) &&
-			    after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker))
-				tp->undo_retrans--;
+			sacked = TCP_SKB_CB(skb)->sacked;
+			in_sack = !after(start_seq[i], TCP_SKB_CB(skb)->seq) &&
+				!before(end_seq[i], TCP_SKB_CB(skb)->end_seq);
 
 			/* The frame is ACKed. */
 			if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) {
-				if (sacked&TCPCB_RETRANS) {
-					if ((dup_sack && in_sack) &&
-					    (sacked&TCPCB_SACKED_ACKED))
-						reord = min(fack_count, reord);
-				} else {
-					/* If it was in a hole, we detected reordering. */
-					if (fack_count < prior_fackets &&
-					    !(sacked&TCPCB_SACKED_ACKED))
-						reord = min(fack_count, reord);
-				}
-
 				/* Nothing to do; acked frame is about to be dropped. */
+				sl = TCP_SKB_CB(sl)->next;
 				continue;
 			}
 
 			if ((sacked&TCPCB_SACKED_RETRANS) &&
-			    after(end_seq, TCP_SKB_CB(skb)->ack_seq) &&
-			    (!lost_retrans || after(end_seq, lost_retrans)))
-				lost_retrans = end_seq;
+				after(end_seq[i], TCP_SKB_CB(skb)->ack_seq) &&
+				(!lost_retrans || after(end_seq[i], lost_retrans)))
+				lost_retrans = end_seq[i];
+		
+			if (!in_sack) {
+						
+				if (!(sacked&TCPCB_TAGBITS) && after(TCP_SKB_CB(skb)->end_seq,tp->snd_una) ) {
+					  /* If packet lies in a hole, force retransmit */
+                                          TCP_SKB_CB(skb)->markedlost |= TCPCB_MARKEDLOST;
+                                          tp->xmit_retransmit_queue_skb_hint=NULL;
+				} else if (sacked & TCPCB_SACKED_RETRANS && tcp_skb_timedout(tp,skb)) {
+					  TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;  tp->retrans_out--;
+                                          TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
+                                          tp->xmit_retransmit_queue_skb_hint=NULL;
+ 				}
+                                if ((TCP_SKB_CB(skb)->markedlost & TCPCB_MARKEDLOST) && packet_cnt ) {
+                                          packet_cnt--;
+                                          TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; tp->lost_out++;
+                                          TCP_SKB_CB(skb)->markedlost &= ~TCPCB_MARKEDLOST;
+                                          tp->retrans_this_ack++;
+                                }
 
-			if (!in_sack)
-				continue;
 
-			if (!(sacked&TCPCB_SACKED_ACKED)) {
+				sl = TCP_SKB_CB(sl)->next;
+			 
+			} else {
 				if (sacked & TCPCB_SACKED_RETRANS) {
 					/* If the segment is not tagged as lost,
 					 * we do not clear RETRANS, believing
@@ -873,44 +1179,29 @@
 						TCP_SKB_CB(skb)->sacked &= ~(TCPCB_LOST|TCPCB_SACKED_RETRANS);
 						tp->lost_out--;
 						tp->retrans_out--;
+                                            tp->xmit_retransmit_queue_skb_hint=NULL;
 					}
 				} else {
-					/* New sack for not retransmitted frame,
-					 * which was in hole. It is reordering.
-					 */
-					if (!(sacked & TCPCB_RETRANS) &&
-					    fack_count < prior_fackets)
-						reord = min(fack_count, reord);
-
 					if (sacked & TCPCB_LOST) {
 						TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
 						tp->lost_out--;
+                                            tp->xmit_retransmit_queue_skb_hint=NULL;
 					}
 				}
 
+                                TCP_SKB_CB(skb)->markedlost &= ~TCPCB_MARKEDLOST;
 				TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
 				flag |= FLAG_DATA_SACKED;
 				tp->sacked_out++;
 
-				if (fack_count > tp->fackets_out)
-					tp->fackets_out = fack_count;
-			} else {
-				if (dup_sack && (sacked&TCPCB_RETRANS))
-					reord = min(fack_count, reord);
+				/* unlink skbuff from sacked_list */
+                                if (tp->xmit_retransmit_queue_skb_hint == sl)
+                                   tp->xmit_retransmit_queue_skb_hint = NULL;
+ 				sl = unlink_sacked_list(sl, tp);
 			}
+		} /* while */
+	} /* for */
 
-			/* D-SACK. We can detect redundant retransmission
-			 * in S|R and plain R frames and clear it.
-			 * undo_retrans is decreased above, L|R frames
-			 * are accounted above as well.
-			 */
-			if (dup_sack &&
-			    (TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_RETRANS)) {
-				TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
-				tp->retrans_out--;
-			}
-		}
-	}
 
 	/* Check for lost retransmit. This superb idea is
 	 * borrowed from "ratehalving". Event "C".
@@ -921,6 +1212,8 @@
 	if (lost_retrans && tp->ca_state == TCP_CA_Recovery) {
 		struct sk_buff *skb;
 
+		HTCP_PRINTK1 ("lost retransmit\n");
+                tp->xmit_retransmit_queue_skb_hint=NULL;
 		for_retrans_queue(skb, sk, tp) {
 			if (after(TCP_SKB_CB(skb)->seq, lost_retrans))
 				break;
@@ -943,10 +1236,10 @@
 		}
 	}
 
+	tp->fackets_out = tp->sacked_out + tp->lost_out; 
 	tp->left_out = tp->sacked_out + tp->lost_out;
-
-	if (reord < tp->fackets_out && tp->ca_state != TCP_CA_Loss)
-		tcp_update_reordering(tp, (tp->fackets_out+1)-reord, 0);
+        HTCP_PRINTK1("sack_list_size=%u",sacked_list_size(tp));
+	HTCP_PRINTK1("left.\n");
 
 #if FASTRETRANS_DEBUG > 0
 	BUG_TRAP((int)tp->sacked_out >= 0);
@@ -957,6 +1250,7 @@
 	return flag;
 }
 
+
 /* RTO occurred, but do not yet enter loss state. Instead, transmit two new
  * segments to see from the next ACKs whether any data was really missing.
  * If the RTO was spurious, new ACKs should arrive.
@@ -988,6 +1282,7 @@
 		TCP_SKB_CB(skb)->sacked &= ~TCPCB_RETRANS;
 	}
 	tcp_sync_left_out(tp);
+        clear_sacktag_hints(tp);
 
 	tp->ca_state = TCP_CA_Open;
 	tp->frto_highmark = tp->snd_nxt;
@@ -1003,10 +1298,14 @@
 	struct sk_buff *skb;
 	int cnt = 0;
 
+	HTCP_PRINTK1 ("tcp_enter_frto_loss()\n");
+	
 	tp->sacked_out = 0;
 	tp->lost_out = 0;
 	tp->fackets_out = 0;
 
+	purge_sacked_list (tp);
+        clear_sacktag_hints (tp);
 	for_retrans_queue(skb, sk, tp) {
 		cnt++;
 		TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
@@ -1028,7 +1327,7 @@
 	tcp_sync_left_out(tp);
 
 	tp->snd_cwnd = tp->frto_counter + tcp_packets_in_flight(tp)+1;
-	tp->snd_cwnd_cnt = 0;
+	tp->snd_cwnd_cnt = 0; htcp_reset(tp); 
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 	tp->undo_marker = 0;
 	tp->frto_counter = 0;
@@ -1042,6 +1341,8 @@
 
 void tcp_clear_retrans(struct tcp_opt *tp)
 {
+        clear_sacktag_hints (tp);
+
 	tp->left_out = 0;
 	tp->retrans_out = 0;
 
@@ -1063,7 +1364,17 @@
 	struct sk_buff *skb;
 	int cnt = 0;
 
+	WEB100_UPDATE_FUNC(tp, web100_update_congestion(tp, 0));
+
+	HTCP_PRINTK1("tcp_enter_loss()\n");
+
 	/* Reduce ssthresh if it has not yet been made inside this window. */
+        if (how == 2) {
+           if (tp->ca_state < TCP_CA_CWR) { //i.e. in Open or Disorder state
+              tp->prior_ssthresh = tcp_current_ssthresh(tp); 
+              tp->snd_ssthresh = tp->snd_cwnd;
+           }
+        } else {
 	if (tp->ca_state <= TCP_CA_Disorder ||
 	    tp->snd_una == tp->high_seq ||
 	    (tp->ca_state == TCP_CA_Loss && !tp->retransmits)) {
@@ -1071,6 +1382,8 @@
 		tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
 	}
 	tp->snd_cwnd = 1;
+           htcp_reset(tp);
+        }
 	tp->snd_cwnd_cnt = 0;
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 
@@ -1081,6 +1394,7 @@
 	if (!how)
 		tp->undo_marker = tp->snd_una;
 
+	purge_sacked_list (tp);
 	for_retrans_queue(skb, sk, tp) {
 		cnt++;
 		if (TCP_SKB_CB(skb)->sacked&TCPCB_RETRANS)
@@ -1098,6 +1412,7 @@
 	tcp_sync_left_out(tp);
 
 	tp->reordering = min_t(unsigned int, tp->reordering, sysctl_tcp_reordering);
+	WEB100_VAR_SET(tp, RetranThresh, tp->reordering);
 	tp->ca_state = TCP_CA_Loss;
 	tp->high_seq = tp->snd_nxt;
 	TCP_ECN_queue_cwr(tp);
@@ -1113,11 +1428,17 @@
 	 * receiver _host_ is heavily congested (or buggy).
 	 * Do processing similar to RTO timeout.
 	 */
+
 	if ((skb = skb_peek(&sk->write_queue)) != NULL &&
 	    (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
 		NET_INC_STATS_BH(TCPSACKReneging);
 
-		tcp_enter_loss(sk, 1);
+                HTCP_PRINTK1("sack reneging ...\n");
+
+                // sack reneging seems to be quite common on high speed links
+                // for reasons that are unclear - setting cwnd to 1 is too
+                // severe a response ...  DL.  
+		tcp_enter_loss(sk, 2);
 		tp->retransmits++;
 		tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
 		tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
@@ -1131,16 +1452,6 @@
 	return IsReno(tp) ? tp->sacked_out+1 : tp->fackets_out;
 }
 
-static inline int tcp_skb_timedout(struct tcp_opt *tp, struct sk_buff *skb)
-{
-	return (tcp_time_stamp - TCP_SKB_CB(skb)->when > tp->rto);
-}
-
-static inline int tcp_head_timedout(struct sock *sk, struct tcp_opt *tp)
-{
-	return tp->packets_out && tcp_skb_timedout(tp, skb_peek(&sk->write_queue));
-}
-
 /* Linux NewReno/SACK/FACK/ECN state machine.
  * --------------------------------------
  *
@@ -1238,18 +1549,21 @@
 tcp_time_to_recover(struct sock *sk, struct tcp_opt *tp)
 {
 	/* Trick#1: The loss is proven. */
-	if (tp->lost_out)
+/*    if (tp->lost_out)
 		return 1;
-
+*/
 	/* Not-A-Trick#2 : Classic rule... */
-	if (tcp_fackets_out(tp) > tp->reordering)
+	if (tcp_fackets_out(tp) > tp->reordering) {
+	  HTCP_PRINTK1("time_to_recover(): TRICK#2, fackets_out =%u, cwnd =%u\n",tcp_fackets_out(tp), tp->snd_cwnd);
 		return 1;
+	}
 
 	/* Trick#3 : when we use RFC2988 timer restart, fast
 	 * retransmit can be triggered by timeout of queue head.
 	 */
-	if (tcp_head_timedout(sk, tp))
-		return 1;
+	if (tcp_head_timedout(sk, tp)) {
+		HTCP_PRINTK1("time_to_recover(): TRICK#3, head_timedout\n");
+		return 1; }
 
 	/* Trick#4: It is still not OK... But will it be useful to delay
 	 * recovery more?
@@ -1260,6 +1574,7 @@
 		/* We have nothing to send. This connection is limited
 		 * either by receiver window or by application.
 		 */
+                HTCP_PRINTK1("time_to_recover(): TRICK#4.\n");
 		return 1;
 	}
 
@@ -1330,6 +1645,7 @@
 			tp->lost_out++;
 		}
 	}
+
 	tcp_sync_left_out(tp);
 }
 
@@ -1370,8 +1686,21 @@
  */
 static __inline__ void tcp_moderate_cwnd(struct tcp_opt *tp)
 {
+        if (sysctl_tcp_htcp_moderation) return; // use alternative form of moderation
+
+#ifdef CONFIG_WEB100_STATS
+	{
+		u32 t = tcp_packets_in_flight(tp) + tcp_max_burst(tp);
+		if (t < tp->snd_cwnd) {
+			tp->snd_cwnd = t;
+			WEB100_VAR_INC(tp, OtherReductions);
+			WEB100_VAR_INC(tp, X_OtherReductionsCM);
+		}
+	};
+#else
 	tp->snd_cwnd = min(tp->snd_cwnd,
 			   tcp_packets_in_flight(tp)+tcp_max_burst(tp));
+#endif
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -1380,14 +1709,25 @@
 static void tcp_cwnd_down(struct tcp_opt *tp)
 {
 	int decr = tp->snd_cwnd_cnt + 1;
+        __u32 cap;
 
 	tp->snd_cwnd_cnt = decr&1;
 	decr >>= 1;
 
-	if (decr && tp->snd_cwnd > tp->snd_ssthresh/2)
+        if (tp->sack_cache.ack_count  && tp->snd_cwnd > tp->snd_ssthresh) {
+           tp->snd_cwnd -= (tp->sack_cache.ack_count+1)>>1; 
+        } else if (decr && tp->snd_cwnd > tp->snd_ssthresh) 
 		tp->snd_cwnd -= decr;
 
+        //packet counting is inaccurate during recovery, so restricting to^
+        //packets_in_flight+1 does more harm than good.
+        if (!sysctl_tcp_htcp_cap)	
 	tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1);
+        else {
+           cap=max(tp->snd_ssthresh, tcp_packets_in_flight(tp)+1);
+           tp->snd_cwnd = min(tp->snd_cwnd, cap);
+        }
+
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -1419,16 +1759,24 @@
 static void tcp_undo_cwr(struct tcp_opt *tp, int undo)
 {
 	if (tp->prior_ssthresh) {
-		tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh<<1);
+		tp->snd_cwnd = max(tp->snd_cwnd, (tp->snd_ssthresh<<7)/tp->snd_decreasenum);
+                undo_htcp_reset(tp);
 
 		if (undo && tp->prior_ssthresh > tp->snd_ssthresh) {
+			if (!sysctl_tcp_htcp_undo)
+                           //original undo 
 			tp->snd_ssthresh = tp->prior_ssthresh;
+                        else
+                           //more accurate undo
+                           tp->snd_ssthresh = tp->undo_ssthresh;
 			TCP_ECN_withdraw_cwr(tp);
 		}
+		WEB100_VAR_INC(tp, CongestionOverCount);
 	} else {
 		tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh);
 	}
 	tcp_moderate_cwnd(tp);
+        clear_sacktag_hints (tp);
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -1492,7 +1840,9 @@
 		tcp_update_reordering(tp, tcp_fackets_out(tp)+acked, 1);
 
 		DBGUNDO(sk, tp, "Hoe");
-		tcp_undo_cwr(tp, 0);
+		//tcp_undo_cwr(tp, 0);
+                tcp_undo_cwr(tp, 1);
+
 		NET_INC_STATS_BH(TCPPartialUndo);
 
 		/* So... Do not make Hoe's retransmit yet.
@@ -1512,6 +1862,8 @@
 		for_retrans_queue(skb, sk, tp) {
 			TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
 		}
+                clear_sacktag_hints(tp);
+
 		DBGUNDO(sk, tp, "partial loss");
 		tp->lost_out = 0;
 		tp->left_out = tp->sacked_out;
@@ -1549,13 +1901,15 @@
 		    tp->retrans_out ||
 		    tp->undo_marker)
 			state = TCP_CA_Disorder;
-
 		if (tp->ca_state != state) {
 			tp->ca_state = state;
 			tp->high_seq = tp->snd_nxt;
+			HTCP_PRINTK1("tcp_try_to_open(): enter state %u\n", state);
 		}
 		tcp_moderate_cwnd(tp);
+                HTCP_PRINTK1("cwnd=%u, in flight=%u\n", tp->snd_cwnd, tcp_packets_in_flight(tp));
 	} else {
+		HTCP_PRINTK1("tcp_try_to_open(): call cwnd_down\n");
 		tcp_cwnd_down(tp);
 	}
 }
@@ -1578,6 +1932,8 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int is_dupack = (tp->snd_una == prior_snd_una && !(flag&FLAG_NOT_DUP));
 
+        HTCP_PRINTK2("fastretrans()...");
+
 	/* Some technical things:
 	 * 1. Reno does not count dupacks (sacked_out) automatically. */
 	if (!tp->packets_out)
@@ -1599,7 +1955,7 @@
 	if ((flag&FLAG_DATA_LOST) &&
 	    before(tp->snd_una, tp->high_seq) &&
 	    tp->ca_state != TCP_CA_Open &&
-	    tp->fackets_out > tp->reordering) {
+		tp->fackets_out > tp->reordering && IsReno(tp)  ) {
 		tcp_mark_head_lost(sk, tp, tp->fackets_out-tp->reordering, tp->high_seq);
 		NET_INC_STATS_BH(TCPLoss);
 	}
@@ -1610,12 +1966,12 @@
 	/* E. Check state exit conditions. State can be terminated
 	 *    when high_seq is ACKed. */
 	if (tp->ca_state == TCP_CA_Open) {
-		if (!sysctl_tcp_frto)
 			BUG_TRAP(tp->retrans_out == 0);
 		tp->retrans_stamp = 0;
 	} else if (!before(tp->snd_una, tp->high_seq)) {
 		switch (tp->ca_state) {
 		case TCP_CA_Loss:
+		HTCP_PRINTK1("tcp_fastretrans_alert(): exit loss. cwnd =%u, ssthresh =%u, high_seq= %u\n", tp->snd_cwnd, tp->snd_ssthresh, tp->high_seq);
 			tp->retransmits = 0;
 			if (tcp_try_undo_recovery(sk, tp))
 				return;
@@ -1638,10 +1994,12 @@
 			    IsReno(tp) || tp->snd_una != tp->high_seq) {
 				tp->undo_marker = 0;
 				tp->ca_state = TCP_CA_Open;
+				HTCP_PRINTK1("tcp_fastretrans_alert(): exit disorder. cwnd =%u, ssthresh =%u, prior ssthresh =%u, high_seq= %u\n", tp->snd_cwnd, tp->snd_ssthresh, tp->prior_ssthresh, tp->high_seq);
 			}
 			break;
 
 		case TCP_CA_Recovery:
+			HTCP_PRINTK1("tcp_fastretrans_alert(): exit recovery. snd_una = %u, cwnd =%u, ssthresh =%u, high_seq= %u\n", tp->snd_una, tp->snd_cwnd, tp->snd_ssthresh, tp->high_seq);
 			if (IsReno(tp))
 				tcp_reset_reno_sack(tp);
 			if (tcp_try_undo_recovery(sk, tp))
@@ -1670,10 +2028,13 @@
 		if (!tcp_try_undo_loss(sk, tp)) {
 			tcp_moderate_cwnd(tp);
 			tcp_xmit_retransmit_queue(sk);
+                        HTCP_PRINTK2("TCP_CA_Loss1..left.\n");
 			return;
 		}
-		if (tp->ca_state != TCP_CA_Open)
+		if (tp->ca_state != TCP_CA_Open) {
+                        HTCP_PRINTK2("TCP_CA_Loss2..left.\n");
 			return;
+                }
 		/* Loss is undone; fall through to processing in Open state. */
 	default:
 		if (IsReno(tp)) {
@@ -1688,6 +2049,7 @@
 
 		if (!tcp_time_to_recover(sk, tp)) {
 			tcp_try_to_open(sk, tp, flag);
+                        HTCP_PRINTK2("timetorecover..left.\n");
 			return;
 		}
 
@@ -1703,28 +2065,45 @@
 		tp->undo_marker = tp->snd_una;
 		tp->undo_retrans = tp->retrans_out;
 
+                decreasenum_update(tp);
+		
+                //reordering etc can lead to spurious backoff immediately after backoff due to a genuine 
+                //congestion event - for spurious backoff undo logic to work we need to log prior_ssthresh
+                //here to avoid multiple backoffs.
+                if (!(flag&FLAG_ECE) && sysctl_tcp_htcp_undo)
+                   tp->prior_ssthresh = tcp_current_ssthresh(tp);
 		if (tp->ca_state < TCP_CA_CWR) {
-			if (!(flag&FLAG_ECE))
+		   if (!(flag&FLAG_ECE) && ! sysctl_tcp_htcp_undo) 
 				tp->prior_ssthresh = tcp_current_ssthresh(tp);
 			tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
 			TCP_ECN_queue_cwr(tp);
 		}
 
+                htcp_reset(tp);  
+                if (tp->snd_cwnd > tp->snd_alpha+2)
+                   /* account for expected packet loss up front */
+                   tp->snd_cwnd = tp->snd_cwnd - tp->snd_alpha;
+
 		tp->snd_cwnd_cnt = 0;
 		tp->ca_state = TCP_CA_Recovery;
+		HTCP_PRINTK1("tcp_fastretrans_alert(): enter recovery. cwnd =%u, ssthresh =%u, prior ssthresh =%u, high_seq= %u\n", tp->snd_cwnd, tp->snd_ssthresh, tp->prior_ssthresh,tp->high_seq);
+		WEB100_UPDATE_FUNC(tp, web100_update_congestion(tp, 0));
+		WEB100_VAR_INC(tp, FastRetran); /* WEB100_XXX */        
 	}
 
-	if (is_dupack || tcp_head_timedout(sk, tp))
+	if ( IsReno(tp) && (is_dupack || tcp_head_timedout(sk, tp)))
 		tcp_update_scoreboard(sk, tp);
 	tcp_cwnd_down(tp);
 	tcp_xmit_retransmit_queue(sk);
+        HTCP_PRINTK2("..left.\n");
 }
 
 /* Read draft-ietf-tcplw-high-performance before mucking
  * with this code. (Superceeds RFC1323)
  */
-static void tcp_ack_saw_tstamp(struct tcp_opt *tp, int flag)
+static void tcp_ack_saw_tstamp(struct sock *sk, int flag)
 {
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	__u32 seq_rtt;
 
 	/* RTTM Rule: A TSecr value received in a segment is used to
@@ -1743,14 +2122,18 @@
 	 * in window is lost... Voila.	 			--ANK (010210)
 	 */
 	seq_rtt = tcp_time_stamp - tp->rcv_tsecr;
+
 	tcp_rtt_estimator(tp, seq_rtt);
 	tcp_set_rto(tp);
+	WEB100_UPDATE_FUNC(tp, web100_update_rtt(tp, seq_rtt));
 	tp->backoff = 0;
 	tcp_bound_rto(tp);
 }
 
-static void tcp_ack_no_tstamp(struct tcp_opt *tp, u32 seq_rtt, int flag)
+static void tcp_ack_no_tstamp(struct sock *sk, u32 seq_rtt, int flag)
 {
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
 	/* We don't have a timestamp. Can only use
 	 * packets that are not retransmitted to determine
 	 * rtt estimates. Also, we must not reset the
@@ -1765,18 +2148,20 @@
 
 	tcp_rtt_estimator(tp, seq_rtt);
 	tcp_set_rto(tp);
+	WEB100_UPDATE_FUNC(tp, web100_update_rtt(tp, seq_rtt));
 	tp->backoff = 0;
 	tcp_bound_rto(tp);
 }
 
 static __inline__ void
-tcp_ack_update_rtt(struct tcp_opt *tp, int flag, s32 seq_rtt)
+tcp_ack_update_rtt(struct sock *sk, int flag, s32 seq_rtt)
 {
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	/* Note that peer MAY send zero echo. In this case it is ignored. (rfc1323) */
 	if (tp->saw_tstamp && tp->rcv_tsecr)
-		tcp_ack_saw_tstamp(tp, flag);
+		tcp_ack_saw_tstamp(sk, flag);
 	else if (seq_rtt >= 0)
-		tcp_ack_no_tstamp(tp, seq_rtt, flag);
+		tcp_ack_no_tstamp(sk, seq_rtt, flag);
 }
 
 /* This is Jacobson's slow start and congestion avoidance. 
@@ -1784,21 +2169,124 @@
  */
 static __inline__ void tcp_cong_avoid(struct tcp_opt *tp)
 {
+#ifdef CONFIG_WEB100_STATS
+        struct web100stats *stats = tp->tcp_stats;
+
+	if (tp->snd_cwnd > tp->snd_cwnd_clamp) {
+                HTCP_PRINTK2("cong_avoid(): cwnd_clamp.\n");
+		tp->snd_cwnd--;
+		return;
+	}
+#endif
+
         if (tp->snd_cwnd <= tp->snd_ssthresh) {
                 /* In "safe" area, increase. */
-		if (tp->snd_cwnd < tp->snd_cwnd_clamp)
+
+#ifdef CONFIG_WEB100_NET100
+                int max_ssthresh = NET100_WAD(tp, WAD_MaxSsthresh, 0);
+
+                if (max_ssthresh <= 0 || tp->snd_cwnd <= max_ssthresh) {
+                        tp->snd_cwnd++;
+                } else {
+                        /* Floyd modified slow start */
+                        if (stats->wc_lss_k == 0)
+                                stats->wc_lss_k = 2 * tp->snd_cwnd / max_ssthresh;
+                        if (stats->wc_lss_cnt1++ >= stats->wc_lss_k) {
+                                tp->snd_cwnd++;
+                                stats->wc_lss_cnt1 = 0;
+                                if (stats->wc_lss_cnt2++ >= max_ssthresh) {
+                                        stats->wc_lss_cnt2 = 0;
+                                        stats->wc_lss_k = 2 * tp->snd_cwnd / max_ssthresh;
+                                }
+                        }
+                }
+#else
 			tp->snd_cwnd++;
+#endif
+                HTCP_PRINTK1("cong_avoid(): slow_start.\n");
+                decreasenum_check(tp);
+
+		WEB100_VAR_INC(tp, SlowStart);
 	} else {
                 /* In dangerous area, increase slowly.
 		 * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd
 		 */
-		if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
-			if (tp->snd_cwnd < tp->snd_cwnd_clamp)
-				tp->snd_cwnd++;
-			tp->snd_cwnd_cnt=0;
+		 
+		/* TO DO: proper ack accounting to allow for both ordinary acks and delayed acks */
+
+		__u32 diff=0;
+		__u32 alpha=1;
+		__u32 thresh=0;
+		__u32 RTT_Scaling=1;
+#ifdef CONFIG_WEB100_NET100
+                int kaicnt = NET100_WAD(tp, WAD_Kaicnt, 0);
+#endif
+
+                /* keep track of number of round-trip times since last backoff event */
+                if (tp->snd_cwnd_cnt2 > tp->snd_cwnd) {
+                   tp->snd_ccount++;
+                   tp->snd_cwnd_cnt2=0;
 		} else
+                   tp->snd_cwnd_cnt2=tp->snd_cwnd_cnt2+2; /* add 2 to account for delayed acking */
+       
+                measure_minRTT (tp);
+ 
+                /* refer current RTT to 100ms, clamping ratio to interval [0.5,10] */
+                RTT_Scaling = max((HZ<<3)/(10*tp->snd_minRTT), (__u32)1<<2);
+                RTT_Scaling = min(RTT_Scaling,(__u32)10<<3); 
+
+                /* time since last backoff */
+                diff= tp->snd_ccount*tp->snd_minRTT ;
+       
+                if (sysctl_tcp_htcp==1) {
+                   /* calculate increase rate - alpha is number of packets added to cwnd per RTT */
+                   alpha = 1;
+                   if (diff > HZ) {
+                      diff = diff-HZ;
+                      alpha += ( 10*diff+((diff>>1)*(diff>>1)/HZ) )/HZ; 
+                   }
+                   HTCP_PRINTK1("diff=%u,alpha=%u\n",diff,alpha);
+                   alpha = (alpha<<3)/RTT_Scaling;
+                   alpha = alpha<<1;  /* account for delayed acking */
+                   alpha = max(alpha, (__u32)1);
+                   alpha = alpha*2*((1<<7)-tp->snd_decreasenum); 
+
+                   tp->snd_alpha = max((__u32)1, alpha>>7);
+   	           HTCP_PRINTK1("cong_avoid, alpha=%u/128, decreasenum=%u/128, RTTScaling=%u/8, thresh=%u\n", alpha, tp->snd_decreasenum,RTT_Scaling, thresh);    
+
 			tp->snd_cwnd_cnt++;
+	           if ((tp->snd_cwnd_cnt*alpha)>>7 > tp->snd_cwnd) {
+		      tp->snd_cwnd=tp->snd_cwnd+1;
+		      tp->snd_cwnd_cnt=0;
+		   }
+                } else {
+                   tp->snd_alpha=1;
+
+#ifdef CONFIG_WEB100_NET100
+                   if (NET100_WAD(tp, WAD_NoAI, 0)) {
+                        tp->snd_cwnd += NET100_WAD(tp, WAD_CwndAdjust, 0);
+                        WEB100_VAR_SET(tp, WAD_CwndAdjust, 0);
+                   } else {
+                        if (NET100_WAD(tp, WAD_FloydAIMD, sysctl_WAD_FloydAIMD))
+                                web100_update_floyd_aimd(tp);
+                        tp->snd_cwnd_cnt += NET100_WAD(tp, WAD_AI, 1<<3);
+                   }
+                   if (kaicnt && tp->snd_cwnd_cnt > kaicnt<<3) {
+                        /* have Kelly AI count */
+                        tp->snd_cwnd++;
+                        tp->snd_cwnd_cnt = 0;
         }
+#else
+                   tp->snd_cwnd_cnt += 1<<3;
+#endif
+                   while (tp->snd_cwnd_cnt > tp->snd_cwnd<<3) {
+                        tp->snd_cwnd_cnt -= tp->snd_cwnd<<3;
+                        tp->snd_cwnd++;
+                   }
+                }
+	        WEB100_VAR_INC(tp, CongAvoid);
+	}
+	tp->snd_cwnd = min(tp->snd_cwnd, (__u32)tp->snd_cwnd_clamp);
 	tp->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -1823,6 +2311,25 @@
 	__u32 now = tcp_time_stamp;
 	int acked = 0;
 	__s32 seq_rtt = -1;
+        struct sk_buff *sl = sacked_list_head(tp);
+
+        HTCP_PRINTK2("clean_rtx_queue ...");
+
+        if( (tp->xmit_retransmit_queue_skb_hint)
+           && !after(TCP_SKB_CB(tp->xmit_retransmit_queue_skb_hint)->end_seq, tp->snd_una))
+              tp->xmit_retransmit_queue_skb_hint=NULL;
+        if( (tp->dupsack_skb_hint)
+           && !after(TCP_SKB_CB(tp->dupsack_skb_hint)->end_seq, tp->snd_una))
+              tp->dupsack_skb_hint=NULL;
+
+	/* clean up sacked list */
+	while (sl != NULL) {
+		if (after(TCP_SKB_CB(sl)->end_seq, tp->snd_una))
+			break;
+		sl = unlink_sacked_list(sl, tp);
+	}
+
+        HTCP_PRINTK2("sl clean ...");
 
 	while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) {
 		struct tcp_skb_cb *scb = TCP_SKB_CB(skb); 
@@ -1844,6 +2351,7 @@
 		 */
 		if(!(scb->flags & TCPCB_FLAG_SYN)) {
 			acked |= FLAG_DATA_ACKED;
+			tp->snd_packetcount++;
 		} else {
 			acked |= FLAG_SYN_ACKED;
 			tp->retrans_stamp = 0;
@@ -1857,10 +2365,12 @@
 				seq_rtt = -1;
 			} else if (seq_rtt < 0)
 				seq_rtt = now - scb->when;
-			if(sacked & TCPCB_SACKED_ACKED)
+			if(sacked & TCPCB_SACKED_ACKED && tp->sacked_out>0)
 				tp->sacked_out--;
-			if(sacked & TCPCB_LOST)
+			if(sacked & TCPCB_LOST) {
 				tp->lost_out--;
+                                tp->xmit_retransmit_queue_cnt_hint--;
+                        }
 			if(sacked & TCPCB_URG) {
 				if (tp->urg_mode &&
 				    !before(scb->end_seq, tp->snd_up))
@@ -1875,11 +2385,15 @@
 		tcp_free_skb(sk, skb);
 	}
 
+        HTCP_PRINTK2("writequeue clean...");
+
 	if (acked&FLAG_ACKED) {
-		tcp_ack_update_rtt(tp, acked, seq_rtt);
+		tcp_ack_update_rtt(sk, acked, seq_rtt);
 		tcp_ack_packets_out(sk, tp);
 	}
 
+        measure_achieved_throughput(tp);
+
 #if FASTRETRANS_DEBUG > 0
 	BUG_TRAP((int)tp->sacked_out >= 0);
 	BUG_TRAP((int)tp->lost_out >= 0);
@@ -1899,6 +2413,7 @@
 		}
 	}
 #endif
+        HTCP_PRINTK2("left.\n");
 	return acked;
 }
 
@@ -1973,10 +2488,12 @@
 				tp->max_window = nwin;
 				tcp_sync_mss(sk, tp->pmtu_cookie);
 			}
+			WEB100_UPDATE_FUNC(tp, web100_update_rwin_rcvd(tp));
 		}
 	}
 
 	tp->snd_una = ack;
+	WEB100_VAR_SET(tp, SndUna, ack);
 
 	return flag;
 }
@@ -2016,6 +2533,26 @@
 	tp->frto_counter = (tp->frto_counter + 1) % 3;
 }
 
+static __inline__  __u32 tcp_dsack(struct sk_buff *skb)
+{
+        u32 ack = TCP_SKB_CB(skb)->ack_seq;
+        unsigned char *ptr = skb->h.raw + TCP_SKB_CB(skb)->sacked;
+        struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2);
+        int num_sacks  = (ptr[1] - TCPOLEN_SACK_BASE)>>3;
+        __u32 start_seq = ntohl(sp->start_seq);
+        __u32 end_seq = ntohl(sp->end_seq);
+ 
+        if (before(start_seq, ack)) {
+                return 1;
+        } else if (num_sacks > 1 &&
+                   !after(end_seq, ntohl(sp[1].end_seq)) &&
+                   !before(start_seq, ntohl(sp[1].start_seq))) {
+                return 1;
+        }
+        return 0;
+
+}
+
 /* This routine deals with incoming acks, but not outgoing ones. */
 static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
 {
@@ -2025,10 +2562,12 @@
 	u32 ack = TCP_SKB_CB(skb)->ack_seq;
 	u32 prior_in_flight;
 	int prior_packets;
+        int i;
 
 	/* If the ack is newer than sent or older than previous acks
 	 * then we can probably ignore it.
 	 */
+        HTCP_PRINTK2("tcp_ack entered.\n");
 	if (after(ack, tp->snd_nxt))
 		goto uninteresting_ack;
 
@@ -2042,8 +2581,11 @@
 		 */
 		tcp_update_wl(tp, ack, ack_seq);
 		tp->snd_una = ack;
+		WEB100_VAR_SET(tp, SndUna, ack);
 		flag |= FLAG_WIN_UPDATE;
 
+                purge_sack_cache(tp);
+
 		NET_INC_STATS_BH(TCPHPAcks);
 	} else {
 		if (ack_seq != TCP_SKB_CB(skb)->end_seq)
@@ -2053,11 +2595,40 @@
 
 		flag |= tcp_ack_update_window(sk, tp, skb, ack, ack_seq);
 
-		if (TCP_SKB_CB(skb)->sacked)
+		if (TCP_SKB_CB(skb)->sacked) {
+                    if ((!tcp_dsack(skb)
+                        // scale cache with number of drops ...
+                        && tp->sack_cache.ack_count<min((sacked_list_size(tp)>>5)+1, SACK_CACHE_SIZE/3)
+                        // no caching if window is small as would stall ack clock ...
+                        && tcp_packets_in_flight(tp)>SACK_CACHE_SIZE
+                        // no caching until we exit Open state ...
+                        && tp->ca_state != TCP_CA_Open)) {
+ 
+                      unsigned char *ptr = skb->h.raw + TCP_SKB_CB(skb)->sacked;
+                      struct tcp_sack_block *sp = (struct tcp_sack_block *)(ptr+2);
+                      int num_sacks  = (ptr[1] - TCPOLEN_SACK_BASE)>>3;
+
+                      BUG_TRAP(tp->sack_cache.num<=SACK_CACHE_SIZE);
+ 
+                      for (i=0; i<num_sacks; i++, sp++) {
+                         tp->sack_cache.start_seq[tp->sack_cache.num] = ntohl(sp->start_seq);
+                         tp->sack_cache.end_seq[tp->sack_cache.num] = ntohl(sp->end_seq);
+                         tp->sack_cache.num++;
+                      }
+                      tp->sack_cache.ack_count++;
+                      return 1;
+                   } else { 
+                      flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una);
+                   }
+                } else {
+                      if (tp->sack_cache.ack_count)
 			flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una);
+                } 
 
-		if (TCP_ECN_rcv_ecn_echo(tp, skb->h.th))
+		if (TCP_ECN_rcv_ecn_echo(tp, skb->h.th)) {
 			flag |= FLAG_ECE;
+			WEB100_VAR_INC(tp, ECERcvd);
+		}
 	}
 
 	/* We passed data and got it acked, remove any soft error
@@ -2065,16 +2636,19 @@
 	 */
 	sk->err_soft = 0;
 	tp->rcv_tstamp = tcp_time_stamp;
-	if ((prior_packets = tp->packets_out) == 0)
+	if ((prior_packets = tp->packets_out) == 0) {
 		goto no_queue;
+        }
 
 	prior_in_flight = tcp_packets_in_flight(tp);
 
 	/* See if we can take anything off of the retransmit queue. */
 	flag |= tcp_clean_rtx_queue(sk);
+        HTCP_PRINTK2("returned cleanrtxqueue.\n");
 
-	if (tp->frto_counter)
+	if (tp->frto_counter) {
 		tcp_process_frto(sk, prior_snd_una);
+        }
 
 	if (tcp_ack_is_dubious(tp, flag)) {
 		/* Advanve CWND, if state allows this. */
@@ -2087,9 +2661,12 @@
 			tcp_cong_avoid(tp);
 	}
 
+        purge_sack_cache(tp);
+
 	if ((flag & FLAG_FORWARD_PROGRESS) || !(flag&FLAG_NOT_DUP))
 		dst_confirm(sk->dst_cache);
 
+        HTCP_PRINTK2("tcp_ack left.\n");
 	return 1;
 
 no_queue:
@@ -2104,10 +2681,12 @@
 	return 1;
 
 old_ack:
+	/* WEB100_XXX */
 	if (TCP_SKB_CB(skb)->sacked)
 		tcp_sacktag_write_queue(sk, skb, prior_snd_una);
 
 uninteresting_ack:
+	/* WEB100_XXX */
 	SOCK_DEBUG(sk, "Ack %u out of %u:%u\n", ack, tp->snd_una, tp->snd_nxt);
 	return 0;
 }
@@ -2460,6 +3039,8 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
+	WEB100_VAR_INC(tp, DupAcksOut);
+	
 	if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
 	    before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
 		NET_INC_STATS_BH(DelayedACKLost);
@@ -2646,6 +3227,18 @@
 
 static int tcp_prune_queue(struct sock *sk);
 
+#ifdef CONFIG_WEB100_STATS
+static inline int tcp_rmem_full(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+	return (tcp_win_from_space(atomic_read(&sk->rmem_alloc)) >
+			 tp->rcv_hi_seq - tp->copied_seq &&
+		tcp_prune_queue(sk) < 0) ||
+		!tcp_rmem_schedule(sk, skb);
+}
+#endif
+
 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
 {
 	struct tcphdr *th = skb->h.th;
@@ -2692,14 +3285,21 @@
 			local_bh_disable();
 		}
 
-		if (eaten <= 0) {
+		if (eaten < 0) {
 queue_and_out:
-			if (eaten < 0 &&
-			    (atomic_read(&sk->rmem_alloc) > sk->rcvbuf ||
-			     !tcp_rmem_schedule(sk, skb))) {
+#ifdef CONFIG_WEB100_STATS
+			if (sysctl_web100_rbufmode == 1) {
+				if (tcp_rmem_full(sk, skb))
+					goto drop;
+			} else
+#endif
+			if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf ||
+				!tcp_rmem_schedule(sk, skb)) {
 				if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
 					goto drop;
 			}
+		}
+		if (eaten <= 0) {
 			tcp_set_owner_r(skb, sk);
 			__skb_queue_tail(&sk->receive_queue, skb);
 		}
@@ -2724,10 +3324,19 @@
 
 		tcp_fast_path_check(sk, tp);
 
+#ifdef CONFIG_WEB100_STATS
+#if 0
+		if (TCP_SKB_CB(skb)->end_seq == 0)
+			printk("a: end_seq == 0\n");
+#endif
+		tcp_good_seg_rcvd(sk, skb);
+#endif
+
 		if (eaten > 0) {
 			__kfree_skb(skb);
 		} else if (!sk->dead)
 			sk->data_ready(sk, 0);
+
 		return;
 	}
 
@@ -2768,6 +3377,12 @@
 
 	TCP_ECN_check_ce(tp, skb);
 
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_rbufmode == 1) {
+		if (tcp_rmem_full(sk, skb))
+			goto drop;
+	} else
+#endif
 	if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf ||
 	    !tcp_rmem_schedule(sk, skb)) {
 		if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
@@ -2783,6 +3398,14 @@
 
 	tcp_set_owner_r(skb, sk);
 
+#ifdef CONFIG_WEB100_STATS
+#if 0
+	if (TCP_SKB_CB(skb)->end_seq == 0)
+		printk("b: end_seq == 0\n");
+#endif
+	tcp_good_seg_rcvd(sk, skb);
+#endif
+	
 	if (skb_peek(&tp->out_of_order_queue) == NULL) {
 		/* Initial out of order segment, build 1 SACK. */
 		if(tp->sack_ok) {
@@ -2917,6 +3540,11 @@
 		memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
 		TCP_SKB_CB(nskb)->seq = TCP_SKB_CB(nskb)->end_seq = start;
 		__skb_insert(nskb, skb->prev, skb, skb->list);
+#ifdef COFNIG_WEB100_STATS
+		if (tp->tcp_stats->wc_vars.X_RBufMode == WC_BUFMODE_WEB100)
+			;
+		else
+#endif
 		tcp_set_owner_r(nskb, sk);
 
 		/* Copy data, releasing collapsed skbs. */
@@ -3002,10 +3630,15 @@
 
 	NET_INC_STATS_BH(PruneCalled);
 
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_rbufmode != 1)
+#endif
+	{
 	if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf)
 		tcp_clamp_window(sk, tp);
 	else if (tcp_memory_pressure)
 		tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U*tp->advmss);
+	}
 
 	tcp_collapse_ofo_queue(sk);
 	tcp_collapse(sk, sk->receive_queue.next,
@@ -3013,6 +3646,10 @@
 		     tp->copied_seq, tp->rcv_nxt);
 	tcp_mem_reclaim(sk);
 
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_rbufmode == 1)
+		return 0;
+#endif
 	if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf)
 		return 0;
 
@@ -3062,8 +3699,11 @@
 		/* Limited by application or receiver window. */
 		u32 win_used = max(tp->snd_cwnd_used, 2U);
 		if (win_used < tp->snd_cwnd) {
+                        HTCP_PRINTK1("application_limited().\n");
 			tp->snd_ssthresh = tcp_current_ssthresh(tp);
 			tp->snd_cwnd = (tp->snd_cwnd+win_used)>>1;
+			WEB100_VAR_INC(tp, OtherReductions);
+			WEB100_VAR_INC(tp, X_OtherReductionsCV);
 		}
 		tp->snd_cwnd_used = 0;
 	}
@@ -3079,6 +3719,9 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
+#ifdef CONFIG_WEB100_STATS
+	if (sysctl_web100_sbufmode != 1)
+#endif
 	if (tp->packets_out < tp->snd_cwnd &&
 	    !(sk->userlocks&SOCK_SNDBUF_LOCK) &&
 	    !tcp_memory_pressure &&
@@ -3411,10 +4054,14 @@
 				    (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
 				    tp->rcv_nxt == tp->rcv_wup)
 					tcp_store_ts_recent(tp);
+
 				/* We know that such packets are checksummed
 				 * on entry.
 				 */
 				tcp_ack(sk, skb, 0);
+#ifdef CONFIG_WEB100_STATS
+				tcp_good_seg_rcvd(sk, skb);
+#endif
 				__kfree_skb(skb); 
 				tcp_data_snd_check(sk);
 				return 0;
@@ -3475,6 +4122,9 @@
 
 			tcp_event_data_recv(sk, tp, skb);
 
+#ifdef CONFIG_WEB100_STATS
+			tcp_good_seg_rcvd(sk, skb);
+#endif
 			if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {
 				/* Well, only one small jumplet in fast path... */
 				tcp_ack(sk, skb, FLAG_DATA);
@@ -3689,6 +4339,10 @@
 		mb();
 		tcp_set_state(sk, TCP_ESTABLISHED);
 
+#ifdef CONFIG_WEB100_STATS
+		web100_stats_establish(sk);
+#endif
+
 		if(!sk->dead) {
 			sk->state_change(sk);
 			sk_wake_async(sk, 0, POLL_OUT);
@@ -3907,6 +4561,9 @@
 				mb();
 				tcp_set_state(sk, TCP_ESTABLISHED);
 				sk->state_change(sk);
+#ifdef CONFIG_WEB100_STATS
+				web100_stats_establish(sk);
+#endif
 
 				/* Note, that this wakeup is only for marginal
 				 * crossed SYN case. Passively open sockets
@@ -3918,7 +4575,12 @@
 				}
 
 				tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
-				tp->snd_wnd = ntohs(th->window) << tp->snd_wscale;
+				WEB100_VAR_SET(tp, SndUna, tp->snd_una);
+				/* RFC1323: The window in SYN & SYN/ACK segments is
+				 * never scaled (PSC/CMU patch {rreddy,mathis}@psc.edu).
+				 */
+				tp->snd_wnd = ntohs(th->window);
+				WEB100_UPDATE_FUNC(tp, web100_update_rwin_rcvd(tp));
 				tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq);
 
 				/* tcp_ack considers this ACK as duplicate
@@ -3926,7 +4588,7 @@
 				 * Fix it at least with timestamps.
 				 */
 				if (tp->saw_tstamp && tp->rcv_tsecr && !tp->srtt)
-					tcp_ack_saw_tstamp(tp, 0);
+					tcp_ack_saw_tstamp(sk, 0);
 
 				if (tp->tstamp_ok)
 					tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/tcp_ipv4.c linux-2.4.23-web100-HSv4/net/ipv4/tcp_ipv4.c
--- linux-2.4.23/net/ipv4/tcp_ipv4.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/ipv4/tcp_ipv4.c	2004-03-01 01:37:29.000000000 +0000
@@ -849,6 +849,10 @@
 	if (!tp->write_seq)
 		tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
 							   sk->sport, usin->sin_port);
+	WEB100_VAR_SET(tp, SndISS, tp->write_seq);
+	WEB100_VAR_SET(tp, SndMax, tp->write_seq);
+	WEB100_VAR_SET(tp, SndNxt, tp->write_seq);
+	WEB100_VAR_SET(tp, SndUna, tp->write_seq);
 
 	sk->protinfo.af_inet.id = tp->write_seq^jiffies;
 
@@ -1028,8 +1032,14 @@
 		/* This is deprecated, but if someone generated it,
 		 * we have no reasons to ignore it.
 		 */
-		if (sk->lock.users == 0)
+		if (sk->lock.users == 0) {
 			tcp_enter_cwr(tp);
+			WEB100_VAR_INC(tp, QuenchRcvd);
+#if 0
+			/* WEB100_XXX */
+			WEB100_UPDATE_FUNC(tp, web100_update_cwnd(tp));
+#endif
+		}
 		goto out;
 	case ICMP_PARAMETERPROB:
 		err = EPROTO;
@@ -1557,6 +1567,13 @@
 	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (!newsk)
 		goto exit;
+#ifdef CONFIG_WEB100_STATS
+	if (web100_stats_create(newsk)) {
+		sk_free(newsk);
+		goto exit;
+	}
+	newsk->tp_pinfo.af_tcp.tcp_stats->wc_vars.LocalAddressType = WC_ADDRTYPE_IPV4;
+#endif
 
 	newsk->dst_cache = dst;
 	newsk->route_caps = dst->dev->features;
@@ -1772,12 +1789,14 @@
 	skb->dev = NULL;
 
 	bh_lock_sock(sk);
+	WEB100_UPDATE_FUNC(&sk->tp_pinfo.af_tcp, web100_update_segrecv(&sk->tp_pinfo.af_tcp, skb));
 	ret = 0;
 	if (!sk->lock.users) {
 		if (!tcp_prequeue(sk, skb))
 			ret = tcp_v4_do_rcv(sk, skb);
 	} else
 		sk_add_backlog(sk, skb);
+	WEB100_UPDATE_FUNC(&sk->tp_pinfo.af_tcp, web100_update_cwnd(&sk->tp_pinfo.af_tcp));
 	bh_unlock_sock(sk);
 
 	sock_put(sk);
@@ -2044,6 +2063,16 @@
 	sk->sndbuf = sysctl_tcp_wmem[1];
 	sk->rcvbuf = sysctl_tcp_rmem[1];
 
+#ifdef CONFIG_WEB100_STATS
+	{
+		int err;
+		if ((err = web100_stats_create(sk))) {
+			return err;
+		}
+		sk->tp_pinfo.af_tcp.tcp_stats->wc_vars.LocalAddressType = WC_ADDRTYPE_IPV4;
+	}
+#endif
+
 	atomic_inc(&tcp_sockets_allocated);
 
 	return 0;
@@ -2068,6 +2097,10 @@
 	if(sk->prev != NULL)
 		tcp_put_port(sk);
 
+#ifdef CONFIG_WEB100_STATS
+	web100_stats_destroy(sk->tp_pinfo.af_tcp.tcp_stats);
+#endif
+
 	/* If sendmsg cached page exists, toss it. */
 	if (tp->sndmsg_page != NULL)
 		__free_page(tp->sndmsg_page);
@@ -2288,6 +2321,7 @@
 	return len;
 }
 
+
 struct proto tcp_prot = {
 	name:		"TCP",
 	close:		tcp_close,
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/tcp_minisocks.c linux-2.4.23-web100-HSv4/net/ipv4/tcp_minisocks.c
--- linux-2.4.23/net/ipv4/tcp_minisocks.c	2003-08-25 12:44:44.000000000 +0100
+++ linux-2.4.23-web100-HSv4/net/ipv4/tcp_minisocks.c	2004-03-01 01:37:30.000000000 +0000
@@ -390,6 +390,9 @@
 			       sizeof(struct in6_addr));
 		}
 #endif
+
+		WEB100_VAR_SET(tp, State, WC_STATE_TIMEWAIT);
+
 		/* Linkage updates. */
 		__tcp_tw_hashdance(sk, tw);
 
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/tcp_output.c linux-2.4.23-web100-HSv4/net/ipv4/tcp_output.c
--- linux-2.4.23/net/ipv4/tcp_output.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/ipv4/tcp_output.c	2004-03-24 23:54:56.000000000 +0000
@@ -51,6 +51,7 @@
 	if (tp->send_head == (struct sk_buff *) &sk->write_queue)
 		tp->send_head = NULL;
 	tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+	WEB100_UPDATE_FUNC(tp, web100_update_snd_nxt(tp));
 	if (tp->packets_out++ == 0)
 		tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
 }
@@ -161,8 +162,26 @@
 		 */
 		new_win = cur_win;
 	}
+#if 0
+#ifdef CONFIG_WEB100_STATS
+	atomic_add(new_win - tp->rcv_wnd, &tcp_rwin_announced);
+	if (sysctl_web100_rbufmode == 1) {
+		if (new_win > 2*tp->rcv_wnd)
+			printk("tcp_select_window: large window %u\n", new_win);
+		if ((int)atomic_read(&tcp_rwin_announced) < 0) {
+			printk("tcp_select_window: BUG: tcp_rwin_announced < 0\n");
+		} else if (new_win > tcp_win_from_space(PAGE_SIZE * sysctl_tcp_mem[0]) -
+							 atomic_read(&tcp_rwin_announced)) {
+				printk("tcp_select_window: over bounds\n");
+			atomic_sub(new_win - tp->rcv_wnd, &tcp_rwin_announced);
+			new_win = tp->rcv_wnd;
+		}
+	}
+#endif
+#endif
 	tp->rcv_wnd = new_win;
 	tp->rcv_wup = tp->rcv_nxt;
+	WEB100_UPDATE_FUNC(tp, web100_update_rwin_sent(tp));
 
 	/* RFC1323 scaling applied */
 	new_win >>= tp->rcv_wscale;
@@ -174,6 +193,22 @@
 	return new_win;
 }
 
+#ifdef CONFIG_WEB100_STATS
+/* Start up a receiver-side window measurement using timestamps */
+static inline void tcp_rcv_tswin_start(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
+	if (tp->rcv_prev_tstamp != tcp_time_stamp) {
+		tp->rcv_prev_tstamp = tcp_time_stamp;
+		if (!tp->rcv_tswin_pending) {
+			tp->rcv_tswin_pending = 1;
+			tp->rcv_tswin_tstamp = tcp_time_stamp;
+			tp->rcv_tswin_seq = tp->rcv_hi_seq;
+		}
+	}
+}
+#endif
 
 /* This routine actually transmits TCP packets queued in by
  * tcp_do_sendmsg().  This is used by both the initial
@@ -200,6 +235,10 @@
 #define SYSCTL_FLAG_WSCALE	0x2
 #define SYSCTL_FLAG_SACK	0x4
 
+		/* XXX this is not correct --- we don't necessarily send the
+		   segment yet */
+		WEB100_UPDATE_FUNC(tp, web100_update_segsend(tp, skb));
+		
 		sysctl_flags = 0;
 		if (tcb->flags & TCPCB_FLAG_SYN) {
 			tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
@@ -267,6 +306,11 @@
 		}
 		tp->af_specific->send_check(sk, th, skb->len, skb);
 
+#ifdef CONFIG_WEB100_STATS
+		if (!(tcb->flags&(TCPCB_FLAG_SYN|TCPCB_FLAG_RST|TCPCB_FLAG_FIN)))
+			tcp_rcv_tswin_start(sk);
+#endif
+
 		if (tcb->flags & TCPCB_FLAG_ACK)
 			tcp_event_ack_sent(sk);
 
@@ -279,7 +323,11 @@
 		if (err <= 0)
 			return err;
 
+#ifdef CONFIG_WEB100_NET100
+		if (!NET100_WAD(tp, WAD_IFQ, sysctl_WAD_IFQ))
+#endif
 		tcp_enter_cwr(tp);
+		WEB100_VAR_INC(tp, SendStall);
 
 		/* NET_XMIT_CN is special. It does not guarantee,
 		 * that this packet is lost. It tells that device
@@ -295,7 +343,6 @@
 #undef SYSCTL_FLAG_SACK
 }
 
-
 /* This is the main buffer sending routine. We queue the buffer
  * and decide whether to queue or transmit now.
  *
@@ -305,23 +352,34 @@
 void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue, unsigned cur_mss)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	int why = WC_SNDLIM_NONE;
 
 	/* Advance write_seq and place onto the write_queue. */
 	tp->write_seq = TCP_SKB_CB(skb)->end_seq;
 	__skb_queue_tail(&sk->write_queue, skb);
 	tcp_charge_skb(sk, skb);
 
-	if (!force_queue && tp->send_head == NULL && tcp_snd_test(tp, skb, cur_mss, tp->nonagle)) {
+	if (!force_queue && tp->send_head == NULL &&
+		(why = tcp_snd_wait(tp, skb, cur_mss, tp->nonagle)) == WC_SNDLIM_NONE) {
 		/* Send it out now. */
 		TCP_SKB_CB(skb)->when = tcp_time_stamp;
 		if (tcp_transmit_skb(sk, skb_clone(skb, sk->allocation)) == 0) {
+#ifdef CONFIG_WEB100_STATS
+			if (tp->tcp_stats->wc_vars.X_SBufMode == WC_BUFMODE_WEB100)
+				tcp_retx_charge_skb(sk, skb);
+#endif
 			tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+			WEB100_UPDATE_FUNC(tp, web100_update_snd_nxt(tp));
 			tcp_minshall_update(tp, cur_mss, skb);
 			if (tp->packets_out++ == 0)
 				tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
 			return;
+		} else {
+			why = WC_SNDLIM_SENDER;
 		}
 	}
+	if (why != WC_SNDLIM_NONE)
+		WEB100_UPDATE_FUNC(tp, web100_update_sndlim(tp, why));
 	/* Queue it, remembering where we must start sending. */
 	if (tp->send_head == NULL)
 		tp->send_head = skb;
@@ -334,18 +392,28 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb = tp->send_head;
+	int why;
 
-	if (tcp_snd_test(tp, skb, cur_mss, 1)) {
+	if ((why = tcp_snd_wait(tp, skb, cur_mss, 1)) == WC_SNDLIM_NONE) {
 		/* Send it out now. */
 		TCP_SKB_CB(skb)->when = tcp_time_stamp;
 		if (tcp_transmit_skb(sk, skb_clone(skb, sk->allocation)) == 0) {
+#ifdef CONFIG_WEB100_STATS
+			if (tp->tcp_stats->wc_vars.X_SBufMode == WC_BUFMODE_WEB100)
+				tcp_retx_charge_skb(sk, skb);
+#endif
 			tp->send_head = NULL;
 			tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+			WEB100_UPDATE_FUNC(tp, web100_update_snd_nxt(tp));
 			if (tp->packets_out++ == 0)
 				tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
 			return;
+		} else {
+			why = WC_SNDLIM_SENDER;
 		}
 	}
+	if (why != WC_SNDLIM_NONE)
+		WEB100_UPDATE_FUNC(tp, web100_update_sndlim(tp, why));
 }
 
 /* Split fragmented skb to two parts at length len. */
@@ -424,6 +492,9 @@
 	int nsize = skb->len - len;
 	u16 flags;
 
+        clear_sacktag_hints(tp);
+        purge_sacked_list(tp);
+
 	if (skb_cloned(skb) &&
 	    skb_is_nonlinear(skb) &&
 	    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
@@ -532,6 +603,7 @@
 	/* And store cached results */
 	tp->pmtu_cookie = pmtu;
 	tp->mss_cache = mss_now;
+	WEB100_UPDATE_FUNC(tp, web100_update_mss(tp));
 	return mss_now;
 }
 
@@ -547,6 +619,9 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	unsigned int mss_now;
+	int why = WC_SNDLIM_NONE;
+        int packet_cnt = ((tp->ca_state==TCP_CA_Recovery)&&sysctl_tcp_htcp_moderation ? max(tcp_max_burst(tp),(__u32)tp->sack_cache.ack_count<<1)-tp->retrans_this_ack : tp->snd_cwnd);
+
 
 	/* If we are closed, the bytes will have to remain here.
 	 * In time closedown will finish, we empty the write queue and all
@@ -564,20 +639,32 @@
 		mss_now = tcp_current_mss(sk); 
 
 		while((skb = tp->send_head) &&
-		      tcp_snd_test(tp, skb, mss_now, tcp_skb_is_last(sk, skb) ? nonagle : 1)) {
+			  (why = tcp_snd_wait(tp, skb, mss_now,
+					  tcp_skb_is_last(sk, skb) ? tp->nonagle : 1))
+			  == WC_SNDLIM_NONE && packet_cnt) {
 			if (skb->len > mss_now) {
 				if (tcp_fragment(sk, skb, mss_now))
 					break;
 			}
 
 			TCP_SKB_CB(skb)->when = tcp_time_stamp;
-			if (tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC)))
+			if (tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC))) {
+				why = WC_SNDLIM_SENDER;
 				break;
+			}
+                        packet_cnt--;
+#ifdef CONFIG_WEB100_STATS
+			if (tp->tcp_stats->wc_vars.X_SBufMode == WC_BUFMODE_WEB100)
+				tcp_retx_charge_skb(sk, skb);
+#endif
 			/* Advance the send_head.  This one is sent out. */
 			update_send_head(sk, tp, skb);
 			tcp_minshall_update(tp, mss_now, skb);
 			sent_pkts = 1;
 		}
+		if (why == WC_SNDLIM_NONE)
+			why = WC_SNDLIM_SENDER;
+		WEB100_UPDATE_FUNC(tp, web100_update_sndlim(tp, why));
 
 		if (sent_pkts) {
 			tcp_cwnd_validate(sk, tp);
@@ -655,6 +742,19 @@
 	int full_space = min_t(int, tp->window_clamp, tcp_full_space(sk));
 	int window;
 
+#ifdef CONFIG_WEB100_STATS
+	WEB100_VAR_SET(tp, X_dbg4, full_space);
+	
+	if (tp->tcp_stats->wc_vars.X_RBufMode == WC_BUFMODE_WEB100) {
+		window = tp->rcv_space - max(tp->rcv_alloc - sk->rcvbuf, 0) +
+				 min_t(int, tp->rcv_hi_seq - tp->rcv_nxt,
+					   tp->rcv_space);
+		
+		window = max(window, 0);
+		window = min_t(__u32, window, tp->tcp_stats->wc_vars.LimRwin);
+		return (u32)window;
+	}
+#endif
 	if (mss > full_space)
 		mss = full_space; 
 
@@ -683,6 +783,9 @@
 	if (window <= free_space - mss || window > free_space)
 		window = (free_space/mss)*mss;
 
+	WEB100_VAR_SET(tp, X_dbg3, free_space);
+	WEB100_VAR_SET(tp, X_dbg2, mss);
+	WEB100_VAR_SET(tp, X_dbg1, window);
 	return window;
 }
 
@@ -716,6 +819,9 @@
 			return;
 
 		/* Ok.  We will be able to collapse the packet. */
+                purge_sacked_list(tp); // could be smarter here if need be.
+                clear_sacktag_hints(tp);
+
 		__skb_unlink(next_skb, next_skb->list);
 
 		memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size);
@@ -770,6 +876,8 @@
 	unsigned int mss = tcp_current_mss(sk);
 	int lost = 0;
 
+        purge_sacked_list(tp); clear_sacktag_hints(tp);
+
 	for_retrans_queue(skb, sk, tp) {
 		if (skb->len > mss && 
 		    !(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) {
@@ -912,20 +1020,44 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
-	int packet_cnt = tp->lost_out;
+        struct sk_buff *sl;
+	int packet_cnt; 
+
+        if (tp->xmit_retransmit_queue_skb_hint) {
+           sl = tp->xmit_retransmit_queue_skb_hint;
+           packet_cnt = tp->xmit_retransmit_queue_cnt_hint;
+        } else {
+           sl = sacked_list_head(tp);
+           packet_cnt=0;
+        }
 
 	/* First pass: retransmit lost packets. */
-	if (packet_cnt) {
-		for_retrans_queue(skb, sk, tp) {
+	if (tp->lost_out) {
+	
+                if (sl == NULL)
+                   skb = (sk)->write_queue.next;
+                else {
+                   skb = sl;
+                }
+                BUG_TRAP(skb!=NULL);
+
+		while ((skb != (tp)->send_head) && (skb != (struct sk_buff *)&(sk)->write_queue)) {
+		
 			__u8 sacked = TCP_SKB_CB(skb)->sacked;
 
+                        tp->xmit_retransmit_queue_skb_hint = skb;
+                        tp->xmit_retransmit_queue_cnt_hint = packet_cnt;
+
 			if (tcp_packets_in_flight(tp) >= tp->snd_cwnd)
 				return;
 
-			if (sacked&TCPCB_LOST) {
-				if (!(sacked&(TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS))) {
-					if (tcp_retransmit_skb(sk, skb))
+			if (sacked&(TCPCB_LOST)) {
+				if (!(sacked&(TCPCB_SACKED_ACKED|TCPCB_SACKED_RETRANS)) )
+				if ( !before(TCP_SKB_CB(skb)->end_seq,tp->snd_una) ) {
+					if (tcp_retransmit_skb(sk, skb)) {
+                                                tp->xmit_retransmit_queue_skb_hint = NULL;
 						return;
+                                        }
 					if (tp->ca_state != TCP_CA_Loss)
 						NET_INC_STATS_BH(TCPFastRetrans);
 					else
@@ -935,9 +1067,15 @@
 						tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
 				}
 
-				if (--packet_cnt <= 0)
+                                if (++packet_cnt >= tp->lost_out)
 					break;
 			}
+
+			if (sl != NULL) sl = TCP_SKB_CB(sl)->next; 
+			if (sl == NULL)
+			   skb = skb->next;
+			else 
+                           skb=sl;
 		}
 	}
 
@@ -965,6 +1103,7 @@
 
 	for_retrans_queue(skb, sk, tp) {
 		if(++packet_cnt > tp->fackets_out)
+
 			break;
 
 		if (tcp_packets_in_flight(tp) >= tp->snd_cwnd)
@@ -1196,6 +1335,7 @@
 	tp->snd_wnd = 0;
 	tcp_init_wl(tp, tp->write_seq, 0);
 	tp->snd_una = tp->write_seq;
+	WEB100_VAR_SET(tp, SndUna, tp->snd_una);
 	tp->snd_sml = tp->write_seq;
 	tp->rcv_nxt = 0;
 	tp->rcv_wup = 0;
@@ -1230,6 +1370,7 @@
 	TCP_SKB_CB(buff)->seq = tp->write_seq++;
 	TCP_SKB_CB(buff)->end_seq = tp->write_seq;
 	tp->snd_nxt = tp->write_seq;
+	WEB100_UPDATE_FUNC(tp, web100_update_snd_nxt(tp));
 	tp->pushed_seq = tp->write_seq;
 
 	/* Send it off. */
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/tcp_timer.c linux-2.4.23-web100-HSv4/net/ipv4/tcp_timer.c
--- linux-2.4.23/net/ipv4/tcp_timer.c	2003-06-13 15:51:39.000000000 +0100
+++ linux-2.4.23-web100-HSv4/net/ipv4/tcp_timer.c	2004-03-01 01:37:30.000000000 +0000
@@ -371,6 +371,7 @@
 			NET_INC_STATS_BH(TCPTimeouts);
 		}
 	}
+	WEB100_UPDATE_FUNC(tp, web100_update_timeout(tp));
 
 	if (tcp_use_frto(sk)) {
 		tcp_enter_frto(sk);
@@ -405,6 +406,7 @@
 	 * the 120 second clamps though!
 	 */
 	tp->backoff++;
+	WEB100_VAR_SET(tp, CurTimeoutCount, tp->backoff);
 	tp->retransmits++;
 
 out_reset_timer:
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv4/web100_stats.c linux-2.4.23-web100-HSv4/net/ipv4/web100_stats.c
--- linux-2.4.23/net/ipv4/web100_stats.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.23-web100-HSv4/net/ipv4/web100_stats.c	2004-03-15 12:58:51.000000000 +0000
@@ -0,0 +1,834 @@
+/*	
+ * net/ipv4/web100_stats.c
+ *
+ * Copyright (C) 2001 Matt Mathis <mathis@psc.edu>
+ * Copyright (C) 2001 John Heffner <jheffner@psc.edu>
+ * Copyright (C) 2000 Jeffrey Semke <semke@psc.edu>
+ *
+ * The Web 100 project.  See http://www.web100.org
+ *
+ *	Functions for creating, destroying, and updating the Web100
+ *	statistics structure.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <net/web100.h>
+#include <net/tcp.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <asm/atomic.h>
+
+#define WC_INF32	0xffffffff
+
+#define WC_DEATH_SLOTS	8
+#define WC_PERSIST_TIME	60
+
+/* BEWARE: The release process updates the version string */
+char *web100_version_string = "2.3.2 200312021603"
+#ifdef CONFIG_WEB100_NET100
+    " net100"
+#endif
+    ;
+
+static void death_cleanup(unsigned long dummy);
+
+/* Global stats reader-writer lock */
+rwlock_t web100_linkage_lock = RW_LOCK_UNLOCKED;
+
+/* Data structures for tying together stats */
+static int web100stats_next_cid;
+static int web100stats_conn_num;
+static int web100stats_htsize;
+struct web100stats **web100stats_ht;
+struct web100stats *web100stats_first = NULL;
+
+static struct web100stats *death_slots[WC_DEATH_SLOTS];
+static int cur_death_slot;
+static spinlock_t death_lock = SPIN_LOCK_UNLOCKED;
+static struct timer_list stats_persist_timer = { function: death_cleanup };
+static int ndeaths;
+
+#ifdef CONFIG_WEB100_NETLINK
+static struct sock *web100_nlsock;
+#endif
+
+extern struct proc_dir_entry *proc_web100_dir;
+
+
+/*
+ * Structural maintainance
+ */
+
+static inline int web100stats_hash(int cid)
+{
+	return cid % web100stats_htsize;
+}
+
+struct web100stats *web100stats_lookup(int cid)
+{
+	struct web100stats *stats;
+	
+	/* Let's ensure safety here.  It's not too expensive and may change. */
+	if (cid < 0 || cid >= WEB100_MAX_CONNS)
+		return NULL;
+	
+	stats = web100stats_ht[web100stats_hash(cid)];
+	while (stats && stats->wc_cid != cid)
+		stats = stats->wc_hash_next;
+	return stats;
+}
+
+/* This will get really slow as the cid space fills.  This can be done
+ * better, but it's just not worth it right now.
+ * The caller must hold the lock.
+ */
+static int get_next_cid(void)
+{
+	int i;
+	
+	if (web100stats_conn_num >= WEB100_MAX_CONNS)
+		return -1;
+	
+	i = web100stats_next_cid;
+	do {
+		if (web100stats_lookup(i) == NULL)
+			break;
+		i = (i + 1) % WEB100_MAX_CONNS;
+	} while (i != web100stats_next_cid);
+	web100stats_next_cid = (i + 1) % WEB100_MAX_CONNS;
+	
+	return i;
+}
+
+static void stats_link(struct web100stats *stats)
+{
+	int hash;
+	
+	write_lock_bh(&web100_linkage_lock);
+	
+	if ((stats->wc_cid = get_next_cid()) < 0) {
+		write_unlock_bh(&web100_linkage_lock);
+		return;
+	}
+	
+	hash = web100stats_hash(stats->wc_cid);
+	stats->wc_hash_next = web100stats_ht[hash];
+	stats->wc_hash_prev = NULL;
+	if (web100stats_ht[hash])
+		web100stats_ht[hash]->wc_hash_prev = stats;
+	web100stats_ht[hash] = stats;
+	
+	stats->wc_next = web100stats_first;
+	stats->wc_prev = NULL;
+	if (web100stats_first)
+		web100stats_first->wc_prev = stats;
+	web100stats_first = stats;
+	
+	web100stats_conn_num++;
+	proc_web100_dir->nlink = web100stats_conn_num + 2;
+	
+	write_unlock_bh(&web100_linkage_lock);
+}
+
+static void stats_unlink(struct web100stats *stats)
+{
+	int hash;
+	
+	write_lock_bh(&web100_linkage_lock);
+	
+	hash = web100stats_hash(stats->wc_cid);
+	if (stats->wc_hash_next)
+		stats->wc_hash_next->wc_hash_prev = stats->wc_hash_prev;
+	if (stats->wc_hash_prev)
+		stats->wc_hash_prev->wc_hash_next = stats->wc_hash_next;
+	if (stats == web100stats_ht[hash])
+		web100stats_ht[hash] = stats->wc_hash_next ?
+					stats->wc_hash_next :
+					stats->wc_hash_prev;
+	
+	if (stats->wc_next)
+		stats->wc_next->wc_prev = stats->wc_prev;
+	if (stats->wc_prev)
+		stats->wc_prev->wc_next = stats->wc_next;
+	if (stats == web100stats_first)
+		web100stats_first = stats->wc_next ? stats->wc_next :
+						      stats->wc_prev;
+	
+	web100stats_conn_num--;
+	proc_web100_dir->nlink = web100stats_conn_num + 2;
+	
+	write_unlock_bh(&web100_linkage_lock);
+}
+
+static void stats_persist(struct web100stats *stats)
+{
+	spin_lock_bh(&death_lock);
+	
+	stats->wc_death_next = death_slots[cur_death_slot];
+	death_slots[cur_death_slot] = stats;
+	if (ndeaths <= 0) {
+		stats_persist_timer.expires = jiffies + WC_PERSIST_TIME * HZ / WC_DEATH_SLOTS;
+		add_timer(&stats_persist_timer);
+	}
+	ndeaths++;
+	
+	spin_unlock_bh(&death_lock);
+}
+
+static void death_cleanup(unsigned long dummy)
+{
+	struct web100stats *stats, *next;
+	
+	spin_lock_bh(&death_lock);
+	
+	cur_death_slot = (cur_death_slot + 1) % WC_DEATH_SLOTS;
+	stats = death_slots[cur_death_slot];
+	while (stats) {
+		stats->wc_dead = 1;
+		ndeaths--;
+		next = stats->wc_death_next;
+		web100_stats_unuse(stats);
+		stats = next;
+	}
+	death_slots[cur_death_slot] = NULL;
+
+	if (ndeaths > 0) {
+		stats_persist_timer.expires = jiffies + WC_PERSIST_TIME * HZ / WC_DEATH_SLOTS;
+		add_timer(&stats_persist_timer);
+	}
+	
+	spin_unlock_bh(&death_lock);
+}
+
+
+/* Tom Dunigan's (slightly modified) netlink code.  Notifies listening apps
+ * of Web100 events.
+ *
+ * NOTE: we are currently squatting on netlink family 10 (NETLINK_WEB100) in
+ * include/linux/netlink.h
+ */
+
+#ifdef CONFIG_WEB100_NETLINK
+void web100_netlink_event(int type, int cid)
+{
+	struct web100_netlink_msg *msg;
+	struct sk_buff *tmpskb;
+	
+	if (web100_nlsock == NULL)
+		return;
+	
+	if ((tmpskb = alloc_skb((sizeof (struct web100_netlink_msg)), GFP_ATOMIC)) == NULL) {
+		printk(KERN_INFO "web100_netlink_event: alloc_skb failure\n");
+		return;
+	}
+	
+	skb_put(tmpskb, sizeof (struct web100_netlink_msg));
+	msg = (struct web100_netlink_msg *)tmpskb->data;
+	msg->type = type;
+	msg->cid = cid;
+	netlink_broadcast(web100_nlsock, tmpskb, 0, ~0, GFP_ATOMIC);
+}
+#endif /* CONFIG_WEB100_NETLINK */
+
+extern __u32 sysctl_wmem_default;
+extern __u32 sysctl_rmem_default;
+
+/* Called whenever a TCP/IPv4 sock is created.
+ * net/ipv4/tcp_ipv4.c: tcp_v4_syn_recv_sock,
+ *			tcp_v4_init_sock
+ * Allocates a stats structure and initializes values.
+ */
+int web100_stats_create(struct sock *sk)
+{
+	struct web100stats *stats;
+	struct web100directs *vars;
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct timeval tv;
+	
+	if ((stats = kmalloc(sizeof (struct web100stats), gfp_any())) == NULL)
+		return -ENOMEM;
+	sk->tp_pinfo.af_tcp.tcp_stats = stats;
+	vars = &stats->wc_vars;
+	
+	memset(stats, 0, sizeof (struct web100stats));
+	
+	stats->wc_cid = -1;
+	stats->wc_sk = sk;
+	atomic_set(&stats->wc_users, 0);
+	
+	stats->wc_limstate = WC_SNDLIM_STARTUP;
+	do_gettimeofday(&stats->wc_limstate_time);
+	
+	vars->NagleEnabled = !(sk->tp_pinfo.af_tcp.nonagle);
+	vars->ActiveOpen = !in_interrupt();
+	
+	vars->SndUna = tp->snd_una;
+	vars->SndNxt = tp->snd_nxt;
+	vars->SndMax = tp->snd_nxt;
+	vars->SndISS = tp->snd_nxt;
+	
+	do_gettimeofday(&tv);
+	vars->StartTime = tv.tv_sec * 10 + tv.tv_usec / 100000;
+	vars->StartTimeSec = tv.tv_sec;
+	vars->StartTimeUsec = tv.tv_usec;
+	stats->wc_start_monotime = web100_mono_time();
+	
+	vars->MinRTT = vars->MinRTO = vars->MinMSS = vars->MinRwinRcvd =
+		vars->MinRwinSent = vars->MinSsthresh = WC_INF32;
+	
+	if (sysctl_web100_sbufmode == WC_BUFMODE_OS) {
+		vars->X_SBufMode = WC_BUFMODE_OS;
+	} else {
+		vars->X_SBufMode = WC_BUFMODE_WEB100;
+		if (!(sk->userlocks & SOCK_SNDBUF_LOCK)) {
+			sk->userlocks |= SOCK_SNDBUF_LOCK;
+			sk->sndbuf = sysctl_wmem_default;
+		}
+	}
+	if (sysctl_web100_rbufmode == WC_BUFMODE_OS) {
+		vars->X_RBufMode = WC_BUFMODE_OS;
+		vars->LimRwin = tp->window_clamp;
+	} else {
+		vars->X_RBufMode = WC_BUFMODE_WEB100;
+		if (!(sk->userlocks & SOCK_RCVBUF_LOCK)) {
+			sk->userlocks |= SOCK_RCVBUF_LOCK;
+			sk->rcvbuf = sysctl_rmem_default;
+		}
+		vars->LimRwin = WC_INF32;
+	}
+	
+	if (sysctl_web100_scalable_tcp) {
+		vars->WAD_Kaicnt = 50;	/* a = 1/100, but account for delayed ack */
+		vars->WAD_MD = 32;	/* b = 1/8 from the Kelly paper */
+	}
+	
+#ifdef CONFIG_WEB100_NET100
+	stats->wc_flindex = 1;
+#endif
+	web100_stats_use(stats);
+	
+	return 0;
+}
+
+void web100_stats_destroy(struct web100stats *stats)
+{
+	atomic_sub(stats->wc_sk->tp_pinfo.af_tcp.rcv_wnd, &tcp_rwin_announced);
+	
+	/* Attribute final sndlim time. */
+	web100_update_sndlim(&stats->wc_sk->tp_pinfo.af_tcp, stats->wc_limstate);
+	
+	if (stats->wc_cid >= 0) {
+#ifdef CONFIG_WEB100_NETLINK
+		web100_netlink_event(WC_NL_TYPE_DISCONNECT, stats->wc_cid);
+#endif
+		stats_persist(stats);
+	} else {
+		web100_stats_unuse(stats);
+	}
+}
+
+/* Do not call directly.  Called from web100_stats_unuse(). */
+void web100_stats_free(struct web100stats *stats)
+{
+	if (stats->wc_cid >= 0) {
+		stats_unlink(stats);
+	}
+	kfree(stats);
+}
+
+extern __u32 sysctl_wmem_default;
+extern __u32 sysctl_rmem_default;
+
+/* Called when a connection enters the ESTABLISHED state, and has all its
+ * state initialized.
+ * net/ipv4/tcp_input.c: tcp_rcv_state_process,
+ *			 tcp_rcv_synsent_state_process
+ * Here we link the statistics structure in so it is visible in the /proc
+ * fs, and do some final init.
+ */
+void web100_stats_establish(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct web100stats *stats = tp->tcp_stats;
+	struct web100directs *vars = &stats->wc_vars;
+	
+	tp->rcv_hi_seq = tp->rcv_nxt;
+	atomic_add(tp->rcv_wnd, &tcp_rwin_announced);
+	
+	if (stats == NULL)
+		return;
+	
+	/* Let's set these here, since they can't change once the
+	 * connection is established.
+	 */
+	vars->LocalPort = sk->num;
+	vars->RemPort = ntohs(sk->dport);
+	
+	if (vars->LocalAddressType == WC_ADDRTYPE_IPV4) {
+		vars->LocalAddress.v4addr = sk->rcv_saddr;
+		vars->RemAddress.v4addr = sk->daddr;
+	}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	else if (vars->LocalAddressType == WC_ADDRTYPE_IPV6) {
+		memcpy(&vars->LocalAddress.v6addr.addr, &sk->net_pinfo.af_inet6.saddr, 16);
+		memcpy(&vars->RemAddress.v6addr.addr, &sk->net_pinfo.af_inet6.daddr, 16);
+	}
+#endif
+	else {
+		printk(KERN_ERR "Web100: LocalAddressType not valid.\n");
+	}
+	vars->LocalAddress.v6addr.type = vars->RemAddress.v6addr.type = vars->LocalAddressType;
+	
+	vars->SACKEnabled = tp->sack_ok;
+	vars->TimestampsEnabled = tp->tstamp_ok;
+#ifdef CONFIG_INET_ECN
+	vars->ECNEnabled = tp->ecn_flags & TCP_ECN_OK;
+#endif
+	
+	if (tp->wscale_ok) {
+		vars->WinScaleRcvd = tp->snd_wscale;
+		vars->WinScaleSent = tp->rcv_wscale;
+	} else {
+		vars->WinScaleRcvd = -1;
+		vars->WinScaleSent = -1;
+	}
+	vars->SndWinScale = vars->WinScaleRcvd;
+	vars->RcvWinScale = vars->WinScaleSent;
+	
+	vars->CurCwnd = tp->snd_cwnd * tp->mss_cache;
+	vars->CurSsthresh = tp->snd_ssthresh * tp->mss_cache;
+	
+	vars->RecvISS = vars->RcvNxt = tp->rcv_nxt;
+	
+	vars->RetranThresh = tp->reordering;
+	
+	vars->LimRwin = min_t(__u32, vars->LimRwin, 65355U << tp->rcv_wscale);
+	
+	stats_link(stats);
+	
+	web100_update_sndlim(tp, WC_SNDLIM_SENDER);
+	
+#ifdef CONFIG_WEB100_NETLINK
+	web100_netlink_event(WC_NL_TYPE_CONNECT, stats->wc_cid);
+#endif
+}
+
+/*
+ * Statistics update functions
+ */
+
+void web100_update_snd_nxt(struct tcp_opt *tp)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	
+	if (after(tp->snd_nxt, stats->wc_vars.SndMax)) {
+		if (before(stats->wc_vars.SndMax, stats->wc_vars.SndISS) &&
+		    after(tp->snd_nxt, stats->wc_vars.SndISS))
+			stats->wc_vars.SendWraps++;
+		stats->wc_vars.ThruBytesAcked += (__u32) (tp->snd_nxt - stats->wc_vars.SndMax); /* XXX */
+		stats->wc_vars.SndMax = tp->snd_nxt;
+	}
+	stats->wc_vars.SndNxt = tp->snd_nxt;
+}
+
+void web100_update_rtt(struct tcp_opt *tp, unsigned long rtt_sample)
+{
+	struct web100stats *stats = tp->tcp_stats;
+        unsigned long rtt_sample_msec = rtt_sample * 1000 / HZ;
+        __u32 rto;
+	
+	stats->wc_vars.SampleRTT = rtt_sample_msec;
+
+	if (rtt_sample_msec > stats->wc_vars.MaxRTT)
+	        stats->wc_vars.MaxRTT = rtt_sample_msec;
+	if (rtt_sample_msec < stats->wc_vars.MinRTT)
+		stats->wc_vars.MinRTT = rtt_sample_msec;
+	
+	stats->wc_vars.CountRTT++;
+	stats->wc_vars.SumRTT += rtt_sample_msec;
+
+	if (stats->wc_vars.PreCongCountRTT != stats->wc_vars.PostCongCountRTT) {
+		stats->wc_vars.PostCongCountRTT++;
+		stats->wc_vars.PostCongSumRTT += rtt_sample_msec;
+	}
+	
+	/* srtt is stored as 8 * the smoothed estimate */
+	stats->wc_vars.SmoothedRTT =
+		(tp->srtt>>3) * 1000 / HZ;
+	
+	rto = tp->rto * 1000 / HZ;
+	if (rto > stats->wc_vars.MaxRTO)
+		stats->wc_vars.MaxRTO = rto;
+	if (rto < stats->wc_vars.MinRTO)
+		stats->wc_vars.MinRTO = rto;
+	stats->wc_vars.CurRTO = rto;
+
+	stats->wc_vars.CurTimeoutCount = 0;
+	
+	stats->wc_vars.RTTVar = (tp->rttvar >> 2) * 1000 / HZ;
+}
+
+void web100_update_timeout(struct tcp_opt *tp) {
+	struct web100stats *stats = tp->tcp_stats;
+
+	stats->wc_vars.CurTimeoutCount++;
+	if (tp->backoff)
+		stats->wc_vars.SubsequentTimeouts++;
+	else
+		stats->wc_vars.Timeouts++;
+	if (tp->ca_state == TCP_CA_Open)
+		stats->wc_vars.AbruptTimeouts++;
+}
+
+void web100_update_mss(struct tcp_opt *tp)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	int mss = tp->mss_cache;
+	
+	stats->wc_vars.CurMSS = mss;
+	if (mss > stats->wc_vars.MaxMSS)
+		stats->wc_vars.MaxMSS = mss;
+	if (mss < stats->wc_vars.MinMSS)
+		stats->wc_vars.MinMSS = mss;
+}
+
+void web100_update_cwnd(struct tcp_opt *tp)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	__u16 mss = tp->mss_cache;
+	__u32 cwnd;
+	__u32 ssthresh;
+	
+	if (mss == 0) {
+		printk("Web100: web100_update_cwnd: mss == 0\n");
+		return;
+	}
+
+        if (tp->ca_state == TCP_CA_Open && tp->snd_ccount>3)
+           stats->wc_vars.Bi = tp->snd_Bi * mss*8;
+	
+	cwnd = min(WC_INF32 / mss, tp->snd_cwnd) * mss;
+	stats->wc_vars.CurCwnd = cwnd;
+	if (cwnd > stats->wc_vars.MaxCwnd)
+		stats->wc_vars.MaxCwnd = cwnd;
+	
+	ssthresh = min(WC_INF32 / mss, tp->snd_ssthresh) * mss;
+	stats->wc_vars.CurSsthresh = ssthresh;
+	
+	/* Discard initiail ssthresh set at infinity. */
+	if (tp->snd_ssthresh >= 0x7ffffff) {
+		return;
+	}
+	if (ssthresh > stats->wc_vars.MaxSsthresh)
+		stats->wc_vars.MaxSsthresh = ssthresh;
+	if (ssthresh < stats->wc_vars.MinSsthresh)
+		stats->wc_vars.MinSsthresh = ssthresh;
+}
+
+void web100_update_rwin_rcvd(struct tcp_opt *tp)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	__u32 win = tp->snd_wnd;
+	
+	stats->wc_vars.CurRwinRcvd = win;
+	if (win > stats->wc_vars.MaxRwinRcvd)
+		stats->wc_vars.MaxRwinRcvd = win;
+	if (win < stats->wc_vars.MinRwinRcvd)
+		stats->wc_vars.MinRwinRcvd = win;
+}
+
+void web100_update_rwin_sent(struct tcp_opt *tp)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	__u32 win = tp->rcv_wnd;
+
+	/* Update our advertised window. */
+	stats->wc_vars.CurRwinSent = win;
+	if (win > stats->wc_vars.MaxRwinSent)
+		stats->wc_vars.MaxRwinSent = win;
+	if (win < stats->wc_vars.MinRwinSent)
+		stats->wc_vars.MinRwinSent = win;
+}
+
+
+/* TODO: change this to a generic state machine instrument */
+static void web100_state_update(struct tcp_opt *tp, int why, __u64 bytes)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	struct timeval now;
+	
+	do_gettimeofday(&now);
+	stats->wc_vars.SndLimTime[stats->wc_limstate] +=
+		(1000000*(now.tv_sec - stats->wc_limstate_time.tv_sec)) +
+		((signed)(now.tv_usec) - stats->wc_limstate_time.tv_usec);
+	memcpy(&stats->wc_limstate_time, &now, sizeof (struct timeval));
+	
+	stats->wc_vars.SndLimBytes[why] += bytes - stats->wc_limstate_bytes;
+	stats->wc_limstate_bytes = bytes;
+	
+	if (stats->wc_limstate != why) {
+		stats->wc_limstate = why;
+		stats->wc_vars.SndLimTrans[why]++;
+	}
+}
+
+void web100_update_sndlim(struct tcp_opt *tp, int why)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	
+	if (why < 0) {
+		printk("web100_update_sndlim: BUG: why < 0\n");
+		return;
+	}
+	
+	web100_state_update(tp, why, stats->wc_vars.DataBytesOut);
+	/* future instruments on other sender bottlenecks here... */
+	/* if (!why) { why = ??? } */
+	/* web100_state_update(tp, why, stats->wc_vars.DataBytesOut); */
+}
+
+void web100_update_congestion(struct tcp_opt *tp, int why_dummy)
+{
+       	struct web100stats *stats = tp->tcp_stats;
+	
+	stats->wc_vars.CongestionSignals++;
+	stats->wc_vars.PreCongSumCwnd += stats->wc_vars.CurCwnd;
+
+	/* This may require more control flags */
+	stats->wc_vars.PreCongCountRTT++;
+	stats->wc_vars.PreCongSumRTT += stats->wc_vars.SampleRTT;
+}
+
+/* Called from tcp_transmit_skb, whenever we push a segment onto the wire.
+ * This must be called before the header is pushed onto the skb.
+ */
+void web100_update_segsend(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	
+	/* We know we're sending a segment. */
+	stats->wc_vars.PktsOut++;
+	
+	/* We know the ack seq is rcv_nxt. web100_XXX bug compatible*/
+	web100_update_rcv_nxt(tp);
+	
+	/* A pure ACK contains no data; everything else is data. */
+	if (skb->len > 0) {
+		stats->wc_vars.DataPktsOut++;
+		stats->wc_vars.DataBytesOut += skb->len;
+	} else {
+		stats->wc_vars.AckPktsOut++;
+	}
+	
+	/* Check for retransmission. */
+	if (before(TCP_SKB_CB(skb)->seq, stats->wc_vars.SndMax)) {
+		stats->wc_vars.PktsRetrans++;
+		stats->wc_vars.BytesRetrans += skb->len;
+	}
+}
+
+void web100_update_segrecv(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	struct web100directs *vars = &tp->tcp_stats->wc_vars;
+	struct tcphdr *th = skb->h.th;
+	
+	vars->PktsIn++;
+	if (skb->len == th->doff*4) {
+		vars->AckPktsIn++;
+		if (TCP_SKB_CB(skb)->ack_seq == tp->snd_una)
+			vars->DupAcksIn++;
+	} else {
+		vars->DataPktsIn++;
+		vars->DataBytesIn += skb->len - th->doff*4;
+	}
+}
+
+void web100_update_rcv_nxt(struct tcp_opt *tp)
+{
+	struct web100stats *stats = tp->tcp_stats;
+	
+	if (before(stats->wc_vars.RcvNxt, stats->wc_vars.RecvISS) &&
+	     after(tp->rcv_nxt, stats->wc_vars.RecvISS))
+		stats->wc_vars.RecvWraps++;
+	stats->wc_vars.ThruBytesReceived += (__u32) (tp->rcv_nxt - stats->wc_vars.RcvNxt); /* XXX */
+	stats->wc_vars.RcvNxt = tp->rcv_nxt;
+}
+
+void web100_update_writeq(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct web100directs *vars = &tp->tcp_stats->wc_vars;
+	int len = tp->write_seq - vars->SndMax;
+	
+	vars->CurAppWQueue = len;
+	if (len > vars->MaxAppWQueue)
+		vars->MaxAppWQueue = len;
+}
+
+void web100_update_recvq(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct web100directs *vars = &tp->tcp_stats->wc_vars;
+	int len = tp->rcv_nxt - tp->copied_seq;
+	
+	tp->rcv_alloc = len;
+	vars->CurAppRQueue = len;
+	if (vars->MaxAppRQueue < len)
+		vars->MaxAppRQueue = len;
+}
+
+void web100_update_ofoq(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct web100directs *vars = &tp->tcp_stats->wc_vars;
+	int len = tp->rcv_hi_seq - tp->rcv_nxt;
+	
+	vars->CurReasmQueue = len;
+	if (vars->MaxReasmQueue < len)
+		vars->MaxReasmQueue = len;
+}
+
+#ifdef CONFIG_WEB100_NET100
+/* floyd aimd calculator based on tables in appendix B, May '02  */
+/*   values are already binary fractions for WAD_AI and WAD_MD */
+static struct Aimd_Vals {
+        unsigned int cwnd;
+        unsigned int increment;
+        unsigned int decrement;
+} aimd_vals[] = {
+ {      0,     8, /*     1 */   128, /*  0.50 */ },
+ {     38,     8, /*     1 */   128, /*  0.50 */ },
+ {    118,    16, /*     2 */   112, /*  0.44 */ },
+ {    221,    24, /*     3 */   104, /*  0.41 */ },
+ {    347,    32, /*     4 */    98, /*  0.38 */ },
+ {    495,    40, /*     5 */    93, /*  0.37 */ },
+ {    663,    48, /*     6 */    89, /*  0.35 */ },
+ {    851,    56, /*     7 */    86, /*  0.34 */ },
+ {   1058,    64, /*     8 */    83, /*  0.33 */ },
+ {   1284,    72, /*     9 */    81, /*  0.32 */ },
+ {   1529,    80, /*    10 */    78, /*  0.31 */ },
+ {   1793,    88, /*    11 */    76, /*  0.30 */ },
+ {   2076,    96, /*    12 */    74, /*  0.29 */ },
+ {   2378,   104, /*    13 */    72, /*  0.28 */ },
+ {   2699,   112, /*    14 */    71, /*  0.28 */ },
+ {   3039,   120, /*    15 */    69, /*  0.27 */ },
+ {   3399,   128, /*    16 */    68, /*  0.27 */ },
+ {   3778,   136, /*    17 */    66, /*  0.26 */ },
+ {   4177,   144, /*    18 */    65, /*  0.26 */ },
+ {   4596,   152, /*    19 */    64, /*  0.25 */ },
+ {   5036,   160, /*    20 */    62, /*  0.25 */ },
+ {   5497,   168, /*    21 */    61, /*  0.24 */ },
+ {   5979,   176, /*    22 */    60, /*  0.24 */ },
+ {   6483,   184, /*    23 */    59, /*  0.23 */ },
+ {   7009,   192, /*    24 */    58, /*  0.23 */ },
+ {   7558,   200, /*    25 */    57, /*  0.22 */ },
+ {   8130,   208, /*    26 */    56, /*  0.22 */ },
+ {   8726,   216, /*    27 */    55, /*  0.22 */ },
+ {   9346,   224, /*    28 */    54, /*  0.21 */ },
+ {   9991,   232, /*    29 */    53, /*  0.21 */ },
+ {  10661,   240, /*    30 */    52, /*  0.21 */ },
+ {  11358,   248, /*    31 */    52, /*  0.20 */ },
+ {  12082,   256, /*    32 */    51, /*  0.20 */ },
+ {  12834,   264, /*    33 */    50, /*  0.20 */ },
+ {  13614,   272, /*    34 */    49, /*  0.19 */ },
+ {  14424,   280, /*    35 */    48, /*  0.19 */ },
+ {  15265,   288, /*    36 */    48, /*  0.19 */ },
+ {  16137,   296, /*    37 */    47, /*  0.19 */ },
+ {  17042,   304, /*    38 */    46, /*  0.18 */ },
+ {  17981,   312, /*    39 */    45, /*  0.18 */ },
+ {  18955,   320, /*    40 */    45, /*  0.18 */ },
+ {  19965,   328, /*    41 */    44, /*  0.17 */ },
+ {  21013,   336, /*    42 */    43, /*  0.17 */ },
+ {  22101,   344, /*    43 */    43, /*  0.17 */ },
+ {  23230,   352, /*    44 */    42, /*  0.17 */ },
+ {  24402,   360, /*    45 */    41, /*  0.16 */ },
+ {  25618,   368, /*    46 */    41, /*  0.16 */ },
+ {  26881,   376, /*    47 */    40, /*  0.16 */ },
+ {  28193,   384, /*    48 */    39, /*  0.16 */ },
+ {  29557,   392, /*    49 */    39, /*  0.15 */ },
+ {  30975,   400, /*    50 */    38, /*  0.15 */ },
+ {  32450,   408, /*    51 */    38, /*  0.15 */ },
+ {  33986,   416, /*    52 */    37, /*  0.15 */ },
+ {  35586,   424, /*    53 */    36, /*  0.14 */ },
+ {  37253,   432, /*    54 */    36, /*  0.14 */ },
+ {  38992,   440, /*    55 */    35, /*  0.14 */ },
+ {  40808,   448, /*    56 */    35, /*  0.14 */ },
+ {  42707,   456, /*    57 */    34, /*  0.13 */ },
+ {  44694,   464, /*    58 */    33, /*  0.13 */ },
+ {  46776,   472, /*    59 */    33, /*  0.13 */ },
+ {  48961,   480, /*    60 */    32, /*  0.13 */ },
+ {  51258,   488, /*    61 */    32, /*  0.13 */ },
+ {  53677,   496, /*    62 */    31, /*  0.12 */ },
+ {  56230,   504, /*    63 */    30, /*  0.12 */ },
+ {  58932,   512, /*    64 */    30, /*  0.12 */ },
+ {  61799,   520, /*    65 */    29, /*  0.12 */ },
+ {  64851,   528, /*    66 */    28, /*  0.11 */ },
+ {  68113,   536, /*    67 */    28, /*  0.11 */ },
+ {  71617,   544, /*    68 */    27, /*  0.11 */ },
+ {  75401,   552, /*    69 */    26, /*  0.10 */ },
+ {  79517,   560, /*    70 */    26, /*  0.10 */ },
+ {  84035,   568, /*    71 */    25, /*  0.10 */ },
+ {  89053,   576, /*    72 */    24, /*  0.10 */ },
+ {  94717,   584, /*    73 */    23, /*  0.09 */ },
+ { 999999,   584, /*    73 */    23  /*  0.09 */ }
+};
+
+void web100_update_floyd_aimd(struct tcp_opt *tp)
+{
+	unsigned int tmpndx, cwndseg;
+	struct web100stats *stats = tp->tcp_stats;
+	
+	tmpndx = stats->wc_flindex; /* previous index */
+	cwndseg = tp->snd_cwnd;   /* window in segments */
+ 
+	if (cwndseg > aimd_vals[tmpndx].cwnd) {
+		/* find new upper bound */
+		while (cwndseg >  aimd_vals[tmpndx].cwnd)
+			tmpndx++;
+	} else if (cwndseg < aimd_vals[tmpndx-1].cwnd) {
+		/* find new lower bound */
+		while (cwndseg < aimd_vals[tmpndx-1].cwnd)
+			tmpndx--;
+	} else {
+		return;   /* no change */
+	}
+	
+	WEB100_VAR_SET(tp,WAD_AI,aimd_vals[tmpndx].increment);
+	WEB100_VAR_SET(tp,WAD_MD,aimd_vals[tmpndx].decrement);
+	stats->wc_flindex = tmpndx;  /* save current index */
+}
+#endif
+
+
+void __init web100_stats_init()
+{
+	int order;
+	
+	memset(death_slots, 0, sizeof (death_slots));
+	
+	web100stats_htsize = tcp_ehash_size;
+	for (order = 0; (1UL << order) * PAGE_SIZE < web100stats_htsize *
+	     sizeof (struct web100stats *); order++)
+		;
+	printk("Web100: initiailizing hash table of size %d (order %d)\n",
+	       web100stats_htsize, order);
+	if ((web100stats_ht = (struct web100stats **)__get_free_pages(GFP_ATOMIC, order)) == NULL)
+		panic("Failed to allocate Web100 stats hash table.\n");
+	memset(web100stats_ht, 0, web100stats_htsize * sizeof (struct web100stats *));
+	
+#ifdef CONFIG_WEB100_NETLINK
+	if ((web100_nlsock = netlink_kernel_create(NETLINK_WEB100, NULL)) == NULL)
+		printk(KERN_ERR "web100_stats_init(): cannot initialize netlink socket\n");
+#endif
+	
+	printk("Web100 %s: Initialization successful\n", web100_version_string);
+}
diff -uwbrN --exclude=config.save linux-2.4.23/net/ipv6/tcp_ipv6.c linux-2.4.23-web100-HSv4/net/ipv6/tcp_ipv6.c
--- linux-2.4.23/net/ipv6/tcp_ipv6.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/ipv6/tcp_ipv6.c	2004-03-01 01:37:34.000000000 +0000
@@ -709,6 +709,11 @@
 		tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,
 							     np->daddr.s6_addr32,
 							     sk->sport, sk->dport);
+	WEB100_VAR_SET(tp, SndISS, tp->write_seq);
+	WEB100_VAR_SET(tp, SndMax, tp->write_seq);
+	WEB100_VAR_SET(tp, SndNxt, tp->write_seq);
+	WEB100_VAR_SET(tp, SndUna, tp->write_seq);
+	
 	err = tcp_connect(sk);
 	if (err)
 		goto late_failure;
@@ -1331,6 +1336,13 @@
 	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (newsk == NULL)
 		goto out;
+#ifdef CONFIG_WEB100_STATS
+	if (web100_stats_create(newsk)) {
+		sk_free(newsk);
+		goto out;
+	}
+	newsk->tp_pinfo.af_tcp.tcp_stats->wc_vars.LocalAddressType = WC_ADDRTYPE_IPV6;
+#endif
 
 	/* Charge newly allocated IPv6 socket */
 #ifdef INET_REFCNT_DEBUG
@@ -1612,12 +1624,14 @@
 	skb->dev = NULL;
 
 	bh_lock_sock(sk);
+	WEB100_UPDATE_FUNC(&sk->tp_pinfo.af_tcp, web100_update_segrecv(&sk->tp_pinfo.af_tcp, skb));
 	ret = 0;
 	if (!sk->lock.users) {
 		if (!tcp_prequeue(sk, skb))
 			ret = tcp_v6_do_rcv(sk, skb);
 	} else
 		sk_add_backlog(sk, skb);
+	WEB100_UPDATE_FUNC(&sk->tp_pinfo.af_tcp, web100_update_cwnd(&sk->tp_pinfo.af_tcp));
 	bh_unlock_sock(sk);
 
 	sock_put(sk);
@@ -1857,6 +1871,16 @@
 	sk->sndbuf = sysctl_tcp_wmem[1];
 	sk->rcvbuf = sysctl_tcp_rmem[1];
 
+#ifdef CONFIG_WEB100_STATS
+	{
+		int err;
+		if ((err = web100_stats_create(sk))) {
+			return err;
+		}
+		sk->tp_pinfo.af_tcp.tcp_stats->wc_vars.LocalAddressType = WC_ADDRTYPE_IPV6;
+	}
+#endif
+	
 	atomic_inc(&tcp_sockets_allocated);
 
 	return 0;
@@ -1881,6 +1905,14 @@
 	if(sk->prev != NULL)
 		tcp_put_port(sk);
 
+#ifdef CONFIG_WEB100_STATS
+#if 0
+	/* Do we have an ipv4 connection here? */
+	if (sk->tp_pinfo.af_tcp.tcp_stats)
+#endif
+		web100_stats_destroy(sk->tp_pinfo.af_tcp.tcp_stats);
+#endif
+	
 	/* If sendmsg cached page exists, toss it. */
 	if (tp->sndmsg_page != NULL)
 		__free_page(tp->sndmsg_page);
diff -uwbrN --exclude=config.save linux-2.4.23/net/netsyms.c linux-2.4.23-web100-HSv4/net/netsyms.c
--- linux-2.4.23/net/netsyms.c	2003-11-28 18:26:21.000000000 +0000
+++ linux-2.4.23-web100-HSv4/net/netsyms.c	2004-03-01 01:37:26.000000000 +0000
@@ -404,6 +404,18 @@
 EXPORT_SYMBOL(sysctl_max_syn_backlog);
 #endif
 
+#if defined(CONFIG_WEB100_STATS) && defined(CONFIG_IPV6_MODULE)
+EXPORT_SYMBOL(web100_stats_create);
+EXPORT_SYMBOL(web100_stats_destroy);
+EXPORT_SYMBOL(web100_update_segrecv);
+EXPORT_SYMBOL(web100_update_cwnd);
+EXPORT_SYMBOL(web100_update_writeq);
+#endif
+
+#ifdef CONFIG_WEB100_STATS
+EXPORT_SYMBOL(tcp_retx_mem);
+#endif
+
 #if defined (CONFIG_IPV6_MODULE)
 EXPORT_SYMBOL(secure_tcpv6_sequence_number);
 EXPORT_SYMBOL(secure_ipv6_id);
