From: Greg Kroah-Hartman <gre...@linuxfoundation.org>

From: Julian Wiedmann <j...@linux.ibm.com>

commit e7a36d27f6b9f389e41d8189a8a08919c6835732 upstream.

When qeth_alloc_qdio_queues() fails to allocate one of the buffers that
back an Output Queue, the 'out_freeoutqbufs' path will free all
previously allocated buffers for this queue. But it misses to free the
half-finished queue struct itself.

Move the buffer allocation into qeth_alloc_output_queue(), and deal with
such errors internally.

Fixes: 0da9581ddb0f ("qeth: exploit asynchronous delivery of storage blocks")
Signed-off-by: Julian Wiedmann <j...@linux.ibm.com>
Reviewed-by: Alexandra Winter <wint...@linux.ibm.com>
Signed-off-by: David S. Miller <da...@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>
---
 drivers/s390/net/qeth_core_main.c |   35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -2630,15 +2630,28 @@ static void qeth_free_output_queue(struc
 static struct qeth_qdio_out_q *qeth_alloc_output_queue(void)
 {
        struct qeth_qdio_out_q *q = kzalloc(sizeof(*q), GFP_KERNEL);
+       unsigned int i;
 
        if (!q)
                return NULL;
 
-       if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q)) {
-               kfree(q);
-               return NULL;
+       if (qdio_alloc_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q))
+               goto err_qdio_bufs;
+
+       for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
+               if (qeth_init_qdio_out_buf(q, i))
+                       goto err_out_bufs;
        }
+
        return q;
+
+err_out_bufs:
+       while (i > 0)
+               kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[--i]);
+       qdio_free_buffers(q->qdio_bufs, QDIO_MAX_BUFFERS_PER_Q);
+err_qdio_bufs:
+       kfree(q);
+       return NULL;
 }
 
 static void qeth_tx_completion_timer(struct timer_list *timer)
@@ -2651,7 +2664,7 @@ static void qeth_tx_completion_timer(str
 
 static int qeth_alloc_qdio_queues(struct qeth_card *card)
 {
-       int i, j;
+       unsigned int i;
 
        QETH_CARD_TEXT(card, 2, "allcqdbf");
 
@@ -2685,13 +2698,6 @@ static int qeth_alloc_qdio_queues(struct
                queue->coalesce_usecs = QETH_TX_COALESCE_USECS;
                queue->max_coalesced_frames = QETH_TX_MAX_COALESCED_FRAMES;
                queue->priority = QETH_QIB_PQUE_PRIO_DEFAULT;
-
-               /* give outbound qeth_qdio_buffers their qdio_buffers */
-               for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
-                       WARN_ON(queue->bufs[j]);
-                       if (qeth_init_qdio_out_buf(queue, j))
-                               goto out_freeoutqbufs;
-               }
        }
 
        /* completion */
@@ -2700,13 +2706,6 @@ static int qeth_alloc_qdio_queues(struct
 
        return 0;
 
-out_freeoutqbufs:
-       while (j > 0) {
-               --j;
-               kmem_cache_free(qeth_qdio_outbuf_cache,
-                               card->qdio.out_qs[i]->bufs[j]);
-               card->qdio.out_qs[i]->bufs[j] = NULL;
-       }
 out_freeoutq:
        while (i > 0) {
                qeth_free_output_queue(card->qdio.out_qs[--i]);


Reply via email to