diff options
Diffstat (limited to 'drivers/md/md.c')
| -rw-r--r-- | drivers/md/md.c | 206 | 
1 files changed, 122 insertions, 84 deletions
| diff --git a/drivers/md/md.c b/drivers/md/md.c index 21da0c48f6c2..49f897fbb89b 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -734,78 +734,94 @@ void mddev_init(struct mddev *mddev)  }  EXPORT_SYMBOL_GPL(mddev_init); +static struct mddev *mddev_find_locked(dev_t unit) +{ +	struct mddev *mddev; + +	list_for_each_entry(mddev, &all_mddevs, all_mddevs) +		if (mddev->unit == unit) +			return mddev; + +	return NULL; +} + +/* find an unused unit number */ +static dev_t mddev_alloc_unit(void) +{ +	static int next_minor = 512; +	int start = next_minor; +	bool is_free = 0; +	dev_t dev = 0; + +	while (!is_free) { +		dev = MKDEV(MD_MAJOR, next_minor); +		next_minor++; +		if (next_minor > MINORMASK) +			next_minor = 0; +		if (next_minor == start) +			return 0;		/* Oh dear, all in use. */ +		is_free = !mddev_find_locked(dev); +	} + +	return dev; +} +  static struct mddev *mddev_find(dev_t unit)  { -	struct mddev *mddev, *new = NULL; +	struct mddev *mddev; -	if (unit && MAJOR(unit) != MD_MAJOR) -		unit &= ~((1<<MdpMinorShift)-1); +	if (MAJOR(unit) != MD_MAJOR) +		unit &= ~((1 << MdpMinorShift) - 1); - retry:  	spin_lock(&all_mddevs_lock); +	mddev = mddev_find_locked(unit); +	if (mddev) +		mddev_get(mddev); +	spin_unlock(&all_mddevs_lock); -	if (unit) { -		list_for_each_entry(mddev, &all_mddevs, all_mddevs) -			if (mddev->unit == unit) { -				mddev_get(mddev); -				spin_unlock(&all_mddevs_lock); -				kfree(new); -				return mddev; -			} +	return mddev; +} -		if (new) { -			list_add(&new->all_mddevs, &all_mddevs); -			spin_unlock(&all_mddevs_lock); -			new->hold_active = UNTIL_IOCTL; -			return new; -		} -	} else if (new) { -		/* find an unused unit number */ -		static int next_minor = 512; -		int start = next_minor; -		int is_free = 0; -		int dev = 0; -		while (!is_free) { -			dev = MKDEV(MD_MAJOR, next_minor); -			next_minor++; -			if (next_minor > MINORMASK) -				next_minor = 0; -			if (next_minor == start) { -				/* Oh dear, all in use. */ -				spin_unlock(&all_mddevs_lock); -				kfree(new); -				return NULL; -			} +static struct mddev *mddev_alloc(dev_t unit) +{ +	struct mddev *new; +	int error; -			is_free = 1; -			list_for_each_entry(mddev, &all_mddevs, all_mddevs) -				if (mddev->unit == dev) { -					is_free = 0; -					break; -				} -		} -		new->unit = dev; -		new->md_minor = MINOR(dev); -		new->hold_active = UNTIL_STOP; -		list_add(&new->all_mddevs, &all_mddevs); -		spin_unlock(&all_mddevs_lock); -		return new; -	} -	spin_unlock(&all_mddevs_lock); +	if (unit && MAJOR(unit) != MD_MAJOR) +		unit &= ~((1 << MdpMinorShift) - 1);  	new = kzalloc(sizeof(*new), GFP_KERNEL);  	if (!new) -		return NULL; - -	new->unit = unit; -	if (MAJOR(unit) == MD_MAJOR) -		new->md_minor = MINOR(unit); -	else -		new->md_minor = MINOR(unit) >> MdpMinorShift; - +		return ERR_PTR(-ENOMEM);  	mddev_init(new); -	goto retry; +	spin_lock(&all_mddevs_lock); +	if (unit) { +		error = -EEXIST; +		if (mddev_find_locked(unit)) +			goto out_free_new; +		new->unit = unit; +		if (MAJOR(unit) == MD_MAJOR) +			new->md_minor = MINOR(unit); +		else +			new->md_minor = MINOR(unit) >> MdpMinorShift; +		new->hold_active = UNTIL_IOCTL; +	} else { +		error = -ENODEV; +		new->unit = mddev_alloc_unit(); +		if (!new->unit) +			goto out_free_new; +		new->md_minor = MINOR(new->unit); +		new->hold_active = UNTIL_STOP; +	} + +	list_add(&new->all_mddevs, &all_mddevs); +	spin_unlock(&all_mddevs_lock); +	return new; +out_free_new: +	spin_unlock(&all_mddevs_lock); +	kfree(new); +	return ERR_PTR(error);  }  static struct attribute_group md_redundancy_group; @@ -5644,29 +5660,29 @@ static int md_alloc(dev_t dev, char *name)  	 * writing to /sys/module/md_mod/parameters/new_array.  	 */  	static DEFINE_MUTEX(disks_mutex); -	struct mddev *mddev = mddev_find(dev); +	struct mddev *mddev;  	struct gendisk *disk;  	int partitioned;  	int shift;  	int unit; -	int error; +	int error ; -	if (!mddev) -		return -ENODEV; - -	partitioned = (MAJOR(mddev->unit) != MD_MAJOR); -	shift = partitioned ? MdpMinorShift : 0; -	unit = MINOR(mddev->unit) >> shift; - -	/* wait for any previous instance of this device to be -	 * completely removed (mddev_delayed_delete). +	/* +	 * Wait for any previous instance of this device to be completely +	 * removed (mddev_delayed_delete).  	 */  	flush_workqueue(md_misc_wq);  	mutex_lock(&disks_mutex); -	error = -EEXIST; -	if (mddev->gendisk) -		goto abort; +	mddev = mddev_alloc(dev); +	if (IS_ERR(mddev)) { +		mutex_unlock(&disks_mutex); +		return PTR_ERR(mddev); +	} + +	partitioned = (MAJOR(mddev->unit) != MD_MAJOR); +	shift = partitioned ? MdpMinorShift : 0; +	unit = MINOR(mddev->unit) >> shift;  	if (name && !dev) {  		/* Need to ensure that 'name' is not a duplicate. @@ -5678,6 +5694,7 @@ static int md_alloc(dev_t dev, char *name)  			if (mddev2->gendisk &&  			    strcmp(mddev2->gendisk->disk_name, name) == 0) {  				spin_unlock(&all_mddevs_lock); +				error = -EEXIST;  				goto abort;  			}  		spin_unlock(&all_mddevs_lock); @@ -6524,11 +6541,9 @@ static void autorun_devices(int part)  		md_probe(dev);  		mddev = mddev_find(dev); -		if (!mddev || !mddev->gendisk) { -			if (mddev) -				mddev_put(mddev); +		if (!mddev)  			break; -		} +  		if (mddev_lock(mddev))  			pr_warn("md: %s locked, cannot run\n", mdname(mddev));  		else if (mddev->raid_disks || mddev->major_version @@ -7821,8 +7836,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)  		/* Wait until bdev->bd_disk is definitely gone */  		if (work_pending(&mddev->del_work))  			flush_workqueue(md_misc_wq); -		/* Then retry the open from the top */ -		return -ERESTARTSYS; +		return -EBUSY;  	}  	BUG_ON(mddev != bdev->bd_disk->private_data); @@ -8153,7 +8167,11 @@ static void *md_seq_start(struct seq_file *seq, loff_t *pos)  	loff_t l = *pos;  	struct mddev *mddev; -	if (l >= 0x10000) +	if (l == 0x10000) { +		++*pos; +		return (void *)2; +	} +	if (l > 0x10000)  		return NULL;  	if (!l--)  		/* header */ @@ -8575,6 +8593,26 @@ void md_write_end(struct mddev *mddev)  EXPORT_SYMBOL(md_write_end); +/* This is used by raid0 and raid10 */ +void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev, +			struct bio *bio, sector_t start, sector_t size) +{ +	struct bio *discard_bio = NULL; + +	if (__blkdev_issue_discard(rdev->bdev, start, size, GFP_NOIO, 0, +			&discard_bio) || !discard_bio) +		return; + +	bio_chain(discard_bio, bio); +	bio_clone_blkg_association(discard_bio, bio); +	if (mddev->gendisk) +		trace_block_bio_remap(discard_bio, +				disk_devt(mddev->gendisk), +				bio->bi_iter.bi_sector); +	submit_bio_noacct(discard_bio); +} +EXPORT_SYMBOL_GPL(md_submit_discard_bio); +  /* md_allow_write(mddev)   * Calling this ensures that the array is marked 'active' so that writes   * may proceed without blocking.  It is important to call this before @@ -9251,11 +9289,11 @@ void md_check_recovery(struct mddev *mddev)  		}  		if (mddev_is_clustered(mddev)) { -			struct md_rdev *rdev; +			struct md_rdev *rdev, *tmp;  			/* kick the device if another node issued a  			 * remove disk.  			 */ -			rdev_for_each(rdev, mddev) { +			rdev_for_each_safe(rdev, tmp, mddev) {  				if (test_and_clear_bit(ClusterRemove, &rdev->flags) &&  						rdev->raid_disk < 0)  					md_kick_rdev_from_array(rdev); @@ -9569,7 +9607,7 @@ err_wq:  static void check_sb_changes(struct mddev *mddev, struct md_rdev *rdev)  {  	struct mdp_superblock_1 *sb = page_address(rdev->sb_page); -	struct md_rdev *rdev2; +	struct md_rdev *rdev2, *tmp;  	int role, ret;  	char b[BDEVNAME_SIZE]; @@ -9586,7 +9624,7 @@ static void check_sb_changes(struct mddev *mddev, struct md_rdev *rdev)  	}  	/* Check for change of roles in the active devices */ -	rdev_for_each(rdev2, mddev) { +	rdev_for_each_safe(rdev2, tmp, mddev) {  		if (test_bit(Faulty, &rdev2->flags))  			continue; | 
