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

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

Merge "soc: qcom: ipc_router_mhi_xprt: Transmit packets asynchronously"

parents 306dcdc7 affb425a
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;
}