/* * Copyright (C) 2017 Netronome Systems, Inc. * * 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/etherdevice.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/slab.h>#include <net/netlink.h>#include <net/pkt_cls.h>#include <net/rtnetlink.h>#include <net/switchdev.h> #include "netdevsim.h" struct nsim_vf_config { int link_state; u16 min_tx_rate; u16 max_tx_rate; u16 vlan; __be16 vlan_proto; u16 qos; u8 vf_mac[ETH_ALEN]; bool spoofchk_enabled; bool trusted; bool rss_query_enabled;}; static u32 nsim_dev_id; static struct dentry *nsim_ddir;static struct dentry *nsim_sdev_ddir; static int nsim_num_vf(struct device *dev){ struct netdevsim *ns = to_nsim(dev); return ns->num_vfs;} static struct bus_type nsim_bus = { .name = DRV_NAME, .dev_name = DRV_NAME, .num_vf = nsim_num_vf,}; static int nsim_vfs_enable(struct netdevsim *ns, unsigned int num_vfs){ ns->vfconfigs = kcalloc(num_vfs, sizeof(struct nsim_vf_config), GFP_KERNEL); if (!ns->vfconfigs) return -ENOMEM; ns->num_vfs = num_vfs; return 0;} static void nsim_vfs_disable(struct netdevsim *ns){ kfree(ns->vfconfigs); ns->vfconfigs = NULL; ns->num_vfs = 0;} static ssize_tnsim_numvfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct netdevsim *ns = to_nsim(dev); unsigned int num_vfs; int ret; ret = kstrtouint(buf, 0, &num_vfs); if (ret) return ret; rtnl_lock(); if (ns->num_vfs == num_vfs) goto exit_good; if (ns->num_vfs && num_vfs) { ret = -EBUSY; goto exit_unlock; } if (num_vfs) { ret = nsim_vfs_enable(ns, num_vfs); if (ret) goto exit_unlock; } else { nsim_vfs_disable(ns); }exit_good: ret = count;exit_unlock: rtnl_unlock(); return ret;} static ssize_tnsim_numvfs_show(struct device *dev, struct device_attribute *attr, char *buf){ struct netdevsim *ns = to_nsim(dev); return sprintf(buf, "%u\n", ns->num_vfs);} static struct device_attribute nsim_numvfs_attr = __ATTR(sriov_numvfs, 0664, nsim_numvfs_show, nsim_numvfs_store); static struct attribute *nsim_dev_attrs[] = { &nsim_numvfs_attr.attr, NULL,}; static const struct attribute_group nsim_dev_attr_group = { .attrs = nsim_dev_attrs,}; static const struct attribute_group *nsim_dev_attr_groups[] = { &nsim_dev_attr_group, NULL,}; static void nsim_dev_release(struct device *dev){ struct netdevsim *ns = to_nsim(dev); nsim_vfs_disable(ns); free_netdev(ns->netdev);} static struct device_type nsim_dev_type = { .groups = nsim_dev_attr_groups, .release = nsim_dev_release,}; static intnsim_port_attr_get(struct net_device *dev, struct switchdev_attr *attr){ struct netdevsim *ns = netdev_priv(dev); switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(ns->sdev->switch_id); memcpy(&attr->u.ppid.id, &ns->sdev->switch_id, attr->u.ppid.id_len); return 0; default: return -EOPNOTSUPP; }} static const struct switchdev_ops nsim_switchdev_ops = { .switchdev_port_attr_get = nsim_port_attr_get,}; static int nsim_init(struct net_device *dev){ char sdev_ddir_name[10], sdev_link_name[32]; struct netdevsim *ns = netdev_priv(dev); int err; ns->netdev = dev; ns->ddir = debugfs_create_dir(netdev_name(dev), nsim_ddir); if (IS_ERR_OR_NULL(ns->ddir)) return -ENOMEM; if (!ns->sdev) { ns->sdev = kzalloc(sizeof(*ns->sdev), GFP_KERNEL); if (!ns->sdev) { err = -ENOMEM; goto err_debugfs_destroy; } ns->sdev->refcnt = 1; ns->sdev->switch_id = nsim_dev_id; sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id); ns->sdev->ddir = debugfs_create_dir(sdev_ddir_name, nsim_sdev_ddir); if (IS_ERR_OR_NULL(ns->sdev->ddir)) { err = PTR_ERR_OR_ZERO(ns->sdev->ddir) ?: -EINVAL; goto err_sdev_free; } } else { sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id); ns->sdev->refcnt++; } sprintf(sdev_link_name, "../../" DRV_NAME "_sdev/%s", sdev_ddir_name); debugfs_create_symlink("sdev", ns->ddir, sdev_link_name); err = nsim_bpf_init(ns); if (err) goto err_sdev_destroy; ns->dev.id = nsim_dev_id++; ns->dev.bus = &nsim_bus; ns->dev.type = &nsim_dev_type; err = device_register(&ns->dev); if (err) goto err_bpf_uninit; SET_NETDEV_DEV(dev, &ns->dev); SWITCHDEV_SET_OPS(dev, &nsim_switchdev_ops); err = nsim_devlink_setup(ns); if (err) goto err_unreg_dev; nsim_ipsec_init(ns); return 0; err_unreg_dev: device_unregister(&ns->dev);err_bpf_uninit: nsim_bpf_uninit(ns);err_sdev_destroy: if (!--ns->sdev->refcnt) { debugfs_remove_recursive(ns->sdev->ddir);err_sdev_free: kfree(ns->sdev); }err_debugfs_destroy: debugfs_remove_recursive(ns->ddir); return err;} static void nsim_uninit(struct net_device *dev){ struct netdevsim *ns = netdev_priv(dev); nsim_ipsec_teardown(ns); nsim_devlink_teardown(ns); debugfs_remove_recursive(ns->ddir); nsim_bpf_uninit(ns); if (!--ns->sdev->refcnt) { debugfs_remove_recursive(ns->sdev->ddir); kfree(ns->sdev); }} static void nsim_free(struct net_device *dev){ struct netdevsim *ns = netdev_priv(dev); device_unregister(&ns->dev); /* netdev and vf state will be freed out of device_release() */} static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct netdevsim *ns = netdev_priv(dev); if (!nsim_ipsec_tx(ns, skb)) goto out; u64_stats_update_begin(&ns->syncp); ns->tx_packets++; ns->tx_bytes += skb->len; u64_stats_update_end(&ns->syncp); out: dev_kfree_skb(skb); return NETDEV_TX_OK;} static void nsim_set_rx_mode(struct net_device *dev){} static int nsim_change_mtu(struct net_device *dev, int new_mtu){ struct netdevsim *ns = netdev_priv(dev); if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU) return -EBUSY; dev->mtu = new_mtu; return 0;} static voidnsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats){ struct netdevsim *ns = netdev_priv(dev); unsigned int start; do { start = u64_stats_fetch_begin(&ns->syncp); stats->tx_bytes = ns->tx_bytes; stats->tx_packets = ns->tx_packets; } while (u64_stats_fetch_retry(&ns->syncp, start));} static intnsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv){ return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv);} static intnsim_setup_tc_block(struct net_device *dev, struct tc_block_offload *f){ struct netdevsim *ns = netdev_priv(dev); if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS) return -EOPNOTSUPP; switch (f->command) { case TC_BLOCK_BIND: return tcf_block_cb_register(f->block, nsim_setup_tc_block_cb, ns, ns, f->extack); case TC_BLOCK_UNBIND: tcf_block_cb_unregister(f->block, nsim_setup_tc_block_cb, ns); return 0; default: return -EOPNOTSUPP; }} static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac){ struct netdevsim *ns = netdev_priv(dev); /* Only refuse multicast addresses, zero address can mean unset/any. */ if (vf >= ns->num_vfs || is_multicast_ether_addr(mac)) return -EINVAL; memcpy(ns->vfconfigs[vf].vf_mac, mac, ETH_ALEN); return 0;} static int nsim_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos, __be16 vlan_proto){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs || vlan > 4095 || qos > 7) return -EINVAL; ns->vfconfigs[vf].vlan = vlan; ns->vfconfigs[vf].qos = qos; ns->vfconfigs[vf].vlan_proto = vlan_proto; return 0;} static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].min_tx_rate = min; ns->vfconfigs[vf].max_tx_rate = max; return 0;} static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].spoofchk_enabled = val; return 0;} static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].rss_query_enabled = val; return 0;} static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].trusted = val; return 0;} static intnsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ivi->vf = vf; ivi->linkstate = ns->vfconfigs[vf].link_state; ivi->min_tx_rate = ns->vfconfigs[vf].min_tx_rate; ivi->max_tx_rate = ns->vfconfigs[vf].max_tx_rate; ivi->vlan = ns->vfconfigs[vf].vlan; ivi->vlan_proto = ns->vfconfigs[vf].vlan_proto; ivi->qos = ns->vfconfigs[vf].qos; memcpy(&ivi->mac, ns->vfconfigs[vf].vf_mac, ETH_ALEN); ivi->spoofchk = ns->vfconfigs[vf].spoofchk_enabled; ivi->trusted = ns->vfconfigs[vf].trusted; ivi->rss_query_en = ns->vfconfigs[vf].rss_query_enabled; return 0;} static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state){ struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; switch (state) { case IFLA_VF_LINK_STATE_AUTO: case IFLA_VF_LINK_STATE_ENABLE: case IFLA_VF_LINK_STATE_DISABLE: break; default: return -EINVAL; } ns->vfconfigs[vf].link_state = state; return 0;} static intnsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data){ switch (type) { case TC_SETUP_BLOCK: return nsim_setup_tc_block(dev, type_data); default: return -EOPNOTSUPP; }} static intnsim_set_features(struct net_device *dev, netdev_features_t features){ struct netdevsim *ns = netdev_priv(dev); if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC)) return nsim_bpf_disable_tc(ns); return 0;} static const struct net_device_ops nsim_netdev_ops = { .ndo_init = nsim_init, .ndo_uninit = nsim_uninit, .ndo_start_xmit = nsim_start_xmit, .ndo_set_rx_mode = nsim_set_rx_mode, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, .ndo_get_stats64 = nsim_get_stats64, .ndo_set_vf_mac = nsim_set_vf_mac, .ndo_set_vf_vlan = nsim_set_vf_vlan, .ndo_set_vf_rate = nsim_set_vf_rate, .ndo_set_vf_spoofchk = nsim_set_vf_spoofchk, .ndo_set_vf_trust = nsim_set_vf_trust, .ndo_get_vf_config = nsim_get_vf_config, .ndo_set_vf_link_state = nsim_set_vf_link_state, .ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en, .ndo_setup_tc = nsim_setup_tc, .ndo_set_features = nsim_set_features, .ndo_bpf = nsim_bpf,}; static void nsim_setup(struct net_device *dev){ ether_setup(dev);Lines 1–500 of 623. Load more
/* * Copyright (C) 2017 Netronome Systems, Inc. * * 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/etherdevice.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/slab.h> #include <net/netlink.h> #include <net/pkt_cls.h> #include <net/rtnetlink.h> #include <net/switchdev.h> #include "netdevsim.h" struct nsim_vf_config { int link_state; u16 min_tx_rate; u16 max_tx_rate; u16 vlan; __be16 vlan_proto; u16 qos; u8 vf_mac[ETH_ALEN]; bool spoofchk_enabled; bool trusted; bool rss_query_enabled; }; static u32 nsim_dev_id; static struct dentry *nsim_ddir; static struct dentry *nsim_sdev_ddir; static int nsim_num_vf(struct device *dev) { struct netdevsim *ns = to_nsim(dev); return ns->num_vfs; } static struct bus_type nsim_bus = { .name = DRV_NAME, .dev_name = DRV_NAME, .num_vf = nsim_num_vf, }; static int nsim_vfs_enable(struct netdevsim *ns, unsigned int num_vfs) { ns->vfconfigs = kcalloc(num_vfs, sizeof(struct nsim_vf_config), GFP_KERNEL); if (!ns->vfconfigs) return -ENOMEM; ns->num_vfs = num_vfs; return 0; } static void nsim_vfs_disable(struct netdevsim *ns) { kfree(ns->vfconfigs); ns->vfconfigs = NULL; ns->num_vfs = 0; } static ssize_t nsim_numvfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct netdevsim *ns = to_nsim(dev); unsigned int num_vfs; int ret; ret = kstrtouint(buf, 0, &num_vfs); if (ret) return ret; rtnl_lock(); if (ns->num_vfs == num_vfs) goto exit_good; if (ns->num_vfs && num_vfs) { ret = -EBUSY; goto exit_unlock; } if (num_vfs) { ret = nsim_vfs_enable(ns, num_vfs); if (ret) goto exit_unlock; } else { nsim_vfs_disable(ns); } exit_good: ret = count; exit_unlock: rtnl_unlock(); return ret; } static ssize_t nsim_numvfs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct netdevsim *ns = to_nsim(dev); return sprintf(buf, "%u\n", ns->num_vfs); } static struct device_attribute nsim_numvfs_attr = __ATTR(sriov_numvfs, 0664, nsim_numvfs_show, nsim_numvfs_store); static struct attribute *nsim_dev_attrs[] = { &nsim_numvfs_attr.attr, NULL, }; static const struct attribute_group nsim_dev_attr_group = { .attrs = nsim_dev_attrs, }; static const struct attribute_group *nsim_dev_attr_groups[] = { &nsim_dev_attr_group, NULL, }; static void nsim_dev_release(struct device *dev) { struct netdevsim *ns = to_nsim(dev); nsim_vfs_disable(ns); free_netdev(ns->netdev); } static struct device_type nsim_dev_type = { .groups = nsim_dev_attr_groups, .release = nsim_dev_release, }; static int nsim_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) { struct netdevsim *ns = netdev_priv(dev); switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(ns->sdev->switch_id); memcpy(&attr->u.ppid.id, &ns->sdev->switch_id, attr->u.ppid.id_len); return 0; default: return -EOPNOTSUPP; } } static const struct switchdev_ops nsim_switchdev_ops = { .switchdev_port_attr_get = nsim_port_attr_get, }; static int nsim_init(struct net_device *dev) { char sdev_ddir_name[10], sdev_link_name[32]; struct netdevsim *ns = netdev_priv(dev); int err; ns->netdev = dev; ns->ddir = debugfs_create_dir(netdev_name(dev), nsim_ddir); if (IS_ERR_OR_NULL(ns->ddir)) return -ENOMEM; if (!ns->sdev) { ns->sdev = kzalloc(sizeof(*ns->sdev), GFP_KERNEL); if (!ns->sdev) { err = -ENOMEM; goto err_debugfs_destroy; } ns->sdev->refcnt = 1; ns->sdev->switch_id = nsim_dev_id; sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id); ns->sdev->ddir = debugfs_create_dir(sdev_ddir_name, nsim_sdev_ddir); if (IS_ERR_OR_NULL(ns->sdev->ddir)) { err = PTR_ERR_OR_ZERO(ns->sdev->ddir) ?: -EINVAL; goto err_sdev_free; } } else { sprintf(sdev_ddir_name, "%u", ns->sdev->switch_id); ns->sdev->refcnt++; } sprintf(sdev_link_name, "../../" DRV_NAME "_sdev/%s", sdev_ddir_name); debugfs_create_symlink("sdev", ns->ddir, sdev_link_name); err = nsim_bpf_init(ns); if (err) goto err_sdev_destroy; ns->dev.id = nsim_dev_id++; ns->dev.bus = &nsim_bus; ns->dev.type = &nsim_dev_type; err = device_register(&ns->dev); if (err) goto err_bpf_uninit; SET_NETDEV_DEV(dev, &ns->dev); SWITCHDEV_SET_OPS(dev, &nsim_switchdev_ops); err = nsim_devlink_setup(ns); if (err) goto err_unreg_dev; nsim_ipsec_init(ns); return 0; err_unreg_dev: device_unregister(&ns->dev); err_bpf_uninit: nsim_bpf_uninit(ns); err_sdev_destroy: if (!--ns->sdev->refcnt) { debugfs_remove_recursive(ns->sdev->ddir); err_sdev_free: kfree(ns->sdev); } err_debugfs_destroy: debugfs_remove_recursive(ns->ddir); return err; } static void nsim_uninit(struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); nsim_ipsec_teardown(ns); nsim_devlink_teardown(ns); debugfs_remove_recursive(ns->ddir); nsim_bpf_uninit(ns); if (!--ns->sdev->refcnt) { debugfs_remove_recursive(ns->sdev->ddir); kfree(ns->sdev); } } static void nsim_free(struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); device_unregister(&ns->dev); /* netdev and vf state will be freed out of device_release() */ } static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct netdevsim *ns = netdev_priv(dev); if (!nsim_ipsec_tx(ns, skb)) goto out; u64_stats_update_begin(&ns->syncp); ns->tx_packets++; ns->tx_bytes += skb->len; u64_stats_update_end(&ns->syncp); out: dev_kfree_skb(skb); return NETDEV_TX_OK; } static void nsim_set_rx_mode(struct net_device *dev) { } static int nsim_change_mtu(struct net_device *dev, int new_mtu) { struct netdevsim *ns = netdev_priv(dev); if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU) return -EBUSY; dev->mtu = new_mtu; return 0; } static void nsim_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct netdevsim *ns = netdev_priv(dev); unsigned int start; do { start = u64_stats_fetch_begin(&ns->syncp); stats->tx_bytes = ns->tx_bytes; stats->tx_packets = ns->tx_packets; } while (u64_stats_fetch_retry(&ns->syncp, start)); } static int nsim_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { return nsim_bpf_setup_tc_block_cb(type, type_data, cb_priv); } static int nsim_setup_tc_block(struct net_device *dev, struct tc_block_offload *f) { struct netdevsim *ns = netdev_priv(dev); if (f->binder_type != TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS) return -EOPNOTSUPP; switch (f->command) { case TC_BLOCK_BIND: return tcf_block_cb_register(f->block, nsim_setup_tc_block_cb, ns, ns, f->extack); case TC_BLOCK_UNBIND: tcf_block_cb_unregister(f->block, nsim_setup_tc_block_cb, ns); return 0; default: return -EOPNOTSUPP; } } static int nsim_set_vf_mac(struct net_device *dev, int vf, u8 *mac) { struct netdevsim *ns = netdev_priv(dev); /* Only refuse multicast addresses, zero address can mean unset/any. */ if (vf >= ns->num_vfs || is_multicast_ether_addr(mac)) return -EINVAL; memcpy(ns->vfconfigs[vf].vf_mac, mac, ETH_ALEN); return 0; } static int nsim_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos, __be16 vlan_proto) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs || vlan > 4095 || qos > 7) return -EINVAL; ns->vfconfigs[vf].vlan = vlan; ns->vfconfigs[vf].qos = qos; ns->vfconfigs[vf].vlan_proto = vlan_proto; return 0; } static int nsim_set_vf_rate(struct net_device *dev, int vf, int min, int max) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].min_tx_rate = min; ns->vfconfigs[vf].max_tx_rate = max; return 0; } static int nsim_set_vf_spoofchk(struct net_device *dev, int vf, bool val) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].spoofchk_enabled = val; return 0; } static int nsim_set_vf_rss_query_en(struct net_device *dev, int vf, bool val) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].rss_query_enabled = val; return 0; } static int nsim_set_vf_trust(struct net_device *dev, int vf, bool val) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ns->vfconfigs[vf].trusted = val; return 0; } static int nsim_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; ivi->vf = vf; ivi->linkstate = ns->vfconfigs[vf].link_state; ivi->min_tx_rate = ns->vfconfigs[vf].min_tx_rate; ivi->max_tx_rate = ns->vfconfigs[vf].max_tx_rate; ivi->vlan = ns->vfconfigs[vf].vlan; ivi->vlan_proto = ns->vfconfigs[vf].vlan_proto; ivi->qos = ns->vfconfigs[vf].qos; memcpy(&ivi->mac, ns->vfconfigs[vf].vf_mac, ETH_ALEN); ivi->spoofchk = ns->vfconfigs[vf].spoofchk_enabled; ivi->trusted = ns->vfconfigs[vf].trusted; ivi->rss_query_en = ns->vfconfigs[vf].rss_query_enabled; return 0; } static int nsim_set_vf_link_state(struct net_device *dev, int vf, int state) { struct netdevsim *ns = netdev_priv(dev); if (vf >= ns->num_vfs) return -EINVAL; switch (state) { case IFLA_VF_LINK_STATE_AUTO: case IFLA_VF_LINK_STATE_ENABLE: case IFLA_VF_LINK_STATE_DISABLE: break; default: return -EINVAL; } ns->vfconfigs[vf].link_state = state; return 0; } static int nsim_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) { switch (type) { case TC_SETUP_BLOCK: return nsim_setup_tc_block(dev, type_data); default: return -EOPNOTSUPP; } } static int nsim_set_features(struct net_device *dev, netdev_features_t features) { struct netdevsim *ns = netdev_priv(dev); if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC)) return nsim_bpf_disable_tc(ns); return 0; } static const struct net_device_ops nsim_netdev_ops = { .ndo_init = nsim_init, .ndo_uninit = nsim_uninit, .ndo_start_xmit = nsim_start_xmit, .ndo_set_rx_mode = nsim_set_rx_mode, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, .ndo_get_stats64 = nsim_get_stats64, .ndo_set_vf_mac = nsim_set_vf_mac, .ndo_set_vf_vlan = nsim_set_vf_vlan, .ndo_set_vf_rate = nsim_set_vf_rate, .ndo_set_vf_spoofchk = nsim_set_vf_spoofchk, .ndo_set_vf_trust = nsim_set_vf_trust, .ndo_get_vf_config = nsim_get_vf_config, .ndo_set_vf_link_state = nsim_set_vf_link_state, .ndo_set_vf_rss_query_en = nsim_set_vf_rss_query_en, .ndo_setup_tc = nsim_setup_tc, .ndo_set_features = nsim_set_features, .ndo_bpf = nsim_bpf, }; static void nsim_setup(struct net_device *dev) { ether_setup(dev);