[LLC]: fix llc_ui_recvmsg, making it behave like tcp_recvmsg
In fact it is an exact copy of the parts that makes sense to LLC :-) Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
This commit is contained in:
parent
d389424e00
commit
8420e1b541
5 changed files with 154 additions and 36 deletions
|
@ -38,6 +38,7 @@ struct llc_sock {
|
||||||
struct llc_addr laddr; /* lsap/mac pair */
|
struct llc_addr laddr; /* lsap/mac pair */
|
||||||
struct llc_addr daddr; /* dsap/mac pair */
|
struct llc_addr daddr; /* dsap/mac pair */
|
||||||
struct net_device *dev; /* device to send to remote */
|
struct net_device *dev; /* device to send to remote */
|
||||||
|
u32 copied_seq; /* head of yet unread data */
|
||||||
u8 retry_count; /* number of retries */
|
u8 retry_count; /* number of retries */
|
||||||
u8 ack_must_be_send;
|
u8 ack_must_be_send;
|
||||||
u8 first_pdu_Ns;
|
u8 first_pdu_Ns;
|
||||||
|
|
180
net/llc/af_llc.c
180
net/llc/af_llc.c
|
@ -648,53 +648,167 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags)
|
||||||
* llc_ui_recvmsg - copy received data to the socket user.
|
* llc_ui_recvmsg - copy received data to the socket user.
|
||||||
* @sock: Socket to copy data from.
|
* @sock: Socket to copy data from.
|
||||||
* @msg: Various user space related information.
|
* @msg: Various user space related information.
|
||||||
* @size: Size of user buffer.
|
* @len: Size of user buffer.
|
||||||
* @flags: User specified flags.
|
* @flags: User specified flags.
|
||||||
*
|
*
|
||||||
* Copy received data to the socket user.
|
* Copy received data to the socket user.
|
||||||
* Returns non-negative upon success, negative otherwise.
|
* Returns non-negative upon success, negative otherwise.
|
||||||
*/
|
*/
|
||||||
static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
|
static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||||
struct msghdr *msg, size_t size, int flags)
|
struct msghdr *msg, size_t len, int flags)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
|
||||||
struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;
|
struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;
|
||||||
struct sk_buff *skb;
|
const int nonblock = flags & MSG_DONTWAIT;
|
||||||
|
struct sk_buff *skb = NULL;
|
||||||
|
struct sock *sk = sock->sk;
|
||||||
|
struct llc_sock *llc = llc_sk(sk);
|
||||||
size_t copied = 0;
|
size_t copied = 0;
|
||||||
int rc = -ENOMEM;
|
u32 peek_seq = 0;
|
||||||
int noblock = flags & MSG_DONTWAIT;
|
u32 *seq;
|
||||||
|
unsigned long used;
|
||||||
|
int target; /* Read at least this many bytes */
|
||||||
|
long timeo;
|
||||||
|
|
||||||
dprintk("%s: receiving in %02X from %02X\n", __FUNCTION__,
|
|
||||||
llc_sk(sk)->laddr.lsap, llc_sk(sk)->daddr.lsap);
|
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
if (skb_queue_empty(&sk->sk_receive_queue)) {
|
copied = -ENOTCONN;
|
||||||
rc = llc_wait_data(sk, sock_rcvtimeo(sk, noblock));
|
if (sk->sk_state == TCP_LISTEN)
|
||||||
if (rc)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
skb = skb_dequeue(&sk->sk_receive_queue);
|
|
||||||
if (!skb) /* shutdown */
|
|
||||||
goto out;
|
goto out;
|
||||||
copied = skb->len;
|
|
||||||
if (copied > size)
|
timeo = sock_rcvtimeo(sk, nonblock);
|
||||||
copied = size;
|
|
||||||
rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
|
seq = &llc->copied_seq;
|
||||||
if (rc)
|
if (flags & MSG_PEEK) {
|
||||||
goto dgram_free;
|
peek_seq = llc->copied_seq;
|
||||||
if (skb->len > copied) {
|
seq = &peek_seq;
|
||||||
skb_pull(skb, copied);
|
}
|
||||||
skb_queue_head(&sk->sk_receive_queue, skb);
|
|
||||||
}
|
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
|
||||||
if (uaddr)
|
copied = 0;
|
||||||
memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
|
|
||||||
msg->msg_namelen = sizeof(*uaddr);
|
do {
|
||||||
if (!skb->next) {
|
u32 offset;
|
||||||
dgram_free:
|
|
||||||
kfree_skb(skb);
|
/*
|
||||||
}
|
* We need to check signals first, to get correct SIGURG
|
||||||
|
* handling. FIXME: Need to check this doesn't impact 1003.1g
|
||||||
|
* and move it down to the bottom of the loop
|
||||||
|
*/
|
||||||
|
if (signal_pending(current)) {
|
||||||
|
if (copied)
|
||||||
|
break;
|
||||||
|
copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Next get a buffer. */
|
||||||
|
|
||||||
|
skb = skb_peek(&sk->sk_receive_queue);
|
||||||
|
if (skb) {
|
||||||
|
offset = *seq;
|
||||||
|
goto found_ok_skb;
|
||||||
|
}
|
||||||
|
/* Well, if we have backlog, try to process it now yet. */
|
||||||
|
|
||||||
|
if (copied >= target && !sk->sk_backlog.tail)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (copied) {
|
||||||
|
if (sk->sk_err ||
|
||||||
|
sk->sk_state == TCP_CLOSE ||
|
||||||
|
(sk->sk_shutdown & RCV_SHUTDOWN) ||
|
||||||
|
!timeo ||
|
||||||
|
(flags & MSG_PEEK))
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (sock_flag(sk, SOCK_DONE))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (sk->sk_err) {
|
||||||
|
copied = sock_error(sk);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (sk->sk_state == TCP_CLOSE) {
|
||||||
|
if (!sock_flag(sk, SOCK_DONE)) {
|
||||||
|
/*
|
||||||
|
* This occurs when user tries to read
|
||||||
|
* from never connected socket.
|
||||||
|
*/
|
||||||
|
copied = -ENOTCONN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!timeo) {
|
||||||
|
copied = -EAGAIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copied >= target) { /* Do not sleep, just process backlog. */
|
||||||
|
release_sock(sk);
|
||||||
|
lock_sock(sk);
|
||||||
|
} else
|
||||||
|
sk_wait_data(sk, &timeo);
|
||||||
|
|
||||||
|
if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) {
|
||||||
|
if (net_ratelimit())
|
||||||
|
printk(KERN_DEBUG "LLC(%s:%d): Application "
|
||||||
|
"bug, race in MSG_PEEK.\n",
|
||||||
|
current->comm, current->pid);
|
||||||
|
peek_seq = llc->copied_seq;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
found_ok_skb:
|
||||||
|
/* Ok so how much can we use? */
|
||||||
|
used = skb->len - offset;
|
||||||
|
if (len < used)
|
||||||
|
used = len;
|
||||||
|
|
||||||
|
if (!(flags & MSG_TRUNC)) {
|
||||||
|
int rc = skb_copy_datagram_iovec(skb, offset,
|
||||||
|
msg->msg_iov, used);
|
||||||
|
if (rc) {
|
||||||
|
/* Exception. Bailout! */
|
||||||
|
if (!copied)
|
||||||
|
copied = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*seq += used;
|
||||||
|
copied += used;
|
||||||
|
len -= used;
|
||||||
|
|
||||||
|
if (used + offset < skb->len)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!(flags & MSG_PEEK)) {
|
||||||
|
sk_eat_skb(sk, skb);
|
||||||
|
*seq = 0;
|
||||||
|
}
|
||||||
|
} while (len > 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* According to UNIX98, msg_name/msg_namelen are ignored
|
||||||
|
* on connected socket. -ANK
|
||||||
|
* But... af_llc still doesn't have separate sets of methods for
|
||||||
|
* SOCK_DGRAM and SOCK_STREAM :-( So we have to do this test, will
|
||||||
|
* eventually fix this tho :-) -acme
|
||||||
|
*/
|
||||||
|
if (sk->sk_type == SOCK_DGRAM)
|
||||||
|
goto copy_uaddr;
|
||||||
out:
|
out:
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
return rc ? : copied;
|
return copied;
|
||||||
|
copy_uaddr:
|
||||||
|
if (uaddr != NULL && skb != NULL) {
|
||||||
|
memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
|
||||||
|
msg->msg_namelen = sizeof(*uaddr);
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -120,8 +120,8 @@ int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
|
||||||
sk->sk_socket->state = SS_UNCONNECTED;
|
sk->sk_socket->state = SS_UNCONNECTED;
|
||||||
sk->sk_state = TCP_CLOSE;
|
sk->sk_state = TCP_CLOSE;
|
||||||
if (!sock_flag(sk, SOCK_DEAD)) {
|
if (!sock_flag(sk, SOCK_DEAD)) {
|
||||||
sk->sk_state_change(sk);
|
|
||||||
sock_set_flag(sk, SOCK_DEAD);
|
sock_set_flag(sk, SOCK_DEAD);
|
||||||
|
sk->sk_state_change(sk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
|
@ -134,7 +134,7 @@ static int llc_seq_socket_show(struct seq_file *seq, void *v)
|
||||||
llc_ui_format_mac(seq, llc->daddr.mac);
|
llc_ui_format_mac(seq, llc->daddr.mac);
|
||||||
seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap,
|
seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap,
|
||||||
atomic_read(&sk->sk_wmem_alloc),
|
atomic_read(&sk->sk_wmem_alloc),
|
||||||
atomic_read(&sk->sk_rmem_alloc),
|
atomic_read(&sk->sk_rmem_alloc) - llc->copied_seq,
|
||||||
sk->sk_state,
|
sk->sk_state,
|
||||||
sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1,
|
sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1,
|
||||||
llc->link);
|
llc->link);
|
||||||
|
|
|
@ -49,9 +49,12 @@ struct sk_buff *llc_alloc_frame(struct sock *sk, struct net_device *dev)
|
||||||
|
|
||||||
void llc_save_primitive(struct sock *sk, struct sk_buff* skb, u8 prim)
|
void llc_save_primitive(struct sock *sk, struct sk_buff* skb, u8 prim)
|
||||||
{
|
{
|
||||||
struct sockaddr_llc *addr = llc_ui_skb_cb(skb);
|
struct sockaddr_llc *addr;
|
||||||
|
|
||||||
|
if (skb->sk->sk_type == SOCK_STREAM) /* See UNIX98 */
|
||||||
|
return;
|
||||||
/* save primitive for use by the user. */
|
/* save primitive for use by the user. */
|
||||||
|
addr = llc_ui_skb_cb(skb);
|
||||||
addr->sllc_family = sk->sk_family;
|
addr->sllc_family = sk->sk_family;
|
||||||
addr->sllc_arphrd = skb->dev->type;
|
addr->sllc_arphrd = skb->dev->type;
|
||||||
addr->sllc_test = prim == LLC_TEST_PRIM;
|
addr->sllc_test = prim == LLC_TEST_PRIM;
|
||||||
|
|
Loading…
Reference in a new issue