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 /drivers | |
| 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
Diffstat (limited to 'drivers')
| -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); |
