summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/ipc_router/ipc_router_core.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c
index 3100ebde7021..4f1ce53f7efa 100644
--- a/net/ipc_router/ipc_router_core.c
+++ b/net/ipc_router/ipc_router_core.c
@@ -147,6 +147,8 @@ struct msm_ipc_router_xprt_info {
struct work_struct read_data;
struct workqueue_struct *workqueue;
void *log_ctx;
+ struct kref ref;
+ struct completion ref_complete;
};
#define RT_HASH_SIZE 4
@@ -194,6 +196,9 @@ static void *ipc_router_get_log_ctx(char *sub_name);
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);
+static int ipc_router_get_xprt_info_ref(
+ struct msm_ipc_router_xprt_info *xprt_info);
+static void ipc_router_release_xprt_info_ref(struct kref *ref);
struct pil_vote_info {
void *pil_handle;
@@ -2037,6 +2042,11 @@ static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
down_read(&rt_entry->lock_lha4);
fwd_xprt_info = rt_entry->xprt_info;
+ ret = ipc_router_get_xprt_info_ref(fwd_xprt_info);
+ if (ret < 0) {
+ IPC_RTR_ERR("%s: Abort invalid xprt\n", __func__);
+ goto fm_error_xprt;
+ }
ret = prepend_header(pkt, fwd_xprt_info);
if (ret < 0) {
IPC_RTR_ERR("%s: Prepend Header failed\n", __func__);
@@ -2071,6 +2081,8 @@ static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
fm_error3:
mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
fm_error2:
+ kref_put(&fwd_xprt_info->ref, ipc_router_release_xprt_info_ref);
+fm_error_xprt:
up_read(&rt_entry->lock_lha4);
fm_error1:
if (rt_entry)
@@ -3037,6 +3049,13 @@ static int msm_ipc_router_write_pkt(struct msm_ipc_port *src,
}
down_read(&rt_entry->lock_lha4);
xprt_info = rt_entry->xprt_info;
+ ret = ipc_router_get_xprt_info_ref(xprt_info);
+ if (ret < 0) {
+ IPC_RTR_ERR("%s: Abort invalid xprt\n", __func__);
+ up_read(&rt_entry->lock_lha4);
+ kref_put(&rt_entry->ref, ipc_router_release_rtentry);
+ return ret;
+ }
ret = prepend_header(pkt, xprt_info);
if (ret < 0) {
IPC_RTR_ERR("%s: Prepend Header failed\n", __func__);
@@ -3065,6 +3084,7 @@ out_write_pkt:
ipc_router_log_msg(xprt_info->log_ctx,
IPC_ROUTER_LOG_EVENT_TX_ERR, pkt, hdr, src, rport_ptr);
+ kref_put(&xprt_info->ref, ipc_router_release_xprt_info_ref);
return ret;
}
update_comm_mode_info(&src->mode_info, xprt_info);
@@ -3082,6 +3102,7 @@ out_write_pkt:
(hdr->size & 0xffff));
}
+ kref_put(&xprt_info->ref, ipc_router_release_xprt_info_ref);
return hdr->size;
}
@@ -3225,9 +3246,16 @@ static int msm_ipc_router_send_resume_tx(void *data)
__func__, hdr->src_node_id);
return -ENODEV;
}
+ ret = ipc_router_get_xprt_info_ref(rt_entry->xprt_info);
+ if (ret < 0) {
+ IPC_RTR_ERR("%s: Abort invalid xprt\n", __func__);
+ kref_put(&rt_entry->ref, ipc_router_release_rtentry);
+ return ret;
+ }
ret = ipc_router_send_ctl_msg(rt_entry->xprt_info, &msg,
hdr->src_node_id);
kref_put(&rt_entry->ref, ipc_router_release_rtentry);
+ kref_put(&rt_entry->xprt_info->ref, ipc_router_release_xprt_info_ref);
if (ret < 0)
IPC_RTR_ERR(
"%s: Send Resume_Tx Failed SRC_NODE: %d SRC_PORT: %d DEST_NODE: %d",
@@ -3939,6 +3967,49 @@ static void *ipc_router_get_log_ctx(char *sub_name)
return log_ctx;
}
+/**
+ * ipc_router_get_xprt_info_ref() - Get a reference to the xprt_info structure
+ * @xprt_info: pointer to the xprt_info.
+ *
+ * @return: Zero on success, -ENODEV on failure.
+ *
+ * This function is used to obtain a reference to the xprt_info structure
+ * corresponding to the requested @xprt_info pointer.
+ */
+static int ipc_router_get_xprt_info_ref(
+ struct msm_ipc_router_xprt_info *xprt_info)
+{
+ int ret = -ENODEV;
+ struct msm_ipc_router_xprt_info *tmp_xprt_info;
+
+ down_read(&xprt_info_list_lock_lha5);
+ list_for_each_entry(tmp_xprt_info, &xprt_info_list, list) {
+ if (tmp_xprt_info == xprt_info) {
+ kref_get(&xprt_info->ref);
+ ret = 0;
+ break;
+ }
+ }
+ up_read(&xprt_info_list_lock_lha5);
+
+ return ret;
+}
+
+/**
+ * ipc_router_release_xprt_info_ref() - release the xprt_info last reference
+ * @ref: Reference to the xprt_info structure.
+ *
+ * This function is called when all references to the xprt_info structure
+ * are released.
+ */
+static void ipc_router_release_xprt_info_ref(struct kref *ref)
+{
+ struct msm_ipc_router_xprt_info *xprt_info =
+ container_of(ref, struct msm_ipc_router_xprt_info, ref);
+
+ complete_all(&xprt_info->ref_complete);
+}
+
static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
{
struct msm_ipc_router_xprt_info *xprt_info;
@@ -3959,6 +4030,8 @@ static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
xprt_info->abort_data_read = 0;
INIT_WORK(&xprt_info->read_data, do_read_data);
INIT_LIST_HEAD(&xprt_info->list);
+ kref_init(&xprt_info->ref);
+ init_completion(&xprt_info->ref_complete);
xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
if (!xprt_info->workqueue) {
@@ -4022,6 +4095,10 @@ static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt)
wakeup_source_trash(&xprt_info->ws);
+ kref_put(&xprt_info->ref,
+ ipc_router_release_xprt_info_ref);
+ wait_for_completion(&xprt_info->ref_complete);
+
xprt->priv = 0;
kfree(xprt_info);
}