aboutsummaryrefslogtreecommitdiffstats
path: root/fs/block_dev.c
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-09-29 04:58:56 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-29 12:18:04 -0400
commit4d7dd8fd9557840162b724a8ac1366dd78a12dff (patch)
treeba67748bb0951a40a8921b5f2c05a4f961646415 /fs/block_dev.c
parentd8c7649e99e4b081b624aefe1e77caa30b53cb18 (diff)
[PATCH] blockdev.c: check driver layer errors
Check driver layer errors. Fix from: "Jun'ichi Nomura" <j-nomura@ce.jp.nec.com> In blockdevc-check-errors.patch, add_bd_holder() is modified to return error values when some of its operation failed. Among them, it returns -EEXIST when a given bd_holder object already exists in the list. However, in this case, the function completed its work successfully and need no action by its caller other than freeing unused bd_holder object. So I think it's better to return success after freeing by itself. Otherwise, bd_claim-ing with same claim pointer will fail. Typically, lvresize will fails with following message: device-mapper: reload ioctl failed: Invalid argument and you'll see messages like below in kernel log: device-mapper: table: 254:13: linear: dm-linear: Device lookup failed device-mapper: ioctl: error adding target to table Similarly, it should not add bd_holder to the list if either one of symlinking fails. I don't have a test case for this to happen but it should cause dereference of freed pointer. If a matching bd_holder is found in bd_holder_list, add_bd_holder() completes its job by just incrementing the reference count. In this case, it should be considered as success but it used to return 'fail' to let the caller free temporary bd_holder. Fixed it to return success and free given object by itself. Also, if either one of symlinking fails, the bd_holder should not be added to the list so that it can be discarded later. Otherwise, the caller will free bd_holder which is in the list. Signed-off-by: Jun'ichi Nomura <j-nomura@ce.jp.nec.com> Cc: "Randy.Dunlap" <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/block_dev.c')
-rw-r--r--fs/block_dev.c34
1 files changed, 22 insertions, 12 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 045f98854f14..8cc144ffc382 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -543,11 +543,11 @@ static struct kobject *bdev_get_holder(struct block_device *bdev)
543 return kobject_get(bdev->bd_disk->holder_dir); 543 return kobject_get(bdev->bd_disk->holder_dir);
544} 544}
545 545
546static void add_symlink(struct kobject *from, struct kobject *to) 546static int add_symlink(struct kobject *from, struct kobject *to)
547{ 547{
548 if (!from || !to) 548 if (!from || !to)
549 return; 549 return 0;
550 sysfs_create_link(from, to, kobject_name(to)); 550 return sysfs_create_link(from, to, kobject_name(to));
551} 551}
552 552
553static void del_symlink(struct kobject *from, struct kobject *to) 553static void del_symlink(struct kobject *from, struct kobject *to)
@@ -648,30 +648,38 @@ static void free_bd_holder(struct bd_holder *bo)
648 * If there is no matching entry with @bo in @bdev->bd_holder_list, 648 * If there is no matching entry with @bo in @bdev->bd_holder_list,
649 * add @bo to the list, create symlinks. 649 * add @bo to the list, create symlinks.
650 * 650 *
651 * Returns 1 if @bo was added to the list. 651 * Returns 0 if symlinks are created or already there.
652 * Returns 0 if @bo wasn't used by any reason and should be freed. 652 * Returns -ve if something fails and @bo can be freed.
653 */ 653 */
654static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) 654static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo)
655{ 655{
656 struct bd_holder *tmp; 656 struct bd_holder *tmp;
657 int ret;
657 658
658 if (!bo) 659 if (!bo)
659 return 0; 660 return -EINVAL;
660 661
661 list_for_each_entry(tmp, &bdev->bd_holder_list, list) { 662 list_for_each_entry(tmp, &bdev->bd_holder_list, list) {
662 if (tmp->sdir == bo->sdir) { 663 if (tmp->sdir == bo->sdir) {
663 tmp->count++; 664 tmp->count++;
665 /* We've already done what we need to do here. */
666 free_bd_holder(bo);
664 return 0; 667 return 0;
665 } 668 }
666 } 669 }
667 670
668 if (!bd_holder_grab_dirs(bdev, bo)) 671 if (!bd_holder_grab_dirs(bdev, bo))
669 return 0; 672 return -EBUSY;
670 673
671 add_symlink(bo->sdir, bo->sdev); 674 ret = add_symlink(bo->sdir, bo->sdev);
672 add_symlink(bo->hdir, bo->hdev); 675 if (ret == 0) {
673 list_add_tail(&bo->list, &bdev->bd_holder_list); 676 ret = add_symlink(bo->hdir, bo->hdev);
674 return 1; 677 if (ret)
678 del_symlink(bo->sdir, bo->sdev);
679 }
680 if (ret == 0)
681 list_add_tail(&bo->list, &bdev->bd_holder_list);
682 return ret;
675} 683}
676 684
677/** 685/**
@@ -741,7 +749,9 @@ static int bd_claim_by_kobject(struct block_device *bdev, void *holder,
741 749
742 mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION); 750 mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION);
743 res = bd_claim(bdev, holder); 751 res = bd_claim(bdev, holder);
744 if (res || !add_bd_holder(bdev, bo)) 752 if (res == 0)
753 res = add_bd_holder(bdev, bo);
754 if (res)
745 free_bd_holder(bo); 755 free_bd_holder(bo);
746 mutex_unlock(&bdev->bd_mutex); 756 mutex_unlock(&bdev->bd_mutex);
747 757