@@ -517,6 +517,47 @@ struct ethtool_flash {
char data[ETHTOOL_FLASH_MAX_FILENAME];
};
+/* Network channel information. A network channel is the combination of
+ * an IRQ and all the queues that can trigger that IRQ. */
+struct ethtool_channels {
+ __u32 cmd;
+ __u32 combined_count; /* number of multi-purpose channels */
+ __u32 rx_count; /* number of RX-only channels */
+ __u32 tx_count; /* number of TX-only channels */
+ __u32 other_count; /* number of additional channels */
+};
+
+/* Channel ID is made up of a type and an index */
+enum ethtool_channel_id {
+ ETH_CHAN_INDEX_MASK = 0x0fffffff,
+ ETH_CHAN_TYPE_MASK = 0xc0000000,
+ ETH_CHAN_TYPE_COMBINED = 0x00000000,
+ ETH_CHAN_TYPE_RX = 0x40000000,
+ ETH_CHAN_TYPE_TX = 0x80000000,
+ ETH_CHAN_TYPE_OTHER = 0xc0000000,
+ ETH_CHAN_ALL = 0xffffffff /* special: operate on all */
+};
+
+/* Special NUMA node IDs */
+enum ethtool_numa_node {
+ ETH_NUMA_NODE_UNSET = -1, /* get/set: no affinity set */
+ ETH_NUMA_NODE_N_A = -2, /* get/set: not applicable; channel
+ * doesn't have this object */
+ ETH_NUMA_NODE_IRQ = -3, /* set: match current IRQ affinity */
+ ETH_NUMA_NODE_DEV = -4, /* set: match device affinity */
+};
+
+struct ethtool_affinity {
+ __u32 cmd;
+ __u32 channel_id; /* channel type and index; may be
+ * ETH_CHAN_ALL when setting */
+ __s32 rx_ring_node; /* affinity of RX descriptor ring */
+ __s32 tx_ring_node; /* affinity of TX descriptor ring */
+ __s32 event_ring_node; /* affinity of event/completion ring */
+ __s32 handler_data_node; /* affinity of IRQ/NAPI handler's
+ * software structures */
+};
+
#ifdef __KERNEL__
#include <linux/rculist.h>
@@ -551,6 +592,9 @@ int ethtool_op_set_ufo(struct net_device *dev, u32 data);
u32 ethtool_op_get_flags(struct net_device *dev);
int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported);
void ethtool_ntuple_flush(struct net_device *dev);
+#ifdef CONFIG_NUMA
+int ethtool_affinity_resolve(s32 node_id, struct net_device *dev, unsigned irq);
+#endif
/**
* ðtool_ops - Alter and report network device settings
@@ -672,6 +716,14 @@ struct ethtool_ops {
struct ethtool_rxfh_indir *);
int (*set_rxfh_indir)(struct net_device *,
const struct ethtool_rxfh_indir *);
+ int (*get_channels)(struct net_device *, struct ethtool_channels *);
+ int (*set_channels)(struct net_device *,
+ const struct ethtool_channels *);
+#ifdef CONFIG_NUMA
+ int (*get_affinity)(struct net_device *, struct ethtool_affinity *);
+ int (*set_affinity)(struct net_device *,
+ const struct ethtool_affinity *);
+#endif
};
#endif /* __KERNEL__ */
@@ -735,6 +787,10 @@ struct ethtool_ops {
#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */
#define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */
#define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */
+#define ETHTOOL_GCHANNELS 0x0000003a /* Get numbers of channels */
+#define ETHTOOL_SCHANNELS 0x0000003b /* Set numbers of channels */
+#define ETHTOOL_GAFFINITY 0x0000003c /* Get NUMA affinity */
+#define ETHTOOL_SAFFINITY 0x0000003d /* Set NUMA affinity */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
@@ -20,6 +20,8 @@
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/numa.h>
/*
* Some useful ethtool_ops methods that're device independent.
@@ -1429,6 +1431,155 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
return dev->ethtool_ops->flash_device(dev, &efl);
}
+static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
+ char __user *useraddr)
+{
+ struct ethtool_channels channels;
+ int rc;
+
+ if (!dev->ethtool_ops->get_channels)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&channels, useraddr, sizeof(channels)))
+ return -EFAULT;
+
+ rc = dev->ethtool_ops->get_channels(dev, &channels);
+ if (rc)
+ return rc;
+
+ if (copy_to_user(useraddr, &channels, sizeof(channels)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
+ char __user *useraddr)
+{
+ struct ethtool_channels channels;
+
+ if (!dev->ethtool_ops->set_channels)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&channels, useraddr, sizeof(channels)))
+ return -EFAULT;
+
+ return dev->ethtool_ops->set_channels(dev, &channels);
+}
+
+#ifdef CONFIG_NUMA
+
+static int ethtool_check_channel_id(struct net_device *dev, u32 channel_id)
+{
+ struct ethtool_channels channels;
+ u32 channel_count;
+ int rc;
+
+ if (!dev->ethtool_ops->get_channels)
+ return -EOPNOTSUPP;
+
+ memset(&channels, 0, sizeof(channels));
+ channels.cmd = ETHTOOL_GCHANNELS;
+ rc = dev->ethtool_ops->get_channels(dev, &channels);
+ if (rc)
+ return rc;
+
+ switch (channel_id & ETH_CHAN_TYPE_MASK) {
+ case ETH_CHAN_TYPE_COMBINED:
+ channel_count = channels.combined_count;
+ break;
+ case ETH_CHAN_TYPE_RX:
+ channel_count = channels.rx_count;
+ break;
+ case ETH_CHAN_TYPE_TX:
+ channel_count = channels.tx_count;
+ break;
+ case ETH_CHAN_TYPE_OTHER:
+ default:
+ channel_count = channels.other_count;
+ break;
+ }
+ if ((channel_id & ETH_CHAN_INDEX_MASK) >= channel_count)
+ return -EINVAL;
+
+ return 0;
+}
+
+static noinline_for_stack int ethtool_get_affinity(struct net_device *dev,
+ char __user *useraddr)
+{
+ struct ethtool_affinity affin;
+ int rc;
+
+ if (!dev->ethtool_ops->get_affinity)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&affin, useraddr, sizeof(affin)))
+ return -EFAULT;
+
+ rc = ethtool_check_channel_id(dev, affin.channel_id);
+ if (rc)
+ return rc;
+
+ rc = dev->ethtool_ops->get_affinity(dev, &affin);
+ if (rc)
+ return rc;
+
+ if (copy_to_user(useraddr, &affin, sizeof(affin)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static noinline_for_stack int ethtool_set_affinity(struct net_device *dev,
+ char __user *useraddr)
+{
+ struct ethtool_affinity affin;
+ int rc;
+
+ if (!dev->ethtool_ops->set_affinity)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&affin, useraddr, sizeof(affin)))
+ return -EFAULT;
+
+ if (affin.channel_id != ETH_CHAN_ALL) {
+ rc = ethtool_check_channel_id(dev, affin.channel_id);
+ if (rc)
+ return rc;
+ }
+
+ return dev->ethtool_ops->set_affinity(dev, &affin);
+}
+
+/**
+ * ethtool_affinity_resolve - resolve the NUMA node ID for a channel
+ * @node_id: User-specified node ID
+ * @dev: The channel's net device
+ * @irq: The channel's IRQ
+ *
+ * This resolves the special node IDs %ETH_NUMA_NODE_IRQ,
+ * %ETH_NUMA_NODE_DEV and %ETH_NUMA_NODE_UNSET and validates that any
+ * other specified node ID is in the valid range. It returns either a
+ * specific node ID, %NUMA_NO_NODE or a negative error code (less than
+ * %NUMA_NO_NODE).
+ */
+int ethtool_affinity_resolve(s32 node_id, struct net_device *dev, unsigned irq)
+{
+ if (node_id == ETH_NUMA_NODE_IRQ)
+ return irq_get_numa_node(irq);
+ if (node_id == ETH_NUMA_NODE_DEV && dev->dev.parent)
+ return dev_to_node(dev->dev.parent);
+ if (node_id == ETH_NUMA_NODE_UNSET)
+ return NUMA_NO_NODE;
+ if (node_id >= 0 && node_id < MAX_NUMNODES && nr_cpus_node(node_id))
+ return node_id;
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ethtool_affinity_resolve);
+
+#endif /* CONFIG_NUMA */
+
/* The main entry point in this file. Called from net/core/dev.c */
int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -1673,6 +1824,20 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_SRXFHINDIR:
rc = ethtool_set_rxfh_indir(dev, useraddr);
break;
+ case ETHTOOL_GCHANNELS:
+ rc = ethtool_get_channels(dev, useraddr);
+ break;
+ case ETHTOOL_SCHANNELS:
+ rc = ethtool_set_channels(dev, useraddr);
+ break;
+#ifdef CONFIG_NUMA
+ case ETHTOOL_GAFFINITY:
+ rc = ethtool_get_affinity(dev, useraddr);
+ break;
+ case ETHTOOL_SAFFINITY:
+ rc = ethtool_set_affinity(dev, useraddr);
+ break;
+#endif
default:
rc = -EOPNOTSUPP;
}