diff options
author | David Ahern <dsa@cumulusnetworks.com> | 2018-03-27 18:22:00 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-03-29 14:10:31 -0400 |
commit | 37923ed6b8cea94d7d76038e2f72c57a0b45daab (patch) | |
tree | 51418fe5934da050f867b26bf0ee836e14cbc38a /drivers/net/netdevsim/devlink.c | |
parent | 2233000cba40ee0784a2d5b5e2b2c38c1159a7ef (diff) | |
download | lwn-37923ed6b8cea94d7d76038e2f72c57a0b45daab.tar.gz lwn-37923ed6b8cea94d7d76038e2f72c57a0b45daab.zip |
netdevsim: Add simple FIB resource controller via devlink
Add devlink support to netdevsim and use it to implement a simple,
profile based resource controller. Only one controller is needed
per namespace, so the first netdevsim netdevice in a namespace
registers with devlink. If that device is deleted, the resource
settings are deleted.
The resource controller allows a user to limit the number of IPv4 and
IPv6 FIB entries and FIB rules. The resource paths are:
/IPv4
/IPv4/fib
/IPv4/fib-rules
/IPv6
/IPv6/fib
/IPv6/fib-rules
The IPv4 and IPv6 top level resources are unlimited in size and can not
be changed. From there, the number of FIB entries and FIB rule entries
are unlimited by default. A user can specify a limit for the fib and
fib-rules resources:
$ devlink resource set netdevsim/netdevsim0 path /IPv4/fib size 96
$ devlink resource set netdevsim/netdevsim0 path /IPv4/fib-rules size 16
$ devlink resource set netdevsim/netdevsim0 path /IPv6/fib size 64
$ devlink resource set netdevsim/netdevsim0 path /IPv6/fib-rules size 16
$ devlink dev reload netdevsim/netdevsim0
such that the number of rules or routes is limited (96 ipv4 routes in the
example above):
$ for n in $(seq 1 32); do ip ro add 10.99.$n.0/24 dev eth1; done
Error: netdevsim: Exceeded number of supported fib entries.
$ devlink resource show netdevsim/netdevsim0
netdevsim/netdevsim0:
name IPv4 size unlimited unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables non
resources:
name fib size 96 occ 96 unit entry size_min 0 size_max unlimited size_gran 1 dpipe_tables
...
With this template in place for resource management, it is fairly trivial
to extend and shows one way to implement a simple counter based resource
controller typical of network profiles.
Currently, devlink only supports initial namespace. Code is in place to
adapt netdevsim to a per namespace controller once the network namespace
issues are resolved.
Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/netdevsim/devlink.c')
-rw-r--r-- | drivers/net/netdevsim/devlink.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c new file mode 100644 index 000000000000..bbdcf064ba10 --- /dev/null +++ b/drivers/net/netdevsim/devlink.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2018 Cumulus Networks. All rights reserved. + * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com> + * + * This software is licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree. + * + * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE + * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME + * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + */ + +#include <linux/device.h> +#include <net/devlink.h> +#include <net/netns/generic.h> + +#include "netdevsim.h" + +static unsigned int nsim_devlink_id; + +/* place holder until devlink and namespaces is sorted out */ +static struct net *nsim_devlink_net(struct devlink *devlink) +{ + return &init_net; +} + +/* IPv4 + */ +static u64 nsim_ipv4_fib_resource_occ_get(struct devlink *devlink) +{ + struct net *net = nsim_devlink_net(devlink); + + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false); +} + +static struct devlink_resource_ops nsim_ipv4_fib_res_ops = { + .occ_get = nsim_ipv4_fib_resource_occ_get, +}; + +static u64 nsim_ipv4_fib_rules_res_occ_get(struct devlink *devlink) +{ + struct net *net = nsim_devlink_net(devlink); + + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false); +} + +static struct devlink_resource_ops nsim_ipv4_fib_rules_res_ops = { + .occ_get = nsim_ipv4_fib_rules_res_occ_get, +}; + +/* IPv6 + */ +static u64 nsim_ipv6_fib_resource_occ_get(struct devlink *devlink) +{ + struct net *net = nsim_devlink_net(devlink); + + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false); +} + +static struct devlink_resource_ops nsim_ipv6_fib_res_ops = { + .occ_get = nsim_ipv6_fib_resource_occ_get, +}; + +static u64 nsim_ipv6_fib_rules_res_occ_get(struct devlink *devlink) +{ + struct net *net = nsim_devlink_net(devlink); + + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false); +} + +static struct devlink_resource_ops nsim_ipv6_fib_rules_res_ops = { + .occ_get = nsim_ipv6_fib_rules_res_occ_get, +}; + +static int devlink_resources_register(struct devlink *devlink) +{ + struct devlink_resource_size_params params = { + .size_max = (u64)-1, + .size_granularity = 1, + .unit = DEVLINK_RESOURCE_UNIT_ENTRY + }; + struct net *net = nsim_devlink_net(devlink); + int err; + u64 n; + + /* Resources for IPv4 */ + err = devlink_resource_register(devlink, "IPv4", (u64)-1, + NSIM_RESOURCE_IPV4, + DEVLINK_RESOURCE_ID_PARENT_TOP, + ¶ms, NULL); + if (err) { + pr_err("Failed to register IPv4 top resource\n"); + goto out; + } + + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true); + err = devlink_resource_register(devlink, "fib", n, + NSIM_RESOURCE_IPV4_FIB, + NSIM_RESOURCE_IPV4, + ¶ms, &nsim_ipv4_fib_res_ops); + if (err) { + pr_err("Failed to register IPv4 FIB resource\n"); + return err; + } + + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true); + err = devlink_resource_register(devlink, "fib-rules", n, + NSIM_RESOURCE_IPV4_FIB_RULES, + NSIM_RESOURCE_IPV4, + ¶ms, &nsim_ipv4_fib_rules_res_ops); + if (err) { + pr_err("Failed to register IPv4 FIB rules resource\n"); + return err; + } + + /* Resources for IPv6 */ + err = devlink_resource_register(devlink, "IPv6", (u64)-1, + NSIM_RESOURCE_IPV6, + DEVLINK_RESOURCE_ID_PARENT_TOP, + ¶ms, NULL); + if (err) { + pr_err("Failed to register IPv6 top resource\n"); + goto out; + } + + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true); + err = devlink_resource_register(devlink, "fib", n, + NSIM_RESOURCE_IPV6_FIB, + NSIM_RESOURCE_IPV6, + ¶ms, &nsim_ipv6_fib_res_ops); + if (err) { + pr_err("Failed to register IPv6 FIB resource\n"); + return err; + } + + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true); + err = devlink_resource_register(devlink, "fib-rules", n, + NSIM_RESOURCE_IPV6_FIB_RULES, + NSIM_RESOURCE_IPV6, + ¶ms, &nsim_ipv6_fib_rules_res_ops); + if (err) { + pr_err("Failed to register IPv6 FIB rules resource\n"); + return err; + } +out: + return err; +} + +static int nsim_devlink_reload(struct devlink *devlink) +{ + enum nsim_resource_id res_ids[] = { + NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, + NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES + }; + struct net *net = nsim_devlink_net(devlink); + int i; + + for (i = 0; i < ARRAY_SIZE(res_ids); ++i) { + int err; + u64 val; + + err = devlink_resource_size_get(devlink, res_ids[i], &val); + if (!err) { + err = nsim_fib_set_max(net, res_ids[i], val); + if (err) + return err; + } + } + + return 0; +} + +static void nsim_devlink_net_reset(struct net *net) +{ + enum nsim_resource_id res_ids[] = { + NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, + NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES + }; + int i; + + for (i = 0; i < ARRAY_SIZE(res_ids); ++i) { + if (nsim_fib_set_max(net, res_ids[i], (u64)-1)) { + pr_err("Failed to reset limit for resource %u\n", + res_ids[i]); + } + } +} + +static const struct devlink_ops nsim_devlink_ops = { + .reload = nsim_devlink_reload, +}; + +/* once devlink / namespace issues are sorted out + * this needs to be net in which a devlink instance + * is to be created. e.g., dev_net(ns->netdev) + */ +static struct net *nsim_to_net(struct netdevsim *ns) +{ + return &init_net; +} + +void nsim_devlink_teardown(struct netdevsim *ns) +{ + if (ns->devlink) { + struct net *net = nsim_to_net(ns); + bool *reg_devlink = net_generic(net, nsim_devlink_id); + + devlink_unregister(ns->devlink); + devlink_free(ns->devlink); + ns->devlink = NULL; + + nsim_devlink_net_reset(net); + *reg_devlink = true; + } +} + +void nsim_devlink_setup(struct netdevsim *ns) +{ + struct net *net = nsim_to_net(ns); + bool *reg_devlink = net_generic(net, nsim_devlink_id); + struct devlink *devlink; + int err = -ENOMEM; + + /* only one device per namespace controls devlink */ + if (!*reg_devlink) { + ns->devlink = NULL; + return; + } + + devlink = devlink_alloc(&nsim_devlink_ops, 0); + if (!devlink) + return; + + err = devlink_register(devlink, &ns->dev); + if (err) + goto err_devlink_free; + + err = devlink_resources_register(devlink); + if (err) + goto err_dl_unregister; + + ns->devlink = devlink; + + *reg_devlink = false; + + return; + +err_dl_unregister: + devlink_unregister(devlink); +err_devlink_free: + devlink_free(devlink); +} + +/* Initialize per network namespace state */ +static int __net_init nsim_devlink_netns_init(struct net *net) +{ + bool *reg_devlink = net_generic(net, nsim_devlink_id); + + *reg_devlink = true; + + return 0; +} + +static struct pernet_operations nsim_devlink_net_ops __net_initdata = { + .init = nsim_devlink_netns_init, + .id = &nsim_devlink_id, + .size = sizeof(bool), +}; + +void nsim_devlink_exit(void) +{ + unregister_pernet_subsys(&nsim_devlink_net_ops); + nsim_fib_exit(); +} + +int nsim_devlink_init(void) +{ + int err; + + err = nsim_fib_init(); + if (err) + goto err_out; + + err = register_pernet_subsys(&nsim_devlink_net_ops); + if (err) + nsim_fib_exit(); + +err_out: + return err; +} |