diff options
Diffstat (limited to 'drivers/block/loop.c')
-rw-r--r-- | drivers/block/loop.c | 39 |
1 files changed, 36 insertions, 3 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index fb06ed659212..edbaac6c0573 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -624,20 +624,38 @@ static int loop_switch(struct loop_device *lo, struct file *file) | |||
624 | } | 624 | } |
625 | 625 | ||
626 | /* | 626 | /* |
627 | * Helper to flush the IOs in loop, but keeping loop thread running | ||
628 | */ | ||
629 | static int loop_flush(struct loop_device *lo) | ||
630 | { | ||
631 | /* loop not yet configured, no running thread, nothing to flush */ | ||
632 | if (!lo->lo_thread) | ||
633 | return 0; | ||
634 | |||
635 | return loop_switch(lo, NULL); | ||
636 | } | ||
637 | |||
638 | /* | ||
627 | * Do the actual switch; called from the BIO completion routine | 639 | * Do the actual switch; called from the BIO completion routine |
628 | */ | 640 | */ |
629 | static void do_loop_switch(struct loop_device *lo, struct switch_request *p) | 641 | static void do_loop_switch(struct loop_device *lo, struct switch_request *p) |
630 | { | 642 | { |
631 | struct file *file = p->file; | 643 | struct file *file = p->file; |
632 | struct file *old_file = lo->lo_backing_file; | 644 | struct file *old_file = lo->lo_backing_file; |
633 | struct address_space *mapping = file->f_mapping; | 645 | struct address_space *mapping; |
646 | |||
647 | /* if no new file, only flush of queued bios requested */ | ||
648 | if (!file) | ||
649 | goto out; | ||
634 | 650 | ||
651 | mapping = file->f_mapping; | ||
635 | mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); | 652 | mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); |
636 | lo->lo_backing_file = file; | 653 | lo->lo_backing_file = file; |
637 | lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ? | 654 | lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ? |
638 | mapping->host->i_bdev->bd_block_size : PAGE_SIZE; | 655 | mapping->host->i_bdev->bd_block_size : PAGE_SIZE; |
639 | lo->old_gfp_mask = mapping_gfp_mask(mapping); | 656 | lo->old_gfp_mask = mapping_gfp_mask(mapping); |
640 | mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); | 657 | mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); |
658 | out: | ||
641 | complete(&p->wait); | 659 | complete(&p->wait); |
642 | } | 660 | } |
643 | 661 | ||
@@ -901,6 +919,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) | |||
901 | 919 | ||
902 | kthread_stop(lo->lo_thread); | 920 | kthread_stop(lo->lo_thread); |
903 | 921 | ||
922 | lo->lo_queue->unplug_fn = NULL; | ||
904 | lo->lo_backing_file = NULL; | 923 | lo->lo_backing_file = NULL; |
905 | 924 | ||
906 | loop_release_xfer(lo); | 925 | loop_release_xfer(lo); |
@@ -1345,11 +1364,25 @@ static int lo_release(struct gendisk *disk, fmode_t mode) | |||
1345 | struct loop_device *lo = disk->private_data; | 1364 | struct loop_device *lo = disk->private_data; |
1346 | 1365 | ||
1347 | mutex_lock(&lo->lo_ctl_mutex); | 1366 | mutex_lock(&lo->lo_ctl_mutex); |
1348 | --lo->lo_refcnt; | ||
1349 | 1367 | ||
1350 | if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) && !lo->lo_refcnt) | 1368 | if (--lo->lo_refcnt) |
1369 | goto out; | ||
1370 | |||
1371 | if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) { | ||
1372 | /* | ||
1373 | * In autoclear mode, stop the loop thread | ||
1374 | * and remove configuration after last close. | ||
1375 | */ | ||
1351 | loop_clr_fd(lo, NULL); | 1376 | loop_clr_fd(lo, NULL); |
1377 | } else { | ||
1378 | /* | ||
1379 | * Otherwise keep thread (if running) and config, | ||
1380 | * but flush possible ongoing bios in thread. | ||
1381 | */ | ||
1382 | loop_flush(lo); | ||
1383 | } | ||
1352 | 1384 | ||
1385 | out: | ||
1353 | mutex_unlock(&lo->lo_ctl_mutex); | 1386 | mutex_unlock(&lo->lo_ctl_mutex); |
1354 | 1387 | ||
1355 | return 0; | 1388 | return 0; |