zdev: Implement internal device attributes

This change adds base infrastructure for implementing internal device
attributes. In the context of the zdev tools, an internal device
attribute is a new type of device attribute with the following
characteristics:

  - Can be set and removed like normal device attributes
  - Affects zdev-internal handling only
  - Does not correspond to an actual device attribute, that is
    it has no representation in SysFS
  - Can not be set in the active configuration
  - Name starts with "zdev:" to prevent conflicts with actual
    device attributes

Values for internal device attributes are stored in udev rules alongside
the normal persistent configuration of a device. They are encoded as
udev environment variables. Note that they have no further effect on
udev processing.

Signed-off-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Peter Oberparleiter
2017-09-01 11:05:58 +02:00
committed by Jan Höppner
parent 3c5644ccfd
commit c0392efa39
12 changed files with 220 additions and 8 deletions

View File

@@ -8,7 +8,7 @@ ALL_CPPFLAGS += -I ../include -std=gnu99 -Wno-unused-parameter \
chzdev_objects += attrib.o chzdev.o device.o devnode.o devtype.o exit_code.o \
export.o hash.o inuse.o misc.o namespace.o opts.o path.o \
root.o select.o setting.o subtype.o table.o table_attribs.o \
table_types.o net.o firmware.o
table_types.o net.o firmware.o internal.o
# Devtype Helpers
chzdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \
@@ -38,7 +38,7 @@ chzdev_objects += generic_ccw.o
lszdev_objects += attrib.o lszdev.o device.o devnode.o devtype.o exit_code.o \
export.o hash.o inuse.o misc.o namespace.o opts.o path.o \
root.o select.o setting.o subtype.o table.o table_types.o \
net.o
net.o internal.o
# Devtype Helpers
lszdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \

View File

@@ -16,6 +16,7 @@
#include "attrib.h"
#include "device.h"
#include "devtype.h"
#include "internal.h"
#include "misc.h"
#include "namespace.h"
#include "setting.h"
@@ -221,6 +222,9 @@ static exit_code_t apply_setting(struct device *dev, config_t config,
goto err_activeonly_forceable;
if (!force && SCOPE_AUTOCONF(config) && a->activeonly)
goto err_activeonly_forceable;
/* Check for internal. */
if (config == config_active && a->internal)
goto err_int_noactive;
/* Check for multiple values. */
if (!force && !a->multi && strlist_find(processed, key))
goto err_multi_forceable;
@@ -230,6 +234,9 @@ static exit_code_t apply_setting(struct device *dev, config_t config,
goto err_unknown;
if (!force)
goto err_unknown_forceable;
/* Check for internal. */
if (config == config_active && internal_by_name(key))
goto err_int_noactive;
}
strlist_add(processed, "%s", key);
@@ -294,6 +301,11 @@ err_activeonly_forceable:
delayed_forceable("Attribute '%s' should only be changed in the active "
"config\n", a->name);
return EXIT_INVALID_SETTING;
err_int_noactive:
delayed_err("Internal attribute '%s' cannot be set in the active config\n",
key);
return EXIT_INVALID_SETTING;
}
/* Apply device settings from strlist to device. */
@@ -542,6 +554,9 @@ exit_code_t device_write_active_settings(struct device *dev)
s = p->ptr;
if (!s->modified || s->removed)
continue;
if ((s->attrib && s->attrib->internal) ||
internal_by_name(s->name))
continue;
path = subtype_get_active_attrib_path(st, dev, s->name);
if (!path) {

25
zdev/src/internal.c Normal file
View File

@@ -0,0 +1,25 @@
/*
* zdev - Modify and display the persistent configuration of devices
*
* Copyright IBM Corp. 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 <stdbool.h>
#include "internal.h"
#include "misc.h"
/* Return identifier of internal attribute with specified @name. */
const char *internal_get_name(const char *name)
{
return name + sizeof(INTERNAL_ATTR_PREFIX) - 1;
}
/* Check if attribute is internal by name. */
bool internal_by_name(const char *name)
{
return starts_with(name, INTERNAL_ATTR_PREFIX);
}

View File

@@ -1717,3 +1717,18 @@ void debug_init(int argc, char *argv[])
fprintf(stderr, "%s\"%s\"", i > 0 ? ", " : "", argv[i]);
fprintf(stderr, "\n");
}
/* Return the last occurrence of @needle in @haystack, or %NULL if @needle
* was not found. */
char *misc_strrstr(const char *haystack, const char *needle)
{
char *result, *next;
result = strstr(haystack, needle);
if (result) {
while ((next = strstr(result + 1, needle)))
result = next;
}
return result;
}

View File

@@ -409,3 +409,35 @@ void udev_settle(void)
return;
misc_system(err_ignore, "%s settle", PATH_UDEVADM);
}
/* Extract internal attribute settings from @entry and add to @list.
* Associate corresponding attribute if found in @attribs. */
void udev_add_internal_from_entry(struct setting_list *list,
struct udev_entry_node *entry,
struct attrib **attribs)
{
char *copy, *name, *end, *u;
struct attrib *a;
/* ENV{zdev_var}="1" */
copy = misc_strdup(entry->key);
/* Find attribute name start. */
name = strchr(copy, '{');
end = strrchr(copy, '}');
if (!name || !end)
goto out;
*end = 0;
name++;
/* zdev_ => zdev: */
u = strchr(name, '_');
if (u)
*u = ':';
a = attrib_find(attribs, name);
setting_list_apply_actual(list, a, name, entry->value);
out:
free(copy);
}

View File

@@ -18,6 +18,7 @@
#include "attrib.h"
#include "ccw.h"
#include "device.h"
#include "internal.h"
#include "misc.h"
#include "path.h"
#include "setting.h"
@@ -49,6 +50,12 @@ static void add_setting_from_entry(struct setting_list *list,
char *copy, *name, *end;
struct attrib *a;
/* ENV{zdev_var}="1" */
if (starts_with(entry->key, "ENV{zdev_") &&
strcmp(entry->op, "=") == 0) {
udev_add_internal_from_entry(list, entry, attribs);
return;
}
/* ATTR{[ccw/0.0.37bf]online}=1 */
if (strncmp(entry->key, "ATTR{[ccw/", 10) != 0 ||
strcmp(entry->op, "=") != 0)
@@ -190,7 +197,14 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
s = p->ptr;
if (s->removed)
continue;
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name, s->value);
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 udev rule epilog. */

View File

@@ -18,6 +18,7 @@
#include "attrib.h"
#include "ccwgroup.h"
#include "device.h"
#include "internal.h"
#include "misc.h"
#include "path.h"
#include "setting.h"
@@ -68,6 +69,12 @@ static void add_setting_from_entry(struct setting_list *list,
char *copy, *name, *end;
struct attrib *a;
/* ENV{zdev_var}="1" */
if (starts_with(entry->key, "ENV{zdev_") &&
strcmp(entry->op, "=") == 0) {
udev_add_internal_from_entry(list, entry, attribs);
return;
}
/* ATTR{[ccwgroup/0.0.f5f0]online}=1 */
if (strncmp(entry->key, "ATTR{[ccwgroup/", 10) != 0 ||
strcmp(entry->op, "=") != 0)
@@ -282,6 +289,10 @@ exit_code_t udev_ccwgroup_write_device(struct device *dev, bool autoconf)
fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n",
ccw_id, s->name, str->str);
}
} else 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{[ccwgroup/%s]%s}=\"%s\"\n", ccw_id,
s->name, s->value);

View File

@@ -18,6 +18,7 @@
#include "attrib.h"
#include "device.h"
#include "internal.h"
#include "misc.h"
#include "path.h"
#include "scsi.h"
@@ -128,7 +129,7 @@ static bool zfcp_lun_devid_from_entry(struct zfcp_lun_devid *id_ptr,
struct udev_entry_node *entry)
{
struct zfcp_lun_devid id;
char *copy = NULL, *s;
char *copy = NULL, *s, *e, *u;
int i;
bool rc = false;
@@ -182,6 +183,28 @@ static bool zfcp_lun_devid_from_entry(struct zfcp_lun_devid *id_ptr,
goto out;
}
/*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/
if (starts_with(entry->key, "ENV{zdev_")) {
copy = misc_strdup(entry->key);
/* Find ID start (last __) and end (last }). */
s = misc_strrstr(copy, "__");
e = strrchr(copy, '}');
if (!s || !e)
goto out;
*e = 0;
s += 2;
/* Convert variable name to ID format. */
for (i = 0, u = s; (u = strchr(u, '_')); i++, u++) {
if (i < 2)
*u = '.';
else
*u = ':';
}
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
true : false;
}
out:
free(copy);
if (rc)
@@ -211,11 +234,46 @@ static struct zfcp_lun_node *zfcp_lun_node_from_entry(
return node;
}
static void add_internal_setting_from_entry(struct udev_entry_node *entry,
struct zfcp_lun_node *node)
{
char *copy, *name, *end, *u;
struct attrib *a;
/*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/
copy = misc_strdup(entry->key);
/* Find attribute name start and end. */
name = strchr(copy, '{');
end = misc_strrstr(copy, "__");
if (!name || !end)
goto out;
*end = 0;
name++;
/* zdev_ => zdev: */
u = strchr(name, '_');
if (u)
*u = ':';
a = attrib_find(zfcp_lun_subtype.dev_attribs, name);
setting_list_apply_actual(node->fc_settings, a, name, entry->value);
out:
free(copy);
}
static void add_fc_setting_from_entry(struct udev_entry_node *entry,
struct zfcp_lun_node *node)
{
char *copy, *s, *e;
/*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/
if (starts_with(entry->key, "ENV{zdev_") &&
strcmp(entry->op, "=") == 0) {
add_internal_setting_from_entry(entry, node);
return;
}
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/0x402340d400000000/failed}="0"*/
if (!starts_with(entry->key, "ATTR{[ccw/"))
return;
@@ -490,7 +548,7 @@ static struct zfcp_lun_node *state_to_zfcp_lun_node(struct zfcp_lun_devid *id,
s->value);
setting_list_add(node->scsi_settings, n);
} else {
n = setting_new(NULL, s->name, s->value);
n = setting_new(s->attrib, s->name, s->value);
setting_list_add(node->fc_settings, n);
}
}
@@ -580,9 +638,21 @@ static exit_code_t write_luns_rule(const char *path, struct util_list *list)
node->id.lun);
util_list_iterate(&node->fc_settings->list, s) {
fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64 "/0x%016"
PRIx64 "/%s}=\"%s\"\n", hba_id, node->id.wwpn,
node->id.lun, s->name, s->value);
if ((s->attrib && s->attrib->internal) ||
internal_by_name(s->name)) {
fprintf(fd, "ENV{zdev_%s__%x_%x_%04x_0x%016"
PRIx64 "_0x%016" PRIx64 "}=\"%s\"\n",
internal_get_name(s->name),
node->id.fcp_dev.cssid,
node->id.fcp_dev.ssid,
node->id.fcp_dev.devno, node->id.wwpn,
node->id.lun, s->value);
} else {
fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64
"/0x%016" PRIx64 "/%s}=\"%s\"\n",
hba_id, node->id.wwpn, node->id.lun,
s->name, s->value);
}
}
last_node = node;
}