We statically allocate 8 cmd buffers on the read channel, when the only IO left that's still using them is the long-running READ. Replace this with a single allocated cmd, that gets restarted whenever the READ completed. This introduces refcounting for allocated cmds, so that the READ cmd can survive the IO completion. Signed-off-by: Julian Wiedmann <jwi@xxxxxxxxxxxxx> --- drivers/s390/net/qeth_core.h | 7 ++++ drivers/s390/net/qeth_core_main.c | 54 ++++++++++++++++++------------- drivers/s390/net/qeth_l2_main.c | 1 - drivers/s390/net/qeth_l3_main.c | 1 - 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 962945a63235..5bcdede5e955 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -579,6 +579,7 @@ struct qeth_channel; struct qeth_cmd_buffer { enum qeth_cmd_buffer_state state; unsigned int length; + refcount_t ref_count; struct qeth_channel *channel; struct qeth_reply *reply; long timeout; @@ -588,6 +589,11 @@ struct qeth_cmd_buffer { void (*callback)(struct qeth_card *card, struct qeth_cmd_buffer *iob); }; +static inline void qeth_get_cmd(struct qeth_cmd_buffer *iob) +{ + refcount_inc(&iob->ref_count); +} + static inline struct qeth_ipa_cmd *__ipa_cmd(struct qeth_cmd_buffer *iob) { return (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE); @@ -771,6 +777,7 @@ struct qeth_card { enum qeth_card_states state; spinlock_t lock; struct ccwgroup_device *gdev; + struct qeth_cmd_buffer *read_cmd; struct qeth_channel read; struct qeth_channel write; struct qeth_channel data; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 11e6a3820421..fe3dfeaf5ceb 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -496,26 +496,21 @@ static void qeth_setup_ccw(struct ccw1 *ccw, u8 cmd_code, u8 flags, u32 len, static int __qeth_issue_next_read(struct qeth_card *card) { - struct qeth_channel *channel = &card->read; - struct qeth_cmd_buffer *iob; - struct ccw1 *ccw; + struct qeth_cmd_buffer *iob = card->read_cmd; + struct qeth_channel *channel = iob->channel; + struct ccw1 *ccw = __ccw_from_cmd(iob); int rc; QETH_CARD_TEXT(card, 5, "issnxrd"); if (channel->state != CH_STATE_UP) return -EIO; - iob = qeth_get_buffer(channel); - if (!iob) { - dev_warn(&card->gdev->dev, "The qeth device driver " - "failed to recover an error on the device\n"); - QETH_DBF_MESSAGE(2, "issue_next_read on device %x failed: no iob available\n", - CARD_DEVID(card)); - return -ENOMEM; - } - ccw = __ccw_from_cmd(iob); - qeth_setup_ccw(ccw, CCW_CMD_READ, 0, QETH_BUFSIZE, iob->data); + memset(iob->data, 0, iob->length); + qeth_setup_ccw(ccw, CCW_CMD_READ, 0, iob->length, iob->data); iob->callback = qeth_issue_next_read_cb; + /* keep the cmd alive after completion: */ + qeth_get_cmd(iob); + QETH_CARD_TEXT(card, 6, "noirqpnd"); rc = ccw_device_start(channel->ccwdev, ccw, (addr_t) iob, 0, 0); if (rc) { @@ -694,6 +689,16 @@ static int qeth_check_idx_response(struct qeth_card *card, return 0; } +static void qeth_put_cmd(struct qeth_cmd_buffer *iob) +{ + if (refcount_dec_and_test(&iob->ref_count)) { + if (iob->reply) + qeth_put_reply(iob->reply); + kfree(iob->data); + kfree(iob); + } +} + static struct qeth_cmd_buffer *__qeth_get_buffer(struct qeth_channel *channel) { __u8 index; @@ -720,10 +725,7 @@ void qeth_release_buffer(struct qeth_cmd_buffer *iob) unsigned long flags; if (iob->state == BUF_STATE_MALLOC) { - if (iob->reply) - qeth_put_reply(iob->reply); - kfree(iob->data); - kfree(iob); + qeth_put_cmd(iob); return; } @@ -787,6 +789,7 @@ static struct qeth_cmd_buffer *qeth_alloc_cmd(struct qeth_channel *channel, } iob->state = BUF_STATE_MALLOC; + refcount_set(&iob->ref_count, 1); iob->channel = channel; iob->timeout = timeout; iob->length = length; @@ -1445,10 +1448,14 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev) dev_name(&gdev->dev)); if (!card->event_wq) goto out_wq; - if (qeth_setup_channel(&card->read, true)) - goto out_ip; + + card->read_cmd = qeth_alloc_cmd(&card->read, QETH_BUFSIZE, 1, 0); + if (!card->read_cmd) + goto out_read_cmd; + if (qeth_setup_channel(&card->read, false)) + goto out_read; if (qeth_setup_channel(&card->write, true)) - goto out_channel; + goto out_write; if (qeth_setup_channel(&card->data, false)) goto out_data; card->qeth_service_level.seq_print = qeth_core_sl_print; @@ -1457,9 +1464,11 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev) out_data: qeth_clean_channel(&card->write); -out_channel: +out_write: qeth_clean_channel(&card->read); -out_ip: +out_read: + qeth_release_buffer(card->read_cmd); +out_read_cmd: destroy_workqueue(card->event_wq); out_wq: dev_set_drvdata(&gdev->dev, NULL); @@ -4892,6 +4901,7 @@ static void qeth_core_free_card(struct qeth_card *card) qeth_clean_channel(&card->read); qeth_clean_channel(&card->write); qeth_clean_channel(&card->data); + qeth_release_buffer(card->read_cmd); destroy_workqueue(card->event_wq); qeth_free_qdio_queues(card); unregister_service_level(&card->qeth_service_level); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index e1b25084dcd4..9565ef9747c1 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -292,7 +292,6 @@ static void qeth_l2_stop_card(struct qeth_card *card) card->state = CARD_STATE_DOWN; } - qeth_clear_cmd_buffers(&card->read); qeth_clear_cmd_buffers(&card->write); flush_workqueue(card->event_wq); card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 15758f45837d..4d66f9556451 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1436,7 +1436,6 @@ static void qeth_l3_stop_card(struct qeth_card *card) card->state = CARD_STATE_DOWN; } - qeth_clear_cmd_buffers(&card->read); qeth_clear_cmd_buffers(&card->write); flush_workqueue(card->event_wq); } -- 2.17.1