summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Chant <achant@google.com>2016-09-14 17:21:48 -0700
committerPatrick Tjin <pattjin@google.com>2016-10-28 20:02:13 -0700
commit5a3d8a9df3e767f51424fcde6d8bcc6b4a9f414c (patch)
tree8984dd0042d30139c12e233d7b728a1bb915f6aa
parent6b9c8643169f6d4fbdf6ec05f864bc4357aee01c (diff)
downloadmsm-android-msm-bullhead-3.10-nougat-mr0.6.tar.gz
concurrent sysfs calls on the fw updater can cause ugly race conditions. Return EBUSY on concurrent sysfs calls. For sysfs calls which generate deferred work, prevent the deferred work from running concurrently with other sysfs calls. Change-Id: Ie33add946fbcca8309998e4cb7cb01525c667c7e Signed-off-by: Andrew Chant <achant@google.com> Bug: 31252388
-rw-r--r--drivers/input/touchscreen/synaptics_fw_update.c144
1 files changed, 109 insertions, 35 deletions
diff --git a/drivers/input/touchscreen/synaptics_fw_update.c b/drivers/input/touchscreen/synaptics_fw_update.c
index 79b3a780550b..ffa992b829a5 100644
--- a/drivers/input/touchscreen/synaptics_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_fw_update.c
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/firmware.h>
@@ -296,6 +297,7 @@ struct synaptics_rmi4_fwu_handle {
static struct synaptics_rmi4_fwu_handle *fwu;
DECLARE_COMPLETION(fwu_remove_complete);
+DEFINE_MUTEX(fwu_sysfs_mutex);
static unsigned int extract_uint(const unsigned char *ptr)
{
@@ -1713,34 +1715,47 @@ static ssize_t fwu_sysfs_show_image(struct file *data_file,
char *buf, loff_t pos, size_t count)
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ ssize_t retval;
+
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
if (count < fwu->config_size) {
dev_err(&rmi4_data->i2c_client->dev,
"%s: Not enough space (%zu bytes) in buffer\n",
__func__, count);
- return -EINVAL;
+ retval = -EINVAL;
+ goto show_image_exit;
}
memcpy(buf, fwu->read_config_buf, fwu->config_size);
-
- return fwu->config_size;
+ retval = fwu->config_size;
+show_image_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_store_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
{
+ ssize_t retval;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (!fwu->ext_data_source) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot use this without setting imagesize!\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto store_image_exit;
}
if (count > fwu->image_size - fwu->data_pos) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"%s: Not enough space in buffer\n",
__func__);
- return -EINVAL;
+ retval = -EINVAL;
+ goto store_image_exit;
}
memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]),
@@ -1749,8 +1764,11 @@ static ssize_t fwu_sysfs_store_image(struct file *data_file,
fwu->data_buffer = fwu->ext_data_source;
fwu->data_pos += count;
+ retval = count;
- return count;
+store_image_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_image_name_store(struct device *dev,
@@ -1758,11 +1776,15 @@ static ssize_t fwu_sysfs_image_name_store(struct device *dev,
{
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
char *strptr;
+ ssize_t retval;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
if (count >= NAME_BUFFER_SIZE) {
dev_err(&rmi4_data->i2c_client->dev,
"Input over %d characters long\n", NAME_BUFFER_SIZE);
- return -EINVAL;
+ retval = -EINVAL;
+ goto image_name_store_exit;
}
strptr = strnstr(buf, ".img",
@@ -1770,21 +1792,32 @@ static ssize_t fwu_sysfs_image_name_store(struct device *dev,
if (!strptr) {
dev_err(&rmi4_data->i2c_client->dev,
"Input is not valid .img file\n");
- return -EINVAL;
+ retval = -EINVAL;
+ goto image_name_store_exit;
}
strlcpy(rmi4_data->fw_image_name, buf, count);
- return count;
+ retval = count;
+
+image_name_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_image_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ ssize_t retval;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
if (strnlen(fwu->rmi4_data->fw_image_name, NAME_BUFFER_SIZE) > 0)
- return snprintf(buf, PAGE_SIZE, "%s\n",
+ retval = snprintf(buf, PAGE_SIZE, "%s\n",
fwu->rmi4_data->fw_image_name);
else
- return snprintf(buf, PAGE_SIZE, "No firmware name given\n");
+ retval = snprintf(buf, PAGE_SIZE, "No firmware name given\n");
+
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
@@ -1794,14 +1827,17 @@ static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto force_reflash_store_exit;
}
if (input != 1) {
retval = -EINVAL;
- goto exit;
+ goto force_reflash_store_exit;
}
if (LOCKDOWN)
fwu->do_lockdown = true;
@@ -1812,16 +1848,18 @@ static ssize_t fwu_sysfs_force_reflash_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to do reflash\n",
__func__);
- goto exit;
+ goto force_reflash_store_free_exit;
}
retval = count;
-exit:
+force_reflash_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = rmi4_data->board->do_lockdown;
+force_reflash_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1832,9 +1870,12 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto reflash_store_exit;
}
if (input & LOCKDOWN) {
@@ -1844,7 +1885,7 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
if ((input != NORMAL) && (input != FORCE)) {
retval = -EINVAL;
- goto exit;
+ goto reflash_store_exit;
}
if (input == FORCE)
@@ -1855,16 +1896,18 @@ static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to do reflash\n",
__func__);
- goto exit;
+ goto reflash_store_free_exit;
}
retval = count;
-exit:
+reflash_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = rmi4_data->board->do_lockdown;
+reflash_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1875,26 +1918,31 @@ static ssize_t fwu_sysfs_write_lockdown_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto lockdown_store_exit;
}
if (input != 1) {
retval = -EINVAL;
- goto exit;
+ goto lockdown_store_exit;
}
if (!fwu->ext_data_source) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot use this without loading image in manual way!\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto lockdown_store_exit;
}
if (fwu->rmi4_data->suspended == true) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot lockdown while device is in suspend\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto lockdown_store_exit;
}
retval = fwu_start_write_lockdown();
@@ -1902,16 +1950,18 @@ static ssize_t fwu_sysfs_write_lockdown_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to write lockdown block\n",
__func__);
- goto exit;
+ goto lockdown_store_free_exit;
}
retval = count;
-exit:
+lockdown_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
fwu->force_update = FORCE_UPDATE;
fwu->do_lockdown = rmi4_data->board->do_lockdown;
+lockdown_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1920,6 +1970,8 @@ static ssize_t fwu_sysfs_check_fw_store(struct device *dev,
{
unsigned int input = 0;
+ /* Takes fwu_sysfs_mutex in the deferred work function. */
+
if (sscanf(buf, "%u", &input) != 1)
return -EINVAL;
@@ -1942,26 +1994,31 @@ static ssize_t fwu_sysfs_write_config_store(struct device *dev,
unsigned int input;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
+
if (sscanf(buf, "%u", &input) != 1) {
retval = -EINVAL;
- goto exit;
+ goto write_config_store_exit;
}
if (input != 1) {
retval = -EINVAL;
- goto exit;
+ goto write_config_store_exit;
}
if (!fwu->ext_data_source) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot use this without loading image in manual way!\n");
- return -EAGAIN;
+ retval = -EAGAIN;
+ goto write_config_store_exit;
}
if (fwu->rmi4_data->suspended == true) {
dev_err(&fwu->rmi4_data->i2c_client->dev,
"Cannot write config while device is in suspend\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto write_config_store_exit;
}
retval = fwu_start_write_config();
@@ -1969,14 +2026,16 @@ static ssize_t fwu_sysfs_write_config_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to write config\n",
__func__);
- goto exit;
+ goto write_config_store_free_exit;
}
retval = count;
-exit:
+write_config_store_free_exit:
kfree(fwu->ext_data_source);
fwu->ext_data_source = NULL;
+write_config_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
return retval;
}
@@ -1999,7 +2058,11 @@ static ssize_t fwu_sysfs_read_config_store(struct device *dev,
return -EBUSY;
}
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
retval = fwu_do_read_config();
+ mutex_unlock(&fwu_sysfs_mutex);
+
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to read config\n",
@@ -2028,7 +2091,10 @@ static ssize_t fwu_sysfs_config_area_store(struct device *dev,
return -EINVAL;
}
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
fwu->config_area = config_area;
+ mutex_unlock(&fwu_sysfs_mutex);
return count;
}
@@ -2039,10 +2105,12 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
int retval;
unsigned long size;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ if (!mutex_trylock(&fwu_sysfs_mutex))
+ return -EBUSY;
retval = kstrtoul(buf, 10, &size);
if (retval)
- return retval;
+ goto image_size_store_exit;
fwu->image_size = size;
fwu->data_pos = 0;
@@ -2053,10 +2121,12 @@ static ssize_t fwu_sysfs_image_size_store(struct device *dev,
dev_err(&rmi4_data->i2c_client->dev,
"%s: Failed to alloc mem for image data\n",
__func__);
- return -ENOMEM;
+ retval = -ENOMEM;
}
- return count;
+image_size_store_exit:
+ mutex_unlock(&fwu_sysfs_mutex);
+ return retval;
}
static ssize_t fwu_sysfs_block_size_show(struct device *dev,
@@ -2241,6 +2311,8 @@ static void synaptics_rmi4_fwu_work(struct work_struct *work)
container_of(to_delayed_work(work),
struct synaptics_rmi4_fwu_handle, fwu_work);
+ mutex_lock(&fwu_sysfs_mutex);
+
if (fwu->fn_ptr->enable)
fwu->fn_ptr->enable(fwu->rmi4_data, false);
@@ -2248,6 +2320,8 @@ static void synaptics_rmi4_fwu_work(struct work_struct *work)
if (fwu->fn_ptr->enable)
fwu->fn_ptr->enable(fwu->rmi4_data, true);
+
+ mutex_unlock(&fwu_sysfs_mutex);
}
static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
@@ -2338,7 +2412,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
INIT_DELAYED_WORK(&fwu->fwu_work, synaptics_rmi4_fwu_work);
#endif
- retval = sysfs_create_bin_file(&rmi4_data->i2c_client->dev.kobj,
+ retval = sysfs_create_bin_file(&rmi4_data->i2c_client->dev.kobj,
&dev_attr_data);
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,