Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/hisilicon/hns3/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 159 kB image not shown  

Quelle  hns3_enet.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2016-2017 Hisilicon Limited.

#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#ifdef CONFIG_RFS_ACCEL
#include <linux/cpu_rmap.h>
#endif
#include <linux/if_vlan.h>
#include <linux/irq.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/sctp.h>
#include <net/gre.h>
#include <net/gro.h>
#include <net/ip6_checksum.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>
#include <net/tcp.h>
#include <net/vxlan.h>
#include <net/geneve.h>

#include "hnae3.h"
#include "hns3_enet.h"
/* All hns3 tracepoints are defined by the include below, which
 * must be included exactly once across the whole kernel with
 * CREATE_TRACE_POINTS defined
 */

#define CREATE_TRACE_POINTS
#include "hns3_trace.h"

#define hns3_set_field(origin, shift, val) ((origin) |= (val) << (shift))
#define hns3_tx_bd_count(S) DIV_ROUND_UP(S, HNS3_MAX_BD_SIZE)

#define hns3_rl_err(fmt, ...)      \
 do {        \
  if (net_ratelimit())     \
   netdev_err(fmt, ##__VA_ARGS__);   \
 } while (0)

static void hns3_clear_all_ring(struct hnae3_handle *h, bool force);

static const char hns3_driver_name[] = "hns3";
static const char hns3_driver_string[] =
   "Hisilicon Ethernet Network Driver for Hip08 Family";
static const char hns3_copyright[] = "Copyright (c) 2017 Huawei Corporation.";
static struct hnae3_client client;

static int debug = -1;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, " Network interface message level setting");

static unsigned int tx_sgl = 1;
module_param(tx_sgl, uint, 0600);
MODULE_PARM_DESC(tx_sgl, "Minimum number of frags when using dma_map_sg() to optimize the IOMMU mapping");

static bool page_pool_enabled = true;
module_param(page_pool_enabled, bool, 0400);

#define HNS3_SGL_SIZE(nfrag) (sizeof(struct scatterlist) * (nfrag) + \
     sizeof(struct sg_table))
#define HNS3_MAX_SGL_SIZE ALIGN(HNS3_SGL_SIZE(HNS3_MAX_TSO_BD_NUM), \
          dma_get_cache_alignment())

#define DEFAULT_MSG_LEVEL (NETIF_MSG_PROBE | NETIF_MSG_LINK | \
      NETIF_MSG_IFDOWN | NETIF_MSG_IFUP)

#define HNS3_INNER_VLAN_TAG 1
#define HNS3_OUTER_VLAN_TAG 2

#define HNS3_MIN_TX_LEN  33U
#define HNS3_MIN_TUN_PKT_LEN 65U

/* hns3_pci_tbl - PCI Device ID Table
 *
 * Last entry must be all 0s
 *
 * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
 *   Class, Class Mask, private data (not used) }
 */

static const struct pci_device_id hns3_pci_tbl[] = {
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), 0},
 {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF),
  HNAE3_DEV_SUPPORT_ROCE_DCB_BITS},
 /* required last entry */
 {0,}
};
MODULE_DEVICE_TABLE(pci, hns3_pci_tbl);

#define HNS3_RX_PTYPE_ENTRY(ptype, l, s, t, h) \
 { ptype, \
  l, \
  CHECKSUM_##s, \
  HNS3_L3_TYPE_##t, \
  1, \
  h}

#define HNS3_RX_PTYPE_UNUSED_ENTRY(ptype) \
  { ptype, 0, CHECKSUM_NONE, HNS3_L3_TYPE_PARSE_FAIL, 0, \
    PKT_HASH_TYPE_NONE }

static const struct hns3_rx_ptype hns3_rx_ptype_tbl[] = {
 HNS3_RX_PTYPE_UNUSED_ENTRY(0),
 HNS3_RX_PTYPE_ENTRY(1, 0, COMPLETE, ARP, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(2, 0, COMPLETE, RARP, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(3, 0, COMPLETE, LLDP, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(4, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(5, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(6, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(7, 0, COMPLETE, CNM, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(8, 0, NONE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_UNUSED_ENTRY(9),
 HNS3_RX_PTYPE_UNUSED_ENTRY(10),
 HNS3_RX_PTYPE_UNUSED_ENTRY(11),
 HNS3_RX_PTYPE_UNUSED_ENTRY(12),
 HNS3_RX_PTYPE_UNUSED_ENTRY(13),
 HNS3_RX_PTYPE_UNUSED_ENTRY(14),
 HNS3_RX_PTYPE_UNUSED_ENTRY(15),
 HNS3_RX_PTYPE_ENTRY(16, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(17, 0, COMPLETE, IPV4, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(18, 0, COMPLETE, IPV4, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(19, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(20, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(21, 0, NONE, IPV4, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(22, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(23, 0, NONE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(24, 0, NONE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(25, 0, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_UNUSED_ENTRY(26),
 HNS3_RX_PTYPE_UNUSED_ENTRY(27),
 HNS3_RX_PTYPE_UNUSED_ENTRY(28),
 HNS3_RX_PTYPE_ENTRY(29, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(30, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(31, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(32, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(33, 1, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(34, 1, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(35, 1, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(36, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(37, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_UNUSED_ENTRY(38),
 HNS3_RX_PTYPE_ENTRY(39, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(40, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(41, 1, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(42, 1, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(43, 1, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(44, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(45, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_UNUSED_ENTRY(46),
 HNS3_RX_PTYPE_UNUSED_ENTRY(47),
 HNS3_RX_PTYPE_UNUSED_ENTRY(48),
 HNS3_RX_PTYPE_UNUSED_ENTRY(49),
 HNS3_RX_PTYPE_UNUSED_ENTRY(50),
 HNS3_RX_PTYPE_UNUSED_ENTRY(51),
 HNS3_RX_PTYPE_UNUSED_ENTRY(52),
 HNS3_RX_PTYPE_UNUSED_ENTRY(53),
 HNS3_RX_PTYPE_UNUSED_ENTRY(54),
 HNS3_RX_PTYPE_UNUSED_ENTRY(55),
 HNS3_RX_PTYPE_UNUSED_ENTRY(56),
 HNS3_RX_PTYPE_UNUSED_ENTRY(57),
 HNS3_RX_PTYPE_UNUSED_ENTRY(58),
 HNS3_RX_PTYPE_UNUSED_ENTRY(59),
 HNS3_RX_PTYPE_UNUSED_ENTRY(60),
 HNS3_RX_PTYPE_UNUSED_ENTRY(61),
 HNS3_RX_PTYPE_UNUSED_ENTRY(62),
 HNS3_RX_PTYPE_UNUSED_ENTRY(63),
 HNS3_RX_PTYPE_UNUSED_ENTRY(64),
 HNS3_RX_PTYPE_UNUSED_ENTRY(65),
 HNS3_RX_PTYPE_UNUSED_ENTRY(66),
 HNS3_RX_PTYPE_UNUSED_ENTRY(67),
 HNS3_RX_PTYPE_UNUSED_ENTRY(68),
 HNS3_RX_PTYPE_UNUSED_ENTRY(69),
 HNS3_RX_PTYPE_UNUSED_ENTRY(70),
 HNS3_RX_PTYPE_UNUSED_ENTRY(71),
 HNS3_RX_PTYPE_UNUSED_ENTRY(72),
 HNS3_RX_PTYPE_UNUSED_ENTRY(73),
 HNS3_RX_PTYPE_UNUSED_ENTRY(74),
 HNS3_RX_PTYPE_UNUSED_ENTRY(75),
 HNS3_RX_PTYPE_UNUSED_ENTRY(76),
 HNS3_RX_PTYPE_UNUSED_ENTRY(77),
 HNS3_RX_PTYPE_UNUSED_ENTRY(78),
 HNS3_RX_PTYPE_UNUSED_ENTRY(79),
 HNS3_RX_PTYPE_UNUSED_ENTRY(80),
 HNS3_RX_PTYPE_UNUSED_ENTRY(81),
 HNS3_RX_PTYPE_UNUSED_ENTRY(82),
 HNS3_RX_PTYPE_UNUSED_ENTRY(83),
 HNS3_RX_PTYPE_UNUSED_ENTRY(84),
 HNS3_RX_PTYPE_UNUSED_ENTRY(85),
 HNS3_RX_PTYPE_UNUSED_ENTRY(86),
 HNS3_RX_PTYPE_UNUSED_ENTRY(87),
 HNS3_RX_PTYPE_UNUSED_ENTRY(88),
 HNS3_RX_PTYPE_UNUSED_ENTRY(89),
 HNS3_RX_PTYPE_UNUSED_ENTRY(90),
 HNS3_RX_PTYPE_UNUSED_ENTRY(91),
 HNS3_RX_PTYPE_UNUSED_ENTRY(92),
 HNS3_RX_PTYPE_UNUSED_ENTRY(93),
 HNS3_RX_PTYPE_UNUSED_ENTRY(94),
 HNS3_RX_PTYPE_UNUSED_ENTRY(95),
 HNS3_RX_PTYPE_UNUSED_ENTRY(96),
 HNS3_RX_PTYPE_UNUSED_ENTRY(97),
 HNS3_RX_PTYPE_UNUSED_ENTRY(98),
 HNS3_RX_PTYPE_UNUSED_ENTRY(99),
 HNS3_RX_PTYPE_UNUSED_ENTRY(100),
 HNS3_RX_PTYPE_UNUSED_ENTRY(101),
 HNS3_RX_PTYPE_UNUSED_ENTRY(102),
 HNS3_RX_PTYPE_UNUSED_ENTRY(103),
 HNS3_RX_PTYPE_UNUSED_ENTRY(104),
 HNS3_RX_PTYPE_UNUSED_ENTRY(105),
 HNS3_RX_PTYPE_UNUSED_ENTRY(106),
 HNS3_RX_PTYPE_UNUSED_ENTRY(107),
 HNS3_RX_PTYPE_UNUSED_ENTRY(108),
 HNS3_RX_PTYPE_UNUSED_ENTRY(109),
 HNS3_RX_PTYPE_UNUSED_ENTRY(110),
 HNS3_RX_PTYPE_ENTRY(111, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(112, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(113, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(114, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(115, 0, NONE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(116, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(117, 0, NONE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(118, 0, NONE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(119, 0, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_UNUSED_ENTRY(120),
 HNS3_RX_PTYPE_UNUSED_ENTRY(121),
 HNS3_RX_PTYPE_UNUSED_ENTRY(122),
 HNS3_RX_PTYPE_ENTRY(123, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(124, 0, COMPLETE, PARSE_FAIL, PKT_HASH_TYPE_NONE),
 HNS3_RX_PTYPE_ENTRY(125, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(126, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(127, 1, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(128, 1, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(129, 1, UNNECESSARY, IPV4, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(130, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(131, 0, COMPLETE, IPV4, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_UNUSED_ENTRY(132),
 HNS3_RX_PTYPE_ENTRY(133, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(134, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(135, 1, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(136, 1, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(137, 1, UNNECESSARY, IPV6, PKT_HASH_TYPE_L4),
 HNS3_RX_PTYPE_ENTRY(138, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_ENTRY(139, 0, COMPLETE, IPV6, PKT_HASH_TYPE_L3),
 HNS3_RX_PTYPE_UNUSED_ENTRY(140),
 HNS3_RX_PTYPE_UNUSED_ENTRY(141),
 HNS3_RX_PTYPE_UNUSED_ENTRY(142),
 HNS3_RX_PTYPE_UNUSED_ENTRY(143),
 HNS3_RX_PTYPE_UNUSED_ENTRY(144),
 HNS3_RX_PTYPE_UNUSED_ENTRY(145),
 HNS3_RX_PTYPE_UNUSED_ENTRY(146),
 HNS3_RX_PTYPE_UNUSED_ENTRY(147),
 HNS3_RX_PTYPE_UNUSED_ENTRY(148),
 HNS3_RX_PTYPE_UNUSED_ENTRY(149),
 HNS3_RX_PTYPE_UNUSED_ENTRY(150),
 HNS3_RX_PTYPE_UNUSED_ENTRY(151),
 HNS3_RX_PTYPE_UNUSED_ENTRY(152),
 HNS3_RX_PTYPE_UNUSED_ENTRY(153),
 HNS3_RX_PTYPE_UNUSED_ENTRY(154),
 HNS3_RX_PTYPE_UNUSED_ENTRY(155),
 HNS3_RX_PTYPE_UNUSED_ENTRY(156),
 HNS3_RX_PTYPE_UNUSED_ENTRY(157),
 HNS3_RX_PTYPE_UNUSED_ENTRY(158),
 HNS3_RX_PTYPE_UNUSED_ENTRY(159),
 HNS3_RX_PTYPE_UNUSED_ENTRY(160),
 HNS3_RX_PTYPE_UNUSED_ENTRY(161),
 HNS3_RX_PTYPE_UNUSED_ENTRY(162),
 HNS3_RX_PTYPE_UNUSED_ENTRY(163),
 HNS3_RX_PTYPE_UNUSED_ENTRY(164),
 HNS3_RX_PTYPE_UNUSED_ENTRY(165),
 HNS3_RX_PTYPE_UNUSED_ENTRY(166),
 HNS3_RX_PTYPE_UNUSED_ENTRY(167),
 HNS3_RX_PTYPE_UNUSED_ENTRY(168),
 HNS3_RX_PTYPE_UNUSED_ENTRY(169),
 HNS3_RX_PTYPE_UNUSED_ENTRY(170),
 HNS3_RX_PTYPE_UNUSED_ENTRY(171),
 HNS3_RX_PTYPE_UNUSED_ENTRY(172),
 HNS3_RX_PTYPE_UNUSED_ENTRY(173),
 HNS3_RX_PTYPE_UNUSED_ENTRY(174),
 HNS3_RX_PTYPE_UNUSED_ENTRY(175),
 HNS3_RX_PTYPE_UNUSED_ENTRY(176),
 HNS3_RX_PTYPE_UNUSED_ENTRY(177),
 HNS3_RX_PTYPE_UNUSED_ENTRY(178),
 HNS3_RX_PTYPE_UNUSED_ENTRY(179),
 HNS3_RX_PTYPE_UNUSED_ENTRY(180),
 HNS3_RX_PTYPE_UNUSED_ENTRY(181),
 HNS3_RX_PTYPE_UNUSED_ENTRY(182),
 HNS3_RX_PTYPE_UNUSED_ENTRY(183),
 HNS3_RX_PTYPE_UNUSED_ENTRY(184),
 HNS3_RX_PTYPE_UNUSED_ENTRY(185),
 HNS3_RX_PTYPE_UNUSED_ENTRY(186),
 HNS3_RX_PTYPE_UNUSED_ENTRY(187),
 HNS3_RX_PTYPE_UNUSED_ENTRY(188),
 HNS3_RX_PTYPE_UNUSED_ENTRY(189),
 HNS3_RX_PTYPE_UNUSED_ENTRY(190),
 HNS3_RX_PTYPE_UNUSED_ENTRY(191),
 HNS3_RX_PTYPE_UNUSED_ENTRY(192),
 HNS3_RX_PTYPE_UNUSED_ENTRY(193),
 HNS3_RX_PTYPE_UNUSED_ENTRY(194),
 HNS3_RX_PTYPE_UNUSED_ENTRY(195),
 HNS3_RX_PTYPE_UNUSED_ENTRY(196),
 HNS3_RX_PTYPE_UNUSED_ENTRY(197),
 HNS3_RX_PTYPE_UNUSED_ENTRY(198),
 HNS3_RX_PTYPE_UNUSED_ENTRY(199),
 HNS3_RX_PTYPE_UNUSED_ENTRY(200),
 HNS3_RX_PTYPE_UNUSED_ENTRY(201),
 HNS3_RX_PTYPE_UNUSED_ENTRY(202),
 HNS3_RX_PTYPE_UNUSED_ENTRY(203),
 HNS3_RX_PTYPE_UNUSED_ENTRY(204),
 HNS3_RX_PTYPE_UNUSED_ENTRY(205),
 HNS3_RX_PTYPE_UNUSED_ENTRY(206),
 HNS3_RX_PTYPE_UNUSED_ENTRY(207),
 HNS3_RX_PTYPE_UNUSED_ENTRY(208),
 HNS3_RX_PTYPE_UNUSED_ENTRY(209),
 HNS3_RX_PTYPE_UNUSED_ENTRY(210),
 HNS3_RX_PTYPE_UNUSED_ENTRY(211),
 HNS3_RX_PTYPE_UNUSED_ENTRY(212),
 HNS3_RX_PTYPE_UNUSED_ENTRY(213),
 HNS3_RX_PTYPE_UNUSED_ENTRY(214),
 HNS3_RX_PTYPE_UNUSED_ENTRY(215),
 HNS3_RX_PTYPE_UNUSED_ENTRY(216),
 HNS3_RX_PTYPE_UNUSED_ENTRY(217),
 HNS3_RX_PTYPE_UNUSED_ENTRY(218),
 HNS3_RX_PTYPE_UNUSED_ENTRY(219),
 HNS3_RX_PTYPE_UNUSED_ENTRY(220),
 HNS3_RX_PTYPE_UNUSED_ENTRY(221),
 HNS3_RX_PTYPE_UNUSED_ENTRY(222),
 HNS3_RX_PTYPE_UNUSED_ENTRY(223),
 HNS3_RX_PTYPE_UNUSED_ENTRY(224),
 HNS3_RX_PTYPE_UNUSED_ENTRY(225),
 HNS3_RX_PTYPE_UNUSED_ENTRY(226),
 HNS3_RX_PTYPE_UNUSED_ENTRY(227),
 HNS3_RX_PTYPE_UNUSED_ENTRY(228),
 HNS3_RX_PTYPE_UNUSED_ENTRY(229),
 HNS3_RX_PTYPE_UNUSED_ENTRY(230),
 HNS3_RX_PTYPE_UNUSED_ENTRY(231),
 HNS3_RX_PTYPE_UNUSED_ENTRY(232),
 HNS3_RX_PTYPE_UNUSED_ENTRY(233),
 HNS3_RX_PTYPE_UNUSED_ENTRY(234),
 HNS3_RX_PTYPE_UNUSED_ENTRY(235),
 HNS3_RX_PTYPE_UNUSED_ENTRY(236),
 HNS3_RX_PTYPE_UNUSED_ENTRY(237),
 HNS3_RX_PTYPE_UNUSED_ENTRY(238),
 HNS3_RX_PTYPE_UNUSED_ENTRY(239),
 HNS3_RX_PTYPE_UNUSED_ENTRY(240),
 HNS3_RX_PTYPE_UNUSED_ENTRY(241),
 HNS3_RX_PTYPE_UNUSED_ENTRY(242),
 HNS3_RX_PTYPE_UNUSED_ENTRY(243),
 HNS3_RX_PTYPE_UNUSED_ENTRY(244),
 HNS3_RX_PTYPE_UNUSED_ENTRY(245),
 HNS3_RX_PTYPE_UNUSED_ENTRY(246),
 HNS3_RX_PTYPE_UNUSED_ENTRY(247),
 HNS3_RX_PTYPE_UNUSED_ENTRY(248),
 HNS3_RX_PTYPE_UNUSED_ENTRY(249),
 HNS3_RX_PTYPE_UNUSED_ENTRY(250),
 HNS3_RX_PTYPE_UNUSED_ENTRY(251),
 HNS3_RX_PTYPE_UNUSED_ENTRY(252),
 HNS3_RX_PTYPE_UNUSED_ENTRY(253),
 HNS3_RX_PTYPE_UNUSED_ENTRY(254),
 HNS3_RX_PTYPE_UNUSED_ENTRY(255),
};

#define HNS3_INVALID_PTYPE \
  ARRAY_SIZE(hns3_rx_ptype_tbl)

static irqreturn_t hns3_irq_handle(int irq, void *vector)
{
 struct hns3_enet_tqp_vector *tqp_vector = vector;

 napi_schedule_irqoff(&tqp_vector->napi);
 tqp_vector->event_cnt++;

 return IRQ_HANDLED;
}

static void hns3_nic_uninit_irq(struct hns3_nic_priv *priv)
{
 struct hns3_enet_tqp_vector *tqp_vectors;
 unsigned int i;

 for (i = 0; i < priv->vector_num; i++) {
  tqp_vectors = &priv->tqp_vector[i];

  if (tqp_vectors->irq_init_flag != HNS3_VECTOR_INITED)
   continue;

  /* clear the affinity mask */
  irq_set_affinity_hint(tqp_vectors->vector_irq, NULL);

  /* release the irq resource */
  free_irq(tqp_vectors->vector_irq, tqp_vectors);
  tqp_vectors->irq_init_flag = HNS3_VECTOR_NOT_INITED;
 }
}

static int hns3_nic_init_irq(struct hns3_nic_priv *priv)
{
 struct hns3_enet_tqp_vector *tqp_vectors;
 int txrx_int_idx = 0;
 int rx_int_idx = 0;
 int tx_int_idx = 0;
 unsigned int i;
 int ret;

 for (i = 0; i < priv->vector_num; i++) {
  tqp_vectors = &priv->tqp_vector[i];

  if (tqp_vectors->irq_init_flag == HNS3_VECTOR_INITED)
   continue;

  if (tqp_vectors->tx_group.ring && tqp_vectors->rx_group.ring) {
   snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN,
     "%s-%s-%s-%d", hns3_driver_name,
     pci_name(priv->ae_handle->pdev),
     "TxRx", txrx_int_idx++);
   txrx_int_idx++;
  } else if (tqp_vectors->rx_group.ring) {
   snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN,
     "%s-%s-%s-%d", hns3_driver_name,
     pci_name(priv->ae_handle->pdev),
     "Rx", rx_int_idx++);
  } else if (tqp_vectors->tx_group.ring) {
   snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN,
     "%s-%s-%s-%d", hns3_driver_name,
     pci_name(priv->ae_handle->pdev),
     "Tx", tx_int_idx++);
  } else {
   /* Skip this unused q_vector */
   continue;
  }

  tqp_vectors->name[HNAE3_INT_NAME_LEN - 1] = '\0';

  irq_set_status_flags(tqp_vectors->vector_irq, IRQ_NOAUTOEN);
  ret = request_irq(tqp_vectors->vector_irq, hns3_irq_handle, 0,
      tqp_vectors->name, tqp_vectors);
  if (ret) {
   netdev_err(priv->netdev, "request irq(%d) fail\n",
       tqp_vectors->vector_irq);
   hns3_nic_uninit_irq(priv);
   return ret;
  }

  irq_set_affinity_hint(tqp_vectors->vector_irq,
          &tqp_vectors->affinity_mask);

  tqp_vectors->irq_init_flag = HNS3_VECTOR_INITED;
 }

 return 0;
}

static void hns3_mask_vector_irq(struct hns3_enet_tqp_vector *tqp_vector,
     u32 mask_en)
{
 writel(mask_en, tqp_vector->mask_addr);
}

static void hns3_irq_enable(struct hns3_enet_tqp_vector *tqp_vector)
{
 napi_enable(&tqp_vector->napi);
 enable_irq(tqp_vector->vector_irq);
}

static void hns3_irq_disable(struct hns3_enet_tqp_vector *tqp_vector)
{
 disable_irq(tqp_vector->vector_irq);
 napi_disable(&tqp_vector->napi);
 cancel_work_sync(&tqp_vector->rx_group.dim.work);
 cancel_work_sync(&tqp_vector->tx_group.dim.work);
}

void hns3_set_vector_coalesce_rl(struct hns3_enet_tqp_vector *tqp_vector,
     u32 rl_value)
{
 u32 rl_reg = hns3_rl_usec_to_reg(rl_value);

 /* this defines the configuration for RL (Interrupt Rate Limiter).
 * Rl defines rate of interrupts i.e. number of interrupts-per-second
 * GL and RL(Rate Limiter) are 2 ways to acheive interrupt coalescing
 */

 if (rl_reg > 0 && !tqp_vector->tx_group.coal.adapt_enable &&
     !tqp_vector->rx_group.coal.adapt_enable)
  /* According to the hardware, the range of rl_reg is
 * 0-59 and the unit is 4.
 */

  rl_reg |=  HNS3_INT_RL_ENABLE_MASK;

 writel(rl_reg, tqp_vector->mask_addr + HNS3_VECTOR_RL_OFFSET);
}

void hns3_set_vector_coalesce_rx_gl(struct hns3_enet_tqp_vector *tqp_vector,
        u32 gl_value)
{
 u32 new_val;

 if (tqp_vector->rx_group.coal.unit_1us)
  new_val = gl_value | HNS3_INT_GL_1US;
 else
  new_val = hns3_gl_usec_to_reg(gl_value);

 writel(new_val, tqp_vector->mask_addr + HNS3_VECTOR_GL0_OFFSET);
}

void hns3_set_vector_coalesce_tx_gl(struct hns3_enet_tqp_vector *tqp_vector,
        u32 gl_value)
{
 u32 new_val;

 if (tqp_vector->tx_group.coal.unit_1us)
  new_val = gl_value | HNS3_INT_GL_1US;
 else
  new_val = hns3_gl_usec_to_reg(gl_value);

 writel(new_val, tqp_vector->mask_addr + HNS3_VECTOR_GL1_OFFSET);
}

void hns3_set_vector_coalesce_tx_ql(struct hns3_enet_tqp_vector *tqp_vector,
        u32 ql_value)
{
 writel(ql_value, tqp_vector->mask_addr + HNS3_VECTOR_TX_QL_OFFSET);
}

void hns3_set_vector_coalesce_rx_ql(struct hns3_enet_tqp_vector *tqp_vector,
        u32 ql_value)
{
 writel(ql_value, tqp_vector->mask_addr + HNS3_VECTOR_RX_QL_OFFSET);
}

static void hns3_vector_coalesce_init(struct hns3_enet_tqp_vector *tqp_vector,
          struct hns3_nic_priv *priv)
{
 struct hns3_enet_coalesce *tx_coal = &tqp_vector->tx_group.coal;
 struct hns3_enet_coalesce *rx_coal = &tqp_vector->rx_group.coal;
 struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(priv->ae_handle);
 struct hns3_enet_coalesce *ptx_coal = &priv->tx_coal;
 struct hns3_enet_coalesce *prx_coal = &priv->rx_coal;

 tx_coal->adapt_enable = ptx_coal->adapt_enable;
 rx_coal->adapt_enable = prx_coal->adapt_enable;

 tx_coal->int_gl = ptx_coal->int_gl;
 rx_coal->int_gl = prx_coal->int_gl;

 rx_coal->flow_level = prx_coal->flow_level;
 tx_coal->flow_level = ptx_coal->flow_level;

 /* device version above V3(include V3), GL can configure 1us
 * unit, so uses 1us unit.
 */

 if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3) {
  tx_coal->unit_1us = 1;
  rx_coal->unit_1us = 1;
 }

 if (ae_dev->dev_specs.int_ql_max) {
  tx_coal->ql_enable = 1;
  rx_coal->ql_enable = 1;
  tx_coal->int_ql_max = ae_dev->dev_specs.int_ql_max;
  rx_coal->int_ql_max = ae_dev->dev_specs.int_ql_max;
  tx_coal->int_ql = ptx_coal->int_ql;
  rx_coal->int_ql = prx_coal->int_ql;
 }
}

static void
hns3_vector_coalesce_init_hw(struct hns3_enet_tqp_vector *tqp_vector,
        struct hns3_nic_priv *priv)
{
 struct hns3_enet_coalesce *tx_coal = &tqp_vector->tx_group.coal;
 struct hns3_enet_coalesce *rx_coal = &tqp_vector->rx_group.coal;
 struct hnae3_handle *h = priv->ae_handle;

 hns3_set_vector_coalesce_tx_gl(tqp_vector, tx_coal->int_gl);
 hns3_set_vector_coalesce_rx_gl(tqp_vector, rx_coal->int_gl);
 hns3_set_vector_coalesce_rl(tqp_vector, h->kinfo.int_rl_setting);

 if (tx_coal->ql_enable)
  hns3_set_vector_coalesce_tx_ql(tqp_vector, tx_coal->int_ql);

 if (rx_coal->ql_enable)
  hns3_set_vector_coalesce_rx_ql(tqp_vector, rx_coal->int_ql);
}

static int hns3_nic_set_real_num_queue(struct net_device *netdev)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);
 struct hnae3_knic_private_info *kinfo = &h->kinfo;
 struct hnae3_tc_info *tc_info = &kinfo->tc_info;
 unsigned int queue_size = kinfo->num_tqps;
 int i, ret;

 if (tc_info->num_tc <= 1 && !tc_info->mqprio_active) {
  netdev_reset_tc(netdev);
 } else {
  ret = netdev_set_num_tc(netdev, tc_info->num_tc);
  if (ret) {
   netdev_err(netdev,
       "netdev_set_num_tc fail, ret=%d!\n", ret);
   return ret;
  }

  for (i = 0; i < tc_info->num_tc; i++)
   netdev_set_tc_queue(netdev, i, tc_info->tqp_count[i],
         tc_info->tqp_offset[i]);
 }

 ret = netif_set_real_num_tx_queues(netdev, queue_size);
 if (ret) {
  netdev_err(netdev,
      "netif_set_real_num_tx_queues fail, ret=%d!\n", ret);
  return ret;
 }

 ret = netif_set_real_num_rx_queues(netdev, queue_size);
 if (ret) {
  netdev_err(netdev,
      "netif_set_real_num_rx_queues fail, ret=%d!\n", ret);
  return ret;
 }

 return 0;
}

u16 hns3_get_max_available_channels(struct hnae3_handle *h)
{
 u16 alloc_tqps, max_rss_size, rss_size;

 h->ae_algo->ops->get_tqps_and_rss_info(h, &alloc_tqps, &max_rss_size);
 rss_size = alloc_tqps / h->kinfo.tc_info.num_tc;

 return min_t(u16, rss_size, max_rss_size);
}

static void hns3_tqp_enable(struct hnae3_queue *tqp)
{
 u32 rcb_reg;

 rcb_reg = hns3_read_dev(tqp, HNS3_RING_EN_REG);
 rcb_reg |= BIT(HNS3_RING_EN_B);
 hns3_write_dev(tqp, HNS3_RING_EN_REG, rcb_reg);
}

static void hns3_tqp_disable(struct hnae3_queue *tqp)
{
 u32 rcb_reg;

 rcb_reg = hns3_read_dev(tqp, HNS3_RING_EN_REG);
 rcb_reg &= ~BIT(HNS3_RING_EN_B);
 hns3_write_dev(tqp, HNS3_RING_EN_REG, rcb_reg);
}

static void hns3_free_rx_cpu_rmap(struct net_device *netdev)
{
#ifdef CONFIG_RFS_ACCEL
 free_irq_cpu_rmap(netdev->rx_cpu_rmap);
 netdev->rx_cpu_rmap = NULL;
#endif
}

static int hns3_set_rx_cpu_rmap(struct net_device *netdev)
{
#ifdef CONFIG_RFS_ACCEL
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hns3_enet_tqp_vector *tqp_vector;
 int i, ret;

 if (!netdev->rx_cpu_rmap) {
  netdev->rx_cpu_rmap = alloc_irq_cpu_rmap(priv->vector_num);
  if (!netdev->rx_cpu_rmap)
   return -ENOMEM;
 }

 for (i = 0; i < priv->vector_num; i++) {
  tqp_vector = &priv->tqp_vector[i];
  ret = irq_cpu_rmap_add(netdev->rx_cpu_rmap,
           tqp_vector->vector_irq);
  if (ret) {
   hns3_free_rx_cpu_rmap(netdev);
   return ret;
  }
 }
#endif
 return 0;
}

static void hns3_enable_irqs_and_tqps(struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hnae3_handle *h = priv->ae_handle;
 u16 i;

 for (i = 0; i < priv->vector_num; i++)
  hns3_irq_enable(&priv->tqp_vector[i]);

 for (i = 0; i < priv->vector_num; i++)
  hns3_mask_vector_irq(&priv->tqp_vector[i], 1);

 for (i = 0; i < h->kinfo.num_tqps; i++)
  hns3_tqp_enable(h->kinfo.tqp[i]);
}

static void hns3_disable_irqs_and_tqps(struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hnae3_handle *h = priv->ae_handle;
 u16 i;

 for (i = 0; i < h->kinfo.num_tqps; i++)
  hns3_tqp_disable(h->kinfo.tqp[i]);

 for (i = 0; i < priv->vector_num; i++)
  hns3_mask_vector_irq(&priv->tqp_vector[i], 0);

 for (i = 0; i < priv->vector_num; i++)
  hns3_irq_disable(&priv->tqp_vector[i]);
}

static int hns3_nic_net_up(struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hnae3_handle *h = priv->ae_handle;
 int ret;

 ret = hns3_nic_reset_all_ring(h);
 if (ret)
  return ret;

 clear_bit(HNS3_NIC_STATE_DOWN, &priv->state);

 hns3_enable_irqs_and_tqps(netdev);

 /* start the ae_dev */
 ret = h->ae_algo->ops->start ? h->ae_algo->ops->start(h) : 0;
 if (ret) {
  set_bit(HNS3_NIC_STATE_DOWN, &priv->state);
  hns3_disable_irqs_and_tqps(netdev);
 }

 return ret;
}

static void hns3_config_xps(struct hns3_nic_priv *priv)
{
 int i;

 for (i = 0; i < priv->vector_num; i++) {
  struct hns3_enet_tqp_vector *tqp_vector = &priv->tqp_vector[i];
  struct hns3_enet_ring *ring = tqp_vector->tx_group.ring;

  while (ring) {
   int ret;

   ret = netif_set_xps_queue(priv->netdev,
        &tqp_vector->affinity_mask,
        ring->tqp->tqp_index);
   if (ret)
    netdev_warn(priv->netdev,
         "set xps queue failed: %d", ret);

   ring = ring->next;
  }
 }
}

static int hns3_nic_net_open(struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hnae3_handle *h = hns3_get_handle(netdev);
 struct hnae3_knic_private_info *kinfo;
 int i, ret;

 if (hns3_nic_resetting(netdev))
  return -EBUSY;

 if (!test_bit(HNS3_NIC_STATE_DOWN, &priv->state)) {
  netdev_warn(netdev, "net open repeatedly!\n");
  return 0;
 }

 netif_carrier_off(netdev);

 ret = hns3_nic_set_real_num_queue(netdev);
 if (ret)
  return ret;

 ret = hns3_nic_net_up(netdev);
 if (ret) {
  netdev_err(netdev, "net up fail, ret=%d!\n", ret);
  return ret;
 }

 kinfo = &h->kinfo;
 for (i = 0; i < HNAE3_MAX_USER_PRIO; i++)
  netdev_set_prio_tc_map(netdev, i, kinfo->tc_info.prio_tc[i]);

 if (h->ae_algo->ops->set_timer_task)
  h->ae_algo->ops->set_timer_task(priv->ae_handle, true);

 hns3_config_xps(priv);

 netif_dbg(h, drv, netdev, "net open\n");

 return 0;
}

static void hns3_reset_tx_queue(struct hnae3_handle *h)
{
 struct net_device *ndev = h->kinfo.netdev;
 struct hns3_nic_priv *priv = netdev_priv(ndev);
 struct netdev_queue *dev_queue;
 u32 i;

 for (i = 0; i < h->kinfo.num_tqps; i++) {
  dev_queue = netdev_get_tx_queue(ndev,
      priv->ring[i].queue_index);
  netdev_tx_reset_queue(dev_queue);
 }
}

static void hns3_nic_net_down(struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 const struct hnae3_ae_ops *ops;

 hns3_disable_irqs_and_tqps(netdev);

 /* stop ae_dev */
 ops = priv->ae_handle->ae_algo->ops;
 if (ops->stop)
  ops->stop(priv->ae_handle);

 /* delay ring buffer clearing to hns3_reset_notify_uninit_enet
 * during reset process, because driver may not be able
 * to disable the ring through firmware when downing the netdev.
 */

 if (!hns3_nic_resetting(netdev))
  hns3_clear_all_ring(priv->ae_handle, false);

 hns3_reset_tx_queue(priv->ae_handle);
}

static int hns3_nic_net_stop(struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hnae3_handle *h = hns3_get_handle(netdev);

 if (test_and_set_bit(HNS3_NIC_STATE_DOWN, &priv->state))
  return 0;

 netif_dbg(h, drv, netdev, "net stop\n");

 if (h->ae_algo->ops->set_timer_task)
  h->ae_algo->ops->set_timer_task(priv->ae_handle, false);

 netif_carrier_off(netdev);
 netif_tx_disable(netdev);

 hns3_nic_net_down(netdev);

 return 0;
}

static int hns3_nic_uc_sync(struct net_device *netdev,
       const unsigned char *addr)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);

 if (h->ae_algo->ops->add_uc_addr)
  return h->ae_algo->ops->add_uc_addr(h, addr);

 return 0;
}

static int hns3_nic_uc_unsync(struct net_device *netdev,
         const unsigned char *addr)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);

 /* need ignore the request of removing device address, because
 * we store the device address and other addresses of uc list
 * in the function's mac filter list.
 */

 if (ether_addr_equal(addr, netdev->dev_addr))
  return 0;

 if (h->ae_algo->ops->rm_uc_addr)
  return h->ae_algo->ops->rm_uc_addr(h, addr);

 return 0;
}

static int hns3_nic_mc_sync(struct net_device *netdev,
       const unsigned char *addr)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);

 if (h->ae_algo->ops->add_mc_addr)
  return h->ae_algo->ops->add_mc_addr(h, addr);

 return 0;
}

static int hns3_nic_mc_unsync(struct net_device *netdev,
         const unsigned char *addr)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);

 if (h->ae_algo->ops->rm_mc_addr)
  return h->ae_algo->ops->rm_mc_addr(h, addr);

 return 0;
}

static u8 hns3_get_netdev_flags(struct net_device *netdev)
{
 u8 flags = 0;

 if (netdev->flags & IFF_PROMISC)
  flags = HNAE3_USER_UPE | HNAE3_USER_MPE | HNAE3_BPE;
 else if (netdev->flags & IFF_ALLMULTI)
  flags = HNAE3_USER_MPE;

 return flags;
}

static void hns3_nic_set_rx_mode(struct net_device *netdev)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);
 u8 new_flags;

 new_flags = hns3_get_netdev_flags(netdev);

 __dev_uc_sync(netdev, hns3_nic_uc_sync, hns3_nic_uc_unsync);
 __dev_mc_sync(netdev, hns3_nic_mc_sync, hns3_nic_mc_unsync);

 /* User mode Promisc mode enable and vlan filtering is disabled to
 * let all packets in.
 */

 h->netdev_flags = new_flags;
 hns3_request_update_promisc_mode(h);
}

void hns3_request_update_promisc_mode(struct hnae3_handle *handle)
{
 const struct hnae3_ae_ops *ops = hns3_get_ops(handle);

 if (ops->request_update_promisc_mode)
  ops->request_update_promisc_mode(handle);
}

static u32 hns3_tx_spare_space(struct hns3_enet_ring *ring)
{
 struct hns3_tx_spare *tx_spare = ring->tx_spare;
 u32 ntc, ntu;

 /* This smp_load_acquire() pairs with smp_store_release() in
 * hns3_tx_spare_update() called in tx desc cleaning process.
 */

 ntc = smp_load_acquire(&tx_spare->last_to_clean);
 ntu = tx_spare->next_to_use;

 if (ntc > ntu)
  return ntc - ntu - 1;

 /* The free tx buffer is divided into two part, so pick the
 * larger one.
 */

 return max(ntc, tx_spare->len - ntu) - 1;
}

static void hns3_tx_spare_update(struct hns3_enet_ring *ring)
{
 struct hns3_tx_spare *tx_spare = ring->tx_spare;

 if (!tx_spare ||
     tx_spare->last_to_clean == tx_spare->next_to_clean)
  return;

 /* This smp_store_release() pairs with smp_load_acquire() in
 * hns3_tx_spare_space() called in xmit process.
 */

 smp_store_release(&tx_spare->last_to_clean,
     tx_spare->next_to_clean);
}

static bool hns3_can_use_tx_bounce(struct hns3_enet_ring *ring,
       struct sk_buff *skb,
       u32 space)
{
 u32 len = skb->len <= ring->tx_copybreak ? skb->len :
    skb_headlen(skb);

 if (len > ring->tx_copybreak)
  return false;

 if (ALIGN(len, dma_get_cache_alignment()) > space) {
  hns3_ring_stats_update(ring, tx_spare_full);
  return false;
 }

 return true;
}

static bool hns3_can_use_tx_sgl(struct hns3_enet_ring *ring,
    struct sk_buff *skb,
    u32 space)
{
 if (skb->len <= ring->tx_copybreak || !tx_sgl ||
     (!skb_has_frag_list(skb) &&
      skb_shinfo(skb)->nr_frags < tx_sgl))
  return false;

 if (space < HNS3_MAX_SGL_SIZE) {
  hns3_ring_stats_update(ring, tx_spare_full);
  return false;
 }

 return true;
}

static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring)
{
 u32 alloc_size = ring->tqp->handle->kinfo.tx_spare_buf_size;
 struct net_device *netdev = ring_to_netdev(ring);
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hns3_tx_spare *tx_spare;
 struct page *page;
 dma_addr_t dma;
 int order;

 if (!alloc_size)
  return;

 order = get_order(alloc_size);
 if (order > MAX_PAGE_ORDER) {
  if (net_ratelimit())
   dev_warn(ring_to_dev(ring), "failed to allocate tx spare buffer, exceed to max order\n");
  return;
 }

 tx_spare = devm_kzalloc(ring_to_dev(ring), sizeof(*tx_spare),
    GFP_KERNEL);
 if (!tx_spare) {
  /* The driver still work without the tx spare buffer */
  dev_warn(ring_to_dev(ring), "failed to allocate hns3_tx_spare\n");
  goto devm_kzalloc_error;
 }

 page = alloc_pages_node(dev_to_node(ring_to_dev(ring)),
    GFP_KERNEL, order);
 if (!page) {
  dev_warn(ring_to_dev(ring), "failed to allocate tx spare pages\n");
  goto alloc_pages_error;
 }

 dma = dma_map_page(ring_to_dev(ring), page, 0,
      PAGE_SIZE << order, DMA_TO_DEVICE);
 if (dma_mapping_error(ring_to_dev(ring), dma)) {
  dev_warn(ring_to_dev(ring), "failed to map pages for tx spare\n");
  goto dma_mapping_error;
 }

 tx_spare->dma = dma;
 tx_spare->buf = page_address(page);
 tx_spare->len = PAGE_SIZE << order;
 ring->tx_spare = tx_spare;
 ring->tx_copybreak = priv->tx_copybreak;
 return;

dma_mapping_error:
 put_page(page);
alloc_pages_error:
 devm_kfree(ring_to_dev(ring), tx_spare);
devm_kzalloc_error:
 ring->tqp->handle->kinfo.tx_spare_buf_size = 0;
}

/* Use hns3_tx_spare_space() to make sure there is enough buffer
 * before calling below function to allocate tx buffer.
 */

static void *hns3_tx_spare_alloc(struct hns3_enet_ring *ring,
     unsigned int size, dma_addr_t *dma,
     u32 *cb_len)
{
 struct hns3_tx_spare *tx_spare = ring->tx_spare;
 u32 ntu = tx_spare->next_to_use;

 size = ALIGN(size, dma_get_cache_alignment());
 *cb_len = size;

 /* Tx spare buffer wraps back here because the end of
 * freed tx buffer is not enough.
 */

 if (ntu + size > tx_spare->len) {
  *cb_len += (tx_spare->len - ntu);
  ntu = 0;
 }

 tx_spare->next_to_use = ntu + size;
 if (tx_spare->next_to_use == tx_spare->len)
  tx_spare->next_to_use = 0;

 *dma = tx_spare->dma + ntu;

 return tx_spare->buf + ntu;
}

static void hns3_tx_spare_rollback(struct hns3_enet_ring *ring, u32 len)
{
 struct hns3_tx_spare *tx_spare = ring->tx_spare;

 if (len > tx_spare->next_to_use) {
  len -= tx_spare->next_to_use;
  tx_spare->next_to_use = tx_spare->len - len;
 } else {
  tx_spare->next_to_use -= len;
 }
}

static void hns3_tx_spare_reclaim_cb(struct hns3_enet_ring *ring,
         struct hns3_desc_cb *cb)
{
 struct hns3_tx_spare *tx_spare = ring->tx_spare;
 u32 ntc = tx_spare->next_to_clean;
 u32 len = cb->length;

 tx_spare->next_to_clean += len;

 if (tx_spare->next_to_clean >= tx_spare->len) {
  tx_spare->next_to_clean -= tx_spare->len;

  if (tx_spare->next_to_clean) {
   ntc = 0;
   len = tx_spare->next_to_clean;
  }
 }

 /* This tx spare buffer is only really reclaimed after calling
 * hns3_tx_spare_update(), so it is still safe to use the info in
 * the tx buffer to do the dma sync or sg unmapping after
 * tx_spare->next_to_clean is moved forword.
 */

 if (cb->type & (DESC_TYPE_BOUNCE_HEAD | DESC_TYPE_BOUNCE_ALL)) {
  dma_addr_t dma = tx_spare->dma + ntc;

  dma_sync_single_for_cpu(ring_to_dev(ring), dma, len,
     DMA_TO_DEVICE);
 } else {
  struct sg_table *sgt = tx_spare->buf + ntc;

  dma_unmap_sg(ring_to_dev(ring), sgt->sgl, sgt->orig_nents,
        DMA_TO_DEVICE);
 }
}

static int hns3_set_tso(struct sk_buff *skb, u32 *paylen_fdop_ol4cs,
   u16 *mss, u32 *type_cs_vlan_tso, u32 *send_bytes)
{
 u32 l4_offset, hdr_len;
 union l3_hdr_info l3;
 union l4_hdr_info l4;
 u32 l4_paylen;
 int ret;

 if (!skb_is_gso(skb))
  return 0;

 ret = skb_cow_head(skb, 0);
 if (unlikely(ret < 0))
  return ret;

 l3.hdr = skb_network_header(skb);
 l4.hdr = skb_transport_header(skb);

 /* Software should clear the IPv4's checksum field when tso is
 * needed.
 */

 if (l3.v4->version == 4)
  l3.v4->check = 0;

 /* tunnel packet */
 if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE |
      SKB_GSO_GRE_CSUM |
      SKB_GSO_UDP_TUNNEL |
      SKB_GSO_UDP_TUNNEL_CSUM)) {
  /* reset l3&l4 pointers from outer to inner headers */
  l3.hdr = skb_inner_network_header(skb);
  l4.hdr = skb_inner_transport_header(skb);

  /* Software should clear the IPv4's checksum field when
 * tso is needed.
 */

  if (l3.v4->version == 4)
   l3.v4->check = 0;
 }

 /* normal or tunnel packet */
 l4_offset = l4.hdr - skb->data;

 /* remove payload length from inner pseudo checksum when tso */
 l4_paylen = skb->len - l4_offset;

 if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) {
  hdr_len = sizeof(*l4.udp) + l4_offset;
  csum_replace_by_diff(&l4.udp->check,
         (__force __wsum)htonl(l4_paylen));
 } else {
  hdr_len = (l4.tcp->doff << 2) + l4_offset;
  csum_replace_by_diff(&l4.tcp->check,
         (__force __wsum)htonl(l4_paylen));
 }

 *send_bytes = (skb_shinfo(skb)->gso_segs - 1) * hdr_len + skb->len;

 /* find the txbd field values */
 *paylen_fdop_ol4cs = skb->len - hdr_len;
 hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_TSO_B, 1);

 /* offload outer UDP header checksum */
 if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)
  hns3_set_field(*paylen_fdop_ol4cs, HNS3_TXD_OL4CS_B, 1);

 /* get MSS for TSO */
 *mss = skb_shinfo(skb)->gso_size;

 trace_hns3_tso(skb);

 return 0;
}

static int hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto,
    u8 *il4_proto)
{
 union l3_hdr_info l3;
 unsigned char *l4_hdr;
 unsigned char *exthdr;
 u8 l4_proto_tmp;
 __be16 frag_off;

 /* find outer header point */
 l3.hdr = skb_network_header(skb);
 l4_hdr = skb_transport_header(skb);

 if (skb->protocol == htons(ETH_P_IPV6)) {
  exthdr = l3.hdr + sizeof(*l3.v6);
  l4_proto_tmp = l3.v6->nexthdr;
  if (l4_hdr != exthdr)
   ipv6_skip_exthdr(skb, exthdr - skb->data,
      &l4_proto_tmp, &frag_off);
 } else if (skb->protocol == htons(ETH_P_IP)) {
  l4_proto_tmp = l3.v4->protocol;
 } else {
  return -EINVAL;
 }

 *ol4_proto = l4_proto_tmp;

 /* tunnel packet */
 if (!skb->encapsulation) {
  *il4_proto = 0;
  return 0;
 }

 /* find inner header point */
 l3.hdr = skb_inner_network_header(skb);
 l4_hdr = skb_inner_transport_header(skb);

 if (l3.v6->version == 6) {
  exthdr = l3.hdr + sizeof(*l3.v6);
  l4_proto_tmp = l3.v6->nexthdr;
  if (l4_hdr != exthdr)
   ipv6_skip_exthdr(skb, exthdr - skb->data,
      &l4_proto_tmp, &frag_off);
 } else if (l3.v4->version == 4) {
  l4_proto_tmp = l3.v4->protocol;
 }

 *il4_proto = l4_proto_tmp;

 return 0;
}

/* when skb->encapsulation is 0, skb->ip_summed is CHECKSUM_PARTIAL
 * and it is udp packet, which has a dest port as the IANA assigned.
 * the hardware is expected to do the checksum offload, but the
 * hardware will not do the checksum offload when udp dest port is
 * 4789, 4790 or 6081.
 */

static bool hns3_tunnel_csum_bug(struct sk_buff *skb)
{
 struct hns3_nic_priv *priv = netdev_priv(skb->dev);
 struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(priv->ae_handle);
 union l4_hdr_info l4;

 /* device version above V3(include V3), the hardware can
 * do this checksum offload.
 */

 if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3)
  return false;

 l4.hdr = skb_transport_header(skb);

 if (!(!skb->encapsulation &&
       (l4.udp->dest == htons(IANA_VXLAN_UDP_PORT) ||
       l4.udp->dest == htons(GENEVE_UDP_PORT) ||
       l4.udp->dest == htons(IANA_VXLAN_GPE_UDP_PORT))))
  return false;

 return true;
}

static void hns3_set_outer_l2l3l4(struct sk_buff *skb, u8 ol4_proto,
      u32 *ol_type_vlan_len_msec)
{
 u32 l2_len, l3_len, l4_len;
 unsigned char *il2_hdr;
 union l3_hdr_info l3;
 union l4_hdr_info l4;

 l3.hdr = skb_network_header(skb);
 l4.hdr = skb_transport_header(skb);

 /* compute OL2 header size, defined in 2 Bytes */
 l2_len = l3.hdr - skb->data;
 hns3_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L2LEN_S, l2_len >> 1);

 /* compute OL3 header size, defined in 4 Bytes */
 l3_len = l4.hdr - l3.hdr;
 hns3_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L3LEN_S, l3_len >> 2);

 il2_hdr = skb_inner_mac_header(skb);
 /* compute OL4 header size, defined in 4 Bytes */
 l4_len = il2_hdr - l4.hdr;
 hns3_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L4LEN_S, l4_len >> 2);

 /* define outer network header type */
 if (skb->protocol == htons(ETH_P_IP)) {
  if (skb_is_gso(skb))
   hns3_set_field(*ol_type_vlan_len_msec,
           HNS3_TXD_OL3T_S,
           HNS3_OL3T_IPV4_CSUM);
  else
   hns3_set_field(*ol_type_vlan_len_msec,
           HNS3_TXD_OL3T_S,
           HNS3_OL3T_IPV4_NO_CSUM);
 } else if (skb->protocol == htons(ETH_P_IPV6)) {
  hns3_set_field(*ol_type_vlan_len_msec, HNS3_TXD_OL3T_S,
          HNS3_OL3T_IPV6);
 }

 if (ol4_proto == IPPROTO_UDP)
  hns3_set_field(*ol_type_vlan_len_msec, HNS3_TXD_TUNTYPE_S,
          HNS3_TUN_MAC_IN_UDP);
 else if (ol4_proto == IPPROTO_GRE)
  hns3_set_field(*ol_type_vlan_len_msec, HNS3_TXD_TUNTYPE_S,
          HNS3_TUN_NVGRE);
}

static void hns3_set_l3_type(struct sk_buff *skb, union l3_hdr_info l3,
        u32 *type_cs_vlan_tso)
{
 if (l3.v4->version == 4) {
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_S,
          HNS3_L3T_IPV4);

  /* the stack computes the IP header already, the only time we
 * need the hardware to recompute it is in the case of TSO.
 */

  if (skb_is_gso(skb))
   hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L3CS_B, 1);
 } else if (l3.v6->version == 6) {
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_S,
          HNS3_L3T_IPV6);
 }
}

static int hns3_set_l4_csum_length(struct sk_buff *skb, union l4_hdr_info l4,
       u32 l4_proto, u32 *type_cs_vlan_tso)
{
 /* compute inner(/normal) L4 header size, defined in 4 Bytes */
 switch (l4_proto) {
 case IPPROTO_TCP:
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1);
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4T_S,
          HNS3_L4T_TCP);
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_S,
          l4.tcp->doff);
  break;
 case IPPROTO_UDP:
  if (hns3_tunnel_csum_bug(skb)) {
   int ret = skb_put_padto(skb, HNS3_MIN_TUN_PKT_LEN);

   return ret ? ret : skb_checksum_help(skb);
  }

  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1);
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4T_S,
          HNS3_L4T_UDP);
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_S,
          (sizeof(struct udphdr) >> 2));
  break;
 case IPPROTO_SCTP:
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1);
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4T_S,
          HNS3_L4T_SCTP);
  hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_S,
          (sizeof(struct sctphdr) >> 2));
  break;
 default:
  /* drop the skb tunnel packet if hardware don't support,
 * because hardware can't calculate csum when TSO.
 */

  if (skb_is_gso(skb))
   return -EDOM;

  /* the stack computes the IP header already,
 * driver calculate l4 checksum when not TSO.
 */

  return skb_checksum_help(skb);
 }

 return 0;
}

static int hns3_set_l2l3l4(struct sk_buff *skb, u8 ol4_proto,
      u8 il4_proto, u32 *type_cs_vlan_tso,
      u32 *ol_type_vlan_len_msec)
{
 unsigned char *l2_hdr = skb->data;
 u32 l4_proto = ol4_proto;
 union l4_hdr_info l4;
 union l3_hdr_info l3;
 u32 l2_len, l3_len;

 l4.hdr = skb_transport_header(skb);
 l3.hdr = skb_network_header(skb);

 /* handle encapsulation skb */
 if (skb->encapsulation) {
  /* If this is a not UDP/GRE encapsulation skb */
  if (!(ol4_proto == IPPROTO_UDP || ol4_proto == IPPROTO_GRE)) {
   /* drop the skb tunnel packet if hardware don't support,
 * because hardware can't calculate csum when TSO.
 */

   if (skb_is_gso(skb))
    return -EDOM;

   /* the stack computes the IP header already,
 * driver calculate l4 checksum when not TSO.
 */

   return skb_checksum_help(skb);
  }

  hns3_set_outer_l2l3l4(skb, ol4_proto, ol_type_vlan_len_msec);

  /* switch to inner header */
  l2_hdr = skb_inner_mac_header(skb);
  l3.hdr = skb_inner_network_header(skb);
  l4.hdr = skb_inner_transport_header(skb);
  l4_proto = il4_proto;
 }

 hns3_set_l3_type(skb, l3, type_cs_vlan_tso);

 /* compute inner(/normal) L2 header size, defined in 2 Bytes */
 l2_len = l3.hdr - l2_hdr;
 hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L2LEN_S, l2_len >> 1);

 /* compute inner(/normal) L3 header size, defined in 4 Bytes */
 l3_len = l4.hdr - l3.hdr;
 hns3_set_field(*type_cs_vlan_tso, HNS3_TXD_L3LEN_S, l3_len >> 2);

 return hns3_set_l4_csum_length(skb, l4, l4_proto, type_cs_vlan_tso);
}

static int hns3_handle_vtags(struct hns3_enet_ring *tx_ring,
        struct sk_buff *skb)
{
 struct hnae3_handle *handle = tx_ring->tqp->handle;
 struct hnae3_ae_dev *ae_dev;
 struct vlan_ethhdr *vhdr;
 int rc;

 if (!(skb->protocol == htons(ETH_P_8021Q) ||
       skb_vlan_tag_present(skb)))
  return 0;

 /* For HW limitation on HNAE3_DEVICE_VERSION_V2, if port based insert
 * VLAN enabled, only one VLAN header is allowed in skb, otherwise it
 * will cause RAS error.
 */

 ae_dev = hns3_get_ae_dev(handle);
 if (unlikely(skb_vlan_tagged_multi(skb) &&
       ae_dev->dev_version <= HNAE3_DEVICE_VERSION_V2 &&
       handle->port_base_vlan_state ==
       HNAE3_PORT_BASE_VLAN_ENABLE))
  return -EINVAL;

 if (skb->protocol == htons(ETH_P_8021Q) &&
     !(handle->kinfo.netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) {
  /* When HW VLAN acceleration is turned off, and the stack
 * sets the protocol to 802.1q, the driver just need to
 * set the protocol to the encapsulated ethertype.
 */

  skb->protocol = vlan_get_protocol(skb);
  return 0;
 }

 if (skb_vlan_tag_present(skb)) {
  /* Based on hw strategy, use out_vtag in two layer tag case,
 * and use inner_vtag in one tag case.
 */

  if (skb->protocol == htons(ETH_P_8021Q) &&
      handle->port_base_vlan_state ==
      HNAE3_PORT_BASE_VLAN_DISABLE)
   rc = HNS3_OUTER_VLAN_TAG;
  else
   rc = HNS3_INNER_VLAN_TAG;

  skb->protocol = vlan_get_protocol(skb);
  return rc;
 }

 rc = skb_cow_head(skb, 0);
 if (unlikely(rc < 0))
  return rc;

 vhdr = skb_vlan_eth_hdr(skb);
 vhdr->h_vlan_TCI |= cpu_to_be16((skb->priority << VLAN_PRIO_SHIFT)
      & VLAN_PRIO_MASK);

 skb->protocol = vlan_get_protocol(skb);
 return 0;
}

/* check if the hardware is capable of checksum offloading */
static bool hns3_check_hw_tx_csum(struct sk_buff *skb)
{
 struct hns3_nic_priv *priv = netdev_priv(skb->dev);

 /* Kindly note, due to backward compatibility of the TX descriptor,
 * HW checksum of the non-IP packets and GSO packets is handled at
 * different place in the following code
 */

 if (skb_csum_is_sctp(skb) || skb_is_gso(skb) ||
     !test_bit(HNS3_NIC_STATE_HW_TX_CSUM_ENABLE, &priv->state))
  return false;

 return true;
}

struct hns3_desc_param {
 u32 paylen_ol4cs;
 u32 ol_type_vlan_len_msec;
 u32 type_cs_vlan_tso;
 u16 mss_hw_csum;
 u16 inner_vtag;
 u16 out_vtag;
};

static void hns3_init_desc_data(struct sk_buff *skb, struct hns3_desc_param *pa)
{
 pa->paylen_ol4cs = skb->len;
 pa->ol_type_vlan_len_msec = 0;
 pa->type_cs_vlan_tso = 0;
 pa->mss_hw_csum = 0;
 pa->inner_vtag = 0;
 pa->out_vtag = 0;
}

static int hns3_handle_vlan_info(struct hns3_enet_ring *ring,
     struct sk_buff *skb,
     struct hns3_desc_param *param)
{
 int ret;

 ret = hns3_handle_vtags(ring, skb);
 if (unlikely(ret < 0)) {
  hns3_ring_stats_update(ring, tx_vlan_err);
  return ret;
 } else if (ret == HNS3_INNER_VLAN_TAG) {
  param->inner_vtag = skb_vlan_tag_get(skb);
  param->inner_vtag |= (skb->priority << VLAN_PRIO_SHIFT) &
    VLAN_PRIO_MASK;
  hns3_set_field(param->type_cs_vlan_tso, HNS3_TXD_VLAN_B, 1);
 } else if (ret == HNS3_OUTER_VLAN_TAG) {
  param->out_vtag = skb_vlan_tag_get(skb);
  param->out_vtag |= (skb->priority << VLAN_PRIO_SHIFT) &
    VLAN_PRIO_MASK;
  hns3_set_field(param->ol_type_vlan_len_msec, HNS3_TXD_OVLAN_B,
          1);
 }
 return 0;
}

static int hns3_handle_csum_partial(struct hns3_enet_ring *ring,
        struct sk_buff *skb,
        struct hns3_desc_cb *desc_cb,
        struct hns3_desc_param *param)
{
 u8 ol4_proto, il4_proto;
 int ret;

 if (hns3_check_hw_tx_csum(skb)) {
  /* set checksum start and offset, defined in 2 Bytes */
  hns3_set_field(param->type_cs_vlan_tso, HNS3_TXD_CSUM_START_S,
          skb_checksum_start_offset(skb) >> 1);
  hns3_set_field(param->ol_type_vlan_len_msec,
          HNS3_TXD_CSUM_OFFSET_S,
          skb->csum_offset >> 1);
  param->mss_hw_csum |= BIT(HNS3_TXD_HW_CS_B);
  return 0;
 }

 skb_reset_mac_len(skb);

 ret = hns3_get_l4_protocol(skb, &ol4_proto, &il4_proto);
 if (unlikely(ret < 0)) {
  hns3_ring_stats_update(ring, tx_l4_proto_err);
  return ret;
 }

 ret = hns3_set_l2l3l4(skb, ol4_proto, il4_proto,
         ¶m->type_cs_vlan_tso,
         ¶m->ol_type_vlan_len_msec);
 if (unlikely(ret < 0)) {
  hns3_ring_stats_update(ring, tx_l2l3l4_err);
  return ret;
 }

 ret = hns3_set_tso(skb, ¶m->paylen_ol4cs, ¶m->mss_hw_csum,
      ¶m->type_cs_vlan_tso, &desc_cb->send_bytes);
 if (unlikely(ret < 0)) {
  hns3_ring_stats_update(ring, tx_tso_err);
  return ret;
 }
 return 0;
}

static int hns3_fill_skb_desc(struct hns3_enet_ring *ring,
         struct sk_buff *skb, struct hns3_desc *desc,
         struct hns3_desc_cb *desc_cb)
{
 struct hns3_desc_param param;
 int ret;

 hns3_init_desc_data(skb, ¶m);
 ret = hns3_handle_vlan_info(ring, skb, ¶m);
 if (unlikely(ret < 0))
  return ret;

 desc_cb->send_bytes = skb->len;

 if (skb->ip_summed == CHECKSUM_PARTIAL) {
  ret = hns3_handle_csum_partial(ring, skb, desc_cb, ¶m);
  if (ret)
   return ret;
 }

 /* Set txbd */
 desc->tx.ol_type_vlan_len_msec =
  cpu_to_le32(param.ol_type_vlan_len_msec);
 desc->tx.type_cs_vlan_tso_len = cpu_to_le32(param.type_cs_vlan_tso);
 desc->tx.paylen_ol4cs = cpu_to_le32(param.paylen_ol4cs);
 desc->tx.mss_hw_csum = cpu_to_le16(param.mss_hw_csum);
 desc->tx.vlan_tag = cpu_to_le16(param.inner_vtag);
 desc->tx.outer_vlan_tag = cpu_to_le16(param.out_vtag);

 return 0;
}

static int hns3_fill_desc(struct hns3_enet_ring *ring, dma_addr_t dma,
     unsigned int size)
{
#define HNS3_LIKELY_BD_NUM 1

 struct hns3_desc *desc = &ring->desc[ring->next_to_use];
 unsigned int frag_buf_num, k;
 int sizeoflast;

 if (likely(size <= HNS3_MAX_BD_SIZE)) {
  desc->addr = cpu_to_le64(dma);
  desc->tx.send_size = cpu_to_le16(size);
  desc->tx.bdtp_fe_sc_vld_ra_ri =
   cpu_to_le16(BIT(HNS3_TXD_VLD_B));

  trace_hns3_tx_desc(ring, ring->next_to_use);
  ring_ptr_move_fw(ring, next_to_use);
  return HNS3_LIKELY_BD_NUM;
 }

 frag_buf_num = hns3_tx_bd_count(size);
 sizeoflast = size % HNS3_MAX_BD_SIZE;
 sizeoflast = sizeoflast ? sizeoflast : HNS3_MAX_BD_SIZE;

 /* When frag size is bigger than hardware limit, split this frag */
 for (k = 0; k < frag_buf_num; k++) {
  /* now, fill the descriptor */
  desc->addr = cpu_to_le64(dma + HNS3_MAX_BD_SIZE * k);
  desc->tx.send_size = cpu_to_le16((k == frag_buf_num - 1) ?
         (u16)sizeoflast : (u16)HNS3_MAX_BD_SIZE);
  desc->tx.bdtp_fe_sc_vld_ra_ri =
    cpu_to_le16(BIT(HNS3_TXD_VLD_B));

  trace_hns3_tx_desc(ring, ring->next_to_use);
  /* move ring pointer to next */
  ring_ptr_move_fw(ring, next_to_use);

  desc = &ring->desc[ring->next_to_use];
 }

 return frag_buf_num;
}

static int hns3_map_and_fill_desc(struct hns3_enet_ring *ring, void *priv,
      unsigned int type)
{
 struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
 struct device *dev = ring_to_dev(ring);
 unsigned int size;
 dma_addr_t dma;

 if (type & (DESC_TYPE_FRAGLIST_SKB | DESC_TYPE_SKB)) {
  struct sk_buff *skb = (struct sk_buff *)priv;

  size = skb_headlen(skb);
  if (!size)
   return 0;

  dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE);
 } else if (type & DESC_TYPE_BOUNCE_HEAD) {
  /* Head data has been filled in hns3_handle_tx_bounce(),
 * just return 0 here.
 */

  return 0;
 } else {
  skb_frag_t *frag = (skb_frag_t *)priv;

  size = skb_frag_size(frag);
  if (!size)
   return 0;

  dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
 }

 if (unlikely(dma_mapping_error(dev, dma))) {
  hns3_ring_stats_update(ring, sw_err_cnt);
  return -ENOMEM;
 }

 desc_cb->priv = priv;
 desc_cb->length = size;
 desc_cb->dma = dma;
 desc_cb->type = type;

 return hns3_fill_desc(ring, dma, size);
}

static unsigned int hns3_skb_bd_num(struct sk_buff *skb, unsigned int *bd_size,
        unsigned int bd_num)
{
 unsigned int size;
 int i;

 size = skb_headlen(skb);
 while (size > HNS3_MAX_BD_SIZE) {
  bd_size[bd_num++] = HNS3_MAX_BD_SIZE;
  size -= HNS3_MAX_BD_SIZE;

  if (bd_num > HNS3_MAX_TSO_BD_NUM)
   return bd_num;
 }

 if (size) {
  bd_size[bd_num++] = size;
  if (bd_num > HNS3_MAX_TSO_BD_NUM)
   return bd_num;
 }

 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
  skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
  size = skb_frag_size(frag);
  if (!size)
   continue;

  while (size > HNS3_MAX_BD_SIZE) {
   bd_size[bd_num++] = HNS3_MAX_BD_SIZE;
   size -= HNS3_MAX_BD_SIZE;

   if (bd_num > HNS3_MAX_TSO_BD_NUM)
    return bd_num;
  }

  bd_size[bd_num++] = size;
  if (bd_num > HNS3_MAX_TSO_BD_NUM)
   return bd_num;
 }

 return bd_num;
}

static unsigned int hns3_tx_bd_num(struct sk_buff *skb, unsigned int *bd_size,
       u8 max_non_tso_bd_num, unsigned int bd_num,
       unsigned int recursion_level)
{
#define HNS3_MAX_RECURSION_LEVEL 24

 struct sk_buff *frag_skb;

 /* If the total len is within the max bd limit */
 if (likely(skb->len <= HNS3_MAX_BD_SIZE && !recursion_level &&
     !skb_has_frag_list(skb) &&
     skb_shinfo(skb)->nr_frags < max_non_tso_bd_num))
  return skb_shinfo(skb)->nr_frags + 1U;

 if (unlikely(recursion_level >= HNS3_MAX_RECURSION_LEVEL))
  return UINT_MAX;

 bd_num = hns3_skb_bd_num(skb, bd_size, bd_num);
 if (!skb_has_frag_list(skb) || bd_num > HNS3_MAX_TSO_BD_NUM)
  return bd_num;

 skb_walk_frags(skb, frag_skb) {
  bd_num = hns3_tx_bd_num(frag_skb, bd_size, max_non_tso_bd_num,
     bd_num, recursion_level + 1);
  if (bd_num > HNS3_MAX_TSO_BD_NUM)
   return bd_num;
 }

 return bd_num;
}

static unsigned int hns3_gso_hdr_len(struct sk_buff *skb)
{
 if (!skb->encapsulation)
  return skb_tcp_all_headers(skb);

 return skb_inner_tcp_all_headers(skb);
}

/* HW need every continuous max_non_tso_bd_num buffer data to be larger
 * than MSS, we simplify it by ensuring skb_headlen + the first continuous
 * max_non_tso_bd_num - 1 frags to be larger than gso header len + mss,
 * and the remaining continuous max_non_tso_bd_num - 1 frags to be larger
 * than MSS except the last max_non_tso_bd_num - 1 frags.
 */

static bool hns3_skb_need_linearized(struct sk_buff *skb, unsigned int *bd_size,
         unsigned int bd_num, u8 max_non_tso_bd_num)
{
 unsigned int tot_len = 0;
 unsigned int i;

 for (i = 0; i < max_non_tso_bd_num - 1U; i++)
  tot_len += bd_size[i];

 /* ensure the first max_non_tso_bd_num frags is greater than
 * mss + header
 */

 if (tot_len + bd_size[max_non_tso_bd_num - 1U] <
     skb_shinfo(skb)->gso_size + hns3_gso_hdr_len(skb))
  return true;

 /* ensure every continuous max_non_tso_bd_num - 1 buffer is greater
 * than mss except the last one.
 */

 for (i = 0; i < bd_num - max_non_tso_bd_num; i++) {
  tot_len -= bd_size[i];
  tot_len += bd_size[i + max_non_tso_bd_num - 1U];

  if (tot_len < skb_shinfo(skb)->gso_size)
   return true;
 }

 return false;
}

void hns3_shinfo_pack(struct skb_shared_info *shinfo, __u32 *size)
{
 u32 i;

 for (i = 0; i < MAX_SKB_FRAGS; i++)
  size[i] = skb_frag_size(&shinfo->frags[i]);
}

static int hns3_skb_linearize(struct hns3_enet_ring *ring,
         struct sk_buff *skb,
         unsigned int bd_num)
{
 /* 'bd_num == UINT_MAX' means the skb' fraglist has a
 * recursion level of over HNS3_MAX_RECURSION_LEVEL.
 */

 if (bd_num == UINT_MAX) {
  hns3_ring_stats_update(ring, over_max_recursion);
  return -ENOMEM;
 }

 /* The skb->len has exceeded the hw limitation, linearization
 * will not help.
 */

 if (skb->len > HNS3_MAX_TSO_SIZE ||
     (!skb_is_gso(skb) && skb->len > HNS3_MAX_NON_TSO_SIZE)) {
  hns3_ring_stats_update(ring, hw_limitation);
  return -ENOMEM;
 }

 if (__skb_linearize(skb)) {
  hns3_ring_stats_update(ring, sw_err_cnt);
  return -ENOMEM;
 }

 return 0;
}

static int hns3_nic_maybe_stop_tx(struct hns3_enet_ring *ring,
      struct net_device *netdev,
      struct sk_buff *skb)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 u8 max_non_tso_bd_num = priv->max_non_tso_bd_num;
 unsigned int bd_size[HNS3_MAX_TSO_BD_NUM + 1U];
 unsigned int bd_num;

 bd_num = hns3_tx_bd_num(skb, bd_size, max_non_tso_bd_num, 0, 0);
 if (unlikely(bd_num > max_non_tso_bd_num)) {
  if (bd_num <= HNS3_MAX_TSO_BD_NUM && skb_is_gso(skb) &&
      !hns3_skb_need_linearized(skb, bd_size, bd_num,
           max_non_tso_bd_num)) {
   trace_hns3_over_max_bd(skb);
   goto out;
  }

  if (hns3_skb_linearize(ring, skb, bd_num))
   return -ENOMEM;

  bd_num = hns3_tx_bd_count(skb->len);

  hns3_ring_stats_update(ring, tx_copy);
 }

out:
 if (likely(ring_space(ring) >= bd_num))
  return bd_num;

 netif_stop_subqueue(netdev, ring->queue_index);
 smp_mb(); /* Memory barrier before checking ring_space */

 /* Start queue in case hns3_clean_tx_ring has just made room
 * available and has not seen the queue stopped state performed
 * by netif_stop_subqueue above.
 */

 if (ring_space(ring) >= bd_num && netif_carrier_ok(netdev) &&
     !test_bit(HNS3_NIC_STATE_DOWN, &priv->state)) {
  netif_start_subqueue(netdev, ring->queue_index);
  return bd_num;
 }

 hns3_ring_stats_update(ring, tx_busy);

 return -EBUSY;
}

static void hns3_clear_desc(struct hns3_enet_ring *ring, int next_to_use_orig)
{
 struct device *dev = ring_to_dev(ring);
 unsigned int i;

 for (i = 0; i < ring->desc_num; i++) {
  struct hns3_desc *desc = &ring->desc[ring->next_to_use];
  struct hns3_desc_cb *desc_cb;

  memset(desc, 0, sizeof(*desc));

  /* check if this is where we started */
  if (ring->next_to_use == next_to_use_orig)
   break;

  /* rollback one */
  ring_ptr_move_bw(ring, next_to_use);

  desc_cb = &ring->desc_cb[ring->next_to_use];

  if (!desc_cb->dma)
   continue;

  /* unmap the descriptor dma address */
  if (desc_cb->type & (DESC_TYPE_SKB | DESC_TYPE_FRAGLIST_SKB))
   dma_unmap_single(dev, desc_cb->dma, desc_cb->length,
      DMA_TO_DEVICE);
  else if (desc_cb->type &
    (DESC_TYPE_BOUNCE_HEAD | DESC_TYPE_BOUNCE_ALL))
   hns3_tx_spare_rollback(ring, desc_cb->length);
  else if (desc_cb->length)
   dma_unmap_page(dev, desc_cb->dma, desc_cb->length,
           DMA_TO_DEVICE);

  desc_cb->length = 0;
  desc_cb->dma = 0;
  desc_cb->type = DESC_TYPE_UNKNOWN;
 }
}

static int hns3_fill_skb_to_desc(struct hns3_enet_ring *ring,
     struct sk_buff *skb, unsigned int type)
{
 struct sk_buff *frag_skb;
 int i, ret, bd_num = 0;

 ret = hns3_map_and_fill_desc(ring, skb, type);
 if (unlikely(ret < 0))
  return ret;

 bd_num += ret;

 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
  skb_frag_t *frag = &skb_shinfo(skb)->frags[i];

  ret = hns3_map_and_fill_desc(ring, frag, DESC_TYPE_PAGE);
  if (unlikely(ret < 0))
   return ret;

  bd_num += ret;
 }

 skb_walk_frags(skb, frag_skb) {
  ret = hns3_fill_skb_to_desc(ring, frag_skb,
         DESC_TYPE_FRAGLIST_SKB);
  if (unlikely(ret < 0))
   return ret;

  bd_num += ret;
 }

 return bd_num;
}

static void hns3_tx_push_bd(struct hns3_enet_ring *ring, int num)
{
#define HNS3_BYTES_PER_64BIT  8

 struct hns3_desc desc[HNS3_MAX_PUSH_BD_NUM] = {};
 int offset = 0;

 /* make sure everything is visible to device before
 * excuting tx push or updating doorbell
 */

 dma_wmb();

 do {
  int idx = (ring->next_to_use - num + ring->desc_num) %
     ring->desc_num;

  u64_stats_update_begin(&ring->syncp);
  ring->stats.tx_push++;
  u64_stats_update_end(&ring->syncp);
  memcpy(&desc[offset], &ring->desc[idx],
         sizeof(struct hns3_desc));
  offset++;
 } while (--num);

 __iowrite64_copy(ring->tqp->mem_base, desc,
    (sizeof(struct hns3_desc) * HNS3_MAX_PUSH_BD_NUM) /
    HNS3_BYTES_PER_64BIT);
}

static void hns3_tx_mem_doorbell(struct hns3_enet_ring *ring)
{
#define HNS3_MEM_DOORBELL_OFFSET 64

 __le64 bd_num = cpu_to_le64((u64)ring->pending_buf);

 /* make sure everything is visible to device before
 * excuting tx push or updating doorbell
 */

 dma_wmb();

 __iowrite64_copy(ring->tqp->mem_base + HNS3_MEM_DOORBELL_OFFSET,
    &bd_num, 1);
 u64_stats_update_begin(&ring->syncp);
 ring->stats.tx_mem_doorbell += ring->pending_buf;
 u64_stats_update_end(&ring->syncp);
}

static void hns3_tx_doorbell(struct hns3_enet_ring *ring, int num,
        bool doorbell)
{
 struct net_device *netdev = ring_to_netdev(ring);
 struct hns3_nic_priv *priv = netdev_priv(netdev);

 /* when tx push is enabled, the packet whose number of BD below
 * HNS3_MAX_PUSH_BD_NUM can be pushed directly.
 */

 if (test_bit(HNS3_NIC_STATE_TX_PUSH_ENABLE, &priv->state) && num &&
     !ring->pending_buf && num <= HNS3_MAX_PUSH_BD_NUM && doorbell) {
  /* This smp_store_release() pairs with smp_load_acquire() in
 * hns3_nic_reclaim_desc(). Ensure that the BD valid bit
 * is updated.
 */

  smp_store_release(&ring->last_to_use, ring->next_to_use);
  hns3_tx_push_bd(ring, num);
  return;
 }

 ring->pending_buf += num;

 if (!doorbell) {
  hns3_ring_stats_update(ring, tx_more);
  return;
 }

 /* This smp_store_release() pairs with smp_load_acquire() in
 * hns3_nic_reclaim_desc(). Ensure that the BD valid bit is updated.
 */

 smp_store_release(&ring->last_to_use, ring->next_to_use);

 if (ring->tqp->mem_base)
  hns3_tx_mem_doorbell(ring);
 else
  writel(ring->pending_buf,
         ring->tqp->io_base + HNS3_RING_TX_RING_TAIL_REG);

 ring->pending_buf = 0;
}

static void hns3_tsyn(struct net_device *netdev, struct sk_buff *skb,
        struct hns3_desc *desc)
{
 struct hnae3_handle *h = hns3_get_handle(netdev);

 if (!(h->ae_algo->ops->set_tx_hwts_info &&
       h->ae_algo->ops->set_tx_hwts_info(h, skb)))
  return;

 desc->tx.bdtp_fe_sc_vld_ra_ri |= cpu_to_le16(BIT(HNS3_TXD_TSYN_B));
}

static int hns3_handle_tx_bounce(struct hns3_enet_ring *ring,
     struct sk_buff *skb)
{
 struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
 unsigned int type = DESC_TYPE_BOUNCE_HEAD;
 unsigned int size = skb_headlen(skb);
 dma_addr_t dma;
 int bd_num = 0;
 u32 cb_len;
 void *buf;
 int ret;

 if (skb->len <= ring->tx_copybreak) {
  size = skb->len;
  type = DESC_TYPE_BOUNCE_ALL;
 }

 /* hns3_can_use_tx_bounce() is called to ensure the below
 * function can always return the tx buffer.
 */

 buf = hns3_tx_spare_alloc(ring, size, &dma, &cb_len);

 ret = skb_copy_bits(skb, 0, buf, size);
 if (unlikely(ret < 0)) {
  hns3_tx_spare_rollback(ring, cb_len);
  hns3_ring_stats_update(ring, copy_bits_err);
  return ret;
 }

 desc_cb->priv = skb;
 desc_cb->length = cb_len;
 desc_cb->dma = dma;
 desc_cb->type = type;

 bd_num += hns3_fill_desc(ring, dma, size);

 if (type == DESC_TYPE_BOUNCE_HEAD) {
  ret = hns3_fill_skb_to_desc(ring, skb,
         DESC_TYPE_BOUNCE_HEAD);
  if (unlikely(ret < 0))
   return ret;

  bd_num += ret;
 }

 dma_sync_single_for_device(ring_to_dev(ring), dma, size,
       DMA_TO_DEVICE);

 hns3_ring_stats_update(ring, tx_bounce);

 return bd_num;
}

static int hns3_handle_tx_sgl(struct hns3_enet_ring *ring,
         struct sk_buff *skb)
{
 struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
 u32 nfrag = skb_shinfo(skb)->nr_frags + 1;
 struct sg_table *sgt;
 int bd_num = 0;
 dma_addr_t dma;
 u32 cb_len, i;
 int nents;

 if (skb_has_frag_list(skb))
  nfrag = HNS3_MAX_TSO_BD_NUM;

 /* hns3_can_use_tx_sgl() is called to ensure the below
 * function can always return the tx buffer.
 */

 sgt = hns3_tx_spare_alloc(ring, HNS3_SGL_SIZE(nfrag),
      &dma, &cb_len);

 /* scatterlist follows by the sg table */
 sgt->sgl = (struct scatterlist *)(sgt + 1);
 sg_init_table(sgt->sgl, nfrag);
 nents = skb_to_sgvec(skb, sgt->sgl, 0, skb->len);
 if (unlikely(nents < 0)) {
  hns3_tx_spare_rollback(ring, cb_len);
  hns3_ring_stats_update(ring, skb2sgl_err);
  return -ENOMEM;
 }

 sgt->orig_nents = nents;
 sgt->nents = dma_map_sg(ring_to_dev(ring), sgt->sgl, sgt->orig_nents,
    DMA_TO_DEVICE);
 if (unlikely(!sgt->nents)) {
  hns3_tx_spare_rollback(ring, cb_len);
  hns3_ring_stats_update(ring, map_sg_err);
  return -ENOMEM;
 }

 desc_cb->priv = skb;
 desc_cb->length = cb_len;
 desc_cb->dma = dma;
 desc_cb->type = DESC_TYPE_SGL_SKB;

 for (i = 0; i < sgt->nents; i++)
  bd_num += hns3_fill_desc(ring, sg_dma_address(sgt->sgl + i),
      sg_dma_len(sgt->sgl + i));
 hns3_ring_stats_update(ring, tx_sgl);

 return bd_num;
}

static int hns3_handle_desc_filling(struct hns3_enet_ring *ring,
        struct sk_buff *skb)
{
 u32 space;

 if (!ring->tx_spare)
  goto out;

 space = hns3_tx_spare_space(ring);

 if (hns3_can_use_tx_sgl(ring, skb, space))
  return hns3_handle_tx_sgl(ring, skb);

 if (hns3_can_use_tx_bounce(ring, skb, space))
  return hns3_handle_tx_bounce(ring, skb);

out:
 return hns3_fill_skb_to_desc(ring, skb, DESC_TYPE_SKB);
}

static int hns3_handle_skb_desc(struct hns3_enet_ring *ring,
    struct sk_buff *skb,
    struct hns3_desc_cb *desc_cb,
    int next_to_use_head)
{
 int ret;

 ret = hns3_fill_skb_desc(ring, skb, &ring->desc[ring->next_to_use],
     desc_cb);
 if (unlikely(ret < 0))
  goto fill_err;

 /* 'ret < 0' means filling error, 'ret == 0' means skb->len is
 * zero, which is unlikely, and 'ret > 0' means how many tx desc
 * need to be notified to the hw.
 */

 ret = hns3_handle_desc_filling(ring, skb);
 if (likely(ret > 0))
  return ret;

fill_err:
 hns3_clear_desc(ring, next_to_use_head);
 return ret;
}

netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
 struct hns3_nic_priv *priv = netdev_priv(netdev);
 struct hns3_enet_ring *ring = &priv->ring[skb->queue_mapping];
 struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
 struct netdev_queue *dev_queue;
 int pre_ntu, ret;
 bool doorbell;

 /* Hardware can only handle short frames above 32 bytes */
 if (skb_put_padto(skb, HNS3_MIN_TX_LEN)) {
  hns3_tx_doorbell(ring, 0, !netdev_xmit_more());

  hns3_ring_stats_update(ring, sw_err_cnt);

  return NETDEV_TX_OK;
 }

 /* Prefetch the data used later */
 prefetch(skb->data);

 ret = hns3_nic_maybe_stop_tx(ring, netdev, skb);
 if (unlikely(ret <= 0)) {
  if (ret == -EBUSY) {
   hns3_tx_doorbell(ring, 0, true);
   return NETDEV_TX_BUSY;
  }

  hns3_rl_err(netdev, "xmit error: %d!\n", ret);
  goto out_err_tx_ok;
 }

 ret = hns3_handle_skb_desc(ring, skb, desc_cb, ring->next_to_use);
 if (unlikely(ret <= 0))
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.