diff --git a/drivers/net/ethernet/alibaba/eea/Makefile b/drivers/net/ethernet/alibaba/eea/Makefile index a07f3019ec1f..7d8e7e8c2f3a 100644 --- a/drivers/net/ethernet/alibaba/eea/Makefile +++ b/drivers/net/ethernet/alibaba/eea/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_ALIBABA_EEA) += eea.o -eea-y := eea_pci.o +eea-y := eea_ring.o \ + eea_pci.o diff --git a/drivers/net/ethernet/alibaba/eea/eea_desc.h b/drivers/net/ethernet/alibaba/eea/eea_desc.h new file mode 100644 index 000000000000..8d94a0f0f237 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_desc.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_DESC_H__ +#define __EEA_DESC_H__ + +#define EEA_DESC_TS_MASK GENMASK_ULL(47, 0) +#define EEA_DESC_TS(desc) (le64_to_cpu((desc)->ts) & EEA_DESC_TS_MASK) + +struct eea_aq_desc { + __le16 flags; + __le16 id; + __le16 reserved; + u8 classid; + u8 command; + __le64 data_addr; + __le64 reply_addr; + __le32 data_len; + __le32 reply_len; +}; + +struct eea_aq_cdesc { + __le16 flags; + __le16 id; +#define EEA_OK 0 +#define EEA_ERR 0xffffffff + __le32 status; + __le32 reply_len; + __le32 reserved1; + + __le64 reserved2; + __le64 reserved3; +}; + +struct eea_rx_desc_no_hdr { + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + __le64 addr; +}; + +struct eea_rx_desc { + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + __le64 addr; + + __le64 hdr_addr; + __le32 reserved2; + __le32 reserved3; +}; + +#define EEA_RX_CDESC_HDR_LEN_MASK GENMASK_ULL(9, 0) + +struct eea_rx_cdesc { +#define EEA_DESC_F_DATA_VALID BIT(6) +#define EEA_DESC_F_SPLIT_HDR BIT(5) + __le16 flags; + __le16 id; + __le16 len; +#define EEA_NET_PT_NONE 0 +#define EEA_NET_PT_IPv4 1 +#define EEA_NET_PT_TCPv4 2 +#define EEA_NET_PT_UDPv4 3 +#define EEA_NET_PT_IPv6 4 +#define EEA_NET_PT_TCPv6 5 +#define EEA_NET_PT_UDPv6 6 +#define EEA_NET_PT_IPv6_EX 7 +#define EEA_NET_PT_TCPv6_EX 8 +#define EEA_NET_PT_UDPv6_EX 9 + /* [9:0] is packet type. */ + __le16 type; + + /* hw timestamp [0:47]: ts */ + __le64 ts; + + __le32 hash; + + /* 0-9: hdr_len split header + * 10-15: reserved1 + */ + __le16 len_ex; + __le16 reserved2; + + __le32 reserved3; + __le32 reserved4; +}; + +#define EEA_TX_GSO_NONE 0 +#define EEA_TX_GSO_TCPV4 1 +#define EEA_TX_GSO_TCPV6 4 +#define EEA_TX_GSO_UDP_L4 5 +#define EEA_TX_GSO_ECN 0x80 + +struct eea_tx_desc { +#define EEA_DESC_F_DO_CSUM BIT(6) + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + __le64 addr; + + __le16 csum_start; + __le16 csum_offset; + u8 gso_type; + u8 reserved2; + __le16 gso_size; + __le64 reserved3; +}; + +struct eea_tx_cdesc { + __le16 flags; + __le16 id; + __le16 len; + __le16 reserved1; + + /* hw timestamp [0:47]: ts */ + __le64 ts; +}; + +#define EEA_DB_FLAGS_OFF 0 +#define EEA_DB_IDX_OFF (2 * 8) +#define EEA_DB_TX_CQ_HEAD_OFF (4 * 8) +#define EEA_DB_RX_CQ_HEAD_OFF (6 * 8) + +#define EEA_IDX_PRESENT BIT(0) +#define EEA_IRQ_MASK BIT(1) +#define EEA_IRQ_UNMASK BIT(2) +#endif diff --git a/drivers/net/ethernet/alibaba/eea/eea_ring.c b/drivers/net/ethernet/alibaba/eea/eea_ring.c new file mode 100644 index 000000000000..99dcabd094b8 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_ring.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#include "eea_pci.h" +#include "eea_ring.h" + +void eea_ering_irq_active(struct eea_ring *ering, struct eea_ring *tx_ering) +{ + u64 value = 0, rx_idx, tx_idx; + + tx_idx = (u64)tx_ering->cq.hw_idx; + rx_idx = (u64)ering->cq.hw_idx; + + value |= EEA_IRQ_UNMASK << EEA_DB_FLAGS_OFF; + value |= tx_idx << EEA_DB_TX_CQ_HEAD_OFF; + value |= rx_idx << EEA_DB_RX_CQ_HEAD_OFF; + + writeq(value, ering->db); +} + +void *eea_ering_cq_get_desc(const struct eea_ring *ering) +{ + u8 phase; + u8 *desc; + + desc = ering->cq.desc + (ering->cq.head << ering->cq.desc_size_shift); + + phase = READ_ONCE(*(u8 *)(desc + ering->cq.desc_size - 1)); + + if ((phase & EEA_RING_DESC_F_CQ_PHASE) == ering->cq.phase) { + dma_rmb(); + return desc; + } + + return NULL; +} + +/* sq api */ +void *eea_ering_sq_alloc_desc(struct eea_ring *ering, u16 id, bool is_last, + u16 flags) +{ + struct eea_ring_sq *sq = &ering->sq; + struct eea_common_desc *desc; + + if (!sq->shadow_num) { + sq->shadow_idx = sq->head; + sq->shadow_id = cpu_to_le16(id); + } + + if (!is_last) + flags |= EEA_RING_DESC_F_MORE; + + desc = sq->desc + (sq->shadow_idx << sq->desc_size_shift); + + desc->flags = cpu_to_le16(flags); + desc->id = sq->shadow_id; + + if (unlikely(++sq->shadow_idx >= ering->num)) + sq->shadow_idx = 0; + + ++sq->shadow_num; + + return desc; +} + +/* This is an allocation API for admin Q. For each call to admin Q, only one + * desc will be allocated. + */ +void *eea_ering_aq_alloc_desc(struct eea_ring *ering) +{ + struct eea_ring_sq *sq = &ering->sq; + struct eea_common_desc *desc; + + if (!sq->shadow_num) + sq->shadow_idx = sq->head; + + desc = sq->desc + (sq->shadow_idx << sq->desc_size_shift); + + if (unlikely(++sq->shadow_idx >= ering->num)) + sq->shadow_idx = 0; + + ++sq->shadow_num; + + return desc; +} + +void eea_ering_sq_commit_desc(struct eea_ring *ering) +{ + struct eea_ring_sq *sq = &ering->sq; + int num; + + num = sq->shadow_num; + + ering->num_free -= num; + + sq->head = sq->shadow_idx; + sq->hw_idx += num; + sq->shadow_num = 0; +} + +void eea_ering_sq_cancel(struct eea_ring *ering) +{ + ering->sq.shadow_num = 0; +} + +/* cq api */ +void eea_ering_cq_ack_desc(struct eea_ring *ering, u32 num) +{ + struct eea_ring_cq *cq = &ering->cq; + + cq->head += num; + cq->hw_idx += num; + + if (unlikely(cq->head >= ering->num)) { + cq->head -= ering->num; + cq->phase ^= EEA_RING_DESC_F_CQ_PHASE; + } + + ering->num_free += num; +} + +/* notify */ +void eea_ering_kick(struct eea_ring *ering) +{ + u64 value = 0, idx; + + idx = (u64)ering->sq.hw_idx; + + value |= EEA_IDX_PRESENT << EEA_DB_FLAGS_OFF; + value |= idx << EEA_DB_IDX_OFF; + + writeq(value, ering->db); +} + +/* ering alloc/free */ +static void ering_free_queue(struct eea_device *edev, size_t size, + void *queue, dma_addr_t dma_handle) +{ + dma_free_coherent(edev->dma_dev, size, queue, dma_handle); +} + +static void *ering_alloc_queue(struct eea_device *edev, size_t size, + dma_addr_t *dma_handle) +{ + gfp_t flags = GFP_KERNEL | __GFP_NOWARN; + + return dma_alloc_coherent(edev->dma_dev, size, dma_handle, flags); +} + +static int ering_alloc_queues(struct eea_ring *ering, struct eea_device *edev, + size_t num, u8 sq_desc_size, u8 cq_desc_size) +{ + dma_addr_t addr; + size_t size; + void *ring; + + size = num * sq_desc_size; + + ring = ering_alloc_queue(edev, size, &addr); + if (!ring) + return -ENOMEM; + + ering->sq.desc = ring; + ering->sq.dma_addr = addr; + ering->sq.dma_size = size; + ering->sq.desc_size = sq_desc_size; + ering->sq.desc_size_shift = fls(sq_desc_size) - 1; + + size = num * cq_desc_size; + + ring = ering_alloc_queue(edev, size, &addr); + if (!ring) + goto err_free_sq; + + ering->cq.desc = ring; + ering->cq.dma_addr = addr; + ering->cq.dma_size = size; + ering->cq.desc_size = cq_desc_size; + ering->cq.desc_size_shift = fls(cq_desc_size) - 1; + + ering->num = num; + + return 0; + +err_free_sq: + ering_free_queue(ering->edev, ering->sq.dma_size, + ering->sq.desc, ering->sq.dma_addr); + return -ENOMEM; +} + +static void ering_init(struct eea_ring *ering) +{ + ering->cq.phase = EEA_RING_DESC_F_CQ_PHASE; + ering->num_free = ering->num; +} + +struct eea_ring *eea_ering_alloc(u32 index, u32 num, struct eea_device *edev, + u8 sq_desc_size, u8 cq_desc_size, + const char *name) +{ + struct eea_ring *ering; + + if (num > EEA_NET_IO_HW_RING_DEPTH_MAX || + num < EEA_NET_IO_RING_DEPTH_MIN) + return NULL; + + if (!is_power_of_2(num)) + return NULL; + + if (!sq_desc_size || !is_power_of_2(sq_desc_size)) + return NULL; + + if (!cq_desc_size || !is_power_of_2(cq_desc_size)) + return NULL; + + ering = kzalloc(sizeof(*ering), GFP_KERNEL); + if (!ering) + return NULL; + + ering->edev = edev; + ering->name = name; + ering->index = index; + + if (ering_alloc_queues(ering, edev, num, sq_desc_size, cq_desc_size)) + goto err_free; + + ering_init(ering); + + return ering; + +err_free: + kfree(ering); + return NULL; +} + +void eea_ering_free(struct eea_ring *ering) +{ + ering_free_queue(ering->edev, ering->cq.dma_size, + ering->cq.desc, ering->cq.dma_addr); + + ering_free_queue(ering->edev, ering->sq.dma_size, + ering->sq.desc, ering->sq.dma_addr); + + kfree(ering); +} diff --git a/drivers/net/ethernet/alibaba/eea/eea_ring.h b/drivers/net/ethernet/alibaba/eea/eea_ring.h new file mode 100644 index 000000000000..a7ce465943a5 --- /dev/null +++ b/drivers/net/ethernet/alibaba/eea/eea_ring.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Alibaba Elastic Ethernet Adapter. + * + * Copyright (C) 2025 Alibaba Inc. + */ + +#ifndef __EEA_RING_H__ +#define __EEA_RING_H__ + +#include +#include "eea_desc.h" + +#define EEA_RING_DESC_F_MORE BIT(0) +#define EEA_RING_DESC_F_CQ_PHASE BIT(7) + +/* These two values define the bounds for the queue depth returned by the + * hardware. + */ +#define EEA_NET_IO_HW_RING_DEPTH_MAX (32 * 1024) +#define EEA_NET_IO_HW_RING_DEPTH_MIN 128 + +/* This value constrains the minimum queue depth that the driver configures for + * the hardware, which typically applies to user-provided settings. Naturally, + * the configured depth must also not exceed the maximum capacity supported by + * the hardware. + */ +#define EEA_NET_IO_RING_DEPTH_MIN 64 + +struct eea_common_desc { + __le16 flags; + __le16 id; +}; + +struct eea_device; + +struct eea_ring_sq { + void *desc; + + u16 head; + u16 hw_idx; + + u16 shadow_idx; + __le16 shadow_id; + u16 shadow_num; + + u8 desc_size; + u8 desc_size_shift; + + dma_addr_t dma_addr; + u32 dma_size; +}; + +struct eea_ring_cq { + void *desc; + + u16 head; + u16 hw_idx; + + u8 phase; + u8 desc_size_shift; + u8 desc_size; + + dma_addr_t dma_addr; + u32 dma_size; +}; + +struct eea_ring { + const char *name; + struct eea_device *edev; + u32 index; + void __iomem *db; + u16 msix_vec; + + u32 num; + + u32 num_free; + + struct eea_ring_sq sq; + struct eea_ring_cq cq; +}; + +struct eea_ring *eea_ering_alloc(u32 index, u32 num, struct eea_device *edev, + u8 sq_desc_size, u8 cq_desc_size, + const char *name); +void eea_ering_free(struct eea_ring *ering); +void eea_ering_kick(struct eea_ring *ering); + +void *eea_ering_sq_alloc_desc(struct eea_ring *ering, u16 id, + bool is_last, u16 flags); +void *eea_ering_aq_alloc_desc(struct eea_ring *ering); +void eea_ering_sq_commit_desc(struct eea_ring *ering); +void eea_ering_sq_cancel(struct eea_ring *ering); + +void eea_ering_cq_ack_desc(struct eea_ring *ering, u32 num); + +void eea_ering_irq_active(struct eea_ring *ering, struct eea_ring *tx_ering); +void *eea_ering_cq_get_desc(const struct eea_ring *ering); +#endif