#include #include #include #include /* Values to be returned by ethtool ops */ static unsigned int rx_rings = 2; module_param(rx_rings, uint, 0444); static int indir_size; module_param(indir_size, int, 0444); static int key_size; module_param(key_size, int, 0444); /* Global state */ static u32 *modtest_indir; static u8 *modtest_key; static struct net_device *modtest_netdev; static int modtest_open(struct net_device *dev) { return -EOPNOTSUPP; } static struct net_device_ops modtest_netdev_ops = { .ndo_open = modtest_open, }; static int modtest_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *rxnfc, u32 *foo) { if (rxnfc->cmd == ETHTOOL_GRXRINGS) { rxnfc->data = rx_rings; return 0; } else { return -EOPNOTSUPP; } } static u32 modtest_get_rxfh_indir_size(struct net_device *netdev) { return indir_size; } static u32 modtest_get_rxfh_key_size(struct net_device *netdev) { return key_size; } static int modtest_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key) { WARN_ON(!indir && !key); if (indir) memcpy(indir, modtest_indir, indir_size * sizeof(*indir)); if (key) memcpy(key, modtest_key, key_size); return 0; } static int modtest_set_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key) { WARN_ON(!indir && !key); if (indir) memcpy(modtest_indir, indir, indir_size * sizeof(*indir)); if (key) memcpy(modtest_key, key, key_size); return 0; } static struct ethtool_ops modtest_ethtool_ops = { .get_rxnfc = modtest_get_rxnfc, .get_rxfh_indir_size = modtest_get_rxfh_indir_size, .get_rxfh_key_size = modtest_get_rxfh_key_size, .get_rxfh = modtest_get_rxfh, .set_rxfh = modtest_set_rxfh, }; static int modtest_init(void) { int rc; /* Also allow ethtool ops to be removed */ if (indir_size < 0) modtest_ethtool_ops.get_rxfh_indir_size = NULL; if (key_size < 0) modtest_ethtool_ops.get_rxfh_key_size = NULL; if (indir_size < 0 && key_size < 0) { modtest_ethtool_ops.get_rxfh = NULL; modtest_ethtool_ops.set_rxfh = NULL; } modtest_indir = kcalloc(indir_size, sizeof(*modtest_indir), GFP_KERNEL); if (!modtest_indir) return -ENOMEM; modtest_key = kzalloc(key_size, GFP_KERNEL); if (!modtest_key) { rc = -ENOMEM; goto fail_free_indir; } modtest_netdev = alloc_etherdev(0); if (!modtest_netdev) { rc = -ENOMEM; goto fail_free_key; } modtest_netdev->netdev_ops = &modtest_netdev_ops; modtest_netdev->ethtool_ops = &modtest_ethtool_ops; strcpy(modtest_netdev->name, "modtest%d"); rc = register_netdev(modtest_netdev); if (rc) goto fail_free_netdev; return 0; fail_free_netdev: free_netdev(modtest_netdev); fail_free_key: kfree(modtest_key); fail_free_indir: kfree(modtest_indir); return rc; } static void modtest_exit(void) { unregister_netdev(modtest_netdev); free_netdev(modtest_netdev); kfree(modtest_key); kfree(modtest_indir); } module_init(modtest_init); module_exit(modtest_exit); MODULE_LICENSE("GPL");