diff --git a/zdev/include/devnode.h b/zdev/include/devnode.h index c9e7f12e..a9a1a170 100644 --- a/zdev/include/devnode.h +++ b/zdev/include/devnode.h @@ -42,5 +42,6 @@ int devnode_cmp(struct devnode *, struct devnode *); int devnode_add_block_from_sysfs(struct util_list *, const char *); int devnode_add_net_from_sysfs(struct util_list *, const char *); char *devnode_readlink(struct devnode *); +char *devnode_get_virtio_dir(const char *ccw_path); #endif /* DEVNODE_H */ diff --git a/zdev/include/virtio.h b/zdev/include/virtio.h new file mode 100644 index 00000000..4e9247a6 --- /dev/null +++ b/zdev/include/virtio.h @@ -0,0 +1,19 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * Copyright IBM Corp. 2025 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef VIRTIO_H +#define VIRTIO_H + +struct devtype; +struct subtype; + +extern struct devtype virtio_devtype; +/* Subtypes are defined in virtio_devtype.subtypes array */ + +#endif /* VIRTIO_H */ diff --git a/zdev/src/Makefile b/zdev/src/Makefile index 181b7c19..d0163390 100644 --- a/zdev/src/Makefile +++ b/zdev/src/Makefile @@ -42,6 +42,9 @@ chzdev_objects += lcs.o lcs_auto.o # AP devtype chzdev_objects += ap.o +# virtio devtype +chzdev_objects += virtio.o + # Generic CCW devtype chzdev_objects += generic_ccw.o @@ -75,6 +78,9 @@ lszdev_objects += lcs.o lcs_auto.o # AP devtype lszdev_objects += ap.o +# virtio devtype +lszdev_objects += virtio.o + # Generic CCW devtype lszdev_objects += generic_ccw.o diff --git a/zdev/src/ccw.c b/zdev/src/ccw.c index 3441c4ad..9d332220 100644 --- a/zdev/src/ccw.c +++ b/zdev/src/ccw.c @@ -1757,8 +1757,10 @@ static void ccw_st_add_devnodes(struct subtype *st, const char *id, char *path; path = ccw_get_dev_path(data->ccwdrv, id); - if (path) + if (path) { devnode_add_block_from_sysfs(devnodes, path); + devnode_add_net_from_sysfs(devnodes, path); + } free(path); } diff --git a/zdev/src/devnode.c b/zdev/src/devnode.c index 0169921e..2adbb733 100644 --- a/zdev/src/devnode.c +++ b/zdev/src/devnode.c @@ -21,6 +21,36 @@ #include "misc.h" #include "path.h" +struct virtio_cb_data { + char *virtio_name; +}; + +static exit_code_t add_virtio_cb(const char *path, const char *filename, void *data) +{ + struct virtio_cb_data *cb_data = data; + + if (starts_with(filename, "virtio")) { + cb_data->virtio_name = misc_strdup(path); + return EXIT_RUNTIME_ERROR; + } + + return EXIT_OK; +} + +/* + * Given path like /sys/bus/ccw/devices/0.0.0001, get virtio subdirectory + * like "virtio4", NULL if not found. + */ +char *devnode_get_virtio_dir(const char *ccw_path) +{ + struct virtio_cb_data cb_data = {}; + + if (util_path_is_dir(ccw_path)) + path_for_each(ccw_path, add_virtio_cb, &cb_data); + + return cb_data.virtio_name; +} + /* Create a newly allocated devnode object from the specified data. */ struct devnode *devnode_new(devnode_t type, unsigned int major, unsigned int minor, const char *name) @@ -244,7 +274,7 @@ static exit_code_t add_block_cb(const char *path, const char *filename, int devnode_add_block_from_sysfs(struct util_list *list, const char *path) { struct add_cb_data cb_data; - char *blkpath; + char *blkpath, *virtio_path; cb_data.list = list; cb_data.num = 0; @@ -255,6 +285,15 @@ int devnode_add_block_from_sysfs(struct util_list *list, const char *path) path_for_each(blkpath, add_block_cb, &cb_data); free(blkpath); + virtio_path = devnode_get_virtio_dir(path); + if (virtio_path) { + blkpath = misc_asprintf("%s/block", virtio_path); + if (util_path_is_dir(blkpath)) + path_for_each(blkpath, add_block_cb, &cb_data); + free(blkpath); + free(virtio_path); + } + return cb_data.num; } @@ -278,7 +317,7 @@ static exit_code_t add_net_cb(const char *path, const char *filename, int devnode_add_net_from_sysfs(struct util_list *list, const char *path) { struct add_cb_data cb_data; - char *netpath; + char *netpath, *virtio_path; cb_data.list = list; cb_data.num = 0; @@ -289,6 +328,15 @@ int devnode_add_net_from_sysfs(struct util_list *list, const char *path) path_for_each(netpath, add_net_cb, &cb_data); free(netpath); + virtio_path = devnode_get_virtio_dir(path); + if (virtio_path) { + netpath = misc_asprintf("%s/net", virtio_path); + if (util_path_is_dir(netpath)) + path_for_each(netpath, add_net_cb, &cb_data); + free(netpath); + free(virtio_path); + } + return cb_data.num; } diff --git a/zdev/src/devtype.c b/zdev/src/devtype.c index fcd46197..3085fdd6 100644 --- a/zdev/src/devtype.c +++ b/zdev/src/devtype.c @@ -26,6 +26,7 @@ #include "select.h" #include "setting.h" #include "subtype.h" +#include "virtio.h" #include "zfcp.h" #include "ap.h" @@ -37,6 +38,7 @@ struct devtype *devtypes[] = { &ctc_devtype, &lcs_devtype, &ap_devtype, + &virtio_devtype, &generic_ccw_devtype, /* Generic types should come last. */ NULL }; diff --git a/zdev/src/virtio.c b/zdev/src/virtio.c new file mode 100644 index 00000000..4562b740 --- /dev/null +++ b/zdev/src/virtio.c @@ -0,0 +1,236 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * Copyright IBM Corp. 2025 + * + * 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 +#include +#include + +#include "attrib.h" +#include "ccw.h" +#include "ccwgroup.h" +#include "devnode.h" +#include "devtype.h" +#include "internal.h" +#include "namespace.h" +#include "path.h" +#include "subtype.h" +#include "virtio.h" + +/* must be negative and distinct from other virtio-ccw types */ +#define VIRTIO_CCW_GENERIC_ID (-1) + +struct ccw_subtype_data_virtio { + struct ccw_subtype_data orig_data; /* must be first */ + int virtio_idx; +}; + +static struct subtype *virtio_ccw_find_st_by_idx(int virtio_device_type) +{ + struct subtype *other_st; + + for (int i = 0; (other_st = virtio_devtype.subtypes[i]); i++) { + struct ccw_subtype_data_virtio *other_subtype_data = + other_st->data; + + if (virtio_device_type == other_subtype_data->virtio_idx) + return other_st; + } + return NULL; +} + +static bool virtio_ccw_exists(struct subtype *st, const char *id, int fast) +{ + struct ccw_subtype_data_virtio *st_data = st->data; + struct ccw_devinfo *devinfo = NULL; + struct ccw_devid devid; + int virtio_device_type; + char *drv = NULL; + bool rc = 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) + goto out; + + if (strcmp(drv, "virtio_ccw")) { + /* not virtio_ccw */ + goto out_free_drv; + } + + devinfo = ccw_devinfo_get(&devid, false); + + if (!devinfo->exists) + goto out_free_devinfo; + + virtio_device_type = devinfo->cumodel; + + if (st_data->virtio_idx == VIRTIO_CCW_GENERIC_ID) { + /* generic virtio-ccw + * rc = true iff device type not found in subtype list + */ + rc = !virtio_ccw_find_st_by_idx(virtio_device_type); + } else { + /* not generic virtio-ccw + * device type must match that of current subtype + */ + rc = (virtio_device_type == st_data->virtio_idx); + } + +out_free_devinfo: + free(devinfo); + +out_free_drv: + free(drv); + +out: + return rc; +} + +static bool virtio_ccw_st_exists_active(struct subtype *st, const char *id) +{ + return virtio_ccw_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 virtio_ccw_exists(data, file, 1); +} + +static void virtio_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); +} + +#define DEF_VIRTIO_TYPE(type__, idx__, desc__, modname__) \ + static struct ccw_subtype_data_virtio virtio_##type__##_data = { \ + .orig_data = { .mod = modname__, \ + .ccwdrv = "virtio_ccw", \ + .any_driver = false }, \ + .virtio_idx = idx__ \ + }; \ + static struct subtype virtio_##type__##_subtype = { \ + .super = &ccw_subtype, \ + .devtype = &virtio_devtype, \ + .name = "virtio-" #type__, \ + .title = desc__, \ + .devname = "Virtual I/O device", \ + .modules = STRING_ARRAY(modname__), \ + .namespace = &ccw_namespace, \ + .data = &virtio_##type__##_data, \ + .dev_attribs = ATTRIB_ARRAY(&ccw_attr_online, \ + &ccw_attr_cmb_enable, \ + &internal_attr_early, ), \ + .exists_active = virtio_ccw_st_exists_active, \ + .add_active_ids = virtio_ccw_st_add_active_ids, \ + .unknown_dev_attribs = 1, \ + } + +DEF_VIRTIO_TYPE(ccw, VIRTIO_CCW_GENERIC_ID, + "Virtual I/O devices (unknown subtype)", "virtio_ccw"); +DEF_VIRTIO_TYPE(net, 1, "Virtual I/O network devices", "virtio_net"); +DEF_VIRTIO_TYPE(blk, 2, "Virtual I/O block devices", "virtio_blk"); +DEF_VIRTIO_TYPE(console, 3, "Virtual I/O console devices", "virtio_console"); +DEF_VIRTIO_TYPE(rng, 4, "Virtual I/O rng devices", "virtio_rng"); +DEF_VIRTIO_TYPE(balloon, 5, "Virtual I/O balloon devices", "virtio_balloon"); +DEF_VIRTIO_TYPE(scsi, 8, "Virtual I/O scsi devices", "virtio_scsi"); +DEF_VIRTIO_TYPE(9p, 9, "Virtual I/O 9p devices", "9pnet_virtio"); +DEF_VIRTIO_TYPE(rproc_serial, 11, "Virtual I/O rproc-serial devices", + "virtio_console"); +DEF_VIRTIO_TYPE(gpu, 16, "Virtual I/O gpu devices", "virtio_gpu"); +DEF_VIRTIO_TYPE(input, 18, "Virtual I/O input devices", "virtio_input"); +DEF_VIRTIO_TYPE(vsock, 19, "Virtual I/O vsock devices", + "vmw_vsock_virtio_transport"); +DEF_VIRTIO_TYPE(crypto, 20, "Virtual I/O crypto devices", "virtio_crypto"); +DEF_VIRTIO_TYPE(mem, 24, "Virtual I/O memory devices", "virtio_mem"); +DEF_VIRTIO_TYPE(fs, 26, "Virtual I/O fs devices", "virtio_fs"); + +/* + * Virtio devtype methods. + */ + +/* Clean up all resources used by devtype object. */ +static void virtio_devtype_exit(struct devtype *dt) +{ + setting_list_free(dt->active_settings); + setting_list_free(dt->persistent_settings); +} + +static exit_code_t virtio_devtype_read_settings(struct devtype *dt, + config_t config) +{ + /* No kernel or module parameters exist for the virtio device driver, + * but at least determine module loaded state. */ + dt->active_settings = setting_list_new(); + dt->persistent_settings = setting_list_new(); + + if (SCOPE_ACTIVE(config)) + dt->active_exists = devtype_is_module_loaded(dt); + + return EXIT_OK; +} + +static exit_code_t virtio_devtype_write_settings(struct devtype *dt, + config_t config) +{ + /* No kernel or module parameters exist for the virtio device driver. */ + + return EXIT_OK; +} + +/* + * Virtio devtype. + */ + +struct devtype virtio_devtype = { + .name = "virtio", + .title = "Virtual I/O (virtio) Channel-Command-Word devices", + .devname = "Virtual I/O", + + /* Subtypes here are generated with the DEF_VIRTIO_TYPE macro. */ + .subtypes = SUBTYPE_ARRAY( + &virtio_9p_subtype, + &virtio_balloon_subtype, + &virtio_blk_subtype, + &virtio_console_subtype, + &virtio_crypto_subtype, + &virtio_fs_subtype, + &virtio_gpu_subtype, + &virtio_input_subtype, + &virtio_mem_subtype, + &virtio_net_subtype, + &virtio_rng_subtype, + &virtio_rproc_serial_subtype, + &virtio_scsi_subtype, + &virtio_vsock_subtype, + &virtio_ccw_subtype, + ), + + .type_attribs = ATTRIB_ARRAY(), + + .exit = &virtio_devtype_exit, + + .read_settings = &virtio_devtype_read_settings, + .write_settings = &virtio_devtype_write_settings, +};