OSDN Git Service

s390/qeth: add TX multiqueue support for IQD devices
authorJulian Wiedmann <jwi@linux.ibm.com>
Wed, 17 Apr 2019 16:17:32 +0000 (18:17 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 17 Apr 2019 17:33:59 +0000 (10:33 -0700)
qeth has been supporting multiple HW Output Queues for a long time. But
rather than exposing those queues to the stack, it uses its own queue
selection logic in .ndo_start_xmit... with all the drawbacks that
entails.
Start off by switching IQD devices over to a proper mqs net_device,
and converting all the netdev_queue management code.

One oddity with IQD devices is the requirement to place all mcast
traffic on the _highest_ established HW queue. Doing so via
.ndo_select_queue seems straight-forward - but that won't work if only
some of the HW queues are active
(ie. when dev->real_num_tx_queues < dev->num_tx_queues), since
netdev_cap_txqueue() will not allow us to put skbs on the higher queues.

To make this work, we
1. let .ndo_select_queue() map all mcast traffic to netdev_queue 0, and
2. later re-map the netdev_queue and HW queue indices in
   .ndo_start_xmit and the TX completion handler.

With this patch we default to a fixed set of 1 ucast and 1 mcast queue.
Support for dynamic reconfiguration is added at a later time.

Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_ethtool.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c

index 7309301..836cde6 100644 (file)
@@ -219,6 +219,9 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
 /* QDIO queue and buffer handling                                            */
 /*****************************************************************************/
 #define QETH_MAX_QUEUES 4
+#define QETH_IQD_MIN_TXQ       2       /* One for ucast, one for mcast. */
+#define QETH_IQD_MCAST_TXQ     0
+#define QETH_IQD_MIN_UCAST_TXQ 1
 #define QETH_IN_BUF_SIZE_DEFAULT 65536
 #define QETH_IN_BUF_COUNT_DEFAULT 64
 #define QETH_IN_BUF_COUNT_HSDEFAULT 128
@@ -835,6 +838,15 @@ static inline bool qeth_netdev_is_registered(struct net_device *dev)
        return dev->netdev_ops != NULL;
 }
 
+static inline u16 qeth_iqd_translate_txq(struct net_device *dev, u16 txq)
+{
+       if (txq == QETH_IQD_MCAST_TXQ)
+               return dev->num_tx_queues - 1;
+       if (txq == dev->num_tx_queues - 1)
+               return QETH_IQD_MCAST_TXQ;
+       return txq;
+}
+
 static inline void qeth_scrub_qdio_buffer(struct qdio_buffer *buf,
                                          unsigned int elements)
 {
@@ -934,10 +946,8 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
                            int ipv);
 static inline struct qeth_qdio_out_q *qeth_get_tx_queue(struct qeth_card *card,
                                                        struct sk_buff *skb,
-                                                       int ipv, int cast_type)
+                                                       int ipv)
 {
-       if (IS_IQD(card) && cast_type != RTN_UNICAST)
-               return card->qdio.out_qs[card->qdio.no_out_queues - 1];
        if (!card->qdio.do_prio_queueing)
                return card->qdio.out_qs[card->qdio.default_out_queue];
        return card->qdio.out_qs[qeth_get_priority_queue(card, skb, ipv)];
@@ -1022,6 +1032,8 @@ netdev_features_t qeth_features_check(struct sk_buff *skb,
                                      struct net_device *dev,
                                      netdev_features_t features);
 void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats);
+u16 qeth_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
+                         u8 cast_type, struct net_device *sb_dev);
 int qeth_open(struct net_device *dev);
 int qeth_stop(struct net_device *dev);
 
index 09ed9e0..68f6043 100644 (file)
@@ -3485,7 +3485,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err,
        QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err);
 
        if (qdio_err) {
-               netif_stop_queue(card->dev);
+               netif_tx_stop_all_queues(card->dev);
                qeth_schedule_recovery(card);
                return;
        }
@@ -3541,12 +3541,14 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev,
        struct qeth_card *card        = (struct qeth_card *) card_ptr;
        struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue];
        struct qeth_qdio_out_buffer *buffer;
+       struct net_device *dev = card->dev;
+       u16 txq;
        int i;
 
        QETH_CARD_TEXT(card, 6, "qdouhdl");
        if (qdio_error & QDIO_ERROR_FATAL) {
                QETH_CARD_TEXT(card, 2, "achkcond");
-               netif_stop_queue(card->dev);
+               netif_tx_stop_all_queues(dev);
                qeth_schedule_recovery(card);
                return;
        }
@@ -3595,7 +3597,8 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev,
        if (card->info.type != QETH_CARD_TYPE_IQD)
                qeth_check_outbound_queue(queue);
 
-       netif_wake_queue(queue->card->dev);
+       txq = IS_IQD(card) ? qeth_iqd_translate_txq(dev, __queue) : 0;
+       netif_wake_subqueue(dev, txq);
 }
 
 /* We cannot use outbound queue 3 for unicast packets on HiperSockets */
@@ -5557,7 +5560,8 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
 
        switch (card->info.type) {
        case QETH_CARD_TYPE_IQD:
-               dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, ether_setup);
+               dev = alloc_netdev_mqs(0, "hsi%d", NET_NAME_UNKNOWN,
+                                      ether_setup, QETH_MAX_QUEUES, 1);
                break;
        case QETH_CARD_TYPE_OSN:
                dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup);
@@ -5585,8 +5589,10 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
                dev->priv_flags &= ~IFF_TX_SKB_SHARING;
                dev->hw_features |= NETIF_F_SG;
                dev->vlan_features |= NETIF_F_SG;
-               if (IS_IQD(card))
+               if (IS_IQD(card)) {
+                       netif_set_real_num_tx_queues(dev, QETH_IQD_MIN_TXQ);
                        dev->features |= NETIF_F_SG;
+               }
        }
 
        return dev;
@@ -6203,6 +6209,15 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 }
 EXPORT_SYMBOL_GPL(qeth_get_stats64);
 
+u16 qeth_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
+                         u8 cast_type, struct net_device *sb_dev)
+{
+       if (cast_type != RTN_UNICAST)
+               return QETH_IQD_MCAST_TXQ;
+       return QETH_IQD_MIN_UCAST_TXQ;
+}
+EXPORT_SYMBOL_GPL(qeth_iqd_select_queue);
+
 int qeth_open(struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
@@ -6213,7 +6228,7 @@ int qeth_open(struct net_device *dev)
                return -EIO;
 
        card->data.state = CH_STATE_UP;
-       netif_start_queue(dev);
+       netif_tx_start_all_queues(dev);
 
        napi_enable(&card->napi);
        local_bh_disable();
index 56deeb6..b43d8bd 100644 (file)
@@ -198,6 +198,9 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,
        if (!card)
                return -EINVAL;
 
+       if (IS_IQD(card))
+               return -EOPNOTSUPP;
+
        mutex_lock(&card->conf_mutex);
        if (card->state != CARD_STATE_DOWN) {
                rc = -EPERM;
index 93a53fe..a443e5f 100644 (file)
@@ -154,6 +154,21 @@ static void qeth_get_drvinfo(struct net_device *dev,
                 CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card));
 }
 
+static void qeth_get_channels(struct net_device *dev,
+                             struct ethtool_channels *channels)
+{
+       struct qeth_card *card = dev->ml_priv;
+
+       channels->max_rx = dev->num_rx_queues;
+       channels->max_tx = card->qdio.no_out_queues;
+       channels->max_other = 0;
+       channels->max_combined = 0;
+       channels->rx_count = dev->real_num_rx_queues;
+       channels->tx_count = dev->real_num_tx_queues;
+       channels->other_count = 0;
+       channels->combined_count = 0;
+}
+
 /* Helper function to fill 'advertising' and 'supported' which are the same. */
 /* Autoneg and full-duplex are supported and advertised unconditionally.     */
 /* Always advertise and support all speeds up to specified, and only one     */
@@ -359,6 +374,7 @@ const struct ethtool_ops qeth_ethtool_ops = {
        .get_ethtool_stats = qeth_get_ethtool_stats,
        .get_sset_count = qeth_get_sset_count,
        .get_drvinfo = qeth_get_drvinfo,
+       .get_channels = qeth_get_channels,
        .get_link_ksettings = qeth_get_link_ksettings,
 };
 
index 641dc17..1491281 100644 (file)
@@ -161,10 +161,8 @@ static void qeth_l2_drain_rx_mode_cache(struct qeth_card *card)
        }
 }
 
-static int qeth_l2_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
+static int qeth_l2_get_cast_type(struct sk_buff *skb)
 {
-       if (card->info.type == QETH_CARD_TYPE_OSN)
-               return RTN_UNICAST;
        if (is_broadcast_ether_addr(skb->data))
                return RTN_BROADCAST;
        if (is_multicast_ether_addr(skb->data))
@@ -603,26 +601,29 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
                                           struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
-       int cast_type = qeth_l2_get_cast_type(card, skb);
+       u16 txq = skb_get_queue_mapping(skb);
        int ipv = qeth_get_ip_version(skb);
        struct qeth_qdio_out_q *queue;
        int tx_bytes = skb->len;
        int rc;
 
-       queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
+       if (IS_IQD(card))
+               queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)];
+       else
+               queue = qeth_get_tx_queue(card, skb, ipv);
 
-       netif_stop_queue(dev);
+       netif_stop_subqueue(dev, txq);
 
        if (IS_OSN(card))
                rc = qeth_l2_xmit_osn(card, skb, queue);
        else
-               rc = qeth_xmit(card, skb, queue, ipv, cast_type,
-                              qeth_l2_fill_header);
+               rc = qeth_xmit(card, skb, queue, ipv,
+                              qeth_l2_get_cast_type(skb), qeth_l2_fill_header);
 
        if (!rc) {
                QETH_TXQ_STAT_INC(queue, tx_packets);
                QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes);
-               netif_wake_queue(dev);
+               netif_wake_subqueue(dev, txq);
                return NETDEV_TX_OK;
        } else if (rc == -EBUSY) {
                return NETDEV_TX_BUSY;
@@ -630,10 +631,17 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
 
        QETH_TXQ_STAT_INC(queue, tx_dropped);
        kfree_skb(skb);
-       netif_wake_queue(dev);
+       netif_wake_subqueue(dev, txq);
        return NETDEV_TX_OK;
 }
 
+static u16 qeth_l2_select_queue(struct net_device *dev, struct sk_buff *skb,
+                               struct net_device *sb_dev)
+{
+       return qeth_iqd_select_queue(dev, skb, qeth_l2_get_cast_type(skb),
+                                    sb_dev);
+}
+
 static const struct device_type qeth_l2_devtype = {
        .name = "qeth_layer2",
        .groups = qeth_l2_attr_groups,
@@ -687,6 +695,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
        .ndo_get_stats64        = qeth_get_stats64,
        .ndo_start_xmit         = qeth_l2_hard_start_xmit,
        .ndo_features_check     = qeth_features_check,
+       .ndo_select_queue       = qeth_l2_select_queue,
        .ndo_validate_addr      = qeth_l2_validate_addr,
        .ndo_set_rx_mode        = qeth_l2_set_rx_mode,
        .ndo_do_ioctl           = qeth_do_ioctl,
index 102be69..120193e 100644 (file)
@@ -2062,28 +2062,36 @@ static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
 static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
                                           struct net_device *dev)
 {
-       int cast_type = qeth_l3_get_cast_type(skb);
        struct qeth_card *card = dev->ml_priv;
+       u16 txq = skb_get_queue_mapping(skb);
        int ipv = qeth_get_ip_version(skb);
        struct qeth_qdio_out_q *queue;
        int tx_bytes = skb->len;
-       int rc;
-
-       queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
+       int cast_type, rc;
 
        if (IS_IQD(card)) {
+               queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)];
+
                if (card->options.sniffer)
                        goto tx_drop;
                if ((card->options.cq != QETH_CQ_ENABLED && !ipv) ||
                    (card->options.cq == QETH_CQ_ENABLED &&
                     skb->protocol != htons(ETH_P_AF_IUCV)))
                        goto tx_drop;
+
+               if (txq == QETH_IQD_MCAST_TXQ)
+                       cast_type = qeth_l3_get_cast_type(skb);
+               else
+                       cast_type = RTN_UNICAST;
+       } else {
+               queue = qeth_get_tx_queue(card, skb, ipv);
+               cast_type = qeth_l3_get_cast_type(skb);
        }
 
        if (cast_type == RTN_BROADCAST && !card->info.broadcast_capable)
                goto tx_drop;
 
-       netif_stop_queue(dev);
+       netif_stop_subqueue(dev, txq);
 
        if (ipv == 4 || IS_IQD(card))
                rc = qeth_l3_xmit(card, skb, queue, ipv, cast_type);
@@ -2094,7 +2102,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
        if (!rc) {
                QETH_TXQ_STAT_INC(queue, tx_packets);
                QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes);
-               netif_wake_queue(dev);
+               netif_wake_subqueue(dev, txq);
                return NETDEV_TX_OK;
        } else if (rc == -EBUSY) {
                return NETDEV_TX_BUSY;
@@ -2103,7 +2111,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
 tx_drop:
        QETH_TXQ_STAT_INC(queue, tx_dropped);
        kfree_skb(skb);
-       netif_wake_queue(dev);
+       netif_wake_subqueue(dev, txq);
        return NETDEV_TX_OK;
 }
 
@@ -2147,11 +2155,19 @@ static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
        return qeth_features_check(skb, dev, features);
 }
 
+static u16 qeth_l3_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
+                                   struct net_device *sb_dev)
+{
+       return qeth_iqd_select_queue(dev, skb, qeth_l3_get_cast_type(skb),
+                                    sb_dev);
+}
+
 static const struct net_device_ops qeth_l3_netdev_ops = {
        .ndo_open               = qeth_open,
        .ndo_stop               = qeth_stop,
        .ndo_get_stats64        = qeth_get_stats64,
        .ndo_start_xmit         = qeth_l3_hard_start_xmit,
+       .ndo_select_queue       = qeth_l3_iqd_select_queue,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_rx_mode        = qeth_l3_set_rx_mode,
        .ndo_do_ioctl           = qeth_do_ioctl,