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

Commit b9366b85 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge changes I741aafa9,I5b29536f into msm-next

* changes:
  rpmsg: glink: Use the local intents when receiving data
  rpmsg: glink: Add support for TX intents
parents b9cf4c79 69b238a7
Loading
Loading
Loading
Loading
+216 −26
Original line number Diff line number Diff line
@@ -59,6 +59,26 @@ struct glink_defer_cmd {
	u8 data[];
};

/**
 * struct glink_core_rx_intent - RX intent
 * RX intent
 *
 * data: pointer to the data (may be NULL for zero-copy)
 * id: remote or local intent ID
 * size: size of the original intent (do not modify)
 * reuse: To mark if the intent can be reused after first use
 * in_use: To mark if intent is already in use for the channel
 * offset: next write offset (initially 0)
 */
struct glink_core_rx_intent {
	void *data;
	u32 id;
	size_t size;
	bool reuse;
	bool in_use;
	u32 offset;
};

/**
 * struct qcom_glink - driver context, relates to one remote subsystem
 * @dev:	reference to the associated struct device
@@ -117,6 +137,8 @@ enum {
 * @name:	unique channel name/identifier
 * @lcid:	channel id, in local space
 * @rcid:	channel id, in remote space
 * @intent_lock: lock for protection of @liids
 * @liids:	idr of all local intents
 * @buf:	receive buffer, for gathering fragments
 * @buf_offset:	write offset in @buf
 * @buf_size:	size of current @buf
@@ -137,7 +159,10 @@ struct glink_channel {
	unsigned int lcid;
	unsigned int rcid;

	void *buf;
	spinlock_t intent_lock;
	struct idr liids;

	struct glink_core_rx_intent *buf;
	int buf_offset;
	int buf_size;

@@ -154,6 +179,9 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops;
#define RPM_CMD_OPEN			2
#define RPM_CMD_CLOSE			3
#define RPM_CMD_OPEN_ACK		4
#define RPM_CMD_INTENT			5
#define RPM_CMD_RX_INTENT_REQ		7
#define RPM_CMD_RX_INTENT_REQ_ACK	8
#define RPM_CMD_TX_DATA			9
#define RPM_CMD_CLOSE_ACK		11
#define RPM_CMD_TX_DATA_CONT		12
@@ -172,12 +200,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,

	/* Setup glink internal glink_channel data */
	spin_lock_init(&channel->recv_lock);
	spin_lock_init(&channel->intent_lock);
	channel->glink = glink;
	channel->name = kstrdup(name, GFP_KERNEL);

	init_completion(&channel->open_req);
	init_completion(&channel->open_ack);

	idr_init(&channel->liids);
	kref_init(&channel->refcount);

	return channel;
@@ -187,6 +217,11 @@ static void qcom_glink_channel_release(struct kref *ref)
{
	struct glink_channel *channel = container_of(ref, struct glink_channel,
						     refcount);
	unsigned long flags;

	spin_lock_irqsave(&channel->intent_lock, flags);
	idr_destroy(&channel->liids);
	spin_unlock_irqrestore(&channel->intent_lock, flags);

	kfree(channel->name);
	kfree(channel);
@@ -424,6 +459,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink,
	}
}

/**
 * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to
				      wire format and transmit
 * @glink:	The transport to transmit on.
 * @channel:	The glink channel
 * @granted:	The request response to encode.
 *
 * Return: 0 on success or standard Linux error code.
 */
static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink,
					  struct glink_channel *channel,
					  bool granted)
{
	struct glink_msg msg;

	msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK);
	msg.param1 = cpu_to_le16(channel->lcid);
	msg.param2 = cpu_to_le32(granted);

	qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);

	return 0;
}

/**
 * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and
 *			   transmit
 * @glink:	The transport to transmit on.
 * @channel:	The local channel
 * @size:	The intent to pass on to remote.
 *
 * Return: 0 on success or standard Linux error code.
 */
static int qcom_glink_advertise_intent(struct qcom_glink *glink,
				       struct glink_channel *channel,
				       struct glink_core_rx_intent *intent)
{
	struct command {
		u16 id;
		u16 lcid;
		u32 count;
		u32 size;
		u32 liid;
	} __packed;
	struct command cmd;

	cmd.id = cpu_to_le16(RPM_CMD_INTENT);
	cmd.lcid = cpu_to_le16(channel->lcid);
	cmd.count = cpu_to_le32(1);
	cmd.size = cpu_to_le32(intent->size);
	cmd.liid = cpu_to_le32(intent->id);

	qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);

	return 0;
}

static struct glink_core_rx_intent *
qcom_glink_alloc_intent(struct qcom_glink *glink,
			struct glink_channel *channel,
			size_t size,
			bool reuseable)
{
	struct glink_core_rx_intent *intent;
	int ret;
	unsigned long flags;

	intent = kzalloc(sizeof(*intent), GFP_KERNEL);

	if (!intent)
		return NULL;

	intent->data = kzalloc(size, GFP_KERNEL);
	if (!intent->data)
		return NULL;

	spin_lock_irqsave(&channel->intent_lock, flags);
	ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC);
	if (ret < 0) {
		spin_unlock_irqrestore(&channel->intent_lock, flags);
		return NULL;
	}
	spin_unlock_irqrestore(&channel->intent_lock, flags);

	intent->id = ret;
	intent->size = size;
	intent->reuse = reuseable;

	return intent;
}

/**
 * qcom_glink_handle_intent_req() - Receive a request for rx_intent
 *					    from remote side
 * if_ptr:      Pointer to the transport interface
 * rcid:	Remote channel ID
 * size:	size of the intent
 *
 * The function searches for the local channel to which the request for
 * rx_intent has arrived and allocates and notifies the remote back
 */
static void qcom_glink_handle_intent_req(struct qcom_glink *glink,
					 u32 cid, size_t size)
{
	struct glink_core_rx_intent *intent;
	struct glink_channel *channel;
	unsigned long flags;

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

	if (!channel) {
		pr_err("%s channel not found for cid %d\n", __func__, cid);
		return;
	}

	intent = qcom_glink_alloc_intent(glink, channel, size, false);
	if (intent)
		qcom_glink_advertise_intent(glink, channel, intent);

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

static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
{
	struct glink_defer_cmd *dcmd;
@@ -455,6 +614,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)
{
	struct glink_core_rx_intent *intent;
	struct glink_channel *channel;
	struct {
		struct glink_msg msg;
@@ -464,6 +624,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
	unsigned int chunk_size;
	unsigned int left_size;
	unsigned int rcid;
	unsigned int liid;
	int ret = 0;
	unsigned long flags;

	if (avail < sizeof(hdr)) {
@@ -491,56 +653,78 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
		dev_dbg(glink->dev, "Data on non-existing channel\n");

		/* Drop the message */
		qcom_glink_rx_advance(glink,
				      ALIGN(sizeof(hdr) + chunk_size, 8));
		return 0;
		goto advance_rx;
	}

	if (glink->intentless) {
		/* Might have an ongoing, fragmented, message to append */
		if (!channel->buf) {
		channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC);
		if (!channel->buf)
			intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
			if (!intent)
				return -ENOMEM;

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

	qcom_glink_rx_advance(glink, sizeof(hdr));
			intent->id = 0xbabababa;
			intent->size = chunk_size + left_size;
			intent->offset = 0;

	if (channel->buf_size - channel->buf_offset < chunk_size) {
		dev_err(glink->dev, "Insufficient space in input buffer\n");
			channel->buf = intent;
		} else {
			intent = channel->buf;
		}
	} else {
		liid = le32_to_cpu(hdr.msg.param2);

		spin_lock_irqsave(&channel->intent_lock, flags);
		intent = idr_find(&channel->liids, liid);
		spin_unlock_irqrestore(&channel->intent_lock, flags);

		if (!intent) {
			dev_err(glink->dev,
				"no intent found for channel %s intent %d",
				channel->name, liid);
			goto advance_rx;
		}
	}

	if (intent->size - intent->offset < chunk_size) {
		dev_err(glink->dev, "Insufficient space in intent\n");

		/* The packet header lied, drop payload */
		qcom_glink_rx_advance(glink, chunk_size);
		return -ENOMEM;
		goto advance_rx;
	}

	qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset,
	qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8));
	qcom_glink_rx_peak(glink, intent->data + intent->offset,
			   chunk_size);
	channel->buf_offset += chunk_size;
	intent->offset += chunk_size;

	/* Handle message when no fragments remain to be received */
	if (!left_size) {
		spin_lock(&channel->recv_lock);
		if (channel->ept.cb) {
			channel->ept.cb(channel->ept.rpdev,
					channel->buf,
					channel->buf_offset,
					intent->data,
					intent->offset,
					channel->ept.priv,
					RPMSG_ADDR_ANY);
		}
		spin_unlock(&channel->recv_lock);

		kfree(channel->buf);
		intent->offset = 0;
		channel->buf = NULL;
		channel->buf_size = 0;
	}

	/* Each message starts at 8 byte aligned address */
advance_rx:
	qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8));

	return 0;
	return ret;
}

static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
@@ -586,6 +770,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)
		case RPM_CMD_VERSION_ACK:
		case RPM_CMD_CLOSE:
		case RPM_CMD_CLOSE_ACK:
		case RPM_CMD_RX_INTENT_REQ:
			ret = qcom_glink_rx_defer(glink, 0);
			break;
		case RPM_CMD_OPEN_ACK:
@@ -1003,6 +1188,9 @@ static void qcom_glink_work(struct work_struct *work)
		case RPM_CMD_CLOSE_ACK:
			qcom_glink_rx_close_ack(glink, param1);
			break;
		case RPM_CMD_RX_INTENT_REQ:
			qcom_glink_handle_intent_req(glink, param1, param2);
			break;
		default:
			WARN(1, "Unknown defer object %d\n", cmd);
			break;
@@ -1015,7 +1203,8 @@ static void qcom_glink_work(struct work_struct *work)
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
					   unsigned long features,
					   struct qcom_glink_pipe *rx,
					   struct qcom_glink_pipe *tx)
					   struct qcom_glink_pipe *tx,
					   bool intentless)
{
	int irq;
	int ret;
@@ -1030,6 +1219,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
	glink->rx_pipe = rx;

	glink->features = features;
	glink->intentless = intentless;

	mutex_init(&glink->tx_lock);
	spin_lock_init(&glink->rx_lock);
+2 −1
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ struct qcom_glink;
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
					   unsigned long features,
					   struct qcom_glink_pipe *rx,
					   struct qcom_glink_pipe *tx);
					   struct qcom_glink_pipe *tx,
					   bool intentless);
void qcom_glink_native_remove(struct qcom_glink *glink);

void qcom_glink_native_unregister(struct qcom_glink *glink);
+2 −1
Original line number Diff line number Diff line
@@ -305,7 +305,8 @@ static int glink_rpm_probe(struct platform_device *pdev)
	glink = qcom_glink_native_probe(&pdev->dev,
					0,
					&rx_pipe->native,
					&tx_pipe->native);
					&tx_pipe->native,
					true);
	if (IS_ERR(glink))
		return PTR_ERR(glink);

+3 −2
Original line number Diff line number Diff line
@@ -284,8 +284,9 @@ struct qcom_glink *qcom_glink_smem_register(struct device *parent,
	*tx_pipe->head = 0;

	glink = qcom_glink_native_probe(dev,
					GLINK_FEATURE_TRACER_PKT,
					&rx_pipe->native, &tx_pipe->native);
					GLINK_FEATURE_INTENT_REUSE,
					&rx_pipe->native, &tx_pipe->native,
					false);
	if (IS_ERR(glink)) {
		ret = PTR_ERR(glink);
		goto err_put_dev;