Page MenuHomeFreeBSD

D10427.id27815.diff
No OneTemporary

D10427.id27815.diff

Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1579,6 +1579,12 @@
dev/e1000/e1000_osdep.c optional em \
compile-with "${NORMAL_C} -I$S/dev/e1000"
dev/et/if_et.c optional et
+dev/ena/ena.c optional ena \
+ compile-with "${NORMAL_C} -I$S/contrib"
+dev/ena/ena_sysctl.c optional ena \
+ compile-with "${NORMAL_C} -I$S/contrib"
+contrib/ena-com/ena_com.c optional ena
+contrib/ena-com/ena_eth_com.c optional ena
dev/ep/if_ep.c optional ep
dev/ep/if_ep_isa.c optional ep isa
dev/ep/if_ep_pccard.c optional ep pccard
Index: sys/dev/ena/ena.h
===================================================================
--- /dev/null
+++ sys/dev/ena/ena.h
@@ -0,0 +1,434 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) 2015-2017 Amazon.com, Inc. or its affiliates.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ENA_H
+#define ENA_H
+
+#include <sys/types.h>
+
+#include "ena-com/ena_com.h"
+#include "ena-com/ena_eth_com.h"
+
+#define DRV_MODULE_VER_MAJOR 0
+#define DRV_MODULE_VER_MINOR 7
+#define DRV_MODULE_VER_SUBMINOR 0
+
+#define DRV_MODULE_NAME "ena"
+
+#ifndef DRV_MODULE_VERSION
+#define DRV_MODULE_VERSION \
+ __XSTRING(DRV_MODULE_VER_MAJOR) "." \
+ __XSTRING(DRV_MODULE_VER_MINOR) "." \
+ __XSTRING(DRV_MODULE_VER_SUBMINOR)
+#endif
+#define DEVICE_NAME "Elastic Network Adapter (ENA)"
+#define DEVICE_DESC "ENA adapter"
+
+/* Calculate DMA mask - width for ena cannot exceed 48, so it is safe */
+#define ENA_DMA_BIT_MASK(x) ((1ULL << (x)) - 1ULL)
+
+/* 1 for AENQ + ADMIN */
+#define ENA_MAX_MSIX_VEC(io_queues) (1 + (io_queues))
+
+#define ENA_REG_BAR 0
+#define ENA_MEM_BAR 2
+
+#define ENA_BUS_DMA_SEGS 32
+
+#define ENA_DEFAULT_RING_SIZE 1024
+#define ENA_DEFAULT_SMALL_PACKET_LEN 128
+#define ENA_DEFAULT_MAX_RX_BUFF_ALLOC_SIZE 1536
+
+#define ENA_RX_REFILL_THRESH_DEVIDER 8
+
+#define ENA_MAX_PUSH_PKT_SIZE 128
+
+#define ENA_NAME_MAX_LEN 20
+#define ENA_IRQNAME_SIZE 40
+
+#define ENA_PKT_MAX_BUFS 19
+#define ENA_STALL_TIMEOUT 100
+
+#define ENA_RX_RSS_TABLE_LOG_SIZE 7
+#define ENA_RX_RSS_TABLE_SIZE (1 << ENA_RX_RSS_TABLE_LOG_SIZE)
+
+#define ENA_HASH_KEY_SIZE 40
+
+#define ENA_DMA_BITS_MASK 40
+#define ENA_MAX_FRAME_LEN 10000
+#define ENA_MIN_FRAME_LEN 60
+#define ENA_RX_HASH_KEY_NUM 10
+#define ENA_RX_THASH_TABLE_SIZE (1 << 8)
+
+#define ENA_TX_CLEANUP_TRESHOLD 128
+
+#define DB_THRESHOLD 64
+
+#define TX_COMMIT 32
+ /*
+ * TX budget for cleaning. It should be half of the RX budget to reduce amount
+ * of TCP retransmissions.
+ */
+#define TX_BUDGET 128
+/* RX cleanup budget. -1 stands for infinity. */
+#define RX_BUDGET 256
+/*
+ * How many times we can repeat cleanup in the io irq handling routine if the
+ * RX or TX budget was depleted.
+ */
+#define CLEAN_BUDGET 8
+
+#define RX_IRQ_INTERVAL 20
+#define TX_IRQ_INTERVAL 50
+
+#define ENA_MAX_MTU 9216
+#define ENA_TSO_MAXSIZE PAGE_SIZE
+#define ENA_TSO_NSEGS ENA_PKT_MAX_BUFS
+#define ENA_RX_OFFSET NET_SKB_PAD + NET_IP_ALIGN
+
+#define ENA_MMIO_DISABLE_REG_READ BIT(0)
+
+#define ENA_TX_RING_IDX_NEXT(idx, ring_size) (((idx) + 1) & ((ring_size) - 1))
+
+#define ENA_RX_RING_IDX_NEXT(idx, ring_size) (((idx) + 1) & ((ring_size) - 1))
+#define ENA_RX_RING_IDX_ADD(idx, n, ring_size) \
+ (((idx) + (n)) & ((ring_size) - 1))
+
+#define ENA_IO_TXQ_IDX(q) (2 * (q))
+#define ENA_IO_RXQ_IDX(q) (2 * (q) + 1)
+
+#define ENA_MGMNT_IRQ_IDX 0
+#define ENA_IO_IRQ_FIRST_IDX 1
+#define ENA_IO_IRQ_IDX(q) (ENA_IO_IRQ_FIRST_IDX + (q))
+
+/*
+ * ENA device should send keep alive msg every 1 sec.
+ * We wait for 6 sec just to be on the safe side.
+ */
+#define DEFAULT_KEEP_ALIVE_TO (SBT_1S * 6)
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define DEFAULT_TX_CMP_TO (SBT_1S * 5)
+
+/* Number of queues to check for missing queues per timer tick */
+#define DEFAULT_TX_MONITORED_QUEUES (4)
+
+/* Max number of timeouted packets before device reset */
+#define DEFAULT_TX_CMP_THRESHOLD (128)
+
+/*
+ * Supported PCI vendor and devices IDs
+ */
+#define PCI_VENDOR_ID_AMAZON 0x1d0f
+
+#define PCI_DEV_ID_ENA_PF 0x0ec2
+#define PCI_DEV_ID_ENA_LLQ_PF 0x1ec2
+#define PCI_DEV_ID_ENA_VF 0xec20
+#define PCI_DEV_ID_ENA_LLQ_VF 0xec21
+
+struct msix_entry {
+ int entry;
+ int vector;
+};
+
+typedef struct _ena_vendor_info_t {
+ unsigned int vendor_id;
+ unsigned int device_id;
+ unsigned int index;
+} ena_vendor_info_t;
+
+struct ena_irq {
+ /* Interrupt resources */
+ struct resource *res;
+ driver_intr_t *handler;
+ void *data;
+ void *cookie;
+ unsigned int vector;
+ bool requested;
+ int cpu;
+ char name[ENA_IRQNAME_SIZE];
+};
+
+struct ena_que {
+ struct ena_adapter *adapter;
+ struct ena_ring *tx_ring;
+ struct ena_ring *rx_ring;
+ uint32_t id;
+ int cpu;
+};
+
+struct ena_tx_buffer {
+ struct mbuf *mbuf;
+ /* # of ena desc for this specific mbuf
+ * (includes data desc and metadata desc) */
+ unsigned int tx_descs;
+ /* # of buffers used by this mbuf */
+ unsigned int num_of_bufs;
+ bus_dmamap_t map;
+
+ /* Used to detect missing tx packets */
+ struct bintime timestamp;
+ bool print_once;
+
+ struct ena_com_buf bufs[ENA_PKT_MAX_BUFS];
+} __aligned(CACHE_LINE_SIZE);
+
+struct ena_rx_buffer {
+ struct mbuf *mbuf;
+ bus_dmamap_t map;
+ struct ena_com_buf ena_buf;
+} __aligned(CACHE_LINE_SIZE);
+
+
+struct ena_stats_tx {
+ counter_u64_t cnt;
+ counter_u64_t bytes;
+ counter_u64_t queue_stop;
+ counter_u64_t prepare_ctx_err;
+ counter_u64_t queue_wakeup;
+ counter_u64_t dma_mapping_err;
+ /* Not counted */
+ counter_u64_t unsupported_desc_num;
+ /* Not counted */
+ counter_u64_t napi_comp;
+ /* Not counted */
+ counter_u64_t tx_poll;
+ counter_u64_t doorbells;
+ counter_u64_t missing_tx_comp;
+ counter_u64_t bad_req_id;
+};
+
+struct ena_stats_rx {
+ counter_u64_t cnt;
+ counter_u64_t bytes;
+ counter_u64_t refil_partial;
+ counter_u64_t bad_csum;
+ /* Not counted */
+ counter_u64_t page_alloc_fail;
+ counter_u64_t mbuf_alloc_fail;
+ counter_u64_t dma_mapping_err;
+ counter_u64_t bad_desc_num;
+ /* Not counted */
+ counter_u64_t small_copy_len_pkt;
+};
+
+
+struct ena_ring {
+ /* Holds the empty requests for TX out of order completions */
+ uint16_t *free_tx_ids;
+ struct ena_com_dev *ena_dev;
+ struct ena_adapter *adapter;
+ struct ena_com_io_cq *ena_com_io_cq;
+ struct ena_com_io_sq *ena_com_io_sq;
+
+ /* The maximum length the driver can push to the device (For LLQ) */
+ enum ena_admin_placement_policy_type tx_mem_queue_type;
+ uint16_t rx_small_copy_len;
+ uint16_t qid;
+ uint16_t mtu;
+ uint8_t tx_max_header_size;
+
+ struct ena_com_rx_buf_info ena_bufs[ENA_PKT_MAX_BUFS];
+ uint32_t smoothed_interval;
+ enum ena_intr_moder_level moder_tbl_idx;
+
+ struct ena_que *que;
+ struct lro_ctrl lro;
+
+ uint16_t next_to_use;
+ uint16_t next_to_clean;
+
+ union {
+ struct ena_tx_buffer *tx_buffer_info; /* contex of tx packet */
+ struct ena_rx_buffer *rx_buffer_info; /* contex of rx packet */
+ };
+ int ring_size; /* number of tx/rx_buffer_info's entries */
+
+ struct buf_ring *br; /* only for TX */
+ struct mtx ring_mtx;
+ char mtx_name[16];
+ struct task enqueue_task;
+ struct taskqueue *enqueue_tq;
+ struct task cmpl_task;
+ struct taskqueue *cmpl_tq;
+
+ union {
+ struct ena_stats_tx tx_stats;
+ struct ena_stats_rx rx_stats;
+ };
+
+} __aligned(CACHE_LINE_SIZE);
+
+struct ena_stats_dev {
+ /* Not counted */
+ counter_u64_t tx_timeout;
+ /* Not counted */
+ counter_u64_t io_suspend;
+ /* Not counted */
+ counter_u64_t io_resume;
+ /* Not counted */
+ counter_u64_t wd_expired;
+ counter_u64_t interface_up;
+ counter_u64_t interface_down;
+ /* Not counted */
+ counter_u64_t admin_q_pause;
+};
+
+struct ena_hw_stats {
+ uint64_t rx_packets;
+ uint64_t tx_packets;
+
+ uint64_t rx_bytes;
+ uint64_t tx_bytes;
+
+ uint64_t rx_drops;
+};
+
+/* Board specific private data structure */
+struct ena_adapter {
+ struct ena_com_dev *ena_dev;
+
+ /* OS defined structs */
+ if_t ifp;
+ device_t pdev;
+ struct ifmedia media;
+
+ /* OS resources */
+ struct resource * memory;
+ struct resource * registers;
+
+ struct mtx global_mtx;
+ struct sx ioctl_sx;
+
+ /* MSI-X */
+ uint32_t msix_enabled;
+ struct msix_entry *msix_entries;
+ int msix_vecs;
+
+ /* DMA tags used throughout the driver adapter for Tx and Rx */
+ bus_dma_tag_t tx_buf_tag;
+ bus_dma_tag_t rx_buf_tag;
+ int dma_width;
+ /*
+ * RX packets that shorter that this len will be copied to the skb
+ * header
+ */
+ unsigned int small_copy_len;
+
+ uint16_t max_tx_sgl_size;
+ uint16_t max_rx_sgl_size;
+
+ uint32_t tx_offload_cap;
+
+ /* Tx fast path data */
+ int num_queues;
+
+ unsigned int tx_usecs, rx_usecs; /* Interrupt coalescing */
+
+ unsigned int tx_ring_size;
+ unsigned int rx_ring_size;
+
+ /* RSS*/
+ uint8_t rss_ind_tbl[ENA_RX_RSS_TABLE_SIZE];
+ bool rss_support;
+
+ uint32_t msg_enable;
+
+ uint8_t mac_addr[ETHER_ADDR_LEN];
+ /* mdio and phy*/
+
+ char name[ENA_NAME_MAX_LEN];
+ bool link_status;
+ bool trigger_reset;
+ bool up;
+ bool running;
+
+ uint32_t wol;
+
+ /* Queue will represent one TX and one RX ring */
+ struct ena_que que[ENA_MAX_NUM_IO_QUEUES]
+ __aligned(CACHE_LINE_SIZE);
+
+ /* TX */
+ struct ena_ring tx_ring[ENA_MAX_NUM_IO_QUEUES]
+ __aligned(CACHE_LINE_SIZE);
+
+ /* RX */
+ struct ena_ring rx_ring[ENA_MAX_NUM_IO_QUEUES]
+ __aligned(CACHE_LINE_SIZE);
+
+ struct ena_irq irq_tbl[ENA_MAX_MSIX_VEC(ENA_MAX_NUM_IO_QUEUES)];
+
+ /* Timer service */
+ struct callout timer_service;
+ sbintime_t keep_alive_timestamp;
+ uint32_t next_monitored_tx_qid;
+ struct task reset_task;
+ struct taskqueue *reset_tq;
+ int wd_active;
+ sbintime_t keep_alive_timeout;
+ sbintime_t missing_tx_timeout;
+ uint32_t missing_tx_max_queues;
+ uint32_t missing_tx_threshold;
+
+ /* Statistics */
+ struct ena_stats_dev dev_stats;
+ struct ena_hw_stats hw_stats;
+};
+
+
+#define ENA_DEV_LOCK mtx_lock(&adapter->global_mtx)
+#define ENA_DEV_UNLOCK mtx_unlock(&adapter->global_mtx)
+
+#define ENA_RING_MTX_LOCK(_ring) mtx_lock(&(_ring)->ring_mtx)
+#define ENA_RING_MTX_TRYLOCK(_ring) mtx_trylock(&(_ring)->ring_mtx)
+#define ENA_RING_MTX_UNLOCK(_ring) mtx_unlock(&(_ring)->ring_mtx)
+
+struct ena_dev *ena_efa_enadev_get(device_t pdev);
+
+int ena_register_adapter(struct ena_adapter *adapter);
+void ena_unregister_adapter(struct ena_adapter *adapter);
+
+int ena_update_stats_counters(struct ena_adapter *adapter);
+
+static inline int ena_mbuf_count(struct mbuf *mbuf)
+{
+ int count = 1;
+
+ while ((mbuf = mbuf->m_next) != NULL)
+ ++count;
+
+ return count;
+}
+
+#endif /* !(ENA_H) */
Index: sys/dev/ena/ena.c
===================================================================
--- /dev/null
+++ sys/dev/ena/ena.c
@@ -0,0 +1,3761 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) 2015-2017 Amazon.com, Inc. or its affiliates.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/smp.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/time.h>
+#include <sys/eventhandler.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/in_cksum.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/rss_config.h>
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <netinet/in_rss.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include "ena.h"
+#include "ena_sysctl.h"
+
+/*********************************************************
+ * Function prototypes
+ *********************************************************/
+static int ena_probe(device_t);
+static void ena_intr_msix_mgmnt(void *);
+static int ena_allocate_pci_resources(struct ena_adapter*);
+static void ena_free_pci_resources(struct ena_adapter *);
+static int ena_change_mtu(if_t, int);
+static inline void ena_alloc_counters(counter_u64_t *, int);
+static inline void ena_free_counters(counter_u64_t *, int);
+static inline void ena_reset_counters(counter_u64_t *, int);
+static void ena_init_io_rings_common(struct ena_adapter *,
+ struct ena_ring *, uint16_t);
+static int ena_init_io_rings(struct ena_adapter *);
+static void ena_free_io_rings_resources(struct ena_adapter *, unsigned int);
+static void ena_free_all_io_rings_resources(struct ena_adapter *);
+static int ena_setup_tx_dma_tag(struct ena_adapter *);
+static int ena_free_tx_dma_tag(struct ena_adapter *);
+static int ena_setup_rx_dma_tag(struct ena_adapter *);
+static int ena_free_rx_dma_tag(struct ena_adapter *);
+static int ena_setup_tx_resources(struct ena_adapter *, int);
+static void ena_free_tx_resources(struct ena_adapter *, int);
+static int ena_setup_all_tx_resources(struct ena_adapter *);
+static void ena_free_all_tx_resources(struct ena_adapter *);
+static int ena_setup_rx_resources(struct ena_adapter *, unsigned int);
+static void ena_free_rx_resources(struct ena_adapter *, unsigned int);
+static int ena_setup_all_rx_resources(struct ena_adapter *);
+static void ena_free_all_rx_resources(struct ena_adapter *);
+static inline int ena_alloc_rx_mbuf(struct ena_adapter *, struct ena_ring *,
+ struct ena_rx_buffer *);
+static void ena_free_rx_mbuf(struct ena_adapter *, struct ena_ring *,
+ struct ena_rx_buffer *);
+static int ena_refill_rx_bufs(struct ena_ring *, uint32_t);
+static void ena_free_rx_bufs(struct ena_adapter *, unsigned int);
+static void ena_refill_all_rx_bufs(struct ena_adapter *);
+static void ena_free_all_rx_bufs(struct ena_adapter *);
+static void ena_free_tx_bufs(struct ena_adapter *, unsigned int);
+static void ena_free_all_tx_bufs(struct ena_adapter *);
+static void ena_destroy_all_tx_queues(struct ena_adapter *);
+static void ena_destroy_all_rx_queues(struct ena_adapter *);
+static void ena_destroy_all_io_queues(struct ena_adapter *);
+static int ena_create_io_queues(struct ena_adapter *);
+static int ena_tx_cleanup(struct ena_ring *);
+static int ena_rx_cleanup(struct ena_ring *);
+static int validate_tx_req_id(struct ena_ring *, uint16_t);
+static void ena_rx_hash_mbuf(struct ena_ring *, struct ena_com_rx_ctx *,
+ struct mbuf *);
+static struct mbuf* ena_rx_mbuf(struct ena_ring *, struct ena_com_rx_buf_info *,
+ struct ena_com_rx_ctx *, uint16_t *);
+static inline void ena_rx_checksum(struct ena_ring *, struct ena_com_rx_ctx *,
+ struct mbuf *);
+static void ena_handle_msix(void *);
+static int ena_enable_msix(struct ena_adapter *);
+static void ena_setup_mgmnt_intr(struct ena_adapter *);
+static void ena_setup_io_intr(struct ena_adapter *);
+static int ena_request_mgmnt_irq(struct ena_adapter *);
+static int ena_request_io_irq(struct ena_adapter *);
+static void ena_free_mgmnt_irq(struct ena_adapter *);
+static void ena_free_io_irq(struct ena_adapter *);
+static void ena_free_irqs(struct ena_adapter*);
+static void ena_disable_msix(struct ena_adapter *);
+static void ena_unmask_all_io_irqs(struct ena_adapter *);
+static int ena_rss_configure(struct ena_adapter *);
+static int ena_up_complete(struct ena_adapter *);
+static int ena_up(struct ena_adapter *);
+static void ena_down(struct ena_adapter *);
+static uint64_t ena_get_counter(if_t, ift_counter);
+static int ena_media_change(if_t);
+static void ena_media_status(if_t, struct ifmediareq *);
+static void ena_init(void *);
+static int ena_ioctl(if_t, u_long, caddr_t);
+static int ena_get_dev_offloads(struct ena_com_dev_get_features_ctx *);
+static void ena_update_host_info(struct ena_admin_host_info *, if_t);
+static void ena_update_hwassist(struct ena_adapter *);
+static int ena_setup_ifnet(device_t, struct ena_adapter *,
+ struct ena_com_dev_get_features_ctx *);
+static void ena_tx_csum(struct ena_com_tx_ctx *, struct mbuf *);
+static int ena_xmit_mbuf(struct ena_ring *, struct mbuf *);
+static void ena_start_xmit(struct ena_ring *);
+static int ena_mq_start(if_t, struct mbuf *);
+static void ena_deferred_mq_start(void *, int);
+static void ena_qflush(if_t);
+static int ena_calc_io_queue_num(struct ena_adapter *,
+ struct ena_com_dev_get_features_ctx *);
+static int ena_calc_queue_size(struct ena_adapter *, uint16_t *,
+ uint16_t *, struct ena_com_dev_get_features_ctx *);
+static int ena_rss_init_default(struct ena_adapter *);
+static void ena_rss_init_default_deferred(void *);
+static void ena_config_host_info(struct ena_com_dev *);
+static int ena_attach(device_t);
+static int ena_detach(device_t);
+static int ena_device_init(struct ena_adapter *, device_t,
+ struct ena_com_dev_get_features_ctx *, int *);
+static int ena_enable_msix_and_set_admin_interrupts(struct ena_adapter *,
+ int);
+static void ena_update_on_link_change(void *, struct ena_admin_aenq_entry *);
+static void unimplemented_aenq_handler(void *,
+ struct ena_admin_aenq_entry *);
+static void ena_timer_service(void *);
+
+static char ena_version[] = DEVICE_NAME DRV_MODULE_NAME " v" DRV_MODULE_VERSION;
+
+static SYSCTL_NODE(_hw, OID_AUTO, ena, CTLFLAG_RD, 0, "ENA driver parameters");
+
+/*
+ * Tuneable number of buffers in the buf-ring (drbr)
+ */
+static int ena_buf_ring_size = 4096;
+SYSCTL_INT(_hw_ena, OID_AUTO, buf_ring_size, CTLFLAG_RWTUN,
+ &ena_buf_ring_size, 0, "Size of the bufring");
+
+
+static ena_vendor_info_t ena_vendor_info_array[] = {
+ { PCI_VENDOR_ID_AMAZON, PCI_DEV_ID_ENA_PF, 0},
+ { PCI_VENDOR_ID_AMAZON, PCI_DEV_ID_ENA_LLQ_PF, 0},
+ { PCI_VENDOR_ID_AMAZON, PCI_DEV_ID_ENA_VF, 0},
+ { PCI_VENDOR_ID_AMAZON, PCI_DEV_ID_ENA_LLQ_VF, 0},
+ /* Last entry */
+ { 0, 0, 0 }
+};
+
+/*
+ * Contains pointers to event handlers, e.g. link state chage.
+ */
+static struct ena_aenq_handlers aenq_handlers;
+
+void
+ena_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ if (error)
+ return;
+ *(bus_addr_t *) arg = segs[0].ds_addr;
+ return;
+}
+
+int
+ena_dma_alloc(device_t dmadev, bus_size_t size,
+ ena_mem_handle_t *dma , int mapflags)
+{
+ struct ena_adapter* adapter = device_get_softc(dmadev);
+ uint32_t maxsize = ((size - 1)/PAGE_SIZE + 1) * PAGE_SIZE;
+ uint64_t dma_space_addr = ENA_DMA_BIT_MASK(adapter->dma_width);
+ int error;
+
+ if (dma_space_addr == 0)
+ dma_space_addr = BUS_SPACE_MAXADDR;
+ error = bus_dma_tag_create(bus_get_dma_tag(dmadev), /* parent */
+ 8, 0, /* alignment, bounds */
+ dma_space_addr, /* lowaddr */
+ dma_space_addr, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ maxsize, /* maxsize */
+ 1, /* nsegments */
+ maxsize, /* maxsegsize */
+ BUS_DMA_ALLOCNOW, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &dma->tag);
+ if (error) {
+ device_printf(dmadev,
+ "%s: bus_dma_tag_create failed: %d\n",
+ __func__, error);
+ goto fail_tag;
+ }
+
+ error = bus_dmamem_alloc(dma->tag, (void**) &dma->vaddr,
+ BUS_DMA_COHERENT | BUS_DMA_ZERO, &dma->map);
+ if (error) {
+ device_printf(dmadev,
+ "%s: bus_dmamem_alloc(%ju) failed: %d\n",
+ __func__, (uintmax_t)size, error);
+ goto fail_map_create;
+ }
+
+ dma->paddr = 0;
+ error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr,
+ size, ena_dmamap_callback, &dma->paddr, mapflags);
+ if (error || dma->paddr == 0) {
+ device_printf(dmadev,
+ "%s: bus_dmamap_load failed: %d\n",
+ __func__, error);
+ goto fail_map_load;
+ }
+
+ return (0);
+
+fail_map_load:
+ bus_dmamap_unload(dma->tag, dma->map);
+fail_map_create:
+ bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
+ bus_dma_tag_destroy(dma->tag);
+fail_tag:
+ dma->tag = NULL;
+
+ return (error);
+}
+
+static int
+ena_allocate_pci_resources(struct ena_adapter* adapter)
+{
+ device_t pdev = adapter->pdev;
+ int rid;
+
+ rid = PCIR_BAR(ENA_REG_BAR);
+ adapter->memory = NULL;
+ adapter->registers = bus_alloc_resource_any(pdev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (adapter->registers == NULL) {
+ device_printf(pdev, "Unable to allocate bus resource: "
+ "registers\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+ena_free_pci_resources(struct ena_adapter *adapter)
+{
+ device_t pdev = adapter->pdev;
+
+ if (adapter->memory != NULL) {
+ bus_release_resource(pdev, SYS_RES_MEMORY,
+ PCIR_BAR(ENA_MEM_BAR), adapter->memory);
+ }
+
+ if (adapter->registers != NULL) {
+ bus_release_resource(pdev, SYS_RES_MEMORY,
+ PCIR_BAR(ENA_REG_BAR), adapter->registers);
+ }
+
+ return;
+}
+
+static int
+ena_probe(device_t dev)
+{
+ ena_vendor_info_t *ent;
+ char adapter_name[60];
+ uint16_t pci_vendor_id = 0;
+ uint16_t pci_device_id = 0;
+
+ pci_vendor_id = pci_get_vendor(dev);
+ pci_device_id = pci_get_device(dev);
+
+ ent = ena_vendor_info_array;
+ while (ent->vendor_id != 0) {
+ if ((pci_vendor_id == ent->vendor_id) &&
+ (pci_device_id == ent->device_id)) {
+ ena_trace(ENA_DBG, "vendor=%x device=%x ",
+ pci_vendor_id, pci_device_id);
+
+ sprintf(adapter_name, DEVICE_DESC);
+ device_set_desc_copy(dev, adapter_name);
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ ent++;
+
+ }
+
+ return (ENXIO);
+}
+
+static int
+ena_change_mtu(if_t ifp, int new_mtu)
+{
+ struct ena_adapter *adapter = if_getsoftc(ifp);
+ struct ena_com_dev_get_features_ctx get_feat_ctx;
+ int rc, old_mtu, max_frame;
+
+ rc = ena_com_get_dev_attr_feat(adapter->ena_dev, &get_feat_ctx);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "Cannot get attribute for ena device\n");
+ return (ENXIO);
+ }
+
+ /* Save old MTU in case of fail */
+ old_mtu = if_getmtu(ifp);
+
+ /* Change MTU and calculate max frame */
+ if_setmtu(ifp, new_mtu);
+ max_frame = ETHER_MAX_FRAME(ifp, ETHERTYPE_VLAN, 1);
+
+ if ((new_mtu < ENA_MIN_FRAME_LEN) ||
+ (new_mtu > get_feat_ctx.dev_attr.max_mtu) ||
+ (max_frame > ENA_MAX_FRAME_LEN)) {
+ device_printf(adapter->pdev, "Invalid MTU setting. "
+ "new_mtu: %d\n", new_mtu);
+ goto error;
+ }
+
+ rc = ena_com_set_dev_mtu(adapter->ena_dev, new_mtu);
+ if (rc != 0)
+ goto error;
+
+ return (0);
+error:
+ if_setmtu(ifp, old_mtu);
+ return (EINVAL);
+}
+
+static inline void
+ena_alloc_counters(counter_u64_t *begin, int size)
+{
+ counter_u64_t *end = (counter_u64_t *)((char *)begin + size);
+
+ for (; begin < end; ++begin)
+ *begin = counter_u64_alloc(M_WAITOK);
+}
+
+static inline void
+ena_free_counters(counter_u64_t *begin, int size)
+{
+ counter_u64_t *end = (counter_u64_t *)((char *)begin + size);
+
+ for (; begin < end; ++begin)
+ counter_u64_free(*begin);
+}
+
+static inline void
+ena_reset_counters(counter_u64_t *begin, int size)
+{
+ counter_u64_t *end = (counter_u64_t *)((char *)begin + size);
+
+ for (; begin < end; ++begin)
+ counter_u64_zero(*begin);
+}
+
+static void
+ena_init_io_rings_common(struct ena_adapter *adapter, struct ena_ring *ring,
+ uint16_t qid)
+{
+
+ ring->qid = qid;
+ ring->adapter = adapter;
+ ring->ena_dev = adapter->ena_dev;
+}
+
+static int
+ena_init_io_rings(struct ena_adapter *adapter)
+{
+ struct ena_com_dev *ena_dev;
+ struct ena_ring *txr, *rxr;
+ struct ena_que *que;
+ int i;
+
+ ena_dev = adapter->ena_dev;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ txr = &adapter->tx_ring[i];
+ rxr = &adapter->rx_ring[i];
+
+ /* TX/RX common ring state */
+ ena_init_io_rings_common(adapter, txr, i);
+ ena_init_io_rings_common(adapter, rxr, i);
+
+ /* TX specific ring state */
+ txr->ring_size = adapter->tx_ring_size;
+ txr->tx_max_header_size = ena_dev->tx_max_header_size;
+ txr->tx_mem_queue_type = ena_dev->tx_mem_queue_type;
+ txr->smoothed_interval =
+ ena_com_get_nonadaptive_moderation_interval_tx(ena_dev);
+
+ /* Allocate a buf ring */
+ txr->br = buf_ring_alloc(ena_buf_ring_size, M_DEVBUF,
+ M_WAITOK, &txr->ring_mtx);
+ if (txr->br == NULL) {
+ device_printf(adapter->pdev,
+ "Error while setting up bufring\n");
+ return ENOMEM;
+ }
+
+ /* Alloc TX statistics. */
+ ena_alloc_counters((counter_u64_t *)&txr->tx_stats,
+ sizeof(txr->tx_stats));
+
+ /* RX specific ring state */
+ rxr->ring_size = adapter->rx_ring_size;
+ rxr->rx_small_copy_len = adapter->small_copy_len;
+ rxr->smoothed_interval =
+ ena_com_get_nonadaptive_moderation_interval_rx(ena_dev);
+
+ /* Alloc RX statistics. */
+ ena_alloc_counters((counter_u64_t *)&rxr->rx_stats,
+ sizeof(rxr->rx_stats));
+
+ /* Initialize locks */
+ snprintf(txr->mtx_name, nitems(txr->mtx_name), "%s:tx(%d)",
+ device_get_nameunit(adapter->pdev), i);
+ snprintf(rxr->mtx_name, nitems(rxr->mtx_name), "%s:rx(%d)",
+ device_get_nameunit(adapter->pdev), i);
+
+ mtx_init(&txr->ring_mtx, txr->mtx_name, NULL, MTX_DEF);
+ mtx_init(&rxr->ring_mtx, rxr->mtx_name, NULL, MTX_DEF);
+
+ que = &adapter->que[i];
+ que->adapter = adapter;
+ que->id = i;
+ que->tx_ring = txr;
+ que->rx_ring = rxr;
+
+ txr->que = que;
+ rxr->que = que;
+ }
+
+ return 0;
+}
+
+static void
+ena_free_io_rings_resources(struct ena_adapter *adapter, unsigned int qid)
+{
+ struct ena_ring *txr = &adapter->tx_ring[qid];
+ struct ena_ring *rxr = &adapter->rx_ring[qid];
+
+ ena_free_counters((counter_u64_t *)&txr->tx_stats,
+ sizeof(txr->tx_stats));
+ ena_free_counters((counter_u64_t *)&rxr->rx_stats,
+ sizeof(rxr->rx_stats));
+
+ mtx_destroy(&txr->ring_mtx);
+ mtx_destroy(&rxr->ring_mtx);
+
+ drbr_free(txr->br, M_DEVBUF);
+
+}
+
+static void
+ena_free_all_io_rings_resources(struct ena_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++)
+ ena_free_io_rings_resources(adapter, i);
+
+}
+
+static int
+ena_setup_tx_dma_tag(struct ena_adapter *adapter)
+{
+ int ret;
+
+ /* Create DMA tag for Tx buffers */
+ ret = bus_dma_tag_create(bus_get_dma_tag(adapter->pdev),
+ 1, 0, /* alignment, bounds */
+ ENA_DMA_BIT_MASK(adapter->dma_width), /* lowaddr */
+ ENA_DMA_BIT_MASK(adapter->dma_width), /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ ENA_TSO_MAXSIZE, /* maxsize */
+ adapter->max_tx_sgl_size, /* nsegments */
+ ENA_TSO_MAXSIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockfuncarg */
+ &adapter->tx_buf_tag);
+
+ if (ret != 0)
+ device_printf(adapter->pdev, "Unable to create Tx DMA tag\n");
+
+ return (ret);
+}
+
+static int
+ena_free_tx_dma_tag(struct ena_adapter *adapter)
+{
+ int ret;
+
+ ret = bus_dma_tag_destroy(adapter->tx_buf_tag);
+
+ if (ret == 0)
+ adapter->tx_buf_tag = NULL;
+
+ return (ret);
+}
+
+static int
+ena_setup_rx_dma_tag(struct ena_adapter *adapter)
+{
+ int ret;
+
+ /* Create DMA tag for Rx buffers*/
+ ret = bus_dma_tag_create(bus_get_dma_tag(adapter->pdev), /* parent */
+ 1, 0, /* alignment, bounds */
+ ENA_DMA_BIT_MASK(adapter->dma_width), /* lowaddr */
+ ENA_DMA_BIT_MASK(adapter->dma_width), /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MJUM16BYTES, /* maxsize */
+ 1, /* nsegments */
+ MJUM16BYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, /* lockfunc */
+ NULL, /* lockarg */
+ &adapter->rx_buf_tag);
+
+ if (ret != 0)
+ device_printf(adapter->pdev, "Unable to create Rx DMA tag\n");
+
+ return (ret);
+}
+
+static int
+ena_free_rx_dma_tag(struct ena_adapter *adapter)
+{
+ int ret;
+
+ ret = bus_dma_tag_destroy(adapter->rx_buf_tag);
+
+ if (ret == 0)
+ adapter->rx_buf_tag = NULL;
+
+ return (ret);
+}
+
+
+/**
+ * ena_setup_tx_resources - allocate Tx resources (Descriptors)
+ * @adapter: network interface device structure
+ * @qid: queue index
+ *
+ * Returns 0 on success, otherwise on failure.
+ **/
+static int
+ena_setup_tx_resources(struct ena_adapter *adapter, int qid)
+{
+ struct ena_que *que = &adapter->que[qid];
+ struct ena_ring *tx_ring = que->tx_ring;
+ int size, i, err;
+#ifdef RSS
+ cpuset_t cpu_mask;
+#endif
+
+ size = sizeof(struct ena_tx_buffer) * tx_ring->ring_size;
+
+ tx_ring->tx_buffer_info = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (!tx_ring->tx_buffer_info)
+ goto err_tx_buffer_info;
+
+ size = sizeof(uint16_t) * tx_ring->ring_size;
+ tx_ring->free_tx_ids = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (!tx_ring->free_tx_ids)
+ goto err_tx_reqs;
+
+ /* Req id stack for TX OOO completions */
+ for (i = 0; i < tx_ring->ring_size; i++)
+ tx_ring->free_tx_ids[i] = i;
+
+ /* Reset TX statistics. */
+ ena_reset_counters((counter_u64_t *)&tx_ring->tx_stats,
+ sizeof(tx_ring->tx_stats));
+
+ tx_ring->next_to_use = 0;
+ tx_ring->next_to_clean = 0;
+
+ /* ... and create the buffer DMA maps */
+ for (i = 0; i < tx_ring->ring_size; i++) {
+ err = bus_dmamap_create(adapter->tx_buf_tag, 0,
+ &tx_ring->tx_buffer_info[i].map);
+ if (err != 0) {
+ device_printf(adapter->pdev,
+ "Unable to create Tx DMA map for buffer %d\n", i);
+ goto err_tx_map;
+ }
+ }
+
+ /* Allocate taskqueues */
+ TASK_INIT(&tx_ring->enqueue_task, 0, ena_deferred_mq_start, tx_ring);
+ tx_ring->enqueue_tq = taskqueue_create_fast("ena_tx_enque", M_NOWAIT,
+ taskqueue_thread_enqueue, &tx_ring->enqueue_tq);
+ if (tx_ring->enqueue_tq == NULL) {
+ device_printf(adapter->pdev,
+ "Unable to create taskqueue for enqueue task\n");
+ i = tx_ring->ring_size;
+ goto err_tx_map;
+ }
+
+ /* RSS set cpu for thread */
+#ifdef RSS
+ CPU_SETOF(que->cpu, &cpu_mask);
+ taskqueue_start_threads_cpuset(&tx_ring->enqueue_tq, 1, PI_NET,
+ &cpu_mask, "%s tx_ring enq (bucket %d)",
+ device_get_nameunit(adapter->pdev), que->cpu);
+#else /* RSS */
+ taskqueue_start_threads(&tx_ring->enqueue_tq, 1, PI_NET,
+ "%s txeq %d", device_get_nameunit(adapter->pdev), que->cpu);
+#endif /* RSS */
+
+ return (0);
+
+err_tx_map:
+ while (i--) {
+ bus_dmamap_destroy(adapter->tx_buf_tag,
+ tx_ring->tx_buffer_info[i].map);
+ }
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, tx_ring->free_tx_ids);
+err_tx_reqs:
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, tx_ring->tx_buffer_info);
+err_tx_buffer_info:
+ return (ENOMEM);
+}
+
+/**
+ * ena_free_tx_resources - Free Tx Resources per Queue
+ * @adapter: network interface device structure
+ * @qid: queue index
+ *
+ * Free all transmit software resources
+ **/
+static void
+ena_free_tx_resources(struct ena_adapter *adapter, int qid)
+{
+ struct ena_ring *tx_ring = &adapter->tx_ring[qid];
+
+ while (taskqueue_cancel(tx_ring->enqueue_tq, &tx_ring->enqueue_task,
+ NULL))
+ taskqueue_drain(tx_ring->enqueue_tq, &tx_ring->enqueue_task);
+
+ taskqueue_free(tx_ring->enqueue_tq);
+
+ /* Flush buffer ring, */
+ drbr_flush(adapter->ifp, tx_ring->br);
+
+ /* Free buffer DMA maps, */
+ for (int i = 0; i < tx_ring->ring_size; i++) {
+ m_freem(tx_ring->tx_buffer_info[i].mbuf);
+ tx_ring->tx_buffer_info[i].mbuf = NULL;
+ bus_dmamap_unload(adapter->tx_buf_tag,
+ tx_ring->tx_buffer_info[i].map);
+ bus_dmamap_destroy(adapter->tx_buf_tag,
+ tx_ring->tx_buffer_info[i].map);
+ }
+
+ /* And free allocated memory. */
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, tx_ring->tx_buffer_info);
+ tx_ring->tx_buffer_info = NULL;
+
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, tx_ring->free_tx_ids);
+ tx_ring->free_tx_ids = NULL;
+}
+
+/**
+ * ena_setup_all_tx_resources - allocate all queues Tx resources
+ * @adapter: network interface device structure
+ *
+ * Returns 0 on success, otherwise on failure.
+ **/
+static int
+ena_setup_all_tx_resources(struct ena_adapter *adapter)
+{
+ int i, rc;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ rc = ena_setup_tx_resources(adapter, i);
+ if (!rc)
+ continue;
+
+ device_printf(adapter->pdev,
+ "Allocation for Tx Queue %u failed\n", i);
+ goto err_setup_tx;
+ }
+
+ return (0);
+
+err_setup_tx:
+ /* Rewind the index freeing the rings as we go */
+ while (i--)
+ ena_free_tx_resources(adapter, i);
+ return (rc);
+}
+
+/**
+ * ena_free_all_tx_resources - Free Tx Resources for All Queues
+ * @adapter: network interface device structure
+ *
+ * Free all transmit software resources
+ **/
+static void
+ena_free_all_tx_resources(struct ena_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++)
+ ena_free_tx_resources(adapter, i);
+
+ return;
+}
+
+/**
+ * ena_setup_rx_resources - allocate Rx resources (Descriptors)
+ * @adapter: network interface device structure
+ * @qid: queue index
+ *
+ * Returns 0 on success, otherwise on failure.
+ **/
+static int
+ena_setup_rx_resources(struct ena_adapter *adapter, unsigned int qid)
+{
+ struct ena_que *que = &adapter->que[qid];
+ struct ena_ring *rx_ring = que->rx_ring;
+ int size, err, i;
+#ifdef RSS
+ cpuset_t cpu_mask;
+#endif
+
+ size = sizeof(struct ena_rx_buffer) * rx_ring->ring_size;
+
+ /*
+ * Alloc extra element so in rx path
+ * we can always prefetch rx_info + 1
+ */
+ size += sizeof(struct ena_rx_buffer);
+
+ rx_ring->rx_buffer_info = ENA_MEM_ALLOC(adapter->ena_dev->dmadev, size);
+ if (!rx_ring->rx_buffer_info)
+ return (ENOMEM);
+
+ /* Reset RX statistics. */
+ ena_reset_counters((counter_u64_t *)&rx_ring->rx_stats,
+ sizeof(rx_ring->rx_stats));
+
+ rx_ring->next_to_clean = 0;
+ rx_ring->next_to_use = 0;
+
+ /* ... and create the buffer DMA maps */
+ for (i = 0; i < rx_ring->ring_size; i++) {
+ err = bus_dmamap_create(adapter->rx_buf_tag, 0,
+ &(rx_ring->rx_buffer_info[i].map));
+ if (err != 0) {
+ device_printf(adapter->pdev,
+ "Unable to create Rx DMA map for buffer %d\n", i);
+ goto err_rx_dma;
+ }
+ }
+
+ /* Create LRO for the ring */
+ if (adapter->ifp->if_capenable & IFCAP_LRO) {
+ int err = tcp_lro_init(&rx_ring->lro);
+ if (err) {
+ device_printf(adapter->pdev,
+ "LRO[%d] Initialization failed!\n", qid);
+ } else {
+ ena_trace(ENA_INFO,
+ "RX Soft LRO[%d] Initialized\n", qid);
+ rx_ring->lro.ifp = adapter->ifp;
+ }
+ }
+
+ return (0);
+
+err_rx_dma:
+ while (i--) {
+ bus_dmamap_destroy(adapter->rx_buf_tag,
+ rx_ring->rx_buffer_info[i].map);
+ }
+
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, rx_ring->rx_buffer_info);
+ rx_ring->rx_buffer_info = NULL;
+ ena_trace(ENA_ALERT, "RX resource allocation fail");
+ return (ENOMEM);
+}
+
+/**
+ * ena_free_rx_resources - Free Rx Resources
+ * @adapter: network interface device structure
+ * @qid: queue index
+ *
+ * Free all receive software resources
+ **/
+static void
+ena_free_rx_resources(struct ena_adapter *adapter, unsigned int qid)
+{
+ struct ena_ring *rx_ring = &adapter->rx_ring[qid];
+
+ ena_trace(ENA_INFO, "%s qid %d\n", __func__, qid);
+
+ /* Free buffer DMA maps, */
+ for (int i = 0; i < rx_ring->ring_size; i++) {
+ m_freem(rx_ring->rx_buffer_info[i].mbuf);
+ rx_ring->rx_buffer_info[i].mbuf = NULL;
+ bus_dmamap_unload(adapter->rx_buf_tag,
+ rx_ring->rx_buffer_info[i].map);
+ bus_dmamap_destroy(adapter->rx_buf_tag,
+ rx_ring->rx_buffer_info[i].map);
+ }
+
+ /* free LRO resources, */
+ tcp_lro_free(&rx_ring->lro);
+
+ /* free allocated memory */
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, rx_ring->rx_buffer_info);
+ rx_ring->rx_buffer_info = NULL;
+
+ return;
+}
+
+/**
+ * ena_setup_all_rx_resources - allocate all queues Rx resources
+ * @adapter: network interface device structure
+ *
+ * Returns 0 on success, otherwise on failure.
+ **/
+static int
+ena_setup_all_rx_resources(struct ena_adapter *adapter)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ rc = ena_setup_rx_resources(adapter, i);
+ if (!rc)
+ continue;
+
+ device_printf(adapter->pdev,
+ "Allocation for Rx Queue %u failed\n", i);
+ goto err_setup_rx;
+ }
+ return (0);
+
+err_setup_rx:
+ /* rewind the index freeing the rings as we go */
+ while (i--)
+ ena_free_rx_resources(adapter, i);
+ return (rc);
+}
+
+/**
+ * ena_free_all_rx_resources - Free Rx resources for all queues
+ * @adapter: network interface device structure
+ *
+ * Free all receive software resources
+ **/
+static void
+ena_free_all_rx_resources(struct ena_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++)
+ ena_free_rx_resources(adapter, i);
+
+ return;
+}
+
+static inline int
+ena_alloc_rx_mbuf(struct ena_adapter *adapter,
+ struct ena_ring *rx_ring, struct ena_rx_buffer *rx_info)
+{
+ struct ena_com_buf *ena_buf;
+ bus_dma_segment_t segs[1];
+ int nsegs, error;
+
+ /* if previous allocated frag is not used */
+ if (rx_info->mbuf != NULL)
+ return (0);
+
+ ENA_RING_MTX_LOCK(rx_ring);
+ /* Get mbuf using UMA allocator */
+ rx_info->mbuf = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUM16BYTES);
+ ENA_RING_MTX_UNLOCK(rx_ring);
+
+ if (!rx_info->mbuf) {
+ counter_u64_add(rx_ring->rx_stats.mbuf_alloc_fail, 1);
+ return (ENOMEM);
+ }
+ /* Set mbuf length*/
+ rx_info->mbuf->m_pkthdr.len = rx_info->mbuf->m_len = MJUM16BYTES;
+
+ /* Map packets for DMA */
+ ena_trace(ENA_DBG | ENA_RSC | ENA_RXPTH,
+ "Using tag %p for buffers' DMA mapping, mbuf %p len: %d",
+ adapter->rx_buf_tag,rx_info->mbuf, rx_info->mbuf->m_len);
+ error = bus_dmamap_load_mbuf_sg(adapter->rx_buf_tag, rx_info->map,
+ rx_info->mbuf, segs, &nsegs, BUS_DMA_NOWAIT);
+ if (error || (nsegs != 1)) {
+ device_printf(adapter->pdev, "failed to map mbuf, error: %d, "
+ "nsegs: %d\n", error, nsegs);
+ counter_u64_add(rx_ring->rx_stats.dma_mapping_err, 1);
+ goto exit;
+
+ }
+
+ bus_dmamap_sync(adapter->rx_buf_tag, rx_info->map, BUS_DMASYNC_PREREAD);
+
+ ena_buf = &rx_info->ena_buf;
+ ena_buf->paddr = segs[0].ds_addr;
+ ena_buf->len = MJUM16BYTES;
+
+ ena_trace(ENA_DBG | ENA_RSC | ENA_RXPTH,
+ "ALLOC RX BUF: mbuf %p, rx_info %p, len %d, paddr %#jx\n",
+ rx_info->mbuf, rx_info,ena_buf->len, (uintmax_t)ena_buf->paddr);
+
+ return (0);
+
+exit:
+ m_freem(rx_info->mbuf);
+ rx_info->mbuf = NULL;
+ return (EFAULT);
+}
+
+static void
+ena_free_rx_mbuf(struct ena_adapter *adapter, struct ena_ring *rx_ring,
+ struct ena_rx_buffer *rx_info)
+{
+
+ if (!rx_info->mbuf)
+ return;
+
+ bus_dmamap_unload(adapter->rx_buf_tag, rx_info->map);
+ m_freem(rx_info->mbuf);
+ rx_info->mbuf = NULL;
+
+ return;
+}
+
+
+/**
+ * ena_refill_rx_bufs - Refills ring with descriptors
+ * @rx_ring: the ring which we want to feed with free descriptors
+ * @num: number of descriptors to refill
+ * Refills the ring with newly allocated DMA-mapped mbufs for receiving
+ **/
+static int
+ena_refill_rx_bufs(struct ena_ring *rx_ring, uint32_t num)
+{
+ struct ena_adapter *adapter = rx_ring->adapter;
+ uint16_t next_to_use;
+ uint32_t i;
+ int rc;
+
+ ena_trace(ENA_DBG | ENA_RXPTH | ENA_RSC, "refill qid: %d",
+ rx_ring->qid);
+
+ next_to_use = rx_ring->next_to_use;
+
+ for (i = 0; i < num; i++) {
+ ena_trace(ENA_DBG | ENA_RXPTH | ENA_RSC,
+ "RX buffer - next to use: %d", next_to_use);
+
+ struct ena_rx_buffer *rx_info =
+ &rx_ring->rx_buffer_info[next_to_use];
+
+ rc = ena_alloc_rx_mbuf(adapter, rx_ring, rx_info);
+ if (rc < 0) {
+ device_printf(adapter->pdev,
+ "failed to alloc buffer for rx queue\n");
+ break;
+ }
+ rc = ena_com_add_single_rx_desc(rx_ring->ena_com_io_sq,
+ &rx_info->ena_buf, next_to_use);
+ if (unlikely(rc)) {
+ device_printf(adapter->pdev,
+ "failed to add buffer for rx queue %d\n",
+ rx_ring->qid);
+ break;
+ }
+ next_to_use = ENA_RX_RING_IDX_NEXT(next_to_use,
+ rx_ring->ring_size);
+ }
+
+ if (i < num) {
+ counter_u64_add(rx_ring->rx_stats.refil_partial, 1);
+ device_printf(adapter->pdev,
+ "refilled rx queue %d with %d pages only\n",
+ rx_ring->qid, i);
+ }
+
+ if (i != 0) {
+ wmb();
+ ena_com_write_sq_doorbell(rx_ring->ena_com_io_sq);
+ }
+ rx_ring->next_to_use = next_to_use;
+ return (i);
+}
+
+static void
+ena_free_rx_bufs(struct ena_adapter *adapter, unsigned int qid)
+{
+ struct ena_ring *rx_ring = &adapter->rx_ring[qid];
+ unsigned int i;
+
+ for (i = 0; i < rx_ring->ring_size; i++) {
+ struct ena_rx_buffer *rx_info = &rx_ring->rx_buffer_info[i];
+
+ if (rx_info->mbuf)
+ ena_free_rx_mbuf(adapter, rx_ring, rx_info);
+ }
+
+ return;
+}
+
+/**
+ * ena_refill_all_rx_bufs - allocate all queues Rx buffers
+ * @adapter: network interface device structure
+ *
+ */
+static void
+ena_refill_all_rx_bufs(struct ena_adapter *adapter)
+{
+ struct ena_ring *rx_ring;
+ int i, rc, bufs_num;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ rx_ring = &adapter->rx_ring[i];
+ bufs_num = rx_ring->ring_size - 1;
+ rc = ena_refill_rx_bufs(rx_ring, bufs_num);
+
+ if (unlikely(rc != bufs_num))
+ device_printf(adapter->pdev,
+ "refilling Queue %d failed. allocated %d buffers"
+ " from: %d\n", i, rc, bufs_num);
+ }
+}
+
+static void
+ena_free_all_rx_bufs(struct ena_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++)
+ ena_free_rx_bufs(adapter, i);
+ return;
+}
+
+/**
+ * ena_free_tx_bufs - Free Tx Buffers per Queue
+ * @adapter: network interface device structure
+ * @qid: queue index
+ **/
+static void
+ena_free_tx_bufs(struct ena_adapter *adapter, unsigned int qid)
+{
+ struct ena_ring *tx_ring = &adapter->tx_ring[qid];
+
+ for (int i = 0; i < tx_ring->ring_size; i++) {
+ struct ena_tx_buffer *tx_info = &tx_ring->tx_buffer_info[i];
+
+ if (tx_info->mbuf == NULL)
+ continue;
+
+ ena_trace(ENA_DBG | ENA_TXPTH | ENA_RSC,
+ "free uncompleted Tx mbufs qid[%d] idx: 0x%x", qid, i);
+
+ bus_dmamap_unload(adapter->tx_buf_tag, tx_info->map);
+ m_free(tx_info->mbuf);
+ tx_info->mbuf = NULL;
+ }
+
+ return;
+}
+
+static void
+ena_free_all_tx_bufs(struct ena_adapter *adapter)
+{
+
+ for (int i = 0; i < adapter->num_queues; i++)
+ ena_free_tx_bufs(adapter, i);
+
+ return;
+}
+
+static void
+ena_destroy_all_tx_queues(struct ena_adapter *adapter)
+{
+ uint16_t ena_qid;
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ ena_qid = ENA_IO_TXQ_IDX(i);
+ ena_com_destroy_io_queue(adapter->ena_dev, ena_qid);
+ }
+}
+
+static void
+ena_destroy_all_rx_queues(struct ena_adapter *adapter)
+{
+ uint16_t ena_qid;
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ ena_qid = ENA_IO_RXQ_IDX(i);
+ ena_com_destroy_io_queue(adapter->ena_dev, ena_qid);
+ }
+}
+
+static void
+ena_destroy_all_io_queues(struct ena_adapter *adapter)
+{
+ ena_destroy_all_tx_queues(adapter);
+ ena_destroy_all_rx_queues(adapter);
+}
+
+static int
+validate_tx_req_id(struct ena_ring *tx_ring, uint16_t req_id)
+{
+ struct ena_tx_buffer *tx_info = NULL;
+
+ if (likely(req_id < tx_ring->ring_size)) {
+ tx_info = &tx_ring->tx_buffer_info[req_id];
+ if (tx_info->mbuf)
+ return 0;
+ }
+
+ counter_u64_add(tx_ring->tx_stats.bad_req_id, 1);
+
+ return (EFAULT);
+}
+
+static int
+ena_create_io_queues(struct ena_adapter *adapter)
+{
+ struct ena_com_dev *ena_dev = adapter->ena_dev;
+ struct ena_com_create_io_ctx ctx;
+ struct ena_ring *ring;
+ uint16_t ena_qid;
+ uint32_t msix_vector;
+ int rc, i;
+
+ /* Create TX queues */
+ for (i = 0; i < adapter->num_queues; i++) {
+ msix_vector = ENA_IO_IRQ_IDX(i);
+ ena_qid = ENA_IO_TXQ_IDX(i);
+ ctx.mem_queue_type = ena_dev->tx_mem_queue_type;
+ ctx.direction = ENA_COM_IO_QUEUE_DIRECTION_TX;
+ ctx.queue_size = adapter->tx_ring_size;
+ ctx.msix_vector = msix_vector;
+ ctx.qid = ena_qid;
+ rc = ena_com_create_io_queue(ena_dev, &ctx);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "Failed to create io TX queue #%d rc: %d\n", i, rc);
+ goto err_tx;
+ }
+ ring = &adapter->tx_ring[i];
+ rc = ena_com_get_io_handlers(ena_dev, ena_qid,
+ &ring->ena_com_io_sq,
+ &ring->ena_com_io_cq);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "Failed to get TX queue handlers. TX queue num"
+ " %d rc: %d\n", i, rc);
+ ena_com_destroy_io_queue(ena_dev, ena_qid);
+ goto err_tx;
+ }
+ }
+
+ /* Create RX queues */
+ for (i = 0; i < adapter->num_queues; i++) {
+ msix_vector = ENA_IO_IRQ_IDX(i);
+ ena_qid = ENA_IO_RXQ_IDX(i);
+ ctx.mem_queue_type = ENA_ADMIN_PLACEMENT_POLICY_HOST;
+ ctx.direction = ENA_COM_IO_QUEUE_DIRECTION_RX;
+ ctx.queue_size = adapter->rx_ring_size;
+ ctx.msix_vector = msix_vector;
+ ctx.qid = ena_qid;
+ rc = ena_com_create_io_queue(ena_dev, &ctx);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "Failed to create io RX queue[%d] rc: %d\n", i, rc);
+ goto err_rx;
+ }
+
+ ring = &adapter->rx_ring[i];
+ rc = ena_com_get_io_handlers(ena_dev, ena_qid,
+ &ring->ena_com_io_sq,
+ &ring->ena_com_io_cq);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "Failed to get RX queue handlers. RX queue num"
+ " %d rc: %d\n", i, rc);
+ ena_com_destroy_io_queue(ena_dev, ena_qid);
+ goto err_rx;
+ }
+ }
+
+ return (0);
+
+err_rx:
+ while (i--)
+ ena_com_destroy_io_queue(ena_dev, ENA_IO_RXQ_IDX(i));
+ i = adapter->num_queues;
+err_tx:
+ while (i--)
+ ena_com_destroy_io_queue(ena_dev, ENA_IO_TXQ_IDX(i));
+
+ return (ENXIO);
+}
+
+/**
+ * ena_tx_cleanup - clear sent packets and corresponding descriptors
+ * @tx_ring: ring for which we want to clean packets
+ *
+ * Once packets are sent, we ask the device in a loop for no longer used
+ * descriptors. We find the related mbuf chain in a map (index in an array)
+ * and free it, then update ring state.
+ * This is performed in "endless" loop, updating ring pointers every
+ * TX_COMMIT. The first check of free descriptor is performed before the actual
+ * loop, then repeated at the loop end.
+ **/
+static int
+ena_tx_cleanup(struct ena_ring *tx_ring)
+{
+ struct ena_adapter *adapter;
+ struct ena_com_io_cq* io_cq;
+ uint16_t next_to_clean;
+ uint16_t req_id;
+ uint16_t ena_qid;
+ unsigned int total_done = 0;
+ int rc;
+ int commit = TX_COMMIT;
+ int budget = TX_BUDGET;
+ int work_done;
+
+ adapter = tx_ring->que->adapter;
+ ena_qid = ENA_IO_TXQ_IDX(tx_ring->que->id);
+ io_cq = &adapter->ena_dev->io_cq_queues[ena_qid];
+ next_to_clean = tx_ring->next_to_clean;
+
+ do {
+ struct ena_tx_buffer *tx_info;
+ struct mbuf *mbuf;
+
+ rc = ena_com_tx_comp_req_id_get(io_cq, &req_id);
+ if (rc != 0)
+ break;
+
+ rc = validate_tx_req_id(tx_ring, req_id);
+ if (rc)
+ break;
+
+ tx_info = &tx_ring->tx_buffer_info[req_id];
+
+ mbuf = tx_info->mbuf;
+
+ tx_info->mbuf = NULL;
+ bintime_clear(&tx_info->timestamp);
+
+ if (tx_info->num_of_bufs != 0) {
+ /* Map is no longer required */
+ bus_dmamap_unload(adapter->tx_buf_tag, tx_info->map);
+ }
+
+ m_freem(mbuf);
+
+ total_done += tx_info->tx_descs;
+
+ tx_ring->free_tx_ids[next_to_clean] = req_id;
+ next_to_clean = ENA_TX_RING_IDX_NEXT(next_to_clean,
+ tx_ring->ring_size);
+
+ if (--commit == 0) {
+ commit = TX_COMMIT;
+ /* update ring state every TX_COMMIT descriptor */
+ tx_ring->next_to_clean = next_to_clean;
+ ena_com_comp_ack(&adapter->ena_dev->io_sq_queues[ena_qid], total_done);
+ ena_com_update_dev_comp_head(io_cq);
+ total_done = 0;
+ }
+ } while (--budget);
+
+ work_done = TX_BUDGET - budget;
+
+ /* If there is still something to commit update ring state */
+ if (commit != TX_COMMIT) {
+ tx_ring->next_to_clean = next_to_clean;
+ ena_com_comp_ack(&adapter->ena_dev->io_sq_queues[ena_qid], total_done);
+ ena_com_update_dev_comp_head(io_cq);
+ }
+
+ taskqueue_enqueue(tx_ring->enqueue_tq, &tx_ring->enqueue_task);
+
+ return (work_done);
+}
+
+static void
+ena_rx_hash_mbuf(struct ena_ring *rx_ring, struct ena_com_rx_ctx *ena_rx_ctx,
+ struct mbuf *mbuf)
+{
+ struct ena_adapter *adapter = rx_ring->adapter;
+
+ if (adapter->rss_support) {
+ mbuf->m_pkthdr.flowid = ena_rx_ctx->hash;
+
+ if (ena_rx_ctx->frag &&
+ ena_rx_ctx->l3_proto != ENA_ETH_IO_L4_PROTO_UNKNOWN) {
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_OPAQUE_HASH);
+ return;
+ }
+
+ switch (ena_rx_ctx->l3_proto) {
+ case ENA_ETH_IO_L3_PROTO_IPV4:
+ switch (ena_rx_ctx->l4_proto) {
+ case ENA_ETH_IO_L4_PROTO_TCP:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_RSS_TCP_IPV4);
+ break;
+ case ENA_ETH_IO_L4_PROTO_UDP:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_RSS_UDP_IPV4);
+ break;
+ default:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_RSS_IPV4);
+ }
+ break;
+ case ENA_ETH_IO_L3_PROTO_IPV6:
+ switch (ena_rx_ctx->l4_proto) {
+ case ENA_ETH_IO_L4_PROTO_TCP:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_RSS_TCP_IPV6);
+ break;
+ case ENA_ETH_IO_L4_PROTO_UDP:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_RSS_UDP_IPV6);
+ break;
+ default:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_RSS_IPV6);
+ }
+ break;
+ case ENA_ETH_IO_L3_PROTO_UNKNOWN:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_NONE);
+ break;
+ default:
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_OPAQUE_HASH);
+ }
+ } else {
+ mbuf->m_pkthdr.flowid = rx_ring->qid;
+ M_HASHTYPE_SET(mbuf, M_HASHTYPE_NONE);
+ }
+}
+
+/**
+ * ena_rx_mbuf - assemble mbuf from descriptors
+ * @rx_ring: ring for which we want to clean packets
+ * @ena_bufs: buffer info
+ * @ena_rx_ctx: metadata for this packet(s)
+ * @next_to_clean: ring pointer
+ *
+ **/
+static struct mbuf*
+ena_rx_mbuf(struct ena_ring *rx_ring, struct ena_com_rx_buf_info *ena_bufs,
+ struct ena_com_rx_ctx *ena_rx_ctx, uint16_t *next_to_clean)
+{
+ struct mbuf *mbuf;
+ struct ena_rx_buffer *rx_info;
+ struct ena_adapter *adapter;
+ unsigned int len, buf = 0;
+ unsigned int descs = ena_rx_ctx->descs;
+
+ adapter = rx_ring->adapter;
+ rx_info = &rx_ring->rx_buffer_info[*next_to_clean];
+
+ ENA_ASSERT(rx_info->mbuf, "Invalid alloc frag buffer\n");
+
+ len = ena_bufs[0].len;
+ ena_trace(ENA_DBG | ENA_RXPTH, "rx_info %p, mbuf %p, paddr %jx",
+ rx_info, rx_info->mbuf, (uintmax_t)rx_info->ena_buf.paddr);
+
+ mbuf = rx_info->mbuf;
+ mbuf->m_flags |= M_PKTHDR;
+ mbuf->m_pkthdr.len = len;
+ mbuf->m_len = len;
+ mbuf->m_pkthdr.rcvif = rx_ring->que->adapter->ifp;
+
+ /* Fill mbuf with hash key and it's interpretation for optimization */
+ ena_rx_hash_mbuf(rx_ring, ena_rx_ctx, mbuf);
+
+ ena_trace(ENA_DBG | ENA_RXPTH, "rx mbuf 0x%p, flags=0x%x, len: %d",
+ mbuf, mbuf->m_flags, mbuf->m_pkthdr.len);
+
+ /* DMA address is not needed anymore, unmap it */
+ bus_dmamap_unload(rx_ring->adapter->rx_buf_tag, rx_info->map);
+
+ rx_info->mbuf = NULL;
+ *next_to_clean = ENA_RX_RING_IDX_NEXT(*next_to_clean,
+ rx_ring->ring_size);
+
+ /*
+ * While we have more than 1 descriptors for one rcvd packet, append
+ * other mbufs to the main one
+ */
+ while (--descs) {
+ rx_info = &rx_ring->rx_buffer_info[*next_to_clean];
+ len = ena_bufs[++buf].len;
+
+ if (!m_append(mbuf, len, rx_info->mbuf->m_data)) {
+ counter_u64_add(rx_ring->rx_stats.mbuf_alloc_fail, 1);
+ ena_trace(ENA_WARNING, "Failed to append Rx mbuf %p",
+ mbuf);
+ }
+ /* Free already appended mbuf, it won't be useful anymore */
+ bus_dmamap_unload(rx_ring->adapter->rx_buf_tag, rx_info->map);
+ m_freem(rx_info->mbuf);
+ rx_info->mbuf = NULL;
+
+ *next_to_clean = ENA_RX_RING_IDX_NEXT(*next_to_clean,
+ rx_ring->ring_size);
+ }
+
+ return (mbuf);
+}
+
+/**
+ * ena_rx_checksum - indicate in mbuf if hw indicated a good cksum
+ **/
+static inline void
+ena_rx_checksum(struct ena_ring *rx_ring, struct ena_com_rx_ctx *ena_rx_ctx,
+ struct mbuf *mbuf)
+{
+
+ /* if IP and error */
+ if ((ena_rx_ctx->l3_proto == ENA_ETH_IO_L3_PROTO_IPV4) &&
+ (ena_rx_ctx->l3_csum_err)) {
+ /* ipv4 checksum error */
+ mbuf->m_pkthdr.csum_flags = 0;
+ counter_u64_add(rx_ring->rx_stats.bad_csum, 1);
+ return;
+ }
+
+ /* if TCP/UDP */
+ if ((ena_rx_ctx->l4_proto == ENA_ETH_IO_L4_PROTO_TCP) ||
+ (ena_rx_ctx->l4_proto == ENA_ETH_IO_L4_PROTO_UDP)) {
+ if (ena_rx_ctx->l4_csum_err) {
+ /* TCP/UDP checksum error */
+ mbuf->m_pkthdr.csum_flags = 0;
+ counter_u64_add(rx_ring->rx_stats.bad_csum, 1);
+ } else {
+ mbuf->m_pkthdr.csum_flags = CSUM_IP_CHECKED;
+ mbuf->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ }
+ }
+
+ return;
+}
+
+/**
+ * ena_rx_cleanup - handle rx irq
+ * @arg: ring for which irq is being handled
+ **/
+static int
+ena_rx_cleanup(struct ena_ring *rx_ring)
+{
+ struct ena_adapter *adapter;
+ struct mbuf *mbuf;
+ struct ena_com_rx_ctx ena_rx_ctx;
+ struct ena_com_io_cq* io_cq;
+ struct ena_com_io_sq* io_sq;
+ /* struct ena_eth_io_intr_reg intr_reg; */
+ if_t ifp;
+ uint16_t ena_qid;
+ uint16_t next_to_clean;
+ uint32_t refill_required;
+ uint32_t refill_threshold;
+ uint32_t do_if_input = 0;
+ unsigned int qid;
+ int rc;
+ int budget = RX_BUDGET;
+
+ adapter = rx_ring->que->adapter;
+ ifp = adapter->ifp;
+ qid = rx_ring->que->id;
+ ena_qid = ENA_IO_RXQ_IDX(qid);
+ io_cq = &adapter->ena_dev->io_cq_queues[ena_qid];
+ io_sq = &adapter->ena_dev->io_sq_queues[ena_qid];
+ next_to_clean = rx_ring->next_to_clean;
+
+ do {
+ ena_rx_ctx.ena_bufs = rx_ring->ena_bufs;
+ ena_rx_ctx.max_bufs = adapter->max_rx_sgl_size;
+ ena_rx_ctx.descs = 0;
+ rc = ena_com_rx_pkt(io_cq, io_sq, &ena_rx_ctx);
+
+ if (unlikely(rc))
+ goto error;
+
+ if (unlikely(ena_rx_ctx.descs == 0))
+ break;
+
+ /* Receive mbuf from the ring */
+ mbuf = ena_rx_mbuf(rx_ring, rx_ring->ena_bufs,
+ &ena_rx_ctx, &next_to_clean);
+
+ /* Exit if we failed to retrieve a buffer */
+ if (unlikely(!mbuf)) {
+ next_to_clean = ENA_RX_RING_IDX_ADD(next_to_clean,
+ ena_rx_ctx.descs, rx_ring->ring_size);
+ break;
+ }
+ ena_trace(ENA_DBG | ENA_RXPTH, "Rx: %d bytes",
+ mbuf->m_pkthdr.len);
+
+ if ((ifp->if_capenable & IFCAP_RXCSUM) ||
+ (ifp->if_capenable & IFCAP_RXCSUM_IPV6)) {
+ ena_rx_checksum(rx_ring, &ena_rx_ctx, mbuf);
+ }
+
+ counter_u64_add(rx_ring->rx_stats.bytes, mbuf->m_pkthdr.len);
+ /*
+ * LRO is only for IP/TCP packets and TCP checksum of the packet
+ * should be computed by hardware.
+ */
+ do_if_input = 1;
+ if ((ifp->if_capenable & IFCAP_LRO) &&
+ (mbuf->m_pkthdr.csum_flags & CSUM_IP_VALID) &&
+ ena_rx_ctx.l4_proto == ENA_ETH_IO_L4_PROTO_TCP) {
+ /*
+ * Send to the stack if:
+ * - LRO not enabled, or
+ * - no LRO resources, or
+ * - lro enqueue fails
+ */
+ if (rx_ring->lro.lro_cnt != 0 &&
+ tcp_lro_rx(&rx_ring->lro, mbuf, 0) == 0)
+ do_if_input = 0;
+ }
+ if (do_if_input) {
+ ena_trace(ENA_DBG | ENA_RXPTH, "calling if_input() with mbuf %p",
+ mbuf);
+ (*ifp->if_input)(ifp, mbuf);
+ }
+
+ counter_u64_add(rx_ring->rx_stats.cnt, 1);
+ } while (--budget);
+
+ rx_ring->next_to_clean = next_to_clean;
+
+ refill_required = ena_com_sq_empty_space(io_sq);
+ refill_threshold = rx_ring->ring_size / ENA_RX_REFILL_THRESH_DEVIDER;
+
+ if (refill_required > refill_threshold) {
+ ena_com_update_dev_comp_head(rx_ring->ena_com_io_cq);
+ ena_refill_rx_bufs(rx_ring, refill_required);
+ }
+
+ tcp_lro_flush_all(&rx_ring->lro);
+
+ return (RX_BUDGET - budget);
+
+error:
+ counter_u64_add(rx_ring->rx_stats.bad_desc_num, 1);
+ return (RX_BUDGET - budget);
+}
+
+/*********************************************************************
+ *
+ * MSIX & Interrupt Service routine
+ *
+ **********************************************************************/
+
+/**
+ * ena_handle_msix - MSIX Interrupt Handler for admin/async queue
+ * @arg: interrupt number
+ **/
+static void
+ena_intr_msix_mgmnt(void *arg)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)arg;
+
+ ena_com_admin_q_comp_intr_handler(adapter->ena_dev);
+ if (likely(adapter->running))
+ ena_com_aenq_intr_handler(adapter->ena_dev, arg);
+}
+
+/**
+ * ena_handle_msix - MSIX Interrupt Handler for Tx/Rx
+ * @arg: interrupt number
+ **/
+static void
+ena_handle_msix(void *arg)
+{
+ struct ena_que *que = arg;
+ struct ena_adapter *adapter = que->adapter;
+ if_t ifp = adapter->ifp;
+ struct ena_ring *tx_ring;
+ struct ena_ring *rx_ring;
+ struct ena_com_io_cq* io_cq;
+ struct ena_eth_io_intr_reg intr_reg;
+ int qid, ena_qid;
+ int txc, rxc, i;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ ena_trace(ENA_DBG, "MSI-X TX/RX routine");
+
+ tx_ring = que->tx_ring;
+ rx_ring = que->rx_ring;
+ qid = que->id;
+ ena_qid = ENA_IO_TXQ_IDX(qid);
+ io_cq = &adapter->ena_dev->io_cq_queues[ena_qid];
+
+ for (i = 0; i < CLEAN_BUDGET; ++i) {
+ rxc = ena_rx_cleanup(rx_ring);
+
+ /* Protection from calling ena_tx_cleanup from ena_start_xmit */
+ ENA_RING_MTX_LOCK(tx_ring);
+ txc = ena_tx_cleanup(tx_ring);
+ ENA_RING_MTX_UNLOCK(tx_ring);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ if (txc != TX_BUDGET && rxc != RX_BUDGET)
+ break;
+ }
+
+ /* Signal that work is done and unmask interrupt */
+ ena_com_update_intr_reg(&intr_reg,
+ RX_IRQ_INTERVAL,
+ TX_IRQ_INTERVAL,
+ true);
+ ena_com_unmask_intr(io_cq, &intr_reg);
+}
+
+static int
+ena_enable_msix(struct ena_adapter *adapter)
+{
+ device_t dev = adapter->pdev;
+ int i, msix_vecs, rc = 0;
+
+ /* Reserved the max msix vectors we might need */
+ msix_vecs = ENA_MAX_MSIX_VEC(adapter->num_queues);
+
+ adapter->msix_entries = ENA_MEM_ALLOC(adapter->ena_dev->dmadev,
+ msix_vecs * sizeof(struct msix_entry));
+ if (!adapter->msix_entries) {
+ device_printf(dev,
+ "Failed to allocate msix_entries, vectors %d\n", msix_vecs);
+ rc = ENOMEM;
+ goto error;
+ }
+ device_printf(dev, "Allocated msix_entries, vectors (cnt: %d)\n",
+ msix_vecs);
+
+ for (i = 0; i < msix_vecs; i++) {
+ adapter->msix_entries[i].entry = i;
+ /* Vectors must start from 1 */
+ adapter->msix_entries[i].vector = i + 1;
+ }
+
+ rc = pci_alloc_msix(dev, &msix_vecs);
+ if (rc != 0) {
+ device_printf(dev,
+ "Failed to enable MSIX, vectors %d rc %d\n", msix_vecs, rc);
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, adapter->msix_entries);
+ adapter->msix_entries = NULL;
+ rc = ENOSPC;
+ goto error;
+ }
+
+ adapter->msix_vecs = msix_vecs;
+ adapter->msix_enabled = true;
+
+error:
+ return (rc);
+}
+
+static void
+ena_setup_mgmnt_intr(struct ena_adapter *adapter)
+{
+
+ snprintf(adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].name,
+ ENA_IRQNAME_SIZE, "ena-mgmnt@pci:%s",
+ device_get_nameunit(adapter->pdev));
+ /*
+ * Handler is NULL on purpose, it will be set
+ * when mgmnt interrupt is acquired
+ */
+ adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].handler = NULL;
+ adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].data = adapter;
+ adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].vector =
+ adapter->msix_entries[ENA_MGMNT_IRQ_IDX].vector;
+
+ return;
+}
+
+static void
+ena_setup_io_intr(struct ena_adapter *adapter)
+{
+ static int last_bind_cpu = -1;
+ int irq_idx;
+ ena_trace(ENA_DBG, "enter");
+
+ for (int i = 0; i < adapter->num_queues; i++) {
+ irq_idx = ENA_IO_IRQ_IDX(i);
+
+ snprintf(adapter->irq_tbl[irq_idx].name, ENA_IRQNAME_SIZE,
+ "%s-TxRx-%d", device_get_nameunit(adapter->pdev), i);
+ adapter->irq_tbl[irq_idx].handler = ena_handle_msix;
+ adapter->irq_tbl[irq_idx].data = &adapter->que[i];
+ adapter->irq_tbl[irq_idx].vector =
+ adapter->msix_entries[irq_idx].vector;
+ ena_trace(ENA_INFO | ENA_IOQ, "ena_setup_io_intr vector: %d\n",
+ adapter->msix_entries[irq_idx].vector);
+#ifdef RSS
+ adapter->que[i].cpu = adapter->irq_tbl[irq_idx].cpu =
+ rss_getcpu(i % rss_getnumbuckets());
+#else
+ /*
+ * We still want to bind rings to the corresponding cpu
+ * using something similar to the RSS round-robin technique.
+ */
+ if (last_bind_cpu < 0)
+ last_bind_cpu = CPU_FIRST();
+ adapter->que[i].cpu = adapter->irq_tbl[irq_idx].cpu =
+ last_bind_cpu;
+ last_bind_cpu = CPU_NEXT(last_bind_cpu);
+#endif
+ }
+
+ return;
+}
+
+static int
+ena_request_mgmnt_irq(struct ena_adapter *adapter)
+{
+ struct ena_irq *irq;
+ unsigned long flags;
+ int rc, rcc;
+
+ flags = RF_ACTIVE | RF_SHAREABLE;
+
+ irq = &adapter->irq_tbl[ENA_MGMNT_IRQ_IDX];
+ irq->res = bus_alloc_resource_any(adapter->pdev, SYS_RES_IRQ,
+ &irq->vector, flags);
+
+ if (irq->res == NULL) {
+ device_printf(adapter->pdev, "could not allocate "
+ "irq vector: %d\n", irq->vector);
+ rc = ENXIO;
+ goto exit_res;
+ }
+
+ if ((rc = bus_activate_resource(adapter->pdev, SYS_RES_IRQ, irq->vector,
+ irq->res)) != 0) {
+ device_printf(adapter->pdev, "could not activate "
+ "irq vector: %d\n", irq->vector);
+ goto exit_intr;
+ }
+
+ if ((rc = bus_setup_intr(adapter->pdev, irq->res,
+ INTR_TYPE_NET | INTR_MPSAFE, NULL,
+ ena_intr_msix_mgmnt, irq->data, &irq->cookie)) != 0) {
+ device_printf(adapter->pdev, "failed to register "
+ "interrupt handler for irq %ju: %d\n",
+ rman_get_start(irq->res), rc);
+ goto exit_intr;
+ }
+ irq->requested = true;
+
+ return (rc);
+
+exit_intr:
+ device_printf(adapter->pdev, "exit_intr: releasing resource"
+ " for irq %d\n", irq->vector);
+ rcc = bus_release_resource(adapter->pdev, SYS_RES_IRQ,
+ irq->vector, irq->res);
+ if (rcc)
+ device_printf(adapter->pdev, "dev has no parent while "
+ "releasing res for irq: %d\n", irq->vector);
+ irq->res = NULL;
+
+exit_res:
+ return (rc);
+}
+
+static int
+ena_request_io_irq(struct ena_adapter *adapter)
+{
+ struct ena_irq *irq;
+ unsigned long flags = 0;
+ int rc = 0, i, rcc;
+
+ if (!adapter->msix_enabled) {
+ device_printf(adapter->pdev, "failed to request irq\n");
+ return (EINVAL);
+ } else {
+ flags = RF_ACTIVE | RF_SHAREABLE;
+ }
+
+ for (i = ENA_IO_IRQ_FIRST_IDX; i < adapter->msix_vecs; i++) {
+ irq = &adapter->irq_tbl[i];
+
+ if (irq->requested)
+ continue;
+
+ irq->res = bus_alloc_resource_any(adapter->pdev, SYS_RES_IRQ,
+ &irq->vector, flags);
+ if (irq->res == NULL) {
+ device_printf(adapter->pdev, "could not allocate "
+ "irq vector: %d\n", irq->vector);
+ goto err;
+ }
+
+ if ((rc = bus_setup_intr(adapter->pdev, irq->res,
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, irq->handler,
+ irq->data, &irq->cookie)) != 0) {
+ device_printf(adapter->pdev, "failed to register "
+ "interrupt handler for irq %ju: %d\n",
+ rman_get_start(irq->res), rc);
+ goto err;
+ }
+ irq->requested = true;
+
+#ifdef RSS
+ device_printf(adapter->pdev, "queue %d - RSS bucket %d\n",
+ i - ENA_IO_IRQ_FIRST_IDX, irq->cpu);
+#else
+ device_printf(adapter->pdev, "queue %d - cpu %d\n",
+ i - ENA_IO_IRQ_FIRST_IDX, irq->cpu);
+#endif
+ }
+
+ return (rc);
+
+err:
+
+ for (; i >= ENA_IO_IRQ_FIRST_IDX; i--) {
+ irq = &adapter->irq_tbl[i];
+ rcc = 0;
+
+ /* Once we entered err: section and irq->requested is true we
+ free both intr and resources */
+ if (irq->requested == true)
+ rcc = bus_teardown_intr(adapter->pdev, irq->res, irq->cookie);
+ if (rcc)
+ device_printf(adapter->pdev, "could not release"
+ " irq: %d, error: %d\n", irq->vector, rcc);
+
+ /* If we entred err: section without irq->requested set we know
+ it was bus_alloc_resource_any() that needs cleanup, provided
+ res is not NULL. In case res is NULL no work in needed in
+ this iteration */
+ rcc = 0;
+ if (irq->res != NULL) {
+ rcc = bus_release_resource(adapter->pdev, SYS_RES_IRQ,
+ irq->vector, irq->res);
+ }
+ if (rcc)
+ device_printf(adapter->pdev, "dev has no parent while "
+ "releasing res for irq: %d\n", irq->vector);
+ irq->requested = false;
+ irq->res = NULL;
+ }
+
+ return (rc);
+}
+
+static void
+ena_free_mgmnt_irq(struct ena_adapter *adapter)
+{
+ struct ena_irq *irq;
+ int rc;
+
+ irq = &adapter->irq_tbl[ENA_MGMNT_IRQ_IDX];
+ if (irq->requested) {
+ ena_trace(ENA_INFO | ENA_ADMQ, "tear down irq: %d\n",
+ irq->vector);
+ rc = bus_teardown_intr(adapter->pdev, irq->res, irq->cookie);
+ if (rc)
+ device_printf(adapter->pdev, "failed to tear "
+ "down irq: %d\n", irq->vector);
+ irq->requested = 0;
+ }
+
+ if (irq->res != NULL) {
+ ena_trace(ENA_INFO | ENA_ADMQ, "release resource irq: %d\n",
+ irq->vector);
+ rc = bus_release_resource(adapter->pdev, SYS_RES_IRQ,
+ irq->vector, irq->res);
+ irq->res = NULL;
+ if (rc)
+ device_printf(adapter->pdev, "dev has no parent while "
+ "releasing res for irq: %d\n", irq->vector);
+ }
+
+ return;
+}
+
+static void
+ena_free_io_irq(struct ena_adapter *adapter)
+{
+ struct ena_irq *irq;
+ int rc;
+
+ for (int i = ENA_IO_IRQ_FIRST_IDX; i < adapter->msix_vecs; i++) {
+ irq = &adapter->irq_tbl[i];
+ if (irq->requested) {
+ ena_trace(ENA_INFO | ENA_IOQ, "tear down irq: %d\n",
+ irq->vector);
+ rc = bus_teardown_intr(adapter->pdev, irq->res,
+ irq->cookie);
+ if (rc) {
+ device_printf(adapter->pdev, "failed to tear "
+ "down irq: %d\n", irq->vector);
+ }
+ irq->requested = 0;
+ }
+
+ if (irq->res != NULL) {
+ ena_trace(ENA_INFO | ENA_IOQ, "release resource irq: %d\n",
+ irq->vector);
+ rc = bus_release_resource(adapter->pdev, SYS_RES_IRQ,
+ irq->vector, irq->res);
+ irq->res = NULL;
+ if (rc) {
+ device_printf(adapter->pdev, "dev has no parent"
+ " while releasing res for irq: %d\n",
+ irq->vector);
+ }
+ }
+ }
+
+ return;
+}
+
+static void
+ena_free_irqs(struct ena_adapter* adapter)
+{
+
+ ena_free_io_irq(adapter);
+ ena_free_mgmnt_irq(adapter);
+ ena_disable_msix(adapter);
+}
+
+static void
+ena_disable_msix(struct ena_adapter *adapter)
+{
+
+ pci_release_msi(adapter->pdev);
+
+ adapter->msix_vecs = 0;
+ ENA_MEM_FREE(adapter->ena_dev->dmadev, adapter->msix_entries);
+ adapter->msix_entries = NULL;
+}
+
+static void
+ena_unmask_all_io_irqs(struct ena_adapter *adapter)
+{
+ struct ena_com_io_cq* io_cq;
+ struct ena_eth_io_intr_reg intr_reg;
+ uint16_t ena_qid;
+ int i;
+
+ /* Unmask interrupts for all queues */
+ for (i = 0; i < adapter->num_queues; i++) {
+ ena_qid = ENA_IO_TXQ_IDX(i);
+ io_cq = &adapter->ena_dev->io_cq_queues[ena_qid];
+ ena_com_update_intr_reg(&intr_reg, 0, 0, true);
+ ena_com_unmask_intr(io_cq, &intr_reg);
+ }
+}
+
+/* Configure the Rx forwarding */
+static int ena_rss_configure(struct ena_adapter *adapter)
+{
+ struct ena_com_dev *ena_dev = adapter->ena_dev;
+ int rc;
+
+ /* Set indirect table */
+ rc = ena_com_indirect_table_set(ena_dev);
+ if (unlikely(rc && rc != EPERM))
+ return rc;
+
+ /* Configure hash function (if supported) */
+ rc = ena_com_set_hash_function(ena_dev);
+ if (unlikely(rc && (rc != EPERM)))
+ return rc;
+
+ /* Configure hash inputs (if supported) */
+ rc = ena_com_set_hash_ctrl(ena_dev);
+ if (unlikely(rc && (rc != EPERM)))
+ return rc;
+
+ return 0;
+}
+
+static int
+ena_up_complete(struct ena_adapter *adapter)
+{
+ int rc;
+
+ if (adapter->rss_support) {
+ rc = ena_rss_configure(adapter);
+ if (rc)
+ return (rc);
+ }
+
+ ena_change_mtu(adapter->ifp, adapter->ifp->if_mtu);
+ ena_refill_all_rx_bufs(adapter);
+ ena_unmask_all_io_irqs(adapter);
+
+ return (0);
+}
+
+static int
+ena_up(struct ena_adapter *adapter)
+{
+ int rc = 0;
+
+ if (!device_is_attached(adapter->pdev)) {
+ device_printf(adapter->pdev, "device is not attached!\n");
+ return (ENXIO);
+ }
+
+ if (!adapter->running) {
+ device_printf(adapter->pdev, "device is not running!\n");
+ return (ENXIO);
+ }
+
+ if (!adapter->up) {
+ device_printf(adapter->pdev, "device is going UP\n");
+
+ /* setup interrupts for IO queues */
+ ena_setup_io_intr(adapter);
+ rc = ena_request_io_irq(adapter);
+ if (rc) {
+ ena_trace(ENA_ALERT, "err_req_irq");
+ goto err_req_irq;
+ }
+
+ /* allocate transmit descriptors */
+ rc = ena_setup_all_tx_resources(adapter);
+ if (rc) {
+ ena_trace(ENA_ALERT, "err_setup_tx");
+ goto err_setup_tx;
+ }
+
+ /* allocate receive descriptors */
+ rc = ena_setup_all_rx_resources(adapter);
+ if (rc) {
+ ena_trace(ENA_ALERT, "err_setup_rx");
+ goto err_setup_rx;
+ }
+
+ /* create IO queues for Rx & Tx */
+ rc = ena_create_io_queues(adapter);
+ if (rc) {
+ ena_trace(ENA_ALERT,
+ "create IO queues failed");
+ goto err_io_que;
+ }
+
+ if (adapter->link_status)
+ if_link_state_change(adapter->ifp, LINK_STATE_UP);
+
+ rc = ena_up_complete(adapter);
+ if (rc)
+ goto err_up_complete;
+
+ counter_u64_add(adapter->dev_stats.interface_up, 1);
+
+ ena_update_hwassist(adapter);
+
+ if_setdrvflagbits(adapter->ifp, IFF_DRV_RUNNING,
+ IFF_DRV_OACTIVE);
+
+ callout_reset_sbt(&adapter->timer_service, SBT_1S, SBT_1S,
+ ena_timer_service, (void *)adapter, 0);
+
+ adapter->up = true;
+ }
+
+ return (0);
+
+err_up_complete:
+ ena_destroy_all_io_queues(adapter);
+err_io_que:
+ ena_free_all_rx_resources(adapter);
+err_setup_rx:
+ ena_free_all_tx_resources(adapter);
+err_setup_tx:
+ ena_free_io_irq(adapter);
+err_req_irq:
+ return (rc);
+}
+
+int
+ena_update_stats_counters(struct ena_adapter *adapter)
+{
+ struct ena_admin_basic_stats ena_stats;
+ struct ena_hw_stats *stats = &adapter->hw_stats;
+ int rc = 0;
+
+ if (!adapter->up)
+ return (rc);
+
+ rc = ena_com_get_dev_basic_stats(adapter->ena_dev, &ena_stats);
+ if (rc)
+ return (rc);
+
+ stats->tx_bytes = ((uint64_t)ena_stats.tx_bytes_high << 32) |
+ ena_stats.tx_bytes_low;
+ stats->rx_bytes = ((uint64_t)ena_stats.rx_bytes_high << 32) |
+ ena_stats.rx_bytes_low;
+
+ stats->rx_packets = ((uint64_t)ena_stats.rx_pkts_high << 32) |
+ ena_stats.rx_pkts_low;
+ stats->tx_packets = ((uint64_t)ena_stats.tx_pkts_high << 32) |
+ ena_stats.tx_pkts_low;
+
+ stats->rx_drops = ((uint64_t)ena_stats.rx_drops_high << 32) |
+ ena_stats.rx_drops_low;
+
+ return (0);
+}
+
+static uint64_t
+ena_get_counter(if_t ifp, ift_counter cnt)
+{
+ struct ena_adapter *adapter;
+ struct ena_hw_stats *stats;
+ int rc;
+
+ adapter = if_getsoftc(ifp);
+
+ /*
+ * Update only when asking for first counter and interface is up.
+ * Usually asks for all statistics in sequence.
+ */
+ if (adapter->up) {
+ if (cnt == 0) {
+ rc = ena_update_stats_counters(adapter);
+ if (rc) {
+ ena_trace(ENA_WARNING,
+ "Error updating stats counters, rc = %d",
+ rc);
+ }
+ }
+ }
+ stats = &adapter->hw_stats;
+
+ switch (cnt) {
+ case IFCOUNTER_IPACKETS:
+ return (stats->rx_packets);
+ case IFCOUNTER_OPACKETS:
+ return (stats->tx_packets);
+ case IFCOUNTER_IBYTES:
+ return (stats->rx_bytes);
+ case IFCOUNTER_OBYTES:
+ return (stats->tx_bytes);
+ case IFCOUNTER_IQDROPS:
+ return (stats->rx_drops);
+ default:
+ return (if_get_counter_default(ifp, cnt));
+ }
+}
+
+static int
+ena_media_change(if_t ifp)
+{
+ /* Media Change is not supported by firmware */
+ return (0);
+}
+
+static void
+ena_media_status(if_t ifp, struct ifmediareq *ifmr)
+{
+ struct ena_adapter *adapter = if_getsoftc(ifp);
+ ena_trace(ENA_DBG, "enter");
+
+ ENA_DEV_LOCK;
+
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+
+ if (!adapter->link_status) {
+ ENA_DEV_UNLOCK;
+ ena_trace(ENA_WARNING, "link_status = false");
+ return;
+ }
+
+ ifmr->ifm_status |= IFM_ACTIVE;
+ ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
+
+ ENA_DEV_UNLOCK;
+
+ return;
+}
+
+static void
+ena_init(void *arg)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)arg;
+
+ if (adapter->up == false)
+ ena_up(adapter);
+
+ return;
+}
+
+static int
+ena_ioctl(if_t ifp, u_long command, caddr_t data)
+{
+ struct ena_adapter *adapter;
+ struct ifreq *ifr;
+ int rc;
+
+ adapter = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+
+ /*
+ * Acquiring lock to prevent from running up and down routines parallel.
+ */
+ sx_xlock(&adapter->ioctl_sx);
+
+ rc = 0;
+ switch (command) {
+ case SIOCSIFMTU:
+ ena_down(adapter);
+
+ ena_change_mtu(ifp, ifr->ifr_mtu);
+
+ rc = ena_up(adapter);
+ break;
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ if (ifp->if_flags & (IFF_PROMISC |
+ IFF_ALLMULTI)) {
+ device_printf(adapter->pdev,
+ "ioctl promisc/allmulti\n");
+ }
+ } else {
+ rc = ena_up(adapter);
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ena_down(adapter);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ rc = ifmedia_ioctl(ifp, ifr, &adapter->media, command);
+ break;
+
+ case SIOCSIFCAP:
+ {
+ int reinit = 0;
+
+ if (ifr->ifr_reqcap != ifp->if_capenable) {
+ ifp->if_capenable = ifr->ifr_reqcap;
+ reinit = 1;
+ }
+
+ if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ ena_down(adapter);
+ rc = ena_up(adapter);
+ }
+ }
+
+ break;
+ default:
+ rc = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ sx_unlock(&adapter->ioctl_sx);
+
+ return (rc);
+}
+
+static int
+ena_get_dev_offloads(struct ena_com_dev_get_features_ctx *feat)
+{
+ int caps = 0;
+
+ if (feat->offload.tx &
+ (ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L4_IPV4_CSUM_FULL_MASK |
+ ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L4_IPV4_CSUM_PART_MASK |
+ ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L3_CSUM_IPV4_MASK))
+ caps |= IFCAP_TXCSUM;
+
+ if (feat->offload.tx &
+ (ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L4_IPV6_CSUM_FULL_MASK |
+ ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L4_IPV6_CSUM_PART_MASK))
+ caps |= IFCAP_TXCSUM_IPV6;
+
+ if (feat->offload.tx & ENA_ADMIN_FEATURE_OFFLOAD_DESC_TSO_IPV4_MASK)
+ caps |= IFCAP_TSO4;
+
+ if (feat->offload.tx & ENA_ADMIN_FEATURE_OFFLOAD_DESC_TSO_IPV6_MASK)
+ caps |= IFCAP_TSO6;
+
+ if (feat->offload.rx_supported &
+ (ENA_ADMIN_FEATURE_OFFLOAD_DESC_RX_L4_IPV4_CSUM_MASK |
+ ENA_ADMIN_FEATURE_OFFLOAD_DESC_RX_L3_CSUM_IPV4_MASK))
+ caps |= IFCAP_RXCSUM;
+
+ if (feat->offload.rx_supported &
+ ENA_ADMIN_FEATURE_OFFLOAD_DESC_RX_L4_IPV6_CSUM_MASK)
+ caps |= IFCAP_RXCSUM_IPV6;
+
+ caps |= IFCAP_LRO | IFCAP_JUMBO_MTU;
+
+ return (caps);
+}
+
+static void
+ena_update_host_info(struct ena_admin_host_info *host_info, if_t ifp)
+{
+
+ host_info->supported_network_features[0] =
+ (uint32_t)if_getcapabilities(ifp);
+}
+
+static void
+ena_update_hwassist(struct ena_adapter *adapter)
+{
+ if_t ifp = adapter->ifp;
+ uint32_t feat = adapter->tx_offload_cap;
+ int cap = if_getcapenable(ifp);
+ int flags = 0;
+
+ if_clearhwassist(ifp);
+
+ if (cap & IFCAP_TXCSUM) {
+ if (feat & ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L3_CSUM_IPV4_MASK)
+ flags |= CSUM_IP;
+ if (feat &
+ (ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L4_IPV4_CSUM_FULL_MASK |
+ ENA_ADMIN_FEATURE_OFFLOAD_DESC_TX_L4_IPV4_CSUM_PART_MASK))
+ flags |= CSUM_IP_UDP | CSUM_IP_TCP;
+ }
+
+ if (cap & IFCAP_TXCSUM_IPV6)
+ flags |= CSUM_IP6_UDP | CSUM_IP6_TCP;
+
+ if (cap & IFCAP_TSO4)
+ flags |= CSUM_IP_TSO;
+
+ if (cap & IFCAP_TSO6)
+ flags |= CSUM_IP6_TSO;
+
+ if_sethwassistbits(ifp, flags, 0);
+}
+
+static int
+ena_setup_ifnet(device_t pdev, struct ena_adapter *adapter,
+ struct ena_com_dev_get_features_ctx *feat)
+{
+ if_t ifp;
+ int caps = 0;
+
+ ena_trace(ENA_DBG, "enter");
+
+ ifp = adapter->ifp = if_gethandle(IFT_ETHER);
+ if (ifp == 0) {
+ device_printf(pdev, "can not allocate ifnet structure\n");
+ return (ENXIO);
+ }
+ if_initname(ifp, device_get_name(pdev), device_get_unit(pdev));
+ if_setdev(ifp, pdev);
+ if_setsoftc(ifp, adapter);
+
+ if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+ if_setinitfn(ifp, ena_init);
+ if_settransmitfn(ifp, ena_mq_start);
+ if_setqflushfn(ifp, ena_qflush);
+ if_setioctlfn(ifp, ena_ioctl);
+ if_setgetcounterfn(ifp, ena_get_counter);
+
+ if_setsendqlen(ifp, adapter->tx_ring_size);
+ if_setsendqready(ifp);
+ if_setmtu(ifp, ETHERMTU);
+ if_setbaudrate(ifp, 0);
+ /* Zeroize capabilities... */
+ if_setcapabilities(ifp, 0);
+ if_setcapenable(ifp, 0);
+ /* check hardware support */
+ caps = ena_get_dev_offloads(feat);
+ /* ... and set them */
+ if_setcapabilitiesbit(ifp, caps, 0);
+
+ /* TSO parameters */
+ ifp->if_hw_tsomax = ENA_TSO_MAXSIZE;
+ ifp->if_hw_tsomaxsegcount = ENA_TSO_NSEGS;
+ ifp->if_hw_tsomaxsegsize = MCLBYTES;
+
+ if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
+ if_setcapenable(ifp, if_getcapabilities(ifp));
+
+ /*
+ * Specify the media types supported by this adapter and register
+ * callbacks to update media and link information
+ */
+ ifmedia_init(&adapter->media, IFM_IMASK,
+ ena_media_change, ena_media_status);
+ ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO);
+
+ ether_ifattach(ifp, adapter->mac_addr);
+
+ return (0);
+}
+
+static void
+ena_down(struct ena_adapter *adapter)
+{
+
+ if (adapter->up) {
+ device_printf(adapter->pdev, "device is going DOWN\n");
+
+ callout_drain(&adapter->timer_service);
+
+ adapter->up = false;
+ if_setdrvflagbits(adapter->ifp, IFF_DRV_OACTIVE,
+ IFF_DRV_RUNNING);
+
+ ena_free_io_irq(adapter);
+
+ ena_destroy_all_io_queues(adapter);
+
+ ena_free_all_tx_bufs(adapter);
+ ena_free_all_rx_bufs(adapter);
+ ena_free_all_tx_resources(adapter);
+ ena_free_all_rx_resources(adapter);
+
+ counter_u64_add(adapter->dev_stats.interface_down, 1);
+ }
+
+ return;
+}
+
+static void
+ena_tx_csum(struct ena_com_tx_ctx *ena_tx_ctx, struct mbuf *mbuf)
+{
+ struct ena_com_tx_meta *ena_meta;
+ struct ether_vlan_header *eh;
+ u32 mss;
+ bool offload;
+ uint16_t etype;
+ int ehdrlen;
+ struct ip *ip;
+ int iphlen;
+ struct tcphdr *th;
+
+ offload = false;
+ ena_meta = &ena_tx_ctx->ena_meta;
+ mss = mbuf->m_pkthdr.tso_segsz;
+
+ if (mss != 0)
+ offload = true;
+
+ if ((mbuf->m_pkthdr.csum_flags & CSUM_TSO) != 0)
+ offload = true;
+
+ if ((mbuf->m_pkthdr.csum_flags & CSUM_OFFLOAD) != 0)
+ offload = true;
+
+ if (offload == false) {
+ ena_tx_ctx->meta_valid = 0;
+ return;
+ }
+
+ /* Determine where frame payload starts. */
+ eh = mtod(mbuf, struct ether_vlan_header *);
+ if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
+ etype = ntohs(eh->evl_proto);
+ ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+ } else {
+ etype = ntohs(eh->evl_encap_proto);
+ ehdrlen = ETHER_HDR_LEN;
+ }
+
+ ip = (struct ip *)(mbuf->m_data + ehdrlen);
+ iphlen = ip->ip_hl << 2;
+ th = (struct tcphdr *)((caddr_t)ip + iphlen);
+
+ if ((mbuf->m_pkthdr.csum_flags & CSUM_IP) != 0) {
+ ena_tx_ctx->l3_csum_enable = 1;
+ }
+ if ((mbuf->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
+ ena_tx_ctx->tso_enable = 1;
+ ena_meta->l4_hdr_len = (th->th_off);
+ }
+
+ switch (etype) {
+ case ETHERTYPE_IP:
+ ena_tx_ctx->l3_proto = ENA_ETH_IO_L3_PROTO_IPV4;
+ if (ip->ip_off == 0)
+ ena_tx_ctx->df = 1;
+ break;
+ case ETHERTYPE_IPV6:
+ ena_tx_ctx->l3_proto = ENA_ETH_IO_L3_PROTO_IPV6;
+
+ default:
+ break;
+ }
+
+ if (ip->ip_p == IPPROTO_TCP) {
+ ena_tx_ctx->l4_proto = ENA_ETH_IO_L4_PROTO_TCP;
+ if (mbuf->m_pkthdr.csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP))
+ ena_tx_ctx->l4_csum_enable = 1;
+ else
+ ena_tx_ctx->l4_csum_enable = 0;
+ } else if (ip->ip_p == IPPROTO_UDP) {
+ ena_tx_ctx->l4_proto = ENA_ETH_IO_L4_PROTO_UDP;
+ if (mbuf->m_pkthdr.csum_flags & (CSUM_IP_UDP | CSUM_IP6_UDP))
+ ena_tx_ctx->l4_csum_enable = 1;
+ else
+ ena_tx_ctx->l4_csum_enable = 0;
+ } else {
+ ena_tx_ctx->l4_proto = ENA_ETH_IO_L4_PROTO_UNKNOWN;
+ ena_tx_ctx->l4_csum_enable = 0;
+ }
+
+ ena_meta->mss = mss;
+ ena_meta->l3_hdr_len = iphlen;
+ ena_meta->l3_hdr_offset = ehdrlen;
+ ena_tx_ctx->meta_valid = 1;
+}
+
+static int
+ena_xmit_mbuf(struct ena_ring *tx_ring, struct mbuf *mbuf)
+{
+ struct ena_adapter *adapter;
+ struct ena_tx_buffer *tx_info;
+ struct ena_com_tx_ctx ena_tx_ctx;
+ struct ena_com_dev *ena_dev;
+ struct ena_com_buf *ena_buf;
+ struct ena_com_io_sq* io_sq;
+ bus_dma_segment_t segs[ENA_BUS_DMA_SEGS];
+ void *push_hdr;
+ uint16_t next_to_use;
+ uint16_t req_id;
+ uint16_t push_len;
+ uint16_t ena_qid;
+ uint32_t len, nsegs, header_len;
+ int i, rc;
+ int nb_hw_desc, num_frags;
+
+ ena_qid = ENA_IO_TXQ_IDX(tx_ring->que->id);
+ adapter = tx_ring->que->adapter;
+ ena_dev = adapter->ena_dev;
+ io_sq = &adapter->ena_dev->io_sq_queues[ena_qid];
+
+ ENA_ASSERT(mbuf, "mbuf is NULL\n");
+
+ num_frags = ena_mbuf_count(mbuf);
+ if (num_frags > (adapter->max_tx_sgl_size - 2)) {
+ device_printf(adapter->pdev,
+ "too many fragments. Last fragment: %d!\n", num_frags);
+ return (ENA_COM_INVAL);
+ }
+
+ next_to_use = tx_ring->next_to_use;
+ req_id = tx_ring->free_tx_ids[next_to_use];
+ tx_info = &tx_ring->tx_buffer_info[req_id];
+
+ tx_info->mbuf = mbuf;
+ tx_info->num_of_bufs = 0;
+
+ ena_buf = tx_info->bufs;
+ len = mbuf->m_len;
+
+ ena_trace(ENA_DBG | ENA_TXPTH, "Tx: %d bytes", mbuf->m_pkthdr.len);
+
+ push_len = 0;
+ header_len = min_t(uint32_t, len, tx_ring->tx_max_header_size);
+ push_hdr = NULL;
+
+ rc = bus_dmamap_load_mbuf_sg(adapter->tx_buf_tag, tx_info->map,
+ mbuf, segs, &nsegs, BUS_DMA_NOWAIT);
+
+ if (rc || (nsegs == 0)) {
+ ena_trace(ENA_WARNING,
+ "dmamap load failed! err: %d nsegs: %d", rc, nsegs);
+ counter_u64_add(tx_ring->tx_stats.dma_mapping_err, 1);
+ tx_info->mbuf = NULL;
+ if (rc == ENOMEM)
+ return (ENA_COM_NO_MEM);
+ else
+ return (ENA_COM_INVAL);
+ }
+
+ for (i = 0; i < nsegs; i++) {
+ ena_buf->len = segs[i].ds_len;
+ ena_buf->paddr = segs[i].ds_addr;
+ ena_buf++;
+ }
+ tx_info->num_of_bufs = nsegs;
+
+ memset(&ena_tx_ctx, 0x0, sizeof(struct ena_com_tx_ctx));
+ ena_tx_ctx.ena_bufs = tx_info->bufs;
+ ena_tx_ctx.push_header = push_hdr;
+ ena_tx_ctx.num_bufs = tx_info->num_of_bufs;
+ ena_tx_ctx.req_id = req_id;
+ ena_tx_ctx.header_len = header_len;
+
+ /* Set flags and meta data */
+ ena_tx_csum(&ena_tx_ctx, mbuf);
+ /* Prepare the packet's descriptors and send them to device */
+ rc = ena_com_prepare_tx(io_sq, &ena_tx_ctx, &nb_hw_desc);
+ if (rc != 0) {
+ ena_trace(ENA_WARNING, "failed to prepare tx bufs\n");
+ counter_enter();
+ counter_u64_add_protected(tx_ring->tx_stats.queue_stop, 1);
+ counter_u64_add_protected(tx_ring->tx_stats.prepare_ctx_err, 1);
+ counter_exit();
+ goto dma_error;
+ }
+
+ counter_enter();
+ counter_u64_add_protected(tx_ring->tx_stats.cnt, 1);
+ counter_u64_add_protected(tx_ring->tx_stats.bytes, mbuf->m_pkthdr.len);
+ counter_exit();
+
+ tx_info->tx_descs = nb_hw_desc;
+ getbinuptime(&tx_info->timestamp);
+ tx_info->print_once = true;
+
+ tx_ring->next_to_use = ENA_TX_RING_IDX_NEXT(next_to_use,
+ tx_ring->ring_size);
+
+ bus_dmamap_sync(adapter->tx_buf_tag, tx_info->map, BUS_DMASYNC_PREWRITE);
+
+ return (0);
+
+dma_error:
+ tx_info->mbuf = NULL;
+ bus_dmamap_unload(adapter->tx_buf_tag, tx_info->map);
+
+ return (rc);
+}
+
+static void
+ena_start_xmit(struct ena_ring *tx_ring)
+{
+ struct mbuf *mbuf;
+ struct ena_adapter *adapter = tx_ring->adapter;
+ struct ena_com_io_sq* io_sq;
+ int ena_qid;
+ int acum_pkts = 0;
+ int ret = 0;
+
+ if ((adapter->ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ if (!adapter->link_status)
+ return;
+
+ ena_qid = ENA_IO_TXQ_IDX(tx_ring->que->id);
+ io_sq = &adapter->ena_dev->io_sq_queues[ena_qid];
+
+ while ((mbuf = drbr_peek(adapter->ifp, tx_ring->br)) != NULL) {
+ ena_trace(ENA_DBG | ENA_TXPTH, "\ndequeued mbuf %p with flags %#x and"
+ " header csum flags %#jx",
+ mbuf, mbuf->m_flags, mbuf->m_pkthdr.csum_flags);
+
+ if (ena_com_sq_empty_space(io_sq) < ENA_TX_CLEANUP_TRESHOLD)
+ ena_tx_cleanup(tx_ring);
+
+ if ((ret = ena_xmit_mbuf(tx_ring, mbuf)) != 0) {
+ if (ret == ENA_COM_NO_MEM) {
+ drbr_putback(adapter->ifp, tx_ring->br, mbuf);
+ } else if (ret == ENA_COM_NO_SPACE) {
+ drbr_putback(adapter->ifp, tx_ring->br, mbuf);
+ } else {
+ m_freem(mbuf);
+ drbr_advance(adapter->ifp, tx_ring->br);
+ }
+
+ break;
+ }
+
+ if ((adapter->ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ drbr_advance(adapter->ifp, tx_ring->br);
+ acum_pkts++;
+
+ BPF_MTAP(adapter->ifp, mbuf);
+
+ if (acum_pkts == DB_THRESHOLD) {
+ acum_pkts = 0;
+ wmb();
+ /* Trigger the dma engine */
+ ena_com_write_sq_doorbell(io_sq);
+ counter_u64_add(tx_ring->tx_stats.doorbells, 1);
+ }
+
+ }
+
+ if (acum_pkts) {
+ wmb();
+ /* Trigger the dma engine */
+ ena_com_write_sq_doorbell(io_sq);
+ counter_u64_add(tx_ring->tx_stats.doorbells, 1);
+ }
+
+ if (ena_com_sq_empty_space(io_sq) < ENA_TX_CLEANUP_TRESHOLD)
+ ena_tx_cleanup(tx_ring);
+}
+
+static void
+ena_deferred_mq_start(void *arg, int pending)
+{
+ struct ena_ring *tx_ring = (struct ena_ring *)arg;
+ struct ifnet *ifp = tx_ring->adapter->ifp;
+
+ while (drbr_empty(ifp, tx_ring->br) == FALSE &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ ENA_RING_MTX_LOCK(tx_ring);
+ ena_start_xmit(tx_ring);
+ ENA_RING_MTX_UNLOCK(tx_ring);
+ }
+}
+
+static int
+ena_mq_start(if_t ifp, struct mbuf *m)
+{
+ struct ena_adapter *adapter = ifp->if_softc;
+ struct ena_ring *tx_ring;
+ int ret, is_drbr_empty;
+ uint32_t i;
+
+ if ((adapter->ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return (ENODEV);
+
+ /* Which queue to use */
+ /*
+ * If everything is setup correctly, it should be the
+ * same bucket that the current CPU we're on is.
+ * It should improve performance.
+ */
+ if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
+#ifdef RSS
+ if (rss_hash2bucket(m->m_pkthdr.flowid,
+ M_HASHTYPE_GET(m), &i) == 0) {
+ i = i % adapter->num_queues;
+
+ } else
+#endif
+ {
+ i = m->m_pkthdr.flowid % adapter->num_queues;
+ }
+ } else {
+ i = curcpu % adapter->num_queues;
+ }
+ tx_ring = &adapter->tx_ring[i];
+
+ /* Check if drbr is empty before putting packet */
+ is_drbr_empty = drbr_empty(ifp, tx_ring->br);
+ ret = drbr_enqueue(ifp, tx_ring->br, m);
+ if (ret) {
+ taskqueue_enqueue(tx_ring->enqueue_tq, &tx_ring->enqueue_task);
+ return (ret);
+ }
+
+ if (is_drbr_empty && ENA_RING_MTX_TRYLOCK(tx_ring)) {
+ ena_start_xmit(tx_ring);
+ ENA_RING_MTX_UNLOCK(tx_ring);
+ } else {
+ taskqueue_enqueue(tx_ring->enqueue_tq, &tx_ring->enqueue_task);
+ }
+
+ return (0);
+}
+
+static void
+ena_qflush(if_t ifp)
+{
+ struct ena_adapter *adapter = ifp->if_softc;
+ struct ena_ring *tx_ring = adapter->tx_ring;
+ int i;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ for(i = 0; i < adapter->num_queues; ++i, ++tx_ring)
+ if (drbr_empty(ifp, tx_ring->br) == FALSE) {
+ ENA_RING_MTX_LOCK(tx_ring);
+ drbr_flush(ifp, tx_ring->br);
+ ENA_RING_MTX_UNLOCK(tx_ring);
+ }
+
+ if_qflush(ifp);
+
+ return;
+}
+
+static int ena_calc_io_queue_num(struct ena_adapter *adapter,
+ struct ena_com_dev_get_features_ctx *get_feat_ctx)
+{
+ int io_sq_num, io_cq_num, io_queue_num;
+
+ io_sq_num = get_feat_ctx->max_queues.max_sq_num;
+ io_cq_num = get_feat_ctx->max_queues.max_sq_num;
+
+ io_queue_num = min_t(int, mp_ncpus, ENA_MAX_NUM_IO_QUEUES);
+ io_queue_num = min_t(int, io_queue_num, io_sq_num);
+ io_queue_num = min_t(int, io_queue_num, io_cq_num);
+ /* 1 IRQ for for mgmnt and 1 IRQ for each TX/RX pair */
+ io_queue_num = min_t(int, io_queue_num,
+ pci_msix_count(adapter->pdev) - 1);
+#ifdef RSS
+ io_queue_num = min_t(int, io_queue_num, rss_getnumbuckets());
+#endif
+
+ return io_queue_num;
+}
+
+static int ena_calc_queue_size(struct ena_adapter *adapter,
+ uint16_t *max_tx_sgl_size, uint16_t *max_rx_sgl_size,
+ struct ena_com_dev_get_features_ctx *feat)
+{
+ uint32_t queue_size = ENA_DEFAULT_RING_SIZE;
+ uint32_t v;
+ uint32_t q;
+
+ queue_size = min_t(uint32_t, queue_size,
+ feat->max_queues.max_cq_depth);
+ queue_size = min_t(uint32_t, queue_size,
+ feat->max_queues.max_sq_depth);
+
+ /* round down to the nearest power of 2 */
+ v = queue_size;
+ while (v != 0) {
+ if (powerof2(queue_size))
+ break;
+ v /= 2;
+ q = rounddown2(queue_size, v);
+ if (q != 0) {
+ queue_size = q;
+ break;
+ }
+ }
+
+ if (unlikely(!queue_size)) {
+ device_printf(adapter->pdev, "Invalid queue size\n");
+ return ENA_COM_FAULT;
+ }
+
+ *max_tx_sgl_size = min_t(uint16_t, ENA_PKT_MAX_BUFS,
+ feat->max_queues.max_packet_tx_descs);
+ *max_rx_sgl_size = min_t(uint16_t, ENA_PKT_MAX_BUFS,
+ feat->max_queues.max_packet_rx_descs);
+
+ return queue_size;
+}
+
+static int ena_rss_init_default(struct ena_adapter *adapter)
+{
+ struct ena_com_dev *ena_dev = adapter->ena_dev;
+ device_t dev = adapter->pdev;
+ int qid, rc, i;
+
+ rc = ena_com_rss_init(ena_dev, ENA_RX_RSS_TABLE_LOG_SIZE);
+ if (unlikely(rc)) {
+ device_printf(dev, "Cannot init RSS\n");
+ goto err_rss_init;
+ }
+
+ for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; i++) {
+#ifdef RSS
+ qid = rss_get_indirection_to_bucket(i);
+ qid = qid % adapter->num_queues;
+#else
+ qid = i % adapter->num_queues;
+#endif
+ rc = ena_com_indirect_table_fill_entry(ena_dev, i,
+ ENA_IO_RXQ_IDX(qid));
+ if (unlikely(rc && (rc != EPERM))) {
+ device_printf(dev, "Cannot fill indirect table\n");
+ goto err_fill_indir;
+ }
+ }
+
+ rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_CRC32, NULL,
+ ENA_HASH_KEY_SIZE, 0xFFFFFFFF);
+ if (unlikely(rc && (rc != EPERM))) {
+ device_printf(dev, "Cannot fill hash function\n");
+ goto err_fill_indir;
+ }
+
+ rc = ena_com_set_default_hash_ctrl(ena_dev);
+ if (unlikely(rc && (rc != EPERM))) {
+ device_printf(dev, "Cannot fill hash control\n");
+ goto err_fill_indir;
+ }
+
+ return (0);
+
+err_fill_indir:
+ ena_com_rss_destroy(ena_dev);
+err_rss_init:
+ return (rc);
+}
+
+static void
+ena_rss_init_default_deferred(void *arg)
+{
+ struct ena_adapter *adapter;
+ devclass_t dc;
+ int max;
+ int rc;
+
+ dc = devclass_find("ena");
+ if (dc == NULL) {
+ ena_trace(ENA_DBG, "No devclass ena\n");
+ return;
+ }
+
+ max = devclass_get_maxunit(dc);
+ while (max-- >= 0) {
+ adapter = devclass_get_softc(dc, max);
+ if (adapter != NULL) {
+ rc = ena_rss_init_default(adapter);
+ adapter->rss_support = true;
+ if (rc) {
+ device_printf(adapter->pdev,
+ "WARNING: RSS was not properly initialized,"
+ " it will affect bandwith\n");
+ adapter->rss_support = false;
+ }
+ }
+ }
+}
+SYSINIT(ena_rss_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_SECOND, ena_rss_init_default_deferred, NULL);
+
+static void ena_config_host_info(struct ena_com_dev *ena_dev)
+{
+ struct ena_admin_host_info *host_info;
+ int rc;
+
+ /* Allocate only the host info */
+ rc = ena_com_allocate_host_info(ena_dev);
+ if (rc) {
+ ena_trace(ENA_ALERT, "Cannot allocate host info\n");
+ return;
+ }
+
+ host_info = ena_dev->host_attr.host_info;
+
+ host_info->os_type = ENA_ADMIN_OS_FREEBSD;
+ host_info->kernel_ver = osreldate;
+
+ sprintf(host_info->kernel_ver_str, "%d", osreldate);
+ host_info->os_dist = 0;
+ strncpy(host_info->os_dist_str, osrelease,
+ sizeof(host_info->os_dist_str) - 1);
+
+ host_info->driver_version =
+ (DRV_MODULE_VER_MAJOR) |
+ (DRV_MODULE_VER_MINOR << ENA_ADMIN_HOST_INFO_MINOR_SHIFT) |
+ (DRV_MODULE_VER_SUBMINOR << ENA_ADMIN_HOST_INFO_SUB_MINOR_SHIFT);
+
+ rc = ena_com_set_host_attributes(ena_dev);
+ if (rc) {
+ if (rc == EPERM)
+ ena_trace(ENA_WARNING, "Cannot set host attributes\n");
+ else
+ ena_trace(ENA_ALERT, "Cannot set host attributes\n");
+
+ goto err;
+ }
+
+ return;
+
+err:
+ ena_com_delete_host_info(ena_dev);
+}
+
+static int
+ena_device_init(struct ena_adapter *adapter, device_t pdev,
+ struct ena_com_dev_get_features_ctx *get_feat_ctx, int *wd_active)
+{
+ struct ena_com_dev* ena_dev = adapter->ena_dev;
+ bool readless_supported;
+ uint32_t aenq_groups;
+ int dma_width;
+ int rc;
+
+ rc = ena_com_mmio_reg_read_request_init(ena_dev);
+ if (rc) {
+ device_printf(pdev, "failed to init mmio read less\n");
+ return rc;
+ }
+
+ /*
+ * The PCIe configuration space revision id indicate if mmio reg
+ * read is disabled
+ */
+ readless_supported = !(pci_get_revid(pdev) & ENA_MMIO_DISABLE_REG_READ);
+ ena_com_set_mmio_read_mode(ena_dev, readless_supported);
+
+ rc = ena_com_dev_reset(ena_dev);
+ if (rc) {
+ device_printf(pdev, "Can not reset device\n");
+ goto err_mmio_read_less;
+ }
+
+ rc = ena_com_validate_version(ena_dev);
+ if (rc) {
+ device_printf(pdev, "device version is too low\n");
+ goto err_mmio_read_less;
+ }
+
+ dma_width = ena_com_get_dma_width(ena_dev);
+ if (dma_width < 0) {
+ device_printf(pdev, "Invalid dma width value %d", dma_width);
+ rc = dma_width;
+ goto err_mmio_read_less;
+ }
+ adapter->dma_width = dma_width;
+
+ /* ENA admin level init */
+ rc = ena_com_admin_init(ena_dev, &aenq_handlers, true);
+ if (rc) {
+ device_printf(pdev,
+ "Can not initialize ena admin queue with device\n");
+ goto err_mmio_read_less;
+ }
+
+ /*
+ * To enable the msix interrupts the driver needs to know the number
+ * of queues. So the driver uses polling mode to retrieve this
+ * information
+ */
+ ena_com_set_admin_polling_mode(ena_dev, true);
+
+ ena_config_host_info(ena_dev);
+
+ /* Get Device Attributes */
+ rc = ena_com_get_dev_attr_feat(ena_dev, get_feat_ctx);
+ if (rc) {
+ device_printf(pdev,
+ "Cannot get attribute for ena device rc: %d\n", rc);
+ goto err_admin_init;
+ }
+
+ aenq_groups = BIT(ENA_ADMIN_LINK_CHANGE) |
+ BIT(ENA_ADMIN_FATAL_ERROR) |
+ BIT(ENA_ADMIN_WARNING) |
+ BIT(ENA_ADMIN_NOTIFICATION) |
+ BIT(ENA_ADMIN_KEEP_ALIVE);
+
+ aenq_groups &= get_feat_ctx->aenq.supported_groups;
+ rc = ena_com_set_aenq_config(ena_dev, aenq_groups);
+ if (rc) {
+ device_printf(pdev, "Cannot configure aenq groups rc: %d\n", rc);
+ goto err_admin_init;
+ }
+
+ *wd_active = !!(aenq_groups & BIT(ENA_ADMIN_KEEP_ALIVE));
+
+ return 0;
+
+err_admin_init:
+ ena_com_delete_host_info(ena_dev);
+ ena_com_admin_destroy(ena_dev);
+err_mmio_read_less:
+ ena_com_mmio_reg_read_request_destroy(ena_dev);
+
+ return rc;
+}
+
+static int ena_enable_msix_and_set_admin_interrupts(struct ena_adapter *adapter,
+ int io_vectors)
+{
+ struct ena_com_dev *ena_dev = adapter->ena_dev;
+ int rc;
+
+ rc = ena_enable_msix(adapter);
+ if (rc) {
+ device_printf(adapter->pdev, "Error with MSI-X enablement\n");
+ return rc;
+ }
+
+ ena_setup_mgmnt_intr(adapter);
+
+ rc = ena_request_mgmnt_irq(adapter);
+ if (rc) {
+ device_printf(adapter->pdev, "Cannot setup mgmnt queue intr\n");
+ goto err_disable_msix;
+ }
+
+ ena_com_set_admin_polling_mode(ena_dev, false);
+
+ ena_com_admin_aenq_enable(ena_dev);
+
+ return 0;
+
+err_disable_msix:
+ ena_disable_msix(adapter);
+
+ return rc;
+}
+
+/* Function called on ENA_ADMIN_KEEP_ALIVE event */
+static void ena_keep_alive_wd(void *adapter_data,
+ struct ena_admin_aenq_entry *aenq_e)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)adapter_data;
+ sbintime_t stime;
+
+ stime = getsbinuptime();
+ atomic_store_rel_64(&adapter->keep_alive_timestamp, stime);
+}
+
+/* Check for keep alive expiration */
+static void check_for_missing_keep_alive(struct ena_adapter *adapter)
+{
+ sbintime_t timestamp, time;
+
+ if (adapter->wd_active == 0)
+ return;
+
+ if (adapter->keep_alive_timeout == 0)
+ return;
+
+ timestamp = atomic_load_acq_64(&adapter->keep_alive_timestamp);
+ time = getsbinuptime() - timestamp;
+ if (unlikely(time > adapter->keep_alive_timeout)) {
+ device_printf(adapter->pdev,
+ "Keep alive watchdog timeout.\n");
+ counter_u64_add(adapter->dev_stats.wd_expired, 1);
+ adapter->trigger_reset = true;
+ }
+}
+
+/* Check if admin queue is enabled */
+static void check_for_admin_com_state(struct ena_adapter *adapter)
+{
+ if (unlikely(!ena_com_get_admin_running_state(adapter->ena_dev))) {
+ device_printf(adapter->pdev,
+ "ENA admin queue is not in running state!\n");
+ counter_u64_add(adapter->dev_stats.admin_q_pause, 1);
+ adapter->trigger_reset = true;
+ }
+}
+
+/*
+ * Check for TX which were not completed on time.
+ * Timeout is defined by "missing_tx_timeout".
+ * Reset will be performed if number of incompleted
+ * transactions exceeds "missing_tx_threshold".
+ */
+static void check_for_missing_tx_completions(struct ena_adapter *adapter)
+{
+ struct ena_ring *tx_ring;
+ struct ena_tx_buffer *tx_info;
+ struct bintime curtime, time;
+ int i, j, budget, missed_tx;
+
+ /* Make sure the driver doesn't turn the device in other process */
+ rmb();
+
+ if (!adapter->up)
+ return;
+
+ if (adapter->trigger_reset)
+ return;
+
+ if (adapter->missing_tx_timeout == 0)
+ return;
+
+ budget = adapter->missing_tx_max_queues;
+ getbinuptime(&curtime);
+
+ for (i = adapter->next_monitored_tx_qid; i < adapter->num_queues; i++) {
+ tx_ring = &adapter->tx_ring[i];
+
+ missed_tx = 0;
+
+ for (j = 0; j < tx_ring->ring_size; j++) {
+ tx_info = &tx_ring->tx_buffer_info[j];
+
+ if (!bintime_isset(&tx_info->timestamp))
+ continue;
+
+ time = curtime;
+ bintime_sub(&time, &tx_info->timestamp);
+
+ /* Check again if packet is still waiting */
+ if (bintime_isset(&tx_info->timestamp) && unlikely(
+ bttosbt(time) > adapter->missing_tx_timeout)) {
+ if (tx_info->print_once)
+ device_printf(adapter->pdev,
+ "Found a Tx that wasn't completed "
+ "on time, qid %d, index %d.\n",
+ tx_ring->qid, j);
+
+ tx_info->print_once = false;
+ missed_tx++;
+
+ if (unlikely(missed_tx >
+ adapter->missing_tx_threshold)) {
+ device_printf(adapter->pdev,
+ "The number of lost tx completion "
+ "is above the threshold (%d > %d). "
+ "Reset the device\n", missed_tx,
+ adapter->missing_tx_threshold);
+ adapter->trigger_reset = true;
+ return;
+ }
+ }
+ }
+
+ budget--;
+ if (!budget) {
+ i++;
+ break;
+ }
+ }
+
+ adapter->next_monitored_tx_qid = i % adapter->num_queues;
+}
+
+
+static void
+ena_timer_service(void *data)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)data;
+ struct ena_admin_host_info *host_info =
+ adapter->ena_dev->host_attr.host_info;
+
+ check_for_missing_keep_alive(adapter);
+
+ check_for_admin_com_state(adapter);
+
+ check_for_missing_tx_completions(adapter);
+
+ if (host_info)
+ ena_update_host_info(host_info, adapter->ifp);
+
+ if (unlikely(adapter->trigger_reset)) {
+ device_printf(adapter->pdev, "Trigger reset is on\n");
+ taskqueue_enqueue(adapter->reset_tq, &adapter->reset_task);
+ return;
+ }
+
+ /*
+ * Schedule another timeout one second from now.
+ */
+ callout_schedule_sbt(&adapter->timer_service, SBT_1S, SBT_1S, 0);
+}
+
+static void
+ena_reset_task(void *arg, int pending)
+{
+ struct ena_com_dev_get_features_ctx get_feat_ctx;
+ struct ena_adapter *adapter = (struct ena_adapter *)arg;
+ struct ena_com_dev *ena_dev = adapter->ena_dev;
+ bool dev_up;
+ int rc;
+
+ if (unlikely(!adapter->trigger_reset)) {
+ device_printf(adapter->pdev,
+ "device reset scheduled but trigger_reset is off\n");
+ return;
+ }
+
+ sx_xlock(&adapter->ioctl_sx);
+
+ callout_drain(&adapter->timer_service);
+
+ dev_up = adapter->up;
+
+ ena_com_set_admin_running_state(ena_dev, false);
+ ena_free_mgmnt_irq(adapter);
+ ena_down(adapter);
+ ena_com_dev_reset(ena_dev);
+ ena_disable_msix(adapter);
+ ena_com_abort_admin_commands(ena_dev);
+ ena_com_wait_for_abort_completion(ena_dev);
+ ena_com_admin_destroy(ena_dev);
+ ena_com_mmio_reg_read_request_destroy(ena_dev);
+
+ adapter->trigger_reset = false;
+
+ /* Finished destroy part. Restart the device */
+ rc = ena_device_init(adapter, adapter->pdev, &get_feat_ctx,
+ &adapter->wd_active);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "ENA device init failed! (err: %d)\n", rc);
+ goto err_dev_free;
+ }
+
+ rc = ena_enable_msix_and_set_admin_interrupts(adapter,
+ adapter->num_queues);
+ if (rc) {
+ device_printf(adapter->pdev, "Enable MSI-X failed\n");
+ goto err_com_free;
+ }
+
+ /* If the interface was up before the reset bring it up */
+ if (dev_up) {
+ rc = ena_up(adapter);
+ if (rc) {
+ device_printf(adapter->pdev,
+ "Failed to create I/O queues\n");
+ goto err_msix_free;
+ }
+ }
+
+ callout_reset_sbt(&adapter->timer_service, SBT_1S, SBT_1S,
+ ena_timer_service, (void *)adapter, 0);
+
+ sx_unlock(&adapter->ioctl_sx);
+
+ return;
+
+err_msix_free:
+ ena_com_dev_reset(ena_dev);
+ ena_free_mgmnt_irq(adapter);
+ ena_disable_msix(adapter);
+err_com_free:
+ ena_com_admin_destroy(ena_dev);
+err_dev_free:
+ device_printf(adapter->pdev, "ENA reset failed!\n");
+ adapter->running = false;
+ sx_unlock(&adapter->ioctl_sx);
+}
+
+/**
+ * ena_attach - Device Initialization Routine
+ * @pdev: device information struct
+ *
+ * Returns 0 on success, otherwise on failure.
+ *
+ * ena_attach initializes an adapter identified by a device structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ **/
+static int
+ena_attach(device_t pdev)
+{
+ struct ena_com_dev_get_features_ctx get_feat_ctx;
+ static int version_printed;
+ struct ena_adapter *adapter;
+ struct ena_com_dev *ena_dev = NULL;
+ uint16_t tx_sgl_size = 0;
+ uint16_t rx_sgl_size = 0;
+ int io_queue_num;
+ int queue_size;
+ int rc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *children;
+
+ adapter = device_get_softc(pdev);
+ adapter->pdev = pdev;
+ ctx = device_get_sysctl_ctx(pdev);
+ children = SYSCTL_CHILDREN(device_get_sysctl_tree(pdev));
+
+ mtx_init(&adapter->global_mtx, "ENA global mtx", NULL, MTX_DEF);
+ sx_init(&adapter->ioctl_sx, "ENA ioctl sx");
+
+ /* Sysctl calls for Watchdog service */
+ SYSCTL_ADD_INT(ctx, children, OID_AUTO, "wd_active",
+ CTLFLAG_RWTUN, &adapter->wd_active, 0,
+ "Watchdog is active");
+
+ SYSCTL_ADD_QUAD(ctx, children, OID_AUTO, "keep_alive_timeout",
+ CTLFLAG_RWTUN, &adapter->keep_alive_timeout,
+ "Timeout for Keep Alive messages");
+
+ SYSCTL_ADD_QUAD(ctx, children, OID_AUTO, "missing_tx_timeout",
+ CTLFLAG_RWTUN, &adapter->missing_tx_timeout,
+ "Timeout for TX completion");
+
+ SYSCTL_ADD_U32(ctx, children, OID_AUTO, "missing_tx_max_queues",
+ CTLFLAG_RWTUN, &adapter->missing_tx_max_queues, 0,
+ "Number of TX queues to check per run");
+
+ SYSCTL_ADD_U32(ctx, children, OID_AUTO, "missing_tx_threshold",
+ CTLFLAG_RWTUN, &adapter->missing_tx_threshold, 0,
+ "Max number of timeouted packets");
+
+ /* Set up the timer service */
+ callout_init_mtx(&adapter->timer_service, &adapter->global_mtx, 0);
+ adapter->keep_alive_timeout = DEFAULT_KEEP_ALIVE_TO;
+ adapter->missing_tx_timeout = DEFAULT_TX_CMP_TO;
+ adapter->missing_tx_max_queues = DEFAULT_TX_MONITORED_QUEUES;
+ adapter->missing_tx_threshold = DEFAULT_TX_CMP_THRESHOLD;
+
+ if (version_printed++ == 0)
+ device_printf(pdev, "%s\n", ena_version);
+
+ rc = ena_allocate_pci_resources(adapter);
+ if (rc) {
+ device_printf(pdev, "PCI resource allocation failed!\n");
+ ena_free_pci_resources(adapter);
+ goto err_pci_res;
+ }
+
+ /* Allocate memory for ena_dev structure */
+ ena_dev = ENA_MEM_ALLOC(pdev, sizeof(struct ena_com_dev));
+ if (!ena_dev) {
+ device_printf(pdev, "allocating ena_dev failed\n");
+ rc = ENOMEM;
+ goto err_select_region;
+ }
+
+ adapter->ena_dev = ena_dev;
+ ena_dev->dmadev = pdev;
+ ena_dev->bus = malloc(sizeof(struct ena_bus), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+
+ /* Store register resources */
+ ((struct ena_bus*)(ena_dev->bus))->reg_bar_t =
+ rman_get_bustag(adapter->registers);
+ ((struct ena_bus*)(ena_dev->bus))->reg_bar_h =
+ rman_get_bushandle(adapter->registers);
+
+ if (((struct ena_bus*)(ena_dev->bus))->reg_bar_h == 0) {
+ device_printf(pdev, "failed to pmap registers bar\n");
+ rc = ENXIO;
+ goto err_dev_free;
+ }
+
+ ena_dev->tx_mem_queue_type = ENA_ADMIN_PLACEMENT_POLICY_HOST;
+
+ /* Device initialization */
+ rc = ena_device_init(adapter, pdev, &get_feat_ctx, &adapter->wd_active);
+ if (rc) {
+ device_printf(pdev, "ENA device init failed! (err: %d)\n", rc);
+ rc = ENXIO;
+ goto err_bus_free;
+ }
+
+ adapter->keep_alive_timestamp = getsbinuptime();
+
+ adapter->tx_offload_cap = get_feat_ctx.offload.tx;
+
+ /* Set for sure that interface is not up */
+ adapter->up = false;
+
+ memcpy(adapter->mac_addr, get_feat_ctx.dev_attr.mac_addr,
+ ETHER_ADDR_LEN);
+
+ adapter->small_copy_len =
+ ENA_DEFAULT_SMALL_PACKET_LEN;
+
+ /* calculate IO queue number to create */
+ io_queue_num = ena_calc_io_queue_num(adapter, &get_feat_ctx);
+
+ ENA_ASSERT(io_queue_num > 0, "Invalid queue number: %d\n",
+ io_queue_num);
+ adapter->num_queues = io_queue_num;
+
+ /* calculatre ring sizes */
+ queue_size = ena_calc_queue_size(adapter,&tx_sgl_size,
+ &rx_sgl_size, &get_feat_ctx);
+ if ((queue_size <= 0) || (io_queue_num <= 0)) {
+ rc = ENA_COM_FAULT;
+ goto err_com_free;
+ }
+
+ adapter->tx_ring_size = queue_size;
+ adapter->rx_ring_size = queue_size;
+
+ adapter->max_tx_sgl_size = tx_sgl_size;
+ adapter->max_rx_sgl_size = rx_sgl_size;
+
+ /* set up dma tags for rx and tx buffers */
+ rc = ena_setup_tx_dma_tag(adapter);
+ if (rc)
+ goto dma_tx_err;
+
+ rc = ena_setup_rx_dma_tag(adapter);
+ if (rc)
+ goto dma_rx_err;
+
+ /* initialize rings basic information */
+ device_printf(pdev, "initalize %d io queues\n", io_queue_num);
+ rc = ena_init_io_rings(adapter);
+ if (rc) {
+ device_printf(pdev,"Error with initialization of IO rings\n");
+ goto err_io_init;
+ }
+
+ /* setup network interface */
+ rc = ena_setup_ifnet(pdev, adapter, &get_feat_ctx);
+ if (rc) {
+ device_printf(pdev,"Error with network interface setup\n");
+ goto err_com_free;
+ }
+
+ rc = ena_enable_msix_and_set_admin_interrupts(adapter, io_queue_num);
+ if (rc) {
+ device_printf(pdev,
+ "Failed to enable and set the admin interrupts\n");
+ goto err_ifp_free;
+ }
+
+ /* Initialize statistics */
+ ena_alloc_counters((counter_u64_t *)&adapter->dev_stats,
+ sizeof(struct ena_stats_dev));
+ ena_update_stats_counters(adapter);
+ ena_sysctl_add_nodes(adapter);
+
+ /* Tell the stack that the interface is not active */
+ if_setdrvflagbits(adapter->ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
+
+ /* Initialize reset task queue */
+ TASK_INIT(&adapter->reset_task, 0, ena_reset_task, adapter);
+ adapter->reset_tq = taskqueue_create("ena_reset_enqueue",
+ M_WAITOK | M_ZERO, taskqueue_thread_enqueue, &adapter->reset_tq);
+ taskqueue_start_threads(&adapter->reset_tq, 1, PI_NET,
+ "%s rstq", device_get_nameunit(adapter->pdev));
+
+ adapter->running = true;
+ return (0);
+
+err_ifp_free:
+ if_detach(adapter->ifp);
+ if_free(adapter->ifp);
+err_com_free:
+ ena_free_all_io_rings_resources(adapter);
+err_io_init:
+ ena_free_rx_dma_tag(adapter);
+dma_rx_err:
+ ena_free_tx_dma_tag(adapter);
+dma_tx_err:
+ ena_com_admin_destroy(ena_dev);
+ ena_com_delete_host_info(ena_dev);
+err_bus_free:
+ free(ena_dev->bus, M_DEVBUF);
+err_dev_free:
+ free(ena_dev, M_DEVBUF);
+err_select_region:
+ ena_free_pci_resources(adapter);
+err_pci_res:
+ return (rc);
+}
+
+/**
+ * ena_detach - Device Removal Routine
+ * @pdev: device information struct
+ *
+ * ena_detach is called by the device subsystem to alert the driver
+ * that it should release a PCI device.
+ **/
+static int
+ena_detach(device_t pdev)
+{
+ struct ena_adapter *adapter = device_get_softc(pdev);
+ struct ena_com_dev *ena_dev = adapter->ena_dev;
+ int rc;
+
+ /* Make sure VLANS are not using driver */
+ if (adapter->ifp->if_vlantrunk != NULL) {
+ device_printf(adapter->pdev ,"VLAN is in use, detach first\n");
+ return (EBUSY);
+ }
+
+ /* Free reset task and callout */
+ callout_drain(&adapter->timer_service);
+ while (taskqueue_cancel(adapter->reset_tq, &adapter->reset_task, NULL))
+ taskqueue_drain(adapter->reset_tq, &adapter->reset_task);
+ taskqueue_free(adapter->reset_tq);
+
+ ena_down(adapter);
+
+ if (adapter->ifp != NULL) {
+ ether_ifdetach(adapter->ifp);
+ if_free(adapter->ifp);
+ }
+
+ ena_free_all_io_rings_resources(adapter);
+
+ ena_free_counters((counter_u64_t *)&adapter->dev_stats,
+ sizeof(struct ena_stats_dev));
+
+ if (adapter->rss_support)
+ ena_com_rss_destroy(ena_dev);
+
+ rc = ena_free_rx_dma_tag(adapter);
+ if (rc)
+ device_printf(adapter->pdev,
+ "Unmapped RX DMA tag associations\n");
+
+ rc = ena_free_tx_dma_tag(adapter);
+ if (rc)
+ device_printf(adapter->pdev,
+ "Unmapped TX DMA tag associations\n");
+
+ /* Reset the device only if the device is running. */
+ if (adapter->running)
+ ena_com_dev_reset(ena_dev);
+
+ ena_com_delete_host_info(ena_dev);
+
+ ena_com_admin_destroy(ena_dev);
+
+ ena_free_irqs(adapter);
+
+ ena_com_mmio_reg_read_request_destroy(ena_dev);
+
+ ena_free_pci_resources(adapter);
+
+ mtx_destroy(&adapter->global_mtx);
+ sx_destroy(&adapter->ioctl_sx);
+
+ if (ena_dev->bus != NULL)
+ free(ena_dev->bus, M_DEVBUF);
+
+ if (ena_dev != NULL)
+ free(ena_dev, M_DEVBUF);
+
+ return (bus_generic_detach(pdev));
+}
+
+/******************************************************************************
+ ******************************** AENQ Handlers *******************************
+ *****************************************************************************/
+/**
+ * ena_update_on_link_change:
+ * Notify the network interface about the change in link status
+ **/
+static void
+ena_update_on_link_change(void *adapter_data,
+ struct ena_admin_aenq_entry *aenq_e)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)adapter_data;
+ struct ena_admin_aenq_link_change_desc *aenq_desc;
+ int status;
+ if_t ifp;
+
+ aenq_desc = (struct ena_admin_aenq_link_change_desc *)aenq_e;
+ ifp = adapter->ifp;
+ status = aenq_desc->flags &
+ ENA_ADMIN_AENQ_LINK_CHANGE_DESC_LINK_STATUS_MASK;
+
+ if (status != 0) {
+ device_printf(adapter->pdev, "link is UP\n");
+ if_link_state_change(ifp, LINK_STATE_UP);
+ } else if (status == 0) {
+ device_printf(adapter->pdev, "link is DOWN\n");
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ } else {
+ device_printf(adapter->pdev, "invalid value recvd\n");
+ BUG();
+ }
+
+ adapter->link_status = status;
+
+ return;
+}
+
+/**
+ * This handler will called for unknown event group or unimplemented handlers
+ **/
+static void
+unimplemented_aenq_handler(void *data,
+ struct ena_admin_aenq_entry *aenq_e)
+{
+ return;
+}
+
+static struct ena_aenq_handlers aenq_handlers = {
+ .handlers = {
+ [ENA_ADMIN_LINK_CHANGE] = ena_update_on_link_change,
+ [ENA_ADMIN_KEEP_ALIVE] = ena_keep_alive_wd,
+ },
+ .unimplemented_handler = unimplemented_aenq_handler
+};
+
+/*********************************************************************
+ * FreeBSD Device Interface Entry Points
+ *********************************************************************/
+
+static device_method_t ena_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ena_probe),
+ DEVMETHOD(device_attach, ena_attach),
+ DEVMETHOD(device_detach, ena_detach),
+ DEVMETHOD_END
+};
+
+static driver_t ena_driver = {
+ "ena", ena_methods, sizeof(struct ena_adapter),
+};
+
+devclass_t ena_devclass;
+DRIVER_MODULE(ena, pci, ena_driver, ena_devclass, 0, 0);
+MODULE_DEPEND(ena, pci, 1, 1, 1);
+MODULE_DEPEND(ena, ether, 1, 1, 1);
+
+/*********************************************************************/
Index: sys/dev/ena/ena_sysctl.h
===================================================================
--- /dev/null
+++ sys/dev/ena/ena_sysctl.h
@@ -0,0 +1,44 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) 2015-2017 Amazon.com, Inc. or its affiliates.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ENA_SYSCTL_H
+#define ENA_SYSCTL_H
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include "ena.h"
+
+void ena_sysctl_add_nodes(struct ena_adapter *);
+
+#endif /* !(ENA_SYSCTL_H) */
Index: sys/dev/ena/ena_sysctl.c
===================================================================
--- /dev/null
+++ sys/dev/ena/ena_sysctl.c
@@ -0,0 +1,244 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) 2015-2017 Amazon.com, Inc. or its affiliates.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ena_sysctl.h"
+
+static int ena_sysctl_update_stats(SYSCTL_HANDLER_ARGS);
+static void ena_sysctl_add_stats(struct ena_adapter *);
+
+void
+ena_sysctl_add_nodes(struct ena_adapter *adapter)
+{
+ ena_sysctl_add_stats(adapter);
+}
+
+static void
+ena_sysctl_add_stats(struct ena_adapter *adapter)
+{
+ device_t dev;
+
+ struct ena_ring *tx_ring;
+ struct ena_ring *rx_ring;
+
+ struct ena_hw_stats *hw_stats;
+ struct ena_stats_dev *dev_stats;
+ struct ena_stats_tx *tx_stats;
+ struct ena_stats_rx *rx_stats;
+ struct ena_com_stats_admin *admin_stats;
+
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ struct sysctl_oid_list *child;
+
+ struct sysctl_oid *queue_node, *tx_node, *rx_node, *hw_node;
+ struct sysctl_oid *admin_node;
+ struct sysctl_oid_list *queue_list, *tx_list, *rx_list, *hw_list;
+ struct sysctl_oid_list *admin_list;
+
+#define QUEUE_NAME_LEN 32
+ char namebuf[QUEUE_NAME_LEN];
+ int i;
+
+ dev = adapter->pdev;
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+ child = SYSCTL_CHILDREN(tree);
+
+ tx_ring = adapter->tx_ring;
+ rx_ring = adapter->rx_ring;
+
+ hw_stats = &adapter->hw_stats;
+ dev_stats = &adapter->dev_stats;
+ admin_stats = &adapter->ena_dev->admin_queue.stats;
+
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "tx_timeout",
+ CTLFLAG_RD, &dev_stats->tx_timeout,
+ "Driver TX timeouts");
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "io_suspend",
+ CTLFLAG_RD, &dev_stats->io_suspend,
+ "IO queue suspends");
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "io_resume",
+ CTLFLAG_RD, &dev_stats->io_resume,
+ "IO queue resumes");
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "wd_expired",
+ CTLFLAG_RD, &dev_stats->wd_expired,
+ "Watchdog expiry count");
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "interface_up",
+ CTLFLAG_RD, &dev_stats->interface_up,
+ "Network interface up count");
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "interface_down",
+ CTLFLAG_RD, &dev_stats->interface_down,
+ "Network interface down count");
+ SYSCTL_ADD_COUNTER_U64(ctx, child, OID_AUTO, "admin_q_pause",
+ CTLFLAG_RD, &dev_stats->admin_q_pause,
+ "Admin queue pauses");
+
+ for (i = 0; i < adapter->num_queues; ++i, ++tx_ring, ++rx_ring) {
+ snprintf(namebuf, QUEUE_NAME_LEN, "queue%d", i);
+
+ queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
+ namebuf, CTLFLAG_RD, NULL, "Queue Name");
+ queue_list = SYSCTL_CHILDREN(queue_node);
+
+ /* TX specific stats */
+ tx_node = SYSCTL_ADD_NODE(ctx, queue_list, OID_AUTO,
+ "tx_ring", CTLFLAG_RD, NULL, "TX ring");
+ tx_list = SYSCTL_CHILDREN(tx_node);
+
+ tx_stats = &tx_ring->tx_stats;
+
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "count", CTLFLAG_RD,
+ &tx_stats->cnt, "Packets sent");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "bytes", CTLFLAG_RD,
+ &tx_stats->bytes, "Bytes sent");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "prepare_ctx_err", CTLFLAG_RD,
+ &tx_stats->prepare_ctx_err,
+ "TX buffer preparation failures");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "queue_wakeup", CTLFLAG_RD,
+ &tx_stats->queue_wakeup, "Queue wakeups");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "dma_mapping_err", CTLFLAG_RD,
+ &tx_stats->dma_mapping_err, "DMA mapping failures");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "unsupported_desc_num", CTLFLAG_RD,
+ &tx_stats->unsupported_desc_num,
+ "Excessive descriptor packet discards");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "napi_comp", CTLFLAG_RD,
+ &tx_stats->napi_comp, "Napi completions");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "tx_poll", CTLFLAG_RD,
+ &tx_stats->tx_poll, "TX poll count");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "doorbells", CTLFLAG_RD,
+ &tx_stats->doorbells, "Queue doorbells");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "missing_tx_comp", CTLFLAG_RD,
+ &tx_stats->missing_tx_comp, "TX completions missed");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "bad_req_id", CTLFLAG_RD,
+ &tx_stats->bad_req_id, "Bad request id count");
+ SYSCTL_ADD_COUNTER_U64(ctx, tx_list, OID_AUTO,
+ "stops", CTLFLAG_RD,
+ &tx_stats->queue_stop, "Queue stops");
+
+ /* RX specific stats */
+ rx_node = SYSCTL_ADD_NODE(ctx, queue_list, OID_AUTO,
+ "rx_ring", CTLFLAG_RD, NULL, "RX ring");
+ rx_list = SYSCTL_CHILDREN(rx_node);
+
+ rx_stats = &rx_ring->rx_stats;
+
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "count", CTLFLAG_RD,
+ &rx_stats->cnt, "Packets received");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "bytes", CTLFLAG_RD,
+ &rx_stats->bytes, "Bytes received");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "refil_partial", CTLFLAG_RD,
+ &rx_stats->refil_partial, "Partial refilled mbufs");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "bad_csum", CTLFLAG_RD,
+ &rx_stats->bad_csum, "Bad RX checksum");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "page_alloc_fail", CTLFLAG_RD,
+ &rx_stats->page_alloc_fail, "Failed page allocs");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "mbuf_alloc_fail", CTLFLAG_RD,
+ &rx_stats->mbuf_alloc_fail, "Failed mbuf allocs");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "dma_mapping_err", CTLFLAG_RD,
+ &rx_stats->dma_mapping_err, "DMA mapping errors");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "bad_desc_num", CTLFLAG_RD,
+ &rx_stats->bad_desc_num, "Bad descriptor count");
+ SYSCTL_ADD_COUNTER_U64(ctx, rx_list, OID_AUTO,
+ "small_copy_len_pkt", CTLFLAG_RD,
+ &rx_stats->small_copy_len_pkt, "Small copy packet count");
+ }
+
+ /* Stats read from device */
+ hw_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "hw_stats",
+ CTLFLAG_RD, NULL, "Statistics from hardware");
+ hw_list = SYSCTL_CHILDREN(hw_node);
+
+ SYSCTL_ADD_U64(ctx, hw_list, OID_AUTO, "rx_packets", CTLFLAG_RD,
+ &hw_stats->rx_packets, 0, "Packets received");
+ SYSCTL_ADD_U64(ctx, hw_list, OID_AUTO, "tx_packets", CTLFLAG_RD,
+ &hw_stats->tx_packets, 0, "Packets transmitted");
+ SYSCTL_ADD_U64(ctx, hw_list, OID_AUTO, "rx_bytes", CTLFLAG_RD,
+ &hw_stats->rx_bytes, 0, "Bytes received");
+ SYSCTL_ADD_U64(ctx, hw_list, OID_AUTO, "tx_bytes", CTLFLAG_RD,
+ &hw_stats->tx_bytes, 0, "Bytes transmitted");
+ SYSCTL_ADD_U64(ctx, hw_list, OID_AUTO, "rx_drops", CTLFLAG_RD,
+ &hw_stats->rx_drops, 0, "Receive packet drops");
+
+ SYSCTL_ADD_PROC(ctx, hw_list, OID_AUTO, "update_stats",
+ CTLTYPE_INT|CTLFLAG_RD, adapter, 0, ena_sysctl_update_stats,
+ "A", "Update stats from hardware");
+ /* ENA Admin queue stats */
+ admin_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "admin_stats",
+ CTLFLAG_RD, NULL, "ENA Admin Queue statistics");
+ admin_list = SYSCTL_CHILDREN(admin_node);
+
+ SYSCTL_ADD_U32(ctx, admin_list, OID_AUTO, "aborted_cmd", CTLFLAG_RD,
+ &admin_stats->aborted_cmd, 0, "Aborted commands");
+ SYSCTL_ADD_U32(ctx, admin_list, OID_AUTO, "sumbitted_cmd", CTLFLAG_RD,
+ &admin_stats->submitted_cmd, 0, "Submitted commands");
+ SYSCTL_ADD_U32(ctx, admin_list, OID_AUTO, "completed_cmd", CTLFLAG_RD,
+ &admin_stats->completed_cmd, 0, "Completed commands");
+ SYSCTL_ADD_U32(ctx, admin_list, OID_AUTO, "out_of_space", CTLFLAG_RD,
+ &admin_stats->out_of_space, 0, "Queue out of space");
+ SYSCTL_ADD_U32(ctx, admin_list, OID_AUTO, "no_completion", CTLFLAG_RD,
+ &admin_stats->no_completion, 0, "Commands not completed");
+}
+
+static int
+ena_sysctl_update_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)arg1;
+ int rc;
+
+ if (adapter->up)
+ ena_update_stats_counters(adapter);
+
+ rc = sysctl_handle_string(oidp, "", 1, req);
+ return (rc);
+}
+
Index: sys/modules/Makefile
===================================================================
--- sys/modules/Makefile
+++ sys/modules/Makefile
@@ -107,6 +107,7 @@
${_efirt} \
${_elink} \
${_em} \
+ ${_ena} \
${_ep} \
${_epic} \
esp \
@@ -564,6 +565,7 @@
_drm2= drm2
_ed= ed
_em= em
+_ena= ena
_ep= ep
_et= et
_exca= exca
Index: sys/modules/ena/Makefile
===================================================================
--- /dev/null
+++ sys/modules/ena/Makefile
@@ -0,0 +1,42 @@
+#
+# BSD LICENSE
+#
+# Copyright (c) 2015-2017 Amazon.com, Inc. or its affiliates.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# # Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# # Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# # Neither the name of copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+.PATH: ${SRCTOP}/sys/dev/ena \
+ ${SRCTOP}/sys/contrib/ena-com
+
+KMOD = if_ena
+SRCS = ena.c ena_com.c ena_eth_com.c ena_sysctl.c
+SRCS += device_if.h bus_if.h pci_if.h
+CFLAGS += -I${SRCTOP}/sys/contrib
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 1, 7:50 PM (6 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30687032
Default Alt Text
D10427.id27815.diff (124 KB)

Event Timeline