/* * 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 #include #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" ",.\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, };