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

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

Merge "msm: ipa: fix tx completion crash during disconnect"

parents 192ec393 0c70d8ff
Loading
Loading
Loading
Loading
+89 −47
Original line number Original line Diff line number Diff line
@@ -65,7 +65,6 @@ static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt)
	for (i = 0; i < cnt; i++) {
	for (i = 0; i < cnt; i++) {
		spin_lock_bh(&sys->spinlock);
		spin_lock_bh(&sys->spinlock);
		if (unlikely(list_empty(&sys->head_desc_list))) {
		if (unlikely(list_empty(&sys->head_desc_list))) {
			WARN_ON(1);
			spin_unlock_bh(&sys->spinlock);
			spin_unlock_bh(&sys->spinlock);
			return;
			return;
		}
		}
@@ -99,12 +98,48 @@ static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt)
	}
	}
}
}


static void ipa_purge_tx_list(struct ipa_sys_context *sys)
{
	struct ipa_tx_pkt_wrapper *tx_pkt_expected;
	struct list_head free_list;

	INIT_LIST_HEAD(&free_list);

	spin_lock_bh(&sys->spinlock);
	list_splice_tail_init(&sys->head_desc_list, &free_list);
	sys->len = 0;
	spin_unlock_bh(&sys->spinlock);

	while (!list_empty(&free_list)) {
		tx_pkt_expected = list_first_entry(&free_list,
						   struct ipa_tx_pkt_wrapper,
						   link);
		list_del(&tx_pkt_expected->link);
		if (!tx_pkt_expected->no_unmap_dma)
			dma_unmap_single(ipa_ctx->pdev,
					tx_pkt_expected->mem.phys_base,
					tx_pkt_expected->mem.size,
					DMA_TO_DEVICE);
		if (tx_pkt_expected->callback)
			tx_pkt_expected->callback(tx_pkt_expected->user1,
					tx_pkt_expected->user2);
		if (tx_pkt_expected->cnt > 1 &&
				tx_pkt_expected->cnt != IPA_LAST_DESC_CNT)
			dma_free_coherent(ipa_ctx->pdev,
				tx_pkt_expected->mult.size,
				tx_pkt_expected->mult.base,
				tx_pkt_expected->mult.phys_base);
		kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt_expected);
	}
}

static void ipa_wq_write_done_status(int src_pipe)
static void ipa_wq_write_done_status(int src_pipe)
{
{
	struct ipa_tx_pkt_wrapper *tx_pkt_expected;
	struct ipa_tx_pkt_wrapper *tx_pkt_expected;
	struct ipa_sys_context *sys;
	struct ipa_sys_context *sys;
	u32 cnt;
	u32 cnt;


	WARN_ON(src_pipe >= IPA_NUM_PIPES);
	sys = ipa_ctx->ep[src_pipe].sys;
	sys = ipa_ctx->ep[src_pipe].sys;
	if (!sys) {
	if (!sys) {
		IPAERR("null sys pipe src %d\n", src_pipe);
		IPAERR("null sys pipe src %d\n", src_pipe);
@@ -114,7 +149,6 @@ static void ipa_wq_write_done_status(int src_pipe)


	spin_lock_bh(&sys->spinlock);
	spin_lock_bh(&sys->spinlock);
	if (unlikely(list_empty(&sys->head_desc_list))) {
	if (unlikely(list_empty(&sys->head_desc_list))) {
		WARN_ON(1);
		spin_unlock_bh(&sys->spinlock);
		spin_unlock_bh(&sys->spinlock);
		return;
		return;
	}
	}
@@ -750,12 +784,8 @@ static int ipa_handle_rx_core(struct ipa_sys_context *sys, bool process_all,
		if (iov.addr == 0)
		if (iov.addr == 0)
			break;
			break;


		if (sys->ep->client == IPA_CLIENT_WLAN1_CONS ||
		if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
				sys->ep->client == IPA_CLIENT_WLAN2_CONS ||
				sys->ep->client == IPA_CLIENT_WLAN3_CONS ||
				sys->ep->client == IPA_CLIENT_WLAN4_CONS) {
			ipa_wlan_wq_rx_common(sys, iov.size);
			ipa_wlan_wq_rx_common(sys, iov.size);
			}
		else
		else
			ipa_wq_rx_common(sys, iov.size);
			ipa_wq_rx_common(sys, iov.size);


@@ -968,24 +998,34 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
		}
		}
	}
	}


	memset(ep, 0, sizeof(struct ipa_ep_context));
	memset(ep, 0, offsetof(struct ipa_ep_context, sys));


	if (!ep->sys) {
		ep->sys = kzalloc(sizeof(struct ipa_sys_context), GFP_KERNEL);
		ep->sys = kzalloc(sizeof(struct ipa_sys_context), GFP_KERNEL);
		if (!ep->sys) {
		if (!ep->sys) {
		IPAERR("failed to sys ctx for client %d\n", sys_in->client);
			IPAERR("failed to sys ctx for client %d\n",
					sys_in->client);
			result = -ENOMEM;
			result = -ENOMEM;
			goto fail_and_disable_clocks;
			goto fail_and_disable_clocks;
		}
		}


	snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipawq%d", sys_in->client);
		ep->sys->ep = ep;
		snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipawq%d",
				sys_in->client);
		ep->sys->wq = create_singlethread_workqueue(buff);
		ep->sys->wq = create_singlethread_workqueue(buff);
		if (!ep->sys->wq) {
		if (!ep->sys->wq) {
		IPAERR("failed to create wq for client %d\n", sys_in->client);
			IPAERR("failed to create wq for client %d\n",
					sys_in->client);
			result = -EFAULT;
			result = -EFAULT;
			goto fail_wq;
			goto fail_wq;
		}
		}


	ep->sys->ep = ep;
		INIT_LIST_HEAD(&ep->sys->head_desc_list);
		spin_lock_init(&ep->sys->spinlock);
	} else {
		memset(ep->sys, 0, offsetof(struct ipa_sys_context, ep));
	}

	ep->skip_ep_cfg = sys_in->skip_ep_cfg;
	ep->skip_ep_cfg = sys_in->skip_ep_cfg;
	if (ipa_assign_policy(sys_in, ep->sys)) {
	if (ipa_assign_policy(sys_in, ep->sys)) {
		IPAERR("failed to sys ctx for client %d\n", sys_in->client);
		IPAERR("failed to sys ctx for client %d\n", sys_in->client);
@@ -1000,9 +1040,6 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
	ep->keep_ipa_awake = sys_in->keep_ipa_awake;
	ep->keep_ipa_awake = sys_in->keep_ipa_awake;
	ep->avail_fifo_desc =
	ep->avail_fifo_desc =
		((sys_in->desc_fifo_sz/sizeof(struct sps_iovec))-1);
		((sys_in->desc_fifo_sz/sizeof(struct sps_iovec))-1);
	INIT_LIST_HEAD(&ep->sys->head_desc_list);

	spin_lock_init(&ep->sys->spinlock);


	result = ipa_enable_data_path(ipa_ep_idx);
	result = ipa_enable_data_path(ipa_ep_idx);
	if (result) {
	if (result) {
@@ -1094,12 +1131,8 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
	if (IPA_CLIENT_IS_CONS(sys_in->client))
	if (IPA_CLIENT_IS_CONS(sys_in->client))
		ipa_replenish_rx_cache(ep->sys);
		ipa_replenish_rx_cache(ep->sys);


	if (sys_in->client == IPA_CLIENT_WLAN1_CONS ||
	if (IPA_CLIENT_IS_WLAN_CONS(sys_in->client))
			sys_in->client == IPA_CLIENT_WLAN2_CONS ||
			sys_in->client == IPA_CLIENT_WLAN3_CONS ||
			sys_in->client == IPA_CLIENT_WLAN4_CONS) {
		ipa_allocate_wlan_rx_common_cache(IPA_WLAN_COMM_RX_POOL_LOW);
		ipa_allocate_wlan_rx_common_cache(IPA_WLAN_COMM_RX_POOL_LOW);
	}


	if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(sys_in->client))
	if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(sys_in->client))
		ipa_install_dflt_flt_rules(ipa_ep_idx);
		ipa_install_dflt_flt_rules(ipa_ep_idx);
@@ -1142,6 +1175,7 @@ EXPORT_SYMBOL(ipa_setup_sys_pipe);
int ipa_teardown_sys_pipe(u32 clnt_hdl)
int ipa_teardown_sys_pipe(u32 clnt_hdl)
{
{
	struct ipa_ep_context *ep;
	struct ipa_ep_context *ep;
	int empty;


	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
	if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
		IPAERR("bad parm.\n");
		IPAERR("bad parm.\n");
@@ -1153,19 +1187,31 @@ int ipa_teardown_sys_pipe(u32 clnt_hdl)
	if (!ep->keep_ipa_awake)
	if (!ep->keep_ipa_awake)
		ipa_inc_client_enable_clks();
		ipa_inc_client_enable_clks();


	if (IPA_CLIENT_IS_CONS(ep->client))
		ipa_cleanup_rx(ep->sys);

	ipa_disable_data_path(clnt_hdl);
	ipa_disable_data_path(clnt_hdl);
	ep->valid = 0;

	do {
		spin_lock_bh(&ep->sys->spinlock);
		empty = list_empty(&ep->sys->head_desc_list);
		spin_unlock_bh(&ep->sys->spinlock);
		if (!empty)
			usleep(100);
		else
			break;
	} while (1);

	flush_workqueue(ep->sys->wq);
	sps_disconnect(ep->ep_hdl);
	sps_disconnect(ep->ep_hdl);
	dma_free_coherent(ipa_ctx->pdev, ep->connect.desc.size,
	dma_free_coherent(ipa_ctx->pdev, ep->connect.desc.size,
			  ep->connect.desc.base,
			  ep->connect.desc.base,
			  ep->connect.desc.phys_base);
			  ep->connect.desc.phys_base);
	sps_free_endpoint(ep->ep_hdl);
	sps_free_endpoint(ep->ep_hdl);
	destroy_workqueue(ep->sys->wq);
	if (IPA_CLIENT_IS_CONS(ep->client))
	kfree(ep->sys);
		ipa_cleanup_rx(ep->sys);
	else
		ipa_purge_tx_list(ep->sys);

	ipa_delete_dflt_flt_rules(clnt_hdl);
	ipa_delete_dflt_flt_rules(clnt_hdl);
	memset(ep, 0, sizeof(struct ipa_ep_context));


	ipa_dec_client_disable_clks();
	ipa_dec_client_disable_clks();


@@ -1266,6 +1312,11 @@ int ipa_tx_dp(enum ipa_client_type dst, struct sk_buff *skb,


	sys = ipa_ctx->ep[src_ep_idx].sys;
	sys = ipa_ctx->ep[src_ep_idx].sys;


	if (!sys->ep->valid) {
		IPAERR("pipe not valid\n");
		goto fail_gen;
	}

	if (dst_ep_idx != -1) {
	if (dst_ep_idx != -1) {
		/* SW data path */
		/* SW data path */
		cmd = kzalloc(sizeof(struct ipa_ip_packet_init), GFP_ATOMIC);
		cmd = kzalloc(sizeof(struct ipa_ip_packet_init), GFP_ATOMIC);
@@ -1451,7 +1502,6 @@ fail_kmem_cache_alloc:
	return;
	return;
}
}



/**
/**
 * ipa_replenish_rx_cache() - Replenish the Rx packets cache.
 * ipa_replenish_rx_cache() - Replenish the Rx packets cache.
 *
 *
@@ -1715,7 +1765,7 @@ begin:


			skb2 = skb_clone(skb, GFP_KERNEL);
			skb2 = skb_clone(skb, GFP_KERNEL);
			if (likely(skb2)) {
			if (likely(skb2)) {
				if (skb->len < len) {
				if (skb->len < len + IPA_PKT_STATUS_SIZE) {
					IPADBG("SPL skb len %d len %d\n",
					IPADBG("SPL skb len %d len %d\n",
							skb->len, len);
							skb->len, len);
					sys->prev_skb = skb2;
					sys->prev_skb = skb2;
@@ -1748,15 +1798,10 @@ begin:
				}
				}
			}
			}
			/* TX comp */
			/* TX comp */
			WARN_ON(status->endp_src_idx >= IPA_NUM_PIPES);
			if (ipa_ctx->ep[status->endp_src_idx].sys) {
			ipa_wq_write_done_status(status->endp_src_idx);
			ipa_wq_write_done_status(status->endp_src_idx);
				IPADBG("tx comp imp for %d\n",
			IPADBG("tx comp imp for %d\n", status->endp_src_idx);
					status->endp_src_idx);
			}
		} else {
		} else {
			/* TX comp */
			/* TX comp */
			WARN_ON(status->endp_src_idx >= IPA_NUM_PIPES);
			ipa_wq_write_done_status(status->endp_src_idx);
			ipa_wq_write_done_status(status->endp_src_idx);
			IPADBG("tx comp exp for %d\n", status->endp_src_idx);
			IPADBG("tx comp exp for %d\n", status->endp_src_idx);
			skb_pull(skb, IPA_PKT_STATUS_SIZE);
			skb_pull(skb, IPA_PKT_STATUS_SIZE);
@@ -2236,10 +2281,7 @@ static int ipa_assign_policy(struct ipa_sys_connect_params *in,
						IPA_CLIENT_APPS_WAN_CONS) {
						IPA_CLIENT_APPS_WAN_CONS) {
					sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
					sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
				}
				}
			} else if (in->client == IPA_CLIENT_WLAN1_CONS ||
			} else if (IPA_CLIENT_IS_WLAN_CONS(in->client)) {
					in->client == IPA_CLIENT_WLAN2_CONS ||
					in->client == IPA_CLIENT_WLAN3_CONS ||
					in->client == IPA_CLIENT_WLAN4_CONS) {
				IPADBG("assigning policy to client:%d",
				IPADBG("assigning policy to client:%d",
					in->client);
					in->client);


+10 −5
Original line number Original line Diff line number Diff line
@@ -356,13 +356,15 @@ struct ipa_ep_context {
	u32 data_fifo_pipe_mem_ofst;
	u32 data_fifo_pipe_mem_ofst;
	bool desc_fifo_client_allocated;
	bool desc_fifo_client_allocated;
	bool data_fifo_client_allocated;
	bool data_fifo_client_allocated;
	struct ipa_sys_context *sys;
	u32 avail_fifo_desc;
	u32 avail_fifo_desc;
	u32 dflt_flt4_rule_hdl;
	u32 dflt_flt4_rule_hdl;
	u32 dflt_flt6_rule_hdl;
	u32 dflt_flt6_rule_hdl;
	bool skip_ep_cfg;
	bool skip_ep_cfg;
	bool keep_ipa_awake;
	bool keep_ipa_awake;
	bool resume_on_connect;
	bool resume_on_connect;

	/* sys MUST be the last element of this struct */
	struct ipa_sys_context *sys;
};
};


enum ipa_sys_pipe_policy {
enum ipa_sys_pipe_policy {
@@ -382,11 +384,8 @@ enum ipa_sys_pipe_policy {
 * IPA context specific to the system-bam pipes a.k.a LAN IN/OUT and WAN
 * IPA context specific to the system-bam pipes a.k.a LAN IN/OUT and WAN
 */
 */
struct ipa_sys_context {
struct ipa_sys_context {
	struct list_head head_desc_list;
	u32 len;
	u32 len;
	spinlock_t spinlock;
	struct sps_register_event event;
	struct sps_register_event event;
	struct ipa_ep_context *ep;
	atomic_t curr_polling_state;
	atomic_t curr_polling_state;
	struct delayed_work switch_to_intr_work;
	struct delayed_work switch_to_intr_work;
	enum ipa_sys_pipe_policy policy;
	enum ipa_sys_pipe_policy policy;
@@ -395,7 +394,6 @@ struct ipa_sys_context {
	void (*free_skb)(struct sk_buff *skb);
	void (*free_skb)(struct sk_buff *skb);
	u32 rx_buff_sz;
	u32 rx_buff_sz;
	u32 rx_pool_sz;
	u32 rx_pool_sz;
	struct workqueue_struct *wq;
	struct sk_buff *prev_skb;
	struct sk_buff *prev_skb;
	unsigned int len_rem;
	unsigned int len_rem;
	unsigned int len_pad;
	unsigned int len_pad;
@@ -404,6 +402,13 @@ struct ipa_sys_context {
	void (*sps_callback)(struct sps_event_notify *notify);
	void (*sps_callback)(struct sps_event_notify *notify);
	enum sps_option sps_option;
	enum sps_option sps_option;
	struct delayed_work replenish_rx_work;
	struct delayed_work replenish_rx_work;

	/* ordering is important - mutable fields go above */
	struct ipa_ep_context *ep;
	struct list_head head_desc_list;
	spinlock_t spinlock;
	struct workqueue_struct *wq;
	/* ordering is important - other immutable fields go below */
};
};


/**
/**
+6 −0
Original line number Original line Diff line number Diff line
@@ -164,6 +164,12 @@ enum ipa_client_type {
	(client) == IPA_CLIENT_USB3_CONS || \
	(client) == IPA_CLIENT_USB3_CONS || \
	(client) == IPA_CLIENT_USB4_CONS)
	(client) == IPA_CLIENT_USB4_CONS)


#define IPA_CLIENT_IS_WLAN_CONS(client) \
	((client) == IPA_CLIENT_WLAN1_CONS || \
	(client) == IPA_CLIENT_WLAN2_CONS || \
	(client) == IPA_CLIENT_WLAN3_CONS || \
	(client) == IPA_CLIENT_WLAN4_CONS)

/**
/**
 * enum ipa_ip_type - Address family: IPv4 or IPv6
 * enum ipa_ip_type - Address family: IPv4 or IPv6
 */
 */