diff options
author | Nate Case <ncase@xes-inc.com> | 2008-01-29 11:05:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-02-03 07:28:41 -0500 |
commit | 35b5f6b1a82b5c586e0b24c711dc6ba944e88ef1 (patch) | |
tree | cf08793802ce8f91f13e262c3b6cdcf0a01d95e9 /drivers/net/phy | |
parent | 2b91213064bd882c3adf35f028c6d12fab3269ec (diff) |
PHYLIB: Locking fixes for PHY I/O potentially sleeping
PHY read/write functions can potentially sleep (e.g., a PHY accessed
via I2C). The following changes were made to account for this:
* Change spin locks to mutex locks
* Add a BUG_ON() to phy_read() phy_write() to warn against
calling them from an interrupt context.
* Use work queue for PHY state machine handling since
it can potentially sleep
* Change phydev lock from spinlock to mutex
Signed-off-by: Nate Case <ncase@xes-inc.com>
Acked-by: Andy Fleming <afleming@freescale.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 68 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 11 |
3 files changed, 52 insertions, 29 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c30196d0ad16..6e9f619c491f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c | |||
@@ -49,7 +49,7 @@ int mdiobus_register(struct mii_bus *bus) | |||
49 | int i; | 49 | int i; |
50 | int err = 0; | 50 | int err = 0; |
51 | 51 | ||
52 | spin_lock_init(&bus->mdio_lock); | 52 | mutex_init(&bus->mdio_lock); |
53 | 53 | ||
54 | if (NULL == bus || NULL == bus->name || | 54 | if (NULL == bus || NULL == bus->name || |
55 | NULL == bus->read || | 55 | NULL == bus->read || |
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7c9e6e349503..12fccb1c76dc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c | |||
@@ -26,7 +26,6 @@ | |||
26 | #include <linux/netdevice.h> | 26 | #include <linux/netdevice.h> |
27 | #include <linux/etherdevice.h> | 27 | #include <linux/etherdevice.h> |
28 | #include <linux/skbuff.h> | 28 | #include <linux/skbuff.h> |
29 | #include <linux/spinlock.h> | ||
30 | #include <linux/mm.h> | 29 | #include <linux/mm.h> |
31 | #include <linux/module.h> | 30 | #include <linux/module.h> |
32 | #include <linux/mii.h> | 31 | #include <linux/mii.h> |
@@ -72,9 +71,11 @@ int phy_read(struct phy_device *phydev, u16 regnum) | |||
72 | int retval; | 71 | int retval; |
73 | struct mii_bus *bus = phydev->bus; | 72 | struct mii_bus *bus = phydev->bus; |
74 | 73 | ||
75 | spin_lock_bh(&bus->mdio_lock); | 74 | BUG_ON(in_interrupt()); |
75 | |||
76 | mutex_lock(&bus->mdio_lock); | ||
76 | retval = bus->read(bus, phydev->addr, regnum); | 77 | retval = bus->read(bus, phydev->addr, regnum); |
77 | spin_unlock_bh(&bus->mdio_lock); | 78 | mutex_unlock(&bus->mdio_lock); |
78 | 79 | ||
79 | return retval; | 80 | return retval; |
80 | } | 81 | } |
@@ -95,9 +96,11 @@ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) | |||
95 | int err; | 96 | int err; |
96 | struct mii_bus *bus = phydev->bus; | 97 | struct mii_bus *bus = phydev->bus; |
97 | 98 | ||
98 | spin_lock_bh(&bus->mdio_lock); | 99 | BUG_ON(in_interrupt()); |
100 | |||
101 | mutex_lock(&bus->mdio_lock); | ||
99 | err = bus->write(bus, phydev->addr, regnum, val); | 102 | err = bus->write(bus, phydev->addr, regnum, val); |
100 | spin_unlock_bh(&bus->mdio_lock); | 103 | mutex_unlock(&bus->mdio_lock); |
101 | 104 | ||
102 | return err; | 105 | return err; |
103 | } | 106 | } |
@@ -428,7 +431,7 @@ int phy_start_aneg(struct phy_device *phydev) | |||
428 | { | 431 | { |
429 | int err; | 432 | int err; |
430 | 433 | ||
431 | spin_lock_bh(&phydev->lock); | 434 | mutex_lock(&phydev->lock); |
432 | 435 | ||
433 | if (AUTONEG_DISABLE == phydev->autoneg) | 436 | if (AUTONEG_DISABLE == phydev->autoneg) |
434 | phy_sanitize_settings(phydev); | 437 | phy_sanitize_settings(phydev); |
@@ -449,13 +452,14 @@ int phy_start_aneg(struct phy_device *phydev) | |||
449 | } | 452 | } |
450 | 453 | ||
451 | out_unlock: | 454 | out_unlock: |
452 | spin_unlock_bh(&phydev->lock); | 455 | mutex_unlock(&phydev->lock); |
453 | return err; | 456 | return err; |
454 | } | 457 | } |
455 | EXPORT_SYMBOL(phy_start_aneg); | 458 | EXPORT_SYMBOL(phy_start_aneg); |
456 | 459 | ||
457 | 460 | ||
458 | static void phy_change(struct work_struct *work); | 461 | static void phy_change(struct work_struct *work); |
462 | static void phy_state_machine(struct work_struct *work); | ||
459 | static void phy_timer(unsigned long data); | 463 | static void phy_timer(unsigned long data); |
460 | 464 | ||
461 | /** | 465 | /** |
@@ -476,6 +480,7 @@ void phy_start_machine(struct phy_device *phydev, | |||
476 | { | 480 | { |
477 | phydev->adjust_state = handler; | 481 | phydev->adjust_state = handler; |
478 | 482 | ||
483 | INIT_WORK(&phydev->state_queue, phy_state_machine); | ||
479 | init_timer(&phydev->phy_timer); | 484 | init_timer(&phydev->phy_timer); |
480 | phydev->phy_timer.function = &phy_timer; | 485 | phydev->phy_timer.function = &phy_timer; |
481 | phydev->phy_timer.data = (unsigned long) phydev; | 486 | phydev->phy_timer.data = (unsigned long) phydev; |
@@ -493,11 +498,12 @@ void phy_start_machine(struct phy_device *phydev, | |||
493 | void phy_stop_machine(struct phy_device *phydev) | 498 | void phy_stop_machine(struct phy_device *phydev) |
494 | { | 499 | { |
495 | del_timer_sync(&phydev->phy_timer); | 500 | del_timer_sync(&phydev->phy_timer); |
501 | cancel_work_sync(&phydev->state_queue); | ||
496 | 502 | ||
497 | spin_lock_bh(&phydev->lock); | 503 | mutex_lock(&phydev->lock); |
498 | if (phydev->state > PHY_UP) | 504 | if (phydev->state > PHY_UP) |
499 | phydev->state = PHY_UP; | 505 | phydev->state = PHY_UP; |
500 | spin_unlock_bh(&phydev->lock); | 506 | mutex_unlock(&phydev->lock); |
501 | 507 | ||
502 | phydev->adjust_state = NULL; | 508 | phydev->adjust_state = NULL; |
503 | } | 509 | } |
@@ -541,9 +547,9 @@ static void phy_force_reduction(struct phy_device *phydev) | |||
541 | */ | 547 | */ |
542 | void phy_error(struct phy_device *phydev) | 548 | void phy_error(struct phy_device *phydev) |
543 | { | 549 | { |
544 | spin_lock_bh(&phydev->lock); | 550 | mutex_lock(&phydev->lock); |
545 | phydev->state = PHY_HALTED; | 551 | phydev->state = PHY_HALTED; |
546 | spin_unlock_bh(&phydev->lock); | 552 | mutex_unlock(&phydev->lock); |
547 | } | 553 | } |
548 | 554 | ||
549 | /** | 555 | /** |
@@ -705,10 +711,10 @@ static void phy_change(struct work_struct *work) | |||
705 | if (err) | 711 | if (err) |
706 | goto phy_err; | 712 | goto phy_err; |
707 | 713 | ||
708 | spin_lock_bh(&phydev->lock); | 714 | mutex_lock(&phydev->lock); |
709 | if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state)) | 715 | if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state)) |
710 | phydev->state = PHY_CHANGELINK; | 716 | phydev->state = PHY_CHANGELINK; |
711 | spin_unlock_bh(&phydev->lock); | 717 | mutex_unlock(&phydev->lock); |
712 | 718 | ||
713 | atomic_dec(&phydev->irq_disable); | 719 | atomic_dec(&phydev->irq_disable); |
714 | enable_irq(phydev->irq); | 720 | enable_irq(phydev->irq); |
@@ -735,7 +741,7 @@ phy_err: | |||
735 | */ | 741 | */ |
736 | void phy_stop(struct phy_device *phydev) | 742 | void phy_stop(struct phy_device *phydev) |
737 | { | 743 | { |
738 | spin_lock_bh(&phydev->lock); | 744 | mutex_lock(&phydev->lock); |
739 | 745 | ||
740 | if (PHY_HALTED == phydev->state) | 746 | if (PHY_HALTED == phydev->state) |
741 | goto out_unlock; | 747 | goto out_unlock; |
@@ -751,7 +757,7 @@ void phy_stop(struct phy_device *phydev) | |||
751 | phydev->state = PHY_HALTED; | 757 | phydev->state = PHY_HALTED; |
752 | 758 | ||
753 | out_unlock: | 759 | out_unlock: |
754 | spin_unlock_bh(&phydev->lock); | 760 | mutex_unlock(&phydev->lock); |
755 | 761 | ||
756 | /* | 762 | /* |
757 | * Cannot call flush_scheduled_work() here as desired because | 763 | * Cannot call flush_scheduled_work() here as desired because |
@@ -773,7 +779,7 @@ out_unlock: | |||
773 | */ | 779 | */ |
774 | void phy_start(struct phy_device *phydev) | 780 | void phy_start(struct phy_device *phydev) |
775 | { | 781 | { |
776 | spin_lock_bh(&phydev->lock); | 782 | mutex_lock(&phydev->lock); |
777 | 783 | ||
778 | switch (phydev->state) { | 784 | switch (phydev->state) { |
779 | case PHY_STARTING: | 785 | case PHY_STARTING: |
@@ -787,19 +793,26 @@ void phy_start(struct phy_device *phydev) | |||
787 | default: | 793 | default: |
788 | break; | 794 | break; |
789 | } | 795 | } |
790 | spin_unlock_bh(&phydev->lock); | 796 | mutex_unlock(&phydev->lock); |
791 | } | 797 | } |
792 | EXPORT_SYMBOL(phy_stop); | 798 | EXPORT_SYMBOL(phy_stop); |
793 | EXPORT_SYMBOL(phy_start); | 799 | EXPORT_SYMBOL(phy_start); |
794 | 800 | ||
795 | /* PHY timer which handles the state machine */ | 801 | /** |
796 | static void phy_timer(unsigned long data) | 802 | * phy_state_machine - Handle the state machine |
803 | * @work: work_struct that describes the work to be done | ||
804 | * | ||
805 | * Description: Scheduled by the state_queue workqueue each time | ||
806 | * phy_timer is triggered. | ||
807 | */ | ||
808 | static void phy_state_machine(struct work_struct *work) | ||
797 | { | 809 | { |
798 | struct phy_device *phydev = (struct phy_device *)data; | 810 | struct phy_device *phydev = |
811 | container_of(work, struct phy_device, state_queue); | ||
799 | int needs_aneg = 0; | 812 | int needs_aneg = 0; |
800 | int err = 0; | 813 | int err = 0; |
801 | 814 | ||
802 | spin_lock_bh(&phydev->lock); | 815 | mutex_lock(&phydev->lock); |
803 | 816 | ||
804 | if (phydev->adjust_state) | 817 | if (phydev->adjust_state) |
805 | phydev->adjust_state(phydev->attached_dev); | 818 | phydev->adjust_state(phydev->attached_dev); |
@@ -965,7 +978,7 @@ static void phy_timer(unsigned long data) | |||
965 | break; | 978 | break; |
966 | } | 979 | } |
967 | 980 | ||
968 | spin_unlock_bh(&phydev->lock); | 981 | mutex_unlock(&phydev->lock); |
969 | 982 | ||
970 | if (needs_aneg) | 983 | if (needs_aneg) |
971 | err = phy_start_aneg(phydev); | 984 | err = phy_start_aneg(phydev); |
@@ -976,3 +989,14 @@ static void phy_timer(unsigned long data) | |||
976 | mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ); | 989 | mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ); |
977 | } | 990 | } |
978 | 991 | ||
992 | /* PHY timer which schedules the state machine work */ | ||
993 | static void phy_timer(unsigned long data) | ||
994 | { | ||
995 | struct phy_device *phydev = (struct phy_device *)data; | ||
996 | |||
997 | /* | ||
998 | * PHY I/O operations can potentially sleep so we ensure that | ||
999 | * it's done from a process context | ||
1000 | */ | ||
1001 | schedule_work(&phydev->state_queue); | ||
1002 | } | ||
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5b9e1751e1b4..f4c4fd85425f 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <linux/netdevice.h> | 25 | #include <linux/netdevice.h> |
26 | #include <linux/etherdevice.h> | 26 | #include <linux/etherdevice.h> |
27 | #include <linux/skbuff.h> | 27 | #include <linux/skbuff.h> |
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/mm.h> | 28 | #include <linux/mm.h> |
30 | #include <linux/module.h> | 29 | #include <linux/module.h> |
31 | #include <linux/mii.h> | 30 | #include <linux/mii.h> |
@@ -80,7 +79,7 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) | |||
80 | 79 | ||
81 | dev->state = PHY_DOWN; | 80 | dev->state = PHY_DOWN; |
82 | 81 | ||
83 | spin_lock_init(&dev->lock); | 82 | mutex_init(&dev->lock); |
84 | 83 | ||
85 | return dev; | 84 | return dev; |
86 | } | 85 | } |
@@ -656,7 +655,7 @@ static int phy_probe(struct device *dev) | |||
656 | if (!(phydrv->flags & PHY_HAS_INTERRUPT)) | 655 | if (!(phydrv->flags & PHY_HAS_INTERRUPT)) |
657 | phydev->irq = PHY_POLL; | 656 | phydev->irq = PHY_POLL; |
658 | 657 | ||
659 | spin_lock_bh(&phydev->lock); | 658 | mutex_lock(&phydev->lock); |
660 | 659 | ||
661 | /* Start out supporting everything. Eventually, | 660 | /* Start out supporting everything. Eventually, |
662 | * a controller will attach, and may modify one | 661 | * a controller will attach, and may modify one |
@@ -670,7 +669,7 @@ static int phy_probe(struct device *dev) | |||
670 | if (phydev->drv->probe) | 669 | if (phydev->drv->probe) |
671 | err = phydev->drv->probe(phydev); | 670 | err = phydev->drv->probe(phydev); |
672 | 671 | ||
673 | spin_unlock_bh(&phydev->lock); | 672 | mutex_unlock(&phydev->lock); |
674 | 673 | ||
675 | return err; | 674 | return err; |
676 | 675 | ||
@@ -682,9 +681,9 @@ static int phy_remove(struct device *dev) | |||
682 | 681 | ||
683 | phydev = to_phy_device(dev); | 682 | phydev = to_phy_device(dev); |
684 | 683 | ||
685 | spin_lock_bh(&phydev->lock); | 684 | mutex_lock(&phydev->lock); |
686 | phydev->state = PHY_DOWN; | 685 | phydev->state = PHY_DOWN; |
687 | spin_unlock_bh(&phydev->lock); | 686 | mutex_unlock(&phydev->lock); |
688 | 687 | ||
689 | if (phydev->drv->remove) | 688 | if (phydev->drv->remove) |
690 | phydev->drv->remove(phydev); | 689 | phydev->drv->remove(phydev); |