Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.5:Update
s390-tools.29120
s390-tools-sles15sp4-06-zdev-Introduce-the-ap-d...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File s390-tools-sles15sp4-06-zdev-Introduce-the-ap-device-type.patch of Package s390-tools.29120
Subject: [PATCH] [FEAT VS1822] zdev: Introduce the ap device type From: Matthew Rosato <mjrosato@linux.ibm.com> Summary: ap_tools: add ap-check and the ap device type to zdev Description: This feature adds multiple components in support of persistent configuration of vfio-ap mediated devices. The ap-check utility is added as a callout function for the mdevctl utility. This allows for meaningful error messages to be presented to end-users when vfio-ap configuration errors are detected while using mdevctl to create/modify vfio-ap mediated devices. Additionally, the 'ap' device type is added to zdev, providing a command-line interface for managing the apmask and aqmask, which determine what AP resources are available for vfio-ap usage. 'chzdev' is updated to allow for modifying the active masks as well as to specify persistent mask values via a udev rule. 'lszdev' is updated to allow for querying of the active and persistent mask values. Upstream-ID: 773d01e674628cd6a0ff49cc3cfe33d78aa587fe Problem-ID: VS1822 Upstream-Description: zdev: Introduce the ap device type The AP device type initially allows only the setting of type attributes 'apmask' and 'aqmask'. Reviewed-by: Tony Krowiak <akrowiak@linux.ibm.com> Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com> Signed-off-by: Jan Hoeppner <hoeppner@linux.ibm.com> Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com> --- zdev/include/ap.h | 40 + zdev/include/namespace.h | 2 zdev/include/path.h | 1 zdev/src/Makefile | 16 zdev/src/ap.c | 1127 +++++++++++++++++++++++++++++++++++++++++++++++ zdev/src/devtype.c | 2 zdev/src/namespace.c | 2 zdev/src/path.c | 6 8 files changed, 1194 insertions(+), 2 deletions(-) --- /dev/null +++ b/zdev/include/ap.h @@ -0,0 +1,40 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * Copyright IBM Corp. 2022 + * + * 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 AP_H +#define AP_H + +#include "lib/util_list.h" + +#define AP_MOD_NAME "ap" +#define AP_NAME "ap" +#define VFIO_AP_MOD_NAME "vfio_ap" + +struct devtype; +struct subtype; +struct namespace; + +extern struct devtype ap_devtype; +extern struct subtype ap_subtype; +extern struct namespace ap_namespace; + +struct ap_node { + struct util_list_node node; + unsigned int id; +}; + +struct mdev_cb_data { + struct util_list *adapters; + struct util_list *domains; + bool typeap; + bool autoconf; + bool found_conflict; +}; + +#endif /* AP_H */ --- a/zdev/include/namespace.h +++ b/zdev/include/namespace.h @@ -36,7 +36,7 @@ struct namespace; struct subtype; /* NULL-terminated list of namespaces. */ -#define NUM_NAMESPACES 5 +#define NUM_NAMESPACES 6 extern struct namespace *namespaces[NUM_NAMESPACES + 1]; /* struct namespace - Definition of a device ID namespace --- a/zdev/include/path.h +++ b/zdev/include/path.h @@ -64,6 +64,7 @@ char *path_get_sys_bus_drv(const char *, 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 *); exit_code_t path_for_each(const char *, exit_code_t (*callback)(const char *, const char *, --- a/zdev/src/Makefile +++ b/zdev/src/Makefile @@ -40,6 +40,9 @@ chzdev_objects += ctc.o ctc_auto.o # LCS devtype chzdev_objects += lcs.o lcs_auto.o +# AP devtype +chzdev_objects += ap.o + # Generic CCW devtype chzdev_objects += generic_ccw.o @@ -70,11 +73,21 @@ lszdev_objects += ctc.o ctc_auto.o # LCS devtype lszdev_objects += lcs.o lcs_auto.o +# AP devtype +lszdev_objects += ap.o + # Generic CCW devtype lszdev_objects += generic_ccw.o all: chzdev lszdev zdev_id +ifneq (${HAVE_LOCKFILE}, 0) +LDLIBS += -llockfile +endif +ifneq (${HAVE_JSONC}, 0) +LDLIBS += -ljson-c +endif + chzdev_usage.c: chzdev_usage.txt $(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \ -e 's/$$/\\n"/g' @@ -90,7 +103,8 @@ lszdev_usage.c: lszdev_usage.txt chzdev.o: chzdev_usage.c lszdev.o: lszdev_usage.c -libs = $(rootdir)/libutil/libutil.a +libs = $(rootdir)/libap/libap.a \ + $(rootdir)/libutil/libutil.a chzdev: $(chzdev_objects) $(libs) lszdev: $(lszdev_objects) $(libs) --- /dev/null +++ b/zdev/src/ap.c @@ -0,0 +1,1127 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * Copyright IBM Corp. 2022 + * + * 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 <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> + +#include "lib/util_libc.h" +#include "lib/util_path.h" +#include "lib/util_list.h" +#include "lib/ap.h" + +#include "attrib.h" +#include "ccw.h" +#include "device.h" +#include "devtype.h" +#include "internal.h" +#include "misc.h" +#include "namespace.h" +#include "path.h" +#include "ap.h" +#include "setting.h" +#include "udev.h" + +#define DEVNAME "AP device" + +static const char default_mask[AP_MASK_SIZE] = + "0x0000000000000000000000000000000000000000000000000000000000000000"; + +static char apmask[AP_MASK_SIZE] = {0}; +static char aqmask[AP_MASK_SIZE] = {0}; +static char p_apmask[AP_MASK_SIZE] = {0}; +static char p_aqmask[AP_MASK_SIZE] = {0}; +static bool valid_p_apmask = false, valid_p_aqmask = false; + +/* + * For 2 input masks (source and target), generate a list of all bits that are + * on in target and off in source (adds) as well as a list of all bits that are + * on in source but off in target (subs). + */ +static void ap_mask_list_changes(const char *source, const char *target, + struct util_list *adds, struct util_list *subs) +{ + int i; + struct ap_node *node; + + for (i = 0; i <= AP_MAX_MASK_VALUE; i++) { + if (ap_test_bit(i, source)) { + if (!ap_test_bit(i, target)) { + node = misc_malloc(sizeof(struct ap_node)); + node->id = i; + util_list_add_tail(subs, node); + } + } else { + if (ap_test_bit(i, target)) { + node = misc_malloc(sizeof(struct ap_node)); + node->id = i; + util_list_add_tail(adds, node); + } + } + } +} + +/* + * For a given character string, convert to unsigned long and validate that + * it is within range. + */ +static bool parse_mask_entry_single(char *entry, unsigned long *val) +{ + char *end; + + *val = strtoul(entry, &end, 0); + if (*val > AP_MAX_MASK_VALUE || *end != '\0') + return false; + return true; +} + +/* + * For a given character string, split it into 2 entries delimited by a '-' + * character and parse each half of the range. + */ +static bool parse_mask_entry_range(char *entry, unsigned long *val, + unsigned long *val2) +{ + char *entry2; + + /* Find range delimeter, convert to 2 null-terminated entries */ + entry2 = strchr(entry, '-'); + *entry2 = '\0'; + entry2++; + + /* Handle invalid range where there is no 2nd number */ + if (*entry2 == '\0') + return false; + + if (!parse_mask_entry_single(entry, val)) + return false; + if (!parse_mask_entry_single(entry2, val2)) + return false; + if (val > val2) + return false; + return true; +} + +/* + * For a given string, parse its contents and modify the provided mask. + * For assignment operations, valid strings are of the format: + * # or #-# + * Otherwise, for modification operations, valid strings are of the format: + * +# or +#-# + * -# or -#-# + */ +static bool parse_mask_entry(char *entry, bool assign, char *mask) +{ + unsigned long val, val2; + bool is_add = true; + + /* + * Modifications must begin with a +/- and assignments cannot begin + * with a +/- + */ + if ((!assign && (*entry != '+' && *entry != '-')) || + (assign && (*entry == '+' || *entry == '-'))) + return false; + + /* + * An assignment is now treated the same as a + modification. + * But for modification operations, skip past the +/- and remember if + * we are doing removal. + */ + if (!assign) { + if (*entry == '-') + is_add = false; + entry++; + /* Handle the case where there's nothing after the +/- */ + if (*entry == '\0') + return false; + } + + /* Next, parse either a single element or a range */ + if (strchr(entry, '-')) { + if (!parse_mask_entry_range(entry, &val, &val2)) + return false; + } else { + if (!parse_mask_entry_single(entry, &val)) + return false; + val2 = val; + } + + /* Set the specified bit(s) accordingly */ + while (val <= val2) { + ap_set_bit(val, mask, is_add); + val++; + } + + return true; +} + +/* + * For an input string, validate that the format of the input is valid, then + * generate the new mask string as well as a list of bits that were added + * for later validation. If all looks valid, change the mask in memory based + * upon the provided input + */ +static bool ap_validate_mask_input(char *mask, const char *input, + struct util_list *adds, + struct util_list *subs) +{ + bool is_assign = false, rc = true; + char *incopy = misc_strdup(input); + char newmask[AP_MASK_SIZE] = {0}; + char *curr, *next; + + /* If we aren't starting with +/-, it's an assignment */ + if (*incopy != '+' && *incopy != '-') { + is_assign = true; + /* Start with all bits OFF */ + strcpy(newmask, default_mask); + } else { + /* Otherwise, input is a list of changes */ + strcpy(newmask, mask); + } + + /* Get the first entry to start -- if there are none, exit on error */ + curr = strtok(incopy, ","); + if (curr == NULL) + goto err_out; + + /* Run the comma-delimited list of entries, updating the mask value */ + do { + /* Ensure we null-terminate the current entry */ + next = strtok(NULL, ","); + if (!parse_mask_entry(curr, is_assign, newmask)) + goto err_out; + curr = next; + } while (curr != NULL); + + /* + * Translate the mask value into a list of additions/removals for later + * conflict analysis. + */ + util_list_init(adds, struct ap_node, node); + ap_mask_list_changes(mask, newmask, adds, subs); + strcpy(mask, newmask); + goto out; + +err_out: + error("Invalid input string %s\n", input); + rc = false; + +out: + free(incopy); + return rc; +} + +static int write_sysfs_mask(const char *path, char *mask) +{ + int rc = 0; + FILE *f; + + f = fopen(path, "w"); + if (!f) { + rc = -1; + goto out; + } + fputs(mask, f); + fclose(f); + +out: + return rc; +} + +static bool ap_read_type_udev(char *ap, char *aq, bool autoconf) +{ + bool retval = false; + char *path = path_get_udev_rule(AP_NAME, NULL, autoconf); + + if (ap_read_udev_masks(path, ap, aq, &valid_p_apmask, &valid_p_aqmask)) + retval = true; + + free(path); + return retval; +} + +static exit_code_t ap_write_type_udev(char *ap, char *aq, bool autoconf) +{ + FILE *fd; + exit_code_t rc; + char *path = path_get_udev_rule(AP_NAME, NULL, autoconf); + + /* Remove if already exists */ + if (util_path_is_reg_file(path)) { + rc = remove_file(path); + if (rc) { + error("Could not remove file %s\n", path); + goto out; + } + } + + /* If there's no masks, no need for a udev file */ + if (!(valid_p_apmask || valid_p_aqmask)) + return EXIT_OK; + + debug("Writing udev rule file %s\n", 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"); + + /* Triggers. */ + fprintf(fd, "ACTION==\"change\", SUBSYSTEM==\"ap\", " + "DEVPATH==\"/devices/ap\", ENV{BINDINGS}==\"complete\", " + "ENV{COMPLETECOUNT}==\"1\", GOTO=\"cfg_ap\"\n"); + fprintf(fd, "GOTO=\"end_ap\"\n\n"); + + /* Begin Configuration */ + fprintf(fd, "LABEL=\"cfg_ap\"\n\n"); + if (valid_p_apmask) + fprintf(fd, "ATTR{../../bus/ap/apmask}=\"%s\"\n", ap); + if (valid_p_aqmask) + fprintf(fd, "ATTR{../../bus/ap/aqmask}=\"%s\"\n", aq); + fprintf(fd, "RUN{builtin}+=\"kmod load %s\"\n", VFIO_AP_MOD_NAME); + + /* Write udev rule epilog. */ + fprintf(fd, "\nLABEL=\"end_ap\"\n"); + + fclose(fd); + +out: + free(path); + return rc; +} + +/* + * AP namespace + */ +static exit_code_t ap_is_id_valid(const char *id, err_t err) +{ + /* AP device doesn't support an ID */ + err_t_print(err, "Error: %s management is not supported\n", + DEVNAME); + + return EXIT_INVALID_ID; +} + +static int ap_cmp_ids(const char *a_str, const char *b_str) +{ + /* AP device doesn't support an ID */ + return EXIT_OK; +} + +static char *ap_normalize_id(const char *id) +{ + /* AP device doesn't support an ID, just return a copy of input */ + return util_strdup(id); +} + +static void *ap_parse_id(const char *id, err_t err) +{ + /* Nothing to be parsed, just use normalized string */ + return ap_normalize_id(id); +} + +static int ap_cmp_parsed_ids(const void *a, const void *b) +{ + return ap_cmp_ids((const char *)a, (const char *)b); +} + +static int ap_qsort_cmp(const void *a_ptr, const void *b_ptr) +{ + const char *a = *((const char **) a_ptr); + const char *b = *((const char **) b_ptr); + + return ap_cmp_ids(a, b); +} + +static exit_code_t ap_is_id_range_valid(const char *range, err_t err) +{ + /* AP device doesn't support an ID */ + err_t_print(err, "Error: %s management is not supported\n", + DEVNAME); + + return EXIT_INVALID_ID; +} + +static unsigned long ap_num_ids_in_range(const char *range) +{ + /* AP device doesn't support an ID */ + return 0; +} + +static bool ap_is_id_in_range(const char *id, const char *range) +{ + /* AP device doesn't support an ID */ + return false; +} + +static void ap_range_start(struct ns_range_iterator *it, + const char *range) +{ + /* AP device doesn't support an ID */ + memset(it, 0, sizeof(struct ns_range_iterator)); +} + +static void ap_range_next(struct ns_range_iterator *it) +{ + /* AP device doesn't support an ID */ +} + +struct namespace ap_namespace = { + .devname = DEVNAME, + .is_id_valid = ap_is_id_valid, + .cmp_ids = ap_cmp_ids, + .normalize_id = ap_normalize_id, + .parse_id = ap_parse_id, + .cmp_parsed_ids = ap_cmp_parsed_ids, + .qsort_cmp = ap_qsort_cmp, + .is_id_range_valid = ap_is_id_range_valid, + .num_ids_in_range = ap_num_ids_in_range, + .is_id_in_range = ap_is_id_in_range, + .range_start = ap_range_start, + .range_next = ap_range_next, +}; + +/* + * AP type attributes + */ +static struct attrib ap_tattr_apmask = { + .name = "apmask", + .title = "Configure Cryptographic Adapter ID availability", + .desc = + "Specify the set of Cryptographic Adapters that will be available\n" + "for host usage. Any bits not enabled for host usage indicate\n" + "adapters that are available for guest passthrough.\n" + "Input can be in the form of a comma-delimited list of adapters\n" + "such as:\n" + " 3,5-10,0x25\n" + "where the listed adapters are set for host usage and the unlisted\n" + "adapters are set for guest passthrough. A range indicates that all\n" + "adapters in this range should be set for host usage.\n\n" + "Alternatively, this attribute can also be specified as a\n" + "comma-delimited list of changes to the current mask such as:\n" + " +0-4,-0xf0\n" + "where each number or range preceded by a '+' will change the current\n" + "mask to set the specified adapters for host usage and each number\n" + "or range preceded by a '-' will set the specified adapters for guest\n" + "passthrough.\n" + "Adapters can be specified in both decimal and hexadecimal.", +}; + +static struct attrib ap_tattr_aqmask = { + .name = "aqmask", + .title = "Configure Cryptographic Queue Index availability", + .desc = + "Specify the set of Cryptographic Queue Indices that will be\n" + "available for host usage. Any bits not enabled for host usage\n" + "indicate a queue index that is available for guest passthrough.\n" + "Input can be in the form of a comma-delimited list of indices\n" + "such as:\n" + " 3,5-10,0x25\n" + "where the listed queue indices are set for host usage and the\n" + "unlisted indices are set for guest passthrough. A range indicates\n" + "that all indices in this range should be set for host usage.\n\n" + "Alternatively, this attribute can also be specified as a\n" + "comma-delimited list of changes to the current mask such as:\n" + " +0-4,-0xf0\n" + "where each number or range preceded by a '+' will change the current\n" + "mask to set the specified indices for host usage and each number\n" + "or range preceded by a '-' will set the specified indices for guest\n" + "passthrough.\n" + "Queue indices can be specified in both decimal and hexadecimal.", +}; + +/* + * AP device attributes - there are currently none. + */ + +/* + * AP subtype methods + */ + +static void ap_st_init(struct subtype *st) +{ + st->devices = device_list_new(st); +} + +static void ap_st_exit(struct subtype *st) +{ + device_list_free(st->devices); +} + +/* Determine if an AP device exists in the active configuration. */ +static bool ap_st_exists_active(struct subtype *st, const char *id) +{ + return false; +} + +static bool ap_st_exists_persistent(struct subtype *st, const char *id) +{ + return false; +} + +static bool ap_st_exists_autoconf(struct subtype *st, const char *id) +{ + return false; +} + +static void ap_st_add_active_ids(struct subtype *st, struct util_list *ids) +{ +} + +static void ap_st_add_persistent_ids(struct subtype *st, struct util_list *ids) +{ +} + +static void ap_st_add_autoconf_ids(struct subtype *st, struct util_list *ids) +{ +} + +/* Read state of an AP device from the active configuration. */ +static exit_code_t ap_st_read_active(struct subtype *st, + struct device *dev, + read_scope_t scope) +{ + return EXIT_OK; +} + +static exit_code_t ap_st_read_persistent(struct subtype *st, + struct device *dev, + read_scope_t scope) +{ + return EXIT_OK; +} + +static exit_code_t ap_st_read_autoconf(struct subtype *st, + struct device *dev, + read_scope_t scope) +{ + return EXIT_OK; +} + +/* Apply the settings of an AP device to the active configuration. */ +static exit_code_t ap_st_configure_active(struct subtype *st, + struct device *dev) +{ + return device_write_active_settings(dev); +} + +static exit_code_t ap_st_configure_persistent(struct subtype *st, + struct device *dev) +{ + return EXIT_OK; +} + +static exit_code_t ap_st_configure_autoconf(struct subtype *st, + struct device *dev) +{ + return EXIT_OK; +} + +static exit_code_t ap_st_deconfigure_active(struct subtype *st, + struct device *dev) +{ + /* No additional step required */ + return EXIT_OK; +} + +static exit_code_t ap_st_deconfigure_persistent(struct subtype *st, + struct device *dev) +{ + /* No additional step required */ + return EXIT_OK; +} + +static exit_code_t ap_st_deconfigure_autoconf(struct subtype *st, + struct device *dev) +{ + /* No additional step required */ + return EXIT_OK; +} + +/* + * AP subtype + */ +struct subtype ap_subtype = { + .super = &subtype_base, + .devtype = &ap_devtype, + .name = AP_NAME, + .title = "Cryptographic Adjunct Processor (AP) device", + + .devname = DEVNAME, + .modules = STRING_ARRAY(AP_MOD_NAME), + .namespace = &ap_namespace, + + .dev_attribs = ATTRIB_ARRAY(), + + .unknown_dev_attribs = 0, + .support_definable = 0, + + .init = &ap_st_init, + .exit = &ap_st_exit, + + .exists_active = &ap_st_exists_active, + .exists_persistent = &ap_st_exists_persistent, + .exists_autoconf = &ap_st_exists_autoconf, + + .add_active_ids = &ap_st_add_active_ids, + .add_persistent_ids = &ap_st_add_persistent_ids, + .add_autoconf_ids = &ap_st_add_autoconf_ids, + + .read_active = &ap_st_read_active, + .read_persistent = &ap_st_read_persistent, + .read_autoconf = &ap_st_read_autoconf, + + .configure_active = &ap_st_configure_active, + .configure_persistent = &ap_st_configure_persistent, + .configure_autoconf = &ap_st_configure_autoconf, + + .deconfigure_active = &ap_st_deconfigure_active, + .deconfigure_persistent = &ap_st_deconfigure_persistent, + .deconfigure_autoconf = &ap_st_deconfigure_autoconf, +}; + +/* + * AP devtype methods + */ +/* Clean up all resources used by devtype object. */ +static void ap_devtype_exit(struct devtype *dt) +{ + setting_list_free(dt->active_settings); + setting_list_free(dt->persistent_settings); +} + +/* + * Turn a full ap/aqmask bitmap into a comma-delimited list of bits + * + * Use a worst-case string size for the setting, which would be a string size + * for a string that has every other bit on, with up to 3 digits per bit plus + * commas. + */ +static char *ap_mask_to_setting(char *value) +{ + int size = ((AP_MAX_MASK_VALUE / 2) + 1) * 4; + char *sval = misc_malloc(size); + char *curr = sval; + bool found_one = false; + int i, rc, start = -1, stop = -1; + + for (i = 0; i <= AP_MAX_MASK_VALUE; i++) { + if (ap_test_bit(i, value)) { + found_one = true; + if (start < 0) { + start = stop = i; + } else if (stop == i - 1) { + stop++; + } else { + /* Write the old range, we have a new one */ + if (start == stop) + /* print a single bit */ + rc = snprintf(curr, size, "%d,", start); + else + /* print a range */ + rc = snprintf(curr, size, "%d-%d,", + start, stop); + curr += rc; + start = stop = i; + } + } + } + + if (found_one) { + /* Print the final entry in the list */ + if (start == stop) + /* print a single bit */ + rc = snprintf(curr, size, "%d", start); + else + /* print a range */ + rc = snprintf(curr, size, "%d-%d", start, stop); + } else { + /* If no bits are on, return an empty string */ + curr[0] = '\0'; + } + + return sval; +} + +static void ap_create_type_setting(struct devtype *dt, + struct setting_list *slist, char *name, + char *value) +{ + struct attrib *a; + struct setting *s; + char *c; + + a = attrib_find(dt->type_attribs, name); + if (a != NULL) { + s = setting_list_find(slist, name); + if (s != NULL) { + free(s->value); + s->value = ap_mask_to_setting(value); + } else { + c = ap_mask_to_setting(value); + s = setting_new(a, NULL, c); + setting_list_add(slist, s); + free(c); + } + } +} + +static exit_code_t ap_devtype_read_settings(struct devtype *dt, config_t config) +{ + exit_code_t rc = EXIT_OK; + + dt->active_settings = setting_list_new(); + dt->persistent_settings = setting_list_new(); + + rc = ap_get_lock(); + if (rc != 0) { + rc = EXIT_RUNTIME_ERROR; + goto out2; + } + + if (SCOPE_ACTIVE(config)) { + /* read apmask and aqmask */ + if (ap_read_sysfs_masks(apmask, aqmask, AP_MASK_SIZE) != 0) { + rc = EXIT_RUNTIME_ERROR; + goto out; + } + dt->active_exists = 1; + } + if (SCOPE_PERSISTENT(config)) { + /* read apmask and aqmask udev */ + if (ap_read_type_udev(p_apmask, p_aqmask, false)) + dt->persistent_exists = 1; + } else if (SCOPE_AUTOCONF(config)) { + /* read apmask and aqmask udev */ + if (ap_read_type_udev(p_apmask, p_aqmask, true)) + dt->persistent_exists = 1; + } + + if (dt->active_exists) { + ap_create_type_setting(dt, dt->active_settings, "apmask", + apmask); + ap_create_type_setting(dt, dt->active_settings, "aqmask", + aqmask); + } + + if (dt->persistent_exists) { + if (valid_p_apmask) + ap_create_type_setting(dt, dt->persistent_settings, + "apmask", p_apmask); + if (valid_p_aqmask) + ap_create_type_setting(dt, dt->persistent_settings, + "aqmask", p_aqmask); + } + +out: + ap_release_lock(); +out2: + return rc; +} + +/* + * Report an error message when the specified configuration will conflict + * with an existing device + */ +static void conflict_error(const char *uuid, unsigned int a, unsigned int d, + bool typeap, bool persistent) +{ + if (persistent) { + if (typeap) { + warnx("persistent apmask conflicts with defined " + "autostart mdev %s APQN %u.%u", uuid, a, d); + } else { + warnx("persistent aqmask conflicts with defined " + "autostart mdev %s APQN %u.%u", + uuid, a, d); + } + } else { + if (typeap) { + warnx("apmask conflicts with mdev %s APQN %u.%u", + uuid, a, d); + } else { + warnx("aqmask conflicts with mdev %s APQN %u.%u", + uuid, a, d); + } + } +} + +/* + * Compare the list of adapters and domains for the system and a vfio-ap + * device, reporting error messages for any conflicts that occur + */ +static int find_apqn_conflicts(const char *uuid, + struct util_list *adapters, + struct util_list *domains, + struct util_list *adapters2, + struct util_list *domains2, + bool typeap, + bool persistent) +{ + struct vfio_ap_node *a, *a2, *d, *d2; + int rc = 0; + + /* Checks for conflicts with the device */ + a = util_list_start(adapters); + a2 = util_list_start(adapters2); + while ((a != NULL) && (a2 != NULL)) { + if (a->id == a2->id) { + d = util_list_start(domains); + d2 = util_list_start(domains2); + while ((d != NULL) && (d2 != NULL)) { + if (d->id == d2->id) { + /* Report error, look for more */ + conflict_error(uuid, a->id, d->id, + typeap, persistent); + rc = -1; + d = util_list_next(domains, d); + d2 = util_list_next(domains2, d2); + } else if (d->id > d2->id) { + d2 = util_list_next(domains2, d2); + } else { + d = util_list_next(domains, d); + } + } + a = util_list_next(adapters, a); + a2 = util_list_next(adapters2, a2); + } else if (a->id > a2->id) { + a2 = util_list_next(adapters2, a2); + } else { + a = util_list_next(adapters, a); + } + } + + return rc; +} + +static exit_code_t check_mask_cfg_cb(const char *path, + const char *filename, + void *data) +{ + struct mdev_cb_data *cb_data = data; + struct vfio_ap_device *dev = NULL; + int rc = 0; + + /* Skip anything that isn't an mdev config */ + if (!is_valid_uuid(filename)) + goto out; + + /* Read the device config */ + dev = vfio_ap_device_new(); + if (vfio_ap_read_device_config(path, dev) != 0) + goto out; + + /* If wrong device type, skip */ + if (strcmp(dev->type, VFIO_AP_TYPE) != 0) + goto out; + + /* If not AUTO device, skip */ + if (dev->manual) + goto out; + + /* Perform mdev-to-system apqn conflict analysis */ + rc = find_apqn_conflicts(filename, cb_data->adapters, cb_data->domains, + dev->adapters, dev->domains, cb_data->typeap, + true); + + if (rc != 0) + cb_data->found_conflict = true; + +out: + if (dev != NULL) + vfio_ap_device_free(dev); + /* Always return 0 so we continue looking for further conflicts */ + return 0; +} + +static exit_code_t check_mask_sysfs_cb(const char *path, + const char *filename, + void *data) +{ + struct mdev_cb_data *cb_data = data; + char *matrix_path; + FILE *f; + char buf[80]; + struct vfio_ap_device *dev; + int rc = 0; + + if (!is_valid_uuid(filename)) + return 0; + + dev = vfio_ap_device_new(); + matrix_path = path_get_vfio_ap_attr(filename, "matrix"); + f = fopen(matrix_path, "r"); + while (fgets(buf, sizeof(buf), f)) + vfio_ap_parse_matrix(dev, buf); + vfio_ap_sort_matrix_results(dev); + fclose(f); + free(matrix_path); + + /* Look for conflicts between the system and this device */ + rc = find_apqn_conflicts(filename, cb_data->adapters, cb_data->domains, + dev->adapters, dev->domains, cb_data->typeap, + false); + + if (rc != 0) + cb_data->found_conflict = true; + + vfio_ap_device_free(dev); + + /* Always return 0 so we continue looking for further conflicts */ + return 0; +} + +/* + * We enter this function to check for mask changes under all circumstances. + * 'persistent' determines whether we are looking at sysfs or config files + * 'adapters' is used for error reporting (adapters vs domains) + */ +static exit_code_t check_mask_changes(struct util_list *a, + struct util_list *d, + bool persistent, + bool typeap, + bool autoconf) +{ + exit_code_t rc = EXIT_OK; + struct mdev_cb_data cb_data; + char *root; + + cb_data.adapters = a; + cb_data.domains = d; + cb_data.found_conflict = false; + cb_data.typeap = typeap; + + if (persistent) { + /* Validate udev changes against mdevctl AUTO configs */ + root = path_get(VFIO_AP_CONFIG_PATH); + if (util_path_is_dir(root)) + rc = path_for_each(root, check_mask_cfg_cb, &cb_data); + } else { + /* Validate immediate changes against active devices */ + root = path_get_vfio_ap_mdev(""); + if (util_path_is_dir(root)) + rc = path_for_each(root, check_mask_sysfs_cb, &cb_data); + } + + if (cb_data.found_conflict) + rc = EXIT_INVALID_CONFIG; + + free(root); + + return rc; +} + +/* To validate changes to the system masks, consider the following set of + * APQNs: + * To validate apmask changes: new_apmask_bits * aqmask + * To validate aqmask changes: apmask * new_aqmask_bits + */ +static exit_code_t ap_check_mask_changes(config_t config, + struct util_list *add_ap, + struct util_list *add_aq, + struct util_list *add_p_ap, + struct util_list *add_p_aq) +{ + int rc = EXIT_OK, rc2 = EXIT_OK, rc3; + struct util_list all_ap; + struct util_list all_aq; + + util_list_init(&all_ap, struct ap_node, node); + util_list_init(&all_aq, struct ap_node, node); + + /* Validate the mask changes against existing vfio-ap devices */ + if (SCOPE_ACTIVE(config) && (!util_list_is_empty(add_ap) || + !util_list_is_empty(add_aq))) { + ap_mask_to_list(apmask, &all_ap); + ap_mask_to_list(aqmask, &all_aq); + rc = check_mask_changes(add_ap, &all_aq, false, true, false); + rc2 = check_mask_changes(&all_ap, add_aq, false, false, false); + ap_list_remove_all(&all_ap); + ap_list_remove_all(&all_aq); + rc = rc == EXIT_OK ? rc2 : rc; + } + if (SCOPE_PERSISTENT(config) && (!util_list_is_empty(add_p_ap) || + !util_list_is_empty(add_p_aq))) { + ap_mask_to_list(p_apmask, &all_ap); + ap_mask_to_list(p_aqmask, &all_aq); + rc2 = check_mask_changes(add_p_ap, &all_aq, true, true, false); + rc3 = check_mask_changes(&all_ap, add_p_aq, true, false, false); + ap_list_remove_all(&all_ap); + ap_list_remove_all(&all_aq); + rc2 = rc2 == EXIT_OK ? rc3 : rc2; + } else if (SCOPE_AUTOCONF(config) && (!util_list_is_empty(add_p_ap) || + !util_list_is_empty(add_p_aq))) { + ap_mask_to_list(p_apmask, &all_ap); + ap_mask_to_list(p_aqmask, &all_aq); + rc2 = check_mask_changes(add_p_ap, &all_aq, true, true, true); + rc3 = check_mask_changes(&all_ap, add_p_aq, true, false, true); + ap_list_remove_all(&all_ap); + ap_list_remove_all(&all_aq); + rc2 = rc2 == EXIT_OK ? rc3 : rc2; + } + + return rc == EXIT_OK ? rc2 : rc; +} + +static exit_code_t ap_devtype_write_settings(struct devtype *dt, + config_t config) +{ + struct setting *s; + char *path; + bool write_ap, write_aq, write_udev; + struct util_list add_ap, add_aq, sub_ap, sub_aq; + struct util_list add_p_ap, add_p_aq, sub_p_ap, sub_p_aq; + exit_code_t rc = EXIT_OK; + /* No kernel or module parameters exist for AP device driver. */ + + util_list_init(&add_ap, struct ap_node, node); + util_list_init(&add_aq, struct ap_node, node); + util_list_init(&sub_ap, struct ap_node, node); + util_list_init(&sub_aq, struct ap_node, node); + util_list_init(&add_p_ap, struct ap_node, node); + util_list_init(&add_p_aq, struct ap_node, node); + util_list_init(&sub_p_ap, struct ap_node, node); + util_list_init(&sub_p_aq, struct ap_node, node); + write_ap = write_aq = write_udev = false; + + rc = ap_get_lock(); + if (rc != 0) { + rc = EXIT_RUNTIME_ERROR; + goto out2; + } + + /* Determine what was specified and if its valid */ + if (SCOPE_ACTIVE(config) && dt->active_settings) { + s = setting_list_find(dt->active_settings, "apmask"); + if (s != NULL && s->specified) { + if (!ap_validate_mask_input(apmask, s->value, + &add_ap, &sub_ap)) { + rc = EXIT_INVALID_SETTING; + goto out; + } + write_ap = true; + } + s = setting_list_find(dt->active_settings, "aqmask"); + if (s != NULL && s->specified) { + if (!ap_validate_mask_input(aqmask, s->value, + &add_aq, &sub_aq)) { + rc = EXIT_INVALID_SETTING; + goto out; + } + write_aq = true; + } + } + + if ((SCOPE_PERSISTENT(config) || SCOPE_AUTOCONF(config)) && + dt->persistent_settings) { + s = setting_list_find(dt->persistent_settings, "apmask"); + if (s != NULL && s->specified) { + if (!ap_validate_mask_input(p_apmask, s->value, + &add_p_ap, &sub_p_ap)) { + rc = EXIT_INVALID_SETTING; + goto out; + } + write_udev = true; + valid_p_apmask = true; + } else if (s != NULL && s->removed) { + write_udev = true; + valid_p_apmask = false; + } + s = setting_list_find(dt->persistent_settings, "aqmask"); + if (s != NULL && s->specified) { + if (!ap_validate_mask_input(p_aqmask, s->value, + &add_p_aq, &sub_p_aq)) { + rc = EXIT_INVALID_SETTING; + goto out; + } + write_udev = true; + valid_p_aqmask = true; + } else if (s != NULL && s->removed) { + write_udev = true; + valid_p_aqmask = false; + } + } + + /* Check for conflicts with existing devices */ + rc = ap_check_mask_changes(config, &add_ap, &add_aq, &add_p_ap, + &add_p_aq); + if (rc != EXIT_OK) + goto out; + + /* Commit the changes to sysfs/udev */ + if (write_ap) { + path = path_get_bus_attr(AP_MOD_NAME, "apmask"); + write_sysfs_mask(path, apmask); + free(path); + } + if (write_aq) { + path = path_get_bus_attr(AP_MOD_NAME, "aqmask"); + write_sysfs_mask(path, aqmask); + free(path); + } + if (write_udev) { + if (SCOPE_PERSISTENT(config)) + ap_write_type_udev(p_apmask, p_aqmask, false); + else if (SCOPE_AUTOCONF(config)) + ap_write_type_udev(p_apmask, p_aqmask, true); + } + +out: + ap_list_remove_all(&add_ap); + ap_list_remove_all(&add_aq); + ap_list_remove_all(&sub_ap); + ap_list_remove_all(&sub_aq); + ap_list_remove_all(&add_p_ap); + ap_list_remove_all(&add_p_aq); + ap_list_remove_all(&sub_p_ap); + ap_list_remove_all(&sub_p_aq); + ap_release_lock(); +out2: + return rc; +} + +/* + * AP devtype. + */ + +struct devtype ap_devtype = { + .name = "ap", + .title = "", /* Only use subtypes. */ + .devname = "AP", + + .subtypes = SUBTYPE_ARRAY( + &ap_subtype, + ), + + .type_attribs = ATTRIB_ARRAY( + &ap_tattr_apmask, + &ap_tattr_aqmask, + ), + + .exit = &ap_devtype_exit, + + .read_settings = &ap_devtype_read_settings, + .write_settings = &ap_devtype_write_settings, +}; --- a/zdev/src/devtype.c +++ b/zdev/src/devtype.c @@ -27,6 +27,7 @@ #include "setting.h" #include "subtype.h" #include "zfcp.h" +#include "ap.h" /* Array of pointers to known device types. */ struct devtype *devtypes[] = { @@ -35,6 +36,7 @@ struct devtype *devtypes[] = { &qeth_devtype, &ctc_devtype, &lcs_devtype, + &ap_devtype, &generic_ccw_devtype, /* Generic types should come last. */ NULL }; --- a/zdev/src/namespace.c +++ b/zdev/src/namespace.c @@ -17,6 +17,7 @@ #include "namespace.h" #include "qeth.h" #include "zfcp_lun.h" +#include "ap.h" struct namespace *namespaces[] = { &ccw_namespace, @@ -24,6 +25,7 @@ struct namespace *namespaces[] = { &qeth_namespace, &ctc_namespace, &lcs_namespace, + &ap_namespace, NULL, }; --- a/zdev/src/path.c +++ b/zdev/src/path.c @@ -398,3 +398,9 @@ char *path_get_scsi_hctl_dev(const char { return path_get("/sys/bus/scsi/devices/%s", hctl); } + +/* Return sysfs path to a bus attribute */ +char *path_get_bus_attr(const char *bus, const char *attr) +{ + return path_get("/sys/bus/%s/%s", bus, attr); +}
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor