ramips: ethernet: unify tx descriptor buffer splitting
A buffer is split into multiple descriptors if it exceeds 16 KB. Apply the same split for the skb head as well (to deal with corner cases on fraglist support) Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
17c9b72046
commit
b605a84a74
1 changed files with 83 additions and 75 deletions
|
@ -565,40 +565,82 @@ static inline u32 fe_empty_txd(struct fe_tx_ring *ring)
|
||||||
(ring->tx_ring_size - 1)));
|
(ring->tx_ring_size - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fe_tx_dma_map_page(struct device *dev, struct fe_tx_buf *tx_buf,
|
struct fe_map_state {
|
||||||
struct fe_tx_dma *txd, int idx,
|
struct device *dev;
|
||||||
struct page *page, size_t offset, size_t size)
|
struct fe_tx_dma txd;
|
||||||
|
u32 def_txd4;
|
||||||
|
int ring_idx;
|
||||||
|
int i;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fe_tx_dma_write_desc(struct fe_tx_ring *ring, struct fe_map_state *st)
|
||||||
{
|
{
|
||||||
|
fe_set_txd(&st->txd, &ring->tx_dma[st->ring_idx]);
|
||||||
|
memset(&st->txd, 0, sizeof(st->txd));
|
||||||
|
st->txd.txd4 = st->def_txd4;
|
||||||
|
st->ring_idx = NEXT_TX_DESP_IDX(st->ring_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __fe_tx_dma_map_page(struct fe_tx_ring *ring, struct fe_map_state *st,
|
||||||
|
struct page *page, size_t offset, size_t size)
|
||||||
|
{
|
||||||
|
struct device *dev = st->dev;
|
||||||
|
struct fe_tx_buf *tx_buf;
|
||||||
dma_addr_t mapped_addr;
|
dma_addr_t mapped_addr;
|
||||||
|
|
||||||
mapped_addr = dma_map_page(dev, page, offset, size, DMA_TO_DEVICE);
|
mapped_addr = dma_map_page(dev, page, offset, size, DMA_TO_DEVICE);
|
||||||
if (unlikely(dma_mapping_error(dev, mapped_addr)))
|
if (unlikely(dma_mapping_error(dev, mapped_addr)))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (idx & 1) {
|
if (st->i && !(st->i & 1))
|
||||||
txd->txd3 = mapped_addr;
|
fe_tx_dma_write_desc(ring, st);
|
||||||
txd->txd2 |= TX_DMA_PLEN1(size);
|
|
||||||
|
tx_buf = &ring->tx_buf[st->ring_idx];
|
||||||
|
if (st->i & 1) {
|
||||||
|
st->txd.txd3 = mapped_addr;
|
||||||
|
st->txd.txd2 |= TX_DMA_PLEN1(size);
|
||||||
dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr);
|
dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr);
|
||||||
dma_unmap_len_set(tx_buf, dma_len1, size);
|
dma_unmap_len_set(tx_buf, dma_len1, size);
|
||||||
} else {
|
} else {
|
||||||
tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
|
tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
|
||||||
txd->txd1 = mapped_addr;
|
st->txd.txd1 = mapped_addr;
|
||||||
txd->txd2 = TX_DMA_PLEN0(size);
|
st->txd.txd2 = TX_DMA_PLEN0(size);
|
||||||
dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
|
dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
|
||||||
dma_unmap_len_set(tx_buf, dma_len0, size);
|
dma_unmap_len_set(tx_buf, dma_len0, size);
|
||||||
}
|
}
|
||||||
|
st->i++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fe_tx_dma_map_skb(struct device *dev, struct fe_tx_buf *tx_buf,
|
static int fe_tx_dma_map_page(struct fe_tx_ring *ring, struct fe_map_state *st,
|
||||||
struct fe_tx_dma *txd, int idx,
|
struct page *page, size_t offset, size_t size)
|
||||||
|
{
|
||||||
|
int cur_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
cur_size = min_t(size_t, size, TX_DMA_BUF_LEN);
|
||||||
|
|
||||||
|
ret = __fe_tx_dma_map_page(ring, st, page, offset, cur_size);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
size -= cur_size;
|
||||||
|
offset += cur_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fe_tx_dma_map_skb(struct fe_tx_ring *ring, struct fe_map_state *st,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct page *page = virt_to_page(skb->data);
|
struct page *page = virt_to_page(skb->data);
|
||||||
size_t offset = offset_in_page(skb->data);
|
size_t offset = offset_in_page(skb->data);
|
||||||
size_t size = skb_headlen(skb);
|
size_t size = skb_headlen(skb);
|
||||||
|
|
||||||
return fe_tx_dma_map_page(dev, tx_buf, txd, idx, page, offset, size);
|
return fe_tx_dma_map_page(ring, st, page, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct sk_buff *
|
static inline struct sk_buff *
|
||||||
|
@ -613,41 +655,39 @@ fe_next_frag(struct sk_buff *head, struct sk_buff *skb)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int fe_tx_map_dma(struct sk_buff *skb, struct net_device *dev,
|
static int fe_tx_map_dma(struct sk_buff *skb, struct net_device *dev,
|
||||||
int tx_num, struct fe_tx_ring *ring)
|
int tx_num, struct fe_tx_ring *ring)
|
||||||
{
|
{
|
||||||
struct fe_priv *priv = netdev_priv(dev);
|
struct fe_priv *priv = netdev_priv(dev);
|
||||||
struct skb_frag_struct *frag;
|
struct fe_map_state st = {
|
||||||
struct fe_tx_dma txd, *ptxd;
|
.dev = &dev->dev,
|
||||||
struct fe_tx_buf *tx_buf;
|
.ring_idx = ring->tx_next_idx,
|
||||||
|
};
|
||||||
struct sk_buff *head = skb;
|
struct sk_buff *head = skb;
|
||||||
|
struct fe_tx_buf *tx_buf;
|
||||||
unsigned int nr_frags;
|
unsigned int nr_frags;
|
||||||
u32 def_txd4;
|
int i, j;
|
||||||
int i, j, k, frag_size, frag_map_size, offset;
|
|
||||||
|
|
||||||
tx_buf = &ring->tx_buf[ring->tx_next_idx];
|
|
||||||
memset(tx_buf, 0, sizeof(*tx_buf));
|
|
||||||
memset(&txd, 0, sizeof(txd));
|
|
||||||
|
|
||||||
/* init tx descriptor */
|
/* init tx descriptor */
|
||||||
if (priv->soc->tx_dma)
|
if (priv->soc->tx_dma)
|
||||||
priv->soc->tx_dma(&txd);
|
priv->soc->tx_dma(&st.txd);
|
||||||
else
|
else
|
||||||
txd.txd4 = TX_DMA_DESP4_DEF;
|
st.txd.txd4 = TX_DMA_DESP4_DEF;
|
||||||
def_txd4 = txd.txd4;
|
st.def_txd4 = st.txd.txd4;
|
||||||
|
|
||||||
/* TX Checksum offload */
|
/* TX Checksum offload */
|
||||||
if (skb->ip_summed == CHECKSUM_PARTIAL)
|
if (skb->ip_summed == CHECKSUM_PARTIAL)
|
||||||
txd.txd4 |= TX_DMA_CHKSUM;
|
st.txd.txd4 |= TX_DMA_CHKSUM;
|
||||||
|
|
||||||
/* VLAN header offload */
|
/* VLAN header offload */
|
||||||
if (skb_vlan_tag_present(skb)) {
|
if (skb_vlan_tag_present(skb)) {
|
||||||
u16 tag = skb_vlan_tag_get(skb);
|
u16 tag = skb_vlan_tag_get(skb);
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_SOC_MT7621))
|
if (IS_ENABLED(CONFIG_SOC_MT7621))
|
||||||
txd.txd4 |= TX_DMA_INS_VLAN_MT7621 | tag;
|
st.txd.txd4 |= TX_DMA_INS_VLAN_MT7621 | tag;
|
||||||
else
|
else
|
||||||
txd.txd4 |= TX_DMA_INS_VLAN |
|
st.txd.txd4 |= TX_DMA_INS_VLAN |
|
||||||
((tag >> VLAN_PRIO_SHIFT) << 4) |
|
((tag >> VLAN_PRIO_SHIFT) << 4) |
|
||||||
(tag & 0xF);
|
(tag & 0xF);
|
||||||
}
|
}
|
||||||
|
@ -661,75 +701,46 @@ static int fe_tx_map_dma(struct sk_buff *skb, struct net_device *dev,
|
||||||
}
|
}
|
||||||
if (skb_shinfo(skb)->gso_type &
|
if (skb_shinfo(skb)->gso_type &
|
||||||
(SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
|
(SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
|
||||||
txd.txd4 |= TX_DMA_TSO;
|
st.txd.txd4 |= TX_DMA_TSO;
|
||||||
tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
|
tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
k = 0;
|
|
||||||
j = ring->tx_next_idx;
|
|
||||||
|
|
||||||
next_frag:
|
next_frag:
|
||||||
if (skb_headlen(skb)) {
|
if (skb_headlen(skb) && fe_tx_dma_map_skb(ring, &st, skb))
|
||||||
if (fe_tx_dma_map_skb(&dev->dev, tx_buf, &txd, k++, skb))
|
goto err_dma;
|
||||||
goto err_dma;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TX SG offload */
|
/* TX SG offload */
|
||||||
nr_frags = skb_shinfo(skb)->nr_frags;
|
nr_frags = skb_shinfo(skb)->nr_frags;
|
||||||
for (i = 0; i < nr_frags; i++) {
|
for (i = 0; i < nr_frags; i++) {
|
||||||
struct page *page;
|
struct skb_frag_struct *frag;
|
||||||
|
|
||||||
frag = &skb_shinfo(skb)->frags[i];
|
frag = &skb_shinfo(skb)->frags[i];
|
||||||
frag_size = skb_frag_size(frag);
|
if (fe_tx_dma_map_page(ring, &st, skb_frag_page(frag),
|
||||||
offset = frag->page_offset;
|
frag->page_offset, skb_frag_size(frag)))
|
||||||
page = skb_frag_page(frag);
|
goto err_dma;
|
||||||
|
|
||||||
while (frag_size > 0) {
|
|
||||||
frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
|
|
||||||
if (!(k & 0x1)) {
|
|
||||||
fe_set_txd(&txd, &ring->tx_dma[j]);
|
|
||||||
memset(&txd, 0, sizeof(txd));
|
|
||||||
txd.txd4 = def_txd4;
|
|
||||||
j = NEXT_TX_DESP_IDX(j);
|
|
||||||
tx_buf = &ring->tx_buf[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fe_tx_dma_map_page(&dev->dev, tx_buf, &txd, k++,
|
|
||||||
page, offset, frag_map_size))
|
|
||||||
goto err_dma;
|
|
||||||
|
|
||||||
frag_size -= frag_map_size;
|
|
||||||
offset += frag_map_size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
skb = fe_next_frag(head, skb);
|
skb = fe_next_frag(head, skb);
|
||||||
if (skb) {
|
if (skb)
|
||||||
if (!(k & 0x1)) {
|
|
||||||
fe_set_txd(&txd, &ring->tx_dma[j]);
|
|
||||||
memset(&txd, 0, sizeof(txd));
|
|
||||||
txd.txd4 = def_txd4;
|
|
||||||
j = NEXT_TX_DESP_IDX(j);
|
|
||||||
tx_buf = &ring->tx_buf[j];
|
|
||||||
}
|
|
||||||
goto next_frag;
|
goto next_frag;
|
||||||
}
|
|
||||||
|
|
||||||
/* set last segment */
|
/* set last segment */
|
||||||
if (k & 0x1)
|
if (st.i & 0x1)
|
||||||
txd.txd2 |= TX_DMA_LS0;
|
st.txd.txd2 |= TX_DMA_LS0;
|
||||||
else
|
else
|
||||||
txd.txd2 |= TX_DMA_LS1;
|
st.txd.txd2 |= TX_DMA_LS1;
|
||||||
fe_set_txd(&txd, &ring->tx_dma[j]);
|
|
||||||
|
|
||||||
/* store skb to cleanup */
|
/* store skb to cleanup */
|
||||||
|
tx_buf = &ring->tx_buf[st.ring_idx];
|
||||||
tx_buf->skb = head;
|
tx_buf->skb = head;
|
||||||
|
|
||||||
netdev_sent_queue(dev, head->len);
|
netdev_sent_queue(dev, head->len);
|
||||||
skb_tx_timestamp(head);
|
skb_tx_timestamp(head);
|
||||||
|
|
||||||
ring->tx_next_idx = NEXT_TX_DESP_IDX(j);
|
fe_tx_dma_write_desc(ring, &st);
|
||||||
|
ring->tx_next_idx = st.ring_idx;
|
||||||
|
|
||||||
/* make sure that all changes to the dma ring are flushed before we
|
/* make sure that all changes to the dma ring are flushed before we
|
||||||
* continue
|
* continue
|
||||||
*/
|
*/
|
||||||
|
@ -749,13 +760,10 @@ next_frag:
|
||||||
err_dma:
|
err_dma:
|
||||||
j = ring->tx_next_idx;
|
j = ring->tx_next_idx;
|
||||||
for (i = 0; i < tx_num; i++) {
|
for (i = 0; i < tx_num; i++) {
|
||||||
ptxd = &ring->tx_dma[j];
|
|
||||||
tx_buf = &ring->tx_buf[j];
|
|
||||||
|
|
||||||
/* unmap dma */
|
/* unmap dma */
|
||||||
fe_txd_unmap(&dev->dev, tx_buf);
|
fe_txd_unmap(&dev->dev, &ring->tx_buf[j]);
|
||||||
|
ring->tx_dma[j].txd2 = TX_DMA_DESP2_DEF;
|
||||||
|
|
||||||
ptxd->txd2 = TX_DMA_DESP2_DEF;
|
|
||||||
j = NEXT_TX_DESP_IDX(j);
|
j = NEXT_TX_DESP_IDX(j);
|
||||||
}
|
}
|
||||||
/* make sure that all changes to the dma ring are flushed before we
|
/* make sure that all changes to the dma ring are flushed before we
|
||||||
|
|
Loading…
Reference in a new issue