net/enic: fix memory leak with oversized Tx packets

If a packet send is attempted with a packet larger than the NIC
is capable of processing (9208) it will be dropped with no
completion descriptor returned or completion index update, which
will lead to an mbuf leak and eventual hang.

Drop and count oversized Tx packets in the Tx burst function and
dereference/free the mbuf without sending it to the NIC.

Since the maximum Rx and Tx packet sizes are different on enic
and are now both being used, make the define ENIC_DEFAULT_MAX_PKT_SIZE
be 2 defines, one for Rx and one for Tx.

Fixes: fefed3d1e6 ("enic: new driver")
Cc: stable@dpdk.org

Signed-off-by: John Daley <johndale@cisco.com>
This commit is contained in:
John Daley 2017-02-02 17:18:40 -08:00 committed by Ferruh Yigit
parent 123c42a487
commit ed6e564c21
5 changed files with 22 additions and 7 deletions

View File

@ -103,6 +103,7 @@ struct enic_fdir {
struct enic_soft_stats { struct enic_soft_stats {
rte_atomic64_t rx_nombuf; rte_atomic64_t rx_nombuf;
rte_atomic64_t rx_packet_errors; rte_atomic64_t rx_packet_errors;
rte_atomic64_t tx_oversized;
}; };
struct enic_memzone_entry { struct enic_memzone_entry {

View File

@ -137,6 +137,7 @@ static void enic_clear_soft_stats(struct enic *enic)
struct enic_soft_stats *soft_stats = &enic->soft_stats; struct enic_soft_stats *soft_stats = &enic->soft_stats;
rte_atomic64_clear(&soft_stats->rx_nombuf); rte_atomic64_clear(&soft_stats->rx_nombuf);
rte_atomic64_clear(&soft_stats->rx_packet_errors); rte_atomic64_clear(&soft_stats->rx_packet_errors);
rte_atomic64_clear(&soft_stats->tx_oversized);
} }
static void enic_init_soft_stats(struct enic *enic) static void enic_init_soft_stats(struct enic *enic)
@ -144,6 +145,7 @@ static void enic_init_soft_stats(struct enic *enic)
struct enic_soft_stats *soft_stats = &enic->soft_stats; struct enic_soft_stats *soft_stats = &enic->soft_stats;
rte_atomic64_init(&soft_stats->rx_nombuf); rte_atomic64_init(&soft_stats->rx_nombuf);
rte_atomic64_init(&soft_stats->rx_packet_errors); rte_atomic64_init(&soft_stats->rx_packet_errors);
rte_atomic64_init(&soft_stats->tx_oversized);
enic_clear_soft_stats(enic); enic_clear_soft_stats(enic);
} }
@ -183,7 +185,8 @@ void enic_dev_stats_get(struct enic *enic, struct rte_eth_stats *r_stats)
r_stats->obytes = stats->tx.tx_bytes_ok; r_stats->obytes = stats->tx.tx_bytes_ok;
r_stats->ierrors = stats->rx.rx_errors + stats->rx.rx_drop; r_stats->ierrors = stats->rx.rx_errors + stats->rx.rx_drop;
r_stats->oerrors = stats->tx.tx_errors; r_stats->oerrors = stats->tx.tx_errors
+ rte_atomic64_read(&soft_stats->tx_oversized);
r_stats->imissed = stats->rx.rx_no_bufs + rx_truncated; r_stats->imissed = stats->rx.rx_no_bufs + rx_truncated;

View File

@ -89,10 +89,11 @@ int enic_get_vnic_config(struct enic *enic)
/* max packet size is only defined in newer VIC firmware /* max packet size is only defined in newer VIC firmware
* and will be 0 for legacy firmware and VICs * and will be 0 for legacy firmware and VICs
*/ */
if (c->max_pkt_size > ENIC_DEFAULT_MAX_PKT_SIZE) if (c->max_pkt_size > ENIC_DEFAULT_RX_MAX_PKT_SIZE)
enic->max_mtu = c->max_pkt_size - (ETHER_HDR_LEN + 4); enic->max_mtu = c->max_pkt_size - (ETHER_HDR_LEN + 4);
else else
enic->max_mtu = ENIC_DEFAULT_MAX_PKT_SIZE - (ETHER_HDR_LEN + 4); enic->max_mtu = ENIC_DEFAULT_RX_MAX_PKT_SIZE
- (ETHER_HDR_LEN + 4);
if (c->mtu == 0) if (c->mtu == 0)
c->mtu = 1500; c->mtu = 1500;

View File

@ -48,7 +48,10 @@
#define ENIC_MIN_MTU 68 #define ENIC_MIN_MTU 68
/* Does not include (possible) inserted VLAN tag and FCS */ /* Does not include (possible) inserted VLAN tag and FCS */
#define ENIC_DEFAULT_MAX_PKT_SIZE 9022 #define ENIC_DEFAULT_RX_MAX_PKT_SIZE 9022
/* Does not include (possible) inserted VLAN tag and FCS */
#define ENIC_TX_MAX_PKT_SIZE 9208
#define ENIC_MULTICAST_PERFECT_FILTERS 32 #define ENIC_MULTICAST_PERFECT_FILTERS 32
#define ENIC_UNICAST_PERFECT_FILTERS 32 #define ENIC_UNICAST_PERFECT_FILTERS 32

View File

@ -546,16 +546,23 @@ uint16_t enic_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
for (index = 0; index < nb_pkts; index++) { for (index = 0; index < nb_pkts; index++) {
tx_pkt = *tx_pkts++; tx_pkt = *tx_pkts++;
pkt_len = tx_pkt->pkt_len;
data_len = tx_pkt->data_len;
ol_flags = tx_pkt->ol_flags;
nb_segs = tx_pkt->nb_segs; nb_segs = tx_pkt->nb_segs;
if (pkt_len > ENIC_TX_MAX_PKT_SIZE) {
rte_pktmbuf_free(tx_pkt);
rte_atomic64_inc(&enic->soft_stats.tx_oversized);
continue;
}
if (nb_segs > wq_desc_avail) { if (nb_segs > wq_desc_avail) {
if (index > 0) if (index > 0)
goto post; goto post;
goto done; goto done;
} }
pkt_len = tx_pkt->pkt_len;
data_len = tx_pkt->data_len;
ol_flags = tx_pkt->ol_flags;
mss = 0; mss = 0;
vlan_id = 0; vlan_id = 0;
vlan_tag_insert = 0; vlan_tag_insert = 0;