diff options
author | David Fries <David@Fries.net> | 2015-05-08 20:51:50 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-05-24 15:08:59 -0400 |
commit | f7134eea05b2fb4a2c0935f8a540539fff01f3eb (patch) | |
tree | 756f42e02e8ca03891f7f2c01bb28405a27528b4 | |
parent | c3098356927254be270e5dc186a2ca144b64463b (diff) |
w1_therm reference count family data
A temperature conversion can take 750 ms and when possible the
w1_therm slave driver drops the bus_mutex to allow other bus
operations, but that includes operations such as a periodic slave
search, which can remove this slave when it is no longer detected.
If that happens the sl->family_data will be freed and set to NULL
causing w1_slave_show to crash when it wakes up.
Signed-off-by: David Fries <David@Fries.net>
Reported-By: Thorsten Bschorr <thorsten@bschorr.de>
Tested-by: Thorsten Bschorr <thorsten@bschorr.de>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/w1/slaves/w1_therm.c | 62 |
1 files changed, 47 insertions, 15 deletions
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 1f11a20a8ab9..55eb86c9e214 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c | |||
@@ -59,16 +59,32 @@ MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00)); | |||
59 | static int w1_strong_pullup = 1; | 59 | static int w1_strong_pullup = 1; |
60 | module_param_named(strong_pullup, w1_strong_pullup, int, 0); | 60 | module_param_named(strong_pullup, w1_strong_pullup, int, 0); |
61 | 61 | ||
62 | struct w1_therm_family_data { | ||
63 | uint8_t rom[9]; | ||
64 | atomic_t refcnt; | ||
65 | }; | ||
66 | |||
67 | /* return the address of the refcnt in the family data */ | ||
68 | #define THERM_REFCNT(family_data) \ | ||
69 | (&((struct w1_therm_family_data*)family_data)->refcnt) | ||
70 | |||
62 | static int w1_therm_add_slave(struct w1_slave *sl) | 71 | static int w1_therm_add_slave(struct w1_slave *sl) |
63 | { | 72 | { |
64 | sl->family_data = kzalloc(9, GFP_KERNEL); | 73 | sl->family_data = kzalloc(sizeof(struct w1_therm_family_data), |
74 | GFP_KERNEL); | ||
65 | if (!sl->family_data) | 75 | if (!sl->family_data) |
66 | return -ENOMEM; | 76 | return -ENOMEM; |
77 | atomic_set(THERM_REFCNT(sl->family_data), 1); | ||
67 | return 0; | 78 | return 0; |
68 | } | 79 | } |
69 | 80 | ||
70 | static void w1_therm_remove_slave(struct w1_slave *sl) | 81 | static void w1_therm_remove_slave(struct w1_slave *sl) |
71 | { | 82 | { |
83 | int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data)); | ||
84 | while(refcnt) { | ||
85 | msleep(1000); | ||
86 | refcnt = atomic_read(THERM_REFCNT(sl->family_data)); | ||
87 | } | ||
72 | kfree(sl->family_data); | 88 | kfree(sl->family_data); |
73 | sl->family_data = NULL; | 89 | sl->family_data = NULL; |
74 | } | 90 | } |
@@ -194,13 +210,22 @@ static ssize_t w1_slave_show(struct device *device, | |||
194 | struct w1_slave *sl = dev_to_w1_slave(device); | 210 | struct w1_slave *sl = dev_to_w1_slave(device); |
195 | struct w1_master *dev = sl->master; | 211 | struct w1_master *dev = sl->master; |
196 | u8 rom[9], crc, verdict, external_power; | 212 | u8 rom[9], crc, verdict, external_power; |
197 | int i, max_trying = 10; | 213 | int i, ret, max_trying = 10; |
198 | ssize_t c = PAGE_SIZE; | 214 | ssize_t c = PAGE_SIZE; |
215 | u8 *family_data = sl->family_data; | ||
216 | |||
217 | ret = mutex_lock_interruptible(&dev->bus_mutex); | ||
218 | if (ret != 0) | ||
219 | goto post_unlock; | ||
199 | 220 | ||
200 | i = mutex_lock_interruptible(&dev->bus_mutex); | 221 | if(!sl->family_data) |
201 | if (i != 0) | 222 | { |
202 | return i; | 223 | ret = -ENODEV; |
224 | goto pre_unlock; | ||
225 | } | ||
203 | 226 | ||
227 | /* prevent the slave from going away in sleep */ | ||
228 | atomic_inc(THERM_REFCNT(family_data)); | ||
204 | memset(rom, 0, sizeof(rom)); | 229 | memset(rom, 0, sizeof(rom)); |
205 | 230 | ||
206 | while (max_trying--) { | 231 | while (max_trying--) { |
@@ -230,17 +255,19 @@ static ssize_t w1_slave_show(struct device *device, | |||
230 | mutex_unlock(&dev->bus_mutex); | 255 | mutex_unlock(&dev->bus_mutex); |
231 | 256 | ||
232 | sleep_rem = msleep_interruptible(tm); | 257 | sleep_rem = msleep_interruptible(tm); |
233 | if (sleep_rem != 0) | 258 | if (sleep_rem != 0) { |
234 | return -EINTR; | 259 | ret = -EINTR; |
260 | goto post_unlock; | ||
261 | } | ||
235 | 262 | ||
236 | i = mutex_lock_interruptible(&dev->bus_mutex); | 263 | ret = mutex_lock_interruptible(&dev->bus_mutex); |
237 | if (i != 0) | 264 | if (ret != 0) |
238 | return i; | 265 | goto post_unlock; |
239 | } else if (!w1_strong_pullup) { | 266 | } else if (!w1_strong_pullup) { |
240 | sleep_rem = msleep_interruptible(tm); | 267 | sleep_rem = msleep_interruptible(tm); |
241 | if (sleep_rem != 0) { | 268 | if (sleep_rem != 0) { |
242 | mutex_unlock(&dev->bus_mutex); | 269 | ret = -EINTR; |
243 | return -EINTR; | 270 | goto pre_unlock; |
244 | } | 271 | } |
245 | } | 272 | } |
246 | 273 | ||
@@ -269,19 +296,24 @@ static ssize_t w1_slave_show(struct device *device, | |||
269 | c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", | 296 | c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", |
270 | crc, (verdict) ? "YES" : "NO"); | 297 | crc, (verdict) ? "YES" : "NO"); |
271 | if (verdict) | 298 | if (verdict) |
272 | memcpy(sl->family_data, rom, sizeof(rom)); | 299 | memcpy(family_data, rom, sizeof(rom)); |
273 | else | 300 | else |
274 | dev_warn(device, "Read failed CRC check\n"); | 301 | dev_warn(device, "Read failed CRC check\n"); |
275 | 302 | ||
276 | for (i = 0; i < 9; ++i) | 303 | for (i = 0; i < 9; ++i) |
277 | c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", | 304 | c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", |
278 | ((u8 *)sl->family_data)[i]); | 305 | ((u8 *)family_data)[i]); |
279 | 306 | ||
280 | c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", | 307 | c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", |
281 | w1_convert_temp(rom, sl->family->fid)); | 308 | w1_convert_temp(rom, sl->family->fid)); |
309 | ret = PAGE_SIZE - c; | ||
310 | |||
311 | pre_unlock: | ||
282 | mutex_unlock(&dev->bus_mutex); | 312 | mutex_unlock(&dev->bus_mutex); |
283 | 313 | ||
284 | return PAGE_SIZE - c; | 314 | post_unlock: |
315 | atomic_dec(THERM_REFCNT(family_data)); | ||
316 | return ret; | ||
285 | } | 317 | } |
286 | 318 | ||
287 | static int __init w1_therm_init(void) | 319 | static int __init w1_therm_init(void) |