All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2] regulator: core: fix a possible race in disable_work handling
@ 2017-07-12 11:38 Tirupathi Reddy
  0 siblings, 0 replies; only message in thread
From: Tirupathi Reddy @ 2017-07-12 11:38 UTC (permalink / raw)
  To: broonie, lgirdwood; +Cc: linux-arm-msm, linux-kernel, Tirupathi Reddy

A race condition between queueing and processing the disable_work
instances results in having a work instance in the queue and the
deferred_disables variable of regulator device structure having a
value '0'. If no new regulator_disable_deferred() call later from
clients, the deferred_disables variable value remains '0' and hits
BUG() in regulator_disable_work() when the queued instance scheduled
for processing the work.

The race occurs as below:

	Core-0					     Core-1
	.....	       /* deferred_disables = 2 */   .....
	.....	       /* disable_work is queued */  .....
	.....					     .....
regulator_disable_deferred: 		regulator_disable_work:
   mutex_lock(&rdev->mutex);			     .....
   rdev->deferred_disables++;		             .....
   mutex_unlock(&rdev->mutex);			     .....
   queue_delayed_work(...)		    mutex_lock(&rdev->mutex);
	.....				    count =rdev->deferred_disables;
	.....				    rdev->deferred_disables = 0;
	.....					     .....
	.....				    mutex_unlock(&rdev->mutex);
	.....					     .....
	.....				    return;
	.....					     .....
	/* No new regulator_disable_deferred() calls from clients */
	/* The newly queued instance is scheduled for processing */
	.....					     .....
regulator_disable_work:
	.....
   mutex_lock(&rdev->mutex);
   BUG_ON(!rdev->deferred_disables); /* deferred_disables = 0 */

The race is fixed by removing the work instance that is queued while
processing the previous queued instance. Cancel the newly queued instance
from disable_work() handler just after reset the deferred_disables variable
to value '0'. Also move the work queueing step before mutex_unlock in
regulator_disable_deferred().

Also use mod_delayed_work() in the pace of queue_delayed_work() as
queue_delayed_work() always uses the delay requested in the first call
when multiple consumers call regulator_disable_deferred() close in time
and does not guarantee the semantics of regulator_disable_deferred().

Signed-off-by: Tirupathi Reddy <tirupath@codeaurora.org>
---
Changes from PATCH V1:
	Fixed commit message.

 drivers/regulator/core.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index e567fa5..9f4d484 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2396,6 +2396,14 @@ static void regulator_disable_work(struct work_struct *work)
 	count = rdev->deferred_disables;
 	rdev->deferred_disables = 0;
 
+	/*
+	 * Workqueue functions queue the new work instance while the previous
+	 * work instance is being processed. Cancel the queued work instance
+	 * as the work instance under processing does the job of the queued
+	 * work instance.
+	 */
+	cancel_delayed_work(&rdev->disable_work);
+
 	for (i = 0; i < count; i++) {
 		ret = _regulator_disable(rdev);
 		if (ret != 0)
@@ -2439,10 +2447,10 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
 
 	mutex_lock(&rdev->mutex);
 	rdev->deferred_disables++;
+	mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
+			 msecs_to_jiffies(ms));
 	mutex_unlock(&rdev->mutex);
 
-	queue_delayed_work(system_power_efficient_wq, &rdev->disable_work,
-			   msecs_to_jiffies(ms));
 	return 0;
 }
 EXPORT_SYMBOL_GPL(regulator_disable_deferred);
-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2017-07-12 11:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-12 11:38 [PATCH V2] regulator: core: fix a possible race in disable_work handling Tirupathi Reddy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.