static int parse_raid_params()

in dm-raid.c [1121:1529]


static int parse_raid_params(struct raid_set *rs, struct dm_arg_set *as,
			     unsigned int num_raid_params)
{
	int value, raid10_format = ALGORITHM_RAID10_DEFAULT;
	unsigned int raid10_copies = 2;
	unsigned int i, write_mostly = 0;
	unsigned int region_size = 0;
	sector_t max_io_len;
	const char *arg, *key;
	struct raid_dev *rd;
	struct raid_type *rt = rs->raid_type;

	arg = dm_shift_arg(as);
	num_raid_params--; /* Account for chunk_size argument */

	if (kstrtoint(arg, 10, &value) < 0) {
		rs->ti->error = "Bad numerical argument given for chunk_size";
		return -EINVAL;
	}

	/*
	 * First, parse the in-order required arguments
	 * "chunk_size" is the only argument of this type.
	 */
	if (rt_is_raid1(rt)) {
		if (value)
			DMERR("Ignoring chunk size parameter for RAID 1");
		value = 0;
	} else if (!is_power_of_2(value)) {
		rs->ti->error = "Chunk size must be a power of 2";
		return -EINVAL;
	} else if (value < 8) {
		rs->ti->error = "Chunk size value is too small";
		return -EINVAL;
	}

	rs->md.new_chunk_sectors = rs->md.chunk_sectors = value;

	/*
	 * We set each individual device as In_sync with a completed
	 * 'recovery_offset'.  If there has been a device failure or
	 * replacement then one of the following cases applies:
	 *
	 *   1) User specifies 'rebuild'.
	 *	- Device is reset when param is read.
	 *   2) A new device is supplied.
	 *	- No matching superblock found, resets device.
	 *   3) Device failure was transient and returns on reload.
	 *	- Failure noticed, resets device for bitmap replay.
	 *   4) Device hadn't completed recovery after previous failure.
	 *	- Superblock is read and overrides recovery_offset.
	 *
	 * What is found in the superblocks of the devices is always
	 * authoritative, unless 'rebuild' or '[no]sync' was specified.
	 */
	for (i = 0; i < rs->raid_disks; i++) {
		set_bit(In_sync, &rs->dev[i].rdev.flags);
		rs->dev[i].rdev.recovery_offset = MaxSector;
	}

	/*
	 * Second, parse the unordered optional arguments
	 */
	for (i = 0; i < num_raid_params; i++) {
		key = dm_shift_arg(as);
		if (!key) {
			rs->ti->error = "Not enough raid parameters given";
			return -EINVAL;
		}

		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_NOSYNC))) {
			if (test_and_set_bit(__CTR_FLAG_NOSYNC, &rs->ctr_flags)) {
				rs->ti->error = "Only one 'nosync' argument allowed";
				return -EINVAL;
			}
			continue;
		}
		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_SYNC))) {
			if (test_and_set_bit(__CTR_FLAG_SYNC, &rs->ctr_flags)) {
				rs->ti->error = "Only one 'sync' argument allowed";
				return -EINVAL;
			}
			continue;
		}
		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_RAID10_USE_NEAR_SETS))) {
			if (test_and_set_bit(__CTR_FLAG_RAID10_USE_NEAR_SETS, &rs->ctr_flags)) {
				rs->ti->error = "Only one 'raid10_use_new_sets' argument allowed";
				return -EINVAL;
			}
			continue;
		}

		arg = dm_shift_arg(as);
		i++; /* Account for the argument pairs */
		if (!arg) {
			rs->ti->error = "Wrong number of raid parameters given";
			return -EINVAL;
		}

		/*
		 * Parameters that take a string value are checked here.
		 */
		/* "raid10_format {near|offset|far} */
		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_RAID10_FORMAT))) {
			if (test_and_set_bit(__CTR_FLAG_RAID10_FORMAT, &rs->ctr_flags)) {
				rs->ti->error = "Only one 'raid10_format' argument pair allowed";
				return -EINVAL;
			}
			if (!rt_is_raid10(rt)) {
				rs->ti->error = "'raid10_format' is an invalid parameter for this RAID type";
				return -EINVAL;
			}
			raid10_format = raid10_name_to_format(arg);
			if (raid10_format < 0) {
				rs->ti->error = "Invalid 'raid10_format' value given";
				return raid10_format;
			}
			continue;
		}

		/* "journal_dev <dev>" */
		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_JOURNAL_DEV))) {
			int r;
			struct md_rdev *jdev;

			if (test_and_set_bit(__CTR_FLAG_JOURNAL_DEV, &rs->ctr_flags)) {
				rs->ti->error = "Only one raid4/5/6 set journaling device allowed";
				return -EINVAL;
			}
			if (!rt_is_raid456(rt)) {
				rs->ti->error = "'journal_dev' is an invalid parameter for this RAID type";
				return -EINVAL;
			}
			r = dm_get_device(rs->ti, arg, dm_table_get_mode(rs->ti->table),
					  &rs->journal_dev.dev);
			if (r) {
				rs->ti->error = "raid4/5/6 journal device lookup failure";
				return r;
			}
			jdev = &rs->journal_dev.rdev;
			md_rdev_init(jdev);
			jdev->mddev = &rs->md;
			jdev->bdev = rs->journal_dev.dev->bdev;
			jdev->sectors = bdev_nr_sectors(jdev->bdev);
			if (jdev->sectors < MIN_RAID456_JOURNAL_SPACE) {
				rs->ti->error = "No space for raid4/5/6 journal";
				return -ENOSPC;
			}
			rs->journal_dev.mode = R5C_JOURNAL_MODE_WRITE_THROUGH;
			set_bit(Journal, &jdev->flags);
			continue;
		}

		/* "journal_mode <mode>" ("journal_dev" mandatory!) */
		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_JOURNAL_MODE))) {
			int r;

			if (!test_bit(__CTR_FLAG_JOURNAL_DEV, &rs->ctr_flags)) {
				rs->ti->error = "raid4/5/6 'journal_mode' is invalid without 'journal_dev'";
				return -EINVAL;
			}
			if (test_and_set_bit(__CTR_FLAG_JOURNAL_MODE, &rs->ctr_flags)) {
				rs->ti->error = "Only one raid4/5/6 'journal_mode' argument allowed";
				return -EINVAL;
			}
			r = dm_raid_journal_mode_to_md(arg);
			if (r < 0) {
				rs->ti->error = "Invalid 'journal_mode' argument";
				return r;
			}
			rs->journal_dev.mode = r;
			continue;
		}

		/*
		 * Parameters with number values from here on.
		 */
		if (kstrtoint(arg, 10, &value) < 0) {
			rs->ti->error = "Bad numerical argument given in raid params";
			return -EINVAL;
		}

		if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_REBUILD))) {
			/*
			 * "rebuild" is being passed in by userspace to provide
			 * indexes of replaced devices and to set up additional
			 * devices on raid level takeover.
			 */
			if (!__within_range(value, 0, rs->raid_disks - 1)) {
				rs->ti->error = "Invalid rebuild index given";
				return -EINVAL;
			}

			if (test_and_set_bit(value, (void *) rs->rebuild_disks)) {
				rs->ti->error = "rebuild for this index already given";
				return -EINVAL;
			}

			rd = rs->dev + value;
			clear_bit(In_sync, &rd->rdev.flags);
			clear_bit(Faulty, &rd->rdev.flags);
			rd->rdev.recovery_offset = 0;
			set_bit(__CTR_FLAG_REBUILD, &rs->ctr_flags);
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_WRITE_MOSTLY))) {
			if (!rt_is_raid1(rt)) {
				rs->ti->error = "write_mostly option is only valid for RAID1";
				return -EINVAL;
			}

			if (!__within_range(value, 0, rs->md.raid_disks - 1)) {
				rs->ti->error = "Invalid write_mostly index given";
				return -EINVAL;
			}

			write_mostly++;
			set_bit(WriteMostly, &rs->dev[value].rdev.flags);
			set_bit(__CTR_FLAG_WRITE_MOSTLY, &rs->ctr_flags);
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_MAX_WRITE_BEHIND))) {
			if (!rt_is_raid1(rt)) {
				rs->ti->error = "max_write_behind option is only valid for RAID1";
				return -EINVAL;
			}

			if (test_and_set_bit(__CTR_FLAG_MAX_WRITE_BEHIND, &rs->ctr_flags)) {
				rs->ti->error = "Only one max_write_behind argument pair allowed";
				return -EINVAL;
			}

			/*
			 * In device-mapper, we specify things in sectors, but
			 * MD records this value in kB
			 */
			if (value < 0 || value / 2 > COUNTER_MAX) {
				rs->ti->error = "Max write-behind limit out of range";
				return -EINVAL;
			}

			rs->md.bitmap_info.max_write_behind = value / 2;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_DAEMON_SLEEP))) {
			if (test_and_set_bit(__CTR_FLAG_DAEMON_SLEEP, &rs->ctr_flags)) {
				rs->ti->error = "Only one daemon_sleep argument pair allowed";
				return -EINVAL;
			}
			if (value < 0) {
				rs->ti->error = "daemon sleep period out of range";
				return -EINVAL;
			}
			rs->md.bitmap_info.daemon_sleep = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_DATA_OFFSET))) {
			/* Userspace passes new data_offset after having extended the the data image LV */
			if (test_and_set_bit(__CTR_FLAG_DATA_OFFSET, &rs->ctr_flags)) {
				rs->ti->error = "Only one data_offset argument pair allowed";
				return -EINVAL;
			}
			/* Ensure sensible data offset */
			if (value < 0 ||
			    (value && (value < MIN_FREE_RESHAPE_SPACE || value % to_sector(PAGE_SIZE)))) {
				rs->ti->error = "Bogus data_offset value";
				return -EINVAL;
			}
			rs->data_offset = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_DELTA_DISKS))) {
			/* Define the +/-# of disks to add to/remove from the given raid set */
			if (test_and_set_bit(__CTR_FLAG_DELTA_DISKS, &rs->ctr_flags)) {
				rs->ti->error = "Only one delta_disks argument pair allowed";
				return -EINVAL;
			}
			/* Ensure MAX_RAID_DEVICES and raid type minimal_devs! */
			if (!__within_range(abs(value), 1, MAX_RAID_DEVICES - rt->minimal_devs)) {
				rs->ti->error = "Too many delta_disk requested";
				return -EINVAL;
			}

			rs->delta_disks = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_STRIPE_CACHE))) {
			if (test_and_set_bit(__CTR_FLAG_STRIPE_CACHE, &rs->ctr_flags)) {
				rs->ti->error = "Only one stripe_cache argument pair allowed";
				return -EINVAL;
			}

			if (!rt_is_raid456(rt)) {
				rs->ti->error = "Inappropriate argument: stripe_cache";
				return -EINVAL;
			}

			if (value < 0) {
				rs->ti->error = "Bogus stripe cache entries value";
				return -EINVAL;
			}
			rs->stripe_cache_entries = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_MIN_RECOVERY_RATE))) {
			if (test_and_set_bit(__CTR_FLAG_MIN_RECOVERY_RATE, &rs->ctr_flags)) {
				rs->ti->error = "Only one min_recovery_rate argument pair allowed";
				return -EINVAL;
			}

			if (value < 0) {
				rs->ti->error = "min_recovery_rate out of range";
				return -EINVAL;
			}
			rs->md.sync_speed_min = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_MAX_RECOVERY_RATE))) {
			if (test_and_set_bit(__CTR_FLAG_MAX_RECOVERY_RATE, &rs->ctr_flags)) {
				rs->ti->error = "Only one max_recovery_rate argument pair allowed";
				return -EINVAL;
			}

			if (value < 0) {
				rs->ti->error = "max_recovery_rate out of range";
				return -EINVAL;
			}
			rs->md.sync_speed_max = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_REGION_SIZE))) {
			if (test_and_set_bit(__CTR_FLAG_REGION_SIZE, &rs->ctr_flags)) {
				rs->ti->error = "Only one region_size argument pair allowed";
				return -EINVAL;
			}

			region_size = value;
			rs->requested_bitmap_chunk_sectors = value;
		} else if (!strcasecmp(key, dm_raid_arg_name_by_flag(CTR_FLAG_RAID10_COPIES))) {
			if (test_and_set_bit(__CTR_FLAG_RAID10_COPIES, &rs->ctr_flags)) {
				rs->ti->error = "Only one raid10_copies argument pair allowed";
				return -EINVAL;
			}

			if (!__within_range(value, 2, rs->md.raid_disks)) {
				rs->ti->error = "Bad value for 'raid10_copies'";
				return -EINVAL;
			}

			raid10_copies = value;
		} else {
			DMERR("Unable to parse RAID parameter: %s", key);
			rs->ti->error = "Unable to parse RAID parameter";
			return -EINVAL;
		}
	}

	if (test_bit(__CTR_FLAG_SYNC, &rs->ctr_flags) &&
	    test_bit(__CTR_FLAG_NOSYNC, &rs->ctr_flags)) {
		rs->ti->error = "sync and nosync are mutually exclusive";
		return -EINVAL;
	}

	if (test_bit(__CTR_FLAG_REBUILD, &rs->ctr_flags) &&
	    (test_bit(__CTR_FLAG_SYNC, &rs->ctr_flags) ||
	     test_bit(__CTR_FLAG_NOSYNC, &rs->ctr_flags))) {
		rs->ti->error = "sync/nosync and rebuild are mutually exclusive";
		return -EINVAL;
	}

	if (write_mostly >= rs->md.raid_disks) {
		rs->ti->error = "Can't set all raid1 devices to write_mostly";
		return -EINVAL;
	}

	if (rs->md.sync_speed_max &&
	    rs->md.sync_speed_min > rs->md.sync_speed_max) {
		rs->ti->error = "Bogus recovery rates";
		return -EINVAL;
	}

	if (validate_region_size(rs, region_size))
		return -EINVAL;

	if (rs->md.chunk_sectors)
		max_io_len = rs->md.chunk_sectors;
	else
		max_io_len = region_size;

	if (dm_set_target_max_io_len(rs->ti, max_io_len))
		return -EINVAL;

	if (rt_is_raid10(rt)) {
		if (raid10_copies > rs->md.raid_disks) {
			rs->ti->error = "Not enough devices to satisfy specification";
			return -EINVAL;
		}

		rs->md.new_layout = raid10_format_to_md_layout(rs, raid10_format, raid10_copies);
		if (rs->md.new_layout < 0) {
			rs->ti->error = "Error getting raid10 format";
			return rs->md.new_layout;
		}

		rt = get_raid_type_by_ll(10, rs->md.new_layout);
		if (!rt) {
			rs->ti->error = "Failed to recognize new raid10 layout";
			return -EINVAL;
		}

		if ((rt->algorithm == ALGORITHM_RAID10_DEFAULT ||
		     rt->algorithm == ALGORITHM_RAID10_NEAR) &&
		    test_bit(__CTR_FLAG_RAID10_USE_NEAR_SETS, &rs->ctr_flags)) {
			rs->ti->error = "RAID10 format 'near' and 'raid10_use_near_sets' are incompatible";
			return -EINVAL;
		}
	}

	rs->raid10_copies = raid10_copies;

	/* Assume there are no metadata devices until the drives are parsed */
	rs->md.persistent = 0;
	rs->md.external = 1;

	/* Check, if any invalid ctr arguments have been passed in for the raid level */
	return rs_check_for_valid_flags(rs);
}