From 47b2772a8e419ebadcf54182bfbd6170f7ab24f1 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 14 Apr 2014 17:19:57 -0400 Subject: [PATCH 04/22] Make nvme work with "-e 3". This will force a long-form NVME device path. Signed-off-by: Peter Jones --- src/include/disk.h | 2 +- src/include/scsi_ioctls.h | 1 + src/lib/disk.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/efi.c | 29 +++++++++++++++++++++++++++-- src/lib/scsi_ioctls.c | 7 +++++++ 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/include/disk.h b/src/include/disk.h index 720248c..3b3370d 100644 --- a/src/include/disk.h +++ b/src/include/disk.h @@ -65,7 +65,7 @@ enum _bus_type {bus_type_unknown, isa, pci}; enum _interface_type {interface_type_unknown, ata, atapi, scsi, usb, i1394, fibre, i2o, md, - virtblk}; + virtblk, nvme}; unsigned int lcm(unsigned int x, unsigned int y); diff --git a/src/include/scsi_ioctls.h b/src/include/scsi_ioctls.h index ba4c8bb..995cbbd 100644 --- a/src/include/scsi_ioctls.h +++ b/src/include/scsi_ioctls.h @@ -38,6 +38,7 @@ typedef struct scsi_idlun { } Scsi_Idlun; +int get_nvme_ns_id(int fd, uint32_t *ns_id); inline int get_scsi_idlun(int fd, Scsi_Idlun *idlun); int get_scsi_pci(int fd, char *slot_name, size_t size); int idlun_to_components (Scsi_Idlun *idlun, diff --git a/src/lib/disk.c b/src/lib/disk.c index 636b509..ad95fd4 100644 --- a/src/lib/disk.c +++ b/src/lib/disk.c @@ -75,6 +75,45 @@ get_virtblk_major(void) return cached; } +static int +get_nvme_major(void) +{ + static int cached; + FILE *f; + char line[256]; + + if (cached != 0) { + return cached; + } + + cached = -1; + f = fopen("/proc/devices", "r"); + if (f == NULL) { + fprintf(stderr, "%s: opening /proc/devices: %s\n", __func__, + strerror(errno)); + return cached; + } + while (fgets(line, sizeof line, f) != NULL) { + size_t len = strlen(line); + int major, scanned; + + if (len == 0 || line[len - 1] != '\n') { + break; + } + if (sscanf(line, "%d %n", &major, &scanned) == 1 && + strcmp(line + scanned, "nvme\n") == 0) { + cached = major; + break; + } + } + fclose(f); + if (cached == -1) { + fprintf(stderr, "%s: nvme driver unavailable\n", + __func__); + } + return cached; +} + int disk_info_from_fd(int fd, struct disk_info *info) { @@ -165,6 +204,12 @@ disk_info_from_fd(int fd, struct disk_info *info) return 0; } + if (get_nvme_major() >= 0 && + (uint64_t)get_nvme_major() == info->major) { + info->interface_type = nvme; + return 0; + } + if (get_virtblk_major() >= 0 && (uint64_t)get_virtblk_major() == info->major) { info->interface_type = virtblk; diff --git a/src/lib/efi.c b/src/lib/efi.c index 078bbc5..7b5e7fd 100644 --- a/src/lib/efi.c +++ b/src/lib/efi.c @@ -352,6 +352,20 @@ make_pci_device_path(uint8_t bus, uint8_t device, uint8_t function, } static ssize_t +make_nvme_device_path(uint32_t ns_id, uint8_t *buf, size_t size) +{ + NVME_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 3; + p.subtype = 23; + p.length = sizeof(p); + p.namespace_id = ns_id; + if (size >= p.length) + memcpy(buf, &p, p.length); + return p.length; +} + +static ssize_t make_scsi_device_path(uint16_t id, uint16_t lun, uint8_t *buf, size_t size) { SCSI_DEVICE_PATH p; @@ -420,13 +434,18 @@ make_edd30_device_path(int fd, uint8_t *buf, size_t size) int rc=0, interface_type; unsigned char bus=0, device=0, function=0; Scsi_Idlun idlun; + uint32_t ns_id; unsigned char host=0, channel=0, id=0, lun=0; size_t needed; off_t buf_offset = 0; rc = disk_get_pci(fd, &interface_type, &bus, &device, &function); if (rc) return 0; - if (interface_type != virtblk) { + if (interface_type == nvme) { + rc = get_nvme_ns_id(fd, &ns_id); + if (rc) + return 0; + } else if (interface_type != virtblk) { memset(&idlun, 0, sizeof(idlun)); rc = get_scsi_idlun(fd, &idlun); if (rc) return 0; @@ -444,7 +463,13 @@ make_edd30_device_path(int fd, uint8_t *buf, size_t size) return needed; buf_offset += needed; - if (interface_type != virtblk) { + if (interface_type == nvme) { + needed = make_nvme_device_path(ns_id, buf + buf_offset, + size == 0 ? 0 : size - buf_offset); + if (needed < 0) + return needed; + buf_offset += needed; + } else if (interface_type != virtblk) { needed = make_scsi_device_path(id, lun, buf + buf_offset, size == 0 ? 0 : size - buf_offset); if (needed < 0) diff --git a/src/lib/scsi_ioctls.c b/src/lib/scsi_ioctls.c index 615b48f..e013a6f 100644 --- a/src/lib/scsi_ioctls.c +++ b/src/lib/scsi_ioctls.c @@ -24,9 +24,16 @@ #include #include #include +#include #include "scsi_ioctls.h" int +get_nvme_ns_id(int fd, uint32_t *ns_id) +{ + return ioctl(fd, NVME_IOCTL_ID, &ns_id); +} + +int idlun_to_components (Scsi_Idlun *idlun, unsigned char *host, unsigned char *channel, -- 1.9.3