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

Commit affb425a authored by Atish Kumar Patra's avatar Atish Kumar Patra
Browse files

soc: qcom: ipc_router_mhi_xprt: Transmit packets asynchronously



Add support to maintain a local mapping of the dma address to virtual
address and manage the buffers during the message exchanges. This enables
IPC Router MHI transport to exchange messages in an asynchronous manner
and improve the performance.

Change-Id: I60415121a54056b347469e6747dbae2028427dfa
Signed-off-by: default avatarAtish Kumar Patra <apatra@codeaurora.org>
parent 50e0173f
Loading
Loading
Loading
Loading
+160 −16
Original line number Diff line number Diff line
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -38,6 +38,22 @@ if (ipc_router_mhi_xprt_debug_mask) \
#define IPC_ROUTER_MHI_XPRT_MAX_PKT_SIZE 0x1000
#define IPC_ROUTER_MHI_XPRT_NUM_TRBS 10

/**
 * ipc_router_mhi_addr_map - DMA to virtual address mapping for an IPC Router
 *				packet.
 * @list_node: Address mapping list node used by mhi transport map list.
 * @virt_addr: The virtual address in mapping.
 * @dma_addr: The dma address in mapping.
 * @pkt: The IPC Router packet for which the virtual address of skbs are mapped
 *	to DMA address during TX/RX operations.
 */
struct ipc_router_mhi_addr_map {
	struct list_head list_node;
	void *virt_addr;
	dma_addr_t dma_addr;
	struct rr_packet *pkt;
};

/**
 * ipc_router_mhi_channel - MHI Channel related information
 * @out_chan_id: Out channel ID for use by IPC ROUTER enumerated in MHI driver.
@@ -49,7 +65,6 @@ if (ipc_router_mhi_xprt_debug_mask) \
 * @state_lock: Lock to protect access to the state information.
 * @out_chan_enabled: State of the outgoing channel.
 * @in_chan_enabled: State of the incoming channel.
 * @bytes_to_tx: Remaining bytes to be transmitted in a packet.
 * @bytes_to_rx: Remaining bytes to be received in a packet.
 * @in_skbq_lock: Lock to protect access to the input skbs queue.
 * @in_skbq: Queue containing the input buffers.
@@ -69,7 +84,6 @@ struct ipc_router_mhi_channel {
	struct mutex state_lock;
	bool out_chan_enabled;
	bool in_chan_enabled;
	int bytes_to_tx;
	int bytes_to_rx;

	struct mutex in_skbq_lock;
@@ -93,6 +107,12 @@ struct ipc_router_mhi_channel {
 *			by IPC Router.
 * @xprt_version: IPC Router header version supported by this XPRT.
 * @xprt_option: XPRT specific options to be handled by IPC Router.
 * @tx_addr_map_list_lock: The lock to protect the address mapping list for TX
 *			operations.
 * @tx_addr_map_list: DMA to virtual address mapping list for TX operations.
 * @rx_addr_map_list_lock: The lock to protect the address mapping list for RX
 *			operations.
 * @rx_addr_map_list: DMA to virtual address mapping list for RX operations.
 */
struct ipc_router_mhi_xprt {
	struct list_head list;
@@ -106,6 +126,10 @@ struct ipc_router_mhi_xprt {
	struct completion sft_close_complete;
	unsigned xprt_version;
	unsigned xprt_option;
	struct mutex tx_addr_map_list_lock;
	struct list_head tx_addr_map_list;
	struct mutex rx_addr_map_list_lock;
	struct list_head rx_addr_map_list;
};

struct ipc_router_mhi_xprt_work {
@@ -138,6 +162,90 @@ struct ipc_router_mhi_xprt_config {
static DEFINE_MUTEX(mhi_xprt_list_lock_lha1);
static LIST_HEAD(mhi_xprt_list);

/*
 * ipc_router_mhi_release_pkt() - Release a cloned IPC Router packet
 * @ref: Reference to the kref object in the IPC Router packet.
 */
void ipc_router_mhi_release_pkt(struct kref *ref)
{
	struct rr_packet *pkt = container_of(ref, struct rr_packet, ref);

	release_pkt(pkt);
}

/*
 * ipc_router_mhi_xprt_find_addr_map() - Search the mapped virtual address
 * @addr_map_list: The list of address mappings.
 * @addr_map_list_lock: Reference to the lock that protects the @addr_map_list.
 * @addr: The dma address for which mapped virtual address need to be found.
 *
 * Return: The mapped virtual Address if found, NULL otherwise.
 */
void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list,
				struct mutex *addr_map_list_lock,
				dma_addr_t addr)
{
	struct ipc_router_mhi_addr_map *addr_mapping;
	struct ipc_router_mhi_addr_map *tmp_addr_mapping;
	void *virt_addr;

	if (!addr_map_list || !addr_map_list_lock)
		return NULL;
	mutex_lock(addr_map_list_lock);
	list_for_each_entry_safe(addr_mapping, tmp_addr_mapping,
				addr_map_list, list_node) {
		if (addr_mapping->dma_addr == addr) {
			virt_addr = addr_mapping->virt_addr;
			list_del(&addr_mapping->list_node);
			if (addr_mapping->pkt)
				kref_put(&addr_mapping->pkt->ref,
					ipc_router_mhi_release_pkt);
			kfree(addr_mapping);
			mutex_unlock(addr_map_list_lock);
			return virt_addr;
		}
	}
	mutex_unlock(addr_map_list_lock);
	IPC_RTR_ERR(
		"%s: Virtual address mapping for DMA addr [%p] not found\n",
		__func__, (void *)addr);
	return NULL;
}

/*
 * ipc_router_mhi_xprt_add_addr_map() - Add a mapping of virtual address to dma
 *					address
 * @addr_map_list: The list of address mappings.
 * @addr_map_list_lock: Reference to the lock that protects the @addr_map_list.
 * @pkt: The IPC Router packet that contains the virtual address in skbs.
 * @addr: The virtual address which needs to be added.
 * @dma_addr: The dma address which needs to be added.
 *
 * Return: 0 on success, standard Linux error code otherwise.
 */
int ipc_router_mhi_xprt_add_addr_map(struct list_head *addr_map_list,
				struct mutex *addr_map_list_lock,
				struct rr_packet *pkt, void *virt_addr,
				dma_addr_t dma_addr)
{
	struct ipc_router_mhi_addr_map *addr_mapping;

	if (!addr_map_list || !addr_map_list_lock)
		return -EINVAL;
	addr_mapping = kmalloc(sizeof(*addr_mapping), GFP_KERNEL);
	if (!addr_mapping)
		return -ENOMEM;
	addr_mapping->virt_addr = virt_addr;
	addr_mapping->dma_addr = dma_addr;
	addr_mapping->pkt = pkt;
	mutex_lock(addr_map_list_lock);
	if (addr_mapping->pkt)
		kref_get(&addr_mapping->pkt->ref);
	list_add_tail(&addr_mapping->list_node, addr_map_list);
	mutex_unlock(addr_map_list_lock);
	return 0;
}

/*
 * mhi_xprt_queue_in_buffers() - Queue input buffers
 * @mhi_xprtp: MHI XPRT in which the input buffer has to be queued.
@@ -169,6 +277,14 @@ int mhi_xprt_queue_in_buffers(struct ipc_router_mhi_xprt *mhi_xprtp,
			kfree_skb(skb);
			break;
		}
		if (ipc_router_mhi_xprt_add_addr_map(
					&mhi_xprtp->rx_addr_map_list,
					&mhi_xprtp->rx_addr_map_list_lock, NULL,
					skb->data, dma_addr) < 0) {
			IPC_RTR_ERR("%s: Could not map %d SKB->DMA address\n",
					__func__, (i + 1));
			break;
		}
		mutex_lock(&mhi_xprtp->ch_hndl.in_skbq_lock);
		rc_val = mhi_queue_xfer(mhi_xprtp->ch_hndl.in_handle,
					 dma_addr, buf_size, MHI_EOT);
@@ -251,7 +367,7 @@ static int ipc_router_mhi_write_avail(struct msm_ipc_router_xprt *xprt)
 *          standard Linux error codes on failure.
 */
static int ipc_router_mhi_write_skb(struct ipc_router_mhi_xprt *mhi_xprtp,
				    struct sk_buff *skb)
				    struct sk_buff *skb, struct rr_packet *pkt)
{
	size_t sz_to_write = 0;
	size_t offset = 0;
@@ -276,22 +392,30 @@ static int ipc_router_mhi_write_skb(struct ipc_router_mhi_xprt *mhi_xprtp,
					  sz_to_write, DMA_TO_DEVICE);
		if (dma_mapping_error(NULL, dma_addr)) {
			mutex_unlock(&mhi_xprtp->ch_hndl.state_lock);
			IPC_RTR_ERR("%s: Failed to map DMA 0x%x\n",
			IPC_RTR_ERR("%s: Failed to map DMA 0x%zx\n",
				    __func__, sz_to_write);
			return -ENOMEM;
		}
		if (ipc_router_mhi_xprt_add_addr_map(
					&mhi_xprtp->tx_addr_map_list,
					&mhi_xprtp->tx_addr_map_list_lock, pkt,
					skb->data + offset, dma_addr) < 0) {
			IPC_RTR_ERR("%s: Could not map SKB->DMA address\n",
					__func__);
			break;
		}

		rc = mhi_queue_xfer(mhi_xprtp->ch_hndl.out_handle,
				     dma_addr, sz_to_write, MHI_EOT | MHI_EOB);
		if (rc != 0) {
			mutex_unlock(&mhi_xprtp->ch_hndl.state_lock);
			dma_unmap_single(NULL, dma_addr, sz_to_write,
					 DMA_TO_DEVICE);
			IPC_RTR_ERR("%s: Error queueing mhi_xfer 0x%x\n",
			IPC_RTR_ERR("%s: Error queueing mhi_xfer 0x%zx\n",
				    __func__, sz_to_write);
			return -EFAULT;
		} else {
			offset += sz_to_write;
			mhi_xprtp->ch_hndl.bytes_to_tx += sz_to_write;
		}
		mutex_unlock(&mhi_xprtp->ch_hndl.state_lock);
	}
@@ -311,6 +435,7 @@ static int ipc_router_mhi_write(void *data,
{
	struct rr_packet *pkt = (struct rr_packet *)data;
	struct sk_buff *ipc_rtr_pkt;
	struct rr_packet *cloned_pkt;
	int rc;
	struct ipc_router_mhi_xprt *mhi_xprtp =
		container_of(xprt, struct ipc_router_mhi_xprt, xprt);
@@ -321,9 +446,15 @@ static int ipc_router_mhi_write(void *data,
	if (!len || pkt->length != len)
		return -EINVAL;

	cloned_pkt = clone_pkt(pkt);
	if (!cloned_pkt) {
		pr_err("%s: Error in cloning packet while tx\n", __func__);
		return -ENOMEM;
	}
	D("%s: Ready to write %d bytes\n", __func__, len);
	skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
		rc = ipc_router_mhi_write_skb(mhi_xprtp, ipc_rtr_pkt);
	skb_queue_walk(cloned_pkt->pkt_fragment_q, ipc_rtr_pkt) {
		rc = ipc_router_mhi_write_skb(mhi_xprtp, ipc_rtr_pkt,
						cloned_pkt);
		if (rc < 0) {
			IPC_RTR_ERR("%s: Error writing SKB %d\n",
				    __func__, rc);
@@ -331,7 +462,7 @@ static int ipc_router_mhi_write(void *data,
		}
	}

	wait_event(mhi_xprtp->write_wait_q, !mhi_xprtp->ch_hndl.bytes_to_tx);
	kref_put(&cloned_pkt->ref, ipc_router_mhi_release_pkt);
	if (rc < 0)
		return rc;
	else
@@ -375,7 +506,7 @@ static void mhi_xprt_read_data(struct work_struct *work)
		rc = mhi_poll_inbound(mhi_xprtp->ch_hndl.in_handle, &result);
		if (rc || !result.payload_buf || !result.bytes_xferd) {
			if (rc != MHI_STATUS_RING_EMPTY)
				IPC_RTR_ERR("%s: Poll failed %s:%d:%p:%zu\n",
				IPC_RTR_ERR("%s: Poll failed %s:%d:%p:%u\n",
					__func__, mhi_xprtp->xprt_name, rc,
					(void *)result.payload_buf,
					result.bytes_xferd);
@@ -395,8 +526,14 @@ static void mhi_xprt_read_data(struct work_struct *work)
			D("%s: Allocated rr_packet\n", __func__);
		}

		skb_data = dma_to_virt(NULL, data_addr);
		skb_data = ipc_router_mhi_xprt_find_addr_map(
					&mhi_xprtp->rx_addr_map_list,
					&mhi_xprtp->rx_addr_map_list_lock,
					data_addr);

		dma_unmap_single(NULL, data_addr, data_sz, DMA_BIDIRECTIONAL);
		if (!skb_data)
			continue;
		mutex_lock(&mhi_xprtp->ch_hndl.in_skbq_lock);
		skb_queue_walk(&mhi_xprtp->ch_hndl.in_skbq, skb) {
			if (skb->data == skb_data) {
@@ -590,14 +727,17 @@ static void mhi_xprt_disable_event(struct work_struct *work)
static void mhi_xprt_xfer_event(struct mhi_cb_info *cb_info)
{
	struct ipc_router_mhi_xprt *mhi_xprtp;
	dma_addr_t out_dma_addr;

	mhi_xprtp = (struct ipc_router_mhi_xprt *)(cb_info->result->user_data);
	if (cb_info->chan == mhi_xprtp->ch_hndl.out_chan_id) {
		dma_unmap_single(NULL, (dma_addr_t)cb_info->result->payload_buf,
		out_dma_addr = (dma_addr_t)cb_info->result->payload_buf;
		dma_unmap_single(NULL, out_dma_addr,
				cb_info->result->bytes_xferd, DMA_TO_DEVICE);
		mutex_lock(&mhi_xprtp->ch_hndl.state_lock);
		mhi_xprtp->ch_hndl.bytes_to_tx -= cb_info->result->bytes_xferd;
		if (!mhi_xprtp->ch_hndl.bytes_to_tx)
		ipc_router_mhi_xprt_find_addr_map(&mhi_xprtp->tx_addr_map_list,
					&mhi_xprtp->tx_addr_map_list_lock,
					out_dma_addr);
		wake_up(&mhi_xprtp->write_wait_q);
		mutex_unlock(&mhi_xprtp->ch_hndl.state_lock);
	} else if (cb_info->chan == mhi_xprtp->ch_hndl.in_chan_id) {
@@ -754,6 +894,10 @@ static int ipc_router_mhi_config_init(
	mhi_xprtp->ch_hndl.max_packet_size = IPC_ROUTER_MHI_XPRT_MAX_PKT_SIZE;
	mhi_xprtp->ch_hndl.num_trbs = IPC_ROUTER_MHI_XPRT_NUM_TRBS;
	mhi_xprtp->ch_hndl.mhi_xprtp = mhi_xprtp;
	INIT_LIST_HEAD(&mhi_xprtp->tx_addr_map_list);
	mutex_init(&mhi_xprtp->tx_addr_map_list_lock);
	INIT_LIST_HEAD(&mhi_xprtp->rx_addr_map_list);
	mutex_init(&mhi_xprtp->rx_addr_map_list_lock);

	rc = ipc_router_mhi_driver_register(mhi_xprtp);
	return rc;
+3 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/msm_ipc.h>
#include <linux/ipc_router.h>
#include <linux/kref.h>

#define IPC_ROUTER_XPRT_EVENT_DATA  1
#define IPC_ROUTER_XPRT_EVENT_OPEN  2
@@ -98,6 +99,7 @@ struct rr_opt_hdr {
 * @opt_hdr: Optinal header information.
 * @pkt_fragment_q: Queue of SKBs containing payload.
 * @length: Length of data in the chain of SKBs
 * @ref: Reference count for the packet.
 */
struct rr_packet {
	struct list_head list;
@@ -105,6 +107,7 @@ struct rr_packet {
	struct rr_opt_hdr opt_hdr;
	struct sk_buff_head *pkt_fragment_q;
	uint32_t length;
	struct kref ref;
};

/**
+2 −0
Original line number Diff line number Diff line
@@ -530,6 +530,7 @@ struct rr_packet *clone_pkt(struct rr_packet *pkt)
		return NULL;
	}
	skb_queue_head_init(pkt_fragment_q);
	kref_init(&cloned_pkt->ref);

	skb_queue_walk(pkt->pkt_fragment_q, temp_skb) {
		cloned_skb = skb_clone(temp_skb, GFP_KERNEL);
@@ -585,6 +586,7 @@ struct rr_packet *create_pkt(struct sk_buff_head *data)
		}
		skb_queue_head_init(pkt->pkt_fragment_q);
	}
	kref_init(&pkt->ref);
	return pkt;
}