2026-01-29 22:25:33 +08:00

1535 lines
41 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2020 - 2023, Chengdu BeiZhongWangXin Technology Co., Ltd. */
#include "txrx.h"
int ne6x_setup_tx_descriptors(struct ne6x_ring *tx_ring)
{
struct device *dev = tx_ring->dev;
int bi_size;
if (!dev)
return -ENOMEM;
/* warn if we are about to overwrite the pointer */
WARN_ON(tx_ring->tx_buf);
bi_size = sizeof(struct ne6x_tx_buf) * tx_ring->count;
tx_ring->tx_buf = kzalloc(bi_size, GFP_KERNEL);
if (!tx_ring->tx_buf)
goto err;
/* round up to nearest 4K */
tx_ring->size = tx_ring->count * sizeof(struct ne6x_tx_desc);
tx_ring->size = ALIGN(tx_ring->size, 4096);
tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, &tx_ring->dma, GFP_KERNEL);
if (!tx_ring->desc) {
dev_info(dev, "Unable to allocate memory for the Tx descriptor ring, size=%d\n",
tx_ring->size);
goto err;
}
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
tx_ring->cq_last_expect = 0;
return 0;
err:
kfree(tx_ring->tx_buf);
tx_ring->tx_buf = NULL;
return -ENOMEM;
}
int ne6x_setup_cq_descriptors(struct ne6x_ring *cq_ring)
{
struct device *dev = cq_ring->dev;
if (!dev)
return -ENOMEM;
/* round up to nearest 4K */
cq_ring->size = cq_ring->count * sizeof(struct ne6x_cq_desc);
cq_ring->size = ALIGN(cq_ring->size, 4096);
cq_ring->desc = dma_alloc_coherent(dev, cq_ring->size, &cq_ring->dma, GFP_KERNEL);
if (!cq_ring->desc) {
dev_info(dev, "Unable to allocate memory for the Tx descriptor ring, size=%d\n",
cq_ring->size);
goto err;
}
cq_ring->next_to_use = 0;
cq_ring->next_to_clean = 0;
return 0;
err:
return -ENOMEM;
}
int ne6x_setup_tg_descriptors(struct ne6x_ring *tg_ring)
{
struct device *dev = tg_ring->dev;
if (!dev)
return -ENOMEM;
/* round up to nearest 4K */
tg_ring->size = tg_ring->count * sizeof(struct ne6x_tx_tag);
tg_ring->size = ALIGN(tg_ring->size, 4096);
tg_ring->desc = dma_alloc_coherent(dev, tg_ring->size, &tg_ring->dma, GFP_KERNEL);
if (!tg_ring->desc) {
dev_info(dev, "Unable to allocate memory for the Tx descriptor ring, size=%d\n",
tg_ring->size);
goto err;
}
tg_ring->next_to_use = 0;
tg_ring->next_to_clean = 0;
return 0;
err:
return -ENOMEM;
}
int ne6x_setup_rx_descriptors(struct ne6x_ring *rx_ring)
{
struct device *dev = rx_ring->dev;
int err = -ENOMEM;
int bi_size;
/* warn if we are about to overwrite the pointer */
WARN_ON(rx_ring->rx_buf);
bi_size = sizeof(struct ne6x_rx_buf) * rx_ring->count;
rx_ring->rx_buf = kzalloc(bi_size, GFP_KERNEL);
if (!rx_ring->rx_buf)
goto err;
u64_stats_init(&rx_ring->syncp);
/* Round up to nearest 4K */
rx_ring->size = rx_ring->count * sizeof(union ne6x_rx_desc);
rx_ring->size = ALIGN(rx_ring->size, 4096);
rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL);
if (!rx_ring->desc)
goto err;
rx_ring->next_to_alloc = 0;
rx_ring->next_to_clean = 0;
rx_ring->next_to_use = 0;
rx_ring->cq_last_expect = 0;
return 0;
err:
kfree(rx_ring->rx_buf);
rx_ring->rx_buf = NULL;
return err;
}
int ne6x_setup_tx_sgl(struct ne6x_ring *tx_ring)
{
struct device *dev = tx_ring->dev;
if (!dev)
return -ENOMEM;
tx_ring->sgl = kzalloc(sizeof(*tx_ring->sgl), GFP_KERNEL);
if (!tx_ring->sgl)
goto err;
return 0;
err:
return -ENOMEM;
}
int __ne6x_maybe_stop_tx(struct ne6x_ring *tx_ring, int size);
static inline int ne6x_maybe_stop_tx(struct ne6x_ring *tx_ring, int size)
{
if (likely(NE6X_DESC_UNUSED(tx_ring) >= size))
return 0;
return __ne6x_maybe_stop_tx(tx_ring, size);
}
static inline bool ne6x_rx_is_programming_status(u8 status)
{
return status & 0x20;
}
static void ne6x_reuse_rx_page(struct ne6x_ring *rx_ring, struct ne6x_rx_buf *old_buff)
{
u16 nta = rx_ring->next_to_alloc;
struct ne6x_rx_buf *new_buff;
new_buff = &rx_ring->rx_buf[nta];
/* update, and store next to alloc */
nta++;
rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0;
/* transfer page from old buffer to new buffer */
new_buff->dma = old_buff->dma;
new_buff->page = old_buff->page;
new_buff->page_offset = old_buff->page_offset;
new_buff->pagecnt_bias = old_buff->pagecnt_bias;
}
static void ne6x_clean_programming_status(struct ne6x_ring *rx_ring,
union ne6x_rx_desc *rx_desc,
u8 status)
{
u32 ntc = rx_ring->next_to_clean;
struct ne6x_rx_buf *rx_buffer;
/* fetch, update, and store next to clean */
rx_buffer = &rx_ring->rx_buf[ntc++];
ntc = (ntc < rx_ring->count) ? ntc : 0;
rx_ring->next_to_clean = ntc;
prefetch(NE6X_RX_DESC(rx_ring, ntc));
/* place unused page back on the ring */
ne6x_reuse_rx_page(rx_ring, rx_buffer);
rx_ring->rx_stats.page_reuse_count++;
/* clear contents of buffer_info */
rx_buffer->page = NULL;
}
static struct ne6x_rx_buf *ne6x_get_rx_buffer(struct ne6x_ring *rx_ring, const unsigned int size)
{
struct ne6x_rx_buf *rx_buffer;
rx_buffer = &rx_ring->rx_buf[rx_ring->next_to_clean];
prefetchw(rx_buffer->page);
/* we are reusing so sync this buffer for CPU use */
dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma, rx_buffer->page_offset, size,
DMA_FROM_DEVICE);
/* We have pulled a buffer for use, so decrement pagecnt_bias */
rx_buffer->pagecnt_bias--;
return rx_buffer;
}
static void ne6x_add_rx_frag(struct ne6x_ring *rx_ring, struct ne6x_rx_buf *rx_buffer,
struct sk_buff *skb, unsigned int size)
{
#if (PAGE_SIZE < 8192)
unsigned int truesize = ne6x_rx_pg_size(rx_ring) / 2;
#else
unsigned int truesize = SKB_DATA_ALIGN(size);
#endif
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, rx_buffer->page_offset,
size, truesize);
/* page is being used so we must update the page offset */
#if (PAGE_SIZE < 8192)
rx_buffer->page_offset ^= truesize;
#else
rx_buffer->page_offset += truesize;
#endif
}
static struct sk_buff *ne6x_construct_skb(struct ne6x_ring *rx_ring,
struct ne6x_rx_buf *rx_buffer,
unsigned int size)
{
void *page_addr = page_address(rx_buffer->page) + rx_buffer->page_offset;
#if (PAGE_SIZE < 8192)
unsigned int truesize = ne6x_rx_pg_size(rx_ring) / 2;
#else
unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +
SKB_DATA_ALIGN(size);
#endif
unsigned int headlen;
struct sk_buff *skb;
/* prefetch first cache line of first page */
prefetch(page_addr);
#if L1_CACHE_BYTES < 128
prefetch((void *)((u8 *)page_addr + L1_CACHE_BYTES));
#endif
/* allocate a skb to store the frags */
skb = __napi_alloc_skb(&rx_ring->q_vector->napi, NE6X_RX_HDR_SIZE,
GFP_ATOMIC | __GFP_NOWARN);
if (unlikely(!skb))
return NULL;
/* Determine available headroom for copy */
headlen = size;
if (headlen > NE6X_RX_HDR_SIZE)
headlen = eth_get_headlen(skb->dev, page_addr, NE6X_RX_HDR_SIZE);
/* align pull length to size of long to optimize memcpy performance */
memcpy(__skb_put(skb, headlen), page_addr, ALIGN(headlen, sizeof(long)));
/* update all of the pointers */
size -= headlen;
if (size) {
skb_add_rx_frag(skb, 0, rx_buffer->page, rx_buffer->page_offset + headlen, size,
truesize);
/* buffer is used by skb, update page_offset */
#if (PAGE_SIZE < 8192)
rx_buffer->page_offset ^= truesize;
#else
rx_buffer->page_offset += truesize;
#endif
} else {
/* buffer is unused, reset bias back to rx_buffer */
rx_buffer->pagecnt_bias++;
}
return skb;
}
static inline bool ne6x_page_is_reusable(struct page *page)
{
return (page_to_nid(page) == numa_mem_id()) && !page_is_pfmemalloc(page);
}
static bool ne6x_can_reuse_rx_page(struct ne6x_rx_buf *rx_buffer)
{
unsigned int pagecnt_bias = rx_buffer->pagecnt_bias;
struct page *page = rx_buffer->page;
/* Is any reuse possible? */
if (unlikely(!ne6x_page_is_reusable(page)))
return false;
#if (PAGE_SIZE < 8192)
/* if we are only owner of page we can reuse it */
if (unlikely((page_count(page) - pagecnt_bias) > 1))
return false;
#else
#define NE6X_LAST_OFFSET (SKB_WITH_OVERHEAD(PAGE_SIZE) - NE6X_RXBUFFER_4096)
if (rx_buffer->page_offset > NE6X_LAST_OFFSET)
return false;
#endif
/* If we have drained the page fragment pool we need to update
* the pagecnt_bias and page count so that we fully restock the
* number of references the driver holds.
*/
if (unlikely(pagecnt_bias == 1)) {
page_ref_add(page, USHRT_MAX - 1);
rx_buffer->pagecnt_bias = USHRT_MAX;
}
return true;
}
static void ne6x_put_rx_buffer(struct ne6x_ring *rx_ring, struct ne6x_rx_buf *rx_buffer)
{
if (ne6x_can_reuse_rx_page(rx_buffer)) {
/* hand second half of page back to the ring */
ne6x_reuse_rx_page(rx_ring, rx_buffer);
rx_ring->rx_stats.page_reuse_count++;
} else {
/* we are not reusing the buffer so unmap it */
dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, ne6x_rx_pg_size(rx_ring),
DMA_FROM_DEVICE, NE6X_RX_DMA_ATTR);
__page_frag_cache_drain(rx_buffer->page, rx_buffer->pagecnt_bias);
}
/* clear contents of buffer_info */
rx_buffer->page = NULL;
}
static inline bool ne6x_test_staterr(union ne6x_rx_desc *rx_desc, const u8 stat_err_bits)
{
return !!(rx_desc->wb.u.val & stat_err_bits);
}
static bool ne6x_is_non_eop(struct ne6x_ring *rx_ring, union ne6x_rx_desc *rx_desc,
struct sk_buff *skb)
{
u32 ntc = rx_ring->next_to_clean + 1;
/* fetch, update, and store next to clean */
ntc = (ntc < rx_ring->count) ? ntc : 0;
rx_ring->next_to_clean = ntc;
prefetch(NE6X_RX_DESC(rx_ring, ntc));
/* if we are the last buffer then there is nothing else to do */
#define NE6X_RXD_EOF BIT(NE6X_RX_DESC_STATUS_EOF_SHIFT)
if (likely(ne6x_test_staterr(rx_desc, NE6X_RXD_EOF)))
return false;
rx_ring->rx_stats.non_eop_descs++;
rx_desc->wb.u.val = 0;
return true;
}
static bool ne6x_cleanup_headers(struct ne6x_ring *rx_ring, struct sk_buff *skb,
union ne6x_rx_desc *rx_desc)
{
if (unlikely(ne6x_test_staterr(rx_desc, BIT(NE6X_RX_DESC_STATUS_ERR_SHIFT)))) {
dev_kfree_skb_any(skb);
rx_ring->rx_stats.rx_mem_error++;
return true;
}
/* if eth_skb_pad returns an error the skb was freed */
if (eth_skb_pad(skb))
return true;
return false;
}
static inline void ne6x_rx_hash(struct ne6x_ring *ring, union ne6x_rx_desc *rx_desc,
struct sk_buff *skb, struct rx_hdr_info *rx_hdr)
{
if (!(ring->netdev->features & NETIF_F_RXHASH))
return;
if (rx_hdr->ol_flag.flag_bits.rx_rss_hash)
skb_set_hash(skb, rx_hdr->rss_hash, PKT_HASH_TYPE_NONE);
}
static inline void ne6x_rx_checksum(struct ne6x_ring *rx_ring, struct sk_buff *skb,
union ne6x_rx_desc *rx_desc,
struct rx_hdr_info *rx_hdr)
{
skb->ip_summed = CHECKSUM_NONE;
skb->csum_level = 0;
skb_checksum_none_assert(skb);
if (!(rx_ring->netdev->features & NETIF_F_RXCSUM))
return;
if (rx_hdr->ol_flag.flag_bits.rx_ip_cksum_bad ||
rx_hdr->ol_flag.flag_bits.rx_l4_cksum_bad ||
rx_hdr->ol_flag.flag_bits.rx_inner_ip_cksum_bad ||
rx_hdr->ol_flag.flag_bits.rx_inner_l4_cksum_bad) {
rx_ring->rx_stats.csum_err++;
} else if (rx_hdr->ol_flag.flag_bits.rx_ip_cksum_good ||
rx_hdr->ol_flag.flag_bits.rx_l4_cksum_good ||
rx_hdr->ol_flag.flag_bits.rx_inner_ip_cksum_good ||
rx_hdr->ol_flag.flag_bits.rx_inner_l4_cksum_good) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum_level = 1;
}
}
static inline void ne6x_process_skb_fields(struct ne6x_ring *rx_ring,
union ne6x_rx_desc *rx_desc,
struct sk_buff *skb,
struct rx_hdr_info *rx_hdr)
{
netdev_features_t features = rx_ring->netdev->features;
bool non_zero_vlan = false;
ne6x_rx_hash(rx_ring, rx_desc, skb, rx_hdr);
rx_hdr->vlan_tci = ntohs(rx_hdr->vlan_tci);
rx_hdr->vlan_tci_outer = ntohs(rx_hdr->vlan_tci_outer);
if (features & NETIF_F_HW_VLAN_CTAG_RX) {
if (rx_hdr->ol_flag.flag_bits.rx_vlan_striped) {
non_zero_vlan = !!(rx_hdr->vlan_tci_outer & VLAN_VID_MASK);
if (non_zero_vlan) {
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
(rx_hdr->vlan_tci_outer));
}
}
} else if (features & NETIF_F_HW_VLAN_STAG_RX) {
if (rx_hdr->ol_flag.flag_bits.rx_qinq_striped) {
non_zero_vlan = !!(rx_hdr->vlan_tci_outer & VLAN_VID_MASK);
if (non_zero_vlan) {
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD),
(rx_hdr->vlan_tci_outer));
}
}
}
ne6x_rx_checksum(rx_ring, skb, rx_desc, rx_hdr);
skb_record_rx_queue(skb, rx_ring->queue_index);
/* modifies the skb - consumes the enet header */
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
}
static void ne6x_receive_skb(struct ne6x_ring *rx_ring, struct sk_buff *skb)
{
struct ne6x_q_vector *q_vector = rx_ring->q_vector;
napi_gro_receive(&q_vector->napi, skb);
}
static bool ne6x_alloc_mapped_page(struct ne6x_ring *rx_ring, struct ne6x_rx_buf *bi)
{
struct page *page = bi->page;
dma_addr_t dma;
/* since we are recycling buffers we should seldom need to alloc */
if (likely(page)) {
rx_ring->rx_stats.page_reuse_count++;
return true;
}
/* alloc new page for storage */
page = dev_alloc_pages(ne6x_rx_pg_order(rx_ring));
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_page_failed++;
return false;
}
/* map page for use */
dma = dma_map_page_attrs(rx_ring->dev, page, 0, ne6x_rx_pg_size(rx_ring), DMA_FROM_DEVICE,
NE6X_RX_DMA_ATTR);
/* if mapping failed free memory back to system since
* there isn't much point in holding memory we can't use
*/
if (dma_mapping_error(rx_ring->dev, dma)) {
__free_pages(page, ne6x_rx_pg_order(rx_ring));
rx_ring->rx_stats.alloc_page_failed++;
return false;
}
bi->dma = dma;
bi->page = page;
bi->page_offset = 0;
page_ref_add(page, USHRT_MAX - 1);
bi->pagecnt_bias = USHRT_MAX;
return true;
}
void ne6x_tail_update(struct ne6x_ring *ring, int val)
{
int i;
for (i = 0; i < NE6X_TAIL_REG_NUM; i++)
writeq(val, ring->tail + i);
}
static inline void ne6x_release_rx_desc(struct ne6x_ring *rx_ring, u32 val)
{
rx_ring->next_to_use = val;
/* update next to alloc since we have filled the ring */
rx_ring->next_to_alloc = val;
/* Force memory writes to complete before letting h/w
* know there are new descriptors to fetch. (Only
* applicable for weak-ordered memory model archs,
* such as IA-64).
*/
wmb();
ne6x_tail_update(rx_ring, val);
}
bool ne6x_alloc_rx_buffers(struct ne6x_ring *rx_ring, u16 cleaned_count)
{
u16 ntu = rx_ring->next_to_use;
union ne6x_rx_desc *rx_desc;
struct ne6x_rx_buf *bi;
/* do nothing if no valid netdev defined */
if (!rx_ring->netdev || !cleaned_count)
return false;
rx_desc = NE6X_RX_DESC(rx_ring, ntu);
bi = &rx_ring->rx_buf[ntu];
do {
if (!ne6x_alloc_mapped_page(rx_ring, bi))
goto no_buffers;
/* sync the buffer for use by the device */
dma_sync_single_range_for_device(rx_ring->dev, bi->dma, bi->page_offset,
rx_ring->rx_buf_len, DMA_FROM_DEVICE);
/* Refresh the desc even if buffer_addrs didn't change
* because each write-back erases this info.
*/
rx_desc->wb.u.val = 0;
rx_desc->w.buffer_mop_addr = cpu_to_le64(bi->dma + bi->page_offset);
rx_desc->w.buffer_sop_addr = 0;
rx_desc->w.mop_mem_len = rx_ring->rx_buf_len;
rx_desc->wb.pkt_len = 0;
rx_desc->w.vp = rx_ring->reg_idx;
rx_desc++;
bi++;
ntu++;
if (unlikely(ntu == rx_ring->count)) {
rx_desc = NE6X_RX_DESC(rx_ring, 0);
bi = rx_ring->rx_buf;
ntu = 0;
}
/* clear the status bits for the next_to_use descriptor */
rx_desc->wb.u.val = 0;
cleaned_count--;
} while (cleaned_count);
if (rx_ring->next_to_use != ntu)
ne6x_release_rx_desc(rx_ring, ntu);
return false;
no_buffers:
if (rx_ring->next_to_use != ntu)
ne6x_release_rx_desc(rx_ring, ntu);
/* make sure to come back via polling to try again after
* allocation failure
*/
return true;
}
static void ne6x_get_rx_head_info(struct sk_buff *skb, struct rx_hdr_info *rx_hdr)
{
skb_frag_t *frag;
void *page_addr;
u32 temp_len, i;
if (skb->data_len == 0) {
memcpy(rx_hdr, &skb->data[skb->len - 16], sizeof(struct rx_hdr_info));
} else {
if (skb_shinfo(skb)->nr_frags > 1) {
i = skb_shinfo(skb)->nr_frags - 1;
frag = &skb_shinfo(skb)->frags[i];
if (skb_frag_size(frag) >= 16) {
page_addr = skb_frag_address(frag) + skb_frag_size(frag) - 16;
memcpy(rx_hdr, page_addr, sizeof(struct rx_hdr_info));
} else if (skb_frag_size(frag) > 4) {
page_addr = skb_frag_address(frag);
temp_len = skb_frag_size(frag);
memcpy((char *)rx_hdr + 16 - temp_len, page_addr, temp_len - 4);
frag = &skb_shinfo(skb)->frags[i - 1];
page_addr = skb_frag_address(frag) + skb_frag_size(frag) - 16 +
temp_len;
memcpy(rx_hdr, page_addr, 16 - temp_len);
} else {
page_addr = skb_frag_address(frag);
temp_len = skb_frag_size(frag);
frag = &skb_shinfo(skb)->frags[i - 1];
page_addr = skb_frag_address(frag) + skb_frag_size(frag) - 16 +
temp_len;
memcpy(rx_hdr, page_addr, sizeof(struct rx_hdr_info));
}
} else {
frag = &skb_shinfo(skb)->frags[0];
if (skb_frag_size(frag) >= 16) {
page_addr = skb_frag_address(frag) + skb_frag_size(frag) - 16;
memcpy(rx_hdr, page_addr, sizeof(struct rx_hdr_info));
} else if (skb_frag_size(frag) > 4) {
page_addr = skb_frag_address(frag);
temp_len = skb_frag_size(frag);
memcpy((char *)rx_hdr + 16 - temp_len, page_addr, temp_len - 4);
page_addr = &skb->data[skb->len - skb->data_len - 16 + temp_len];
memcpy(rx_hdr, page_addr, 16 - temp_len);
} else {
page_addr = skb_frag_address(frag);
temp_len = skb_frag_size(frag);
page_addr = &skb->data[skb->len - skb->data_len - 16 + temp_len];
memcpy(rx_hdr, page_addr, sizeof(struct rx_hdr_info));
}
}
}
}
static void ne6x_clean_tx_desc(struct ne6x_tx_desc *tx_desc, struct ne6x_ring *ring)
{
if (tx_desc->u.flags.tx_drop_addr)
ring->tx_stats.tx_drop_addr++;
if (tx_desc->u.flags.tx_ecc_err)
ring->tx_stats.tx_ecc_err++;
if (tx_desc->u.flags.tx_pcie_read_err) {
ring->tx_stats.tx_pcie_read_err++;
dev_info(ring->dev, "**** tx_desc: flag[0x%x], vp[%d], et[%d], ch[%d], tt[%d], sopv[%d], eopv[%d], tso[%d], l3chk[%d], l3oft[%d], l4chk[%d], l4oft[%d], pld[%d], mop[%d], sop[%d], mss[%d],mopa[%lld],sopa[%lld]\n",
tx_desc->u.val, tx_desc->vp, tx_desc->event_trigger, tx_desc->chain,
tx_desc->transmit_type, tx_desc->sop_valid, tx_desc->eop_valid,
tx_desc->tso, tx_desc->l3_csum, tx_desc->l3_ofst, tx_desc->l4_csum,
tx_desc->l4_ofst, tx_desc->pld_ofst, tx_desc->mop_cnt, tx_desc->sop_cnt,
tx_desc->mss, tx_desc->buffer_mop_addr, tx_desc->buffer_sop_addr);
}
tx_desc->u.val = 0;
tx_desc->vp = 0;
tx_desc->event_trigger = 0;
tx_desc->chain = 0;
tx_desc->transmit_type = 0;
tx_desc->sop_valid = 0;
tx_desc->eop_valid = 0;
tx_desc->tso = 0;
tx_desc->l3_csum = 0;
tx_desc->l3_ofst = 0;
tx_desc->l4_csum = 0;
tx_desc->l4_ofst = 0;
tx_desc->pld_ofst = 0;
tx_desc->mop_cnt = 0;
tx_desc->sop_cnt = 0;
tx_desc->mss = 0;
tx_desc->buffer_mop_addr = 0;
tx_desc->buffer_sop_addr = 0;
}
int ne6x_clean_cq_irq(struct ne6x_q_vector *q_vector, struct ne6x_ring *cq_ring, int napi_budget)
{
struct ne6x_cq_desc *cq_desc = NULL;
struct ne6x_tx_desc *tx_desc = NULL;
struct ne6x_ring *clean_ring = NULL;
union ne6x_rx_desc *rx_desc = NULL;
int i, cq_num, off_idx, ntc;
int budget = napi_budget;
int last_expect = 0;
int total = 0;
do {
cq_desc = NE6X_CQ_DESC(cq_ring, cq_ring->next_to_use);
cq_num = cq_desc->num;
if (!cq_num)
break;
dma_rmb();
cq_ring->stats.packets += cq_num;
if (cq_desc->ctype) {
clean_ring = q_vector->rx.ring;
last_expect = clean_ring->cq_last_expect;
for (i = 0; i < cq_num; i++) {
off_idx = cq_desc->payload.rx_cq[i].cq_rx_offset;
if (unlikely(off_idx != last_expect)) {
netdev_err(cq_ring->netdev, "ne6xpf: cqrx err, need debug! cq: %d, rx: %d\n",
off_idx, last_expect);
netdev_err(cq_ring->netdev, "ne6xpf: queue: %d, vp: %d, rxq: %d\n",
cq_ring->queue_index, cq_ring->reg_idx,
clean_ring->queue_index);
}
rx_desc = NE6X_RX_DESC(clean_ring, off_idx);
rx_desc->wb.u.val = cq_desc->payload.rx_cq[i].cq_rx_stats;
rx_desc->wb.pkt_len = cq_desc->payload.rx_cq[i].cq_rx_len;
if (rx_desc->wb.pkt_len > clean_ring->rx_buf_len) {
if (!rx_desc->wb.u.flags.rx_eop)
rx_desc->wb.pkt_len = clean_ring->rx_buf_len;
else
rx_desc->wb.pkt_len = rx_desc->wb.pkt_len %
clean_ring->rx_buf_len ?
rx_desc->wb.pkt_len %
clean_ring->rx_buf_len :
clean_ring->rx_buf_len;
}
last_expect++;
last_expect = (last_expect < clean_ring->count) ? last_expect : 0;
}
cq_ring->cq_stats.rx_num += cq_num;
} else {
clean_ring = q_vector->tx.ring;
last_expect = clean_ring->cq_last_expect;
for (i = 0; i < cq_num; i++) {
off_idx = cq_desc->payload.tx_cq[i].cq_tx_offset;
if (unlikely(off_idx != last_expect)) {
netdev_info(cq_ring->netdev, "ne6xpf: cqtx err, need debug! cq: %d, tx: %d\n",
off_idx, last_expect);
netdev_info(cq_ring->netdev, "ne6xpf: queue: %d, vp: %d, txq: %d\n",
cq_ring->queue_index, cq_ring->reg_idx,
clean_ring->queue_index);
}
tx_desc = NE6X_TX_DESC(clean_ring, off_idx);
tx_desc->u.val = cq_desc->payload.tx_cq[i].cq_tx_stats;
last_expect++;
last_expect = (last_expect < clean_ring->count) ? last_expect : 0;
}
cq_ring->cq_stats.tx_num += cq_num;
}
clean_ring->cq_last_expect = last_expect;
cq_ring->cq_stats.cq_num++;
/* clean cq desc */
cq_desc->num = 0;
ntc = cq_ring->next_to_use + 1;
ntc = (ntc < cq_ring->count) ? ntc : 0;
cq_ring->next_to_use = ntc;
prefetch(NE6X_CQ_DESC(cq_ring, ntc));
budget--;
total++;
} while (likely(budget));
if (NE6X_DESC_UNUSED(cq_ring) < 1024) {
cq_ring->next_to_clean = cq_ring->next_to_use;
/* memory barrier updating cq ring tail */
wmb();
writeq(cq_ring->next_to_clean, cq_ring->tail);
}
return total;
}
int ne6x_clean_rx_irq(struct ne6x_ring *rx_ring, int budget)
{
unsigned int total_rx_bytes = 0, total_rx_packets = 0;
u16 cleaned_count = NE6X_DESC_UNUSED(rx_ring);
struct ne6x_rx_buf *rx_buffer = NULL;
struct sk_buff *skb = rx_ring->skb;
union ne6x_rx_desc *rx_desc = NULL;
struct rx_hdr_info rx_hdr;
bool failure = false;
unsigned int size;
u8 rx_status;
while (likely(total_rx_packets < (unsigned int)budget)) {
if (cleaned_count >= NE6X_RX_BUFFER_WRITE) {
failure = failure || ne6x_alloc_rx_buffers(rx_ring, cleaned_count);
cleaned_count = 0;
}
rx_desc = NE6X_RX_DESC(rx_ring, rx_ring->next_to_clean);
rx_status = rx_desc->wb.u.val;
if (!rx_status)
break;
/* This memory barrier is needed to keep us from reading
* any other fields out of the rx_desc until we have
* verified the descriptor has been written back.
*/
dma_rmb();
if (unlikely(ne6x_rx_is_programming_status(rx_status))) {
rx_ring->rx_stats.rx_err++;
ne6x_clean_programming_status(rx_ring, rx_desc, rx_status);
cleaned_count++;
continue;
}
size = rx_desc->wb.pkt_len;
rx_buffer = ne6x_get_rx_buffer(rx_ring, size);
/* retrieve a buffer from the ring */
if (skb)
ne6x_add_rx_frag(rx_ring, rx_buffer, skb, size);
else
skb = ne6x_construct_skb(rx_ring, rx_buffer, size);
/* exit if we failed to retrieve a buffer */
if (!skb) {
rx_ring->rx_stats.alloc_buf_failed++;
rx_buffer->pagecnt_bias++;
break;
}
ne6x_put_rx_buffer(rx_ring, rx_buffer);
cleaned_count++;
if (ne6x_is_non_eop(rx_ring, rx_desc, skb))
continue;
if (ne6x_cleanup_headers(rx_ring, skb, rx_desc)) {
skb = NULL;
continue;
}
ne6x_get_rx_head_info(skb, &rx_hdr);
pskb_trim(skb, skb->len - 16);
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
/* populate checksum, VLAN, and protocol */
ne6x_process_skb_fields(rx_ring, rx_desc, skb, &rx_hdr);
ne6x_receive_skb(rx_ring, skb);
skb = NULL;
rx_desc->wb.u.val = 0;
/* update budget accounting */
total_rx_packets++;
}
rx_ring->skb = skb;
u64_stats_update_begin(&rx_ring->syncp);
rx_ring->stats.packets += total_rx_packets;
rx_ring->stats.bytes += total_rx_bytes;
u64_stats_update_end(&rx_ring->syncp);
/* guarantee a trip back through this routine if there was a failure */
return failure ? budget : (int)total_rx_packets;
}
int ne6x_clean_tx_irq(struct ne6x_adapt_comm *comm, struct ne6x_ring *tx_ring, int napi_budget)
{
unsigned int total_bytes = 0, total_packets = 0;
struct ne6x_tx_desc *eop_desc = NULL;
u16 i = tx_ring->next_to_clean;
struct ne6x_tx_desc *tx_desc;
struct ne6x_tx_buf *tx_buf;
unsigned int budget = 256;
tx_buf = &tx_ring->tx_buf[i];
tx_desc = NE6X_TX_DESC(tx_ring, i);
if (unlikely(tx_buf->jumbo_frame)) {
tx_buf->napi_budget += napi_budget;
if (!tx_buf->jumbo_finsh)
return !!budget;
napi_budget = tx_buf->napi_budget;
}
do {
eop_desc = tx_buf->next_to_watch;
if (!eop_desc)
break;
prefetchw(&tx_buf->skb->users);
if (!eop_desc->u.val)
break;
dma_rmb();
/* clear next_to_watch to prevent false hangs */
tx_buf->next_to_watch = NULL;
tx_buf->jumbo_frame = 0;
tx_buf->jumbo_finsh = 0;
/* update the statistics for this packet */
total_bytes += tx_buf->bytecount;
total_packets += tx_buf->gso_segs;
/* free the skb/XDP data */
ne6x_clean_tx_desc(tx_desc, tx_ring);
/* free the skb */
napi_consume_skb(tx_buf->skb, napi_budget);
/* unmap skb header data */
dma_unmap_single(tx_ring->dev, dma_unmap_addr(tx_buf, dma),
dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
/* clear tx_buffer data */
tx_buf->skb = NULL;
dma_unmap_len_set(tx_buf, len, 0);
/* unmap remaining buffers */
while (tx_desc != eop_desc) {
tx_buf++;
tx_desc++;
i++;
if (i == tx_ring->count) {
i = 0;
tx_buf = tx_ring->tx_buf;
tx_desc = NE6X_TX_DESC(tx_ring, 0);
}
/* unmap any remaining paged data */
if (dma_unmap_len(tx_buf, len)) {
dma_unmap_page(tx_ring->dev, dma_unmap_addr(tx_buf, dma),
dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
dma_unmap_len_set(tx_buf, len, 0);
}
/* free the skb/XDP data */
ne6x_clean_tx_desc(tx_desc, tx_ring);
}
/* move us one more past the eop_desc for start of next pkt */
tx_buf++;
tx_desc++;
i++;
if (i == tx_ring->count) {
i = 0;
tx_buf = tx_ring->tx_buf;
tx_desc = NE6X_TX_DESC(tx_ring, 0);
}
if (unlikely(tx_buf->jumbo_frame && !tx_buf->jumbo_finsh))
break;
prefetch(tx_desc);
/* update budget accounting */
budget--;
} while (likely(budget));
if (total_packets) {
tx_ring->next_to_clean = i;
u64_stats_update_begin(&tx_ring->syncp);
tx_ring->stats.bytes += total_bytes;
tx_ring->stats.packets += total_packets;
u64_stats_update_end(&tx_ring->syncp);
/* notify netdev of completed buffers */
netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, total_bytes);
#define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2))
if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
(NE6X_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) {
/* Make sure that anybody stopping the queue after this
* sees the new next_to_clean.
*/
smp_mb();
if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) &&
!test_bit(NE6X_ADPT_DOWN, comm->state)) {
netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index);
++tx_ring->tx_stats.restart_q;
}
}
}
return !!budget;
}
static inline int ne6x_xmit_descriptor_count(struct sk_buff *skb)
{
int count = 0;
count = 1;
count += skb_shinfo(skb)->nr_frags;
return count;
}
int __ne6x_maybe_stop_tx(struct ne6x_ring *tx_ring, int size)
{
netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
/* Memory barrier before checking head and tail */
smp_mb();
/* Check again in a case another CPU has just made room available. */
if (likely(NE6X_DESC_UNUSED(tx_ring) < size))
return -EBUSY;
/* A reprieve! - use start_queue because it doesn't call schedule */
netif_start_subqueue(tx_ring->netdev, tx_ring->queue_index);
return 0;
}
static inline u16 ne6x_gso_get_seg_hdrlen(struct sk_buff *skb)
{
u16 gso_hdr_len;
gso_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
if (unlikely(skb->encapsulation))
gso_hdr_len = skb_inner_transport_offset(skb) + inner_tcp_hdrlen(skb);
return gso_hdr_len;
}
static int ne6x_tso(struct ne6x_ring *tx_ring, struct ne6x_tx_buf *first,
struct ne6x_tx_tag *ptx_tag)
{
struct sk_buff *skb = first->skb;
u8 hdrlen = 0;
int err;
if (skb->ip_summed != CHECKSUM_PARTIAL || !skb_is_gso(skb))
return 0;
hdrlen = ne6x_gso_get_seg_hdrlen(skb);
err = skb_cow_head(skb, 0);
if (err < 0)
return err;
/* update gso_segs and bytecount */
first->gso_segs = skb_shinfo(skb)->gso_segs;
first->bytecount += (first->gso_segs - 1) * hdrlen;
ptx_tag->tag_mss = skb_shinfo(skb)->gso_size;
return 1;
}
static void ne6x_tx_prepare_vlan_flags(struct ne6x_ring *tx_ring,
struct ne6x_tx_buf *first,
struct ne6x_tx_tag *ptx_tag)
{
struct sk_buff *skb = first->skb;
/* nothing left to do, software offloaded VLAN */
if (!skb_vlan_tag_present(skb) && eth_type_vlan(skb->protocol))
return;
/* the VLAN ethertype/tpid is determined by adapter configuration and netdev
* feature flags, which the driver only allows either 802.1Q or 802.1ad
* VLAN offloads exclusively so we only care about the VLAN ID here
*/
if (skb_vlan_tag_present(skb)) {
if (tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)
ptx_tag->tag_vlan2 = cpu_to_be16(skb_vlan_tag_get(skb));
else if (tx_ring->netdev->features & NETIF_F_HW_VLAN_STAG_TX)
ptx_tag->tag_vlan1 = cpu_to_be16(skb_vlan_tag_get(skb));
}
}
static int ne6x_tx_csum(struct ne6x_ring *tx_ring, struct ne6x_tx_buf *first,
struct ne6x_tx_tag *ptx_tag)
{
tx_ring->tx_stats.csum_good++;
return 1;
}
static inline void ne6x_tx_desc_push(struct ne6x_tx_desc *tx_desc,
dma_addr_t dma, u32 size)
{
tx_desc->buffer_mop_addr = cpu_to_le64(dma);
tx_desc->mop_cnt = size;
tx_desc->event_trigger = 1;
}
void ne6x_unmap_and_free_tx_resource(struct ne6x_ring *ring,
struct ne6x_tx_buf *tx_buffer)
{
if (tx_buffer->skb) {
dev_kfree_skb_any(tx_buffer->skb);
if (dma_unmap_len(tx_buffer, len))
dma_unmap_single(ring->dev, dma_unmap_addr(tx_buffer, dma),
dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE);
} else if (dma_unmap_len(tx_buffer, len)) {
dma_unmap_page(ring->dev, dma_unmap_addr(tx_buffer, dma),
dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE);
}
tx_buffer->next_to_watch = NULL;
tx_buffer->skb = NULL;
dma_unmap_len_set(tx_buffer, len, 0);
}
static inline void ne6x_fill_gso_sg(void *p, u16 offset, u16 len, struct ne6x_sg_info *sg)
{
sg->p = p;
sg->offset = offset;
sg->len = len;
}
static int ne6x_fill_jumbo_sgl(struct ne6x_ring *tx_ring, struct sk_buff *skb)
{
u16 sg_max_dlen = 0, dlen = 0, len = 0, offset = 0, send_dlen = 0, total_dlen = 0;
u16 subframe = 0, send_subframe = 0, sg_avail = 0, i = 0, j = 0;
u16 gso_hdr_len = ne6x_gso_get_seg_hdrlen(skb);
struct ne6x_sg_list *sgl = tx_ring->sgl;
WARN_ON(!sgl);
memset(sgl, 0, sizeof(struct ne6x_sg_list));
dlen = skb_headlen(skb) - gso_hdr_len;
sgl->mss = skb_shinfo(skb)->gso_size;
sg_max_dlen = NE6X_MAX_DATA_PER_TXD - gso_hdr_len;
sg_max_dlen = ((u16)(sg_max_dlen / sgl->mss)) * sgl->mss;
total_dlen = skb->data_len + dlen;
sgl->sgl_mss_cnt = sg_max_dlen / sgl->mss;
subframe = total_dlen / sg_max_dlen;
subframe += total_dlen % sg_max_dlen ? 1 : 0;
ne6x_fill_gso_sg(skb->data, 0, gso_hdr_len, &sgl->sg[i]);
sgl->sg[i].flag |= NE6X_SG_FST_SG_FLAG | NE6X_SG_SOP_FLAG | NE6X_SG_JUMBO_FLAG;
offset = gso_hdr_len;
sg_avail = sg_max_dlen;
++send_subframe;
i++;
while (dlen) {
len = dlen > sg_avail ? sg_avail : dlen;
ne6x_fill_gso_sg(skb->data, offset, len, &sgl->sg[i]);
offset += len;
dlen -= len;
send_dlen += len;
sg_avail -= len;
if (send_dlen == total_dlen)
goto end;
if (!(send_dlen % sg_max_dlen)) {
sgl->sg[i].flag |= NE6X_SG_EOP_FLAG;
++i;
if (unlikely(i > NE6X_MAX_DESC_NUM_PER_SKB))
goto err;
ne6x_fill_gso_sg(skb->data, 0, gso_hdr_len, &sgl->sg[i]);
sgl->sg[i].flag |= NE6X_SG_SOP_FLAG | NE6X_SG_JUMBO_FLAG;
sgl->sg[i].base_mss_no = send_subframe * sgl->sgl_mss_cnt;
if (++send_subframe == subframe)
sgl->sg[i].flag |= NE6X_SG_LST_SG_FLAG;
sgl->sg[i].base_mss_no = send_subframe * sgl->sgl_mss_cnt;
sg_avail = sg_max_dlen;
}
++i;
if (unlikely(i > NE6X_MAX_DESC_NUM_PER_SKB))
goto err;
}
for (j = 0; j < skb_shinfo(skb)->nr_frags; j++) {
skb_frag_t *f = &skb_shinfo(skb)->frags[j];
dlen = skb_frag_size(f);
offset = 0;
while (dlen) {
len = dlen > sg_avail ? sg_avail : dlen;
ne6x_fill_gso_sg(f, offset, len, &sgl->sg[i]);
sgl->sg[i].flag |= NE6X_SG_FRAG_FLAG;
offset += len;
dlen -= len;
send_dlen += len;
sg_avail -= len;
if (send_dlen == total_dlen)
goto end;
if (!(send_dlen % sg_max_dlen)) {
sgl->sg[i].flag |= NE6X_SG_EOP_FLAG;
++i;
if (unlikely(i > NE6X_MAX_DESC_NUM_PER_SKB))
goto err;
ne6x_fill_gso_sg(skb->data, 0, gso_hdr_len, &sgl->sg[i]);
sgl->sg[i].flag |= NE6X_SG_SOP_FLAG | NE6X_SG_JUMBO_FLAG;
sgl->sg[i].base_mss_no = send_subframe * sgl->sgl_mss_cnt;
if (++send_subframe == subframe)
sgl->sg[i].flag |= NE6X_SG_LST_SG_FLAG;
sg_avail = sg_max_dlen;
}
++i;
if (unlikely(i > NE6X_MAX_DESC_NUM_PER_SKB))
goto err;
}
offset = 0;
}
end:
sgl->sg[i].flag |= NE6X_SG_EOP_FLAG;
sgl->sg_num = ++i;
return 0;
err:
return -1;
}
static void ne6x_fill_tx_desc(struct ne6x_tx_desc *tx_desc, u8 vp, dma_addr_t tag_dma,
dma_addr_t dma, struct ne6x_sg_info *sg)
{
memset(tx_desc, 0, NE6X_TX_DESC_SIZE);
tx_desc->buffer_mop_addr = cpu_to_le64(dma);
tx_desc->buffer_sop_addr = (sg->flag & NE6X_SG_SOP_FLAG) ? cpu_to_le64(tag_dma) : 0;
tx_desc->mop_cnt = sg->len;
tx_desc->event_trigger = 1;
tx_desc->vp = vp;
tx_desc->sop_valid = (sg->flag & NE6X_SG_SOP_FLAG) ? 1u : 0u;
tx_desc->eop_valid = (sg->flag & NE6X_SG_EOP_FLAG) ? 1u : 0u;
tx_desc->sop_cnt = (sg->flag & NE6X_SG_SOP_FLAG) ? 32 : 0;
if (tx_desc->eop_valid) {
tx_desc->sop_cnt = tx_desc->mop_cnt;
tx_desc->buffer_sop_addr = tx_desc->buffer_mop_addr;
tx_desc->mop_cnt = 4;
}
}
static void ne6x_fill_tx_priv_tag(struct ne6x_ring *tx_ring, struct ne6x_tx_tag *tx_tag,
int mss, struct ne6x_sg_info *sg)
{
struct ne6x_adapt_comm *comm = (struct ne6x_adapt_comm *)tx_ring->adpt;
tx_tag->tag_pi1 = (comm->port_info & 0x2) ? 1 : 0;
tx_tag->tag_pi0 = (comm->port_info & 0x1) ? 1 : 0;
tx_tag->tag_vport = (comm->port_info >> 8) & 0xFF;
tx_tag->tag_mss = cpu_to_be16(mss);
tx_tag->tag_num = sg->base_mss_no | (sg->flag & NE6X_SG_JUMBO_FLAG) |
(sg->flag & NE6X_SG_LST_SG_FLAG) |
(sg->flag & NE6X_SG_FST_SG_FLAG);
tx_tag->tag_num = cpu_to_be16(tx_tag->tag_num);
}
static void ne6x_xmit_jumbo(struct ne6x_ring *tx_ring, struct ne6x_tx_buf *first,
struct ne6x_ring *tag_ring, struct ne6x_tx_tag *tx_tag)
{
int j = 0;
struct ne6x_sg_list *sgl = tx_ring->sgl;
struct ne6x_sg_info *sg;
dma_addr_t dma, tag_dma;
struct sk_buff *skb = first->skb;
struct ne6x_tx_buf *tx_bi;
struct ne6x_tx_tag *tag_desc = tx_tag;
u32 i = tx_ring->next_to_use;
struct ne6x_tx_desc *tx_desc = NE6X_TX_DESC(tx_ring, i);
for (; j < sgl->sg_num; j++) {
sg = &sgl->sg[j];
if (likely(sg->flag & NE6X_SG_FRAG_FLAG)) {
dma = skb_frag_dma_map(tx_ring->dev, sg->p, sg->offset, sg->len,
DMA_TO_DEVICE);
} else {
dma = dma_map_single(tx_ring->dev, sg->p + sg->offset, sg->len,
DMA_TO_DEVICE);
}
if (dma_mapping_error(tx_ring->dev, dma))
goto dma_error;
tx_bi = &tx_ring->tx_buf[i];
dma_unmap_len_set(tx_bi, len, sg->len);
dma_unmap_addr_set(tx_bi, dma, dma);
if (sg->flag & NE6X_SG_SOP_FLAG) {
tag_dma = tag_ring->dma + tag_ring->next_to_use * NE6X_TX_PRIV_TAG_SIZE;
tag_desc = NE6X_TX_TAG(tag_ring, tag_ring->next_to_use);
ne6x_fill_tx_priv_tag(tx_ring, tag_desc, sgl->mss, sg);
if (++tag_ring->next_to_use == tag_ring->count)
tag_ring->next_to_use = 0;
} else {
tag_dma = 0;
}
tx_desc = NE6X_TX_DESC(tx_ring, i);
ne6x_fill_tx_desc(tx_desc, tx_ring->reg_idx, tag_dma, dma, sg);
if (++i == tx_ring->count)
i = 0;
}
tx_ring->next_to_use = i;
ne6x_maybe_stop_tx(tx_ring, DESC_NEEDED);
skb_tx_timestamp(skb);
/* Force memory writes to complete before letting h/w know there
* are new descriptors to fetch.
*
* We also use this memory barrier to make certain all of the
* status bits have been updated before next_to_watch is written.
*/
wmb();
/* set next_to_watch value indicating a packet is present */
first->next_to_watch = tx_desc;
/* notify HW of packet */
if (netif_xmit_stopped(txring_txq(tx_ring)) || !netdev_xmit_more())
ne6x_tail_update(tx_ring, i);
netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount);
first->jumbo_finsh = 1u;
return;
dma_error:
dev_info(tx_ring->dev, "TX DMA map failed\n");
/* clear dma mappings for failed tx_bi map */
for (;;) {
tx_bi = &tx_ring->tx_buf[i];
ne6x_unmap_and_free_tx_resource(tx_ring, tx_bi);
if (tx_bi == first)
break;
if (i == 0)
i = tx_ring->count;
i--;
}
tx_ring->next_to_use = i;
}
static void ne6x_xmit_simple(struct ne6x_ring *tx_ring, struct ne6x_tx_buf *first,
struct ne6x_ring *tag_ring, struct ne6x_tx_tag *tx_tag)
{
struct sk_buff *skb = first->skb;
struct ne6x_adapt_comm *comm = (struct ne6x_adapt_comm *)tx_ring->adpt;
struct ne6x_tx_desc *tx_desc;
unsigned int size = skb_headlen(skb);
u32 i = tx_ring->next_to_use;
struct ne6x_tx_tag *ttx_desc;
struct ne6x_tx_buf *tx_bi;
bool is_first = true;
int send_len = 0;
skb_frag_t *frag;
dma_addr_t dma;
dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
tx_desc = NE6X_TX_DESC(tx_ring, i);
tx_desc->sop_valid = 1;
tx_desc->eop_valid = 0;
tx_bi = first;
ttx_desc = (struct ne6x_tx_tag *)tx_tag;
ttx_desc->tag_pi1 = (comm->port_info & 0x2) ? 1 : 0;
ttx_desc->tag_pi0 = (comm->port_info & 0x1) ? 1 : 0;
ttx_desc->tag_vport = (comm->port_info >> 8) & 0xFF;
ttx_desc->tag_mss = tx_tag->tag_mss;
ttx_desc->tag_num = 0x0;
send_len += size;
for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
if (dma_mapping_error(tx_ring->dev, dma))
goto dma_error;
/* record length, and DMA address */
dma_unmap_len_set(tx_bi, len, size);
dma_unmap_addr_set(tx_bi, dma, dma);
ne6x_tx_desc_push(tx_desc, dma, size);
tx_desc->vp = tx_ring->reg_idx;
tx_desc->tso = 0x0;
tx_desc->l3_csum = 0x00;
tx_desc->l3_ofst = 0x00;
tx_desc->l4_csum = 0x00;
tx_desc->l4_ofst = 0x00;
tx_desc->pld_ofst = 0x00;
tx_desc->u.val = 0x0;
tx_desc->rsv4 = 0;
if (is_first) {
tx_desc->sop_valid = 1u;
is_first = false;
tx_desc->sop_cnt = 32;
tx_desc->buffer_sop_addr = cpu_to_le64(first->tag_dma);
}
if (send_len == skb->len) {
tx_desc->eop_valid = 1u;
break;
}
if (++i == tx_ring->count)
i = 0;
tx_desc = NE6X_TX_DESC(tx_ring, i);
size = skb_frag_size(frag);
send_len += size;
dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size, DMA_TO_DEVICE);
tx_bi = &tx_ring->tx_buf[i];
}
netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount);
if (++i == tx_ring->count)
i = 0;
tx_ring->next_to_use = i;
if (++tag_ring->next_to_use == tag_ring->count)
tag_ring->next_to_use = 0;
ne6x_maybe_stop_tx(tx_ring, DESC_NEEDED);
/* timestamp the skb as late as possible, just prior to notifying
* the MAC that it should transmit this packet
*/
skb_tx_timestamp(skb);
/* Force memory writes to complete before letting h/w know there
* are new descriptors to fetch.
*
* We also use this memory barrier to make certain all of the
* status bits have been updated before next_to_watch is written.
*/
wmb();
/* set next_to_watch value indicating a packet is present */
first->next_to_watch = tx_desc;
/* notify HW of packet */
if (netif_xmit_stopped(txring_txq(tx_ring)) || !netdev_xmit_more())
ne6x_tail_update(tx_ring, i);
return;
dma_error:
dev_info(tx_ring->dev, "TX DMA map failed\n");
/* clear dma mappings for failed tx_bi map */
for (;;) {
tx_bi = &tx_ring->tx_buf[i];
ne6x_unmap_and_free_tx_resource(tx_ring, tx_bi);
if (tx_bi == first)
break;
if (i == 0)
i = tx_ring->count;
i--;
}
tx_ring->next_to_use = i;
}
netdev_tx_t ne6x_xmit_frame_ring(struct sk_buff *skb, struct ne6x_ring *tx_ring,
struct ne6x_ring *tag_ring, bool jumbo_frame)
{
struct ne6x_tx_tag *tx_tagx = NE6X_TX_TAG(tag_ring, tag_ring->next_to_use);
struct ne6x_tx_buf *first;
int tso, count;
/* prefetch the data, we'll need it later */
prefetch(tx_tagx);
prefetch(skb->data);
if (!jumbo_frame) {
count = ne6x_xmit_descriptor_count(skb);
} else {
if (ne6x_fill_jumbo_sgl(tx_ring, skb)) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
count = tx_ring->sgl->sg_num;
}
/* reserve 5 descriptors to avoid tail over-write */
if (ne6x_maybe_stop_tx(tx_ring, count + 4 + 1)) {
/* this is a hard error */
tx_ring->tx_stats.tx_busy++;
return NETDEV_TX_BUSY;
}
/* record the location of the first descriptor for this packet */
first = &tx_ring->tx_buf[tx_ring->next_to_use];
first->skb = skb;
first->bytecount = skb->len;
first->gso_segs = 1;
/* record initial flags and protocol */
first->jumbo_frame = 0;
first->jumbo_finsh = 0;
first->tag_dma = tag_ring->dma + tag_ring->next_to_use * sizeof(struct ne6x_tx_tag);
memset(tx_tagx, 0x00, sizeof(*tx_tagx));
ne6x_tx_prepare_vlan_flags(tx_ring, first, tx_tagx);
tso = ne6x_tso(tx_ring, first, tx_tagx);
if (tso < 0)
goto out_drop;
tso = ne6x_tx_csum(tx_ring, first, tx_tagx);
if (tso < 0)
goto out_drop;
tx_tagx->tag_mss = cpu_to_be16(tx_tagx->tag_mss);
if (!jumbo_frame) {
ne6x_xmit_simple(tx_ring, first, tag_ring, tx_tagx);
} else {
first->jumbo_frame = true;
ne6x_xmit_jumbo(tx_ring, first, tag_ring, tx_tagx);
}
return NETDEV_TX_OK;
out_drop:
ne6x_unmap_and_free_tx_resource(tx_ring, first);
return NETDEV_TX_OK;
}