From: akepner@sgi.com
To: linux@horizon.com
Cc: davem@davemloft.net, netdev@vger.kernel.org
Subject: Re: [RFC/TOY]Extensible hashing and RCU
Date: Mon, 5 Feb 2007 10:41:39 -0800 (PST) [thread overview]
Message-ID: <Pine.LNX.4.61.0702051030550.26852@localhost.localdomain> (raw)
In-Reply-To: <20070204074143.26312.qmail@science.horizon.com>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 624 bytes --]
On Sat, 4 Feb 2007 linux@horizon.com wrote:
> I noticed in an LCA talk mention that apprently extensible hashing
> with RCU access is an unsolved problem. Here's an idea for solving it.
>
> I'm assuming the table is a power of 2 in size with open chaining
> for collisions. When the chains get too long, the table is doubled.
> When the chains get too short, the table size is halved.
> .....
For purposes of discussion, I've attached a "toy" implementation
for doing dynamic resizing of a hashtable. It is useless, except
as a proof of concept.
I think this is very similar to what you are describing, no?
--
Arthur
[-- Attachment #2: resizable hashtable toy implementation --]
[-- Type: TEXT/PLAIN, Size: 18987 bytes --]
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/seqlock.h>
#include <linux/rcupdate.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
#define _NETHASH_BUFSZ_ 16
#define MODULE_NAME "nethash"
#define _DEBUG_IT_
#ifdef _DEBUG_IT_
#define nprintk(x...) printk(KERN_ALERT x);
#else /* !_DEBUG_IT_ */
#define nprintk(x)
#endif /* _DEBUG_IT_ */
static struct proc_dir_entry *nh_proc_dir;
enum nh_type { NH_ENTRY, NH_HEAD };
struct nh_item {
/* the list head must be first followed by nh_type */
struct list_head nh_list;
enum nh_type nh_type;
};
struct nh_entry {
/* the list head must be first followed by nh_type */
struct list_head nh_list;
enum nh_type nh_type;
unsigned long data;
struct rcu_head rcu_head;
};
struct nh_head {
/* the list head must be first followed by nh_type */
struct list_head list;
enum nh_type nh_type;
spinlock_t lock;
};
struct nh {
unsigned long nentries;
struct nh_head* hash;
struct rcu_head rcu_head;
};
static struct nh * __nh;
static DEFINE_SEQLOCK(nethash_seq);
static DEFINE_MUTEX(nethash_resize_mutex);
extern void * system_hash_alloc(unsigned long sz);
extern void system_hash_free(void * v, unsigned long sz);
/* XXX nh_dump() is for for debug only
* it must be called under rcu_read_lock() */
static int nh_dump(struct nh * nh, const char * debug_str);
static struct nh * get_nh(void);
static unsigned long nh_hashval(unsigned long data)
{
return data;
}
static void nh_entry_free(struct rcu_head * head)
{
struct nh_entry * entry = container_of(head, struct nh_entry, rcu_head);
nprintk("%s: freeing entry with data %lu\n", __FUNCTION__, entry->data);
kfree(entry);
}
static void nh_free(struct rcu_head * head)
{
struct nh * nh = container_of(head, struct nh, rcu_head);
unsigned long nentries = nh->nentries;
unsigned long size = sizeof (struct nh_head) * nentries;
nprintk("%s: freeing nh of size %lu\n", __FUNCTION__, size);
system_hash_free((void *)nh->hash, size);
nprintk("%s: freeing nh\n", __FUNCTION__);
kfree(nh);
}
/* called with head's spin_lock held */
static int __nh_insert(struct nh_entry * entry,
struct nh_head * head,
unsigned long nentries)
{
struct list_head * prev; /* insert entry after prev */
struct list_head * list;
nprintk("%s: linking entry with data %lu\n", __FUNCTION__,
entry->data);
/* find the appropriate spot to place entry */
prev = &head->list;
if ( nh_hashval(entry->data) & nentries ) {
list_for_each_rcu(list, &head->list) {
struct nh_entry * tmp;
struct nh_item * item = (struct nh_item *)list;
if (item->nh_type != NH_ENTRY)
continue;
tmp = list_entry(list, struct nh_entry, nh_list);
prev = &tmp->nh_list;
if (nh_hashval(tmp->data) & nentries) {
/* put entry after nh */
break;
}
}
}
list_add_rcu(&entry->nh_list, prev);
return 0;
}
/* called with head's lock held */
static void __nh_sort_chain(struct nh_head * head, unsigned long nentries)
{
struct list_head tmp, *list = &head->list;
struct nh_entry* entry;
INIT_LIST_HEAD(&tmp);
list_splice_init(list, &tmp);
while ( !list_empty(&tmp) ) {
struct list_head * first = tmp.next;
list_del(first);
entry = (struct nh_entry*)first;
__nh_insert(entry, head, nentries);
}
}
static void nh_fixup_grow(struct rcu_head * head)
{
struct nh * nh;
struct nh * oh = container_of(head, struct nh, rcu_head);
unsigned long i, oentries = oh->nentries;
rcu_read_lock();
nh = get_nh();
/* this is an rcu_callback - only the "new" nh is
* visible elsewhere now, so make sure to access
* the hashtable using the proper (new) locks,... */
for (i = 0; i < oentries; i++) {
struct nh_head * nhead1 = &nh->hash[i];
struct nh_head * nhead2 = &nh->hash[i + oentries];
struct list_head *olist, *tail1, *tail2;
/* grab locks in order */
spin_lock(&nhead1->lock);
spin_lock(&nhead2->lock);
olist = &oh->hash[i].list;
list_del(olist); /* don't need rcu variant here */
tail1 = nhead2->list.prev;
tail2 = nhead1->list.prev;
nhead1->list.prev = tail1;
nhead2->list.prev = tail2;
tail1->next = &nhead1->list;
tail2->next = &nhead2->list;
__nh_sort_chain(nhead1, oentries << 1);
__nh_sort_chain(nhead2, oentries << 1);
spin_unlock(&nhead2->lock);
spin_unlock(&nhead1->lock);
}
rcu_read_unlock();
nh_free(head);
}
static void nh_fixup_shrink(struct rcu_head * head)
{
struct nh * nh;
struct nh * oh = container_of(head, struct nh, rcu_head);
unsigned long i, nentries;
rcu_read_lock();
nh = get_nh();
nentries = nh->nentries;
/* this is an rcu_callback - only the "new" nh is
* visible elsewhere now, so make sure to access
* the hashtable using the proper (new) locks,... */
for (i = 0; i < nh->nentries; i++) {
struct nh_head * nhead = &nh->hash[i];
struct list_head * olist1 = &oh->hash[i].list;
struct list_head * olist2 = &oh->hash[i + nentries].list;
spin_lock(&nhead->lock);
list_del(olist1); /* don't need RCU variants here */
list_del(olist2);
__nh_sort_chain(nhead, nentries);
spin_unlock(&nhead->lock);
}
rcu_read_unlock();
nh_free(head);
}
/* called under rcu_read_lock */
static struct nh * get_nh(void)
{
unsigned long seq;
struct nh * nh = NULL;
do {
seq = read_seqbegin(&nethash_seq);
nh = __nh;
} while (read_seqretry(&nethash_seq, seq));
return nh;
}
/* use returned value only under rcu_read_lock */
static struct nh * nh_replace(struct nh * new)
{
struct nh * old = NULL;
write_seqlock(&nethash_seq);
old = __nh;
__nh = new;
write_sequnlock(&nethash_seq);
return old;
}
static void nh_destroy(void)
{
unsigned long i;
struct nh * nh;
rcu_read_lock();
nh = nh_replace(NULL);
if (!nh)
goto out_unlock;
for (i = 0; i < nh->nentries; i++) {
struct nh_head * head = &nh->hash[i];
struct list_head *list;
spin_lock(&head->lock);
list_for_each_rcu(list, &head->list) {
struct nh_item * item = (struct nh_item *)list;
struct nh_entry *entry;
if (item->nh_type != NH_ENTRY)
continue;
entry = list_entry(list, struct nh_entry, nh_list);
list_del_rcu(&entry->nh_list);
nprintk("%s: delinking entry with data %lu\n",
__FUNCTION__, entry->data)
call_rcu(&entry->rcu_head, nh_entry_free);
}
spin_unlock(&head->lock);
}
call_rcu(&nh->rcu_head, nh_free);
out_unlock:
rcu_read_unlock();
}
static void nh_init(struct nh* nh,
struct nh_head * hash,
unsigned long nentries)
{
unsigned long i;
for (i = 0; i < nentries; i++) {
INIT_LIST_HEAD(&hash[i].list);
hash[i].nh_type = NH_HEAD;
spin_lock_init(&hash[i].lock);
}
nh->hash = hash;
nh->nentries = nentries;
}
static struct nh * nh_alloc(unsigned long nentries)
{
struct nh * nh;
unsigned long sz;
struct nh_head * hash;
/* nentries must be a power of 2 */
nentries = roundup_pow_of_two(nentries);
sz = nentries * sizeof(struct nh_head);
nprintk("%s: creating hash of %lu entries, size %lu bytes\n",
__FUNCTION__, nentries, sz);
nh = kmalloc(sizeof(struct nh), GFP_KERNEL);
if (!nh) {
nprintk("%s: failed to allocate hash of size %lu\n",
__FUNCTION__, sz);
return NULL;
}
hash = system_hash_alloc(sz);
if (!hash) {
nprintk("%s: failed to allocate hash of size %lu\n",
__FUNCTION__, sz);
kfree(nh);
return NULL;
}
nh_init(nh, hash, nentries);
return nh;
}
static int nh_create(unsigned long nentries)
{
struct nh * nh;
nh_destroy();
nh = nh_alloc(nentries);
if (!nh)
return -ENOMEM;
nh_replace(nh);
return 0;
}
static unsigned long nh_bucket(unsigned long hashval, unsigned long nentries)
{
/* nentries is a power of 2 */
return (hashval & (nentries - 1));
}
static int nh_add(unsigned long data)
{
struct nh_entry * entry;
struct nh * nh = NULL;
int ret = 0;
entry = kmalloc(sizeof(struct nh_entry), GFP_KERNEL);
if (!entry) return -ENOMEM;
entry->data = data;
entry->nh_type = NH_ENTRY;
rcu_read_lock();
nh = get_nh();
if (nh) {
struct nh_head *hash = nh->hash;
unsigned long nentries = nh->nentries;
unsigned long hashval = nh_hashval(data);
unsigned long bucket = nh_bucket(hashval, nentries);
struct nh_head * head = &hash[bucket];
spin_lock(&head->lock);
ret = __nh_insert(entry, head, nentries);
spin_unlock(&head->lock);
}
rcu_read_unlock();
return ret;
}
static void nh_migrate_grow(struct nh * old, struct nh * new)
{
unsigned long i, oentries = old->nentries;
/* this is called prior to calling replace_nh(), so
* only the "old" nh is visible elsewhere - only
* access the hashtable under the appropriate "old"
* locks */
for ( i = 0; i < oentries; i++ ) {
struct nh_head * ohead = &old->hash[i];
struct list_head * olist = &ohead->list;
struct list_head * nlist1 = &new->hash[i].list;
struct list_head * nlist2 = &new->hash[i + oentries].list;
struct nh_entry *entry;
struct list_head * list, * prev = NULL;
nprintk("%s: migrating bucket %lu\n", __FUNCTION__, i);
spin_lock(&ohead->lock);
list_add_rcu(nlist1, olist);
/* find where to split chain */
list_for_each_rcu(list, olist) {
struct nh_item * item = (struct nh_item *)list;
if (item->nh_type != NH_ENTRY) {
prev = list;
continue;
}
entry = list_entry(list, struct nh_entry, nh_list);
if (nh_hashval(entry->data) & oentries) {
break;
}
prev = list;
}
BUG_ON(prev == NULL);
list_add_rcu(nlist2, prev);
spin_unlock(&ohead->lock);
}
}
static void nh_migrate_shrink(struct nh * old, struct nh * new)
{
unsigned long i, oentries = old->nentries;
/* this is called prior to calling replace_nh(), so
* only the "old" nh is visible elsewhere - only
* access the hashtable under the appropriate "old"
* locks */
for ( i = 0; i < oentries >> 1; i++ ) {
struct list_head * nlist = &new->hash[i].list;
struct nh_head * ohead1 = &old->hash[i];
struct nh_head * ohead2 = &old->hash[i + (oentries>>1)];
struct list_head * olist1, * olist2, tmp;
nprintk("%s: migrating bucket %lu\n", __FUNCTION__, i);
/* acquire locks in order */
spin_lock(&ohead1->lock);
spin_lock(&ohead2->lock);
olist1 = &ohead1->list;
olist2 = &ohead2->list;
INIT_LIST_HEAD(&tmp);
list_add_tail_rcu(&tmp, olist2);
list_splice_init(&tmp, olist1->prev);
list_add_tail_rcu(nlist, olist1);
spin_unlock(&ohead2->lock);
spin_unlock(&ohead1->lock);
}
}
unsigned long nh_nentries_grow(void) {
unsigned long nentries = 0;
/* we're called under rcu_read_lock */
struct nh * nh = get_nh();
if (nh)
nentries = nh->nentries << 1;
return nentries;
}
unsigned long nh_nentries_shrink(void) {
unsigned long nentries = 0;
/* we're called under rcu_read_lock */
struct nh * nh = get_nh();
if (nh)
nentries = nh->nentries >> 1;
return nentries;
}
enum { NH_GROW_OPS, NH_SHRINK_OPS, NH_MAX_OPS };
/* all of the nh_resize_ops must be called under the
* nethash_resize_mutex and rcu_read_lock
*/
struct nh_resize_ops {
unsigned long (*nh_nentries_op) (void);
void (*nh_migrate_op) (struct nh *old, struct nh *new);
void (*nh_fixup_op) (struct rcu_head *head);
} nh_resize_ops [NH_MAX_OPS] = {
{
nh_nentries_grow,
nh_migrate_grow,
nh_fixup_grow,
},
{
nh_nentries_shrink,
nh_migrate_shrink,
nh_fixup_shrink,
},
};
static int nh_resize(long dir)
{
struct nh *nh, *new_nh;
unsigned long new_nentries;
int err = 0;
struct nh_resize_ops * nh_ops;
if (dir > 0)
nh_ops = &nh_resize_ops[NH_GROW_OPS];
else if (dir < 0)
nh_ops = &nh_resize_ops[NH_SHRINK_OPS];
else
return -EINVAL;
mutex_lock(&nethash_resize_mutex);
rcu_read_lock();
nh = get_nh();
if (!nh)
goto out_unlock;
new_nentries = nh_ops->nh_nentries_op();
nprintk("%s: resizing nh to %lu buckets\n", __FUNCTION__,
new_nentries);
new_nh = nh_alloc(new_nentries);
if (!new_nh) {
err = -ENOMEM;
goto out_unlock;
}
nh_ops->nh_migrate_op(nh, new_nh);
nh_dump(nh, "old nh [post migration]");
nh_dump(new_nh, "new nh [newborn]");
nh = nh_replace(new_nh);
/* now callers of get_nh() will get the updated
* version, but references to the old nh can still
* be in use. It must be possible for users to see
* all elements of the table using either the old
* or new nh */
call_rcu(&nh->rcu_head, nh_ops->nh_fixup_op);
rcu_read_unlock();
synchronize_net();
/* by holding the nethash_resize_mutex until after
* synchronize_net() we are sure that there are at
* most 2 nh structures that we need to be
* concerned with */
mutex_unlock(&nethash_resize_mutex);
rcu_read_lock();
nh_dump(new_nh, "new nh [done]");
rcu_read_unlock();
return err;
out_unlock:
rcu_read_unlock();
mutex_unlock(&nethash_resize_mutex);
return err;
}
static int nh_getalong(const char __user *buf, size_t count, long *res)
{
char kbuf[_NETHASH_BUFSZ_+1];
if (count > _NETHASH_BUFSZ_)
return -EINVAL;
if (copy_from_user(&kbuf, buf, count))
return -EFAULT;
kbuf[_NETHASH_BUFSZ_] = '\0';
if (sscanf(kbuf, "%ld", res) != 1)
return -EINVAL;
return 0;
}
ssize_t nh_create_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
long res;
int ret;
if ((ret = nh_getalong(buf, count, &res)) != 0)
return ret;
if(res <= 0)
return -EINVAL;
if ((ret = nh_create((unsigned long)res)) != 0)
return ret;
return count;
}
static struct file_operations nh_create_fops = {
.write = nh_create_write,
};
ssize_t nh_add_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
long res;
int ret;
if ((ret = nh_getalong(buf, count, &res)) != 0)
return ret;
if(res <= 0)
return -EINVAL;
if ((ret = nh_add((unsigned long)res)) != 0)
return ret;
return count;
}
static struct file_operations nh_add_fops = {
.write = nh_add_write,
};
ssize_t nh_resize_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
long res;
int ret;
if ((ret = nh_getalong(buf, count, &res)) != 0)
return ret;
if ((ret = nh_resize(res)) != 0)
return ret;
return count;
}
static struct file_operations nh_resize_fops = {
.write = nh_resize_write,
};
static void * d_start (struct seq_file *m, loff_t *pos)
{
struct nh * nh;
rcu_read_lock();
nh = get_nh();
if (nh) {
struct nh_head *hash = nh->hash;
unsigned long nentries = nh->nentries;
if (nentries > *pos) {
seq_printf(m, "%lu: ", (unsigned long)*pos);
return (&hash[*pos]);
}
}
return NULL;
}
static void * d_next (struct seq_file *m, void *v, loff_t *pos)
{
struct nh * nh;
(*pos)++;
/* rcu_read_lock is still held */
nh = get_nh();
if (nh) {
struct nh_head *hash = nh->hash;
unsigned long nentries = nh->nentries;
if (nentries > *pos) {
seq_printf(m, "%lu: ", (unsigned long)*pos);
return (&hash[*pos]);
}
}
return NULL;
}
static void d_stop (struct seq_file *m, void *v)
{
rcu_read_unlock();
}
static int d_show (struct seq_file *m, void *v)
{
struct nh_head *head = v;
struct nh_entry *entry;
struct list_head * list;
rcu_read_lock();
list_for_each_rcu(list, &head->list) {
struct nh_item * item = (struct nh_item *)list;
switch (item->nh_type) {
case NH_ENTRY:
entry = list_entry(list, struct nh_entry, nh_list);
seq_printf(m, "%lu", entry->data);
seq_printf(m, "%s", " -> ");
break;
case NH_HEAD:
seq_printf(m, "%s", " .. ");
break;
default:
seq_printf(m, "%s", "bad nh_type!\n");
}
}
rcu_read_unlock();
seq_printf(m, "%s", "\n");
return 0;
}
struct seq_operations nh_dump_op = {
.start = d_start,
.next = d_next,
.stop = d_stop,
.show = d_show,
};
static int nh_dump_open(struct inode *inode, struct file *file)
{
return seq_open(file, &nh_dump_op);
}
static struct file_operations proc_nh_dump_operations = {
.open = nh_dump_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
/* XXX nh_dump() is for for debug only
* it must be called under rcu_read_lock() */
static int nh_dump(struct nh * nh, const char * debug_str)
{
unsigned long i;
struct nh_head * head;
struct nh_entry *entry;
struct list_head * list;
nprintk("%s: %s\n", __FUNCTION__, debug_str);
for (i = 0; i < nh->nentries; i++ ) {
head = &nh->hash[i];
nprintk("[%lu]: (%p)", i, head);
list_for_each_rcu(list, &head->list) {
struct nh_item * item = (struct nh_item *)list;
nprintk(" %p ", list);
switch (item->nh_type) {
case NH_ENTRY:
entry = list_entry(list, struct nh_entry,
nh_list);
nprintk("%lu -> ", entry->data);
break;
case NH_HEAD:
nprintk(" .. ");
break;
default:
nprintk("bad nh_type!\n");
}
}
nprintk("\n");
}
return 0;
}
static int nethash_module_init(void)
{
struct proc_dir_entry *entry;
nh_proc_dir = proc_mkdir(MODULE_NAME, NULL);
if (!nh_proc_dir) return -ENODEV;
/* first the entry to create the hashtable */
entry = create_proc_entry("create", 0644, nh_proc_dir);
if (!entry) return -ENODEV;
entry->proc_fops = &nh_create_fops;
/* and the entry to add an element to the hashtable */
entry = create_proc_entry("add", 0644, nh_proc_dir);
if (!entry) return -ENODEV;
entry->proc_fops = &nh_add_fops;
/* the entry to dump the hashtable */
entry = create_proc_entry("dump", 0444, nh_proc_dir);
if (!entry) return -ENODEV;
entry->proc_fops = &proc_nh_dump_operations;
/* and to resize the hashtable */
entry = create_proc_entry("resize", 0644, nh_proc_dir);
if (!entry) return -ENODEV;
entry->proc_fops = &nh_resize_fops;
return 0;
}
static void nethash_module_exit(void)
{
nh_destroy();
rcu_barrier(); /* don't unload until rcu callbacks are done
* XXX is this really safe? */
if (nh_proc_dir) {
remove_proc_entry("resize", nh_proc_dir);
remove_proc_entry("dump", nh_proc_dir);
remove_proc_entry("add", nh_proc_dir);
remove_proc_entry("create", nh_proc_dir);
}
remove_proc_entry(MODULE_NAME, NULL);
printk("%s removed\n", MODULE_NAME);
}
module_init(nethash_module_init);
module_exit(nethash_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("akepner@sgi.com");
next prev parent reply other threads:[~2007-02-05 18:51 UTC|newest]
Thread overview: 102+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-02-04 7:41 Extensible hashing and RCU linux
2007-02-05 18:02 ` akepner
2007-02-17 13:13 ` Evgeniy Polyakov
2007-02-18 18:46 ` Eric Dumazet
2007-02-18 19:10 ` Evgeniy Polyakov
2007-02-18 20:21 ` Eric Dumazet
2007-02-18 21:23 ` Michael K. Edwards
2007-02-18 22:04 ` Michael K. Edwards
2007-02-19 12:04 ` Andi Kleen
2007-02-19 19:18 ` Michael K. Edwards
2007-02-19 11:41 ` Evgeniy Polyakov
2007-02-19 13:38 ` Eric Dumazet
2007-02-19 13:56 ` Evgeniy Polyakov
2007-02-19 14:14 ` Eric Dumazet
2007-02-19 14:25 ` Evgeniy Polyakov
2007-02-19 15:14 ` Eric Dumazet
2007-02-19 18:13 ` Eric Dumazet
2007-02-19 18:26 ` Benjamin LaHaise
2007-02-19 18:38 ` Benjamin LaHaise
2007-02-20 9:25 ` Evgeniy Polyakov
2007-02-20 9:57 ` David Miller
2007-02-20 10:22 ` Evgeniy Polyakov
2007-02-20 10:04 ` Eric Dumazet
2007-02-20 10:12 ` David Miller
2007-02-20 10:30 ` Evgeniy Polyakov
2007-02-20 11:10 ` Eric Dumazet
2007-02-20 11:23 ` Evgeniy Polyakov
2007-02-20 11:30 ` Eric Dumazet
2007-02-20 11:41 ` Evgeniy Polyakov
2007-02-20 10:49 ` Eric Dumazet
2007-02-20 15:07 ` Michael K. Edwards
2007-02-20 15:11 ` Evgeniy Polyakov
2007-02-20 15:49 ` Michael K. Edwards
2007-02-20 15:59 ` Evgeniy Polyakov
2007-02-20 16:08 ` Eric Dumazet
2007-02-20 16:20 ` Evgeniy Polyakov
2007-02-20 16:38 ` Eric Dumazet
2007-02-20 16:59 ` Evgeniy Polyakov
2007-02-20 17:05 ` Evgeniy Polyakov
2007-02-20 17:53 ` Eric Dumazet
2007-02-20 18:00 ` Evgeniy Polyakov
2007-02-20 18:55 ` Eric Dumazet
2007-02-20 19:06 ` Evgeniy Polyakov
2007-02-20 19:17 ` Eric Dumazet
2007-02-20 19:36 ` Evgeniy Polyakov
2007-02-20 19:44 ` Michael K. Edwards
2007-02-20 17:20 ` Eric Dumazet
2007-02-20 17:55 ` Evgeniy Polyakov
2007-02-20 18:12 ` Evgeniy Polyakov
2007-02-20 19:13 ` Michael K. Edwards
2007-02-20 19:44 ` Evgeniy Polyakov
2007-02-20 20:03 ` Michael K. Edwards
2007-02-20 20:09 ` Michael K. Edwards
2007-02-21 8:56 ` Evgeniy Polyakov
2007-02-21 9:34 ` David Miller
2007-02-21 9:51 ` Evgeniy Polyakov
2007-02-21 10:03 ` David Miller
2007-02-21 8:54 ` Evgeniy Polyakov
2007-02-21 9:15 ` Eric Dumazet
2007-02-21 9:27 ` Evgeniy Polyakov
2007-02-21 9:38 ` Eric Dumazet
2007-02-21 9:57 ` Evgeniy Polyakov
2007-02-21 21:15 ` Michael K. Edwards
2007-02-22 9:06 ` David Miller
2007-02-22 11:00 ` Michael K. Edwards
2007-02-22 11:07 ` David Miller
2007-02-22 19:24 ` Stephen Hemminger
2007-02-20 16:04 ` Eric Dumazet
2007-02-22 23:49 ` linux
2007-02-23 2:31 ` Michael K. Edwards
2007-02-20 10:44 ` Evgeniy Polyakov
2007-02-20 11:09 ` Eric Dumazet
2007-02-20 11:29 ` Evgeniy Polyakov
2007-02-20 11:34 ` Eric Dumazet
2007-02-20 11:45 ` Evgeniy Polyakov
2007-02-21 12:41 ` Andi Kleen
2007-02-21 13:19 ` Eric Dumazet
2007-02-21 13:37 ` David Miller
2007-02-21 23:13 ` Robert Olsson
2007-02-22 6:06 ` Eric Dumazet
2007-02-22 11:41 ` Andi Kleen
2007-02-22 11:44 ` David Miller
2007-02-20 12:11 ` Evgeniy Polyakov
2007-02-19 22:10 ` Andi Kleen
2007-02-19 12:02 ` Andi Kleen
2007-02-19 12:35 ` Robert Olsson
2007-02-19 14:04 ` Evgeniy Polyakov
2007-03-02 8:52 ` Evgeniy Polyakov
2007-03-02 9:56 ` Eric Dumazet
2007-03-02 10:28 ` Evgeniy Polyakov
2007-03-02 20:45 ` Michael K. Edwards
2007-03-03 10:46 ` Evgeniy Polyakov
2007-03-04 10:02 ` Michael K. Edwards
2007-03-04 20:36 ` David Miller
2007-03-05 7:12 ` Michael K. Edwards
2007-03-05 10:02 ` Robert Olsson
2007-03-05 10:00 ` Evgeniy Polyakov
2007-03-13 9:32 ` Evgeniy Polyakov
2007-03-13 10:08 ` Eric Dumazet
2007-03-13 10:24 ` Evgeniy Polyakov
2007-02-05 18:41 ` akepner [this message]
2007-02-06 19:09 ` [RFC/TOY]Extensible " linux
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Pine.LNX.4.61.0702051030550.26852@localhost.localdomain \
--to=akepner@sgi.com \
--cc=davem@davemloft.net \
--cc=linux@horizon.com \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox