mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
ipl-tools: Add nvme device support to lsreipl/chreipl
Support is added to lsreipl to detect the nvme reipl type and list the appropriate parameters. Support is added to chreipl to specify and populate sysfs reipl entries with nvme device targets. Signed-off-by: Jason J. Herne <jjherne@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
94a3272dac
commit
0472b5ea5c
@@ -4,9 +4,7 @@ libs = $(rootdir)/libutil/libutil.a
|
|||||||
|
|
||||||
all: chreipl lsreipl chshut lsshut
|
all: chreipl lsreipl chshut lsshut
|
||||||
|
|
||||||
libs = $(rootdir)/libutil/libutil.a
|
objects = main.o ccw.o fcp.o nvme.o system.o shutdown.o \
|
||||||
|
|
||||||
objects = main.o ccw.o fcp.o system.o shutdown.o \
|
|
||||||
cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o
|
cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o
|
||||||
|
|
||||||
chreipl: $(objects) $(libs)
|
chreipl: $(objects) $(libs)
|
||||||
|
|||||||
@@ -28,12 +28,14 @@ enum target_type {
|
|||||||
TT_FCP,
|
TT_FCP,
|
||||||
TT_NSS,
|
TT_NSS,
|
||||||
TT_NODE,
|
TT_NODE,
|
||||||
|
TT_NVME,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum reipl_type {
|
enum reipl_type {
|
||||||
REIPL_FCP,
|
REIPL_FCP,
|
||||||
REIPL_CCW,
|
REIPL_CCW,
|
||||||
REIPL_NSS
|
REIPL_NSS,
|
||||||
|
REIPL_NVME,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const usage_chreipl =
|
static const char *const usage_chreipl =
|
||||||
@@ -41,6 +43,7 @@ static const char *const usage_chreipl =
|
|||||||
"\n"
|
"\n"
|
||||||
" chreipl [ccw] [-d] <DEVICE> [OPTIONS]\n"
|
" chreipl [ccw] [-d] <DEVICE> [OPTIONS]\n"
|
||||||
" chreipl [fcp] [-d] <DEVICE> [-w] <WWPN> [-l] <LUN> [OPTIONS]\n"
|
" chreipl [fcp] [-d] <DEVICE> [-w] <WWPN> [-l] <LUN> [OPTIONS]\n"
|
||||||
|
" chreipl nvme [-i] <FID> [-s] <NSID> [OPTIONS]\n"
|
||||||
" chreipl [node] <NODE|DIR> [OPTIONS]\n"
|
" chreipl [node] <NODE|DIR> [OPTIONS]\n"
|
||||||
" chreipl nss [-n] <NAME> [OPTIONS]\n"
|
" chreipl nss [-n] <NAME> [OPTIONS]\n"
|
||||||
" chreipl [-h] [-v]\n"
|
" chreipl [-h] [-v]\n"
|
||||||
@@ -48,6 +51,7 @@ static const char *const usage_chreipl =
|
|||||||
"The following re-IPL targets are supported:\n"
|
"The following re-IPL targets are supported:\n"
|
||||||
" ccw IPL from CCW device\n"
|
" ccw IPL from CCW device\n"
|
||||||
" fcp IPL from FCP device\n"
|
" fcp IPL from FCP device\n"
|
||||||
|
" nvme IPL from NVME device\n"
|
||||||
" nss IPL from NSS\n"
|
" nss IPL from NSS\n"
|
||||||
" node IPL from device specified by device node or directory\n"
|
" node IPL from device specified by device node or directory\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -70,6 +74,12 @@ static const char *const usage_chreipl =
|
|||||||
" -L, --loadparm <PARM> Loadparm specification\n"
|
" -L, --loadparm <PARM> Loadparm specification\n"
|
||||||
" -c, --clear 0|1 Control if memory is cleared on re-IPL\n"
|
" -c, --clear 0|1 Control if memory is cleared on re-IPL\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"Options for nvme target:\n"
|
||||||
|
" -i, --fid <FUNCTION_ID> PCI Function ID of NVME IPL device (hex)\n"
|
||||||
|
" -s --nsid <NAMESPACE_ID> Namespace ID of NVME IPL device (decimal, default 1)\n"
|
||||||
|
" -b, --bootprog <BPROG> Bootprog specification\n"
|
||||||
|
" -L, --loadparm <PARM> Loadparm specification\n"
|
||||||
|
"\n"
|
||||||
"Options for nss target:\n"
|
"Options for nss target:\n"
|
||||||
" -n, --name <NAME> Identifier of the NSS\n"
|
" -n, --name <NAME> Identifier of the NSS\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -86,6 +96,10 @@ static struct locals {
|
|||||||
char lun[20]; /* 18 character +0x" */
|
char lun[20]; /* 18 character +0x" */
|
||||||
int lun_set;
|
int lun_set;
|
||||||
char busid[10]; /* Bus ID e.g. 0.0.4711 */
|
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;
|
int busid_set;
|
||||||
char dev[15]; /* Device (e.g. dasda) */
|
char dev[15]; /* Device (e.g. dasda) */
|
||||||
int dev_set;
|
int dev_set;
|
||||||
@@ -94,10 +108,10 @@ static struct locals {
|
|||||||
char bootparms[4096];
|
char bootparms[4096];
|
||||||
int bootparms_set;
|
int bootparms_set;
|
||||||
int force_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_set;
|
||||||
int target_type_auto_mode;
|
int target_type_auto_mode;
|
||||||
enum reipl_type reipl_type; /* CCW, FCP, NSS */
|
enum reipl_type reipl_type; /* CCW, FCP, NVME, NSS */
|
||||||
int reipl_clear;
|
int reipl_clear;
|
||||||
} l;
|
} l;
|
||||||
|
|
||||||
@@ -229,6 +243,34 @@ static void set_wwpn(const char *wwpn)
|
|||||||
l.wwpn_set = 1;
|
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)
|
static void parse_fcp_args(char *nargv[], int nargc)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -248,6 +290,28 @@ static void parse_fcp_args(char *nargv[], int nargc)
|
|||||||
set_lun(nargv[2]);
|
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)
|
static void parse_ccw_args(char *nargv[], int nargc)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -288,6 +352,13 @@ static void dev_from_part(char *dev_name)
|
|||||||
dev_name[i] = 0;
|
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)
|
static int set_reipl_type(const char *dev_name)
|
||||||
{
|
{
|
||||||
if (strncmp(dev_name, "dasd", strlen("dasd")) == 0 ||
|
if (strncmp(dev_name, "dasd", strlen("dasd")) == 0 ||
|
||||||
@@ -295,11 +366,18 @@ static int set_reipl_type(const char *dev_name)
|
|||||||
l.reipl_type = REIPL_CCW;
|
l.reipl_type = REIPL_CCW;
|
||||||
else if (strncmp(dev_name, "sd", strlen("sd")) == 0)
|
else if (strncmp(dev_name, "sd", strlen("sd")) == 0)
|
||||||
l.reipl_type = REIPL_FCP;
|
l.reipl_type = REIPL_FCP;
|
||||||
|
else if (strncmp(dev_name, "nvme", strlen("nvme")) == 0)
|
||||||
|
l.reipl_type = REIPL_NVME;
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
util_strlcpy(l.dev, dev_name, sizeof(l.dev));
|
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;
|
l.dev_set = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -402,6 +480,9 @@ static void parse_pos_args(char *nargv[], int nargc)
|
|||||||
case TT_FCP:
|
case TT_FCP:
|
||||||
parse_fcp_args(nargv, nargc);
|
parse_fcp_args(nargv, nargc);
|
||||||
break;
|
break;
|
||||||
|
case TT_NVME:
|
||||||
|
parse_nvme_args(nargv, nargc);
|
||||||
|
break;
|
||||||
case TT_CCW:
|
case TT_CCW:
|
||||||
parse_ccw_args(nargv, nargc);
|
parse_ccw_args(nargv, nargc);
|
||||||
break;
|
break;
|
||||||
@@ -423,6 +504,14 @@ static void check_fcp_opts(void)
|
|||||||
"and LUN");
|
"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)
|
static void check_ccw_opts(void)
|
||||||
{
|
{
|
||||||
if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set)
|
if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set)
|
||||||
@@ -482,6 +571,8 @@ static void parse_chreipl_options(int argc, char *argv[])
|
|||||||
{ "device", required_argument, NULL, 'd' },
|
{ "device", required_argument, NULL, 'd' },
|
||||||
{ "lun", required_argument, NULL, 'l' },
|
{ "lun", required_argument, NULL, 'l' },
|
||||||
{ "wwpn", required_argument, NULL, 'w' },
|
{ "wwpn", required_argument, NULL, 'w' },
|
||||||
|
{ "fid", required_argument, NULL, 'i' },
|
||||||
|
{ "nsid", required_argument, NULL, 's' },
|
||||||
{ "loadparm", required_argument, NULL, 'L' },
|
{ "loadparm", required_argument, NULL, 'L' },
|
||||||
{ "name", required_argument, NULL, 'n' },
|
{ "name", required_argument, NULL, 'n' },
|
||||||
{ "bootparms", required_argument, NULL, 'p' },
|
{ "bootparms", required_argument, NULL, 'p' },
|
||||||
@@ -490,7 +581,7 @@ static void parse_chreipl_options(int argc, char *argv[])
|
|||||||
{ "clear", required_argument, NULL, 'c' },
|
{ "clear", required_argument, NULL, 'c' },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
static const char optstr[] = "hd:vw:l:fL:b:n:p:c:";
|
static const char optstr[] = "hd:vw:l:fL:b:n:p:c:i:s:";
|
||||||
|
|
||||||
/* dont run without any argument */
|
/* dont run without any argument */
|
||||||
if (argc == 1)
|
if (argc == 1)
|
||||||
@@ -502,6 +593,8 @@ static void parse_chreipl_options(int argc, char *argv[])
|
|||||||
set_target_type(TT_CCW, 0);
|
set_target_type(TT_CCW, 0);
|
||||||
else if (strcmp(argv[1], "nss") == 0)
|
else if (strcmp(argv[1], "nss") == 0)
|
||||||
set_target_type(TT_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)
|
else if (strcmp(argv[1], "node") == 0)
|
||||||
set_target_type(TT_NODE, 0);
|
set_target_type(TT_NODE, 0);
|
||||||
else
|
else
|
||||||
@@ -516,9 +609,15 @@ static void parse_chreipl_options(int argc, char *argv[])
|
|||||||
case 'd':
|
case 'd':
|
||||||
set_device(optarg);
|
set_device(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'i':
|
||||||
|
set_nvme_fid(optarg);
|
||||||
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
set_lun(optarg);
|
set_lun(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
set_nvme_nsid(optarg);
|
||||||
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
set_wwpn(optarg);
|
set_wwpn(optarg);
|
||||||
break;
|
break;
|
||||||
@@ -680,6 +779,40 @@ static void chreipl_fcp(void)
|
|||||||
print_fcp(0, 0);
|
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)
|
static void chreipl_nss(void)
|
||||||
{
|
{
|
||||||
check_nss_opts();
|
check_nss_opts();
|
||||||
@@ -720,6 +853,13 @@ static void chreipl_node(void)
|
|||||||
l.busid_set = 1;
|
l.busid_set = 1;
|
||||||
chreipl_fcp();
|
chreipl_fcp();
|
||||||
break;
|
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:
|
default:
|
||||||
ERR_EXIT("Internal error: chreipl_node");
|
ERR_EXIT("Internal error: chreipl_node");
|
||||||
}
|
}
|
||||||
@@ -735,6 +875,9 @@ void cmd_chreipl(int argc, char *argv[])
|
|||||||
case TT_FCP:
|
case TT_FCP:
|
||||||
chreipl_fcp();
|
chreipl_fcp();
|
||||||
break;
|
break;
|
||||||
|
case TT_NVME:
|
||||||
|
chreipl_nvme();
|
||||||
|
break;
|
||||||
case TT_NSS:
|
case TT_NSS:
|
||||||
chreipl_nss();
|
chreipl_nss();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -84,6 +84,35 @@ void print_fcp(int show_ipl, int dump)
|
|||||||
print_fw_str("clear: %s\n", dir, "clear");
|
print_fw_str("clear: %s\n", dir, "clear");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void print_ccw(int show_ipl)
|
||||||
{
|
{
|
||||||
char loadparm[9], loadparm_path[PATH_MAX];
|
char loadparm[9], loadparm_path[PATH_MAX];
|
||||||
@@ -155,6 +184,10 @@ void cmd_lsreipl(int argc, char *argv[])
|
|||||||
print_fcp(l.ipl_set, 0);
|
print_fcp(l.ipl_set, 0);
|
||||||
else if (strcmp(reipl_type_str, "fcp_dump") == 0)
|
else if (strcmp(reipl_type_str, "fcp_dump") == 0)
|
||||||
print_fcp(l.ipl_set, 1);
|
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)
|
else if (strcmp(reipl_type_str, "ccw") == 0)
|
||||||
print_ccw(l.ipl_set);
|
print_ccw(l.ipl_set);
|
||||||
else if (strcmp(reipl_type_str, "nss") == 0)
|
else if (strcmp(reipl_type_str, "nss") == 0)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ extern void cmd_chreipl(int argc, char *argv[]);
|
|||||||
|
|
||||||
extern void print_ccw(int show_ipl);
|
extern void print_ccw(int show_ipl);
|
||||||
extern void print_fcp(int show_ipl, int dump);
|
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);
|
extern void print_nss(int show_ipl);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -70,6 +71,16 @@ extern void fcp_lun_get(const char *device, char *lun);
|
|||||||
extern void fcp_wwpn_get(const char *device, char *wwpn);
|
extern void fcp_wwpn_get(const char *device, char *wwpn);
|
||||||
extern void fcp_busid_get(const char *device, char *devno);
|
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
|
* CCW
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ Specify a DASD CCW device for reboot
|
|||||||
.RB "- " fcp :
|
.RB "- " fcp :
|
||||||
Specify a FCP device for reboot
|
Specify a FCP device for reboot
|
||||||
.TP
|
.TP
|
||||||
|
.RB "- " nvme :
|
||||||
|
Specify an NVMe device for reboot
|
||||||
|
.TP
|
||||||
.RB "- " nss :
|
.RB "- " nss :
|
||||||
Specify a named saved system (NSS) for reboot
|
Specify a named saved system (NSS) for reboot
|
||||||
.TP
|
.TP
|
||||||
@@ -118,6 +121,11 @@ WWPN 0x500507630300c562, and LUN 0x401040b300000000. In addition to that
|
|||||||
append kernel parameter "mem=" to restrict memory to 512 MB:
|
append kernel parameter "mem=" to restrict memory to 512 MB:
|
||||||
|
|
||||||
\fB# chreipl 0.0.1700 0x500507630300c562 0x401040b300000000 -p "mem=512M"\fP
|
\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
|
.SH ccw
|
||||||
Use the ccw re-IPL target for DASD devices that are accessed by the hardware
|
Use the ccw re-IPL target for DASD devices that are accessed by the hardware
|
||||||
using channel command word (CCW) channels.
|
using channel command word (CCW) channels.
|
||||||
@@ -207,6 +215,44 @@ use options instead of positional parameters:
|
|||||||
.br
|
.br
|
||||||
|
|
||||||
\fB# chreipl fcp -d 0.0.1700 -w 0x5005076... -l 0x401040b3... -b 2\fP
|
\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
|
.SH nss
|
||||||
Use the nss re-IPL target to specify z/VM named saved systems (NSS) for
|
Use the nss re-IPL target to specify z/VM named saved systems (NSS) for
|
||||||
reboot.
|
reboot.
|
||||||
@@ -221,7 +267,7 @@ Use the NSS named LINUX1 for the next reboot:
|
|||||||
|
|
||||||
\fB# chreipl nss LINUX1\fP
|
\fB# chreipl nss LINUX1\fP
|
||||||
.SH node
|
.SH node
|
||||||
You can identify DASD or SCSI re-IPL devices indirectly through a device
|
You can identify DASD, SCSI, or NVMe re-IPL devices indirectly through a device
|
||||||
node or directory. The chreipl tool then determines the information
|
node or directory. The chreipl tool then determines the information
|
||||||
that you would otherwise have to specify with the ccw or fcp target.
|
that you would otherwise have to specify with the ccw or fcp target.
|
||||||
.PP
|
.PP
|
||||||
@@ -244,6 +290,11 @@ that you would otherwise have to specify with the ccw or fcp target.
|
|||||||
|
|
||||||
\fB# chreipl node /mnt/boot\fP
|
\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
|
.SH SEE ALSO
|
||||||
.BR lsreipl (8),
|
.BR lsreipl (8),
|
||||||
.BR zipl (8),
|
.BR zipl (8),
|
||||||
|
|||||||
169
ipl_tools/nvme.c
Normal file
169
ipl_tools/nvme.c
Normal file
@@ -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 <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user