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

Commit a9ffd259 authored by Pranav Mahesh Phansalkar's avatar Pranav Mahesh Phansalkar
Browse files

rpmsg: glink: Get reference of channel objects in rx path



Get channel references in data receive path as channel might get freed
while processing commands received from remote processor.

This ensures channel context is not freed before its usage is complete.

Change-Id: I7d9a98e34c21ae0d277456853a755dab8d105d5f
Signed-off-by: default avatarPranav Mahesh Phansalkar <quic_pphansal@quicinc.com>
parent 75db4733
Loading
Loading
Loading
Loading
+57 −28
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@
/*
/*
 * Copyright (c) 2016-2017, Linaro Ltd
 * Copyright (c) 2016-2017, Linaro Ltd
 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
 * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
 */
 */


#include <linux/idr.h>
#include <linux/idr.h>
@@ -336,6 +337,38 @@ static void qcom_glink_channel_release(struct kref *ref)
	kfree(channel);
	kfree(channel);
}
}


static struct glink_channel *qcom_glink_channel_ref_get(
						struct qcom_glink *glink,
						bool remote_channel, int cid)
{
	struct glink_channel *channel = NULL;
	unsigned long flags;

	if (!glink)
		return NULL;

	spin_lock_irqsave(&glink->idr_lock, flags);
	if (remote_channel)
		channel = idr_find(&glink->rcids, cid);
	else
		channel = idr_find(&glink->lcids, cid);

	if (channel)
		kref_get(&channel->refcount);

	spin_unlock_irqrestore(&glink->idr_lock, flags);
	return channel;
}

static void qcom_glink_channel_ref_put(struct glink_channel *channel)
{

	if (!channel)
		return;

	kref_put(&channel->refcount, qcom_glink_channel_release);
}

static size_t qcom_glink_rx_avail(struct qcom_glink *glink)
static size_t qcom_glink_rx_avail(struct qcom_glink *glink)
{
{
	return glink->rx_pipe->avail(glink->rx_pipe);
	return glink->rx_pipe->avail(glink->rx_pipe);
@@ -487,11 +520,8 @@ static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink,
					     unsigned int cid, bool granted)
					     unsigned int cid, bool granted)
{
{
	struct glink_channel *channel;
	struct glink_channel *channel;
	unsigned long flags;


	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = qcom_glink_channel_ref_get(glink, true, cid);
	channel = idr_find(&glink->rcids, cid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (!channel) {
	if (!channel) {
		dev_err(glink->dev, "unable to find channel\n");
		dev_err(glink->dev, "unable to find channel\n");
		return;
		return;
@@ -501,6 +531,7 @@ static void qcom_glink_handle_intent_req_ack(struct qcom_glink *glink,
	atomic_inc(&channel->intent_req_comp);
	atomic_inc(&channel->intent_req_comp);
	wake_up(&channel->intent_req_event);
	wake_up(&channel->intent_req_event);
	CH_INFO(channel, "\n");
	CH_INFO(channel, "\n");
	qcom_glink_channel_ref_put(channel);
}
}


/**
/**
@@ -849,9 +880,7 @@ static void qcom_glink_handle_rx_done(struct qcom_glink *glink,
	struct glink_channel *channel;
	struct glink_channel *channel;
	unsigned long flags;
	unsigned long flags;


	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = qcom_glink_channel_ref_get(glink, true, cid);
	channel = idr_find(&glink->rcids, cid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (!channel) {
	if (!channel) {
		dev_err(glink->dev, "invalid channel id received\n");
		dev_err(glink->dev, "invalid channel id received\n");
		return;
		return;
@@ -863,6 +892,7 @@ static void qcom_glink_handle_rx_done(struct qcom_glink *glink,
	if (!intent) {
	if (!intent) {
		spin_unlock_irqrestore(&channel->intent_lock, flags);
		spin_unlock_irqrestore(&channel->intent_lock, flags);
		dev_err(glink->dev, "invalid intent id received\n");
		dev_err(glink->dev, "invalid intent id received\n");
		qcom_glink_channel_ref_put(channel);
		return;
		return;
	}
	}


@@ -874,6 +904,7 @@ static void qcom_glink_handle_rx_done(struct qcom_glink *glink,
		kfree(intent);
		kfree(intent);
	}
	}
	spin_unlock_irqrestore(&channel->intent_lock, flags);
	spin_unlock_irqrestore(&channel->intent_lock, flags);
	qcom_glink_channel_ref_put(channel);
}
}


/**
/**
@@ -896,9 +927,7 @@ static void qcom_glink_handle_intent_req(struct qcom_glink *glink,
	unsigned long flags;
	unsigned long flags;
	int iid;
	int iid;


	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = qcom_glink_channel_ref_get(glink, true, cid);
	channel = idr_find(&glink->rcids, cid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);


	if (!channel) {
	if (!channel) {
		pr_err("%s channel not found for cid %d\n", __func__, cid);
		pr_err("%s channel not found for cid %d\n", __func__, cid);
@@ -915,6 +944,7 @@ static void qcom_glink_handle_intent_req(struct qcom_glink *glink,
	spin_unlock_irqrestore(&channel->intent_lock, flags);
	spin_unlock_irqrestore(&channel->intent_lock, flags);
	if (intent) {
	if (intent) {
		qcom_glink_send_intent_req_ack(glink, channel, !!intent);
		qcom_glink_send_intent_req_ack(glink, channel, !!intent);
		qcom_glink_channel_ref_put(channel);
		return;
		return;
	}
	}


@@ -924,6 +954,7 @@ static void qcom_glink_handle_intent_req(struct qcom_glink *glink,
		qcom_glink_advertise_intent(glink, channel, intent);
		qcom_glink_advertise_intent(glink, channel, intent);


	qcom_glink_send_intent_req_ack(glink, channel, !!intent);
	qcom_glink_send_intent_req_ack(glink, channel, !!intent);
	qcom_glink_channel_ref_put(channel);
}
}


static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
@@ -958,7 +989,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
{
{
	struct glink_core_rx_intent *intent;
	struct glink_core_rx_intent *intent;
	struct glink_channel *channel;
	struct glink_channel *channel = NULL;
	struct {
	struct {
		struct glink_msg msg;
		struct glink_msg msg;
		__le32 chunk_size;
		__le32 chunk_size;
@@ -986,9 +1017,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
	}
	}


	rcid = le16_to_cpu(hdr.msg.param1);
	rcid = le16_to_cpu(hdr.msg.param1);
	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = qcom_glink_channel_ref_get(glink, true, rcid);
	channel = idr_find(&glink->rcids, rcid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (!channel) {
	if (!channel) {
		dev_dbg(glink->dev, "Data on non-existing channel\n");
		dev_dbg(glink->dev, "Data on non-existing channel\n");


@@ -1009,13 +1038,16 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
		/* Might have an ongoing, fragmented, message to append */
		/* Might have an ongoing, fragmented, message to append */
		if (!channel->buf) {
		if (!channel->buf) {
			intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
			intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
			if (!intent)
			if (!intent) {
				qcom_glink_channel_ref_put(channel);
				return -ENOMEM;
				return -ENOMEM;
			}


			intent->data = kmalloc(chunk_size + left_size,
			intent->data = kmalloc(chunk_size + left_size,
					       GFP_ATOMIC);
					       GFP_ATOMIC);
			if (!intent->data) {
			if (!intent->data) {
				kfree(intent);
				kfree(intent);
				qcom_glink_channel_ref_put(channel);
				return -ENOMEM;
				return -ENOMEM;
			}
			}


@@ -1081,7 +1113,7 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)


advance_rx:
advance_rx:
	qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8));
	qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr) + chunk_size, 8));

	qcom_glink_channel_ref_put(channel);
	return ret;
	return ret;
}
}


@@ -1112,17 +1144,17 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink,
		return;
		return;
	}
	}


	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = qcom_glink_channel_ref_get(glink, true, cid);
	channel = idr_find(&glink->rcids, cid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (!channel) {
	if (!channel) {
		dev_err(glink->dev, "intents for non-existing channel\n");
		dev_err(glink->dev, "intents for non-existing channel\n");
		return;
		return;
	}
	}


	msg = kmalloc(msglen, GFP_ATOMIC);
	msg = kmalloc(msglen, GFP_ATOMIC);
	if (!msg)
	if (!msg) {
		qcom_glink_channel_ref_put(channel);
		return;
		return;
	}


	qcom_glink_rx_peak(glink, msg, 0, msglen);
	qcom_glink_rx_peak(glink, msg, 0, msglen);


@@ -1149,15 +1181,14 @@ static void qcom_glink_handle_intent(struct qcom_glink *glink,


	kfree(msg);
	kfree(msg);
	qcom_glink_rx_advance(glink, ALIGN(msglen, 8));
	qcom_glink_rx_advance(glink, ALIGN(msglen, 8));
	qcom_glink_channel_ref_put(channel);
}
}


static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
{
{
	struct glink_channel *channel;
	struct glink_channel *channel;


	spin_lock(&glink->idr_lock);
	channel = qcom_glink_channel_ref_get(glink, false, lcid);
	channel = idr_find(&glink->lcids, lcid);
	spin_unlock(&glink->idr_lock);
	if (!channel) {
	if (!channel) {
		dev_err(glink->dev, "Invalid open ack packet\n");
		dev_err(glink->dev, "Invalid open ack packet\n");
		return -EINVAL;
		return -EINVAL;
@@ -1165,7 +1196,7 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)


	CH_INFO(channel, "\n");
	CH_INFO(channel, "\n");
	complete_all(&channel->open_ack);
	complete_all(&channel->open_ack);

	qcom_glink_channel_ref_put(channel);
	return 0;
	return 0;
}
}


@@ -1195,12 +1226,9 @@ static int qcom_glink_handle_signals(struct qcom_glink *glink,
				     unsigned int rcid, unsigned int signals)
				     unsigned int rcid, unsigned int signals)
{
{
	struct glink_channel *channel;
	struct glink_channel *channel;
	unsigned long flags;
	u32 old;
	u32 old;


	spin_lock_irqsave(&glink->idr_lock, flags);
	channel = qcom_glink_channel_ref_get(glink, true, rcid);
	channel = idr_find(&glink->rcids, rcid);
	spin_unlock_irqrestore(&glink->idr_lock, flags);
	if (!channel) {
	if (!channel) {
		dev_err(glink->dev, "signal for non-existing channel\n");
		dev_err(glink->dev, "signal for non-existing channel\n");
		return -EINVAL;
		return -EINVAL;
@@ -1214,6 +1242,7 @@ static int qcom_glink_handle_signals(struct qcom_glink *glink,


	CH_INFO(channel, "old:%d new:%d\n", old, channel->rsigs);
	CH_INFO(channel, "old:%d new:%d\n", old, channel->rsigs);


	qcom_glink_channel_ref_put(channel);
	return 0;
	return 0;
}
}