]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
eea: introduce ring and descriptor structures
authorXuan Zhuo <xuanzhuo@linux.alibaba.com>
Thu, 14 May 2026 09:51:32 +0000 (17:51 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 19 May 2026 10:07:49 +0000 (12:07 +0200)
Add basic driver framework for the Alibaba Elastic Ethernet Adapter(EEA).

This commit introduces the ring and descriptor implementations.

These structures and ring APIs are used by the RX, TX, and admin queues.

Reviewed-by: Dust Li <dust.li@linux.alibaba.com>
Reviewed-by: Philo Lu <lulie@linux.alibaba.com>
Signed-off-by: Wen Gu <guwen@linux.alibaba.com>
Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Link: https://patch.msgid.link/20260514095138.80680-3-xuanzhuo@linux.alibaba.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/alibaba/eea/Makefile
drivers/net/ethernet/alibaba/eea/eea_desc.h [new file with mode: 0644]
drivers/net/ethernet/alibaba/eea/eea_ring.c [new file with mode: 0644]
drivers/net/ethernet/alibaba/eea/eea_ring.h [new file with mode: 0644]

index a07f3019ec1f2edfb6a664f24f7a759ce5cf58d0..7d8e7e8c2f3aac66a96749840292ee546bdde6f3 100644 (file)
@@ -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 (file)
index 0000000..8d94a0f
--- /dev/null
@@ -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 (file)
index 0000000..99dcabd
--- /dev/null
@@ -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 (file)
index 0000000..a7ce465
--- /dev/null
@@ -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 <linux/dma-mapping.h>
+#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