[PATCH net v6 2/4] nsim: Add support for VLAN filters

Cosmin Ratiu
2026-03-30T13:02:27Z

Add support for storing the list of VLANs in nsim devices, together with
ops for adding/removing them and a debug file to show them.

This will be used in upcoming tests.

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
drivers/net/netdevsim/netdev.c | 65 ++++++++++++++++++++++++++++++-
drivers/net/netdevsim/netdevsim.h | 8 ++++
2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 3645ebde049a..19b0ff183c45 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -605,6 +605,36 @@ static int nsim_stop(struct net_device *dev)
return 0;
}

+static int nsim_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (vid >= VLAN_N_VID)
+ return -EINVAL;
+
+ if (proto == htons(ETH_P_8021Q))
+ WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.ctag));
+ else if (proto == htons(ETH_P_8021AD))
+ WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.stag));
+
+ return 0;
+}
+
+static int nsim_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (vid >= VLAN_N_VID)
+ return -EINVAL;
+
+ if (proto == htons(ETH_P_8021Q))
+ WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.ctag));
+ else if (proto == htons(ETH_P_8021AD))
+ WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.stag));
+
+ return 0;
+}
+
static int nsim_shaper_set(struct net_shaper_binding *binding,
const struct net_shaper *shaper,
struct netlink_ext_ack *extack)
@@ -662,6 +692,8 @@ static const struct net_device_ops nsim_netdev_ops = {
.ndo_bpf = nsim_bpf,
.ndo_open = nsim_open,
.ndo_stop = nsim_stop,
+ .ndo_vlan_rx_add_vid = nsim_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nsim_vlan_rx_kill_vid,
.net_shaper_ops = &nsim_shaper_ops,
};

@@ -673,6 +705,8 @@ static const struct net_device_ops nsim_vf_netdev_ops = {
.ndo_change_mtu = nsim_change_mtu,
.ndo_setup_tc = nsim_setup_tc,
.ndo_set_features = nsim_set_features,
+ .ndo_vlan_rx_add_vid = nsim_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nsim_vlan_rx_kill_vid,
};

/* We don't have true per-queue stats, yet, so do some random fakery here.
@@ -970,6 +1004,20 @@ static const struct file_operations nsim_pp_hold_fops = {
.owner = THIS_MODULE,
};

+static int nsim_vlan_show(struct seq_file *s, void *data)
+{
+ struct netdevsim *ns = s->private;
+ int vid;
+
+ for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+ seq_printf(s, "ctag %d\n", vid);
+ for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+ seq_printf(s, "stag %d\n", vid);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(nsim_vlan);
+
static void nsim_setup(struct net_device *dev)
{
ether_setup(dev);
@@ -982,14 +1030,18 @@ static void nsim_setup(struct net_device *dev)
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
- NETIF_F_TSO;
+ NETIF_F_TSO |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
dev->hw_features |= NETIF_F_HW_TC |
NETIF_F_SG |
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
NETIF_F_TSO |
- NETIF_F_LOOPBACK;
+ NETIF_F_LOOPBACK |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
dev->max_mtu = ETH_MAX_MTU;
dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD;
@@ -1156,6 +1208,8 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev,
ns->qr_dfs = debugfs_create_file("queue_reset", 0200,
nsim_dev_port->ddir, ns,
&nsim_qreset_fops);
+ ns->vlan_dfs = debugfs_create_file("vlan", 0400, nsim_dev_port->ddir,
+ ns, &nsim_vlan_fops);
return ns;

err_free_netdev:
@@ -1167,7 +1221,9 @@ void nsim_destroy(struct netdevsim *ns)
{
struct net_device *dev = ns->netdev;
struct netdevsim *peer;
+ u16 vid;

+ debugfs_remove(ns->vlan_dfs);
debugfs_remove(ns->qr_dfs);
debugfs_remove(ns->pp_dfs);

@@ -1193,6 +1249,11 @@ void nsim_destroy(struct netdevsim *ns)
if (nsim_dev_port_is_pf(ns->nsim_dev_port))
nsim_exit_netdevsim(ns);

+ for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+ WARN_ON_ONCE(1);
+ for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+ WARN_ON_ONCE(1);
+
/* Put this intentionally late to exercise the orphaning path */
if (ns->page) {
page_pool_put_full_page(pp_page_to_nmdesc(ns->page)->pp,
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index f767fc8a7505..f844c27ca78b 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -18,6 +18,7 @@
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/kernel.h>
+#include <linux/if_vlan.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/ptp_mock.h>
@@ -75,6 +76,11 @@ struct nsim_macsec {
u8 nsim_secy_count;
};

+struct nsim_vlan {
+ DECLARE_BITMAP(ctag, VLAN_N_VID);
+ DECLARE_BITMAP(stag, VLAN_N_VID);
+};
+
struct nsim_ethtool_pauseparam {
bool rx;
bool tx;
@@ -135,6 +141,7 @@ struct netdevsim {
bool bpf_map_accept;
struct nsim_ipsec ipsec;
struct nsim_macsec macsec;
+ struct nsim_vlan vlan;
struct {
u32 inject_error;
u32 __ports[2][NSIM_UDP_TUNNEL_N_PORTS];
@@ -146,6 +153,7 @@ struct netdevsim {
struct page *page;
struct dentry *pp_dfs;
struct dentry *qr_dfs;
+ struct dentry *vlan_dfs;

struct nsim_ethtool ethtool;
struct netdevsim __rcu *peer;
--
2.53.0