diff options
author | Pavel Herrmann <morpheus.ibis@gmail.com> | 2011-07-17 12:39:19 -0400 |
---|---|---|
committer | Jean Delvare <khali@endymion.delvare> | 2011-07-17 12:39:19 -0400 |
commit | d3f684f2820a7f42acef68bea6622d9032127fb2 (patch) | |
tree | 0ca1227b9c1499093e7af42d01ad75f41481b5e1 | |
parent | fa8b69758e65b406c8010936b541cd00deef804d (diff) |
hwmon: (max1111) Fix race condition causing NULL pointer exception
spi_sync call uses its spi_message parameter to keep completion information,
using a drvdata structure is not thread-safe. Use a mutex to prevent
multiple access to shared driver data.
Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
Acked-by: Pavel Machek <pavel@ucw.cz>
Acked-by: Marek Vasut <marek.vasut@gmail.com>
Acked-by: Cyril Hrubis <metan@ucw.cz>
Tested-by: Stanislav Brabec <utx@penguin.cz>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Cc: stable@kernel.org
-rw-r--r-- | drivers/hwmon/max1111.c | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 12a54aa29776..14335bbc9bdc 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c | |||
@@ -40,6 +40,8 @@ struct max1111_data { | |||
40 | struct spi_transfer xfer[2]; | 40 | struct spi_transfer xfer[2]; |
41 | uint8_t *tx_buf; | 41 | uint8_t *tx_buf; |
42 | uint8_t *rx_buf; | 42 | uint8_t *rx_buf; |
43 | struct mutex drvdata_lock; | ||
44 | /* protect msg, xfer and buffers from multiple access */ | ||
43 | }; | 45 | }; |
44 | 46 | ||
45 | static int max1111_read(struct device *dev, int channel) | 47 | static int max1111_read(struct device *dev, int channel) |
@@ -48,6 +50,9 @@ static int max1111_read(struct device *dev, int channel) | |||
48 | uint8_t v1, v2; | 50 | uint8_t v1, v2; |
49 | int err; | 51 | int err; |
50 | 52 | ||
53 | /* writing to drvdata struct is not thread safe, wait on mutex */ | ||
54 | mutex_lock(&data->drvdata_lock); | ||
55 | |||
51 | data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) | | 56 | data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) | |
52 | MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 | | 57 | MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 | |
53 | MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR; | 58 | MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR; |
@@ -55,12 +60,15 @@ static int max1111_read(struct device *dev, int channel) | |||
55 | err = spi_sync(data->spi, &data->msg); | 60 | err = spi_sync(data->spi, &data->msg); |
56 | if (err < 0) { | 61 | if (err < 0) { |
57 | dev_err(dev, "spi_sync failed with %d\n", err); | 62 | dev_err(dev, "spi_sync failed with %d\n", err); |
63 | mutex_unlock(&data->drvdata_lock); | ||
58 | return err; | 64 | return err; |
59 | } | 65 | } |
60 | 66 | ||
61 | v1 = data->rx_buf[0]; | 67 | v1 = data->rx_buf[0]; |
62 | v2 = data->rx_buf[1]; | 68 | v2 = data->rx_buf[1]; |
63 | 69 | ||
70 | mutex_unlock(&data->drvdata_lock); | ||
71 | |||
64 | if ((v1 & 0xc0) || (v2 & 0x3f)) | 72 | if ((v1 & 0xc0) || (v2 & 0x3f)) |
65 | return -EINVAL; | 73 | return -EINVAL; |
66 | 74 | ||
@@ -176,6 +184,8 @@ static int __devinit max1111_probe(struct spi_device *spi) | |||
176 | if (err) | 184 | if (err) |
177 | goto err_free_data; | 185 | goto err_free_data; |
178 | 186 | ||
187 | mutex_init(&data->drvdata_lock); | ||
188 | |||
179 | data->spi = spi; | 189 | data->spi = spi; |
180 | spi_set_drvdata(spi, data); | 190 | spi_set_drvdata(spi, data); |
181 | 191 | ||
@@ -213,6 +223,7 @@ static int __devexit max1111_remove(struct spi_device *spi) | |||
213 | 223 | ||
214 | hwmon_device_unregister(data->hwmon_dev); | 224 | hwmon_device_unregister(data->hwmon_dev); |
215 | sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); | 225 | sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); |
226 | mutex_destroy(&data->drvdata_lock); | ||
216 | kfree(data->rx_buf); | 227 | kfree(data->rx_buf); |
217 | kfree(data->tx_buf); | 228 | kfree(data->tx_buf); |
218 | kfree(data); | 229 | kfree(data); |