|
@@ -268,6 +268,7 @@ struct mvneta_pcpu_stats {
|
|
|
|
|
|
struct mvneta_port {
|
|
|
int pkt_size;
|
|
|
+ unsigned int frag_size;
|
|
|
void __iomem *base;
|
|
|
struct mvneta_rx_queue *rxqs;
|
|
|
struct mvneta_tx_queue *txqs;
|
|
@@ -1332,28 +1333,43 @@ static int mvneta_txq_done(struct mvneta_port *pp,
|
|
|
return tx_done;
|
|
|
}
|
|
|
|
|
|
+static void *mvneta_frag_alloc(const struct mvneta_port *pp)
|
|
|
+{
|
|
|
+ if (likely(pp->frag_size <= PAGE_SIZE))
|
|
|
+ return netdev_alloc_frag(pp->frag_size);
|
|
|
+ else
|
|
|
+ return kmalloc(pp->frag_size, GFP_ATOMIC);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvneta_frag_free(const struct mvneta_port *pp, void *data)
|
|
|
+{
|
|
|
+ if (likely(pp->frag_size <= PAGE_SIZE))
|
|
|
+ put_page(virt_to_head_page(data));
|
|
|
+ else
|
|
|
+ kfree(data);
|
|
|
+}
|
|
|
+
|
|
|
/* Refill processing */
|
|
|
static int mvneta_rx_refill(struct mvneta_port *pp,
|
|
|
struct mvneta_rx_desc *rx_desc)
|
|
|
|
|
|
{
|
|
|
dma_addr_t phys_addr;
|
|
|
- struct sk_buff *skb;
|
|
|
+ void *data;
|
|
|
|
|
|
- skb = netdev_alloc_skb(pp->dev, pp->pkt_size);
|
|
|
- if (!skb)
|
|
|
+ data = mvneta_frag_alloc(pp);
|
|
|
+ if (!data)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- phys_addr = dma_map_single(pp->dev->dev.parent, skb->head,
|
|
|
+ phys_addr = dma_map_single(pp->dev->dev.parent, data,
|
|
|
MVNETA_RX_BUF_SIZE(pp->pkt_size),
|
|
|
DMA_FROM_DEVICE);
|
|
|
if (unlikely(dma_mapping_error(pp->dev->dev.parent, phys_addr))) {
|
|
|
- dev_kfree_skb(skb);
|
|
|
+ mvneta_frag_free(pp, data);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
- mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb);
|
|
|
-
|
|
|
+ mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)data);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1407,9 +1423,9 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
|
|
|
rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
|
|
|
for (i = 0; i < rxq->size; i++) {
|
|
|
struct mvneta_rx_desc *rx_desc = rxq->descs + i;
|
|
|
- struct sk_buff *skb = (struct sk_buff *)rx_desc->buf_cookie;
|
|
|
+ void *data = (void *)rx_desc->buf_cookie;
|
|
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
+ mvneta_frag_free(pp, data);
|
|
|
dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
|
|
|
MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
|
|
|
}
|
|
@@ -1440,20 +1456,21 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
|
|
while (rx_done < rx_todo) {
|
|
|
struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq);
|
|
|
struct sk_buff *skb;
|
|
|
+ unsigned char *data;
|
|
|
u32 rx_status;
|
|
|
int rx_bytes, err;
|
|
|
|
|
|
rx_done++;
|
|
|
rx_filled++;
|
|
|
rx_status = rx_desc->status;
|
|
|
- skb = (struct sk_buff *)rx_desc->buf_cookie;
|
|
|
+ data = (unsigned char *)rx_desc->buf_cookie;
|
|
|
|
|
|
if (!mvneta_rxq_desc_is_first_last(rx_status) ||
|
|
|
- (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
|
|
|
+ (rx_status & MVNETA_RXD_ERR_SUMMARY) ||
|
|
|
+ !(skb = build_skb(data, pp->frag_size > PAGE_SIZE ? 0 : pp->frag_size))) {
|
|
|
dev->stats.rx_errors++;
|
|
|
mvneta_rx_error(pp, rx_desc);
|
|
|
- mvneta_rx_desc_fill(rx_desc, rx_desc->buf_phys_addr,
|
|
|
- (u32)skb);
|
|
|
+ /* leave the descriptor untouched */
|
|
|
continue;
|
|
|
}
|
|
|
|
|
@@ -1466,7 +1483,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
|
|
rcvd_bytes += rx_bytes;
|
|
|
|
|
|
/* Linux processing */
|
|
|
- skb_reserve(skb, MVNETA_MH_SIZE);
|
|
|
+ skb_reserve(skb, MVNETA_MH_SIZE + NET_SKB_PAD);
|
|
|
skb_put(skb, rx_bytes);
|
|
|
|
|
|
skb->protocol = eth_type_trans(skb, dev);
|
|
@@ -2276,6 +2293,8 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu)
|
|
|
mvneta_cleanup_rxqs(pp);
|
|
|
|
|
|
pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
|
|
|
+ pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
|
|
|
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
|
|
|
|
|
ret = mvneta_setup_rxqs(pp);
|
|
|
if (ret) {
|
|
@@ -2423,6 +2442,8 @@ static int mvneta_open(struct net_device *dev)
|
|
|
mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
|
|
|
|
|
|
pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
|
|
|
+ pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
|
|
|
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
|
|
|
|
|
ret = mvneta_setup_rxqs(pp);
|
|
|
if (ret)
|