Skip to content

Commit ce28867

Browse files
julianwiedmanndavem330
authored andcommitted
s390/qeth: don't clobber buffer on async TX completion
If qeth_qdio_output_handler() detects that a transmit requires async completion, it replaces the pending buffer's metadata object (qeth_qdio_out_buffer) so that this queue buffer can be re-used while the data is pending completion. Later when the CQ indicates async completion of such a metadata object, qeth_qdio_cq_handler() tries to free any data associated with this object (since HW has now completed the transfer). By calling qeth_clear_output_buffer(), it erronously operates on the queue buffer that _previously_ belonged to this transfer ... but which has been potentially re-used several times by now. This results in double-free's of the buffer's data, and failing transmits as the buffer descriptor is scrubbed in mid-air. The correct way of handling this situation is to 1. scrub the queue buffer when it is prepared for re-use, and 2. later obtain the data addresses from the async-completion notifier (ie. the AOB), instead of the queue buffer. All this only affects qeth devices used for af_iucv HiperTransport. Fixes: 0da9581 ("qeth: exploit asynchronous delivery of storage blocks") Signed-off-by: Julian Wiedmann <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9d0a58f commit ce28867

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

drivers/s390/net/qeth_core.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,17 @@ struct qeth_trap_id {
829829
/*some helper functions*/
830830
#define QETH_CARD_IFNAME(card) (((card)->dev)? (card)->dev->name : "")
831831

832+
static inline void qeth_scrub_qdio_buffer(struct qdio_buffer *buf,
833+
unsigned int elements)
834+
{
835+
unsigned int i;
836+
837+
for (i = 0; i < elements; i++)
838+
memset(&buf->element[i], 0, sizeof(struct qdio_buffer_element));
839+
buf->element[14].sflags = 0;
840+
buf->element[15].sflags = 0;
841+
}
842+
832843
/**
833844
* qeth_get_elements_for_range() - find number of SBALEs to cover range.
834845
* @start: Start of the address range.

drivers/s390/net/qeth_core_main.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *queue,
7373
struct qeth_qdio_out_buffer *buf,
7474
enum iucv_tx_notify notification);
7575
static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf);
76-
static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
77-
struct qeth_qdio_out_buffer *buf,
78-
enum qeth_qdio_buffer_states newbufstate);
7976
static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
8077

8178
struct workqueue_struct *qeth_wq;
@@ -489,6 +486,7 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
489486
struct qaob *aob;
490487
struct qeth_qdio_out_buffer *buffer;
491488
enum iucv_tx_notify notification;
489+
unsigned int i;
492490

493491
aob = (struct qaob *) phys_to_virt(phys_aob_addr);
494492
QETH_CARD_TEXT(card, 5, "haob");
@@ -513,10 +511,18 @@ static void qeth_qdio_handle_aob(struct qeth_card *card,
513511
qeth_notify_skbs(buffer->q, buffer, notification);
514512

515513
buffer->aob = NULL;
516-
qeth_clear_output_buffer(buffer->q, buffer,
517-
QETH_QDIO_BUF_HANDLED_DELAYED);
514+
/* Free dangling allocations. The attached skbs are handled by
515+
* qeth_cleanup_handled_pending().
516+
*/
517+
for (i = 0;
518+
i < aob->sb_count && i < QETH_MAX_BUFFER_ELEMENTS(card);
519+
i++) {
520+
if (aob->sba[i] && buffer->is_header[i])
521+
kmem_cache_free(qeth_core_header_cache,
522+
(void *) aob->sba[i]);
523+
}
524+
atomic_set(&buffer->state, QETH_QDIO_BUF_HANDLED_DELAYED);
518525

519-
/* from here on: do not touch buffer anymore */
520526
qdio_release_aob(aob);
521527
}
522528

@@ -3759,6 +3765,10 @@ static void qeth_qdio_output_handler(struct ccw_device *ccwdev,
37593765
QETH_CARD_TEXT(queue->card, 5, "aob");
37603766
QETH_CARD_TEXT_(queue->card, 5, "%lx",
37613767
virt_to_phys(buffer->aob));
3768+
3769+
/* prepare the queue slot for re-use: */
3770+
qeth_scrub_qdio_buffer(buffer->buffer,
3771+
QETH_MAX_BUFFER_ELEMENTS(card));
37623772
if (qeth_init_qdio_out_buf(queue, bidx)) {
37633773
QETH_CARD_TEXT(card, 2, "outofbuf");
37643774
qeth_schedule_recovery(card);

0 commit comments

Comments
 (0)