/* * 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(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;}
/*
* 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(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;
}