diff options
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r-- | net/tipc/socket.c | 394 |
1 files changed, 162 insertions, 232 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index aab4948f0aff..6cc7ddd2fb7c 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -55,6 +55,9 @@ struct tipc_sock { #define tipc_sk(sk) ((struct tipc_sock *)(sk)) #define tipc_sk_port(sk) (tipc_sk(sk)->p) +#define tipc_rx_ready(sock) (!skb_queue_empty(&sock->sk->sk_receive_queue) || \ + (sock->state == SS_DISCONNECTING)) + static int backlog_rcv(struct sock *sk, struct sk_buff *skb); static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf); static void wakeupdispatch(struct tipc_port *tport); @@ -236,6 +239,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, int protocol, int tipc_sock_create_local(int type, struct socket **res) { int rc; + struct sock *sk; rc = sock_create_lite(AF_TIPC, type, 0, res); if (rc < 0) { @@ -244,6 +248,8 @@ int tipc_sock_create_local(int type, struct socket **res) } tipc_sk_create(&init_net, *res, 0, 1); + sk = (*res)->sk; + return 0; } @@ -332,7 +338,7 @@ static int release(struct socket *sock) buf = __skb_dequeue(&sk->sk_receive_queue); if (buf == NULL) break; - if (TIPC_SKB_CB(buf)->handle != NULL) + if (TIPC_SKB_CB(buf)->handle != 0) kfree_skb(buf); else { if ((sock->state == SS_CONNECTING) || @@ -348,7 +354,7 @@ static int release(struct socket *sock) * Delete TIPC port; this ensures no more messages are queued * (also disconnects an active connection & sends a 'FIN-' to peer) */ - res = tipc_deleteport(tport); + res = tipc_deleteport(tport->ref); /* Discard any remaining (connection-based) messages in receive queue */ __skb_queue_purge(&sk->sk_receive_queue); @@ -380,46 +386,30 @@ static int release(struct socket *sock) */ static int bind(struct socket *sock, struct sockaddr *uaddr, int uaddr_len) { - struct sock *sk = sock->sk; struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; - struct tipc_port *tport = tipc_sk_port(sock->sk); - int res = -EINVAL; + u32 portref = tipc_sk_port(sock->sk)->ref; - lock_sock(sk); - if (unlikely(!uaddr_len)) { - res = tipc_withdraw(tport, 0, NULL); - goto exit; - } + if (unlikely(!uaddr_len)) + return tipc_withdraw(portref, 0, NULL); - if (uaddr_len < sizeof(struct sockaddr_tipc)) { - res = -EINVAL; - goto exit; - } - if (addr->family != AF_TIPC) { - res = -EAFNOSUPPORT; - goto exit; - } + if (uaddr_len < sizeof(struct sockaddr_tipc)) + return -EINVAL; + if (addr->family != AF_TIPC) + return -EAFNOSUPPORT; if (addr->addrtype == TIPC_ADDR_NAME) addr->addr.nameseq.upper = addr->addr.nameseq.lower; - else if (addr->addrtype != TIPC_ADDR_NAMESEQ) { - res = -EAFNOSUPPORT; - goto exit; - } + else if (addr->addrtype != TIPC_ADDR_NAMESEQ) + return -EAFNOSUPPORT; if ((addr->addr.nameseq.type < TIPC_RESERVED_TYPES) && (addr->addr.nameseq.type != TIPC_TOP_SRV) && - (addr->addr.nameseq.type != TIPC_CFG_SRV)) { - res = -EACCES; - goto exit; - } + (addr->addr.nameseq.type != TIPC_CFG_SRV)) + return -EACCES; - res = (addr->scope > 0) ? - tipc_publish(tport, addr->scope, &addr->addr.nameseq) : - tipc_withdraw(tport, -addr->scope, &addr->addr.nameseq); -exit: - release_sock(sk); - return res; + return (addr->scope > 0) ? + tipc_publish(portref, addr->scope, &addr->addr.nameseq) : + tipc_withdraw(portref, -addr->scope, &addr->addr.nameseq); } /** @@ -564,31 +554,6 @@ static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m) return 0; } -static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) -{ - struct sock *sk = sock->sk; - struct tipc_port *tport = tipc_sk_port(sk); - DEFINE_WAIT(wait); - int done; - - do { - int err = sock_error(sk); - if (err) - return err; - if (sock->state == SS_DISCONNECTING) - return -EPIPE; - if (!*timeo_p) - return -EAGAIN; - if (signal_pending(current)) - return sock_intr_errno(*timeo_p); - - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - done = sk_wait_event(sk, timeo_p, !tport->congested); - finish_wait(sk_sleep(sk), &wait); - } while (!done); - return 0; -} - /** * send_msg - send message in connectionless manner * @iocb: if NULL, indicates that socket lock is already held @@ -608,9 +573,9 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, { struct sock *sk = sock->sk; struct tipc_port *tport = tipc_sk_port(sk); - DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); + struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; int needs_conn; - long timeo; + long timeout_val; int res = -EINVAL; if (unlikely(!dest)) @@ -647,7 +612,8 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, reject_rx_queue(sk); } - timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); + do { if (dest->addrtype == TIPC_ADDR_NAME) { res = dest_name_check(dest, m); @@ -656,11 +622,13 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, res = tipc_send2name(tport->ref, &dest->addr.name.name, dest->addr.name.domain, + m->msg_iovlen, m->msg_iov, total_len); } else if (dest->addrtype == TIPC_ADDR_ID) { res = tipc_send2port(tport->ref, &dest->addr.id, + m->msg_iovlen, m->msg_iov, total_len); } else if (dest->addrtype == TIPC_ADDR_MCAST) { @@ -673,6 +641,7 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, break; res = tipc_multicast(tport->ref, &dest->addr.nameseq, + m->msg_iovlen, m->msg_iov, total_len); } @@ -681,9 +650,14 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, sock->state = SS_CONNECTING; break; } - res = tipc_wait_for_sndmsg(sock, &timeo); - if (res) + if (timeout_val <= 0L) { + res = timeout_val ? timeout_val : -EWOULDBLOCK; break; + } + release_sock(sk); + timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), + !tport->congested, timeout_val); + lock_sock(sk); } while (1); exit: @@ -692,34 +666,6 @@ exit: return res; } -static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) -{ - struct sock *sk = sock->sk; - struct tipc_port *tport = tipc_sk_port(sk); - DEFINE_WAIT(wait); - int done; - - do { - int err = sock_error(sk); - if (err) - return err; - if (sock->state == SS_DISCONNECTING) - return -EPIPE; - else if (sock->state != SS_CONNECTED) - return -ENOTCONN; - if (!*timeo_p) - return -EAGAIN; - if (signal_pending(current)) - return sock_intr_errno(*timeo_p); - - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - done = sk_wait_event(sk, timeo_p, - (!tport->congested || !tport->connected)); - finish_wait(sk_sleep(sk), &wait); - } while (!done); - return 0; -} - /** * send_packet - send a connection-oriented message * @iocb: if NULL, indicates that socket lock is already held @@ -736,9 +682,9 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, { struct sock *sk = sock->sk; struct tipc_port *tport = tipc_sk_port(sk); - DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); - int res = -EINVAL; - long timeo; + struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; + long timeout_val; + int res; /* Handle implied connection establishment */ if (unlikely(dest)) @@ -750,24 +696,31 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, if (iocb) lock_sock(sk); - if (unlikely(sock->state != SS_CONNECTED)) { - if (sock->state == SS_DISCONNECTING) - res = -EPIPE; - else - res = -ENOTCONN; - goto exit; - } + timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); - timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); do { - res = tipc_send(tport->ref, m->msg_iov, total_len); + if (unlikely(sock->state != SS_CONNECTED)) { + if (sock->state == SS_DISCONNECTING) + res = -EPIPE; + else + res = -ENOTCONN; + break; + } + + res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov, + total_len); if (likely(res != -ELINKCONG)) break; - res = tipc_wait_for_sndpkt(sock, &timeo); - if (res) + if (timeout_val <= 0L) { + res = timeout_val ? timeout_val : -EWOULDBLOCK; break; + } + release_sock(sk); + timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), + (!tport->congested || !tport->connected), timeout_val); + lock_sock(sk); } while (1); -exit: + if (iocb) release_sock(sk); return res; @@ -805,11 +758,16 @@ static int send_stream(struct kiocb *iocb, struct socket *sock, /* Handle special cases where there is no connection */ if (unlikely(sock->state != SS_CONNECTED)) { - if (sock->state == SS_UNCONNECTED) + if (sock->state == SS_UNCONNECTED) { res = send_packet(NULL, sock, m, total_len); - else - res = sock->state == SS_DISCONNECTING ? -EPIPE : -ENOTCONN; - goto exit; + goto exit; + } else if (sock->state == SS_DISCONNECTING) { + res = -EPIPE; + goto exit; + } else { + res = -ENOTCONN; + goto exit; + } } if (unlikely(m->msg_name)) { @@ -906,7 +864,7 @@ static int auto_connect(struct socket *sock, struct tipc_msg *msg) */ static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg) { - DECLARE_SOCKADDR(struct sockaddr_tipc *, addr, m->msg_name); + struct sockaddr_tipc *addr = (struct sockaddr_tipc *)m->msg_name; if (addr) { addr->family = AF_TIPC; @@ -991,37 +949,6 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg, return 0; } -static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo) -{ - struct sock *sk = sock->sk; - DEFINE_WAIT(wait); - int err; - - for (;;) { - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - if (skb_queue_empty(&sk->sk_receive_queue)) { - if (sock->state == SS_DISCONNECTING) { - err = -ENOTCONN; - break; - } - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - } - err = 0; - if (!skb_queue_empty(&sk->sk_receive_queue)) - break; - err = sock_intr_errno(timeo); - if (signal_pending(current)) - break; - err = -EAGAIN; - if (!timeo) - break; - } - finish_wait(sk_sleep(sk), &wait); - return err; -} - /** * recv_msg - receive packet-oriented message * @iocb: (unused) @@ -1041,7 +968,7 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock, struct tipc_port *tport = tipc_sk_port(sk); struct sk_buff *buf; struct tipc_msg *msg; - long timeo; + long timeout; unsigned int sz; u32 err; int res; @@ -1057,13 +984,28 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock, goto exit; } - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + /* 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 */ - res = tipc_wait_for_rcvmsg(sock, timeo); - if (res) - goto exit; + 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); @@ -1135,7 +1077,7 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock, struct tipc_port *tport = tipc_sk_port(sk); struct sk_buff *buf; struct tipc_msg *msg; - long timeo; + long timeout; unsigned int sz; int sz_to_copy, target, needed; int sz_copied = 0; @@ -1148,19 +1090,34 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock, lock_sock(sk); - if (unlikely(sock->state == SS_UNCONNECTED)) { + if (unlikely((sock->state == SS_UNCONNECTED))) { 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); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); restart: /* Look for a message in receive queue; wait if necessary */ - res = tipc_wait_for_rcvmsg(sock, timeo); - if (res) - goto exit; + 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); @@ -1364,12 +1321,14 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); + unsigned int limit; if (msg_connected(msg)) - return sysctl_tipc_rmem[2]; - - return sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE << - msg_importance(msg); + limit = sysctl_tipc_rmem[2]; + else + limit = sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE << + msg_importance(msg); + return limit; } /** @@ -1409,7 +1368,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf) return TIPC_ERR_OVERLOAD; /* Enqueue message */ - TIPC_SKB_CB(buf)->handle = NULL; + TIPC_SKB_CB(buf)->handle = 0; __skb_queue_tail(&sk->sk_receive_queue, buf); skb_set_owner_r(buf, sk); @@ -1483,28 +1442,6 @@ static void wakeupdispatch(struct tipc_port *tport) sk->sk_write_space(sk); } -static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) -{ - struct sock *sk = sock->sk; - DEFINE_WAIT(wait); - int done; - - do { - int err = sock_error(sk); - if (err) - return err; - if (!*timeo_p) - return -ETIMEDOUT; - if (signal_pending(current)) - return sock_intr_errno(*timeo_p); - - prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - done = sk_wait_event(sk, timeo_p, sock->state != SS_CONNECTING); - finish_wait(sk_sleep(sk), &wait); - } while (!done); - return 0; -} - /** * connect - establish a connection to another TIPC port * @sock: socket structure @@ -1520,8 +1457,7 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, struct sock *sk = sock->sk; struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; struct msghdr m = {NULL,}; - long timeout = (flags & O_NONBLOCK) ? 0 : tipc_sk(sk)->conn_timeout; - socket_state previous; + unsigned int timeout; int res; lock_sock(sk); @@ -1543,7 +1479,8 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, goto exit; } - previous = sock->state; + timeout = (flags & O_NONBLOCK) ? 0 : tipc_sk(sk)->conn_timeout; + switch (sock->state) { case SS_UNCONNECTED: /* Send a 'SYN-' to destination */ @@ -1565,22 +1502,43 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, * case is EINPROGRESS, rather than EALREADY. */ res = -EINPROGRESS; + break; case SS_CONNECTING: - if (previous == SS_CONNECTING) - res = -EALREADY; - if (!timeout) - goto exit; - timeout = msecs_to_jiffies(timeout); - /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ - res = tipc_wait_for_connect(sock, &timeout); + res = -EALREADY; break; case SS_CONNECTED: res = -EISCONN; break; default: res = -EINVAL; - break; + goto exit; + } + + if (sock->state == SS_CONNECTING) { + if (!timeout) + goto exit; + + /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ + release_sock(sk); + res = wait_event_interruptible_timeout(*sk_sleep(sk), + sock->state != SS_CONNECTING, + timeout ? (long)msecs_to_jiffies(timeout) + : MAX_SCHEDULE_TIMEOUT); + lock_sock(sk); + if (res <= 0) { + if (res == 0) + res = -ETIMEDOUT; + else + ; /* leave "res" unchanged */ + goto exit; + } } + + if (unlikely(sock->state == SS_DISCONNECTING)) + res = sock_error(sk); + else + res = 0; + exit: release_sock(sk); return res; @@ -1611,42 +1569,6 @@ static int listen(struct socket *sock, int len) return res; } -static int tipc_wait_for_accept(struct socket *sock, long timeo) -{ - struct sock *sk = sock->sk; - DEFINE_WAIT(wait); - int err; - - /* True wake-one mechanism for incoming connections: only - * one process gets woken up, not the 'whole herd'. - * Since we do not 'race & poll' for established sockets - * anymore, the common case will execute the loop only once. - */ - for (;;) { - prepare_to_wait_exclusive(sk_sleep(sk), &wait, - TASK_INTERRUPTIBLE); - if (skb_queue_empty(&sk->sk_receive_queue)) { - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - } - err = 0; - if (!skb_queue_empty(&sk->sk_receive_queue)) - break; - err = -EINVAL; - if (sock->state != SS_LISTENING) - break; - err = sock_intr_errno(timeo); - if (signal_pending(current)) - break; - err = -EAGAIN; - if (!timeo) - break; - } - finish_wait(sk_sleep(sk), &wait); - return err; -} - /** * accept - wait for connection request * @sock: listening socket @@ -1663,7 +1585,7 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags) struct tipc_port *new_tport; struct tipc_msg *msg; u32 new_ref; - long timeo; + int res; lock_sock(sk); @@ -1673,10 +1595,18 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags) goto exit; } - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - res = tipc_wait_for_accept(sock, timeo); - if (res) - goto exit; + while (skb_queue_empty(&sk->sk_receive_queue)) { + if (flags & O_NONBLOCK) { + res = -EWOULDBLOCK; + goto exit; + } + release_sock(sk); + res = wait_event_interruptible(*sk_sleep(sk), + (!skb_queue_empty(&sk->sk_receive_queue))); + lock_sock(sk); + if (res) + goto exit; + } buf = skb_peek(&sk->sk_receive_queue); @@ -1761,7 +1691,7 @@ restart: /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */ buf = __skb_dequeue(&sk->sk_receive_queue); if (buf) { - if (TIPC_SKB_CB(buf)->handle != NULL) { + if (TIPC_SKB_CB(buf)->handle != 0) { kfree_skb(buf); goto restart; } |