1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00
lvm2/lib/device/nvme.c
David Teigland d952358636 device_id: nvme devices may use alternate wwids
Device quirks may cause sysfs wwid file to change what it
displays, from a bogus eui... string to an nvme... string.

The old wwid may be saved in system.devices, so recognizing
the device requires finding the old value from libnvme.

After matching the old bogus value using libnvme, system.devices
is updated with the current sysfs wwid value.
2024-12-09 16:03:43 -06:00

274 lines
5.8 KiB
C

/*
* Copyright (C) 2024 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/device/device.h"
#include "lib/device/device_id.h"
#include "lib/mm/xlate.h"
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <assert.h>
#ifdef NVME_SUPPORT
#include <libnvme.h>
static int iszero(unsigned char *d, size_t n)
{
size_t i;
for (i = 0; i < n; ++i) {
if (d[i])
return 0;
}
return 1;
}
static void _save_uuid(struct device *dev, unsigned char *uuid)
{
char idname[DEV_WWID_SIZE] = {0};
int max, pos, num, i;
max = sizeof(idname);
pos = 0;
num = snprintf(idname + pos, max - pos, "uuid.");
if (num >= max - pos)
goto bad;
pos += num;
for (i = 0; i < NVME_UUID_LEN; ++i) {
num = snprintf(idname + pos, max - pos, "%02x", uuid[i]);
if (num >= max - pos)
goto bad;
pos += num;
if (i == 3 || i == 5 || i == 7 || i == 9) {
num = snprintf(idname + pos, max - pos, "-");
if (num >= max - pos)
goto bad;
pos += num;
}
}
idname[DEV_WWID_SIZE-1] = '\0';
dev_add_nvme_wwid(idname, 3, &dev->wwids);
return;
bad:
log_debug("dev_read_nvme_wwids ignore invalid uuid %s for %s", uuid, dev_name(dev));
}
static void _save_nguid(struct device *dev, unsigned char *nguid)
{
char idname[DEV_WWID_SIZE] = {0};
int max, pos, num, i;
max = sizeof(idname);
pos = 0;
num = snprintf(idname + pos, max - pos, "eui.");
if (num >= max - pos)
goto bad;
pos += num;
for (i = 0; i < 16; ++i) {
num = snprintf(idname + pos, max - pos, "%02x", nguid[i]);
if (num >= max - pos)
goto bad;
pos += num;
}
idname[DEV_WWID_SIZE-1] = '\0';
dev_add_nvme_wwid(idname, 2, &dev->wwids);
return;
bad:
log_debug("dev_read_nvme_wwids ignore invalid nguid %s for %s", nguid, dev_name(dev));
}
static void _save_eui64(struct device *dev, unsigned char *eui64)
{
char idname[DEV_WWID_SIZE] = {0};
int max, pos, num, i;
max = sizeof(idname);
pos = 0;
num = snprintf(idname + pos, max - pos, "eui.");
if (num >= max - pos)
goto bad;
pos += num;
for (i = 0; i < 8; ++i) {
num = snprintf(idname + pos, max - pos, "%02x", eui64[i]);
if (num >= max - pos)
goto bad;
pos += num;
}
idname[DEV_WWID_SIZE-1] = '\0';
dev_add_nvme_wwid(idname, 1, &dev->wwids);
return;
bad:
log_debug("dev_read_nvme_wwids ignore invalid eui64 %s for %s", eui64, dev_name(dev));
}
#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
static void *_nvme_alloc(size_t len)
{
size_t _len = ROUND_UP(len, 0x1000);
void *p;
if (posix_memalign((void *)&p, getpagesize(), _len))
return NULL;
memset(p, 0, _len);
return p;
}
void dev_read_nvme_wwids(struct device *dev)
{
const char *devpath;
unsigned char *data = NULL;
struct nvme_id_ns *ns = NULL;
struct nvme_id_ctrl *ctrl_id = NULL;
unsigned char nguid[16] = {0};
unsigned char eui64[8] = {0};
unsigned char uuid[NVME_UUID_LEN] = {0};
uint32_t nsid;
int fd, i, len;
dev->flags |= DEV_ADDED_NVME_WWIDS;
/* shouldn't happen */
if (dm_list_empty(&dev->aliases))
return;
devpath = dev_name(dev);
if ((fd = open(devpath, O_RDONLY)) < 0) {
log_debug("dev_read_nvme_wwids cannot open %s", devpath);
return;
}
if (nvme_get_nsid(fd, &nsid)) {
log_print("dev_read_nvme_wwids nvme_get_nsid error %d %s", errno, devpath);
goto out;
}
if (!(ns = _nvme_alloc(sizeof(*ns))))
goto_out;
if (nvme_identify_ns(fd, nsid, ns)) {
log_debug("dev_read_nvme_wwids nvme_identify_ns error %d %s", errno, devpath);
goto out;
}
memcpy(nguid, ns->nguid, 16);
memcpy(eui64, ns->eui64, 8);
if (!iszero(nguid, 16))
_save_nguid(dev, nguid);
if (!iszero(eui64, 8))
_save_eui64(dev, eui64);
if (!(ctrl_id = _nvme_alloc(sizeof(struct nvme_id_ctrl))))
goto_out;
/* Avoid using nvme_identify_ns_descs before ver 1.3. */
if (!nvme_identify_ctrl(fd, ctrl_id)) {
if (le32_to_cpu(ctrl_id->ver) < 0x10300)
goto_out;
}
if (!(data = _nvme_alloc(NVME_IDENTIFY_DATA_SIZE)))
goto_out;
if (nvme_identify_ns_descs(fd, nsid, (struct nvme_ns_id_desc *)data)) {
log_debug("dev_read_nvme_wwids nvme_identify_ns_descs error %d %s", errno, devpath);
goto out;
}
for (i = 0; i < NVME_IDENTIFY_DATA_SIZE; i += len) {
struct nvme_ns_id_desc *cur = (struct nvme_ns_id_desc *)(data + i);
if (cur->nidl == 0)
break;
memset(eui64, 0, sizeof(eui64));
memset(nguid, 0, sizeof(nguid));
memset(uuid, 0, sizeof(uuid));
switch (cur->nidt) {
case NVME_NIDT_EUI64:
memcpy(eui64, data + i + sizeof(*cur), sizeof(eui64));
len = sizeof(eui64);
break;
case NVME_NIDT_NGUID:
memcpy(nguid, data + i + sizeof(*cur), sizeof(nguid));
len = sizeof(nguid);
break;
case NVME_NIDT_UUID:
memcpy(uuid, data + i + sizeof(*cur), NVME_UUID_LEN);
len = sizeof(uuid);
break;
case NVME_NIDT_CSI:
len = 1;
break;
default:
len = cur->nidl;
break;
}
len += sizeof(*cur);
if (!iszero(uuid, NVME_UUID_LEN))
_save_uuid(dev, uuid);
else if (!iszero(nguid, 16))
_save_nguid(dev, nguid);
else if (!iszero(eui64, 8))
_save_eui64(dev, eui64);
}
out:
free(ctrl_id);
free(ns);
free(data);
close(fd);
}
#else
void dev_read_nvme_wwids(struct device *dev)
{
}
#endif