diff options
author | David S. Miller <davem@davemloft.net> | 2019-12-05 12:25:05 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-12-05 12:25:05 -0800 |
commit | b87440526324d253a9dc4c043d84d7c1b4df0614 (patch) | |
tree | d62bad662fb722988ad846fb5b04940eca4ecef9 /drivers | |
parent | a350d2e7adbb57181d33e3aa6f0565632747feaa (diff) | |
parent | f9e50b02a99c3ebbaa30690e8d5be28a5c2624eb (diff) |
Merge branch 's390-fixes'
Julian Wiedmann says:
====================
s390/qeth: fixes 2019-12-05
please apply the following fixes to your net tree.
The first two patches target the RX data path, the third fixes a memory
leak when shutting down a qeth device.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/net/qeth_core.h | 4 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 158 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 14 | ||||
-rw-r--r-- | drivers/s390/net/qeth_ethtool.c | 1 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 12 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 13 |
6 files changed, 119 insertions, 83 deletions
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 293dd99b7fef..871d44746f5c 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -480,6 +480,7 @@ struct qeth_card_stats { u64 rx_dropped_nomem; u64 rx_dropped_notsupp; + u64 rx_dropped_runt; /* rtnl_link_stats64 */ u64 rx_packets; @@ -627,6 +628,7 @@ struct qeth_ipato { struct qeth_channel { struct ccw_device *ccwdev; + struct qeth_cmd_buffer *active_cmd; enum qeth_channel_states state; atomic_t irq_pending; }; @@ -1037,6 +1039,8 @@ int qeth_do_run_thread(struct qeth_card *, unsigned long); void qeth_clear_thread_start_bit(struct qeth_card *, unsigned long); void qeth_clear_thread_running_bit(struct qeth_card *, unsigned long); int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok); +int qeth_stop_channel(struct qeth_channel *channel); + void qeth_print_status_message(struct qeth_card *); int qeth_init_qdio_queues(struct qeth_card *); int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index efcbe60220d1..b9a2349e4b90 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -515,7 +515,9 @@ static int __qeth_issue_next_read(struct qeth_card *card) QETH_CARD_TEXT(card, 6, "noirqpnd"); rc = ccw_device_start(channel->ccwdev, ccw, (addr_t) iob, 0, 0); - if (rc) { + if (!rc) { + channel->active_cmd = iob; + } else { QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n", rc, CARD_DEVID(card)); atomic_set(&channel->irq_pending, 0); @@ -986,8 +988,21 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, QETH_CARD_TEXT(card, 5, "data"); } - if (qeth_intparm_is_iob(intparm)) - iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm); + if (intparm == 0) { + QETH_CARD_TEXT(card, 5, "irqunsol"); + } else if ((addr_t)intparm != (addr_t)channel->active_cmd) { + QETH_CARD_TEXT(card, 5, "irqunexp"); + + dev_err(&cdev->dev, + "Received IRQ with intparm %lx, expected %px\n", + intparm, channel->active_cmd); + if (channel->active_cmd) + qeth_cancel_cmd(channel->active_cmd, -EIO); + } else { + iob = (struct qeth_cmd_buffer *) (addr_t)intparm; + } + + channel->active_cmd = NULL; rc = qeth_check_irb_error(card, cdev, irb); if (rc) { @@ -1007,15 +1022,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) channel->state = CH_STATE_HALTED; - if (intparm == QETH_CLEAR_CHANNEL_PARM) { - QETH_CARD_TEXT(card, 6, "clrchpar"); - /* we don't have to handle this further */ - intparm = 0; - } - if (intparm == QETH_HALT_CHANNEL_PARM) { - QETH_CARD_TEXT(card, 6, "hltchpar"); - /* we don't have to handle this further */ - intparm = 0; + if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC | + SCSW_FCTL_HALT_FUNC))) { + qeth_cancel_cmd(iob, -ECANCELED); + iob = NULL; } cstat = irb->scsw.cmd.cstat; @@ -1408,7 +1418,7 @@ static int qeth_clear_channel(struct qeth_card *card, QETH_CARD_TEXT(card, 3, "clearch"); spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); - rc = ccw_device_clear(channel->ccwdev, QETH_CLEAR_CHANNEL_PARM); + rc = ccw_device_clear(channel->ccwdev, (addr_t)channel->active_cmd); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) @@ -1430,7 +1440,7 @@ static int qeth_halt_channel(struct qeth_card *card, QETH_CARD_TEXT(card, 3, "haltch"); spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); - rc = ccw_device_halt(channel->ccwdev, QETH_HALT_CHANNEL_PARM); + rc = ccw_device_halt(channel->ccwdev, (addr_t)channel->active_cmd); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) @@ -1444,6 +1454,25 @@ static int qeth_halt_channel(struct qeth_card *card, return 0; } +int qeth_stop_channel(struct qeth_channel *channel) +{ + struct ccw_device *cdev = channel->ccwdev; + int rc; + + rc = ccw_device_set_offline(cdev); + + spin_lock_irq(get_ccwdev_lock(cdev)); + if (channel->active_cmd) { + dev_err(&cdev->dev, "Stopped channel while cmd %px was still active\n", + channel->active_cmd); + channel->active_cmd = NULL; + } + spin_unlock_irq(get_ccwdev_lock(cdev)); + + return rc; +} +EXPORT_SYMBOL_GPL(qeth_stop_channel); + static int qeth_halt_channels(struct qeth_card *card) { int rc1 = 0, rc2 = 0, rc3 = 0; @@ -1746,6 +1775,8 @@ static int qeth_send_control_data(struct qeth_card *card, spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); rc = ccw_device_start_timeout(channel->ccwdev, __ccw_from_cmd(iob), (addr_t) iob, 0, 0, timeout); + if (!rc) + channel->active_cmd = iob; spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); if (rc) { QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n", @@ -4667,12 +4698,12 @@ EXPORT_SYMBOL_GPL(qeth_vm_request_mac); static void qeth_determine_capabilities(struct qeth_card *card) { + struct qeth_channel *channel = &card->data; + struct ccw_device *ddev = channel->ccwdev; int rc; - struct ccw_device *ddev; int ddev_offline = 0; QETH_CARD_TEXT(card, 2, "detcapab"); - ddev = CARD_DDEV(card); if (!ddev->online) { ddev_offline = 1; rc = ccw_device_set_online(ddev); @@ -4711,7 +4742,7 @@ static void qeth_determine_capabilities(struct qeth_card *card) out_offline: if (ddev_offline == 1) - ccw_device_set_offline(ddev); + qeth_stop_channel(channel); out: return; } @@ -4911,9 +4942,9 @@ retry: QETH_DBF_MESSAGE(2, "Retrying to do IDX activates on device %x.\n", CARD_DEVID(card)); rc = qeth_qdio_clear_card(card, !IS_IQD(card)); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); + qeth_stop_channel(&card->data); + qeth_stop_channel(&card->write); + qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); rc = ccw_device_set_online(CARD_RDEV(card)); if (rc) @@ -5028,27 +5059,15 @@ out: } EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); -static void qeth_create_skb_frag(struct qdio_buffer_element *element, - struct sk_buff *skb, int offset, int data_len) +static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len) { - struct page *page = virt_to_page(element->addr); + struct page *page = virt_to_page(data); unsigned int next_frag; - /* first fill the linear space */ - if (!skb->len) { - unsigned int linear = min(data_len, skb_tailroom(skb)); - - skb_put_data(skb, element->addr + offset, linear); - data_len -= linear; - if (!data_len) - return; - offset += linear; - /* fall through to add page frag for remaining data */ - } - next_frag = skb_shinfo(skb)->nr_frags; get_page(page); - skb_add_rx_frag(skb, next_frag, page, offset, data_len, data_len); + skb_add_rx_frag(skb, next_frag, page, offset_in_page(data), data_len, + data_len); } static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale) @@ -5063,13 +5082,12 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, { struct qdio_buffer_element *element = *__element; struct qdio_buffer *buffer = qethbuffer->buffer; + unsigned int linear_len = 0; int offset = *__offset; bool use_rx_sg = false; unsigned int headroom; struct sk_buff *skb; int skb_len = 0; - void *data_ptr; - int data_len; next_packet: /* qeth_hdr must not cross element boundaries */ @@ -5082,29 +5100,41 @@ next_packet: *hdr = element->addr + offset; offset += sizeof(struct qeth_hdr); + skb = NULL; + switch ((*hdr)->hdr.l2.id) { case QETH_HEADER_TYPE_LAYER2: skb_len = (*hdr)->hdr.l2.pkt_length; + linear_len = ETH_HLEN; headroom = 0; break; case QETH_HEADER_TYPE_LAYER3: skb_len = (*hdr)->hdr.l3.length; if (!IS_LAYER3(card)) { QETH_CARD_STAT_INC(card, rx_dropped_notsupp); - skb = NULL; goto walk_packet; } + if ((*hdr)->hdr.l3.flags & QETH_HDR_PASSTHRU) { + linear_len = ETH_HLEN; + headroom = 0; + break; + } + + if ((*hdr)->hdr.l3.flags & QETH_HDR_IPV6) + linear_len = sizeof(struct ipv6hdr); + else + linear_len = sizeof(struct iphdr); headroom = ETH_HLEN; break; case QETH_HEADER_TYPE_OSN: skb_len = (*hdr)->hdr.osn.pdu_length; if (!IS_OSN(card)) { QETH_CARD_STAT_INC(card, rx_dropped_notsupp); - skb = NULL; goto walk_packet; } + linear_len = skb_len; headroom = sizeof(struct qeth_hdr); break; default: @@ -5117,8 +5147,10 @@ next_packet: return NULL; } - if (!skb_len) - return NULL; + if (skb_len < linear_len) { + QETH_CARD_STAT_INC(card, rx_dropped_runt); + goto walk_packet; + } use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) || ((skb_len >= card->options.rx_sg_cb) && @@ -5130,9 +5162,9 @@ next_packet: skb = qethbuffer->rx_skb; qethbuffer->rx_skb = NULL; } else { - unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len; - - skb = napi_alloc_skb(&card->napi, linear + headroom); + if (!use_rx_sg) + linear_len = skb_len; + skb = napi_alloc_skb(&card->napi, linear_len + headroom); } if (!skb) @@ -5141,18 +5173,32 @@ next_packet: skb_reserve(skb, headroom); walk_packet: - data_ptr = element->addr + offset; while (skb_len) { - data_len = min(skb_len, (int)(element->length - offset)); + int data_len = min(skb_len, (int)(element->length - offset)); + char *data = element->addr + offset; + + skb_len -= data_len; + offset += data_len; + /* Extract data from current element: */ if (skb && data_len) { - if (use_rx_sg) - qeth_create_skb_frag(element, skb, offset, - data_len); - else - skb_put_data(skb, data_ptr, data_len); + if (linear_len) { + unsigned int copy_len; + + copy_len = min_t(unsigned int, linear_len, + data_len); + + skb_put_data(skb, data, copy_len); + linear_len -= copy_len; + data_len -= copy_len; + data += copy_len; + } + + if (data_len) + qeth_create_skb_frag(skb, data, data_len); } - skb_len -= data_len; + + /* Step forward to next element: */ if (skb_len) { if (qeth_is_last_sbale(element)) { QETH_CARD_TEXT(card, 4, "unexeob"); @@ -5166,9 +5212,6 @@ walk_packet: } element++; offset = 0; - data_ptr = element->addr; - } else { - offset += data_len; } } @@ -6268,7 +6311,8 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) card->stats.rx_frame_errors + card->stats.rx_fifo_errors; stats->rx_dropped = card->stats.rx_dropped_nomem + - card->stats.rx_dropped_notsupp; + card->stats.rx_dropped_notsupp + + card->stats.rx_dropped_runt; stats->multicast = card->stats.rx_multicast; stats->rx_length_errors = card->stats.rx_length_errors; stats->rx_frame_errors = card->stats.rx_frame_errors; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 53fcf6641154..88f4dc140751 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -29,20 +29,6 @@ extern unsigned char IPA_PDU_HEADER[]; #define QETH_TIMEOUT (10 * HZ) #define QETH_IPA_TIMEOUT (45 * HZ) -#define QETH_CLEAR_CHANNEL_PARM -10 -#define QETH_HALT_CHANNEL_PARM -11 - -static inline bool qeth_intparm_is_iob(unsigned long intparm) -{ - switch (intparm) { - case QETH_CLEAR_CHANNEL_PARM: - case QETH_HALT_CHANNEL_PARM: - case 0: - return false; - } - return true; -} - /*****************************************************************************/ /* IP Assist related definitions */ /*****************************************************************************/ diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index f7485c6dea25..ab59bc975719 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -51,6 +51,7 @@ static const struct qeth_stats card_stats[] = { QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page), QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem), QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp), + QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt), }; #define TXQ_STATS_LEN ARRAY_SIZE(txq_stats) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 989935d67b31..9086bc04fa6b 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -845,9 +845,9 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) out_remove: qeth_l2_stop_card(card); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); + qeth_stop_channel(&card->data); + qeth_stop_channel(&card->write); + qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); mutex_unlock(&card->conf_mutex); @@ -878,9 +878,9 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, rtnl_unlock(); qeth_l2_stop_card(card); - rc = ccw_device_set_offline(CARD_DDEV(card)); - rc2 = ccw_device_set_offline(CARD_WDEV(card)); - rc3 = ccw_device_set_offline(CARD_RDEV(card)); + rc = qeth_stop_channel(&card->data); + rc2 = qeth_stop_channel(&card->write); + rc3 = qeth_stop_channel(&card->read); if (!rc) rc = (rc2) ? rc2 : rc3; if (rc) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index e7ce73b9f016..27126330a4b0 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2259,9 +2259,9 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev) return 0; out_remove: qeth_l3_stop_card(card); - ccw_device_set_offline(CARD_DDEV(card)); - ccw_device_set_offline(CARD_WDEV(card)); - ccw_device_set_offline(CARD_RDEV(card)); + qeth_stop_channel(&card->data); + qeth_stop_channel(&card->write); + qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); mutex_unlock(&card->conf_mutex); @@ -2297,9 +2297,10 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, call_netdevice_notifiers(NETDEV_REBOOT, card->dev); rtnl_unlock(); } - rc = ccw_device_set_offline(CARD_DDEV(card)); - rc2 = ccw_device_set_offline(CARD_WDEV(card)); - rc3 = ccw_device_set_offline(CARD_RDEV(card)); + + rc = qeth_stop_channel(&card->data); + rc2 = qeth_stop_channel(&card->write); + rc3 = qeth_stop_channel(&card->read); if (!rc) rc = (rc2) ? rc2 : rc3; if (rc) |