Jintao 50c46e6857 Add LingYao
Change-Id: Iae6634ce565940904ee320c678d0f77473bebb90
2025-01-03 16:08:55 +08:00

517 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/* A network driver using virtio.
*
* Copyright 2007 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
*/
//#define DEBUG
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/virtio.h>
#include <linux/virtio_net.h>
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/scatterlist.h>
#include <linux/if_vlan.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/average.h>
#include <linux/filter.h>
#include <linux/kernel.h>
#include <net/route.h>
#include <net/xdp.h>
#include <net/net_failover.h>
#include "crete_nic.h"
#include "crete_nic_io.h"
#include <linux/firmware.h>
#include "../crete-core/crete_fw_update.h"
#define CRETE_VNIC_DRIVER_VERSION "crete-vnic-1.0"
/******************net tools****************************/
static void crete_vnic_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
struct crete_core_dev *core_dev = vnic_priv->coredev;
struct pci_dev *pdev = core_dev->pdev;
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
snprintf(info->fw_version, sizeof(info->fw_version),
"%d.%d.%d.%d", fw_ver_maj(core_dev),
fw_ver_min(core_dev), fw_ver_sub(core_dev), fw_ver_rev(core_dev));
strlcpy(info->version, CRETE_VNIC_DRIVER_VERSION,
sizeof(info->version));
strlcpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info));
}
static u32 crete_vnic_get_link(struct net_device *netdev)
{
//struct crete_vnic_priv *vnic_priv = netdev_priv(netdev);
//struct crete_core_dev *core_dev =vnic_priv->coredev;
/* If the link is not reported up to netdev, interrupts are disabled,
* and so the physical link state may have changed since we last
* looked. Set get_link_status to make sure that the true link
* state is interrogated, rather than pulling a cached and possibly
* stale link state from the driver.
*/
if (!netif_carrier_ok(netdev)) {
/*todo to vdev get link status */
return 0;
}
return 1;
}
/*
* about the anolis 5.10.134-14 kernel version
* auxliary define with return int value
*/
#if defined(SNIC_ANOLIS_VERSION14) || !defined(CONFIG_ETHTOOL_EXTEND_RINGPARAM)
static void crete_vnic_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *ring)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
ring->rx_max_pending = virtqueue_get_vring_size(vnic_priv->rq[0].vq);
ring->tx_max_pending = virtqueue_get_vring_size(vnic_priv->sq[0].vq);
ring->rx_pending = ring->rx_max_pending;
ring->tx_pending = ring->tx_max_pending;
}
#else
static void crete_vnic_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam
*kernel_ring,
struct netlink_ext_ack *extack)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
ring->rx_max_pending = virtqueue_get_vring_size(vnic_priv->rq[0].vq);
ring->tx_max_pending = virtqueue_get_vring_size(vnic_priv->sq[0].vq);
ring->rx_pending = ring->rx_max_pending;
ring->tx_pending = ring->tx_max_pending;
}
#endif
static void crete_vnic_get_channels(struct net_device *dev,
struct ethtool_channels *channels)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
channels->combined_count = vnic_priv->curr_queue_pairs;
channels->max_combined = vnic_priv->max_queue_pairs;
channels->max_other = 0;
channels->rx_count = 0;
channels->tx_count = 0;
channels->other_count = 0;
}
/* TODO: Eliminate OOO packets during switching */
static int crete_vnic_set_channels(struct net_device *dev,
struct ethtool_channels *channels)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
u16 queue_pairs = channels->combined_count;
int err;
/* We don't support separate rx/tx channels.
* We don't allow setting 'other' channels.
*/
if (channels->rx_count || channels->tx_count || channels->other_count)
return -EINVAL;
if (queue_pairs > vnic_priv->max_queue_pairs || queue_pairs == 0)
return -EINVAL;
err = __crete_vnic_set_queues(dev, queue_pairs);
if (err)
goto err;
netif_set_real_num_tx_queues(dev, queue_pairs);
netif_set_real_num_rx_queues(dev, queue_pairs);
err:
return err;
}
int crete_vnic_ethtool_flash_device(struct crete_vnic_priv *vnic_priv,
struct ethtool_flash *flash)
{
struct crete_core_dev *cdev = vnic_priv->coredev;
struct net_device *dev = vnic_priv->netdev;
const struct firmware *fw;
int err;
if (flash->region != ETHTOOL_FLASH_ALL_REGIONS)
return -EOPNOTSUPP;
err = request_firmware_direct(&fw, flash->data, &dev->dev);
if (err)
return err;
dev_hold(dev);
rtnl_unlock();
err = crete_firmware_flash(cdev, fw, NULL, CRETE_FWU_ETHTOOL);
release_firmware(fw);
rtnl_lock();
dev_put(dev);
return err;
}
static int crete_vnic_flash_device(struct net_device *dev,
struct ethtool_flash *flash)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
return crete_vnic_ethtool_flash_device(vnic_priv, flash);
}
struct ptys2ethtool_config {
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertised);
};
static
struct ptys2ethtool_config ptys2ethtool_table[CRETE_LINK_MODES_CAP_BIT];
#define CRETE_BUILD_PTYS2ETHTOOL_CONFIG(reg_, table, ...) \
({ \
struct ptys2ethtool_config *cfg; \
const unsigned int modes[] = { __VA_ARGS__ }; \
unsigned int i, bit, idx; \
cfg = &table[reg_]; \
bitmap_zero(cfg->supported, \
__ETHTOOL_LINK_MODE_MASK_NBITS); \
bitmap_zero(cfg->advertised, \
__ETHTOOL_LINK_MODE_MASK_NBITS); \
for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) { \
bit = modes[i] % 64; \
idx = modes[i] / 64; \
__set_bit(bit, &cfg->supported[idx]); \
__set_bit(bit, &cfg->advertised[idx]); \
} \
})
void crete_build_ptys2ethtool_map(void)
{
memset(ptys2ethtool_table, 0, sizeof(ptys2ethtool_table));
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_10GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
ETHTOOL_LINK_MODE_10000baseER_Full_BIT);
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_25GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
ETHTOOL_LINK_MODE_25000baseSR_Full_BIT);
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_40GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT);
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_50GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT);
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_100GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT);
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_200GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT);
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_400GBASE_BIT, ptys2ethtool_table,
ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT);
}
u16 crete_port_speed2linkmode(u32 speed)
{
int i;
for (i = 0; i < ARRAY_SIZE(crete_link_speed); ++i) {
if (crete_link_speed[i] == speed)
return i;
}
return 0;
}
int crete_ethtool_set_link_ksettings(struct crete_vnic_priv *vnic_priv,
const struct ethtool_link_ksettings *link_ksettings)
{
struct crete_core_dev *cdev = vnic_priv->coredev;
const struct ethtool_link_settings *base = &link_ksettings->base;
struct net_device *dev = vnic_priv->netdev;
struct crete_speed_duplex sd_current = {0};
u16 change_bit = 0;
u16 link_mode;
int ret;
if (base->autoneg == AUTONEG_ENABLE) {
netdev_err(dev, "Autonegotiation is not supported!\n");
return -EINVAL;
}
if (base->duplex != DUPLEX_FULL) {
netdev_err(dev, "Only full duplex mode is supported!\n");
return -EINVAL;
}
link_mode = crete_port_speed2linkmode(base->speed);
if (link_mode >= CRETE_LINK_MODES_NUMBER || link_mode < CRETE_10GBASE) {
netdev_err(dev, "speed parameter is illeagl.\n");
return -EINVAL;
}
ret = crete_get_port_speed_duplex(cdev, &sd_current);
if (ret) {
netdev_err(dev, "Failed to get current speed and duplex mode!\n");
return ret;
}
if (sd_current.speed == link_mode && sd_current.duplex == base->duplex) {
netdev_info(dev, "Speed and duplex mode are already set to the requested values.\n");
return 0;
}
change_bit |= (sd_current.speed != link_mode) ? LINK_SPEED_OP_BIT : 0;
change_bit |= (sd_current.duplex != base->duplex) ? DUPLEX_MODE_OP_BIT : 0;
if (!netif_running(dev)) {
netdev_err(dev, "Network interface is not running. Cannot change settings.\n");
return -EINVAL;
}
ret = crete_set_port_speed_duplex(cdev, link_mode, base->duplex, change_bit);
if (ret) {
netdev_err(dev, "Failed to set speed and duplex mode!\n");
return ret;
}
netdev_info(dev, "Speed set to %d Mbps, duplex mode set to full.\n", base->speed);
return 0;
}
static int crete_vnic_set_link_ksettings(struct net_device *dev,
const struct ethtool_link_ksettings *link_ksettings)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
return crete_ethtool_set_link_ksettings(vnic_priv, link_ksettings);
}
static void ptys2ethtool_adver_link(u32 eth_speed_cap,
u8 tx_pause, u8 rx_pause, struct ethtool_link_ksettings *link_ksettings)
{
unsigned long speed_cap = eth_speed_cap;
unsigned long *advertising_modes = link_ksettings->link_modes.advertising;
int speed;
for_each_set_bit(speed, &speed_cap, CRETE_LINK_MODES_CAP_BIT)
bitmap_or(advertising_modes, advertising_modes,
ptys2ethtool_table[speed].advertised,
__ETHTOOL_LINK_MODE_MASK_NBITS);
if (rx_pause)
ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, Pause);
if (tx_pause ^ rx_pause)
ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, Asym_Pause);
}
static void ptys2ethtool_supported_link(u32 eth_speed_cap,
struct ethtool_link_ksettings *link_ksettings)
{
unsigned long speed_cap = eth_speed_cap;
unsigned long *supported_modes = link_ksettings->link_modes.supported;
int speed;
for_each_set_bit(speed, &speed_cap, CRETE_LINK_MODES_CAP_BIT)
bitmap_or(supported_modes, supported_modes,
ptys2ethtool_table[speed].supported,
__ETHTOOL_LINK_MODE_MASK_NBITS);
ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Pause);
}
static int get_fec_supported_advertised(struct crete_core_dev *cdev,
struct ethtool_link_ksettings *link_ksettings)
{
u16 active_fec;
u16 eth_fec_cap;
u8 fec;
int err;
unsigned long fec_cap;
err = crete_get_fec_mode(cdev, &active_fec, &eth_fec_cap);
if (err)
return err;
fec_cap = eth_fec_cap;
for_each_set_bit(fec, &fec_cap, ARRAY_SIZE(crete_fec_2_ethtool_bit))
__set_bit(crete_fec_2_ethtool_bit[fec], link_ksettings->link_modes.supported);
if (active_fec <= CRETE_FEC_LLRS)
__set_bit(crete_fec_2_ethtool_bit[active_fec],
link_ksettings->link_modes.advertising);
return 0;
}
static int crete_vnic_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *link_ksettings)
{
struct crete_speed_duplex sd = {0};
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
struct crete_core_dev *cdev = vnic_priv->coredev;
u8 rx_pause = 0;
u8 tx_pause = 0;
int ret = 0;
link_ksettings->base.speed = SPEED_UNKNOWN;
link_ksettings->base.duplex = DUPLEX_UNKNOWN;
ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising);
ethtool_link_ksettings_zero_link_mode(link_ksettings, supported);
crete_get_pfc_enable(cdev, &rx_pause, &tx_pause);
ret = crete_get_port_speed_duplex(cdev, &sd);
if (ret) {
netdev_err(dev, "Failed to get speed and duplex mode!\n");
ret = 0;
}
if (sd.speed >= CRETE_10GBASE && sd.speed < CRETE_LINK_MODES_NUMBER)
link_ksettings->base.speed = crete_link_speed[sd.speed];
if (sd.duplex >= CRETE_HALF_DUPLEX && sd.duplex < CRETE_DUPLEX_MAX)
link_ksettings->base.duplex = crete_link_duplex[sd.duplex];
netdev_info(dev, "Speed Get speed[%d] speed_cap[0x%x].\n", sd.speed, sd.speed_cap);
ptys2ethtool_supported_link(sd.speed_cap, link_ksettings);
ptys2ethtool_adver_link(sd.speed_cap, tx_pause, rx_pause, link_ksettings);
link_ksettings->base.port = PORT_FIBRE;
ret = get_fec_supported_advertised(cdev, link_ksettings);
if (ret) {
netdev_err(dev, "%s: FEC caps query failed: %d\n",
__func__, ret);
ret = 0; /* don't fail caps query because of FEC error */
}
ethtool_link_ksettings_add_link_mode(link_ksettings, supported, FIBRE);
ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, FIBRE);
return ret;
}
static int crete_vnic_set_fecparam(struct net_device *dev,
struct ethtool_fecparam *fecparam)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
struct crete_core_dev *cdev = vnic_priv->coredev;
unsigned long fec_bitmap;
u16 fec_policy = 0;
bitmap_from_arr32(&fec_bitmap, &fecparam->fec, sizeof(fecparam->fec) * BITS_PER_BYTE);
if (bitmap_weight(&fec_bitmap, ETHTOOL_FEC_LLRS_BIT + 1) > 1)
return -EOPNOTSUPP;
switch (fecparam->fec) {
case ETHTOOL_FEC_OFF:
fec_policy = CRETE_FEC_OFF;
break;
case ETHTOOL_FEC_RS:
fec_policy = CRETE_FEC_RS;
break;
case ETHTOOL_FEC_BASER:
fec_policy = CRETE_FEC_BASER;
break;
default:
netdev_err(dev, "Unsupported FEC mode: %d\n", fecparam->fec);
return -EINVAL;
}
return crete_set_fec_mode(cdev, fec_policy);
}
static u32 crete_fec_cap_to_ethtool_fec(u16 fec_cap)
{
u32 ethtool_fec = 0;
if (fec_cap & BIT(CRETE_FEC_OFF))
ethtool_fec |= ETHTOOL_FEC_OFF;
if (fec_cap & BIT(CRETE_FEC_AUTO))
ethtool_fec |= ETHTOOL_FEC_AUTO;
if (fec_cap & BIT(CRETE_FEC_RS))
ethtool_fec |= ETHTOOL_FEC_RS;
if (fec_cap & BIT(CRETE_FEC_BASER))
ethtool_fec |= ETHTOOL_FEC_BASER;
if (fec_cap & BIT(CRETE_FEC_LLRS))
ethtool_fec |= ETHTOOL_FEC_LLRS;
return ethtool_fec;
}
static int crete_vnic_get_fecparam(struct net_device *dev,
struct ethtool_fecparam *fecparam)
{
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
struct crete_core_dev *cdev = vnic_priv->coredev;
u16 fec_active;
u16 fec_cap;
int err;
err = crete_get_fec_mode(cdev, &fec_active, &fec_cap);
if (err)
return err;
fecparam->active_fec = crete_fec_2_ethtool[fec_active];
fecparam->fec = crete_fec_cap_to_ethtool_fec(fec_cap);
return 0;
}
static const struct ethtool_ops crete_vnic_ethtool_ops = {
.get_drvinfo = crete_vnic_get_drvinfo,
.get_link = crete_vnic_get_link,
.get_ringparam = crete_vnic_get_ringparam,
.get_strings = crete_vnic_get_strings,
.get_sset_count = crete_vnic_get_sset_count,
.get_ethtool_stats = crete_vnic_get_ethtool_stats,
.get_channels = crete_vnic_get_channels,
.set_channels = crete_vnic_set_channels,
.flash_device = crete_vnic_flash_device,
.get_link_ksettings = crete_vnic_get_link_ksettings,
.set_link_ksettings = crete_vnic_set_link_ksettings,
.get_fecparam = crete_vnic_get_fecparam,
.set_fecparam = crete_vnic_set_fecparam,
};
void crete_set_ethtool_ops(struct net_device *netdev)
{
netdev->ethtool_ops = &crete_vnic_ethtool_ops;
}