net: phy: Threaded interrupts allow some simplification
authorAndrew Lunn <andrew@lunn.ch>
Sun, 16 Oct 2016 17:56:51 +0000 (19:56 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Oct 2016 15:18:08 +0000 (11:18 -0400)
The PHY interrupts are now handled in a threaded interrupt handler,
which can sleep. The work queue is no longer needed, phy_change() can
be called directly. phy_mac_interrupt() still needs to be safe to call
in interrupt context, so keep the work queue, and use a helper to call
phy_change().

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
include/linux/phy.h

index 25f2b296aaba70e3fc5f578081f89c4c079792ac..bb673c63c85c7528232eec9d8d115f1738f2b58d 100644 (file)
@@ -664,7 +664,7 @@ static void phy_error(struct phy_device *phydev)
  * @phy_dat: phy_device pointer
  *
  * Description: When a PHY interrupt occurs, the handler disables
- * interrupts, and schedules a work task to clear the interrupt.
+ * interrupts, and uses phy_change to handle the interrupt.
  */
 static irqreturn_t phy_interrupt(int irq, void *phy_dat)
 {
@@ -673,15 +673,10 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
        if (PHY_HALTED == phydev->state)
                return IRQ_NONE;                /* It can't be ours.  */
 
-       /* The MDIO bus is not allowed to be written in interrupt
-        * context, so we need to disable the irq here.  A work
-        * queue will write the PHY to disable and clear the
-        * interrupt, and then reenable the irq line.
-        */
        disable_irq_nosync(irq);
        atomic_inc(&phydev->irq_disable);
 
-       queue_work(system_power_efficient_wq, &phydev->phy_queue);
+       phy_change(phydev);
 
        return IRQ_HANDLED;
 }
@@ -766,12 +761,6 @@ int phy_stop_interrupts(struct phy_device *phydev)
 
        free_irq(phydev->irq, phydev);
 
-       /* Cannot call flush_scheduled_work() here as desired because
-        * of rtnl_lock(), but we do not really care about what would
-        * be done, except from enable_irq(), so cancel any work
-        * possibly pending and take care of the matter below.
-        */
-       cancel_work_sync(&phydev->phy_queue);
        /* If work indeed has been cancelled, disable_irq() will have
         * been left unbalanced from phy_interrupt() and enable_irq()
         * has to be called so that other devices on the line work.
@@ -784,14 +773,11 @@ int phy_stop_interrupts(struct phy_device *phydev)
 EXPORT_SYMBOL(phy_stop_interrupts);
 
 /**
- * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes
- * @work: work_struct that describes the work to be done
+ * phy_change - Called by the phy_interrupt to handle PHY changes
+ * @phydev: phy_device struct that interrupted
  */
-void phy_change(struct work_struct *work)
+void phy_change(struct phy_device *phydev)
 {
-       struct phy_device *phydev =
-               container_of(work, struct phy_device, phy_queue);
-
        if (phy_interrupt_is_valid(phydev)) {
                if (phydev->drv->did_interrupt &&
                    !phydev->drv->did_interrupt(phydev))
@@ -832,6 +818,18 @@ phy_err:
        phy_error(phydev);
 }
 
+/**
+ * phy_change_work - Scheduled by the phy_mac_interrupt to handle PHY changes
+ * @work: work_struct that describes the work to be done
+ */
+void phy_change_work(struct work_struct *work)
+{
+       struct phy_device *phydev =
+               container_of(work, struct phy_device, phy_queue);
+
+       phy_change(phydev);
+}
+
 /**
  * phy_stop - Bring down the PHY link, and stop checking the status
  * @phydev: target phy_device struct
@@ -1116,6 +1114,15 @@ void phy_state_machine(struct work_struct *work)
                                   PHY_STATE_TIME * HZ);
 }
 
+/**
+ * phy_mac_interrupt - MAC says the link has changed
+ * @phydev: phy_device struct with changed link
+ * @new_link: Link is Up/Down.
+ *
+ * Description: The MAC layer is able indicate there has been a change
+ *   in the PHY link status. Set the new link status, and trigger the
+ *   state machine, work a work queue.
+ */
 void phy_mac_interrupt(struct phy_device *phydev, int new_link)
 {
        phydev->link = new_link;
index e977ba931878e77cb149fd5a66c5618f723500a3..ac440a815353d1a0826da9af6bcb35c6764d1f7a 100644 (file)
@@ -347,7 +347,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
 
        mutex_init(&dev->lock);
        INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
-       INIT_WORK(&dev->phy_queue, phy_change);
+       INIT_WORK(&dev->phy_queue, phy_change_work);
 
        /* Request the appropriate module unconditionally; don't
         * bother trying to do so only if it isn't already loaded,
index e25f1830fbcfbcfc34f76fc4f16c882109b736c6..c47378c9360781217ba0bc6e1ae85978a443bc62 100644 (file)
@@ -343,7 +343,7 @@ struct phy_c45_device_ids {
  * giving up on the current attempt at acquiring a link
  * irq: IRQ number of the PHY's interrupt (-1 if none)
  * phy_timer: The timer for handling the state machine
- * phy_queue: A work_queue for the interrupt
+ * phy_queue: A work_queue for the phy_mac_interrupt
  * attached_dev: The attached enet driver's device instance ptr
  * adjust_link: Callback for the enet controller to respond to
  * changes in the link state.
@@ -802,7 +802,8 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
 int phy_drivers_register(struct phy_driver *new_driver, int n,
                         struct module *owner);
 void phy_state_machine(struct work_struct *work);
-void phy_change(struct work_struct *work);
+void phy_change(struct phy_device *phydev);
+void phy_change_work(struct work_struct *work);
 void phy_mac_interrupt(struct phy_device *phydev, int new_link);
 void phy_start_machine(struct phy_device *phydev);
 void phy_stop_machine(struct phy_device *phydev);