mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Remove unused header files from these sources. This simplifies maintenance and slightly reduces compile time. Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com> Signed-off-by: Jan Polensky <japo@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
889 lines
25 KiB
C
889 lines
25 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 <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_copy_pair = {
|
||
.name = "copy_pair",
|
||
.title = "Modify copy-pair relations",
|
||
.desc = "Make a copy-pair relation for this device known to the DASD "
|
||
"driver.\n"
|
||
"A copy-pair is a comma-separated pair of device bus-IDs\n"
|
||
"<primary>,<secondary>.\n"
|
||
"Example: 0.0.1000,0.0.2000\n"
|
||
"Up to 4 copy-pairs are accepted by the DASD driver for each "
|
||
"device.\n",
|
||
.unstable = 1,
|
||
.multi = 1,
|
||
.activerem = 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,
|
||
};
|
||
|
||
static struct attrib dasd_attr_aq_mask = {
|
||
.name = "aq_mask",
|
||
.title = "Specify autoquiesce triggers",
|
||
.desc =
|
||
"Use the aq_mask attribute to automatically quiesce a device and block\n"
|
||
"new I/O after certain events.\n"
|
||
"\n"
|
||
"The value is a bitmask in decimal or hexadecimal format where each set bit\n"
|
||
"indicates that the associated event shown in the table below triggers an\n"
|
||
"autoquiesce.\n"
|
||
" Bit 0 is not used.\n"
|
||
" 1 - 0x02 - A terminal I/O error occurred\n"
|
||
" 2 - 0x04 - No active channel paths remain for the device\n"
|
||
" 3 - 0x08 - A state change interrupt occurred\n"
|
||
" 4 - 0x10 - The device is PPRC suspended\n"
|
||
" 5 - 0x20 - No space is left on an ESE device\n"
|
||
" 6 - 0x40 – The number of timeouts specified in aq_timeouts is reached\n"
|
||
" 7 - 0x80 - I/O was not started because of an error in the start function\n"
|
||
"\n"
|
||
"For example bits 1,3 and 5 set (0010 1010) lead to an integer value of 42\n"
|
||
"or 0x2A.\n"
|
||
"An integer value of 0 turns off the autoquiesce function.\n",
|
||
.order_cmp = ccw_online_only_order_cmp,
|
||
.check = ccw_online_only_check,
|
||
.defval = "0",
|
||
/*
|
||
* Currently only 8 bits are defined and the max value is 255.
|
||
* This needs to be adjusted if more bits are defined.
|
||
*/
|
||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 255)),
|
||
};
|
||
|
||
static struct attrib dasd_attr_aq_requeue = {
|
||
.name = "aq_requeue",
|
||
.title = "Control I/O requeing during autoquiesce",
|
||
.desc =
|
||
"Use the aq_requeue attribute to control whether outstanding I/O\n"
|
||
"operations to the blocklayer should be automatically requeued after\n"
|
||
"an autoquiesce event.\n"
|
||
"Valid values are 1 for requeuing, or 0 for no requeueing.\n"
|
||
"Requeing the I/O requests to the blocklayer might benefit I/O\n"
|
||
"in case of a copy_pair swap operation.\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_aq_timeouts = {
|
||
.name = "aq_timeouts",
|
||
.title = "Specify timeout retry threshold",
|
||
.desc =
|
||
"Specify the number of sequential timeout events for an I/O operation\n"
|
||
"before an autoquiesce is triggered on a device.\n"
|
||
"This requires that the corresponding trigger bit 6 is set\n"
|
||
"in the aq_mask attribute.\n",
|
||
.order_cmp = ccw_online_only_order_cmp,
|
||
.check = ccw_online_only_check,
|
||
.defval = "32768",
|
||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 32768)),
|
||
};
|
||
|
||
/*
|
||
* 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);
|
||
}
|
||
|
||
static char *dasd_eckd_st_get_ipldev_id(struct subtype *st)
|
||
{
|
||
char *id, *type;
|
||
|
||
/* Handle CCW-type IPL first. */
|
||
id = st->super->get_ipldev_id(st);
|
||
if (id)
|
||
return id;
|
||
|
||
/* Extra-handling for eckd LD-IPL. */
|
||
type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type");
|
||
if (strcmp(type, "eckd") != 0)
|
||
goto out;
|
||
id = path_read_text_file(1, err_ignore, PATH_IPL "/ipl/device");
|
||
|
||
out:
|
||
free(type);
|
||
return id;
|
||
}
|
||
|
||
/*
|
||
* 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;
|
||
}
|
||
|
||
/* Remove all entries from the copy_pair attribute by writing 'clear' to it. */
|
||
static void dasd_clear_copy_pair(struct setting *s)
|
||
{
|
||
free(s->value);
|
||
s->value = misc_strdup("clear");
|
||
strlist_free(s->values);
|
||
s->values = strlist_new();
|
||
strlist_add(s->values, "clear");
|
||
s->removed = 0;
|
||
s->modified = 1;
|
||
}
|
||
|
||
/*
|
||
* Copy-pair values may contain multiple values in one line
|
||
* those need to be split up and put in multiple values entries
|
||
* delimiter is " " and "\n" depending on the source
|
||
* values read from sysfs have " " as delimiter and values from
|
||
* zdev have "\n"
|
||
*/
|
||
static void dasd_split_copy_pair(struct setting *s)
|
||
{
|
||
struct util_list *new = NULL;
|
||
struct strlist_node *node;
|
||
|
||
new = strlist_new();
|
||
util_list_iterate(s->values, node)
|
||
strlist_add_multi(new, node->str, "\n ", 0);
|
||
|
||
strlist_free(s->values);
|
||
s->values = new;
|
||
}
|
||
|
||
/*
|
||
* For persistent configurations one value per values[] entry is required
|
||
* to achieve a correct multi-line copy-pair statement in the resulting
|
||
* udev-rule.
|
||
*/
|
||
static exit_code_t dasd_st_configure_persistent(struct subtype *st,
|
||
struct device *dev)
|
||
{
|
||
struct setting *s;
|
||
|
||
util_list_iterate(&dev->persistent.settings->list, s) {
|
||
if (strcmp(s->name, "copy_pair") == 0)
|
||
dasd_split_copy_pair(s);
|
||
}
|
||
|
||
return st->super->configure_persistent(st, dev);
|
||
}
|
||
|
||
/*
|
||
* For active configurations one value per values[] entry is required to
|
||
* correctly operate the sysfs attribute.
|
||
*
|
||
* In case of removal the string "clear" has to be written to the
|
||
* value and values[] entry.
|
||
*/
|
||
static exit_code_t dasd_st_configure_active(struct subtype *st,
|
||
struct device *dev)
|
||
{
|
||
struct setting *s;
|
||
|
||
util_list_iterate(&dev->active.settings->list, s) {
|
||
if (strcmp(s->name, "copy_pair") != 0)
|
||
continue;
|
||
|
||
if (s->removed)
|
||
dasd_clear_copy_pair(s);
|
||
else
|
||
dasd_split_copy_pair(s);
|
||
}
|
||
|
||
return st->super->configure_active(st, dev);
|
||
}
|
||
|
||
/*
|
||
* 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,
|
||
&dasd_attr_copy_pair,
|
||
&dasd_attr_aq_mask,
|
||
&dasd_attr_aq_requeue,
|
||
&dasd_attr_aq_timeouts,
|
||
&internal_attr_early,
|
||
),
|
||
.unknown_dev_attribs = 1,
|
||
|
||
.configure_active = &dasd_st_configure_active,
|
||
.configure_persistent = &dasd_st_configure_persistent,
|
||
.check_pre_configure = &dasd_st_check_pre_configure,
|
||
.add_modules = &dasd_st_add_modules,
|
||
.get_ipldev_id = &dasd_eckd_st_get_ipldev_id,
|
||
};
|
||
|
||
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,
|
||
};
|