static int recv_msg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct sock *sk = sock->sk; struct tipc_port *tport = tipc_sk_port(sk); struct sk_buff *buf; struct tipc_msg *msg; long timeout; unsigned int sz; u32 err; int res; /* Catch invalid receive requests */ if (unlikely(!buf_len)) return -EINVAL; lock_sock(sk); if (unlikely(sock->state == SS_UNCONNECTED)) { res = -ENOTCONN; goto exit; } /* will be updated in set_orig_addr() if needed */ m->msg_namelen = 0; timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); restart: /* Look for a message in receive queue; wait if necessary */ while (skb_queue_empty(&sk->sk_receive_queue)) { if (sock->state == SS_DISCONNECTING) { res = -ENOTCONN; goto exit; } if (timeout <= 0L) { res = timeout ? timeout : -EWOULDBLOCK; goto exit; } release_sock(sk); timeout = wait_event_interruptible_timeout(*sk_sleep(sk), tipc_rx_ready(sock), timeout); lock_sock(sk); } /* Look at first message in receive queue */ buf = skb_peek(&sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Complete connection setup for an implied connect */ if (unlikely(sock->state == SS_CONNECTING)) { res = auto_connect(sock, msg); if (res) goto exit; } /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_rx_queue(sk); goto restart; } /* Capture sender's address (optional) */ set_orig_addr(m, msg); /* Capture ancillary data (optional) */ res = anc_data_recv(m, msg, tport); if (res) goto exit; /* Capture message data (if valid) & compute return value (always) */ if (!err) { if (unlikely(buf_len < sz)) { sz = buf_len; m->msg_flags |= MSG_TRUNC; } res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg), m->msg_iov, sz); if (res) goto exit; res = sz; } else { if ((sock->state == SS_READY) || ((err == TIPC_CONN_SHUTDOWN) || m->msg_control)) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if ((sock->state != SS_READY) && (++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tport->ref, tport->conn_unacked); advance_rx_queue(sk); } exit: release_sock(sk); return res; }
static int recv_stream(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct sock *sk = sock->sk; struct tipc_port *tport = tipc_sk_port(sk); struct sk_buff *buf; struct tipc_msg *msg; long timeout; unsigned int sz; int sz_to_copy, target, needed; int sz_copied = 0; u32 err; int res = 0; /* Catch invalid receive attempts */ if (unlikely(!buf_len)) return -EINVAL; lock_sock(sk); if (unlikely((sock->state == SS_UNCONNECTED) || (sock->state == SS_CONNECTING))) { res = -ENOTCONN; goto exit; } /* will be updated in set_orig_addr() if needed */ m->msg_namelen = 0; target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); restart: /* Look for a message in receive queue; wait if necessary */ while (skb_queue_empty(&sk->sk_receive_queue)) { if (sock->state == SS_DISCONNECTING) { res = -ENOTCONN; goto exit; } if (timeout <= 0L) { res = timeout ? timeout : -EWOULDBLOCK; goto exit; } release_sock(sk); timeout = wait_event_interruptible_timeout(*sk_sleep(sk), tipc_rx_ready(sock), timeout); lock_sock(sk); } /* Look at first message in receive queue */ buf = skb_peek(&sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_rx_queue(sk); goto restart; } /* Optionally capture sender's address & ancillary data of first msg */ if (sz_copied == 0) { set_orig_addr(m, msg); res = anc_data_recv(m, msg, tport); if (res) goto exit; } /* Capture message data (if valid) & compute return value (always) */ if (!err) { u32 offset = (u32)(unsigned long)(TIPC_SKB_CB(buf)->handle); sz -= offset; needed = (buf_len - sz_copied); sz_to_copy = (sz <= needed) ? sz : needed; res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg) + offset, m->msg_iov, sz_to_copy); if (res) goto exit; sz_copied += sz_to_copy; if (sz_to_copy < sz) { if (!(flags & MSG_PEEK)) TIPC_SKB_CB(buf)->handle = (void *)(unsigned long)(offset + sz_to_copy); goto exit; } } else { if (sz_copied != 0) goto exit; /* can't add error msg to valid data */ if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tport->ref, tport->conn_unacked); advance_rx_queue(sk); } /* Loop around if more data is required */ if ((sz_copied < buf_len) && /* didn't get all requested data */ (!skb_queue_empty(&sk->sk_receive_queue) || (sz_copied < target)) && /* and more is ready or required */ (!(flags & MSG_PEEK)) && /* and aren't just peeking at data */ (!err)) /* and haven't reached a FIN */ goto restart; exit: release_sock(sk); return sz_copied ? sz_copied : res; }
static int recv_msg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct tipc_sock *tsock = tipc_sk(sock->sk); struct sk_buff *buf; struct tipc_msg *msg; unsigned int q_len; unsigned int sz; u32 err; int res; /* Currently doesn't support receiving into multiple iovec entries */ if (m->msg_iovlen != 1) return -EOPNOTSUPP; /* Catch invalid receive attempts */ if (unlikely(!buf_len)) return -EINVAL; if (sock->type == SOCK_SEQPACKET) { if (unlikely(sock->state == SS_UNCONNECTED)) return -ENOTCONN; if (unlikely((sock->state == SS_DISCONNECTING) && (skb_queue_len(&sock->sk->sk_receive_queue) == 0))) return -ENOTCONN; } /* Look for a message in receive queue; wait if necessary */ if (unlikely(down_interruptible(&tsock->sem))) return -ERESTARTSYS; restart: if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) && (flags & MSG_DONTWAIT))) { res = -EWOULDBLOCK; goto exit; } if ((res = wait_event_interruptible( *sock->sk->sk_sleep, ((q_len = skb_queue_len(&sock->sk->sk_receive_queue)) || (sock->state == SS_DISCONNECTING))) )) { goto exit; } /* Catch attempt to receive on an already terminated connection */ /* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */ if (!q_len) { res = -ENOTCONN; goto exit; } /* Get access to first message in receive queue */ buf = skb_peek(&sock->sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Complete connection setup for an implied connect */ if (unlikely(sock->state == SS_CONNECTING)) { if ((res = auto_connect(sock, tsock, msg))) goto exit; } /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_queue(tsock); goto restart; } /* Capture sender's address (optional) */ set_orig_addr(m, msg); /* Capture ancillary data (optional) */ if ((res = anc_data_recv(m, msg, tsock->p))) goto exit; /* Capture message data (if valid) & compute return value (always) */ if (!err) { if (unlikely(buf_len < sz)) { sz = buf_len; m->msg_flags |= MSG_TRUNC; } if (unlikely(copy_to_user(m->msg_iov->iov_base, msg_data(msg), sz))) { res = -EFAULT; goto exit; } res = sz; } else { if ((sock->state == SS_READY) || ((err == TIPC_CONN_SHUTDOWN) || m->msg_control)) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tsock->p->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tsock->p->ref, tsock->p->conn_unacked); advance_queue(tsock); } exit: up(&tsock->sem); return res; }
static int recv_stream(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct tipc_sock *tsock = tipc_sk(sock->sk); struct sk_buff *buf; struct tipc_msg *msg; unsigned int q_len; unsigned int sz; int sz_to_copy; int sz_copied = 0; int needed; char __user *crs = m->msg_iov->iov_base; unsigned char *buf_crs; u32 err; int res; /* Currently doesn't support receiving into multiple iovec entries */ if (m->msg_iovlen != 1) return -EOPNOTSUPP; /* Catch invalid receive attempts */ if (unlikely(!buf_len)) return -EINVAL; if (unlikely(sock->state == SS_DISCONNECTING)) { if (skb_queue_len(&sock->sk->sk_receive_queue) == 0) return -ENOTCONN; } else if (unlikely(sock->state != SS_CONNECTED)) return -ENOTCONN; /* Look for a message in receive queue; wait if necessary */ if (unlikely(down_interruptible(&tsock->sem))) return -ERESTARTSYS; restart: if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) && (flags & MSG_DONTWAIT))) { res = -EWOULDBLOCK; goto exit; } if ((res = wait_event_interruptible( *sock->sk->sk_sleep, ((q_len = skb_queue_len(&sock->sk->sk_receive_queue)) || (sock->state == SS_DISCONNECTING))) )) { goto exit; } /* Catch attempt to receive on an already terminated connection */ /* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */ if (!q_len) { res = -ENOTCONN; goto exit; } /* Get access to first message in receive queue */ buf = skb_peek(&sock->sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_queue(tsock); goto restart; } /* Optionally capture sender's address & ancillary data of first msg */ if (sz_copied == 0) { set_orig_addr(m, msg); if ((res = anc_data_recv(m, msg, tsock->p))) goto exit; } /* Capture message data (if valid) & compute return value (always) */ if (!err) { buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle); sz = skb_tail_pointer(buf) - buf_crs; needed = (buf_len - sz_copied); sz_to_copy = (sz <= needed) ? sz : needed; if (unlikely(copy_to_user(crs, buf_crs, sz_to_copy))) { res = -EFAULT; goto exit; } sz_copied += sz_to_copy; if (sz_to_copy < sz) { if (!(flags & MSG_PEEK)) TIPC_SKB_CB(buf)->handle = buf_crs + sz_to_copy; goto exit; } crs += sz_to_copy; } else { if (sz_copied != 0) goto exit; /* can't add error msg to valid data */ if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tsock->p->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tsock->p->ref, tsock->p->conn_unacked); advance_queue(tsock); } /* Loop around if more data is required */ if ((sz_copied < buf_len) /* didn't get all requested data */ && (flags & MSG_WAITALL) /* ... and need to wait for more */ && (!(flags & MSG_PEEK)) /* ... and aren't just peeking at data */ && (!err) /* ... and haven't reached a FIN */ ) goto restart; exit: up(&tsock->sem); return sz_copied ? sz_copied : res; }
static int recv_stream(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct sock *sk = sock->sk; struct tipc_port *tport = tipc_sk_port(sk); struct sk_buff *buf; struct tipc_msg *msg; unsigned int sz; int sz_to_copy, target, needed; int sz_copied = 0; char __user *crs = m->msg_iov->iov_base; unsigned char *buf_crs; u32 err; int res = 0; /* Catch invalid receive attempts */ if (m->msg_iovlen != 1) return -EOPNOTSUPP; /* Don't do multiple iovec entries yet */ if (unlikely(!buf_len)) return -EINVAL; lock_sock(sk); if (unlikely((sock->state == SS_UNCONNECTED) || (sock->state == SS_CONNECTING))) { res = -ENOTCONN; goto exit; } target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); restart: /* Look for a message in receive queue; wait if necessary */ while (skb_queue_empty(&sk->sk_receive_queue)) { if (sock->state == SS_DISCONNECTING) { res = -ENOTCONN; goto exit; } if (flags & MSG_DONTWAIT) { res = -EWOULDBLOCK; goto exit; } release_sock(sk); res = wait_event_interruptible(*sk_sleep(sk), (!skb_queue_empty(&sk->sk_receive_queue) || (sock->state == SS_DISCONNECTING))); lock_sock(sk); if (res) goto exit; } /* Look at first message in receive queue */ buf = skb_peek(&sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_rx_queue(sk); goto restart; } /* Optionally capture sender's address & ancillary data of first msg */ if (sz_copied == 0) { set_orig_addr(m, msg); res = anc_data_recv(m, msg, tport); if (res) goto exit; } /* Capture message data (if valid) & compute return value (always) */ if (!err) { buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle); sz = (unsigned char *)msg + msg_size(msg) - buf_crs; needed = (buf_len - sz_copied); sz_to_copy = (sz <= needed) ? sz : needed; if (unlikely(copy_to_user(crs, buf_crs, sz_to_copy))) { res = -EFAULT; goto exit; } sz_copied += sz_to_copy; if (sz_to_copy < sz) { if (!(flags & MSG_PEEK)) TIPC_SKB_CB(buf)->handle = buf_crs + sz_to_copy; goto exit; } crs += sz_to_copy; } else { if (sz_copied != 0) goto exit; /* can't add error msg to valid data */ if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tport->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tport->ref, tport->conn_unacked); advance_rx_queue(sk); } /* Loop around if more data is required */ if ((sz_copied < buf_len) && /* didn't get all requested data */ (!skb_queue_empty(&sk->sk_receive_queue) || (sz_copied < target)) && /* and more is ready or required */ (!(flags & MSG_PEEK)) && /* and aren't just peeking at data */ (!err)) /* and haven't reached a FIN */ goto restart; exit: release_sock(sk); return sz_copied ? sz_copied : res; }