mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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>
244 lines
5.6 KiB
C
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,
|
|
};
|