Initial commit

This commit is contained in:
domenico
2025-06-24 13:14:22 +02:00
commit 4002f145fc
9002 changed files with 1731834 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
#
# Copyright (C) 2008-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=nss-ifb
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define KernelPackage/nss-ifb
SECTION:=kernel
CATEGORY:=Kernel modules
SUBMENU:=Network Devices
TITLE:=NSS IFB Interface
DEPENDS:=+kmod-qca-nss-drv @LINUX_5_4
FILES:=$(PKG_BUILD_DIR)/nss-ifb.ko
KCONFIG:=
endef
define KernelPackage/nss-ifb/description
Kernel module to register a NSS aware IFB interface.
endef
EXTRA_KCONFIG:= \
CONFIG_NET_CLS=y
EXTRA_CFLAGS:= \
-I$(STAGING_DIR)/usr/include/qca-nss-drv
MAKE_OPTS:= \
$(KERNEL_MAKE_FLAGS) \
M="$(PKG_BUILD_DIR)" \
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
$(EXTRA_KCONFIG)
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \
$(MAKE_OPTS) \
modules
endef
$(eval $(call KernelPackage,nss-ifb))

View File

@@ -0,0 +1,45 @@
NSS Physical Interface Ingress Driver
=====================================
This driver redirect NSS physical interface (namely GMACs) ingress traffic to itself
and sends it back to the Linux network stack (as the source GMACs packets) as it's
egress traffic.
This allows the NSS QDISC drivers to manage the egress traffic of this driver's
NSS virtual interface.
This driver will create a single network interface named 'nssifb'. The default
source interface is defined as 'eth0'. It can be changed using the following module
parameter path:
/sys/module/nss-ifb/parameter/nss_src_dev
To change the source NSS physical interface to 'eth1', use the following command:
printf eth1 > /sys/module/nss-ifb/parameter/nss_src_dev
You need to change the source interface first before bringing up the 'nssifb'
interface. Changing it after the interface is up will have no effect. You need
to bring down the interface and bring it back up to have the changes take effect.
CPU load imposed on the Krait CPUs appears negligible with this driver intercepting
the physical interface's ingress traffic. Full line speed of the GMAC interface
could still be achieved.
The commands below shows an example to shape ingress traffic to 500 Mbps and egress
to 200 Mbps for the 'eth0' interface.
# Load the module if it's not loaded
modprobe nss-ifb
# Bring up the nssifb interface to active ingress redirect
ip link set up nssifb
# Shape ingress traffic to 500 Mbit with chained NSSFQ_CODEL
tc qdisc add dev nssifb root handle 1: nsstbl rate 500Mbit burst 1Mb
tc qdisc add dev nssifb parent 1: handle 10: nssfq_codel limit 10240 flows 1024 quantum 1514 target 5ms interval 100ms set_default
# Shape egress traffic to 200 Mbit with chained NSSFQ_CODEL
tc qdisc add dev eth0 root handle 1: nsstbl rate 200Mbit burst 1Mb
tc qdisc add dev eth0 parent 1: handle 10: nssfq_codel limit 10240 flows 1024 quantum 1514 target 5ms interval 100ms set_default

View File

@@ -0,0 +1,3 @@
obj-m += nss-ifb.o
nss-ifb-objs := nss_ifb.o

View File

@@ -0,0 +1,304 @@
// 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");