You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3458 lines
92 KiB
3458 lines
92 KiB
--- |
|
Makefile.inc | 1 |
|
libmultipath/Makefile | 7 |
|
libmultipath/hwtable.c | 1 |
|
libmultipath/nvme-ioctl.c | 869 ++++++++++++++++++++ |
|
libmultipath/nvme-ioctl.h | 139 +++ |
|
libmultipath/nvme-lib.c | 49 + |
|
libmultipath/nvme-lib.h | 39 |
|
libmultipath/nvme/argconfig.h | 99 ++ |
|
libmultipath/nvme/json.h | 87 ++ |
|
libmultipath/nvme/linux/nvme.h | 1450 +++++++++++++++++++++++++++++++++++ |
|
libmultipath/nvme/linux/nvme_ioctl.h | 67 + |
|
libmultipath/nvme/nvme.h | 163 +++ |
|
libmultipath/nvme/plugin.h | 36 |
|
libmultipath/prio.h | 1 |
|
libmultipath/prioritizers/Makefile | 4 |
|
libmultipath/prioritizers/ana.c | 236 +++++ |
|
libmultipath/propsel.c | 10 |
|
libmultipath/util.h | 2 |
|
multipath/multipath.conf.5 | 3 |
|
19 files changed, 3258 insertions(+), 5 deletions(-) |
|
|
|
Index: multipath-tools-130222/libmultipath/nvme/argconfig.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme/argconfig.h |
|
@@ -0,0 +1,99 @@ |
|
+//////////////////////////////////////////////////////////////////////// |
|
+// |
|
+// Copyright 2014 PMC-Sierra, Inc. |
|
+// |
|
+// This program is free software; you can redistribute it and/or |
|
+// modify it under the terms of the GNU General Public License |
|
+// as published by the Free Software Foundation; either version 2 |
|
+// of the License, or (at your option) any later version. |
|
+// |
|
+// This program is distributed in the hope that it will be useful, |
|
+// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
+// GNU General Public License for more details. |
|
+// |
|
+// You should have received a copy of the GNU 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. |
|
+// |
|
+//////////////////////////////////////////////////////////////////////// |
|
+ |
|
+//////////////////////////////////////////////////////////////////////// |
|
+// |
|
+// Author: Logan Gunthorpe <logang@deltatee.com> |
|
+// Logan Gunthorpe |
|
+// |
|
+// Date: Oct 23 2014 |
|
+// |
|
+// Description: |
|
+// Header file for argconfig.c |
|
+// |
|
+//////////////////////////////////////////////////////////////////////// |
|
+ |
|
+#ifndef argconfig_H |
|
+#define argconfig_H |
|
+ |
|
+#include <string.h> |
|
+#include <getopt.h> |
|
+#include <stdarg.h> |
|
+ |
|
+enum argconfig_types { |
|
+ CFG_NONE, |
|
+ CFG_STRING, |
|
+ CFG_INT, |
|
+ CFG_SIZE, |
|
+ CFG_LONG, |
|
+ CFG_LONG_SUFFIX, |
|
+ CFG_DOUBLE, |
|
+ CFG_BOOL, |
|
+ CFG_BYTE, |
|
+ CFG_SHORT, |
|
+ CFG_POSITIVE, |
|
+ CFG_INCREMENT, |
|
+ CFG_SUBOPTS, |
|
+ CFG_FILE_A, |
|
+ CFG_FILE_W, |
|
+ CFG_FILE_R, |
|
+ CFG_FILE_AP, |
|
+ CFG_FILE_WP, |
|
+ CFG_FILE_RP, |
|
+}; |
|
+ |
|
+struct argconfig_commandline_options { |
|
+ const char *option; |
|
+ const char short_option; |
|
+ const char *meta; |
|
+ enum argconfig_types config_type; |
|
+ void *default_value; |
|
+ int argument_type; |
|
+ const char *help; |
|
+}; |
|
+ |
|
+#define CFG_MAX_SUBOPTS 500 |
|
+#define MAX_HELP_FUNC 20 |
|
+ |
|
+#ifdef __cplusplus |
|
+extern "C" { |
|
+#endif |
|
+ |
|
+typedef void argconfig_help_func(void); |
|
+void argconfig_append_usage(const char *str); |
|
+void argconfig_print_help(const char *program_desc, |
|
+ const struct argconfig_commandline_options *options); |
|
+int argconfig_parse(int argc, char *argv[], const char *program_desc, |
|
+ const struct argconfig_commandline_options *options, |
|
+ void *config_out, size_t config_size); |
|
+int argconfig_parse_subopt_string(char *string, char **options, |
|
+ size_t max_options); |
|
+unsigned argconfig_parse_comma_sep_array(char *string, int *ret, |
|
+ unsigned max_length); |
|
+unsigned argconfig_parse_comma_sep_array_long(char *string, |
|
+ unsigned long long *ret, |
|
+ unsigned max_length); |
|
+void argconfig_register_help_func(argconfig_help_func * f); |
|
+ |
|
+void print_word_wrapped(const char *s, int indent, int start); |
|
+#ifdef __cplusplus |
|
+} |
|
+#endif |
|
+#endif |
|
Index: multipath-tools-130222/libmultipath/nvme/json.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme/json.h |
|
@@ -0,0 +1,87 @@ |
|
+#ifndef __JSON__H |
|
+#define __JSON__H |
|
+ |
|
+struct json_object; |
|
+struct json_array; |
|
+struct json_pair; |
|
+ |
|
+#define JSON_TYPE_STRING 0 |
|
+#define JSON_TYPE_INTEGER 1 |
|
+#define JSON_TYPE_FLOAT 2 |
|
+#define JSON_TYPE_OBJECT 3 |
|
+#define JSON_TYPE_ARRAY 4 |
|
+#define JSON_TYPE_UINT 5 |
|
+#define JSON_PARENT_TYPE_PAIR 0 |
|
+#define JSON_PARENT_TYPE_ARRAY 1 |
|
+struct json_value { |
|
+ int type; |
|
+ union { |
|
+ long long integer_number; |
|
+ unsigned long long uint_number; |
|
+ long double float_number; |
|
+ char *string; |
|
+ struct json_object *object; |
|
+ struct json_array *array; |
|
+ }; |
|
+ int parent_type; |
|
+ union { |
|
+ struct json_pair *parent_pair; |
|
+ struct json_array *parent_array; |
|
+ }; |
|
+}; |
|
+ |
|
+struct json_array { |
|
+ struct json_value **values; |
|
+ int value_cnt; |
|
+ struct json_value *parent; |
|
+}; |
|
+ |
|
+struct json_object { |
|
+ struct json_pair **pairs; |
|
+ int pair_cnt; |
|
+ struct json_value *parent; |
|
+}; |
|
+ |
|
+struct json_pair { |
|
+ char *name; |
|
+ struct json_value *value; |
|
+ struct json_object *parent; |
|
+}; |
|
+ |
|
+struct json_object *json_create_object(void); |
|
+struct json_array *json_create_array(void); |
|
+ |
|
+void json_free_object(struct json_object *obj); |
|
+ |
|
+int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...); |
|
+#define json_object_add_value_int(obj, name, val) \ |
|
+ json_object_add_value_type((obj), name, JSON_TYPE_INTEGER, (long long) (val)) |
|
+#define json_object_add_value_uint(obj, name, val) \ |
|
+ json_object_add_value_type((obj), name, JSON_TYPE_UINT, (unsigned long long) (val)) |
|
+#define json_object_add_value_float(obj, name, val) \ |
|
+ json_object_add_value_type((obj), name, JSON_TYPE_FLOAT, (val)) |
|
+#define json_object_add_value_string(obj, name, val) \ |
|
+ json_object_add_value_type((obj), name, JSON_TYPE_STRING, (val)) |
|
+#define json_object_add_value_object(obj, name, val) \ |
|
+ json_object_add_value_type((obj), name, JSON_TYPE_OBJECT, (val)) |
|
+#define json_object_add_value_array(obj, name, val) \ |
|
+ json_object_add_value_type((obj), name, JSON_TYPE_ARRAY, (val)) |
|
+int json_array_add_value_type(struct json_array *array, int type, ...); |
|
+#define json_array_add_value_int(obj, val) \ |
|
+ json_array_add_value_type((obj), JSON_TYPE_INTEGER, (val)) |
|
+#define json_array_add_value_uint(obj, val) \ |
|
+ json_array_add_value_type((obj), JSON_TYPE_UINT, (val)) |
|
+#define json_array_add_value_float(obj, val) \ |
|
+ json_array_add_value_type((obj), JSON_TYPE_FLOAT, (val)) |
|
+#define json_array_add_value_string(obj, val) \ |
|
+ json_array_add_value_type((obj), JSON_TYPE_STRING, (val)) |
|
+#define json_array_add_value_object(obj, val) \ |
|
+ json_array_add_value_type((obj), JSON_TYPE_OBJECT, (val)) |
|
+#define json_array_add_value_array(obj, val) \ |
|
+ json_array_add_value_type((obj), JSON_TYPE_ARRAY, (val)) |
|
+ |
|
+#define json_array_last_value_object(obj) \ |
|
+ (obj->values[obj->value_cnt - 1]->object) |
|
+ |
|
+void json_print_object(struct json_object *obj, void *); |
|
+#endif |
|
Index: multipath-tools-130222/libmultipath/nvme/nvme.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme/nvme.h |
|
@@ -0,0 +1,163 @@ |
|
+/* |
|
+ * Definitions for the NVM Express interface |
|
+ * Copyright (c) 2011-2014, Intel Corporation. |
|
+ * |
|
+ * This program is free software; you can redistribute it and/or modify it |
|
+ * under the terms and conditions of the GNU General Public License, |
|
+ * version 2, as published by the Free Software Foundation. |
|
+ * |
|
+ * This program is distributed in the hope it will be useful, but WITHOUT |
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|
+ * more details. |
|
+ */ |
|
+ |
|
+#ifndef _NVME_H |
|
+#define _NVME_H |
|
+ |
|
+#include <stdbool.h> |
|
+#include <stdint.h> |
|
+#include <endian.h> |
|
+#include "plugin.h" |
|
+#include "json.h" |
|
+ |
|
+#define unlikely(x) x |
|
+ |
|
+#ifdef LIBUUID |
|
+#include <uuid/uuid.h> |
|
+#else |
|
+typedef struct { |
|
+ uint8_t b[16]; |
|
+} uuid_t; |
|
+#endif |
|
+ |
|
+#include "linux/nvme.h" |
|
+ |
|
+struct nvme_effects_log_page { |
|
+ __le32 acs[256]; |
|
+ __le32 iocs[256]; |
|
+ __u8 resv[2048]; |
|
+}; |
|
+ |
|
+struct nvme_error_log_page { |
|
+ __u64 error_count; |
|
+ __u16 sqid; |
|
+ __u16 cmdid; |
|
+ __u16 status_field; |
|
+ __u16 parm_error_location; |
|
+ __u64 lba; |
|
+ __u32 nsid; |
|
+ __u8 vs; |
|
+ __u8 resv[3]; |
|
+ __u64 cs; |
|
+ __u8 resv2[24]; |
|
+}; |
|
+ |
|
+struct nvme_firmware_log_page { |
|
+ __u8 afi; |
|
+ __u8 resv[7]; |
|
+ __u64 frs[7]; |
|
+ __u8 resv2[448]; |
|
+}; |
|
+ |
|
+/* idle and active power scales occupy the last 2 bits of the field */ |
|
+#define POWER_SCALE(s) ((s) >> 6) |
|
+ |
|
+struct nvme_host_mem_buffer { |
|
+ __u32 hsize; |
|
+ __u32 hmdlal; |
|
+ __u32 hmdlau; |
|
+ __u32 hmdlec; |
|
+ __u8 rsvd16[4080]; |
|
+}; |
|
+ |
|
+struct nvme_auto_pst { |
|
+ __u32 data; |
|
+ __u32 rsvd32; |
|
+}; |
|
+ |
|
+struct nvme_timestamp { |
|
+ __u8 timestamp[6]; |
|
+ __u8 attr; |
|
+ __u8 rsvd; |
|
+}; |
|
+ |
|
+struct nvme_controller_list { |
|
+ __le16 num; |
|
+ __le16 identifier[]; |
|
+}; |
|
+ |
|
+struct nvme_bar_cap { |
|
+ __u16 mqes; |
|
+ __u8 ams_cqr; |
|
+ __u8 to; |
|
+ __u16 bps_css_nssrs_dstrd; |
|
+ __u8 mpsmax_mpsmin; |
|
+ __u8 reserved; |
|
+}; |
|
+ |
|
+#ifdef __CHECKER__ |
|
+#define __force __attribute__((force)) |
|
+#else |
|
+#define __force |
|
+#endif |
|
+ |
|
+#define cpu_to_le16(x) \ |
|
+ ((__force __le16)htole16(x)) |
|
+#define cpu_to_le32(x) \ |
|
+ ((__force __le32)htole32(x)) |
|
+#define cpu_to_le64(x) \ |
|
+ ((__force __le64)htole64(x)) |
|
+ |
|
+#define le16_to_cpu(x) \ |
|
+ le16toh((__force __u16)(x)) |
|
+#define le32_to_cpu(x) \ |
|
+ le32toh((__force __u32)(x)) |
|
+#define le64_to_cpu(x) \ |
|
+ le64toh((__force __u64)(x)) |
|
+ |
|
+#define MAX_LIST_ITEMS 256 |
|
+struct list_item { |
|
+ char node[1024]; |
|
+ struct nvme_id_ctrl ctrl; |
|
+ int nsid; |
|
+ struct nvme_id_ns ns; |
|
+ unsigned block; |
|
+}; |
|
+ |
|
+struct ctrl_list_item { |
|
+ char *name; |
|
+ char *address; |
|
+ char *transport; |
|
+ char *state; |
|
+ char *ana_state; |
|
+}; |
|
+ |
|
+struct subsys_list_item { |
|
+ char *name; |
|
+ char *subsysnqn; |
|
+ int nctrls; |
|
+ struct ctrl_list_item *ctrls; |
|
+}; |
|
+ |
|
+enum { |
|
+ NORMAL, |
|
+ JSON, |
|
+ BINARY, |
|
+}; |
|
+ |
|
+void register_extension(struct plugin *plugin); |
|
+ |
|
+#include "argconfig.h" |
|
+int parse_and_open(int argc, char **argv, const char *desc, |
|
+ const struct argconfig_commandline_options *clo, void *cfg, size_t size); |
|
+ |
|
+extern const char *devicename; |
|
+ |
|
+int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin, void (*vs)(__u8 *vs, struct json_object *root)); |
|
+int validate_output_format(char *format); |
|
+ |
|
+struct subsys_list_item *get_subsys_list(int *subcnt, char *subsysnqn, __u32 nsid); |
|
+void free_subsys_list(struct subsys_list_item *slist, int n); |
|
+char *nvme_char_from_block(char *block); |
|
+#endif /* _NVME_H */ |
|
Index: multipath-tools-130222/libmultipath/nvme/plugin.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme/plugin.h |
|
@@ -0,0 +1,36 @@ |
|
+#ifndef PLUGIN_H |
|
+#define PLUGIN_H |
|
+ |
|
+#include <stdbool.h> |
|
+ |
|
+struct program { |
|
+ const char *name; |
|
+ const char *version; |
|
+ const char *usage; |
|
+ const char *desc; |
|
+ const char *more; |
|
+ struct command **commands; |
|
+ struct plugin *extensions; |
|
+}; |
|
+ |
|
+struct plugin { |
|
+ const char *name; |
|
+ const char *desc; |
|
+ struct command **commands; |
|
+ struct program *parent; |
|
+ struct plugin *next; |
|
+ struct plugin *tail; |
|
+}; |
|
+ |
|
+struct command { |
|
+ char *name; |
|
+ char *help; |
|
+ int (*fn)(int argc, char **argv, struct command *command, struct plugin *plugin); |
|
+ char *alias; |
|
+}; |
|
+ |
|
+void usage(struct plugin *plugin); |
|
+void general_help(struct plugin *plugin); |
|
+int handle_plugin(int argc, char **argv, struct plugin *plugin); |
|
+ |
|
+#endif |
|
Index: multipath-tools-130222/libmultipath/nvme/linux/nvme.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme/linux/nvme.h |
|
@@ -0,0 +1,1450 @@ |
|
+/* |
|
+ * Definitions for the NVM Express interface |
|
+ * Copyright (c) 2011-2014, Intel Corporation. |
|
+ * |
|
+ * This program is free software; you can redistribute it and/or modify it |
|
+ * under the terms and conditions of the GNU General Public License, |
|
+ * version 2, as published by the Free Software Foundation. |
|
+ * |
|
+ * This program is distributed in the hope it will be useful, but WITHOUT |
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|
+ * more details. |
|
+ */ |
|
+ |
|
+#ifndef _LINUX_NVME_H |
|
+#define _LINUX_NVME_H |
|
+ |
|
+#include <linux/types.h> |
|
+#include <linux/uuid.h> |
|
+ |
|
+/* NQN names in commands fields specified one size */ |
|
+#define NVMF_NQN_FIELD_LEN 256 |
|
+ |
|
+/* However the max length of a qualified name is another size */ |
|
+#define NVMF_NQN_SIZE 223 |
|
+ |
|
+#define NVMF_TRSVCID_SIZE 32 |
|
+#define NVMF_TRADDR_SIZE 256 |
|
+#define NVMF_TSAS_SIZE 256 |
|
+ |
|
+#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery" |
|
+ |
|
+#define NVME_RDMA_IP_PORT 4420 |
|
+ |
|
+#define NVME_NSID_ALL 0xffffffff |
|
+ |
|
+enum nvme_subsys_type { |
|
+ NVME_NQN_DISC = 1, /* Discovery type target subsystem */ |
|
+ NVME_NQN_NVME = 2, /* NVME type target subsystem */ |
|
+}; |
|
+ |
|
+/* Address Family codes for Discovery Log Page entry ADRFAM field */ |
|
+enum { |
|
+ NVMF_ADDR_FAMILY_PCI = 0, /* PCIe */ |
|
+ NVMF_ADDR_FAMILY_IP4 = 1, /* IP4 */ |
|
+ NVMF_ADDR_FAMILY_IP6 = 2, /* IP6 */ |
|
+ NVMF_ADDR_FAMILY_IB = 3, /* InfiniBand */ |
|
+ NVMF_ADDR_FAMILY_FC = 4, /* Fibre Channel */ |
|
+}; |
|
+ |
|
+/* Transport Type codes for Discovery Log Page entry TRTYPE field */ |
|
+enum { |
|
+ NVMF_TRTYPE_RDMA = 1, /* RDMA */ |
|
+ NVMF_TRTYPE_FC = 2, /* Fibre Channel */ |
|
+ NVMF_TRTYPE_TCP = 3, /* TCP */ |
|
+ NVMF_TRTYPE_LOOP = 254, /* Reserved for host usage */ |
|
+ NVMF_TRTYPE_MAX, |
|
+}; |
|
+ |
|
+/* Transport Requirements codes for Discovery Log Page entry TREQ field */ |
|
+enum { |
|
+ NVMF_TREQ_NOT_SPECIFIED = 0, /* Not specified */ |
|
+ NVMF_TREQ_REQUIRED = 1, /* Required */ |
|
+ NVMF_TREQ_NOT_REQUIRED = 2, /* Not Required */ |
|
+ NVMF_TREQ_DISABLE_SQFLOW = (1 << 2), /* SQ flow control disable supported */ |
|
+}; |
|
+ |
|
+/* RDMA QP Service Type codes for Discovery Log Page entry TSAS |
|
+ * RDMA_QPTYPE field |
|
+ */ |
|
+enum { |
|
+ NVMF_RDMA_QPTYPE_CONNECTED = 1, /* Reliable Connected */ |
|
+ NVMF_RDMA_QPTYPE_DATAGRAM = 2, /* Reliable Datagram */ |
|
+}; |
|
+ |
|
+/* RDMA QP Service Type codes for Discovery Log Page entry TSAS |
|
+ * RDMA_QPTYPE field |
|
+ */ |
|
+enum { |
|
+ NVMF_RDMA_PRTYPE_NOT_SPECIFIED = 1, /* No Provider Specified */ |
|
+ NVMF_RDMA_PRTYPE_IB = 2, /* InfiniBand */ |
|
+ NVMF_RDMA_PRTYPE_ROCE = 3, /* InfiniBand RoCE */ |
|
+ NVMF_RDMA_PRTYPE_ROCEV2 = 4, /* InfiniBand RoCEV2 */ |
|
+ NVMF_RDMA_PRTYPE_IWARP = 5, /* IWARP */ |
|
+}; |
|
+ |
|
+/* RDMA Connection Management Service Type codes for Discovery Log Page |
|
+ * entry TSAS RDMA_CMS field |
|
+ */ |
|
+enum { |
|
+ NVMF_RDMA_CMS_RDMA_CM = 1, /* Sockets based endpoint addressing */ |
|
+}; |
|
+ |
|
+/* TCP port security type for Discovery Log Page entry TSAS |
|
+ */ |
|
+enum { |
|
+ NVMF_TCP_SECTYPE_NONE = 0, /* No Security */ |
|
+ NVMF_TCP_SECTYPE_TLS = 1, /* Transport Layer Security */ |
|
+}; |
|
+ |
|
+#define NVME_AQ_DEPTH 32 |
|
+#define NVME_NR_AEN_COMMANDS 1 |
|
+#define NVME_AQ_BLK_MQ_DEPTH (NVME_AQ_DEPTH - NVME_NR_AEN_COMMANDS) |
|
+ |
|
+/* |
|
+ * Subtract one to leave an empty queue entry for 'Full Queue' condition. See |
|
+ * NVM-Express 1.2 specification, section 4.1.2. |
|
+ */ |
|
+#define NVME_AQ_MQ_TAG_DEPTH (NVME_AQ_BLK_MQ_DEPTH - 1) |
|
+ |
|
+enum { |
|
+ NVME_REG_CAP = 0x0000, /* Controller Capabilities */ |
|
+ NVME_REG_VS = 0x0008, /* Version */ |
|
+ NVME_REG_INTMS = 0x000c, /* Interrupt Mask Set */ |
|
+ NVME_REG_INTMC = 0x0010, /* Interrupt Mask Clear */ |
|
+ NVME_REG_CC = 0x0014, /* Controller Configuration */ |
|
+ NVME_REG_CSTS = 0x001c, /* Controller Status */ |
|
+ NVME_REG_NSSR = 0x0020, /* NVM Subsystem Reset */ |
|
+ NVME_REG_AQA = 0x0024, /* Admin Queue Attributes */ |
|
+ NVME_REG_ASQ = 0x0028, /* Admin SQ Base Address */ |
|
+ NVME_REG_ACQ = 0x0030, /* Admin CQ Base Address */ |
|
+ NVME_REG_CMBLOC = 0x0038, /* Controller Memory Buffer Location */ |
|
+ NVME_REG_CMBSZ = 0x003c, /* Controller Memory Buffer Size */ |
|
+ NVME_REG_BPINFO = 0x0040, /* Boot Partition Information */ |
|
+ NVME_REG_BPRSEL = 0x0044, /* Boot Partition Read Select */ |
|
+ NVME_REG_BPMBL = 0x0048, /* Boot Partition Memory Buffer Location */ |
|
+ NVME_REG_DBS = 0x1000, /* SQ 0 Tail Doorbell */ |
|
+}; |
|
+ |
|
+#define NVME_CAP_MQES(cap) ((cap) & 0xffff) |
|
+#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) |
|
+#define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf) |
|
+#define NVME_CAP_NSSRC(cap) (((cap) >> 36) & 0x1) |
|
+#define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf) |
|
+#define NVME_CAP_MPSMAX(cap) (((cap) >> 52) & 0xf) |
|
+ |
|
+#define NVME_CMB_BIR(cmbloc) ((cmbloc) & 0x7) |
|
+#define NVME_CMB_OFST(cmbloc) (((cmbloc) >> 12) & 0xfffff) |
|
+#define NVME_CMB_SZ(cmbsz) (((cmbsz) >> 12) & 0xfffff) |
|
+#define NVME_CMB_SZU(cmbsz) (((cmbsz) >> 8) & 0xf) |
|
+ |
|
+#define NVME_CMB_WDS(cmbsz) ((cmbsz) & 0x10) |
|
+#define NVME_CMB_RDS(cmbsz) ((cmbsz) & 0x8) |
|
+#define NVME_CMB_LISTS(cmbsz) ((cmbsz) & 0x4) |
|
+#define NVME_CMB_CQS(cmbsz) ((cmbsz) & 0x2) |
|
+#define NVME_CMB_SQS(cmbsz) ((cmbsz) & 0x1) |
|
+ |
|
+/* |
|
+ * Submission and Completion Queue Entry Sizes for the NVM command set. |
|
+ * (In bytes and specified as a power of two (2^n)). |
|
+ */ |
|
+#define NVME_NVM_IOSQES 6 |
|
+#define NVME_NVM_IOCQES 4 |
|
+ |
|
+enum { |
|
+ NVME_CC_ENABLE = 1 << 0, |
|
+ NVME_CC_CSS_NVM = 0 << 4, |
|
+ NVME_CC_EN_SHIFT = 0, |
|
+ NVME_CC_CSS_SHIFT = 4, |
|
+ NVME_CC_MPS_SHIFT = 7, |
|
+ NVME_CC_AMS_SHIFT = 11, |
|
+ NVME_CC_SHN_SHIFT = 14, |
|
+ NVME_CC_IOSQES_SHIFT = 16, |
|
+ NVME_CC_IOCQES_SHIFT = 20, |
|
+ NVME_CC_AMS_RR = 0 << NVME_CC_AMS_SHIFT, |
|
+ NVME_CC_AMS_WRRU = 1 << NVME_CC_AMS_SHIFT, |
|
+ NVME_CC_AMS_VS = 7 << NVME_CC_AMS_SHIFT, |
|
+ NVME_CC_SHN_NONE = 0 << NVME_CC_SHN_SHIFT, |
|
+ NVME_CC_SHN_NORMAL = 1 << NVME_CC_SHN_SHIFT, |
|
+ NVME_CC_SHN_ABRUPT = 2 << NVME_CC_SHN_SHIFT, |
|
+ NVME_CC_SHN_MASK = 3 << NVME_CC_SHN_SHIFT, |
|
+ NVME_CC_IOSQES = NVME_NVM_IOSQES << NVME_CC_IOSQES_SHIFT, |
|
+ NVME_CC_IOCQES = NVME_NVM_IOCQES << NVME_CC_IOCQES_SHIFT, |
|
+ NVME_CSTS_RDY = 1 << 0, |
|
+ NVME_CSTS_CFS = 1 << 1, |
|
+ NVME_CSTS_NSSRO = 1 << 4, |
|
+ NVME_CSTS_PP = 1 << 5, |
|
+ NVME_CSTS_SHST_NORMAL = 0 << 2, |
|
+ NVME_CSTS_SHST_OCCUR = 1 << 2, |
|
+ NVME_CSTS_SHST_CMPLT = 2 << 2, |
|
+ NVME_CSTS_SHST_MASK = 3 << 2, |
|
+}; |
|
+ |
|
+struct nvme_id_power_state { |
|
+ __le16 max_power; /* centiwatts */ |
|
+ __u8 rsvd2; |
|
+ __u8 flags; |
|
+ __le32 entry_lat; /* microseconds */ |
|
+ __le32 exit_lat; /* microseconds */ |
|
+ __u8 read_tput; |
|
+ __u8 read_lat; |
|
+ __u8 write_tput; |
|
+ __u8 write_lat; |
|
+ __le16 idle_power; |
|
+ __u8 idle_scale; |
|
+ __u8 rsvd19; |
|
+ __le16 active_power; |
|
+ __u8 active_work_scale; |
|
+ __u8 rsvd23[9]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_PS_FLAGS_MAX_POWER_SCALE = 1 << 0, |
|
+ NVME_PS_FLAGS_NON_OP_STATE = 1 << 1, |
|
+}; |
|
+ |
|
+struct nvme_id_ctrl { |
|
+ __le16 vid; |
|
+ __le16 ssvid; |
|
+ char sn[20]; |
|
+ char mn[40]; |
|
+ char fr[8]; |
|
+ __u8 rab; |
|
+ __u8 ieee[3]; |
|
+ __u8 cmic; |
|
+ __u8 mdts; |
|
+ __le16 cntlid; |
|
+ __le32 ver; |
|
+ __le32 rtd3r; |
|
+ __le32 rtd3e; |
|
+ __le32 oaes; |
|
+ __le32 ctratt; |
|
+ __le16 rrls; |
|
+ __u8 rsvd102[154]; |
|
+ __le16 oacs; |
|
+ __u8 acl; |
|
+ __u8 aerl; |
|
+ __u8 frmw; |
|
+ __u8 lpa; |
|
+ __u8 elpe; |
|
+ __u8 npss; |
|
+ __u8 avscc; |
|
+ __u8 apsta; |
|
+ __le16 wctemp; |
|
+ __le16 cctemp; |
|
+ __le16 mtfa; |
|
+ __le32 hmpre; |
|
+ __le32 hmmin; |
|
+ __u8 tnvmcap[16]; |
|
+ __u8 unvmcap[16]; |
|
+ __le32 rpmbs; |
|
+ __le16 edstt; |
|
+ __u8 dsto; |
|
+ __u8 fwug; |
|
+ __le16 kas; |
|
+ __le16 hctma; |
|
+ __le16 mntmt; |
|
+ __le16 mxtmt; |
|
+ __le32 sanicap; |
|
+ __le32 hmminds; |
|
+ __le16 hmmaxd; |
|
+ __le16 nsetidmax; |
|
+ __u8 rsvd340[2]; |
|
+ __u8 anatt; |
|
+ __u8 anacap; |
|
+ __le32 anagrpmax; |
|
+ __le32 nanagrpid; |
|
+ __u8 rsvd352[160]; |
|
+ __u8 sqes; |
|
+ __u8 cqes; |
|
+ __le16 maxcmd; |
|
+ __le32 nn; |
|
+ __le16 oncs; |
|
+ __le16 fuses; |
|
+ __u8 fna; |
|
+ __u8 vwc; |
|
+ __le16 awun; |
|
+ __le16 awupf; |
|
+ __u8 nvscc; |
|
+ __u8 nwpc; |
|
+ __le16 acwu; |
|
+ __u8 rsvd534[2]; |
|
+ __le32 sgls; |
|
+ __le32 mnan; |
|
+ __u8 rsvd544[224]; |
|
+ char subnqn[256]; |
|
+ __u8 rsvd1024[768]; |
|
+ __le32 ioccsz; |
|
+ __le32 iorcsz; |
|
+ __le16 icdoff; |
|
+ __u8 ctrattr; |
|
+ __u8 msdbd; |
|
+ __u8 rsvd1804[244]; |
|
+ struct nvme_id_power_state psd[32]; |
|
+ __u8 vs[1024]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_CTRL_ONCS_COMPARE = 1 << 0, |
|
+ NVME_CTRL_ONCS_WRITE_UNCORRECTABLE = 1 << 1, |
|
+ NVME_CTRL_ONCS_DSM = 1 << 2, |
|
+ NVME_CTRL_ONCS_WRITE_ZEROES = 1 << 3, |
|
+ NVME_CTRL_ONCS_TIMESTAMP = 1 << 6, |
|
+ NVME_CTRL_VWC_PRESENT = 1 << 0, |
|
+ NVME_CTRL_OACS_SEC_SUPP = 1 << 0, |
|
+ NVME_CTRL_OACS_DIRECTIVES = 1 << 5, |
|
+ NVME_CTRL_OACS_DBBUF_SUPP = 1 << 8, |
|
+ NVME_CTRL_LPA_CMD_EFFECTS_LOG = 1 << 1, |
|
+ NVME_CTRL_CTRATT_128_ID = 1 << 0, |
|
+ NVME_CTRL_CTRATT_NON_OP_PSP = 1 << 1, |
|
+ NVME_CTRL_CTRATT_NVM_SETS = 1 << 2, |
|
+ NVME_CTRL_CTRATT_READ_RECV_LVLS = 1 << 3, |
|
+ NVME_CTRL_CTRATT_ENDURANCE_GROUPS = 1 << 4, |
|
+ NVME_CTRL_CTRATT_PREDICTABLE_LAT = 1 << 5, |
|
+}; |
|
+ |
|
+struct nvme_lbaf { |
|
+ __le16 ms; |
|
+ __u8 ds; |
|
+ __u8 rp; |
|
+}; |
|
+ |
|
+struct nvme_id_ns { |
|
+ __le64 nsze; |
|
+ __le64 ncap; |
|
+ __le64 nuse; |
|
+ __u8 nsfeat; |
|
+ __u8 nlbaf; |
|
+ __u8 flbas; |
|
+ __u8 mc; |
|
+ __u8 dpc; |
|
+ __u8 dps; |
|
+ __u8 nmic; |
|
+ __u8 rescap; |
|
+ __u8 fpi; |
|
+ __u8 dlfeat; |
|
+ __le16 nawun; |
|
+ __le16 nawupf; |
|
+ __le16 nacwu; |
|
+ __le16 nabsn; |
|
+ __le16 nabo; |
|
+ __le16 nabspf; |
|
+ __le16 noiob; |
|
+ __u8 nvmcap[16]; |
|
+ __u8 rsvd64[28]; |
|
+ __le32 anagrpid; |
|
+ __u8 rsvd96[3]; |
|
+ __u8 nsattr; |
|
+ __le16 nvmsetid; |
|
+ __le16 endgid; |
|
+ __u8 nguid[16]; |
|
+ __u8 eui64[8]; |
|
+ struct nvme_lbaf lbaf[16]; |
|
+ __u8 rsvd192[192]; |
|
+ __u8 vs[3712]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_ID_CNS_NS = 0x00, |
|
+ NVME_ID_CNS_CTRL = 0x01, |
|
+ NVME_ID_CNS_NS_ACTIVE_LIST = 0x02, |
|
+ NVME_ID_CNS_NS_DESC_LIST = 0x03, |
|
+ NVME_ID_CNS_NVMSET_LIST = 0x04, |
|
+ NVME_ID_CNS_NS_PRESENT_LIST = 0x10, |
|
+ NVME_ID_CNS_NS_PRESENT = 0x11, |
|
+ NVME_ID_CNS_CTRL_NS_LIST = 0x12, |
|
+ NVME_ID_CNS_CTRL_LIST = 0x13, |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_DIR_IDENTIFY = 0x00, |
|
+ NVME_DIR_STREAMS = 0x01, |
|
+ NVME_DIR_SND_ID_OP_ENABLE = 0x01, |
|
+ NVME_DIR_SND_ST_OP_REL_ID = 0x01, |
|
+ NVME_DIR_SND_ST_OP_REL_RSC = 0x02, |
|
+ NVME_DIR_RCV_ID_OP_PARAM = 0x01, |
|
+ NVME_DIR_RCV_ST_OP_PARAM = 0x01, |
|
+ NVME_DIR_RCV_ST_OP_STATUS = 0x02, |
|
+ NVME_DIR_RCV_ST_OP_RESOURCE = 0x03, |
|
+ NVME_DIR_ENDIR = 0x01, |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_NS_FEAT_THIN = 1 << 0, |
|
+ NVME_NS_FLBAS_LBA_MASK = 0xf, |
|
+ NVME_NS_FLBAS_META_EXT = 0x10, |
|
+ NVME_LBAF_RP_BEST = 0, |
|
+ NVME_LBAF_RP_BETTER = 1, |
|
+ NVME_LBAF_RP_GOOD = 2, |
|
+ NVME_LBAF_RP_DEGRADED = 3, |
|
+ NVME_NS_DPC_PI_LAST = 1 << 4, |
|
+ NVME_NS_DPC_PI_FIRST = 1 << 3, |
|
+ NVME_NS_DPC_PI_TYPE3 = 1 << 2, |
|
+ NVME_NS_DPC_PI_TYPE2 = 1 << 1, |
|
+ NVME_NS_DPC_PI_TYPE1 = 1 << 0, |
|
+ NVME_NS_DPS_PI_FIRST = 1 << 3, |
|
+ NVME_NS_DPS_PI_MASK = 0x7, |
|
+ NVME_NS_DPS_PI_TYPE1 = 1, |
|
+ NVME_NS_DPS_PI_TYPE2 = 2, |
|
+ NVME_NS_DPS_PI_TYPE3 = 3, |
|
+}; |
|
+ |
|
+struct nvme_ns_id_desc { |
|
+ __u8 nidt; |
|
+ __u8 nidl; |
|
+ __le16 reserved; |
|
+}; |
|
+ |
|
+#define NVME_NIDT_EUI64_LEN 8 |
|
+#define NVME_NIDT_NGUID_LEN 16 |
|
+#define NVME_NIDT_UUID_LEN 16 |
|
+ |
|
+enum { |
|
+ NVME_NIDT_EUI64 = 0x01, |
|
+ NVME_NIDT_NGUID = 0x02, |
|
+ NVME_NIDT_UUID = 0x03, |
|
+}; |
|
+ |
|
+#define NVME_MAX_NVMSET 31 |
|
+ |
|
+struct nvme_nvmset_attr_entry { |
|
+ __le16 id; |
|
+ __le16 endurance_group_id; |
|
+ __u8 rsvd4[4]; |
|
+ __le32 random_4k_read_typical; |
|
+ __le32 opt_write_size; |
|
+ __u8 total_nvmset_cap[16]; |
|
+ __u8 unalloc_nvmset_cap[16]; |
|
+ __u8 rsvd48[80]; |
|
+}; |
|
+ |
|
+struct nvme_id_nvmset { |
|
+ __u8 nid; |
|
+ __u8 rsvd1[127]; |
|
+ struct nvme_nvmset_attr_entry ent[NVME_MAX_NVMSET]; |
|
+}; |
|
+ |
|
+/* Derived from 1.3a Figure 101: Get Log Page – Telemetry Host |
|
+ * -Initiated Log (Log Identifier 07h) |
|
+ */ |
|
+struct nvme_telemetry_log_page_hdr { |
|
+ __u8 lpi; /* Log page identifier */ |
|
+ __u8 rsvd[4]; |
|
+ __u8 iee_oui[3]; |
|
+ __u16 dalb1; /* Data area 1 last block */ |
|
+ __u16 dalb2; /* Data area 2 last block */ |
|
+ __u16 dalb3; /* Data area 3 last block */ |
|
+ __u8 rsvd1[368]; /* TODO verify */ |
|
+ __u8 ctrlavail; /* Controller initiated data avail?*/ |
|
+ __u8 ctrldgn; /* Controller initiated telemetry Data Gen # */ |
|
+ __u8 rsnident[128]; |
|
+ /* We'll have to double fetch so we can get the header, |
|
+ * parse dalb1->3 determine how much size we need for the |
|
+ * log then alloc below. Or just do a secondary non-struct |
|
+ * allocation. |
|
+ */ |
|
+ __u8 telemetry_dataarea[0]; |
|
+}; |
|
+ |
|
+struct nvme_endurance_group_log { |
|
+ __u32 rsvd0; |
|
+ __u8 avl_spare_threshold; |
|
+ __u8 percent_used; |
|
+ __u8 rsvd6[26]; |
|
+ __u8 endurance_estimate[16]; |
|
+ __u8 data_units_read[16]; |
|
+ __u8 data_units_written[16]; |
|
+ __u8 media_units_written[16]; |
|
+ __u8 rsvd96[416]; |
|
+}; |
|
+ |
|
+struct nvme_smart_log { |
|
+ __u8 critical_warning; |
|
+ __u8 temperature[2]; |
|
+ __u8 avail_spare; |
|
+ __u8 spare_thresh; |
|
+ __u8 percent_used; |
|
+ __u8 rsvd6[26]; |
|
+ __u8 data_units_read[16]; |
|
+ __u8 data_units_written[16]; |
|
+ __u8 host_reads[16]; |
|
+ __u8 host_writes[16]; |
|
+ __u8 ctrl_busy_time[16]; |
|
+ __u8 power_cycles[16]; |
|
+ __u8 power_on_hours[16]; |
|
+ __u8 unsafe_shutdowns[16]; |
|
+ __u8 media_errors[16]; |
|
+ __u8 num_err_log_entries[16]; |
|
+ __le32 warning_temp_time; |
|
+ __le32 critical_comp_time; |
|
+ __le16 temp_sensor[8]; |
|
+ __le32 thm_temp1_trans_count; |
|
+ __le32 thm_temp2_trans_count; |
|
+ __le32 thm_temp1_total_time; |
|
+ __le32 thm_temp2_total_time; |
|
+ __u8 rsvd232[280]; |
|
+}; |
|
+ |
|
+struct nvme_self_test_res { |
|
+ __u8 device_self_test_status; |
|
+ __u8 segment_num; |
|
+ __u8 valid_diagnostic_info; |
|
+ __u8 rsvd; |
|
+ __le64 power_on_hours; |
|
+ __le32 nsid; |
|
+ __le64 failing_lba; |
|
+ __u8 status_code_type; |
|
+ __u8 status_code; |
|
+ __u8 vendor_specific[2]; |
|
+} __attribute__((packed)); |
|
+ |
|
+struct nvme_self_test_log { |
|
+ __u8 crnt_dev_selftest_oprn; |
|
+ __u8 crnt_dev_selftest_compln; |
|
+ __u8 rsvd[2]; |
|
+ struct nvme_self_test_res result[20]; |
|
+} __attribute__((packed)); |
|
+ |
|
+struct nvme_fw_slot_info_log { |
|
+ __u8 afi; |
|
+ __u8 rsvd1[7]; |
|
+ __le64 frs[7]; |
|
+ __u8 rsvd64[448]; |
|
+}; |
|
+ |
|
+/* NVMe Namespace Write Protect State */ |
|
+enum { |
|
+ NVME_NS_NO_WRITE_PROTECT = 0, |
|
+ NVME_NS_WRITE_PROTECT, |
|
+ NVME_NS_WRITE_PROTECT_POWER_CYCLE, |
|
+ NVME_NS_WRITE_PROTECT_PERMANENT, |
|
+}; |
|
+ |
|
+#define NVME_MAX_CHANGED_NAMESPACES 1024 |
|
+ |
|
+struct nvme_changed_ns_list_log { |
|
+ __le32 log[NVME_MAX_CHANGED_NAMESPACES]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_CMD_EFFECTS_CSUPP = 1 << 0, |
|
+ NVME_CMD_EFFECTS_LBCC = 1 << 1, |
|
+ NVME_CMD_EFFECTS_NCC = 1 << 2, |
|
+ NVME_CMD_EFFECTS_NIC = 1 << 3, |
|
+ NVME_CMD_EFFECTS_CCC = 1 << 4, |
|
+ NVME_CMD_EFFECTS_CSE_MASK = 3 << 16, |
|
+}; |
|
+ |
|
+struct nvme_effects_log { |
|
+ __le32 acs[256]; |
|
+ __le32 iocs[256]; |
|
+ __u8 resv[2048]; |
|
+}; |
|
+ |
|
+enum nvme_ana_state { |
|
+ NVME_ANA_OPTIMIZED = 0x01, |
|
+ NVME_ANA_NONOPTIMIZED = 0x02, |
|
+ NVME_ANA_INACCESSIBLE = 0x03, |
|
+ NVME_ANA_PERSISTENT_LOSS = 0x04, |
|
+ NVME_ANA_CHANGE = 0x0f, |
|
+}; |
|
+ |
|
+struct nvme_ana_group_desc { |
|
+ __le32 grpid; |
|
+ __le32 nnsids; |
|
+ __le64 chgcnt; |
|
+ __u8 state; |
|
+ __u8 rsvd17[15]; |
|
+ __le32 nsids[]; |
|
+}; |
|
+ |
|
+/* flag for the log specific field of the ANA log */ |
|
+#define NVME_ANA_LOG_RGO (1 << 0) |
|
+ |
|
+struct nvme_ana_rsp_hdr { |
|
+ __le64 chgcnt; |
|
+ __le16 ngrps; |
|
+ __le16 rsvd10[3]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_SMART_CRIT_SPARE = 1 << 0, |
|
+ NVME_SMART_CRIT_TEMPERATURE = 1 << 1, |
|
+ NVME_SMART_CRIT_RELIABILITY = 1 << 2, |
|
+ NVME_SMART_CRIT_MEDIA = 1 << 3, |
|
+ NVME_SMART_CRIT_VOLATILE_MEMORY = 1 << 4, |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_AER_ERROR = 0, |
|
+ NVME_AER_SMART = 1, |
|
+ NVME_AER_CSS = 6, |
|
+ NVME_AER_VS = 7, |
|
+ NVME_AER_NOTICE_NS_CHANGED = 0x0002, |
|
+ NVME_AER_NOTICE_ANA = 0x0003, |
|
+ NVME_AER_NOTICE_FW_ACT_STARTING = 0x0102, |
|
+}; |
|
+ |
|
+struct nvme_lba_range_type { |
|
+ __u8 type; |
|
+ __u8 attributes; |
|
+ __u8 rsvd2[14]; |
|
+ __u64 slba; |
|
+ __u64 nlb; |
|
+ __u8 guid[16]; |
|
+ __u8 rsvd48[16]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_LBART_TYPE_FS = 0x01, |
|
+ NVME_LBART_TYPE_RAID = 0x02, |
|
+ NVME_LBART_TYPE_CACHE = 0x03, |
|
+ NVME_LBART_TYPE_SWAP = 0x04, |
|
+ |
|
+ NVME_LBART_ATTRIB_TEMP = 1 << 0, |
|
+ NVME_LBART_ATTRIB_HIDE = 1 << 1, |
|
+}; |
|
+ |
|
+struct nvme_plm_config { |
|
+ __u16 enable_event; |
|
+ __u8 rsvd2[30]; |
|
+ __u64 dtwin_reads_thresh; |
|
+ __u64 dtwin_writes_thresh; |
|
+ __u64 dtwin_time_thresh; |
|
+ __u8 rsvd56[456]; |
|
+}; |
|
+ |
|
+struct nvme_reservation_status { |
|
+ __le32 gen; |
|
+ __u8 rtype; |
|
+ __u8 regctl[2]; |
|
+ __u8 resv5[2]; |
|
+ __u8 ptpls; |
|
+ __u8 resv10[13]; |
|
+ struct { |
|
+ __le16 cntlid; |
|
+ __u8 rcsts; |
|
+ __u8 resv3[5]; |
|
+ __le64 hostid; |
|
+ __le64 rkey; |
|
+ } regctl_ds[]; |
|
+}; |
|
+ |
|
+struct nvme_reservation_status_ext { |
|
+ __le32 gen; |
|
+ __u8 rtype; |
|
+ __u8 regctl[2]; |
|
+ __u8 resv5[2]; |
|
+ __u8 ptpls; |
|
+ __u8 resv10[14]; |
|
+ __u8 resv24[40]; |
|
+ struct { |
|
+ __le16 cntlid; |
|
+ __u8 rcsts; |
|
+ __u8 resv3[5]; |
|
+ __le64 rkey; |
|
+ __u8 hostid[16]; |
|
+ __u8 resv32[32]; |
|
+ } regctl_eds[]; |
|
+}; |
|
+ |
|
+enum nvme_async_event_type { |
|
+ NVME_AER_TYPE_ERROR = 0, |
|
+ NVME_AER_TYPE_SMART = 1, |
|
+ NVME_AER_TYPE_NOTICE = 2, |
|
+}; |
|
+ |
|
+/* I/O commands */ |
|
+ |
|
+enum nvme_opcode { |
|
+ nvme_cmd_flush = 0x00, |
|
+ nvme_cmd_write = 0x01, |
|
+ nvme_cmd_read = 0x02, |
|
+ nvme_cmd_write_uncor = 0x04, |
|
+ nvme_cmd_compare = 0x05, |
|
+ nvme_cmd_write_zeroes = 0x08, |
|
+ nvme_cmd_dsm = 0x09, |
|
+ nvme_cmd_resv_register = 0x0d, |
|
+ nvme_cmd_resv_report = 0x0e, |
|
+ nvme_cmd_resv_acquire = 0x11, |
|
+ nvme_cmd_resv_release = 0x15, |
|
+}; |
|
+ |
|
+/* |
|
+ * Descriptor subtype - lower 4 bits of nvme_(keyed_)sgl_desc identifier |
|
+ * |
|
+ * @NVME_SGL_FMT_ADDRESS: absolute address of the data block |
|
+ * @NVME_SGL_FMT_OFFSET: relative offset of the in-capsule data block |
|
+ * @NVME_SGL_FMT_TRANSPORT_A: transport defined format, value 0xA |
|
+ * @NVME_SGL_FMT_INVALIDATE: RDMA transport specific remote invalidation |
|
+ * request subtype |
|
+ */ |
|
+enum { |
|
+ NVME_SGL_FMT_ADDRESS = 0x00, |
|
+ NVME_SGL_FMT_OFFSET = 0x01, |
|
+ NVME_SGL_FMT_TRANSPORT_A = 0x0A, |
|
+ NVME_SGL_FMT_INVALIDATE = 0x0f, |
|
+}; |
|
+ |
|
+/* |
|
+ * Descriptor type - upper 4 bits of nvme_(keyed_)sgl_desc identifier |
|
+ * |
|
+ * For struct nvme_sgl_desc: |
|
+ * @NVME_SGL_FMT_DATA_DESC: data block descriptor |
|
+ * @NVME_SGL_FMT_SEG_DESC: sgl segment descriptor |
|
+ * @NVME_SGL_FMT_LAST_SEG_DESC: last sgl segment descriptor |
|
+ * |
|
+ * For struct nvme_keyed_sgl_desc: |
|
+ * @NVME_KEY_SGL_FMT_DATA_DESC: keyed data block descriptor |
|
+ * |
|
+ * Transport-specific SGL types: |
|
+ * @NVME_TRANSPORT_SGL_DATA_DESC: Transport SGL data dlock descriptor |
|
+ */ |
|
+enum { |
|
+ NVME_SGL_FMT_DATA_DESC = 0x00, |
|
+ NVME_SGL_FMT_SEG_DESC = 0x02, |
|
+ NVME_SGL_FMT_LAST_SEG_DESC = 0x03, |
|
+ NVME_KEY_SGL_FMT_DATA_DESC = 0x04, |
|
+ NVME_TRANSPORT_SGL_DATA_DESC = 0x05, |
|
+}; |
|
+ |
|
+struct nvme_sgl_desc { |
|
+ __le64 addr; |
|
+ __le32 length; |
|
+ __u8 rsvd[3]; |
|
+ __u8 type; |
|
+}; |
|
+ |
|
+struct nvme_keyed_sgl_desc { |
|
+ __le64 addr; |
|
+ __u8 length[3]; |
|
+ __u8 key[4]; |
|
+ __u8 type; |
|
+}; |
|
+ |
|
+union nvme_data_ptr { |
|
+ struct { |
|
+ __le64 prp1; |
|
+ __le64 prp2; |
|
+ }; |
|
+ struct nvme_sgl_desc sgl; |
|
+ struct nvme_keyed_sgl_desc ksgl; |
|
+}; |
|
+ |
|
+/* |
|
+ * Lowest two bits of our flags field (FUSE field in the spec): |
|
+ * |
|
+ * @NVME_CMD_FUSE_FIRST: Fused Operation, first command |
|
+ * @NVME_CMD_FUSE_SECOND: Fused Operation, second command |
|
+ * |
|
+ * Highest two bits in our flags field (PSDT field in the spec): |
|
+ * |
|
+ * @NVME_CMD_PSDT_SGL_METABUF: Use SGLS for this transfer, |
|
+ * If used, MPTR contains addr of single physical buffer (byte aligned). |
|
+ * @NVME_CMD_PSDT_SGL_METASEG: Use SGLS for this transfer, |
|
+ * If used, MPTR contains an address of an SGL segment containing |
|
+ * exactly 1 SGL descriptor (qword aligned). |
|
+ */ |
|
+enum { |
|
+ NVME_CMD_FUSE_FIRST = (1 << 0), |
|
+ NVME_CMD_FUSE_SECOND = (1 << 1), |
|
+ |
|
+ NVME_CMD_SGL_METABUF = (1 << 6), |
|
+ NVME_CMD_SGL_METASEG = (1 << 7), |
|
+ NVME_CMD_SGL_ALL = NVME_CMD_SGL_METABUF | NVME_CMD_SGL_METASEG, |
|
+}; |
|
+ |
|
+struct nvme_common_command { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __le32 cdw2[2]; |
|
+ __le64 metadata; |
|
+ union nvme_data_ptr dptr; |
|
+ __le32 cdw10[6]; |
|
+}; |
|
+ |
|
+struct nvme_rw_command { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2; |
|
+ __le64 metadata; |
|
+ union nvme_data_ptr dptr; |
|
+ __le64 slba; |
|
+ __le16 length; |
|
+ __le16 control; |
|
+ __le32 dsmgmt; |
|
+ __le32 reftag; |
|
+ __le16 apptag; |
|
+ __le16 appmask; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_RW_LR = 1 << 15, |
|
+ NVME_RW_FUA = 1 << 14, |
|
+ NVME_RW_DEAC = 1 << 9, |
|
+ NVME_RW_DSM_FREQ_UNSPEC = 0, |
|
+ NVME_RW_DSM_FREQ_TYPICAL = 1, |
|
+ NVME_RW_DSM_FREQ_RARE = 2, |
|
+ NVME_RW_DSM_FREQ_READS = 3, |
|
+ NVME_RW_DSM_FREQ_WRITES = 4, |
|
+ NVME_RW_DSM_FREQ_RW = 5, |
|
+ NVME_RW_DSM_FREQ_ONCE = 6, |
|
+ NVME_RW_DSM_FREQ_PREFETCH = 7, |
|
+ NVME_RW_DSM_FREQ_TEMP = 8, |
|
+ NVME_RW_DSM_LATENCY_NONE = 0 << 4, |
|
+ NVME_RW_DSM_LATENCY_IDLE = 1 << 4, |
|
+ NVME_RW_DSM_LATENCY_NORM = 2 << 4, |
|
+ NVME_RW_DSM_LATENCY_LOW = 3 << 4, |
|
+ NVME_RW_DSM_SEQ_REQ = 1 << 6, |
|
+ NVME_RW_DSM_COMPRESSED = 1 << 7, |
|
+ NVME_RW_PRINFO_PRCHK_REF = 1 << 10, |
|
+ NVME_RW_PRINFO_PRCHK_APP = 1 << 11, |
|
+ NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, |
|
+ NVME_RW_PRINFO_PRACT = 1 << 13, |
|
+ NVME_RW_DTYPE_STREAMS = 1 << 4, |
|
+}; |
|
+ |
|
+struct nvme_dsm_cmd { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2[2]; |
|
+ union nvme_data_ptr dptr; |
|
+ __le32 nr; |
|
+ __le32 attributes; |
|
+ __u32 rsvd12[4]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_DSMGMT_IDR = 1 << 0, |
|
+ NVME_DSMGMT_IDW = 1 << 1, |
|
+ NVME_DSMGMT_AD = 1 << 2, |
|
+}; |
|
+ |
|
+#define NVME_DSM_MAX_RANGES 256 |
|
+ |
|
+struct nvme_dsm_range { |
|
+ __le32 cattr; |
|
+ __le32 nlb; |
|
+ __le64 slba; |
|
+}; |
|
+ |
|
+struct nvme_write_zeroes_cmd { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2; |
|
+ __le64 metadata; |
|
+ union nvme_data_ptr dptr; |
|
+ __le64 slba; |
|
+ __le16 length; |
|
+ __le16 control; |
|
+ __le32 dsmgmt; |
|
+ __le32 reftag; |
|
+ __le16 apptag; |
|
+ __le16 appmask; |
|
+}; |
|
+ |
|
+/* Features */ |
|
+ |
|
+struct nvme_feat_auto_pst { |
|
+ __le64 entries[32]; |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_HOST_MEM_ENABLE = (1 << 0), |
|
+ NVME_HOST_MEM_RETURN = (1 << 1), |
|
+}; |
|
+ |
|
+/* Admin commands */ |
|
+ |
|
+enum nvme_admin_opcode { |
|
+ nvme_admin_delete_sq = 0x00, |
|
+ nvme_admin_create_sq = 0x01, |
|
+ nvme_admin_get_log_page = 0x02, |
|
+ nvme_admin_delete_cq = 0x04, |
|
+ nvme_admin_create_cq = 0x05, |
|
+ nvme_admin_identify = 0x06, |
|
+ nvme_admin_abort_cmd = 0x08, |
|
+ nvme_admin_set_features = 0x09, |
|
+ nvme_admin_get_features = 0x0a, |
|
+ nvme_admin_async_event = 0x0c, |
|
+ nvme_admin_ns_mgmt = 0x0d, |
|
+ nvme_admin_activate_fw = 0x10, |
|
+ nvme_admin_download_fw = 0x11, |
|
+ nvme_admin_dev_self_test = 0x14, |
|
+ nvme_admin_ns_attach = 0x15, |
|
+ nvme_admin_keep_alive = 0x18, |
|
+ nvme_admin_directive_send = 0x19, |
|
+ nvme_admin_directive_recv = 0x1a, |
|
+ nvme_admin_virtual_mgmt = 0x1c, |
|
+ nvme_admin_nvme_mi_send = 0x1d, |
|
+ nvme_admin_nvme_mi_recv = 0x1e, |
|
+ nvme_admin_dbbuf = 0x7C, |
|
+ nvme_admin_format_nvm = 0x80, |
|
+ nvme_admin_security_send = 0x81, |
|
+ nvme_admin_security_recv = 0x82, |
|
+ nvme_admin_sanitize_nvm = 0x84, |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_QUEUE_PHYS_CONTIG = (1 << 0), |
|
+ NVME_CQ_IRQ_ENABLED = (1 << 1), |
|
+ NVME_SQ_PRIO_URGENT = (0 << 1), |
|
+ NVME_SQ_PRIO_HIGH = (1 << 1), |
|
+ NVME_SQ_PRIO_MEDIUM = (2 << 1), |
|
+ NVME_SQ_PRIO_LOW = (3 << 1), |
|
+ NVME_FEAT_ARBITRATION = 0x01, |
|
+ NVME_FEAT_POWER_MGMT = 0x02, |
|
+ NVME_FEAT_LBA_RANGE = 0x03, |
|
+ NVME_FEAT_TEMP_THRESH = 0x04, |
|
+ NVME_FEAT_ERR_RECOVERY = 0x05, |
|
+ NVME_FEAT_VOLATILE_WC = 0x06, |
|
+ NVME_FEAT_NUM_QUEUES = 0x07, |
|
+ NVME_FEAT_IRQ_COALESCE = 0x08, |
|
+ NVME_FEAT_IRQ_CONFIG = 0x09, |
|
+ NVME_FEAT_WRITE_ATOMIC = 0x0a, |
|
+ NVME_FEAT_ASYNC_EVENT = 0x0b, |
|
+ NVME_FEAT_AUTO_PST = 0x0c, |
|
+ NVME_FEAT_HOST_MEM_BUF = 0x0d, |
|
+ NVME_FEAT_TIMESTAMP = 0x0e, |
|
+ NVME_FEAT_KATO = 0x0f, |
|
+ NVME_FEAT_HCTM = 0X10, |
|
+ NVME_FEAT_NOPSC = 0X11, |
|
+ NVME_FEAT_RRL = 0x12, |
|
+ NVME_FEAT_PLM_CONFIG = 0x13, |
|
+ NVME_FEAT_PLM_WINDOW = 0x14, |
|
+ NVME_FEAT_SW_PROGRESS = 0x80, |
|
+ NVME_FEAT_HOST_ID = 0x81, |
|
+ NVME_FEAT_RESV_MASK = 0x82, |
|
+ NVME_FEAT_RESV_PERSIST = 0x83, |
|
+ NVME_FEAT_WRITE_PROTECT = 0x84, |
|
+ NVME_LOG_ERROR = 0x01, |
|
+ NVME_LOG_SMART = 0x02, |
|
+ NVME_LOG_FW_SLOT = 0x03, |
|
+ NVME_LOG_CHANGED_NS = 0x04, |
|
+ NVME_LOG_CMD_EFFECTS = 0x05, |
|
+ NVME_LOG_DEVICE_SELF_TEST = 0x06, |
|
+ NVME_LOG_TELEMETRY_HOST = 0x07, |
|
+ NVME_LOG_TELEMETRY_CTRL = 0x08, |
|
+ NVME_LOG_ENDURANCE_GROUP = 0x09, |
|
+ NVME_LOG_ANA = 0x0c, |
|
+ NVME_LOG_DISC = 0x70, |
|
+ NVME_LOG_RESERVATION = 0x80, |
|
+ NVME_LOG_SANITIZE = 0x81, |
|
+ NVME_FWACT_REPL = (0 << 3), |
|
+ NVME_FWACT_REPL_ACTV = (1 << 3), |
|
+ NVME_FWACT_ACTV = (2 << 3), |
|
+}; |
|
+ |
|
+enum { |
|
+ NVME_NO_LOG_LSP = 0x0, |
|
+ NVME_NO_LOG_LPO = 0x0, |
|
+ NVME_LOG_ANA_LSP_RGO = 0x1, |
|
+ NVME_TELEM_LSP_CREATE = 0x1, |
|
+}; |
|
+ |
|
+/* Sanitize and Sanitize Monitor/Log */ |
|
+enum { |
|
+ /* Sanitize */ |
|
+ NVME_SANITIZE_NO_DEALLOC = 0x00000200, |
|
+ NVME_SANITIZE_OIPBP = 0x00000100, |
|
+ NVME_SANITIZE_OWPASS_SHIFT = 0x00000004, |
|
+ NVME_SANITIZE_AUSE = 0x00000008, |
|
+ NVME_SANITIZE_ACT_CRYPTO_ERASE = 0x00000004, |
|
+ NVME_SANITIZE_ACT_OVERWRITE = 0x00000003, |
|
+ NVME_SANITIZE_ACT_BLOCK_ERASE = 0x00000002, |
|
+ NVME_SANITIZE_ACT_EXIT = 0x00000001, |
|
+ |
|
+ /* Sanitize Monitor/Log */ |
|
+ NVME_SANITIZE_LOG_DATA_LEN = 0x0014, |
|
+ NVME_SANITIZE_LOG_GLOBAL_DATA_ERASED = 0x0100, |
|
+ NVME_SANITIZE_LOG_NUM_CMPLTED_PASS_MASK = 0x00F8, |
|
+ NVME_SANITIZE_LOG_STATUS_MASK = 0x0007, |
|
+ NVME_SANITIZE_LOG_NEVER_SANITIZED = 0x0000, |
|
+ NVME_SANITIZE_LOG_COMPLETED_SUCCESS = 0x0001, |
|
+ NVME_SANITIZE_LOG_IN_PROGESS = 0x0002, |
|
+ NVME_SANITIZE_LOG_COMPLETED_FAILED = 0x0003, |
|
+}; |
|
+ |
|
+enum { |
|
+ /* Self-test log Validation bits */ |
|
+ NVME_SELF_TEST_VALID_NSID = 1 << 0, |
|
+ NVME_SELF_TEST_VALID_FLBA = 1 << 1, |
|
+ NVME_SELF_TEST_VALID_SCT = 1 << 2, |
|
+ NVME_SELF_TEST_VALID_SC = 1 << 3, |
|
+ NVME_SELF_TEST_REPORTS = 20, |
|
+}; |
|
+ |
|
+struct nvme_identify { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2[2]; |
|
+ union nvme_data_ptr dptr; |
|
+ __u8 cns; |
|
+ __u8 rsvd3; |
|
+ __le16 ctrlid; |
|
+ __u32 rsvd11[5]; |
|
+}; |
|
+ |
|
+#define NVME_IDENTIFY_DATA_SIZE 4096 |
|
+ |
|
+struct nvme_features { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2[2]; |
|
+ union nvme_data_ptr dptr; |
|
+ __le32 fid; |
|
+ __le32 dword11; |
|
+ __le32 dword12; |
|
+ __le32 dword13; |
|
+ __le32 dword14; |
|
+ __le32 dword15; |
|
+}; |
|
+ |
|
+struct nvme_host_mem_buf_desc { |
|
+ __le64 addr; |
|
+ __le32 size; |
|
+ __u32 rsvd; |
|
+}; |
|
+ |
|
+struct nvme_create_cq { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __u32 rsvd1[5]; |
|
+ __le64 prp1; |
|
+ __u64 rsvd8; |
|
+ __le16 cqid; |
|
+ __le16 qsize; |
|
+ __le16 cq_flags; |
|
+ __le16 irq_vector; |
|
+ __u32 rsvd12[4]; |
|
+}; |
|
+ |
|
+struct nvme_create_sq { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __u32 rsvd1[5]; |
|
+ __le64 prp1; |
|
+ __u64 rsvd8; |
|
+ __le16 sqid; |
|
+ __le16 qsize; |
|
+ __le16 sq_flags; |
|
+ __le16 cqid; |
|
+ __u32 rsvd12[4]; |
|
+}; |
|
+ |
|
+struct nvme_delete_queue { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __u32 rsvd1[9]; |
|
+ __le16 qid; |
|
+ __u16 rsvd10; |
|
+ __u32 rsvd11[5]; |
|
+}; |
|
+ |
|
+struct nvme_abort_cmd { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __u32 rsvd1[9]; |
|
+ __le16 sqid; |
|
+ __u16 cid; |
|
+ __u32 rsvd11[5]; |
|
+}; |
|
+ |
|
+struct nvme_download_firmware { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __u32 rsvd1[5]; |
|
+ union nvme_data_ptr dptr; |
|
+ __le32 numd; |
|
+ __le32 offset; |
|
+ __u32 rsvd12[4]; |
|
+}; |
|
+ |
|
+struct nvme_format_cmd { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2[4]; |
|
+ __le32 cdw10; |
|
+ __u32 rsvd11[5]; |
|
+}; |
|
+ |
|
+struct nvme_get_log_page_command { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2[2]; |
|
+ union nvme_data_ptr dptr; |
|
+ __u8 lid; |
|
+ __u8 lsp; |
|
+ __le16 numdl; |
|
+ __le16 numdu; |
|
+ __u16 rsvd11; |
|
+ __le32 lpol; |
|
+ __le32 lpou; |
|
+ __u32 rsvd14[2]; |
|
+}; |
|
+ |
|
+struct nvme_directive_cmd { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __le32 nsid; |
|
+ __u64 rsvd2[2]; |
|
+ union nvme_data_ptr dptr; |
|
+ __le32 numd; |
|
+ __u8 doper; |
|
+ __u8 dtype; |
|
+ __le16 dspec; |
|
+ __u8 endir; |
|
+ __u8 tdtype; |
|
+ __u16 rsvd15; |
|
+ |
|
+ __u32 rsvd16[3]; |
|
+}; |
|
+ |
|
+/* Sanitize Log Page */ |
|
+struct nvme_sanitize_log_page { |
|
+ __le16 progress; |
|
+ __le16 status; |
|
+ __le32 cdw10_info; |
|
+ __le32 est_ovrwrt_time; |
|
+ __le32 est_blk_erase_time; |
|
+ __le32 est_crypto_erase_time; |
|
+}; |
|
+ |
|
+/* |
|
+ * Fabrics subcommands. |
|
+ */ |
|
+enum nvmf_fabrics_opcode { |
|
+ nvme_fabrics_command = 0x7f, |
|
+}; |
|
+ |
|
+enum nvmf_capsule_command { |
|
+ nvme_fabrics_type_property_set = 0x00, |
|
+ nvme_fabrics_type_connect = 0x01, |
|
+ nvme_fabrics_type_property_get = 0x04, |
|
+}; |
|
+ |
|
+struct nvmf_common_command { |
|
+ __u8 opcode; |
|
+ __u8 resv1; |
|
+ __u16 command_id; |
|
+ __u8 fctype; |
|
+ __u8 resv2[35]; |
|
+ __u8 ts[24]; |
|
+}; |
|
+ |
|
+/* |
|
+ * The legal cntlid range a NVMe Target will provide. |
|
+ * Note that cntlid of value 0 is considered illegal in the fabrics world. |
|
+ * Devices based on earlier specs did not have the subsystem concept; |
|
+ * therefore, those devices had their cntlid value set to 0 as a result. |
|
+ */ |
|
+#define NVME_CNTLID_MIN 1 |
|
+#define NVME_CNTLID_MAX 0xffef |
|
+#define NVME_CNTLID_DYNAMIC 0xffff |
|
+ |
|
+#define MAX_DISC_LOGS 255 |
|
+ |
|
+/* Discovery log page entry */ |
|
+struct nvmf_disc_rsp_page_entry { |
|
+ __u8 trtype; |
|
+ __u8 adrfam; |
|
+ __u8 subtype; |
|
+ __u8 treq; |
|
+ __le16 portid; |
|
+ __le16 cntlid; |
|
+ __le16 asqsz; |
|
+ __u8 resv8[22]; |
|
+ char trsvcid[NVMF_TRSVCID_SIZE]; |
|
+ __u8 resv64[192]; |
|
+ char subnqn[NVMF_NQN_FIELD_LEN]; |
|
+ char traddr[NVMF_TRADDR_SIZE]; |
|
+ union tsas { |
|
+ char common[NVMF_TSAS_SIZE]; |
|
+ struct rdma { |
|
+ __u8 qptype; |
|
+ __u8 prtype; |
|
+ __u8 cms; |
|
+ __u8 resv3[5]; |
|
+ __u16 pkey; |
|
+ __u8 resv10[246]; |
|
+ } rdma; |
|
+ struct tcp { |
|
+ __u8 sectype; |
|
+ } tcp; |
|
+ } tsas; |
|
+}; |
|
+ |
|
+/* Discovery log page header */ |
|
+struct nvmf_disc_rsp_page_hdr { |
|
+ __le64 genctr; |
|
+ __le64 numrec; |
|
+ __le16 recfmt; |
|
+ __u8 resv14[1006]; |
|
+ struct nvmf_disc_rsp_page_entry entries[0]; |
|
+}; |
|
+ |
|
+struct nvmf_connect_command { |
|
+ __u8 opcode; |
|
+ __u8 resv1; |
|
+ __u16 command_id; |
|
+ __u8 fctype; |
|
+ __u8 resv2[19]; |
|
+ union nvme_data_ptr dptr; |
|
+ __le16 recfmt; |
|
+ __le16 qid; |
|
+ __le16 sqsize; |
|
+ __u8 cattr; |
|
+ __u8 resv3; |
|
+ __le32 kato; |
|
+ __u8 resv4[12]; |
|
+}; |
|
+ |
|
+struct nvmf_connect_data { |
|
+ uuid_t hostid; |
|
+ __le16 cntlid; |
|
+ char resv4[238]; |
|
+ char subsysnqn[NVMF_NQN_FIELD_LEN]; |
|
+ char hostnqn[NVMF_NQN_FIELD_LEN]; |
|
+ char resv5[256]; |
|
+}; |
|
+ |
|
+struct nvmf_property_set_command { |
|
+ __u8 opcode; |
|
+ __u8 resv1; |
|
+ __u16 command_id; |
|
+ __u8 fctype; |
|
+ __u8 resv2[35]; |
|
+ __u8 attrib; |
|
+ __u8 resv3[3]; |
|
+ __le32 offset; |
|
+ __le64 value; |
|
+ __u8 resv4[8]; |
|
+}; |
|
+ |
|
+struct nvmf_property_get_command { |
|
+ __u8 opcode; |
|
+ __u8 resv1; |
|
+ __u16 command_id; |
|
+ __u8 fctype; |
|
+ __u8 resv2[35]; |
|
+ __u8 attrib; |
|
+ __u8 resv3[3]; |
|
+ __le32 offset; |
|
+ __u8 resv4[16]; |
|
+}; |
|
+ |
|
+struct nvme_dbbuf { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 command_id; |
|
+ __u32 rsvd1[5]; |
|
+ __le64 prp1; |
|
+ __le64 prp2; |
|
+ __u32 rsvd12[6]; |
|
+}; |
|
+ |
|
+struct streams_directive_params { |
|
+ __le16 msl; |
|
+ __le16 nssa; |
|
+ __le16 nsso; |
|
+ __u8 rsvd[10]; |
|
+ __le32 sws; |
|
+ __le16 sgs; |
|
+ __le16 nsa; |
|
+ __le16 nso; |
|
+ __u8 rsvd2[6]; |
|
+}; |
|
+ |
|
+struct nvme_command { |
|
+ union { |
|
+ struct nvme_common_command common; |
|
+ struct nvme_rw_command rw; |
|
+ struct nvme_identify identify; |
|
+ struct nvme_features features; |
|
+ struct nvme_create_cq create_cq; |
|
+ struct nvme_create_sq create_sq; |
|
+ struct nvme_delete_queue delete_queue; |
|
+ struct nvme_download_firmware dlfw; |
|
+ struct nvme_format_cmd format; |
|
+ struct nvme_dsm_cmd dsm; |
|
+ struct nvme_write_zeroes_cmd write_zeroes; |
|
+ struct nvme_abort_cmd abort; |
|
+ struct nvme_get_log_page_command get_log_page; |
|
+ struct nvmf_common_command fabrics; |
|
+ struct nvmf_connect_command connect; |
|
+ struct nvmf_property_set_command prop_set; |
|
+ struct nvmf_property_get_command prop_get; |
|
+ struct nvme_dbbuf dbbuf; |
|
+ struct nvme_directive_cmd directive; |
|
+ }; |
|
+}; |
|
+ |
|
+static inline bool nvme_is_write(struct nvme_command *cmd) |
|
+{ |
|
+ /* |
|
+ * What a mess... |
|
+ * |
|
+ * Why can't we simply have a Fabrics In and Fabrics out command? |
|
+ */ |
|
+ if (unlikely(cmd->common.opcode == nvme_fabrics_command)) |
|
+ return cmd->fabrics.fctype & 1; |
|
+ return cmd->common.opcode & 1; |
|
+} |
|
+ |
|
+enum { |
|
+ /* |
|
+ * Generic Command Status: |
|
+ */ |
|
+ NVME_SC_SUCCESS = 0x0, |
|
+ NVME_SC_INVALID_OPCODE = 0x1, |
|
+ NVME_SC_INVALID_FIELD = 0x2, |
|
+ NVME_SC_CMDID_CONFLICT = 0x3, |
|
+ NVME_SC_DATA_XFER_ERROR = 0x4, |
|
+ NVME_SC_POWER_LOSS = 0x5, |
|
+ NVME_SC_INTERNAL = 0x6, |
|
+ NVME_SC_ABORT_REQ = 0x7, |
|
+ NVME_SC_ABORT_QUEUE = 0x8, |
|
+ NVME_SC_FUSED_FAIL = 0x9, |
|
+ NVME_SC_FUSED_MISSING = 0xa, |
|
+ NVME_SC_INVALID_NS = 0xb, |
|
+ NVME_SC_CMD_SEQ_ERROR = 0xc, |
|
+ NVME_SC_SGL_INVALID_LAST = 0xd, |
|
+ NVME_SC_SGL_INVALID_COUNT = 0xe, |
|
+ NVME_SC_SGL_INVALID_DATA = 0xf, |
|
+ NVME_SC_SGL_INVALID_METADATA = 0x10, |
|
+ NVME_SC_SGL_INVALID_TYPE = 0x11, |
|
+ |
|
+ NVME_SC_SGL_INVALID_OFFSET = 0x16, |
|
+ NVME_SC_SGL_INVALID_SUBTYPE = 0x17, |
|
+ |
|
+ NVME_SC_SANITIZE_FAILED = 0x1C, |
|
+ NVME_SC_SANITIZE_IN_PROGRESS = 0x1D, |
|
+ |
|
+ NVME_SC_NS_WRITE_PROTECTED = 0x20, |
|
+ |
|
+ NVME_SC_LBA_RANGE = 0x80, |
|
+ NVME_SC_CAP_EXCEEDED = 0x81, |
|
+ NVME_SC_NS_NOT_READY = 0x82, |
|
+ NVME_SC_RESERVATION_CONFLICT = 0x83, |
|
+ |
|
+ /* |
|
+ * Command Specific Status: |
|
+ */ |
|
+ NVME_SC_CQ_INVALID = 0x100, |
|
+ NVME_SC_QID_INVALID = 0x101, |
|
+ NVME_SC_QUEUE_SIZE = 0x102, |
|
+ NVME_SC_ABORT_LIMIT = 0x103, |
|
+ NVME_SC_ABORT_MISSING = 0x104, |
|
+ NVME_SC_ASYNC_LIMIT = 0x105, |
|
+ NVME_SC_FIRMWARE_SLOT = 0x106, |
|
+ NVME_SC_FIRMWARE_IMAGE = 0x107, |
|
+ NVME_SC_INVALID_VECTOR = 0x108, |
|
+ NVME_SC_INVALID_LOG_PAGE = 0x109, |
|
+ NVME_SC_INVALID_FORMAT = 0x10a, |
|
+ NVME_SC_FW_NEEDS_CONV_RESET = 0x10b, |
|
+ NVME_SC_INVALID_QUEUE = 0x10c, |
|
+ NVME_SC_FEATURE_NOT_SAVEABLE = 0x10d, |
|
+ NVME_SC_FEATURE_NOT_CHANGEABLE = 0x10e, |
|
+ NVME_SC_FEATURE_NOT_PER_NS = 0x10f, |
|
+ NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x110, |
|
+ NVME_SC_FW_NEEDS_RESET = 0x111, |
|
+ NVME_SC_FW_NEEDS_MAX_TIME = 0x112, |
|
+ NVME_SC_FW_ACIVATE_PROHIBITED = 0x113, |
|
+ NVME_SC_OVERLAPPING_RANGE = 0x114, |
|
+ NVME_SC_NS_INSUFFICENT_CAP = 0x115, |
|
+ NVME_SC_NS_ID_UNAVAILABLE = 0x116, |
|
+ NVME_SC_NS_ALREADY_ATTACHED = 0x118, |
|
+ NVME_SC_NS_IS_PRIVATE = 0x119, |
|
+ NVME_SC_NS_NOT_ATTACHED = 0x11a, |
|
+ NVME_SC_THIN_PROV_NOT_SUPP = 0x11b, |
|
+ NVME_SC_CTRL_LIST_INVALID = 0x11c, |
|
+ NVME_SC_BP_WRITE_PROHIBITED = 0x11e, |
|
+ |
|
+ /* |
|
+ * I/O Command Set Specific - NVM commands: |
|
+ */ |
|
+ NVME_SC_BAD_ATTRIBUTES = 0x180, |
|
+ NVME_SC_INVALID_PI = 0x181, |
|
+ NVME_SC_READ_ONLY = 0x182, |
|
+ NVME_SC_ONCS_NOT_SUPPORTED = 0x183, |
|
+ |
|
+ /* |
|
+ * I/O Command Set Specific - Fabrics commands: |
|
+ */ |
|
+ NVME_SC_CONNECT_FORMAT = 0x180, |
|
+ NVME_SC_CONNECT_CTRL_BUSY = 0x181, |
|
+ NVME_SC_CONNECT_INVALID_PARAM = 0x182, |
|
+ NVME_SC_CONNECT_RESTART_DISC = 0x183, |
|
+ NVME_SC_CONNECT_INVALID_HOST = 0x184, |
|
+ |
|
+ NVME_SC_DISCOVERY_RESTART = 0x190, |
|
+ NVME_SC_AUTH_REQUIRED = 0x191, |
|
+ |
|
+ /* |
|
+ * Media and Data Integrity Errors: |
|
+ */ |
|
+ NVME_SC_WRITE_FAULT = 0x280, |
|
+ NVME_SC_READ_ERROR = 0x281, |
|
+ NVME_SC_GUARD_CHECK = 0x282, |
|
+ NVME_SC_APPTAG_CHECK = 0x283, |
|
+ NVME_SC_REFTAG_CHECK = 0x284, |
|
+ NVME_SC_COMPARE_FAILED = 0x285, |
|
+ NVME_SC_ACCESS_DENIED = 0x286, |
|
+ NVME_SC_UNWRITTEN_BLOCK = 0x287, |
|
+ |
|
+ /* |
|
+ * Path-related Errors: |
|
+ */ |
|
+ NVME_SC_ANA_PERSISTENT_LOSS = 0x301, |
|
+ NVME_SC_ANA_INACCESSIBLE = 0x302, |
|
+ NVME_SC_ANA_TRANSITION = 0x303, |
|
+ |
|
+ NVME_SC_DNR = 0x4000, |
|
+}; |
|
+ |
|
+struct nvme_completion { |
|
+ /* |
|
+ * Used by Admin and Fabrics commands to return data: |
|
+ */ |
|
+ union nvme_result { |
|
+ __le16 u16; |
|
+ __le32 u32; |
|
+ __le64 u64; |
|
+ } result; |
|
+ __le16 sq_head; /* how much of this queue may be reclaimed */ |
|
+ __le16 sq_id; /* submission queue that generated this entry */ |
|
+ __u16 command_id; /* of the command which completed */ |
|
+ __le16 status; /* did the command fail, and if so, why? */ |
|
+}; |
|
+ |
|
+#define NVME_VS(major, minor, tertiary) \ |
|
+ (((major) << 16) | ((minor) << 8) | (tertiary)) |
|
+ |
|
+#define NVME_MAJOR(ver) ((ver) >> 16) |
|
+#define NVME_MINOR(ver) (((ver) >> 8) & 0xff) |
|
+#define NVME_TERTIARY(ver) ((ver) & 0xff) |
|
+ |
|
+#endif /* _LINUX_NVME_H */ |
|
Index: multipath-tools-130222/libmultipath/nvme/linux/nvme_ioctl.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme/linux/nvme_ioctl.h |
|
@@ -0,0 +1,67 @@ |
|
+/* |
|
+ * Definitions for the NVM Express ioctl interface |
|
+ * Copyright (c) 2011-2014, Intel Corporation. |
|
+ * |
|
+ * This program is free software; you can redistribute it and/or modify it |
|
+ * under the terms and conditions of the GNU General Public License, |
|
+ * version 2, as published by the Free Software Foundation. |
|
+ * |
|
+ * This program is distributed in the hope it will be useful, but WITHOUT |
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|
+ * more details. |
|
+ */ |
|
+ |
|
+#ifndef _UAPI_LINUX_NVME_IOCTL_H |
|
+#define _UAPI_LINUX_NVME_IOCTL_H |
|
+ |
|
+#include <linux/types.h> |
|
+#include <sys/ioctl.h> |
|
+ |
|
+struct nvme_user_io { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 control; |
|
+ __u16 nblocks; |
|
+ __u16 rsvd; |
|
+ __u64 metadata; |
|
+ __u64 addr; |
|
+ __u64 slba; |
|
+ __u32 dsmgmt; |
|
+ __u32 reftag; |
|
+ __u16 apptag; |
|
+ __u16 appmask; |
|
+}; |
|
+ |
|
+struct nvme_passthru_cmd { |
|
+ __u8 opcode; |
|
+ __u8 flags; |
|
+ __u16 rsvd1; |
|
+ __u32 nsid; |
|
+ __u32 cdw2; |
|
+ __u32 cdw3; |
|
+ __u64 metadata; |
|
+ __u64 addr; |
|
+ __u32 metadata_len; |
|
+ __u32 data_len; |
|
+ __u32 cdw10; |
|
+ __u32 cdw11; |
|
+ __u32 cdw12; |
|
+ __u32 cdw13; |
|
+ __u32 cdw14; |
|
+ __u32 cdw15; |
|
+ __u32 timeout_ms; |
|
+ __u32 result; |
|
+}; |
|
+ |
|
+#define nvme_admin_cmd nvme_passthru_cmd |
|
+ |
|
+#define NVME_IOCTL_ID _IO('N', 0x40) |
|
+#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd) |
|
+#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io) |
|
+#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd) |
|
+#define NVME_IOCTL_RESET _IO('N', 0x44) |
|
+#define NVME_IOCTL_SUBSYS_RESET _IO('N', 0x45) |
|
+#define NVME_IOCTL_RESCAN _IO('N', 0x46) |
|
+ |
|
+#endif /* _UAPI_LINUX_NVME_IOCTL_H */ |
|
Index: multipath-tools-130222/Makefile.inc |
|
=================================================================== |
|
--- multipath-tools-130222.orig/Makefile.inc |
|
+++ multipath-tools-130222/Makefile.inc |
|
@@ -37,6 +37,7 @@ mpathpersistdir = $(TOPDIR)/libmpathpers |
|
includedir = $(prefix)/usr/include |
|
mpathcmddir = $(TOPDIR)/libmpathcmd |
|
libdmmpdir = $(TOPDIR)/libdmmp |
|
+nvmedir = $(TOPDIR)/libmultipath/nvme |
|
pkgconfdir = $(prefix)/usr/$(LIB)/pkgconfig |
|
|
|
GZIP = /bin/gzip -9 -c |
|
Index: multipath-tools-130222/libmultipath/Makefile |
|
=================================================================== |
|
--- multipath-tools-130222.orig/libmultipath/Makefile |
|
+++ multipath-tools-130222/libmultipath/Makefile |
|
@@ -8,7 +8,7 @@ SONAME=0 |
|
DEVLIB = libmultipath.so |
|
LIBS = $(DEVLIB).$(SONAME) |
|
LIBDEPS = -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -laio |
|
-CFLAGS += -fPIC -I$(mpathcmddir) -I$(mpathpersistdir) |
|
+CFLAGS += -fPIC -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir) |
|
|
|
OBJS = memory.o parser.o vector.o devmapper.o \ |
|
hwtable.o blacklist.o util.o dmparser.o config.o \ |
|
@@ -17,7 +17,7 @@ OBJS = memory.o parser.o vector.o devmap |
|
switchgroup.o uxsock.o print.o alias.o log_pthread.o \ |
|
log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \ |
|
lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \ |
|
- io_err_stat.o |
|
+ io_err_stat.o nvme-lib.o |
|
|
|
LIBDM_API_FLUSH = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_no_flush' /usr/include/libdevmapper.h) |
|
|
|
@@ -46,6 +46,9 @@ endif |
|
|
|
all: $(LIBS) |
|
|
|
+nvme-lib.o: nvme-lib.c nvme-ioctl.c nvme-ioctl.h |
|
+ $(CC) $(CFLAGS) -Wno-unused-function -c -o $@ $< |
|
+ |
|
$(LIBS): $(OBJS) |
|
$(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS) |
|
ln -sf $@ $(DEVLIB) |
|
Index: multipath-tools-130222/libmultipath/nvme-ioctl.c |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme-ioctl.c |
|
@@ -0,0 +1,869 @@ |
|
+#include <sys/ioctl.h> |
|
+#include <sys/stat.h> |
|
+#include <string.h> |
|
+#include <errno.h> |
|
+#include <unistd.h> |
|
+ |
|
+#include <errno.h> |
|
+#include <getopt.h> |
|
+#include <fcntl.h> |
|
+#include <inttypes.h> |
|
+#include <locale.h> |
|
+#include <stdio.h> |
|
+#include <stdlib.h> |
|
+#include <string.h> |
|
+#include <unistd.h> |
|
+#include <math.h> |
|
+ |
|
+#include "nvme-ioctl.h" |
|
+ |
|
+static int nvme_verify_chr(int fd) |
|
+{ |
|
+ static struct stat nvme_stat; |
|
+ int err = fstat(fd, &nvme_stat); |
|
+ |
|
+ if (err < 0) { |
|
+ perror("fstat"); |
|
+ return errno; |
|
+ } |
|
+ if (!S_ISCHR(nvme_stat.st_mode)) { |
|
+ fprintf(stderr, |
|
+ "Error: requesting reset on non-controller handle\n"); |
|
+ return ENOTBLK; |
|
+ } |
|
+ return 0; |
|
+} |
|
+ |
|
+static int nvme_subsystem_reset(int fd) |
|
+{ |
|
+ int ret; |
|
+ |
|
+ ret = nvme_verify_chr(fd); |
|
+ if (ret) |
|
+ return ret; |
|
+ return ioctl(fd, NVME_IOCTL_SUBSYS_RESET); |
|
+} |
|
+ |
|
+static int nvme_reset_controller(int fd) |
|
+{ |
|
+ int ret; |
|
+ |
|
+ ret = nvme_verify_chr(fd); |
|
+ if (ret) |
|
+ return ret; |
|
+ return ioctl(fd, NVME_IOCTL_RESET); |
|
+} |
|
+ |
|
+static int nvme_ns_rescan(int fd) |
|
+{ |
|
+ int ret; |
|
+ |
|
+ ret = nvme_verify_chr(fd); |
|
+ if (ret) |
|
+ return ret; |
|
+ return ioctl(fd, NVME_IOCTL_RESCAN); |
|
+} |
|
+ |
|
+static int nvme_get_nsid(int fd) |
|
+{ |
|
+ static struct stat nvme_stat; |
|
+ int err = fstat(fd, &nvme_stat); |
|
+ |
|
+ if (err < 0) |
|
+ return -errno; |
|
+ |
|
+ if (!S_ISBLK(nvme_stat.st_mode)) { |
|
+ fprintf(stderr, |
|
+ "Error: requesting namespace-id from non-block device\n"); |
|
+ errno = ENOTBLK; |
|
+ return -errno; |
|
+ } |
|
+ return ioctl(fd, NVME_IOCTL_ID); |
|
+} |
|
+ |
|
+static int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, |
|
+ struct nvme_passthru_cmd *cmd) |
|
+{ |
|
+ return ioctl(fd, ioctl_cmd, cmd); |
|
+} |
|
+ |
|
+static int nvme_submit_admin_passthru(int fd, struct nvme_passthru_cmd *cmd) |
|
+{ |
|
+ return ioctl(fd, NVME_IOCTL_ADMIN_CMD, cmd); |
|
+} |
|
+ |
|
+static int nvme_submit_io_passthru(int fd, struct nvme_passthru_cmd *cmd) |
|
+{ |
|
+ return ioctl(fd, NVME_IOCTL_IO_CMD, cmd); |
|
+} |
|
+ |
|
+static int nvme_passthru(int fd, unsigned long ioctl_cmd, __u8 opcode, |
|
+ __u8 flags, __u16 rsvd, |
|
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, __u32 cdw11, |
|
+ __u32 cdw12, __u32 cdw13, __u32 cdw14, __u32 cdw15, |
|
+ __u32 data_len, void *data, __u32 metadata_len, |
|
+ void *metadata, __u32 timeout_ms, __u32 *result) |
|
+{ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = opcode, |
|
+ .flags = flags, |
|
+ .rsvd1 = rsvd, |
|
+ .nsid = nsid, |
|
+ .cdw2 = cdw2, |
|
+ .cdw3 = cdw3, |
|
+ .metadata = (__u64)(uintptr_t) metadata, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .metadata_len = metadata_len, |
|
+ .data_len = data_len, |
|
+ .cdw10 = cdw10, |
|
+ .cdw11 = cdw11, |
|
+ .cdw12 = cdw12, |
|
+ .cdw13 = cdw13, |
|
+ .cdw14 = cdw14, |
|
+ .cdw15 = cdw15, |
|
+ .timeout_ms = timeout_ms, |
|
+ .result = 0, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_passthru(fd, ioctl_cmd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_io(int fd, __u8 opcode, __u64 slba, __u16 nblocks, __u16 control, |
|
+ __u32 dsmgmt, __u32 reftag, __u16 apptag, __u16 appmask, void *data, |
|
+ void *metadata) |
|
+{ |
|
+ struct nvme_user_io io = { |
|
+ .opcode = opcode, |
|
+ .flags = 0, |
|
+ .control = control, |
|
+ .nblocks = nblocks, |
|
+ .rsvd = 0, |
|
+ .metadata = (__u64)(uintptr_t) metadata, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .slba = slba, |
|
+ .dsmgmt = dsmgmt, |
|
+ .reftag = reftag, |
|
+ .appmask = appmask, |
|
+ .apptag = apptag, |
|
+ }; |
|
+ return ioctl(fd, NVME_IOCTL_SUBMIT_IO, &io); |
|
+} |
|
+ |
|
+static int nvme_read(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, |
|
+ __u32 reftag, __u16 apptag, __u16 appmask, void *data, |
|
+ void *metadata) |
|
+{ |
|
+ return nvme_io(fd, nvme_cmd_read, slba, nblocks, control, dsmgmt, |
|
+ reftag, apptag, appmask, data, metadata); |
|
+} |
|
+ |
|
+static int nvme_write(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, |
|
+ __u32 reftag, __u16 apptag, __u16 appmask, void *data, |
|
+ void *metadata) |
|
+{ |
|
+ return nvme_io(fd, nvme_cmd_write, slba, nblocks, control, dsmgmt, |
|
+ reftag, apptag, appmask, data, metadata); |
|
+} |
|
+ |
|
+static int nvme_compare(int fd, __u64 slba, __u16 nblocks, __u16 control, __u32 dsmgmt, |
|
+ __u32 reftag, __u16 apptag, __u16 appmask, void *data, |
|
+ void *metadata) |
|
+{ |
|
+ return nvme_io(fd, nvme_cmd_compare, slba, nblocks, control, dsmgmt, |
|
+ reftag, apptag, appmask, data, metadata); |
|
+} |
|
+ |
|
+static int nvme_passthru_io(int fd, __u8 opcode, __u8 flags, __u16 rsvd, |
|
+ __u32 nsid, __u32 cdw2, __u32 cdw3, __u32 cdw10, |
|
+ __u32 cdw11, __u32 cdw12, __u32 cdw13, __u32 cdw14, |
|
+ __u32 cdw15, __u32 data_len, void *data, |
|
+ __u32 metadata_len, void *metadata, __u32 timeout_ms) |
|
+{ |
|
+ return nvme_passthru(fd, NVME_IOCTL_IO_CMD, opcode, flags, rsvd, nsid, |
|
+ cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, cdw14, |
|
+ cdw15, data_len, data, metadata_len, metadata, |
|
+ timeout_ms, NULL); |
|
+} |
|
+ |
|
+static int nvme_write_zeros(int fd, __u32 nsid, __u64 slba, __u16 nlb, |
|
+ __u16 control, __u32 reftag, __u16 apptag, __u16 appmask) |
|
+{ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_write_zeroes, |
|
+ .nsid = nsid, |
|
+ .cdw10 = slba & 0xffffffff, |
|
+ .cdw11 = slba >> 32, |
|
+ .cdw12 = nlb | (control << 16), |
|
+ .cdw14 = reftag, |
|
+ .cdw15 = apptag | (appmask << 16), |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_write_uncorrectable(int fd, __u32 nsid, __u64 slba, __u16 nlb) |
|
+{ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_write_uncor, |
|
+ .nsid = nsid, |
|
+ .cdw10 = slba & 0xffffffff, |
|
+ .cdw11 = slba >> 32, |
|
+ .cdw12 = nlb, |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_flush(int fd, __u32 nsid) |
|
+{ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_flush, |
|
+ .nsid = nsid, |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_dsm(int fd, __u32 nsid, __u32 cdw11, struct nvme_dsm_range *dsm, |
|
+ __u16 nr_ranges) |
|
+{ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_dsm, |
|
+ .nsid = nsid, |
|
+ .addr = (__u64)(uintptr_t) dsm, |
|
+ .data_len = nr_ranges * sizeof(*dsm), |
|
+ .cdw10 = nr_ranges - 1, |
|
+ .cdw11 = cdw11, |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static struct nvme_dsm_range *nvme_setup_dsm_range(__u32 *ctx_attrs, __u32 *llbas, |
|
+ __u64 *slbas, __u16 nr_ranges) |
|
+{ |
|
+ int i; |
|
+ struct nvme_dsm_range *dsm = malloc(nr_ranges * sizeof(*dsm)); |
|
+ |
|
+ if (!dsm) { |
|
+ fprintf(stderr, "malloc: %s\n", strerror(errno)); |
|
+ return NULL; |
|
+ } |
|
+ for (i = 0; i < nr_ranges; i++) { |
|
+ dsm[i].cattr = cpu_to_le32(ctx_attrs[i]); |
|
+ dsm[i].nlb = cpu_to_le32(llbas[i]); |
|
+ dsm[i].slba = cpu_to_le64(slbas[i]); |
|
+ } |
|
+ return dsm; |
|
+} |
|
+ |
|
+static int nvme_resv_acquire(int fd, __u32 nsid, __u8 rtype, __u8 racqa, |
|
+ bool iekey, __u64 crkey, __u64 nrkey) |
|
+{ |
|
+ __le64 payload[2] = { cpu_to_le64(crkey), cpu_to_le64(nrkey) }; |
|
+ __u32 cdw10 = (racqa & 0x7) | (iekey ? 1 << 3 : 0) | rtype << 8; |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_resv_acquire, |
|
+ .nsid = nsid, |
|
+ .cdw10 = cdw10, |
|
+ .addr = (__u64)(uintptr_t) (payload), |
|
+ .data_len = sizeof(payload), |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_resv_register(int fd, __u32 nsid, __u8 rrega, __u8 cptpl, |
|
+ bool iekey, __u64 crkey, __u64 nrkey) |
|
+{ |
|
+ __le64 payload[2] = { cpu_to_le64(crkey), cpu_to_le64(nrkey) }; |
|
+ __u32 cdw10 = (rrega & 0x7) | (iekey ? 1 << 3 : 0) | cptpl << 30; |
|
+ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_resv_register, |
|
+ .nsid = nsid, |
|
+ .cdw10 = cdw10, |
|
+ .addr = (__u64)(uintptr_t) (payload), |
|
+ .data_len = sizeof(payload), |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_resv_release(int fd, __u32 nsid, __u8 rtype, __u8 rrela, |
|
+ bool iekey, __u64 crkey) |
|
+{ |
|
+ __le64 payload[1] = { cpu_to_le64(crkey) }; |
|
+ __u32 cdw10 = (rrela & 0x7) | (iekey ? 1 << 3 : 0) | rtype << 8; |
|
+ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_resv_release, |
|
+ .nsid = nsid, |
|
+ .cdw10 = cdw10, |
|
+ .addr = (__u64)(uintptr_t) (payload), |
|
+ .data_len = sizeof(payload), |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_resv_report(int fd, __u32 nsid, __u32 numd, __u32 cdw11, void *data) |
|
+{ |
|
+ struct nvme_passthru_cmd cmd = { |
|
+ .opcode = nvme_cmd_resv_report, |
|
+ .nsid = nsid, |
|
+ .cdw10 = numd, |
|
+ .cdw11 = cdw11, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = (numd + 1) << 2, |
|
+ }; |
|
+ |
|
+ return nvme_submit_io_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_identify13(int fd, __u32 nsid, __u32 cdw10, __u32 cdw11, void *data) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_identify, |
|
+ .nsid = nsid, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = NVME_IDENTIFY_DATA_SIZE, |
|
+ .cdw10 = cdw10, |
|
+ .cdw11 = cdw11, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_identify(int fd, __u32 nsid, __u32 cdw10, void *data) |
|
+{ |
|
+ return nvme_identify13(fd, nsid, cdw10, 0, data); |
|
+} |
|
+ |
|
+static int nvme_identify_ctrl(int fd, void *data) |
|
+{ |
|
+ return nvme_identify(fd, 0, 1, data); |
|
+} |
|
+ |
|
+static int nvme_identify_ns(int fd, __u32 nsid, bool present, void *data) |
|
+{ |
|
+ int cns = present ? NVME_ID_CNS_NS_PRESENT : NVME_ID_CNS_NS; |
|
+ |
|
+ return nvme_identify(fd, nsid, cns, data); |
|
+} |
|
+ |
|
+static int nvme_identify_ns_list(int fd, __u32 nsid, bool all, void *data) |
|
+{ |
|
+ int cns = all ? NVME_ID_CNS_NS_PRESENT_LIST : NVME_ID_CNS_NS_ACTIVE_LIST; |
|
+ |
|
+ return nvme_identify(fd, nsid, cns, data); |
|
+} |
|
+ |
|
+static int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data) |
|
+{ |
|
+ int cns = nsid ? NVME_ID_CNS_CTRL_NS_LIST : NVME_ID_CNS_CTRL_LIST; |
|
+ |
|
+ return nvme_identify(fd, nsid, (cntid << 16) | cns, data); |
|
+} |
|
+ |
|
+static int nvme_identify_ns_descs(int fd, __u32 nsid, void *data) |
|
+{ |
|
+ |
|
+ return nvme_identify(fd, nsid, NVME_ID_CNS_NS_DESC_LIST, data); |
|
+} |
|
+ |
|
+static int nvme_identify_nvmset(int fd, __u16 nvmset_id, void *data) |
|
+{ |
|
+ return nvme_identify13(fd, 0, NVME_ID_CNS_NVMSET_LIST, nvmset_id, data); |
|
+} |
|
+ |
|
+static int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo, |
|
+ __u16 lsi, bool rae, __u32 data_len, void *data) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_get_log_page, |
|
+ .nsid = nsid, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ }; |
|
+ __u32 numd = (data_len >> 2) - 1; |
|
+ __u16 numdu = numd >> 16, numdl = numd & 0xffff; |
|
+ |
|
+ cmd.cdw10 = log_id | (numdl << 16) | (rae ? 1 << 15 : 0); |
|
+ if (lsp) |
|
+ cmd.cdw10 |= lsp << 8; |
|
+ |
|
+ cmd.cdw11 = numdu | (lsi << 16); |
|
+ cmd.cdw12 = lpo; |
|
+ cmd.cdw13 = (lpo >> 32); |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+ |
|
+} |
|
+ |
|
+static int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae, |
|
+ __u32 data_len, void *data) |
|
+{ |
|
+ void *ptr = data; |
|
+ __u32 offset = 0, xfer_len = data_len; |
|
+ int ret; |
|
+ |
|
+ /* |
|
+ * 4k is the smallest possible transfer unit, so by |
|
+ * restricting ourselves for 4k transfers we avoid having |
|
+ * to check the MDTS value of the controller. |
|
+ */ |
|
+ do { |
|
+ xfer_len = data_len - offset; |
|
+ if (xfer_len > 4096) |
|
+ xfer_len = 4096; |
|
+ |
|
+ ret = nvme_get_log13(fd, nsid, log_id, NVME_NO_LOG_LSP, |
|
+ offset, 0, rae, xfer_len, ptr); |
|
+ if (ret) |
|
+ return ret; |
|
+ |
|
+ offset += xfer_len; |
|
+ ptr += xfer_len; |
|
+ } while (offset < data_len); |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+static int nvme_get_telemetry_log(int fd, void *lp, int generate_report, |
|
+ int ctrl_init, size_t log_page_size, __u64 offset) |
|
+{ |
|
+ if (ctrl_init) |
|
+ return nvme_get_log13(fd, NVME_NSID_ALL, NVME_LOG_TELEMETRY_CTRL, |
|
+ NVME_NO_LOG_LSP, offset, |
|
+ 0, 1, log_page_size, lp); |
|
+ if (generate_report) |
|
+ return nvme_get_log13(fd, NVME_NSID_ALL, NVME_LOG_TELEMETRY_HOST, |
|
+ NVME_TELEM_LSP_CREATE, offset, |
|
+ 0, 1, log_page_size, lp); |
|
+ else |
|
+ return nvme_get_log13(fd, NVME_NSID_ALL, NVME_LOG_TELEMETRY_HOST, |
|
+ NVME_NO_LOG_LSP, offset, |
|
+ 0, 1, log_page_size, lp); |
|
+} |
|
+ |
|
+static int nvme_fw_log(int fd, struct nvme_firmware_log_page *fw_log) |
|
+{ |
|
+ return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_FW_SLOT, true, |
|
+ sizeof(*fw_log), fw_log); |
|
+} |
|
+ |
|
+static int nvme_changed_ns_list_log(int fd, struct nvme_changed_ns_list_log *changed_ns_list_log) |
|
+{ |
|
+ return nvme_get_log(fd, 0, NVME_LOG_CHANGED_NS, true, |
|
+ sizeof(changed_ns_list_log->log), |
|
+ changed_ns_list_log->log); |
|
+} |
|
+ |
|
+static int nvme_error_log(int fd, int entries, struct nvme_error_log_page *err_log) |
|
+{ |
|
+ return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_ERROR, false, |
|
+ entries * sizeof(*err_log), err_log); |
|
+} |
|
+ |
|
+static int nvme_endurance_log(int fd, __u16 group_id, struct nvme_endurance_group_log *endurance_log) |
|
+{ |
|
+ return nvme_get_log13(fd, 0, NVME_LOG_ENDURANCE_GROUP, 0, 0, group_id, 0, |
|
+ sizeof(*endurance_log), endurance_log); |
|
+} |
|
+ |
|
+static int nvme_smart_log(int fd, __u32 nsid, struct nvme_smart_log *smart_log) |
|
+{ |
|
+ return nvme_get_log(fd, nsid, NVME_LOG_SMART, false, |
|
+ sizeof(*smart_log), smart_log); |
|
+} |
|
+ |
|
+static int nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo) |
|
+{ |
|
+ __u64 lpo = 0; |
|
+ |
|
+ return nvme_get_log13(fd, NVME_NSID_ALL, NVME_LOG_ANA, rgo, lpo, 0, |
|
+ true, ana_log_len, ana_log); |
|
+} |
|
+ |
|
+static int nvme_self_test_log(int fd, struct nvme_self_test_log *self_test_log) |
|
+{ |
|
+ return nvme_get_log(fd, NVME_NSID_ALL, NVME_LOG_DEVICE_SELF_TEST, false, |
|
+ sizeof(*self_test_log), self_test_log); |
|
+} |
|
+ |
|
+static int nvme_effects_log(int fd, struct nvme_effects_log_page *effects_log) |
|
+{ |
|
+ return nvme_get_log(fd, 0, NVME_LOG_CMD_EFFECTS, false, |
|
+ sizeof(*effects_log), effects_log); |
|
+} |
|
+ |
|
+static int nvme_discovery_log(int fd, struct nvmf_disc_rsp_page_hdr *log, __u32 size) |
|
+{ |
|
+ return nvme_get_log(fd, 0, NVME_LOG_DISC, false, size, log); |
|
+} |
|
+ |
|
+static int nvme_sanitize_log(int fd, struct nvme_sanitize_log_page *sanitize_log) |
|
+{ |
|
+ return nvme_get_log(fd, 0, NVME_LOG_SANITIZE, false, |
|
+ sizeof(*sanitize_log), sanitize_log); |
|
+} |
|
+ |
|
+static int nvme_feature(int fd, __u8 opcode, __u32 nsid, __u32 cdw10, __u32 cdw11, |
|
+ __u32 cdw12, __u32 data_len, void *data, __u32 *result) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = opcode, |
|
+ .nsid = nsid, |
|
+ .cdw10 = cdw10, |
|
+ .cdw11 = cdw11, |
|
+ .cdw12 = cdw12, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_set_feature(int fd, __u32 nsid, __u8 fid, __u32 value, __u32 cdw12, |
|
+ bool save, __u32 data_len, void *data, __u32 *result) |
|
+{ |
|
+ __u32 cdw10 = fid | (save ? 0x80000000 : 0); |
|
+ |
|
+ return nvme_feature(fd, nvme_admin_set_features, nsid, cdw10, value, |
|
+ cdw12, data_len, data, result); |
|
+} |
|
+ |
|
+static int nvme_property(int fd, __u8 fctype, __le32 off, __le64 *value, __u8 attrib) |
|
+{ |
|
+ int err; |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_fabrics_command, |
|
+ .cdw10 = attrib, |
|
+ .cdw11 = off, |
|
+ }; |
|
+ |
|
+ if (!value) { |
|
+ errno = EINVAL; |
|
+ return -errno; |
|
+ } |
|
+ |
|
+ if (fctype == nvme_fabrics_type_property_get){ |
|
+ cmd.nsid = nvme_fabrics_type_property_get; |
|
+ } else if(fctype == nvme_fabrics_type_property_set) { |
|
+ cmd.nsid = nvme_fabrics_type_property_set; |
|
+ cmd.cdw12 = *value; |
|
+ } else { |
|
+ errno = EINVAL; |
|
+ return -errno; |
|
+ } |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && fctype == nvme_fabrics_type_property_get) |
|
+ *value = cpu_to_le64(cmd.result); |
|
+ return err; |
|
+} |
|
+ |
|
+static int get_property_helper(int fd, int offset, void *value, int *advance) |
|
+{ |
|
+ __le64 value64; |
|
+ int err = -EINVAL; |
|
+ |
|
+ switch (offset) { |
|
+ case NVME_REG_CAP: |
|
+ case NVME_REG_ASQ: |
|
+ case NVME_REG_ACQ: |
|
+ *advance = 8; |
|
+ break; |
|
+ default: |
|
+ *advance = 4; |
|
+ } |
|
+ |
|
+ if (!value) |
|
+ return err; |
|
+ |
|
+ err = nvme_property(fd, nvme_fabrics_type_property_get, |
|
+ cpu_to_le32(offset), &value64, (*advance == 8)); |
|
+ |
|
+ if (!err) { |
|
+ if (*advance == 8) |
|
+ *((uint64_t *)value) = le64_to_cpu(value64); |
|
+ else |
|
+ *((uint32_t *)value) = le32_to_cpu(value64); |
|
+ } |
|
+ |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_get_property(int fd, int offset, uint64_t *value) |
|
+{ |
|
+ int advance; |
|
+ return get_property_helper(fd, offset, value, &advance); |
|
+} |
|
+ |
|
+static int nvme_get_properties(int fd, void **pbar) |
|
+{ |
|
+ int offset, advance; |
|
+ int err, ret = -EINVAL; |
|
+ int size = getpagesize(); |
|
+ |
|
+ *pbar = malloc(size); |
|
+ if (!*pbar) { |
|
+ fprintf(stderr, "malloc: %s\n", strerror(errno)); |
|
+ return -ENOMEM; |
|
+ } |
|
+ |
|
+ memset(*pbar, 0xff, size); |
|
+ for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ; offset += advance) { |
|
+ err = get_property_helper(fd, offset, *pbar + offset, &advance); |
|
+ if (!err) |
|
+ ret = 0; |
|
+ } |
|
+ |
|
+ return ret; |
|
+} |
|
+ |
|
+static int nvme_set_property(int fd, int offset, int value) |
|
+{ |
|
+ __le64 val = cpu_to_le64(value); |
|
+ __le32 off = cpu_to_le32(offset); |
|
+ bool is64bit; |
|
+ |
|
+ switch (off) { |
|
+ case NVME_REG_CAP: |
|
+ case NVME_REG_ASQ: |
|
+ case NVME_REG_ACQ: |
|
+ is64bit = true; |
|
+ break; |
|
+ default: |
|
+ is64bit = false; |
|
+ } |
|
+ |
|
+ return nvme_property(fd, nvme_fabrics_type_property_set, |
|
+ off, &val, is64bit ? 1: 0); |
|
+} |
|
+ |
|
+static int nvme_get_feature(int fd, __u32 nsid, __u8 fid, __u8 sel, __u32 cdw11, |
|
+ __u32 data_len, void *data, __u32 *result) |
|
+{ |
|
+ __u32 cdw10 = fid | sel << 8; |
|
+ |
|
+ return nvme_feature(fd, nvme_admin_get_features, nsid, cdw10, cdw11, |
|
+ 0, data_len, data, result); |
|
+} |
|
+ |
|
+static int nvme_format(int fd, __u32 nsid, __u8 lbaf, __u8 ses, __u8 pi, |
|
+ __u8 pil, __u8 ms, __u32 timeout) |
|
+{ |
|
+ __u32 cdw10 = lbaf | ms << 4 | pi << 5 | pil << 8 | ses << 9; |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_format_nvm, |
|
+ .nsid = nsid, |
|
+ .cdw10 = cdw10, |
|
+ .timeout_ms = timeout, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas, |
|
+ __u8 dps, __u8 nmic, __u32 *result) |
|
+{ |
|
+ struct nvme_id_ns ns = { |
|
+ .nsze = cpu_to_le64(nsze), |
|
+ .ncap = cpu_to_le64(ncap), |
|
+ .flbas = flbas, |
|
+ .dps = dps, |
|
+ .nmic = nmic, |
|
+ }; |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_ns_mgmt, |
|
+ .addr = (__u64)(uintptr_t) ((void *)&ns), |
|
+ .cdw10 = 0, |
|
+ .data_len = 0x1000, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_ns_delete(int fd, __u32 nsid) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_ns_mgmt, |
|
+ .nsid = nsid, |
|
+ .cdw10 = 1, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist, |
|
+ bool attach) |
|
+{ |
|
+ int i; |
|
+ __u8 buf[0x1000]; |
|
+ struct nvme_controller_list *cntlist = |
|
+ (struct nvme_controller_list *)buf; |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_ns_attach, |
|
+ .nsid = nsid, |
|
+ .addr = (__u64)(uintptr_t) cntlist, |
|
+ .cdw10 = attach ? 0 : 1, |
|
+ .data_len = 0x1000, |
|
+ }; |
|
+ |
|
+ memset(buf, 0, sizeof(buf)); |
|
+ cntlist->num = cpu_to_le16(num_ctrls); |
|
+ for (i = 0; i < num_ctrls; i++) |
|
+ cntlist->identifier[i] = cpu_to_le16(ctrlist[i]); |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_ns_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist) |
|
+{ |
|
+ return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, true); |
|
+} |
|
+ |
|
+static int nvme_ns_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist) |
|
+{ |
|
+ return nvme_ns_attachment(fd, nsid, num_ctrls, ctrlist, false); |
|
+} |
|
+ |
|
+static int nvme_fw_download(int fd, __u32 offset, __u32 data_len, void *data) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_download_fw, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ .cdw10 = (data_len >> 2) - 1, |
|
+ .cdw11 = offset >> 2, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_fw_commit(int fd, __u8 slot, __u8 action, __u8 bpid) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_activate_fw, |
|
+ .cdw10 = (bpid << 31) | (action << 3) | slot, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_sec_send(int fd, __u32 nsid, __u8 nssf, __u16 spsp, |
|
+ __u8 secp, __u32 tl, __u32 data_len, void *data, __u32 *result) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_security_send, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ .nsid = nsid, |
|
+ .cdw10 = secp << 24 | spsp << 8 | nssf, |
|
+ .cdw11 = tl, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp, |
|
+ __u8 secp, __u32 al, __u32 data_len, void *data, __u32 *result) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_security_recv, |
|
+ .nsid = nsid, |
|
+ .cdw10 = secp << 24 | spsp << 8 | nssf, |
|
+ .cdw11 = al, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper, |
|
+ __u32 data_len, __u32 dw12, void *data, __u32 *result) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_directive_send, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ .nsid = nsid, |
|
+ .cdw10 = data_len? (data_len >> 2) - 1 : 0, |
|
+ .cdw11 = dspec << 16 | dtype << 8 | doper, |
|
+ .cdw12 = dw12, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_dir_recv(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper, |
|
+ __u32 data_len, __u32 dw12, void *data, __u32 *result) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_directive_recv, |
|
+ .addr = (__u64)(uintptr_t) data, |
|
+ .data_len = data_len, |
|
+ .nsid = nsid, |
|
+ .cdw10 = data_len? (data_len >> 2) - 1 : 0, |
|
+ .cdw11 = dspec << 16 | dtype << 8 | doper, |
|
+ .cdw12 = dw12, |
|
+ }; |
|
+ int err; |
|
+ |
|
+ err = nvme_submit_admin_passthru(fd, &cmd); |
|
+ if (!err && result) |
|
+ *result = cmd.result; |
|
+ return err; |
|
+} |
|
+ |
|
+static int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp, |
|
+ __u8 no_dealloc, __u32 ovrpat) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_sanitize_nvm, |
|
+ .cdw10 = no_dealloc << 9 | oipbp << 8 | |
|
+ owpass << NVME_SANITIZE_OWPASS_SHIFT | |
|
+ ause << 3 | sanact, |
|
+ .cdw11 = ovrpat, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
+ |
|
+static int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10) |
|
+{ |
|
+ struct nvme_admin_cmd cmd = { |
|
+ .opcode = nvme_admin_dev_self_test, |
|
+ .nsid = nsid, |
|
+ .cdw10 = cdw10, |
|
+ }; |
|
+ |
|
+ return nvme_submit_admin_passthru(fd, &cmd); |
|
+} |
|
Index: multipath-tools-130222/libmultipath/nvme-ioctl.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme-ioctl.h |
|
@@ -0,0 +1,139 @@ |
|
+#ifndef _NVME_LIB_H |
|
+#define _NVME_LIB_H |
|
+ |
|
+#include <linux/types.h> |
|
+#include <stdbool.h> |
|
+#include "linux/nvme_ioctl.h" |
|
+#include "nvme.h" |
|
+ |
|
+static int nvme_get_nsid(int fd); |
|
+ |
|
+/* Generic passthrough */ |
|
+static int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, |
|
+ struct nvme_passthru_cmd *cmd); |
|
+ |
|
+static int nvme_passthru(int fd, unsigned long ioctl_cmd, __u8 opcode, __u8 flags, |
|
+ __u16 rsvd, __u32 nsid, __u32 cdw2, __u32 cdw3, |
|
+ __u32 cdw10, __u32 cdw11, __u32 cdw12, |
|
+ __u32 cdw13, __u32 cdw14, __u32 cdw15, |
|
+ __u32 data_len, void *data, __u32 metadata_len, |
|
+ void *metadata, __u32 timeout_ms, __u32 *result); |
|
+ |
|
+/* NVME_SUBMIT_IO */ |
|
+static int nvme_io(int fd, __u8 opcode, __u64 slba, __u16 nblocks, __u16 control, |
|
+ __u32 dsmgmt, __u32 reftag, __u16 apptag, |
|
+ __u16 appmask, void *data, void *metadata); |
|
+ |
|
+static int nvme_read(int fd, __u64 slba, __u16 nblocks, __u16 control, |
|
+ __u32 dsmgmt, __u32 reftag, __u16 apptag, |
|
+ __u16 appmask, void *data, void *metadata); |
|
+ |
|
+static int nvme_write(int fd, __u64 slba, __u16 nblocks, __u16 control, |
|
+ __u32 dsmgmt, __u32 reftag, __u16 apptag, |
|
+ __u16 appmask, void *data, void *metadata); |
|
+ |
|
+static int nvme_compare(int fd, __u64 slba, __u16 nblocks, __u16 control, |
|
+ __u32 dsmgmt, __u32 reftag, __u16 apptag, |
|
+ __u16 appmask, void *data, void *metadata); |
|
+ |
|
+/* NVME_IO_CMD */ |
|
+static int nvme_passthru_io(int fd, __u8 opcode, __u8 flags, __u16 rsvd, |
|
+ __u32 nsid, __u32 cdw2, __u32 cdw3, |
|
+ __u32 cdw10, __u32 cdw11, __u32 cdw12, |
|
+ __u32 cdw13, __u32 cdw14, __u32 cdw15, |
|
+ __u32 data_len, void *data, __u32 metadata_len, |
|
+ void *metadata, __u32 timeout); |
|
+ |
|
+static int nvme_write_zeros(int fd, __u32 nsid, __u64 slba, __u16 nlb, |
|
+ __u16 control, __u32 reftag, __u16 apptag, __u16 appmask); |
|
+ |
|
+static int nvme_write_uncorrectable(int fd, __u32 nsid, __u64 slba, __u16 nlb); |
|
+ |
|
+static int nvme_flush(int fd, __u32 nsid); |
|
+ |
|
+static int nvme_dsm(int fd, __u32 nsid, __u32 cdw11, struct nvme_dsm_range *dsm, |
|
+ __u16 nr_ranges); |
|
+static struct nvme_dsm_range *nvme_setup_dsm_range(__u32 *ctx_attrs, |
|
+ __u32 *llbas, __u64 *slbas, |
|
+ __u16 nr_ranges); |
|
+ |
|
+static int nvme_resv_acquire(int fd, __u32 nsid, __u8 rtype, __u8 racqa, |
|
+ bool iekey, __u64 crkey, __u64 nrkey); |
|
+static int nvme_resv_register(int fd, __u32 nsid, __u8 rrega, __u8 cptpl, |
|
+ bool iekey, __u64 crkey, __u64 nrkey); |
|
+static int nvme_resv_release(int fd, __u32 nsid, __u8 rtype, __u8 rrela, |
|
+ bool iekey, __u64 crkey); |
|
+static int nvme_resv_report(int fd, __u32 nsid, __u32 numd, __u32 cdw11, void *data); |
|
+ |
|
+static int nvme_identify13(int fd, __u32 nsid, __u32 cdw10, __u32 cdw11, void *data); |
|
+static int nvme_identify(int fd, __u32 nsid, __u32 cdw10, void *data); |
|
+static int nvme_identify_ctrl(int fd, void *data); |
|
+static int nvme_identify_ns(int fd, __u32 nsid, bool present, void *data); |
|
+static int nvme_identify_ns_list(int fd, __u32 nsid, bool all, void *data); |
|
+static int nvme_identify_ctrl_list(int fd, __u32 nsid, __u16 cntid, void *data); |
|
+static int nvme_identify_ns_descs(int fd, __u32 nsid, void *data); |
|
+static int nvme_identify_nvmset(int fd, __u16 nvmset_id, void *data); |
|
+static int nvme_get_log13(int fd, __u32 nsid, __u8 log_id, __u8 lsp, __u64 lpo, |
|
+ __u16 group_id, bool rae, __u32 data_len, void *data); |
|
+static int nvme_get_log(int fd, __u32 nsid, __u8 log_id, bool rae, |
|
+ __u32 data_len, void *data); |
|
+ |
|
+ |
|
+static int nvme_get_telemetry_log(int fd, void *lp, int generate_report, |
|
+ int ctrl_gen, size_t log_page_size, __u64 offset); |
|
+static int nvme_fw_log(int fd, struct nvme_firmware_log_page *fw_log); |
|
+static int nvme_changed_ns_list_log(int fd, |
|
+ struct nvme_changed_ns_list_log *changed_ns_list_log); |
|
+static int nvme_error_log(int fd, int entries, struct nvme_error_log_page *err_log); |
|
+static int nvme_smart_log(int fd, __u32 nsid, struct nvme_smart_log *smart_log); |
|
+static int nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo); |
|
+static int nvme_effects_log(int fd, struct nvme_effects_log_page *effects_log); |
|
+static int nvme_discovery_log(int fd, struct nvmf_disc_rsp_page_hdr *log, __u32 size); |
|
+static int nvme_sanitize_log(int fd, struct nvme_sanitize_log_page *sanitize_log); |
|
+static int nvme_endurance_log(int fd, __u16 group_id, |
|
+ struct nvme_endurance_group_log *endurance_log); |
|
+ |
|
+static int nvme_feature(int fd, __u8 opcode, __u32 nsid, __u32 cdw10, |
|
+ __u32 cdw11, __u32 cdw12, __u32 data_len, void *data, |
|
+ __u32 *result); |
|
+static int nvme_set_feature(int fd, __u32 nsid, __u8 fid, __u32 value, __u32 cdw12, |
|
+ bool save, __u32 data_len, void *data, __u32 *result); |
|
+static int nvme_get_feature(int fd, __u32 nsid, __u8 fid, __u8 sel, |
|
+ __u32 cdw11, __u32 data_len, void *data, __u32 *result); |
|
+ |
|
+static int nvme_format(int fd, __u32 nsid, __u8 lbaf, __u8 ses, __u8 pi, |
|
+ __u8 pil, __u8 ms, __u32 timeout); |
|
+ |
|
+static int nvme_ns_create(int fd, __u64 nsze, __u64 ncap, __u8 flbas, |
|
+ __u8 dps, __u8 nmic, __u32 *result); |
|
+static int nvme_ns_delete(int fd, __u32 nsid); |
|
+ |
|
+static int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls, |
|
+ __u16 *ctrlist, bool attach); |
|
+static int nvme_ns_attach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist); |
|
+static int nvme_ns_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, __u16 *ctrlist); |
|
+ |
|
+static int nvme_fw_download(int fd, __u32 offset, __u32 data_len, void *data); |
|
+static int nvme_fw_commit(int fd, __u8 slot, __u8 action, __u8 bpid); |
|
+ |
|
+static int nvme_sec_send(int fd, __u32 nsid, __u8 nssf, __u16 spsp, |
|
+ __u8 secp, __u32 tl, __u32 data_len, void *data, __u32 *result); |
|
+static int nvme_sec_recv(int fd, __u32 nsid, __u8 nssf, __u16 spsp, |
|
+ __u8 secp, __u32 al, __u32 data_len, void *data, __u32 *result); |
|
+ |
|
+static int nvme_subsystem_reset(int fd); |
|
+static int nvme_reset_controller(int fd); |
|
+static int nvme_ns_rescan(int fd); |
|
+ |
|
+static int nvme_dir_send(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper, |
|
+ __u32 data_len, __u32 dw12, void *data, __u32 *result); |
|
+static int nvme_dir_recv(int fd, __u32 nsid, __u16 dspec, __u8 dtype, __u8 doper, |
|
+ __u32 data_len, __u32 dw12, void *data, __u32 *result); |
|
+static int nvme_get_properties(int fd, void **pbar); |
|
+static int nvme_set_property(int fd, int offset, int value); |
|
+static int nvme_get_property(int fd, int offset, uint64_t *value); |
|
+static int nvme_sanitize(int fd, __u8 sanact, __u8 ause, __u8 owpass, __u8 oipbp, |
|
+ __u8 no_dealloc, __u32 ovrpat); |
|
+static int nvme_self_test_start(int fd, __u32 nsid, __u32 cdw10); |
|
+static int nvme_self_test_log(int fd, struct nvme_self_test_log *self_test_log); |
|
+#endif /* _NVME_LIB_H */ |
|
Index: multipath-tools-130222/libmultipath/nvme-lib.c |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme-lib.c |
|
@@ -0,0 +1,49 @@ |
|
+#include <sys/types.h> |
|
+/* avoid inclusion of standard API */ |
|
+#define _NVME_LIB_C 1 |
|
+#include "nvme-lib.h" |
|
+#include "nvme-ioctl.c" |
|
+#include "debug.h" |
|
+ |
|
+int log_nvme_errcode(int err, const char *dev, const char *msg) |
|
+{ |
|
+ if (err > 0) |
|
+ condlog(3, "%s: %s: NVMe status %d", dev, msg, err); |
|
+ else if (err < 0) |
|
+ condlog(3, "%s: %s: %s", dev, msg, strerror(errno)); |
|
+ return err; |
|
+} |
|
+ |
|
+int libmp_nvme_get_nsid(int fd) |
|
+{ |
|
+ return nvme_get_nsid(fd); |
|
+} |
|
+ |
|
+int libmp_nvme_identify_ctrl(int fd, struct nvme_id_ctrl *ctrl) |
|
+{ |
|
+ return nvme_identify_ctrl(fd, ctrl); |
|
+} |
|
+ |
|
+int libmp_nvme_identify_ns(int fd, __u32 nsid, bool present, |
|
+ struct nvme_id_ns *ns) |
|
+{ |
|
+ return nvme_identify_ns(fd, nsid, present, ns); |
|
+} |
|
+ |
|
+int libmp_nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo) |
|
+{ |
|
+ return nvme_ana_log(fd, ana_log, ana_log_len, rgo); |
|
+} |
|
+ |
|
+int nvme_id_ctrl_ana(int fd, struct nvme_id_ctrl *ctrl) |
|
+{ |
|
+ int rc; |
|
+ struct nvme_id_ctrl c; |
|
+ |
|
+ rc = nvme_identify_ctrl(fd, &c); |
|
+ if (rc < 0) |
|
+ return rc; |
|
+ if (ctrl) |
|
+ *ctrl = c; |
|
+ return c.cmic & (1 << 3) ? 1 : 0; |
|
+} |
|
Index: multipath-tools-130222/libmultipath/nvme-lib.h |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/nvme-lib.h |
|
@@ -0,0 +1,39 @@ |
|
+#ifndef NVME_LIB_H |
|
+#define NVME_LIB_H |
|
+ |
|
+#include "nvme.h" |
|
+ |
|
+int log_nvme_errcode(int err, const char *dev, const char *msg); |
|
+int libmp_nvme_get_nsid(int fd); |
|
+int libmp_nvme_identify_ctrl(int fd, struct nvme_id_ctrl *ctrl); |
|
+int libmp_nvme_identify_ns(int fd, __u32 nsid, bool present, |
|
+ struct nvme_id_ns *ns); |
|
+int libmp_nvme_ana_log(int fd, void *ana_log, size_t ana_log_len, int rgo); |
|
+/* |
|
+ * Identify controller, and return true if ANA is supported |
|
+ * ctrl will be filled in if controller is identified, even w/o ANA |
|
+ * ctrl may be NULL |
|
+ */ |
|
+int nvme_id_ctrl_ana(int fd, struct nvme_id_ctrl *ctrl); |
|
+ |
|
+#ifndef _NVME_LIB_C |
|
+/* |
|
+ * In all files except nvme-lib.c, the nvme functions can be called |
|
+ * by their usual name. |
|
+ */ |
|
+#define nvme_get_nsid libmp_nvme_get_nsid |
|
+#define nvme_identify_ctrl libmp_nvme_identify_ctrl |
|
+#define nvme_identify_ns libmp_nvme_identify_ns |
|
+#define nvme_ana_log libmp_nvme_ana_log |
|
+/* |
|
+ * Undefine these to avoid clashes with libmultipath's byteorder.h |
|
+ */ |
|
+#undef cpu_to_le16 |
|
+#undef cpu_to_le32 |
|
+#undef cpu_to_le64 |
|
+#undef le16_to_cpu |
|
+#undef le32_to_cpu |
|
+#undef le64_to_cpu |
|
+#endif |
|
+ |
|
+#endif /* NVME_LIB_H */ |
|
Index: multipath-tools-130222/libmultipath/prio.h |
|
=================================================================== |
|
--- multipath-tools-130222.orig/libmultipath/prio.h |
|
+++ multipath-tools-130222/libmultipath/prio.h |
|
@@ -29,6 +29,7 @@ struct path; |
|
#define PRIO_RDAC "rdac" |
|
#define PRIO_DATACORE "datacore" |
|
#define PRIO_WEIGHTED_PATH "weightedpath" |
|
+#define PRIO_ANA "ana" |
|
|
|
/* |
|
* Value used to mark the fact prio was not defined |
|
Index: multipath-tools-130222/libmultipath/prioritizers/Makefile |
|
=================================================================== |
|
--- multipath-tools-130222.orig/libmultipath/prioritizers/Makefile |
|
+++ multipath-tools-130222/libmultipath/prioritizers/Makefile |
|
@@ -2,6 +2,7 @@ |
|
# |
|
# Copyright (C) 2007 Christophe Varoqui, <christophe.varoqui@opensvc.com> |
|
# |
|
+TOPDIR = ../.. |
|
include ../../Makefile.inc |
|
|
|
LIBS = \ |
|
@@ -15,9 +16,10 @@ LIBS = \ |
|
libpriodatacore.so \ |
|
libpriohds.so \ |
|
libprioweightedpath.so \ |
|
+ libprioana.so \ |
|
libprioiet.so |
|
|
|
-CFLAGS += -fPIC -I.. |
|
+CFLAGS += -fPIC -I.. -I$(nvmedir) |
|
|
|
all: $(LIBS) |
|
|
|
Index: multipath-tools-130222/libmultipath/prioritizers/ana.c |
|
=================================================================== |
|
--- /dev/null |
|
+++ multipath-tools-130222/libmultipath/prioritizers/ana.c |
|
@@ -0,0 +1,236 @@ |
|
+/* |
|
+ * (C) Copyright HUAWEI Technology Corp. 2017 All Rights Reserved. |
|
+ * |
|
+ * ana.c |
|
+ * Version 1.00 |
|
+ * |
|
+ * Tool to make use of a NVMe-feature called Asymmetric Namespace Access. |
|
+ * It determines the ANA state of a device and prints a priority value to stdout. |
|
+ * |
|
+ * Author(s): Cheng Jike <chengjike.cheng@huawei.com> |
|
+ * Li Jie <lijie34@huawei.com> |
|
+ * |
|
+ * This file is released under the GPL version 2, or any later version. |
|
+ */ |
|
+#include <stdio.h> |
|
+#include <sys/ioctl.h> |
|
+#include <sys/stat.h> |
|
+#include <sys/types.h> |
|
+#include <stdbool.h> |
|
+#include <libudev.h> |
|
+ |
|
+#include "debug.h" |
|
+#include "nvme-lib.h" |
|
+#include "prio.h" |
|
+#include "util.h" |
|
+#include "structs.h" |
|
+#include "def_func.h" |
|
+ |
|
+enum { |
|
+ ANA_ERR_GETCTRL_FAILED = 1, |
|
+ ANA_ERR_NOT_NVME, |
|
+ ANA_ERR_NOT_SUPPORTED, |
|
+ ANA_ERR_GETANAS_OVERFLOW, |
|
+ ANA_ERR_GETANAS_NOTFOUND, |
|
+ ANA_ERR_GETANALOG_FAILED, |
|
+ ANA_ERR_GETNSID_FAILED, |
|
+ ANA_ERR_GETNS_FAILED, |
|
+ ANA_ERR_NO_MEMORY, |
|
+ ANA_ERR_NO_INFORMATION, |
|
+}; |
|
+ |
|
+static const char *ana_errmsg[] = { |
|
+ [ANA_ERR_GETCTRL_FAILED] = "couldn't get ctrl info", |
|
+ [ANA_ERR_NOT_NVME] = "not an NVMe device", |
|
+ [ANA_ERR_NOT_SUPPORTED] = "ANA not supported", |
|
+ [ANA_ERR_GETANAS_OVERFLOW] = "buffer overflow in ANA log", |
|
+ [ANA_ERR_GETANAS_NOTFOUND] = "NSID or ANAGRPID not found", |
|
+ [ANA_ERR_GETANALOG_FAILED] = "couldn't get ana log", |
|
+ [ANA_ERR_GETNSID_FAILED] = "couldn't get NSID", |
|
+ [ANA_ERR_GETNS_FAILED] = "couldn't get namespace info", |
|
+ [ANA_ERR_NO_MEMORY] = "out of memory", |
|
+ [ANA_ERR_NO_INFORMATION] = "invalid fd", |
|
+}; |
|
+ |
|
+static const char *anas_string[] = { |
|
+ [NVME_ANA_OPTIMIZED] = "ANA Optimized State", |
|
+ [NVME_ANA_NONOPTIMIZED] = "ANA Non-Optimized State", |
|
+ [NVME_ANA_INACCESSIBLE] = "ANA Inaccessible State", |
|
+ [NVME_ANA_PERSISTENT_LOSS] = "ANA Persistent Loss State", |
|
+ [NVME_ANA_CHANGE] = "ANA Change state", |
|
+}; |
|
+ |
|
+static const char *aas_print_string(int rc) |
|
+{ |
|
+ rc &= 0xff; |
|
+ if (rc >= 0 && rc < ARRAY_SIZE(anas_string) && |
|
+ anas_string[rc] != NULL) |
|
+ return anas_string[rc]; |
|
+ |
|
+ return "invalid ANA state"; |
|
+} |
|
+ |
|
+static int get_ana_state(__u32 nsid, __u32 anagrpid, void *ana_log, |
|
+ size_t ana_log_len) |
|
+{ |
|
+ void *base = ana_log; |
|
+ struct nvme_ana_rsp_hdr *hdr = base; |
|
+ struct nvme_ana_group_desc *ana_desc; |
|
+ size_t offset = sizeof(struct nvme_ana_rsp_hdr); |
|
+ __u32 nr_nsids; |
|
+ size_t nsid_buf_size; |
|
+ int i, j; |
|
+ |
|
+ for (i = 0; i < le16_to_cpu(hdr->ngrps); i++) { |
|
+ ana_desc = base + offset; |
|
+ |
|
+ offset += sizeof(*ana_desc); |
|
+ if (offset > ana_log_len) |
|
+ return -ANA_ERR_GETANAS_OVERFLOW; |
|
+ |
|
+ nr_nsids = le32_to_cpu(ana_desc->nnsids); |
|
+ nsid_buf_size = nr_nsids * sizeof(__le32); |
|
+ |
|
+ offset += nsid_buf_size; |
|
+ if (offset > ana_log_len) |
|
+ return -ANA_ERR_GETANAS_OVERFLOW; |
|
+ |
|
+ for (j = 0; j < nr_nsids; j++) { |
|
+ if (nsid == le32_to_cpu(ana_desc->nsids[j])) |
|
+ return ana_desc->state; |
|
+ } |
|
+ |
|
+ if (anagrpid != 0 && anagrpid == le32_to_cpu(ana_desc->grpid)) |
|
+ return ana_desc->state; |
|
+ |
|
+ } |
|
+ return -ANA_ERR_GETANAS_NOTFOUND; |
|
+} |
|
+ |
|
+int get_ana_info(struct path * pp, unsigned int timeout) |
|
+{ |
|
+ int rc; |
|
+ __u32 nsid; |
|
+ struct nvme_id_ctrl ctrl; |
|
+ struct nvme_id_ns ns; |
|
+ void *ana_log; |
|
+ size_t ana_log_len; |
|
+ bool is_anagrpid_const; |
|
+ |
|
+ rc = nvme_id_ctrl_ana(pp->fd, &ctrl); |
|
+ if (rc < 0) { |
|
+ log_nvme_errcode(rc, pp->dev, "nvme_identify_ctrl"); |
|
+ return -ANA_ERR_GETCTRL_FAILED; |
|
+ } else if (rc == 0) |
|
+ return -ANA_ERR_NOT_SUPPORTED; |
|
+ |
|
+ nsid = nvme_get_nsid(pp->fd); |
|
+ if (nsid <= 0) { |
|
+ log_nvme_errcode(rc, pp->dev, "nvme_get_nsid"); |
|
+ return -ANA_ERR_GETNSID_FAILED; |
|
+ } |
|
+ is_anagrpid_const = ctrl.anacap & (1 << 6); |
|
+ |
|
+ /* |
|
+ * Code copied from nvme-cli/nvme.c. We don't need to allocate an |
|
+ * [nanagrpid*mnan] array of NSIDs because each NSID can occur at most |
|
+ * in one ANA group. |
|
+ */ |
|
+ ana_log_len = sizeof(struct nvme_ana_rsp_hdr) + |
|
+ le32_to_cpu(ctrl.nanagrpid) |
|
+ * sizeof(struct nvme_ana_group_desc); |
|
+ |
|
+ if (is_anagrpid_const) { |
|
+ rc = nvme_identify_ns(pp->fd, nsid, 0, &ns); |
|
+ if (rc) { |
|
+ log_nvme_errcode(rc, pp->dev, "nvme_identify_ns"); |
|
+ return -ANA_ERR_GETNS_FAILED; |
|
+ } |
|
+ } else |
|
+ ana_log_len += le32_to_cpu(ctrl.mnan) * sizeof(__le32); |
|
+ |
|
+ ana_log = malloc(ana_log_len); |
|
+ if (!ana_log) |
|
+ return -ANA_ERR_NO_MEMORY; |
|
+ pthread_cleanup_push(free, ana_log); |
|
+ rc = nvme_ana_log(pp->fd, ana_log, ana_log_len, |
|
+ is_anagrpid_const ? NVME_ANA_LOG_RGO : 0); |
|
+ if (rc) { |
|
+ log_nvme_errcode(rc, pp->dev, "nvme_ana_log"); |
|
+ rc = -ANA_ERR_GETANALOG_FAILED; |
|
+ } else |
|
+ rc = get_ana_state(nsid, |
|
+ is_anagrpid_const ? |
|
+ le32_to_cpu(ns.anagrpid) : 0, |
|
+ ana_log, ana_log_len); |
|
+ pthread_cleanup_pop(1); |
|
+ if (rc >= 0) |
|
+ condlog(3, "%s: ana state = %02x [%s]", pp->dev, rc, |
|
+ aas_print_string(rc)); |
|
+ return rc; |
|
+} |
|
+ |
|
+/* |
|
+ * Priorities modeled roughly after the ALUA model (alua.c/sysfs.c) |
|
+ * Reference: ANA Base Protocol (NVMe TP 4004a, 11/13/2018). |
|
+ * |
|
+ * Differences: |
|
+ * |
|
+ * - The ANA base spec defines no implicit or explicit (STPG) state management. |
|
+ * If a state is encountered that doesn't allow normal I/O (all except |
|
+ * OPTIMIZED and NON_OPTIMIZED), we can't do anything but either wait for a |
|
+ * Access State Change Notice (can't do that in multipathd as we don't receive |
|
+ * those), or retry commands in regular time intervals until ANATT is expired |
|
+ * (not implemented). Mapping UNAVAILABLE state to ALUA STANDBY is the best we |
|
+ * can currently do. |
|
+ * |
|
+ * FIXME: Waiting for ANATT could be implemented with a "delayed failback" |
|
+ * mechanism. The current "failback" method can't be used, as it would |
|
+ * affect failback to every state, and here only failback to UNAVAILABLE |
|
+ * should be delayed. |
|
+ * |
|
+ * - PERSISTENT_LOSS state is even below ALUA's UNAVAILABLE state. |
|
+ * FIXME: According to the ANA TP, accessing paths in PERSISTENT_LOSS state |
|
+ * in any way makes no sense (e.g. §8.19.6 - paths in this state shouldn't |
|
+ * even be checked under "all paths down" conditions). Device mapper can, |
|
+ * and will, select a PG for IO if it has non-failed paths, even if the |
|
+ * PG has priority 0. We could avoid that only with an "ANA path checker". |
|
+ * |
|
+ * - ALUA has no CHANGE state. The ANA TP §8.18.3 / §8.19.4 suggests |
|
+ * that CHANGE state should be treated in roughly the same way as |
|
+ * INACCESSIBLE. Therefore we assign the same prio to it. |
|
+ * |
|
+ * - ALUA's LBA-dependent state has no ANA equivalent. |
|
+ */ |
|
+ |
|
+int getprio(struct path *pp, char *args) |
|
+{ |
|
+ int rc; |
|
+ |
|
+ if (pp->fd < 0) |
|
+ rc = -ANA_ERR_NO_INFORMATION; |
|
+ else |
|
+ rc = get_ana_info(pp, get_prio_timeout(60000)); |
|
+ |
|
+ switch (rc) { |
|
+ case NVME_ANA_OPTIMIZED: |
|
+ return 50; |
|
+ case NVME_ANA_NONOPTIMIZED: |
|
+ return 10; |
|
+ case NVME_ANA_INACCESSIBLE: |
|
+ case NVME_ANA_CHANGE: |
|
+ return 1; |
|
+ case NVME_ANA_PERSISTENT_LOSS: |
|
+ return 0; |
|
+ default: |
|
+ break; |
|
+ } |
|
+ if (rc < 0 && -rc < ARRAY_SIZE(ana_errmsg)) |
|
+ condlog(2, "%s: ANA error: %s", pp->dev, ana_errmsg[-rc]); |
|
+ else |
|
+ condlog(1, "%s: invalid ANA rc code %d", pp->dev, rc); |
|
+ return -1; |
|
+} |
|
+ |
|
+declare_nop_prio(initprio) |
|
+declare_nop_prio(freeprio) |
|
Index: multipath-tools-130222/libmultipath/util.h |
|
=================================================================== |
|
--- multipath-tools-130222.orig/libmultipath/util.h |
|
+++ multipath-tools-130222/libmultipath/util.h |
|
@@ -18,6 +18,8 @@ int parse_prkey(char *ptr, uint64_t *prk |
|
int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags); |
|
int safe_write(int fd, const void *buf, size_t count); |
|
|
|
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) |
|
+ |
|
#define safe_sprintf(var, format, args...) \ |
|
snprintf(var, sizeof(var), format, ##args) >= sizeof(var) |
|
#define safe_snprintf(var, size, format, args...) \ |
|
Index: multipath-tools-130222/multipath/multipath.conf.5 |
|
=================================================================== |
|
--- multipath-tools-130222.orig/multipath/multipath.conf.5 |
|
+++ multipath-tools-130222/multipath/multipath.conf.5 |
|
@@ -196,6 +196,9 @@ Generate the path priority for LSI/Engen |
|
Generate the path priority for Compaq/HP controller in |
|
active/standby mode. |
|
.TP |
|
+.B ana |
|
+Generate the path priority based on the NVMe ANA settings. |
|
+.TP |
|
.B hds |
|
Generate the path priority for Hitachi HDS Modular storage arrays. |
|
.TP |
|
Index: multipath-tools-130222/libmultipath/propsel.c |
|
=================================================================== |
|
--- multipath-tools-130222.orig/libmultipath/propsel.c |
|
+++ multipath-tools-130222/libmultipath/propsel.c |
|
@@ -5,6 +5,7 @@ |
|
*/ |
|
#include <stdio.h> |
|
|
|
+#include "nvme-lib.h" |
|
#include "checkers.h" |
|
#include "memory.h" |
|
#include "vector.h" |
|
@@ -489,8 +490,13 @@ select_getuid (struct path * pp) |
|
void |
|
detect_prio(struct path * pp) |
|
{ |
|
- if (detect_alua(pp)) |
|
- prio_get(&pp->prio, PRIO_ALUA, DEFAULT_PRIO_ARGS); |
|
+ if (pp->bus == SYSFS_BUS_NVME) { |
|
+ if (nvme_id_ctrl_ana(pp->fd, NULL) == 1) |
|
+ prio_get(&pp->prio, PRIO_ANA, DEFAULT_PRIO_ARGS); |
|
+ } else if (pp->bus == SYSFS_BUS_SCSI) { |
|
+ if (detect_alua(pp)) |
|
+ prio_get(&pp->prio, PRIO_ALUA, DEFAULT_PRIO_ARGS); |
|
+ } |
|
} |
|
|
|
extern int |
|
Index: multipath-tools-130222/libmultipath/hwtable.c |
|
=================================================================== |
|
--- multipath-tools-130222.orig/libmultipath/hwtable.c |
|
+++ multipath-tools-130222/libmultipath/hwtable.c |
|
@@ -1178,6 +1178,7 @@ static struct hwentry default_hw[] = { |
|
.vendor = "NVME", |
|
.product = ".*", |
|
.uid_attribute = "ID_WWN", |
|
+ .detect_prio = DETECT_PRIO_ON, |
|
.checker_name = NONE, |
|
}, |
|
/*
|
|
|