Commit f956b690 authored by Frank Pavlic's avatar Frank Pavlic Committed by Jeff Garzik

[PATCH] s390: qeth driver fixes [5/6]

[PATCH 8/9] s390: qeth driver fixes [5/6]

From: Frank Pavlic <fpavlic@de.ibm.com>
	fix kernel panic in qdio queue handling.
	qeth_qdio_clear_card() could be invoked by 2 CPUs
	simultaneously (for example reboot event and recovery).
Signed-off-by: default avatarFrank Pavlic <fpavlic@de.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 09d2d38a
...@@ -463,6 +463,7 @@ enum qeth_qdio_info_states { ...@@ -463,6 +463,7 @@ enum qeth_qdio_info_states {
QETH_QDIO_UNINITIALIZED, QETH_QDIO_UNINITIALIZED,
QETH_QDIO_ALLOCATED, QETH_QDIO_ALLOCATED,
QETH_QDIO_ESTABLISHED, QETH_QDIO_ESTABLISHED,
QETH_QDIO_CLEANING
}; };
struct qeth_buffer_pool_entry { struct qeth_buffer_pool_entry {
...@@ -537,7 +538,7 @@ struct qeth_qdio_out_q { ...@@ -537,7 +538,7 @@ struct qeth_qdio_out_q {
} __attribute__ ((aligned(256))); } __attribute__ ((aligned(256)));
struct qeth_qdio_info { struct qeth_qdio_info {
volatile enum qeth_qdio_info_states state; atomic_t state;
/* input */ /* input */
struct qeth_qdio_q *in_q; struct qeth_qdio_q *in_q;
struct qeth_qdio_buffer_pool in_buf_pool; struct qeth_qdio_buffer_pool in_buf_pool;
......
...@@ -3179,13 +3179,14 @@ qeth_alloc_qdio_buffers(struct qeth_card *card) ...@@ -3179,13 +3179,14 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
QETH_DBF_TEXT(setup, 2, "allcqdbf"); QETH_DBF_TEXT(setup, 2, "allcqdbf");
if (card->qdio.state == QETH_QDIO_ALLOCATED) if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED,
QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED)
return 0; return 0;
card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q),
GFP_KERNEL|GFP_DMA); GFP_KERNEL|GFP_DMA);
if (!card->qdio.in_q) if (!card->qdio.in_q)
return - ENOMEM; goto out_nomem;
QETH_DBF_TEXT(setup, 2, "inq"); QETH_DBF_TEXT(setup, 2, "inq");
QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *)); QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *));
memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q)); memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q));
...@@ -3194,27 +3195,19 @@ qeth_alloc_qdio_buffers(struct qeth_card *card) ...@@ -3194,27 +3195,19 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
card->qdio.in_q->bufs[i].buffer = card->qdio.in_q->bufs[i].buffer =
&card->qdio.in_q->qdio_bufs[i]; &card->qdio.in_q->qdio_bufs[i];
/* inbound buffer pool */ /* inbound buffer pool */
if (qeth_alloc_buffer_pool(card)){ if (qeth_alloc_buffer_pool(card))
kfree(card->qdio.in_q); goto out_freeinq;
return -ENOMEM;
}
/* outbound */ /* outbound */
card->qdio.out_qs = card->qdio.out_qs =
kmalloc(card->qdio.no_out_queues * kmalloc(card->qdio.no_out_queues *
sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); sizeof(struct qeth_qdio_out_q *), GFP_KERNEL);
if (!card->qdio.out_qs){ if (!card->qdio.out_qs)
qeth_free_buffer_pool(card); goto out_freepool;
return -ENOMEM; for (i = 0; i < card->qdio.no_out_queues; ++i) {
}
for (i = 0; i < card->qdio.no_out_queues; ++i){
card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q), card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q),
GFP_KERNEL|GFP_DMA); GFP_KERNEL|GFP_DMA);
if (!card->qdio.out_qs[i]){ if (!card->qdio.out_qs[i])
while (i > 0) goto out_freeoutq;
kfree(card->qdio.out_qs[--i]);
kfree(card->qdio.out_qs);
return -ENOMEM;
}
QETH_DBF_TEXT_(setup, 2, "outq %i", i); QETH_DBF_TEXT_(setup, 2, "outq %i", i);
QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *)); QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *));
memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q)); memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));
...@@ -3231,8 +3224,19 @@ qeth_alloc_qdio_buffers(struct qeth_card *card) ...@@ -3231,8 +3224,19 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list); INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list);
} }
} }
card->qdio.state = QETH_QDIO_ALLOCATED;
return 0; return 0;
out_freeoutq:
while (i > 0)
kfree(card->qdio.out_qs[--i]);
kfree(card->qdio.out_qs);
out_freepool:
qeth_free_buffer_pool(card);
out_freeinq:
kfree(card->qdio.in_q);
out_nomem:
atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
return -ENOMEM;
} }
static void static void
...@@ -3241,7 +3245,8 @@ qeth_free_qdio_buffers(struct qeth_card *card) ...@@ -3241,7 +3245,8 @@ qeth_free_qdio_buffers(struct qeth_card *card)
int i, j; int i, j;
QETH_DBF_TEXT(trace, 2, "freeqdbf"); QETH_DBF_TEXT(trace, 2, "freeqdbf");
if (card->qdio.state == QETH_QDIO_UNINITIALIZED) if (atomic_swap(&card->qdio.state, QETH_QDIO_UNINITIALIZED) ==
QETH_QDIO_UNINITIALIZED)
return; return;
kfree(card->qdio.in_q); kfree(card->qdio.in_q);
/* inbound buffer pool */ /* inbound buffer pool */
...@@ -3254,7 +3259,6 @@ qeth_free_qdio_buffers(struct qeth_card *card) ...@@ -3254,7 +3259,6 @@ qeth_free_qdio_buffers(struct qeth_card *card)
kfree(card->qdio.out_qs[i]); kfree(card->qdio.out_qs[i]);
} }
kfree(card->qdio.out_qs); kfree(card->qdio.out_qs);
card->qdio.state = QETH_QDIO_UNINITIALIZED;
} }
static void static void
...@@ -3276,7 +3280,7 @@ static void ...@@ -3276,7 +3280,7 @@ static void
qeth_init_qdio_info(struct qeth_card *card) qeth_init_qdio_info(struct qeth_card *card)
{ {
QETH_DBF_TEXT(setup, 4, "intqdinf"); QETH_DBF_TEXT(setup, 4, "intqdinf");
card->qdio.state = QETH_QDIO_UNINITIALIZED; atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
/* inbound */ /* inbound */
card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
...@@ -3339,7 +3343,7 @@ qeth_qdio_establish(struct qeth_card *card) ...@@ -3339,7 +3343,7 @@ qeth_qdio_establish(struct qeth_card *card)
struct qdio_buffer **in_sbal_ptrs; struct qdio_buffer **in_sbal_ptrs;
struct qdio_buffer **out_sbal_ptrs; struct qdio_buffer **out_sbal_ptrs;
int i, j, k; int i, j, k;
int rc; int rc = 0;
QETH_DBF_TEXT(setup, 2, "qdioest"); QETH_DBF_TEXT(setup, 2, "qdioest");
...@@ -3398,8 +3402,10 @@ qeth_qdio_establish(struct qeth_card *card) ...@@ -3398,8 +3402,10 @@ qeth_qdio_establish(struct qeth_card *card)
init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; init_data.input_sbal_addr_array = (void **) in_sbal_ptrs;
init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
if (!(rc = qdio_initialize(&init_data))) if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,
card->qdio.state = QETH_QDIO_ESTABLISHED; QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED)
if ((rc = qdio_initialize(&init_data)))
atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
kfree(out_sbal_ptrs); kfree(out_sbal_ptrs);
kfree(in_sbal_ptrs); kfree(in_sbal_ptrs);
...@@ -3515,13 +3521,20 @@ qeth_qdio_clear_card(struct qeth_card *card, int use_halt) ...@@ -3515,13 +3521,20 @@ qeth_qdio_clear_card(struct qeth_card *card, int use_halt)
int rc = 0; int rc = 0;
QETH_DBF_TEXT(trace,3,"qdioclr"); QETH_DBF_TEXT(trace,3,"qdioclr");
if (card->qdio.state == QETH_QDIO_ESTABLISHED){ switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED,
QETH_QDIO_CLEANING)) {
case QETH_QDIO_ESTABLISHED:
if ((rc = qdio_cleanup(CARD_DDEV(card), if ((rc = qdio_cleanup(CARD_DDEV(card),
(card->info.type == QETH_CARD_TYPE_IQD) ? (card->info.type == QETH_CARD_TYPE_IQD) ?
QDIO_FLAG_CLEANUP_USING_HALT : QDIO_FLAG_CLEANUP_USING_HALT :
QDIO_FLAG_CLEANUP_USING_CLEAR))) QDIO_FLAG_CLEANUP_USING_CLEAR)))
QETH_DBF_TEXT_(trace, 3, "1err%d", rc); QETH_DBF_TEXT_(trace, 3, "1err%d", rc);
card->qdio.state = QETH_QDIO_ALLOCATED; atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED);
break;
case QETH_QDIO_CLEANING:
return rc;
default:
break;
} }
if ((rc = qeth_clear_halt_card(card, use_halt))) if ((rc = qeth_clear_halt_card(card, use_halt)))
QETH_DBF_TEXT_(trace, 3, "2err%d", rc); QETH_DBF_TEXT_(trace, 3, "2err%d", 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