diff options
author | Sang-Hun Lee <sanlee@nvidia.com> | 2013-10-08 14:14:24 -0700 |
---|---|---|
committer | Harshada Kale <hkale@nvidia.com> | 2013-10-09 01:27:49 -0700 |
commit | 810f9b671e1764f101bf6c0bfa4797edf1f707dc (patch) | |
tree | dcb3ee42c34b4b4d14c70982fed1799b044daf9d /drivers | |
parent | 08e8dc0ee99d04b8bb9266396961e68291f5e2b2 (diff) |
input: misc: inv: fix race in shutdown and suspend
Problem description:
- Write to shutdown flag is not mutex protected
- nvi_work_func may be scheduled after shutdown or suspend
- kfifo is being freed for shutdown, which could
corrupt memory if there is a further access to
kfifo by nvi_pm_exit and its function calls
Fix description:
- Encapsulate write to shutdown flag with a mutex,
to ensure readers get valid information
- Wait for nvi_work_func to complete during shutdown and suspend
- Free kfifo only during remove
Bug 1361923
Change-Id: I64dabfb21a289354e7f7c58ac408dc48bcff9267
Signed-off-by: Sang-Hun Lee <sanlee@nvidia.com>
Reviewed-on: http://git-master/r/283813
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Deepak Nibade <dnibade@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/misc/mpu/inv_gyro.c | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/drivers/input/misc/mpu/inv_gyro.c b/drivers/input/misc/mpu/inv_gyro.c index eaf86c17c215..d1c2bcd93868 100644 --- a/drivers/input/misc/mpu/inv_gyro.c +++ b/drivers/input/misc/mpu/inv_gyro.c @@ -4381,6 +4381,8 @@ static int nvi_suspend(struct device *dev) spin_unlock_irqrestore(&inf->time_stamp_lock, flags); synchronize_irq(inf->i2c->irq); + flush_work_sync(&inf->work_struct); + mutex_lock(&inf->mutex); inf->suspend = true; err = nvi_pm(inf, NVI_PM_OFF); @@ -4423,29 +4425,34 @@ static void nvi_shutdown(struct i2c_client *client) int i; unsigned long flags; inf = i2c_get_clientdata(client); + if (inf == NULL) + return; + spin_lock_irqsave(&inf->time_stamp_lock, flags); if (!inf->irq_disabled) disable_irq_nosync(client->irq); inf->irq_disabled = true; spin_unlock_irqrestore(&inf->time_stamp_lock, flags); synchronize_irq(inf->i2c->irq); - if (inf != NULL) { - for (i = 0; i < AUX_PORT_SPECIAL; i++) { - if (inf->aux.port[i].nmp.shutdown_bypass) { - nvi_aux_bypass_enable(inf, true); - break; - } + + flush_work_sync(&inf->work_struct); + + mutex_lock(&inf->mutex); + for (i = 0; i < AUX_PORT_SPECIAL; i++) { + if (inf->aux.port[i].nmp.shutdown_bypass) { + nvi_aux_bypass_enable(inf, true); + break; } - inf->shutdown = true; - if (inf->inv_dev) - remove_sysfs_interfaces(inf); - kfifo_free(&inf->trigger.timestamps); - free_irq(client->irq, inf); - if (inf->idev) - input_unregister_device(inf->idev); - if ((INV_ITG3500 != inf->chip_type) && (inf->idev_dmp)) - input_unregister_device(inf->idev_dmp); } + inf->shutdown = true; + if (inf->inv_dev) + remove_sysfs_interfaces(inf); + free_irq(client->irq, inf); + mutex_unlock(&inf->mutex); + if (inf->idev) + input_unregister_device(inf->idev); + if ((INV_ITG3500 != inf->chip_type) && (inf->idev_dmp)) + input_unregister_device(inf->idev_dmp); } static int nvi_remove(struct i2c_client *client) @@ -4456,6 +4463,7 @@ static int nvi_remove(struct i2c_client *client) inf = i2c_get_clientdata(client); if (inf != NULL) { nvi_pm_exit(inf); + kfifo_free(&inf->trigger.timestamps); kfree(inf); } dev_info(&client->dev, "%s\n", __func__); @@ -4471,7 +4479,10 @@ static void nvi_work_func(struct work_struct *work) mutex_lock(&inf->mutex); nvi_pm_current = inf->pm; nvi_pm(inf, NVI_PM_OFF_FORCE); - if (!(inf->suspend || inf->shutdown)) { + /* + * If suspending, no need to revive the power state + */ + if (!(inf->suspend)) { nvi_pm(inf, nvi_pm_current); nvi_reset(inf, true, true); nvi_global_delay(inf); |