@@ -2062,6 +2062,43 @@ static inline int skb_bond_should_drop(struct sk_buff *skb)
extern struct pernet_operations __net_initdata loopback_net_ops;
+#ifdef CONFIG_SMP
+extern int netdev_reserve_cpu(int cpu);
+extern int netdev_release_cpu(int cpu);
+extern int netdev_request_cpu(void);
+extern int netdev_request_cpu_mask(struct cpumask *exclude_mask);
+extern int netdev_map_to_cpu(unsigned int index);
+#else
+/*
+ * raw_smp_processor_id() will be 0 and the 'cpu' argument will be 0;
+ * and it's OK to reserve/release ourselves.
+ */
+static inline int netdev_reserve_cpu(int cpu)
+{
+ return 0;
+}
+
+static inline int netdev_release_cpu(int cpu)
+{
+ return 0;
+}
+
+static inline int netdev_request_cpu(void)
+{
+ return 0;
+}
+
+static inline int netdev_request_cpu_mask(struct cpumask *exclude_mask)
+{
+ return cpumask_test_cpu(0, exclude_mask) ? -EAGAIN : 0;
+}
+
+static inline int netdev_map_to_cpu(unsigned int index)
+{
+ return 0;
+}
+#endif /* CONFIG_SMP */
+
static inline int dev_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
@@ -5299,6 +5299,135 @@ static void netdev_init_queues(struct net_device *dev)
spin_lock_init(&dev->tx_global_lock);
}
+/*
+ * CPU reservation
+ */
+#ifdef CONFIG_SMP
+static u16 netdev_cpu_usage_map[NR_CPUS] = { 0 };
+static DEFINE_SPINLOCK(netdev_cpu_map_lock);
+
+/*
+ * netdev_map_to_cpu maps an IRQ to a CPU number. It can be replaced by
+ * an architecture-specific version if this version is unlikely to
+ * produce good performance.
+ */
+int __weak netdev_map_to_cpu(unsigned int index)
+{
+ return index % NR_CPUS;
+}
+
+/*
+ * "Reserving" a CPU is simply a logical operation meant to distribute
+ * incoming packet streams to CPUs for cache optimization. We keep
+ * a count of reservations per cpu number; see below for more details.
+ */
+int netdev_reserve_cpu(int cpu)
+{
+ if ((cpu < 0) || (cpu > ARRAY_SIZE(netdev_cpu_usage_map)))
+ return -EINVAL;
+
+ if (!cpu_online(cpu))
+ return -ENXIO;
+
+ spin_lock(&netdev_cpu_map_lock);
+ netdev_cpu_usage_map[cpu]++;
+ spin_unlock(&netdev_cpu_map_lock);
+ return 0;
+}
+
+int netdev_release_cpu(int cpu)
+{
+ int ret;
+
+ if ((cpu < 0) || (cpu > ARRAY_SIZE(netdev_cpu_usage_map)))
+ return -EINVAL;
+
+ if (!cpu_online(cpu))
+ return -ENXIO;
+
+ spin_lock(&netdev_cpu_map_lock);
+ if (netdev_cpu_usage_map[cpu] == 0)
+ ret = -EINVAL;
+ else {
+ netdev_cpu_usage_map[cpu]--;
+ ret = 0;
+ }
+ spin_unlock(&netdev_cpu_map_lock);
+ return ret;
+}
+
+/* Pick any least-loaded cpu; see netdev_request_cpu_mask. */
+int netdev_request_cpu(void)
+{
+ return netdev_request_cpu_mask(NULL);
+}
+
+/*
+ * netdev_request_cpu_mask is the main use point for CPU reservation.
+ * We find the CPU with the fewest reservations against it that is not
+ * also "pre-excluded" via the supplied mask. This allows the caller to
+ * pick a lightly-loaded CPU, then pick the next-best CPU excluding the
+ * one just picked, then pick the third-best excluding the previous two,
+ * and so on.
+ *
+ * The intent is to allow demultiplexing of incoming streaming data by
+ * distributing each stream to the "least-loaded" CPU that is not involved
+ * in any other streams associated with a particular network device.
+ */
+int netdev_request_cpu_mask(struct cpumask *exclude_mask)
+{
+ int cpu_id, min_index, ret, i;
+ u16 min_val;
+
+ min_index = -1;
+ min_val = USHORT_MAX;
+
+ spin_lock(&netdev_cpu_map_lock);
+ for (i = 0; i < ARRAY_SIZE(netdev_cpu_usage_map); i++) {
+ cpu_id = netdev_map_to_cpu(i);
+
+ if (!cpu_online(cpu_id))
+ continue;
+
+ if (exclude_mask && cpumask_test_cpu(cpu_id, exclude_mask))
+ continue;
+
+ if (netdev_cpu_usage_map[cpu_id] == 0) {
+ netdev_cpu_usage_map[cpu_id]++;
+ ret = cpu_id;
+ goto out_unlock;
+ }
+
+ if (netdev_cpu_usage_map[cpu_id] < min_val) {
+ min_val = netdev_cpu_usage_map[cpu_id];
+ min_index = cpu_id;
+ }
+ }
+
+ /*
+ * Can only happen if there are no online CPUs, or all CPUs have
+ * been excluded.
+ */
+ if (min_val == USHORT_MAX) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+
+ netdev_cpu_usage_map[min_index]++;
+ ret = min_index;
+
+out_unlock:
+ spin_unlock(&netdev_cpu_map_lock);
+ return ret;
+}
+
+EXPORT_SYMBOL(netdev_reserve_cpu);
+EXPORT_SYMBOL(netdev_release_cpu);
+EXPORT_SYMBOL(netdev_request_cpu);
+EXPORT_SYMBOL(netdev_request_cpu_mask);
+EXPORT_SYMBOL(netdev_map_to_cpu);
+#endif /* CONFIG_SMP */
+
/**
* alloc_netdev_mq - allocate network device
* @sizeof_priv: size of private data to allocate space for