Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/qeth: conclude all event processing before offlining a card

Work for Bridgeport events is currently placed on a driver-wide
workqueue. If the card is removed and freed while any such work is still
active, this causes a use-after-free.
So put the events on a per-card queue, where we can control their
lifetime. As we also don't want stale events to last beyond an
offline & online cycle, flush this queue when setting the card offline.

Fixes: b4d72c08b358 ("qeth: bridgeport support - basic control")
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Julian Wiedmann and committed by
David S. Miller
c0a2e4d1 c2780c1a

+15 -5
+1 -1
drivers/s390/net/qeth_core.h
··· 790 790 struct qeth_seqno seqno; 791 791 struct qeth_card_options options; 792 792 793 + struct workqueue_struct *event_wq; 793 794 wait_queue_head_t wait_q; 794 795 spinlock_t mclock; 795 796 unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; ··· 964 963 extern const struct attribute_group qeth_device_attr_group; 965 964 extern const struct attribute_group qeth_device_blkt_group; 966 965 extern const struct device_type qeth_generic_devtype; 967 - extern struct workqueue_struct *qeth_wq; 968 966 969 967 int qeth_card_hw_is_reachable(struct qeth_card *); 970 968 const char *qeth_get_cardname_short(struct qeth_card *);
+8 -2
drivers/s390/net/qeth_core_main.c
··· 74 74 static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf); 75 75 static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); 76 76 77 - struct workqueue_struct *qeth_wq; 78 - EXPORT_SYMBOL_GPL(qeth_wq); 77 + static struct workqueue_struct *qeth_wq; 79 78 80 79 int qeth_card_hw_is_reachable(struct qeth_card *card) 81 80 { ··· 1468 1469 CARD_RDEV(card) = gdev->cdev[0]; 1469 1470 CARD_WDEV(card) = gdev->cdev[1]; 1470 1471 CARD_DDEV(card) = gdev->cdev[2]; 1472 + 1473 + card->event_wq = alloc_ordered_workqueue("%s", 0, dev_name(&gdev->dev)); 1474 + if (!card->event_wq) 1475 + goto out_wq; 1471 1476 if (qeth_setup_channel(&card->read, true)) 1472 1477 goto out_ip; 1473 1478 if (qeth_setup_channel(&card->write, true)) ··· 1487 1484 out_channel: 1488 1485 qeth_clean_channel(&card->read); 1489 1486 out_ip: 1487 + destroy_workqueue(card->event_wq); 1488 + out_wq: 1490 1489 dev_set_drvdata(&gdev->dev, NULL); 1491 1490 kfree(card); 1492 1491 out: ··· 5036 5031 qeth_clean_channel(&card->read); 5037 5032 qeth_clean_channel(&card->write); 5038 5033 qeth_clean_channel(&card->data); 5034 + destroy_workqueue(card->event_wq); 5039 5035 qeth_free_qdio_buffers(card); 5040 5036 unregister_service_level(&card->qeth_service_level); 5041 5037 dev_set_drvdata(&card->gdev->dev, NULL);
+4 -2
drivers/s390/net/qeth_l2_main.c
··· 369 369 qeth_clear_cmd_buffers(&card->read); 370 370 qeth_clear_cmd_buffers(&card->write); 371 371 } 372 + 373 + flush_workqueue(card->event_wq); 372 374 } 373 375 374 376 static int qeth_l2_process_inbound_buffer(struct qeth_card *card, ··· 1438 1436 data->card = card; 1439 1437 memcpy(&data->qports, qports, 1440 1438 sizeof(struct qeth_sbp_state_change) + extrasize); 1441 - queue_work(qeth_wq, &data->worker); 1439 + queue_work(card->event_wq, &data->worker); 1442 1440 } 1443 1441 1444 1442 struct qeth_bridge_host_data { ··· 1510 1508 data->card = card; 1511 1509 memcpy(&data->hostevs, hostevs, 1512 1510 sizeof(struct qeth_ipacmd_addr_change) + extrasize); 1513 - queue_work(qeth_wq, &data->worker); 1511 + queue_work(card->event_wq, &data->worker); 1514 1512 } 1515 1513 1516 1514 /* SETBRIDGEPORT support; sending commands */
+2
drivers/s390/net/qeth_l3_main.c
··· 1433 1433 qeth_clear_cmd_buffers(&card->read); 1434 1434 qeth_clear_cmd_buffers(&card->write); 1435 1435 } 1436 + 1437 + flush_workqueue(card->event_wq); 1436 1438 } 1437 1439 1438 1440 /*