Blob Blame History Raw
From 47b2772a8e419ebadcf54182bfbd6170f7ab24f1 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
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 <pjones@redhat.com>
---
 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 <sys/stat.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <linux/nvme.h>
 #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