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[] = {
