compat: backport netif_set_real_num_tx_queues()
authorLuis R. Rodriguez <mcgrof@do-not-panic.com>
Mon, 3 Dec 2012 23:40:21 +0000 (15:40 -0800)
committerLuis R. Rodriguez <mcgrof@do-not-panic.com>
Tue, 4 Dec 2012 01:12:06 +0000 (17:12 -0800)
This was added as of v2.6.35. We implement a slightly
modified version given that this was later changed
to return int through commit e6484930. The changes
on e6484930 however were added due to some internal
changes on core networking, we'll leave in place
a simpler implementation for older kernels.

Note that for older kernels than 2.6.27 we use
the netdev egress_subqueue_count to set the TX
queue count. Commit fd2ea0a7 added real_num_tx_queues
to the netdevice when Multiqueue was added.

mcgrof@frijol ~/linux-next (git::master)$ git describe --contains f0796d5c73e59786d09a1e617689d1d415f2db44
v2.6.35-rc5~14^2~14

mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 3171d026
v2.6.37-rc1~147^2~339

mcgrof@frijol ~/linux-next (git::master)$ git describe --contains fd2ea0a7
v2.6.27-rc1~964^2~78

commit f0796d5c73e59786d09a1e617689d1d415f2db44
Author: John Fastabend <john.r.fastabend@intel.com>
Date:   Thu Jul 1 13:21:57 2010 +0000

    net: decreasing real_num_tx_queues needs to flush qdisc

    Reducing real_num_queues needs to flush the qdisc otherwise
    skbs with queue_mappings greater then real_num_tx_queues can
    be sent to the underlying driver.

    The flow for this is,

    dev_queue_xmit()
        dev_pick_tx()
                skb_tx_hash()  => hash using real_num_tx_queues
                skb_set_queue_mapping()
        ...
        qdisc_enqueue_root() => enqueue skb on txq from hash
    ...
    dev->real_num_tx_queues -= n
    ...
    sch_direct_xmit()
        dev_hard_start_xmit()
                ndo_start_xmit(skb,dev) => skb queue set with old hash

    skbs are enqueued on the qdisc with skb->queue_mapping set
    0 < queue_mappings < real_num_tx_queues.  When the driver
    decreases real_num_tx_queues skb's may be dequeued from the
    qdisc with a queue_mapping greater then real_num_tx_queues.

    This fixes a case in ixgbe where this was occurring with DCB
    and FCoE. Because the driver is using queue_mapping to map
    skbs to tx descriptor rings we can potentially map skbs to
    rings that no longer exist.

Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
Tested-by: Ross Brattain <ross.b.brattain@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
commit e6484930d7c73d324bccda7d43d131088da697b9
Author: Tom Herbert <therbert@google.com>
Date:   Mon Oct 18 18:04:39 2010 +0000

    net: allocate tx queues in register_netdevice

    This patch introduces netif_alloc_netdev_queues which is called from
    register_device instead of alloc_netdev_mq.  This makes TX queue
    allocation symmetric with RX allocation.  Also, queue locks allocation
    is done in netdev_init_one_queue.  Change set_real_num_tx_queues to
    fail if requested number < 1 or greater than number of allocated
    queues.

Signed-off-by: Tom Herbert <therbert@google.com>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
commit fd2ea0a79faad824258af5dcec1927aa24d81c16
Author: David S. Miller <davem@davemloft.net>
Date:   Thu Jul 17 01:56:23 2008 -0700

    net: Use queue aware tests throughout.

    This effectively "flips the switch" by making the core networking
    and multiqueue-aware drivers use the new TX multiqueue structures.

    Non-multiqueue drivers need no changes.  The interfaces they use such
    as netif_stop_queue() degenerate into an operation on TX queue zero.
    So everything "just works" for them.

    Code that really wants to do "X" to all TX queues now invokes a
    routine that does so, such as netif_tx_wake_all_queues(),
    netif_tx_stop_all_queues(), etc.

    pktgen and netpoll required a little bit more surgery than the others.

    In particular the pktgen changes, whilst functional, could be largely
    improved.  The initial check in pktgen_xmit() will sometimes check the
    wrong queue, which is mostly harmless.  The thing to do is probably to
    invoke fill_packet() earlier.

    The bulk of the netpoll changes is to make the code operate solely on
    the TX queue indicated by by the SKB queue mapping.

    Setting of the SKB queue mapping is entirely confined inside of
    net/core/dev.c:dev_pick_tx().  If we end up needing any kind of
    special semantics (drops, for example) it will be implemented here.

    Finally, we now have a "real_num_tx_queues" which is where the driver
    indicates how many TX queues are actually active.

    With IGB changes from Jeff Kirsher.

Signed-off-by: David S. Miller <davem@davemloft.net>
mcgrof@drvbp1 ~/compat (git::master)$ time ckmake
Trying kernel 3.6.5-030605-generic [OK]
Trying kernel 3.5.7-030507-generic [OK]
Trying kernel 3.4.17-030417-generic [OK]
Trying kernel 3.3.8-030308-generic [OK]
Trying kernel 3.2.33-030233-generic [OK]
Trying kernel 3.1.10-030110-generic [OK]
Trying kernel 3.0.50-030050-generic [OK]
Trying kernel 2.6.39-02063904-generic [OK]
Trying kernel 2.6.38-02063808-generic [OK]
Trying kernel 2.6.37-02063706-generic [OK]
Trying kernel 2.6.36-02063604-generic [OK]
Trying kernel 2.6.35-02063513-generic [OK]
Trying kernel 2.6.34-02063413-generic [OK]
Trying kernel 2.6.33-02063320-generic [OK]
Trying kernel 2.6.32-02063260-generic [OK]
Trying kernel 2.6.31-02063113-generic [OK]
Trying kernel 2.6.30-02063010-generic [OK]
Trying kernel 2.6.29-02062906-generic [OK]
Trying kernel 2.6.28-02062810-generic [OK]
Trying kernel 2.6.27-020627-generic [OK]
Trying kernel 2.6.26-020626-generic [OK]
Trying kernel 2.6.25-020625-generic [OK]
Trying kernel 2.6.24-020624-generic [OK]

real    1m51.110s
user    4m58.571s
sys     1m39.282s

Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
compat/compat-2.6.35.c
include/linux/compat-2.6.35.h

index 24551722274dbb2ac5e37d173305ab2acf89cbce..c2c54bb8734a43039612f7c496495570605ac858 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright 2010    Kshitij Kulshreshtha <kkhere.geo@gmail.com>
+ * Copyright 2012    Luis R. Rodriguez <mcgrof@do-not-panic.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <linux/compat.h>
 #include <linux/ctype.h>
 
+/*
+ * Routine to help set real_num_tx_queues. To avoid skbs mapped to queues
+ * greater then real_num_tx_queues stale skbs on the qdisc must be flushed.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
+int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq)
+{
+       unsigned int real_num = dev->real_num_tx_queues;
+
+       if (unlikely(txq > dev->num_tx_queues))
+               return -EINVAL;
+       else if (txq > real_num)
+               dev->real_num_tx_queues = txq;
+       else if (txq < real_num) {
+               dev->real_num_tx_queues = txq;
+               qdisc_reset_all_tx_gt(dev, txq);
+       }
+}
+#else
+int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq)
+{
+       dev->egress_subqueue_count = txq;
+       /* XXX: consider qdisc reset for older kernels */
+}
+#endif
+EXPORT_SYMBOL_GPL(netif_set_real_num_tx_queues);
+
 /**
  * hex_to_bin - convert a hex digit to its real value
  * @ch: ascii character represents hex digit
index d184f00ed53623d48db5fcbec0eb888674454c90..22981a94fcfaa1bd4476870cee1d4b99cc37f1c7 100644 (file)
@@ -8,6 +8,8 @@
 #include <net/sock.h>
 #include <linux/types.h>
 #include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <net/sch_generic.h>
 
 /* added on linux/kernel.h */
 #define USHRT_MAX      ((u16)(~0U))
 
 #define netdev_hw_addr dev_mc_list
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27))
+/* Reset all TX qdiscs greater then index of a device.  */
+static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
+{
+       struct Qdisc *qdisc;
+
+       for (; i < dev->num_tx_queues; i++) {
+               qdisc = netdev_get_tx_queue(dev, i)->qdisc;
+               if (qdisc) {
+                       spin_lock_bh(qdisc_lock(qdisc));
+                       qdisc_reset(qdisc);
+                       spin_unlock_bh(qdisc_lock(qdisc));
+               }
+       }
+}
+#else
+static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
+{
+}
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) */
+
+extern int netif_set_real_num_tx_queues(struct net_device *dev,
+                                       unsigned int txq);
+
 /* mask irq_set_affinity_hint as RHEL6 backports this */
 #define irq_set_affinity_hint(a,b) compat_irq_set_affinity_hint(a,b)
 /*