Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 225f4ea2 authored by Karthikeyan Ramasubramanian's avatar Karthikeyan Ramasubramanian
Browse files

net: ipc_router: Add support for connect system call



Enable support for connect system call, so that the destination address is
stored as part of the port. Subsequently the clients of IPC Router can use
send and recv system calls in addition to sendto and recvfrom.

Reset any connection between the local port and remote port if the remote
port exits either voluntarily or due to subsystem restart.

Change-Id: Icf45934a1fc9d01ff96f2a7a47359b66ac22ccbd
Signed-off-by: default avatarKarthikeyan Ramasubramanian <kramasub@codeaurora.org>
parent 6708b21b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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];
+117 −0
Original line number Diff line number Diff line
@@ -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:
@@ -1855,6 +1863,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;
@@ -2023,6 +2032,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)
{
@@ -2412,6 +2512,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;
@@ -2438,6 +2539,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;
@@ -2969,6 +3076,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;
+16 −0
Original line number Diff line number Diff line
@@ -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
+59 −8
Original line number Diff line number Diff line
@@ -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)
	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,