1213 lines · 33.5 KB · C
RawDownload
/* * Copyright (c) 2018 Cumulus Networks. All rights reserved. * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com> * Copyright (c) 2019 Mellanox Technologies. All rights reserved. * * 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/debugfs.h>#include <linux/device.h>#include <linux/etherdevice.h>#include <linux/inet.h>#include <linux/jiffies.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/mutex.h>#include <linux/random.h>#include <linux/rtnetlink.h>#include <linux/workqueue.h>#include <net/devlink.h>#include <net/ip.h>#include <net/flow_offload.h>#include <uapi/linux/devlink.h>#include <uapi/linux/ip.h>#include <uapi/linux/udp.h> #include "netdevsim.h" static struct dentry *nsim_dev_ddir; #define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32) static intnsim_dev_take_snapshot(struct devlink *devlink, struct netlink_ext_ack *extack,		       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;} 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;	struct devlink *devlink;	u8 *dummy_data;	int err;	u32 id; 	devlink = priv_to_devlink(nsim_dev); 	err = nsim_dev_take_snapshot(devlink, NULL, &dummy_data);	if (err)		return err; 	err = devlink_region_snapshot_id_get(devlink, &id);	if (err) {		pr_err("Failed to get snapshot id\n");		kfree(dummy_data);		return err;	}	err = devlink_region_snapshot_create(nsim_dev->dummy_region,					     dummy_data, id);	devlink_region_snapshot_id_put(devlink, id);	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,}; 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,}; static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev){	char dev_ddir_name[sizeof(DRV_NAME) + 10]; 	sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);	nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);	if (IS_ERR(nsim_dev->ddir))		return PTR_ERR(nsim_dev->ddir);	nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);	if (IS_ERR(nsim_dev->ports_ddir))		return PTR_ERR(nsim_dev->ports_ddir);	debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,			    &nsim_dev->fw_update_status);	debugfs_create_u32("max_macs", 0600, nsim_dev->ddir,			   &nsim_dev->max_macs);	debugfs_create_bool("test1", 0600, nsim_dev->ddir,			    &nsim_dev->test1);	nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",						      0200,						      nsim_dev->ddir,						      nsim_dev,						&nsim_dev_take_snapshot_fops);	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);	debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir,			    nsim_dev, &nsim_dev_trap_fa_cookie_fops);	debugfs_create_bool("fail_trap_group_set", 0600,			    nsim_dev->ddir,			    &nsim_dev->fail_trap_group_set);	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);	nsim_udp_tunnels_debugfs_create(nsim_dev);	return 0;} static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev){	debugfs_remove_recursive(nsim_dev->ports_ddir);	debugfs_remove_recursive(nsim_dev->ddir);} 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(nsim_dev_port->ddir))		return PTR_ERR(nsim_dev_port->ddir); 	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);} static int nsim_dev_resources_register(struct devlink *devlink){	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,					&params);	if (err) {		pr_err("Failed to register IPv4 top resource\n");		goto out;	} 	err = devlink_resource_register(devlink, "fib", (u64)-1,					NSIM_RESOURCE_IPV4_FIB,					NSIM_RESOURCE_IPV4, &params);	if (err) {		pr_err("Failed to register IPv4 FIB resource\n");		return err;	} 	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,					NSIM_RESOURCE_IPV4_FIB_RULES,					NSIM_RESOURCE_IPV4, &params);	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,					&params);	if (err) {		pr_err("Failed to register IPv6 top resource\n");		goto out;	} 	err = devlink_resource_register(devlink, "fib", (u64)-1,					NSIM_RESOURCE_IPV6_FIB,					NSIM_RESOURCE_IPV6, &params);	if (err) {		pr_err("Failed to register IPv6 FIB resource\n");		return err;	} 	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,					NSIM_RESOURCE_IPV6_FIB_RULES,					NSIM_RESOURCE_IPV6, &params);	if (err) {		pr_err("Failed to register IPv6 FIB rules resource\n");		return err;	} out:	return err;} 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;} #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16 static const struct devlink_region_ops dummy_region_ops = {	.name = "dummy",	.destructor = &kfree,	.snapshot = nsim_dev_take_snapshot,}; static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,				      struct devlink *devlink){	nsim_dev->dummy_region =		devlink_region_create(devlink, &dummy_region_ops,				      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);} 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;	u64 *trap_policers_cnt_arr;	struct nsim_dev *nsim_dev;	spinlock_t trap_lock;	/* Protects trap_items_arr */}; /* All driver-specific traps must be documented in * Documentation/networking/devlink/netdevsim.rst */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,				      \			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \			     NSIM_TRAP_METADATA)#define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata)			      \	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \			     NSIM_TRAP_METADATA | (_metadata))#define NSIM_TRAP_EXCEPTION(_id, _group_id)				      \	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \			     NSIM_TRAP_METADATA)#define NSIM_TRAP_CONTROL(_id, _group_id, _action)			      \	DEVLINK_TRAP_GENERIC(CONTROL, _action, _id,			      \			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \			     NSIM_TRAP_METADATA)#define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id)			      \	DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id,	      \			    NSIM_TRAP_NAME_##_id,			      \			    DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \			    NSIM_TRAP_METADATA) #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),}; static const struct devlink_trap_group nsim_trap_groups_arr[] = {	DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),	DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),	DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1),	DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2),	DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3),	DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3),}; 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),	NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS),	NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS),	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),	NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR),	NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP),}; #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; 

Lines 1–500 of 1213.