If TCP retransmits a TLS packet that requires encryption by the NIC, the TCP sequence number will go backwards and the hardware will require some assistance from the driver. The driver needs to retrieve the TLS record that covers the byte sequence of the retransmitted packet. If the retransmitted packet does not include the tag, the hardware can simply encrypt the packet using the informtaion in the TLS record. The driver provides the TLS record information for the retransmitted packet in the presync TX BD. The presync TX BD introduced in the last patch is treated very much like a TX push BD with inline data. The only exception is that no SKB will be stored for the presync TX BD. Retransmission that includes the TLS tag will be handled in future patches. Reviewed-by: Andy Gospodarek Signed-off-by: Michael Chan --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 + .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 + .../net/ethernet/broadcom/bnxt/bnxt_ktls.c | 126 +++++++++++++++++- 4 files changed, 128 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 89c900db45ba..5697190dc541 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -499,7 +499,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) txq = netdev_get_tx_queue(dev, i); txr = &bp->tx_ring[bp->tx_ring_map[i]]; - prod = txr->tx_prod; #if (MAX_SKB_FRAGS > TX_MAX_FRAGS) if (skb_shinfo(skb)->nr_frags > TX_MAX_FRAGS) { @@ -532,6 +531,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(!skb)) return NETDEV_TX_OK; + prod = txr->tx_prod; length = skb->len; len = skb_headlen(skb); last_frag = skb_shinfo(skb)->nr_frags; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index e0880b8c4b73..696dfe522c7b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1188,6 +1188,8 @@ struct bnxt_cmn_sw_stats { enum bnxt_ktls_data_counters { BNXT_KTLS_TX_PKTS = 0, BNXT_KTLS_TX_BYTES, + BNXT_KTLS_TX_OOO_PKTS, + BNXT_KTLS_TX_DROP_NO_SYNC, BNXT_KTLS_MAX_DATA_COUNTERS, }; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 66b323e94140..769058a6ec31 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -359,6 +359,8 @@ static const char *const bnxt_ring_drv_stats_arr[] = { static const char *const bnxt_ktls_data_stats[] = { [BNXT_KTLS_TX_PKTS] = "tx_tls_encrypted_packets", [BNXT_KTLS_TX_BYTES] = "tx_tls_encrypted_bytes", + [BNXT_KTLS_TX_OOO_PKTS] = "tx_tls_ooo_packets", + [BNXT_KTLS_TX_DROP_NO_SYNC] = "tx_tls_drop_no_sync", }; /* kTLS control plane counter strings indexed by enum bnxt_ktls_ctrl_counters */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ktls.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ktls.c index 263b075af621..b94418ee5436 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ktls.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ktls.c @@ -289,6 +289,116 @@ int bnxt_ktls_init(struct bnxt *bp) return 0; } +static void bnxt_ktls_pre_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + u32 kid, struct crypto_prefix_cmd *pre_cmd) +{ + struct bnxt_sw_tx_bd *tx_buf; + struct tx_bd_presync *psbd; + u32 bd_space, space; + u8 *pcmd; + u16 prod; + + prod = txr->tx_prod; + tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)]; + + psbd = (void *)&txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)]; + psbd->tx_bd_len_flags_type = CRYPTO_PRESYNC_BD_CMD; + psbd->tx_bd_kid = cpu_to_le32(BNXT_KID_HW(kid)); + psbd->tx_bd_opaque = + SET_TX_OPAQUE(bp, txr, prod, CRYPTO_PREFIX_CMD_BDS + 1); + + prod = NEXT_TX(prod); + pcmd = (void *)&txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)]; + bd_space = TX_DESC_CNT - TX_IDX(prod); + space = bd_space * sizeof(struct tx_bd); + if (space >= CRYPTO_PREFIX_CMD_SIZE) { + memcpy(pcmd, pre_cmd, CRYPTO_PREFIX_CMD_SIZE); + prod += CRYPTO_PREFIX_CMD_BDS; + } else { + memcpy(pcmd, pre_cmd, space); + prod += bd_space; + pcmd = (void *)&txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)]; + memcpy(pcmd, (u8 *)pre_cmd + space, + CRYPTO_PREFIX_CMD_SIZE - space); + prod += CRYPTO_PREFIX_CMD_BDS - bd_space; + } + txr->tx_prod = prod; + tx_buf->is_push = 1; + /* Minus 1 since the header psbd is a single entry short BD */ + tx_buf->inline_data_bds = CRYPTO_PREFIX_CMD_BDS - 1; +} + +static int bnxt_ktls_tx_ooo(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + struct sk_buff *skb, u32 payload_len, u32 seq, + struct tls_context *tls_ctx) +{ + struct bnxt_sw_stats *sw_stats = txr->tx_cpr->sw_stats; + struct tls_offload_context_tx *tx_tls_ctx; + struct bnxt_ktls_offload_ctx_tx *kctx_tx; + u32 hdr_tcp_seq, end_seq, total_bds; + struct crypto_prefix_cmd pcmd = {}; + struct tls_record_info *record; + unsigned long flags; + bool fwd = false; + u64 rec_sn; + u8 *hdr; + int rc; + + tx_tls_ctx = tls_offload_ctx_tx(tls_ctx); + kctx_tx = __tls_driver_ctx(tls_ctx, TLS_OFFLOAD_CTX_DIR_TX); + end_seq = seq + skb->len - skb_tcp_all_headers(skb); + if (unlikely(after(seq, kctx_tx->tcp_seq_no) || + after(end_seq, kctx_tx->tcp_seq_no))) { + fwd = true; + pcmd.flags = CRYPTO_PREFIX_CMD_FLAGS_UPDATE_IN_ORDER_VAR_LE; + } + + spin_lock_irqsave(&tx_tls_ctx->lock, flags); + record = tls_get_record(tx_tls_ctx, seq, &rec_sn); + if (!record || !record->num_frags) { + rc = -EPROTO; + sw_stats->tls.counters[BNXT_KTLS_TX_DROP_NO_SYNC]++; + goto unlock_exit; + } + hdr_tcp_seq = tls_record_start_seq(record); + hdr = skb_frag_address_safe(&record->frags[0]); + + total_bds = CRYPTO_PRESYNC_BDS + skb_shinfo(skb)->nr_frags + 2; + if (bnxt_tx_avail(bp, txr) < total_bds) { + rc = -ENOSPC; + goto unlock_exit; + } + + if (before(record->end_seq - tls_ctx->prot_info.tag_size, + seq + payload_len)) { + /* retransmission includes tag bytes */ + rc = -EOPNOTSUPP; + goto unlock_exit; + } + pcmd.header_tcp_seq_num = cpu_to_le32(hdr_tcp_seq); + pcmd.start_tcp_seq_num = cpu_to_le32(seq); + pcmd.end_tcp_seq_num = cpu_to_le32(seq + payload_len - 1); + if (tls_ctx->prot_info.version == TLS_1_2_VERSION) { + u32 nonce_bytes = tls_ctx->prot_info.iv_size; + u32 retrans_off = seq - hdr_tcp_seq; + + if (retrans_off > 5 && retrans_off < 5 + nonce_bytes) + nonce_bytes = retrans_off - 5; + memcpy(pcmd.explicit_nonce, hdr + 5, nonce_bytes); + } + memcpy(&pcmd.record_seq_num[0], &rec_sn, sizeof(rec_sn)); + + rc = 0; + bnxt_ktls_pre_xmit(bp, txr, kctx_tx->kid, &pcmd); + + if (fwd) + kctx_tx->tcp_seq_no = end_seq; + +unlock_exit: + spin_unlock_irqrestore(&tx_tls_ctx->lock, flags); + return rc; +} + struct sk_buff *bnxt_ktls_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr, struct sk_buff *skb, __le32 *lflags, u32 *kid) { @@ -297,6 +407,7 @@ struct sk_buff *bnxt_ktls_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr, struct bnxt_ktls_offload_ctx_tx *kctx_tx; struct tls_context *tls_ctx; u32 seq, payload_len; + int rc; if (!IS_ENABLED(CONFIG_TLS_DEVICE) || !ktls || !tls_is_skb_tx_device_offloaded(skb)) @@ -316,9 +427,18 @@ struct sk_buff *bnxt_ktls_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr, sw_stats->tls.counters[BNXT_KTLS_TX_PKTS]++; sw_stats->tls.counters[BNXT_KTLS_TX_BYTES] += payload_len; } else { - skb = tls_encrypt_skb(skb); - if (!skb) - return NULL; + sw_stats->tls.counters[BNXT_KTLS_TX_OOO_PKTS]++; + + rc = bnxt_ktls_tx_ooo(bp, txr, skb, payload_len, seq, tls_ctx); + if (rc) + return tls_encrypt_skb(skb); + + *kid = BNXT_KID_HW(kctx_tx->kid); + *lflags |= cpu_to_le32(TX_BD_FLAGS_CRYPTO_EN | + BNXT_TX_KID_LO(*kid)); + sw_stats->tls.counters[BNXT_KTLS_TX_PKTS]++; + sw_stats->tls.counters[BNXT_KTLS_TX_BYTES] += payload_len; + return skb; } return skb; } -- 2.51.0