diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /net | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'net')
121 files changed, 4812 insertions, 2876 deletions
diff --git a/net/802/llc_macinit.c b/net/802/llc_macinit.c index a51a868f2..da47f4883 100644 --- a/net/802/llc_macinit.c +++ b/net/802/llc_macinit.c @@ -17,6 +17,8 @@ * Alan Cox : Chainsawed to Linux format * Added llc_ to names * Started restructuring handlers + * + * Horst von Brand : Add #include <linux/string.h> */ #include <linux/module.h> @@ -24,6 +26,7 @@ #include <linux/kernel.h> #include <linux/malloc.h> #include <linux/unistd.h> +#include <linux/string.h> #include <linux/netdevice.h> #include <linux/init.h> #include <net/p8022.h> diff --git a/net/Config.in b/net/Config.in index ed8510209..53cd5b0c6 100644 --- a/net/Config.in +++ b/net/Config.in @@ -32,10 +32,10 @@ if [ "$CONFIG_IPX" != "n" ]; then fi tristate 'Appletalk DDP' CONFIG_ATALK if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -# tristate 'DECnet Support (NOT YET FUNCTIONAL)' CONFIG_DECNET -# if [ "$CONFIG_DECNET" != "n" ]; then -# source net/decnet/Config.in -# fi + tristate 'DECnet Support (EXPERIMENTAL)' CONFIG_DECNET + if [ "$CONFIG_DECNET" != "n" ]; then + source net/decnet/Config.in + fi tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE diff --git a/net/Makefile b/net/Makefile index d20953259..999e5b9eb 100644 --- a/net/Makefile +++ b/net/Makefile @@ -10,7 +10,7 @@ MOD_SUB_DIRS := ipv4 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \ netrom rose lapb x25 wanrouter netlink sched packet sunrpc \ - econet irda #decnet + econet irda decnet SUB_DIRS := core ethernet sched MOD_LIST_NAME := NET_MISC_MODULES diff --git a/net/core/datagram.c b/net/core/datagram.c index da09973cd..98233a224 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -6,7 +6,7 @@ * This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical poll code and mostly * identical recvmsg() code. So we share it here. The poll was shared before but buried in udp.c so I moved it. * - * Authors: Alan Cox <alan@cymru.net>. (datagram_poll() from old udp.c code) + * Authors: Alan Cox <alan@redhat.com>. (datagram_poll() from old udp.c code) * * Fixes: * Alan Cox : NULL return from skb_peek_copy() understood @@ -54,7 +54,7 @@ static inline void wait_for_packet(struct sock * sk) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); add_wait_queue(sk->sleep, &wait); current->state = TASK_INTERRUPTIBLE; @@ -151,15 +151,15 @@ restart: is reentearble (it is not) or this function is called by interrupts. - Protect it with global skb spinlock, + Protect it with skb queue spinlock, though for now even this is overkill. --ANK (980728) */ - spin_lock_irqsave(&skb_queue_lock, cpu_flags); + spin_lock_irqsave(&sk->receive_queue.lock, cpu_flags); skb = skb_peek(&sk->receive_queue); if(skb!=NULL) atomic_inc(&skb->users); - spin_unlock_irqrestore(&skb_queue_lock, cpu_flags); + spin_unlock_irqrestore(&sk->receive_queue.lock, cpu_flags); } else skb = skb_dequeue(&sk->receive_queue); diff --git a/net/core/dev.c b/net/core/dev.c index 921f05470..b9bd18343 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -129,8 +129,9 @@ const char *if_port_text[] = { * 86DD IPv6 */ -struct packet_type *ptype_base[16]; /* 16 way hashed list */ -struct packet_type *ptype_all = NULL; /* Taps */ +static struct packet_type *ptype_base[16]; /* 16 way hashed list */ +static struct packet_type *ptype_all = NULL; /* Taps */ +static rwlock_t ptype_lock = RW_LOCK_UNLOCKED; /* * Device list lock. Setting it provides that interface @@ -199,6 +200,7 @@ void dev_add_pack(struct packet_type *pt) dev_clear_fastroute(pt->dev); } #endif + write_lock_bh(&ptype_lock); if(pt->type==htons(ETH_P_ALL)) { netdev_nit++; @@ -211,6 +213,7 @@ void dev_add_pack(struct packet_type *pt) pt->next = ptype_base[hash]; ptype_base[hash] = pt; } + write_unlock_bh(&ptype_lock); } @@ -228,19 +231,21 @@ void dev_remove_pack(struct packet_type *pt) } else pt1=&ptype_base[ntohs(pt->type)&15]; + write_lock_bh(&ptype_lock); for(; (*pt1)!=NULL; pt1=&((*pt1)->next)) { if(pt==(*pt1)) { *pt1=pt->next; - synchronize_bh(); #ifdef CONFIG_NET_FASTROUTE if (pt->data) netdev_fastroute_obstacles--; #endif + write_unlock_bh(&ptype_lock); return; } } + write_unlock_bh(&ptype_lock); printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt); } @@ -258,37 +263,43 @@ struct device *dev_get(const char *name) { struct device *dev; - for (dev = dev_base; dev != NULL; dev = dev->next) - { + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if (strcmp(dev->name, name) == 0) - return(dev); + goto out; } - return NULL; +out: + read_unlock(&dev_base_lock); + return dev; } struct device * dev_get_by_index(int ifindex) { struct device *dev; - for (dev = dev_base; dev != NULL; dev = dev->next) - { + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->ifindex == ifindex) - return(dev); + goto out; } - return NULL; +out: + read_unlock(&dev_base_lock); + return dev; } struct device *dev_getbyhwaddr(unsigned short type, char *ha) { struct device *dev; - for (dev = dev_base; dev != NULL; dev = dev->next) - { + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->type == type && memcmp(dev->dev_addr, ha, dev->addr_len) == 0) - return(dev); + goto out; } - return(NULL); +out: + read_unlock(&dev_base_lock); + return dev; } /* @@ -310,7 +321,7 @@ int dev_alloc_name(struct device *dev, const char *name) } return -ENFILE; /* Over 100 of the things .. bail out! */ } - + struct device *dev_alloc(const char *name, int *err) { struct device *dev=kmalloc(sizeof(struct device)+16, GFP_KERNEL); @@ -438,8 +449,10 @@ void dev_clear_fastroute(struct device *dev) if (dev) { dev_do_clear_fastroute(dev); } else { + read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) dev_do_clear_fastroute(dev); + read_unlock(&dev_base_lock); } } #endif @@ -512,6 +525,7 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev) struct packet_type *ptype; get_fast_time(&skb->stamp); + read_lock(&ptype_lock); for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) { /* Never send packets back to the socket @@ -552,6 +566,7 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev) ptype->func(skb2, skb->dev, ptype); } } + read_unlock(&ptype_lock); } /* @@ -578,59 +593,61 @@ int dev_queue_xmit(struct sk_buff *skb) struct device *dev = skb->dev; struct Qdisc *q; -#ifdef CONFIG_NET_PROFILE - start_bh_atomic(); - NET_PROFILE_ENTER(dev_queue_xmit); -#endif - - start_bh_atomic(); + /* Grab device queue */ + spin_lock_bh(&dev->queue_lock); q = dev->qdisc; if (q->enqueue) { q->enqueue(skb, q); - qdisc_wakeup(dev); - end_bh_atomic(); -#ifdef CONFIG_NET_PROFILE - NET_PROFILE_LEAVE(dev_queue_xmit); - end_bh_atomic(); -#endif + /* If the device is not busy, kick it. + * Otherwise or if queue is not empty after kick, + * add it to run list. + */ + if (dev->tbusy || qdisc_restart(dev)) + qdisc_run(dev->qdisc); + spin_unlock_bh(&dev->queue_lock); return 0; } + spin_unlock_bh(&dev->queue_lock); /* The device has no queue. Common case for software devices: loopback, all the sorts of tunnels... - Really, it is unlikely that bh protection is necessary here: - virtual devices do not generate EOI events. - However, it is possible, that they rely on bh protection + Really, it is unlikely that xmit_lock protection is necessary here. + (f.e. loopback and IP tunnels are clean ignoring statistics counters.) + However, it is possible, that they rely on protection made by us here. + + Check this and shot the lock. It is not prone from deadlocks. + Either shot noqueue qdisc, it is even simpler 8) */ if (dev->flags&IFF_UP) { if (netdev_nit) dev_queue_xmit_nit(skb,dev); - if (dev->hard_start_xmit(skb, dev) == 0) { - end_bh_atomic(); - -#ifdef CONFIG_NET_PROFILE - NET_PROFILE_LEAVE(dev_queue_xmit); - end_bh_atomic(); -#endif - return 0; + local_bh_disable(); + if (dev->xmit_lock_owner != smp_processor_id()) { + spin_lock(&dev->xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); + if (dev->hard_start_xmit(skb, dev) == 0) { + dev->xmit_lock_owner = -1; + spin_unlock_bh(&dev->xmit_lock); + return 0; + } + dev->xmit_lock_owner = -1; + spin_unlock_bh(&dev->xmit_lock); + if (net_ratelimit()) + printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name); + } else { + /* Recursion is detected! It is possible, unfortunately */ + local_bh_enable(); + if (net_ratelimit()) + printk(KERN_DEBUG "Dead loop on virtual device %s, fix it urgently!\n", dev->name); } - if (net_ratelimit()) - printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name); } - end_bh_atomic(); kfree_skb(skb); - -#ifdef CONFIG_NET_PROFILE - NET_PROFILE_LEAVE(dev_queue_xmit); - end_bh_atomic(); -#endif - return 0; } @@ -642,9 +659,6 @@ int dev_queue_xmit(struct sk_buff *skb) int netdev_dropping = 0; int netdev_max_backlog = 300; atomic_t netdev_rx_dropped; -#ifdef CONFIG_CPU_IS_SLOW -int net_cpu_congestion; -#endif #ifdef CONFIG_NET_HW_FLOWCONTROL int netdev_throttle_events; @@ -732,9 +746,9 @@ static void dev_clear_backlog(struct device *dev) curr=curr->next; if ( curr->prev->dev == dev ) { prev = curr->prev; - spin_lock_irqsave(&skb_queue_lock, flags); + spin_lock_irqsave(&backlog.lock, flags); __skb_unlink(prev, &backlog); - spin_unlock_irqrestore(&skb_queue_lock, flags); + spin_unlock_irqrestore(&backlog.lock, flags); kfree_skb(prev); } } @@ -834,14 +848,6 @@ void net_bh(void) struct packet_type *pt_prev; unsigned short type; unsigned long start_time = jiffies; -#ifdef CONFIG_CPU_IS_SLOW - static unsigned long start_busy = 0; - static unsigned long ave_busy = 0; - - if (start_busy == 0) - start_busy = start_time; - net_cpu_congestion = ave_busy>>8; -#endif NET_PROFILE_ENTER(net_bh); /* @@ -851,9 +857,9 @@ void net_bh(void) * latency on a transmit interrupt bh. */ - if (qdisc_head.forw != &qdisc_head) + if (qdisc_pending()) qdisc_run_queues(); - + /* * Any data left to process. This may occur because a * mark_bh() is done after we empty the queue including @@ -881,19 +887,6 @@ void net_bh(void) */ skb = skb_dequeue(&backlog); -#ifdef CONFIG_CPU_IS_SLOW - if (ave_busy > 128*16) { - kfree_skb(skb); - while ((skb = skb_dequeue(&backlog)) != NULL) - kfree_skb(skb); - break; - } -#endif - - -#if 0 - NET_PROFILE_SKB_PASSED(skb, net_bh_skb); -#endif #ifdef CONFIG_NET_FASTROUTE if (skb->pkt_type == PACKET_FASTROUTE) { dev_queue_xmit(skb); @@ -939,6 +932,7 @@ void net_bh(void) */ pt_prev = NULL; + read_lock(&ptype_lock); for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next) { if (!ptype->dev || ptype->dev == skb->dev) { @@ -992,6 +986,7 @@ void net_bh(void) else { kfree_skb(skb); } + read_unlock(&ptype_lock); } /* End of queue loop */ /* @@ -1002,16 +997,9 @@ void net_bh(void) * One last output flush. */ - if (qdisc_head.forw != &qdisc_head) + if (qdisc_pending()) qdisc_run_queues(); -#ifdef CONFIG_CPU_IS_SLOW - if (1) { - unsigned long start_idle = jiffies; - ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4); - start_busy = 0; - } -#endif #ifdef CONFIG_NET_HW_FLOWCONTROL if (netdev_dropping) netdev_wakeup(); @@ -1045,14 +1033,6 @@ int register_gifconf(unsigned int family, gifconf_func_t * gifconf) */ /* - * This call is useful, but I'd remove it too. - * - * The reason is purely aestetical, it is the only call - * from SIOC* family using struct ifreq in reversed manner. - * Besides that, it is pretty silly to put "drawing" facility - * to kernel, it is useful only to print ifindices - * in readable form, is not it? --ANK - * * We need this ioctl for efficient implementation of the * if_indextoname() function required by the IPv6 API. Without * it, we would have to search all the interfaces to find a @@ -1105,14 +1085,20 @@ static int dev_ifconf(char *arg) if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) return -EFAULT; - pos = ifc.ifc_buf; len = ifc.ifc_len; + if (ifc.ifc_buf) { + pos = (char *) kmalloc(len, GFP_KERNEL); + if(pos == NULL) + return -ENOBUFS; + } else + pos = NULL; /* * Loop over the interfaces, and write an info block for each. */ total = 0; + read_lock(&dev_base_lock); for (dev = dev_base; dev != NULL; dev = dev->next) { for (i=0; i<NPROTO; i++) { if (gifconf_list[i]) { @@ -1122,12 +1108,19 @@ static int dev_ifconf(char *arg) } else { done = gifconf_list[i](dev, pos+total, len-total); } - if (done<0) - return -EFAULT; total += done; } } } + read_unlock(&dev_base_lock); + + if(pos != NULL) { + int err = copy_to_user(ifc.ifc_buf, pos, total); + + kfree(pos); + if(err) + return -EFAULT; + } /* * All done. Write the updated control block back to the caller. @@ -1199,20 +1192,20 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy len+=size; - for (dev = dev_base; dev != NULL; dev = dev->next) - { + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { size = sprintf_stats(buffer+len, dev); len+=size; pos=begin+len; - if(pos<offset) - { + if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } + read_unlock(&dev_base_lock); *start=buffer+(offset-begin); /* Start of wanted data */ len-=(offset-begin); /* Start slop */ @@ -1314,20 +1307,20 @@ int dev_get_wireless_info(char * buffer, char **start, off_t offset, pos+=size; len+=size; - for(dev = dev_base; dev != NULL; dev = dev->next) - { + read_lock(&dev_base_lock); + for(dev = dev_base; dev != NULL; dev = dev->next) { size = sprintf_wireless_stats(buffer+len, dev); len+=size; pos=begin+len; - if(pos < offset) - { + if(pos < offset) { len=0; begin=pos; } if(pos > offset + length) break; } + read_unlock(&dev_base_lock); *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); /* Start slop */ @@ -1703,11 +1696,10 @@ int dev_ioctl(unsigned int cmd, void *arg) if (IW_IS_SET(cmd)) { if (!suser()) return -EPERM; - rtnl_lock(); } + rtnl_lock(); ret = dev_ifsioc(&ifr, cmd); - if (IW_IS_SET(cmd)) - rtnl_unlock(); + rtnl_unlock(); if (!ret && IW_IS_GET(cmd) && copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; @@ -1736,6 +1728,10 @@ int register_netdevice(struct device *dev) { struct device *d, **dp; + spin_lock_init(&dev->queue_lock); + spin_lock_init(&dev->xmit_lock); + dev->xmit_lock_owner = -1; + if (dev_boot_phase) { /* This is NOT bug, but I am not sure, that all the devices, initialized before netdev module is started @@ -1752,11 +1748,14 @@ int register_netdevice(struct device *dev) /* Check for existence, and append to tail of chain */ for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) { - if (d == dev || strcmp(d->name, dev->name) == 0) + if (d == dev || strcmp(d->name, dev->name) == 0) { return -EEXIST; + } } dev->next = NULL; + write_lock_bh(&dev_base_lock); *dp = dev; + write_unlock_bh(&dev_base_lock); return 0; } @@ -1766,17 +1765,21 @@ int register_netdevice(struct device *dev) if (dev->init && dev->init(dev) != 0) return -EIO; + dev->ifindex = dev_new_index(); + if (dev->iflink == -1) + dev->iflink = dev->ifindex; + /* Check for existence, and append to tail of chain */ for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) { - if (d == dev || strcmp(d->name, dev->name) == 0) + if (d == dev || strcmp(d->name, dev->name) == 0) { return -EEXIST; + } } dev->next = NULL; dev_init_scheduler(dev); - dev->ifindex = dev_new_index(); - if (dev->iflink == -1) - dev->iflink = dev->ifindex; + write_lock_bh(&dev_base_lock); *dp = dev; + write_unlock_bh(&dev_base_lock); /* Notify protocols, that a new device appeared. */ notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); @@ -1788,15 +1791,35 @@ int unregister_netdevice(struct device *dev) { struct device *d, **dp; - if (dev_boot_phase == 0) { - /* If device is running, close it. - It is very bad idea, really we should - complain loudly here, but random hackery - in linux/drivers/net likes it. - */ - if (dev->flags & IFF_UP) - dev_close(dev); + /* If device is running, close it first. */ + if (dev->flags & IFF_UP) + dev_close(dev); + /* And unlink it from device chain. */ + for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) { + if (d == dev) { + write_lock_bh(&dev_base_lock); + *dp = d->next; + write_unlock_bh(&dev_base_lock); + + /* Sorry. It is known "feature". The race is clear. + Keep it after device reference counting will + be complete. + */ + synchronize_bh(); + break; + } + } + if (d == NULL) + return -ENODEV; + + /* It is "synchronize_bh" to those of guys, who overslept + in skb_alloc/page fault etc. that device is off-line. + Again, it can be removed only if devices are refcounted. + */ + dev_lock_wait(); + + if (dev_boot_phase == 0) { #ifdef CONFIG_NET_FASTROUTE dev_clear_fastroute(dev); #endif @@ -1813,25 +1836,11 @@ int unregister_netdevice(struct device *dev) * Flush the multicast chain */ dev_mc_discard(dev); - - /* To avoid pointers looking to nowhere, - we wait for end of critical section */ - dev_lock_wait(); } - /* And unlink it from device chain. */ - for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) { - if (d == dev) { - *dp = d->next; - synchronize_bh(); - d->next = NULL; - - if (dev->destructor) - dev->destructor(dev); - return 0; - } - } - return -ENODEV; + if (dev->destructor) + dev->destructor(dev); + return 0; } @@ -1973,22 +1982,25 @@ __initfunc(int net_dev_init(void)) * If the call to dev->init fails, the dev is removed * from the chain disconnecting the device until the * next reboot. + * + * NB At boot phase networking is dead. No locking is required. + * But we still preserve dev_base_lock for sanity. */ dp = &dev_base; - while ((dev = *dp) != NULL) - { + while ((dev = *dp) != NULL) { + spin_lock_init(&dev->queue_lock); + spin_lock_init(&dev->xmit_lock); + dev->xmit_lock_owner = -1; dev->iflink = -1; - if (dev->init && dev->init(dev)) - { + if (dev->init && dev->init(dev)) { /* * It failed to come up. Unhook it. */ + write_lock_bh(&dev_base_lock); *dp = dev->next; - synchronize_bh(); - } - else - { + write_unlock_bh(&dev_base_lock); + } else { dp = &dev->next; dev->ifindex = dev_new_index(); if (dev->iflink == -1) @@ -2015,6 +2027,7 @@ __initfunc(int net_dev_init(void)) dev_boot_phase = 0; + dst_init(); dev_mcast_init(); #ifdef CONFIG_IP_PNP diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index bce3f4a4a..f7fcb1f87 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -58,7 +58,11 @@ * * Device mc lists are changed by bh at least if IPv6 is enabled, * so that it must be bh protected. + * + * We protect all mc lists with global rw lock + * and block accesses to device mc filters with dev->xmit_lock. */ +static rwlock_t dev_mc_lock = RW_LOCK_UNLOCKED; /* * Update the multicast list into the physical NIC controller. @@ -69,7 +73,7 @@ void dev_mc_upload(struct device *dev) /* Don't do anything till we up the interface [dev_open will call this function so the list will stay sane] */ - + if(!(dev->flags&IFF_UP)) return; @@ -80,11 +84,15 @@ void dev_mc_upload(struct device *dev) if(dev->set_multicast_list==NULL) return; - start_bh_atomic(); + read_lock_bh(&dev_mc_lock); + spin_lock(&dev->xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); dev->set_multicast_list(dev); - end_bh_atomic(); + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); + read_unlock_bh(&dev_mc_lock); } - + /* * Delete a device level multicast */ @@ -94,7 +102,7 @@ int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl) int err = 0; struct dev_mc_list *dmi, **dmip; - start_bh_atomic(); + write_lock_bh(&dev_mc_lock); for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) { /* * Find the entry we want to delete. The device could @@ -120,14 +128,15 @@ int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl) * We have altered the list, so the card * loaded filter is now wrong. Fix it */ - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); + dev_mc_upload(dev); return 0; } } err = -ENOENT; done: - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); return err; } @@ -140,9 +149,12 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) int err = 0; struct dev_mc_list *dmi, *dmi1; + /* RED-PEN: does gfp_any() work now? It requires + true local_bh_disable rather than global. + */ dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any()); - start_bh_atomic(); + write_lock_bh(&dev_mc_lock); 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) { @@ -156,8 +168,10 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) } } - if ((dmi=dmi1)==NULL) + if ((dmi=dmi1)==NULL) { + write_unlock_bh(&dev_mc_lock); return -ENOMEM; + } memcpy(dmi->dmi_addr, addr, alen); dmi->dmi_addrlen=alen; dmi->next=dev->mc_list; @@ -165,12 +179,12 @@ int dev_mc_add(struct device *dev, void *addr, int alen, int glbl) dmi->dmi_gusers=glbl ? 1 : 0; dev->mc_list=dmi; dev->mc_count++; - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); dev_mc_upload(dev); return 0; done: - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); if (dmi1) kfree(dmi1); return err; @@ -182,7 +196,7 @@ done: void dev_mc_discard(struct device *dev) { - start_bh_atomic(); + write_lock_bh(&dev_mc_lock); while (dev->mc_list!=NULL) { struct dev_mc_list *tmp=dev->mc_list; dev->mc_list=tmp->next; @@ -191,7 +205,7 @@ void dev_mc_discard(struct device *dev) kfree_s(tmp,sizeof(*tmp)); } dev->mc_count=0; - end_bh_atomic(); + write_unlock_bh(&dev_mc_lock); } #ifdef CONFIG_PROC_FS @@ -203,9 +217,9 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset, int len=0; struct device *dev; - start_bh_atomic(); - + read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { + read_lock_bh(&dev_mc_lock); for (m = dev->mc_list; m; m = m->next) { int i; @@ -222,14 +236,17 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset, len=0; begin=pos; } - if (pos > offset+length) + if (pos > offset+length) { + read_unlock_bh(&dev_mc_lock); goto done; + } } + read_unlock_bh(&dev_mc_lock); } *eof = 1; done: - end_bh_atomic(); + read_unlock(&dev_base_lock); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) diff --git a/net/core/dst.c b/net/core/dst.c index 9007dde66..f1695ca84 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -16,11 +16,22 @@ #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/skbuff.h> +#include <linux/init.h> #include <net/dst.h> -struct dst_entry * dst_garbage_list; -atomic_t dst_total = ATOMIC_INIT(0); +/* Locking strategy: + * 1) Garbage collection state of dead destination cache + * entries is protected by dst_lock. + * 2) GC is run only from BH context, and is the only remover + * of entries. + * 3) Entries are added to the garbage list from both BH + * and non-BH context, so local BH disabling is needed. + * 4) All operations modify state, so a spinlock is used. + */ +static struct dst_entry *dst_garbage_list; +static atomic_t dst_total = ATOMIC_INIT(0); +static spinlock_t dst_lock = SPIN_LOCK_UNLOCKED; static unsigned long dst_gc_timer_expires; static unsigned long dst_gc_timer_inc = DST_GC_MAX; @@ -29,15 +40,17 @@ static void dst_run_gc(unsigned long); static struct timer_list dst_gc_timer = { NULL, NULL, DST_GC_MIN, 0L, dst_run_gc }; -#if RT_CACHE_DEBUG >= 2 -atomic_t hh_count; -#endif static void dst_run_gc(unsigned long dummy) { int delayed = 0; struct dst_entry * dst, **dstp; + if (!spin_trylock(&dst_lock)) { + mod_timer(&dst_gc_timer, jiffies + HZ/10); + return; + } + del_timer(&dst_gc_timer); dstp = &dst_garbage_list; while ((dst = *dstp) != NULL) { @@ -51,7 +64,7 @@ static void dst_run_gc(unsigned long dummy) } if (!dst_garbage_list) { dst_gc_timer_inc = DST_GC_MAX; - return; + goto out; } if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX) dst_gc_timer_expires = DST_GC_MAX; @@ -62,6 +75,9 @@ static void dst_run_gc(unsigned long dummy) atomic_read(&dst_total), delayed, dst_gc_timer_expires); #endif add_timer(&dst_gc_timer); + +out: + spin_unlock(&dst_lock); } static int dst_discard(struct sk_buff *skb) @@ -100,7 +116,8 @@ void * dst_alloc(int size, struct dst_ops * ops) void __dst_free(struct dst_entry * dst) { - start_bh_atomic(); + spin_lock_bh(&dst_lock); + /* The first case (dev==NULL) is required, when protocol module is unloaded. */ @@ -119,7 +136,8 @@ void __dst_free(struct dst_entry * dst) dst_gc_timer.expires = jiffies + dst_gc_timer_expires; add_timer(&dst_gc_timer); } - end_bh_atomic(); + + spin_unlock_bh(&dst_lock); } void dst_destroy(struct dst_entry * dst) @@ -143,3 +161,36 @@ void dst_destroy(struct dst_entry * dst) atomic_dec(&dst_total); kfree(dst); } + +static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct device *dev = ptr; + struct dst_entry *dst; + + switch (event) { + case NETDEV_UNREGISTER: + case NETDEV_DOWN: + spin_lock_bh(&dst_lock); + for (dst = dst_garbage_list; dst; dst = dst->next) { + if (dst->dev == dev) { + dst->input = dst_discard; + dst->output = dst_blackhole; + dst->dev = &loopback_dev; + } + } + spin_unlock_bh(&dst_lock); + break; + } + return NOTIFY_DONE; +} + +struct notifier_block dst_dev_notifier = { + dst_dev_event, + NULL, + 0 +}; + +__initfunc(void dst_init(void)) +{ + register_netdevice_notifier(&dst_dev_notifier); +} diff --git a/net/core/filter.c b/net/core/filter.c index cc1ed83cd..8e1ffb628 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -106,7 +106,7 @@ int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen) continue; case BPF_ALU|BPF_MUL|BPF_K: - A *= X; + A *= fentry->k; continue; case BPF_ALU|BPF_DIV|BPF_X: diff --git a/net/core/firewall.c b/net/core/firewall.c index fc7b1a517..7ca90f49a 100644 --- a/net/core/firewall.c +++ b/net/core/firewall.c @@ -13,7 +13,7 @@ #include <linux/interrupt.h> #include <asm/semaphore.h> -struct semaphore firewall_sem = MUTEX; +DECLARE_MUTEX(firewall_sem); static int firewall_policy[NPROTO]; static struct firewall_ops *firewall_chain[NPROTO]; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index b96650bcd..6124fcfc3 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -28,33 +28,6 @@ #include <net/sock.h> #include <linux/rtnetlink.h> -/* - NOTE. The most unpleasent question is serialization of - accesses to resolved addresses. The problem is that addresses - are modified by bh, but they are referenced from normal - kernel thread. Before today no locking was made. - My reasoning was that corrupted address token will be copied - to packet with cosmologically small probability - (it is even difficult to estimate such small number) - and it is very silly to waste cycles in fast path to lock them. - - But now I changed my mind, but not because previous statement - is wrong. Actually, neigh->ha MAY BE not opaque byte array, - but reference to some private data. In this case even neglibible - corruption probability becomes bug. - - - hh cache is protected by rwlock. It assumes that - hh cache update procedure is short and fast, and that - read_lock is cheaper than start_bh_atomic(). - - ha tokens, saved in neighbour entries, are protected - by bh_atomic(). - - no protection is made in /proc reading. It is OK, because - /proc is broken by design in any case, and - corrupted output is normal behaviour there. - - --ANK (981025) - */ - #define NEIGH_DEBUG 1 #define NEIGH_PRINTK(x...) printk(x) @@ -81,6 +54,46 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct device *dev); static int neigh_glbl_allocs; static struct neigh_table *neigh_tables; +#if defined(__i386__) && defined(__SMP__) +#define ASSERT_WL(n) if ((int)((n)->lock.lock) >= 0) { printk("WL assertion failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); } +#else +#define ASSERT_WL(n) do { } while(0) +#endif + +/* + Neighbour hash table buckets are protected with rwlock tbl->lock. + + - All the scans/updates to hash buckets MUST be made under this lock. + - NOTHING clever should be made under this lock: no callbacks + to protocol backends, no attempts to send something to network. + It will result in deadlocks, if backend/driver wants to use neighbour + cache. + - If the entry requires some non-trivial actions, increase + its reference count and release table lock. + + Neighbour entries are protected: + - with reference count. + - with rwlock neigh->lock + + Reference count prevents destruction. + + neigh->lock mainly serializes ll address data and its validity state. + However, the same lock is used to protect another entry fields: + - timer + - resolution queue + + Again, nothing clever shall be made under neigh->lock, + the most complicated procedure, which we allow is dev->hard_header. + It is supposed, that dev->hard_header is simplistic and does + not make callbacks to neighbour tables. + + The last lock is neigh_tbl_lock. It is pure SMP lock, protecting + list of neighbour tables. This list is used only in process context, + so that this lock is useless with big kernel lock. + */ + +static rwlock_t neigh_tbl_lock = RW_LOCK_UNLOCKED; + static int neigh_blackhole(struct sk_buff *skb) { kfree_skb(skb); @@ -104,13 +117,11 @@ static int neigh_forced_gc(struct neigh_table *tbl) int shrunk = 0; int i; - if (atomic_read(&tbl->lock)) - return 0; - for (i=0; i<=NEIGH_HASHMASK; i++) { struct neighbour *n, **np; np = &tbl->hash_buckets[i]; + write_lock_bh(&tbl->lock); while ((n = *np) != NULL) { /* Neighbour record may be discarded if: - nobody refers to it. @@ -122,6 +133,7 @@ static int neigh_forced_gc(struct neigh_table *tbl) It is not clear, what is better table overflow or flooding. */ + write_lock(&n->lock); if (atomic_read(&n->refcnt) == 0 && !(n->nud_state&NUD_PERMANENT) && (n->nud_state != NUD_INCOMPLETE || @@ -130,11 +142,14 @@ static int neigh_forced_gc(struct neigh_table *tbl) n->tbl = NULL; tbl->entries--; shrunk = 1; + write_unlock(&n->lock); neigh_destroy(n); continue; } + write_unlock(&n->lock); np = &n->next; } + write_unlock_bh(&tbl->lock); } tbl->last_flush = jiffies; @@ -145,12 +160,8 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev) { int i; - if (atomic_read(&tbl->lock)) { - NEIGH_PRINTK1("neigh_ifdown: impossible event 1763\n"); - return -EBUSY; - } + write_lock_bh(&tbl->lock); - start_bh_atomic(); for (i=0; i<=NEIGH_HASHMASK; i++) { struct neighbour *n, **np; @@ -161,6 +172,7 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev) continue; } *np = n->next; + write_lock(&n->lock); n->tbl = NULL; tbl->entries--; if (atomic_read(&n->refcnt)) { @@ -183,33 +195,32 @@ int neigh_ifdown(struct neigh_table *tbl, struct device *dev) else n->nud_state = NUD_NONE; NEIGH_PRINTK2("neigh %p is stray.\n", n); - } else + write_unlock(&n->lock); + } else { + write_unlock(&n->lock); neigh_destroy(n); + } } } del_timer(&tbl->proxy_timer); skb_queue_purge(&tbl->proxy_queue); pneigh_ifdown(tbl, dev); - end_bh_atomic(); + write_unlock_bh(&tbl->lock); return 0; } -static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat) +static struct neighbour *neigh_alloc(struct neigh_table *tbl) { struct neighbour *n; unsigned long now = jiffies; - if (tbl->entries > tbl->gc_thresh1) { - if (creat < 0) + if (tbl->entries > tbl->gc_thresh3 || + (tbl->entries > tbl->gc_thresh2 && + now - tbl->last_flush > 5*HZ)) { + if (neigh_forced_gc(tbl) == 0 && + tbl->entries > tbl->gc_thresh3) return NULL; - if (tbl->entries > tbl->gc_thresh3 || - (tbl->entries > tbl->gc_thresh2 && - now - tbl->last_flush > 5*HZ)) { - if (neigh_forced_gc(tbl) == 0 && - tbl->entries > tbl->gc_thresh3) - return NULL; - } } n = kmalloc(tbl->entry_size, GFP_ATOMIC); @@ -219,6 +230,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat) memset(n, 0, tbl->entry_size); skb_queue_head_init(&n->arp_queue); + n->lock = RW_LOCK_UNLOCKED; n->updated = n->used = now; n->nud_state = NUD_NONE; n->output = neigh_blackhole; @@ -231,9 +243,8 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat) return n; } - -struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey, - struct device *dev, int creat) +struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, + struct device *dev) { struct neighbour *n; u32 hash_val; @@ -245,17 +256,26 @@ struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey, hash_val ^= hash_val>>3; hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK; + read_lock_bh(&tbl->lock); for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { if (dev == n->dev && memcmp(n->primary_key, pkey, key_len) == 0) { atomic_inc(&n->refcnt); - return n; + break; } } - if (!creat) - return NULL; + read_unlock_bh(&tbl->lock); + return n; +} + +struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey, + struct device *dev) +{ + struct neighbour *n, *n1; + u32 hash_val; + int key_len = tbl->key_len; - n = neigh_alloc(tbl, creat); + n = neigh_alloc(tbl); if (n == NULL) return NULL; @@ -275,11 +295,30 @@ struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey, } n->confirmed = jiffies - (n->parms->base_reachable_time<<1); - atomic_set(&n->refcnt, 1); + + hash_val = *(u32*)(pkey + key_len - 4); + hash_val ^= (hash_val>>16); + hash_val ^= hash_val>>8; + hash_val ^= hash_val>>3; + hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK; + + write_lock_bh(&tbl->lock); + for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) { + if (dev == n1->dev && + memcmp(n1->primary_key, pkey, key_len) == 0) { + atomic_inc(&n1->refcnt); + write_unlock_bh(&tbl->lock); + neigh_destroy(n); + return n1; + } + } + tbl->entries++; + n->tbl = tbl; + atomic_set(&n->refcnt, 1); n->next = tbl->hash_buckets[hash_val]; tbl->hash_buckets[hash_val] = n; - n->tbl = tbl; + write_unlock_bh(&tbl->lock); NEIGH_PRINTK2("neigh %p is created.\n", n); return n; } @@ -391,7 +430,9 @@ void neigh_destroy(struct neighbour *neigh) while ((hh = neigh->hh) != NULL) { neigh->hh = hh->hh_next; hh->hh_next = NULL; + write_lock_bh(&hh->hh_lock); hh->hh_output = neigh_blackhole; + write_unlock_bh(&hh->hh_lock); if (atomic_dec_and_test(&hh->hh_refcnt)) kfree(hh); } @@ -409,6 +450,8 @@ void neigh_destroy(struct neighbour *neigh) /* Neighbour state is suspicious; disable fast path. + + Called with write_locked neigh. */ static void neigh_suspect(struct neighbour *neigh) { @@ -416,6 +459,8 @@ static void neigh_suspect(struct neighbour *neigh) NEIGH_PRINTK2("neigh %p is suspecteded.\n", neigh); + ASSERT_WL(neigh); + neigh->output = neigh->ops->output; for (hh = neigh->hh; hh; hh = hh->hh_next) @@ -424,6 +469,8 @@ static void neigh_suspect(struct neighbour *neigh) /* Neighbour state is OK; enable fast path. + + Called with write_locked neigh. */ static void neigh_connect(struct neighbour *neigh) { @@ -431,6 +478,8 @@ static void neigh_connect(struct neighbour *neigh) NEIGH_PRINTK2("neigh %p is connected.\n", neigh); + ASSERT_WL(neigh); + neigh->output = neigh->ops->connected_output; for (hh = neigh->hh; hh; hh = hh->hh_next) @@ -446,6 +495,8 @@ static void neigh_connect(struct neighbour *neigh) If a routine wants to know TRUE entry state, it calls neigh_sync before checking state. + + Called with write_locked neigh. */ static void neigh_sync(struct neighbour *n) @@ -453,6 +504,7 @@ static void neigh_sync(struct neighbour *n) unsigned long now = jiffies; u8 state = n->nud_state; + ASSERT_WL(n); if (state&(NUD_NOARP|NUD_PERMANENT)) return; if (state&NUD_REACHABLE) { @@ -476,11 +528,8 @@ static void neigh_periodic_timer(unsigned long arg) unsigned long now = jiffies; int i; - if (atomic_read(&tbl->lock)) { - tbl->gc_timer.expires = now + 1*HZ; - add_timer(&tbl->gc_timer); - return; - } + + write_lock(&tbl->lock); /* * periodicly recompute ReachableTime from random function @@ -498,10 +547,15 @@ static void neigh_periodic_timer(unsigned long arg) np = &tbl->hash_buckets[i]; while ((n = *np) != NULL) { - unsigned state = n->nud_state; + unsigned state; - if (state&(NUD_PERMANENT|NUD_IN_TIMER)) + write_lock(&n->lock); + + state = n->nud_state; + if (state&(NUD_PERMANENT|NUD_IN_TIMER)) { + write_unlock(&n->lock); goto next_elt; + } if ((long)(n->used - n->confirmed) < 0) n->used = n->confirmed; @@ -512,6 +566,7 @@ static void neigh_periodic_timer(unsigned long arg) n->tbl = NULL; n->next = NULL; tbl->entries--; + write_unlock(&n->lock); neigh_destroy(n); continue; } @@ -521,6 +576,7 @@ static void neigh_periodic_timer(unsigned long arg) n->nud_state = NUD_STALE; neigh_suspect(n); } + write_unlock(&n->lock); next_elt: np = &n->next; @@ -529,6 +585,7 @@ next_elt: tbl->gc_timer.expires = now + tbl->gc_interval; add_timer(&tbl->gc_timer); + write_unlock(&tbl->lock); } static __inline__ int neigh_max_probes(struct neighbour *n) @@ -544,11 +601,17 @@ static void neigh_timer_handler(unsigned long arg) { unsigned long now = jiffies; struct neighbour *neigh = (struct neighbour*)arg; - unsigned state = neigh->nud_state; + unsigned state; + int notify = 0; + + write_lock(&neigh->lock); + atomic_inc(&neigh->refcnt); + + state = neigh->nud_state; if (!(state&NUD_IN_TIMER)) { NEIGH_PRINTK1("neigh: timer & !nud_in_timer\n"); - return; + goto out; } if ((state&NUD_VALID) && @@ -556,18 +619,19 @@ static void neigh_timer_handler(unsigned long arg) neigh->nud_state = NUD_REACHABLE; NEIGH_PRINTK2("neigh %p is still alive.\n", neigh); neigh_connect(neigh); - return; + goto out; } if (state == NUD_DELAY) { NEIGH_PRINTK2("neigh %p is probed.\n", neigh); neigh->nud_state = NUD_PROBE; - neigh->probes = 0; + atomic_set(&neigh->probes, 0); } - if (neigh->probes >= neigh_max_probes(neigh)) { + if (atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { struct sk_buff *skb; neigh->nud_state = NUD_FAILED; + notify = 1; neigh->tbl->stats.res_failed++; NEIGH_PRINTK2("neigh %p is failed.\n", neigh); @@ -576,44 +640,60 @@ static void neigh_timer_handler(unsigned long arg) So that, we try to be accurate and avoid dead loop. --ANK */ - while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) + while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) { + write_unlock(&neigh->lock); neigh->ops->error_report(neigh, skb); + write_lock(&neigh->lock); + } skb_queue_purge(&neigh->arp_queue); - return; + goto out; } neigh->timer.expires = now + neigh->parms->retrans_time; add_timer(&neigh->timer); + write_unlock(&neigh->lock); neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue)); - neigh->probes++; + atomic_inc(&neigh->probes); + neigh_release(neigh); + return; + +out: + write_unlock(&neigh->lock); +#ifdef CONFIG_ARPD + if (notify && neigh->parms->app_probes) + neigh_app_notify(neigh); +#endif + neigh_release(neigh); } int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) { - start_bh_atomic(); + write_lock_bh(&neigh->lock); if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) { if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) { if (neigh->tbl == NULL) { NEIGH_PRINTK2("neigh %p used after death.\n", neigh); if (skb) kfree_skb(skb); - end_bh_atomic(); + write_unlock_bh(&neigh->lock); return 1; } if (neigh->parms->mcast_probes + neigh->parms->app_probes) { - neigh->probes = neigh->parms->ucast_probes; + atomic_set(&neigh->probes, neigh->parms->ucast_probes); neigh->nud_state = NUD_INCOMPLETE; neigh->timer.expires = jiffies + neigh->parms->retrans_time; add_timer(&neigh->timer); - + write_unlock_bh(&neigh->lock); neigh->ops->solicit(neigh, skb); - neigh->probes++; + atomic_inc(&neigh->probes); + write_lock_bh(&neigh->lock); } else { neigh->nud_state = NUD_FAILED; + write_unlock_bh(&neigh->lock); + if (skb) kfree_skb(skb); - end_bh_atomic(); return 1; } } @@ -627,7 +707,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) } __skb_queue_head(&neigh->arp_queue, skb); } - end_bh_atomic(); + write_unlock_bh(&neigh->lock); return 1; } if (neigh->nud_state == NUD_STALE) { @@ -637,7 +717,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) add_timer(&neigh->timer); } } - end_bh_atomic(); + write_unlock_bh(&neigh->lock); return 0; } @@ -649,9 +729,9 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh) if (update) { for (hh=neigh->hh; hh; hh=hh->hh_next) { - write_lock_irq(&hh->hh_lock); + write_lock_bh(&hh->hh_lock); update(hh, neigh->dev, neigh->ha); - write_unlock_irq(&hh->hh_lock); + write_unlock_bh(&hh->hh_lock); } } } @@ -663,15 +743,23 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh) -- new is new state. -- override==1 allows to override existing lladdr, if it is different. -- arp==0 means that the change is administrative. + + Caller MUST hold reference count on the entry. */ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp) { - u8 old = neigh->nud_state; + u8 old; + int err; + int notify = 0; struct device *dev = neigh->dev; + write_lock_bh(&neigh->lock); + old = neigh->nud_state; + + err = -EPERM; if (arp && (old&(NUD_NOARP|NUD_PERMANENT))) - return -EPERM; + goto out; if (!(new&NUD_VALID)) { if (old&NUD_IN_TIMER) @@ -679,7 +767,9 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int if (old&NUD_CONNECTED) neigh_suspect(neigh); neigh->nud_state = new; - return 0; + err = 0; + notify = old&NUD_VALID; + goto out; } /* Compare new lladdr with cached one */ @@ -696,14 +786,15 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0) lladdr = neigh->ha; else if (!override) - return -EPERM; + goto out; } } else { /* No address is supplied; if we know something, use it, otherwise discard the request. */ + err = -EINVAL; if (!(old&NUD_VALID)) - return -EINVAL; + goto out; lladdr = neigh->ha; } @@ -716,10 +807,11 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int /* If entry was valid and address is not changed, do not change entry state, if new one is STALE. */ + err = 0; if (old&NUD_VALID) { if (lladdr == neigh->ha) if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED))) - return 0; + goto out; } if (old&NUD_IN_TIMER) del_timer(&neigh->timer); @@ -729,12 +821,11 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int neigh_update_hhs(neigh); neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1); #ifdef CONFIG_ARPD - if (neigh->parms->app_probes) - neigh_app_notify(neigh); + notify = 1; #endif } if (new == old) - return 0; + goto out; if (new&NUD_CONNECTED) neigh_connect(neigh); else @@ -747,14 +838,22 @@ int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int while (neigh->nud_state&NUD_VALID && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) { struct neighbour *n1 = neigh; + write_unlock_bh(&neigh->lock); /* On shaper/eql skb->dst->neighbour != neigh :( */ if (skb->dst && skb->dst->neighbour) n1 = skb->dst->neighbour; n1->output(skb); + write_lock_bh(&neigh->lock); } skb_queue_purge(&neigh->arp_queue); } - return 0; +out: + write_unlock_bh(&neigh->lock); +#ifdef CONFIG_ARPD + if (notify && neigh->parms->app_probes) + neigh_app_notify(neigh); +#endif + return err; } struct neighbour * neigh_event_ns(struct neigh_table *tbl, @@ -837,15 +936,15 @@ int neigh_resolve_output(struct sk_buff *skb) int err; struct device *dev = neigh->dev; if (dev->hard_header_cache && dst->hh == NULL) { - start_bh_atomic(); + write_lock_bh(&neigh->lock); if (dst->hh == NULL) neigh_hh_init(neigh, dst, dst->ops->protocol); err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); - end_bh_atomic(); + write_unlock_bh(&neigh->lock); } else { - start_bh_atomic(); + read_lock_bh(&neigh->lock); err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); - end_bh_atomic(); + read_unlock_bh(&neigh->lock); } if (err >= 0) return neigh->ops->queue_xmit(skb); @@ -871,9 +970,9 @@ int neigh_connected_output(struct sk_buff *skb) __skb_pull(skb, skb->nh.raw - skb->data); - start_bh_atomic(); + read_lock_bh(&neigh->lock); err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); - end_bh_atomic(); + read_unlock_bh(&neigh->lock); if (err >= 0) return neigh->ops->queue_xmit(skb); kfree_skb(skb); @@ -947,8 +1046,10 @@ struct neigh_parms *neigh_parms_alloc(struct device *dev, struct neigh_table *tb return NULL; } } + write_lock_bh(&tbl->lock); p->next = tbl->parms.next; tbl->parms.next = p; + write_unlock_bh(&tbl->lock); } return p; } @@ -959,10 +1060,11 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) if (parms == NULL || parms == &tbl->parms) return; + write_lock_bh(&tbl->lock); for (p = &tbl->parms.next; *p; p = &(*p)->next) { if (*p == parms) { *p = parms->next; - synchronize_bh(); + write_unlock_bh(&tbl->lock); #ifdef CONFIG_SYSCTL neigh_sysctl_unregister(parms); #endif @@ -970,6 +1072,7 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) return; } } + write_unlock_bh(&tbl->lock); NEIGH_PRINTK1("neigh_release_parms: not found\n"); } @@ -981,6 +1084,7 @@ void neigh_table_init(struct neigh_table *tbl) tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time); init_timer(&tbl->gc_timer); + tbl->lock = RW_LOCK_UNLOCKED; tbl->gc_timer.data = (unsigned long)tbl; tbl->gc_timer.function = neigh_periodic_timer; tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time; @@ -993,29 +1097,30 @@ void neigh_table_init(struct neigh_table *tbl) tbl->last_flush = now; tbl->last_rand = now + tbl->parms.reachable_time*20; + write_lock(&neigh_tbl_lock); tbl->next = neigh_tables; neigh_tables = tbl; + write_unlock(&neigh_tbl_lock); } int neigh_table_clear(struct neigh_table *tbl) { struct neigh_table **tp; - start_bh_atomic(); del_timer(&tbl->gc_timer); del_timer(&tbl->proxy_timer); skb_queue_purge(&tbl->proxy_queue); neigh_ifdown(tbl, NULL); - end_bh_atomic(); if (tbl->entries) printk(KERN_CRIT "neighbour leakage\n"); + write_lock(&neigh_tbl_lock); for (tp = &neigh_tables; *tp; tp = &(*tp)->next) { if (*tp == tbl) { *tp = tbl->next; - synchronize_bh(); break; } } + write_unlock(&neigh_tbl_lock); #ifdef CONFIG_SYSCTL neigh_sysctl_unregister(&tbl->parms); #endif @@ -1037,12 +1142,14 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) return -ENODEV; } + read_lock(&neigh_tbl_lock); for (tbl=neigh_tables; tbl; tbl = tbl->next) { int err = 0; struct neighbour *n; if (tbl->family != ndm->ndm_family) continue; + read_unlock(&neigh_tbl_lock); if (nda[NDA_DST-1] == NULL || nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len)) @@ -1054,15 +1161,14 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if (dev == NULL) return -EINVAL; - start_bh_atomic(); - n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0); + n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev); if (n) { err = neigh_update(n, NULL, NUD_FAILED, 1, 0); neigh_release(n); } - end_bh_atomic(); return err; } + read_unlock(&neigh_tbl_lock); return -EADDRNOTAVAIL; } @@ -1079,12 +1185,15 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) return -ENODEV; } + read_lock(&neigh_tbl_lock); for (tbl=neigh_tables; tbl; tbl = tbl->next) { int err = 0; struct neighbour *n; if (tbl->family != ndm->ndm_family) continue; + read_unlock(&neigh_tbl_lock); + if (nda[NDA_DST-1] == NULL || nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len)) return -EINVAL; @@ -1098,8 +1207,7 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if (nda[NDA_LLADDR-1] != NULL && nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len)) return -EINVAL; - start_bh_atomic(); - n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0); + n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev); if (n) { if (nlh->nlmsg_flags&NLM_F_EXCL) err = -EEXIST; @@ -1117,9 +1225,9 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) } if (n) neigh_release(n); - end_bh_atomic(); return err; } + read_unlock(&neigh_tbl_lock); return -EADDRNOTAVAIL; } @@ -1139,15 +1247,17 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n, ndm->ndm_family = n->ops->family; ndm->ndm_flags = n->flags; ndm->ndm_type = n->type; - ndm->ndm_state = n->nud_state; ndm->ndm_ifindex = n->dev->ifindex; RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key); + read_lock_bh(&n->lock); + ndm->ndm_state = n->nud_state; if (n->nud_state&NUD_VALID) RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha); ci.ndm_used = now - n->used; ci.ndm_confirmed = now - n->confirmed; ci.ndm_updated = now - n->updated; ci.ndm_refcnt = atomic_read(&n->refcnt); + read_unlock_bh(&n->lock); RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci); nlh->nlmsg_len = skb->tail - b; return skb->len; @@ -1171,20 +1281,20 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct if (h < s_h) continue; if (h > s_h) s_idx = 0; - start_bh_atomic(); + read_lock_bh(&tbl->lock); for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next, idx++) { if (idx < s_idx) continue; if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) { - end_bh_atomic(); + read_unlock_bh(&tbl->lock); cb->args[1] = h; cb->args[2] = idx; return -1; } } - end_bh_atomic(); + read_unlock_bh(&tbl->lock); } cb->args[1] = h; @@ -1201,6 +1311,7 @@ int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; + read_lock(&neigh_tbl_lock); for (tbl=neigh_tables, t=0; tbl; tbl = tbl->next, t++) { if (t < s_t) continue; if (family && tbl->family != family) @@ -1210,6 +1321,7 @@ int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) if (neigh_dump_table(tbl, skb, cb) < 0) break; } + read_unlock(&neigh_tbl_lock); cb->args[0] = t; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index ed27c8e1d..dad9ee252 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -50,22 +50,22 @@ #include <net/sock.h> #include <net/pkt_sched.h> -atomic_t rtnl_rlockct; -struct wait_queue *rtnl_wait; +DECLARE_MUTEX(rtnl_sem); - -void rtnl_lock() +void rtnl_lock(void) { rtnl_shlock(); rtnl_exlock(); } - -void rtnl_unlock() + +void rtnl_unlock(void) { rtnl_exunlock(); rtnl_shunlock(); } + + int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len) { memset(tb, 0, sizeof(struct rtattr*)*maxattr); @@ -82,8 +82,6 @@ int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len) #ifdef CONFIG_RTNETLINK struct sock *rtnl; -unsigned long rtnl_wlockct; - struct rtnetlink_link * rtnetlink_links[NPROTO]; #define _S 1 /* superuser privileges required */ @@ -189,12 +187,14 @@ int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) int s_idx = cb->args[0]; struct device *dev; + read_lock(&dev_base_lock); for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { if (idx < s_idx) continue; if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0) break; } + read_unlock(&dev_base_lock); cb->args[0] = idx; return skb->len; @@ -216,9 +216,7 @@ int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb) continue; if (idx > s_idx) memset(&cb->args[0], 0, sizeof(cb->args)); - if (rtnetlink_links[idx][type].dumpit(skb, cb) == 0) - continue; - if (skb_tailroom(skb) < 256) + if (rtnetlink_links[idx][type].dumpit(skb, cb)) break; } cb->family = idx; @@ -245,8 +243,6 @@ void rtmsg_ifinfo(int type, struct device *dev) static int rtnetlink_done(struct netlink_callback *cb) { - if (cap_raised(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC) - rtnl_shunlock(); return 0; } @@ -314,15 +310,9 @@ rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) if (link->dumpit == NULL) goto err_inval; - /* Super-user locks all the tables to get atomic snapshot */ - if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) - && nlh->nlmsg_flags&NLM_F_ATOMIC) - atomic_inc(&rtnl_rlockct); if ((*errp = netlink_dump_start(rtnl, skb, nlh, link->dumpit, rtnetlink_done)) != 0) { - if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC) - atomic_dec(&rtnl_rlockct); return -1; } rlen = NLMSG_ALIGN(nlh->nlmsg_len); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b76364371..5ea21d7b4 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4,7 +4,7 @@ * Authors: Alan Cox <iiitac@pyr.swan.ac.uk> * Florian La Roche <rzsfl@rz.uni-sb.de> * - * Version: $Id: skbuff.c,v 1.55 1999/02/23 08:12:27 davem Exp $ + * Version: $Id: skbuff.c,v 1.56 1999/05/29 23:20:42 davem Exp $ * * Fixes: * Alan Cox : Fixed the worst of the load balancer bugs. @@ -62,11 +62,6 @@ #include <asm/system.h> /* - * Skb list spinlock - */ -spinlock_t skb_queue_lock = SPIN_LOCK_UNLOCKED; - -/* * Resource tracking variables */ diff --git a/net/core/sock.c b/net/core/sock.c index e0eb41a01..c38e92e93 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -7,7 +7,7 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.80 1999/05/08 03:04:34 davem Exp $ + * Version: $Id: sock.c,v 1.82 1999/05/27 00:37:03 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -487,10 +487,10 @@ struct sock *sk_alloc(int family, int priority, int zero_it) { struct sock *sk = kmem_cache_alloc(sk_cachep, priority); - if(sk) { - if (zero_it) - memset(sk, 0, sizeof(struct sock)); + if(sk && zero_it) { + memset(sk, 0, sizeof(struct sock)); sk->family = family; + sock_lock_init(sk); } return sk; @@ -650,7 +650,7 @@ unsigned long sock_rspace(struct sock *sk) */ static void sock_wait_for_wmem(struct sock * sk) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); sk->socket->flags &= ~SO_NOSPACE; add_wait_queue(sk->sleep, &wait); @@ -736,24 +736,44 @@ failure: return NULL; } - -void __release_sock(struct sock *sk) +void lock_sock(struct sock *sk) { -#ifdef CONFIG_INET - if (!sk->prot || !sk->backlog_rcv) - return; - - /* See if we have any packets built up. */ - start_bh_atomic(); - while (!skb_queue_empty(&sk->back_log)) { - struct sk_buff * skb = sk->back_log.next; - __skb_unlink(skb, &sk->back_log); - sk->backlog_rcv(sk, skb); + spin_lock_bh(&sk->lock.slock); + if(sk->lock.users != 0) { + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue_exclusive(&sk->lock.wq, &wait); + for(;;) { + current->state = TASK_EXCLUSIVE | TASK_UNINTERRUPTIBLE; + spin_unlock_bh(&sk->lock.slock); + schedule(); + spin_lock_bh(&sk->lock.slock); + if(!sk->lock.users) + break; + } + current->state = TASK_RUNNING; + remove_wait_queue(&sk->lock.wq, &wait); } - end_bh_atomic(); -#endif + sk->lock.users = 1; + spin_unlock_bh(&sk->lock.slock); } +void release_sock(struct sock *sk) +{ + spin_lock_bh(&sk->lock.slock); + sk->lock.users = 0; + if(sk->backlog.tail != NULL) { + struct sk_buff *skb = sk->backlog.head; + do { struct sk_buff *next = skb->next; + skb->next = NULL; + sk->backlog_rcv(sk, skb); + skb = next; + } while(skb != NULL); + sk->backlog.head = sk->backlog.tail = NULL; + } + wake_up(&sk->lock.wq); + spin_unlock_bh(&sk->lock.slock); +} /* * Generic socket manager library. Most simpler socket families @@ -1019,7 +1039,6 @@ void sock_init_data(struct socket *sock, struct sock *sk) { skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); - skb_queue_head_init(&sk->back_log); skb_queue_head_init(&sk->error_queue); init_timer(&sk->timer); @@ -1036,7 +1055,8 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->type = sock->type; sk->sleep = &sock->wait; sock->sk = sk; - } + } else + sk->sleep = NULL; sk->state_change = sock_def_wakeup; sk->data_ready = sock_def_readable; diff --git a/net/decnet/README b/net/decnet/README index 316435d29..9384fa322 100644 --- a/net/decnet/README +++ b/net/decnet/README @@ -1,15 +1,8 @@ Linux DECnet Project ====================== -For information on the Linux DECnet Project and the latest progress, -look at the project home page: - -http://www.sucs.swan.ac.uk/~rohan/DECnet/ - -To contribute either mail <SteveW@ACM.org> or post on one of the Linux -mailing lists (either linux-net or netdev). DECnet for Linux will not -be distributed as part of the 2.2.xx kernel series. It is available as a -patch from the above site. Expect DECnet to arrive as part of the standard -kernel distribution early in the 2.3.xx series. +The documentation for this kernel subsystem is available in the +Documentation/networking subdirctory of this distribution and also +on line at http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html. Steve Whitehouse <SteveW@ACM.org> diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index bce35d484..128c2a5e9 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -63,10 +63,12 @@ __initfunc(void eth_setup(char *str, int *ints)) { - struct device *d = dev_base; + struct device *d; if (!str || !*str) return; + + d = dev_base; while (d) { if (!strcmp(str,d->name)) @@ -246,6 +248,7 @@ int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh) eth->h_proto = type; memcpy(eth->h_source, dev->dev_addr, dev->addr_len); memcpy(eth->h_dest, neigh->ha, dev->addr_len); + hh->hh_len = ETH_HLEN; return 0; } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 70fcf4024..ca0f27d0c 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.87 1999/04/22 10:07:33 davem Exp $ + * Version: $Id: af_inet.c,v 1.91 1999/06/09 08:28:55 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -147,22 +147,17 @@ static __inline__ void kill_sk_queues(struct sock *sk) struct sk_buff *skb; /* First the read buffer. */ - while((skb = skb_dequeue(&sk->receive_queue)) != NULL) { - /* This will take care of closing sockets that were - * listening and didn't accept everything. - */ - if (skb->sk != NULL && skb->sk != sk) - skb->sk->prot->close(skb->sk, 0); + while((skb = skb_dequeue(&sk->receive_queue)) != NULL) kfree_skb(skb); - } /* Next, the error queue. */ while((skb = skb_dequeue(&sk->error_queue)) != NULL) kfree_skb(skb); - /* Now the backlog. */ - while((skb=skb_dequeue(&sk->back_log)) != NULL) - kfree_skb(skb); + /* It is _impossible_ for the backlog to contain anything + * when we get here. All user references to this socket + * have gone away, only the net layer knows can touch it. + */ } static __inline__ void kill_sk_now(struct sock *sk) @@ -195,14 +190,19 @@ static __inline__ void kill_sk_later(struct sock *sk) sk->destroy = 1; sk->ack_backlog = 0; - release_sock(sk); + bh_unlock_sock(sk); net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME); } +/* Callers must hold the BH spinlock. + * + * At this point, there should be no process reference to this + * socket, and thus no user references at all. Therefore we + * can assume the socket waitqueue is inactive and nobody will + * try to jump onto it. + */ void destroy_sock(struct sock *sk) { - lock_sock(sk); /* just to be safe. */ - /* Now we can no longer get new packets or once the * timers are killed, send them. */ @@ -213,12 +213,6 @@ void destroy_sock(struct sock *sk) kill_sk_queues(sk); - /* Now if it has a half accepted/ closed socket. */ - if (sk->pair) { - sk->pair->prot->close(sk->pair, 0); - sk->pair = NULL; - } - /* Now if everything is gone we can free the socket * structure, otherwise we need to keep it around until * everything is gone. @@ -284,6 +278,14 @@ static int inet_autobind(struct sock *sk) return 0; } +/* Listening INET sockets never sleep to wait for memory, so + * it is completely silly to wake them up on queue space + * available events. So we hook them up to this dummy callback. + */ +static void inet_listen_write_space(struct sock *sk) +{ +} + /* * Move a socket into listening state. */ @@ -310,6 +312,7 @@ int inet_listen(struct socket *sock, int backlog) dst_release(xchg(&sk->dst_cache, NULL)); sk->prot->rehash(sk); add_to_prot_sklist(sk); + sk->write_space = inet_listen_write_space; } sk->socket->flags |= SO_ACCEPTCON; return(0); @@ -368,7 +371,7 @@ static int inet_create(struct socket *sock, int protocol) if (protocol && protocol != IPPROTO_UDP) goto free_and_noproto; protocol = IPPROTO_UDP; - sk->no_check = UDP_NO_CHECK; + sk->no_check = UDP_CSUM_DEFAULT; sk->ip_pmtudisc = IP_PMTUDISC_DONT; prot=&udp_prot; sock->ops = &inet_dgram_ops; @@ -578,7 +581,7 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr, static void inet_wait_for_connect(struct sock *sk) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); add_wait_queue(sk->sleep, &wait); current->state = TASK_INTERRUPTIBLE; @@ -684,14 +687,8 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags) if (sk1->prot->accept == NULL) goto do_err; - /* Restore the state if we have been interrupted, and then returned. */ - if (sk1->pair != NULL) { - sk2 = sk1->pair; - sk1->pair = NULL; - } else { - if((sk2 = sk1->prot->accept(sk1,flags)) == NULL) - goto do_sk1_err; - } + if((sk2 = sk1->prot->accept(sk1,flags)) == NULL) + goto do_sk1_err; /* * We've been passed an extra socket. diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 2c311f233..a3ca88701 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1,6 +1,6 @@ /* linux/net/inet/arp.c * - * Version: $Id: arp.c,v 1.77 1999/03/21 05:22:30 davem Exp $ + * Version: $Id: arp.c,v 1.78 1999/06/09 10:10:36 davem Exp $ * * Copyright (C) 1994 by Florian La Roche * @@ -119,6 +119,11 @@ #include <asm/system.h> #include <asm/uaccess.h> +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +static char *ax2asc2(ax25_address *a, char *buf); +#endif + + /* * Interface to generic neighbour cache. */ @@ -304,7 +309,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) u8 *dst_ha = NULL; struct device *dev = neigh->dev; u32 target = *(u32*)neigh->primary_key; - int probes = neigh->probes; + int probes = atomic_read(&neigh->probes); if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) saddr = skb->nh.iph->saddr; @@ -315,6 +320,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) if (!(neigh->nud_state&NUD_VALID)) printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n"); dst_ha = neigh->ha; + read_lock_bh(&neigh->lock); } else if ((probes -= neigh->parms->app_probes) < 0) { #ifdef CONFIG_ARPD neigh_app_ns(neigh); @@ -324,6 +330,8 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, dst_ha, dev->dev_addr, NULL); + if (dst_ha) + read_unlock_bh(&neigh->lock); } /* OBSOLETE FUNCTIONS */ @@ -372,29 +380,25 @@ int arp_find(unsigned char *haddr, struct sk_buff *skb) if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev)) return 0; - start_bh_atomic(); n = __neigh_lookup(&arp_tbl, &paddr, dev, 1); if (n) { n->used = jiffies; if (n->nud_state&NUD_VALID || neigh_event_send(n, skb) == 0) { - memcpy(haddr, n->ha, dev->addr_len); + read_lock_bh(&n->lock); + memcpy(haddr, n->ha, dev->addr_len); + read_unlock_bh(&n->lock); neigh_release(n); - end_bh_atomic(); return 0; } + neigh_release(n); } else kfree_skb(skb); - neigh_release(n); - end_bh_atomic(); return 1; } /* END OF OBSOLETE FUNCTIONS */ -/* - * Note: requires bh_atomic locking. - */ int arp_bind_neighbour(struct dst_entry *dst) { struct device *dev = dst->dev; @@ -672,7 +676,8 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) (addr_type == RTN_UNICAST && rt->u.dst.dev != dev && (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) { n = neigh_event_ns(&arp_tbl, sha, &sip, dev); - neigh_release(n); + if (n) + neigh_release(n); if (skb->stamp.tv_sec == 0 || skb->pkt_type == PACKET_HOST || @@ -785,7 +790,6 @@ int arp_req_set(struct arpreq *r, struct device * dev) return -EINVAL; err = -ENOBUFS; - start_bh_atomic(); neigh = __neigh_lookup(&arp_tbl, &ip, dev, 1); if (neigh) { unsigned state = NUD_STALE; @@ -795,7 +799,6 @@ int arp_req_set(struct arpreq *r, struct device * dev) r->arp_ha.sa_data : NULL, state, 1, 0); neigh_release(neigh); } - end_bh_atomic(); return err; } @@ -819,17 +822,17 @@ static int arp_req_get(struct arpreq *r, struct device *dev) struct neighbour *neigh; int err = -ENXIO; - start_bh_atomic(); - neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0); + neigh = neigh_lookup(&arp_tbl, &ip, dev); if (neigh) { + read_lock_bh(&neigh->lock); memcpy(r->arp_ha.sa_data, neigh->ha, dev->addr_len); + r->arp_flags = arp_state_to_flags(neigh); + read_unlock_bh(&neigh->lock); r->arp_ha.sa_family = dev->type; strncpy(r->arp_dev, dev->name, sizeof(r->arp_dev)); - r->arp_flags = arp_state_to_flags(neigh); neigh_release(neigh); err = 0; } - end_bh_atomic(); return err; } @@ -867,14 +870,12 @@ int arp_req_delete(struct arpreq *r, struct device * dev) return -EINVAL; } err = -ENXIO; - start_bh_atomic(); - neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0); + neigh = neigh_lookup(&arp_tbl, &ip, dev); if (neigh) { if (neigh->nud_state&~NUD_NOARP) err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0); neigh_release(neigh); } - end_bh_atomic(); return err; } @@ -961,16 +962,16 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy char hbuffer[HBUFFERLEN]; int i,j,k; const char hexbuf[] = "0123456789ABCDEF"; + char abuf[16]; size = sprintf(buffer,"IP address HW type Flags HW address Mask Device\n"); pos+=size; len+=size; - neigh_table_lock(&arp_tbl); - - for(i=0; i<=NEIGH_HASHMASK; i++) { + for(i=0; i<=NEIGH_HASHMASK; i++) { struct neighbour *n; + read_lock_bh(&arp_tbl.lock); for (n=arp_tbl.hash_buckets[i]; n; n=n->next) { struct device *dev = n->dev; int hatype = dev->type; @@ -979,17 +980,14 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy if (!(n->nud_state&~NUD_NOARP)) continue; - /* I'd get great pleasure deleting - this ugly code. Let's output it in hexadecimal format. - "arp" utility will eventually repaired --ANK - */ -#if 1 /* UGLY CODE */ + read_lock(&n->lock); + /* * Convert hardware address to XX:XX:XX:XX ... form. */ #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM) - strcpy(hbuffer,ax2asc((ax25_address *)n->ha)); + ax2asc2((ax25_address *)n->ha, hbuffer); else { #endif for (k=0,j=0;k<HBUFFERLEN-3 && j<dev->addr_len;j++) { @@ -998,37 +996,33 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy hbuffer[k++]=':'; } hbuffer[--k]=0; - + #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) } #endif -#else - if ((neigh->nud_state&NUD_VALID) && dev->addr_len) { - int j; - for (j=0; j < dev->addr_len; j++) - sprintf(hbuffer+2*j, "%02x", neigh->ha[j]); - } else - sprintf(hbuffer, "0"); -#endif size = sprintf(buffer+len, "%-17s0x%-10x0x%-10x%s", - in_ntoa(*(u32*)n->primary_key), + in_ntoa2(*(u32*)n->primary_key, abuf), hatype, arp_state_to_flags(n), hbuffer); size += sprintf(buffer+len+size, " %-17s %s\n", "*", dev->name); + read_unlock(&n->lock); len += size; pos += size; if (pos <= offset) len=0; - if (pos >= offset+length) - goto done; + if (pos >= offset+length) { + read_unlock_bh(&arp_tbl.lock); + goto done; + } } + read_unlock_bh(&arp_tbl.lock); } for (i=0; i<=PNEIGH_HASHMASK; i++) { @@ -1039,7 +1033,7 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy size = sprintf(buffer+len, "%-17s0x%-10x0x%-10x%s", - in_ntoa(*(u32*)n->key), + in_ntoa2(*(u32*)n->key, abuf), hatype, ATF_PUBL|ATF_PERM, "00:00:00:00:00:00"); @@ -1058,7 +1052,6 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy } done: - neigh_table_unlock(&arp_tbl); *start = buffer+len-(pos-offset); /* Start of wanted data */ len = pos-offset; /* Start slop */ @@ -1117,14 +1110,13 @@ __initfunc(void arp_init (void)) } -#ifdef CONFIG_AX25_MODULE +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) /* * ax25 -> ASCII conversion */ -char *ax2asc(ax25_address *a) +char *ax2asc2(ax25_address *a, char *buf) { - static char buf[11]; char c, *s; int n; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index c8b0fbbc8..ff2c930d1 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1,7 +1,7 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.28 1999/05/08 20:00:16 davem Exp $ + * Version: $Id: devinet.c,v 1.32 1999/06/09 11:15:33 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -607,41 +607,39 @@ inet_gifconf(struct device *dev, char *buf, int len) { struct in_device *in_dev = dev->ip_ptr; struct in_ifaddr *ifa; - struct ifreq ifr; + struct ifreq *ifr = (struct ifreq *) buf; int done=0; if (in_dev==NULL || (ifa=in_dev->ifa_list)==NULL) return 0; for ( ; ifa; ifa = ifa->ifa_next) { - if (!buf) { + if (!ifr) { done += sizeof(ifr); continue; } if (len < (int) sizeof(ifr)) return done; - memset(&ifr, 0, sizeof(struct ifreq)); + memset(ifr, 0, sizeof(struct ifreq)); if (ifa->ifa_label) - strcpy(ifr.ifr_name, ifa->ifa_label); + strcpy(ifr->ifr_name, ifa->ifa_label); else - strcpy(ifr.ifr_name, dev->name); + strcpy(ifr->ifr_name, dev->name); - (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET; - (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = ifa->ifa_local; + (*(struct sockaddr_in *) &ifr->ifr_addr).sin_family = AF_INET; + (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr = ifa->ifa_local; - if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) - return -EFAULT; - buf += sizeof(struct ifreq); + ifr++; len -= sizeof(struct ifreq); done += sizeof(struct ifreq); } return done; } -u32 inet_select_addr(struct device *dev, u32 dst, int scope) +u32 inet_select_addr(const struct device *dev, u32 dst, int scope) { u32 addr = 0; - struct in_device *in_dev = dev->ip_ptr; + const struct in_device *in_dev = dev->ip_ptr; if (in_dev == NULL) return 0; @@ -661,15 +659,19 @@ u32 inet_select_addr(struct device *dev, u32 dst, int scope) in this case. It is importnat that lo is the first interface in dev_base list. */ + read_lock(&dev_base_lock); for (dev=dev_base; dev; dev=dev->next) { if ((in_dev=dev->ip_ptr) == NULL) continue; for_primary_ifa(in_dev) { - if (ifa->ifa_scope <= scope) + if (ifa->ifa_scope <= scope) { + read_unlock(&dev_base_lock); return ifa->ifa_local; + } } endfor_ifa(in_dev); } + read_unlock(&dev_base_lock); return 0; } @@ -790,6 +792,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; + read_lock(&dev_base_lock); for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { if (idx < s_idx) continue; @@ -807,6 +810,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) } } done: + read_unlock(&dev_base_lock); cb->args[0] = idx; cb->args[1] = ip_idx; @@ -881,11 +885,13 @@ void inet_forward_change() ipv4_devconf.accept_redirects = !on; ipv4_devconf_dflt.forwarding = on; + read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { struct in_device *in_dev = dev->ip_ptr; if (in_dev) in_dev->cnf.forwarding = on; } + read_unlock(&dev_base_lock); rt_cache_flush(0); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index a17470483..d57d4daa9 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: FIB frontend. * - * Version: $Id: fib_frontend.c,v 1.15 1999/03/21 05:22:31 davem Exp $ + * Version: $Id: fib_frontend.c,v 1.16 1999/06/09 10:10:42 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -123,13 +123,11 @@ fib_get_procinfo(char *buffer, char **start, off_t offset, int length, int dummy first = 0; } - /* rtnl_shlock(); -- it is pointless at the moment --ANK */ if (main_table && count > 0) { int n = main_table->tb_get_info(main_table, ptr, first, count); count -= n; ptr += n*128; } - /* rtnl_shunlock(); */ len = ptr - *start; if (len >= length) return length; diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index d9e029cef..0472f6118 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -5,7 +5,7 @@ * * IPv4 FIB: lookup engine and maintenance routines. * - * Version: $Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $ + * Version: $Id: fib_hash.c,v 1.10 1999/06/09 10:10:45 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -145,13 +145,16 @@ extern __inline__ int fn_key_leq(fn_key_t a, fn_key_t b) return a.datum <= b.datum; } +static rwlock_t fib_hash_lock = RW_LOCK_UNLOCKED; + #define FZ_MAX_DIVISOR 1024 #ifdef CONFIG_IP_ROUTE_LARGE_TABLES +/* The fib hash lock must be held when this is called. */ static __inline__ void fn_rebuild_zone(struct fn_zone *fz, - struct fib_node **old_ht, - int old_divisor) + struct fib_node **old_ht, + int old_divisor) { int i; struct fib_node *f, **fp, *next; @@ -198,13 +201,13 @@ static void fn_rehash_zone(struct fn_zone *fz) if (ht) { memset(ht, 0, new_divisor*sizeof(struct fib_node*)); - start_bh_atomic(); + write_lock_bh(&fib_hash_lock); old_ht = fz->fz_hash; fz->fz_hash = ht; fz->fz_hashmask = new_hashmask; fz->fz_divisor = new_divisor; fn_rebuild_zone(fz, old_ht, old_divisor); - end_bh_atomic(); + write_unlock_bh(&fib_hash_lock); kfree(old_ht); } } @@ -246,6 +249,7 @@ fn_new_zone(struct fn_hash *table, int z) for (i=z+1; i<=32; i++) if (table->fn_zones[i]) break; + write_lock_bh(&fib_hash_lock); if (i>32) { /* No more specific masks, we are the first. */ fz->fz_next = table->fn_zone_list; @@ -255,6 +259,7 @@ fn_new_zone(struct fn_hash *table, int z) table->fn_zones[i]->fz_next = fz; } table->fn_zones[z] = fz; + write_unlock_bh(&fib_hash_lock); return fz; } @@ -265,6 +270,7 @@ fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result struct fn_zone *fz; struct fn_hash *t = (struct fn_hash*)tb->tb_data; + read_lock(&fib_hash_lock); for (fz = t->fn_zone_list; fz; fz = fz->fz_next) { struct fib_node *f; fn_key_t k = fz_key(key->dst, fz); @@ -293,13 +299,16 @@ fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result res->scope = f->fn_scope; res->prefixlen = fz->fz_order; res->prefix = &fz_prefix(f->fn_key, fz); - return 0; + goto out; } if (err < 0) - return err; + goto out; } } - return 1; + err = 1; +out: + read_unlock(&fib_hash_lock); + return err; } static int fn_hash_last_dflt=-1; @@ -344,6 +353,7 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi last_resort = NULL; order = -1; + read_lock(&fib_hash_lock); for (f = fz->fz_hash[0]; f; f = f->fn_next) { struct fib_info *next_fi = FIB_INFO(f); @@ -364,7 +374,7 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi } else if (!fib_detect_death(fi, order, &last_resort, &last_idx)) { res->fi = fi; fn_hash_last_dflt = order; - return; + goto out; } fi = next_fi; order++; @@ -372,18 +382,20 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi if (order<=0 || fi==NULL) { fn_hash_last_dflt = -1; - return; + goto out; } if (!fib_detect_death(fi, order, &last_resort, &last_idx)) { res->fi = fi; fn_hash_last_dflt = order; - return; + goto out; } if (last_idx >= 0) res->fi = last_resort; fn_hash_last_dflt = last_idx; +out: + read_unlock(&fib_hash_lock); } #define FIB_SCAN(f, fp) \ @@ -457,6 +469,7 @@ rta->rta_prefsrc ? *(u32*)rta->rta_prefsrc : 0); fp = fz_chain_p(key, fz); + /* * Scan list to find the first route with the same destination */ @@ -560,14 +573,17 @@ replace: */ new_f->fn_next = f; + write_lock_bh(&fib_hash_lock); *fp = new_f; + write_unlock_bh(&fib_hash_lock); fz->fz_nent++; if (del_fp) { f = *del_fp; /* Unlink replaced node */ + write_lock_bh(&fib_hash_lock); *del_fp = f->fn_next; - synchronize_bh(); + write_unlock_bh(&fib_hash_lock); if (!(f->fn_state&FN_S_ZOMBIE)) rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); @@ -619,11 +635,13 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? fp = fz_chain_p(key, fz); + FIB_SCAN(f, fp) { if (fn_key_eq(f->fn_key, key)) break; - if (fn_key_leq(key, f->fn_key)) + if (fn_key_leq(key, f->fn_key)) { return -ESRCH; + } } #ifdef CONFIG_IP_ROUTE_TOS FIB_SCAN_KEY(f, fp, key) { @@ -637,9 +655,9 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? FIB_SCAN_TOS(f, fp, key, tos) { struct fib_info * fi = FIB_INFO(f); - if (f->fn_state&FN_S_ZOMBIE) + if (f->fn_state&FN_S_ZOMBIE) { return -ESRCH; - + } matched++; if (del_fp == NULL && @@ -655,8 +673,9 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); if (matched != 1) { + write_lock_bh(&fib_hash_lock); *del_fp = f->fn_next; - synchronize_bh(); + write_unlock_bh(&fib_hash_lock); if (f->fn_state&FN_S_ACCESSED) rt_cache_flush(-1); @@ -687,8 +706,9 @@ fn_flush_list(struct fib_node ** fp, int z, struct fn_hash *table) struct fib_info *fi = FIB_INFO(f); if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) { + write_lock_bh(&fib_hash_lock); *fp = f->fn_next; - synchronize_bh(); + write_unlock_bh(&fib_hash_lock); fn_free_node(f); found++; @@ -727,6 +747,7 @@ static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int c int pos = 0; int n = 0; + read_lock(&fib_hash_lock); for (fz=table->fn_zone_list; fz; fz = fz->fz_next) { int i; struct fib_node *f; @@ -752,10 +773,12 @@ static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int c FZ_MASK(fz), buffer); buffer += 128; if (++n >= count) - return n; + goto out; } } } +out: + read_unlock(&fib_hash_lock); return n; } #endif @@ -818,15 +841,18 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin struct fn_hash *table = (struct fn_hash*)tb->tb_data; s_m = cb->args[1]; + read_lock(&fib_hash_lock); for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) { if (m < s_m) continue; if (m > s_m) memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0])); if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) { cb->args[1] = m; + read_unlock(&fib_hash_lock); return -1; } } + read_unlock(&fib_hash_lock); cb->args[1] = m; return skb->len; } diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 868c44c31..97074198e 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: policy rules. * - * Version: $Id: fib_rules.c,v 1.9 1999/03/25 10:04:23 davem Exp $ + * Version: $Id: fib_rules.c,v 1.11 1999/06/09 10:10:47 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -79,12 +79,14 @@ static struct fib_rule main_rule = { &default_rule, 0x7FFE, RT_TABLE_MAIN, RTN_U static struct fib_rule local_rule = { &main_rule, 0, RT_TABLE_LOCAL, RTN_UNICAST, }; static struct fib_rule *fib_rules = &local_rule; +static rwlock_t fib_rules_lock = RW_LOCK_UNLOCKED; int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) { struct rtattr **rta = arg; struct rtmsg *rtm = NLMSG_DATA(nlh); struct fib_rule *r, **rp; + int err = -ESRCH; for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) { if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) && @@ -99,18 +101,20 @@ int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) && (!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) && (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) { + err = -EPERM; if (r == &local_rule) - return -EPERM; + break; + write_lock_bh(&fib_rules_lock); *rp = r->r_next; - synchronize_bh(); - + write_unlock_bh(&fib_rules_lock); if (r != &default_rule && r != &main_rule) kfree(r); - return 0; + err = 0; + break; } } - return -ESRCH; + return err; } /* Allocate new unique table id */ @@ -205,7 +209,9 @@ int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) } new_r->r_next = r; + write_lock_bh(&fib_rules_lock); *rp = new_r; + write_unlock_bh(&fib_rules_lock); return 0; } @@ -250,8 +256,11 @@ static void fib_rules_detach(struct device *dev) struct fib_rule *r; for (r=fib_rules; r; r=r->r_next) { - if (r->r_ifindex == dev->ifindex) + if (r->r_ifindex == dev->ifindex) { + write_lock_bh(&fib_rules_lock); r->r_ifindex = -1; + write_unlock_bh(&fib_rules_lock); + } } } @@ -260,8 +269,11 @@ static void fib_rules_attach(struct device *dev) struct fib_rule *r; for (r=fib_rules; r; r=r->r_next) { - if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) + if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) { + write_lock_bh(&fib_rules_lock); r->r_ifindex = dev->ifindex; + write_unlock_bh(&fib_rules_lock); + } } } @@ -275,6 +287,7 @@ int fib_lookup(const struct rt_key *key, struct fib_result *res) u32 saddr = key->src; FRprintk("Lookup: %08x <- %08x ", key->dst, key->src); + read_lock(&fib_rules_lock); for (r = fib_rules; r; r=r->r_next) { if (((saddr^r->r_src) & r->r_srcmask) || ((daddr^r->r_dst) & r->r_dstmask) || @@ -294,11 +307,14 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action); policy = r; break; case RTN_UNREACHABLE: + read_unlock(&fib_rules_lock); return -ENETUNREACH; default: case RTN_BLACKHOLE: + read_unlock(&fib_rules_lock); return -EINVAL; case RTN_PROHIBIT: + read_unlock(&fib_rules_lock); return -EACCES; } @@ -308,12 +324,16 @@ FRprintk("tb %d r %d ", r->r_table, r->r_action); if (err == 0) { FRprintk("ok\n"); res->r = policy; + read_unlock(&fib_rules_lock); return 0; } - if (err < 0 && err != -EAGAIN) + if (err < 0 && err != -EAGAIN) { + read_unlock(&fib_rules_lock); return err; + } } FRprintk("FAILURE\n"); + read_unlock(&fib_rules_lock); return -ENETUNREACH; } @@ -400,12 +420,14 @@ int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) int s_idx = cb->args[0]; struct fib_rule *r; + read_lock(&fib_rules_lock); for (r=fib_rules, idx=0; r; r = r->r_next, idx++) { if (idx < s_idx) continue; if (inet_fill_rule(skb, r, cb) < 0) break; } + read_unlock(&fib_rules_lock); cb->args[0] = idx; return skb->len; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 199550ffb..9456c7f29 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -1,9 +1,9 @@ /* * NET3: Implementation of the ICMP protocol layer. * - * Alan Cox, <alan@cymru.net> + * Alan Cox, <alan@redhat.com> * - * Version: $Id: icmp.c,v 1.52 1999/03/21 12:04:11 davem Exp $ + * Version: $Id: icmp.c,v 1.57 1999/06/09 10:10:50 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -699,8 +699,8 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len) case ICMP_FRAG_NEEDED: if (ipv4_config.no_pmtu_disc) { if (net_ratelimit()) - printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n", - in_ntoa(iph->daddr)); + printk(KERN_INFO "ICMP: %d.%d.%d.%d: fragmentation needed and DF set.\n", + NIPQUAD(iph->daddr)); } else { unsigned short new_mtu; new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu)); @@ -711,7 +711,7 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len) break; case ICMP_SR_FAILED: if (net_ratelimit()) - printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr)); + printk(KERN_INFO "ICMP: %d.%d.%d.%d: Source Route Failed.\n", NIPQUAD(iph->daddr)); break; default: break; @@ -741,8 +741,8 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len) if (inet_addr_type(iph->daddr) == RTN_BROADCAST) { if (net_ratelimit()) - printk(KERN_WARNING "%s sent an invalid ICMP error to a broadcast.\n", - in_ntoa(skb->nh.iph->saddr)); + printk(KERN_WARNING "%d.%d.%d.%d sent an invalid ICMP error to a broadcast.\n", + NIPQUAD(skb->nh.iph->saddr)); return; } } @@ -1142,6 +1142,8 @@ __initfunc(void icmp_init(struct net_proto_family *ops)) icmp_inode.i_sock = 1; icmp_inode.i_uid = 0; icmp_inode.i_gid = 0; + init_waitqueue_head(&icmp_inode.i_wait); + init_waitqueue_head(&icmp_inode.u.socket_i.wait); icmp_socket->inode = &icmp_inode; icmp_socket->state = SS_UNCONNECTED; @@ -1150,6 +1152,11 @@ __initfunc(void icmp_init(struct net_proto_family *ops)) if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0) panic("Failed to create the ICMP control socket.\n"); icmp_socket->sk->allocation=GFP_ATOMIC; - icmp_socket->sk->num = 256; /* Don't receive any data */ icmp_socket->sk->ip_ttl = MAXTTL; + + /* Unhash it so that IP input processing does not even + * see it, we do not wish this socket to see incoming + * packets. + */ + icmp_socket->sk->prot->unhash(icmp_socket->sk); } diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 68e52633e..61c530418 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -8,7 +8,7 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.30 1999/03/25 10:04:10 davem Exp $ + * Version: $Id: igmp.c,v 1.32 1999/06/09 10:10:53 davem Exp $ * * Authors: * Alan Cox <Alan.Cox@linux.org> @@ -97,6 +97,15 @@ #include <linux/mroute.h> #endif +/* Big mc list lock for all the devices */ +static rwlock_t ip_mc_lock = RW_LOCK_UNLOCKED; +/* Big mc list semaphore for all the sockets. + We do not refer to this list in IP data paths or from BH, + so that semaphore is OK. + */ +DECLARE_MUTEX(ip_sk_mc_sem); + + #define IP_MAX_MEMBERSHIPS 20 #ifdef CONFIG_IP_MULTICAST @@ -216,6 +225,8 @@ static void igmp_timer_expire(unsigned long data) struct in_device *in_dev = im->interface; int err; + read_lock(&ip_mc_lock); + im->tm_running=0; if (IGMP_V1_SEEN(in_dev)) @@ -234,6 +245,7 @@ static void igmp_timer_expire(unsigned long data) igmp_start_timer(im, IGMP_Unsolicited_Report_Interval); } im->reporter = 1; + read_unlock(&ip_mc_lock); } static void igmp_heard_report(struct in_device *in_dev, u32 group) @@ -245,14 +257,16 @@ static void igmp_heard_report(struct in_device *in_dev, u32 group) if (LOCAL_MCAST(group)) return; + read_lock(&ip_mc_lock); for (im=in_dev->mc_list; im!=NULL; im=im->next) { if (im->multiaddr == group) { igmp_stop_timer(im); im->reporter = 0; im->unsolicit_count = 0; - return; + break; } } + read_unlock(&ip_mc_lock); } static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time, @@ -281,6 +295,7 @@ static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_ti * - Use the igmp->igmp_code field as the maximum * delay possible */ + read_lock(&ip_mc_lock); for (im=in_dev->mc_list; im!=NULL; im=im->next) { if (group && group != im->multiaddr) continue; @@ -291,6 +306,7 @@ static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_ti igmp_stop_timer(im); igmp_start_timer(im, max_delay); } + read_unlock(&ip_mc_lock); } int igmp_rcv(struct sk_buff *skb, unsigned short len) @@ -380,9 +396,7 @@ static void igmp_group_dropped(struct ip_mc_list *im) if (LOCAL_MCAST(im->multiaddr)) return; - start_bh_atomic(); igmp_stop_timer(im); - end_bh_atomic(); if (im->reporter && !IGMP_V1_SEEN(im->interface)) igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE); @@ -400,9 +414,7 @@ static void igmp_group_added(struct ip_mc_list *im) if (LOCAL_MCAST(im->multiaddr)) return; - start_bh_atomic(); igmp_start_timer(im, IGMP_Initial_Report_Delay); - end_bh_atomic(); #endif } @@ -422,16 +434,17 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr) im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL); + write_lock_bh(&ip_mc_lock); for (i=in_dev->mc_list; i; i=i->next) { if (i->multiaddr == addr) { i->users++; if (im) kfree(im); - return; + goto out; } } if (!im) - return; + goto out; im->users=1; im->interface=in_dev; im->multiaddr=addr; @@ -447,9 +460,13 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr) im->next=in_dev->mc_list; in_dev->mc_list=im; igmp_group_added(im); + write_unlock_bh(&ip_mc_lock); if (in_dev->dev->flags & IFF_UP) ip_rt_multicast_event(in_dev); return; +out: + write_unlock_bh(&ip_mc_lock); + return; } /* @@ -458,22 +475,27 @@ void ip_mc_inc_group(struct in_device *in_dev, u32 addr) int ip_mc_dec_group(struct in_device *in_dev, u32 addr) { + int err = -ESRCH; struct ip_mc_list *i, **ip; + write_lock_bh(&ip_mc_lock); for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { if (i->multiaddr==addr) { if (--i->users == 0) { *ip = i->next; - synchronize_bh(); - igmp_group_dropped(i); + + write_unlock_bh(&ip_mc_lock); if (in_dev->dev->flags & IFF_UP) ip_rt_multicast_event(in_dev); kfree_s(i, sizeof(*i)); + return 0; } - return 0; + err = 0; + break; } } + write_unlock_bh(&ip_mc_lock); return -ESRCH; } @@ -483,8 +505,10 @@ void ip_mc_down(struct in_device *in_dev) { struct ip_mc_list *i; + read_lock_bh(&ip_mc_lock); for (i=in_dev->mc_list; i; i=i->next) igmp_group_dropped(i); + read_unlock_bh(&ip_mc_lock); ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS); } @@ -497,8 +521,10 @@ void ip_mc_up(struct in_device *in_dev) ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS); + read_lock_bh(&ip_mc_lock); for (i=in_dev->mc_list; i; i=i->next) igmp_group_added(i); + read_unlock_bh(&ip_mc_lock); } /* @@ -509,11 +535,13 @@ void ip_mc_destroy_dev(struct in_device *in_dev) { struct ip_mc_list *i; + write_lock_bh(&ip_mc_lock); while ((i = in_dev->mc_list) != NULL) { in_dev->mc_list = i->next; igmp_group_dropped(i); kfree_s(i, sizeof(*i)); } + write_unlock_bh(&ip_mc_lock); } static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr) @@ -570,6 +598,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); err = -EADDRINUSE; + down(&ip_sk_mc_sem); for (i=sk->ip_mc_list; i; i=i->next) { if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) { /* New style additions are reference counted */ @@ -577,13 +606,13 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) i->count++; err = 0; } - goto done; + goto done_unlock; } count++; } err = -ENOBUFS; if (iml == NULL || count >= sysctl_igmp_max_memberships) - goto done; + goto done_unlock; memcpy(&iml->multi, imr, sizeof(*imr)); iml->next = sk->ip_mc_list; iml->count = 1; @@ -591,6 +620,9 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) ip_mc_inc_group(in_dev, addr); iml = NULL; err = 0; + +done_unlock: + up(&ip_sk_mc_sem); done: rtnl_shunlock(); if (iml) @@ -606,6 +638,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { struct ip_mc_socklist *iml, **imlp; + down(&ip_sk_mc_sem); for (imlp=&sk->ip_mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) { if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr && iml->multi.imr_address.s_addr==imr->imr_address.s_addr && @@ -615,7 +648,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) return 0; *imlp = iml->next; - synchronize_bh(); + up(&ip_sk_mc_sem); in_dev = inetdev_by_index(iml->multi.imr_ifindex); if (in_dev) @@ -624,6 +657,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) return 0; } } + up(&ip_sk_mc_sem); return -EADDRNOTAVAIL; } @@ -635,13 +669,37 @@ void ip_mc_drop_socket(struct sock *sk) { struct ip_mc_socklist *iml; + down(&ip_sk_mc_sem); while ((iml=sk->ip_mc_list) != NULL) { struct in_device *in_dev; sk->ip_mc_list = iml->next; + up(&ip_sk_mc_sem); + if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); sock_kfree_s(sk, iml, sizeof(*iml)); + + down(&ip_sk_mc_sem); } + up(&ip_sk_mc_sem); +} + +int ip_check_mc(struct device *dev, u32 mc_addr) +{ + struct in_device *in_dev = dev->ip_ptr; + struct ip_mc_list *im; + + if (in_dev) { + read_lock(&ip_mc_lock); + for (im=in_dev->mc_list; im; im=im->next) { + if (im->multiaddr == mc_addr) { + read_unlock(&ip_mc_lock); + return 1; + } + } + read_unlock(&ip_mc_lock); + } + return 0; } @@ -653,11 +711,11 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dum struct ip_mc_list *im; int len=0; struct device *dev; - + len=sprintf(buffer,"Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n"); - - for(dev = dev_base; dev; dev = dev->next) - { + + read_lock(&dev_base_lock); + for(dev = dev_base; dev; dev = dev->next) { struct in_device *in_dev = dev->ip_ptr; char *querier = "NONE"; @@ -669,6 +727,7 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dum len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n", dev->ifindex, dev->name, dev->mc_count, querier); + read_lock(&ip_mc_lock); for (im = in_dev->mc_list; im; im = im->next) { len+=sprintf(buffer+len, "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n", @@ -681,11 +740,16 @@ int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dum len=0; begin=pos; } - if(pos>offset+length) + if(pos>offset+length) { + read_unlock(&ip_mc_lock); goto done; + } } + read_unlock(&ip_mc_lock); } done: + read_unlock(&dev_base_lock); + *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index f066e6073..29747fee6 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -5,7 +5,7 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.40 1999/03/20 23:58:34 davem Exp $ + * Version: $Id: ip_fragment.c,v 1.41 1999/05/27 00:38:07 davem Exp $ * * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> * Alan Cox <Alan.Cox@linux.org> @@ -71,7 +71,8 @@ struct ipq { #define IPQ_HASHSZ 64 -struct ipq *ipq_hash[IPQ_HASHSZ]; +static struct ipq *ipq_hash[IPQ_HASHSZ]; +static spinlock_t ipfrag_lock = SPIN_LOCK_UNLOCKED; #define ipqhashfn(id, saddr, daddr, prot) \ ((((id) >> 1) ^ (saddr) ^ (daddr) ^ (prot)) & (IPQ_HASHSZ - 1)) @@ -141,7 +142,9 @@ static inline struct ipq *ip_find(struct iphdr *iph, struct dst_entry *dst) unsigned int hash = ipqhashfn(id, saddr, daddr, protocol); struct ipq *qp; - /* Always, we are in a BH context, so no locking. -DaveM */ + /* We are always in BH context, and protected by the + * ipfrag lock. + */ for(qp = ipq_hash[hash]; qp; qp = qp->next) { if(qp->iph->id == id && qp->iph->saddr == saddr && @@ -158,8 +161,9 @@ static inline struct ipq *ip_find(struct iphdr *iph, struct dst_entry *dst) * because we completed, reassembled and processed it, or because * it timed out. * - * This is called _only_ from BH contexts, on packet reception - * processing and from frag queue expiration timers. -DaveM + * This is called _only_ from BH contexts with the ipfrag lock held, + * on packet reception processing and from frag queue expiration + * timers. -DaveM */ static void ip_free(struct ipq *qp) { @@ -197,6 +201,7 @@ static void ip_expire(unsigned long arg) { struct ipq *qp = (struct ipq *) arg; + spin_lock(&ipfrag_lock); if(!qp->fragments) { #ifdef IP_EXPIRE_DEBUG @@ -213,10 +218,13 @@ static void ip_expire(unsigned long arg) out: /* Nuke the fragment queue. */ ip_free(qp); + spin_lock(&ipfrag_lock); } /* Memory limiting on fragments. Evictor trashes the oldest * fragment queue until we are back under the low threshold. + * + * We are always called in BH with the ipfrag lock held. */ static void ip_evictor(void) { @@ -229,9 +237,6 @@ restart: struct ipq *qp; if (atomic_read(&ip_frag_mem) <= sysctl_ipfrag_low_thresh) return; - /* We are in a BH context, so these queue - * accesses are safe. -DaveM - */ qp = ipq_hash[i]; if (qp) { /* find the oldest queue for this hash bucket */ @@ -283,7 +288,7 @@ static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph) /* Add this entry to the queue. */ hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol); - /* We are in a BH context, no locking necessary. -DaveM */ + /* In a BH context and ipfrag lock is held. -DaveM */ if((qp->next = ipq_hash[hash]) != NULL) qp->next->pprev = &qp->next; ipq_hash[hash] = qp; @@ -421,6 +426,8 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) ip_statistics.IpReasmReqds++; + spin_lock(&ipfrag_lock); + /* Start by cleaning up the memory. */ if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh) ip_evictor(); @@ -565,6 +572,7 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) out_freequeue: ip_free(qp); out_skb: + spin_unlock(&ipfrag_lock); return skb; } @@ -574,6 +582,7 @@ out_skb: out_timer: mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time); /* ~ 30 seconds */ out: + spin_unlock(&ipfrag_lock); return NULL; /* diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 7a3e2618b..107ccaa16 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) module. * - * Version: $Id: ip_input.c,v 1.37 1999/04/22 10:38:36 davem Exp $ + * Version: $Id: ip_input.c,v 1.40 1999/06/09 10:10:55 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -154,44 +154,11 @@ struct ip_mib ip_statistics={2,IPDEFTTL,}; /* Forwarding=No, Default TTL=64 */ - -/* - * Handle the issuing of an ioctl() request - * for the ip device. This is scheduled to - * disappear - */ - -int ip_ioctl(struct sock *sk, int cmd, unsigned long arg) -{ - switch(cmd) - { - default: - return(-EINVAL); - } -} - - #if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG) #define CONFIG_IP_ALWAYS_DEFRAG 1 #endif /* - * 0 - deliver - * 1 - block - */ -static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb) -{ - int type; - - type = skb->h.icmph->type; - if (type < 32) - return test_bit(type, &sk->tp_pinfo.tp_raw4.filter); - - /* Do not block unknown ICMP types */ - return 0; -} - -/* * Process Router Attention IP option */ int ip_call_ra_chain(struct sk_buff *skb) @@ -224,16 +191,37 @@ int ip_call_ra_chain(struct sk_buff *skb) return 0; } +/* Handle this out of line, it is rare. */ +static int ip_run_ipprot(struct sk_buff *skb, struct iphdr *iph, + struct inet_protocol *ipprot, int force_copy) +{ + int ret = 0; + + do { + if (ipprot->protocol == iph->protocol) { + struct sk_buff *skb2 = skb; + if (ipprot->copy || force_copy) + skb2 = skb_clone(skb, GFP_ATOMIC); + if(skb2 != NULL) { + ret = 1; + ipprot->handler(skb2, + ntohs(iph->tot_len) - (iph->ihl * 4)); + } + } + ipprot = (struct inet_protocol *) ipprot->next; + } while(ipprot != NULL); + + return ret; +} + +extern struct sock *raw_v4_input(struct sk_buff *, struct iphdr *, int); + /* * Deliver IP Packets to the higher protocol layers. */ int ip_local_deliver(struct sk_buff *skb) { struct iphdr *iph = skb->nh.iph; - struct inet_protocol *ipprot; - struct sock *raw_sk=NULL; - unsigned char hash; - int flag = 0; #ifndef CONFIG_IP_ALWAYS_DEFRAG /* @@ -249,34 +237,29 @@ int ip_local_deliver(struct sk_buff *skb) #endif #ifdef CONFIG_IP_MASQUERADE - /* - * Do we need to de-masquerade this packet? - */ - { - int ret; - /* - * Some masq modules can re-inject packets if - * bad configured. + /* Do we need to de-masquerade this packet? */ + if((IPCB(skb)->flags&IPSKB_MASQUERADED)) { + /* Some masq modules can re-inject packets if + * bad configured. */ + printk(KERN_DEBUG "ip_input(): demasq recursion detected. " + "Check masq modules configuration\n"); + kfree_skb(skb); + return 0; + } else { + int ret = ip_fw_demasquerade(&skb); - if((IPCB(skb)->flags&IPSKB_MASQUERADED)) { - printk(KERN_DEBUG "ip_input(): demasq recursion detected. Check masq modules configuration\n"); - kfree_skb(skb); - return 0; - } - - ret = ip_fw_demasquerade(&skb); if (ret < 0) { kfree_skb(skb); return 0; } - if (ret) { - iph=skb->nh.iph; + iph = skb->nh.iph; IPCB(skb)->flags |= IPSKB_MASQUERADED; dst_release(skb->dst); skb->dst = NULL; - if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) { + if (ip_route_input(skb, iph->daddr, iph->saddr, + iph->tos, skb->dev)) { kfree_skb(skb); return 0; } @@ -285,112 +268,50 @@ int ip_local_deliver(struct sk_buff *skb) } #endif - /* - * Point into the IP datagram, just past the header. - */ - + /* Point into the IP datagram, just past the header. */ skb->h.raw = skb->nh.raw + iph->ihl*4; - /* - * Deliver to raw sockets. This is fun as to avoid copies we want to make no - * surplus copies. - * - * RFC 1122: SHOULD pass TOS value up to the transport layer. - * -> It does. And not only TOS, but all IP header. - */ - - /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ - hash = iph->protocol & (MAX_INET_PROTOS - 1); - - /* - * If there maybe a raw socket we must check - if not we don't care less - */ - - if((raw_sk = raw_v4_htable[hash]) != NULL) { - struct sock *sknext = NULL; - struct sk_buff *skb1; - raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr, skb->dev->ifindex); - if(raw_sk) { /* Any raw sockets */ - do { - /* Find the next */ - sknext = raw_v4_lookup(raw_sk->next, iph->protocol, - iph->saddr, iph->daddr, skb->dev->ifindex); - if (iph->protocol != IPPROTO_ICMP || !icmp_filter(raw_sk, skb)) { - if (sknext == NULL) - break; - skb1 = skb_clone(skb, GFP_ATOMIC); - if(skb1) - { - raw_rcv(raw_sk, skb1); - } - } - raw_sk = sknext; - } while(raw_sk!=NULL); - - /* Here either raw_sk is the last raw socket, or NULL if - * none. We deliver to the last raw socket AFTER the - * protocol checks as it avoids a surplus copy. - */ - } - } - - /* - * skb->h.raw now points at the protocol beyond the IP header. - */ - - for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next) { - struct sk_buff *skb2; - - if (ipprot->protocol != iph->protocol) - continue; - /* - * See if we need to make a copy of it. This will - * only be set if more than one protocol wants it. - * and then not for the last one. If there is a pending - * raw delivery wait for that + /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ + int hash = iph->protocol & (MAX_INET_PROTOS - 1); + struct sock *raw_sk = raw_v4_htable[hash]; + struct inet_protocol *ipprot; + int flag; + + /* If there maybe a raw socket we must check - if not we + * don't care less */ - - if (ipprot->copy || raw_sk) - { - skb2 = skb_clone(skb, GFP_ATOMIC); - if(skb2==NULL) - continue; - } - else - { - skb2 = skb; - } - flag = 1; + if(raw_sk != NULL) + raw_sk = raw_v4_input(skb, iph, hash); + + ipprot = (struct inet_protocol *) inet_protos[hash]; + flag = 0; + if(ipprot != NULL) { + if(raw_sk == NULL && + ipprot->next == NULL && + ipprot->protocol == iph->protocol) { + /* Fast path... */ + return ipprot->handler(skb, (ntohs(iph->tot_len) - + (iph->ihl * 4))); + } else { + flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL)); + } + } - /* - * Pass on the datagram to each protocol that wants it, - * based on the datagram protocol. We should really - * check the protocol handler's return values here... + /* All protocols checked. + * If this packet was a broadcast, we may *not* reply to it, since that + * causes (proven, grin) ARP storms and a leakage of memory (i.e. all + * ICMP reply messages get queued up for transmission...) */ - - ipprot->handler(skb2, ntohs(iph->tot_len) - (iph->ihl * 4)); - } - - /* - * All protocols checked. - * If this packet was a broadcast, we may *not* reply to it, since that - * causes (proven, grin) ARP storms and a leakage of memory (i.e. all - * ICMP reply messages get queued up for transmission...) - */ - - if(raw_sk!=NULL) /* Shift to last raw user */ - { - raw_rcv(raw_sk, skb); - - } - else if (!flag) /* Free and report errors */ - { - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); - kfree_skb(skb); + if(raw_sk != NULL) { /* Shift to last raw user */ + raw_rcv(raw_sk, skb); + } else if (!flag) { /* Free and report errors */ + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); + kfree_skb(skb); + } } - return(0); + return 0; } /* @@ -404,9 +325,8 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) u16 rport; #endif /* CONFIG_FIREWALL */ - /* - * When the interface is in promisc. mode, drop all the crap - * that it receives, do not try to analyse it. + /* When the interface is in promisc. mode, drop all the crap + * that it receives, do not try to analyse it. */ if (skb->pkt_type == PACKET_OTHERHOST) goto drop; @@ -430,17 +350,15 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) goto inhdr_error; { - __u32 len = ntohs(iph->tot_len); - if (skb->len < len) - goto inhdr_error; + __u32 len = ntohs(iph->tot_len); + if (skb->len < len) + goto inhdr_error; - /* - * Our transport medium may have padded the buffer out. Now we know it - * is IP we can trim to the true length of the frame. - * Note this now means skb->len holds ntohs(iph->tot_len). - */ - - __skb_trim(skb, len); + /* Our transport medium may have padded the buffer out. Now we know it + * is IP we can trim to the true length of the frame. + * Note this now means skb->len holds ntohs(iph->tot_len). + */ + __skb_trim(skb, len); } #ifdef CONFIG_IP_ALWAYS_DEFRAG @@ -474,21 +392,17 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) if (skb->dst == NULL) { if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) goto drop; -#ifdef CONFIG_CPU_IS_SLOW - if (net_cpu_congestion > 10 && !(iph->tos&IPTOS_RELIABILITY) && - IPTOS_PREC(iph->tos) < IPTOS_PREC_INTERNETCONTROL) { - goto drop; - } -#endif } #ifdef CONFIG_NET_CLS_ROUTE if (skb->dst->tclassid) { u32 idx = skb->dst->tclassid; + write_lock(&ip_rt_acct_lock); ip_rt_acct[idx&0xFF].o_packets++; ip_rt_acct[idx&0xFF].o_bytes+=skb->len; ip_rt_acct[(idx>>16)&0xFF].i_packets++; ip_rt_acct[(idx>>16)&0xFF].i_bytes+=skb->len; + write_unlock(&ip_rt_acct_lock); } #endif diff --git a/net/ipv4/ip_masq_mfw.c b/net/ipv4/ip_masq_mfw.c index dc38b1712..ff07231fc 100644 --- a/net/ipv4/ip_masq_mfw.c +++ b/net/ipv4/ip_masq_mfw.c @@ -3,7 +3,7 @@ * * Does (reverse-masq) forwarding based on skb->fwmark value * - * $Id: ip_masq_mfw.c,v 1.3 1999/01/26 05:33:47 davem Exp $ + * $Id: ip_masq_mfw.c,v 1.4 1999/05/13 23:25:07 davem Exp $ * * Author: Juan Jose Ciarlante <jjciarla@raiz.uncu.edu.ar> * based on Steven Clarke's portfw @@ -79,7 +79,7 @@ struct ip_masq_mfw { }; -static struct semaphore mfw_sema = MUTEX; +static DECLARE_MUTEX(mfw_sema); #ifdef __SMP__ static rwlock_t mfw_lock = RW_LOCK_UNLOCKED; #endif diff --git a/net/ipv4/ip_masq_quake.c b/net/ipv4/ip_masq_quake.c index 165dd6bd5..17b11a799 100644 --- a/net/ipv4/ip_masq_quake.c +++ b/net/ipv4/ip_masq_quake.c @@ -12,6 +12,7 @@ * http://www.gamers.org/dEngine/quake/spec/ * Harald Hoyer : Check for QUAKE-STRING * Juan Jose Ciarlante : litl bits for 2.1 + * Horst von Brand : Add #include <linux/string.h> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,6 +25,7 @@ #include <linux/module.h> #include <asm/system.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/in.h> @@ -44,7 +46,7 @@ typedef struct struct quake_priv_data { /* Have we seen a client connect message */ - char cl_connect; + signed char cl_connect; }; static int diff --git a/net/ipv4/ip_masq_vdolive.c b/net/ipv4/ip_masq_vdolive.c index 4724e3b93..2d8d672cc 100644 --- a/net/ipv4/ip_masq_vdolive.c +++ b/net/ipv4/ip_masq_vdolive.c @@ -2,7 +2,7 @@ * IP_MASQ_VDOLIVE - VDO Live masquerading module * * - * Version: @(#)$Id: ip_masq_vdolive.c,v 1.4 1998/10/06 04:49:07 davem Exp $ + * Version: @(#)$Id: ip_masq_vdolive.c,v 1.6 1999/06/09 08:29:03 davem Exp $ * * Author: Nigel Metheringham <Nigel.Metheringham@ThePLAnet.net> * PLAnet Online Ltd @@ -10,6 +10,9 @@ * Fixes: Minor changes for 2.1 by * Steven Clarke <Steven.Clarke@ThePlanet.Net>, Planet Online Ltd * + * Add missing #include <linux/string.h> + * Horst von Brand <vonbrand@sleipnir.valparaiso.cl> + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -25,6 +28,7 @@ #include <linux/config.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/kernel.h> #include <asm/system.h> #include <linux/skbuff.h> diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index fae22cbe7..359926a4c 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -5,7 +5,7 @@ * * The options processing module for ip.c * - * Version: $Id: ip_options.c,v 1.16 1999/03/21 05:22:40 davem Exp $ + * Version: $Id: ip_options.c,v 1.18 1999/06/09 08:29:06 davem Exp $ * * Authors: A.N.Kuznetsov * @@ -452,7 +452,6 @@ eol: error: if (skb) { icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24)); - kfree_skb(skb); } return -EINVAL; } diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index abe93ec27..51e27ad67 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1,5 +1,5 @@ /* - * $Id: ipconfig.c,v 1.20 1999/03/28 10:18:28 davem Exp $ + * $Id: ipconfig.c,v 1.22 1999/06/09 10:10:57 davem Exp $ * * Automatic Configuration of IP -- use BOOTP or RARP or user-supplied * information to configure own IP address and routes. @@ -112,7 +112,8 @@ static int __init ic_open_devs(void) unsigned short oflags; last = &ic_first_dev; - for (dev = dev_base; dev; dev = dev->next) + read_lock(&dev_base_lock); + for (dev = dev_base; dev; dev = dev->next) { if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) : (!(dev->flags & IFF_LOOPBACK) && (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) && @@ -142,6 +143,9 @@ static int __init ic_open_devs(void) ic_proto_have_if |= able; DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able)); } + } + read_unlock(&dev_base_lock); + *last = NULL; if (!ic_first_dev) { diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index d7db0c007..1034e0e7a 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1,7 +1,7 @@ /* * IP multicast routing support for mrouted 3.6/3.8 * - * (c) 1995 Alan Cox, <alan@cymru.net> + * (c) 1995 Alan Cox, <alan@redhat.com> * Linux Consultancy and Custom Driver Development * * This program is free software; you can redistribute it and/or @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: ipmr.c,v 1.40 1999/03/25 10:04:25 davem Exp $ + * Version: $Id: ipmr.c,v 1.43 1999/06/09 10:10:59 davem Exp $ * * Fixes: * Michael Chastain : Incorrect size of copying. @@ -23,6 +23,8 @@ * Brad Parker : Better behaviour on mrouted upcall * overflow. * Carlos Picoto : PIMv1 Support + * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header + * Relax this requrement to work with older peers. * */ @@ -431,7 +433,7 @@ static void ipmr_cache_resolve(struct mfc_cache *cache) skb_trim(skb, nlh->nlmsg_len); ((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -EMSGSIZE; } - err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).pid, MSG_DONTWAIT); + err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT); } else #endif ip_mr_forward(skb, cache, 0); @@ -1343,7 +1345,8 @@ int pim_rcv(struct sk_buff * skb, unsigned short len) pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) || (pim->flags&PIM_NULL_REGISTER) || reg_dev == NULL || - ip_compute_csum((void *)pim, len)) { + (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && + ip_compute_csum((void *)pim, len))) { kfree_skb(skb); return -EINVAL; } diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 1640a0560..52c5ee5a4 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -7,7 +7,7 @@ * PROC file system. It is mainly used for debugging and * statistics. * - * Version: $Id: proc.c,v 1.34 1999/02/08 11:20:34 davem Exp $ + * Version: $Id: proc.c,v 1.35 1999/05/27 00:37:38 davem Exp $ * * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de> @@ -114,10 +114,8 @@ static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format) slot_dist = tcp_tw_death_row_slot - slot_dist; timer_expires = jiffies + (slot_dist * TCP_TWKILL_PERIOD); } else { - timer_active1 = del_timer(&tp->retransmit_timer); - timer_active2 = del_timer(&sp->timer); - if (!timer_active1) tp->retransmit_timer.expires=0; - if (!timer_active2) sp->timer.expires=0; + timer_active1 = tp->retransmit_timer.prev != NULL; + timer_active2 = sp->timer.prev != NULL; timer_active = 0; timer_expires = (unsigned) -1; } @@ -147,9 +145,6 @@ static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format) (!tw_bucket && sp->socket) ? sp->socket->inode->i_uid : 0, (!tw_bucket && timer_active) ? sp->timeout : 0, (!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0); - - if (timer_active1) add_timer(&tp->retransmit_timer); - if (timer_active2) add_timer(&sp->timer); } /* @@ -176,7 +171,7 @@ get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t of " sl local_address rem_address st tx_queue " "rx_queue tr tm->when retrnsmt uid timeout inode"); pos = 128; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_READ(); sp = pro->sklist_next; while(sp != (struct sock *)pro) { if (format == 0 && sp->state == TCP_LISTEN) { @@ -211,7 +206,7 @@ get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t of i++; } out: - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); begin = len - (pos - offset); *start = buffer + begin; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index fc6b1f2ee..dd2e7555e 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -5,7 +5,7 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: $Id: raw.c,v 1.39 1998/11/08 11:17:04 davem Exp $ + * Version: $Id: raw.c,v 1.41 1999/05/30 01:16:19 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -75,11 +75,11 @@ static void raw_v4_hash(struct sock *sk) num &= (RAWV4_HTABLE_SIZE - 1); skp = &raw_v4_htable[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); sk->next = *skp; *skp = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void raw_v4_unhash(struct sock *sk) @@ -90,7 +90,7 @@ static void raw_v4_unhash(struct sock *sk) num &= (RAWV4_HTABLE_SIZE - 1); skp = &raw_v4_htable[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -98,7 +98,7 @@ static void raw_v4_unhash(struct sock *sk) } skp = &((*skp)->next); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void raw_v4_rehash(struct sock *sk) @@ -110,7 +110,7 @@ static void raw_v4_rehash(struct sock *sk) num &= (RAWV4_HTABLE_SIZE - 1); skp = &raw_v4_htable[oldnum]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -121,16 +121,15 @@ static void raw_v4_rehash(struct sock *sk) sk->next = raw_v4_htable[num]; raw_v4_htable[num] = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } -/* Grumble... icmp and ip_input want to get at this... */ -struct sock *raw_v4_lookup(struct sock *sk, unsigned short num, - unsigned long raddr, unsigned long laddr, int dif) +static __inline__ struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num, + unsigned long raddr, unsigned long laddr, + int dif) { struct sock *s = sk; - SOCKHASH_LOCK(); for(s = sk; s; s = s->next) { if((s->num == num) && !(s->dead && (s->state == TCP_CLOSE)) && @@ -139,10 +138,79 @@ struct sock *raw_v4_lookup(struct sock *sk, unsigned short num, !(s->bound_dev_if && s->bound_dev_if != dif)) break; /* gotcha */ } - SOCKHASH_UNLOCK(); return s; } +struct sock *raw_v4_lookup(struct sock *sk, unsigned short num, + unsigned long raddr, unsigned long laddr, + int dif) +{ + SOCKHASH_LOCK_READ(); + sk = __raw_v4_lookup(sk, num, raddr, laddr, dif); + SOCKHASH_UNLOCK_READ(); + + return sk; +} + +/* + * 0 - deliver + * 1 - block + */ +static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb) +{ + int type; + + type = skb->h.icmph->type; + if (type < 32) + return test_bit(type, &sk->tp_pinfo.tp_raw4.filter); + + /* Do not block unknown ICMP types */ + return 0; +} + +/* IP input processing comes here for RAW socket delivery. + * This is fun as to avoid copies we want to make no surplus + * copies. + * + * RFC 1122: SHOULD pass TOS value up to the transport layer. + * -> It does. And not only TOS, but all IP header. + */ +struct sock *raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash) +{ + struct sock *sk; + + SOCKHASH_LOCK_READ_BH(); + if ((sk = raw_v4_htable[hash]) == NULL) + goto out; + sk = __raw_v4_lookup(sk, iph->protocol, + iph->saddr, iph->daddr, + skb->dev->ifindex); + while(sk != NULL) { + struct sock *sknext = __raw_v4_lookup(sk->next, iph->protocol, + iph->saddr, iph->daddr, + skb->dev->ifindex); + + if (iph->protocol != IPPROTO_ICMP || + ! icmp_filter(sk, skb)) { + struct sk_buff *clone; + + if(sknext == NULL) + break; + clone = skb_clone(skb, GFP_ATOMIC); + if(clone) { + SOCKHASH_UNLOCK_READ_BH(); + raw_rcv(sk, clone); + SOCKHASH_LOCK_READ_BH(); + } + } + sk = sknext; + } +out: + SOCKHASH_UNLOCK_READ_BH(); + + return sk; +} + void raw_err (struct sock *sk, struct sk_buff *skb) { int type = skb->h.icmph->type; @@ -402,6 +470,8 @@ done: static void raw_close(struct sock *sk, long timeout) { + bh_lock_sock(sk); + /* Observation: when raw_close is called, processes have no access to socket anymore. But net still has. Step one, detach it from networking: diff --git a/net/ipv4/route.c b/net/ipv4/route.c index dbde97b70..3d9e87de3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,7 +5,7 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.67 1999/05/08 20:00:20 davem Exp $ + * Version: $Id: route.c,v 1.69 1999/06/09 10:11:02 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -174,7 +174,18 @@ __u8 ip_tos2prio[16] = { * Route cache. */ -struct rtable *rt_hash_table[RT_HASH_DIVISOR]; +/* The locking scheme is rather straight forward: + * + * 1) A BH protected rwlock protects the central route hash. + * 2) Only writers remove entries, and they hold the lock + * as they look at rtable reference counts. + * 3) Only readers acquire references to rtable entries, + * they do so with atomic increments and with the + * lock held. + */ + +static struct rtable *rt_hash_table[RT_HASH_DIVISOR]; +static rwlock_t rt_hash_lock = RW_LOCK_UNLOCKED; static int rt_intern_hash(unsigned hash, struct rtable * rth, struct rtable ** res); @@ -204,7 +215,7 @@ static int rt_cache_get_info(char *buffer, char **start, off_t offset, int lengt } - start_bh_atomic(); + read_lock_bh(&rt_hash_lock); for (i = 0; i<RT_HASH_DIVISOR; i++) { for (r = rt_hash_table[i]; r; r = r->u.rt_next) { @@ -239,7 +250,7 @@ static int rt_cache_get_info(char *buffer, char **start, off_t offset, int lengt } done: - end_bh_atomic(); + read_unlock_bh(&rt_hash_lock); *start = buffer+len-(pos-offset); len = pos-offset; @@ -292,6 +303,7 @@ static __inline__ int rt_may_expire(struct rtable *rth, int tmo1, int tmo2) return 1; } +/* This runs via a timer and thus is always in BH context. */ static void rt_check_expire(unsigned long dummy) { int i; @@ -305,6 +317,7 @@ static void rt_check_expire(unsigned long dummy) rover = (rover + 1) & (RT_HASH_DIVISOR-1); rthp = &rt_hash_table[rover]; + write_lock(&rt_hash_lock); while ((rth = *rthp) != NULL) { if (rth->u.dst.expires) { /* Entrie is expired even if it is in use */ @@ -325,6 +338,7 @@ static void rt_check_expire(unsigned long dummy) *rthp = rth->u.rt_next; rt_free(rth); } + write_unlock(&rt_hash_lock); /* Fallback loop breaker. */ if ((jiffies - now) > 0) @@ -334,6 +348,9 @@ static void rt_check_expire(unsigned long dummy) add_timer(&rt_periodic_timer); } +/* This can run from both BH and non-BH contexts, the latter + * in the case of a forced flush event. + */ static void rt_run_flush(unsigned long dummy) { int i; @@ -341,23 +358,23 @@ static void rt_run_flush(unsigned long dummy) rt_deadline = 0; - start_bh_atomic(); for (i=0; i<RT_HASH_DIVISOR; i++) { - if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL) - continue; - end_bh_atomic(); + write_lock_bh(&rt_hash_lock); + rth = rt_hash_table[i]; + if(rth != NULL) + rt_hash_table[i] = NULL; + write_unlock_bh(&rt_hash_lock); for (; rth; rth=next) { next = rth->u.rt_next; rth->u.rt_next = NULL; rt_free(rth); } - - start_bh_atomic(); } - end_bh_atomic(); } +static spinlock_t rt_flush_lock = SPIN_LOCK_UNLOCKED; + void rt_cache_flush(int delay) { unsigned long now = jiffies; @@ -366,7 +383,7 @@ void rt_cache_flush(int delay) if (delay < 0) delay = ip_rt_min_delay; - start_bh_atomic(); + spin_lock_bh(&rt_flush_lock); if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) { long tmo = (long)(rt_deadline - now); @@ -386,7 +403,7 @@ void rt_cache_flush(int delay) } if (delay <= 0) { - end_bh_atomic(); + spin_unlock_bh(&rt_flush_lock); rt_run_flush(0); return; } @@ -396,7 +413,7 @@ void rt_cache_flush(int delay) rt_flush_timer.expires = now + delay; add_timer(&rt_flush_timer); - end_bh_atomic(); + spin_unlock_bh(&rt_flush_lock); } /* @@ -459,7 +476,10 @@ static int rt_garbage_collect(void) do { int i, k; - start_bh_atomic(); + /* The write lock is held during the entire hash + * traversal to ensure consistent state of the rover. + */ + write_lock_bh(&rt_hash_lock); for (i=0, k=rover; i<RT_HASH_DIVISOR; i++) { unsigned tmo = expire; @@ -480,7 +500,7 @@ static int rt_garbage_collect(void) break; } rover = k; - end_bh_atomic(); + write_unlock_bh(&rt_hash_lock); if (goal <= 0) goto work_done; @@ -530,10 +550,9 @@ static int rt_intern_hash(unsigned hash, struct rtable * rt, struct rtable ** rp int attempts = !in_interrupt(); restart: - start_bh_atomic(); - rthp = &rt_hash_table[hash]; + write_lock_bh(&rt_hash_lock); while ((rth = *rthp) != NULL) { if (memcmp(&rth->key, &rt->key, sizeof(rt->key)) == 0) { /* Put it first */ @@ -544,7 +563,7 @@ restart: atomic_inc(&rth->u.dst.refcnt); atomic_inc(&rth->u.dst.use); rth->u.dst.lastuse = now; - end_bh_atomic(); + write_unlock_bh(&rt_hash_lock); rt_drop(rt); *rp = rth; @@ -559,7 +578,7 @@ restart: */ if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0) { if (!arp_bind_neighbour(&rt->u.dst)) { - end_bh_atomic(); + write_unlock_bh(&rt_hash_lock); /* Neighbour tables are full and nothing can be released. Try to shrink route cache, @@ -594,7 +613,7 @@ restart: } #endif rt_hash_table[hash] = rt; - end_bh_atomic(); + write_unlock_bh(&rt_hash_lock); *rp = rt; return 0; } @@ -633,6 +652,7 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, rthp=&rt_hash_table[hash]; + write_lock_bh(&rt_hash_lock); while ( (rth = *rthp) != NULL) { struct rtable *rt; @@ -657,6 +677,7 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops); if (rt == NULL) { ip_rt_put(rth); + write_unlock_bh(&rt_hash_lock); return; } @@ -688,11 +709,15 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, } *rthp = rth->u.rt_next; + write_unlock_bh(&rt_hash_lock); if (!rt_intern_hash(hash, rt, &rt)) ip_rt_put(rt); rt_drop(rth); - break; + goto do_next; } + write_unlock_bh(&rt_hash_lock); + do_next: + ; } } return; @@ -722,8 +747,8 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) #if RT_CACHE_DEBUG >= 1 printk(KERN_DEBUG "ip_rt_advice: redirect to %d.%d.%d.%d/%02x dropped\n", NIPQUAD(rt->rt_dst), rt->key.tos); #endif - start_bh_atomic(); ip_rt_put(rt); + write_lock_bh(&rt_hash_lock); for (rthp = &rt_hash_table[hash]; *rthp; rthp = &(*rthp)->u.rt_next) { if (*rthp == rt) { *rthp = rt->u.rt_next; @@ -731,7 +756,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) break; } } - end_bh_atomic(); + write_unlock_bh(&rt_hash_lock); return NULL; } } @@ -861,6 +886,7 @@ unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu) for (i=0; i<2; i++) { unsigned hash = rt_hash_code(daddr, skeys[i], tos); + read_lock_bh(&rt_hash_lock); for (rth = rt_hash_table[hash]; rth; rth = rth->u.rt_next) { if (rth->key.dst == daddr && rth->key.src == skeys[i] && @@ -890,6 +916,7 @@ unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu) } } } + read_unlock_bh(&rt_hash_lock); } return est_mtu ? : new_mtu; } @@ -1362,6 +1389,7 @@ int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, tos &= IPTOS_TOS_MASK; hash = rt_hash_code(daddr, saddr^(iif<<5), tos); + read_lock_bh(&rt_hash_lock); for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) { if (rth->key.dst == daddr && rth->key.src == saddr && @@ -1374,10 +1402,12 @@ int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, rth->u.dst.lastuse = jiffies; atomic_inc(&rth->u.dst.use); atomic_inc(&rth->u.dst.refcnt); + read_unlock_bh(&rt_hash_lock); skb->dst = (struct dst_entry*)rth; return 0; } } + read_unlock_bh(&rt_hash_lock); /* Multicast recognition logic is moved from route cache to here. The problem was that too many Ethernet cards have broken/missing @@ -1657,7 +1687,7 @@ int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif) hash = rt_hash_code(daddr, saddr^(oif<<5), tos); - start_bh_atomic(); + read_lock_bh(&rt_hash_lock); for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) { if (rth->key.dst == daddr && rth->key.src == saddr && @@ -1673,12 +1703,12 @@ int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif) rth->u.dst.lastuse = jiffies; atomic_inc(&rth->u.dst.use); atomic_inc(&rth->u.dst.refcnt); - end_bh_atomic(); + read_unlock_bh(&rt_hash_lock); *rp = rth; return 0; } } - end_bh_atomic(); + read_unlock_bh(&rt_hash_lock); return ip_route_output_slow(rp, daddr, saddr, tos, oif); } @@ -1821,9 +1851,7 @@ int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) return -ENODEV; skb->protocol = __constant_htons(ETH_P_IP); skb->dev = dev; - start_bh_atomic(); err = ip_route_input(skb, dst, src, rtm->rtm_tos, dev); - end_bh_atomic(); rt = (struct rtable*)skb->dst; if (!err && rt->u.dst.error) err = -rt->u.dst.error; @@ -1869,7 +1897,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) if (h < s_h) continue; if (h > s_h) s_idx = 0; - start_bh_atomic(); + read_lock_bh(&rt_hash_lock); for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) { if (idx < s_idx) continue; @@ -1877,12 +1905,12 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) { dst_release(xchg(&skb->dst, NULL)); - end_bh_atomic(); + read_unlock_bh(&rt_hash_lock); goto done; } dst_release(xchg(&skb->dst, NULL)); } - end_bh_atomic(); + read_unlock_bh(&rt_hash_lock); } done: @@ -1968,6 +1996,7 @@ ctl_table ipv4_route_table[] = { #ifdef CONFIG_NET_CLS_ROUTE struct ip_rt_acct ip_rt_acct[256]; +rwlock_t ip_rt_acct_lock = RW_LOCK_UNLOCKED; #ifdef CONFIG_PROC_FS static int ip_rt_acct_read(char *buffer, char **start, off_t offset, @@ -1980,9 +2009,9 @@ static int ip_rt_acct_read(char *buffer, char **start, off_t offset, *eof = 1; } if (length > 0) { - start_bh_atomic(); + read_lock_bh(&ip_rt_acct_lock); memcpy(buffer, ((u8*)&ip_rt_acct)+offset, length); - end_bh_atomic(); + read_unlock_bh(&ip_rt_acct_lock); return length; } return 0; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8c1c9f9be..779c31cef 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.140 1999/04/22 10:34:31 davem Exp $ + * Version: $Id: tcp.c,v 1.144 1999/05/27 01:03:37 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -416,6 +416,7 @@ #include <linux/fcntl.h> #include <linux/poll.h> #include <linux/init.h> +#include <linux/smp_lock.h> #include <net/icmp.h> #include <net/tcp.h> @@ -432,7 +433,7 @@ kmem_cache_t *tcp_timewait_cachep; /* * Find someone to 'accept'. Must be called with - * the socket locked or with interrupts disabled + * the listening socket locked. */ static struct open_request *tcp_find_established(struct tcp_opt *tp, @@ -441,10 +442,11 @@ static struct open_request *tcp_find_established(struct tcp_opt *tp, struct open_request *req = tp->syn_wait_queue; struct open_request *prev = (struct open_request *)&tp->syn_wait_queue; while(req) { - if (req->sk && - ((1 << req->sk->state) & - ~(TCPF_SYN_SENT|TCPF_SYN_RECV))) - break; + if (req->sk) { + if((1 << req->sk->state) & + ~(TCPF_SYN_SENT|TCPF_SYN_RECV)) + break; + } prev = req; req = req->dl_next; } @@ -655,12 +657,13 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) /* * Wait for a socket to get into the connected state * - * Note: must be called with the socket locked. + * Note: Must be called with the socket locked, and it + * runs with the kernel fully unlocked. */ static int wait_for_tcp_connect(struct sock * sk, int flags) { struct task_struct *tsk = current; - struct wait_queue wait = { tsk, NULL }; + DECLARE_WAITQUEUE(wait, tsk); while((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) { if(sk->err) @@ -698,12 +701,14 @@ static inline int tcp_memory_free(struct sock *sk) /* * Wait for more memory for a socket + * + * NOTE: This runs with the kernel fully unlocked. */ static void wait_for_tcp_memory(struct sock * sk) { release_sock(sk); if (!tcp_memory_free(sk)) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); sk->socket->flags &= ~SO_NOSPACE; add_wait_queue(sk->sleep, &wait); @@ -744,6 +749,7 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg) int mss_now; int err, copied; + unlock_kernel(); lock_sock(sk); err = 0; @@ -896,6 +902,7 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg) err = -ERESTARTSYS; goto do_interrupted; } + tcp_push_pending_frames(sk, tp); wait_for_tcp_memory(sk); /* If SACK's were formed or PMTU events happened, @@ -969,6 +976,7 @@ do_fault2: out: tcp_push_pending_frames(sk, tp); release_sock(sk); + lock_kernel(); return err; } @@ -1117,7 +1125,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, int len, int nonblock, int flags, int *addr_len) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); int copied = 0; u32 peek_seq; volatile u32 *seq; /* So gcc doesn't overoptimise */ @@ -1148,6 +1156,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, if (flags & MSG_WAITALL) target=len; + unlock_kernel(); add_wait_queue(sk->sleep, &wait); lock_sock(sk); @@ -1300,6 +1309,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, /* We now will not sleep again until we are finished * with skb. Sorry if you are doing the SMP port * but you'll just have to fix it neatly ;) + * + * Very funny Alan... -DaveM */ atomic_dec(&skb->users); @@ -1344,6 +1355,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, /* Clean up data we have read: This will do ACK frames. */ cleanup_rbuf(sk, copied); release_sock(sk); + lock_kernel(); return copied; } @@ -1415,16 +1427,15 @@ void tcp_shutdown(struct sock *sk, int how) return; /* If we've already sent a FIN, or it's a closed state, skip this. */ + lock_sock(sk); if ((1 << sk->state) & (TCPF_ESTABLISHED|TCPF_SYN_SENT|TCPF_SYN_RECV|TCPF_CLOSE_WAIT)) { - lock_sock(sk); /* Clear out any half completed packets. FIN if needed. */ if (tcp_close_state(sk,0)) tcp_send_fin(sk); - - release_sock(sk); } + release_sock(sk); } @@ -1471,13 +1482,6 @@ void tcp_close(struct sock *sk, long timeout) struct sk_buff *skb; int data_was_unread = 0; - /* - * Check whether the socket is locked ... supposedly - * it's impossible to tcp_close() a locked socket. - */ - if (atomic_read(&sk->sock_readers)) - printk("tcp_close: socket already locked!\n"); - /* We need to grab some memory, and put together a FIN, * and then put it into the queue to be sent. */ @@ -1491,6 +1495,8 @@ void tcp_close(struct sock *sk, long timeout) return; } + unlock_kernel(); + /* It is questionable, what the role of this is now. * In any event either it should be removed, or * increment of SLT_KEEPALIVE be done, this is causing @@ -1534,24 +1540,23 @@ void tcp_close(struct sock *sk, long timeout) if (timeout) { struct task_struct *tsk = current; - struct wait_queue wait = { tsk, NULL }; + DECLARE_WAITQUEUE(wait, current); add_wait_queue(sk->sleep, &wait); - release_sock(sk); while (1) { tsk->state = TASK_INTERRUPTIBLE; if (!closing(sk)) break; + release_sock(sk); timeout = schedule_timeout(timeout); + lock_sock(sk); if (signal_pending(tsk) || !timeout) break; } tsk->state = TASK_RUNNING; remove_wait_queue(sk->sleep, &wait); - - lock_sock(sk); } /* Now that the socket is dead, if we are in the FIN_WAIT2 state @@ -1559,23 +1564,40 @@ void tcp_close(struct sock *sk, long timeout) */ tcp_check_fin_timer(sk); - release_sock(sk); sk->dead = 1; + + release_sock(sk); + lock_kernel(); } /* * Wait for an incoming connection, avoid race - * conditions. This must be called with the socket locked. + * conditions. This must be called with the socket locked, + * and without the kernel lock held. */ static struct open_request * wait_for_connect(struct sock * sk, struct open_request **pprev) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); struct open_request *req; - add_wait_queue(sk->sleep, &wait); + /* + * True wake-one mechanism for incoming connections: only + * one process gets woken up, not the 'whole herd'. + * Since we do not 'race & poll' for established sockets + * anymore, the common case will execute the loop only once. + * + * Subtle issue: "add_wait_queue_exclusive()" will be added + * after any current non-exclusive waiters, and we know that + * it will always _stay_ after any new non-exclusive waiters + * because all non-exclusive waiters are added at the + * beginning of the wait-queue. As such, it's ok to "drop" + * our exclusiveness temporarily when we get woken up without + * having to remove and re-insert us on the wait queue. + */ + add_wait_queue_exclusive(sk->sleep, &wait); for (;;) { - current->state = TASK_INTERRUPTIBLE; + current->state = TASK_EXCLUSIVE | TASK_INTERRUPTIBLE; release_sock(sk); schedule(); lock_sock(sk); @@ -1603,6 +1625,7 @@ struct sock *tcp_accept(struct sock *sk, int flags) struct sock *newsk = NULL; int error; + unlock_kernel(); lock_sock(sk); /* We need to make sure that this socket is listening, @@ -1633,16 +1656,17 @@ struct sock *tcp_accept(struct sock *sk, int flags) sk->ack_backlog--; if(sk->keepopen) tcp_inc_slow_timer(TCP_SLT_KEEPALIVE); - release_sock(sk); + lock_kernel(); return newsk; out: /* sk should be in LISTEN state, thus accept can use sk->err for - * internal purposes without stomping one anyone's feed. + * internal purposes without stomping on anyone's feed. */ sk->err = error; release_sock(sk); + lock_kernel(); return newsk; } @@ -1765,6 +1789,8 @@ extern void __skb_cb_too_small_for_tcp(int, int); void __init tcp_init(void) { struct sk_buff *skb = NULL; + unsigned long goal; + int order; if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)) __skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb), @@ -1790,4 +1816,37 @@ void __init tcp_init(void) NULL, NULL); if(!tcp_timewait_cachep) panic("tcp_init: Cannot alloc tcp_tw_bucket cache."); + + /* Size and allocate the main established and bind bucket + * hash tables. + * + * The methodology is similar to that of the buffer cache. + */ + goal = num_physpages >> (20 - PAGE_SHIFT); + for(order = 5; (1UL << order) < goal; order++) + ; + do { + tcp_ehash_size = (1UL << order) * PAGE_SIZE / + sizeof(struct sock *); + tcp_ehash = (struct sock **) + __get_free_pages(GFP_ATOMIC, order); + } while (tcp_ehash == NULL && --order > 4); + + if (!tcp_ehash) + panic("Failed to allocate TCP established hash table\n"); + memset(tcp_ehash, 0, tcp_ehash_size * sizeof(struct sock *)); + + do { + tcp_bhash_size = (1UL << order) * PAGE_SIZE / + sizeof(struct tcp_bind_bucket *); + tcp_bhash = (struct tcp_bind_bucket **) + __get_free_pages(GFP_ATOMIC, order); + } while (tcp_bhash == NULL && --order > 4); + + if (!tcp_bhash) + panic("Failed to allocate TCP bind hash table\n"); + memset(tcp_bhash, 0, tcp_bhash_size * sizeof(struct tcp_bind_bucket *)); + + printk("TCP: Hash tables configured (established %d bind %d)\n", + tcp_ehash_size, tcp_bhash_size); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4a607a749..af4165fce 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.164 1999/05/08 21:09:52 davem Exp $ + * Version: $Id: tcp_input.c,v 1.169 1999/06/09 08:29:13 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -748,7 +748,6 @@ static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp, static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp) { struct sk_buff *skb = skb_peek(&sk->write_queue); - __u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when); /* Some data was ACK'd, if still retransmitting (due to a * timeout), resend more of the retransmit queue. The @@ -758,6 +757,9 @@ static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp) tcp_xmit_retransmit_queue(sk); tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); } else { + __u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when); + if ((__s32)when < 0) + when = 1; tcp_reset_xmit_timer(sk, TIME_RETRANS, when); } } @@ -785,8 +787,6 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, if (after(ack, tp->snd_nxt) || before(ack, tp->snd_una)) goto uninteresting_ack; - dst_confirm(sk->dst_cache); - /* If there is data set flag 1 */ if (len != th->doff*4) { flag |= FLAG_DATA; @@ -882,6 +882,24 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, /* Clear any aborted fast retransmit starts. */ tp->dup_acks = 0; } + /* It is not a brain fart, I thought a bit now. 8) + * + * Forward progress is indicated, if: + * 1. the ack acknowledges new data. + * 2. or the ack is duplicate, but it is caused by new segment + * arrival. This case is filtered by: + * - it contains no data, syn or fin. + * - it does not update window. + * 3. or new SACK. It is difficult to check, so that we ignore it. + * + * Forward progress is also indicated by arrival new data, + * which was caused by window open from our side. This case is more + * difficult and it is made (alas, incorrectly) in tcp_data_queue(). + * --ANK (990513) + */ + if (ack != tp->snd_una || (flag == 0 && !th->fin)) + dst_confirm(sk->dst_cache); + /* Remember the highest ack received. */ tp->snd_una = ack; return 1; @@ -896,8 +914,11 @@ extern void tcp_tw_schedule(struct tcp_tw_bucket *tw); extern void tcp_tw_reschedule(struct tcp_tw_bucket *tw); extern void tcp_tw_deschedule(struct tcp_tw_bucket *tw); +/* Must be called only from BH context. */ void tcp_timewait_kill(struct tcp_tw_bucket *tw) { + SOCKHASH_LOCK_WRITE_BH(); + /* Unlink from various places. */ if(tw->bind_next) tw->bind_next->bind_pprev = tw->bind_pprev; @@ -915,6 +936,8 @@ void tcp_timewait_kill(struct tcp_tw_bucket *tw) tw->sklist_next->sklist_prev = tw->sklist_prev; tw->sklist_prev->sklist_next = tw->sklist_next; + SOCKHASH_UNLOCK_WRITE_BH(); + /* Ok, now free it up. */ kmem_cache_free(tcp_timewait_cachep, tw); } @@ -945,6 +968,7 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, struct sock *sk; struct tcp_func *af_specific = tw->af_specific; __u32 isn; + int ret; isn = tw->rcv_nxt + 128000; if(isn == 0) @@ -953,14 +977,25 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb, tcp_timewait_kill(tw); sk = af_specific->get_sock(skb, th); if(sk == NULL || - !ipsec_sk_policy(sk,skb) || - atomic_read(&sk->sock_readers) != 0) + !ipsec_sk_policy(sk,skb)) return 0; + + bh_lock_sock(sk); + + /* Default is to discard the frame. */ + ret = 0; + + if(sk->lock.users) + goto out_unlock; + skb_set_owner_r(skb, sk); af_specific = sk->tp_pinfo.af_tcp.af_specific; + if(af_specific->conn_request(sk, skb, isn) < 0) - return 1; /* Toss a reset back. */ - return 0; /* Discard the frame. */ + ret = 1; /* Toss a reset back. */ + out_unlock: + bh_unlock_sock(sk); + return ret; } /* Check RST or SYN */ @@ -1013,7 +1048,7 @@ static __inline__ void tcp_tw_hashdance(struct sock *sk, struct tcp_tw_bucket *t sk->prot->inuse--; /* Step 4: Hash TW into TIMEWAIT half of established hash table. */ - head = &tcp_established_hash[sk->hashent + (TCP_HTABLE_SIZE/2)]; + head = &tcp_ehash[sk->hashent + (tcp_ehash_size >> 1)]; sktw = (struct sock *)tw; if((sktw->next = *head) != NULL) (*head)->pprev = &sktw->next; @@ -1051,7 +1086,9 @@ void tcp_time_wait(struct sock *sk) } #endif /* Linkage updates. */ + SOCKHASH_LOCK_WRITE(); tcp_tw_hashdance(sk, tw); + SOCKHASH_UNLOCK_WRITE(); /* Get the TIME_WAIT timeout firing. */ tcp_tw_schedule(tw); @@ -1801,7 +1838,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, } } - flg = *(((u32 *)th) + 3) & ~htonl(0x8 << 16); + flg = *(((u32 *)th) + 3) & ~htonl(0xFC8 << 16); /* pred_flags is 0xS?10 << 16 + snd_wnd * if header_predition is to be made @@ -2031,8 +2068,26 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, /* These use the socket TOS.. * might want to be the received TOS */ - if(th->ack) - return 1; + if(th->ack) { + struct sock *realsk; + int ret; + + realsk = tp->af_specific->get_sock(skb, th); + if(realsk == sk) + return 1; + + bh_lock_sock(realsk); + ret = 0; + if(realsk->lock.users != 0) { + skb_orphan(skb); + sk_add_backlog(realsk, skb); + } else { + ret = tcp_rcv_state_process(realsk, skb, + skb->h.th, skb->len); + } + bh_unlock_sock(realsk); + return ret; + } if(th->syn) { if(tp->af_specific->conn_request(sk, skb, 0) < 0) @@ -2067,21 +2122,81 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * not be in line code. [AC] */ if(th->ack) { - tp->snd_wl1 = TCP_SKB_CB(skb)->seq; - - /* We got an ack, but it's not a good ack. */ - if(!tcp_ack(sk,th, TCP_SKB_CB(skb)->seq, - TCP_SKB_CB(skb)->ack_seq, len)) + /* rfc793: + * "If the state is SYN-SENT then + * first check the ACK bit + * If the ACK bit is set + * If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send + * a reset (unless the RST bit is set, if so drop + * the segment and return)" + * + * I cite this place to emphasize one essential + * detail, this check is different of one + * in established state: SND.UNA <= SEG.ACK <= SND.NXT. + * SEG_ACK == SND.UNA == ISS is invalid in SYN-SENT, + * because we have no previous data sent before SYN. + * --ANK(990513) + * + * We do not send data with SYN, so that RFC-correct + * test reduces to: + */ + if (sk->zapped || + TCP_SKB_CB(skb)->ack_seq != tp->snd_nxt) return 1; - if(th->rst) { + /* Now ACK is acceptable. + * + * "If the RST bit is set + * If the ACK was acceptable then signal the user "error: + * connection reset", drop the segment, enter CLOSED state, + * delete TCB, and return." + */ + + if (th->rst) { tcp_reset(sk); goto discard; } - if(!th->syn) + /* rfc793: + * "fifth, if neither of the SYN or RST bits is set then + * drop the segment and return." + * + * See note below! + * --ANK(990513) + */ + + if (!th->syn) goto discard; + /* rfc793: + * "If the SYN bit is on ... + * are acceptable then ... + * (our SYN has been ACKed), change the connection + * state to ESTABLISHED..." + * + * Do you see? SYN-less ACKs in SYN-SENT state are + * completely ignored. + * + * The bug causing stalled SYN-SENT sockets + * was here: tcp_ack advanced snd_una and canceled + * retransmit timer, so that bare ACK received + * in SYN-SENT state (even with invalid ack==ISS, + * because tcp_ack check is too weak for SYN-SENT) + * causes moving socket to invalid semi-SYN-SENT, + * semi-ESTABLISHED state and connection hangs. + * + * There exist buggy stacks, which really send + * such ACKs: f.e. 202.226.91.94 (okigate.oki.co.jp) + * Actually, if this host did not try to get something + * from ftp.inr.ac.ru I'd never find this bug 8) + * + * --ANK (990514) + */ + + tp->snd_wl1 = TCP_SKB_CB(skb)->seq; + tcp_ack(sk,th, TCP_SKB_CB(skb)->seq, + TCP_SKB_CB(skb)->ack_seq, len); + /* Ok.. it's good. Set up sequence numbers and * move to established. */ @@ -2206,8 +2321,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, !(th->fin && TCP_SKB_CB(skb)->end_seq == tp->rcv_nxt)) { if (!th->rst) { tcp_send_ack(sk); - goto discard; } + goto discard; } /* step 2: check RST bit */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b5070c3a7..564e859f2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.175 1999/05/08 21:09:54 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.180 1999/06/09 08:29:19 davem Exp $ * * IPv4 specific functions * @@ -90,12 +90,14 @@ void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, * First half of the table is for sockets not in TIME_WAIT, second half * is for TIME_WAIT sockets only. */ -struct sock *tcp_established_hash[TCP_HTABLE_SIZE]; +struct sock **tcp_ehash; +int tcp_ehash_size; /* Ok, let's try this, I give up, we do need a local binding * TCP hash as well as the others for fast bind/connect. */ -struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE]; +struct tcp_bind_bucket **tcp_bhash; +int tcp_bhash_size; /* All sockets in TCP_LISTEN state will be in here. This is the only table * where wildcard'd TCP sockets can exist. Hash function here is just local @@ -117,7 +119,7 @@ int tcp_port_rover = (1024 - 1); static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport, __u32 faddr, __u16 fport) { - return ((laddr ^ lport) ^ (faddr ^ fport)) & ((TCP_HTABLE_SIZE/2) - 1); + return ((laddr ^ lport) ^ (faddr ^ fport)) & ((tcp_ehash_size >> 1) - 1); } static __inline__ int tcp_sk_hashfn(struct sock *sk) @@ -136,8 +138,8 @@ void tcp_bucket_unlock(struct sock *sk) struct tcp_bind_bucket *tb; unsigned short snum = sk->num; - SOCKHASH_LOCK(); - for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb; tb = tb->next) { + SOCKHASH_LOCK_WRITE(); + for(tb = tcp_bhash[tcp_bhashfn(snum)]; tb; tb = tb->next) { if(tb->port == snum) { if(tb->owners == NULL && (tb->flags & TCPB_FLAG_LOCKED)) { @@ -148,9 +150,10 @@ void tcp_bucket_unlock(struct sock *sk) break; } } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } +/* The sockhash lock must be held as a writer here. */ struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum) { struct tcp_bind_bucket *tb; @@ -158,7 +161,7 @@ struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum) tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC); if(tb != NULL) { struct tcp_bind_bucket **head = - &tcp_bound_hash[tcp_bhashfn(snum)]; + &tcp_bhash[tcp_bhashfn(snum)]; tb->port = snum; tb->flags = TCPB_FLAG_LOCKED; tb->owners = NULL; @@ -176,13 +179,18 @@ struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum) */ static __inline__ int tcp_bucket_check(unsigned short snum) { - struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(snum)]; + struct tcp_bind_bucket *tb; + int ret = 0; + + SOCKHASH_LOCK_WRITE(); + tb = tcp_bhash[tcp_bhashfn(snum)]; for( ; (tb && (tb->port != snum)); tb = tb->next) ; if(tb == NULL && tcp_bucket_create(snum) == NULL) - return 1; - else - return 0; + ret = 1; + SOCKHASH_UNLOCK_WRITE(); + + return ret; } #endif @@ -191,8 +199,8 @@ static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum) struct tcp_bind_bucket *tb; int result = 0; - SOCKHASH_LOCK(); - for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; + SOCKHASH_LOCK_WRITE(); + for(tb = tcp_bhash[tcp_bhashfn(snum)]; (tb && (tb->port != snum)); tb = tb->next) ; @@ -256,7 +264,7 @@ static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum) } } go_like_smoke: - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); return result; } @@ -268,13 +276,13 @@ unsigned short tcp_good_socknum(void) int remaining = (high - low) + 1; int rover; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); rover = tcp_port_rover; do { rover += 1; if((rover < low) || (rover > high)) rover = low; - tb = tcp_bound_hash[tcp_bhashfn(rover)]; + tb = tcp_bhash[tcp_bhashfn(rover)]; for( ; tb; tb = tb->next) { if(tb->port == rover) goto next; @@ -288,7 +296,7 @@ unsigned short tcp_good_socknum(void) rover = 0; if (tb != NULL) tb->flags |= TCPB_FLAG_GOODSOCKNUM; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); return rover; } @@ -298,20 +306,20 @@ static void tcp_v4_hash(struct sock *sk) if (sk->state != TCP_CLOSE) { struct sock **skp; - SOCKHASH_LOCK(); - skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))]; + SOCKHASH_LOCK_WRITE(); + skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; tcp_sk_bindify(sk); - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } } static void tcp_v4_unhash(struct sock *sk) { - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); if(sk->pprev) { if(sk->next) sk->next->pprev = sk->pprev; @@ -320,14 +328,14 @@ static void tcp_v4_unhash(struct sock *sk) tcp_reg_zap(sk); tcp_sk_unbindify(sk); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void tcp_v4_rehash(struct sock *sk) { unsigned char state; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); state = sk->state; if(sk->pprev != NULL) { if(sk->next) @@ -342,7 +350,7 @@ static void tcp_v4_rehash(struct sock *sk) if(state == TCP_LISTEN) skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; else - skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))]; + skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; @@ -351,7 +359,7 @@ static void tcp_v4_rehash(struct sock *sk) if(state == TCP_LISTEN) tcp_sk_bindify(sk); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } /* Don't inline this cruft. Here are some nice properties to @@ -395,10 +403,10 @@ static struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, int d /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM - * It is assumed that this code only gets called from within NET_BH. + * + * The sockhash lock must be held as a reader here. */ -static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, - u32 saddr, u16 sport, +static inline struct sock *__tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif) { TCP_V4_ADDR_COOKIE(acookie, saddr, daddr) @@ -416,7 +424,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, * have wildcards anyways. */ hash = tcp_hashfn(daddr, hnum, saddr, sport); - for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { + for(sk = tcp_ehash[hash]; sk; sk = sk->next) { if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) { if (sk->state == TCP_ESTABLISHED) TCP_RHASH(sport) = sk; @@ -424,7 +432,7 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, } } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) + for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next) if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) goto hit; sk = tcp_v4_lookup_listener(daddr, hnum, dif); @@ -434,7 +442,13 @@ hit: __inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif) { - return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dif); + struct sock *sk; + + SOCKHASH_LOCK_READ(); + sk = __tcp_v4_lookup(saddr, sport, daddr, dport, dif); + SOCKHASH_UNLOCK_READ(); + + return sk; } #ifdef CONFIG_IP_TRANSPARENT_PROXY @@ -462,9 +476,12 @@ static struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, paddr = idev->ifa_list->ifa_local; } - /* This code must run only from NET_BH. */ + /* We must obtain the sockhash lock here, we are always + * in BH context. + */ + SOCKHASH_LOCK_READ_BH(); { - struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hnum)]; + struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hnum)]; for( ; (tb && tb->port != hnum); tb = tb->next) ; if(tb == NULL) @@ -505,7 +522,7 @@ pass2: } next: if(firstpass--) { - struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hpnum)]; + struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hpnum)]; for( ; (tb && tb->port != hpnum); tb = tb->next) ; if(tb) { @@ -514,6 +531,7 @@ next: } } gotit: + SOCKHASH_UNLOCK_READ_BH(); return result; } #endif /* CONFIG_IP_TRANSPARENT_PROXY */ @@ -540,21 +558,23 @@ static int tcp_v4_unique_address(struct sock *sk) int retval = 1; /* Freeze the hash while we snoop around. */ - SOCKHASH_LOCK(); - tb = tcp_bound_hash[tcp_bhashfn(snum)]; + SOCKHASH_LOCK_READ(); + tb = tcp_bhash[tcp_bhashfn(snum)]; for(; tb; tb = tb->next) { if(tb->port == snum && tb->owners != NULL) { /* Almost certainly the re-use port case, search the real hashes * so it actually scales. */ - sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dport, + sk = __tcp_v4_lookup(sk->daddr, sk->dport, sk->rcv_saddr, snum, sk->bound_dev_if); + SOCKHASH_UNLOCK_READ(); + if((sk != NULL) && (sk->state != TCP_LISTEN)) retval = 0; - break; + return retval; } } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); return retval; } @@ -727,16 +747,17 @@ static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; - if (atomic_read(&sk->sock_readers)) - return; - - /* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs + /* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs * send out by Linux are always <576bytes so they should go through * unfragmented). */ if (sk->state == TCP_LISTEN) return; + bh_lock_sock(sk); + if(sk->lock.users != 0) + goto out; + /* We don't check in the destentry if pmtu discovery is forbidden * on this route. We just assume that no packet_to_big packets * are send back when pmtu discovery is not active. @@ -744,7 +765,8 @@ static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned * route, but I think that's acceptable. */ if (sk->dst_cache == NULL) - return; + goto out; + ip_rt_update_pmtu(sk->dst_cache, mtu); if (sk->ip_pmtudisc != IP_PMTUDISC_DONT && tp->pmtu_cookie > sk->dst_cache->pmtu) { @@ -757,6 +779,8 @@ static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned */ tcp_simple_retransmit(sk); } /* else let the usual retransmit timer handle it */ +out: + bh_unlock_sock(sk); } /* @@ -849,17 +873,6 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) switch (sk->state) { struct open_request *req, *prev; case TCP_LISTEN: - /* Prevent race conditions with accept() - - * ICMP is unreliable. - */ - if (atomic_read(&sk->sock_readers)) { - net_statistics.LockDroppedIcmps++; - /* If too many ICMPs get dropped on busy - * servers this needs to be solved differently. - */ - return; - } - /* The final ACK of the handshake should be already * handled in the new socket context, not here. * Strictly speaking - an ICMP error for the final @@ -869,12 +882,24 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) if (!no_flags && !th->syn && !th->ack) return; + /* Prevent race conditions with accept() - + * ICMP is unreliable. + */ + bh_lock_sock(sk); + if (sk->lock.users != 0) { + net_statistics.LockDroppedIcmps++; + /* If too many ICMPs get dropped on busy + * servers this needs to be solved differently. + */ + goto out_unlock; + } + req = tcp_v4_search_req(tp, iph, th, &prev); if (!req) - return; + goto out_unlock; if (seq != req->snt_isn) { net_statistics.OutOfWindowIcmps++; - return; + goto out_unlock; } if (req->sk) { /* @@ -884,6 +909,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) * but only with the next operation on the socket after * accept. */ + bh_unlock_sock(sk); sk = req->sk; } else { /* @@ -896,6 +922,8 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len) tcp_synq_unlink(tp, req, prev); req->class->destructor(req); tcp_openreq_free(req); + out_unlock: + bh_unlock_sock(sk); return; } break; @@ -1025,9 +1053,10 @@ static struct sock *tcp_v4_search_proxy_openreq(struct sk_buff *skb) { struct iphdr *iph = skb->nh.iph; struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4); - struct sock *sk; + struct sock *sk = NULL; int i; + SOCKHASH_LOCK_READ(); for (i=0; i<TCP_LHTABLE_SIZE; i++) { for(sk = tcp_listening_hash[i]; sk; sk = sk->next) { struct open_request *dummy; @@ -1035,10 +1064,12 @@ static struct sock *tcp_v4_search_proxy_openreq(struct sk_buff *skb) th, &dummy) && (!sk->bound_dev_if || sk->bound_dev_if == skb->dev->ifindex)) - return sk; + goto out; } } - return NULL; +out: + SOCKHASH_UNLOCK_READ(); + return sk; } /* @@ -1319,7 +1350,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, /* Clone the TCP header template */ newsk->dport = req->rmt_port; - atomic_set(&newsk->sock_readers, 0); + sock_lock_init(newsk); + atomic_set(&newsk->rmem_alloc, 0); skb_queue_head_init(&newsk->receive_queue); atomic_set(&newsk->wmem_alloc, 0); @@ -1328,9 +1360,9 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, newsk->done = 0; newsk->proc = 0; - newsk->pair = NULL; - skb_queue_head_init(&newsk->back_log); + newsk->backlog.head = newsk->backlog.tail = NULL; skb_queue_head_init(&newsk->error_queue); + newsk->write_space = tcp_write_space; #ifdef CONFIG_FILTER if ((filter = newsk->filter) != NULL) sk_filter_charge(newsk, filter); @@ -1552,7 +1584,8 @@ static inline struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb) } /* Check for SYN|ACK */ - if (flg & __constant_htonl(0x00120000)) { + flg &= __constant_htonl(0x00120000); + if (flg) { struct open_request *req, *dummy; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); @@ -1570,8 +1603,17 @@ static inline struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb) return sk; } +/* The socket must have it's spinlock held when we get + * here. + * + * We have a potential double-lock case here, so even when + * doing backlog processing we use the BH locking scheme. + * This is because we cannot sleep with the original spinlock + * held. + */ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) { + int need_unlock = 0; #ifdef CONFIG_FILTER struct sk_filter *filter = sk->filter; if (filter && sk_filter(skb, filter)) @@ -1591,7 +1633,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) return 0; } - if (sk->state == TCP_LISTEN) { struct sock *nsk; @@ -1604,17 +1645,22 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) * otherwise we just shortcircuit this and continue with * the new socket.. */ - if (atomic_read(&nsk->sock_readers)) { - skb_orphan(skb); - __skb_queue_tail(&nsk->back_log, skb); - return 0; + if (nsk != sk) { + bh_lock_sock(nsk); + if (nsk->lock.users != 0) { + skb_orphan(skb); + sk_add_backlog(nsk, skb); + bh_unlock_sock(nsk); + return 0; + } + need_unlock = 1; + sk = nsk; } - sk = nsk; } if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) goto reset; - return 0; + goto out_maybe_unlock; reset: tcp_v4_send_reset(skb); @@ -1625,6 +1671,9 @@ discard: * might be destroyed here. This current version compiles correctly, * but you have been warned. */ +out_maybe_unlock: + if(need_unlock) + bh_unlock_sock(sk); return 0; } @@ -1636,6 +1685,7 @@ int tcp_v4_rcv(struct sk_buff *skb, unsigned short len) { struct tcphdr *th; struct sock *sk; + int ret; if (skb->pkt_type!=PACKET_HOST) goto discard_it; @@ -1681,8 +1731,10 @@ int tcp_v4_rcv(struct sk_buff *skb, unsigned short len) IPCB(skb)->redirport, skb->dev->ifindex); else { #endif - sk = __tcp_v4_lookup(th, skb->nh.iph->saddr, th->source, + SOCKHASH_LOCK_READ_BH(); + sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source, skb->nh.iph->daddr, th->dest, skb->dev->ifindex); + SOCKHASH_UNLOCK_READ_BH(); #ifdef CONFIG_IP_TRANSPARENT_PROXY if (!sk) sk = tcp_v4_search_proxy_openreq(skb); @@ -1702,11 +1754,16 @@ int tcp_v4_rcv(struct sk_buff *skb, unsigned short len) if (sk->state == TCP_TIME_WAIT) goto do_time_wait; - if (!atomic_read(&sk->sock_readers)) - return tcp_v4_do_rcv(sk, skb); - __skb_queue_tail(&sk->back_log, skb); - return 0; + bh_lock_sock(sk); + ret = 0; + if (!sk->lock.users) + ret = tcp_v4_do_rcv(sk, skb); + else + sk_add_backlog(sk, skb); + bh_unlock_sock(sk); + + return ret; no_tcp_socket: tcp_v4_send_reset(skb); @@ -1944,6 +2001,8 @@ __initfunc(void tcp_v4_init(struct net_proto_family *ops)) tcp_inode.i_sock = 1; tcp_inode.i_uid = 0; tcp_inode.i_gid = 0; + init_waitqueue_head(&tcp_inode.i_wait); + init_waitqueue_head(&tcp_inode.u.socket_i.wait); tcp_socket->inode = &tcp_inode; tcp_socket->state = SS_UNCONNECTED; @@ -1952,6 +2011,11 @@ __initfunc(void tcp_v4_init(struct net_proto_family *ops)) if ((err=ops->create(tcp_socket, IPPROTO_TCP))<0) panic("Failed to create the TCP control socket.\n"); tcp_socket->sk->allocation=GFP_ATOMIC; - tcp_socket->sk->num = 256; /* Don't receive any data */ tcp_socket->sk->ip_ttl = MAXTTL; + + /* Unhash it so that IP input processing does not even + * see it, we do not wish this socket to see incoming + * packets. + */ + tcp_socket->sk->prot->unhash(tcp_socket->sk); } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9a096f0f3..18b5ebf80 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.108 1999/05/08 21:48:59 davem Exp $ + * Version: $Id: tcp_output.c,v 1.110 1999/05/27 00:37:45 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -36,6 +36,8 @@ #include <net/tcp.h> +#include <linux/smp_lock.h> + extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; extern int sysctl_tcp_sack; @@ -240,6 +242,11 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len) /* Rechecksum original buffer. */ skb->csum = csum_partial(skb->data, skb->len, 0); + /* Looks stupid, but our code really uses when of + * skbs, which it never sent before. --ANK + */ + TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when; + /* Link BUFF into the send queue. */ __skb_append(skb, buff); @@ -961,6 +968,7 @@ void tcp_connect(struct sock *sk, struct sk_buff *buff, int mtu) /* Ok, now lock the socket before we make it visible to * the incoming packet engine. */ + unlock_kernel(); lock_sock(sk); /* Socket identity change complete, no longer @@ -988,6 +996,7 @@ void tcp_connect(struct sock *sk, struct sk_buff *buff, int mtu) /* Now, it is safe to release the socket. */ release_sock(sk); + lock_kernel(); } /* Send out a delayed ack, the caller does the policy checking diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index ad6ccace9..d23eef143 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.62 1999/05/08 21:09:55 davem Exp $ + * Version: $Id: tcp_timer.c,v 1.64 1999/05/27 00:37:31 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -168,15 +168,16 @@ void tcp_delack_timer(unsigned long data) { struct sock *sk = (struct sock*)data; + bh_lock_sock(sk); if(!sk->zapped && sk->tp_pinfo.af_tcp.delayed_acks && sk->state != TCP_CLOSE) { - /* If socket is currently locked, defer the ACK. */ - if (!atomic_read(&sk->sock_readers)) + if (!sk->lock.users) tcp_send_ack(sk); else tcp_send_delayed_ack(&(sk->tp_pinfo.af_tcp), HZ/10); } + bh_unlock_sock(sk); } void tcp_probe_timer(unsigned long data) @@ -187,9 +188,11 @@ void tcp_probe_timer(unsigned long data) if(sk->zapped) return; - if (atomic_read(&sk->sock_readers)) { + bh_lock_sock(sk); + if (sk->lock.users) { /* Try again later. */ tcp_reset_xmit_timer(sk, TIME_PROBE0, HZ/5); + bh_unlock_sock(sk); return; } @@ -216,6 +219,7 @@ void tcp_probe_timer(unsigned long data) /* Only send another probe if we didn't close things up. */ tcp_send_probe0(sk); } + bh_unlock_sock(sk); } static __inline__ int tcp_keepopen_proc(struct sock *sk) @@ -253,8 +257,9 @@ static void tcp_bucketgc(unsigned long data) { int i, reaped = 0;; - for(i = 0; i < TCP_BHTABLE_SIZE; i++) { - struct tcp_bind_bucket *tb = tcp_bound_hash[i]; + SOCKHASH_LOCK_WRITE_BH(); + for(i = 0; i < tcp_bhash_size; i++) { + struct tcp_bind_bucket *tb = tcp_bhash[i]; while(tb) { struct tcp_bind_bucket *next = tb->next; @@ -274,6 +279,8 @@ static void tcp_bucketgc(unsigned long data) tb = next; } } + SOCKHASH_UNLOCK_WRITE_BH(); + if(reaped != 0) { struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data; @@ -294,8 +301,14 @@ static void tcp_twkill(unsigned long data) struct tcp_tw_bucket *tw; int killed = 0; + /* The death-row tw chains are only ever touched + * in BH context so no locking is needed. + */ tw = tcp_tw_death_row[tcp_tw_death_row_slot]; tcp_tw_death_row[tcp_tw_death_row_slot] = NULL; + tcp_tw_death_row_slot = + ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1)); + while(tw != NULL) { struct tcp_tw_bucket *next = tw->next_death; @@ -307,8 +320,6 @@ static void tcp_twkill(unsigned long data) struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data; atomic_sub(killed, &slt->count); } - tcp_tw_death_row_slot = - ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1)); } /* These are always called from BH context. See callers in @@ -319,12 +330,14 @@ void tcp_tw_schedule(struct tcp_tw_bucket *tw) int slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1); struct tcp_tw_bucket **tpp = &tcp_tw_death_row[slot]; + SOCKHASH_LOCK_WRITE_BH(); if((tw->next_death = *tpp) != NULL) (*tpp)->pprev_death = &tw->next_death; *tpp = tw; tw->pprev_death = tpp; tw->death_slot = slot; + SOCKHASH_UNLOCK_WRITE_BH(); tcp_inc_slow_timer(TCP_SLT_TWKILL); } @@ -335,6 +348,7 @@ void tcp_tw_reschedule(struct tcp_tw_bucket *tw) struct tcp_tw_bucket **tpp; int slot; + SOCKHASH_LOCK_WRITE_BH(); if(tw->next_death) tw->next_death->pprev_death = tw->pprev_death; *tw->pprev_death = tw->next_death; @@ -348,16 +362,21 @@ void tcp_tw_reschedule(struct tcp_tw_bucket *tw) tw->pprev_death = tpp; tw->death_slot = slot; + SOCKHASH_UNLOCK_WRITE_BH(); + /* Timer was incremented when we first entered the table. */ } /* This is for handling early-kills of TIME_WAIT sockets. */ void tcp_tw_deschedule(struct tcp_tw_bucket *tw) { + SOCKHASH_LOCK_WRITE_BH(); if(tw->next_death) tw->next_death->pprev_death = tw->pprev_death; *tw->pprev_death = tw->next_death; tw->pprev_death = NULL; + SOCKHASH_UNLOCK_WRITE_BH(); + tcp_dec_slow_timer(TCP_SLT_TWKILL); } @@ -399,20 +418,30 @@ static void tcp_keepalive(unsigned long data) int count = 0; int i; - for(i = chain_start; i < (chain_start + ((TCP_HTABLE_SIZE/2) >> 2)); i++) { - struct sock *sk = tcp_established_hash[i]; + SOCKHASH_LOCK_READ_BH(); + for(i = chain_start; i < (chain_start + ((tcp_ehash_size >> 1) >> 2)); i++) { + struct sock *sk; + + sk = tcp_ehash[i]; while(sk) { - if(!atomic_read(&sk->sock_readers) && sk->keepopen) { + struct sock *next = sk->next; + + bh_lock_sock(sk); + if (sk->keepopen && !sk->lock.users) { + SOCKHASH_UNLOCK_READ_BH(); count += tcp_keepopen_proc(sk); - if(count == sysctl_tcp_max_ka_probes) - goto out; + SOCKHASH_LOCK_READ_BH(); } - sk = sk->next; + bh_unlock_sock(sk); + if(count == sysctl_tcp_max_ka_probes) + goto out; + sk = next; } } out: - chain_start = ((chain_start + ((TCP_HTABLE_SIZE/2)>>2)) & - ((TCP_HTABLE_SIZE/2) - 1)); + SOCKHASH_UNLOCK_READ_BH(); + chain_start = ((chain_start + ((tcp_ehash_size >> 1)>>2)) & + ((tcp_ehash_size >> 1) - 1)); } /* @@ -439,9 +468,11 @@ void tcp_retransmit_timer(unsigned long data) return; } - if (atomic_read(&sk->sock_readers)) { + bh_lock_sock(sk); + if (sk->lock.users) { /* Try again later */ tcp_reset_xmit_timer(sk, TIME_RETRANS, HZ/20); + bh_unlock_sock(sk); return; } @@ -508,12 +539,51 @@ void tcp_retransmit_timer(unsigned long data) tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto); tcp_write_timeout(sk); + + bh_unlock_sock(sk); } /* * Slow timer for SYN-RECV sockets */ +static void tcp_do_syn_queue(struct sock *sk, struct tcp_opt *tp, unsigned long now) +{ + struct open_request *prev, *req; + + prev = (struct open_request *) &tp->syn_wait_queue; + for(req = tp->syn_wait_queue; req; ) { + struct open_request *next = req->dl_next; + + if (! req->sk) { + tcp_synq_unlink(tp, req, prev); + if(req->retrans >= sysctl_tcp_retries1) { + (*req->class->destructor)(req); + tcp_dec_slow_timer(TCP_SLT_SYNACK); + tp->syn_backlog--; + tcp_openreq_free(req); + if (! tp->syn_wait_queue) + break; + } else { + unsigned long timeo; + struct open_request *rp; + + (*req->class->rtx_syn_ack)(sk, req); + req->retrans++; + timeo = min((TCP_TIMEOUT_INIT << req->retrans), + (120 * HZ)); + req->expires = now + timeo; + rp = prev->dl_next; + tcp_synq_queue(tp, req); + if(rp != prev->dl_next) + prev = prev->dl_next; + } + } else + prev = req; + req = next; + } +} + /* This now scales very nicely. -DaveM */ static void tcp_syn_recv_timer(unsigned long data) { @@ -521,70 +591,21 @@ static void tcp_syn_recv_timer(unsigned long data) unsigned long now = jiffies; int i; + SOCKHASH_LOCK_READ_BH(); for(i = 0; i < TCP_LHTABLE_SIZE; i++) { sk = tcp_listening_hash[i]; - while(sk) { struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; /* TCP_LISTEN is implied. */ - if (!atomic_read(&sk->sock_readers) && tp->syn_wait_queue) { - struct open_request *prev = (struct open_request *)(&tp->syn_wait_queue); - struct open_request *req = tp->syn_wait_queue; - do { - struct open_request *conn; - - conn = req; - req = req->dl_next; - - if (conn->sk) { - prev = conn; - continue; - } - - if ((long)(now - conn->expires) <= 0) - break; - - - tcp_synq_unlink(tp, conn, prev); - if (conn->retrans >= sysctl_tcp_retries1) { -#ifdef TCP_DEBUG - printk(KERN_DEBUG "syn_recv: " - "too many retransmits\n"); -#endif - (*conn->class->destructor)(conn); - tcp_dec_slow_timer(TCP_SLT_SYNACK); - tp->syn_backlog--; - tcp_openreq_free(conn); - - if (!tp->syn_wait_queue) - break; - } else { - unsigned long timeo; - struct open_request *op; - - (*conn->class->rtx_syn_ack)(sk, conn); - - conn->retrans++; -#ifdef TCP_DEBUG - printk(KERN_DEBUG "syn_ack rtx %d\n", - conn->retrans); -#endif - timeo = min((TCP_TIMEOUT_INIT - << conn->retrans), - 120*HZ); - conn->expires = now + timeo; - op = prev->dl_next; - tcp_synq_queue(tp, conn); - if (op != prev->dl_next) - prev = prev->dl_next; - } - /* old prev still valid here */ - } while (req); - } + bh_lock_sock(sk); + if (!sk->lock.users && tp->syn_wait_queue) + tcp_do_syn_queue(sk, tp, now); + bh_unlock_sock(sk); sk = sk->next; } } + SOCKHASH_UNLOCK_READ_BH(); } void tcp_sltimer_handler(unsigned long data) diff --git a/net/ipv4/timer.c b/net/ipv4/timer.c index 3821a7c4c..0487f5bfa 100644 --- a/net/ipv4/timer.c +++ b/net/ipv4/timer.c @@ -5,7 +5,7 @@ * * TIMER - implementation of software timers for IP. * - * Version: $Id: timer.c,v 1.15 1999/02/22 13:54:29 davem Exp $ + * Version: $Id: timer.c,v 1.16 1999/05/27 00:37:39 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -69,13 +69,15 @@ void net_reset_timer (struct sock *t, int timeout, unsigned long len) */ void net_timer (unsigned long data) { - struct sock *sk = (struct sock*)data; + struct sock *sk = (struct sock *) data; int why = sk->timeout; /* Only process if socket is not in use. */ - if (atomic_read(&sk->sock_readers)) { + bh_lock_sock(sk); + if (sk->lock.users) { /* Try again later. */ mod_timer(&sk->timer, jiffies+HZ/20); + bh_unlock_sock(sk); return; } @@ -99,15 +101,15 @@ void net_timer (unsigned long data) printk (KERN_DEBUG "non CLOSE socket in time_done\n"); break; } - destroy_sock (sk); - break; + destroy_sock(sk); + return; case TIME_DESTROY: /* We've waited for a while for all the memory associated with * the socket to be freed. */ destroy_sock(sk); - break; + return; case TIME_CLOSE: /* We've waited long enough, close the socket. */ @@ -123,5 +125,8 @@ void net_timer (unsigned long data) printk ("net_timer: timer expired - reason %d is unknown\n", why); break; } + + /* We only need to unlock if the socket was not destroyed. */ + bh_unlock_sock(sk); } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 5fcec9cf3..320e5151e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,7 +5,7 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.66 1999/05/08 20:00:25 davem Exp $ + * Version: $Id: udp.c,v 1.69 1999/06/09 11:15:31 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -128,7 +128,7 @@ static int udp_v4_verify_bind(struct sock *sk, unsigned short snum) struct sock *sk2; int retval = 0, sk_reuse = sk->reuse; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_READ(); for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) { if((sk2->num == snum) && (sk2 != sk)) { unsigned char state = sk2->state; @@ -158,7 +158,7 @@ static int udp_v4_verify_bind(struct sock *sk, unsigned short snum) } } } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); return retval; } @@ -173,14 +173,14 @@ static inline int udp_lport_inuse(u16 num) return 0; } -/* Shared by v4/v6 tcp. */ +/* Shared by v4/v6 udp. */ unsigned short udp_good_socknum(void) { int result; static int start = 0; int i, best, best_size_so_far; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_READ(); if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0]) start = sysctl_local_port_range[0]; @@ -223,15 +223,10 @@ unsigned short udp_good_socknum(void) } out: start = result; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); return result; } -/* Last hit UDP socket cache, this is ipv4 specific so make it static. */ -static u32 uh_cache_saddr, uh_cache_daddr; -static u16 uh_cache_dport, uh_cache_sport; -static struct sock *uh_cache_sk = NULL; - static void udp_v4_hash(struct sock *sk) { struct sock **skp; @@ -240,11 +235,11 @@ static void udp_v4_hash(struct sock *sk) num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); sk->next = *skp; *skp = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void udp_v4_unhash(struct sock *sk) @@ -255,7 +250,7 @@ static void udp_v4_unhash(struct sock *sk) num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -263,9 +258,7 @@ static void udp_v4_unhash(struct sock *sk) } skp = &((*skp)->next); } - if(uh_cache_sk == sk) - uh_cache_sk = NULL; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void udp_v4_rehash(struct sock *sk) @@ -277,7 +270,7 @@ static void udp_v4_rehash(struct sock *sk) num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[oldnum]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -288,13 +281,11 @@ static void udp_v4_rehash(struct sock *sk) sk->next = udp_hash[num]; udp_hash[num] = sk; sk->hashent = num; - if(uh_cache_sk == sk) - uh_cache_sk = NULL; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } /* UDP is nearly always wildcards out the wazoo, it makes no sense to try - * harder than this here plus the last hit cache. -DaveM + * harder than this. -DaveM */ struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif) { @@ -341,21 +332,9 @@ __inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport { struct sock *sk; - if(!dif && uh_cache_sk && - uh_cache_saddr == saddr && - uh_cache_sport == sport && - uh_cache_dport == dport && - uh_cache_daddr == daddr) - return uh_cache_sk; - + SOCKHASH_LOCK_READ(); sk = udp_v4_lookup_longway(saddr, sport, daddr, dport, dif); - if(!dif) { - uh_cache_sk = sk; - uh_cache_saddr = saddr; - uh_cache_daddr = daddr; - uh_cache_sport = sport; - uh_cache_dport = dport; - } + SOCKHASH_UNLOCK_READ(); return sk; } @@ -393,7 +372,7 @@ static struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr, paddr = idev->ifa_list->ifa_local; } - SOCKHASH_LOCK(); + SOCKHASH_LOCK_READ(); for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass); s != NULL; s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) { @@ -431,7 +410,7 @@ static struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr, } } } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); return result; } @@ -784,7 +763,10 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len) /* 4.1.3.4. It's configurable by the application via setsockopt() */ /* (MAY) and it defaults to on (MUST). */ - err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag, + err = ip_build_xmit(sk, + (sk->no_check == UDP_CSUM_NOXMIT ? + udp_getfrag_nosum : + udp_getfrag), &ufh, ulen, &ipc, rt, msg->msg_flags); out: @@ -979,8 +961,6 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk->rcv_saddr=INADDR_ANY; sk->daddr=INADDR_ANY; sk->state = TCP_CLOSE; - if(uh_cache_sk == sk) - uh_cache_sk = NULL; return 0; } @@ -1005,9 +985,6 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk->dport = usin->sin_port; sk->state = TCP_ESTABLISHED; - if(uh_cache_sk == sk) - uh_cache_sk = NULL; - sk->dst_cache = &rt->u.dst; return(0); } @@ -1015,6 +992,8 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) static void udp_close(struct sock *sk, long timeout) { + bh_lock_sock(sk); + /* See for explanation: raw_close in ipv4/raw.c */ sk->state = TCP_CLOSE; udp_v4_unhash(sk); @@ -1117,6 +1096,33 @@ int udp_chkaddr(struct sk_buff *skb) } #endif +static int udp_checksum_verify(struct sk_buff *skb, struct udphdr *uh, + unsigned short ulen, u32 saddr, u32 daddr, + int full_csum_deferred) +{ + if (!full_csum_deferred) { + if (uh->check) { + if (skb->ip_summed == CHECKSUM_HW && + udp_check(uh, ulen, saddr, daddr, skb->csum)) + return -1; + if (skb->ip_summed == CHECKSUM_NONE && + udp_check(uh, ulen, saddr, daddr, + csum_partial((char *)uh, ulen, 0))) + return -1; + } + } else { + if (uh->check == 0) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else if (skb->ip_summed == CHECKSUM_HW) { + if (udp_check(uh, ulen, saddr, daddr, skb->csum)) + return -1; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else if (skb->ip_summed != CHECKSUM_UNNECESSARY) + skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0); + } + return 0; +} + /* * All we need to do is get the socket, and then do a checksum. */ @@ -1158,25 +1164,18 @@ int udp_rcv(struct sk_buff *skb, unsigned short len) } skb_trim(skb, ulen); -#ifndef CONFIG_UDP_DELAY_CSUM - if (uh->check && - (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) || - ((skb->ip_summed==CHECKSUM_NONE) && - (udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0)))))) - goto csum_error; + if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) { + int defer; + +#ifdef CONFIG_UDP_DELAY_CSUM + defer = 1; #else - if (uh->check==0) - skb->ip_summed = CHECKSUM_UNNECESSARY; - else if (skb->ip_summed==CHECKSUM_HW) { - if (udp_check(uh,ulen,saddr,daddr,skb->csum)) - goto csum_error; - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (skb->ip_summed != CHECKSUM_UNNECESSARY) - skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0); + defer = 0; #endif - - if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) + if (udp_checksum_verify(skb, uh, ulen, saddr, daddr, defer)) + goto csum_error; return udp_v4_mcast_deliver(skb, uh, saddr, daddr); + } #ifdef CONFIG_IP_TRANSPARENT_PROXY if (IPCB(skb)->redirport) @@ -1203,6 +1202,15 @@ int udp_rcv(struct sk_buff *skb, unsigned short len) kfree_skb(skb); return(0); } + if (udp_checksum_verify(skb, uh, ulen, saddr, daddr, +#ifdef CONFIG_UDP_DELAY_CSUM + 1 +#else + (sk->no_check & UDP_CSUM_NORCV) != 0 +#endif + )) + goto csum_error; + udp_deliver(sk, skb); return 0; diff --git a/net/ipv4/utils.c b/net/ipv4/utils.c index ce74ade2a..5992cbc55 100644 --- a/net/ipv4/utils.c +++ b/net/ipv4/utils.c @@ -6,7 +6,7 @@ * Various kernel-resident INET utility functions; mainly * for format conversion and debugging output. * - * Version: $Id: utils.c,v 1.6 1997/12/13 21:53:03 kuznet Exp $ + * Version: $Id: utils.c,v 1.7 1999/06/09 10:11:05 davem Exp $ * * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * @@ -57,6 +57,11 @@ char *in_ntoa(__u32 in) return(buff); } +char *in_ntoa2(__u32 in, char *buff) +{ + sprintf(buff, "%d.%d.%d.%d", NIPQUAD(in)); + return buff; +} /* * Convert an ASCII string to binary IP. diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f34975076..9f71f7cda 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: addrconf.c,v 1.48 1999/03/25 10:04:43 davem Exp $ + * $Id: addrconf.c,v 1.50 1999/06/09 10:11:09 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -100,9 +100,7 @@ static struct timer_list addr_chk_timer = { 1. The result of inet6_add_addr() is used only inside lock or from bh_atomic context. - 2. inet6_get_lladdr() is used only from bh protected context. - - 3. The result of ipv6_chk_addr() is not used outside of bh protected context. + 2. The result of ipv6_chk_addr() is not used outside of bh protected context. */ static __inline__ void addrconf_lock(void) @@ -463,7 +461,7 @@ out: return err; } -struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev) +int ipv6_get_lladdr(struct device *dev, struct in6_addr *addr) { struct inet6_ifaddr *ifp = NULL; struct inet6_dev *idev; @@ -471,12 +469,15 @@ struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev) if ((idev = ipv6_get_idev(dev)) != NULL) { addrconf_lock(); for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { - if (ifp->scope == IFA_LINK) - break; + if (ifp->scope == IFA_LINK) { + ipv6_addr_copy(addr, &ifp->addr); + addrconf_unlock(); + return 0; + } } addrconf_unlock(); } - return ifp; + return -EADDRNOTAVAIL; } /* @@ -982,6 +983,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev) return; } + read_lock(&dev_base_lock); for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->ip_ptr && (dev->flags & IFF_UP)) { struct in_device * in_dev = dev->ip_ptr; @@ -1014,6 +1016,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev) } } } + read_unlock(&dev_base_lock); } static void init_loopback(struct device *dev) @@ -1842,7 +1845,7 @@ __initfunc(void addrconf_init(void)) struct device *dev; /* This takes sense only during module load. */ - + read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { if (!(dev->flags&IFF_UP)) continue; @@ -1858,6 +1861,7 @@ __initfunc(void addrconf_init(void)) /* Ignore all other */ } } + read_unlock(&dev_base_lock); #endif #ifdef CONFIG_PROC_FS diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 36ab229ed..f7f50df86 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.43 1999/04/22 10:07:39 davem Exp $ + * $Id: af_inet6.c,v 1.44 1999/06/09 08:29:29 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -103,7 +103,7 @@ static int inet6_create(struct socket *sock, int protocol) if (protocol && protocol != IPPROTO_UDP) goto free_and_noproto; protocol = IPPROTO_UDP; - sk->no_check = UDP_NO_CHECK; + sk->no_check = UDP_CSUM_DEFAULT; prot=&udpv6_prot; sock->ops = &inet6_dgram_ops; } else if(sock->type == SOCK_RAW) { diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 8a4f85b6c..5fb915390 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -7,7 +7,7 @@ * Andi Kleen <ak@muc.de> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * - * $Id: exthdrs.c,v 1.8 1998/10/03 09:38:27 davem Exp $ + * $Id: exthdrs.c,v 1.9 1999/05/17 23:47:35 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -369,7 +369,7 @@ ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr) Certainly, it is possible only for udp and raw sockets, but not for tcp. AUTH header has 4byte granular length, which kills all the idea - behind AUTOMATIC 64bit alignment of IPv6. Now we will loose + behind AUTOMATIC 64bit alignment of IPv6. Now we will lose cpu ticks, checking that sender did not something stupid and opt->hdrlen is even. Shit! --ANK (980730) */ diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 3760be8eb..1abc87541 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.21 1999/03/21 05:22:51 davem Exp $ + * $Id: icmp.c,v 1.22 1999/05/19 22:06:39 davem Exp $ * * Based on net/ipv4/icmp.c * @@ -315,6 +315,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, fl.nl_u.ip6_u.daddr = &hdr->saddr; fl.nl_u.ip6_u.saddr = saddr; fl.oif = iif; + fl.fl6_flowlabel = 0; fl.uli_u.icmpt.type = type; fl.uli_u.icmpt.code = code; @@ -388,6 +389,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl.nl_u.ip6_u.daddr = &hdr->saddr; fl.nl_u.ip6_u.saddr = saddr; fl.oif = skb->dev->ifindex; + fl.fl6_flowlabel = 0; fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY; fl.uli_u.icmpt.code = 0; diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c index c19a561e9..a6263d41c 100644 --- a/net/ipv6/ip6_fw.c +++ b/net/ipv6/ip6_fw.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_fw.c,v 1.10 1998/08/26 12:04:57 davem Exp $ + * $Id: ip6_fw.c,v 1.12 1999/06/09 08:29:32 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,6 +16,7 @@ #include <linux/config.h> #include <linux/errno.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/socket.h> #include <linux/sockios.h> #include <linux/net.h> @@ -382,7 +383,7 @@ __initfunc(void ip6_fw_init(void)) } #ifdef MODULE -void module_cleanup(void) +void cleanup_module(void) { #ifdef CONFIG_NETLINK netlink_detach(NETLINK_IP6_FW); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 26ec51c4d..9a635f882 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.17 1999/04/22 10:07:42 davem Exp $ + * $Id: ip6_output.c,v 1.20 1999/06/09 10:11:12 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -20,11 +20,13 @@ * route changes now work. * ip6_forward does not confuse sniffers. * etc. - * + * + * H. von Brand : Added missing #include <linux/string.h> */ #include <linux/errno.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/socket.h> #include <linux/net.h> #include <linux/netdevice.h> @@ -73,19 +75,10 @@ int ip6_output(struct sk_buff *skb) } if (hh) { -#ifdef __alpha__ - /* Alpha has disguisting memcpy. Help it. */ - u64 *aligned_hdr = (u64*)(skb->data - 16); - u64 *aligned_hdr0 = hh->hh_data; - read_lock_irq(&hh->hh_lock); - aligned_hdr[0] = aligned_hdr0[0]; - aligned_hdr[1] = aligned_hdr0[1]; -#else - read_lock_irq(&hh->hh_lock); + read_lock_bh(&hh->hh_lock); memcpy(skb->data - 16, hh->hh_data, 16); -#endif - read_unlock_irq(&hh->hh_lock); - skb_push(skb, dev->hard_header_len); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); return hh->hh_output(skb); } else if (dst->neighbour) return dst->neighbour->output(skb); diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 939d268da..cedf9e691 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: mcast.c,v 1.19 1999/03/25 10:04:50 davem Exp $ + * $Id: mcast.c,v 1.23 1999/06/09 10:11:14 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/socket.h> #include <linux/sockios.h> #include <linux/sched.h> @@ -52,6 +53,11 @@ #define MDBG(x) #endif +/* Big mc list lock for all the devices */ +static rwlock_t ipv6_mc_lock = RW_LOCK_UNLOCKED; +/* Big mc list lock for all the sockets */ +static rwlock_t ipv6_sk_mc_lock = RW_LOCK_UNLOCKED; + static struct socket *igmp6_socket; static void igmp6_join_group(struct ifmcaddr6 *ma); @@ -114,8 +120,10 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr) return err; } + write_lock_bh(&ipv6_sk_mc_lock); mc_lst->next = np->ipv6_mc_list; np->ipv6_mc_list = mc_lst; + write_unlock_bh(&ipv6_sk_mc_lock); return 0; } @@ -128,13 +136,14 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr) struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct ipv6_mc_socklist *mc_lst, **lnk; + write_lock_bh(&ipv6_sk_mc_lock); for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) { if (mc_lst->ifindex == ifindex && ipv6_addr_cmp(&mc_lst->addr, addr) == 0) { struct device *dev; *lnk = mc_lst->next; - synchronize_bh(); + write_unlock_bh(&ipv6_sk_mc_lock); if ((dev = dev_get_by_index(ifindex)) != NULL) ipv6_dev_mc_dec(dev, &mc_lst->addr); @@ -142,6 +151,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr) return 0; } } + write_unlock_bh(&ipv6_sk_mc_lock); return -ENOENT; } @@ -151,15 +161,38 @@ void ipv6_sock_mc_close(struct sock *sk) struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct ipv6_mc_socklist *mc_lst; + write_lock_bh(&ipv6_sk_mc_lock); while ((mc_lst = np->ipv6_mc_list) != NULL) { - struct device *dev = dev_get_by_index(mc_lst->ifindex); + struct device *dev; + + np->ipv6_mc_list = mc_lst->next; + write_unlock_bh(&ipv6_sk_mc_lock); + dev = dev_get_by_index(mc_lst->ifindex); if (dev) ipv6_dev_mc_dec(dev, &mc_lst->addr); - np->ipv6_mc_list = mc_lst->next; sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); + + write_lock_bh(&ipv6_sk_mc_lock); + } + write_unlock_bh(&ipv6_sk_mc_lock); +} + +int inet6_mc_check(struct sock *sk, struct in6_addr *addr) +{ + struct ipv6_mc_socklist *mc; + + read_lock(&ipv6_sk_mc_lock); + for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) { + if (ipv6_addr_cmp(&mc->addr, addr) == 0) { + read_unlock(&ipv6_sk_mc_lock); + return 1; + } } + read_unlock(&ipv6_sk_mc_lock); + + return 0; } static int igmp6_group_added(struct ifmcaddr6 *mc) @@ -209,9 +242,11 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr) hash = ipv6_addr_hash(addr); + write_lock_bh(&ipv6_mc_lock); for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) { atomic_inc(&mc->mca_users); + write_unlock_bh(&ipv6_mc_lock); return 0; } } @@ -222,8 +257,10 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr) mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC); - if (mc == NULL) + if (mc == NULL) { + write_unlock_bh(&ipv6_mc_lock); return -ENOMEM; + } memset(mc, 0, sizeof(struct ifmcaddr6)); mc->mca_timer.function = igmp6_timer_handler; @@ -241,6 +278,8 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr) igmp6_group_added(mc); + write_unlock_bh(&ipv6_mc_lock); + return 0; } @@ -256,7 +295,6 @@ static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma) for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) { if (iter == ma) { *lnk = iter->if_next; - synchronize_bh(); return; } } @@ -273,20 +311,22 @@ int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr) hash = ipv6_addr_hash(addr); + write_lock_bh(&ipv6_mc_lock); for (lnk = &inet6_mcast_lst[hash]; (ma=*lnk) != NULL; lnk = &ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) { if (atomic_dec_and_test(&ma->mca_users)) { igmp6_group_dropped(ma); *lnk = ma->next; - synchronize_bh(); ipv6_mca_remove(dev, ma); kfree(ma); } + write_unlock_bh(&ipv6_mc_lock); return 0; } } + write_unlock_bh(&ipv6_mc_lock); return -ENOENT; } @@ -301,10 +341,14 @@ int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr) hash = ipv6_addr_hash(addr); + read_lock_bh(&ipv6_mc_lock); for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) { - if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0) + if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { + read_unlock_bh(&ipv6_mc_lock); return 1; + } } + read_unlock_bh(&ipv6_mc_lock); return 0; } @@ -363,11 +407,14 @@ int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len) if (idev == NULL) return 0; + read_lock(&ipv6_mc_lock); for (ma = idev->mc_list; ma; ma=ma->if_next) igmp6_group_queried(ma, resptime); + read_unlock(&ipv6_mc_lock); } else { int hash = ipv6_addr_hash(addrp); + read_lock(&ipv6_mc_lock); for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) { if (ma->dev == skb->dev && ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) { @@ -375,6 +422,7 @@ int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len) break; } } + read_unlock(&ipv6_mc_lock); } return 0; @@ -409,6 +457,7 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len) hash = ipv6_addr_hash(addrp); + read_lock(&ipv6_mc_lock); for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) { if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) { if (ma->mca_flags & MAF_TIMER_RUNNING) { @@ -420,6 +469,7 @@ int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len) break; } } + read_unlock(&ipv6_mc_lock); return 0; } @@ -429,9 +479,9 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type) struct sock *sk = igmp6_socket->sk; struct sk_buff *skb; struct icmp6hdr *hdr; - struct inet6_ifaddr *ifp; struct in6_addr *snd_addr; struct in6_addr *addrp; + struct in6_addr addr_buf; struct in6_addr all_routers; int err, len, payload_len, full_len; u8 ra[8] = { IPPROTO_ICMPV6, 0, @@ -460,9 +510,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type) dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len); } - ifp = ipv6_get_lladdr(dev); - - if (ifp == NULL) { + if (ipv6_get_lladdr(dev, &addr_buf)) { #if MCAST_DEBUG >= 1 printk(KERN_DEBUG "igmp6: %s no linklocal address\n", dev->name); @@ -470,7 +518,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type) return; } - ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len); + ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); @@ -481,7 +529,7 @@ void igmp6_send(struct in6_addr *addr, struct device *dev, int type) addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr)); ipv6_addr_copy(addrp, addr); - hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len, + hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); @@ -503,7 +551,6 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK))) return; - start_bh_atomic(); igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT); delay = net_random() % IGMP6_UNSOLICITED_IVAL; @@ -514,7 +561,6 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) add_timer(&ma->mca_timer); ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER; - end_bh_atomic(); } static void igmp6_leave_group(struct ifmcaddr6 *ma) @@ -526,22 +572,22 @@ static void igmp6_leave_group(struct ifmcaddr6 *ma) if ((addr_type & IPV6_ADDR_LINKLOCAL)) return; - start_bh_atomic(); if (ma->mca_flags & MAF_LAST_REPORTER) igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION); if (ma->mca_flags & MAF_TIMER_RUNNING) del_timer(&ma->mca_timer); - end_bh_atomic(); } void igmp6_timer_handler(unsigned long data) { struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data; + read_lock(&ipv6_mc_lock); ma->mca_flags |= MAF_LAST_REPORTER; igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT); ma->mca_flags &= ~MAF_TIMER_RUNNING; + read_unlock(&ipv6_mc_lock); } /* Device going down */ @@ -553,8 +599,10 @@ void ipv6_mc_down(struct inet6_dev *idev) /* Withdraw multicast list */ + read_lock_bh(&ipv6_mc_lock); for (i = idev->mc_list; i; i=i->if_next) igmp6_group_dropped(i); + read_unlock_bh(&ipv6_mc_lock); /* Delete all-nodes address. */ @@ -576,8 +624,10 @@ void ipv6_mc_up(struct inet6_dev *idev) /* Install multicast list, except for all-nodes (already installed) */ + read_lock(&ipv6_mc_lock); for (i = idev->mc_list; i; i=i->if_next) igmp6_group_added(i); + read_unlock(&ipv6_mc_lock); } /* @@ -589,6 +639,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev) int hash; struct ifmcaddr6 *i, **lnk; + write_lock_bh(&ipv6_mc_lock); while ((i = idev->mc_list) != NULL) { idev->mc_list = i->if_next; @@ -597,13 +648,13 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev) for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) { if (*lnk == i) { *lnk = i->next; - synchronize_bh(); break; } } igmp6_group_dropped(i); kfree(i); } + write_unlock_bh(&ipv6_mc_lock); } #ifdef CONFIG_PROC_FS @@ -615,12 +666,14 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset, int len=0; struct device *dev; + read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { struct inet6_dev *idev; if ((idev = ipv6_get_idev(dev)) == NULL) continue; + read_lock_bh(&ipv6_mc_lock); for (im = idev->mc_list; im; im = im->if_next) { int i; @@ -640,13 +693,18 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset, len=0; begin=pos; } - if (pos > offset+length) + if (pos > offset+length) { + read_unlock_bh(&ipv6_mc_lock); goto done; + } } + read_unlock_bh(&ipv6_mc_lock); } *eof = 1; done: + read_unlock(&dev_base_lock); + *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index bb5e08373..d0613056a 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -268,14 +268,21 @@ ndisc_build_ll_hdr(struct sk_buff *skb, struct device *dev, ndisc_mc_map(daddr, ha, dev, 1); h_dest = ha; } else if (neigh) { - h_dest = neigh->ha; + read_lock_bh(&neigh->lock); + if (neigh->nud_state&NUD_VALID) { + memcpy(ha, neigh->ha, dev->addr_len); + h_dest = ha; + } + read_unlock_bh(&neigh->lock); } else { neigh = neigh_lookup(&nd_tbl, daddr, dev); if (neigh) { + read_lock_bh(&neigh->lock); if (neigh->nud_state&NUD_VALID) { memcpy(ha, neigh->ha, dev->addr_len); h_dest = ha; } + read_unlock_bh(&neigh->lock); neigh_release(neigh); } } @@ -362,6 +369,7 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh, struct sock *sk = ndisc_socket->sk; struct sk_buff *skb; struct nd_msg *msg; + struct in6_addr addr_buf; int len; int err; @@ -377,13 +385,8 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh, } if (saddr == NULL) { - struct inet6_ifaddr *ifa; - - /* use link local address */ - ifa = ipv6_get_lladdr(dev); - - if (ifa) - saddr = &ifa->addr; + if (!ipv6_get_lladdr(dev, &addr_buf)) + saddr = &addr_buf; } if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) { @@ -501,13 +504,15 @@ static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb) kfree_skb(skb); } +/* Called with locked neigh: either read or both */ + static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) { struct in6_addr *saddr = NULL; struct in6_addr mcaddr; struct device *dev = neigh->dev; struct in6_addr *target = (struct in6_addr *)&neigh->primary_key; - int probes = neigh->probes; + int probes = atomic_read(&neigh->probes); if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 0)) saddr = &skb->nh.ipv6h->saddr; @@ -774,8 +779,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, struct sock *sk = ndisc_socket->sk; int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); struct sk_buff *buff; - struct inet6_ifaddr *ifp; struct icmp6hdr *icmph; + struct in6_addr saddr_buf; struct in6_addr *addrp; struct device *dev; struct rt6_info *rt; @@ -817,12 +822,10 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, rd_len &= ~0x7; len += rd_len; - ifp = ipv6_get_lladdr(dev); - - if (ifp == NULL) { - ND_PRINTK1("redirect: no link_local addr for dev\n"); - return; - } + if (ipv6_get_lladdr(dev, &saddr_buf)) { + ND_PRINTK1("redirect: no link_local addr for dev\n"); + return; + } buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15, 0, 0, &err); @@ -838,7 +841,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, return; } - ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr, + ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr, IPPROTO_ICMPV6, len); icmph = (struct icmp6hdr *) skb_put(buff, len); @@ -875,7 +878,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, memcpy(opt, skb->nh.ipv6h, rd_len - 8); - icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr, + icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr, len, IPPROTO_ICMPV6, csum_partial((u8 *) icmph, len, 0)); @@ -1034,7 +1037,7 @@ int ndisc_rcv(struct sk_buff *skb, unsigned long len) ifp->idev->dev->name); return 0; } - neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 0); + neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev); if (neigh) { if (neigh->flags & NTF_ROUTER) { @@ -1083,11 +1086,10 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum unsigned long now = jiffies; int i; - neigh_table_lock(&nd_tbl); - for (i = 0; i <= NEIGH_HASHMASK; i++) { struct neighbour *neigh; + read_lock_bh(&nd_tbl.lock); for (neigh = nd_tbl.hash_buckets[i]; neigh; neigh = neigh->next) { int j; @@ -1097,6 +1099,7 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum size += 2; } + read_lock(&neigh->lock); size += sprintf(buffer+len+size, " %02x %02x %02x %02x %08lx %08lx %08x %04x %04x %04x %8s ", i, 128, @@ -1118,19 +1121,22 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dum } else { size += sprintf(buffer+len+size, "000000000000"); } + read_unlock(&neigh->lock); size += sprintf(buffer+len+size, "\n"); len += size; pos += size; if (pos <= offset) len=0; - if (pos >= offset+length) + if (pos >= offset+length) { + read_unlock_bh(&nd_tbl.lock); goto done; + } } + read_unlock_bh(&nd_tbl.lock); } done: - neigh_table_unlock(&nd_tbl); *start = buffer+len-(pos-offset); /* Start of wanted data */ len = pos-offset; /* Start slop */ diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 31f6a2f55..b83bdc34b 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -7,7 +7,7 @@ * PROC file system. This is very similar to the IPv4 version, * except it reports the sockets in the INET6 address family. * - * Version: $Id: proc.c,v 1.9 1998/08/26 12:05:11 davem Exp $ + * Version: $Id: proc.c,v 1.10 1999/05/27 00:38:14 davem Exp $ * * Authors: David S. Miller (davem@caip.rutgers.edu) * @@ -52,7 +52,7 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta /*144 */ pos = 149; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_READ(); sp = pro->sklist_next; while(sp != (struct sock *)pro) { struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp; @@ -72,6 +72,7 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta } destp = ntohs(sp->dport); srcp = ntohs(sp->sport); + if((format == 0) && (sp->state == TCP_TIME_WAIT)) { extern int tcp_tw_death_row_slot; int slot_dist; @@ -85,10 +86,8 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta slot_dist = tcp_tw_death_row_slot - slot_dist; timer_expires = jiffies + (slot_dist * TCP_TWKILL_PERIOD); } else { - timer_active1 = del_timer(&tp->retransmit_timer); - timer_active2 = del_timer(&sp->timer); - if(!timer_active1) tp->retransmit_timer.expires = 0; - if(!timer_active2) sp->timer.expires = 0; + timer_active1 = tp->retransmit_timer.prev != NULL; + timer_active2 = sp->timer.prev != NULL; timer_active = 0; timer_expires = (unsigned) -1; } @@ -128,8 +127,6 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta ((!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0)); - if(timer_active1) add_timer(&tp->retransmit_timer); - if(timer_active2) add_timer(&sp->timer); len += sprintf(buffer+len, "%-148s\n", tmpbuf); if(len >= length) break; @@ -137,7 +134,7 @@ static int get__netinfo6(struct proto *pro, char *buffer, int format, char **sta sp = sp->sklist_next; i++; } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); begin = len - (pos - offset); *start = buffer + begin; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index f82ac33db..70394dc03 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.24 1999/04/22 10:07:45 davem Exp $ + * $Id: raw.c,v 1.26 1999/06/09 10:11:18 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,11 +50,11 @@ static void raw_v6_hash(struct sock *sk) num &= (RAWV6_HTABLE_SIZE - 1); skp = &raw_v6_htable[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); sk->next = *skp; *skp = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void raw_v6_unhash(struct sock *sk) @@ -65,7 +65,7 @@ static void raw_v6_unhash(struct sock *sk) num &= (RAWV6_HTABLE_SIZE - 1); skp = &raw_v6_htable[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -73,7 +73,7 @@ static void raw_v6_unhash(struct sock *sk) } skp = &((*skp)->next); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void raw_v6_rehash(struct sock *sk) @@ -85,7 +85,7 @@ static void raw_v6_rehash(struct sock *sk) num &= (RAWV6_HTABLE_SIZE - 1); skp = &raw_v6_htable[oldnum]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -96,20 +96,9 @@ static void raw_v6_rehash(struct sock *sk) sk->next = raw_v6_htable[num]; raw_v6_htable[num] = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } -static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr) -{ - struct ipv6_mc_socklist *mc; - - for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) { - if (ipv6_addr_cmp(&mc->addr, addr) == 0) - return 1; - } - - return 0; -} /* Grumble... icmp and ip_input want to get at this... */ struct sock *raw_v6_lookup(struct sock *sk, unsigned short num, @@ -631,6 +620,8 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname, static void rawv6_close(struct sock *sk, long timeout) { + bh_lock_sock(sk); + /* See for explanation: raw_close in ipv4/raw.c */ sk->state = TCP_CLOSE; raw_v6_unhash(sk); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index e455b0533..74cf4571b 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: reassembly.c,v 1.11 1998/08/26 12:05:16 davem Exp $ + * $Id: reassembly.c,v 1.13 1999/06/09 08:29:40 davem Exp $ * * Based on: net/ipv4/ip_fragment.c * @@ -19,9 +19,12 @@ * Fixes: * Andi Kleen Make it work with multiple hosts. * More RFC compliance. + * + * Horst von Brand Add missing #include <linux/string.h> */ #include <linux/errno.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/socket.h> #include <linux/sockios.h> #include <linux/sched.h> diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 04b49d843..5f8ff914b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: route.c,v 1.35 1999/03/21 05:22:57 davem Exp $ + * $Id: route.c,v 1.36 1999/06/09 10:11:21 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1607,7 +1607,7 @@ static int fib6_dump_node(struct fib6_walker_t *w) return 0; } -static int fib6_dump_done(struct netlink_callback *cb) +static void fib6_dump_end(struct netlink_callback *cb) { struct fib6_walker_t *w = (void*)cb->args[0]; @@ -1622,6 +1622,11 @@ static int fib6_dump_done(struct netlink_callback *cb) cb->done = (void*)cb->args[1]; cb->args[1] = 0; } +} + +static int fib6_dump_done(struct netlink_callback *cb) +{ + fib6_dump_end(cb); return cb->done(cb); } @@ -1668,11 +1673,15 @@ int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) if (res <= 0 && skb->len == 0) RT6_TRACE("%p>dump end\n", w); #endif + res = res < 0 ? res : skb->len; /* res < 0 is an error. (really, impossible) res == 0 means that dump is complete, but skb still can contain data. res > 0 dump is not complete, but frame is full. */ - return res < 0 ? res : skb->len; + /* Destroy walker, if dump of this table is complete. */ + if (res <= 0) + fib6_dump_end(cb); + return res; } int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f1ef74de8..2164e245e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.104 1999/04/24 00:27:25 davem Exp $ + * $Id: tcp_ipv6.c,v 1.108 1999/06/09 08:29:43 davem Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -67,7 +67,7 @@ static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport, int hashent = (lport ^ fport); hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]); - return (hashent & ((TCP_HTABLE_SIZE/2) - 1)); + return (hashent & ((tcp_ehash_size >> 1) - 1)); } static __inline__ int tcp_v6_sk_hashfn(struct sock *sk) @@ -89,8 +89,8 @@ static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum) struct tcp_bind_bucket *tb; int result = 0; - SOCKHASH_LOCK(); - for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; + SOCKHASH_LOCK_WRITE(); + for(tb = tcp_bhash[tcp_bhashfn(snum)]; (tb && (tb->port != snum)); tb = tb->next) ; @@ -156,7 +156,7 @@ static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum) } } go_like_smoke: - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); return result; } @@ -172,20 +172,20 @@ static void tcp_v6_hash(struct sock *sk) if(sk->state != TCP_CLOSE) { struct sock **skp; - SOCKHASH_LOCK(); - skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; + SOCKHASH_LOCK_WRITE(); + skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; tcp_sk_bindify(sk); - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } } static void tcp_v6_unhash(struct sock *sk) { - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); if(sk->pprev) { if(sk->next) sk->next->pprev = sk->pprev; @@ -194,14 +194,14 @@ static void tcp_v6_unhash(struct sock *sk) tcp_sk_unbindify(sk); tcp_reg_zap(sk); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void tcp_v6_rehash(struct sock *sk) { unsigned char state; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); state = sk->state; if(sk->pprev != NULL) { if(sk->next) @@ -216,7 +216,7 @@ static void tcp_v6_rehash(struct sock *sk) if(state == TCP_LISTEN) skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)]; else - skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; + skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))]; if((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; @@ -225,7 +225,7 @@ static void tcp_v6_rehash(struct sock *sk) if(state == TCP_LISTEN) tcp_sk_bindify(sk); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif) @@ -264,10 +264,10 @@ static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned shor /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM - * It is assumed that this code only gets called from within NET_BH. + * + * The sockhash lock must be held as a reader here. */ -static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, - struct in6_addr *saddr, u16 sport, +static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport, struct in6_addr *daddr, u16 dport, int dif) { @@ -285,7 +285,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, * have wildcards anyways. */ hash = tcp_v6_hashfn(daddr, hnum, saddr, sport); - for(sk = tcp_established_hash[hash]; sk; sk = sk->next) { + for(sk = tcp_ehash[hash]; sk; sk = sk->next) { /* For IPV6 do the cheaper port and family tests first. */ if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) { if (sk->state == TCP_ESTABLISHED) @@ -294,7 +294,7 @@ static inline struct sock *__tcp_v6_lookup(struct tcphdr *th, } } /* Must check for a TIME_WAIT'er before going to listener hash. */ - for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) { + for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next) { if(*((__u32 *)&(sk->dport)) == ports && sk->family == PF_INET6) { struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk; @@ -309,7 +309,13 @@ hit: return sk; } -#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif)) +#define tcp_v6_lookup(sa, sp, da, dp, dif) \ +({ struct sock *___sk; \ + SOCKHASH_LOCK_READ(); \ + ___sk = __tcp_v6_lookup((sa),(sp),(da),(dp),(dif)); \ + SOCKHASH_UNLOCK_READ(); \ + ___sk; \ +}) static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len, struct in6_addr *saddr, @@ -344,24 +350,26 @@ static int tcp_v6_unique_address(struct sock *sk) int retval = 1; /* Freeze the hash while we snoop around. */ - SOCKHASH_LOCK(); - tb = tcp_bound_hash[tcp_bhashfn(snum)]; + SOCKHASH_LOCK_READ(); + tb = tcp_bhash[tcp_bhashfn(snum)]; for(; tb; tb = tb->next) { if(tb->port == snum && tb->owners != NULL) { /* Almost certainly the re-use port case, search the real hashes * so it actually scales. (we hope that all ipv6 ftp servers will * use passive ftp, I just cover this case for completeness) */ - sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr, + sk = __tcp_v6_lookup(&sk->net_pinfo.af_inet6.daddr, sk->dport, &sk->net_pinfo.af_inet6.rcv_saddr, snum, sk->bound_dev_if); + SOCKHASH_UNLOCK_READ(); + if((sk != NULL) && (sk->state != TCP_LISTEN)) retval = 0; - break; + return retval; } } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); return retval; } @@ -551,7 +559,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, failure: dst_release(xchg(&sk->dst_cache, NULL)); - memcpy(&np->daddr, 0, sizeof(struct in6_addr)); + memset(&np->daddr, 0, sizeof(struct in6_addr)); sk->daddr = 0; return err; } @@ -628,11 +636,14 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, if (type == ICMPV6_PKT_TOOBIG) { struct dst_entry *dst = NULL; - if (atomic_read(&sk->sock_readers)) + if (sk->state == TCP_LISTEN) return; - if (sk->state == TCP_LISTEN) + bh_lock_sock(sk); + if(sk->lock.users) { + bh_unlock_sock(sk); return; + } /* icmp should have updated the destination cache entry */ if (sk->dst_cache) @@ -664,7 +675,7 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, tcp_simple_retransmit(sk); } /* else let the usual retransmit timer handle it */ dst_release(dst); - return; + bh_unlock_sock(sk); } icmpv6_err_convert(type, code, &err); @@ -674,11 +685,13 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, struct open_request *req, *prev; struct ipv6hdr hd; case TCP_LISTEN: - if (atomic_read(&sk->sock_readers)) { + bh_lock_sock(sk); + if (sk->lock.users) { net_statistics.LockDroppedIcmps++; /* If too many ICMPs get dropped on busy * servers this needs to be solved differently. */ + bh_unlock_sock(sk); return; } @@ -686,20 +699,22 @@ void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr, ipv6_addr_copy(&hd.saddr, saddr); ipv6_addr_copy(&hd.daddr, daddr); req = tcp_v6_search_req(tp, &hd, th, tcp_v6_iif(skb), &prev); - if (!req) - return; - if (seq != req->snt_isn) { + if (!req || (seq != req->snt_isn)) { net_statistics.OutOfWindowIcmps++; + bh_unlock_sock(sk); return; } if (req->sk) { + bh_unlock_sock(sk); sk = req->sk; /* report error in accept */ } else { tp->syn_backlog--; tcp_synq_unlink(tp, req, prev); req->class->destructor(req); tcp_openreq_free(req); + bh_unlock_sock(sk); } + /* FALL THROUGH */ case TCP_SYN_SENT: case TCP_SYN_RECV: /* Cannot happen */ @@ -1210,12 +1225,20 @@ static inline struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) return sk; } +/* The socket must have it's spinlock held when we get + * here. + * + * We have a potential double-lock case here, so even when + * doing backlog processing we use the BH locking scheme. + * This is because we cannot sleep with the original spinlock + * held. + */ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) { #ifdef CONFIG_FILTER struct sk_filter *filter; #endif - int users = 0; + int users = 0, need_unlock = 0; /* Imagine: socket is IPv6. IPv4 packet arrives, goes to IPv4 receive handler and backlogged. @@ -1286,19 +1309,24 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) * otherwise we just shortcircuit this and continue with * the new socket.. */ - if (atomic_read(&nsk->sock_readers)) { - skb_orphan(skb); - __skb_queue_tail(&nsk->back_log, skb); - return 0; + if(nsk != sk) { + bh_lock_sock(nsk); + if (nsk->lock.users) { + skb_orphan(skb); + sk_add_backlog(nsk, skb); + bh_unlock_sock(nsk); + return 0; + } + need_unlock = 1; + sk = nsk; } - sk = nsk; } if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) goto reset; if (users) goto ipv6_pktoptions; - return 0; + goto out_maybe_unlock; reset: tcp_v6_send_reset(skb); @@ -1306,7 +1334,7 @@ discard: if (users) kfree_skb(skb); kfree_skb(skb); - return 0; + goto out_maybe_unlock; ipv6_pktoptions: /* Do you ask, what is it? @@ -1335,6 +1363,9 @@ ipv6_pktoptions: if (skb) kfree_skb(skb); +out_maybe_unlock: + if (need_unlock) + bh_unlock_sock(sk); return 0; } @@ -1344,6 +1375,7 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len) struct sock *sk; struct in6_addr *saddr = &skb->nh.ipv6h->saddr; struct in6_addr *daddr = &skb->nh.ipv6h->daddr; + int ret; th = skb->h.th; @@ -1383,7 +1415,9 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len) /* CHECKSUM_UNNECESSARY */ }; - sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, tcp_v6_iif(skb)); + SOCKHASH_LOCK_READ_BH(); + sk = __tcp_v6_lookup(saddr, th->source, daddr, th->dest, tcp_v6_iif(skb)); + SOCKHASH_UNLOCK_READ_BH(); if (!sk) goto no_tcp_socket; @@ -1396,11 +1430,15 @@ int tcp_v6_rcv(struct sk_buff *skb, unsigned long len) if(sk->state == TCP_TIME_WAIT) goto do_time_wait; - if (!atomic_read(&sk->sock_readers)) - return tcp_v6_do_rcv(sk, skb); + bh_lock_sock(sk); + ret = 0; + if (!sk->lock.users) + ret = tcp_v6_do_rcv(sk, skb); + else + sk_add_backlog(sk, skb); + bh_unlock_sock(sk); - __skb_queue_tail(&sk->back_log, skb); - return(0); + return ret; no_tcp_socket: tcp_v6_send_reset(skb); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5b4d55f9e..da020d8fb 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,7 +7,7 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.40 1999/05/08 20:00:32 davem Exp $ + * $Id: udp.c,v 1.42 1999/06/09 10:11:24 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,7 +55,7 @@ static int udp_v6_verify_bind(struct sock *sk, unsigned short snum) int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr); int retval = 0, sk_reuse = sk->reuse; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_READ(); for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) { if((sk2->num == snum) && (sk2 != sk)) { unsigned char state = sk2->state; @@ -86,7 +86,7 @@ static int udp_v6_verify_bind(struct sock *sk, unsigned short snum) } } } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_READ(); return retval; } @@ -98,11 +98,11 @@ static void udp_v6_hash(struct sock *sk) num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); sk->next = *skp; *skp = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void udp_v6_unhash(struct sock *sk) @@ -113,7 +113,7 @@ static void udp_v6_unhash(struct sock *sk) num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[num]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -121,7 +121,7 @@ static void udp_v6_unhash(struct sock *sk) } skp = &((*skp)->next); } - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static void udp_v6_rehash(struct sock *sk) @@ -133,7 +133,7 @@ static void udp_v6_rehash(struct sock *sk) num &= (UDP_HTABLE_SIZE - 1); skp = &udp_hash[oldnum]; - SOCKHASH_LOCK(); + SOCKHASH_LOCK_WRITE(); while(*skp != NULL) { if(*skp == sk) { *skp = sk->next; @@ -144,7 +144,7 @@ static void udp_v6_rehash(struct sock *sk) sk->next = udp_hash[num]; udp_hash[num] = sk; sk->hashent = num; - SOCKHASH_UNLOCK(); + SOCKHASH_UNLOCK_WRITE(); } static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport, @@ -154,6 +154,7 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport, unsigned short hnum = ntohs(dport); int badness = -1; + SOCKHASH_LOCK_READ(); for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) { if((sk->num == hnum) && (sk->family == PF_INET6) && @@ -189,6 +190,7 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport, } } } + SOCKHASH_UNLOCK_READ(); return result; } @@ -331,6 +333,8 @@ ipv4_connected: static void udpv6_close(struct sock *sk, long timeout) { + bh_lock_sock(sk); + /* See for explanation: raw_close in ipv4/raw.c */ sk->state = TCP_CLOSE; udp_v6_unhash(sk); @@ -498,18 +502,6 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) return 0; } -static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr) -{ - struct ipv6_mc_socklist *mc; - - for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) { - if (ipv6_addr_cmp(&mc->addr, addr) == 0) - return 1; - } - - return 0; -} - static struct sock *udp_v6_mcast_next(struct sock *sk, u16 loc_port, struct in6_addr *loc_addr, u16 rmt_port, struct in6_addr *rmt_addr, diff --git a/net/irda/Config.in b/net/irda/Config.in index 8912d6cb9..92300cb30 100644 --- a/net/irda/Config.in +++ b/net/irda/Config.in @@ -2,34 +2,31 @@ # IrDA protocol configuration # -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_NET" != "n" ] ; then +if [ "$CONFIG_NET" != "n" ] ; then - mainmenu_option next_comment - comment 'IrDA subsystem support' - dep_tristate 'IrDA subsystem support' CONFIG_IRDA $CONFIG_EXPERIMENTAL $CONFIG_NET + mainmenu_option next_comment + comment 'IrDA subsystem support' + dep_tristate 'IrDA subsystem support' CONFIG_IRDA $CONFIG_NET - if [ "$CONFIG_IRDA" != "n" ] ; then - comment 'IrDA protocols' - source net/irda/irlan/Config.in - source net/irda/ircomm/Config.in - source net/irda/irlpt/Config.in + if [ "$CONFIG_IRDA" != "n" ] ; then + comment 'IrDA protocols' + source net/irda/irlan/Config.in + source net/irda/ircomm/Config.in + source net/irda/irlpt/Config.in - bool 'IrDA protocol options' CONFIG_IRDA_OPTIONS - if [ "$CONFIG_IRDA_OPTIONS" != "n" ] ; then - comment ' IrDA options' - bool ' Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP - bool ' Fast RRs' CONFIG_IRDA_FAST_RR - bool ' Debug information' CONFIG_IRDA_DEBUG - fi + bool 'IrDA protocol options' CONFIG_IRDA_OPTIONS + if [ "$CONFIG_IRDA_OPTIONS" != "n" ] ; then + comment ' IrDA options' + bool ' Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP + bool ' Fast RRs' CONFIG_IRDA_FAST_RR + bool ' Debug information' CONFIG_IRDA_DEBUG fi + fi - if [ "$CONFIG_IRDA" != "n" ] ; then - source net/irda/compressors/Config.in - source drivers/net/irda/Config.in - fi - endmenu - + if [ "$CONFIG_IRDA" != "n" ] ; then + source net/irda/compressors/Config.in + source drivers/net/irda/Config.in fi + endmenu fi diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 6dd118024..3e656e565 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun May 31 10:12:43 1998 - * Modified at: Thu Apr 22 12:08:04 1999 + * Modified at: Wed May 19 16:12:06 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc. * @@ -30,6 +30,7 @@ #include <linux/if_arp.h> #include <linux/net.h> #include <linux/irda.h> +#include <linux/poll.h> #include <asm/uaccess.h> @@ -46,11 +47,12 @@ extern void irda_cleanup(void); extern int irlap_driver_rcv(struct sk_buff *, struct device *, struct packet_type *); -static struct proto_ops irda_proto_ops; +static struct proto_ops irda_stream_ops; +static struct proto_ops irda_dgram_ops; static hashbin_t *cachelog = NULL; -static struct wait_queue *discovery_wait; /* Wait for discovery */ +static DECLARE_WAIT_QUEUE_HEAD(discovery_wait); /* Wait for discovery */ -#define IRDA_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_HEADER) +#define IRDA_MAX_HEADER (TTP_MAX_HEADER) /* * Function irda_data_indication (instance, sap, skb) @@ -121,7 +123,8 @@ static void irda_disconnect_indication(void *instance, void *sap, */ static void irda_connect_confirm(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *skb) + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb) { struct irda_sock *self; struct sock *sk; @@ -130,13 +133,28 @@ static void irda_connect_confirm(void *instance, void *sap, self = (struct irda_sock *) instance; + /* How much header space do we need to reserve */ + self->max_header_size = max_header_size; + + /* IrTTP max SDU size in transmit direction */ self->max_sdu_size_tx = max_sdu_size; + + /* Find out what the largest chunk of data that we can transmit is */ + if (max_sdu_size == SAR_DISABLE) + self->max_data_size = qos->data_size.value - max_header_size; + else + self->max_data_size = max_sdu_size; + + DEBUG(1, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size); + memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); sk = self->sk; if (sk == NULL) return; + skb_queue_tail(&sk->receive_queue, skb); + /* We are now connected! */ sk->state = TCP_ESTABLISHED; sk->state_change(sk); @@ -150,7 +168,7 @@ static void irda_connect_confirm(void *instance, void *sap, */ static void irda_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, - struct sk_buff *skb) + __u8 max_header_size, struct sk_buff *skb) { struct irda_sock *self; struct sock *sk; @@ -158,8 +176,21 @@ static void irda_connect_indication(void *instance, void *sap, DEBUG(1, __FUNCTION__ "()\n"); self = (struct irda_sock *) instance; - - self->max_sdu_size_tx = max_sdu_size; + + /* How much header space do we need to reserve */ + self->max_header_size = max_header_size; + + /* IrTTP max SDU size in transmit direction */ + self->max_sdu_size_tx = max_sdu_size; + + /* Find out what the largest chunk of data that we can transmit is */ + if (max_sdu_size == SAR_DISABLE) + self->max_data_size = qos->data_size.value - max_header_size; + else + self->max_data_size = max_sdu_size; + + DEBUG(1, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size); + memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); sk = self->sk; @@ -187,12 +218,12 @@ void irda_connect_response(struct irda_sock *self) skb = dev_alloc_skb(64); if (skb == NULL) { - DEBUG( 0, __FUNCTION__ "() Could not allocate sk_buff!\n"); + DEBUG(0, __FUNCTION__ "() Unable to allocate sk_buff!\n"); return; } /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(skb, IRDA_MAX_HEADER); irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb); } @@ -219,12 +250,12 @@ static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) switch (flow) { case FLOW_STOP: - DEBUG( 0, __FUNCTION__ "(), IrTTP wants us to slow down\n"); + DEBUG(1, __FUNCTION__ "(), IrTTP wants us to slow down\n"); self->tx_flow = flow; break; case FLOW_START: self->tx_flow = flow; - DEBUG(0, __FUNCTION__ "(), IrTTP wants us to start again\n"); + DEBUG(1, __FUNCTION__ "(), IrTTP wants us to start again\n"); wake_up_interruptible(sk->sleep); break; default: @@ -514,10 +545,13 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags) new->stsap_sel = new->tsap->stsap_sel; new->dtsap_sel = new->tsap->dtsap_sel; new->saddr = irttp_get_saddr(new->tsap); - new->saddr = irttp_get_saddr(new->tsap); + new->daddr = irttp_get_daddr(new->tsap); new->max_sdu_size_tx = self->max_sdu_size_tx; new->max_sdu_size_rx = self->max_sdu_size_rx; + new->max_data_size = self->max_data_size; + new->max_header_size = self->max_header_size; + memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info)); /* Clean up the original one to keep it in listen state */ @@ -669,7 +703,11 @@ static int irda_create(struct socket *sock, int protocol) sock_init_data(sock, sk); - sock->ops = &irda_proto_ops; + if (sock->type == SOCK_STREAM) + sock->ops = &irda_stream_ops; + else + sock->ops = &irda_dgram_ops; + sk->protocol = protocol; /* Register as a client with IrLMP */ @@ -786,12 +824,20 @@ static int irda_sendmsg(struct socket *sock, struct msghdr *msg, int len, return -ENOTCONN; } - skb = sock_alloc_send_skb(sk, len + IRDA_MAX_HEADER, 0, + /* Check that we don't send out to big frames */ + if (len > self->max_data_size) { + DEBUG(0, __FUNCTION__ "(), Warning to much data! " + "Chopping frame from %d to %d bytes!\n", len, + self->max_data_size); + len = self->max_data_size; + } + + skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return -ENOBUFS; - skb_reserve(skb, IRDA_MAX_HEADER); + skb_reserve(skb, self->max_header_size); DEBUG(4, __FUNCTION__ "(), appending user data\n"); asmptr = skb->h.raw = skb_put(skb, len); @@ -815,8 +861,8 @@ static int irda_sendmsg(struct socket *sock, struct msghdr *msg, int len, * Try to receive message and copy it to user * */ -static int irda_recvmsg(struct socket *sock, struct msghdr *msg, int size, - int flags, struct scm_cookie *scm) +static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, + int size, int flags, struct scm_cookie *scm) { struct irda_sock *self; struct sock *sk = sock->sk; @@ -862,6 +908,161 @@ static int irda_recvmsg(struct socket *sock, struct msghdr *msg, int size, } /* + * Function irda_data_wait (sk) + * + * Sleep until data has arrive. But check for races.. + * + */ +static void irda_data_wait(struct sock *sk) +{ + if (!skb_peek(&sk->receive_queue)) { + sk->socket->flags |= SO_WAITDATA; + interruptible_sleep_on(sk->sleep); + sk->socket->flags &= ~SO_WAITDATA; + } +} + +/* + * Function irda_recvmsg_stream (sock, msg, size, flags, scm) + * + * + * + */ +static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, + int size, int flags, struct scm_cookie *scm) +{ + struct irda_sock *self; + struct sock *sk = sock->sk; + int noblock = flags & MSG_DONTWAIT; + int copied = 0; + int target = 1; + + DEBUG(3, __FUNCTION__ "()\n"); + + self = sk->protinfo.irda; + ASSERT(self != NULL, return -1;); + + if (sock->flags & SO_ACCEPTCON) + return(-EINVAL); + + if (flags & MSG_OOB) + return -EOPNOTSUPP; + + if (flags & MSG_WAITALL) + target = size; + + + msg->msg_namelen = 0; + + /* Lock the socket to prevent queue disordering + * while sleeps in memcpy_tomsg + */ +/* down(&self->readsem); */ + + do { + int chunk; + struct sk_buff *skb; + + skb=skb_dequeue(&sk->receive_queue); + if (skb==NULL) { + if (copied >= target) + break; + + /* + * POSIX 1003.1g mandates this order. + */ + + if (sk->err) { + /* up(&self->readsem); */ + return sock_error(sk); + } + + if (sk->shutdown & RCV_SHUTDOWN) + break; + + /* up(&self->readsem); */ + + if (noblock) + return -EAGAIN; + irda_data_wait(sk); + if (signal_pending(current)) + return -ERESTARTSYS; + /* down(&self->readsem); */ + continue; + } + + /* Never glue messages from different writers */ +/* if (check_creds && */ +/* memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0) */ +/* { */ +/* skb_queue_head(&sk->receive_queue, skb); */ +/* break; */ +/* } */ + + chunk = min(skb->len, size); + if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + skb_queue_head(&sk->receive_queue, skb); + if (copied == 0) + copied = -EFAULT; + break; + } + copied += chunk; + size -= chunk; + + /* Copy credentials */ +/* scm->creds = *UNIXCREDS(skb); */ +/* check_creds = 1; */ + + /* Mark read part of skb as used */ + if (!(flags & MSG_PEEK)) { + skb_pull(skb, chunk); + +/* if (UNIXCB(skb).fp) */ +/* unix_detach_fds(scm, skb); */ + + /* put the skb back if we didn't use it up.. */ + if (skb->len) { + DEBUG(1, __FUNCTION__ "(), back on q!\n"); + skb_queue_head(&sk->receive_queue, skb); + break; + } + + kfree_skb(skb); + +/* if (scm->fp) */ +/* break; */ + } else { + DEBUG(0, __FUNCTION__ "() questionable!?\n"); + /* It is questionable, see note in unix_dgram_recvmsg. */ +/* if (UNIXCB(skb).fp) */ +/* scm->fp = scm_fp_dup(UNIXCB(skb).fp); */ + + /* put message back and return */ + skb_queue_head(&sk->receive_queue, skb); + break; + } + } while (size); + + /* + * Check if we have previously stopped IrTTP and we know + * have more free space in our rx_queue. If so tell IrTTP + * to start delivering frames again before our rx_queue gets + * empty + */ + if (self->rx_flow == FLOW_STOP) { + if ((atomic_read(&sk->rmem_alloc) << 2) <= sk->rcvbuf) { + DEBUG(2, __FUNCTION__ "(), Starting IrTTP\n"); + self->rx_flow = FLOW_START; + irttp_flow_request(self->tsap, FLOW_START); + } + } + + /* up(&self->readsem); */ + + return copied; +} + +/* * Function irda_shutdown (sk, how) * * @@ -875,19 +1076,45 @@ static int irda_shutdown( struct socket *sk, int how) return -EOPNOTSUPP; } - /* * Function irda_poll (file, sock, wait) * * * */ -unsigned int irda_poll(struct file *file, struct socket *sock, - struct poll_table_struct *wait) +static unsigned int irda_poll(struct file * file, struct socket *sock, + poll_table *wait) { - DEBUG(0, __FUNCTION__ "()\n"); + struct sock *sk = sock->sk; + unsigned int mask; - return 0; + DEBUG(1, __FUNCTION__ "()\n"); + + poll_wait(file, sk->sleep, wait); + mask = 0; + + /* exceptional events? */ + if (sk->err) + mask |= POLLERR; + if (sk->shutdown & RCV_SHUTDOWN) + mask |= POLLHUP; + + /* readable? */ + if (!skb_queue_empty(&sk->receive_queue)) + mask |= POLLIN | POLLRDNORM; + + /* Connection-based need to check for termination and startup */ + if (sk->type == SOCK_STREAM && sk->state==TCP_CLOSE) + mask |= POLLHUP; + + /* + * we set writable also when the other side has shut down the + * connection. This prevents stuck sockets. + */ + if (sk->sndbuf - (int)atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + + return mask; } /* @@ -900,7 +1127,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - DEBUG(0, __FUNCTION__ "(), cmd=%#x\n", cmd); + DEBUG(4, __FUNCTION__ "(), cmd=%#x\n", cmd); switch (cmd) { case TIOCOUTQ: { @@ -947,6 +1174,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EINVAL; default: + DEBUG(1, __FUNCTION__ "(), doing device ioctl!\n"); return dev_ioctl(cmd, (void *) arg); } @@ -1082,13 +1310,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; break; case IRTTP_MAX_SDU_SIZE: - if (self->max_sdu_size_tx != SAR_DISABLE) - val = self->max_sdu_size_tx; - else - /* SAR is disabled, so use the IrLAP data size - * instead */ - val = self->qos_tx.data_size.value - IRDA_MAX_HEADER; - + val = self->max_data_size; DEBUG(0, __FUNCTION__ "(), getting max_sdu_size = %d\n", val); len = sizeof(int); if (put_user(len, optlen)) @@ -1110,7 +1332,7 @@ static struct net_proto_family irda_family_ops = irda_create }; -static struct proto_ops irda_proto_ops = { +static struct proto_ops irda_stream_ops = { PF_IRDA, sock_no_dup, @@ -1128,7 +1350,28 @@ static struct proto_ops irda_proto_ops = { irda_getsockopt, sock_no_fcntl, irda_sendmsg, - irda_recvmsg + irda_recvmsg_stream +}; + +static struct proto_ops irda_dgram_ops = { + PF_IRDA, + + sock_no_dup, + irda_release, + irda_bind, + irda_connect, + sock_no_socketpair, + irda_accept, + irda_getname, + datagram_poll, + irda_ioctl, + irda_listen, + irda_shutdown, + irda_setsockopt, + irda_getsockopt, + sock_no_fcntl, + irda_sendmsg, + irda_recvmsg_dgram }; /* @@ -1215,7 +1458,7 @@ void irda_proto_cleanup(void) irda_packet_type.type = htons(ETH_P_IRDA); dev_remove_pack(&irda_packet_type); - unregister_netdevice_notifier( &irda_dev_notifier); + unregister_netdevice_notifier(&irda_dev_notifier); sock_unregister(PF_IRDA); irda_cleanup(); diff --git a/net/irda/crc.c b/net/irda/crc.c index 9a6f3021f..b3019d5c2 100644 --- a/net/irda/crc.c +++ b/net/irda/crc.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Sat Dec 12 09:56:35 1998 + * Modified at: Sun May 2 20:28:08 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: ppp.c by Michael Callahan <callahan@maths.ox.ac.uk> * Al Longyear <longyear@netcom.com> @@ -59,7 +59,7 @@ __u16 const irda_crc16_table[256] = unsigned short crc_calc( __u16 fcs, __u8 const *buf, size_t len) { - while ( len--) - fcs = IR_FCS(fcs, *buf++); - return fcs; + while (len--) + fcs = irda_fcs(fcs, *buf++); + return fcs; } diff --git a/net/irda/discovery.c b/net/irda/discovery.c index 22def3a1e..380f1f6a8 100644 --- a/net/irda/discovery.c +++ b/net/irda/discovery.c @@ -6,8 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Apr 6 15:33:50 1999 - * Modified at: Sun Apr 11 00:41:58 1999 + * Modified at: Fri May 28 20:46:38 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> + * Modified at: Fri May 28 3:11 CST 1999 + * Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl> * * Copyright (c) 1999 Dag Brattli, All Rights Reserved. * @@ -28,6 +30,7 @@ * ********************************************************************/ +#include <linux/string.h> #include <linux/socket.h> #include <linux/irda.h> @@ -39,28 +42,51 @@ /* * Function irlmp_add_discovery (cachelog, discovery) * - * - * + * Add a new discovery to the cachelog, and remove any old discoveries + * from the same device */ -void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *discovery) +void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) { - discovery_t *old; + discovery_t *discovery, *node; + unsigned long flags; - DEBUG(4, __FUNCTION__ "()\n"); + spin_lock_irqsave(&irlmp->lock, flags); + + /* + * Remove all discoveries of devices that has previously been + * discovered on the same link with the same name (info), or the + * same daddr. We do this since some devices (mostly PDAs) change + * their device address between every discovery. + */ + discovery = (discovery_t *) hashbin_get_first(cachelog); + while (discovery != NULL ) { + node = discovery; + + /* Be sure to stay one item ahead */ + discovery = (discovery_t *) hashbin_get_next(cachelog); + + if ((node->daddr == new->daddr) || + (strcmp(node->info, new->info) == 0)) + { + /* This discovery is a previous discovery + * from the same device, so just remove it + */ + hashbin_remove(cachelog, node->daddr, NULL); + kfree(node); + } + } - /* Check if we have discovered this device before */ - old = hashbin_remove(cachelog, discovery->daddr, NULL); - if (old) - kfree(old); /* Insert the new and updated version */ - hashbin_insert(cachelog, (QUEUE *) discovery, discovery->daddr, NULL); + hashbin_insert(cachelog, (QUEUE *) new, new->daddr, NULL); + + spin_unlock_irqrestore(&irlmp->lock, flags); } /* * Function irlmp_add_discovery_log (cachelog, log) * - * + * Merge a disovery log into the cachlog. * */ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log) @@ -201,10 +227,12 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int len, discovery = (discovery_t *) hashbin_get_first(cachelog); while ( discovery != NULL) { - len += sprintf( buf+len, " name: %s,", - discovery->info); + len += sprintf(buf+len, "name: %s,", discovery->info); - len += sprintf( buf+len, " hint: "); + len += sprintf(buf+len, " hint: 0x%02x%02x", + discovery->hints.byte[0], + discovery->hints.byte[1]); +#if 0 if ( discovery->hints.byte[0] & HINT_PNP) len += sprintf( buf+len, "PnP Compatible "); if ( discovery->hints.byte[0] & HINT_PDA) @@ -228,14 +256,14 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int len, len += sprintf( buf+len, "IrCOMM "); if ( discovery->hints.byte[1] & HINT_OBEX) len += sprintf( buf+len, "IrOBEX "); - +#endif len += sprintf(buf+len, ", saddr: 0x%08x", discovery->saddr); len += sprintf(buf+len, ", daddr: 0x%08x\n", discovery->daddr); - len += sprintf( buf+len, "\n"); + len += sprintf(buf+len, "\n"); discovery = (discovery_t *) hashbin_get_next(cachelog); } diff --git a/net/irda/ircomm/ircomm_common.c b/net/irda/ircomm/ircomm_common.c index bc8758e1e..5300f5f3c 100644 --- a/net/irda/ircomm/ircomm_common.c +++ b/net/irda/ircomm/ircomm_common.c @@ -8,7 +8,7 @@ * Author: Takahide Higuchi <thiguchi@pluto.dti.ne.jp> * Source: irlpt_event.c * - * Copyright (c) 1998, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>, + * Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -41,21 +41,20 @@ #include <net/irda/ircomm_common.h> -static char *revision_date = "Sun Apr 18 00:40:19 1999"; +static char *revision_date = "Tue May 18 03:11:39 1999"; -static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ); - -static void ircomm_state_discoverywait( struct ircomm_cb *self, IRCOMM_EVENT event, +static void ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb ); +static void ircomm_state_discoverywait(struct ircomm_cb *self, + IRCOMM_EVENT event, + struct sk_buff *skb ); +static void ircomm_state_queryparamwait(struct ircomm_cb *self, + IRCOMM_EVENT event, struct sk_buff *skb ); - -static void ircomm_state_queryparamwait( struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ); - -static void ircomm_state_querylsapwait( struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ); - +static void ircomm_state_querylsapwait(struct ircomm_cb *self, + IRCOMM_EVENT event, + struct sk_buff *skb ); static void ircomm_state_waiti( struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb ); static void ircomm_state_waitr( struct ircomm_cb *self, IRCOMM_EVENT event, @@ -206,15 +205,17 @@ __initfunc(int ircomm_init(void)) ircomm[i]->enq_char = 0x05; ircomm[i]->ack_char = 0x06; - ircomm[i]->max_txbuff_size = COMM_DEFAULT_DATA_SIZE; /* 64 */ - ircomm[i]->maxsdusize = SAR_DISABLE; - ircomm[i]->ctrl_skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); + ircomm[i]->max_header_size = COMM_MAX_HEADER_SIZE; + ircomm[i]->tx_max_sdu_size = COMM_DEFAULT_SDU_SIZE; + ircomm[i]->rx_max_sdu_size = SAR_DISABLE; + ircomm[i]->ctrl_skb = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + + COMM_MAX_HEADER_SIZE); if (ircomm[i]->ctrl_skb == NULL){ DEBUG(0,"ircomm:init_module:alloc_skb failed!\n"); return -ENOMEM; } - skb_reserve(ircomm[i]->ctrl_skb,COMM_HEADER_SIZE); + skb_reserve(ircomm[i]->ctrl_skb,COMM_MAX_HEADER_SIZE); } @@ -226,7 +227,6 @@ __initfunc(int ircomm_init(void)) create_proc_entry("ircomm", 0, proc_irda)->get_info = ircomm_proc_read; #endif /* CONFIG_PROC_FS */ - discovering_instance = NULL; return 0; } @@ -275,51 +275,55 @@ void ircomm_cleanup(void) static int ircomm_accept_data_indication(void *instance, void *sap, struct sk_buff *skb) { - - struct ircomm_cb *self = (struct ircomm_cb *)instance; + struct ircomm_cb *self = (struct ircomm_cb *) instance; - ASSERT( self != NULL, return -1;); - ASSERT( self->magic == IRCOMM_MAGIC, return -1;); - ASSERT( skb != NULL, return -1;); + ASSERT(self != NULL, return -1;); + ASSERT(self->magic == IRCOMM_MAGIC, return -1;); + ASSERT(skb != NULL, return -1;); DEBUG(4,__FUNCTION__"():\n"); - ircomm_do_event( self, TTP_DATA_INDICATION, skb); + ircomm_do_event(self, TTP_DATA_INDICATION, skb); self->rx_packets++; return 0; } static void ircomm_accept_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 maxsdusize, struct sk_buff *skb) + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) { + struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_cb *self = (struct ircomm_cb *)instance; - - ASSERT( self != NULL, return;); - ASSERT( self->magic == IRCOMM_MAGIC, return;); - ASSERT( skb != NULL, return;); - ASSERT( qos != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == IRCOMM_MAGIC, return;); + ASSERT(skb != NULL, return;); + ASSERT(qos != NULL, return;); DEBUG(0,__FUNCTION__"(): got connected!\n"); - if(maxsdusize == SAR_DISABLE) - self->max_txbuff_size = qos->data_size.value; + if (max_sdu_size == SAR_DISABLE) + self->tx_max_sdu_size =(qos->data_size.value - max_header_size + - COMM_HEADER_SIZE); else { - ASSERT(maxsdusize >= COMM_DEFAULT_DATA_SIZE, return;); - self->max_txbuff_size = maxsdusize; /* use fragmentation */ + ASSERT(max_sdu_size >= COMM_DEFAULT_SDU_SIZE, return;); + /* use fragmentation */ + self->tx_max_sdu_size = max_sdu_size - COMM_HEADER_SIZE; } self->qos = qos; - self->null_modem_mode = 0; /* disable null modem emulation */ + self->max_header_size = max_header_size + COMM_HEADER_SIZE; + self->null_modem_mode = 0; /* disable null modem emulation */ - ircomm_do_event( self, TTP_CONNECT_CONFIRM, skb); + ircomm_do_event(self, TTP_CONNECT_CONFIRM, skb); } static void ircomm_accept_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 maxsdusize, - struct sk_buff *skb ) + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) { struct ircomm_cb *self = (struct ircomm_cb *)instance; @@ -330,12 +334,15 @@ static void ircomm_accept_connect_indication(void *instance, void *sap, DEBUG(0,__FUNCTION__"()\n"); - if(maxsdusize == SAR_DISABLE) - self->max_txbuff_size = qos->data_size.value; + if (max_sdu_size == SAR_DISABLE) + self->tx_max_sdu_size =(qos->data_size.value - max_header_size + - COMM_HEADER_SIZE); else - self->max_txbuff_size = maxsdusize; + self->tx_max_sdu_size = max_sdu_size - COMM_HEADER_SIZE; self->qos = qos; + self->max_header_size = max_header_size + COMM_HEADER_SIZE; + ircomm_do_event( self, TTP_CONNECT_INDICATION, skb); /* stop connecting */ @@ -556,7 +563,7 @@ static void issue_connect_request(struct ircomm_cb *self, irttp_connect_request(self->tsap, self->dlsap, self->saddr, self->daddr, - NULL, self->maxsdusize, userdata); + NULL, self->rx_max_sdu_size, userdata); break; default: @@ -588,9 +595,10 @@ static void connect_indication(struct ircomm_cb *self, struct qos_info *qos, /* if( !ircomm_parse_controlchannel( self, data)) */ /* self->servicetype = DEFAULT; TODOD:fix this! TH */ - if(self->notify.connect_indication) + if (self->notify.connect_indication) self->notify.connect_indication(self->notify.instance, self, - qos, 0, skb); + qos, self->tx_max_sdu_size, + self->max_header_size, skb); } #if 0 @@ -602,28 +610,27 @@ static void connect_indication_three_wire_raw(void) #endif -static void connect_confirmation(struct ircomm_cb *self, struct sk_buff *skb) +static void connect_confirm(struct ircomm_cb *self, struct sk_buff *skb) { DEBUG(4 ,__FUNCTION__"()\n"); /* give a connect_confirm to the client */ if( self->notify.connect_confirm ) self->notify.connect_confirm(self->notify.instance, - self, NULL, SAR_DISABLE, skb); + self, NULL, self->tx_max_sdu_size, + self->max_header_size, skb); } static void issue_connect_response(struct ircomm_cb *self, struct sk_buff *skb) { - DEBUG(0,__FUNCTION__"()\n"); if( self->servicetype == THREE_WIRE_RAW){ DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented yet\n"); /* irlmp_connect_rsp(); */ - } else { - irttp_connect_response(self->tsap, self->maxsdusize, skb); - } + } else + irttp_connect_response(self->tsap, self->rx_max_sdu_size, skb); } static void issue_disconnect_request(struct ircomm_cb *self, @@ -642,30 +649,29 @@ static void issue_data_request(struct ircomm_cb *self, { int err; - if(self->servicetype == THREE_WIRE_RAW){ + if (self->servicetype == THREE_WIRE_RAW){ /* irlmp_data_request(self->lmhandle,userdata); */ DEBUG(0,__FUNCTION__"():not implemented!"); return; } DEBUG(4,__FUNCTION__"():sending frame\n"); - err = irttp_data_request(self->tsap , userdata ); - if(err){ + err = irttp_data_request(self->tsap, userdata); + if (err){ printk(KERN_ERR __FUNCTION__":ttp_data_request failed\n"); - if(userdata) + if (userdata) dev_kfree_skb( userdata); } self->tx_packets++; } static void issue_control_request(struct ircomm_cb *self, - struct sk_buff *userdata ) + struct sk_buff *userdata) { int err; DEBUG(4,__FUNCTION__"()\n"); - if(self->servicetype == THREE_WIRE_RAW) - { + if (self->servicetype == THREE_WIRE_RAW) { DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented\n"); } @@ -676,7 +682,7 @@ static void issue_control_request(struct ircomm_cb *self, { printk( __FUNCTION__"():ttp_data_request failed\n"); if(userdata) - dev_kfree_skb( userdata); + dev_kfree_skb(userdata); } else self->tx_controls++; @@ -701,7 +707,7 @@ static void process_data(struct ircomm_cb *self, struct sk_buff *skb ) /* ircomm_parse_control(self, skb, CONTROL_CHANNEL); */ - if(self->notify.data_indication && skb->len) + if (self->notify.data_indication && skb->len) self->notify.data_indication(self->notify.instance, self, skb); } @@ -728,7 +734,7 @@ static void ircomm_do_event( struct ircomm_cb *self, IRCOMM_EVENT event, DEBUG( 4, __FUNCTION__": STATE = %s, EVENT = %s\n", ircommstate[self->state], ircommevent[event]); - (*state[ self->state ]) ( self, event, skb); + (*state[self->state])(self, event, skb); } static void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state) @@ -747,7 +753,7 @@ static void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state) static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb ) { - switch(event){ + switch (event){ case IRCOMM_CONNECT_REQUEST: /* ircomm_next_state(self, COMM_WAITI); */ @@ -779,7 +785,8 @@ static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, /* * ircomm_state_discoverywait */ -static void ircomm_state_discoverywait(struct ircomm_cb *self, IRCOMM_EVENT event, +static void ircomm_state_discoverywait(struct ircomm_cb *self, + IRCOMM_EVENT event, struct sk_buff *skb ) { switch(event){ @@ -817,11 +824,11 @@ static void ircomm_state_discoverywait(struct ircomm_cb *self, IRCOMM_EVENT even * ircomm_state_queryparamwait */ -static void ircomm_state_queryparamwait(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ) +static void ircomm_state_queryparamwait(struct ircomm_cb *self, + IRCOMM_EVENT event, + struct sk_buff *skb) { - switch(event){ - + switch (event) { case TTP_CONNECT_INDICATION: ircomm_next_state(self, COMM_WAITR); @@ -855,10 +862,11 @@ static void ircomm_state_queryparamwait(struct ircomm_cb *self, IRCOMM_EVENT eve * ircomm_state_querylsapwait */ -static void ircomm_state_querylsapwait(struct ircomm_cb *self, IRCOMM_EVENT event, +static void ircomm_state_querylsapwait(struct ircomm_cb *self, + IRCOMM_EVENT event, struct sk_buff *skb ) { - switch(event){ + switch (event) { case TTP_CONNECT_INDICATION: @@ -898,10 +906,10 @@ static void ircomm_state_querylsapwait(struct ircomm_cb *self, IRCOMM_EVENT even static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb ) { - switch(event){ + switch (event) { case TTP_CONNECT_CONFIRM: ircomm_next_state(self, COMM_CONN); - connect_confirmation( self, skb ); + connect_confirm(self, skb ); break; case TTP_DISCONNECT_INDICATION: ircomm_next_state(self, COMM_IDLE); @@ -921,21 +929,18 @@ static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, } } - - /* * ircomm_state_waitr */ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ) + struct sk_buff *skb ) { - - switch(event){ + switch (event) { case IRCOMM_CONNECT_RESPONSE: /* issue_connect_response */ - if(self->servicetype==THREE_WIRE_RAW){ + if (self->servicetype==THREE_WIRE_RAW) { DEBUG(0,__FUNCTION__"():3WIRE_RAW is not implemented\n"); /* irlmp_connect_response(Vpeersap, * ACCEPT,null); @@ -987,7 +992,7 @@ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb ) { - switch(event){ + switch (event) { case TTP_DATA_INDICATION: process_data(self, skb); break; @@ -1033,8 +1038,6 @@ static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, } } - - /* * ---------------------------------------------------------------------- * IrCOMM service interfaces and supporting functions @@ -1042,12 +1045,12 @@ static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, * ---------------------------------------------------------------------- */ -/* - * start_discovering() +/* + * Function start_discovering (self) + * + * Start discovering and enter DISCOVERY_WAIT state * - * start discovering and enter DISCOVERY_WAIT state */ - static void start_discovering(struct ircomm_cb *self) { __u16 hints; @@ -1058,7 +1061,7 @@ static void start_discovering(struct ircomm_cb *self) hints = irlmp_service_to_hint(S_COMM); - DEBUG(0,__FUNCTION__"():start discovering..\n"); + DEBUG(1,__FUNCTION__"():start discovering..\n"); switch (ircomm_cs) { case 0: MOD_INC_USE_COUNT; @@ -1092,19 +1095,26 @@ static void start_discovering(struct ircomm_cb *self) /* * queryias_done(self) * - * called when discovery process got wrong results, completed, or terminated. + * */ +/* + * Function queryias_done (self) + * + * Called when discovery process got wrong results, completed, or + * terminated. + * + */ static void queryias_done(struct ircomm_cb *self) { DEBUG(0, __FUNCTION__"():\n"); - if(self->queryias_lock){ + if (self->queryias_lock){ self->queryias_lock = 0; discovering_instance = NULL; MOD_DEC_USE_COUNT; irlmp_unregister_client(self->ckey); } - if(ircomm_cs != 1) + if (ircomm_cs != 1) irlmp_unregister_service(self->skey); return; } @@ -1120,7 +1130,6 @@ static void query_parameters(struct ircomm_cb *self) ircomm_getvalue_confirm, self ); } - static void query_lsapsel(struct ircomm_cb * self) { DEBUG(0, __FUNCTION__"():querying IAS: Lsapsel...\n"); @@ -1135,13 +1144,13 @@ static void query_lsapsel(struct ircomm_cb * self) } } -/* - * ircomm_connect_request() - * Impl. of this function is differ from one of the reference. - * This functin does discovery as well as sending connect request +/* + * Function ircomm_connect_request (self, servicetype) + * + * Impl. of this function is differ from one of the reference. This + * function does discovery as well as sending connect request + * */ - - void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype) { /* @@ -1153,17 +1162,17 @@ void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype) ASSERT( self->magic == IRCOMM_MAGIC, return;); - DEBUG(0, __FUNCTION__"():sending connect_request...\n"); + DEBUG(1, __FUNCTION__"():sending connect_request...\n"); self->servicetype= servicetype; /* ircomm_control_request(self, SERVICETYPE); */ /*servictype*/ - self->maxsdusize = SAR_DISABLE; - ircomm_do_event( self, IRCOMM_CONNECT_REQUEST, NULL); + self->rx_max_sdu_size = SAR_DISABLE; + ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, NULL); } void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata, - __u32 maxsdusize) + __u32 max_sdu_size) { ASSERT( self != NULL, return;); @@ -1177,20 +1186,20 @@ void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata, * and send it with connect_response */ - if(!userdata){ + if (!userdata){ /* FIXME: check for errors and initialize? DB */ - userdata = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); - ASSERT(userdata != NULL, return;); + userdata = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE); + if (userdata == NULL) + return; - skb_reserve(userdata,COMM_HEADER_SIZE); + skb_reserve(userdata,COMM_MAX_HEADER_SIZE); } /* enable null-modem emulation (i.e. server mode )*/ self->null_modem_mode = 1; - self->maxsdusize = maxsdusize; - if(maxsdusize != SAR_DISABLE) - self->max_txbuff_size = maxsdusize; + self->rx_max_sdu_size = max_sdu_size; + ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata); } @@ -1303,10 +1312,10 @@ static void ircomm_tx_controlchannel(struct ircomm_cb *self ) ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb); self->control_ch_pending = 0; - skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); + skb = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE); ASSERT(skb != NULL, return ;); - skb_reserve(skb,COMM_HEADER_SIZE); + skb_reserve(skb,COMM_MAX_HEADER_SIZE); self->ctrl_skb = skb; } @@ -1341,14 +1350,13 @@ static void append_tuple(struct ircomm_cb *self, __u8 instruction, __u8 pl , self->control_ch_pending = 1; } - - /* - * ircomm_control_request(); - * this function is exported as a request to send some control-channel tuples - * to peer device + * Function ircomm_control_request (self, instruction) + * + * This function is exported as a request to send some control-channel + * tuples * to peer device + * */ - void ircomm_control_request(struct ircomm_cb *self, __u8 instruction) { diff --git a/net/irda/ircomm/irvtd_driver.c b/net/irda/ircomm/irvtd_driver.c index 2df2fdd60..7b1ddf3cb 100644 --- a/net/irda/ircomm/irvtd_driver.c +++ b/net/irda/ircomm/irvtd_driver.c @@ -8,7 +8,7 @@ * Source: serial.c by Linus Torvalds * isdn_tty.c by Fritz Elfert * - * Copyright (c) 1998, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>, + * Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -51,7 +51,7 @@ struct termios *irvtd_termios_locked[COMM_MAX_TTY]; static int irvtd_refcount; struct irvtd_cb **irvtd = NULL; -static char *revision_date = "Sun Apr 18 17:31:53 1999"; +static char *revision_date = "Wed May 26 00:49:11 1999"; /* @@ -83,8 +83,10 @@ static void irvtd_break(struct tty_struct *tty, int break_state); static void irvtd_send_xchar(struct tty_struct *tty, char ch); static void irvtd_wait_until_sent(struct tty_struct *tty, int timeout); -static void irvtd_start_timer( struct irvtd_cb *driver); -static void irvtd_timer_expired(unsigned long data); +static void irvtd_start_tx_timer( struct irvtd_cb *driver, int timeout); +static void irvtd_tx_timer_expired(unsigned long data); +static void irvtd_start_rx_timer( struct irvtd_cb *driver, int timeout); +static void irvtd_rx_timer_expired(unsigned long data); static int line_info(char *buf, struct irvtd_cb *driver); static int irvtd_read_proc(char *buf, char **start, off_t offset, int len, @@ -118,7 +120,7 @@ static void irvtd_write_to_tty( struct irvtd_cb *driver) if(driver->rx_disable) return; - skb = skb_dequeue(&driver->rxbuff); + skb = skb_dequeue(&driver->rxbuff); if(skb == NULL) return; /* there's nothing */ @@ -211,8 +213,13 @@ static void irvtd_write_to_tty( struct irvtd_cb *driver) if(skb_queue_len(&driver->rxbuff)< IRVTD_RX_QUEUE_LOW && driver->ttp_stoprx){ - irttp_flow_request(driver->comm->tsap, FLOW_START); + DEBUG(1, __FUNCTION__"():FLOW_START\n"); + /* + * next 2 lines must follow this order since irttp_flow_request() + * will run its rx queue + */ driver->ttp_stoprx = 0; + irttp_flow_request(driver->comm->tsap, FLOW_START); } if(skb_queue_empty(&driver->rxbuff) && driver->disconnect_pend){ @@ -236,10 +243,14 @@ static int irvtd_receive_data(void *instance, void *sap, struct sk_buff *skb) skb_queue_tail( &driver->rxbuff, skb ); if(skb_queue_len(&driver->rxbuff) > IRVTD_RX_QUEUE_HIGH){ + DEBUG(1, __FUNCTION__"():FLOW_STOP\n"); irttp_flow_request(driver->comm->tsap, FLOW_STOP); driver->ttp_stoprx = 1; } irvtd_write_to_tty(driver); + + if(!skb_queue_empty(&driver->rxbuff)) + irvtd_start_rx_timer(driver,0); return 0; } @@ -255,22 +266,36 @@ static int irvtd_receive_data(void *instance, void *sap, struct sk_buff *skb) */ -static void irvtd_start_timer( struct irvtd_cb *driver) +static void irvtd_start_tx_timer( struct irvtd_cb *driver, int timeout) +{ + ASSERT( driver != NULL, return;); + ASSERT( driver->magic == IRVTD_MAGIC, return;); + + del_timer( &driver->tx_timer); + + driver->tx_timer.data = (unsigned long) driver; + driver->tx_timer.function = &irvtd_tx_timer_expired; + driver->tx_timer.expires = jiffies + timeout; + + add_timer( &driver->tx_timer); +} + +static void irvtd_start_rx_timer( struct irvtd_cb *driver, int timeout) { ASSERT( driver != NULL, return;); ASSERT( driver->magic == IRVTD_MAGIC, return;); - del_timer( &driver->timer); + del_timer( &driver->rx_timer); - driver->timer.data = (unsigned long) driver; - driver->timer.function = &irvtd_timer_expired; - driver->timer.expires = jiffies + (HZ / 5); /* 200msec */ + driver->rx_timer.data = (unsigned long) driver; + driver->rx_timer.function = &irvtd_rx_timer_expired; + driver->rx_timer.expires = jiffies + timeout; - add_timer( &driver->timer); + add_timer( &driver->rx_timer); } -static void irvtd_timer_expired(unsigned long data) +static void irvtd_tx_timer_expired(unsigned long data) { struct irvtd_cb *driver = (struct irvtd_cb *)data; @@ -279,11 +304,26 @@ static void irvtd_timer_expired(unsigned long data) DEBUG(4, __FUNCTION__"()\n"); irvtd_send_data_request(driver); +} - irvtd_write_to_tty(driver); +static void irvtd_rx_timer_expired(unsigned long data) +{ + struct irvtd_cb *driver = (struct irvtd_cb *)data; + + ASSERT(driver != NULL,return;); + ASSERT(driver->magic == IRVTD_MAGIC,return;); + DEBUG(4, __FUNCTION__"()\n"); - /* start our timer again and again */ - irvtd_start_timer(driver); + while(TTY_FLIPBUF_SIZE - driver->tty->flip.count + && !skb_queue_empty(&driver->rxbuff)) + irvtd_write_to_tty(driver); + + DEBUG(1, __FUNCTION__"(): room in flip_buffer = %d\n", + TTY_FLIPBUF_SIZE - driver->tty->flip.count); + + if(!skb_queue_empty(&driver->rxbuff)) + /* handle it later */ + irvtd_start_rx_timer(driver, 1); } @@ -310,21 +350,23 @@ static void irvtd_send_data_request(struct irvtd_cb *driver) } #endif - DEBUG(1, __FUNCTION__"():sending %d octets\n",(int)skb->len ); + DEBUG(1, __FUNCTION__"():len = %d, room = %d\n",(int)skb->len, + skb_tailroom(skb)); driver->icount.tx += skb->len; err = ircomm_data_request(driver->comm, driver->txbuff); if (err){ ASSERT(err == 0,;); - DEBUG(0,"%d chars are lost\n",(int)skb->len); + DEBUG(1,"%d chars are lost\n",(int)skb->len); skb_trim(skb, 0); } /* allocate a new frame */ - skb = driver->txbuff = dev_alloc_skb(driver->comm->max_txbuff_size); + skb = driver->txbuff + = dev_alloc_skb(driver->tx_max_sdu_size + driver->max_header_size); if (skb == NULL){ printk(__FUNCTION__"():alloc_skb failed!\n"); } else { - skb_reserve(skb, COMM_HEADER_SIZE); + skb_reserve(skb, driver->max_header_size); } wake_up_interruptible(&driver->tty->write_wait); @@ -341,20 +383,23 @@ static void irvtd_send_data_request(struct irvtd_cb *driver) *********************************************************************** */ - /* * Function irvtd_connect_confirm (instance, sap, qos, max_sdu_size, skb) * - * ircomm_connect_request which we have send have succeed! + * ircomm_connect_request which we have send, has succeeded! * */ void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *skb) + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb) { struct irvtd_cb *driver = (struct irvtd_cb *)instance; ASSERT(driver != NULL, return;); ASSERT(driver->magic == IRVTD_MAGIC, return;); + + driver->tx_max_sdu_size = max_sdu_size; + driver->max_header_size = max_header_size; /* * set default value */ @@ -364,7 +409,7 @@ void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos, /* * sending initial control parameters here */ - if(driver->comm->servicetype == THREE_WIRE_RAW) + if (driver->comm->servicetype == THREE_WIRE_RAW) return; /* do nothing */ driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS); @@ -376,7 +421,7 @@ void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos, ircomm_control_request(driver->comm, XON_XOFF_CHAR); /* ircomm_control_request(driver->comm, ENQ_ACK_CHAR); */ - switch(driver->comm->servicetype){ + switch (driver->comm->servicetype) { case CENTRONICS: break; @@ -397,17 +442,20 @@ void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos, * */ void irvtd_connect_indication(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *skb) + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb) { - struct irvtd_cb *driver = (struct irvtd_cb *)instance; struct ircomm_cb *comm = (struct ircomm_cb *)sap; + ASSERT(driver != NULL, return;); ASSERT(driver->magic == IRVTD_MAGIC, return;); ASSERT(comm != NULL, return;); ASSERT(comm->magic == IRCOMM_MAGIC, return;); - DEBUG(4,"irvtd_connect_indication:sending connect_response...\n"); + driver->tx_max_sdu_size = max_sdu_size; + driver->max_header_size = max_header_size; + DEBUG(4, __FUNCTION__ "():sending connect_response...\n"); ircomm_connect_response(comm, NULL, SAR_DISABLE ); @@ -416,7 +464,7 @@ void irvtd_connect_indication(void *instance, void *sap, struct qos_info *qos, /* * send initial control parameters */ - if(driver->comm->servicetype == THREE_WIRE_RAW) + if (driver->comm->servicetype == THREE_WIRE_RAW) return; /* do nothing */ driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS); @@ -426,6 +474,7 @@ void irvtd_connect_indication(void *instance, void *sap, struct qos_info *qos, ircomm_control_request(driver->comm, DTELINE_STATE); break; default: + DEBUG(0, __FUNCTION__ "(), not implemented!\n"); } @@ -479,11 +528,12 @@ void irvtd_control_indication(void *instance, void *sap, IRCOMM_CMD cmd) if(cmd == TX_READY){ driver->ttp_stoptx = 0; driver->tty->hw_stopped = driver->cts_stoptx; - irvtd_start_timer( driver); if(driver->cts_stoptx) return; + /* push tx queue so that client can send at least 1 octet */ + irvtd_send_data_request(driver); /* * driver->tty->write_wait will keep asleep if * our txbuff is full. @@ -498,7 +548,7 @@ void irvtd_control_indication(void *instance, void *sap, IRCOMM_CMD cmd) if(cmd == TX_BUSY){ driver->ttp_stoptx = driver->tty->hw_stopped = 1; - del_timer( &driver->timer); + del_timer( &driver->tx_timer); return; } @@ -576,6 +626,7 @@ void irvtd_control_indication(void *instance, void *sap, IRCOMM_CMD cmd) case DATA_RATE: case XON_XOFF_CHAR: case DTELINE_STATE: + case ENQ_ACK_CHAR: /* got this from win95 */ /* (maybe) nothing to do */ break; default: @@ -607,7 +658,7 @@ static int irvtd_block_til_ready(struct tty_struct *tty, struct file * filp, struct irvtd_cb *driver) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait,current); int retval = 0; int do_clocal = 0; @@ -678,7 +729,7 @@ static int irvtd_block_til_ready(struct tty_struct *tty, struct file * filp, driver->blocked_open--; - DEBUG(0, __FUNCTION__"():after blocking\n"); + DEBUG(1, __FUNCTION__"():after blocking\n"); if (retval) return retval; @@ -765,7 +816,7 @@ static int irvtd_startup(struct irvtd_cb *driver) struct notify_t irvtd_notify; /* FIXME: it should not be hard coded */ - __u8 oct_seq[6] = { 0,1,4,1,1,1 }; + __u8 oct_seq[6] = { 0,1,6,1,1,1 }; DEBUG(4,__FUNCTION__"()\n" ); if(driver->flags & ASYNC_INITIALIZED) @@ -776,12 +827,12 @@ static int irvtd_startup(struct irvtd_cb *driver) */ skb_queue_head_init(&driver->rxbuff); - driver->txbuff = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); + driver->txbuff = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE); if (!driver->txbuff){ - DEBUG(0,__FUNCTION__"():alloc_skb failed!\n"); + DEBUG(0,__FUNCTION__"(), alloc_skb failed!\n"); return -ENOMEM; } - skb_reserve(driver->txbuff, COMM_HEADER_SIZE); + skb_reserve(driver->txbuff, COMM_MAX_HEADER_SIZE); irda_notify_init(&irvtd_notify); irvtd_notify.data_indication = irvtd_receive_data; @@ -793,9 +844,8 @@ static int irvtd_startup(struct irvtd_cb *driver) irvtd_notify.instance = driver; driver->comm = ircomm_open_instance(irvtd_notify); - if(!driver->comm){ + if (!driver->comm) return -ENODEV; - } /* @@ -811,22 +861,20 @@ static int irvtd_startup(struct irvtd_cb *driver) driver->flags |= ASYNC_INITIALIZED; - /* - * discover a peer device - * TODO: other servicetype(i.e. 3wire,3wireraw) support - */ - ircomm_connect_request(driver->comm, NINE_WIRE); - - /* - * TODO:we have to initialize control-channel here! - * i.e.set something into RTS,CTS and so on.... - */ - if (driver->tty) clear_bit(TTY_IO_ERROR, &driver->tty->flags); change_speed(driver); - irvtd_start_timer( driver); + + /* + * discover a peer device + */ + if(driver->tty->termios->c_cflag & CRTSCTS) + ircomm_connect_request(driver->comm, NINE_WIRE); + else + ircomm_connect_request(driver->comm, THREE_WIRE); + + /* irvtd_start_timer( driver); */ driver->rx_disable = 0; driver->tx_disable = 1; @@ -989,7 +1037,8 @@ static void irvtd_shutdown(struct irvtd_cb * driver) if (driver->tty) set_bit(TTY_IO_ERROR, &driver->tty->flags); - del_timer( &driver->timer); + del_timer( &driver->tx_timer); + del_timer( &driver->rx_timer); irias_delete_object("IrDA:IrCOMM"); @@ -1144,13 +1193,21 @@ int irvtd_write(struct tty_struct * tty, int from_user, DEBUG(4, __FUNCTION__"()\n"); save_flags(flags); - while(1){ + while(count > 0){ cli(); skb = driver->txbuff; ASSERT(skb != NULL, break;); c = MIN(count, (skb_tailroom(skb))); if (c <= 0) - break; + { + if(!driver->ttp_stoptx) + { + irvtd_send_data_request(driver); + continue; + } + else + break; + } /* write to the frame */ @@ -1164,9 +1221,9 @@ int irvtd_write(struct tty_struct * tty, int from_user, wrote += c; count -= c; buf += c; - irvtd_send_data_request(driver); } restore_flags(flags); + irvtd_send_data_request(driver); return (wrote); } @@ -1199,19 +1256,27 @@ void irvtd_put_char(struct tty_struct *tty, unsigned char ch) DEBUG(4, __FUNCTION__"()\n"); + again: save_flags(flags);cli(); skb = driver->txbuff; ASSERT(skb != NULL,return;); + if(!skb_tailroom(skb)) + { + restore_flags(flags); + irvtd_send_data_request(driver); + goto again; + } ASSERT(skb_tailroom(skb) > 0, return;); - DEBUG(4, "irvtd_put_char(0x%02x) skb_len(%d) MAX(%d):\n", + DEBUG(4, "irvtd_put_char(0x%02x) skb_len(%d) room(%d):\n", (int)ch ,(int)skb->len, - driver->comm->max_txbuff_size - COMM_HEADER_SIZE); + skb_tailroom(skb)); /* append a character */ frame = skb_put(skb,1); frame[0] = ch; restore_flags(flags); + irvtd_start_tx_timer(driver,20); return; } @@ -1635,6 +1700,7 @@ void irvtd_throttle(struct tty_struct *tty){ driver->comm->dte = driver->mcr; ircomm_control_request(driver->comm, DTELINE_STATE ); + DEBUG(1, __FUNCTION__"():FLOW_STOP\n"); irttp_flow_request(driver->comm->tsap, FLOW_STOP); } @@ -1649,6 +1715,7 @@ void irvtd_unthrottle(struct tty_struct *tty){ driver->comm->dte = driver->mcr; ircomm_control_request(driver->comm, DTELINE_STATE ); + DEBUG(1, __FUNCTION__"():FLOW_START\n"); irttp_flow_request(driver->comm->tsap, FLOW_START); } @@ -1859,6 +1926,12 @@ static int line_info(char *buf, struct irvtd_cb *driver) if (driver->msr & MSR_RI) ret += sprintf(buf+ret, "|RI"); + ret += sprintf(buf+ret, "\n"); + ret += sprintf(buf+ret, "rx queue:%d", + skb_queue_len( &driver->rxbuff)); + ret += sprintf(buf+ret, "ttp_stoprx:%s", + driver->ttp_stoprx?"TRUE":"FALSE"); + exit: ret += sprintf(buf+ret, "\n"); return ret; @@ -1930,6 +2003,10 @@ __initfunc(int irvtd_init(void)) irvtd[i]->line = i; irvtd[i]->closing_wait = 10*HZ ; irvtd[i]->close_delay = 5*HZ/10 ; + init_waitqueue_head(&irvtd[i]->open_wait); + init_waitqueue_head(&irvtd[i]->close_wait); + init_waitqueue_head(&irvtd[i]->tx_wait); + init_waitqueue_head(&irvtd[i]->delta_msr_wait); } /* diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index cf9e6ea34..ec7b7233a 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -6,10 +6,12 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Wed Sep 2 20:22:08 1998 - * Modified at: Wed Apr 21 09:48:19 1999 + * Modified at: Tue Jun 1 09:05:13 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> + * Modified at: Fri May 28 3:11 CST 1999 + * Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl> * - * Copyright (c) 1998 Dag Brattli, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,6 +25,7 @@ ********************************************************************/ #include <linux/config.h> +#include <linux/string.h> #include <linux/proc_fs.h> #include <linux/skbuff.h> #include <linux/if.h> @@ -31,6 +34,8 @@ #include <linux/netdevice.h> #include <linux/init.h> #include <linux/tty.h> +#include <linux/kmod.h> +#include <linux/wireless.h> #include <asm/ioctls.h> #include <asm/segment.h> @@ -53,7 +58,8 @@ extern int tekram_init(void); extern int actisys_init(void); extern int girbil_init(void); -hashbin_t *irda_device = NULL; +static hashbin_t *irda_device = NULL; +static hashbin_t *dongles = NULL; /* Netdevice functions */ static int irda_device_net_rebuild_header(struct sk_buff *skb); @@ -61,9 +67,9 @@ static int irda_device_net_hard_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); -static int irda_device_net_set_config( struct device *dev, struct ifmap *map); -static int irda_device_net_change_mtu( struct device *dev, int new_mtu); - +static int irda_device_net_set_config(struct device *dev, struct ifmap *map); +static int irda_device_net_change_mtu(struct device *dev, int new_mtu); +static int irda_device_net_ioctl(struct device *dev, struct ifreq *rq,int cmd); #ifdef CONFIG_PROC_FS int irda_device_proc_read( char *buf, char **start, off_t offset, int len, int unused); @@ -74,8 +80,15 @@ __initfunc(int irda_device_init( void)) { /* Allocate master array */ irda_device = hashbin_new( HB_LOCAL); - if ( irda_device == NULL) { - printk( KERN_WARNING "IrDA: Can't allocate irda_device hashbin!\n"); + if (irda_device == NULL) { + WARNING("IrDA: Can't allocate irda_device hashbin!\n"); + return -ENOMEM; + } + + dongles = hashbin_new(HB_LOCAL); + if (dongles == NULL) { + printk(KERN_WARNING + "IrDA: Can't allocate dongles hashbin!\n"); return -ENOMEM; } @@ -92,6 +105,12 @@ __initfunc(int irda_device_init( void)) #ifdef CONFIG_NSC_FIR pc87108_init(); #endif +#ifdef CONFIG_TOSHIBA_FIR + toshoboe_init(); +#endif +#ifdef CONFIG_SMC_IRCC_FIR + ircc_init(); +#endif #ifdef CONFIG_ESI_DONGLE esi_init(); #endif @@ -104,6 +123,10 @@ __initfunc(int irda_device_init( void)) #ifdef CONFIG_GIRBIL_DONGLE girbil_init(); #endif +#ifdef CONFIG_GIRBIL_DONGLE + litelink_init(); +#endif + return 0; } @@ -113,6 +136,7 @@ void irda_device_cleanup(void) ASSERT(irda_device != NULL, return;); + hashbin_delete(dongles, NULL); hashbin_delete(irda_device, (FREE_FUNC) irda_device_close); } @@ -155,6 +179,8 @@ int irda_device_open(struct irda_device *self, char *name, void *priv) /* Initialize timers */ init_timer(&self->media_busy_timer); + self->lock = SPIN_LOCK_UNLOCKED; + /* A pointer to the low level implementation */ self->priv = priv; @@ -186,7 +212,7 @@ int irda_device_open(struct irda_device *self, char *name, void *priv) /* Open network device */ dev_open(&self->netdev); - MESSAGE("IrDA: Registred device %s\n", self->name); + MESSAGE("IrDA: Registered device %s\n", self->name); irda_device_set_media_busy(self, FALSE); @@ -238,7 +264,7 @@ void __irda_device_close(struct irda_device *self) /* * Function irda_device_close (self) * - * + * Close the device * */ void irda_device_close(struct irda_device *self) @@ -248,6 +274,10 @@ void irda_device_close(struct irda_device *self) ASSERT(self != NULL, return;); ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;); + /* We are not using any dongle anymore! */ + if (self->dongle) + self->dongle->close(self); + /* Stop and remove instance of IrLAP */ if (self->irlap) irlap_close(self->irlap); @@ -289,6 +319,8 @@ void irda_device_set_media_busy(struct irda_device *self, int status) */ static void __irda_device_change_speed(struct irda_device *self, int speed) { + int n = 0; + ASSERT(self != NULL, return;); ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;); @@ -296,18 +328,37 @@ static void __irda_device_change_speed(struct irda_device *self, int speed) * Is is possible to change speed yet? Wait until the last byte * has been transmitted. */ - if (self->wait_until_sent) { - self->wait_until_sent(self); - if (self->change_speed) { - self->change_speed(self, speed); + if (!self->wait_until_sent) { + ERROR("IrDA: wait_until_sent() " + "has not implemented by the IrDA device driver!\n"); + return; + } + + /* Make sure all transmitted data has actually been sent */ + self->wait_until_sent(self); - /* Update the QoS value only */ - self->qos.baud_rate.value = speed; + /* Make sure nobody tries to transmit during the speed change */ + while (irda_lock((void *) &self->netdev.tbusy) == FALSE) { + WARNING(__FUNCTION__ "(), device locked!\n"); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(MSECS_TO_JIFFIES(10)); + + if (n++ > 10) { + WARNING(__FUNCTION__ "(), breaking loop!\n"); + break; } - } else { - printk(KERN_WARNING "wait_until_sent() " - "has not implemented by the IrDA device driver!\n"); } + + if (self->dongle) + self->dongle->change_speed(self, speed); + + if (self->change_speed) { + self->change_speed(self, speed); + + /* Update the QoS value only */ + self->qos.baud_rate.value = speed; + } + self->netdev.tbusy = FALSE; } /* @@ -318,8 +369,6 @@ static void __irda_device_change_speed(struct irda_device *self, int speed) */ inline void irda_device_change_speed(struct irda_device *self, int speed) { - DEBUG(4, __FUNCTION__ "()\n"); - ASSERT(self != NULL, return;); ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;); @@ -330,27 +379,27 @@ inline void irda_device_change_speed(struct irda_device *self, int speed) inline int irda_device_is_media_busy( struct irda_device *self) { - ASSERT( self != NULL, return FALSE;); - ASSERT( self->magic == IRDA_DEVICE_MAGIC, return FALSE;); + ASSERT(self != NULL, return FALSE;); + ASSERT(self->magic == IRDA_DEVICE_MAGIC, return FALSE;); return self->media_busy; } inline int irda_device_is_receiving( struct irda_device *self) { - ASSERT( self != NULL, return FALSE;); - ASSERT( self->magic == IRDA_DEVICE_MAGIC, return FALSE;); + ASSERT(self != NULL, return FALSE;); + ASSERT(self->magic == IRDA_DEVICE_MAGIC, return FALSE;); - if ( self->is_receiving) - return self->is_receiving( self); + if (self->is_receiving) + return self->is_receiving(self); else return FALSE; } -inline struct qos_info *irda_device_get_qos( struct irda_device *self) +inline struct qos_info *irda_device_get_qos(struct irda_device *self) { - ASSERT( self != NULL, return NULL;); - ASSERT( self->magic == IRDA_DEVICE_MAGIC, return NULL;); + ASSERT(self != NULL, return NULL;); + ASSERT(self->magic == IRDA_DEVICE_MAGIC, return NULL;); return &self->qos; } @@ -372,8 +421,6 @@ int irda_device_setup(struct device *dev) { struct irda_device *self; - DEBUG(4, __FUNCTION__ "()\n"); - ASSERT(dev != NULL, return -1;); self = (struct irda_device *) dev->priv; @@ -386,6 +433,7 @@ int irda_device_setup(struct device *dev) dev->set_config = irda_device_net_set_config; dev->change_mtu = irda_device_net_change_mtu; /* dev->hard_header = irda_device_net_hard_header; */ + dev->do_ioctl = irda_device_net_ioctl; dev->hard_header_len = 0; dev->addr_len = 0; @@ -444,6 +492,131 @@ static int irda_device_net_change_mtu( struct device *dev, int new_mtu) return 0; } + +#define SIOCSDONGLE SIOCDEVPRIVATE +static int irda_device_net_ioctl(struct device *dev, /* ioctl device */ + struct ifreq *rq, /* Data passed */ + int cmd) /* Ioctl number */ +{ + unsigned long flags; + int ret = 0; +#ifdef WIRELESS_EXT + struct iwreq *wrq = (struct iwreq *) rq; +#endif + struct irda_device *self; + + DEBUG(4, __FUNCTION__ "()\n"); + + ASSERT(dev != NULL, return -1;); + + self = (struct irda_device *) dev->priv; + + ASSERT(self != NULL, return -1;); + ASSERT(self->magic == IRDA_DEVICE_MAGIC, return -1;); + + DEBUG(0, "%s: ->irda_device_net_ioctl(cmd=0x%X)\n", dev->name, cmd); + + /* Disable interrupts & save flags */ + save_flags(flags); + cli(); + + /* Look what is the request */ + switch (cmd) { +#ifdef WIRELESS_EXT + case SIOCGIWNAME: + /* Get name */ + strcpy(wrq->u.name, self->name); + break; + case SIOCSIWNWID: + /* Set domain */ + if (wrq->u.nwid.on) { + + } break; + case SIOCGIWNWID: + /* Read domain*/ +/* wrq->u.nwid.nwid = domain; */ +/* wrq->u.nwid.on = 1; */ + break; + case SIOCGIWENCODE: + /* Get scramble key */ + /* wrq->u.encoding.code = scramble_key; */ +/* wrq->u.encoding.method = 1; */ + break; + case SIOCSIWENCODE: + /* Set scramble key */ + /* scramble_key = wrq->u.encoding.code; */ + break; + case SIOCGIWRANGE: + /* Basic checking... */ + if(wrq->u.data.pointer != (caddr_t) 0) { + struct iw_range range; + + /* Verify the user buffer */ + ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer, + sizeof(struct iw_range)); + if(ret) + break; + + /* Set the length (useless : its constant...) */ + wrq->u.data.length = sizeof(struct iw_range); + + /* Set information in the range struct */ + range.throughput = 1.6 * 1024 * 1024; /* don't argue on this ! */ + range.min_nwid = 0x0000; + range.max_nwid = 0x01FF; + + range.num_channels = range.num_frequency = 0; + + range.sensitivity = 0x3F; + range.max_qual.qual = 255; + range.max_qual.level = 255; + range.max_qual.noise = 0; + + /* Copy structure to the user buffer */ + copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range)); + } + break; + case SIOCGIWPRIV: + /* Basic checking... */ +#if 0 + if (wrq->u.data.pointer != (caddr_t) 0) { + struct iw_priv_args priv[] = + { /* cmd, set_args, get_args, name */ + { SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, + sizeof(struct site_survey), + "getsitesurvey" }, + }; + + /* Verify the user buffer */ + ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer, + sizeof(priv)); + if (ret) + break; + + /* Set the number of ioctl available */ + wrq->u.data.length = 1; + + /* Copy structure to the user buffer */ + copy_to_user(wrq->u.data.pointer, (u_char *) priv, + sizeof(priv)); + } +#endif + break; +#endif + case SIOCSDONGLE: /* Set dongle */ + /* Initialize dongle */ + irda_device_init_dongle(self, (int) rq->ifr_data); + break; + default: + ret = -EOPNOTSUPP; + } + + restore_flags(flags); + + return ret; +} + /* * Function irda_device_transmit_finished (void) * @@ -451,7 +624,7 @@ static int irda_device_net_change_mtu( struct device *dev, int new_mtu) * device. Maybe we should use: q->q.qlen == 0. * */ -int irda_device_txqueue_empty( struct irda_device *self) +int irda_device_txqueue_empty(struct irda_device *self) { ASSERT(self != NULL, return -1;); ASSERT(self->magic == IRDA_DEVICE_MAGIC, return -1;); @@ -463,6 +636,122 @@ int irda_device_txqueue_empty( struct irda_device *self) } /* + * Function irda_device_init_dongle (self, type) + * + * Initialize attached dongle. Warning, must be called with a process + * context! + */ +void irda_device_init_dongle(struct irda_device *self, int type) +{ + struct dongle_q *node; + + ASSERT(self != NULL, return;); + ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;); + +#ifdef CONFIG_KMOD + /* Try to load the module needed */ + switch (type) { + case ESI_DONGLE: + MESSAGE("IrDA: Initializing ESI dongle!\n"); + request_module("esi"); + break; + case TEKRAM_DONGLE: + MESSAGE("IrDA: Initializing Tekram dongle!\n"); + request_module("tekram"); + break; + case ACTISYS_DONGLE: /* FALLTHROUGH */ + case ACTISYS_PLUS_DONGLE: + MESSAGE("IrDA: Initializing ACTiSYS dongle!\n"); + request_module("actisys"); + break; + case GIRBIL_DONGLE: + MESSAGE("IrDA: Initializing GIrBIL dongle!\n"); + request_module("girbil"); + break; + case LITELINK_DONGLE: + MESSAGE("IrDA: Initializing Litelink dongle!\n"); + request_module("litelink"); + break; + default: + ERROR("Unknown dongle type!\n"); + return; + } +#endif /* CONFIG_KMOD */ + + node = hashbin_find(dongles, type, NULL); + if (!node) { + ERROR("IrDA: Unable to find requested dongle\n"); + return; + } + + /* Check if we're already using a dongle */ + if (self->dongle) { + self->dongle->close(self); + } + + /* Set the dongle to be used by this driver */ + self->dongle = node->dongle; + + /* Now initialize the dongle! */ + node->dongle->open(self, type); + node->dongle->qos_init(self, &self->qos); + + /* Reset dongle */ + node->dongle->reset(self); + + /* Set to default baudrate */ + irda_device_change_speed(self, 9600); +} + +/* + * Function irda_device_register_dongle (dongle) + * + * + * + */ +int irda_device_register_dongle(struct dongle *dongle) +{ + struct dongle_q *new; + + /* Check if this dongle has been registred before */ + if (hashbin_find(dongles, dongle->type, NULL)) { + MESSAGE(__FUNCTION__ "(), Dongle already registered\n"); + return 0; + } + + /* Make new IrDA dongle */ + new = (struct dongle_q *) kmalloc(sizeof(struct dongle_q), GFP_KERNEL); + if (new == NULL) + return -1; + + memset(new, 0, sizeof( struct dongle_q)); + new->dongle = dongle; + + /* Insert IrDA dongle into hashbin */ + hashbin_insert(dongles, (QUEUE *) new, dongle->type, NULL); + + return 0; +} + +/* + * Function irda_device_unregister_dongle (dongle) + * + * + * + */ +void irda_device_unregister_dongle(struct dongle *dongle) +{ + struct dongle_q *node; + + node = hashbin_remove(dongles, dongle->type, NULL); + if (!node) { + ERROR(__FUNCTION__ "(), dongle not found!\n"); + return; + } + kfree(node); +} + +/* * Function setup_dma (idev, buffer, count, mode) * * Setup the DMA channel @@ -536,7 +825,7 @@ int irda_device_proc_read(char *buf, char **start, off_t offset, int len, self = (struct irda_device *) hashbin_get_first(irda_device); while ( self != NULL) { - len += sprintf(buf+len, "%s,", self->name); + len += sprintf(buf+len, "\n%s,", self->name); len += sprintf(buf+len, "\tbinding: %s\n", self->description); diff --git a/net/irda/iriap.c b/net/irda/iriap.c index b87ccbd02..f3b752eb0 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Fri Apr 23 09:57:12 1999 + * Modified at: Sun May 9 15:59:05 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -62,12 +62,17 @@ static __u32 service_handle; extern char *lmp_reasons[]; static struct iriap_cb *iriap_open( __u8 slsap, int mode); -static void __iriap_close( struct iriap_cb *self); +static void __iriap_close(struct iriap_cb *self); static void iriap_disconnect_indication(void *instance, void *sap, LM_REASON reason, struct sk_buff *skb); static void iriap_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, + __u8 max_header_size, struct sk_buff *skb); +static void iriap_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb); static int iriap_data_indication(void *instance, void *sap, struct sk_buff *skb); @@ -181,7 +186,7 @@ struct iriap_cb *iriap_open( __u8 slsap_sel, int mode) self->slsap_sel = slsap_sel; self->mode = mode; - init_timer( &self->watchdog_timer); + init_timer(&self->watchdog_timer); hashbin_insert( iriap, (QUEUE*) self, slsap_sel, NULL); @@ -206,7 +211,7 @@ static void __iriap_close( struct iriap_cb *self) ASSERT( self != NULL, return;); ASSERT( self->magic == IAS_MAGIC, return;); - del_timer( &self->watchdog_timer); + del_timer(&self->watchdog_timer); self->magic = 0; @@ -260,7 +265,7 @@ static void iriap_disconnect_indication( void *instance, void *sap, ASSERT( iriap != NULL, return;); - del_timer( &self->watchdog_timer); + del_timer(&self->watchdog_timer); if ( self->mode == IAS_CLIENT) { DEBUG( 4, __FUNCTION__ "(), disconnect as client\n"); @@ -284,9 +289,8 @@ static void iriap_disconnect_indication( void *instance, void *sap, NULL); } - if ( userdata) { + if (userdata) dev_kfree_skb( userdata); - } } /* @@ -295,28 +299,28 @@ static void iriap_disconnect_indication( void *instance, void *sap, * * */ -void iriap_disconnect_request( struct iriap_cb *self) +void iriap_disconnect_request(struct iriap_cb *self) { struct sk_buff *skb; - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == IAS_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == IAS_MAGIC, return;); - skb = dev_alloc_skb( 64); + skb = dev_alloc_skb(64); if (skb == NULL) { - DEBUG( 0, __FUNCTION__ - "(), Could not allocate an sk_buff of length %d\n", 64); + DEBUG(0, __FUNCTION__ + "(), Could not allocate an sk_buff of length %d\n", 64); return; } /* - * Reserve space for MUX and LAP header + * Reserve space for MUX control and LAP header */ - skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(skb, LMP_MAX_HEADER); - irlmp_disconnect_request( self->lsap, skb); + irlmp_disconnect_request(self->lsap, skb); } void iriap_getinfobasedetails_request(void) @@ -381,7 +385,7 @@ void iriap_getvaluebyclass_request(char *name, char *attr, /* Give ourselves 10 secs to finish this operation */ iriap_start_watchdog_timer(self, 10*HZ); - skb = dev_alloc_skb( 64); + skb = dev_alloc_skb(64); if (!skb) return; @@ -389,7 +393,7 @@ void iriap_getvaluebyclass_request(char *name, char *attr, attr_len = strlen(attr); /* Reserve space for MUX and LAP header */ - skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(skb, self->max_header_size); skb_put(skb, 3+name_len+attr_len); frame = skb->data; @@ -535,13 +539,13 @@ void iriap_getvaluebyclass_response(struct iriap_cb *self, __u16 obj_id, * value. We add 9 bytes because of the 6 bytes for the frame and * max 3 bytes for the value coding. */ - skb = dev_alloc_skb(value->len + LMP_HEADER + LAP_HEADER + 9); + skb = dev_alloc_skb(value->len + self->max_header_size + 9); if (!skb) return; /* Reserve space for MUX and LAP header */ - skb_reserve( skb, LMP_HEADER+LAP_HEADER); - skb_put( skb, 6); + skb_reserve(skb, self->max_header_size); + skb_put(skb, 6); fp = skb->data; @@ -666,7 +670,7 @@ void iriap_getvaluebyclass_indication(struct iriap_cb *self, /* * Function iriap_send_ack (void) * - * + * Currently not used * */ void iriap_send_ack( struct iriap_cb *self) @@ -679,13 +683,13 @@ void iriap_send_ack( struct iriap_cb *self) ASSERT( self != NULL, return;); ASSERT( self->magic == IAS_MAGIC, return;); - skb = dev_alloc_skb( 64); + skb = dev_alloc_skb(64); if (!skb) return; /* Reserve space for MUX and LAP header */ - skb_reserve( skb, 4); - skb_put( skb, 3); + skb_reserve(skb, self->max_header_size); + skb_put(skb, 1); frame = skb->data; /* Build frame */ @@ -698,8 +702,10 @@ void iriap_send_ack( struct iriap_cb *self) * LSAP connection confirmed! * */ -void iriap_connect_confirm(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *userdata) +static void iriap_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, __u8 header_size, + struct sk_buff *userdata) { struct iriap_cb *self; @@ -711,7 +717,7 @@ void iriap_connect_confirm(void *instance, void *sap, struct qos_info *qos, DEBUG(4, __FUNCTION__ "()\n"); - /* del_timer( &self->watchdog_timer); */ + del_timer(&self->watchdog_timer); iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, userdata); } @@ -724,19 +730,17 @@ void iriap_connect_confirm(void *instance, void *sap, struct qos_info *qos, */ static void iriap_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, + __u8 header_size, struct sk_buff *userdata) { struct iriap_cb *self; - DEBUG( 4, __FUNCTION__ "()\n"); - - self = ( struct iriap_cb *) instance; + self = (struct iriap_cb *) instance; - ASSERT( self != NULL, return;); - ASSERT( self->magic == IAS_MAGIC, return;); - ASSERT( self->mode == IAS_SERVER, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == IAS_MAGIC, return;); - iriap_do_server_event( self, IAP_LM_CONNECT_INDICATION, userdata); + iriap_do_server_event(self, IAP_LM_CONNECT_INDICATION, userdata); } /* @@ -856,7 +860,7 @@ void iriap_call_indication( struct iriap_cb *self, struct sk_buff *skb) } opcode &= 0x7f; /* Mask away LST bit */ - switch( opcode) { + switch (opcode) { case GET_INFO_BASE: DEBUG( 0, "IrLMP GetInfoBaseDetails not implemented!\n"); break; diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c index ccba78ece..18a70fec3 100644 --- a/net/irda/iriap_event.c +++ b/net/irda/iriap_event.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Tue Jan 26 12:29:36 1999 + * Modified at: Sun May 9 11:01:47 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. + * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -387,9 +387,9 @@ static void state_r_disconnect( struct iriap_cb *self, IRIAP_EVENT event, } /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve( tx_skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(tx_skb, LMP_MAX_HEADER); - irlmp_connect_response( self->lsap, tx_skb); + irlmp_connect_response(self->lsap, tx_skb); /*LM_Idle_request(idle); */ iriap_next_server_state( self, R_CALL); diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c index f2f8271cf..e231a08d4 100644 --- a/net/irda/irlan/irlan_client.c +++ b/net/irda/irlan/irlan_client.c @@ -6,13 +6,14 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Thu Apr 22 23:03:55 1999 + * Modified at: Mon May 31 14:19:34 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov> * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, + * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -61,6 +62,7 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap, static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, + __u8 max_header_size, struct sk_buff *); static void irlan_check_response_param(struct irlan_cb *self, char *param, char *value, int val_len); @@ -79,7 +81,7 @@ static void irlan_client_kick_timer_expired(unsigned long data) * indication it needs to make progress. If the client is still in * IDLE state, we must kick it to, but only if the provider is not IDLE */ - if ((self->access_type == ACCESS_PEER) && + if ((self->provider.access_type == ACCESS_PEER) && (self->client.state == IRLAN_IDLE) && (self->provider.state != IRLAN_IDLE)) { irlan_client_wakeup(self, self->saddr, self->daddr); @@ -105,23 +107,29 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) { struct irmanager_event mgr_event; - DEBUG(0, __FUNCTION__ "()\n"); + DEBUG(1, __FUNCTION__ "()\n"); ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); - /* Check if we are already awake */ - if (self->client.state != IRLAN_IDLE) + /* + * Check if we are already awake, or if we are a provider in direct + * mode (in that case we must leave the client idle + */ + if ((self->client.state != IRLAN_IDLE) || + (self->provider.access_type == ACCESS_DIRECT)) return; /* saddr may have changed! */ self->saddr = saddr; - /* Check if network device is up */ + /* Before we try to connect, we check if network device is up. If it + * is up, that means that the "user" really wants to connect. If not + * we notify the user about the possibility of an IrLAN connection + */ if (self->dev.start) { /* Open TSAPs */ irlan_client_open_ctrl_tsap(self); - irlan_provider_open_ctrl_tsap(self); irlan_open_data_tsap(self); irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL); @@ -161,7 +169,7 @@ void irlan_client_discovery_indication(discovery_t *discovery) struct irlan_cb *self, *entry; __u32 saddr, daddr; - DEBUG(0, __FUNCTION__"()\n"); + DEBUG(1, __FUNCTION__"()\n"); ASSERT(irlan != NULL, return;); ASSERT(discovery != NULL, return;); @@ -176,7 +184,8 @@ void irlan_client_discovery_indication(discovery_t *discovery) if (self) { ASSERT(self->magic == IRLAN_MAGIC, return;); - DEBUG(2, __FUNCTION__ "(), Found instance!\n"); + DEBUG(1, __FUNCTION__ "(), Found instance (%08x)!\n", + daddr); irlan_client_wakeup(self, saddr, daddr); @@ -184,30 +193,13 @@ void irlan_client_discovery_indication(discovery_t *discovery) } /* - * We have no instance for daddr, so try and find an unused one + * We have no instance for daddr, so start a new one */ - self = hashbin_find(irlan, DEV_ADDR_ANY, NULL); - if (self) { - DEBUG(0, __FUNCTION__ "(), Found instance with DEV_ADDR_ANY!\n"); - /* - * Rehash instance, now we have a client (daddr) to serve. - */ - entry = hashbin_remove(irlan, self->daddr, NULL); - ASSERT(entry == self, return;); + DEBUG(1, __FUNCTION__ "(), starting new instance!\n"); + self = irlan_open(saddr, daddr, TRUE); - self->daddr = daddr; - self->saddr = saddr; - - DEBUG(0, __FUNCTION__ "(), daddr=%08x\n", self->daddr); - hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL); - - /* Check if network device has been registered */ - if (!self->netdev_registered) - irlan_register_netdev(self); - - /* Restart watchdog timer */ - irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); - } + /* Restart watchdog timer */ + irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); } /* @@ -221,7 +213,7 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap, { struct irlan_cb *self; - DEBUG(4, __FUNCTION__ "()\n"); + DEBUG(2, __FUNCTION__ "()\n"); self = (struct irlan_cb *) instance; @@ -231,6 +223,12 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap, irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb); + /* Ready for a new command */ + self->client.tx_busy = FALSE; + + /* Check if we have some queued commands waiting to be sent */ + irlan_run_ctrl_tx_queue(self); + return 0; } @@ -302,6 +300,7 @@ void irlan_client_open_ctrl_tsap(struct irlan_cb *self) static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, + __u8 max_header_size, struct sk_buff *skb) { struct irlan_cb *self; @@ -313,6 +312,9 @@ static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); + self->client.max_sdu_size = max_sdu_size; + self->client.max_header_size = max_header_size; + /* TODO: we could set the MTU depending on the max_sdu_size */ irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL); @@ -339,7 +341,7 @@ void irlan_client_reconnect_data_channel(struct irlan_cb *self) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->max_header_size); skb_put(skb, 2); frame = skb->data; @@ -410,11 +412,11 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) /* For all parameters */ for (i=0; i<count;i++) { ret = irlan_extract_param(ptr, name, value, &val_len); - if (ret == -1) { + if (ret < 0) { DEBUG(2, __FUNCTION__ "(), IrLAN, Error!\n"); break; } - ptr+=ret; + ptr += ret; irlan_check_response_param(self, name, value, val_len); } /* Cleanup */ @@ -423,9 +425,9 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) } /* - * Function check_param (param, value) + * Function irlan_check_response_param (self, param, value, val_len) * - * Check which parameter is received and update local variables + * Check which parameter is received and update local variables * */ static void irlan_check_response_param(struct irlan_cb *self, char *param, @@ -469,11 +471,11 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, } if (strcmp(param, "ACCESS_TYPE") == 0) { if (strcmp(value, "DIRECT") == 0) - self->access_type = ACCESS_DIRECT; + self->client.access_type = ACCESS_DIRECT; else if (strcmp(value, "PEER") == 0) - self->access_type = ACCESS_PEER; + self->client.access_type = ACCESS_PEER; else if (strcmp(value, "HOSTED") == 0) - self->access_type = ACCESS_HOSTED; + self->client.access_type = ACCESS_HOSTED; else { DEBUG(2, __FUNCTION__ "(), unknown access type!\n"); } diff --git a/net/irda/irlan/irlan_client_event.c b/net/irda/irlan/irlan_client_event.c index 1544c093e..2b92e7a74 100644 --- a/net/irda/irlan/irlan_client_event.c +++ b/net/irda/irlan/irlan_client_event.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Thu Apr 22 12:23:22 1999 + * Modified at: Fri May 14 23:08:15 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -97,7 +97,7 @@ static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, ASSERT(self != NULL, return -1;); ASSERT(self->magic == IRLAN_MAGIC, return -1;); - switch(event) { + switch (event) { case IRLAN_DISCOVERY_INDICATION: /* Get some values from peer IAS */ iriap_getvaluebyclass_request( @@ -152,7 +152,7 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); /* Give the client a kick! */ - if ((self->access_type == ACCESS_PEER) && + if ((self->provider.access_type == ACCESS_PEER) && (self->provider.state != IRLAN_IDLE)) irlan_client_wakeup(self, self->saddr, self->daddr); break; @@ -222,7 +222,7 @@ static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, ASSERT(self != NULL, return -1;); - switch(event) { + switch (event) { case IRLAN_DATA_INDICATION: ASSERT(skb != NULL, return -1;); @@ -314,7 +314,7 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, ASSERT(self->dtsap_sel_data != 0, return -1;); /* Check which access type we are dealing with */ - switch(self->access_type) { + switch (self->client.access_type) { case ACCESS_PEER: if (self->provider.state == IRLAN_OPEN) { diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 6a30574ca..d0a77557b 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -6,10 +6,11 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Thu Apr 22 23:13:47 1999 + * Modified at: Mon May 31 14:25:19 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. + * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, + * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -93,19 +94,25 @@ static void __irlan_close(struct irlan_cb *self); static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, __u8 value_byte, __u16 value_short, __u8 *value_array, __u16 value_len); -static void irlan_close_tsaps(struct irlan_cb *self); +void irlan_close_tsaps(struct irlan_cb *self); #ifdef CONFIG_PROC_FS static int irlan_proc_read(char *buf, char **start, off_t offset, int len, int unused); extern struct proc_dir_entry *proc_irda; -#endif +#endif /* CONFIG_PROC_FS */ +/* + * Function irlan_watchdog_timer_expired (data) + * + * + * + */ void irlan_watchdog_timer_expired(unsigned long data) { struct irmanager_event mgr_event; - struct irlan_cb *self, *entry; + struct irlan_cb *self; DEBUG(0, __FUNCTION__ "()\n"); @@ -116,6 +123,7 @@ void irlan_watchdog_timer_expired(unsigned long data) /* Check if device still configured */ if (self->dev.start) { + DEBUG(0, __FUNCTION__ "(), notifying irmanager to stop irlan!\n"); mgr_event.event = EVENT_IRLAN_STOP; sprintf(mgr_event.devname, "%s", self->ifname); irmanager_notify(&mgr_event); @@ -128,22 +136,13 @@ void irlan_watchdog_timer_expired(unsigned long data) */ self->notify_irmanager = FALSE; } else { - DEBUG(0, __FUNCTION__ "(), recycling instance!\n"); + DEBUG(0, __FUNCTION__ "(), closing instance!\n"); if (self->netdev_registered) { DEBUG(0, __FUNCTION__ "(), removing netdev!\n"); unregister_netdev(&self->dev); self->netdev_registered = FALSE; } - - /* Unbind from daddr */ - entry = hashbin_remove(irlan, self->daddr, NULL); - ASSERT(entry == self, return;); - - self->daddr = DEV_ADDR_ANY; - self->saddr = DEV_ADDR_ANY; - - DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr); - hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL); + irlan_close(self); } } @@ -195,12 +194,12 @@ __initfunc(int irlan_init(void)) /* Register with IrLMP as a service */ skey = irlmp_register_service(hints); - /* Start the first IrLAN instance */ + /* Start the master IrLAN instance */ new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE); - irlan_open_data_tsap(new); - irlan_client_open_ctrl_tsap(new); + /* The master will only open its (listen) control TSAP */ irlan_provider_open_ctrl_tsap(new); + new->master = TRUE; /* Do some fast discovery! */ irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); @@ -293,7 +292,7 @@ struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev) self->daddr = daddr; /* Provider access can only be PEER, DIRECT, or HOSTED */ - self->access_type = access; + self->provider.access_type = access; self->media = MEDIA_802_3; self->notify_irmanager = TRUE; @@ -302,7 +301,9 @@ struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev) init_timer(&self->client.kick_timer); hashbin_insert(irlan, (QUEUE *) self, daddr, NULL); - + + skb_queue_head_init(&self->client.txq); + irlan_next_client_state(self, IRLAN_IDLE); irlan_next_provider_state(self, IRLAN_IDLE); @@ -322,7 +323,7 @@ struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev) */ static void __irlan_close(struct irlan_cb *self) { - DEBUG(0, __FUNCTION__ "()\n"); + DEBUG(2, __FUNCTION__ "()\n"); ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -359,8 +360,11 @@ void irlan_close(struct irlan_cb *self) /* Check if device is still configured */ if (self->dev.start) { - DEBUG(2, __FUNCTION__ + DEBUG(0, __FUNCTION__ "(), Device still configured, closing later!\n"); + + /* Give it a chance to reconnect */ + irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); return; } DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr); @@ -371,8 +375,15 @@ void irlan_close(struct irlan_cb *self) __irlan_close(self); } +/* + * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb) + * + * Here we receive the connect indication for the data channel + * + */ void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *skb) + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb) { struct irlan_cb *self; struct tsap_cb *tsap; @@ -386,13 +397,17 @@ void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos, ASSERT(self->magic == IRLAN_MAGIC, return;); ASSERT(tsap == self->tsap_data,return;); - DEBUG(2, "IrLAN, We are now connected!\n"); + self->max_sdu_size = max_sdu_size; + self->max_header_size = max_header_size; + + DEBUG(0, "IrLAN, We are now connected!\n"); + del_timer(&self->watchdog_timer); irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, skb); irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, skb); - if (self->access_type == ACCESS_PEER) { + if (self->provider.access_type == ACCESS_PEER) { /* * Data channel is open, so we are now allowed to * configure the remote filter @@ -400,22 +415,24 @@ void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos, irlan_get_unicast_addr(self); irlan_open_unicast_addr(self); } - /* Ready to transfer Ethernet frames */ + /* Ready to transfer Ethernet frames (at last) */ self->dev.tbusy = 0; } void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *skb) + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb) { struct irlan_cb *self; - DEBUG(2, __FUNCTION__ "()\n"); - self = (struct irlan_cb *) instance; ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); + self->max_sdu_size = max_sdu_size; + self->max_header_size = max_header_size; + /* TODO: we could set the MTU depending on the max_sdu_size */ DEBUG(2, "IrLAN, We are now connected!\n"); @@ -427,9 +444,15 @@ void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, */ irlan_get_unicast_addr(self); irlan_open_unicast_addr(self); + + /* Open broadcast and multicast filter by default */ + irlan_set_broadcast_filter(self, TRUE); + irlan_set_multicast_filter(self, TRUE); /* Ready to transfer Ethernet frames */ self->dev.tbusy = 0; + + irlan_eth_send_gratuitous_arp(&self->dev); } /* @@ -444,7 +467,7 @@ void irlan_disconnect_indication(void *instance, void *sap, LM_REASON reason, struct irlan_cb *self; struct tsap_cb *tsap; - DEBUG(2, __FUNCTION__ "(), reason=%d\n", reason); + DEBUG(0, __FUNCTION__ "(), reason=%d\n", reason); self = (struct irlan_cb *) instance; tsap = (struct tsap_cb *) sap; @@ -460,7 +483,7 @@ void irlan_disconnect_indication(void *instance, void *sap, LM_REASON reason, switch(reason) { case LM_USER_REQUEST: /* User request */ - //irlan_close(self); + irlan_close(self); break; case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); @@ -478,9 +501,6 @@ void irlan_disconnect_indication(void *instance, void *sap, LM_REASON reason, break; } - /* Stop IP from transmitting more packets */ - /* irlan_client_flow_indication(handle, FLOW_STOP, priv); */ - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); } @@ -490,7 +510,7 @@ void irlan_open_data_tsap(struct irlan_cb *self) struct notify_t notify; struct tsap_cb *tsap; - DEBUG(4, __FUNCTION__ "()\n"); + DEBUG(2, __FUNCTION__ "()\n"); ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -500,12 +520,12 @@ void irlan_open_data_tsap(struct irlan_cb *self) return; irda_notify_init(¬ify); - + notify.data_indication = irlan_eth_receive; notify.udata_indication = irlan_eth_receive; notify.connect_indication = irlan_connect_indication; notify.connect_confirm = irlan_connect_confirm; - notify.flow_indication = irlan_eth_flow_indication; + /*notify.flow_indication = irlan_eth_flow_indication;*/ notify.disconnect_indication = irlan_disconnect_indication; notify.instance = self; strncpy(notify.name, "IrLAN data", NOTIFY_MAX_NAME); @@ -538,7 +558,6 @@ void irlan_close_tsaps(struct irlan_cb *self) irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL); irttp_close_tsap(self->tsap_data); self->tsap_data = NULL; - } if (self->client.tsap_ctrl) { irttp_disconnect_request(self->client.tsap_ctrl, NULL, @@ -591,15 +610,60 @@ void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) irias_add_string_attrib(obj, "Name", "Linux"); #endif irias_add_string_attrib(obj, "DeviceID", "HWP19F0"); - irias_add_integer_attrib(obj, "CompCnt", 2); - irias_add_string_attrib(obj, "Comp#01", "PNP8294"); - irias_add_string_attrib(obj, "Comp#02", "PNP8389"); + irias_add_integer_attrib(obj, "CompCnt", 1); + if (self->provider.access_type == ACCESS_PEER) + irias_add_string_attrib(obj, "Comp#02", "PNP8389"); + else + irias_add_string_attrib(obj, "Comp#01", "PNP8294"); + irias_add_string_attrib(obj, "Manufacturer", "Linux-IrDA Project"); irias_insert_object(obj); } } /* + * Function irlan_run_ctrl_tx_queue (self) + * + * Try to send the next command in the control transmit queue + * + */ +int irlan_run_ctrl_tx_queue(struct irlan_cb *self) +{ + struct sk_buff *skb; + + if (irda_lock(&self->client.tx_busy) == FALSE) + return -EBUSY; + + skb = skb_dequeue(&self->client.txq); + if (!skb) { + self->client.tx_busy = FALSE; + return 0; + } + if (self->client.tsap_ctrl == NULL) { + self->client.tx_busy = FALSE; + dev_kfree_skb(skb); + return -1; + } + + return irttp_data_request(self->client.tsap_ctrl, skb); +} + +/* + * Function irlan_ctrl_data_request (self, skb) + * + * This function makes sure that commands on the control channel is being + * sent in a command/response fashion + */ +void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb) +{ + /* Queue command */ + skb_queue_tail(&self->client.txq, skb); + + /* Try to send command */ + irlan_run_ctrl_tx_queue(self); +} + +/* * Function irlan_get_provider_info (self) * * Send Get Provider Information command to peer IrLAN layer @@ -620,7 +684,7 @@ void irlan_get_provider_info(struct irlan_cb *self) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -628,7 +692,8 @@ void irlan_get_provider_info(struct irlan_cb *self) frame[0] = CMD_GET_PROVIDER_INFO; frame[1] = 0x00; /* Zero parameters */ - irttp_data_request(self->client.tsap_ctrl, skb); + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -651,7 +716,7 @@ void irlan_open_data_channel(struct irlan_cb *self) if (!skb) return; - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -666,7 +731,8 @@ void irlan_open_data_channel(struct irlan_cb *self) /* self->use_udata = TRUE; */ - irttp_data_request(self->client.tsap_ctrl, skb); + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } void irlan_close_data_channel(struct irlan_cb *self) @@ -679,11 +745,15 @@ void irlan_close_data_channel(struct irlan_cb *self) ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); + /* Check if the TSAP is still there */ + if (self->client.tsap_ctrl == NULL) + return; + skb = dev_alloc_skb(64); if (!skb) return; - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -694,7 +764,8 @@ void irlan_close_data_channel(struct irlan_cb *self) irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irttp_data_request(self->client.tsap_ctrl, skb); + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -719,7 +790,7 @@ void irlan_open_unicast_addr(struct irlan_cb *self) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->max_header_size); skb_put(skb, 2); frame = skb->data; @@ -730,7 +801,8 @@ void irlan_open_unicast_addr(struct irlan_cb *self) irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); - irttp_data_request(self->client.tsap_ctrl, skb); + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -757,7 +829,7 @@ void irlan_set_broadcast_filter(struct irlan_cb *self, int status) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -770,8 +842,9 @@ void irlan_set_broadcast_filter(struct irlan_cb *self, int status) irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); else irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - - irttp_data_request(self->client.tsap_ctrl, skb); + + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -796,7 +869,7 @@ void irlan_set_multicast_filter(struct irlan_cb *self, int status) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -809,8 +882,9 @@ void irlan_set_multicast_filter(struct irlan_cb *self, int status) irlan_insert_string_param(skb, "FILTER_MODE", "ALL"); else irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - - irttp_data_request(self->client.tsap_ctrl, skb); + + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -836,7 +910,7 @@ void irlan_get_unicast_addr(struct irlan_cb *self) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -847,7 +921,8 @@ void irlan_get_unicast_addr(struct irlan_cb *self) irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); - irttp_data_request(self->client.tsap_ctrl, skb); + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -871,7 +946,7 @@ void irlan_get_media_char(struct irlan_cb *self) return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->client.max_header_size); skb_put(skb, 2); frame = skb->data; @@ -882,7 +957,8 @@ void irlan_get_media_char(struct irlan_cb *self) irlan_insert_string_param(skb, "MEDIA", "802.3"); - irttp_data_request(self->client.tsap_ctrl, skb); + /* irttp_data_request(self->client.tsap_ctrl, skb); */ + irlan_ctrl_data_request(self, skb); } /* @@ -1033,7 +1109,7 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) /* get parameter name */ memcpy(name, buf+n, name_len); - name[ name_len] = '\0'; + name[name_len] = '\0'; n+=name_len; /* @@ -1051,7 +1127,7 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) /* get parameter value */ memcpy(value, buf+n, val_len); - value[ val_len] = '\0'; + value[val_len] = '\0'; n+=val_len; DEBUG(4, "Parameter: %s ", name); @@ -1085,31 +1161,35 @@ static int irlan_proc_read(char *buf, char **start, off_t offset, int len, while (self != NULL) { ASSERT(self->magic == IRLAN_MAGIC, return len;); - len += sprintf(buf+len, "ifname: %s,\n", - self->ifname); - len += sprintf(buf+len, "client state: %s, ", - irlan_state[ self->client.state]); - len += sprintf(buf+len, "provider state: %s,\n", - irlan_state[ self->provider.state]); - len += sprintf(buf+len, "saddr: %#08x, ", - self->saddr); - len += sprintf(buf+len, "daddr: %#08x\n", - self->daddr); - len += sprintf(buf+len, "version: %d.%d,\n", - self->version[1], self->version[0]); - len += sprintf(buf+len, "access type: %s\n", - irlan_access[ self->access_type]); - len += sprintf(buf+len, "media: %s\n", - irlan_media[ self->media]); - - len += sprintf(buf+len, "local filter:\n"); - len += sprintf(buf+len, "remote filter: "); - len += irlan_print_filter(self->client.filter_type, buf+len); - - len += sprintf(buf+len, "tx busy: %s\n", self->dev.tbusy ? - "TRUE" : "FALSE"); - - len += sprintf(buf+len, "\n"); + /* Don't display the master server */ + if (self->master == 0) { + len += sprintf(buf+len, "ifname: %s,\n", + self->ifname); + len += sprintf(buf+len, "client state: %s, ", + irlan_state[ self->client.state]); + len += sprintf(buf+len, "provider state: %s,\n", + irlan_state[ self->provider.state]); + len += sprintf(buf+len, "saddr: %#08x, ", + self->saddr); + len += sprintf(buf+len, "daddr: %#08x\n", + self->daddr); + len += sprintf(buf+len, "version: %d.%d,\n", + self->version[1], self->version[0]); + len += sprintf(buf+len, "access type: %s\n", + irlan_access[self->client.access_type]); + len += sprintf(buf+len, "media: %s\n", + irlan_media[self->media]); + + len += sprintf(buf+len, "local filter:\n"); + len += sprintf(buf+len, "remote filter: "); + len += irlan_print_filter(self->client.filter_type, + buf+len); + + len += sprintf(buf+len, "tx busy: %s\n", + self->dev.tbusy ? "TRUE" : "FALSE"); + + len += sprintf(buf+len, "\n"); + } self = (struct irlan_cb *) hashbin_get_next(irlan); } @@ -1132,34 +1212,34 @@ void print_ret_code(__u8 code) printk(KERN_INFO "Success\n"); break; case 1: - printk(KERN_WARNING "Insufficient resources\n"); + WARNING("IrLAN: Insufficient resources\n"); break; case 2: - printk(KERN_WARNING "Invalid command format\n"); + WARNING("IrLAN: Invalid command format\n"); break; case 3: - printk(KERN_WARNING "Command not supported\n"); + WARNING("IrLAN: Command not supported\n"); break; case 4: - printk(KERN_WARNING "Parameter not supported\n"); + WARNING("IrLAN: Parameter not supported\n"); break; case 5: - printk(KERN_WARNING "Value not supported\n"); + WARNING("IrLAN: Value not supported\n"); break; case 6: - printk(KERN_WARNING "Not open\n"); + WARNING("IrLAN: Not open\n"); break; case 7: - printk(KERN_WARNING "Authentication required\n"); + WARNING("IrLAN: Authentication required\n"); break; case 8: - printk(KERN_WARNING "Invalid password\n"); + WARNING("IrLAN: Invalid password\n"); break; case 9: - printk(KERN_WARNING "Protocol error\n"); + WARNING("IrLAN: Protocol error\n"); break; case 255: - printk(KERN_WARNING "Asynchronous status\n"); + WARNING("IrLAN: Asynchronous status\n"); break; } } diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index c1965a117..c2e2453db 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -6,13 +6,13 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Thu Oct 15 08:37:58 1998 - * Modified at: Thu Apr 22 14:26:39 1999 + * Modified at: Mon May 31 19:57:08 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov> * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> * - * Copyright (c) 1998 Dag Brattli, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -29,6 +29,7 @@ #include <linux/etherdevice.h> #include <linux/inetdevice.h> #include <linux/if_arp.h> +#include <linux/random.h> #include <net/arp.h> #include <net/irda/irda.h> @@ -49,7 +50,7 @@ int irlan_eth_init(struct device *dev) struct irmanager_event mgr_event; struct irlan_cb *self; - DEBUG(0, __FUNCTION__"()\n"); + DEBUG(2, __FUNCTION__"()\n"); ASSERT(dev != NULL, return -1;); @@ -65,21 +66,26 @@ int irlan_eth_init(struct device *dev) ether_setup(dev); - dev->tx_queue_len = TTP_MAX_QUEUE; - -#if 0 - /* - * OK, since we are emulating an IrLAN sever we will have to give - * ourself an ethernet address! - * FIXME: this must be more dynamically + /* + * Lets do all queueing in IrTTP instead of this device driver. + * Queueing here as well can introduce some strange latency + * problems, which we will avoid by setting the queue size to 0. */ - dev->dev_addr[0] = 0x40; - dev->dev_addr[1] = 0x00; - dev->dev_addr[2] = 0x00; - dev->dev_addr[3] = 0x00; - dev->dev_addr[4] = 0x23; - dev->dev_addr[5] = 0x45; -#endif + dev->tx_queue_len = 0; + + if (self->provider.access_type == ACCESS_DIRECT) { + /* + * Since we are emulating an IrLAN sever we will have to + * give ourself an ethernet address! + */ + dev->dev_addr[0] = 0x40; + dev->dev_addr[1] = 0x00; + dev->dev_addr[2] = 0x00; + dev->dev_addr[3] = 0x00; + get_random_bytes(dev->dev_addr+4, 1); + get_random_bytes(dev->dev_addr+5, 1); + } + /* * Network device has now been registered, so tell irmanager about * it, so it can be configured with network parameters @@ -109,7 +115,7 @@ int irlan_eth_open(struct device *dev) { struct irlan_cb *self; - DEBUG(0, __FUNCTION__ "()\n"); + DEBUG(2, __FUNCTION__ "()\n"); ASSERT(dev != NULL, return -1;); @@ -144,7 +150,7 @@ int irlan_eth_close(struct device *dev) { struct irlan_cb *self = (struct irlan_cb *) dev->priv; - DEBUG(0, __FUNCTION__ "()\n"); + DEBUG(2, __FUNCTION__ "()\n"); /* Stop device */ dev->tbusy = 1; @@ -179,76 +185,58 @@ int irlan_eth_close(struct device *dev) int irlan_eth_xmit(struct sk_buff *skb, struct device *dev) { struct irlan_cb *self; + int ret; - DEBUG(4, __FUNCTION__ "()\n"); - self = (struct irlan_cb *) dev->priv; ASSERT(self != NULL, return 0;); ASSERT(self->magic == IRLAN_MAGIC, return 0;); - /* Lock transmit buffer */ - if (irda_lock((void *) &dev->tbusy) == FALSE) { - /* - * If we get here, some higher level has decided we are broken. - * There should really be a "kick me" function call instead. - */ - int tickssofar = jiffies - dev->trans_start; - - if (tickssofar < 5) - return -EBUSY; - - dev->tbusy = 0; - dev->trans_start = jiffies; - } + /* Check if IrTTP can accept more frames */ + if (dev->tbusy) + return -EBUSY; - DEBUG(4, "Room left at head: %d\n", skb_headroom(skb)); - DEBUG(4, "Room left at tail: %d\n", skb_tailroom(skb)); - DEBUG(4, "Required room: %d\n", IRLAN_MAX_HEADER); - - /* skb headroom large enough to contain IR-headers? */ - if ((skb_headroom(skb) < IRLAN_MAX_HEADER) || (skb_shared(skb))) { + /* skb headroom large enough to contain all IrDA-headers? */ + if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) { struct sk_buff *new_skb = - skb_realloc_headroom(skb, IRLAN_MAX_HEADER); - ASSERT(new_skb != NULL, return 0;); - ASSERT(skb_headroom(new_skb) >= IRLAN_MAX_HEADER, return 0;); + skb_realloc_headroom(skb, self->max_header_size); - /* Free original skb, and use the new one */ + /* We have to free the original skb anyway */ dev_kfree_skb(skb); + + /* Did the realloc succeed? */ + if (new_skb == NULL) + return 0; + + /* Use the new skb instead */ skb = new_skb; } dev->trans_start = jiffies; - self->stats.tx_packets++; - self->stats.tx_bytes += skb->len; - /* - * Now queue the packet in the transport layer - * FIXME: clean up the code below! DB - */ - if (self->use_udata) { - irttp_udata_request(self->tsap_data, skb); - dev->tbusy = 0; - - return 0; - } - - if (irttp_data_request(self->tsap_data, skb) == -1) { - /* - * IrTTPs tx queue is full, so we just have to drop the - * frame! You might think that we should just return -1 - * and don't deallocate the frame, but that is dangerous - * since it's possible that we have replaced the original - * skb with a new one with larger headroom, and that would - * really confuse do_dev_queue_xmit() in dev.c! I have - * tried :-) DB + /* Now queue the packet in the transport layer */ + if (self->use_udata) + ret = irttp_udata_request(self->tsap_data, skb); + else + ret = irttp_data_request(self->tsap_data, skb); + + if (ret < 0) { + /* + * IrTTPs tx queue is full, so we just have to + * drop the frame! You might think that we should + * just return -1 and don't deallocate the frame, + * but that is dangerous since it's possible that + * we have replaced the original skb with a new + * one with larger headroom, and that would really + * confuse do_dev_queue_xmit() in dev.c! I have + * tried :-) DB */ dev_kfree_skb(skb); - ++self->stats.tx_dropped; - - return 0; + self->stats.tx_dropped++; + } else { + self->stats.tx_packets++; + self->stats.tx_bytes += skb->len; } - dev->tbusy = 0; /* Finished! */ return 0; } @@ -282,11 +270,11 @@ int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) skb->dev = &self->dev; skb->protocol=eth_type_trans(skb, skb->dev); /* Remove eth header */ - netif_rx(skb); /* Eat it! */ - self->stats.rx_packets++; self->stats.rx_bytes += skb->len; + netif_rx(skb); /* Eat it! */ + return 0; } @@ -301,8 +289,6 @@ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) struct irlan_cb *self; struct device *dev; - DEBUG(4, __FUNCTION__ "()\n"); - self = (struct irlan_cb *) instance; ASSERT(self != NULL, return;); @@ -314,26 +300,16 @@ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) switch (flow) { case FLOW_STOP: - DEBUG(4, "IrLAN, stopping Ethernet layer\n"); - dev->tbusy = 1; break; case FLOW_START: - /* - * Tell upper layers that its time to transmit frames again - */ - DEBUG(4, "IrLAN, starting Ethernet layer\n"); - + default: + /* Tell upper layers that its time to transmit frames again */ dev->tbusy = 0; - /* - * Ready to receive more frames, so schedule the network - * layer - */ + /* Schedule network layer */ mark_bh(NET_BH); break; - default: - DEBUG(0, __FUNCTION__ "(), Unknown flow command!\n"); } } @@ -360,7 +336,7 @@ void irlan_eth_rebuild_header(void *buff, struct device *dev, * Send gratuitous ARP to announce that we have changed * hardware address, so that all peers updates their ARP tables */ -void irlan_etc_send_gratuitous_arp(struct device *dev) +void irlan_eth_send_gratuitous_arp(struct device *dev) { struct in_device *in_dev; @@ -373,7 +349,7 @@ void irlan_etc_send_gratuitous_arp(struct device *dev) in_dev = dev->ip_ptr; arp_send(ARPOP_REQUEST, ETH_P_ARP, in_dev->ifa_list->ifa_address, - &dev, + dev, in_dev->ifa_list->ifa_address, NULL, dev->dev_addr, NULL); } @@ -391,16 +367,21 @@ void irlan_eth_set_multicast_list(struct device *dev) self = dev->priv; - DEBUG(0, __FUNCTION__ "()\n"); - return; + DEBUG(2, __FUNCTION__ "()\n"); + ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); - if (dev->flags&IFF_PROMISC) { - /* Enable promiscuous mode */ - DEBUG(0, "Promiscous mode not implemented\n"); - /* outw(MULTICAST|PROMISC, ioaddr); */ + /* Check if data channel has been connected yet */ + if (self->client.state != IRLAN_DATA) { + DEBUG(1, __FUNCTION__ "(), delaying!\n"); + return; } + + if (dev->flags & IFF_PROMISC) { + /* Enable promiscuous mode */ + WARNING("Promiscous mode not implemented by IrLAN!\n"); + } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS) { /* Disable promiscuous mode, use normal mode. */ DEBUG(4, __FUNCTION__ "(), Setting multicast filter\n"); @@ -420,13 +401,10 @@ void irlan_eth_set_multicast_list(struct device *dev) irlan_set_multicast_filter(self, FALSE); } - if (dev->flags & IFF_BROADCAST) { - DEBUG(4, __FUNCTION__ "(), Setting broadcast filter\n"); + if (dev->flags & IFF_BROADCAST) irlan_set_broadcast_filter(self, TRUE); - } else { - DEBUG(4, __FUNCTION__ "(), Clearing broadcast filter\n"); + else irlan_set_broadcast_filter(self, FALSE); - } } /* diff --git a/net/irda/irlan/irlan_event.c b/net/irda/irlan/irlan_event.c index d54e7cc12..93d7c4efe 100644 --- a/net/irda/irlan/irlan_event.c +++ b/net/irda/irlan/irlan_event.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Oct 20 09:10:16 1998 - * Modified at: Wed Feb 3 21:42:27 1999 + * Modified at: Sun May 9 21:17:44 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -38,22 +38,22 @@ char *irlan_state[] = { "IRLAN_SYNC", }; -void irlan_next_client_state( struct irlan_cb *self, IRLAN_STATE state) +void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) { DEBUG(2, __FUNCTION__"(), %s\n", irlan_state[state]); - ASSERT( self != NULL, return;); - ASSERT( self->magic == IRLAN_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == IRLAN_MAGIC, return;); self->client.state = state; } -void irlan_next_provider_state( struct irlan_cb *self, IRLAN_STATE state) +void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state) { DEBUG(2, __FUNCTION__"(), %s\n", irlan_state[state]); - ASSERT( self != NULL, return;); - ASSERT( self->magic == IRLAN_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == IRLAN_MAGIC, return;); self->provider.state = state; } diff --git a/net/irda/irlan/irlan_filter.c b/net/irda/irlan/irlan_filter.c index c4c1079dd..ec7178db4 100644 --- a/net/irda/irlan/irlan_filter.c +++ b/net/irda/irlan/irlan_filter.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Fri Jan 29 11:16:38 1999 - * Modified at: Thu Feb 25 15:10:54 1999 + * Modified at: Fri May 14 23:11:01 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,6 +23,7 @@ ********************************************************************/ #include <linux/skbuff.h> +#include <linux/random.h> #include <net/irda/irlan_common.h> @@ -41,29 +42,29 @@ void handle_filter_request(struct irlan_cb *self, struct sk_buff *skb) (self->provider.filter_operation == DYNAMIC)) { DEBUG(0, "Giving peer a dynamic Ethernet address\n"); - self->provider.mac_address[0] = 0x40; self->provider.mac_address[1] = 0x00; self->provider.mac_address[2] = 0x00; self->provider.mac_address[3] = 0x00; /* Use arbitration value to generate MAC address */ - if (self->access_type == ACCESS_PEER) { + if (self->provider.access_type == ACCESS_PEER) { self->provider.mac_address[4] = self->provider.send_arb_val & 0xff; self->provider.mac_address[5] = (self->provider.send_arb_val >> 8) & 0xff;; } else { /* Just generate something for now */ - self->provider.mac_address[4] = jiffies & 0xff; - self->provider.mac_address[5] = (jiffies >> 8) & 0xff; + get_random_bytes(self->provider.mac_address+4, 1); + get_random_bytes(self->provider.mac_address+5, 1); } skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x03; irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001); - irlan_insert_array_param(skb, "FILTER_ENTRY", self->provider.mac_address, 6); + irlan_insert_array_param(skb, "FILTER_ENTRY", + self->provider.mac_address, 6); return; } @@ -138,8 +139,7 @@ void handle_filter_request(struct irlan_cb *self, struct sk_buff *skb) * Check parameters in request from peer device * */ -void irlan_check_command_param(struct irlan_cb *self, char *param, - char *value) +void irlan_check_command_param(struct irlan_cb *self, char *param, char *value) { __u8 *bytes; @@ -210,6 +210,12 @@ void irlan_check_command_param(struct irlan_cb *self, char *param, } } +/* + * Function irlan_print_filter (filter_type, buf) + * + * Print status of filter. Used by /proc file system + * + */ int irlan_print_filter(int filter_type, char *buf) { int len = 0; diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c index 8e2c3c25a..947141872 100644 --- a/net/irda/irlan/irlan_provider.c +++ b/net/irda/irlan/irlan_provider.c @@ -6,13 +6,14 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Thu Apr 22 14:28:52 1999 + * Modified at: Sun May 9 12:22:56 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov> * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, + * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -31,6 +32,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/init.h> +#include <linux/random.h> #include <asm/system.h> #include <asm/bitops.h> @@ -50,14 +52,20 @@ #include <net/irda/irlan_filter.h> #include <net/irda/irlan_client.h> +static void irlan_provider_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb); + /* * Function irlan_provider_control_data_indication (handle, skb) * * This function gets the data that is received on the control channel * */ -int irlan_provider_data_indication(void *instance, void *sap, - struct sk_buff *skb) +static int irlan_provider_data_indication(void *instance, void *sap, + struct sk_buff *skb) { struct irlan_cb *self; __u8 code; @@ -111,14 +119,17 @@ int irlan_provider_data_indication(void *instance, void *sap, * Got connection from peer IrLAN layer * */ -void irlan_provider_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, struct sk_buff *skb) +static void irlan_provider_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) { - struct irlan_cb *self, *entry, *new; + struct irlan_cb *self, *new; struct tsap_cb *tsap; + __u32 saddr, daddr; - DEBUG(2, __FUNCTION__ "()\n"); + DEBUG(0, __FUNCTION__ "()\n"); self = (struct irlan_cb *) instance; tsap = (struct tsap_cb *) sap; @@ -126,34 +137,69 @@ void irlan_provider_connect_indication(void *instance, void *sap, ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); + self->provider.max_sdu_size = max_sdu_size; + self->provider.max_header_size = max_header_size; + ASSERT(tsap == self->provider.tsap_ctrl,return;); ASSERT(self->provider.state == IRLAN_IDLE, return;); - /* Check if this provider is currently unused */ - if (self->daddr == DEV_ADDR_ANY) { - /* - * Rehash instance, now we have a client (daddr) to serve. - */ - entry = hashbin_remove(irlan, self->daddr, NULL); - ASSERT( entry == self, return;); - - self->daddr = irttp_get_daddr(tsap); - DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr); - hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL); + daddr = irttp_get_daddr(tsap); + saddr = irttp_get_saddr(tsap); + + /* Check if we already dealing with this client or peer */ + new = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL); + if (new) { + ASSERT(new->magic == IRLAN_MAGIC, return;); + DEBUG(0, __FUNCTION__ "(), found instance!\n"); + + /* Update saddr, since client may have moved to a new link */ + new->saddr = saddr; + DEBUG(2, __FUNCTION__ "(), saddr=%08x\n", new->saddr); + + /* Make sure that any old provider control TSAP is removed */ + if ((new != self) && new->provider.tsap_ctrl) { + irttp_disconnect_request(new->provider.tsap_ctrl, + NULL, P_NORMAL); + irttp_close_tsap(new->provider.tsap_ctrl); + new->provider.tsap_ctrl = NULL; + } } else { - /* - * If we already have the daddr set, this means that the - * client must already have started (peer mode). We must - * make sure that this connection attempt is from the same - * device as the client is dealing with! + /* This must be the master instance, so start a new instance */ + DEBUG(0, __FUNCTION__ "(), starting new provider!\n"); + + new = irlan_open(saddr, daddr, TRUE); + } + + /* + * Check if the connection came in on the master server, or the + * slave server. If it came on the slave, then everything is + * really, OK (reconnect), if not we need to dup the connection and + * hand it over to the slave. + */ + if (new != self) { + + /* Now attach up the new "socket" */ + new->provider.tsap_ctrl = irttp_dup(self->provider.tsap_ctrl, + new); + if (!new->provider.tsap_ctrl) { + DEBUG(0, __FUNCTION__ "(), dup failed!\n"); + return; + } + + /* new->stsap_sel = new->tsap->stsap_sel; */ + new->dtsap_sel_ctrl = new->provider.tsap_ctrl->dtsap_sel; + + /* Clean up the original one to keep it in listen state */ + self->provider.tsap_ctrl->dtsap_sel = LSAP_ANY; + self->provider.tsap_ctrl->lsap->dlsap_sel = LSAP_ANY; + self->provider.tsap_ctrl->lsap->lsap_state = LSAP_DISCONNECTED; + + /* + * Use the new instance from here instead of the master + * struct! */ - ASSERT(self->daddr == irttp_get_daddr(tsap), return;); + self = new; } - - /* Update saddr, since client may have moved to a new link */ - self->saddr = irttp_get_saddr(tsap); - DEBUG(2, __FUNCTION__ "(), saddr=%08x\n", self->saddr); - /* Check if network device has been registered */ if (!self->netdev_registered) irlan_register_netdev(self); @@ -165,9 +211,10 @@ void irlan_provider_connect_indication(void *instance, void *sap, * indication it needs to make progress. If the client is still in * IDLE state, we must kick it to */ - if ((self->access_type == ACCESS_PEER) && - (self->client.state == IRLAN_IDLE)) + if ((self->provider.access_type == ACCESS_PEER) && + (self->client.state == IRLAN_IDLE)) { irlan_client_wakeup(self, self->saddr, self->daddr); + } } /* @@ -225,6 +272,9 @@ int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb) ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb); + /* Open data channel */ + irlan_open_data_tsap(self); + return ret; } @@ -314,7 +364,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, return; /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER); + skb_reserve(skb, self->provider.max_header_size); skb_put(skb, 2); switch (command) { @@ -334,6 +384,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, } irlan_insert_short_param(skb, "IRLAN_VER", 0x0101); break; + case CMD_GET_MEDIA_CHAR: skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x05; /* 5 parameters */ @@ -341,7 +392,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); - switch(self->access_type) { + switch (self->provider.access_type) { case ACCESS_DIRECT: irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); break; diff --git a/net/irda/irlan/irlan_provider_event.c b/net/irda/irlan/irlan_provider_event.c index 6bdf503f1..29e660fa7 100644 --- a/net/irda/irlan/irlan_provider_event.c +++ b/net/irda/irlan/irlan_provider_event.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Thu Apr 22 10:46:28 1999 + * Modified at: Fri May 7 10:53:58 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -108,7 +108,7 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, switch(event) { case IRLAN_GET_INFO_CMD: /* Be sure to use 802.3 in case of peer mode */ - if (self->access_type == ACCESS_PEER) { + if (self->provider.access_type == ACCESS_PEER) { self->media = MEDIA_802_3; /* Check if client has started yet */ @@ -129,7 +129,7 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, break; case IRLAN_OPEN_DATA_CMD: ret = irlan_parse_open_data_cmd(self, skb); - if (self->access_type == ACCESS_PEER) { + if (self->provider.access_type == ACCESS_PEER) { /* FIXME: make use of random functions! */ self->provider.send_arb_val = (jiffies & 0xffff); } @@ -205,8 +205,6 @@ static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - struct irmanager_event mgr_event; - DEBUG(4, __FUNCTION__ "()\n"); ASSERT(self != NULL, return -1;); @@ -220,10 +218,6 @@ static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, break; case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ case IRLAN_LAP_DISCONNECT: - mgr_event.event = EVENT_IRLAN_STOP; - sprintf(mgr_event.devname, "%s", self->ifname); - irmanager_notify(&mgr_event); - irlan_next_provider_state(self, IRLAN_IDLE); break; default: diff --git a/net/irda/irlap.c b/net/irda/irlap.c index d24923652..245884cf7 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -1,26 +1,31 @@ /********************************************************************* * * Filename: irlap.c - * Version: 0.9 - * Description: An IrDA LAP driver for Linux - * Status: Stable. + * Version: 1.0 + * Description: IrLAP implementation for Linux + * Status: Stable * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Fri Apr 23 10:12:29 1999 + * Modified at: Mon May 31 21:43:55 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, - * All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * - * This program is free software; you can redistribute iyt and/or + * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * ********************************************************************/ #include <linux/config.h> @@ -60,23 +65,23 @@ static char *lap_reasons[] = { }; #ifdef CONFIG_PROC_FS -int irlap_proc_read( char *, char **, off_t, int, int); +int irlap_proc_read(char *, char **, off_t, int, int); #endif /* CONFIG_PROC_FS */ -__initfunc(int irlap_init( void)) +__initfunc(int irlap_init(void)) { /* Allocate master array */ - irlap = hashbin_new( HB_LOCAL); - if ( irlap == NULL) { - printk( KERN_WARNING "IrLAP: Can't allocate irlap hashbin!\n"); + irlap = hashbin_new(HB_LOCAL); + if (irlap == NULL) { + printk(KERN_WARNING "IrLAP: Can't allocate irlap hashbin!\n"); return -ENOMEM; } #ifdef CONFIG_IRDA_COMPRESSION - irlap_compressors = hashbin_new( HB_LOCAL); - if ( irlap_compressors == NULL) { - printk( KERN_WARNING "IrLAP: Can't allocate compressors hashbin!\n"); + irlap_compressors = hashbin_new(HB_LOCAL); + if (irlap_compressors == NULL) { + printk(KERN_WARNING "IrLAP: Can't allocate compressors hashbin!\n"); return -ENOMEM; } #endif @@ -86,12 +91,12 @@ __initfunc(int irlap_init( void)) void irlap_cleanup(void) { - ASSERT( irlap != NULL, return;); + ASSERT(irlap != NULL, return;); - hashbin_delete( irlap, (FREE_FUNC) __irlap_close); + hashbin_delete(irlap, (FREE_FUNC) __irlap_close); #ifdef CONFIG_IRDA_COMPRESSION - hashbin_delete( irlap_compressors, (FREE_FUNC) kfree); + hashbin_delete(irlap_compressors, (FREE_FUNC) kfree); #endif } @@ -101,32 +106,32 @@ void irlap_cleanup(void) * Initialize IrLAP layer * */ -struct irlap_cb *irlap_open( struct irda_device *irdev) +struct irlap_cb *irlap_open(struct irda_device *irdev) { struct irlap_cb *self; - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( irdev != NULL, return NULL;); - ASSERT( irdev->magic == IRDA_DEVICE_MAGIC, return NULL;); + ASSERT(irdev != NULL, return NULL;); + ASSERT(irdev->magic == IRDA_DEVICE_MAGIC, return NULL;); /* Initialize the irlap structure. */ - self = kmalloc( sizeof( struct irlap_cb), GFP_KERNEL); - if ( self == NULL) + self = kmalloc(sizeof(struct irlap_cb), GFP_KERNEL); + if (self == NULL) return NULL; - memset( self, 0, sizeof(struct irlap_cb)); + memset(self, 0, sizeof(struct irlap_cb)); self->magic = LAP_MAGIC; /* Make a binding between the layers */ self->irdev = irdev; self->netdev = &irdev->netdev; - irlap_next_state( self, LAP_OFFLINE); + irlap_next_state(self, LAP_OFFLINE); /* Initialize transmitt queue */ - skb_queue_head_init( &self->tx_list); - skb_queue_head_init( &self->wx_list); + skb_queue_head_init(&self->tx_list); + skb_queue_head_init(&self->wx_list); /* My unique IrLAP device address! */ get_random_bytes(&self->saddr, sizeof(self->saddr)); @@ -140,21 +145,21 @@ struct irlap_cb *irlap_open( struct irda_device *irdev) self->caddr &= 0xfe; } - init_timer( &self->slot_timer); - init_timer( &self->query_timer); - init_timer( &self->discovery_timer); - init_timer( &self->final_timer); - init_timer( &self->poll_timer); - init_timer( &self->wd_timer); - init_timer( &self->backoff_timer); + init_timer(&self->slot_timer); + init_timer(&self->query_timer); + init_timer(&self->discovery_timer); + init_timer(&self->final_timer); + init_timer(&self->poll_timer); + init_timer(&self->wd_timer); + init_timer(&self->backoff_timer); - irlap_apply_default_connection_parameters( self); + irlap_apply_default_connection_parameters(self); - irlap_next_state( self, LAP_NDM); + irlap_next_state(self, LAP_NDM); - hashbin_insert( irlap, (QUEUE *) self, self->saddr, NULL); + hashbin_insert(irlap, (QUEUE *) self, self->saddr, NULL); - irlmp_register_link( self, self->saddr, &self->notify); + irlmp_register_link(self, self->saddr, &self->notify); return self; } @@ -165,26 +170,26 @@ struct irlap_cb *irlap_open( struct irda_device *irdev) * Remove IrLAP and all allocated memory. Stop any pending timers. * */ -static void __irlap_close( struct irlap_cb *self) +static void __irlap_close(struct irlap_cb *self) { - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); /* Stop timers */ - del_timer( &self->slot_timer); - del_timer( &self->query_timer); - del_timer( &self->discovery_timer); - del_timer( &self->final_timer); - del_timer( &self->poll_timer); - del_timer( &self->wd_timer); - del_timer( &self->backoff_timer); - - irlap_flush_all_queues( self); + del_timer(&self->slot_timer); + del_timer(&self->query_timer); + del_timer(&self->discovery_timer); + del_timer(&self->final_timer); + del_timer(&self->poll_timer); + del_timer(&self->wd_timer); + del_timer(&self->backoff_timer); + + irlap_flush_all_queues(self); self->irdev = NULL; self->magic = 0; - kfree( self); + kfree(self); } /* @@ -193,27 +198,27 @@ static void __irlap_close( struct irlap_cb *self) * Remove IrLAP instance * */ -void irlap_close( struct irlap_cb *self) +void irlap_close(struct irlap_cb *self) { struct irlap_cb *lap; - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); - irlap_disconnect_indication( self, LAP_DISC_INDICATION); + irlap_disconnect_indication(self, LAP_DISC_INDICATION); irlmp_unregister_link(self->saddr); self->notify.instance = NULL; /* Be sure that we manage to remove ourself from the hash */ - lap = hashbin_remove( irlap, self->saddr, NULL); - if ( !lap) { - DEBUG( 1, __FUNCTION__ "(), Didn't find myself!\n"); + lap = hashbin_remove(irlap, self->saddr, NULL); + if (!lap) { + DEBUG(1, __FUNCTION__ "(), Didn't find myself!\n"); return; } - __irlap_close( lap); + __irlap_close(lap); } /* @@ -243,7 +248,7 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) */ void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb) { - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); irlap_do_event(self, CONNECT_RESPONSE, skb, NULL); } @@ -324,23 +329,23 @@ inline void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb) * Received some data that was sent unreliable * */ -void irlap_unit_data_indication( struct irlap_cb *self, struct sk_buff *skb) +void irlap_unit_data_indication(struct irlap_cb *self, struct sk_buff *skb) { - DEBUG( 1, __FUNCTION__ "()\n"); + DEBUG(1, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); - ASSERT( skb != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); + ASSERT(skb != NULL, return;); /* Hide LAP header from IrLMP layer */ - skb_pull( skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); #ifdef CONFIG_IRDA_COMPRESSION - if ( self->qos_tx.compression.value) { + if (self->qos_tx.compression.value) { - skb = irlap_decompress_frame( self, skb); - if ( !skb) { - DEBUG( 1, __FUNCTION__ "(), Decompress error!\n"); + skb = irlap_decompress_frame(self, skb); + if (!skb) { + DEBUG(1, __FUNCTION__ "(), Decompress error!\n"); return; } } @@ -354,40 +359,35 @@ void irlap_unit_data_indication( struct irlap_cb *self, struct sk_buff *skb) * Queue data for transmission, must wait until XMIT state * */ -inline void irlap_data_request( struct irlap_cb *self, struct sk_buff *skb, +inline void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, int reliable) { - DEBUG( 4, __FUNCTION__ "()\n"); - - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); - ASSERT( skb != NULL, return;); - - DEBUG( 4, __FUNCTION__ "(), tx_list=%d\n", - skb_queue_len( &self->tx_list)); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); + ASSERT(skb != NULL, return;); #ifdef CONFIG_IRDA_COMPRESSION - if ( self->qos_tx.compression.value) { - skb = irlap_compress_frame( self, skb); - if ( !skb) { - DEBUG( 1, __FUNCTION__ "(), Compress error!\n"); + if (self->qos_tx.compression.value) { + skb = irlap_compress_frame(self, skb); + if (!skb) { + DEBUG(1, __FUNCTION__ "(), Compress error!\n"); return; } } #endif - ASSERT( skb_headroom( skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), - return;); - skb_push( skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), + return;); + skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); /* * Must set frame format now so that the rest of the code knows * if its dealing with an I or an UI frame */ - if ( reliable) + if (reliable) skb->data[1] = I_FRAME; else { - DEBUG( 4, __FUNCTION__ "(), queueing unreliable frame\n"); + DEBUG(4, __FUNCTION__ "(), queueing unreliable frame\n"); skb->data[1] = UI_FRAME; } @@ -395,20 +395,20 @@ inline void irlap_data_request( struct irlap_cb *self, struct sk_buff *skb, * Send event if this frame only if we are in the right state * FIXME: udata should be sent first! (skb_queue_head?) */ - if (( self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { + if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { /* * Check if the transmit queue contains some unsent frames, * and if so, make sure they are sent first */ - if ( !skb_queue_empty( &self->tx_list)) { - skb_queue_tail( &self->tx_list, skb); - skb = skb_dequeue( &self->tx_list); + if (!skb_queue_empty(&self->tx_list)) { + skb_queue_tail(&self->tx_list, skb); + skb = skb_dequeue(&self->tx_list); - ASSERT( skb != NULL, return;); + ASSERT(skb != NULL, return;); } - irlap_do_event( self, SEND_I_CMD, skb, NULL); + irlap_do_event(self, SEND_I_CMD, skb, NULL); } else - skb_queue_tail( &self->tx_list, skb); + skb_queue_tail(&self->tx_list, skb); } /* @@ -444,33 +444,33 @@ void irlap_disconnect_request(struct irlap_cb *self) * Disconnect request from other device * */ -void irlap_disconnect_indication( struct irlap_cb *self, LAP_REASON reason) +void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) { - DEBUG( 1, __FUNCTION__ "(), reason=%s\n", lap_reasons[reason]); + DEBUG(1, __FUNCTION__ "(), reason=%s\n", lap_reasons[reason]); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); #ifdef CONFIG_IRDA_COMPRESSION - irda_free_compression( self); + irda_free_compression(self); #endif /* Flush queues */ - irlap_flush_all_queues( self); + irlap_flush_all_queues(self); - switch( reason) { + switch(reason) { case LAP_RESET_INDICATION: - DEBUG( 1, __FUNCTION__ "(), Sending reset request!\n"); - irlap_do_event( self, RESET_REQUEST, NULL, NULL); + DEBUG(1, __FUNCTION__ "(), Sending reset request!\n"); + irlap_do_event(self, RESET_REQUEST, NULL, NULL); break; case LAP_NO_RESPONSE: /* FALLTROUGH */ case LAP_DISC_INDICATION: /* FALLTROUGH */ case LAP_FOUND_NONE: /* FALLTROUGH */ case LAP_MEDIA_BUSY: - irlmp_link_disconnect_indication( self->notify.instance, + irlmp_link_disconnect_indication(self->notify.instance, self, reason, NULL); break; default: - DEBUG( 1, __FUNCTION__ "(), Reason %d not implemented!\n", + DEBUG(1, __FUNCTION__ "(), Reason %d not implemented!\n", reason); } } @@ -485,22 +485,22 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) { struct irlap_info info; - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); - ASSERT( discovery != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); + ASSERT(discovery != NULL, return;); - DEBUG( 4, __FUNCTION__ "(), nslots = %d\n", discovery->nslots); + DEBUG(4, __FUNCTION__ "(), nslots = %d\n", discovery->nslots); - ASSERT(( discovery->nslots == 1) || ( discovery->nslots == 6) || - ( discovery->nslots == 8) || ( discovery->nslots == 16), + ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) || + (discovery->nslots == 8) || (discovery->nslots == 16), return;); /* * Discovery is only possible in NDM mode */ - if ( self->state == LAP_NDM) { - ASSERT( self->discovery_log == NULL, return;); - self->discovery_log= hashbin_new( HB_LOCAL); + if (self->state == LAP_NDM) { + ASSERT(self->discovery_log == NULL, return;); + self->discovery_log= hashbin_new(HB_LOCAL); info.S = discovery->nslots; /* Number of slots */ info.s = 0; /* Current slot */ @@ -526,11 +526,11 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) self->slot_timeout = sysctl_slot_timeout * HZ / 1000; - irlap_do_event( self, DISCOVERY_REQUEST, NULL, &info); + irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info); } else { - DEBUG( 4, __FUNCTION__ + DEBUG(4, __FUNCTION__ "(), discovery only possible in NDM mode\n"); - irlap_discovery_confirm( self, NULL); + irlap_discovery_confirm(self, NULL); } } @@ -540,12 +540,12 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) * A device has been discovered in front of this station, we * report directly to LMP. */ -void irlap_discovery_confirm( struct irlap_cb *self, hashbin_t *discovery_log) +void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) { - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); - ASSERT( self->notify.instance != NULL, return;); + ASSERT(self->notify.instance != NULL, return;); /* * Check for successful discovery, since we are then allowed to clear @@ -556,7 +556,7 @@ void irlap_discovery_confirm( struct irlap_cb *self, hashbin_t *discovery_log) irda_device_set_media_busy(self->irdev, FALSE); /* Inform IrLMP */ - irlmp_link_discovery_confirm( self->notify.instance, discovery_log); + irlmp_link_discovery_confirm(self->notify.instance, discovery_log); /* * IrLMP has now the responsibilities for the discovery_log @@ -572,13 +572,13 @@ void irlap_discovery_confirm( struct irlap_cb *self, hashbin_t *discovery_log) */ void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) { - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); - ASSERT( discovery != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); + ASSERT(discovery != NULL, return;); - ASSERT( self->notify.instance != NULL, return;); + ASSERT(self->notify.instance != NULL, return;); irlmp_link_discovery_indication(self->notify.instance, discovery); } @@ -591,12 +591,12 @@ void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) */ void irlap_status_indication(int quality_of_link) { - switch( quality_of_link) { + switch(quality_of_link) { case STATUS_NO_ACTIVITY: - printk( KERN_INFO "IrLAP, no activity on link!\n"); + printk(KERN_INFO "IrLAP, no activity on link!\n"); break; case STATUS_NOISY: - printk( KERN_INFO "IrLAP, noisy link!\n"); + printk(KERN_INFO "IrLAP, noisy link!\n"); break; default: break; @@ -610,17 +610,17 @@ void irlap_status_indication(int quality_of_link) * * */ -void irlap_reset_indication( struct irlap_cb *self) +void irlap_reset_indication(struct irlap_cb *self) { - DEBUG( 1, __FUNCTION__ "()\n"); + DEBUG(1, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); - if ( self->state == LAP_RESET_WAIT) - irlap_do_event( self, RESET_REQUEST, NULL, NULL); + if (self->state == LAP_RESET_WAIT) + irlap_do_event(self, RESET_REQUEST, NULL, NULL); else - irlap_do_event( self, RESET_RESPONSE, NULL, NULL); + irlap_do_event(self, RESET_RESPONSE, NULL, NULL); } /* @@ -631,7 +631,7 @@ void irlap_reset_indication( struct irlap_cb *self) */ void irlap_reset_confirm(void) { - DEBUG( 1, __FUNCTION__ "()\n"); + DEBUG(1, __FUNCTION__ "()\n"); } /* @@ -641,15 +641,15 @@ void irlap_reset_confirm(void) * S = Number of slots (0 -> S-1) * s = Current slot */ -int irlap_generate_rand_time_slot( int S, int s) +int irlap_generate_rand_time_slot(int S, int s) { int slot; - ASSERT(( S - s) > 0, return 0;); + ASSERT((S - s) > 0, return 0;); slot = s + jiffies % (S-s); - ASSERT(( slot >= s) || ( slot < S), return 0;); + ASSERT((slot >= s) || (slot < S), return 0;); return slot; } @@ -661,51 +661,51 @@ int irlap_generate_rand_time_slot( int S, int s) * not intuitive and you should not try to change it. If you think it * contains bugs, please mail a patch to the author instead. */ -void irlap_update_nr_received( struct irlap_cb *self, int nr) +void irlap_update_nr_received(struct irlap_cb *self, int nr) { struct sk_buff *skb = NULL; int count = 0; - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); /* * Remove all the ack-ed frames from the window queue. */ - DEBUG( 4, "--> wx_list=%d, va=%d, nr=%d\n", - skb_queue_len( &self->wx_list), self->va, nr); + DEBUG(4, "--> wx_list=%d, va=%d, nr=%d\n", + skb_queue_len(&self->wx_list), self->va, nr); /* * Optimize for the common case. It is most likely that the receiver * will acknowledge all the frames we have sent! So in that case we * delete all frames stored in window. */ - if ( nr == self->vs) { - while (( skb = skb_dequeue( &self->wx_list)) != NULL) { + if (nr == self->vs) { + while ((skb = skb_dequeue(&self->wx_list)) != NULL) { dev_kfree_skb(skb); } /* The last acked frame is the next to send minus one */ self->va = nr - 1; } else { /* Remove all acknowledged frames in current window */ - while (( skb_peek( &self->wx_list) != NULL) && - ((( self->va+1) % 8) != nr)) + while ((skb_peek(&self->wx_list) != NULL) && + (((self->va+1) % 8) != nr)) { - skb = skb_dequeue( &self->wx_list); + skb = skb_dequeue(&self->wx_list); dev_kfree_skb(skb); self->va = (self->va + 1) % 8; count++; } - DEBUG( 4, "irlap_update_nr_received(), removed %d\n", count); - DEBUG( 4, "wx_list=%d, va=%d, nr=%d -->\n", - skb_queue_len( &self->wx_list), self->va, nr); + DEBUG(4, "irlap_update_nr_received(), removed %d\n", count); + DEBUG(4, "wx_list=%d, va=%d, nr=%d -->\n", + skb_queue_len(&self->wx_list), self->va, nr); } /* Advance window */ - self->window = self->window_size - skb_queue_len( &self->wx_list); + self->window = self->window_size - skb_queue_len(&self->wx_list); } /* @@ -713,14 +713,14 @@ void irlap_update_nr_received( struct irlap_cb *self, int nr) * * Validate the next to send (ns) field from received frame. */ -int irlap_validate_ns_received( struct irlap_cb *self, int ns) +int irlap_validate_ns_received(struct irlap_cb *self, int ns) { - ASSERT( self != NULL, return -ENODEV;); - ASSERT( self->magic == LAP_MAGIC, return -EBADR;); + ASSERT(self != NULL, return -ENODEV;); + ASSERT(self->magic == LAP_MAGIC, return -EBADR;); /* ns as expected? */ - if ( ns == self->vr) { - DEBUG( 4, __FUNCTION__ "(), expected!\n"); + if (ns == self->vr) { + DEBUG(4, __FUNCTION__ "(), expected!\n"); return NS_EXPECTED; } /* @@ -737,14 +737,14 @@ int irlap_validate_ns_received( struct irlap_cb *self, int ns) * Validate the next to receive (nr) field from received frame. * */ -int irlap_validate_nr_received( struct irlap_cb *self, int nr) +int irlap_validate_nr_received(struct irlap_cb *self, int nr) { - ASSERT( self != NULL, return -ENODEV;); - ASSERT( self->magic == LAP_MAGIC, return -EBADR;); + ASSERT(self != NULL, return -ENODEV;); + ASSERT(self->magic == LAP_MAGIC, return -EBADR;); /* nr as expected? */ - if ( nr == self->vs) { - DEBUG( 4, __FUNCTION__ "(), expected!\n"); + if (nr == self->vs) { + DEBUG(4, __FUNCTION__ "(), expected!\n"); return NR_EXPECTED; } @@ -752,11 +752,11 @@ int irlap_validate_nr_received( struct irlap_cb *self, int nr) * unexpected nr? (but within current window), first we check if the * ns numbers of the frames in the current window wrap. */ - if ( self->va < self->vs) { - if (( nr >= self->va) && ( nr <= self->vs)) + if (self->va < self->vs) { + if ((nr >= self->va) && (nr <= self->vs)) return NR_UNEXPECTED; } else { - if (( nr >= self->va) || ( nr <= self->vs)) + if ((nr >= self->va) || (nr <= self->vs)) return NR_UNEXPECTED; } @@ -770,12 +770,12 @@ int irlap_validate_nr_received( struct irlap_cb *self, int nr) * Initialize the connection state parameters * */ -void irlap_initiate_connection_state( struct irlap_cb *self) +void irlap_initiate_connection_state(struct irlap_cb *self) { - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); /* Next to send and next to receive */ self->vs = self->vr = 0; @@ -829,24 +829,24 @@ void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos) * Flush all queues * */ -void irlap_flush_all_queues( struct irlap_cb *self) +void irlap_flush_all_queues(struct irlap_cb *self) { struct sk_buff* skb; - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); /* Free transmission queue */ - while (( skb = skb_dequeue( &self->tx_list)) != NULL) - dev_kfree_skb( skb); + while ((skb = skb_dequeue(&self->tx_list)) != NULL) + dev_kfree_skb(skb); /* Free sliding window buffered packets */ - while (( skb = skb_dequeue( &self->wx_list)) != NULL) - dev_kfree_skb( skb); + while ((skb = skb_dequeue(&self->wx_list)) != NULL) + dev_kfree_skb(skb); #ifdef CONFIG_IRDA_RECYCLE_RR - if ( self->recycle_rr_skb) { - dev_kfree_skb( self->recycle_rr_skb); + if (self->recycle_rr_skb) { + dev_kfree_skb(self->recycle_rr_skb); self->recycle_rr_skb = NULL; } #endif @@ -866,7 +866,7 @@ void irlap_change_speed(struct irlap_cb *self, int speed) ASSERT(self->magic == LAP_MAGIC, return;); if (!self->irdev) { - DEBUG( 1, __FUNCTION__ "(), driver missing!\n"); + DEBUG(1, __FUNCTION__ "(), driver missing!\n"); return; } @@ -883,8 +883,8 @@ void irlap_init_comp_qos_capabilities(struct irlap_cb *self) __u8 mask; /* Current bit tested */ int i; - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); /* * Find out which compressors we support. We do this be checking that @@ -892,24 +892,24 @@ void irlap_init_comp_qos_capabilities(struct irlap_cb *self) * actually been loaded. Ths is sort of hairy code but that is what * you get when you do a little bit flicking :-) */ - DEBUG( 4, __FUNCTION__ "(), comp bits 0x%02x\n", + DEBUG(4, __FUNCTION__ "(), comp bits 0x%02x\n", self->qos_rx.compression.bits); mask = 0x80; /* Start with testing MSB */ - for ( i=0;i<8;i++) { - DEBUG( 4, __FUNCTION__ "(), testing bit %d\n", 8-i); - if ( self->qos_rx.compression.bits & mask) { - DEBUG( 4, __FUNCTION__ "(), bit %d is set by defalt\n", + for (i=0;i<8;i++) { + DEBUG(4, __FUNCTION__ "(), testing bit %d\n", 8-i); + if (self->qos_rx.compression.bits & mask) { + DEBUG(4, __FUNCTION__ "(), bit %d is set by defalt\n", 8-i); - comp = hashbin_find( irlap_compressors, + comp = hashbin_find(irlap_compressors, compression[ msb_index(mask)], NULL); - if ( !comp) { + if (!comp) { /* Protocol not supported, so clear the bit */ - DEBUG( 4, __FUNCTION__ "(), Compression " + DEBUG(4, __FUNCTION__ "(), Compression " "protocol %d has not been loaded!\n", compression[msb_index(mask)]); self->qos_rx.compression.bits &= ~mask; - DEBUG( 4, __FUNCTION__ + DEBUG(4, __FUNCTION__ "(), comp bits 0x%02x\n", self->qos_rx.compression.bits); } @@ -931,20 +931,20 @@ void irlap_init_comp_qos_capabilities(struct irlap_cb *self) void irlap_init_qos_capabilities(struct irlap_cb *self, struct qos_info *qos_user) { - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); - ASSERT( self->irdev != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); + ASSERT(self->irdev != NULL, return;); /* Start out with the maximum QoS support possible */ - irda_init_max_qos_capabilies( &self->qos_rx); + irda_init_max_qos_capabilies(&self->qos_rx); #ifdef CONFIG_IRDA_COMPRESSION - irlap_init_comp_qos_capabilities( self); + irlap_init_comp_qos_capabilities(self); #endif /* Apply drivers QoS capabilities */ - irda_qos_compute_intersection( &self->qos_rx, - irda_device_get_qos( self->irdev)); + irda_qos_compute_intersection(&self->qos_rx, + irda_device_get_qos(self->irdev)); /* * Check for user supplied QoS parameters. The service user is only @@ -952,17 +952,17 @@ void irlap_init_qos_capabilities(struct irlap_cb *self, * user may not have set all of them. */ if (qos_user) { - DEBUG( 1, __FUNCTION__ "(), Found user specified QoS!\n"); + DEBUG(1, __FUNCTION__ "(), Found user specified QoS!\n"); - if ( qos_user->baud_rate.bits) + if (qos_user->baud_rate.bits) self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits; - if ( qos_user->max_turn_time.bits) + if (qos_user->max_turn_time.bits) self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits; - if ( qos_user->data_size.bits) + if (qos_user->data_size.bits) self->qos_rx.data_size.bits &= qos_user->data_size.bits; - if ( qos_user->link_disc_time.bits) + if (qos_user->link_disc_time.bits) self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits; #ifdef CONFIG_IRDA_COMPRESSION self->qos_rx.compression.bits &= qos_user->compression.bits; @@ -984,7 +984,7 @@ void irlap_init_qos_capabilities(struct irlap_cb *self, /* Set disconnect time */ self->qos_rx.link_disc_time.bits &= 0x07; - irda_qos_bits_to_value( &self->qos_rx); + irda_qos_bits_to_value(&self->qos_rx); } /* @@ -993,14 +993,14 @@ void irlap_init_qos_capabilities(struct irlap_cb *self, * Use the default connection and transmission parameters * */ -void irlap_apply_default_connection_parameters( struct irlap_cb *self) +void irlap_apply_default_connection_parameters(struct irlap_cb *self) { - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); - irlap_change_speed( self, 9600); + irlap_change_speed(self, 9600); /* Default value in NDM */ self->bofs_count = 11; @@ -1028,12 +1028,12 @@ void irlap_apply_default_connection_parameters( struct irlap_cb *self) void irlap_apply_connection_parameters(struct irlap_cb *self, struct qos_info *qos) { - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LAP_MAGIC, return;); - irlap_change_speed( self, qos->baud_rate.value); + irlap_change_speed(self, qos->baud_rate.value); self->window_size = qos->window_size.value; self->window = qos->window_size.value; @@ -1045,7 +1045,7 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, */ self->window_bytes = qos->baud_rate.value * qos->max_turn_time.value / 10000; - DEBUG( 4, "Setting window_bytes = %d\n", self->window_bytes); + DEBUG(4, "Setting window_bytes = %d\n", self->window_bytes); /* * Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to @@ -1058,10 +1058,10 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, else self->N1 = 3000 / qos->max_turn_time.value; - DEBUG( 4, "Setting N1 = %d\n", self->N1); + DEBUG(4, "Setting N1 = %d\n", self->N1); self->N2 = qos->link_disc_time.value * 1000 / qos->max_turn_time.value; - DEBUG( 4, "Setting N2 = %d\n", self->N2); + DEBUG(4, "Setting N2 = %d\n", self->N2); /* * Initialize timeout values, some of the rules are listed on @@ -1072,11 +1072,11 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, self->wd_timeout = self->poll_timeout * 2; #ifdef CONFIG_IRDA_COMPRESSION - if ( qos->compression.value) { - DEBUG( 1, __FUNCTION__ "(), Initializing compression\n"); - irda_set_compression( self, qos->compression.value); + if (qos->compression.value) { + DEBUG(1, __FUNCTION__ "(), Initializing compression\n"); + irda_set_compression(self, qos->compression.value); - irlap_compressor_init( self, 0); + irlap_compressor_init(self, 0); } #endif } @@ -1088,7 +1088,7 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, * Give some info to the /proc file system * */ -int irlap_proc_read( char *buf, char **start, off_t offset, int len, +int irlap_proc_read(char *buf, char **start, off_t offset, int len, int unused) { struct irlap_cb *self; @@ -1100,81 +1100,81 @@ int irlap_proc_read( char *buf, char **start, off_t offset, int len, len = 0; - self = (struct irlap_cb *) hashbin_get_first( irlap); - while ( self != NULL) { - ASSERT( self != NULL, return -ENODEV;); - ASSERT( self->magic == LAP_MAGIC, return -EBADR;); + self = (struct irlap_cb *) hashbin_get_first(irlap); + while (self != NULL) { + ASSERT(self != NULL, return -ENODEV;); + ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - len += sprintf( buf+len, "irlap%d <-> %s ", + len += sprintf(buf+len, "irlap%d <-> %s ", i++, self->irdev->name); - len += sprintf( buf+len, "state: %s\n", + len += sprintf(buf+len, "state: %s\n", irlap_state[ self->state]); - len += sprintf( buf+len, " caddr: %#02x, ", self->caddr); - len += sprintf( buf+len, "saddr: %#08x, ", self->saddr); - len += sprintf( buf+len, "daddr: %#08x\n", self->daddr); + len += sprintf(buf+len, " caddr: %#02x, ", self->caddr); + len += sprintf(buf+len, "saddr: %#08x, ", self->saddr); + len += sprintf(buf+len, "daddr: %#08x\n", self->daddr); - len += sprintf( buf+len, " win size: %d, ", + len += sprintf(buf+len, " win size: %d, ", self->window_size); - len += sprintf( buf+len, "win: %d, ", self->window); - len += sprintf( buf+len, "win bytes: %d, ", self->window_bytes); - len += sprintf( buf+len, "bytes left: %d\n", self->bytes_left); - - len += sprintf( buf+len, " tx queue len: %d ", - skb_queue_len( &self->tx_list)); - len += sprintf( buf+len, "win queue len: %d ", - skb_queue_len( &self->wx_list)); - len += sprintf( buf+len, "rbusy: %s\n", self->remote_busy ? + len += sprintf(buf+len, "win: %d, ", self->window); + len += sprintf(buf+len, "win bytes: %d, ", self->window_bytes); + len += sprintf(buf+len, "bytes left: %d\n", self->bytes_left); + + len += sprintf(buf+len, " tx queue len: %d ", + skb_queue_len(&self->tx_list)); + len += sprintf(buf+len, "win queue len: %d ", + skb_queue_len(&self->wx_list)); + len += sprintf(buf+len, "rbusy: %s\n", self->remote_busy ? "TRUE" : "FALSE"); - len += sprintf( buf+len, " retrans: %d ", self->retry_count); - len += sprintf( buf+len, "vs: %d ", self->vs); - len += sprintf( buf+len, "vr: %d ", self->vr); - len += sprintf( buf+len, "va: %d\n", self->va); + len += sprintf(buf+len, " retrans: %d ", self->retry_count); + len += sprintf(buf+len, "vs: %d ", self->vs); + len += sprintf(buf+len, "vr: %d ", self->vr); + len += sprintf(buf+len, "va: %d\n", self->va); - len += sprintf( buf+len, " qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n"); + len += sprintf(buf+len, " qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n"); - len += sprintf( buf+len, " tx\t%d\t", + len += sprintf(buf+len, " tx\t%d\t", self->qos_tx.baud_rate.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_tx.max_turn_time.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_tx.data_size.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_tx.window_size.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_tx.additional_bofs.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_tx.min_turn_time.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_tx.link_disc_time.value); #ifdef CONFIG_IRDA_COMPRESSION - len += sprintf( buf+len, "%d", + len += sprintf(buf+len, "%d", self->qos_tx.compression.value); #endif - len += sprintf( buf+len, "\n"); + len += sprintf(buf+len, "\n"); - len += sprintf( buf+len, " rx\t%d\t", + len += sprintf(buf+len, " rx\t%d\t", self->qos_rx.baud_rate.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_rx.max_turn_time.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_rx.data_size.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_rx.window_size.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_rx.additional_bofs.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_rx.min_turn_time.value); - len += sprintf( buf+len, "%d\t", + len += sprintf(buf+len, "%d\t", self->qos_rx.link_disc_time.value); #ifdef CONFIG_IRDA_COMPRESSION - len += sprintf( buf+len, "%d", + len += sprintf(buf+len, "%d", self->qos_rx.compression.value); #endif - len += sprintf( buf+len, "\n"); + len += sprintf(buf+len, "\n"); - self = (struct irlap_cb *) hashbin_get_next( irlap); + self = (struct irlap_cb *) hashbin_get_next(irlap); } restore_flags(flags); diff --git a/net/irda/irlap_comp.c b/net/irda/irlap_comp.c index 9959b64bc..299d1705c 100644 --- a/net/irda/irlap_comp.c +++ b/net/irda/irlap_comp.c @@ -6,11 +6,13 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Fri Oct 9 09:18:07 1998 - * Modified at: Mon Feb 8 01:23:52 1999 + * Modified at: Sun May 9 11:37:06 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> + * Modified at: Fri May 28 3:11 CST 1999 + * Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl> * Sources: ppp.c, isdn_ppp.c * - * Copyright (c) 1998 Dag Brattli, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,6 +25,8 @@ * ********************************************************************/ +#include <linux/string.h> + #include <net/irda/irda.h> #include <net/irda/irqueue.h> #include <net/irda/irlap.h> @@ -255,11 +259,11 @@ struct sk_buff *irlap_compress_frame( struct irlap_cb *self, } /* FIXME: Find out what is the max overhead (not 10) */ - new_skb = dev_alloc_skb( skb->len+LAP_HEADER+10); + new_skb = dev_alloc_skb( skb->len+LAP_MAX_HEADER+10); if(!new_skb) return skb; - skb_reserve( new_skb, LAP_HEADER); + skb_reserve( new_skb, LAP_MAX_HEADER); skb_put( new_skb, skb->len+10); count = (self->compressor.cp->compress)( self->compressor.state, diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index a2fbadf65..aeb8ff678 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Fri Apr 23 11:55:12 1999 + * Modified at: Mon May 31 21:55:42 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, * Thomas Davis <ratbert@radiks.net> * All Rights Reserved. * @@ -209,8 +209,8 @@ void irlap_start_poll_timer(struct irlap_cb *self, int timeout) * Rushes through the state machine without any delay. If state == XMIT * then send queued data frames. */ -void irlap_do_event( struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) +void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) { int ret; @@ -218,7 +218,7 @@ void irlap_do_event( struct irlap_cb *self, IRLAP_EVENT event, return; DEBUG(4, __FUNCTION__ "(), event = %s, state = %s\n", - irlap_event[ event], irlap_state[ self->state]); + irlap_event[event], irlap_state[self->state]); ret = (*state[ self->state]) (self, event, skb, info); @@ -236,13 +236,12 @@ void irlap_do_event( struct irlap_cb *self, IRLAP_EVENT event, if (skb_queue_len(&self->tx_list)) { /* Try to send away all queued data frames */ while ((skb = skb_dequeue(&self->tx_list)) != NULL) { - ret = (*state[ self->state])(self, SEND_I_CMD, - skb, NULL); + ret = (*state[self->state])(self, SEND_I_CMD, + skb, NULL); if ( ret == -EPROTO) break; /* Try again later! */ } } else if (self->disconnect_pending) { - DEBUG(0, __FUNCTION__ "(), disconnecting!\n"); self->disconnect_pending = FALSE; ret = (*state[self->state])(self, DISCONNECT_REQUEST, @@ -274,22 +273,22 @@ void irlap_do_event( struct irlap_cb *self, IRLAP_EVENT event, * Switches state and provides debug information * */ -void irlap_next_state( struct irlap_cb *self, IRLAP_STATE state) +void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state) { - if ( !self || self->magic != LAP_MAGIC) + if (!self || self->magic != LAP_MAGIC) return; - DEBUG( 4, "next LAP state = %s\n", irlap_state[ state]); + DEBUG(4, "next LAP state = %s\n", irlap_state[ state]); self->state = state; /* * If we are swithing away from a XMIT state then we are allowed to * transmit a maximum number of bytes again when we enter the XMIT - * state again. Since its possible to "switch" from XMIT to XMIT and + * state again. Since its possible to "switch" from XMIT to XMIT, * we cannot do this when swithing into the XMIT state :-) */ - if (( state != LAP_XMIT_P) && ( state != LAP_XMIT_S)) + if ((state != LAP_XMIT_P) && (state != LAP_XMIT_S)) self->bytes_left = self->window_bytes; } @@ -310,7 +309,7 @@ static int irlap_state_ndm( struct irlap_cb *self, IRLAP_EVENT event, ASSERT( self != NULL, return -1;); ASSERT( self->magic == LAP_MAGIC, return -1;); - switch( event) { + switch(event) { case CONNECT_REQUEST: ASSERT( self->irdev != NULL, return -1;); @@ -393,7 +392,6 @@ static int irlap_state_ndm( struct irlap_cb *self, IRLAP_EVENT event, irlap_start_query_timer( self, QUERY_TIMEOUT); irlap_next_state( self, LAP_REPLY); } - dev_kfree_skb(skb); break; @@ -530,7 +528,7 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, irlap_send_discovery_xid_frame(self, info->S, self->slot, FALSE, discovery_rsp); - + self->frame_sent = TRUE; irlap_next_state(self, LAP_REPLY); } @@ -568,27 +566,28 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, switch (event) { case CONNECT_RESPONSE: - skb_pull( skb, 11); + /* skb_pull(skb, 11); */ + skb_pull(skb, sizeof(struct snrm_frame)); - ASSERT( self->irdev != NULL, return -1;); - irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb); + ASSERT(self->irdev != NULL, return -1;); + irda_qos_negotiate(&self->qos_rx, &self->qos_tx, skb); irlap_initiate_connection_state( self); /* * We are allowed to send two frames! */ - irlap_send_ua_response_frame( self, &self->qos_rx); - irlap_send_ua_response_frame( self, &self->qos_rx); + irlap_send_ua_response_frame(self, &self->qos_rx); + irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_apply_connection_parameters( self, &self->qos_tx); + irlap_apply_connection_parameters(self, &self->qos_tx); /* * The WD-timer could be set to the duration of the P-timer - * for this case, but it is recommomended to use twice the + * for this case, but it is recommended to use twice the * value (note 3 IrLAP p. 60). */ - irlap_start_wd_timer( self, self->wd_timeout); + irlap_start_wd_timer(self, self->wd_timeout); irlap_next_state( self, LAP_NRM_S); break; @@ -669,28 +668,30 @@ static int irlap_state_setup( struct irlap_cb *self, IRLAP_EVENT event, * The device with the largest device address wins the battle * (both have sent a SNRM command!) */ - if ( info->daddr > self->saddr) { - del_timer( &self->final_timer); - irlap_initiate_connection_state( self); + if (info->daddr > self->saddr) { + del_timer(&self->final_timer); + irlap_initiate_connection_state(self); - ASSERT( self->irdev != NULL, return -1;); - irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb); + ASSERT(self->irdev != NULL, return -1;); + /* skb_pull(skb, 11); */ + skb_pull(skb, sizeof(struct snrm_frame)); + irda_qos_negotiate(&self->qos_rx, &self->qos_tx, skb); irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_apply_connection_parameters( self, &self->qos_tx); - irlap_connect_confirm( self, skb); + irlap_apply_connection_parameters(self, &self->qos_tx); + irlap_connect_confirm(self, skb); /* * The WD-timer could be set to the duration of the - * P-timer for this case, but it is recommomended + * P-timer for this case, but it is recommended * to use twice the value (note 3 IrLAP p. 60). */ - irlap_start_wd_timer( self, self->wd_timeout); + irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state( self, LAP_NRM_S); + irlap_next_state(self, LAP_NRM_S); } else { /* We just ignore the other device! */ - irlap_next_state( self, LAP_SETUP); + irlap_next_state(self, LAP_SETUP); } break; case RECV_UA_RSP: @@ -702,9 +703,10 @@ static int irlap_state_setup( struct irlap_cb *self, IRLAP_EVENT event, /* Negotiate connection parameters */ ASSERT( skb->len > 10, return -1;); - skb_pull( skb, 10); + /* skb_pull(skb, 10); */ + skb_pull(skb, sizeof(struct ua_frame)); - ASSERT( self->irdev != NULL, return -1;); + ASSERT(self->irdev != NULL, return -1;); irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb); irlap_apply_connection_parameters( self, &self->qos_tx); @@ -758,36 +760,30 @@ static int irlap_state_offline( struct irlap_cb *self, IRLAP_EVENT event, * stations. * */ -static int irlap_state_xmit_p( struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) +static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) { int ret = 0; - ASSERT( self != NULL, return -ENODEV;); - ASSERT( self->magic == LAP_MAGIC, return -EBADR;); - - DEBUG( 4, __FUNCTION__ "(), event=%s, vs=%d, vr=%d", - irlap_event[ event], self->vs, self->vr); + DEBUG(4, __FUNCTION__ "(), event=%s, vs=%d, vr=%d", + irlap_event[event], self->vs, self->vr); switch (event) { case SEND_I_CMD: - ASSERT( skb != NULL, return -1;); - DEBUG( 4, __FUNCTION__ "(), Window=%d\n", self->window); - /* * Only send frame if send-window > 0. */ - if (( self->window > 0) && ( !self->remote_busy)) { + if ((self->window > 0) && (!self->remote_busy)) { /* * Test if we have transmitted more bytes over the * link than its possible to do with the current * speed and turn-around-time. */ - if (( skb->len+self->bofs_count) > self->bytes_left) { - DEBUG( 4, __FUNCTION__ "(), Not allowed to " - "transmit more bytes!\n"); - skb_queue_head( &self->tx_list, skb); + if ((skb->len+self->bofs_count) > self->bytes_left) { + DEBUG(4, __FUNCTION__ "(), Not allowed to " + "transmit more bytes!\n"); + skb_queue_head(&self->tx_list, skb); /* * We should switch state to LAP_NRM_P, but @@ -799,7 +795,7 @@ static int irlap_state_xmit_p( struct irlap_cb *self, IRLAP_EVENT event, */ return -EPROTO; } - self->bytes_left -= ( skb->len + self->bofs_count); + self->bytes_left -= (skb->len + self->bofs_count); /* * Send data with poll bit cleared only if window > 1 @@ -808,11 +804,9 @@ static int irlap_state_xmit_p( struct irlap_cb *self, IRLAP_EVENT event, if (( self->window > 1) && skb_queue_len( &self->tx_list) > 0) { - DEBUG( 4, __FUNCTION__ "(), window > 1\n"); irlap_send_data_primary( self, skb); irlap_next_state( self, LAP_XMIT_P); } else { - DEBUG( 4, __FUNCTION__ "(), window <= 1\n"); irlap_send_data_primary_poll( self, skb); irlap_next_state( self, LAP_NRM_P); @@ -930,9 +924,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, int ns_status; int nr_status; - ASSERT(self != NULL, return -1;); - ASSERT(self->magic == LAP_MAGIC, return -1;); - switch (event) { case RECV_I_RSP: /* Optimize for the common case */ /* FIXME: must check for remote_busy below */ @@ -944,7 +935,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, */ self->fast_RR = FALSE; #endif - ASSERT( info != NULL, return -1;); ns_status = irlap_validate_ns_received(self, info->ns); @@ -1138,13 +1128,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, } break; case RECV_RR_RSP: - DEBUG(4, __FUNCTION__ "(), RECV_RR_FRAME: " - "Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n", - self->retry_count, info->nr, self->va, self->vs, - self->vr); - - ASSERT(info != NULL, return -1;); - /* * If you get a RR, the remote isn't busy anymore, * no matter what the NR @@ -1191,14 +1174,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Resend rejected frames */ irlap_resend_rejected_frames( self, CMD_FRAME); - /* - * Start only if not running, DB - * TODO: Should this one be here? - */ - /* if ( !self->final_timer.prev) */ -/* irda_start_timer( FINAL_TIMER, self->final_timeout); */ - - /* Keep state */ irlap_next_state( self, LAP_NRM_P); } else if (ret == NR_INVALID) { DEBUG(1, "irlap_state_nrm_p: received RR with " @@ -1207,8 +1182,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state( self, LAP_RESET_WAIT); - irlap_disconnect_indication( self, - LAP_RESET_INDICATION); + irlap_disconnect_indication(self, LAP_RESET_INDICATION); self->xmitflag = TRUE; } if (skb) @@ -1476,13 +1450,13 @@ static int irlap_state_xmit_s( struct irlap_cb *self, IRLAP_EVENT event, /* * Send frame only if send window > 1 */ - if (( self->window > 0) && ( !self->remote_busy)) { + if ((self->window > 0) && ( !self->remote_busy)) { /* * Test if we have transmitted more bytes over the * link than its possible to do with the current * speed and turn-around-time. */ - if (( skb->len+self->bofs_count) > self->bytes_left) { + if ((skb->len+self->bofs_count) > self->bytes_left) { DEBUG( 4, "IrDA: Not allowed to transmit more bytes!\n"); skb_queue_head( &self->tx_list, skb); /* @@ -1504,11 +1478,9 @@ static int irlap_state_xmit_s( struct irlap_cb *self, IRLAP_EVENT event, if (( self->window > 1) && skb_queue_len( &self->tx_list) > 0) { - DEBUG( 4, __FUNCTION__ "(), window > 1\n"); irlap_send_data_secondary( self, skb); irlap_next_state( self, LAP_XMIT_S); } else { - DEBUG( 4, "(), window <= 1\n"); irlap_send_data_secondary_final( self, skb); irlap_next_state( self, LAP_NRM_S); @@ -1570,7 +1542,7 @@ static int irlap_state_nrm_s( struct irlap_cb *self, IRLAP_EVENT event, /* * poll bit cleared? */ - if ( !info->pf) { + if (!info->pf) { self->vr = (self->vr + 1) % 8; /* Update Nr received */ @@ -1600,35 +1572,39 @@ static int irlap_state_nrm_s( struct irlap_cb *self, IRLAP_EVENT event, * also before changing to XMIT_S * state. (note 1, IrLAP p. 82) */ - irlap_wait_min_turn_around( self, &self->qos_tx); - /* - * Any pending data requests? + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* + * Give higher layers a chance to + * immediately reply with some data before + * we decide if we should send a RR frame + * or not */ - if (( skb_queue_len( &self->tx_list) > 0) && - ( self->window > 0)) + irlap_data_indication(self, skb); + + /* Any pending data requests? */ + if ((skb_queue_len(&self->tx_list) > 0) && + (self->window > 0)) { self->ack_required = TRUE; - del_timer( &self->wd_timer); + del_timer(&self->wd_timer); - irlap_next_state( self, LAP_XMIT_S); + irlap_next_state(self, LAP_XMIT_S); } else { - irlap_send_rr_frame( self, RSP_FRAME); - irlap_start_wd_timer( self, self->wd_timeout); + irlap_send_rr_frame(self, RSP_FRAME); + irlap_start_wd_timer(self, self->wd_timeout); /* Keep the state */ - irlap_next_state( self, LAP_NRM_S); + irlap_next_state(self, LAP_NRM_S); } - irlap_data_indication( self, skb); - break; } } /* * Check for Unexpected next to send (Ns) */ - if (( ns_status == NS_UNEXPECTED) && - ( nr_status == NR_EXPECTED)) + if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) { /* Unexpected next to send, with final bit cleared */ if ( !info->pf) { @@ -1651,8 +1627,7 @@ static int irlap_state_nrm_s( struct irlap_cb *self, IRLAP_EVENT event, /* * Unexpected Next to Receive(NR) ? */ - if (( ns_status == NS_EXPECTED) && - ( nr_status == NR_UNEXPECTED)) + if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) { if ( info->pf) { DEBUG( 4, "RECV_I_RSP: frame(s) lost\n"); @@ -1748,20 +1723,20 @@ static int irlap_state_nrm_s( struct irlap_cb *self, IRLAP_EVENT event, irlap_update_nr_received( self, info->nr); del_timer( &self->wd_timer); - irlap_wait_min_turn_around( self, &self->qos_tx); + irlap_wait_min_turn_around(self, &self->qos_tx); irlap_next_state( self, LAP_XMIT_S); } else { self->remote_busy = FALSE; /* Update Nr received */ - irlap_update_nr_received( self, info->nr); - irlap_wait_min_turn_around( self, &self->qos_tx); + irlap_update_nr_received(self, info->nr); + irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame( self, RSP_FRAME); + irlap_send_rr_frame(self, RSP_FRAME); - irlap_start_wd_timer( self, self->wd_timeout); - irlap_next_state( self, LAP_NRM_S); + irlap_start_wd_timer(self, self->wd_timeout); + irlap_next_state(self, LAP_NRM_S); } - } else if ( nr_status == NR_UNEXPECTED) { + } else if (nr_status == NR_UNEXPECTED) { self->remote_busy = FALSE; irlap_update_nr_received( self, info->nr); irlap_resend_rejected_frames( self, RSP_FRAME); @@ -1773,8 +1748,8 @@ static int irlap_state_nrm_s( struct irlap_cb *self, IRLAP_EVENT event, } else { DEBUG(1, __FUNCTION__ "(), invalid nr not implemented!\n"); } - if ( skb) - dev_kfree_skb( skb); + if (skb) + dev_kfree_skb(skb); break; case RECV_SNRM_CMD: @@ -1886,7 +1861,7 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, ASSERT( self != NULL, return -ENODEV;); ASSERT( self->magic == LAP_MAGIC, return -EBADR;); - switch( event) { + switch(event) { case RESET_RESPONSE: irlap_send_ua_response_frame( self, &self->qos_rx); irlap_initiate_connection_state( self); diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c index cda78e7f1..3011284d1 100644 --- a/net/irda/irlap_frame.c +++ b/net/irda/irlap_frame.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Aug 19 10:27:26 1997 - * Modified at: Fri Apr 23 09:30:42 1999 + * Modified at: Mon May 31 09:29:13 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Resrved. + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Resrved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -1001,10 +1001,6 @@ void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, { __u8 *frame; - ASSERT( self != NULL, return;); - ASSERT( self->magic == LAP_MAGIC, return;); - ASSERT( skb != NULL, return;); - frame = skb->data; /* Insert connection address */ @@ -1014,15 +1010,6 @@ void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, /* Insert next to receive (Vr) */ frame[1] |= (self->vr << 5); /* insert nr */ -#if 0 - { - int ns; - ns = (frame[1] >> 1) & 0x07; /* Next to send */ - - DEBUG(0, __FUNCTION__ "(), ns=%d\n", ns); - } -#endif - irlap_queue_xmit(self, skb); } @@ -1056,8 +1043,8 @@ static inline void irlap_recv_i_frame(struct irlap_cb *self, * Receive and parse an Unnumbered Information (UI) frame * */ -static void irlap_recv_ui_frame( struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) +static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info) { __u8 *frame; @@ -1240,7 +1227,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct device *dev, * Optimize for the common case and check if the frame is an * I(nformation) frame. Only I-frames have bit 0 set to 0 */ - if(~control & 0x01) { + if (~control & 0x01) { irlap_recv_i_frame(self, skb, &info, command); self->stats.rx_packets++; return 0; @@ -1254,7 +1241,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct device *dev, * Received S(upervisory) frame, check which frame type it is * only the first nibble is of interest */ - switch(control & 0x0f) { + switch (control & 0x0f) { case RR: irlap_recv_rr_frame( self, skb, &info, command); self->stats.rx_packets++; @@ -1279,7 +1266,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct device *dev, /* * This must be a C(ontrol) frame */ - switch(control) { + switch (control) { case XID_RSP: irlap_recv_discovery_xid_rsp(self, skb, &info); break; diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c index d76661b6c..7df849c2a 100644 --- a/net/irda/irlmp.c +++ b/net/irda/irlmp.c @@ -6,10 +6,10 @@ * Status: Stable. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 17 20:54:32 1997 - * Modified at: Fri Apr 23 09:13:24 1999 + * Modified at: Mon May 31 21:49:41 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -197,7 +197,7 @@ struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, struct notify_t *notify) } /* - * Function irlmp_close_lsap (self) + * Function __irlmp_close_lsap (self) * * Remove an instance of LSAP */ @@ -369,11 +369,11 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, if (!skb) return -ENOMEM; - skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(skb, LMP_MAX_HEADER); } else skb = userdata; - /* Make room for MUX control header ( 3 bytes) */ + /* Make room for MUX control header (3 bytes) */ ASSERT(skb_headroom(skb) >= LMP_CONTROL_HEADER, return -1;); skb_push(skb, LMP_CONTROL_HEADER); @@ -443,25 +443,35 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) { int max_seg_size; - - DEBUG(3, __FUNCTION__ "()\n"); + int lap_header_size; + int max_header_size; ASSERT(self != NULL, return;); ASSERT(self->magic == LMP_LSAP_MAGIC, return;); ASSERT(skb != NULL, return;); ASSERT(self->lap != NULL, return;); + DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", + self->slsap_sel, self->dlsap_sel); + self->qos = *self->lap->qos; - max_seg_size = self->lap->qos->data_size.value; - DEBUG(4, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size); + max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; + DEBUG(2, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size); + lap_header_size = irlap_get_header_size(self->lap->irlap); + + max_header_size = LMP_HEADER + lap_header_size; + + DEBUG(2, __FUNCTION__ "(), max_header_size=%d\n", max_header_size); + /* Hide LMP_CONTROL_HEADER header from layer above */ skb_pull(skb, LMP_CONTROL_HEADER); if (self->notify.connect_indication) self->notify.connect_indication(self->notify.instance, self, - &self->qos, max_seg_size, skb); + &self->qos, max_seg_size, + max_header_size, skb); } /* @@ -470,24 +480,22 @@ void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) * Service user is accepting connection * */ -void irlmp_connect_response( struct lsap_cb *self, struct sk_buff *userdata) +void irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata) { - DEBUG(3, __FUNCTION__ "()\n"); - - ASSERT( self != NULL, return;); - ASSERT( self->magic == LMP_LSAP_MAGIC, return;); - ASSERT( userdata != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + ASSERT(userdata != NULL, return;); self->connected = TRUE; - DEBUG( 4, "irlmp_connect_response: slsap_sel=%02x, dlsap_sel=%02x\n", - self->slsap_sel, self->dlsap_sel); + DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", + self->slsap_sel, self->dlsap_sel); /* Make room for MUX control header ( 3 bytes) */ - ASSERT( skb_headroom( userdata) >= LMP_CONTROL_HEADER, return;); - skb_push( userdata, LMP_CONTROL_HEADER); + ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return;); + skb_push(userdata, LMP_CONTROL_HEADER); - irlmp_do_lsap_event( self, LM_CONNECT_RESPONSE, userdata); + irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata); } /* @@ -498,25 +506,34 @@ void irlmp_connect_response( struct lsap_cb *self, struct sk_buff *userdata) void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) { int max_seg_size; + int max_header_size; + int lap_header_size; DEBUG(3, __FUNCTION__ "()\n"); - ASSERT( skb != NULL, return;); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LMP_LSAP_MAGIC, return;); + ASSERT(skb != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - ASSERT( self->lap != NULL, return;); + ASSERT(self->lap != NULL, return;); self->qos = *self->lap->qos; - max_seg_size = self->qos.data_size.value; - DEBUG( 4, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size); + max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; + DEBUG(2, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size); + + lap_header_size = irlap_get_header_size(self->lap->irlap); + max_header_size = LMP_HEADER + lap_header_size; + + DEBUG(2, __FUNCTION__ "(), max_header_size=%d\n", max_header_size); + /* Hide LMP_CONTROL_HEADER header from layer above */ - skb_pull( skb, LMP_CONTROL_HEADER); + skb_pull(skb, LMP_CONTROL_HEADER); - if ( self->notify.connect_confirm) { - self->notify.connect_confirm( self->notify.instance, self, - &self->qos, max_seg_size, skb); + if (self->notify.connect_confirm) { + self->notify.connect_confirm(self->notify.instance, self, + &self->qos, max_seg_size, + max_header_size, skb); } } @@ -620,8 +637,8 @@ void irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata) * * LSAP is being closed! */ -void irlmp_disconnect_indication( struct lsap_cb *self, LM_REASON reason, - struct sk_buff *userdata) +void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, + struct sk_buff *userdata) { struct lsap_cb *lsap; @@ -637,6 +654,10 @@ void irlmp_disconnect_indication( struct lsap_cb *self, LM_REASON reason, self->connected = FALSE; self->dlsap_sel = LSAP_ANY; +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + irlmp->cache.valid = FALSE; +#endif + /* * Remove association between this LSAP and the link it used */ @@ -975,7 +996,7 @@ void irlmp_status_request(void) DEBUG( 1, "irlmp_status_request(), Not implemented\n"); } -void irlmp_status_indication( LINK_STATUS link, LOCK_STATUS lock) +void irlmp_status_indication(LINK_STATUS link, LOCK_STATUS lock) { DEBUG( 4, "irlmp_status_indication(), Not implemented\n"); } @@ -1418,14 +1439,14 @@ __u32 irlmp_get_daddr(struct lsap_cb *self) * Give some info to the /proc file system * */ -int irlmp_proc_read( char *buf, char **start, off_t offset, int len, - int unused) +int irlmp_proc_read(char *buf, char **start, off_t offset, int len, + int unused) { struct lsap_cb *self; struct lap_cb *lap; unsigned long flags; - ASSERT( irlmp != NULL, return 0;); + ASSERT(irlmp != NULL, return 0;); save_flags( flags); cli(); @@ -1449,35 +1470,34 @@ int irlmp_proc_read( char *buf, char **start, off_t offset, int len, } len += sprintf( buf+len, "\nRegistred Link Layers:\n"); - lap = (struct lap_cb *) hashbin_get_first( irlmp->links); - while ( lap != NULL) { - ASSERT( lap->magic == LMP_LAP_MAGIC, return 0;); - len += sprintf( buf+len, "lap state: %s, ", - irlmp_state[ lap->lap_state]); + lap = (struct lap_cb *) hashbin_get_first(irlmp->links); + while (lap != NULL) { + len += sprintf(buf+len, "lap state: %s, ", + irlmp_state[lap->lap_state]); - len += sprintf( buf+len, "saddr: %#08x, daddr: %#08x, ", - lap->saddr, lap->daddr); - len += sprintf( buf+len, "\n"); + len += sprintf(buf+len, "saddr: %#08x, daddr: %#08x, ", + lap->saddr, lap->daddr); + len += sprintf(buf+len, "\n"); len += sprintf( buf+len, "\nConnected LSAPs:\n"); self = (struct lsap_cb *) hashbin_get_first( lap->lsaps); - while ( self != NULL) { - ASSERT( self->magic == LMP_LSAP_MAGIC, return 0;); - len += sprintf( buf+len, "lsap state: %s, ", - irlsap_state[ self->lsap_state]); - len += sprintf( buf+len, - "slsap_sel: %#02x, dlsap_sel: %#02x, ", - self->slsap_sel, self->dlsap_sel); - len += sprintf( buf+len, "(%s)", self->notify.name); - len += sprintf( buf+len, "\n"); + while (self != NULL) { + ASSERT(self->magic == LMP_LSAP_MAGIC, return 0;); + len += sprintf(buf+len, "lsap state: %s, ", + irlsap_state[ self->lsap_state]); + len += sprintf(buf+len, + "slsap_sel: %#02x, dlsap_sel: %#02x, ", + self->slsap_sel, self->dlsap_sel); + len += sprintf(buf+len, "(%s)", self->notify.name); + len += sprintf(buf+len, "\n"); - self = ( struct lsap_cb *) hashbin_get_next( + self = (struct lsap_cb *) hashbin_get_next( lap->lsaps); } + len += sprintf(buf+len, "\n"); - lap = ( struct lap_cb *) hashbin_get_next( - irlmp->links); + lap = (struct lap_cb *) hashbin_get_next(irlmp->links); } restore_flags( flags); diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c index bf1bab31e..95a707a7f 100644 --- a/net/irda/irlmp_frame.c +++ b/net/irda/irlmp_frame.c @@ -1,15 +1,15 @@ /********************************************************************* * * Filename: irlmp_frame.c - * Version: 0.8 + * Version: 0.9 * Description: IrLMP frame implementation * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Aug 19 02:09:59 1997 - * Modified at: Fri Apr 23 09:12:23 1999 + * Modified at: Mon May 31 09:53:16 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no> + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no> * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -59,16 +59,16 @@ inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, * * Send Link Control Frame to IrLAP */ -void irlmp_send_lcf_pdu( struct lap_cb *self, __u8 dlsap, __u8 slsap, - __u8 opcode, struct sk_buff *skb) +void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, + __u8 opcode, struct sk_buff *skb) { __u8 *frame; - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LMP_LAP_MAGIC, return;); - ASSERT( skb != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LMP_LAP_MAGIC, return;); + ASSERT(skb != NULL, return;); frame = skb->data; @@ -82,8 +82,8 @@ void irlmp_send_lcf_pdu( struct lap_cb *self, __u8 dlsap, __u8 slsap, else frame[3] = 0x00; /* rsvd */ - ASSERT( self->irlap != NULL, return;); - irlap_data_request( self->irlap, skb, TRUE); + ASSERT(self->irlap != NULL, return;); + irlap_data_request(self->irlap, skb, TRUE); } /* @@ -112,7 +112,7 @@ void irlmp_link_data_indication(struct lap_cb *self, int reliable, */ slsap_sel = fp[0] & LSAP_MASK; dlsap_sel = fp[1]; - + /* * Check if this is an incoming connection, since we must deal with * it in a different way than other established connections. @@ -134,17 +134,17 @@ void irlmp_link_data_indication(struct lap_cb *self, int reliable, self->lsaps); if (lsap == NULL) { - DEBUG(0, "IrLMP, Sorry, no LSAP for received frame!\n"); - DEBUG(0, __FUNCTION__ + DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n"); + DEBUG(2, __FUNCTION__ "(), slsap_sel = %02x, dlsap_sel = %02x\n", slsap_sel, dlsap_sel); if (fp[0] & CONTROL_BIT) { - DEBUG(0, __FUNCTION__ + DEBUG(2, __FUNCTION__ "(), received control frame %02x\n", fp[2]); } else { - DEBUG(0, __FUNCTION__ "(), received data frame\n"); + DEBUG(2, __FUNCTION__ "(), received data frame\n"); } - dev_kfree_skb( skb); + dev_kfree_skb(skb); return; } @@ -224,11 +224,11 @@ void irlmp_link_disconnect_indication(struct lap_cb *lap, * Incoming LAP connection! * */ -void irlmp_link_connect_indication( struct lap_cb *self, __u32 saddr, - __u32 daddr, struct qos_info *qos, - struct sk_buff *skb) +void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, + __u32 daddr, struct qos_info *qos, + struct sk_buff *skb) { - DEBUG( 4, __FUNCTION__ "()\n"); + DEBUG(4, __FUNCTION__ "()\n"); /* Copy QoS settings for this session */ self->qos = qos; @@ -237,7 +237,7 @@ void irlmp_link_connect_indication( struct lap_cb *self, __u32 saddr, self->daddr = daddr; ASSERT(self->saddr == saddr, return;); - irlmp_do_lap_event( self, LM_LAP_CONNECT_INDICATION, skb); + irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb); } /* @@ -246,19 +246,19 @@ void irlmp_link_connect_indication( struct lap_cb *self, __u32 saddr, * LAP connection confirmed! * */ -void irlmp_link_connect_confirm( struct lap_cb *self, struct qos_info *qos, - struct sk_buff *userdata) +void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, + struct sk_buff *userdata) { - DEBUG( 4, "irlmp_link_connect_confirm()\n"); + DEBUG(4, __FUNCTION__ "()\n"); - ASSERT( self != NULL, return;); - ASSERT( self->magic == LMP_LAP_MAGIC, return;); - ASSERT( qos != NULL, return;); + ASSERT(self != NULL, return;); + ASSERT(self->magic == LMP_LAP_MAGIC, return;); + ASSERT(qos != NULL, return;); /* Copy QoS settings for this session */ self->qos = qos; - irlmp_do_lap_event( self, LM_LAP_CONNECT_CONFIRM, NULL); + irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL); } /* @@ -276,7 +276,9 @@ void irlmp_link_discovery_indication(struct lap_cb *self, irlmp_add_discovery(irlmp->cachelog, discovery); /* Just handle it the same way as a discovery confirm */ +#if 0 irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL); +#endif } /* @@ -365,7 +367,7 @@ static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel, #endif return lsap; } - lsap = ( struct lsap_cb *) hashbin_get_next(queue); + lsap = (struct lsap_cb *) hashbin_get_next(queue); } /* Sorry not found! */ diff --git a/net/irda/irlpt/irlpt_cli.c b/net/irda/irlpt/irlpt_cli.c index a0fbe23d6..9e02465cc 100644 --- a/net/irda/irlpt/irlpt_cli.c +++ b/net/irda/irlpt/irlpt_cli.c @@ -51,10 +51,11 @@ static void irlpt_client_discovery_indication(discovery_t *); static void irlpt_client_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb); -static void irlpt_client_disconnect_indication( void *instance, void *sap, - LM_REASON reason, - struct sk_buff *userdata); +static void irlpt_client_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *userdata); static void irlpt_client_expired(unsigned long data); #if 0 @@ -187,7 +188,7 @@ __initfunc(int irlpt_client_init(void)) #ifdef CONFIG_PROC_FS create_proc_entry("irlpt_client", 0, proc_irda)->get_info - = irlpt_client_proc_read; + = irlpt_client_proc_read; #endif /* CONFIG_PROC_FS */ DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n"); @@ -215,7 +216,6 @@ static void irlpt_client_cleanup(void) #ifdef CONFIG_PROC_FS remove_proc_entry("irlpt_client", proc_irda); #endif - DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n"); } #endif /* MODULE */ @@ -403,9 +403,8 @@ static void irlpt_client_disconnect_indication( void *instance, irlpt_client_do_event( self, LMP_DISCONNECT, NULL, NULL); - if (skb) { + if (skb) dev_kfree_skb( skb); - } DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n"); } @@ -417,7 +416,8 @@ static void irlpt_client_disconnect_indication( void *instance, */ static void irlpt_client_connect_confirm(void *instance, void *sap, struct qos_info *qos, - __u32 max_sdu_size, + __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb) { struct irlpt_info info; @@ -443,14 +443,14 @@ static void irlpt_client_connect_confirm(void *instance, void *sap, } #endif - self->irlap_data_size = (qos->data_size.value - IRLPT_MAX_HEADER); + self->max_data_size = max_seg_size; + self->max_header_size = max_header_size; self->connected = TRUE; irlpt_client_do_event( self, LMP_CONNECT, NULL, NULL); - if (skb) { + if (skb) dev_kfree_skb( skb); - } DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n"); } @@ -603,7 +603,7 @@ static void irlpt_client_expired(unsigned long data) return; } - skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(skb, LMP_MAX_HEADER); irlmp_disconnect_request(self->lsap, skb); DEBUG(irlpt_client_debug, __FUNCTION__ ": irlmp_close_slap(self->lsap)\n"); diff --git a/net/irda/irlpt/irlpt_cli_fsm.c b/net/irda/irlpt/irlpt_cli_fsm.c index 75598742a..83a2e6991 100644 --- a/net/irda/irlpt/irlpt_cli_fsm.c +++ b/net/irda/irlpt/irlpt_cli_fsm.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Jan 12 11:06:00 1999 - * Modified at: Tue Jan 26 12:02:31 1999 + * Modified at: Sun May 9 13:36:13 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net> + * Copyright (c) 1998-1999, Thomas Davis, <ratbert@radiks.net> * Copyright (c) 1998, Dag Brattli, <dagb@cs.uit.no> * All Rights Reserved. * @@ -43,10 +43,10 @@ static int irlpt_client_state_query ( struct irlpt_cb *self, IRLPT_EVENT event, struct sk_buff *skb, struct irlpt_info *info); -static int irlpt_client_state_ready ( struct irlpt_cb *self, - IRLPT_EVENT event, - struct sk_buff *skb, - struct irlpt_info *info); +static int irlpt_client_state_ready ( struct irlpt_cb *self, + IRLPT_EVENT event, + struct sk_buff *skb, + struct irlpt_info *info); static int irlpt_client_state_waiti ( struct irlpt_cb *self, IRLPT_EVENT event, struct sk_buff *skb, diff --git a/net/irda/irlpt/irlpt_common.c b/net/irda/irlpt/irlpt_common.c index b4512736a..c6401416b 100644 --- a/net/irda/irlpt/irlpt_common.c +++ b/net/irda/irlpt/irlpt_common.c @@ -251,18 +251,18 @@ ssize_t irlpt_write(struct file *file, const char *buffer, } DEBUG( irlpt_common_debug, __FUNCTION__ - ": count = %d, irlap_data_size = %d, IRLPT_MAX_HEADER = %d\n", - count, self->irlap_data_size, IRLPT_MAX_HEADER); + ": count = %d, max_data_size = %d, IRLPT_MAX_HEADER = %d\n", + count, self->max_data_size, IRLPT_MAX_HEADER); - if (count > (self->irlap_data_size - IRLPT_MAX_HEADER)) { - count = (self->irlap_data_size - IRLPT_MAX_HEADER); + if (count > self->max_data_size) { + count = self->max_data_size; DEBUG(irlpt_common_debug, __FUNCTION__ ": setting count to %d\n", count); } DEBUG( irlpt_common_debug, __FUNCTION__ ": count = %d\n", count); - skb = dev_alloc_skb(count + IRLPT_MAX_HEADER); + skb = dev_alloc_skb(count + self->max_header_size); if ( skb == NULL) { printk( KERN_INFO __FUNCTION__ ": couldn't allocate skbuff!\n"); @@ -417,7 +417,7 @@ int irlpt_close(struct inode *inode, return 0; } - skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve( skb, LMP_MAX_HEADER); irlmp_disconnect_request(self->lsap, skb); DEBUG(irlpt_common_debug, __FUNCTION__ ": irlmp_close_slap(self->lsap)\n"); diff --git a/net/irda/irlpt/irlpt_srvr.c b/net/irda/irlpt/irlpt_srvr.c index a1362d0dc..12e5867a5 100644 --- a/net/irda/irlpt/irlpt_srvr.c +++ b/net/irda/irlpt/irlpt_srvr.c @@ -51,15 +51,21 @@ int irlpt_server_init(void); static void irlpt_server_disconnect_indication(void *instance, void *sap, LM_REASON reason, struct sk_buff *skb); + +#if 0 static void irlpt_server_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb); static void irlpt_server_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb); +#endif + static int irlpt_server_data_indication(void *instance, void *sap, struct sk_buff *skb); static void register_irlpt_server(void); @@ -161,7 +167,6 @@ static int irlpt_server_proc_read(char *buf, char **start, off_t offset, } extern struct proc_dir_entry *proc_irda; - #endif /* CONFIG_PROC_FS */ /* @@ -171,9 +176,9 @@ extern struct proc_dir_entry *proc_irda; * */ -/*int irlpt_init( struct device *dev) {*/ __initfunc(int irlpt_server_init(void)) { + struct irmanager_event mgr_event; __u16 hints; DEBUG( irlpt_server_debug, "--> " __FUNCTION__ "\n"); @@ -212,6 +217,10 @@ __initfunc(int irlpt_server_init(void)) = irlpt_server_proc_read; #endif /* CONFIG_PROC_FS */ + mgr_event.event = EVENT_IRLPT_START; + sprintf(mgr_event.devname, "%s", irlpt_server->ifname); + irmanager_notify(&mgr_event); + DEBUG( irlpt_server_debug, __FUNCTION__ " -->\n"); return 0; @@ -225,6 +234,7 @@ __initfunc(int irlpt_server_init(void)) */ static void irlpt_server_cleanup(void) { + struct irmanager_event mgr_event; struct sk_buff *skb; DEBUG( irlpt_server_debug, "--> " __FUNCTION__ "\n"); @@ -245,6 +255,10 @@ static void irlpt_server_cleanup(void) remove_proc_entry("irlpt_server", proc_irda); #endif + mgr_event.event = EVENT_IRLPT_STOP; + sprintf( mgr_event.devname, "%s", irlpt_server->ifname); + irmanager_notify( &mgr_event); + DEBUG( irlpt_server_debug, __FUNCTION__ " -->\n"); } @@ -304,6 +318,7 @@ static void irlpt_server_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb) { struct irlpt_cb *self; @@ -314,6 +329,9 @@ static void irlpt_server_connect_confirm(void *instance, ASSERT( self != NULL, return;); ASSERT( self->magic == IRLPT_MAGIC, return;); + self->max_data_size = max_seg_size; + self->max_header_size = max_header_size; + self->connected = TRUE; irlpt_server_do_event( self, LMP_CONNECT, NULL, NULL); @@ -329,6 +347,7 @@ static void irlpt_server_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb) { struct irlpt_cb *self; @@ -343,14 +362,16 @@ static void irlpt_server_connect_indication(void *instance, ASSERT( self != NULL, return;); ASSERT( self->magic == IRLPT_MAGIC, return;); + self->max_data_size = max_seg_size; + self->max_header_size = max_header_size; + self->connected = IRLPT_CONNECTED; self->eof = FALSE; irlpt_server_do_event( self, LMP_CONNECT, NULL, &info); - if (skb) { + if (skb) dev_kfree_skb( skb); - } DEBUG( irlpt_server_debug, __FUNCTION__ " -->\n"); } diff --git a/net/irda/irmod.c b/net/irda/irmod.c index 88d61c2cd..ab7354e1d 100644 --- a/net/irda/irmod.c +++ b/net/irda/irmod.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Mon Dec 15 13:55:39 1997 - * Modified at: Mon Apr 12 11:31:01 1999 + * Modified at: Fri May 14 13:46:02 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1997 Dag Brattli, All Rights Reserved. + * Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -45,7 +45,7 @@ #include <net/irda/wrapper.h> #include <net/irda/timer.h> -extern struct proc_dir_entry proc_irda; +extern struct proc_dir_entry *proc_irda; struct irda_cb irda; /* One global instance */ @@ -110,6 +110,7 @@ EXPORT_SYMBOL(irttp_disconnect_request); EXPORT_SYMBOL(irttp_flow_request); EXPORT_SYMBOL(irttp_connect_request); EXPORT_SYMBOL(irttp_udata_request); +EXPORT_SYMBOL(irttp_dup); /* Main IrDA module */ #ifdef CONFIG_IRDA_DEBUG @@ -151,6 +152,7 @@ EXPORT_SYMBOL(irlmp_connect_response); EXPORT_SYMBOL(irlmp_disconnect_request); EXPORT_SYMBOL(irlmp_get_daddr); EXPORT_SYMBOL(irlmp_get_saddr); +EXPORT_SYMBOL(irlmp_dup); EXPORT_SYMBOL(lmp_reasons); /* Queue */ @@ -174,10 +176,15 @@ EXPORT_SYMBOL(irda_device_close); EXPORT_SYMBOL(irda_device_setup); EXPORT_SYMBOL(irda_device_set_media_busy); EXPORT_SYMBOL(irda_device_txqueue_empty); + +EXPORT_SYMBOL(irda_device_init_dongle); +EXPORT_SYMBOL(irda_device_register_dongle); +EXPORT_SYMBOL(irda_device_unregister_dongle); + EXPORT_SYMBOL(async_wrap_skb); EXPORT_SYMBOL(async_unwrap_char); EXPORT_SYMBOL(irda_start_timer); -EXPORT_SYMBOL(irda_get_mtt); +/* EXPORT_SYMBOL(irda_get_mtt); */ EXPORT_SYMBOL(setup_dma); #ifdef CONFIG_IRTTY @@ -505,19 +512,28 @@ void irda_mod_dec_use_count(void) #endif } -#ifdef MODULE -#ifdef CONFIG_PROC_FS +/* + * Function irda_proc_modcount (inode, fill) + * + * Use by the proc file system functions to prevent the irda module + * being removed while the use is standing in the net/irda directory + */ void irda_proc_modcount(struct inode *inode, int fill) { +#ifdef MODULE +#ifdef CONFIG_PROC_FS if (fill) MOD_INC_USE_COUNT; else MOD_DEC_USE_COUNT; -} #endif /* CONFIG_PROC_FS */ +#endif /* MODULE */ +} + +#ifdef MODULE MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); -MODULE_DESCRIPTION("The Linux IrDA protocol subsystem"); +MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem"); MODULE_PARM(irda_debug, "1l"); /* diff --git a/net/irda/irproc.c b/net/irda/irproc.c index f3b710b95..a04951694 100644 --- a/net/irda/irproc.c +++ b/net/irda/irproc.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Thomas Davis, <ratbert@radiks.net> * Created at: Sat Feb 21 21:33:24 1998 - * Modified at: Tue Apr 6 19:07:06 1999 + * Modified at: Fri May 7 08:06:49 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>, + * Copyright (c) 1998-1999, Thomas Davis, <ratbert@radiks.net>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -20,8 +20,6 @@ * I, Thomas Davis, provide no warranty for any of this software. * This material is provided "AS-IS" and at no charge. * - * Portions lifted from the linux/fs/procfs/ files. - * ********************************************************************/ #include <linux/miscdevice.h> @@ -44,28 +42,27 @@ extern int irias_proc_read(char *buf, char **start, off_t offset, int len, int unused); extern int discovery_proc_read(char *buf, char **start, off_t offset, int len, int unused); +static int proc_discovery_read(char *buf, char **start, off_t offset, int len, + int unused); -enum irda_directory_inos { - PROC_IRDA_LAP = 1, - PROC_IRDA_LMP, - PROC_IRDA_TTP, - PROC_IRDA_LPT, - PROC_IRDA_COMM, - PROC_IRDA_IRDA_DEVICE, - PROC_IRDA_IRIAS -}; +/* enum irda_directory_inos { */ +/* PROC_IRDA_LAP = 1, */ +/* PROC_IRDA_LMP, */ +/* PROC_IRDA_TTP, */ +/* PROC_IRDA_LPT, */ +/* PROC_IRDA_COMM, */ +/* PROC_IRDA_IRDA_DEVICE, */ +/* PROC_IRDA_IRIAS */ +/* }; */ struct irda_entry { char *name; - int (*fn)(char*,char**,off_t,int,int); + int (*fn)(char*, char**, off_t, int, int); }; struct proc_dir_entry *proc_irda; - + static struct irda_entry dir[] = { -#if 0 - {"lpt", irlpt_proc_read}, -#endif {"discovery", discovery_proc_read}, {"irda_device", irda_device_proc_read}, {"irttp", irttp_proc_read}, @@ -75,19 +72,22 @@ static struct irda_entry dir[] = { }; #define IRDA_ENTRIES_NUM (sizeof(dir)/sizeof(dir[0])) - + /* * Function irda_proc_register (void) * * Register irda entry in /proc file system * */ -void irda_proc_register(void) { +void irda_proc_register(void) +{ int i; + proc_irda = create_proc_entry("net/irda", S_IFDIR, NULL); #ifdef MODULE proc_irda->fill_inode = &irda_proc_modcount; #endif /* MODULE */ + for (i=0;i<IRDA_ENTRIES_NUM;i++) create_proc_entry(dir[i].name,0,proc_irda)->get_info=dir[i].fn; } @@ -98,9 +98,14 @@ void irda_proc_register(void) { * Unregister irda entry in /proc file system * */ -void irda_proc_unregister(void) { +void irda_proc_unregister(void) +{ int i; + for (i=0;i<IRDA_ENTRIES_NUM;i++) remove_proc_entry(dir[i].name, proc_irda); + remove_proc_entry("net/irda", NULL); } + + diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index 0b9a4f189..e82c2edd3 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -1,15 +1,15 @@ /********************************************************************* * * Filename: irsysctl.c - * Version: - * Description: + * Version: 1.0 + * Description: Sysctl interface for IrDA * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun May 24 22:12:06 1998 - * Modified at: Fri Apr 23 09:46:38 1999 + * Modified at: Thu May 6 21:32:46 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1997 Dag Brattli, All Rights Reserved. + * Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/net/irda/irttp.c b/net/irda/irttp.c index bf0624eee..8d5e31f41 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -6,10 +6,10 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:31 1997 - * Modified at: Sat Apr 10 10:32:21 1999 + * Modified at: Mon May 31 10:29:56 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -35,7 +35,7 @@ #include <net/irda/irlmp.h> #include <net/irda/irttp.h> -struct irttp_cb *irttp = NULL; +static struct irttp_cb *irttp = NULL; static void __irttp_close_tsap(struct tsap_cb *self); @@ -44,19 +44,20 @@ static int irttp_data_indication(void *instance, void *sap, static int irttp_udata_indication(void *instance, void *sap, struct sk_buff *skb); static void irttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *); + LM_REASON reason, struct sk_buff *); static void irttp_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, - struct sk_buff *skb); - + __u8 header_size, struct sk_buff *skb); +static void irttp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, __u32 max_sdu_size, + __u8 header_size, struct sk_buff *skb); static void irttp_run_tx_queue(struct tsap_cb *self); static void irttp_run_rx_queue(struct tsap_cb *self); static void irttp_flush_queues(struct tsap_cb *self); static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb); -static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self); static void irttp_start_todo_timer(struct tsap_cb *self, int timeout); +static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self); /* * Function irttp_init (void) @@ -296,7 +297,7 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) /* Check that nothing bad happens */ if ((skb->len == 0) || (!self->connected)) { - DEBUG(4, __FUNCTION__ "(), No data, or not connected\n"); + ERROR(__FUNCTION__ "(), No data, or not connected\n"); return -ENOTCONN; } @@ -305,8 +306,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) * inside an IrLAP frame */ if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { - DEBUG(1, __FUNCTION__ - "(), SAR disabled, and data is to large for IrLAP!\n"); + ERROR(__FUNCTION__ + "(), SAR disabled, and data is to large for IrLAP!\n"); return -EMSGSIZE; } @@ -318,8 +319,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) (self->tx_max_sdu_size != SAR_UNBOUND) && (skb->len > self->tx_max_sdu_size)) { - DEBUG(1, __FUNCTION__ "(), SAR enabled, " - "but data is larger than TxMaxSduSize!\n"); + ERROR(__FUNCTION__ "(), SAR enabled, " + "but data is larger than TxMaxSduSize!\n"); return -EMSGSIZE; } /* @@ -337,10 +338,10 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) /* Queue frame, or queue frame segments */ if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) { /* Queue frame */ + ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;); frame = skb_push(skb, TTP_HEADER); frame[0] = 0x00; /* Clear more bit */ - DEBUG(4, __FUNCTION__ "(), queueing original skb\n"); skb_queue_tail(&self->tx_queue, skb); } else { /* @@ -360,8 +361,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) self->tx_sdu_busy = TRUE; if (self->notify.flow_indication) { - self->notify.flow_indication( - self->notify.instance, self, FLOW_STOP); + self->notify.flow_indication(self->notify.instance, + self, FLOW_STOP); } } @@ -381,12 +382,8 @@ static void irttp_run_tx_queue(struct tsap_cb *self) { struct sk_buff *skb = NULL; unsigned long flags; - __u8 *frame; int n; - ASSERT(self != NULL, return;); - ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - if (irda_lock(&self->tx_queue_lock) == FALSE) return; @@ -421,12 +418,7 @@ static void irttp_run_tx_queue(struct tsap_cb *self) * More bit must be set by the data_request() or fragment() * functions */ - frame = skb->data; - - DEBUG(4, __FUNCTION__ "(), More=%s\n", frame[0] & 0x80 ? - "TRUE" : "FALSE" ); - - frame[0] |= (__u8) (n & 0x7f); + skb->data[0] |= (n & 0x7f); irlmp_data_request(self->lsap, skb); self->stats.tx_packets++; @@ -434,12 +426,12 @@ static void irttp_run_tx_queue(struct tsap_cb *self) /* Check if we can accept more frames from client */ if ((self->tx_sdu_busy) && (skb_queue_len(&self->tx_queue) < LOW_THRESHOLD)) - { + { self->tx_sdu_busy = FALSE; if (self->notify.flow_indication) self->notify.flow_indication( - self->notify.instance, self, + self->notify.instance, self, FLOW_START); } } @@ -472,7 +464,7 @@ void irttp_give_credit(struct tsap_cb *self) return; /* Reserve space for LMP, and LAP header */ - skb_reserve(tx_skb, LMP_HEADER+LAP_HEADER); + skb_reserve(tx_skb, self->max_header_size); /* * Since we can transmit and receive frames concurrently, @@ -538,23 +530,14 @@ static int irttp_data_indication(void *instance, void *sap, struct sk_buff *skb) { struct tsap_cb *self; - int more; int n; - __u8 *frame; - + self = (struct tsap_cb *) instance; ASSERT(self != NULL, return -1;); ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - ASSERT(skb != NULL, return -1;); - frame = skb->data; - - n = frame[0] & 0x7f; /* Extract the credits */ - more = frame[0] & 0x80; - - DEBUG(3, __FUNCTION__"(), got %d credits, TSAP sel=%02x\n", - n, self->stsap_sel); + n = skb->data[0] & 0x7f; /* Extract the credits */ self->stats.rx_packets++; @@ -562,10 +545,9 @@ static int irttp_data_indication(void *instance, void *sap, * Data or dataless frame? Dataless frames only contain the * TTP_HEADER */ - if (skb->len == 1) { - /* Dataless flowdata TTP-PDU */ - self->send_credit += n; - } else { + if (skb->len == 1) + self->send_credit += n; /* Dataless flowdata TTP-PDU */ + else { /* Deal with inbound credit */ self->send_credit += n; self->remote_credit--; @@ -655,15 +637,14 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, return -ENOMEM; /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(skb, (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER)); + skb_reserve(skb, TTP_MAX_HEADER); } else { skb = userdata; /* * Check that the client has reserved enough space for * headers */ - ASSERT(skb_headroom(userdata) >= - (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER), return -1;); + ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, return -1;); } /* Initialize connection parameters */ @@ -691,12 +672,11 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, /* SAR enabled? */ if (max_sdu_size > 0) { - ASSERT(skb_headroom(skb) >= - (TTP_HEADER_WITH_SAR+LMP_CONTROL_HEADER+LAP_HEADER), - return -1;); + ASSERT(skb_headroom(skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), + return -1;); /* Insert SAR parameters */ - frame = skb_push(skb, TTP_HEADER_WITH_SAR); + frame = skb_push(skb, TTP_HEADER+TTP_SAR_HEADER); frame[0] = TTP_PARAMETERS | n; frame[1] = 0x04; /* Length */ @@ -724,8 +704,10 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, * Sevice user confirms TSAP connection with peer. * */ -void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos, - __u32 max_seg_size, struct sk_buff *skb) +static void irttp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_seg_size, __u8 max_header_size, + struct sk_buff *skb) { struct tsap_cb *self; int parameters; @@ -741,7 +723,8 @@ void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos, ASSERT(self->magic == TTP_TSAP_MAGIC, return;); ASSERT(skb != NULL, return;); - self->max_seg_size = max_seg_size-LMP_HEADER-LAP_HEADER; + self->max_seg_size = max_seg_size; + self->max_header_size = max_header_size + TTP_HEADER; /* * Check if we have got some QoS parameters back! This should be the @@ -764,6 +747,10 @@ void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos, self->connected = TRUE; parameters = frame[0] & 0x80; + + ASSERT(skb->len >= TTP_HEADER, return;); + skb_pull(skb, TTP_HEADER); + if (parameters) { plen = frame[1]; pi = frame[2]; @@ -789,17 +776,19 @@ void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos, DEBUG(4, __FUNCTION__ "(), RxMaxSduSize=%d\n", self->tx_max_sdu_size); + + /* Remove parameters */ + ASSERT(skb->len >= (plen+1), return;); + skb_pull(skb, plen+1); } DEBUG(4, __FUNCTION__ "() send=%d,avail=%d,remote=%d\n", self->send_credit, self->avail_credit, self->remote_credit); - skb_pull(skb, TTP_HEADER); - if (self->notify.connect_confirm) { - self->notify.connect_confirm(self->notify.instance, self, - qos, self->tx_max_sdu_size, - skb); + self->notify.connect_confirm(self->notify.instance, self, qos, + self->tx_max_sdu_size, + self->max_header_size, skb); } } @@ -809,8 +798,8 @@ void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos, * Some other device is connecting to this TSAP * */ -void irttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, +void irttp_connect_indication(void *instance, void *sap, struct qos_info *qos, + __u32 max_seg_size, __u8 max_header_size, struct sk_buff *skb) { struct tsap_cb *self; @@ -828,7 +817,9 @@ void irttp_connect_indication(void *instance, void *sap, lsap = (struct lsap_cb *) sap; - self->max_seg_size = max_seg_size-LMP_HEADER-LAP_HEADER; + self->max_seg_size = max_seg_size; + + self->max_header_size = max_header_size+TTP_HEADER; DEBUG(4, __FUNCTION__ "(), TSAP sel=%02x\n", self->stsap_sel); @@ -841,7 +832,11 @@ void irttp_connect_indication(void *instance, void *sap, self->send_credit = n; self->tx_max_sdu_size = 0; - parameters = frame[0] & 0x80; + parameters = frame[0] & 0x80; + + ASSERT(skb->len >= TTP_HEADER, return;); + skb_pull(skb, TTP_HEADER); + if (parameters) { DEBUG(3, __FUNCTION__ "(), Contains parameters!\n"); plen = frame[1]; @@ -850,7 +845,7 @@ void irttp_connect_indication(void *instance, void *sap, switch (pl) { case 1: - self->tx_max_sdu_size = *(frame+4); + self->tx_max_sdu_size = frame[4]; break; case 2: self->tx_max_sdu_size = @@ -865,7 +860,10 @@ void irttp_connect_indication(void *instance, void *sap, "() illegal value length for max_sdu_size!\n"); self->tx_max_sdu_size = 0; }; - + + /* Remove parameters */ + ASSERT(skb->len >= (plen+1), return;); + skb_pull(skb, plen+1); DEBUG(3, __FUNCTION__ "(), MaxSduSize=%d\n", self->tx_max_sdu_size); @@ -873,12 +871,10 @@ void irttp_connect_indication(void *instance, void *sap, DEBUG(4, __FUNCTION__ "(), initial send_credit=%d\n", n); - skb_pull(skb, 1); /* Remove TTP header */ - if (self->notify.connect_indication) { self->notify.connect_indication(self->notify.instance, self, qos, self->rx_max_sdu_size, - skb); + self->max_header_size, skb); } } @@ -909,15 +905,14 @@ void irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, return; /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(skb, (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER)); + skb_reserve(skb, TTP_MAX_HEADER); } else { skb = userdata; /* * Check that the client has reserved enough space for * headers */ - ASSERT(skb_headroom(skb) >= - (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER), return;); + ASSERT(skb_headroom(skb) >= TTP_MAX_HEADER, return;); } self->avail_credit = 0; @@ -939,12 +934,11 @@ void irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, /* SAR enabled? */ if (max_sdu_size > 0) { - ASSERT(skb_headroom(skb) >= - (TTP_HEADER_WITH_SAR+LMP_CONTROL_HEADER+LAP_HEADER), + ASSERT(skb_headroom(skb) >= (TTP_MAX_HEADER+TTP_SAR_HEADER), return;); /* Insert TTP header with SAR parameters */ - frame = skb_push(skb, TTP_HEADER_WITH_SAR); + frame = skb_push(skb, TTP_HEADER+TTP_SAR_HEADER); frame[0] = TTP_PARAMETERS | n; frame[1] = 0x04; /* Length */ @@ -1079,7 +1073,7 @@ void irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, /* * Reserve space for MUX and LAP header */ - skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER); + skb_reserve(skb, TTP_MAX_HEADER); userdata = skb; } @@ -1357,13 +1351,11 @@ static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb) } /* Make new segment */ - frag = dev_alloc_skb(self->max_seg_size+ - TTP_HEADER+LMP_HEADER+ - LAP_HEADER); + frag = dev_alloc_skb(self->max_seg_size+self->max_header_size); if (!frag) return; - skb_reserve(frag, LMP_HEADER+LAP_HEADER); + skb_reserve(frag, self->max_header_size); /* * Copy data from the original skb into this fragment. We @@ -1401,11 +1393,9 @@ static void irttp_todo_expired(unsigned long data) irttp_run_tx_queue(self); /* Give avay some credits to peer? */ - if ((skb_queue_empty(&self->tx_queue)) && - (self->remote_credit < LOW_THRESHOLD) && - (self->avail_credit > 0)) + if ((self->remote_credit < LOW_THRESHOLD) && + (self->avail_credit > 0) && (skb_queue_empty(&self->tx_queue))) { - DEBUG(4, __FUNCTION__ "(), sending credit!\n"); irttp_give_credit(self); } diff --git a/net/irda/qos.c b/net/irda/qos.c index 7b226dfa6..82f7fc28a 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -6,10 +6,11 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Sep 9 00:00:26 1997 - * Modified at: Mon Apr 12 11:49:24 1999 + * Modified at: Mon May 3 21:15:08 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved. + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, + * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -52,10 +53,10 @@ int compression[] = { CI_BZIP2, CI_DEFLATE, CI_DEFLATE_DRAFT }; * Compute the intersection of the old QoS capabilites with new ones * */ -void irda_qos_compute_intersection( struct qos_info *qos, struct qos_info *new) +void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new) { - ASSERT( qos != NULL, return;); - ASSERT( new != NULL, return;); + ASSERT(qos != NULL, return;); + ASSERT(new != NULL, return;); /* Apply */ qos->baud_rate.bits &= new->baud_rate.bits; diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c index c4822e2c6..2ddf84efa 100644 --- a/net/irda/wrapper.c +++ b/net/irda/wrapper.c @@ -1,15 +1,17 @@ /********************************************************************* * * Filename: wrapper.c - * Version: 1.1 - * Description: SIR wrapper layer + * Version: 1.2 + * Description: IrDA SIR async wrapper layer * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Wed Apr 21 12:45:55 1999 + * Modified at: Fri May 28 20:30:24 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> + * Modified at: Fri May 28 3:11 CST 1999 + * Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl> * - * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, + * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -24,6 +26,7 @@ ********************************************************************/ #include <linux/skbuff.h> +#include <linux/string.h> #include <asm/byteorder.h> #include <net/irda/irda.h> @@ -34,7 +37,20 @@ #include <net/irda/irlap_frame.h> #include <net/irda/irda_device.h> -inline static int stuff_byte(__u8 byte, __u8 *buf); +static inline int stuff_byte(__u8 byte, __u8 *buf); + +static void state_outside_frame(struct irda_device *idev, __u8 byte); +static void state_begin_frame(struct irda_device *idev, __u8 byte); +static void state_link_escape(struct irda_device *idev, __u8 byte); +static void state_inside_frame(struct irda_device *idev, __u8 byte); + +static void (*state[])(struct irda_device *idev, __u8 byte) = +{ + state_outside_frame, + state_begin_frame, + state_link_escape, + state_inside_frame, +}; /* * Function async_wrap (skb, *tx_buff) @@ -52,8 +68,6 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) __u8 bytes[2]; } fcs; - ASSERT(skb != NULL, return 0;); - /* Initialize variables */ fcs.value = INIT_FCS; n = 0; @@ -74,13 +88,9 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) } else xbofs = ((struct irlap_skb_cb *)(skb->cb))->xbofs; -#if 0 - for (i=0; i<xbofs; i++) - tx_buff[n++] = XBOF; -#else memset(tx_buff+n, XBOF, xbofs); n += xbofs; -#endif + /* Start of packet character BOF */ tx_buff[n++] = BOF; @@ -94,7 +104,7 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) ASSERT(n < (buffsize-5), return n;); n += stuff_byte(skb->data[i], tx_buff+n); - fcs.value = IR_FCS(fcs.value, skb->data[i]); + fcs.value = irda_fcs(fcs.value, skb->data[i]); } /* Insert CRC in little endian format (LSB first) */ @@ -108,15 +118,6 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) #endif tx_buff[n++] = EOF; -#if 0 - { - int i; - - for (i=0;i<n;i++) - printk("%02x", tx_buff[i]); - printk("\n"); - } -#endif return n; } @@ -155,147 +156,13 @@ static inline void async_bump(struct irda_device *idev, __u8 *buf, int len) } /* - * Function async_unwrap (skb) - * - * Parse and de-stuff frame received from the IR-port - * - */ -void async_unwrap_char(struct irda_device *idev, __u8 byte) -{ - /* State machine for receiving frames */ - switch (idev->rx_buff.state) { - case OUTSIDE_FRAME: - switch(byte) { - case BOF: - idev->rx_buff.state = BEGIN_FRAME; - idev->rx_buff.in_frame = TRUE; - break; - case XBOF: - /* idev->xbofs++; */ - break; - case EOF: - irda_device_set_media_busy( idev, TRUE); - break; - default: - break; - } - break; - case BEGIN_FRAME: - switch (byte) { - case BOF: - /* Continue */ - break; - case CE: - /* Stuffed byte */ - idev->rx_buff.state = LINK_ESCAPE; - break; - case EOF: - /* Abort frame */ - idev->rx_buff.state = OUTSIDE_FRAME; - - idev->stats.rx_errors++; - idev->stats.rx_frame_errors++; - break; - default: - /* Got first byte of frame */ - idev->rx_buff.data = idev->rx_buff.head; - idev->rx_buff.len = 0; - - idev->rx_buff.data[idev->rx_buff.len++] = byte; - - idev->rx_buff.fcs = IR_FCS(INIT_FCS, byte); - idev->rx_buff.state = INSIDE_FRAME; - break; - } - break; - case LINK_ESCAPE: - switch (byte) { - case BOF: - /* New frame? */ - idev->rx_buff.state = BEGIN_FRAME; - irda_device_set_media_busy(idev, TRUE); - break; - case CE: - DEBUG(4, "WARNING: State not defined\n"); - break; - case EOF: - /* Abort frame */ - idev->rx_buff.state = OUTSIDE_FRAME; - break; - default: - /* - * Stuffed char, complement bit 5 of byte - * following CE, IrLAP p.114 - */ - byte ^= IR_TRANS; - if (idev->rx_buff.len < idev->rx_buff.truesize) { - idev->rx_buff.data[idev->rx_buff.len++] = byte; - idev->rx_buff.fcs = IR_FCS(idev->rx_buff.fcs, - byte); - idev->rx_buff.state = INSIDE_FRAME; - } else { - DEBUG(1, __FUNCTION__ - "(), Rx buffer overflow, aborting\n"); - idev->rx_buff.state = OUTSIDE_FRAME; - } - break; - } - break; - case INSIDE_FRAME: - switch (byte) { - case BOF: - /* New frame? */ - idev->rx_buff.state = BEGIN_FRAME; - irda_device_set_media_busy(idev, TRUE); - break; - case CE: - /* Stuffed char */ - idev->rx_buff.state = LINK_ESCAPE; - break; - case EOF: - /* End of frame */ - idev->rx_buff.state = OUTSIDE_FRAME; - idev->rx_buff.in_frame = FALSE; - - /* - * Test FCS and deliver frame if it's good - */ - if (idev->rx_buff.fcs == GOOD_FCS) { - async_bump(idev, idev->rx_buff.data, - idev->rx_buff.len); - } else { - /* Wrong CRC, discard frame! */ - irda_device_set_media_busy(idev, TRUE); - - idev->stats.rx_errors++; - idev->stats.rx_crc_errors++; - } - break; - default: - /* Next byte of frame */ - if (idev->rx_buff.len < idev->rx_buff.truesize) { - idev->rx_buff.data[idev->rx_buff.len++] = byte; - idev->rx_buff.fcs = IR_FCS(idev->rx_buff.fcs, - byte); - } else { - DEBUG(1, __FUNCTION__ - "(), Rx buffer overflow, aborting\n"); - idev->rx_buff.state = OUTSIDE_FRAME; - } - break; - } - break; - } -} - -/* * Function stuff_byte (byte, buf) * * Byte stuff one single byte and put the result in buffer pointed to by * buf. The buffer must at all times be able to have two bytes inserted. * */ -inline static int stuff_byte(__u8 byte, __u8 *buf) +static inline int stuff_byte(__u8 byte, __u8 *buf) { switch (byte) { case BOF: /* FALLTHROUGH */ @@ -303,7 +170,7 @@ inline static int stuff_byte(__u8 byte, __u8 *buf) case CE: /* Insert transparently coded */ buf[0] = CE; /* Send link escape */ - buf[1] = byte^IR_TRANS; /* Complement bit 5 */ + buf[1] = byte^IRDA_TRANS; /* Complement bit 5 */ return 2; /* break; */ default: @@ -313,7 +180,163 @@ inline static int stuff_byte(__u8 byte, __u8 *buf) /* break; */ } } + +/* + * Function async_unwrap (skb) + * + * Parse and de-stuff frame received from the IrDA-port + * + */ +inline void async_unwrap_char(struct irda_device *idev, __u8 byte) +{ + (*state[idev->rx_buff.state]) (idev, byte); +} +/* + * Function state_outside_frame (idev, byte) + * + * + * + */ +static void state_outside_frame(struct irda_device *idev, __u8 byte) +{ + switch (byte) { + case BOF: + idev->rx_buff.state = BEGIN_FRAME; + idev->rx_buff.in_frame = TRUE; + break; + case XBOF: + /* idev->xbofs++; */ + break; + case EOF: + irda_device_set_media_busy( idev, TRUE); + break; + default: + break; + } +} +/* + * Function state_begin_frame (idev, byte) + * + * Begin of frame detected + * + */ +static void state_begin_frame(struct irda_device *idev, __u8 byte) +{ + switch (byte) { + case BOF: + /* Continue */ + break; + case CE: + /* Stuffed byte */ + idev->rx_buff.state = LINK_ESCAPE; + + /* Time to initialize receive buffer */ + idev->rx_buff.data = idev->rx_buff.head; + idev->rx_buff.len = 0; + break; + case EOF: + /* Abort frame */ + idev->rx_buff.state = OUTSIDE_FRAME; + + idev->stats.rx_errors++; + idev->stats.rx_frame_errors++; + break; + default: + /* Time to initialize receive buffer */ + idev->rx_buff.data = idev->rx_buff.head; + idev->rx_buff.len = 0; + + idev->rx_buff.data[idev->rx_buff.len++] = byte; + + idev->rx_buff.fcs = irda_fcs(INIT_FCS, byte); + idev->rx_buff.state = INSIDE_FRAME; + break; + } +} + +/* + * Function state_link_escape (idev, byte) + * + * + * + */ +static void state_link_escape(struct irda_device *idev, __u8 byte) +{ + switch (byte) { + case BOF: /* New frame? */ + idev->rx_buff.state = BEGIN_FRAME; + irda_device_set_media_busy(idev, TRUE); + break; + case CE: + DEBUG(4, "WARNING: State not defined\n"); + break; + case EOF: /* Abort frame */ + idev->rx_buff.state = OUTSIDE_FRAME; + break; + default: + /* + * Stuffed char, complement bit 5 of byte + * following CE, IrLAP p.114 + */ + byte ^= IRDA_TRANS; + if (idev->rx_buff.len < idev->rx_buff.truesize) { + idev->rx_buff.data[idev->rx_buff.len++] = byte; + idev->rx_buff.fcs = irda_fcs(idev->rx_buff.fcs, byte); + idev->rx_buff.state = INSIDE_FRAME; + } else { + DEBUG(1, __FUNCTION__ + "(), Rx buffer overflow, aborting\n"); + idev->rx_buff.state = OUTSIDE_FRAME; + } + break; + } +} + +/* + * Function state_inside_frame (idev, byte) + * + * Handle bytes received within a frame + * + */ +static void state_inside_frame(struct irda_device *idev, __u8 byte) +{ + switch (byte) { + case BOF: /* New frame? */ + idev->rx_buff.state = BEGIN_FRAME; + irda_device_set_media_busy(idev, TRUE); + break; + case CE: /* Stuffed char */ + idev->rx_buff.state = LINK_ESCAPE; + break; + case EOF: /* End of frame */ + idev->rx_buff.state = OUTSIDE_FRAME; + idev->rx_buff.in_frame = FALSE; + + /* Test FCS and deliver frame if it's good */ + if (idev->rx_buff.fcs == GOOD_FCS) { + async_bump(idev, idev->rx_buff.data, + idev->rx_buff.len); + } else { + /* Wrong CRC, discard frame! */ + irda_device_set_media_busy(idev, TRUE); + + idev->stats.rx_errors++; + idev->stats.rx_crc_errors++; + } + break; + default: /* Must be the next byte of the frame */ + if (idev->rx_buff.len < idev->rx_buff.truesize) { + idev->rx_buff.data[idev->rx_buff.len++] = byte; + idev->rx_buff.fcs = irda_fcs(idev->rx_buff.fcs, byte); + } else { + DEBUG(1, __FUNCTION__ + "(), Rx buffer overflow, aborting\n"); + idev->rx_buff.state = OUTSIDE_FRAME; + } + break; + } +} diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 9247bf99c..eed55f8ac 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1,7 +1,7 @@ /* * NETLINK Kernel-user communication protocol. * - * Authors: Alan Cox <alan@cymru.net> + * Authors: Alan Cox <alan@redhat.com> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * * This program is free software; you can redistribute it and/or @@ -45,7 +45,7 @@ static struct sock *nl_table[MAX_LINKS]; static atomic_t nl_table_lock[MAX_LINKS]; -static struct wait_queue *nl_table_wait; +static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); #ifdef NL_EMULATE_DEV static struct socket *netlink_kernel[MAX_LINKS]; @@ -203,7 +203,7 @@ static int netlink_release(struct socket *sock, struct socket *peer) */ while (netlink_locked(sk)) { - current->counter = 0; + current->policy |= SCHED_YIELD; schedule(); } @@ -331,7 +331,7 @@ int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock struct sock *sk; int len = skb->len; int protocol = ssk->protocol; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); retry: for (sk = nl_table[protocol]; sk; sk = sk->next) { diff --git a/net/netlink/netlink_dev.c b/net/netlink/netlink_dev.c index b127137b2..c4b600729 100644 --- a/net/netlink/netlink_dev.c +++ b/net/netlink/netlink_dev.c @@ -2,7 +2,7 @@ * NETLINK An implementation of a loadable kernel mode driver providing * multiple kernel/user space bidirectional communications links. * - * Author: Alan Cox <alan@cymru.net> + * Author: Alan Cox <alan@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index d46e45eb6..fa2167a46 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -564,10 +564,13 @@ struct device *nr_dev_first(void) { struct device *dev, *first = NULL; - for (dev = dev_base; dev != NULL; dev = dev->next) + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM) if (first == NULL || strncmp(dev->name, first->name, 3) < 0) first = dev; + } + read_unlock(&dev_base_lock); return first; } @@ -579,11 +582,14 @@ struct device *nr_dev_get(ax25_address *addr) { struct device *dev; - for (dev = dev_base; dev != NULL; dev = dev->next) + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) - return dev; - - return NULL; + goto out; + } +out: + read_unlock(&dev_base_lock); + return dev; } static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) diff --git a/net/netsyms.c b/net/netsyms.c index 764900d50..021e17ced 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -106,10 +106,17 @@ EXPORT_SYMBOL(dev_lockct); EXPORT_SYMBOL(skb_over_panic); EXPORT_SYMBOL(skb_under_panic); +/* Socket layer global data */ +EXPORT_SYMBOL(sockhash_lock); + /* Socket layer registration */ EXPORT_SYMBOL(sock_register); EXPORT_SYMBOL(sock_unregister); +/* Socket locking */ +EXPORT_SYMBOL(lock_sock); +EXPORT_SYMBOL(release_sock); + /* Socket layer support routines */ EXPORT_SYMBOL(memcpy_fromiovec); EXPORT_SYMBOL(memcpy_tokerneliovec); @@ -156,7 +163,6 @@ EXPORT_SYMBOL(put_cmsg); EXPORT_SYMBOL(net_families); EXPORT_SYMBOL(sock_kmalloc); EXPORT_SYMBOL(sock_kfree_s); -EXPORT_SYMBOL(skb_queue_lock); #ifdef CONFIG_FILTER EXPORT_SYMBOL(sk_run_filter); @@ -164,7 +170,6 @@ EXPORT_SYMBOL(sk_run_filter); EXPORT_SYMBOL(neigh_table_init); EXPORT_SYMBOL(neigh_table_clear); -EXPORT_SYMBOL(__neigh_lookup); EXPORT_SYMBOL(neigh_resolve_output); EXPORT_SYMBOL(neigh_connected_output); EXPORT_SYMBOL(neigh_update); @@ -187,7 +192,6 @@ EXPORT_SYMBOL(neigh_rand_reach_time); /* dst_entry */ EXPORT_SYMBOL(dst_alloc); EXPORT_SYMBOL(__dst_free); -EXPORT_SYMBOL(dst_total); EXPORT_SYMBOL(dst_destroy); /* misc. support routines */ @@ -243,7 +247,6 @@ EXPORT_SYMBOL(ip_mc_dec_group); EXPORT_SYMBOL(__ip_finish_output); EXPORT_SYMBOL(inet_dgram_ops); EXPORT_SYMBOL(ip_cmsg_recv); -EXPORT_SYMBOL(__release_sock); /* Route manipulation */ EXPORT_SYMBOL(ip_rt_ioctl); @@ -279,9 +282,11 @@ EXPORT_SYMBOL(inet_recvmsg); /* Socket demultiplexing. */ EXPORT_SYMBOL(tcp_good_socknum); -EXPORT_SYMBOL(tcp_established_hash); +EXPORT_SYMBOL(tcp_ehash); +EXPORT_SYMBOL(tcp_ehash_size); EXPORT_SYMBOL(tcp_listening_hash); -EXPORT_SYMBOL(tcp_bound_hash); +EXPORT_SYMBOL(tcp_bhash); +EXPORT_SYMBOL(tcp_bhash_size); EXPORT_SYMBOL(udp_good_socknum); EXPORT_SYMBOL(udp_hash); @@ -382,8 +387,7 @@ EXPORT_SYMBOL(neigh_dump_info); EXPORT_SYMBOL(dev_set_allmulti); EXPORT_SYMBOL(dev_set_promiscuity); EXPORT_SYMBOL(sklist_remove_socket); -EXPORT_SYMBOL(rtnl_wait); -EXPORT_SYMBOL(rtnl_rlockct); +EXPORT_SYMBOL(rtnl_sem); EXPORT_SYMBOL(rtnl_lock); EXPORT_SYMBOL(rtnl_unlock); @@ -470,6 +474,7 @@ EXPORT_SYMBOL(netdev_unregister_fc); EXPORT_SYMBOL(netdev_fc_xoff); #endif EXPORT_SYMBOL(dev_base); +EXPORT_SYMBOL(dev_base_lock); EXPORT_SYMBOL(dev_close); EXPORT_SYMBOL(dev_mc_add); EXPORT_SYMBOL(dev_mc_delete); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e78e41352..fbfe95482 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -5,7 +5,7 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.19 1999/03/21 05:23:03 davem Exp $ + * Version: $Id: af_packet.c,v 1.20 1999/06/09 10:11:32 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -286,26 +286,27 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len, else return(-ENOTCONN); /* SOCK_PACKET must be sent giving an address */ + dev_lock_list(); + /* * Find the device first to size check it */ saddr->spkt_device[13] = 0; dev = dev_get(saddr->spkt_device); - if (dev == NULL) - { - return(-ENODEV); - } + err = -ENODEV; + if (dev == NULL) + goto out_unlock; /* * You may not queue a frame bigger than the mtu. This is the lowest level * raw protocol and you must do your own fragmentation at this level. */ - if(len>dev->mtu+dev->hard_header_len) - return -EMSGSIZE; + err = -EMSGSIZE; + if(len>dev->mtu+dev->hard_header_len) + goto out_unlock; - dev_lock_list(); err = -ENOBUFS; skb = sock_wmalloc(sk, len+dev->hard_header_len+15, 0, GFP_KERNEL); @@ -351,8 +352,8 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, int len, * Now send it */ - dev_unlock_list(); dev_queue_xmit(skb); + dev_unlock_list(); return(len); out_free: @@ -455,16 +456,18 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len, addr = saddr->sll_addr; } + dev_lock_list(); dev = dev_get_by_index(ifindex); + err = -ENXIO; if (dev == NULL) - return -ENXIO; + goto out_unlock; if (sock->type == SOCK_RAW) reserve = dev->hard_header_len; + err = -EMSGSIZE; if (len > dev->mtu+reserve) - return -EMSGSIZE; + goto out_unlock; - dev_lock_list(); skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, msg->msg_flags & MSG_DONTWAIT, &err); @@ -501,8 +504,8 @@ static int packet_sendmsg(struct socket *sock, struct msghdr *msg, int len, * Now send it */ - dev_unlock_list(); dev_queue_xmit(skb); + dev_unlock_list(); return(len); out_free: diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 1fad6b7cc..28a6f8adb 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -543,10 +543,13 @@ struct device *rose_dev_first(void) { struct device *dev, *first = NULL; - for (dev = dev_base; dev != NULL; dev = dev->next) + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE) if (first == NULL || strncmp(dev->name, first->name, 3) < 0) first = dev; + } + read_unlock(&dev_base_lock); return first; } @@ -558,11 +561,14 @@ struct device *rose_dev_get(rose_address *addr) { struct device *dev; - for (dev = dev_base; dev != NULL; dev = dev->next) + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0) - return dev; - - return NULL; + goto out; + } +out: + read_unlock(&dev_base_lock); + return dev; } struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 683063137..9d2a95ea6 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -39,20 +39,24 @@ static struct tcf_proto_ops *tcf_proto_base; +/* Protects list of registered TC modules. It is pure SMP lock. */ +static rwlock_t cls_mod_lock = RW_LOCK_UNLOCKED; /* Find classifier type by string name */ struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind) { - struct tcf_proto_ops *t; + struct tcf_proto_ops *t = NULL; if (kind) { + read_lock(&cls_mod_lock); for (t = tcf_proto_base; t; t = t->next) { if (rtattr_strcmp(kind, t->kind) == 0) - return t; + break; } + read_unlock(&cls_mod_lock); } - return NULL; + return t; } /* Register(unregister) new classifier type */ @@ -61,12 +65,17 @@ int register_tcf_proto_ops(struct tcf_proto_ops *ops) { struct tcf_proto_ops *t, **tp; - for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next) - if (strcmp(ops->kind, t->kind) == 0) + write_lock(&cls_mod_lock); + for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next) { + if (strcmp(ops->kind, t->kind) == 0) { + write_unlock(&cls_mod_lock); return -EEXIST; + } + } ops->next = NULL; *tp = ops; + write_unlock(&cls_mod_lock); return 0; } @@ -74,13 +83,17 @@ int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) { struct tcf_proto_ops *t, **tp; + write_lock(&cls_mod_lock); for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next) if (t == ops) break; - if (!t) + if (!t) { + write_unlock(&cls_mod_lock); return -ENOENT; + } *tp = t->next; + write_unlock(&cls_mod_lock); return 0; } @@ -217,8 +230,12 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg) kfree(tp); goto errout; } + write_lock(&qdisc_tree_lock); + spin_lock_bh(&dev->queue_lock); tp->next = *back; *back = tp; + spin_unlock_bh(&dev->queue_lock); + write_unlock(&qdisc_tree_lock); } else if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], tp->ops->kind)) goto errout; @@ -226,8 +243,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg) if (fh == 0) { if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) { + write_lock(&qdisc_tree_lock); + spin_lock_bh(&dev->queue_lock); *back = tp->next; - synchronize_bh(); + spin_unlock_bh(&dev->queue_lock); + write_unlock(&qdisc_tree_lock); tp->ops->destroy(tp); kfree(tp); @@ -344,12 +364,16 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL) return skb->len; + + read_lock(&qdisc_tree_lock); if (!tcm->tcm_parent) q = dev->qdisc_sleeping; else q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); - if (q == NULL) + if (q == NULL) { + read_unlock(&qdisc_tree_lock); return skb->len; + } if ((cops = q->ops->cl_ops) == NULL) goto errout; if (TC_H_MIN(tcm->tcm_parent)) { @@ -400,6 +424,7 @@ errout: if (cl) cops->put(q, cl); + read_unlock(&qdisc_tree_lock); return skb->len; } diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index e92b846ee..598867187 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -136,7 +136,7 @@ static void fw_destroy(struct tcf_proto *tp) unsigned long cl; head->ht[h] = f->next; - if ((cl = cls_set_class(&f->res.class, 0)) != 0) + if ((cl = __cls_set_class(&f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE tcf_police_release(f->police); @@ -161,10 +161,11 @@ static int fw_delete(struct tcf_proto *tp, unsigned long arg) if (*fp == f) { unsigned long cl; + tcf_tree_lock(tp); *fp = f->next; - synchronize_bh(); + tcf_tree_unlock(tp); - if ((cl = cls_set_class(&f->res.class, 0)) != 0) + if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE tcf_police_release(f->police); @@ -203,7 +204,7 @@ static int fw_change(struct tcf_proto *tp, unsigned long base, f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]); cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid); - cl = cls_set_class(&f->res.class, cl); + cl = cls_set_class(tp, &f->res.class, cl); if (cl) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); } @@ -211,8 +212,9 @@ static int fw_change(struct tcf_proto *tp, unsigned long base, if (tb[TCA_FW_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]); + tcf_tree_lock(tp); police = xchg(&f->police, police); - synchronize_bh(); + tcf_tree_unlock(tp); tcf_police_release(police); } @@ -229,8 +231,9 @@ static int fw_change(struct tcf_proto *tp, unsigned long base, return -ENOBUFS; memset(head, 0, sizeof(*head)); + tcf_tree_lock(tp); tp->root = head; - synchronize_bh(); + tcf_tree_unlock(tp); } f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL); @@ -245,7 +248,7 @@ static int fw_change(struct tcf_proto *tp, unsigned long base, if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != 4) goto errout; f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]); - cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); + cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); } #ifdef CONFIG_NET_CLS_POLICE @@ -254,8 +257,9 @@ static int fw_change(struct tcf_proto *tp, unsigned long base, #endif f->next = head->ht[fw_hash(handle)]; - wmb(); + tcf_tree_lock(tp); head->ht[fw_hash(handle)] = f; + tcf_tree_unlock(tp); *arg = (unsigned long)f; return 0; @@ -335,7 +339,8 @@ static int fw_dump(struct tcf_proto *tp, unsigned long fh, rta->rta_len = skb->tail - b; #ifdef CONFIG_NET_CLS_POLICE if (f->police) { - RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats); + if (qdisc_copy_stats(skb, &f->police->stats)) + goto rtattr_failure; } #endif return skb->len; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index f83e79134..cb0d21d0f 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -83,11 +83,11 @@ static __inline__ int route4_fastmap_hash(u32 id, int iif) return id&0xF; } -static void route4_reset_fastmap(struct route4_head *head, u32 id) +static void route4_reset_fastmap(struct device *dev, struct route4_head *head, u32 id) { - start_bh_atomic(); + spin_lock_bh(&dev->queue_lock); memset(head->fastmap, 0, sizeof(head->fastmap)); - end_bh_atomic(); + spin_unlock_bh(&dev->queue_lock); } static void __inline__ @@ -297,7 +297,7 @@ static void route4_destroy(struct tcf_proto *tp) unsigned long cl; b->ht[h2] = f->next; - if ((cl = cls_set_class(&f->res.class, 0)) != 0) + if ((cl = __cls_set_class(&f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE tcf_police_release(f->police); @@ -329,12 +329,13 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) if (*fp == f) { unsigned long cl; + tcf_tree_lock(tp); *fp = f->next; - synchronize_bh(); + tcf_tree_unlock(tp); - route4_reset_fastmap(head, f->id); + route4_reset_fastmap(tp->q->dev, head, f->id); - if ((cl = cls_set_class(&f->res.class, 0)) != 0) + if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE @@ -349,8 +350,9 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) return 0; /* OK, session has no flows */ + tcf_tree_lock(tp); head->table[to_hash(h)] = NULL; - synchronize_bh(); + tcf_tree_unlock(tp); kfree(b); return 0; @@ -387,7 +389,7 @@ static int route4_change(struct tcf_proto *tp, unsigned long base, unsigned long cl; f->res.classid = *(u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID-1]); - cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); + cl = cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); if (cl) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); } @@ -395,8 +397,9 @@ static int route4_change(struct tcf_proto *tp, unsigned long base, if (tb[TCA_ROUTE4_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]); + tcf_tree_lock(tp); police = xchg(&f->police, police); - synchronize_bh(); + tcf_tree_unlock(tp); tcf_police_release(police); } @@ -412,8 +415,9 @@ static int route4_change(struct tcf_proto *tp, unsigned long base, return -ENOBUFS; memset(head, 0, sizeof(struct route4_head)); + tcf_tree_lock(tp); tp->root = head; - synchronize_bh(); + tcf_tree_unlock(tp); } f = kmalloc(sizeof(struct route4_filter), GFP_KERNEL); @@ -475,8 +479,9 @@ static int route4_change(struct tcf_proto *tp, unsigned long base, goto errout; memset(b, 0, sizeof(*b)); + tcf_tree_lock(tp); head->table[h1] = b; - synchronize_bh(); + tcf_tree_unlock(tp); } f->bkt = b; @@ -489,17 +494,18 @@ static int route4_change(struct tcf_proto *tp, unsigned long base, goto errout; } - cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); + cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); #ifdef CONFIG_NET_CLS_POLICE if (tb[TCA_ROUTE4_POLICE-1]) f->police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]); #endif f->next = f1; - wmb(); + tcf_tree_lock(tp); *ins_f = f; + tcf_tree_unlock(tp); - route4_reset_fastmap(head, f->id); + route4_reset_fastmap(tp->q->dev, head, f->id); *arg = (unsigned long)f; return 0; @@ -589,7 +595,8 @@ static int route4_dump(struct tcf_proto *tp, unsigned long fh, rta->rta_len = skb->tail - b; #ifdef CONFIG_NET_CLS_POLICE if (f->police) { - RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats); + if (qdisc_copy_stats(skb, &f->police->stats)) + goto rtattr_failure; } #endif return skb->len; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 48142c6e7..be4471d78 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -282,7 +282,7 @@ static void rsvp_destroy(struct tcf_proto *tp) unsigned long cl; s->ht[h2] = f->next; - if ((cl = cls_set_class(&f->res.class, 0)) != 0) + if ((cl = __cls_set_class(&f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE tcf_police_release(f->police); @@ -310,10 +310,11 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) unsigned long cl; + tcf_tree_lock(tp); *fp = f->next; - synchronize_bh(); + tcf_tree_unlock(tp); - if ((cl = cls_set_class(&f->res.class, 0)) != 0) + if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE @@ -332,8 +333,9 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF]; *sp; sp = &(*sp)->next) { if (*sp == s) { + tcf_tree_lock(tp); *sp = s->next; - synchronize_bh(); + tcf_tree_unlock(tp); kfree(s); return 0; @@ -446,7 +448,7 @@ static int rsvp_change(struct tcf_proto *tp, unsigned long base, unsigned long cl; f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]); - cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); + cl = cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); if (cl) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); } @@ -454,8 +456,9 @@ static int rsvp_change(struct tcf_proto *tp, unsigned long base, if (tb[TCA_RSVP_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]); + tcf_tree_lock(tp); police = xchg(&f->police, police); - synchronize_bh(); + tcf_tree_unlock(tp); tcf_police_release(police); } @@ -536,7 +539,7 @@ insert: f->sess = s; if (f->tunnelhdr == 0) - cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); + cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid)); #ifdef CONFIG_NET_CLS_POLICE if (tb[TCA_RSVP_POLICE-1]) f->police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]); @@ -659,7 +662,8 @@ static int rsvp_dump(struct tcf_proto *tp, unsigned long fh, rta->rta_len = skb->tail - b; #ifdef CONFIG_NET_CLS_POLICE if (f->police) { - RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats); + if (qdisc_copy_stats(skb, &f->police->stats)) + goto rtattr_failure; } #endif return skb->len; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 98d4e1f7b..f759d150a 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -307,7 +307,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n) { unsigned long cl; - if ((cl = cls_set_class(&n->res.class, 0)) != 0) + if ((cl = __cls_set_class(&n->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); #ifdef CONFIG_NET_CLS_POLICE tcf_police_release(n->police); @@ -326,8 +326,9 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key) if (ht) { for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) { if (*kp == key) { + tcf_tree_lock(tp); *kp = key->next; - synchronize_bh(); + tcf_tree_unlock(tp); u32_destroy_key(tp, key); return 0; @@ -346,7 +347,6 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) for (h=0; h<=ht->divisor; h++) { while ((n = ht->ht[h]) != NULL) { ht->ht[h] = n->next; - synchronize_bh(); u32_destroy_key(tp, n); } @@ -465,8 +465,9 @@ static int u32_set_parms(struct Qdisc *q, unsigned long base, ht_down->refcnt++; } + sch_tree_lock(q); ht_down = xchg(&n->ht_down, ht_down); - synchronize_bh(); + sch_tree_unlock(q); if (ht_down) ht_down->refcnt--; @@ -475,7 +476,9 @@ static int u32_set_parms(struct Qdisc *q, unsigned long base, unsigned long cl; n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]); - cl = cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid)); + sch_tree_lock(q); + cl = __cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid)); + sch_tree_unlock(q); if (cl) q->ops->cl_ops->unbind_tcf(q, cl); } @@ -483,8 +486,9 @@ static int u32_set_parms(struct Qdisc *q, unsigned long base, if (tb[TCA_U32_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1], est); + sch_tree_lock(q); police = xchg(&n->police, police); - synchronize_bh(); + sch_tree_lock(q); tcf_police_release(police); } @@ -682,7 +686,8 @@ static int u32_dump(struct tcf_proto *tp, unsigned long fh, rta->rta_len = skb->tail - b; #ifdef CONFIG_NET_CLS_POLICE if (TC_U32_KEY(n->handle) && n->police) { - RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &n->police->stats); + if (qdisc_copy_stats(skb, &n->police->stats)) + goto rtattr_failure; } #endif return skb->len; diff --git a/net/sched/estimator.c b/net/sched/estimator.c index d51017c84..e70066f9c 100644 --- a/net/sched/estimator.c +++ b/net/sched/estimator.c @@ -97,29 +97,38 @@ struct qdisc_estimator_head static struct qdisc_estimator_head elist[EST_MAX_INTERVAL+1]; +/* Estimator array lock */ +static rwlock_t est_lock = RW_LOCK_UNLOCKED; + static void est_timer(unsigned long arg) { int idx = (int)arg; struct qdisc_estimator *e; + read_lock(&est_lock); for (e = elist[idx].list; e; e = e->next) { - u64 nbytes = e->stats->bytes; - u32 npackets = e->stats->packets; + struct tc_stats *st = e->stats; + u64 nbytes; + u32 npackets; u32 rate; - + + spin_lock(st->lock); + nbytes = st->bytes; + npackets = st->packets; rate = (nbytes - e->last_bytes)<<(7 - idx); e->last_bytes = nbytes; e->avbps += ((long)rate - (long)e->avbps) >> e->ewma_log; - e->stats->bps = (e->avbps+0xF)>>5; + st->bps = (e->avbps+0xF)>>5; rate = (npackets - e->last_packets)<<(12 - idx); e->last_packets = npackets; e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log; e->stats->pps = (e->avpps+0x1FF)>>10; + spin_unlock(st->lock); } - elist[idx].timer.expires = jiffies + ((HZ/4)<<idx); - add_timer(&elist[idx].timer); + mod_timer(&elist[idx].timer, jiffies + ((HZ/4)<<idx)); + read_unlock(&est_lock); } int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt) @@ -154,7 +163,9 @@ int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt) elist[est->interval].timer.function = est_timer; add_timer(&elist[est->interval].timer); } + write_lock_bh(&est_lock); elist[est->interval].list = est; + write_unlock_bh(&est_lock); return 0; } @@ -172,8 +183,9 @@ void qdisc_kill_estimator(struct tc_stats *stats) continue; } + write_lock_bh(&est_lock); *pest = est->next; - synchronize_bh(); + write_unlock_bh(&est_lock); kfree(est); killed++; diff --git a/net/sched/police.c b/net/sched/police.c index 89e58d8be..f81f89aed 100644 --- a/net/sched/police.c +++ b/net/sched/police.c @@ -38,6 +38,10 @@ static u32 idx_gen; static struct tcf_police *tcf_police_ht[16]; +/* Policer hash table lock */ +static rwlock_t police_lock = RW_LOCK_UNLOCKED; + +/* Each policer is serialized by its individual spinlock */ static __inline__ unsigned tcf_police_hash(u32 index) { @@ -48,11 +52,13 @@ static __inline__ struct tcf_police * tcf_police_lookup(u32 index) { struct tcf_police *p; + read_lock(&police_lock); for (p = tcf_police_ht[tcf_police_hash(index)]; p; p = p->next) { if (p->index == index) - return p; + break; } - return NULL; + read_unlock(&police_lock); + return p; } static __inline__ u32 tcf_police_new_index(void) @@ -73,7 +79,9 @@ void tcf_police_destroy(struct tcf_police *p) for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->next) { if (*p1p == p) { + write_lock_bh(&police_lock); *p1p = p->next; + write_unlock_bh(&police_lock); #ifdef CONFIG_NET_ESTIMATOR qdisc_kill_estimator(&p->stats); #endif @@ -114,6 +122,8 @@ struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est) memset(p, 0, sizeof(*p)); p->refcnt = 1; + spin_lock_init(&p->lock); + p->stats.lock = &p->lock; if (parm->rate.rate) { if ((p->R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1])) == NULL) goto failure; @@ -144,8 +154,10 @@ struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est) qdisc_new_estimator(&p->stats, est); #endif h = tcf_police_hash(p->index); + write_lock_bh(&police_lock); p->next = tcf_police_ht[h]; tcf_police_ht[h] = p; + write_unlock_bh(&police_lock); return p; failure: @@ -161,19 +173,24 @@ int tcf_police(struct sk_buff *skb, struct tcf_police *p) long toks; long ptoks = 0; + spin_lock(&p->lock); + p->stats.bytes += skb->len; p->stats.packets++; #ifdef CONFIG_NET_ESTIMATOR if (p->ewma_rate && p->stats.bps >= p->ewma_rate) { p->stats.overlimits++; + spin_unlock(&p->lock); return p->action; } #endif if (skb->len <= p->mtu) { - if (p->R_tab == NULL) + if (p->R_tab == NULL) { + spin_unlock(&p->lock); return p->result; + } PSCHED_GET_TIME(now); @@ -194,11 +211,13 @@ int tcf_police(struct sk_buff *skb, struct tcf_police *p) p->t_c = now; p->toks = toks; p->ptoks = ptoks; + spin_unlock(&p->lock); return p->result; } } p->stats.overlimits++; + spin_unlock(&p->lock); return p->action; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 0ced70bbc..fec6faefe 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -124,6 +124,10 @@ static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n, changes qdisc parameters. */ +/* Protects list of registered TC modules. It is pure SMP lock. */ +static rwlock_t qdisc_mod_lock = RW_LOCK_UNLOCKED; + + /************************************************ * Queueing disciplines manipulation. * ************************************************/ @@ -139,9 +143,13 @@ int register_qdisc(struct Qdisc_ops *qops) { struct Qdisc_ops *q, **qp; - for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next) - if (strcmp(qops->id, q->id) == 0) + write_lock(&qdisc_mod_lock); + for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next) { + if (strcmp(qops->id, q->id) == 0) { + write_unlock(&qdisc_mod_lock); return -EEXIST; + } + } if (qops->enqueue == NULL) qops->enqueue = noop_qdisc_ops.enqueue; @@ -152,20 +160,26 @@ int register_qdisc(struct Qdisc_ops *qops) qops->next = NULL; *qp = qops; + write_unlock(&qdisc_mod_lock); return 0; } int unregister_qdisc(struct Qdisc_ops *qops) { struct Qdisc_ops *q, **qp; + int err = -ENOENT; + + write_lock(&qdisc_mod_lock); for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next) if (q == qops) break; - if (!q) - return -ENOENT; - *qp = q->next; - q->next = NULL; - return 0; + if (q) { + *qp = q->next; + q->next = NULL; + err = 0; + } + write_unlock(&qdisc_mod_lock); + return err; } /* We know handle. Find qdisc among all qdisc's attached to device @@ -203,15 +217,17 @@ struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) struct Qdisc_ops *qdisc_lookup_ops(struct rtattr *kind) { - struct Qdisc_ops *q; + struct Qdisc_ops *q = NULL; if (kind) { + read_lock(&qdisc_mod_lock); for (q = qdisc_base; q; q = q->next) { if (rtattr_strcmp(kind, q->id) == 0) - return q; + break; } + read_unlock(&qdisc_mod_lock); } - return NULL; + return q; } static struct qdisc_rate_table *qdisc_rtab_list; @@ -284,7 +300,8 @@ dev_graft_qdisc(struct device *dev, struct Qdisc *qdisc) if (dev->flags & IFF_UP) dev_deactivate(dev); - start_bh_atomic(); + write_lock(&qdisc_tree_lock); + spin_lock_bh(&dev->queue_lock); oqdisc = dev->qdisc_sleeping; /* Prune old scheduler */ @@ -296,7 +313,8 @@ dev_graft_qdisc(struct device *dev, struct Qdisc *qdisc) qdisc = &noop_qdisc; dev->qdisc_sleeping = qdisc; dev->qdisc = &noop_qdisc; - end_bh_atomic(); + spin_unlock_bh(&dev->queue_lock); + write_unlock(&qdisc_tree_lock); if (dev->flags & IFF_UP) dev_activate(dev); @@ -376,7 +394,7 @@ qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp) goto err_out; /* Grrr... Resolve race condition with module unload */ - + err = -EINVAL; if (ops != qdisc_lookup_ops(kind)) goto err_out; @@ -389,6 +407,7 @@ qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp) sch->dequeue = ops->dequeue; sch->dev = dev; atomic_set(&sch->refcnt, 1); + sch->stats.lock = &dev->queue_lock; if (handle == 0) { handle = qdisc_alloc_handle(dev); err = -ENOMEM; @@ -398,8 +417,10 @@ qdisc_create(struct device *dev, u32 handle, struct rtattr **tca, int *errp) sch->handle = handle; if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) { + write_lock(&qdisc_tree_lock); sch->next = dev->qdisc_list; dev->qdisc_list = sch; + write_unlock(&qdisc_tree_lock); #ifdef CONFIG_NET_ESTIMATOR if (tca[TCA_RATE-1]) qdisc_new_estimator(&sch->stats, tca[TCA_RATE-1]); @@ -521,7 +542,9 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) return err; if (q) { qdisc_notify(skb, n, clid, q, NULL); + spin_lock_bh(&dev->queue_lock); qdisc_destroy(q); + spin_unlock_bh(&dev->queue_lock); } } else { qdisc_notify(skb, n, clid, NULL, q); @@ -637,17 +660,36 @@ graft: struct Qdisc *old_q = NULL; err = qdisc_graft(dev, p, clid, q, &old_q); if (err) { - if (q) + if (q) { + spin_lock_bh(&dev->queue_lock); qdisc_destroy(q); + spin_unlock_bh(&dev->queue_lock); + } return err; } qdisc_notify(skb, n, clid, old_q, q); - if (old_q) + if (old_q) { + spin_lock_bh(&dev->queue_lock); qdisc_destroy(old_q); + spin_unlock_bh(&dev->queue_lock); + } } return 0; } +int qdisc_copy_stats(struct sk_buff *skb, struct tc_stats *st) +{ + spin_lock_bh(st->lock); + RTA_PUT(skb, TCA_STATS, (char*)&st->lock - (char*)st, st); + spin_unlock_bh(st->lock); + return 0; + +rtattr_failure: + spin_unlock_bh(st->lock); + return -1; +} + + static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, u32 pid, u32 seq, unsigned flags, int event) { @@ -667,7 +709,8 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, if (q->ops->dump && q->ops->dump(q, skb) < 0) goto rtattr_failure; q->stats.qlen = q->q.qlen; - RTA_PUT(skb, TCA_STATS, sizeof(q->stats), &q->stats); + if (qdisc_copy_stats(skb, &q->stats)) + goto rtattr_failure; nlh->nlmsg_len = skb->tail - b; return skb->len; @@ -713,22 +756,29 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) s_idx = cb->args[0]; s_q_idx = q_idx = cb->args[1]; + read_lock(&dev_base_lock); for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) { if (idx < s_idx) continue; if (idx > s_idx) s_q_idx = 0; + read_lock(&qdisc_tree_lock); for (q = dev->qdisc_list, q_idx = 0; q; q = q->next, q_idx++) { if (q_idx < s_q_idx) continue; if (tc_fill_qdisc(skb, q, 0, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) { + read_unlock(&qdisc_tree_lock); goto done; + } } + read_unlock(&qdisc_tree_lock); } done: + read_unlock(&dev_base_lock); + cb->args[0] = idx; cb->args[1] = q_idx; @@ -933,6 +983,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; + read_lock(&qdisc_tree_lock); for (q=dev->qdisc_list, t=0; q; q = q->next, t++) { if (t < s_t) continue; if (!q->ops->cl_ops) continue; @@ -951,6 +1002,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) if (arg.w.stop) break; } + read_unlock(&qdisc_tree_lock); cb->args[0] = t; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index c8094a882..2244b68ed 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1417,6 +1417,7 @@ static int cbq_init(struct Qdisc *sch, struct rtattr *opt) q->link.ewma_log = TC_CBQ_DEF_EWMA; q->link.avpkt = q->link.allot/2; q->link.minidle = -0x7FFFFFFF; + q->link.stats.lock = &sch->dev->queue_lock; init_timer(&q->wd_timer); q->wd_timer.data = (unsigned long)sch; @@ -1558,6 +1559,16 @@ static int cbq_dump_attr(struct sk_buff *skb, struct cbq_class *cl) return 0; } +int cbq_copy_xstats(struct sk_buff *skb, struct tc_cbq_xstats *st) +{ + RTA_PUT(skb, TCA_STATS, sizeof(*st), st); + return 0; + +rtattr_failure: + return -1; +} + + static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb) { struct cbq_sched_data *q = (struct cbq_sched_data*)sch->data; @@ -1569,8 +1580,13 @@ static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb) if (cbq_dump_attr(skb, &q->link) < 0) goto rtattr_failure; rta->rta_len = skb->tail - b; + spin_lock_bh(&sch->dev->queue_lock); q->link.xstats.avgidle = q->link.avgidle; - RTA_PUT(skb, TCA_XSTATS, sizeof(q->link.xstats), &q->link.xstats); + if (cbq_copy_xstats(skb, &q->link.xstats)) { + spin_unlock_bh(&sch->dev->queue_lock); + goto rtattr_failure; + } + spin_unlock_bh(&sch->dev->queue_lock); return skb->len; rtattr_failure: @@ -1600,12 +1616,19 @@ cbq_dump_class(struct Qdisc *sch, unsigned long arg, goto rtattr_failure; rta->rta_len = skb->tail - b; cl->stats.qlen = cl->q->q.qlen; - RTA_PUT(skb, TCA_STATS, sizeof(cl->stats), &cl->stats); + if (qdisc_copy_stats(skb, &cl->stats)) + goto rtattr_failure; + spin_lock_bh(&sch->dev->queue_lock); cl->xstats.avgidle = cl->avgidle; cl->xstats.undertime = 0; if (!PSCHED_IS_PASTPERFECT(cl->undertime)) cl->xstats.undertime = PSCHED_TDIFF(cl->undertime, q->now); - RTA_PUT(skb, TCA_XSTATS, sizeof(cl->xstats), &cl->xstats); + q->link.xstats.avgidle = q->link.avgidle; + if (cbq_copy_xstats(skb, &cl->xstats)) { + spin_unlock_bh(&sch->dev->queue_lock); + goto rtattr_failure; + } + spin_unlock_bh(&sch->dev->queue_lock); return skb->len; @@ -1631,8 +1654,11 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, new->reshape_fail = cbq_reshape_fail; #endif } - if ((*old = xchg(&cl->q, new)) != NULL) - qdisc_reset(*old); + sch_tree_lock(sch); + *old = cl->q; + cl->q = new; + qdisc_reset(*old); + sch_tree_unlock(sch); return 0; } @@ -1710,16 +1736,16 @@ static void cbq_put(struct Qdisc *sch, unsigned long arg) struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data; struct cbq_class *cl = (struct cbq_class*)arg; - start_bh_atomic(); if (--cl->refcnt == 0) { #ifdef CONFIG_NET_CLS_POLICE + spin_lock_bh(&sch->dev->queue_lock); if (q->rx_class == cl) q->rx_class = NULL; + spin_unlock_bh(&sch->dev->queue_lock); #endif + cbq_destroy_class(cl); } - end_bh_atomic(); - return; } static int @@ -1780,7 +1806,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t } /* Change class parameters */ - start_bh_atomic(); + sch_tree_lock(sch); if (cl->next_alive != NULL) cbq_deactivate_class(cl); @@ -1812,7 +1838,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t if (cl->q->q.qlen) cbq_activate_class(cl); - end_bh_atomic(); + sch_tree_lock(sch); #ifdef CONFIG_NET_ESTIMATOR if (tca[TCA_RATE-1]) { @@ -1878,8 +1904,9 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t cl->allot = parent->allot; cl->quantum = cl->allot; cl->weight = cl->R_tab->rate.rate; + cl->stats.lock = &sch->dev->queue_lock; - start_bh_atomic(); + sch_tree_lock(sch); cbq_link_class(cl); cl->borrow = cl->tparent; if (cl->tparent != &q->link) @@ -1903,7 +1930,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **t #endif if (tb[TCA_CBQ_FOPT-1]) cbq_set_fopt(cl, RTA_DATA(tb[TCA_CBQ_FOPT-1])); - end_bh_atomic(); + sch_tree_unlock(sch); #ifdef CONFIG_NET_ESTIMATOR if (tca[TCA_RATE-1]) @@ -1926,7 +1953,7 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg) if (cl->filters || cl->children || cl == &q->link) return -EBUSY; - start_bh_atomic(); + sch_tree_lock(sch); if (cl->next_alive) cbq_deactivate_class(cl); @@ -1948,12 +1975,11 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg) cbq_sync_defmap(cl); cbq_rmprio(q, cl); + sch_tree_unlock(sch); if (--cl->refcnt == 0) cbq_destroy_class(cl); - end_bh_atomic(); - return 0; } diff --git a/net/sched/sch_csz.c b/net/sched/sch_csz.c index 2202fd81a..c1be3729e 100644 --- a/net/sched/sch_csz.c +++ b/net/sched/sch_csz.c @@ -885,7 +885,7 @@ static int csz_change(struct Qdisc *sch, u32 handle, u32 parent, struct rtattr * a = &q->flow[cl]; - start_bh_atomic(); + spin_lock_bh(&sch->dev->queue_lock); #if 0 a->rate_log = copt->rate_log; #endif @@ -899,7 +899,7 @@ static int csz_change(struct Qdisc *sch, u32 handle, u32 parent, struct rtattr * if (tb[TCA_CSZ_RTAB-1]) memcpy(a->L_tab, RTA_DATA(tb[TCA_CSZ_RTAB-1]), 1024); - end_bh_atomic(); + spin_unlock_bh(&sch->dev->queue_lock); return 0; } /* NI */ @@ -920,14 +920,14 @@ static int csz_delete(struct Qdisc *sch, unsigned long cl) a = &q->flow[cl]; - start_bh_atomic(); + spin_lock_bh(&sch->dev->queue_lock); a->fprev->fnext = a->fnext; a->fnext->fprev = a->fprev; a->sprev->snext = a->snext; a->snext->sprev = a->sprev; a->start = a->finish = 0; kfree(xchg(&q->flow[cl].L_tab, NULL)); - end_bh_atomic(); + spin_unlock_bh(&sch->dev->queue_lock); return 0; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index ba40033e5..2dc1ed327 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -34,7 +34,45 @@ /* Main transmission queue. */ -struct Qdisc_head qdisc_head = { &qdisc_head }; +struct Qdisc_head qdisc_head = { &qdisc_head, &qdisc_head }; +spinlock_t qdisc_runqueue_lock = SPIN_LOCK_UNLOCKED; + +/* Main qdisc structure lock. + + However, modifications + to data, participating in scheduling must be additionally + protected with dev->queue_lock spinlock. + + The idea is the following: + - enqueue, dequeue are serialized via top level device + spinlock dev->queue_lock. + - tree walking is protected by read_lock(qdisc_tree_lock) + and this lock is used only in process context. + - updates to tree are made only under rtnl semaphore, + hence this lock may be made without local bh disabling. + + qdisc_tree_lock must be grabbed BEFORE dev->queue_lock! + */ +rwlock_t qdisc_tree_lock = RW_LOCK_UNLOCKED; + +/* Anti deadlock rules: + + qdisc_runqueue_lock protects main transmission list qdisc_head. + Run list is accessed only under this spinlock. + + dev->queue_lock serializes queue accesses for this device + AND dev->qdisc pointer itself. + + dev->xmit_lock serializes accesses to device driver. + + dev->queue_lock and dev->xmit_lock are mutually exclusive, + if one is grabbed, another must be free. + + qdisc_runqueue_lock may be requested under dev->queue_lock, + but neither dev->queue_lock nor dev->xmit_lock may be requested + under qdisc_runqueue_lock. + */ + /* Kick device. Note, that this procedure can be called by a watchdog timer, so that @@ -44,7 +82,7 @@ struct Qdisc_head qdisc_head = { &qdisc_head }; >0 - queue is not empty, but throttled. <0 - queue is not empty. Device is throttled, if dev->tbusy != 0. - NOTE: Called only from NET BH + NOTE: Called under dev->queue_lock with locally disabled BH. */ int qdisc_restart(struct device *dev) @@ -53,27 +91,97 @@ int qdisc_restart(struct device *dev) struct sk_buff *skb; if ((skb = q->dequeue(q)) != NULL) { + /* Dequeue packet and release queue */ + spin_unlock(&dev->queue_lock); + if (netdev_nit) dev_queue_xmit_nit(skb, dev); - if (dev->hard_start_xmit(skb, dev) == 0) { - q->tx_last = jiffies; - return -1; + if (spin_trylock(&dev->xmit_lock)) { + /* Remember that the driver is grabbed by us. */ + dev->xmit_lock_owner = smp_processor_id(); + if (dev->hard_start_xmit(skb, dev) == 0) { + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); + + spin_lock(&dev->queue_lock); + dev->qdisc->tx_last = jiffies; + return -1; + } + /* Release the driver */ + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); + } 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 virtual %s, fix it urgently!\n", dev->name); + spin_lock(&dev->queue_lock); + return -1; + } + + /* Otherwise, packet is requeued + and will be sent by the next net_bh run. + */ + mark_bh(NET_BH); } /* 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) */ + spin_lock(&dev->queue_lock); + q = dev->qdisc; q->ops->requeue(skb, q); return -1; } - return q->q.qlen; + return dev->qdisc->q.qlen; +} + +static __inline__ void +qdisc_stop_run(struct Qdisc *q) +{ + q->h.forw->back = q->h.back; + q->h.back->forw = q->h.forw; + q->h.forw = NULL; +} + +extern __inline__ void +qdisc_continue_run(struct Qdisc *q) +{ + if (!qdisc_on_runqueue(q) && q->dev) { + q->h.forw = &qdisc_head; + q->h.back = qdisc_head.back; + qdisc_head.back->forw = &q->h; + qdisc_head.back = &q->h; + } +} + +static __inline__ int +qdisc_init_run(struct Qdisc_head *lh) +{ + if (qdisc_head.forw != &qdisc_head) { + *lh = qdisc_head; + lh->forw->back = lh; + lh->back->forw = lh; + qdisc_head.forw = &qdisc_head; + qdisc_head.back = &qdisc_head; + return 1; + } + return 0; } /* Scan transmission queue and kick devices. @@ -84,58 +192,90 @@ int qdisc_restart(struct device *dev) I have no idea how to solve it using only "anonymous" Linux mark_bh(). To change queue from device interrupt? Ough... only not this... + + This function is called only from net_bh. */ void qdisc_run_queues(void) { - struct Qdisc_head **hp, *h; + struct Qdisc_head lh, *h; + + spin_lock(&qdisc_runqueue_lock); + if (!qdisc_init_run(&lh)) + goto out; - hp = &qdisc_head.forw; - while ((h = *hp) != &qdisc_head) { - int res = -1; + while ((h = lh.forw) != &lh) { + int res; + struct device *dev; struct Qdisc *q = (struct Qdisc*)h; - struct device *dev = q->dev; - - while (!dev->tbusy && (res = qdisc_restart(dev)) < 0) - /* NOTHING */; - - /* An explanation is necessary here. - qdisc_restart called dev->hard_start_xmit, - if device is virtual, it could trigger one more - dev_queue_xmit and a new device could appear - in the active chain. In this case we cannot unlink - the empty queue, because we lost the back pointer. - No problem, we will unlink it during the next round. - */ - if (res == 0 && *hp == h) { - *hp = h->forw; - h->forw = NULL; - continue; + qdisc_stop_run(q); + + dev = q->dev; + spin_unlock(&qdisc_runqueue_lock); + + res = -1; + if (spin_trylock(&dev->queue_lock)) { + while (!dev->tbusy && (res = qdisc_restart(dev)) < 0) + /* NOTHING */; + spin_unlock(&dev->queue_lock); } - hp = &h->forw; + + spin_lock(&qdisc_runqueue_lock); + /* If qdisc is not empty add it to the tail of list */ + if (res) + qdisc_continue_run(q); } +out: + spin_unlock(&qdisc_runqueue_lock); } -/* Periodic watchdoc timer to recover from hard/soft device bugs. */ +/* Periodic watchdog timer to recover from hard/soft device bugs. */ static void dev_do_watchdog(unsigned long dummy); static struct timer_list dev_watchdog = { NULL, NULL, 0L, 0L, &dev_do_watchdog }; +/* This function is called only from timer */ + static void dev_do_watchdog(unsigned long dummy) { - struct Qdisc_head *h; + struct Qdisc_head lh, *h; + + if (!spin_trylock(&qdisc_runqueue_lock)) { + /* No hurry with watchdog. */ + mod_timer(&dev_watchdog, jiffies + HZ/10); + return; + } - for (h = qdisc_head.forw; h != &qdisc_head; h = h->forw) { + if (!qdisc_init_run(&lh)) + goto out; + + while ((h = lh.forw) != &lh) { + struct device *dev; struct Qdisc *q = (struct Qdisc*)h; - struct device *dev = q->dev; - if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo) - qdisc_restart(dev); + + qdisc_stop_run(q); + + dev = q->dev; + spin_unlock(&qdisc_runqueue_lock); + + if (spin_trylock(&dev->queue_lock)) { + q = dev->qdisc; + if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo) + qdisc_restart(dev); + spin_unlock(&dev->queue_lock); + } + + spin_lock(&qdisc_runqueue_lock); + + qdisc_continue_run(dev->qdisc); } - dev_watchdog.expires = jiffies + 5*HZ; - add_timer(&dev_watchdog); + +out: + mod_timer(&dev_watchdog, jiffies + 5*HZ); + spin_unlock(&qdisc_runqueue_lock); } @@ -206,7 +346,7 @@ struct Qdisc noqueue_qdisc = { { NULL }, NULL, - NULL, + noop_dequeue, TCQ_F_BUILTIN, &noqueue_qdisc_ops, }; @@ -322,6 +462,7 @@ struct Qdisc * qdisc_create_dflt(struct device *dev, struct Qdisc_ops *ops) sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; sch->dev = dev; + sch->stats.lock = &dev->queue_lock; atomic_set(&sch->refcnt, 1); if (!ops->init || ops->init(sch, NULL) == 0) return sch; @@ -330,42 +471,45 @@ struct Qdisc * qdisc_create_dflt(struct device *dev, struct Qdisc_ops *ops) return NULL; } +/* Under dev->queue_lock and BH! */ + void qdisc_reset(struct Qdisc *qdisc) { struct Qdisc_ops *ops = qdisc->ops; - start_bh_atomic(); if (ops->reset) ops->reset(qdisc); - end_bh_atomic(); } +/* Under dev->queue_lock and BH! */ + void qdisc_destroy(struct Qdisc *qdisc) { struct Qdisc_ops *ops = qdisc->ops; + struct device *dev; if (!atomic_dec_and_test(&qdisc->refcnt)) return; + dev = qdisc->dev; + #ifdef CONFIG_NET_SCHED - if (qdisc->dev) { + if (dev) { struct Qdisc *q, **qp; - for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) + for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) { if (q == qdisc) { *qp = q->next; - q->next = NULL; break; } + } } #ifdef CONFIG_NET_ESTIMATOR qdisc_kill_estimator(&qdisc->stats); #endif #endif - start_bh_atomic(); if (ops->reset) ops->reset(qdisc); if (ops->destroy) ops->destroy(qdisc); - end_bh_atomic(); if (!(qdisc->flags&TCQ_F_BUILTIN)) kfree(qdisc); } @@ -380,19 +524,23 @@ void dev_activate(struct device *dev) */ if (dev->qdisc_sleeping == &noop_qdisc) { + struct Qdisc *qdisc; if (dev->tx_queue_len) { - struct Qdisc *qdisc; qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops); if (qdisc == NULL) { printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - dev->qdisc_sleeping = qdisc; - } else - dev->qdisc_sleeping = &noqueue_qdisc; + } else { + qdisc = &noqueue_qdisc; + } + write_lock(&qdisc_tree_lock); + dev->qdisc_sleeping = qdisc; + write_unlock(&qdisc_tree_lock); } - start_bh_atomic(); + spin_lock_bh(&dev->queue_lock); + spin_lock(&qdisc_runqueue_lock); if ((dev->qdisc = dev->qdisc_sleeping) != &noqueue_qdisc) { dev->qdisc->tx_timeo = 5*HZ; dev->qdisc->tx_last = jiffies - dev->qdisc->tx_timeo; @@ -400,51 +548,50 @@ void dev_activate(struct device *dev) dev_watchdog.expires = jiffies + 5*HZ; add_timer(&dev_watchdog); } - end_bh_atomic(); + spin_unlock(&qdisc_runqueue_lock); + spin_unlock_bh(&dev->queue_lock); } void dev_deactivate(struct device *dev) { struct Qdisc *qdisc; - start_bh_atomic(); - - qdisc = xchg(&dev->qdisc, &noop_qdisc); + spin_lock_bh(&dev->queue_lock); + qdisc = dev->qdisc; + dev->qdisc = &noop_qdisc; qdisc_reset(qdisc); - if (qdisc->h.forw) { - struct Qdisc_head **hp, *h; - - for (hp = &qdisc_head.forw; (h = *hp) != &qdisc_head; hp = &h->forw) { - if (h == &qdisc->h) { - *hp = h->forw; - break; - } - } - } - - end_bh_atomic(); + spin_lock(&qdisc_runqueue_lock); + if (qdisc_on_runqueue(qdisc)) + qdisc_stop_run(qdisc); + spin_unlock(&qdisc_runqueue_lock); + spin_unlock_bh(&dev->queue_lock); } void dev_init_scheduler(struct device *dev) { + write_lock(&qdisc_tree_lock); + spin_lock_bh(&dev->queue_lock); dev->qdisc = &noop_qdisc; + spin_unlock_bh(&dev->queue_lock); dev->qdisc_sleeping = &noop_qdisc; dev->qdisc_list = NULL; + write_unlock(&qdisc_tree_lock); } void dev_shutdown(struct device *dev) { struct Qdisc *qdisc; - start_bh_atomic(); + write_lock(&qdisc_tree_lock); + spin_lock_bh(&dev->queue_lock); qdisc = dev->qdisc_sleeping; dev->qdisc = &noop_qdisc; dev->qdisc_sleeping = &noop_qdisc; qdisc_destroy(qdisc); BUG_TRAP(dev->qdisc_list == NULL); dev->qdisc_list = NULL; - end_bh_atomic(); + spin_unlock_bh(&dev->queue_lock); + write_unlock(&qdisc_tree_lock); } - diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 5222d149d..a7069dec7 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -178,7 +178,7 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt) return -EINVAL; } - start_bh_atomic(); + sch_tree_lock(sch); q->bands = qopt->bands; memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1); @@ -187,7 +187,7 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt) if (child != &noop_qdisc) qdisc_destroy(child); } - end_bh_atomic(); + sch_tree_unlock(sch); for (i=0; i<=TC_PRIO_MAX; i++) { int band = q->prio2band[i]; @@ -195,11 +195,12 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt) struct Qdisc *child; child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); if (child) { + sch_tree_lock(sch); child = xchg(&q->queues[band], child); - synchronize_bh(); if (child != &noop_qdisc) qdisc_destroy(child); + sch_tree_unlock(sch); } } } @@ -265,7 +266,11 @@ static int prio_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (new == NULL) new = &noop_qdisc; - *old = xchg(&q->queues[band], new); + sch_tree_lock(sch); + *old = q->queues[band]; + q->queues[band] = new; + qdisc_reset(*old); + sch_tree_unlock(sch); return 0; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 8baf254eb..dfde116f3 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -387,7 +387,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) if (opt->rta_len < RTA_LENGTH(sizeof(*ctl))) return -EINVAL; - start_bh_atomic(); + sch_tree_lock(sch); q->quantum = ctl->quantum ? : psched_mtu(sch->dev); q->perturb_period = ctl->perturb_period*HZ; @@ -396,7 +396,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) q->perturb_timer.expires = jiffies + q->perturb_period; add_timer(&q->perturb_timer); } - end_bh_atomic(); + sch_tree_unlock(sch); return 0; } diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index a4d13b628..90e469b02 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -308,7 +308,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) if (rtab->data[max_size>>qopt->rate.cell_log] > qopt->buffer) goto done; - start_bh_atomic(); + sch_tree_lock(sch); q->limit = qopt->limit; q->mtu = qopt->mtu; q->max_size = max_size; @@ -317,7 +317,7 @@ static int tbf_change(struct Qdisc* sch, struct rtattr *opt) q->ptokens = q->mtu; rtab = xchg(&q->R_tab, rtab); ptab = xchg(&q->P_tab, ptab); - end_bh_atomic(); + sch_tree_unlock(sch); err = 0; done: if (rtab) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 66040d5e9..ffed0de11 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -125,9 +125,11 @@ teql_dequeue(struct Qdisc* sch) if (skb == NULL) { struct device *m = dat->m->dev.qdisc->dev; if (m) { - m->tbusy = 0; dat->m->slaves = sch; + spin_lock(&m->queue_lock); + m->tbusy = 0; qdisc_restart(m); + spin_unlock(&m->queue_lock); } } sch->q.qlen = dat->q.qlen + dat->m->dev.qdisc->q.qlen; @@ -167,7 +169,9 @@ teql_destroy(struct Qdisc* sch) master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { master->slaves = NULL; + spin_lock_bh(&master->dev.queue_lock); qdisc_reset(master->dev.qdisc); + spin_unlock_bh(&master->dev.queue_lock); } } skb_queue_purge(&dat->q); @@ -190,6 +194,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct rtattr *opt) if (dev->hard_header_len > m->dev.hard_header_len) return -EINVAL; + if (&m->dev == dev) + return -ELOOP; + q->m = m; skb_queue_head_init(&q->q); @@ -244,7 +251,11 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct device *dev) return -ENOBUFS; } if (neigh_event_send(n, skb_res) == 0) { - if (dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len) < 0) { + int err; + read_lock(&n->lock); + err = dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len); + read_unlock(&n->lock); + if (err < 0) { neigh_release(n); return -EINVAL; } @@ -295,19 +306,24 @@ restart: continue; } - if (q->h.forw == NULL) { - q->h.forw = qdisc_head.forw; - qdisc_head.forw = &q->h; - } + if (!qdisc_on_runqueue(q)) + qdisc_run(q); switch (teql_resolve(skb, skb_res, slave)) { case 0: - if (slave->hard_start_xmit(skb, slave) == 0) { - master->slaves = NEXT_SLAVE(q); - dev->tbusy = 0; - master->stats.tx_packets++; - master->stats.tx_bytes += len; + if (spin_trylock(&slave->xmit_lock)) { + slave->xmit_lock_owner = smp_processor_id(); + if (slave->hard_start_xmit(skb, slave) == 0) { + slave->xmit_lock_owner = -1; + spin_unlock(&slave->xmit_lock); + master->slaves = NEXT_SLAVE(q); + dev->tbusy = 0; + master->stats.tx_packets++; + master->stats.tx_bytes += len; return 0; + } + slave->xmit_lock_owner = -1; + spin_unlock(&slave->xmit_lock); } if (dev->tbusy) busy = 1; diff --git a/net/socket.c b/net/socket.c index 181effb79..41499da08 100644 --- a/net/socket.c +++ b/net/socket.c @@ -131,6 +131,11 @@ struct net_proto_family *net_families[NPROTO]; static int sockets_in_use = 0; /* + * Socket hashing lock. + */ +rwlock_t sockhash_lock = RW_LOCK_UNLOCKED; + +/* * Support routines. Move socket addresses back and forth across the kernel/user * divide and look after the messy bits. */ @@ -199,7 +204,7 @@ static int get_fd(struct inode *inode) return -ENFILE; } - file->f_dentry = d_alloc_root(inode, NULL); + file->f_dentry = d_alloc_root(inode); if (!file->f_dentry) { put_filp(file); put_unused_fd(fd); @@ -283,7 +288,7 @@ struct socket *sock_alloc(void) inode->i_gid = current->fsgid; sock->inode = inode; - init_waitqueue(&sock->wait); + init_waitqueue_head(&sock->wait); sock->fasync_list = NULL; sock->state = SS_UNCONNECTED; sock->flags = 0; @@ -561,7 +566,8 @@ int sock_wake_async(struct socket *sock, int how) /* fall through */ case 0: call_kill: - kill_fasync(sock->fasync_list, SIGIO); + if(sock->fasync_list != NULL) + kill_fasync(sock->fasync_list, SIGIO); break; } return 0; diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 2b8db00cc..7c966779b 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -4,9 +4,12 @@ * Generic RPC authentication API. * * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> + * + * Modified May 1999, Horst von Brand <vonbrand@sleipnir.valparaiso.cl> */ #include <linux/types.h> +#include <linux/string.h> #include <linux/sched.h> #include <linux/malloc.h> #include <linux/errno.h> diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 6912c229d..6596085b3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -4,9 +4,12 @@ * UNIX-style authentication; no AUTH_SHORT support * * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> + * + * Modified May 1999 Horst von Brand <vonbrand@sleipnir.valparaiso.cl> */ #include <linux/types.h> +#include <linux/string.h> #include <linux/malloc.h> #include <linux/socket.h> #include <linux/in.h> diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index dc06be6b0..6ffcc187b 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -39,7 +39,7 @@ # define RPCDBG_FACILITY RPCDBG_CALL #endif -static struct wait_queue * destroy_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD(destroy_wait); static void call_bind(struct rpc_task *task); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 222a3f9ec..b4e0b4b76 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -60,9 +60,9 @@ static struct rpc_task * all_tasks = NULL; /* * rpciod-related stuff */ -static struct wait_queue * rpciod_idle = NULL; -static struct wait_queue * rpciod_killer = NULL; -static struct semaphore rpciod_sema = MUTEX; +static DECLARE_WAIT_QUEUE_HEAD(rpciod_idle); +static DECLARE_WAIT_QUEUE_HEAD(rpciod_killer); +static DECLARE_MUTEX(rpciod_sema); static unsigned int rpciod_users = 0; static pid_t rpciod_pid = 0; static int rpc_inhibit = 0; @@ -616,6 +616,7 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, task->tk_client = clnt; task->tk_flags = RPC_TASK_RUNNING | flags; task->tk_exit = callback; + init_waitqueue_head(&task->tk_wait); if (current->uid != current->fsuid || current->gid != current->fsgid) task->tk_flags |= RPC_TASK_SETUID; @@ -800,7 +801,7 @@ rpc_killall_tasks(struct rpc_clnt *clnt) rpc_inhibit--; } -static struct semaphore rpciod_running = MUTEX_LOCKED; +static DECLARE_MUTEX_LOCKED(rpciod_running); /* * This is the rpciod kernel thread @@ -808,7 +809,7 @@ static struct semaphore rpciod_running = MUTEX_LOCKED; static int rpciod(void *ptr) { - struct wait_queue **assassin = (struct wait_queue **) ptr; + wait_queue_head_t *assassin = (wait_queue_head_t*) ptr; unsigned long oldflags; int rounds = 0; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 2353c2e27..d98adb31c 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -123,6 +123,8 @@ svc_create_thread(svc_thread_fn func, struct svc_serv *serv) goto out; memset(rqstp, 0, sizeof(*rqstp)); + init_waitqueue_head(&rqstp->rq_wait); + if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) || !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index d2248ad74..0fb992f47 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -738,7 +738,7 @@ svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp, long timeout) { struct svc_sock *svsk; int len; - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); dprintk("svc: server %p waiting for data (to = %ld)\n", rqstp, timeout); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 1e5ad01c3..aa1bfa8f5 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -42,6 +42,7 @@ #define __KERNEL_SYSCALLS__ #include <linux/version.h> +#include <linux/config.h> #include <linux/types.h> #include <linux/malloc.h> #include <linux/sched.h> @@ -56,6 +57,8 @@ #include <linux/file.h> #include <net/sock.h> +#include <net/checksum.h> +#include <net/udp.h> #include <asm/uaccess.h> @@ -356,6 +359,7 @@ xprt_close(struct rpc_xprt *xprt) sk->user_data = NULL; #endif sk->data_ready = xprt->old_data_ready; + sk->no_check = 0; sk->state_change = xprt->old_state_change; sk->write_space = xprt->old_write_space; @@ -563,18 +567,61 @@ xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) return; } -/* - * Input handler for RPC replies. Called from a bottom half and hence +/* We have set things up such that we perform the checksum of the UDP + * packet in parallel with the copies into the RPC client iovec. -DaveM + */ +static int csum_partial_copy_to_page_cache(struct iovec *iov, + struct sk_buff *skb, + int copied) +{ + __u8 *pkt_data = skb->data + sizeof(struct udphdr); + __u8 *cur_ptr = iov->iov_base; + __kernel_size_t cur_len = iov->iov_len; + unsigned int csum = skb->csum; + int need_csum = (skb->ip_summed != CHECKSUM_UNNECESSARY); + int slack = skb->len - copied - sizeof(struct udphdr); + + if (need_csum) + csum = csum_partial(skb->h.raw, sizeof(struct udphdr), csum); + while (copied > 0) { + if (cur_len) { + int to_move = cur_len; + if (to_move > copied) + to_move = copied; + if (need_csum) + csum = csum_partial_copy_nocheck(pkt_data, cur_ptr, + to_move, csum); + else + memcpy(cur_ptr, pkt_data, to_move); + pkt_data += to_move; + copied -= to_move; + cur_ptr += to_move; + cur_len -= to_move; + } + if (cur_len <= 0) { + iov++; + cur_len = iov->iov_len; + cur_ptr = iov->iov_base; + } + } + if (need_csum) { + if (slack > 0) + csum = csum_partial(pkt_data, slack, csum); + if ((unsigned short)csum_fold(csum)) + return -1; + } + return 0; +} + +/* Input handler for RPC replies. Called from a bottom half and hence * atomic. */ static inline void udp_data_ready(struct sock *sk, int len) { - struct rpc_task *task; struct rpc_xprt *xprt; struct rpc_rqst *rovr; struct sk_buff *skb; - struct iovec iov[MAX_IOVEC]; int err, repsize, copied; dprintk("RPC: udp_data_ready...\n"); @@ -584,28 +631,31 @@ udp_data_ready(struct sock *sk, int len) if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) return; - repsize = skb->len - 8; /* don't account for UDP header */ + repsize = skb->len - sizeof(struct udphdr); if (repsize < 4) { printk("RPC: impossible RPC reply size %d!\n", repsize); goto dropit; } /* Look up the request corresponding to the given XID */ - if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8)))) + if (!(rovr = xprt_lookup_rqst(xprt, + *(u32 *) (skb->h.raw + sizeof(struct udphdr))))) goto dropit; - task = rovr->rq_task; - dprintk("RPC: %4d received reply\n", task->tk_pid); - xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize); + dprintk("RPC: %4d received reply\n", rovr->rq_task->tk_pid); + xprt_pktdump("packet data:", + (u32 *) (skb->h.raw + sizeof(struct udphdr)), repsize); if ((copied = rovr->rq_rlen) > repsize) copied = repsize; - /* Okay, we have it. Copy datagram... */ - memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0])); - /* This needs to stay tied with the usermode skb_copy_dagram... */ - memcpy_tokerneliovec(iov, skb->data+8, copied); + /* Suck it into the iovec, verify checksum if not done by hw. */ + if (csum_partial_copy_to_page_cache(rovr->rq_rvec, skb, copied)) + goto dropit; + + /* Something worked... */ + dst_confirm(skb->dst); xprt_complete_rqst(xprt, rovr, copied); @@ -1341,6 +1391,7 @@ xprt_setup(struct socket *sock, int proto, xprt->old_write_space = inet->write_space; if (proto == IPPROTO_UDP) { inet->data_ready = udp_data_ready; + inet->no_check = UDP_CSUM_NORCV; } else { inet->data_ready = tcp_data_ready; inet->state_change = tcp_state_change; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 21614a3c6..1d12037da 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.76 1999/05/08 05:54:55 davem Exp $ + * Version: $Id: af_unix.c,v 1.78 1999/05/27 00:38:41 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -114,8 +114,8 @@ int sysctl_unix_max_dgram_qlen = 10; unix_socket *unix_socket_table[UNIX_HASH_SIZE+1]; static atomic_t unix_nr_socks = ATOMIC_INIT(0); -static struct wait_queue * unix_ack_wqueue = NULL; -static struct wait_queue * unix_dgram_wqueue = NULL; +static DECLARE_WAIT_QUEUE_HEAD(unix_ack_wqueue); +static DECLARE_WAIT_QUEUE_HEAD(unix_dgram_wqueue); #define unix_sockets_unbound (unix_socket_table[UNIX_HASH_SIZE]) @@ -144,19 +144,21 @@ extern __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk) return (unix_peer(osk) == NULL || unix_our_peer(sk, osk)); } +#define ulock(sk) (&(sk->protinfo.af_unix.user_count)) + extern __inline__ void unix_lock(unix_socket *sk) { - atomic_inc(&sk->sock_readers); + atomic_inc(ulock(sk)); } extern __inline__ void unix_unlock(unix_socket *sk) { - atomic_dec(&sk->sock_readers); + atomic_dec(ulock(sk)); } extern __inline__ int unix_locked(unix_socket *sk) { - return atomic_read(&sk->sock_readers); + return (atomic_read(ulock(sk)) != 0); } extern __inline__ void unix_release_addr(struct unix_address *addr) @@ -433,7 +435,7 @@ static struct sock * unix_create1(struct socket *sock, int stream) sk->destruct = unix_destruct_addr; sk->protinfo.af_unix.family=PF_UNIX; sk->protinfo.af_unix.dentry=NULL; - sk->protinfo.af_unix.readsem=MUTEX; /* single task reading lock */ + init_MUTEX(&sk->protinfo.af_unix.readsem);/* single task reading lock */ sk->protinfo.af_unix.list=&unix_sockets_unbound; unix_insert_socket(sk); @@ -1511,7 +1513,7 @@ static int unix_read_proc(char *buffer, char **start, off_t offset, { len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld", s, - atomic_read(&s->sock_readers), + atomic_read(ulock(s)), 0, s->socket ? s->socket->flags : 0, s->type, diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c index 18942bd20..0c91e4c44 100644 --- a/net/wanrouter/wanmain.c +++ b/net/wanrouter/wanmain.c @@ -30,6 +30,7 @@ * Dec 22, 1998 Arnaldo Melo vmalloc/vfree used in device_setup to allocate * kernel memory and copy configuration data to * kernel space (for big firmwares) +* May 19, 1999 Arnaldo Melo __initfunc in wanrouter_init *****************************************************************************/ #include <linux/config.h> @@ -104,17 +105,18 @@ static unsigned char oui_802_2[] = { 0x00, 0x80, 0xC2 }; #endif #ifndef MODULE - -int wanrouter_init(void) +__initfunc(int wanrouter_init(void)) { int err; - extern void wanpipe_init(void); + extern int wanpipe_init(void), + cyclomx_init(void); printk(KERN_INFO "%s v%u.%u %s\n", fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright); err = wanrouter_proc_init(); if (err) - printk(KERN_ERR "%s: can't create entry in proc filesystem!\n", modname); + printk(KERN_ERR "%s: can't create entry in proc filesystem!\n", + modname); /* * Initialise compiled in boards @@ -123,6 +125,9 @@ int wanrouter_init(void) #ifdef CONFIG_VENDOR_SANGOMA wanpipe_init(); #endif +#ifdef CONFIG_CYCLADES_SYNC + cyclomx_init(); +#endif return err; } @@ -187,7 +192,6 @@ void cleanup_module (void) * Context: process */ - int register_wan_device(wan_device_t* wandev) { int err, namelen; @@ -207,7 +211,6 @@ int register_wan_device(wan_device_t* wandev) printk(KERN_INFO "%s: registering WAN device %s\n", modname, wandev->name); #endif - /* * Register /proc directory entry */ diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index e7f894e8e..a4f070023 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1336,14 +1336,17 @@ int init_module(void) /* * Register any pre existing devices. */ - for (dev = dev_base; dev != NULL; dev = dev->next) + read_lock(&dev_base_lock); + for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) || dev->type == ARPHRD_ETHER #endif - )) - x25_link_device_up(dev); - + )) + x25_link_device_up(dev); + } + read_unlock(&dev_base_lock); + return 0; } |