#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ix_txrx.c,v 1.117 2024/06/29 12:11:12 riastradh Exp $");
#include "opt_inet.h"
#include "opt_inet6.h"
#include "ixgbe.h"
#ifdef RSC
static bool ixgbe_rsc_enable = FALSE;
#endif
#ifdef IXGBE_FDIR
static int atr_sample_rate = 20;
#endif
#define IXGBE_M_ADJ(sc, rxr, mp) \
if (sc->max_frame_size <= (rxr->mbuf_sz - ETHER_ALIGN)) \
m_adj(mp, ETHER_ALIGN)
static void ixgbe_setup_transmit_ring(struct tx_ring *);
static void ixgbe_free_transmit_buffers(struct tx_ring *);
static int ixgbe_setup_receive_ring(struct rx_ring *);
static void ixgbe_free_receive_buffers(struct rx_ring *);
static void ixgbe_rx_checksum(u32, struct mbuf *, u32,
struct ixgbe_hw_stats *);
static void ixgbe_refresh_mbufs(struct rx_ring *, int);
static void ixgbe_drain(struct ifnet *, struct tx_ring *);
static int ixgbe_xmit(struct tx_ring *, struct mbuf *);
static int ixgbe_tx_ctx_setup(struct tx_ring *,
struct mbuf *, u32 *, u32 *);
static int ixgbe_tso_setup(struct tx_ring *,
struct mbuf *, u32 *, u32 *);
static __inline void ixgbe_rx_discard(struct rx_ring *, int);
static __inline void ixgbe_rx_input(struct rx_ring *, struct ifnet *,
struct mbuf *, u32);
static int ixgbe_dma_malloc(struct ixgbe_softc *, bus_size_t,
struct ixgbe_dma_alloc *, int);
static void ixgbe_dma_free(struct ixgbe_softc *, struct ixgbe_dma_alloc *);
#ifdef RSC
static void ixgbe_setup_hw_rsc(struct rx_ring *);
#endif
int
ixgbe_legacy_start_locked(struct ifnet *ifp, struct tx_ring *txr)
{
int rc;
struct mbuf *m_head;
struct ixgbe_softc *sc = txr->sc;
IXGBE_TX_LOCK_ASSERT(txr);
if (sc->link_active != LINK_STATE_UP) {
ixgbe_drain(ifp, txr);
return (ENETDOWN);
}
if ((ifp->if_flags & IFF_RUNNING) == 0)
return (ENETDOWN);
if (txr->txr_no_space)
return (ENETDOWN);
while (!IFQ_IS_EMPTY(&ifp->if_snd)) {
if (txr->tx_avail <= IXGBE_QUEUE_MIN_FREE)
break;
IFQ_POLL(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
if ((rc = ixgbe_xmit(txr, m_head)) == EAGAIN) {
break;
}
IFQ_DEQUEUE(&ifp->if_snd, m_head);
if (rc != 0) {
m_freem(m_head);
continue;
}
bpf_mtap(ifp, m_head, BPF_D_OUT);
}
return IXGBE_SUCCESS;
}
void
ixgbe_legacy_start(struct ifnet *ifp)
{
struct ixgbe_softc *sc = ifp->if_softc;
struct tx_ring *txr = sc->tx_rings;
if (ifp->if_flags & IFF_RUNNING) {
IXGBE_TX_LOCK(txr);
ixgbe_legacy_start_locked(ifp, txr);
IXGBE_TX_UNLOCK(txr);
}
}
int
ixgbe_mq_start(struct ifnet *ifp, struct mbuf *m)
{
struct ixgbe_softc *sc = ifp->if_softc;
struct tx_ring *txr;
int i;
#ifdef RSS
uint32_t bucket_id;
#endif
#ifdef RSS
if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) {
if ((sc->feat_en & IXGBE_FEATURE_RSS) &&
(rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m),
&bucket_id) == 0)) {
i = bucket_id % sc->num_queues;
#ifdef IXGBE_DEBUG
if (bucket_id > sc->num_queues)
if_printf(ifp,
"bucket_id (%d) > num_queues (%d)\n",
bucket_id, sc->num_queues);
#endif
} else
i = m->m_pkthdr.flowid % sc->num_queues;
} else
#endif
i = (cpu_index(curcpu()) % ncpu) % sc->num_queues;
if (((1ULL << i) & sc->active_queues) == 0)
i = ffs64(sc->active_queues);
txr = &sc->tx_rings[i];
if (__predict_false(!pcq_put(txr->txr_interq, m))) {
m_freem(m);
IXGBE_EVC_ADD(&txr->pcq_drops, 1);
return ENOBUFS;
}
#ifdef IXGBE_ALWAYS_TXDEFER
kpreempt_disable();
softint_schedule(txr->txr_si);
kpreempt_enable();
#else
if (IXGBE_TX_TRYLOCK(txr)) {
ixgbe_mq_start_locked(ifp, txr);
IXGBE_TX_UNLOCK(txr);
} else {
if (sc->txrx_use_workqueue) {
u_int *enqueued;
enqueued = percpu_getref(sc->txr_wq_enqueued);
if (*enqueued == 0) {
*enqueued = 1;
percpu_putref(sc->txr_wq_enqueued);
workqueue_enqueue(sc->txr_wq,
&txr->wq_cookie, curcpu());
} else
percpu_putref(sc->txr_wq_enqueued);
} else {
kpreempt_disable();
softint_schedule(txr->txr_si);
kpreempt_enable();
}
}
#endif
return (0);
}
int
ixgbe_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr)
{
struct mbuf *next;
int enqueued = 0, err = 0;
if (txr->sc->link_active != LINK_STATE_UP) {
ixgbe_drain(ifp, txr);
return (ENETDOWN);
}
if ((ifp->if_flags & IFF_RUNNING) == 0)
return (ENETDOWN);
if (txr->txr_no_space)
return (ENETDOWN);
while ((next = pcq_get(txr->txr_interq)) != NULL) {
if ((err = ixgbe_xmit(txr, next)) != 0) {
m_freem(next);
break;
}
enqueued++;
#if __FreeBSD_version >= 1100036
if ((txr->sc->feat_en & IXGBE_FEATURE_VF) &&
(next->m_flags & M_MCAST))
if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
#endif
bpf_mtap(ifp, next, BPF_D_OUT);
if ((ifp->if_flags & IFF_RUNNING) == 0)
break;
}
if (txr->tx_avail < IXGBE_TX_CLEANUP_THRESHOLD(txr->sc))
ixgbe_txeof(txr);
return (err);
}
void
ixgbe_deferred_mq_start(void *arg)
{
struct tx_ring *txr = arg;
struct ixgbe_softc *sc = txr->sc;
struct ifnet *ifp = sc->ifp;
IXGBE_TX_LOCK(txr);
if (pcq_peek(txr->txr_interq) != NULL)
ixgbe_mq_start_locked(ifp, txr);
IXGBE_TX_UNLOCK(txr);
}
void
ixgbe_deferred_mq_start_work(struct work *wk, void *arg)
{
struct tx_ring *txr = container_of(wk, struct tx_ring, wq_cookie);
struct ixgbe_softc *sc = txr->sc;
u_int *enqueued = percpu_getref(sc->txr_wq_enqueued);
*enqueued = 0;
percpu_putref(sc->txr_wq_enqueued);
ixgbe_deferred_mq_start(txr);
}
void
ixgbe_drain_all(struct ixgbe_softc *sc)
{
struct ifnet *ifp = sc->ifp;
struct ix_queue *que = sc->queues;
for (int i = 0; i < sc->num_queues; i++, que++) {
struct tx_ring *txr = que->txr;
IXGBE_TX_LOCK(txr);
ixgbe_drain(ifp, txr);
IXGBE_TX_UNLOCK(txr);
}
}
static int
ixgbe_xmit(struct tx_ring *txr, struct mbuf *m_head)
{
struct ixgbe_softc *sc = txr->sc;
struct ixgbe_tx_buf *txbuf;
union ixgbe_adv_tx_desc *txd = NULL;
struct ifnet *ifp = sc->ifp;
int i, j, error;
int first;
u32 olinfo_status = 0, cmd_type_len;
bool remap = TRUE;
bus_dmamap_t map;
cmd_type_len = (IXGBE_ADVTXD_DTYP_DATA |
IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT);
if (vlan_has_tag(m_head))
cmd_type_len |= IXGBE_ADVTXD_DCMD_VLE;
first = txr->next_avail_desc;
txbuf = &txr->tx_buffers[first];
map = txbuf->map;
retry:
error = bus_dmamap_load_mbuf(txr->txtag->dt_dmat, map, m_head,
BUS_DMA_NOWAIT);
if (__predict_false(error)) {
struct mbuf *m;
switch (error) {
case EAGAIN:
txr->q_eagain_tx_dma_setup++;
return EAGAIN;
case ENOMEM:
txr->q_enomem_tx_dma_setup++;
return EAGAIN;
case EFBIG:
if (remap == TRUE) {
remap = FALSE;
txr->q_efbig_tx_dma_setup++;
m = m_defrag(m_head, M_NOWAIT);
if (m == NULL) {
txr->q_mbuf_defrag_failed++;
return ENOBUFS;
}
m_head = m;
goto retry;
} else {
txr->q_efbig2_tx_dma_setup++;
return error;
}
case EINVAL:
txr->q_einval_tx_dma_setup++;
return error;
default:
txr->q_other_tx_dma_setup++;
return error;
}
}
if (txr->tx_avail < (map->dm_nsegs + 2)) {
txr->txr_no_space = true;
IXGBE_EVC_ADD(&txr->no_desc_avail, 1);
ixgbe_dmamap_unload(txr->txtag, txbuf->map);
return EAGAIN;
}
error = ixgbe_tx_ctx_setup(txr, m_head, &cmd_type_len, &olinfo_status);
if (__predict_false(error)) {
return (error);
}
#ifdef IXGBE_FDIR
if ((sc->feat_en & IXGBE_FEATURE_FDIR) &&
(txr->atr_sample) && (!sc->fdir_reinit)) {
++txr->atr_count;
if (txr->atr_count >= atr_sample_rate) {
ixgbe_atr(txr, m_head);
txr->atr_count = 0;
}
}
#endif
olinfo_status |= IXGBE_ADVTXD_CC;
i = txr->next_avail_desc;
for (j = 0; j < map->dm_nsegs; j++) {
bus_size_t seglen;
uint64_t segaddr;
txbuf = &txr->tx_buffers[i];
txd = &txr->tx_base[i];
seglen = map->dm_segs[j].ds_len;
segaddr = htole64(map->dm_segs[j].ds_addr);
txd->read.buffer_addr = segaddr;
txd->read.cmd_type_len = htole32(cmd_type_len | seglen);
txd->read.olinfo_status = htole32(olinfo_status);
if (++i == txr->num_desc)
i = 0;
}
txd->read.cmd_type_len |= htole32(IXGBE_TXD_CMD_EOP | IXGBE_TXD_CMD_RS);
txr->tx_avail -= map->dm_nsegs;
txr->next_avail_desc = i;
txbuf->m_head = m_head;
txr->tx_buffers[first].map = txbuf->map;
txbuf->map = map;
bus_dmamap_sync(txr->txtag->dt_dmat, map, 0, m_head->m_pkthdr.len,
BUS_DMASYNC_PREWRITE);
txbuf = &txr->tx_buffers[first];
txbuf->eop = txd;
ixgbe_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
IXGBE_EVC_ADD(&txr->total_packets, 1);
IXGBE_WRITE_REG(&sc->hw, txr->tail, i);
net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
if_statadd_ref(ifp, nsr, if_obytes, m_head->m_pkthdr.len);
if (m_head->m_flags & M_MCAST)
if_statinc_ref(ifp, nsr, if_omcasts);
IF_STAT_PUTREF(ifp);
if (txr->busy == 0)
txr->busy = 1;
return (0);
}
static void
ixgbe_drain(struct ifnet *ifp, struct tx_ring *txr)
{
struct mbuf *m;
IXGBE_TX_LOCK_ASSERT(txr);
if (txr->me == 0) {
while (!IFQ_IS_EMPTY(&ifp->if_snd)) {
IFQ_DEQUEUE(&ifp->if_snd, m);
m_freem(m);
IF_DROP(&ifp->if_snd);
}
}
while ((m = pcq_get(txr->txr_interq)) != NULL) {
m_freem(m);
IXGBE_EVC_ADD(&txr->pcq_drops, 1);
}
}
static int
ixgbe_allocate_transmit_buffers(struct tx_ring *txr)
{
struct ixgbe_softc *sc = txr->sc;
device_t dev = sc->dev;
struct ixgbe_tx_buf *txbuf;
int error, i;
error = ixgbe_dma_tag_create(
sc->osdep.dmat,
1,
0,
IXGBE_TSO_SIZE,
sc->num_segs,
PAGE_SIZE,
0,
&txr->txtag);
if (error != 0) {
aprint_error_dev(dev,"Unable to allocate TX DMA tag\n");
goto fail;
}
txr->tx_buffers = kmem_zalloc(sizeof(struct ixgbe_tx_buf) *
sc->num_tx_desc, KM_SLEEP);
txbuf = txr->tx_buffers;
for (i = 0; i < sc->num_tx_desc; i++, txbuf++) {
error = ixgbe_dmamap_create(txr->txtag, 0, &txbuf->map);
if (error != 0) {
aprint_error_dev(dev,
"Unable to create TX DMA map (%d)\n", error);
goto fail;
}
}
return 0;
fail:
#if 0
ixgbe_free_transmit_structures(sc);
#else
ixgbe_free_transmit_buffers(txr);
#endif
return (error);
}
static void
ixgbe_setup_transmit_ring(struct tx_ring *txr)
{
struct ixgbe_softc *sc = txr->sc;
struct ixgbe_tx_buf *txbuf;
#ifdef DEV_NETMAP
struct netmap_sc *na = NA(sc->ifp);
struct netmap_slot *slot;
#endif
IXGBE_TX_LOCK(txr);
#ifdef DEV_NETMAP
if (sc->feat_en & IXGBE_FEATURE_NETMAP) {
slot = netmap_reset(na, NR_TX, txr->me, 0);
}
#endif
bzero((void *)txr->tx_base,
(sizeof(union ixgbe_adv_tx_desc)) * sc->num_tx_desc);
txr->next_avail_desc = 0;
txr->next_to_clean = 0;
txbuf = txr->tx_buffers;
for (int i = 0; i < txr->num_desc; i++, txbuf++) {
if (txbuf->m_head != NULL) {
bus_dmamap_sync(txr->txtag->dt_dmat, txbuf->map,
0, txbuf->m_head->m_pkthdr.len,
BUS_DMASYNC_POSTWRITE);
ixgbe_dmamap_unload(txr->txtag, txbuf->map);
m_freem(txbuf->m_head);
txbuf->m_head = NULL;
}
#ifdef DEV_NETMAP
if ((sc->feat_en & IXGBE_FEATURE_NETMAP) && slot) {
int si = netmap_idx_n2k(na->tx_rings[txr->me], i);
netmap_load_map(na, txr->txtag,
txbuf->map, NMB(na, slot + si));
}
#endif
txbuf->eop = NULL;
}
#ifdef IXGBE_FDIR
if (sc->feat_en & IXGBE_FEATURE_FDIR)
txr->atr_sample = atr_sample_rate;
#endif
txr->tx_avail = sc->num_tx_desc;
ixgbe_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
IXGBE_TX_UNLOCK(txr);
}
int
ixgbe_setup_transmit_structures(struct ixgbe_softc *sc)
{
struct tx_ring *txr = sc->tx_rings;
for (int i = 0; i < sc->num_queues; i++, txr++)
ixgbe_setup_transmit_ring(txr);
return (0);
}
void
ixgbe_free_transmit_structures(struct ixgbe_softc *sc)
{
struct tx_ring *txr = sc->tx_rings;
for (int i = 0; i < sc->num_queues; i++, txr++) {
ixgbe_free_transmit_buffers(txr);
ixgbe_dma_free(sc, &txr->txdma);
IXGBE_TX_LOCK_DESTROY(txr);
}
kmem_free(sc->tx_rings, sizeof(struct tx_ring) * sc->num_queues);
}
static void
ixgbe_free_transmit_buffers(struct tx_ring *txr)
{
struct ixgbe_softc *sc = txr->sc;
struct ixgbe_tx_buf *tx_buffer;
int i;
INIT_DEBUGOUT("ixgbe_free_transmit_buffers: begin");
if (txr->tx_buffers == NULL)
return;
tx_buffer = txr->tx_buffers;
for (i = 0; i < sc->num_tx_desc; i++, tx_buffer++) {
if (tx_buffer->m_head != NULL) {
bus_dmamap_sync(txr->txtag->dt_dmat, tx_buffer->map,
0, tx_buffer->m_head->m_pkthdr.len,
BUS_DMASYNC_POSTWRITE);
ixgbe_dmamap_unload(txr->txtag, tx_buffer->map);
m_freem(tx_buffer->m_head);
tx_buffer->m_head = NULL;
if (tx_buffer->map != NULL) {
ixgbe_dmamap_destroy(txr->txtag,
tx_buffer->map);
tx_buffer->map = NULL;
}
} else if (tx_buffer->map != NULL) {
ixgbe_dmamap_unload(txr->txtag, tx_buffer->map);
ixgbe_dmamap_destroy(txr->txtag, tx_buffer->map);
tx_buffer->map = NULL;
}
}
if (txr->txr_interq != NULL) {
struct mbuf *m;
while ((m = pcq_get(txr->txr_interq)) != NULL)
m_freem(m);
pcq_destroy(txr->txr_interq);
}
if (txr->tx_buffers != NULL) {
kmem_free(txr->tx_buffers,
sizeof(struct ixgbe_tx_buf) * sc->num_tx_desc);
txr->tx_buffers = NULL;
}
if (txr->txtag != NULL) {
ixgbe_dma_tag_destroy(txr->txtag);
txr->txtag = NULL;
}
}
static int
ixgbe_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp,
u32 *cmd_type_len, u32 *olinfo_status)
{
struct ixgbe_softc *sc = txr->sc;
struct ixgbe_adv_tx_context_desc *TXD;
struct ether_vlan_header *eh;
#ifdef INET
struct ip *ip;
#endif
#ifdef INET6
struct ip6_hdr *ip6;
#endif
int ehdrlen, ip_hlen = 0;
int offload = TRUE;
int ctxd = txr->next_avail_desc;
u32 vlan_macip_lens = 0;
u32 type_tucmd_mlhl = 0;
u16 vtag = 0;
u16 etype;
u8 ipproto = 0;
char *l3d;
if (mp->m_pkthdr.csum_flags & (M_CSUM_TSOv4 | M_CSUM_TSOv6)) {
int rv = ixgbe_tso_setup(txr, mp, cmd_type_len, olinfo_status);
if (rv != 0)
IXGBE_EVC_ADD(&sc->tso_err, 1);
return rv;
}
if ((mp->m_pkthdr.csum_flags & M_CSUM_OFFLOAD) == 0)
offload = FALSE;
*olinfo_status |= mp->m_pkthdr.len << IXGBE_ADVTXD_PAYLEN_SHIFT;
if (vlan_has_tag(mp)) {
vtag = htole16(vlan_get_tag(mp));
vlan_macip_lens |= (vtag << IXGBE_ADVTXD_VLAN_SHIFT);
} else if (!(txr->sc->feat_en & IXGBE_FEATURE_NEEDS_CTXD) &&
(offload == FALSE))
return (0);
KASSERT(mp->m_len >= offsetof(struct ether_vlan_header, evl_tag));
eh = mtod(mp, struct ether_vlan_header *);
if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
KASSERT(mp->m_len >= sizeof(struct ether_vlan_header));
etype = ntohs(eh->evl_proto);
ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
} else {
etype = ntohs(eh->evl_encap_proto);
ehdrlen = ETHER_HDR_LEN;
}
vlan_macip_lens |= ehdrlen << IXGBE_ADVTXD_MACLEN_SHIFT;
if (offload == FALSE)
goto no_offloads;
if (mp->m_len == ehdrlen && mp->m_next)
l3d = mtod(mp->m_next, char *);
else
l3d = mtod(mp, char *) + ehdrlen;
switch (etype) {
#ifdef INET
case ETHERTYPE_IP:
ip = (struct ip *)(l3d);
ip_hlen = ip->ip_hl << 2;
ipproto = ip->ip_p;
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
KASSERT((mp->m_pkthdr.csum_flags & M_CSUM_IPv4) == 0 ||
ip->ip_sum == 0);
break;
#endif
#ifdef INET6
case ETHERTYPE_IPV6:
ip6 = (struct ip6_hdr *)(l3d);
ip_hlen = sizeof(struct ip6_hdr);
ipproto = ip6->ip6_nxt;
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV6;
break;
#endif
default:
offload = false;
break;
}
if ((mp->m_pkthdr.csum_flags & M_CSUM_IPv4) != 0)
*olinfo_status |= IXGBE_TXD_POPTS_IXSM << 8;
vlan_macip_lens |= ip_hlen;
switch (ipproto) {
case IPPROTO_TCP:
if (mp->m_pkthdr.csum_flags &
(M_CSUM_TCPv4 | M_CSUM_TCPv6))
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
else
offload = false;
break;
case IPPROTO_UDP:
if (mp->m_pkthdr.csum_flags &
(M_CSUM_UDPv4 | M_CSUM_UDPv6))
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_UDP;
else
offload = false;
break;
default:
offload = false;
break;
}
if (offload)
*olinfo_status |= IXGBE_TXD_POPTS_TXSM << 8;
no_offloads:
type_tucmd_mlhl |= IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT;
TXD = (struct ixgbe_adv_tx_context_desc *)&txr->tx_base[ctxd];
TXD->vlan_macip_lens = htole32(vlan_macip_lens);
TXD->type_tucmd_mlhl = htole32(type_tucmd_mlhl);
TXD->seqnum_seed = htole32(0);
TXD->mss_l4len_idx = htole32(0);
if (++ctxd == txr->num_desc)
ctxd = 0;
txr->next_avail_desc = ctxd;
--txr->tx_avail;
return (0);
}
static int
ixgbe_tso_setup(struct tx_ring *txr, struct mbuf *mp, u32 *cmd_type_len,
u32 *olinfo_status)
{
struct ixgbe_adv_tx_context_desc *TXD;
struct ether_vlan_header *eh;
#ifdef INET6
struct ip6_hdr *ip6;
#endif
#ifdef INET
struct ip *ip;
#endif
struct tcphdr *th;
int ctxd, ehdrlen, ip_hlen, tcp_hlen;
u32 vlan_macip_lens = 0;
u32 type_tucmd_mlhl = 0;
u32 mss_l4len_idx = 0, paylen;
u16 vtag = 0, eh_type;
eh = mtod(mp, struct ether_vlan_header *);
if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
eh_type = eh->evl_proto;
} else {
ehdrlen = ETHER_HDR_LEN;
eh_type = eh->evl_encap_proto;
}
switch (ntohs(eh_type)) {
#ifdef INET
case ETHERTYPE_IP:
ip = (struct ip *)(mp->m_data + ehdrlen);
if (ip->ip_p != IPPROTO_TCP)
return (ENXIO);
ip->ip_sum = 0;
ip_hlen = ip->ip_hl << 2;
th = (struct tcphdr *)((char *)ip + ip_hlen);
th->th_sum = in_cksum_phdr(ip->ip_src.s_addr,
ip->ip_dst.s_addr, htons(IPPROTO_TCP));
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
*olinfo_status |= IXGBE_TXD_POPTS_IXSM << 8;
break;
#endif
#ifdef INET6
case ETHERTYPE_IPV6:
ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
if (ip6->ip6_nxt != IPPROTO_TCP)
return (ENXIO);
ip_hlen = sizeof(struct ip6_hdr);
ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
th = (struct tcphdr *)((char *)ip6 + ip_hlen);
th->th_sum = in6_cksum_phdr(&ip6->ip6_src,
&ip6->ip6_dst, 0, htonl(IPPROTO_TCP));
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV6;
break;
#endif
default:
panic("%s: CSUM_TSO but no supported IP version (0x%04x)",
__func__, ntohs(eh_type));
break;
}
ctxd = txr->next_avail_desc;
TXD = (struct ixgbe_adv_tx_context_desc *)&txr->tx_base[ctxd];
tcp_hlen = th->th_off << 2;
paylen = mp->m_pkthdr.len - ehdrlen - ip_hlen - tcp_hlen;
if (vlan_has_tag(mp)) {
vtag = htole16(vlan_get_tag(mp));
vlan_macip_lens |= (vtag << IXGBE_ADVTXD_VLAN_SHIFT);
}
vlan_macip_lens |= ehdrlen << IXGBE_ADVTXD_MACLEN_SHIFT;
vlan_macip_lens |= ip_hlen;
TXD->vlan_macip_lens = htole32(vlan_macip_lens);
type_tucmd_mlhl |= IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT;
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
TXD->type_tucmd_mlhl = htole32(type_tucmd_mlhl);
mss_l4len_idx |= (mp->m_pkthdr.segsz << IXGBE_ADVTXD_MSS_SHIFT);
mss_l4len_idx |= (tcp_hlen << IXGBE_ADVTXD_L4LEN_SHIFT);
TXD->mss_l4len_idx = htole32(mss_l4len_idx);
TXD->seqnum_seed = htole32(0);
if (++ctxd == txr->num_desc)
ctxd = 0;
txr->tx_avail--;
txr->next_avail_desc = ctxd;
*cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE;
*olinfo_status |= IXGBE_TXD_POPTS_TXSM << 8;
*olinfo_status |= paylen << IXGBE_ADVTXD_PAYLEN_SHIFT;
IXGBE_EVC_ADD(&txr->tso_tx, 1);
return (0);
}
bool
ixgbe_txeof(struct tx_ring *txr)
{
struct ixgbe_softc *sc = txr->sc;
struct ifnet *ifp = sc->ifp;
struct ixgbe_tx_buf *buf;
union ixgbe_adv_tx_desc *txd;
u32 work, processed = 0;
u32 limit = sc->tx_process_limit;
u16 avail;
KASSERT(mutex_owned(&txr->tx_mtx));
#ifdef DEV_NETMAP
if ((sc->feat_en & IXGBE_FEATURE_NETMAP) &&
(sc->ifp->if_capenable & IFCAP_NETMAP)) {
struct netmap_sc *na = NA(sc->ifp);
struct netmap_kring *kring = na->tx_rings[txr->me];
txd = txr->tx_base;
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_POSTREAD);
if (kring->nr_kflags < kring->nkr_num_slots &&
le32toh(txd[kring->nr_kflags].wb.status) & IXGBE_TXD_STAT_DD) {
netmap_tx_irq(ifp, txr->me);
}
return false;
}
#endif
if (txr->tx_avail == txr->num_desc) {
txr->busy = 0;
return false;
}
work = txr->next_to_clean;
buf = &txr->tx_buffers[work];
txd = &txr->tx_base[work];
work -= txr->num_desc;
avail = txr->tx_avail;
ixgbe_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_POSTREAD);
do {
union ixgbe_adv_tx_desc *eop = buf->eop;
if (eop == NULL)
break;
if ((le32toh(eop->wb.status) & IXGBE_TXD_STAT_DD) == 0)
break;
if (buf->m_head) {
txr->bytes += buf->m_head->m_pkthdr.len;
bus_dmamap_sync(txr->txtag->dt_dmat, buf->map,
0, buf->m_head->m_pkthdr.len,
BUS_DMASYNC_POSTWRITE);
ixgbe_dmamap_unload(txr->txtag, buf->map);
m_freem(buf->m_head);
buf->m_head = NULL;
}
buf->eop = NULL;
++avail;
while (txd != eop) {
++txd;
++buf;
++work;
if (__predict_false(!work)) {
work -= txr->num_desc;
buf = txr->tx_buffers;
txd = txr->tx_base;
}
if (buf->m_head) {
txr->bytes +=
buf->m_head->m_pkthdr.len;
bus_dmamap_sync(txr->txtag->dt_dmat,
buf->map,
0, buf->m_head->m_pkthdr.len,
BUS_DMASYNC_POSTWRITE);
ixgbe_dmamap_unload(txr->txtag,
buf->map);
m_freem(buf->m_head);
buf->m_head = NULL;
}
++avail;
buf->eop = NULL;
}
++processed;
++txd;
++buf;
++work;
if (__predict_false(!work)) {
work -= txr->num_desc;
buf = txr->tx_buffers;
txd = txr->tx_base;
}
prefetch(txd);
} while (__predict_true(--limit));
ixgbe_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
work += txr->num_desc;
txr->next_to_clean = work;
if (processed) {
txr->tx_avail = avail;
txr->txr_no_space = false;
txr->packets += processed;
if_statadd(ifp, if_opackets, processed);
}
if ((processed == 0) && (txr->busy != IXGBE_QUEUE_HUNG))
++txr->busy;
if (processed)
txr->busy = 1;
if (txr->tx_avail == txr->num_desc)
txr->busy = 0;
return ((limit > 0) ? false : true);
}
#ifdef RSC
static inline u32
ixgbe_rsc_count(union ixgbe_adv_rx_desc *rx)
{
return (le32toh(rx->wb.lower.lo_dword.data) &
IXGBE_RXDADV_RSCCNT_MASK) >> IXGBE_RXDADV_RSCCNT_SHIFT;
}
static void
ixgbe_setup_hw_rsc(struct rx_ring *rxr)
{
struct ixgbe_softc *sc = rxr->sc;
struct ixgbe_hw *hw = &sc->hw;
u32 rscctrl, rdrxctl;
if ((sc->ifp->if_capenable & IFCAP_LRO) == 0) {
rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(rxr->me));
rscctrl &= ~IXGBE_RSCCTL_RSCEN;
return;
}
rdrxctl = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
rdrxctl &= ~IXGBE_RDRXCTL_RSCFRSTSIZE;
#ifdef DEV_NETMAP
if (!(sc->feat_en & IXGBE_FEATURE_NETMAP) ||
!(sc->ifp->if_capenable & IFCAP_NETMAP) ||
ix_crcstrip)
#endif
rdrxctl |= IXGBE_RDRXCTL_CRCSTRIP;
rdrxctl |= IXGBE_RDRXCTL_RSCACKC;
IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rdrxctl);
rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(rxr->me));
rscctrl |= IXGBE_RSCCTL_RSCEN;
if (rxr->mbuf_sz == MCLBYTES)
rscctrl |= IXGBE_RSCCTL_MAXDESC_16;
else if (rxr->mbuf_sz == MJUMPAGESIZE)
rscctrl |= IXGBE_RSCCTL_MAXDESC_8;
else if (rxr->mbuf_sz == MJUM9BYTES)
rscctrl |= IXGBE_RSCCTL_MAXDESC_4;
else
rscctrl |= IXGBE_RSCCTL_MAXDESC_1;
IXGBE_WRITE_REG(hw, IXGBE_RSCCTL(rxr->me), rscctrl);
IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(0),
(IXGBE_READ_REG(hw, IXGBE_PSRTYPE(0)) | IXGBE_PSRTYPE_TCPHDR));
IXGBE_WRITE_REG(hw, IXGBE_RSCDBU,
(IXGBE_RSCDBU_RSCACKDIS | IXGBE_READ_REG(hw, IXGBE_RSCDBU)));
rxr->hw_rsc = TRUE;
}
#endif
static void
ixgbe_refresh_mbufs(struct rx_ring *rxr, int limit)
{
struct ixgbe_softc *sc = rxr->sc;
struct ixgbe_rx_buf *rxbuf;
struct mbuf *mp;
int i, error;
bool refreshed = false;
i = rxr->next_to_refresh;
if (++i == rxr->num_desc)
i = 0;
while (i != limit) {
rxbuf = &rxr->rx_buffers[i];
if (__predict_false(rxbuf->buf == NULL)) {
mp = ixgbe_getcl();
if (mp == NULL) {
IXGBE_EVC_ADD(&rxr->no_mbuf, 1);
goto update;
}
mp->m_pkthdr.len = mp->m_len = rxr->mbuf_sz;
IXGBE_M_ADJ(sc, rxr, mp);
} else
mp = rxbuf->buf;
if ((rxbuf->flags & IXGBE_RX_COPY) == 0) {
ixgbe_dmamap_unload(rxr->ptag, rxbuf->pmap);
error = bus_dmamap_load_mbuf(rxr->ptag->dt_dmat,
rxbuf->pmap, mp, BUS_DMA_NOWAIT);
if (__predict_false(error != 0)) {
device_printf(sc->dev, "Refresh mbufs: "
"payload dmamap load failure - %d\n",
error);
m_free(mp);
rxbuf->buf = NULL;
goto update;
}
rxbuf->buf = mp;
bus_dmamap_sync(rxr->ptag->dt_dmat, rxbuf->pmap,
0, mp->m_pkthdr.len, BUS_DMASYNC_PREREAD);
rxbuf->addr = rxr->rx_base[i].read.pkt_addr =
htole64(rxbuf->pmap->dm_segs[0].ds_addr);
} else {
rxr->rx_base[i].read.pkt_addr = rxbuf->addr;
rxbuf->flags &= ~IXGBE_RX_COPY;
}
refreshed = true;
rxr->next_to_refresh = i;
if (++i == rxr->num_desc)
i = 0;
}
update:
if (refreshed)
IXGBE_WRITE_REG(&sc->hw, rxr->tail, rxr->next_to_refresh);
return;
}
static int
ixgbe_allocate_receive_buffers(struct rx_ring *rxr)
{
struct ixgbe_softc *sc = rxr->sc;
device_t dev = sc->dev;
struct ixgbe_rx_buf *rxbuf;
int bsize, error;
bsize = sizeof(struct ixgbe_rx_buf) * rxr->num_desc;
rxr->rx_buffers = kmem_zalloc(bsize, KM_SLEEP);
error = ixgbe_dma_tag_create(
sc->osdep.dmat,
1,
0,
MJUM16BYTES,
1,
MJUM16BYTES,
0,
&rxr->ptag);
if (error != 0) {
aprint_error_dev(dev, "Unable to create RX DMA tag\n");
goto fail;
}
for (int i = 0; i < rxr->num_desc; i++, rxbuf++) {
rxbuf = &rxr->rx_buffers[i];
error = ixgbe_dmamap_create(rxr->ptag, 0, &rxbuf->pmap);
if (error) {
aprint_error_dev(dev, "Unable to create RX dma map\n");
goto fail;
}
}
return (0);
fail:
ixgbe_free_receive_structures(sc);
return (error);
}
static void
ixgbe_free_receive_ring(struct rx_ring *rxr)
{
for (int i = 0; i < rxr->num_desc; i++) {
ixgbe_rx_discard(rxr, i);
}
}
static int
ixgbe_setup_receive_ring(struct rx_ring *rxr)
{
struct ixgbe_softc *sc;
struct ixgbe_rx_buf *rxbuf;
#ifdef LRO
struct ifnet *ifp;
struct lro_ctrl *lro = &rxr->lro;
#endif
#ifdef DEV_NETMAP
struct netmap_sc *na = NA(rxr->sc->ifp);
struct netmap_slot *slot;
#endif
int rsize, error = 0;
sc = rxr->sc;
#ifdef LRO
ifp = sc->ifp;
#endif
IXGBE_RX_LOCK(rxr);
#ifdef DEV_NETMAP
if (sc->feat_en & IXGBE_FEATURE_NETMAP)
slot = netmap_reset(na, NR_RX, rxr->me, 0);
#endif
rsize = sc->num_rx_desc * sizeof(union ixgbe_adv_rx_desc);
KASSERT((rsize % DBA_ALIGN) == 0);
bzero((void *)rxr->rx_base, rsize);
rxr->mbuf_sz = sc->rx_mbuf_sz;
ixgbe_free_receive_ring(rxr);
for (int i = 0; i < rxr->num_desc; i++) {
struct mbuf *mp;
rxbuf = &rxr->rx_buffers[i];
#ifdef DEV_NETMAP
if ((sc->feat_en & IXGBE_FEATURE_NETMAP) && slot) {
int sj = netmap_idx_n2k(na->rx_rings[rxr->me], i);
uint64_t paddr;
void *addr;
addr = PNMB(na, slot + sj, &paddr);
netmap_load_map(na, rxr->ptag, rxbuf->pmap, addr);
rxr->rx_base[i].read.pkt_addr = htole64(paddr);
rxbuf->addr = htole64(paddr);
continue;
}
#endif
rxbuf->flags = 0;
rxbuf->buf = ixgbe_getcl();
if (rxbuf->buf == NULL) {
IXGBE_EVC_ADD(&rxr->no_mbuf, 1);
error = ENOBUFS;
goto fail;
}
mp = rxbuf->buf;
mp->m_pkthdr.len = mp->m_len = rxr->mbuf_sz;
IXGBE_M_ADJ(sc, rxr, mp);
error = bus_dmamap_load_mbuf(rxr->ptag->dt_dmat, rxbuf->pmap,
mp, BUS_DMA_NOWAIT);
if (error != 0) {
m_freem(mp);
rxbuf->buf = NULL;
goto fail;
}
bus_dmamap_sync(rxr->ptag->dt_dmat, rxbuf->pmap,
0, mp->m_pkthdr.len, BUS_DMASYNC_PREREAD);
rxr->rx_base[i].read.pkt_addr =
htole64(rxbuf->pmap->dm_segs[0].ds_addr);
rxbuf->addr = htole64(rxbuf->pmap->dm_segs[0].ds_addr);
}
rxr->next_to_check = 0;
rxr->next_to_refresh = sc->num_rx_desc - 1;
#ifdef LRO
rxr->lro_enabled = FALSE;
#endif
rxr->discard_multidesc = false;
IXGBE_EVC_STORE(&rxr->rx_copies, 0);
#if 0
IXGBE_EVC_STORE(&rxr->rx_bytes, 0);
#if 1
IXGBE_EVC_STORE(&rxr->rx_packets, 0);
#endif
#endif
rxr->vtag_strip = FALSE;
ixgbe_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
#ifdef RSC
if (ixgbe_rsc_enable)
ixgbe_setup_hw_rsc(rxr);
#endif
#ifdef LRO
#ifdef RSC
else
#endif
if (ifp->if_capenable & IFCAP_LRO) {
device_t dev = sc->dev;
int err = tcp_lro_init(lro);
if (err) {
device_printf(dev, "LRO Initialization failed!\n");
goto fail;
}
INIT_DEBUGOUT("RX Soft LRO Initialized\n");
rxr->lro_enabled = TRUE;
lro->ifp = sc->ifp;
}
#endif
IXGBE_RX_UNLOCK(rxr);
return (0);
fail:
ixgbe_free_receive_ring(rxr);
IXGBE_RX_UNLOCK(rxr);
return (error);
}
int
ixgbe_setup_receive_structures(struct ixgbe_softc *sc)
{
struct rx_ring *rxr = sc->rx_rings;
int j;
INIT_DEBUGOUT("ixgbe_setup_receive_structures");
for (j = 0; j < sc->num_queues; j++, rxr++)
if (ixgbe_setup_receive_ring(rxr))
goto fail;
return (0);
fail:
for (int i = 0; i < j; ++i) {
rxr = &sc->rx_rings[i];
IXGBE_RX_LOCK(rxr);
ixgbe_free_receive_ring(rxr);
IXGBE_RX_UNLOCK(rxr);
}
return (ENOBUFS);
}
void
ixgbe_free_receive_structures(struct ixgbe_softc *sc)
{
struct rx_ring *rxr = sc->rx_rings;
INIT_DEBUGOUT("ixgbe_free_receive_structures: begin");
for (int i = 0; i < sc->num_queues; i++, rxr++) {
ixgbe_free_receive_buffers(rxr);
#ifdef LRO
tcp_lro_free(&rxr->lro);
#endif
ixgbe_dma_free(sc, &rxr->rxdma);
IXGBE_RX_LOCK_DESTROY(rxr);
}
kmem_free(sc->rx_rings, sizeof(struct rx_ring) * sc->num_queues);
}
static void
ixgbe_free_receive_buffers(struct rx_ring *rxr)
{
struct ixgbe_softc *sc = rxr->sc;
struct ixgbe_rx_buf *rxbuf;
INIT_DEBUGOUT("ixgbe_free_receive_buffers: begin");
if (rxr->rx_buffers != NULL) {
for (int i = 0; i < sc->num_rx_desc; i++) {
rxbuf = &rxr->rx_buffers[i];
ixgbe_rx_discard(rxr, i);
if (rxbuf->pmap != NULL) {
ixgbe_dmamap_destroy(rxr->ptag, rxbuf->pmap);
rxbuf->pmap = NULL;
}
}
if (rxr->rx_buffers != NULL) {
kmem_free(rxr->rx_buffers,
sizeof(struct ixgbe_rx_buf) * rxr->num_desc);
rxr->rx_buffers = NULL;
}
}
if (rxr->ptag != NULL) {
ixgbe_dma_tag_destroy(rxr->ptag);
rxr->ptag = NULL;
}
return;
}
static __inline void
ixgbe_rx_input(struct rx_ring *rxr, struct ifnet *ifp, struct mbuf *m,
u32 ptype)
{
struct ixgbe_softc *sc = ifp->if_softc;
#ifdef LRO
struct ethercom *ec = &sc->osdep.ec;
if (rxr->lro_enabled &&
(ec->ec_capenable & ETHERCAP_VLAN_HWTAGGING) != 0 &&
(ptype & IXGBE_RXDADV_PKTTYPE_ETQF) == 0 &&
((ptype & (IXGBE_RXDADV_PKTTYPE_IPV4 | IXGBE_RXDADV_PKTTYPE_TCP)) ==
(IXGBE_RXDADV_PKTTYPE_IPV4 | IXGBE_RXDADV_PKTTYPE_TCP) ||
(ptype & (IXGBE_RXDADV_PKTTYPE_IPV6 | IXGBE_RXDADV_PKTTYPE_TCP)) ==
(IXGBE_RXDADV_PKTTYPE_IPV6 | IXGBE_RXDADV_PKTTYPE_TCP)) &&
(m->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) ==
(CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) {
if (rxr->lro.lro_cnt != 0)
if (tcp_lro_rx(&rxr->lro, m, 0) == 0)
return;
}
#endif
if_percpuq_enqueue(sc->ipq, m);
}
static __inline void
ixgbe_rx_discard(struct rx_ring *rxr, int i)
{
struct ixgbe_rx_buf *rbuf;
rbuf = &rxr->rx_buffers[i];
if (rbuf->fmp != NULL) {
bus_dmamap_sync(rxr->ptag->dt_dmat, rbuf->pmap, 0,
rbuf->buf->m_pkthdr.len, BUS_DMASYNC_POSTREAD);
ixgbe_dmamap_unload(rxr->ptag, rbuf->pmap);
m_freem(rbuf->fmp);
rbuf->fmp = NULL;
rbuf->buf = NULL;
} else if (rbuf->buf) {
bus_dmamap_sync(rxr->ptag->dt_dmat, rbuf->pmap, 0,
rbuf->buf->m_pkthdr.len, BUS_DMASYNC_POSTREAD);
ixgbe_dmamap_unload(rxr->ptag, rbuf->pmap);
m_free(rbuf->buf);
rbuf->buf = NULL;
}
rbuf->flags = 0;
return;
}
bool
ixgbe_rxeof(struct ix_queue *que)
{
struct ixgbe_softc *sc = que->sc;
struct rx_ring *rxr = que->rxr;
struct ifnet *ifp = sc->ifp;
#ifdef LRO
struct lro_ctrl *lro = &rxr->lro;
#endif
union ixgbe_adv_rx_desc *cur;
struct ixgbe_rx_buf *rbuf, *nbuf;
int i, nextp, processed = 0;
u32 staterr = 0;
u32 loopcount = 0, numdesc;
u32 limit = sc->rx_process_limit;
u32 rx_copy_len = sc->rx_copy_len;
bool discard_multidesc = rxr->discard_multidesc;
bool wraparound = false;
unsigned int syncremain;
#ifdef RSS
u16 pkt_info;
#endif
IXGBE_RX_LOCK(rxr);
#ifdef DEV_NETMAP
if (sc->feat_en & IXGBE_FEATURE_NETMAP) {
if (netmap_rx_irq(ifp, rxr->me, &processed)) {
IXGBE_RX_UNLOCK(rxr);
return (FALSE);
}
}
#endif
if ((rxr->next_to_check + limit) <= rxr->num_desc) {
numdesc = limit;
syncremain = 0;
} else {
numdesc = rxr->num_desc - rxr->next_to_check;
syncremain = limit - numdesc;
}
bus_dmamap_sync(rxr->rxdma.dma_tag->dt_dmat,
rxr->rxdma.dma_map,
sizeof(union ixgbe_adv_rx_desc) * rxr->next_to_check,
sizeof(union ixgbe_adv_rx_desc) * numdesc,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
for (i = rxr->next_to_check;
(loopcount < limit) || (discard_multidesc == true);) {
struct mbuf *sendmp, *mp;
struct mbuf *newmp;
#ifdef RSC
u32 rsc;
#endif
u32 ptype;
u16 len;
u16 vtag = 0;
bool eop;
bool discard = false;
if (wraparound) {
KASSERT(syncremain != 0);
numdesc = syncremain;
wraparound = false;
} else if (__predict_false(loopcount >= limit)) {
KASSERT(discard_multidesc == true);
numdesc = 1;
} else
numdesc = 0;
if (numdesc != 0)
bus_dmamap_sync(rxr->rxdma.dma_tag->dt_dmat,
rxr->rxdma.dma_map, 0,
sizeof(union ixgbe_adv_rx_desc) * numdesc,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
cur = &rxr->rx_base[i];
staterr = le32toh(cur->wb.upper.status_error);
#ifdef RSS
pkt_info = le16toh(cur->wb.lower.lo_dword.hs_rss.pkt_info);
#endif
if ((staterr & IXGBE_RXD_STAT_DD) == 0)
break;
loopcount++;
sendmp = newmp = NULL;
nbuf = NULL;
#ifdef RSC
rsc = 0;
#endif
cur->wb.upper.status_error = 0;
rbuf = &rxr->rx_buffers[i];
mp = rbuf->buf;
len = le16toh(cur->wb.upper.length);
ptype = le32toh(cur->wb.lower.lo_dword.data) &
IXGBE_RXDADV_PKTTYPE_MASK;
eop = ((staterr & IXGBE_RXD_STAT_EOP) != 0);
if (eop && (staterr & IXGBE_RXDADV_ERR_FRAME_ERR_MASK) != 0) {
#if __FreeBSD_version >= 1100036
if (sc->feat_en & IXGBE_FEATURE_VF)
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
#endif
IXGBE_EVC_ADD(&rxr->rx_discarded, 1);
ixgbe_rx_discard(rxr, i);
discard_multidesc = false;
goto next_desc;
}
if (__predict_false(discard_multidesc))
discard = true;
else {
if ((rbuf->fmp == NULL) &&
eop && (len <= rx_copy_len)) {
sendmp = m_gethdr(M_NOWAIT, MT_DATA);
if (__predict_false(sendmp == NULL)) {
IXGBE_EVC_ADD(&rxr->no_mbuf, 1);
discard = true;
}
} else {
newmp = ixgbe_getcl();
if (__predict_false(newmp == NULL)) {
IXGBE_EVC_ADD(&rxr->no_mbuf, 1);
discard = true;
}
}
}
if (__predict_false(discard)) {
cur->read.pkt_addr = rbuf->addr;
m_freem(rbuf->fmp);
rbuf->fmp = NULL;
if (!eop) {
discard_multidesc = true;
} else
discard_multidesc = false;
goto next_desc;
}
discard_multidesc = false;
bus_dmamap_sync(rxr->ptag->dt_dmat, rbuf->pmap, 0,
rbuf->buf->m_pkthdr.len, BUS_DMASYNC_POSTREAD);
if (!eop) {
#ifdef RSC
if (rxr->hw_rsc == TRUE) {
rsc = ixgbe_rsc_count(cur);
rxr->rsc_num += (rsc - 1);
}
if (rsc) {
nextp = ((staterr & IXGBE_RXDADV_NEXTP_MASK) >>
IXGBE_RXDADV_NEXTP_SHIFT);
} else
#endif
{
nextp = i + 1;
if (nextp == sc->num_rx_desc)
nextp = 0;
}
nbuf = &rxr->rx_buffers[nextp];
prefetch(nbuf);
}
if (rbuf->fmp != NULL) {
sendmp = rbuf->fmp;
newmp->m_pkthdr.len = newmp->m_len = rxr->mbuf_sz;
IXGBE_M_ADJ(sc, rxr, newmp);
rbuf->buf = newmp;
rbuf->fmp = NULL;
mp->m_len = len;
mp->m_flags &= ~M_PKTHDR;
sendmp->m_pkthdr.len += mp->m_len;
} else {
if (eop && (len <= rx_copy_len)) {
sendmp->m_data += ETHER_ALIGN;
memcpy(mtod(sendmp, void *),
mtod(mp, void *), len);
IXGBE_EVC_ADD(&rxr->rx_copies, 1);
rbuf->flags |= IXGBE_RX_COPY;
} else {
newmp->m_pkthdr.len = newmp->m_len
= rxr->mbuf_sz;
IXGBE_M_ADJ(sc, rxr, newmp);
rbuf->buf = newmp;
rbuf->fmp = NULL;
sendmp = mp;
}
sendmp->m_pkthdr.len = sendmp->m_len = len;
}
++processed;
if (eop == 0) {
nbuf->fmp = sendmp;
sendmp = NULL;
mp->m_next = nbuf->buf;
} else {
m_set_rcvif(sendmp, ifp);
++rxr->packets;
IXGBE_EVC_ADD(&rxr->rx_packets, 1);
rxr->bytes += sendmp->m_pkthdr.len;
IXGBE_EVC_ADD(&rxr->rx_bytes, sendmp->m_pkthdr.len);
if ((rxr->vtag_strip) && (staterr & IXGBE_RXD_STAT_VP))
vtag = le16toh(cur->wb.upper.vlan);
if (vtag) {
vlan_set_tag(sendmp, vtag);
}
if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
ixgbe_rx_checksum(staterr, sendmp, ptype,
&sc->stats.pf);
}
#if 0
if (sc->num_queues > 1) {
sendmp->m_pkthdr.flowid =
le32toh(cur->wb.lower.hi_dword.rss);
switch (pkt_info & IXGBE_RXDADV_RSSTYPE_MASK) {
case IXGBE_RXDADV_RSSTYPE_IPV4:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_IPV4);
break;
case IXGBE_RXDADV_RSSTYPE_IPV4_TCP:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_TCP_IPV4);
break;
case IXGBE_RXDADV_RSSTYPE_IPV6:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_IPV6);
break;
case IXGBE_RXDADV_RSSTYPE_IPV6_TCP:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_TCP_IPV6);
break;
case IXGBE_RXDADV_RSSTYPE_IPV6_EX:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_IPV6_EX);
break;
case IXGBE_RXDADV_RSSTYPE_IPV6_TCP_EX:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_TCP_IPV6_EX);
break;
#if __FreeBSD_version > 1100000
case IXGBE_RXDADV_RSSTYPE_IPV4_UDP:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_UDP_IPV4);
break;
case IXGBE_RXDADV_RSSTYPE_IPV6_UDP:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_UDP_IPV6);
break;
case IXGBE_RXDADV_RSSTYPE_IPV6_UDP_EX:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_RSS_UDP_IPV6_EX);
break;
#endif
default:
M_HASHTYPE_SET(sendmp,
M_HASHTYPE_OPAQUE_HASH);
}
} else {
sendmp->m_pkthdr.flowid = que->msix;
M_HASHTYPE_SET(sendmp, M_HASHTYPE_OPAQUE);
}
#endif
}
next_desc:
ixgbe_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
if (++i == rxr->num_desc) {
wraparound = true;
i = 0;
}
rxr->next_to_check = i;
if (sendmp != NULL)
ixgbe_rx_input(rxr, ifp, sendmp, ptype);
if (processed == 8) {
ixgbe_refresh_mbufs(rxr, i);
processed = 0;
}
}
rxr->discard_multidesc = discard_multidesc;
if (ixgbe_rx_unrefreshed(rxr))
ixgbe_refresh_mbufs(rxr, i);
IXGBE_RX_UNLOCK(rxr);
#ifdef LRO
tcp_lro_flush_all(lro);
#endif
if ((staterr & IXGBE_RXD_STAT_DD) != 0)
return (TRUE);
return (FALSE);
}
static void
ixgbe_rx_checksum(u32 staterr, struct mbuf * mp, u32 ptype,
struct ixgbe_hw_stats *stats)
{
u16 status = (u16)staterr;
u8 errors = (u8)(staterr >> 24);
#if 0
bool sctp = false;
if ((ptype & IXGBE_RXDADV_PKTTYPE_ETQF) == 0 &&
(ptype & IXGBE_RXDADV_PKTTYPE_SCTP) != 0)
sctp = true;
#endif
if (status & IXGBE_RXD_STAT_IPCS) {
IXGBE_EVC_ADD(&stats->ipcs, 1);
if (!(errors & IXGBE_RXD_ERR_IPE)) {
mp->m_pkthdr.csum_flags = M_CSUM_IPv4;
} else {
IXGBE_EVC_ADD(&stats->ipcs_bad, 1);
mp->m_pkthdr.csum_flags = M_CSUM_IPv4|M_CSUM_IPv4_BAD;
}
}
if (status & IXGBE_RXD_STAT_L4CS) {
IXGBE_EVC_ADD(&stats->l4cs, 1);
int type = M_CSUM_TCPv4|M_CSUM_TCPv6|M_CSUM_UDPv4|M_CSUM_UDPv6;
if (!(errors & IXGBE_RXD_ERR_TCPE)) {
mp->m_pkthdr.csum_flags |= type;
} else {
IXGBE_EVC_ADD(&stats->l4cs_bad, 1);
mp->m_pkthdr.csum_flags |= type | M_CSUM_TCP_UDP_BAD;
}
}
}
int
ixgbe_dma_malloc(struct ixgbe_softc *sc, const bus_size_t size,
struct ixgbe_dma_alloc *dma, const int mapflags)
{
device_t dev = sc->dev;
int r, rsegs;
r = ixgbe_dma_tag_create(
sc->osdep.dmat,
DBA_ALIGN,
0,
size,
1,
size,
BUS_DMA_ALLOCNOW,
&dma->dma_tag);
if (r != 0) {
aprint_error_dev(dev,
"%s: ixgbe_dma_tag_create failed; error %d\n", __func__,
r);
goto fail_0;
}
r = bus_dmamem_alloc(dma->dma_tag->dt_dmat, size,
dma->dma_tag->dt_alignment, dma->dma_tag->dt_boundary,
&dma->dma_seg, 1, &rsegs, BUS_DMA_NOWAIT);
if (r != 0) {
aprint_error_dev(dev,
"%s: bus_dmamem_alloc failed; error %d\n", __func__, r);
goto fail_1;
}
r = bus_dmamem_map(dma->dma_tag->dt_dmat, &dma->dma_seg, rsegs,
size, &dma->dma_vaddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
if (r != 0) {
aprint_error_dev(dev, "%s: bus_dmamem_map failed; error %d\n",
__func__, r);
goto fail_2;
}
r = ixgbe_dmamap_create(dma->dma_tag, 0, &dma->dma_map);
if (r != 0) {
aprint_error_dev(dev, "%s: bus_dmamem_map failed; error %d\n",
__func__, r);
goto fail_3;
}
r = bus_dmamap_load(dma->dma_tag->dt_dmat, dma->dma_map,
dma->dma_vaddr, size, NULL, mapflags | BUS_DMA_NOWAIT);
if (r != 0) {
aprint_error_dev(dev, "%s: bus_dmamap_load failed; error %d\n",
__func__, r);
goto fail_4;
}
dma->dma_paddr = dma->dma_map->dm_segs[0].ds_addr;
dma->dma_size = size;
return 0;
fail_4:
ixgbe_dmamap_destroy(dma->dma_tag, dma->dma_map);
fail_3:
bus_dmamem_unmap(dma->dma_tag->dt_dmat, dma->dma_vaddr, size);
fail_2:
bus_dmamem_free(dma->dma_tag->dt_dmat, &dma->dma_seg, rsegs);
fail_1:
ixgbe_dma_tag_destroy(dma->dma_tag);
fail_0:
return (r);
}
void
ixgbe_dma_free(struct ixgbe_softc *sc, struct ixgbe_dma_alloc *dma)
{
bus_dmamap_sync(dma->dma_tag->dt_dmat, dma->dma_map, 0, dma->dma_size,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
ixgbe_dmamap_unload(dma->dma_tag, dma->dma_map);
bus_dmamem_unmap(dma->dma_tag->dt_dmat, dma->dma_vaddr, dma->dma_size);
bus_dmamem_free(dma->dma_tag->dt_dmat, &dma->dma_seg, 1);
ixgbe_dma_tag_destroy(dma->dma_tag);
}
int
ixgbe_allocate_queues(struct ixgbe_softc *sc)
{
device_t dev = sc->dev;
struct ix_queue *que;
struct tx_ring *txr;
struct rx_ring *rxr;
int rsize, tsize, error = IXGBE_SUCCESS;
int txconf = 0, rxconf = 0;
sc->queues = kmem_zalloc(sizeof(struct ix_queue) * sc->num_queues,
KM_SLEEP);
sc->tx_rings = kmem_zalloc(sizeof(struct tx_ring) * sc->num_queues,
KM_SLEEP);
sc->rx_rings = kmem_zalloc(sizeof(struct rx_ring) * sc->num_queues,
KM_SLEEP);
tsize = sc->num_tx_desc * sizeof(union ixgbe_adv_tx_desc);
KASSERT((tsize % DBA_ALIGN) == 0);
for (int i = 0; i < sc->num_queues; i++, txconf++) {
txr = &sc->tx_rings[i];
txr->sc = sc;
txr->txr_interq = NULL;
#ifdef PCI_IOV
txr->me = ixgbe_vf_que_index(sc->iov_mode, sc->pool,
i);
#else
txr->me = i;
#endif
txr->num_desc = sc->num_tx_desc;
mutex_init(&txr->tx_mtx, MUTEX_DEFAULT, IPL_NET);
if (ixgbe_dma_malloc(sc, tsize, &txr->txdma,
BUS_DMA_NOWAIT)) {
aprint_error_dev(dev,
"Unable to allocate TX Descriptor memory\n");
error = ENOMEM;
goto err_tx_desc;
}
txr->tx_base = (union ixgbe_adv_tx_desc *)txr->txdma.dma_vaddr;
bzero((void *)txr->tx_base, tsize);
if (ixgbe_allocate_transmit_buffers(txr)) {
aprint_error_dev(dev,
"Critical Failure setting up transmit buffers\n");
error = ENOMEM;
goto err_tx_desc;
}
if (!(sc->feat_en & IXGBE_FEATURE_LEGACY_TX)) {
txr->txr_interq = pcq_create(IXGBE_BR_SIZE, KM_SLEEP);
if (txr->txr_interq == NULL) {
aprint_error_dev(dev,
"Critical Failure setting up buf ring\n");
error = ENOMEM;
goto err_tx_desc;
}
}
}
rsize = sc->num_rx_desc * sizeof(union ixgbe_adv_rx_desc);
KASSERT((rsize % DBA_ALIGN) == 0);
for (int i = 0; i < sc->num_queues; i++, rxconf++) {
rxr = &sc->rx_rings[i];
rxr->sc = sc;
#ifdef PCI_IOV
rxr->me = ixgbe_vf_que_index(sc->iov_mode, sc->pool,
i);
#else
rxr->me = i;
#endif
rxr->num_desc = sc->num_rx_desc;
mutex_init(&rxr->rx_mtx, MUTEX_DEFAULT, IPL_NET);
if (ixgbe_dma_malloc(sc, rsize, &rxr->rxdma,
BUS_DMA_NOWAIT)) {
aprint_error_dev(dev,
"Unable to allocate RxDescriptor memory\n");
error = ENOMEM;
goto err_rx_desc;
}
rxr->rx_base = (union ixgbe_adv_rx_desc *)rxr->rxdma.dma_vaddr;
bzero((void *)rxr->rx_base, rsize);
if (ixgbe_allocate_receive_buffers(rxr)) {
aprint_error_dev(dev,
"Critical Failure setting up receive buffers\n");
error = ENOMEM;
goto err_rx_desc;
}
}
for (int i = 0; i < sc->num_queues; i++) {
que = &sc->queues[i];
que->sc = sc;
que->me = i;
que->txr = &sc->tx_rings[i];
que->rxr = &sc->rx_rings[i];
mutex_init(&que->dc_mtx, MUTEX_DEFAULT, IPL_NET);
que->disabled_count = 0;
}
return (0);
err_rx_desc:
for (rxr = sc->rx_rings; rxconf > 0; rxr++, rxconf--)
ixgbe_dma_free(sc, &rxr->rxdma);
err_tx_desc:
for (txr = sc->tx_rings; txconf > 0; txr++, txconf--)
ixgbe_dma_free(sc, &txr->txdma);
kmem_free(sc->rx_rings, sizeof(struct rx_ring) * sc->num_queues);
kmem_free(sc->tx_rings, sizeof(struct tx_ring) * sc->num_queues);
kmem_free(sc->queues, sizeof(struct ix_queue) * sc->num_queues);
return (error);
}
void
ixgbe_free_queues(struct ixgbe_softc *sc)
{
struct ix_queue *que;
int i;
ixgbe_free_transmit_structures(sc);
ixgbe_free_receive_structures(sc);
for (i = 0; i < sc->num_queues; i++) {
que = &sc->queues[i];
mutex_destroy(&que->dc_mtx);
}
kmem_free(sc->queues, sizeof(struct ix_queue) * sc->num_queues);
}