/* * Copyright (c) 2018 Cumulus Networks. All rights reserved. * Copyright (c) 2018 David Ahern * * 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 #include #include #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(void *priv) { struct net *net = priv; return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false); } static u64 nsim_ipv4_fib_rules_res_occ_get(void *priv) { struct net *net = priv; return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false); } /* IPv6 */ static u64 nsim_ipv6_fib_resource_occ_get(void *priv) { struct net *net = priv; return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false); } static u64 nsim_ipv6_fib_rules_res_occ_get(void *priv) { struct net *net = priv; return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false); } 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); 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); 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); 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); 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); 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); if (err) { pr_err("Failed to register IPv6 FIB rules resource\n"); return err; } devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV4_FIB, nsim_ipv4_fib_resource_occ_get, net); devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV4_FIB_RULES, nsim_ipv4_fib_rules_res_occ_get, net); devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV6_FIB, nsim_ipv6_fib_resource_occ_get, net); devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV6_FIB_RULES, nsim_ipv6_fib_rules_res_occ_get, net); out: return err; } static int nsim_devlink_reload(struct devlink *devlink, struct netlink_ext_ack *extack) { 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, extack); 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, NULL)) { 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_resources_unregister(ns->devlink, NULL); devlink_unregister(ns->devlink); devlink_free(ns->devlink); ns->devlink = NULL; nsim_devlink_net_reset(net); *reg_devlink = true; } } int 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; /* only one device per namespace controls devlink */ if (!*reg_devlink) { ns->devlink = NULL; return 0; } devlink = devlink_alloc(&nsim_devlink_ops, 0); if (!devlink) return -ENOMEM; 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 0; err_dl_unregister: devlink_unregister(devlink); err_devlink_free: devlink_free(devlink); return err; } /* 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 = { .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; }