[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>

authored by Frank Pavlic and committed by Jeff Garzik f956b690 09d2d38a

+44 -30
+2 -1
drivers/s390/net/qeth.h
··· 463 463 QETH_QDIO_UNINITIALIZED, 464 464 QETH_QDIO_ALLOCATED, 465 465 QETH_QDIO_ESTABLISHED, 466 + QETH_QDIO_CLEANING 466 467 }; 467 468 468 469 struct qeth_buffer_pool_entry { ··· 538 537 } __attribute__ ((aligned(256))); 539 538 540 539 struct qeth_qdio_info { 541 - volatile enum qeth_qdio_info_states state; 540 + atomic_t state; 542 541 /* input */ 543 542 struct qeth_qdio_q *in_q; 544 543 struct qeth_qdio_buffer_pool in_buf_pool;
+42 -29
drivers/s390/net/qeth_main.c
··· 3179 3179 3180 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 3184 return 0; 3184 3185 3185 3186 card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), 3186 3187 GFP_KERNEL|GFP_DMA); 3187 3188 if (!card->qdio.in_q) 3188 - return - ENOMEM; 3189 + goto out_nomem; 3189 3190 QETH_DBF_TEXT(setup, 2, "inq"); 3190 3191 QETH_DBF_HEX(setup, 2, &card->qdio.in_q, sizeof(void *)); 3191 3192 memset(card->qdio.in_q, 0, sizeof(struct qeth_qdio_q)); ··· 3195 3194 card->qdio.in_q->bufs[i].buffer = 3196 3195 &card->qdio.in_q->qdio_bufs[i]; 3197 3196 /* inbound buffer pool */ 3198 - if (qeth_alloc_buffer_pool(card)){ 3199 - kfree(card->qdio.in_q); 3200 - return -ENOMEM; 3201 - } 3197 + if (qeth_alloc_buffer_pool(card)) 3198 + goto out_freeinq; 3202 3199 /* outbound */ 3203 3200 card->qdio.out_qs = 3204 3201 kmalloc(card->qdio.no_out_queues * 3205 3202 sizeof(struct qeth_qdio_out_q *), GFP_KERNEL); 3206 - if (!card->qdio.out_qs){ 3207 - qeth_free_buffer_pool(card); 3208 - return -ENOMEM; 3209 - } 3210 - for (i = 0; i < card->qdio.no_out_queues; ++i){ 3203 + if (!card->qdio.out_qs) 3204 + goto out_freepool; 3205 + for (i = 0; i < card->qdio.no_out_queues; ++i) { 3211 3206 card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q), 3212 3207 GFP_KERNEL|GFP_DMA); 3213 - if (!card->qdio.out_qs[i]){ 3214 - while (i > 0) 3215 - kfree(card->qdio.out_qs[--i]); 3216 - kfree(card->qdio.out_qs); 3217 - return -ENOMEM; 3218 - } 3208 + if (!card->qdio.out_qs[i]) 3209 + goto out_freeoutq; 3219 3210 QETH_DBF_TEXT_(setup, 2, "outq %i", i); 3220 3211 QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *)); 3221 3212 memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q)); ··· 3224 3231 INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list); 3225 3232 } 3226 3233 } 3227 - card->qdio.state = QETH_QDIO_ALLOCATED; 3228 3234 return 0; 3235 + 3236 + out_freeoutq: 3237 + while (i > 0) 3238 + kfree(card->qdio.out_qs[--i]); 3239 + kfree(card->qdio.out_qs); 3240 + out_freepool: 3241 + qeth_free_buffer_pool(card); 3242 + out_freeinq: 3243 + kfree(card->qdio.in_q); 3244 + out_nomem: 3245 + atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 3246 + return -ENOMEM; 3229 3247 } 3230 3248 3231 3249 static void ··· 3245 3241 int i, j; 3246 3242 3247 3243 QETH_DBF_TEXT(trace, 2, "freeqdbf"); 3248 - if (card->qdio.state == QETH_QDIO_UNINITIALIZED) 3244 + if (atomic_swap(&card->qdio.state, QETH_QDIO_UNINITIALIZED) == 3245 + QETH_QDIO_UNINITIALIZED) 3249 3246 return; 3250 3247 kfree(card->qdio.in_q); 3251 3248 /* inbound buffer pool */ ··· 3259 3254 kfree(card->qdio.out_qs[i]); 3260 3255 } 3261 3256 kfree(card->qdio.out_qs); 3262 - card->qdio.state = QETH_QDIO_UNINITIALIZED; 3263 3257 } 3264 3258 3265 3259 static void ··· 3280 3276 qeth_init_qdio_info(struct qeth_card *card) 3281 3277 { 3282 3278 QETH_DBF_TEXT(setup, 4, "intqdinf"); 3283 - card->qdio.state = QETH_QDIO_UNINITIALIZED; 3279 + atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); 3284 3280 /* inbound */ 3285 3281 card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT; 3286 3282 card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT; ··· 3343 3339 struct qdio_buffer **in_sbal_ptrs; 3344 3340 struct qdio_buffer **out_sbal_ptrs; 3345 3341 int i, j, k; 3346 - int rc; 3342 + int rc = 0; 3347 3343 3348 3344 QETH_DBF_TEXT(setup, 2, "qdioest"); 3349 3345 ··· 3402 3398 init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; 3403 3399 init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; 3404 3400 3405 - if (!(rc = qdio_initialize(&init_data))) 3406 - card->qdio.state = QETH_QDIO_ESTABLISHED; 3401 + if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, 3402 + QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) 3403 + if ((rc = qdio_initialize(&init_data))) 3404 + atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 3407 3405 3408 3406 kfree(out_sbal_ptrs); 3409 3407 kfree(in_sbal_ptrs); ··· 3521 3515 int rc = 0; 3522 3516 3523 3517 QETH_DBF_TEXT(trace,3,"qdioclr"); 3524 - if (card->qdio.state == QETH_QDIO_ESTABLISHED){ 3518 + switch (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ESTABLISHED, 3519 + QETH_QDIO_CLEANING)) { 3520 + case QETH_QDIO_ESTABLISHED: 3525 3521 if ((rc = qdio_cleanup(CARD_DDEV(card), 3526 - (card->info.type == QETH_CARD_TYPE_IQD) ? 3527 - QDIO_FLAG_CLEANUP_USING_HALT : 3528 - QDIO_FLAG_CLEANUP_USING_CLEAR))) 3522 + (card->info.type == QETH_CARD_TYPE_IQD) ? 3523 + QDIO_FLAG_CLEANUP_USING_HALT : 3524 + QDIO_FLAG_CLEANUP_USING_CLEAR))) 3529 3525 QETH_DBF_TEXT_(trace, 3, "1err%d", rc); 3530 - card->qdio.state = QETH_QDIO_ALLOCATED; 3526 + atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); 3527 + break; 3528 + case QETH_QDIO_CLEANING: 3529 + return rc; 3530 + default: 3531 + break; 3531 3532 } 3532 3533 if ((rc = qeth_clear_halt_card(card, use_halt))) 3533 3534 QETH_DBF_TEXT_(trace, 3, "2err%d", rc);