forked from mirrors/linux
		
	md: make reconfig_mutex optional for writes to md sysfs files.
Rather than using mddev_lock() to take the reconfig_mutex when writing to any md sysfs file, we only take mddev_lock() in the particular _store() functions that require it. Admittedly this is most, but it isn't all. This also allows us to remove special-case handling for new_dev_store (in md_attr_store). Signed-off-by: NeilBrown <neilb@suse.de>
This commit is contained in:
		
							parent
							
								
									5c47daf6e7
								
							
						
					
					
						commit
						6791875e2e
					
				
					 2 changed files with 320 additions and 168 deletions
				
			
		
							
								
								
									
										348
									
								
								drivers/md/md.c
									
									
									
									
									
								
							
							
						
						
									
										348
									
								
								drivers/md/md.c
									
									
									
									
									
								
							|  | @ -3256,26 +3256,32 @@ static ssize_t | ||||||
| level_store(struct mddev *mddev, const char *buf, size_t len) | level_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	char clevel[16]; | 	char clevel[16]; | ||||||
| 	ssize_t rv = len; | 	ssize_t rv; | ||||||
|  | 	size_t slen = len; | ||||||
| 	struct md_personality *pers, *oldpers; | 	struct md_personality *pers, *oldpers; | ||||||
| 	long level; | 	long level; | ||||||
| 	void *priv, *oldpriv; | 	void *priv, *oldpriv; | ||||||
| 	struct md_rdev *rdev; | 	struct md_rdev *rdev; | ||||||
| 
 | 
 | ||||||
| 	if (mddev->pers == NULL) { | 	if (slen == 0 || slen >= sizeof(clevel)) | ||||||
| 		if (len == 0) | 		return -EINVAL; | ||||||
| 			return 0; | 
 | ||||||
| 		if (len >= sizeof(mddev->clevel)) | 	rv = mddev_lock(mddev); | ||||||
| 			return -ENOSPC; | 	if (rv) | ||||||
| 		strncpy(mddev->clevel, buf, len); |  | ||||||
| 		if (mddev->clevel[len-1] == '\n') |  | ||||||
| 			len--; |  | ||||||
| 		mddev->clevel[len] = 0; |  | ||||||
| 		mddev->level = LEVEL_NONE; |  | ||||||
| 		return rv; | 		return rv; | ||||||
|  | 
 | ||||||
|  | 	if (mddev->pers == NULL) { | ||||||
|  | 		strncpy(mddev->clevel, buf, slen); | ||||||
|  | 		if (mddev->clevel[slen-1] == '\n') | ||||||
|  | 			slen--; | ||||||
|  | 		mddev->clevel[slen] = 0; | ||||||
|  | 		mddev->level = LEVEL_NONE; | ||||||
|  | 		rv = len; | ||||||
|  | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
|  | 	rv = -EROFS; | ||||||
| 	if (mddev->ro) | 	if (mddev->ro) | ||||||
| 		return  -EROFS; | 		goto out_unlock; | ||||||
| 
 | 
 | ||||||
| 	/* request to change the personality.  Need to ensure:
 | 	/* request to change the personality.  Need to ensure:
 | ||||||
| 	 *  - array is not engaged in resync/recovery/reshape | 	 *  - array is not engaged in resync/recovery/reshape | ||||||
|  | @ -3283,25 +3289,25 @@ level_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 	 *  - new personality will access other array. | 	 *  - new personality will access other array. | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
|  | 	rv = -EBUSY; | ||||||
| 	if (mddev->sync_thread || | 	if (mddev->sync_thread || | ||||||
| 	    test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || | 	    test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || | ||||||
| 	    mddev->reshape_position != MaxSector || | 	    mddev->reshape_position != MaxSector || | ||||||
| 	    mddev->sysfs_active) | 	    mddev->sysfs_active) | ||||||
| 		return -EBUSY; | 		goto out_unlock; | ||||||
| 
 | 
 | ||||||
|  | 	rv = -EINVAL; | ||||||
| 	if (!mddev->pers->quiesce) { | 	if (!mddev->pers->quiesce) { | ||||||
| 		printk(KERN_WARNING "md: %s: %s does not support online personality change\n", | 		printk(KERN_WARNING "md: %s: %s does not support online personality change\n", | ||||||
| 		       mdname(mddev), mddev->pers->name); | 		       mdname(mddev), mddev->pers->name); | ||||||
| 		return -EINVAL; | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Now find the new personality */ | 	/* Now find the new personality */ | ||||||
| 	if (len == 0 || len >= sizeof(clevel)) | 	strncpy(clevel, buf, slen); | ||||||
| 		return -EINVAL; | 	if (clevel[slen-1] == '\n') | ||||||
| 	strncpy(clevel, buf, len); | 		slen--; | ||||||
| 	if (clevel[len-1] == '\n') | 	clevel[slen] = 0; | ||||||
| 		len--; |  | ||||||
| 	clevel[len] = 0; |  | ||||||
| 	if (kstrtol(clevel, 10, &level)) | 	if (kstrtol(clevel, 10, &level)) | ||||||
| 		level = LEVEL_NONE; | 		level = LEVEL_NONE; | ||||||
| 
 | 
 | ||||||
|  | @ -3312,20 +3318,23 @@ level_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 	if (!pers || !try_module_get(pers->owner)) { | 	if (!pers || !try_module_get(pers->owner)) { | ||||||
| 		spin_unlock(&pers_lock); | 		spin_unlock(&pers_lock); | ||||||
| 		printk(KERN_WARNING "md: personality %s not loaded\n", clevel); | 		printk(KERN_WARNING "md: personality %s not loaded\n", clevel); | ||||||
| 		return -EINVAL; | 		rv = -EINVAL; | ||||||
|  | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 	spin_unlock(&pers_lock); | 	spin_unlock(&pers_lock); | ||||||
| 
 | 
 | ||||||
| 	if (pers == mddev->pers) { | 	if (pers == mddev->pers) { | ||||||
| 		/* Nothing to do! */ | 		/* Nothing to do! */ | ||||||
| 		module_put(pers->owner); | 		module_put(pers->owner); | ||||||
| 		return rv; | 		rv = len; | ||||||
|  | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 	if (!pers->takeover) { | 	if (!pers->takeover) { | ||||||
| 		module_put(pers->owner); | 		module_put(pers->owner); | ||||||
| 		printk(KERN_WARNING "md: %s: %s does not support personality takeover\n", | 		printk(KERN_WARNING "md: %s: %s does not support personality takeover\n", | ||||||
| 		       mdname(mddev), clevel); | 		       mdname(mddev), clevel); | ||||||
| 		return -EINVAL; | 		rv = -EINVAL; | ||||||
|  | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rdev_for_each(rdev, mddev) | 	rdev_for_each(rdev, mddev) | ||||||
|  | @ -3345,7 +3354,8 @@ level_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		module_put(pers->owner); | 		module_put(pers->owner); | ||||||
| 		printk(KERN_WARNING "md: %s: %s would not accept array\n", | 		printk(KERN_WARNING "md: %s: %s would not accept array\n", | ||||||
| 		       mdname(mddev), clevel); | 		       mdname(mddev), clevel); | ||||||
| 		return PTR_ERR(priv); | 		rv = PTR_ERR(priv); | ||||||
|  | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Looks like we have a winner */ | 	/* Looks like we have a winner */ | ||||||
|  | @ -3438,6 +3448,9 @@ level_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		md_update_sb(mddev, 1); | 		md_update_sb(mddev, 1); | ||||||
| 	sysfs_notify(&mddev->kobj, NULL, "level"); | 	sysfs_notify(&mddev->kobj, NULL, "level"); | ||||||
| 	md_new_event(mddev); | 	md_new_event(mddev); | ||||||
|  | 	rv = len; | ||||||
|  | out_unlock: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
| 	return rv; | 	return rv; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -3460,28 +3473,32 @@ layout_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	char *e; | 	char *e; | ||||||
| 	unsigned long n = simple_strtoul(buf, &e, 10); | 	unsigned long n = simple_strtoul(buf, &e, 10); | ||||||
|  | 	int err; | ||||||
| 
 | 
 | ||||||
| 	if (!*buf || (*e && *e != '\n')) | 	if (!*buf || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 
 | 
 | ||||||
| 	if (mddev->pers) { | 	if (mddev->pers) { | ||||||
| 		int err; |  | ||||||
| 		if (mddev->pers->check_reshape == NULL) | 		if (mddev->pers->check_reshape == NULL) | ||||||
| 			return -EBUSY; | 			err = -EBUSY; | ||||||
| 		if (mddev->ro) | 		else if (mddev->ro) | ||||||
| 			return -EROFS; | 			err = -EROFS; | ||||||
| 		mddev->new_layout = n; | 		else { | ||||||
| 		err = mddev->pers->check_reshape(mddev); | 			mddev->new_layout = n; | ||||||
| 		if (err) { | 			err = mddev->pers->check_reshape(mddev); | ||||||
| 			mddev->new_layout = mddev->layout; | 			if (err) | ||||||
| 			return err; | 				mddev->new_layout = mddev->layout; | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		mddev->new_layout = n; | 		mddev->new_layout = n; | ||||||
| 		if (mddev->reshape_position == MaxSector) | 		if (mddev->reshape_position == MaxSector) | ||||||
| 			mddev->layout = n; | 			mddev->layout = n; | ||||||
| 	} | 	} | ||||||
| 	return len; | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_layout = | static struct md_sysfs_entry md_layout = | ||||||
| __ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store); | __ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store); | ||||||
|  | @ -3504,32 +3521,39 @@ static ssize_t | ||||||
| raid_disks_store(struct mddev *mddev, const char *buf, size_t len) | raid_disks_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	char *e; | 	char *e; | ||||||
| 	int rv = 0; | 	int err; | ||||||
| 	unsigned long n = simple_strtoul(buf, &e, 10); | 	unsigned long n = simple_strtoul(buf, &e, 10); | ||||||
| 
 | 
 | ||||||
| 	if (!*buf || (*e && *e != '\n')) | 	if (!*buf || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	if (mddev->pers) | 	if (mddev->pers) | ||||||
| 		rv = update_raid_disks(mddev, n); | 		err = update_raid_disks(mddev, n); | ||||||
| 	else if (mddev->reshape_position != MaxSector) { | 	else if (mddev->reshape_position != MaxSector) { | ||||||
| 		struct md_rdev *rdev; | 		struct md_rdev *rdev; | ||||||
| 		int olddisks = mddev->raid_disks - mddev->delta_disks; | 		int olddisks = mddev->raid_disks - mddev->delta_disks; | ||||||
| 
 | 
 | ||||||
|  | 		err = -EINVAL; | ||||||
| 		rdev_for_each(rdev, mddev) { | 		rdev_for_each(rdev, mddev) { | ||||||
| 			if (olddisks < n && | 			if (olddisks < n && | ||||||
| 			    rdev->data_offset < rdev->new_data_offset) | 			    rdev->data_offset < rdev->new_data_offset) | ||||||
| 				return -EINVAL; | 				goto out_unlock; | ||||||
| 			if (olddisks > n && | 			if (olddisks > n && | ||||||
| 			    rdev->data_offset > rdev->new_data_offset) | 			    rdev->data_offset > rdev->new_data_offset) | ||||||
| 				return -EINVAL; | 				goto out_unlock; | ||||||
| 		} | 		} | ||||||
|  | 		err = 0; | ||||||
| 		mddev->delta_disks = n - olddisks; | 		mddev->delta_disks = n - olddisks; | ||||||
| 		mddev->raid_disks = n; | 		mddev->raid_disks = n; | ||||||
| 		mddev->reshape_backwards = (mddev->delta_disks < 0); | 		mddev->reshape_backwards = (mddev->delta_disks < 0); | ||||||
| 	} else | 	} else | ||||||
| 		mddev->raid_disks = n; | 		mddev->raid_disks = n; | ||||||
| 	return rv ? rv : len; | out_unlock: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ? err : len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_raid_disks = | static struct md_sysfs_entry md_raid_disks = | ||||||
| __ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store); | __ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store); | ||||||
|  | @ -3548,30 +3572,34 @@ chunk_size_show(struct mddev *mddev, char *page) | ||||||
| static ssize_t | static ssize_t | ||||||
| chunk_size_store(struct mddev *mddev, const char *buf, size_t len) | chunk_size_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
|  | 	int err; | ||||||
| 	char *e; | 	char *e; | ||||||
| 	unsigned long n = simple_strtoul(buf, &e, 10); | 	unsigned long n = simple_strtoul(buf, &e, 10); | ||||||
| 
 | 
 | ||||||
| 	if (!*buf || (*e && *e != '\n')) | 	if (!*buf || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	if (mddev->pers) { | 	if (mddev->pers) { | ||||||
| 		int err; |  | ||||||
| 		if (mddev->pers->check_reshape == NULL) | 		if (mddev->pers->check_reshape == NULL) | ||||||
| 			return -EBUSY; | 			err = -EBUSY; | ||||||
| 		if (mddev->ro) | 		else if (mddev->ro) | ||||||
| 			return -EROFS; | 			err = -EROFS; | ||||||
| 		mddev->new_chunk_sectors = n >> 9; | 		else { | ||||||
| 		err = mddev->pers->check_reshape(mddev); | 			mddev->new_chunk_sectors = n >> 9; | ||||||
| 		if (err) { | 			err = mddev->pers->check_reshape(mddev); | ||||||
| 			mddev->new_chunk_sectors = mddev->chunk_sectors; | 			if (err) | ||||||
| 			return err; | 				mddev->new_chunk_sectors = mddev->chunk_sectors; | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		mddev->new_chunk_sectors = n >> 9; | 		mddev->new_chunk_sectors = n >> 9; | ||||||
| 		if (mddev->reshape_position == MaxSector) | 		if (mddev->reshape_position == MaxSector) | ||||||
| 			mddev->chunk_sectors = n >> 9; | 			mddev->chunk_sectors = n >> 9; | ||||||
| 	} | 	} | ||||||
| 	return len; | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_chunk_size = | static struct md_sysfs_entry md_chunk_size = | ||||||
| __ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store); | __ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store); | ||||||
|  | @ -3587,20 +3615,27 @@ resync_start_show(struct mddev *mddev, char *page) | ||||||
| static ssize_t | static ssize_t | ||||||
| resync_start_store(struct mddev *mddev, const char *buf, size_t len) | resync_start_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
|  | 	int err; | ||||||
| 	char *e; | 	char *e; | ||||||
| 	unsigned long long n = simple_strtoull(buf, &e, 10); | 	unsigned long long n = simple_strtoull(buf, &e, 10); | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	if (mddev->pers && !test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) | 	if (mddev->pers && !test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) | ||||||
| 		return -EBUSY; | 		err = -EBUSY; | ||||||
| 	if (cmd_match(buf, "none")) | 	else if (cmd_match(buf, "none")) | ||||||
| 		n = MaxSector; | 		n = MaxSector; | ||||||
| 	else if (!*buf || (*e && *e != '\n')) | 	else if (!*buf || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		err = -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	mddev->recovery_cp = n; | 	if (!err) { | ||||||
| 	if (mddev->pers) | 		mddev->recovery_cp = n; | ||||||
| 		set_bit(MD_CHANGE_CLEAN, &mddev->flags); | 		if (mddev->pers) | ||||||
| 	return len; | 			set_bit(MD_CHANGE_CLEAN, &mddev->flags); | ||||||
|  | 	} | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_resync_start = | static struct md_sysfs_entry md_resync_start = | ||||||
| __ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store); | __ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store); | ||||||
|  | @ -3698,8 +3733,39 @@ static int restart_array(struct mddev *mddev); | ||||||
| static ssize_t | static ssize_t | ||||||
| array_state_store(struct mddev *mddev, const char *buf, size_t len) | array_state_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	int err = -EINVAL; | 	int err; | ||||||
| 	enum array_state st = match_word(buf, array_states); | 	enum array_state st = match_word(buf, array_states); | ||||||
|  | 
 | ||||||
|  | 	if (mddev->pers && (st == active || st == clean) && mddev->ro != 1) { | ||||||
|  | 		/* don't take reconfig_mutex when toggling between
 | ||||||
|  | 		 * clean and active | ||||||
|  | 		 */ | ||||||
|  | 		spin_lock(&mddev->lock); | ||||||
|  | 		if (st == active) { | ||||||
|  | 			restart_array(mddev); | ||||||
|  | 			clear_bit(MD_CHANGE_PENDING, &mddev->flags); | ||||||
|  | 			wake_up(&mddev->sb_wait); | ||||||
|  | 			err = 0; | ||||||
|  | 		} else /* st == clean */ { | ||||||
|  | 			restart_array(mddev); | ||||||
|  | 			if (atomic_read(&mddev->writes_pending) == 0) { | ||||||
|  | 				if (mddev->in_sync == 0) { | ||||||
|  | 					mddev->in_sync = 1; | ||||||
|  | 					if (mddev->safemode == 1) | ||||||
|  | 						mddev->safemode = 0; | ||||||
|  | 					set_bit(MD_CHANGE_CLEAN, &mddev->flags); | ||||||
|  | 				} | ||||||
|  | 				err = 0; | ||||||
|  | 			} else | ||||||
|  | 				err = -EBUSY; | ||||||
|  | 		} | ||||||
|  | 		spin_unlock(&mddev->lock); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 	err = -EINVAL; | ||||||
| 	switch(st) { | 	switch(st) { | ||||||
| 	case bad_word: | 	case bad_word: | ||||||
| 		break; | 		break; | ||||||
|  | @ -3775,14 +3841,14 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		/* these cannot be set */ | 		/* these cannot be set */ | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	if (err) | 
 | ||||||
| 		return err; | 	if (!err) { | ||||||
| 	else { |  | ||||||
| 		if (mddev->hold_active == UNTIL_IOCTL) | 		if (mddev->hold_active == UNTIL_IOCTL) | ||||||
| 			mddev->hold_active = 0; | 			mddev->hold_active = 0; | ||||||
| 		sysfs_notify_dirent_safe(mddev->sysfs_state); | 		sysfs_notify_dirent_safe(mddev->sysfs_state); | ||||||
| 		return len; |  | ||||||
| 	} | 	} | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_array_state = | static struct md_sysfs_entry md_array_state = | ||||||
| __ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); | __ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); | ||||||
|  | @ -3843,6 +3909,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 	    minor != MINOR(dev)) | 	    minor != MINOR(dev)) | ||||||
| 		return -EOVERFLOW; | 		return -EOVERFLOW; | ||||||
| 
 | 
 | ||||||
|  | 	flush_workqueue(md_misc_wq); | ||||||
|  | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	if (mddev->persistent) { | 	if (mddev->persistent) { | ||||||
| 		rdev = md_import_device(dev, mddev->major_version, | 		rdev = md_import_device(dev, mddev->major_version, | ||||||
| 					mddev->minor_version); | 					mddev->minor_version); | ||||||
|  | @ -3866,6 +3937,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
|  out: |  out: | ||||||
| 	if (err) | 	if (err) | ||||||
| 		export_rdev(rdev); | 		export_rdev(rdev); | ||||||
|  | 	mddev_unlock(mddev); | ||||||
| 	return err ? err : len; | 	return err ? err : len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -3877,7 +3949,11 @@ bitmap_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	char *end; | 	char *end; | ||||||
| 	unsigned long chunk, end_chunk; | 	unsigned long chunk, end_chunk; | ||||||
|  | 	int err; | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	if (!mddev->bitmap) | 	if (!mddev->bitmap) | ||||||
| 		goto out; | 		goto out; | ||||||
| 	/* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */ | 	/* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */ | ||||||
|  | @ -3895,6 +3971,7 @@ bitmap_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 	} | 	} | ||||||
| 	bitmap_unplug(mddev->bitmap); /* flush the bits to disk */ | 	bitmap_unplug(mddev->bitmap); /* flush the bits to disk */ | ||||||
| out: | out: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
| 	return len; | 	return len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -3922,6 +3999,9 @@ size_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 
 | 
 | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| 		return err; | 		return err; | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	if (mddev->pers) { | 	if (mddev->pers) { | ||||||
| 		err = update_size(mddev, sectors); | 		err = update_size(mddev, sectors); | ||||||
| 		md_update_sb(mddev, 1); | 		md_update_sb(mddev, 1); | ||||||
|  | @ -3932,6 +4012,7 @@ size_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		else | 		else | ||||||
| 			err = -ENOSPC; | 			err = -ENOSPC; | ||||||
| 	} | 	} | ||||||
|  | 	mddev_unlock(mddev); | ||||||
| 	return err ? err : len; | 	return err ? err : len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -3961,21 +4042,28 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	int major, minor; | 	int major, minor; | ||||||
| 	char *e; | 	char *e; | ||||||
|  | 	int err; | ||||||
| 	/* Changing the details of 'external' metadata is
 | 	/* Changing the details of 'external' metadata is
 | ||||||
| 	 * always permitted.  Otherwise there must be | 	 * always permitted.  Otherwise there must be | ||||||
| 	 * no devices attached to the array. | 	 * no devices attached to the array. | ||||||
| 	 */ | 	 */ | ||||||
|  | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 	err = -EBUSY; | ||||||
| 	if (mddev->external && strncmp(buf, "external:", 9) == 0) | 	if (mddev->external && strncmp(buf, "external:", 9) == 0) | ||||||
| 		; | 		; | ||||||
| 	else if (!list_empty(&mddev->disks)) | 	else if (!list_empty(&mddev->disks)) | ||||||
| 		return -EBUSY; | 		goto out_unlock; | ||||||
| 
 | 
 | ||||||
|  | 	err = 0; | ||||||
| 	if (cmd_match(buf, "none")) { | 	if (cmd_match(buf, "none")) { | ||||||
| 		mddev->persistent = 0; | 		mddev->persistent = 0; | ||||||
| 		mddev->external = 0; | 		mddev->external = 0; | ||||||
| 		mddev->major_version = 0; | 		mddev->major_version = 0; | ||||||
| 		mddev->minor_version = 90; | 		mddev->minor_version = 90; | ||||||
| 		return len; | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 	if (strncmp(buf, "external:", 9) == 0) { | 	if (strncmp(buf, "external:", 9) == 0) { | ||||||
| 		size_t namelen = len-9; | 		size_t namelen = len-9; | ||||||
|  | @ -3989,22 +4077,27 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		mddev->external = 1; | 		mddev->external = 1; | ||||||
| 		mddev->major_version = 0; | 		mddev->major_version = 0; | ||||||
| 		mddev->minor_version = 90; | 		mddev->minor_version = 90; | ||||||
| 		return len; | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
| 	major = simple_strtoul(buf, &e, 10); | 	major = simple_strtoul(buf, &e, 10); | ||||||
|  | 	err = -EINVAL; | ||||||
| 	if (e==buf || *e != '.') | 	if (e==buf || *e != '.') | ||||||
| 		return -EINVAL; | 		goto out_unlock; | ||||||
| 	buf = e+1; | 	buf = e+1; | ||||||
| 	minor = simple_strtoul(buf, &e, 10); | 	minor = simple_strtoul(buf, &e, 10); | ||||||
| 	if (e==buf || (*e && *e != '\n') ) | 	if (e==buf || (*e && *e != '\n') ) | ||||||
| 		return -EINVAL; | 		goto out_unlock; | ||||||
|  | 	err = -ENOENT; | ||||||
| 	if (major >= ARRAY_SIZE(super_types) || super_types[major].name == NULL) | 	if (major >= ARRAY_SIZE(super_types) || super_types[major].name == NULL) | ||||||
| 		return -ENOENT; | 		goto out_unlock; | ||||||
| 	mddev->major_version = major; | 	mddev->major_version = major; | ||||||
| 	mddev->minor_version = minor; | 	mddev->minor_version = minor; | ||||||
| 	mddev->persistent = 1; | 	mddev->persistent = 1; | ||||||
| 	mddev->external = 0; | 	mddev->external = 0; | ||||||
| 	return len; | 	err = 0; | ||||||
|  | out_unlock: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry md_metadata = | static struct md_sysfs_entry md_metadata = | ||||||
|  | @ -4049,7 +4142,10 @@ action_store(struct mddev *mddev, const char *page, size_t len) | ||||||
| 		flush_workqueue(md_misc_wq); | 		flush_workqueue(md_misc_wq); | ||||||
| 		if (mddev->sync_thread) { | 		if (mddev->sync_thread) { | ||||||
| 			set_bit(MD_RECOVERY_INTR, &mddev->recovery); | 			set_bit(MD_RECOVERY_INTR, &mddev->recovery); | ||||||
| 			md_reap_sync_thread(mddev); | 			if (mddev_lock(mddev) == 0) { | ||||||
|  | 				md_reap_sync_thread(mddev); | ||||||
|  | 				mddev_unlock(mddev); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || | 	} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || | ||||||
| 		   test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) | 		   test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) | ||||||
|  | @ -4063,7 +4159,11 @@ action_store(struct mddev *mddev, const char *page, size_t len) | ||||||
| 		int err; | 		int err; | ||||||
| 		if (mddev->pers->start_reshape == NULL) | 		if (mddev->pers->start_reshape == NULL) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		err = mddev->pers->start_reshape(mddev); | 		err = mddev_lock(mddev); | ||||||
|  | 		if (!err) { | ||||||
|  | 			err = mddev->pers->start_reshape(mddev); | ||||||
|  | 			mddev_unlock(mddev); | ||||||
|  | 		} | ||||||
| 		if (err) | 		if (err) | ||||||
| 			return err; | 			return err; | ||||||
| 		sysfs_notify(&mddev->kobj, NULL, "degraded"); | 		sysfs_notify(&mddev->kobj, NULL, "degraded"); | ||||||
|  | @ -4346,14 +4446,20 @@ suspend_lo_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	char *e; | 	char *e; | ||||||
| 	unsigned long long new = simple_strtoull(buf, &e, 10); | 	unsigned long long new = simple_strtoull(buf, &e, 10); | ||||||
| 	unsigned long long old = mddev->suspend_lo; | 	unsigned long long old; | ||||||
|  | 	int err; | ||||||
| 
 | 
 | ||||||
| 	if (mddev->pers == NULL || |  | ||||||
| 	    mddev->pers->quiesce == NULL) |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	if (buf == e || (*e && *e != '\n')) | 	if (buf == e || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 	err = -EINVAL; | ||||||
|  | 	if (mddev->pers == NULL || | ||||||
|  | 	    mddev->pers->quiesce == NULL) | ||||||
|  | 		goto unlock; | ||||||
|  | 	old = mddev->suspend_lo; | ||||||
| 	mddev->suspend_lo = new; | 	mddev->suspend_lo = new; | ||||||
| 	if (new >= old) | 	if (new >= old) | ||||||
| 		/* Shrinking suspended region */ | 		/* Shrinking suspended region */ | ||||||
|  | @ -4363,7 +4469,10 @@ suspend_lo_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		mddev->pers->quiesce(mddev, 1); | 		mddev->pers->quiesce(mddev, 1); | ||||||
| 		mddev->pers->quiesce(mddev, 0); | 		mddev->pers->quiesce(mddev, 0); | ||||||
| 	} | 	} | ||||||
| 	return len; | 	err = 0; | ||||||
|  | unlock: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_suspend_lo = | static struct md_sysfs_entry md_suspend_lo = | ||||||
| __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store); | __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store); | ||||||
|  | @ -4379,14 +4488,20 @@ suspend_hi_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	char *e; | 	char *e; | ||||||
| 	unsigned long long new = simple_strtoull(buf, &e, 10); | 	unsigned long long new = simple_strtoull(buf, &e, 10); | ||||||
| 	unsigned long long old = mddev->suspend_hi; | 	unsigned long long old; | ||||||
|  | 	int err; | ||||||
| 
 | 
 | ||||||
| 	if (mddev->pers == NULL || |  | ||||||
| 	    mddev->pers->quiesce == NULL) |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	if (buf == e || (*e && *e != '\n')) | 	if (buf == e || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 	err = -EINVAL; | ||||||
|  | 	if (mddev->pers == NULL || | ||||||
|  | 	    mddev->pers->quiesce == NULL) | ||||||
|  | 		goto unlock; | ||||||
|  | 	old = mddev->suspend_hi; | ||||||
| 	mddev->suspend_hi = new; | 	mddev->suspend_hi = new; | ||||||
| 	if (new <= old) | 	if (new <= old) | ||||||
| 		/* Shrinking suspended region */ | 		/* Shrinking suspended region */ | ||||||
|  | @ -4396,7 +4511,10 @@ suspend_hi_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		mddev->pers->quiesce(mddev, 1); | 		mddev->pers->quiesce(mddev, 1); | ||||||
| 		mddev->pers->quiesce(mddev, 0); | 		mddev->pers->quiesce(mddev, 0); | ||||||
| 	} | 	} | ||||||
| 	return len; | 	err = 0; | ||||||
|  | unlock: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| static struct md_sysfs_entry md_suspend_hi = | static struct md_sysfs_entry md_suspend_hi = | ||||||
| __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store); | __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store); | ||||||
|  | @ -4416,11 +4534,17 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	struct md_rdev *rdev; | 	struct md_rdev *rdev; | ||||||
| 	char *e; | 	char *e; | ||||||
|  | 	int err; | ||||||
| 	unsigned long long new = simple_strtoull(buf, &e, 10); | 	unsigned long long new = simple_strtoull(buf, &e, 10); | ||||||
| 	if (mddev->pers) | 
 | ||||||
| 		return -EBUSY; |  | ||||||
| 	if (buf == e || (*e && *e != '\n')) | 	if (buf == e || (*e && *e != '\n')) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 	err = -EBUSY; | ||||||
|  | 	if (mddev->pers) | ||||||
|  | 		goto unlock; | ||||||
| 	mddev->reshape_position = new; | 	mddev->reshape_position = new; | ||||||
| 	mddev->delta_disks = 0; | 	mddev->delta_disks = 0; | ||||||
| 	mddev->reshape_backwards = 0; | 	mddev->reshape_backwards = 0; | ||||||
|  | @ -4429,7 +4553,10 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 	mddev->new_chunk_sectors = mddev->chunk_sectors; | 	mddev->new_chunk_sectors = mddev->chunk_sectors; | ||||||
| 	rdev_for_each(rdev, mddev) | 	rdev_for_each(rdev, mddev) | ||||||
| 		rdev->new_data_offset = rdev->data_offset; | 		rdev->new_data_offset = rdev->data_offset; | ||||||
| 	return len; | 	err = 0; | ||||||
|  | unlock: | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry md_reshape_position = | static struct md_sysfs_entry md_reshape_position = | ||||||
|  | @ -4447,6 +4574,8 @@ static ssize_t | ||||||
| reshape_direction_store(struct mddev *mddev, const char *buf, size_t len) | reshape_direction_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	int backwards = 0; | 	int backwards = 0; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
| 	if (cmd_match(buf, "forwards")) | 	if (cmd_match(buf, "forwards")) | ||||||
| 		backwards = 0; | 		backwards = 0; | ||||||
| 	else if (cmd_match(buf, "backwards")) | 	else if (cmd_match(buf, "backwards")) | ||||||
|  | @ -4456,16 +4585,19 @@ reshape_direction_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 	if (mddev->reshape_backwards == backwards) | 	if (mddev->reshape_backwards == backwards) | ||||||
| 		return len; | 		return len; | ||||||
| 
 | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 	/* check if we are allowed to change */ | 	/* check if we are allowed to change */ | ||||||
| 	if (mddev->delta_disks) | 	if (mddev->delta_disks) | ||||||
| 		return -EBUSY; | 		err = -EBUSY; | ||||||
| 
 | 	else if (mddev->persistent && | ||||||
| 	if (mddev->persistent && |  | ||||||
| 	    mddev->major_version == 0) | 	    mddev->major_version == 0) | ||||||
| 		return -EINVAL; | 		err =  -EINVAL; | ||||||
| 
 | 	else | ||||||
| 	mddev->reshape_backwards = backwards; | 		mddev->reshape_backwards = backwards; | ||||||
| 	return len; | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry md_reshape_direction = | static struct md_sysfs_entry md_reshape_direction = | ||||||
|  | @ -4486,6 +4618,11 @@ static ssize_t | ||||||
| array_size_store(struct mddev *mddev, const char *buf, size_t len) | array_size_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| { | { | ||||||
| 	sector_t sectors; | 	sector_t sectors; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	err = mddev_lock(mddev); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
| 
 | 
 | ||||||
| 	if (strncmp(buf, "default", 7) == 0) { | 	if (strncmp(buf, "default", 7) == 0) { | ||||||
| 		if (mddev->pers) | 		if (mddev->pers) | ||||||
|  | @ -4496,19 +4633,22 @@ array_size_store(struct mddev *mddev, const char *buf, size_t len) | ||||||
| 		mddev->external_size = 0; | 		mddev->external_size = 0; | ||||||
| 	} else { | 	} else { | ||||||
| 		if (strict_blocks_to_sectors(buf, §ors) < 0) | 		if (strict_blocks_to_sectors(buf, §ors) < 0) | ||||||
| 			return -EINVAL; | 			err = -EINVAL; | ||||||
| 		if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors) | 		else if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors) | ||||||
| 			return -E2BIG; | 			err = -E2BIG; | ||||||
| 
 | 		else | ||||||
| 		mddev->external_size = 1; | 			mddev->external_size = 1; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mddev->array_sectors = sectors; | 	if (!err) { | ||||||
| 	if (mddev->pers) { | 		mddev->array_sectors = sectors; | ||||||
| 		set_capacity(mddev->gendisk, mddev->array_sectors); | 		if (mddev->pers) { | ||||||
| 		revalidate_disk(mddev->gendisk); | 			set_capacity(mddev->gendisk, mddev->array_sectors); | ||||||
|  | 			revalidate_disk(mddev->gendisk); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return len; | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry md_array_size = | static struct md_sysfs_entry md_array_size = | ||||||
|  | @ -4596,13 +4736,7 @@ md_attr_store(struct kobject *kobj, struct attribute *attr, | ||||||
| 	} | 	} | ||||||
| 	mddev_get(mddev); | 	mddev_get(mddev); | ||||||
| 	spin_unlock(&all_mddevs_lock); | 	spin_unlock(&all_mddevs_lock); | ||||||
| 	if (entry->store == new_dev_store) | 	rv = entry->store(mddev, page, length); | ||||||
| 		flush_workqueue(md_misc_wq); |  | ||||||
| 	rv = mddev_lock(mddev); |  | ||||||
| 	if (!rv) { |  | ||||||
| 		rv = entry->store(mddev, page, length); |  | ||||||
| 		mddev_unlock(mddev); |  | ||||||
| 	} |  | ||||||
| 	mddev_put(mddev); | 	mddev_put(mddev); | ||||||
| 	return rv; | 	return rv; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5400,21 +5400,25 @@ EXPORT_SYMBOL(raid5_set_cache_size); | ||||||
| static ssize_t | static ssize_t | ||||||
| raid5_store_stripe_cache_size(struct mddev *mddev, const char *page, size_t len) | raid5_store_stripe_cache_size(struct mddev *mddev, const char *page, size_t len) | ||||||
| { | { | ||||||
| 	struct r5conf *conf = mddev->private; | 	struct r5conf *conf; | ||||||
| 	unsigned long new; | 	unsigned long new; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	if (len >= PAGE_SIZE) | 	if (len >= PAGE_SIZE) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (!conf) |  | ||||||
| 		return -ENODEV; |  | ||||||
| 
 |  | ||||||
| 	if (kstrtoul(page, 10, &new)) | 	if (kstrtoul(page, 10, &new)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	err = raid5_set_cache_size(mddev, new); | 	err = mddev_lock(mddev); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
| 	return len; | 	conf = mddev->private; | ||||||
|  | 	if (!conf) | ||||||
|  | 		err = -ENODEV; | ||||||
|  | 	else | ||||||
|  | 		err = raid5_set_cache_size(mddev, new); | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 
 | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry | static struct md_sysfs_entry | ||||||
|  | @ -5438,19 +5442,27 @@ raid5_show_preread_threshold(struct mddev *mddev, char *page) | ||||||
| static ssize_t | static ssize_t | ||||||
| raid5_store_preread_threshold(struct mddev *mddev, const char *page, size_t len) | raid5_store_preread_threshold(struct mddev *mddev, const char *page, size_t len) | ||||||
| { | { | ||||||
| 	struct r5conf *conf = mddev->private; | 	struct r5conf *conf; | ||||||
| 	unsigned long new; | 	unsigned long new; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
| 	if (len >= PAGE_SIZE) | 	if (len >= PAGE_SIZE) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (!conf) |  | ||||||
| 		return -ENODEV; |  | ||||||
| 
 |  | ||||||
| 	if (kstrtoul(page, 10, &new)) | 	if (kstrtoul(page, 10, &new)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (new > conf->max_nr_stripes) | 
 | ||||||
| 		return -EINVAL; | 	err = mddev_lock(mddev); | ||||||
| 	conf->bypass_threshold = new; | 	if (err) | ||||||
| 	return len; | 		return err; | ||||||
|  | 	conf = mddev->private; | ||||||
|  | 	if (!conf) | ||||||
|  | 		err = -ENODEV; | ||||||
|  | 	else if (new > conf->max_nr_stripes) | ||||||
|  | 		err = -EINVAL; | ||||||
|  | 	else | ||||||
|  | 		conf->bypass_threshold = new; | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry | static struct md_sysfs_entry | ||||||
|  | @ -5475,29 +5487,35 @@ raid5_show_skip_copy(struct mddev *mddev, char *page) | ||||||
| static ssize_t | static ssize_t | ||||||
| raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len) | raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len) | ||||||
| { | { | ||||||
| 	struct r5conf *conf = mddev->private; | 	struct r5conf *conf; | ||||||
| 	unsigned long new; | 	unsigned long new; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
| 	if (len >= PAGE_SIZE) | 	if (len >= PAGE_SIZE) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (!conf) |  | ||||||
| 		return -ENODEV; |  | ||||||
| 
 |  | ||||||
| 	if (kstrtoul(page, 10, &new)) | 	if (kstrtoul(page, 10, &new)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	new = !!new; | 	new = !!new; | ||||||
| 	if (new == conf->skip_copy) |  | ||||||
| 		return len; |  | ||||||
| 
 | 
 | ||||||
| 	mddev_suspend(mddev); | 	err = mddev_lock(mddev); | ||||||
| 	conf->skip_copy = new; | 	if (err) | ||||||
| 	if (new) | 		return err; | ||||||
| 		mddev->queue->backing_dev_info.capabilities |= | 	conf = mddev->private; | ||||||
| 						BDI_CAP_STABLE_WRITES; | 	if (!conf) | ||||||
| 	else | 		err = -ENODEV; | ||||||
| 		mddev->queue->backing_dev_info.capabilities &= | 	else if (new != conf->skip_copy) { | ||||||
| 						~BDI_CAP_STABLE_WRITES; | 		mddev_suspend(mddev); | ||||||
| 	mddev_resume(mddev); | 		conf->skip_copy = new; | ||||||
| 	return len; | 		if (new) | ||||||
|  | 			mddev->queue->backing_dev_info.capabilities |= | ||||||
|  | 				BDI_CAP_STABLE_WRITES; | ||||||
|  | 		else | ||||||
|  | 			mddev->queue->backing_dev_info.capabilities &= | ||||||
|  | 				~BDI_CAP_STABLE_WRITES; | ||||||
|  | 		mddev_resume(mddev); | ||||||
|  | 	} | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry | static struct md_sysfs_entry | ||||||
|  | @ -5538,7 +5556,7 @@ static int alloc_thread_groups(struct r5conf *conf, int cnt, | ||||||
| static ssize_t | static ssize_t | ||||||
| raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) | raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) | ||||||
| { | { | ||||||
| 	struct r5conf *conf = mddev->private; | 	struct r5conf *conf; | ||||||
| 	unsigned long new; | 	unsigned long new; | ||||||
| 	int err; | 	int err; | ||||||
| 	struct r5worker_group *new_groups, *old_groups; | 	struct r5worker_group *new_groups, *old_groups; | ||||||
|  | @ -5546,41 +5564,41 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) | ||||||
| 
 | 
 | ||||||
| 	if (len >= PAGE_SIZE) | 	if (len >= PAGE_SIZE) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	if (!conf) |  | ||||||
| 		return -ENODEV; |  | ||||||
| 
 |  | ||||||
| 	if (kstrtoul(page, 10, &new)) | 	if (kstrtoul(page, 10, &new)) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	if (new == conf->worker_cnt_per_group) | 	err = mddev_lock(mddev); | ||||||
| 		return len; |  | ||||||
| 
 |  | ||||||
| 	mddev_suspend(mddev); |  | ||||||
| 
 |  | ||||||
| 	old_groups = conf->worker_groups; |  | ||||||
| 	if (old_groups) |  | ||||||
| 		flush_workqueue(raid5_wq); |  | ||||||
| 
 |  | ||||||
| 	err = alloc_thread_groups(conf, new, |  | ||||||
| 				  &group_cnt, &worker_cnt_per_group, |  | ||||||
| 				  &new_groups); |  | ||||||
| 	if (!err) { |  | ||||||
| 		spin_lock_irq(&conf->device_lock); |  | ||||||
| 		conf->group_cnt = group_cnt; |  | ||||||
| 		conf->worker_cnt_per_group = worker_cnt_per_group; |  | ||||||
| 		conf->worker_groups = new_groups; |  | ||||||
| 		spin_unlock_irq(&conf->device_lock); |  | ||||||
| 
 |  | ||||||
| 		if (old_groups) |  | ||||||
| 			kfree(old_groups[0].workers); |  | ||||||
| 		kfree(old_groups); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	mddev_resume(mddev); |  | ||||||
| 
 |  | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
| 	return len; | 	conf = mddev->private; | ||||||
|  | 	if (!conf) | ||||||
|  | 		err = -ENODEV; | ||||||
|  | 	else if (new != conf->worker_cnt_per_group) { | ||||||
|  | 		mddev_suspend(mddev); | ||||||
|  | 
 | ||||||
|  | 		old_groups = conf->worker_groups; | ||||||
|  | 		if (old_groups) | ||||||
|  | 			flush_workqueue(raid5_wq); | ||||||
|  | 
 | ||||||
|  | 		err = alloc_thread_groups(conf, new, | ||||||
|  | 					  &group_cnt, &worker_cnt_per_group, | ||||||
|  | 					  &new_groups); | ||||||
|  | 		if (!err) { | ||||||
|  | 			spin_lock_irq(&conf->device_lock); | ||||||
|  | 			conf->group_cnt = group_cnt; | ||||||
|  | 			conf->worker_cnt_per_group = worker_cnt_per_group; | ||||||
|  | 			conf->worker_groups = new_groups; | ||||||
|  | 			spin_unlock_irq(&conf->device_lock); | ||||||
|  | 
 | ||||||
|  | 			if (old_groups) | ||||||
|  | 				kfree(old_groups[0].workers); | ||||||
|  | 			kfree(old_groups); | ||||||
|  | 		} | ||||||
|  | 		mddev_resume(mddev); | ||||||
|  | 	} | ||||||
|  | 	mddev_unlock(mddev); | ||||||
|  | 
 | ||||||
|  | 	return err ?: len; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct md_sysfs_entry | static struct md_sysfs_entry | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 NeilBrown
						NeilBrown