1784 lines · 48.8 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 unsigned intnsim_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;} static struct dentry *nsim_dev_ddir; unsigned int nsim_dev_get_vfs(struct nsim_dev *nsim_dev){	WARN_ON(!lockdep_rtnl_is_held() &&		!devl_lock_is_held(priv_to_devlink(nsim_dev))); 	return nsim_dev->nsim_bus_dev->num_vfs;} static voidnsim_bus_dev_set_vfs(struct nsim_bus_dev *nsim_bus_dev, unsigned int num_vfs){	rtnl_lock();	nsim_bus_dev->num_vfs = num_vfs;	rtnl_unlock();} #define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32) static intnsim_dev_take_snapshot(struct devlink *devlink,		       const struct devlink_region_ops *ops,		       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, 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,	.owner = THIS_MODULE,}; 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,	.owner = THIS_MODULE,}; static ssize_t nsim_bus_dev_max_vfs_read(struct file *file, char __user *data,					 size_t count, loff_t *ppos){	struct nsim_dev *nsim_dev = file->private_data;	char buf[11];	ssize_t len; 	len = scnprintf(buf, sizeof(buf), "%u\n",			READ_ONCE(nsim_dev->nsim_bus_dev->max_vfs)); 	return simple_read_from_buffer(data, count, ppos, buf, len);} static ssize_t nsim_bus_dev_max_vfs_write(struct file *file,					  const char __user *data,					  size_t count, loff_t *ppos){	struct nsim_vf_config *vfconfigs;	struct nsim_dev *nsim_dev;	char buf[10];	ssize_t ret;	u32 val; 	if (*ppos != 0)		return 0; 	if (count >= sizeof(buf))		return -ENOSPC; 	ret = copy_from_user(buf, data, count);	if (ret)		return -EFAULT;	buf[count] = '\0'; 	ret = kstrtouint(buf, 10, &val);	if (ret)		return -EINVAL; 	/* max_vfs limited by the maximum number of provided port indexes */	if (val > NSIM_DEV_VF_PORT_INDEX_MAX - NSIM_DEV_VF_PORT_INDEX_BASE)		return -ERANGE; 	vfconfigs = kcalloc(val, sizeof(struct nsim_vf_config),			    GFP_KERNEL | __GFP_NOWARN);	if (!vfconfigs)		return -ENOMEM; 	nsim_dev = file->private_data;	devl_lock(priv_to_devlink(nsim_dev));	/* Reject if VFs are configured */	if (nsim_dev_get_vfs(nsim_dev)) {		ret = -EBUSY;	} else {		swap(nsim_dev->vfconfigs, vfconfigs);		WRITE_ONCE(nsim_dev->nsim_bus_dev->max_vfs, val);		*ppos += count;		ret = count;	}	devl_unlock(priv_to_devlink(nsim_dev)); 	kfree(vfconfigs);	return ret;} 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,}; static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev){	char dev_ddir_name[sizeof(DRV_NAME) + 10];	int err; 	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)) {		err = PTR_ERR(nsim_dev->ports_ddir);		goto err_ddir;	}	debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,			    &nsim_dev->fw_update_status);	debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir,			    &nsim_dev->fw_update_overwrite_mask);	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);	/* caution, dev_max_vfs write takes devlink lock */	debugfs_create_file("max_vfs", 0600, nsim_dev->ddir,			    nsim_dev, &nsim_dev_max_vfs_fops); 	nsim_dev->nodes_ddir = debugfs_create_dir("rate_nodes", nsim_dev->ddir);	if (IS_ERR(nsim_dev->nodes_ddir)) {		err = PTR_ERR(nsim_dev->nodes_ddir);		goto err_ports_ddir;	}	debugfs_create_bool("fail_trap_drop_counter_get", 0600,			    nsim_dev->ddir,			    &nsim_dev->fail_trap_drop_counter_get);	nsim_udp_tunnels_debugfs_create(nsim_dev);	return 0; err_ports_ddir:	debugfs_remove_recursive(nsim_dev->ports_ddir);err_ddir:	debugfs_remove_recursive(nsim_dev->ddir);	return err;} static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev){	debugfs_remove_recursive(nsim_dev->nodes_ddir);	debugfs_remove_recursive(nsim_dev->ports_ddir);	debugfs_remove_recursive(nsim_dev->ddir);} static ssize_t nsim_dev_rate_parent_read(struct file *file,					 char __user *data,					 size_t count, loff_t *ppos){	char **name_ptr = file->private_data;	size_t len; 	if (!*name_ptr)		return 0; 	len = strlen(*name_ptr);	return simple_read_from_buffer(data, count, ppos, *name_ptr, len);} static const struct file_operations nsim_dev_rate_parent_fops = {	.open = simple_open,	.read = nsim_dev_rate_parent_read,	.llseek = generic_file_llseek,	.owner = THIS_MODULE,}; static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,				      struct nsim_dev_port *nsim_dev_port){	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;	unsigned int port_index = nsim_dev_port->port_index;	char port_ddir_name[16];	char dev_link_name[32]; 	sprintf(port_ddir_name, "%u", 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_bus_dev->dev.id);	if (nsim_dev_port_is_vf(nsim_dev_port)) {		unsigned int vf_id = nsim_dev_port_index_to_vf_index(port_index); 		debugfs_create_u16("tx_share", 0400, nsim_dev_port->ddir,				   &nsim_dev->vfconfigs[vf_id].min_tx_rate);		debugfs_create_u16("tx_max", 0400, nsim_dev_port->ddir,				   &nsim_dev->vfconfigs[vf_id].max_tx_rate);		nsim_dev_port->rate_parent = debugfs_create_file("rate_parent",								 0400,								 nsim_dev_port->ddir,								 &nsim_dev_port->parent_name,								 &nsim_dev_rate_parent_fops);	}	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 = devl_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 err_out;	} 	err = devl_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");		goto err_out;	} 	err = devl_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");		goto err_out;	} 	/* Resources for IPv6 */	err = devl_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 err_out;	} 	err = devl_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");		goto err_out;	} 	err = devl_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");		goto err_out;	} 	/* Resources for nexthops */	err = devl_resource_register(devlink, "nexthops", (u64)-1,				     NSIM_RESOURCE_NEXTHOPS,				     DEVLINK_RESOURCE_ID_PARENT_TOP,				     &params);	if (err) {		pr_err("Failed to register NEXTHOPS resource\n");		goto err_out;

Lines 1–500 of 1784.