Files
s390-tools/zdev/src/generic_ccw.c
Dimitri John Ledkov 07b11982e3 zdev: Trigger generic_ccw devices on any kernel module loads.
Generic CCW device can use any driver, and the value of the driver is
not known ahead of time. To avoid the race between loading and binding
a kernel module, and devices added - retrigger generic-ccw devices on
any kernel module load.

Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/37
Link: https://bugs.launchpad.net/ubuntu-z-systems/+bug/1794308
Closes: https://github.com/ibm-s390-tools/s390-tools/pull/45

Signed-off-by: Dimitri John Ledkov <xnox@ubuntu.com>
Acked-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2018-10-19 15:32:40 +02:00

244 lines
5.6 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 <string.h>
#include "lib/util_path.h"
#include "attrib.h"
#include "ccw.h"
#include "ccwgroup.h"
#include "devnode.h"
#include "devtype.h"
#include "generic_ccw.h"
#include "internal.h"
#include "namespace.h"
#include "path.h"
#include "subtype.h"
/*
* Generic CCW device sub-type.
*/
static struct ccw_subtype_data generic_ccw_data = {
.ccwdrv = NULL,
.mod = NULL,
.any_driver = true,
};
/* Check if there is a non-generic subtype in the CCW namespace that uses the
* specified CCW device driver. */
static bool match_non_generic(const char *drv)
{
int i, j;
struct devtype *dt;
struct subtype *st;
struct ccw_subtype_data *ccwdata;
struct ccwgroup_subtype_data *ccwgroupdata;
const char *ccwdrv;
for (i = 0; (dt = devtypes[i]); i++) {
for (j = 0; (st = dt->subtypes[j]); j++) {
if (st->generic || !st->data)
continue;
if (st->namespace == &ccw_namespace) {
ccwdata = st->data;
ccwdrv = ccwdata->ccwdrv;
} else if (st->namespace->cmp_parsed_ids ==
&ccwgroup_cmp_parsed_ids) {
ccwgroupdata = st->data;
ccwdrv = ccwgroupdata->ccwdrv;
} else
continue;
if (!ccwdrv)
continue;
if (strcmp(drv, ccwdrv) == 0)
return true;
}
}
return false;
}
/* Determine if the specified device exists in the CCW namespace and is not
* handled by another subtype. */
static bool generic_exists(struct subtype *st, const char *id, int fast)
{
struct ccw_devid devid;
char *drv = NULL;
bool result = false;
if (ccw_parse_devid(&devid, id, err_ignore) != EXIT_OK)
goto out;
if (!fast && !ccw_exists(NULL, NULL, id))
goto out;
drv = ccw_get_driver(&devid);
if (!drv) {
/* Account devices with no driver to generic-ccw to make them
* visible. */
result = true;
goto out;
}
result = !match_non_generic(drv);
out:
free(drv);
return result;
}
static bool generic_ccw_st_exists_active(struct subtype *st, const char *id)
{
return generic_exists(st, id, 0);
}
static bool get_ids_cb(const char *file, void *data)
{
/* We use fast=1 here to prevent another stat() syscall per device
* when we already know that the directory exists. */
return generic_exists(data, file, 1);
}
/* Add the IDs of all CCW devices existing in the active configuration which
* are not handled by other subtypes. */
static void generic_ccw_st_add_active_ids(struct subtype *st,
struct util_list *ids)
{
char *path;
cio_settle(0);
path = path_get_ccw_devices(NULL);
misc_read_dir(path, ids, get_ids_cb, st);
free(path);
}
struct add_cb_data {
struct util_list *devnodes;
const char *id;
};
static exit_code_t add_cb(const char *abs_path, const char *rel_path,
void *data)
{
struct add_cb_data *cb_data = data;
char *link, *target, *name;
static const char *prefix[] = { "vmrdr-", "vmpun-", "vmprt-" };
unsigned int i, major, minor;
struct devnode *devnode;
link = misc_readlink(abs_path);
if (!link)
return EXIT_OK;
target = basename(link);
for (i = 0; i < ARRAY_SIZE(prefix); i++) {
name = misc_asprintf("%s%s", prefix[i], cb_data->id);
if (strcmp(target, name) != 0)
goto next;
if (sscanf(rel_path, "%u:%u", &major, &minor) != 2)
goto next;
devnode = devnode_new(CHARDEV, major, minor, target);
ptrlist_add(cb_data->devnodes, devnode);
next:
free(name);
}
free(link);
return EXIT_OK;
}
/* Add struct devnodes to ptrlist @devnodes for each Linux device that is
* provided by the CCW device with the specified ID. Since this is a generic
* CCW driver covering multiple device drivers, this is somewhat of trial
* and error work. */
static void generic_ccw_st_add_devnodes(struct subtype *st, const char *id,
struct util_list *devnodes)
{
struct add_cb_data cb_data;
char *path;
cb_data.devnodes = devnodes;
cb_data.id = id;
path = path_get_sys_dev_char_devices();
if (util_path_is_dir(path))
path_for_each(path, add_cb, &cb_data);
free(path);
}
static struct subtype generic_ccw_subtype = {
.super = &ccw_subtype,
.devtype = &generic_ccw_devtype,
.name = "generic-ccw",
.title = "Generic Channel-Command-Word (CCW) devices",
.devname = "Generic CCW device",
.modules = NULL,
.namespace = &ccw_namespace,
.data = &generic_ccw_data,
.dev_attribs = ATTRIB_ARRAY(
&ccw_attr_online,
&ccw_attr_cmb_enable,
&internal_attr_early,
),
.unknown_dev_attribs = 1,
.generic = 1,
.exists_active = &generic_ccw_st_exists_active,
.add_active_ids = &generic_ccw_st_add_active_ids,
.add_devnodes = &generic_ccw_st_add_devnodes,
};
/*
* Generic CCW device type methods.
*/
/* Clean up all resources used by devtype object. */
static void generic_ccw_devtype_exit(struct devtype *dt)
{
setting_list_free(dt->active_settings);
setting_list_free(dt->persistent_settings);
}
static exit_code_t generic_ccw_devtype_read_settings(struct devtype *dt,
config_t config)
{
dt->active_settings = setting_list_new();
dt->persistent_settings = setting_list_new();
return EXIT_OK;
}
static exit_code_t generic_ccw_devtype_write_settings(struct devtype *dt,
config_t config)
{
return EXIT_OK;
}
/*
* Generic CCW device type.
*/
struct devtype generic_ccw_devtype = {
.name = "generic-ccw",
.title = "", /* Only use subtypes. */
.devname = "Generic CCW device",
.modules = NULL,
.subtypes = SUBTYPE_ARRAY(
&generic_ccw_subtype,
),
.type_attribs = ATTRIB_ARRAY(),
.exit = &generic_ccw_devtype_exit,
.read_settings = &generic_ccw_devtype_read_settings,
.write_settings = &generic_ccw_devtype_write_settings,
};