Commit 75541efd authored by David S. Miller's avatar David S. Miller

Merge http://kernel-acme.bkbits.net:8080/net-cleanups-2.5-datagram

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents 410e9a9b 64127bdf
/* /*
* SUCS NET3: * SUCS NET3:
* *
* Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top * Generic datagram handling routines. These are generic for all
* of these would make sense. Not tonight however 8-). * protocols. Possibly a generic IP version on top of these would
* This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical poll code and mostly * make sense. Not tonight however 8-).
* identical recvmsg() code. So we share it here. The poll was shared before but buried in udp.c so I moved it. * 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@redhat.com>. (datagram_poll() from old udp.c code) * Authors: Alan Cox <alan@redhat.com>. (datagram_poll() from old
* udp.c code)
* *
* Fixes: * Fixes:
* Alan Cox : NULL return from skb_peek_copy() understood * Alan Cox : NULL return from skb_peek_copy()
* Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff. * understood
* Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but * Alan Cox : Rewrote skb_read_datagram to avoid the
* AX.25 now works right, and SPX is feasible. * skb_peek_copy stuff.
* Alan Cox : Fixed write poll of non IP protocol crash. * Alan Cox : Added support for SOCK_SEQPACKET.
* IPX can no longer use the SO_TYPE hack
* but AX.25 now works right, and SPX is
* feasible.
* Alan Cox : Fixed write poll of non IP protocol
* crash.
* Florian La Roche: Changed for my new skbuff handling. * Florian La Roche: Changed for my new skbuff handling.
* Darryl Miles : Fixed non-blocking SOCK_SEQPACKET. * Darryl Miles : Fixed non-blocking SOCK_SEQPACKET.
* Linus Torvalds : BSD semantic fixes. * Linus Torvalds : BSD semantic fixes.
...@@ -48,18 +57,15 @@ ...@@ -48,18 +57,15 @@
/* /*
* Is a socket 'connection oriented' ? * Is a socket 'connection oriented' ?
*/ */
static inline int connection_based(struct sock *sk) static inline int connection_based(struct sock *sk)
{ {
return (sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM); return sk->type == SOCK_SEQPACKET || sk->type == SOCK_STREAM;
} }
/* /*
* Wait for a packet.. * Wait for a packet..
*/ */
static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
static int wait_for_packet(struct sock * sk, int *err, long *timeo_p)
{ {
int error; int error;
...@@ -74,51 +80,57 @@ static int wait_for_packet(struct sock * sk, int *err, long *timeo_p) ...@@ -74,51 +80,57 @@ static int wait_for_packet(struct sock * sk, int *err, long *timeo_p)
goto out_err; goto out_err;
if (!skb_queue_empty(&sk->receive_queue)) if (!skb_queue_empty(&sk->receive_queue))
goto ready; goto out;
/* Socket shut down? */ /* Socket shut down? */
if (sk->shutdown & RCV_SHUTDOWN) if (sk->shutdown & RCV_SHUTDOWN)
goto out_noerr; goto out_noerr;
/* Sequenced packets can come disconnected. If so we report the problem */ /* Sequenced packets can come disconnected.
* If so we report the problem
*/
error = -ENOTCONN; error = -ENOTCONN;
if(connection_based(sk) && !(sk->state==TCP_ESTABLISHED || sk->state==TCP_LISTEN)) if (connection_based(sk) && !(sk->state == TCP_ESTABLISHED ||
sk->state == TCP_LISTEN))
goto out_err; goto out_err;
/* handle signals */ /* handle signals */
if (signal_pending(current)) if (signal_pending(current))
goto interrupted; goto interrupted;
error = 0;
*timeo_p = schedule_timeout(*timeo_p); *timeo_p = schedule_timeout(*timeo_p);
out:
ready:
current->state = TASK_RUNNING; current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait); remove_wait_queue(sk->sleep, &wait);
return 0; return error;
interrupted: interrupted:
error = sock_intr_errno(*timeo_p); error = sock_intr_errno(*timeo_p);
out_err: out_err:
*err = error; *err = error;
out: goto out;
current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait);
return error;
out_noerr: out_noerr:
*err = 0; *err = 0;
error = 1; error = 1;
goto out; goto out;
} }
/* /**
* Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible * skb_recv_datagram - Receive a datagram skbuff
* races. This replaces identical code in packet,raw and udp, as well as the IPX * @sk - socket
* AX.25 and Appletalk. It also finally fixes the long standing peek and read * @flags - MSG_ flags
* race for datagram sockets. If you alter this routine remember it must be * @noblock - blocking operation?
* re-entrant. * @err - error code returned
*
* Get a datagram skbuff, understands the peeking, nonblocking wakeups
* and possible races. This replaces identical code in packet, raw and
* udp, as well as the IPX AX.25 and Appletalk. It also finally fixes
* the long standing peek and read race for datagram sockets. If you
* alter this routine remember it must be re-entrant.
* *
* This function will lock the socket if a skb is returned, so the caller * This function will lock the socket if a skb is returned, so the caller
* needs to unlock the socket in that case (usually by calling skb_free_datagram) * needs to unlock the socket in that case (usually by calling
* skb_free_datagram)
* *
* * It does not lock socket since today. This function is * * It does not lock socket since today. This function is
* * free of race conditions. This measure should/can improve * * free of race conditions. This measure should/can improve
...@@ -132,36 +144,35 @@ static int wait_for_packet(struct sock * sk, int *err, long *timeo_p) ...@@ -132,36 +144,35 @@ static int wait_for_packet(struct sock * sk, int *err, long *timeo_p)
* quite explicitly by POSIX 1003.1g, don't change them without having * quite explicitly by POSIX 1003.1g, don't change them without having
* the standard around please. * the standard around please.
*/ */
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) int noblock, int *err)
{ {
int error;
struct sk_buff *skb; struct sk_buff *skb;
long timeo; long timeo;
/* Caller is allowed not to check sk->err before skb_recv_datagram() */ /* Caller is allowed not to check sk->err before skb_recv_datagram() */
error = sock_error(sk); int error = sock_error(sk);
if (error) if (error)
goto no_packet; goto no_packet;
timeo = sock_rcvtimeo(sk, noblock); timeo = sock_rcvtimeo(sk, noblock);
do { do {
/* Again only user level code calls this function, so nothing interrupt level /* Again only user level code calls this function, so nothing
will suddenly eat the receive_queue. * interrupt level will suddenly eat the receive_queue.
*
Look at current nfs client by the way... * Look at current nfs client by the way...
However, this function was corrent in any case. 8) * However, this function was corrent in any case. 8)
*/ */
if (flags & MSG_PEEK) if (flags & MSG_PEEK) {
{
unsigned long cpu_flags; unsigned long cpu_flags;
spin_lock_irqsave(&sk->receive_queue.lock, cpu_flags); spin_lock_irqsave(&sk->receive_queue.lock, cpu_flags);
skb = skb_peek(&sk->receive_queue); skb = skb_peek(&sk->receive_queue);
if(skb!=NULL) if (skb)
atomic_inc(&skb->users); atomic_inc(&skb->users);
spin_unlock_irqrestore(&sk->receive_queue.lock, cpu_flags); spin_unlock_irqrestore(&sk->receive_queue.lock,
cpu_flags);
} else } else
skb = skb_dequeue(&sk->receive_queue); skb = skb_dequeue(&sk->receive_queue);
...@@ -173,7 +184,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, ...@@ -173,7 +184,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
if (!timeo) if (!timeo)
goto no_packet; goto no_packet;
} while (wait_for_packet(sk, err, &timeo) == 0); } while (!wait_for_packet(sk, err, &timeo));
return NULL; return NULL;
...@@ -182,7 +193,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, ...@@ -182,7 +193,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
return NULL; return NULL;
} }
void skb_free_datagram(struct sock * sk, struct sk_buff *skb) void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
{ {
kfree_skb(skb); kfree_skb(skb);
} }
...@@ -190,26 +201,33 @@ void skb_free_datagram(struct sock * sk, struct sk_buff *skb) ...@@ -190,26 +201,33 @@ void skb_free_datagram(struct sock * sk, struct sk_buff *skb)
/* /*
* Copy a datagram to a linear buffer. * Copy a datagram to a linear buffer.
*/ */
int skb_copy_datagram(const struct sk_buff *skb, int offset, char *to, int size) int skb_copy_datagram(const struct sk_buff *skb, int offset, char *to, int size)
{ {
struct iovec iov = { to, size }; struct iovec iov = {
iov_base: to,
iov_len: size,
};
return skb_copy_datagram_iovec(skb, offset, &iov, size); return skb_copy_datagram_iovec(skb, offset, &iov, size);
} }
/* /**
* Copy a datagram to an iovec. * skb_copy_datagram_iovec - Copy a datagram to an iovec.
* @skb - buffer to copy
* @offset - offset in the buffer to start copying from
* @iovec - io vector to copy to
* @len - amount of data to copy from buffer to iovec
*
* Note: the iovec is modified during the copy. * Note: the iovec is modified during the copy.
*/ */
int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec *to, int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
int len) struct iovec *to, int len)
{ {
int i, copy;
int start = skb->len - skb->data_len; int start = skb->len - skb->data_len;
int i, copy = start - offset;
/* Copy header. */ /* Copy header. */
if ((copy = start-offset) > 0) { if (copy > 0) {
if (copy > len) if (copy > len)
copy = len; copy = len;
if (memcpy_toiovec(to, skb->data + offset, copy)) if (memcpy_toiovec(to, skb->data + offset, copy))
...@@ -220,13 +238,13 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec ...@@ -220,13 +238,13 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec
} }
/* Copy paged appendix. Hmm... why does this look so complicated? */ /* Copy paged appendix. Hmm... why does this look so complicated? */
for (i=0; i<skb_shinfo(skb)->nr_frags; i++) { for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
int end; int end;
BUG_TRAP(start <= offset+len); BUG_TRAP(start <= offset + len);
end = start + skb_shinfo(skb)->frags[i].size; end = start + skb_shinfo(skb)->frags[i].size;
if ((copy = end-offset) > 0) { if ((copy = end - offset) > 0) {
int err; int err;
u8 *vaddr; u8 *vaddr;
skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
...@@ -236,7 +254,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec ...@@ -236,7 +254,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec
copy = len; copy = len;
vaddr = kmap(page); vaddr = kmap(page);
err = memcpy_toiovec(to, vaddr + frag->page_offset + err = memcpy_toiovec(to, vaddr + frag->page_offset +
offset-start, copy); offset - start, copy);
kunmap(page); kunmap(page);
if (err) if (err)
goto fault; goto fault;
...@@ -248,18 +266,20 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec ...@@ -248,18 +266,20 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec
} }
if (skb_shinfo(skb)->frag_list) { if (skb_shinfo(skb)->frag_list) {
struct sk_buff *list; struct sk_buff *list = skb_shinfo(skb)->frag_list;
for (list = skb_shinfo(skb)->frag_list; list; list=list->next) { for (; list; list = list->next) {
int end; int end;
BUG_TRAP(start <= offset+len); BUG_TRAP(start <= offset + len);
end = start + list->len; end = start + list->len;
if ((copy = end-offset) > 0) { if ((copy = end - offset) > 0) {
if (copy > len) if (copy > len)
copy = len; copy = len;
if (skb_copy_datagram_iovec(list, offset-start, to, copy)) if (skb_copy_datagram_iovec(list,
offset - start,
to, copy))
goto fault; goto fault;
if ((len -= copy) == 0) if ((len -= copy) == 0)
return 0; return 0;
...@@ -268,25 +288,27 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec ...@@ -268,25 +288,27 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec
start = end; start = end;
} }
} }
if (len == 0) if (!len)
return 0; return 0;
fault: fault:
return -EFAULT; return -EFAULT;
} }
int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int *csump) int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
u8 *to, int len, unsigned int *csump)
{ {
int i, copy;
int start = skb->len - skb->data_len; int start = skb->len - skb->data_len;
int pos = 0; int pos = 0;
int i, copy = start - offset;
/* Copy header. */ /* Copy header. */
if ((copy = start-offset) > 0) { if (copy > 0) {
int err = 0; int err = 0;
if (copy > len) if (copy > len)
copy = len; copy = len;
*csump = csum_and_copy_to_user(skb->data+offset, to, copy, *csump, &err); *csump = csum_and_copy_to_user(skb->data + offset, to, copy,
*csump, &err);
if (err) if (err)
goto fault; goto fault;
if ((len -= copy) == 0) if ((len -= copy) == 0)
...@@ -296,13 +318,13 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in ...@@ -296,13 +318,13 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in
pos = copy; pos = copy;
} }
for (i=0; i<skb_shinfo(skb)->nr_frags; i++) { for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
int end; int end;
BUG_TRAP(start <= offset+len); BUG_TRAP(start <= offset + len);
end = start + skb_shinfo(skb)->frags[i].size; end = start + skb_shinfo(skb)->frags[i].size;
if ((copy = end-offset) > 0) { if ((copy = end - offset) > 0) {
unsigned int csum2; unsigned int csum2;
int err = 0; int err = 0;
u8 *vaddr; u8 *vaddr;
...@@ -312,8 +334,10 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in ...@@ -312,8 +334,10 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in
if (copy > len) if (copy > len)
copy = len; copy = len;
vaddr = kmap(page); vaddr = kmap(page);
csum2 = csum_and_copy_to_user(vaddr + frag->page_offset + csum2 = csum_and_copy_to_user(vaddr +
offset-start, to, copy, 0, &err); frag->page_offset +
offset - start,
to, copy, 0, &err);
kunmap(page); kunmap(page);
if (err) if (err)
goto fault; goto fault;
...@@ -328,19 +352,22 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in ...@@ -328,19 +352,22 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in
} }
if (skb_shinfo(skb)->frag_list) { if (skb_shinfo(skb)->frag_list) {
struct sk_buff *list; struct sk_buff *list = skb_shinfo(skb)->frag_list;
for (list = skb_shinfo(skb)->frag_list; list; list=list->next) { for (; list; list=list->next) {
int end; int end;
BUG_TRAP(start <= offset+len); BUG_TRAP(start <= offset + len);
end = start + list->len; end = start + list->len;
if ((copy = end-offset) > 0) { if ((copy = end - offset) > 0) {
unsigned int csum2 = 0; unsigned int csum2 = 0;
if (copy > len) if (copy > len)
copy = len; copy = len;
if (skb_copy_and_csum_datagram(list, offset-start, to, copy, &csum2)) if (skb_copy_and_csum_datagram(list,
offset - start,
to, copy,
&csum2))
goto fault; goto fault;
*csump = csum_block_add(*csump, csum2, pos); *csump = csum_block_add(*csump, csum2, pos);
if ((len -= copy) == 0) if ((len -= copy) == 0)
...@@ -352,39 +379,48 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in ...@@ -352,39 +379,48 @@ int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, in
start = end; start = end;
} }
} }
if (len == 0) if (!len)
return 0; return 0;
fault: fault:
return -EFAULT; return -EFAULT;
} }
/* Copy and checkum skb to user iovec. Caller _must_ check that /**
skb will fit to this iovec. * skb_copy_and_csum_datagram_iovec - Copy and checkum skb to user iovec.
* @skb - skbuff
Returns: 0 - success. * @hlen - hardware length
-EINVAL - checksum failure. * @iovec - io vector
-EFAULT - fault during copy. Beware, in this case iovec can be *
modified! * Caller _must_ check that skb will fit to this iovec.
*
* Returns: 0 - success.
* -EINVAL - checksum failure.
* -EFAULT - fault during copy. Beware, in this case iovec
* can be modified!
*/ */
int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb,
int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct iovec *iov) int hlen, struct iovec *iov)
{ {
unsigned int csum; unsigned int csum;
int chunk = skb->len - hlen; int chunk = skb->len - hlen;
/* Skip filled elements. Pretty silly, look at memcpy_toiovec, though 8) */ /* Skip filled elements.
while (iov->iov_len == 0) * Pretty silly, look at memcpy_toiovec, though 8)
*/
while (!iov->iov_len)
iov++; iov++;
if (iov->iov_len < chunk) { if (iov->iov_len < chunk) {
if ((unsigned short)csum_fold(skb_checksum(skb, 0, chunk+hlen, skb->csum))) if ((unsigned short)csum_fold(skb_checksum(skb, 0, chunk + hlen,
skb->csum)))
goto csum_error; goto csum_error;
if (skb_copy_datagram_iovec(skb, hlen, iov, chunk)) if (skb_copy_datagram_iovec(skb, hlen, iov, chunk))
goto fault; goto fault;
} else { } else {
csum = csum_partial(skb->data, hlen, skb->csum); csum = csum_partial(skb->data, hlen, skb->csum);
if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base, chunk, &csum)) if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base,
chunk, &csum))
goto fault; goto fault;
if ((unsigned short)csum_fold(csum)) if ((unsigned short)csum_fold(csum))
goto csum_error; goto csum_error;
...@@ -392,17 +428,18 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct ...@@ -392,17 +428,18 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct
iov->iov_base += chunk; iov->iov_base += chunk;
} }
return 0; return 0;
csum_error: csum_error:
return -EINVAL; return -EINVAL;
fault: fault:
return -EFAULT; return -EFAULT;
} }
/**
* datagram_poll - generic datagram poll
/* * @file - file struct
* @sock - socket
* @wait - poll table
*
* Datagram poll: Again totally generic. This also handles * Datagram poll: Again totally generic. This also handles
* sequenced packet sockets providing the socket receive queue * sequenced packet sockets providing the socket receive queue
* is only ever holding data ready to receive. * is only ever holding data ready to receive.
...@@ -411,8 +448,8 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct ...@@ -411,8 +448,8 @@ int skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct
* and you use a different write policy from sock_writeable() * and you use a different write policy from sock_writeable()
* then please supply your own write_space callback. * then please supply your own write_space callback.
*/ */
unsigned int datagram_poll(struct file *file, struct socket *sock,
unsigned int datagram_poll(struct file * file, struct socket *sock, poll_table *wait) poll_table *wait)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
unsigned int mask; unsigned int mask;
...@@ -427,12 +464,13 @@ unsigned int datagram_poll(struct file * file, struct socket *sock, poll_table * ...@@ -427,12 +464,13 @@ unsigned int datagram_poll(struct file * file, struct socket *sock, poll_table *
mask |= POLLHUP; mask |= POLLHUP;
/* readable? */ /* readable? */
if (!skb_queue_empty(&sk->receive_queue) || (sk->shutdown&RCV_SHUTDOWN)) if (!skb_queue_empty(&sk->receive_queue) ||
(sk->shutdown & RCV_SHUTDOWN))
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
/* Connection-based need to check for termination and startup */ /* Connection-based need to check for termination and startup */
if (connection_based(sk)) { if (connection_based(sk)) {
if (sk->state==TCP_CLOSE) if (sk->state == TCP_CLOSE)
mask |= POLLHUP; mask |= POLLHUP;
/* connection hasn't started yet? */ /* connection hasn't started yet? */
if (sk->state == TCP_SYN_SENT) if (sk->state == TCP_SYN_SENT)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment