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

Quelle  bnxt.c   Sprache: C

 
/* Broadcom NetXtreme-C/E network driver.
 *
 * Copyright (c) 2014-2016 Broadcom Corporation
 * Copyright (c) 2016-2019 Broadcom Limited
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 */


#include <linux/module.h>

#include <linux/stringify.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <asm/byteorder.h>
#include <asm/page.h>
#include <linux/time.h>
#include <linux/mii.h>
#include <linux/mdio.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/if_bridge.h>
#include <linux/rtc.h>
#include <linux/bpf.h>
#include <net/gro.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <net/udp_tunnel.h>
#include <linux/workqueue.h>
#include <linux/prefetch.h>
#include <linux/cache.h>
#include <linux/log2.h>
#include <linux/bitmap.h>
#include <linux/cpu_rmap.h>
#include <linux/cpumask.h>
#include <net/pkt_cls.h>
#include <net/page_pool/helpers.h>
#include <linux/align.h>
#include <net/netdev_lock.h>
#include <net/netdev_queues.h>
#include <net/netdev_rx_queue.h>
#include <linux/pci-tph.h>
#include <linux/bnxt/hsi.h>

#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_ulp.h"
#include "bnxt_sriov.h"
#include "bnxt_ethtool.h"
#include "bnxt_dcb.h"
#include "bnxt_xdp.h"
#include "bnxt_ptp.h"
#include "bnxt_vfr.h"
#include "bnxt_tc.h"
#include "bnxt_devlink.h"
#include "bnxt_debugfs.h"
#include "bnxt_coredump.h"
#include "bnxt_hwmon.h"

#define BNXT_TX_TIMEOUT  (5 * HZ)
#define BNXT_DEF_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_HW | \
     NETIF_MSG_TX_ERR)

MODULE_IMPORT_NS("NETDEV_INTERNAL");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Broadcom NetXtreme network driver");

#define BNXT_RX_OFFSET (NET_SKB_PAD + NET_IP_ALIGN)
#define BNXT_RX_DMA_OFFSET NET_SKB_PAD

#define BNXT_TX_PUSH_THRESH 164

/* indexed by enum board_idx */
static const struct {
 char *name;
} board_info[] = {
 [BCM57301] = { "Broadcom BCM57301 NetXtreme-C 10Gb Ethernet" },
 [BCM57302] = { "Broadcom BCM57302 NetXtreme-C 10Gb/25Gb Ethernet" },
 [BCM57304] = { "Broadcom BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" },
 [BCM57417_NPAR] = { "Broadcom BCM57417 NetXtreme-E Ethernet Partition" },
 [BCM58700] = { "Broadcom BCM58700 Nitro 1Gb/2.5Gb/10Gb Ethernet" },
 [BCM57311] = { "Broadcom BCM57311 NetXtreme-C 10Gb Ethernet" },
 [BCM57312] = { "Broadcom BCM57312 NetXtreme-C 10Gb/25Gb Ethernet" },
 [BCM57402] = { "Broadcom BCM57402 NetXtreme-E 10Gb Ethernet" },
 [BCM57404] = { "Broadcom BCM57404 NetXtreme-E 10Gb/25Gb Ethernet" },
 [BCM57406] = { "Broadcom BCM57406 NetXtreme-E 10GBase-T Ethernet" },
 [BCM57402_NPAR] = { "Broadcom BCM57402 NetXtreme-E Ethernet Partition" },
 [BCM57407] = { "Broadcom BCM57407 NetXtreme-E 10GBase-T Ethernet" },
 [BCM57412] = { "Broadcom BCM57412 NetXtreme-E 10Gb Ethernet" },
 [BCM57414] = { "Broadcom BCM57414 NetXtreme-E 10Gb/25Gb Ethernet" },
 [BCM57416] = { "Broadcom BCM57416 NetXtreme-E 10GBase-T Ethernet" },
 [BCM57417] = { "Broadcom BCM57417 NetXtreme-E 10GBase-T Ethernet" },
 [BCM57412_NPAR] = { "Broadcom BCM57412 NetXtreme-E Ethernet Partition" },
 [BCM57314] = { "Broadcom BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" },
 [BCM57417_SFP] = { "Broadcom BCM57417 NetXtreme-E 10Gb/25Gb Ethernet" },
 [BCM57416_SFP] = { "Broadcom BCM57416 NetXtreme-E 10Gb Ethernet" },
 [BCM57404_NPAR] = { "Broadcom BCM57404 NetXtreme-E Ethernet Partition" },
 [BCM57406_NPAR] = { "Broadcom BCM57406 NetXtreme-E Ethernet Partition" },
 [BCM57407_SFP] = { "Broadcom BCM57407 NetXtreme-E 25Gb Ethernet" },
 [BCM57407_NPAR] = { "Broadcom BCM57407 NetXtreme-E Ethernet Partition" },
 [BCM57414_NPAR] = { "Broadcom BCM57414 NetXtreme-E Ethernet Partition" },
 [BCM57416_NPAR] = { "Broadcom BCM57416 NetXtreme-E Ethernet Partition" },
 [BCM57452] = { "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" },
 [BCM57454] = { "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" },
 [BCM5745x_NPAR] = { "Broadcom BCM5745x NetXtreme-E Ethernet Partition" },
 [BCM57508] = { "Broadcom BCM57508 NetXtreme-E 10Gb/25Gb/50Gb/100Gb/200Gb Ethernet" },
 [BCM57504] = { "Broadcom BCM57504 NetXtreme-E 10Gb/25Gb/50Gb/100Gb/200Gb Ethernet" },
 [BCM57502] = { "Broadcom BCM57502 NetXtreme-E 10Gb/25Gb/50Gb Ethernet" },
 [BCM57608] = { "Broadcom BCM57608 NetXtreme-E 10Gb/25Gb/50Gb/100Gb/200Gb/400Gb Ethernet" },
 [BCM57604] = { "Broadcom BCM57604 NetXtreme-E 10Gb/25Gb/50Gb/100Gb/200Gb Ethernet" },
 [BCM57602] = { "Broadcom BCM57602 NetXtreme-E 10Gb/25Gb/50Gb/100Gb Ethernet" },
 [BCM57601] = { "Broadcom BCM57601 NetXtreme-E 10Gb/25Gb/50Gb/100Gb/200Gb/400Gb Ethernet" },
 [BCM57508_NPAR] = { "Broadcom BCM57508 NetXtreme-E Ethernet Partition" },
 [BCM57504_NPAR] = { "Broadcom BCM57504 NetXtreme-E Ethernet Partition" },
 [BCM57502_NPAR] = { "Broadcom BCM57502 NetXtreme-E Ethernet Partition" },
 [BCM58802] = { "Broadcom BCM58802 NetXtreme-S 10Gb/25Gb/40Gb/50Gb Ethernet" },
 [BCM58804] = { "Broadcom BCM58804 NetXtreme-S 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" },
 [BCM58808] = { "Broadcom BCM58808 NetXtreme-S 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" },
 [NETXTREME_E_VF] = { "Broadcom NetXtreme-E Ethernet Virtual Function" },
 [NETXTREME_C_VF] = { "Broadcom NetXtreme-C Ethernet Virtual Function" },
 [NETXTREME_S_VF] = { "Broadcom NetXtreme-S Ethernet Virtual Function" },
 [NETXTREME_C_VF_HV] = { "Broadcom NetXtreme-C Virtual Function for Hyper-V" },
 [NETXTREME_E_VF_HV] = { "Broadcom NetXtreme-E Virtual Function for Hyper-V" },
 [NETXTREME_E_P5_VF] = { "Broadcom BCM5750X NetXtreme-E Ethernet Virtual Function" },
 [NETXTREME_E_P5_VF_HV] = { "Broadcom BCM5750X NetXtreme-E Virtual Function for Hyper-V" },
 [NETXTREME_E_P7_VF] = { "Broadcom BCM5760X Virtual Function" },
 [NETXTREME_E_P7_VF_HV] = { "Broadcom BCM5760X Virtual Function for Hyper-V" },
};

static const struct pci_device_id bnxt_pci_tbl[] = {
 { PCI_VDEVICE(BROADCOM, 0x1604), .driver_data = BCM5745x_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1605), .driver_data = BCM5745x_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1614), .driver_data = BCM57454 },
 { PCI_VDEVICE(BROADCOM, 0x16c0), .driver_data = BCM57417_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16c8), .driver_data = BCM57301 },
 { PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 },
 { PCI_VDEVICE(BROADCOM, 0x16ca), .driver_data = BCM57304 },
 { PCI_VDEVICE(BROADCOM, 0x16cc), .driver_data = BCM57417_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16cd), .driver_data = BCM58700 },
 { PCI_VDEVICE(BROADCOM, 0x16ce), .driver_data = BCM57311 },
 { PCI_VDEVICE(BROADCOM, 0x16cf), .driver_data = BCM57312 },
 { PCI_VDEVICE(BROADCOM, 0x16d0), .driver_data = BCM57402 },
 { PCI_VDEVICE(BROADCOM, 0x16d1), .driver_data = BCM57404 },
 { PCI_VDEVICE(BROADCOM, 0x16d2), .driver_data = BCM57406 },
 { PCI_VDEVICE(BROADCOM, 0x16d4), .driver_data = BCM57402_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16d5), .driver_data = BCM57407 },
 { PCI_VDEVICE(BROADCOM, 0x16d6), .driver_data = BCM57412 },
 { PCI_VDEVICE(BROADCOM, 0x16d7), .driver_data = BCM57414 },
 { PCI_VDEVICE(BROADCOM, 0x16d8), .driver_data = BCM57416 },
 { PCI_VDEVICE(BROADCOM, 0x16d9), .driver_data = BCM57417 },
 { PCI_VDEVICE(BROADCOM, 0x16de), .driver_data = BCM57412_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16df), .driver_data = BCM57314 },
 { PCI_VDEVICE(BROADCOM, 0x16e2), .driver_data = BCM57417_SFP },
 { PCI_VDEVICE(BROADCOM, 0x16e3), .driver_data = BCM57416_SFP },
 { PCI_VDEVICE(BROADCOM, 0x16e7), .driver_data = BCM57404_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16e8), .driver_data = BCM57406_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16e9), .driver_data = BCM57407_SFP },
 { PCI_VDEVICE(BROADCOM, 0x16ea), .driver_data = BCM57407_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16eb), .driver_data = BCM57412_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16ec), .driver_data = BCM57414_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16ed), .driver_data = BCM57414_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16ee), .driver_data = BCM57416_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16ef), .driver_data = BCM57416_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x16f0), .driver_data = BCM58808 },
 { PCI_VDEVICE(BROADCOM, 0x16f1), .driver_data = BCM57452 },
 { PCI_VDEVICE(BROADCOM, 0x1750), .driver_data = BCM57508 },
 { PCI_VDEVICE(BROADCOM, 0x1751), .driver_data = BCM57504 },
 { PCI_VDEVICE(BROADCOM, 0x1752), .driver_data = BCM57502 },
 { PCI_VDEVICE(BROADCOM, 0x1760), .driver_data = BCM57608 },
 { PCI_VDEVICE(BROADCOM, 0x1761), .driver_data = BCM57604 },
 { PCI_VDEVICE(BROADCOM, 0x1762), .driver_data = BCM57602 },
 { PCI_VDEVICE(BROADCOM, 0x1763), .driver_data = BCM57601 },
 { PCI_VDEVICE(BROADCOM, 0x1800), .driver_data = BCM57502_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1801), .driver_data = BCM57504_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1802), .driver_data = BCM57508_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1803), .driver_data = BCM57502_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1804), .driver_data = BCM57504_NPAR },
 { PCI_VDEVICE(BROADCOM, 0x1805), .driver_data = BCM57508_NPAR },
 { PCI_VDEVICE(BROADCOM, 0xd802), .driver_data = BCM58802 },
 { PCI_VDEVICE(BROADCOM, 0xd804), .driver_data = BCM58804 },
#ifdef CONFIG_BNXT_SRIOV
 { PCI_VDEVICE(BROADCOM, 0x1606), .driver_data = NETXTREME_E_VF },
 { PCI_VDEVICE(BROADCOM, 0x1607), .driver_data = NETXTREME_E_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x1608), .driver_data = NETXTREME_E_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x1609), .driver_data = NETXTREME_E_VF },
 { PCI_VDEVICE(BROADCOM, 0x16bd), .driver_data = NETXTREME_E_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x16c1), .driver_data = NETXTREME_E_VF },
 { PCI_VDEVICE(BROADCOM, 0x16c2), .driver_data = NETXTREME_C_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x16c3), .driver_data = NETXTREME_C_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x16c4), .driver_data = NETXTREME_E_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x16c5), .driver_data = NETXTREME_E_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x16cb), .driver_data = NETXTREME_C_VF },
 { PCI_VDEVICE(BROADCOM, 0x16d3), .driver_data = NETXTREME_E_VF },
 { PCI_VDEVICE(BROADCOM, 0x16dc), .driver_data = NETXTREME_E_VF },
 { PCI_VDEVICE(BROADCOM, 0x16e1), .driver_data = NETXTREME_C_VF },
 { PCI_VDEVICE(BROADCOM, 0x16e5), .driver_data = NETXTREME_C_VF },
 { PCI_VDEVICE(BROADCOM, 0x16e6), .driver_data = NETXTREME_C_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x1806), .driver_data = NETXTREME_E_P5_VF },
 { PCI_VDEVICE(BROADCOM, 0x1807), .driver_data = NETXTREME_E_P5_VF },
 { PCI_VDEVICE(BROADCOM, 0x1808), .driver_data = NETXTREME_E_P5_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x1809), .driver_data = NETXTREME_E_P5_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0x1819), .driver_data = NETXTREME_E_P7_VF },
 { PCI_VDEVICE(BROADCOM, 0x181b), .driver_data = NETXTREME_E_P7_VF_HV },
 { PCI_VDEVICE(BROADCOM, 0xd800), .driver_data = NETXTREME_S_VF },
#endif
 { 0 }
};

MODULE_DEVICE_TABLE(pci, bnxt_pci_tbl);

static const u16 bnxt_vf_req_snif[] = {
 HWRM_FUNC_CFG,
 HWRM_FUNC_VF_CFG,
 HWRM_PORT_PHY_QCFG,
 HWRM_CFA_L2_FILTER_ALLOC,
};

static const u16 bnxt_async_events_arr[] = {
 ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE,
 ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE,
 ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD,
 ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED,
 ASYNC_EVENT_CMPL_EVENT_ID_VF_CFG_CHANGE,
 ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_CHANGE,
 ASYNC_EVENT_CMPL_EVENT_ID_PORT_PHY_CFG_CHANGE,
 ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY,
 ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY,
 ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION,
 ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE,
 ASYNC_EVENT_CMPL_EVENT_ID_RING_MONITOR_MSG,
 ASYNC_EVENT_CMPL_EVENT_ID_ECHO_REQUEST,
 ASYNC_EVENT_CMPL_EVENT_ID_PPS_TIMESTAMP,
 ASYNC_EVENT_CMPL_EVENT_ID_ERROR_REPORT,
 ASYNC_EVENT_CMPL_EVENT_ID_PHC_UPDATE,
 ASYNC_EVENT_CMPL_EVENT_ID_DBG_BUF_PRODUCER,
};

const u16 bnxt_bstore_to_trace[] = {
 [BNXT_CTX_SRT]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_SRT_TRACE,
 [BNXT_CTX_SRT2]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_SRT2_TRACE,
 [BNXT_CTX_CRT]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_CRT_TRACE,
 [BNXT_CTX_CRT2]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_CRT2_TRACE,
 [BNXT_CTX_RIGP0] = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_RIGP0_TRACE,
 [BNXT_CTX_L2HWRM] = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_L2_HWRM_TRACE,
 [BNXT_CTX_REHWRM] = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_ROCE_HWRM_TRACE,
 [BNXT_CTX_CA0]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_CA0_TRACE,
 [BNXT_CTX_CA1]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_CA1_TRACE,
 [BNXT_CTX_CA2]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_CA2_TRACE,
 [BNXT_CTX_RIGP1] = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_RIGP1_TRACE,
 [BNXT_CTX_KONG]  = DBG_LOG_BUFFER_FLUSH_REQ_TYPE_AFM_KONG_HWRM_TRACE,
};

static struct workqueue_struct *bnxt_pf_wq;

#define BNXT_IPV6_MASK_ALL {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
          0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
#define BNXT_IPV6_MASK_NONE {{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}}

const struct bnxt_flow_masks BNXT_FLOW_MASK_NONE = {
 .ports = {
  .src = 0,
  .dst = 0,
 },
 .addrs = {
  .v6addrs = {
   .src = BNXT_IPV6_MASK_NONE,
   .dst = BNXT_IPV6_MASK_NONE,
  },
 },
};

const struct bnxt_flow_masks BNXT_FLOW_IPV6_MASK_ALL = {
 .ports = {
  .src = cpu_to_be16(0xffff),
  .dst = cpu_to_be16(0xffff),
 },
 .addrs = {
  .v6addrs = {
   .src = BNXT_IPV6_MASK_ALL,
   .dst = BNXT_IPV6_MASK_ALL,
  },
 },
};

const struct bnxt_flow_masks BNXT_FLOW_IPV4_MASK_ALL = {
 .ports = {
  .src = cpu_to_be16(0xffff),
  .dst = cpu_to_be16(0xffff),
 },
 .addrs = {
  .v4addrs = {
   .src = cpu_to_be32(0xffffffff),
   .dst = cpu_to_be32(0xffffffff),
  },
 },
};

static bool bnxt_vf_pciid(enum board_idx idx)
{
 return (idx == NETXTREME_C_VF || idx == NETXTREME_E_VF ||
  idx == NETXTREME_S_VF || idx == NETXTREME_C_VF_HV ||
  idx == NETXTREME_E_VF_HV || idx == NETXTREME_E_P5_VF ||
  idx == NETXTREME_E_P5_VF_HV || idx == NETXTREME_E_P7_VF ||
  idx == NETXTREME_E_P7_VF_HV);
}

#define DB_CP_REARM_FLAGS (DB_KEY_CP | DB_IDX_VALID)
#define DB_CP_FLAGS  (DB_KEY_CP | DB_IDX_VALID | DB_IRQ_DIS)

#define BNXT_DB_CQ(db, idx)      \
 writel(DB_CP_FLAGS | DB_RING_IDX(db, idx), (db)->doorbell)

#define BNXT_DB_NQ_P5(db, idx)      \
 bnxt_writeq(bp, (db)->db_key64 | DBR_TYPE_NQ | DB_RING_IDX(db, idx),\
      (db)->doorbell)

#define BNXT_DB_NQ_P7(db, idx)      \
 bnxt_writeq(bp, (db)->db_key64 | DBR_TYPE_NQ_MASK |  \
      DB_RING_IDX(db, idx), (db)->doorbell)

#define BNXT_DB_CQ_ARM(db, idx)      \
 writel(DB_CP_REARM_FLAGS | DB_RING_IDX(db, idx), (db)->doorbell)

#define BNXT_DB_NQ_ARM_P5(db, idx)     \
 bnxt_writeq(bp, (db)->db_key64 | DBR_TYPE_NQ_ARM |  \
      DB_RING_IDX(db, idx), (db)->doorbell)

static void bnxt_db_nq(struct bnxt *bp, struct bnxt_db_info *db, u32 idx)
{
 if (bp->flags & BNXT_FLAG_CHIP_P7)
  BNXT_DB_NQ_P7(db, idx);
 else if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
  BNXT_DB_NQ_P5(db, idx);
 else
  BNXT_DB_CQ(db, idx);
}

static void bnxt_db_nq_arm(struct bnxt *bp, struct bnxt_db_info *db, u32 idx)
{
 if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
  BNXT_DB_NQ_ARM_P5(db, idx);
 else
  BNXT_DB_CQ_ARM(db, idx);
}

static void bnxt_db_cq(struct bnxt *bp, struct bnxt_db_info *db, u32 idx)
{
 if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
  bnxt_writeq(bp, db->db_key64 | DBR_TYPE_CQ_ARMALL |
       DB_RING_IDX(db, idx), db->doorbell);
 else
  BNXT_DB_CQ(db, idx);
}

static void bnxt_queue_fw_reset_work(struct bnxt *bp, unsigned long delay)
{
 if (!(test_bit(BNXT_STATE_IN_FW_RESET, &bp->state)))
  return;

 if (BNXT_PF(bp))
  queue_delayed_work(bnxt_pf_wq, &bp->fw_reset_task, delay);
 else
  schedule_delayed_work(&bp->fw_reset_task, delay);
}

static void __bnxt_queue_sp_work(struct bnxt *bp)
{
 if (BNXT_PF(bp))
  queue_work(bnxt_pf_wq, &bp->sp_task);
 else
  schedule_work(&bp->sp_task);
}

static void bnxt_queue_sp_work(struct bnxt *bp, unsigned int event)
{
 set_bit(event, &bp->sp_event);
 __bnxt_queue_sp_work(bp);
}

static void bnxt_sched_reset_rxr(struct bnxt *bp, struct bnxt_rx_ring_info *rxr)
{
 if (!rxr->bnapi->in_reset) {
  rxr->bnapi->in_reset = true;
  if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
   set_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event);
  else
   set_bit(BNXT_RST_RING_SP_EVENT, &bp->sp_event);
  __bnxt_queue_sp_work(bp);
 }
 rxr->rx_next_cons = 0xffff;
}

void bnxt_sched_reset_txr(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
     u16 curr)
{
 struct bnxt_napi *bnapi = txr->bnapi;

 if (bnapi->tx_fault)
  return;

 netdev_err(bp->dev, "Invalid Tx completion (ring:%d tx_hw_cons:%u cons:%u prod:%u curr:%u)",
     txr->txq_index, txr->tx_hw_cons,
     txr->tx_cons, txr->tx_prod, curr);
 WARN_ON_ONCE(1);
 bnapi->tx_fault = 1;
 bnxt_queue_sp_work(bp, BNXT_RESET_TASK_SP_EVENT);
}

const u16 bnxt_lhint_arr[] = {
 TX_BD_FLAGS_LHINT_512_AND_SMALLER,
 TX_BD_FLAGS_LHINT_512_TO_1023,
 TX_BD_FLAGS_LHINT_1024_TO_2047,
 TX_BD_FLAGS_LHINT_1024_TO_2047,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
 TX_BD_FLAGS_LHINT_2048_AND_LARGER,
};

static u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb)
{
 struct metadata_dst *md_dst = skb_metadata_dst(skb);

 if (!md_dst || md_dst->type != METADATA_HW_PORT_MUX)
  return 0;

 return md_dst->u.port_info.port_id;
}

static void bnxt_txr_db_kick(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
        u16 prod)
{
 /* Sync BD data before updating doorbell */
 wmb();
 bnxt_db_write(bp, &txr->tx_db, prod);
 txr->kick_pending = 0;
}

static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
 struct bnxt *bp = netdev_priv(dev);
 struct tx_bd *txbd, *txbd0;
 struct tx_bd_ext *txbd1;
 struct netdev_queue *txq;
 int i;
 dma_addr_t mapping;
 unsigned int length, pad = 0;
 u32 len, free_size, vlan_tag_flags, cfa_action, flags;
 struct bnxt_ptp_cfg *ptp = bp->ptp_cfg;
 struct pci_dev *pdev = bp->pdev;
 u16 prod, last_frag, txts_prod;
 struct bnxt_tx_ring_info *txr;
 struct bnxt_sw_tx_bd *tx_buf;
 __le32 lflags = 0;
 skb_frag_t *frag;

 i = skb_get_queue_mapping(skb);
 if (unlikely(i >= bp->tx_nr_rings)) {
  dev_kfree_skb_any(skb);
  dev_core_stats_tx_dropped_inc(dev);
  return NETDEV_TX_OK;
 }

 txq = netdev_get_tx_queue(dev, i);
 txr = &bp->tx_ring[bp->tx_ring_map[i]];
 prod = txr->tx_prod;

#if (MAX_SKB_FRAGS > TX_MAX_FRAGS)
 if (skb_shinfo(skb)->nr_frags > TX_MAX_FRAGS) {
  netdev_warn_once(dev, "SKB has too many (%d) fragments, max supported is %d. SKB will be linearized.\n",
     skb_shinfo(skb)->nr_frags, TX_MAX_FRAGS);
  if (skb_linearize(skb)) {
   dev_kfree_skb_any(skb);
   dev_core_stats_tx_dropped_inc(dev);
   return NETDEV_TX_OK;
  }
 }
#endif
 free_size = bnxt_tx_avail(bp, txr);
 if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) {
  /* We must have raced with NAPI cleanup */
  if (net_ratelimit() && txr->kick_pending)
   netif_warn(bp, tx_err, dev,
       "bnxt: ring busy w/ flush pending!\n");
  if (!netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
     bp->tx_wake_thresh))
   return NETDEV_TX_BUSY;
 }

 if (unlikely(ipv6_hopopt_jumbo_remove(skb)))
  goto tx_free;

 length = skb->len;
 len = skb_headlen(skb);
 last_frag = skb_shinfo(skb)->nr_frags;

 txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];

 tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
 tx_buf->skb = skb;
 tx_buf->nr_frags = last_frag;

 vlan_tag_flags = 0;
 cfa_action = bnxt_xmit_get_cfa_action(skb);
 if (skb_vlan_tag_present(skb)) {
  vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN |
     skb_vlan_tag_get(skb);
  /* Currently supports 8021Q, 8021AD vlan offloads
 * QINQ1, QINQ2, QINQ3 vlan headers are deprecated
 */

  if (skb->vlan_proto == htons(ETH_P_8021Q))
   vlan_tag_flags |= 1 << TX_BD_CFA_META_TPID_SHIFT;
 }

 if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && ptp &&
     ptp->tx_tstamp_en) {
  if (bp->fw_cap & BNXT_FW_CAP_TX_TS_CMP) {
   lflags |= cpu_to_le32(TX_BD_FLAGS_STAMP);
   tx_buf->is_ts_pkt = 1;
   skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
  } else if (!skb_is_gso(skb)) {
   u16 seq_id, hdr_off;

   if (!bnxt_ptp_parse(skb, &seq_id, &hdr_off) &&
       !bnxt_ptp_get_txts_prod(ptp, &txts_prod)) {
    if (vlan_tag_flags)
     hdr_off += VLAN_HLEN;
    lflags |= cpu_to_le32(TX_BD_FLAGS_STAMP);
    tx_buf->is_ts_pkt = 1;
    skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;

    ptp->txts_req[txts_prod].tx_seqid = seq_id;
    ptp->txts_req[txts_prod].tx_hdr_off = hdr_off;
    tx_buf->txts_prod = txts_prod;
   }
  }
 }
 if (unlikely(skb->no_fcs))
  lflags |= cpu_to_le32(TX_BD_FLAGS_NO_CRC);

 if (free_size == bp->tx_ring_size && length <= bp->tx_push_thresh &&
     skb_frags_readable(skb) && !lflags) {
  struct tx_push_buffer *tx_push_buf = txr->tx_push;
  struct tx_push_bd *tx_push = &tx_push_buf->push_bd;
  struct tx_bd_ext *tx_push1 = &tx_push->txbd2;
  void __iomem *db = txr->tx_db.doorbell;
  void *pdata = tx_push_buf->data;
  u64 *end;
  int j, push_len;

  /* Set COAL_NOW to be ready quickly for the next push */
  tx_push->tx_bd_len_flags_type =
   cpu_to_le32((length << TX_BD_LEN_SHIFT) |
     TX_BD_TYPE_LONG_TX_BD |
     TX_BD_FLAGS_LHINT_512_AND_SMALLER |
     TX_BD_FLAGS_COAL_NOW |
     TX_BD_FLAGS_PACKET_END |
     TX_BD_CNT(2));

  if (skb->ip_summed == CHECKSUM_PARTIAL)
   tx_push1->tx_bd_hsize_lflags =
     cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
  else
   tx_push1->tx_bd_hsize_lflags = 0;

  tx_push1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags);
  tx_push1->tx_bd_cfa_action =
   cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT);

  end = pdata + length;
  end = PTR_ALIGN(end, 8) - 1;
  *end = 0;

  skb_copy_from_linear_data(skb, pdata, len);
  pdata += len;
  for (j = 0; j < last_frag; j++) {
   void *fptr;

   frag = &skb_shinfo(skb)->frags[j];
   fptr = skb_frag_address_safe(frag);
   if (!fptr)
    goto normal_tx;

   memcpy(pdata, fptr, skb_frag_size(frag));
   pdata += skb_frag_size(frag);
  }

  txbd->tx_bd_len_flags_type = tx_push->tx_bd_len_flags_type;
  txbd->tx_bd_haddr = txr->data_mapping;
  txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod, 2);
  prod = NEXT_TX(prod);
  tx_push->tx_bd_opaque = txbd->tx_bd_opaque;
  txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
  memcpy(txbd, tx_push1, sizeof(*txbd));
  prod = NEXT_TX(prod);
  tx_push->doorbell =
   cpu_to_le32(DB_KEY_TX_PUSH | DB_LONG_TX_PUSH |
        DB_RING_IDX(&txr->tx_db, prod));
  WRITE_ONCE(txr->tx_prod, prod);

  tx_buf->is_push = 1;
  netdev_tx_sent_queue(txq, skb->len);
  wmb(); /* Sync is_push and byte queue before pushing data */

  push_len = (length + sizeof(*tx_push) + 7) / 8;
  if (push_len > 16) {
   __iowrite64_copy(db, tx_push_buf, 16);
   __iowrite32_copy(db + 4, tx_push_buf + 1,
      (push_len - 16) << 1);
  } else {
   __iowrite64_copy(db, tx_push_buf, push_len);
  }

  goto tx_done;
 }

normal_tx:
 if (length < BNXT_MIN_PKT_SIZE) {
  pad = BNXT_MIN_PKT_SIZE - length;
  if (skb_pad(skb, pad))
   /* SKB already freed. */
   goto tx_kick_pending;
  length = BNXT_MIN_PKT_SIZE;
 }

 mapping = dma_map_single(&pdev->dev, skb->data, len, DMA_TO_DEVICE);

 if (unlikely(dma_mapping_error(&pdev->dev, mapping)))
  goto tx_free;

 dma_unmap_addr_set(tx_buf, mapping, mapping);
 flags = (len << TX_BD_LEN_SHIFT) | TX_BD_TYPE_LONG_TX_BD |
  TX_BD_CNT(last_frag + 2);

 txbd->tx_bd_haddr = cpu_to_le64(mapping);
 txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod, 2 + last_frag);

 prod = NEXT_TX(prod);
 txbd1 = (struct tx_bd_ext *)
  &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];

 txbd1->tx_bd_hsize_lflags = lflags;
 if (skb_is_gso(skb)) {
  bool udp_gso = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4);
  u32 hdr_len;

  if (skb->encapsulation) {
   if (udp_gso)
    hdr_len = skb_inner_transport_offset(skb) +
       sizeof(struct udphdr);
   else
    hdr_len = skb_inner_tcp_all_headers(skb);
  } else if (udp_gso) {
   hdr_len = skb_transport_offset(skb) +
      sizeof(struct udphdr);
  } else {
   hdr_len = skb_tcp_all_headers(skb);
  }

  txbd1->tx_bd_hsize_lflags |= cpu_to_le32(TX_BD_FLAGS_LSO |
     TX_BD_FLAGS_T_IPID |
     (hdr_len << (TX_BD_HSIZE_SHIFT - 1)));
  length = skb_shinfo(skb)->gso_size;
  txbd1->tx_bd_mss = cpu_to_le32(length);
  length += hdr_len;
 } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
  txbd1->tx_bd_hsize_lflags |=
   cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
  txbd1->tx_bd_mss = 0;
 }

 length >>= 9;
 if (unlikely(length >= ARRAY_SIZE(bnxt_lhint_arr))) {
  dev_warn_ratelimited(&pdev->dev, "Dropped oversize %d bytes TX packet.\n",
         skb->len);
  i = 0;
  goto tx_dma_error;
 }
 flags |= bnxt_lhint_arr[length];
 txbd->tx_bd_len_flags_type = cpu_to_le32(flags);

 txbd1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags);
 txbd1->tx_bd_cfa_action =
   cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT);
 txbd0 = txbd;
 for (i = 0; i < last_frag; i++) {
  frag = &skb_shinfo(skb)->frags[i];
  prod = NEXT_TX(prod);
  txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];

  len = skb_frag_size(frag);
  mapping = skb_frag_dma_map(&pdev->dev, frag, 0, len,
        DMA_TO_DEVICE);

  if (unlikely(dma_mapping_error(&pdev->dev, mapping)))
   goto tx_dma_error;

  tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
  netmem_dma_unmap_addr_set(skb_frag_netmem(frag), tx_buf,
       mapping, mapping);

  txbd->tx_bd_haddr = cpu_to_le64(mapping);

  flags = len << TX_BD_LEN_SHIFT;
  txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
 }

 flags &= ~TX_BD_LEN;
 txbd->tx_bd_len_flags_type =
  cpu_to_le32(((len + pad) << TX_BD_LEN_SHIFT) | flags |
       TX_BD_FLAGS_PACKET_END);

 netdev_tx_sent_queue(txq, skb->len);

 skb_tx_timestamp(skb);

 prod = NEXT_TX(prod);
 WRITE_ONCE(txr->tx_prod, prod);

 if (!netdev_xmit_more() || netif_xmit_stopped(txq)) {
  bnxt_txr_db_kick(bp, txr, prod);
 } else {
  if (free_size >= bp->tx_wake_thresh)
   txbd0->tx_bd_len_flags_type |=
    cpu_to_le32(TX_BD_FLAGS_NO_CMPL);
  txr->kick_pending = 1;
 }

tx_done:

 if (unlikely(bnxt_tx_avail(bp, txr) <= MAX_SKB_FRAGS + 1)) {
  if (netdev_xmit_more() && !tx_buf->is_push) {
   txbd0->tx_bd_len_flags_type &=
    cpu_to_le32(~TX_BD_FLAGS_NO_CMPL);
   bnxt_txr_db_kick(bp, txr, prod);
  }

  netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
       bp->tx_wake_thresh);
 }
 return NETDEV_TX_OK;

tx_dma_error:
 last_frag = i;

 /* start back at beginning and unmap skb */
 prod = txr->tx_prod;
 tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
 dma_unmap_single(&pdev->dev, dma_unmap_addr(tx_buf, mapping),
    skb_headlen(skb), DMA_TO_DEVICE);
 prod = NEXT_TX(prod);

 /* unmap remaining mapped pages */
 for (i = 0; i < last_frag; i++) {
  prod = NEXT_TX(prod);
  tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
  frag = &skb_shinfo(skb)->frags[i];
  netmem_dma_unmap_page_attrs(&pdev->dev,
         dma_unmap_addr(tx_buf, mapping),
         skb_frag_size(frag),
         DMA_TO_DEVICE, 0);
 }

tx_free:
 dev_kfree_skb_any(skb);
tx_kick_pending:
 if (BNXT_TX_PTP_IS_SET(lflags)) {
  txr->tx_buf_ring[RING_TX(bp, txr->tx_prod)].is_ts_pkt = 0;
  atomic64_inc(&bp->ptp_cfg->stats.ts_err);
  if (!(bp->fw_cap & BNXT_FW_CAP_TX_TS_CMP))
   /* set SKB to err so PTP worker will clean up */
   ptp->txts_req[txts_prod].tx_skb = ERR_PTR(-EIO);
 }
 if (txr->kick_pending)
  bnxt_txr_db_kick(bp, txr, txr->tx_prod);
 txr->tx_buf_ring[RING_TX(bp, txr->tx_prod)].skb = NULL;
 dev_core_stats_tx_dropped_inc(dev);
 return NETDEV_TX_OK;
}

/* Returns true if some remaining TX packets not processed. */
static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
     int budget)
{
 struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, txr->txq_index);
 struct pci_dev *pdev = bp->pdev;
 u16 hw_cons = txr->tx_hw_cons;
 unsigned int tx_bytes = 0;
 u16 cons = txr->tx_cons;
 skb_frag_t *frag;
 int tx_pkts = 0;
 bool rc = false;

 while (RING_TX(bp, cons) != hw_cons) {
  struct bnxt_sw_tx_bd *tx_buf;
  struct sk_buff *skb;
  bool is_ts_pkt;
  int j, last;

  tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
  skb = tx_buf->skb;

  if (unlikely(!skb)) {
   bnxt_sched_reset_txr(bp, txr, cons);
   return rc;
  }

  is_ts_pkt = tx_buf->is_ts_pkt;
  if (is_ts_pkt && (bp->fw_cap & BNXT_FW_CAP_TX_TS_CMP)) {
   rc = true;
   break;
  }

  cons = NEXT_TX(cons);
  tx_pkts++;
  tx_bytes += skb->len;
  tx_buf->skb = NULL;
  tx_buf->is_ts_pkt = 0;

  if (tx_buf->is_push) {
   tx_buf->is_push = 0;
   goto next_tx_int;
  }

  dma_unmap_single(&pdev->dev, dma_unmap_addr(tx_buf, mapping),
     skb_headlen(skb), DMA_TO_DEVICE);
  last = tx_buf->nr_frags;

  for (j = 0; j < last; j++) {
   frag = &skb_shinfo(skb)->frags[j];
   cons = NEXT_TX(cons);
   tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
   netmem_dma_unmap_page_attrs(&pdev->dev,
          dma_unmap_addr(tx_buf,
           mapping),
          skb_frag_size(frag),
          DMA_TO_DEVICE, 0);
  }
  if (unlikely(is_ts_pkt)) {
   if (BNXT_CHIP_P5(bp)) {
    /* PTP worker takes ownership of the skb */
    bnxt_get_tx_ts_p5(bp, skb, tx_buf->txts_prod);
    skb = NULL;
   }
  }

next_tx_int:
  cons = NEXT_TX(cons);

  dev_consume_skb_any(skb);
 }

 WRITE_ONCE(txr->tx_cons, cons);

 __netif_txq_completed_wake(txq, tx_pkts, tx_bytes,
       bnxt_tx_avail(bp, txr), bp->tx_wake_thresh,
       READ_ONCE(txr->dev_state) == BNXT_DEV_STATE_CLOSING);

 return rc;
}

static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int budget)
{
 struct bnxt_tx_ring_info *txr;
 bool more = false;
 int i;

 bnxt_for_each_napi_tx(i, bnapi, txr) {
  if (txr->tx_hw_cons != RING_TX(bp, txr->tx_cons))
   more |= __bnxt_tx_int(bp, txr, budget);
 }
 if (!more)
  bnapi->events &= ~BNXT_TX_CMP_EVENT;
}

static bool bnxt_separate_head_pool(struct bnxt_rx_ring_info *rxr)
{
 return rxr->need_head_pool || PAGE_SIZE > BNXT_RX_PAGE_SIZE;
}

static struct page *__bnxt_alloc_rx_page(struct bnxt *bp, dma_addr_t *mapping,
      struct bnxt_rx_ring_info *rxr,
      unsigned int *offset,
      gfp_t gfp)
{
 struct page *page;

 if (PAGE_SIZE > BNXT_RX_PAGE_SIZE) {
  page = page_pool_dev_alloc_frag(rxr->page_pool, offset,
      BNXT_RX_PAGE_SIZE);
 } else {
  page = page_pool_dev_alloc_pages(rxr->page_pool);
  *offset = 0;
 }
 if (!page)
  return NULL;

 *mapping = page_pool_get_dma_addr(page) + *offset;
 return page;
}

static netmem_ref __bnxt_alloc_rx_netmem(struct bnxt *bp, dma_addr_t *mapping,
      struct bnxt_rx_ring_info *rxr,
      unsigned int *offset,
      gfp_t gfp)
{
 netmem_ref netmem;

 if (PAGE_SIZE > BNXT_RX_PAGE_SIZE) {
  netmem = page_pool_alloc_frag_netmem(rxr->page_pool, offset, BNXT_RX_PAGE_SIZE, gfp);
 } else {
  netmem = page_pool_alloc_netmems(rxr->page_pool, gfp);
  *offset = 0;
 }
 if (!netmem)
  return 0;

 *mapping = page_pool_get_dma_addr_netmem(netmem) + *offset;
 return netmem;
}

static inline u8 *__bnxt_alloc_rx_frag(struct bnxt *bp, dma_addr_t *mapping,
           struct bnxt_rx_ring_info *rxr,
           gfp_t gfp)
{
 unsigned int offset;
 struct page *page;

 page = page_pool_alloc_frag(rxr->head_pool, &offset,
        bp->rx_buf_size, gfp);
 if (!page)
  return NULL;

 *mapping = page_pool_get_dma_addr(page) + bp->rx_dma_offset + offset;
 return page_address(page) + offset;
}

int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
         u16 prod, gfp_t gfp)
{
 struct rx_bd *rxbd = &rxr->rx_desc_ring[RX_RING(bp, prod)][RX_IDX(prod)];
 struct bnxt_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[RING_RX(bp, prod)];
 dma_addr_t mapping;

 if (BNXT_RX_PAGE_MODE(bp)) {
  unsigned int offset;
  struct page *page =
   __bnxt_alloc_rx_page(bp, &mapping, rxr, &offset, gfp);

  if (!page)
   return -ENOMEM;

  mapping += bp->rx_dma_offset;
  rx_buf->data = page;
  rx_buf->data_ptr = page_address(page) + offset + bp->rx_offset;
 } else {
  u8 *data = __bnxt_alloc_rx_frag(bp, &mapping, rxr, gfp);

  if (!data)
   return -ENOMEM;

  rx_buf->data = data;
  rx_buf->data_ptr = data + bp->rx_offset;
 }
 rx_buf->mapping = mapping;

 rxbd->rx_bd_haddr = cpu_to_le64(mapping);
 return 0;
}

void bnxt_reuse_rx_data(struct bnxt_rx_ring_info *rxr, u16 cons, void *data)
{
 u16 prod = rxr->rx_prod;
 struct bnxt_sw_rx_bd *cons_rx_buf, *prod_rx_buf;
 struct bnxt *bp = rxr->bnapi->bp;
 struct rx_bd *cons_bd, *prod_bd;

 prod_rx_buf = &rxr->rx_buf_ring[RING_RX(bp, prod)];
 cons_rx_buf = &rxr->rx_buf_ring[cons];

 prod_rx_buf->data = data;
 prod_rx_buf->data_ptr = cons_rx_buf->data_ptr;

 prod_rx_buf->mapping = cons_rx_buf->mapping;

 prod_bd = &rxr->rx_desc_ring[RX_RING(bp, prod)][RX_IDX(prod)];
 cons_bd = &rxr->rx_desc_ring[RX_RING(bp, cons)][RX_IDX(cons)];

 prod_bd->rx_bd_haddr = cons_bd->rx_bd_haddr;
}

static inline u16 bnxt_find_next_agg_idx(struct bnxt_rx_ring_info *rxr, u16 idx)
{
 u16 next, max = rxr->rx_agg_bmap_size;

 next = find_next_zero_bit(rxr->rx_agg_bmap, max, idx);
 if (next >= max)
  next = find_first_zero_bit(rxr->rx_agg_bmap, max);
 return next;
}

static int bnxt_alloc_rx_netmem(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
    u16 prod, gfp_t gfp)
{
 struct rx_bd *rxbd =
  &rxr->rx_agg_desc_ring[RX_AGG_RING(bp, prod)][RX_IDX(prod)];
 struct bnxt_sw_rx_agg_bd *rx_agg_buf;
 u16 sw_prod = rxr->rx_sw_agg_prod;
 unsigned int offset = 0;
 dma_addr_t mapping;
 netmem_ref netmem;

 netmem = __bnxt_alloc_rx_netmem(bp, &mapping, rxr, &offset, gfp);
 if (!netmem)
  return -ENOMEM;

 if (unlikely(test_bit(sw_prod, rxr->rx_agg_bmap)))
  sw_prod = bnxt_find_next_agg_idx(rxr, sw_prod);

 __set_bit(sw_prod, rxr->rx_agg_bmap);
 rx_agg_buf = &rxr->rx_agg_ring[sw_prod];
 rxr->rx_sw_agg_prod = RING_RX_AGG(bp, NEXT_RX_AGG(sw_prod));

 rx_agg_buf->netmem = netmem;
 rx_agg_buf->offset = offset;
 rx_agg_buf->mapping = mapping;
 rxbd->rx_bd_haddr = cpu_to_le64(mapping);
 rxbd->rx_bd_opaque = sw_prod;
 return 0;
}

static struct rx_agg_cmp *bnxt_get_agg(struct bnxt *bp,
           struct bnxt_cp_ring_info *cpr,
           u16 cp_cons, u16 curr)
{
 struct rx_agg_cmp *agg;

 cp_cons = RING_CMP(ADV_RAW_CMP(cp_cons, curr));
 agg = (struct rx_agg_cmp *)
  &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];
 return agg;
}

static struct rx_agg_cmp *bnxt_get_tpa_agg_p5(struct bnxt *bp,
           struct bnxt_rx_ring_info *rxr,
           u16 agg_id, u16 curr)
{
 struct bnxt_tpa_info *tpa_info = &rxr->rx_tpa[agg_id];

 return &tpa_info->agg_arr[curr];
}

static void bnxt_reuse_rx_agg_bufs(struct bnxt_cp_ring_info *cpr, u16 idx,
       u16 start, u32 agg_bufs, bool tpa)
{
 struct bnxt_napi *bnapi = cpr->bnapi;
 struct bnxt *bp = bnapi->bp;
 struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
 u16 prod = rxr->rx_agg_prod;
 u16 sw_prod = rxr->rx_sw_agg_prod;
 bool p5_tpa = false;
 u32 i;

 if ((bp->flags & BNXT_FLAG_CHIP_P5_PLUS) && tpa)
  p5_tpa = true;

 for (i = 0; i < agg_bufs; i++) {
  struct bnxt_sw_rx_agg_bd *cons_rx_buf, *prod_rx_buf;
  struct rx_agg_cmp *agg;
  struct rx_bd *prod_bd;
  netmem_ref netmem;
  u16 cons;

  if (p5_tpa)
   agg = bnxt_get_tpa_agg_p5(bp, rxr, idx, start + i);
  else
   agg = bnxt_get_agg(bp, cpr, idx, start + i);
  cons = agg->rx_agg_cmp_opaque;
  __clear_bit(cons, rxr->rx_agg_bmap);

  if (unlikely(test_bit(sw_prod, rxr->rx_agg_bmap)))
   sw_prod = bnxt_find_next_agg_idx(rxr, sw_prod);

  __set_bit(sw_prod, rxr->rx_agg_bmap);
  prod_rx_buf = &rxr->rx_agg_ring[sw_prod];
  cons_rx_buf = &rxr->rx_agg_ring[cons];

  /* It is possible for sw_prod to be equal to cons, so
 * set cons_rx_buf->netmem to 0 first.
 */

  netmem = cons_rx_buf->netmem;
  cons_rx_buf->netmem = 0;
  prod_rx_buf->netmem = netmem;
  prod_rx_buf->offset = cons_rx_buf->offset;

  prod_rx_buf->mapping = cons_rx_buf->mapping;

  prod_bd = &rxr->rx_agg_desc_ring[RX_AGG_RING(bp, prod)][RX_IDX(prod)];

  prod_bd->rx_bd_haddr = cpu_to_le64(cons_rx_buf->mapping);
  prod_bd->rx_bd_opaque = sw_prod;

  prod = NEXT_RX_AGG(prod);
  sw_prod = RING_RX_AGG(bp, NEXT_RX_AGG(sw_prod));
 }
 rxr->rx_agg_prod = prod;
 rxr->rx_sw_agg_prod = sw_prod;
}

static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp,
           struct bnxt_rx_ring_info *rxr,
           u16 cons, void *data, u8 *data_ptr,
           dma_addr_t dma_addr,
           unsigned int offset_and_len)
{
 unsigned int len = offset_and_len & 0xffff;
 struct page *page = data;
 u16 prod = rxr->rx_prod;
 struct sk_buff *skb;
 int err;

 err = bnxt_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC);
 if (unlikely(err)) {
  bnxt_reuse_rx_data(rxr, cons, data);
  return NULL;
 }
 dma_addr -= bp->rx_dma_offset;
 dma_sync_single_for_cpu(&bp->pdev->dev, dma_addr, BNXT_RX_PAGE_SIZE,
    bp->rx_dir);
 skb = napi_build_skb(data_ptr - bp->rx_offset, BNXT_RX_PAGE_SIZE);
 if (!skb) {
  page_pool_recycle_direct(rxr->page_pool, page);
  return NULL;
 }
 skb_mark_for_recycle(skb);
 skb_reserve(skb, bp->rx_offset);
 __skb_put(skb, len);

 return skb;
}

static struct sk_buff *bnxt_rx_page_skb(struct bnxt *bp,
     struct bnxt_rx_ring_info *rxr,
     u16 cons, void *data, u8 *data_ptr,
     dma_addr_t dma_addr,
     unsigned int offset_and_len)
{
 unsigned int payload = offset_and_len >> 16;
 unsigned int len = offset_and_len & 0xffff;
 skb_frag_t *frag;
 struct page *page = data;
 u16 prod = rxr->rx_prod;
 struct sk_buff *skb;
 int off, err;

 err = bnxt_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC);
 if (unlikely(err)) {
  bnxt_reuse_rx_data(rxr, cons, data);
  return NULL;
 }
 dma_addr -= bp->rx_dma_offset;
 dma_sync_single_for_cpu(&bp->pdev->dev, dma_addr, BNXT_RX_PAGE_SIZE,
    bp->rx_dir);

 if (unlikely(!payload))
  payload = eth_get_headlen(bp->dev, data_ptr, len);

 skb = napi_alloc_skb(&rxr->bnapi->napi, payload);
 if (!skb) {
  page_pool_recycle_direct(rxr->page_pool, page);
  return NULL;
 }

 skb_mark_for_recycle(skb);
 off = (void *)data_ptr - page_address(page);
 skb_add_rx_frag(skb, 0, page, off, len, BNXT_RX_PAGE_SIZE);
 memcpy(skb->data - NET_IP_ALIGN, data_ptr - NET_IP_ALIGN,
        payload + NET_IP_ALIGN);

 frag = &skb_shinfo(skb)->frags[0];
 skb_frag_size_sub(frag, payload);
 skb_frag_off_add(frag, payload);
 skb->data_len -= payload;
 skb->tail += payload;

 return skb;
}

static struct sk_buff *bnxt_rx_skb(struct bnxt *bp,
       struct bnxt_rx_ring_info *rxr, u16 cons,
       void *data, u8 *data_ptr,
       dma_addr_t dma_addr,
       unsigned int offset_and_len)
{
 u16 prod = rxr->rx_prod;
 struct sk_buff *skb;
 int err;

 err = bnxt_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC);
 if (unlikely(err)) {
  bnxt_reuse_rx_data(rxr, cons, data);
  return NULL;
 }

 skb = napi_build_skb(data, bp->rx_buf_size);
 dma_sync_single_for_cpu(&bp->pdev->dev, dma_addr, bp->rx_buf_use_size,
    bp->rx_dir);
 if (!skb) {
  page_pool_free_va(rxr->head_pool, data, true);
  return NULL;
 }

 skb_mark_for_recycle(skb);
 skb_reserve(skb, bp->rx_offset);
 skb_put(skb, offset_and_len & 0xffff);
 return skb;
}

static u32 __bnxt_rx_agg_netmems(struct bnxt *bp,
     struct bnxt_cp_ring_info *cpr,
     u16 idx, u32 agg_bufs, bool tpa,
     struct sk_buff *skb,
     struct xdp_buff *xdp)
{
 struct bnxt_napi *bnapi = cpr->bnapi;
 struct skb_shared_info *shinfo;
 struct bnxt_rx_ring_info *rxr;
 u32 i, total_frag_len = 0;
 bool p5_tpa = false;
 u16 prod;

 rxr = bnapi->rx_ring;
 prod = rxr->rx_agg_prod;

 if ((bp->flags & BNXT_FLAG_CHIP_P5_PLUS) && tpa)
  p5_tpa = true;

 if (skb)
  shinfo = skb_shinfo(skb);
 else
  shinfo = xdp_get_shared_info_from_buff(xdp);

 for (i = 0; i < agg_bufs; i++) {
  struct bnxt_sw_rx_agg_bd *cons_rx_buf;
  struct rx_agg_cmp *agg;
  u16 cons, frag_len;
  netmem_ref netmem;

  if (p5_tpa)
   agg = bnxt_get_tpa_agg_p5(bp, rxr, idx, i);
  else
   agg = bnxt_get_agg(bp, cpr, idx, i);
  cons = agg->rx_agg_cmp_opaque;
  frag_len = (le32_to_cpu(agg->rx_agg_cmp_len_flags_type) &
       RX_AGG_CMP_LEN) >> RX_AGG_CMP_LEN_SHIFT;

  cons_rx_buf = &rxr->rx_agg_ring[cons];
  if (skb) {
   skb_add_rx_frag_netmem(skb, i, cons_rx_buf->netmem,
            cons_rx_buf->offset,
            frag_len, BNXT_RX_PAGE_SIZE);
  } else {
   skb_frag_t *frag = &shinfo->frags[i];

   skb_frag_fill_netmem_desc(frag, cons_rx_buf->netmem,
        cons_rx_buf->offset,
        frag_len);
   shinfo->nr_frags = i + 1;
  }
  __clear_bit(cons, rxr->rx_agg_bmap);

  /* It is possible for bnxt_alloc_rx_netmem() to allocate
 * a sw_prod index that equals the cons index, so we
 * need to clear the cons entry now.
 */

  netmem = cons_rx_buf->netmem;
  cons_rx_buf->netmem = 0;

  if (xdp && netmem_is_pfmemalloc(netmem))
   xdp_buff_set_frag_pfmemalloc(xdp);

  if (bnxt_alloc_rx_netmem(bp, rxr, prod, GFP_ATOMIC) != 0) {
   if (skb) {
    skb->len -= frag_len;
    skb->data_len -= frag_len;
    skb->truesize -= BNXT_RX_PAGE_SIZE;
   }

   --shinfo->nr_frags;
   cons_rx_buf->netmem = netmem;

   /* Update prod since possibly some netmems have been
 * allocated already.
 */

   rxr->rx_agg_prod = prod;
   bnxt_reuse_rx_agg_bufs(cpr, idx, i, agg_bufs - i, tpa);
   return 0;
  }

  page_pool_dma_sync_netmem_for_cpu(rxr->page_pool, netmem, 0,
        BNXT_RX_PAGE_SIZE);

  total_frag_len += frag_len;
  prod = NEXT_RX_AGG(prod);
 }
 rxr->rx_agg_prod = prod;
 return total_frag_len;
}

static struct sk_buff *bnxt_rx_agg_netmems_skb(struct bnxt *bp,
            struct bnxt_cp_ring_info *cpr,
            struct sk_buff *skb, u16 idx,
            u32 agg_bufs, bool tpa)
{
 u32 total_frag_len = 0;

 total_frag_len = __bnxt_rx_agg_netmems(bp, cpr, idx, agg_bufs, tpa,
            skb, NULL);
 if (!total_frag_len) {
  skb_mark_for_recycle(skb);
  dev_kfree_skb(skb);
  return NULL;
 }

 return skb;
}

static u32 bnxt_rx_agg_netmems_xdp(struct bnxt *bp,
       struct bnxt_cp_ring_info *cpr,
       struct xdp_buff *xdp, u16 idx,
       u32 agg_bufs, bool tpa)
{
 struct skb_shared_info *shinfo = xdp_get_shared_info_from_buff(xdp);
 u32 total_frag_len = 0;

 if (!xdp_buff_has_frags(xdp))
  shinfo->nr_frags = 0;

 total_frag_len = __bnxt_rx_agg_netmems(bp, cpr, idx, agg_bufs, tpa,
            NULL, xdp);
 if (total_frag_len) {
  xdp_buff_set_frags_flag(xdp);
  shinfo->nr_frags = agg_bufs;
  shinfo->xdp_frags_size = total_frag_len;
 }
 return total_frag_len;
}

static int bnxt_agg_bufs_valid(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
          u8 agg_bufs, u32 *raw_cons)
{
 u16 last;
 struct rx_agg_cmp *agg;

 *raw_cons = ADV_RAW_CMP(*raw_cons, agg_bufs);
 last = RING_CMP(*raw_cons);
 agg = (struct rx_agg_cmp *)
  &cpr->cp_desc_ring[CP_RING(last)][CP_IDX(last)];
 return RX_AGG_CMP_VALID(agg, *raw_cons);
}

static struct sk_buff *bnxt_copy_data(struct bnxt_napi *bnapi, u8 *data,
          unsigned int len,
          dma_addr_t mapping)
{
 struct bnxt *bp = bnapi->bp;
 struct pci_dev *pdev = bp->pdev;
 struct sk_buff *skb;

 skb = napi_alloc_skb(&bnapi->napi, len);
 if (!skb)
  return NULL;

 dma_sync_single_for_cpu(&pdev->dev, mapping, bp->rx_copybreak,
    bp->rx_dir);

 memcpy(skb->data - NET_IP_ALIGN, data - NET_IP_ALIGN,
        len + NET_IP_ALIGN);

 dma_sync_single_for_device(&pdev->dev, mapping, bp->rx_copybreak,
       bp->rx_dir);

 skb_put(skb, len);

 return skb;
}

static struct sk_buff *bnxt_copy_skb(struct bnxt_napi *bnapi, u8 *data,
         unsigned int len,
         dma_addr_t mapping)
{
 return bnxt_copy_data(bnapi, data, len, mapping);
}

static struct sk_buff *bnxt_copy_xdp(struct bnxt_napi *bnapi,
         struct xdp_buff *xdp,
         unsigned int len,
         dma_addr_t mapping)
{
 unsigned int metasize = 0;
 u8 *data = xdp->data;
 struct sk_buff *skb;

 len = xdp->data_end - xdp->data_meta;
 metasize = xdp->data - xdp->data_meta;
 data = xdp->data_meta;

 skb = bnxt_copy_data(bnapi, data, len, mapping);
 if (!skb)
  return skb;

 if (metasize) {
  skb_metadata_set(skb, metasize);
  __skb_pull(skb, metasize);
 }

 return skb;
}

static int bnxt_discard_rx(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
      u32 *raw_cons, void *cmp)
{
 struct rx_cmp *rxcmp = cmp;
 u32 tmp_raw_cons = *raw_cons;
 u8 cmp_type, agg_bufs = 0;

 cmp_type = RX_CMP_TYPE(rxcmp);

 if (cmp_type == CMP_TYPE_RX_L2_CMP) {
  agg_bufs = (le32_to_cpu(rxcmp->rx_cmp_misc_v1) &
       RX_CMP_AGG_BUFS) >>
      RX_CMP_AGG_BUFS_SHIFT;
 } else if (cmp_type == CMP_TYPE_RX_L2_TPA_END_CMP) {
  struct rx_tpa_end_cmp *tpa_end = cmp;

  if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
   return 0;

  agg_bufs = TPA_END_AGG_BUFS(tpa_end);
 }

 if (agg_bufs) {
  if (!bnxt_agg_bufs_valid(bp, cpr, agg_bufs, &tmp_raw_cons))
   return -EBUSY;
 }
 *raw_cons = tmp_raw_cons;
 return 0;
}

static u16 bnxt_alloc_agg_idx(struct bnxt_rx_ring_info *rxr, u16 agg_id)
{
 struct bnxt_tpa_idx_map *map = rxr->rx_tpa_idx_map;
 u16 idx = agg_id & MAX_TPA_P5_MASK;

 if (test_bit(idx, map->agg_idx_bmap))
  idx = find_first_zero_bit(map->agg_idx_bmap,
       BNXT_AGG_IDX_BMAP_SIZE);
 __set_bit(idx, map->agg_idx_bmap);
 map->agg_id_tbl[agg_id] = idx;
 return idx;
}

static void bnxt_free_agg_idx(struct bnxt_rx_ring_info *rxr, u16 idx)
{
 struct bnxt_tpa_idx_map *map = rxr->rx_tpa_idx_map;

 __clear_bit(idx, map->agg_idx_bmap);
}

static u16 bnxt_lookup_agg_idx(struct bnxt_rx_ring_info *rxr, u16 agg_id)
{
 struct bnxt_tpa_idx_map *map = rxr->rx_tpa_idx_map;

 return map->agg_id_tbl[agg_id];
}

static void bnxt_tpa_metadata(struct bnxt_tpa_info *tpa_info,
         struct rx_tpa_start_cmp *tpa_start,
         struct rx_tpa_start_cmp_ext *tpa_start1)
{
 tpa_info->cfa_code_valid = 1;
 tpa_info->cfa_code = TPA_START_CFA_CODE(tpa_start1);
 tpa_info->vlan_valid = 0;
 if (tpa_info->flags2 & RX_CMP_FLAGS2_META_FORMAT_VLAN) {
  tpa_info->vlan_valid = 1;
  tpa_info->metadata =
   le32_to_cpu(tpa_start1->rx_tpa_start_cmp_metadata);
 }
}

static void bnxt_tpa_metadata_v2(struct bnxt_tpa_info *tpa_info,
     struct rx_tpa_start_cmp *tpa_start,
     struct rx_tpa_start_cmp_ext *tpa_start1)
{
 tpa_info->vlan_valid = 0;
 if (TPA_START_VLAN_VALID(tpa_start)) {
  u32 tpid_sel = TPA_START_VLAN_TPID_SEL(tpa_start);
  u32 vlan_proto = ETH_P_8021Q;

  tpa_info->vlan_valid = 1;
  if (tpid_sel == RX_TPA_START_METADATA1_TPID_8021AD)
   vlan_proto = ETH_P_8021AD;
  tpa_info->metadata = vlan_proto << 16 |
         TPA_START_METADATA0_TCI(tpa_start1);
 }
}

static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
      u8 cmp_type, struct rx_tpa_start_cmp *tpa_start,
      struct rx_tpa_start_cmp_ext *tpa_start1)
{
 struct bnxt_sw_rx_bd *cons_rx_buf, *prod_rx_buf;
 struct bnxt_tpa_info *tpa_info;
 u16 cons, prod, agg_id;
 struct rx_bd *prod_bd;
 dma_addr_t mapping;

 if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
  agg_id = TPA_START_AGG_ID_P5(tpa_start);
  agg_id = bnxt_alloc_agg_idx(rxr, agg_id);
 } else {
  agg_id = TPA_START_AGG_ID(tpa_start);
 }
 cons = tpa_start->rx_tpa_start_cmp_opaque;
 prod = rxr->rx_prod;
 cons_rx_buf = &rxr->rx_buf_ring[cons];
 prod_rx_buf = &rxr->rx_buf_ring[RING_RX(bp, prod)];
 tpa_info = &rxr->rx_tpa[agg_id];

 if (unlikely(cons != rxr->rx_next_cons ||
       TPA_START_ERROR(tpa_start))) {
  netdev_warn(bp->dev, "TPA cons %x, expected cons %x, error code %x\n",
       cons, rxr->rx_next_cons,
       TPA_START_ERROR_CODE(tpa_start1));
  bnxt_sched_reset_rxr(bp, rxr);
  return;
 }
 prod_rx_buf->data = tpa_info->data;
 prod_rx_buf->data_ptr = tpa_info->data_ptr;

 mapping = tpa_info->mapping;
 prod_rx_buf->mapping = mapping;

 prod_bd = &rxr->rx_desc_ring[RX_RING(bp, prod)][RX_IDX(prod)];

 prod_bd->rx_bd_haddr = cpu_to_le64(mapping);

 tpa_info->data = cons_rx_buf->data;
 tpa_info->data_ptr = cons_rx_buf->data_ptr;
 cons_rx_buf->data = NULL;
 tpa_info->mapping = cons_rx_buf->mapping;

 tpa_info->len =
  le32_to_cpu(tpa_start->rx_tpa_start_cmp_len_flags_type) >>
    RX_TPA_START_CMP_LEN_SHIFT;
 if (likely(TPA_START_HASH_VALID(tpa_start))) {
  tpa_info->hash_type = PKT_HASH_TYPE_L4;
  tpa_info->gso_type = SKB_GSO_TCPV4;
  if (TPA_START_IS_IPV6(tpa_start1))
   tpa_info->gso_type = SKB_GSO_TCPV6;
  /* RSS profiles 1 and 3 with extract code 0 for inner 4-tuple */
  else if (!BNXT_CHIP_P4_PLUS(bp) &&
    TPA_START_HASH_TYPE(tpa_start) == 3)
   tpa_info->gso_type = SKB_GSO_TCPV6;
  tpa_info->rss_hash =
   le32_to_cpu(tpa_start->rx_tpa_start_cmp_rss_hash);
 } else {
  tpa_info->hash_type = PKT_HASH_TYPE_NONE;
  tpa_info->gso_type = 0;
  netif_warn(bp, rx_err, bp->dev, "TPA packet without valid hash\n");
 }
 tpa_info->flags2 = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_flags2);
 tpa_info->hdr_info = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_hdr_info);
 if (cmp_type == CMP_TYPE_RX_L2_TPA_START_CMP)
  bnxt_tpa_metadata(tpa_info, tpa_start, tpa_start1);
 else
  bnxt_tpa_metadata_v2(tpa_info, tpa_start, tpa_start1);
 tpa_info->agg_count = 0;

 rxr->rx_prod = NEXT_RX(prod);
 cons = RING_RX(bp, NEXT_RX(cons));
 rxr->rx_next_cons = RING_RX(bp, NEXT_RX(cons));
 cons_rx_buf = &rxr->rx_buf_ring[cons];

 bnxt_reuse_rx_data(rxr, cons, cons_rx_buf->data);
 rxr->rx_prod = NEXT_RX(rxr->rx_prod);
 cons_rx_buf->data = NULL;
}

static void bnxt_abort_tpa(struct bnxt_cp_ring_info *cpr, u16 idx, u32 agg_bufs)
{
 if (agg_bufs)
  bnxt_reuse_rx_agg_bufs(cpr, idx, 0, agg_bufs, true);
}

#ifdef CONFIG_INET
static void bnxt_gro_tunnel(struct sk_buff *skb, __be16 ip_proto)
{
 struct udphdr *uh = NULL;

 if (ip_proto == htons(ETH_P_IP)) {
  struct iphdr *iph = (struct iphdr *)skb->data;

  if (iph->protocol == IPPROTO_UDP)
   uh = (struct udphdr *)(iph + 1);
 } else {
  struct ipv6hdr *iph = (struct ipv6hdr *)skb->data;

  if (iph->nexthdr == IPPROTO_UDP)
   uh = (struct udphdr *)(iph + 1);
 }
 if (uh) {
  if (uh->check)
   skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
  else
   skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
 }
}
#endif

static struct sk_buff *bnxt_gro_func_5731x(struct bnxt_tpa_info *tpa_info,
        int payload_off, int tcp_ts,
        struct sk_buff *skb)
{
#ifdef CONFIG_INET
 struct tcphdr *th;
 int len, nw_off;
 u16 outer_ip_off, inner_ip_off, inner_mac_off;
 u32 hdr_info = tpa_info->hdr_info;
 bool loopback = false;

 inner_ip_off = BNXT_TPA_INNER_L3_OFF(hdr_info);
 inner_mac_off = BNXT_TPA_INNER_L2_OFF(hdr_info);
 outer_ip_off = BNXT_TPA_OUTER_L3_OFF(hdr_info);

 /* If the packet is an internal loopback packet, the offsets will
 * have an extra 4 bytes.
 */

 if (inner_mac_off == 4) {
  loopback = true;
 } else if (inner_mac_off > 4) {
  __be16 proto = *((__be16 *)(skb->data + inner_ip_off -
         ETH_HLEN - 2));

  /* We only support inner iPv4/ipv6.  If we don't see the
 * correct protocol ID, it must be a loopback packet where
 * the offsets are off by 4.
 */

  if (proto != htons(ETH_P_IP) && proto != htons(ETH_P_IPV6))
   loopback = true;
 }
 if (loopback) {
  /* internal loopback packet, subtract all offsets by 4 */
  inner_ip_off -= 4;
  inner_mac_off -= 4;
  outer_ip_off -= 4;
 }

 nw_off = inner_ip_off - ETH_HLEN;
 skb_set_network_header(skb, nw_off);
 if (tpa_info->flags2 & RX_TPA_START_CMP_FLAGS2_IP_TYPE) {
  struct ipv6hdr *iph = ipv6_hdr(skb);

  skb_set_transport_header(skb, nw_off + sizeof(struct ipv6hdr));
  len = skb->len - skb_transport_offset(skb);
  th = tcp_hdr(skb);
  th->check = ~tcp_v6_check(len, &iph->saddr, &iph->daddr, 0);
 } else {
  struct iphdr *iph = ip_hdr(skb);

  skb_set_transport_header(skb, nw_off + sizeof(struct iphdr));
  len = skb->len - skb_transport_offset(skb);
  th = tcp_hdr(skb);
  th->check = ~tcp_v4_check(len, iph->saddr, iph->daddr, 0);
 }

 if (inner_mac_off) { /* tunnel */
  __be16 proto = *((__be16 *)(skb->data + outer_ip_off -
         ETH_HLEN - 2));

  bnxt_gro_tunnel(skb, proto);
 }
#endif
 return skb;
}

static struct sk_buff *bnxt_gro_func_5750x(struct bnxt_tpa_info *tpa_info,
        int payload_off, int tcp_ts,
        struct sk_buff *skb)
{
#ifdef CONFIG_INET
 u16 outer_ip_off, inner_ip_off, inner_mac_off;
 u32 hdr_info = tpa_info->hdr_info;
 int iphdr_len, nw_off;

 inner_ip_off = BNXT_TPA_INNER_L3_OFF(hdr_info);
 inner_mac_off = BNXT_TPA_INNER_L2_OFF(hdr_info);
 outer_ip_off = BNXT_TPA_OUTER_L3_OFF(hdr_info);

 nw_off = inner_ip_off - ETH_HLEN;
 skb_set_network_header(skb, nw_off);
 iphdr_len = (tpa_info->flags2 & RX_TPA_START_CMP_FLAGS2_IP_TYPE) ?
       sizeof(struct ipv6hdr) : sizeof(struct iphdr);
 skb_set_transport_header(skb, nw_off + iphdr_len);

 if (inner_mac_off) { /* tunnel */
  __be16 proto = *((__be16 *)(skb->data + outer_ip_off -
         ETH_HLEN - 2));

  bnxt_gro_tunnel(skb, proto);
 }
#endif
 return skb;
}

#define BNXT_IPV4_HDR_SIZE (sizeof(struct iphdr) + sizeof(struct tcphdr))
#define BNXT_IPV6_HDR_SIZE (sizeof(struct ipv6hdr) + sizeof(struct tcphdr))

static struct sk_buff *bnxt_gro_func_5730x(struct bnxt_tpa_info *tpa_info,
        int payload_off, int tcp_ts,
        struct sk_buff *skb)
{
#ifdef CONFIG_INET
 struct tcphdr *th;
 int len, nw_off, tcp_opt_len = 0;

 if (tcp_ts)
  tcp_opt_len = 12;

 if (tpa_info->gso_type == SKB_GSO_TCPV4) {
  struct iphdr *iph;

  nw_off = payload_off - BNXT_IPV4_HDR_SIZE - tcp_opt_len -
    ETH_HLEN;
  skb_set_network_header(skb, nw_off);
  iph = ip_hdr(skb);
  skb_set_transport_header(skb, nw_off + sizeof(struct iphdr));
  len = skb->len - skb_transport_offset(skb);
  th = tcp_hdr(skb);
  th->check = ~tcp_v4_check(len, iph->saddr, iph->daddr, 0);
 } else if (tpa_info->gso_type == SKB_GSO_TCPV6) {
  struct ipv6hdr *iph;

  nw_off = payload_off - BNXT_IPV6_HDR_SIZE - tcp_opt_len -
    ETH_HLEN;
  skb_set_network_header(skb, nw_off);
  iph = ipv6_hdr(skb);
  skb_set_transport_header(skb, nw_off + sizeof(struct ipv6hdr));
  len = skb->len - skb_transport_offset(skb);
  th = tcp_hdr(skb);
  th->check = ~tcp_v6_check(len, &iph->saddr, &iph->daddr, 0);
 } else {
  dev_kfree_skb_any(skb);
  return NULL;
 }

 if (nw_off) /* tunnel */
  bnxt_gro_tunnel(skb, skb->protocol);
#endif
 return skb;
}

static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp,
        struct bnxt_tpa_info *tpa_info,
        struct rx_tpa_end_cmp *tpa_end,
        struct rx_tpa_end_cmp_ext *tpa_end1,
        struct sk_buff *skb)
{
#ifdef CONFIG_INET
 int payload_off;
 u16 segs;

 segs = TPA_END_TPA_SEGS(tpa_end);
 if (segs == 1)
  return skb;

 NAPI_GRO_CB(skb)->count = segs;
 skb_shinfo(skb)->gso_size =
  le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len);
 skb_shinfo(skb)->gso_type = tpa_info->gso_type;
 if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS)
  payload_off = TPA_END_PAYLOAD_OFF_P5(tpa_end1);
 else
  payload_off = TPA_END_PAYLOAD_OFF(tpa_end);
 skb = bp->gro_func(tpa_info, payload_off, TPA_END_GRO_TS(tpa_end), skb);
 if (likely(skb))
  tcp_gro_complete(skb);
#endif
 return skb;
}

/* Given the cfa_code of a received packet determine which
 * netdev (vf-rep or PF) the packet is destined to.
 */

static struct net_device *bnxt_get_pkt_dev(struct bnxt *bp, u16 cfa_code)
{
 struct net_device *dev = bnxt_get_vf_rep(bp, cfa_code);

 /* if vf-rep dev is NULL, it must belong to the PF */
 return dev ? dev : bp->dev;
}

static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp,
        struct bnxt_cp_ring_info *cpr,
        u32 *raw_cons,
        struct rx_tpa_end_cmp *tpa_end,
        struct rx_tpa_end_cmp_ext *tpa_end1,
        u8 *event)
{
 struct bnxt_napi *bnapi = cpr->bnapi;
 struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
 struct net_device *dev = bp->dev;
 u8 *data_ptr, agg_bufs;
 unsigned int len;
 struct bnxt_tpa_info *tpa_info;
 dma_addr_t mapping;
 struct sk_buff *skb;
 u16 idx = 0, agg_id;
 void *data;
 bool gro;

 if (unlikely(bnapi->in_reset)) {
  int rc = bnxt_discard_rx(bp, cpr, raw_cons, tpa_end);

  if (rc < 0)
   return ERR_PTR(-EBUSY);
  return NULL;
 }

 if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
  agg_id = TPA_END_AGG_ID_P5(tpa_end);
  agg_id = bnxt_lookup_agg_idx(rxr, agg_id);
  agg_bufs = TPA_END_AGG_BUFS_P5(tpa_end1);
  tpa_info = &rxr->rx_tpa[agg_id];
  if (unlikely(agg_bufs != tpa_info->agg_count)) {
   netdev_warn(bp->dev, "TPA end agg_buf %d != expected agg_bufs %d\n",
        agg_bufs, tpa_info->agg_count);
   agg_bufs = tpa_info->agg_count;
  }
  tpa_info->agg_count = 0;
  *event |= BNXT_AGG_EVENT;
  bnxt_free_agg_idx(rxr, agg_id);
  idx = agg_id;
  gro = !!(bp->flags & BNXT_FLAG_GRO);
 } else {
  agg_id = TPA_END_AGG_ID(tpa_end);
  agg_bufs = TPA_END_AGG_BUFS(tpa_end);
  tpa_info = &rxr->rx_tpa[agg_id];
  idx = RING_CMP(*raw_cons);
  if (agg_bufs) {
   if (!bnxt_agg_bufs_valid(bp, cpr, agg_bufs, raw_cons))
    return ERR_PTR(-EBUSY);

   *event |= BNXT_AGG_EVENT;
   idx = NEXT_CMP(idx);
  }
  gro = !!TPA_END_GRO(tpa_end);
 }
 data = tpa_info->data;
 data_ptr = tpa_info->data_ptr;
 prefetch(data_ptr);
 len = tpa_info->len;
 mapping = tpa_info->mapping;

 if (unlikely(agg_bufs > MAX_SKB_FRAGS || TPA_END_ERRORS(tpa_end1))) {
  bnxt_abort_tpa(cpr, idx, agg_bufs);
  if (agg_bufs > MAX_SKB_FRAGS)
   netdev_warn(bp->dev, "TPA frags %d exceeded MAX_SKB_FRAGS %d\n",
        agg_bufs, (int)MAX_SKB_FRAGS);
  return NULL;
 }

 if (len <= bp->rx_copybreak) {
  skb = bnxt_copy_skb(bnapi, data_ptr, len, mapping);
  if (!skb) {
   bnxt_abort_tpa(cpr, idx, agg_bufs);
   cpr->sw_stats->rx.rx_oom_discards += 1;
   return NULL;
  }
 } else {
  u8 *new_data;
  dma_addr_t new_mapping;

  new_data = __bnxt_alloc_rx_frag(bp, &new_mapping, rxr,
      GFP_ATOMIC);
  if (!new_data) {
   bnxt_abort_tpa(cpr, idx, agg_bufs);
   cpr->sw_stats->rx.rx_oom_discards += 1;
   return NULL;
  }

  tpa_info->data = new_data;
  tpa_info->data_ptr = new_data + bp->rx_offset;
  tpa_info->mapping = new_mapping;

  skb = napi_build_skb(data, bp->rx_buf_size);
  dma_sync_single_for_cpu(&bp->pdev->dev, mapping,
     bp->rx_buf_use_size, bp->rx_dir);

  if (!skb) {
   page_pool_free_va(rxr->head_pool, data, true);
   bnxt_abort_tpa(cpr, idx, agg_bufs);
   cpr->sw_stats->rx.rx_oom_discards += 1;
   return NULL;
  }
  skb_mark_for_recycle(skb);
  skb_reserve(skb, bp->rx_offset);
  skb_put(skb, len);
 }

 if (agg_bufs) {
  skb = bnxt_rx_agg_netmems_skb(bp, cpr, skb, idx, agg_bufs,
           true);
  if (!skb) {
   /* Page reuse already handled by bnxt_rx_pages(). */
   cpr->sw_stats->rx.rx_oom_discards += 1;
   return NULL;
  }
 }

 if (tpa_info->cfa_code_valid)
  dev = bnxt_get_pkt_dev(bp, tpa_info->cfa_code);
 skb->protocol = eth_type_trans(skb, dev);

 if (tpa_info->hash_type != PKT_HASH_TYPE_NONE)
  skb_set_hash(skb, tpa_info->rss_hash, tpa_info->hash_type);

 if (tpa_info->vlan_valid &&
     (dev->features & BNXT_HW_FEATURE_VLAN_ALL_RX)) {
  __be16 vlan_proto = htons(tpa_info->metadata >>
       RX_CMP_FLAGS2_METADATA_TPID_SFT);
  u16 vtag = tpa_info->metadata & RX_CMP_FLAGS2_METADATA_TCI_MASK;

  if (eth_type_vlan(vlan_proto)) {
   __vlan_hwaccel_put_tag(skb, vlan_proto, vtag);
  } else {
   dev_kfree_skb(skb);
   return NULL;
  }
 }

 skb_checksum_none_assert(skb);
 if (likely(tpa_info->flags2 & RX_TPA_START_CMP_FLAGS2_L4_CS_CALC)) {
  skb->ip_summed = CHECKSUM_UNNECESSARY;
  skb->csum_level =
   (tpa_info->flags2 & RX_CMP_FLAGS2_T_L4_CS_CALC) >> 3;
 }

 if (gro)
  skb = bnxt_gro_skb(bp, tpa_info, tpa_end, tpa_end1, skb);

 return skb;
}

static void bnxt_tpa_agg(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
    struct rx_agg_cmp *rx_agg)
{
 u16 agg_id = TPA_AGG_AGG_ID(rx_agg);
 struct bnxt_tpa_info *tpa_info;

 agg_id = bnxt_lookup_agg_idx(rxr, agg_id);
 tpa_info = &rxr->rx_tpa[agg_id];
 BUG_ON(tpa_info->agg_count >= MAX_SKB_FRAGS);
 tpa_info->agg_arr[tpa_info->agg_count++] = *rx_agg;
}

static void bnxt_deliver_skb(struct bnxt *bp, struct bnxt_napi *bnapi,
        struct sk_buff *skb)
{
 skb_mark_for_recycle(skb);

 if (skb->dev != bp->dev) {
  /* this packet belongs to a vf-rep */
  bnxt_vf_rep_rx(bp, skb);
  return;
 }
 skb_record_rx_queue(skb, bnapi->index);
 napi_gro_receive(&bnapi->napi, skb);
}

static bool bnxt_rx_ts_valid(struct bnxt *bp, u32 flags,
        struct rx_cmp_ext *rxcmp1, u32 *cmpl_ts)
{
 u32 ts = le32_to_cpu(rxcmp1->rx_cmp_timestamp);

 if (BNXT_PTP_RX_TS_VALID(flags))
  goto ts_valid;
 if (!bp->ptp_all_rx_tstamp || !ts || !BNXT_ALL_RX_TS_VALID(flags))
  return false;

ts_valid:
 *cmpl_ts = ts;
 return true;
}

static struct sk_buff *bnxt_rx_vlan(struct sk_buff *skb, u8 cmp_type,
        struct rx_cmp *rxcmp,
        struct rx_cmp_ext *rxcmp1)
{
 __be16 vlan_proto;
 u16 vtag;

 if (cmp_type == CMP_TYPE_RX_L2_CMP) {
  __le32 flags2 = rxcmp1->rx_cmp_flags2;
  u32 meta_data;

  if (!(flags2 & cpu_to_le32(RX_CMP_FLAGS2_META_FORMAT_VLAN)))
   return skb;

  meta_data = le32_to_cpu(rxcmp1->rx_cmp_meta_data);
  vtag = meta_data & RX_CMP_FLAGS2_METADATA_TCI_MASK;
  vlan_proto = htons(meta_data >> RX_CMP_FLAGS2_METADATA_TPID_SFT);
  if (eth_type_vlan(vlan_proto))
   __vlan_hwaccel_put_tag(skb, vlan_proto, vtag);
  else
   goto vlan_err;
 } else if (cmp_type == CMP_TYPE_RX_L2_V3_CMP) {
  if (RX_CMP_VLAN_VALID(rxcmp)) {
   u32 tpid_sel = RX_CMP_VLAN_TPID_SEL(rxcmp);

   if (tpid_sel == RX_CMP_METADATA1_TPID_8021Q)
    vlan_proto = htons(ETH_P_8021Q);
   else if (tpid_sel == RX_CMP_METADATA1_TPID_8021AD)
    vlan_proto = htons(ETH_P_8021AD);
   else
    goto vlan_err;
   vtag = RX_CMP_METADATA0_TCI(rxcmp1);
   __vlan_hwaccel_put_tag(skb, vlan_proto, vtag);
  }
 }
 return skb;
vlan_err:
 skb_mark_for_recycle(skb);
 dev_kfree_skb(skb);
 return NULL;
}

static enum pkt_hash_types bnxt_rss_ext_op(struct bnxt *bp,
        struct rx_cmp *rxcmp)
{
 u8 ext_op;

 ext_op = RX_CMP_V3_HASH_TYPE(bp, rxcmp);
 switch (ext_op) {
 case EXT_OP_INNER_4:
 case EXT_OP_OUTER_4:
 case EXT_OP_INNFL_3:
 case EXT_OP_OUTFL_3:
  return PKT_HASH_TYPE_L4;
 default:
  return PKT_HASH_TYPE_L3;
 }
}

/* returns the following:
 * 1       - 1 packet successfully received
 * 0       - successful TPA_START, packet not completed yet
 * -EBUSY  - completion ring does not have all the agg buffers yet
 * -ENOMEM - packet aborted due to out of memory
 * -EIO    - packet aborted due to hw error indicated in BD
 */

static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
         u32 *raw_cons, u8 *event)
{
 struct bnxt_napi *bnapi = cpr->bnapi;
 struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
 struct net_device *dev = bp->dev;
 struct rx_cmp *rxcmp;
 struct rx_cmp_ext *rxcmp1;
 u32 tmp_raw_cons = *raw_cons;
 u16 cons, prod, cp_cons = RING_CMP(tmp_raw_cons);
 struct skb_shared_info *sinfo;
 struct bnxt_sw_rx_bd *rx_buf;
 unsigned int len;
 u8 *data_ptr, agg_bufs, cmp_type;
 bool xdp_active = false;
 dma_addr_t dma_addr;
 struct sk_buff *skb;
 struct xdp_buff xdp;
 u32 flags, misc;
 u32 cmpl_ts;
 void *data;
 int rc = 0;

 rxcmp = (struct rx_cmp *)
   &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];

 cmp_type = RX_CMP_TYPE(rxcmp);

 if (cmp_type == CMP_TYPE_RX_TPA_AGG_CMP) {
  bnxt_tpa_agg(bp, rxr, (struct rx_agg_cmp *)rxcmp);
  goto next_rx_no_prod_no_len;
 }

 tmp_raw_cons = NEXT_RAW_CMP(tmp_raw_cons);
 cp_cons = RING_CMP(tmp_raw_cons);
 rxcmp1 = (struct rx_cmp_ext *)
   &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)];

 if (!RX_CMP_VALID(rxcmp1, tmp_raw_cons))
  return -EBUSY;

 /* The valid test of the entry must be done first before
 * reading any further.
 */

 dma_rmb();
 prod = rxr->rx_prod;

 if (cmp_type == CMP_TYPE_RX_L2_TPA_START_CMP ||
     cmp_type == CMP_TYPE_RX_L2_TPA_START_V3_CMP) {
  bnxt_tpa_start(bp, rxr, cmp_type,
          (struct rx_tpa_start_cmp *)rxcmp,
          (struct rx_tpa_start_cmp_ext *)rxcmp1);

  *event |= BNXT_RX_EVENT;
  goto next_rx_no_prod_no_len;

 } else if (cmp_type == CMP_TYPE_RX_L2_TPA_END_CMP) {
  skb = bnxt_tpa_end(bp, cpr, &tmp_raw_cons,
       (struct rx_tpa_end_cmp *)rxcmp,
       (struct rx_tpa_end_cmp_ext *)rxcmp1, event);

  if (IS_ERR(skb))
   return -EBUSY;

  rc = -ENOMEM;
  if (likely(skb)) {
   bnxt_deliver_skb(bp, bnapi, skb);
   rc = 1;
  }
  *event |= BNXT_RX_EVENT;
  goto next_rx_no_prod_no_len;
 }

 cons = rxcmp->rx_cmp_opaque;
 if (unlikely(cons != rxr->rx_next_cons)) {
  int rc1 = bnxt_discard_rx(bp, cpr, &tmp_raw_cons, rxcmp);

  /* 0xffff is forced error, don't print it */
  if (rxr->rx_next_cons != 0xffff)
   netdev_warn(bp->dev, "RX cons %x != expected cons %x\n",
        cons, rxr->rx_next_cons);
  bnxt_sched_reset_rxr(bp, rxr);
  if (rc1)
   return rc1;
  goto next_rx_no_prod_no_len;
 }
 rx_buf = &rxr->rx_buf_ring[cons];
 data = rx_buf->data;
 data_ptr = rx_buf->data_ptr;
 prefetch(data_ptr);

 misc = le32_to_cpu(rxcmp->rx_cmp_misc_v1);
 agg_bufs = (misc & RX_CMP_AGG_BUFS) >> RX_CMP_AGG_BUFS_SHIFT;

 if (agg_bufs) {
  if (!bnxt_agg_bufs_valid(bp, cpr, agg_bufs, &tmp_raw_cons))
   return -EBUSY;

  cp_cons = NEXT_CMP(cp_cons);
  *event |= BNXT_AGG_EVENT;
 }
 *event |= BNXT_RX_EVENT;

 rx_buf->data = NULL;
 if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L2_ERRORS) {
  u32 rx_err = le32_to_cpu(rxcmp1->rx_cmp_cfa_code_errors_v2);

  bnxt_reuse_rx_data(rxr, cons, data);
  if (agg_bufs)
   bnxt_reuse_rx_agg_bufs(cpr, cp_cons, 0, agg_bufs,
            false);

  rc = -EIO;
  if (rx_err & RX_CMPL_ERRORS_BUFFER_ERROR_MASK) {
   bnapi->cp_ring.sw_stats->rx.rx_buf_errors++;
   if (!(bp->flags & BNXT_FLAG_CHIP_P5_PLUS) &&
       !(bp->fw_cap & BNXT_FW_CAP_RING_MONITOR)) {
    netdev_warn_once(bp->dev, "RX buffer error %x\n",
       rx_err);
    bnxt_sched_reset_rxr(bp, rxr);
   }
  }
  goto next_rx_no_len;
 }

 flags = le32_to_cpu(rxcmp->rx_cmp_len_flags_type);
 len = flags >> RX_CMP_LEN_SHIFT;
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© 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.