Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#include <uapi/linux/ppp_defs.h>

#include "airoha_regs.h"
@@ -439,6 +440,41 @@ static void airoha_fe_crsn_qsel_init(str
@@ -439,6 +440,48 @@ static void airoha_fe_crsn_qsel_init(str
CDM_CRSN_QSEL_Q1));
}

+static void airoha_fe_lro_init_rx_queue(struct airoha_eth *eth, int qdma_id,
+ int lro_queue_index, int qid,
+ int nbuf, int buf_size)
+ int buf_size)
+{
+ int id = qdma_id + 1;
+
+ airoha_fe_rmw(eth, REG_CDM_LRO_LIMIT(id),
+ CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK,
+ FIELD_PREP(CDM_LRO_AGG_NUM_MASK, nbuf) |
+ FIELD_PREP(CDM_LRO_AGG_SIZE_MASK, buf_size));
+ FIELD_PREP(CDM_LRO_AGG_SIZE_MASK, buf_size) |
+ FIELD_PREP(CDM_LRO_AGG_NUM_MASK,
+ AIROHA_RXQ_LRO_MAX_AGG_COUNT));
+ airoha_fe_rmw(eth, REG_CDM_LRO_AGE_TIME(id),
+ CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK,
+ FIELD_PREP(CDM_LRO_AGE_TIME_MASK,
Expand All @@ -51,68 +52,85 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+{
+ int i, id = qdma_id + 1;
+
+ airoha_fe_clear(eth, REG_CDM_LRO_EN(id), LRO_RXQ_EN_MASK);
+ airoha_fe_clear(eth, REG_CDM_LRO_LIMIT(id),
+ CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK);
+ airoha_fe_clear(eth, REG_CDM_LRO_AGE_TIME(id),
+ CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK);
+ airoha_fe_clear(eth, REG_CDM_LRO_EN(id), LRO_RXQ_EN_MASK);
+ for (i = 0; i < AIROHA_MAX_NUM_LRO_QUEUES; i++)
+ airoha_fe_clear(eth, REG_CDM_LRO_RXQ(id, i), LRO_RXQ_MASK(i));
+}
+
+static bool airoha_fe_lro_is_enabled(struct airoha_eth *eth, int qdma_id)
+{
+ return airoha_fe_get(eth, REG_CDM_LRO_EN(qdma_id + 1),
+ LRO_RXQ_EN_MASK);
+}
+
static int airoha_fe_init(struct airoha_eth *eth)
{
airoha_fe_maccr_init(eth);
@@ -603,9 +639,78 @@ static int airoha_qdma_get_gdm_port(stru
@@ -603,6 +646,85 @@ static int airoha_qdma_get_gdm_port(stru
return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
}

+static int airoha_qdma_lro_rx_process(struct airoha_queue *q,
+ struct airoha_qdma_desc *desc)
+{
+ u32 desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ u32 msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
+ u32 msg2 = le32_to_cpu(READ_ONCE(desc->msg2));
+ u32 msg3 = le32_to_cpu(READ_ONCE(desc->msg3));
+ bool ipv4 = FIELD_GET(QDMA_ETH_RXMSG_IP4_MASK, msg1);
+ bool ipv6 = FIELD_GET(QDMA_ETH_RXMSG_IP6_MASK, msg1);
+ struct sk_buff *skb = q->skb;
+ u32 th_off, tcp_ack_seq;
+ u32 len, th_off, tcp_ack_seq;
+ u16 tcp_win, l2_len;
+ struct tcphdr *th;
+ bool ipv4, ipv6;
+
+ if (FIELD_GET(QDMA_ETH_RXMSG_AGG_COUNT_MASK, msg2) <= 1)
+ return 0;
+
+ ipv4 = FIELD_GET(QDMA_ETH_RXMSG_IP4_MASK, msg1);
+ ipv6 = FIELD_GET(QDMA_ETH_RXMSG_IP6_MASK, msg1);
+ if (!ipv4 && !ipv6)
+ return -EOPNOTSUPP;
+
+ l2_len = FIELD_GET(QDMA_ETH_RXMSG_L2_LEN_MASK, msg2);
+ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
+ if (ipv4) {
+ u16 agg_len = FIELD_GET(QDMA_ETH_RXMSG_AGG_LEN_MASK, msg3);
+ struct iphdr *iph = (struct iphdr *)(skb->data + l2_len);
+ struct iphdr *iph;
+
+ if (!pskb_may_pull(skb, l2_len + sizeof(*iph)))
+ return -EINVAL;
+
+ iph = (struct iphdr *)(skb->data + l2_len);
+ if (iph->protocol != IPPROTO_TCP)
+ return -EOPNOTSUPP;
+
+ iph->tot_len = cpu_to_be16(agg_len);
+ iph->tot_len = cpu_to_be16(len - l2_len);
+ iph->check = 0;
+ iph->check = ip_fast_csum((void *)iph, iph->ihl);
+ th_off = l2_len + (iph->ihl << 2);
+ } else {
+ struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + l2_len);
+ u32 len, desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ struct ipv6hdr *ip6h;
+
+ if (!pskb_may_pull(skb, l2_len + sizeof(*ip6h)))
+ return -EINVAL;
+
+ ip6h = (struct ipv6hdr *)(skb->data + l2_len);
+ if (ip6h->nexthdr != NEXTHDR_TCP)
+ return -EOPNOTSUPP;
+
+ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
+ ip6h->payload_len = cpu_to_be16(len - l2_len - sizeof(*ip6h));
+ th_off = l2_len + sizeof(*ip6h);
+ }
+
+ tcp_win = FIELD_GET(QDMA_ETH_RXMSG_TCP_WIN_MASK, msg3);
+ tcp_ack_seq = le32_to_cpu(READ_ONCE(desc->data));
+
+ if (!pskb_may_pull(skb, th_off + sizeof(*th)))
+ return -EINVAL;
+
+ th = (struct tcphdr *)(skb->data + th_off);
+ th->ack_seq = cpu_to_be32(tcp_ack_seq);
+ th->window = cpu_to_be16(tcp_win);
Expand All @@ -138,111 +156,127 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
{
enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool);
+ bool lro_q = airoha_qdma_is_lro_queue(q);
struct airoha_qdma *qdma = q->qdma;
struct airoha_eth *eth = qdma->eth;
int qid = q - &qdma->q_rx[0];
@@ -652,9 +757,14 @@ static int airoha_qdma_rx_process(struct
@@ -650,11 +772,15 @@ static int airoha_qdma_rx_process(struct

skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
- skb_mark_for_recycle(q->skb);
q->skb->dev = port->dev;
- q->skb->protocol = eth_type_trans(q->skb, port->dev);
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
+
+ if (lro_q && (port->dev->features & NETIF_F_LRO) &&
+ airoha_qdma_lro_rx_process(q, desc) < 0)
+ if (airoha_qdma_lro_rx_process(q, desc) < 0)
+ goto free_frag;
+
+ q->skb->protocol = eth_type_trans(q->skb, port->dev);
+ skb_mark_for_recycle(q->skb);
} else { /* scattered frame */
struct skb_shared_info *shinfo = skb_shinfo(q->skb);
int nr_frags = shinfo->nr_frags;
@@ -743,23 +853,19 @@ static int airoha_qdma_rx_napi_poll(stru
@@ -743,12 +869,10 @@ static int airoha_qdma_rx_napi_poll(stru
static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
struct airoha_qdma *qdma, int ndesc)
{
- const struct page_pool_params pp_params = {
- .order = 0,
- .pool_size = 256,
+ struct page_pool_params pp_params = {
.pool_size = 256,
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.dma_dir = DMA_FROM_DEVICE,
- .max_len = PAGE_SIZE,
.nid = NUMA_NO_NODE,
.dev = qdma->eth->dev,
.napi = &q->napi,
};
+ int pp_order, qid = q - &qdma->q_rx[0], thr;
@@ -756,9 +880,10 @@ static int airoha_qdma_init_rx_queue(str
struct airoha_eth *eth = qdma->eth;
- int qid = q - &qdma->q_rx[0], thr;
int qid = q - &qdma->q_rx[0], thr;
dma_addr_t dma_addr;
+ bool lro_q;

- q->buf_size = PAGE_SIZE / 2;
q->qdma = qdma;
-
+ lro_q = airoha_qdma_is_lro_queue(q);

q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
GFP_KERNEL);
if (!q->entry)
@@ -770,6 +876,12 @@ static int airoha_qdma_init_rx_queue(str
@@ -770,6 +895,9 @@ static int airoha_qdma_init_rx_queue(str
if (!q->desc)
return -ENOMEM;

+ lro_q = airoha_qdma_is_lro_queue(q);
+ pp_order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0;
+ pp_params.order = pp_order;
+ pp_params.pool_size = 256 >> pp_order;
+ pp_params.max_len = PAGE_SIZE << pp_order;
+ pp_params.order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0;
+ pp_params.max_len = PAGE_SIZE << pp_params.order;
+
q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(q->page_pool)) {
int err = PTR_ERR(q->page_pool);
@@ -778,6 +890,7 @@ static int airoha_qdma_init_rx_queue(str
@@ -778,6 +906,7 @@ static int airoha_qdma_init_rx_queue(str
return err;
}

+ q->buf_size = pp_params.max_len / (2 * (1 + lro_q));
+ q->buf_size = lro_q ? pp_params.max_len : pp_params.max_len / 2;
q->ndesc = ndesc;
netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);

@@ -2032,6 +2145,45 @@ int airoha_get_fe_port(struct airoha_gdm
@@ -2034,6 +2163,67 @@ int airoha_get_fe_port(struct airoha_gdm
}
}

+static int airoha_dev_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+
+ netdev_features_t diff = dev->features ^ features;
+ struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_qdma *qdma = port->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - &eth->qdma[0];
+ int i;
+
+ if (!(diff & NETIF_F_LRO))
+ return 0;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* reset LRO configuration */
+ if (features & NETIF_F_LRO) {
+ int i, lro_queue_index = 0;
+ int lro_queue_index = 0;
+
+ if (airoha_fe_lro_is_enabled(eth, qdma_id))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+ struct airoha_queue *q = &qdma->q_rx[i];
+ u32 size;
+
+ if (!q->ndesc)
+ continue;
+
+ if (!airoha_qdma_is_lro_queue(q))
+ continue;
+
+ size = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
+ size = min_t(u32, size, CDM_LRO_AGG_SIZE_MASK);
+ airoha_fe_lro_init_rx_queue(eth, qdma_id,
+ lro_queue_index, i,
+ q->page_pool->p.pool_size,
+ q->buf_size);
+ lro_queue_index, i, size);
+ lro_queue_index++;
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *p = eth->ports[i];
+
+ if (!p)
+ continue;
+
+ if (p->qdma != qdma)
+ continue;
+
+ if (p->dev == dev)
+ continue;
+
+ if (p->dev->features & NETIF_F_LRO)
+ return 0;
+ }
+ airoha_fe_lro_disable(eth, qdma_id);
+ }
+
Expand All @@ -252,15 +286,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -2931,6 +3083,7 @@ static const struct net_device_ops airoh
@@ -2933,6 +3123,7 @@ static const struct net_device_ops airoh
.ndo_stop = airoha_dev_stop,
.ndo_change_mtu = airoha_dev_change_mtu,
.ndo_select_queue = airoha_dev_select_queue,
+ .ndo_set_features = airoha_dev_set_features,
.ndo_start_xmit = airoha_dev_xmit,
.ndo_get_stats64 = airoha_dev_get_stats64,
.ndo_set_mac_address = airoha_dev_set_macaddr,
@@ -3153,12 +3306,9 @@ static int airoha_alloc_gdm_port(struct
@@ -3150,12 +3341,9 @@ static int airoha_alloc_gdm_port(struct
dev->ethtool_ops = &airoha_ethtool_ops;
dev->max_mtu = AIROHA_MAX_MTU;
dev->watchdog_timeo = 5 * HZ;
Expand All @@ -278,13 +312,14 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>

--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -43,6 +43,17 @@
@@ -43,6 +43,18 @@
(_n) == 15 ? 128 : \
(_n) == 0 ? 1024 : 16)

+#define AIROHA_LRO_PAGE_ORDER 2
+#define AIROHA_MAX_NUM_LRO_QUEUES 8
+#define AIROHA_RXQ_LRO_EN_MASK 0xff000
+#define AIROHA_RXQ_LRO_EN_MASK 0xff000000
+#define AIROHA_RXQ_LRO_MAX_AGG_COUNT 64
+#define AIROHA_RXQ_LRO_MAX_AGG_TIME 100
+#define AIROHA_RXQ_LRO_MAX_AGE_TIME 2000 /* 1ms */
+
Expand All @@ -296,7 +331,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#define PSE_RSV_PAGES 128
#define PSE_QUEUE_RSV_PAGES 64

@@ -666,6 +677,18 @@ static inline bool airoha_is_7583(struct
@@ -666,6 +678,18 @@ static inline bool airoha_is_7583(struct
return eth->soc->version == 0x7583;
}

Expand Down
Loading
Loading