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