Commit 3a18d754 authored by Julian Wiedmann's avatar Julian Wiedmann Committed by David S. Miller

s390/qeth: add TX multiqueue support for IQD devices

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: default avatarJulian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 333ef9d1
...@@ -219,6 +219,9 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, ...@@ -219,6 +219,9 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
/* QDIO queue and buffer handling */ /* QDIO queue and buffer handling */
/*****************************************************************************/ /*****************************************************************************/
#define QETH_MAX_QUEUES 4 #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_SIZE_DEFAULT 65536
#define QETH_IN_BUF_COUNT_DEFAULT 64 #define QETH_IN_BUF_COUNT_DEFAULT 64
#define QETH_IN_BUF_COUNT_HSDEFAULT 128 #define QETH_IN_BUF_COUNT_HSDEFAULT 128
...@@ -835,6 +838,15 @@ static inline bool qeth_netdev_is_registered(struct net_device *dev) ...@@ -835,6 +838,15 @@ static inline bool qeth_netdev_is_registered(struct net_device *dev)
return dev->netdev_ops != NULL; 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, static inline void qeth_scrub_qdio_buffer(struct qdio_buffer *buf,
unsigned int elements) unsigned int elements)
{ {
...@@ -934,10 +946,8 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, ...@@ -934,10 +946,8 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
int ipv); int ipv);
static inline struct qeth_qdio_out_q *qeth_get_tx_queue(struct qeth_card *card, static inline struct qeth_qdio_out_q *qeth_get_tx_queue(struct qeth_card *card,
struct sk_buff *skb, 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) if (!card->qdio.do_prio_queueing)
return card->qdio.out_qs[card->qdio.default_out_queue]; return card->qdio.out_qs[card->qdio.default_out_queue];
return card->qdio.out_qs[qeth_get_priority_queue(card, skb, ipv)]; 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, ...@@ -1022,6 +1032,8 @@ netdev_features_t qeth_features_check(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
netdev_features_t features); netdev_features_t features);
void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats); 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_open(struct net_device *dev);
int qeth_stop(struct net_device *dev); int qeth_stop(struct net_device *dev);
......
...@@ -3485,7 +3485,7 @@ static void qeth_qdio_cq_handler(struct qeth_card *card, unsigned int qdio_err, ...@@ -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); QETH_CARD_TEXT_(card, 5, "qcqherr%d", qdio_err);
if (qdio_err) { if (qdio_err) {
netif_stop_queue(card->dev); netif_tx_stop_all_queues(card->dev);
qeth_schedule_recovery(card); qeth_schedule_recovery(card);
return; return;
} }
...@@ -3541,12 +3541,14 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, ...@@ -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_card *card = (struct qeth_card *) card_ptr;
struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue]; struct qeth_qdio_out_q *queue = card->qdio.out_qs[__queue];
struct qeth_qdio_out_buffer *buffer; struct qeth_qdio_out_buffer *buffer;
struct net_device *dev = card->dev;
u16 txq;
int i; int i;
QETH_CARD_TEXT(card, 6, "qdouhdl"); QETH_CARD_TEXT(card, 6, "qdouhdl");
if (qdio_error & QDIO_ERROR_FATAL) { if (qdio_error & QDIO_ERROR_FATAL) {
QETH_CARD_TEXT(card, 2, "achkcond"); QETH_CARD_TEXT(card, 2, "achkcond");
netif_stop_queue(card->dev); netif_tx_stop_all_queues(dev);
qeth_schedule_recovery(card); qeth_schedule_recovery(card);
return; return;
} }
...@@ -3595,7 +3597,8 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev, ...@@ -3595,7 +3597,8 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev,
if (card->info.type != QETH_CARD_TYPE_IQD) if (card->info.type != QETH_CARD_TYPE_IQD)
qeth_check_outbound_queue(queue); 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 */ /* 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) ...@@ -5557,7 +5560,8 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
switch (card->info.type) { switch (card->info.type) {
case QETH_CARD_TYPE_IQD: 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; break;
case QETH_CARD_TYPE_OSN: case QETH_CARD_TYPE_OSN:
dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup); 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) ...@@ -5585,8 +5589,10 @@ static struct net_device *qeth_alloc_netdev(struct qeth_card *card)
dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->hw_features |= NETIF_F_SG; dev->hw_features |= NETIF_F_SG;
dev->vlan_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; dev->features |= NETIF_F_SG;
}
} }
return dev; return dev;
...@@ -6203,6 +6209,15 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) ...@@ -6203,6 +6209,15 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
} }
EXPORT_SYMBOL_GPL(qeth_get_stats64); 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) int qeth_open(struct net_device *dev)
{ {
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
...@@ -6213,7 +6228,7 @@ int qeth_open(struct net_device *dev) ...@@ -6213,7 +6228,7 @@ int qeth_open(struct net_device *dev)
return -EIO; return -EIO;
card->data.state = CH_STATE_UP; card->data.state = CH_STATE_UP;
netif_start_queue(dev); netif_tx_start_all_queues(dev);
napi_enable(&card->napi); napi_enable(&card->napi);
local_bh_disable(); local_bh_disable();
......
...@@ -198,6 +198,9 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev, ...@@ -198,6 +198,9 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,
if (!card) if (!card)
return -EINVAL; return -EINVAL;
if (IS_IQD(card))
return -EOPNOTSUPP;
mutex_lock(&card->conf_mutex); mutex_lock(&card->conf_mutex);
if (card->state != CARD_STATE_DOWN) { if (card->state != CARD_STATE_DOWN) {
rc = -EPERM; rc = -EPERM;
......
...@@ -154,6 +154,21 @@ static void qeth_get_drvinfo(struct net_device *dev, ...@@ -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)); 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. */ /* Helper function to fill 'advertising' and 'supported' which are the same. */
/* Autoneg and full-duplex are supported and advertised unconditionally. */ /* Autoneg and full-duplex are supported and advertised unconditionally. */
/* Always advertise and support all speeds up to specified, and only one */ /* Always advertise and support all speeds up to specified, and only one */
...@@ -359,6 +374,7 @@ const struct ethtool_ops qeth_ethtool_ops = { ...@@ -359,6 +374,7 @@ const struct ethtool_ops qeth_ethtool_ops = {
.get_ethtool_stats = qeth_get_ethtool_stats, .get_ethtool_stats = qeth_get_ethtool_stats,
.get_sset_count = qeth_get_sset_count, .get_sset_count = qeth_get_sset_count,
.get_drvinfo = qeth_get_drvinfo, .get_drvinfo = qeth_get_drvinfo,
.get_channels = qeth_get_channels,
.get_link_ksettings = qeth_get_link_ksettings, .get_link_ksettings = qeth_get_link_ksettings,
}; };
......
...@@ -161,10 +161,8 @@ static void qeth_l2_drain_rx_mode_cache(struct qeth_card *card) ...@@ -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)) if (is_broadcast_ether_addr(skb->data))
return RTN_BROADCAST; return RTN_BROADCAST;
if (is_multicast_ether_addr(skb->data)) if (is_multicast_ether_addr(skb->data))
...@@ -603,26 +601,29 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, ...@@ -603,26 +601,29 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev) struct net_device *dev)
{ {
struct qeth_card *card = dev->ml_priv; 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); int ipv = qeth_get_ip_version(skb);
struct qeth_qdio_out_q *queue; struct qeth_qdio_out_q *queue;
int tx_bytes = skb->len; int tx_bytes = skb->len;
int rc; 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)) if (IS_OSN(card))
rc = qeth_l2_xmit_osn(card, skb, queue); rc = qeth_l2_xmit_osn(card, skb, queue);
else else
rc = qeth_xmit(card, skb, queue, ipv, cast_type, rc = qeth_xmit(card, skb, queue, ipv,
qeth_l2_fill_header); qeth_l2_get_cast_type(skb), qeth_l2_fill_header);
if (!rc) { if (!rc) {
QETH_TXQ_STAT_INC(queue, tx_packets); QETH_TXQ_STAT_INC(queue, tx_packets);
QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes); QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes);
netif_wake_queue(dev); netif_wake_subqueue(dev, txq);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} else if (rc == -EBUSY) { } else if (rc == -EBUSY) {
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
...@@ -630,10 +631,17 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, ...@@ -630,10 +631,17 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
QETH_TXQ_STAT_INC(queue, tx_dropped); QETH_TXQ_STAT_INC(queue, tx_dropped);
kfree_skb(skb); kfree_skb(skb);
netif_wake_queue(dev); netif_wake_subqueue(dev, txq);
return NETDEV_TX_OK; 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 = { static const struct device_type qeth_l2_devtype = {
.name = "qeth_layer2", .name = "qeth_layer2",
.groups = qeth_l2_attr_groups, .groups = qeth_l2_attr_groups,
...@@ -687,6 +695,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = { ...@@ -687,6 +695,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_get_stats64 = qeth_get_stats64, .ndo_get_stats64 = qeth_get_stats64,
.ndo_start_xmit = qeth_l2_hard_start_xmit, .ndo_start_xmit = qeth_l2_hard_start_xmit,
.ndo_features_check = qeth_features_check, .ndo_features_check = qeth_features_check,
.ndo_select_queue = qeth_l2_select_queue,
.ndo_validate_addr = qeth_l2_validate_addr, .ndo_validate_addr = qeth_l2_validate_addr,
.ndo_set_rx_mode = qeth_l2_set_rx_mode, .ndo_set_rx_mode = qeth_l2_set_rx_mode,
.ndo_do_ioctl = qeth_do_ioctl, .ndo_do_ioctl = qeth_do_ioctl,
......
...@@ -2062,28 +2062,36 @@ static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, ...@@ -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, static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev) struct net_device *dev)
{ {
int cast_type = qeth_l3_get_cast_type(skb);
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
u16 txq = skb_get_queue_mapping(skb);
int ipv = qeth_get_ip_version(skb); int ipv = qeth_get_ip_version(skb);
struct qeth_qdio_out_q *queue; struct qeth_qdio_out_q *queue;
int tx_bytes = skb->len; int tx_bytes = skb->len;
int rc; int cast_type, rc;
queue = qeth_get_tx_queue(card, skb, ipv, cast_type);
if (IS_IQD(card)) { if (IS_IQD(card)) {
queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)];
if (card->options.sniffer) if (card->options.sniffer)
goto tx_drop; goto tx_drop;
if ((card->options.cq != QETH_CQ_ENABLED && !ipv) || if ((card->options.cq != QETH_CQ_ENABLED && !ipv) ||
(card->options.cq == QETH_CQ_ENABLED && (card->options.cq == QETH_CQ_ENABLED &&
skb->protocol != htons(ETH_P_AF_IUCV))) skb->protocol != htons(ETH_P_AF_IUCV)))
goto tx_drop; 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) if (cast_type == RTN_BROADCAST && !card->info.broadcast_capable)
goto tx_drop; goto tx_drop;
netif_stop_queue(dev); netif_stop_subqueue(dev, txq);
if (ipv == 4 || IS_IQD(card)) if (ipv == 4 || IS_IQD(card))
rc = qeth_l3_xmit(card, skb, queue, ipv, cast_type); 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, ...@@ -2094,7 +2102,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
if (!rc) { if (!rc) {
QETH_TXQ_STAT_INC(queue, tx_packets); QETH_TXQ_STAT_INC(queue, tx_packets);
QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes); QETH_TXQ_STAT_ADD(queue, tx_bytes, tx_bytes);
netif_wake_queue(dev); netif_wake_subqueue(dev, txq);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} else if (rc == -EBUSY) { } else if (rc == -EBUSY) {
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
...@@ -2103,7 +2111,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, ...@@ -2103,7 +2111,7 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
tx_drop: tx_drop:
QETH_TXQ_STAT_INC(queue, tx_dropped); QETH_TXQ_STAT_INC(queue, tx_dropped);
kfree_skb(skb); kfree_skb(skb);
netif_wake_queue(dev); netif_wake_subqueue(dev, txq);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
...@@ -2147,11 +2155,19 @@ static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb, ...@@ -2147,11 +2155,19 @@ static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
return qeth_features_check(skb, dev, features); 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 = { static const struct net_device_ops qeth_l3_netdev_ops = {
.ndo_open = qeth_open, .ndo_open = qeth_open,
.ndo_stop = qeth_stop, .ndo_stop = qeth_stop,
.ndo_get_stats64 = qeth_get_stats64, .ndo_get_stats64 = qeth_get_stats64,
.ndo_start_xmit = qeth_l3_hard_start_xmit, .ndo_start_xmit = qeth_l3_hard_start_xmit,
.ndo_select_queue = qeth_l3_iqd_select_queue,
.ndo_validate_addr = eth_validate_addr, .ndo_validate_addr = eth_validate_addr,
.ndo_set_rx_mode = qeth_l3_set_rx_mode, .ndo_set_rx_mode = qeth_l3_set_rx_mode,
.ndo_do_ioctl = qeth_do_ioctl, .ndo_do_ioctl = qeth_do_ioctl,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment