diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /net/core/dev_mcast.c | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff) |
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too
o Upgrade to 2.1.89.
Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'net/core/dev_mcast.c')
-rw-r--r-- | net/core/dev_mcast.c | 130 |
1 files changed, 102 insertions, 28 deletions
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index eaa1bd058..a724497e0 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -19,7 +19,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ - + +#include <linux/config.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/bitops.h> @@ -37,6 +38,8 @@ #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/proc_fs.h> +#include <linux/init.h> #include <net/ip.h> #include <net/route.h> #include <linux/skbuff.h> @@ -52,6 +55,9 @@ * that a casual user application can add/delete multicasts used by * protocols without doing damage to the protocols when it deletes the * entries. It also helps IP as it tracks overlapping maps. + * + * BUGGGG! IPv6 calls dev_mac_add/delete from BH, it means + * that all the functions in this file are racy. [NOT FIXED] --ANK */ @@ -82,64 +88,81 @@ void dev_mc_upload(struct device *dev) * Delete a device level multicast */ -void dev_mc_delete(struct device *dev, void *addr, int alen, int all) +int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl) { - struct dev_mc_list **dmi; + struct dev_mc_list *dmi, **dmip; - for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next) - { + for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) { /* * Find the entry we want to delete. The device could * have variable length entries so check these too. */ - if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen) - { - struct dev_mc_list *tmp= *dmi; - if(--(*dmi)->dmi_users && !all) - return; + if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && alen==dmi->dmi_addrlen) { + if (glbl) { + int old_glbl = dmi->dmi_gusers; + dmi->dmi_gusers = 0; + if (old_glbl == 0) + return -ENOENT; + } + if(--dmi->dmi_users) + return 0; + /* * Last user. So delete the entry. */ - *dmi=(*dmi)->next; + *dmip = dmi->next; dev->mc_count--; - kfree_s(tmp,sizeof(*tmp)); + kfree_s(dmi,sizeof(*dmi)); /* * We have altered the list, so the card * loaded filter is now wrong. Fix it */ dev_mc_upload(dev); - return; + return 0; } } + return -ENOENT; } /* * Add a device level multicast */ -void dev_mc_add(struct device *dev, void *addr, int alen, int newonly) +int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) { struct dev_mc_list *dmi; - for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next) - { - if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) - { - if(!newonly) - dmi->dmi_users++; - return; + for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) { + if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) { + if (glbl) { + int old_glbl = dmi->dmi_gusers; + dmi->dmi_gusers = 1; + if (old_glbl) + return 0; + } + dmi->dmi_users++; + return 0; } } - dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi),GFP_KERNEL); - if(dmi==NULL) - return; /* GFP_KERNEL so can't happen anyway */ + + /* GFP_ATOMIC!! It is used by IPv6 from interrupt, + when new address arrives. + + Particularly, it means that this part of code is weirdly + racy, and needs numerous *_bh_atomic --ANK + */ + dmi=(struct dev_mc_list *)kmalloc(sizeof(*dmi), GFP_ATOMIC); + if (dmi==NULL) + return -ENOBUFS; memcpy(dmi->dmi_addr, addr, alen); dmi->dmi_addrlen=alen; dmi->next=dev->mc_list; dmi->dmi_users=1; + dmi->dmi_gusers=glbl ? 1 : 0; dev->mc_list=dmi; dev->mc_count++; dev_mc_upload(dev); + return 0; } /* @@ -148,13 +171,64 @@ void dev_mc_add(struct device *dev, void *addr, int alen, int newonly) void dev_mc_discard(struct device *dev) { - while(dev->mc_list!=NULL) - { + while (dev->mc_list!=NULL) { struct dev_mc_list *tmp=dev->mc_list; - dev->mc_list=dev->mc_list->next; - if (tmp->dmi_users) + dev->mc_list=tmp->next; + if (tmp->dmi_users > tmp->dmi_gusers) printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); kfree_s(tmp,sizeof(*tmp)); } dev->mc_count=0; } + +#ifdef CONFIG_PROC_FS +static int dev_mc_read_proc(char *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + off_t pos=0, begin=0; + struct dev_mc_list *m; + int len=0; + struct device *dev; + + for (dev = dev_base; dev; dev = dev->next) { + for (m = dev->mc_list; m; m = m->next) { + int i; + + len += sprintf(buffer+len,"%-4d %-15s %-5d %-5d ", dev->ifindex, dev->name, + m->dmi_users, m->dmi_gusers); + + for (i=0; i<m->dmi_addrlen; i++) + len += sprintf(buffer+len, "%02x", m->dmi_addr[i]); + + len+=sprintf(buffer+len, "\n"); + + pos=begin+len; + if (pos < offset) { + len=0; + begin=pos; + } + if (pos > offset+length) + goto done; + } + } + *eof = 1; + +done: + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; +} +#endif + +__initfunc(void dev_mcast_init(void)) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *ent; + + ent = create_proc_entry("net/dev_mcast", 0, 0); + ent->read_proc = dev_mc_read_proc; +#endif +} + |