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

Commit c858082e authored by Bojun Pan's avatar Bojun Pan Committed by Gerrit - the friendly Code Review server
Browse files

msm: ipa: add NULL pointer check to ipa gsb



Add useful NULL pointer check on IPA GSB functions, add
spin/mutex interface locks to avoid race condition.

Change-Id: If5a7a304accf006ef320c65e851eea47978c38b4
Signed-off-by: default avatarBojun Pan <bojunp@codeaurora.org>
parent a515a311
Loading
Loading
Loading
Loading
+131 −38
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ struct ipa_gsb_iface_info {
 * struct ipa_gsb_context - GSB driver context information
 * @logbuf: buffer of ipc logging
 * @logbuf_low: buffer of ipc logging (low priority)
 * @lock: mutex lock
 * @lock: global mutex lock for global variables
 * @prod_hdl: handle for prod pipe
 * @cons_hdl: handle for cons pipe
 * @ipa_sys_desc_size: sys pipe desc size
@@ -141,6 +141,8 @@ struct ipa_gsb_iface_info {
 * @num_connected_iface: number of connected interface
 * @num_resumed_iface: number of resumed interface
 * @iface: interface information
 * @iface_lock: interface mutex lock for control path
 * @iface_spinlock: interface spinlock for data path
 * @pm_hdl: IPA PM handle
 */
struct ipa_gsb_context {
@@ -155,6 +157,8 @@ struct ipa_gsb_context {
	int num_connected_iface;
	int num_resumed_iface;
	struct ipa_gsb_iface_info *iface[MAX_SUPPORTED_IFACE];
	struct mutex iface_lock[MAX_SUPPORTED_IFACE];
	spinlock_t iface_spinlock[MAX_SUPPORTED_IFACE];
	u32 pm_hdl;
};

@@ -240,6 +244,7 @@ static void ipa_gsb_debugfs_destroy(void)

static int ipa_gsb_driver_init(struct odu_bridge_params *params)
{
	int i;
	if (!ipa_is_ready()) {
		IPA_GSB_ERR("IPA is not ready\n");
		return -EFAULT;
@@ -252,6 +257,10 @@ static int ipa_gsb_driver_init(struct odu_bridge_params *params)
		return -ENOMEM;

	mutex_init(&ipa_gsb_ctx->lock);
	for (i = 0; i < MAX_SUPPORTED_IFACE; i++) {
		mutex_init(&ipa_gsb_ctx->iface_lock[i]);
		spin_lock_init(&ipa_gsb_ctx->iface_spinlock[i]);
	}
	ipa_gsb_debugfs_init();

	return 0;
@@ -475,7 +484,7 @@ int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
	if (!params || !params->wakeup_request || !hdl ||
		!params->info.netdev_name || !params->info.tx_dp_notify ||
		!params->info.send_dl_skb) {
		IPA_GSB_ERR("NULL parameters\n");
		IPA_GSB_ERR("Invalid parameters\n");
		return -EINVAL;
	}

@@ -596,6 +605,7 @@ static void ipa_gsb_deregister_pm(void)

int ipa_bridge_cleanup(u32 hdl)
{
	int i;
	if (!ipa_gsb_ctx) {
		IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
		return -EFAULT;
@@ -606,39 +616,44 @@ int ipa_bridge_cleanup(u32 hdl)
		return -EINVAL;
	}

	if (ipa_gsb_ctx->iface[hdl] == NULL) {
		IPA_GSB_ERR("fail to find interface\n");
	mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	IPA_GSB_DBG("client hdl: %d\n", hdl);
	mutex_lock(&ipa_gsb_ctx->lock);

	if (ipa_gsb_ctx->iface[hdl]->is_connected) {
		IPA_GSB_ERR("cannot cleanup when iface is connected\n");
		mutex_unlock(&ipa_gsb_ctx->lock);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	ipa_gsb_dereg_intf_props(ipa_gsb_ctx->iface[hdl]);
	ipa_gsb_delete_partial_hdr(ipa_gsb_ctx->iface[hdl]);
	spin_lock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
	kfree(ipa_gsb_ctx->iface[hdl]);
	ipa_gsb_ctx->iface[hdl] = NULL;
	ipa_gsb_ctx->iface_hdl[hdl] = false;
	spin_unlock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
	mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
	mutex_lock(&ipa_gsb_ctx->lock);
	ipa_gsb_ctx->num_iface--;
	IPA_GSB_DBG("num_iface %d\n", ipa_gsb_ctx->num_iface);

	if (ipa_gsb_ctx->num_iface == 0) {
		ipa_gsb_deregister_pm();
		ipa_gsb_debugfs_destroy();
		ipc_log_context_destroy(ipa_gsb_ctx->logbuf);
		ipc_log_context_destroy(ipa_gsb_ctx->logbuf_low);
		mutex_unlock(&ipa_gsb_ctx->lock);
		mutex_destroy(&ipa_gsb_ctx->lock);
		for (i = 0; i < MAX_SUPPORTED_IFACE; i++)
			mutex_destroy(&ipa_gsb_ctx->iface_lock[i]);
		kfree(ipa_gsb_ctx);
		ipa_gsb_ctx = NULL;
		return 0;
	}

	mutex_unlock(&ipa_gsb_ctx->lock);
	return 0;
}
@@ -670,6 +685,10 @@ static void ipa_gsb_cons_cb(void *priv, enum ipa_dp_evt_type evt,
			(pkt_size + sizeof(*mux_hdr) +
			ETH_HLEN + IPA_GSB_SKB_DUMMY_HEADER);
		hdl = mux_hdr->iface_hdl;
		if (hdl >= MAX_SUPPORTED_IFACE) {
			IPA_GSB_ERR("invalid hdl: %d\n", hdl);
			break;
		}
		IPA_GSB_DBG_LOW("pkt_size: %d, pad_byte: %d, hdl: %d\n",
			pkt_size, pad_byte, hdl);

@@ -683,11 +702,21 @@ static void ipa_gsb_cons_cb(void *priv, enum ipa_dp_evt_type evt,
			break;
		}
		skb_trim(skb2, pkt_size + ETH_HLEN);
		spin_lock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
		if (ipa_gsb_ctx->iface[hdl] != NULL) {
			ipa_gsb_ctx->iface[hdl]->send_dl_skb(
				ipa_gsb_ctx->iface[hdl]->priv, skb2);
			ipa_gsb_ctx->iface[hdl]->iface_stats.num_dl_packets++;
			spin_unlock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
			skb_pull(skb, pkt_size + ETH_HLEN + pad_byte);
		} else {
			IPA_GSB_ERR("Invalid hdl: %d, drop the skb\n", hdl);
			spin_unlock_bh(&ipa_gsb_ctx->iface_spinlock[hdl]);
			dev_kfree_skb_any(skb2);
			break;
		}
	}

	if (skb) {
		dev_kfree_skb_any(skb);
		skb = NULL;
@@ -714,6 +743,11 @@ static void ipa_gsb_tx_dp_notify(void *priv, enum ipa_dp_evt_type evt,
	/* change to host order */
	*(u32 *)mux_hdr = ntohl(*(u32 *)mux_hdr);
	hdl = mux_hdr->iface_hdl;
	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("invalid hdl: %d and cb, drop the skb\n", hdl);
		dev_kfree_skb_any(skb);
		return;
	}
	IPA_GSB_DBG_LOW("evt: %d, hdl in tx_dp_notify: %d\n", evt, hdl);

	/* remove 4 byte mux header */
@@ -797,13 +831,23 @@ int ipa_bridge_connect(u32 hdl)
		return -EFAULT;
	}

	if (hdl >= MAX_SUPPORTED_IFACE) {
		IPA_GSB_ERR("invalid hdl: %d\n", hdl);
		return -EINVAL;
	}

	IPA_GSB_DBG("client hdl: %d\n", hdl);

	mutex_lock(&ipa_gsb_ctx->lock);
	mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	if (ipa_gsb_ctx->iface[hdl]->is_connected) {
		IPA_GSB_DBG("iface was already connected\n");
		mutex_unlock(&ipa_gsb_ctx->lock);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return 0;
	}

@@ -811,13 +855,13 @@ int ipa_bridge_connect(u32 hdl)
		ret = ipa_pm_activate_sync(ipa_gsb_ctx->pm_hdl);
		if (ret) {
			IPA_GSB_ERR("failed to activate ipa pm\n");
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return ret;
		}
		ret = ipa_gsb_connect_sys_pipe();
		if (ret) {
			IPA_GSB_ERR("fail to connect pipe\n");
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return ret;
		}
	}
@@ -833,7 +877,7 @@ int ipa_bridge_connect(u32 hdl)
	IPA_GSB_DBG("num resumed iface: %d\n",
		ipa_gsb_ctx->num_resumed_iface);

	mutex_unlock(&ipa_gsb_ctx->lock);
	mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
	return 0;
}
EXPORT_SYMBOL(ipa_bridge_connect);
@@ -871,13 +915,23 @@ int ipa_bridge_disconnect(u32 hdl)
		return -EFAULT;
	}

	if (hdl >= MAX_SUPPORTED_IFACE) {
		IPA_GSB_ERR("invalid hdl: %d\n", hdl);
		return -EINVAL;
	}

	IPA_GSB_DBG("client hdl: %d\n", hdl);

	mutex_lock(&ipa_gsb_ctx->lock);
	mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
		IPA_GSB_DBG("iface was not connected\n");
		mutex_unlock(&ipa_gsb_ctx->lock);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return 0;
	}

@@ -885,14 +939,14 @@ int ipa_bridge_disconnect(u32 hdl)
		ret = ipa_gsb_disconnect_sys_pipe();
		if (ret) {
			IPA_GSB_ERR("fail to discon pipes\n");
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return -EFAULT;
		}

		ret = ipa_pm_deactivate_sync(ipa_gsb_ctx->pm_hdl);
		if (ret) {
			IPA_GSB_ERR("failed to deactivate ipa pm\n");
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return -EFAULT;
		}
	}
@@ -910,7 +964,7 @@ int ipa_bridge_disconnect(u32 hdl)
			ipa_gsb_ctx->num_resumed_iface);
	}

	mutex_unlock(&ipa_gsb_ctx->lock);
	mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
	return 0;
}
EXPORT_SYMBOL(ipa_bridge_disconnect);
@@ -924,25 +978,37 @@ int ipa_bridge_resume(u32 hdl)
		return -EFAULT;
	}

	if (hdl >= MAX_SUPPORTED_IFACE) {
		IPA_GSB_ERR("invalid hdl: %d\n", hdl);
		return -EINVAL;
	}

	IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);

	mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
		IPA_GSB_ERR("iface is not connected\n");
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	if (ipa_gsb_ctx->iface[hdl]->is_resumed) {
		IPA_GSB_DBG_LOW("iface was already resumed\n");
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return 0;
	}

	mutex_lock(&ipa_gsb_ctx->lock);

	if (ipa_gsb_ctx->num_resumed_iface == 0) {
		ret = ipa_pm_activate_sync(ipa_gsb_ctx->pm_hdl);
		if (ret) {
			IPA_GSB_ERR("fail to activate ipa pm\n");
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return ret;
		}

@@ -952,7 +1018,7 @@ int ipa_bridge_resume(u32 hdl)
			IPA_GSB_ERR(
				"fail to start con ep %d\n",
				ret);
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return ret;
		}
	}
@@ -962,7 +1028,7 @@ int ipa_bridge_resume(u32 hdl)
	IPA_GSB_DBG_LOW("num resumed iface: %d\n",
		ipa_gsb_ctx->num_resumed_iface);

	mutex_unlock(&ipa_gsb_ctx->lock);
	mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
	return 0;
}
EXPORT_SYMBOL(ipa_bridge_resume);
@@ -976,20 +1042,32 @@ int ipa_bridge_suspend(u32 hdl)
		return -EFAULT;
	}

	if (hdl >= MAX_SUPPORTED_IFACE) {
		IPA_GSB_ERR("invalid hdl: %d\n", hdl);
		return -EINVAL;
	}

	IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);

	mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);
	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	if (!ipa_gsb_ctx->iface[hdl]->is_connected) {
		IPA_GSB_ERR("iface is not connected\n");
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return -EFAULT;
	}

	if (!ipa_gsb_ctx->iface[hdl]->is_resumed) {
		IPA_GSB_DBG_LOW("iface was already suspended\n");
		mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
		return 0;
	}

	mutex_lock(&ipa_gsb_ctx->lock);

	if (ipa_gsb_ctx->num_resumed_iface == 1) {
		ret = ipa_stop_gsi_channel(
			ipa_gsb_ctx->cons_hdl);
@@ -997,7 +1075,7 @@ int ipa_bridge_suspend(u32 hdl)
			IPA_GSB_ERR(
				"fail to stop cons ep %d\n",
				ret);
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return ret;
		}

@@ -1005,7 +1083,7 @@ int ipa_bridge_suspend(u32 hdl)
		if (ret) {
			IPA_GSB_ERR("fail to deactivate ipa pm\n");
			ipa_start_gsi_channel(ipa_gsb_ctx->cons_hdl);
			mutex_unlock(&ipa_gsb_ctx->lock);
			mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
			return ret;
		}
	}
@@ -1015,7 +1093,7 @@ int ipa_bridge_suspend(u32 hdl)
	IPA_GSB_DBG_LOW("num resumed iface: %d\n",
		ipa_gsb_ctx->num_resumed_iface);

	mutex_unlock(&ipa_gsb_ctx->lock);
	mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
	return 0;
}
EXPORT_SYMBOL(ipa_bridge_suspend);
@@ -1024,16 +1102,26 @@ int ipa_bridge_set_perf_profile(u32 hdl, u32 bandwidth)
{
	int ret;

	if (!ipa_gsb_ctx) {
		IPA_GSB_ERR("ipa_gsb_ctx was not initialized\n");
		return -EFAULT;
	}

	if (hdl >= MAX_SUPPORTED_IFACE) {
		IPA_GSB_ERR("invalid hdl: %d\n", hdl);
		return -EINVAL;
	}

	IPA_GSB_DBG("client hdl: %d, BW: %d\n", hdl, bandwidth);

	mutex_lock(&ipa_gsb_ctx->lock);
	mutex_lock(&ipa_gsb_ctx->iface_lock[hdl]);

	ret = ipa_pm_set_perf_profile(ipa_gsb_ctx->pm_hdl,
		bandwidth);
	if (ret)
		IPA_GSB_ERR("fail to set perf profile\n");

	mutex_unlock(&ipa_gsb_ctx->lock);
	mutex_unlock(&ipa_gsb_ctx->iface_lock[hdl]);
	return ret;
}
EXPORT_SYMBOL(ipa_bridge_set_perf_profile);
@@ -1047,6 +1135,11 @@ int ipa_bridge_tx_dp(u32 hdl, struct sk_buff *skb,

	IPA_GSB_DBG_LOW("client hdl: %d\n", hdl);

	if (!ipa_gsb_ctx->iface[hdl]) {
		IPA_GSB_ERR("fail to find interface, hdl: %d\n", hdl);
		return -EFAULT;
	}

	/* make sure skb has enough headroom */
	if (unlikely(skb_headroom(skb) < sizeof(struct ipa_gsb_mux_hdr))) {
		IPA_GSB_DBG_LOW("skb doesn't have enough headroom\n");