diff options
-rw-r--r-- | drivers/net/iseries_veth.c | 121 |
1 files changed, 83 insertions, 38 deletions
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index ab9fb218d111..5ce9f9348042 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c | |||
@@ -129,6 +129,7 @@ struct veth_lpar_connection { | |||
129 | int num_events; | 129 | int num_events; |
130 | struct VethCapData local_caps; | 130 | struct VethCapData local_caps; |
131 | 131 | ||
132 | struct kobject kobject; | ||
132 | struct timer_list ack_timer; | 133 | struct timer_list ack_timer; |
133 | 134 | ||
134 | spinlock_t lock; | 135 | spinlock_t lock; |
@@ -171,6 +172,11 @@ static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); | |||
171 | static void veth_flush_pending(struct veth_lpar_connection *cnx); | 172 | static void veth_flush_pending(struct veth_lpar_connection *cnx); |
172 | static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); | 173 | static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); |
173 | static void veth_timed_ack(unsigned long connectionPtr); | 174 | static void veth_timed_ack(unsigned long connectionPtr); |
175 | static void veth_release_connection(struct kobject *kobject); | ||
176 | |||
177 | static struct kobj_type veth_lpar_connection_ktype = { | ||
178 | .release = veth_release_connection | ||
179 | }; | ||
174 | 180 | ||
175 | /* | 181 | /* |
176 | * Utility functions | 182 | * Utility functions |
@@ -611,7 +617,7 @@ static int veth_init_connection(u8 rlp) | |||
611 | { | 617 | { |
612 | struct veth_lpar_connection *cnx; | 618 | struct veth_lpar_connection *cnx; |
613 | struct veth_msg *msgs; | 619 | struct veth_msg *msgs; |
614 | int i; | 620 | int i, rc; |
615 | 621 | ||
616 | if ( (rlp == this_lp) | 622 | if ( (rlp == this_lp) |
617 | || ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) | 623 | || ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) |
@@ -632,6 +638,14 @@ static int veth_init_connection(u8 rlp) | |||
632 | 638 | ||
633 | veth_cnx[rlp] = cnx; | 639 | veth_cnx[rlp] = cnx; |
634 | 640 | ||
641 | /* This gets us 1 reference, which is held on behalf of the driver | ||
642 | * infrastructure. It's released at module unload. */ | ||
643 | kobject_init(&cnx->kobject); | ||
644 | cnx->kobject.ktype = &veth_lpar_connection_ktype; | ||
645 | rc = kobject_set_name(&cnx->kobject, "cnx%.2d", rlp); | ||
646 | if (rc != 0) | ||
647 | return rc; | ||
648 | |||
635 | msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL); | 649 | msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL); |
636 | if (! msgs) { | 650 | if (! msgs) { |
637 | veth_error("Can't allocate buffers for LPAR %d.\n", rlp); | 651 | veth_error("Can't allocate buffers for LPAR %d.\n", rlp); |
@@ -660,11 +674,9 @@ static int veth_init_connection(u8 rlp) | |||
660 | return 0; | 674 | return 0; |
661 | } | 675 | } |
662 | 676 | ||
663 | static void veth_stop_connection(u8 rlp) | 677 | static void veth_stop_connection(struct veth_lpar_connection *cnx) |
664 | { | 678 | { |
665 | struct veth_lpar_connection *cnx = veth_cnx[rlp]; | 679 | if (!cnx) |
666 | |||
667 | if (! cnx) | ||
668 | return; | 680 | return; |
669 | 681 | ||
670 | spin_lock_irq(&cnx->lock); | 682 | spin_lock_irq(&cnx->lock); |
@@ -685,11 +697,9 @@ static void veth_stop_connection(u8 rlp) | |||
685 | flush_scheduled_work(); | 697 | flush_scheduled_work(); |
686 | } | 698 | } |
687 | 699 | ||
688 | static void veth_destroy_connection(u8 rlp) | 700 | static void veth_destroy_connection(struct veth_lpar_connection *cnx) |
689 | { | 701 | { |
690 | struct veth_lpar_connection *cnx = veth_cnx[rlp]; | 702 | if (!cnx) |
691 | |||
692 | if (! cnx) | ||
693 | return; | 703 | return; |
694 | 704 | ||
695 | if (cnx->num_events > 0) | 705 | if (cnx->num_events > 0) |
@@ -704,8 +714,16 @@ static void veth_destroy_connection(u8 rlp) | |||
704 | NULL, NULL); | 714 | NULL, NULL); |
705 | 715 | ||
706 | kfree(cnx->msgs); | 716 | kfree(cnx->msgs); |
717 | veth_cnx[cnx->remote_lp] = NULL; | ||
707 | kfree(cnx); | 718 | kfree(cnx); |
708 | veth_cnx[rlp] = NULL; | 719 | } |
720 | |||
721 | static void veth_release_connection(struct kobject *kobj) | ||
722 | { | ||
723 | struct veth_lpar_connection *cnx; | ||
724 | cnx = container_of(kobj, struct veth_lpar_connection, kobject); | ||
725 | veth_stop_connection(cnx); | ||
726 | veth_destroy_connection(cnx); | ||
709 | } | 727 | } |
710 | 728 | ||
711 | /* | 729 | /* |
@@ -1349,15 +1367,31 @@ static void veth_timed_ack(unsigned long ptr) | |||
1349 | 1367 | ||
1350 | static int veth_remove(struct vio_dev *vdev) | 1368 | static int veth_remove(struct vio_dev *vdev) |
1351 | { | 1369 | { |
1352 | int i = vdev->unit_address; | 1370 | struct veth_lpar_connection *cnx; |
1353 | struct net_device *dev; | 1371 | struct net_device *dev; |
1372 | struct veth_port *port; | ||
1373 | int i; | ||
1354 | 1374 | ||
1355 | dev = veth_dev[i]; | 1375 | dev = veth_dev[vdev->unit_address]; |
1356 | if (dev != NULL) { | 1376 | |
1357 | veth_dev[i] = NULL; | 1377 | if (! dev) |
1358 | unregister_netdev(dev); | 1378 | return 0; |
1359 | free_netdev(dev); | 1379 | |
1380 | port = netdev_priv(dev); | ||
1381 | |||
1382 | for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { | ||
1383 | cnx = veth_cnx[i]; | ||
1384 | |||
1385 | if (cnx && (port->lpar_map & (1 << i))) { | ||
1386 | /* Drop our reference to connections on our VLAN */ | ||
1387 | kobject_put(&cnx->kobject); | ||
1388 | } | ||
1360 | } | 1389 | } |
1390 | |||
1391 | veth_dev[vdev->unit_address] = NULL; | ||
1392 | unregister_netdev(dev); | ||
1393 | free_netdev(dev); | ||
1394 | |||
1361 | return 0; | 1395 | return 0; |
1362 | } | 1396 | } |
1363 | 1397 | ||
@@ -1365,6 +1399,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
1365 | { | 1399 | { |
1366 | int i = vdev->unit_address; | 1400 | int i = vdev->unit_address; |
1367 | struct net_device *dev; | 1401 | struct net_device *dev; |
1402 | struct veth_port *port; | ||
1368 | 1403 | ||
1369 | dev = veth_probe_one(i, &vdev->dev); | 1404 | dev = veth_probe_one(i, &vdev->dev); |
1370 | if (dev == NULL) { | 1405 | if (dev == NULL) { |
@@ -1373,11 +1408,23 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
1373 | } | 1408 | } |
1374 | veth_dev[i] = dev; | 1409 | veth_dev[i] = dev; |
1375 | 1410 | ||
1376 | /* Start the state machine on each connection, to commence | 1411 | port = (struct veth_port*)netdev_priv(dev); |
1377 | * link negotiation */ | 1412 | |
1378 | for (i = 0; i < HVMAXARCHITECTEDLPS; i++) | 1413 | /* Start the state machine on each connection on this vlan. If we're |
1379 | if (veth_cnx[i]) | 1414 | * the first dev to do so this will commence link negotiation */ |
1380 | veth_kick_statemachine(veth_cnx[i]); | 1415 | for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { |
1416 | struct veth_lpar_connection *cnx; | ||
1417 | |||
1418 | if (! (port->lpar_map & (1 << i))) | ||
1419 | continue; | ||
1420 | |||
1421 | cnx = veth_cnx[i]; | ||
1422 | if (!cnx) | ||
1423 | continue; | ||
1424 | |||
1425 | kobject_get(&cnx->kobject); | ||
1426 | veth_kick_statemachine(cnx); | ||
1427 | } | ||
1381 | 1428 | ||
1382 | return 0; | 1429 | return 0; |
1383 | } | 1430 | } |
@@ -1406,29 +1453,27 @@ static struct vio_driver veth_driver = { | |||
1406 | void __exit veth_module_cleanup(void) | 1453 | void __exit veth_module_cleanup(void) |
1407 | { | 1454 | { |
1408 | int i; | 1455 | int i; |
1456 | struct veth_lpar_connection *cnx; | ||
1409 | 1457 | ||
1410 | /* Stop the queues first to stop any new packets being sent. */ | 1458 | /* Disconnect our "irq" to stop events coming from the Hypervisor. */ |
1411 | for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) | ||
1412 | if (veth_dev[i]) | ||
1413 | netif_stop_queue(veth_dev[i]); | ||
1414 | |||
1415 | /* Stop the connections before we unregister the driver. This | ||
1416 | * ensures there's no skbs lying around holding the device open. */ | ||
1417 | for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) | ||
1418 | veth_stop_connection(i); | ||
1419 | |||
1420 | HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); | 1459 | HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); |
1421 | 1460 | ||
1422 | /* Hypervisor callbacks may have scheduled more work while we | 1461 | /* Make sure any work queued from Hypervisor callbacks is finished. */ |
1423 | * were stoping connections. Now that we've disconnected from | ||
1424 | * the hypervisor make sure everything's finished. */ | ||
1425 | flush_scheduled_work(); | 1462 | flush_scheduled_work(); |
1426 | 1463 | ||
1427 | vio_unregister_driver(&veth_driver); | 1464 | for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { |
1465 | cnx = veth_cnx[i]; | ||
1466 | |||
1467 | if (!cnx) | ||
1468 | continue; | ||
1428 | 1469 | ||
1429 | for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) | 1470 | /* Drop the driver's reference to the connection */ |
1430 | veth_destroy_connection(i); | 1471 | kobject_put(&cnx->kobject); |
1472 | } | ||
1431 | 1473 | ||
1474 | /* Unregister the driver, which will close all the netdevs and stop | ||
1475 | * the connections when they're no longer referenced. */ | ||
1476 | vio_unregister_driver(&veth_driver); | ||
1432 | } | 1477 | } |
1433 | module_exit(veth_module_cleanup); | 1478 | module_exit(veth_module_cleanup); |
1434 | 1479 | ||
@@ -1456,7 +1501,7 @@ int __init veth_module_init(void) | |||
1456 | 1501 | ||
1457 | error: | 1502 | error: |
1458 | for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { | 1503 | for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { |
1459 | veth_destroy_connection(i); | 1504 | veth_destroy_connection(veth_cnx[i]); |
1460 | } | 1505 | } |
1461 | 1506 | ||
1462 | return rc; | 1507 | return rc; |