blob: 7805fa8403836e7c4b11182452c0bc0840b9073a [file] [log] [blame]
Jakub Kicinski83c9e132017-12-01 15:08:58 -08001/*
2 * Copyright (C) 2017 Netronome Systems, Inc.
3 *
4 * This software is licensed under the GNU General License Version 2,
5 * June 1991 as shown in the file COPYING in the top-level directory of this
6 * source tree.
7 *
8 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
9 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
10 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
12 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
13 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
14 */
15
Jakub Kicinski31d3ad82017-12-01 15:08:59 -080016#include <linux/debugfs.h>
Jakub Kicinski83c9e132017-12-01 15:08:58 -080017#include <linux/etherdevice.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/netdevice.h>
21#include <linux/slab.h>
22#include <net/netlink.h>
Jakub Kicinski31d3ad82017-12-01 15:08:59 -080023#include <net/pkt_cls.h>
Jakub Kicinski83c9e132017-12-01 15:08:58 -080024#include <net/rtnetlink.h>
25
26#include "netdevsim.h"
27
Jiri Pirkoaf9095f2019-04-12 14:49:26 +020028static u32 nsim_dev_id;
29
Jakub Kicinski79579222017-12-01 15:09:01 -080030struct nsim_vf_config {
31 int link_state;
32 u16 min_tx_rate;
33 u16 max_tx_rate;
34 u16 vlan;
35 __be16 vlan_proto;
36 u16 qos;
37 u8 vf_mac[ETH_ALEN];
38 bool spoofchk_enabled;
39 bool trusted;
40 bool rss_query_enabled;
41};
42
Jakub Kicinskif61b6db2018-07-26 14:25:26 -070043static struct dentry *nsim_ddir;
Jakub Kicinskif61b6db2018-07-26 14:25:26 -070044
Jakub Kicinski79579222017-12-01 15:09:01 -080045static int nsim_num_vf(struct device *dev)
46{
47 struct netdevsim *ns = to_nsim(dev);
48
49 return ns->num_vfs;
50}
51
52static struct bus_type nsim_bus = {
53 .name = DRV_NAME,
54 .dev_name = DRV_NAME,
55 .num_vf = nsim_num_vf,
56};
57
58static int nsim_vfs_enable(struct netdevsim *ns, unsigned int num_vfs)
59{
60 ns->vfconfigs = kcalloc(num_vfs, sizeof(struct nsim_vf_config),
61 GFP_KERNEL);
62 if (!ns->vfconfigs)
63 return -ENOMEM;
64 ns->num_vfs = num_vfs;
65
66 return 0;
67}
68
69static void nsim_vfs_disable(struct netdevsim *ns)
70{
71 kfree(ns->vfconfigs);
72 ns->vfconfigs = NULL;
73 ns->num_vfs = 0;
74}
75
76static ssize_t
77nsim_numvfs_store(struct device *dev, struct device_attribute *attr,
78 const char *buf, size_t count)
79{
80 struct netdevsim *ns = to_nsim(dev);
81 unsigned int num_vfs;
82 int ret;
83
84 ret = kstrtouint(buf, 0, &num_vfs);
85 if (ret)
86 return ret;
87
88 rtnl_lock();
89 if (ns->num_vfs == num_vfs)
90 goto exit_good;
91 if (ns->num_vfs && num_vfs) {
92 ret = -EBUSY;
93 goto exit_unlock;
94 }
95
96 if (num_vfs) {
97 ret = nsim_vfs_enable(ns, num_vfs);
98 if (ret)
99 goto exit_unlock;
100 } else {
101 nsim_vfs_disable(ns);
102 }
103exit_good:
104 ret = count;
105exit_unlock:
106 rtnl_unlock();
107
108 return ret;
109}
110
111static ssize_t
112nsim_numvfs_show(struct device *dev, struct device_attribute *attr, char *buf)
113{
114 struct netdevsim *ns = to_nsim(dev);
115
116 return sprintf(buf, "%u\n", ns->num_vfs);
117}
118
119static struct device_attribute nsim_numvfs_attr =
120 __ATTR(sriov_numvfs, 0664, nsim_numvfs_show, nsim_numvfs_store);
121
122static struct attribute *nsim_dev_attrs[] = {
123 &nsim_numvfs_attr.attr,
124 NULL,
125};
126
127static const struct attribute_group nsim_dev_attr_group = {
128 .attrs = nsim_dev_attrs,
129};
130
131static const struct attribute_group *nsim_dev_attr_groups[] = {
132 &nsim_dev_attr_group,
133 NULL,
134};
135
136static void nsim_dev_release(struct device *dev)
137{
138 struct netdevsim *ns = to_nsim(dev);
139
140 nsim_vfs_disable(ns);
Jakub Kicinski79579222017-12-01 15:09:01 -0800141}
142
Jakub Kicinskifd5ebbc2017-12-19 12:10:42 -0800143static struct device_type nsim_dev_type = {
Jakub Kicinski79579222017-12-01 15:09:01 -0800144 .groups = nsim_dev_attr_groups,
145 .release = nsim_dev_release,
146};
147
Florian Fainellie58df562019-02-06 09:45:43 -0800148static int nsim_get_port_parent_id(struct net_device *dev,
149 struct netdev_phys_item_id *ppid)
Jakub Kicinski5f076552018-07-17 10:53:19 -0700150{
151 struct netdevsim *ns = netdev_priv(dev);
152
Florian Fainellie58df562019-02-06 09:45:43 -0800153 ppid->id_len = sizeof(ns->sdev->switch_id);
154 memcpy(&ppid->id, &ns->sdev->switch_id, ppid->id_len);
155 return 0;
Jakub Kicinski5f076552018-07-17 10:53:19 -0700156}
157
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800158static int nsim_init(struct net_device *dev)
159{
160 struct netdevsim *ns = netdev_priv(dev);
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200161 char sdev_link_name[32];
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800162 int err;
163
164 ns->netdev = dev;
165 ns->ddir = debugfs_create_dir(netdev_name(dev), nsim_ddir);
Prashant Bhole9ee19422017-12-20 12:18:57 +0900166 if (IS_ERR_OR_NULL(ns->ddir))
167 return -ENOMEM;
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800168
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200169 sprintf(sdev_link_name, "../../" DRV_NAME "_sdev/%u",
170 ns->sdev->switch_id);
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700171 debugfs_create_symlink("sdev", ns->ddir, sdev_link_name);
172
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800173 err = nsim_bpf_init(ns);
174 if (err)
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200175 goto err_debugfs_destroy;
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800176
Jakub Kicinski79579222017-12-01 15:09:01 -0800177 ns->dev.id = nsim_dev_id++;
178 ns->dev.bus = &nsim_bus;
179 ns->dev.type = &nsim_dev_type;
180 err = device_register(&ns->dev);
181 if (err)
182 goto err_bpf_uninit;
183
184 SET_NETDEV_DEV(dev, &ns->dev);
185
David Ahernef817102018-03-30 09:28:51 -0700186 err = nsim_devlink_setup(ns);
187 if (err)
188 goto err_unreg_dev;
David Ahern37923ed2018-03-27 18:22:00 -0700189
Shannon Nelson76993532018-06-26 10:07:54 -0700190 nsim_ipsec_init(ns);
191
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800192 return 0;
193
David Ahernef817102018-03-30 09:28:51 -0700194err_unreg_dev:
195 device_unregister(&ns->dev);
Jakub Kicinski79579222017-12-01 15:09:01 -0800196err_bpf_uninit:
197 nsim_bpf_uninit(ns);
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800198err_debugfs_destroy:
199 debugfs_remove_recursive(ns->ddir);
200 return err;
201}
202
203static void nsim_uninit(struct net_device *dev)
204{
205 struct netdevsim *ns = netdev_priv(dev);
206
Shannon Nelson76993532018-06-26 10:07:54 -0700207 nsim_ipsec_teardown(ns);
David Ahern37923ed2018-03-27 18:22:00 -0700208 nsim_devlink_teardown(ns);
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800209 debugfs_remove_recursive(ns->ddir);
210 nsim_bpf_uninit(ns);
211}
212
Jakub Kicinski79579222017-12-01 15:09:01 -0800213static void nsim_free(struct net_device *dev)
214{
215 struct netdevsim *ns = netdev_priv(dev);
216
217 device_unregister(&ns->dev);
218 /* netdev and vf state will be freed out of device_release() */
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200219 nsim_sdev_put(ns->sdev);
Jakub Kicinski79579222017-12-01 15:09:01 -0800220}
221
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800222static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
223{
224 struct netdevsim *ns = netdev_priv(dev);
225
Shannon Nelson76993532018-06-26 10:07:54 -0700226 if (!nsim_ipsec_tx(ns, skb))
227 goto out;
228
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800229 u64_stats_update_begin(&ns->syncp);
230 ns->tx_packets++;
231 ns->tx_bytes += skb->len;
232 u64_stats_update_end(&ns->syncp);
233
Shannon Nelson76993532018-06-26 10:07:54 -0700234out:
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800235 dev_kfree_skb(skb);
236
237 return NETDEV_TX_OK;
238}
239
240static void nsim_set_rx_mode(struct net_device *dev)
241{
242}
243
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800244static int nsim_change_mtu(struct net_device *dev, int new_mtu)
245{
246 struct netdevsim *ns = netdev_priv(dev);
247
Jakub Kicinski799e1732018-07-11 20:36:42 -0700248 if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU)
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800249 return -EBUSY;
250
251 dev->mtu = new_mtu;
252
253 return 0;
254}
255
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800256static void
257nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
258{
259 struct netdevsim *ns = netdev_priv(dev);
260 unsigned int start;
261
262 do {
263 start = u64_stats_fetch_begin(&ns->syncp);
264 stats->tx_bytes = ns->tx_bytes;
265 stats->tx_packets = ns->tx_packets;
266 } while (u64_stats_fetch_retry(&ns->syncp, start));
267}
268
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800269static int
270nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv)
271{
272 return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv);
273}
274
275static int
276nsim_setup_tc_block(struct net_device *dev, struct tc_block_offload *f)
277{
278 struct netdevsim *ns = netdev_priv(dev);
279
280 if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
281 return -EOPNOTSUPP;
282
283 switch (f->command) {
284 case TC_BLOCK_BIND:
285 return tcf_block_cb_register(f->block, nsim_setup_tc_block_cb,
John Hurley60513bd2018-06-25 14:30:04 -0700286 ns, ns, f->extack);
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800287 case TC_BLOCK_UNBIND:
288 tcf_block_cb_unregister(f->block, nsim_setup_tc_block_cb, ns);
289 return 0;
290 default:
291 return -EOPNOTSUPP;
292 }
293}
294
Jakub Kicinski79579222017-12-01 15:09:01 -0800295static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
296{
297 struct netdevsim *ns = netdev_priv(dev);
298
299 /* Only refuse multicast addresses, zero address can mean unset/any. */
300 if (vf >= ns->num_vfs || is_multicast_ether_addr(mac))
301 return -EINVAL;
302 memcpy(ns->vfconfigs[vf].vf_mac, mac, ETH_ALEN);
303
304 return 0;
305}
306
307static int nsim_set_vf_vlan(struct net_device *dev, int vf,
308 u16 vlan, u8 qos, __be16 vlan_proto)
309{
310 struct netdevsim *ns = netdev_priv(dev);
311
312 if (vf >= ns->num_vfs || vlan > 4095 || qos > 7)
313 return -EINVAL;
314
315 ns->vfconfigs[vf].vlan = vlan;
316 ns->vfconfigs[vf].qos = qos;
317 ns->vfconfigs[vf].vlan_proto = vlan_proto;
318
319 return 0;
320}
321
322static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max)
323{
324 struct netdevsim *ns = netdev_priv(dev);
325
326 if (vf >= ns->num_vfs)
327 return -EINVAL;
328
329 ns->vfconfigs[vf].min_tx_rate = min;
330 ns->vfconfigs[vf].max_tx_rate = max;
331
332 return 0;
333}
334
335static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val)
336{
337 struct netdevsim *ns = netdev_priv(dev);
338
339 if (vf >= ns->num_vfs)
340 return -EINVAL;
341 ns->vfconfigs[vf].spoofchk_enabled = val;
342
343 return 0;
344}
345
346static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val)
347{
348 struct netdevsim *ns = netdev_priv(dev);
349
350 if (vf >= ns->num_vfs)
351 return -EINVAL;
352 ns->vfconfigs[vf].rss_query_enabled = val;
353
354 return 0;
355}
356
357static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val)
358{
359 struct netdevsim *ns = netdev_priv(dev);
360
361 if (vf >= ns->num_vfs)
362 return -EINVAL;
363 ns->vfconfigs[vf].trusted = val;
364
365 return 0;
366}
367
368static int
369nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi)
370{
371 struct netdevsim *ns = netdev_priv(dev);
372
373 if (vf >= ns->num_vfs)
374 return -EINVAL;
375
376 ivi->vf = vf;
377 ivi->linkstate = ns->vfconfigs[vf].link_state;
378 ivi->min_tx_rate = ns->vfconfigs[vf].min_tx_rate;
379 ivi->max_tx_rate = ns->vfconfigs[vf].max_tx_rate;
380 ivi->vlan = ns->vfconfigs[vf].vlan;
381 ivi->vlan_proto = ns->vfconfigs[vf].vlan_proto;
382 ivi->qos = ns->vfconfigs[vf].qos;
383 memcpy(&ivi->mac, ns->vfconfigs[vf].vf_mac, ETH_ALEN);
384 ivi->spoofchk = ns->vfconfigs[vf].spoofchk_enabled;
385 ivi->trusted = ns->vfconfigs[vf].trusted;
386 ivi->rss_query_en = ns->vfconfigs[vf].rss_query_enabled;
387
388 return 0;
389}
390
391static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state)
392{
393 struct netdevsim *ns = netdev_priv(dev);
394
395 if (vf >= ns->num_vfs)
396 return -EINVAL;
397
398 switch (state) {
399 case IFLA_VF_LINK_STATE_AUTO:
400 case IFLA_VF_LINK_STATE_ENABLE:
401 case IFLA_VF_LINK_STATE_DISABLE:
402 break;
403 default:
404 return -EINVAL;
405 }
406
407 ns->vfconfigs[vf].link_state = state;
408
409 return 0;
410}
411
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800412static int
413nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
414{
415 switch (type) {
416 case TC_SETUP_BLOCK:
417 return nsim_setup_tc_block(dev, type_data);
418 default:
419 return -EOPNOTSUPP;
420 }
421}
422
423static int
424nsim_set_features(struct net_device *dev, netdev_features_t features)
425{
426 struct netdevsim *ns = netdev_priv(dev);
427
428 if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC))
429 return nsim_bpf_disable_tc(ns);
430
431 return 0;
432}
433
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800434static const struct net_device_ops nsim_netdev_ops = {
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800435 .ndo_init = nsim_init,
436 .ndo_uninit = nsim_uninit,
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800437 .ndo_start_xmit = nsim_start_xmit,
438 .ndo_set_rx_mode = nsim_set_rx_mode,
439 .ndo_set_mac_address = eth_mac_addr,
440 .ndo_validate_addr = eth_validate_addr,
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800441 .ndo_change_mtu = nsim_change_mtu,
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800442 .ndo_get_stats64 = nsim_get_stats64,
Jakub Kicinski79579222017-12-01 15:09:01 -0800443 .ndo_set_vf_mac = nsim_set_vf_mac,
444 .ndo_set_vf_vlan = nsim_set_vf_vlan,
445 .ndo_set_vf_rate = nsim_set_vf_rate,
446 .ndo_set_vf_spoofchk = nsim_set_vf_spoofchk,
447 .ndo_set_vf_trust = nsim_set_vf_trust,
448 .ndo_get_vf_config = nsim_get_vf_config,
449 .ndo_set_vf_link_state = nsim_set_vf_link_state,
450 .ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en,
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800451 .ndo_setup_tc = nsim_setup_tc,
452 .ndo_set_features = nsim_set_features,
453 .ndo_bpf = nsim_bpf,
Florian Fainellie58df562019-02-06 09:45:43 -0800454 .ndo_get_port_parent_id = nsim_get_port_parent_id,
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800455};
456
457static void nsim_setup(struct net_device *dev)
458{
459 ether_setup(dev);
460 eth_hw_addr_random(dev);
461
462 dev->netdev_ops = &nsim_netdev_ops;
Jiri Pirkoc3d9a432019-04-09 17:14:53 +0200463 dev->needs_free_netdev = true;
Jakub Kicinski79579222017-12-01 15:09:01 -0800464 dev->priv_destructor = nsim_free;
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800465
466 dev->tx_queue_len = 0;
467 dev->flags |= IFF_NOARP;
468 dev->flags &= ~IFF_MULTICAST;
469 dev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
470 IFF_NO_QUEUE;
471 dev->features |= NETIF_F_HIGHDMA |
472 NETIF_F_SG |
473 NETIF_F_FRAGLIST |
474 NETIF_F_HW_CSUM |
475 NETIF_F_TSO;
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800476 dev->hw_features |= NETIF_F_HW_TC;
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800477 dev->max_mtu = ETH_MAX_MTU;
478}
479
480static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
481 struct netlink_ext_ack *extack)
482{
483 if (tb[IFLA_ADDRESS]) {
484 if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
485 return -EINVAL;
486 if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
487 return -EADDRNOTAVAIL;
488 }
489 return 0;
490}
491
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700492static int nsim_newlink(struct net *src_net, struct net_device *dev,
493 struct nlattr *tb[], struct nlattr *data[],
494 struct netlink_ext_ack *extack)
495{
496 struct netdevsim *ns = netdev_priv(dev);
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200497 struct netdevsim *joinns = NULL;
498 int err;
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700499
500 if (tb[IFLA_LINK]) {
501 struct net_device *joindev;
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700502
503 joindev = __dev_get_by_index(src_net,
504 nla_get_u32(tb[IFLA_LINK]));
505 if (!joindev)
506 return -ENODEV;
507 if (joindev->netdev_ops != &nsim_netdev_ops)
508 return -EINVAL;
509
510 joinns = netdev_priv(joindev);
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700511 }
512
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200513 ns->sdev = nsim_sdev_get(joinns);
514 if (IS_ERR(ns->sdev))
515 return PTR_ERR(ns->sdev);
516
517 err = register_netdevice(dev);
518 if (err)
519 goto err_sdev_put;
520 return 0;
521
522err_sdev_put:
523 nsim_sdev_put(ns->sdev);
524 return err;
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700525}
526
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800527static struct rtnl_link_ops nsim_link_ops __read_mostly = {
528 .kind = DRV_NAME,
529 .priv_size = sizeof(struct netdevsim),
530 .setup = nsim_setup,
531 .validate = nsim_validate,
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700532 .newlink = nsim_newlink,
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800533};
534
535static int __init nsim_module_init(void)
536{
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800537 int err;
538
539 nsim_ddir = debugfs_create_dir(DRV_NAME, NULL);
Prashant Bhole9ee19422017-12-20 12:18:57 +0900540 if (IS_ERR_OR_NULL(nsim_ddir))
541 return -ENOMEM;
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800542
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200543 err = nsim_sdev_init();
544 if (err)
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700545 goto err_debugfs_destroy;
Jakub Kicinskieeeaaf12018-07-17 10:53:20 -0700546
Jakub Kicinski79579222017-12-01 15:09:01 -0800547 err = bus_register(&nsim_bus);
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800548 if (err)
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200549 goto err_sdev_exit;
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800550
David Ahern37923ed2018-03-27 18:22:00 -0700551 err = nsim_devlink_init();
Jakub Kicinski79579222017-12-01 15:09:01 -0800552 if (err)
553 goto err_unreg_bus;
554
David Ahern37923ed2018-03-27 18:22:00 -0700555 err = rtnl_link_register(&nsim_link_ops);
556 if (err)
557 goto err_dl_fini;
558
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800559 return 0;
560
David Ahern37923ed2018-03-27 18:22:00 -0700561err_dl_fini:
562 nsim_devlink_exit();
Jakub Kicinski79579222017-12-01 15:09:01 -0800563err_unreg_bus:
564 bus_unregister(&nsim_bus);
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200565err_sdev_exit:
566 nsim_sdev_exit();
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800567err_debugfs_destroy:
568 debugfs_remove_recursive(nsim_ddir);
569 return err;
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800570}
571
572static void __exit nsim_module_exit(void)
573{
574 rtnl_link_unregister(&nsim_link_ops);
David Ahern37923ed2018-03-27 18:22:00 -0700575 nsim_devlink_exit();
Jakub Kicinski79579222017-12-01 15:09:01 -0800576 bus_unregister(&nsim_bus);
Jiri Pirkoaf9095f2019-04-12 14:49:26 +0200577 nsim_sdev_exit();
Jakub Kicinski31d3ad82017-12-01 15:08:59 -0800578 debugfs_remove_recursive(nsim_ddir);
Jakub Kicinski83c9e132017-12-01 15:08:58 -0800579}
580
581module_init(nsim_module_init);
582module_exit(nsim_module_exit);
583MODULE_LICENSE("GPL");
584MODULE_ALIAS_RTNL_LINK(DRV_NAME);