diff --git a/zdev/include/path.h b/zdev/include/path.h index 0ed263ca..9d1adcbc 100644 --- a/zdev/include/path.h +++ b/zdev/include/path.h @@ -18,6 +18,7 @@ #define MODPROBE_PREFIX "s390x" #define UDEV_PREFIX "41" #define UDEV_SUFFIX ".rules" +#define ZDEV_PREFIX "40" #define PATH_MODPROBE_CONF "/etc/modprobe.d" #define PATH_MODPROBE "/sbin/modprobe" @@ -65,6 +66,7 @@ char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *); char *path_get_zfcp_port_dev(struct zfcp_lun_devid *); char *path_get_scsi_hctl_dev(const char *); char *path_get_bus_attr(const char *, const char *); +char *path_get_zdev_rule(const char *name); exit_code_t path_for_each(const char *, exit_code_t (*callback)(const char *, const char *, diff --git a/zdev/include/udev.h b/zdev/include/udev.h index fb1074c7..e37d7e3a 100644 --- a/zdev/include/udev.h +++ b/zdev/include/udev.h @@ -19,6 +19,9 @@ struct setting_list; extern int udev_need_settle; extern int udev_no_settle; +#define SITE_BLOCK_START "# site_start" +#define SITE_BLOCK_END "# site_end" + bool udev_file_is_empty(struct util_udev_file *file); void udev_get_device_ids(const char *type, struct util_list *list, @@ -31,4 +34,7 @@ void udev_add_internal_from_entry(struct setting_list *list, struct util_udev_entry_node *entry, struct attrib **attribs); +exit_code_t udev_write_site_rule(void); +bool is_legacy_rule(struct util_udev_file *file); + #endif /* UDEV_H */ diff --git a/zdev/src/ccw.c b/zdev/src/ccw.c index e8c40f9f..d0c08892 100644 --- a/zdev/src/ccw.c +++ b/zdev/src/ccw.c @@ -1593,7 +1593,7 @@ static exit_code_t ccw_st_deconfigure_active(struct subtype *st, static exit_code_t ccw_st_deconfigure_persistent(struct subtype *st, struct device *dev) { - return udev_remove_rule(st->name, dev->id, false); + return ccw_st_configure_persistent(st, dev); } /** diff --git a/zdev/src/path.c b/zdev/src/path.c index e040adbe..e1b496d7 100644 --- a/zdev/src/path.c +++ b/zdev/src/path.c @@ -404,3 +404,13 @@ char *path_get_bus_attr(const char *bus, const char *attr) { return path_get("/sys/bus/%s/%s", bus, attr); } + + /* Return path will be with ZDEV_PREFIX oon PATH_UDEV_RULES */ +char *path_get_zdev_rule(const char *name) +{ + const char *path = PATH_UDEV_RULES; + + /* Create file. */ + return path_get("%s/%s-%s%s", path, + ZDEV_PREFIX, name, UDEV_SUFFIX); +} diff --git a/zdev/src/udev.c b/zdev/src/udev.c index a59ba655..988a2be7 100644 --- a/zdev/src/udev.c +++ b/zdev/src/udev.c @@ -23,6 +23,7 @@ #include "path.h" #include "setting.h" #include "udev.h" +#include "zdev_id.h" int udev_need_settle = 0; int udev_no_settle; @@ -137,3 +138,61 @@ void udev_add_internal_from_entry(struct setting_list *list, out: free(copy); } + +/* + * Write the UDEV rule that determines the active SITE_ID + */ +exit_code_t udev_write_site_rule(void) +{ + char *path, *name = "zdev-id"; + exit_code_t rc = EXIT_OK; + FILE *fd; + + /* Create file. */ + path = path_get_zdev_rule(name); + + if (!util_path_exists(path)) { + rc = path_create(path); + if (rc) + goto out; + } + + fd = misc_fopen(path, "w"); + if (!fd) { + error("Could not write to file %s: %s\n", path, + strerror(errno)); + rc = EXIT_RUNTIME_ERROR; + goto out; + } + fprintf(fd, "# Generated by chzdev\n"); + fprintf(fd, "\n"); + /* We have to make sure that we are not trying to determine the SITE_ID + * for all the corresponding uevent. Check if the SITE_ID is already + * determined by reading the file /run/zdev_site_id; and if it is not + * available, execute the zdev_id program and determine the SITE_ID and + * create the new zdev_site_id file for next run. + */ + fprintf(fd, "TEST!=\"%s\", IMPORT{program}=" + "\"%s/zdev_id\",GOTO=\"end\"\n", ZDEV_SITE_ID_FILE, + TOOLS_LIBDIR); + fprintf(fd, "IMPORT{file}=\"%s\"\n", ZDEV_SITE_ID_FILE); + fprintf(fd, "LABEL=\"end\"\n"); + + if (misc_fclose(fd)) + warn("Could not close file %s: %s\n", path, strerror(errno)); + +out: + free(path); + return rc; +} + +bool is_legacy_rule(struct util_udev_file *file) +{ + struct util_udev_line_node *line; + + util_list_iterate(&file->lines, line) + if (starts_with(line->line, SITE_BLOCK_START)) + return false; + + return true; +} diff --git a/zdev/src/udev_ccw.c b/zdev/src/udev_ccw.c index 6446cb31..7c0a7030 100644 --- a/zdev/src/udev_ccw.c +++ b/zdev/src/udev_ccw.c @@ -26,6 +26,8 @@ #include "udev.h" #include "udev_ccw.h" +#define SITE_FALLBACK_SUFFIX "fb" + /* Check if a udev rule for the specified ccw device exists. */ bool udev_ccw_exists(const char *type, const char *id, bool autoconf) { @@ -79,10 +81,68 @@ out: free(copy); } +static void get_site_from_line(char *line, int *site, bool *in_site) +{ + char site_str[2]; + + if (starts_with(line, SITE_BLOCK_START)) { + memcpy(site_str, line + sizeof(SITE_BLOCK_START), 2); + if (strcmp(site_str, "fb") != 0) + *site = atoi(site_str); + else + *site = SITE_FALLBACK; + *in_site = true; + } else if (starts_with(line, SITE_BLOCK_END)) { + memcpy(site_str, line + sizeof(SITE_BLOCK_END), 2); + if (strcmp(site_str, "fb") != 0) + *site = atoi(site_str); + else + *site = SITE_FALLBACK; + *in_site = false; + } +} + +static void dev_state_copy(struct device_state *dst, struct device_state *src) +{ + setting_list_free(dst->settings); + dst->settings = setting_list_copy(src->settings); + dst->exists = src->exists; + dst->modified = src->modified; + dst->deconfigured = src->deconfigured; + dst->blacklisted = src->blacklisted; + dst->definable = src->definable; +} + +/* Extract per-site CCW device settings from a CCW device udev rule file. */ +static void _udev_file_get_settings_new(struct util_udev_file *file, + struct attrib **attribs, + struct device *dev) +{ + struct util_udev_line_node *line; + struct util_udev_entry_node *entry; + int site_id = SITE_FALLBACK; + struct setting_list *list; + bool in_site = false; + + util_list_iterate(&file->lines, line) { + get_site_from_line(line->line, &site_id, &in_site); + if (!in_site || site_id >= NUM_SITES || site_id < 0) + continue; + entry = util_list_start(&line->entries); + if (!entry) + continue; + list = dev->site_specific[site_id].settings; + add_setting_from_entry(list, entry, attribs); + dev->site_specific[site_id].exists = 1; + if (site_id == global_site_id) + dev_state_copy(&dev->persistent, &dev->site_specific[site_id]); + } +} + /* Extract CCW device settings from a CCW device udev rule file. */ -static void udev_file_get_settings(struct util_udev_file *file, - struct attrib **attribs, - struct setting_list *list) +static void _udev_file_get_settings_legacy(struct util_udev_file *file, + struct attrib **attribs, + struct setting_list *list) { struct util_udev_line_node *line; struct util_udev_entry_node *entry; @@ -95,6 +155,22 @@ static void udev_file_get_settings(struct util_udev_file *file, } } +static void udev_file_get_settings(struct util_udev_file *file, + struct attrib **attribs, + struct device *dev, + bool autoconf) +{ + struct device_state *state = autoconf ? &dev->autoconf : + &dev->persistent; + + if (is_legacy_rule(file) || autoconf) { + _udev_file_get_settings_legacy(file, attribs, state->settings); + state->exists = 1; + } else { + _udev_file_get_settings_new(file, attribs, dev); + } +} + /* Read the persistent configuration of a CCW device from a udev rule. */ exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf) { @@ -113,8 +189,7 @@ exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf) warn_once("Warning: Invalid udev rule: %s\n", path); state->exists = 0; } else { - udev_file_get_settings(file, st->dev_attribs, state->settings); - state->exists = 1; + udev_file_get_settings(file, st->dev_attribs, dev, autoconf); } util_udev_free_file(file); @@ -141,8 +216,32 @@ static char *get_label_id(const char *prefix, const char *type, return id; } -/* Write the persistent configuration of a CCW device to a udev rule. */ -exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf) +static void write_attr_to_file(FILE *fd, struct device_state *state, const char *id) +{ + struct ptrlist_node *p; + struct setting *s; + struct util_list *list = NULL; + + /* Apply attributes in correct order. */ + list = setting_list_get_sorted(state->settings); + + util_list_iterate(list, p) { + s = p->ptr; + if (s->removed) + continue; + if ((s->attrib && s->attrib->internal) || + internal_by_name(s->name)) { + fprintf(fd, "ENV{zdev_%s}=\"%s\"\n", + internal_get_name(s->name), s->value); + } else { + fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name, + s->value); + } + } + ptrlist_free(list, 0); +} + +static exit_code_t udev_ccw_write_device_legacy(struct device *dev, bool autoconf) { struct subtype *st = dev->subtype; struct ccw_subtype_data *data = st->data; @@ -150,22 +249,16 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf) struct device_state *state = autoconf ? &dev->autoconf : &dev->persistent; char *path, *cfg_label = NULL, *end_label = NULL; - struct util_list *list; - struct ptrlist_node *p; - struct setting *s; exit_code_t rc = EXIT_OK; FILE *fd; if (!state->exists) - return udev_remove_rule(type, id, autoconf); + return udev_remove_rule(type, id, true); cfg_label = get_label_id("cfg", type, id); end_label = get_label_id("end", type, id); - /* Apply attributes in correct order. */ - list = setting_list_get_sorted(state->settings); - - path = path_get_udev_rule(type, id, autoconf); + path = path_get_udev_rule(type, id, true); debug("Writing %s udev rule file %s\n", type, path); if (!util_path_exists(path)) { rc = path_create(path); @@ -196,22 +289,8 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf) } fprintf(fd, "GOTO=\"%s\"\n", end_label); fprintf(fd, "\n"); - fprintf(fd, "LABEL=\"%s\"\n", cfg_label); - /* Write settings. */ - util_list_iterate(list, p) { - s = p->ptr; - if (s->removed) - continue; - if ((s->attrib && s->attrib->internal) || - internal_by_name(s->name)) { - fprintf(fd, "ENV{zdev_%s}=\"%s\"\n", - internal_get_name(s->name), s->value); - } else { - fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name, - s->value); - } - } + write_attr_to_file(fd, state, id); /* Write udev rule epilog. */ fprintf(fd, "\n"); @@ -221,7 +300,6 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf) warn("Could not close file %s: %s\n", path, strerror(errno)); out: - ptrlist_free(list, 0); free(end_label); free(cfg_label); free(path); @@ -229,6 +307,150 @@ out: return rc; } +/* Write the persistent configuration of a CCW device to a udev rule. */ +static exit_code_t udev_ccw_write_device_new(struct device *dev) +{ + struct subtype *st = dev->subtype; + struct ccw_subtype_data *data = st->data; + const char *type = st->name, *drv = data->any_driver ? "*" : data->ccwdrv, *id = dev->id; + char *path, *cfg_label = NULL, *end_label = NULL; + exit_code_t rc = EXIT_OK; + FILE *fd; + int i, configured = 0; + + /* + * Remove the previous file; The new udev rule will be created based on the + * current configuration settings + */ + udev_remove_rule(type, id, 0); + + /* + * Copy the dev->persistent to the site_specific array; While writing the new + * udev-rule we consider the site_specific array only + */ + + dev_state_copy(&dev->site_specific[global_site_id], &dev->persistent); + for (i = 0; i < NUM_SITES; i++) { + configured += dev->site_specific[i].exists; + configured -= dev->site_specific[i].deconfigured; + } + + /* + * If there is no configuration settings available, do not create the new + * udev-rule file; exit here + */ + if (!configured) + return rc; + + cfg_label = get_label_id("cfg", type, id); + end_label = get_label_id("end", type, id); + + path = path_get_udev_rule(type, id, false); + debug("Writing %s udev rule file %s\n", type, path); + if (!util_path_exists(path)) { + rc = path_create(path); + if (rc) + goto out; + } + + fd = misc_fopen(path, "w"); + if (!fd) { + error("Could not write to file %s: %s\n", path, + strerror(errno)); + rc = EXIT_RUNTIME_ERROR; + goto out; + } + + /* Write udev rule prolog. */ + fprintf(fd, "# Generated by chzdev\n"); + + if (drv) { + fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", " + "KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", id, + drv, cfg_label); + fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", " + "KERNEL==\"%s\", TEST==\"[ccw/%s]\", " + "GOTO=\"%s\"\n", drv, id, cfg_label); + } else { + fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", " + "KERNEL==\"%s\", GOTO=\"%s\"\n", id, cfg_label); + } + fprintf(fd, "GOTO=\"%s\"\n", end_label); + fprintf(fd, "\n"); + + fprintf(fd, "LABEL=\"%s\"\n", cfg_label); + /* site comparison block */ + for (i = 0; i < NUM_USER_SITES; i++) { + if (dev->site_specific[i].exists && + !dev->site_specific[i].deconfigured) { + fprintf(fd, "ENV{ZDEV_SITE_ID}==\"%d\",GOTO=\"%s_site%d\"\n", i, + cfg_label, i); + } + } + + /* + * If we have a generic configuration available, then use that setting as a + * fail-over incase of no site-id information in LOADPARM + */ + if (dev->site_specific[SITE_FALLBACK].exists && + !dev->site_specific[SITE_FALLBACK].deconfigured) + fprintf(fd, "GOTO=\"%s_site_fb\"\n", cfg_label); + else + fprintf(fd, "GOTO=\"%s\"\n", end_label); + fprintf(fd, "\n"); + + /* Write the site blocks for all the available configurations */ + for (i = 0; i < NUM_USER_SITES; i++) { + if (dev->site_specific[i].exists && + !dev->site_specific[i].deconfigured) { + fprintf(fd, "# site_start_%d\n", i); + fprintf(fd, "LABEL=\"%s_site%d\"\n", cfg_label, i); + + write_attr_to_file(fd, &dev->site_specific[i], id); + /* Write udev rule epilog. */ + fprintf(fd, "GOTO=\"%s\"\n", end_label); + fprintf(fd, "# site_end_%d\n", i); + fprintf(fd, "\n"); + } + } + if (dev->site_specific[SITE_FALLBACK].exists && + !dev->site_specific[SITE_FALLBACK].deconfigured) { + fprintf(fd, "# site_start_fb\n"); + fprintf(fd, "LABEL=\"%s_site_fb\"\n", cfg_label); + + write_attr_to_file(fd, &dev->site_specific[i], id); + /* Write udev rule epilog. */ + fprintf(fd, "GOTO=\"%s\"\n", end_label); + fprintf(fd, "# site_end_fb\n"); + fprintf(fd, "\n"); + } + + /* Write udev rule epilog. */ + fprintf(fd, "\n"); + fprintf(fd, "LABEL=\"%s\"\n", end_label); + + if (misc_fclose(fd)) + warn("Could not close file %s: %s\n", path, strerror(errno)); + + rc = udev_write_site_rule(); + if (rc) + goto out; +out: + free(end_label); + free(cfg_label); + free(path); + + return rc; +} + +exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf) +{ + if (autoconf) + return udev_ccw_write_device_legacy(dev, autoconf); + else + return udev_ccw_write_device_new(dev); +} + #define MARKER "echo free " static char *read_cio_ignore(const char *path) @@ -300,6 +522,7 @@ exit_code_t udev_ccw_write_cio_ignore(const char *id_list, bool autoconf) /* Write udev rule. */ fprintf(fd, "# Generated by chzdev\n"); + fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"subsystem\", " "KERNEL==\"ccw\", RUN{program}+=\"/bin/sh -c " "'echo free %s > /proc/cio_ignore'\"\n", id_list);