Commit b8744052 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents a350d2e7 f9e50b02
...@@ -480,6 +480,7 @@ struct qeth_card_stats { ...@@ -480,6 +480,7 @@ struct qeth_card_stats {
u64 rx_dropped_nomem; u64 rx_dropped_nomem;
u64 rx_dropped_notsupp; u64 rx_dropped_notsupp;
u64 rx_dropped_runt;
/* rtnl_link_stats64 */ /* rtnl_link_stats64 */
u64 rx_packets; u64 rx_packets;
...@@ -627,6 +628,7 @@ struct qeth_ipato { ...@@ -627,6 +628,7 @@ struct qeth_ipato {
struct qeth_channel { struct qeth_channel {
struct ccw_device *ccwdev; struct ccw_device *ccwdev;
struct qeth_cmd_buffer *active_cmd;
enum qeth_channel_states state; enum qeth_channel_states state;
atomic_t irq_pending; atomic_t irq_pending;
}; };
...@@ -1037,6 +1039,8 @@ int qeth_do_run_thread(struct qeth_card *, unsigned long); ...@@ -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_start_bit(struct qeth_card *, unsigned long);
void qeth_clear_thread_running_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_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 *); void qeth_print_status_message(struct qeth_card *);
int qeth_init_qdio_queues(struct qeth_card *); int qeth_init_qdio_queues(struct qeth_card *);
int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
......
...@@ -515,7 +515,9 @@ static int __qeth_issue_next_read(struct qeth_card *card) ...@@ -515,7 +515,9 @@ static int __qeth_issue_next_read(struct qeth_card *card)
QETH_CARD_TEXT(card, 6, "noirqpnd"); QETH_CARD_TEXT(card, 6, "noirqpnd");
rc = ccw_device_start(channel->ccwdev, ccw, (addr_t) iob, 0, 0); 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", QETH_DBF_MESSAGE(2, "error %i on device %x when starting next read ccw!\n",
rc, CARD_DEVID(card)); rc, CARD_DEVID(card));
atomic_set(&channel->irq_pending, 0); atomic_set(&channel->irq_pending, 0);
...@@ -986,8 +988,21 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, ...@@ -986,8 +988,21 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
QETH_CARD_TEXT(card, 5, "data"); QETH_CARD_TEXT(card, 5, "data");
} }
if (qeth_intparm_is_iob(intparm)) if (intparm == 0) {
iob = (struct qeth_cmd_buffer *) __va((addr_t)intparm); 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); rc = qeth_check_irb_error(card, cdev, irb);
if (rc) { if (rc) {
...@@ -1007,15 +1022,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, ...@@ -1007,15 +1022,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC))
channel->state = CH_STATE_HALTED; channel->state = CH_STATE_HALTED;
if (intparm == QETH_CLEAR_CHANNEL_PARM) { if (iob && (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC |
QETH_CARD_TEXT(card, 6, "clrchpar"); SCSW_FCTL_HALT_FUNC))) {
/* we don't have to handle this further */ qeth_cancel_cmd(iob, -ECANCELED);
intparm = 0; iob = NULL;
}
if (intparm == QETH_HALT_CHANNEL_PARM) {
QETH_CARD_TEXT(card, 6, "hltchpar");
/* we don't have to handle this further */
intparm = 0;
} }
cstat = irb->scsw.cmd.cstat; cstat = irb->scsw.cmd.cstat;
...@@ -1408,7 +1418,7 @@ static int qeth_clear_channel(struct qeth_card *card, ...@@ -1408,7 +1418,7 @@ static int qeth_clear_channel(struct qeth_card *card,
QETH_CARD_TEXT(card, 3, "clearch"); QETH_CARD_TEXT(card, 3, "clearch");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); 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)); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc) if (rc)
...@@ -1430,7 +1440,7 @@ static int qeth_halt_channel(struct qeth_card *card, ...@@ -1430,7 +1440,7 @@ static int qeth_halt_channel(struct qeth_card *card,
QETH_CARD_TEXT(card, 3, "haltch"); QETH_CARD_TEXT(card, 3, "haltch");
spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); 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)); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc) if (rc)
...@@ -1444,6 +1454,25 @@ static int qeth_halt_channel(struct qeth_card *card, ...@@ -1444,6 +1454,25 @@ static int qeth_halt_channel(struct qeth_card *card,
return 0; 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) static int qeth_halt_channels(struct qeth_card *card)
{ {
int rc1 = 0, rc2 = 0, rc3 = 0; int rc1 = 0, rc2 = 0, rc3 = 0;
...@@ -1746,6 +1775,8 @@ static int qeth_send_control_data(struct qeth_card *card, ...@@ -1746,6 +1775,8 @@ static int qeth_send_control_data(struct qeth_card *card,
spin_lock_irq(get_ccwdev_lock(channel->ccwdev)); spin_lock_irq(get_ccwdev_lock(channel->ccwdev));
rc = ccw_device_start_timeout(channel->ccwdev, __ccw_from_cmd(iob), rc = ccw_device_start_timeout(channel->ccwdev, __ccw_from_cmd(iob),
(addr_t) iob, 0, 0, timeout); (addr_t) iob, 0, 0, timeout);
if (!rc)
channel->active_cmd = iob;
spin_unlock_irq(get_ccwdev_lock(channel->ccwdev)); spin_unlock_irq(get_ccwdev_lock(channel->ccwdev));
if (rc) { if (rc) {
QETH_DBF_MESSAGE(2, "qeth_send_control_data on device %x: ccw_device_start rc = %i\n", 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); ...@@ -4667,12 +4698,12 @@ EXPORT_SYMBOL_GPL(qeth_vm_request_mac);
static void qeth_determine_capabilities(struct qeth_card *card) static void qeth_determine_capabilities(struct qeth_card *card)
{ {
struct qeth_channel *channel = &card->data;
struct ccw_device *ddev = channel->ccwdev;
int rc; int rc;
struct ccw_device *ddev;
int ddev_offline = 0; int ddev_offline = 0;
QETH_CARD_TEXT(card, 2, "detcapab"); QETH_CARD_TEXT(card, 2, "detcapab");
ddev = CARD_DDEV(card);
if (!ddev->online) { if (!ddev->online) {
ddev_offline = 1; ddev_offline = 1;
rc = ccw_device_set_online(ddev); rc = ccw_device_set_online(ddev);
...@@ -4711,7 +4742,7 @@ static void qeth_determine_capabilities(struct qeth_card *card) ...@@ -4711,7 +4742,7 @@ static void qeth_determine_capabilities(struct qeth_card *card)
out_offline: out_offline:
if (ddev_offline == 1) if (ddev_offline == 1)
ccw_device_set_offline(ddev); qeth_stop_channel(channel);
out: out:
return; return;
} }
...@@ -4911,9 +4942,9 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok) ...@@ -4911,9 +4942,9 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok)
QETH_DBF_MESSAGE(2, "Retrying to do IDX activates on device %x.\n", QETH_DBF_MESSAGE(2, "Retrying to do IDX activates on device %x.\n",
CARD_DEVID(card)); CARD_DEVID(card));
rc = qeth_qdio_clear_card(card, !IS_IQD(card)); rc = qeth_qdio_clear_card(card, !IS_IQD(card));
ccw_device_set_offline(CARD_DDEV(card)); qeth_stop_channel(&card->data);
ccw_device_set_offline(CARD_WDEV(card)); qeth_stop_channel(&card->write);
ccw_device_set_offline(CARD_RDEV(card)); qeth_stop_channel(&card->read);
qdio_free(CARD_DDEV(card)); qdio_free(CARD_DDEV(card));
rc = ccw_device_set_online(CARD_RDEV(card)); rc = ccw_device_set_online(CARD_RDEV(card));
if (rc) if (rc)
...@@ -5028,27 +5059,15 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok) ...@@ -5028,27 +5059,15 @@ int qeth_core_hardsetup_card(struct qeth_card *card, bool *carrier_ok)
} }
EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card); EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
static void qeth_create_skb_frag(struct qdio_buffer_element *element, static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len)
struct sk_buff *skb, int offset, int data_len)
{ {
struct page *page = virt_to_page(element->addr); struct page *page = virt_to_page(data);
unsigned int next_frag; 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; next_frag = skb_shinfo(skb)->nr_frags;
get_page(page); 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) 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, ...@@ -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_element *element = *__element;
struct qdio_buffer *buffer = qethbuffer->buffer; struct qdio_buffer *buffer = qethbuffer->buffer;
unsigned int linear_len = 0;
int offset = *__offset; int offset = *__offset;
bool use_rx_sg = false; bool use_rx_sg = false;
unsigned int headroom; unsigned int headroom;
struct sk_buff *skb; struct sk_buff *skb;
int skb_len = 0; int skb_len = 0;
void *data_ptr;
int data_len;
next_packet: next_packet:
/* qeth_hdr must not cross element boundaries */ /* qeth_hdr must not cross element boundaries */
...@@ -5082,29 +5100,41 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, ...@@ -5082,29 +5100,41 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
*hdr = element->addr + offset; *hdr = element->addr + offset;
offset += sizeof(struct qeth_hdr); offset += sizeof(struct qeth_hdr);
skb = NULL;
switch ((*hdr)->hdr.l2.id) { switch ((*hdr)->hdr.l2.id) {
case QETH_HEADER_TYPE_LAYER2: case QETH_HEADER_TYPE_LAYER2:
skb_len = (*hdr)->hdr.l2.pkt_length; skb_len = (*hdr)->hdr.l2.pkt_length;
linear_len = ETH_HLEN;
headroom = 0; headroom = 0;
break; break;
case QETH_HEADER_TYPE_LAYER3: case QETH_HEADER_TYPE_LAYER3:
skb_len = (*hdr)->hdr.l3.length; skb_len = (*hdr)->hdr.l3.length;
if (!IS_LAYER3(card)) { if (!IS_LAYER3(card)) {
QETH_CARD_STAT_INC(card, rx_dropped_notsupp); QETH_CARD_STAT_INC(card, rx_dropped_notsupp);
skb = NULL;
goto walk_packet; 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; headroom = ETH_HLEN;
break; break;
case QETH_HEADER_TYPE_OSN: case QETH_HEADER_TYPE_OSN:
skb_len = (*hdr)->hdr.osn.pdu_length; skb_len = (*hdr)->hdr.osn.pdu_length;
if (!IS_OSN(card)) { if (!IS_OSN(card)) {
QETH_CARD_STAT_INC(card, rx_dropped_notsupp); QETH_CARD_STAT_INC(card, rx_dropped_notsupp);
skb = NULL;
goto walk_packet; goto walk_packet;
} }
linear_len = skb_len;
headroom = sizeof(struct qeth_hdr); headroom = sizeof(struct qeth_hdr);
break; break;
default: default:
...@@ -5117,8 +5147,10 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, ...@@ -5117,8 +5147,10 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
return NULL; return NULL;
} }
if (!skb_len) if (skb_len < linear_len) {
return NULL; QETH_CARD_STAT_INC(card, rx_dropped_runt);
goto walk_packet;
}
use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) || use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) ||
((skb_len >= card->options.rx_sg_cb) && ((skb_len >= card->options.rx_sg_cb) &&
...@@ -5130,9 +5162,9 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, ...@@ -5130,9 +5162,9 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
skb = qethbuffer->rx_skb; skb = qethbuffer->rx_skb;
qethbuffer->rx_skb = NULL; qethbuffer->rx_skb = NULL;
} else { } else {
unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len; if (!use_rx_sg)
linear_len = skb_len;
skb = napi_alloc_skb(&card->napi, linear + headroom); skb = napi_alloc_skb(&card->napi, linear_len + headroom);
} }
if (!skb) if (!skb)
...@@ -5141,18 +5173,32 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, ...@@ -5141,18 +5173,32 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
skb_reserve(skb, headroom); skb_reserve(skb, headroom);
walk_packet: walk_packet:
data_ptr = element->addr + offset;
while (skb_len) { 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 (skb && data_len) {
if (use_rx_sg) if (linear_len) {
qeth_create_skb_frag(element, skb, offset, unsigned int copy_len;
data_len);
else copy_len = min_t(unsigned int, linear_len,
skb_put_data(skb, data_ptr, data_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 (skb_len) {
if (qeth_is_last_sbale(element)) { if (qeth_is_last_sbale(element)) {
QETH_CARD_TEXT(card, 4, "unexeob"); QETH_CARD_TEXT(card, 4, "unexeob");
...@@ -5166,9 +5212,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card, ...@@ -5166,9 +5212,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
} }
element++; element++;
offset = 0; 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) ...@@ -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_frame_errors +
card->stats.rx_fifo_errors; card->stats.rx_fifo_errors;
stats->rx_dropped = card->stats.rx_dropped_nomem + 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->multicast = card->stats.rx_multicast;
stats->rx_length_errors = card->stats.rx_length_errors; stats->rx_length_errors = card->stats.rx_length_errors;
stats->rx_frame_errors = card->stats.rx_frame_errors; stats->rx_frame_errors = card->stats.rx_frame_errors;
......
...@@ -29,20 +29,6 @@ extern unsigned char IPA_PDU_HEADER[]; ...@@ -29,20 +29,6 @@ extern unsigned char IPA_PDU_HEADER[];
#define QETH_TIMEOUT (10 * HZ) #define QETH_TIMEOUT (10 * HZ)
#define QETH_IPA_TIMEOUT (45 * 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 */ /* IP Assist related definitions */
/*****************************************************************************/ /*****************************************************************************/
......
...@@ -51,6 +51,7 @@ static const struct qeth_stats card_stats[] = { ...@@ -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 SG page allocs", rx_sg_alloc_page),
QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem), 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, bad format", rx_dropped_notsupp),
QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt),
}; };
#define TXQ_STATS_LEN ARRAY_SIZE(txq_stats) #define TXQ_STATS_LEN ARRAY_SIZE(txq_stats)
......
...@@ -845,9 +845,9 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev) ...@@ -845,9 +845,9 @@ static int qeth_l2_set_online(struct ccwgroup_device *gdev)
out_remove: out_remove:
qeth_l2_stop_card(card); qeth_l2_stop_card(card);
ccw_device_set_offline(CARD_DDEV(card)); qeth_stop_channel(&card->data);
ccw_device_set_offline(CARD_WDEV(card)); qeth_stop_channel(&card->write);
ccw_device_set_offline(CARD_RDEV(card)); qeth_stop_channel(&card->read);
qdio_free(CARD_DDEV(card)); qdio_free(CARD_DDEV(card));
mutex_unlock(&card->conf_mutex); mutex_unlock(&card->conf_mutex);
...@@ -878,9 +878,9 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, ...@@ -878,9 +878,9 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev,
rtnl_unlock(); rtnl_unlock();
qeth_l2_stop_card(card); qeth_l2_stop_card(card);
rc = ccw_device_set_offline(CARD_DDEV(card)); rc = qeth_stop_channel(&card->data);
rc2 = ccw_device_set_offline(CARD_WDEV(card)); rc2 = qeth_stop_channel(&card->write);
rc3 = ccw_device_set_offline(CARD_RDEV(card)); rc3 = qeth_stop_channel(&card->read);
if (!rc) if (!rc)
rc = (rc2) ? rc2 : rc3; rc = (rc2) ? rc2 : rc3;
if (rc) if (rc)
......
...@@ -2259,9 +2259,9 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev) ...@@ -2259,9 +2259,9 @@ static int qeth_l3_set_online(struct ccwgroup_device *gdev)
return 0; return 0;
out_remove: out_remove:
qeth_l3_stop_card(card); qeth_l3_stop_card(card);
ccw_device_set_offline(CARD_DDEV(card)); qeth_stop_channel(&card->data);
ccw_device_set_offline(CARD_WDEV(card)); qeth_stop_channel(&card->write);
ccw_device_set_offline(CARD_RDEV(card)); qeth_stop_channel(&card->read);
qdio_free(CARD_DDEV(card)); qdio_free(CARD_DDEV(card));
mutex_unlock(&card->conf_mutex); mutex_unlock(&card->conf_mutex);
...@@ -2297,9 +2297,10 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, ...@@ -2297,9 +2297,10 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev,
call_netdevice_notifiers(NETDEV_REBOOT, card->dev); call_netdevice_notifiers(NETDEV_REBOOT, card->dev);
rtnl_unlock(); rtnl_unlock();
} }
rc = ccw_device_set_offline(CARD_DDEV(card));
rc2 = ccw_device_set_offline(CARD_WDEV(card)); rc = qeth_stop_channel(&card->data);
rc3 = ccw_device_set_offline(CARD_RDEV(card)); rc2 = qeth_stop_channel(&card->write);
rc3 = qeth_stop_channel(&card->read);
if (!rc) if (!rc)
rc = (rc2) ? rc2 : rc3; rc = (rc2) ? rc2 : rc3;
if (rc) if (rc)
......
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