aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFrank Pavlic <fpavlic@de.ibm.com>2006-09-15 10:26:52 -0400
committerJeff Garzik <jeff@garzik.org>2006-09-17 01:03:07 -0400
commitf956b6902eabbff249000287c7b36cd65761d8b8 (patch)
tree8452e831e5361be54e392cf678abefa55716a719 /drivers
parent09d2d38a152419467f764c0f730821e896766c1f (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')
-rw-r--r--drivers/s390/net/qeth.h3
-rw-r--r--drivers/s390/net/qeth_main.c71
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
468struct qeth_buffer_pool_entry { 469struct qeth_buffer_pool_entry {
@@ -537,7 +538,7 @@ struct qeth_qdio_out_q {
537} __attribute__ ((aligned(256))); 538} __attribute__ ((aligned(256)));
538 539
539struct qeth_qdio_info { 540struct 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
3229out_freeoutq:
3230 while (i > 0)
3231 kfree(card->qdio.out_qs[--i]);
3232 kfree(card->qdio.out_qs);
3233out_freepool:
3234 qeth_free_buffer_pool(card);
3235out_freeinq:
3236 kfree(card->qdio.in_q);
3237out_nomem:
3238 atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
3239 return -ENOMEM;
3236} 3240}
3237 3241
3238static void 3242static 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
3260static void 3264static void
@@ -3276,7 +3280,7 @@ static void
3276qeth_init_qdio_info(struct qeth_card *card) 3280qeth_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);