diff --git a/debian/changelog b/debian/changelog index 33febaf..402dbc6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,20 @@ +s390-tools (2.12.0-0ubuntu3.2) focal; urgency=medium + + * debian/patches/s390-tools-sru-lp1903984-focal.patch + zcryptstats: Fix handling of partial results with many domains + Thanks to Ingo Franzki (LP: #1903984) + * debian/patches/s390-tools-sru-lp1908371-focal.patch: + zipl command isn't working correctly in combination with the -M + (respectively --mvdump) option. + cherry-picking 4 commits from s390-tools v2.15.1 to v2.14 + Thanks to Stefan Haberland and Sven Schnelle (LP: #1908371) + * debian/patches/s390-tools-sru-lp1902179-focal.patch: + Enabling IPL (boot) from NVMe devices by adding 2 upstream commits + from s390-tools v2.13/2.14 as backports to this v2.12 base. + Thanks to Jason J. Herne (LP: #1902179) + + -- Frank Heimes Fri, 22 Jan 2021 15:52:19 +0100 + s390-tools (2.12.0-0ubuntu3.1) focal; urgency=medium * debian/patches/0082-*.patch .. 0111-*.patch (LP: #1892350) diff --git a/debian/patches/s390-tools-sru-lp1902179-focal.patch b/debian/patches/s390-tools-sru-lp1902179-focal.patch new file mode 100644 index 0000000..91c2dcf --- /dev/null +++ b/debian/patches/s390-tools-sru-lp1902179-focal.patch @@ -0,0 +1,689 @@ +Description: Enabling IPL (boot) from NVMe devices in focal + These three patches are needed to enable IPL (boot on s390x) from NVMe devices + They are already upstream and in the groovy and hirsute versions of the s390-tools. + A cherry pick wasn't cleanly possible, hence these backports that are based on: + 1b65b23b4398 "zipl: Support nvme devices" (went in v2.13.0) + 0472b5ea5c97 "ipl-tools: Add nvme device support to lsreipl/chreipl" (went in v2.14.0) +Author: Jason J. Herne +Origin: backports of: + https://github.com/ibm-s390-tools/s390-tools/commit/1b65b23b43985cb8a1da2ef399ec6def31bbcc69 + https://github.com/ibm-s390-tools/s390-tools/commit/0472b5ea5c97f2c59a938deebe53b7f27e8a9a32 +Bug: IBM Bugzilla 188732 +Bug-IBM: https://bugs.launchpad.net/bugs/1902179 +Forwarded: not-needed +Applied-Upstream: >= 2.14.0 +Reviewed-by: Frank Heimes +Last-Update: 2021-01-22 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/ipl_tools/cmd_chreipl.c ++++ b/ipl_tools/cmd_chreipl.c +@@ -28,12 +28,14 @@ + TT_FCP, + TT_NSS, + TT_NODE, ++ TT_NVME, + }; + + enum reipl_type { + REIPL_FCP, + REIPL_CCW, +- REIPL_NSS ++ REIPL_NSS, ++ REIPL_NVME, + }; + + static const char *const usage_chreipl = +@@ -41,6 +43,7 @@ + "\n" + " chreipl [ccw] [-d] [OPTIONS]\n" + " chreipl [fcp] [-d] [-w] [-l] [OPTIONS]\n" ++" chreipl nvme [-i] [-s] [OPTIONS]\n" + " chreipl [node] [OPTIONS]\n" + " chreipl nss [-n] [OPTIONS]\n" + " chreipl [-h] [-v]\n" +@@ -48,6 +51,7 @@ + "The following re-IPL targets are supported:\n" + " ccw IPL from CCW device\n" + " fcp IPL from FCP device\n" ++" nvme IPL from NVME device\n" + " nss IPL from NSS\n" + " node IPL from device specified by device node or directory\n" + "\n" +@@ -68,6 +72,12 @@ + " -b, --bootprog Bootprog specification\n" + " -L, --loadparm Loadparm specification\n" + "\n" ++"Options for nvme target:\n" ++" -i, --fid PCI Function ID of NVME IPL device (hex)\n" ++" -s --nsid Namespace ID of NVME IPL device (decimal, default 1)\n" ++" -b, --bootprog Bootprog specification\n" ++" -L, --loadparm Loadparm specification\n" ++"\n" + "Options for nss target:\n" + " -n, --name Identifier of the NSS\n" + "\n" +@@ -84,6 +94,10 @@ + char lun[20]; /* 18 character +0x" */ + int lun_set; + char busid[10]; /* Bus ID e.g. 0.0.4711 */ ++ int fid_set; ++ char fid[FID_MAX_LEN]; ++ int nsid_set; ++ char nsid[11]; /* 10 decimal chars + null */ + int busid_set; + char dev[15]; /* Device (e.g. dasda) */ + int dev_set; +@@ -92,10 +106,10 @@ + char bootparms[4096]; + int bootparms_set; + int force_set; +- enum target_type target_type; /* CCW, FCP, NSS or NODE */ ++ enum target_type target_type; /* CCW, FCP, NVME, NSS or NODE */ + int target_type_set; + int target_type_auto_mode; +- enum reipl_type reipl_type; /* CCW, FCP, NSS */ ++ enum reipl_type reipl_type; /* CCW, FCP, NVME, NSS */ + } l; + + static void __noreturn print_usage_chreipl_exit(void) +@@ -226,6 +240,34 @@ + l.wwpn_set = 1; + } + ++static void set_nvme_nsid(const char *nsid) ++{ ++ unsigned long long nsid_tmp; ++ char *endptr; ++ ++ nsid_tmp = strtoull(nsid, &endptr, 10); ++ if (*endptr) ++ ERR_EXIT("NSID \"%s\" is not a decimal number", nsid); ++ snprintf(l.nsid, sizeof(l.nsid), "%08llu", nsid_tmp); ++ l.nsid_set = 1; ++} ++ ++static void set_nvme_fid(const char *fid) ++{ ++ unsigned long long fid_tmp; ++ char *endptr; ++ ++ fid_tmp = strtoull(fid, &endptr, 16); ++ if (*endptr) ++ ERR_EXIT("FID \"%s\" is not a hexadecimal number", fid); ++ snprintf(l.fid, sizeof(l.fid), "0x%08llx", fid_tmp); ++ l.fid_set = 1; ++ ++ /* nsid defaults to 1, if not already set */ ++ if (!l.nsid_set) ++ set_nvme_nsid("1"); ++} ++ + static void parse_fcp_args(char *nargv[], int nargc) + { + /* +@@ -245,6 +287,28 @@ + set_lun(nargv[2]); + } + ++static void parse_nvme_args(char *nargv[], int nargc) ++{ ++ /* ++ * we might be called like this: ++ * chreipl nvme 0x13 1 ++ */ ++ if (l.busid_set || l.fid_set || l.nsid_set || l.dev_set) ++ ERR_EXIT("Use either options or positional parameters"); ++ if (nargc > 2) ++ ERR_EXIT("Too many arguments specified for \"nvme\" re-IPL " ++ "type"); ++ else if (nargc < 1) ++ ERR_EXIT("The \"nvme\" re-IPL type requires function id, and " ++ "optional namespace id"); ++ set_nvme_fid(nargv[0]); ++ ++ if (nargc == 2) ++ set_nvme_nsid(nargv[1]); ++ else ++ set_nvme_nsid("1"); ++} ++ + static void parse_ccw_args(char *nargv[], int nargc) + { + /* +@@ -285,6 +349,13 @@ + dev_name[i] = 0; + } + ++static void dev_from_part_nvme(char *dev_name) ++{ ++ char *delim = strrchr(dev_name, 'p'); ++ if (delim) ++ *delim = 0; ++} ++ + static int set_reipl_type(const char *dev_name) + { + if (strncmp(dev_name, "dasd", strlen("dasd")) == 0 || +@@ -292,11 +363,18 @@ + l.reipl_type = REIPL_CCW; + else if (strncmp(dev_name, "sd", strlen("sd")) == 0) + l.reipl_type = REIPL_FCP; ++ else if (strncmp(dev_name, "nvme", strlen("nvme")) == 0) ++ l.reipl_type = REIPL_NVME; + else + return -1; + + util_strlcpy(l.dev, dev_name, sizeof(l.dev)); +- dev_from_part(l.dev); ++ ++ if (l.reipl_type == REIPL_NVME) ++ dev_from_part_nvme(l.dev); ++ else ++ dev_from_part(l.dev); ++ + l.dev_set = 1; + return 0; + } +@@ -399,6 +477,9 @@ + case TT_FCP: + parse_fcp_args(nargv, nargc); + break; ++ case TT_NVME: ++ parse_nvme_args(nargv, nargc); ++ break; + case TT_CCW: + parse_ccw_args(nargv, nargc); + break; +@@ -420,6 +501,14 @@ + "and LUN"); + } + ++static void check_nvme_opts(void) ++{ ++ if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set) ++ ERR_EXIT("Invalid option for \"nvme\" target specified"); ++ if (!(l.fid_set && l.nsid_set)) ++ ERR_EXIT("The \"nvme\" target requires FID, and optional NSID"); ++} ++ + static void check_ccw_opts(void) + { + if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set) +@@ -469,6 +558,8 @@ + { "device", required_argument, NULL, 'd' }, + { "lun", required_argument, NULL, 'l' }, + { "wwpn", required_argument, NULL, 'w' }, ++ { "fid", required_argument, NULL, 'i' }, ++ { "nsid", required_argument, NULL, 's' }, + { "loadparm", required_argument, NULL, 'L' }, + { "name", required_argument, NULL, 'n' }, + { "bootparms", required_argument, NULL, 'p' }, +@@ -476,7 +567,7 @@ + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; +- static const char optstr[] = "hcd:vw:l:fL:b:n:p:"; ++ static const char optstr[] = "hcd:vw:l:fL:b:n:p:i:s:"; + + /* dont run without any argument */ + if (argc == 1) +@@ -488,6 +579,8 @@ + set_target_type(TT_CCW, 0); + else if (strcmp(argv[1], "nss") == 0) + set_target_type(TT_NSS, 0); ++ else if (strcmp(argv[1], "nvme") == 0) ++ set_target_type(TT_NVME, 0); + else if (strcmp(argv[1], "node") == 0) + set_target_type(TT_NODE, 0); + else +@@ -500,9 +593,15 @@ + case 'd': + set_device(optarg); + break; ++ case 'i': ++ set_nvme_fid(optarg); ++ break; + case 'l': + set_lun(optarg); + break; ++ case 's': ++ set_nvme_nsid(optarg); ++ break; + case 'w': + set_wwpn(optarg); + break; +@@ -650,6 +749,40 @@ + print_fcp(0, 0); + } + ++static void chreipl_nvme(void) ++{ ++ check_nvme_opts(); ++ ++ if (!nvme_is_device(l.fid, l.nsid) && !l.force_set) { ++ ERR_EXIT("Could not find NVME device with fid %s and nsid %s", ++ l.fid, l.nsid); ++ } ++ check_exists("reipl/nvme/fid", "\"nvme\" re-IPL target"); ++ ++ if (l.bootparms_set && strlen(l.bootparms) > BOOTPARMS_FCP_MAX) { ++ ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", ++ strlen(l.bootparms), BOOTPARMS_FCP_MAX); ++ } ++ ++ write_str_optional(l.loadparm, "reipl/nvme/loadparm", l.loadparm_set, ++ "loadparm"); ++ write_str_optional(l.bootparms, "reipl/nvme/scp_data", l.bootparms_set, ++ "boot parameters"); ++ write_str(l.fid, "reipl/nvme/fid"); ++ write_str(l.nsid, "reipl/nvme/nsid"); ++ /* ++ * set the boot record logical block address. Master boot ++ * record. It is always 0 for Linux ++ */ ++ write_str("0", "reipl/nvme/br_lba"); ++ if (!l.bootprog_set) ++ sprintf(l.bootprog, "0"); ++ write_str(l.bootprog, "reipl/nvme/bootprog"); ++ write_str("nvme", "reipl/reipl_type"); ++ ++ print_nvme(0, 0); ++} ++ + static void chreipl_nss(void) + { + check_nss_opts(); +@@ -690,6 +823,13 @@ + l.busid_set = 1; + chreipl_fcp(); + break; ++ case REIPL_NVME: ++ nvme_fid_get(l.dev, l.fid); ++ l.fid_set = 1; ++ nvme_nsid_get(l.dev, l.nsid); ++ l.nsid_set = 1; ++ chreipl_nvme(); ++ break; + default: + ERR_EXIT("Internal error: chreipl_node"); + } +@@ -705,6 +845,9 @@ + case TT_FCP: + chreipl_fcp(); + break; ++ case TT_NVME: ++ chreipl_nvme(); ++ break; + case TT_NSS: + chreipl_nss(); + break; +--- a/ipl_tools/cmd_lsreipl.c ++++ b/ipl_tools/cmd_lsreipl.c +@@ -81,6 +81,35 @@ + print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data"); + } + ++void print_nvme(int show_ipl, int dump) ++{ ++ char *dir = show_ipl ? "ipl" : "reipl/nvme"; ++ char *path_bootparms = show_ipl ? "/sys/firmware/ipl/scp_data" : ++ "/sys/firmware/reipl/nvme/scp_data"; ++ char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" : ++ "/sys/firmware/reipl/nvme/loadparm"; ++ char loadparm[9], loadparm_path[PATH_MAX]; ++ ++ if (dump) ++ printf("%-12s nvme_dump\n", get_ipl_banner(show_ipl)); ++ else ++ printf("%-12s nvme\n", get_ipl_banner(show_ipl)); ++ ++ print_fw_str("FID: %s\n", dir, "fid"); ++ print_fw_str("NSID: %s\n", dir, "nsid"); ++ print_fw_str("bootprog: %s\n", dir, "bootprog"); ++ print_fw_str("br_lba: %s\n", dir, "br_lba"); ++ if (access(path_loadparm, R_OK) == 0) { ++ sprintf(loadparm_path, "%s/%s", dir, "loadparm"); ++ read_fw_str(loadparm, loadparm_path, sizeof(loadparm)); ++ if (strcmp(loadparm, " ") == 0) ++ loadparm[0] = 0; ++ printf("Loadparm: \"%s\"\n", loadparm); ++ } ++ if (access(path_bootparms, R_OK) == 0) ++ print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data"); ++} ++ + void print_ccw(int show_ipl) + { + char loadparm[9], loadparm_path[PATH_MAX]; +@@ -149,6 +178,10 @@ + print_fcp(l.ipl_set, 0); + else if (strcmp(reipl_type_str, "fcp_dump") == 0) + print_fcp(l.ipl_set, 1); ++ else if (strcmp(reipl_type_str, "nvme") == 0) ++ print_nvme(l.ipl_set, 0); ++ else if (strcmp(reipl_type_str, "nvme_dump") == 0) ++ print_nvme(l.ipl_set, 1); + else if (strcmp(reipl_type_str, "ccw") == 0) + print_ccw(l.ipl_set); + else if (strcmp(reipl_type_str, "nss") == 0) +--- a/ipl_tools/ipl_tools.h ++++ b/ipl_tools/ipl_tools.h +@@ -43,6 +43,7 @@ + + extern void print_ccw(int show_ipl); + extern void print_fcp(int show_ipl, int dump); ++extern void print_nvme(int show_ipl, int dump); + extern void print_nss(int show_ipl); + + /* +@@ -71,6 +72,16 @@ + extern void fcp_busid_get(const char *device, char *devno); + + /* ++ * NVME ++ */ ++#define FID_MAX_LEN 11 /* 8 characters + 0x + null */ ++#define NVME_PATH_MAX (PATH_MAX + NAME_MAX + 1) ++ ++extern void nvme_fid_get(const char *device, char *fid); ++extern void nvme_nsid_get(const char *device, char *nsid); ++int nvme_is_device(char *fid_str, char *nsid_str); ++ ++/* + * CCW + */ + extern int ccw_is_device(const char *devno); +--- a/ipl_tools/Makefile ++++ b/ipl_tools/Makefile +@@ -6,7 +6,7 @@ + + libs = $(rootdir)/libutil/libutil.a + +-objects = main.o ccw.o fcp.o system.o shutdown.o \ ++objects = main.o ccw.o fcp.o nvme.o system.o shutdown.o \ + cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o + + chreipl: $(objects) $(libs) +@@ -40,4 +40,3 @@ + $(DESTDIR)$(MANDIR)/man8 + + .PHONY: all install clean +- +--- a/ipl_tools/man/chreipl.8 ++++ b/ipl_tools/man/chreipl.8 +@@ -39,6 +39,9 @@ + .RB "- " fcp : + Specify a FCP device for reboot + .TP ++.RB "- " nvme : ++Specify an NVMe device for reboot ++.TP + .RB "- " nss : + Specify a named saved system (NSS) for reboot + .TP +@@ -118,6 +121,11 @@ + append kernel parameter "mem=" to restrict memory to 512 MB: + + \fB# chreipl 0.0.1700 0x500507630300c562 0x401040b300000000 -p "mem=512M"\fP ++ ++4. Next time reboot from the NVMe device with function id 0x13, namespace 1: ++ ++\fB# chreipl nvme 0x13 1 ++ + .SH ccw + Use the ccw re-IPL target for DASD devices that are accessed by the hardware + using channel command word (CCW) channels. +@@ -192,6 +200,44 @@ + .br + + \fB# chreipl fcp -d 0.0.1700 -w 0x5005076... -l 0x401040b3... -b 2\fP ++.SH nvme ++Use the nvme re-IPL target for specifying an NVMe disk for reboot. ++.TP ++.BR "\-i" " or " "\-\-fid" ++PCI Function ID of NVME IPL device (hex). ++ ++.TP ++.BR "\-s" " or " "\-\-nsid" ++Namespace ID of the NVME IPL device (decimal, default 1). ++ ++.TP ++.BR "\-b" " or " "\-\-bootprog" ++Specifies an entry in the boot configuration by defining the IPL boot ++program selector. If omitted, '0' will be used. ++ ++.TP ++.BR "\-L" " or " "\-\-loadparm" ++The loadparm for the nvme re-IPL target is not used to control the boot ++configuration that is defined by the ++.BR zipl (8) ++boot menu. Instead it can be used to control higher level boot loaders ++like GRUB. For more details refer to distribution specific documentation. ++ ++.PP ++\fBExamples:\fP ++.br ++ ++1. Next time reboot from the NVMe disk with function-id 0x13 and namespace 1: ++.br ++ ++\fB# chreipl nvme 0x13 1\fP ++.br ++ ++2. Use same configuration as (1) but choose boot program selector 2 and ++use options instead of positional parameters: ++.br ++ ++\fB# chreipl nvme -i 0x13 -s 1 -b 2\fP + .SH nss + Use the nss re-IPL target to specify z/VM named saved systems (NSS) for + reboot. +@@ -206,7 +252,7 @@ + + \fB# chreipl nss LINUX1\fP + .SH node +-You can identify DASD or SCSI re-IPL devices indirectly through a device ++You can identify DASD or SCSI, or NVMe re-IPL devices indirectly through a device + node or directory. The chreipl tool then determines the information + that you would otherwise have to specify with the ccw or fcp target. + .PP +@@ -229,6 +275,11 @@ + + \fB# chreipl node /mnt/boot\fP + ++4. Next time reboot from the NVMe device represented by /dev/nvme0n1 ++.br ++ ++\fB# chreipl node /dev/nvme0n1\fP ++ + .SH SEE ALSO + .BR lsreipl (8), + .BR zipl (8), +--- /dev/null ++++ b/ipl_tools/nvme.c +@@ -0,0 +1,169 @@ ++/* ++ * ipl_tools - Linux for System z reipl and shutdown tools ++ * ++ * NVMe device functions ++ * ++ * Copyright IBM Corp. 2020 ++ * ++ * 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 "lib/util_libc.h" ++#include "lib/util_file.h" ++#include "ipl_tools.h" ++ ++/* ++ * Return the fid of a device ++ */ ++void nvme_fid_get(const char *device, char *fid) ++{ ++ char path[PATH_MAX], buf[FID_MAX_LEN]; ++ ++ snprintf(path, PATH_MAX, "/sys/block/%s/device/device/function_id", ++ device); ++ if (util_file_read_line(buf, FID_MAX_LEN, path)) ++ ERR_EXIT_ERRNO("Could not read from \"%s\"", path); ++ ++ util_strlcpy(fid, buf, FID_MAX_LEN); ++} ++/* ++ * Return the nsid of a device ++ */ ++void nvme_nsid_get(const char *device, char *nsid) ++{ ++ char path[PATH_MAX], buf[FID_MAX_LEN]; ++ ++ snprintf(path, PATH_MAX, "/sys/block/%s/nsid", device); ++ if (util_file_read_line(buf, FID_MAX_LEN, path)) ++ ERR_EXIT_ERRNO("Could not read from \"%s\"", path); ++ ++ util_strlcpy(nsid, buf, FID_MAX_LEN); ++} ++ ++static int next_entry(DIR *dir, char *in_path, char *out_path, ++ unsigned char entry_type) ++{ ++ struct dirent *dirent; ++ char temp_path[NVME_PATH_MAX]; ++ ++ while ((dirent = readdir(dir)) != NULL) { ++ if (strcmp(dirent->d_name, ".") == 0 || ++ strcmp(dirent->d_name, "..") == 0 || ++ dirent->d_type != entry_type) ++ continue; ++ ++ /* Resolve the symlink, if needed */ ++ if (dirent->d_type == DT_LNK) { ++ snprintf(temp_path, sizeof(temp_path), "%s/%s", in_path, ++ dirent->d_name); ++ if (!realpath(temp_path, out_path)) ++ ERR_EXIT_ERRNO("Could not resolve link %s", ++ temp_path); ++ return 1; ++ } ++ ++ snprintf(out_path, NVME_PATH_MAX, "%s/%s", in_path, ++ dirent->d_name); ++ return 1; ++ } ++ return 0; ++} ++ ++static int nvme_getdev_by_fid(char *fidstr, char *devpath) ++{ ++ char temp_path[PATH_MAX+19], real_path[PATH_MAX]; ++ char *sys_path = "/sys/class/nvme"; ++ u_int64_t target_fid, curfid; ++ DIR *dir; ++ char *end; ++ int rc = -1; ++ ++ target_fid = strtoul(fidstr, &end, 16); ++ if (*end) ++ ERR_EXIT("Invalid function_id given %s", fidstr); ++ ++ dir = opendir(sys_path); ++ if (!dir) ++ ERR_EXIT("Could not open %s", sys_path); ++ ++ errno = 0; ++ while (next_entry(dir, sys_path, real_path, DT_LNK)) { ++ snprintf(temp_path, sizeof(temp_path), "%s/%s", real_path, ++ "device/function_id"); ++ if (access(temp_path, F_OK)) ++ continue; ++ ++ if (util_file_read_ul(&curfid, 16, temp_path)) ++ ERR_EXIT("Invalid function_id found in %s", temp_path); ++ ++ if (curfid == target_fid) { ++ strncpy(devpath, real_path, PATH_MAX); ++ rc = 0; ++ break; ++ } ++ } ++ ++ closedir(dir); ++ return rc; ++} ++ ++static int nvme_getdev_by_nsid(char *nsid_str, char *path, char *dev_path) ++{ ++ char full_path[NVME_PATH_MAX+1], nsid_path[sizeof(full_path)+5]; ++ char *end; ++ u_int64_t nsid, curnsid; ++ DIR *dir; ++ ++ nsid = strtoul(nsid_str, &end, 10); ++ if (*end) ++ ERR_EXIT_ERRNO("Invalid namespace id given %s", nsid_str); ++ ++ dir = opendir(path); ++ if (!dir) ++ ERR_EXIT_ERRNO("Could not open %s", path); ++ ++ errno = 0; ++ while (next_entry(dir, path, full_path, DT_DIR)) { ++ snprintf(nsid_path, sizeof(nsid_path), "%s/%s", full_path, ++ "nsid"); ++ if (access(nsid_path, F_OK)) ++ continue; ++ ++ if (util_file_read_ul(&curnsid, 10, nsid_path)) ++ ERR_EXIT("Invalid namespace id found in %s", nsid_path); ++ ++ if (curnsid == nsid) { ++ strncpy(dev_path, full_path, NVME_PATH_MAX+1); ++ closedir(dir); ++ return 0; ++ } ++ } ++ closedir(dir); ++ return -1; ++} ++ ++static int nvme_getdev(char *fid_str, char *nsid_str, char *dev_path) ++{ ++ char path_tmp[NVME_PATH_MAX]; ++ ++ if (nvme_getdev_by_fid(fid_str, path_tmp)) ++ return -1; ++ ++ return nvme_getdev_by_nsid(nsid_str, path_tmp, dev_path); ++} ++ ++/* ++ * Check if the specified fid and nsid leads to a valid nvme device ++ */ ++int nvme_is_device(char *fid_str, char *nsid_str) ++{ ++ char path_tmp[NVME_PATH_MAX+1]; ++ ++ return !(nvme_getdev(fid_str, nsid_str, path_tmp)); ++} +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -421,6 +421,12 @@ + data->device = stats.st_rdev; + data->partnum = 0; + } ++ /* NVMe path, driver name is 'blkext' */ ++ } else if (strcmp(data->drv_name, "blkext") == 0) { ++ data->devno = -1; ++ data->type = disk_type_scsi; ++ data->partnum = stats.st_rdev & SCSI_PARTN_MASK; ++ data->device = stats.st_rdev & ~SCSI_PARTN_MASK; + } else { + /* Driver name is unknown */ + error_reason("Unsupported device driver '%s'", data->drv_name); diff --git a/debian/patches/s390-tools-sru-lp1903984-focal.patch b/debian/patches/s390-tools-sru-lp1903984-focal.patch new file mode 100644 index 0000000..fc4952f --- /dev/null +++ b/debian/patches/s390-tools-sru-lp1903984-focal.patch @@ -0,0 +1,49 @@ +Description: zcryptstats fails + The zcryptstats tool reports data for only the last two domain IDs. + Further, it hangs if one collect data for a complete crypto card, + and then one has to kill the ssh session to get back. + Patch/commit is: + cf2311f cf2311f1f1de17435b49ba8c8697be91705ba031 + "zcryptstats: Fix handling of partial results with many domains" + - zconf/zcrypt/zcryptstats.c: code fix to pass the correct next-domain + to the subsequent CHSC call of a partial response +Author: Ingo Franzki +Origin: https://github.com/ibm-s390-tools/s390-tools/commit/cf2311f1f1de17435b49ba8c8697be91705ba031 +Bug-IBM: IBM Bugzilla 189183 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1903984 +Forwarded: not-needed +Applied-Upstream: upstream accepted with > v2.15.1 +Reviewed-by: Frank Heimes +Last-Update: 2021-01-19 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/zconf/zcrypt/zcryptstats.c ++++ b/zconf/zcrypt/zcryptstats.c +@@ -1178,8 +1178,14 @@ + scdmd_area.request.header.code = 0x102d; + scdmd_area.request.header.length = + sizeof(struct chsc_scdmd_request); +- scdmd_area.request.first_drid.ap_index = card; +- scdmd_area.request.first_drid.domain_index = g.min_domain; ++ if (scdmd_area.response.p) { ++ scdmd_area.request.first_drid = ++ scdmd_area.response.crid; ++ } else { ++ scdmd_area.request.first_drid.ap_index = card; ++ scdmd_area.request.first_drid.domain_index = ++ g.min_domain; ++ } + scdmd_area.request.last_drid.ap_index = card; + scdmd_area.request.last_drid.domain_index = g.max_domain; + scdmd_area.request.s = 1; +@@ -1217,10 +1223,6 @@ + rc = process_apqn_measurement_data(&scdmd_area); + if (rc != 0) + break; +- +- if (scdmd_area.response.p) +- scdmd_area.request.first_drid = +- scdmd_area.response.crid; + } while (scdmd_area.response.p); + + return rc; diff --git a/debian/patches/s390-tools-sru-lp1908371-focal.patch b/debian/patches/s390-tools-sru-lp1908371-focal.patch new file mode 100644 index 0000000..7ba3725 --- /dev/null +++ b/debian/patches/s390-tools-sru-lp1908371-focal.patch @@ -0,0 +1,402 @@ +Description: The zipl command doesn't work correctly in combination with the -M + (respectively --mvdump) option. There is an error with the preparation of + multi-volume dumps, since the bus-ID for the first disk partition that is + listed in file dump_conf (DEVLIST) isn't found. + - include/lib/util_sys.h: declaration of new util_sys_* functions + - include/lib/util_file.h: declaration of two new util_file_read_* functions + - libutil/util_sys.c: implementation of new util_sys_* functions; + getting minor and major device number now based on base device + and prechecking if base device available util_file_read_* functions + - libutil/util_file.c: implementation of two new + - zipl/src/disk.c: removing blkext_* function set and changing code to + use the new util_sys_* functions +Author: Stefan Haberland , Sven Schnelle +Origin: https://github.com/ibm-s390-tools/s390-tools/commit/fa7a4dafa3d89b2e8787de102e4362e895d44b05 + https://github.com/ibm-s390-tools/s390-tools/commit/aa8c2945cc7b047c626e75817c6a3e3924413023 + https://github.com/ibm-s390-tools/s390-tools/commit/6802b86414b48c4f118da09c7bf7ee142a459fa0 + https://github.com/ibm-s390-tools/s390-tools/commit/37348ef662a7052dae798e500ca8c9b769fff3e6 +Bug-IBM: IBM Bugzilla 187221 +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1908371 +Forwarded: not-needed +Applied-Upstream: >= v2.15.1 +Reviewed-by: Frank Heimes +Last-Update: 2020-12-22 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/include/lib/util_file.h ++++ b/include/lib/util_file.h +@@ -12,8 +12,10 @@ + #define LIB_UTIL_FILE_H + + int util_file_read_line(char *str, size_t size, const char *fmt, ...); ++int util_file_read_i(int *val, int base, const char *fmt, ...); + int util_file_read_l(long *val, int base, const char *fmt, ...); + int util_file_read_ll(long long *val, int base, const char *fmt, ...); ++int util_file_read_ui(unsigned int *val, int base, const char *fmt, ...); + int util_file_read_ul(unsigned long *val, int base, const char *fmt, ...); + int util_file_read_ull(unsigned long long *val, int base, const char *fmt, ...); + +--- a/include/lib/util_sys.h ++++ b/include/lib/util_sys.h +@@ -12,6 +12,11 @@ + #ifndef LIB_UTIL_SYS_H + #define LIB_UTIL_SYS_H + ++#include ++ + int util_sys_get_dev_addr(const char *dev, char *addr); ++bool util_sys_dev_is_partition(dev_t dev); ++int util_sys_get_partnum(dev_t dev); ++int util_sys_get_base_dev(dev_t dev, dev_t *base_dev); + + #endif /** LIB_UTIL_SYS_H @} */ +--- a/libutil/util_file.c ++++ b/libutil/util_file.c +@@ -282,6 +282,44 @@ + } + + /** ++ * Read a file and convert it to signed int according to given base ++ * ++ * @param[out] val Buffer for value ++ * @param[in] base Base for conversion, either 8, 10, or 16 ++ * @param[in] fmt Format string for generation of the path name ++ * @param[in] ... Parameters for format string ++ * ++ * @retval 0 Integer has been read correctly ++ * @retval -1 Error while reading file ++ */ ++int util_file_read_i(int *val, int base, const char *fmt, ...) ++{ ++ char path[PATH_MAX], buf[512]; ++ va_list ap; ++ int count; ++ ++ /* Construct the file name */ ++ UTIL_VSPRINTF(path, fmt, ap); ++ ++ if (file_gets(buf, sizeof(buf), path)) ++ return -1; ++ switch (base) { ++ case 8: ++ count = sscanf(buf, "%do", val); ++ break; ++ case 10: ++ count = sscanf(buf, "%dd", val); ++ break; ++ case 16: ++ count = sscanf(buf, "%dx", val); ++ break; ++ default: ++ util_panic("Invalid base: %d\n", base); ++ } ++ return (count == 1) ? 0 : -1; ++} ++ ++/** + * Read a file and convert it to signed long according to given base + * + * @param[out] val Buffer for value +@@ -353,6 +391,44 @@ + break; + default: + util_panic("Invalid base: %d\n", base); ++ } ++ return (count == 1) ? 0 : -1; ++} ++ ++/** ++ * Read a file and convert it to unsigned int according to given base ++ * ++ * @param[out] val Buffer for value ++ * @param[in] base Base for conversion, either 8, 10, or 16 ++ * @param[in] fmt Format string for generation of the path name ++ * @param[in] ... Parameters for format string ++ * ++ * @retval 0 Integer has been read correctly ++ * @retval -1 Error while reading file ++ */ ++int util_file_read_ui(unsigned int *val, int base, const char *fmt, ...) ++{ ++ char path[PATH_MAX], buf[512]; ++ va_list ap; ++ int count; ++ ++ /* Construct the file name */ ++ UTIL_VSPRINTF(path, fmt, ap); ++ ++ if (file_gets(buf, sizeof(buf), path)) ++ return -1; ++ switch (base) { ++ case 8: ++ count = sscanf(buf, "%uo", val); ++ break; ++ case 10: ++ count = sscanf(buf, "%uu", val); ++ break; ++ case 16: ++ count = sscanf(buf, "%ux", val); ++ break; ++ default: ++ util_panic("Invalid base: %d\n", base); + } + return (count == 1) ? 0 : -1; + } +--- a/libutil/util_sys.c ++++ b/libutil/util_sys.c +@@ -11,17 +11,114 @@ + + #include + #include ++#include + #include ++#include + #include + #include + #include + #include + ++#include "lib/util_file.h" ++#include "lib/util_libc.h" + #include "lib/util_path.h" + #include "lib/util_sys.h" + + /* lstat() doesn't work for sysfs files, a fixed size is therefore inevitable */ + #define READLINK_SIZE 256 ++#define PAGE_SIZE 4096 ++ ++/** ++ * Return the partition number of a given partition. ++ * ++ * @param[in] dev Device node of interest ++ * ++ * @retval int Partition number of the device ++ * @retval -1 Error when trying to read the partition number. ++ */ ++int util_sys_get_partnum(dev_t dev) ++{ ++ int partnum = -1; ++ char *path; ++ ++ path = util_path_sysfs("dev/block/%u:%u/partition", ++ major(dev), minor(dev)); ++ if (util_file_read_i(&partnum, 10, path)) { ++ warnx("Could not read from path '%s'", path); ++ goto out; ++ } ++ if (partnum <= 0) { ++ warnx("Bad partition number in '%s'", path); ++ partnum = -1; ++ goto out; ++ } ++ ++out: ++ free(path); ++ return partnum; ++} ++ ++/** ++ * Determine if the given device is a partition. ++ * ++ * @param[in] dev Device node of interest ++ * ++ * @retval true Device is partition ++ * @retval false Device is not a partition ++ */ ++bool util_sys_dev_is_partition(dev_t dev) ++{ ++ bool is_part; ++ char *path; ++ ++ path = util_path_sysfs("dev/block/%u:%u/partition", ++ major(dev), minor(dev)); ++ is_part = util_path_exists(path); ++ free(path); ++ ++ return is_part; ++} ++ ++/** ++ * Determine base device ++ * ++ * This function determines the base device \p base_dev of a given ++ * device \p dev. If \p dev is a base device, \p base_dev becomes \p dev. ++ * ++ * @param[in] dev Device node of interest ++ * @param[out] base_dev Identified base device ++ * ++ * @retval 0 Success ++ * @retval -1 Error while reading device information or ++ * constructed path ++ */ ++int util_sys_get_base_dev(dev_t dev, dev_t *base_dev) ++{ ++ int base_major, base_minor; ++ char buf[PAGE_SIZE]; ++ char *path; ++ ++ /* check if the device already is a base device */ ++ if (!util_sys_dev_is_partition(dev)) { ++ *base_dev = makedev(major(dev), minor(dev)); ++ return 0; ++ } ++ path = util_path_sysfs("dev/block/%d:%d/../dev", ++ major(dev), minor(dev)); ++ if (util_file_read_line(buf, sizeof(buf), path)) { ++ warnx("Could not read from path '%s'", path); ++ free(path); ++ return -1; ++ } ++ free(path); ++ if (sscanf(buf, "%i:%i", &base_major, &base_minor) != 2) { ++ warn("Could not parse major:minor from string '%s'", buf); ++ return -1; ++ } ++ *base_dev = makedev(base_major, base_minor); ++ ++ return 0; ++} + + /** + * Identify device address +@@ -44,13 +141,17 @@ + unsigned int maj, min; + struct stat s; + ssize_t len; ++ dev_t base; + char *path; + + if (stat(dev, &s) != 0) + return -1; + +- maj = major(s.st_rdev); +- min = minor(s.st_rdev); ++ if (util_sys_get_base_dev(s.st_rdev, &base)) ++ return -1; ++ ++ maj = major(base); ++ min = minor(base); + + if (S_ISBLK(s.st_mode)) + path = util_path_sysfs("dev/block/%u:%u/device", maj, min); +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -25,6 +25,7 @@ + #include + + #include "lib/util_proc.h" ++#include "lib/util_sys.h" + + #include "disk.h" + #include "error.h" +@@ -89,88 +90,6 @@ + return 0; + } + +-static int blkext_get_partnum(dev_t dev) +-{ +- char path[PATH_MAX], *buf; +- int dev_major, dev_minor, partnum = -1; +- +- dev_major = major(dev); +- dev_minor = minor(dev); +- snprintf(path, PATH_MAX, "/sys/dev/block/%d:%d/partition", +- dev_major, dev_minor); +- +- if (misc_read_special_file(path, &buf, NULL, 1)) { +- error_text("Could not read from path '%s'", path); +- return -1; +- } +- +- partnum = atoi(buf); +- free(buf); +- if (partnum < 0) { +- error_text("Bad partition number in '%s'", path); +- return -1; +- } +- +- return partnum; +-} +- +-static int blkext_is_base_device(dev_t dev) +-{ +- int dev_major, dev_minor; +- char path[PATH_MAX]; +- struct stat stats; +- +- dev_major = major(dev); +- dev_minor = minor(dev); +- +- snprintf(path, PATH_MAX, "/sys/dev/block/%d:%d/partition", +- dev_major, dev_minor); +- return (stat(path, &stats)); +-} +- +-static int blkext_get_base_dev(dev_t dev, dev_t *base_dev) +-{ +- int base_major, base_minor; +- char dev_path[PATH_MAX], base_path[PATH_MAX]; +- char *temp_path, *buf; +- +- misc_asprintf(&temp_path, "/sys/dev/block/%d:%d", major(dev), minor(dev)); +- if (!realpath(temp_path, dev_path)) { +- error_reason(strerror(errno)); +- error_text("Could not resolve link %s", temp_path); +- free(temp_path); +- return -1; +- } +- free(temp_path); +- +- misc_asprintf(&temp_path, "%s/..", dev_path); +- if (!realpath(temp_path, base_path)) { +- error_reason(strerror(errno)); +- error_text("Could not resolve path %s", temp_path); +- free(temp_path); +- return -1; +- } +- free(temp_path); +- +- misc_asprintf(&temp_path, "%s/dev", base_path); +- if (misc_read_special_file(temp_path, &buf, NULL, 1)) { +- error_text("Could not read from path '%s'", temp_path); +- free(temp_path); +- return -1; +- } +- free(temp_path); +- +- if (sscanf(buf, "%i:%i", &base_major, &base_minor) != 2) { +- error_text("Could not parse major:minor from string '%s'", buf); +- free(buf); +- return -1; +- } +- +- free(buf); +- *base_dev = makedev(base_major, base_minor); +- return 0; +-} +- + /* Return non-zero for ECKD type. */ + int + disk_is_eckd(disk_type_t type) +@@ -492,15 +411,15 @@ + data->devno = -1; + data->type = disk_type_scsi; + +- if (blkext_is_base_device(stats.st_rdev)) { +- data->device = stats.st_rdev; +- data->partnum = 0; +- } else { +- if (blkext_get_base_dev(stats.st_rdev, &data->device)) ++ if (util_sys_dev_is_partition(stats.st_rdev)) { ++ if (util_sys_get_base_dev(stats.st_rdev, &data->device)) + goto out_close; +- data->partnum = blkext_get_partnum(stats.st_rdev); ++ data->partnum = util_sys_get_partnum(stats.st_rdev); + if (data->partnum == -1) + goto out_close; ++ } else { ++ data->device = stats.st_rdev; ++ data->partnum = 0; + } + } else { + /* Driver name is unknown */ diff --git a/debian/patches/series b/debian/patches/series index e779db8..894d113 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -110,3 +110,6 @@ f742ed73e1db99dc04b821a1f9aa4a852584e4d8.patch 0110-zipl-stage3-correctly-handle-diag308-response-code.patch 0111-zipl-fix-incorrect-setup-of-stage3-flags.patch 0112-cpumf-lscpumf.pl-displays-raw-event-number-incorrect.patch +s390-tools-sru-lp1903984-focal.patch +s390-tools-sru-lp1908371-focal.patch +s390-tools-sru-lp1902179-focal.patch