sfxge: support FATSOv2

Reviewed by:    gnn
Sponsored by:   Solarflare Communications, Inc.
MFC after:      2 days
Differential Revision: https://reviews.freebsd.org/D4934
This commit is contained in:
arybchik 2016-01-15 06:25:26 +00:00
parent ecc02c6407
commit 8cbcec3cf7
4 changed files with 171 additions and 75 deletions

View File

@ -121,8 +121,10 @@ If a packet is dropped, the
counter is incremented and the local sender receives ENOBUFS.
The value must be greater than or equal to 0.
.It Va hw.sfxge.tso_fw_assisted
Enable/disable usage of FW-assisted TSO if supported by NIC firmware.
Enabled by default.
Bitmask to enable/disable usage of FW-assisted TSO version if supported
by NIC firmware.
FATSOv1 (bit 0) and FATSOv2 (bit 1) are supported.
All enabled by default.
.It Va hw.sfxge.N.max_rss_channels
The maximum number of allocated RSS channels for the Nth adapter.
If set to 0 or unset, the number of channels is determined by the number

View File

@ -280,7 +280,10 @@ struct sfxge_softc {
unsigned int rxq_count;
unsigned int txq_count;
int tso_fw_assisted;
unsigned int tso_fw_assisted;
#define SFXGE_FATSOV1 (1 << 0)
#define SFXGE_FATSOV2 (1 << 1)
#if EFSYS_OPT_MCDI_LOGGING
int mcdi_logging;
#endif

View File

@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/limits.h>
#include <net/bpf.h>
#include <net/ethernet.h>
@ -96,11 +97,11 @@ SYSCTL_INT(_hw_sfxge, OID_AUTO, tx_dpl_put_max, CTLFLAG_RDTUN,
"Maximum number of any packets in deferred packet put-list");
#define SFXGE_PARAM_TSO_FW_ASSISTED SFXGE_PARAM(tso_fw_assisted)
static int sfxge_tso_fw_assisted = 1;
static int sfxge_tso_fw_assisted = (SFXGE_FATSOV1 | SFXGE_FATSOV2);
TUNABLE_INT(SFXGE_PARAM_TSO_FW_ASSISTED, &sfxge_tso_fw_assisted);
SYSCTL_INT(_hw_sfxge, OID_AUTO, tso_fw_assisted, CTLFLAG_RDTUN,
&sfxge_tso_fw_assisted, 0,
"Use FW-assisted TSO if supported by NIC firmware");
"Bitmask of FW-assisted TSO allowed to use if supported by NIC firmware");
static const struct {
@ -850,6 +851,8 @@ struct sfxge_tso_state {
unsigned out_len; /* Remaining length in current segment */
unsigned seqnum; /* Current sequence number */
unsigned packet_space; /* Remaining space in current packet */
unsigned segs_space; /* Remaining number of DMA segments
for the packet (FATSOv2 only) */
/* Input position */
uint64_t dma_addr; /* DMA address of current position */
@ -952,7 +955,7 @@ static void tso_start(struct sfxge_txq *txq, struct sfxge_tso_state *tso,
struct tcphdr th_copy;
#endif
tso->fw_assisted = txq->sc->tso_fw_assisted;
tso->fw_assisted = txq->tso_fw_assisted;
tso->mbuf = mbuf;
/* Find network protocol and header */
@ -1059,6 +1062,8 @@ static void tso_fill_packet_with_fragment(struct sfxge_txq *txq,
{
efx_desc_t *desc;
int n;
uint64_t dma_addr = tso->dma_addr;
boolean_t eop;
if (tso->in_len == 0 || tso->packet_space == 0)
return;
@ -1066,20 +1071,38 @@ static void tso_fill_packet_with_fragment(struct sfxge_txq *txq,
KASSERT(tso->in_len > 0, ("TSO input length went negative"));
KASSERT(tso->packet_space > 0, ("TSO packet space went negative"));
n = min(tso->in_len, tso->packet_space);
if (tso->fw_assisted & SFXGE_FATSOV2) {
n = tso->in_len;
tso->out_len -= n;
tso->seqnum += n;
tso->in_len = 0;
if (n < tso->packet_space) {
tso->packet_space -= n;
tso->segs_space--;
} else {
tso->packet_space = tso->seg_size -
(n - tso->packet_space) % tso->seg_size;
tso->segs_space =
EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX - 1 -
(tso->packet_space != tso->seg_size);
}
} else {
n = min(tso->in_len, tso->packet_space);
tso->packet_space -= n;
tso->out_len -= n;
tso->dma_addr += n;
tso->in_len -= n;
}
tso->packet_space -= n;
tso->out_len -= n;
tso->in_len -= n;
/*
* It is OK to use binary OR below to avoid extra branching
* since all conditions may always be checked.
*/
eop = (tso->out_len == 0) | (tso->packet_space == 0) |
(tso->segs_space == 0);
desc = &txq->pend_desc[txq->n_pend_desc++];
efx_tx_qdesc_dma_create(txq->common,
tso->dma_addr,
n,
tso->out_len == 0 || tso->packet_space == 0,
desc);
tso->dma_addr += n;
efx_tx_qdesc_dma_create(txq->common, dma_addr, n, eop, desc);
}
/* Callback from bus_dmamap_load() for long TSO headers. */
@ -1112,28 +1135,47 @@ static int tso_start_new_packet(struct sfxge_txq *txq,
int rc;
if (tso->fw_assisted) {
uint8_t tcp_flags = tso->tcp_flags;
if (tso->fw_assisted & SFXGE_FATSOV2) {
/* Add 2 FATSOv2 option descriptors */
desc = &txq->pend_desc[txq->n_pend_desc];
efx_tx_qdesc_tso2_create(txq->common,
tso->packet_id,
tso->seqnum,
tso->seg_size,
desc,
EFX_TX_FATSOV2_OPT_NDESCS);
desc += EFX_TX_FATSOV2_OPT_NDESCS;
txq->n_pend_desc += EFX_TX_FATSOV2_OPT_NDESCS;
KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0"));
id = (id + EFX_TX_FATSOV2_OPT_NDESCS) & txq->ptr_mask;
if (tso->out_len > tso->seg_size)
tcp_flags &= ~(TH_FIN | TH_PUSH);
tso->segs_space =
EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX - 1;
} else {
uint8_t tcp_flags = tso->tcp_flags;
/* TSO option descriptor */
desc = &txq->pend_desc[txq->n_pend_desc++];
efx_tx_qdesc_tso_create(txq->common,
tso->packet_id,
tso->seqnum,
tcp_flags,
desc++);
KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0"));
id = (id + 1) & txq->ptr_mask;
if (tso->out_len > tso->seg_size)
tcp_flags &= ~(TH_FIN | TH_PUSH);
/* Add FATSOv1 option descriptor */
desc = &txq->pend_desc[txq->n_pend_desc++];
efx_tx_qdesc_tso_create(txq->common,
tso->packet_id,
tso->seqnum,
tcp_flags,
desc++);
KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0"));
id = (id + 1) & txq->ptr_mask;
tso->seqnum += tso->seg_size;
tso->segs_space = UINT_MAX;
}
/* Header DMA descriptor */
*desc = tso->header_desc;
txq->n_pend_desc++;
KASSERT(txq->stmp[id].flags == 0, ("stmp flags are not 0"));
id = (id + 1) & txq->ptr_mask;
tso->seqnum += tso->seg_size;
} else {
/* Allocate a DMA-mapped header buffer. */
if (__predict_true(tso->header_len <= TSOH_STD_SIZE)) {
@ -1215,6 +1257,8 @@ static int tso_start_new_packet(struct sfxge_txq *txq,
0,
desc);
id = (id + 1) & txq->ptr_mask;
tso->segs_space = UINT_MAX;
}
tso->packet_space = tso->seg_size;
txq->tso_packets++;
@ -1264,15 +1308,19 @@ sfxge_tx_queue_tso(struct sfxge_txq *txq, struct mbuf *mbuf,
}
/* End of packet? */
if (tso.packet_space == 0) {
if ((tso.packet_space == 0) | (tso.segs_space == 0)) {
unsigned int n_fatso_opt_desc =
(tso.fw_assisted & SFXGE_FATSOV2) ?
EFX_TX_FATSOV2_OPT_NDESCS :
(tso.fw_assisted & SFXGE_FATSOV1) ? 1 : 0;
/* If the queue is now full due to tiny MSS,
* or we can't create another header, discard
* the remainder of the input mbuf but do not
* roll back the work we have done.
*/
if (txq->n_pend_desc + tso.fw_assisted +
1 /* header */ + n_dma_seg >
txq->max_pkt_desc) {
if (txq->n_pend_desc + n_fatso_opt_desc +
1 /* header */ + n_dma_seg > txq->max_pkt_desc) {
txq->tso_pdrop_too_many++;
break;
}
@ -1407,12 +1455,67 @@ sfxge_tx_qstop(struct sfxge_softc *sc, unsigned int index)
SFXGE_TXQ_UNLOCK(txq);
}
/*
* Estimate maximum number of Tx descriptors required for TSO packet.
* With minimum MSS and maximum mbuf length we might need more (even
* than a ring-ful of descriptors), but this should not happen in
* practice except due to deliberate attack. In that case we will
* truncate the output at a packet boundary.
*/
static unsigned int
sfxge_tx_max_pkt_desc(const struct sfxge_softc *sc, enum sfxge_txq_type type,
unsigned int tso_fw_assisted)
{
/* One descriptor for every input fragment */
unsigned int max_descs = SFXGE_TX_MAPPING_MAX_SEG;
unsigned int sw_tso_max_descs;
unsigned int fa_tso_v1_max_descs = 0;
unsigned int fa_tso_v2_max_descs = 0;
/* VLAN tagging Tx option descriptor may be required */
if (efx_nic_cfg_get(sc->enp)->enc_hw_tx_insert_vlan_enabled)
max_descs++;
if (type == SFXGE_TXQ_IP_TCP_UDP_CKSUM) {
/*
* Plus header and payload descriptor for each output segment.
* Minus one since header fragment is already counted.
* Even if FATSO is used, we should be ready to fallback
* to do it in the driver.
*/
sw_tso_max_descs = SFXGE_TSO_MAX_SEGS * 2 - 1;
/* FW assisted TSOv1 requires one more descriptor per segment
* in comparison to SW TSO */
if (tso_fw_assisted & SFXGE_FATSOV1)
fa_tso_v1_max_descs =
sw_tso_max_descs + SFXGE_TSO_MAX_SEGS;
/* FW assisted TSOv2 requires 3 (2 FATSO plus header) extra
* descriptors per superframe limited by number of DMA fetches
* per packet. The first packet header is already counted.
*/
if (tso_fw_assisted & SFXGE_FATSOV2) {
fa_tso_v2_max_descs =
howmany(SFXGE_TX_MAPPING_MAX_SEG,
EFX_TX_FATSOV2_DMA_SEGS_PER_PKT_MAX - 1) *
(EFX_TX_FATSOV2_OPT_NDESCS + 1) - 1;
}
max_descs += MAX(sw_tso_max_descs,
MAX(fa_tso_v1_max_descs, fa_tso_v2_max_descs));
}
return (max_descs);
}
static int
sfxge_tx_qstart(struct sfxge_softc *sc, unsigned int index)
{
struct sfxge_txq *txq;
efsys_mem_t *esmp;
uint16_t flags;
unsigned int tso_fw_assisted;
struct sfxge_evq *evq;
unsigned int desc_index;
int rc;
@ -1434,6 +1537,7 @@ sfxge_tx_qstart(struct sfxge_softc *sc, unsigned int index)
return (rc);
/* Determine the kind of queue we are creating. */
tso_fw_assisted = 0;
switch (txq->type) {
case SFXGE_TXQ_NON_CKSUM:
flags = 0;
@ -1443,6 +1547,9 @@ sfxge_tx_qstart(struct sfxge_softc *sc, unsigned int index)
break;
case SFXGE_TXQ_IP_TCP_UDP_CKSUM:
flags = EFX_TXQ_CKSUM_IPV4 | EFX_TXQ_CKSUM_TCPUDP;
tso_fw_assisted = sc->tso_fw_assisted;
if (tso_fw_assisted & SFXGE_FATSOV2)
flags |= EFX_TXQ_FATSOV2;
break;
default:
KASSERT(0, ("Impossible TX queue"));
@ -1453,8 +1560,19 @@ sfxge_tx_qstart(struct sfxge_softc *sc, unsigned int index)
/* Create the common code transmit queue. */
if ((rc = efx_tx_qcreate(sc->enp, index, txq->type, esmp,
sc->txq_entries, txq->buf_base_id, flags, evq->common,
&txq->common, &desc_index)) != 0)
goto fail;
&txq->common, &desc_index)) != 0) {
/* Retry if no FATSOv2 resources, otherwise fail */
if ((rc != ENOSPC) || (~flags & EFX_TXQ_FATSOV2))
goto fail;
/* Looks like all FATSOv2 contexts are used */
flags &= ~EFX_TXQ_FATSOV2;
tso_fw_assisted &= ~SFXGE_FATSOV2;
if ((rc = efx_tx_qcreate(sc->enp, index, txq->type, esmp,
sc->txq_entries, txq->buf_base_id, flags, evq->common,
&txq->common, &desc_index)) != 0)
goto fail;
}
/* Initialise queue descriptor indexes */
txq->added = txq->pending = txq->completed = txq->reaped = desc_index;
@ -1466,6 +1584,10 @@ sfxge_tx_qstart(struct sfxge_softc *sc, unsigned int index)
txq->init_state = SFXGE_TXQ_STARTED;
txq->flush_state = SFXGE_FLUSH_REQUIRED;
txq->tso_fw_assisted = tso_fw_assisted;
txq->max_pkt_desc = sfxge_tx_max_pkt_desc(sc, txq->type,
tso_fw_assisted);
SFXGE_TXQ_UNLOCK(txq);
@ -1574,38 +1696,6 @@ sfxge_tx_qfini(struct sfxge_softc *sc, unsigned int index)
free(txq, M_SFXGE);
}
/*
* Estimate maximum number of Tx descriptors required for TSO packet.
* With minimum MSS and maximum mbuf length we might need more (even
* than a ring-ful of descriptors), but this should not happen in
* practice except due to deliberate attack. In that case we will
* truncate the output at a packet boundary.
*/
static unsigned int
sfxge_tx_max_pkt_desc(const struct sfxge_softc *sc, enum sfxge_txq_type type)
{
/* One descriptor for every input fragment */
unsigned int max_descs = SFXGE_TX_MAPPING_MAX_SEG;
/* VLAN tagging Tx option descriptor may be required */
if (efx_nic_cfg_get(sc->enp)->enc_hw_tx_insert_vlan_enabled)
max_descs++;
if (type == SFXGE_TXQ_IP_TCP_UDP_CKSUM) {
/*
* Plus header and payload descriptor for each output segment.
* Minus one since header fragment is already counted.
*/
max_descs += SFXGE_TSO_MAX_SEGS * 2 - 1;
/* FW assisted TSO requires one more descriptor per segment */
if (sc->tso_fw_assisted)
max_descs += SFXGE_TSO_MAX_SEGS;
}
return (max_descs);
}
static int
sfxge_tx_qinit(struct sfxge_softc *sc, unsigned int txq_index,
enum sfxge_txq_type type, unsigned int evq_index)
@ -1735,8 +1825,6 @@ sfxge_tx_qinit(struct sfxge_softc *sc, unsigned int txq_index,
txq->init_state = SFXGE_TXQ_INITIALIZED;
txq->hw_vlan_tci = 0;
txq->max_pkt_desc = sfxge_tx_max_pkt_desc(sc, type);
return (0);
fail_txq_stat_init:
@ -1846,10 +1934,12 @@ sfxge_tx_init(struct sfxge_softc *sc)
sc->txq_count = SFXGE_TXQ_NTYPES - 1 + sc->intr.n_alloc;
sc->tso_fw_assisted = sfxge_tso_fw_assisted;
if (sc->tso_fw_assisted)
sc->tso_fw_assisted =
(encp->enc_features & EFX_FEATURE_FW_ASSISTED_TSO) &&
(encp->enc_fw_assisted_tso_enabled);
if ((~encp->enc_features & EFX_FEATURE_FW_ASSISTED_TSO) ||
(!encp->enc_fw_assisted_tso_enabled))
sc->tso_fw_assisted &= ~SFXGE_FATSOV1;
if ((~encp->enc_features & EFX_FEATURE_FW_ASSISTED_TSO_V2) ||
(!encp->enc_fw_assisted_tso_v2_enabled))
sc->tso_fw_assisted &= ~SFXGE_FATSOV2;
sc->txqs_node = SYSCTL_ADD_NODE(
device_get_sysctl_ctx(sc->dev),

View File

@ -170,6 +170,7 @@ struct sfxge_txq {
struct sfxge_softc *sc;
enum sfxge_txq_state init_state;
enum sfxge_flush_state flush_state;
unsigned int tso_fw_assisted;
enum sfxge_txq_type type;
unsigned int txq_index;
unsigned int evq_index;