On Thu, 2008-01-03 at 17:36 -0600, Nate Case wrote:
> 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 <[EMAIL PROTECTED]>

Excellent ! I've been wanting to do that for some time. I'll be able to
convert EMAC to phylib now :-) I'll review the patch in detail as I do
this convesion, maybe next week. Thanks !

Ben.

> ---
>  drivers/net/phy/mdio_bus.c   |    2 +-
>  drivers/net/phy/phy.c        |   68 ++++++++++++++++++++++++++++-------------
>  drivers/net/phy/phy_device.c |   11 +++----
>  include/linux/phy.h          |    5 ++-
>  4 files changed, 55 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> index c30196d..6e9f619 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)
>       int i;
>       int err = 0;
>  
> -     spin_lock_init(&bus->mdio_lock);
> +     mutex_init(&bus->mdio_lock);
>  
>       if (NULL == bus || NULL == bus->name ||
>                       NULL == bus->read ||
> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> index 7c9e6e3..12fccb1 100644
> --- a/drivers/net/phy/phy.c
> +++ b/drivers/net/phy/phy.c
> @@ -26,7 +26,6 @@
>  #include <linux/netdevice.h>
>  #include <linux/etherdevice.h>
>  #include <linux/skbuff.h>
> -#include <linux/spinlock.h>
>  #include <linux/mm.h>
>  #include <linux/module.h>
>  #include <linux/mii.h>
> @@ -72,9 +71,11 @@ int phy_read(struct phy_device *phydev, u16 regnum)
>       int retval;
>       struct mii_bus *bus = phydev->bus;
>  
> -     spin_lock_bh(&bus->mdio_lock);
> +     BUG_ON(in_interrupt());
> +
> +     mutex_lock(&bus->mdio_lock);
>       retval = bus->read(bus, phydev->addr, regnum);
> -     spin_unlock_bh(&bus->mdio_lock);
> +     mutex_unlock(&bus->mdio_lock);
>  
>       return retval;
>  }
> @@ -95,9 +96,11 @@ int phy_write(struct phy_device *phydev, u16 regnum, u16 
> val)
>       int err;
>       struct mii_bus *bus = phydev->bus;
>  
> -     spin_lock_bh(&bus->mdio_lock);
> +     BUG_ON(in_interrupt());
> +
> +     mutex_lock(&bus->mdio_lock);
>       err = bus->write(bus, phydev->addr, regnum, val);
> -     spin_unlock_bh(&bus->mdio_lock);
> +     mutex_unlock(&bus->mdio_lock);
>  
>       return err;
>  }
> @@ -428,7 +431,7 @@ int phy_start_aneg(struct phy_device *phydev)
>  {
>       int err;
>  
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>  
>       if (AUTONEG_DISABLE == phydev->autoneg)
>               phy_sanitize_settings(phydev);
> @@ -449,13 +452,14 @@ int phy_start_aneg(struct phy_device *phydev)
>       }
>  
>  out_unlock:
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>       return err;
>  }
>  EXPORT_SYMBOL(phy_start_aneg);
>  
> 
>  static void phy_change(struct work_struct *work);
> +static void phy_state_machine(struct work_struct *work);
>  static void phy_timer(unsigned long data);
>  
>  /**
> @@ -476,6 +480,7 @@ void phy_start_machine(struct phy_device *phydev,
>  {
>       phydev->adjust_state = handler;
>  
> +     INIT_WORK(&phydev->state_queue, phy_state_machine);
>       init_timer(&phydev->phy_timer);
>       phydev->phy_timer.function = &phy_timer;
>       phydev->phy_timer.data = (unsigned long) phydev;
> @@ -493,11 +498,12 @@ void phy_start_machine(struct phy_device *phydev,
>  void phy_stop_machine(struct phy_device *phydev)
>  {
>       del_timer_sync(&phydev->phy_timer);
> +     cancel_work_sync(&phydev->state_queue);
>  
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>       if (phydev->state > PHY_UP)
>               phydev->state = PHY_UP;
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  
>       phydev->adjust_state = NULL;
>  }
> @@ -541,9 +547,9 @@ static void phy_force_reduction(struct phy_device *phydev)
>   */
>  void phy_error(struct phy_device *phydev)
>  {
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>       phydev->state = PHY_HALTED;
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  }
>  
>  /**
> @@ -705,10 +711,10 @@ static void phy_change(struct work_struct *work)
>       if (err)
>               goto phy_err;
>  
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>       if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
>               phydev->state = PHY_CHANGELINK;
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  
>       atomic_dec(&phydev->irq_disable);
>       enable_irq(phydev->irq);
> @@ -735,7 +741,7 @@ phy_err:
>   */
>  void phy_stop(struct phy_device *phydev)
>  {
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>  
>       if (PHY_HALTED == phydev->state)
>               goto out_unlock;
> @@ -751,7 +757,7 @@ void phy_stop(struct phy_device *phydev)
>       phydev->state = PHY_HALTED;
>  
>  out_unlock:
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  
>       /*
>        * Cannot call flush_scheduled_work() here as desired because
> @@ -773,7 +779,7 @@ out_unlock:
>   */
>  void phy_start(struct phy_device *phydev)
>  {
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>  
>       switch (phydev->state) {
>               case PHY_STARTING:
> @@ -787,19 +793,26 @@ void phy_start(struct phy_device *phydev)
>               default:
>                       break;
>       }
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  }
>  EXPORT_SYMBOL(phy_stop);
>  EXPORT_SYMBOL(phy_start);
>  
> -/* PHY timer which handles the state machine */
> -static void phy_timer(unsigned long data)
> +/**
> + * phy_state_machine - Handle the state machine
> + * @work: work_struct that describes the work to be done
> + *
> + * Description: Scheduled by the state_queue workqueue each time
> + *   phy_timer is triggered.
> + */
> +static void phy_state_machine(struct work_struct *work)
>  {
> -     struct phy_device *phydev = (struct phy_device *)data;
> +     struct phy_device *phydev =
> +                     container_of(work, struct phy_device, state_queue);
>       int needs_aneg = 0;
>       int err = 0;
>  
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>  
>       if (phydev->adjust_state)
>               phydev->adjust_state(phydev->attached_dev);
> @@ -965,7 +978,7 @@ static void phy_timer(unsigned long data)
>                       break;
>       }
>  
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  
>       if (needs_aneg)
>               err = phy_start_aneg(phydev);
> @@ -976,3 +989,14 @@ static void phy_timer(unsigned long data)
>       mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
>  }
>  
> +/* PHY timer which schedules the state machine work */
> +static void phy_timer(unsigned long data)
> +{
> +     struct phy_device *phydev = (struct phy_device *)data;
> +
> +     /*
> +      * PHY I/O operations can potentially sleep so we ensure that
> +      * it's done from a process context
> +      */
> +     schedule_work(&phydev->state_queue);
> +}
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 5b9e175..f4c4fd8 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -25,7 +25,6 @@
>  #include <linux/netdevice.h>
>  #include <linux/etherdevice.h>
>  #include <linux/skbuff.h>
> -#include <linux/spinlock.h>
>  #include <linux/mm.h>
>  #include <linux/module.h>
>  #include <linux/mii.h>
> @@ -80,7 +79,7 @@ struct phy_device* phy_device_create(struct mii_bus *bus, 
> int addr, int phy_id)
>  
>       dev->state = PHY_DOWN;
>  
> -     spin_lock_init(&dev->lock);
> +     mutex_init(&dev->lock);
>  
>       return dev;
>  }
> @@ -656,7 +655,7 @@ static int phy_probe(struct device *dev)
>       if (!(phydrv->flags & PHY_HAS_INTERRUPT))
>               phydev->irq = PHY_POLL;
>  
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>  
>       /* Start out supporting everything. Eventually,
>        * a controller will attach, and may modify one
> @@ -670,7 +669,7 @@ static int phy_probe(struct device *dev)
>       if (phydev->drv->probe)
>               err = phydev->drv->probe(phydev);
>  
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  
>       return err;
>  
> @@ -682,9 +681,9 @@ static int phy_remove(struct device *dev)
>  
>       phydev = to_phy_device(dev);
>  
> -     spin_lock_bh(&phydev->lock);
> +     mutex_lock(&phydev->lock);
>       phydev->state = PHY_DOWN;
> -     spin_unlock_bh(&phydev->lock);
> +     mutex_unlock(&phydev->lock);
>  
>       if (phydev->drv->remove)
>               phydev->drv->remove(phydev);
> diff --git a/include/linux/phy.h b/include/linux/phy.h
> index 554836e..5e43ae7 100644
> --- a/include/linux/phy.h
> +++ b/include/linux/phy.h
> @@ -88,7 +88,7 @@ struct mii_bus {
>  
>       /* A lock to ensure that only one thing can read/write
>        * the MDIO bus at a time */
> -     spinlock_t mdio_lock;
> +     struct mutex mdio_lock;
>  
>       struct device *dev;
>  
> @@ -284,10 +284,11 @@ struct phy_device {
>  
>       /* Interrupt and Polling infrastructure */
>       struct work_struct phy_queue;
> +     struct work_struct state_queue;
>       struct timer_list phy_timer;
>       atomic_t irq_disable;
>  
> -     spinlock_t lock;
> +     struct mutex lock;
>  
>       struct net_device *attached_dev;
>  

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to