patch-2.3.15 linux/net/decnet/af_decnet.c
Next file: linux/net/decnet/dn_dev.c
Previous file: linux/net/decnet/TODO
Back to the patch index
Back to the overall index
- Lines: 1049
- Date:
Mon Aug 23 10:01:02 1999
- Orig file:
v2.3.14/linux/net/decnet/af_decnet.c
- Orig date:
Wed Aug 18 11:38:49 1999
diff -u --recursive --new-file v2.3.14/linux/net/decnet/af_decnet.c linux/net/decnet/af_decnet.c
@@ -30,6 +30,8 @@
* Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username.
* Steve Whitehouse: Fixes to connect() error returns.
* Patrick Caulfield: Fixes to delayed acceptance logic.
+ * David S. Miller: New socket locking
+ * Steve Whitehouse: Socket list hashing/locking
*/
@@ -136,11 +138,99 @@
int decnet_node_type = DN_RT_INFO_ENDN;
static struct proto_ops dn_proto_ops;
+rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
static struct sock *dn_sklist = NULL;
static struct sock *dn_wild_sk = NULL;
-static int _dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
-static int _dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);
+static int __dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
+static int __dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);
+
+static struct sock **dn_find_list(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (scp->addr.sdn_flags & SDF_WILD)
+ return dn_wild_sk ? NULL : &dn_wild_sk;
+
+ return &dn_sklist;
+}
+
+static unsigned short port_alloc(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+static unsigned short port = 0x2000;
+
+ if (port == 0)
+ port++;
+
+ scp->addrloc = port++;
+
+ return 1;
+}
+
+/*
+ * Since this is only ever called from user
+ * level, we don't need a write_lock() version
+ * of this.
+ */
+static int dn_hash_sock(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sock **skp;
+ int rv = -EUSERS;
+
+ write_lock_bh(&dn_hash_lock);
+
+ if (!scp->addrloc && !port_alloc(sk))
+ goto out;
+
+ rv = -EADDRINUSE;
+ if ((skp = dn_find_list(sk)) == NULL)
+ goto out;
+
+ sk->next = *skp;
+ sk->pprev = skp;
+ *skp = sk;
+ rv = 0;
+out:
+ write_unlock_bh(&dn_hash_lock);
+ return rv;
+}
+
+static void dn_unhash_sock(struct sock *sk)
+{
+ struct sock **skp = sk->pprev;
+
+ if (skp == NULL)
+ return;
+
+ write_lock(&dn_hash_lock);
+ while(*skp != sk)
+ skp = &((*skp)->next);
+ *skp = sk->next;
+ write_unlock(&dn_hash_lock);
+
+ sk->next = NULL;
+ sk->pprev = NULL;
+}
+
+static void dn_unhash_sock_bh(struct sock *sk)
+{
+ struct sock **skp = sk->pprev;
+
+ if (skp == NULL)
+ return;
+
+ write_lock_bh(&dn_hash_lock);
+ while(*skp != sk)
+ skp = &((*skp)->next);
+ *skp = sk->next;
+ write_unlock_bh(&dn_hash_lock);
+
+ sk->next = NULL;
+ sk->pprev = NULL;
+}
+
int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
{
@@ -154,16 +244,16 @@
break;
case 1:
*buf++ = 0;
- *buf++ = sdn->sdn_objnamel;
- memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
- len = 3 + sdn->sdn_objnamel;
+ *buf++ = dn_ntohs(sdn->sdn_objnamel);
+ memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
+ len = 3 + dn_ntohs(sdn->sdn_objnamel);
break;
case 2:
memset(buf, 0, 5);
buf += 5;
- *buf++ = sdn->sdn_objnamel;
- memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
- len = 7 + sdn->sdn_objnamel;
+ *buf++ = dn_ntohs(sdn->sdn_objnamel);
+ memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
+ len = 7 + dn_ntohs(sdn->sdn_objnamel);
break;
}
@@ -184,7 +274,7 @@
int namel = 12;
sdn->sdn_objnum = 0;
- sdn->sdn_objnamel = 0;
+ sdn->sdn_objnamel = dn_htons(0);
memset(sdn->sdn_objname, 0, DN_MAXOBJL);
if (len < 2)
@@ -196,7 +286,7 @@
switch(*fmt) {
case 0:
- sdn->sdn_objnum = type;
+ sdn->sdn_objnum = dn_htons(type);
return 2;
case 1:
namel = 16;
@@ -218,13 +308,13 @@
if (len < 0)
return -1;
- sdn->sdn_objnamel = *data++;
- len -= sdn->sdn_objnamel;
+ sdn->sdn_objnamel = dn_htons(*data++);
+ len -= dn_ntohs(sdn->sdn_objnamel);
- if ((len < 0) || (sdn->sdn_objnamel > namel))
+ if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel))
return -1;
- memcpy(sdn->sdn_objname, data, sdn->sdn_objnamel);
+ memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel));
return size - len;
}
@@ -233,6 +323,7 @@
{
struct sock *sk;
+ read_lock(&dn_hash_lock);
for(sk = dn_sklist; sk != NULL; sk = sk->next) {
struct dn_scp *scp = &sk->protinfo.dn;
if (sk->state != TCP_LISTEN)
@@ -245,64 +336,28 @@
continue;
if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
continue;
- if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, addr->sdn_objnamel) != 0)
+ if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0)
continue;
}
+ sock_hold(sk);
+ read_unlock(&dn_hash_lock);
return sk;
}
- return (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN)) ? dn_wild_sk : NULL;
-}
-
-struct sock *dn_sklist_find(unsigned short port)
-{
- struct sock *s;
-
- for (s = dn_sklist; s != NULL; s = s->next) {
- if (s->protinfo.dn.addrloc == port) {
- return s;
- }
- }
-
- return NULL;
-}
-
-static struct sock *dn_sklist_find_by_objnum(unsigned char objnum)
-{
- struct sock *s;
-
- for (s = dn_sklist; s != NULL; s = s->next) {
- if ((s->protinfo.dn.addr.sdn_objnum == objnum) &&
- (s->state == TCP_LISTEN)) {
- return s;
- }
- }
- return NULL;
-}
+ if (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN))
+ sock_hold((sk = dn_wild_sk));
-static struct sock *dn_sklist_find_by_name(char *name)
-{
- struct sock *s;
-
- for (s = dn_sklist; s != NULL; s = s->next) {
- if (s->protinfo.dn.addr.sdn_objnum)
- continue;
- if ((memcmp(s->protinfo.dn.addr.sdn_objname,name,
- s->protinfo.dn.addr.sdn_objnamel) == 0)
- && (s->state == TCP_LISTEN)) {
- return s;
- }
- }
- return NULL;
+ read_unlock(&dn_hash_lock);
+ return sk;
}
-
struct sock *dn_find_by_skb(struct sk_buff *skb)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct sock *sk;
struct dn_scp *scp;
+ read_lock(&dn_hash_lock);
for(sk = dn_sklist; sk != NULL; sk = sk->next) {
scp = &sk->protinfo.dn;
if (cb->src != dn_saddr2dn(&scp->peer))
@@ -314,27 +369,14 @@
break;
}
- return sk;
-}
-
-
-unsigned short dn_alloc_port(void)
-{
- struct sock *sk;
- static unsigned short dn_port = 0x2000;
- short port;
+ if (sk)
+ sock_hold(sk);
- start_bh_atomic();
+ read_unlock(&dn_hash_lock);
- do {
- port = dn_port++;
- sk = dn_sklist_find(port);
- } while((sk != NULL) || (port == 0));
-
- end_bh_atomic();
+ return sk;
+}
- return dn_htons(port);
-};
static void dn_destruct(struct sock *sk)
@@ -453,14 +495,12 @@
switch(scp->state) {
case DN_DI:
- /* printk(KERN_DEBUG "dn_destroy_timer: DI\n"); */
dn_send_disc(sk, NSP_DISCINIT, 0);
if (scp->nsp_rxtshift >= decnet_di_count)
scp->state = DN_CN;
return 0;
case DN_DR:
- /* printk(KERN_DEBUG "dn_destroy_timer: DR\n"); */
dn_send_disc(sk, NSP_DISCINIT, 0);
if (scp->nsp_rxtshift >= decnet_dr_count)
scp->state = DN_DRC;
@@ -476,19 +516,16 @@
scp->persist = (HZ * decnet_time_wait);
-/* printk(KERN_DEBUG "dn_destroy_timer: testing dead\n"); */
-
if (sk->socket)
return 0;
dn_stop_fast_timer(sk); /* unlikely, but possible that this is runninng */
if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) {
- sklist_destroy_socket(&dn_sklist, sk);
+ dn_unhash_sock(sk);
+ sock_put(sk);
return 1;
}
- /*printk(KERN_DEBUG "dn_destroy_timer: dead 'n' waiting...\n"); */
-
return 0;
}
@@ -538,18 +575,12 @@
default:
printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
case DN_O:
- start_bh_atomic();
dn_stop_fast_timer(sk);
dn_stop_slow_timer(sk);
- if (sk == dn_wild_sk) {
- dn_wild_sk = NULL;
- sklist_destroy_socket(NULL, sk);
- } else {
- sklist_destroy_socket(&dn_sklist, sk);
- }
+ dn_unhash_sock_bh(sk);
+ sock_put(sk);
- end_bh_atomic();
break;
}
}
@@ -606,52 +637,6 @@
return "????";
}
-static int dn_get_info(char *buffer, char **start, off_t offset,
- int length, int dummy)
-{
- struct sock *sk;
- int len = 0;
- off_t pos = 0;
- off_t begin = 0;
- char buf[DN_ASCBUF_LEN];
-
- len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n",
- "Remote","Source","Remote","Object","Link ",
- " Data Packets ","Link Packets");
- len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n\n",
- "Node ","Port ","Port ","Number","State",
- " Out In "," Out In");
- start_bh_atomic();
- for (sk = dn_sklist; sk != NULL; sk = sk->next) {
- len += sprintf(buffer+len,
- "%6s %04X %04X %6d %4s %6d %6d %6d %6d\n",
-
- dn_addr2asc(dn_ntohs(dn_saddr2dn(&sk->protinfo.dn.peer)), buf),
- sk->protinfo.dn.addrloc,sk->protinfo.dn.addrrem,
- sk->protinfo.dn.addr.sdn_objnum,
- dn_state2asc(sk->protinfo.dn.state),
- sk->protinfo.dn.numdat, sk->protinfo.dn.numdat_rcv,
- sk->protinfo.dn.numoth, sk->protinfo.dn.numoth_rcv);
-
- pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- if (pos > offset+length)
- break;
- }
- end_bh_atomic();
-
- *start = buffer + (offset - begin);
- len -= (offset - begin);
-
- if (len > length)
- len = length;
-
- return len;
-}
-
static int dn_create(struct socket *sock, int protocol)
{
struct sock *sk;
@@ -688,7 +673,7 @@
static int
-dn_release(struct socket *sock, struct socket *peer)
+dn_release(struct socket *sock)
{
struct sock *sk = sock->sk;
@@ -705,8 +690,11 @@
static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- struct sock *sk = sock->sk;
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+ struct net_device *dev;
+ int rv;
if (sk->zapped == 0)
return -EINVAL;
@@ -717,50 +705,47 @@
if (saddr->sdn_family != AF_DECnet)
return -EINVAL;
+ if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2))
+ return -EINVAL;
+
if (saddr->sdn_objnum && !suser())
return -EPERM;
- if (!saddr->sdn_objname && (saddr->sdn_objnamel > DN_MAXOBJL))
+ if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL)
return -EINVAL;
if (saddr->sdn_flags & ~SDF_WILD)
return -EINVAL;
- if ((saddr->sdn_flags & SDF_WILD) && !suser())
- return -EPERM;
-
- start_bh_atomic();
-
if (saddr->sdn_flags & SDF_WILD) {
- if (dn_wild_sk) {
- end_bh_atomic();
- return -EADDRINUSE;
- }
- dn_wild_sk = sk;
- sk->zapped = 0;
- memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
- end_bh_atomic();
- return 0;
- }
-
- if (saddr->sdn_objnum && dn_sklist_find_by_objnum(saddr->sdn_objnum)) {
- end_bh_atomic();
- return -EADDRINUSE;
- }
-
- if (!saddr->sdn_objnum) {
- if (dn_sklist_find_by_name(saddr->sdn_objname)) {
- end_bh_atomic();
- return -EADDRINUSE;
+ if (!suser())
+ return -EPERM;
+ } else {
+ if (dn_ntohs(saddr->sdn_nodeaddrl)) {
+ read_lock(&dev_base_lock);
+ for(dev = dev_base; dev; dev = dev->next) {
+ if (!dev->dn_ptr)
+ continue;
+ if (dn_dev_islocal(dev, dn_saddr2dn(saddr)))
+ break;
+ }
+ read_unlock(&dev_base_lock);
+ if (dev == NULL)
+ return -EADDRNOTAVAIL;
}
}
- memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
+
+ memcpy(&scp->addr, saddr, addr_len);
sk->zapped = 0;
- sklist_insert_socket(&dn_sklist, sk);
- end_bh_atomic();
- return 0;
+ if ((rv = dn_hash_sock(sk)) == 0)
+ goto out;
+
+ sk->zapped = 1;
+out:
+
+ return rv;
}
@@ -781,17 +766,17 @@
if ((scp->accessdata.acc_accl != 0) &&
(scp->accessdata.acc_accl <= 12)) {
- scp->addr.sdn_objnamel = scp->accessdata.acc_accl;
- memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, scp->addr.sdn_objnamel);
+ scp->addr.sdn_objnamel = dn_htons(scp->accessdata.acc_accl);
+ memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, dn_ntohs(scp->addr.sdn_objnamel));
scp->accessdata.acc_accl = 0;
memset(scp->accessdata.acc_acc, 0, 40);
}
- scp->addr.sdn_add.a_len = 2;
+ scp->addr.sdn_add.a_len = dn_htons(2);
*(dn_address *)scp->addr.sdn_add.a_addr = decnet_address;
- sklist_insert_socket(&dn_sklist, sk);
+ dn_hash_sock(sk);
return 0;
}
@@ -801,6 +786,7 @@
{
struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
int err = -EISCONN;
lock_sock(sk);
@@ -835,10 +821,10 @@
if (sk->zapped && (err = dn_auto_bind(sock)))
goto out;
- memcpy(&sk->protinfo.dn.peer, addr, addr_len);
+ memcpy(&scp->peer, addr, addr_len);
err = -EHOSTUNREACH;
- if (dn_route_output(sk) < 0)
+ if (dn_route_output(&sk->dst_cache, dn_saddr2dn(&scp->peer), dn_saddr2dn(&scp->addr), 0) < 0)
goto out;
sk->state = TCP_SYN_SENT;
@@ -925,8 +911,6 @@
{
struct sock *sk = sock->sk;
- /* printk(KERN_DEBUG "dn_wait_accept: in\n"); */
-
while(sk->state == TCP_LISTEN) {
if (flags & O_NONBLOCK) {
return -EAGAIN;
@@ -939,10 +923,8 @@
SOCK_SLEEP_POST(sk)
- if (signal_pending(current)) {
- /* printk(KERN_DEBUG "dn_wait_accept: signal\n"); */
+ if (signal_pending(current))
return -ERESTARTSYS; /* But of course you don't! */
- }
}
if ((sk->protinfo.dn.state != DN_RUN) && (sk->protinfo.dn.state != DN_DRC)) {
@@ -951,16 +933,15 @@
}
sock->state = SS_CONNECTED;
- /* printk(KERN_DEBUG "dn_wait_accept: out\n"); */
return 0;
}
-static int dn_accept(struct socket *sock, struct socket *newsock,int flags)
+static int dn_accept(struct socket *sock, struct socket *newsock, int flags)
{
- struct sock *sk=sock->sk, *newsk;
- struct sk_buff *skb = NULL;
+ struct sock *sk = sock->sk, *newsk;
+ struct sk_buff *skb = NULL;
struct dn_skb_cb *cb;
unsigned char menuver;
int err = 0;
@@ -978,15 +959,8 @@
return -EINVAL;
}
- if (newsock->sk != NULL) {
- newsock->sk->socket = NULL;
- dn_destroy_sock(newsock->sk);
- newsock->sk = NULL;
- }
-
do
{
- /* printk(KERN_DEBUG "dn_accept: loop top\n"); */
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL)
{
if (flags & O_NONBLOCK)
@@ -1023,9 +997,7 @@
dst_release(xchg(&newsk->dst_cache, skb->dst));
skb->dst = NULL;
-
newsk->protinfo.dn.state = DN_CR;
- newsk->protinfo.dn.addrloc = dn_alloc_port();
newsk->protinfo.dn.addrrem = cb->src_port;
newsk->protinfo.dn.mss = cb->segsize;
newsk->protinfo.dn.accept_mode = sk->protinfo.dn.accept_mode;
@@ -1041,6 +1013,7 @@
skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.addr, &type));
skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.peer, &type));
*(dn_address *)newsk->protinfo.dn.peer.sdn_add.a_addr = cb->src;
+ *(dn_address *)newsk->protinfo.dn.addr.sdn_add.a_addr = cb->dst;
menuver = *skb->data;
skb_pull(skb, 1);
@@ -1065,7 +1038,7 @@
sizeof(struct optdata_dn));
lock_sock(newsk);
- sklist_insert_socket(&dn_sklist, newsk);
+ dn_hash_sock(newsk);
dn_send_conn_ack(newsk);
@@ -1091,7 +1064,7 @@
lock_sock(sk);
if (peer) {
- if (sock->state != SS_CONNECTED && sk->protinfo.dn.accept_mode == ACC_IMMED)
+ if (sock->state != SS_CONNECTED && scp->accept_mode == ACC_IMMED)
return -ENOTCONN;
memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
@@ -1259,12 +1232,15 @@
static int dn_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
+ int err = -EINVAL;
+
+ lock_sock(sk);
if (sk->zapped)
- return -EINVAL;
+ goto out;
if ((sk->protinfo.dn.state != DN_O) || (sk->state == TCP_LISTEN))
- return -EINVAL;
+ goto out;
if (backlog > SOMAXCONN)
backlog = SOMAXCONN;
@@ -1272,8 +1248,12 @@
sk->max_ack_backlog = backlog;
sk->ack_backlog = 0;
sk->state = TCP_LISTEN;
+ err = 0;
- return 0;
+out:
+ release_sock(sk);
+
+ return err;
}
@@ -1312,10 +1292,17 @@
static int dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
- return _dn_setsockopt(sock, level, optname, optval, optlen, 0);
+ struct sock *sk = sock->sk;
+ int err;
+
+ lock_sock(sk);
+ err = __dn_setsockopt(sock, level, optname, optval, optlen, 0);
+ release_sock(sk);
+
+ return err;
}
-static int _dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags)
+static int __dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags)
{
struct sock *sk = sock->sk;
struct dn_scp *scp = &sk->protinfo.dn;
@@ -1406,7 +1393,6 @@
break;
case DSO_CONACCEPT:
- lock_sock(sk);
if (scp->state != DN_CR)
return -EINVAL;
@@ -1414,11 +1400,9 @@
scp->state = DN_CC;
dn_send_conn_conf(sk);
err = dn_wait_accept(sock, sock->file->f_flags);
- release_sock(sk);
return err;
case DSO_CONREJECT:
- lock_sock(sk);
if (scp->state != DN_CR)
return -EINVAL;
@@ -1426,11 +1410,9 @@
scp->state = DN_DR;
sk->shutdown = SHUTDOWN_MASK;
dn_send_disc(sk, 0x38, 0);
- release_sock(sk);
break;
#ifdef CONFIG_DECNET_FW
- case DN_FW_MASQ_TIMEOUTS:
case DN_FW_APPEND:
case DN_FW_REPLACE:
case DN_FW_DELETE:
@@ -1465,11 +1447,17 @@
static int dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
- return _dn_getsockopt(sock, level, optname, optval, optlen, 0);
+ struct sock *sk = sock->sk;
+ int err;
+
+ lock_sock(sk);
+ err = __dn_getsockopt(sock, level, optname, optval, optlen, 0);
+ release_sock(sk);
+
+ return err;
}
-static int
-_dn_getsockopt(struct socket *sock, int level,int optname, char *optval,int *optlen, int flags)
+static int __dn_getsockopt(struct socket *sock, int level,int optname, char *optval,int *optlen, int flags)
{
struct sock *sk = sock->sk;
struct dn_scp *scp = &sk->protinfo.dn;
@@ -1552,8 +1540,6 @@
struct dn_scp *scp = &sk->protinfo.dn;
int err = 0;
- /* printk(KERN_DEBUG "dn_wait_run %d\n", scp->state); */
-
switch(scp->state) {
case DN_RUN:
return 0;
@@ -1567,7 +1553,6 @@
break;
default:
return -ENOTCONN;
- goto out;
}
if (flags & MSG_DONTWAIT)
@@ -1575,11 +1560,11 @@
do {
if ((err = sock_error(sk)) != 0)
- goto out;
+ break;
if (signal_pending(current)) {
err = -ERESTARTSYS;
- goto out;
+ break;
}
SOCK_SLEEP_PRE(sk)
@@ -1591,8 +1576,6 @@
} while(scp->state != DN_RUN);
-out:
-
return 0;
}
@@ -1621,6 +1604,8 @@
/* minimum data length for read exceeded */
if (len >= target)
return 1;
+
+ skb = skb->next;
}
return 0;
@@ -1636,8 +1621,9 @@
int target = size > 1 ? 1 : 0;
int copied = 0;
int rv = 0;
- struct sk_buff *skb = NULL, **pskb;
+ struct sk_buff *skb, *nskb;
struct dn_skb_cb *cb = NULL;
+ unsigned char eor = 0;
lock_sock(sk);
@@ -1710,8 +1696,7 @@
sock->flags &= ~SO_WAITDATA;
}
- pskb = &((struct sk_buff *)queue)->next;
- while((skb = *pskb) != (struct sk_buff *)queue) {
+ for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) {
int chunk = skb->len;
cb = (struct dn_skb_cb *)skb->cb;
@@ -1727,27 +1712,29 @@
if (!(flags & MSG_PEEK))
skb->len -= chunk;
+ eor = cb->nsp_flags & 0x40;
+ nskb = skb->next;
+
if (skb->len == 0) {
skb_unlink(skb);
kfree_skb(skb);
+ /*
+ * N.B. Don't refer to skb or cb after this point
+ * in loop.
+ */
if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
scp->flowloc_sw = DN_SEND;
dn_nsp_send_lnk(sk, DN_SEND);
}
}
- pskb = &skb->next;
-
- if (cb->nsp_flags & 0x40) {
- if (sk->type == SOCK_SEQPACKET) {
- msg->msg_flags |= MSG_EOR;
+ if (eor) {
+ if (sk->type == SOCK_SEQPACKET)
+ break;
+ if (!(flags & MSG_WAITALL))
break;
- }
}
- if (!(flags & MSG_WAITALL))
- break;
-
if (flags & MSG_OOB)
break;
@@ -1756,10 +1743,19 @@
}
rv = copied;
+
+ if (eor && (sk->type == SOCK_SEQPACKET))
+ msg->msg_flags |= MSG_EOR;
+
out:
if (rv == 0)
rv = (flags & MSG_PEEK) ? -sk->err : sock_error(sk);
+ if ((rv >= 0) && msg->msg_name) {
+ memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn));
+ msg->msg_namelen = sizeof(struct sockaddr_dn);
+ }
+
release_sock(sk);
return rv;
@@ -1787,7 +1783,7 @@
unsigned short ack;
int len;
- if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT))
+ if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT|MSG_EOR))
return -EOPNOTSUPP;
if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
@@ -1913,14 +1909,18 @@
} else {
cb->segnum = scp->numdat++;
scp->numdat &= 0x0fff;
+ msgflg = 0x00;
if (sock->type == SOCK_STREAM)
msgflg = 0x60;
- else
- msgflg = 0x00;
- if (numseg == 0)
+ if (scp->seg_size == 0)
msgflg |= 0x20;
- if ((sent + len) == size)
+
+ scp->seg_size += len;
+
+ if (((sent + len) == size) && (flags & MSG_EOR)) {
msgflg |= 0x40;
+ scp->seg_size = 0;
+ }
ack = scp->ackxmt_dat | 0x8000;
}
@@ -1987,6 +1987,61 @@
};
#ifdef CONFIG_PROC_FS
+
+static int dn_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct sock *sk;
+ struct dn_scp *scp;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ char buf1[DN_ASCBUF_LEN];
+ char buf2[DN_ASCBUF_LEN];
+
+ len += sprintf(buffer + len, "Local Remote\n");
+
+ read_lock(&dn_hash_lock);
+ for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ scp = &sk->protinfo.dn;
+
+ len += sprintf(buffer + len,
+ "%6s/%04X %04d:%04d %04d:%04d %01d %6s/%04X %04d:%04d %04d:%04d %01d %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,
+ 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,
+ 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;
+ }
+ read_unlock(&dn_hash_lock);
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length)
+ len = length;
+
+ return len;
+}
+
struct proc_dir_entry decnet_linkinfo = {
PROC_NET_DN_SKT, 6, "decnet", S_IFREG | S_IRUGO,
1, 0, 0, 0, &proc_net_inode_operations, dn_get_info
@@ -2011,7 +2066,6 @@
static struct proto_ops dn_proto_ops = {
AF_DECnet,
- sock_no_dup,
dn_release,
dn_bind,
dn_connect,
@@ -2026,7 +2080,8 @@
dn_getsockopt,
sock_no_fcntl,
dn_sendmsg,
- dn_recvmsg
+ dn_recvmsg,
+ sock_no_mmap
};
#ifdef CONFIG_SYSCTL
@@ -2061,46 +2116,41 @@
#ifdef CONFIG_SYSCTL
dn_register_sysctl();
#endif /* CONFIG_SYSCTL */
- printk(KERN_INFO "DECnet for Linux: V.2.2.5s (C) 1995-1999 Linux DECnet Project Team\n");
+ printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.13s (C) 1995-1999 Linux DECnet Project Team\n");
}
-void __init decnet_setup(char *str, int *ints)
+#ifndef MODULE
+static int __init decnet_setup(char *str)
{
+ unsigned short area = simple_strtoul(str, &str, 0);
+ unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
+ unsigned short type = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
- if ((ints[0] == 2) || (ints[0] == 3)) {
-
- if (ints[1] < 0)
- ints[1] = 0;
- if (ints[1] > 63)
- ints[1] = 63;
-
- if (ints[2] < 0)
- ints[2] = 0;
- if (ints[2] > 1023)
- ints[2] = 1023;
+ decnet_address = dn_htons(area << 10 | node);
+ dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
- decnet_address = dn_htons(ints[1] << 10 | ints[2]);
- dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
-
- if (ints[0] == 3) {
- switch(ints[3]) {
+ switch(type) {
+ default:
+ printk(KERN_INFO "Invalid DECnet node type, switching to EndNode\n");
+ case 0:
+ decnet_node_type = DN_RT_INFO_ENDN;
+ break;
#ifdef CONFIG_DECNET_ROUTER
- case 1:
- decnet_node_type = DN_RT_INFO_L1RT;
- break;
- case 2:
- decnet_node_type = DN_RT_INFO_L2RT;
- break;
+ case 1:
+ decnet_node_type = DN_RT_INFO_L1RT;
+ break;
+ case 2:
+ decnet_node_type = DN_RT_INFO_L2RT;
+ break;
#endif /* CONFIG_DECNET_ROUTER */
- default:
- decnet_node_type = DN_RT_INFO_ENDN;
- }
- }
- } else {
- printk(KERN_ERR "DECnet: Invalid command line options\n");
}
+
+ return 0;
}
+
+__setup("decnet=", decnet_setup);
+#endif
#ifdef MODULE
EXPORT_NO_SYMBOLS;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)