From 871602b1079234b2a9279062eaeadb3aad7bc863 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:42 +0100 Subject: [PATCH 1/9] s390/qeth: only handle IRQs while device is online A qeth device that's offline should not be receiving any IRQs - all pending IOs have been terminated, and we avoid starting any new ones. So rather than immediately registering the IRQ handler when the device is probed, only register it while the device is online. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 68 ++++++++++++++----------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index ce7ff1abbef3..3d2374801308 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -972,8 +972,6 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, /* while we hold the ccwdev lock, this stays valid: */ gdev = dev_get_drvdata(&cdev->dev); card = dev_get_drvdata(&gdev->dev); - if (!card) - return; QETH_CARD_TEXT(card, 5, "irq"); @@ -1198,31 +1196,6 @@ static void qeth_free_buffer_pool(struct qeth_card *card) } } -static void qeth_clean_channel(struct qeth_channel *channel) -{ - struct ccw_device *cdev = channel->ccwdev; - - QETH_DBF_TEXT(SETUP, 2, "freech"); - - spin_lock_irq(get_ccwdev_lock(cdev)); - cdev->handler = NULL; - spin_unlock_irq(get_ccwdev_lock(cdev)); -} - -static void qeth_setup_channel(struct qeth_channel *channel) -{ - struct ccw_device *cdev = channel->ccwdev; - - QETH_DBF_TEXT(SETUP, 2, "setupch"); - - channel->state = CH_STATE_DOWN; - atomic_set(&channel->irq_pending, 0); - - spin_lock_irq(get_ccwdev_lock(cdev)); - cdev->handler = qeth_irq; - spin_unlock_irq(get_ccwdev_lock(cdev)); -} - static int qeth_osa_set_output_queues(struct qeth_card *card, bool single) { unsigned int count = single ? 1 : card->dev->num_tx_queues; @@ -1395,9 +1368,6 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev) if (!card->read_cmd) goto out_read_cmd; - qeth_setup_channel(&card->read); - qeth_setup_channel(&card->write); - qeth_setup_channel(&card->data); card->qeth_service_level.seq_print = qeth_core_sl_print; register_service_level(&card->qeth_service_level); return card; @@ -1467,12 +1437,38 @@ int qeth_stop_channel(struct qeth_channel *channel) channel->active_cmd); channel->active_cmd = NULL; } + cdev->handler = NULL; spin_unlock_irq(get_ccwdev_lock(cdev)); return rc; } EXPORT_SYMBOL_GPL(qeth_stop_channel); +static int qeth_start_channel(struct qeth_channel *channel) +{ + struct ccw_device *cdev = channel->ccwdev; + int rc; + + channel->state = CH_STATE_DOWN; + atomic_set(&channel->irq_pending, 0); + + spin_lock_irq(get_ccwdev_lock(cdev)); + cdev->handler = qeth_irq; + spin_unlock_irq(get_ccwdev_lock(cdev)); + + rc = ccw_device_set_online(cdev); + if (rc) + goto err; + + return 0; + +err: + spin_lock_irq(get_ccwdev_lock(cdev)); + cdev->handler = NULL; + spin_unlock_irq(get_ccwdev_lock(cdev)); + return rc; +} + static int qeth_halt_channels(struct qeth_card *card) { int rc1 = 0, rc2 = 0, rc3 = 0; @@ -4706,7 +4702,7 @@ static void qeth_determine_capabilities(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "detcapab"); if (!ddev->online) { ddev_offline = 1; - rc = ccw_device_set_online(ddev); + rc = qeth_start_channel(channel); if (rc) { QETH_CARD_TEXT_(card, 2, "3err%d", rc); goto out; @@ -4881,9 +4877,6 @@ static int qeth_qdio_establish(struct qeth_card *card) static void qeth_core_free_card(struct qeth_card *card) { QETH_CARD_TEXT(card, 2, "freecrd"); - qeth_clean_channel(&card->read); - qeth_clean_channel(&card->write); - qeth_clean_channel(&card->data); qeth_put_cmd(card->read_cmd); destroy_workqueue(card->event_wq); unregister_service_level(&card->qeth_service_level); @@ -4946,13 +4939,14 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok) qeth_stop_channel(&card->write); qeth_stop_channel(&card->read); qdio_free(CARD_DDEV(card)); - rc = ccw_device_set_online(CARD_RDEV(card)); + + rc = qeth_start_channel(&card->read); if (rc) goto retriable; - rc = ccw_device_set_online(CARD_WDEV(card)); + rc = qeth_start_channel(&card->write); if (rc) goto retriable; - rc = ccw_device_set_online(CARD_DDEV(card)); + rc = qeth_start_channel(&card->data); if (rc) goto retriable; retriable: From 7359393f3c19c9351e81345ef29f4f47229a6416 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:43 +0100 Subject: [PATCH 2/9] s390/qeth: wake up all waiters from qeth_irq() card->wait_q is shared by different users, for different wake-up conditions. qeth_irq() can potentially trigger multiple of these conditions: 1) A change to channel->irq_pending, which qeth_send_control_data() is waiting for. 2) A change to card->state, which qeth_clear_channel() and qeth_halt_channel() are waiting for. As qeth_irq() does only a single wake_up(), we might miss to wake up a second eligible waiter. Luckily all waiters are guarded with a timeout, so this situation should recover on its own eventually. To make things work robustly, add an additional wake_up() for changes to channel->state. And extract a helper that updates channel->irq_pending along with the needed wake_up(). Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 7 +++++++ drivers/s390/net/qeth_core_main.c | 27 +++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index a23875a4fa2b..b54ef12840a2 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -862,6 +862,13 @@ static inline bool qeth_card_hw_is_reachable(struct qeth_card *card) return card->state == CARD_STATE_SOFTSETUP; } +static inline void qeth_unlock_channel(struct qeth_card *card, + struct qeth_channel *channel) +{ + atomic_set(&channel->irq_pending, 0); + wake_up(&card->wait_q); +} + struct qeth_trap_id { __u16 lparnr; char vmname[8]; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 3d2374801308..38f3ed7567bc 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -520,11 +520,10 @@ static int __qeth_issue_next_read(struct qeth_card *card) } 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); + qeth_unlock_channel(card, channel); qeth_put_cmd(iob); card->read_or_write_problem = 1; qeth_schedule_recovery(card); - wake_up(&card->wait_q); } return rc; } @@ -1001,24 +1000,25 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, } channel->active_cmd = NULL; + qeth_unlock_channel(card, channel); rc = qeth_check_irb_error(card, cdev, irb); if (rc) { /* IO was terminated, free its resources. */ if (iob) qeth_cancel_cmd(iob, rc); - atomic_set(&channel->irq_pending, 0); - wake_up(&card->wait_q); return; } - atomic_set(&channel->irq_pending, 0); - - if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC)) + if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { channel->state = CH_STATE_STOPPED; + wake_up(&card->wait_q); + } - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) + if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { channel->state = CH_STATE_HALTED; + wake_up(&card->wait_q); + } if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC))) { @@ -1052,7 +1052,7 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, qeth_cancel_cmd(iob, rc); qeth_clear_ipacmd_list(card); qeth_schedule_recovery(card); - goto out; + return; } } @@ -1060,16 +1060,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, /* sanity check: */ if (irb->scsw.cmd.count > iob->length) { qeth_cancel_cmd(iob, -EIO); - goto out; + return; } if (iob->callback) iob->callback(card, iob, iob->length - irb->scsw.cmd.count); } - -out: - wake_up(&card->wait_q); - return; } static void qeth_notify_skbs(struct qeth_qdio_out_q *q, @@ -1780,8 +1776,7 @@ static int qeth_send_control_data(struct qeth_card *card, QETH_CARD_TEXT_(card, 2, " err%d", rc); qeth_dequeue_cmd(card, iob); qeth_put_cmd(iob); - atomic_set(&channel->irq_pending, 0); - wake_up(&card->wait_q); + qeth_unlock_channel(card, channel); goto out; } From e6b1b7da247b329bb9e62adc97ef28678199cf35 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:44 +0100 Subject: [PATCH 3/9] s390/qeth: overhaul L3 IP address dump code The current code that dumps the RXIP/VIPA/IPATO addresses via sysfs first checks whether the buffer still provides sufficient space to hold another formatted address. But the maximum length of an formatted IPv4 address is 15 characters, not 12. So we underestimate the max required length and if the buffer was previously filled to _just_ the right level, a formatted address can end up being truncated. Revamp these code paths to use the _actually_ required length of the formatted IP address, and while at it suppress a gratuitous newline. Also use scnprintf() to format the output. In case of a truncation, this would allow us to return the number of characters that were actually written. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3.h | 3 +- drivers/s390/net/qeth_l3_main.c | 20 +++------- drivers/s390/net/qeth_l3_sys.c | 66 ++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 5db04fe472c0..2383ffad0a4a 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -102,7 +102,8 @@ struct qeth_ipato_entry { extern const struct attribute_group *qeth_l3_attr_groups[]; -void qeth_l3_ipaddr_to_string(enum qeth_prot_versions, const __u8 *, char *); +int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr, + char *buf); int qeth_l3_create_device_attributes(struct device *); void qeth_l3_remove_device_attributes(struct device *); int qeth_l3_setrouting_v4(struct qeth_card *); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 27126330a4b0..3710761831cc 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -44,23 +44,13 @@ static int qeth_l3_register_addr_entry(struct qeth_card *, static int qeth_l3_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *); -static void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf) -{ - sprintf(buf, "%pI4", addr); -} - -static void qeth_l3_ipaddr6_to_string(const __u8 *addr, char *buf) -{ - sprintf(buf, "%pI6", addr); -} - -void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, - char *buf) +int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr, + char *buf) { if (proto == QETH_PROT_IPV4) - qeth_l3_ipaddr4_to_string(addr, buf); - else if (proto == QETH_PROT_IPV6) - qeth_l3_ipaddr6_to_string(addr, buf); + return sprintf(buf, "%pI4", addr); + else + return sprintf(buf, "%pI6", addr); } static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index f9067ed6c7d3..aa9f3fc45447 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -386,30 +386,35 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, enum qeth_prot_versions proto) { struct qeth_ipato_entry *ipatoe; - char addr_str[40]; - int entry_len; /* length of 1 entry string, differs between v4 and v6 */ - int i = 0; + int str_len = 0; - entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; - /* add strlen for "/\n" */ - entry_len += (proto == QETH_PROT_IPV4)? 5 : 6; mutex_lock(&card->ip_lock); list_for_each_entry(ipatoe, &card->ipato.entries, entry) { + char addr_str[40]; + int entry_len; + if (ipatoe->proto != proto) continue; - /* String must not be longer than PAGE_SIZE. So we check if - * string length gets near PAGE_SIZE. Then we can savely display - * the next IPv6 address (worst case, compared to IPv4) */ - if ((PAGE_SIZE - i) <= entry_len) + + entry_len = qeth_l3_ipaddr_to_string(proto, ipatoe->addr, + addr_str); + if (entry_len < 0) + continue; + + /* Append /%mask to the entry: */ + entry_len += 1 + ((proto == QETH_PROT_IPV4) ? 2 : 3); + /* Enough room to format %entry\n into null terminated page? */ + if (entry_len + 1 > PAGE_SIZE - str_len - 1) break; - qeth_l3_ipaddr_to_string(proto, ipatoe->addr, addr_str); - i += snprintf(buf + i, PAGE_SIZE - i, - "%s/%i\n", addr_str, ipatoe->mask_bits); + + entry_len = scnprintf(buf, PAGE_SIZE - str_len, + "%s/%i\n", addr_str, ipatoe->mask_bits); + str_len += entry_len; + buf += entry_len; } mutex_unlock(&card->ip_lock); - i += snprintf(buf + i, PAGE_SIZE - i, "\n"); - return i; + return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev, @@ -607,31 +612,34 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf, { struct qeth_card *card = dev_get_drvdata(dev); struct qeth_ipaddr *ipaddr; - char addr_str[40]; int str_len = 0; - int entry_len; /* length of 1 entry string, differs between v4 and v6 */ int i; - entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; - entry_len += 2; /* \n + terminator */ mutex_lock(&card->ip_lock); hash_for_each(card->ip_htable, i, ipaddr, hnode) { + char addr_str[40]; + int entry_len; + if (ipaddr->proto != proto || ipaddr->type != type) continue; - /* String must not be longer than PAGE_SIZE. So we check if - * string length gets near PAGE_SIZE. Then we can savely display - * the next IPv6 address (worst case, compared to IPv4) */ - if ((PAGE_SIZE - str_len) <= entry_len) + + entry_len = qeth_l3_ipaddr_to_string(proto, (u8 *)&ipaddr->u, + addr_str); + if (entry_len < 0) + continue; + + /* Enough room to format %addr\n into null terminated page? */ + if (entry_len + 1 > PAGE_SIZE - str_len - 1) break; - qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, - addr_str); - str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n", - addr_str); + + entry_len = scnprintf(buf, PAGE_SIZE - str_len, "%s\n", + addr_str); + str_len += entry_len; + buf += entry_len; } mutex_unlock(&card->ip_lock); - str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); - return str_len; + return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, From 2390166a6b457d3849dd4067c970f714b65bd9c3 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:45 +0100 Subject: [PATCH 4/9] s390/qeth: clean up L3 sysfs code Consolidate some duplicated code for adding RXIP/VIPA addresses, and move the locking to where it's actually needed. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 12 +++- drivers/s390/net/qeth_l3_sys.c | 106 +++++++------------------------- 2 files changed, 33 insertions(+), 85 deletions(-) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 3710761831cc..8db548340632 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -571,6 +571,7 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card, QETH_CARD_TEXT(card, 2, "addipato"); + mutex_lock(&card->conf_mutex); mutex_lock(&card->ip_lock); list_for_each_entry(ipatoe, &card->ipato.entries, entry) { @@ -590,6 +591,7 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card, } mutex_unlock(&card->ip_lock); + mutex_unlock(&card->conf_mutex); return rc; } @@ -603,6 +605,7 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card, QETH_CARD_TEXT(card, 2, "delipato"); + mutex_lock(&card->conf_mutex); mutex_lock(&card->ip_lock); list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { @@ -619,6 +622,8 @@ int qeth_l3_del_ipato_entry(struct qeth_card *card, } mutex_unlock(&card->ip_lock); + mutex_unlock(&card->conf_mutex); + return rc; } @@ -627,6 +632,7 @@ int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip, enum qeth_prot_versions proto) { struct qeth_ipaddr addr; + int rc; qeth_l3_init_ipaddr(&addr, type, proto); if (proto == QETH_PROT_IPV4) @@ -634,7 +640,11 @@ int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip, else memcpy(&addr.u.a6.addr, ip, 16); - return qeth_l3_modify_ip(card, &addr, add); + mutex_lock(&card->conf_mutex); + rc = qeth_l3_modify_ip(card, &addr, add); + mutex_unlock(&card->conf_mutex); + + return rc; } int qeth_l3_modify_hsuid(struct qeth_card *card, bool add) diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index aa9f3fc45447..96c73965eb68 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -460,16 +460,14 @@ static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count, int mask_bits; int rc = 0; - mutex_lock(&card->conf_mutex); rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); if (rc) - goto out; + return rc; ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL); - if (!ipatoe) { - rc = -ENOMEM; - goto out; - } + if (!ipatoe) + return -ENOMEM; + ipatoe->proto = proto; memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16); ipatoe->mask_bits = mask_bits; @@ -477,8 +475,7 @@ static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count, rc = qeth_l3_add_ipato_entry(card, ipatoe); if (rc) kfree(ipatoe); -out: - mutex_unlock(&card->conf_mutex); + return rc ? rc : count; } @@ -501,11 +498,9 @@ static ssize_t qeth_l3_dev_ipato_del_store(const char *buf, size_t count, int mask_bits; int rc = 0; - mutex_lock(&card->conf_mutex); rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); if (!rc) rc = qeth_l3_del_ipato_entry(card, proto, addr, mask_bits); - mutex_unlock(&card->conf_mutex); return rc ? rc : count; } @@ -650,63 +645,34 @@ static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, QETH_IP_TYPE_VIPA); } -static int qeth_l3_parse_vipae(const char *buf, enum qeth_prot_versions proto, - u8 *addr) -{ - if (qeth_l3_string_to_ipaddr(buf, proto, addr)) { - return -EINVAL; - } - return 0; -} - -static ssize_t qeth_l3_dev_vipa_add_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) +static ssize_t qeth_l3_vipa_store(struct device *dev, const char *buf, bool add, + size_t count, enum qeth_prot_versions proto) { + struct qeth_card *card = dev_get_drvdata(dev); u8 addr[16] = {0, }; int rc; - mutex_lock(&card->conf_mutex); - rc = qeth_l3_parse_vipae(buf, proto, addr); + rc = qeth_l3_string_to_ipaddr(buf, proto, addr); if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, true, addr, + rc = qeth_l3_modify_rxip_vipa(card, add, addr, QETH_IP_TYPE_VIPA, proto); - mutex_unlock(&card->conf_mutex); return rc ? rc : count; } static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, qeth_l3_dev_vipa_add4_show, qeth_l3_dev_vipa_add4_store); -static ssize_t qeth_l3_dev_vipa_del_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) -{ - u8 addr[16]; - int rc; - - mutex_lock(&card->conf_mutex); - rc = qeth_l3_parse_vipae(buf, proto, addr); - if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, false, addr, - QETH_IP_TYPE_VIPA, proto); - mutex_unlock(&card->conf_mutex); - return rc ? rc : count; -} - static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, @@ -723,9 +689,7 @@ static ssize_t qeth_l3_dev_vipa_add6_show(struct device *dev, static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, @@ -735,9 +699,7 @@ static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_vipa_store(dev, buf, false, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL, @@ -790,54 +752,34 @@ static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto, return 0; } -static ssize_t qeth_l3_dev_rxip_add_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) +static ssize_t qeth_l3_rxip_store(struct device *dev, const char *buf, bool add, + size_t count, enum qeth_prot_versions proto) { + struct qeth_card *card = dev_get_drvdata(dev); u8 addr[16] = {0, }; int rc; - mutex_lock(&card->conf_mutex); rc = qeth_l3_parse_rxipe(buf, proto, addr); if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, true, addr, + rc = qeth_l3_modify_rxip_vipa(card, add, addr, QETH_IP_TYPE_RXIP, proto); - mutex_unlock(&card->conf_mutex); return rc ? rc : count; } static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_rxip_store(dev, buf, true, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(rxip_add4, add4, 0644, qeth_l3_dev_rxip_add4_show, qeth_l3_dev_rxip_add4_store); -static ssize_t qeth_l3_dev_rxip_del_store(const char *buf, size_t count, - struct qeth_card *card, enum qeth_prot_versions proto) -{ - u8 addr[16]; - int rc; - - mutex_lock(&card->conf_mutex); - rc = qeth_l3_parse_rxipe(buf, proto, addr); - if (!rc) - rc = qeth_l3_modify_rxip_vipa(card, false, addr, - QETH_IP_TYPE_RXIP, proto); - mutex_unlock(&card->conf_mutex); - return rc ? rc : count; -} - static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4); + return qeth_l3_rxip_store(dev, buf, false, count, QETH_PROT_IPV4); } static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL, @@ -854,9 +796,7 @@ static ssize_t qeth_l3_dev_rxip_add6_show(struct device *dev, static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_rxip_store(dev, buf, true, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, @@ -866,9 +806,7 @@ static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct qeth_card *card = dev_get_drvdata(dev); - - return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6); + return qeth_l3_rxip_store(dev, buf, false, count, QETH_PROT_IPV6); } static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL, From 490df97142fe38f7af9ff8d4df8e9ece41df38ec Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:46 +0100 Subject: [PATCH 5/9] s390/qeth: remove open-coded inet_make_mask() Use inet_make_mask() to replace some complicated bit-fiddling. Also use the right data types to replace some raw memcpy calls with proper assignments. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_mpc.h | 8 ++++---- drivers/s390/net/qeth_l3.h | 2 +- drivers/s390/net/qeth_l3_main.c | 34 ++++++++++++++------------------ 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 88f4dc140751..f13225b43781 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -338,14 +338,14 @@ enum qeth_card_info_port_speed { /* (SET)DELIP(M) IPA stuff ***************************************************/ struct qeth_ipacmd_setdelip4 { - __u8 ip_addr[4]; - __u8 mask[4]; + __be32 addr; + __be32 mask; __u32 flags; } __attribute__ ((packed)); struct qeth_ipacmd_setdelip6 { - __u8 ip_addr[16]; - __u8 mask[16]; + struct in6_addr addr; + struct in6_addr prefix; __u32 flags; } __attribute__ ((packed)); diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 2383ffad0a4a..89fb91dad12e 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -35,7 +35,7 @@ struct qeth_ipaddr { union { struct { __be32 addr; - unsigned int mask; + __be32 mask; } a4; struct { struct in6_addr addr; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 8db548340632..bd2273f9ca35 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -369,17 +369,16 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card, return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); } -static void qeth_l3_fill_netmask(u8 *netmask, unsigned int len) +static void qeth_l3_set_ipv6_prefix(struct in6_addr *prefix, unsigned int len) { - int i, j; - for (i = 0; i < 16; i++) { - j = (len) - (i * 8); - if (j >= 8) - netmask[i] = 0xff; - else if (j > 0) - netmask[i] = (u8)(0xFF00 >> j); - else - netmask[i] = 0; + unsigned int i = 0; + + while (len && i < 4) { + int mask_len = min_t(int, len, 32); + + prefix->s6_addr32[i] = inet_make_mask(mask_len); + len -= mask_len; + i++; } } @@ -402,7 +401,6 @@ static int qeth_l3_send_setdelip(struct qeth_card *card, { struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; - __u8 netmask[16]; u32 flags; QETH_CARD_TEXT(card, 4, "setdelip"); @@ -417,15 +415,13 @@ static int qeth_l3_send_setdelip(struct qeth_card *card, QETH_CARD_TEXT_(card, 4, "flags%02X", flags); if (addr->proto == QETH_PROT_IPV6) { - memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr, - sizeof(struct in6_addr)); - qeth_l3_fill_netmask(netmask, addr->u.a6.pfxlen); - memcpy(cmd->data.setdelip6.mask, netmask, - sizeof(struct in6_addr)); + cmd->data.setdelip6.addr = addr->u.a6.addr; + qeth_l3_set_ipv6_prefix(&cmd->data.setdelip6.prefix, + addr->u.a6.pfxlen); cmd->data.setdelip6.flags = flags; } else { - memcpy(cmd->data.setdelip4.ip_addr, &addr->u.a4.addr, 4); - memcpy(cmd->data.setdelip4.mask, &addr->u.a4.mask, 4); + cmd->data.setdelip4.addr = addr->u.a4.addr; + cmd->data.setdelip4.mask = addr->u.a4.mask; cmd->data.setdelip4.flags = flags; } @@ -2436,7 +2432,7 @@ static int qeth_l3_ip_event(struct notifier_block *this, qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); addr.u.a4.addr = ifa->ifa_address; - addr.u.a4.mask = be32_to_cpu(ifa->ifa_mask); + addr.u.a4.mask = ifa->ifa_mask; return qeth_l3_handle_ip_event(card, &addr, event); } From b6beb62a5261f4ba2e73a39889f19e5af3ce545a Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:47 +0100 Subject: [PATCH 6/9] s390/qeth: don't raise NETDEV_REBOOT event from L3 offline path This is a leftover from back when a recovery action didn't go through dev_close(), and was meant to shoot down all remaining af_iucv sockets on the interface. Now that the offline path always calls dev_close(), the NETDEV_GOING_DOWN event from __dev_close_many() is sufficient and this hack can be removed. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index bd2273f9ca35..8bd3fc73a4fb 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2288,12 +2288,6 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, rtnl_unlock(); qeth_l3_stop_card(card); - if (card->options.cq == QETH_CQ_ENABLED) { - rtnl_lock(); - call_netdevice_notifiers(NETDEV_REBOOT, card->dev); - rtnl_unlock(); - } - rc = qeth_stop_channel(&card->data); rc2 = qeth_stop_channel(&card->write); rc3 = qeth_stop_channel(&card->read); From adee2592b6c0e96d8b079c7c9116264293e923e6 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:48 +0100 Subject: [PATCH 7/9] s390/qeth: stop yielding the ip_lock during IPv4 registration As commit df2a2a5225cc ("s390/qeth: convert IP table spinlock to mutex") converted the ip_lock to a mutex, we no longer have to yield it while the subsequent IO sleep-waits for completion. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3.h | 1 - drivers/s390/net/qeth_l3_main.c | 34 ++------------------------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h index 89fb91dad12e..6ccfe2121095 100644 --- a/drivers/s390/net/qeth_l3.h +++ b/drivers/s390/net/qeth_l3.h @@ -23,7 +23,6 @@ struct qeth_ipaddr { struct hlist_node hnode; enum qeth_ip_types type; u8 is_multicast:1; - u8 in_progress:1; u8 disp_flag:2; u8 ipato:1; /* ucast only */ diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 8bd3fc73a4fb..789d3b2ba0de 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -151,8 +151,6 @@ static int qeth_l3_delete_ip(struct qeth_card *card, addr->ref_counter--; if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0) return rc; - if (addr->in_progress) - return -EINPROGRESS; if (qeth_card_hw_is_reachable(card)) rc = qeth_l3_deregister_addr_entry(card, addr); @@ -213,29 +211,10 @@ static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) return 0; } - /* qeth_l3_register_addr_entry can go to sleep - * if we add a IPV4 addr. It is caused by the reason - * that SETIP ipa cmd starts ARP staff for IPV4 addr. - * Thus we should unlock spinlock, and make a protection - * using in_progress variable to indicate that there is - * an hardware operation with this IPV4 address - */ - if (addr->proto == QETH_PROT_IPV4) { - addr->in_progress = 1; - mutex_unlock(&card->ip_lock); - rc = qeth_l3_register_addr_entry(card, addr); - mutex_lock(&card->ip_lock); - addr->in_progress = 0; - } else - rc = qeth_l3_register_addr_entry(card, addr); + rc = qeth_l3_register_addr_entry(card, addr); if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; - if (addr->ref_counter < 1) { - qeth_l3_deregister_addr_entry(card, addr); - hash_del(&addr->hnode); - kfree(addr); - } } else { hash_del(&addr->hnode); kfree(addr); @@ -303,19 +282,10 @@ static void qeth_l3_recover_ip(struct qeth_card *card) hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { if (addr->disp_flag == QETH_DISP_ADDR_ADD) { - if (addr->proto == QETH_PROT_IPV4) { - addr->in_progress = 1; - mutex_unlock(&card->ip_lock); - rc = qeth_l3_register_addr_entry(card, addr); - mutex_lock(&card->ip_lock); - addr->in_progress = 0; - } else - rc = qeth_l3_register_addr_entry(card, addr); + rc = qeth_l3_register_addr_entry(card, addr); if (!rc) { addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; - if (addr->ref_counter < 1) - qeth_l3_delete_ip(card, addr); } else { hash_del(&addr->hnode); kfree(addr); From 52f82bf16b6e754c2a9971420ccc8209ff52081c Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:49 +0100 Subject: [PATCH 8/9] s390/qeth: consolidate helpers for capability checking Convert the old code to use struct qeth_ipa_caps, and while at it remove all unused helper macros. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 47 ++----------------------------- drivers/s390/net/qeth_core_main.c | 28 +++++++++--------- drivers/s390/net/qeth_core_mpc.h | 13 +++++++-- 3 files changed, 27 insertions(+), 61 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index b54ef12840a2..6e16b19732f6 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -125,12 +125,6 @@ struct qeth_routing_info { enum qeth_routing_types type; }; -/* IPA stuff */ -struct qeth_ipa_info { - __u32 supported_funcs; - __u32 enabled_funcs; -}; - /* SETBRIDGEPORT stuff */ enum qeth_sbp_roles { QETH_SBP_ROLE_NONE = 0, @@ -169,41 +163,6 @@ struct qeth_vnicc_info { bool rx_bcast_enabled; }; -static inline int qeth_is_adp_supported(struct qeth_ipa_info *ipa, - enum qeth_ipa_setadp_cmd func) -{ - return (ipa->supported_funcs & func); -} - -static inline int qeth_is_ipa_supported(struct qeth_ipa_info *ipa, - enum qeth_ipa_funcs func) -{ - return (ipa->supported_funcs & func); -} - -static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, - enum qeth_ipa_funcs func) -{ - return (ipa->supported_funcs & ipa->enabled_funcs & func); -} - -#define qeth_adp_supported(c, f) \ - qeth_is_adp_supported(&c->options.adp, f) -#define qeth_is_supported(c, f) \ - qeth_is_ipa_supported(&c->options.ipa4, f) -#define qeth_is_enabled(c, f) \ - qeth_is_ipa_enabled(&c->options.ipa4, f) -#define qeth_is_supported6(c, f) \ - qeth_is_ipa_supported(&c->options.ipa6, f) -#define qeth_is_enabled6(c, f) \ - qeth_is_ipa_enabled(&c->options.ipa6, f) -#define qeth_is_ipafunc_supported(c, prot, f) \ - ((prot == QETH_PROT_IPV6) ? \ - qeth_is_supported6(c, f) : qeth_is_supported(c, f)) -#define qeth_is_ipafunc_enabled(c, prot, f) \ - ((prot == QETH_PROT_IPV6) ? \ - qeth_is_enabled6(c, f) : qeth_is_enabled(c, f)) - #define QETH_IDX_FUNC_LEVEL_OSD 0x0101 #define QETH_IDX_FUNC_LEVEL_IQD 0x4108 @@ -735,11 +694,11 @@ enum qeth_discipline_id { }; struct qeth_card_options { + struct qeth_ipa_caps ipa4; + struct qeth_ipa_caps ipa6; struct qeth_routing_info route4; - struct qeth_ipa_info ipa4; - struct qeth_ipa_info adp; /*Adapter parameters*/ struct qeth_routing_info route6; - struct qeth_ipa_info ipa6; + struct qeth_ipa_caps adp; /* Adapter parameters */ struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */ struct qeth_vnicc_info vnicc; /* VNICC options */ int fake_broadcast; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 38f3ed7567bc..6b55271091cb 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2862,7 +2862,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card, cmd->data.setadapterparms.data.query_cmds_supp.lan_type; QETH_CARD_TEXT_(card, 2, "lnk %d", card->info.link_type); } - card->options.adp.supported_funcs = + card->options.adp.supported = cmd->data.setadapterparms.data.query_cmds_supp.supported_cmds; return 0; } @@ -2918,8 +2918,8 @@ static int qeth_query_ipassists_cb(struct qeth_card *card, case IPA_RC_NOTSUPP: case IPA_RC_L2_UNSUPPORTED_CMD: QETH_CARD_TEXT(card, 2, "ipaunsup"); - card->options.ipa4.supported_funcs |= IPA_SETADAPTERPARMS; - card->options.ipa6.supported_funcs |= IPA_SETADAPTERPARMS; + card->options.ipa4.supported |= IPA_SETADAPTERPARMS; + card->options.ipa6.supported |= IPA_SETADAPTERPARMS; return -EOPNOTSUPP; default: QETH_DBF_MESSAGE(1, "IPA_CMD_QIPASSIST on device %x: Unhandled rc=%#x\n", @@ -2927,13 +2927,11 @@ static int qeth_query_ipassists_cb(struct qeth_card *card, return -EIO; } - if (cmd->hdr.prot_version == QETH_PROT_IPV4) { - card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported; - card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; - } else if (cmd->hdr.prot_version == QETH_PROT_IPV6) { - card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported; - card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; - } else + if (cmd->hdr.prot_version == QETH_PROT_IPV4) + card->options.ipa4 = cmd->hdr.assists; + else if (cmd->hdr.prot_version == QETH_PROT_IPV6) + card->options.ipa6 = cmd->hdr.assists; + else QETH_DBF_MESSAGE(1, "IPA_CMD_QIPASSIST on device %x: Flawed LIC detected\n", CARD_DEVID(card)); return 0; @@ -5002,9 +5000,9 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok) *carrier_ok = true; } - card->options.ipa4.supported_funcs = 0; - card->options.ipa6.supported_funcs = 0; - card->options.adp.supported_funcs = 0; + card->options.ipa4.supported = 0; + card->options.ipa6.supported = 0; + card->options.adp.supported = 0; card->options.sbp.supported_funcs = 0; card->info.diagass_support = 0; rc = qeth_query_ipassists(card, QETH_PROT_IPV4); @@ -5421,9 +5419,9 @@ int qeth_setassparms_cb(struct qeth_card *card, cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; if (cmd->hdr.prot_version == QETH_PROT_IPV4) - card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled; + card->options.ipa4.enabled = cmd->hdr.assists.enabled; if (cmd->hdr.prot_version == QETH_PROT_IPV6) - card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; + card->options.ipa6.enabled = cmd->hdr.assists.enabled; return 0; } EXPORT_SYMBOL_GPL(qeth_setassparms_cb); diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index f13225b43781..f4dc37e28ac7 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -53,6 +53,16 @@ static inline bool qeth_ipa_caps_enabled(struct qeth_ipa_caps *caps, u32 mask) return (caps->enabled & mask) == mask; } +#define qeth_adp_supported(c, f) \ + qeth_ipa_caps_supported(&c->options.adp, f) +#define qeth_is_supported(c, f) \ + qeth_ipa_caps_supported(&c->options.ipa4, f) +#define qeth_is_supported6(c, f) \ + qeth_ipa_caps_supported(&c->options.ipa6, f) +#define qeth_is_ipafunc_supported(c, prot, f) \ + ((prot == QETH_PROT_IPV6) ? qeth_is_supported6(c, f) : \ + qeth_is_supported(c, f)) + enum qeth_card_types { QETH_CARD_TYPE_OSD = 1, QETH_CARD_TYPE_IQD = 5, @@ -766,8 +776,7 @@ struct qeth_ipacmd_hdr { __u8 prim_version_no; __u8 param_count; __u16 prot_version; - __u32 ipa_supported; - __u32 ipa_enabled; + struct qeth_ipa_caps assists; } __attribute__ ((packed)); /* The IPA command itself */ From 334b49de120dd6deb28c31eedb7ca81351f7464e Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Wed, 18 Dec 2019 17:34:50 +0100 Subject: [PATCH 9/9] s390/qeth: make use of napi_schedule_irqoff() qeth_qdio_start_poll() is called from the qdio layer's IRQ handler, while IRQs are masked. Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 6b55271091cb..78349355c582 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3402,7 +3402,7 @@ static void qeth_qdio_start_poll(struct ccw_device *ccwdev, int queue, struct qeth_card *card = (struct qeth_card *)card_ptr; if (card->dev->flags & IFF_UP) - napi_schedule(&card->napi); + napi_schedule_irqoff(&card->napi); } int qeth_configure_cq(struct qeth_card *card, enum qeth_cq cq)