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

Commit 79354076 authored by Steven Cahail's avatar Steven Cahail
Browse files

msm: ipc: Add single-threaded workqueue for PIL voting



IPC Router votes for the modem by default to ensure that the modem is
loaded so QMI services can be discovered. However, this causes a large
delay measured in seconds for the first client to bind to an IPC Router
socket. For clients that do not need the modem, this delay is
unnecessary.

Move the PIL loading to a single-threaded workqueue to load the modem
and maintain reference counting asynchronously.

Change-Id: I2a2a375fa4538442e5a16465935a07ba1d7437b8
Signed-off-by: default avatarEric Holmberg <eholmber@codeaurora.org>
Signed-off-by: default avatarSteven Cahail <scahail@codeaurora.org>
parent 47d53bf2
Loading
Loading
Loading
Loading
+95 −9
Original line number Diff line number Diff line
@@ -129,6 +129,10 @@ static struct delayed_work ipc_router_smd_xprt_probe_work;
static DEFINE_MUTEX(smd_remote_xprt_list_lock_lha1);
static LIST_HEAD(smd_remote_xprt_list);

static void pil_vote_load_worker(struct work_struct *work);
static void pil_vote_unload_worker(struct work_struct *work);
static struct workqueue_struct *pil_vote_wq;

static int msm_ipc_router_smd_get_xprt_version(
	struct msm_ipc_router_xprt *xprt)
{
@@ -545,28 +549,104 @@ static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev)
	return 0;
}

void *msm_ipc_load_default_node(void)
struct pil_vote_info {
	void *pil_handle;
	struct work_struct load_work;
	struct work_struct unload_work;
};

/**
 * pil_vote_load_worker() - Process vote to load the modem
 *
 * @work: Work item to process
 *
 * This function is called to process votes to load the modem that have been
 * queued by msm_ipc_load_default_node().
 */
static void pil_vote_load_worker(struct work_struct *work)
{
	void *pil = NULL;
	const char *peripheral;
	struct pil_vote_info *vote_info;

	vote_info = container_of(work, struct pil_vote_info, load_work);
	peripheral = smd_edge_to_pil_str(SMD_APPS_MODEM);

	if (!IS_ERR_OR_NULL(peripheral) && !strcmp(peripheral, "modem")) {
		pil = subsystem_get(peripheral);
		if (IS_ERR(pil)) {
		vote_info->pil_handle = subsystem_get(peripheral);
		if (IS_ERR(vote_info->pil_handle)) {
			pr_err("%s: Failed to load %s\n",
				__func__, peripheral);
			pil = NULL;
			vote_info->pil_handle = NULL;
		}
	}
	return pil;
}

/**
 * pil_vote_unload_worker() - Process vote to unload the modem
 *
 * @work: Work item to process
 *
 * This function is called to process votes to unload the modem that have been
 * queued by msm_ipc_unload_default_node().
 */
static void pil_vote_unload_worker(struct work_struct *work)
{
	struct pil_vote_info *vote_info;

	vote_info = container_of(work, struct pil_vote_info, unload_work);

	if (vote_info->pil_handle) {
		subsystem_put(vote_info->pil_handle);
		vote_info->pil_handle = NULL;
	}
	kfree(vote_info);
}

/**
 * msm_ipc_load_default_node() - Queue a vote to load the modem.
 *
 * @return: PIL vote info structure on success, NULL on failure.
 *
 * This function places a work item that loads the modem on the
 * single-threaded workqueue used for processing PIL votes to load
 * or unload the modem.
 */
void *msm_ipc_load_default_node(void)
{
	struct pil_vote_info *vote_info;

	vote_info = kmalloc(sizeof(struct pil_vote_info), GFP_KERNEL);
	if (vote_info == NULL) {
		pr_err("%s: mem alloc for pil_vote_info failed\n", __func__);
		return NULL;
	}

	INIT_WORK(&vote_info->load_work, pil_vote_load_worker);
	queue_work(pil_vote_wq, &vote_info->load_work);

	return vote_info;
}
EXPORT_SYMBOL(msm_ipc_load_default_node);

void msm_ipc_unload_default_node(void *pil)
/**
 * msm_ipc_unload_default_node() - Queue a vote to unload the modem.
 *
 * @pil_vote: PIL vote info structure, containing the PIL handle
 * and work structure.
 *
 * This function places a work item that unloads the modem on the
 * single-threaded workqueue used for processing PIL votes to load
 * or unload the modem.
 */
void msm_ipc_unload_default_node(void *pil_vote)
{
	if (pil)
		subsystem_put(pil);
	struct pil_vote_info *vote_info;

	if (pil_vote) {
		vote_info = (struct pil_vote_info *) pil_vote;
		INIT_WORK(&vote_info->unload_work, pil_vote_unload_worker);
		queue_work(pil_vote_wq, &vote_info->unload_work);
	}
}
EXPORT_SYMBOL(msm_ipc_unload_default_node);

@@ -823,6 +903,12 @@ static int __init msm_ipc_router_smd_xprt_init(void)
		return rc;
	}

	pil_vote_wq = create_singlethread_workqueue("pil_vote_wq");
	if (IS_ERR_OR_NULL(pil_vote_wq)) {
		pr_err("%s: create_singlethread_workqueue failed\n", __func__);
		return -EFAULT;
	}

	INIT_DELAYED_WORK(&ipc_router_smd_xprt_probe_work,
					ipc_router_smd_xprt_probe_worker);
	schedule_delayed_work(&ipc_router_smd_xprt_probe_work,
+1 −1
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ enum {
struct msm_ipc_sock {
	struct sock sk;
	struct msm_ipc_port *port;
	void *default_pil;
	void *default_node_vote_info;
};

/**
+8 −9
Original line number Diff line number Diff line
@@ -303,7 +303,6 @@ int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
	struct sock *sk = sock->sk;
	struct msm_ipc_port *port_ptr;
	int ret;
	void *pil;

	if (!sk)
		return -EINVAL;
@@ -333,8 +332,9 @@ int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
	if (!port_ptr)
		return -ENODEV;

	pil = msm_ipc_load_default_node();
	msm_ipc_sk(sk)->default_pil = pil;
	if (!msm_ipc_sk(sk)->default_node_vote_info)
		msm_ipc_sk(sk)->default_node_vote_info =
			msm_ipc_load_default_node();
	lock_sock(sk);

	ret = msm_ipc_router_register_server(port_ptr, &addr->address);
@@ -445,7 +445,6 @@ static int msm_ipc_router_ioctl(struct socket *sock,
	unsigned int n;
	size_t srv_info_sz = 0;
	int ret;
	void *pil;

	if (!sk)
		return -EINVAL;
@@ -473,8 +472,10 @@ static int msm_ipc_router_ioctl(struct socket *sock,
		break;

	case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
		pil = msm_ipc_load_default_node();
		msm_ipc_sk(sk)->default_pil = pil;
		if (!msm_ipc_sk(sk)->default_node_vote_info)
			msm_ipc_sk(sk)->default_node_vote_info =
				msm_ipc_load_default_node();

		ret = copy_from_user(&server_arg, (void *)arg,
				     sizeof(server_arg));
		if (ret) {
@@ -568,13 +569,11 @@ static int msm_ipc_router_close(struct socket *sock)
{
	struct sock *sk = sock->sk;
	struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
	void *pil = msm_ipc_sk(sk)->default_pil;
	int ret;

	lock_sock(sk);
	ret = msm_ipc_router_close_port(port_ptr);
	if (pil)
		msm_ipc_unload_default_node(pil);
	msm_ipc_unload_default_node(msm_ipc_sk(sk)->default_node_vote_info);
	release_sock(sk);
	sock_put(sk);
	sock->sk = NULL;