aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNate Case <ncase@xes-inc.com>2008-01-29 11:05:09 -0500
committerDavid S. Miller <davem@davemloft.net>2008-02-03 07:28:41 -0500
commit35b5f6b1a82b5c586e0b24c711dc6ba944e88ef1 (patch)
treecf08793802ce8f91f13e262c3b6cdcf0a01d95e9
parent2b91213064bd882c3adf35f028c6d12fab3269ec (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>
-rw-r--r--drivers/net/phy/mdio_bus.c2
-rw-r--r--drivers/net/phy/phy.c68
-rw-r--r--drivers/net/phy/phy_device.c11
-rw-r--r--include/linux/phy.h5
4 files changed, 55 insertions, 31 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
451out_unlock: 454out_unlock:
452 spin_unlock_bh(&phydev->lock); 455 mutex_unlock(&phydev->lock);
453 return err; 456 return err;
454} 457}
455EXPORT_SYMBOL(phy_start_aneg); 458EXPORT_SYMBOL(phy_start_aneg);
456 459
457 460
458static void phy_change(struct work_struct *work); 461static void phy_change(struct work_struct *work);
462static void phy_state_machine(struct work_struct *work);
459static void phy_timer(unsigned long data); 463static 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,
493void phy_stop_machine(struct phy_device *phydev) 498void 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 */
542void phy_error(struct phy_device *phydev) 548void 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 */
736void phy_stop(struct phy_device *phydev) 742void 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
753out_unlock: 759out_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 */
774void phy_start(struct phy_device *phydev) 780void 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}
792EXPORT_SYMBOL(phy_stop); 798EXPORT_SYMBOL(phy_stop);
793EXPORT_SYMBOL(phy_start); 799EXPORT_SYMBOL(phy_start);
794 800
795/* PHY timer which handles the state machine */ 801/**
796static 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 */
808static 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 */
993static 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);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 554836edd915..5e43ae751412 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -88,7 +88,7 @@ struct mii_bus {
88 88
89 /* A lock to ensure that only one thing can read/write 89 /* A lock to ensure that only one thing can read/write
90 * the MDIO bus at a time */ 90 * the MDIO bus at a time */
91 spinlock_t mdio_lock; 91 struct mutex mdio_lock;
92 92
93 struct device *dev; 93 struct device *dev;
94 94
@@ -284,10 +284,11 @@ struct phy_device {
284 284
285 /* Interrupt and Polling infrastructure */ 285 /* Interrupt and Polling infrastructure */
286 struct work_struct phy_queue; 286 struct work_struct phy_queue;
287 struct work_struct state_queue;
287 struct timer_list phy_timer; 288 struct timer_list phy_timer;
288 atomic_t irq_disable; 289 atomic_t irq_disable;
289 290
290 spinlock_t lock; 291 struct mutex lock;
291 292
292 struct net_device *attached_dev; 293 struct net_device *attached_dev;
293 294