ftgmac100: Make ring sizes configurable via ethtool

We set an arbitrary max at 1024 since we pre-allocate the actual
descriptor arrays and skb arrays to the full size to keep the
code a bit simpler and avoid allocation failures in the reset
task.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Benjamin Herrenschmidt 2017-04-12 13:27:09 +10:00 committed by David S. Miller
parent 3833dc6c18
commit 52d9138fb3

View File

@ -40,8 +40,15 @@
#define DRV_NAME "ftgmac100" #define DRV_NAME "ftgmac100"
#define DRV_VERSION "0.7" #define DRV_VERSION "0.7"
#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */ /* Arbitrary values, I am not sure the HW has limits */
#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */ #define MAX_RX_QUEUE_ENTRIES 1024
#define MAX_TX_QUEUE_ENTRIES 1024
#define MIN_RX_QUEUE_ENTRIES 32
#define MIN_TX_QUEUE_ENTRIES 32
/* Defaults */
#define DEF_RX_QUEUE_ENTRIES 256
#define DEF_TX_QUEUE_ENTRIES 512
#define MAX_PKT_SIZE 1536 #define MAX_PKT_SIZE 1536
#define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ #define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */
@ -49,30 +56,32 @@
/* Min number of tx ring entries before stopping queue */ /* Min number of tx ring entries before stopping queue */
#define TX_THRESHOLD (MAX_SKB_FRAGS + 1) #define TX_THRESHOLD (MAX_SKB_FRAGS + 1)
struct ftgmac100_descs {
struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
};
struct ftgmac100 { struct ftgmac100 {
/* Registers */ /* Registers */
struct resource *res; struct resource *res;
void __iomem *base; void __iomem *base;
struct ftgmac100_descs *descs;
dma_addr_t descs_dma_addr;
/* Rx ring */ /* Rx ring */
struct sk_buff *rx_skbs[RX_QUEUE_ENTRIES]; unsigned int rx_q_entries;
struct ftgmac100_rxdes *rxdes;
dma_addr_t rxdes_dma;
struct sk_buff **rx_skbs;
unsigned int rx_pointer; unsigned int rx_pointer;
u32 rxdes0_edorr_mask; u32 rxdes0_edorr_mask;
/* Tx ring */ /* Tx ring */
struct sk_buff *tx_skbs[TX_QUEUE_ENTRIES]; unsigned int tx_q_entries;
struct ftgmac100_txdes *txdes;
dma_addr_t txdes_dma;
struct sk_buff **tx_skbs;
unsigned int tx_clean_pointer; unsigned int tx_clean_pointer;
unsigned int tx_pointer; unsigned int tx_pointer;
u32 txdes0_edotr_mask; u32 txdes0_edotr_mask;
/* Used to signal the reset task of ring change request */
unsigned int new_rx_q_entries;
unsigned int new_tx_q_entries;
/* Scratch page to use when rx skb alloc fails */ /* Scratch page to use when rx skb alloc fails */
void *rx_scratch; void *rx_scratch;
dma_addr_t rx_scratch_dma; dma_addr_t rx_scratch_dma;
@ -219,14 +228,10 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv)
iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR); iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR);
/* Setup RX ring buffer base */ /* Setup RX ring buffer base */
iowrite32(priv->descs_dma_addr + iowrite32(priv->rxdes_dma, priv->base + FTGMAC100_OFFSET_RXR_BADR);
offsetof(struct ftgmac100_descs, rxdes),
priv->base + FTGMAC100_OFFSET_RXR_BADR);
/* Setup TX ring buffer base */ /* Setup TX ring buffer base */
iowrite32(priv->descs_dma_addr + iowrite32(priv->txdes_dma, priv->base + FTGMAC100_OFFSET_NPTXR_BADR);
offsetof(struct ftgmac100_descs, txdes),
priv->base + FTGMAC100_OFFSET_NPTXR_BADR);
/* Configure RX buffer size */ /* Configure RX buffer size */
iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE), iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE),
@ -339,7 +344,7 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry,
dma_wmb(); dma_wmb();
/* Clean status (which resets own bit) */ /* Clean status (which resets own bit) */
if (entry == (RX_QUEUE_ENTRIES - 1)) if (entry == (priv->rx_q_entries - 1))
rxdes->rxdes0 = cpu_to_le32(priv->rxdes0_edorr_mask); rxdes->rxdes0 = cpu_to_le32(priv->rxdes0_edorr_mask);
else else
rxdes->rxdes0 = 0; rxdes->rxdes0 = 0;
@ -347,9 +352,10 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry,
return 0; return 0;
} }
static int ftgmac100_next_rx_pointer(int pointer) static unsigned int ftgmac100_next_rx_pointer(struct ftgmac100 *priv,
unsigned int pointer)
{ {
return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); return (pointer + 1) & (priv->rx_q_entries - 1);
} }
static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, u32 status) static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, u32 status)
@ -379,7 +385,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
/* Grab next RX descriptor */ /* Grab next RX descriptor */
pointer = priv->rx_pointer; pointer = priv->rx_pointer;
rxdes = &priv->descs->rxdes[pointer]; rxdes = &priv->rxdes[pointer];
/* Grab descriptor status */ /* Grab descriptor status */
status = le32_to_cpu(rxdes->rxdes0); status = le32_to_cpu(rxdes->rxdes0);
@ -467,7 +473,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
/* Resplenish rx ring */ /* Resplenish rx ring */
ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC); ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); priv->rx_pointer = ftgmac100_next_rx_pointer(priv, pointer);
skb->protocol = eth_type_trans(skb, netdev); skb->protocol = eth_type_trans(skb, netdev);
@ -486,7 +492,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
drop: drop:
/* Clean rxdes0 (which resets own bit) */ /* Clean rxdes0 (which resets own bit) */
rxdes->rxdes0 = cpu_to_le32(status & priv->rxdes0_edorr_mask); rxdes->rxdes0 = cpu_to_le32(status & priv->rxdes0_edorr_mask);
priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); priv->rx_pointer = ftgmac100_next_rx_pointer(priv, pointer);
netdev->stats.rx_dropped++; netdev->stats.rx_dropped++;
return true; return true;
} }
@ -494,15 +500,16 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
static u32 ftgmac100_base_tx_ctlstat(struct ftgmac100 *priv, static u32 ftgmac100_base_tx_ctlstat(struct ftgmac100 *priv,
unsigned int index) unsigned int index)
{ {
if (index == (TX_QUEUE_ENTRIES - 1)) if (index == (priv->tx_q_entries - 1))
return priv->txdes0_edotr_mask; return priv->txdes0_edotr_mask;
else else
return 0; return 0;
} }
static int ftgmac100_next_tx_pointer(int pointer) static unsigned int ftgmac100_next_tx_pointer(struct ftgmac100 *priv,
unsigned int pointer)
{ {
return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); return (pointer + 1) & (priv->tx_q_entries - 1);
} }
static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv) static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv)
@ -514,7 +521,7 @@ static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv)
* test for ftgmac100_tx_buf_cleanable() below * test for ftgmac100_tx_buf_cleanable() below
*/ */
return (priv->tx_clean_pointer - priv->tx_pointer - 1) & return (priv->tx_clean_pointer - priv->tx_pointer - 1) &
(TX_QUEUE_ENTRIES - 1); (priv->tx_q_entries - 1);
} }
static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv) static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv)
@ -554,7 +561,7 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
u32 ctl_stat; u32 ctl_stat;
pointer = priv->tx_clean_pointer; pointer = priv->tx_clean_pointer;
txdes = &priv->descs->txdes[pointer]; txdes = &priv->txdes[pointer];
ctl_stat = le32_to_cpu(txdes->txdes0); ctl_stat = le32_to_cpu(txdes->txdes0);
if (ctl_stat & FTGMAC100_TXDES0_TXDMA_OWN) if (ctl_stat & FTGMAC100_TXDES0_TXDMA_OWN)
@ -566,7 +573,7 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat); ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat);
txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask); txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask);
priv->tx_clean_pointer = ftgmac100_next_tx_pointer(pointer); priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv, pointer);
return true; return true;
} }
@ -655,7 +662,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
/* Grab the next free tx descriptor */ /* Grab the next free tx descriptor */
pointer = priv->tx_pointer; pointer = priv->tx_pointer;
txdes = first = &priv->descs->txdes[pointer]; txdes = first = &priv->txdes[pointer];
/* Setup it up with the packet head. Don't write the head to the /* Setup it up with the packet head. Don't write the head to the
* ring just yet * ring just yet
@ -677,7 +684,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
txdes->txdes1 = cpu_to_le32(csum_vlan); txdes->txdes1 = cpu_to_le32(csum_vlan);
/* Next descriptor */ /* Next descriptor */
pointer = ftgmac100_next_tx_pointer(pointer); pointer = ftgmac100_next_tx_pointer(priv, pointer);
/* Add the fragments */ /* Add the fragments */
for (i = 0; i < nfrags; i++) { for (i = 0; i < nfrags; i++) {
@ -693,7 +700,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
/* Setup descriptor */ /* Setup descriptor */
priv->tx_skbs[pointer] = skb; priv->tx_skbs[pointer] = skb;
txdes = &priv->descs->txdes[pointer]; txdes = &priv->txdes[pointer];
ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer); ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer);
ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN; ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN;
ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len); ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len);
@ -704,7 +711,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
txdes->txdes3 = cpu_to_le32(map); txdes->txdes3 = cpu_to_le32(map);
/* Next one */ /* Next one */
pointer = ftgmac100_next_tx_pointer(pointer); pointer = ftgmac100_next_tx_pointer(priv, pointer);
} }
/* Order the previous packet and descriptor udpates /* Order the previous packet and descriptor udpates
@ -744,8 +751,8 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
/* Then all fragments */ /* Then all fragments */
for (j = 0; j < i; j++) { for (j = 0; j < i; j++) {
pointer = ftgmac100_next_tx_pointer(pointer); pointer = ftgmac100_next_tx_pointer(priv, pointer);
txdes = &priv->descs->txdes[pointer]; txdes = &priv->txdes[pointer];
ctl_stat = le32_to_cpu(txdes->txdes0); ctl_stat = le32_to_cpu(txdes->txdes0);
ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat); ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat);
txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask); txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask);
@ -768,8 +775,8 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
int i; int i;
/* Free all RX buffers */ /* Free all RX buffers */
for (i = 0; i < RX_QUEUE_ENTRIES; i++) { for (i = 0; i < priv->rx_q_entries; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; struct ftgmac100_rxdes *rxdes = &priv->rxdes[i];
struct sk_buff *skb = priv->rx_skbs[i]; struct sk_buff *skb = priv->rx_skbs[i];
dma_addr_t map = le32_to_cpu(rxdes->rxdes3); dma_addr_t map = le32_to_cpu(rxdes->rxdes3);
@ -782,8 +789,8 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
} }
/* Free all TX buffers */ /* Free all TX buffers */
for (i = 0; i < TX_QUEUE_ENTRIES; i++) { for (i = 0; i < priv->tx_q_entries; i++) {
struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; struct ftgmac100_txdes *txdes = &priv->txdes[i];
struct sk_buff *skb = priv->tx_skbs[i]; struct sk_buff *skb = priv->tx_skbs[i];
if (!skb) if (!skb)
@ -795,10 +802,22 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
static void ftgmac100_free_rings(struct ftgmac100 *priv) static void ftgmac100_free_rings(struct ftgmac100 *priv)
{ {
/* Free skb arrays */
kfree(priv->rx_skbs);
kfree(priv->tx_skbs);
/* Free descriptors */ /* Free descriptors */
if (priv->descs) if (priv->rxdes)
dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), dma_free_coherent(priv->dev, MAX_RX_QUEUE_ENTRIES *
priv->descs, priv->descs_dma_addr); sizeof(struct ftgmac100_rxdes),
priv->rxdes, priv->rxdes_dma);
priv->rxdes = NULL;
if (priv->txdes)
dma_free_coherent(priv->dev, MAX_TX_QUEUE_ENTRIES *
sizeof(struct ftgmac100_txdes),
priv->txdes, priv->txdes_dma);
priv->txdes = NULL;
/* Free scratch packet buffer */ /* Free scratch packet buffer */
if (priv->rx_scratch) if (priv->rx_scratch)
@ -808,11 +827,28 @@ static void ftgmac100_free_rings(struct ftgmac100 *priv)
static int ftgmac100_alloc_rings(struct ftgmac100 *priv) static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
{ {
/* Allocate skb arrays */
priv->rx_skbs = kcalloc(MAX_RX_QUEUE_ENTRIES, sizeof(void *),
GFP_KERNEL);
if (!priv->rx_skbs)
return -ENOMEM;
priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *),
GFP_KERNEL);
if (!priv->tx_skbs)
return -ENOMEM;
/* Allocate descriptors */ /* Allocate descriptors */
priv->descs = dma_zalloc_coherent(priv->dev, priv->rxdes = dma_zalloc_coherent(priv->dev,
sizeof(struct ftgmac100_descs), MAX_RX_QUEUE_ENTRIES *
&priv->descs_dma_addr, GFP_KERNEL); sizeof(struct ftgmac100_rxdes),
if (!priv->descs) &priv->rxdes_dma, GFP_KERNEL);
if (!priv->rxdes)
return -ENOMEM;
priv->txdes = dma_zalloc_coherent(priv->dev,
MAX_TX_QUEUE_ENTRIES *
sizeof(struct ftgmac100_txdes),
&priv->txdes_dma, GFP_KERNEL);
if (!priv->txdes)
return -ENOMEM; return -ENOMEM;
/* Allocate scratch packet buffer */ /* Allocate scratch packet buffer */
@ -828,22 +864,32 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
static void ftgmac100_init_rings(struct ftgmac100 *priv) static void ftgmac100_init_rings(struct ftgmac100 *priv)
{ {
struct ftgmac100_rxdes *rxdes; struct ftgmac100_rxdes *rxdes = NULL;
struct ftgmac100_txdes *txdes; struct ftgmac100_txdes *txdes = NULL;
int i; int i;
/* Update entries counts */
priv->rx_q_entries = priv->new_rx_q_entries;
priv->tx_q_entries = priv->new_tx_q_entries;
if (WARN_ON(priv->rx_q_entries < MIN_RX_QUEUE_ENTRIES))
return;
/* Initialize RX ring */ /* Initialize RX ring */
for (i = 0; i < RX_QUEUE_ENTRIES; i++) { for (i = 0; i < priv->rx_q_entries; i++) {
rxdes = &priv->descs->rxdes[i]; rxdes = &priv->rxdes[i];
rxdes->rxdes0 = 0; rxdes->rxdes0 = 0;
rxdes->rxdes3 = cpu_to_le32(priv->rx_scratch_dma); rxdes->rxdes3 = cpu_to_le32(priv->rx_scratch_dma);
} }
/* Mark the end of the ring */ /* Mark the end of the ring */
rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask);
if (WARN_ON(priv->tx_q_entries < MIN_RX_QUEUE_ENTRIES))
return;
/* Initialize TX ring */ /* Initialize TX ring */
for (i = 0; i < TX_QUEUE_ENTRIES; i++) { for (i = 0; i < priv->tx_q_entries; i++) {
txdes = &priv->descs->txdes[i]; txdes = &priv->txdes[i];
txdes->txdes0 = 0; txdes->txdes0 = 0;
} }
txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask); txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask);
@ -853,8 +899,8 @@ static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv)
{ {
int i; int i;
for (i = 0; i < RX_QUEUE_ENTRIES; i++) { for (i = 0; i < priv->rx_q_entries; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; struct ftgmac100_rxdes *rxdes = &priv->rxdes[i];
if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL)) if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
@ -999,11 +1045,53 @@ static void ftgmac100_get_drvinfo(struct net_device *netdev,
strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info));
} }
static int ftgmac100_nway_reset(struct net_device *ndev)
{
if (!ndev->phydev)
return -ENXIO;
return phy_start_aneg(ndev->phydev);
}
static void ftgmac100_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ering)
{
struct ftgmac100 *priv = netdev_priv(netdev);
memset(ering, 0, sizeof(*ering));
ering->rx_max_pending = MAX_RX_QUEUE_ENTRIES;
ering->tx_max_pending = MAX_TX_QUEUE_ENTRIES;
ering->rx_pending = priv->rx_q_entries;
ering->tx_pending = priv->tx_q_entries;
}
static int ftgmac100_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ering)
{
struct ftgmac100 *priv = netdev_priv(netdev);
if (ering->rx_pending > MAX_RX_QUEUE_ENTRIES ||
ering->tx_pending > MAX_TX_QUEUE_ENTRIES ||
ering->rx_pending < MIN_RX_QUEUE_ENTRIES ||
ering->tx_pending < MIN_TX_QUEUE_ENTRIES ||
!is_power_of_2(ering->rx_pending) ||
!is_power_of_2(ering->tx_pending))
return -EINVAL;
priv->new_rx_q_entries = ering->rx_pending;
priv->new_tx_q_entries = ering->tx_pending;
if (netif_running(netdev))
schedule_work(&priv->reset_task);
return 0;
}
static const struct ethtool_ops ftgmac100_ethtool_ops = { static const struct ethtool_ops ftgmac100_ethtool_ops = {
.get_drvinfo = ftgmac100_get_drvinfo, .get_drvinfo = ftgmac100_get_drvinfo,
.get_link = ethtool_op_get_link, .get_link = ethtool_op_get_link,
.get_link_ksettings = phy_ethtool_get_link_ksettings, .get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_ringparam = ftgmac100_get_ringparam,
.set_ringparam = ftgmac100_set_ringparam,
}; };
static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
@ -1059,7 +1147,7 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
static bool ftgmac100_check_rx(struct ftgmac100 *priv) static bool ftgmac100_check_rx(struct ftgmac100 *priv)
{ {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[priv->rx_pointer]; struct ftgmac100_rxdes *rxdes = &priv->rxdes[priv->rx_pointer];
/* Do we have a packet ? */ /* Do we have a packet ? */
return !!(rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY)); return !!(rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY));
@ -1496,6 +1584,10 @@ static int ftgmac100_probe(struct platform_device *pdev)
goto err_setup_mdio; goto err_setup_mdio;
} }
/* Default ring sizes */
priv->rx_q_entries = priv->new_rx_q_entries = DEF_RX_QUEUE_ENTRIES;
priv->tx_q_entries = priv->new_tx_q_entries = DEF_TX_QUEUE_ENTRIES;
/* Base feature set */ /* Base feature set */
netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
NETIF_F_GRO | NETIF_F_SG; NETIF_F_GRO | NETIF_F_SG;