diff options
Diffstat (limited to 'drivers/md/md.c')
| -rw-r--r-- | drivers/md/md.c | 96 | 
1 files changed, 80 insertions, 16 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 24638ccedce4..1be7abeb24fd 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -376,6 +376,11 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)  	struct mddev *mddev = q->queuedata;  	unsigned int sectors; +	if (unlikely(test_bit(MD_BROKEN, &mddev->flags)) && (rw == WRITE)) { +		bio_io_error(bio); +		return BLK_QC_T_NONE; +	} +  	blk_queue_split(q, &bio);  	if (mddev == NULL || mddev->pers == NULL) { @@ -1232,6 +1237,8 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)  			mddev->new_layout = mddev->layout;  			mddev->new_chunk_sectors = mddev->chunk_sectors;  		} +		if (mddev->level == 0) +			mddev->layout = -1;  		if (sb->state & (1<<MD_SB_CLEAN))  			mddev->recovery_cp = MaxSector; @@ -1647,6 +1654,10 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_  		rdev->ppl.sector = rdev->sb_start + rdev->ppl.offset;  	} +	if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RAID0_LAYOUT) && +	    sb->level != 0) +		return -EINVAL; +  	if (!refdev) {  		ret = 1;  	} else { @@ -1757,6 +1768,10 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)  			mddev->new_chunk_sectors = mddev->chunk_sectors;  		} +		if (mddev->level == 0 && +		    !(le32_to_cpu(sb->feature_map) & MD_FEATURE_RAID0_LAYOUT)) +			mddev->layout = -1; +  		if (le32_to_cpu(sb->feature_map) & MD_FEATURE_JOURNAL)  			set_bit(MD_HAS_JOURNAL, &mddev->flags); @@ -1826,8 +1841,15 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)  				if (!(le32_to_cpu(sb->feature_map) &  				      MD_FEATURE_RECOVERY_BITMAP))  					rdev->saved_raid_disk = -1; -			} else -				set_bit(In_sync, &rdev->flags); +			} else { +				/* +				 * If the array is FROZEN, then the device can't +				 * be in_sync with rest of array. +				 */ +				if (!test_bit(MD_RECOVERY_FROZEN, +					      &mddev->recovery)) +					set_bit(In_sync, &rdev->flags); +			}  			rdev->raid_disk = role;  			break;  		} @@ -3664,11 +3686,7 @@ int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale)  		return -EINVAL;  	if (decimals < 0)  		decimals = 0; -	while (decimals < scale) { -		result *= 10; -		decimals ++; -	} -	*res = result; +	*res = result * int_pow(10, scale - decimals);  	return 0;  } @@ -4155,12 +4173,17 @@ __ATTR_PREALLOC(resync_start, S_IRUGO|S_IWUSR,   * active-idle   *     like active, but no writes have been seen for a while (100msec).   * + * broken + *     RAID0/LINEAR-only: same as clean, but array is missing a member. + *     It's useful because RAID0/LINEAR mounted-arrays aren't stopped + *     when a member is gone, so this state will at least alert the + *     user that something is wrong.   */  enum array_state { clear, inactive, suspended, readonly, read_auto, clean, active, -		   write_pending, active_idle, bad_word}; +		   write_pending, active_idle, broken, bad_word};  static char *array_states[] = {  	"clear", "inactive", "suspended", "readonly", "read-auto", "clean", "active", -	"write-pending", "active-idle", NULL }; +	"write-pending", "active-idle", "broken", NULL };  static int match_word(const char *word, char **list)  { @@ -4176,7 +4199,7 @@ array_state_show(struct mddev *mddev, char *page)  {  	enum array_state st = inactive; -	if (mddev->pers) +	if (mddev->pers && !test_bit(MD_NOT_READY, &mddev->flags)) {  		switch(mddev->ro) {  		case 1:  			st = readonly; @@ -4196,7 +4219,10 @@ array_state_show(struct mddev *mddev, char *page)  				st = active;  			spin_unlock(&mddev->lock);  		} -	else { + +		if (test_bit(MD_BROKEN, &mddev->flags) && st == clean) +			st = broken; +	} else {  		if (list_empty(&mddev->disks) &&  		    mddev->raid_disks == 0 &&  		    mddev->dev_sectors == 0) @@ -4310,6 +4336,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)  		break;  	case write_pending:  	case active_idle: +	case broken:  		/* these cannot be set */  		break;  	} @@ -5182,6 +5209,34 @@ static struct md_sysfs_entry md_consistency_policy =  __ATTR(consistency_policy, S_IRUGO | S_IWUSR, consistency_policy_show,         consistency_policy_store); +static ssize_t fail_last_dev_show(struct mddev *mddev, char *page) +{ +	return sprintf(page, "%d\n", mddev->fail_last_dev); +} + +/* + * Setting fail_last_dev to true to allow last device to be forcibly removed + * from RAID1/RAID10. + */ +static ssize_t +fail_last_dev_store(struct mddev *mddev, const char *buf, size_t len) +{ +	int ret; +	bool value; + +	ret = kstrtobool(buf, &value); +	if (ret) +		return ret; + +	if (value != mddev->fail_last_dev) +		mddev->fail_last_dev = value; + +	return len; +} +static struct md_sysfs_entry md_fail_last_dev = +__ATTR(fail_last_dev, S_IRUGO | S_IWUSR, fail_last_dev_show, +       fail_last_dev_store); +  static struct attribute *md_default_attrs[] = {  	&md_level.attr,  	&md_layout.attr, @@ -5198,6 +5253,7 @@ static struct attribute *md_default_attrs[] = {  	&md_array_size.attr,  	&max_corr_read_errors.attr,  	&md_consistency_policy.attr, +	&md_fail_last_dev.attr,  	NULL,  }; @@ -5744,9 +5800,6 @@ int md_run(struct mddev *mddev)  		md_update_sb(mddev, 0);  	md_new_event(mddev); -	sysfs_notify_dirent_safe(mddev->sysfs_state); -	sysfs_notify_dirent_safe(mddev->sysfs_action); -	sysfs_notify(&mddev->kobj, NULL, "degraded");  	return 0;  bitmap_abort: @@ -5767,6 +5820,7 @@ static int do_md_run(struct mddev *mddev)  {  	int err; +	set_bit(MD_NOT_READY, &mddev->flags);  	err = md_run(mddev);  	if (err)  		goto out; @@ -5787,9 +5841,14 @@ static int do_md_run(struct mddev *mddev)  	set_capacity(mddev->gendisk, mddev->array_sectors);  	revalidate_disk(mddev->gendisk); +	clear_bit(MD_NOT_READY, &mddev->flags);  	mddev->changed = 1;  	kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); +	sysfs_notify_dirent_safe(mddev->sysfs_state); +	sysfs_notify_dirent_safe(mddev->sysfs_action); +	sysfs_notify(&mddev->kobj, NULL, "degraded");  out: +	clear_bit(MD_NOT_READY, &mddev->flags);  	return err;  } @@ -6849,6 +6908,9 @@ static int set_array_info(struct mddev *mddev, mdu_array_info_t *info)  	mddev->external	     = 0;  	mddev->layout        = info->layout; +	if (mddev->level == 0) +		/* Cannot trust RAID0 layout info here */ +		mddev->layout = -1;  	mddev->chunk_sectors = info->chunk_size >> 9;  	if (mddev->persistent) { @@ -8900,6 +8962,7 @@ void md_check_recovery(struct mddev *mddev)  	if (mddev_trylock(mddev)) {  		int spares = 0; +		bool try_set_sync = mddev->safemode != 0;  		if (!mddev->external && mddev->safemode == 1)  			mddev->safemode = 0; @@ -8945,7 +9008,7 @@ void md_check_recovery(struct mddev *mddev)  			}  		} -		if (!mddev->external && !mddev->in_sync) { +		if (try_set_sync && !mddev->external && !mddev->in_sync) {  			spin_lock(&mddev->lock);  			set_in_sync(mddev);  			spin_unlock(&mddev->lock); @@ -9043,7 +9106,8 @@ void md_reap_sync_thread(struct mddev *mddev)  	/* resync has finished, collect result */  	md_unregister_thread(&mddev->sync_thread);  	if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) && -	    !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) { +	    !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) && +	    mddev->degraded != mddev->raid_disks) {  		/* success...*/  		/* activate any spares */  		if (mddev->pers->spare_active(mddev)) {  |