patch for 2.1.92 net/socket.c et al

Bill Hawes (whawes@transmeta.com)
Mon, 06 Apr 1998 11:48:18 -0700


This is a multi-part message in MIME format.
--------------1FAAEABCF404DAC74D92FD56
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

The attached patch to fixes a few problems in net/socket.c and related
code.

In socket.c I've changed the allocation of iovecs (when necessary) to
use the sock_malloc() call, as suggested by Alexey. This provides a
consistent way of tracking auxiliary memory used by a socket, and keeps
the allocation and cleanup at the client level.The corresponding
kmalloc() calls in net/core/iovec.c have been removed.

I've also removed a useless test for a negative unsigned quantity, and
simplified the code used to call kill_fasync().

In net/core/sock.c I've simplified the implementation of sock_wspace() a
bit, as there's no need for two atomic reads. In sock_alloc_send_skb
the xchg getting the sk->err field has been replaced with sock_error(),
as the sign of the error must be inverted. I've also simplified the
logic of allocating with a fallback size.

Regards,
Bill
--------------1FAAEABCF404DAC74D92FD56
Content-Type: text/plain; charset=us-ascii; name="net_socket92-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="net_socket92-patch"

--- linux-2.1.92/net/socket.c.old Fri Apr 3 19:30:04 1998
+++ linux-2.1.92/net/socket.c Sun Apr 5 14:20:55 1998
@@ -547,20 +547,19 @@
return -1;
switch (how)
{
- case 0:
- kill_fasync(sock->fasync_list, SIGIO);
- break;
- case 1:
- if (!(sock->flags & SO_WAITDATA))
- kill_fasync(sock->fasync_list, SIGIO);
- break;
- case 2:
- if (sock->flags & SO_NOSPACE)
- {
- kill_fasync(sock->fasync_list, SIGIO);
- sock->flags &= ~SO_NOSPACE;
- }
+ case 1:
+ if (sock->flags & SO_WAITDATA)
break;
+ goto call_kill;
+ case 2:
+ if (!(sock->flags & SO_NOSPACE))
+ break;
+ sock->flags &= ~SO_NOSPACE;
+ /* fall through */
+ case 0:
+ call_kill:
+ kill_fasync(sock->fasync_list, SIGIO);
+ break;
}
return 0;
}
@@ -827,6 +826,7 @@
sys_close(err);
goto restart;
}
+ /* N.B. Should check for errors here */
move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
}

@@ -912,13 +912,13 @@
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
- int len;
- int err;
+ int len, err;

lock_kernel();
if ((sock = sockfd_lookup(fd, &err))!=NULL)
{
- if((err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1))==0)
+ err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);
+ if (!err)
err=move_addr_to_user(address,len, usockaddr, usockaddr_len);
sockfd_put(sock);
}
@@ -940,28 +940,22 @@

lock_kernel();
sock = sockfd_lookup(fd, &err);
- if (!sock)
- goto out;
- err = -EINVAL;
- if (len < 0)
- goto out_put;
-
- iov.iov_base=buff;
- iov.iov_len=len;
- msg.msg_name=NULL;
- msg.msg_namelen=0;
- msg.msg_iov=&iov;
- msg.msg_iovlen=1;
- msg.msg_control=NULL;
- msg.msg_controllen=0;
- if (sock->file->f_flags & O_NONBLOCK)
- flags |= MSG_DONTWAIT;
- msg.msg_flags = flags;
- err = sock_sendmsg(sock, &msg, len);
+ if (sock) {
+ iov.iov_base=buff;
+ iov.iov_len=len;
+ msg.msg_name=NULL;
+ msg.msg_namelen=0;
+ msg.msg_iov=&iov;
+ msg.msg_iovlen=1;
+ msg.msg_control=NULL;
+ msg.msg_controllen=0;
+ if (sock->file->f_flags & O_NONBLOCK)
+ flags |= MSG_DONTWAIT;
+ msg.msg_flags = flags;
+ err = sock_sendmsg(sock, &msg, len);

-out_put:
- sockfd_put(sock);
-out:
+ sockfd_put(sock);
+ }
unlock_kernel();
return err;
}
@@ -1140,11 +1134,11 @@
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
- struct iovec iov[UIO_FASTIOV];
+ struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */
unsigned char *ctl_buf = ctl;
struct msghdr msg_sys;
- int err, ctl_len, total_len;
+ int err, ctl_len, iov_size, total_len;

lock_kernel();

@@ -1152,20 +1146,29 @@
if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
goto out;

+ sock = sockfd_lookup(fd, &err);
+ if (!sock)
+ goto out;
+
/* do not move before msg_sys is valid */
err = -EINVAL;
if (msg_sys.msg_iovlen > UIO_MAXIOV)
- goto out;
+ goto out_put;
+
+ /* Check whether to allocate the iovec area*/
+ err = -ENOMEM;
+ iov_size = msg_sys.msg_iovlen * sizeof(struct iovec);
+ if (msg_sys.msg_iovlen > 1 /* UIO_FASTIOV */) {
+ iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
+ if (!iov)
+ goto out_put;
+ }

/* This will also move the address data into kernel space */
err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
if (err < 0)
- goto out;
- total_len=err;
-
- sock = sockfd_lookup(fd, &err);
- if (!sock)
goto out_freeiov;
+ total_len = err;

ctl_len = msg_sys.msg_controllen;
if (ctl_len)
@@ -1181,7 +1184,7 @@
err = -ENOBUFS;
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
if (ctl_buf == NULL)
- goto out_put;
+ goto out_freeiov;
}
err = -EFAULT;
if (copy_from_user(ctl_buf, msg_sys.msg_control, ctl_len))
@@ -1197,11 +1200,11 @@
out_freectl:
if (ctl_buf != ctl)
sock_kfree_s(sock->sk, ctl_buf, ctl_len);
+out_freeiov:
+ if (iov != iovstack)
+ sock_kfree_s(sock->sk, iov, iov_size);
out_put:
sockfd_put(sock);
-out_freeiov:
- if (msg_sys.msg_iov != iov)
- kfree(msg_sys.msg_iov);
out:
unlock_kernel();
return err;
@@ -1218,9 +1221,7 @@
struct iovec *iov=iovstack;
struct msghdr msg_sys;
unsigned long cmsg_ptr;
- int err;
- int total_len;
- int len = 0;
+ int err, iov_size, total_len, len;

/* kernel mode address */
char addr[MAX_SOCK_ADDR];
@@ -1234,10 +1235,23 @@
if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
goto out;

- err=-EINVAL;
- if (msg_sys.msg_iovlen > UIO_MAXIOV)
+ sock = sockfd_lookup(fd, &err);
+ if (!sock)
goto out;
+
+ err = -EINVAL;
+ if (msg_sys.msg_iovlen > UIO_MAXIOV)
+ goto out_put;

+ /* Check whether to allocate the iovec area*/
+ err = -ENOMEM;
+ iov_size = msg_sys.msg_iovlen * sizeof(struct iovec);
+ if (msg_sys.msg_iovlen > UIO_FASTIOV) {
+ iov = sock_kmalloc(sock->sk, iov_size, GFP_KERNEL);
+ if (!iov)
+ goto out_put;
+ }
+
/*
* Save the user-mode address (verify_iovec will change the
* kernel msghdr to use the kernel address space)
@@ -1245,41 +1259,43 @@

uaddr = msg_sys.msg_name;
uaddr_len = &msg->msg_namelen;
- err=verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
- if (err<0)
- goto out;
-
+ err = verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
+ if (err < 0)
+ goto out_freeiov;
total_len=err;

cmsg_ptr = (unsigned long)msg_sys.msg_control;
msg_sys.msg_flags = 0;

- if ((sock = sockfd_lookup(fd, &err))!=NULL)
- {
- if (sock->file->f_flags & O_NONBLOCK)
- flags |= MSG_DONTWAIT;
- err=sock_recvmsg(sock, &msg_sys, total_len, flags);
- if(err>=0)
- len=err;
- sockfd_put(sock);
- }
- if (msg_sys.msg_iov != iov)
- kfree(msg_sys.msg_iov);
+ if (sock->file->f_flags & O_NONBLOCK)
+ flags |= MSG_DONTWAIT;
+ err = sock_recvmsg(sock, &msg_sys, total_len, flags);
+ if (err < 0)
+ goto out_freeiov;
+ len = err;

- if (uaddr != NULL && err>=0)
+ if (uaddr != NULL) {
err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len);
- if (err < 0)
- goto out;
+ if (err < 0)
+ goto out_freeiov;
+ }
err = __put_user(msg_sys.msg_flags, &msg->msg_flags);
if (err)
- goto out;
+ goto out_freeiov;
err = __put_user((unsigned long)msg_sys.msg_control-cmsg_ptr,
&msg->msg_controllen);
+ if (err)
+ goto out_freeiov;
+ err = len;
+
+out_freeiov:
+ if (iov != iovstack)
+ sock_kfree_s(sock->sk, iov, iov_size);
+out_put:
+ sockfd_put(sock);
out:
unlock_kernel();
- if(err<0)
- return err;
- return len;
+ return err;
}


@@ -1462,10 +1478,13 @@

sk_init();

+#ifdef SLAB_SKB
/*
* Initialize skbuff SLAB cache
*/
skb_init();
+#endif
+

/*
* Wan router layer.
--- linux-2.1.92/net/core/sock.c.old Fri Apr 3 19:27:11 1998
+++ linux-2.1.92/net/core/sock.c Sun Apr 5 17:24:14 1998
@@ -589,38 +589,37 @@
*/
unsigned long sock_rspace(struct sock *sk)
{
- int amt;
+ int amt = 0;

if (sk != NULL) {
- /* This used to have some bizzare complications that
+ /* This used to have some bizarre complications that
* to attempt to reserve some amount of space. This doesn't
* make sense, since the number returned here does not
* actually reflect allocated space, but rather the amount
* of space we committed to. We gamble that we won't
* run out of memory, and returning a smaller number does
- * not change the gamble. If we loose the gamble tcp still
+ * not change the gamble. If we lose the gamble tcp still
* works, it may just slow down for retransmissions.
*/
amt = sk->rcvbuf - atomic_read(&sk->rmem_alloc);
if (amt < 0)
- return(0);
- return(amt);
+ amt = 0;
}
- return(0);
+ return amt;
}


/* FIXME: this is also insane. See above comment */
unsigned long sock_wspace(struct sock *sk)
{
- if (sk != NULL) {
- if (sk->shutdown & SEND_SHUTDOWN)
- return(0);
- if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf)
- return(0);
- return sk->sndbuf - atomic_read(&sk->wmem_alloc);
+ int amt = 0;
+
+ if (sk != NULL && !(sk->shutdown & SEND_SHUTDOWN)) {
+ amt = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+ if (amt < 0)
+ amt = 0;
}
- return(0);
+ return amt;
}

/* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
@@ -653,13 +652,17 @@
* Generic send/receive buffer handlers
*/

-struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode)
+struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
+ unsigned long fallback, int noblock, int *errcode)
{
int err;
struct sk_buff *skb;

- do {
- if ((err = xchg(&sk->err,0)) != 0)
+ while (1) {
+ unsigned long try_size = size;
+
+ err = sock_error(sk);
+ if (err != 0)
goto failure;

/*
@@ -676,33 +679,32 @@
if (sk->shutdown&SEND_SHUTDOWN)
goto failure;

- if (!fallback)
- skb = sock_wmalloc(sk, size, 0, sk->allocation);
- else {
- /* The buffer get won't block, or use the atomic queue. It does
- produce annoying no free page messages still.... */
+ if (fallback) {
+ /* The buffer get won't block, or use the atomic queue.
+ * It does produce annoying no free page messages still.
+ */
skb = sock_wmalloc(sk, size, 0, GFP_BUFFER);
- if (!skb)
- skb=sock_wmalloc(sk, fallback, 0, sk->allocation);
+ if (skb)
+ break;
+ try_size = fallback;
}
+ skb = sock_wmalloc(sk, try_size, 0, sk->allocation);
+ if (skb)
+ break;

/*
* This means we have too many buffers for this socket already.
*/

- /* The following code is stolen "as is" from tcp.c */
-
- if (skb==NULL) {
- sk->socket->flags |= SO_NOSPACE;
- err = -EAGAIN;
- if (noblock)
- goto failure;
- err = -ERESTARTSYS;
- if (signal_pending(current))
- goto failure;
- sock_wait_for_wmem(sk);
- }
- } while (skb==NULL);
+ sk->socket->flags |= SO_NOSPACE;
+ err = -EAGAIN;
+ if (noblock)
+ goto failure;
+ err = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto failure;
+ sock_wait_for_wmem(sk);
+ }

return skb;

--- linux-2.1.92/net/core/iovec.c.old Fri Apr 3 19:27:11 1998
+++ linux-2.1.92/net/core/iovec.c Sun Apr 5 14:26:02 1998
@@ -29,7 +29,8 @@
#include <net/checksum.h>

/*
- * Verify iovec
+ * Verify iovec. The caller must ensure that the iovec is big enough
+ * to hold the message iovec.
*
* Save time not doing verify_area. copy_*_user will make this work
* in any case.
@@ -37,8 +38,7 @@

int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
{
- int size = m->msg_iovlen * sizeof(struct iovec);
- int err, ct;
+ int size, err, ct;

if(m->msg_namelen)
{
@@ -53,28 +53,16 @@
} else
m->msg_name = NULL;

- if (m->msg_iovlen > UIO_FASTIOV)
- {
- err = -ENOMEM;
- iov = kmalloc(size, GFP_KERNEL);
- if (!iov)
- goto out;
- }
-
+ err = -EFAULT;
+ size = m->msg_iovlen * sizeof(struct iovec);
if (copy_from_user(iov, m->msg_iov, size))
- goto out_free;
+ goto out;
m->msg_iov=iov;

for (err = 0, ct = 0; ct < m->msg_iovlen; ct++)
err += iov[ct].iov_len;
out:
return err;
-
-out_free:
- err = -EFAULT;
- if (m->msg_iovlen > UIO_FASTIOV)
- kfree(iov);
- goto out;
}

/*

--------------1FAAEABCF404DAC74D92FD56--

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu