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 | |
| 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>
| -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); |
