Merge "net: ipc_router: Add support for connect system call"

This commit is contained in:
Linux Build Service Account 2014-09-15 21:04:07 -07:00 committed by Gerrit - the friendly Code Review server
commit 9a12539569
4 changed files with 194 additions and 7 deletions

View File

@ -113,6 +113,9 @@ struct msm_ipc_port {
struct mutex port_lock_lhc3;
struct comm_mode_info mode_info;
struct msm_ipc_port_addr dest_addr;
int conn_status;
struct list_head port_rx_q;
struct mutex port_rx_q_lock_lhc3;
char rx_ws_name[MAX_WS_NAME_SZ];

View File

@ -141,6 +141,11 @@ struct msm_ipc_resume_tx_port {
uint32_t node_id;
};
struct ipc_router_conn_info {
struct list_head list;
uint32_t port_id;
};
#define RP_HASH_SIZE 32
struct msm_ipc_router_remote_port {
struct list_head list;
@ -150,6 +155,7 @@ struct msm_ipc_router_remote_port {
uint32_t port_id;
uint32_t tx_quota_cnt;
struct list_head resume_tx_port_list;
struct list_head conn_info_list;
void *sec_rule;
struct msm_ipc_server *server;
};
@ -202,6 +208,7 @@ static struct workqueue_struct *msm_ipc_router_workqueue;
static int process_resume_tx_msg(union rr_control_msg *msg,
struct rr_packet *pkt);
static void ipc_router_reset_conn(struct msm_ipc_router_remote_port *rport_ptr);
enum {
DOWN,
@ -1188,6 +1195,7 @@ static struct msm_ipc_router_remote_port *ipc_router_create_rport(
kref_init(&rport_ptr->ref);
mutex_init(&rport_ptr->rport_lock_lhb2);
INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
INIT_LIST_HEAD(&rport_ptr->conn_info_list);
list_add_tail(&rport_ptr->list,
&rt_entry->remote_port_list[key]);
out_create_rmt_port1:
@ -1854,6 +1862,7 @@ static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info,
D("Remove server %08x:%08x - %08x:%08x",
server->name.service, server->name.instance,
rport_ptr->node_id, rport_ptr->port_id);
ipc_router_reset_conn(rport_ptr);
memset(&ctl, 0, sizeof(ctl));
ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
ctl.srv.service = server->name.service;
@ -2022,6 +2031,97 @@ void msm_ipc_sync_default_sec_rule(void *rule)
up_write(&server_list_lock_lha2);
}
/**
* ipc_router_reset_conn() - Reset the connection to remote port
* @rport_ptr: Pointer to the remote port to be disconnected.
*
* This function is used to reset all the local ports that are connected to
* the remote port being passed.
*/
static void ipc_router_reset_conn(struct msm_ipc_router_remote_port *rport_ptr)
{
struct msm_ipc_port *port_ptr;
struct ipc_router_conn_info *conn_info, *tmp_conn_info;
mutex_lock(&rport_ptr->rport_lock_lhb2);
list_for_each_entry_safe(conn_info, tmp_conn_info,
&rport_ptr->conn_info_list, list) {
port_ptr = ipc_router_get_port_ref(conn_info->port_id);
if (!port_ptr)
continue;
mutex_lock(&port_ptr->port_lock_lhc3);
port_ptr->conn_status = CONNECTION_RESET;
mutex_unlock(&port_ptr->port_lock_lhc3);
wake_up(&port_ptr->port_rx_wait_q);
kref_put(&port_ptr->ref, ipc_router_release_port);
list_del(&conn_info->list);
kfree(conn_info);
}
mutex_unlock(&rport_ptr->rport_lock_lhb2);
}
/**
* ipc_router_set_conn() - Set the connection by initializing dest address
* @port_ptr: Local port in which the connection has to be set.
* @addr: Destination address of the connection.
*
* @return: 0 on success, standard Linux error codes on failure.
*/
int ipc_router_set_conn(struct msm_ipc_port *port_ptr,
struct msm_ipc_addr *addr)
{
struct msm_ipc_router_remote_port *rport_ptr;
struct ipc_router_conn_info *conn_info;
if (unlikely(!port_ptr || !addr))
return -EINVAL;
if (addr->addrtype != MSM_IPC_ADDR_ID) {
IPC_RTR_ERR("%s: Invalid Address type\n", __func__);
return -EINVAL;
}
if (port_ptr->type == SERVER_PORT) {
IPC_RTR_ERR("%s: Connection refused on a server port\n",
__func__);
return -ECONNREFUSED;
}
if (port_ptr->conn_status == CONNECTED) {
IPC_RTR_ERR("%s: Port %08x already connected\n",
__func__, port_ptr->this_port.port_id);
return -EISCONN;
}
conn_info = kzalloc(sizeof(struct ipc_router_conn_info), GFP_KERNEL);
if (!conn_info) {
IPC_RTR_ERR("%s: Error allocating conn_info\n", __func__);
return -ENOMEM;
}
INIT_LIST_HEAD(&conn_info->list);
conn_info->port_id = port_ptr->this_port.port_id;
rport_ptr = ipc_router_get_rport_ref(addr->addr.port_addr.node_id,
addr->addr.port_addr.port_id);
if (!rport_ptr) {
IPC_RTR_ERR("%s: Invalid remote endpoint\n", __func__);
kfree(conn_info);
return -ENODEV;
}
mutex_lock(&rport_ptr->rport_lock_lhb2);
list_add_tail(&conn_info->list, &rport_ptr->conn_info_list);
mutex_unlock(&rport_ptr->rport_lock_lhb2);
mutex_lock(&port_ptr->port_lock_lhc3);
memcpy(&port_ptr->dest_addr, &addr->addr.port_addr,
sizeof(struct msm_ipc_port_addr));
port_ptr->conn_status = CONNECTED;
mutex_unlock(&port_ptr->port_lock_lhc3);
kref_put(&rport_ptr->ref, ipc_router_release_rport);
return 0;
}
static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
struct rr_header_v1 *hdr)
{
@ -2411,6 +2511,7 @@ int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr)
{
struct msm_ipc_server *server;
union rr_control_msg ctl;
struct msm_ipc_router_remote_port *rport_ptr;
if (!port_ptr)
return -EINVAL;
@ -2437,6 +2538,12 @@ int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr)
return -ENODEV;
}
mutex_lock(&port_ptr->port_lock_lhc3);
port_ptr->type = CLIENT_PORT;
rport_ptr = (struct msm_ipc_router_remote_port *)port_ptr->rport_info;
mutex_unlock(&port_ptr->port_lock_lhc3);
if (rport_ptr)
ipc_router_reset_conn(rport_ptr);
memset(&ctl, 0, sizeof(ctl));
ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
ctl.srv.service = server->name.service;
@ -2968,6 +3075,16 @@ int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
list_del(&port_ptr->list);
up_write(&local_ports_lock_lhc2);
mutex_lock(&port_ptr->port_lock_lhc3);
rport_ptr = (struct msm_ipc_router_remote_port *)
port_ptr->rport_info;
port_ptr->rport_info = NULL;
mutex_unlock(&port_ptr->port_lock_lhc3);
if (rport_ptr) {
ipc_router_reset_conn(rport_ptr);
ipc_router_destroy_rport(rport_ptr);
}
if (port_ptr->type == SERVER_PORT) {
memset(&msg, 0, sizeof(msg));
msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;

View File

@ -64,6 +64,12 @@ enum {
MULTI_LINK_MODE,
};
enum {
CONNECTION_RESET = -1,
NOT_CONNECTED,
CONNECTED,
};
struct msm_ipc_sock {
struct sock sk;
struct msm_ipc_port *port;
@ -114,4 +120,14 @@ void msm_ipc_sync_default_sec_rule(void *rule);
int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout);
void msm_ipc_router_free_skb(struct sk_buff_head *skb_head);
/**
* ipc_router_set_conn() - Set the connection by initializing dest address
* @port_ptr: Local port in which the connection has to be set.
* @addr: Destination address of the connection.
*
* @return: 0 on success, standard Linux error codes on failure.
*/
int ipc_router_set_conn(struct msm_ipc_port *port_ptr,
struct msm_ipc_addr *addr);
#endif

View File

@ -345,6 +345,42 @@ int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
return ret;
}
static int ipc_router_connect(struct socket *sock, struct sockaddr *uaddr,
int uaddr_len, int flags)
{
struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
int ret;
if (!sk)
return -EINVAL;
if (uaddr_len <= 0) {
IPC_RTR_ERR("%s: Invalid address length\n", __func__);
return -EINVAL;
}
if (!addr) {
IPC_RTR_ERR("%s: Invalid address\n", __func__);
return -EINVAL;
}
if (addr->family != AF_MSM_IPC) {
IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
return -EAFNOSUPPORT;
}
port_ptr = msm_ipc_sk_port(sk);
if (!port_ptr)
return -ENODEV;
lock_sock(sk);
ret = ipc_router_set_conn(port_ptr, &addr->address);
release_sock(sk);
return ret;
}
static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len)
{
@ -354,12 +390,24 @@ static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sk_buff_head *msg;
struct sk_buff *ipc_buf;
int ret;
struct msm_ipc_addr dest_addr = {0};
if (!dest)
return -EDESTADDRREQ;
if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC)
return -EINVAL;
if (dest) {
if (m->msg_namelen < sizeof(*dest) ||
dest->family != AF_MSM_IPC)
return -EINVAL;
memcpy(&dest_addr, &dest->address, sizeof(dest_addr));
} else {
if (port_ptr->conn_status == NOT_CONNECTED) {
return -EDESTADDRREQ;
} else if (port_ptr->conn_status < CONNECTION_RESET) {
return -ENETRESET;
} else {
memcpy(&dest_addr.addr.port_addr, &port_ptr->dest_addr,
sizeof(struct msm_ipc_port_addr));
dest_addr.addrtype = MSM_IPC_ADDR_ID;
}
}
if (total_len > MAX_IPC_PKT_SIZE)
return -EINVAL;
@ -378,7 +426,7 @@ static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
ipc_buf = skb_peek(msg);
if (ipc_buf)
msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
ret = msm_ipc_router_send_to(port_ptr, msg, &dest_addr);
if (ret != total_len) {
if (ret < 0) {
if (ret != -EAGAIN)
@ -564,6 +612,9 @@ static unsigned int msm_ipc_router_poll(struct file *file,
if (!list_empty(&port_ptr->port_rx_q))
mask |= (POLLRDNORM | POLLIN);
if (port_ptr->conn_status == CONNECTION_RESET)
mask |= (POLLHUP | POLLERR);
return mask;
}
@ -594,7 +645,7 @@ static const struct proto_ops msm_ipc_proto_ops = {
.owner = THIS_MODULE,
.release = msm_ipc_router_close,
.bind = msm_ipc_router_bind,
.connect = sock_no_connect,
.connect = ipc_router_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,