diff options
author | Frank Pavlic <fpavlic@de.ibm.com> | 2006-09-15 10:26:52 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-17 01:03:07 -0400 |
commit | f956b6902eabbff249000287c7b36cd65761d8b8 (patch) | |
tree | 8452e831e5361be54e392cf678abefa55716a719 /drivers/s390 | |
parent | 09d2d38a152419467f764c0f730821e896766c1f (diff) |
[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: Frank Pavlic <fpavlic@de.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/qeth.h | 3 | ||||
-rw-r--r-- | drivers/s390/net/qeth_main.c | 71 |
2 files changed, 44 insertions, 30 deletions
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index 22a7ffbcaa42..821383d8cbe7 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h | |||
@@ -463,6 +463,7 @@ enum qeth_qdio_info_states { | |||
463 | QETH_QDIO_UNINITIALIZED, | 463 | QETH_QDIO_UNINITIALIZED, |
464 | QETH_QDIO_ALLOCATED, | 464 | QETH_QDIO_ALLOCATED, |
465 | QETH_QDIO_ESTABLISHED, | 465 | QETH_QDIO_ESTABLISHED, |
466 | QETH_QDIO_CLEANING | ||
466 | }; | 467 | }; |
467 | 468 | ||
468 | struct qeth_buffer_pool_entry { | 469 | struct qeth_buffer_pool_entry { |
@@ -537,7 +538,7 @@ struct qeth_qdio_out_q { | |||
537 | } __attribute__ ((aligned(256))); | 538 | } __attribute__ ((aligned(256))); |
538 | 539 | ||
539 | struct qeth_qdio_info { | 540 | struct qeth_qdio_info { |
540 | volatile enum qeth_qdio_info_states state; | 541 | atomic_t state; |
541 | /* input */ | 542 | /* input */ |
542 | struct qeth_qdio_q *in_q; | 543 | struct qeth_qdio_q *in_q; |
543 | struct qeth_qdio_buffer_pool in_buf_pool; | 544 | struct qeth_qdio_buffer_pool in_buf_pool; |
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 0bc55a327907..a1b2e6fd38ca 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c | |||
@@ -3179,13 +3179,14 @@ qeth_alloc_qdio_buffers(struct qeth_card *card) | |||
3179 | 3179 | ||
3180 | QETH_DBF_TEXT(setup, 2, "allcqdbf"); | 3180 | QETH_DBF_TEXT(setup, 2, "allcqdbf"); |
3181 | 3181 | ||
3182 | if (card->qdio.state == QETH_QDIO_ALLOCATED) | 3182 | if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_UNINITIALIZED, |
3183 | QETH_QDIO_ALLOCATED) != QETH_QDIO_UNINITIALIZED) | ||
3183 | return 0; | 3184 | return 0; |
3184 | 3185 | ||
3185 | card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), | 3186 | card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), |
3186 | GFP_KERNEL|GFP_DMA); | 3187 | GFP_KERNEL|GFP_DMA); |
3187 | if (!card->qdio.in_q) | 3188 | if (!card->qdio.in_q) |
3188 | return - ENOMEM; | 3189 | goto out_nomem; |
3189 | QETH_DBF_TEXT(setup, 2, "inq"); | 3190 | QETH_DBF_TEXT(setup, 2, "inq"); |
3190 | QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *)); | 3191 | QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *)); |
3191 | memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q)); | 3192 | memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q)); |
@@ -3194,27 +3195,19 @@ qeth_alloc_qdio_buffers(struct qeth_card *card) | |||
3194 | card->qdio.in_q->bufs[i].buffer = | 3195 | card->qdio.in_q->bufs[i].buffer = |
3195 | &card->qdio.in_q->qdio_bufs[i]; | 3196 | &card->qdio.in_q->qdio_bufs[i]; |
3196 | /* inbound buffer pool */ | 3197 | /* inbound buffer pool */ |
3197 | if (qeth_alloc_buffer_pool(card)){ | 3198 | if (qeth_alloc_buffer_pool(card)) |
3198 | kfree(card->qdio.in_q); | 3199 | goto out_freeinq; |
3199 | return -ENOMEM; | ||
3200 | } | ||
3201 | /* outbound */ | 3200 | /* outbound */ |
3202 | card->qdio.out_qs = | 3201 | card->qdio.out_qs = |
3203 | kmalloc(card->qdio.no_out_queues * | 3202 | kmalloc(card->qdio.no_out_queues * |
3204 | sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); | 3203 | sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); |
3205 | if (!card->qdio.out_qs){ | 3204 | if (!card->qdio.out_qs) |
3206 | qeth_free_buffer_pool(card); | 3205 | goto out_freepool; |
3207 | return -ENOMEM; | 3206 | for (i = 0; i < card->qdio.no_out_queues; ++i) { |
3208 | } | ||
3209 | for (i = 0; i < card->qdio.no_out_queues; ++i){ | ||
3210 | card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q), | 3207 | card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q), |
3211 | GFP_KERNEL|GFP_DMA); | 3208 | GFP_KERNEL|GFP_DMA); |
3212 | if (!card->qdio.out_qs[i]){ | 3209 | if (!card->qdio.out_qs[i]) |
3213 | while (i > 0) | 3210 | goto out_freeoutq; |
3214 | kfree(card->qdio.out_qs[--i]); | ||
3215 | kfree(card->qdio.out_qs); | ||
3216 | return -ENOMEM; | ||
3217 | } | ||
3218 | QETH_DBF_TEXT_(setup, 2, "outq %i", i); | 3211 | QETH_DBF_TEXT_(setup, 2, "outq %i", i); |
3219 | QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *)); | 3212 | QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *)); |
3220 | memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q)); | 3213 | 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 | INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list); | 3224 | INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list); |
3232 | } | 3225 | } |
3233 | } | 3226 | } |
3234 | card->qdio.state = QETH_QDIO_ALLOCATED; | ||
3235 | return 0; | 3227 | return 0; |
3228 | |||
3229 | out_freeoutq: | ||
3230 | while (i > 0) | ||
3231 | kfree(card->qdio.out_qs[--i]); | ||
3232 | kfree(card->qdio.out_qs); | ||
3233 | out_freepool: | ||
3234 | qeth_free_buffer_pool(card); | ||
3235 | out_freeinq: | ||
3236 | kfree(card->qdio.in_q); | ||
3237 | out_nomem: | ||
3238 | atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); | ||
3239 | return -ENOMEM; | ||
3236 | } | 3240 | } |
3237 | 3241 | ||
3238 | static void | 3242 | static void |
@@ -3241,7 +3245,8 @@ qeth_free_qdio_buffers(struct qeth_card *card) | |||
3241 | int i, j; | 3245 | int i, j; |
3242 | 3246 | ||
3243 | QETH_DBF_TEXT(trace, 2, "freeqdbf"); | 3247 | QETH_DBF_TEXT(trace, 2, "freeqdbf"); |
3244 | if (card->qdio.state == QETH_QDIO_UNINITIALIZED) | 3248 | if (atomic_swap(&card->qdio.state, QETH_QDIO_UNINITIALIZED) == |
3249 | QETH_QDIO_UNINITIALIZED) | ||
3245 | return; | 3250 | return; |
3246 | kfree(card->qdio.in_q); | 3251 | kfree(card->qdio.in_q); |
3247 | /* inbound buffer pool */ | 3252 | /* inbound buffer pool */ |
@@ -3254,7 +3259,6 @@ qeth_free_qdio_buffers(struct qeth_card *card) | |||
3254 | kfree(card->qdio.out_qs[i]); | 3259 | kfree(card->qdio.out_qs[i]); |
3255 | } | 3260 | } |
3256 | kfree(card->qdio.out_qs); | 3261 | kfree(card->qdio.out_qs); |
3257 | card->qdio.state = QETH_QDIO_UNINITIALIZED; | ||
3258 | } | 3262 | } |
3259 | 3263 | ||
3260 | static void | 3264 | static void |
@@ -3276,7 +3280,7 @@ static void | |||
3276 | qeth_init_qdio_info(struct qeth_card *card) | 3280 | qeth_init_qdio_info(struct qeth_card *card) |
3277 | { | 3281 | { |
3278 | QETH_DBF_TEXT(setup, 4, "intqdinf"); | 3282 | QETH_DBF_TEXT(setup, 4, "intqdinf"); |
3279 | card->qdio.state = QETH_QDIO_UNINITIALIZED; | 3283 | atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); |
3280 | /* inbound */ | 3284 | /* inbound */ |
3281 | card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; | 3285 | card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; |
3282 | card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; | 3286 | card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; |
@@ -3339,7 +3343,7 @@ qeth_qdio_establish(struct qeth_card *card) | |||
3339 | struct qdio_buffer **in_sbal_ptrs; | 3343 | struct qdio_buffer **in_sbal_ptrs; |
3340 | struct qdio_buffer **out_sbal_ptrs; | 3344 | struct qdio_buffer **out_sbal_ptrs; |
3341 | int i, j, k; | 3345 | int i, j, k; |
3342 | int rc; | 3346 | int rc = 0; |
3343 | 3347 | ||
3344 | QETH_DBF_TEXT(setup, 2, "qdioest"); | 3348 | QETH_DBF_TEXT(setup, 2, "qdioest"); |
3345 | 3349 | ||
@@ -3398,8 +3402,10 @@ qeth_qdio_establish(struct qeth_card *card) | |||
3398 | init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; | 3402 | init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; |
3399 | init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; | 3403 | init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; |
3400 | 3404 | ||
3401 | if (!(rc = qdio_initialize(&init_data))) | 3405 | if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, |
3402 | card->qdio.state = QETH_QDIO_ESTABLISHED; | 3406 | QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) |
3407 | if ((rc = qdio_initialize(&init_data))) | ||
3408 | atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); | ||
3403 | 3409 | ||
3404 | kfree(out_sbal_ptrs); | 3410 | kfree(out_sbal_ptrs); |
3405 | kfree(in_sbal_ptrs); | 3411 | kfree(in_sbal_ptrs); |
@@ -3515,13 +3521,20 @@ qeth_qdio_clear_card(struct qeth_card *card, int use_halt) | |||
3515 | int rc = 0; | 3521 | int rc = 0; |
3516 | 3522 | ||
3517 | QETH_DBF_TEXT(trace,3,"qdioclr"); | 3523 | QETH_DBF_TEXT(trace,3,"qdioclr"); |
3518 | if (card->qdio.state == QETH_QDIO_ESTABLISHED){ | 3524 | switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED, |
3525 | QETH_QDIO_CLEANING)) { | ||
3526 | case QETH_QDIO_ESTABLISHED: | ||
3519 | if ((rc = qdio_cleanup(CARD_DDEV(card), | 3527 | if ((rc = qdio_cleanup(CARD_DDEV(card), |
3520 | (card->info.type == QETH_CARD_TYPE_IQD) ? | 3528 | (card->info.type == QETH_CARD_TYPE_IQD) ? |
3521 | QDIO_FLAG_CLEANUP_USING_HALT : | 3529 | QDIO_FLAG_CLEANUP_USING_HALT : |
3522 | QDIO_FLAG_CLEANUP_USING_CLEAR))) | 3530 | QDIO_FLAG_CLEANUP_USING_CLEAR))) |
3523 | QETH_DBF_TEXT_(trace, 3, "1err%d", rc); | 3531 | QETH_DBF_TEXT_(trace, 3, "1err%d", rc); |
3524 | card->qdio.state = QETH_QDIO_ALLOCATED; | 3532 | atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); |
3533 | break; | ||
3534 | case QETH_QDIO_CLEANING: | ||
3535 | return rc; | ||
3536 | default: | ||
3537 | break; | ||
3525 | } | 3538 | } |
3526 | if ((rc = qeth_clear_halt_card(card, use_halt))) | 3539 | if ((rc = qeth_clear_halt_card(card, use_halt))) |
3527 | QETH_DBF_TEXT_(trace, 3, "2err%d", rc); | 3540 | QETH_DBF_TEXT_(trace, 3, "2err%d", rc); |