[RFC PATCH] bq27000 battery mutex protection

5 messages Options
Embed this post
Permalink
Michael Trimarchi

[RFC PATCH] bq27000 battery mutex protection

Reply Threaded More More options
Print post
Permalink


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 <[hidden email]>

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

Re: [RFC PATCH] bq27000 battery mutex protection

Reply Threaded More More options
Print post
Permalink
Michael Trimarchi wrote:
> 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.

Looks good, but I'd suggest to put the inner part of
bq27000_battery_get_property into a separate function and have a
small wrapper that does just the locking. This way, you can still
just return (keeps the code simpler) and there's no risk of ever
missing an unlock path.

- Werner

Lars-Peter Clausen

Re: [RFC PATCH] bq27000 battery mutex protection

Reply Threaded More More options
Print post
Permalink
In reply to this post by Michael Trimarchi
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael Trimarchi wrote:

>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 <[hidden email]>
>
>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");
>
> ...
>
> 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);
> }
 
You only need to protect the assignment to di->regs. There is no need
to hold the look for the whole function call.

- -Lars
 static enum power_supply_property bq27000_battery_props[] = {
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkrTdJAACgkQBX4mSR26RiPqVgCfVH8tV1HDb04wdj+ufJFuk+1c
7HAAnil8I/P+K+3pmh7gJfmeZwqw5AIe
=qCTo
-----END PGP SIGNATURE-----


Michael Trimarchi

Re: [RFC PATCH] bq27000 battery mutex protection

Reply Threaded More More options
Print post
Permalink
Lars-Peter Clausen wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Michael Trimarchi wrote:
>
>  
>> 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 <[hidden email]>
>>
>> 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");
>>
>> ...
>>
>> 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);
>> }
>>    
Ok, I will change it
Michael

>  
> You only need to protect the assignment to di->regs. There is no need
> to hold the look for the whole function call.
>
> - -Lars
>  static enum power_supply_property bq27000_battery_props[] = {
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iEYEARECAAYFAkrTdJAACgkQBX4mSR26RiPqVgCfVH8tV1HDb04wdj+ufJFuk+1c
> 7HAAnil8I/P+K+3pmh7gJfmeZwqw5AIe
> =qCTo
> -----END PGP SIGNATURE-----
>
>
>  


Michael Trimarchi

[RFC PATCH V2] bq27000 battery mutex protection

Reply Threaded More More options
Print post
Permalink
Hi

this is a revision. I prefer to change the function instead use a wrapper.
Is ok Werner?

Michael

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 <[hidden email]>

---
diff --git a/drivers/power/bq27000_battery.c b/drivers/power/bq27000_battery.c
index 01168ea..24f0c31 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,41 +282,55 @@ 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:
- if (di->regs.temp < 0)
- return di->regs.temp;
+ if (di->regs.temp < 0) {
+ ret = di->regs.temp;
+ goto out_unlock;
+ }
  /* K (in 0.25K units) is 273.15 up from C (in 0.1C)*/
  /* 10926 = 27315 * 4 / 10 */
  val->intval = (((long)di->regs.temp * 10l) - 10926) / 4;
@@ -319,33 +340,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)
@@ -368,7 +400,9 @@ static void bq27000_battery_work(struct work_struct *work)
  update_hdq_data(di, BQ27000_RSOC, ®s.rsoc);
 
  if (memcmp (®s, &di->regs, sizeof(regs)) != 0) {
+ mutex_lock(&battery_mutex);
  di->regs = regs;
+ mutex_unlock(&battery_mutex);
  power_supply_changed(&di->bat);
  }
  }