summaryrefslogtreecommitdiff
path: root/net/tipc/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r--net/tipc/socket.c394
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;
}