1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter-feeds.git synced 2025-03-09 15:40:03 +00:00
openmptcprouter-feeds/qaa/nss-ifb/src/nss_ifb.c
2022-09-10 05:56:57 +08:00

304 lines
7.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This driver is adapted from the Linux /drivers/net/ifb.c file.
*
* Redirect QCA NSS physical interface ingress traffic to this driver's
* virtual interface. This will allow ingress traffic shaping using the
* QCA NSS shaper.
*/
#include <nss_api_if.h>
#define TX_Q_LIMIT 32
struct nss_ifb_dev_private {
struct nss_virt_if_handle *nssctx;
struct net_device *nss_src_dev;
uint32_t nss_src_if_num;
char nss_src_dev_name[32];
};
char nss_dev_name_array[32] = "eth0";
char *nss_dev_name = nss_dev_name_array;
module_param(nss_dev_name, charp, 0644);
MODULE_PARM_DESC(nss_dev_name, "NSS physical interface source device name");
/*
* Virtual interface egress packet callback.
*
* We send it back to the Linux network stack.
*/
static void nss_ifb_data_cb(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi)
{
struct nss_ifb_dev_private *dp = netdev_priv(netdev);
skb->protocol = eth_type_trans(skb, dp->nss_src_dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
napi_gro_receive(napi, skb);
}
/*
* Virtual interface ingress packet callback.
*
* We just send it back to the NSS firmware to let the shaper work on it.
*/
static void nss_ifb_xmit_cb(struct net_device *netdev, struct sk_buff *skb)
{
struct nss_ifb_dev_private *dp = netdev_priv(netdev);
int ret;
ret = nss_virt_if_tx_buf(dp->nssctx, skb);
if (unlikely(ret)) {
pr_warn("Failed [%d] to send skb [len: %d, protocol: 0x%X] to NSS!\n",
ret, skb->len, ntohs(skb->protocol));
}
}
static void nss_ifb_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
}
static int nss_ifb_dev_init(struct net_device *dev)
{
struct nss_ifb_dev_private *dp = netdev_priv(dev);
dp->nssctx = nss_virt_if_create_sync_nexthop(dev, NSS_ETH_RX_INTERFACE, NSS_ETH_RX_INTERFACE);
if (!dp->nssctx) {
dp->nssctx = NULL;
pr_warn("Could not create a NSS virtual interface for dev [%s]\n",
dev->name);
return -ENODEV;
}
pr_info("Created a NSS virtual interface for dev [%s]\n", dev->name);
nss_virt_if_register(dp->nssctx, nss_ifb_data_cb, dev);
pr_info("NSS IFB data callback registered\n");
nss_virt_if_xmit_callback_register(dp->nssctx, nss_ifb_xmit_cb);
pr_info("NSS IFB transmit callback registered\n");
return 0;
}
static void nss_ifb_dev_uninit(struct net_device *dev)
{
struct nss_ifb_dev_private *dp = netdev_priv(dev);
int ret;
nss_virt_if_xmit_callback_unregister(dp->nssctx);
pr_info("NSS IFB transmit callback unregistered\n");
ret = nss_virt_if_destroy_sync(dp->nssctx);
if (ret == NSS_TX_SUCCESS) {
pr_info("NSS virtual interface destroyed for dev [%s]\n", dev->name);
}
else {
pr_warn("Unable to destroy NSS virtual interface for dev [%s], error[%d]\n",
dev->name, ret);
}
dp->nssctx = NULL;
}
static netdev_tx_t nss_ifb_xmit(struct sk_buff *skb, struct net_device *dev)
{
return NETDEV_TX_OK;
}
static int nss_ifb_close(struct net_device *dev)
{
struct nss_ifb_dev_private *dp = netdev_priv(dev);
struct nss_ctx_instance *nss_ctx;
struct net_device *src_dev;
uint32_t src_if_num;
int ret;
nss_ctx = dp->nssctx->nss_ctx;
src_dev = dp->nss_src_dev;
src_if_num = dp->nss_src_if_num;
ret = nss_phys_if_set_nexthop(nss_ctx, src_if_num, NSS_ETH_RX_INTERFACE);
if (ret != NSS_TX_SUCCESS) {
pr_warn("%p: Failed to reset next hop for net device [%s].\n",
nss_ctx, src_dev->name);
}
else {
pr_info("%p: Reset nexthop successful for net device [%s].\n",
nss_ctx, src_dev->name);
}
dev_put(src_dev);
dp->nss_src_dev = NULL;
dp->nss_src_if_num = -1;
return 0;
}
static int nss_ifb_open(struct net_device *dev)
{
struct nss_ifb_dev_private *dp = netdev_priv(dev);
struct net_device *src_dev;
uint32_t src_if_num;
uint32_t nh_if_num;
nss_tx_status_t nss_tx_status;
struct nss_ctx_instance *nss_ctx;
nss_ctx = dp->nssctx->nss_ctx;
nh_if_num = dp->nssctx->if_num_n2h;
strcpy(dp->nss_src_dev_name, nss_dev_name);
src_dev = dev_get_by_name(&init_net, dp->nss_src_dev_name);
if (!src_dev) {
pr_warn("%p: Cannot find the net device [%s]\n",
nss_ctx, dp->nss_src_dev_name);
return -ENODEV;
}
pr_info("%p: Found net device [%s]\n", nss_ctx, dp->nss_src_dev_name);
src_if_num = nss_cmn_get_interface_number_by_dev(src_dev);
if (src_if_num < 0) {
pr_warn("%p: Invalid interface number:%d\n", nss_ctx, src_if_num);
dev_put(src_dev);
return -ENODEV;
}
pr_info("%p: Net device [%s] has NSS intf_num [%d]\n",
nss_ctx, dp->nss_src_dev_name, src_if_num);
nss_tx_status = nss_phys_if_set_nexthop(nss_ctx, src_if_num, nh_if_num);
if (nss_tx_status != NSS_TX_SUCCESS) {
pr_warn("%p: Sending message failed, cannot change nexthop for [%s]\n",
nss_ctx, dp->nss_src_dev_name);
}
else {
pr_info("Nexthop successfully set for [%s] to [%s]\n",
dp->nss_src_dev_name, dev->name);
}
dp->nss_src_dev = src_dev;
dp->nss_src_if_num = src_if_num;
return 0;
}
static const struct net_device_ops nss_ifb_netdev_ops = {
.ndo_open = nss_ifb_open,
.ndo_stop = nss_ifb_close,
.ndo_get_stats64 = nss_ifb_stats64,
.ndo_start_xmit = nss_ifb_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_init = nss_ifb_dev_init,
.ndo_uninit = nss_ifb_dev_uninit,
};
#define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \
NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \
NETIF_F_GSO_ENCAP_ALL | \
NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX | \
NETIF_F_HW_VLAN_STAG_TX)
static void nss_ifb_dev_free(struct net_device *dev)
{
}
static void nss_ifb_setup(struct net_device *dev)
{
/* Initialize the device structure. */
dev->netdev_ops = &nss_ifb_netdev_ops;
/* Fill in device structure with ethernet-generic values. */
ether_setup(dev);
dev->tx_queue_len = TX_Q_LIMIT;
dev->features |= IFB_FEATURES;
dev->hw_features |= dev->features;
dev->hw_enc_features |= dev->features;
dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
netif_keep_dst(dev);
eth_hw_addr_random(dev);
dev->needs_free_netdev = true;
dev->priv_destructor = nss_ifb_dev_free;
dev->min_mtu = 0;
dev->max_mtu = 0;
}
static int nss_ifb_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
if (tb[IFLA_ADDRESS]) {
if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
return -EINVAL;
if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
return -EADDRNOTAVAIL;
}
return 0;
}
static struct rtnl_link_ops nss_ifb_link_ops __read_mostly = {
.kind = "nss_ifb",
.priv_size = sizeof(struct nss_ifb_dev_private),
.setup = nss_ifb_setup,
.validate = nss_ifb_validate,
};
static int __init nss_ifb_init_module(void)
{
struct net_device *dev;
int err;
down_write(&pernet_ops_rwsem);
rtnl_lock();
err = __rtnl_link_register(&nss_ifb_link_ops);
if (err < 0)
goto out;
dev = alloc_netdev(sizeof(struct nss_ifb_dev_private), "nssifb",
NET_NAME_UNKNOWN, nss_ifb_setup);
if (dev) {
dev->rtnl_link_ops = &nss_ifb_link_ops;
err = register_netdevice(dev);
}
else {
err = -ENOMEM;
}
if (err)
__rtnl_link_unregister(&nss_ifb_link_ops);
out:
rtnl_unlock();
up_write(&pernet_ops_rwsem);
if (!err)
pr_info("NSS IFB module loaded.\n");
else
pr_warn("Failed to load NSS IFB module.\n");
return err;
}
static void __exit nss_ifb_cleanup_module(void)
{
rtnl_link_unregister(&nss_ifb_link_ops);
pr_info("NSS IFB module unloaded.\n");
}
module_init(nss_ifb_init_module);
module_exit(nss_ifb_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_ALIAS_RTNL_LINK("nss_ifb");