1 /*
2 * Copyright (c) 2011-2019 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
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_atomic.h> /* qdf_atomic_inc, etc. */
21 #include <qdf_lock.h> /* qdf_os_spinlock */
22 #include <qdf_time.h> /* qdf_system_ticks, etc. */
23 #include <qdf_nbuf.h> /* qdf_nbuf_t */
24 #include <qdf_net_types.h> /* QDF_NBUF_TX_EXT_TID_INVALID */
25
26 #include "queue.h" /* TAILQ */
27 #ifdef QCA_COMPUTE_TX_DELAY
28 #include <enet.h> /* ethernet_hdr_t, etc. */
29 #include <ipv6_defs.h> /* ipv6_traffic_class */
30 #endif
31
32 #include <ol_txrx_api.h> /* ol_txrx_vdev_handle, etc. */
33 #include <ol_htt_tx_api.h> /* htt_tx_compl_desc_id */
34 #include <ol_txrx_htt_api.h> /* htt_tx_status */
35
36 #include <ol_ctrl_txrx_api.h>
37 #include <cdp_txrx_tx_delay.h>
38 #include <ol_txrx_types.h> /* ol_txrx_vdev_t, etc */
39 #include <ol_tx_desc.h> /* ol_tx_desc_find, ol_tx_desc_frame_free */
40 #ifdef QCA_COMPUTE_TX_DELAY
41 #include <ol_tx_classify.h> /* ol_tx_dest_addr_find */
42 #endif
43 #include <ol_txrx_internal.h> /* OL_TX_DESC_NO_REFS, etc. */
44 #include <ol_osif_txrx_api.h>
45 #include <ol_tx.h> /* ol_tx_reinject */
46 #include <ol_tx_send.h>
47
48 #include <ol_cfg.h> /* ol_cfg_is_high_latency */
49 #include <ol_tx_sched.h>
50 #ifdef QCA_SUPPORT_SW_TXRX_ENCAP
51 #include <ol_txrx_encap.h> /* OL_TX_RESTORE_HDR, etc */
52 #endif
53 #include <ol_tx_queue.h>
54 #include <ol_txrx.h>
55 #include <pktlog_ac_fmt.h>
56 #include <cdp_txrx_handle.h>
57
ol_tx_init_pdev(ol_txrx_pdev_handle pdev)58 void ol_tx_init_pdev(ol_txrx_pdev_handle pdev)
59 {
60 qdf_atomic_add(ol_cfg_target_tx_credit(pdev->ctrl_pdev),
61 &pdev->target_tx_credit);
62 }
63
ol_tx_reinject(struct ol_txrx_vdev_t * vdev,qdf_nbuf_t msdu,uint16_t peer_id)64 qdf_nbuf_t ol_tx_reinject(struct ol_txrx_vdev_t *vdev,
65 qdf_nbuf_t msdu, uint16_t peer_id)
66 {
67 struct ol_tx_desc_t *tx_desc = NULL;
68 struct ol_txrx_msdu_info_t msdu_info;
69
70 msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type;
71 msdu_info.htt.info.ext_tid = HTT_TX_EXT_TID_INVALID;
72 msdu_info.peer = NULL;
73 msdu_info.htt.action.tx_comp_req = 0;
74 msdu_info.tso_info.is_tso = 0;
75
76 tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info);
77 if (!tx_desc)
78 return msdu;
79
80 HTT_TX_DESC_POSTPONED_SET(*((uint32_t *)(tx_desc->htt_tx_desc)), true);
81
82 htt_tx_desc_set_peer_id(tx_desc->htt_tx_desc, peer_id);
83
84 ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
85
86 return NULL;
87 }
88
89 /*
90 * The TXRX module doesn't accept tx frames unless the target has
91 * enough descriptors for them.
92 * For LL, the TXRX descriptor pool is sized to match the target's
93 * descriptor pool. Hence, if the descriptor allocation in TXRX
94 * succeeds, that guarantees that the target has room to accept
95 * the new tx frame.
96 */
97 struct ol_tx_desc_t *
ol_tx_prepare_ll(ol_txrx_vdev_handle vdev,qdf_nbuf_t msdu,struct ol_txrx_msdu_info_t * msdu_info)98 ol_tx_prepare_ll(ol_txrx_vdev_handle vdev,
99 qdf_nbuf_t msdu,
100 struct ol_txrx_msdu_info_t *msdu_info)
101 {
102 struct ol_tx_desc_t *tx_desc;
103 struct ol_txrx_pdev_t *pdev = vdev->pdev;
104
105 (msdu_info)->htt.info.frame_type = pdev->htt_pkt_type;
106 tx_desc = ol_tx_desc_ll(pdev, vdev, msdu, msdu_info);
107 if (qdf_unlikely(!tx_desc)) {
108 /*
109 * If TSO packet, free associated
110 * remaining TSO segment descriptors
111 */
112 if (qdf_nbuf_is_tso(msdu))
113 ol_free_remaining_tso_segs(
114 vdev, msdu_info, true);
115 TXRX_STATS_MSDU_LIST_INCR(
116 pdev, tx.dropped.host_reject, msdu);
117 return NULL;
118 }
119
120 return tx_desc;
121 }
122
123 qdf_nbuf_t
ol_tx_non_std_ll(struct ol_txrx_vdev_t * vdev,enum ol_tx_spec tx_spec,qdf_nbuf_t msdu_list)124 ol_tx_non_std_ll(struct ol_txrx_vdev_t *vdev,
125 enum ol_tx_spec tx_spec,
126 qdf_nbuf_t msdu_list)
127 {
128 qdf_nbuf_t msdu = msdu_list;
129 htt_pdev_handle htt_pdev = vdev->pdev->htt_pdev;
130 struct ol_txrx_msdu_info_t msdu_info;
131
132 msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type;
133 msdu_info.htt.action.tx_comp_req = 0;
134
135 /*
136 * The msdu_list variable could be used instead of the msdu var,
137 * but just to clarify which operations are done on a single MSDU
138 * vs. a list of MSDUs, use a distinct variable for single MSDUs
139 * within the list.
140 */
141 while (msdu) {
142 qdf_nbuf_t next;
143 struct ol_tx_desc_t *tx_desc = NULL;
144
145 msdu_info.htt.info.ext_tid = qdf_nbuf_get_tid(msdu);
146 msdu_info.peer = NULL;
147 msdu_info.tso_info.is_tso = 0;
148
149 tx_desc = ol_tx_prepare_ll(vdev, msdu, &msdu_info);
150 if (!tx_desc)
151 return msdu;
152
153 /*
154 * The netbuf may get linked into a different list inside the
155 * ol_tx_send function, so store the next pointer before the
156 * tx_send call.
157 */
158 next = qdf_nbuf_next(msdu);
159
160 if (tx_spec != OL_TX_SPEC_STD) {
161 if (tx_spec & OL_TX_SPEC_NO_FREE) {
162 tx_desc->pkt_type = OL_TX_FRM_NO_FREE;
163 } else if (tx_spec & OL_TX_SPEC_TSO) {
164 tx_desc->pkt_type = OL_TX_FRM_TSO;
165 } else if (tx_spec & OL_TX_SPEC_NWIFI_NO_ENCRYPT) {
166 uint8_t sub_type =
167 ol_txrx_tx_raw_subtype(tx_spec);
168 htt_tx_desc_type(htt_pdev, tx_desc->htt_tx_desc,
169 htt_pkt_type_native_wifi,
170 sub_type);
171 } else if (ol_txrx_tx_is_raw(tx_spec)) {
172 /* different types of raw frames */
173 uint8_t sub_type =
174 ol_txrx_tx_raw_subtype(tx_spec);
175 htt_tx_desc_type(htt_pdev, tx_desc->htt_tx_desc,
176 htt_pkt_type_raw, sub_type);
177 }
178 }
179 /*
180 * If debug display is enabled, show the meta-data being
181 * downloaded to the target via the HTT tx descriptor.
182 */
183 htt_tx_desc_display(tx_desc->htt_tx_desc);
184 ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
185 msdu = next;
186 }
187 return NULL; /* all MSDUs were accepted */
188 }
189
ol_tx_trace_pkt(qdf_nbuf_t skb,uint16_t msdu_id,uint8_t vdev_id,enum QDF_OPMODE op_mode)190 void ol_tx_trace_pkt(qdf_nbuf_t skb, uint16_t msdu_id, uint8_t vdev_id,
191 enum QDF_OPMODE op_mode)
192 {
193 DPTRACE(qdf_dp_trace_ptr(skb,
194 QDF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD,
195 QDF_TRACE_DEFAULT_PDEV_ID,
196 qdf_nbuf_data_addr(skb),
197 sizeof(qdf_nbuf_data(skb)),
198 msdu_id, vdev_id, 0,
199 op_mode));
200
201 qdf_dp_trace_log_pkt(vdev_id, skb, QDF_TX, QDF_TRACE_DEFAULT_PDEV_ID,
202 op_mode);
203
204 DPTRACE(qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID,
205 QDF_DP_TRACE_TX_PACKET_RECORD,
206 msdu_id, QDF_TX));
207 }
208
209 #if defined(HELIUMPLUS)
ol_txrx_dump_frag_desc(char * msg,struct ol_tx_desc_t * tx_desc)210 void ol_txrx_dump_frag_desc(char *msg, struct ol_tx_desc_t *tx_desc)
211 {
212 uint32_t *frag_ptr_i_p;
213 int i;
214
215 ol_txrx_err("OL TX Descriptor 0x%pK msdu_id %d",
216 tx_desc, tx_desc->id);
217 ol_txrx_err("HTT TX Descriptor vaddr: 0x%pK paddr: %pad",
218 tx_desc->htt_tx_desc, &tx_desc->htt_tx_desc_paddr);
219 ol_txrx_err("Fragment Descriptor 0x%pK (paddr=%pad)",
220 tx_desc->htt_frag_desc, &tx_desc->htt_frag_desc_paddr);
221
222 /*
223 * it looks from htt_tx_desc_frag() that tx_desc->htt_frag_desc
224 * is already de-referrable (=> in virtual address space)
225 */
226 frag_ptr_i_p = tx_desc->htt_frag_desc;
227
228 /* Dump 6 words of TSO flags */
229 print_hex_dump(KERN_DEBUG, "MLE Desc:TSO Flags: ",
230 DUMP_PREFIX_NONE, 8, 4,
231 frag_ptr_i_p, 24, true);
232
233 frag_ptr_i_p += 6; /* Skip 6 words of TSO flags */
234
235 i = 0;
236 while (*frag_ptr_i_p) {
237 print_hex_dump(KERN_DEBUG, "MLE Desc:Frag Ptr: ",
238 DUMP_PREFIX_NONE, 8, 4,
239 frag_ptr_i_p, 8, true);
240 i++;
241 if (i > 5) /* max 6 times: frag_ptr0 to frag_ptr5 */
242 break;
243 /* jump to next pointer - skip length */
244 frag_ptr_i_p += 2;
245 }
246 }
247 #endif /* HELIUMPLUS */
248
249 struct ol_tx_desc_t *
ol_txrx_mgmt_tx_desc_alloc(struct ol_txrx_pdev_t * pdev,struct ol_txrx_vdev_t * vdev,qdf_nbuf_t tx_mgmt_frm,struct ol_txrx_msdu_info_t * tx_msdu_info)250 ol_txrx_mgmt_tx_desc_alloc(
251 struct ol_txrx_pdev_t *pdev,
252 struct ol_txrx_vdev_t *vdev,
253 qdf_nbuf_t tx_mgmt_frm,
254 struct ol_txrx_msdu_info_t *tx_msdu_info)
255 {
256 struct ol_tx_desc_t *tx_desc;
257
258 /* For LL tx_comp_req is not used so initialized to 0 */
259 tx_msdu_info->htt.action.tx_comp_req = 0;
260 tx_desc = ol_tx_desc_ll(pdev, vdev, tx_mgmt_frm, tx_msdu_info);
261 /* FIX THIS -
262 * The FW currently has trouble using the host's fragments table
263 * for management frames. Until this is fixed, rather than
264 * specifying the fragment table to the FW, specify just the
265 * address of the initial fragment.
266 */
267 #if defined(HELIUMPLUS)
268 /* ol_txrx_dump_frag_desc("ol_txrx_mgmt_send(): after ol_tx_desc_ll",
269 * tx_desc);
270 */
271 #endif /* defined(HELIUMPLUS) */
272 if (tx_desc) {
273 /*
274 * Following the call to ol_tx_desc_ll, frag 0 is the
275 * HTT tx HW descriptor, and the frame payload is in
276 * frag 1.
277 */
278 htt_tx_desc_frags_table_set(
279 pdev->htt_pdev,
280 tx_desc->htt_tx_desc,
281 qdf_nbuf_get_frag_paddr(tx_mgmt_frm, 1),
282 0, 0);
283 #if defined(HELIUMPLUS) && defined(HELIUMPLUS_DEBUG)
284 ol_txrx_dump_frag_desc(
285 "after htt_tx_desc_frags_table_set",
286 tx_desc);
287 #endif /* defined(HELIUMPLUS) */
288 }
289
290 return tx_desc;
291 }
292
ol_txrx_mgmt_send_frame(struct ol_txrx_vdev_t * vdev,struct ol_tx_desc_t * tx_desc,qdf_nbuf_t tx_mgmt_frm,struct ol_txrx_msdu_info_t * tx_msdu_info,uint16_t chanfreq)293 int ol_txrx_mgmt_send_frame(
294 struct ol_txrx_vdev_t *vdev,
295 struct ol_tx_desc_t *tx_desc,
296 qdf_nbuf_t tx_mgmt_frm,
297 struct ol_txrx_msdu_info_t *tx_msdu_info,
298 uint16_t chanfreq)
299 {
300 struct ol_txrx_pdev_t *pdev = vdev->pdev;
301
302 htt_tx_desc_set_chanfreq(tx_desc->htt_tx_desc, chanfreq);
303 QDF_NBUF_CB_TX_PACKET_TRACK(tx_desc->netbuf) =
304 QDF_NBUF_TX_PKT_MGMT_TRACK;
305 ol_tx_send_nonstd(pdev, tx_desc, tx_mgmt_frm,
306 htt_pkt_type_mgmt);
307
308 return 0;
309 }
310
311 #if defined(FEATURE_TSO)
ol_free_remaining_tso_segs(ol_txrx_vdev_handle vdev,struct ol_txrx_msdu_info_t * msdu_info,bool is_tso_seg_mapping_done)312 void ol_free_remaining_tso_segs(ol_txrx_vdev_handle vdev,
313 struct ol_txrx_msdu_info_t *msdu_info,
314 bool is_tso_seg_mapping_done)
315 {
316 struct qdf_tso_seg_elem_t *next_seg;
317 struct qdf_tso_seg_elem_t *free_seg = msdu_info->tso_info.curr_seg;
318 struct ol_txrx_pdev_t *pdev;
319 bool is_last_seg = false;
320
321 if (qdf_unlikely(!vdev)) {
322 ol_txrx_err("vdev is null");
323 return;
324 }
325
326 pdev = vdev->pdev;
327 if (qdf_unlikely(!pdev)) {
328 ol_txrx_err("pdev is null");
329 return;
330 }
331
332 /*
333 * TSO segment are mapped already, therefore,
334 * 1. unmap the tso segments,
335 * 2. free tso num segment if it is a last segment, and
336 * 3. free the tso segments.
337 */
338
339 if (is_tso_seg_mapping_done) {
340 struct qdf_tso_num_seg_elem_t *tso_num_desc =
341 msdu_info->tso_info.tso_num_seg_list;
342
343 if (qdf_unlikely(!tso_num_desc)) {
344 ol_txrx_err("TSO common info is NULL!");
345 return;
346 }
347
348 while (free_seg) {
349 qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex);
350 tso_num_desc->num_seg.tso_cmn_num_seg--;
351
352 is_last_seg = (tso_num_desc->num_seg.tso_cmn_num_seg ==
353 0) ? true : false;
354 qdf_nbuf_unmap_tso_segment(pdev->osdev, free_seg,
355 is_last_seg);
356 qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
357
358 if (is_last_seg) {
359 ol_tso_num_seg_free(pdev,
360 msdu_info->tso_info.
361 tso_num_seg_list);
362 msdu_info->tso_info.tso_num_seg_list = NULL;
363 }
364
365 next_seg = free_seg->next;
366 free_seg->force_free = 1;
367 ol_tso_free_segment(pdev, free_seg);
368 free_seg = next_seg;
369 }
370 } else {
371 /*
372 * TSO segment are not mapped therefore,
373 * free the tso segments only.
374 */
375 while (free_seg) {
376 next_seg = free_seg->next;
377 free_seg->force_free = 1;
378 ol_tso_free_segment(pdev, free_seg);
379 free_seg = next_seg;
380 }
381 }
382 }
383
384 /**
385 * ol_tx_prepare_tso() - Given a jumbo msdu, prepare the TSO
386 * related information in the msdu_info meta data
387 * @vdev: virtual device handle
388 * @msdu: network buffer
389 * @msdu_info: meta data associated with the msdu
390 *
391 * Return: 0 - success, >0 - error
392 */
ol_tx_prepare_tso(ol_txrx_vdev_handle vdev,qdf_nbuf_t msdu,struct ol_txrx_msdu_info_t * msdu_info)393 uint8_t ol_tx_prepare_tso(ol_txrx_vdev_handle vdev,
394 qdf_nbuf_t msdu,
395 struct ol_txrx_msdu_info_t *msdu_info)
396 {
397 msdu_info->tso_info.curr_seg = NULL;
398 if (qdf_nbuf_is_tso(msdu)) {
399 int num_seg = qdf_nbuf_get_tso_num_seg(msdu);
400 struct qdf_tso_num_seg_elem_t *tso_num_seg;
401
402 msdu_info->tso_info.tso_num_seg_list = NULL;
403 msdu_info->tso_info.tso_seg_list = NULL;
404 msdu_info->tso_info.num_segs = num_seg;
405 while (num_seg) {
406 struct qdf_tso_seg_elem_t *tso_seg =
407 ol_tso_alloc_segment(vdev->pdev);
408 if (tso_seg) {
409 qdf_tso_seg_dbg_record(tso_seg,
410 TSOSEG_LOC_PREPARETSO);
411 tso_seg->next =
412 msdu_info->tso_info.tso_seg_list;
413 msdu_info->tso_info.tso_seg_list
414 = tso_seg;
415 num_seg--;
416 } else {
417 /* Free above allocated TSO segments till now */
418 msdu_info->tso_info.curr_seg =
419 msdu_info->tso_info.tso_seg_list;
420 ol_free_remaining_tso_segs(vdev, msdu_info,
421 false);
422 return 1;
423 }
424 }
425 tso_num_seg = ol_tso_num_seg_alloc(vdev->pdev);
426 if (tso_num_seg) {
427 tso_num_seg->next = msdu_info->tso_info.
428 tso_num_seg_list;
429 msdu_info->tso_info.tso_num_seg_list = tso_num_seg;
430 } else {
431 /* Free the already allocated num of segments */
432 msdu_info->tso_info.curr_seg =
433 msdu_info->tso_info.tso_seg_list;
434 ol_free_remaining_tso_segs(vdev, msdu_info, false);
435 return 1;
436 }
437
438 if (qdf_unlikely(!qdf_nbuf_get_tso_info(vdev->pdev->osdev,
439 msdu, &msdu_info->tso_info))) {
440 /* Free the already allocated num of segments */
441 msdu_info->tso_info.curr_seg =
442 msdu_info->tso_info.tso_seg_list;
443 ol_free_remaining_tso_segs(vdev, msdu_info, false);
444 return 1;
445 }
446
447 msdu_info->tso_info.curr_seg =
448 msdu_info->tso_info.tso_seg_list;
449 num_seg = msdu_info->tso_info.num_segs;
450 } else {
451 msdu_info->tso_info.is_tso = 0;
452 msdu_info->tso_info.num_segs = 1;
453 }
454 return 0;
455 }
456
457 /**
458 * ol_tx_tso_update_stats() - update TSO stats
459 * @pdev: pointer to ol_txrx_pdev_t structure
460 * @msdu_info: tso msdu_info for the msdu
461 * @msdu: tso mdsu for which stats are updated
462 * @tso_msdu_idx: stats index in the global TSO stats array where stats will be
463 * updated
464 *
465 * Return: None
466 */
ol_tx_tso_update_stats(struct ol_txrx_pdev_t * pdev,struct qdf_tso_info_t * tso_info,qdf_nbuf_t msdu,uint32_t tso_msdu_idx)467 void ol_tx_tso_update_stats(struct ol_txrx_pdev_t *pdev,
468 struct qdf_tso_info_t *tso_info, qdf_nbuf_t msdu,
469 uint32_t tso_msdu_idx)
470 {
471 TXRX_STATS_TSO_HISTOGRAM(pdev, tso_info->num_segs);
472 TXRX_STATS_TSO_GSO_SIZE_UPDATE(pdev, tso_msdu_idx,
473 qdf_nbuf_tcp_tso_size(msdu));
474 TXRX_STATS_TSO_TOTAL_LEN_UPDATE(pdev,
475 tso_msdu_idx, qdf_nbuf_len(msdu));
476 TXRX_STATS_TSO_NUM_FRAGS_UPDATE(pdev, tso_msdu_idx,
477 qdf_nbuf_get_nr_frags(msdu));
478 }
479
480 /**
481 * ol_tx_tso_get_stats_idx() - retrieve global TSO stats index and increment it
482 * @pdev: pointer to ol_txrx_pdev_t structure
483 *
484 * Retrieve the current value of the global variable and increment it. This is
485 * done in a spinlock as the global TSO stats may be accessed in parallel by
486 * multiple TX streams.
487 *
488 * Return: The current value of TSO stats index.
489 */
ol_tx_tso_get_stats_idx(struct ol_txrx_pdev_t * pdev)490 uint32_t ol_tx_tso_get_stats_idx(struct ol_txrx_pdev_t *pdev)
491 {
492 uint32_t msdu_stats_idx = 0;
493
494 qdf_spin_lock_bh(&pdev->stats.pub.tx.tso.tso_stats_lock);
495 msdu_stats_idx = pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx;
496 pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx++;
497 pdev->stats.pub.tx.tso.tso_info.tso_msdu_idx &=
498 NUM_MAX_TSO_MSDUS_MASK;
499 qdf_spin_unlock_bh(&pdev->stats.pub.tx.tso.tso_stats_lock);
500
501 TXRX_STATS_TSO_RESET_MSDU(pdev, msdu_stats_idx);
502
503 return msdu_stats_idx;
504 }
505
506 /**
507 * ol_tso_seg_list_init() - function to initialise the tso seg freelist
508 * @pdev: the data physical device sending the data
509 * @num_seg: number of segments needs to be initialized
510 *
511 * Return: none
512 */
ol_tso_seg_list_init(struct ol_txrx_pdev_t * pdev,uint32_t num_seg)513 void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
514 {
515 int i = 0;
516 struct qdf_tso_seg_elem_t *c_element;
517
518 /* Host should not allocate any c_element. */
519 if (num_seg <= 0) {
520 ol_txrx_err("Pool size passed is 0");
521 QDF_BUG(0);
522 pdev->tso_seg_pool.pool_size = i;
523 qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
524 return;
525 }
526
527 c_element = qdf_mem_malloc(sizeof(struct qdf_tso_seg_elem_t));
528 pdev->tso_seg_pool.freelist = c_element;
529 for (i = 0; i < (num_seg - 1); i++) {
530 if (qdf_unlikely(!c_element)) {
531 ol_txrx_err("c_element NULL for seg %d", i);
532 QDF_BUG(0);
533 pdev->tso_seg_pool.pool_size = i;
534 pdev->tso_seg_pool.num_free = i;
535 qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
536 return;
537 }
538 /* set the freelist bit and magic cookie*/
539 c_element->on_freelist = 1;
540 c_element->cookie = TSO_SEG_MAGIC_COOKIE;
541 #ifdef TSOSEG_DEBUG
542 c_element->dbg.txdesc = NULL;
543 qdf_atomic_init(&c_element->dbg.cur); /* history empty */
544 qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT1);
545 #endif /* TSOSEG_DEBUG */
546 c_element->next =
547 qdf_mem_malloc(sizeof(struct qdf_tso_seg_elem_t));
548 c_element = c_element->next;
549 }
550 /*
551 * NULL check for the last c_element of the list or
552 * first c_element if num_seg is equal to 1.
553 */
554 if (qdf_unlikely(!c_element)) {
555 ol_txrx_err("c_element NULL for seg %d", i);
556 QDF_BUG(0);
557 pdev->tso_seg_pool.pool_size = i;
558 pdev->tso_seg_pool.num_free = i;
559 qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
560 return;
561 }
562 c_element->on_freelist = 1;
563 c_element->cookie = TSO_SEG_MAGIC_COOKIE;
564 #ifdef TSOSEG_DEBUG
565 qdf_tso_seg_dbg_init(c_element);
566 qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT2);
567 #endif /* TSOSEG_DEBUG */
568 c_element->next = NULL;
569 pdev->tso_seg_pool.pool_size = num_seg;
570 pdev->tso_seg_pool.num_free = num_seg;
571 qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
572 }
573
574 /**
575 * ol_tso_seg_list_deinit() - function to de-initialise the tso seg freelist
576 * @pdev: the data physical device sending the data
577 *
578 * Return: none
579 */
ol_tso_seg_list_deinit(struct ol_txrx_pdev_t * pdev)580 void ol_tso_seg_list_deinit(struct ol_txrx_pdev_t *pdev)
581 {
582 int i;
583 struct qdf_tso_seg_elem_t *c_element;
584 struct qdf_tso_seg_elem_t *temp;
585
586 /* pool size 0 implies that tso seg list is not initialised*/
587 if (!pdev->tso_seg_pool.freelist &&
588 pdev->tso_seg_pool.pool_size == 0)
589 return;
590
591 qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex);
592 c_element = pdev->tso_seg_pool.freelist;
593 i = pdev->tso_seg_pool.pool_size;
594
595 pdev->tso_seg_pool.freelist = NULL;
596 pdev->tso_seg_pool.num_free = 0;
597 pdev->tso_seg_pool.pool_size = 0;
598
599 qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
600 qdf_spinlock_destroy(&pdev->tso_seg_pool.tso_mutex);
601
602 while (i-- > 0 && c_element) {
603 temp = c_element->next;
604 if (c_element->on_freelist != 1) {
605 qdf_tso_seg_dbg_bug("seg already freed (double?)");
606 return;
607 } else if (c_element->cookie != TSO_SEG_MAGIC_COOKIE) {
608 qdf_tso_seg_dbg_bug("seg cookie is bad (corruption?)");
609 return;
610 }
611 /* free this seg, so reset the cookie value*/
612 c_element->cookie = 0;
613 qdf_mem_free(c_element);
614 c_element = temp;
615 }
616 }
617
618 /**
619 * ol_tso_num_seg_list_init() - function to initialise the freelist of elements
620 * use to count the num of tso segments in jumbo
621 * skb packet freelist
622 * @pdev: the data physical device sending the data
623 * @num_seg: number of elements needs to be initialized
624 *
625 * Return: none
626 */
ol_tso_num_seg_list_init(struct ol_txrx_pdev_t * pdev,uint32_t num_seg)627 void ol_tso_num_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
628 {
629 int i = 0;
630 struct qdf_tso_num_seg_elem_t *c_element;
631
632 /* Host should not allocate any c_element. */
633 if (num_seg <= 0) {
634 ol_txrx_err("Pool size passed is 0");
635 QDF_BUG(0);
636 pdev->tso_num_seg_pool.num_seg_pool_size = i;
637 qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
638 return;
639 }
640
641 c_element = qdf_mem_malloc(sizeof(struct qdf_tso_num_seg_elem_t));
642 pdev->tso_num_seg_pool.freelist = c_element;
643 for (i = 0; i < (num_seg - 1); i++) {
644 if (qdf_unlikely(!c_element)) {
645 ol_txrx_err("c_element NULL for num of seg %d", i);
646 QDF_BUG(0);
647 pdev->tso_num_seg_pool.num_seg_pool_size = i;
648 pdev->tso_num_seg_pool.num_free = i;
649 qdf_spinlock_create(&pdev->tso_num_seg_pool.
650 tso_num_seg_mutex);
651 return;
652 }
653 c_element->next =
654 qdf_mem_malloc(sizeof(struct qdf_tso_num_seg_elem_t));
655 c_element = c_element->next;
656 }
657 /*
658 * NULL check for the last c_element of the list or
659 * first c_element if num_seg is equal to 1.
660 */
661 if (qdf_unlikely(!c_element)) {
662 ol_txrx_err("c_element NULL for num of seg %d", i);
663 QDF_BUG(0);
664 pdev->tso_num_seg_pool.num_seg_pool_size = i;
665 pdev->tso_num_seg_pool.num_free = i;
666 qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
667 return;
668 }
669 c_element->next = NULL;
670 pdev->tso_num_seg_pool.num_seg_pool_size = num_seg;
671 pdev->tso_num_seg_pool.num_free = num_seg;
672 qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
673 }
674
675 /**
676 * ol_tso_num_seg_list_deinit() - function to de-initialise the freelist of
677 * elements use to count the num of tso segment
678 * in a jumbo skb packet freelist
679 * @pdev: the data physical device sending the data
680 *
681 * Return: none
682 */
ol_tso_num_seg_list_deinit(struct ol_txrx_pdev_t * pdev)683 void ol_tso_num_seg_list_deinit(struct ol_txrx_pdev_t *pdev)
684 {
685 int i;
686 struct qdf_tso_num_seg_elem_t *c_element;
687 struct qdf_tso_num_seg_elem_t *temp;
688
689 /* pool size 0 implies that tso num seg list is not initialised*/
690 if (!pdev->tso_num_seg_pool.freelist &&
691 pdev->tso_num_seg_pool.num_seg_pool_size == 0)
692 return;
693
694 qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
695 c_element = pdev->tso_num_seg_pool.freelist;
696 i = pdev->tso_num_seg_pool.num_seg_pool_size;
697
698 pdev->tso_num_seg_pool.freelist = NULL;
699 pdev->tso_num_seg_pool.num_free = 0;
700 pdev->tso_num_seg_pool.num_seg_pool_size = 0;
701
702 qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
703 qdf_spinlock_destroy(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
704
705 while (i-- > 0 && c_element) {
706 temp = c_element->next;
707 qdf_mem_free(c_element);
708 c_element = temp;
709 }
710 }
711 #endif /* FEATURE_TSO */
712
713 #if defined(FEATURE_TSO) && defined(FEATURE_TSO_DEBUG)
ol_txrx_tso_stats_init(ol_txrx_pdev_handle pdev)714 void ol_txrx_tso_stats_init(ol_txrx_pdev_handle pdev)
715 {
716 qdf_spinlock_create(&pdev->stats.pub.tx.tso.tso_stats_lock);
717 }
718
ol_txrx_tso_stats_deinit(ol_txrx_pdev_handle pdev)719 void ol_txrx_tso_stats_deinit(ol_txrx_pdev_handle pdev)
720 {
721 qdf_spinlock_destroy(&pdev->stats.pub.tx.tso.tso_stats_lock);
722 }
723
ol_txrx_stats_display_tso(ol_txrx_pdev_handle pdev)724 void ol_txrx_stats_display_tso(ol_txrx_pdev_handle pdev)
725 {
726 int msdu_idx;
727 int seg_idx;
728
729 txrx_nofl_info("TSO Statistics:");
730 txrx_nofl_info("TSO pkts %lld, bytes %lld",
731 pdev->stats.pub.tx.tso.tso_pkts.pkts,
732 pdev->stats.pub.tx.tso.tso_pkts.bytes);
733
734 txrx_nofl_info("TSO Histogram for numbers of segments:\n"
735 "Single segment %d\n"
736 " 2-5 segments %d\n"
737 " 6-10 segments %d\n"
738 "11-15 segments %d\n"
739 "16-20 segments %d\n"
740 " 20+ segments %d\n",
741 pdev->stats.pub.tx.tso.tso_hist.pkts_1,
742 pdev->stats.pub.tx.tso.tso_hist.pkts_2_5,
743 pdev->stats.pub.tx.tso.tso_hist.pkts_6_10,
744 pdev->stats.pub.tx.tso.tso_hist.pkts_11_15,
745 pdev->stats.pub.tx.tso.tso_hist.pkts_16_20,
746 pdev->stats.pub.tx.tso.tso_hist.pkts_20_plus);
747
748 txrx_nofl_info("TSO History Buffer: Total size %d, current_index %d",
749 NUM_MAX_TSO_MSDUS,
750 TXRX_STATS_TSO_MSDU_IDX(pdev));
751
752 for (msdu_idx = 0; msdu_idx < NUM_MAX_TSO_MSDUS; msdu_idx++) {
753 if (TXRX_STATS_TSO_MSDU_TOTAL_LEN(pdev, msdu_idx) == 0)
754 continue;
755 txrx_nofl_info("jumbo pkt idx: %d num segs %d gso_len %d total_len %d nr_frags %d",
756 msdu_idx,
757 TXRX_STATS_TSO_MSDU_NUM_SEG(pdev, msdu_idx),
758 TXRX_STATS_TSO_MSDU_GSO_SIZE(pdev, msdu_idx),
759 TXRX_STATS_TSO_MSDU_TOTAL_LEN(pdev, msdu_idx),
760 TXRX_STATS_TSO_MSDU_NR_FRAGS(pdev, msdu_idx));
761
762 for (seg_idx = 0;
763 ((seg_idx < TXRX_STATS_TSO_MSDU_NUM_SEG(pdev,
764 msdu_idx)) && (seg_idx < NUM_MAX_TSO_SEGS));
765 seg_idx++) {
766 struct qdf_tso_seg_t tso_seg =
767 TXRX_STATS_TSO_SEG(pdev, msdu_idx, seg_idx);
768
769 txrx_nofl_info("seg idx: %d", seg_idx);
770 txrx_nofl_info("tso_enable: %d",
771 tso_seg.tso_flags.tso_enable);
772 txrx_nofl_info("fin %d syn %d rst %d psh %d ack %d urg %d ece %d cwr %d ns %d",
773 tso_seg.tso_flags.fin,
774 tso_seg.tso_flags.syn,
775 tso_seg.tso_flags.rst,
776 tso_seg.tso_flags.psh,
777 tso_seg.tso_flags.ack,
778 tso_seg.tso_flags.urg,
779 tso_seg.tso_flags.ece,
780 tso_seg.tso_flags.cwr,
781 tso_seg.tso_flags.ns);
782 txrx_nofl_info("tcp_seq_num: 0x%x ip_id: %d",
783 tso_seg.tso_flags.tcp_seq_num,
784 tso_seg.tso_flags.ip_id);
785 }
786 }
787 }
788
ol_txrx_tso_stats_clear(ol_txrx_pdev_handle pdev)789 void ol_txrx_tso_stats_clear(ol_txrx_pdev_handle pdev)
790 {
791 qdf_mem_zero(&pdev->stats.pub.tx.tso.tso_pkts,
792 sizeof(struct ol_txrx_stats_elem));
793 #if defined(FEATURE_TSO)
794 qdf_mem_zero(&pdev->stats.pub.tx.tso.tso_info,
795 sizeof(struct ol_txrx_stats_tso_info));
796 qdf_mem_zero(&pdev->stats.pub.tx.tso.tso_hist,
797 sizeof(struct ol_txrx_tso_histogram));
798 #endif
799 }
800 #endif /* defined(FEATURE_TSO) && defined(FEATURE_TSO_DEBUG) */
801