Protect the data using a mutex. Fix a race that can happen when
the user read from the sysfs and the worker execute in the middle.

Signed-off-by: Michael Trimarchi <[email protected]>

diff --git a/drivers/power/bq27000_battery.c b/drivers/power/bq27000_battery.c
index 01168ea..ddee537 100644
--- a/drivers/power/bq27000_battery.c
+++ b/drivers/power/bq27000_battery.c
@@ -134,6 +134,7 @@ struct bq27000_device_info {
 	struct bq27000_bat_regs regs;
 };
 
+static DEFINE_MUTEX(battery_mutex);
 static unsigned int cache_time = 5000;
 module_param(cache_time, uint, 0644);
 MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
@@ -210,11 +211,15 @@ static int bq27000_battery_get_property(struct power_supply *psy,
 				       union power_supply_propval *val)
 {
 	int n;
+	int ret = 0;
 	struct bq27000_device_info *di =
 		container_of(psy, struct bq27000_device_info, bat);
 
-	if (di->regs.rsoc < 0 && psp != POWER_SUPPLY_PROP_PRESENT)
-		return -ENODEV;
+	mutex_lock(&battery_mutex);
+	if (di->regs.rsoc < 0 && psp != POWER_SUPPLY_PROP_PRESENT) {
+		ret = -ENODEV;
+		goto out_unlock;
+	}
 
 	switch (psp) {
 	case POWER_SUPPLY_PROP_STATUS:
@@ -265,8 +270,10 @@ use_bat:
 			break;
 		}
 		/* power is actually going in or out... */
-		if (di->regs.flags < 0)
-			return di->regs.flags;
+		if (di->regs.flags < 0) {
+			ret = di->regs.flags;
+			goto out_unlock;
+		}
 		if (di->regs.flags & BQ27000_STATUS_CHGS)
 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
 		else
@@ -275,36 +282,48 @@ use_bat:
 	case POWER_SUPPLY_PROP_HEALTH:
 		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
 		/* Do we have accurate readings... */
-		if (di->regs.flags < 0)
-			return di->regs.flags;
+		if (di->regs.flags < 0) {
+			ret = di->regs.flags;
+			goto out_unlock;
+		}
 		if (di->regs.flags & BQ27000_STATUS_VDQ)
 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		if (di->regs.volt < 0)
-			return di->regs.volt;
+		if (di->regs.volt < 0) {
+			ret = di->regs.volt;
+			goto out_unlock;
+		}
 		/* mV -> uV */
 		val->intval = di->regs.volt * 1000;
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		if (di->regs.flags < 0)
-			return di->regs.flags;
+		if (di->regs.flags < 0) {
+			ret = di->regs.flags;
+			goto out_unlock;
+		}
 		if (di->regs.flags & BQ27000_STATUS_CHGS)
 			n = -NANOVOLTS_UNIT;
 		else
 			n = NANOVOLTS_UNIT;
-		if (di->regs.ai < 0)
-			return di->regs.ai;
+		if (di->regs.ai < 0) {
+			ret = di->regs.ai;
+			goto out_unlock;
+		}
 		val->intval = (di->regs.ai * n) / di->pdata->rsense_mohms;
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_FULL:
-		if (di->regs.lmd < 0)
-			return di->regs.lmd;
+		if (di->regs.lmd < 0) {
+			ret = di->regs.lmd;
+			goto out_unlock;
+		}
 		val->intval = (di->regs.lmd * 3570) / di->pdata->rsense_mohms;
 		break;
 	case POWER_SUPPLY_PROP_CHARGE_NOW:
-		if (di->regs.nac < 0)
-			return di->regs.nac;
+		if (di->regs.nac < 0) {
+			ret = di->regs.nac;
+			goto out_unlock;
+		}
 		val->intval = (di->regs.nac * 3570) / di->pdata->rsense_mohms;
 		break;
 	case POWER_SUPPLY_PROP_TEMP:
@@ -319,33 +338,44 @@ use_bat:
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
 		val->intval = di->regs.rsoc;
-		if (val->intval < 0)
-			return val->intval;
+		if (val->intval < 0) {
+			ret = val->intval;
+			goto out_unlock;
+		}
 		break;
 	case POWER_SUPPLY_PROP_PRESENT:
 		val->intval = !(di->regs.rsoc < 0);
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-		if (di->regs.tte < 0)
-			return di->regs.tte;
+		if (di->regs.tte < 0) {
+			ret = di->regs.tte;
+			goto out_unlock;
+		}
 		val->intval = 60 * di->regs.tte;
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
-		if (di->regs.ttf < 0)
-			return di->regs.ttf;
+		if (di->regs.ttf < 0) {
+			ret = di->regs.ttf;
+			goto out_unlock;
+		}
 		val->intval = 60 * di->regs.ttf;
 		break;
 	case POWER_SUPPLY_PROP_ONLINE:
 		if (di->pdata->get_charger_online_status)
 			val->intval = (di->pdata->get_charger_online_status)();
-		else
-			return -EINVAL;
+		else {
+			ret = -EINVAL;
+			goto out_unlock;
+		}
 		break;
 	default:
-		return -EINVAL;
+		ret = -EINVAL;
+		break;
 	}
 
-	return 0;
+out_unlock:
+	mutex_unlock(&battery_mutex);
+	return ret;
 }
 
 static void bq27000_battery_work(struct work_struct *work)
@@ -353,6 +383,8 @@ static void bq27000_battery_work(struct work_struct *work)
 	struct bq27000_device_info *di =
 		container_of(work, struct bq27000_device_info, work.work);
 
+	mutex_lock(&battery_mutex);
+
 	if ((di->pdata->hdq_initialized)()) {
 		struct bq27000_bat_regs regs;
 
@@ -375,6 +407,8 @@ static void bq27000_battery_work(struct work_struct *work)
 
 	if (!schedule_delayed_work(&di->work, cache_time))
 		dev_err(di->dev, "battery service reschedule failed\n");
+
+	mutex_unlock(&battery_mutex);
 }
 
 static enum power_supply_property bq27000_battery_props[] = {

Reply via email to