mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Closes: #20 Signed-off-by: Rafael Fonseca <r4f4rfs@gmail.com> Acked-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
365 lines
7.8 KiB
C
365 lines
7.8 KiB
C
/*
|
|
* zdev - Modify and display the persistent configuration of devices
|
|
*
|
|
* Copyright IBM Corp. 2016, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lib/util_path.h"
|
|
|
|
#include "misc.h"
|
|
#include "path.h"
|
|
#include "scsi.h"
|
|
#include "zfcp_lun.h"
|
|
|
|
struct scsi_hctl_devid {
|
|
unsigned int host;
|
|
unsigned int channel;
|
|
unsigned int target;
|
|
uint64_t lun;
|
|
};
|
|
|
|
struct scsi_zfcp {
|
|
struct scsi_hctl_devid scsi;
|
|
struct zfcp_lun_devid zfcp;
|
|
};
|
|
|
|
static struct util_list *scsi_zfcp_list;
|
|
|
|
static char *scsi_hctl_devid_to_str(struct scsi_hctl_devid *devid)
|
|
{
|
|
return misc_asprintf("%u:%u:%u:%" PRIu64, devid->host, devid->channel,
|
|
devid->target, devid->lun);
|
|
}
|
|
|
|
/* Used for debugging. */
|
|
void scsi_zfcp_print(struct scsi_zfcp *s, int i)
|
|
{
|
|
char *scsi, *zfcp;
|
|
|
|
indent(i, "scsi_zfcp at %p:\n", (void *) s);
|
|
i += 2;
|
|
scsi = scsi_hctl_devid_to_str(&s->scsi);
|
|
zfcp = zfcp_lun_devid_to_str(&s->zfcp);
|
|
indent(i, "scsi=%s\n", scsi);
|
|
indent(i, "zfcp=%s\n", zfcp);
|
|
free(zfcp);
|
|
free(scsi);
|
|
}
|
|
|
|
void scsi_reread(void)
|
|
{
|
|
ptrlist_free(scsi_zfcp_list, 1);
|
|
scsi_zfcp_list = NULL;
|
|
}
|
|
|
|
void scsi_exit(void)
|
|
{
|
|
ptrlist_free(scsi_zfcp_list, 1);
|
|
}
|
|
|
|
static bool scsi_hctl_parse_devid(struct scsi_hctl_devid *id, const char *str)
|
|
{
|
|
unsigned int host, channel, target;
|
|
uint64_t lun;
|
|
char dummy;
|
|
|
|
if (sscanf(str, "%u:%u:%u:%" SCNu64 " %c", &host, &channel, &target,
|
|
&lun, &dummy) != 4)
|
|
return false;
|
|
|
|
if (id) {
|
|
id->host = host;
|
|
id->channel = channel;
|
|
id->target = target;
|
|
id->lun = lun;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Retrieve CCW device ID of HBA for specified SCSI device path. */
|
|
static char *devpath_to_hba_id(const char *path)
|
|
{
|
|
char *copy, *start, *end, *hba_id = NULL;
|
|
|
|
copy = misc_strdup(path);
|
|
|
|
/* copy=/devices/css0/0.0.001c/0.0.1940/host0/... */
|
|
end = strstr(copy, "/host");
|
|
if (!end)
|
|
goto out;
|
|
*end = 0;
|
|
/* copy=/devices/css0/0.0.001c/0.0.1940 */
|
|
start = strrchr(copy, '/');
|
|
if (!start)
|
|
goto out;
|
|
start++;
|
|
hba_id = misc_strdup(start);
|
|
|
|
out:
|
|
free(copy);
|
|
|
|
return hba_id;
|
|
}
|
|
|
|
/* Retrieve WWPN from specified SCSI device path. */
|
|
static char *devpath_to_wwpn(const char *devpath)
|
|
{
|
|
char *copy, *rport, *end, *path = NULL, *wwpn = NULL;
|
|
|
|
/* devpath=/devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16/
|
|
* target0:0:16/0:0:16:1085030433/ */
|
|
copy = misc_strdup(devpath);
|
|
rport = strstr(copy, "/rport-");
|
|
end = skip_comp(rport);
|
|
if (!end)
|
|
goto out;
|
|
*end = 0;
|
|
|
|
/* devpath=/devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16
|
|
* rport=rport-0:0-16 */
|
|
path = path_get("/sys%s/fc_remote_ports%s/port_name", copy, rport);
|
|
wwpn = misc_read_text_file(path, 1, err_ignore);
|
|
|
|
out:
|
|
free(path);
|
|
free(copy);
|
|
|
|
return wwpn;
|
|
}
|
|
|
|
static unsigned int lun_swap[] = { 6, 7, 4, 5 };
|
|
|
|
uint64_t scsi_lun_to_fcp_lun(uint64_t lun)
|
|
{
|
|
byte_swap((uint8_t *) &lun, lun_swap, ARRAY_SIZE(lun_swap));
|
|
|
|
return lun;
|
|
}
|
|
|
|
uint64_t scsi_lun_from_fcp_lun(uint64_t lun)
|
|
{
|
|
byte_swap((uint8_t *) &lun, lun_swap, ARRAY_SIZE(lun_swap));
|
|
|
|
return lun;
|
|
}
|
|
|
|
/* Retrieve FCP LUN from specified HCTL. */
|
|
static char *hctl_to_fcp_lun(const char *hctl)
|
|
{
|
|
struct scsi_hctl_devid devid;
|
|
|
|
if (!scsi_hctl_parse_devid(&devid, hctl))
|
|
return NULL;
|
|
|
|
return misc_asprintf("0x%016" PRIx64, scsi_lun_to_fcp_lun(devid.lun));
|
|
}
|
|
|
|
/* Retrieve FCP LUN from specified SCSI device path. Works with paths to
|
|
* SCSI device and sub-directories (required for paths from /sys/dev/block and
|
|
* /sys/dev/char). */
|
|
static char *devpath_to_fcp_lun(const char *devpath)
|
|
{
|
|
char *hctl, *fcp_lun = NULL;
|
|
|
|
hctl = scsi_hctl_from_devpath(devpath);
|
|
if (hctl) {
|
|
fcp_lun = hctl_to_fcp_lun(hctl);
|
|
free(hctl);
|
|
}
|
|
|
|
return fcp_lun;
|
|
}
|
|
|
|
/* Try to determine the zfcp LUN ID from the specified zfcp SCSI device path. */
|
|
static char *devpath_to_zfcp_lun_id(const char *path)
|
|
{
|
|
const char *devpath;
|
|
char *hba_id, *wwpn, *fcp_lun, *zfcp_lun_id = NULL;
|
|
|
|
devpath = strstr(path, "/devices/");
|
|
if (!devpath)
|
|
return NULL;
|
|
|
|
hba_id = devpath_to_hba_id(devpath);
|
|
wwpn = devpath_to_wwpn(devpath);
|
|
fcp_lun = devpath_to_fcp_lun(devpath);
|
|
|
|
if (hba_id && wwpn && fcp_lun)
|
|
zfcp_lun_id = misc_asprintf("%s:%s:%s", hba_id, wwpn, fcp_lun);
|
|
|
|
free(fcp_lun);
|
|
free(wwpn);
|
|
free(hba_id);
|
|
|
|
return zfcp_lun_id;
|
|
}
|
|
|
|
/* Return the zfcp LUN ID from the specified SCSI HCTL ID. */
|
|
char *scsi_hctl_to_zfcp_lun_id(const char *hctl)
|
|
{
|
|
char *buspath, *link = NULL, *zfcp_lun_id = NULL;
|
|
|
|
buspath = path_get_sys_bus_dev("scsi", hctl);
|
|
link = misc_readlink(buspath);
|
|
if (!link)
|
|
goto out;
|
|
|
|
zfcp_lun_id = devpath_to_zfcp_lun_id(link);
|
|
|
|
out:
|
|
free(link);
|
|
free(buspath);
|
|
|
|
return zfcp_lun_id;
|
|
}
|
|
|
|
static exit_code_t add_ids_cb(const char *path, const char *name, void *data)
|
|
{
|
|
struct util_list *list = data;
|
|
struct scsi_hctl_devid scsi_devid;
|
|
struct zfcp_lun_devid zfcp_devid;
|
|
char *zfcp_id;
|
|
struct scsi_zfcp *s;
|
|
|
|
if (starts_with(name, "host") || starts_with(name, "target"))
|
|
return EXIT_OK;
|
|
if (!scsi_hctl_parse_devid(&scsi_devid, name))
|
|
return EXIT_OK;
|
|
zfcp_id = scsi_hctl_to_zfcp_lun_id(name);
|
|
if (!zfcp_id)
|
|
return EXIT_OK;
|
|
if (zfcp_lun_parse_devid(&zfcp_devid, zfcp_id, err_ignore) == EXIT_OK) {
|
|
s = misc_malloc(sizeof(struct scsi_zfcp));
|
|
s->scsi = scsi_devid;
|
|
s->zfcp = zfcp_devid;
|
|
ptrlist_add(list, s);
|
|
}
|
|
free(zfcp_id);
|
|
|
|
return EXIT_OK;
|
|
}
|
|
|
|
static struct util_list *read_scsi_zfcp_list(void)
|
|
{
|
|
struct util_list *list;
|
|
char *path;
|
|
|
|
list = ptrlist_new();
|
|
path = path_get_sys_bus_dev("scsi", NULL);
|
|
if (util_path_is_dir(path))
|
|
path_for_each(path, add_ids_cb, list);
|
|
free(path);
|
|
|
|
return list;
|
|
}
|
|
|
|
static struct scsi_zfcp *get_scsi_zfcp(struct zfcp_lun_devid *devid)
|
|
{
|
|
struct ptrlist_node *p;
|
|
struct scsi_zfcp *s;
|
|
|
|
if (!scsi_zfcp_list)
|
|
scsi_zfcp_list = read_scsi_zfcp_list();
|
|
|
|
/* Search for zfcp LUN device ID in cached list. */
|
|
util_list_iterate(scsi_zfcp_list, p) {
|
|
s = p->ptr;
|
|
if (zfcp_lun_cmp_devids(&s->zfcp, devid) == 0)
|
|
return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Check for SCSI device associated with zfcp lun device @devid. Return newly
|
|
* allocated HCTL ID of SCSI device on success, %NULL on failure. */
|
|
char *scsi_hctl_from_zfcp_lun_devid(struct zfcp_lun_devid *devid)
|
|
{
|
|
struct scsi_zfcp *s;
|
|
|
|
s = get_scsi_zfcp(devid);
|
|
if (s)
|
|
return scsi_hctl_devid_to_str(&s->scsi);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Check for SCSI device associated with zfcp LUN device @id. Return newly
|
|
* allocated HCTL ID of SCSI device on success, %NULL on failure. */
|
|
char *scsi_hctl_from_zfcp_lun_id(const char *id)
|
|
{
|
|
struct zfcp_lun_devid devid;
|
|
|
|
if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
|
return NULL;
|
|
|
|
return scsi_hctl_from_zfcp_lun_devid(&devid);
|
|
}
|
|
|
|
/* Check if SCSI device exists for zfcp LUN @id. */
|
|
bool scsi_hctl_exists(const char *id)
|
|
{
|
|
struct zfcp_lun_devid devid;
|
|
|
|
if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
|
return false;
|
|
|
|
if (!get_scsi_zfcp(&devid))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Add zfcp LUN ids for all SCSI devices to strlist @list. */
|
|
void scsi_hctl_add_zfcp_lun_ids(struct util_list *list)
|
|
{
|
|
struct ptrlist_node *p;
|
|
struct scsi_zfcp *s;
|
|
char *id;
|
|
|
|
if (!scsi_zfcp_list)
|
|
scsi_zfcp_list = read_scsi_zfcp_list();
|
|
|
|
util_list_iterate(scsi_zfcp_list, p) {
|
|
s = p->ptr;
|
|
id = zfcp_lun_devid_to_str(&s->zfcp);
|
|
strlist_add(list, id);
|
|
free(id);
|
|
}
|
|
}
|
|
|
|
/* Return SCSI HCTL ID from SCSI device path. Works with paths to SCSI device
|
|
* and sub-directories (required for paths from /sys/dev/block and
|
|
* /sys/dev/char). */
|
|
char *scsi_hctl_from_devpath(const char *path)
|
|
{
|
|
char *copy, *start, *end, *hctl = NULL;
|
|
|
|
/* ../../devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16/
|
|
* target0:0:16/0:0:16:1085030433/ */
|
|
copy = misc_strdup(path);
|
|
start = strstr(copy, "/target");
|
|
start = skip_comp(start);
|
|
if (!start)
|
|
goto out;
|
|
start++;
|
|
end = strchr(start, '/');
|
|
if (end)
|
|
*end = 0;
|
|
hctl = misc_strdup(start);
|
|
|
|
out:
|
|
free(copy);
|
|
|
|
return hctl;
|
|
}
|