* net/tipc/socket.c: TIPC socket API
*
* Copyright (c) 2001-2007, Ericsson AB
- * Copyright (c) 2004-2007, Wind River Systems
+ * Copyright (c) 2004-2008, Wind River Systems
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/mm.h>
-#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
+#include <linux/gfp.h>
#include <asm/string.h>
#include <asm/atomic.h>
#include <net/sock.h>
struct tipc_sock {
struct sock sk;
struct tipc_port *p;
+ struct tipc_portid peer_name;
};
#define tipc_sk(sk) ((struct tipc_sock *)(sk))
* @net: network namespace (must be default network)
* @sock: pre-allocated socket structure
* @protocol: protocol indicator (must be 0)
+ * @kern: caused by kernel or by userspace?
*
* This routine creates additional data structures used by the TIPC socket,
* initializes them, and links them together.
* Returns 0 on success, errno otherwise
*/
-static int tipc_create(struct net *net, struct socket *sock, int protocol)
+static int tipc_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
{
const struct proto_ops *ops;
socket_state state;
struct sock *sk;
- u32 portref;
+ struct tipc_port *tp_ptr;
/* Validate arguments */
- if (net != &init_net)
+ if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
if (unlikely(protocol != 0))
/* Allocate TIPC port for socket to use */
- portref = tipc_createport_raw(sk, &dispatch, &wakeupdispatch,
- TIPC_LOW_IMPORTANCE);
- if (unlikely(portref == 0)) {
+ tp_ptr = tipc_createport_raw(sk, &dispatch, &wakeupdispatch,
+ TIPC_LOW_IMPORTANCE);
+ if (unlikely(!tp_ptr)) {
sk_free(sk);
return -ENOMEM;
}
sock_init_data(sock, sk);
sk->sk_rcvtimeo = msecs_to_jiffies(CONN_TIMEOUT_DEFAULT);
sk->sk_backlog_rcv = backlog_rcv;
- tipc_sk(sk)->p = tipc_get_port(portref);
+ tipc_sk(sk)->p = tp_ptr;
+
+ spin_unlock_bh(tp_ptr->lock);
if (sock->state == SS_READY) {
- tipc_set_portunreturnable(portref, 1);
+ tipc_set_portunreturnable(tp_ptr->ref, 1);
if (sock->type == SOCK_DGRAM)
- tipc_set_portunreliable(portref, 1);
+ tipc_set_portunreliable(tp_ptr->ref, 1);
}
atomic_inc(&tipc_user_count);
* @sock: socket structure
* @uaddr: area for returned socket address
* @uaddr_len: area for returned length of socket address
- * @peer: 0 to obtain socket name, 1 to obtain peer socket name
+ * @peer: 0 = own ID, 1 = current peer ID, 2 = current/former peer ID
*
* Returns 0 on success, errno otherwise
*
- * NOTE: This routine doesn't need to take the socket lock since it doesn't
- * access any non-constant socket information.
+ * NOTE: This routine doesn't need to take the socket lock since it only
+ * accesses socket information that is unchanging (or which changes in
+ * a completely predictable manner).
*/
static int get_name(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr;
- u32 portref = tipc_sk_port(sock->sk)->ref;
- u32 res;
+ struct tipc_sock *tsock = tipc_sk(sock->sk);
if (peer) {
- res = tipc_peer(portref, &addr->addr.id);
- if (res)
- return res;
+ if ((sock->state != SS_CONNECTED) &&
+ ((peer != 2) || (sock->state != SS_DISCONNECTING)))
+ return -ENOTCONN;
+ addr->addr.id.ref = tsock->peer_name.ref;
+ addr->addr.id.node = tsock->peer_name.node;
} else {
- tipc_ownidentity(portref, &addr->addr.id);
+ tipc_ownidentity(tsock->p->ref, &addr->addr.id);
}
*uaddr_len = sizeof(*addr);
struct sock *sk = sock->sk;
u32 mask;
- poll_wait(file, sk->sk_sleep, wait);
+ poll_wait(file, sk_sleep(sk), wait);
if (!skb_queue_empty(&sk->sk_receive_queue) ||
(sock->state == SS_UNCONNECTED) ||
break;
}
release_sock(sk);
- res = wait_event_interruptible(*sk->sk_sleep,
+ res = wait_event_interruptible(*sk_sleep(sk),
!tport->congested);
lock_sock(sk);
if (res)
break;
}
release_sock(sk);
- res = wait_event_interruptible(*sk->sk_sleep,
+ res = wait_event_interruptible(*sk_sleep(sk),
(!tport->congested || !tport->connected));
lock_sock(sk);
if (res)
static int auto_connect(struct socket *sock, struct tipc_msg *msg)
{
- struct tipc_port *tport = tipc_sk_port(sock->sk);
- struct tipc_portid peer;
+ struct tipc_sock *tsock = tipc_sk(sock->sk);
if (msg_errcode(msg)) {
sock->state = SS_DISCONNECTING;
return -ECONNREFUSED;
}
- peer.ref = msg_origport(msg);
- peer.node = msg_orignode(msg);
- tipc_connect2port(tport->ref, &peer);
- tipc_set_portimportance(tport->ref, msg_importance(msg));
+ tsock->peer_name.ref = msg_origport(msg);
+ tsock->peer_name.node = msg_orignode(msg);
+ tipc_connect2port(tsock->p->ref, &tsock->peer_name);
+ tipc_set_portimportance(tsock->p->ref, msg_importance(msg));
sock->state = SS_CONNECTED;
return 0;
}
goto exit;
}
release_sock(sk);
- res = wait_event_interruptible(*sk->sk_sleep,
+ res = wait_event_interruptible(*sk_sleep(sk),
(!skb_queue_empty(&sk->sk_receive_queue) ||
(sock->state == SS_DISCONNECTING)));
lock_sock(sk);
goto exit;
}
release_sock(sk);
- res = wait_event_interruptible(*sk->sk_sleep,
+ res = wait_event_interruptible(*sk_sleep(sk),
(!skb_queue_empty(&sk->sk_receive_queue) ||
(sock->state == SS_DISCONNECTING)));
lock_sock(sk);
/* Loop around if more data is required */
- if ((sz_copied < buf_len) /* didn't get all requested data */
- && (!skb_queue_empty(&sock->sk->sk_receive_queue) ||
- (flags & MSG_WAITALL))
- /* ... and more is ready or required */
- && (!(flags & MSG_PEEK)) /* ... and aren't just peeking at data */
- && (!err) /* ... and haven't reached a FIN */
- )
+ if ((sz_copied < buf_len) && /* didn't get all requested data */
+ (!skb_queue_empty(&sk->sk_receive_queue) ||
+ (flags & MSG_WAITALL)) && /* 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:
tipc_disconnect_port(tipc_sk_port(sk));
}
- if (waitqueue_active(sk->sk_sleep))
- wake_up_interruptible(sk->sk_sleep);
+ if (waitqueue_active(sk_sleep(sk)))
+ wake_up_interruptible(sk_sleep(sk));
return TIPC_OK;
}
if (!sock_owned_by_user(sk)) {
res = filter_rcv(sk, buf);
} else {
- sk_add_backlog(sk, buf);
- res = TIPC_OK;
+ if (sk_add_backlog(sk, buf))
+ res = TIPC_ERR_OVERLOAD;
+ else
+ res = TIPC_OK;
}
bh_unlock_sock(sk);
{
struct sock *sk = (struct sock *)tport->usr_handle;
- if (waitqueue_active(sk->sk_sleep))
- wake_up_interruptible(sk->sk_sleep);
+ if (waitqueue_active(sk_sleep(sk)))
+ wake_up_interruptible(sk_sleep(sk));
}
/**
/* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
release_sock(sk);
- res = wait_event_interruptible_timeout(*sk->sk_sleep,
+ res = wait_event_interruptible_timeout(*sk_sleep(sk),
(!skb_queue_empty(&sk->sk_receive_queue) ||
(sock->state != SS_CONNECTING)),
sk->sk_rcvtimeo);
goto exit;
}
release_sock(sk);
- res = wait_event_interruptible(*sk->sk_sleep,
+ res = wait_event_interruptible(*sk_sleep(sk),
(!skb_queue_empty(&sk->sk_receive_queue)));
lock_sock(sk);
if (res)
buf = skb_peek(&sk->sk_receive_queue);
- res = tipc_create(sock_net(sock->sk), new_sock, 0);
+ res = tipc_create(sock_net(sock->sk), new_sock, 0, 0);
if (!res) {
struct sock *new_sk = new_sock->sk;
- struct tipc_port *new_tport = tipc_sk_port(new_sk);
+ struct tipc_sock *new_tsock = tipc_sk(new_sk);
+ struct tipc_port *new_tport = new_tsock->p;
u32 new_ref = new_tport->ref;
- struct tipc_portid id;
struct tipc_msg *msg = buf_msg(buf);
lock_sock(new_sk);
/* Connect new socket to it's peer */
- id.ref = msg_origport(msg);
- id.node = msg_orignode(msg);
- tipc_connect2port(new_ref, &id);
+ new_tsock->peer_name.ref = msg_origport(msg);
+ new_tsock->peer_name.node = msg_orignode(msg);
+ tipc_connect2port(new_ref, &new_tsock->peer_name);
new_sock->state = SS_CONNECTED;
tipc_set_portimportance(new_ref, msg_importance(msg));
/* Discard any unreceived messages; wake up sleeping tasks */
discard_rx_queue(sk);
- if (waitqueue_active(sk->sk_sleep))
- wake_up_interruptible(sk->sk_sleep);
+ if (waitqueue_active(sk_sleep(sk)))
+ wake_up_interruptible(sk_sleep(sk));
res = 0;
break;
*/
static int setsockopt(struct socket *sock,
- int lvl, int opt, char __user *ov, int ol)
+ int lvl, int opt, char __user *ov, unsigned int ol)
{
struct sock *sk = sock->sk;
struct tipc_port *tport = tipc_sk_port(sk);
value = jiffies_to_msecs(sk->sk_rcvtimeo);
/* no need to set "res", since already 0 at this point */
break;
+ case TIPC_NODE_RECVQ_DEPTH:
+ value = (u32)atomic_read(&tipc_queue_size);
+ break;
+ case TIPC_SOCK_RECVQ_DEPTH:
+ value = skb_queue_len(&sk->sk_receive_queue);
+ break;
default:
res = -EINVAL;
}
else if (len < sizeof(value)) {
res = -EINVAL;
}
- else if ((res = copy_to_user(ov, &value, sizeof(value)))) {
- /* couldn't return value */
+ else if (copy_to_user(ov, &value, sizeof(value))) {
+ res = -EFAULT;
}
else {
res = put_user(sizeof(value), ol);