--- 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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, [email protected]\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)

#