devlink.c 6.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 * 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
 */
33
static u64 nsim_ipv4_fib_resource_occ_get(void *priv)
34
{
35
	struct net *net = priv;
36 37 38 39

	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
}

40
static u64 nsim_ipv4_fib_rules_res_occ_get(void *priv)
41
{
42
	struct net *net = priv;
43 44 45 46 47 48

	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
}

/* IPv6
 */
49
static u64 nsim_ipv6_fib_resource_occ_get(void *priv)
50
{
51
	struct net *net = priv;
52 53 54 55

	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
}

56
static u64 nsim_ipv6_fib_rules_res_occ_get(void *priv)
57
{
58
	struct net *net = priv;
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

	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,
78
					&params);
79 80 81 82 83 84 85 86
	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,
87
					NSIM_RESOURCE_IPV4, &params);
88 89 90 91 92 93 94 95
	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,
96
					NSIM_RESOURCE_IPV4, &params);
97 98 99 100 101 102 103 104 105
	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,
106
					&params);
107 108 109 110 111 112 113 114
	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,
115
					NSIM_RESOURCE_IPV6, &params);
116 117 118 119 120 121 122 123
	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,
124
					NSIM_RESOURCE_IPV6, &params);
125 126 127 128
	if (err) {
		pr_err("Failed to register IPv6 FIB rules resource\n");
		return err;
	}
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

	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);
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
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;
	}
}

218
int nsim_devlink_setup(struct netdevsim *ns)
219 220 221 222
{
	struct net *net = nsim_to_net(ns);
	bool *reg_devlink = net_generic(net, nsim_devlink_id);
	struct devlink *devlink;
223
	int err;
224 225 226 227

	/* only one device per namespace controls devlink */
	if (!*reg_devlink) {
		ns->devlink = NULL;
228
		return 0;
229 230 231 232
	}

	devlink = devlink_alloc(&nsim_devlink_ops, 0);
	if (!devlink)
233
		return -ENOMEM;
234 235 236 237 238 239 240 241 242 243 244 245 246

	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;

247
	return 0;
248 249 250 251 252

err_dl_unregister:
	devlink_unregister(devlink);
err_devlink_free:
	devlink_free(devlink);
253 254

	return err;
255 256 257 258 259 260 261 262 263 264 265 266
}

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

267
static struct pernet_operations nsim_devlink_net_ops = {
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	.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;
}