s390/qeth: don't replace a fully completed async TX buffer

For TX buffers that require an additional async notification via QAOB, the
TX completion code can now manage all the necessary processing if the
notification has already occurred (or is occurring concurrently).

In such cases we can avoid replacing the metadata that is associated
with the buffer's slot on the ring, and just keep using the current one.

As qeth_clear_output_buffer() will also handle any kmem cache-allocated
memory that was mapped into the TX buffer, qeth_qdio_handle_aob()
doesn't need to worry about it.

While at it, also remove the unneeded forward declaration for
qeth_init_qdio_out_buf().

Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Julian Wiedmann 2020-12-07 14:12:31 +01:00 committed by David S. Miller
parent 0b8da8110b
commit db4ffdcef7
1 changed files with 52 additions and 39 deletions

View File

@ -75,7 +75,6 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
enum iucv_tx_notify notification); enum iucv_tx_notify notification);
static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error, static void qeth_tx_complete_buf(struct qeth_qdio_out_buffer *buf, bool error,
int budget); int budget);
static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
static void qeth_close_dev_handler(struct work_struct *work) static void qeth_close_dev_handler(struct work_struct *work)
{ {
@ -517,18 +516,6 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
buffer = (struct qeth_qdio_out_buffer *) aob->user1; buffer = (struct qeth_qdio_out_buffer *) aob->user1;
QETH_CARD_TEXT_(card, 5, "%lx", aob->user1); QETH_CARD_TEXT_(card, 5, "%lx", aob->user1);
/* Free dangling allocations. The attached skbs are handled by
* qeth_cleanup_handled_pending().
*/
for (i = 0;
i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card);
i++) {
void *data = phys_to_virt(aob->sba[i]);
if (data && buffer->is_header[i])
kmem_cache_free(qeth_core_header_cache, data);
}
if (aob->aorc) { if (aob->aorc) {
QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc); QETH_CARD_TEXT_(card, 2, "aorc%02X", aob->aorc);
new_state = QETH_QDIO_BUF_QAOB_ERROR; new_state = QETH_QDIO_BUF_QAOB_ERROR;
@ -536,10 +523,9 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
switch (atomic_xchg(&buffer->state, new_state)) { switch (atomic_xchg(&buffer->state, new_state)) {
case QETH_QDIO_BUF_PRIMED: case QETH_QDIO_BUF_PRIMED:
/* Faster than TX completion code. */ /* Faster than TX completion code, let it handle the async
notification = qeth_compute_cq_notification(aob->aorc, 0); * completion for us.
qeth_notify_skbs(buffer->q, buffer, notification); */
atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
break; break;
case QETH_QDIO_BUF_PENDING: case QETH_QDIO_BUF_PENDING:
/* TX completion code is active and will handle the async /* TX completion code is active and will handle the async
@ -550,6 +536,19 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
/* TX completion code is already finished. */ /* TX completion code is already finished. */
notification = qeth_compute_cq_notification(aob->aorc, 1); notification = qeth_compute_cq_notification(aob->aorc, 1);
qeth_notify_skbs(buffer->q, buffer, notification); qeth_notify_skbs(buffer->q, buffer, notification);
/* Free dangling allocations. The attached skbs are handled by
* qeth_cleanup_handled_pending().
*/
for (i = 0;
i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card);
i++) {
void *data = phys_to_virt(aob->sba[i]);
if (data && buffer->is_header[i])
kmem_cache_free(qeth_core_header_cache, data);
}
atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED); atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
break; break;
default: default:
@ -6078,9 +6077,13 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue,
QDIO_OUTBUF_STATE_FLAG_PENDING)) { QDIO_OUTBUF_STATE_FLAG_PENDING)) {
WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED);
if (atomic_cmpxchg(&buffer->state, QETH_QDIO_BUF_PRIMED, QETH_CARD_TEXT_(card, 5, "pel%u", bidx);
QETH_QDIO_BUF_PENDING) ==
QETH_QDIO_BUF_PRIMED) { switch (atomic_cmpxchg(&buffer->state,
QETH_QDIO_BUF_PRIMED,
QETH_QDIO_BUF_PENDING)) {
case QETH_QDIO_BUF_PRIMED:
/* We have initial ownership, no QAOB (yet): */
qeth_notify_skbs(queue, buffer, TX_NOTIFY_PENDING); qeth_notify_skbs(queue, buffer, TX_NOTIFY_PENDING);
/* Handle race with qeth_qdio_handle_aob(): */ /* Handle race with qeth_qdio_handle_aob(): */
@ -6088,39 +6091,49 @@ static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue,
QETH_QDIO_BUF_NEED_QAOB)) { QETH_QDIO_BUF_NEED_QAOB)) {
case QETH_QDIO_BUF_PENDING: case QETH_QDIO_BUF_PENDING:
/* No concurrent QAOB notification. */ /* No concurrent QAOB notification. */
break;
/* Prepare the queue slot for immediate re-use: */
qeth_scrub_qdio_buffer(buffer->buffer, queue->max_elements);
if (qeth_init_qdio_out_buf(queue, bidx)) {
QETH_CARD_TEXT(card, 2, "outofbuf");
qeth_schedule_recovery(card);
}
/* Skip clearing the buffer: */
return;
case QETH_QDIO_BUF_QAOB_OK: case QETH_QDIO_BUF_QAOB_OK:
qeth_notify_skbs(queue, buffer, qeth_notify_skbs(queue, buffer,
TX_NOTIFY_DELAYED_OK); TX_NOTIFY_DELAYED_OK);
atomic_set(&buffer->state, error = false;
QETH_QDIO_BUF_HANDLED_DELAYED);
break; break;
case QETH_QDIO_BUF_QAOB_ERROR: case QETH_QDIO_BUF_QAOB_ERROR:
qeth_notify_skbs(queue, buffer, qeth_notify_skbs(queue, buffer,
TX_NOTIFY_DELAYED_GENERALERROR); TX_NOTIFY_DELAYED_GENERALERROR);
atomic_set(&buffer->state, error = true;
QETH_QDIO_BUF_HANDLED_DELAYED);
break; break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
break;
case QETH_QDIO_BUF_QAOB_OK:
/* qeth_qdio_handle_aob() already received a QAOB: */
qeth_notify_skbs(queue, buffer, TX_NOTIFY_OK);
error = false;
break;
case QETH_QDIO_BUF_QAOB_ERROR:
/* qeth_qdio_handle_aob() already received a QAOB: */
qeth_notify_skbs(queue, buffer, TX_NOTIFY_GENERALERROR);
error = true;
break;
default:
WARN_ON_ONCE(1);
} }
} else if (card->options.cq == QETH_CQ_ENABLED) {
QETH_CARD_TEXT_(card, 5, "pel%u", bidx);
/* prepare the queue slot for re-use: */
qeth_scrub_qdio_buffer(buffer->buffer, queue->max_elements);
if (qeth_init_qdio_out_buf(queue, bidx)) {
QETH_CARD_TEXT(card, 2, "outofbuf");
qeth_schedule_recovery(card);
}
return;
}
if (card->options.cq == QETH_CQ_ENABLED)
qeth_notify_skbs(queue, buffer, qeth_notify_skbs(queue, buffer,
qeth_compute_cq_notification(sflags, 0)); qeth_compute_cq_notification(sflags, 0));
}
qeth_clear_output_buffer(queue, buffer, error, budget); qeth_clear_output_buffer(queue, buffer, error, budget);
} }