dev.c 12.9 KB
Newer Older
1 2 3
/*
 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4
 * Copyright (c) 2019 Mellanox Technologies. All rights reserved.
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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.
 */

J
Jiri Pirko 已提交
18
#include <linux/debugfs.h>
19
#include <linux/device.h>
20
#include <linux/list.h>
21
#include <linux/mutex.h>
22
#include <linux/random.h>
23
#include <linux/rtnetlink.h>
24 25 26 27
#include <net/devlink.h>

#include "netdevsim.h"

J
Jiri Pirko 已提交
28 29 30 31
static struct dentry *nsim_dev_ddir;

static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
{
32
	char dev_ddir_name[16];
J
Jiri Pirko 已提交
33

34
	sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
J
Jiri Pirko 已提交
35 36 37
	nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
	if (IS_ERR_OR_NULL(nsim_dev->ddir))
		return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL;
38 39 40
	nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
	if (IS_ERR_OR_NULL(nsim_dev->ports_ddir))
		return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL;
41 42
	debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
			    &nsim_dev->fw_update_status);
J
Jiri Pirko 已提交
43 44 45 46 47
	return 0;
}

static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
{
48
	debugfs_remove_recursive(nsim_dev->ports_ddir);
J
Jiri Pirko 已提交
49 50 51
	debugfs_remove_recursive(nsim_dev->ddir);
}

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
				      struct nsim_dev_port *nsim_dev_port)
{
	char port_ddir_name[16];
	char dev_link_name[32];

	sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
	nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
						 nsim_dev->ports_ddir);
	if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
		return -ENOMEM;

	sprintf(dev_link_name, "../../../" DRV_NAME "%u",
		nsim_dev->nsim_bus_dev->dev.id);
	debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);

	return 0;
}

static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
{
	debugfs_remove_recursive(nsim_dev_port->ddir);
}

76
static u64 nsim_dev_ipv4_fib_resource_occ_get(void *priv)
77
{
78
	struct nsim_dev *nsim_dev = priv;
79

80
	return nsim_fib_get_val(nsim_dev->fib_data,
81
				NSIM_RESOURCE_IPV4_FIB, false);
82 83
}

84
static u64 nsim_dev_ipv4_fib_rules_res_occ_get(void *priv)
85
{
86
	struct nsim_dev *nsim_dev = priv;
87

88
	return nsim_fib_get_val(nsim_dev->fib_data,
89
				NSIM_RESOURCE_IPV4_FIB_RULES, false);
90 91
}

92
static u64 nsim_dev_ipv6_fib_resource_occ_get(void *priv)
93
{
94
	struct nsim_dev *nsim_dev = priv;
95

96
	return nsim_fib_get_val(nsim_dev->fib_data,
97
				NSIM_RESOURCE_IPV6_FIB, false);
98 99
}

100
static u64 nsim_dev_ipv6_fib_rules_res_occ_get(void *priv)
101
{
102
	struct nsim_dev *nsim_dev = priv;
103

104
	return nsim_fib_get_val(nsim_dev->fib_data,
105
				NSIM_RESOURCE_IPV6_FIB_RULES, false);
106 107
}

108
static int nsim_dev_resources_register(struct devlink *devlink)
109
{
110
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
111 112 113 114 115 116 117 118 119 120 121 122
	struct devlink_resource_size_params params = {
		.size_max = (u64)-1,
		.size_granularity = 1,
		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
	};
	int err;
	u64 n;

	/* Resources for IPv4 */
	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
					NSIM_RESOURCE_IPV4,
					DEVLINK_RESOURCE_ID_PARENT_TOP,
123
					&params);
124 125 126 127 128
	if (err) {
		pr_err("Failed to register IPv4 top resource\n");
		goto out;
	}

129
	n = nsim_fib_get_val(nsim_dev->fib_data,
130
			     NSIM_RESOURCE_IPV4_FIB, true);
131 132
	err = devlink_resource_register(devlink, "fib", n,
					NSIM_RESOURCE_IPV4_FIB,
133
					NSIM_RESOURCE_IPV4, &params);
134 135 136 137 138
	if (err) {
		pr_err("Failed to register IPv4 FIB resource\n");
		return err;
	}

139
	n = nsim_fib_get_val(nsim_dev->fib_data,
140
			     NSIM_RESOURCE_IPV4_FIB_RULES, true);
141 142
	err = devlink_resource_register(devlink, "fib-rules", n,
					NSIM_RESOURCE_IPV4_FIB_RULES,
143
					NSIM_RESOURCE_IPV4, &params);
144 145 146 147 148 149 150 151 152
	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,
153
					&params);
154 155 156 157 158
	if (err) {
		pr_err("Failed to register IPv6 top resource\n");
		goto out;
	}

159
	n = nsim_fib_get_val(nsim_dev->fib_data,
160
			     NSIM_RESOURCE_IPV6_FIB, true);
161 162
	err = devlink_resource_register(devlink, "fib", n,
					NSIM_RESOURCE_IPV6_FIB,
163
					NSIM_RESOURCE_IPV6, &params);
164 165 166 167 168
	if (err) {
		pr_err("Failed to register IPv6 FIB resource\n");
		return err;
	}

169
	n = nsim_fib_get_val(nsim_dev->fib_data,
170
			     NSIM_RESOURCE_IPV6_FIB_RULES, true);
171 172
	err = devlink_resource_register(devlink, "fib-rules", n,
					NSIM_RESOURCE_IPV6_FIB_RULES,
173
					NSIM_RESOURCE_IPV6, &params);
174 175 176 177
	if (err) {
		pr_err("Failed to register IPv6 FIB rules resource\n");
		return err;
	}
178 179 180

	devlink_resource_occ_get_register(devlink,
					  NSIM_RESOURCE_IPV4_FIB,
181 182
					  nsim_dev_ipv4_fib_resource_occ_get,
					  nsim_dev);
183 184
	devlink_resource_occ_get_register(devlink,
					  NSIM_RESOURCE_IPV4_FIB_RULES,
185 186
					  nsim_dev_ipv4_fib_rules_res_occ_get,
					  nsim_dev);
187 188
	devlink_resource_occ_get_register(devlink,
					  NSIM_RESOURCE_IPV6_FIB,
189 190
					  nsim_dev_ipv6_fib_resource_occ_get,
					  nsim_dev);
191 192
	devlink_resource_occ_get_register(devlink,
					  NSIM_RESOURCE_IPV6_FIB_RULES,
193 194
					  nsim_dev_ipv6_fib_rules_res_occ_get,
					  nsim_dev);
195 196 197 198
out:
	return err;
}

199 200
static int nsim_dev_reload(struct devlink *devlink,
			   struct netlink_ext_ack *extack)
201
{
202
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
203 204 205 206 207 208 209 210 211 212 213 214
	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) {
		int err;
		u64 val;

		err = devlink_resource_size_get(devlink, res_ids[i], &val);
		if (!err) {
215
			err = nsim_fib_set_max(nsim_dev->fib_data,
216
					       res_ids[i], val, extack);
217 218 219 220 221 222 223 224
			if (err)
				return err;
		}
	}

	return 0;
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
#define NSIM_DEV_FLASH_SIZE 500000
#define NSIM_DEV_FLASH_CHUNK_SIZE 1000
#define NSIM_DEV_FLASH_CHUNK_TIME_MS 10

static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
				 const char *component,
				 struct netlink_ext_ack *extack)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	int i;

	if (nsim_dev->fw_update_status) {
		devlink_flash_update_begin_notify(devlink);
		devlink_flash_update_status_notify(devlink,
						   "Preparing to flash",
						   component, 0, 0);
	}

	for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
		if (nsim_dev->fw_update_status)
			devlink_flash_update_status_notify(devlink, "Flashing",
							   component,
							   i * NSIM_DEV_FLASH_CHUNK_SIZE,
							   NSIM_DEV_FLASH_SIZE);
		msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
	}

	if (nsim_dev->fw_update_status) {
		devlink_flash_update_status_notify(devlink, "Flashing",
						   component,
						   NSIM_DEV_FLASH_SIZE,
						   NSIM_DEV_FLASH_SIZE);
		devlink_flash_update_status_notify(devlink, "Flashing done",
						   component, 0, 0);
		devlink_flash_update_end_notify(devlink);
	}

	return 0;
}

265 266
static const struct devlink_ops nsim_dev_devlink_ops = {
	.reload = nsim_dev_reload,
267
	.flash_update = nsim_dev_flash_update,
268 269
};

270 271
static struct nsim_dev *
nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
272
{
273
	struct nsim_dev *nsim_dev;
274
	struct devlink *devlink;
275
	int err;
276

277
	devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
278
	if (!devlink)
279
		return ERR_PTR(-ENOMEM);
280
	nsim_dev = devlink_priv(devlink);
J
Jiri Pirko 已提交
281
	nsim_dev->nsim_bus_dev = nsim_bus_dev;
282 283
	nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
	get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
284
	INIT_LIST_HEAD(&nsim_dev->port_list);
285
	mutex_init(&nsim_dev->port_list_lock);
286
	nsim_dev->fw_update_status = true;
287

288 289 290
	nsim_dev->fib_data = nsim_fib_create();
	if (IS_ERR(nsim_dev->fib_data)) {
		err = PTR_ERR(nsim_dev->fib_data);
291
		goto err_devlink_free;
292
	}
293

294
	err = nsim_dev_resources_register(devlink);
295
	if (err)
296
		goto err_fib_destroy;
297

298
	err = devlink_register(devlink, &nsim_bus_dev->dev);
299 300
	if (err)
		goto err_resources_unregister;
301

J
Jiri Pirko 已提交
302 303 304 305 306 307 308 309
	err = nsim_dev_debugfs_init(nsim_dev);
	if (err)
		goto err_dl_unregister;

	err = nsim_bpf_dev_init(nsim_dev);
	if (err)
		goto err_debugfs_exit;

310
	return nsim_dev;
311

J
Jiri Pirko 已提交
312 313 314 315
err_debugfs_exit:
	nsim_dev_debugfs_exit(nsim_dev);
err_dl_unregister:
	devlink_unregister(devlink);
316 317 318
err_resources_unregister:
	devlink_resources_unregister(devlink, NULL);
err_fib_destroy:
319
	nsim_fib_destroy(nsim_dev->fib_data);
320 321
err_devlink_free:
	devlink_free(devlink);
322
	return ERR_PTR(err);
323 324
}

325
static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
326
{
327
	struct devlink *devlink = priv_to_devlink(nsim_dev);
328

J
Jiri Pirko 已提交
329 330
	nsim_bpf_dev_exit(nsim_dev);
	nsim_dev_debugfs_exit(nsim_dev);
331 332
	devlink_unregister(devlink);
	devlink_resources_unregister(devlink, NULL);
333
	nsim_fib_destroy(nsim_dev->fib_data);
334
	mutex_destroy(&nsim_dev->port_list_lock);
335
	devlink_free(devlink);
336
}
J
Jiri Pirko 已提交
337

338 339
static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
			       unsigned int port_index)
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
{
	struct nsim_dev_port *nsim_dev_port;
	struct devlink_port *devlink_port;
	int err;

	nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
	if (!nsim_dev_port)
		return -ENOMEM;
	nsim_dev_port->port_index = port_index;

	devlink_port = &nsim_dev_port->devlink_port;
	devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
			       port_index + 1, 0, 0,
			       nsim_dev->switch_id.id,
			       nsim_dev->switch_id.id_len);
	err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
				    port_index);
	if (err)
		goto err_port_free;

	err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
	if (err)
		goto err_dl_port_unregister;

364 365 366 367 368 369 370
	nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
	if (IS_ERR(nsim_dev_port->ns)) {
		err = PTR_ERR(nsim_dev_port->ns);
		goto err_port_debugfs_exit;
	}

	devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
371 372 373 374
	list_add(&nsim_dev_port->list, &nsim_dev->port_list);

	return 0;

375 376
err_port_debugfs_exit:
	nsim_dev_port_debugfs_exit(nsim_dev_port);
377 378 379 380 381 382 383
err_dl_port_unregister:
	devlink_port_unregister(devlink_port);
err_port_free:
	kfree(nsim_dev_port);
	return err;
}

384
static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
385 386 387 388
{
	struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;

	list_del(&nsim_dev_port->list);
389 390
	devlink_port_type_clear(devlink_port);
	nsim_destroy(nsim_dev_port->ns);
391 392 393 394 395 396 397 398 399 400 401
	nsim_dev_port_debugfs_exit(nsim_dev_port);
	devlink_port_unregister(devlink_port);
	kfree(nsim_dev_port);
}

static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
{
	struct nsim_dev_port *nsim_dev_port, *tmp;

	list_for_each_entry_safe(nsim_dev_port, tmp,
				 &nsim_dev->port_list, list)
402
		__nsim_dev_port_del(nsim_dev_port);
403 404 405 406 407 408 409 410 411 412 413 414 415 416
}

int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
{
	struct nsim_dev *nsim_dev;
	int i;
	int err;

	nsim_dev = nsim_dev_create(nsim_bus_dev, nsim_bus_dev->port_count);
	if (IS_ERR(nsim_dev))
		return PTR_ERR(nsim_dev);
	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);

	for (i = 0; i < nsim_bus_dev->port_count; i++) {
417
		err = __nsim_dev_port_add(nsim_dev, i);
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
		if (err)
			goto err_port_del_all;
	}
	return 0;

err_port_del_all:
	nsim_dev_port_del_all(nsim_dev);
	nsim_dev_destroy(nsim_dev);
	return err;
}

void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
{
	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);

	nsim_dev_port_del_all(nsim_dev);
	nsim_dev_destroy(nsim_dev);
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
static struct nsim_dev_port *
__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
{
	struct nsim_dev_port *nsim_dev_port;

	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
		if (nsim_dev_port->port_index == port_index)
			return nsim_dev_port;
	return NULL;
}

int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
		      unsigned int port_index)
{
	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
	int err;

	mutex_lock(&nsim_dev->port_list_lock);
	if (__nsim_dev_port_lookup(nsim_dev, port_index))
		err = -EEXIST;
	else
		err = __nsim_dev_port_add(nsim_dev, port_index);
	mutex_unlock(&nsim_dev->port_list_lock);
	return err;
}

int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
		      unsigned int port_index)
{
	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
	struct nsim_dev_port *nsim_dev_port;
	int err = 0;

	mutex_lock(&nsim_dev->port_list_lock);
	nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
	if (!nsim_dev_port)
		err = -ENOENT;
	else
		__nsim_dev_port_del(nsim_dev_port);
	mutex_unlock(&nsim_dev->port_list_lock);
	return err;
}

J
Jiri Pirko 已提交
480 481
int nsim_dev_init(void)
{
482
	nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
J
Jiri Pirko 已提交
483 484 485 486 487 488 489 490 491
	if (IS_ERR_OR_NULL(nsim_dev_ddir))
		return -ENOMEM;
	return 0;
}

void nsim_dev_exit(void)
{
	debugfs_remove_recursive(nsim_dev_ddir);
}