--- 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 #include #include +#include + /* * 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 #include +#include +#include #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 #include +#include /* * 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 +#include +#include +#include +#include +#include +#include +#include + +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 #include - /* * 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 + * + * http.h: main structure definitions and function prototypes + */ + +#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* 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<\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 + * + * 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 #include #include +#include #include #include @@ -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; ihttp_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 #include #include +#include #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) #include /* 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 +#include + + 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 #include #include @@ -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 + * + * accept.c: accept connections - sleep if there is no work left. + */ + +#include + +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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + 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 + * + * output.c: Send actual file-data to pending connections + */ + +#include + +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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + 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 + * + * input.c: handle HTTP headers arriving on accepted connections + */ + +#include +#include + +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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + 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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + 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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + + 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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + 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 + * + * redirect.c: redirect requests to other server sockets (such as Apache). + */ + +#include + +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 + * + * 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 + +#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(¤t->sigmask_lock); +#if 1 + ka = current->sig->action + SIGCHLD-1; + ka->sa.sa_handler = SIG_IGN; +#endif + siginitsetinv(¤t->blocked, sigmask(SIGCHLD)); + recalc_sigpending(current); + spin_unlock_irq(¤t->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 + * + * logger.c: log requests finished by TUX. + */ + +#define __KERNEL_SYSCALLS__ +#include + +#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 [] = ""; + +#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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); +#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 : "", req->uri ? req->uri : "", req->query ? req->query : "", req->version_str ? req->version_str : ""); + 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(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, 0); + recalc_sigpending(current); + spin_unlock_irq(¤t->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 + * + * 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 +#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 + * + * CAD.c: Implementation of the SPECweb99 dynamic application via + * the HTTP trusted-API. + */ + +#include + +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 "" + +#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 \ + "\n" \ + "SPECweb99 Dynamic GET & POST Test\n"\ + "\n" \ + "

SERVER_SOFTWARE = TUX 1.0\n" \ + "

REMOTE_ADDR = %d.%d.%d.%d\n" \ + "

SCRIPT_NAME = %s\n" \ + "

QUERY_STRING = %s\n" \ + "

\n"
+
+#define REPLY_TAIL \
+	"\n
\n" \ + "\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 + * + * httpmod.c: loading/registering of HTTP dynamic modules + */ + +#include +#include + +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 + * + * cgi.c: user-space CGI (and other) code execution. + */ + +#define __KERNEL_SYSCALLS__ + +#include + +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(¤t->sigmask_lock); + flush_signals(current); + flush_signal_handlers(current); + spin_unlock_irq(¤t->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 = ¶m_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 + * + * httpmain.c: main management and initialization routines + */ + +#define __KERNEL_SYSCALLS__ +#include + +#include +#include + + +/* + * 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(¤t->sigmask_lock); + flush_signals(current); + recalc_sigpending(current); + spin_unlock_irq(¤t->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(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, sigmask(SIGKILL) | + sigmask(SIGSTOP)| sigmask(SIGHUP) | sigmask(SIGCHLD)); + recalc_sigpending(current); + spin_unlock_irq(¤t->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 + * + * extcgi.c: dynamic HTTP module which forks and starts an external CGI + */ + +#define __KERNEL_SYSCALLS__ + +#include +#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, ¶m, 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 + * + * 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 + * + * sysctl.c: /proc/sysctl/http handling + */ + +#include + +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 + * + * CAD.c: Implementation of the SPECweb99 CAD dynamic application via + * the HTTP trusted-module. + */ + +#include + +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 "" + +#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 \ + "\n" \ + "SPECweb99 Dynamic GET & POST Test\n"\ + "\n" \ + "

SERVER_SOFTWARE = TUX 1.0\n" \ + "

REMOTE_ADDR = %d.%d.%d.%d\n" \ + "

SCRIPT_NAME = %s\n" \ + "

QUERY_STRING = %s\n" \ + "

\n"
+
+#define REPLY_TAIL \
+	"\n
\n" \ + "\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 + * + * userspace.c: handle userspace-module requests + */ + +#include + +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 + /* 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 #undef ETHTOOL -#undef INDEX_DEBUG +//#define INDEX_DEBUG +#define TX_DEBUG 0 #ifdef ETHTOOL #include @@ -97,7 +98,7 @@ #endif #ifndef wmb -#define wmb() mb() +#define wmb() wmb() #endif #ifndef __exit @@ -182,6 +183,8 @@ #include "acenic_firmware.h" +#define dprintk(x...) do { } while (0) + /* * This driver currently supports Tigon I and Tigon II based cards * including the Alteon AceNIC, the 3Com 3C985[B] and NetGear @@ -375,18 +378,42 @@ #define DEF_JUMBO_RX_MAX_DESC 6 #define DEF_JUMBO_TX_RATIO 21 -#define TX_COAL_INTS_ONLY 0 /* seems not worth it */ +#define TX_COAL_INTS_ONLY 1 /* seems not worth it */ #define DEF_TRACE 0 #define DEF_STAT (2 * TICKS_PER_SEC) static int link[ACE_MAX_MOD_PARMS] = {0, }; static int trace[ACE_MAX_MOD_PARMS] = {0, }; -static int tx_coal_tick[ACE_MAX_MOD_PARMS] = {0, }; -static int rx_coal_tick[ACE_MAX_MOD_PARMS] = {0, }; -static int max_tx_desc[ACE_MAX_MOD_PARMS] = {0, }; -static int max_rx_desc[ACE_MAX_MOD_PARMS] = {0, }; -static int tx_ratio[ACE_MAX_MOD_PARMS] = {0, }; -static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; +static int tx_coal_tick[ACE_MAX_MOD_PARMS] = + { [0 ... ACE_MAX_MOD_PARMS-1] = 400 }; +static int rx_coal_tick[ACE_MAX_MOD_PARMS] = + { [0 ... ACE_MAX_MOD_PARMS-1] = 200 }; +static int max_tx_desc[ACE_MAX_MOD_PARMS] = + { [0 ... ACE_MAX_MOD_PARMS-1] = 32 }; +static int max_rx_desc[ACE_MAX_MOD_PARMS] = + { [0 ... ACE_MAX_MOD_PARMS-1] = 16 }; +static int tx_ratio[ACE_MAX_MOD_PARMS] = + { [0 ... ACE_MAX_MOD_PARMS-1] = 56 /*56*/ }; +static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = + { [0 ... ACE_MAX_MOD_PARMS-1] = 0 }; + +static int __init ace_coal_setup (char *str) +{ + int par; + + if (get_option(&str,&par) >= 0) { + int i; + + for (i = 0; i < ACE_MAX_MOD_PARMS; i++) { + tx_coal_tick[i] = par; + rx_coal_tick[i] = par; + } + } + return 1; +} + +__setup("ace_coal=", ace_coal_setup); + static const char __initdata *version = "acenic.c: v0.44 05/11/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n" @@ -461,6 +488,7 @@ dev->irq = pdev->irq; dev->open = &ace_open; dev->hard_start_xmit = &ace_start_xmit; + dev->hard_start_xmit_dual = &ace_start_xmit; dev->stop = &ace_close; dev->get_stats = &ace_get_stats; dev->set_multicast_list = &ace_set_multicast_list; @@ -726,7 +754,7 @@ } #else module_init(ace_module_init); -module_exit(ace_module_cleanup); +//module_exit(ace_module_cleanup); #endif @@ -909,7 +937,7 @@ writel((CLR_INT | WORD_SWAP | ((CLR_INT | WORD_SWAP) << 24)), ®s->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, ®s->ModeStat); #endif - mb(); + wmb(); mac1 = 0; for(i = 0; i < 4; i++) { @@ -1091,7 +1119,7 @@ #endif writel(tmp, ®s->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(®s->EvtCsm); @@ -2120,6 +2151,31 @@ writel(0, ®s->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(®s->LocalCtrl); local |= EEPROM_DATA_OUT | EEPROM_WRITE_ENABLE; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local |= EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local &= ~EEPROM_DATA_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local &= ~EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); } @@ -2773,7 +2924,7 @@ local &= ~EEPROM_DATA_OUT; local |= EEPROM_WRITE_ENABLE; writel(local, ®s->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, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local |= EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local &= ~(EEPROM_CLK_OUT | EEPROM_DATA_OUT); writel(local, ®s->LocalCtrl); - mb(); + wmb(); } } @@ -2804,18 +2955,18 @@ local = readl(®s->LocalCtrl); local &= ~EEPROM_WRITE_ENABLE; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_LONG_DELAY); local |= EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); /* sample data in middle of high clk */ state = (readl(®s->LocalCtrl) & EEPROM_DATA_IN) != 0; udelay(ACE_SHORT_DELAY); - mb(); + wmb(); writel(readl(®s->LocalCtrl) & ~EEPROM_CLK_OUT, ®s->LocalCtrl); - mb(); + wmb(); return state; } @@ -2829,23 +2980,23 @@ local = readl(®s->LocalCtrl); local |= EEPROM_WRITE_ENABLE; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local &= ~EEPROM_DATA_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local |= EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); local |= EEPROM_DATA_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_LONG_DELAY); local &= ~EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); } @@ -2919,37 +3070,37 @@ local &= ~EEPROM_WRITE_ENABLE; writel(local, ®s->LocalCtrl); udelay(ACE_LONG_DELAY); - mb(); + wmb(); local |= EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); /* sample data mid high clk */ result = (result << 1) | ((readl(®s->LocalCtrl) & EEPROM_DATA_IN) != 0); udelay(ACE_SHORT_DELAY); - mb(); + wmb(); local = readl(®s->LocalCtrl); local &= ~EEPROM_CLK_OUT; writel(local, ®s->LocalCtrl); udelay(ACE_SHORT_DELAY); - mb(); + wmb(); if (i == 7) { local |= EEPROM_WRITE_ENABLE; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); } } local |= EEPROM_DATA_OUT; writel(local, ®s->LocalCtrl); - mb(); + wmb(); udelay(ACE_SHORT_DELAY); writel(readl(®s->LocalCtrl) | EEPROM_CLK_OUT, ®s->LocalCtrl); udelay(ACE_LONG_DELAY); writel(readl(®s->LocalCtrl) & ~EEPROM_CLK_OUT, ®s->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 #include #include +#include #include #include --- 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) # .