10
10
11
11
#include <linux/etherdevice.h>
12
12
#include <net/ip.h>
13
+ #include <net/tso.h>
13
14
14
15
#include "otx2_reg.h"
15
16
#include "otx2_common.h"
@@ -428,6 +429,38 @@ static bool otx2_sqe_add_sg(struct otx2_nic *pfvf, struct otx2_snd_queue *sq,
428
429
return true;
429
430
}
430
431
432
+ /* Add SQE extended header subdescriptor */
433
+ static void otx2_sqe_add_ext (struct otx2_nic * pfvf , struct otx2_snd_queue * sq ,
434
+ struct sk_buff * skb , int * offset )
435
+ {
436
+ struct nix_sqe_ext_s * ext ;
437
+
438
+ ext = (struct nix_sqe_ext_s * )(sq -> sqe_base + * offset );
439
+ ext -> subdc = NIX_SUBDC_EXT ;
440
+ if (skb_shinfo (skb )-> gso_size ) {
441
+ ext -> lso = 1 ;
442
+ ext -> lso_sb = skb_transport_offset (skb ) + tcp_hdrlen (skb );
443
+ ext -> lso_mps = skb_shinfo (skb )-> gso_size ;
444
+
445
+ /* Only TSOv4 and TSOv6 GSO offloads are supported */
446
+ if (skb_shinfo (skb )-> gso_type & SKB_GSO_TCPV4 ) {
447
+ ext -> lso_format = pfvf -> hw .lso_tsov4_idx ;
448
+
449
+ /* HW adds payload size to 'ip_hdr->tot_len' while
450
+ * sending TSO segment, hence set payload length
451
+ * in IP header of the packet to just header length.
452
+ */
453
+ ip_hdr (skb )-> tot_len =
454
+ htons (ext -> lso_sb - skb_network_offset (skb ));
455
+ } else {
456
+ ext -> lso_format = pfvf -> hw .lso_tsov6_idx ;
457
+ ipv6_hdr (skb )-> payload_len =
458
+ htons (ext -> lso_sb - skb_network_offset (skb ));
459
+ }
460
+ }
461
+ * offset += sizeof (* ext );
462
+ }
463
+
431
464
/* Add SQE header subdescriptor structure */
432
465
static void otx2_sqe_add_hdr (struct otx2_nic * pfvf , struct otx2_snd_queue * sq ,
433
466
struct nix_sqe_hdr_s * sqe_hdr ,
@@ -475,6 +508,209 @@ static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq,
475
508
}
476
509
}
477
510
511
+ static int otx2_dma_map_tso_skb (struct otx2_nic * pfvf ,
512
+ struct otx2_snd_queue * sq ,
513
+ struct sk_buff * skb , int sqe , int hdr_len )
514
+ {
515
+ int num_segs = skb_shinfo (skb )-> nr_frags + 1 ;
516
+ struct sg_list * sg = & sq -> sg [sqe ];
517
+ u64 dma_addr ;
518
+ int seg , len ;
519
+
520
+ sg -> num_segs = 0 ;
521
+
522
+ /* Get payload length at skb->data */
523
+ len = skb_headlen (skb ) - hdr_len ;
524
+
525
+ for (seg = 0 ; seg < num_segs ; seg ++ ) {
526
+ /* Skip skb->data, if there is no payload */
527
+ if (!seg && !len )
528
+ continue ;
529
+ dma_addr = otx2_dma_map_skb_frag (pfvf , skb , seg , & len );
530
+ if (dma_mapping_error (pfvf -> dev , dma_addr ))
531
+ goto unmap ;
532
+
533
+ /* Save DMA mapping info for later unmapping */
534
+ sg -> dma_addr [sg -> num_segs ] = dma_addr ;
535
+ sg -> size [sg -> num_segs ] = len ;
536
+ sg -> num_segs ++ ;
537
+ }
538
+ return 0 ;
539
+ unmap :
540
+ otx2_dma_unmap_skb_frags (pfvf , sg );
541
+ return - EINVAL ;
542
+ }
543
+
544
+ static u64 otx2_tso_frag_dma_addr (struct otx2_snd_queue * sq ,
545
+ struct sk_buff * skb , int seg ,
546
+ u64 seg_addr , int hdr_len , int sqe )
547
+ {
548
+ struct sg_list * sg = & sq -> sg [sqe ];
549
+ const skb_frag_t * frag ;
550
+ int offset ;
551
+
552
+ if (seg < 0 )
553
+ return sg -> dma_addr [0 ] + (seg_addr - (u64 )skb -> data );
554
+
555
+ frag = & skb_shinfo (skb )-> frags [seg ];
556
+ offset = seg_addr - (u64 )skb_frag_address (frag );
557
+ if (skb_headlen (skb ) - hdr_len )
558
+ seg ++ ;
559
+ return sg -> dma_addr [seg ] + offset ;
560
+ }
561
+
562
+ static void otx2_sqe_tso_add_sg (struct otx2_snd_queue * sq ,
563
+ struct sg_list * list , int * offset )
564
+ {
565
+ struct nix_sqe_sg_s * sg = NULL ;
566
+ u16 * sg_lens = NULL ;
567
+ u64 * iova = NULL ;
568
+ int seg ;
569
+
570
+ /* Add SG descriptors with buffer addresses */
571
+ for (seg = 0 ; seg < list -> num_segs ; seg ++ ) {
572
+ if ((seg % MAX_SEGS_PER_SG ) == 0 ) {
573
+ sg = (struct nix_sqe_sg_s * )(sq -> sqe_base + * offset );
574
+ sg -> ld_type = NIX_SEND_LDTYPE_LDD ;
575
+ sg -> subdc = NIX_SUBDC_SG ;
576
+ sg -> segs = 0 ;
577
+ sg_lens = (void * )sg ;
578
+ iova = (void * )sg + sizeof (* sg );
579
+ /* Next subdc always starts at a 16byte boundary.
580
+ * So if sg->segs is whether 2 or 3, offset += 16bytes.
581
+ */
582
+ if ((list -> num_segs - seg ) >= (MAX_SEGS_PER_SG - 1 ))
583
+ * offset += sizeof (* sg ) + (3 * sizeof (u64 ));
584
+ else
585
+ * offset += sizeof (* sg ) + sizeof (u64 );
586
+ }
587
+ sg_lens [frag_num (seg % MAX_SEGS_PER_SG )] = list -> size [seg ];
588
+ * iova ++ = list -> dma_addr [seg ];
589
+ sg -> segs ++ ;
590
+ }
591
+ }
592
+
593
+ static void otx2_sq_append_tso (struct otx2_nic * pfvf , struct otx2_snd_queue * sq ,
594
+ struct sk_buff * skb , u16 qidx )
595
+ {
596
+ struct netdev_queue * txq = netdev_get_tx_queue (pfvf -> netdev , qidx );
597
+ int hdr_len = skb_transport_offset (skb ) + tcp_hdrlen (skb );
598
+ int tcp_data , seg_len , pkt_len , offset ;
599
+ struct nix_sqe_hdr_s * sqe_hdr ;
600
+ int first_sqe = sq -> head ;
601
+ struct sg_list list ;
602
+ struct tso_t tso ;
603
+
604
+ /* Map SKB's fragments to DMA.
605
+ * It's done here to avoid mapping for every TSO segment's packet.
606
+ */
607
+ if (otx2_dma_map_tso_skb (pfvf , sq , skb , first_sqe , hdr_len )) {
608
+ dev_kfree_skb_any (skb );
609
+ return ;
610
+ }
611
+
612
+ netdev_tx_sent_queue (txq , skb -> len );
613
+
614
+ tso_start (skb , & tso );
615
+ tcp_data = skb -> len - hdr_len ;
616
+ while (tcp_data > 0 ) {
617
+ char * hdr ;
618
+
619
+ seg_len = min_t (int , skb_shinfo (skb )-> gso_size , tcp_data );
620
+ tcp_data -= seg_len ;
621
+
622
+ /* Set SQE's SEND_HDR */
623
+ memset (sq -> sqe_base , 0 , sq -> sqe_size );
624
+ sqe_hdr = (struct nix_sqe_hdr_s * )(sq -> sqe_base );
625
+ otx2_sqe_add_hdr (pfvf , sq , sqe_hdr , skb , qidx );
626
+ offset = sizeof (* sqe_hdr );
627
+
628
+ /* Add TSO segment's pkt header */
629
+ hdr = sq -> tso_hdrs -> base + (sq -> head * TSO_HEADER_SIZE );
630
+ tso_build_hdr (skb , hdr , & tso , seg_len , tcp_data == 0 );
631
+ list .dma_addr [0 ] =
632
+ sq -> tso_hdrs -> iova + (sq -> head * TSO_HEADER_SIZE );
633
+ list .size [0 ] = hdr_len ;
634
+ list .num_segs = 1 ;
635
+
636
+ /* Add TSO segment's payload data fragments */
637
+ pkt_len = hdr_len ;
638
+ while (seg_len > 0 ) {
639
+ int size ;
640
+
641
+ size = min_t (int , tso .size , seg_len );
642
+
643
+ list .size [list .num_segs ] = size ;
644
+ list .dma_addr [list .num_segs ] =
645
+ otx2_tso_frag_dma_addr (sq , skb ,
646
+ tso .next_frag_idx - 1 ,
647
+ (u64 )tso .data , hdr_len ,
648
+ first_sqe );
649
+ list .num_segs ++ ;
650
+ pkt_len += size ;
651
+ seg_len -= size ;
652
+ tso_build_data (skb , & tso , size );
653
+ }
654
+ sqe_hdr -> total = pkt_len ;
655
+ otx2_sqe_tso_add_sg (sq , & list , & offset );
656
+
657
+ /* DMA mappings and skb needs to be freed only after last
658
+ * TSO segment is transmitted out. So set 'PNC' only for
659
+ * last segment. Also point last segment's sqe_id to first
660
+ * segment's SQE index where skb address and DMA mappings
661
+ * are saved.
662
+ */
663
+ if (!tcp_data ) {
664
+ sqe_hdr -> pnc = 1 ;
665
+ sqe_hdr -> sqe_id = first_sqe ;
666
+ sq -> sg [first_sqe ].skb = (u64 )skb ;
667
+ } else {
668
+ sqe_hdr -> pnc = 0 ;
669
+ }
670
+
671
+ sqe_hdr -> sizem1 = (offset / 16 ) - 1 ;
672
+
673
+ /* Flush SQE to HW */
674
+ otx2_sqe_flush (sq , offset );
675
+ }
676
+ }
677
+
678
+ static bool is_hw_tso_supported (struct otx2_nic * pfvf ,
679
+ struct sk_buff * skb )
680
+ {
681
+ int payload_len , last_seg_size ;
682
+
683
+ if (!pfvf -> hw .hw_tso )
684
+ return false;
685
+
686
+ /* HW has an issue due to which when the payload of the last LSO
687
+ * segment is shorter than 16 bytes, some header fields may not
688
+ * be correctly modified, hence don't offload such TSO segments.
689
+ */
690
+ if (!is_96xx_B0 (pfvf -> pdev ))
691
+ return true;
692
+
693
+ payload_len = skb -> len - (skb_transport_offset (skb ) + tcp_hdrlen (skb ));
694
+ last_seg_size = payload_len % skb_shinfo (skb )-> gso_size ;
695
+ if (last_seg_size && last_seg_size < 16 )
696
+ return false;
697
+
698
+ return true;
699
+ }
700
+
701
+ static int otx2_get_sqe_count (struct otx2_nic * pfvf , struct sk_buff * skb )
702
+ {
703
+ if (!skb_shinfo (skb )-> gso_size )
704
+ return 1 ;
705
+
706
+ /* HW TSO */
707
+ if (is_hw_tso_supported (pfvf , skb ))
708
+ return 1 ;
709
+
710
+ /* SW TSO */
711
+ return skb_shinfo (skb )-> gso_segs ;
712
+ }
713
+
478
714
bool otx2_sq_append_skb (struct net_device * netdev , struct otx2_snd_queue * sq ,
479
715
struct sk_buff * skb , u16 qidx )
480
716
{
@@ -489,7 +725,8 @@ bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq,
489
725
*/
490
726
free_sqe = (sq -> num_sqbs - * sq -> aura_fc_addr ) * sq -> sqe_per_sqb ;
491
727
492
- if (!free_sqe || free_sqe < sq -> sqe_thresh )
728
+ if (free_sqe < sq -> sqe_thresh ||
729
+ free_sqe < otx2_get_sqe_count (pfvf , skb ))
493
730
return false;
494
731
495
732
num_segs = skb_shinfo (skb )-> nr_frags + 1 ;
@@ -505,6 +742,11 @@ bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq,
505
742
num_segs = skb_shinfo (skb )-> nr_frags + 1 ;
506
743
}
507
744
745
+ if (skb_shinfo (skb )-> gso_size && !is_hw_tso_supported (pfvf , skb )) {
746
+ otx2_sq_append_tso (pfvf , sq , skb , qidx );
747
+ return true;
748
+ }
749
+
508
750
/* Set SQE's SEND_HDR.
509
751
* Do not clear the first 64bit as it contains constant info.
510
752
*/
@@ -513,6 +755,9 @@ bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq,
513
755
otx2_sqe_add_hdr (pfvf , sq , sqe_hdr , skb , qidx );
514
756
offset = sizeof (* sqe_hdr );
515
757
758
+ /* Add extended header if needed */
759
+ otx2_sqe_add_ext (pfvf , sq , skb , & offset );
760
+
516
761
/* Add SG subdesc with data frags */
517
762
if (!otx2_sqe_add_sg (pfvf , sq , skb , num_segs , & offset )) {
518
763
otx2_dma_unmap_skb_frags (pfvf , & sq -> sg [sq -> head ]);
0 commit comments