Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a390d1f3 authored by Marcin Slusarz's avatar Marcin Slusarz Committed by David S. Miller
Browse files

phylib: convert state_queue work to delayed_work



It closes a race in phy_stop_machine when reprogramming of phy_timer
(from phy_state_machine) happens between del_timer_sync and cancel_work_sync.

Without this change it could lead to crash if phy_device would be freed after
phy_stop_machine (timer would fire and schedule freed work).

Signed-off-by: default avatarMarcin Slusarz <marcin.slusarz@gmail.com>
Acked-by: default avatarJean Delvare <khali@linux-fr.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 34cd347c
Loading
Loading
Loading
Loading
+11 −30
Original line number Original line Diff line number Diff line
@@ -414,7 +414,6 @@ EXPORT_SYMBOL(phy_start_aneg);


static void phy_change(struct work_struct *work);
static void phy_change(struct work_struct *work);
static void phy_state_machine(struct work_struct *work);
static void phy_state_machine(struct work_struct *work);
static void phy_timer(unsigned long data);


/**
/**
 * phy_start_machine - start PHY state machine tracking
 * phy_start_machine - start PHY state machine tracking
@@ -434,11 +433,8 @@ void phy_start_machine(struct phy_device *phydev,
{
{
	phydev->adjust_state = handler;
	phydev->adjust_state = handler;


	INIT_WORK(&phydev->state_queue, phy_state_machine);
	INIT_DELAYED_WORK(&phydev->state_queue, phy_state_machine);
	init_timer(&phydev->phy_timer);
	schedule_delayed_work(&phydev->state_queue, jiffies + HZ);
	phydev->phy_timer.function = &phy_timer;
	phydev->phy_timer.data = (unsigned long) phydev;
	mod_timer(&phydev->phy_timer, jiffies + HZ);
}
}


/**
/**
@@ -451,8 +447,7 @@ void phy_start_machine(struct phy_device *phydev,
 */
 */
void phy_stop_machine(struct phy_device *phydev)
void phy_stop_machine(struct phy_device *phydev)
{
{
	del_timer_sync(&phydev->phy_timer);
	cancel_delayed_work_sync(&phydev->state_queue);
	cancel_work_sync(&phydev->state_queue);


	mutex_lock(&phydev->lock);
	mutex_lock(&phydev->lock);
	if (phydev->state > PHY_UP)
	if (phydev->state > PHY_UP)
@@ -680,11 +675,9 @@ static void phy_change(struct work_struct *work)
	if (err)
	if (err)
		goto irq_enable_err;
		goto irq_enable_err;


	/* Stop timer and run the state queue now.  The work function for
	/* reschedule state queue work to run as soon as possible */
	 * state_queue will start the timer up again.
	cancel_delayed_work_sync(&phydev->state_queue);
	 */
	schedule_delayed_work(&phydev->state_queue, 0);
	del_timer(&phydev->phy_timer);
	schedule_work(&phydev->state_queue);


	return;
	return;


@@ -761,14 +754,13 @@ EXPORT_SYMBOL(phy_start);
/**
/**
 * phy_state_machine - Handle the state machine
 * phy_state_machine - Handle the state machine
 * @work: work_struct that describes the work to be done
 * @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)
static void phy_state_machine(struct work_struct *work)
{
{
	struct delayed_work *dwork =
			container_of(work, struct delayed_work, work);
	struct phy_device *phydev =
	struct phy_device *phydev =
			container_of(work, struct phy_device, state_queue);
			container_of(dwork, struct phy_device, state_queue);
	int needs_aneg = 0;
	int needs_aneg = 0;
	int err = 0;
	int err = 0;


@@ -946,17 +938,6 @@ static void phy_state_machine(struct work_struct *work)
	if (err < 0)
	if (err < 0)
		phy_error(phydev);
		phy_error(phydev);


	mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
	schedule_delayed_work(&phydev->state_queue,
}
				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);
}
}
+1 −2
Original line number Original line Diff line number Diff line
@@ -315,8 +315,7 @@ struct phy_device {


	/* Interrupt and Polling infrastructure */
	/* Interrupt and Polling infrastructure */
	struct work_struct phy_queue;
	struct work_struct phy_queue;
	struct work_struct state_queue;
	struct delayed_work state_queue;
	struct timer_list phy_timer;
	atomic_t irq_disable;
	atomic_t irq_disable;


	struct mutex lock;
	struct mutex lock;