[Linux-decnet-user] Patch to 2.4.15-pre5 to fix local port alloc bug
Brought to you by:
chrissie_c,
ph3-der-loewe
|
From: Steven W. <st...@gw...> - 2001-11-18 14:14:36
|
Hi,
Here is a patch with the following features:
1. Fixes local port allocation bug (we didn't check that a port wasn't already
in use before using it before)
2. Hash socket list so that systems with large numbers of sockets (either
listening or in data transfer modes) should work faster. As a side effect
this also speeds up the checking required by feature 1.
3. Various documentation tweeks and updates
Still to do: Add code to send a message to connecting nodes when we run out
of local ports (see comment in dn_accept()).
Also, please note that this code
limits the maximum number of DECnet sockets (system wide) to 0xffff - 1. There
isn't really any need for this limit because if you take the local and remote
DECnet addresses into account during socket demultiplexing then you should be
able to have 0xffff - 1 connections to each remote node. I'm intending though
to use the rather simpler code in this patch for 2.4 and solve the problem
properly in 2.5. It won't be on my urgent problems list unless someone tells
me that they are finding the limits restrictive in the mean time though, so if
its a problem for you, let me know.
Please test this patch and let me know if it works (I've tested it and it
seems to work for me). Once I've got a few reports I'll request this to be
included in 2.4,
Steve.
-----------------------------------------------------------------------------
diff -Nru linux-2.4.15-pre5/Documentation/networking/decnet.txt linux/Documentation/networking/decnet.txt
--- linux-2.4.15-pre5/Documentation/networking/decnet.txt Sun Mar 11 21:33:27 2001
+++ linux/Documentation/networking/decnet.txt Sat Nov 17 20:12:02 2001
@@ -4,17 +4,10 @@
1) Other documentation....
o Project Home Pages
- http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html - Kernel info
+ http://www.chygwyn.com/DECnet/ - Kernel info
http://linux-decnet.sourceforge.net/ - Userland tools
o FTP sites
- ftp://ftp.sucs.swan.ac.uk/pub/Linux/DECnet/
- - Swansea University Computer Society DECnet Archive
- (contains kernel patches and info)
- - Mirror of userland tools on ftp.dreamtime.org
- - Mirror of Alexey Kuznetsov's iproute2 package and
- other utilities
-
ftp://linux-decnet.sourceforge.net/pub/linux-decnet/
- Patrick Caulfield's archive of userland tools and
Eduardo Serrat's kernel patches
@@ -34,6 +27,10 @@
CONFIG_NETLINK (to allow rtnetlink)
CONFIG_RTNETLINK (for communication with the kernel routing layer)
CONFIG_NETFILTER (will be required for the DECnet routing daemon)
+
+Don't turn on SIOCGIFCONF support for DECnet unless you are really sure
+that you need it, in general you won't and it can cause ifconfig to
+malfunction.
3) Command line options
diff -Nru linux-2.4.15-pre5/net/decnet/README linux/net/decnet/README
--- linux-2.4.15-pre5/net/decnet/README Wed May 26 17:36:36 1999
+++ linux/net/decnet/README Sat Nov 17 20:07:21 2001
@@ -3,6 +3,6 @@
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.
+on line at http://www.chygwyn.com/DECnet/
Steve Whitehouse <St...@AC...>
diff -Nru linux-2.4.15-pre5/net/decnet/af_decnet.c linux/net/decnet/af_decnet.c
--- linux-2.4.15-pre5/net/decnet/af_decnet.c Sat Nov 17 18:03:34 2001
+++ linux/net/decnet/af_decnet.c Sat Nov 17 23:27:51 2001
@@ -36,6 +36,7 @@
* Steve Whitehouse: Removed unused code. Fix to use sk->allocation
* when required.
* Patrick Caulfield: /proc/net/decnet now has object name/number
+ * Steve Whitehouse: Fixed local port allocation, hashed sk list
*/
@@ -139,9 +140,13 @@
dn_address decnet_address = 0;
unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
+#define DN_SK_HASH_SHIFT 8
+#define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT)
+#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)
+
static struct proto_ops dn_proto_ops;
rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
-static struct sock *dn_sklist;
+static struct sock *dn_sk_hash[DN_SK_HASH_SIZE];
static struct sock *dn_wild_sk;
static int __dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
@@ -154,18 +159,38 @@
if (scp->addr.sdn_flags & SDF_WILD)
return dn_wild_sk ? NULL : &dn_wild_sk;
- return &dn_sklist;
+ return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK];
+}
+
+/*
+ * Valid ports are those greater than zero and not already in use.
+ */
+static int check_port(unsigned short port)
+{
+ struct sock *sk = dn_sk_hash[port & DN_SK_HASH_MASK];
+ if (port == 0)
+ return -1;
+ while(sk) {
+ struct dn_scp *scp = DN_SK(sk);
+ if (scp->addrloc == port)
+ return -1;
+ sk = sk->next;
+ }
+ return 0;
}
static unsigned short port_alloc(struct sock *sk)
{
struct dn_scp *scp = DN_SK(sk);
static unsigned short port = 0x2000;
+ unsigned short i_port = port;
- if (port == 0)
- port++;
+ while(check_port(++port) != 0) {
+ if (port == i_port)
+ return 0;
+ }
- scp->addrloc = port++;
+ scp->addrloc = port;
return 1;
}
@@ -238,6 +263,44 @@
sk->pprev = NULL;
}
+struct sock **listen_hash(struct sockaddr_dn *addr)
+{
+ int i;
+ unsigned hash = addr->sdn_objnum;
+
+ if (hash == 0) {
+ hash = addr->sdn_objnamel;
+ for(i = 0; i < addr->sdn_objnamel; i++) {
+ hash ^= addr->sdn_objname[i];
+ hash ^= (hash << 3);
+ }
+ }
+
+ return &dn_sk_hash[hash & DN_SK_HASH_MASK];
+}
+
+/*
+ * Called to transform a socket from bound (i.e. with a local address)
+ * into a listening socket (doesn't need a local port number) and rehashes
+ * based upon the object name/number.
+ */
+static void dn_rehash_sock(struct sock *sk)
+{
+ struct sock **skp = sk->pprev;
+
+ write_lock_bh(&dn_hash_lock);
+ while(*skp != sk)
+ skp = &((*skp)->next);
+ *skp = sk->next;
+
+ DN_SK(sk)->addrloc = 0;
+ skp = listen_hash(&DN_SK(sk)->addr);
+
+ sk->next = *skp;
+ sk->pprev = skp;
+ *skp = sk;
+ write_unlock_bh(&dn_hash_lock);
+}
int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
{
@@ -328,10 +391,11 @@
struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
{
+ struct sock **skp = listen_hash(addr);
struct sock *sk;
read_lock(&dn_hash_lock);
- for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ for(sk = *skp; sk != NULL; sk = sk->next) {
struct dn_scp *scp = DN_SK(sk);
if (sk->state != TCP_LISTEN)
continue;
@@ -365,7 +429,8 @@
struct dn_scp *scp;
read_lock(&dn_hash_lock);
- for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ sk = dn_sk_hash[cb->dst_port & DN_SK_HASH_MASK];
+ for (; sk != NULL; sk = sk->next) {
scp = DN_SK(sk);
if (cb->src != dn_saddr2dn(&scp->peer))
continue;
@@ -1045,6 +1110,9 @@
sizeof(struct optdata_dn));
lock_sock(newsk);
+ /*
+ * FIXME: This can fail if we've run out of local ports....
+ */
dn_hash_sock(newsk);
dn_send_conn_ack(newsk);
@@ -1200,6 +1268,7 @@
sk->ack_backlog = 0;
sk->state = TCP_LISTEN;
err = 0;
+ dn_rehash_sock(sk);
out:
release_sock(sk);
@@ -2063,44 +2132,47 @@
char buf2[DN_ASCBUF_LEN];
char local_object[DN_MAXOBJL+3];
char remote_object[DN_MAXOBJL+3];
+ int i;
len += sprintf(buffer + len, "Local Remote\n");
read_lock(&dn_hash_lock);
- for(sk = dn_sklist; sk != NULL; sk = sk->next) {
- scp = DN_SK(sk);
-
- dn_printable_object(&scp->addr, local_object);
- dn_printable_object(&scp->peer, remote_object);
-
- len += sprintf(buffer + len,
- "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n",
- dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1),
- scp->addrloc,
- scp->numdat,
- scp->numoth,
- scp->ackxmt_dat,
- scp->ackxmt_oth,
- scp->flowloc_sw,
- local_object,
- dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2),
- scp->addrrem,
- scp->numdat_rcv,
- scp->numoth_rcv,
- scp->ackrcv_dat,
- scp->ackrcv_oth,
- scp->flowrem_sw,
- remote_object,
- dn_state2asc(scp->state),
- ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
-
- pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
+ for(i = 0; i < DN_SK_HASH_SIZE; i++) {
+ for(sk = dn_sk_hash[i]; sk != NULL; sk = sk->next) {
+ scp = DN_SK(sk);
+
+ dn_printable_object(&scp->addr, local_object);
+ dn_printable_object(&scp->peer, remote_object);
+
+ len += sprintf(buffer + len,
+ "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n",
+ dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1),
+ scp->addrloc,
+ scp->numdat,
+ scp->numoth,
+ scp->ackxmt_dat,
+ scp->ackxmt_oth,
+ scp->flowloc_sw,
+ local_object,
+ dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2),
+ scp->addrrem,
+ scp->numdat_rcv,
+ scp->numoth_rcv,
+ scp->ackrcv_dat,
+ scp->ackrcv_oth,
+ scp->flowrem_sw,
+ remote_object,
+ dn_state2asc(scp->state),
+ ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
+
+ pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > (offset + length))
+ break;
}
- if (pos > (offset + length))
- break;
}
read_unlock(&dn_hash_lock);
@@ -2158,7 +2230,7 @@
MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
#endif
-static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.9s (C) 1995-2001 Linux DECnet Project Team\n";
+static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.15-pre5s (C) 1995-2001 Linux DECnet Project Team\n";
static int __init decnet_init(void)
{
|