1 /*
2  * Copyright (c) 2011, 2014-2019 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <qdf_net_types.h>      /* QDF_NBUF_EXEMPT_NO_EXEMPTION, etc. */
21 #include <qdf_nbuf.h>           /* qdf_nbuf_t, etc. */
22 #include <qdf_util.h>           /* qdf_assert */
23 #include <qdf_lock.h>           /* qdf_spinlock */
24 #include <qdf_trace.h>          /* qdf_tso_seg_dbg stuff */
25 #ifdef QCA_COMPUTE_TX_DELAY
26 #include <qdf_time.h>           /* qdf_system_ticks */
27 #endif
28 
29 #include <ol_htt_tx_api.h>      /* htt_tx_desc_id */
30 
31 #include <ol_tx_desc.h>
32 #include <ol_txrx_internal.h>
33 #ifdef QCA_SUPPORT_SW_TXRX_ENCAP
34 #include <ol_txrx_encap.h>      /* OL_TX_RESTORE_HDR, etc */
35 #endif
36 #include <ol_txrx.h>
37 
38 #ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS
ol_tx_desc_sanity_checks(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)39 static inline void ol_tx_desc_sanity_checks(struct ol_txrx_pdev_t *pdev,
40 					struct ol_tx_desc_t *tx_desc)
41 {
42 	if (tx_desc->pkt_type != ol_tx_frm_freed) {
43 		ol_txrx_err("Potential tx_desc corruption pkt_type:0x%x pdev:0x%pK",
44 			    tx_desc->pkt_type, pdev);
45 		qdf_assert(0);
46 	}
47 }
ol_tx_desc_reset_pkt_type(struct ol_tx_desc_t * tx_desc)48 static inline void ol_tx_desc_reset_pkt_type(struct ol_tx_desc_t *tx_desc)
49 {
50 	tx_desc->pkt_type = ol_tx_frm_freed;
51 }
52 #ifdef QCA_COMPUTE_TX_DELAY
ol_tx_desc_compute_delay(struct ol_tx_desc_t * tx_desc)53 static inline void ol_tx_desc_compute_delay(struct ol_tx_desc_t *tx_desc)
54 {
55 	if (tx_desc->entry_timestamp_ticks != 0xffffffff) {
56 		ol_txrx_err("Timestamp:0x%x", tx_desc->entry_timestamp_ticks);
57 		qdf_assert(0);
58 	}
59 	tx_desc->entry_timestamp_ticks = qdf_system_ticks();
60 }
ol_tx_desc_reset_timestamp(struct ol_tx_desc_t * tx_desc)61 static inline void ol_tx_desc_reset_timestamp(struct ol_tx_desc_t *tx_desc)
62 {
63 	tx_desc->entry_timestamp_ticks = 0xffffffff;
64 }
65 #endif
66 #else
ol_tx_desc_sanity_checks(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)67 static inline void ol_tx_desc_sanity_checks(struct ol_txrx_pdev_t *pdev,
68 						struct ol_tx_desc_t *tx_desc)
69 {
70 }
ol_tx_desc_reset_pkt_type(struct ol_tx_desc_t * tx_desc)71 static inline void ol_tx_desc_reset_pkt_type(struct ol_tx_desc_t *tx_desc)
72 {
73 }
ol_tx_desc_compute_delay(struct ol_tx_desc_t * tx_desc)74 static inline void ol_tx_desc_compute_delay(struct ol_tx_desc_t *tx_desc)
75 {
76 }
ol_tx_desc_reset_timestamp(struct ol_tx_desc_t * tx_desc)77 static inline void ol_tx_desc_reset_timestamp(struct ol_tx_desc_t *tx_desc)
78 {
79 }
80 #endif
81 
82 #ifdef DESC_TIMESTAMP_DEBUG_INFO
ol_tx_desc_update_tx_ts(struct ol_tx_desc_t * tx_desc)83 static inline void ol_tx_desc_update_tx_ts(struct ol_tx_desc_t *tx_desc)
84 {
85 	tx_desc->desc_debug_info.prev_tx_ts = tx_desc
86 						->desc_debug_info.curr_tx_ts;
87 	tx_desc->desc_debug_info.curr_tx_ts = qdf_get_log_timestamp();
88 }
89 #else
ol_tx_desc_update_tx_ts(struct ol_tx_desc_t * tx_desc)90 static inline void ol_tx_desc_update_tx_ts(struct ol_tx_desc_t *tx_desc)
91 {
92 }
93 #endif
94 
95 /**
96  * ol_tx_desc_vdev_update() - vedv assign.
97  * @tx_desc: tx descriptor pointer
98  * @vdev: vdev handle
99  *
100  * Return: None
101  */
102 static inline void
ol_tx_desc_vdev_update(struct ol_tx_desc_t * tx_desc,struct ol_txrx_vdev_t * vdev)103 ol_tx_desc_vdev_update(struct ol_tx_desc_t *tx_desc,
104 		       struct ol_txrx_vdev_t *vdev)
105 {
106 	tx_desc->vdev = vdev;
107 	tx_desc->vdev_id = vdev->vdev_id;
108 }
109 
110 #ifdef QCA_HL_NETDEV_FLOW_CONTROL
111 
112 /**
113  * ol_tx_desc_count_inc() - tx desc count increment for desc allocation.
114  * @vdev: vdev handle
115  *
116  * Return: None
117  */
118 static inline void
ol_tx_desc_count_inc(struct ol_txrx_vdev_t * vdev)119 ol_tx_desc_count_inc(struct ol_txrx_vdev_t *vdev)
120 {
121 	qdf_atomic_inc(&vdev->tx_desc_count);
122 }
123 #else
124 
125 static inline void
ol_tx_desc_count_inc(struct ol_txrx_vdev_t * vdev)126 ol_tx_desc_count_inc(struct ol_txrx_vdev_t *vdev)
127 {
128 }
129 
130 #endif
131 
132 #ifndef QCA_LL_TX_FLOW_CONTROL_V2
133 #ifdef QCA_LL_PDEV_TX_FLOW_CONTROL
134 /**
135  * ol_tx_do_pdev_flow_control_pause - pause queues when stop_th reached.
136  * @pdev: pdev handle
137  *
138  * Return: void
139  */
ol_tx_do_pdev_flow_control_pause(struct ol_txrx_pdev_t * pdev)140 static void ol_tx_do_pdev_flow_control_pause(struct ol_txrx_pdev_t *pdev)
141 {
142 	struct ol_txrx_vdev_t *vdev;
143 
144 	if (qdf_unlikely(pdev->tx_desc.num_free <
145 				pdev->tx_desc.stop_th &&
146 			pdev->tx_desc.num_free >=
147 			 pdev->tx_desc.stop_priority_th &&
148 			pdev->tx_desc.status ==
149 			 FLOW_POOL_ACTIVE_UNPAUSED)) {
150 		pdev->tx_desc.status = FLOW_POOL_NON_PRIO_PAUSED;
151 		/* pause network NON PRIORITY queues */
152 		TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
153 			pdev->pause_cb(vdev->vdev_id,
154 				       WLAN_STOP_NON_PRIORITY_QUEUE,
155 				       WLAN_DATA_FLOW_CONTROL);
156 		}
157 	} else if (qdf_unlikely((pdev->tx_desc.num_free <
158 				 pdev->tx_desc.stop_priority_th) &&
159 			pdev->tx_desc.status ==
160 			FLOW_POOL_NON_PRIO_PAUSED)) {
161 		pdev->tx_desc.status = FLOW_POOL_ACTIVE_PAUSED;
162 		/* pause priority queue */
163 		TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
164 			pdev->pause_cb(vdev->vdev_id,
165 				       WLAN_NETIF_PRIORITY_QUEUE_OFF,
166 				       WLAN_DATA_FLOW_CONTROL_PRIORITY);
167 		}
168 	}
169 }
170 
171 /**
172  * ol_tx_do_pdev_flow_control_unpause - unpause queues when start_th restored.
173  * @pdev: pdev handle
174  *
175  * Return: void
176  */
ol_tx_do_pdev_flow_control_unpause(struct ol_txrx_pdev_t * pdev)177 static void ol_tx_do_pdev_flow_control_unpause(struct ol_txrx_pdev_t *pdev)
178 {
179 	struct ol_txrx_vdev_t *vdev;
180 
181 	switch (pdev->tx_desc.status) {
182 	case FLOW_POOL_ACTIVE_PAUSED:
183 		if (pdev->tx_desc.num_free >
184 		    pdev->tx_desc.start_priority_th) {
185 			/* unpause priority queue */
186 			TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
187 				pdev->pause_cb(vdev->vdev_id,
188 				       WLAN_NETIF_PRIORITY_QUEUE_ON,
189 				       WLAN_DATA_FLOW_CONTROL_PRIORITY);
190 			}
191 			pdev->tx_desc.status = FLOW_POOL_NON_PRIO_PAUSED;
192 		}
193 		break;
194 	case FLOW_POOL_NON_PRIO_PAUSED:
195 		if (pdev->tx_desc.num_free > pdev->tx_desc.start_th) {
196 			TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
197 				pdev->pause_cb(vdev->vdev_id,
198 					       WLAN_WAKE_NON_PRIORITY_QUEUE,
199 					       WLAN_DATA_FLOW_CONTROL);
200 			}
201 			pdev->tx_desc.status = FLOW_POOL_ACTIVE_UNPAUSED;
202 		}
203 		break;
204 	case FLOW_POOL_INVALID:
205 		if (pdev->tx_desc.num_free == pdev->tx_desc.pool_size)
206 			ol_txrx_err("pool is INVALID State!!");
207 		break;
208 	case FLOW_POOL_ACTIVE_UNPAUSED:
209 		break;
210 	default:
211 		ol_txrx_err("pool is INACTIVE State!!");
212 		break;
213 	};
214 }
215 #else
216 static inline void
ol_tx_do_pdev_flow_control_pause(struct ol_txrx_pdev_t * pdev)217 ol_tx_do_pdev_flow_control_pause(struct ol_txrx_pdev_t *pdev)
218 {
219 }
220 
221 static inline void
ol_tx_do_pdev_flow_control_unpause(struct ol_txrx_pdev_t * pdev)222 ol_tx_do_pdev_flow_control_unpause(struct ol_txrx_pdev_t *pdev)
223 {
224 }
225 #endif
226 /**
227  * ol_tx_desc_alloc() - allocate descriptor from freelist
228  * @pdev: pdev handle
229  * @vdev: vdev handle
230  *
231  * Return: tx descriptor pointer/ NULL in case of error
232  */
233 static
ol_tx_desc_alloc(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev)234 struct ol_tx_desc_t *ol_tx_desc_alloc(struct ol_txrx_pdev_t *pdev,
235 					     struct ol_txrx_vdev_t *vdev)
236 {
237 	struct ol_tx_desc_t *tx_desc = NULL;
238 
239 	qdf_spin_lock_bh(&pdev->tx_mutex);
240 	if (pdev->tx_desc.freelist) {
241 		tx_desc = ol_tx_get_desc_global_pool(pdev);
242 		if (!tx_desc) {
243 			qdf_spin_unlock_bh(&pdev->tx_mutex);
244 			return NULL;
245 		}
246 		ol_tx_desc_dup_detect_set(pdev, tx_desc);
247 		ol_tx_do_pdev_flow_control_pause(pdev);
248 		ol_tx_desc_sanity_checks(pdev, tx_desc);
249 		ol_tx_desc_compute_delay(tx_desc);
250 		ol_tx_desc_vdev_update(tx_desc, vdev);
251 		ol_tx_desc_count_inc(vdev);
252 		ol_tx_desc_update_tx_ts(tx_desc);
253 		qdf_atomic_inc(&tx_desc->ref_cnt);
254 	}
255 	qdf_spin_unlock_bh(&pdev->tx_mutex);
256 	return tx_desc;
257 }
258 
259 /**
260  * ol_tx_desc_alloc_wrapper() -allocate tx descriptor
261  * @pdev: pdev handler
262  * @vdev: vdev handler
263  * @msdu_info: msdu handler
264  *
265  * Return: tx descriptor or NULL
266  */
267 struct ol_tx_desc_t *
ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,struct ol_txrx_msdu_info_t * msdu_info)268 ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev,
269 			 struct ol_txrx_vdev_t *vdev,
270 			 struct ol_txrx_msdu_info_t *msdu_info)
271 {
272 	return ol_tx_desc_alloc(pdev, vdev);
273 }
274 
275 #else
276 /**
277  * ol_tx_desc_alloc() -allocate tx descriptor
278  * @pdev: pdev handler
279  * @vdev: vdev handler
280  * @pool: flow pool
281  *
282  * Return: tx descriptor or NULL
283  */
284 static
ol_tx_desc_alloc(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,struct ol_tx_flow_pool_t * pool)285 struct ol_tx_desc_t *ol_tx_desc_alloc(struct ol_txrx_pdev_t *pdev,
286 				      struct ol_txrx_vdev_t *vdev,
287 				      struct ol_tx_flow_pool_t *pool)
288 {
289 	struct ol_tx_desc_t *tx_desc = NULL;
290 
291 	if (!pool) {
292 		pdev->pool_stats.pkt_drop_no_pool++;
293 		goto end;
294 	}
295 
296 	qdf_spin_lock_bh(&pool->flow_pool_lock);
297 	if (pool->avail_desc) {
298 		tx_desc = ol_tx_get_desc_flow_pool(pool);
299 		ol_tx_desc_dup_detect_set(pdev, tx_desc);
300 		if (qdf_unlikely(pool->avail_desc < pool->stop_th &&
301 				(pool->avail_desc >= pool->stop_priority_th) &&
302 				(pool->status == FLOW_POOL_ACTIVE_UNPAUSED))) {
303 			pool->status = FLOW_POOL_NON_PRIO_PAUSED;
304 			/* pause network NON PRIORITY queues */
305 			pdev->pause_cb(vdev->vdev_id,
306 				       WLAN_STOP_NON_PRIORITY_QUEUE,
307 				       WLAN_DATA_FLOW_CONTROL);
308 		} else if (qdf_unlikely((pool->avail_desc <
309 						pool->stop_priority_th) &&
310 				pool->status == FLOW_POOL_NON_PRIO_PAUSED)) {
311 			pool->status = FLOW_POOL_ACTIVE_PAUSED;
312 			/* pause priority queue */
313 			pdev->pause_cb(vdev->vdev_id,
314 				       WLAN_NETIF_PRIORITY_QUEUE_OFF,
315 				       WLAN_DATA_FLOW_CONTROL_PRIORITY);
316 		}
317 
318 		qdf_spin_unlock_bh(&pool->flow_pool_lock);
319 
320 		ol_tx_desc_sanity_checks(pdev, tx_desc);
321 		ol_tx_desc_compute_delay(tx_desc);
322 		ol_tx_desc_update_tx_ts(tx_desc);
323 		ol_tx_desc_vdev_update(tx_desc, vdev);
324 		qdf_atomic_inc(&tx_desc->ref_cnt);
325 	} else {
326 		pool->pkt_drop_no_desc++;
327 		qdf_spin_unlock_bh(&pool->flow_pool_lock);
328 	}
329 
330 end:
331 	return tx_desc;
332 }
333 
334 /**
335  * ol_tx_desc_alloc_wrapper() -allocate tx descriptor
336  * @pdev: pdev handler
337  * @vdev: vdev handler
338  * @msdu_info: msdu handler
339  *
340  * Return: tx descriptor or NULL
341  */
342 #ifdef QCA_LL_TX_FLOW_GLOBAL_MGMT_POOL
343 struct ol_tx_desc_t *
ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,struct ol_txrx_msdu_info_t * msdu_info)344 ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev,
345 			 struct ol_txrx_vdev_t *vdev,
346 			 struct ol_txrx_msdu_info_t *msdu_info)
347 {
348 	if (qdf_unlikely(msdu_info->htt.info.frame_type == htt_pkt_type_mgmt))
349 		return ol_tx_desc_alloc(pdev, vdev, pdev->mgmt_pool);
350 	else
351 		return ol_tx_desc_alloc(pdev, vdev, vdev->pool);
352 }
353 #else
354 struct ol_tx_desc_t *
ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,struct ol_txrx_msdu_info_t * msdu_info)355 ol_tx_desc_alloc_wrapper(struct ol_txrx_pdev_t *pdev,
356 			 struct ol_txrx_vdev_t *vdev,
357 			 struct ol_txrx_msdu_info_t *msdu_info)
358 {
359 	return ol_tx_desc_alloc(pdev, vdev, vdev->pool);
360 }
361 #endif
362 #endif
363 
364 /**
365  * ol_tx_desc_alloc_hl() - allocate tx descriptor
366  * @pdev: pdev handle
367  * @vdev: vdev handle
368  * @msdu_info: tx msdu info
369  *
370  * Return: tx descriptor pointer/ NULL in case of error
371  */
372 static struct ol_tx_desc_t *
ol_tx_desc_alloc_hl(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,struct ol_txrx_msdu_info_t * msdu_info)373 ol_tx_desc_alloc_hl(struct ol_txrx_pdev_t *pdev,
374 		    struct ol_txrx_vdev_t *vdev,
375 		    struct ol_txrx_msdu_info_t *msdu_info)
376 {
377 	struct ol_tx_desc_t *tx_desc;
378 
379 	tx_desc = ol_tx_desc_alloc_wrapper(pdev, vdev, msdu_info);
380 	if (!tx_desc)
381 		return NULL;
382 
383 	qdf_atomic_dec(&pdev->tx_queue.rsrc_cnt);
384 
385 	return tx_desc;
386 }
387 
388 #ifdef QCA_HL_NETDEV_FLOW_CONTROL
389 
390 /**
391  * ol_tx_desc_vdev_rm() - decrement the tx desc count for vdev.
392  * @tx_desc: tx desc
393  *
394  * Return: None
395  */
396 static inline void
ol_tx_desc_vdev_rm(struct ol_tx_desc_t * tx_desc)397 ol_tx_desc_vdev_rm(struct ol_tx_desc_t *tx_desc)
398 {
399 	/*
400 	 * In module exit context, vdev handle could be destroyed but still
401 	 * we need to free pending completion tx_desc.
402 	 */
403 	if (!tx_desc || !tx_desc->vdev)
404 		return;
405 
406 	qdf_atomic_dec(&tx_desc->vdev->tx_desc_count);
407 	tx_desc->vdev = NULL;
408 }
409 #else
410 
411 static inline void
ol_tx_desc_vdev_rm(struct ol_tx_desc_t * tx_desc)412 ol_tx_desc_vdev_rm(struct ol_tx_desc_t *tx_desc)
413 {
414 }
415 #endif
416 
417 #ifdef FEATURE_TSO
418 /**
419  * ol_tso_unmap_tso_segment() - Unmap TSO segment
420  * @pdev: pointer to ol_txrx_pdev_t structure
421  * @tx_desc: pointer to ol_tx_desc_t containing the TSO segment
422  *
423  * Unmap TSO segment (frag[1]). If it is the last TSO segment corresponding the
424  * nbuf, also unmap the EIT header(frag[0]).
425  *
426  * Return: None
427  */
ol_tso_unmap_tso_segment(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)428 static void ol_tso_unmap_tso_segment(struct ol_txrx_pdev_t *pdev,
429 						struct ol_tx_desc_t *tx_desc)
430 {
431 	bool is_last_seg = false;
432 	struct qdf_tso_num_seg_elem_t *tso_num_desc = NULL;
433 
434 	if (qdf_unlikely(!tx_desc->tso_desc)) {
435 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
436 			  "%s %d TSO desc is NULL!",
437 			  __func__, __LINE__);
438 		qdf_assert(0);
439 		return;
440 	} else if (qdf_unlikely(!tx_desc->tso_num_desc)) {
441 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
442 			  "%s %d TSO common info is NULL!",
443 			  __func__, __LINE__);
444 		qdf_assert(0);
445 		return;
446 	}
447 
448 	tso_num_desc = tx_desc->tso_num_desc;
449 
450 	qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex);
451 
452 	tso_num_desc->num_seg.tso_cmn_num_seg--;
453 	is_last_seg = (tso_num_desc->num_seg.tso_cmn_num_seg == 0) ?
454 								true : false;
455 	qdf_nbuf_unmap_tso_segment(pdev->osdev, tx_desc->tso_desc, is_last_seg);
456 
457 	qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
458 
459 }
460 
461 /**
462  * ol_tx_tso_desc_free() - Add TSO TX descs back to the freelist
463  * @pdev: pointer to ol_txrx_pdev_t structure
464  * @tx_desc: pointer to ol_tx_desc_t containing the TSO segment
465  *
466  * Add qdf_tso_seg_elem_t corresponding to the TSO seg back to freelist.
467  * If it is the last segment of the jumbo skb, also add the
468  * qdf_tso_num_seg_elem_t to the free list.
469  *
470  * Return: None
471  */
ol_tx_tso_desc_free(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)472 static void ol_tx_tso_desc_free(struct ol_txrx_pdev_t *pdev,
473 				struct ol_tx_desc_t *tx_desc)
474 {
475 	bool is_last_seg;
476 	struct qdf_tso_num_seg_elem_t *tso_num_desc = tx_desc->tso_num_desc;
477 
478 	is_last_seg = (tso_num_desc->num_seg.tso_cmn_num_seg == 0) ?
479 								true : false;
480 	if (is_last_seg) {
481 		ol_tso_num_seg_free(pdev, tx_desc->tso_num_desc);
482 		tx_desc->tso_num_desc = NULL;
483 	}
484 
485 	ol_tso_free_segment(pdev, tx_desc->tso_desc);
486 	tx_desc->tso_desc = NULL;
487 }
488 
489 #else
ol_tx_tso_desc_free(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)490 static inline void ol_tx_tso_desc_free(struct ol_txrx_pdev_t *pdev,
491 				       struct ol_tx_desc_t *tx_desc)
492 {
493 }
494 
ol_tso_unmap_tso_segment(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)495 static inline void ol_tso_unmap_tso_segment(
496 					struct ol_txrx_pdev_t *pdev,
497 					struct ol_tx_desc_t *tx_desc)
498 {
499 }
500 #endif
501 
502 /**
503  * ol_tx_desc_free_common() - common funcs to free tx_desc for all flow ctl vers
504  * @pdev: pdev handle
505  * @tx_desc: tx descriptor
506  *
507  * Set of common functions needed for QCA_LL_TX_FLOW_CONTROL_V2 and older
508  * versions of flow control. Needs to be called from within a spinlock.
509  *
510  * Return: None
511  */
ol_tx_desc_free_common(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)512 static void ol_tx_desc_free_common(struct ol_txrx_pdev_t *pdev,
513 						struct ol_tx_desc_t *tx_desc)
514 {
515 	ol_tx_desc_dup_detect_reset(pdev, tx_desc);
516 
517 	if (tx_desc->pkt_type == OL_TX_FRM_TSO)
518 		ol_tx_tso_desc_free(pdev, tx_desc);
519 
520 	ol_tx_desc_reset_pkt_type(tx_desc);
521 	ol_tx_desc_reset_timestamp(tx_desc);
522 	/* clear the ref cnt */
523 	qdf_atomic_init(&tx_desc->ref_cnt);
524 	tx_desc->vdev_id = OL_TXRX_INVALID_VDEV_ID;
525 }
526 
527 #ifndef QCA_LL_TX_FLOW_CONTROL_V2
528 /**
529  * ol_tx_desc_free() - put descriptor to freelist
530  * @pdev: pdev handle
531  * @tx_desc: tx descriptor
532  *
533  * Return: None
534  */
ol_tx_desc_free(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)535 void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc)
536 {
537 	qdf_spin_lock_bh(&pdev->tx_mutex);
538 
539 	ol_tx_desc_free_common(pdev, tx_desc);
540 
541 	ol_tx_put_desc_global_pool(pdev, tx_desc);
542 	ol_tx_desc_vdev_rm(tx_desc);
543 	ol_tx_do_pdev_flow_control_unpause(pdev);
544 
545 	qdf_spin_unlock_bh(&pdev->tx_mutex);
546 }
547 
548 #else
549 
550 /**
551  * ol_tx_update_free_desc_to_pool() - update free desc to pool
552  * @pdev: pdev handle
553  * @tx_desc: descriptor
554  *
555  * Return : 1 desc distribution required / 0 don't need distribution
556  */
557 #ifdef QCA_LL_TX_FLOW_CONTROL_RESIZE
ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)558 static inline bool ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t *pdev,
559 						  struct ol_tx_desc_t *tx_desc)
560 {
561 	struct ol_tx_flow_pool_t *pool = tx_desc->pool;
562 	bool distribute_desc = false;
563 
564 	if (unlikely(pool->overflow_desc)) {
565 		ol_tx_put_desc_global_pool(pdev, tx_desc);
566 		--pool->overflow_desc;
567 		distribute_desc = true;
568 	} else {
569 		ol_tx_put_desc_flow_pool(pool, tx_desc);
570 	}
571 
572 	return distribute_desc;
573 }
574 #else
ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)575 static inline bool ol_tx_update_free_desc_to_pool(struct ol_txrx_pdev_t *pdev,
576 						  struct ol_tx_desc_t *tx_desc)
577 {
578 	ol_tx_put_desc_flow_pool(tx_desc->pool, tx_desc);
579 	return false;
580 }
581 #endif
582 
583 /**
584  * ol_tx_desc_free() - put descriptor to pool freelist
585  * @pdev: pdev handle
586  * @tx_desc: tx descriptor
587  *
588  * Return: None
589  */
ol_tx_desc_free(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc)590 void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc)
591 {
592 	bool distribute_desc = false;
593 	struct ol_tx_flow_pool_t *pool = tx_desc->pool;
594 
595 	qdf_spin_lock_bh(&pool->flow_pool_lock);
596 
597 	ol_tx_desc_free_common(pdev, tx_desc);
598 	distribute_desc = ol_tx_update_free_desc_to_pool(pdev, tx_desc);
599 
600 	switch (pool->status) {
601 	case FLOW_POOL_ACTIVE_PAUSED:
602 		if (pool->avail_desc > pool->start_priority_th) {
603 			/* unpause priority queue */
604 			pdev->pause_cb(pool->member_flow_id,
605 			       WLAN_NETIF_PRIORITY_QUEUE_ON,
606 			       WLAN_DATA_FLOW_CONTROL_PRIORITY);
607 			pool->status = FLOW_POOL_NON_PRIO_PAUSED;
608 		}
609 		break;
610 	case FLOW_POOL_NON_PRIO_PAUSED:
611 		if (pool->avail_desc > pool->start_th) {
612 			pdev->pause_cb(pool->member_flow_id,
613 				       WLAN_WAKE_NON_PRIORITY_QUEUE,
614 				       WLAN_DATA_FLOW_CONTROL);
615 			pool->status = FLOW_POOL_ACTIVE_UNPAUSED;
616 		}
617 		break;
618 	case FLOW_POOL_INVALID:
619 		if (pool->avail_desc == pool->flow_pool_size) {
620 			qdf_spin_unlock_bh(&pool->flow_pool_lock);
621 			ol_tx_free_invalid_flow_pool(pool);
622 			qdf_print("pool is INVALID State!!");
623 			return;
624 		}
625 		break;
626 	case FLOW_POOL_ACTIVE_UNPAUSED:
627 		break;
628 	default:
629 		qdf_print("pool is INACTIVE State!!");
630 		break;
631 	};
632 
633 	qdf_spin_unlock_bh(&pool->flow_pool_lock);
634 
635 	if (unlikely(distribute_desc))
636 		ol_tx_distribute_descs_to_deficient_pools_from_global_pool();
637 
638 }
639 #endif
640 
641 const uint32_t htt_to_ce_pkt_type[] = {
642 	[htt_pkt_type_raw] = tx_pkt_type_raw,
643 	[htt_pkt_type_native_wifi] = tx_pkt_type_native_wifi,
644 	[htt_pkt_type_ethernet] = tx_pkt_type_802_3,
645 	[htt_pkt_type_mgmt] = tx_pkt_type_mgmt,
646 	[htt_pkt_type_eth2] = tx_pkt_type_eth2,
647 	[htt_pkt_num_types] = 0xffffffff
648 };
649 
650 #define WISA_DEST_PORT_6MBPS	50000
651 #define WISA_DEST_PORT_24MBPS	50001
652 
653 /**
654  * ol_tx_get_wisa_ext_hdr_type() - get header type for WiSA mode
655  * @netbuf: network buffer
656  *
657  * Return: extension header type
658  */
659 static enum extension_header_type
ol_tx_get_wisa_ext_hdr_type(qdf_nbuf_t netbuf)660 ol_tx_get_wisa_ext_hdr_type(qdf_nbuf_t netbuf)
661 {
662 	uint8_t *buf = qdf_nbuf_data(netbuf);
663 	uint16_t dport;
664 
665 	if (qdf_is_macaddr_group(
666 		(struct qdf_mac_addr *)(buf + QDF_NBUF_DEST_MAC_OFFSET))) {
667 
668 		dport = (uint16_t)(*(uint16_t *)(buf +
669 			QDF_NBUF_TRAC_IPV4_OFFSET +
670 			QDF_NBUF_TRAC_IPV4_HEADER_SIZE + sizeof(uint16_t)));
671 
672 		if (dport == QDF_SWAP_U16(WISA_DEST_PORT_6MBPS))
673 			return WISA_MODE_EXT_HEADER_6MBPS;
674 		else if (dport == QDF_SWAP_U16(WISA_DEST_PORT_24MBPS))
675 			return WISA_MODE_EXT_HEADER_24MBPS;
676 		else
677 			return EXT_HEADER_NOT_PRESENT;
678 	} else {
679 		return EXT_HEADER_NOT_PRESENT;
680 	}
681 }
682 
683 /**
684  * ol_tx_get_ext_header_type() - extension header is required or not
685  * @vdev: vdev pointer
686  * @netbuf: network buffer
687  *
688  * This function returns header type and if extension header is
689  * not required than returns EXT_HEADER_NOT_PRESENT.
690  *
691  * Return: extension header type
692  */
693 enum extension_header_type
ol_tx_get_ext_header_type(struct ol_txrx_vdev_t * vdev,qdf_nbuf_t netbuf)694 ol_tx_get_ext_header_type(struct ol_txrx_vdev_t *vdev,
695 	qdf_nbuf_t netbuf)
696 {
697 	if (vdev->is_wisa_mode_enable == true)
698 		return ol_tx_get_wisa_ext_hdr_type(netbuf);
699 	else
700 		return EXT_HEADER_NOT_PRESENT;
701 }
702 
ol_tx_desc_ll(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,qdf_nbuf_t netbuf,struct ol_txrx_msdu_info_t * msdu_info)703 struct ol_tx_desc_t *ol_tx_desc_ll(struct ol_txrx_pdev_t *pdev,
704 				   struct ol_txrx_vdev_t *vdev,
705 				   qdf_nbuf_t netbuf,
706 				   struct ol_txrx_msdu_info_t *msdu_info)
707 {
708 	struct ol_tx_desc_t *tx_desc;
709 	unsigned int i;
710 	uint32_t num_frags;
711 	enum extension_header_type type;
712 
713 	msdu_info->htt.info.vdev_id = vdev->vdev_id;
714 	msdu_info->htt.action.cksum_offload = qdf_nbuf_get_tx_cksum(netbuf);
715 	switch (qdf_nbuf_get_exemption_type(netbuf)) {
716 	case QDF_NBUF_EXEMPT_NO_EXEMPTION:
717 	case QDF_NBUF_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE:
718 		/* We want to encrypt this frame */
719 		msdu_info->htt.action.do_encrypt = 1;
720 		break;
721 	case QDF_NBUF_EXEMPT_ALWAYS:
722 		/* We don't want to encrypt this frame */
723 		msdu_info->htt.action.do_encrypt = 0;
724 		break;
725 	default:
726 		qdf_assert(0);
727 		break;
728 	}
729 
730 	/* allocate the descriptor */
731 	tx_desc = ol_tx_desc_alloc_wrapper(pdev, vdev, msdu_info);
732 	if (!tx_desc)
733 		return NULL;
734 
735 	/* initialize the SW tx descriptor */
736 	tx_desc->netbuf = netbuf;
737 
738 	if (msdu_info->tso_info.is_tso) {
739 		tx_desc->tso_desc = msdu_info->tso_info.curr_seg;
740 		tx_desc->tso_num_desc = msdu_info->tso_info.tso_num_seg_list;
741 		tx_desc->pkt_type = OL_TX_FRM_TSO;
742 		TXRX_STATS_MSDU_INCR(pdev, tx.tso.tso_pkts, netbuf);
743 	} else {
744 		tx_desc->pkt_type = OL_TX_FRM_STD;
745 	}
746 
747 	type = ol_tx_get_ext_header_type(vdev, netbuf);
748 
749 	/* initialize the HW tx descriptor */
750 	if (qdf_unlikely(htt_tx_desc_init(pdev->htt_pdev, tx_desc->htt_tx_desc,
751 			 tx_desc->htt_tx_desc_paddr,
752 			 ol_tx_desc_id(pdev, tx_desc), netbuf, &msdu_info->htt,
753 			 &msdu_info->tso_info, NULL, type))) {
754 		/*
755 		 * HTT Tx descriptor initialization failed.
756 		 * therefore, free the tx desc
757 		 */
758 		ol_tx_desc_free(pdev, tx_desc);
759 		return NULL;
760 	}
761 
762 	/*
763 	 * Initialize the fragmentation descriptor.
764 	 * Skip the prefix fragment (HTT tx descriptor) that was added
765 	 * during the call to htt_tx_desc_init above.
766 	 */
767 	num_frags = qdf_nbuf_get_num_frags(netbuf);
768 	/* num_frags are expected to be 2 max */
769 	num_frags = (num_frags > QDF_NBUF_CB_TX_MAX_EXTRA_FRAGS)
770 		? QDF_NBUF_CB_TX_MAX_EXTRA_FRAGS
771 		: num_frags;
772 #if defined(HELIUMPLUS)
773 	/*
774 	 * Use num_frags - 1, since 1 frag is used to store
775 	 * the HTT/HTC descriptor
776 	 * Refer to htt_tx_desc_init()
777 	 */
778 	htt_tx_desc_num_frags(pdev->htt_pdev, tx_desc->htt_frag_desc,
779 			      num_frags - 1);
780 #else /* ! defined(HELIUMPLUS) */
781 	htt_tx_desc_num_frags(pdev->htt_pdev, tx_desc->htt_tx_desc,
782 			      num_frags - 1);
783 #endif /* defined(HELIUMPLUS) */
784 
785 	if (msdu_info->tso_info.is_tso) {
786 		htt_tx_desc_fill_tso_info(pdev->htt_pdev,
787 			 tx_desc->htt_frag_desc, &msdu_info->tso_info);
788 		TXRX_STATS_TSO_SEG_UPDATE(pdev,
789 			 msdu_info->tso_info.msdu_stats_idx,
790 			 msdu_info->tso_info.curr_seg->seg);
791 	} else {
792 		for (i = 1; i < num_frags; i++) {
793 			qdf_size_t frag_len;
794 			qdf_dma_addr_t frag_paddr;
795 #ifdef HELIUMPLUS_DEBUG
796 			void *frag_vaddr;
797 
798 			frag_vaddr = qdf_nbuf_get_frag_vaddr(netbuf, i);
799 #endif
800 			frag_len = qdf_nbuf_get_frag_len(netbuf, i);
801 			frag_paddr = qdf_nbuf_get_frag_paddr(netbuf, i);
802 #if defined(HELIUMPLUS)
803 			htt_tx_desc_frag(pdev->htt_pdev, tx_desc->htt_frag_desc,
804 					 i - 1, frag_paddr, frag_len);
805 #if defined(HELIUMPLUS_DEBUG)
806 			qdf_debug("htt_fdesc=%pK frag=%d frag_vaddr=0x%pK frag_paddr=0x%llx len=%zu",
807 				  tx_desc->htt_frag_desc,
808 				  i-1, frag_vaddr, frag_paddr, frag_len);
809 			ol_txrx_dump_pkt(netbuf, frag_paddr, 64);
810 #endif /* HELIUMPLUS_DEBUG */
811 #else /* ! defined(HELIUMPLUS) */
812 			htt_tx_desc_frag(pdev->htt_pdev, tx_desc->htt_tx_desc,
813 					 i - 1, frag_paddr, frag_len);
814 #endif /* defined(HELIUMPLUS) */
815 		}
816 	}
817 
818 #if defined(HELIUMPLUS_DEBUG)
819 	ol_txrx_dump_frag_desc("ol_tx_desc_ll()", tx_desc);
820 #endif
821 	return tx_desc;
822 }
823 
824 struct ol_tx_desc_t *
ol_tx_desc_hl(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,qdf_nbuf_t netbuf,struct ol_txrx_msdu_info_t * msdu_info)825 ol_tx_desc_hl(
826 	struct ol_txrx_pdev_t *pdev,
827 	struct ol_txrx_vdev_t *vdev,
828 	qdf_nbuf_t netbuf,
829 	struct ol_txrx_msdu_info_t *msdu_info)
830 {
831 	struct ol_tx_desc_t *tx_desc;
832 
833 	/* FIX THIS: these inits should probably be done by tx classify */
834 	msdu_info->htt.info.vdev_id = vdev->vdev_id;
835 	msdu_info->htt.info.frame_type = pdev->htt_pkt_type;
836 	msdu_info->htt.action.cksum_offload = qdf_nbuf_get_tx_cksum(netbuf);
837 	switch (qdf_nbuf_get_exemption_type(netbuf)) {
838 	case QDF_NBUF_EXEMPT_NO_EXEMPTION:
839 	case QDF_NBUF_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE:
840 		/* We want to encrypt this frame */
841 		msdu_info->htt.action.do_encrypt = 1;
842 		break;
843 	case QDF_NBUF_EXEMPT_ALWAYS:
844 		/* We don't want to encrypt this frame */
845 		msdu_info->htt.action.do_encrypt = 0;
846 		break;
847 	default:
848 		qdf_assert(0);
849 		break;
850 	}
851 
852 	/* allocate the descriptor */
853 	tx_desc = ol_tx_desc_alloc_hl(pdev, vdev, msdu_info);
854 	if (!tx_desc)
855 		return NULL;
856 
857 	/* initialize the SW tx descriptor */
858 	tx_desc->netbuf = netbuf;
859 	/* fix this - get pkt_type from msdu_info */
860 	tx_desc->pkt_type = OL_TX_FRM_STD;
861 
862 #ifdef QCA_SUPPORT_SW_TXRX_ENCAP
863 	tx_desc->orig_l2_hdr_bytes = 0;
864 #endif
865 	/* the HW tx descriptor will be initialized later by the caller */
866 
867 	return tx_desc;
868 }
869 
ol_tx_desc_frame_list_free(struct ol_txrx_pdev_t * pdev,ol_tx_desc_list * tx_descs,int had_error)870 void ol_tx_desc_frame_list_free(struct ol_txrx_pdev_t *pdev,
871 				ol_tx_desc_list *tx_descs, int had_error)
872 {
873 	struct ol_tx_desc_t *tx_desc, *tmp;
874 	qdf_nbuf_t msdus = NULL;
875 
876 	TAILQ_FOREACH_SAFE(tx_desc, tx_descs, tx_desc_list_elem, tmp) {
877 		qdf_nbuf_t msdu = tx_desc->netbuf;
878 
879 		qdf_atomic_init(&tx_desc->ref_cnt);   /* clear the ref cnt */
880 #ifdef QCA_SUPPORT_SW_TXRX_ENCAP
881 		/* restore original hdr offset */
882 		OL_TX_RESTORE_HDR(tx_desc, msdu);
883 #endif
884 
885 		/*
886 		 * In MCC IPA tx context, IPA driver provides skb with directly
887 		 * DMA mapped address. In such case, there's no need for WLAN
888 		 * driver to DMA unmap the skb.
889 		 */
890 		if (qdf_nbuf_get_users(msdu) <= 1) {
891 			if (!qdf_nbuf_ipa_owned_get(msdu))
892 				qdf_nbuf_unmap(pdev->osdev, msdu,
893 					       QDF_DMA_TO_DEVICE);
894 		}
895 
896 		/* free the tx desc */
897 		ol_tx_desc_free(pdev, tx_desc);
898 		/* link the netbuf into a list to free as a batch */
899 		qdf_nbuf_set_next(msdu, msdus);
900 		msdus = msdu;
901 	}
902 	/* free the netbufs as a batch */
903 	qdf_nbuf_tx_free(msdus, had_error);
904 }
905 
ol_tx_desc_frame_free_nonstd(struct ol_txrx_pdev_t * pdev,struct ol_tx_desc_t * tx_desc,int had_error)906 void ol_tx_desc_frame_free_nonstd(struct ol_txrx_pdev_t *pdev,
907 				  struct ol_tx_desc_t *tx_desc, int had_error)
908 {
909 	int mgmt_type;
910 	ol_txrx_mgmt_tx_cb ota_ack_cb;
911 
912 	qdf_atomic_init(&tx_desc->ref_cnt);     /* clear the ref cnt */
913 #ifdef QCA_SUPPORT_SW_TXRX_ENCAP
914 	/* restore original hdr offset */
915 	OL_TX_RESTORE_HDR(tx_desc, (tx_desc->netbuf));
916 #endif
917 	if (tx_desc->pkt_type == OL_TX_FRM_NO_FREE) {
918 
919 		/* free the tx desc but don't unmap or free the frame */
920 		if (pdev->tx_data_callback.func) {
921 			qdf_nbuf_set_next(tx_desc->netbuf, NULL);
922 			pdev->tx_data_callback.func(pdev->tx_data_callback.ctxt,
923 						    tx_desc->netbuf, had_error);
924 			goto free_tx_desc;
925 		}
926 		/* let the code below unmap and free the frame */
927 	}
928 	if (tx_desc->pkt_type == OL_TX_FRM_TSO)
929 		ol_tso_unmap_tso_segment(pdev, tx_desc);
930 	else
931 		qdf_nbuf_unmap(pdev->osdev, tx_desc->netbuf, QDF_DMA_TO_DEVICE);
932 	/* check the frame type to see what kind of special steps are needed */
933 	if ((tx_desc->pkt_type >= OL_TXRX_MGMT_TYPE_BASE) &&
934 		   (tx_desc->pkt_type != ol_tx_frm_freed)) {
935 		qdf_dma_addr_t frag_desc_paddr = 0;
936 
937 #if defined(HELIUMPLUS)
938 		frag_desc_paddr = tx_desc->htt_frag_desc_paddr;
939 		/* FIX THIS -
940 		 * The FW currently has trouble using the host's fragments
941 		 * table for management frames.  Until this is fixed,
942 		 * rather than specifying the fragment table to the FW,
943 		 * the host SW will specify just the address of the initial
944 		 * fragment.
945 		 * Now that the mgmt frame is done, the HTT tx desc's frags
946 		 * table pointer needs to be reset.
947 		 */
948 #if defined(HELIUMPLUS_DEBUG)
949 		qdf_print("Frag Descriptor Reset [%d] to 0x%x",
950 			  tx_desc->id,
951 			  frag_desc_paddr);
952 #endif /* HELIUMPLUS_DEBUG */
953 #endif /* HELIUMPLUS */
954 		htt_tx_desc_frags_table_set(pdev->htt_pdev,
955 					    tx_desc->htt_tx_desc, 0,
956 					    frag_desc_paddr, 1);
957 
958 		mgmt_type = tx_desc->pkt_type - OL_TXRX_MGMT_TYPE_BASE;
959 		/*
960 		 *  we already checked the value when the mgmt frame was
961 		 *  provided to the txrx layer.
962 		 *  no need to check it a 2nd time.
963 		 */
964 		ota_ack_cb = pdev->tx_mgmt_cb.ota_ack_cb;
965 		if (ota_ack_cb) {
966 			void *ctxt;
967 			ctxt = pdev->tx_mgmt_cb.ctxt;
968 			ota_ack_cb(ctxt, tx_desc->netbuf, had_error);
969 		}
970 	} else if (had_error == htt_tx_status_download_fail) {
971 		/* Failed to send to target */
972 		goto free_tx_desc;
973 	} else {
974 		/* single regular frame, called from completion path */
975 		qdf_nbuf_set_next(tx_desc->netbuf, NULL);
976 		qdf_nbuf_tx_free(tx_desc->netbuf, had_error);
977 	}
978 free_tx_desc:
979 	/* free the tx desc */
980 	ol_tx_desc_free(pdev, tx_desc);
981 }
982 
983 #if defined(FEATURE_TSO)
984 #ifdef TSOSEG_DEBUG
985 static int
ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t * tsoseg)986 ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t *tsoseg)
987 {
988 	int rc = -1;
989 	struct ol_tx_desc_t *txdesc;
990 
991 	if (tsoseg) {
992 		txdesc = tsoseg->dbg.txdesc;
993 		/* Don't validate if TX desc is NULL*/
994 		if (!txdesc)
995 			return 0;
996 		if (txdesc->tso_desc != tsoseg)
997 			qdf_tso_seg_dbg_bug("Owner sanity failed");
998 		else
999 			rc = 0;
1000 	}
1001 	return rc;
1002 
1003 };
1004 #else
1005 static int
ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t * tsoseg)1006 ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t *tsoseg)
1007 {
1008 	return 0;
1009 }
1010 #endif /* TSOSEG_DEBUG */
1011 
1012 /**
1013  * ol_tso_alloc_segment() - function to allocate a TSO segment
1014  * element
1015  * @pdev: the data physical device sending the data
1016  *
1017  * Allocates a TSO segment element from the free list held in
1018  * the pdev
1019  *
1020  * Return: tso_seg
1021  */
ol_tso_alloc_segment(struct ol_txrx_pdev_t * pdev)1022 struct qdf_tso_seg_elem_t *ol_tso_alloc_segment(struct ol_txrx_pdev_t *pdev)
1023 {
1024 	struct qdf_tso_seg_elem_t *tso_seg = NULL;
1025 
1026 	qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex);
1027 	if (pdev->tso_seg_pool.freelist) {
1028 		pdev->tso_seg_pool.num_free--;
1029 		tso_seg = pdev->tso_seg_pool.freelist;
1030 		if (tso_seg->on_freelist != 1) {
1031 			qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1032 			qdf_print("tso seg alloc failed: not in freelist");
1033 			QDF_BUG(0);
1034 			return NULL;
1035 		} else if (tso_seg->cookie != TSO_SEG_MAGIC_COOKIE) {
1036 			qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1037 			qdf_print("tso seg alloc failed: bad cookie");
1038 			QDF_BUG(0);
1039 			return NULL;
1040 		}
1041 		/*this tso seg is not a part of freelist now.*/
1042 		tso_seg->on_freelist = 0;
1043 		tso_seg->sent_to_target = 0;
1044 		tso_seg->force_free = 0;
1045 		pdev->tso_seg_pool.freelist = pdev->tso_seg_pool.freelist->next;
1046 		qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_ALLOC);
1047 	}
1048 	qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1049 
1050 	return tso_seg;
1051 }
1052 
1053 /**
1054  * ol_tso_free_segment() - function to free a TSO segment
1055  * element
1056  * @pdev: the data physical device sending the data
1057  * @tso_seg: The TSO segment element to be freed
1058  *
1059  * Returns a TSO segment element to the free list held in the
1060  * pdev
1061  *
1062  * Return: none
1063  */
ol_tso_free_segment(struct ol_txrx_pdev_t * pdev,struct qdf_tso_seg_elem_t * tso_seg)1064 void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev,
1065 	 struct qdf_tso_seg_elem_t *tso_seg)
1066 {
1067 	qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex);
1068 	if (tso_seg->on_freelist != 0) {
1069 		qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1070 		qdf_print("Do not free tso seg, already freed");
1071 		QDF_BUG(0);
1072 		return;
1073 	} else if (tso_seg->cookie != TSO_SEG_MAGIC_COOKIE) {
1074 		qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1075 		qdf_print("Do not free tso seg: cookie is not good.");
1076 		QDF_BUG(0);
1077 		return;
1078 	} else if ((tso_seg->sent_to_target != 1) &&
1079 		   (tso_seg->force_free != 1)) {
1080 		qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1081 		qdf_print("Do not free tso seg:  yet to be sent to target");
1082 		QDF_BUG(0);
1083 		return;
1084 	}
1085 	/* sanitize before free */
1086 	ol_tso_seg_dbg_sanitize(tso_seg);
1087 	qdf_tso_seg_dbg_setowner(tso_seg, NULL);
1088 	/*this tso seg is now a part of freelist*/
1089 	/* retain segment history, if debug is enabled */
1090 	qdf_tso_seg_dbg_zero(tso_seg);
1091 	tso_seg->next = pdev->tso_seg_pool.freelist;
1092 	tso_seg->on_freelist = 1;
1093 	tso_seg->sent_to_target = 0;
1094 	tso_seg->cookie = TSO_SEG_MAGIC_COOKIE;
1095 	pdev->tso_seg_pool.freelist = tso_seg;
1096 	pdev->tso_seg_pool.num_free++;
1097 	qdf_tso_seg_dbg_record(tso_seg, tso_seg->force_free
1098 			       ? TSOSEG_LOC_FORCE_FREE
1099 			       : TSOSEG_LOC_FREE);
1100 	tso_seg->force_free = 0;
1101 	qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
1102 }
1103 
1104 /**
1105  * ol_tso_num_seg_alloc() - function to allocate a element to count TSO segments
1106  *			    in a jumbo skb packet.
1107  * @pdev: the data physical device sending the data
1108  *
1109  * Allocates a element to count TSO segments from the free list held in
1110  * the pdev
1111  *
1112  * Return: tso_num_seg
1113  */
ol_tso_num_seg_alloc(struct ol_txrx_pdev_t * pdev)1114 struct qdf_tso_num_seg_elem_t *ol_tso_num_seg_alloc(struct ol_txrx_pdev_t *pdev)
1115 {
1116 	struct qdf_tso_num_seg_elem_t *tso_num_seg = NULL;
1117 
1118 	qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
1119 	if (pdev->tso_num_seg_pool.freelist) {
1120 		pdev->tso_num_seg_pool.num_free--;
1121 		tso_num_seg = pdev->tso_num_seg_pool.freelist;
1122 		pdev->tso_num_seg_pool.freelist =
1123 				pdev->tso_num_seg_pool.freelist->next;
1124 	}
1125 	qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
1126 
1127 	return tso_num_seg;
1128 }
1129 
1130 /**
1131  * ol_tso_num_seg_free() - function to free a TSO segment
1132  * element
1133  * @pdev: the data physical device sending the data
1134  * @tso_seg: The TSO segment element to be freed
1135  *
1136  * Returns a element to the free list held in the pdev
1137  *
1138  * Return: none
1139  */
ol_tso_num_seg_free(struct ol_txrx_pdev_t * pdev,struct qdf_tso_num_seg_elem_t * tso_num_seg)1140 void ol_tso_num_seg_free(struct ol_txrx_pdev_t *pdev,
1141 	 struct qdf_tso_num_seg_elem_t *tso_num_seg)
1142 {
1143 	qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
1144 	tso_num_seg->next = pdev->tso_num_seg_pool.freelist;
1145 	pdev->tso_num_seg_pool.freelist = tso_num_seg;
1146 		pdev->tso_num_seg_pool.num_free++;
1147 	qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
1148 }
1149 #endif
1150