dev.c 36.3 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 21 22 23
#include <linux/etherdevice.h>
#include <linux/inet.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
24
#include <linux/list.h>
25
#include <linux/mutex.h>
26
#include <linux/random.h>
27
#include <linux/rtnetlink.h>
28
#include <linux/workqueue.h>
29
#include <net/devlink.h>
30
#include <net/ip.h>
31
#include <net/flow_offload.h>
32 33 34
#include <uapi/linux/devlink.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/udp.h>
35 36 37

#include "netdevsim.h"

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
static unsigned int
nsim_dev_port_index(enum nsim_dev_port_type type, unsigned int port_index)
{
	switch (type) {
	case NSIM_DEV_PORT_TYPE_VF:
		port_index = NSIM_DEV_VF_PORT_INDEX_BASE + port_index;
		break;
	case NSIM_DEV_PORT_TYPE_PF:
		break;
	}

	return port_index;
}

static inline unsigned int nsim_dev_port_index_to_vf_index(unsigned int port_index)
{
	return port_index - NSIM_DEV_VF_PORT_INDEX_BASE;
}

J
Jiri Pirko 已提交
57 58
static struct dentry *nsim_dev_ddir;

59 60
#define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32)

61
static int
62 63 64
nsim_dev_take_snapshot(struct devlink *devlink,
		       const struct devlink_region_ops *ops,
		       struct netlink_ext_ack *extack,
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
		       u8 **data)
{
	void *dummy_data;

	dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL);
	if (!dummy_data)
		return -ENOMEM;

	get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE);

	*data = dummy_data;

	return 0;
}

80 81 82 83 84
static ssize_t nsim_dev_take_snapshot_write(struct file *file,
					    const char __user *data,
					    size_t count, loff_t *ppos)
{
	struct nsim_dev *nsim_dev = file->private_data;
85
	struct devlink *devlink;
86
	u8 *dummy_data;
87 88 89
	int err;
	u32 id;

90 91
	devlink = priv_to_devlink(nsim_dev);

92
	err = nsim_dev_take_snapshot(devlink, NULL, NULL, &dummy_data);
93 94
	if (err)
		return err;
95

96
	err = devlink_region_snapshot_id_get(devlink, &id);
97 98
	if (err) {
		pr_err("Failed to get snapshot id\n");
99
		kfree(dummy_data);
100 101
		return err;
	}
102
	err = devlink_region_snapshot_create(nsim_dev->dummy_region,
103
					     dummy_data, id);
104
	devlink_region_snapshot_id_put(devlink, id);
105 106 107 108 109 110 111 112 113 114 115 116 117
	if (err) {
		pr_err("Failed to create region snapshot\n");
		kfree(dummy_data);
		return err;
	}

	return count;
}

static const struct file_operations nsim_dev_take_snapshot_fops = {
	.open = simple_open,
	.write = nsim_dev_take_snapshot_write,
	.llseek = generic_file_llseek,
T
Taehee Yoo 已提交
118
	.owner = THIS_MODULE,
119 120
};

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 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
static ssize_t nsim_dev_trap_fa_cookie_read(struct file *file,
					    char __user *data,
					    size_t count, loff_t *ppos)
{
	struct nsim_dev *nsim_dev = file->private_data;
	struct flow_action_cookie *fa_cookie;
	unsigned int buf_len;
	ssize_t ret;
	char *buf;

	spin_lock(&nsim_dev->fa_cookie_lock);
	fa_cookie = nsim_dev->fa_cookie;
	if (!fa_cookie) {
		ret = -EINVAL;
		goto errout;
	}
	buf_len = fa_cookie->cookie_len * 2;
	buf = kmalloc(buf_len, GFP_ATOMIC);
	if (!buf) {
		ret = -ENOMEM;
		goto errout;
	}
	bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len);
	spin_unlock(&nsim_dev->fa_cookie_lock);

	ret = simple_read_from_buffer(data, count, ppos, buf, buf_len);

	kfree(buf);
	return ret;

errout:
	spin_unlock(&nsim_dev->fa_cookie_lock);
	return ret;
}

static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file,
					     const char __user *data,
					     size_t count, loff_t *ppos)
{
	struct nsim_dev *nsim_dev = file->private_data;
	struct flow_action_cookie *fa_cookie;
	size_t cookie_len;
	ssize_t ret;
	char *buf;

	if (*ppos != 0)
		return -EINVAL;
	cookie_len = (count - 1) / 2;
	if ((count - 1) % 2)
		return -EINVAL;
	buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN);
	if (!buf)
		return -ENOMEM;

	ret = simple_write_to_buffer(buf, count, ppos, data, count);
	if (ret < 0)
		goto free_buf;

	fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len,
			    GFP_KERNEL | __GFP_NOWARN);
	if (!fa_cookie) {
		ret = -ENOMEM;
		goto free_buf;
	}

	fa_cookie->cookie_len = cookie_len;
	ret = hex2bin(fa_cookie->cookie, buf, cookie_len);
	if (ret)
		goto free_fa_cookie;
	kfree(buf);

	spin_lock(&nsim_dev->fa_cookie_lock);
	kfree(nsim_dev->fa_cookie);
	nsim_dev->fa_cookie = fa_cookie;
	spin_unlock(&nsim_dev->fa_cookie_lock);

	return count;

free_fa_cookie:
	kfree(fa_cookie);
free_buf:
	kfree(buf);
	return ret;
}

static const struct file_operations nsim_dev_trap_fa_cookie_fops = {
	.open = simple_open,
	.read = nsim_dev_trap_fa_cookie_read,
	.write = nsim_dev_trap_fa_cookie_write,
	.llseek = generic_file_llseek,
T
Taehee Yoo 已提交
211
	.owner = THIS_MODULE,
212 213
};

214 215 216 217 218 219 220 221
static const struct file_operations nsim_dev_max_vfs_fops = {
	.open = simple_open,
	.read = nsim_bus_dev_max_vfs_read,
	.write = nsim_bus_dev_max_vfs_write,
	.llseek = generic_file_llseek,
	.owner = THIS_MODULE,
};

J
Jiri Pirko 已提交
222 223
static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
{
224
	char dev_ddir_name[sizeof(DRV_NAME) + 10];
J
Jiri Pirko 已提交
225

226
	sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
J
Jiri Pirko 已提交
227
	nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
228 229
	if (IS_ERR(nsim_dev->ddir))
		return PTR_ERR(nsim_dev->ddir);
230
	nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
231 232
	if (IS_ERR(nsim_dev->ports_ddir))
		return PTR_ERR(nsim_dev->ports_ddir);
233 234
	debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
			    &nsim_dev->fw_update_status);
235 236
	debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir,
			    &nsim_dev->fw_update_overwrite_mask);
237 238 239 240
	debugfs_create_u32("max_macs", 0600, nsim_dev->ddir,
			   &nsim_dev->max_macs);
	debugfs_create_bool("test1", 0600, nsim_dev->ddir,
			    &nsim_dev->test1);
241 242 243 244 245
	nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
						      0200,
						      nsim_dev->ddir,
						      nsim_dev,
						&nsim_dev_take_snapshot_fops);
246 247 248 249
	debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir,
			    &nsim_dev->dont_allow_reload);
	debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir,
			    &nsim_dev->fail_reload);
250 251
	debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir,
			    nsim_dev, &nsim_dev_trap_fa_cookie_fops);
252 253 254
	debugfs_create_bool("fail_trap_group_set", 0600,
			    nsim_dev->ddir,
			    &nsim_dev->fail_trap_group_set);
255 256 257 258 259 260
	debugfs_create_bool("fail_trap_policer_set", 0600,
			    nsim_dev->ddir,
			    &nsim_dev->fail_trap_policer_set);
	debugfs_create_bool("fail_trap_policer_counter_get", 0600,
			    nsim_dev->ddir,
			    &nsim_dev->fail_trap_policer_counter_get);
261 262 263 264 265
	nsim_dev->max_vfs = debugfs_create_file("max_vfs",
						0600,
						nsim_dev->ddir,
						nsim_dev->nsim_bus_dev,
						&nsim_dev_max_vfs_fops);
266
	nsim_udp_tunnels_debugfs_create(nsim_dev);
J
Jiri Pirko 已提交
267 268 269 270 271
	return 0;
}

static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
{
272
	debugfs_remove_recursive(nsim_dev->ports_ddir);
J
Jiri Pirko 已提交
273 274 275
	debugfs_remove_recursive(nsim_dev->ddir);
}

276 277 278 279 280 281 282 283 284
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);
285 286
	if (IS_ERR(nsim_dev_port->ddir))
		return PTR_ERR(nsim_dev_port->ddir);
287 288 289 290 291 292 293 294 295 296 297 298 299

	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);
}

300
static int nsim_dev_resources_register(struct devlink *devlink)
301 302 303 304 305 306 307 308 309 310 311 312
{
	struct devlink_resource_size_params params = {
		.size_max = (u64)-1,
		.size_granularity = 1,
		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
	};
	int err;

	/* Resources for IPv4 */
	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
					NSIM_RESOURCE_IPV4,
					DEVLINK_RESOURCE_ID_PARENT_TOP,
313
					&params);
314 315 316 317 318
	if (err) {
		pr_err("Failed to register IPv4 top resource\n");
		goto out;
	}

319
	err = devlink_resource_register(devlink, "fib", (u64)-1,
320
					NSIM_RESOURCE_IPV4_FIB,
321
					NSIM_RESOURCE_IPV4, &params);
322 323 324 325 326
	if (err) {
		pr_err("Failed to register IPv4 FIB resource\n");
		return err;
	}

327
	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
328
					NSIM_RESOURCE_IPV4_FIB_RULES,
329
					NSIM_RESOURCE_IPV4, &params);
330 331 332 333 334 335 336 337 338
	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,
339
					&params);
340 341 342 343 344
	if (err) {
		pr_err("Failed to register IPv6 top resource\n");
		goto out;
	}

345
	err = devlink_resource_register(devlink, "fib", (u64)-1,
346
					NSIM_RESOURCE_IPV6_FIB,
347
					NSIM_RESOURCE_IPV6, &params);
348 349 350 351 352
	if (err) {
		pr_err("Failed to register IPv6 FIB resource\n");
		return err;
	}

353
	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
354
					NSIM_RESOURCE_IPV6_FIB_RULES,
355
					NSIM_RESOURCE_IPV6, &params);
356 357 358 359
	if (err) {
		pr_err("Failed to register IPv6 FIB rules resource\n");
		return err;
	}
360

361 362 363 364 365 366
	/* Resources for nexthops */
	err = devlink_resource_register(devlink, "nexthops", (u64)-1,
					NSIM_RESOURCE_NEXTHOPS,
					DEVLINK_RESOURCE_ID_PARENT_TOP,
					&params);

367 368 369 370
out:
	return err;
}

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
enum nsim_devlink_param_id {
	NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
	NSIM_DEVLINK_PARAM_ID_TEST1,
};

static const struct devlink_param nsim_devlink_params[] = {
	DEVLINK_PARAM_GENERIC(MAX_MACS,
			      BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
			      NULL, NULL, NULL),
	DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1,
			     "test1", DEVLINK_PARAM_TYPE_BOOL,
			     BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
			     NULL, NULL, NULL),
};

static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev,
						struct devlink *devlink)
{
	union devlink_param_value value;

	value.vu32 = nsim_dev->max_macs;
	devlink_param_driverinit_value_set(devlink,
					   DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
					   value);
	value.vbool = nsim_dev->test1;
	devlink_param_driverinit_value_set(devlink,
					   NSIM_DEVLINK_PARAM_ID_TEST1,
					   value);
}

static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	union devlink_param_value saved_value;
	int err;

	err = devlink_param_driverinit_value_get(devlink,
						 DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
						 &saved_value);
	if (!err)
		nsim_dev->max_macs = saved_value.vu32;
	err = devlink_param_driverinit_value_get(devlink,
						 NSIM_DEVLINK_PARAM_ID_TEST1,
						 &saved_value);
	if (!err)
		nsim_dev->test1 = saved_value.vbool;
}

419 420
#define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16

421 422
static const struct devlink_region_ops dummy_region_ops = {
	.name = "dummy",
423
	.destructor = &kfree,
424
	.snapshot = nsim_dev_take_snapshot,
425 426
};

427 428 429 430
static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,
				      struct devlink *devlink)
{
	nsim_dev->dummy_region =
431
		devlink_region_create(devlink, &dummy_region_ops,
432 433 434 435 436 437 438 439 440 441
				      NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
				      NSIM_DEV_DUMMY_REGION_SIZE);
	return PTR_ERR_OR_ZERO(nsim_dev->dummy_region);
}

static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)
{
	devlink_region_destroy(nsim_dev->dummy_region);
}

442 443 444 445 446 447 448 449
struct nsim_trap_item {
	void *trap_ctx;
	enum devlink_trap_action action;
};

struct nsim_trap_data {
	struct delayed_work trap_report_dw;
	struct nsim_trap_item *trap_items_arr;
450
	u64 *trap_policers_cnt_arr;
451 452 453 454
	struct nsim_dev *nsim_dev;
	spinlock_t trap_lock;	/* Protects trap_items_arr */
};

455
/* All driver-specific traps must be documented in
456
 * Documentation/networking/devlink/netdevsim.rst
457
 */
458 459 460 461 462 463 464 465 466 467 468
enum {
	NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
	NSIM_TRAP_ID_FID_MISS,
};

#define NSIM_TRAP_NAME_FID_MISS "fid_miss"

#define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT

#define NSIM_TRAP_DROP(_id, _group_id)					      \
	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
469
			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
470
			     NSIM_TRAP_METADATA)
471 472
#define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata)			      \
	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
473
			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
474
			     NSIM_TRAP_METADATA | (_metadata))
475 476
#define NSIM_TRAP_EXCEPTION(_id, _group_id)				      \
	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \
477
			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
478
			     NSIM_TRAP_METADATA)
I
Ido Schimmel 已提交
479 480 481 482
#define NSIM_TRAP_CONTROL(_id, _group_id, _action)			      \
	DEVLINK_TRAP_GENERIC(CONTROL, _action, _id,			      \
			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
			     NSIM_TRAP_METADATA)
483 484 485
#define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id)			      \
	DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id,	      \
			    NSIM_TRAP_NAME_##_id,			      \
486
			    DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
487 488
			    NSIM_TRAP_METADATA)

489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
#define NSIM_DEV_TRAP_POLICER_MIN_RATE	1
#define NSIM_DEV_TRAP_POLICER_MAX_RATE	8000
#define NSIM_DEV_TRAP_POLICER_MIN_BURST	8
#define NSIM_DEV_TRAP_POLICER_MAX_BURST	65536

#define NSIM_TRAP_POLICER(_id, _rate, _burst)				      \
	DEVLINK_TRAP_POLICER(_id, _rate, _burst,			      \
			     NSIM_DEV_TRAP_POLICER_MAX_RATE,		      \
			     NSIM_DEV_TRAP_POLICER_MIN_RATE,		      \
			     NSIM_DEV_TRAP_POLICER_MAX_BURST,		      \
			     NSIM_DEV_TRAP_POLICER_MIN_BURST)

static const struct devlink_trap_policer nsim_trap_policers_arr[] = {
	NSIM_TRAP_POLICER(1, 1000, 128),
	NSIM_TRAP_POLICER(2, 2000, 256),
	NSIM_TRAP_POLICER(3, 3000, 512),
};

507
static const struct devlink_trap_group nsim_trap_groups_arr[] = {
508 509
	DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
	DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),
510
	DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1),
511 512
	DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2),
	DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3),
I
Ido Schimmel 已提交
513
	DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3),
514 515
};

516 517 518 519 520 521 522 523 524
static const struct devlink_trap nsim_traps_arr[] = {
	NSIM_TRAP_DROP(SMAC_MC, L2_DROPS),
	NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
	NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
	NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
	NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
	NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
	NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS),
	NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
525
	NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS),
526
	NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS),
527 528 529 530
	NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS,
			   DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
	NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS,
			   DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
I
Ido Schimmel 已提交
531 532
	NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR),
	NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP),
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
};

#define NSIM_TRAP_L4_DATA_LEN 100

static struct sk_buff *nsim_dev_trap_skb_build(void)
{
	int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN;
	struct sk_buff *skb;
	struct udphdr *udph;
	struct ethhdr *eth;
	struct iphdr *iph;

	skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
	if (!skb)
		return NULL;
	tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len;

550
	skb_reset_mac_header(skb);
551 552 553 554 555 556
	eth = skb_put(skb, sizeof(struct ethhdr));
	eth_random_addr(eth->h_dest);
	eth_random_addr(eth->h_source);
	eth->h_proto = htons(ETH_P_IP);
	skb->protocol = htons(ETH_P_IP);

557
	skb_set_network_header(skb, skb->len);
558 559 560 561 562 563 564 565 566
	iph = skb_put(skb, sizeof(struct iphdr));
	iph->protocol = IPPROTO_UDP;
	iph->saddr = in_aton("192.0.2.1");
	iph->daddr = in_aton("198.51.100.1");
	iph->version = 0x4;
	iph->frag_off = 0;
	iph->ihl = 0x5;
	iph->tot_len = htons(tot_len);
	iph->ttl = 100;
567 568
	iph->check = 0;
	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
569

570
	skb_set_transport_header(skb, skb->len);
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
	udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
	get_random_bytes(&udph->source, sizeof(u16));
	get_random_bytes(&udph->dest, sizeof(u16));
	udph->len = htons(sizeof(struct udphdr) + data_len);

	return skb;
}

static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port)
{
	struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;
	struct devlink *devlink = priv_to_devlink(nsim_dev);
	struct nsim_trap_data *nsim_trap_data;
	int i;

	nsim_trap_data = nsim_dev->trap_data;

	spin_lock(&nsim_trap_data->trap_lock);
	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
590
		struct flow_action_cookie *fa_cookie = NULL;
591 592
		struct nsim_trap_item *nsim_trap_item;
		struct sk_buff *skb;
593 594 595 596
		bool has_fa_cookie;

		has_fa_cookie = nsim_traps_arr[i].metadata_cap &
				DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE;
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611

		nsim_trap_item = &nsim_trap_data->trap_items_arr[i];
		if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP)
			continue;

		skb = nsim_dev_trap_skb_build();
		if (!skb)
			continue;
		skb->dev = nsim_dev_port->ns->netdev;

		/* Trapped packets are usually passed to devlink in softIRQ,
		 * but in this case they are generated in a workqueue. Disable
		 * softIRQs to prevent lockdep from complaining about
		 * "incosistent lock state".
		 */
612 613 614

		spin_lock_bh(&nsim_dev->fa_cookie_lock);
		fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL;
615
		devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx,
616 617
				    &nsim_dev_port->devlink_port, fa_cookie);
		spin_unlock_bh(&nsim_dev->fa_cookie_lock);
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
		consume_skb(skb);
	}
	spin_unlock(&nsim_trap_data->trap_lock);
}

#define NSIM_TRAP_REPORT_INTERVAL_MS	100

static void nsim_dev_trap_report_work(struct work_struct *work)
{
	struct nsim_trap_data *nsim_trap_data;
	struct nsim_dev_port *nsim_dev_port;
	struct nsim_dev *nsim_dev;

	nsim_trap_data = container_of(work, struct nsim_trap_data,
				      trap_report_dw.work);
	nsim_dev = nsim_trap_data->nsim_dev;

	/* For each running port and enabled packet trap, generate a UDP
	 * packet with a random 5-tuple and report it.
	 */
	mutex_lock(&nsim_dev->port_list_lock);
	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) {
		if (!netif_running(nsim_dev_port->ns->netdev))
			continue;

		nsim_dev_trap_report(nsim_dev_port);
	}
	mutex_unlock(&nsim_dev->port_list_lock);

	schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
			      msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
}

static int nsim_dev_traps_init(struct devlink *devlink)
{
653
	size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr);
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	struct nsim_trap_data *nsim_trap_data;
	int err;

	nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL);
	if (!nsim_trap_data)
		return -ENOMEM;

	nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr),
						 sizeof(struct nsim_trap_item),
						 GFP_KERNEL);
	if (!nsim_trap_data->trap_items_arr) {
		err = -ENOMEM;
		goto err_trap_data_free;
	}

670 671 672 673 674 675 676 677
	nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count,
							sizeof(u64),
							GFP_KERNEL);
	if (!nsim_trap_data->trap_policers_cnt_arr) {
		err = -ENOMEM;
		goto err_trap_items_free;
	}

678 679 680 681 682 683 684 685
	/* The lock is used to protect the action state of the registered
	 * traps. The value is written by user and read in delayed work when
	 * iterating over all the traps.
	 */
	spin_lock_init(&nsim_trap_data->trap_lock);
	nsim_trap_data->nsim_dev = nsim_dev;
	nsim_dev->trap_data = nsim_trap_data;

686 687 688 689 690
	err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr,
					     policers_count);
	if (err)
		goto err_trap_policers_cnt_free;

691 692 693
	err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr,
					   ARRAY_SIZE(nsim_trap_groups_arr));
	if (err)
694
		goto err_trap_policers_unregister;
695

696 697 698
	err = devlink_traps_register(devlink, nsim_traps_arr,
				     ARRAY_SIZE(nsim_traps_arr), NULL);
	if (err)
699
		goto err_trap_groups_unregister;
700 701 702 703 704 705 706 707

	INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw,
			  nsim_dev_trap_report_work);
	schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
			      msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));

	return 0;

708 709 710
err_trap_groups_unregister:
	devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
				       ARRAY_SIZE(nsim_trap_groups_arr));
711 712 713 714 715
err_trap_policers_unregister:
	devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
					 ARRAY_SIZE(nsim_trap_policers_arr));
err_trap_policers_cnt_free:
	kfree(nsim_trap_data->trap_policers_cnt_arr);
716 717 718 719 720 721 722 723 724 725 726 727 728 729
err_trap_items_free:
	kfree(nsim_trap_data->trap_items_arr);
err_trap_data_free:
	kfree(nsim_trap_data);
	return err;
}

static void nsim_dev_traps_exit(struct devlink *devlink)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);

	cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw);
	devlink_traps_unregister(devlink, nsim_traps_arr,
				 ARRAY_SIZE(nsim_traps_arr));
730 731
	devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
				       ARRAY_SIZE(nsim_trap_groups_arr));
732 733 734
	devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
					 ARRAY_SIZE(nsim_trap_policers_arr));
	kfree(nsim_dev->trap_data->trap_policers_cnt_arr);
735 736 737 738
	kfree(nsim_dev->trap_data->trap_items_arr);
	kfree(nsim_dev->trap_data);
}

739 740 741 742
static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
				  struct netlink_ext_ack *extack);
static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev);

743
static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change,
744 745
				enum devlink_reload_action action, enum devlink_reload_limit limit,
				struct netlink_ext_ack *extack)
746
{
747 748
	struct nsim_dev *nsim_dev = devlink_priv(devlink);

749 750 751 752
	if (nsim_dev->dont_allow_reload) {
		/* For testing purposes, user set debugfs dont_allow_reload
		 * value to true. So forbid it.
		 */
753
		NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes");
754 755 756
		return -EOPNOTSUPP;
	}

757
	nsim_dev_reload_destroy(nsim_dev);
758 759 760
	return 0;
}

761
static int nsim_dev_reload_up(struct devlink *devlink, enum devlink_reload_action action,
762 763
			      enum devlink_reload_limit limit, u32 *actions_performed,
			      struct netlink_ext_ack *extack)
764
{
765
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
766

767 768 769 770 771 772 773 774
	if (nsim_dev->fail_reload) {
		/* For testing purposes, user set debugfs fail_reload
		 * value to true. Fail right away.
		 */
		NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes");
		return -EINVAL;
	}

775
	*actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
776
	return nsim_dev_reload_create(nsim_dev, extack);
777 778
}

779 780 781 782 783 784 785
static int nsim_dev_info_get(struct devlink *devlink,
			     struct devlink_info_req *req,
			     struct netlink_ext_ack *extack)
{
	return devlink_info_driver_name_put(req, DRV_NAME);
}

786 787 788 789
#define NSIM_DEV_FLASH_SIZE 500000
#define NSIM_DEV_FLASH_CHUNK_SIZE 1000
#define NSIM_DEV_FLASH_CHUNK_TIME_MS 10

790 791
static int nsim_dev_flash_update(struct devlink *devlink,
				 struct devlink_flash_update_params *params,
792 793 794 795 796
				 struct netlink_ext_ack *extack)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	int i;

797 798 799
	if ((params->overwrite_mask & ~nsim_dev->fw_update_overwrite_mask) != 0)
		return -EOPNOTSUPP;

800 801 802
	if (nsim_dev->fw_update_status) {
		devlink_flash_update_status_notify(devlink,
						   "Preparing to flash",
803
						   params->component, 0, 0);
804 805 806 807 808
	}

	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",
809
							   params->component,
810 811 812 813 814 815 816
							   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",
817
						   params->component,
818 819
						   NSIM_DEV_FLASH_SIZE,
						   NSIM_DEV_FLASH_SIZE);
820
		devlink_flash_update_timeout_notify(devlink, "Flash select",
821
						    params->component, 81);
822
		devlink_flash_update_status_notify(devlink, "Flashing done",
823
						   params->component, 0, 0);
824 825 826 827 828
	}

	return 0;
}

829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
static struct nsim_trap_item *
nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id)
{
	struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data;
	int i;

	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
		if (nsim_traps_arr[i].id == trap_id)
			return &nsim_trap_data->trap_items_arr[i];
	}

	return NULL;
}

static int nsim_dev_devlink_trap_init(struct devlink *devlink,
				      const struct devlink_trap *trap,
				      void *trap_ctx)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	struct nsim_trap_item *nsim_trap_item;

	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
	if (WARN_ON(!nsim_trap_item))
		return -ENOENT;

	nsim_trap_item->trap_ctx = trap_ctx;
	nsim_trap_item->action = trap->init_action;

	return 0;
}

static int
nsim_dev_devlink_trap_action_set(struct devlink *devlink,
				 const struct devlink_trap *trap,
863 864
				 enum devlink_trap_action action,
				 struct netlink_ext_ack *extack)
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	struct nsim_trap_item *nsim_trap_item;

	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
	if (WARN_ON(!nsim_trap_item))
		return -ENOENT;

	spin_lock(&nsim_dev->trap_data->trap_lock);
	nsim_trap_item->action = action;
	spin_unlock(&nsim_dev->trap_data->trap_lock);

	return 0;
}

880 881 882
static int
nsim_dev_devlink_trap_group_set(struct devlink *devlink,
				const struct devlink_trap_group *group,
883 884
				const struct devlink_trap_policer *policer,
				struct netlink_ext_ack *extack)
885 886 887 888 889 890 891 892 893
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);

	if (nsim_dev->fail_trap_group_set)
		return -EINVAL;

	return 0;
}

894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
static int
nsim_dev_devlink_trap_policer_set(struct devlink *devlink,
				  const struct devlink_trap_policer *policer,
				  u64 rate, u64 burst,
				  struct netlink_ext_ack *extack)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);

	if (nsim_dev->fail_trap_policer_set) {
		NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes");
		return -EINVAL;
	}

	return 0;
}

static int
nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink,
					  const struct devlink_trap_policer *policer,
					  u64 *p_drops)
{
	struct nsim_dev *nsim_dev = devlink_priv(devlink);
	u64 *cnt;

	if (nsim_dev->fail_trap_policer_counter_get)
		return -EINVAL;

	cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1];
922
	*p_drops = (*cnt)++;
923 924 925 926

	return 0;
}

927
static const struct devlink_ops nsim_dev_devlink_ops = {
928 929
	.supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT |
					 DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
930
	.reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
931 932
	.reload_down = nsim_dev_reload_down,
	.reload_up = nsim_dev_reload_up,
933
	.info_get = nsim_dev_info_get,
934
	.flash_update = nsim_dev_flash_update,
935 936
	.trap_init = nsim_dev_devlink_trap_init,
	.trap_action_set = nsim_dev_devlink_trap_action_set,
937
	.trap_group_set = nsim_dev_devlink_trap_group_set,
938 939
	.trap_policer_set = nsim_dev_devlink_trap_policer_set,
	.trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get,
940 941
};

942 943 944
#define NSIM_DEV_MAX_MACS_DEFAULT 32
#define NSIM_DEV_TEST1_DEFAULT true

945
static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type,
946 947
			       unsigned int port_index)
{
D
Dmytro Linkin 已提交
948
	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;
949
	struct devlink_port_attrs attrs = {};
950 951 952 953
	struct nsim_dev_port *nsim_dev_port;
	struct devlink_port *devlink_port;
	int err;

D
Dmytro Linkin 已提交
954 955 956
	if (type == NSIM_DEV_PORT_TYPE_VF && !nsim_bus_dev->num_vfs)
		return -EINVAL;

957 958 959
	nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
	if (!nsim_dev_port)
		return -ENOMEM;
960 961
	nsim_dev_port->port_index = nsim_dev_port_index(type, port_index);
	nsim_dev_port->port_type = type;
962 963

	devlink_port = &nsim_dev_port->devlink_port;
D
Dmytro Linkin 已提交
964 965 966 967 968 969 970 971
	if (nsim_dev_port_is_pf(nsim_dev_port)) {
		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
		attrs.phys.port_number = port_index + 1;
	} else {
		attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF;
		attrs.pci_vf.pf = 0;
		attrs.pci_vf.vf = port_index;
	}
972 973 974
	memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
	attrs.switch_id.id_len = nsim_dev->switch_id.id_len;
	devlink_port_attrs_set(devlink_port, &attrs);
975
	err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
976
				    nsim_dev_port->port_index);
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
	if (err)
		goto err_port_free;

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

	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);
	list_add(&nsim_dev_port->list, &nsim_dev->port_list);

	return 0;

err_port_debugfs_exit:
	nsim_dev_port_debugfs_exit(nsim_dev_port);
err_dl_port_unregister:
	devlink_port_unregister(devlink_port);
err_port_free:
	kfree(nsim_dev_port);
	return err;
}

static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
{
	struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;

	list_del(&nsim_dev_port->list);
	devlink_port_type_clear(devlink_port);
	nsim_destroy(nsim_dev_port->ns);
	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;

1020
	mutex_lock(&nsim_dev->port_list_lock);
1021 1022 1023
	list_for_each_entry_safe(nsim_dev_port, tmp,
				 &nsim_dev->port_list, list)
		__nsim_dev_port_del(nsim_dev_port);
1024
	mutex_unlock(&nsim_dev->port_list_lock);
1025 1026 1027 1028 1029 1030 1031 1032
}

static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev,
				 unsigned int port_count)
{
	int i, err;

	for (i = 0; i < port_count; i++) {
1033
		err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_PF, i);
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
		if (err)
			goto err_port_del_all;
	}
	return 0;

err_port_del_all:
	nsim_dev_port_del_all(nsim_dev);
	return err;
}

1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
				  struct netlink_ext_ack *extack)
{
	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;
	struct devlink *devlink;
	int err;

	devlink = priv_to_devlink(nsim_dev);
	nsim_dev = devlink_priv(devlink);
	INIT_LIST_HEAD(&nsim_dev->port_list);
	mutex_init(&nsim_dev->port_list_lock);
	nsim_dev->fw_update_status = true;
1056
	nsim_dev->fw_update_overwrite_mask = 0;
1057 1058 1059 1060 1061

	nsim_devlink_param_load_driverinit_values(devlink);

	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
	if (err)
1062
		return err;
1063 1064 1065 1066 1067

	err = nsim_dev_traps_init(devlink);
	if (err)
		goto err_dummy_region_exit;

1068 1069 1070 1071 1072 1073
	nsim_dev->fib_data = nsim_fib_create(devlink, extack);
	if (IS_ERR(nsim_dev->fib_data)) {
		err = PTR_ERR(nsim_dev->fib_data);
		goto err_traps_exit;
	}

1074
	err = nsim_dev_health_init(nsim_dev, devlink);
1075
	if (err)
1076
		goto err_fib_destroy;
1077

1078
	err = nsim_dev_psample_init(nsim_dev);
1079 1080 1081
	if (err)
		goto err_health_exit;

1082 1083 1084 1085
	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
	if (err)
		goto err_psample_exit;

1086 1087 1088 1089 1090
	nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
						      0200,
						      nsim_dev->ddir,
						      nsim_dev,
						&nsim_dev_take_snapshot_fops);
1091 1092
	return 0;

1093 1094
err_psample_exit:
	nsim_dev_psample_exit(nsim_dev);
1095 1096
err_health_exit:
	nsim_dev_health_exit(nsim_dev);
1097 1098
err_fib_destroy:
	nsim_fib_destroy(devlink, nsim_dev->fib_data);
1099 1100 1101 1102 1103 1104 1105
err_traps_exit:
	nsim_dev_traps_exit(devlink);
err_dummy_region_exit:
	nsim_dev_dummy_region_exit(nsim_dev);
	return err;
}

1106
int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
1107
{
1108
	struct nsim_dev *nsim_dev;
1109
	struct devlink *devlink;
1110
	int err;
1111

1112
	devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
1113
	if (!devlink)
1114
		return -ENOMEM;
1115
	devlink_net_set(devlink, nsim_bus_dev->initial_net);
1116
	nsim_dev = devlink_priv(devlink);
J
Jiri Pirko 已提交
1117
	nsim_dev->nsim_bus_dev = nsim_bus_dev;
1118 1119
	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);
1120
	INIT_LIST_HEAD(&nsim_dev->port_list);
1121
	mutex_init(&nsim_dev->port_list_lock);
1122
	nsim_dev->fw_update_status = true;
1123
	nsim_dev->fw_update_overwrite_mask = 0;
1124 1125
	nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
	nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
1126
	spin_lock_init(&nsim_dev->fa_cookie_lock);
1127

1128 1129
	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);

1130
	err = nsim_dev_resources_register(devlink);
1131
	if (err)
1132
		goto err_devlink_free;
1133

1134
	err = devlink_register(devlink, &nsim_bus_dev->dev);
1135
	if (err)
1136
		goto err_resources_unregister;
1137

1138 1139
	err = devlink_params_register(devlink, nsim_devlink_params,
				      ARRAY_SIZE(nsim_devlink_params));
J
Jiri Pirko 已提交
1140 1141
	if (err)
		goto err_dl_unregister;
1142 1143
	nsim_devlink_set_params_init_values(nsim_dev, devlink);

1144
	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
1145 1146
	if (err)
		goto err_params_unregister;
J
Jiri Pirko 已提交
1147

1148
	err = nsim_dev_traps_init(devlink);
1149 1150 1151
	if (err)
		goto err_dummy_region_exit;

1152 1153 1154 1155
	err = nsim_dev_debugfs_init(nsim_dev);
	if (err)
		goto err_traps_exit;

1156 1157 1158 1159 1160 1161
	nsim_dev->fib_data = nsim_fib_create(devlink, NULL);
	if (IS_ERR(nsim_dev->fib_data)) {
		err = PTR_ERR(nsim_dev->fib_data);
		goto err_debugfs_exit;
	}

1162
	err = nsim_dev_health_init(nsim_dev, devlink);
J
Jiri Pirko 已提交
1163
	if (err)
1164
		goto err_fib_destroy;
J
Jiri Pirko 已提交
1165

1166 1167 1168 1169
	err = nsim_bpf_dev_init(nsim_dev);
	if (err)
		goto err_health_exit;

1170
	err = nsim_dev_psample_init(nsim_dev);
1171 1172 1173
	if (err)
		goto err_bpf_dev_exit;

1174 1175 1176 1177
	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
	if (err)
		goto err_psample_exit;

1178
	devlink_params_publish(devlink);
1179
	devlink_reload_enable(devlink);
1180
	return 0;
1181

1182 1183
err_psample_exit:
	nsim_dev_psample_exit(nsim_dev);
1184 1185
err_bpf_dev_exit:
	nsim_bpf_dev_exit(nsim_dev);
1186 1187
err_health_exit:
	nsim_dev_health_exit(nsim_dev);
1188 1189
err_fib_destroy:
	nsim_fib_destroy(devlink, nsim_dev->fib_data);
J
Jiri Pirko 已提交
1190 1191
err_debugfs_exit:
	nsim_dev_debugfs_exit(nsim_dev);
1192 1193
err_traps_exit:
	nsim_dev_traps_exit(devlink);
1194 1195
err_dummy_region_exit:
	nsim_dev_dummy_region_exit(nsim_dev);
1196 1197 1198
err_params_unregister:
	devlink_params_unregister(devlink, nsim_devlink_params,
				  ARRAY_SIZE(nsim_devlink_params));
J
Jiri Pirko 已提交
1199 1200
err_dl_unregister:
	devlink_unregister(devlink);
1201 1202
err_resources_unregister:
	devlink_resources_unregister(devlink, NULL);
1203 1204
err_devlink_free:
	devlink_free(devlink);
1205
	return err;
1206 1207
}

1208
static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
1209
{
1210
	struct devlink *devlink = priv_to_devlink(nsim_dev);
1211

1212 1213
	if (devlink_is_reload_failed(devlink))
		return;
1214
	debugfs_remove(nsim_dev->take_snapshot);
1215 1216 1217 1218 1219 1220

	mutex_lock(&nsim_dev->nsim_bus_dev->vfs_lock);
	if (nsim_dev->nsim_bus_dev->num_vfs)
		nsim_bus_dev_vfs_disable(nsim_dev->nsim_bus_dev);
	mutex_unlock(&nsim_dev->nsim_bus_dev->vfs_lock);

1221
	nsim_dev_port_del_all(nsim_dev);
1222
	nsim_dev_psample_exit(nsim_dev);
1223
	nsim_dev_health_exit(nsim_dev);
1224
	nsim_fib_destroy(devlink, nsim_dev->fib_data);
1225
	nsim_dev_traps_exit(devlink);
1226
	nsim_dev_dummy_region_exit(nsim_dev);
1227 1228 1229
	mutex_destroy(&nsim_dev->port_list_lock);
}

1230
void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
1231
{
1232
	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
1233 1234
	struct devlink *devlink = priv_to_devlink(nsim_dev);

1235 1236
	devlink_reload_disable(devlink);

1237 1238 1239 1240
	nsim_dev_reload_destroy(nsim_dev);

	nsim_bpf_dev_exit(nsim_dev);
	nsim_dev_debugfs_exit(nsim_dev);
1241 1242
	devlink_params_unregister(devlink, nsim_devlink_params,
				  ARRAY_SIZE(nsim_devlink_params));
1243 1244 1245
	devlink_unregister(devlink);
	devlink_resources_unregister(devlink, NULL);
	devlink_free(devlink);
1246
}
J
Jiri Pirko 已提交
1247

1248
static struct nsim_dev_port *
1249 1250
__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type,
		       unsigned int port_index)
1251 1252 1253
{
	struct nsim_dev_port *nsim_dev_port;

1254
	port_index = nsim_dev_port_index(type, port_index);
1255 1256 1257 1258 1259 1260
	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;
}

1261
int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type,
1262 1263 1264 1265 1266 1267
		      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);
1268
	if (__nsim_dev_port_lookup(nsim_dev, type, port_index))
1269 1270
		err = -EEXIST;
	else
1271
		err = __nsim_dev_port_add(nsim_dev, type, port_index);
1272 1273 1274 1275
	mutex_unlock(&nsim_dev->port_list_lock);
	return err;
}

1276
int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type,
1277 1278 1279 1280 1281 1282 1283
		      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);
1284
	nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, type, port_index);
1285 1286 1287 1288 1289 1290 1291 1292
	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 已提交
1293 1294
int nsim_dev_init(void)
{
1295
	nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
1296
	return PTR_ERR_OR_ZERO(nsim_dev_ddir);
J
Jiri Pirko 已提交
1297 1298 1299 1300 1301 1302
}

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