#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <net/netdev_queues.h>
#include "eea_adminq.h"
#include "eea_net.h"
#include "eea_pci.h"
#include "eea_ring.h"
#define EEA_SPLIT_HDR_SIZE ALIGN(128, L1_CACHE_BYTES)
static irqreturn_t eea_irq_handler(int irq, void *data)
{
struct eea_irq_blk *blk = data;
napi_schedule_irqoff(&blk->napi);
return IRQ_HANDLED;
}
static void eea_free_irq_blk(struct eea_net *enet)
{
struct eea_irq_blk *blk;
u32 num;
int i;
if (!enet->irq_blks)
return;
num = enet->edev->rx_num;
for (i = 0; i < num; i++) {
blk = &enet->irq_blks[i];
if (blk->ready)
eea_pci_free_irq(blk);
blk->ready = false;
}
kvfree(enet->irq_blks);
enet->irq_blks = NULL;
}
static int eea_alloc_irq_blks(struct eea_net *enet)
{
struct eea_device *edev = enet->edev;
struct eea_irq_blk *blk, *irq_blks;
int i, err, num;
num = enet->edev->rx_num;
irq_blks = kvcalloc(num, sizeof(*blk), GFP_KERNEL);
if (!irq_blks)
return -ENOMEM;
enet->irq_blks = irq_blks;
for (i = 0; i < num; i++) {
blk = &irq_blks[i];
blk->idx = i;
blk->msix_vec = i + 1;
err = eea_pci_request_irq(edev, blk, eea_irq_handler);
if (err)
goto err_free_irq_blk;
blk->ready = true;
}
return 0;
err_free_irq_blk:
eea_free_irq_blk(enet);
return err;
}
static int eea_update_queues(struct eea_net *enet)
{
return netif_set_real_num_queues(enet->netdev, enet->cfg.tx_ring_num,
enet->cfg.rx_ring_num);
}
void eea_init_ctx(struct eea_net *enet, struct eea_net_init_ctx *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->netdev = enet->netdev;
ctx->edev = enet->edev;
ctx->cfg = enet->cfg;
}
static void eea_bind_q_and_cfg(struct eea_net *enet,
struct eea_net_init_ctx *ctx)
{
struct eea_irq_blk *blk;
struct eea_net_rx *rx;
struct eea_net_tx *tx;
int i;
spin_lock(&enet->stats_lock);
enet->cfg = ctx->cfg;
enet->rx = ctx->rx;
enet->tx = ctx->tx;
for (i = 0; i < ctx->cfg.rx_ring_num; i++) {
blk = &enet->irq_blks[i];
rx = ctx->rx[i];
tx = &ctx->tx[i];
rx->enet = enet;
rx->napi = &blk->napi;
rx->ering->msix_vec = blk->msix_vec;
tx->enet = enet;
tx->ering->msix_vec = blk->msix_vec;
blk->rx = rx;
}
spin_unlock(&enet->stats_lock);
}
static void eea_unbind_q_and_cfg(struct eea_net *enet,
struct eea_net_init_ctx *ctx)
{
struct eea_irq_blk *blk;
struct eea_net_rx *rx;
int i;
spin_lock(&enet->stats_lock);
ctx->cfg = enet->cfg;
ctx->rx = enet->rx;
ctx->tx = enet->tx;
enet->rx = NULL;
enet->tx = NULL;
for (i = 0; i < ctx->cfg.rx_ring_num; i++) {
blk = &enet->irq_blks[i];
rx = ctx->rx[i];
rx->napi = NULL;
blk->rx = NULL;
}
spin_unlock(&enet->stats_lock);
}
static void eea_free_rxtx_q_mem(struct eea_net_init_ctx *ctx)
{
struct eea_net_rx *rx;
struct eea_net_tx *tx;
int i;
for (i = 0; i < ctx->cfg.rx_ring_num; i++) {
rx = ctx->rx[i];
tx = &ctx->tx[i];
eea_free_rx(rx, &ctx->cfg);
eea_free_tx(tx, &ctx->cfg);
}
kvfree(ctx->rx);
kvfree(ctx->tx);
}
static int eea_alloc_rxtx_q_mem(struct eea_net_init_ctx *ctx)
{
struct eea_net_rx *rx;
struct eea_net_tx *tx;
int err, i;
ctx->tx = kvcalloc(ctx->cfg.tx_ring_num, sizeof(*ctx->tx), GFP_KERNEL);
if (!ctx->tx)
return -ENOMEM;
ctx->rx = kvcalloc(ctx->cfg.rx_ring_num, sizeof(*ctx->rx), GFP_KERNEL);
if (!ctx->rx)
goto err_free_tx;
ctx->cfg.rx_sq_desc_size = sizeof(struct eea_rx_desc);
ctx->cfg.rx_cq_desc_size = sizeof(struct eea_rx_cdesc);
ctx->cfg.tx_sq_desc_size = sizeof(struct eea_tx_desc);
ctx->cfg.tx_cq_desc_size = sizeof(struct eea_tx_cdesc);
if (!ctx->cfg.split_hdr)
ctx->cfg.rx_sq_desc_size = sizeof(struct eea_rx_desc_no_hdr);
for (i = 0; i < ctx->cfg.rx_ring_num; i++) {
rx = eea_alloc_rx(ctx, i);
if (!rx)
goto err_free;
ctx->rx[i] = rx;
tx = ctx->tx + i;
err = eea_alloc_tx(ctx, tx, i);
if (err)
goto err_free;
}
return 0;
err_free:
for (i = 0; i < ctx->cfg.rx_ring_num; i++) {
rx = ctx->rx[i];
tx = ctx->tx + i;
eea_free_rx(rx, &ctx->cfg);
eea_free_tx(tx, &ctx->cfg);
}
kvfree(ctx->rx);
err_free_tx:
kvfree(ctx->tx);
return -ENOMEM;
}
static int eea_hw_active_ring(struct eea_net *enet)
{
return eea_adminq_create_q(enet, enet->cfg.rx_ring_num
+ enet->cfg.tx_ring_num, 0);
}
static int eea_hw_unactive_ring(struct eea_net *enet)
{
int err;
err = eea_adminq_destroy_all_q(enet);
if (err)
netdev_warn(enet->netdev, "unactive rxtx ring failed.\n");
return err;
}
static void eea_stop_rxtx(struct net_device *netdev)
{
struct eea_net *enet = netdev_priv(netdev);
int i;
netif_tx_disable(netdev);
for (i = 0; i < enet->cfg.rx_ring_num; i++)
enet_rx_stop(enet->rx[i]);
netif_carrier_off(netdev);
}
static void eea_start_rxtx(struct eea_net *enet)
{
int i;
for (i = 0; i < enet->cfg.rx_ring_num; i++)
enet_rx_start(enet->rx[i]);
netif_tx_start_all_queues(enet->netdev);
netif_carrier_on(enet->netdev);
enet->started = true;
}
static int eea_netdev_stop(struct net_device *netdev)
{
struct eea_net *enet = netdev_priv(netdev);
struct eea_net_init_ctx ctx;
if (!enet->started) {
netdev_warn(netdev, "eea netdev stop: but dev is not started.\n");
return 0;
}
eea_init_ctx(enet, &ctx);
eea_stop_rxtx(netdev);
eea_hw_unactive_ring(enet);
eea_unbind_q_and_cfg(enet, &ctx);
eea_free_rxtx_q_mem(&ctx);
enet->started = false;
return 0;
}
static int eea_netdev_open(struct net_device *netdev)
{
struct eea_net *enet = netdev_priv(netdev);
struct eea_net_init_ctx ctx;
int err;
if (enet->link_err) {
netdev_err(netdev, "netdev open err, because link error: %d\n",
enet->link_err);
return -EBUSY;
}
eea_init_ctx(enet, &ctx);
err = eea_alloc_rxtx_q_mem(&ctx);
if (err)
goto err_done;
eea_bind_q_and_cfg(enet, &ctx);
err = eea_update_queues(enet);
if (err)
goto err_free_q;
err = eea_hw_active_ring(enet);
if (err)
goto err_free_q;
eea_start_rxtx(enet);
return 0;
err_free_q:
eea_unbind_q_and_cfg(enet, &ctx);
eea_free_rxtx_q_mem(&ctx);
err_done:
return err;
}
static void eea_stats(struct net_device *netdev, struct rtnl_link_stats64 *tot)
{
struct eea_net *enet = netdev_priv(netdev);
u64 packets, bytes, drop, lerr;
u32 start;
int i;
spin_lock(&enet->stats_lock);
if (enet->rx) {
for (i = 0; i < enet->cfg.rx_ring_num; i++) {
struct eea_net_rx *rx = enet->rx[i];
do {
start = u64_stats_fetch_begin(&rx->stats.syncp);
packets = u64_stats_read(&rx->stats.packets);
bytes = u64_stats_read(&rx->stats.bytes);
drop = u64_stats_read(&rx->stats.drops);
lerr = u64_stats_read(&rx->stats.length_errors);
} while (u64_stats_fetch_retry(&rx->stats.syncp,
start));
tot->rx_packets += packets;
tot->rx_bytes += bytes;
tot->rx_dropped += drop;
tot->rx_length_errors += lerr;
tot->rx_errors += lerr;
}
}
if (enet->tx) {
for (i = 0; i < enet->cfg.tx_ring_num; i++) {
struct eea_net_tx *tx = &enet->tx[i];
do {
start = u64_stats_fetch_begin(&tx->stats.syncp);
packets = u64_stats_read(&tx->stats.packets);
bytes = u64_stats_read(&tx->stats.bytes);
drop = u64_stats_read(&tx->stats.drops);
} while (u64_stats_fetch_retry(&tx->stats.syncp,
start));
tot->tx_packets += packets;
tot->tx_bytes += bytes;
tot->tx_dropped += drop;
}
}
spin_unlock(&enet->stats_lock);
}
int eea_reset_hw_resources(struct eea_net *enet, struct eea_net_init_ctx *ctx)
{
struct eea_net_init_ctx ctx_old = {0};
int err, error;
if (!netif_running(enet->netdev) || !enet->started) {
spin_lock(&enet->stats_lock);
enet->cfg = ctx->cfg;
spin_unlock(&enet->stats_lock);
return 0;
}
err = eea_alloc_rxtx_q_mem(ctx);
if (err) {
netdev_warn(enet->netdev,
"eea reset: alloc q failed. stop reset. err %d\n",
err);
return err;
}
eea_stop_rxtx(enet->netdev);
eea_hw_unactive_ring(enet);
eea_unbind_q_and_cfg(enet, &ctx_old);
eea_bind_q_and_cfg(enet, ctx);
err = eea_update_queues(enet);
if (err) {
netdev_err(enet->netdev,
"eea reset: set real num queues failed. err %d\n",
err);
goto err_bind_old;
}
err = eea_hw_active_ring(enet);
if (err) {
netdev_err(enet->netdev, "eea reset: active new ring. err %d\n",
err);
eea_unbind_q_and_cfg(enet, ctx);
goto err_free_q;
}
eea_start_rxtx(enet);
eea_free_rxtx_q_mem(&ctx_old);
return 0;
err_bind_old:
eea_unbind_q_and_cfg(enet, ctx);
eea_bind_q_and_cfg(enet, &ctx_old);
error = eea_hw_active_ring(enet);
if (error) {
netdev_err(enet->netdev, "eea reset: active old ring. err %d\n",
error);
eea_unbind_q_and_cfg(enet, &ctx_old);
err = error;
goto err_free_q;
}
eea_start_rxtx(enet);
eea_free_rxtx_q_mem(ctx);
return err;
err_free_q:
eea_free_rxtx_q_mem(ctx);
eea_free_rxtx_q_mem(&ctx_old);
enet->started = false;
return err;
}
int eea_queues_check_and_reset(struct eea_device *edev)
{
struct eea_aq_dev_status dstatus = {0};
struct eea_aq_queue_status *qstatus;
struct eea_aq_queue_status *qs;
struct eea_net_init_ctx ctx;
bool need_reset = false;
int i, err = 0;
rtnl_lock();
if (!netif_running(edev->enet->netdev))
goto err_unlock;
if (!edev->enet->started || edev->enet->link_err)
goto err_unlock;
err = eea_adminq_dev_status(edev->enet, &dstatus);
if (err) {
netdev_warn(edev->enet->netdev, "query queue status failed.\n");
goto err_unlock;
}
if (le16_to_cpu(dstatus.status->link_status) == EEA_LINK_DOWN_STATUS) {
eea_netdev_stop(edev->enet->netdev);
edev->enet->link_err = EEA_LINK_ERR_LINK_DOWN;
netdev_warn(edev->enet->netdev, "device link is down. stop device.\n");
goto err_free;
}
qstatus = dstatus.status->q_status;
for (i = 0; i < dstatus.num; ++i) {
qs = &qstatus[i];
if (le16_to_cpu(qs->status) == EEA_QUEUE_STATUS_NEED_RESET) {
netdev_warn(edev->enet->netdev,
"queue status: queue %u needs to reset\n",
le16_to_cpu(qs->qidx));
need_reset = true;
}
}
if (need_reset) {
eea_init_ctx(edev->enet, &ctx);
err = eea_reset_hw_resources(edev->enet, &ctx);
}
err_free:
kfree(dstatus.status);
err_unlock:
rtnl_unlock();
return err;
}
static int eea_update_cfg(struct eea_net *enet,
struct eea_device *edev,
struct eea_aq_cfg *hwcfg)
{
u32 rx_max = le32_to_cpu(hwcfg->rx_depth_max);
u32 tx_max = le32_to_cpu(hwcfg->tx_depth_max);
u32 rx_def = le32_to_cpu(hwcfg->rx_depth_def);
u32 tx_def = le32_to_cpu(hwcfg->tx_depth_def);
if (edev->rx_num != edev->tx_num) {
dev_err(edev->dma_dev, "Inconsistent ring num: RX %u, TX %u\n",
edev->rx_num, edev->tx_num);
return -EINVAL;
}
if (rx_max > EEA_NET_IO_HW_RING_DEPTH_MAX ||
rx_max < EEA_NET_IO_HW_RING_DEPTH_MIN ||
tx_max > EEA_NET_IO_HW_RING_DEPTH_MAX ||
tx_max < EEA_NET_IO_HW_RING_DEPTH_MIN) {
dev_err(edev->dma_dev, "Invalid HW max depth: RX %u, TX %u\n",
rx_max, tx_max);
return -EINVAL;
}
if (rx_def > rx_max ||
tx_def > tx_max ||
rx_def < EEA_NET_IO_HW_RING_DEPTH_MIN ||
tx_def < EEA_NET_IO_HW_RING_DEPTH_MIN) {
dev_err(edev->dma_dev, "Invalid default depth: RX %u (max %u), TX %u (max %u)\n",
rx_def, rx_max, tx_def, tx_max);
return -EINVAL;
}
if (!is_power_of_2(rx_max) || !is_power_of_2(tx_max) ||
!is_power_of_2(rx_def) || !is_power_of_2(tx_def)) {
dev_err(edev->dma_dev, "Ring depth must be power of 2\n");
return -EINVAL;
}
enet->cfg_hw.rx_ring_depth = rx_max;
enet->cfg_hw.tx_ring_depth = tx_max;
enet->cfg_hw.rx_ring_num = edev->rx_num;
enet->cfg_hw.tx_ring_num = edev->tx_num;
enet->cfg_hw.split_hdr = EEA_SPLIT_HDR_SIZE;
enet->cfg.rx_ring_depth = rx_def;
enet->cfg.tx_ring_depth = tx_def;
enet->cfg.rx_ring_num = edev->rx_num;
enet->cfg.tx_ring_num = edev->tx_num;
return 0;
}
static int eea_netdev_init_features(struct net_device *netdev,
struct eea_net *enet,
struct eea_device *edev)
{
struct eea_aq_cfg *cfg;
int err;
u32 mtu;
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
err = eea_adminq_query_cfg(enet, cfg);
if (err)
goto err_free;
mtu = le16_to_cpu(cfg->mtu);
if (mtu < ETH_MIN_MTU) {
dev_err(edev->dma_dev, "The device gave us an invalid MTU. Here we can only exit the initialization. %u < %u\n",
mtu, ETH_MIN_MTU);
err = -EINVAL;
goto err_free;
}
err = eea_update_cfg(enet, edev, cfg);
if (err)
goto err_free;
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
netdev->hw_features |= NETIF_F_HW_CSUM;
netdev->hw_features |= NETIF_F_GRO_HW;
netdev->hw_features |= NETIF_F_SG;
netdev->hw_features |= NETIF_F_TSO;
netdev->hw_features |= NETIF_F_TSO_ECN;
netdev->hw_features |= NETIF_F_TSO6;
netdev->hw_features |= NETIF_F_GSO_UDP_L4;
netdev->features |= NETIF_F_HIGHDMA;
netdev->features |= NETIF_F_HW_CSUM;
netdev->features |= NETIF_F_SG;
netdev->features |= NETIF_F_GSO_ROBUST;
netdev->features |= netdev->hw_features & NETIF_F_ALL_TSO;
netdev->features |= NETIF_F_RXCSUM;
netdev->features |= NETIF_F_GRO_HW;
netdev->vlan_features = netdev->features;
if (!is_valid_ether_addr(cfg->mac)) {
dev_err(edev->dma_dev, "The device gave invalid mac %pM\n",
cfg->mac);
err = -EINVAL;
goto err_free;
}
eth_hw_addr_set(netdev, cfg->mac);
enet->speed = SPEED_UNKNOWN;
enet->duplex = DUPLEX_UNKNOWN;
netdev->min_mtu = ETH_MIN_MTU;
netdev->mtu = mtu;
netdev->max_mtu = mtu;
err_free:
kfree(cfg);
return err;
}
static const struct net_device_ops eea_netdev = {
.ndo_open = eea_netdev_open,
.ndo_stop = eea_netdev_stop,
.ndo_start_xmit = eea_tx_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_get_stats64 = eea_stats,
.ndo_features_check = passthru_features_check,
};
static struct eea_net *eea_netdev_alloc(struct eea_device *edev, u32 pairs)
{
struct net_device *netdev;
struct eea_net *enet;
int err;
netdev = alloc_etherdev_mq(sizeof(struct eea_net), pairs);
if (!netdev) {
dev_err(edev->dma_dev,
"alloc_etherdev_mq failed with pairs %d\n", pairs);
return NULL;
}
netdev->netdev_ops = &eea_netdev;
netdev->ethtool_ops = &eea_ethtool_ops;
SET_NETDEV_DEV(netdev, edev->dma_dev);
enet = netdev_priv(netdev);
enet->netdev = netdev;
enet->edev = edev;
edev->enet = enet;
err = eea_alloc_irq_blks(enet);
if (err) {
dev_err(edev->dma_dev,
"eea_alloc_irq_blks failed with pairs %d\n", pairs);
free_netdev(netdev);
return NULL;
}
spin_lock_init(&enet->stats_lock);
return enet;
}
static void eea_update_ts_off(struct eea_device *edev, struct eea_net *enet)
{
u64 ts;
ts = eea_pci_device_ts(edev);
enet->hw_ts_offset = ktime_get_real() - ts;
}
static int eea_net_reprobe(struct eea_device *edev)
{
struct eea_net *enet = edev->enet;
int err = 0;
enet->edev = edev;
if (!enet->adminq.ring) {
err = eea_create_adminq(enet, edev->rx_num + edev->tx_num);
if (err)
return err;
}
err = eea_alloc_irq_blks(enet);
if (err)
goto err_destroy_aq;
eea_update_ts_off(edev, enet);
rtnl_lock();
enet->link_err = 0;
if (edev->ha_reset_netdev_running &&
netif_running(edev->enet->netdev)) {
err = eea_netdev_open(enet->netdev);
if (err) {
enet->link_err = EEA_LINK_ERR_HA_RESET_DEV;
rtnl_unlock();
goto err_free_irq_blks;
}
}
rtnl_unlock();
enet->wait_pci_ready = false;
return 0;
err_free_irq_blks:
eea_free_irq_blk(enet);
err_destroy_aq:
eea_destroy_adminq(enet);
return err;
}
int eea_net_probe(struct eea_device *edev)
{
struct eea_net *enet;
int err = -ENOMEM;
if (edev->enet)
return eea_net_reprobe(edev);
enet = eea_netdev_alloc(edev, edev->rx_num);
if (!enet)
return -ENOMEM;
err = eea_create_adminq(enet, edev->rx_num + edev->tx_num);
if (err)
goto err_free_netdev;
eea_adminq_config_host_info(enet);
err = eea_netdev_init_features(enet->netdev, enet, edev);
if (err)
goto err_reset_dev;
eea_update_ts_off(edev, enet);
netif_carrier_off(enet->netdev);
err = register_netdev(enet->netdev);
if (err)
goto err_reset_dev;
netdev_dbg(enet->netdev, "eea probe success.\n");
return 0;
err_reset_dev:
eea_device_reset(edev);
eea_destroy_adminq(enet);
err_free_netdev:
eea_free_irq_blk(enet);
free_netdev(enet->netdev);
return err;
}
static void eea_net_ha_reset_remove(struct eea_net *enet,
struct eea_device *edev)
{
rtnl_lock();
edev->ha_reset_netdev_running = false;
if (netif_running(enet->netdev)) {
eea_netdev_stop(enet->netdev);
edev->ha_reset_netdev_running = true;
}
enet->link_err = EEA_LINK_ERR_HA_RESET_DEV;
rtnl_unlock();
eea_device_reset(edev);
eea_destroy_adminq(enet);
eea_free_irq_blk(enet);
enet->wait_pci_ready = true;
}
void eea_net_remove(struct eea_device *edev, bool ha)
{
struct net_device *netdev;
struct eea_net *enet;
enet = edev->enet;
netdev = enet->netdev;
if (ha) {
if (enet->wait_pci_ready)
return;
eea_net_ha_reset_remove(enet, edev);
return;
}
unregister_netdev(netdev);
if (!enet->wait_pci_ready) {
eea_device_reset(edev);
eea_destroy_adminq(enet);
eea_free_irq_blk(enet);
}
free_netdev(netdev);
}
void eea_net_shutdown(struct eea_device *edev)
{
struct net_device *netdev;
struct eea_net *enet;
enet = edev->enet;
netdev = enet->netdev;
rtnl_lock();
netif_device_detach(netdev);
dev_close(netdev);
if (!enet->wait_pci_ready) {
eea_device_reset(edev);
eea_destroy_adminq(enet);
eea_free_irq_blk(enet);
}
rtnl_unlock();
}