Author: sbruno
Date: Tue Jan 10 03:23:22 2017
New Revision: 311849
URL: https://svnweb.freebsd.org/changeset/base/311849

Log:
  Migrate e1000 to the IFLIB framework:
  - em(4) igb(4) and lem(4)
  - deprecate the igb device from kernel configurations
  - create a symbolic link in /boot/kernel from if_em.ko to if_igb.ko
  
  Devices tested:
  - 82574L
  - I218-LM
  - 82546GB
  - 82579LM
  - I350
  - I217
  
  Please report problems to freebsd-...@freebsd.org
  
  Partial review from jhb and suggestions on how to *not* brick folks who
  originally would have lost their igbX device.
  
  Submitted by: mm...@nextbsd.org
  MFC after:    2 weeks
  Relnotes:     yes
  Sponsored by: Limelight Networks and Dell EMC Isilon
  Differential Revision:        https://reviews.freebsd.org/D8299

Added:
  head/sys/dev/e1000/em_txrx.c   (contents, props changed)
  head/sys/dev/e1000/igb_txrx.c   (contents, props changed)
Deleted:
  head/sys/dev/e1000/if_igb.c
  head/sys/dev/e1000/if_igb.h
  head/sys/dev/e1000/if_lem.c
  head/sys/dev/e1000/if_lem.h
  head/sys/modules/igb/
Modified:
  head/UPDATING
  head/sys/amd64/conf/GENERIC
  head/sys/arm64/conf/GENERIC
  head/sys/conf/NOTES
  head/sys/conf/files
  head/sys/conf/makeLINT.mk
  head/sys/dev/e1000/if_em.c
  head/sys/dev/e1000/if_em.h
  head/sys/i386/conf/GENERIC
  head/sys/mips/conf/OCTEON1
  head/sys/modules/Makefile
  head/sys/modules/em/Makefile
  head/sys/powerpc/conf/GENERIC64

Modified: head/UPDATING
==============================================================================
--- head/UPDATING       Tue Jan 10 01:36:50 2017        (r311848)
+++ head/UPDATING       Tue Jan 10 03:23:22 2017        (r311849)
@@ -51,6 +51,11 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 12
 
 ****************************** SPECIAL WARNING: ******************************
 
+20170109:
+       The igb(4), em(4) and lem(4) ethernet drivers are now implemented via
+       IFLIB.  If you have a custom kernel configuration that excludes em(4)
+       but you use igb(4), you need to re-add em(4) to your custom 
configuration.
+
 20161217:
        Clang, llvm, lldb, compiler-rt and libc++ have been upgraded to 3.9.1.
        Please see the 20141231 entry below for information about prerequisites

Modified: head/sys/amd64/conf/GENERIC
==============================================================================
--- head/sys/amd64/conf/GENERIC Tue Jan 10 01:36:50 2017        (r311848)
+++ head/sys/amd64/conf/GENERIC Tue Jan 10 03:23:22 2017        (r311849)
@@ -230,7 +230,6 @@ device              puc                     # Multi I/O 
cards and mult
 device         bxe                     # Broadcom NetXtreme II 
BCM5771X/BCM578XX 10GbE
 device         de                      # DEC/Intel DC21x4x (``Tulip'')
 device         em                      # Intel PRO/1000 Gigabit Ethernet Family
-device         igb                     # Intel PRO/1000 PCIE Server Gigabit 
Family
 device         ix                      # Intel PRO/10GbE PCIE PF Ethernet
 device         ixv                     # Intel PRO/10GbE PCIE VF Ethernet
 device         ixl                     # Intel XL710 40Gbe PCIE Ethernet

Modified: head/sys/arm64/conf/GENERIC
==============================================================================
--- head/sys/arm64/conf/GENERIC Tue Jan 10 01:36:50 2017        (r311848)
+++ head/sys/arm64/conf/GENERIC Tue Jan 10 03:23:22 2017        (r311849)
@@ -120,7 +120,6 @@ device              mii
 device         miibus          # MII bus support
 device         awg             # Allwinner EMAC Gigabit Ethernet
 device         em              # Intel PRO/1000 Gigabit Ethernet Family
-device         igb             # Intel PRO/1000 PCIE Server Gigabit Family
 device         ix              # Intel 10Gb Ethernet Family
 device         msk             # Marvell/SysKonnect Yukon II Gigabit Ethernet
 device         smc             # SMSC LAN91C111

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES Tue Jan 10 01:36:50 2017        (r311848)
+++ head/sys/conf/NOTES Tue Jan 10 03:23:22 2017        (r311849)
@@ -1972,7 +1972,6 @@ device            xmphy           # XaQti XMAC II
 #       KNE110TX.
 # de:   Digital Equipment DC21040
 # em:   Intel Pro/1000 Gigabit Ethernet 82542, 82543, 82544 based adapters.
-# igb:  Intel Pro/1000 PCI Express Gigabit Ethernet: 82575 and later adapters.
 # ep:   3Com 3C509, 3C529, 3C556, 3C562D, 3C563D, 3C572, 3C574X, 3C579, 3C589
 #       and PC Card devices using these chipsets.
 # ex:   Intel EtherExpress Pro/10 and other i82595-based adapters,
@@ -2145,7 +2144,6 @@ device            cxgbe           # Chelsio T4-T6 
1/10/25/4
 device         cxgbev          # Chelsio T4-T6 Virtual Functions
 device         de              # DEC/Intel DC21x4x (``Tulip'')
 device         em              # Intel Pro/1000 Gigabit Ethernet
-device         igb             # Intel Pro/1000 PCIE Gigabit Ethernet
 device         ixgb            # Intel Pro/10Gbe PCI-X Ethernet
 device         ix              # Intel Pro/10Gbe PCIE Ethernet
 device         ixv             # Intel Pro/10Gbe PCIE Ethernet VF

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Tue Jan 10 01:36:50 2017        (r311848)
+++ head/sys/conf/files Tue Jan 10 03:23:22 2017        (r311849)
@@ -1572,43 +1572,43 @@ dev/eisa/eisa_if.m              standard
 dev/eisa/eisaconf.c            optional eisa
 dev/e1000/if_em.c              optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/if_lem.c             optional em \
+dev/e1000/em_txrx.c            optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/if_igb.c             optional igb \
+dev/e1000/igb_txrx.c           optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_80003es2lan.c  optional em | igb \
+dev/e1000/e1000_80003es2lan.c  optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_82540.c                optional em | igb \
+dev/e1000/e1000_82540.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_82541.c                optional em | igb \
+dev/e1000/e1000_82541.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_82542.c                optional em | igb \
+dev/e1000/e1000_82542.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_82543.c                optional em | igb \
+dev/e1000/e1000_82543.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_82571.c                optional em | igb \
+dev/e1000/e1000_82571.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_82575.c                optional em | igb \
+dev/e1000/e1000_82575.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_ich8lan.c      optional em | igb \
+dev/e1000/e1000_ich8lan.c      optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_i210.c         optional em | igb \
+dev/e1000/e1000_i210.c         optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_api.c          optional em | igb \
+dev/e1000/e1000_api.c          optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_mac.c          optional em | igb \
+dev/e1000/e1000_mac.c          optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_manage.c       optional em | igb \
+dev/e1000/e1000_manage.c       optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_nvm.c          optional em | igb \
+dev/e1000/e1000_nvm.c          optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_phy.c          optional em | igb \
+dev/e1000/e1000_phy.c          optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_vf.c           optional em | igb \
+dev/e1000/e1000_vf.c           optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_mbx.c          optional em | igb \
+dev/e1000/e1000_mbx.c          optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
-dev/e1000/e1000_osdep.c                optional em | igb \
+dev/e1000/e1000_osdep.c                optional em \
        compile-with "${NORMAL_C} -I$S/dev/e1000"
 dev/et/if_et.c                 optional et
 dev/en/if_en_pci.c             optional en pci

Modified: head/sys/conf/makeLINT.mk
==============================================================================
--- head/sys/conf/makeLINT.mk   Tue Jan 10 01:36:50 2017        (r311848)
+++ head/sys/conf/makeLINT.mk   Tue Jan 10 03:23:22 2017        (r311849)
@@ -38,7 +38,6 @@ LINT: ${NOTES} ../../conf/makeLINT.sed
        echo "nodevice bxe"             >> ${.TARGET}-NOIP
        echo "nodevice em"              >> ${.TARGET}-NOIP
        echo "nodevice fxp"             >> ${.TARGET}-NOIP
-       echo "nodevice igb"             >> ${.TARGET}-NOIP
        echo "nodevice jme"             >> ${.TARGET}-NOIP
        echo "nodevice msk"             >> ${.TARGET}-NOIP
        echo "nodevice mxge"            >> ${.TARGET}-NOIP

Added: head/sys/dev/e1000/em_txrx.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/e1000/em_txrx.c        Tue Jan 10 03:23:22 2017        
(r311849)
@@ -0,0 +1,720 @@
+/* $FreeBSD$ */
+#include "if_em.h"
+
+#ifdef RSS
+#include <net/rss_config.h>
+#include <netinet/in_rss.h>
+#endif
+
+#ifdef VERBOSE_DEBUG
+#define DPRINTF device_printf
+#else
+#define DPRINTF(...)
+#endif
+
+/*********************************************************************
+ *  Local Function prototypes
+ *********************************************************************/
+static int em_tso_setup(struct adapter *adapter, if_pkt_info_t pi, u32 
*txd_upper, u32 *txd_lower);
+static int em_transmit_checksum_setup(struct adapter *adapter, if_pkt_info_t 
pi, u32 *txd_upper, u32 *txd_lower);
+static int em_isc_txd_encap(void *arg, if_pkt_info_t pi);
+static void em_isc_txd_flush(void *arg, uint16_t txqid, uint32_t pidx);
+static int em_isc_txd_credits_update(void *arg, uint16_t txqid, uint32_t 
cidx_init, bool clear);
+static void em_isc_rxd_refill(void *arg, uint16_t rxqid, uint8_t flid __unused,
+                             uint32_t pidx, uint64_t *paddrs, caddr_t *vaddrs 
__unused, uint16_t count, uint16_t buflen __unused);
+static void em_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused, 
uint32_t pidx);
+static int em_isc_rxd_available(void *arg, uint16_t rxqid, uint32_t idx,
+                               int budget);
+static int em_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri);
+
+static void lem_isc_rxd_refill(void *arg, uint16_t rxqid, uint8_t flid 
__unused,
+                             uint32_t pidx, uint64_t *paddrs, caddr_t *vaddrs 
__unused, uint16_t count, uint16_t buflen __unused);
+
+static int lem_isc_rxd_available(void *arg, uint16_t rxqid, uint32_t idx,
+                               int budget);
+static int lem_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri);
+
+static void lem_receive_checksum(int status, int errors, if_rxd_info_t ri);
+static void em_receive_checksum(uint32_t status, if_rxd_info_t ri);
+extern int em_intr(void *arg);
+
+struct if_txrx em_txrx  = {
+       em_isc_txd_encap,
+       em_isc_txd_flush,
+       em_isc_txd_credits_update,
+       em_isc_rxd_available,
+       em_isc_rxd_pkt_get,
+       em_isc_rxd_refill,
+       em_isc_rxd_flush,
+       em_intr
+};
+
+struct if_txrx lem_txrx  = {
+       em_isc_txd_encap,
+       em_isc_txd_flush,
+       em_isc_txd_credits_update,
+       lem_isc_rxd_available,
+       lem_isc_rxd_pkt_get,
+       lem_isc_rxd_refill,
+       em_isc_rxd_flush,
+       em_intr
+};
+
+extern if_shared_ctx_t em_sctx; 
+
+/**********************************************************************
+ *
+ *  Setup work for hardware segmentation offload (TSO) on
+ *  adapters using advanced tx descriptors
+ *
+ **********************************************************************/
+static int
+em_tso_setup(struct adapter *adapter, if_pkt_info_t pi, u32 *txd_upper, u32 
*txd_lower)
+{
+       if_softc_ctx_t scctx = adapter->shared;
+        struct em_tx_queue *que = &adapter->tx_queues[pi->ipi_qsidx];
+        struct tx_ring *txr = &que->txr;
+       struct e1000_context_desc *TXD;
+       struct em_txbuffer  *tx_buffer;
+        int cur, hdr_len;
+
+       hdr_len = pi->ipi_ehdrlen + pi->ipi_ip_hlen + pi->ipi_tcp_hlen;
+       *txd_lower = (E1000_TXD_CMD_DEXT |      /* Extended descr type */
+                     E1000_TXD_DTYP_D |        /* Data descr type */
+                     E1000_TXD_CMD_TSE);       /* Do TSE on this packet */
+
+       /* IP and/or TCP header checksum calculation and insertion. */
+       *txd_upper = (E1000_TXD_POPTS_IXSM | E1000_TXD_POPTS_TXSM) << 8;
+
+       cur = pi->ipi_pidx;
+        TXD = (struct e1000_context_desc *)&txr->tx_base[cur];
+        tx_buffer = &txr->tx_buffers[cur];
+       
+        /*
+        * Start offset for header checksum calculation.
+        * End offset for header checksum calculation.
+        * Offset of place put the checksum.
+        */
+       TXD->lower_setup.ip_fields.ipcss = pi->ipi_ehdrlen;
+       TXD->lower_setup.ip_fields.ipcse =
+           htole16(pi->ipi_ehdrlen + pi->ipi_ip_hlen - 1);
+       TXD->lower_setup.ip_fields.ipcso = pi->ipi_ehdrlen + offsetof(struct 
ip, ip_sum);
+
+         /*
+        * Start offset for payload checksum calculation.
+        * End offset for payload checksum calculation.
+        * Offset of place to put the checksum.
+        */
+       TXD->upper_setup.tcp_fields.tucss = pi->ipi_ehdrlen + pi->ipi_ip_hlen;
+       TXD->upper_setup.tcp_fields.tucse = 0;
+       TXD->upper_setup.tcp_fields.tucso =
+           pi->ipi_ehdrlen + pi->ipi_ip_hlen + offsetof(struct tcphdr, th_sum);
+       
+         /*
+        * Payload size per packet w/o any headers.
+        * Length of all headers up to payload.
+        */
+       TXD->tcp_seg_setup.fields.mss = htole16(pi->ipi_tso_segsz);
+       TXD->tcp_seg_setup.fields.hdr_len = hdr_len;
+
+       TXD->cmd_and_length = htole32(adapter->txd_cmd |
+                               E1000_TXD_CMD_DEXT |    /* Extended descr */
+                               E1000_TXD_CMD_TSE |     /* TSE context */
+                               E1000_TXD_CMD_IP |      /* Do IP csum */
+                               E1000_TXD_CMD_TCP |     /* Do TCP checksum */
+                                     (pi->ipi_len - hdr_len)); /* Total len */
+       tx_buffer->eop = -1;
+       txr->tx_tso = TRUE;
+
+       if (++cur == scctx->isc_ntxd[0]) {
+               cur = 0;
+       }
+       DPRINTF(iflib_get_dev(adapter->ctx), "%s: pidx: %d cur: %d\n", 
__FUNCTION__, pi->ipi_pidx, cur);
+       return (cur);
+}
+
+#define TSO_WORKAROUND 4
+#define DONT_FORCE_CTX 1
+
+
+/*********************************************************************
+ *  The offload context is protocol specific (TCP/UDP) and thus
+ *  only needs to be set when the protocol changes. The occasion
+ *  of a context change can be a performance detriment, and
+ *  might be better just disabled. The reason arises in the way
+ *  in which the controller supports pipelined requests from the
+ *  Tx data DMA. Up to four requests can be pipelined, and they may
+ *  belong to the same packet or to multiple packets. However all
+ *  requests for one packet are issued before a request is issued
+ *  for a subsequent packet and if a request for the next packet
+ *  requires a context change, that request will be stalled
+ *  until the previous request completes. This means setting up
+ *  a new context effectively disables pipelined Tx data DMA which
+ *  in turn greatly slow down performance to send small sized
+ *  frames. 
+ **********************************************************************/
+
+static int
+em_transmit_checksum_setup(struct adapter *adapter, if_pkt_info_t pi, u32 
*txd_upper, u32 *txd_lower)
+{
+        struct e1000_context_desc   *TXD = NULL;
+       if_softc_ctx_t              scctx = adapter->shared;
+       struct em_tx_queue          *que = &adapter->tx_queues[pi->ipi_qsidx];
+       struct tx_ring              *txr = &que->txr;
+       struct em_txbuffer          *tx_buffer;
+       int                         csum_flags = pi->ipi_csum_flags;
+       int                         cur, hdr_len;
+       u32                         cmd;
+       
+       cur = pi->ipi_pidx;
+       hdr_len = pi->ipi_ehdrlen + pi->ipi_ip_hlen;
+       cmd = adapter->txd_cmd;
+
+       /*
+        * The 82574L can only remember the *last* context used
+        * regardless of queue that it was use for.  We cannot reuse
+        * contexts on this hardware platform and must generate a new
+        * context every time.  82574L hardware spec, section 7.2.6,
+        * second note.
+        */
+       if (DONT_FORCE_CTX &&
+           adapter->tx_num_queues == 1 &&
+           txr->csum_lhlen == pi->ipi_ehdrlen &&
+           txr->csum_iphlen == pi->ipi_ip_hlen &&
+           txr->csum_flags == csum_flags) {
+               /*
+                * Same csum offload context as the previous packets;
+                * just return.
+                */
+               *txd_upper = txr->csum_txd_upper;
+               *txd_lower = txr->csum_txd_lower;
+               return (cur);
+       }
+
+       TXD = (struct e1000_context_desc *)&txr->tx_base[cur];
+       if (csum_flags & CSUM_IP) {
+               *txd_upper |= E1000_TXD_POPTS_IXSM << 8;
+               /*
+                * Start offset for header checksum calculation.
+                * End offset for header checksum calculation.
+                * Offset of place to put the checksum.
+                */
+               TXD->lower_setup.ip_fields.ipcss = pi->ipi_ehdrlen;
+               TXD->lower_setup.ip_fields.ipcse = htole16(hdr_len);
+               TXD->lower_setup.ip_fields.ipcso = pi->ipi_ehdrlen + 
offsetof(struct ip, ip_sum);
+               cmd |= E1000_TXD_CMD_IP;
+       }
+
+       if (csum_flags & (CSUM_TCP|CSUM_UDP)) {
+               uint8_t tucso;
+
+               *txd_upper |= E1000_TXD_POPTS_TXSM << 8;
+               *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D;
+
+               if (csum_flags & CSUM_TCP) {
+                       tucso = hdr_len + offsetof(struct tcphdr, th_sum);
+                       cmd |= E1000_TXD_CMD_TCP;
+               } else
+                       tucso = hdr_len + offsetof(struct udphdr, uh_sum);
+               TXD->upper_setup.tcp_fields.tucss = hdr_len;
+               TXD->upper_setup.tcp_fields.tucse = htole16(0);
+               TXD->upper_setup.tcp_fields.tucso = tucso;
+       }
+
+       txr->csum_lhlen = pi->ipi_ehdrlen;
+       txr->csum_iphlen = pi->ipi_ip_hlen;
+       txr->csum_flags = csum_flags;
+       txr->csum_txd_upper = *txd_upper;
+       txr->csum_txd_lower = *txd_lower;
+
+       TXD->tcp_seg_setup.data = htole32(0);
+       TXD->cmd_and_length =
+               htole32(E1000_TXD_CMD_IFCS | E1000_TXD_CMD_DEXT | cmd);
+
+       tx_buffer = &txr->tx_buffers[cur];
+       tx_buffer->eop = -1;
+
+       if (++cur == scctx->isc_ntxd[0]) {
+               cur = 0;
+       }
+       DPRINTF(iflib_get_dev(adapter->ctx), "checksum_setup csum_flags=%x 
txd_upper=%x txd_lower=%x hdr_len=%d cmd=%x\n",
+                     csum_flags, *txd_upper, *txd_lower, hdr_len, cmd);
+       return (cur);
+}
+
+static int
+em_isc_txd_encap(void *arg, if_pkt_info_t pi)
+{
+        struct adapter *sc       = arg;
+       if_softc_ctx_t scctx     = sc->shared;
+       struct em_tx_queue *que  = &sc->tx_queues[pi->ipi_qsidx];
+       struct tx_ring *txr      = &que->txr;
+       bus_dma_segment_t *segs  = pi->ipi_segs;
+       int nsegs                = pi->ipi_nsegs;
+       int csum_flags           = pi->ipi_csum_flags;
+        int i, j, first, pidx_last; 
+       u32                     txd_upper = 0, txd_lower = 0; 
+       
+       struct em_txbuffer *tx_buffer;
+       struct e1000_tx_desc *ctxd = NULL;
+       bool do_tso, tso_desc; 
+       
+       i = first = pi->ipi_pidx;         
+       do_tso = (csum_flags & CSUM_TSO);
+       tso_desc = FALSE;
+           /*
+        * TSO Hardware workaround, if this packet is not
+        * TSO, and is only a single descriptor long, and
+        * it follows a TSO burst, then we need to add a
+        * sentinel descriptor to prevent premature writeback.
+        */
+       if ((!do_tso) && (txr->tx_tso == TRUE)) {
+               if (nsegs == 1)
+                       tso_desc = TRUE;
+               txr->tx_tso = FALSE;
+       }
+
+       /* Do hardware assists */
+       if (do_tso) {
+               i = em_tso_setup(sc, pi, &txd_upper, &txd_lower);
+               tso_desc = TRUE;
+       } else if (csum_flags & CSUM_OFFLOAD) {
+               i = em_transmit_checksum_setup(sc, pi, &txd_upper, &txd_lower);
+       }
+
+       if (pi->ipi_mflags & M_VLANTAG) {
+         /* Set the vlan id. */
+               txd_upper |= htole16(pi->ipi_vtag) << 16;
+                /* Tell hardware to add tag */
+                txd_lower |= htole32(E1000_TXD_CMD_VLE);
+       }
+
+       DPRINTF(iflib_get_dev(sc->ctx), "encap: set up tx: nsegs=%d first=%d 
i=%d\n", nsegs, first, i);
+       /* XXX adapter->pcix_82544 -- lem_fill_descriptors */
+
+       /* Set up our transmit descriptors */
+       for (j = 0; j < nsegs; j++) {
+               bus_size_t seg_len;
+               bus_addr_t seg_addr;
+               uint32_t cmd;
+
+               ctxd = &txr->tx_base[i];
+               tx_buffer = &txr->tx_buffers[i];
+               seg_addr = segs[j].ds_addr;
+               seg_len = segs[j].ds_len;
+               cmd = E1000_TXD_CMD_IFCS | sc->txd_cmd;
+
+               /*
+               ** TSO Workaround:
+               ** If this is the last descriptor, we want to
+               ** split it so we have a small final sentinel
+               */
+               if (tso_desc && (j == (nsegs - 1)) && (seg_len > 8)) {
+                       seg_len -= TSO_WORKAROUND;
+                       ctxd->buffer_addr = htole64(seg_addr);
+                       ctxd->lower.data = htole32(cmd | txd_lower | seg_len);
+                       ctxd->upper.data = htole32(txd_upper);
+
+                        if (++i == scctx->isc_ntxd[0])
+                               i = 0;
+
+                       /* Now make the sentinel */
+                       ctxd = &txr->tx_base[i];
+                       tx_buffer = &txr->tx_buffers[i];
+                       ctxd->buffer_addr = htole64(seg_addr + seg_len);
+                       ctxd->lower.data = htole32(cmd | txd_lower | 
TSO_WORKAROUND);
+                       ctxd->upper.data = htole32(txd_upper);
+                       pidx_last = i;
+                       if (++i == scctx->isc_ntxd[0])
+                               i = 0;
+                       DPRINTF(iflib_get_dev(sc->ctx), "TSO path pidx_last=%d 
i=%d ntxd[0]=%d\n", pidx_last, i, scctx->isc_ntxd[0]);
+               } else {
+                       ctxd->buffer_addr = htole64(seg_addr);
+                       ctxd->lower.data = htole32(cmd | txd_lower | seg_len);
+                       ctxd->upper.data = htole32(txd_upper);
+                       pidx_last = i;
+                       if (++i == scctx->isc_ntxd[0])
+                               i = 0;
+                       DPRINTF(iflib_get_dev(sc->ctx), "pidx_last=%d i=%d 
ntxd[0]=%d\n", pidx_last, i, scctx->isc_ntxd[0]);
+               }
+               tx_buffer->eop = -1;
+       }
+
+       /*
+         * Last Descriptor of Packet
+        * needs End Of Packet (EOP)
+        * and Report Status (RS)
+         */
+        ctxd->lower.data |=
+               htole32(E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS);
+
+       tx_buffer = &txr->tx_buffers[first];
+       tx_buffer->eop = pidx_last;
+       DPRINTF(iflib_get_dev(sc->ctx), "tx_buffers[%d]->eop = %d 
ipi_new_pidx=%d\n", first, pidx_last, i);
+       pi->ipi_new_pidx = i;
+
+       return (0); 
+}
+
+static void
+em_isc_txd_flush(void *arg, uint16_t txqid, uint32_t pidx)
+{
+       struct adapter *adapter = arg;
+       struct em_tx_queue *que = &adapter->tx_queues[txqid];
+       struct tx_ring *txr = &que->txr;
+
+       E1000_WRITE_REG(&adapter->hw, E1000_TDT(txr->me), pidx);
+}
+
+static int
+em_isc_txd_credits_update(void *arg, uint16_t txqid, uint32_t cidx_init, bool 
clear)
+{
+       struct adapter *adapter = arg;
+       if_softc_ctx_t scctx = adapter->shared;
+       struct em_tx_queue *que = &adapter->tx_queues[txqid];
+       struct tx_ring *txr = &que->txr;
+
+       u32 cidx, processed = 0;
+       int last, done;
+       struct em_txbuffer *buf;
+       struct e1000_tx_desc *tx_desc, *eop_desc;
+
+       cidx = cidx_init;
+       buf = &txr->tx_buffers[cidx];
+       tx_desc = &txr->tx_base[cidx];
+        last = buf->eop;
+       eop_desc = &txr->tx_base[last];
+
+       DPRINTF(iflib_get_dev(adapter->ctx), "credits_update: cidx_init=%d 
clear=%d last=%d\n",
+                     cidx_init, clear, last);
+       /*
+        * What this does is get the index of the
+        * first descriptor AFTER the EOP of the
+        * first packet, that way we can do the
+        * simple comparison on the inner while loop.
+        */
+       if (++last == scctx->isc_ntxd[0])
+            last = 0;
+       done = last;
+
+
+       while (eop_desc->upper.fields.status & E1000_TXD_STAT_DD) {
+               /* We clean the range of the packet */
+               while (cidx != done) {
+                       if (clear) {
+                               tx_desc->upper.data = 0;
+                               tx_desc->lower.data = 0;
+                               tx_desc->buffer_addr = 0;
+                               buf->eop = -1;
+                       }
+                       tx_desc++;
+                       buf++;
+                       processed++;
+                 
+                       /* wrap the ring ? */
+                       if (++cidx == scctx->isc_ntxd[0]) {
+                               cidx = 0;
+                       }
+                       buf = &txr->tx_buffers[cidx];
+                       tx_desc = &txr->tx_base[cidx];
+               }
+               /* See if we can continue to the next packet */
+               last = buf->eop;
+               if (last == -1)
+                       break;
+               eop_desc = &txr->tx_base[last];
+               /* Get new done point */
+               if (++last == scctx->isc_ntxd[0])
+                       last = 0;
+               done = last;
+       }
+
+       DPRINTF(iflib_get_dev(adapter->ctx), "Processed %d credits update\n", 
processed);
+       return(processed);
+}
+
+static void
+lem_isc_rxd_refill(void *arg, uint16_t rxqid, uint8_t flid __unused,
+                 uint32_t pidx, uint64_t *paddrs, caddr_t *vaddrs __unused, 
uint16_t count, uint16_t buflen __unused)
+{
+       struct adapter *sc = arg;
+       if_softc_ctx_t scctx = sc->shared;
+       struct em_rx_queue *que = &sc->rx_queues[rxqid];
+       struct rx_ring *rxr = &que->rxr;
+       struct e1000_rx_desc *rxd;
+       int i;
+       uint32_t next_pidx;
+
+       for (i = 0, next_pidx = pidx; i < count; i++) {
+               rxd = (struct e1000_rx_desc *)&rxr->rx_base[next_pidx];
+               rxd->buffer_addr = htole64(paddrs[i]);
+               /* status bits must be cleared */
+               rxd->status = 0;
+
+               if (++next_pidx == scctx->isc_nrxd[0])
+                       next_pidx = 0;
+       }
+}
+
+static void
+em_isc_rxd_refill(void *arg, uint16_t rxqid, uint8_t flid __unused,
+                 uint32_t pidx, uint64_t *paddrs, caddr_t *vaddrs __unused, 
uint16_t count, uint16_t buflen __unused)
+{
+       struct adapter *sc = arg;
+       if_softc_ctx_t scctx = sc->shared;
+       struct em_rx_queue *que = &sc->rx_queues[rxqid];
+       struct rx_ring *rxr = &que->rxr;
+       union e1000_rx_desc_extended *rxd;
+       int i;
+       uint32_t next_pidx;
+
+       for (i = 0, next_pidx = pidx; i < count; i++) {
+               rxd = &rxr->rx_base[next_pidx];
+               rxd->read.buffer_addr = htole64(paddrs[i]);
+               /* DD bits must be cleared */
+               rxd->wb.upper.status_error = 0; 
+       
+               if (++next_pidx == scctx->isc_nrxd[0])
+                       next_pidx = 0;
+       }
+}
+
+static void
+em_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused, uint32_t 
pidx)
+{
+       struct adapter *sc       = arg;
+       struct em_rx_queue *que     = &sc->rx_queues[rxqid];
+       struct rx_ring *rxr      = &que->rxr;
+
+        E1000_WRITE_REG(&sc->hw, E1000_RDT(rxr->me), pidx);
+}
+
+static int
+lem_isc_rxd_available(void *arg, uint16_t rxqid, uint32_t idx, int budget)
+{
+       struct adapter *sc         = arg;
+       if_softc_ctx_t scctx = sc->shared;
+       struct em_rx_queue *que   = &sc->rx_queues[rxqid];
+       struct rx_ring *rxr        = &que->rxr;
+       struct e1000_rx_desc *rxd;
+       u32                      staterr = 0;
+       int                      cnt, i;
+
+       for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) {
+               rxd = (struct e1000_rx_desc *)&rxr->rx_base[i];
+               staterr = rxd->status;
+
+               if ((staterr & E1000_RXD_STAT_DD) == 0)
+                       break;
+
+               if (++i == scctx->isc_nrxd[0])
+                       i = 0;
+
+               if (staterr & E1000_RXD_STAT_EOP)
+                       cnt++;
+       }
+       return (cnt);
+}
+
+static int
+em_isc_rxd_available(void *arg, uint16_t rxqid, uint32_t idx, int budget)
+{
+               struct adapter *sc         = arg;
+       if_softc_ctx_t scctx = sc->shared;
+       struct em_rx_queue *que   = &sc->rx_queues[rxqid];
+       struct rx_ring *rxr        = &que->rxr;
+       union e1000_rx_desc_extended *rxd;
+       u32                      staterr = 0;
+       int                      cnt, i;
+
+       for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) {
+               rxd = &rxr->rx_base[i];
+               staterr = le32toh(rxd->wb.upper.status_error);
+
+               if ((staterr & E1000_RXD_STAT_DD) == 0)
+                       break;
+               
+               if (++i == scctx->isc_nrxd[0]) {
+                       i = 0;
+               }
+
+               if (staterr & E1000_RXD_STAT_EOP)
+                       cnt++;
+
+       }
+       return (cnt);
+}
+
+static int
+lem_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
+{
+       struct adapter           *adapter = arg;
+       if_softc_ctx_t           scctx = adapter->shared;
+       struct em_rx_queue       *que = &adapter->rx_queues[ri->iri_qsidx];
+       struct rx_ring           *rxr = &que->rxr;
+       struct e1000_rx_desc *rxd;
+       u16                      len;
+       u32                      status, errors;
+       bool                     eop;
+       int                      i, cidx;
+
+       status = errors = i = 0;
+       cidx = ri->iri_cidx;
+
+       do {
+               rxd = (struct e1000_rx_desc *)&rxr->rx_base[cidx];
+               status = rxd->status;
+               errors = rxd->errors;
+
+               /* Error Checking then decrement count */
+               MPASS ((status & E1000_RXD_STAT_DD) != 0);
+
+               len = le16toh(rxd->length);
+               ri->iri_len += len;
+
+               eop = (status & E1000_RXD_STAT_EOP) != 0;
+
+               /* Make sure bad packets are discarded */
+               if (errors & E1000_RXD_ERR_FRAME_ERR_MASK) {
+                       adapter->dropped_pkts++;
+                       /* XXX fixup if common */
+                       return (EBADMSG);
+               }
+
+               ri->iri_frags[i].irf_flid = 0;
+               ri->iri_frags[i].irf_idx = cidx;
+               ri->iri_frags[i].irf_len = len;
+               /* Zero out the receive descriptors status. */
+               rxd->status = 0;
+
+               if (++cidx == scctx->isc_nrxd[0])
+                       cidx = 0;
+               i++;
+       } while (!eop);
+
+       /* XXX add a faster way to look this up */
+       if (adapter->hw.mac.type >= e1000_82543 && !(status & 
E1000_RXD_STAT_IXSM))
+               lem_receive_checksum(status, errors, ri);
+
+       if (status & E1000_RXD_STAT_VP) {
+               ri->iri_vtag = le16toh(rxd->special);
+               ri->iri_flags |= M_VLANTAG;
+       }
+
+       ri->iri_nfrags = i;
+
+       return (0);
+}
+
+static int
+em_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
+{
+       struct adapter           *adapter = arg;
+       if_softc_ctx_t           scctx = adapter->shared;
+       struct em_rx_queue       *que = &adapter->rx_queues[ri->iri_qsidx];
+       struct rx_ring           *rxr = &que->rxr;
+       union e1000_rx_desc_extended *rxd;
+
+       u16                      len; 
+       u32                      staterr = 0;
+       bool                     eop;
+       int                      i, cidx, vtag;
+
+       i = vtag = 0;
+       cidx = ri->iri_cidx;
+
+       do {
+               rxd = &rxr->rx_base[cidx];
+               staterr = le32toh(rxd->wb.upper.status_error);  
+       
+               /* Error Checking then decrement count */
+               MPASS ((staterr & E1000_RXD_STAT_DD) != 0);
+
+               len = le16toh(rxd->wb.upper.length);
+               ri->iri_len += len; 
+
+               eop = (staterr & E1000_RXD_STAT_EOP) != 0;
+
+               /* Make sure bad packets are discarded */
+               if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) {
+                       adapter->dropped_pkts++;
+                       return EBADMSG;
+               }
+
+               ri->iri_frags[i].irf_flid = 0;
+               ri->iri_frags[i].irf_idx = cidx;
+               ri->iri_frags[i].irf_len = len;
+               /* Zero out the receive descriptors status. */
+               rxd->wb.upper.status_error &= htole32(~0xFF);
+
+               if (++cidx == scctx->isc_nrxd[0])
+                       cidx = 0;
+               i++;
+       } while (!eop);
+
+       /* XXX add a faster way to look this up */
+       if (adapter->hw.mac.type >= e1000_82543)
+               em_receive_checksum(staterr, ri);
+
+       if (staterr & E1000_RXD_STAT_VP) {
+               vtag = le16toh(rxd->wb.upper.vlan);
+       } 
+       
+       ri->iri_vtag = vtag;
+       ri->iri_nfrags = i;
+       if (vtag)
+               ri->iri_flags |= M_VLANTAG;
+               
+       return (0);
+}
+
+/*********************************************************************
+ *
+ *  Verify that the hardware indicated that the checksum is valid.
+ *  Inform the stack about the status of checksum so that stack
+ *  doesn't spend time verifying the checksum.
+ *
+ *********************************************************************/
+static void
+lem_receive_checksum(int status, int errors, if_rxd_info_t ri)
+{
+       /* Did it pass? */
+       if (status & E1000_RXD_STAT_IPCS && !(errors & E1000_RXD_ERR_IPE))
+               ri->iri_csum_flags = (CSUM_IP_CHECKED|CSUM_IP_VALID);
+
+       if (status & E1000_RXD_STAT_TCPCS) {
+               /* Did it pass? */
+               if (!(errors & E1000_RXD_ERR_TCPE)) {
+                       ri->iri_csum_flags |=
+                       (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
+                       ri->iri_csum_data = htons(0xffff);
+               }
+       }
+}
+
+static void
+em_receive_checksum(uint32_t status, if_rxd_info_t ri)
+{
+       ri->iri_csum_flags = 0;
+
+       /* Ignore Checksum bit is set */
+       if (status & E1000_RXD_STAT_IXSM)
+               return;
+
+       /* If the IP checksum exists and there is no IP Checksum error */
+       if ((status & (E1000_RXD_STAT_IPCS | E1000_RXDEXT_STATERR_IPE)) ==
+               E1000_RXD_STAT_IPCS) {
+               ri->iri_csum_flags = (CSUM_IP_CHECKED | CSUM_IP_VALID);
+       }
+
+       /* TCP or UDP checksum */
+       if ((status & (E1000_RXD_STAT_TCPCS | E1000_RXDEXT_STATERR_TCPE)) ==
+           E1000_RXD_STAT_TCPCS) {
+               ri->iri_csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
+               ri->iri_csum_data = htons(0xffff);
+       }
+       if (status & E1000_RXD_STAT_UDPCS) {
+               ri->iri_csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
+               ri->iri_csum_data = htons(0xffff);
+       }
+}

Modified: head/sys/dev/e1000/if_em.c
==============================================================================
--- head/sys/dev/e1000/if_em.c  Tue Jan 10 01:36:50 2017        (r311848)
+++ head/sys/dev/e1000/if_em.c  Tue Jan 10 03:23:22 2017        (r311849)
@@ -1,99 +1,10 @@
-/******************************************************************************
-
-  Copyright (c) 2001-2015, Intel Corporation 
-  All rights reserved.
-  
-  Redistribution and use in source and binary forms, with or without 
-  modification, are permitted provided that the following conditions are met:
-  
-   1. Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-  
-   2. 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.
-  
-   3. Neither the name of the Intel Corporation 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.
-
-******************************************************************************/
-/*$FreeBSD$*/
-
-#include "opt_em.h"
-#include "opt_ddb.h"
-#include "opt_inet.h"
-#include "opt_inet6.h"
-
-#ifdef HAVE_KERNEL_OPTION_HEADERS
-#include "opt_device_polling.h"
-#endif
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#ifdef DDB
-#include <sys/types.h>
-#include <ddb/ddb.h>
-#endif
-#if __FreeBSD_version >= 800000
-#include <sys/buf_ring.h>
-#endif
-#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/eventhandler.h>
-#include <machine/bus.h>
-#include <machine/resource.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/if_types.h>
-#include <net/if_vlan_var.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 <machine/in_cksum.h>
-#include <dev/led/led.h>
-#include <dev/pci/pcivar.h>
-#include <dev/pci/pcireg.h>
-
-#include "e1000_api.h"
-#include "e1000_82571.h"
+/* $FreeBSD$ */
 #include "if_em.h"
+#include <sys/sbuf.h>
+#include <machine/_inttypes.h>
+
+#define em_mac_min e1000_82547
+#define igb_mac_min e1000_82575
 
 /*********************************************************************
  *  Driver version:
@@ -110,184 +21,213 @@ char em_driver_version[] = "7.6.1-k";
  *  { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index }
  *********************************************************************/
 
-static em_vendor_info_t em_vendor_info_array[] =
+static pci_vendor_info_t em_vendor_info_array[] =
 {
-       /* Intel(R) PRO/1000 Network Connection */
-       { 0x8086, E1000_DEV_ID_82571EB_COPPER,  PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_FIBER,   PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_SERDES,  PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_SERDES_DUAL,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_SERDES_QUAD,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_QUAD_COPPER,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_QUAD_COPPER_LP,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571EB_QUAD_FIBER,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82571PT_QUAD_COPPER,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82572EI_COPPER,  PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82572EI_FIBER,   PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82572EI_SERDES,  PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82572EI,         PCI_ANY_ID, PCI_ANY_ID, 0},
-
-       { 0x8086, E1000_DEV_ID_82573E,          PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82573E_IAMT,     PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82573L,          PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_82583V,          PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_80003ES2LAN_COPPER_SPT,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_80003ES2LAN_SERDES_SPT,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_80003ES2LAN_COPPER_DPT,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_80003ES2LAN_SERDES_DPT,
-                                               PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_ICH8_IGP_M_AMT,  PCI_ANY_ID, PCI_ANY_ID, 0},
-       { 0x8086, E1000_DEV_ID_ICH8_IGP_AMT,    PCI_ANY_ID, PCI_ANY_ID, 0},

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to