mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
zipl_helper.device-mapper: add support for 'raid' device-mapper target
Introduce target operations (struct target_ops); For each supported device-mapper target implement . check_target_status() . get_target_data() Add support for new 'raid' device-mapper target, which is now default in LVM. Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com> Reviewed-by: Stefan Haberland <sth@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
committed by
Steffen Eiden
parent
3296d85e35
commit
0e69dc1262
@@ -82,6 +82,8 @@
|
||||
fprintf(stderr, "Error: " __VA_ARGS__)
|
||||
|
||||
static bool fail_on_dm_mirrors;
|
||||
static struct target_ops *target_ops_by_type(int target_type);
|
||||
static struct target_ops *find_target_ops(char *id);
|
||||
|
||||
struct target_status {
|
||||
char *path;
|
||||
@@ -103,6 +105,13 @@ struct target {
|
||||
struct util_list_node list;
|
||||
};
|
||||
|
||||
struct target_ops {
|
||||
int type;
|
||||
const char *id;
|
||||
int (*check_target_status)(const char *devname);
|
||||
struct util_list *(*get_target_data)(const char *devname, char *args);
|
||||
};
|
||||
|
||||
/* "extended" device */
|
||||
struct ext_dev {
|
||||
dev_t dev;
|
||||
@@ -184,7 +193,9 @@ enum dev_type {
|
||||
enum target_type {
|
||||
TARGET_TYPE_LINEAR = 0,
|
||||
TARGET_TYPE_MIRROR,
|
||||
TARGET_TYPE_MULTIPATH
|
||||
TARGET_TYPE_MULTIPATH,
|
||||
TARGET_TYPE_RAID,
|
||||
LAST_TARGET_TYPE
|
||||
};
|
||||
|
||||
enum lookup_result {
|
||||
@@ -195,6 +206,31 @@ enum lookup_result {
|
||||
DM_MULTIPLE_TARGETS
|
||||
};
|
||||
|
||||
/**
|
||||
* Issue an error message about THIS bad property followed by the FIXUP
|
||||
*/
|
||||
static void print_bad_raid_state(const char *this, const char *fixup,
|
||||
const char *devname)
|
||||
{
|
||||
fprintf(stderr, "%s: Inconsistent RAID state (%s)\n",
|
||||
devname, this);
|
||||
fprintf(stderr, "%s\n",
|
||||
fixup ? fixup : "Bring it into consistent state first");
|
||||
}
|
||||
|
||||
/**
|
||||
* If STATE contains THIS property, then fail with the proposed FIXUP
|
||||
*/
|
||||
static int error_on(const char *state, const char *this, const char *fixup,
|
||||
const char *devname)
|
||||
{
|
||||
if (strstr(state, this)) {
|
||||
print_bad_raid_state(this, fixup, devname);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_device_mapper(unsigned int major)
|
||||
{
|
||||
return major == DEVICE_MAPPER_MAJOR;
|
||||
@@ -540,11 +576,8 @@ static int check_mirror_status(const char *devname)
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (status[i] != 'A') {
|
||||
fprintf(stderr,
|
||||
"%s: Raid inconsistent(some mirrors are not 'alive and in-sync').\n",
|
||||
devname);
|
||||
fprintf(stderr,
|
||||
"Bring it into consistent state first.\n");
|
||||
print_bad_raid_state("some mirrors are not 'alive and in-sync'",
|
||||
NULL, devname);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -556,7 +589,10 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* The 'mirror' dm target is deprecated. Users are recommended to use 'raid'
|
||||
* target of 'raid1' type instead.
|
||||
*
|
||||
* There is no kernel documentation for the mirror target. Parameters obtained
|
||||
* from Linux sources: drivers/md/dm-log.c and drivers/md/dm-raid1.c
|
||||
*
|
||||
@@ -607,6 +643,116 @@ out2:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kernel documentation for 'raid' target:
|
||||
* https://www.kernel.org/doc/Documentation/device-mapper/dm-raid.txt
|
||||
*
|
||||
* The target is named "raid" and it accepts the following parameters:
|
||||
*
|
||||
* <raid_type> <#raid_params> <raid_params> \
|
||||
* <#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>]
|
||||
*/
|
||||
struct util_list *get_raid_data(const char *devname, char *args)
|
||||
{
|
||||
struct util_list *data = util_list_new(struct target_data, list);
|
||||
long nparm, ndevs;
|
||||
char *type;
|
||||
|
||||
STR_TOKEN_OR_GOTO(args, type, out);
|
||||
if (strcmp(type, "raid1") != 0) {
|
||||
ERR("Unsupported raid type %s (only 'raid1' is supported)\n",
|
||||
type);
|
||||
goto out;
|
||||
}
|
||||
NEXT_INT_TOKEN_OR_GOTO(nparm, out); /* #raid_params */
|
||||
SKIP_NEXT_TOKENS_OR_GOTO(nparm, out); /* raid_params */
|
||||
NEXT_INT_TOKEN_OR_GOTO(ndevs, out);
|
||||
|
||||
for (; ndevs > 0; ndevs--) {
|
||||
unsigned int major, minor;
|
||||
char *name;
|
||||
|
||||
SKIP_NEXT_TOKEN_OR_GOTO(out); /* metadata device */
|
||||
NEXT_STR_TOKEN_OR_GOTO(name, out); /* data device */
|
||||
if (sscanf(name, "%u:%u", &major, &minor) < 2)
|
||||
goto out;
|
||||
util_list_add_tail(data,
|
||||
target_data_new(major, minor,
|
||||
0 /* offset */));
|
||||
}
|
||||
return data;
|
||||
out:
|
||||
ERR("Unrecognized device-mapper table format for device '%s'\n",
|
||||
devname);
|
||||
target_data_list_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* From https://www.kernel.org/doc/Documentation/device-mapper/dm-raid.txt
|
||||
* Status output:
|
||||
*
|
||||
* <s> <l> raid \
|
||||
* <raid_type> <#devices> <health_chars> \
|
||||
* <sync_ratio> <sync_action> <mismatch_cnt>
|
||||
*/
|
||||
static int check_raid_status(const char *devname)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t n = 0;
|
||||
int ret = -1;
|
||||
FILE *fp;
|
||||
|
||||
fp = exec_cmd_and_get_out_stream("dmsetup status /dev/%s 2>/dev/null",
|
||||
devname);
|
||||
if (fp == NULL) {
|
||||
ERR("Failed to get raid status\n");
|
||||
return -1;
|
||||
}
|
||||
while (getline(&line, &n, fp) != -1) {
|
||||
char *status = NULL;
|
||||
char *token = NULL;
|
||||
long cnt;
|
||||
int i;
|
||||
|
||||
/* Sample output (single line):
|
||||
*
|
||||
* 0 43261952 raid \
|
||||
* raid1 2 AA \
|
||||
* 43261952/43261952 \
|
||||
* idle 0 0 -
|
||||
*/
|
||||
STR_TOKEN_OR_GOTO(line, token, out);
|
||||
SKIP_NEXT_TOKEN_OR_GOTO(out); /* length */
|
||||
NEXT_STR_TOKEN_OR_GOTO(token, out); /* type */
|
||||
if (strcmp(token, "raid") != 0) {
|
||||
ERR("Unrecognized status for 'raid' target\n");
|
||||
goto out;
|
||||
}
|
||||
NEXT_STR_TOKEN_OR_GOTO(token, out); /* type */
|
||||
if (strcmp(token, "raid1") != 0) {
|
||||
ERR("Unrecognized type (%s) of 'raid' target. Only 'raid1' is supported\n",
|
||||
token);
|
||||
goto out;
|
||||
}
|
||||
NEXT_INT_TOKEN_OR_GOTO(cnt, out); /* #nr_mirrors */
|
||||
NEXT_STR_TOKEN_OR_GOTO(status, out); /* status buffer */
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (status[i] != 'A') {
|
||||
print_bad_raid_state("some mirrors are not 'alive and in-sync'",
|
||||
NULL, devname);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
free(line);
|
||||
pclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct target_status *target_status_new(const char *path, char status)
|
||||
{
|
||||
struct target_status *ts = util_malloc(sizeof(struct target_status));
|
||||
@@ -855,7 +1001,7 @@ static int get_table(dev_t dev, struct util_list **table)
|
||||
char *type = NULL, *args = NULL;
|
||||
struct util_list *data = NULL;
|
||||
unsigned long start, length;
|
||||
unsigned short ttype;
|
||||
struct target_ops *tops;
|
||||
|
||||
if (sscanf(line, "%lu %lu %ms %m[a-zA-Z0-9_: -]",
|
||||
&start, &length, &type, &args) < 4) {
|
||||
@@ -863,25 +1009,19 @@ static int get_table(dev_t dev, struct util_list **table)
|
||||
devname);
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(type, "linear") == 0) {
|
||||
data = get_linear_data(devname, args);
|
||||
ttype = TARGET_TYPE_LINEAR;
|
||||
} else if (strcmp(type, "mirror") == 0) {
|
||||
data = get_mirror_data(devname, args);
|
||||
ttype = TARGET_TYPE_MIRROR;
|
||||
} else if (strcmp(type, "multipath") == 0) {
|
||||
data = get_multipath_data(devname, args);
|
||||
ttype = TARGET_TYPE_MULTIPATH;
|
||||
} else {
|
||||
tops = find_target_ops(type);
|
||||
if (!tops) {
|
||||
ERR("Unsupported setup: Unsupported device-mapper "
|
||||
"target type '%s' for device '%s'\n",
|
||||
type, devname);
|
||||
}
|
||||
data = tops->get_target_data(devname, args);
|
||||
free(type);
|
||||
free(args);
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
util_list_add_tail(*table, target_new(start, length, ttype, data));
|
||||
util_list_add_tail(*table,
|
||||
target_new(start, length, tops->type, data));
|
||||
}
|
||||
free(line);
|
||||
pclose(fp);
|
||||
@@ -938,6 +1078,11 @@ static int lookup_parent(dev_t device, struct util_list **table,
|
||||
return DM_SINGLE_TARGET;
|
||||
}
|
||||
|
||||
static int target_is_mirrored(struct target *t)
|
||||
{
|
||||
return t->type == TARGET_TYPE_MIRROR || t->type == TARGET_TYPE_RAID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting from DEVICE go upward the device tree and find the topmost
|
||||
* device, which is not a logical device managed by device-mapper driver.
|
||||
@@ -988,7 +1133,7 @@ static struct util_list *dmpath_walk(struct ext_dev *bottom, const char *dir,
|
||||
|
||||
target_move(target, &top, &table, dmpath);
|
||||
de = util_list_start(dmpath);
|
||||
if (de->target->type == TARGET_TYPE_MIRROR) {
|
||||
if (target_is_mirrored(de->target)) {
|
||||
if (*mirror || fail_on_dm_mirrors) {
|
||||
ERR("Unsupported setup: Nested mirrors");
|
||||
goto error;
|
||||
@@ -1217,13 +1362,17 @@ static int dm_dev_to_zipl_params(struct ext_dev *dev, char *dir)
|
||||
base_dev_to_params(base_dev, &pd.dc, pd.offset);
|
||||
|
||||
if (pd.mirror) {
|
||||
/* check mirror status */
|
||||
struct target *mt = pd.mirror->target;
|
||||
struct target_data *mtd = util_list_start(mt->data);
|
||||
char devname[BDEVNAME_SIZE];
|
||||
struct target_data *mtd;
|
||||
struct target_ops *tops;
|
||||
struct target *mt;
|
||||
|
||||
mt = pd.mirror->target;
|
||||
mtd = util_list_start(mt->data);
|
||||
tops = target_ops_by_type(mt->type);
|
||||
get_device_name(devname, pd.mirror->dev.dev);
|
||||
if (check_mirror_status(devname))
|
||||
|
||||
if (tops->check_target_status(devname))
|
||||
goto error;
|
||||
/* Print also parameters for other mirrors */
|
||||
|
||||
@@ -1419,13 +1568,12 @@ static int md_get_data_offset(unsigned long major, unsigned long minor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void md_print_bad_status(struct mdstat *md, const char *devname)
|
||||
static void md_print_status(struct mdstat *md, const char *devname)
|
||||
{
|
||||
fprintf(stderr, "Raid Devices: %lu\n", md->raid_devices);
|
||||
fprintf(stderr, "Active Devices: %lu\n", md->active_devices);
|
||||
fprintf(stderr, "Working Devices: %lu\n", md->working_devices);
|
||||
fprintf(stderr, "%s: RAID inconsistent (some its components are not active or working).\n",
|
||||
devname);
|
||||
fprintf(stderr, "%s: Raid status:\n", devname);
|
||||
fprintf(stderr, " Raid Devices: %lu\n", md->raid_devices);
|
||||
fprintf(stderr, " Active Devices: %lu\n", md->active_devices);
|
||||
fprintf(stderr, " Working Devices: %lu\n", md->working_devices);
|
||||
}
|
||||
|
||||
static int devname_by_device(dev_t device, char **devname)
|
||||
@@ -1540,33 +1688,23 @@ static int md_mirror_to_params(unsigned int major, unsigned int minor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If STATE contains THIS property, then issue an error message
|
||||
* with the proposed FIXUP
|
||||
*/
|
||||
static int error_on(const char *state, const char *this, const char *fixup,
|
||||
const char *devname)
|
||||
{
|
||||
if (strstr(state, this)) {
|
||||
fprintf(stderr, "%s: Inconsistent RAID state ('%s'). %s.\n",
|
||||
devname, this, fixup);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_md_state(struct mdstat *md, const char *devname)
|
||||
{
|
||||
if (error_on(md->state, "recovering", "Complete its recovery", devname))
|
||||
if (error_on(md->state, "recovering",
|
||||
"Complete its recovery first", devname))
|
||||
return -1;
|
||||
if (error_on(md->state, "degraded", "Restore its redundancy", devname))
|
||||
if (error_on(md->state, "degraded",
|
||||
"Restore its redundancy first", devname))
|
||||
return -1;
|
||||
if (error_on(md->state, "dirty", "Bring it into clean state\n", devname))
|
||||
if (error_on(md->state, "dirty",
|
||||
"Bring it into clean state first", devname))
|
||||
return -1;
|
||||
|
||||
if (md->raid_devices != md->active_devices ||
|
||||
md->raid_devices > md->working_devices) {
|
||||
md_print_bad_status(md, devname);
|
||||
md_print_status(md, devname);
|
||||
print_bad_raid_state("some mirrors are not active or working",
|
||||
NULL, devname);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -1732,6 +1870,47 @@ static int check_usage_chreipl_helper(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct target_ops target_ops[LAST_TARGET_TYPE] = {
|
||||
[TARGET_TYPE_LINEAR] = {
|
||||
. type = TARGET_TYPE_LINEAR,
|
||||
. id = "linear",
|
||||
. get_target_data = get_linear_data
|
||||
},
|
||||
[TARGET_TYPE_MIRROR] = {
|
||||
. type = TARGET_TYPE_MIRROR,
|
||||
. id = "mirror",
|
||||
. get_target_data = get_mirror_data,
|
||||
. check_target_status = check_mirror_status
|
||||
},
|
||||
[TARGET_TYPE_MULTIPATH] = {
|
||||
. type = TARGET_TYPE_MULTIPATH,
|
||||
. id = "multipath",
|
||||
. get_target_data = get_multipath_data
|
||||
},
|
||||
[TARGET_TYPE_RAID] = {
|
||||
. type = TARGET_TYPE_RAID,
|
||||
. id = "raid",
|
||||
. get_target_data = get_raid_data,
|
||||
. check_target_status = check_raid_status
|
||||
}
|
||||
};
|
||||
|
||||
static struct target_ops *target_ops_by_type(int target_type)
|
||||
{
|
||||
return &target_ops[target_type];
|
||||
}
|
||||
|
||||
static struct target_ops *find_target_ops(char *id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LAST_TARGET_TYPE; i++) {
|
||||
if (!strcmp(id, target_ops_by_type(i)->id))
|
||||
return target_ops_by_type(i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct helper helpers[LAST_DRIVER_ID][LAST_UTIL_ID] = {
|
||||
[DM_DRIVER_ID][ZIPL_UTIL_ID] = {
|
||||
. util_id = ZIPL_UTIL_ID,
|
||||
|
||||
Reference in New Issue
Block a user