From: Caleb Sander <csander@purestorage.com>
Date: Fri, 12 May 2023 00:40:26 +0000 (-0600)
Subject: fabrics: handle /dev/nvme-fabrics read failure
X-Git-Tag: v1.5~30
X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=0c9443bc23e3e4c41ec8767e5f7ce120f907044b;p=users%2Fsagi%2Flibnvme.git

fabrics: handle /dev/nvme-fabrics read failure

The ability to read from /dev/nvme-fabrics to find supported options
is a newer Linux kernel feature added in f18ee3d988157 (5.17-rc1).
On earlier kernels, this read returns EINVAL,
preventing the controller from being added:
$ nvme discover --transport tcp --traddr 192.168.1.62
Failed to read from /dev/nvme-fabrics: Invalid argument
failed to add controller, error Invalid argument

So don't treat EINVAL as a fatal error, and instead fall back
to a default set of supported options.
With this change, controllers can be created successfully:
$ nvme discover --transport tcp --traddr 192.168.1.62

Discovery Log Number of Records 4, Generation counter 125
...

Fixes: d123131f2e ("fabrics: Do not pass unsupported options to kernel")
Signed-off-by: Caleb Sander <csander@purestorage.com>
---

diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c
index cb2dd0d2..17628981 100644
--- a/src/nvme/fabrics.c
+++ b/src/nvme/fabrics.c
@@ -196,6 +196,37 @@ const char *nvmf_cms_str(__u8 cm)
 	return arg_str(cms, ARRAY_SIZE(cms), cm);
 }
 
+/*
+ * Derived from Linux's supported options (the opt_tokens table)
+ * when the mechanism to report supported options was added (f18ee3d988157).
+ * Not all of these options may actually be supported,
+ * but we retain the old behavior of passing all that might be.
+ */
+static const struct nvme_fabric_options default_supported_options = {
+	.ctrl_loss_tmo = true,
+	.data_digest = true,
+	.disable_sqflow = true,
+	.discovery = true,
+	.duplicate_connect = true,
+	.fast_io_fail_tmo = true,
+	.hdr_digest = true,
+	.host_iface = true,
+	.host_traddr = true,
+	.hostid = true,
+	.hostnqn = true,
+	.keep_alive_tmo = true,
+	.nqn = true,
+	.nr_io_queues = true,
+	.nr_poll_queues = true,
+	.nr_write_queues = true,
+	.queue_size = true,
+	.reconnect_delay = true,
+	.tos = true,
+	.traddr = true,
+	.transport = true,
+	.trsvcid = true,
+};
+
 void nvmf_default_config(struct nvme_fabrics_config *cfg)
 {
 	memset(cfg, 0, sizeof(*cfg));
@@ -644,6 +675,19 @@ static  int __nvmf_supported_options(nvme_root_t r)
 	memset(buf, 0x0, sizeof(buf));
 	len = read(fd, buf, sizeof(buf) - 1);
 	if (len < 0) {
+		if (errno == EINVAL) {
+			/*
+			 * Older Linux kernels don't allow reading from nvmf_dev
+			 * to get supported options, so use a default set
+			 */
+			nvme_msg(r, LOG_DEBUG,
+			         "Cannot read %s, using default options\n",
+			         nvmf_dev);
+			*r->options = default_supported_options;
+			ret = 0;
+			goto out_close;
+		}
+
 		nvme_msg(r, LOG_ERR, "Failed to read from %s: %s\n",
 			 nvmf_dev, strerror(errno));
 		ret = -ENVME_CONNECT_READ;