aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/iseries_veth.c
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2005-08-31 21:29:09 -0400
committerJeff Garzik <jgarzik@pobox.com>2005-08-31 22:39:43 -0400
commitf0c129caa34b4bb0944bbb758b56c3d85b105557 (patch)
tree84f22d680065e39d11be61ecd992d6b8597704dd /drivers/net/iseries_veth.c
parentec60beebed497691c97d674c1facac5ca3d7a4b3 (diff)
[PATCH] iseries_veth: Use kobjects to track lifecycle of connection structs
The iseries_veth driver can attach to multiple vlans, which correspond to multiple net devices. However there is only 1 connection between each LPAR, so the connection structure may be shared by multiple net devices. This makes module removal messy, because we can't deallocate the connections until we know there are no net devices still using them. The solution is to use ref counts on the connections, so we can delete them (actually stop) as soon as the ref count hits zero. This patch fixes (part of) a bug we were seeing with IPv6 sending probes to a dead LPAR, which would then hang us forever due to leftover skbs. Signed-off-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/net/iseries_veth.c')
-rw-r--r--drivers/net/iseries_veth.c121
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 *);
171static void veth_flush_pending(struct veth_lpar_connection *cnx); 172static void veth_flush_pending(struct veth_lpar_connection *cnx);
172static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *); 173static void veth_receive(struct veth_lpar_connection *, struct VethLpEvent *);
173static void veth_timed_ack(unsigned long connectionPtr); 174static void veth_timed_ack(unsigned long connectionPtr);
175static void veth_release_connection(struct kobject *kobject);
176
177static 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
663static void veth_stop_connection(u8 rlp) 677static 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
688static void veth_destroy_connection(u8 rlp) 700static 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
721static 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
1350static int veth_remove(struct vio_dev *vdev) 1368static 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 = {
1406void __exit veth_module_cleanup(void) 1453void __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}
1433module_exit(veth_module_cleanup); 1478module_exit(veth_module_cleanup);
1434 1479
@@ -1456,7 +1501,7 @@ int __init veth_module_init(void)
1456 1501
1457error: 1502error:
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;