Commit 6d01bb59 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Li Nan
Browse files

md: fix error handling in md_alloc

mainline inclusion
from mainline-v6.0-rc1
commit c57094a6
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I81XCK

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c57094a6e1ed5dd2d6401f79b8e6da34dd28f959



--------------------------------

Error handling in md_alloc is a mess.  Untangle it to just free the mddev
directly before add_disk is called and thus the gendisk is globally
visible.  After that clear the hold flag and let the mddev_put take care
of cleaning up the mddev through the usual mechanisms.

Fixes: 5e55e2f5 ("[PATCH] md: convert compile time warnings into runtime warnings")
Fixes: 9be68dd7 ("md: add error handling support for add_disk()")
Fixes: 7ad10691 ("md: properly unwind when failing to add the kobject in md_alloc")
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarLogan Gunthorpe <logang@deltatee.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarSong Liu <song@kernel.org>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>

Conflict:
	drivers/md/md.c
	Commit 8b9ab626 ("block: remove blk_cleanup_disk") change
	blk_cleanup_disk() to put_disk(). Keep using blk_cleanup_disk()
	here. And add_disk_safe().

Signed-off-by: default avatarLi Nan <linan122@huawei.com>
parent da72f3d2
Loading
Loading
Loading
Loading
+32 −13
Original line number Original line Diff line number Diff line
@@ -800,6 +800,15 @@ static struct mddev *mddev_alloc(dev_t unit)
	return ERR_PTR(error);
	return ERR_PTR(error);
}
}


static void mddev_free(struct mddev *mddev)
{
	spin_lock(&all_mddevs_lock);
	list_del(&mddev->all_mddevs);
	spin_unlock(&all_mddevs_lock);

	kfree(mddev);
}

static const struct attribute_group md_redundancy_group;
static const struct attribute_group md_redundancy_group;


void mddev_unlock(struct mddev *mddev)
void mddev_unlock(struct mddev *mddev)
@@ -5748,8 +5757,8 @@ static int md_alloc(dev_t dev, char *name)
	mutex_lock(&disks_mutex);
	mutex_lock(&disks_mutex);
	mddev = mddev_alloc(dev);
	mddev = mddev_alloc(dev);
	if (IS_ERR(mddev)) {
	if (IS_ERR(mddev)) {
		mutex_unlock(&disks_mutex);
		error = PTR_ERR(mddev);
		return PTR_ERR(mddev);
		goto out_unlock;
	}
	}


	partitioned = (MAJOR(mddev->unit) != MD_MAJOR);
	partitioned = (MAJOR(mddev->unit) != MD_MAJOR);
@@ -5767,7 +5776,7 @@ static int md_alloc(dev_t dev, char *name)
			    strcmp(mddev2->gendisk->disk_name, name) == 0) {
			    strcmp(mddev2->gendisk->disk_name, name) == 0) {
				spin_unlock(&all_mddevs_lock);
				spin_unlock(&all_mddevs_lock);
				error = -EEXIST;
				error = -EEXIST;
				goto out_unlock_disks_mutex;
				goto out_free_mddev;
			}
			}
		spin_unlock(&all_mddevs_lock);
		spin_unlock(&all_mddevs_lock);
	}
	}
@@ -5780,7 +5789,7 @@ static int md_alloc(dev_t dev, char *name)
	error = -ENOMEM;
	error = -ENOMEM;
	disk = blk_alloc_disk(NUMA_NO_NODE);
	disk = blk_alloc_disk(NUMA_NO_NODE);
	if (!disk)
	if (!disk)
		goto out_unlock_disks_mutex;
		goto out_free_mddev;


	disk->major = MAJOR(mddev->unit);
	disk->major = MAJOR(mddev->unit);
	disk->first_minor = unit << shift;
	disk->first_minor = unit << shift;
@@ -5806,26 +5815,36 @@ static int md_alloc(dev_t dev, char *name)
	mddev->gendisk = disk;
	mddev->gendisk = disk;
	error = add_disk_safe(disk);
	error = add_disk_safe(disk);
	if (error)
	if (error)
		goto out_cleanup_disk;
		goto out_put_disk;


	kobject_init(&mddev->kobj, &md_ktype);
	kobject_init(&mddev->kobj, &md_ktype);
	error = kobject_add(&mddev->kobj, &disk_to_dev(disk)->kobj, "%s", "md");
	error = kobject_add(&mddev->kobj, &disk_to_dev(disk)->kobj, "%s", "md");
	if (error)
	if (error) {
		goto out_del_gendisk;
		/*
		 * The disk is already live at this point.  Clear the hold flag
		 * and let mddev_put take care of the deletion, as it isn't any
		 * different from a normal close on last release now.
		 */
		mddev->hold_active = 0;
		goto done;
	}


	kobject_uevent(&mddev->kobj, KOBJ_ADD);
	kobject_uevent(&mddev->kobj, KOBJ_ADD);
	mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd, "array_state");
	mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd, "array_state");
	mddev->sysfs_level = sysfs_get_dirent_safe(mddev->kobj.sd, "level");
	mddev->sysfs_level = sysfs_get_dirent_safe(mddev->kobj.sd, "level");
	goto out_unlock_disks_mutex;


out_del_gendisk:
done:
	del_gendisk(disk);
out_cleanup_disk:
	blk_cleanup_disk(disk);
out_unlock_disks_mutex:
	mutex_unlock(&disks_mutex);
	mutex_unlock(&disks_mutex);
	mddev_put(mddev);
	mddev_put(mddev);
	return error;
	return error;

out_put_disk:
	blk_cleanup_disk(disk);
out_free_mddev:
	mddev_free(mddev);
out_unlock:
	mutex_unlock(&disks_mutex);
	return error;
}
}


static struct kobject *md_probe(dev_t dev, int *part, void *data)
static struct kobject *md_probe(dev_t dev, int *part, void *data)