aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxim Levitsky <maximlevitsky@gmail.com>2010-02-22 13:39:30 -0500
committerDavid Woodhouse <David.Woodhouse@intel.com>2010-02-26 12:05:26 -0500
commit048d87199566663e4edc4880df3703c04bcf41d9 (patch)
treeda875d4f7802d6ca8a7db9f2cdba57b74d6c3672
parenta863862257b7dd08d855bafcb0aedd9ad848ed91 (diff)
mtd: blktrans: Hotplug fixes
* Add locking where it was missing. * Don't do a get_mtd_device in blktrans_open because it would lead to a deadlock; instead do that in add_mtd_blktrans_dev. * Only free the mtd_blktrans_dev structure when the last user exits. * Flush request queue on device removal. * Track users, and call tr->release in del_mtd_blktrans_dev Due to that ->open and release aren't called more that once. Now it is safe to call del_mtd_blktrans_dev while the device is still in use. Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/ftl.c1
-rwxr-xr-xdrivers/mtd/inftlcore.c1
-rw-r--r--drivers/mtd/mtd_blkdevs.c202
-rw-r--r--drivers/mtd/mtdblock.c2
-rw-r--r--drivers/mtd/mtdblock_ro.c1
-rw-r--r--drivers/mtd/nftlcore.c1
-rw-r--r--drivers/mtd/rfd_ftl.c1
-rw-r--r--drivers/mtd/ssfdc.c1
-rw-r--r--include/linux/mtd/blktrans.h3
9 files changed, 151 insertions, 62 deletions
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index e56d6b42f020..62da9eb7032b 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -1082,7 +1082,6 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1082{ 1082{
1083 del_mtd_blktrans_dev(dev); 1083 del_mtd_blktrans_dev(dev);
1084 ftl_freepart((partition_t *)dev); 1084 ftl_freepart((partition_t *)dev);
1085 kfree(dev);
1086} 1085}
1087 1086
1088static struct mtd_blktrans_ops ftl_tr = { 1087static struct mtd_blktrans_ops ftl_tr = {
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c
index 8aca5523a337..015a7fe1b6ee 100755
--- a/drivers/mtd/inftlcore.c
+++ b/drivers/mtd/inftlcore.c
@@ -139,7 +139,6 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
139 139
140 kfree(inftl->PUtable); 140 kfree(inftl->PUtable);
141 kfree(inftl->VUtable); 141 kfree(inftl->VUtable);
142 kfree(inftl);
143} 142}
144 143
145/* 144/*
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 6a572625bfc0..646cc84ae692 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -24,6 +24,40 @@
24#include "mtdcore.h" 24#include "mtdcore.h"
25 25
26static LIST_HEAD(blktrans_majors); 26static LIST_HEAD(blktrans_majors);
27static DEFINE_MUTEX(blktrans_ref_mutex);
28
29void blktrans_dev_release(struct kref *kref)
30{
31 struct mtd_blktrans_dev *dev =
32 container_of(kref, struct mtd_blktrans_dev, ref);
33
34 dev->disk->private_data = NULL;
35 put_disk(dev->disk);
36 list_del(&dev->list);
37 kfree(dev);
38}
39
40static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk)
41{
42 struct mtd_blktrans_dev *dev;
43
44 mutex_lock(&blktrans_ref_mutex);
45 dev = disk->private_data;
46
47 if (!dev)
48 goto unlock;
49 kref_get(&dev->ref);
50unlock:
51 mutex_unlock(&blktrans_ref_mutex);
52 return dev;
53}
54
55void blktrans_dev_put(struct mtd_blktrans_dev *dev)
56{
57 mutex_lock(&blktrans_ref_mutex);
58 kref_put(&dev->ref, blktrans_dev_release);
59 mutex_unlock(&blktrans_ref_mutex);
60}
27 61
28 62
29static int do_blktrans_request(struct mtd_blktrans_ops *tr, 63static int do_blktrans_request(struct mtd_blktrans_ops *tr,
@@ -111,81 +145,112 @@ static int mtd_blktrans_thread(void *arg)
111 145
112static void mtd_blktrans_request(struct request_queue *rq) 146static void mtd_blktrans_request(struct request_queue *rq)
113{ 147{
114 struct mtd_blktrans_dev *dev = rq->queuedata; 148 struct mtd_blktrans_dev *dev;
115 wake_up_process(dev->thread); 149 struct request *req = NULL;
116} 150
151 dev = rq->queuedata;
117 152
153 if (!dev)
154 while ((req = blk_fetch_request(rq)) != NULL)
155 __blk_end_request_all(req, -ENODEV);
156 else
157 wake_up_process(dev->thread);
158}
118 159
119static int blktrans_open(struct block_device *bdev, fmode_t mode) 160static int blktrans_open(struct block_device *bdev, fmode_t mode)
120{ 161{
121 struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; 162 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
122 struct mtd_blktrans_ops *tr = dev->tr; 163 int ret;
123 int ret = -ENODEV; 164
124 165 if (!dev)
125 if (!get_mtd_device(NULL, dev->mtd->index)) 166 return -ERESTARTSYS;
126 goto out; 167
127 168 mutex_lock(&dev->lock);
128 if (!try_module_get(tr->owner)) 169
129 goto out_tr; 170 if (!dev->mtd) {
130 171 ret = -ENXIO;
131 /* FIXME: Locking. A hot pluggable device can go away 172 goto unlock;
132 (del_mtd_device can be called for it) without its module
133 being unloaded. */
134 dev->mtd->usecount++;
135
136 ret = 0;
137 if (tr->open && (ret = tr->open(dev))) {
138 dev->mtd->usecount--;
139 put_mtd_device(dev->mtd);
140 out_tr:
141 module_put(tr->owner);
142 } 173 }
143 out: 174
175 ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
176
177 /* Take another reference on the device so it won't go away till
178 last release */
179 if (!ret)
180 kref_get(&dev->ref);
181unlock:
182 mutex_unlock(&dev->lock);
183 blktrans_dev_put(dev);
144 return ret; 184 return ret;
145} 185}
146 186
147static int blktrans_release(struct gendisk *disk, fmode_t mode) 187static int blktrans_release(struct gendisk *disk, fmode_t mode)
148{ 188{
149 struct mtd_blktrans_dev *dev = disk->private_data; 189 struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
150 struct mtd_blktrans_ops *tr = dev->tr; 190 int ret = -ENXIO;
151 int ret = 0;
152 191
153 if (tr->release) 192 if (!dev)
154 ret = tr->release(dev); 193 return ret;
155 194
156 if (!ret) { 195 mutex_lock(&dev->lock);
157 dev->mtd->usecount--; 196
158 put_mtd_device(dev->mtd); 197 /* Release one reference, we sure its not the last one here*/
159 module_put(tr->owner); 198 kref_put(&dev->ref, blktrans_dev_release);
160 }
161 199
200 if (!dev->mtd)
201 goto unlock;
202
203 ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
204unlock:
205 mutex_unlock(&dev->lock);
206 blktrans_dev_put(dev);
162 return ret; 207 return ret;
163} 208}
164 209
165static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) 210static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
166{ 211{
167 struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; 212 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
213 int ret = -ENXIO;
214
215 if (!dev)
216 return ret;
217
218 mutex_lock(&dev->lock);
168 219
169 if (dev->tr->getgeo) 220 if (!dev->mtd)
170 return dev->tr->getgeo(dev, geo); 221 goto unlock;
171 return -ENOTTY; 222
223 ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
224unlock:
225 mutex_unlock(&dev->lock);
226 blktrans_dev_put(dev);
227 return ret;
172} 228}
173 229
174static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, 230static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
175 unsigned int cmd, unsigned long arg) 231 unsigned int cmd, unsigned long arg)
176{ 232{
177 struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; 233 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
178 struct mtd_blktrans_ops *tr = dev->tr; 234 int ret = -ENXIO;
235
236 if (!dev)
237 return ret;
238
239 mutex_lock(&dev->lock);
240
241 if (!dev->mtd)
242 goto unlock;
179 243
180 switch (cmd) { 244 switch (cmd) {
181 case BLKFLSBUF: 245 case BLKFLSBUF:
182 if (tr->flush) 246 ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
183 return tr->flush(dev);
184 /* The core code did the work, we had nothing to do. */
185 return 0;
186 default: 247 default:
187 return -ENOTTY; 248 ret = -ENOTTY;
188 } 249 }
250unlock:
251 mutex_unlock(&dev->lock);
252 blktrans_dev_put(dev);
253 return ret;
189} 254}
190 255
191static const struct block_device_operations mtd_blktrans_ops = { 256static const struct block_device_operations mtd_blktrans_ops = {
@@ -209,6 +274,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
209 BUG(); 274 BUG();
210 } 275 }
211 276
277 mutex_lock(&blktrans_ref_mutex);
212 list_for_each_entry(d, &tr->devs, list) { 278 list_for_each_entry(d, &tr->devs, list) {
213 if (new->devnum == -1) { 279 if (new->devnum == -1) {
214 /* Use first free number */ 280 /* Use first free number */
@@ -220,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
220 } 286 }
221 } else if (d->devnum == new->devnum) { 287 } else if (d->devnum == new->devnum) {
222 /* Required number taken */ 288 /* Required number taken */
289 mutex_unlock(&blktrans_ref_mutex);
223 return -EBUSY; 290 return -EBUSY;
224 } else if (d->devnum > new->devnum) { 291 } else if (d->devnum > new->devnum) {
225 /* Required number was free */ 292 /* Required number was free */
@@ -237,16 +304,20 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
237 * minor numbers and that the disk naming code below can cope 304 * minor numbers and that the disk naming code below can cope
238 * with this number. */ 305 * with this number. */
239 if (new->devnum > (MINORMASK >> tr->part_bits) || 306 if (new->devnum > (MINORMASK >> tr->part_bits) ||
240 (tr->part_bits && new->devnum >= 27 * 26)) 307 (tr->part_bits && new->devnum >= 27 * 26)) {
308 mutex_unlock(&blktrans_ref_mutex);
241 goto error1; 309 goto error1;
310 }
242 311
243 list_add_tail(&new->list, &tr->devs); 312 list_add_tail(&new->list, &tr->devs);
244 added: 313 added:
314 mutex_unlock(&blktrans_ref_mutex);
315
245 mutex_init(&new->lock); 316 mutex_init(&new->lock);
317 kref_init(&new->ref);
246 if (!tr->writesect) 318 if (!tr->writesect)
247 new->readonly = 1; 319 new->readonly = 1;
248 320
249
250 /* Create gendisk */ 321 /* Create gendisk */
251 ret = -ENOMEM; 322 ret = -ENOMEM;
252 gd = alloc_disk(1 << tr->part_bits); 323 gd = alloc_disk(1 << tr->part_bits);
@@ -275,7 +346,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
275 346
276 set_capacity(gd, (new->size * tr->blksize) >> 9); 347 set_capacity(gd, (new->size * tr->blksize) >> 9);
277 348
278
279 /* Create the request queue */ 349 /* Create the request queue */
280 spin_lock_init(&new->queue_lock); 350 spin_lock_init(&new->queue_lock);
281 new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); 351 new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);
@@ -292,6 +362,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
292 362
293 gd->queue = new->rq; 363 gd->queue = new->rq;
294 364
365 __get_mtd_device(new->mtd);
366 __module_get(tr->owner);
367
295 /* Create processing thread */ 368 /* Create processing thread */
296 /* TODO: workqueue ? */ 369 /* TODO: workqueue ? */
297 new->thread = kthread_run(mtd_blktrans_thread, new, 370 new->thread = kthread_run(mtd_blktrans_thread, new,
@@ -308,6 +381,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
308 add_disk(gd); 381 add_disk(gd);
309 return 0; 382 return 0;
310error4: 383error4:
384 module_put(tr->owner);
385 __put_mtd_device(new->mtd);
311 blk_cleanup_queue(new->rq); 386 blk_cleanup_queue(new->rq);
312error3: 387error3:
313 put_disk(new->disk); 388 put_disk(new->disk);
@@ -320,20 +395,41 @@ error1:
320 395
321int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) 396int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
322{ 397{
398 unsigned long flags;
399
323 if (mutex_trylock(&mtd_table_mutex)) { 400 if (mutex_trylock(&mtd_table_mutex)) {
324 mutex_unlock(&mtd_table_mutex); 401 mutex_unlock(&mtd_table_mutex);
325 BUG(); 402 BUG();
326 } 403 }
327 404
328 list_del(&old->list); 405 /* Stop new requests to arrive */
329
330 /* stop new requests to arrive */
331 del_gendisk(old->disk); 406 del_gendisk(old->disk);
332 407
333 /* Stop the thread */ 408 /* Stop the thread */
334 kthread_stop(old->thread); 409 kthread_stop(old->thread);
335 410
411 /* Kill current requests */
412 spin_lock_irqsave(&old->queue_lock, flags);
413 old->rq->queuedata = NULL;
414 blk_start_queue(old->rq);
415 spin_unlock_irqrestore(&old->queue_lock, flags);
336 blk_cleanup_queue(old->rq); 416 blk_cleanup_queue(old->rq);
417
418 /* Ask trans driver for release to the mtd device */
419 mutex_lock(&old->lock);
420 if (old->open && old->tr->release) {
421 old->tr->release(old);
422 old->open = 0;
423 }
424
425 __put_mtd_device(old->mtd);
426 module_put(old->tr->owner);
427
428 /* At that point, we don't touch the mtd anymore */
429 old->mtd = NULL;
430
431 mutex_unlock(&old->lock);
432 blktrans_dev_put(old);
337 return 0; 433 return 0;
338} 434}
339 435
@@ -396,7 +492,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
396 tr->add_mtd(tr, mtd); 492 tr->add_mtd(tr, mtd);
397 493
398 mutex_unlock(&mtd_table_mutex); 494 mutex_unlock(&mtd_table_mutex);
399
400 return 0; 495 return 0;
401} 496}
402 497
@@ -406,7 +501,6 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
406 501
407 mutex_lock(&mtd_table_mutex); 502 mutex_lock(&mtd_table_mutex);
408 503
409
410 /* Remove it from the list of active majors */ 504 /* Remove it from the list of active majors */
411 list_del(&tr->list); 505 list_del(&tr->list);
412 506
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index 69f6bf2e0a8c..8e5da1e46076 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -354,9 +354,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
354static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) 354static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
355{ 355{
356 struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); 356 struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
357
358 del_mtd_blktrans_dev(dev); 357 del_mtd_blktrans_dev(dev);
359 kfree(mtdblk);
360} 358}
361 359
362static struct mtd_blktrans_ops mtdblock_tr = { 360static struct mtd_blktrans_ops mtdblock_tr = {
diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c
index 852165f8b1c3..54ff2880cf65 100644
--- a/drivers/mtd/mtdblock_ro.c
+++ b/drivers/mtd/mtdblock_ro.c
@@ -49,7 +49,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
49static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) 49static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
50{ 50{
51 del_mtd_blktrans_dev(dev); 51 del_mtd_blktrans_dev(dev);
52 kfree(dev);
53} 52}
54 53
55static struct mtd_blktrans_ops mtdblock_tr = { 54static struct mtd_blktrans_ops mtdblock_tr = {
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index 1002e1882996..a4578bf903aa 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -126,7 +126,6 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
126 del_mtd_blktrans_dev(dev); 126 del_mtd_blktrans_dev(dev);
127 kfree(nftl->ReplUnitTable); 127 kfree(nftl->ReplUnitTable);
128 kfree(nftl->EUNtable); 128 kfree(nftl->EUNtable);
129 kfree(nftl);
130} 129}
131 130
132/* 131/*
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
index d2aa9c46530f..63b83c0d9a13 100644
--- a/drivers/mtd/rfd_ftl.c
+++ b/drivers/mtd/rfd_ftl.c
@@ -817,7 +817,6 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
817 vfree(part->sector_map); 817 vfree(part->sector_map);
818 kfree(part->header_cache); 818 kfree(part->header_cache);
819 kfree(part->blocks); 819 kfree(part->blocks);
820 kfree(part);
821} 820}
822 821
823static struct mtd_blktrans_ops rfd_ftl_tr = { 822static struct mtd_blktrans_ops rfd_ftl_tr = {
diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c
index 3f67e00d98e0..81c4ecdc11f5 100644
--- a/drivers/mtd/ssfdc.c
+++ b/drivers/mtd/ssfdc.c
@@ -375,7 +375,6 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
375 375
376 del_mtd_blktrans_dev(dev); 376 del_mtd_blktrans_dev(dev);
377 kfree(ssfdc->logic_block_map); 377 kfree(ssfdc->logic_block_map);
378 kfree(ssfdc);
379} 378}
380 379
381static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, 380static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h
index a4b392868b54..d89b8fbba4c9 100644
--- a/include/linux/mtd/blktrans.h
+++ b/include/linux/mtd/blktrans.h
@@ -9,6 +9,7 @@
9#define __MTD_TRANS_H__ 9#define __MTD_TRANS_H__
10 10
11#include <linux/mutex.h> 11#include <linux/mutex.h>
12#include <linux/kref.h>
12 13
13struct hd_geometry; 14struct hd_geometry;
14struct mtd_info; 15struct mtd_info;
@@ -24,6 +25,8 @@ struct mtd_blktrans_dev {
24 int devnum; 25 int devnum;
25 unsigned long size; 26 unsigned long size;
26 int readonly; 27 int readonly;
28 int open;
29 struct kref ref;
27 struct gendisk *disk; 30 struct gendisk *disk;
28 struct task_struct *thread; 31 struct task_struct *thread;
29 struct request_queue *rq; 32 struct request_queue *rq;