/
githubmirror
/
lvm2
Обзор
Документация
Войти
/
githubmirror
/
lvm2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/device/nvme.c
521 строка
11 KB
David Teigland
persist: warn when host has multiple PR keys registered
27 июл 2026, 21:24
27 июл 2026, 21:24
9d65a10
Код
Авторство
О чём код?
/* * 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/device/persist.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 <limits.h> #include <dirent.h> #include <errno.h> #include <stdbool.h> #include <assert.h> #define NVME_PR_BUF_SIZE 8192 /* space for 127 keys */ #ifdef NVME_SUPPORT #include <libnvme.h> static int _is_zero(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 %.16s 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 %.16s 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 %.8s 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 = 0; 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_debug("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 (!_is_zero(nguid, 16)) _save_nguid(dev, nguid); if (!_is_zero(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 (le32toh(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 < (int)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 (!_is_zero(uuid, NVME_UUID_LEN)) _save_uuid(dev, uuid); else if (!_is_zero(nguid, 16)) _save_nguid(dev, nguid); else if (!_is_zero(eui64, 8)) _save_eui64(dev, eui64); } out: free(ctrl_id); free(ns); free(data); if (close(fd)) log_sys_debug("close", devpath); } static int _prtype_from_nvme(uint8_t nvme_type) { switch (nvme_type) { case 0: return 0; case 1: return PR_TYPE_WE; case 2: return PR_TYPE_EA; case 3: return PR_TYPE_WERO; case 4: return PR_TYPE_EARO; case 5: return PR_TYPE_WEAR; case 6: return PR_TYPE_EAAR; default: return -1; }; } int dev_read_reservation_nvme(struct cmd_context *cmd, struct device *dev, uint64_t *holder_ret, int *prtype_ret) { const char *devname; struct nvme_resv_report_args args = { 0 }; struct nvme_resv_status *status = NULL; uint64_t rkey; uint32_t nsid = 0; int status_size = NVME_PR_BUF_SIZE; int status_entries; int regctl; int err; int fd; int i; int ret = 0; devname = dev_name(dev); if ((fd = open(devname, O_RDONLY)) < 0) { log_error("dev_read_reservation %s open error %d", devname, errno); return 0; } if (nvme_get_nsid(fd, &nsid)) { log_error("dev_read_reservation %s nvme_get_nsid error %d", devname, errno); goto out; } if (!(status = malloc(status_size))) goto out; args.args_size = sizeof(args); args.fd = fd; args.nsid = nsid; args.eds = 1; args.len = status_size; args.report = status; args.timeout = 0; args.result = NULL; if ((err = nvme_resv_report(&args))) { log_error("dev_read_reservation %s error %d", devname, err); goto out; } *prtype_ret = _prtype_from_nvme(status->rtype); ret = 1; if (!holder_ret) goto out; *holder_ret = 0; /* all are holders for these types so don't bother checking */ if (*prtype_ret == PR_TYPE_WEAR || *prtype_ret == PR_TYPE_EAAR) goto out; regctl = status->regctl[0] | (status->regctl[1] << 8); status_entries = (status_size - 64) / 64; if (status_entries < regctl) { log_warn("dev_read_reservation %s too many entries %d limit %d", devname, regctl, status_entries); regctl = status_entries; } for (i = 0; i < regctl; i++) { if (status->regctl_eds[i].rcsts) { rkey = le64toh(status->regctl_eds[i].rkey); *holder_ret = rkey; break; } } out: free(status); if (close(fd)) log_sys_debug("close", devname); return ret; } int dev_find_key_nvme(struct cmd_context *cmd, struct device *dev, int may_fail, uint64_t find_key, int *found_key, int find_host_id, uint64_t *found_host_id_key, int find_all, int *found_count, uint64_t **found_all) { const char *devname; struct nvme_resv_report_args args = { 0 }; struct nvme_resv_status *status = NULL; uint64_t *all_keys = NULL; uint64_t key; uint32_t nsid = 0; int status_size = NVME_PR_BUF_SIZE; int status_entries; int regctl; int num_keys; int host_id_match_count = 0; int err; int fd; int i; int ret = 0; devname = dev_name(dev); if ((fd = open(devname, O_RDONLY)) < 0) { log_error("dev_find_key_nvme %s open error %d", devname, errno); return 0; } if (nvme_get_nsid(fd, &nsid)) { if (may_fail) log_debug("dev_find_key_nvme %s nvme_get_nsid error %d", devname, errno); else log_error("dev_find_key_nvme %s nvme_get_nsid error %d", devname, errno); goto out; } if (!(status = malloc(status_size))) goto out; args.args_size = sizeof(args); args.fd = fd; args.nsid = nsid; args.eds = 1; args.len = status_size; args.report = status; args.timeout = 0; args.result = NULL; if ((err = nvme_resv_report(&args))) { if (may_fail) log_debug("dev_find_key_nvme %s error %d", devname, err); else log_error("dev_find_key_nvme %s error %d", devname, err); goto out; } regctl = status->regctl[0] | (status->regctl[1] << 8); status_entries = (status_size - 64) / 64; if (status_entries < regctl) { log_warn("dev_find_key_nvme %s too many entries %d limit %d", devname, regctl, status_entries); regctl = status_entries; } num_keys = regctl; log_debug("dev_find_key_nvme %s num %d", devname, num_keys); /* caller wants just a count of all keys */ if (find_all && found_count && !found_all) { *found_count = num_keys; ret = 1; goto out; } /* caller wants a count and array of all keys */ if (find_all && found_count && found_all) { *found_count = num_keys; *found_all = NULL; if (!num_keys) { ret = 1; goto out; } if (!(all_keys = dm_pool_zalloc(cmd->mem, num_keys * sizeof(uint64_t)))) { ret = 0; goto out; } *found_all = all_keys; } if (!num_keys) { ret = 1; goto out; } for (i = 0; i < num_keys; i++) { key = le64toh(status->regctl_eds[i].rkey); log_debug("dev_find_key_nvme %s 0x%llx", devname, (unsigned long long)key); if (find_all && found_count && found_all) all_keys[i] = key; if (find_key && (find_key == key)) { if (found_key) *found_key = 1; if (!find_all) break; } if (find_host_id && (find_host_id == (int)(key & 0xFFFF))) { if (!host_id_match_count && found_host_id_key) *found_host_id_key = key; host_id_match_count++; } } ret = 1; out: free(status); if (close(fd)) log_sys_debug("close", devname); return ret; } #else void dev_read_nvme_wwids(struct device *dev) { } int dev_read_reservation_nvme(struct cmd_context *cmd, struct device *dev, uint64_t *holder_ret, int *prtype_ret) { return 0; } int dev_find_key_nvme(struct cmd_context *cmd, struct device *dev, int may_fail, uint64_t find_key, int *found_key, int find_host_id, uint64_t *found_host_id_key, int find_all, int *found_count, uint64_t **found_all) { return 0; } #endif