Files
s390-tools/zdev/src/dasd.c
Vineeth Vijayan 198c9fc052 zdev: onboard --site option on zdev commands
In some high-availability setups, root and boot disks of a Linux
installation are copied to remote sites that can take over execution
of a Linux workload in case of an outage of the original site.
A site in this context is an execution environment such as an LPAR
or z/VM guest.

Each site may provide a different set of devices, or require different
parameters to be applied per device. chzdev supports site-specific
device configuration for up to 10 sites. Each site is identified by
a number in the range 0 to 9. You can provide a separate set of persistent
device configuration for each site. The IPL Load parameter value specified
during boot determines the currently active site. Only the device
configuration of the active site is applied during boot and when new
devices become available. A common configuration can be provided that is
applied when no site-specific configuration is available for a device in
the active site.

User can use the --site parameter to configure devices for a specific
site only. Configuration actions without a site parameter apply to the
common configuration.

During the boot, the active site configuration is chosed based on the
IPL LOADPARM variable. If the LOADPARM contains no valid site-id (0 to 9),
the fallback-site id will be used. The fallback-site-id can also be
configured with the zdev tools by not specifying the --site parameter.

Some of the new chzdev/lszdev commands with site-parameter can be as
follows

1. chzdev -ep f001 --site 3

This command is used to configure the device f001 for the site 3. During
boot, if the LOADPARM value is S3, this device configuration will be
used.

2. lszdev --site 3

This command lists all the devices which are configured for site 3.

3. chzdev -ep f001

Above command does not have a --site parameter. This device settings
will be used for the following conditions

1. When the loadparm specifies a site, (e.g S3), and the device does not
   have an associated config-set, zdev uses fallback config set.
2. When the loadparm does not specify any site information, then the
   fallback config set will be used.

When the loadparm specifies a site, and the device does not have an
associated config set, nor a fallback site, nothing is configured for this
device.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:38:53 +01:00

711 lines
19 KiB
C

/*
* zdev - Modify and display the persistent configuration of devices
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attrib.h"
#include "ccw.h"
#include "dasd.h"
#include "device.h"
#include "devtype.h"
#include "internal.h"
#include "misc.h"
#include "modprobe.h"
#include "module.h"
#include "path.h"
#include "setting.h"
#include "udev.h"
#define DASD_MOD_NAME "dasd_mod"
#define DASD_ECKD_MOD_NAME "dasd_eckd_mod"
#define DASD_FBA_MOD_NAME "dasd_fba_mod"
#define DASD_DIAG_MOD_NAME "dasd_diag_mod"
/* Subtype specific data. */
struct dasd_subtype_data {
const char *ccwdrv;
const char *module;
};
/* List of dependent modules. */
static const char *dasd_mods[] = {
DASD_ECKD_MOD_NAME,
DASD_FBA_MOD_NAME,
DASD_DIAG_MOD_NAME,
NULL,
};
/*
* DASD type attributes.
*/
static struct attrib dasd_tattr_autodetect = {
.name = "autodetect",
.title = "Enable automatic DASD activation",
.desc =
"Control whether the DASD driver automatically enables all detected\n"
"DASDs:\n"
" 0: Only configured DASDs are enabled\n"
" 1: All detected DASDs are automatically enabled\n",
.defval = "0",
.activerem = 1,
.defunset = 1,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_tattr_probeonly = {
.name = "probeonly",
.title = "Inhibit access to DASD device nodes",
.desc =
"Control whether the DASD driver allows access to DASD devices:\n"
" 0: All enabled DASDs can be accessed normally\n"
" 1: Reject any attempt to open DASD devices\n",
.defval = "0",
.activerem = 1,
.defunset = 1,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_tattr_nopav = {
.name = "nopav",
.title = "Deactivate the Parallel Access Volume feature",
.desc = "Control the use of the Parallel Access Volume (PAV) feature:\n"
" 0: PAV is used when supported by the storage system\n"
" 1: The use of PAV is suppressed when running in LPAR\n",
.defval = "0",
.activerem = 1,
.defunset = 1,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_tattr_nofcx = {
.name = "nofcx",
.title = "Deactivate the High Performance FICON feature",
.desc = "Control the use of the High Performance FICON (HPF) feature:\n"
" 0: HPF is used when supported by the storage hardware\n"
" 1: HPF is not used\n",
.defval = "0",
.activerem = 1,
.defunset = 1,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_tattr_eer_pages = {
.name = "eer_pages",
.title = "Modify buffer size for error records",
.desc =
"Control the number of 4 KB pages that are used for internal "
"buffering\n"
"of error records generated by the Extended Error Reporting (EER)\n"
"feature.\n",
.defval = "5",
.activerem = 1,
.nounload = 1,
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)),
};
/*
* DASD device attributes.
*/
static struct attrib dasd_attr_failfast = {
.name = "failfast",
.title = "Modify error recovery in no-path scenario",
.desc =
"Control I/O handling when all paths to a DASD have been lost:\n"
" 0: Queue I/O until path becomes available\n"
" 1: Fail I/O request immediately\n",
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_attr_readonly = {
.name = "readonly",
.title = "Inhibit write access to DASD",
.desc = "Control the DASD driver read-only setting for a DASD:\n"
" 0: Allow writing to the device\n"
" 1: Deny writing to the device\n",
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_attr_erplog = {
.name = "erplog",
.title = "Enable logging of Error Recovery Processing",
.desc = "Control logging of Error Recovery Processing (ERP), such as\n"
"failing channel programs:\n"
" 0: ERP logging is disabled\n"
" 1: ERP logging is enabled\n",
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static int dasd_raw_diag_order_cmp(struct setting *, struct setting *);
static bool dasd_raw_diag_check(struct setting *, struct setting *, config_t);
static struct attrib dasd_attr_use_diag = {
.name = "use_diag",
.title = "Activate z/VM hypervisor assisted I/O processing",
.desc =
"Control I/O access mode for a DASD:\n"
" 0: I/O is performed using standard channel programs\n"
" 1: I/O is performed using the z/VM DIAGNOSE X'250' interface\n"
"\n"
"The DIAGNOSE X'250' access mode works only when:\n"
" - Running Linux as z/VM guest\n"
" - Using devices formatted with consistent block sizes, such as\n"
" ECKD DASDs with LDL or CMS format, or FBA devices\n",
.order_cmp = dasd_raw_diag_order_cmp,
.check = dasd_raw_diag_check,
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_attr_raw_track_access = {
.name = "raw_track_access",
.title = "Enable access to ECKD track metadata",
.desc =
"Control whether the DASD driver provides access to full ECKD tracks,\n"
"including ECKD-specific meta-data:\n"
" 0: Only access the data portion of ECKD tracks\n"
" 1: Access the full track\n",
.order_cmp = dasd_raw_diag_order_cmp,
.check = dasd_raw_diag_check,
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
/* Ensure ordering if both use_diag and raw_track_access are modified. */
static int dasd_raw_diag_order_cmp(struct setting *a, struct setting *b)
{
int a_val, b_val;
if ((a->attrib == &dasd_attr_use_diag &&
b->attrib == &dasd_attr_raw_track_access) ||
(a->attrib == &dasd_attr_raw_track_access &&
b->attrib == &dasd_attr_use_diag)) {
/* Define order to ensure that =0 is done before =1. */
a_val = atoi(a->value);
b_val = atoi(b->value);
if (a_val == 1 && b_val == 0)
return 1;
if (a_val == 0 && b_val == 1)
return -1;
}
return ccw_offline_only_order_cmp(a, b);
}
/* Conflict if both use_diag and raw_track_access are set. */
static bool dasd_raw_diag_check(struct setting *a, struct setting *b,
config_t config)
{
if ((a->attrib == &dasd_attr_use_diag &&
b->attrib == &dasd_attr_raw_track_access) ||
(a->attrib == &dasd_attr_raw_track_access &&
b->attrib == &dasd_attr_use_diag)) {
if (atoi(a->value) == 1 && atoi(b->value) == 1)
return false;
}
return ccw_offline_only_check(a, b, config);
}
static struct attrib dasd_attr_eer_enabled = {
.name = "eer_enabled",
.title = "Enable Extended Error Reporting",
.desc =
"Control the Extended Error Reporting (EER) feature for a DASD:\n"
" 0: EER is disabled\n"
" 1: EER is enabled\n",
.order_cmp = ccw_online_only_order_cmp,
.check = ccw_online_only_check,
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_attr_expires = {
.name = "expires",
.title = "Modify I/O operation timeout",
.desc =
"Specify the time in seconds that the DASD driver waits for the\n"
"completion of a single I/O operation before considering that I/O\n"
"operation to have failed.\n"
"\n"
"The default value depends on the DASD type.\n",
.order_cmp = ccw_online_only_order_cmp,
.check = ccw_online_only_check,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(1, 40000000)),
};
static struct attrib dasd_attr_retries = {
.name = "retries",
.title = "Modify I/O operation retry counter",
.desc =
"Specify the number of times that a failed I/O should be retried\n"
"before reporting it as failed.\n"
"\n"
"The default value is dependent on the DASD type.\n",
.order_cmp = ccw_online_only_order_cmp,
.check = ccw_online_only_check,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 32768)),
};
static struct attrib dasd_attr_timeout = {
.name = "timeout",
.title = "Modify I/O request timeout",
.desc =
"Specify the total time in seconds that the Linux block device layer\n"
"waits for the completion of an I/O request issued to the DASD driver\n"
"before considering that I/O to have failed. Specify 0 to deactivate\n"
"timeout handling.\n",
.order_cmp = ccw_online_only_order_cmp,
.check = ccw_online_only_check,
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(0)),
};
static struct attrib dasd_attr_reservation_policy = {
.name = "reservation_policy",
.title = "Modify lost device reservation behavior",
.desc =
"Control the DASD driver behavior if an existing DASD reservation of\n"
"this system for a DASD is lost:\n"
" ignore: I/O operations are blocked until the external reservation\n"
" is released (default)\n"
" fail: All I/O operations are considered to have failed\n",
.defval = "ignore",
.accept = ACCEPT_ARRAY(ACCEPT_STR("ignore"), ACCEPT_STR("fail")),
};
static struct attrib dasd_attr_last_known_reservation_state = {
.name = "last_known_reservation_state",
.title = "Display and reset driver device reservation view",
.desc =
"Display the DASD driver's view of device reservations held by this\n"
"system:\n"
" none: No reservation held or no information available\n"
" reserved: Device is assumed to be reserved by this system\n"
" lost: A reservation held by this system was lost\n"
"\n"
"To reset the reservation state from 'lost' to 'none', set this\n"
"attribute to 'reset'.\n",
.unstable = 1,
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
static struct attrib dasd_attr_safe_offline = {
.name = "safe_offline",
.title = "Deactivate DASD after processing pending I/Os",
.desc = "Write an arbitrary value to this attribute to attempt to set\n"
"the DASD offline after all outstanding I/O requests have\n"
"been processed.\n",
.activeonly = 1,
.writeonly = 1,
};
static struct attrib dasd_attr_fc_security = {
.name = "fc_security",
.title = "Show FC Endpoint Security state of DASD device",
.desc =
"This read-only attribute shows the Fibre Channel Endpoint Security\n"
"status of the connection to the DASD device:\n"
" Unsupported : The DASD device does not support Fibre Channel\n"
" Endpoint Security\n"
" Inconsistent : The operational channel paths of the DASD device\n"
" report inconsistent Fibre Channel Endpoint\n"
" Security status\n"
" Authentication: The connection has been authenticated\n"
" Encryption : The connection is encrypted\n",
.readonly = 1,
};
/*
* DASD subtype methods.
*/
static bool _check_use_diag(struct setting_list *list)
{
struct setting *u;
u = setting_list_find(list, dasd_attr_use_diag.name);
if (u && u->modified) {
if (u->value && atoi(u->value) == 1) {
delayed_warn("Cannot set 'use_diag=1' on non-z/VM system\n");
return false;
}
}
return true;
}
/* Check if use_diag setting can be correctly applied. */
static exit_code_t check_use_diag(struct device *dev, config_t config)
{
if (is_zvm())
return EXIT_OK;
if (SCOPE_ACTIVE(config) && !_check_use_diag(dev->active.settings))
return EXIT_INVALID_CONFIG;
if (SCOPE_PERSISTENT(config) &&
!_check_use_diag(dev->persistent.settings))
return EXIT_INVALID_CONFIG;
if (SCOPE_AUTOCONF(config) &&
!_check_use_diag(dev->autoconf.settings))
return EXIT_INVALID_CONFIG;
return EXIT_OK;
}
static exit_code_t dasd_st_check_pre_configure(struct subtype *st,
struct device *dev,
int prereq, config_t config)
{
exit_code_t rc;
if (dev->active.deconfigured)
return EXIT_OK;
rc = check_use_diag(dev, config);
if (rc)
return rc;
return EXIT_OK;
}
static void dasd_st_add_modules(struct subtype *st, struct device *dev,
struct util_list *modules)
{
int changed, aset, pset;
/* Add main module. */
st->super->add_modules(st, dev, modules);
/* Add dasd_diag_mod if use_diag is set. */
setting_list_get_bool_state(dev->active.settings,
dasd_attr_use_diag.name, &changed, &aset);
setting_list_get_bool_state(dev->persistent.settings,
dasd_attr_use_diag.name, &changed, &pset);
setting_list_get_bool_state(dev->autoconf.settings,
dasd_attr_use_diag.name, &changed, &pset);
if (aset || pset)
strlist_add_unique(modules, DASD_DIAG_MOD_NAME);
}
/*
* DASD methods.
*/
/* Clean up all resources used by devtype object. */
static void dasd_devtype_exit(struct devtype *dt)
{
setting_list_free(dt->active_settings);
setting_list_free(dt->persistent_settings);
}
/* Split a dasd= module parameter string into a setting_list, based on
* known attributes encoded in dasd= parameters. */
static void split_dasd(struct devtype *dt, const char *str,
struct setting_list *list)
{
char *copy, *curr, *next, *dasd_str;
struct attrib *a;
struct util_list *dasd;
dasd = strlist_new();
copy = misc_strdup(str);
next = copy;
while ((curr = strsep(&next, ","))) {
if (strcmp(curr, "(null)") == 0)
continue;
if (*curr == 0) {
/* Handle dasd="" */
continue;
}
/* Check for known attributes. */
a = attrib_find(dt->type_attribs, curr);
if (a) {
/* Currently only boolean attributes implemented. */
setting_list_apply_actual(list, a, curr, "1");
continue;
}
/* An unknown setting or device spec - leave in dasd=. */
strlist_add(dasd, "%s", curr);
}
free(copy);
/* Add dasd= setting if necessary. */
if (!util_list_is_empty(dasd)) {
dasd_str = strlist_flatten(dasd, ",");
setting_list_apply_actual(list, NULL, "dasd", dasd_str);
free(dasd_str);
}
strlist_free(dasd);
}
/* Convert a module parameter list into a devtype settings list. */
static void convert_params_to_settings(struct devtype *dt,
struct setting_list *from,
struct setting_list *to)
{
struct setting *s;
util_list_iterate(&from->list, s) {
if (strcmp(s->name, "dasd") == 0) {
/* Special handling: split known attribs out. */
split_dasd(dt, s->value, to);
continue;
}
/* Just copy remaining attributes. */
setting_list_apply_actual(to, s->attrib, s->name, s->value);
}
}
/* Convert a devtype settings list to a module parameter list. */
static void convert_settings_to_params(struct devtype *dt,
struct setting_list *from,
struct setting_list **to)
{
struct setting *s_from, *s_to;
struct util_list *dasd;
int dasd_mod;
char *flat;
*to = setting_list_new();
dasd = strlist_new();
dasd_mod = 0;
util_list_iterate(&from->list, s_from) {
if (s_from->removed)
continue;
if (s_from->derived && !s_from->modified)
continue;
if (strcmp(s_from->name, "eer_pages") == 0) {
/* Copy stand-alone parameter. */
setting_list_add(*to, setting_copy(s_from));
continue;
}
/* All other settings need to go into dasd=. */
if (strcmp(s_from->name, "dasd") == 0) {
/* Just add explicit dasd= content. */
strlist_add(dasd, "%s", s_from->value);
} else if (strcmp(s_from->value, "0") == 0) {
/* A flag in dasd= counts as set, so skip the unset
* ones. */
continue;
} else {
/* Just add the name for set attributes. */
strlist_add(dasd, "%s", s_from->name);
dasd_mod += s_from->modified;
}
}
flat = strlist_flatten(dasd, ",");
s_to = setting_new(NULL, "dasd", flat);
free(flat);
if (dasd_mod > 0)
s_to->modified = 1;
if (util_list_is_empty(dasd))
s_to->removed = 1;
setting_list_add(*to, s_to);
strlist_free(dasd);
}
static exit_code_t dasd_devtype_read_settings(struct devtype *dt,
config_t config)
{
struct setting_list *list;
char *path;
exit_code_t rc = EXIT_OK;
if (SCOPE_ACTIVE(config) && !dt->active_settings) {
dt->active_exists = 0;
rc = module_get_params(DASD_MOD_NAME, dt->type_attribs, &list);
if (rc)
return rc;
dt->active_settings = setting_list_new();
if (list) {
convert_params_to_settings(dt, list,
dt->active_settings);
setting_list_mark_default_derived(dt->active_settings);
setting_list_apply_defaults(dt->active_settings,
dt->type_attribs, false);
setting_list_free(list);
dt->active_exists = 1;
}
}
if (SCOPE_PERSISTENT(config) && !dt->persistent_settings) {
dt->persistent_exists = 0;
path = path_get_modprobe_conf(dt);
rc = modprobe_read_settings(path, DASD_MOD_NAME,
dt->type_attribs, &list);
free(path);
if (rc)
return rc;
dt->persistent_settings = setting_list_new();
if (list) {
convert_params_to_settings(dt, list,
dt->persistent_settings);
setting_list_apply_defaults(dt->persistent_settings,
dt->type_attribs, false);
setting_list_free(list);
dt->persistent_exists = 1;
}
}
return rc;
}
static exit_code_t dasd_devtype_write_settings(struct devtype *dt,
config_t config)
{
struct setting_list *list;
char *path;
exit_code_t rc = EXIT_OK;
if (SCOPE_ACTIVE(config) && dt->active_settings) {
/* Try setting parameters directly via Sysfs. */
if (module_set_params(DASD_MOD_NAME, dt->active_settings))
goto persistent;
convert_settings_to_params(dt, dt->active_settings, &list);
rc = module_load(DASD_MOD_NAME, dasd_mods, list,
err_delayed_print);
setting_list_free(list);
if (rc)
return rc;
}
persistent:
if (SCOPE_PERSISTENT(config) && dt->persistent_settings) {
path = path_get_modprobe_conf(dt);
if (!rc) {
convert_settings_to_params(dt, dt->persistent_settings,
&list);
rc = modprobe_write_settings(path, DASD_MOD_NAME, list);
setting_list_free(list);
}
free(path);
}
return rc;
}
/*
* DASD device sub-types.
*/
static struct ccw_subtype_data dasd_eckd_data = {
.ccwdrv = "dasd-eckd",
.mod = "dasd_eckd_mod",
};
struct subtype dasd_subtype_eckd = {
.super = &ccw_subtype,
.devtype = &dasd_devtype,
.name = "dasd-eckd",
.title = "Enhanced Count Key Data (ECKD) DASDs",
.devname = "ECKD DASD",
.modules = STRING_ARRAY(DASD_ECKD_MOD_NAME),
.namespace = &ccw_namespace,
.data = &dasd_eckd_data,
.dev_attribs = ATTRIB_ARRAY(
&ccw_attr_online_force,
&ccw_attr_cmb_enable,
&dasd_attr_failfast,
&dasd_attr_readonly,
&dasd_attr_erplog,
&dasd_attr_use_diag,
&dasd_attr_raw_track_access,
&dasd_attr_eer_enabled,
&dasd_attr_expires,
&dasd_attr_retries,
&dasd_attr_timeout,
&dasd_attr_reservation_policy,
&dasd_attr_last_known_reservation_state,
&dasd_attr_safe_offline,
&dasd_attr_fc_security,
&internal_attr_early,
),
.unknown_dev_attribs = 1,
.check_pre_configure = &dasd_st_check_pre_configure,
.add_modules = &dasd_st_add_modules,
};
static struct ccw_subtype_data dasd_fba_data = {
.ccwdrv = "dasd-fba",
.mod = "dasd_fba_mod",
};
struct subtype dasd_subtype_fba = {
.super = &ccw_subtype,
.devtype = &dasd_devtype,
.name = "dasd-fba",
.title = "Fixed Block Architecture (FBA) DASDs",
.devname = "FBA DASD",
.modules = STRING_ARRAY(DASD_FBA_MOD_NAME),
.namespace = &ccw_namespace,
.data = &dasd_fba_data,
.dev_attribs = ATTRIB_ARRAY(
&ccw_attr_online_force,
&ccw_attr_cmb_enable,
&dasd_attr_failfast,
&dasd_attr_readonly,
&dasd_attr_erplog,
&dasd_attr_use_diag,
&dasd_attr_expires,
&dasd_attr_retries,
&dasd_attr_timeout,
&dasd_attr_reservation_policy,
&dasd_attr_last_known_reservation_state,
&dasd_attr_safe_offline,
&internal_attr_early,
),
.unknown_dev_attribs = 1,
.check_pre_configure = &dasd_st_check_pre_configure,
.add_modules = &dasd_st_add_modules,
};
/*
* DASD device type.
*/
struct devtype dasd_devtype = {
.name = "dasd",
.title = "FICON-attached Direct Access Storage Devices "
"(DASDs)",
.devname = "DASD",
.modules = STRING_ARRAY(DASD_MOD_NAME),
.site_support = 1,
.subtypes = SUBTYPE_ARRAY(
&dasd_subtype_eckd,
&dasd_subtype_fba,
),
.type_attribs = ATTRIB_ARRAY(
&dasd_tattr_autodetect,
&dasd_tattr_probeonly,
&dasd_tattr_nopav,
&dasd_tattr_nofcx,
&dasd_tattr_eer_pages,
),
.exit = &dasd_devtype_exit,
.read_settings = &dasd_devtype_read_settings,
.write_settings = &dasd_devtype_write_settings,
};