1 /*
2  * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #ifdef WLAN_DP_FEATURE_SW_LATENCY_MGR
19 
20 #include <dp_types.h>
21 #include <dp_internal.h>
22 #include <wlan_cfg.h>
23 #include "wlan_dp_swlm.h"
24 #include "qdf_time.h"
25 #include "qdf_util.h"
26 #include "hal_internal.h"
27 #include "hal_api.h"
28 #include "hif.h"
29 #include <qdf_status.h>
30 #include <qdf_nbuf.h>
31 
32 /**
33  * dp_swlm_is_tput_thresh_reached() - Calculate the current tx and rx TPUT
34  *				      and check if it passes the pre-set
35  *				      threshold.
36  * @soc: Datapath global soc handle
37  * @rid: TCL ring id
38  *
39  * This function calculates the current TX and RX throughput and checks
40  * if it is above the pre-set thresholds by SWLM.
41  *
42  * Returns: true, if the TX/RX throughput is passing the threshold
43  *	    false, otherwise
44  */
dp_swlm_is_tput_thresh_reached(struct dp_soc * soc,uint8_t rid)45 static bool dp_swlm_is_tput_thresh_reached(struct dp_soc *soc, uint8_t rid)
46 {
47 	struct dp_swlm_params *params = &soc->swlm.params;
48 	int rx_delta, tx_delta, tx_packet_delta;
49 	bool result = false;
50 
51 	tx_delta = soc->stats.tx.egress[rid].bytes -
52 			params->tcl[rid].prev_tx_bytes;
53 	params->tcl[rid].prev_tx_bytes = soc->stats.tx.egress[rid].bytes;
54 	if (tx_delta > params->tx_traffic_thresh) {
55 		params->tcl[rid].sampling_session_tx_bytes = tx_delta;
56 		result = true;
57 	}
58 
59 	rx_delta = soc->stats.rx.ingress.bytes - params->tcl[rid].prev_rx_bytes;
60 	params->tcl[rid].prev_rx_bytes = soc->stats.rx.ingress.bytes;
61 	if (!result && rx_delta > params->rx_traffic_thresh) {
62 		params->tcl[rid].sampling_session_tx_bytes = tx_delta;
63 		result = true;
64 	}
65 
66 	tx_packet_delta = soc->stats.tx.egress[rid].num -
67 		params->tcl[rid].prev_tx_packets;
68 	params->tcl[rid].prev_tx_packets = soc->stats.tx.egress[rid].num;
69 	if (tx_packet_delta < params->tx_pkt_thresh)
70 		result = false;
71 
72 	return result;
73 }
74 
75 /**
76  * dp_swlm_can_tcl_wr_coalesce() - To check if current TCL reg write can be
77  *				   coalesced or not.
78  * @soc: Datapath global soc handle
79  * @tcl_data: priv data for tcl coalescing
80  *
81  * This function takes into account the current tx and rx throughput and
82  * decides whether the TCL register write corresponding to the current packet,
83  * to be transmitted, is to be processed or coalesced.
84  * It maintains a session for which the TCL register writes are coalesced and
85  * then flushed if a certain time/bytes threshold is reached.
86  *
87  * Returns: 1 if the current TCL write is to be coalesced
88  *	    0, if the current TCL write is to be processed.
89  */
90 static int
dp_swlm_can_tcl_wr_coalesce(struct dp_soc * soc,struct dp_swlm_tcl_data * tcl_data)91 dp_swlm_can_tcl_wr_coalesce(struct dp_soc *soc,
92 			    struct dp_swlm_tcl_data *tcl_data)
93 {
94 	u64 curr_time = qdf_get_log_timestamp_usecs();
95 	int tput_level_pass, coalesce = 0;
96 	struct dp_swlm *swlm = &soc->swlm;
97 	uint8_t rid = tcl_data->ring_id;
98 	struct dp_swlm_params *params = &soc->swlm.params;
99 
100 	if (curr_time >= params->tcl[rid].expire_time) {
101 		params->tcl[rid].expire_time = qdf_get_log_timestamp_usecs() +
102 			      params->sampling_time;
103 		tput_level_pass = dp_swlm_is_tput_thresh_reached(soc, rid);
104 		if (tput_level_pass) {
105 			params->tcl[rid].tput_pass_cnt++;
106 		} else {
107 			params->tcl[rid].tput_pass_cnt = 0;
108 			DP_STATS_INC(swlm, tcl[rid].tput_criteria_fail, 1);
109 			goto coalescing_fail;
110 		}
111 	}
112 
113 	params->tcl[rid].bytes_coalesced += tcl_data->pkt_len;
114 
115 	if (params->tcl[rid].tput_pass_cnt > DP_SWLM_TCL_TPUT_PASS_THRESH) {
116 		coalesce = 1;
117 		if (params->tcl[rid].bytes_coalesced >
118 		    params->tcl[rid].bytes_flush_thresh) {
119 			coalesce = 0;
120 			DP_STATS_INC(swlm, tcl[rid].bytes_thresh_reached, 1);
121 		} else if (curr_time > params->tcl[rid].coalesce_end_time) {
122 			coalesce = 0;
123 			DP_STATS_INC(swlm, tcl[rid].time_thresh_reached, 1);
124 		}
125 	}
126 
127 coalescing_fail:
128 	if (!coalesce) {
129 		dp_swlm_tcl_reset_session_data(soc, rid);
130 		return 0;
131 	}
132 
133 	qdf_timer_mod(&params->tcl[rid].flush_timer, 1);
134 
135 	return 1;
136 }
137 
dp_print_swlm_stats(struct dp_soc * soc)138 QDF_STATUS dp_print_swlm_stats(struct dp_soc *soc)
139 {
140 	struct dp_swlm *swlm = &soc->swlm;
141 	int i;
142 
143 	for (i = 0; i < soc->num_tcl_data_rings; i++) {
144 		dp_info("TCL: %u Coalescing stats:", i);
145 		dp_info("Num coalesce success: %d",
146 			swlm->stats.tcl[i].coalesce_success);
147 		dp_info("Num coalesce fail: %d",
148 			swlm->stats.tcl[i].coalesce_fail);
149 		dp_info("Timer flush success: %d",
150 			swlm->stats.tcl[i].timer_flush_success);
151 		dp_info("Timer flush fail: %d",
152 			swlm->stats.tcl[i].timer_flush_fail);
153 		dp_info("Coalesce fail (TID): %d",
154 			swlm->stats.tcl[i].tid_fail);
155 		dp_info("Coalesce fail (special frame): %d",
156 			swlm->stats.tcl[i].sp_frames);
157 		dp_info("Coalesce fail (Low latency connection): %d",
158 			swlm->stats.tcl[i].ll_connection);
159 		dp_info("Coalesce fail (bytes thresh crossed): %d",
160 			swlm->stats.tcl[i].bytes_thresh_reached);
161 		dp_info("Coalesce fail (time thresh crossed): %d",
162 			swlm->stats.tcl[i].time_thresh_reached);
163 		dp_info("Coalesce fail (TPUT sampling fail): %d",
164 			swlm->stats.tcl[i].tput_criteria_fail);
165 	}
166 
167 	return QDF_STATUS_SUCCESS;
168 }
169 
170 static struct dp_swlm_ops dp_latency_mgr_ops = {
171 	.tcl_wr_coalesce_check = dp_swlm_can_tcl_wr_coalesce,
172 };
173 
174 /**
175  * dp_swlm_tcl_flush_timer() - Timer handler for tcl register write coalescing
176  * @arg: private data of the timer
177  *
178  * Returns: none
179  */
dp_swlm_tcl_flush_timer(void * arg)180 static void dp_swlm_tcl_flush_timer(void *arg)
181 {
182 	struct dp_swlm_tcl_params *tcl = arg;
183 	struct dp_soc *soc = tcl->soc;
184 	struct dp_swlm *swlm = &soc->swlm;
185 	int ret;
186 
187 	ret = soc->arch_ops.dp_flush_tx_ring(soc->pdev_list[0], tcl->ring_id);
188 	if (ret) {
189 		DP_STATS_INC(swlm, tcl[tcl->ring_id].timer_flush_fail, 1);
190 		return;
191 	}
192 
193 	DP_STATS_INC(swlm, tcl[tcl->ring_id].timer_flush_success, 1);
194 }
195 
196 /**
197  * dp_soc_swlm_tcl_attach() - attach the TCL resources for the software
198  *			      latency manager.
199  * @soc: Datapath global soc handle
200  *
201  * Returns: QDF_STATUS
202  */
dp_soc_swlm_tcl_attach(struct dp_soc * soc)203 static inline QDF_STATUS dp_soc_swlm_tcl_attach(struct dp_soc *soc)
204 {
205 	struct dp_swlm *swlm = &soc->swlm;
206 	int i;
207 
208 	swlm->params.rx_traffic_thresh = DP_SWLM_TCL_RX_TRAFFIC_THRESH;
209 	swlm->params.tx_traffic_thresh = DP_SWLM_TCL_TX_TRAFFIC_THRESH;
210 	swlm->params.sampling_time = DP_SWLM_TCL_TRAFFIC_SAMPLING_TIME;
211 	swlm->params.time_flush_thresh = DP_SWLM_TCL_TIME_FLUSH_THRESH;
212 	swlm->params.tx_thresh_multiplier = DP_SWLM_TCL_TX_THRESH_MULTIPLIER;
213 	swlm->params.tx_pkt_thresh = DP_SWLM_TCL_TX_PKT_THRESH;
214 
215 	for (i = 0; i < soc->num_tcl_data_rings; i++) {
216 		swlm->params.tcl[i].soc = soc;
217 		swlm->params.tcl[i].ring_id = i;
218 		swlm->params.tcl[i].bytes_flush_thresh = 0;
219 		qdf_timer_init(soc->osdev,
220 			       &swlm->params.tcl[i].flush_timer,
221 			       dp_swlm_tcl_flush_timer,
222 			       (void *)&swlm->params.tcl[i],
223 			       QDF_TIMER_TYPE_WAKE_APPS);
224 	}
225 
226 	return QDF_STATUS_SUCCESS;
227 }
228 
229 /**
230  * dp_soc_swlm_tcl_detach() - detach the TCL resources for the software
231  *			      latency manager.
232  * @swlm: SWLM data pointer
233  * @ring_id: TCL ring id
234  *
235  * Returns: QDF_STATUS
236  */
dp_soc_swlm_tcl_detach(struct dp_swlm * swlm,uint8_t ring_id)237 static inline QDF_STATUS dp_soc_swlm_tcl_detach(struct dp_swlm *swlm,
238 						uint8_t ring_id)
239 {
240 	qdf_timer_stop(&swlm->params.tcl[ring_id].flush_timer);
241 	qdf_timer_free(&swlm->params.tcl[ring_id].flush_timer);
242 
243 	return QDF_STATUS_SUCCESS;
244 }
245 
dp_soc_swlm_attach(struct dp_soc * soc)246 QDF_STATUS dp_soc_swlm_attach(struct dp_soc *soc)
247 {
248 	struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx;
249 	struct dp_swlm *swlm = &soc->swlm;
250 	QDF_STATUS ret;
251 
252 	/* Check if it is enabled in the INI */
253 	if (!wlan_cfg_is_swlm_enabled(cfg)) {
254 		dp_err("SWLM feature is disabled");
255 		swlm->is_init = false;
256 		swlm->is_enabled = false;
257 		return QDF_STATUS_E_NOSUPPORT;
258 	}
259 
260 	swlm->ops = &dp_latency_mgr_ops;
261 
262 	ret = dp_soc_swlm_tcl_attach(soc);
263 	if (QDF_IS_STATUS_ERROR(ret))
264 		goto swlm_tcl_setup_fail;
265 
266 	swlm->is_init = true;
267 	swlm->is_enabled = true;
268 
269 	return QDF_STATUS_SUCCESS;
270 
271 swlm_tcl_setup_fail:
272 	swlm->is_enabled = false;
273 	return ret;
274 }
275 
dp_soc_swlm_detach(struct dp_soc * soc)276 QDF_STATUS dp_soc_swlm_detach(struct dp_soc *soc)
277 {
278 	struct dp_swlm *swlm = &soc->swlm;
279 	QDF_STATUS ret;
280 	int i;
281 
282 	if (!swlm->is_enabled)
283 		return QDF_STATUS_SUCCESS;
284 
285 	swlm->is_enabled = false;
286 
287 	for (i = 0; i < soc->num_tcl_data_rings; i++) {
288 		ret = dp_soc_swlm_tcl_detach(swlm, i);
289 		if (QDF_IS_STATUS_ERROR(ret))
290 			return ret;
291 	}
292 
293 	swlm->ops = NULL;
294 
295 	return QDF_STATUS_SUCCESS;
296 }
297 #endif /* WLAN_DP_FEATURE_SW_LATENCY_MGR */
298