Add SACK support, we choose to send SACK (and extended header) when we have to send an ACK but cannot advance CACK. The logic is a bit complicated because the spec says we have to align CACK and SACK_BASE to 8 which could effectively move CACK back so we have to fill in for those bits as 1s in the SACK bitmap Signed-off-by: Nikolay Aleksandrov <nikolay@xxxxxxxxxxxxx> Signed-off-by: Alex Badea <alex.badea@xxxxxxxxxxxx> --- drivers/ultraeth/uet_pdc.c | 100 ++++++++++++++++++++++++++++++--- drivers/ultraeth/uet_pds.c | 3 + include/net/ultraeth/uet_pdc.h | 10 ++++ include/uapi/linux/ultraeth.h | 40 +++++++++++++ 4 files changed, 146 insertions(+), 7 deletions(-) diff --git a/drivers/ultraeth/uet_pdc.c b/drivers/ultraeth/uet_pdc.c index 55b893ac5479..e9469edd9014 100644 --- a/drivers/ultraeth/uet_pdc.c +++ b/drivers/ultraeth/uet_pdc.c @@ -401,13 +401,55 @@ static int uet_pdc_build_req(struct uet_pdc *pdc, return 0; } +static void pdc_build_sack(struct uet_pdc *pdc, + struct uet_pds_ack_ext_hdr *ack_ext) +{ + u32 sack_base = pdc->lowest_unack_psn, shift; + unsigned long bit, start_bit; + s16 sack_psn_offset; + u64 sack_bitmap; + + if (sack_base + UET_PDC_SACK_BITS > pdc->max_rcv_psn) + sack_base = max(pdc->max_rcv_psn - UET_PDC_SACK_BITS, + pdc->rx_base_psn); + sack_base &= UET_PDC_SACK_MASK; + sack_psn_offset = (s16)(sack_base - + (pdc->rx_base_psn & UET_PDC_SACK_MASK)); + if (sack_base == pdc->rx_base_psn) { + shift = 1; + sack_bitmap = 1; + bit = 0; + } else if (sack_base < pdc->rx_base_psn) { + shift = pdc->rx_base_psn - sack_base; + sack_bitmap = U64_MAX >> (64 - shift); + bit = 0; + } else { + shift = 0; + sack_bitmap = 0; + bit = sack_base - pdc->rx_base_psn; + } + + start_bit = bit; + for_each_set_bit_from(bit, pdc->rx_bitmap, UET_PDC_MPR) { + shift += (bit - start_bit); + if (shift >= UET_PDC_SACK_BITS) + break; + sack_bitmap |= BIT(shift); + } + + pdc->lowest_unack_psn += UET_PDC_SACK_BITS; + ack_ext->sack_psn_offset = cpu_to_be16(sack_psn_offset); + ack_ext->sack_bitmap = cpu_to_be64(sack_bitmap); +} + static void pdc_build_ack(struct uet_pdc *pdc, struct sk_buff *skb, u32 psn, u8 ack_flags, bool exact_psn) { + u8 type = pdc_should_sack(pdc) ? UET_PDS_TYPE_ACK_CC : UET_PDS_TYPE_ACK; struct uet_pds_ack_hdr *ack = skb_put(skb, sizeof(*ack)); - uet_pdc_build_prologue(&ack->prologue, UET_PDS_TYPE_ACK, - UET_PDS_NEXT_HDR_RSP, ack_flags); + uet_pdc_build_prologue(&ack->prologue, type, UET_PDS_NEXT_HDR_RSP, + ack_flags); if (exact_psn) { ack->ack_psn_offset = 0; ack->cack_psn = cpu_to_be32(psn); @@ -417,6 +459,13 @@ static void pdc_build_ack(struct uet_pdc *pdc, struct sk_buff *skb, u32 psn, } ack->spdcid = cpu_to_be16(pdc->spdcid); ack->dpdcid = cpu_to_be16(pdc->dpdcid); + + if (pdc_should_sack(pdc)) { + struct uet_pds_ack_ext_hdr *ack_ext = skb_put(skb, + sizeof(*ack_ext)); + + pdc_build_sack(pdc, ack_ext); + } } static void uet_pdc_build_ses_ack(struct uet_pdc *pdc, struct sk_buff *skb, @@ -439,10 +488,12 @@ static void uet_pdc_build_ses_ack(struct uet_pdc *pdc, struct sk_buff *skb, static int uet_pdc_send_ses_ack(struct uet_pdc *pdc, __u8 ses_rc, __be16 msg_id, u32 psn, u8 ack_flags, bool exact_psn) { + unsigned int skb_size = sizeof(struct uet_ses_rsp_hdr) + + sizeof(struct uet_pds_ack_hdr); struct sk_buff *skb; - skb = alloc_skb(sizeof(struct uet_ses_rsp_hdr) + - sizeof(struct uet_pds_ack_hdr), GFP_ATOMIC); + skb_size += pdc_should_sack(pdc) ? sizeof(struct uet_pds_ack_ext_hdr) : 0; + skb = alloc_skb(skb_size, GFP_ATOMIC); if (!skb) return -ENOBUFS; @@ -514,6 +565,30 @@ int uet_pdc_tx_req(struct uet_pdc *pdc, struct sk_buff *skb, u8 type) return ret; } +static void uet_pdc_rx_sack(struct uet_pdc *pdc, struct sk_buff *skb, + u32 cack_psn, struct uet_pds_ack_ext_hdr *ext_ack, + bool ecn_marked) +{ + unsigned long bit, *sack_bitmap = (unsigned long *)&ext_ack->sack_bitmap; + u32 sack_base_psn = cack_psn + + (s16)be16_to_cpu(ext_ack->sack_psn_offset); + + while ((bit = find_next_bit(sack_bitmap, 64, 0)) != 64) { + /* skip bits that were already acked */ + if (sack_base_psn + bit <= pdc->tx_base_psn) { + if (sack_base_psn + bit == pdc->tx_base_psn) + __uet_pdc_mpr_advance_tx(pdc, 1); + continue; + } + if (!psn_bit_valid((sack_base_psn + bit) - pdc->tx_base_psn)) + break; + if (test_and_set_bit((sack_base_psn + bit) - pdc->tx_base_psn, + pdc->ack_bitmap)) + continue; + uet_pdc_ack_psn(pdc, skb, sack_base_psn + bit, ecn_marked); + } +} + int uet_pdc_rx_ack(struct uet_pdc *pdc, struct sk_buff *skb, __be32 remote_fep_addr) { @@ -521,10 +596,11 @@ int uet_pdc_rx_ack(struct uet_pdc *pdc, struct sk_buff *skb, struct uet_pds_ack_hdr *ack = pds_ack_hdr(skb); s16 ack_psn_offset = be16_to_cpu(ack->ack_psn_offset); const char *drop_reason = "ack_psn not in MPR window"; + struct uet_pds_ack_ext_hdr *ext_ack = NULL; u32 cack_psn = be32_to_cpu(ack->cack_psn); u32 ack_psn = cack_psn + ack_psn_offset; + bool is_sack = false, ecn_marked; int ret = -EINVAL; - bool ecn_marked; u32 psn_bit; spin_lock(&pdc->lock); @@ -545,9 +621,16 @@ int uet_pdc_rx_ack(struct uet_pdc *pdc, struct sk_buff *skb, drop_reason = "ack_psn bit is invalid"; goto err_dbg; } + if (uet_prologue_type(&ack->prologue) == UET_PDS_TYPE_ACK_CC) { + ext_ack = pds_ack_ext_hdr(skb); + is_sack = !!ext_ack->sack_bitmap; + } if (test_and_set_bit(psn_bit, pdc->ack_bitmap)) { - drop_reason = "ack_psn bit already set in ack_bitmap"; - goto err_dbg; + /* SACK packets can include already acked packets */ + if (!is_sack) { + drop_reason = "ack_psn bit already set in ack_bitmap"; + goto err_dbg; + } } /* either using ROD mode or in SYN_SENT state */ @@ -573,6 +656,9 @@ int uet_pdc_rx_ack(struct uet_pdc *pdc, struct sk_buff *skb, if (cack_psn != ack_psn) uet_pdc_ack_psn(pdc, skb, ack_psn, ecn_marked); + if (is_sack) + uet_pdc_rx_sack(pdc, skb, cack_psn, ext_ack, ecn_marked); + ret = 0; switch (pdc->state) { case UET_PDC_EP_STATE_SYN_SENT: diff --git a/drivers/ultraeth/uet_pds.c b/drivers/ultraeth/uet_pds.c index 52122998079d..436b63189800 100644 --- a/drivers/ultraeth/uet_pds.c +++ b/drivers/ultraeth/uet_pds.c @@ -266,6 +266,9 @@ int uet_pds_rx(struct uet_pds *pds, struct sk_buff *skb, __be32 local_fep_addr, prologue = pds_prologue_hdr(skb); switch (uet_prologue_type(prologue)) { + case UET_PDS_TYPE_ACK_CC: + offset += sizeof(struct uet_pds_ack_ext_hdr); + fallthrough; case UET_PDS_TYPE_ACK: if (!uet_pds_rx_valid_ack_next_hdr(prologue)) break; diff --git a/include/net/ultraeth/uet_pdc.h b/include/net/ultraeth/uet_pdc.h index 8a87fc0bc869..d6710f92fb16 100644 --- a/include/net/ultraeth/uet_pdc.h +++ b/include/net/ultraeth/uet_pdc.h @@ -20,6 +20,8 @@ NSEC_PER_SEC) #define UET_PDC_RTX_DEFAULT_MAX 3 #define UET_PDC_MPR 128 +#define UET_PDC_SACK_BITS 64 +#define UET_PDC_SACK_MASK (U64_MAX << 3) #define UET_SKB_CB(skb) ((struct uet_skb_cb *)&((skb)->cb[0])) @@ -93,6 +95,8 @@ struct uet_pdc { u32 rx_base_psn; u32 tx_base_psn; + u32 lowest_unack_psn; + u32 max_rcv_psn; u32 ack_gen_trigger; u32 ack_gen_min_pkt_add; @@ -146,4 +150,10 @@ static inline bool before(u32 seq1, u32 seq2) { return (s32)(seq1-seq2) < 0; } + +static inline bool pdc_should_sack(const struct uet_pdc *pdc) +{ + return pdc->lowest_unack_psn > pdc->rx_base_psn && + pdc->lowest_unack_psn < pdc->max_rcv_psn; +} #endif /* _UECON_PDC_H */ diff --git a/include/uapi/linux/ultraeth.h b/include/uapi/linux/ultraeth.h index cc39bf970e08..3b8e95d7ed7b 100644 --- a/include/uapi/linux/ultraeth.h +++ b/include/uapi/linux/ultraeth.h @@ -152,6 +152,46 @@ struct uet_pds_ack_hdr { __be16 dpdcid; } __attribute__ ((__packed__)); +/* ext ack CC flags */ +enum { + UET_PDS_ACK_EXT_CC_F_RSVD = (1 << 0) +}; + +/* field: cc_type_mpr_sack_off */ +#define UET_PDS_ACK_EXT_MPR_BITS 8 +#define UET_PDS_ACK_EXT_MPR_MASK 0xff +#define UET_PDS_ACK_EXT_CC_FLAGS_BITS 4 +#define UET_PDS_ACK_EXT_CC_FLAGS_MASK 0xf +#define UET_PDS_ACK_EXT_CC_FLAGS_SHIFT UET_PDS_ACK_EXT_MPR_BITS +#define UET_PDS_ACK_EXT_CC_TYPE_BITS 4 +#define UET_PDS_ACK_EXT_CC_TYPE_MASK 0xf +#define UET_PDS_ACK_EXT_CC_TYPE_SHIFT (UET_PDS_ACK_EXT_CC_FLAGS_SHIFT + \ + UET_PDS_ACK_EXT_CC_FLAGS_BITS) +/* header used for ACK_CC */ +struct uet_pds_ack_ext_hdr { + __be16 cc_type_flags_mpr; + __be16 sack_psn_offset; + __be64 sack_bitmap; + __be64 ack_cc_state; +} __attribute__ ((__packed__)); + +static inline __u8 uet_pds_ack_ext_mpr(const struct uet_pds_ack_ext_hdr *ack) +{ + return __be16_to_cpu(ack->cc_type_flags_mpr) & UET_PDS_ACK_EXT_MPR_MASK; +} + +static inline __u8 uet_pds_ack_ext_cc_flags(const struct uet_pds_ack_ext_hdr *ack) +{ + return (__be16_to_cpu(ack->cc_type_flags_mpr) >> UET_PDS_ACK_EXT_CC_FLAGS_SHIFT) & + UET_PDS_ACK_EXT_CC_FLAGS_MASK; +} + +static inline __u8 uet_pds_ack_ext_cc_type(const struct uet_pds_ack_ext_hdr *ack) +{ + return (__be16_to_cpu(ack->cc_type_flags_mpr) >> UET_PDS_ACK_EXT_CC_TYPE_SHIFT) & + UET_PDS_ACK_EXT_CC_TYPE_MASK; +} + /* ses request op codes */ enum { UET_SES_REQ_OP_NOOP = 0x00, -- 2.48.1