Factor out sharable netdevsim sub-object and use IFLA_LINK to link
netdevsims together at creation time.  Sharable object will have
its own DebugFS directory.

Signed-off-by: Jakub Kicinski <jakub.kicin...@netronome.com>
Reviewed-by: Quentin Monnet <quentin.mon...@netronome.com>
---
 drivers/net/netdevsim/netdev.c    | 87 ++++++++++++++++++++++++++++---
 drivers/net/netdevsim/netdevsim.h | 10 +++-
 2 files changed, 90 insertions(+), 7 deletions(-)

diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 9125637ef5d8..2d244551298b 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -152,8 +152,8 @@ nsim_port_attr_get(struct net_device *dev, struct 
switchdev_attr *attr)
 
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-               attr->u.ppid.id_len = sizeof(ns->switch_id);
-               memcpy(&attr->u.ppid.id, &ns->switch_id,
+               attr->u.ppid.id_len = sizeof(ns->sdev->switch_id);
+               memcpy(&attr->u.ppid.id, &ns->sdev->switch_id,
                       attr->u.ppid.id_len);
                return 0;
        default:
@@ -167,19 +167,41 @@ static const struct switchdev_ops nsim_switchdev_ops = {
 
 static int nsim_init(struct net_device *dev)
 {
+       char sdev_ddir_name[10], sdev_link_name[32];
        struct netdevsim *ns = netdev_priv(dev);
        int err;
 
        ns->netdev = dev;
-       ns->switch_id = nsim_dev_id;
-
        ns->ddir = debugfs_create_dir(netdev_name(dev), nsim_ddir);
        if (IS_ERR_OR_NULL(ns->ddir))
                return -ENOMEM;
 
+       if (!ns->sdev) {
+               ns->sdev = kzalloc(sizeof(*ns->sdev), GFP_KERNEL);
+               if (!ns->sdev) {
+                       err = -ENOMEM;
+                       goto err_debugfs_destroy;
+               }
+               ns->sdev->refcnt = 1;
+               ns->sdev->switch_id = nsim_dev_id;
+               sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id);
+               ns->sdev->ddir = debugfs_create_dir(sdev_ddir_name,
+                                                   nsim_sdev_ddir);
+               if (IS_ERR_OR_NULL(ns->sdev->ddir)) {
+                       err = PTR_ERR_OR_ZERO(ns->sdev->ddir) ?: -EINVAL;
+                       goto err_sdev_free;
+               }
+       } else {
+               sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id);
+               ns->sdev->refcnt++;
+       }
+
+       sprintf(sdev_link_name, "../../" DRV_NAME "_sdev/%s", sdev_ddir_name);
+       debugfs_create_symlink("sdev", ns->ddir, sdev_link_name);
+
        err = nsim_bpf_init(ns);
        if (err)
-               goto err_debugfs_destroy;
+               goto err_sdev_destroy;
 
        ns->dev.id = nsim_dev_id++;
        ns->dev.bus = &nsim_bus;
@@ -203,6 +225,12 @@ static int nsim_init(struct net_device *dev)
        device_unregister(&ns->dev);
 err_bpf_uninit:
        nsim_bpf_uninit(ns);
+err_sdev_destroy:
+       if (!--ns->sdev->refcnt) {
+               debugfs_remove_recursive(ns->sdev->ddir);
+err_sdev_free:
+               kfree(ns->sdev);
+       }
 err_debugfs_destroy:
        debugfs_remove_recursive(ns->ddir);
        return err;
@@ -216,6 +244,10 @@ static void nsim_uninit(struct net_device *dev)
        nsim_devlink_teardown(ns);
        debugfs_remove_recursive(ns->ddir);
        nsim_bpf_uninit(ns);
+       if (!--ns->sdev->refcnt) {
+               debugfs_remove_recursive(ns->sdev->ddir);
+               kfree(ns->sdev);
+       }
 }
 
 static void nsim_free(struct net_device *dev)
@@ -494,14 +526,48 @@ static int nsim_validate(struct nlattr *tb[], struct 
nlattr *data[],
        return 0;
 }
 
+static int nsim_newlink(struct net *src_net, struct net_device *dev,
+                       struct nlattr *tb[], struct nlattr *data[],
+                       struct netlink_ext_ack *extack)
+{
+       struct netdevsim *ns = netdev_priv(dev);
+
+       if (tb[IFLA_LINK]) {
+               struct net_device *joindev;
+               struct netdevsim *joinns;
+
+               joindev = __dev_get_by_index(src_net,
+                                            nla_get_u32(tb[IFLA_LINK]));
+               if (!joindev)
+                       return -ENODEV;
+               if (joindev->netdev_ops != &nsim_netdev_ops)
+                       return -EINVAL;
+
+               joinns = netdev_priv(joindev);
+               if (!joinns->sdev || !joinns->sdev->refcnt)
+                       return -EINVAL;
+               ns->sdev = joinns->sdev;
+       }
+
+       return register_netdevice(dev);
+}
+
+static void nsim_dellink(struct net_device *dev, struct list_head *head)
+{
+       unregister_netdevice_queue(dev, head);
+}
+
 static struct rtnl_link_ops nsim_link_ops __read_mostly = {
        .kind           = DRV_NAME,
        .priv_size      = sizeof(struct netdevsim),
        .setup          = nsim_setup,
        .validate       = nsim_validate,
+       .newlink        = nsim_newlink,
+       .dellink        = nsim_dellink,
 };
 
 struct dentry *nsim_ddir;
+struct dentry *nsim_sdev_ddir;
 
 static int __init nsim_module_init(void)
 {
@@ -511,9 +577,15 @@ static int __init nsim_module_init(void)
        if (IS_ERR_OR_NULL(nsim_ddir))
                return -ENOMEM;
 
+       nsim_sdev_ddir = debugfs_create_dir(DRV_NAME "_sdev", NULL);
+       if (IS_ERR_OR_NULL(nsim_sdev_ddir)) {
+               err = -ENOMEM;
+               goto err_debugfs_destroy;
+       }
+
        err = bus_register(&nsim_bus);
        if (err)
-               goto err_debugfs_destroy;
+               goto err_sdir_destroy;
 
        err = nsim_devlink_init();
        if (err)
@@ -529,6 +601,8 @@ static int __init nsim_module_init(void)
        nsim_devlink_exit();
 err_unreg_bus:
        bus_unregister(&nsim_bus);
+err_sdir_destroy:
+       debugfs_remove_recursive(nsim_sdev_ddir);
 err_debugfs_destroy:
        debugfs_remove_recursive(nsim_ddir);
        return err;
@@ -539,6 +613,7 @@ static void __exit nsim_module_exit(void)
        rtnl_link_unregister(&nsim_link_ops);
        nsim_devlink_exit();
        bus_unregister(&nsim_bus);
+       debugfs_remove_recursive(nsim_sdev_ddir);
        debugfs_remove_recursive(nsim_ddir);
 }
 
diff --git a/drivers/net/netdevsim/netdevsim.h 
b/drivers/net/netdevsim/netdevsim.h
index e2f232325259..8743ce74d2d9 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -30,6 +30,13 @@ struct bpf_prog;
 struct dentry;
 struct nsim_vf_config;
 
+struct netdevsim_shared_dev {
+       unsigned int refcnt;
+       u32 switch_id;
+
+       struct dentry *ddir;
+};
+
 #define NSIM_IPSEC_MAX_SA_COUNT                33
 #define NSIM_IPSEC_VALID               BIT(31)
 
@@ -59,7 +66,7 @@ struct netdevsim {
        struct u64_stats_sync syncp;
 
        struct device dev;
-       u32 switch_id;
+       struct netdevsim_shared_dev *sdev;
 
        struct dentry *ddir;
 
@@ -93,6 +100,7 @@ struct netdevsim {
 };
 
 extern struct dentry *nsim_ddir;
+extern struct dentry *nsim_sdev_ddir;
 
 #ifdef CONFIG_BPF_SYSCALL
 int nsim_bpf_init(struct netdevsim *ns);
-- 
2.17.1

Reply via email to