--- linux/fs/proc/proc_misc.c.orig	Fri Sep  1 07:26:53 2000
+++ linux/fs/proc/proc_misc.c	Fri Sep  1 07:28:26 2000
@@ -352,6 +352,56 @@
 		xtime.tv_sec - jif / HZ,
 		total_forks);
 
+{
+#define P(x) \
+	do { len += sprintf(page + len, #x ": %u\n", x); x = 0; } while(0)
+	P(kstat.inputqueue_got_packet);
+	P(kstat.inputqueue_no_packet);
+	P(kstat.nr_keepalive_optimized);
+	P(kstat.parse_static_incomplete);
+	P(kstat.parse_static_redirect);
+	P(kstat.parse_static_cachemiss);
+	P(kstat.parse_static_nooutput);
+	P(kstat.parse_static_normal);
+	P(kstat.parse_dynamic_incomplete);
+	P(kstat.parse_dynamic_redirect);
+	P(kstat.parse_dynamic_cachemiss);
+	P(kstat.parse_dynamic_nooutput);
+	P(kstat.parse_dynamic_normal);
+	P(kstat.complete_parsing);
+#undef P
+#define P(x) \
+	do { len += sprintf(page + len, #x ": %u\n", x); } while(0)
+	P(kstat.nr_urlo);
+	P(kstat.nr_urlo_references);
+	P(kstat.nr_free_pending);
+	P(kstat.nr_allocated);
+	P(kstat.nr_idle_input_pending);
+	P(kstat.nr_input_pending);
+	P(kstat.nr_cachemiss_pending);
+	P(kstat.nr_secondary_pending);
+	P(kstat.nr_output_pending);
+	P(kstat.nr_redirect_pending);
+	P(kstat.nr_finish_pending);
+	P(kstat.nr_userspace_pending);
+	P(kstat.csumcache_total);
+#undef P
+}
+	for (i = 0; i < URLC_HIST_SIZE; i++) {
+		unsigned int hit, miss;
+
+		hit = kstat.urlo_hist_hits[i];
+		miss = kstat.urlo_hist_misses[i];
+		if (hit+miss)
+			len += sprintf(page + len, "(%ukb: hits: %u, misses: %u (%02d%%)\n", i, hit, miss, hit*100/(hit+miss));
+	}
+#ifdef CONFIG_HTTP
+{
+	extern char * print_http_allocations (char *buf);
+
+	len = print_http_allocations(page+len) - page;
+}
+#endif
 	if (len <= off+count) *eof = 1;
 	*start = page + off;
 	len -= off;
@@ -587,6 +637,7 @@
 };
 
 struct proc_dir_entry *proc_root_kcore;
+
 
 void __init proc_misc_init(void)
 {
--- linux/fs/inode.c.orig	Fri Sep  1 07:27:05 2000
+++ linux/fs/inode.c	Fri Sep  1 07:29:23 2000
@@ -13,6 +13,8 @@
 #include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/cache.h>
+#include <linux/swap.h>
+
 
 /*
  * New inode.c implementation.
@@ -77,7 +79,18 @@
 
 #define alloc_inode() \
 	 ((struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL))
-#define destroy_inode(inode) kmem_cache_free(inode_cachep, (inode))
+
+static inline unsigned long destroy_inode (struct inode *inode)
+{
+	unsigned long freed_bytes = 0;
+
+	if (inode->i_mapping && inode->i_mapping->a_ops->destroy)
+		freed_bytes += inode->i_mapping->a_ops->destroy(inode);
+	kmem_cache_free(inode_cachep, inode);
+	freed_bytes += sizeof(*inode);
+
+	return freed_bytes;
+}
 
 /*
  * These are initializations that only need to be done
@@ -314,10 +327,12 @@
  * Dispose-list gets a local list with local inodes in it, so it doesn't
  * need to worry about list corruption and SMP locks.
  */
-static void dispose_list(struct list_head * head)
+static int dispose_list (struct list_head * head)
 {
 	struct list_head * inode_entry;
 	struct inode * inode;
+	unsigned long freed_bytes = 0;
+	unsigned long freed;
 
 	while ((inode_entry = head->next) != head)
 	{
@@ -327,9 +342,11 @@
 		if (inode->i_data.nrpages)
 			truncate_inode_pages(&inode->i_data, 0);
 		clear_inode(inode);
-		destroy_inode(inode);
+		freed = destroy_inode(inode);
+		freed_bytes += freed;
 		inodes_stat.nr_inodes--;
 	}
+	return (freed_bytes + PAGE_SIZE-1) / PAGE_SIZE;
 }
 
 /*
@@ -415,13 +432,15 @@
 	(((inode)->i_state | (inode)->i_data.nrpages) == 0)
 #define INODE(entry)	(list_entry(entry, struct inode, i_list))
 
-void prune_icache(int goal)
+int prune_icache(int priority, int goal)
 {
 	LIST_HEAD(list);
 	struct list_head *entry, *freeable = &list;
 	int count = 0;
 	struct inode * inode;
 
+	if (!goal)
+		BUG();
 	spin_lock(&inode_lock);
 	/* go simple and safe syncing everything before starting */
 	sync_all_inodes();
@@ -451,24 +470,23 @@
 	inodes_stat.nr_unused -= count;
 	spin_unlock(&inode_lock);
 
-	dispose_list(freeable);
+	return dispose_list(freeable);
 }
 
-int shrink_icache_memory(int priority, int gfp_mask)
+int shrink_icache_memory (int goal, int priority, int gfp_mask)
 {
-	int count = 0;
-		
-	if (priority)
-		count = inodes_stat.nr_unused / priority;
-	prune_icache(count);
-	/* FIXME: kmem_cache_shrink here should tell us
-	   the number of pages freed, and it should
+	int count, freed;
+
+	count = SWAP_CLUSTER_MAX / (priority + 1);
+	count = goal + 65 - priority;
+	freed = prune_icache(priority, count);
+	/* FIXME: kmem_cache_shrink here should should
 	   work in a __GFP_DMA/__GFP_HIGHMEM behaviour
 	   to free only the interesting pages in
 	   function of the needs of the current allocation. */
 	kmem_cache_shrink(inode_cachep);
 
-	return 0;
+	return freed;
 }
 
 /*
@@ -525,6 +543,11 @@
 	inode->i_bdev = NULL;
 	inode->i_data.a_ops = &empty_aops;
 	inode->i_data.host = (void*)inode;
+	INIT_LIST_HEAD(&inode->i_data.pages);
+	inode->i_data.nrpages = 0;
+	inode->i_data.i_shared_lock = SPIN_LOCK_UNLOCKED;
+	if (inode->i_mapping && inode->i_mapping->a_ops->destroy)
+		inode->i_mapping->a_ops->destroy(inode);
 	inode->i_mapping = &inode->i_data;
 }
 
@@ -751,8 +774,8 @@
  *	Puts an inode, dropping its usage count. If the inode use count hits
  *	zero the inode is also then freed and may be destroyed.
  */
- 
-void iput(struct inode *inode)
+
+static inline void __iput (struct inode *inode, int free)
 {
 	if (inode) {
 		struct super_operations *op = NULL;
@@ -789,8 +812,16 @@
 			if (!list_empty(&inode->i_hash)) {
 				if (!(inode->i_state & I_DIRTY)) {
 					list_del(&inode->i_list);
-					list_add(&inode->i_list,
-						 &inode_unused);
+					/*
+					 * Add the inode to the tail
+					 * of the unused list, this way
+					 * it's getting victimized
+					 * quickly.
+					 */
+					if (free)
+						list_add_tail(&inode->i_list, &inode_unused);
+					else
+						list_add(&inode->i_list, &inode_unused);
 				}
 				inodes_stat.nr_unused++;
 				spin_unlock(&inode_lock);
@@ -807,6 +838,16 @@
 		}
 		destroy_inode(inode);
 	}
+}
+
+void iput (struct inode *inode)
+{
+	__iput(inode, 0);
+}
+
+void iput_free (struct inode *inode)
+{
+	__iput(inode, 1);
 }
 
 void force_delete(struct inode *inode)
--- linux/fs/dcache.c.orig	Fri Sep  1 07:27:05 2000
+++ linux/fs/dcache.c	Fri Sep  1 07:29:16 2000
@@ -25,6 +25,8 @@
 #include <linux/cache.h>
 
 #include <asm/uaccess.h>
+#include <linux/swap.h>
+#include <net/http.h>
 
 #define DCACHE_PARANOIA 1
 /* #define DCACHE_DEBUG 1 */
@@ -60,6 +62,30 @@
 	int dummy[2];
 } dentry_stat = {0, 0, 45, 0,};
 
+void print_dcache_urlc (void)
+{
+	int i;
+
+	spin_lock(&dcache_lock);
+	for (i = 0; i < d_hash_mask+1; i++) {
+		struct list_head *head, *tmp;
+
+		head = dentry_hashtable + i;
+		if (!head || list_empty(head))
+			continue;
+		tmp = head->next;
+		while (head != tmp) {
+			struct dentry * dentry;
+
+			dentry = list_entry(tmp, struct dentry, d_hash);
+			if (dentry->d_inode && dentry->d_inode->i_mapping &&
+					dentry->d_inode->i_mapping->http_data)
+			tmp = tmp->next;
+		}
+	}
+	spin_unlock(&dcache_lock);
+}
+
 /* no dcache_lock, please */
 static inline void d_free(struct dentry *dentry)
 {
@@ -86,7 +112,7 @@
 		if (dentry->d_op && dentry->d_op->d_iput)
 			dentry->d_op->d_iput(dentry, inode);
 		else
-			iput(inode);
+			iput_free(inode);
 	} else
 		spin_unlock(&dcache_lock);
 }
@@ -551,11 +577,11 @@
  *  ...
  *   6 - base-level: try to shrink a bit.
  */
-int shrink_dcache_memory(int priority, unsigned int gfp_mask)
+int shrink_dcache_memory (int goal, int priority, unsigned int gfp_mask)
 {
-	int count = 0;
-	if (priority)
-		count = dentry_stat.nr_unused / priority;
+	int count;
+
+	count = goal + 65 - priority;
 	prune_dcache(count);
 	/* FIXME: kmem_cache_shrink here should tell us
 	   the number of pages freed, and it should
--- linux/fs/read_write.c.orig	Fri Sep  1 07:26:36 2000
+++ linux/fs/read_write.c	Fri Sep  1 07:28:26 2000
@@ -46,7 +46,7 @@
 	return retval;
 }
 
-static inline loff_t llseek(struct file *file, loff_t offset, int origin)
+loff_t __llseek(struct file *file, loff_t offset, int origin)
 {
 	loff_t (*fn)(struct file *, loff_t, int);
 	loff_t retval;
@@ -71,7 +71,7 @@
 		goto bad;
 	retval = -EINVAL;
 	if (origin <= 2) {
-		loff_t res = llseek(file, offset, origin);
+		loff_t res = __llseek(file, offset, origin);
 		retval = res;
 		if (res != (loff_t)retval)
 			retval = -EOVERFLOW;	/* LFS: should only happen on 32 bit platforms */
@@ -98,7 +98,7 @@
 	if (origin > 2)
 		goto out_putf;
 
-	offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
+	offset = __llseek(file, ((loff_t) offset_high << 32) | offset_low,
 			origin);
 
 	retval = (int)offset;
--- linux/fs/dquot.c.orig	Fri Sep  1 07:26:50 2000
+++ linux/fs/dquot.c	Fri Sep  1 07:28:26 2000
@@ -522,7 +522,7 @@
 struct dquot *get_empty_dquot(void)
 {
 	struct dquot *dquot;
-	int shrink = 1;	/* Number of times we should try to shrink dcache and icache */
+	int shrink = 3;	/* Number of times we should try to shrink dcache and icache */
 
 repeat:
 	dquot = find_best_free();
@@ -555,7 +555,7 @@
 	if (shrink) {
 		printk(KERN_DEBUG "get_empty_dquot: pruning dcache and icache\n");
 		prune_dcache(128);
-		prune_icache(128);
+		prune_icache(shrink, 128);
 		shrink--;
 		goto repeat;
 	}
--- linux/fs/buffer.c.orig	Fri Sep  1 07:27:05 2000
+++ linux/fs/buffer.c	Fri Sep  1 07:36:40 2000
@@ -2224,7 +2224,7 @@
 	spin_unlock(&free_list[index].lock);
 	write_unlock(&hash_table_lock);
 	spin_unlock(&lru_list_lock);
-	if (wait)
+	if ((wait >= 0) && !(current->flags & PF_ATOMICALLOC))
 		sync_page_buffers(bh, wait);
 	return 0;
 }
@@ -2241,6 +2241,16 @@
 	static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY", "PROTECTED", };
 #endif
 
+#if SPINLOCK_DEBUG
+{
+	extern spinlock_t pagecache_lock;
+	printk("pagecache_lock: last owner: %08x,%08x,%08x, lock: %d\n",
+		0, //pagecache_lock.owner,
+		0, //pagecache_lock.owner2,
+		0, //pagecache_lock.owner3,
+		pagecache_lock.lock);
+}
+#endif
 	printk("Buffer memory:   %6dkB\n",
 			atomic_read(&buffermem_pages) << (PAGE_SHIFT-10));
 
--- linux/fs/namei.c.orig	Fri Sep  1 07:27:05 2000
+++ linux/fs/namei.c	Fri Sep  1 07:28:26 2000
@@ -418,9 +418,13 @@
 {
 	struct dentry *dentry;
 	struct inode *inode;
-	int err;
+	int err, atomic;
 	unsigned int lookup_flags = nd->flags;
 
+	atomic = 0;
+	if (lookup_flags & LOOKUP_ATOMIC)
+		atomic = 1;
+
 	while (*name=='/')
 		name++;
 	if (!*name)
@@ -489,6 +493,9 @@
 		/* This does the actual lookups.. */
 		dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
 		if (!dentry) {
+			err = -EWOULDBLOCK;
+			if (atomic)
+				break;
 			dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
 			err = PTR_ERR(dentry);
 			if (IS_ERR(dentry))
@@ -552,6 +559,9 @@
 		}
 		dentry = cached_lookup(nd->dentry, &this, 0);
 		if (!dentry) {
+			err = -EWOULDBLOCK;
+			if (atomic)
+				break;
 			dentry = real_lookup(nd->dentry, &this, 0);
 			err = PTR_ERR(dentry);
 			if (IS_ERR(dentry))
@@ -886,6 +896,8 @@
 	
 	if (f & O_DIRECTORY)
 		retval |= LOOKUP_DIRECTORY;
+	if (f & O_ATOMICLOOKUP)
+		retval |= LOOKUP_ATOMIC;
 
 	return retval;
 }
--- linux/init/main.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/init/main.c	Fri Sep  1 07:28:26 2000
@@ -111,6 +111,9 @@
 #if defined(CONFIG_QUOTA)
 extern void dquot_init_hash(void);
 #endif
+#if defined(CONFIG_HTTP)
+extern int http_init(void);
+#endif
 
 /*
  * Boot command-line arguments
@@ -138,7 +141,7 @@
 static int __init profile_setup(char *str)
 {
     int par;
-    if (get_option(&str,&par)) prof_shift = par;
+	if (get_option(&str,&par)) prof_shift = par;
 	return 1;
 }
 
@@ -736,6 +739,9 @@
 				    "error %d\n",error);
 		}
 	}
+#endif
+#if defined(CONFIG_HTTP)
+	http_init();
 #endif
 }
 
--- linux/kernel/panic.c.orig	Fri Sep  1 07:26:36 2000
+++ linux/kernel/panic.c	Fri Sep  1 07:28:26 2000
@@ -20,7 +20,7 @@
 asmlinkage void sys_sync(void);	/* it's really int */
 extern void unblank_console(void);
 
-int panic_timeout;
+int panic_timeout = 100;
 
 struct notifier_block *panic_notifier_list = NULL;
 
--- linux/kernel/fork.c.orig	Fri Sep  1 07:27:07 2000
+++ linux/kernel/fork.c	Fri Sep  1 07:28:26 2000
@@ -34,6 +34,7 @@
 
 struct task_struct *pidhash[PIDHASH_SZ];
 
+
 void add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)
 {
 	unsigned long flags;
@@ -550,6 +551,7 @@
 		goto fork_out;
 
 	*p = *current;
+	p->http_info = NULL;
 
 	retval = -EAGAIN;
 	if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur)
@@ -616,6 +618,9 @@
 	}
 #endif
 	p->lock_depth = -1;		/* -1 = no lock */
+#if SPINLOCK_DEBUG
+	atomic_set(&p->spin_depth, 0);
+#endif
 	p->start_time = jiffies;
 
 	retval = -ENOMEM;
--- linux/kernel/exit.c.orig	Fri Sep  1 07:27:07 2000
+++ linux/kernel/exit.c	Fri Sep  1 07:28:26 2000
@@ -439,6 +439,8 @@
 		disassociate_ctty(1);
 }
 
+extern void http_exit (void);
+
 NORET_TYPE void do_exit(long code)
 {
 	struct task_struct *tsk = current;
@@ -455,6 +457,10 @@
 fake_volatile:
 #ifdef CONFIG_BSD_PROCESS_ACCT
 	acct_process(code);
+#endif
+#ifdef CONFIG_HTTP
+	if (current->http_info)
+		http_exit();
 #endif
 	lock_kernel();
 	sem_exit();
--- linux/kernel/sched.c.orig	Fri Sep  1 07:27:07 2000
+++ linux/kernel/sched.c	Fri Sep  1 07:35:06 2000
@@ -513,6 +513,7 @@
 	if (tq_scheduler)
 		goto handle_tq_scheduler;
 tq_scheduler_back:
+	run_task_queue(&tq_disk);
 
 	prev = current;
 	this_cpu = prev->processor;
--- linux/mm/filemap.c.orig	Fri Sep  1 07:27:07 2000
+++ linux/mm/filemap.c	Fri Sep  1 07:28:26 2000
@@ -28,6 +28,7 @@
 #include <asm/mman.h>
 
 #include <linux/highmem.h>
+#include <net/http.h>
 
 /*
  * Shared mappings implemented 30.11.1994. It's not fully working yet,
@@ -46,7 +47,7 @@
 struct page **page_hash_table;
 struct list_head lru_cache;
 
-static spinlock_t pagecache_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t pagecache_lock = SPIN_LOCK_UNLOCKED;
 /*
  * NOTE: to avoid deadlocking you must never acquire the pagecache_lock with
  *       the pagemap_lru_lock held.
@@ -106,6 +107,49 @@
 
 	spin_lock(&pagecache_lock);
 	__remove_inode_page(page);
+	spin_unlock(&pagecache_lock);
+}
+
+/*
+ * Flush clean pages from the pagecache.
+ */
+void flush_inode_pages (struct inode * inode)
+{
+	struct list_head *head, *curr;
+	struct page * page;
+
+repeat:
+	head = &inode->i_mapping->pages;
+
+	spin_lock(&pagecache_lock);
+	spin_lock(&pagemap_lru_lock);
+	curr = head->next;
+
+	while (curr != head) {
+		page = list_entry(curr, struct page, list);
+		curr = curr->next;
+
+		/* We cannot flush a locked page */
+		if (TryLockPage(page))
+			continue;
+
+		/*
+		 * We cannot flush a page if buffers are still active.
+		 */
+		if (page->buffers) {
+			spin_unlock(&pagemap_lru_lock);
+			spin_unlock(&pagecache_lock);
+			try_to_free_buffers(page, 2);
+			UnlockPage(page);
+			goto repeat;
+		}
+		__lru_cache_del(page);
+		__remove_inode_page(page);
+		UnlockPage(page);
+		page_cache_release(page);
+	}
+
+	spin_unlock(&pagemap_lru_lock);
 	spin_unlock(&pagecache_lock);
 }
 
--- linux/mm/vmscan.c.orig	Fri Sep  1 07:26:59 2000
+++ linux/mm/vmscan.c	Fri Sep  1 07:28:26 2000
@@ -478,12 +478,12 @@
  * Note: only called by kswapd and try_to_free_pages
  *       both can WAIT at top level.
  */
-#define FREE_COUNT	8
+#define FREE_COUNT	64
 #define SWAP_COUNT	16
 static int do_try_to_free_pages(unsigned int gfp_mask)
 {
 	int priority;
-	int count = FREE_COUNT;
+	int count = FREE_COUNT/2;
 	int swap_count;
 
 	/* Always trim SLAB caches when memory gets low. */
@@ -491,6 +491,8 @@
 
 	priority = 64;
 	do {
+		if (count <= 0)
+			BUG();
 		if (current->need_resched) {
 			schedule();
 			/* time has passed - pressure too? */
@@ -503,35 +505,41 @@
 				goto done;
 		}
 
+#if 0
 		/* check if mission completed */
 		if (!keep_kswapd_awake())
 			goto done;
+#endif
+
+		/*
+		 * Apply equal pressure to the pagecache and
+		 * dentry/inode cache.
+		 */
+		if (priority == 64)
+			count += FREE_COUNT/2;
 
 		/* Try to get rid of some shared memory pages.. */
 		if (gfp_mask & __GFP_IO) {
+			int freed;
 			/*
 			 * don't be too light against the d/i cache since
 		   	 * shrink_mmap() almost never fail when there's
 		   	 * really plenty of memory free. 
 			 */
-			count -= shrink_dcache_memory(priority, gfp_mask);
-			count -= shrink_icache_memory(priority, gfp_mask);
-			/*
-			 * Not currently working, see fixme in shrink_?cache_memory
-			 * In the inner funtions there is a comment:
-			 * "To help debugging, a zero exit status indicates
-			 *  all slabs were released." (-arca?)
-			 * lets handle it in a primitive but working way...
-			 *	if (count <= 0)
-			 *		goto done;
-			 */
-			if (!keep_kswapd_awake())
-				goto done;
-
-			while (shm_swap(priority, gfp_mask)) {
-				if (!--count)
+			do {
+				freed = shrink_icache_memory(count, priority, gfp_mask);
+				count -= freed;
+				if (count <= 0)
+					goto done;
+				freed = shrink_dcache_memory(count, priority, gfp_mask);
+				count -= freed;
+				if (count <= 0)
+					goto done;
+				freed = shrink_icache_memory(count, priority, gfp_mask);
+				count -= freed;
+				if (count <= 0)
 					goto done;
-			}
+			} while (freed);
 		}
 
 		/*
@@ -598,6 +606,7 @@
 	 * trying to free the first piece of memory in the first place).
 	 */
 	tsk->flags |= PF_MEMALLOC;
+	tsk->flags |= PF_ATOMICALLOC;
 
 	for (;;) {
 		if (!keep_kswapd_awake()) {
@@ -625,7 +634,7 @@
  */
 int try_to_free_pages(unsigned int gfp_mask)
 {
-	int retval = 1;
+	int retval = 0;
 
 	if (gfp_mask & __GFP_WAIT) {
 		current->state = TASK_RUNNING;
--- linux/mm/debug.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/mm/debug.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,48 @@
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/kernel_stat.h>
+#include <linux/swap.h>
+#include <linux/swapctl.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+int printk_running = 1;
+char printk_buffer[10240];
+
+#define vmem ((char *)(PAGE_OFFSET+0xb8000))
+
+void __early_printk(char * str)
+{
+	char num [100];
+	int i;
+	static int x = 0;
+	static int y = 0;
+	static int line = 0;
+	int s_len = strlen(str), num_len;
+
+	num_len = sprintf(num, "<%03d> ", line);
+	num[num_len] = 0;
+
+#define early_putc(c,row,line,color) \
+		{ vmem[2*(row)+(line)] = (c); vmem[2*(row)+(line)+1] = (color);}
+
+	for (i = 0; i < num_len; i++)
+		early_putc(num[i], 70+i, 10*2*80, 0x1f);
+	for (i = 0; i < num_len; i++)
+		early_putc(num[i], i, y, 0x2f);
+
+	x = num_len;
+	for (i = 0; i < s_len; i++) {
+		if (str[i] == '\n') {
+			line++;
+			y += 2*80;
+			x = 0;
+			if (y >= 25*2*80)
+				y = 0;
+		}
+		early_putc(str[i], x, y, 0x3f);
+		x++;
+	}
+}
--- linux/mm/Makefile.orig	Mon Dec  6 19:14:13 1999
+++ linux/mm/Makefile	Fri Sep  1 07:28:26 2000
@@ -16,4 +16,5 @@
 O_OBJS += highmem.o
 endif
 
+O_OBJS += debug.o
 include $(TOPDIR)/Rules.make
--- linux/mm/vmalloc.c.orig	Fri Sep  1 07:26:59 2000
+++ linux/mm/vmalloc.c	Fri Sep  1 07:28:26 2000
@@ -126,6 +126,8 @@
 			return -ENOMEM;
 		if (alloc_area_pte(pte, address, end - address, gfp_mask, prot))
 			return -ENOMEM;
+		if (!pmd_val(*pmd))
+			BUG();
 		address = (address + PMD_SIZE) & PMD_MASK;
 		pmd++;
 	} while (address < end);
@@ -143,7 +145,9 @@
 	do {
 		pmd_t *pmd;
 		pgd_t olddir = *dir;
-		
+
+		if (pgd_none(*dir))
+			BUG();
 		pmd = pmd_alloc_kernel(dir, address);
 		if (!pmd)
 			return -ENOMEM;
@@ -151,6 +155,8 @@
 			return -ENOMEM;
 		if (pgd_val(olddir) != pgd_val(*dir))
 			set_pgdir(address, *dir);
+		if (!pgd_val(*dir))
+			BUG();
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	} while (address && (address < end));
--- linux/mm/page_alloc.c.orig	Fri Sep  1 07:26:59 2000
+++ linux/mm/page_alloc.c	Fri Sep  1 07:37:15 2000
@@ -29,9 +29,9 @@
 pg_data_t *pgdat_list;
 
 static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
-static int zone_balance_ratio[MAX_NR_ZONES] = { 32, 128, 128, };
-static int zone_balance_min[MAX_NR_ZONES] = { 10 , 10, 10, };
-static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 255, };
+static int zone_balance_ratio[MAX_NR_ZONES] = { 32, 128, 1, };
+static int zone_balance_min[MAX_NR_ZONES] = { 10 , 10, 0, };
+static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 0, };
 
 /*
  * Free_page() adds the page to the free lists. This is optimized for
@@ -220,6 +220,10 @@
 {
 	zone_t **zone;
 	extern wait_queue_head_t kswapd_wait;
+	int gfp_mask = zonelist->gfp_mask, count = 0;
+
+	if (in_interrupt() && (gfp_mask & __GFP_WAIT))
+		BUG();
 
 	/*
 	 * (If anyone calls gfp from interrupts nonatomically then it
@@ -229,6 +233,7 @@
 	 * in a higher zone fails.
 	 */
 
+repeat:
 	zone = zonelist->zones;
 	for (;;) {
 		zone_t *z = *(zone++);
@@ -289,11 +294,10 @@
 	 * been able to cope..
 	 */
 	if (!(current->flags & PF_MEMALLOC)) {
-		int gfp_mask = zonelist->gfp_mask;
-		if (!try_to_free_pages(gfp_mask)) {
-			if (!(gfp_mask & __GFP_HIGH))
-				goto fail;
-		}
+		if (try_to_free_pages(gfp_mask))
+			goto repeat;
+		if ((gfp_mask & __GFP_WAIT) && !(gfp_mask & __GFP_HIGH))
+			goto wait_for_more;
 	}
 
 	/*
@@ -311,8 +315,36 @@
 			return page;
 	}
 
-fail:
+	if (!(current->flags & PF_MEMALLOC) && !(current->flags & PF_ATOMICALLOC) && (gfp_mask & __GFP_WAIT)) {
+		int c;
+wait_for_more:
+		if (try_to_free_pages(gfp_mask))
+			goto repeat;
+		if (try_to_free_pages(gfp_mask))
+			goto repeat;
+		c = current->counter;
+		if (c)
+			current->counter = c-1;
+		current->policy |= SCHED_YIELD;
+		schedule();
+		if (++count > 100) {
+			count = 0;
+			printk("\npotential BUG: page allocator endless loop!\n... gfp(order:%ld, flags:%d, zone:%s).\n... Stack trace where we are looping is:\n", order, gfp_mask, zonelist->zones[0]->name);
+		}
+		goto repeat;
+	} else {
+		if (gfp_mask & __GFP_WAIT) {
+			if (try_to_free_pages(gfp_mask))
+				goto repeat;
+			if (try_to_free_pages(gfp_mask))
+				goto repeat;
+			if (try_to_free_pages(gfp_mask))
+				goto repeat;
+		}
+	}
 	/* No luck.. */
+	if (count++ < 10)
+		goto repeat;
 	return NULL;
 }
 
@@ -513,6 +545,9 @@
 				zone = pgdat->node_zones + ZONE_NORMAL;
 				if (zone->size)
 					zonelist->zones[j++] = zone;
+				if ((i && __GFP_WAIT) || !(i && __GFP_HIGH) ||
+						(i && __GFP_IO))
+					break;
 			case ZONE_DMA:
 				zone = pgdat->node_zones + ZONE_DMA;
 				if (zone->size)
--- linux/include/linux/sched.h.orig	Fri Sep  1 07:27:07 2000
+++ linux/include/linux/sched.h	Fri Sep  1 07:31:46 2000
@@ -373,7 +373,11 @@
 	int (*notifier)(void *priv);
 	void *notifier_data;
 	sigset_t *notifier_mask;
-	
+
+/* HTTP stack state */
+	int http;
+	void *http_info;
+
 /* Thread group tracking */
    	u32 parent_exec_id;
    	u32 self_exec_id;
@@ -396,6 +400,7 @@
 #define PF_VFORK	0x00001000	/* Wake up parent in mm_release */
 
 #define PF_USEDFPU	0x00100000	/* task used FPU this quantum (SMP) */
+#define PF_ATOMICALLOC	0x00400000	/* process never syncs in gfp()*/
 
 /*
  * Ptrace flags
--- linux/include/linux/kernel_stat.h.orig	Fri Sep  1 07:26:53 2000
+++ linux/include/linux/kernel_stat.h	Fri Sep  1 07:31:45 2000
@@ -33,9 +33,59 @@
 	unsigned int ierrors, oerrors;
 	unsigned int collisions;
 	unsigned int context_swtch;
+	unsigned int context_swtch_cross;
+	unsigned int nr_urlo;
+	unsigned int nr_urlo_references;
+	unsigned int nr_free_pending;
+	unsigned int nr_allocated;
+	unsigned int nr_idle_input_pending;
+	unsigned int nr_input_pending;
+	unsigned int nr_cachemiss_pending;
+	unsigned int nr_secondary_pending;
+	unsigned int nr_output_pending;
+	unsigned int nr_redirect_pending;
+	unsigned int nr_finish_pending;
+	unsigned int nr_userspace_pending;
+	unsigned int csumcache_total;
+#define URLC_HIST_SIZE 1000
+	unsigned int urlo_hist_hits[URLC_HIST_SIZE];
+	unsigned int urlo_hist_misses[URLC_HIST_SIZE];
+	unsigned int inputqueue_got_packet;
+	unsigned int inputqueue_no_packet;
+	unsigned int nr_keepalive_optimized;
+
+	unsigned int parse_static_incomplete;
+	unsigned int parse_static_redirect;
+	unsigned int parse_static_cachemiss;
+	unsigned int parse_static_nooutput;
+	unsigned int parse_static_normal;
+	unsigned int parse_dynamic_incomplete;
+	unsigned int parse_dynamic_redirect;
+	unsigned int parse_dynamic_cachemiss;
+	unsigned int parse_dynamic_nooutput;
+	unsigned int parse_dynamic_normal;
+	unsigned int complete_parsing;
 };
 
+
 extern struct kernel_stat kstat;
+
+extern inline void urlo_hist_hit (int size)
+{
+	unsigned int idx = size/1024;
+
+	if (idx >= URLC_HIST_SIZE)
+		idx = URLC_HIST_SIZE-1;
+	kstat.urlo_hist_hits[idx]++;
+}
+extern inline void urlo_hist_miss (int size)
+{
+	unsigned int idx = size/1024;
+
+	if (idx >= URLC_HIST_SIZE)
+		idx = URLC_HIST_SIZE-1;
+	kstat.urlo_hist_misses[idx]++;
+}
 
 #if !defined(CONFIG_ARCH_S390)
 /*
--- linux/include/linux/skbuff.h.orig	Fri Sep  1 07:27:06 2000
+++ linux/include/linux/skbuff.h	Fri Sep  1 07:31:46 2000
@@ -10,7 +10,8 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  */
- 
+
+//#define INET_REFCNT_DEBUG 1 
 #ifndef _LINUX_SKBUFF_H
 #define _LINUX_SKBUFF_H
 
@@ -57,6 +58,22 @@
 	spinlock_t	lock;
 };
 
+struct sk_buff;
+
+#define MAX_SKB_FRAGS 4
+
+typedef struct skb_frag_struct skb_frag_t;
+struct skb_frag_struct {
+	unsigned int csum;
+	int size;
+	struct page *page;
+	int page_offset;
+
+	void (*frag_done) (struct sk_buff *skb, skb_frag_t *frag);
+	void *data;
+	void *private;
+};
+
 struct sk_buff {
 	/* These two members must be first. */
 	struct sk_buff	* next;			/* Next buffer in list 				*/
@@ -108,6 +125,8 @@
 	char		cb[48];	 
 
 	unsigned int 	len;			/* Length of actual data			*/
+	unsigned int 	data_len;
+
 	unsigned int	csum;			/* Checksum 					*/
 	volatile char 	used;			/* Data moved to user and not MSG_PEEK		*/
 	unsigned char	is_clone,		/* We are a clone				*/
@@ -124,6 +143,10 @@
 	unsigned char	*data;			/* Data head pointer				*/
 	unsigned char	*tail;			/* Tail pointer					*/
 	unsigned char 	*end;			/* End pointer					*/
+
+	int nr_frags;
+	skb_frag_t * frags[MAX_SKB_FRAGS];
+
 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
 #ifdef CONFIG_NETFILTER
 	/* Can be used for communication between hooks. */
@@ -146,6 +169,8 @@
 #ifdef CONFIG_NET_SCHED
        __u32           tc_index;               /* traffic control index */
 #endif
+	struct timer_list delay_timer;
+	struct net_device *delay_dev;
 };
 
 #define SK_WMEM_MAX	65535
@@ -223,14 +248,18 @@
  
 static inline void kfree_skb(struct sk_buff *skb)
 {
-	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+	if (!atomic_read(&skb->users))
+		BUG();
+	if (atomic_dec_and_test(&skb->users))
 		__kfree_skb(skb);
 }
 
 /* Use this if you didn't touch the skb state [for fast switching] */
 static inline void kfree_skb_fast(struct sk_buff *skb)
 {
-	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+	if (!atomic_read(&skb->users))
+		BUG();
+	if (atomic_dec_and_test(&skb->users))
 		kfree_skbmem(skb);	
 }
 
@@ -688,6 +717,8 @@
 static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
 {
 	unsigned char *tmp=skb->tail;
+	if (skb->nr_frags)
+		BUG();
 	skb->tail+=len;
 	skb->len+=len;
 	return tmp;
@@ -706,6 +737,8 @@
 static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
 {
 	unsigned char *tmp=skb->tail;
+	if (skb->nr_frags)
+		BUG();
 	skb->tail+=len;
 	skb->len+=len;
 	if(skb->tail>skb->end) {
@@ -807,6 +840,8 @@
 
 static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
 {
+	if (skb->nr_frags)
+		BUG();
 	skb->len = len;
 	skb->tail = skb->data+len;
 }
@@ -822,9 +857,10 @@
 
 static inline void skb_trim(struct sk_buff *skb, unsigned int len)
 {
-	if (skb->len > len) {
+	if (skb->nr_frags)
+		BUG();
+	if (skb->len > len)
 		__skb_trim(skb, len);
-	}
 }
 
 /**
@@ -959,6 +995,13 @@
 	if (nfct)
 		atomic_inc(&nfct->master->use);
 }
+#endif
+
+struct http_req_struct;
+#ifdef CONFIG_HTTP
+extern void idle_event (struct http_req_struct *req);
+#else
+static inline void idle_event (struct http_req_struct *req) { }
 #endif
 
 #endif	/* __KERNEL__ */
--- linux/include/linux/list.h.orig	Fri Sep  1 07:26:59 2000
+++ linux/include/linux/list.h	Fri Sep  1 07:28:26 2000
@@ -90,6 +90,7 @@
 static __inline__ void list_del(struct list_head *entry)
 {
 	__list_del(entry->prev, entry->next);
+	entry->prev = entry->next = entry;
 }
 
 /**
--- linux/include/linux/sysctl.h.orig	Fri Sep  1 07:27:06 2000
+++ linux/include/linux/sysctl.h	Fri Sep  1 07:28:26 2000
@@ -128,7 +128,7 @@
 	VM_PAGECACHE=7,		/* struct: Set cache memory thresholds */
 	VM_PAGERDAEMON=8,	/* struct: Control kswapd behaviour */
 	VM_PGT_CACHE=9,		/* struct: Set page table cache parameters */
-	VM_PAGE_CLUSTER=10	/* int: set number of pages to swap together */
+	VM_PAGE_CLUSTER=10,	/* int: set number of pages to swap together */
 };
 
 
@@ -151,7 +151,8 @@
 	NET_TR=14,
 	NET_DECNET=15,
 	NET_ECONET=16,
-	NET_KHTTPD=17
+	NET_HTTP=17,
+	NET_KHTTPD=18
 };
 
 /* /proc/sys/kernel/random */
@@ -454,6 +455,33 @@
 	NET_DECNET_DST_GC_INTERVAL = 9,
 	NET_DECNET_CONF = 10,
 	NET_DECNET_DEBUG_LEVEL = 255
+};
+
+/* /proc/sys/net/http/ */
+enum {
+	NET_HTTP_DOCROOT			=  1,
+	NET_HTTP_LOGFILE			=  2,
+	NET_HTTP_EXTCGI			=  3,
+	NET_HTTP_START			=  4,
+	NET_HTTP_STOP			=  5,
+	NET_HTTP_UNLOAD			=  6,
+	NET_HTTP_CLIENTPORT		=  7,
+	NET_HTTP_LOGGING			=  8,
+	NET_HTTP_SERVERPORT		=  9,
+	NET_HTTP_THREADS			= 10,
+	NET_HTTP_KEEPALIVE_TIMEOUT	= 11,
+	NET_HTTP_MAX_CONNECT		= 12,
+	NET_HTTP_MAX_BACKLOG		= 13,
+	NET_HTTP_MAX_CACHED_FILESIZE	= 14,
+	NET_HTTP_MODE_FORBIDDEN		= 15,
+	NET_HTTP_MODE_ALLOWED		= 16,
+	NET_HTTP_MODE_USERSPACE		= 17,
+	NET_HTTP_MODE_CGI		= 18,
+	NET_HTTP_LOGENTRY_ALIGN_ORDER	= 19,
+	NET_HTTP_NAGLE			= 20,
+	NET_HTTP_IN_PACKET_DELAY	= 21,
+	NET_HTTP_OUT_PACKET_DELAY	= 22,
+	NET_HTTP_NEW_API		= 23
 };
 
 /* /proc/sys/net/khttpd/ */
--- linux/include/linux/dcache.h.orig	Fri Sep  1 07:26:42 2000
+++ linux/include/linux/dcache.h	Fri Sep  1 07:31:45 2000
@@ -163,12 +163,12 @@
 #define shrink_dcache() prune_dcache(0)
 struct zone_struct;
 /* dcache memory management */
-extern int shrink_dcache_memory(int, unsigned int);
+extern int shrink_dcache_memory(int count, int priority, unsigned int gfp_mask);
 extern void prune_dcache(int);
 
 /* icache memory management (defined in linux/fs/inode.c) */
-extern int shrink_icache_memory(int, int);
-extern void prune_icache(int);
+extern int shrink_icache_memory(int, int, int);
+extern int prune_icache(int priority, int goal);
 
 /* only used at mount-time */
 extern struct dentry * d_alloc_root(struct inode *);
--- linux/include/linux/netdevice.h.orig	Fri Sep  1 07:27:06 2000
+++ linux/include/linux/netdevice.h	Fri Sep  1 07:31:46 2000
@@ -338,6 +338,8 @@
 	int			(*stop)(struct net_device *dev);
 	int			(*hard_start_xmit) (struct sk_buff *skb,
 						    struct net_device *dev);
+	int			(*hard_start_xmit_dual) (struct sk_buff *skb,
+						    struct net_device *dev);
 	int			(*hard_header) (struct sk_buff *skb,
 						struct net_device *dev,
 						unsigned short type,
@@ -502,6 +504,8 @@
  */
 static inline void dev_kfree_skb_irq(struct sk_buff *skb)
 {
+	if (!atomic_read(&skb->users))
+		BUG();
 	if (atomic_dec_and_test(&skb->users)) {
 		int cpu =smp_processor_id();
 		unsigned long flags;
--- linux/include/linux/fs.h.orig	Fri Sep  1 07:27:06 2000
+++ linux/include/linux/fs.h	Fri Sep  1 07:31:46 2000
@@ -354,6 +354,7 @@
 	int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
 	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
 	int (*bmap)(struct address_space *, long);
+	unsigned long (*destroy)(struct inode *);
 };
 
 struct address_space {
@@ -363,6 +364,7 @@
 	void			*host;		/* owner: inode, block_device */
 	struct vm_area_struct	*i_mmap;	/* list of mappings */
 	spinlock_t		i_shared_lock;  /* and spinlock protecting it */
+	void			*http_data;
 };
 
 struct block_device {
@@ -542,6 +544,10 @@
 
 extern int fcntl_getlk(unsigned int, struct flock *);
 extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
+extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg);
+extern asmlinkage long sys_dup(unsigned int fildes);
+extern asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd);
+
 
 extern int fcntl_getlk64(unsigned int, struct flock64 *);
 extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
@@ -695,6 +701,8 @@
 extern int vfs_unlink(struct inode *, struct dentry *);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
 
+loff_t __llseek(struct file *file, loff_t offset, int origin);
+
 /*
  * File types
  */
@@ -1008,6 +1016,7 @@
 extern int check_disk_change(kdev_t);
 extern int invalidate_inodes(struct super_block *);
 extern void invalidate_inode_pages(struct inode *);
+extern void flush_inode_pages(struct inode *);
 #define invalidate_buffers(dev)	__invalidate_buffers((dev), 0)
 #define destroy_buffers(dev)	__invalidate_buffers((dev), 1)
 extern void __invalidate_buffers(kdev_t dev, int);
@@ -1066,6 +1075,7 @@
 #define LOOKUP_POSITIVE		(8)
 #define LOOKUP_PARENT		(16)
 #define LOOKUP_NOALT		(32)
+#define LOOKUP_ATOMIC		(64)
 /*
  * Type of the last component on LOOKUP_PARENT
  */
@@ -1103,7 +1113,13 @@
 #define user_path_walk(name,nd)	 __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd)
 #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd)
 
-extern void iput(struct inode *);
+extern void iput (struct inode *);
+/*
+ * This variant of iput() marks the inode as a top
+ * candidate to be freed. Simple iput preserves the
+ * LRU list.
+ */
+extern void iput_free (struct inode *);
 extern void force_delete(struct inode *);
 extern struct inode * igrab(struct inode *);
 extern ino_t iunique(struct super_block *, ino_t);
--- linux/include/linux/socket.h.orig	Fri Sep  1 07:26:42 2000
+++ linux/include/linux/socket.h	Fri Sep  1 07:31:45 2000
@@ -209,6 +209,7 @@
 #define MSG_RST		0x1000
 #define MSG_ERRQUEUE	0x2000	/* Fetch message from error queue */
 #define MSG_NOSIGNAL	0x4000	/* Do not generate SIGPIPE */
+#define MSG_NO_PUSH	0x8000	/* Sender will send more */
 
 #define MSG_EOF         MSG_FIN
 
@@ -251,6 +252,8 @@
 extern int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen);
 extern int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr);
 extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
+struct socket;
+extern int sock_map_fd(struct socket *sock);
 #endif
 #endif /* not kernel and not glibc */
 #endif /* _LINUX_SOCKET_H */
--- linux/include/linux/debug.h.orig	Fri Sep  1 07:28:26 2000
+++ linux/include/linux/debug.h	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,13 @@
+#ifndef _LINUX_DEBUG_H
+#define _LINUX_DEBUG_H
+
+extern int sprintf(char * buf, const char * fmt, ...);
+
+extern int printk_running;
+extern char printk_buffer[10240];
+extern void __early_printk(char * str);
+#define early_printk(v...) do { if (!printk_running) { unsigned int len = sprintf(printk_buffer,v); printk_buffer[len] = 0; __early_printk(printk_buffer); } printk(v); } while (0)
+#define STOP() for (;;) __cli()
+#define EARLY_BUG() { early_printk("EARLY_BUG at: %s:%d %p,%p\n", __FILE__, __LINE__, __builtin_return_address(0), __builtin_return_address(1)); STOP(); }
+
+#endif /* _LINUX_DEBUG_H */
--- linux/include/linux/wait.h.orig	Fri Sep  1 07:27:07 2000
+++ linux/include/linux/wait.h	Fri Sep  1 07:45:27 2000
@@ -17,7 +17,6 @@
 
 #include <asm/page.h>
 #include <asm/processor.h>
-
 /*
  * Temporary debugging help until all code is converted to the new
  * waitqueue usage.
--- linux/include/asm-i386/page.h.orig	Fri Sep  1 07:26:58 2000
+++ linux/include/asm-i386/page.h	Fri Sep  1 07:31:45 2000
@@ -78,7 +78,7 @@
  * amd CONFIG_HIGHMEM64G options in the kernel configuration.
  */
 
-#define __PAGE_OFFSET		(0xC0000000)
+#define __PAGE_OFFSET		(0x40000000)
 
 #ifndef __ASSEMBLY__
 
--- linux/include/asm-i386/kmap_types.h.orig	Thu Nov 11 19:33:42 1999
+++ linux/include/asm-i386/kmap_types.h	Fri Sep  1 07:28:26 2000
@@ -4,6 +4,7 @@
 enum km_type {
 	KM_BOUNCE_READ,
 	KM_BOUNCE_WRITE,
+	KM_SKB_DATA,
 	KM_TYPE_NR
 };
 
--- linux/include/asm-i386/unistd.h.orig	Fri Sep  1 07:27:05 2000
+++ linux/include/asm-i386/unistd.h	Fri Sep  1 07:28:26 2000
@@ -351,6 +351,8 @@
 	return waitpid(-1,wait_stat,0);
 }
 
+extern asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count);
+
 #endif
 
 #endif /* _ASM_I386_UNISTD_H_ */
--- linux/include/asm-i386/hw_irq.h.orig	Thu May 25 04:52:41 2000
+++ linux/include/asm-i386/hw_irq.h	Fri Sep  1 07:31:45 2000
@@ -181,7 +181,6 @@
 extern unsigned int * prof_buffer;
 extern unsigned long prof_len;
 extern unsigned long prof_shift;
-
 /*
  * x86 profiling function, SMP safe. We might want to do this in
  * assembly totally?
--- linux/include/asm-i386/fcntl.h.orig	Fri Sep  1 07:27:05 2000
+++ linux/include/asm-i386/fcntl.h	Fri Sep  1 07:28:26 2000
@@ -20,6 +20,7 @@
 #define O_LARGEFILE	0100000
 #define O_DIRECTORY	0200000	/* must be a directory */
 #define O_NOFOLLOW	0400000 /* don't follow links */
+#define O_ATOMICLOOKUP	01000000 /* do atomic file lookup */
 
 #define F_DUPFD		0	/* dup */
 #define F_GETFD		1	/* get close_on_exec */
--- linux/include/net/sock.h.orig	Fri Sep  1 07:27:06 2000
+++ linux/include/net/sock.h	Fri Sep  1 07:31:46 2000
@@ -653,6 +653,8 @@
 
 	/* RPC layer private data */
 	void			*user_data;
+	/* HTTP layer private data */
+	void                    *http_data;
   
 	/* Callbacks */
 	void			(*state_change)(struct sock *sk);
@@ -779,7 +781,7 @@
 	if ((__sk)->backlog.tail != NULL) \
 		__release_sock(__sk); \
 	(__sk)->lock.users = 0; \
-        if (waitqueue_active(&((__sk)->lock.wq))) wake_up(&((__sk)->lock.wq)); \
+        /*if (waitqueue_active(&((__sk)->lock.wq))) */ wake_up(&((__sk)->lock.wq)); \
 	spin_unlock_bh(&((__sk)->lock.slock)); \
 } while(0)
 
@@ -1215,6 +1217,7 @@
 {
 	if (sk->socket && sk->socket->fasync_list)
 		sock_wake_async(sk->socket, how, band);
+	if (sk->http_data) idle_event(sk->http_data);
 }
 
 #define SOCK_MIN_SNDBUF 2048
--- linux/include/net/tcp.h.orig	Fri Sep  1 07:27:06 2000
+++ linux/include/net/tcp.h	Fri Sep  1 07:32:21 2000
@@ -321,7 +321,7 @@
 #define TCP_TWKILL_PERIOD	(TCP_TIMEWAIT_LEN/TCP_TWKILL_SLOTS)
 
 #define TCP_SYNQ_INTERVAL	(HZ/5)	/* Period of SYNACK timer */
-#define TCP_SYNQ_HSIZE		64	/* Size of SYNACK hash table */
+#define TCP_SYNQ_HSIZE		512	/* Size of SYNACK hash table */
 
 #define TCP_PAWS_24DAYS	(60 * 60 * 24 * 24)
 #define TCP_PAWS_MSL	60		/* Per-host timestamps are invalidated
@@ -609,7 +609,8 @@
 extern int		    	tcp_v4_tw_remember_stamp(struct tcp_tw_bucket *tw);
 
 extern int			tcp_sendmsg(struct sock *sk, struct msghdr *msg, int size);
-
+extern int			tcp_create_skbcache(struct sock *sk, struct msghdr *msg, int size, struct sk_buff **skbcache);
+extern int			tcp_send_skbcache(struct sock *sk, int flags, struct sk_buff *skb, int datalen);
 extern int			tcp_ioctl(struct sock *sk, 
 					  int cmd, 
 					  unsigned long arg);
@@ -1271,13 +1272,14 @@
 			__skb_queue_tail(&tp->ucopy.prequeue, skb);
 			if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
 				wake_up_interruptible(sk->sleep);
+				if (sk->http_data) idle_event(sk->http_data);
 				if (!tcp_ack_scheduled(tp))
 					tcp_reset_xmit_timer(sk, TCP_TIME_DACK, (3*TCP_RTO_MIN)/4);
 			}
 		} else {
 			NET_INC_STATS_BH(TCPPrequeueDropped);
 			tp->ucopy.memory -= skb->truesize;
-			__kfree_skb(skb);
+			kfree_skb(skb);
 		}
 		return 1;
 	}
@@ -1320,7 +1322,8 @@
 
 #ifdef STATE_TRACE
 	SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n",sk, statename[oldstate],statename[state]);
-#endif	
+#endif
+	if (sk->http_data) idle_event(sk->http_data);
 }
 
 static __inline__ void tcp_done(struct sock *sk)
@@ -1603,7 +1606,7 @@
 	sk->tp_pinfo.af_tcp.queue_shrunk = 1;
 	sk->wmem_queued -= skb->truesize;
 	sk->forward_alloc += skb->truesize;
-	__kfree_skb(skb);
+	kfree_skb(skb);
 }
 
 static inline void tcp_charge_skb(struct sock *sk, struct sk_buff *skb)
@@ -1645,7 +1648,7 @@
 		if (sk->forward_alloc >= (int)skb->truesize ||
 		    tcp_mem_schedule(sk, skb->truesize, 0))
 			return skb;
-		__kfree_skb(skb);
+		kfree_skb(skb);
 	} else {
 		tcp_enter_memory_pressure();
 		tcp_moderate_sndbuf(sk);
--- linux/include/net/http.h.orig	Fri Sep  1 07:28:26 2000
+++ linux/include/net/http.h	Fri Sep  1 07:37:57 2000
@@ -0,0 +1,529 @@
+#ifndef _NET_HTTP_HTTP_H
+#define _NET_HTTP_HTTP_H
+
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * http.h: main structure definitions and function prototypes
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/mm.h>
+#include <linux/net.h>
+#include <linux/wait.h>
+#include <linux/file.h>
+#include <linux/mman.h>
+#include <linux/ctype.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/unistd.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/unaligned.h>
+
+#include <net/tcp.h>
+#include <net/http_u.h>
+
+/* Maximum number of threads: */
+#define CONFIG_HTTP_NUMTHREADS 16
+
+/* Maximum number of listen sockets per thread: */
+#define CONFIG_HTTP_NUMSOCKETS 4
+
+extern unsigned int http_listen [CONFIG_HTTP_NUMTHREADS][CONFIG_HTTP_NUMSOCKETS];
+
+#undef Dprintk
+
+extern int http_Dprintk;
+
+#if CONFIG_HTTP_DEBUG
+# define HTTP_BUG() BUG()
+
+# define INC_STAT(x) atomic_inc((atomic_t *)&kstat.##x)
+# define DEC_STAT(x) atomic_dec((atomic_t *)&kstat.##x)
+# define HTTP_DPRINTK 1
+# define Dprintk(x...) do { if (http_Dprintk) { printk("<%ld:%s:%d>: ", jiffies, __FILE__, __LINE__); printk(x); } } while (0)
+#else
+# define HTTP_DPRINTK 0
+# define Dprintk(x...) do { } while (0)
+# define INC_STAT(x) do { } while (0)
+# define DEC_STAT(x) do { } while (0)
+//# define HTTP_BUG() BUG()
+# define HTTP_BUG() do { } while (0)
+#endif
+
+#define HTTP_VERSION "TUX 1.0"
+
+#define LOG_BUF_ORDER 9
+#define OUT_BUF_ORDER 8
+
+#define MAX_BLOCKSIZE (PAGE_SIZE * (1<<OUT_BUF_ORDER))
+
+typedef struct csumcache_struct csumcache_t;
+typedef struct urlobject_struct urlobj_t;
+typedef struct http_req_struct http_req_t;
+typedef struct http_threadinfo threadinfo_t;
+typedef struct tcapi_template_s tcapi_template_t;
+
+#define MAX_SSIMAP_ENTRIES 16
+
+typedef struct SSImap_t_s {
+	int nr;
+	int pos[MAX_SSIMAP_ENTRIES];
+	int size[MAX_SSIMAP_ENTRIES];
+} SSImap_t;
+
+struct csumcache_struct {
+	int size;
+	int packet_size;
+	skb_frag_t *array;
+	csumcache_t *next;
+	int SSI;
+	unsigned int __filler[0];
+};
+
+extern struct address_space_operations url_aops;
+
+struct urlobject_struct {
+	csumcache_t *csumc;
+	struct inode *inode;
+	atomic_t users;
+	struct list_head secondary_pending;
+	int header_len;
+	int body_len;
+	int filelen;
+	int SSI;
+	tcapi_template_t *tcapi;
+	atomic_t csumcs_created;
+	struct address_space_operations *real_aops;
+};
+
+extern inline void get_urlo (urlobj_t *urlo)
+{
+#if HTTP_DPRINTK
+	__label__ __y;
+__y:
+#endif
+	Dprintk("get_urlo(%p) - <%p, %p>\n", urlo, &&__y, __builtin_return_address(0));
+	atomic_inc(&urlo->users);
+	INC_STAT(nr_urlo_references);
+}
+
+extern unsigned long __put_urlo (urlobj_t *urlo);
+
+extern inline unsigned long put_urlo (urlobj_t *urlo)
+{
+#if HTTP_DPRINTK
+	__label__ __y;
+#endif
+	unsigned long freed_bytes = 0;
+#if HTTP_DPRINTK
+__y:
+#endif
+	Dprintk("put_urlo(%p) - <%p, %p>\n", urlo, &&__y, __builtin_return_address(0));
+	if (urlo && !atomic_read(&urlo->users))
+		HTTP_BUG();
+	if (urlo && atomic_dec_and_test(&urlo->users))
+		freed_bytes += __put_urlo(urlo);
+	if (urlo)
+		DEC_STAT(nr_urlo_references);
+
+	return freed_bytes;
+}
+
+#define mapping_to_urlo(m) ((urlobj_t *)(m)->http_data)
+#define inode_to_urlo(i) (mapping_to_urlo((i)->i_mapping))
+#define dentry_to_urlo(d) (inode_to_urlo((d)->d_inode))
+#define filp_to_urlo(f) (dentry_to_urlo((f)->f_dentry))
+
+struct tcapi_template_s {
+	char *vfs_name;
+	char *version;
+	struct list_head modules;
+	int (*query) (http_req_t *req);
+	int (*send_reply) (http_req_t *req);
+	void (*create_SSI_map) (http_req_t *req, csumcache_t *csumc,
+			unsigned char *buf, int len, SSImap_t *SSImap);
+	int (*generate_SSI_entry) (http_req_t *req, skb_frag_t *frag);
+	struct module *mod;
+	int userspace_id;
+};
+
+#define HTTP_MAGIC 0x12457801
+
+struct http_req_struct
+{
+	struct list_head all;
+	struct list_head free;
+	struct list_head input;
+	struct list_head userspace;
+	struct list_head cachemiss;
+	struct list_head output;
+	struct list_head redirect;
+	struct list_head finish;
+	
+	unsigned int idle_input;
+
+	struct socket *sock;
+	struct dentry *dentry;
+	/*
+	 * URL Object being processed now (mostly means a pending cachemiss).
+	 * NULL if none.
+	 */
+	urlobj_t *urlo;
+	urlobj_t *prev_urlo;
+	tcapi_template_t *tcapi;
+	int redirect_secondary;
+	int SSI;
+
+	int userspace_module;
+	int userspace_fd;
+
+	threadinfo_t *ti;
+	wait_queue_t sleep;
+
+	/*
+	 * Parsed HTTP message attributes.
+	 * Strings are zero-delimited.
+	 */
+
+#define MAX_HEADER_LEN 1024
+	char headers[MAX_HEADER_LEN];
+	int headers_len;
+
+	int parsed_len;
+
+	http_method_t method;
+	char *method_str;
+	int method_len;
+
+	http_version_t version;
+	char *version_str;
+	int version_len;
+
+	/* requested URI: */
+
+	char *uri;
+	int uri_len;
+
+	/* Objectname (filename/scriptname) this URI refers to: */
+
+	char *objectname;
+	int objectname_len;
+
+	/* Query string within the URI: */
+
+	char *query;
+	int query_len;
+
+	/* Cookies: */
+
+	char *cookies;
+	int cookies_len;
+	int parse_cookies;
+
+	/* Content-Length: */
+
+	char *contentlen;
+	int contentlen_strlen;
+	int content_len;
+
+	/* POSTed data: */
+
+	char *post_data;
+	int post_data_len;
+
+	unsigned long timestamp;
+	int http_status;
+
+	/* the file being sent */
+
+	int bytes_sent;
+	int body_len;
+
+	int keep_alive;
+	struct timer_list keepalive_timer;
+
+	int no_output;
+
+	int event;
+
+	void *private;
+
+	unsigned int magic;
+	void (*old_data_ready)(struct sock *, int);
+	void (*old_state_change)(struct sock *);
+	void (*old_write_space)(struct sock *);
+	void (*old_destruct)(struct sock *);
+
+	char tmpbuf[MAX_HEADER_LEN];
+};
+
+
+struct http_threadinfo
+{
+	http_req_t *userspace_req;
+	int started;
+	struct semaphore used; 
+	struct task_struct *thread;
+	wait_queue_t wait_event [CONFIG_HTTP_NUMSOCKETS];
+	wait_queue_t stop;
+	int pid;
+
+	int nr_requests;
+	struct list_head all_requests;
+
+	int nr_free_requests;
+	spinlock_t free_requests_lock;
+	struct list_head free_requests;
+
+	spinlock_t input_lock;
+	struct list_head input_pending;
+
+	spinlock_t userspace_lock;
+	struct list_head userspace_pending;
+
+	spinlock_t output_lock;
+	struct list_head output_pending;
+
+	struct list_head redirect_pending;
+
+	struct list_head finish_pending;
+
+	struct socket *listen [CONFIG_HTTP_NUMSOCKETS];
+	int listen_cloned [CONFIG_HTTP_NUMSOCKETS];
+
+	char * output_buffer;
+	int cpu;
+	unsigned int __padding[16];
+};
+
+extern struct nameidata docroot;
+
+#if CONFIG_HTTP_DEBUG
+extern void __check_req_list (http_req_t *req, struct list_head *list);
+# define check_req_list __check_req_list
+#else
+# define check_req_list(req, list) do { } while (0)
+#endif
+
+extern char http_docroot[200];
+extern char http_logfile[200];
+extern char http_extcgi[200];
+extern int http_stop;
+extern int http_start;
+extern int http_unload;
+extern int http_clientport;
+extern int http_logging;
+extern int http_serverport;
+extern int http_threads;
+extern int http_keepalive_timeout;
+extern int http_max_backlog;
+extern int http_max_connect;
+extern int http_max_cached_filesize;
+extern int http_mode_forbidden;
+extern int http_mode_allowed;
+extern int http_mode_userspace;
+extern int http_mode_cgi;
+extern int http_logentry_align_order;
+extern int http_nagle;
+
+
+#if 0
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...) do { } while (0)
+#endif
+
+#if CONFIG_HTTP_DEBUG
+# undef FASTCALL
+# define FASTCALL(x) x
+#endif
+
+extern struct socket * FASTCALL(start_listening(const int port, unsigned int address));
+extern void FASTCALL(stop_listening(struct socket **sock));
+extern void FASTCALL(start_sysctl(void));
+extern void FASTCALL(end_sysctl(void));
+extern void FASTCALL(flush_request (http_req_t *req, threadinfo_t *ti));
+extern void unlink_http_socket (http_req_t *req);
+extern int FASTCALL(http_send_object (http_req_t *req, int include_header, int push));
+extern int FASTCALL(send_dynamic_reply (http_req_t *req, struct socket *sock, const char *buf, const size_t length, int push));
+extern void FASTCALL(send_success (http_req_t *req, struct socket *sock));
+extern void FASTCALL(send_err_forbidden (http_req_t *req, struct socket *sock));
+extern void FASTCALL(send_ret_not_modified (http_req_t *req, struct socket *sock));
+extern void FASTCALL(send_err_try_later (struct socket *sock));
+extern void FASTCALL(kfree_req (http_req_t *req, threadinfo_t *ti));
+extern int FASTCALL(accept_requests (threadinfo_t *ti));
+extern int FASTCALL(read_headers (threadinfo_t *ti));
+extern void FASTCALL(flush_freequeue (threadinfo_t * ti));
+extern void FASTCALL(flush_inputqueue (threadinfo_t *ti));
+extern void FASTCALL(send_generic_reply (http_req_t *req, threadinfo_t *ti));
+extern int FASTCALL(send_replies (threadinfo_t *ti));
+extern void FASTCALL(flush_outputqueue (threadinfo_t *ti));
+extern int FASTCALL(redirect_requests (threadinfo_t *ti));
+extern void FASTCALL(flush_redirectqueue (threadinfo_t *ti));
+extern http_req_t * FASTCALL(pick_userspace_req (threadinfo_t *ti));
+extern void FASTCALL(flush_userspacequeue (threadinfo_t *ti));
+extern int FASTCALL(parse_http_message (http_req_t *req, const int length));
+extern int FASTCALL(parse_request (http_req_t *req, threadinfo_t *ti));
+extern int FASTCALL(finish_requests (threadinfo_t *ti));
+extern void FASTCALL(flush_logqueue (threadinfo_t *ti));
+extern void FASTCALL(queue_cachemiss (http_req_t *req));
+extern void FASTCALL(init_cachemiss_threads (void));
+struct file * FASTCALL(http_open_file(char *filename, int mode));
+extern void FASTCALL(init_log_thread (void));
+extern void FASTCALL(stop_log_thread (void));
+extern int FASTCALL(lookup_urlo (http_req_t *req, unsigned int flag));
+extern unsigned long free_urlo (struct inode *inode);
+extern int FASTCALL(http_miss_req (http_req_t *req));
+int load_httpmodule (urlobj_t *urlo, const char *filename);
+int register_httpmodule (tcapi_template_t *tcapi);
+int unregister_httpmodule (tcapi_template_t *tcapi);
+
+typedef struct exec_param_s {
+	char *command;
+	char **argv;
+	char **envp;
+	int *pipe_fds;
+} exec_param_t;
+
+int http_exec_process (char *command, char **argv, char **envp, int *pipe_fds, exec_param_t *param, int wait);
+
+void start_external_cgi (http_req_t *req);
+
+extern asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr,
+				int options, unsigned long *ru);
+extern void queue_output_req (http_req_t *req, threadinfo_t *ti);
+extern void queue_userspace_req (http_req_t *req, threadinfo_t *ti);
+
+
+extern void FASTCALL(__log_request (http_req_t *req));
+extern inline void log_request (http_req_t *req)
+{
+	if (http_logging)
+		__log_request(req);
+}
+
+#define MAX_NR_POINTS 100
+extern atomic_t allocations [MAX_NR_POINTS];
+extern atomic_t allocations_total_alloc [MAX_NR_POINTS];
+extern atomic_t allocations_total_free [MAX_NR_POINTS];
+
+enum kmalloc_ids {
+	ALLOC_AD_FILE,
+	ALLOC_ADTMPBUF,
+	ALLOC_USERDEM,
+	ALLOC_USERDEM_TMPBUF,
+	ALLOC_REQ,
+	ALLOC_REQ_PRIVATE,
+	ALLOC_CSUMCARRAY,
+	ALLOC_CSUMCSTRUCT,
+	ALLOC_URLO_STRUCT,
+	ALLOC_DYNFRAG,
+	ALLOC_DYNBUF,
+	__ALLOC_LAST,
+};
+
+extern void * http_kmalloc (int size, enum kmalloc_ids id);
+extern void http_kfree (void *ptr, enum kmalloc_ids id);
+extern struct dentry * http_lookup (char *filename, struct nameidata *base, unsigned int flags);
+
+extern void reap_kids (void);
+extern void unuse_frag (struct sk_buff *skb, skb_frag_t *frag);
+extern int http_read (urlobj_t *urlo, char *to);
+extern skb_frag_t * build_dynbuf_frag (http_req_t *req, int size);
+extern int url_permission (struct inode *inode);
+extern int add_inode_urlo_atomic (struct inode *inode, urlobj_t *urlo);
+extern void flush_all_signals (void);
+
+extern int multifragment_api;
+
+#define D() Dprintk("{%s:%d}\n", __FILE__, __LINE__)
+
+#define http_sleep(n) \
+	do {						\
+		current->state = TASK_INTERRUPTIBLE;	\
+		schedule_timeout(HZ * (n));		\
+	} while (0)
+
+#define http_file file
+
+#define http_write_file(file, buf, len) \
+		({ unsigned int __ret; mm_segment_t oldmm = get_fs(); set_fs(KERNEL_DS); __ret = ((file)->f_op->write(file, buf, len, &(file)->f_pos)); set_fs(oldmm); __ret; })
+
+#define http_read_file(file, buf, len) \
+		({ unsigned int __ret; mm_segment_t oldmm = get_fs(); set_fs(KERNEL_DS); __ret = ((file)->f_op->read(file, buf, len, &(file)->f_pos)); set_fs(oldmm); __ret; })
+
+#define http_close_file(file) \
+		(fput(file))
+
+#define http_malloc http_kmalloc
+#define http_free http_kfree
+
+#define http_send_client(req, buf, len, push) \
+		send_dynamic_reply(req, (req)->sock, buf, len, push)
+
+#define HTTP_DECLARE_MUTEX DECLARE_MUTEX
+#define http_down down
+#define http_up up
+
+#define http_time() CURRENT_TIME
+
+#define http_direntry dentry
+#define http_direntry_open(d,r,fl) \
+	({ struct file *__f; lock_kernel(); __f = dentry_open(d,r,fl); unlock_kernel(); __f; })
+#define http_lookup_direntry(f,r,fl) \
+	({ struct dentry *__d; lock_kernel(); __d = http_lookup(f,r,fl); unlock_kernel(); __d; })
+#define http_file_size(file) ((file)->f_dentry->d_inode->i_size)
+
+#define http_mmap_page(file, virt, offset) \
+({ \
+	struct page *page = NULL; \
+        page = grab_cache_page((file)->f_dentry->d_inode->i_mapping, 0); \
+	if (page) { \
+		virt = (char *)kmap(page); \
+		UnlockPage(page); \
+	} \
+	page; \
+})
+
+#define http_direntry_error(dentry) \
+		(!(dentry) || IS_ERR(dentry) || !(dentry)->d_inode)
+#define http_dput(d) do { lock_kernel(); dput(d); unlock_kernel(); } while (0)
+#define http_mtime(dentry) \
+		((dentry)->d_inode->i_mtime)
+#define http_file_error(file) \
+		((!file) || !(file)->f_dentry || !(file)->f_dentry->d_inode)
+
+#define http_getpid() (current->pid)
+#define http_client_addr(req) ((req)->sock->sk->daddr)
+
+#define http_page page
+#define urlo_t urlobj_t
+
+extern int nr_async_io_pending (void);
+extern void trunc_headers (http_req_t *req);
+
+extern void __add_keepalive_timer (http_req_t *req);
+static inline void add_keepalive_timer (http_req_t *req)
+{
+	if (http_keepalive_timeout)
+		__add_keepalive_timer(req);
+}
+extern void del_keepalive_timer (http_req_t *req);
+extern void print_req (http_req_t *req);
+
+extern char tux_date [DATE_LEN];
+
+
+#endif
--- linux/include/net/http_u.h.orig	Fri Sep  1 07:28:26 2000
+++ linux/include/net/http_u.h	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,129 @@
+#ifndef _NET_HTTP_HTTP_U_H
+#define _NET_HTTP_HTTP_U_H
+
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * http_u.h: HTTP module API - HTTP interface to user-space
+ */
+
+#define __KERNEL_SYSCALLS__
+
+typedef enum http_versions {
+        HTTP_1_0,
+        HTTP_1_1
+} http_version_t;
+
+/*
+ * Request methods known to HTTP:
+ */
+typedef enum http_methods {
+        METHOD_NONE,
+        METHOD_GET,
+        METHOD_HEAD,
+        METHOD_POST,
+        METHOD_PUT
+} http_method_t;
+
+enum user_req {
+	HTTP_ACTION_STARTUP = 1,
+	HTTP_ACTION_SHUTDOWN = 2,
+	HTTP_ACTION_STARTTHREAD = 3,
+	HTTP_ACTION_STOPTHREAD = 4,
+	HTTP_ACTION_EVENTLOOP = 5,
+	HTTP_ACTION_GET_OBJECT = 6,
+	HTTP_ACTION_SEND_OBJECT = 7,
+	HTTP_ACTION_READ_OBJECT = 8,
+	HTTP_ACTION_FINISH_REQ = 9,
+	HTTP_ACTION_REGISTER_MODULE = 10,
+	HTTP_ACTION_UNREGISTER_MODULE = 11,
+	HTTP_ACTION_CURRENT_DATE = 12,
+	MAX_HTTP_ACTION
+};
+
+enum http_ret {
+	HTTP_RETURN_USERSPACE_REQUEST = 0,
+	HTTP_RETURN_EXIT = 1,
+	HTTP_RETURN_SIGNAL = 2,
+};
+
+#define MAX_MODULENAME_LEN 16
+#define MAX_URI_LEN 256
+#define MAX_POST_DATA 1024
+#define MAX_COOKIE_LEN 128
+#define DATE_LEN 30
+
+typedef struct user_req_s {
+	int http_version;
+	int http_method;
+	int sock;
+	int bytes_sent;
+	int http_status;
+	unsigned int client_host;
+	unsigned int objectlen;
+	char query[MAX_URI_LEN];
+	char *object_addr;
+	char objectname[MAX_URI_LEN];
+	int module_index;
+	char modulename[MAX_MODULENAME_LEN];
+	char post_data[MAX_POST_DATA];
+	char new_date[DATE_LEN];
+
+	int cookies_len;
+	char cookies[MAX_COOKIE_LEN];
+
+	int event;
+	int thread_nr;
+	void *id;
+	void *private;
+} user_req_t;
+
+extern char *HTTPAPI_docroot;
+extern char *HTTPAPI_version;
+
+extern int http (unsigned int action, user_req_t *req);
+
+extern void * HTTPAPI_malloc_shared (unsigned int len);
+
+#ifndef __KERNEL__
+#define BUG()								\
+do {									\
+	printf("CAD BUG at %d:%s!\n", __LINE__, __FILE__);		\
+	http(HTTP_ACTION_STOPTHREAD, NULL);				\
+	http(HTTP_ACTION_SHUTDOWN, NULL);				\
+	*(int*)0=0;							\
+	exit(-1);							\
+} while (0)
+
+#define LOCK_PREFIX "lock ; "
+
+#define barrier() __asm__ __volatile__("": : :"memory")
+struct __dummy { unsigned long a[100]; };
+#define ADDR (*(volatile struct __dummy *) addr)
+
+extern __inline__ int test_and_set_bit(int nr, volatile void * addr)
+{
+	int oldbit;
+
+	__asm__ __volatile__( LOCK_PREFIX
+		"btsl %2,%1\n\tsbbl %0,%0"
+		:"=r" (oldbit),"=m" (ADDR)
+		:"Ir" (nr));
+	return oldbit;
+}
+
+extern __inline__ void http_down (int *sem)
+{
+	while (test_and_set_bit(0, sem))
+		barrier();
+}
+
+extern __inline__ void http_up (int *sem)
+{
+	*((volatile int *)sem) = 0;
+}
+#endif
+
+#endif
--- linux/net/core/skbuff.c.orig	Mon May 22 18:50:55 2000
+++ linux/net/core/skbuff.c	Fri Sep  1 07:28:26 2000
@@ -51,6 +51,7 @@
 #include <linux/slab.h>
 #include <linux/cache.h>
 #include <linux/init.h>
+#include <linux/highmem.h>
 
 #include <net/ip.h>
 #include <net/protocol.h>
@@ -166,6 +167,7 @@
 {
 	struct sk_buff *skb;
 	u8 *data;
+	int i;
 
 	if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
 		static int count = 0;
@@ -183,6 +185,7 @@
 		skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
 		if (skb == NULL)
 			goto nohead;
+		memset(skb, 0, sizeof(*skb));
 	}
 
 	/* Get the DATA. Size must match skb_add_mtu(). */
@@ -204,6 +207,11 @@
 	skb->len = 0;
 	skb->is_clone = 0;
 	skb->cloned = 0;
+	skb->nr_frags = 0;
+
+	skb->data_len = 0;
+	for (i = 0; i < MAX_SKB_FRAGS; i++)
+		skb->frags[i] = NULL;
 
 	atomic_set(&skb->users, 1); 
 	atomic_set(skb_datarefp(skb), 1);
@@ -234,6 +242,7 @@
 	skb->security = 0;	/* By default packets are insecure */
 	skb->dst = NULL;
 	skb->rx_dev = NULL;
+	skb->nr_frags = 0;
 #ifdef CONFIG_NETFILTER
 	skb->nfmark = skb->nfcache = 0;
 	skb->nfct = NULL;
@@ -253,12 +262,15 @@
  */
 void kfree_skbmem(struct sk_buff *skb)
 {
+	if (skb->nr_frags)
+		BUG();
 	if (!skb->cloned || atomic_dec_and_test(skb_datarefp(skb)))  
 		kfree(skb->head);
 
 	skb_head_to_pool(skb);
 }
 
+extern int http_in_packet_delay, http_out_packet_delay;
 /**
  *	__kfree_skb - private function 
  *	@skb: buffer
@@ -275,6 +287,16 @@
 		       "on a list (from %p).\n", NET_CALLER(skb));
 		BUG();
 	}
+	if (atomic_read(&skb->users))
+		BUG();
+
+	if (!skb->is_clone) {
+		int i;
+
+		for (i = 0; i < skb->nr_frags; i++)
+			if (skb->frags[i]->frag_done)
+				skb->frags[i]->frag_done(skb, skb->frags[i]);
+	}
 
 	dst_release(skb->dst);
 	if(skb->destructor) {
@@ -288,9 +310,11 @@
 	nf_conntrack_put(skb->nfct);
 #endif
 #ifdef CONFIG_NET		
-	if(skb->rx_dev)
+	if (skb->rx_dev)
 		dev_put(skb->rx_dev);
-#endif		
+#endif
+	if (http_in_packet_delay || http_out_packet_delay)
+		del_timer(&skb->delay_timer);
 	skb_headerinit(skb, NULL, 0);  /* clean state */
 	kfree_skbmem(skb);
 }
@@ -395,22 +419,49 @@
 struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
 {
 	struct sk_buff *n;
+	int headerlen;
 
 	/*
 	 *	Allocate the copy buffer
 	 */
 	 
-	n=alloc_skb(skb->end - skb->head, gfp_mask);
-	if(n==NULL)
+	n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
+	if (!n)
 		return NULL;
 
 	/* Set the data pointer */
-	skb_reserve(n,skb->data-skb->head);
+	skb_reserve(n, skb->data-skb->head);
+
 	/* Set the tail pointer and length */
 	skb_put(n,skb->len);
+
 	/* Copy the bytes */
-	memcpy(n->head,skb->head,skb->end-skb->head);
+	headerlen = skb->tail - skb->head;
+	memcpy(n->head, skb->head, headerlen);
+
+	if (skb->nr_frags) {
+		int i, pos;
+		unsigned long vfrom;
+		unsigned long flags;
+
+		pos = headerlen;
+		__save_flags(flags);
+		__cli();
+		for (i = 0; i < skb->nr_frags; i++) {
+			skb_frag_t *frag = skb->frags[i];
+
+			vfrom = kmap_atomic(frag->page, KM_SKB_DATA);
+			memcpy(n->head+pos, (char *)vfrom + frag->page_offset,
+					frag->size);
+			pos += frag->size;
+			kunmap_atomic(vfrom, KM_SKB_DATA);
+		}
+		__restore_flags(flags);
+	}
+
 	n->csum = skb->csum;
+	
+
 	copy_skb_header(n, skb);
 
 	return n;
@@ -443,6 +494,8 @@
 {
 	struct sk_buff *n;
 
+	if (skb->data_len)
+		BUG();
 	/*
 	 *	Allocate the copy buffer
 	 */
@@ -492,3 +545,4 @@
 	for (i=0; i<NR_CPUS; i++)
 		skb_queue_head_init(&skb_head_pool[i].list);
 }
+
--- linux/net/core/sock.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/core/sock.c	Fri Sep  1 07:28:26 2000
@@ -1068,30 +1068,35 @@
  *	Default Socket Callbacks
  */
 
+#define HTTP_EVENT(sk) if (sk->http_data) idle_event(sk->http_data)
+
 void sock_def_wakeup(struct sock *sk)
 {
 	read_lock(&sk->callback_lock);
-	if (sk->sleep && waitqueue_active(sk->sleep))
+	if (sk->sleep /*&& waitqueue_active(sk->sleep)*/)
 		wake_up_interruptible_all(sk->sleep);
 	read_unlock(&sk->callback_lock);
+	HTTP_EVENT(sk);
 }
 
 void sock_def_error_report(struct sock *sk)
 {
 	read_lock(&sk->callback_lock);
-	if (sk->sleep && waitqueue_active(sk->sleep))
+	if (sk->sleep /*&& waitqueue_active(sk->sleep)*/)
 		wake_up_interruptible(sk->sleep);
 	sk_wake_async(sk,0,POLL_ERR); 
 	read_unlock(&sk->callback_lock);
+	HTTP_EVENT(sk);
 }
 
 void sock_def_readable(struct sock *sk, int len)
 {
 	read_lock(&sk->callback_lock);
-	if (sk->sleep && waitqueue_active(sk->sleep))
+	if (sk->sleep /*&& waitqueue_active(sk->sleep)*/)
 		wake_up_interruptible(sk->sleep);
 	sk_wake_async(sk,1,POLL_IN);
 	read_unlock(&sk->callback_lock);
+	HTTP_EVENT(sk);
 }
 
 void sock_def_write_space(struct sock *sk)
@@ -1102,7 +1107,7 @@
 	 * progress.  --DaveM
 	 */
 	if((atomic_read(&sk->wmem_alloc) << 1) <= sk->sndbuf) {
-		if (sk->sleep && waitqueue_active(sk->sleep))
+		if (sk->sleep /*&& waitqueue_active(sk->sleep)*/)
 			wake_up_interruptible(sk->sleep);
 
 		/* Should agree with poll, otherwise some programs break */
@@ -1111,12 +1116,14 @@
 	}
 
 	read_unlock(&sk->callback_lock);
+	HTTP_EVENT(sk);
 }
 
 void sock_def_destruct(struct sock *sk)
 {
 	if (sk->protinfo.destruct_hook)
 		kfree(sk->protinfo.destruct_hook);
+	HTTP_EVENT(sk);
 }
 
 void sock_init_data(struct socket *sock, struct sock *sk)
--- linux/net/core/dev.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/core/dev.c	Fri Sep  1 07:28:26 2000
@@ -90,6 +90,7 @@
 #include <net/profile.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
+#include <linux/brlock.h>
 #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
 #include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
 #endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
@@ -868,6 +869,8 @@
 	br_read_unlock(BR_NETPROTO_LOCK);
 }
 
+static void __netif_rx (struct sk_buff *newskb);
+
 /*
  *	Fast path for loopback frames.
  */
@@ -884,7 +887,7 @@
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
 	if (newskb->dst==NULL)
 		printk(KERN_DEBUG "BUG: packet without dst looped back 1\n");
-	netif_rx(newskb);
+	__netif_rx(newskb);
 }
 
 /**
@@ -899,6 +902,8 @@
  *	guarantee the frame will be transmitted as it may be dropped due
  *	to congestion or traffic shaping.
  */
+
+extern int multifragment_api;
  
 int dev_queue_xmit(struct sk_buff *skb)
 {
@@ -940,6 +945,20 @@
 				if (netdev_nit)
 					dev_queue_xmit_nit(skb,dev);
 
+
+				if (!dev->hard_start_xmit_dual
+#ifdef CONFIG_HTTP
+					||!multifragment_api
+#endif
+				) {
+					struct sk_buff *tmp;
+
+					tmp = skb_copy(skb, GFP_ATOMIC);
+					if (!tmp)
+						BUG();
+					dev_kfree_skb_irq(skb);
+					skb = tmp;
+				}
 				if (dev->hard_start_xmit(skb, dev) == 0) {
 					dev->xmit_lock_owner = -1;
 					spin_unlock_bh(&dev->xmit_lock);
@@ -969,7 +988,7 @@
 			Receiver routines
   =======================================================================*/
 
-int netdev_max_backlog = 300;
+int netdev_max_backlog = 300000;
 
 struct netif_rx_stats netdev_rx_stat[NR_CPUS];
 
@@ -1043,7 +1062,34 @@
  *	protocol layers.
  */
 
-void netif_rx(struct sk_buff *skb)
+/*
+ * artifical rx packet delay, in msecs.
+ */
+int http_in_packet_delay = 0;
+
+static void packet_rx_delay (unsigned long data)
+{
+	struct sk_buff *skb = (struct sk_buff *) data;
+
+	__netif_rx(skb);
+}
+
+void netif_rx (struct sk_buff *skb)
+{
+	if (http_in_packet_delay) {
+		struct timer_list *timer = &skb->delay_timer;
+
+		init_timer(timer);
+		timer->expires = jiffies + HZ*http_in_packet_delay/1000;
+		skb->delay_dev = NULL;
+		timer->data = (unsigned long) skb;
+		timer->function = packet_rx_delay;
+		add_timer(timer);
+	} else
+		__netif_rx (skb);
+}
+
+static void __netif_rx (struct sk_buff *skb)
 {
 	int this_cpu = smp_processor_id();
 	struct softnet_data *queue;
--- linux/net/ipv4/tcp.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/ipv4/tcp.c	Fri Sep  1 07:28:26 2000
@@ -426,6 +426,9 @@
 
 #include <asm/uaccess.h>
 
+#include <net/http.h>
+
+
 int sysctl_tcp_fin_timeout = TCP_FIN_TIMEOUT;
 
 struct tcp_mib	tcp_statistics[NR_CPUS*2];
@@ -543,13 +546,15 @@
  */
 unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait)
 {
-	unsigned int mask;
+	unsigned int mask = 0;
 	struct sock *sk = sock->sk;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	poll_wait(file, sk->sleep, wait);
-	if (sk->state == TCP_LISTEN)
-		return tcp_listen_poll(sk, wait);
+	if (sk->state == TCP_LISTEN) {
+		mask = tcp_listen_poll(sk, wait);
+		goto out;
+	}
 
 	/* Socket is not locked. We are protected from async events
 	   by poll logic and correct handling of state changes
@@ -622,6 +627,11 @@
 		if (tp->urg_data & TCP_URG_VALID)
 			mask |= POLLPRI;
 	}
+
+
+
+
+out:
 	return mask;
 }
 
@@ -887,7 +897,9 @@
 }
 
 /* When all user supplied data has been queued set the PSH bit */
-#define PSH_NEEDED (seglen == 0 && iovlen == 0)
+#define PSH_NEEDED(flags) \
+		(!(flags & MSG_NO_PUSH) && (seglen == 0) && (iovlen == 0))
+
 
 /*
  *	This routine copies from a user buffer into a socket,
@@ -904,6 +916,7 @@
 	int err, copied;
 	long timeo;
 
+
 	err = 0;
 	tp = &(sk->tp_pinfo.af_tcp);
 
@@ -952,8 +965,12 @@
 			/* Now we need to check if we have a half
 			 * built packet we can tack some data onto.
 			 */
-			if (tp->send_head && !(flags & MSG_OOB)) {
-				skb = sk->write_queue.prev;
+			if (tp->send_head && !(flags & MSG_OOB)
+				/*
+				 * Except if the skb is fragmented.
+				 */
+				&& !(skb = sk->write_queue.prev)->nr_frags)
+			{
 				copy = skb->len;
 				/* If the remote does SWS avoidance we should
 				 * queue the best we can if not we should in 
@@ -999,7 +1016,7 @@
 					from += copy;
 					copied += copy;
 					seglen -= copy;
-					if (PSH_NEEDED ||
+					if (PSH_NEEDED(flags) ||
 					    after(tp->write_seq, tp->pushed_seq+(tp->max_window>>1))) {
 						TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
 						tp->pushed_seq = tp->write_seq;
@@ -1012,7 +1029,7 @@
 
 			/* Determine how large of a buffer to allocate.  */
 			tmp = MAX_TCP_HEADER + 15 + tp->mss_cache;
-			if (copy < mss_now && !(flags & MSG_OOB)) {
+			if ((copy < mss_now) && !(flags & MSG_OOB) && !PSH_NEEDED(flags)) {
 				/* What is happening here is that we want to
 				 * tack on later members of the users iovec
 				 * if possible into a single frame.  When we
@@ -1054,7 +1071,7 @@
 
 			/* Prepare control bits for TCP header creation engine. */
 			TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
-			if (PSH_NEEDED ||
+			if (PSH_NEEDED(flags) ||
 			    after(tp->write_seq+copy, tp->pushed_seq+(tp->max_window>>1))) {
 				TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK|TCPCB_FLAG_PSH;
 				tp->pushed_seq = tp->write_seq + copy;
@@ -1119,7 +1136,7 @@
 		err = copied;
 	goto out;
 do_fault:
-	__kfree_skb(skb);
+	kfree_skb(skb);
 do_fault2:
 	err = -EFAULT;
 	goto out;
@@ -1156,7 +1173,7 @@
 		msg->msg_flags|=MSG_OOB;
 
 		if(len>0) {
-			if (!(flags & MSG_PEEK))
+			if (!(flags & MSG_PEEK) && !(flags & MSG_TRUNC))
 				err = memcpy_toiovec(msg->msg_iov, &c, 1);
 			len = 1;
 		} else
@@ -1186,7 +1203,7 @@
 static inline void tcp_eat_skb(struct sock *sk, struct sk_buff * skb)
 {
 	__skb_unlink(skb, &sk->receive_queue);
-	__kfree_skb(skb);
+	kfree_skb(skb);
 }
 
 /* Clean up the receive buffer for full frames taken by the user,
@@ -1265,6 +1282,7 @@
 {
 	DECLARE_WAITQUEUE(wait, current);
 
+
 	add_wait_queue(sk->sleep, &wait);
 
 	__set_current_state(TASK_INTERRUPTIBLE);
@@ -1280,6 +1298,8 @@
 
 	remove_wait_queue(sk->sleep, &wait);
 	__set_current_state(TASK_RUNNING);
+
+
 	return timeo;
 }
 
@@ -1355,7 +1375,7 @@
 		 * handling. FIXME: Need to check this doesnt impact 1003.1g
 		 * and move it down to the bottom of the loop
 		 */
-		if (signal_pending(current)) {
+		if (!nonblock && signal_pending(current)) {
 			if (copied)
 				break;
 			copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
@@ -1770,7 +1790,7 @@
 	while((skb=__skb_dequeue(&sk->receive_queue))!=NULL) {
 		u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - skb->h.th->fin;
 		data_was_unread += len;
-		__kfree_skb(skb);
+		kfree_skb(skb);
 	}
 
 	tcp_mem_reclaim(sk);
@@ -1786,6 +1806,7 @@
 	 */
 	if(data_was_unread != 0) {
 		/* Unread data was tossed, zap the connection. */
+		printk("TCP: %d bytes data unread!\n", data_was_unread);
 		NET_INC_STATS_USER(TCPAbortOnClose);
 		tcp_set_state(sk, TCP_CLOSE);
 		tcp_send_active_reset(sk, GFP_KERNEL);
@@ -1882,6 +1903,10 @@
 			if (tmo > TCP_TIMEWAIT_LEN) {
 				tcp_reset_keepalive_timer(sk, tcp_fin_time(tp));
 			} else {
+
+
+				if (sk->http_data) idle_event(sk->http_data);
+
 				atomic_inc(&tcp_orphan_count);
 				tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
 				goto out;
@@ -1900,6 +1925,11 @@
 			NET_INC_STATS_BH(TCPAbortOnMemory);
 		}
 	}
+
+
+	if (sk->http_data) idle_event(sk->http_data);
+
+
 	atomic_inc(&tcp_orphan_count);
 
 	if (sk->state == TCP_CLOSE)
@@ -1907,6 +1937,9 @@
 	/* Otherwise, socket is reprieved until protocol close. */
 
 out:
+
+
+	if (sk->http_data) idle_event(sk->http_data);
 	bh_unlock_sock(sk);
 	local_bh_enable();
 	sock_put(sk);
@@ -2376,7 +2409,7 @@
 		sysctl_local_port_range[1] = 61000;
 		sysctl_tcp_max_tw_buckets = 180000;
 		sysctl_tcp_max_orphans = 4096<<(order-4);
-		sysctl_max_syn_backlog = 1024;
+		sysctl_max_syn_backlog = 4096;
 	} else if (order < 3) {
 		sysctl_local_port_range[0] = 1024*(3-order);
 		sysctl_tcp_max_tw_buckets >>= (3-order);
--- linux/net/ipv4/tcp_output.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/ipv4/tcp_output.c	Fri Sep  1 07:28:26 2000
@@ -343,6 +343,9 @@
 	int nsize = skb->len - len;
 	u16 flags;
 
+	// FIXME: how should we do this?
+	if (skb->nr_frags)
+		return 0;
 	/* Get a new skb... force flag on. */
 	buff = tcp_alloc_skb(sk, nsize + MAX_TCP_HEADER + 15, GFP_ATOMIC);
 	if (buff == NULL)
@@ -642,7 +645,8 @@
 		 * would exceed the MSS.
 		 */
 		if ((next_skb_size > skb_tailroom(skb)) ||
-		    ((skb_size + next_skb_size) > mss_now))
+		    ((skb_size + next_skb_size) > mss_now) ||
+				skb->nr_frags || next_skb->nr_frags)
 			return;
 
 		/* Ok.  We will be able to collapse the packet. */
@@ -777,9 +781,9 @@
 	 * retransmit when old data is attached.  So strip it off
 	 * since it is cheap to do so and saves bytes on the network.
 	 */
-	if(skb->len > 0 &&
+	if(!skb->nr_frags && (skb->len > 0 &&
 	   (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
-	   tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
+	   tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1))) {
 		TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1;
 		skb_trim(skb, 0);
 		skb->csum = 0;
--- linux/net/ipv4/tcp_input.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/ipv4/tcp_input.c	Fri Sep  1 07:28:26 2000
@@ -393,6 +393,8 @@
 
 	if (skb->len >= 128)
 		tcp_grow_window(sk, tp, skb);
+
+	if (sk->http_data) idle_event(sk->http_data);
 }
 
 /* Called to compute a smoothed rtt estimate. The data fed to this
@@ -2450,7 +2452,7 @@
 		if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
 			SOCK_DEBUG(sk, "ofo packet was already received \n");
 			__skb_unlink(skb, skb->list);
-			__kfree_skb(skb);
+			kfree_skb(skb);
 			continue;
 		}
 		SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
@@ -2505,6 +2507,8 @@
 queue_and_out:
 			tcp_set_owner_r(skb, sk);
 			__skb_queue_tail(&sk->receive_queue, skb);
+			if (!sk->dead)
+				sk->data_ready(sk, skb->len);
 		}
 		tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 		if(skb->len)
@@ -2534,7 +2538,7 @@
 			tcp_fast_path_on(tp);
 
 		if (eaten) {
-			__kfree_skb(skb);
+			kfree_skb(skb);
 		} else if (!sk->dead)
 			sk->data_ready(sk, 0);
 		return;
@@ -2550,7 +2554,7 @@
 		printk("BUG: retransmit in tcp_data_queue: seq %X\n", TCP_SKB_CB(skb)->seq);
 		tcp_enter_quickack_mode(tp);
 		tcp_schedule_ack(tp);
-		__kfree_skb(skb);
+		kfree_skb(skb);
 		return;
 	}
 #endif
@@ -2616,7 +2620,7 @@
 		    before(seq, TCP_SKB_CB(skb1)->end_seq)) {
 			if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
 				/* All the bits are present. Drop. */
-				__kfree_skb(skb);
+				kfree_skb(skb);
 				tcp_dsack_set(tp, seq, end_seq);
 				goto add_sack;
 			}
@@ -2638,7 +2642,7 @@
 		       }
 		       __skb_unlink(skb1, skb1->list);
 		       tcp_dsack_extend(tp, TCP_SKB_CB(skb1)->seq, TCP_SKB_CB(skb1)->end_seq);
-		       __kfree_skb(skb1);
+		       kfree_skb(skb1);
 		}
 
 add_sack:
@@ -2668,7 +2672,7 @@
 			memcpy(skb_put(skb, skb_next->len), skb_next->data, skb_next->len);
 			__skb_unlink(skb_next, skb_next->list);
 			scb->end_seq = scb_next->end_seq;
-			__kfree_skb(skb_next);
+			kfree_skb(skb_next);
 			NET_INC_STATS_BH(TCPRcvCollapsed);
 		} else {
 			/* Lots of spare tailroom, reallocate this skb to trim it. */
@@ -2684,7 +2688,7 @@
 					       skb_headroom(skb));
 					__skb_append(skb, nskb);
 					__skb_unlink(skb, skb->list);
-					__kfree_skb(skb);
+					kfree_skb(skb);
 				}
 			}
 			skb = skb_next;
@@ -2798,7 +2802,7 @@
 	return;
 
 drop:
-	__kfree_skb(skb);
+	kfree_skb(skb);
 }
 
 /* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
@@ -2851,8 +2855,10 @@
 
 		clear_bit(SOCK_NOSPACE, &sock->flags);
 
-		if (sk->sleep && waitqueue_active(sk->sleep))
+		if (sk->sleep /*&& waitqueue_active(sk->sleep)*/) {
 			wake_up_interruptible(sk->sleep);
+			if (sk->http_data) idle_event(sk->http_data);
+		}
 
 		if (sock->fasync_list && !(sk->shutdown&SEND_SHUTDOWN))
 			sock_wake_async(sock, 2, POLL_OUT);
@@ -2963,6 +2969,7 @@
 		else
 			kill_pg(-sk->proc, SIGURG, 1);
 		sk_wake_async(sk, 3, POLL_PRI);
+		if (sk->http_data) idle_event(sk->http_data);
 	}
 
 	/* We may be adding urgent data when the last byte read was
@@ -3176,7 +3183,7 @@
 				 * on entry.
 				 */
 				tcp_ack(sk, skb, 0);
-				__kfree_skb(skb); 
+				kfree_skb(skb); 
 				tcp_data_snd_check(sk);
 				return 0;
 			} else { /* Header too small */
@@ -3240,7 +3247,7 @@
 
 no_ack:
 			if (eaten)
-				__kfree_skb(skb);
+				kfree_skb(skb);
 			else
 				sk->data_ready(sk, 0);
 			return 0;
@@ -3316,7 +3323,7 @@
 	TCP_INC_STATS_BH(TcpInErrs);
 
 discard:
-	__kfree_skb(skb);
+	kfree_skb(skb);
 	return 0;
 }
 
@@ -3471,15 +3478,13 @@
 
 	/* No ACK in the segment */
 
-	if (th->rst) {
+	if (th->rst)
 		/* rfc793:
 		 * "If the RST bit is set
 		 *
 		 *      Otherwise (no ACK) drop the segment and return."
 		 */
-
 		goto discard;
-	}
 
 	/* PAWS check. */
 	if (tp->ts_recent_stamp && tp->saw_tstamp && tcp_paws_check(tp, 0))
@@ -3528,7 +3533,7 @@
 	 */
 
 discard:
-	__kfree_skb(skb);
+	kfree_skb(skb);
 	return 0;
 }
 
@@ -3735,8 +3740,9 @@
 			}
 			break;
 		}
-	} else
+	} else {
 		goto discard;
+	}
 
 step6:
 	/* step 6: check the URG bit */
@@ -3777,7 +3783,7 @@
 
 	if (!queued) { 
 discard:
-		__kfree_skb(skb);
+		kfree_skb(skb);
 	}
 	return 0;
 }
--- linux/net/sched/sch_generic.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/sched/sch_generic.c	Fri Sep  1 07:28:26 2000
@@ -74,68 +74,123 @@
    NOTE: Called under dev->queue_lock with locally disabled BH.
 */
 
-int qdisc_restart(struct net_device *dev)
+extern int multifragment_api;
+
+static int xmit_skb (struct net_device *dev, struct sk_buff *skb)
 {
 	struct Qdisc *q = dev->qdisc;
-	struct sk_buff *skb;
 
-	/* Dequeue packet */
-	if ((skb = q->dequeue(q)) != NULL) {
-		if (spin_trylock(&dev->xmit_lock)) {
-			/* Remember that the driver is grabbed by us. */
-			dev->xmit_lock_owner = smp_processor_id();
-
-			/* And release queue */
-			spin_unlock(&dev->queue_lock);
-
-			if (!netif_queue_stopped(dev)) {
-				if (netdev_nit)
-					dev_queue_xmit_nit(skb, dev);
-
-				if (dev->hard_start_xmit(skb, dev) == 0) {
-					dev->xmit_lock_owner = -1;
-					spin_unlock(&dev->xmit_lock);
-
-					spin_lock(&dev->queue_lock);
-					return -1;
-				}
+	if (spin_trylock(&dev->xmit_lock)) {
+		/* Remember that the driver is grabbed by us. */
+		dev->xmit_lock_owner = smp_processor_id();
+
+		/* And release queue */
+		spin_unlock(&dev->queue_lock);
+
+		if (!netif_queue_stopped(dev)) {
+			if (netdev_nit)
+				dev_queue_xmit_nit(skb, dev);
+
+			if (!dev->hard_start_xmit_dual
+#ifdef CONFIG_HTTP
+				||!multifragment_api
+#endif
+			) {
+				struct sk_buff *tmp;
+
+				tmp = skb_copy(skb, GFP_ATOMIC);
+				if (!tmp)
+					BUG();
+				dev_kfree_skb_irq(skb);
+				skb = tmp;
 			}
 
-			/* Release the driver */
-			dev->xmit_lock_owner = -1;
-			spin_unlock(&dev->xmit_lock);
-			spin_lock(&dev->queue_lock);
-			q = dev->qdisc;
-		} else {
-			/* So, someone grabbed the driver. */
+			if (dev->hard_start_xmit(skb, dev) == 0) {
+				dev->xmit_lock_owner = -1;
+				spin_unlock(&dev->xmit_lock);
 
-			/* It may be transient configuration error,
-			   when hard_start_xmit() recurses. We detect
-			   it by checking xmit owner and drop the
-			   packet when deadloop is detected.
-			 */
-			if (dev->xmit_lock_owner == smp_processor_id()) {
-				kfree_skb(skb);
-				if (net_ratelimit())
-					printk(KERN_DEBUG "Dead loop on netdevice %s, fix it urgently!\n", dev->name);
+				spin_lock(&dev->queue_lock);
 				return -1;
 			}
-			netdev_rx_stat[smp_processor_id()].cpu_collision++;
 		}
 
-		/* Device kicked us out :(
-		   This is possible in three cases:
-
-		   0. driver is locked
-		   1. fastroute is enabled
-		   2. device cannot determine busy state
-		      before start of transmission (f.e. dialout)
-		   3. device is buggy (ppp)
+		/* Release the driver */
+		dev->xmit_lock_owner = -1;
+		spin_unlock(&dev->xmit_lock);
+		spin_lock(&dev->queue_lock);
+		q = dev->qdisc;
+	} else {
+		/* So, someone grabbed the driver. */
+
+		/* It may be transient configuration error,
+		   when hard_start_xmit() recurses. We detect
+		   it by checking xmit owner and drop the
+		   packet when deadloop is detected.
 		 */
+		if (dev->xmit_lock_owner == smp_processor_id()) {
+			kfree_skb(skb);
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Dead loop on netdevice %s, fix it urgently!\n", dev->name);
+			return -1;
+		}
+		netdev_rx_stat[smp_processor_id()].cpu_collision++;
+	}
+
+	/* Device kicked us out :(
+	   This is possible in three cases:
+
+	   0. driver is locked
+	   1. fastroute is enabled
+	   2. device cannot determine busy state
+	      before start of transmission (f.e. dialout)
+	   3. device is buggy (ppp)
+	 */
+
+	q->ops->requeue(skb, q);
+	netif_schedule(dev);
+	return 1;
+}
+
+/*
+ * artifical xmit packet delay, in msecs.
+ */
+int http_out_packet_delay = 0;
+
+static void packet_tx_delay (unsigned long data)
+{
+	struct sk_buff *skb = (struct sk_buff *)data;
 
-		q->ops->requeue(skb, q);
-		netif_schedule(dev);
-		return 1;
+	spin_lock_bh(&skb->dev->queue_lock);
+	if (atomic_read(&skb->users) == 1)
+		dev_kfree_skb_irq(skb);
+	else {
+		atomic_dec(&skb->users);
+		xmit_skb(skb->dev, skb);
+	}
+	spin_unlock_bh(&skb->dev->queue_lock);
+}
+
+int qdisc_restart(struct net_device *dev)
+{
+	struct Qdisc *q = dev->qdisc;
+	struct sk_buff *skb;
+
+	/* Dequeue packet */
+	if ((skb = q->dequeue(q)) != NULL) {
+		if (http_out_packet_delay) {
+			struct timer_list *timer = &skb->delay_timer;
+
+			BUG();
+			init_timer(timer);
+			timer->expires = jiffies + HZ*http_out_packet_delay/1000;
+			timer->data = (unsigned long) skb;
+			timer->function = packet_tx_delay;
+			atomic_inc(&skb->users);
+			add_timer(timer);
+		} else {
+			int ret = xmit_skb(dev, skb);
+			return ret;
+		}
 	}
 	return q->q.qlen;
 }
--- linux/net/socket.c.orig	Fri Sep  1 07:27:07 2000
+++ linux/net/socket.c	Fri Sep  1 07:28:26 2000
@@ -328,7 +328,7 @@
  *	but we take care of internal coherence yet.
  */
 
-static int sock_map_fd(struct socket *sock)
+int sock_map_fd(struct socket *sock)
 {
 	int fd;
 	struct qstr this;
--- linux/net/netsyms.c.orig	Fri Sep  1 07:27:06 2000
+++ linux/net/netsyms.c	Fri Sep  1 07:28:26 2000
@@ -51,7 +51,7 @@
 
 extern struct net_proto_family inet_family_ops;
 
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_HTTP) || defined (CONFIG_HTTP_MODULE)
 #include <linux/in6.h>
 #include <linux/icmpv6.h>
 #include <net/ipv6.h>
@@ -256,7 +256,7 @@
 EXPORT_SYMBOL(ipv6_addr_type);
 EXPORT_SYMBOL(icmpv6_send);
 #endif
-#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE) || defined (CONFIG_HTTP) || defined (CONFIG_HTTP_MODULE)
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_stream_ops);
 EXPORT_SYMBOL(inet_release);
@@ -312,6 +312,7 @@
 EXPORT_SYMBOL(tcp_getsockopt);
 EXPORT_SYMBOL(tcp_recvmsg);
 EXPORT_SYMBOL(tcp_send_synack);
+EXPORT_SYMBOL(tcp_send_skb);
 EXPORT_SYMBOL(tcp_check_req);
 EXPORT_SYMBOL(tcp_child_process);
 EXPORT_SYMBOL(tcp_parse_options);
--- linux/net/Makefile.orig	Fri Sep  1 07:26:59 2000
+++ linux/net/Makefile	Fri Sep  1 07:28:26 2000
@@ -48,6 +48,15 @@
   endif
 endif
 
+ifeq ($(CONFIG_HTTP),y)
+SUB_DIRS += http
+MOD_SUB_DIRS += http
+else
+  ifeq ($(CONFIG_HTTP),m)
+  MOD_SUB_DIRS += http
+  endif
+endif
+
 ifeq ($(CONFIG_KHTTPD),y)
 SUB_DIRS += khttpd
 else
--- linux/net/Config.in.orig	Fri Sep  1 07:26:36 2000
+++ linux/net/Config.in	Fri Sep  1 07:28:26 2000
@@ -20,6 +20,7 @@
 tristate 'Unix domain sockets' CONFIG_UNIX
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
+   source net/http/Config.in
    source net/ipv4/Config.in
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 #   IPv6 as module will cause a CRASH if you try to unload it
--- linux/net/http/Config.in.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/Config.in	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,8 @@
+tristate '  Threaded linUX HTTP layer (TUX)' CONFIG_HTTP
+if [ "$CONFIG_HTTP" = "y" ]; then
+  tristate '    CAD module' CONFIG_HTTP_CAD
+  tristate '    CAD2 module' CONFIG_HTTP_CAD2
+  tristate '    External CGI module' CONFIG_HTTP_EXTCGI
+  bool     ' debug TUX' CONFIG_HTTP_DEBUG
+fi
+
--- linux/net/http/Makefile.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/Makefile	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,38 @@
+#
+# Makefile for TUX
+#
+
+O_TARGET := http.o
+MOD_LIST_NAME := NET_MODULES
+
+O_OBJS := accept.o input.o userspace.o cachemiss.o output.o \
+	  redirect.o logger.o http_parser.o proc.o httpmain.o cgi.o
+
+OX_OBJS := httpmod.o
+
+ifeq ($(CONFIG_HTTP_CAD2),y)
+  O_OBJS += CAD2.o
+else
+  ifeq ($(CONFIG_HTTP_CAD2),m)
+    M_OBJS += CAD2.o
+  endif
+endif
+
+ifeq ($(CONFIG_HTTP_CAD),y)
+  O_OBJS += CAD.o
+else
+  ifeq ($(CONFIG_HTTP_CAD),m)
+    M_OBJS += CAD.o
+  endif
+endif
+
+ifeq ($(CONFIG_HTTP_EXTCGI),y)
+  O_OBJS += extcgi.o
+else
+  ifeq ($(CONFIG_HTTP_EXTCGI),m)
+    M_OBJS += extcgi.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
--- linux/net/http/accept.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/accept.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,552 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * accept.c: accept connections - sleep if there is no work left.
+ */
+
+#include <net/http.h>
+
+int http_nagle = 0;
+
+atomic_t allocations [MAX_NR_POINTS];
+atomic_t allocations_total_alloc [MAX_NR_POINTS];
+atomic_t allocations_total_free [MAX_NR_POINTS];
+
+void * http_kmalloc (int size, enum kmalloc_ids id)
+{
+	int count = 3, flag = GFP_USER, priority = 3, nr = 0, freed;
+	void *tmp;
+
+	if (id >= MAX_NR_POINTS)
+		HTTP_BUG();
+repeat:
+	tmp = kmalloc(size, flag);
+	if (!tmp) {
+		/*
+		 * This might look a bit excessive but we take
+		 * no chances :-)
+		 */
+		switch (count) {
+			case 3:
+				count--;
+				goto repeat;
+			case 2:
+				__set_task_state(current, TASK_RUNNING);
+				current->policy |= SCHED_YIELD;
+				schedule();
+				count--;
+				goto repeat;
+			case 1:
+				__set_task_state(current, TASK_INTERRUPTIBLE);
+				schedule_timeout(2);
+				count--;
+				goto repeat;
+			case 0:
+				freed = shrink_dcache_memory(32, priority, flag);
+				freed += shrink_icache_memory(32, priority, flag);
+				if (priority)
+					priority--;
+				else
+					printk("http_kmalloc allocation problem (size %d bytes (freed %d), <%p>), retrying <#%d>.\n", size, freed, __builtin_return_address(0), nr++);
+				__set_task_state(current, TASK_INTERRUPTIBLE);
+				schedule_timeout(5);
+				goto repeat;
+			default:
+				HTTP_BUG();
+		}
+	}
+
+	atomic_inc(allocations+id);
+	atomic_inc(allocations_total_alloc+id);
+	Dprintk("http_kmalloc(%d,ID:%d), <%p> = %p\n", size, id, __builtin_return_address(0), tmp);
+	return tmp;
+}
+
+void http_kfree (void *ptr, enum kmalloc_ids id)
+{
+	Dprintk("http_kfree(%p,ID:%d), <%p>\n", ptr, id, __builtin_return_address(0));
+	if (id >= MAX_NR_POINTS)
+		HTTP_BUG();
+	kfree(ptr);
+	atomic_dec(allocations+id);
+	atomic_inc(allocations_total_free+id);
+}
+
+extern char * print_async_io_threads (char *buf);
+
+char * print_http_allocations (char *buf)
+{
+	int i;
+
+	buf += sprintf(buf, "HTTP allocations:\n");
+	for (i = 0; i < __ALLOC_LAST; i++)
+		buf += sprintf(buf, " - (%02d): A:%d, F:%d, +-:%d\n",
+			i, atomic_read(allocations_total_alloc+i),
+			atomic_read(allocations_total_free+i),
+			atomic_read(allocations+i));
+
+
+	return print_async_io_threads(buf);
+}
+
+/*
+ * Static request so that we can log out-of-memory
+ * connections as well.
+ */
+http_req_t out_of_mem_req = { objectname: "", http_status: 503 };
+
+
+struct socket * start_listening (const int port, unsigned int address)
+{
+	struct socket *sock;
+	struct sockaddr_in sin;
+	int error;
+
+	/* First create a socket */
+
+	printk("start_listen(%d:%08x)\n", port, address);
+	error = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
+	if (error < 0) {
+		printk(KERN_ERR "Error during creation of socket.\n");
+		return NULL;
+	}
+
+	/* Now bind the socket */
+
+	sin.sin_family	     = AF_INET;
+	sin.sin_addr.s_addr  = htonl(address);
+	sin.sin_port	 = htons((unsigned short)port);
+
+	error = sock->ops->bind(sock,(struct sockaddr*)&sin, sizeof(sin));
+	if (error < 0) {
+		printk(KERN_ERR
+
+"HTTP: Error binding socket. This means that some other \n"
+"	daemon is (or was a short time ago) using port %i.\n",port);
+
+		goto err;
+	}
+
+	sock->sk->reuse = 1;
+	sock->sk->linger = 0;
+	sock->sk->tp_pinfo.af_tcp.linger2 = 0;
+	sock->sk->tp_pinfo.af_tcp.defer_accept = 1;
+
+	/* Now, start listening on the socket */
+
+	error = sock->ops->listen(sock, http_max_backlog);
+	if (error) {
+		printk(KERN_ERR "HTTP: Error listening on socket.\n");
+		goto err;
+	}
+	return sock; 
+err:
+	sock_release(sock);
+	return NULL;
+}
+
+void stop_listening (struct socket **sock)
+{
+	struct socket *tmp;
+	if (!*sock)
+		return;
+
+	tmp = *sock;
+	*sock = NULL;
+	sock_release(tmp);
+}
+
+static inline void __kfree_req (http_req_t *req, threadinfo_t * ti)
+{
+	list_del(&req->all);
+	ti->nr_requests--;
+	http_kfree(req, ALLOC_REQ);
+}
+
+void flush_freequeue (threadinfo_t * ti)
+{
+	struct list_head *tmp;
+	http_req_t *req;
+
+	spin_lock(&ti->free_requests_lock);
+	while (ti->nr_free_requests) {
+		ti->nr_free_requests--;
+		tmp = ti->free_requests.next;
+		req = list_entry(tmp, http_req_t, free);
+		list_del(tmp);
+		DEC_STAT(nr_free_pending);
+		__kfree_req(req, ti);
+	}
+	spin_unlock(&ti->free_requests_lock);
+}
+
+static http_req_t * kmalloc_req (threadinfo_t * ti)
+{
+	struct list_head *tmp;
+	http_req_t *req;
+
+	spin_lock(&ti->free_requests_lock);
+	if (ti->nr_free_requests) {
+		struct list_head tmp2;
+
+		ti->nr_free_requests--;
+		tmp = ti->free_requests.next;
+		req = list_entry(tmp, http_req_t, free);
+		list_del(tmp);
+		DEC_STAT(nr_free_pending);
+		req->magic = HTTP_MAGIC;
+		check_req_list(req, NULL);
+		spin_unlock(&ti->free_requests_lock);
+		tmp2 = req->all;
+//		memset (req, 0, sizeof(*req));
+		req->all = tmp2;
+	} else {
+		spin_unlock(&ti->free_requests_lock);
+		req = http_kmalloc(sizeof(*req), ALLOC_REQ); 
+		if (!req)
+			return NULL;
+		ti->nr_requests++;
+		memset (req, 0, sizeof(*req));
+		list_add(&req->all, &ti->all_requests);
+	}
+	req->magic = HTTP_MAGIC;
+	INC_STAT(nr_allocated);
+	init_waitqueue_entry(&req->sleep,current);
+	INIT_LIST_HEAD(&req->free);
+	INIT_LIST_HEAD(&req->input);
+	INIT_LIST_HEAD(&req->userspace);
+	INIT_LIST_HEAD(&req->cachemiss);
+	INIT_LIST_HEAD(&req->output);
+	INIT_LIST_HEAD(&req->redirect);
+	INIT_LIST_HEAD(&req->finish);
+	req->objectname_len = 0;
+	req->ti = ti;
+	req->timestamp = CURRENT_TIME;
+	req->userspace_fd = -1;
+	init_timer(&req->keepalive_timer);
+	check_req_list(req, NULL);
+	Dprintk("allocated NEW req %p.\n", req);
+	return req;
+}
+
+void kfree_req (http_req_t *req, threadinfo_t * ti)
+{
+	Dprintk("freeing req %p.\n", req);
+	spin_lock(&ti->free_requests_lock);
+	check_req_list(req, NULL);
+	req->magic = 0;
+	DEC_STAT(nr_allocated);
+	if (req->sock)
+		HTTP_BUG();
+	if (req->dentry)
+		HTTP_BUG();
+	if (req->urlo || req->prev_urlo)
+		HTTP_BUG();
+	if (req->private)
+		HTTP_BUG();
+	if (ti->nr_free_requests > 1000) {
+		spin_unlock(&ti->free_requests_lock);
+		__kfree_req(req, ti);
+		return;
+	}
+	ti->nr_free_requests++;
+	// the free requests queue is LIFO
+	list_add(&req->free, &ti->free_requests);
+	INC_STAT(nr_free_pending);
+	spin_unlock(&ti->free_requests_lock);
+}
+
+void del_keepalive_timer (http_req_t *req)
+{
+	if (timer_pending(&req->keepalive_timer))
+		del_timer(&req->keepalive_timer);
+}
+
+static void keepalive_timeout_fn (unsigned long data)
+{
+	http_req_t *req = (http_req_t *)data;
+
+	printk("CONNECTION TIMEOUT FOR REQ %p AFTER %d SECONDS!\n", req, http_keepalive_timeout);
+	print_req(req);
+}
+
+void __add_keepalive_timer (http_req_t *req)
+{
+	struct timer_list *timer = &req->keepalive_timer;
+
+	if (!http_keepalive_timeout)
+		HTTP_BUG();
+
+	timer->expires = jiffies + http_keepalive_timeout * HZ;
+	timer->data = (unsigned long) req;
+	timer->function = &keepalive_timeout_fn;
+	add_timer(timer);
+}
+
+void idle_event (http_req_t *req)
+{
+	threadinfo_t *ti;
+	unsigned long flags;
+
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	Dprintk("EVENT req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+	ti = req->ti;
+
+	if (!test_and_clear_bit(0, &req->idle_input)) {
+		Dprintk("data ready event at <%p>, on non-idle %p.\n", __builtin_return_address(0), req);
+		if (ti->thread)
+			wake_up_process(ti->thread);
+		return;
+	}
+
+	Dprintk("data ready event at <%p>, %p was idle!\n", __builtin_return_address(0), req);
+	del_keepalive_timer(req);
+	DEC_STAT(nr_idle_input_pending);
+
+	spin_lock_irqsave(&ti->input_lock, flags);
+	check_req_list(req, NULL);
+	list_add_tail(&req->input, &ti->input_pending);
+	INC_STAT(nr_input_pending);
+	spin_unlock_irqrestore(&ti->input_lock, flags);
+
+	if (ti->thread)
+		wake_up_process(ti->thread);
+}
+
+static void http_data_ready (struct sock *sk, int len)
+{
+	http_req_t *req = sk->http_data;
+
+	if (!req)
+		HTTP_BUG();
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+
+	if (req->old_data_ready)
+		req->old_data_ready(sk, len);
+
+	if (len)
+		idle_event(req);
+}
+
+static void http_destruct (struct sock *sk)
+{
+	http_req_t *req = sk->http_data;
+
+	if (!req)
+		HTTP_BUG();
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	if (req->old_destruct)
+		req->old_destruct(sk);
+
+	idle_event(req);
+}
+
+static void http_state_change (struct sock *sk)
+{
+	http_req_t *req = sk->http_data;
+
+	if (!req)
+		HTTP_BUG();
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	if (req->old_destruct)
+		req->old_state_change(sk);
+
+	idle_event(req);
+}
+
+static void link_http_socket (http_req_t *req, struct socket *sock)
+{
+	/*
+	 * (No need to lock the socket, we just want to
+	 * make sure that events from now on go through
+	 * http_data_ready())
+	 */
+	req->sock = sock;
+	sock->sk->tp_pinfo.af_tcp.nonagle = !http_nagle;
+
+	req->old_data_ready = sock->sk->data_ready;
+	req->old_state_change = sock->sk->state_change;
+	req->old_write_space = sock->sk->write_space;
+	req->old_destruct = sock->sk->destruct;
+	sock->sk->http_data = req;
+	xchg(&sock->sk->data_ready, http_data_ready);
+	xchg(&sock->sk->state_change, http_state_change);
+	xchg(&sock->sk->destruct, http_destruct);
+
+	add_wait_queue(sock->sk->sleep, &req->sleep);
+}
+
+void unlink_http_socket (http_req_t *req)
+{
+	struct sock *sk;
+	
+
+	if (!req->sock)
+		return;
+	sk = req->sock->sk;
+	if (!sk)
+		return;
+
+	lock_sock(sk);
+	TCP_CHECK_TIMER(sk);
+
+	xchg(&sk->data_ready, req->old_data_ready);
+	xchg(&sk->state_change, req->old_state_change);
+	xchg(&sk->destruct, req->old_destruct);
+	sk->http_data = NULL;
+	remove_wait_queue(sk->sleep,&(req->sleep));
+
+	TCP_CHECK_TIMER(sk);
+	release_sock(sk);
+}
+
+#define MAX_ACCEPT_HIST 1100
+int accept_hist [MAX_ACCEPT_HIST];
+
+void profile_accept_queue (struct open_request *head)
+{
+	int count = 0;
+
+	if (!head)
+		goto out;
+	count++;
+	while (head->dl_next)
+		head = head->dl_next, count++;
+out:
+	if (count >= MAX_ACCEPT_HIST)
+		count = MAX_ACCEPT_HIST-1;
+	accept_hist[count]++;
+}
+
+char * print_accept_hist (char *buf)
+{
+	int i;
+
+	buf += sprintf(buf, "HTTP TCP-accept hist in syslog.\n");
+	printk("HTTP TCP-accept hist:\n");
+	for (i = 0; i < MAX_ACCEPT_HIST; i++)
+		if (accept_hist[i])
+			printk("%03d: %d\n", i, accept_hist[i]);
+
+	return buf;
+}
+/*
+ * Puts newly accepted connections into the inputqueue.
+ */
+int accept_requests (threadinfo_t *ti)
+{
+	struct socket *sock;
+	http_req_t *new_req;
+	struct socket *new_sock;
+	int count = 0, last_count = 0;
+	int error;
+	int socknr = 0;
+
+repeat:
+	for (socknr = 0; socknr < CONFIG_HTTP_NUMSOCKETS; socknr++) {
+		sock = ti->listen[socknr];
+		if (!sock)
+			break;
+
+	/*
+	 * Quick test to see if there are connections on the queue.
+	 * This is cheaper than accept() itself because this saves us
+	 * the allocation of a new socket. (Which doesn't seem to be
+	 * used anyway)
+	 */
+	if (sock->sk->tp_pinfo.af_tcp.accept_queue) {
+		int ret;
+
+		if (current->need_resched)
+			break;
+		if (!count++)
+			__set_task_state(current, TASK_RUNNING);
+
+		new_sock = sock_alloc();
+		if (!new_sock)
+			goto out;
+		new_sock->type = sock->type;
+		new_sock->ops = sock->ops;
+
+//		profile_accept_queue(sock->sk->tp_pinfo.af_tcp.accept_queue);
+		error = sock->ops->accept(sock, new_sock, O_NONBLOCK);
+
+		if (error < 0)
+			goto err;
+		if (new_sock->sk->state == TCP_CLOSE)
+			goto err;
+
+		/* Allocate a request-entry for the connection */
+		new_req = kmalloc_req(ti);
+
+		if (!new_req) {
+ 			/*
+			 * Service not available, try again later.
+			 * since we have no request structure, we
+			 * explicitly log though the static 'out of mem'
+			 * request:
+			 */
+			send_err_try_later(new_sock);
+			log_request(&out_of_mem_req);
+			goto err;
+		}
+		link_http_socket(new_req, new_sock);
+
+#if 1
+		add_keepalive_timer(new_req);
+		if (test_and_set_bit(0, &new_req->idle_input))
+			HTTP_BUG();
+		INC_STAT(nr_idle_input_pending);
+		Dprintk("idled request %p.\n", new_req);
+
+		ret = parse_request(new_req, ti);
+		if (ret == -1)
+			continue;
+		if (new_req->userspace_module) {
+			if (ti->userspace_req)
+				HTTP_BUG();
+			goto out;
+		}
+		if (new_req->idle_input)
+			HTTP_BUG();
+		if (ret == -2) {
+			flush_request(new_req, ti);
+			continue;
+		}
+		if (new_req->redirect_secondary) {
+			list_add_tail(&new_req->redirect, &ti->redirect_pending);
+			INC_STAT(nr_redirect_pending);
+			check_req_list(new_req, &new_req->redirect);
+			continue;
+		}
+		send_generic_reply(new_req, ti);
+		if (!list_empty(&ti->output_pending))
+			send_replies(ti);
+		if (!list_empty(&ti->finish_pending))
+			finish_requests(ti);
+#else
+		spin_lock_irq(&ti->input_lock);
+		list_add_tail(&new_req->input, &ti->input_pending);
+		INC_STAT(nr_input_pending);
+		spin_unlock_irq(&ti->input_lock);
+#endif
+	}
+	}
+	if (count != last_count) {
+		last_count = count;
+		goto repeat;
+	}
+
+out:
+	return count;
+err:
+	sock_release(new_sock);
+	goto out;
+}
+
--- linux/net/http/output.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/output.c	Fri Sep  1 07:40:06 2000
@@ -0,0 +1,506 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * output.c: Send actual file-data to pending connections
+ */
+
+#include <net/http.h>
+
+int send_dynamic_reply (http_req_t *req, struct socket *sock, const char *buf, const size_t length, int push)
+{
+	mm_segment_t oldmm;
+	struct msghdr msg;
+	struct iovec iov;
+	int len, written = 0, left = length;
+
+	if (req) {
+		if (req->magic != HTTP_MAGIC)
+			HTTP_BUG();
+		if (req->no_output)
+			HTTP_BUG();
+	}
+
+	msg.msg_name     = 0;
+	msg.msg_namelen  = 0;
+	msg.msg_iov	 = &iov;
+	msg.msg_iovlen   = 1;
+	msg.msg_control  = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags    = MSG_NOSIGNAL;
+	if (!push)
+		msg.msg_flags |= MSG_NO_PUSH;
+repeat_send:
+	msg.msg_iov->iov_len = left;
+	msg.msg_iov->iov_base = (char *) buf + written;
+
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+	len = sock_sendmsg(sock, &msg, left);
+	set_fs(oldmm);
+
+	if ((len == -512) || (len == -EAGAIN)) {
+		reap_kids();
+		goto repeat_send;
+	}
+	if (len > 0) {
+		written += len;
+		left -= len;
+		if (left)
+			goto repeat_send;
+	}
+	if (len < 0)
+		Dprintk("hm, sendmsg ret: %d, written: %d, left: %d.\n",
+					len, written, left);
+	else {
+		if (written != length)
+			HTTP_BUG();
+		if (left)
+			HTTP_BUG();
+	}
+	return written;
+}
+
+static int __local_tcp_send_csumcache(struct sock *sk, int flags, struct sk_buff *skb, int datalen, int last)
+{
+	int err;
+	struct tcp_opt *tp;
+
+	err = 0;
+	tp = &sk->tp_pinfo.af_tcp;
+
+	TCP_CHECK_TIMER(sk);
+
+	/* Wait for a connection to finish. */
+	if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
+		printk("whoops 3\n");
+
+	clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+
+	/* Stop on errors. */
+	if (sk->err)
+		goto do_sock_err;
+
+	/* Make sure that we are established. */
+	if (sk->shutdown & SEND_SHUTDOWN)
+		goto do_shutdown;
+
+	/* Prepare control bits for TCP header creation engine. */
+	TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
+	if (last)
+		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
+	TCP_SKB_CB(skb)->sacked = 0;
+	TCP_SKB_CB(skb)->urg_ptr = 0;
+	TCP_SKB_CB(skb)->seq = tp->write_seq;
+	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + datalen;
+
+	/* This advances tp->write_seq for us. */
+//	printk("TCP-sending SKB %p: len:%d, data_len:%d, head:%p, data:%p, real_data:%p, tail:%p, end:%p.\n", skb, skb->len, skb->data_len, skb->head, skb->data, skb->real_data, skb->tail, skb->end);
+	tcp_send_skb(sk, skb, 0, datalen);
+	err = datalen;
+out:
+	TCP_CHECK_TIMER(sk);
+	return err;
+
+do_sock_err:
+	err = sock_error(sk);
+	goto out;
+do_shutdown:
+	err = -EPIPE;
+	goto out;
+}
+
+int nr_csumcache;
+int nr_csumcache_hit;
+int nr_csumcache_miss;
+
+void unuse_frag (struct sk_buff *skb, skb_frag_t *frag)
+{
+	urlobj_t *urlo = (urlobj_t *)frag->data;
+
+	if (!urlo)
+		HTTP_BUG();
+	Dprintk("unuse urlo (%p), skb %p (%d), frag %p.\n", urlo, skb, atomic_read(&skb->users), frag);
+	if (atomic_read(&skb->users) > 0)
+		HTTP_BUG();
+
+	put_urlo(urlo);
+}
+
+static void unuse_dynbuf (struct sk_buff *skb, skb_frag_t *frag)
+{
+	Dprintk("unuse dynbuf skb %p (%d), frag %p.\n", skb, atomic_read(&skb->users), frag);
+	if (atomic_read(&skb->users))
+		HTTP_BUG();
+
+	http_kfree(frag, ALLOC_DYNBUF);
+}
+
+static void unuse_dynfrag (struct sk_buff *skb, skb_frag_t *frag)
+{
+	urlobj_t *urlo = (urlobj_t *)frag->data;
+
+	if (!urlo)
+		HTTP_BUG();
+	Dprintk("unuse urlo (%p), skb %p (%d), frag %p.\n", urlo, skb, atomic_read(&skb->users), frag);
+	if (atomic_read(&skb->users))
+		HTTP_BUG();
+
+	http_kfree(frag, ALLOC_DYNFRAG);
+	put_urlo(urlo);
+}
+
+skb_frag_t * build_dynbuf_frag (http_req_t *req, int size)
+{
+	skb_frag_t *dynfrag;
+	struct page *page;
+	char *buf;
+
+	buf = http_kmalloc(sizeof(*dynfrag) + size, ALLOC_DYNBUF);
+	page = virt_to_page(buf);
+
+	dynfrag = (skb_frag_t *)buf;
+	dynfrag->size = size;
+	dynfrag->page = page;
+	dynfrag->page_offset = sizeof(*dynfrag) +
+					buf-(char *)page_address(page);
+	dynfrag->frag_done = unuse_dynbuf;
+	dynfrag->data = buf + sizeof(*dynfrag);
+	dynfrag->private = NULL;
+
+	return dynfrag;
+}
+
+static skb_frag_t * build_SSI_frag (http_req_t *req, skb_frag_t *frag)
+{
+	skb_frag_t *dynfrag;
+	struct page *page;
+	char *buf;
+	int ret;
+
+	if (!req->SSI || !frag->private)
+		HTTP_BUG();
+	if (!req->tcapi)
+		HTTP_BUG();
+
+	buf = http_kmalloc(sizeof(*frag) + frag->size, ALLOC_DYNFRAG);
+	page = virt_to_page(buf);
+
+	dynfrag = (skb_frag_t *)buf;
+	dynfrag->size = frag->size;
+	dynfrag->page = page;
+	dynfrag->page_offset = sizeof(skb_frag_t) +
+					buf-(char *)page_address(page);
+	dynfrag->frag_done = unuse_dynfrag;
+	dynfrag->data = frag->data;
+	dynfrag->private = frag->private;
+
+	ret = req->tcapi->generate_SSI_entry(req, dynfrag);
+	if (ret)
+		return dynfrag;
+
+	__free_page(page);
+	return frag;
+}
+
+static struct sk_buff *http_alloc_skb (void)
+{
+	struct sk_buff *skb;
+	int skbsize;
+
+	skbsize = MAX_TCP_HEADER + 15;
+repeat_alloc:
+	skb = alloc_skb(skbsize, GFP_USER);
+	if (!skb)
+		goto repeat_alloc;
+	atomic_set(&skb->users, 1);
+	skb_reserve(skb, MAX_TCP_HEADER);
+	skb->csum = 0;
+
+	return skb;
+}
+
+void add_frag_skb (http_req_t *req, struct sk_buff *skb, skb_frag_t *frag)
+{
+	unsigned int orig_csum;
+	int nr;
+
+	if (req->SSI && frag->private)
+		frag = build_SSI_frag(req, frag);
+
+	if (frag->size < 2)
+		HTTP_BUG();
+//	if (skb->nr_frags)
+//		HTTP_BUG();
+
+	nr = skb->nr_frags++;
+
+	/*
+	 * Fold checksum:
+	 */
+//	if (skb->csum)
+//		HTTP_BUG();
+	orig_csum = skb->csum;
+	if (skb->data_len & 1) {
+		skb->csum += frag->csum >> 8;
+		skb->csum += (frag->csum & 0xff) << 24;
+	} else
+		skb->csum += frag->csum;
+	if (skb->csum < orig_csum)
+		skb->csum++;
+
+	skb->frags[nr] = frag;
+	skb->len += frag->size;
+	skb->data_len += frag->size;
+
+	get_urlo(req->urlo);
+}
+
+static inline void push_sk (struct sock *sk)
+{
+	struct tcp_opt *tp;
+
+	tp = &sk->tp_pinfo.af_tcp;
+	if (tcp_write_xmit(sk))
+		tcp_check_probe_timer(sk, tp);
+	__tcp_push_pending_frames(sk, tp, tcp_current_mss(sk));
+}
+
+int http_send_object (http_req_t *req, int include_header, int push)
+{
+	int ret = 0, datasize, i, size = 0, packets;
+	skb_frag_t *frag;
+	struct sk_buff *skb;
+	csumcache_t *csumc;
+	struct sock *sk;
+
+	if (req->no_output)
+		HTTP_BUG();
+	req->http_status = 200;
+	csumc = req->urlo->csumc;
+	if (!csumc)
+		HTTP_BUG();
+
+	/*
+	 * (Skip sending the cached header if the Trusted API has sent its
+	 * own header.)
+	 */
+	i = 0;
+	if (!include_header)
+		i = 1;
+
+	packets = 0;
+	sk = req->sock->sk;
+	if (current->state != TASK_RUNNING)
+		HTTP_BUG();
+
+	lock_sock(sk);
+	TCP_CHECK_TIMER(sk);
+	if (sk->err || (sk->state != TCP_ESTABLISHED)) {
+		ret = -1;
+		goto out_push;
+	}
+	for (; i < csumc->size; i++, packets++) {
+		frag = csumc->array + i;
+
+		/*
+		 * build the split-skb from the csum entry:
+		 */
+		skb = http_alloc_skb();
+		datasize = 0;
+		for (;;) {
+			add_frag_skb(req, skb, frag);
+			datasize += frag->size;
+			Dprintk("built skb %p (%d), offset %d, size %d out of frag %p, size %d.\n", skb, datasize, size, ret, frag, frag->size);
+			if (i == csumc->size-1)
+				break;
+			frag = csumc->array + i+1;
+			if (frag->size + datasize > csumc->packet_size)
+				break;
+			i++;
+		}
+
+		if (!datasize)
+			HTTP_BUG();
+		ret = __local_tcp_send_csumcache(sk, MSG_NOSIGNAL, skb, datasize, push && (i == csumc->size-1));
+		if (!ret)
+			HTTP_BUG();
+		if (ret < 0) {
+			HTTP_BUG();
+			goto err;
+		}
+		Dprintk("have sent skb %p (%d), offset %d, size %d.\n", skb, datasize, size, ret);
+		size += ret;
+	}
+	if (size > 0)
+		ret = req->urlo->body_len;
+	else
+		ret = size;
+out_push:
+	if (push)
+		push_sk(sk);
+	TCP_CHECK_TIMER(sk);
+	release_sock(sk);
+	return ret;
+
+err:
+	if (atomic_read(&skb->users) != 1)
+		HTTP_BUG();
+	kfree_skb(skb);
+	goto out_push;
+}
+
+/*
+ * HTTP header shortcuts.
+ */
+
+static const char success[] =
+	"HTTP/1.1 200 OK\r\n";
+//	"Server: TUX 1.0\r\n";
+
+static const char no_perm[] =
+	"HTTP/1.1 403 Forbidden\r\n"
+	"Server: TUX 1.0\r\n\r\n";
+
+static const char try_later[] =
+	"HTTP/1.1 503 Service Unavailable\r\n"
+	"Server: TUX 1.0\r\n"
+	"Content-Length: 15\r\n\r\n"
+	"Try again later";
+
+static const char not_modified[] =
+	"HTTP/1.1 304 Not Modified\r\n"
+	"Server: TUX 1.0\r\n\r\n";
+
+
+/*
+ * note, send_success() is for external CGIs, and doesnt
+ * close the header part with a double newline.
+ */
+void send_success (http_req_t *req, struct socket *sock)
+{
+	req->http_status = 200;
+	send_dynamic_reply(req, sock, success, sizeof(success)-1, 0);
+}
+
+void send_err_forbidden (http_req_t *req, struct socket *sock)
+{
+	printk("WARNING: sending 403 reply!\n");
+	req->http_status = 403;
+	send_dynamic_reply(req, sock, no_perm, sizeof(no_perm)-1, 1);
+}
+
+void send_ret_not_modified (http_req_t *req, struct socket *sock)
+{
+	req->http_status = 304;
+	send_dynamic_reply(req, sock, not_modified, sizeof(not_modified)-1, 1);
+}
+
+void send_err_try_later (struct socket *sock)
+{
+	send_dynamic_reply(NULL, sock, try_later, sizeof(try_later)-1, 1);
+}
+
+void send_generic_reply (http_req_t *req, threadinfo_t *ti)
+{
+	int retval = 0;
+
+	Dprintk("SEND req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	check_req_list(req, NULL);
+	if (req->no_output)
+		goto out;
+
+	if (req->sock->sk && (req->sock->sk->state == TCP_ESTABLISHED)) {
+		if (req->tcapi)
+			retval = req->tcapi->send_reply(req);
+		else
+			retval = http_send_object(req, 1, 1);
+
+		if (retval >= 0)
+			req->bytes_sent += retval;
+	}
+out:
+	if (retval != -3)
+		flush_request(req, ti);
+}
+
+int send_replies (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	struct sock *sk;
+	http_req_t *req;
+	int count = 0;
+
+	Dprintk("checking output queue ...\n");
+
+repeat_lock:
+	spin_lock_irq(&ti->output_lock);
+	head = &ti->output_pending;
+	next = head->next;
+	
+	while ((curr = next) != head) {
+		if (current->need_resched)
+			break;
+		if (!count++)
+			__set_task_state(current, TASK_RUNNING);
+
+		req = list_entry(curr, http_req_t, output);
+		if (req->ti != ti)
+			HTTP_BUG();
+		if (ti->thread != current)
+			HTTP_BUG();
+		if (req->userspace_module)
+			HTTP_BUG();
+		check_req_list(req, &req->output);
+		next = curr->next;
+
+		if (req->redirect_secondary)
+			HTTP_BUG();
+		sk = req->sock->sk;
+
+		Dprintk("pending output req %p, socket state %d.\n", req, sk->state);
+		check_req_list(req, &req->output);
+		list_del(curr);
+		DEC_STAT(nr_output_pending);
+		check_req_list(req, NULL);
+
+		spin_unlock_irq(&ti->output_lock);
+
+		send_generic_reply(req, ti);
+		goto repeat_lock;
+	}
+	spin_unlock_irq(&ti->output_lock);
+	Dprintk("finished checking output queue ...\n");
+	return count;
+}
+
+void flush_outputqueue (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	http_req_t *req;
+
+repeat:
+	spin_lock_irq(&ti->output_lock);
+	head = &ti->output_pending;
+	curr = head->next;
+
+	if (curr != head) {
+		req = list_entry(curr, http_req_t, output);
+		next = curr->next;
+		list_del(curr);
+		DEC_STAT(nr_output_pending);
+		spin_unlock_irq(&ti->output_lock);
+
+		req->keep_alive = 0;
+		req->http_status = -1;
+		flush_request(req, ti);
+		goto repeat;
+	} else
+		spin_unlock_irq(&ti->output_lock);
+
+	free_pages((unsigned long)ti->output_buffer, OUT_BUF_ORDER);
+	ti->output_buffer = NULL;
+}
+
--- linux/net/http/input.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/input.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,863 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * input.c: handle HTTP headers arriving on accepted connections
+ */
+
+#include <net/http.h>
+#include <linux/kmod.h>
+
+static int url_writepage (struct file *file, struct page *page)
+{
+	urlobj_t *urlo = dentry_to_urlo(file->f_dentry);
+
+	return urlo->real_aops->writepage(file, page);
+}
+
+static int url_readpage (struct file *file, struct page *page)
+{
+	unsigned long offset, next_offset, index;
+	char *orig_buf, *buf, *from;
+	int size, bytes, i;
+	csumcache_t *csumc;
+	skb_frag_t *frag;
+	urlobj_t *urlo;
+
+
+	urlo = dentry_to_urlo(file->f_dentry);
+	get_urlo(urlo);
+	csumc = urlo->csumc;
+	if (!csumc || !atomic_read(&urlo->csumcs_created)) {
+		put_urlo(urlo);
+		return urlo->real_aops->readpage(file, page);
+	}
+
+	index = page->index << PAGE_CACHE_SHIFT;
+	size = PAGE_CACHE_SIZE;
+	offset = 0;
+	orig_buf = buf = (char *) kmap(page);
+
+	for (i = 1; i < csumc->size; i++, offset = next_offset) {
+		frag = csumc->array + i;
+
+		next_offset = offset + frag->size;
+		if (index >= next_offset)
+			continue;
+		if (index < offset)
+			HTTP_BUG();
+		bytes = size;
+		if (bytes > next_offset-index)
+			bytes = next_offset-index;
+		if (bytes > PAGE_OFFSET)
+			HTTP_BUG();
+		if (frag->page_offset + index-offset > PAGE_CACHE_SIZE)
+			HTTP_BUG();
+
+		from = (char *)kmap(frag->page) + frag->page_offset;
+		memcpy(buf, from + index-offset, bytes);
+		kunmap(frag->page);
+
+		size -= bytes;
+		if (size < 0)
+			HTTP_BUG();
+		buf += bytes;
+		index += bytes;
+		if (!size)
+			break;
+	}
+	if (buf > orig_buf + PAGE_CACHE_SIZE)
+		HTTP_BUG();
+	kunmap(page);
+	put_urlo(urlo);
+	SetPageUptodate(page);
+	UnlockPage(page);
+	return 0;
+}
+
+static int url_prepare_write (struct file *filp, struct page *page, unsigned from, unsigned to)
+{
+	urlobj_t *urlo = mapping_to_urlo(page->mapping);
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct address_space_operations *aops;
+
+	aops = urlo->real_aops;
+	free_urlo(inode);
+	return aops->prepare_write(filp, page, from, to);
+}
+
+static int url_commit_write (struct file *filp, struct page *page,
+						unsigned from, unsigned to)
+{
+	int ret;
+	urlobj_t *urlo = filp_to_urlo(filp);
+	struct address_space_operations *aops;
+
+
+	aops = urlo->real_aops;
+	ret = aops->commit_write(filp, page, from, to);
+	return ret;
+}
+
+static int url_bmap(struct address_space *mapping, long block)
+{
+	urlobj_t *urlo = mapping_to_urlo(mapping);
+
+	return urlo->real_aops->bmap(mapping, block);
+}
+
+static unsigned long url_destroy(struct inode *inode)
+{
+	urlobj_t *urlo;
+
+	if (inode->i_mapping->a_ops != &url_aops)
+		HTTP_BUG();
+
+	urlo = inode_to_urlo(inode);
+	if (urlo->real_aops->destroy)
+		HTTP_BUG();
+	return free_urlo(inode);
+}
+
+struct address_space_operations url_aops = {
+	writepage: url_writepage,
+	readpage: url_readpage,
+	prepare_write: url_prepare_write,
+	commit_write: url_commit_write,
+	destroy: url_destroy,
+	bmap: url_bmap
+};
+
+extern spinlock_t pagecache_lock;
+static spinlock_t add_urlo_lock = SPIN_LOCK_UNLOCKED;
+
+int add_inode_urlo_atomic (struct inode *inode, urlobj_t *urlo)
+{
+	struct address_space *mapping;
+	int ret = 1;
+
+	mapping = inode->i_mapping;
+	/*
+	 * Only regular inodes can be in the checksumcache:
+	 */
+	if (&inode->i_data != mapping)
+		HTTP_BUG();
+
+	spin_lock(&add_urlo_lock);
+	spin_lock(&mapping->i_shared_lock);
+	spin_lock(&pagecache_lock);
+
+	/*
+	 * Are we really the one installing the new mapping?
+	 */
+	if (inode->i_mapping->http_data)
+		goto out;
+
+	urlo->inode = inode;
+	urlo->real_aops = mapping->a_ops;
+	mapping->a_ops = &url_aops;
+	mapping->http_data = urlo;
+
+	ret = 0;
+out:
+	spin_unlock(&pagecache_lock);
+	spin_unlock(&inode->i_mapping->i_shared_lock);
+	spin_unlock(&add_urlo_lock);
+
+	return ret;
+}
+
+static void remove_inode_urlo (struct inode *inode, urlobj_t *urlo)
+{
+	struct address_space *mapping = inode->i_mapping;
+
+	if (&inode->i_data != mapping)
+		HTTP_BUG();
+
+	spin_lock(&pagecache_lock);
+	spin_lock(&mapping->i_shared_lock);
+
+	mapping->a_ops = urlo->real_aops;
+	urlo->real_aops = NULL;
+	mapping->http_data = NULL;
+
+	spin_unlock(&mapping->i_shared_lock);
+	spin_unlock(&pagecache_lock);
+}
+
+unsigned long __put_urlo (urlobj_t *urlo)
+{
+	int i;
+	csumcache_t *csumc = urlo->csumc;
+	unsigned long freed_bytes = 0;
+
+
+	if (csumc) {
+		struct page *page = NULL;
+
+		atomic_sub(urlo->filelen, (atomic_t *)&kstat.csumcache_total);
+
+		for (i = 0; i < csumc->size; i++) {
+			skb_frag_t *frag = csumc->array + i;
+
+			if (frag->page != page) {
+				page = frag->page;
+				if (page_count(page) == 1)
+					freed_bytes += PAGE_SIZE;
+				__free_page(page);
+			}
+		}
+		http_kfree(csumc->array, ALLOC_CSUMCARRAY);
+		freed_bytes += csumc->size * sizeof(skb_frag_t);
+		http_kfree(csumc, ALLOC_CSUMCSTRUCT);
+		freed_bytes += sizeof(*csumc);
+	}
+	if (atomic_read(&urlo->users))
+		HTTP_BUG();
+// FIXME: do a usage count rather, or something
+//	if (urlo->tcapi)
+//		unregister_httpmodule(urlo->tcapi);
+	memset(urlo, 0, sizeof(*urlo));
+	http_kfree(urlo, ALLOC_URLO_STRUCT);
+	DEC_STAT(nr_urlo);
+	urlo = NULL;
+	freed_bytes += sizeof(*urlo);
+
+	return freed_bytes;
+}
+
+unsigned long free_urlo (struct inode *inode)
+{
+	unsigned long freed_bytes = 0;
+	urlobj_t *urlo;
+
+	lock_kernel();
+	if (inode->i_mapping->a_ops != &url_aops) {
+		printk("race #1.\n");
+		unlock_kernel();
+		return freed_bytes;
+	}
+	urlo = inode_to_urlo(inode);
+
+	if (inode->i_mapping->a_ops != &url_aops)
+		HTTP_BUG();
+	if (!atomic_read(&urlo->users))
+		HTTP_BUG();
+	if (urlo->inode != inode)
+		HTTP_BUG();
+
+	remove_inode_urlo(inode, urlo);
+	unlock_kernel();
+
+	return put_urlo(urlo);
+}
+
+
+struct dentry * http_lookup (char *filename, struct nameidata *base,
+					 unsigned int flags)
+{
+	struct nameidata nd;
+	int err;
+
+	if (!base) {
+		nd.mnt = current->fs->rootmnt;
+		nd.dentry = current->fs->root;
+		nd.last.len = 0;
+		nd.flags = LOOKUP_FOLLOW|LOOKUP_POSITIVE;
+	} else
+		nd = *base;
+	nd.flags |= flags;
+	mntget(nd.mnt);
+	dget(nd.dentry);
+
+	if ((err = path_walk(filename, &nd))) {
+		Dprintk("path_walk() returned with %d!\n", err);
+		return ERR_PTR(err);
+	}
+	mntput(nd.mnt);
+	return nd.dentry;
+}
+
+#define ERR() do { Dprintk("error at %s:%d.\n", __FILE__, __LINE__); } while (0)
+
+int url_permission (struct inode *inode)
+{
+	umode_t mode;
+
+	/*
+	 * Maybe allow a dynamic HTTP module. We are really paranoid,
+	 * only root:root setuid root, setguid root and others+group:none
+	 * permissions are allowed. This should at least show that
+	 * these dynamic applications are truly _TRUSTED_.
+	 */
+	#define TRUSTED_MODULE_REQUIRED (S_ISUID|S_ISGID)
+
+	mode = inode->i_mode;
+	Dprintk("URL inode mode: %08x.\n", mode);
+
+	if (!S_ISREG(mode))
+		return -1;
+
+	if (((mode & TRUSTED_MODULE_REQUIRED) == TRUSTED_MODULE_REQUIRED) &&
+			!inode->i_uid && !inode->i_gid) {
+		Dprintk("http_mode_userspace: %08x\n", http_mode_userspace);
+		if (mode & http_mode_userspace) {
+			Dprintk("userspace module!\n");
+			return 2;
+		}
+		Dprintk("kernelspace module.\n");
+		return 1;
+	}
+#if CONFIG_HTTP_EXTCGI
+	if (((mode & 0xfff) == http_mode_cgi) && !inode->i_uid && !inode->i_gid)
+		return 1;
+#endif
+	/*
+	 * Paranoia: first forbid things, then maybe allow.
+	 * Only regular files allowed.
+	 */
+	if (mode & http_mode_forbidden)
+		return -2;
+	/*
+	 * at least one bit in the 'allowed' set has to
+	 * be present to allow access.
+	 */
+	if (!(mode & http_mode_allowed))
+		return -3;
+	return 0;
+}
+
+int lookup_urlo (http_req_t *req, unsigned int flag)
+{
+	int trusted_url = 0, miss = 0, userspace;
+	urlobj_t *urlo = NULL;
+	struct dentry *dentry = NULL;
+	struct inode *inode;
+	char *filename;
+
+	if (req->dentry) HTTP_BUG();
+	if (req->urlo) HTTP_BUG();
+	/*
+	 * Eat all leading slashes.
+	 */
+	filename = req->objectname;
+	while (*filename == '/') filename++;
+
+	dentry = http_lookup (filename, &docroot, flag);
+	Dprintk("looked up {%s} == dentry %p.\n", filename, dentry);
+	if (IS_ERR(dentry) || !dentry) {
+		if (PTR_ERR(dentry) == -EWOULDBLOCK) {
+			if (!flag)
+				HTTP_BUG();
+			goto cachemiss_atomic;
+		}
+		goto abort_locked;
+	}
+	Dprintk("SUCCESS, looked up {%s} == dentry %p (inode %p, count %d.)\n", filename, dentry, dentry->d_inode, atomic_read(&dentry->d_count));
+	if (!dentry->d_inode)
+		goto abort_locked_dput;
+	inode = dentry->d_inode;
+
+	/*
+	 * At this point we have a real, non-negative dentry.
+	 * Check for an urlo potentially attached to the inode:
+	 */
+	if (urlo)
+		HTTP_BUG();
+	trusted_url = url_permission(inode);
+
+	if (trusted_url < 0) {
+		Dprintk("FAILED trusted dentry %p (urlo %p) permission %d.\n", dentry, urlo, trusted_url);
+		goto abort_no_permission_unlock;
+	}
+	userspace = 0;
+	if (trusted_url == 2)
+		userspace = 1;
+
+repeat_urlo:
+	if (inode->i_mapping->a_ops != &url_aops) {
+		if (flag == LOOKUP_ATOMIC)
+			goto cachemiss_atomic_dput;
+		else
+			goto cachemiss;
+	}
+
+	urlo = dentry_to_urlo(dentry);
+	if (urlo->inode != inode)
+		HTTP_BUG();
+	get_urlo(urlo); // usage count
+
+	Dprintk("looked up cached dentry %p, (urlo %p, API %p, count %d.)\n", dentry, urlo, urlo->tcapi, dentry ? atomic_read(&dentry->d_count) : -1 );
+
+	if (urlo->tcapi && !trusted_url)
+		BUG(); // should not happen for the time being
+	if (!urlo->tcapi && trusted_url)
+		BUG(); // should not happen either
+
+	if (urlo->tcapi) {
+		if (!req->query && (req->method == METHOD_GET))
+			goto abort_no_permission;
+		req->tcapi = urlo->tcapi;
+		if (urlo->tcapi->userspace_id) {
+			if (!userspace)
+				goto abort_no_permission;
+			if (req->userspace_module)
+				HTTP_BUG();
+			req->userspace_module = 1;
+		}
+	} else
+		if (trusted_url)
+			HTTP_BUG(); // should not happen
+	if (!urlo->tcapi)
+		urlo_hist_hit(urlo->filelen);
+	if (!urlo->tcapi && !urlo->csumc)
+		miss = 1;
+
+out:
+	if (!miss && !req->tcapi && (!urlo || !urlo->csumc)) {
+		if (dentry) printk("BUG ... looked up {%s} == dentry %p (inode %p, count %d.)\n", filename, dentry, dentry->d_inode, atomic_read(&dentry->d_count));
+		if (dentry) printk("BUG ... cached dentry %p, (urlo %p, API %p, count %d.)\n", dentry, urlo, urlo->tcapi, dentry ? atomic_read(&dentry->d_count) : -1 );
+		HTTP_BUG();
+	}
+
+abort:
+	if (req->urlo) HTTP_BUG();
+	if (req->dentry) HTTP_BUG();
+	if (urlo && urlo->tcapi) {
+		if (!req->tcapi)
+			HTTP_BUG();
+		dput(dentry);
+		dentry = NULL;
+		put_urlo(urlo);
+		urlo = NULL;
+	}
+	req->dentry = dentry;
+	req->urlo = urlo;
+	return miss;
+
+cachemiss_atomic_dput:
+	dput(dentry);
+
+cachemiss_atomic:
+	dentry = NULL;
+	urlo = NULL;
+	miss = 1;
+	goto out;
+
+cachemiss:
+	if (urlo)
+		BUG();
+	urlo = http_kmalloc(sizeof(*urlo), ALLOC_URLO_STRUCT);
+	INC_STAT(nr_urlo);
+	memset(urlo, 0, sizeof(*urlo));
+	INIT_LIST_HEAD(&urlo->secondary_pending);
+	get_urlo(urlo); // inode reference
+	get_urlo(urlo); // usage count
+
+	urlo->filelen = inode->i_size;
+
+	if (trusted_url) {
+#if CONFIG_HTTP_EXTCGI
+		extern tcapi_template_t extcgi_tcapi;
+
+		if ((inode->i_mode & 0xfff) == http_mode_cgi)
+			urlo->tcapi = &extcgi_tcapi;
+		else
+#endif
+			if (load_httpmodule(urlo, filename))
+				goto abort_no_permission;
+		if (userspace && !urlo->tcapi->userspace_id)
+			goto abort_no_permission;
+		if (!userspace && urlo->tcapi->userspace_id)
+			goto abort_no_permission;
+		req->tcapi = urlo->tcapi;
+		if (req->tcapi->userspace_id) {
+			if (!userspace)
+				goto abort_no_permission;
+			if (req->userspace_module)
+				HTTP_BUG();
+			req->userspace_module = 1;
+		}
+	}
+	if (add_inode_urlo_atomic(inode, urlo)) {
+		http_kfree(urlo, ALLOC_URLO_STRUCT);
+		DEC_STAT(nr_urlo);
+		urlo = NULL;
+		DEC_STAT(nr_urlo_references);
+		DEC_STAT(nr_urlo_references);
+		goto repeat_urlo;
+	}
+	/*
+	 * Do not cache the file above the threshold. This still
+	 * keeps the urlo for the duration of this request, but
+	 * it's going to be freed when the request finishes.
+	 */
+	if (urlo->filelen > http_max_cached_filesize)
+		free_urlo(urlo->inode);
+	miss = 1;
+	if (urlo && urlo->tcapi)
+		miss = 0;
+	goto out;
+
+abort_locked_dput:
+	dput(dentry);
+abort_locked:
+	dentry = NULL;
+	goto abort;
+
+abort_no_permission:
+abort_no_permission_unlock:
+	if (dentry) {
+		dput(dentry);
+		dentry = NULL;
+	}
+	put_urlo(urlo);
+	urlo = NULL; req->redirect_secondary = 1;
+	goto abort;
+}
+
+int http_miss_req (http_req_t *req)
+{
+	/*
+	 * this is the 'slow path', look up, read the file, construct
+	 * the csumcache. We pass this work off to one of the 'async
+	 * IO threads', so that the fast TUX threads do not get held
+	 * up unnecesserily.
+	 */
+	Dprintk("queueing primary cachemiss.\n");
+	queue_cachemiss(req);
+
+	return -1;
+}
+
+static void unidle_req (http_req_t *req)
+{
+	threadinfo_t *ti = req->ti;
+
+	Dprintk("UNIDLE req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	if (!test_and_clear_bit(0, &req->idle_input)) {
+		Dprintk("unidling %p, wasnt idle!\n", req);
+		spin_lock_irq(&ti->input_lock);
+		check_req_list(req, &req->input);
+		list_del(&req->input);
+		DEC_STAT(nr_input_pending);
+		check_req_list(req, NULL);
+		spin_unlock_irq(&ti->input_lock);
+	} else {
+		if (timer_pending(&req->keepalive_timer))
+			del_timer(&req->keepalive_timer);
+		DEC_STAT(nr_idle_input_pending);
+		Dprintk("unidled %p.\n", req);
+	}
+	if (req->idle_input)
+		HTTP_BUG();
+}
+
+#define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete; } while (0)
+#define GOTO_REDIRECT do { printk("redirect at %s:%d.\n", __FILE__, __LINE__); goto redirect; } while (0)
+#define GOTO_REDIRECT_NONIDLE do { printk("redirect at %s:%d.\n", __FILE__, __LINE__); goto redirect_nonidle; } while (0)
+
+static int read_request (struct socket *sock, char *buf, int size)
+{
+	mm_segment_t oldmm;
+	struct msghdr msg;
+	struct iovec iov;
+	int len;
+
+	msg.msg_name     = 0;
+	msg.msg_namelen  = 0;
+	msg.msg_iov	 = &iov;
+	msg.msg_iovlen   = 1;
+	msg.msg_control  = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags    = 0;
+	
+	msg.msg_iov->iov_base = buf;
+	msg.msg_iov->iov_len  = size;
+	
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+
+read_again:
+	len = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT|MSG_PEEK);
+	set_fs(oldmm);
+
+	/*
+	 * We must not get a signal inbetween
+	 */
+	if ((len == -EAGAIN) || (len == -512)) {
+		if (!signal_pending(current)) {
+			len = 0;
+			goto out;
+		}
+		reap_kids();
+		goto read_again;
+	}
+out:
+	return len;
+}
+
+void trunc_headers (http_req_t *req)
+{
+	int len, addr_len = 0;
+
+	len = req->sock->sk->prot->recvmsg(req->sock->sk, NULL, req->parsed_len, 1, MSG_TRUNC, &addr_len);
+	Dprintk("truncated %d bytes at %p. (wanted: %d.)\n", len, __builtin_return_address(0), req->parsed_len);
+}
+
+void print_req (http_req_t *req)
+{
+	int i;
+	char *tmp;
+	struct sock *sk = req->sock->sk;
+
+	printk("PRINT req %p <%p>\n", req, __builtin_return_address(0));
+	printk("... sock %p, sk %p, sk->state: %d, sk->err: %d\n", req->sock, req->sock->sk, sk->state, sk->err);
+	printk("... receive_queue: %d, error_queue: %d, keepalive: %d, status: %d\n", !skb_queue_empty(&sk->receive_queue), !skb_queue_empty(&sk->error_queue), req->keep_alive, req->http_status);
+	printk("... meth:{%s}, uri:{%s}, query:{%s}, ver:{%s}\n", req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+	printk("... post_data:{%s}(%d).\n", req->post_data, req->post_data_len);
+	tmp = req->headers;
+	for (i = 0; i < 10; i++) {
+		printk("... header%d: {%s}\n", i, tmp);
+		tmp = tmp + strlen(tmp) + 1;
+	}
+}
+/* 
+ * parse_request() reads all available TCP/IP data and prepares
+ * the request if the HTTP request is complete. (we can get HTTP
+ * requests in several packets.) Invalid requests are redirected
+ * to the secondary server.
+ */
+
+int parse_request (http_req_t *req, threadinfo_t *ti)
+{
+	int len, reqlen, ret = 0;
+	urlobj_t *urlo;
+	int missed;
+
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+
+	/* First, read the data */
+	len = read_request(req->sock, req->headers, MAX_HEADER_LEN-1);
+	if (len < 0) {
+		printk("got %d from read_request().\n", len);
+//		print_req(req);
+		GOTO_REDIRECT;
+	}
+	if (!len) {
+		GOTO_INCOMPLETE;
+	}
+
+	/*
+	 * Make it a zero-delimited string to automatically get
+	 * protection against various buffer overflow situations.
+	 * Then pass it to the HTTP protocol stack.
+	 */
+	req->headers[len] = 0;
+	req->headers_len = len;
+
+	reqlen = parse_http_message(req, len);
+
+	/*
+	 * Is the request fully read? (or is there any error)
+	 */
+	if (reqlen < 0)
+		GOTO_REDIRECT;
+	if (!reqlen) {
+		if (len >= MAX_HEADER_LEN-1)
+			GOTO_REDIRECT;
+		GOTO_INCOMPLETE;
+	}
+	unidle_req(req);
+
+	missed = lookup_urlo(req, LOOKUP_ATOMIC);
+	urlo = req->urlo;
+	if (req->userspace_module)
+		goto userspace_module_nonidle;
+//	if (!missed && !urlo)
+//		GOTO_REDIRECT_NONIDLE;
+	if (missed || (urlo && !urlo->tcapi && !urlo->csumc)) {
+		Dprintk("uncached request.\n");
+		if (req->parsed_len)
+			trunc_headers(req);
+		return http_miss_req(req);
+	}
+#if 0
+	if (req->tcapi)
+		HTTP_BUG();
+#endif
+	if ((req->method != METHOD_GET) || req->query) {
+		Dprintk("TCAPI %p request.\n", req->tcapi);
+		if (!req->tcapi)
+			GOTO_REDIRECT_NONIDLE;
+		if (req->dentry)
+			HTTP_BUG();
+		ret = req->tcapi->query(req);
+	}
+	if (!req->redirect_secondary && req->parsed_len)
+		trunc_headers(req);
+
+	return ret;
+redirect:
+	unidle_req(req);
+redirect_nonidle:
+	req->redirect_secondary = 1;
+	INC_STAT(parse_static_redirect);
+	return 0;
+incomplete:
+	INC_STAT(parse_static_incomplete);
+	return -1;
+userspace_module_nonidle:
+	if (req->redirect_secondary)
+		HTTP_BUG();
+	trunc_headers(req);
+	queue_userspace_req(req, ti);
+	return -1;
+}
+
+int read_headers (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	int count = 0, ret;
+	struct sock *sk;
+	http_req_t *req;
+
+restart_loop:
+	spin_lock_irq(&ti->input_lock);
+	head = &ti->input_pending;
+	next = head->next;
+	
+	while ((curr = next) != head) {
+		if (current->need_resched)
+			break;
+
+		req = list_entry(curr, http_req_t, input);
+		Dprintk("READ req %p HEADERS <%p> (sock %p, sk %p) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+
+		if (req->ti != ti)
+			HTTP_BUG();
+		if (req->magic != HTTP_MAGIC)
+			HTTP_BUG();
+		if (req->userspace_module)
+			HTTP_BUG();
+
+		check_req_list(req, &req->input);
+		next = curr->next;
+		list_del(curr);
+		DEC_STAT(nr_input_pending);
+		check_req_list(req, NULL);
+
+		if (test_bit(0, &req->idle_input))
+			HTTP_BUG();
+		/*
+		 * If the connection is lost, remove from queue
+		 */
+		sk = req->sock->sk;
+		if (!sk || (sk->state != TCP_ESTABLISHED) || sk->err ||
+				!skb_queue_empty(&sk->error_queue)) {
+
+			Dprintk("LOST req %p <%p> (sock %p, sk %p, sk->state: %d, sk->err: %d, skb_queue_empty(error_queue): %d) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), req->sock, req->sock->sk, sk->state, sk->err, skb_queue_empty(&sk->error_queue), req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+			spin_unlock_irq(&ti->input_lock);
+
+			if (!count++)
+				__set_task_state(current, TASK_RUNNING);
+			req->keep_alive = 0;
+			req->http_status = -1;
+			flush_request(req, ti);
+			goto restart_loop;
+		}
+		add_keepalive_timer(req);
+		if (test_and_set_bit(0, &req->idle_input))
+			HTTP_BUG();
+		INC_STAT(nr_idle_input_pending);
+		Dprintk("marked %p idle!\n", req);
+
+		/*
+		 * If no data pending then do not parse request
+		 */
+		if (skb_queue_empty(&sk->receive_queue)) {
+			INC_STAT(inputqueue_no_packet);
+			Dprintk("request %p had no input packets!\n", req);
+			continue;
+		}
+		spin_unlock_irq(&ti->input_lock);
+		INC_STAT(inputqueue_got_packet);
+
+		if (!count++)
+			__set_task_state(current, TASK_RUNNING);
+
+		ret = parse_request(req, ti);
+
+		/*
+		 * Is input data incomplete (or is cachemiss/userspace pending):
+		 */
+		if (ret == -1)
+			goto restart_loop;
+
+		/*
+		 * Is it finished:
+		 */
+		if (ret == -2) {
+			flush_request(req, ti);
+			goto restart_loop;
+		}
+
+		check_req_list(req, NULL);
+		/*
+		 * Add to either the redirect_pending or
+		 * the output_pending queue
+		 */
+		if (req->redirect_secondary) {
+			list_add_tail(&req->redirect, &ti->redirect_pending);
+			INC_STAT(nr_redirect_pending);
+			check_req_list(req, &req->redirect);
+			goto restart_loop;
+		}
+
+		/*
+		 * Is it a request for userspace:
+		 */
+		if (req->userspace_module)
+			HTTP_BUG();
+#if 1
+		send_generic_reply(req, ti);
+#else
+		spin_lock_irq(&ti->output_lock);
+		list_add_tail(&req->output, &ti->output_pending);
+		INC_STAT(nr_output_pending);
+		check_req_list(req, &req->output);
+		spin_unlock_irq(&ti->output_lock);
+#endif
+		goto restart_loop;
+	}
+	spin_unlock_irq(&ti->input_lock);
+	return count;
+}
+
+void flush_inputqueue (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	http_req_t *req;
+
+restart:
+	spin_lock_irq(&ti->input_lock);
+	head = &ti->input_pending;
+	curr = head->next;
+
+	if (curr != head) {
+		req = list_entry(curr, http_req_t, input);
+		next = curr->next;
+		list_del(curr);
+		DEC_STAT(nr_input_pending);
+		req->keep_alive = 0;
+		req->http_status = -1;
+		spin_unlock_irq(&ti->input_lock);
+		flush_request(req, ti);
+		goto restart;
+	}
+	spin_unlock_irq(&ti->input_lock);
+}
+
--- linux/net/http/redirect.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/redirect.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,161 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * redirect.c: redirect requests to other server sockets (such as Apache).
+ */
+
+#include <net/http.h>
+
+static void dummy_destructor(struct open_request *req)
+{
+}
+
+static struct or_calltable dummy = 
+{
+	0,
+ 	NULL,
+ 	NULL,
+ 	&dummy_destructor,
+ 	NULL
+};
+
+static int redirect_sock (http_req_t *req, const int port)
+{
+	struct socket *sock = req->sock;
+	struct open_request *tcpreq;
+	struct sock *sk, *oldsk;
+
+	/*
+	 * Look up (optional) listening user-space socket.
+	 */
+	local_bh_disable();
+	sk = tcp_v4_lookup_listener(INADDR_ANY, port, 0);
+	local_bh_enable();
+
+	/* No secondary server found */
+	if (!sk)
+		return -1;
+
+	/*
+	 * Requeue the 'old' socket as an accept-socket of
+	 * the listening socket. This way we can shuffle
+	 * a socket around. Since we've read the input data
+	 * via the non-destructive MSG_PEEK, the secondary
+	 * server can be used transparently.
+	 */
+	oldsk = sock->sk;
+	unlink_http_socket(req);
+	lock_sock(sk);
+
+	if (sk->state != TCP_LISTEN || tcp_acceptq_is_full(sk)) {
+		release_sock(sk);
+		sock_put(sk);
+		return -1;
+	}
+
+	tcpreq = tcp_openreq_alloc();
+
+	if (!tcpreq) {
+		release_sock(sk);
+		sock_put(sk);
+		return -1;
+	}
+
+	sock->sk = NULL;
+	sock->state = SS_UNCONNECTED;
+
+	tcpreq->class = &dummy;
+	write_lock_irq(&oldsk->callback_lock);
+	oldsk->socket = NULL;
+        oldsk->sleep = NULL;
+	write_unlock_irq(&oldsk->callback_lock);
+
+	tcp_acceptq_queue(sk, tcpreq, oldsk);
+
+	sk->data_ready(sk, 0);
+
+	release_sock(sk);
+	sock_put(sk);
+
+	return 0;
+}
+
+int redirect_requests (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	struct sock *sk;
+	http_req_t *req;
+	int count = 0;
+
+	head = &ti->redirect_pending;
+	next = head->next;
+
+	while ((curr = next) != head) {
+		if (current->need_resched)
+			break;
+		if (!count++)
+			__set_task_state(current, TASK_RUNNING);
+		req = list_entry(curr, http_req_t, redirect);
+		if (req->magic != HTTP_MAGIC)
+			HTTP_BUG();
+		check_req_list(req, &req->redirect);
+		if (req->ti != ti)
+			HTTP_BUG();
+		if (ti->thread != current)
+			HTTP_BUG();
+		next = curr->next;
+		list_del(curr);
+		check_req_list(req, NULL);
+
+		sk = req->sock->sk;
+		if (req->sock && req->sock->sk)
+			remove_wait_queue(req->sock->sk->sleep,&(req->sleep));
+
+		Dprintk("redirecting request (headers: {%s})\n", req->headers);
+		check_req_list(req, NULL);
+		if (redirect_sock(req, http_clientport)) {
+			check_req_list(req, NULL);
+			send_err_forbidden(req, req->sock);
+			check_req_list(req, NULL);
+		} else {
+			/*
+			 * It's now completely up to the secondary
+			 * server to handle this request.
+			 */
+			check_req_list(req, NULL);
+			sock_release(req->sock);
+			check_req_list(req, NULL);
+			req->sock = NULL;
+		}
+		check_req_list(req, NULL);
+		DEC_STAT(nr_redirect_pending);
+		req->keep_alive = 0;
+		req->http_status = -1;
+		req->redirect_secondary = 0;
+		flush_request(req, ti);
+	}
+	return count;
+}
+
+void flush_redirectqueue (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	http_req_t *req;
+
+	head = &ti->redirect_pending;
+	curr = head->next;
+
+	while (curr != head) {
+		req = list_entry(curr, http_req_t, redirect);
+		next = curr->next;
+		list_del(curr);
+		DEC_STAT(nr_redirect_pending);
+		req->keep_alive = 0;
+		req->http_status = -1;
+		req->redirect_secondary = 0;
+		flush_request(req, ti);
+	}
+}
+
--- linux/net/http/cachemiss.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/cachemiss.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,762 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * cachemiss.c: handle the 'slow IO path' by queueing not-yet-cached
+ * requests to the IO-thread pool. Dynamic load balancing is done
+ * between IO threads, based on the number of requests they have pending.
+ */
+
+#include <net/http.h>
+
+#define NR_IO_THREADS 10
+
+static spinlock_t async_lock = SPIN_LOCK_UNLOCKED;
+static struct list_head async_queue;
+static int nr_async_pending = 0;
+static wait_queue_head_t async_sleep;
+
+static spinlock_t add_csumc = SPIN_LOCK_UNLOCKED;
+
+int nr_async_io_pending (void)
+{
+	return nr_async_pending;
+}
+
+char * print_async_io_threads (char *buf)
+{
+	buf += sprintf(buf, "async IO threads: ");
+	return buf;
+}
+
+void queue_userspace_req (http_req_t *req, threadinfo_t *ti)
+{
+	if (!req->userspace_module)
+		HTTP_BUG();
+	if (!req->tcapi)
+		HTTP_BUG();
+	if (ti != req->ti)
+		HTTP_BUG();
+	if (!ti->started)
+		HTTP_BUG();
+
+	spin_lock_irq(&ti->userspace_lock);
+
+	Dprintk("userspace-queueing request %p.\n", req);
+	check_req_list(req, NULL);
+	list_add_tail(&req->userspace, &ti->userspace_pending);
+	INC_STAT(nr_userspace_pending);
+	check_req_list(req, &req->userspace);
+
+	spin_unlock_irq(&ti->userspace_lock);
+
+	if (ti->thread)
+		wake_up_process(ti->thread);
+	return;
+}
+
+void queue_output_req (http_req_t *req, threadinfo_t *ti)
+{
+	if (!req->tcapi && (!req->urlo || !req->urlo->csumc))
+		HTTP_BUG();
+	if (req->userspace_module)
+		HTTP_BUG();
+	if (ti != req->ti)
+		HTTP_BUG();
+	if (!ti->started)
+		HTTP_BUG();
+
+	spin_lock_irq(&ti->output_lock);
+	Dprintk("output-queueing request %p.\n", req);
+	check_req_list(req, NULL);
+	list_add_tail(&req->output, &ti->output_pending);
+	INC_STAT(nr_output_pending);
+	check_req_list(req, &req->output);
+
+	spin_unlock_irq(&ti->output_lock);
+
+	if (ti->thread)
+		wake_up_process(ti->thread);
+	return;
+}
+
+static void queue_userspace_cachemiss_req (http_req_t *req, threadinfo_t *ti)
+{
+	if (!req->userspace_module)
+		HTTP_BUG();
+	if (!req->tcapi)
+		HTTP_BUG();
+	if (ti != req->ti)
+		HTTP_BUG();
+	if (!ti->started)
+		HTTP_BUG();
+
+	spin_lock_irq(&ti->userspace_lock);
+
+	check_req_list(req, NULL);
+	Dprintk("userspace-cachemiss-queueing request %p.\n", req);
+	list_add_tail(&req->userspace, &ti->userspace_pending);
+	INC_STAT(nr_userspace_pending);
+	check_req_list(req, &req->userspace);
+
+	wake_up_process(ti->thread);
+
+	spin_unlock_irq(&ti->userspace_lock);
+
+	return;
+}
+
+static void queue_output_cachemiss_req (http_req_t *req, threadinfo_t *ti)
+{
+	if (!req->tcapi && (!req->urlo || !req->urlo->csumc))
+		HTTP_BUG();
+	if (req->userspace_module)
+		HTTP_BUG();
+	if (ti != req->ti)
+		HTTP_BUG();
+	if (!ti->started)
+		HTTP_BUG();
+
+	spin_lock_irq(&ti->output_lock);
+
+	check_req_list(req, NULL);
+	Dprintk("output-cachemiss-queueing request %p.\n", req);
+	list_add_tail(&req->output, &ti->output_pending);
+	INC_STAT(nr_output_pending);
+	check_req_list(req, &req->output);
+
+	wake_up_process(ti->thread);
+
+	spin_unlock_irq(&ti->output_lock);
+
+	return;
+}
+
+static void cachemiss_unqueue (http_req_t *req)
+{
+	spin_lock(&async_lock);
+	nr_async_pending--;
+	DEC_STAT(nr_cachemiss_pending);
+	spin_unlock(&async_lock);
+}
+
+static void __queue_req (http_req_t *req, threadinfo_t *ti)
+{
+	if (req->userspace_module)
+		queue_userspace_cachemiss_req(req, ti);
+	else
+		queue_output_cachemiss_req(req, ti);
+}
+
+static void queue_req (http_req_t *req)
+{
+	spin_lock(&async_lock);
+	__queue_req(req, req->ti);
+	nr_async_pending--;
+	DEC_STAT(nr_cachemiss_pending);
+	spin_unlock(&async_lock);
+}
+
+void queue_cachemiss (http_req_t *req)
+{
+	check_req_list(req, NULL);
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+
+	spin_lock(&async_lock);
+	list_add_tail(&req->cachemiss, &async_queue);
+	nr_async_pending++;
+	INC_STAT(nr_cachemiss_pending);
+	spin_unlock(&async_lock);
+
+	wake_up(&async_sleep);
+}
+
+static int __queue_secondary_cachemiss (http_req_t *req, urlobj_t *urlo)
+{
+	threadinfo_t *ti = req->ti;
+	int ret = -1;
+
+	check_req_list(req, NULL);
+
+	spin_lock(&add_csumc);
+	if (!urlo->csumc) {
+		spin_lock_irq(&ti->output_lock);
+		list_add_tail(&req->cachemiss, &urlo->secondary_pending);
+		INC_STAT(nr_secondary_pending);
+		spin_unlock_irq(&ti->output_lock);
+		if (urlo->csumc)
+			HTTP_BUG(); // race
+	} else {
+		if (!req->urlo)
+			HTTP_BUG();
+		if (urlo != req->urlo)
+			HTTP_BUG();
+		if (req->userspace_module) {
+			if (!req->tcapi)
+				HTTP_BUG();
+			queue_userspace_req(req, ti);
+		} else
+			queue_output_req(req, ti);
+		ret = 0;
+	}
+	spin_unlock(&add_csumc);
+
+	return ret;
+}
+
+static http_req_t * get_cachemiss (void)
+{
+	struct list_head *tmp;
+	http_req_t *req = NULL;
+
+	spin_lock(&async_lock);
+	if (!list_empty(&async_queue)) {
+
+		tmp = async_queue.next;
+		req = list_entry(tmp, http_req_t, cachemiss);
+
+		check_req_list(req, &req->cachemiss);
+
+		list_del(tmp);
+
+		if (req->magic != HTTP_MAGIC)
+			HTTP_BUG();
+	}
+	spin_unlock(&async_lock);
+	return req;
+}
+
+struct file * http_open_file (char *filename, int mode)
+{
+	struct file *filp;
+
+	if (!filename)
+		HTTP_BUG();
+
+	/* Rule no. 3 -- Does the file exist ? */
+
+	filp = filp_open(filename, mode, 0600);
+
+	if (IS_ERR(filp) || !filp || !filp->f_dentry)
+		goto err;
+
+out:
+	return filp;
+err:
+	printk("filp_open() error: %d.\n", (int)filp);
+	filp = NULL;
+	goto out;
+}
+
+static void queue_pending (http_req_t *req, urlobj_t *urlo)
+{
+	struct list_head *head, *curr, *next;
+	threadinfo_t *ti;
+
+	ti = req->ti;
+	spin_lock(&async_lock);
+	Dprintk("output-queueing primary request %p.\n", req);
+	__queue_req(req, ti);
+	nr_async_pending--;
+	DEC_STAT(nr_cachemiss_pending);
+	urlo_hist_miss(urlo->filelen);
+
+	head = &urlo->secondary_pending;
+	next = head->next;
+
+	/*
+	 * Just in case the fast thread is blocked waiting for
+	 * incoming connections.
+	 */
+	if (ti->thread)
+		wake_up_process(ti->thread);
+
+	while ((curr = next) != head) {
+		req = list_entry(curr, http_req_t, cachemiss);
+		check_req_list(req, &req->cachemiss);
+		next = curr->next;
+		DEC_STAT(nr_secondary_pending);
+		ti = req->ti;
+		list_del(curr);
+		__queue_req(req, ti);
+		urlo_hist_miss(urlo->filelen);
+	}
+	spin_unlock(&async_lock);
+}
+
+char tux_date [DATE_LEN] = "Wed, 01 Jan 1970 00:00:01 GMT";
+
+/*
+ * Just send the bare essentials
+ */
+#define SIMPLE_HEADER \
+		"HTTP/1.1 200 OK\r\n" \
+		"Content-Type: text/html\r\n" \
+		"Connection: Keep-Alive\r\n" \
+		"Date: %s\r\n" \
+		"Content-Length: %u\r\n" \
+		"\r\n"
+
+static int create_csumstream (http_req_t *req, const size_t length,
+	csumcache_t *csumc, struct file *filp, char *buf, urlobj_t *prev_urlo)
+{
+	int i = 0, datasize, packetsize, next_packetsize, currpos, fragmentsize;
+	unsigned long pageaddr, pageoffset;
+	skb_frag_t *frag;
+	struct page *tmppage;
+	int nr_frames, SSInr;
+	mm_segment_t oldmm;
+	char *curr;
+	urlobj_t *urlo;
+	SSImap_t SSImap;
+	int cached_read = 0;
+
+	urlo = req->urlo;
+	if (length < 0)
+		HTTP_BUG();
+	if (csumc->array)
+		HTTP_BUG();
+
+	datasize = length;
+	urlo->body_len = datasize;
+
+	Dprintk("body_len: %d.\n", urlo->body_len);
+
+	if (req->sock->sk)
+		fragmentsize = tcp_current_mss(req->sock->sk);
+	else
+		goto out;
+	if (fragmentsize > PAGE_SIZE)
+		fragmentsize = PAGE_SIZE;
+	csumc->packet_size = fragmentsize;
+
+	SSImap.nr = 0;
+	if (req->tcapi && req->tcapi->create_SSI_map) {
+		int len = length;
+
+		if (len > MAX_BLOCKSIZE)
+			HTTP_BUG();
+
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		if (prev_urlo && prev_urlo->csumc)
+			http_read(prev_urlo, buf);
+		else {
+repeat_read:
+			len = filp->f_op->read(filp, buf, len, &filp->f_pos);
+			if (len < 0) {
+				if ((len == -512) || (len == -EAGAIN)) {
+					flush_all_signals();
+					reap_kids();
+					goto repeat_read;
+				}
+				if ((len == -ENOMEM) || (len == -EFAULT))
+					goto repeat_read;
+				printk("read returned %d.\n", len);
+				HTTP_BUG();
+			}
+		}
+		set_fs(oldmm);
+
+		buf[len] = 0;
+		filp->f_pos = 0;
+		req->tcapi->create_SSI_map(req, csumc, buf, datasize, &SSImap);
+		cached_read = 1;
+	}
+	/*
+	 * Calculate number of fragments. Worst-case: every SSI
+	 * mapping divides the packet into 3 pieces instead of
+	 * the 1 original, plus every page creates two extra frames.
+	 */
+	nr_frames = (length + fragmentsize-1)/fragmentsize + 1;
+	nr_frames += SSImap.nr*2;
+	nr_frames += (length/PAGE_SIZE*2 + 1);
+
+	csumc->array = http_kmalloc(nr_frames*sizeof(skb_frag_t), ALLOC_CSUMCARRAY);
+	memset(csumc->array, 0, nr_frames*sizeof(skb_frag_t));
+
+	if (urlo->header_len)
+		HTTP_BUG();
+
+repeat_alloc:
+	tmppage = alloc_page(GFP_HIGHUSER);
+	if (!tmppage)
+		goto repeat_alloc;
+	pageaddr = kmap(tmppage);
+	curr = (char *)pageaddr;
+	pageoffset = 0;
+
+	/*
+	 * Create default header. Is always smaller than PAGE_SIZE.
+	 */
+	sprintf(curr, SIMPLE_HEADER, tux_date, urlo->filelen);
+
+	urlo->header_len = strlen(curr);
+	packetsize = urlo->header_len;
+	datasize += packetsize;
+	currpos = -packetsize;
+
+	if (urlo->header_len > fragmentsize)
+		HTTP_BUG();
+	if (!urlo->header_len)
+		HTTP_BUG();
+	Dprintk("first (header) csumc's size: %d.\n", packetsize);
+
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+	SSInr = 0;
+	frag = csumc->array;
+	next_packetsize = fragmentsize - urlo->header_len;
+	if (next_packetsize > datasize - urlo->header_len)
+		next_packetsize = 0;
+
+	goto inside;
+
+	while (datasize) {
+		int len, continuous;
+
+		if (pageoffset > PAGE_SIZE)
+			HTTP_BUG();
+		if (next_packetsize) {
+			packetsize = next_packetsize;
+			next_packetsize = 0;
+		} else {
+			packetsize = datasize;
+			if (packetsize > fragmentsize)
+				packetsize = fragmentsize;
+		}
+
+		frag = csumc->array + i;
+		Dprintk("SSInr: %d, SSImap.nr: %d.\n", SSInr, SSImap.nr);
+		continuous = 0;
+		if (SSInr < SSImap.nr) {
+			int distance = SSImap.pos[SSInr] - currpos;
+
+			if (distance < 0)
+				HTTP_BUG();
+			Dprintk("currpos: %d, SSImap.pos[SSInr]: %d, distance: %d, packetsize: %d.\n", currpos, SSImap.pos[SSInr], distance, packetsize);
+			if (distance < packetsize) {
+				if (!distance) {
+					if (!SSImap.size[SSInr])
+						HTTP_BUG();
+					packetsize = SSImap.size[SSInr];
+					continuous = 1;
+					frag->private = (void *)currpos;
+					SSInr++;
+				} else
+					packetsize = distance;
+			}
+		}
+
+		if (pageoffset == PAGE_SIZE) {
+next_page:
+			Dprintk("unmapping %08lx.\n", pageaddr);
+			kunmap(tmppage);
+repeat_tmppage_alloc:
+			tmppage = alloc_page(GFP_HIGHUSER);
+			if (!tmppage)
+				goto repeat_tmppage_alloc;
+			pageaddr = kmap(tmppage);
+			Dprintk("mapped %08lx.\n", pageaddr);
+			curr = (char *)pageaddr;
+			pageoffset = 0;
+		} else  {
+			if (pageoffset + packetsize > PAGE_SIZE) {
+				if (continuous) {
+					Dprintk("need continuous area...\n");
+					goto next_page;
+				}
+				next_packetsize = packetsize -
+						(PAGE_SIZE - pageoffset);
+				packetsize = PAGE_SIZE-pageoffset;
+			}
+		}
+
+		if (cached_read) {
+			if (currpos < 0)
+				HTTP_BUG();
+			if (packetsize <= 0)
+				HTTP_BUG();
+			memcpy(curr, buf + currpos, packetsize);
+		} else {
+repeat_read2:
+			len = filp->f_op->read(filp, curr, packetsize, &filp->f_pos);
+			if (len < 0) {
+				if ((len == -ERESTARTSYS) || (len == -EAGAIN)) {
+					flush_all_signals();
+					reap_kids();
+					goto repeat_read2;
+				}
+				if ((len == -ENOMEM) || (len == -EFAULT))
+					goto repeat_read2;
+			}
+			if (len != packetsize) {
+				printk("whoops, %d != %d. (filelen: %d)\n", len, packetsize, urlo->filelen);
+				printk("checksumming csumc %p, size %d, datasize: %d, curr: %p, currpos: %d, pageoffset: %ld.\n", frag, packetsize, datasize, curr, currpos, pageoffset);
+				HTTP_BUG();
+			}
+		}
+inside:
+		if (!packetsize)
+			HTTP_BUG();
+		if (pageoffset + packetsize > PAGE_SIZE)
+			HTTP_BUG();
+
+		Dprintk("checksumming csumc %p, size %d, datasize: %d, curr: %p, currpos: %d, pageoffset: %ld.\n", frag, packetsize, datasize, curr, currpos, pageoffset);
+
+		frag->csum = csum_partial(curr, packetsize, 0);
+		frag->size = packetsize;
+		frag->page = tmppage;
+		frag->page_offset = pageoffset;
+		frag->data = urlo;
+		frag->frag_done = unuse_frag;
+
+		datasize -= packetsize;
+		currpos += packetsize;
+		pageoffset += packetsize;
+		/*
+		 * Align individual frames to cacheline size.
+		 * this can never lead to anything bigger than PAGE_SIZE.
+		 */
+		pageoffset = L1_CACHE_ALIGN(pageoffset);
+		if (pageoffset > PAGE_SIZE)
+			HTTP_BUG();
+		curr = (char *)pageaddr + pageoffset;
+
+		i++;
+		if (i > nr_frames)
+			HTTP_BUG();
+	}
+	set_fs(oldmm);
+
+	csumc->size = i;
+	kunmap(tmppage);
+out:
+	return i;
+}
+
+static int create_csumcache (http_req_t *req, struct file *filp,
+		 char *buf, urlobj_t *prev_urlo)
+{
+	csumcache_t *csumc, *tmp;
+	urlobj_t *urlo;
+	int len;
+
+	if (!filp)
+		HTTP_BUG();
+	filp->f_pos = 0;
+	urlo = req->urlo;
+	len = urlo->filelen;
+
+	if (len < 0)
+		HTTP_BUG();
+
+	csumc = http_kmalloc(sizeof(csumcache_t), ALLOC_CSUMCSTRUCT);
+	memset(csumc, 0, sizeof(csumcache_t));
+	create_csumstream(req, len, csumc, filp, buf, prev_urlo);
+	if (!csumc->size)
+		HTTP_BUG();
+
+	/*
+	 * Rare operation.
+	 */
+	spin_lock(&add_csumc);
+	tmp = urlo->csumc;
+	if (!tmp)
+		req->urlo->csumc = csumc;
+	spin_unlock(&add_csumc);
+
+	if (tmp) {
+		printk("free_csumc 3().\n");
+		csumc = tmp;
+	}
+	return 0;
+}
+
+#define kmap_frag(frag) ((char *)kmap((frag)->page) + (frag)->page_offset)
+#define kunmap_frag(frag) kunmap((frag)->page)
+
+int http_read (urlobj_t *urlo, char *to)
+{
+	csumcache_t *csumc = urlo->csumc;
+	int size, i, err;
+
+	size = 0;
+
+	for (i = 1; i < csumc->size; i++) {
+		skb_frag_t *frag = csumc->array + i;
+		char *from;
+
+		from = kmap_frag(frag);
+		err = copy_to_user(to, from, frag->size);
+		kunmap_frag(frag);
+		if (err)
+			return err;
+		to += frag->size;
+		size += frag->size;
+	}
+	return size;
+}
+
+static void handle_cachemiss (http_req_t *req, char *buf)
+{
+	urlobj_t *urlo = req->urlo, *prev_urlo;
+	struct dentry *dentry;
+	struct file *filp;
+	int err, miss;
+
+	Dprintk("handling cachemiss on req %p.\n", req);
+	check_req_list(req, NULL);
+	if (!urlo) {
+		miss = lookup_urlo(req, 0);
+		if (!miss && (!req->tcapi || req->userspace_module)) {
+			if (req->userspace_module && !req->tcapi)
+				HTTP_BUG();
+			if (!req->tcapi && !req->urlo)
+				HTTP_BUG();
+			queue_req(req);
+			return;
+		}
+		urlo = req->urlo;
+		if (!urlo && !req->tcapi)
+			HTTP_BUG();
+	}
+	prev_urlo = req->prev_urlo;
+	req->prev_urlo = NULL;
+
+	dentry = req->dentry;
+	if (!dentry && !req->tcapi)
+		HTTP_BUG();
+
+	check_req_list(req, NULL);
+	Dprintk("req->userspace_module: %d, req->tcapi: %p, req->method: %d, req->query: %s.\n", req->userspace_module, req->tcapi, req->method, req->query);
+	if (!req->userspace_module && req->tcapi && ((req->method != METHOD_GET) || req->query)) {
+		int ret;
+
+		Dprintk("TCAPI %p request.\n", req->tcapi);
+		if (!req->tcapi)
+			HTTP_BUG(); // FIXME: fail more gracefully
+		if (req->urlo)
+			HTTP_BUG();
+		if (req->dentry)
+			HTTP_BUG();
+		check_req_list(req, NULL);
+		cachemiss_unqueue(req);
+		ret = req->tcapi->query(req);
+		Dprintk("->query() returned %d.\n", ret);
+		switch (ret) {
+			case -1:
+				break;
+			case -2:
+				req->no_output = 1;
+			case 0:
+				if (req->userspace_module)
+					queue_userspace_req(req, req->ti);
+				else
+					queue_output_req(req, req->ti);
+				break;
+			default:
+				HTTP_BUG();
+		}
+		goto out;
+	}
+	Dprintk("handle cachemiss simple file path.\n");
+
+	if (test_and_set_bit(0, &urlo->csumcs_created)) {
+		spin_lock(&async_lock);
+		list_del(&req->cachemiss);
+		nr_async_pending--;
+		DEC_STAT(nr_cachemiss_pending);
+		__queue_secondary_cachemiss(req, urlo);
+		spin_unlock(&async_lock);
+		goto out;
+	}
+
+	filp = dentry_open(dentry, O_RDONLY, 0);
+	dget(dentry);
+
+	if (!filp)
+		HTTP_BUG();
+	if (!filp->f_dentry || !filp->f_dentry->d_inode)
+		HTTP_BUG();
+	if (filp->f_dentry != dentry)
+		HTTP_BUG();
+	if (filp->f_dentry->d_inode != urlo->inode)
+		HTTP_BUG();
+
+	err = create_csumcache(req, filp, buf, prev_urlo);
+	atomic_add(urlo->filelen, (atomic_t *)&kstat.csumcache_total);
+	queue_pending(req, urlo);
+	if (!err && (urlo->filelen <= http_max_cached_filesize))
+		flush_inode_pages(urlo->inode);
+	fput(filp);
+out:
+	if (prev_urlo)
+		put_urlo(prev_urlo);
+	return;
+}
+
+static int cachemiss_thread (void *data)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct k_sigaction *ka;
+	http_req_t *req;
+	char *buf;
+	int nr = (int)data;
+
+	printk("async IO thread %d started.\n", nr);
+	sprintf(current->comm, "async IO %d", nr);
+
+	spin_lock_irq(&current->sigmask_lock);
+#if 1
+	ka = current->sig->action + SIGCHLD-1;
+	ka->sa.sa_handler = SIG_IGN;
+#endif
+	siginitsetinv(&current->blocked, sigmask(SIGCHLD));
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	buf = (char *)__get_free_pages(GFP_USER, OUT_BUF_ORDER);
+	if (!buf)
+		HTTP_BUG();
+
+	for (;;) {
+		while (!list_empty(&async_queue) && (req = get_cachemiss())) {
+			handle_cachemiss(req, buf);
+			if (signal_pending(current)) {
+				flush_all_signals();
+				while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
+					/* nothing */;
+			}
+		}
+		if (signal_pending(current)) {
+			flush_all_signals();
+			while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
+				/* nothing */;
+		}
+		if (!list_empty(&async_queue))
+			continue;
+		add_wait_queue_exclusive(&async_sleep, &wait);
+		__set_current_state(TASK_EXCLUSIVE|TASK_INTERRUPTIBLE);
+		if (list_empty(&async_queue))
+			schedule();
+		__set_current_state(TASK_RUNNING);
+		remove_wait_queue(&async_sleep, &wait);
+	}
+
+	free_pages((unsigned long)buf, OUT_BUF_ORDER);
+
+	return 0;
+}
+
+void init_cachemiss_threads (void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&async_queue);
+	init_waitqueue_head(&async_sleep);
+
+	for (i = 0; i < NR_IO_THREADS; i++)
+		kernel_thread(cachemiss_thread, (void *)i, 0);
+}
+
--- linux/net/http/logger.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/logger.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,555 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * logger.c: log requests finished by TUX.
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <net/http.h>
+
+#define LOG_LEN ((1 << LOG_BUF_ORDER) * PAGE_SIZE)
+
+static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int log_head, log_tail;
+static char * log_buffer = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(log_wait);
+static DECLARE_WAIT_QUEUE_HEAD(log_full);
+static int logger_pid = 0;
+
+static struct file *log_filp = NULL;
+
+/*
+ * High-speed TUX logging architecture:
+ *
+ * All fast threads share a common log-ringbuffer. (default size 1MB)
+ * Log entries are binary and are padded to be cacheline aligned, this
+ * ensures that there is no cache-pingpong between fast threads.
+ *
+ * The logger thread writes out pending log entries within 1 second
+ * (buffer-cache writes data out within 5 seconds). The logger thread
+ * gets activated once we have more than 25% of the log ringbuffer
+ * filled - or the 1 second log timeout expires. Fast threads block
+ * if if more than 95% of the ringbuffer is filled and unblock only
+ * if used logbuffer space drops below 90%.
+ *
+ * This architecture guarantees that 1) logging is reliable (no
+ * log entry is ever lost), 2) timely (touches disk within 6 seconds),
+ * 3) in the log-contention case the saturation behavior is still
+ * write-clustered, but 4) if the logger thread can keep up then
+ * the coupling is completely asynchron and parallel.
+ *
+ * The binary log format gives us about 50% saved IO/memory bandwith
+ * and 50% less on-disk used log space than the traditional W3C ASCII
+ * format.
+ *
+ * (We might switch to raw IO though to write the logfile.)
+ */
+
+#define SOFT_LIMIT		(LOG_LEN*25/100)
+#define HARD_LIMIT		(LOG_LEN*95/100)
+#define HARD_RELAX_LIMIT	(LOG_LEN*90/100)
+
+int http_logentry_align_order = 5;
+
+#define ROUND_UP(x) (((((x)-1) >> http_logentry_align_order) + 1) \
+					<< http_logentry_align_order)
+
+static char no_uri [] = "<no URI>";
+
+#define CHECK_LOGPTR(ptr) \
+do { \
+	if ((ptr < log_buffer) || (ptr > log_buffer + LOG_LEN)) { \
+		printk("ouch: log ptr %p > %p + %ld!\n", \
+			ptr, log_buffer, LOG_LEN); \
+		HTTP_BUG(); \
+	} \
+} while (0)
+
+void __log_request (http_req_t *req)
+{
+	char *str, *next;
+	unsigned int inc, len, uri_len, pending, next_head;
+	unsigned long *tmp;
+
+	if (!log_filp)
+		return;
+	if (!log_buffer)
+		HTTP_BUG();
+	/*
+	 * Log the HTTP reply status (success, or type of failure)
+	 */
+	if (!req->http_status || (req->bytes_sent == -1) || !req->uri) {
+		
+		Dprintk("not logging req %p: {%s} [%d/%d]\n", req, req->uri, req->http_status, req->bytes_sent);
+		return;
+	}
+	if (!req->uri) {
+		req->uri = no_uri;
+		req->uri_len = strlen(no_uri);
+	}
+	uri_len = strlen(req->uri);
+	len = uri_len;
+	Dprintk("uri: {%s} [%d/%d]\n", req->uri, req->uri_len, strlen(req->uri));
+	if (len != req->uri_len) {
+		printk("hm, %s (%d != %d).\n",
+				req->uri, len, req->uri_len);
+//		HTTP_BUG();
+	}
+	if (req->method_str) {
+		Dprintk("method_str: {%s} [%d/%d]\n", req->method_str, req->method_len, strlen(req->method_str));
+		len += req->method_len;
+	}
+	if (req->version_str) {
+		Dprintk("version_str: {%s} [%d/%d]\n", req->version_str, req->version_len, strlen(req->version_str));
+		len += req->version_len;
+	}
+	if (req->query)
+		len += strlen(req->query) + 1;
+
+	inc = 5*sizeof(unsigned long) + len + 1 + 1;
+
+	spin_lock(&log_lock);
+
+	next_head = ROUND_UP(log_head + inc);
+
+	if (next_head < LOG_LEN) {
+		str = log_buffer + log_head;
+		if (str > log_buffer + LOG_LEN)
+			HTTP_BUG();
+		log_head = next_head;
+	} else {
+		if (log_head < LOG_LEN)
+			memset(log_buffer+log_head, 0, LOG_LEN-log_head);
+		str = log_buffer;
+		log_head = ROUND_UP(inc);
+	}
+
+	if (str < log_buffer || str+inc >= log_buffer+LOG_LEN) {
+		printk("hm, %s (%d).\n", req->uri, len);
+		printk("hm, %p + %d > %p + %ld.\n", str, len, log_buffer, LOG_LEN);
+//		HTTP_BUG();
+	}
+
+	tmp = (unsigned long *) str;
+	/*
+	 * Log record signature - this makes finding the next entry
+	 * easier (since record length is variable), and makes the
+	 * binary logfile more robust against potential data corruption
+	 * and other damage. It also handles the case where we wrap
+	 * the ringbuffer.
+	 */
+	*tmp = 0xdeadbeef;
+	str += sizeof(unsigned long);
+	CHECK_LOGPTR(str);
+	tmp++;
+
+	/*
+	 * Log the client IP address:
+	 */
+	if (req->sock && req->sock->sk)
+		*tmp = req->sock->sk->daddr;
+	else
+		*tmp = 0xffffffff;
+	str += sizeof(unsigned long);
+	CHECK_LOGPTR(str);
+	tmp++;
+
+	/*
+	 * Log the request timestamp, in units of 'seconds since 1970'.
+	 */
+	if (req->timestamp)
+		*tmp = req->timestamp;
+	else
+		*tmp = CURRENT_TIME;
+	str += sizeof(unsigned long);
+	CHECK_LOGPTR(str);
+	tmp++;
+
+	/*
+	 * Log the requested file size (in fact, log actual bytes sent.)
+	 */
+	*tmp = req->bytes_sent;
+	str += sizeof(unsigned long);
+	CHECK_LOGPTR(str);
+	tmp++;
+
+	*tmp = req->http_status;
+	str += sizeof(unsigned long);
+	CHECK_LOGPTR(str);
+
+	/*
+	 * Zero-terminated method, (base) URI, query and version string.
+	 */
+	if (req->method_str) {
+		memcpy(str, req->method_str, req->method_len-1);
+		str += req->method_len-1;
+		CHECK_LOGPTR(str);
+		*str++ = ' ';
+	}
+	strcpy(str, req->uri);
+	str += uri_len;
+	CHECK_LOGPTR(str);
+	if (req->query) {
+		*str++ = '?';
+		strcpy(str, req->query);
+		str += strlen(req->query);
+		CHECK_LOGPTR(str);
+	}
+	if (req->version_str) {
+		*str++ = ' ';
+		memcpy(str, req->version_str, req->version_len-1);
+		str += req->version_len-1;
+		CHECK_LOGPTR(str);
+	}
+	*str++ = 0;
+	CHECK_LOGPTR(str);
+	/*
+	 * pad with spaces to next cacheline, with an ending newline.
+	 * (not needed for the user-space log utility, but results in
+	 * a more readable binary log file, and reduces the amount
+	 * of cache pingpong.)
+	 */
+	next = (char *)ROUND_UP((unsigned long)str+1);
+
+	*--next = '\n';
+	CHECK_LOGPTR(next);
+	len = next-str;
+	memset(str, ' ', len);
+
+	pending = (log_head-log_tail) % LOG_LEN;
+	spin_unlock(&log_lock);
+
+	if (pending >= SOFT_LIMIT)
+		wake_up(&log_wait);
+
+	if (pending >= HARD_LIMIT)
+		sleep_on(&log_full);
+}
+
+void flush_request (http_req_t *req, threadinfo_t *ti)
+{
+	struct socket *sock;
+	struct sock *sk = NULL;
+	int keep_alive;
+
+	check_req_list(req, NULL);
+	__set_task_state(current, TASK_RUNNING);
+
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	if (req->ti != ti)
+		HTTP_BUG();
+	if (ti->thread != current)
+		HTTP_BUG();
+
+	if (!req->http_status) {
+//		printk("no HTTP status! {m:%d, f:{%s}, q:{%s}}\n", req->method, req->objectname, req->query);
+//		HTTP_BUG();
+	}
+	log_request(req);
+	sock = req->sock;
+	if (sock)
+		sk = sock->sk;
+	Dprintk("FLUSHING req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), sock, sk, req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+#if 0
+	if (sk)
+		if (sock->sk->tp_pinfo.af_tcp.nonagle != 1)
+			HTTP_BUG();
+#endif
+	if (req->dentry) {
+	    	dput(req->dentry);
+	    	req->dentry = NULL;
+	}
+	if (req->urlo) {
+		put_urlo(req->urlo);
+		req->urlo = NULL;
+	}
+	if (req->prev_urlo)
+		HTTP_BUG();
+	if (req->private) {
+		http_kfree(req->private, ALLOC_REQ_PRIVATE);
+		req->private = NULL;
+	}
+	if (req->userspace_module)
+		HTTP_BUG();
+	if (req->redirect_secondary)
+		HTTP_BUG();
+	if (test_bit(0, &req->idle_input))
+		HTTP_BUG();
+
+	req->tcapi = NULL;
+	req->SSI = 0;
+
+	req->headers_len = 0;
+	req->parsed_len = 0;
+	req->method = METHOD_NONE;
+	req->method_len = 0;
+	req->method_str = NULL;
+	req->version = 0;
+	req->version_str = NULL;
+	req->version_len = 0;
+
+	req->uri = NULL;
+	req->uri_len = 0;
+
+	req->objectname = NULL;
+	req->objectname_len = 0;
+
+	req->query = NULL;
+	req->query_len = 0;
+
+	req->cookies = NULL;
+	req->cookies_len = 0;
+	req->parse_cookies = 0;
+
+	req->contentlen = NULL;
+	req->contentlen_strlen = 0;
+	req->content_len = 0;
+
+	req->post_data = NULL;
+	req->post_data_len = 0;
+
+	req->timestamp = 0;
+	req->http_status = 0;
+
+	req->bytes_sent = 0;
+	req->body_len = 0;
+	keep_alive = req->keep_alive;
+	req->keep_alive = 0;
+	req->no_output = 0;
+	req->event = 0;
+
+	if (req->private)
+		HTTP_BUG();
+
+	if (sk && keep_alive) {
+		if (skb_queue_empty(&sk->receive_queue)) {
+			add_keepalive_timer(req);
+			if (test_and_set_bit(0, &req->idle_input))
+				HTTP_BUG();
+			/*
+			 * Avoid the race with the event callback:
+			 */
+			if (skb_queue_empty(&sk->receive_queue) ||
+				   !test_and_clear_bit(0, &req->idle_input)) {
+				INC_STAT(nr_idle_input_pending);
+				return;
+			}
+			del_keepalive_timer(req);
+		}
+		Dprintk("KEEPALIVE PENDING req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d) ({%s}, {%s}, {%s}, {%s}).\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->http_status, req->method_str ? req->method_str : "<null>", req->uri ? req->uri : "<null>", req->query ? req->query : "<null>", req->version_str ? req->version_str : "<null>");
+		check_req_list(req, NULL);
+		spin_lock_irq(&ti->input_lock);
+#if 0
+		list_add_tail(&req->input, &ti->input_pending);
+#else
+		list_add(&req->input, &ti->input_pending);
+#endif
+		INC_STAT(nr_input_pending);
+		INC_STAT(nr_keepalive_optimized);
+		spin_unlock_irq(&ti->input_lock);
+		return;
+	}
+	del_keepalive_timer(req);
+	if (sk)
+		remove_wait_queue(sk->sleep, &req->sleep);
+	if (sock) {
+		unlink_http_socket(req);
+		req->sock = NULL;
+	}
+	/*
+	 * Close potential user-space file descriptors.
+	 */
+	{
+		int fd = req->userspace_fd;
+
+		if (fd != -1) {
+			req->userspace_fd = -1;
+			sys_close(fd);
+		} else
+			if (sock)
+				sock_release(sock);
+	}
+	check_req_list(req, NULL);
+	kfree_req(req, ti);
+}
+
+int finish_requests (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	http_req_t *req;
+	int count = 0;
+	
+	head = &ti->finish_pending;
+	next = head->next;
+
+	Dprintk("START of finish_requests() loop ...\n");
+	while ((curr = next) != head) {
+		if (current->need_resched)
+			break;
+		if (!count++)
+			__set_task_state(current, TASK_RUNNING);
+
+		req = list_entry(curr, http_req_t, finish);
+		check_req_list(req, &req->finish);
+		next = curr->next;
+
+		if (req->ti != ti)
+			HTTP_BUG();
+		if (ti->thread != current)
+			HTTP_BUG();
+
+		list_del(curr);
+		DEC_STAT(nr_finish_pending);
+		Dprintk("found req %p in finish_requests() queue.\n", req);
+		flush_request(req, ti);
+	}
+	Dprintk("END of finish_requests() loop ...\n");
+	return count;
+}
+
+static int writeout_log (void)
+{
+	unsigned int len, pending;
+	char * str;
+	int ret;
+
+	spin_lock(&log_lock);
+	str = log_buffer + log_tail;
+	if (log_head < log_tail) {
+		len = LOG_LEN-log_tail;
+		log_tail = 0;
+	} else {
+		len = log_head-log_tail;
+		log_tail = log_head;
+	}
+	pending = (log_head-log_tail) % LOG_LEN;
+	spin_unlock(&log_lock);
+	if (!len)
+		goto out;
+
+	ret = http_write_file(log_filp, str, len);
+	if (len != ret) {
+		printk("hm, log write returned %d != %d.\n", ret, len);
+		printk("... log_filp: %p, str: %p, len: %d str[len-1]: %d.\n", log_filp, str, len, str[len-1]);
+	}
+	/*
+	 * Reduce the cache footprint of the logger file - it's
+	 * typically write-once.
+	 */
+	flush_inode_pages(log_filp->f_dentry->d_inode);
+out:
+	if (pending < HARD_RELAX_LIMIT)
+		wake_up(&log_full);
+
+	return pending;
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(stop_logger_wait);
+static volatile int stop_logger = 0;
+
+static int logger_thread (void *data)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned int pending;
+	mm_segment_t oldmm;
+
+	oldmm = get_fs();
+	set_fs(KERNEL_DS);
+	printk("logger thread started.\n");
+	sprintf(current->comm, "HTTP logger");
+
+	spin_lock_irq(&current->sigmask_lock);
+	siginitsetinv(&current->blocked, 0);
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	if (log_buffer)
+		HTTP_BUG();
+	log_buffer = (char *) __get_free_pages(GFP_USER, LOG_BUF_ORDER);
+	printk("log buffer: %p.\n", log_buffer);
+	memset(log_buffer, 0, LOG_LEN);
+	log_head = log_tail = 0;
+
+	add_wait_queue(&log_wait, &wait);
+	for (;;) {
+		Dprintk("logger does writeout - stop:%d.\n", stop_logger);
+		do {
+			pending = writeout_log();
+		} while (pending >= SOFT_LIMIT);
+
+		Dprintk("logger does sleep - stop:%d.\n", stop_logger);
+		__set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ);
+		Dprintk("logger back from sleep - stop:%d.\n", stop_logger);
+		if (stop_logger)
+			break;
+	}
+	remove_wait_queue(&log_wait, &wait);
+
+	free_pages((unsigned long)log_buffer, LOG_BUF_ORDER);
+	log_buffer = NULL;
+	if (log_filp) {
+		fput(log_filp);
+		log_filp = NULL;
+	}
+	stop_logger = 0;
+
+	wake_up(&stop_logger_wait);
+	set_fs(oldmm);
+
+	return 0;
+}
+
+void flush_logqueue (threadinfo_t *ti)
+{
+	struct list_head *head, *curr, *next;
+	http_req_t *req;
+	
+	head = &ti->finish_pending;
+	curr = head->next;
+
+	while (curr != head) {
+		req = list_entry(curr, http_req_t, finish);
+		next = curr->next;
+		list_del(curr);
+		DEC_STAT(nr_finish_pending);
+		req->keep_alive = 0;
+		flush_request(req, ti);
+	}
+}
+
+void init_log_thread (void)
+{
+	if (log_filp)
+		HTTP_BUG();
+	logger_pid = kernel_thread(logger_thread, NULL, 0);
+	if (logger_pid < 0)
+		HTTP_BUG();
+	printk("HTTP logger: opening log file {%s}.\n", http_logfile);
+	log_filp = http_open_file(http_logfile, O_CREAT|O_APPEND|O_WRONLY);
+	if (!log_filp)
+		printk("HTTP logger: couldnt open log file {%s}!\n", http_logfile);
+}
+
+void stop_log_thread (void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int ret;
+
+	add_wait_queue(&stop_logger_wait, &wait);
+	__set_current_state(TASK_UNINTERRUPTIBLE);
+	stop_logger = 1;
+	wake_up(&log_wait);
+	schedule();
+	remove_wait_queue(&stop_logger_wait, &wait);
+
+	ret = waitpid(logger_pid, NULL, __WCLONE);
+#if 0
+// unsure
+	if (ret < 0)
+		HTTP_BUG();
+#endif
+}
--- linux/net/http/http_parser.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/http_parser.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,404 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * http_parser.c: HTTP header parsing and construction routines
+ *
+ * Right now we detect simple GET headers, anything more
+ * subtle gets redirected to secondary server port.
+ */
+
+#include <net/http.h>
+#include "parser.h"
+
+int http_Dprintk = 0;
+
+/* 
+ * Parse the HTTP message and put results into the request structure.
+ * CISAPI extensions do not see the actual message buffer.
+ *
+ * Any perceived irregularity is honored with a redirect to the
+ * secondary server - which in most cases should be Apache. So
+ * if TUX gets confused by some strange request we fall back
+ * to Apache to be RFC-correct.
+ *
+ * The parser is 'optimistic', ie. it's optimized for the case where
+ * the whole message is available and correct. The parser is also
+ * supposed to be 'robust', ie. it can be called multiple times with
+ * an incomplete message, as new packets arrive.
+ */
+
+int parse_http_message (http_req_t *req, const int len)
+{
+	char *message;
+	char c, *curr, *end, *uri;
+	int objectname_len;
+	int had_cookie;
+
+	message = req->headers;
+	Dprintk("parsing request:\n---\n%s\n---\n", message);
+/*
+ * RFC 2616, 5.1:
+ *
+ *         Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
+ */
+
+	if (!len)
+		HTTP_BUG();
+
+	curr = message;
+	end = message + len;
+
+	if (req->method_len) {
+		curr += req->method_len;
+		goto continue_filename;
+	}
+
+#define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete_message; } while (0)
+#define GOTO_REDIR do { printk("redirect secondary at %s:%d.\n", __FILE__, __LINE__); goto redirect_secondary; } while (0)
+
+#define PRINT_MESSAGE_LEFT \
+    Dprintk("message left at %s:%d:\n--->{%s}<---\n", __FILE__, __LINE__, curr)
+
+	switch (*curr) {
+		case 'G':
+			if (PARSE_METHOD(req,curr,end,GET))
+				break;
+			GOTO_REDIR;
+
+		case 'H':
+			if (PARSE_METHOD(req,curr,end,HEAD))
+				break;
+			GOTO_REDIR;
+
+		case 'P':
+			if (PARSE_METHOD(req,curr,end,POST))
+				break;
+			if (PARSE_METHOD(req,curr,end,PUT))
+				break;
+			GOTO_REDIR;
+
+		default:
+			GOTO_REDIR;
+	}
+
+	req->method_str = message;
+	req->method_len = curr-message;
+
+continue_filename:
+	Dprintk("got method %d\n", req->method);
+
+	PRINT_MESSAGE_LEFT;
+
+	if (req->objectname_len) {
+		curr += req->objectname_len;
+		objectname_len = req->objectname_len;
+		goto continue_query;
+	}
+	/*
+	 * Ok, we got one of the methods we can handle, parse
+	 * the URI:
+	 */
+
+	req->uri = req->objectname = uri = curr;
+
+	for (;;) {
+		c = *curr;
+
+		if (!c)
+			GOTO_INCOMPLETE;
+		if (c == ' ' || c == '?')
+			break;
+		if (++curr == end)
+			GOTO_INCOMPLETE;
+	}
+	objectname_len = curr - uri;
+	req->objectname_len = objectname_len;
+	req->uri_len = objectname_len;
+	if (!objectname_len)
+		GOTO_REDIR;
+continue_query:
+
+	c = *curr;
+	*curr = 0;
+	curr++;
+
+	Dprintk("got filename %s (%d)\n", req->objectname, req->objectname_len);
+
+	PRINT_MESSAGE_LEFT;
+
+	if (req->query_len) {
+		curr += req->query_len;
+		goto continue_version;
+	}
+	/*
+	 * Parse optional query string. Copy until end-of-string or space.
+	 */
+	if (c == '?') {
+		int query_len;
+		char *query;
+
+		if (req->query && (req->query != curr))
+			HTTP_BUG();
+		req->query = query = curr;
+
+		for (;;) {
+			c = *curr;
+
+			if (!c)
+				GOTO_INCOMPLETE;
+			if (c == ' ')
+				break;
+			if (++curr == end)
+				GOTO_INCOMPLETE;
+		}
+		query_len = curr - query;
+		req->query_len = query_len;
+	}
+continue_version:
+	if (req->query_len) {
+		*curr = 0;
+		curr++;
+		Dprintk("got query string %s (%d)\n", req->query, req->query_len);
+	}
+	PRINT_MESSAGE_LEFT;
+	if (req->version_len) {
+		curr += req->version_len;
+		goto continue_headers;
+	}
+	/*
+	 * Parse the HTTP version field:
+	 */
+	req->version_str = curr;
+	if (!PARSE_TOKEN(curr,end,"HTTP/1."))
+		GOTO_REDIR;
+
+	switch (*curr++) {
+		case '0':
+			req->version = HTTP_1_0;
+			req->keep_alive = 0;
+			break;
+		case '1':
+			req->version = HTTP_1_1;
+			req->keep_alive = 1;
+			break;
+		default:
+			GOTO_REDIR;
+	}
+	if (get_c(curr) != '\n') {
+		if (curr - message == len-1)
+			GOTO_INCOMPLETE;
+		GOTO_REDIR;
+	}
+	req->version_len = curr - req->version_str;
+
+continue_headers:
+	*curr = 0;
+	curr++;
+	Dprintk("got version %d\n", req->version);
+	PRINT_MESSAGE_LEFT;
+
+	/*
+	 * Now parse (optional) request header fields:
+	 */
+	had_cookie = 0;
+	for (;;) {
+		switch (get_c(curr)) {
+		case '\r':
+			if (!*++curr)
+				GOTO_INCOMPLETE;
+			GOTO_REDIR;
+		case '\n':
+			curr++;
+			goto out;
+		case 'A':
+			if (PARSE_TOKEN(curr,end,"Accept: ")) {
+				Dprintk("ignoring Accept field.\n");
+				// ignore for now
+				SKIP_LINE;
+				break;
+			}
+			if (PARSE_TOKEN(curr,end,"Accept-Encoding: ")) {
+				Dprintk("ignoring Accept-Encoding field.\n");
+				// ignore for now
+				SKIP_LINE;
+				break;
+			}
+			if (PARSE_TOKEN(curr,end,"Accept-Language: ")) {
+				Dprintk("ignoring Accept-Language field.\n");
+				// ignore for now
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'C':
+			if (PARSE_TOKEN(curr,end,"Connection: ")) {
+				switch (get_c(curr)) {
+				case 'K':
+					if (!PARSE_TOKEN(curr,end,"Keep-Alive"))
+						GOTO_REDIR;
+					req->keep_alive = 1;
+					break;
+
+				case 'c':
+					if (!PARSE_TOKEN(curr,end,"close"))
+						GOTO_REDIR;
+					req->keep_alive = 0;
+					break;
+				default:
+					GOTO_REDIR;
+				}
+				if (get_c(curr) == '\n')
+					break;
+				if (!*curr)
+					GOTO_INCOMPLETE;
+			}
+			if (PARSE_TOKEN(curr,end,"Cookie: ")) {
+				if (had_cookie)
+					GOTO_REDIR;
+				had_cookie = 1;
+				if (req->cookies_len) {
+					curr += req->cookies_len;
+					goto continue_cookies;
+				}
+				req->cookies = curr;
+				SKIP_LINE;
+				req->cookies_len = curr - req->cookies;
+				if (req->cookies_len)
+					*(curr-1) = 0;
+continue_cookies:
+				Dprintk("Cookie field: %s.\n", req->cookies);
+				break;
+			}
+			if (PARSE_TOKEN(curr,end,"Content-Type: ")) {
+				// ignore for now
+				Dprintk("ignoring Content-Type field.\n");
+				SKIP_LINE;
+				break;
+			}
+			if (PARSE_TOKEN(curr,end,"Content-type: ")) {
+				// ignore for now
+				Dprintk("ignoring Content-type field.\n");
+				SKIP_LINE;
+				break;
+			}
+			if (PARSE_TOKEN(curr,end,"Cache-Control: ")) {
+				// ignore for now
+				Dprintk("ignoring Cache-Control field.\n");
+				SKIP_LINE;
+				break;
+			}
+			if (PARSE_TOKEN(curr,end,"Content-Length: ")) {
+				char *tmp;
+				if (req->contentlen_strlen) {
+					curr += req->contentlen_strlen;
+					goto continue_contentlen;
+				}
+				req->contentlen = curr;
+				SKIP_LINE;
+				req->contentlen_strlen = curr - req->contentlen;
+continue_contentlen:
+				if (req->contentlen_strlen) {
+					*(curr-1) = 0;
+					tmp = req->contentlen;
+					req->content_len = simple_strtoul(tmp, &tmp, 10);
+				}
+				Dprintk("Content-Length field: %s.\n", req->contentlen);
+				Dprintk("Content-Length value: %d.\n", req->content_len);
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'H':
+			if (PARSE_TOKEN(curr,end,"Host: ")) {
+				// ignore for now
+				Dprintk("ignoring Host field.\n");
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'I':
+			if (PARSE_TOKEN(curr,end,"If-Modified-Since: ")) {
+				// ignore for now
+				Dprintk("ignoring If-Modified-Since field.\n");
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'N':
+			if (PARSE_TOKEN(curr,end,"Negotiate: ")) {
+				// ignore for now
+				Dprintk("ignoring Negotiate field.\n");
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'P':
+			if (PARSE_TOKEN(curr,end,"Pragma: ")) {
+				// ignore for now
+				Dprintk("ignoring Pragma field.\n");
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'R':
+			if (PARSE_TOKEN(curr,end,"Referer: ")) {
+				// ignore for now
+				Dprintk("ignoring Referer field.\n");
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 'U':
+			if (PARSE_TOKEN(curr,end,"User-Agent: ")) {
+				// ignore for now
+				Dprintk("ignoring User-Agent field.\n");
+				SKIP_LINE;
+				break;
+			}
+			GOTO_REDIR;
+
+		case 0:
+			GOTO_INCOMPLETE;
+		default:
+			GOTO_REDIR;
+		}
+		curr++;
+		PRINT_MESSAGE_LEFT;
+	}
+out:
+	/*
+	 * POST data.
+	 */
+	if ((req->method == METHOD_POST) && req->content_len) {
+		PRINT_MESSAGE_LEFT;
+		if (curr + req->content_len > message + len)
+			GOTO_INCOMPLETE;
+		req->post_data = curr;
+		req->post_data_len = req->content_len;
+		curr += req->content_len;
+		*curr = 0;
+		Dprintk("POST-ed data: {%s}\n", req->post_data);
+	}
+	Dprintk("ok, request accepted.\n");
+	PRINT_MESSAGE_LEFT;
+	req->parsed_len = curr-message;
+	return objectname_len;
+
+incomplete_message:
+	Dprintk("incomplete message!\n");
+	PRINT_MESSAGE_LEFT;
+	return 0;
+
+redirect_secondary:
+	Dprintk("redirecting message to secondary server!\n");
+	PRINT_MESSAGE_LEFT;
+	return -1;
+}
--- linux/net/http/CAD.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/CAD.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,853 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * CAD.c: Implementation of the SPECweb99 dynamic application via
+ *        the HTTP trusted-API.
+ */
+
+#include <net/http.h>
+
+static inline int send_reply_head (http_req_t *req, size_t body_size);
+static inline int send_reply_tail (http_req_t *req);
+static int send_err (http_req_t *req, char *message);
+
+/*
+ * We memory-map the head of the postlog file so that we
+ * do not have to call write() every time we update it.
+ * The postlog record is written out to disk on every POST
+ * request, and the record counter (in the memory mapped
+ * buffer) is updated atomically as well.
+ */
+#define POSTLOG "/tmp/postlog"
+
+static HTTP_DECLARE_MUTEX(postlog_sem);
+static struct http_file *post_file;
+static int postlog_count;
+static char *postlog_head;
+static struct http_page *postlog_head_page;
+
+#define CAD_TAG_HEAD	"<!WEB99CAD><IMG SRC=\"/file_set/"
+#define CAD_TAG_BODY	"dirNNNNN/classX_Y"
+#define CAD_TAG_TAIL	"\"><!/WEB99CAD>"
+
+#define CAD_TAG CAD_TAG_HEAD CAD_TAG_BODY CAD_TAG_TAIL
+
+#define CAD_TAG_BODY_POS (sizeof(CAD_TAG_HEAD)-1)
+#define CAD_TAG_BODY_LEN (sizeof(CAD_TAG_BODY)-1)
+#define CAD_TAG_TAIL_LEN (sizeof(CAD_TAG_TAIL)-1)
+#define CAD_TAG_LEN (sizeof(CAD_TAG)-1)
+
+typedef struct CAD_struct {
+	int user_id;
+	int last_ad;
+	char ad_filename [100];
+	int reply_cookies_len;
+	char reply_cookies[MAX_COOKIE_LEN];
+} CAD_t;
+
+static inline int is_class12 (char *str)
+{
+	unsigned char *tmp;
+
+	tmp = strstr(str, "class");
+	if (tmp) {
+		tmp += sizeof("class")-1;
+		if ((tmp[0] != '1') && (tmp[0] != '2'))
+			return 0;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * This function reads the object, scans the temporary buffer for
+ * the SPECweb99 tag string, does CAD replacement and sends the
+ * result out to the client.
+ */
+static inline int scan_send_file (http_req_t *req, CAD_t *CADp)
+{
+	char *tmpbuf, *target;
+	int size, left, ret;
+	mm_segment_t oldmm;
+
+	if (!CADp || !req->query || !is_class12(req->query))
+		return http_send_object(req, 0, 0);
+
+	size = req->urlo->filelen;
+	tmpbuf = req->ti->output_buffer;
+	oldmm = get_fs(); set_fs(KERNEL_DS);
+	ret = http_read(req->urlo, tmpbuf);
+	set_fs(oldmm);
+
+	if (ret != size)
+		return send_err(req, "CAD: error while reading object file!\n");
+	tmpbuf[size] = 0; // zero-delimited string
+
+	target = tmpbuf;
+	left = size - CAD_TAG_LEN + 1;
+
+	for (;;) {
+		target = memchr(target, CAD_TAG[0], left);
+		if (!target)
+			break; // no such character left
+
+		if (memcmp(target, CAD_TAG, CAD_TAG_LEN)) {
+			target++; // skip past the '<'
+			left--;
+			continue;
+		}
+		target += CAD_TAG_BODY_POS;
+		memcpy(target, CADp->ad_filename, CAD_TAG_BODY_LEN);
+		target += CAD_TAG_BODY_LEN + CAD_TAG_TAIL_LEN;
+		left -= CAD_TAG_LEN;
+	}
+
+	http_send_client(req, tmpbuf, size, 0);
+
+	return size;
+}
+
+
+typedef struct user_dem_s {
+	unsigned int dem;
+} user_dem_t;
+
+static int max_userid;
+static user_dem_t *user_dem = NULL;
+static struct http_direntry *user_pers_dentry;
+
+#define USER_PERS_FILE "User.Personality"
+#define USER_PERS_RECLEN 15
+
+typedef struct ad_s {
+	unsigned int dem;
+	unsigned int gender_weight;
+	unsigned int age_weight;
+	unsigned int region_weight;
+	unsigned int interest_1_weight;
+	unsigned int interest_2_weight;
+	unsigned int min_match;
+	unsigned int expires;
+} ad_t;
+
+static int max_adid;
+static ad_t *ad = NULL;
+
+#define AD_FILE "Custom.Ads"
+#define AD_RECLEN 39
+
+static int read_custom_ads (http_req_t *req)
+{
+	struct http_file *file;
+	int ret, len, err = -2;
+	char *buf = NULL, *tmp;
+	struct http_direntry *dentry;
+	unsigned int adid, i, dem, min_match, weight, expires;
+
+
+	dentry = http_lookup_direntry(AD_FILE, &docroot, 0);
+	if (http_direntry_error(dentry))
+		goto error;
+	file = http_direntry_open(dentry, O_RDONLY, 0);
+	if (!file)
+		goto error;
+	len = http_file_size(file);
+	if (!len)
+		goto error;
+	if (ad) {
+		http_free(ad, ALLOC_AD_FILE);
+		ad = NULL;
+	}
+	max_adid = len/AD_RECLEN + 1;
+	ad = http_malloc(max_adid * sizeof(ad_t), ALLOC_AD_FILE);
+	buf = http_malloc(len, ALLOC_ADTMPBUF);
+	if (!ad || !buf)
+		goto error;
+
+	ret = http_read_file(file, buf, len);
+	http_close_file(file);
+	if (ret != len)
+		goto error;
+
+/*
+ * Sample ad record:
+	"   54 24808100    97F61  75  952393980\n"
+ */
+
+	tmp = buf;
+	i = 0;
+	for (tmp = buf; tmp != buf+len; tmp++) {
+
+		while (*tmp == ' ') tmp++;
+		adid = simple_strtoul(tmp, &tmp, 10);
+		if (adid != i)
+			goto error;
+		if (adid >= max_adid)
+			goto error;
+		i++;
+		if (*tmp != ' ')
+			goto error;
+		tmp++;
+		while (*tmp == ' ') tmp++;
+		dem = simple_strtoul(tmp, &tmp, 16);
+		tmp++;
+		while (*tmp == ' ') tmp++;
+		weight = simple_strtoul(tmp, &tmp, 16);
+		while (*tmp == ' ') tmp++;
+		min_match = simple_strtoul(tmp, &tmp, 10);
+		while (*tmp == ' ') tmp++;
+		expires = simple_strtoul(tmp, &tmp, 10);
+		if (*tmp != '\n')
+			goto error;
+		ad[adid].dem = dem;
+
+		ad[adid].gender_weight		= (weight & 0x000f0000) >> 16;
+		ad[adid].age_weight		= (weight & 0x0000f000) >> 12;
+		ad[adid].region_weight		= (weight & 0x00000f00) >> 8;
+		ad[adid].interest_1_weight	= (weight & 0x000000f0) >> 4;
+		ad[adid].interest_2_weight	= (weight & 0x0000000f);
+
+		ad[adid].min_match = min_match;
+		ad[adid].expires = expires;
+
+	}
+	err = 0;
+error:
+	if (buf)
+		http_free(buf, ALLOC_ADTMPBUF);
+	if (err)
+		return send_err(req, "CAD: error while reading & parsing the ad file.\n");
+	return err;
+}
+
+static int read_user_personality (http_req_t *req)
+{
+	struct http_file *file;
+	int ret, len, err = -2;
+	char *buf = NULL, *tmp;
+	unsigned int uid, i, dem;
+	struct http_direntry *dentry;
+
+	dentry = http_lookup_direntry(USER_PERS_FILE, &docroot, 0);
+	if (http_direntry_error(dentry))
+		goto error;
+	file = http_direntry_open(dentry, O_RDONLY, 0);
+	if (!file)
+		goto error;
+	len = http_file_size(file);
+	if (!len)
+		goto error;
+	if (user_dem) {
+		http_free(user_dem, ALLOC_USERDEM);
+		user_dem = NULL;
+	}
+	max_userid = len/USER_PERS_RECLEN + 1;
+	user_dem = http_malloc(max_userid * sizeof(user_dem_t), ALLOC_USERDEM);
+	buf = http_malloc(len, ALLOC_USERDEM_TMPBUF);
+	if (!user_dem || !buf) {
+		goto error;
+	}
+
+	ret = http_read_file(file, buf, len);
+	http_close_file(file);
+	if (ret != len) {
+		goto error;
+	}
+
+	i = 0;
+	for (tmp = buf; tmp != buf+len; tmp++) {
+		if (*tmp == ' ')
+			continue;
+		uid = simple_strtoul(tmp, &tmp, 10);
+		if (uid != i)
+			goto error;
+		if (uid >= max_userid)
+			goto error;
+		i++;
+		if (*tmp != ' ')
+			goto error;
+		while (*tmp == ' ') tmp++;
+		dem = simple_strtoul(tmp, &tmp, 16);
+		if (*tmp != '\n')
+			goto error;
+		user_dem[uid].dem = dem;
+	}
+	err = 0;
+error:
+	if (buf)
+		http_free(buf, ALLOC_USERDEM_TMPBUF);
+	if (err)
+		return send_err(req, "CAD: error while reading & parsing the user file.\n");
+	return err;
+}
+
+#define MAX_CUSTOM_ADS 360
+
+static inline int find_ad (int user_id, int last_ad, int *weight_p)
+{
+	int adid, weight = 0, dem;
+
+	for (adid = last_ad + 1; adid != last_ad; adid++) {
+		if (adid >= MAX_CUSTOM_ADS)
+			adid = 0;
+
+		dem = user_dem[user_id].dem & ad[adid].dem;
+		weight = 0;
+
+		if (dem & 0x30000000)
+			weight += ad[adid].gender_weight;
+		if (dem & 0x0f000000)
+			weight += ad[adid].age_weight;
+		if (dem & 0x00f00000)
+			weight += ad[adid].region_weight;
+		if (dem & 0x000ffc00)
+			weight += ad[adid].interest_1_weight;
+		if (dem & 0x000003ff)
+			weight += ad[adid].interest_2_weight;
+		if (weight >= ad[adid].min_match)
+			break;
+	}
+
+	*weight_p = weight;
+	return adid;
+}
+
+static unsigned int last_mtime = 0;
+
+static int reread_files (http_req_t *req)
+{
+	int ret = -2;
+	struct http_direntry *dentry;
+
+	http_dput(user_pers_dentry);
+	dentry = http_lookup_direntry(USER_PERS_FILE, &docroot, 0);
+	if (http_direntry_error(dentry))
+		goto error;
+	user_pers_dentry = dentry;
+
+	if (http_mtime(dentry) != last_mtime) {
+		void *tmp = user_dem;
+		user_dem = NULL;
+		http_free(tmp, ALLOC_USERDEM);
+		if (read_user_personality(req))
+			goto error;
+		if (read_custom_ads(req))
+			goto error;
+		last_mtime = http_mtime(dentry);
+	}
+	ret = 0;
+
+error:
+	return ret;
+}
+
+static inline int custom_ad_rotate (http_req_t *req, CAD_t *CADp)
+{
+	int adid, weight, expired, err;
+	int user_id, last_ad;
+	time_t now;
+
+	user_id = CADp->user_id;
+	last_ad = CADp->last_ad;
+
+	if (http_direntry_error(user_pers_dentry) ||
+			(http_mtime(user_pers_dentry) != last_mtime)) {
+		err = reread_files(req);
+		if (err)
+			return err;
+	}
+
+	/*
+	 * Any error in either reading or parsing of the files results
+	 * in a returned -1 adid.
+	 */
+	adid = -1;
+	expired = 1;
+	weight = 0;
+
+	adid = find_ad(user_id, last_ad, &weight);
+	if (adid < 0)
+		goto error;
+	now = http_time();
+	if (now <= ad[adid].expires)
+		expired = 0;
+
+error:
+	CADp->reply_cookies_len = sprintf(CADp->reply_cookies,
+		"found_cookie=Ad_id=%d&Ad_weight=%d&Expired=%d",
+			adid, weight, expired);
+
+	sprintf(CADp->ad_filename, "dir%05d/class%d_%d",
+		adid / 36, ((adid % 36) / 9), adid % 9);
+	return 0;
+}
+
+
+#define TOKEN_EQUAL(input,token) \
+		(!memcmp(input, token, sizeof(token)-1))
+
+#define PARSE_STRING(token,input,output)				\
+	({								\
+		int __ret = 0;						\
+		if (TOKEN_EQUAL(input, token)) {			\
+			char *tmp;					\
+									\
+			input += sizeof(token)-1;			\
+			tmp = output;					\
+			while (*input && *input != '&' &&		\
+						*input != ',')		\
+				*tmp++ = *input++;			\
+			*tmp = 0;					\
+			__ret = 1;					\
+		}							\
+		__ret;							\
+	})
+
+#define PARSE_UINT(token,input,output)					\
+	({								\
+		int __ret = 0;						\
+		if (TOKEN_EQUAL(input, token)) {			\
+									\
+			input += sizeof(token)-1;			\
+			output = simple_strtoul(input, &input, 10);	\
+			__ret = 1;					\
+		}							\
+		__ret;							\
+	})
+
+static int init_postlog_file (http_req_t *req)
+{
+	char buf[400], *tmp;
+	int ret;
+
+	if (post_file)
+		http_close_file(post_file);
+	post_file = http_open_file(POSTLOG, O_CREAT|O_TRUNC|O_APPEND|O_RDWR);
+	if (!post_file)
+		return send_err(req, "CAD: could not open POST-log!\n");
+	postlog_count = 0;
+	tmp = buf;
+	tmp += sprintf(tmp, "%10d\n", 0);
+	ret = http_write_file(post_file, buf, tmp-buf);
+	if (ret != tmp-buf)
+		return send_err(req, "CAD: POST-log write error!\n");
+	postlog_head_page = http_mmap_page(post_file, postlog_head, 0);
+	if (!postlog_head_page)
+		return send_err(req, "CAD: POST-log mmap error!\n");
+	return 0;
+}
+
+#define COMMAND_STRING "command/"
+#define COMMAND_RESET "Reset"
+#define COMMAND_FETCH "Fetch"
+
+static int do_reset (http_req_t *req, char *query)
+{
+	char maxload [20], pttime[20], maxthread[20],
+			exp1[20], exp2[20], urlroot [100];
+	char tmpstr1[256], tmpstr2[256];
+
+	http_sleep(1);
+	if (!PARSE_STRING("&maxload=", query, maxload))
+		return send_err(req,"CAD: invalid &maxload field!\n");
+	if (!PARSE_STRING("&pttime=", query, pttime))
+		return send_err(req,"CAD: invalid &pttime field!\n");
+	if (!PARSE_STRING("&maxthread=", query, maxthread))
+		return send_err(req,"CAD: invalid &maxthread field!\n");
+	if (!PARSE_STRING("&exp=", query, exp1))
+		return send_err(req,"CAD: invalid &exp1 field!\n");
+	if (!PARSE_STRING(",", query, exp2))
+		return send_err(req,"CAD: invalid &exp2 field!\n");
+	if (!PARSE_STRING("&urlroot=", query, urlroot))
+		return send_err(req,"CAD: invalid &urlroot field!\n");
+
+
+	strcpy(tmpstr1, http_docroot); strcat(tmpstr1, "/upfgen99");
+	strcpy(tmpstr2, http_docroot); strcat(tmpstr2, "/cadgen99");
+#define TOPDIR http_docroot
+#define UPFGEN tmpstr1
+#define CADGEN tmpstr2
+
+	{
+		char *argv_upfgen[] = { UPFGEN, "-C", TOPDIR, "-n", maxload,
+					"-t", maxthread, NULL};
+		char *argv_cadgen[] = { CADGEN, "-C", TOPDIR, "-e", pttime,
+					"-t", maxthread, exp1, exp2, NULL};
+		char * envp[] = { "HOME=/", "TERM=linux",
+			"PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+
+		if (http_exec_process(UPFGEN, argv_upfgen, envp, 0, 0, 1) < 0)
+			return send_err(req,"CAD: could not execute UPFGEN!\n");
+
+		if (http_exec_process(CADGEN, argv_cadgen, envp, 0, 0, 1) < 0)
+			return send_err(req,"CAD: could not execute CADGEN!\n");
+	}
+	/*
+	 * Clear post log
+	 */
+	http_down(&postlog_sem);
+	init_postlog_file(req);
+	http_up(&postlog_sem);
+
+	/*
+	 * mtime has a 1 second resolution, sleep 1 second so that
+	 * the check for modified User.Personality and Custom.Ads
+	 * files notices multiple resets correctly.
+	 */
+	http_sleep(1);
+
+	req->bytes_sent = send_reply_head(req, 0);
+	req->bytes_sent += send_reply_tail(req);
+
+	return -2;
+}
+
+#define BLOCKLEN 512
+
+static int send_postlog (http_req_t *req)
+{
+	char buf [BLOCKLEN];
+	struct http_file *file;
+	int len, total, bytes;
+
+	file = http_open_file(POSTLOG, O_RDONLY);
+	if (http_file_error(file))
+		return send_err(req, "CAD: no POST-log file!\n");
+
+	req->body_len = http_file_size(file);
+	bytes = send_reply_head(req, req->body_len);
+	http_down(&postlog_sem);
+	total = 0;
+	do {
+		len = http_read_file(file, buf, BLOCKLEN);
+		if (len <= 0)
+			break;
+		bytes += http_send_client(req, buf, len, 0);
+		total += len;
+	} while (len == BLOCKLEN);
+
+	http_close_file(file);
+	http_up(&postlog_sem);
+	bytes += send_reply_tail(req);
+	req->bytes_sent = bytes;
+	req->keep_alive = 0;
+
+	return -2;
+}
+
+static int do_command (http_req_t *req, char *query)
+{
+	if (TOKEN_EQUAL(query, COMMAND_RESET))
+		return do_reset(req, query + sizeof(COMMAND_RESET)-1);
+	if (TOKEN_EQUAL(query, COMMAND_FETCH))
+		return send_postlog(req);
+	return send_err(req,"CAD: got invalid command!\n");
+}
+
+static CAD_t * parse_GET_cookies (http_req_t *req)
+{
+	int uid, last_ad;
+	CAD_t *CADp;
+	char *tmp;
+
+	if (!req->cookies_len) {
+		return NULL;
+	}
+
+	CADp = http_malloc(sizeof(CAD_t), ALLOC_REQ_PRIVATE);
+	CADp->reply_cookies_len = 0;
+	req->private = (void *) CADp;
+
+	tmp = req->cookies + sizeof("my_cookie=user_id=")-1;
+	uid = simple_strtoul(tmp, &tmp, 10) - 10000;
+
+	tmp += sizeof("&last_ad=")-1;
+	last_ad = simple_strtoul(tmp, &tmp, 10);
+
+	CADp->user_id = uid;
+	CADp->last_ad = last_ad;
+
+	return CADp;
+}
+
+static int do_POST (http_req_t *req)
+{
+	int dir = -1, class = -1, num = -1, client = -1;
+	char buf[400], *tmp;
+	char urlroot[100];
+	CAD_t *CADp;
+	char *curr;
+	int ret;
+
+	CADp = parse_GET_cookies(req);
+
+	curr = req->post_data;
+	if (!curr)
+		goto parse_error;
+
+#define POST_URLROOT "urlroot="
+#define POST_CLASS "class="
+#define POST_CLIENT "client="
+#define POST_DIR "dir="
+#define POST_NUM "num="
+
+	for (;;) {
+		switch (*curr) {
+			case 'u':
+				if (PARSE_STRING( POST_URLROOT, curr, urlroot))
+					continue;
+				goto parse_error;
+			case 'c':
+				if (PARSE_UINT( POST_CLASS, curr, class))
+					continue;
+				if (PARSE_UINT( POST_CLIENT, curr, client))
+					continue;
+				goto parse_error;
+			case 'd':
+				if (PARSE_UINT( POST_DIR, curr, dir))
+					continue;
+				goto parse_error;
+			case 'n':
+				if (PARSE_UINT( POST_NUM, curr, num))
+					continue;
+				goto parse_error;
+			case '&':
+				curr++;
+				continue;
+			case 0:
+				goto out;
+			default:
+				goto parse_error;
+		}
+		goto parse_error;
+	}
+out:
+	if (!CADp)
+		goto parse_error;
+	tmp = CADp->ad_filename;
+	tmp += sprintf(tmp, "%sdir%05d/class%d_%d", urlroot, dir, class, num);
+
+	/*
+	 * Aquire semaphore guaranteeing atomic operations
+	 * on the postlog file.
+	 */
+	http_down(&postlog_sem);
+	if (!post_file)
+		if (init_postlog_file(req))
+			return 0;
+
+	postlog_count++;
+	tmp = postlog_head;
+	tmp += sprintf(tmp, "%10d", postlog_count);
+	*tmp = '\n';
+
+	tmp = buf;
+	tmp += sprintf(tmp, "%10d %10ld %10d %5d %2d %2d %10d %-60.60s %10d %10d\n", postlog_count, http_time(), http_getpid(), dir, class, num, client, CADp->ad_filename, http_getpid(), CADp->user_id + 10000);
+
+	ret = http_write_file(post_file, buf, tmp-buf);
+	http_up(&postlog_sem);
+
+	if (ret != tmp-buf)
+		goto write_error;
+
+	CADp->reply_cookies_len = sprintf(CADp->reply_cookies,
+		"my_cookie=%u", 10000 + CADp->user_id);
+	return 0;
+
+parse_error:
+	return send_err(req,"CAD: error while parsing POST request!\n");
+
+write_error:
+	return send_err(req, "CAD: POST-log write error!\n");
+}
+
+static int query_CAD (http_req_t *req)
+{
+	urlo_t *urlo = NULL;
+	int ret = 0;
+	CAD_t *CADp;
+	int missed;
+
+
+	if (req->method == METHOD_POST) {
+		ret = do_POST(req);
+		if (ret)
+			return ret;
+		CADp = (CAD_t *)req->private;
+		req->objectname = CADp->ad_filename;
+		req->objectname_len = strlen(CADp->ad_filename);
+	} else {
+		char *tmp = req->query;
+
+		if (req->method != METHOD_GET)
+			goto url_error;
+		if (TOKEN_EQUAL(req->query, COMMAND_STRING)) {
+			tmp += sizeof(COMMAND_STRING)-1;
+			return do_command(req, tmp);
+		}
+		req->objectname = req->query;
+		req->objectname_len = req->query_len;
+		CADp = parse_GET_cookies(req);
+	}
+
+	missed = lookup_urlo(req, LOOKUP_ATOMIC);
+	if (req->userspace_module)
+		HTTP_BUG();
+	urlo = req->urlo;
+	if ((!missed && !urlo) || (urlo && urlo->tcapi))
+		goto url_error;
+	if (req->method == METHOD_GET) {
+		if (CADp) {
+			ret = custom_ad_rotate(req, CADp);
+			if (ret)
+				return ret;
+		}
+	}
+
+	if (missed || !urlo->csumc)
+		return http_miss_req(req);
+
+	return ret;
+
+url_error:
+	return send_err(req, "CAD: error while parsing CAD request!\n");
+}
+
+#define REPLY_HEAD_HEAD \
+	"HTTP/1.1 200 OK\r\n" \
+	"Content-Type: text/html\r\n" \
+	"Connection: Keep-Alive\r\n" \
+	"Content-Length: %d\r\n\r\n"
+
+#define REPLY_HEAD_HEAD_COOKIE \
+	"HTTP/1.1 200 OK\r\n" \
+	"Content-Type: text/html\r\n" \
+	"Connection: Keep-Alive\r\n" \
+	"Content-Length: %d\r\n" \
+	"Set-Cookie: %s\r\n" \
+	"\r\n"
+
+#define REPLY_HEAD_TAIL \
+	"<html>\n" \
+	"<head><title>SPECweb99 Dynamic GET & POST Test</title></head>\n"\
+	"<body>\n" \
+	"<p>SERVER_SOFTWARE = TUX 1.0\n" \
+	"<p>REMOTE_ADDR = %d.%d.%d.%d\n" \
+	"<p>SCRIPT_NAME = %s\n" \
+	"<p>QUERY_STRING = %s\n" \
+	"<pre>\n"
+
+#define REPLY_TAIL \
+	"\n</pre>\n" \
+	"</body></html>\n"
+
+static int inline send_reply_head (http_req_t *req, size_t body_size)
+{
+	char buf [1000];
+	char *tmp, *head, *tail;
+	CAD_t *CADp = (CAD_t *)req->private;
+	unsigned int host, head_len, tail_len, total_len;
+
+	host = http_client_addr(req);
+#define IP(x) (((unsigned char *)&host)[x])
+
+	tmp = tail = buf;
+	tmp += sprintf(tmp, REPLY_HEAD_TAIL, IP(0), IP(1), IP(2), IP(3),
+			req->tcapi->vfs_name, req->query);
+
+	tail_len = tmp-buf;
+
+	total_len = tail_len + sizeof(REPLY_TAIL)-1 + body_size;
+
+	head = tmp;
+	if (CADp && CADp->reply_cookies_len)
+		tmp += sprintf(tmp, REPLY_HEAD_HEAD_COOKIE, total_len, CADp->reply_cookies);
+	else
+		tmp += sprintf(tmp, REPLY_HEAD_HEAD, total_len);
+
+	head_len = tmp-head;
+	http_send_client(req, head, head_len, 0);
+	http_send_client(req, tail, tail_len, 0);
+	req->http_status = 200;
+
+	return tail_len;
+}
+
+static inline int send_reply_tail (http_req_t *req)
+{
+	int len = sizeof(REPLY_TAIL)-1;
+
+	http_send_client(req, REPLY_TAIL, len, 1);
+	return len;
+}
+
+
+/*
+ * Send a dynamicly generated buffer. (this is the typical
+ * CAD case) Every reply is generated dynamically based on
+ * the template and cookie values. The template is scanned
+ * for every send.
+ */
+static int send_reply_CAD (http_req_t *req)
+{
+	int bytes;
+	CAD_t *CADp = (CAD_t *)req->private;
+
+	req->body_len = req->urlo->body_len;
+
+	bytes = send_reply_head(req, req->body_len);
+	bytes += scan_send_file(req, CADp);
+	bytes += send_reply_tail(req);
+
+	return bytes;
+}
+
+/*
+ * Return SPECweb99 error message.
+ */
+static int send_err (http_req_t *req, char *message)
+{
+	CAD_t *CADp = req->private;
+	int len = strlen(message);
+	int bytes;
+
+	/*
+	 * Return a -1 Ad_id in the reply cookie.
+	 */
+	CADp->reply_cookies_len = sprintf(CADp->reply_cookies,
+			"found_cookie=Ad_id=-1&Ad_weight=0&Expired=1");
+
+	req->body_len = len;
+	bytes = send_reply_head(req, len);
+	http_send_client(req, message, len, 0);
+	bytes += len;
+	bytes += send_reply_tail(req);
+
+	req->bytes_sent = bytes;
+	return -2;
+}
+
+static tcapi_template_t CAD_tcapi = {
+	vfs_name: "d",
+	version: HTTP_VERSION,
+	query: query_CAD,
+	send_reply: send_reply_CAD,
+};
+
+static int CAD_start (void)
+{
+	CAD_tcapi.mod = THIS_MODULE;
+
+	return register_httpmodule(&CAD_tcapi);
+}
+
+void CAD_stop (void)
+{
+	unregister_httpmodule(&CAD_tcapi);
+}
+
+module_init(CAD_start)
+module_exit(CAD_stop)
+
--- linux/net/http/TODO.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/TODO	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,4 @@
+- If-Modified-Since HTTP request header
+- Last-Modified reply HTTP header
+- byte ranges?
+- add and stabilize the virtual hosting patches
--- linux/net/http/httpmod.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/httpmod.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,125 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * httpmod.c: loading/registering of HTTP dynamic modules
+ */
+
+#include <net/http.h>
+#include <linux/kmod.h>
+
+spinlock_t httpmodules_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(httpmodules_list);
+
+static tcapi_template_t * lookup_module (const char *vfs_name)
+{
+	tcapi_template_t *tcapi;
+	struct list_head *head, *curr, *next;
+
+	Dprintk("looking up HTTP module {%s}.\n", vfs_name);
+	head = &httpmodules_list;
+	next = head->next;
+
+	while ((curr = next) != head) {
+		tcapi = list_entry(curr, tcapi_template_t, modules);
+		next = curr->next;
+		Dprintk("checking module {%s} == {%s}?\n", vfs_name, tcapi->vfs_name);
+		if (!strcmp(tcapi->vfs_name, vfs_name))
+			return tcapi;
+	}
+	return NULL;
+}
+
+/*
+ * Attempt to load a HTTP application module.
+ * This is the slow path, we cache ('link') the module's
+ * API vector to the inode.
+ * The module loading path is serialized, and we handshake
+ * with the loaded module and fetch it's API vector.
+ */
+int load_httpmodule (urlobj_t *urlo, const char *filename)
+{
+	tcapi_template_t *tcapi;
+	int err = 0;
+
+	spin_lock(&httpmodules_lock);
+	if (urlo->tcapi)
+		goto out;
+	tcapi = lookup_module(filename);
+	if (!tcapi) {
+		printk("did not find module vfs:{%s}\n", filename);
+		err = -1;
+	}
+	urlo->tcapi = tcapi;
+out:
+	spin_unlock(&httpmodules_lock);
+	return err;
+}
+
+
+int register_httpmodule (tcapi_template_t *tcapi)
+{
+	int ret = -EEXIST;
+
+	spin_lock(&httpmodules_lock);
+
+	if (lookup_module(tcapi->vfs_name)) {
+		printk("module with VFS binding '%s' already registered!\n",
+						 tcapi->vfs_name);
+		goto out;
+	} 
+
+	list_add(&tcapi->modules, &httpmodules_list);
+	ret = 0;
+	Dprintk("HTTP module %s registered.\n", tcapi->vfs_name);
+out:
+	spin_unlock(&httpmodules_lock);
+
+	return ret;
+}
+
+int unregister_httpmodule (tcapi_template_t *tcapi)
+{
+	tcapi_template_t *tmp;
+	int err = 0;
+
+	spin_lock(&httpmodules_lock);
+	tmp = lookup_module(tcapi->vfs_name);
+	if (!tcapi) {
+		Dprintk("huh, module %s not registered??\n", tcapi->vfs_name);
+		err = -1;
+	} else {
+		list_del(&tcapi->modules);
+		Dprintk("HTTP module %s unregistered.\n", tcapi->vfs_name);
+	}
+	tmp = lookup_module(tcapi->vfs_name);
+	if (tmp)
+		Dprintk("huh, module %s still registered??\n", tcapi->vfs_name);
+	spin_unlock(&httpmodules_lock);
+
+	return err;
+}
+
+#if CONFIG_IO_TRACE
+#endif
+EXPORT_SYMBOL(lookup_urlo);
+EXPORT_SYMBOL(register_httpmodule);
+EXPORT_SYMBOL(unregister_httpmodule);
+EXPORT_SYMBOL(http_open_file);
+EXPORT_SYMBOL(send_dynamic_reply);
+EXPORT_SYMBOL(errno);
+EXPORT_SYMBOL(docroot);
+EXPORT_SYMBOL(http_docroot);
+EXPORT_SYMBOL(queue_output_req);
+EXPORT_SYMBOL(http_miss_req);
+EXPORT_SYMBOL(do_pipe);
+EXPORT_SYMBOL(http_kmalloc);
+EXPORT_SYMBOL(sys_read);
+EXPORT_SYMBOL(reap_kids);
+EXPORT_SYMBOL(free_uid);
+EXPORT_SYMBOL(flush_signal_handlers);
+EXPORT_SYMBOL(http_send_object);
+EXPORT_SYMBOL(http_Dprintk);
+EXPORT_SYMBOL(http_exec_process);
+
--- linux/net/http/cgi.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/cgi.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,140 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * cgi.c: user-space CGI (and other) code execution.
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <net/http.h>
+
+static int exec_usermode(char *program_path, char *argv[], char *envp[])
+{
+	int i;
+
+Dprintk("exec_usermode #0\n");
+	current->session = -1;
+	current->pgrp = -1;
+
+	spin_lock_irq(&current->sigmask_lock);
+	flush_signals(current);
+	flush_signal_handlers(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+Dprintk("exec_usermode #1\n");
+	for (i = 2; i < current->files->max_fds; i++ ) {
+		if (current->files->fd[i]) close(i);
+	}
+
+	free_uid(current->user);
+
+Dprintk("exec_usermode #2\n");
+	/* Give the new process no privileges.. */
+	current->uid = current->euid = current->fsuid = -1;
+	cap_clear(current->cap_permitted);
+	cap_clear(current->cap_inheritable);
+	cap_clear(current->cap_effective);
+
+	/* Allow execve args to be in kernel space. */
+	set_fs(KERNEL_DS);
+
+Dprintk("exec_usermode #3\n");
+	if (execve(program_path, argv, envp) < 0) {
+Dprintk("exec_usermode #4\n");
+		return -errno;
+	}
+Dprintk("exec_usermode #5\n");
+	return 0;
+}
+
+static int exec_helper (void * data)
+{
+	exec_param_t *param = data;
+	char **tmp;
+	int ret;
+
+	current->flags &= ~PF_ATOMICALLOC;
+	current->http--;
+	sprintf(current->comm,"doexec - %i", current->pid);
+
+	if (!param)
+		HTTP_BUG();
+	Dprintk("doing exec(%s).\n", param->command);
+
+	Dprintk("argv: ");
+	tmp = param->argv;
+	while (*tmp) {
+		Dprintk("{%s} ", *tmp);
+		tmp++;
+	}
+	Dprintk("\n");
+	Dprintk("envp: ");
+	tmp = param->envp;
+	while (*tmp) {
+		Dprintk("{%s} ", *tmp);
+		tmp++;
+	}
+	Dprintk("\n");
+	/*
+	 * set up the socket as stdin and stdout of the external
+	 * CGI application.
+	 */
+	if (param->pipe_fds) {
+		// do not close on exec.
+		sys_fcntl(0, F_SETFD, 0);
+		sys_fcntl(1, F_SETFD, 0);
+	}
+
+	ret = exec_usermode(param->command, param->argv, param->envp);
+	if (ret < 0)
+		Dprintk("bug: exec() returned %d.\n", ret);
+	else
+		Dprintk("exec()-ed successfully!\n");
+	return 0;
+}
+
+int http_exec_process (char *command, char **argv,
+			char **envp, int *pipe_fds,
+				exec_param_t *param, int wait)
+{
+	exec_param_t param_local;
+	pid_t pid;
+	int ret = 0;
+	struct k_sigaction *ka;
+
+	ka = current->sig->action + SIGCHLD-1;
+	ka->sa.sa_handler = SIG_IGN;
+
+	if (!param && wait)
+		param = &param_local;
+
+	param->command = command;
+	param->argv = argv;
+	param->envp = envp;
+	param->pipe_fds = pipe_fds;
+
+repeat_fork:
+	pid = kernel_thread(exec_helper, (void*) param, CLONE_SIGHAND|SIGCHLD);
+	Dprintk("kernel thread created PID %d.\n", pid);
+	if (pid < 0) {
+		printk("couldnt create new kernel thread due to %d... retrying.\n", pid);
+		current->http--;
+		schedule_timeout(1);
+		current->http++;
+		goto repeat_fork;
+	}
+	if (wait) {
+		current->http--;
+repeat:
+		ret = waitpid(pid, NULL, __WALL);
+		Dprintk("waitpid returned %d.\n", ret);
+		if (ret == -ERESTARTSYS) {
+			reap_kids();
+			goto repeat;
+		}
+		current->http++;
+	}
+	return ret;
+}
--- linux/net/http/httpmain.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/httpmain.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,873 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * httpmain.c: main management and initialization routines
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <net/http.h>
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+
+/*
+ * Threads information.
+ */
+static int nr_threads;
+static atomic_t nr_threads_running = ATOMIC_INIT(0);
+
+threadinfo_t threadinfo[CONFIG_HTTP_NUMTHREADS];
+
+struct nameidata docroot;
+
+void flush_all_signals (void)
+{
+	spin_lock_irq(&current->sigmask_lock);
+	flush_signals(current);
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+}
+
+static int work_pending (threadinfo_t *ti)
+{
+	int j;
+
+	if (!list_empty(&ti->input_pending) ||
+		!list_empty(&ti->userspace_pending) ||
+		!list_empty(&ti->output_pending) ||
+		!list_empty(&ti->redirect_pending) ||
+		!list_empty(&ti->finish_pending))
+			return 1;
+
+	for (j = 0; j < CONFIG_HTTP_NUMSOCKETS; j++) {
+		if (!ti->listen[j])
+			break;
+		if (ti->listen[j]->sk->tp_pinfo.af_tcp.accept_queue)
+			return 1;
+	}
+	return 0;
+}
+
+void reap_kids (void)
+{
+	int count = 0;
+
+	__set_task_state(current, TASK_RUNNING);
+//	flush_all_signals();
+	while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
+		count++;
+
+	Dprintk("reaped %d kids (%p).\n", count, __builtin_return_address(0));
+}
+
+static int event_loop (threadinfo_t *ti)
+{
+	int work_done;
+
+repeat:
+	if (ti->thread != current)
+		HTTP_BUG();
+	work_done = 0;
+
+	/*
+	 * Any (relevant) event on the socket will change this
+	 * thread to TASK_RUNNING because we add it to both
+	 * the main listening and the connection request socket
+	 * waitqueues. Thus we can do 'lazy checking' of work
+	 * to be done and schedule away only if the thread is
+	 * still TASK_INTERRUPTIBLE. This makes TUX fully
+	 * event driven.
+	 */
+	__set_task_state(current, TASK_INTERRUPTIBLE);
+
+	work_done += accept_requests(ti);
+	if (ti->userspace_req)
+		HTTP_BUG();
+	if (!list_empty(&ti->input_pending))
+		work_done += read_headers(ti);
+	if (ti->userspace_req)
+		HTTP_BUG();
+	if (!list_empty(&ti->userspace_pending)) {
+		http_req_t *req;
+
+		req = pick_userspace_req(ti);
+		if (!req)
+			HTTP_BUG();
+		if (!req->tcapi)
+			BUG();
+		ti->userspace_req = req;
+		if (req->ti != ti)
+			HTTP_BUG();
+		goto handle_userspace_req;
+	}
+	if (!list_empty(&ti->output_pending))
+		work_done += send_replies(ti);
+	if (ti->userspace_req)
+		HTTP_BUG();
+	if (!list_empty(&ti->redirect_pending))
+		work_done += redirect_requests(ti);
+	if (ti->userspace_req)
+		HTTP_BUG();
+	if (!list_empty(&ti->finish_pending))
+		work_done += finish_requests(ti);
+	if (ti->userspace_req)
+		HTTP_BUG();
+
+	if (http_stop)
+		return HTTP_RETURN_EXIT;
+	/*
+	 * Catch possible SIGCHLDs coming from external CGI
+	 * processes.
+	 */
+	if (signal_pending(current)) {
+		reap_kids();
+		work_done = 1;
+	}
+	/*
+	 * Any signals left?
+	 */
+	if (signal_pending(current))
+		goto handle_signal;
+
+	/*
+	 * Any socket event either on the listen socket
+	 * or on the request sockets will wake us up:
+	 */
+	if (!work_done && (current->state != TASK_RUNNING) &&
+							!work_pending(ti)) {
+		Dprintk("fast thread: no work to be done, sleeping.\n");
+//		up(ti->used);
+		schedule();
+//		down(ti->used);
+		Dprintk("fast thread: back from sleep!\n");
+	}
+
+	/*
+	 * Be nice to other processes:
+	 */
+	if (!current->need_resched)
+		goto repeat;
+
+	__set_task_state(current, TASK_RUNNING);
+	current->policy |= SCHED_YIELD;
+	schedule();
+	goto repeat;
+handle_userspace_req:
+	__set_task_state(current, TASK_RUNNING);
+	return HTTP_RETURN_USERSPACE_REQUEST;
+handle_signal:
+	__set_task_state(current, TASK_RUNNING);
+	return HTTP_RETURN_SIGNAL;
+}
+
+static int init_queues (int nr_threads)
+{
+	int i;
+
+	for (i = 0; i < nr_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+
+		ti->output_buffer = (char*) __get_free_pages(GFP_USER,
+							OUT_BUF_ORDER);
+		if (!ti->output_buffer)
+			HTTP_BUG();
+
+		INIT_LIST_HEAD(&ti->all_requests);
+		ti->free_requests_lock = SPIN_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&ti->free_requests);
+		ti->input_lock = SPIN_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&ti->input_pending);
+		ti->userspace_lock = SPIN_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&ti->userspace_pending);
+		ti->output_lock = SPIN_LOCK_UNLOCKED;
+		INIT_LIST_HEAD(&ti->output_pending);
+		INIT_LIST_HEAD(&ti->redirect_pending);
+		INIT_LIST_HEAD(&ti->finish_pending);
+	}
+	return 0;
+}
+
+static int initialized = 0;
+
+static int user_req_startup (void)
+{
+	int i;
+
+	if (initialized)
+		return -EINVAL;
+	initialized = 1;
+
+	/*
+	 * Look up document root:
+	 */
+	if (docroot.mnt)
+		HTTP_BUG();
+	docroot.mnt = mntget(current->fs->rootmnt);
+	docroot.dentry = dget(current->fs->root);
+	docroot.last.len = 0;
+	docroot.flags = LOOKUP_FOLLOW|LOOKUP_POSITIVE;
+
+	if (path_walk(http_docroot, &docroot)) {
+		docroot.mnt = NULL;
+		initialized = 0;
+		return -EINVAL;
+	}
+
+	/*
+	 * Start up the logger thread. (which opens the logfile)
+	 */
+	init_log_thread();
+
+	nr_threads = http_threads;
+	if (nr_threads < 1) 
+		nr_threads = 1;
+	if (nr_threads > CONFIG_HTTP_NUMTHREADS) 
+		nr_threads = CONFIG_HTTP_NUMTHREADS;
+	http_threads = nr_threads;
+
+	/*
+	 * Set up per-thread work-queues:
+	 */
+	memset(threadinfo, 0, CONFIG_HTTP_NUMTHREADS*sizeof(threadinfo_t));
+	init_queues(nr_threads);
+
+	/*
+	 * Prepare the worker thread structures.
+	 */
+	for (i = 0; i < nr_threads; i++) {
+		threadinfo_t *ti = threadinfo + i;
+		ti->cpu = i;
+		init_MUTEX(&ti->used);
+	}
+
+	return 0;
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(wait_stop);
+
+static int user_req_shutdown (void)
+{
+	int err = -EINVAL;
+
+	lock_kernel();
+	if (!initialized)
+		goto err;
+	/*
+	 * Wake up all the worker threads so they notice
+	 * that we are being stopped.
+	 */
+	wake_up(&wait_stop);
+
+	if (atomic_read(&nr_threads_running) > 0)
+		goto err;
+	initialized = 0;
+	if (nr_async_io_pending())
+		HTTP_BUG();
+	stop_log_thread();
+	mntput(docroot.mnt);
+	docroot.mnt = NULL;
+	dput(docroot.dentry);
+	docroot.dentry = NULL;
+	http_stop = 0;
+	err = 0;
+
+err:
+	unlock_kernel();
+	return err;
+}
+
+static int user_req_start_thread (threadinfo_t *ti)
+{
+	unsigned int mask, i, j, k, cpu;
+	struct k_sigaction *ka;
+
+	cpu = ti->cpu;
+	mask = 1 << cpu;
+#if CONFIG_SMP
+	if (cpu_online_map & mask)
+		 current->cpus_allowed = mask;
+#endif
+	printk("setting TUX - %d 's cpus_allowed mask to %08lx\n",
+						cpu, current->cpus_allowed);
+	ti->thread = current;
+	current->http = 1;
+	current->flags |= PF_ATOMICALLOC;
+
+	init_waitqueue_entry(&ti->stop, current);
+	for (j = 0; j < CONFIG_HTTP_NUMSOCKETS; j++)
+		init_waitqueue_entry(ti->wait_event + j, current);
+
+	ka = current->sig->action + SIGCHLD-1;
+	ka->sa.sa_handler = SIG_IGN;
+
+	/* Block all signals except SIGKILL, SIGSTOP, SIGHUP and SIGCHLD */
+	spin_lock_irq(&current->sigmask_lock);
+	siginitsetinv(&current->blocked, sigmask(SIGKILL) |
+			sigmask(SIGSTOP)| sigmask(SIGHUP) | sigmask(SIGCHLD));
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	for (k = 0; k < CONFIG_HTTP_NUMSOCKETS; k++) {
+		if (http_listen[cpu][k] == -1)
+			break;
+		for (i = 0; i < cpu; i++) {
+			for (j = 0; j < CONFIG_HTTP_NUMSOCKETS; j++) {
+				if (http_listen[i][j] == -1)
+					break;
+				if (http_listen[i][j] == http_listen[cpu][k]) {
+					while (!threadinfo[i].listen[j]) {
+						current->policy |= SCHED_YIELD;
+						schedule();
+					}
+					ti->listen[k] = threadinfo[i].listen[j];
+					ti->listen_cloned[k] = 1;
+					goto out;
+				}
+			}
+		}
+		ti->listen[k] = start_listening(http_serverport,
+							http_listen[cpu][k]);
+		if (!ti->listen[k])
+			goto error;
+		ti->listen_cloned[k] = 0;
+	}
+out:
+	if (!ti->listen[0])
+		HTTP_BUG();
+
+	add_wait_queue(&wait_stop, &ti->stop);
+	for (j = 0; j < CONFIG_HTTP_NUMSOCKETS; j++)
+		if (ti->listen[j])
+			add_wait_queue_exclusive(ti->listen[j]->sk->sleep,
+				ti->wait_event + j);
+	ti->started = 1;
+	atomic_inc(&nr_threads_running);
+	return 0;
+
+error:	
+	flush_inputqueue(ti);
+	flush_userspacequeue(ti);
+	flush_outputqueue(ti);
+	flush_redirectqueue(ti);
+	flush_logqueue(ti);
+
+	printk(KERN_NOTICE "HTTP: could not start worker thread %i.\n", ti->cpu);
+
+	return -EINVAL;
+}
+
+static void flush_idleinput (threadinfo_t * ti)
+{
+	struct list_head *head, *tmp;
+	http_req_t *req;
+
+	head = &ti->all_requests;
+	tmp = head->next;
+
+	while (tmp != head) {
+		req = list_entry(tmp, http_req_t, all);
+		tmp = tmp->next;
+		if (test_bit(0, &req->idle_input))
+			idle_event(req);
+	}
+}
+
+static void flush_all_requests (threadinfo_t *ti)
+{
+	for (;;) {
+		int n1, n2;
+
+		n1 = ti->nr_requests;
+		__set_task_state(current, TASK_RUNNING);
+		flush_logqueue(ti);
+		__set_task_state(current, TASK_INTERRUPTIBLE);
+
+		flush_idleinput(ti);
+		flush_inputqueue(ti);
+		flush_userspacequeue(ti);
+		flush_outputqueue(ti);
+		flush_redirectqueue(ti);
+		flush_freequeue(ti);
+		if (!ti->nr_requests)
+			break;
+		n2 = ti->nr_requests;
+		if (n1 != n2)
+			continue;
+		schedule();
+	}
+}
+
+static int user_req_stop_thread (threadinfo_t *ti)
+{
+	int j;
+
+	printk(KERN_NOTICE "HTTP fast thread %d: stopping\n", threadinfo-ti);
+	printk("worker: event #1.\n");
+
+	if (!ti->started)
+		HTTP_BUG();
+	for (j = 0; j < CONFIG_HTTP_NUMSOCKETS; j++)
+		if (ti->listen[j])
+			remove_wait_queue(ti->listen[j]->sk->sleep,
+				ti->wait_event + j);
+	remove_wait_queue(&wait_stop, &ti->stop);
+
+	for (j = 0; j < CONFIG_HTTP_NUMSOCKETS; j++) {
+		if (!ti->listen[j])
+			break;
+		if (!ti->listen_cloned[j]) {
+			while (waitqueue_active(ti->listen[j]->sk->sleep)) {
+				current->policy |= SCHED_YIELD;
+				schedule();
+			}
+			printk("worker: event #2.\n");
+			stop_listening(&ti->listen[j]);
+		}
+	}
+
+	flush_all_requests(ti);
+
+	if (ti->nr_requests)
+		HTTP_BUG();
+	ti->started = 0;
+	wmb();
+
+	printk(KERN_NOTICE "HTTP: worker thread %i stopped.\n", ti->cpu);
+
+	ti->thread = NULL;
+	current->http_info = NULL;
+	atomic_dec(&nr_threads_running);
+
+	return 0;
+}
+
+static int prepare_userspace_req (threadinfo_t *ti, user_req_t *u_info)
+{
+	http_req_t *req = ti->userspace_req;
+	unsigned int tmp;
+	int fd;
+
+	Dprintk("prepare_userspace_req(%p).\n", req);
+	if (!req)
+		HTTP_BUG();
+
+	if (req->userspace_fd == -1) {
+		fd = sock_map_fd(req->sock);
+		Dprintk("sock_map_fd(%p) :%d.\n", req, fd);
+		if (fd < 0)
+			HTTP_BUG();
+		req->userspace_fd = fd;
+	//	igrab((frip(current->files, fd))->f_dentry->d_inode);
+	} else
+		fd = req->userspace_fd;
+
+#define return_EFAULT do { Dprintk("-EFAULT at %d:%s.\n", __LINE__, __FILE__); return -EFAULT; } while (0)
+
+	if (copy_to_user(&u_info->sock, &fd, sizeof(fd)))
+		return_EFAULT;
+	if (!req->tcapi)
+		HTTP_BUG();
+	if (copy_to_user(&u_info->module_index,
+				&req->tcapi->userspace_id, sizeof(int)))
+		return_EFAULT;
+	if (req->query) {
+		if (copy_to_user(&u_info->query, req->query, req->query_len + 1))
+			return_EFAULT;
+	} else {
+		/*
+		 * Put a null-string into the user-space query string.
+		 */
+		char null = 0;
+
+		if (copy_to_user(&u_info->query, &null, 1))
+			return_EFAULT;
+	}
+	if (copy_to_user(&u_info->event, &req->event, sizeof(req->event)))
+		return_EFAULT;
+	{
+		unsigned int filelen;
+
+		if (req->urlo)
+			filelen = req->urlo->filelen;
+		else
+			filelen = -1;
+		if (copy_to_user(&u_info->objectlen, &filelen, sizeof(req->event)))
+			return_EFAULT;
+	}
+	if (copy_to_user(&u_info->http_version, &req->version, sizeof(req->version)))
+		return_EFAULT;
+	if (copy_to_user(&u_info->http_method, &req->method, sizeof(req->method)))
+		return_EFAULT;
+	if (copy_to_user(&u_info->cookies_len, &req->cookies_len, sizeof(req->cookies_len)))
+		return_EFAULT;
+	if (req->cookies_len)
+		if (copy_to_user(&u_info->cookies, req->cookies, req->cookies_len+1))
+			return_EFAULT;
+	if (copy_to_user(&u_info->id, &req, sizeof(req)))
+		return_EFAULT;
+	if (copy_to_user(&u_info->private, &req->private, sizeof(req->private)))
+		return_EFAULT;
+	if (copy_to_user(&u_info->bytes_sent, &req->bytes_sent, sizeof(int)))
+		return_EFAULT;
+	if ((req->method == METHOD_POST) && req->post_data)
+		if (copy_to_user(&u_info->post_data, req->post_data, req->post_data_len+1))
+			return_EFAULT;
+	tmp = http_client_addr(req);
+	if (copy_to_user(&u_info->client_host, &tmp, sizeof(req->version)))
+		return_EFAULT;
+	return HTTP_RETURN_USERSPACE_REQUEST;
+}
+
+static int user_register_module (user_req_t *u_info)
+{
+	int ret = -EINVAL;
+	tcapi_template_t *tcapi;
+	char modulename [MAX_MODULENAME_LEN+1];
+	int idx, len;
+
+	Dprintk("register user-module, %p.\n", u_info);
+	ret = strncpy_from_user(modulename, u_info->modulename,
+					MAX_MODULENAME_LEN);
+	if (ret <= 0)
+		goto out;
+	Dprintk("... user-module is: {%s}.\n", modulename);
+	len = strlen(modulename);
+	if (!len || (len > MAX_MODULENAME_LEN))
+		HTTP_BUG();
+	Dprintk("... user-module len is: %d.\n", len);
+
+	ret = copy_from_user(&idx, &u_info->module_index, sizeof(int));
+	if (ret || !idx)
+		goto out;
+	Dprintk("... user-module index is: %d.\n", idx);
+
+	ret = -ENOMEM;
+	tcapi = (tcapi_template_t *) kmalloc(sizeof(*tcapi), GFP_KERNEL);
+	if (!tcapi)
+		goto out;
+	memset(tcapi, 0, sizeof(*tcapi));
+
+	tcapi->vfs_name = (char *) kmalloc(len, GFP_KERNEL);
+	if (!tcapi->vfs_name) {
+		kfree(tcapi);
+		goto out;
+	}
+	strcpy(tcapi->vfs_name, modulename);
+	tcapi->userspace_id = idx;
+
+	Dprintk("... registering module {%s}.\n", tcapi->vfs_name);
+	ret = register_httpmodule(tcapi);
+out:
+	return ret;
+}
+
+static int user_unregister_module (user_req_t *u_info)
+{
+	// FIXME: not here yet.
+	return -EINVAL;
+}
+
+asmlinkage int sys_http (unsigned int action, user_req_t *u_info)
+{
+	int ret;
+	threadinfo_t *ti;
+	http_req_t *req;
+
+	Dprintk("got sys_http(%d, %p).\n", action, u_info);
+
+	if (action >= MAX_HTTP_ACTION)
+		goto err_no_unlock;
+
+	ti = (threadinfo_t *) current->http_info;
+	if (ti)
+		if (ti->thread != current)
+			HTTP_BUG();
+
+	switch (action) {
+		case HTTP_ACTION_STARTUP:
+			lock_kernel();
+			ret = user_req_startup();
+			unlock_kernel();
+			goto out_no_unlock;
+
+		case HTTP_ACTION_SHUTDOWN:
+			lock_kernel();
+			ret = user_req_shutdown();
+			unlock_kernel();
+			goto out_no_unlock;
+
+		case HTTP_ACTION_REGISTER_MODULE:
+			ret = user_register_module(u_info);
+			goto out_no_unlock;
+
+		case HTTP_ACTION_UNREGISTER_MODULE:
+			ret = user_unregister_module(u_info);
+			goto out_no_unlock;
+
+		case HTTP_ACTION_STARTTHREAD:
+		{
+			int nr;
+
+			ret = copy_from_user(&nr, &u_info->thread_nr,
+						sizeof(int));
+			if (ret)
+				goto err_no_unlock;
+			if (nr >= nr_threads)
+				goto err_no_unlock;
+			ti = threadinfo + nr;
+			down(&ti->used);
+			if (ti->started)
+				goto err_unlock;
+			current->http_info = ti;
+			if (ti->thread)
+				HTTP_BUG();
+			lock_kernel();
+			ret = user_req_start_thread(ti);
+			unlock_kernel();
+			if (ret)
+				current->http_info = NULL;
+			else {
+				if (ti->thread != current)
+					HTTP_BUG();
+			}
+			goto out_unlock;
+		}
+
+		case HTTP_ACTION_STOPTHREAD:
+			if (!ti)
+				goto err_no_unlock;
+			down(&ti->used);
+			if (!ti->started)
+				goto err_unlock;
+			req = ti->userspace_req;
+			if (req) {
+				ti->userspace_req = NULL;
+				req->userspace_module = 0;
+				req->private = NULL;
+				DEC_STAT(nr_userspace_pending);
+				flush_request(req, ti);
+			}
+			
+			lock_kernel();
+			ret = user_req_stop_thread(ti);
+			unlock_kernel();
+			goto out_unlock;
+
+		case HTTP_ACTION_CURRENT_DATE:
+			ret = strncpy_from_user(tux_date, u_info->new_date,
+				DATE_LEN);
+			if (ret <= 0)
+				goto err_no_unlock;
+			goto out_no_unlock;
+			
+		default:
+	}
+
+	if (!ti)
+		goto err_no_unlock;
+	down(&ti->used);
+
+	if (!ti->started)
+		goto err_unlock;
+
+	req = ti->userspace_req;
+	if (!req) {
+		if (action == HTTP_ACTION_EVENTLOOP)
+			goto eventloop;
+		goto err_unlock;
+	}
+	if (!req->userspace_module)
+		HTTP_BUG();
+
+	ret = copy_from_user(&req->event, &u_info->event, sizeof(int));
+	if (ret)
+		goto out_unlock;
+	ret = copy_from_user(&req->http_status, &u_info->http_status, sizeof(int));
+	if (ret)
+		goto out_unlock;
+	ret = copy_from_user(&req->bytes_sent, &u_info->bytes_sent, sizeof(int));
+	if (ret)
+		goto out_unlock;
+	ret = copy_from_user(&req->private, &u_info->private, sizeof(req->private));
+	if (ret)
+		goto out_unlock;
+
+	switch (action) {
+
+		case HTTP_ACTION_EVENTLOOP:
+eventloop:
+			req = ti->userspace_req;
+			if (req) {
+				ti->userspace_req = NULL;
+				req->userspace_module = 0;
+				req->private = NULL;
+				DEC_STAT(nr_userspace_pending);
+				flush_request(req, ti);
+			}
+			ret = event_loop(ti);
+			goto out_unlock;
+
+		case HTTP_ACTION_FINISH_REQ:
+
+			ti->userspace_req = NULL;
+			req->userspace_module = 0;
+			req->private = NULL;
+			DEC_STAT(nr_userspace_pending);
+			flush_request(req, ti);
+			goto eventloop;
+			break;
+
+		case HTTP_ACTION_GET_OBJECT:
+		{
+			int missed;
+			urlobj_t *urlo;
+
+			check_req_list(req, NULL);
+			req->tmpbuf[MAX_URI_LEN-1] = 0;
+			req->objectname = req->tmpbuf;
+			ret = strncpy_from_user(req->objectname,
+				u_info->objectname, MAX_URI_LEN-1);
+			if (ret <= 0) {
+				req->objectname = NULL;
+				req->objectname_len = 0;
+				goto out_unlock;
+			}
+			req->objectname[ret] = 0; // string delimit
+			req->objectname_len = ret;
+
+			req->userspace_module = 0;
+			missed = lookup_urlo(req, LOOKUP_ATOMIC);
+			if (req->userspace_module)
+				HTTP_BUG();
+			req->userspace_module = 1;
+			urlo = req->urlo;
+			if ((!missed && !urlo) || (urlo && urlo->tcapi))
+				goto err_unlock;
+			if (missed || !urlo->csumc) {
+				ti->userspace_req = NULL;
+				DEC_STAT(nr_userspace_pending);
+				http_miss_req(req);
+				goto eventloop;
+			}
+			ret = HTTP_RETURN_USERSPACE_REQUEST;
+			break;
+		}
+
+		case HTTP_ACTION_READ_OBJECT:
+		{
+			char *addr;
+
+			if (!req->urlo)
+				goto err_unlock;
+			if (!req->urlo->csumc)
+				goto err_unlock;
+			
+			ret = copy_from_user(&addr, &u_info->object_addr,
+					sizeof(addr));
+			if (ret)
+				goto out_unlock;
+			http_read(req->urlo, addr);
+			ret = HTTP_RETURN_USERSPACE_REQUEST;
+			break;
+		}
+
+		case HTTP_ACTION_SEND_OBJECT:
+			if (!req->urlo)
+				goto err_unlock;
+			if (!req->urlo->csumc)
+				goto err_unlock;
+			req->bytes_sent += http_send_object(req, 0, 1);
+			ret = HTTP_RETURN_USERSPACE_REQUEST;
+			break;
+
+		default:
+			HTTP_BUG();
+	}
+
+out_unlock:
+	if (ti->userspace_req)
+		ret = prepare_userspace_req(ti, u_info);
+	up(&ti->used);
+out_no_unlock:
+	Dprintk("sys_http(%d, %p) returning %d.\n", action, u_info, ret);
+	return ret;
+err_unlock:
+	up(&ti->used);
+err_no_unlock:
+	Dprintk("sys_http(%d, %p) returning -EINVAL!\n", action, u_info);
+	return -EINVAL;
+}
+
+/*
+ * This gets called if a TUX thread does an exit().
+ */
+void http_exit (void)
+{
+	sys_http(HTTP_ACTION_STOPTHREAD, NULL);
+}
+
+int __init http_init(void)
+{
+	start_sysctl();
+	init_cachemiss_threads();
+
+	return 0;
+}
+
+void http_cleanup (void)
+{
+	end_sysctl();
+}
+
+int init_module (void)
+{
+	return http_init();
+}
+
+int cleanup_module (void)
+{
+	http_cleanup();
+	return 0;
+}
+
+#define CHECK_LIST(l) \
+	if ((list != (l)) && !list_empty(l)) HTTP_BUG();
+#define CHECK_LIST2(l) \
+	if ((list == (l)) && list_empty(l)) HTTP_BUG();
+
+void __check_req_list (http_req_t *req, struct list_head *list)
+{
+	if (req->magic != HTTP_MAGIC)
+		HTTP_BUG();
+	if (!req->ti)
+		HTTP_BUG();
+	if (current->http_info && !in_interrupt()) {
+		threadinfo_t *ti = (threadinfo_t *) current->http_info;
+
+		if (ti != req->ti) {
+			printk("HTTP BUG: ti (%p,%d) != req->ti (%p,%d)!\n",
+				ti, ti-threadinfo, req->ti, req->ti-threadinfo);
+			HTTP_BUG();
+		}
+		if (ti->thread != current)
+			HTTP_BUG();
+	}
+	CHECK_LIST(&req->free);
+	CHECK_LIST2(&req->free);
+	CHECK_LIST(&req->input);
+	CHECK_LIST2(&req->input);
+	CHECK_LIST(&req->userspace);
+	CHECK_LIST2(&req->userspace);
+	CHECK_LIST(&req->cachemiss);
+	CHECK_LIST2(&req->cachemiss);
+	CHECK_LIST(&req->output);
+	CHECK_LIST2(&req->output);
+	CHECK_LIST(&req->redirect);
+	CHECK_LIST2(&req->redirect);
+	CHECK_LIST(&req->finish);
+	CHECK_LIST2(&req->finish);
+}
+
--- linux/net/http/extcgi.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/extcgi.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,379 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * extcgi.c: dynamic HTTP module which forks and starts an external CGI
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <net/http.h>
+#include "parser.h"
+
+//#undef Dprintk
+//#define Dprintk(x...) do { if (http_Dprintk) { printk("<%p>: ", req); printk(x); } } while (0)
+
+#define MAX_ENVLEN 1000
+#define NR_CGI_METAVARIABLES 13
+
+#define MAX_CGI_DATA 2048
+
+typedef struct CGI_reply_s {
+	char buf[MAX_CGI_DATA+1024];
+	char sendfile_name[128];
+	int len;
+	int sendfile_pos;
+	int error;
+} CGI_reply_t;
+
+#if 0
+#define PRINT_MESSAGE_LEFT \
+	Dprintk("CGI message left at %s:%d:\n--->{%s}<---\n", \
+		__FILE__, __LINE__, curr)
+#else
+#define PRINT_MESSAGE_LEFT do {} while(0)
+#endif
+
+#define GOTO_INCOMPLETE do { Dprintk("invalid CGI reply at %s:%d.\n", __FILE__, __LINE__); goto invalid; } while (0)
+
+int parse_cgi_headers (char *cgi_message, int len, http_req_t *req)
+{
+	urlobj_t *urlo;
+	CGI_reply_t *CGI_reply;
+	char *curr, *end, *reply, *tmp;
+	int sendfile_name_len = 0, sendfile_pos = 0;
+	int header_len = 0, body_len, created, ret = 0, http_len;
+
+	curr = cgi_message;
+	end = cgi_message + len;
+
+	PRINT_MESSAGE_LEFT;
+	CGI_reply = http_kmalloc(sizeof(*CGI_reply), ALLOC_REQ_PRIVATE);
+	memset(CGI_reply, 0, sizeof(*CGI_reply));
+	reply = CGI_reply->buf;
+
+#define CGI_SUCCESS "HTTP/1.1 200 OK\r\n"
+
+	memcpy(reply, CGI_SUCCESS, sizeof(CGI_SUCCESS)-1);
+	reply += sizeof(CGI_SUCCESS)-1;
+
+	while (*curr) {
+	switch (*curr) {
+
+		case 'C':
+
+#define CONTENT_TYPE "Content-Type: text/html"
+#define CONTENT_TYPE_CGI CONTENT_TYPE "\n"
+#define CONTENT_TYPE_HTTP CONTENT_TYPE "\r\n"
+
+			PRINT_MESSAGE_LEFT;
+			tmp = curr;
+			if (PARSE_TOKEN(curr, end, CONTENT_TYPE_CGI)) {
+				memcpy(reply, CONTENT_TYPE_HTTP, sizeof(CONTENT_TYPE_HTTP)-1);
+				reply += sizeof(CONTENT_TYPE_HTTP)-1;
+				PRINT_MESSAGE_LEFT;
+				continue;
+			}
+			GOTO_INCOMPLETE;
+
+		case 'X':
+
+#define HTTP_SENDFILE "X-CGI-TUX-sendfile: "
+
+			PRINT_MESSAGE_LEFT;
+			if (PARSE_TOKEN(curr, end, HTTP_SENDFILE)) {
+				tmp = CGI_reply->sendfile_name;
+				COPY_FIELD(tmp);
+				*tmp = 0;
+				sendfile_name_len = tmp-CGI_reply->sendfile_name;
+
+				curr++;
+				PRINT_MESSAGE_LEFT;
+				sendfile_pos = simple_strtoul(curr, &curr, 10);
+				if (get_c(curr) != '\n')
+					GOTO_INCOMPLETE;
+				PRINT_MESSAGE_LEFT;
+				Dprintk("got X-sendfile(%s (%d), %d)\n", CGI_reply->sendfile_name, sendfile_name_len, sendfile_pos);
+				curr++;
+				PRINT_MESSAGE_LEFT;
+				continue;
+			}
+			GOTO_INCOMPLETE;
+		case '\n':
+			curr++;
+			PRINT_MESSAGE_LEFT;
+			goto headers_finished;
+
+		default:
+			GOTO_INCOMPLETE;
+	}
+	}
+	GOTO_INCOMPLETE;
+headers_finished:
+	Dprintk("got valid CGI headers!\n");
+	PRINT_MESSAGE_LEFT;
+
+	/*
+	 * First load the urlo - we need to know the length
+	 * of the file to be sent. But we start the (potential)
+	 * cachemiss only after we have created the headers!
+	 */
+	req->objectname = CGI_reply->sendfile_name;
+	req->objectname_len = sendfile_name_len;
+
+	created = lookup_urlo(req, LOOKUP_ATOMIC);
+	if (req->userspace_module)
+		HTTP_BUG();
+	urlo = req->urlo;
+	Dprintk("extcgi: got urlo: %p.\n", urlo);
+	if (!urlo || urlo->tcapi || (req->method != METHOD_GET))
+		GOTO_INCOMPLETE;
+
+	/*
+	 * Create Content-Length reply field and close the header:
+	 */
+	body_len = end-curr;
+	http_len = urlo->filelen + body_len;
+	req->bytes_sent = http_len;
+
+#define CONTENT_LENGTH_HTTP "Content-Length: %d\r\n"
+	reply += sprintf(reply, CONTENT_LENGTH_HTTP, http_len);
+	*reply++ = '\r';
+	*reply++ = '\n';
+
+	header_len = reply - CGI_reply->buf;
+
+	/*
+	 * The rest of the user-CGI reply is part of the HTTP body.
+	 */
+	memcpy(reply, curr, body_len);
+	reply += body_len;
+
+	if (sendfile_pos > body_len)
+		sendfile_pos = body_len;
+	CGI_reply->sendfile_pos = header_len + sendfile_pos;
+	CGI_reply->len = body_len + header_len;
+	if (reply - CGI_reply->buf != CGI_reply->len)
+		GOTO_INCOMPLETE;
+
+	req->private = CGI_reply;
+
+	/*
+	 * Now start the cachemiss or queue for output.
+	 */
+	if (!urlo->csumc)
+		ret = http_miss_req(req);
+	else {
+		if (req->redirect_secondary)
+			HTTP_BUG();
+		queue_output_req(req, req->ti);
+	}
+
+	return ret;
+
+invalid:
+	CGI_reply->error = 1;
+	queue_output_req(req, req->ti);
+	return -1;
+}
+
+
+#define CGI_SUCCESS2 "HTTP/1.1 200 OK\r\nConnection: close\r\n"
+
+static int handle_cgi_reply (http_req_t *req, int *pipe_fds)
+{
+	int first = 1;
+	int len, left, total;
+	char buf [MAX_CGI_DATA+1], *tmp;
+	mm_segment_t oldmm;
+
+	close(pipe_fds[1]);
+	send_dynamic_reply(NULL, req->sock, CGI_SUCCESS2, sizeof(CGI_SUCCESS2)-1, 0);
+
+	req->bytes_sent = 0;
+	/*
+	 * The new process is the new owner of the socket, it will
+	 * close it.
+	 */
+repeat:
+	left = MAX_CGI_DATA;
+	len = 0;
+	total = 0;
+	tmp = buf;
+	do {
+		tmp += len;
+		total += len;
+		left -= len;
+		if (!left)
+			break;
+repeat_read:
+		Dprintk("reading %d bytes from sys_read().\n", left);
+		oldmm = get_fs(); set_fs(KERNEL_DS);
+		len = sys_read(pipe_fds[0], tmp, left);
+		set_fs(oldmm);
+		Dprintk("got %d bytes from sys_read() (total: %d).\n", len, total);
+		if (len > 0)
+			tmp[len] = 0;
+		Dprintk("CGI reply: (%d bytes, total %d).\n", len, total);
+		if (len == -512) {
+			reap_kids();
+			goto repeat_read;
+		}
+	} while (len > 0);
+	if (total > MAX_CGI_DATA)
+		HTTP_BUG();
+	if (total) {
+		if (!len)
+			send_dynamic_reply(NULL, req->sock, buf, total, 1);
+		else
+			send_dynamic_reply(NULL, req->sock, buf, total, 0);
+		req->bytes_sent += total;
+	}
+
+	Dprintk("bytes_sent: %d\n", req->bytes_sent);
+	if ((total > 0) && first) {
+		first = 0;
+
+//		Dprintk("looking for enter/enter in:{%s}\n", buf);
+		if (buf[total])
+			HTTP_BUG();
+		tmp = strstr(buf, "\n\n");
+		if (tmp) {
+//			Dprintk("found enter/enter at: {%s}\n", tmp);
+			req->bytes_sent -= (tmp-buf) + 2;
+			Dprintk("new bytes_sent: %d\n", req->bytes_sent);
+		} else {
+			printk("huh 001?\n");
+		}
+	}
+	if (len < 0)
+		Dprintk("sys_read returned with %d.\n", len);
+	else {
+		if ((total == MAX_CGI_DATA) && len)
+			goto repeat;
+	}
+	close(pipe_fds[0]);
+
+	req->http_status = 200;
+	queue_output_req(req, req->ti);
+	return -1;
+}
+
+static int exec_external_cgi (void *data)
+{
+	exec_param_t param;
+	http_req_t *req = data;
+	char *envp[NR_CGI_METAVARIABLES+1], **envp_p;
+	char *argv[] = { "/tmp/mingo", NULL};
+	char envstr[MAX_ENVLEN], *tmp;
+	unsigned int host;
+	int pipe_fds[2], len;
+	char command [100]; // FIXME: crash, exploit.
+
+	current->flags &= ~PF_ATOMICALLOC;
+	current->http = 0;
+	sprintf(current->comm,"cgimain - %i", current->pid);
+#define IP(x) (((unsigned char *)&host)[x])
+	host = req->sock->sk->daddr;
+
+	tmp = envstr;
+	envp_p = envp;
+
+#define WRITE_ENV(str...) \
+	if (envp_p > envp + NR_CGI_METAVARIABLES) \
+		HTTP_BUG(); \
+	len = sprintf(tmp, str); \
+	*envp_p++ = tmp; \
+	tmp += len + 1; \
+	if (tmp >= envstr + MAX_ENVLEN) \
+		HTTP_BUG();
+
+	WRITE_ENV("CONTENT_LENGTH=0");
+	WRITE_ENV("CONTENT_TYPE=html/text");
+	WRITE_ENV("DOCUMENT_ROOT=%s", http_docroot);
+	WRITE_ENV("GATEWAY_INTERFACE=1.1");
+	WRITE_ENV("PATH_INFO=%s", http_docroot);
+	WRITE_ENV("QUERY_STRING=%s", req->query);
+	WRITE_ENV("REMOTE_ADDR=%d.%d.%d.%d", IP(0), IP(1), IP(2), IP(3));
+	WRITE_ENV("REQUEST_METHOD=GET");
+	WRITE_ENV("SCRIPT_NAME=%s", req->objectname);
+	WRITE_ENV("SERVER_NAME=mg");
+	WRITE_ENV("SERVER_PORT=80");
+	WRITE_ENV("SERVER_PROTOCOL=HTTP/1.1");
+	WRITE_ENV("SERVER_SOFTWARE=TUX 1.0");
+	*envp_p = NULL;
+
+	sys_close(0);
+	sys_close(1);
+	pipe_fds[0] = -1;
+	pipe_fds[1] = -1;
+	if (do_pipe(pipe_fds))
+		HTTP_BUG();
+	if (pipe_fds[0] != 0)
+		HTTP_BUG();
+	if (pipe_fds[1] != 1)
+		HTTP_BUG();
+	sprintf(command, "/%s/cgi-bin/%s", http_docroot, req->objectname);
+	http_exec_process(command, argv, envp, pipe_fds, &param, 0);
+
+	return handle_cgi_reply(req, pipe_fds);
+}
+
+void start_external_cgi (http_req_t *req)
+{
+	int pid;
+
+repeat:
+	pid = kernel_thread(exec_external_cgi, (void*) req, SIGCHLD);
+	if (pid < 0) {
+		printk("HTTP: Could not fork external CGI process due to %d, retrying!\n", pid);
+		schedule_timeout(2);
+		goto repeat;
+	}
+}
+
+int query_extcgi (http_req_t *req)
+{
+	req->keep_alive = 0;
+	start_external_cgi(req);
+	return -1;
+}
+
+#define EXTCGI_INVALID_HEADER \
+	"HTTP/1.1 503 Service Unavailable\r\n" \
+	"Server: TUX 1.0\r\n" \
+	"Content-Length: 23\r\n\r\n"
+
+#define EXTCGI_INVALID_BODY \
+	"TUX: invalid CGI reply."
+
+#define EXTCGI_INVALID EXTCGI_INVALID_HEADER EXTCGI_INVALID_BODY
+
+int send_reply_extcgi (http_req_t *req)
+{
+	return 0;
+}
+
+tcapi_template_t extcgi_tcapi = {
+	vfs_name: "x",
+	version: HTTP_VERSION,
+	query: query_extcgi,
+	send_reply: send_reply_extcgi,
+};
+
+int extcgi_start (void)
+{
+	extcgi_tcapi.mod = THIS_MODULE;
+
+	return register_httpmodule(&extcgi_tcapi);
+}
+
+void extcgi_stop (void)
+{
+	unregister_httpmodule(&extcgi_tcapi);
+}
+
+module_init(extcgi_start)
+module_exit(extcgi_stop)
+
--- linux/net/http/parser.h.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/parser.h	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,80 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * parser.h: generic parsing routines
+ */
+
+#define get_c(ptr)						\
+({								\
+	unsigned char __ret = *(ptr);				\
+								\
+	if (__ret == '\r') {					\
+		unsigned char __next = *((ptr)+1);		\
+								\
+		if (__next == '\n' || !__next) {		\
+			__ret = __next;				\
+			(ptr)++;				\
+		}						\
+	}							\
+	__ret;							\
+})
+
+#define PARSE_TOKEN(ptr,end,str)				\
+	({							\
+		int __ret;					\
+								\
+		if (ptr + sizeof(str)-1 > end)			\
+			GOTO_INCOMPLETE;			\
+								\
+		if (memcmp(ptr, str, sizeof(str)-1))		\
+			__ret = 0;				\
+		else {						\
+			ptr += sizeof(str)-1;			\
+			__ret = 1;				\
+		}						\
+		__ret;						\
+	})
+
+#define PARSE_METHOD(req,ptr,end,name)				\
+	({							\
+		int __ret;					\
+								\
+		if (PARSE_TOKEN(ptr,end,#name" ")) {		\
+			req->method = METHOD_##name;		\
+			__ret = 1;				\
+		} else						\
+			__ret = 0;				\
+		__ret;						\
+	})
+
+#define COPY_LINE(target)					\
+	do {							\
+		while (get_c(curr) != '\n') {			\
+			if (!*curr)				\
+				GOTO_INCOMPLETE;		\
+			*target++ = *curr;			\
+			curr++;					\
+		}						\
+	} while (0)
+
+#define COPY_FIELD(target)					\
+	do {							\
+		while (get_c(curr) != ' ') {			\
+			if (!*curr)				\
+				GOTO_INCOMPLETE;		\
+			*target++ = *curr;			\
+			curr++;					\
+		}						\
+	} while (0)
+
+#define SKIP_LINE						\
+	do {							\
+		while (get_c(curr) != '\n') {			\
+			if (!*curr)				\
+				GOTO_INCOMPLETE;		\
+			curr++;					\
+		}						\
+	} while (0)
+
--- linux/net/http/proc.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/proc.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,445 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * sysctl.c: /proc/sysctl/http handling
+ */
+
+#include <net/http.h>
+
+char http_docroot[200] = "/var/www/http/";
+char http_logfile[200] = "/var/log/http";
+int http_stop = 0;
+int http_start = 0;
+int http_unload = 0;
+int http_clientport = 8080;
+int http_logging = 0;
+extern int http_Dprintk;
+int http_serverport= 80;
+int http_threads = 2;
+int http_max_connect = 10000;
+int http_max_backlog = 2048;
+int http_keepalive_timeout = 0;
+int http_max_cached_filesize = 100000;
+int http_mode_forbidden = 0 /*S_IXUGO*/; /* do not allow executable (CGI) files */
+int http_mode_allowed = S_IROTH; /* allow access if read-other is set */
+int http_mode_userspace = S_IXUSR;
+int http_mode_cgi = S_IXUGO;
+extern int http_in_packet_delay;
+extern int http_out_packet_delay;
+int multifragment_api = 1;
+
+static struct ctl_table_header *http_table_header;
+
+static ctl_table http_table[] = {
+	{	NET_HTTP_DOCROOT,
+		"documentroot",
+		&http_docroot,
+		sizeof(http_docroot),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_LOGFILE,
+		"logfile",
+		&http_logfile,
+		sizeof(http_logfile),
+		0644,
+		NULL,
+		proc_dostring,
+		&sysctl_string,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_STOP,
+		"stop",
+		&http_stop,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_START,
+		"start",
+		&http_start,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_UNLOAD,
+		"unload",
+		&http_unload,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_THREADS,
+		"threads",
+		&http_threads,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_KEEPALIVE_TIMEOUT,
+		"keepalive_timeout",
+		&http_keepalive_timeout,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MAX_BACKLOG,
+		"max_backlog",
+		&http_max_backlog,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MAX_CONNECT,
+		"max_connect",
+		&http_max_connect,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MAX_CACHED_FILESIZE,
+		"max_cached_filesize",
+		&http_max_cached_filesize,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MODE_FORBIDDEN,
+		"mode_forbidden",
+		&http_mode_forbidden,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MODE_ALLOWED,
+		"mode_allowed",
+		&http_mode_allowed,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MODE_USERSPACE,
+		"mode_userspace",
+		&http_mode_userspace,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_MODE_CGI,
+		"mode_cgi",
+		&http_mode_cgi,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_CLIENTPORT,
+		"clientport",
+		&http_clientport,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_LOGGING,
+		"Dprintk",
+		&http_Dprintk,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_LOGGING,
+		"logging",
+		&http_logging,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_SERVERPORT,
+		"serverport",
+		&http_serverport,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_LOGENTRY_ALIGN_ORDER,
+		"logentry_align_order",
+		&http_logentry_align_order,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_NAGLE,
+		"nagle",
+		&http_nagle,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_NEW_API,
+		"multifragment_api",
+		&multifragment_api,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_IN_PACKET_DELAY,
+		"in_packet_delay",
+		&http_in_packet_delay,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{	NET_HTTP_OUT_PACKET_DELAY,
+		"out_packet_delay",
+		&http_out_packet_delay,
+		sizeof(int),
+		0644,
+		NULL,
+		proc_dointvec,
+		&sysctl_intvec,
+		NULL,
+		NULL,
+		NULL
+	},
+	{0,0,0,0,0,0,0,0,0,0,0}	};
+	
+	
+static ctl_table http_dir_table[] = {
+	{NET_HTTP, "http", NULL, 0, 0555, http_table,0,0,0,0,0},
+	{0,0,0,0,0,0,0,0,0,0,0}
+};
+
+static ctl_table http_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, http_dir_table,0,0,0,0,0},
+	{0,0,0,0,0,0,0,0,0,0,0}
+};
+
+static void init_http_proc (void);
+
+void start_sysctl(void)
+{
+	init_http_proc();
+	http_table_header = register_sysctl_table(http_root_table,1);
+}
+
+
+void end_sysctl(void)
+{
+	unregister_sysctl_table(http_table_header);
+}
+
+static struct proc_dir_entry * root_http_dir;
+static struct proc_dir_entry * http_dir [CONFIG_HTTP_NUMTHREADS];
+static struct proc_dir_entry * listen_dir [CONFIG_HTTP_NUMTHREADS];
+static struct proc_dir_entry * listen_entries [CONFIG_HTTP_NUMTHREADS][CONFIG_HTTP_NUMSOCKETS];
+
+unsigned int http_listen [CONFIG_HTTP_NUMTHREADS][CONFIG_HTTP_NUMSOCKETS] =
+ { [0 ... CONFIG_HTTP_NUMTHREADS-1] = { 0, [1 ... CONFIG_HTTP_NUMSOCKETS-1] = -1 } };
+
+#define HEX_DIGITS 8
+
+static int http_listen_read_proc (char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	if (count < HEX_DIGITS+1)
+		return -EINVAL;
+	return sprintf (page, "%08x\n", *(unsigned int *)data);
+}
+
+static int http_listen_write_proc (struct file *file, const char *buffer,
+					unsigned long count, void *data)
+{
+	unsigned char hexnum [HEX_DIGITS];
+	unsigned int new_value;
+	int i, full_count = count;
+
+	if (!count)
+		return -EINVAL;
+	if (count > HEX_DIGITS)
+		count = HEX_DIGITS;
+	if (copy_from_user(hexnum, buffer, count))
+		return -EFAULT;
+
+	/*
+	 * Parse the first 8 characters as a hex string, any non-hex char
+	 * is end-of-string. '00e1', 'e1', '00E1', 'E1' are the same.
+	 */
+	new_value = 0;
+
+	for (i = 0; i < count; i++) {
+		unsigned int c = hexnum[i];
+
+		switch (c) {
+			case '0' ... '9': c -= '0'; break;
+			case 'a' ... 'f': c -= 'a'-10; break;
+			case 'A' ... 'F': c -= 'A'-10; break;
+		default:
+			goto out;
+		}
+		new_value = (new_value << 4) | c;
+	}
+out:
+	*(int *)data = new_value;
+
+	return full_count;
+}
+
+#define MAX_NAMELEN 10
+
+static void register_http_proc (unsigned int nr)
+{
+	struct proc_dir_entry *entry;
+	char name [MAX_NAMELEN];
+	int i;
+
+	if (!root_http_dir)
+		HTTP_BUG();
+
+	memset(name, 0, MAX_NAMELEN);
+	sprintf(name, "%d", nr);
+
+	/* create /proc/net/http/1234/ */
+	http_dir[nr] = proc_mkdir(name, root_http_dir);
+
+	/* create /proc/net/http/1234/listen/ */
+	listen_dir[nr] = proc_mkdir("listen", http_dir[nr]);
+
+	/* create /proc/net/http/1234/listen/ */
+	for (i = 0; i < CONFIG_HTTP_NUMSOCKETS; i++) {
+		sprintf(name, "%d", i);
+		entry = create_proc_entry(name, 0700, listen_dir[nr]);
+
+		entry->nlink = 1;
+		entry->data = (void *)&http_listen[nr][i];
+		entry->read_proc = http_listen_read_proc;
+		entry->write_proc = http_listen_write_proc;
+
+		listen_entries[nr][i] = entry;
+	}
+}
+
+static void init_http_proc (void)
+{
+	int i;
+
+	if (root_http_dir)
+		return;
+
+	/* create /proc/net/http */
+	root_http_dir = proc_mkdir("http", proc_net);
+
+	/*
+	 * Create entries for all existing threads.
+	 */
+	for (i = 0; i < CONFIG_HTTP_NUMTHREADS; i++)
+		register_http_proc(i);
+}
+
--- linux/net/http/CAD2.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/CAD2.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,907 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * CAD.c: Implementation of the SPECweb99 CAD dynamic application via
+ *        the HTTP trusted-module.
+ */
+
+#include <net/http.h>
+
+static int send_reply_head (http_req_t *req, size_t body_size);
+static int send_reply_tail (http_req_t *req);
+static int send_err (http_req_t *req, char *message);
+
+static HTTP_DECLARE_MUTEX(postlog_sem);
+#define POSTLOG "/tmp/postlog"
+static struct http_file *post_file;
+static int postlog_count;
+static char *postlog_head;
+static struct http_page *postlog_head_page;
+
+#define CAD_TAG_HEAD	"<!WEB99CAD><IMG SRC=\"/file_set/"
+#define CAD_TAG_BODY	"dirNNNNN/classX_Y"
+#define CAD_TAG_TAIL	"\"><!/WEB99CAD>"
+
+#define CAD_TAG CAD_TAG_HEAD CAD_TAG_BODY CAD_TAG_TAIL
+
+#define CAD_TAG_BODY_POS (sizeof(CAD_TAG_HEAD)-1)
+#define CAD_TAG_BODY_SIZE (sizeof(CAD_TAG_BODY)-1)
+#define CAD_TAG_SIZE (sizeof(CAD_TAG)-1)
+
+typedef struct CAD_struct {
+	int user_id;
+	int last_ad;
+	char ad_filename [100];
+	int reply_cookies_len;
+	char reply_cookies[MAX_COOKIE_LEN];
+} CAD_t;
+
+/*
+ * Called by the generic SSI (Server Side Include) engine to generate
+ * custom server-side include 'regions' (fragments) from the template.
+ * The object's content (the template) is passed via 'buf', the SSI
+ * descriptor table is filled out by this function. (can be left empty
+ * as well to indicate no changes.)
+ */
+static void create_SSI_map_CAD (http_req_t *req, csumcache_t *csumc, unsigned char *buf, int len, SSImap_t *SSImap)
+{
+	unsigned char *target = buf, *tmp;
+
+	/*
+	 * First the dynamic API determines wether this object is
+	 * a server-side-include file belonging to it's domain.
+	 * (Multiple modules using the same SSI-file are supported
+	 * as well.) A module does not want to generate a SSI map
+	 * for every object, obviously. (Other modules might want
+	 * to parse for .shmtl extension in the filename - but the
+	 * TUX SSI engine does not mandate this.)
+	 */
+	SSImap->nr = 0;
+	if (!req->query)
+		return;
+	req->urlo->SSI = 1;
+	tmp = strstr(req->query, "class");
+	if (tmp) {
+		tmp += sizeof("class")-1;
+		if ((tmp[0] != '1') && (tmp[0] != '2'))
+			return;
+	}
+	for (;;) {
+		int pos;
+
+		target = strstr(target, CAD_TAG);
+		if (!target)
+			break;
+		pos = target-buf + CAD_TAG_BODY_POS;
+		target += CAD_TAG_SIZE;
+
+
+		SSImap->pos[SSImap->nr] = pos;
+		SSImap->size[SSImap->nr] = CAD_TAG_BODY_SIZE;
+		SSImap->nr++;
+	}
+}
+
+
+typedef struct user_dem_s {
+	unsigned int dem;
+} user_dem_t;
+
+static int max_userid;
+static user_dem_t *user_dem = NULL;
+static struct http_direntry *user_pers_dentry;
+
+#define USER_PERS_FILE "User.Personality"
+#define USER_PERS_RECLEN 15
+
+typedef struct ad_s {
+	unsigned int dem;
+	unsigned int gender_weight;
+	unsigned int age_weight;
+	unsigned int region_weight;
+	unsigned int interest_1_weight;
+	unsigned int interest_2_weight;
+	unsigned int min_match;
+	unsigned int expires;
+} ad_t;
+
+static int max_adid;
+static ad_t *ad = NULL;
+
+#define AD_FILE "Custom.Ads"
+#define AD_RECLEN 39
+
+static int read_custom_ads (http_req_t *req)
+{
+	struct http_file *file;
+	int ret, len, err = -2;
+	char *buf = NULL, *tmp;
+	struct http_direntry *dentry;
+	unsigned int adid, i, dem, min_match, weight, expires;
+
+
+	dentry = http_lookup_direntry(AD_FILE, &docroot, 0);
+	if (http_direntry_error(dentry))
+		goto error;
+	file = http_direntry_open(dentry, O_RDONLY, 0);
+	if (!file)
+		goto error;
+	len = http_file_size(file);
+	if (!len)
+		goto error;
+	if (ad) {
+		http_free(ad, ALLOC_AD_FILE);
+		ad = NULL;
+	}
+	max_adid = len/AD_RECLEN + 1;
+	ad = http_malloc(max_adid * sizeof(ad_t), ALLOC_AD_FILE);
+	buf = http_malloc(len, ALLOC_ADTMPBUF);
+	if (!ad || !buf)
+		goto error;
+
+	ret = http_write_file(file, buf, len);
+	http_close_file(file);
+	if (ret <= 0)
+		goto error;
+
+/*
+ * Sample ad record:
+	"   54 24808100    97F61  75  952393980\n"
+ */
+
+	tmp = buf;
+	i = 0;
+	for (tmp = buf; tmp != buf+len; tmp++) {
+
+		while (*tmp == ' ') tmp++;
+		adid = simple_strtoul(tmp, &tmp, 10);
+		if (adid != i)
+			goto error;
+		if (adid >= max_adid)
+			goto error;
+		i++;
+		if (*tmp != ' ')
+			goto error;
+		tmp++;
+		while (*tmp == ' ') tmp++;
+		dem = simple_strtoul(tmp, &tmp, 16);
+		tmp++;
+		while (*tmp == ' ') tmp++;
+		weight = simple_strtoul(tmp, &tmp, 16);
+		while (*tmp == ' ') tmp++;
+		min_match = simple_strtoul(tmp, &tmp, 10);
+		while (*tmp == ' ') tmp++;
+		expires = simple_strtoul(tmp, &tmp, 10);
+		if (*tmp != '\n')
+			goto error;
+		ad[adid].dem = dem;
+
+		ad[adid].gender_weight		= (weight & 0x000f0000) >> 16;
+		ad[adid].age_weight		= (weight & 0x0000f000) >> 12;
+		ad[adid].region_weight		= (weight & 0x00000f00) >> 8;
+		ad[adid].interest_1_weight	= (weight & 0x000000f0) >> 4;
+		ad[adid].interest_2_weight	= (weight & 0x0000000f);
+
+		ad[adid].min_match = min_match;
+		ad[adid].expires = expires;
+
+	}
+	err = 0;
+error:
+	if (buf)
+		http_free(buf, ALLOC_ADTMPBUF);
+	if (err)
+		return send_err(req, "CAD: error while reading & parsing the ad file.\n");
+	return err;
+}
+
+static int read_user_personality (http_req_t *req)
+{
+	struct http_file *file;
+	int ret, len, err = -2;
+	char *buf = NULL, *tmp;
+	unsigned int uid, i, dem;
+	struct http_direntry *dentry;
+
+	dentry = http_lookup(USER_PERS_FILE, &docroot, 0);
+	file = http_direntry_open(dentry, O_RDONLY, 0);
+	if (!file)
+		goto error;
+	len = http_file_size(file);
+	if (!len)
+		goto error;
+	if (user_dem) {
+		http_free(user_dem, ALLOC_USERDEM);
+		user_dem = NULL;
+	}
+	max_userid = len/USER_PERS_RECLEN + 1;
+	user_dem = http_malloc(max_userid * sizeof(user_dem_t), ALLOC_USERDEM);
+	buf = http_malloc(len, ALLOC_USERDEM_TMPBUF);
+	if (!user_dem || !buf)
+		goto error;
+
+	ret = http_read_file(file, buf, len);
+	fput(file);
+	if (ret <= 0)
+		goto error;
+
+	tmp = buf;
+	i = 0;
+	for (tmp = buf; tmp != buf+len; tmp++) {
+		if (*tmp == ' ')
+			continue;
+		uid = simple_strtoul(tmp, &tmp, 10);
+		if (uid != i)
+			goto error;
+		if (uid >= max_userid)
+			goto error;
+		i++;
+		if (*tmp != ' ')
+			goto error;
+		while (*tmp == ' ') tmp++;
+		dem = simple_strtoul(tmp, &tmp, 16);
+		if (*tmp != '\n')
+			goto error;
+		user_dem[uid].dem = dem;
+	}
+	err = 0;
+error:
+	if (buf)
+		http_free(buf, ALLOC_USERDEM_TMPBUF);
+	if (err)
+		return send_err(req, "CAD: error while reading & parsing the user file.\n");
+	return err;
+}
+
+#define MAX_CUSTOM_ADS 360
+
+static int find_ad (int user_id, int last_ad, int *weight_p)
+{
+	int adid, weight = 0, dem;
+
+	for (adid = last_ad + 1; adid != last_ad; adid++) {
+		if (adid >= MAX_CUSTOM_ADS)
+			adid = 0;
+
+		dem = user_dem[user_id].dem & ad[adid].dem;
+		weight = 0;
+
+		if (dem & 0x30000000)
+			weight += ad[adid].gender_weight;
+		if (dem & 0x0f000000)
+			weight += ad[adid].age_weight;
+		if (dem & 0x00f00000)
+			weight += ad[adid].region_weight;
+		if (dem & 0x000ffc00)
+			weight += ad[adid].interest_1_weight;
+		if (dem & 0x000003ff)
+			weight += ad[adid].interest_2_weight;
+		if (weight >= ad[adid].min_match)
+			break;
+	}
+
+	*weight_p = weight;
+	return adid;
+}
+
+static unsigned int last_mtime = 0;
+
+static int reread_files (http_req_t *req)
+{
+	int ret = -2;
+	struct http_direntry *dentry;
+
+	http_dput(user_pers_dentry);
+	dentry = http_lookup(USER_PERS_FILE, &docroot, 0);
+	if (http_direntry_error(dentry))
+		goto error;
+	user_pers_dentry = dentry;
+
+	if (http_mtime(dentry) != last_mtime) {
+		void *tmp = user_dem;
+		user_dem = NULL;
+		http_free(tmp, ALLOC_USERDEM);
+		if (read_user_personality(req))
+			goto error;
+		if (read_custom_ads(req))
+			goto error;
+		last_mtime = http_mtime(dentry);
+	}
+	ret = 0;
+
+error:
+	return ret;
+}
+
+static int custom_ad_rotate (http_req_t *req, CAD_t *CADp)
+{
+	int adid, weight, expired, err;
+	int user_id, last_ad;
+	time_t now;
+
+	user_id = CADp->user_id;
+	last_ad = CADp->last_ad;
+
+	if (http_direntry_error(user_pers_dentry) ||
+			(http_mtime(user_pers_dentry) != last_mtime)) {
+		err = reread_files(req);
+		if (err)
+			return err;
+	}
+
+	/*
+	 * Any error in either reading or parsing of the files results
+	 * in a returned -1 adid.
+	 */
+	adid = -1;
+	expired = 1;
+	weight = 0;
+
+	adid = find_ad(user_id, last_ad, &weight);
+	if (adid < 0)
+		goto error;
+	now = http_time();
+	if (now <= ad[adid].expires)
+		expired = 0;
+
+error:
+	CADp->reply_cookies_len = sprintf(CADp->reply_cookies,
+		"found_cookie=Ad_id=%d&Ad_weight=%d&Expired=%d",
+			adid, weight, expired);
+
+	sprintf(CADp->ad_filename, "dir%05d/class%d_%d",
+		adid / 36, ((adid % 36) / 9), adid % 9);
+	return 0;
+}
+
+
+#define TOKEN_EQUAL(input,token) \
+		(!memcmp(input, token, sizeof(token)-1))
+
+#define PARSE_STRING(token,input,output)				\
+	({								\
+		int __ret = 0;						\
+		if (TOKEN_EQUAL(input, token)) {			\
+			char *tmp;					\
+									\
+			input += sizeof(token)-1;			\
+			tmp = output;					\
+			while (*input && *input != '&' &&		\
+						*input != ',')		\
+				*tmp++ = *input++;			\
+			*tmp = 0;					\
+			__ret = 1;					\
+		}							\
+		__ret;							\
+	})
+
+#define PARSE_UINT(token,input,output)					\
+	({								\
+		int __ret = 0;						\
+		if (TOKEN_EQUAL(input, token)) {			\
+									\
+			input += sizeof(token)-1;			\
+			output = simple_strtoul(input, &input, 10);	\
+			__ret = 1;					\
+		}							\
+		__ret;							\
+	})
+
+static int init_postlog_file (void)
+{
+	char buf[400], *tmp;
+	int ret;
+
+	if (post_file)
+		fput(post_file);
+	post_file = http_open_file(POSTLOG, O_CREAT|O_TRUNC|O_APPEND|O_RDWR);
+	if (!post_file) {
+		printk("CAD: could not open POST log {%s}!\n", POSTLOG);
+		return -2;
+	}
+	postlog_count = 0;
+	tmp = buf;
+	tmp += sprintf(tmp, "%10d\n", 0);
+	ret = http_write_file(post_file, buf, tmp-buf);
+	if (ret != tmp-buf) {
+		printk("hm, initial postlog write didnt succeed: %d != %p-%p.\n", ret, tmp, buf);
+		return -2;
+	}
+	postlog_head_page = http_mmap_page(post_file, postlog_head, 0);
+	if (!postlog_head_page)
+		return -2;
+
+	return 0;
+}
+
+#define COMMAND_STRING "command/"
+#define COMMAND_RESET "Reset"
+#define COMMAND_FETCH "Fetch"
+
+static int do_reset (http_req_t *req, char *query)
+{
+	char maxload [20], pttime[20], maxthread[20],
+			exp1[20], exp2[20], urlroot [100];
+	char tmpstr1[256], tmpstr2[256];
+
+	http_sleep(1);
+	if (!PARSE_STRING("&maxload=", query, maxload))
+		return send_err(req,"CAD: invalid &maxload field!\n");
+	if (!PARSE_STRING("&pttime=", query, pttime))
+		return send_err(req,"CAD: invalid &pttime field!\n");
+	if (!PARSE_STRING("&maxthread=", query, maxthread))
+		return send_err(req,"CAD: invalid &maxthread field!\n");
+	if (!PARSE_STRING("&exp=", query, exp1))
+		return send_err(req,"CAD: invalid &exp1 field!\n");
+	if (!PARSE_STRING(",", query, exp2))
+		return send_err(req,"CAD: invalid &exp2 field!\n");
+	if (!PARSE_STRING("&urlroot=", query, urlroot))
+		return send_err(req,"CAD: invalid &urlroot field!\n");
+
+
+	strcpy(tmpstr1, http_docroot); strcat(tmpstr1, "/upfgen99");
+	strcpy(tmpstr2, http_docroot); strcat(tmpstr2, "/cadgen99");
+#define TOPDIR http_docroot
+#define UPFGEN tmpstr1
+#define CADGEN tmpstr2
+
+	{
+		char *argv_upfgen[] = { UPFGEN, "-C", TOPDIR, "-n", maxload,
+					"-t", maxthread, NULL};
+		char *argv_cadgen[] = { CADGEN, "-C", TOPDIR, "-e", pttime,
+					"-t", maxthread, exp1, exp2, NULL};
+		char * envp[] = { "HOME=/", "TERM=linux",
+			"PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+
+		if (http_exec_process(UPFGEN, argv_upfgen, envp, NULL, NULL, 1) < 0)
+			return send_err(req,"CAD: could not execute UPFGEN!\n");
+
+		if (http_exec_process(CADGEN, argv_cadgen, envp, NULL, NULL, 1) < 0)
+			return send_err(req,"CAD: could not execute CADGEN!\n");
+	}
+	/*
+	 * Clear post log
+	 */
+	http_down(&postlog_sem);
+		
+	init_postlog_file();
+	http_up(&postlog_sem);
+
+	/*
+	 * mtime has a 1 second resolution, sleep 1 second so that
+	 * the check for modified User.Personality and Custom.Ads
+	 * files notices multiple resets correctly.
+	 */
+	http_sleep(1);
+
+	req->bytes_sent = send_reply_head(req, 0);
+	req->bytes_sent += send_reply_tail(req);
+
+	return -2;
+}
+
+#define BLOCKLEN 512
+
+static int send_postlog (http_req_t *req)
+{
+	char buf [BLOCKLEN];
+	int len, total, bytes;
+
+	if (http_file_error(post_file)) {
+		printk("hm, no postlog.\n");
+		return -2;
+	}
+	if (req->urlo)
+		HTTP_BUG();
+	/*
+	 * Atomic transaction - serializes with all POST activity while
+	 * we send the log.
+	 */
+	http_down(&postlog_sem);
+
+	req->body_len = http_file_size(post_file);
+	post_file->f_pos = 0;
+	bytes = send_reply_head(req, req->body_len);
+	total = 0;
+	do {
+		len = http_write_file(post_file, buf, BLOCKLEN);
+		if (!len)
+			break;
+		if (len < 0)
+			HTTP_BUG();
+		bytes += http_send_client(req, buf, len, 0);
+		total += len;
+	} while (len == BLOCKLEN);
+
+	http_up(&postlog_sem);
+
+	bytes += send_reply_tail(req);
+	req->bytes_sent = bytes;
+
+	return -2;
+}
+
+static int do_command (http_req_t *req, char *query)
+{
+	if (TOKEN_EQUAL(query, COMMAND_RESET))
+		return do_reset(req, query + sizeof(COMMAND_RESET)-1);
+	if (TOKEN_EQUAL(query, COMMAND_FETCH))
+		return send_postlog(req);
+	return send_err(req,"CAD: got invalid command!\n");
+}
+
+static CAD_t * parse_GET_cookies (http_req_t *req)
+{
+	int uid, last_ad;
+	CAD_t *CADp;
+	char *tmp;
+
+	if (!req->cookies_len) {
+		return NULL;
+	}
+
+	CADp = http_malloc(sizeof(CAD_t), ALLOC_REQ_PRIVATE);
+	CADp->reply_cookies_len = 0;
+
+	tmp = req->cookies + sizeof("my_cookie=user_id=")-1;
+	uid = simple_strtoul(tmp, &tmp, 10) - 10000;
+
+	tmp += sizeof("&last_ad=")-1;
+	last_ad = simple_strtoul(tmp, &tmp, 10);
+
+	CADp->user_id = uid;
+	CADp->last_ad = last_ad;
+
+	if (req->private)
+		HTTP_BUG();
+	req->private = (void *) CADp;
+	return CADp;
+}
+
+static int do_POST (http_req_t *req)
+{
+	int dir = -1, class = -1, num = -1, client = -1;
+	char buf[400], *tmp;
+	char urlroot[100];
+	CAD_t *CADp;
+	char *curr;
+	int ret;
+
+	CADp = parse_GET_cookies(req);
+
+	curr = req->post_data;
+	if (!curr)
+		goto parse_error;
+
+#define POST_URLROOT "urlroot="
+#define POST_CLASS "class="
+#define POST_CLIENT "client="
+#define POST_DIR "dir="
+#define POST_NUM "num="
+
+	for (;;) {
+		switch (*curr) {
+			case 'u':
+				if (PARSE_STRING( POST_URLROOT, curr, urlroot))
+					continue;
+				goto parse_error;
+			case 'c':
+				if (PARSE_UINT( POST_CLASS, curr, class))
+					continue;
+				if (PARSE_UINT( POST_CLIENT, curr, client))
+					continue;
+				goto parse_error;
+			case 'd':
+				if (PARSE_UINT( POST_DIR, curr, dir))
+					continue;
+				goto parse_error;
+			case 'n':
+				if (PARSE_UINT( POST_NUM, curr, num))
+					continue;
+				goto parse_error;
+			case '&':
+				curr++;
+				continue;
+			case 0:
+				goto out;
+			default:
+				goto parse_error;
+		}
+		goto parse_error;
+	}
+out:
+	if (!CADp)
+		goto parse_error;
+	tmp = CADp->ad_filename;
+	tmp += sprintf(tmp, "%sdir%05d/class%d_%d", urlroot, dir, class, num);
+
+	/*
+	 * Aquire semaphore guaranteeing atomic operations
+	 * on the postlog file.
+	 */
+	http_down(&postlog_sem);
+	if (!post_file)
+		if (init_postlog_file())
+			return 0;
+
+	postlog_count++;
+	tmp = postlog_head;
+	tmp += sprintf(tmp, "%10d", postlog_count);
+	*tmp = '\n';
+
+	tmp = buf;
+	tmp += sprintf(tmp, "%10d %10ld %10d %5d %2d %2d %10d %-60.60s %10d %10d\n", postlog_count, http_time(), http_getpid(), dir, class, num, client, CADp->ad_filename, http_getpid(), CADp->user_id + 10000);
+
+	ret = http_write_file(post_file, buf, tmp-buf);
+	http_up(&postlog_sem);
+
+	if (ret != tmp-buf) {
+		printk("hm, append postlog write didnt succeed: %d != %p-%p.\n", ret, tmp, buf);
+		goto parse_error;
+	}
+
+	CADp->reply_cookies_len = sprintf(CADp->reply_cookies,
+		"my_cookie=%u", 10000 + CADp->user_id);
+	return 0;
+parse_error:
+	return send_err(req,"CAD: error while parsing POST request!\n");
+}
+
+/*
+ * Called by TUX for every new connection:
+ */
+static int query_CAD (http_req_t *req)
+{
+	urlo_t *urlo = NULL, *prev_urlo = NULL;
+	int ret = 0;
+	CAD_t *CADp;
+	int missed;
+
+	if (req->dentry) HTTP_BUG();
+
+	if (req->method == METHOD_POST) {
+		ret = do_POST(req);
+		if (ret)
+			return ret;
+		CADp = (CAD_t *)req->private;
+		if (!CADp)
+			HTTP_BUG();
+		req->objectname = CADp->ad_filename;
+		req->objectname_len = strlen(CADp->ad_filename);
+	} else {
+		char *tmp = req->query;
+
+		if (req->method != METHOD_GET)
+			HTTP_BUG();
+		if (TOKEN_EQUAL(req->query, COMMAND_STRING)) {
+			tmp += sizeof(COMMAND_STRING)-1;
+			return do_command(req, tmp);
+		}
+		req->objectname = req->query;
+		req->objectname_len = req->query_len;
+		if (req->private)
+			HTTP_BUG();
+		CADp = parse_GET_cookies(req);
+	}
+
+repeat_lookup:
+	if (req->urlo)
+		HTTP_BUG();
+	missed = lookup_urlo(req, LOOKUP_ATOMIC);
+	if (req->userspace_module)
+		HTTP_BUG();
+	urlo = req->urlo;
+	if ((!missed && !urlo) || (urlo && urlo->tcapi))
+		goto url_error;
+	if (req->method == METHOD_GET) {
+		if (CADp) {
+			if (!missed && !urlo->SSI) {
+				char *tmp;
+				tmp = strstr(req->objectname, "class");
+				if (tmp) {
+					tmp += sizeof("class")-1;
+					if ((tmp[0] == '1') || (tmp[0] == '2')) {
+						free_urlo(urlo->inode);
+						prev_urlo = urlo;
+						req->urlo = NULL;
+						http_dput(req->dentry);
+						req->dentry = NULL;
+						goto repeat_lookup;
+					}
+					urlo->SSI = 1;
+				}
+			}
+			req->SSI = 1;
+			ret = custom_ad_rotate(req, CADp);
+			if (ret)
+				return ret;
+		}
+	}
+	if (missed || !urlo->csumc) {
+		if (req->prev_urlo)
+			HTTP_BUG();
+		req->prev_urlo = prev_urlo;
+		return http_miss_req(req);
+	}
+	if (prev_urlo)
+		put_urlo(urlo);
+
+	return ret;
+url_error:
+	return send_err(req, "CAD: error while parsing CAD request!\n");
+}
+
+#define REPLY_HEAD_HEAD \
+	"HTTP/1.1 200 OK\r\n" \
+	"Content-Type: text/html\r\n" \
+	"Connection: Keep-Alive\r\n" \
+	"Content-Length: %d\r\n\r\n"
+
+#define REPLY_HEAD_HEAD_COOKIE \
+	"HTTP/1.1 200 OK\r\n" \
+	"Content-Type: text/html\r\n" \
+	"Connection: Keep-Alive\r\n" \
+	"Content-Length: %d\r\n" \
+	"Set-Cookie: %s\r\n" \
+	"\r\n"
+
+#define REPLY_HEAD_TAIL \
+	"<html>\n" \
+	"<head><title>SPECweb99 Dynamic GET & POST Test</title></head>\n"\
+	"<body>\n" \
+	"<p>SERVER_SOFTWARE = TUX 1.0\n" \
+	"<p>REMOTE_ADDR = %d.%d.%d.%d\n" \
+	"<p>SCRIPT_NAME = %s\n" \
+	"<p>QUERY_STRING = %s\n" \
+	"<pre>\n"
+
+#define REPLY_TAIL \
+	"\n</pre>\n" \
+	"</body></html>\n"
+
+static int send_reply_head (http_req_t *req, size_t body_size)
+{
+	char buf [1000];
+	char *tmp, *head, *tail;
+	CAD_t *CADp = (CAD_t *)req->private;
+	unsigned int host, head_len, tail_len, total_len;
+
+	host = http_client_addr(req);
+#define IP(x) (((unsigned char *)&host)[x])
+
+	tmp = tail = buf;
+	tmp += sprintf(tmp, REPLY_HEAD_TAIL, IP(0), IP(1), IP(2), IP(3),
+			req->tcapi->vfs_name, req->query);
+
+	tail_len = tmp-buf;
+
+	total_len = tail_len + sizeof(REPLY_TAIL)-1 + body_size;
+
+	head = tmp;
+	if (CADp && CADp->reply_cookies_len)
+		tmp += sprintf(tmp, REPLY_HEAD_HEAD_COOKIE, total_len,
+						CADp->reply_cookies);
+	else
+		tmp += sprintf(tmp, REPLY_HEAD_HEAD, total_len);
+
+	head_len = tmp-head;
+	http_send_client(req, head, head_len, 0);
+	http_send_client(req, tail, tail_len, 0);
+	req->http_status = 200;
+
+	return tail_len;
+}
+
+static int send_reply_tail (http_req_t *req)
+{
+	int len = sizeof(REPLY_TAIL)-1;
+
+	http_send_client(req, REPLY_TAIL, len, 1);
+	return len;
+}
+
+
+/*
+ * Send dynamicly generated SSI server-side include
+ * content. (this is the typical CAD case) Every reply is
+ * generated dynamically, based on the template position
+ * and cookie values, or other information.
+ */
+static int send_reply_CAD (http_req_t *req)
+{
+	int bytes;
+
+	req->body_len = req->urlo->body_len;
+
+	bytes = send_reply_head(req, req->urlo->body_len);
+	bytes += http_send_object(req, 0, 0);
+	bytes += send_reply_tail(req);
+
+	return bytes;
+}
+
+/*
+ * Return SPECweb99 error message.
+ */
+static int send_err (http_req_t *req, char *message)
+{
+	CAD_t *CADp = (CAD_t *)req->private;
+	int len = strlen(message);
+	int bytes;
+
+	/*
+	 * Return a -1 Ad_id in the reply cookie.
+	 */
+	if (CADp)
+		CADp->reply_cookies_len = sprintf(CADp->reply_cookies,
+			"found_cookie=Ad_id=-1&Ad_weight=0&Expired=1");
+
+	req->body_len = len;
+	bytes = send_reply_head(req, len);
+	http_send_client(req, message, len, 0);
+	bytes += len;
+	bytes += send_reply_tail(req);
+
+	req->bytes_sent = bytes;
+	return -2;
+}
+
+/*
+ * This callback is called when a particular SSI object is being
+ * sent. Note that there can be more SSI 'fragments' within one
+ * object, and that this fragment is local (not cached, dynamically
+ * generated and TCP-checksummed) to this request. This means that
+ * a generic module can use whatever request-local state to generate
+ * dynami SSI content. Eg. a greeting message in the language
+ * determined by the IP address, or cookies-based advertisement like
+ * in the CAD case. The SSI fragment's size can be changed as well,
+ * frag->size is the default size.
+ *
+ * this callback is the 'heart' of TUX's SSI implementation.
+ */
+static int generate_SSI_entry_CAD (http_req_t *req, skb_frag_t *frag)
+{
+	int packetsize = frag->size;
+	CAD_t *CADp;
+	char *buf;
+
+	CADp = (CAD_t *)req->private;
+	if (!CADp)
+		HTTP_BUG();
+	if (packetsize != sizeof(CAD_TAG_BODY)-1)
+		HTTP_BUG();
+
+	buf = (char *)kmap(frag->page) + frag->page_offset;
+	memcpy(buf, CADp->ad_filename, sizeof(CAD_TAG_BODY)-1);
+
+	frag->csum = csum_partial(buf, packetsize, 0);
+	kunmap(frag->page);
+	return 1;
+}
+
+static tcapi_template_t CAD_tcapi = {
+	vfs_name: "e",
+	version: HTTP_VERSION,
+	query: query_CAD,
+	send_reply: send_reply_CAD,
+	create_SSI_map: create_SSI_map_CAD,
+	generate_SSI_entry: generate_SSI_entry_CAD
+};
+
+static int CAD2_start (void)
+{
+	CAD_tcapi.mod = THIS_MODULE;
+
+	return register_httpmodule(&CAD_tcapi);
+}
+
+void CAD2_stop (void)
+{
+        unregister_httpmodule(&CAD_tcapi);
+}
+
+module_init(CAD2_start)
+module_exit(CAD2_stop)
+
--- linux/net/http/userspace.c.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/userspace.c	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,54 @@
+/*
+ * TUX - Integrated HTTP layer and Object Cache
+ *
+ * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com>
+ *
+ * userspace.c: handle userspace-module requests
+ */
+
+#include <net/http.h>
+
+http_req_t * pick_userspace_req (threadinfo_t *ti)
+{
+	struct list_head *head, *curr;
+	http_req_t *req = NULL;
+
+	spin_lock_irq(&ti->userspace_lock);
+	head = &ti->userspace_pending;
+	curr = head->next;
+
+	if (curr != head) {
+		req = list_entry(curr, http_req_t, userspace);
+		if (req->magic != HTTP_MAGIC)
+			HTTP_BUG();
+		check_req_list(req, &req->userspace);
+		if (req->ti != ti)
+			HTTP_BUG();
+		if (ti->thread != current)
+			HTTP_BUG();
+		if (!req->userspace_module)
+			HTTP_BUG();
+		if (!req->tcapi)
+			HTTP_BUG();
+		list_del(curr);
+		check_req_list(req, NULL);
+	}
+	spin_unlock_irq(&ti->userspace_lock);
+
+	return req;
+}
+
+void flush_userspacequeue (threadinfo_t *ti)
+{
+	http_req_t *req;
+
+	while ((req = pick_userspace_req(ti))) {
+		req->keep_alive = 0;
+		req->http_status = -1;
+		req->userspace_module = 0;
+		req->private = NULL;
+		DEC_STAT(nr_userspace_pending);
+		flush_request(req, ti);
+	}
+}
+
--- linux/net/http/HTTPAPI.txt.orig	Fri Sep  1 07:28:26 2000
+++ linux/net/http/HTTPAPI.txt	Fri Sep  1 07:28:26 2000
@@ -0,0 +1,430 @@
+
+HTTP-module HOWTO.
+
+Introduction:
+
+every HTTP trusted dynamic module defines a 'tcapi template', which
+defines entry points and module properties. Note that TUX's in-kernel
+dynamic API is ment to be small and simple. Complex and slow requests
+should be handled in user-space.
+
+-----------------------------------------------------------------------
+
+Currently defined fields in the TUX 1.0 module template are:
+
+struct tcapi_template_s {
+        char *vfs_name;
+        char *version;
+        int (*query) (http_req_t *req);
+        int (*send_reply) (http_req_t *req);
+        int (*log) (http_req_t *req, char *log_buffer);
+        void (*finish) (http_req_t *req);
+};
+
+-----------------------------------------------------------------------
+
+HTTP-module vfs_name:
+
+        char *vfs_name;
+
+the VFS name to which the module is mapped. Eg. if vfs_name is "httpapi",
+then http://server/httpapi?filename requests will be directed to this
+module.
+
+-----------------------------------------------------------------------
+
+HTTP-module version:
+
+        char *version;
+
+the TUX version string of the module. Current TUX version is "TUX 1.0".
+Future API changes might result in older, incompatible modules being not
+loaded. Future APIs might support older APIs as well.
+
+-----------------------------------------------------------------------
+
+TUX-module query():
+
+        int (*query) (http_req_t *req);
+
+callback which happens after a new request arrives which involves this
+module. 'req' is the HTTP request descriptor.
+
+RETURN VALUES:
+
+         0: request parsed, continue with output
+
+        -1: input data incomplete, put socket into idle state
+
+        -2: request finished, flush now
+
+         (all other return values undefined)
+
+-----------------------------------------------------------------------
+
+HTTP-module send_reply():
+
+        int (*send_reply) (http_req_t *req);
+
+if defined then replies are generated by the module. Simpler modules
+which have this callback set to NULL will just fill out req->filename
+and req->urlc, which file will then be transmitted by TUX.
+
+RETURN VALUES:
+
+         positive values: bytes transmitted
+                      -3: redirect request to secondary server
+         everything else: 0 bytes transmitted, flush request now
+
+-----------------------------------------------------------------------
+
+HTTP-module log():
+
+        int (*log) (http_req_t *req, char *log_buffer);
+
+if defined then the module can create a custom log entry, and returns
+the log entry's size. log_buffer is maximum MAX_LOG_ENTRY long.
+
+RETURN VALUES:
+
+        length of log entry
+
+-----------------------------------------------------------------------
+
+HTTP-module finish():
+
+        void (*finish) (http_req_t *req);
+
+if defined then after closing the connection TUX calls this callback.
+Modules can free potential per-request private data structures this way.
+
+RETURN VALUES:
+
+        none
+
+-----------------------------------------------------------------------
+-----------------------------------------------------------------------
+-----------------------------------------------------------------------
+
+TUX helper functions and object descriptors available to HTTP modules,
+all these functions have a http_ prefix, to avoid namespace pollution.
+
+-----------------------------------------------------------------------
+
+http_req_t *;
+
+descriptor of a client connection. Defined fields are:
+
+
+char * query; // the string part of the URI after the question mark.
+urlc_t *urlc; // HTTP object belonging to this connection
+
+
+
+-----------------------------------------------------------------------
+
+struct http_file *;
+
+opaque file descriptor similar to user-space 'FILE *'. Fields are
+undefined for modules and are not needed to use the pointer.
+
+
+-----------------------------------------------------------------------
+
+struct http_page *;
+
+opaque page descriptor used for mapping pages. Fields are undefined
+for modules and are not needed to use the pointer.
+
+-----------------------------------------------------------------------
+
+struct http_direntry *;
+
+opaque type describing directory entries. Fields are undefined
+for modules and are not needed to use the pointer.
+
+-----------------------------------------------------------------------
+
+extern struct http_nameidata docroot;
+
+document root 'lookup context'. It is automatically provided for all
+HTTP modules, lookups are always relative to a lookup context.
+
+-----------------------------------------------------------------------
+
+struct http_mutex *;
+
+HTTP_DECLARE_MUTEX(name)
+
+macro to declare a mutex. The mutex can be referred to by 'name'. The
+type of the mutex is opaque.
+
+-----------------------------------------------------------------------
+
+urlc_t *;
+
+TUX URL object descriptor. Defined fields are:
+
+int filelen; // length of object
+tcapi_t *tcapi; // HTTP module this object belongs to
+csumc_t *csumc; // checksum-object
+int body_len; // length of the body of the object
+threadinfo_t *ti; // HTTP thread descriptor
+char *reply_cookie; // max 256 bytes reply cookie buffer
+int bytes_sent; // nr of bytes sent to client
+int keep_alive; // wether the connection should be kept after the request
+char *cookies; // input cookies
+int cookies_len; // length of input cookie field
+void *private; // opaque pointer free to be used by modules
+char *post_data; // input POST data
+http_method_t method; // input HTTP method
+char *filename; // HTTP object name
+int filename_len; // length of HTTP object name string
+int http_status; // reply message status towards client
+int body_len; // body length of HTTP reply message
+
+-----------------------------------------------------------------------
+
+typedef enum http_methods {
+        METHOD_NONE,
+        METHOD_GET,
+        METHOD_HEAD,
+        METHOD_POST,
+        METHOD_PUT
+} http_method_t;
+
+-----------------------------------------------------------------------
+
+threadinfo_t *;
+
+descriptor for a TUX thread. Defined fields are:
+
+char *output_buffer;
+
+output buffer belonging to this thread.
+
+-----------------------------------------------------------------------
+
+csumc_t *;
+
+HTTP checksum-object descriptor.
+
+-----------------------------------------------------------------------
+-----------------------------------------------------------------------
+-----------------------------------------------------------------------
+
+extern void * http_malloc (int size, int id);
+
+mallocs a new buffer of size 'size', with an allocation ID 'id'. Ids can
+be used to debug memory leaks - the allocation and freeing ID must be
+the same, and must be under MAX_ALLOC_ID, no other restrictions. TUX 
+provides runtime statistics about various ID allocation patterns and
+allocation balance. This function can potentially block, so make careful
+use of it.
+
+RETURN VALUES:
+
+         the allocated buffer
+
+-----------------------------------------------------------------------
+
+extern void http_free (void *ptr, int id);
+
+free a http_malloc()-ed temporary buffer.
+
+
+RETURN VALUES:
+
+        none
+
+-----------------------------------------------------------------------
+
+int http_exec_process (char *command, char **argv, char **envp, int *unused1, exec_param_t *unused2, int wait);
+
+starts and exec()s a new user-space process, pointed to by 'command', with
+argument array of 'argv', environment array 'envp'. If 'wait' is 1 then TUX
+waits for the process to exit, otherwise it's running asynchronously.
+
+RETURN VALUES:
+
+        0 on success
+        non-zero on failure
+
+-----------------------------------------------------------------------
+
+http_open_file():
+
+struct http_file * http_open_file (char *filename, int mode);
+
+opens a file descriptor pointed to by 'filename', with file mode 'mode'.
+
+RETURN VALUES:
+
+        the opened file descriptor or NULL on failure
+
+-----------------------------------------------------------------------
+
+int http_read (urlc_t *urlc, char *buf)
+
+read a full HTTP object into a sufficiently sized temporary buffer.
+
+RETURN VALUES:
+
+        0 on success
+        non-zero on failure
+
+-----------------------------------------------------------------------
+
+int http_send_client (http_req_t *req, char *buf, int len, int push)
+
+sends a given buffer's contents to the client as-is. If 'push' is 1
+then the content is 'pushed' to the client.
+
+RETURN VALUES:
+
+        >=0 bytes sent
+        <0 on error
+
+-----------------------------------------------------------------------
+
+int http_send_file (http_req_t *req, int include_header, int push)
+
+sends a given HTTP object to the client as a reply. include_headers
+specifies wether TUX should construct a header. The 'push' argument
+specifies wether the TCP data should be pushed to the client.
+
+RETURN VALUES:
+
+        0: success
+       -1: failure
+
+-----------------------------------------------------------------------
+
+struct http_direntry * http_lookup_direntry (char *pathname,
+                struct http_nameidata *docroot, int flags)
+
+looks up a given file identified by pathname, starting at docroot, and
+returns a direntry pointer to it. This pointer can later on be used to
+do file operations.
+
+RETURN VALUES:
+
+        the looked up directory entry on success
+        non-zero http_direntry_error() on failure
+
+-----------------------------------------------------------------------
+
+int http_direntry_error (struct http_direntry * dentry)
+
+converts the error code embedded in the dentry pointer to an integer
+error code.
+
+RETURN VALUES:
+
+        0: the dentry is valid
+        nonzero: the dentry lookup had an error
+
+-----------------------------------------------------------------------
+
+int http_file_size (struct http_file *file)
+
+returns the length of the file.
+
+-----------------------------------------------------------------------
+
+unsigned int http_mtime (struct http_file *file)
+
+returns the last modification time of the file, in Unix time.
+
+-----------------------------------------------------------------------
+
+int http_write_file (struct http_file *file, char *buf, int len)
+
+writes a given buffer's contents into the file, and updates the file
+position.
+
+RETURN VALUES:
+
+        >0 bytes written
+        <=0 on error
+
+-----------------------------------------------------------------------
+
+int http_read_file (struct http_file *file, char *buf, int len)
+
+reads a file (from the current position) into a given buffer and
+updates the file position.
+
+RETURN VALUES:
+
+        >0 bytes read
+        <=0 on error
+
+-----------------------------------------------------------------------
+
+void http_close_file (struct http_file *file)
+
+closes a file descriptor. (usage of the file pointer after closing the
+file may result in undefined behavior.)
+
+-----------------------------------------------------------------------
+
+struct http_page * http_mmap_page (struct http_file *file, char *buf,
+                                unsigned int offset)
+
+mmaps a given page at a given offset from a given file into the
+process's address space.
+
+RETURN VALUES:
+
+        NULL: error
+        non-NULL: pointer to the page structure
+
+-----------------------------------------------------------------------
+
+int http_miss_req (struct http_req_t *req)
+
+the module signals towards TUX that the object described via
+req->filename should be constructed by TUX.
+
+RETURN VALUES:
+
+         0: request parsed, continue with output
+
+        -1: input data incomplete, put socket into idle state
+
+        -2: request finished, flush now
+
+        (ie. can be used as a ->query() return value.)
+
+-----------------------------------------------------------------------
+
+void http_sleep (int seconds)
+
+suspends execution for a given number of seconds.
+
+-----------------------------------------------------------------------
+
+void http_down (struct http_mutex *mutex)
+
+'down' operation on the mutex (enters critical section). Suspends
+execution if the critical section is already entered.
+
+-----------------------------------------------------------------------
+
+void http_up (struct http_mutex *mutex)
+
+'up' operation on the mutex. (release critical section)
+
+-----------------------------------------------------------------------
+
+unsigned int http_client_addr (http_req_t *req)
+
+retrieve the IP address of the client connection 'req'.
+
+RETURN VALUES:
+
+        the client IP address in host-endian format
+
+-----------------------------------------------------------------------
+
--- linux/drivers/net/sk98lin/h/skdrv2nd.h.orig	Wed Feb  9 03:58:25 2000
+++ linux/drivers/net/sk98lin/h/skdrv2nd.h	Fri Sep  1 07:28:26 2000
@@ -131,8 +131,8 @@
  * define sizes of descriptor rings in bytes
  */
 
-#define		TX_RING_SIZE	(8*1024)
-#define		RX_RING_SIZE	(24*1024)
+#define		TX_RING_SIZE	(256*1024)
+#define		RX_RING_SIZE	(256*1024)
 
 /*
  * Buffer size for ethernet packets
--- linux/drivers/net/sk98lin/skge.c.orig	Fri Sep  1 07:26:56 2000
+++ linux/drivers/net/sk98lin/skge.c	Fri Sep  1 07:28:26 2000
@@ -137,7 +137,7 @@
  *	Fixed pci config space accesses.
  *	
  *	Revision 1.4  1999/02/18 15:48:44  cgoos
- *	Corrected some printk's.
+ *	Corrected some Dprintk's.
  *	
  *	Revision 1.3  1999/02/18 12:45:55  cgoos
  *	Changed SK_MAX_CARD_PARAM to default 16
@@ -233,6 +233,8 @@
 #include	"h/skdrv1st.h"
 #include	"h/skdrv2nd.h"
 
+#include	<net/http.h>
+
 /* defines ******************************************************************/
 
 #define BOOT_STRING	"sk98lin: Network Device Driver v3.02\n" \
@@ -248,14 +250,15 @@
 #define USE_TX_COMPLETE
 
 /* use interrupt moderation (for tx complete only) */
-// #define USE_INT_MOD
-#define INTS_PER_SEC	1000
+#define USE_INT_MOD
+#define INTS_PER_SEC	300000
 
 /*
  * threshold for copying small receive frames
  * set to 0 to avoid copying, set to 9001 to copy all frames
  */
-#define SK_COPY_THRESHOLD	200
+#define SK_COPY_THRESHOLD	0
+//#define SK_COPY_THRESHOLD	200
 
 /* number of adapters that can be configured via command line params */
 #define SK_MAX_CARD_PARAM	16
@@ -363,24 +366,30 @@
 		/* set display flag to TRUE so that */
 		/* we only display this string ONCE */
 		version_disp = 1;
-		printk("%s\n", BootString);
+		Dprintk("%s\n", BootString);
 	}
 
 	if (!pci_present())		/* is PCI support present? */
 		return -ENODEV;
 
-	while((pdev = pci_find_device(PCI_VENDOR_ID_SYSKONNECT,
-				      PCI_DEVICE_ID_SYSKONNECT_GE, pdev)) != NULL) {
-		if (pci_enable_device(pdev))
+	while((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev)))
+	{
+		dev = NULL;
+
+		if (pdev->vendor != PCI_VENDOR_ID_SYSKONNECT || 
+			pdev->device != PCI_DEVICE_ID_SYSKONNECT_GE) {
 			continue;
+		}
 		dev = init_etherdev(dev, sizeof(SK_AC));
 
-		if (dev == NULL) {
+		if (dev == NULL || dev->priv == NULL){
 			printk(KERN_ERR "Unable to allocate etherdev "
 			       "structure!\n");
 			break;
 		}
 
+		memset(dev->priv, 0, sizeof(SK_AC));
+
 		pAC = dev->priv;
 		pAC->PciDev = *pdev;
 		pAC->PciDevId = pdev->device;
@@ -393,6 +402,7 @@
 		dev->open =		&SkGeOpen;
 		dev->stop =		&SkGeClose;
 		dev->hard_start_xmit =	&SkGeXmit;
+		dev->hard_start_xmit_dual = &SkGeXmit;
 		dev->get_stats =	&SkGeStats;
 		dev->set_multicast_list = &SkGeSetRxMode;
 		dev->set_mac_address =	&SkGeSetMacAddr;
@@ -406,7 +416,7 @@
 
 		pci_set_master(pdev);
 
-		base_address = pci_resource_start (pdev, 0);
+		base_address = pdev->resource[0].start;
 
 #ifdef SK_BIG_ENDIAN
 		/*
@@ -428,7 +438,7 @@
 
 		pAC->IoBase = (char*)ioremap(base_address, 0x4000);
 		if (!pAC->IoBase){
-			printk(KERN_ERR "%s:  Unable to map I/O register, "
+			Dprintk(KERN_ERR "%s:  Unable to map I/O register, "
 			       "SK 98xx No. %i will be disabled.\n",
 			       dev->name, boards_found);
 			break;
@@ -602,7 +612,7 @@
 
 	cards = skge_probe();
 	if (cards == 0) {
-		printk("No adapter found\n");
+		Dprintk("No adapter found\n");
 	}
 	return cards ? 0 : -ENODEV;
 } /* skge_init_module */
@@ -711,7 +721,7 @@
 	spin_lock_irqsave(&pAC->SlowPathLock, Flags);
 	/* Does a RESET on board ...*/
 	if (SkGeInit(pAC, pAC->IoBase, 0) != 0) {
-		printk("HWInit (0) failed.\n");
+		Dprintk("HWInit (0) failed.\n");
 		spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
 		return(-EAGAIN);
 	}
@@ -735,7 +745,7 @@
 	/* level 1 init common modules here (HW init) */
 	spin_lock_irqsave(&pAC->SlowPathLock, Flags);
 	if (SkGeInit(pAC, pAC->IoBase, 1) != 0) {
-		printk("HWInit (1) failed.\n");
+		Dprintk("HWInit (1) failed.\n");
 		spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
 		return(-EAGAIN);
 	}
@@ -755,12 +765,12 @@
 		Ret = request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ,
 			pAC->Name, dev);
 	} else {
-		printk(KERN_WARNING "%s: illegal number of ports: %d\n",
+		Dprintk(KERN_WARNING "%s: illegal number of ports: %d\n",
 		       dev->name, pAC->GIni.GIMacsFound);
 		return -EAGAIN;
 	}
 	if (Ret) {
-		printk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
+		Dprintk(KERN_WARNING "%s: Requested IRQ %d is busy\n",
 		       dev->name, dev->irq);
 		return -EAGAIN;
 	}
@@ -768,7 +778,7 @@
 
 	/* Alloc memory for this board (Mem for RxD/TxD) : */
 	if(!BoardAllocMem(pAC)) {
-		printk("No memory for descriptor rings\n");
+		Dprintk("No memory for descriptor rings\n");
        		return(-EAGAIN);
 	}
 
@@ -783,7 +793,7 @@
 
 	/* Print adapter specific string from vpd */
 	ProductStr(pAC);
-	printk("%s: %s\n", dev->name, pAC->DeviceStr);
+	Dprintk("%s: %s\n", dev->name, pAC->DeviceStr);
 
 	SkGeYellowLED(pAC, pAC->IoBase, 1);
 
@@ -1354,7 +1364,7 @@
 	if (pAC->BoardLevel == 0) {
 		/* level 1 init common modules here */
 		if (SkGeInit(pAC, pAC->IoBase, 1) != 0) {
-			printk("%s: HWInit(1) failed\n", pAC->dev->name);
+			Dprintk("%s: HWInit(1) failed\n", pAC->dev->name);
 			return (-1);
 		}
 		SkI2cInit	(pAC, pAC->IoBase, 1);
@@ -1385,7 +1395,8 @@
 
 #ifdef USE_INT_MOD
 // moderate only TX complete interrupts (these are not time critical)
-#define IRQ_MOD_MASK (IRQ_EOF_AS_TX1 | IRQ_EOF_AS_TX2)
+//#define IRQ_MOD_MASK (IRQ_EOF_AS_TX1 | IRQ_EOF_AS_TX2)
+#define IRQ_MOD_MASK (IRQ_EOF_AS_TX1 | IRQ_EOF_AS_TX2 | IRQ_EOF_RX1 | IRQ_EOF_RX2)
 	{
 		unsigned long ModBase;
 		ModBase = 53125000 / INTS_PER_SEC;
@@ -1498,7 +1509,7 @@
 {
 SK_AC		*pAC;
 int		Rc;	/* return code of XmitFrame */
-	
+
 	pAC = (SK_AC*) dev->priv;
 
 	Rc = XmitFrame(pAC, &pAC->TxPort[pAC->ActivePort][TX_PRIO_LOW], skb);
@@ -1543,7 +1554,7 @@
 static int XmitFrame(
 SK_AC 		*pAC,		/* pointer to adapter context */
 TX_PORT		*pTxPort,	/* pointer to struct of port to send to */
-struct sk_buff	*pMessage)	/* pointer to send-message */
+struct sk_buff	*skb)	/* pointer to send-message */
 {
 TXD		*pTxd;		/* the rxd to fill */
 unsigned int	Flags;
@@ -1555,10 +1566,10 @@
 
 	spin_lock_irqsave(&pTxPort->TxDesRingLock, Flags);
 
-	if (pTxPort->TxdRingFree == 0) {
+	if (pTxPort->TxdRingFree <= MAX_SKB_FRAGS) {
 		/* no enough free descriptors in ring at the moment */
 		FreeTxDescriptors(pAC, pTxPort);
-		if (pTxPort->TxdRingFree == 0) {
+		if (pTxPort->TxdRingFree <= MAX_SKB_FRAGS) {
 			spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags);
 			SK_PNMI_CNT_NO_TX_BUF(pAC);
 			SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
@@ -1579,25 +1590,79 @@
 	 */
 
 #ifdef SK_DUMP_TX
-	DumpMsg(pMessage, "XmitFrame");
+	DumpMsg(skb, "XmitFrame");
 #endif
 
 	/* set up descriptor and CONTROL dword */
 	PhysAddr = (SK_U64) pci_map_single(&pAC->PciDev,
-					   pMessage->data,
-					   pMessage->len,
+					   skb->data,
+					   skb->len-skb->data_len,
 					   PCI_DMA_TODEVICE);
 	pTxd->VDataLow = (SK_U32)  (PhysAddr & 0xffffffff);
 	pTxd->VDataHigh = (SK_U32) (PhysAddr >> 32);
-	pTxd->pMBuf = pMessage;
-	pTxd->TBControl = TX_CTRL_OWN_BMU | TX_CTRL_STF |
-		TX_CTRL_CHECK_DEFAULT | TX_CTRL_SOFTWARE |
+
+	if (!skb->nr_frags) {
+		Dprintk("send SKB normal SKGE: skb %p: len:%d, data_len:%d, head:%p, data:%p, tail:%p, end:%p.\n", skb, skb->len, skb->data_len, skb->head, skb->data, skb->tail, skb->end);
+		if (skb->data_len)
+			BUG();
+		pTxd->pMBuf = skb;
+		pTxd->TBControl = TX_CTRL_OWN_BMU | TX_CTRL_STF |
+			TX_CTRL_CHECK_DEFAULT | TX_CTRL_SOFTWARE |
 #ifdef USE_TX_COMPLETE
-		TX_CTRL_EOF | TX_CTRL_EOF_IRQ | pMessage->len;
+			TX_CTRL_EOF | TX_CTRL_EOF_IRQ | skb->len;
 #else
-		TX_CTRL_EOF | pMessage->len;
+			TX_CTRL_EOF | skb->len;
 #endif
-	
+	} else {
+		int i, len;
+
+		Dprintk("SKGE: skb %p: len:%d, data_len:%d, head:%p, data:%p, tail:%p, end:%p.\n", skb, skb->len, skb->data_len, skb->head, skb->data, skb->tail, skb->end);
+		if (skb->tail - skb->data != skb->len - skb->data_len)
+			BUG();
+		if (skb->tail == skb->data)
+			BUG();
+		/*
+		 * No end of fragment flag.
+		 */
+		pTxd->TBControl = TX_CTRL_OWN_BMU | TX_CTRL_STF |
+			/*TX_CTRL_EOB_IRQ |*/ TX_CTRL_CHECK_DEFAULT |
+			TX_CTRL_SOFTWARE | (skb->len - skb->data_len);
+
+		len = 0;
+		for (i = 0; i < skb->nr_frags; i++) {
+			unsigned long control_bits;
+			skb_frag_t *frag = skb->frags[i];
+
+			len += frag->size;
+			pTxd = pTxPort->pTxdRingHead;
+			pTxPort->pTxdRingHead = pTxd->pNextTxd;
+			if (!pTxPort->TxdRingFree)
+				BUG();
+			pTxPort->TxdRingFree--;
+
+			PhysAddr = (frag->page-mem_map) *
+				(unsigned long long) PAGE_SIZE + frag->page_offset;
+			pTxd->VDataLow = (SK_U32)  (PhysAddr & 0xffffffff);
+			pTxd->VDataHigh = (SK_U32) (PhysAddr >> 32);
+			control_bits = TX_CTRL_OWN_BMU | TX_CTRL_STF |
+				TX_CTRL_CHECK_DEFAULT | TX_CTRL_SOFTWARE | 
+#ifdef USE_TX_COMPLETE
+				/*TX_CTRL_EOF_IRQ |*/ frag->size;
+#else
+				frag->size;
+#endif
+			if (i == skb->nr_frags-1) {
+				// last fragment triggers an IRQ and should
+				// free the skb
+				pTxd->pMBuf = skb;
+				control_bits |= TX_CTRL_EOF_IRQ | TX_CTRL_EOF;
+			}
+			pTxd->TBControl = control_bits;
+		}
+		if (len != skb->data_len)
+			BUG();
+	}
+
 	if ((pTxPort->pTxdRingPrev->TBControl & TX_CTRL_OWN_BMU) == 0) {
 		/* previous descriptor already done, so give tx start cmd */
 		/* StartTx(pAC, pTxPort->HwAddr); */
@@ -1606,9 +1671,9 @@
 	pTxPort->pTxdRingPrev = pTxd;
 	
 	
-	BytesSend = pMessage->len;
+	BytesSend = skb->len;
 	/* after releasing the lock, the skb may be immidiately freed */
-	if (pTxPort->TxdRingFree != 0) {
+	if (pTxPort->TxdRingFree > MAX_SKB_FRAGS) {
 		spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags);
 		return (BytesSend);
 	}
@@ -1656,23 +1721,19 @@
 	 */
 	while (1) {
 		Control = pTxd->TBControl;
-		if ((Control & TX_CTRL_SOFTWARE) == 0) {
+		if (!(Control & TX_CTRL_SOFTWARE)) {
 			/* 
 			 * software controllable bit is set in first
 			 * fragment when given to BMU. Not set means that
 			 * this fragment was never sent or is already 
 			 * freed ( -> ring completely free now).
 			 */
-			pTxPort->pTxdRingTail = pTxd;
-			netif_start_queue(pAC->dev);
-			return;
+			Dprintk("stopped freeing Tx at SK TX descriptor %p due to !CTRL_SOFTWARE.\n", pTxd);
+			break;
 		}
 		if (Control & TX_CTRL_OWN_BMU) {
-			pTxPort->pTxdRingTail = pTxd;
-			if (pTxPort->TxdRingFree > 0) {
-				netif_start_queue(pAC->dev);
-			}
-			return;
+			Dprintk("stopped freeing Tx at SK TX descriptor %p due to OWN_BMU.\n", pTxd);
+			break;
 		}
 		
 		/* release the DMA mapping */
@@ -1683,11 +1744,25 @@
 				 PCI_DMA_TODEVICE);
 
 		/* free message */
-		DEV_KFREE_SKB_ANY(pTxd->pMBuf);
+		{
+			struct sk_buff *skb;
+			skb = (struct sk_buff *)pTxd->pMBuf;
+			Dprintk("free SK TX descriptor %p (skb %p).\n",
+						pTxd, skb);
+			if (skb) {
+				pTxd->pMBuf = NULL;
+				DEV_KFREE_SKB_ANY(skb);
+				Dprintk("SKGE: FREE skb %p: len:%d, data_len:%d, head:%p, data:%p, tail:%p, end:%p - physaddr: %08lx.\n", skb, skb->len, skb->data_len, skb->head, skb->data, skb->tail, skb->end, (long)PhysAddr);
+			}
+		}
 		pTxPort->TxdRingFree++;
 		pTxd->TBControl &= ~TX_CTRL_SOFTWARE;
 		pTxd = pTxd->pNextTxd; /* point behind fragment with EOF */
 	} /* while(forever) */
+
+	pTxPort->pTxdRingTail = pTxd;
+	if (pTxPort->TxdRingFree > MAX_SKB_FRAGS)
+		netif_start_queue(pAC->dev);
 } /* FreeTxDescriptors */
 
 
@@ -2029,13 +2104,13 @@
 		else {
 			/* there is a receive error in this frame */
 			if ((FrameStat & XMR_FS_1L_VLAN) != 0) {
-				printk("%s: Received frame"
+				Dprintk("%s: Received frame"
 					" with VLAN Level 1 header, check"
 					" switch configuration\n",
 					pAC->dev->name);
 			}
 			if ((FrameStat & XMR_FS_2L_VLAN) != 0) {
-				printk("%s: Received frame"
+				Dprintk("%s: Received frame"
 					" with VLAN Level 2 header, check"
 					" switch configuration\n",
 					pAC->dev->name);
@@ -2740,7 +2815,7 @@
 		else if (strcmp(AutoNeg_A[pAC->Index],"Sense")==0) {
 			AutoNeg = AN_SENS;
 		}
-		else printk("%s: Illegal value for AutoNeg_A\n",
+		else Dprintk("%s: Illegal value for AutoNeg_A\n",
 			pAC->dev->name);
 	}
 
@@ -2761,17 +2836,17 @@
 		else if (strcmp(DupCap_A[pAC->Index],"Half")==0) {
 			DuplexCap = DC_HALF;
 		}
-		else printk("%s: Illegal value for DupCap_A\n",
+		else Dprintk("%s: Illegal value for DupCap_A\n",
 			pAC->dev->name);
 	}
 	
 	/* check for illegal combinations */
 	if (AutoSet && AutoNeg==AN_SENS && DupSet) {
-		printk("%s, Port A: DuplexCapabilities"
+		Dprintk("%s, Port A: DuplexCapabilities"
 			" ignored using Sense mode\n", pAC->dev->name);
 	}
 	if (AutoSet && AutoNeg==AN_OFF && DupSet && DuplexCap==DC_BOTH){
-		printk("%s, Port A: Illegal combination"
+		Dprintk("%s, Port A: Illegal combination"
 			" of values AutoNeg. and DuplexCap.\n    Using "
 			"Full Duplex\n", pAC->dev->name);
 
@@ -2782,7 +2857,7 @@
 	}
 	
 	if (!AutoSet && DupSet) {
-		printk("%s, Port A: Duplex setting not"
+		Dprintk("%s, Port A: Duplex setting not"
 			" possible in\n    default AutoNegotiation mode"
 			" (Sense).\n    Using AutoNegotiation On\n",
 			pAC->dev->name);
@@ -2814,11 +2889,11 @@
 			pAC->GIni.GP[0].PFlowCtrlMode =
 				SK_FLOW_MODE_NONE;
 		}
-		else printk("Illegal value for FlowCtrl_A\n");
+		else Dprintk("Illegal value for FlowCtrl_A\n");
 	}
 	if (AutoNeg==AN_OFF && pAC->GIni.GP[0].PFlowCtrlMode!=
 		SK_FLOW_MODE_NONE) {
-		printk("%s, Port A: FlowControl"
+		Dprintk("%s, Port A: FlowControl"
 			" impossible without AutoNegotiation,"
 			" disabled\n", pAC->dev->name);
 		pAC->GIni.GP[0].PFlowCtrlMode = SK_FLOW_MODE_NONE;
@@ -2838,7 +2913,7 @@
 		else if (strcmp(Role_A[pAC->Index],"Slave")==0) {
 			MSMode = SK_MS_MODE_SLAVE;
 		}
-		else printk("%s: Illegal value for Role_A\n",
+		else Dprintk("%s: Illegal value for Role_A\n",
 			pAC->dev->name);
 	}
 	pAC->GIni.GP[0].PMSMode = MSMode;
@@ -2862,7 +2937,7 @@
 		else if (strcmp(AutoNeg_B[pAC->Index],"Sense")==0) {
 			AutoNeg = AN_SENS;
 		}
-		else printk("Illegal value for AutoNeg_B\n");
+		else Dprintk("Illegal value for AutoNeg_B\n");
 	}
 
 	DuplexCap = DC_BOTH;
@@ -2882,16 +2957,16 @@
 		else if (strcmp(DupCap_B[pAC->Index],"Half")==0) {
 			DuplexCap = DC_HALF;
 		}
-		else printk("Illegal value for DupCap_B\n");
+		else Dprintk("Illegal value for DupCap_B\n");
 	}
 	
 	/* check for illegal combinations */
 	if (AutoSet && AutoNeg==AN_SENS && DupSet) {
-		printk("%s, Port B: DuplexCapabilities"
+		Dprintk("%s, Port B: DuplexCapabilities"
 			" ignored using Sense mode\n", pAC->dev->name);
 	}
 	if (AutoSet && AutoNeg==AN_OFF && DupSet && DuplexCap==DC_BOTH){
-		printk("%s, Port B: Illegal combination"
+		Dprintk("%s, Port B: Illegal combination"
 			" of values AutoNeg. and DuplexCap.\n    Using "
 			"Full Duplex\n", pAC->dev->name);
 
@@ -2902,7 +2977,7 @@
 	}
 	
 	if (!AutoSet && DupSet) {
-		printk("%s, Port B: Duplex setting not"
+		Dprintk("%s, Port B: Duplex setting not"
 			" possible in\n    default AutoNegotiation mode"
 			" (Sense).\n    Using AutoNegotiation On\n",
 			pAC->dev->name);
@@ -2934,11 +3009,11 @@
 			pAC->GIni.GP[1].PFlowCtrlMode =
 				SK_FLOW_MODE_NONE;
 		}
-		else printk("Illegal value for FlowCtrl_B\n");
+		else Dprintk("Illegal value for FlowCtrl_B\n");
 	}
 	if (AutoNeg==AN_OFF && pAC->GIni.GP[1].PFlowCtrlMode!=
 		SK_FLOW_MODE_NONE) {
-		printk("%s, Port B: FlowControl"
+		Dprintk("%s, Port B: FlowControl"
 			" impossible without AutoNegotiation,"
 			" disabled\n", pAC->dev->name);
 		pAC->GIni.GP[1].PFlowCtrlMode = SK_FLOW_MODE_NONE;
@@ -2958,7 +3033,7 @@
 		else if (strcmp(Role_B[pAC->Index],"Slave")==0) {
 			MSMode = SK_MS_MODE_SLAVE;
 		}
-		else printk("%s: Illegal value for Role_B\n",
+		else Dprintk("%s: Illegal value for Role_B\n",
 			pAC->dev->name);
 	}
 	pAC->GIni.GP[1].PMSMode = MSMode;
@@ -2991,7 +3066,7 @@
 			pAC->Rlmt.MacPreferred = Port;
 			pAC->Rlmt.PrefPort = Port;
 		}
-		else printk("%s: Illegal value for PrefPort\n",
+		else Dprintk("%s: Illegal value for PrefPort\n",
 			pAC->dev->name);
 	}
 	
@@ -3013,7 +3088,7 @@
 				SK_RLMT_CHECK_SEG;
 		}
 		else {
-			printk("%s: Illegal value for"
+			Dprintk("%s: Illegal value for"
 				" RlmtMode, using default\n", pAC->dev->name);
 			pAC->RlmtMode = 0;
 		}
@@ -3316,7 +3391,7 @@
 	case SK_DRV_ADAP_FAIL:
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("ADAPTER FAIL EVENT\n"));
-		printk("%s: Adapter failed.\n", pAC->dev->name);
+		Dprintk("%s: Adapter failed.\n", pAC->dev->name);
 		/* disable interrupts */
 		SK_OUT32(pAC->IoBase, B0_IMSK, 0);
 		/* cgoos */
@@ -3326,9 +3401,9 @@
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("PORT FAIL EVENT, Port: %d\n", FromPort));
 		if (FromPort == 0) {
-			printk("%s: Port A failed.\n", pAC->dev->name);
+			Dprintk("%s: Port A failed.\n", pAC->dev->name);
 		} else {
-			printk("%s: Port B failed.\n", pAC->dev->name);
+			Dprintk("%s: Port B failed.\n", pAC->dev->name);
 		}
 		/* cgoos */
 		break;
@@ -3368,47 +3443,47 @@
 		FromPort = Param.Para32[0];
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("NET UP EVENT, Port: %d ", Param.Para32[0]));
-		printk("%s: network connection up using"
+		Dprintk("%s: network connection up using"
 			" port %c\n", pAC->dev->name, 'A'+Param.Para32[0]);
-		printk("    speed:           1000\n");
+		Dprintk("    speed:           1000\n");
 		Stat = pAC->GIni.GP[FromPort].PLinkModeStatus;
 		if (Stat == SK_LMODE_STAT_AUTOHALF ||
 			Stat == SK_LMODE_STAT_AUTOFULL) {
-			printk("    autonegotiation: yes\n");
+			Dprintk("    autonegotiation: yes\n");
 		}
 		else {
-			printk("    autonegotiation: no\n");
+			Dprintk("    autonegotiation: no\n");
 		}
 		if (Stat == SK_LMODE_STAT_AUTOHALF ||
 			Stat == SK_LMODE_STAT_HALF) {
-			printk("    duplex mode:     half\n");
+			Dprintk("    duplex mode:     half\n");
 		}
 		else {
-			printk("    duplex mode:     full\n");
+			Dprintk("    duplex mode:     full\n");
 		}
 		Stat = pAC->GIni.GP[FromPort].PFlowCtrlStatus;
 		if (Stat == SK_FLOW_STAT_REM_SEND ) {
-			printk("    flowctrl:        remote send\n");
+			Dprintk("    flowctrl:        remote send\n");
 		}
 		else if (Stat == SK_FLOW_STAT_LOC_SEND ){
-			printk("    flowctrl:        local send\n");
+			Dprintk("    flowctrl:        local send\n");
 		}
 		else if (Stat == SK_FLOW_STAT_SYMMETRIC ){
-			printk("    flowctrl:        symmetric\n");
+			Dprintk("    flowctrl:        symmetric\n");
 		}
 		else {
-			printk("    flowctrl:        none\n");
+			Dprintk("    flowctrl:        none\n");
 		}
 		if (pAC->GIni.GP[FromPort].PhyType != SK_PHY_XMAC) {
 		Stat = pAC->GIni.GP[FromPort].PMSStatus;
 			if (Stat == SK_MS_STAT_MASTER ) {
-				printk("    role:            master\n");
+				Dprintk("    role:            master\n");
 			}
 			else if (Stat == SK_MS_STAT_SLAVE ) {
-				printk("    role:            slave\n");
+				Dprintk("    role:            slave\n");
 			}
 			else {
-				printk("    role:            ???\n");
+				Dprintk("    role:            ???\n");
 			}
 		}
 		
@@ -3423,14 +3498,14 @@
 		/* action list 7 */
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("NET DOWN EVENT "));
-		printk("%s: network connection down\n", pAC->dev->name);
+		Dprintk("%s: network connection down\n", pAC->dev->name);
 		break;
 	case SK_DRV_SWITCH_HARD: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */
 		SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_EVENT,
 			("PORT SWITCH HARD "));
 	case SK_DRV_SWITCH_SOFT: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */
 	/* action list 6 */
-		printk("%s: switching to port %c\n", pAC->dev->name,
+		Dprintk("%s: switching to port %c\n", pAC->dev->name,
 			'A'+Param.Para32[1]);
 	case SK_DRV_SWITCH_INTERN: /* SK_U32 FromPortIdx SK_U32 ToPortIdx */
 		FromPort = Param.Para32[0];
@@ -3551,7 +3626,7 @@
 		strcpy(ClassStr, "Communication error");
 		break;
 	}
-	printk(KERN_INFO "%s: -- ERROR --\n        Class:  %s\n"
+	Dprintk(KERN_INFO "%s: -- ERROR --\n        Class:  %s\n"
 		"        Nr:  0x%x\n        Msg:  %s\n", pAC->dev->name,
 		ClassStr, ErrNum, pErrorMsg);
 
@@ -3577,12 +3652,12 @@
 	int	msglen;
 
 	if (skb == NULL) {
-		printk("DumpMsg(): NULL-Message\n");
+		Dprintk("DumpMsg(): NULL-Message\n");
 		return;
 	}
 
 	if (skb->data == NULL) {
-		printk("DumpMsg(): Message empty\n");
+		Dprintk("DumpMsg(): Message empty\n");
 		return;
 	}
 
@@ -3590,11 +3665,11 @@
 	if (msglen > 64)
 		msglen = 64;
 
-	printk("--- Begin of message from %s , len %d (from %d) ----\n", str, msglen, skb->len);
+	Dprintk("--- Begin of message from %s , len %d (from %d) ----\n", str, msglen, skb->len);
 
 	DumpData((char *)skb->data, msglen);
 
-	printk("------- End of message ---------\n");
+	Dprintk("------- End of message ---------\n");
 } /* DumpMsg */
 
 
@@ -3639,7 +3714,7 @@
 		p++;
 		i++;
 		if (i%16 == 0) {
-			printk("%s  %s\n", hex_buffer, asc_buffer);
+			Dprintk("%s  %s\n", hex_buffer, asc_buffer);
 			addr = 0;
 			haddr = 0;
 		}
@@ -3697,11 +3772,11 @@
 		p++;
 		i++;
 		if (i%8 == 0) {
-			printk("%4x %s\n", (i-8)*4, hex_buffer);
+			Dprintk("%4x %s\n", (i-8)*4, hex_buffer);
 			haddr = 0;
 		}
 	}
-	printk("------------------------\n");
+	Dprintk("------------------------\n");
 } /* DumpLong */
 
 #endif /* DEBUG */
--- linux/drivers/net/eepro100.c.orig	Fri Sep  1 07:27:03 2000
+++ linux/drivers/net/eepro100.c	Fri Sep  1 07:28:26 2000
@@ -1532,17 +1532,21 @@
 
 	do {
 		status = inw(ioaddr + SCBStatus);
-		/* Acknowledge all of the current interrupt sources ASAP. */
-		/* Will change from 0xfc00 to 0xff00 when we start handling
-		   FCP and ER interrupts --Dragan */
-		outw(status & 0xfc00, ioaddr + SCBStatus);
-
 		if (speedo_debug > 4)
 			printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
 				   dev->name, status);
 
+		/*
+		 * For the sake of performance during shared interrupts
+		 * we first check wether there is any work pending.
+		 */
 		if ((status & 0xfc00) == 0)
 			break;
+
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		/* Will change from 0xfc00 to 0xff00 when we start handling
+		   FCP and ER interrupts --Dragan */
+		outw(status & 0xfc00, ioaddr + SCBStatus);
 
 		/* Always check if all rx buffers are allocated.  --SAW */
 		speedo_refill_rx_buffers(dev, 0);
--- linux/drivers/net/acenic.c.orig	Fri Sep  1 07:26:56 2000
+++ linux/drivers/net/acenic.c	Fri Sep  1 07:28:26 2000
@@ -48,7 +48,8 @@
 #include <linux/mm.h>
 
 #undef ETHTOOL
-#undef INDEX_DEBUG
+//#define INDEX_DEBUG
+#define TX_DEBUG 0
 
 #ifdef ETHTOOL
 #include <linux/ethtool.h>
@@ -97,7 +98,7 @@
 #endif
 
 #ifndef wmb
-#define wmb()	mb()
+#define wmb()	wmb()
 #endif
 
 #ifndef __exit
@@ -182,6 +183,8 @@
 
 #include "acenic_firmware.h"
 
+#define dprintk(x...) do { } while (0)
+
 /*
  * This driver currently supports Tigon I and Tigon II based cards
  * including the Alteon AceNIC, the 3Com 3C985[B] and NetGear
@@ -375,18 +378,42 @@
 #define DEF_JUMBO_RX_MAX_DESC	6
 #define DEF_JUMBO_TX_RATIO	21
 
-#define TX_COAL_INTS_ONLY	0	/* seems not worth it */
+#define TX_COAL_INTS_ONLY	1	/* seems not worth it */
 #define DEF_TRACE		0
 #define DEF_STAT		(2 * TICKS_PER_SEC)
 
 static int link[ACE_MAX_MOD_PARMS] = {0, };
 static int trace[ACE_MAX_MOD_PARMS] = {0, };
-static int tx_coal_tick[ACE_MAX_MOD_PARMS] = {0, };
-static int rx_coal_tick[ACE_MAX_MOD_PARMS] = {0, };
-static int max_tx_desc[ACE_MAX_MOD_PARMS] = {0, };
-static int max_rx_desc[ACE_MAX_MOD_PARMS] = {0, };
-static int tx_ratio[ACE_MAX_MOD_PARMS] = {0, };
-static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
+static int tx_coal_tick[ACE_MAX_MOD_PARMS] =
+			 { [0 ... ACE_MAX_MOD_PARMS-1] = 400 };
+static int rx_coal_tick[ACE_MAX_MOD_PARMS] =
+			 { [0 ... ACE_MAX_MOD_PARMS-1] = 200 };
+static int max_tx_desc[ACE_MAX_MOD_PARMS] =
+			 { [0 ... ACE_MAX_MOD_PARMS-1] = 32 };
+static int max_rx_desc[ACE_MAX_MOD_PARMS] =
+			 { [0 ... ACE_MAX_MOD_PARMS-1] = 16 };
+static int tx_ratio[ACE_MAX_MOD_PARMS] =
+			 { [0 ... ACE_MAX_MOD_PARMS-1] = 56 /*56*/ };
+static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] =
+			 { [0 ... ACE_MAX_MOD_PARMS-1] = 0 };
+
+static int __init ace_coal_setup (char *str)
+{
+	int par;
+
+	if (get_option(&str,&par) >= 0) {
+		int i;
+
+		for (i = 0; i < ACE_MAX_MOD_PARMS; i++) {
+			tx_coal_tick[i] = par;
+			rx_coal_tick[i] = par;
+		}
+	}
+        return 1;
+}
+
+__setup("ace_coal=", ace_coal_setup);
+
 
 static const char __initdata *version = 
   "acenic.c: v0.44 05/11/2000  Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
@@ -461,6 +488,7 @@
 		dev->irq = pdev->irq;
 		dev->open = &ace_open;
 		dev->hard_start_xmit = &ace_start_xmit;
+		dev->hard_start_xmit_dual = &ace_start_xmit;
 		dev->stop = &ace_close;
 		dev->get_stats = &ace_get_stats;
 		dev->set_multicast_list = &ace_set_multicast_list;
@@ -726,7 +754,7 @@
 }
 #else
 module_init(ace_module_init);
-module_exit(ace_module_cleanup);
+//module_exit(ace_module_cleanup);
 #endif
 
 
@@ -909,7 +937,7 @@
 	writel((CLR_INT | WORD_SWAP | ((CLR_INT | WORD_SWAP) << 24)),
 	       &regs->HostCtrl);
 #endif
-	mb();
+	wmb();
 
 	/*
 	 * Stop the NIC CPU and clear pending interrupts
@@ -964,7 +992,7 @@
 	writel(ACE_BYTE_SWAP_DMA | ACE_WARN | ACE_FATAL |
 	       ACE_WORD_SWAP_BD | ACE_NO_JUMBO_FRAG, &regs->ModeStat);
 #endif
-	mb();
+	wmb();
 
 	mac1 = 0;
 	for(i = 0; i < 4; i++) {
@@ -1091,7 +1119,7 @@
 #endif
 	writel(tmp, &regs->PciState);
 
-#if 0
+#if 1
 	/*
 	 * I have received reports from people having problems when this
 	 * bit is enabled.
@@ -1255,6 +1283,10 @@
 		writel(0, (unsigned long)ap->tx_ring + i * 4);
 	}
 
+	printk("TX ring base (physical) address: %08x.\n", TX_RING_BASE);
+	printk("tx_ring (physical) address: %08lx. (should be about the same as above)\n",
+		__pa((unsigned long)ap->tx_ring));
+
 	set_aceaddr(&info->tx_ctrl.rngptr, TX_RING_BASE);
 	info->tx_ctrl.max_len = TX_RING_ENTRIES;
 #if TX_COAL_INTS_ONLY
@@ -1371,7 +1403,6 @@
 	 * tx ints before we are up and running, which may cause a null
 	 * pointer access in the int handler.
 	 */
-	ap->tx_full = 0;
 	ap->cur_rx = 0;
 	ap->tx_prd = *(ap->tx_csm) = ap->tx_ret_csm = 0;
 
@@ -1822,8 +1853,7 @@
 			ap->jumbo = 0;
 			printk(KERN_INFO "%s: Jumbo ring flushed\n",
 			       dev->name);
-			if (!ap->tx_full)
-				netif_wake_queue(dev);
+			netif_wake_queue(dev);
 			clear_bit(0, &ap->jumbo_refill_busy);
 			break;
 		}
@@ -1962,6 +1992,7 @@
 	ap = dev->priv;
 	regs = ap->regs;
 
+	dprintk("Got AceNIC IRQ%d, ap: %p\n", irq, ap);
 	/*
 	 * In case of PCI shared interrupts or spurious interrupts,
 	 * we want to make sure it is actually our interrupt before
@@ -1985,27 +2016,38 @@
 	rxretprd = *ap->rx_ret_prd;
 	rxretcsm = ap->cur_rx;
 
-	if (rxretprd != rxretcsm)
+	if (rxretprd != rxretcsm) {
+		dprintk("Processing RX IRQ (prod: %d, csm: %d).\n", rxretprd, rxretcsm);
 		ace_rx_int(dev, rxretprd, rxretcsm);
+	}
 
 	txcsm = *ap->tx_csm;
 	idx = ap->tx_ret_csm;
 
 	if (txcsm != idx) {
+		dprintk("Processing TX IRQ (txcsm: %d, tx_ret_csm: %d).\n", txcsm, idx);
 		do {
 			struct sk_buff *skb;
 			dma_addr_t mapping;
+			struct ring_info *info;
+			struct tx_desc *desc;
 
-			skb = ap->skb->tx_skbuff[idx].skb;
-			mapping = ap->skb->tx_skbuff[idx].mapping;
+			info = ap->skb->tx_skbuff + idx;
+			desc = ap->tx_ring + idx;
+			skb = info->skb;
+			mapping = info->mapping;
+
+			dprintk("Freeing TX descriptor %p (%d), skb %p, high:%08x, low:%08x, flags/size:%08x.).\n", desc, idx, skb, desc->addr.addrhi, desc->addr.addrlo, desc->flagsize);
 
 			ap->stats.tx_packets++;
-			ap->stats.tx_bytes += skb->len;
-			pci_unmap_single(ap->pdev, mapping, skb->len,
-					 PCI_DMA_TODEVICE);
-			dev_kfree_skb_irq(skb);
+			if (skb) {
+				ap->stats.tx_bytes += skb->len;
+//				pci_unmap_single(ap->pdev, mapping, skb->len,
+//						 PCI_DMA_TODEVICE);
+				dev_kfree_skb_irq(skb);
 
-			ap->skb->tx_skbuff[idx].skb = NULL;
+				info->skb = NULL;
+			}
 
 			/*
 			 * Question here is whether one should not skip
@@ -2013,38 +2055,27 @@
 			 * caused by the NIC actually trying to access
 			 * these incorrectly.
 			 */
-#if (BITS_PER_LONG == 64)
+#if TX_DEBUG
 			writel(0, &ap->tx_ring[idx].addr.addrhi);
-#endif
 			writel(0, &ap->tx_ring[idx].addr.addrlo);
 			writel(0, &ap->tx_ring[idx].flagsize);
+#endif
 
 			idx = (idx + 1) % TX_RING_ENTRIES;
 		} while (idx != txcsm);
 
+		wmb();
+		dprintk("%d free TX descriptors, new ret_csm: %d\n", tx_free(ap), txcsm);
+
+		ap->tx_ret_csm = txcsm;
 		/*
 		 * Once we actually get to this point the tx ring has
 		 * already been trimmed thus it cannot be full!
 		 * Ie. skip the comparison of the tx producer vs. the
 		 * consumer.
 		 */
-		if (netif_queue_stopped(dev) && xchg(&ap->tx_full, 0)) {
-			/*
-			 * This does not need to be atomic (and expensive),
-			 * I've seen cases where it would fail otherwise ;-(
-			 */
+		if (!tx_ring_full(ap))
 			netif_wake_queue(dev);
-			ace_mark_net_bh(NET_BH);
-
-			/*
-			 * TX ring is no longer full, aka the
-			 * transmitter is working fine - kill timer.
-			 */
-			del_timer(&ap->timer);
-		}
-
-		ap->tx_ret_csm = txcsm;
-		wmb();
 	}
 
 	evtcsm = readl(&regs->EvtCsm);
@@ -2120,6 +2151,31 @@
 	writel(0, &regs->Mb0Lo);
 }
 
+#define MAX_DEV 16
+
+static struct net_device *dev_array[MAX_DEV];
+static int nr_dev = 0;
+
+void wake_acenics (void)
+{
+	int i;
+
+       	printk("hack_acenics() called.\n");
+	for (i = 0; i < MAX_DEV; i++) {
+		struct net_device *dev = dev_array[i];
+		struct ace_private *ap;
+		ap = dev->priv;
+	        printk(".device %s.\n", dev->name);
+	        printk("... queue state was: %08lx.\n", dev->state);
+	        printk("... tx_free(): %d.\n", tx_free(ap));
+	        printk("... tx_ret_csm: %d.\n", ap->tx_ret_csm);
+	        printk("... tx_prd: %d.\n", ap->tx_prd);
+	        printk("... evt_prd: %d.\n", *ap->evt_prd);
+	        printk("... rx_ret_prd: %d.\n", *ap->rx_ret_prd);
+	        printk("... tx_csm: %d.\n", *ap->tx_csm);
+		netif_wake_queue(dev);
+	}
+}
 
 static int ace_open(struct net_device *dev)
 {
@@ -2127,6 +2183,8 @@
 	struct ace_regs *regs;
 	struct cmd cmd;
 
+	dev_array[nr_dev++] = dev;
+
 	ap = dev->priv;
 	regs = ap->regs;
 
@@ -2205,13 +2263,21 @@
 	unsigned long flags;
 	short i;
 
-	ace_if_down(dev);
-	netif_stop_queue(dev);
 
-	ap = dev->priv;
+        ap = dev->priv;
 	regs = ap->regs;
 
-	del_timer(&ap->timer);
+        printk("ace_close(%p) called.\n", dev);
+        printk("... queue state was: %08lx.\n", dev->state);
+        printk("... tx_free(): %d.\n", tx_free(ap));
+        printk("... tx_ret_csm: %d.\n", ap->tx_ret_csm);
+        printk("... tx_prd: %d.\n", ap->tx_prd);
+        printk("... evt_prd: %d.\n", *ap->evt_prd);
+        printk("... rx_ret_prd: %d.\n", *ap->rx_ret_prd);
+        printk("... tx_csm: %d.\n", *ap->tx_csm);
+
+	ace_if_down(dev);
+	netif_stop_queue(dev);
 
 	if (ap->promisc) {
 		cmd.evt = C_SET_PROMISC_MODE;
@@ -2236,17 +2302,27 @@
 	for (i = 0; i < TX_RING_ENTRIES; i++) {
 		struct sk_buff *skb;
 		dma_addr_t mapping;
+		struct ring_info *info;
 
-		skb = ap->skb->tx_skbuff[i].skb;
-		mapping = ap->skb->tx_skbuff[i].mapping;
+		info = ap->skb->tx_skbuff + i;
+		skb = info->skb;
+		mapping = info->mapping;
+//		printk("ring entry %d, skb %p, info %p.\n", i, skb, info);
+//		printk("... addrhi: %08x, addrlo: %08x, flags-size:%08x.\n",
+//			ap->tx_ring[i].addr.addrhi,
+//			ap->tx_ring[i].addr.addrlo,
+//			ap->tx_ring[i].flagsize);
 		if (skb) {
+//			pci_unmap_single(ap->pdev, mapping, skb->len,
+//					 PCI_DMA_TODEVICE);
+
+#if 1
 			writel(0, &ap->tx_ring[i].addr.addrhi);
 			writel(0, &ap->tx_ring[i].addr.addrlo);
 			writel(0, &ap->tx_ring[i].flagsize);
-			pci_unmap_single(ap->pdev, mapping, skb->len,
-					 PCI_DMA_TODEVICE);
+#endif
 			dev_kfree_skb(skb);
-			ap->skb->tx_skbuff[i].skb = NULL;
+			info->skb = NULL;
 		}
 	}
 
@@ -2268,44 +2344,137 @@
 {
 	struct ace_private *ap = dev->priv;
 	struct ace_regs *regs = ap->regs;
-	unsigned long addr;
+	struct tx_desc *desc;
+	struct ring_info *info;
+	unsigned long long addr, phys;
 	u32 idx, flagsize;
 
-	/*
-	 * ARGH, there is just no pretty way to do this
-	 */
-#if (LINUX_VERSION_CODE < 0x02032b)
-	if (test_and_set_bit(0, &dev->tbusy))
+	dprintk("AceNIC start_xmit(skb: %p), dev %p.\n", skb, dev);
+
+#if 0
+	if (tx_ring_full(ap)) {
+		printk("%s: trying to transmit while the tx ring is full "
+		       "- i think this should not happen. (state: %ld)\n",
+				dev->name, dev->state);
+		netif_stop_queue(dev);
 		return 1;
-#else
-	netif_stop_queue(dev);
+	}
 #endif
-
 	idx = ap->tx_prd;
 
-	if ((idx + 1) % TX_RING_ENTRIES == ap->tx_ret_csm) {
-		ap->tx_full = 1;
-#if DEBUG
-		printk("%s: trying to transmit while the tx ring is full "
-		       "- this should not happen!\n", dev->name);
+	if (!skb->nr_frags) {
+		info = ap->skb->tx_skbuff + idx;
+		desc = ap->tx_ring + idx;
+		phys = pci_map_single(ap->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+//		info->mapping = phys;
+		addr = phys;
+#if TX_DEBUG
+		if (desc->addr.addrhi)
+			BUG();
+		if (desc->addr.addrlo)
+			BUG();
+		if (desc->flagsize)
+			BUG();
+		if (!skb->len)
+			BUG();
+		if (info->skb)
+			BUG();
+#endif
+		info->skb = skb;
+		flagsize = (skb->len << 16) | (BD_FLG_END) ;
+
+		writel(addr >> 32, &desc->addr.addrhi);
+		writel(addr & 0xffffffff, &desc->addr.addrlo);
+		writel(flagsize, &desc->flagsize);
+//		writel(0, &desc->vlanres);
+
+		dprintk("added NORMAL TX descriptor %p (%d), skb %p, high:%08x, low:%08x, flags/size:%08x.).\n", desc, idx, skb, desc->addr.addrhi, desc->addr.addrlo, flagsize);
+
+		idx = (idx + 1) % TX_RING_ENTRIES;
+	} else {
+		int i, len = 0;
+
+#if TX_DEBUG
+		if (idx & 1)
+			idx = (idx + 1) % TX_RING_ENTRIES;
+#endif
+		info = ap->skb->tx_skbuff + idx;
+		desc = ap->tx_ring + idx;
+#if TX_DEBUG
+		if (info->skb)
+			BUG();
+		if (desc->addr.addrhi)
+			BUG();
+		if (desc->addr.addrlo)
+			BUG();
+		if (desc->flagsize)
+			BUG();
+		if (!(skb->len - skb->data_len))
+			BUG();
+#endif
+		phys = pci_map_single(ap->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+//		info->mapping = phys;
+		info->skb = NULL;
+		addr = phys;
+		flagsize = ((skb->len - skb->data_len) << 16);
+
+		writel(addr >> 32, &desc->addr.addrhi);
+		writel(addr & 0xffffffff, &desc->addr.addrlo);
+		writel(flagsize, &desc->flagsize);
+//		writel(0, &desc->vlanres);
+
+		idx = (idx + 1) % TX_RING_ENTRIES;
+
+		for (i = 0; i < skb->nr_frags; i++) {
+			skb_frag_t *frag = skb->frags[i];
+
+			len += frag->size;
+			info = ap->skb->tx_skbuff + idx;
+			desc = ap->tx_ring + idx;
+#if TX_DEBUG
+			if (info->skb)
+				BUG();
+			if (desc->addr.addrhi)
+				BUG();
+			if (desc->addr.addrlo)
+				BUG();
+			if (desc->flagsize)
+				BUG();
+			if (!frag->size)
+				BUG();
+#endif
+
+			phys = (frag->page-mem_map) *
+					(unsigned long long) PAGE_SIZE +
+						frag->page_offset;
+			flagsize = (frag->size << 16);
+			dprintk("added HEAD FRAGMENTED TX descriptor %p (%d), skb %p, high:%08x, low:%08x, flags/size:%08x., info->skb: %p).\n", desc, idx, skb, desc->addr.addrhi, desc->addr.addrlo, flagsize, info->skb);
+			if (i == skb->nr_frags-1) {
+				flagsize |= BD_FLG_END;
+				/*
+				 * Only the last fragment frees
+				 * the skb!
+				 */
+				info->skb = skb;
+				dprintk("added LAST FRAGMENTED TX descriptor %p (%d), skb %p, high:%08x, low:%08x, flags/size:%08x., info->skb: %p).\n", desc, idx, skb, desc->addr.addrhi, desc->addr.addrlo, flagsize, info->skb);
+			} else {
+				dprintk("added MIDDLE FRAGMENTED TX descriptor %p (%d), skb %p, high:%08x, low:%08x, flags/size:%08x., info->skb: %p).\n", desc, idx, skb, desc->addr.addrhi, desc->addr.addrlo, flagsize, info->skb);
+			}
+			writel(phys >> 32, &desc->addr.addrhi);
+			writel(phys & 0xffffffff, &desc->addr.addrlo);
+			writel(flagsize, &desc->flagsize);
+//			writel(0, &desc->vlanres);
+			idx = (idx + 1) % TX_RING_ENTRIES;
+		}
+#if TX_DEBUG
+		if (len != skb->data_len)
+			BUG();
+		if (idx & 1)
+			idx = (idx + 1) % TX_RING_ENTRIES;
 #endif
-		return 1;
 	}
 
-	ap->skb->tx_skbuff[idx].skb = skb;
-	ap->skb->tx_skbuff[idx].mapping =
-		pci_map_single(ap->pdev, skb->data, skb->len,
-			       PCI_DMA_TODEVICE);
-	addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping;
-#if (BITS_PER_LONG == 64)
-	writel(addr >> 32, &ap->tx_ring[idx].addr.addrhi);
-#endif
-	writel(addr & 0xffffffff, &ap->tx_ring[idx].addr.addrlo);
-	flagsize = (skb->len << 16) | (BD_FLG_END) ;
-	writel(flagsize, &ap->tx_ring[idx].flagsize);
 	wmb();
-	idx = (idx + 1) % TX_RING_ENTRIES;
-
 	ap->tx_prd = idx;
 	ace_set_txprd(regs, ap, idx);
 
@@ -2313,34 +2482,16 @@
 	 * tx_csm is set by the NIC whereas we set tx_ret_csm which
 	 * is always trying to catch tx_csm
 	 */
-	if ((idx + 2) % TX_RING_ENTRIES == ap->tx_ret_csm) {
-		ap->tx_full = 1;
+	if (tx_ring_full(ap)) {
+		netif_stop_queue(dev);
 		/*
-		 * Queue is full, add timer to detect whether the
-		 * transmitter is stuck. Use mod_timer as we can get
-		 * into the situation where we risk adding several
-		 * timers.
+		 * A TX-descriptor producer (an IRQ) might have gotten
+		 * inbetween, making the ring free again. Since xmit is
+		 * serialized, this is the only situation we have to
+		 * re-test.
 		 */
-		mod_timer(&ap->timer, jiffies + (3 * HZ));
-
-		/* The following check will fix a race between the interrupt
-		 * handler increasing the tx_ret_csm and testing for tx_full
-		 * and this tx routine's testing the tx_ret_csm and setting
-		 * the tx_full; note that this fix makes assumptions on the
-		 * ordering of writes (sequential consistency will fly; TSO
-		 * processor order would work too) but that's what lock-less
-		 * programming is all about
-		 */
-		if (((idx + 2) % TX_RING_ENTRIES != ap->tx_ret_csm)
-			&& xchg(&ap->tx_full, 0)) {
-			del_timer(&ap->timer);
+		if (!tx_ring_full(ap))
 			netif_wake_queue(dev);
-		}
-	} else {
-		/*
-		 * No need for it to be atomic - seems it needs to be
-		 */
-		netif_wake_queue(dev);
 	}
 
 	dev->trans_start = jiffies;
@@ -2747,19 +2898,19 @@
 	local = readl(&regs->LocalCtrl);
 	local |= EEPROM_DATA_OUT | EEPROM_WRITE_ENABLE;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	local |= EEPROM_CLK_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	local &= ~EEPROM_DATA_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	local &= ~EEPROM_CLK_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 }
 
 
@@ -2773,7 +2924,7 @@
 	local &= ~EEPROM_DATA_OUT;
 	local |= EEPROM_WRITE_ENABLE;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 
 	for (i = 0; i < 8; i++, magic <<= 1) {
 		udelay(ACE_SHORT_DELAY);
@@ -2782,16 +2933,16 @@
 		else
 			local &= ~EEPROM_DATA_OUT;
 		writel(local, &regs->LocalCtrl);
-		mb();
+		wmb();
 
 		udelay(ACE_SHORT_DELAY);
 		local |= EEPROM_CLK_OUT;
 		writel(local, &regs->LocalCtrl);
-		mb();
+		wmb();
 		udelay(ACE_SHORT_DELAY);
 		local &= ~(EEPROM_CLK_OUT | EEPROM_DATA_OUT);
 		writel(local, &regs->LocalCtrl);
-		mb();
+		wmb();
 	}
 }
 
@@ -2804,18 +2955,18 @@
 	local = readl(&regs->LocalCtrl);
 	local &= ~EEPROM_WRITE_ENABLE;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_LONG_DELAY);
 	local |= EEPROM_CLK_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	/* sample data in middle of high clk */
 	state = (readl(&regs->LocalCtrl) & EEPROM_DATA_IN) != 0;
 	udelay(ACE_SHORT_DELAY);
-	mb();
+	wmb();
 	writel(readl(&regs->LocalCtrl) & ~EEPROM_CLK_OUT, &regs->LocalCtrl);
-	mb();
+	wmb();
 
 	return state;
 }
@@ -2829,23 +2980,23 @@
 	local = readl(&regs->LocalCtrl);
 	local |= EEPROM_WRITE_ENABLE;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	local &= ~EEPROM_DATA_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	local |= EEPROM_CLK_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	local |= EEPROM_DATA_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_LONG_DELAY);
 	local &= ~EEPROM_CLK_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 }
 
 
@@ -2919,37 +3070,37 @@
 		local &= ~EEPROM_WRITE_ENABLE;
 		writel(local, &regs->LocalCtrl);
 		udelay(ACE_LONG_DELAY);
-		mb();
+		wmb();
 		local |= EEPROM_CLK_OUT;
 		writel(local, &regs->LocalCtrl);
-		mb();
+		wmb();
 		udelay(ACE_SHORT_DELAY);
 		/* sample data mid high clk */
 		result = (result << 1) |
 			((readl(&regs->LocalCtrl) & EEPROM_DATA_IN) != 0);
 		udelay(ACE_SHORT_DELAY);
-		mb();
+		wmb();
 		local = readl(&regs->LocalCtrl);
 		local &= ~EEPROM_CLK_OUT;
 		writel(local, &regs->LocalCtrl);
 		udelay(ACE_SHORT_DELAY);
-		mb();
+		wmb();
 		if (i == 7) {
 			local |= EEPROM_WRITE_ENABLE;
 			writel(local, &regs->LocalCtrl);
-			mb();
+			wmb();
 			udelay(ACE_SHORT_DELAY);
 		}
 	}
 
 	local |= EEPROM_DATA_OUT;
 	writel(local, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	writel(readl(&regs->LocalCtrl) | EEPROM_CLK_OUT, &regs->LocalCtrl);
 	udelay(ACE_LONG_DELAY);
 	writel(readl(&regs->LocalCtrl) & ~EEPROM_CLK_OUT, &regs->LocalCtrl);
-	mb();
+	wmb();
 	udelay(ACE_SHORT_DELAY);
 	eeprom_stop(regs);
 
@@ -2966,6 +3117,6 @@
 
 /*
  * Local variables:
- * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o acenic.o acenic.c"
+ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o acenic.o acenic.c"
  * End:
  */
--- linux/drivers/net/acenic.h.orig	Fri May 12 20:38:35 2000
+++ linux/drivers/net/acenic.h	Fri Sep  1 07:28:26 2000
@@ -1,6 +1,8 @@
 #ifndef _ACENIC_H_
 #define _ACENIC_H_
 
+//#define DEBUG
+
 /*
  * Addressing:
  *
@@ -415,12 +417,8 @@
 
 
 /*
- * TX ring
+ * TX ring.
  */
-#define TX_RING_ENTRIES	128
-#define TX_RING_SIZE	(TX_RING_ENTRIES * sizeof(struct tx_desc))
-#define TX_RING_BASE	0x3800
-
 struct tx_desc{
         aceaddr	addr;
 	u32	flagsize; 
@@ -444,6 +442,14 @@
 	u32	vlanres;
 };
 
+/*
+ * TX ring size can be 128, 256 or 512.
+ * (any other value will result in a crash.)
+ */
+#define TX_RING_ENTRIES	128
+#define TX_RING_SIZE	(TX_RING_ENTRIES * sizeof(struct tx_desc))
+#define TX_RING_END 0x4000
+#define TX_RING_BASE (TX_RING_END - sizeof(struct tx_desc)*TX_RING_ENTRIES)
 
 #define RX_STD_RING_ENTRIES	512
 #define RX_STD_RING_SIZE	(RX_STD_RING_ENTRIES * sizeof(struct rx_desc))
@@ -603,9 +609,8 @@
 	 */
 	struct ace_info		*info;
 	struct tx_desc		*tx_ring;
-	dma_addr_t		info_dma;
 	u32			tx_prd;
-	volatile u32		tx_full, tx_ret_csm;
+	volatile u32		tx_ret_csm;
 	struct timer_list	timer;
 
 	unsigned long		std_refill_busy
@@ -625,13 +630,26 @@
 	struct rx_desc		*rx_jumbo_ring;
 	struct rx_desc		*rx_mini_ring;
 	struct rx_desc		*rx_return_ring;
-	dma_addr_t		rx_ring_base_dma;
 
 	struct event		*evt_ring;
-	dma_addr_t		evt_ring_dma;
-
 	volatile u32		*evt_prd, *rx_ret_prd, *tx_csm;
-	dma_addr_t		evt_prd_dma, rx_ret_prd_dma, tx_csm_dma;
+
+	/*
+	 * These are the places where the NIC DMAs into, so we
+	 * want to have them on separate cachelines.
+	 */
+	dma_addr_t		rx_ring_base_dma
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
+	dma_addr_t		info_dma
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
+	dma_addr_t		evt_ring_dma
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
+	dma_addr_t		evt_prd_dma
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
+	dma_addr_t		rx_ret_prd_dma
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
+	dma_addr_t		tx_csm_dma
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
 
 	unsigned char		*trace_buf;
 	struct pci_dev		*pdev;
@@ -642,12 +660,22 @@
 	char			name[48];
 #ifdef INDEX_DEBUG
 	spinlock_t		debug_lock
-				__attribute__ ((aligned (L1_CACHE_BYTES)));;
+				__attribute__ ((aligned (L1_CACHE_BYTES)));
 	u32			last_tx, last_std_rx, last_mini_rx;
 #endif
 	struct net_device_stats stats;
 };
 
+#define TX_RESERVED (MAX_SKB_FRAGS + 4)
+
+static inline int tx_free (struct ace_private *ap)
+{
+	// 2's complement arithmetics
+
+	return (ap->tx_ret_csm - ap->tx_prd - 1) & (TX_RING_ENTRIES-1);
+}
+
+#define tx_ring_full(ap) (tx_free(ap) <= TX_RESERVED)
 
 static inline void set_aceaddr(aceaddr *aa, dma_addr_t addr)
 {
--- linux/drivers/block/ll_rw_blk.c.orig	Fri Sep  1 07:27:00 2000
+++ linux/drivers/block/ll_rw_blk.c	Fri Sep  1 07:28:26 2000
@@ -21,6 +21,7 @@
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
+#include <linux/bootmem.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
--- linux/arch/i386/mm/fault.c.orig	Thu May 25 03:38:26 2000
+++ linux/arch/i386/mm/fault.c	Fri Sep  1 07:28:26 2000
@@ -102,6 +102,30 @@
 	printk("Ok");
 }
 
+static void print_pagetable_entries (pgd_t *pgdir, unsigned long vaddr)
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	pgd = pgdir + __pgd_offset(vaddr);
+	printk("pgd entry %p: %016Lx\n", pgd, (long long)pgd_val(*pgd));
+	if (!pgd_present(*pgd)) {
+		printk("... pgd not present!\n");
+		return;
+	}
+	pmd = pmd_offset(pgd, vaddr);
+	printk("pmd entry %p: %016Lx\n", pmd, (long long)pmd_val(*pmd));
+	if (!pmd_present(*pmd)) {
+		printk("... pmd not present!\n");
+		return;
+	}
+	pte = pte_offset(pmd, vaddr);
+	printk("pte entry %p: %016Lx\n", pte, (long long)pte_val(*pte));
+	if (!pte_present(*pte))
+		printk("... pte not present!\n");
+}
+
 asmlinkage void do_invalid_op(struct pt_regs *, unsigned long);
 extern unsigned long idt;
 
@@ -277,14 +301,7 @@
 	printk(" printing eip:\n");
 	printk("%08lx\n", regs->eip);
 	asm("movl %%cr3,%0":"=r" (page));
-	page = ((unsigned long *) __va(page))[address >> 22];
-	printk(KERN_ALERT "*pde = %08lx\n", page);
-	if (page & 1) {
-		page &= PAGE_MASK;
-		address &= 0x003ff000;
-		page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT];
-		printk(KERN_ALERT "*pte = %08lx\n", page);
-	}
+	print_pagetable_entries((pgd_t *)__va(page), address);
 	die("Oops", regs, error_code);
 	do_exit(SIGKILL);
 
--- linux/arch/i386/kernel/entry.S.orig	Fri Sep  1 07:27:00 2000
+++ linux/arch/i386/kernel/entry.S	Fri Sep  1 07:28:26 2000
@@ -643,6 +643,11 @@
 	.long SYMBOL_NAME(sys_madvise)
 	.long SYMBOL_NAME(sys_getdents64)	/* 220 */
 	.long SYMBOL_NAME(sys_fcntl64)
+#ifdef CONFIG_HTTP
+	.long SYMBOL_NAME(sys_http)
+#else
+	.long SYMBOL_NAME(sys_ni_syscall)	/* placeholder */
+#endif
 
 	/*
 	 * NOTE!! This doesn't have to be exact - we just have
@@ -650,6 +655,6 @@
 	 * entries. Don't panic if you notice that this hasn't
 	 * been shrunk every time we add a new system call.
 	 */
-	.rept NR_syscalls-221
+	.rept NR_syscalls-222
 		.long SYMBOL_NAME(sys_ni_syscall)
 	.endr
--- linux/arch/i386/vmlinux.lds.orig	Fri Sep  1 07:26:37 2000
+++ linux/arch/i386/vmlinux.lds	Fri Sep  1 07:28:26 2000
@@ -6,7 +6,7 @@
 ENTRY(_start)
 SECTIONS
 {
-  . = 0xC0000000 + 0x100000;
+  . = 0x40000000 + 0x100000;
   _text = .;			/* Text and read-only data */
   .text : {
 	*(.text)
--- linux/Makefile.orig	Fri Sep  1 07:27:06 2000
+++ linux/Makefile	Fri Sep  1 07:28:26 2000
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 0
-EXTRAVERSION = -test8
+EXTRAVERSION = -test8-TUX
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -87,7 +87,11 @@
 
 CPPFLAGS := -D__KERNEL__ -I$(HPATH)
 
-CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
+#ifdef CONFIG_HTTP_DEBUG
+CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fno-omit-frame-pointer
+#else
+CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer
+#endif
 AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
 
 #
