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

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

Merge "msm: ipa3: support HW stats query for LTE"

parents 239cb2ea 96140a9d
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -4515,8 +4515,6 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
		IPADBG("teth_bridge initialized");
		IPADBG("teth_bridge initialized");
	}
	}


	ipa3_debugfs_init();

	result = ipa3_uc_interface_init();
	result = ipa3_uc_interface_init();
	if (result)
	if (result)
		IPAERR(":ipa Uc interface init failed (%d)\n", -result);
		IPAERR(":ipa Uc interface init failed (%d)\n", -result);
@@ -4554,6 +4552,8 @@ static int ipa3_post_init(const struct ipa3_plat_drv_res *resource_p,
	complete_all(&ipa3_ctx->init_completion_obj);
	complete_all(&ipa3_ctx->init_completion_obj);
	pr_info("IPA driver initialization was successful.\n");
	pr_info("IPA driver initialization was successful.\n");


	ipa3_debugfs_init();

	return 0;
	return 0;


fail_teth_bridge_driver_init:
fail_teth_bridge_driver_init:
+146 −39
Original line number Original line Diff line number Diff line
/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * 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
 * it under the terms of the GNU General Public License version 2 and
@@ -24,12 +24,63 @@


int ipa_hw_stats_init(void)
int ipa_hw_stats_init(void)
{
{
	int ret = 0, ep_index;
	struct ipa_teth_stats_endpoints *teth_stats_init;

	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0)
	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0)
		return 0;
		return 0;


	/* initialize stats here */
	/* initialize stats here */
	ipa3_ctx->hw_stats.enabled = true;
	ipa3_ctx->hw_stats.enabled = true;
	return 0;

	teth_stats_init = kzalloc(sizeof(*teth_stats_init), GFP_KERNEL);
	if (!teth_stats_init) {
		IPAERR("mem allocated failed!\n");
		return -ENOMEM;
	}
	/* enable prod mask */
	teth_stats_init->prod_mask = (
		IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_PROD) |
		IPA_CLIENT_BIT_32(IPA_CLIENT_USB_PROD) |
		IPA_CLIENT_BIT_32(IPA_CLIENT_WLAN1_PROD));

	if (IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_PROD)) {
		ep_index = ipa3_get_ep_mapping(IPA_CLIENT_Q6_WAN_PROD);
		if (ep_index == -1) {
			IPAERR("Invalid client.\n");
			kfree(teth_stats_init);
			return -EINVAL;
		}
		teth_stats_init->dst_ep_mask[ep_index] =
			(IPA_CLIENT_BIT_32(IPA_CLIENT_WLAN1_CONS) |
			IPA_CLIENT_BIT_32(IPA_CLIENT_USB_CONS));
	}

	if (IPA_CLIENT_BIT_32(IPA_CLIENT_USB_PROD)) {
		ep_index = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD);
		if (ep_index == -1) {
			IPAERR("Invalid client.\n");
			kfree(teth_stats_init);
			return -EINVAL;
		}
		teth_stats_init->dst_ep_mask[ep_index] =
			IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_CONS);
	}

	if (IPA_CLIENT_BIT_32(IPA_CLIENT_WLAN1_PROD)) {
		ep_index = ipa3_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
		if (ep_index == -1) {
			IPAERR("Invalid client.\n");
			kfree(teth_stats_init);
			return -EINVAL;
		}
		teth_stats_init->dst_ep_mask[ep_index] =
			IPA_CLIENT_BIT_32(IPA_CLIENT_Q6_WAN_CONS);
	}

	ret = ipa_init_teth_stats(teth_stats_init);
	kfree(teth_stats_init);
	return ret;
}
}


int ipa_init_quota_stats(u32 pipe_bitmask)
int ipa_init_quota_stats(u32 pipe_bitmask)
@@ -348,9 +399,12 @@ int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in)
	/* reset driver's cache */
	/* reset driver's cache */
	memset(&ipa3_ctx->hw_stats.teth.init, 0,
	memset(&ipa3_ctx->hw_stats.teth.init, 0,
		sizeof(ipa3_ctx->hw_stats.teth.init));
		sizeof(ipa3_ctx->hw_stats.teth.init));
	for (i = 0; i < IPA_CLIENT_MAX; i++)
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
		memset(&ipa3_ctx->hw_stats.teth.prod_stats_sum[i], 0,
			sizeof(ipa3_ctx->hw_stats.teth.prod_stats_sum[i]));
		memset(&ipa3_ctx->hw_stats.teth.prod_stats[i], 0,
		memset(&ipa3_ctx->hw_stats.teth.prod_stats[i], 0,
			sizeof(ipa3_ctx->hw_stats.teth.prod_stats[i]));
			sizeof(ipa3_ctx->hw_stats.teth.prod_stats[i]));
	}
	ipa3_ctx->hw_stats.teth.init.prod_bitmask = in->prod_mask;
	ipa3_ctx->hw_stats.teth.init.prod_bitmask = in->prod_mask;
	memcpy(ipa3_ctx->hw_stats.teth.init.cons_bitmask, in->dst_ep_mask,
	memcpy(ipa3_ctx->hw_stats.teth.init.cons_bitmask, in->dst_ep_mask,
		sizeof(ipa3_ctx->hw_stats.teth.init.cons_bitmask));
		sizeof(ipa3_ctx->hw_stats.teth.init.cons_bitmask));
@@ -458,8 +512,7 @@ int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in)
	return ret;
	return ret;
}
}


int ipa_get_teth_stats(enum ipa_client_type prod,
int ipa_get_teth_stats(void)
	struct ipa_quota_stats_all *out)
{
{
	int i, j;
	int i, j;
	int ret;
	int ret;
@@ -470,15 +523,14 @@ int ipa_get_teth_stats(enum ipa_client_type prod,
	struct ipa_mem_buffer mem;
	struct ipa_mem_buffer mem;
	struct ipa3_desc desc = { 0 };
	struct ipa3_desc desc = { 0 };
	struct ipahal_stats_tethering_all *stats;
	struct ipahal_stats_tethering_all *stats;
	struct ipa_hw_stats_teth *sw_stats = &ipa3_ctx->hw_stats.teth;
	struct ipahal_stats_init_tethering *init =
		(struct ipahal_stats_init_tethering *)
			&ipa3_ctx->hw_stats.teth.init;


	if (!ipa3_ctx->hw_stats.enabled)
	if (!ipa3_ctx->hw_stats.enabled)
		return 0;
		return 0;


	if (!IPA_CLIENT_IS_PROD(prod) || ipa3_get_ep_mapping(prod) == -1) {
		IPAERR("invalid prod %d\n", prod);
		return -EINVAL;
	}

	get_offset.init = ipa3_ctx->hw_stats.teth.init;
	get_offset.init = ipa3_ctx->hw_stats.teth.init;
	ret = ipahal_stats_get_offset(IPAHAL_HW_STATS_TETHERING, &get_offset,
	ret = ipahal_stats_get_offset(IPAHAL_HW_STATS_TETHERING, &get_offset,
		&offset);
		&offset);
@@ -539,6 +591,12 @@ int ipa_get_teth_stats(enum ipa_client_type prod,
		goto free_stats;
		goto free_stats;
	}
	}


	/* reset prod_stats cache */
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
		memset(&ipa3_ctx->hw_stats.teth.prod_stats[i], 0,
			sizeof(ipa3_ctx->hw_stats.teth.prod_stats[i]));
	}

	/*
	/*
	 * update driver cache.
	 * update driver cache.
	 * the stats were read from hardware with clear_after_read meaning
	 * the stats were read from hardware with clear_after_read meaning
@@ -546,8 +604,6 @@ int ipa_get_teth_stats(enum ipa_client_type prod,
	 */
	 */
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
		for (j = 0; j < IPA_CLIENT_MAX; j++) {
		for (j = 0; j < IPA_CLIENT_MAX; j++) {
			struct ipa_hw_stats_teth *sw_stats =
				&ipa3_ctx->hw_stats.teth;
			int prod_idx = ipa3_get_ep_mapping(i);
			int prod_idx = ipa3_get_ep_mapping(i);
			int cons_idx = ipa3_get_ep_mapping(j);
			int cons_idx = ipa3_get_ep_mapping(j);


@@ -557,29 +613,64 @@ int ipa_get_teth_stats(enum ipa_client_type prod,
			if (cons_idx == -1 || cons_idx >= IPA3_MAX_NUM_PIPES)
			if (cons_idx == -1 || cons_idx >= IPA3_MAX_NUM_PIPES)
				continue;
				continue;


			if (ipa3_ctx->ep[prod_idx].client != i ||
			/* save hw-query result */
			    ipa3_ctx->ep[cons_idx].client != j)
			if ((init->prod_bitmask & (1 << prod_idx)) &&
				continue;
				(init->cons_bitmask[prod_idx]

					& (1 << cons_idx))) {
			sw_stats->prod_stats[i].client[j].num_ipv4_bytes +=
				IPADBG_LOW("prod %d cons %d\n",
				stats->stats[prod_idx][cons_idx].num_ipv4_bytes;
					prod_idx, cons_idx);
			sw_stats->prod_stats[i].client[j].num_ipv4_pkts +=
				IPADBG_LOW("num_ipv4_bytes %lld\n",
				stats->stats[prod_idx][cons_idx].num_ipv4_pkts;
					stats->stats[prod_idx][cons_idx].
			sw_stats->prod_stats[i].client[j].num_ipv6_bytes +=
					num_ipv4_bytes);
				stats->stats[prod_idx][cons_idx].num_ipv6_bytes;
				IPADBG_LOW("num_ipv4_pkts %lld\n",
			sw_stats->prod_stats[i].client[j].num_ipv6_pkts +=
					stats->stats[prod_idx][cons_idx].
				stats->stats[prod_idx][cons_idx].num_ipv6_pkts;
					num_ipv4_pkts);
				IPADBG_LOW("num_ipv6_pkts %lld\n",
					stats->stats[prod_idx][cons_idx].
					num_ipv6_pkts);
				IPADBG_LOW("num_ipv6_bytes %lld\n",
					stats->stats[prod_idx][cons_idx].
					num_ipv6_bytes);

				/* update stats*/
				sw_stats->prod_stats[i].
					client[j].num_ipv4_bytes =
					stats->stats[prod_idx][cons_idx].
					num_ipv4_bytes;
				sw_stats->prod_stats[i].
					client[j].num_ipv4_pkts =
					stats->stats[prod_idx][cons_idx].
					num_ipv4_pkts;
				sw_stats->prod_stats[i].
					client[j].num_ipv6_bytes =
					stats->stats[prod_idx][cons_idx].
					num_ipv6_bytes;
				sw_stats->prod_stats[i].
					client[j].num_ipv6_pkts =
					stats->stats[prod_idx][cons_idx].
					num_ipv6_pkts;

				/* Accumulated stats */
				sw_stats->prod_stats_sum[i].
					client[j].num_ipv4_bytes +=
					stats->stats[prod_idx][cons_idx].
					num_ipv4_bytes;
				sw_stats->prod_stats_sum[i].
					client[j].num_ipv4_pkts +=
					stats->stats[prod_idx][cons_idx].
					num_ipv4_pkts;
				sw_stats->prod_stats_sum[i].
					client[j].num_ipv6_bytes +=
					stats->stats[prod_idx][cons_idx].
					num_ipv6_bytes;
				sw_stats->prod_stats_sum[i].
					client[j].num_ipv6_pkts +=
					stats->stats[prod_idx][cons_idx].
					num_ipv6_pkts;
			}
			}
		}
		}

	if (!out) {
		ret = 0;
		goto free_stats;
	}
	}


	/* copy results to out parameter */
	*out = ipa3_ctx->hw_stats.teth.prod_stats[prod];

	ret = 0;
	ret = 0;
free_stats:
free_stats:
	kfree(stats);
	kfree(stats);
@@ -591,6 +682,22 @@ int ipa_get_teth_stats(enum ipa_client_type prod,


}
}


int ipa_query_teth_stats(enum ipa_client_type prod,
	struct ipa_quota_stats_all *out, bool reset)
{
	if (!IPA_CLIENT_IS_PROD(prod) || ipa3_get_ep_mapping(prod) == -1) {
		IPAERR("invalid prod %d\n", prod);
		return -EINVAL;
	}

	/* copy results to out parameter */
	if (reset)
		*out = ipa3_ctx->hw_stats.teth.prod_stats[prod];
	else
		*out = ipa3_ctx->hw_stats.teth.prod_stats_sum[prod];
	return 0;
}

int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons)
int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons)
{
{
	int ret;
	int ret;
@@ -605,14 +712,14 @@ int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons)
	}
	}


	/* reading stats will reset them in hardware */
	/* reading stats will reset them in hardware */
	ret = ipa_get_teth_stats(prod, NULL);
	ret = ipa_get_teth_stats();
	if (ret) {
	if (ret) {
		IPAERR("ipa_get_teth_stats failed %d\n", ret);
		IPAERR("ipa_get_teth_stats failed %d\n", ret);
		return ret;
		return ret;
	}
	}


	/* reset driver's cache */
	/* reset driver's cache */
	stats = &ipa3_ctx->hw_stats.teth.prod_stats[prod].client[cons];
	stats = &ipa3_ctx->hw_stats.teth.prod_stats_sum[prod].client[cons];
	memset(stats, 0, sizeof(*stats));
	memset(stats, 0, sizeof(*stats));
	return 0;
	return 0;
}
}
@@ -632,7 +739,7 @@ int ipa_reset_all_cons_teth_stats(enum ipa_client_type prod)
	}
	}


	/* reading stats will reset them in hardware */
	/* reading stats will reset them in hardware */
	ret = ipa_get_teth_stats(prod, NULL);
	ret = ipa_get_teth_stats();
	if (ret) {
	if (ret) {
		IPAERR("ipa_get_teth_stats failed %d\n", ret);
		IPAERR("ipa_get_teth_stats failed %d\n", ret);
		return ret;
		return ret;
@@ -640,7 +747,7 @@ int ipa_reset_all_cons_teth_stats(enum ipa_client_type prod)


	/* reset driver's cache */
	/* reset driver's cache */
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
		stats = &ipa3_ctx->hw_stats.teth.prod_stats[prod].client[i];
		stats = &ipa3_ctx->hw_stats.teth.prod_stats_sum[prod].client[i];
		memset(stats, 0, sizeof(*stats));
		memset(stats, 0, sizeof(*stats));
	}
	}


@@ -659,7 +766,7 @@ int ipa_reset_all_teth_stats(void)
	/* reading stats will reset them in hardware */
	/* reading stats will reset them in hardware */
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
		if (IPA_CLIENT_IS_PROD(i) && ipa3_get_ep_mapping(i) != -1) {
		if (IPA_CLIENT_IS_PROD(i) && ipa3_get_ep_mapping(i) != -1) {
			ret = ipa_get_teth_stats(i, NULL);
			ret = ipa_get_teth_stats();
			if (ret) {
			if (ret) {
				IPAERR("ipa_get_teth_stats failed %d\n", ret);
				IPAERR("ipa_get_teth_stats failed %d\n", ret);
				return ret;
				return ret;
@@ -671,7 +778,7 @@ int ipa_reset_all_teth_stats(void)


	/* reset driver's cache */
	/* reset driver's cache */
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
	for (i = 0; i < IPA_CLIENT_MAX; i++) {
		stats = &ipa3_ctx->hw_stats.teth.prod_stats[i];
		stats = &ipa3_ctx->hw_stats.teth.prod_stats_sum[i];
		memset(stats, 0, sizeof(*stats));
		memset(stats, 0, sizeof(*stats));
	}
	}


@@ -1566,7 +1673,7 @@ static ssize_t ipa_debugfs_print_tethering_stats(struct file *file,
			(1 << ep_idx)))
			(1 << ep_idx)))
			continue;
			continue;


		res = ipa_get_teth_stats(i, out);
		res = ipa_get_teth_stats();
		if (res) {
		if (res) {
			mutex_unlock(&ipa3_ctx->lock);
			mutex_unlock(&ipa3_ctx->lock);
			kfree(out);
			kfree(out);
+5 −2
Original line number Original line Diff line number Diff line
@@ -1124,6 +1124,7 @@ struct ipa_hw_stats_quota {


struct ipa_hw_stats_teth {
struct ipa_hw_stats_teth {
	struct ipahal_stats_init_tethering init;
	struct ipahal_stats_init_tethering init;
	struct ipa_quota_stats_all prod_stats_sum[IPA_CLIENT_MAX];
	struct ipa_quota_stats_all prod_stats[IPA_CLIENT_MAX];
	struct ipa_quota_stats_all prod_stats[IPA_CLIENT_MAX];
};
};


@@ -2251,8 +2252,10 @@ int ipa_reset_all_drop_stats(void);


int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in);
int ipa_init_teth_stats(struct ipa_teth_stats_endpoints *in);


int ipa_get_teth_stats(enum ipa_client_type prod,
int ipa_get_teth_stats(void);
	struct ipa_quota_stats_all *out);

int ipa_query_teth_stats(enum ipa_client_type prod,
	struct ipa_quota_stats_all *out, bool reset);


int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons);
int ipa_reset_teth_stats(enum ipa_client_type prod, enum ipa_client_type cons);


+148 −9
Original line number Original line Diff line number Diff line
@@ -3183,9 +3183,6 @@ static int rmnet_ipa3_query_tethering_stats_modem(
		req->reset_stats_valid = true;
		req->reset_stats_valid = true;
		req->reset_stats = true;
		req->reset_stats = true;
		IPAWANDBG("reset the pipe stats\n");
		IPAWANDBG("reset the pipe stats\n");
	} else {
		/* print tethered-client enum */
		IPAWANDBG("Tethered-client enum(%d)\n", data->ipa_client);
	}
	}


	rc = ipa3_qmi_get_data_stats(req, resp);
	rc = ipa3_qmi_get_data_stats(req, resp);
@@ -3277,7 +3274,7 @@ static int rmnet_ipa3_query_tethering_stats_modem(
				if (data->ipa_client == ipa_get_client(resp->
				if (data->ipa_client == ipa_get_client(resp->
				ul_src_pipe_stats_list[pipe_len].
				ul_src_pipe_stats_list[pipe_len].
				pipe_index)) {
				pipe_index)) {
					/* update the DL stats */
					/* update the UL stats */
					data->ipv4_tx_packets += resp->
					data->ipv4_tx_packets += resp->
					ul_src_pipe_stats_list[pipe_len].
					ul_src_pipe_stats_list[pipe_len].
					num_ipv4_packets;
					num_ipv4_packets;
@@ -3304,6 +3301,133 @@ static int rmnet_ipa3_query_tethering_stats_modem(
	return 0;
	return 0;
}
}


static int rmnet_ipa3_query_tethering_stats_hw(
	struct wan_ioctl_query_tether_stats *data, bool reset)
{
	int rc = 0;
	struct ipa_quota_stats_all *con_stats;

	if (reset) {
		IPAWANERR("only reset the pipe stats without returning stats");
		rc = ipa_get_teth_stats();
		if (rc) {
			IPAWANERR("ipa_get_teth_stats failed %d,\n", rc);
			return rc;
		}
		return 0;
	}
	/* qet HW-stats */
	rc = ipa_get_teth_stats();
	if (rc) {
		IPAWANDBG("ipa_get_teth_stats failed %d,\n", rc);
		return rc;
	}

	/* query DL stats */
	IPAWANDBG("reset the pipe stats? (%d)\n", reset);
	con_stats = kzalloc(sizeof(*con_stats), GFP_KERNEL);
	if (!con_stats) {
		IPAWANERR("no memory\n");
		return -ENOMEM;
	}
	rc = ipa_query_teth_stats(IPA_CLIENT_Q6_WAN_PROD, con_stats, reset);
	if (rc) {
		IPAERR("IPA_CLIENT_Q6_WAN_PROD query failed %d,\n", rc);
		kfree(con_stats);
		return rc;
	}
	IPAWANDBG("wlan: v4_rx_p(%d) b(%lld) v6_rx_p(%d) b(%lld)\n",
	con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_pkts,
	con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_bytes,
	con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_pkts,
	con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_bytes);

	IPAWANDBG("usb: v4_rx_p(%d) b(%lld) v6_rx_p(%d) b(%lld)\n",
	con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_pkts,
	con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_bytes,
	con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_pkts,
	con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_bytes);

	/* update the DL stats */
	data->ipv4_rx_packets =
		con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_pkts +
			con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_pkts;
	data->ipv6_rx_packets =
		con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_pkts +
			con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_pkts;
	data->ipv4_rx_bytes =
		con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv4_bytes +
			con_stats->client[IPA_CLIENT_USB_CONS].num_ipv4_bytes;
	data->ipv6_rx_bytes =
		con_stats->client[IPA_CLIENT_WLAN1_CONS].num_ipv6_bytes +
			con_stats->client[IPA_CLIENT_USB_CONS].num_ipv6_bytes;

	IPAWANDBG("v4_rx_p(%lu) v6_rx_p(%lu) v4_rx_b(%lu) v6_rx_b(%lu)\n",
		(unsigned long int) data->ipv4_rx_packets,
		(unsigned long int) data->ipv6_rx_packets,
		(unsigned long int) data->ipv4_rx_bytes,
		(unsigned long int) data->ipv6_rx_bytes);

	/* query USB UL stats */
	memset(con_stats, 0, sizeof(struct ipa_quota_stats_all));
	rc = ipa_query_teth_stats(IPA_CLIENT_USB_PROD, con_stats, reset);
	if (rc) {
		IPAERR("IPA_CLIENT_USB_PROD query failed %d\n", rc);
		kfree(con_stats);
		return rc;
	}

	IPAWANDBG("usb: v4_tx_p(%d) b(%lld) v6_tx_p(%d) b(%lld)\n",
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts,
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes,
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts,
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes);

	/* update the USB UL stats */
	data->ipv4_tx_packets =
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts;
	data->ipv6_tx_packets =
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts;
	data->ipv4_tx_bytes =
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes;
	data->ipv6_tx_bytes =
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes;

	/* query WLAN UL stats */
	memset(con_stats, 0, sizeof(struct ipa_quota_stats_all));
	rc = ipa_query_teth_stats(IPA_CLIENT_WLAN1_PROD, con_stats, reset);
	if (rc) {
		IPAERR("IPA_CLIENT_WLAN1_PROD query failed %d\n", rc);
		kfree(con_stats);
		return rc;
	}

	IPAWANDBG("wlan: v4_tx_p(%d) b(%lld) v6_tx_p(%d) b(%lld)\n",
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts,
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes,
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts,
	con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes);

	/* update the wlan UL stats */
	data->ipv4_tx_packets +=
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_pkts;
	data->ipv6_tx_packets +=
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_pkts;
	data->ipv4_tx_bytes +=
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv4_bytes;
	data->ipv6_tx_bytes +=
		con_stats->client[IPA_CLIENT_Q6_WAN_CONS].num_ipv6_bytes;

	IPAWANDBG("v4_tx_p(%lu) v6_tx_p(%lu) v4_tx_b(%lu) v6_tx_b(%lu)\n",
		(unsigned long int) data->ipv4_tx_packets,
		(unsigned long  int) data->ipv6_tx_packets,
		(unsigned long int) data->ipv4_tx_bytes,
		(unsigned long int) data->ipv6_tx_bytes);
	kfree(con_stats);
	return rc;
}


int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data,
int rmnet_ipa3_query_tethering_stats(struct wan_ioctl_query_tether_stats *data,
	bool reset)
	bool reset)
{
{
@@ -3373,12 +3497,27 @@ int rmnet_ipa3_query_tethering_stats_all(
	} else {
	} else {
		IPAWANDBG_LOW(" query modem-backhaul stats\n");
		IPAWANDBG_LOW(" query modem-backhaul stats\n");
		tether_stats.ipa_client = data->ipa_client;
		tether_stats.ipa_client = data->ipa_client;
		if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0 ||
			!ipa3_ctx->hw_stats.enabled) {
			IPAWANDBG("hw version %d,hw_stats.enabled %d\n",
				ipa3_ctx->ipa_hw_type,
				ipa3_ctx->hw_stats.enabled);
			/* get modem stats from QMI */
			rc = rmnet_ipa3_query_tethering_stats_modem(
			rc = rmnet_ipa3_query_tethering_stats_modem(
				&tether_stats, data->reset_stats);
				&tether_stats, data->reset_stats);
			if (rc) {
			if (rc) {
			IPAWANERR("modem WAN_IOC_QUERY_TETHER_STATS failed\n");
				IPAWANERR("modem QUERY_TETHER_STATS failed\n");
				return rc;
				return rc;
			}
			}
		} else {
			/* get modem stats from IPA-HW counters */
			rc = rmnet_ipa3_query_tethering_stats_hw(
				&tether_stats, data->reset_stats);
			if (rc) {
				IPAWANERR("modem QUERY_TETHER_STATS failed\n");
				return rc;
			}
		}
		data->tx_bytes = tether_stats.ipv4_tx_bytes
		data->tx_bytes = tether_stats.ipv4_tx_bytes
			+ tether_stats.ipv6_tx_bytes;
			+ tether_stats.ipv6_tx_bytes;
		data->rx_bytes = tether_stats.ipv4_rx_bytes
		data->rx_bytes = tether_stats.ipv4_rx_bytes