aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Fries <David@Fries.net>2015-05-08 20:51:50 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-05-24 15:08:59 -0400
commitf7134eea05b2fb4a2c0935f8a540539fff01f3eb (patch)
tree756f42e02e8ca03891f7f2c01bb28405a27528b4
parentc3098356927254be270e5dc186a2ca144b64463b (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.c62
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));
59static int w1_strong_pullup = 1; 59static int w1_strong_pullup = 1;
60module_param_named(strong_pullup, w1_strong_pullup, int, 0); 60module_param_named(strong_pullup, w1_strong_pullup, int, 0);
61 61
62struct 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
62static int w1_therm_add_slave(struct w1_slave *sl) 71static 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
70static void w1_therm_remove_slave(struct w1_slave *sl) 81static 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
311pre_unlock:
282 mutex_unlock(&dev->bus_mutex); 312 mutex_unlock(&dev->bus_mutex);
283 313
284 return PAGE_SIZE - c; 314post_unlock:
315 atomic_dec(THERM_REFCNT(family_data));
316 return ret;
285} 317}
286 318
287static int __init w1_therm_init(void) 319static int __init w1_therm_init(void)