| File: | .build-ci/../libnvme/src/nvme/fabrics.c |
| Warning: | line 536, column 6 Potential leak of memory pointed to by 'hid' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: LGPL-2.1-or-later | ||||
| 2 | /* | ||||
| 3 | * This file is part of libnvme. | ||||
| 4 | * Copyright (c) 2020 Western Digital Corporation or its affiliates. | ||||
| 5 | * | ||||
| 6 | * Authors: Keith Busch <keith.busch@wdc.com> | ||||
| 7 | * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com> | ||||
| 8 | */ | ||||
| 9 | |||||
| 10 | #include <dirent.h> | ||||
| 11 | #include <errno(*__errno_location ()).h> | ||||
| 12 | #include <fcntl.h> | ||||
| 13 | #include <fnmatch.h> | ||||
| 14 | #include <ifaddrs.h> | ||||
| 15 | #include <inttypes.h> | ||||
| 16 | #include <limits.h> | ||||
| 17 | #include <stdbool.h> | ||||
| 18 | #include <stddef.h> | ||||
| 19 | #include <stdio.h> | ||||
| 20 | #include <stdlib.h> | ||||
| 21 | #include <string.h> | ||||
| 22 | #include <strings.h> | ||||
| 23 | #include <unistd.h> | ||||
| 24 | |||||
| 25 | #include <arpa/inet.h> | ||||
| 26 | #include <linux1/if_ether.h> | ||||
| 27 | #include <net/if.h> | ||||
| 28 | #include <netpacket/packet.h> | ||||
| 29 | #include <sys/param.h> | ||||
| 30 | #include <sys/stat.h> | ||||
| 31 | #include <sys/types.h> | ||||
| 32 | |||||
| 33 | #include <ccan/array_size/array_size.h> | ||||
| 34 | #include <ccan/endian/endian.h> | ||||
| 35 | #include <ccan/list/list.h> | ||||
| 36 | #include <ccan/str/str.h> | ||||
| 37 | |||||
| 38 | #include <shared/array-util.h> | ||||
| 39 | #include <shared/compiler-attributes-util.h> | ||||
| 40 | #include <shared/machine-id-util.h> | ||||
| 41 | #include <shared/nqn-util.h> | ||||
| 42 | #include <shared/string-util.h> | ||||
| 43 | #include <shared/uuid-util.h> | ||||
| 44 | |||||
| 45 | #include <libnvme.h> | ||||
| 46 | |||||
| 47 | #include "cleanup.h" | ||||
| 48 | #include "cleanup-linux.h" | ||||
| 49 | #include "private.h" | ||||
| 50 | #include "private-fabrics.h" | ||||
| 51 | |||||
| 52 | const char *nvmf_dev = "/dev/nvme-fabrics"; | ||||
| 53 | |||||
| 54 | static inline void free_uri(struct libnvmf_uri **uri) | ||||
| 55 | { | ||||
| 56 | libnvmf_uri_free(*uri); | ||||
| 57 | } | ||||
| 58 | #define __cleanup_uri__attribute__((cleanup(free_uri))) __cleanup(free_uri)__attribute__((cleanup(free_uri))) | ||||
| 59 | |||||
| 60 | static struct libnvme_ctrl_params ctrl_params_copy( | ||||
| 61 | const struct libnvme_ctrl_params *src) | ||||
| 62 | { | ||||
| 63 | struct libnvme_ctrl_params params = *src; | ||||
| 64 | |||||
| 65 | params.host_iface = NULL((void*)0); | ||||
| 66 | return params; | ||||
| 67 | } | ||||
| 68 | |||||
| 69 | static struct libnvme_ctrl_params ctrl_params_dup( | ||||
| 70 | const struct libnvme_ctrl_params *src) | ||||
| 71 | { | ||||
| 72 | struct libnvme_ctrl_params params = *src; | ||||
| 73 | |||||
| 74 | params.host_iface = shr_xstrdup(src->host_iface); | ||||
| 75 | return params; | ||||
| 76 | } | ||||
| 77 | |||||
| 78 | static inline void cleanup_ctrl_params(struct libnvme_ctrl_params *params) | ||||
| 79 | { | ||||
| 80 | free((char *)params->host_iface); | ||||
| 81 | } | ||||
| 82 | #define __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) __cleanup(cleanup_ctrl_params)__attribute__((cleanup(cleanup_ctrl_params))) | ||||
| 83 | |||||
| 84 | const char *arg_str(const char * const *strings, | ||||
| 85 | size_t array_size, size_t idx) | ||||
| 86 | { | ||||
| 87 | if (idx < array_size && strings[idx]) | ||||
| 88 | return strings[idx]; | ||||
| 89 | return "unrecognized"; | ||||
| 90 | } | ||||
| 91 | |||||
| 92 | #define NVMF_HOSTID_SIZE37 37 | ||||
| 93 | |||||
| 94 | #define NVMF_HOSTNQN_FILE"/usr/local/etc" "/nvme/hostnqn" SYSCONFDIR"/usr/local/etc" "/nvme/hostnqn" | ||||
| 95 | #define NVMF_HOSTID_FILE"/usr/local/etc" "/nvme/hostid" SYSCONFDIR"/usr/local/etc" "/nvme/hostid" | ||||
| 96 | |||||
| 97 | static int uuid_from_device_tree(struct libnvme_global_ctx *ctx, | ||||
| 98 | char *system_uuid) | ||||
| 99 | { | ||||
| 100 | __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int f = -1; | ||||
| 101 | ssize_t len; | ||||
| 102 | |||||
| 103 | f = open(libnvme_uuid_ibm_filename(ctx), O_RDONLY00); | ||||
| 104 | if (f < 0) | ||||
| 105 | return -ENXIO6; | ||||
| 106 | |||||
| 107 | memset(system_uuid, 0, NVME_UUID_LEN_STRING37); | ||||
| 108 | len = read(f, system_uuid, NVME_UUID_LEN_STRING37 - 1); | ||||
| 109 | if (len < 0) | ||||
| 110 | return -ENXIO6; | ||||
| 111 | |||||
| 112 | return strlen(system_uuid) ? 0 : -ENXIO6; | ||||
| 113 | } | ||||
| 114 | |||||
| 115 | /* | ||||
| 116 | * See System Management BIOS (SMBIOS) Reference Specification | ||||
| 117 | * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf | ||||
| 118 | */ | ||||
| 119 | #define DMI_SYSTEM_INFORMATION1 1 | ||||
| 120 | |||||
| 121 | static bool_Bool is_dmi_uuid_valid(const char *buf, size_t len) | ||||
| 122 | { | ||||
| 123 | int i; | ||||
| 124 | |||||
| 125 | /* UUID bytes are from byte 8 to 23 */ | ||||
| 126 | if (len < 24) | ||||
| 127 | return false0; | ||||
| 128 | |||||
| 129 | /* Test it's a invalid UUID with all zeros */ | ||||
| 130 | for (i = 8; i < 24; i++) { | ||||
| 131 | if (buf[i]) | ||||
| 132 | break; | ||||
| 133 | } | ||||
| 134 | if (i == 24) | ||||
| 135 | return false0; | ||||
| 136 | |||||
| 137 | return true1; | ||||
| 138 | } | ||||
| 139 | |||||
| 140 | static int read_file(char *filename, char *buf, size_t size) | ||||
| 141 | { | ||||
| 142 | __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int f = -1; | ||||
| 143 | int len; | ||||
| 144 | |||||
| 145 | f = open(filename, O_RDONLY00); | ||||
| 146 | if (f < 0) | ||||
| 147 | return -errno(*__errno_location ()); | ||||
| 148 | len = read(f, buf, size - 1); | ||||
| 149 | if (len < 0) | ||||
| 150 | return -errno(*__errno_location ()); | ||||
| 151 | buf[len] = 0; | ||||
| 152 | |||||
| 153 | return len; | ||||
| 154 | } | ||||
| 155 | |||||
| 156 | static int uuid_from_dmi_entries(struct libnvme_global_ctx *ctx, | ||||
| 157 | char *system_uuid) | ||||
| 158 | { | ||||
| 159 | __cleanup_dir__attribute__((cleanup(cleanup_dir))) DIR *d = NULL((void*)0); | ||||
| 160 | const char *entries_dir = libnvme_dmi_entries_dir(ctx); | ||||
| 161 | char filename[PATH_MAX4096]; | ||||
| 162 | struct dirent *de; | ||||
| 163 | char buf[513] = {0}; | ||||
| 164 | int len, type; | ||||
| 165 | |||||
| 166 | system_uuid[0] = '\0'; | ||||
| 167 | d = opendir(entries_dir); | ||||
| 168 | if (!d) | ||||
| 169 | return -ENXIO6; | ||||
| 170 | while ((de = readdir(d))) { | ||||
| 171 | if (de->d_name[0] == '.') | ||||
| 172 | continue; | ||||
| 173 | snprintf(filename, sizeof(filename), "%s/%s/type", entries_dir, | ||||
| 174 | de->d_name); | ||||
| 175 | len = read_file(filename, buf, sizeof(buf)); | ||||
| 176 | if (len <= 0) | ||||
| 177 | continue; | ||||
| 178 | if (sscanf(buf, "%d", &type) != 1) | ||||
| 179 | continue; | ||||
| 180 | if (type != DMI_SYSTEM_INFORMATION1) | ||||
| 181 | continue; | ||||
| 182 | snprintf(filename, sizeof(filename), "%s/%s/raw", entries_dir, | ||||
| 183 | de->d_name); | ||||
| 184 | len = read_file(filename, buf, sizeof(buf)); | ||||
| 185 | if (len <= 0) | ||||
| 186 | continue; | ||||
| 187 | |||||
| 188 | if (!is_dmi_uuid_valid(buf, len)) | ||||
| 189 | continue; | ||||
| 190 | |||||
| 191 | /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ | ||||
| 192 | /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ | ||||
| 193 | sprintf(system_uuid, | ||||
| 194 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" | ||||
| 195 | "%02x%02x%02x%02x%02x%02x", | ||||
| 196 | (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2], | ||||
| 197 | (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0], | ||||
| 198 | (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4], | ||||
| 199 | (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6], | ||||
| 200 | (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9], | ||||
| 201 | (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11], | ||||
| 202 | (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13], | ||||
| 203 | (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]); | ||||
| 204 | break; | ||||
| 205 | } | ||||
| 206 | return strlen(system_uuid) ? 0 : -ENXIO6; | ||||
| 207 | } | ||||
| 208 | |||||
| 209 | /** | ||||
| 210 | * uuid_from_product_uuid() - Get system UUID from product_uuid | ||||
| 211 | * @ctx: Global context, for the sysfs path. | ||||
| 212 | * @system_uuid: Where to save the system UUID. | ||||
| 213 | * | ||||
| 214 | * Return: 0 on success, -ENXIO otherwise. | ||||
| 215 | */ | ||||
| 216 | static int uuid_from_product_uuid(struct libnvme_global_ctx *ctx, | ||||
| 217 | char *system_uuid) | ||||
| 218 | { | ||||
| 219 | __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *stream = NULL((void*)0); | ||||
| 220 | |||||
| 221 | stream = fopen(libnvme_dmi_product_uuid_filename(ctx), "re"); | ||||
| 222 | if (!stream) | ||||
| 223 | return -ENXIO6; | ||||
| 224 | |||||
| 225 | system_uuid[0] = '\0'; | ||||
| 226 | |||||
| 227 | /* The kernel is handling the byte swapping according DMTF | ||||
| 228 | * SMBIOS 3.0 Section 7.2.1 System UUID */ | ||||
| 229 | |||||
| 230 | /* | ||||
| 231 | * Expect exactly: | ||||
| 232 | * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ||||
| 233 | */ | ||||
| 234 | if (!fgets(system_uuid, NVME_UUID_LEN_STRING37, stream)) | ||||
| 235 | return -ENXIO6; | ||||
| 236 | |||||
| 237 | if (strlen(system_uuid) != NVME_UUID_LEN_STRING37 - 1) | ||||
| 238 | return -ENXIO6; | ||||
| 239 | |||||
| 240 | if (system_uuid[8] != '-' || system_uuid[13] != '-' || | ||||
| 241 | system_uuid[18] != '-' || system_uuid[23] != '-') | ||||
| 242 | return -ENXIO6; | ||||
| 243 | |||||
| 244 | system_uuid[NVME_UUID_LEN_STRING37 - 1] = '\0'; | ||||
| 245 | |||||
| 246 | return 0; | ||||
| 247 | } | ||||
| 248 | |||||
| 249 | /** | ||||
| 250 | * uuid_from_machine_id() - Derive a system UUID from the local machine ID | ||||
| 251 | * @ctx: Global context, for the file path. | ||||
| 252 | * @system_uuid: Where to save the system UUID. | ||||
| 253 | * | ||||
| 254 | * The machine ID identifies an installation rather than the hardware, so it | ||||
| 255 | * is less stable than DMI or the device tree. It is still deterministic, and | ||||
| 256 | * unlike them it is readable without privileges. | ||||
| 257 | * | ||||
| 258 | * Return: 0 on success, negative errno otherwise. | ||||
| 259 | */ | ||||
| 260 | static int uuid_from_machine_id(struct libnvme_global_ctx *ctx, | ||||
| 261 | char *system_uuid) | ||||
| 262 | { | ||||
| 263 | /* | ||||
| 264 | * Fixed application ID for nvme-cli's use of the local machine ID. | ||||
| 265 | * Chosen once, at random, and part of the derivation: changing it | ||||
| 266 | * changes the host identifier of every machine that has no DMI or | ||||
| 267 | * device tree UUID. | ||||
| 268 | */ | ||||
| 269 | static const unsigned char app_id[SHR_UUID_LEN16] = { | ||||
| 270 | 0x85, 0x3c, 0x32, 0x20, 0xca, 0x64, 0x4d, 0xad, | ||||
| 271 | 0xa0, 0x6f, 0x1d, 0xc7, 0x52, 0xe3, 0x22, 0x63 | ||||
| 272 | }; | ||||
| 273 | unsigned char uuid[NVME_UUID_LEN16]; | ||||
| 274 | int ret; | ||||
| 275 | |||||
| 276 | ret = shr_machine_id_app_specific(libnvme_machine_id_filename(ctx), | ||||
| 277 | app_id, uuid); | ||||
| 278 | if (ret) | ||||
| 279 | return ret; | ||||
| 280 | |||||
| 281 | libnvme_uuid_to_string(uuid, system_uuid); | ||||
| 282 | |||||
| 283 | return 0; | ||||
| 284 | } | ||||
| 285 | |||||
| 286 | /** | ||||
| 287 | * uuid_from_dmi() - read system UUID | ||||
| 288 | * @ctx: Global context, for the sysfs paths. | ||||
| 289 | * @system_uuid: buffer for the UUID | ||||
| 290 | * | ||||
| 291 | * The system UUID can be read from two different locations: | ||||
| 292 | * | ||||
| 293 | * 1) /sys/class/dmi/id/product_uuid | ||||
| 294 | * 2) /sys/firmware/dmi/entries | ||||
| 295 | * | ||||
| 296 | * Note that the second location is not present on Debian-based systems. | ||||
| 297 | * | ||||
| 298 | * Return: 0 on success, negative errno otherwise. | ||||
| 299 | */ | ||||
| 300 | static int uuid_from_dmi(struct libnvme_global_ctx *ctx, char *system_uuid) | ||||
| 301 | { | ||||
| 302 | int ret = uuid_from_product_uuid(ctx, system_uuid); | ||||
| 303 | if (ret != 0) | ||||
| 304 | ret = uuid_from_dmi_entries(ctx, system_uuid); | ||||
| 305 | return ret; | ||||
| 306 | } | ||||
| 307 | |||||
| 308 | /* | ||||
| 309 | * Virtual machines and some firmware report a placeholder instead of a real | ||||
| 310 | * identifier. Every machine reporting the same placeholder would end up with | ||||
| 311 | * the same host identifier. | ||||
| 312 | * | ||||
| 313 | * shr_hostid_valid() rejects the all-zeros UUID. The all-ones UUID is the | ||||
| 314 | * other common placeholder, but it is a legitimate value to configure by | ||||
| 315 | * hand, so it is refused here rather than in the shared validator. | ||||
| 316 | */ | ||||
| 317 | static bool_Bool hostid_source_usable(const char *system_uuid) | ||||
| 318 | { | ||||
| 319 | return shr_hostid_valid(system_uuid) && | ||||
| 320 | !shr_streqcase0(system_uuid, | ||||
| 321 | "ffffffff-ffff-ffff-ffff-ffffffffffff"); | ||||
| 322 | } | ||||
| 323 | |||||
| 324 | __shr_public__attribute__((visibility("default"))) char *libnvmf_generate_hostid(struct libnvme_global_ctx *ctx) | ||||
| 325 | { | ||||
| 326 | /* | ||||
| 327 | * Ordered from most to least stable. The firmware sources describe | ||||
| 328 | * the hardware and survive a reinstall; the machine ID describes the | ||||
| 329 | * installation and does not. | ||||
| 330 | */ | ||||
| 331 | static const struct { | ||||
| 332 | const char *name; | ||||
| 333 | int (*get)(struct libnvme_global_ctx *ctx, char *system_uuid); | ||||
| 334 | } hostid_sources[] = { | ||||
| 335 | { "DMI", uuid_from_dmi }, | ||||
| 336 | { "device tree", uuid_from_device_tree }, | ||||
| 337 | { "machine ID", uuid_from_machine_id }, | ||||
| 338 | }; | ||||
| 339 | char uuid_str[NVME_UUID_LEN_STRING37]; | ||||
| 340 | unsigned char uuid[NVME_UUID_LEN16]; | ||||
| 341 | size_t i; | ||||
| 342 | |||||
| 343 | if (!ctx) | ||||
| 344 | return NULL((void*)0); | ||||
| 345 | |||||
| 346 | for (i = 0; i < ARRAY_SIZE(hostid_sources)(sizeof(hostid_sources) / sizeof((hostid_sources)[0]) + 0); i++) { | ||||
| 347 | if (hostid_sources[i].get(ctx, uuid_str)) | ||||
| 348 | continue; | ||||
| 349 | |||||
| 350 | if (!hostid_source_usable(uuid_str)) { | ||||
| 351 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s reports an unusable host identifier '%s'\n" , hostid_sources[i].name, uuid_str) | ||||
| 352 | "%s reports an unusable host identifier '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s reports an unusable host identifier '%s'\n" , hostid_sources[i].name, uuid_str) | ||||
| 353 | hostid_sources[i].name, uuid_str)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s reports an unusable host identifier '%s'\n" , hostid_sources[i].name, uuid_str); | ||||
| 354 | continue; | ||||
| 355 | } | ||||
| 356 | |||||
| 357 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "host identifier taken from %s\n" , hostid_sources[i].name) | ||||
| 358 | "host identifier taken from %s\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "host identifier taken from %s\n" , hostid_sources[i].name) | ||||
| 359 | hostid_sources[i].name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "host identifier taken from %s\n" , hostid_sources[i].name); | ||||
| 360 | |||||
| 361 | return strdup(uuid_str); | ||||
| 362 | } | ||||
| 363 | |||||
| 364 | libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "no stable host identifier available, using a random one\n" ) | ||||
| 365 | "no stable host identifier available, using a random one\n")__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "no stable host identifier available, using a random one\n" ); | ||||
| 366 | |||||
| 367 | if (libnvme_random_uuid(uuid) < 0) | ||||
| 368 | memset(uuid, 0, NVME_UUID_LEN16); | ||||
| 369 | libnvme_uuid_to_string(uuid, uuid_str); | ||||
| 370 | |||||
| 371 | return strdup(uuid_str); | ||||
| 372 | } | ||||
| 373 | |||||
| 374 | __shr_public__attribute__((visibility("default"))) char *libnvmf_generate_hostnqn_from_hostid( | ||||
| 375 | struct libnvme_global_ctx *ctx, char *hostid) | ||||
| 376 | { | ||||
| 377 | char *hid = NULL((void*)0); | ||||
| 378 | char *hostnqn; | ||||
| 379 | int ret; | ||||
| 380 | |||||
| 381 | if (!ctx) | ||||
| 382 | return NULL((void*)0); | ||||
| 383 | |||||
| 384 | if (!hostid) { | ||||
| 385 | hostid = hid = libnvmf_generate_hostid(ctx); | ||||
| 386 | if (!hostid) | ||||
| 387 | return NULL((void*)0); | ||||
| 388 | } | ||||
| 389 | |||||
| 390 | ret = asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", hostid); | ||||
| 391 | free(hid); | ||||
| 392 | |||||
| 393 | return (ret < 0) ? NULL((void*)0) : hostnqn; | ||||
| 394 | } | ||||
| 395 | |||||
| 396 | __shr_public__attribute__((visibility("default"))) char *libnvmf_generate_hostnqn(struct libnvme_global_ctx *ctx) | ||||
| 397 | { | ||||
| 398 | return libnvmf_generate_hostnqn_from_hostid(ctx, NULL((void*)0)); | ||||
| 399 | } | ||||
| 400 | |||||
| 401 | static char *nvmf_read_file(const char *f, int len) | ||||
| 402 | { | ||||
| 403 | char buf[len]; | ||||
| 404 | __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1; | ||||
| 405 | int ret; | ||||
| 406 | |||||
| 407 | fd = open(f, O_RDONLY00); | ||||
| 408 | if (fd < 0) | ||||
| 409 | return NULL((void*)0); | ||||
| 410 | |||||
| 411 | memset(buf, 0, len); | ||||
| 412 | ret = read(fd, buf, len - 1); | ||||
| 413 | |||||
| 414 | if (ret < 0 || !strlen(buf)) | ||||
| 415 | return NULL((void*)0); | ||||
| 416 | return strndup(buf, strcspn(buf, "\n")); | ||||
| 417 | } | ||||
| 418 | |||||
| 419 | __shr_public__attribute__((visibility("default"))) char *libnvmf_read_hostnqn(struct libnvme_global_ctx *ctx) | ||||
| 420 | { | ||||
| 421 | char *val; | ||||
| 422 | |||||
| 423 | if (!ctx) | ||||
| 424 | return NULL((void*)0); | ||||
| 425 | |||||
| 426 | if (shr_nqn_valid(ctx->hostnqn)) | ||||
| 427 | return strdup(ctx->hostnqn); | ||||
| 428 | |||||
| 429 | val = nvmf_read_file(NVMF_HOSTNQN_FILE"/usr/local/etc" "/nvme/hostnqn", NVMF_NQN_SIZE223); | ||||
| 430 | if (shr_nqn_valid(val)) | ||||
| 431 | return val; | ||||
| 432 | |||||
| 433 | if (val) { | ||||
| 434 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host NQN\n" , "/usr/local/etc" "/nvme/hostnqn") | ||||
| 435 | "%s does not contain a valid host NQN\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host NQN\n" , "/usr/local/etc" "/nvme/hostnqn") | ||||
| 436 | NVMF_HOSTNQN_FILE)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host NQN\n" , "/usr/local/etc" "/nvme/hostnqn"); | ||||
| 437 | free(val); | ||||
| 438 | } | ||||
| 439 | |||||
| 440 | return NULL((void*)0); | ||||
| 441 | } | ||||
| 442 | |||||
| 443 | __shr_public__attribute__((visibility("default"))) char *libnvmf_read_hostid(struct libnvme_global_ctx *ctx) | ||||
| 444 | { | ||||
| 445 | char *val; | ||||
| 446 | |||||
| 447 | if (!ctx) | ||||
| 448 | return NULL((void*)0); | ||||
| 449 | |||||
| 450 | if (shr_hostid_valid(ctx->hostid)) | ||||
| 451 | return strdup(ctx->hostid); | ||||
| 452 | |||||
| 453 | val = nvmf_read_file(NVMF_HOSTID_FILE"/usr/local/etc" "/nvme/hostid", NVMF_HOSTID_SIZE37); | ||||
| 454 | if (shr_hostid_valid(val)) | ||||
| 455 | return val; | ||||
| 456 | |||||
| 457 | if (val) { | ||||
| 458 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host identifier\n" , "/usr/local/etc" "/nvme/hostid") | ||||
| 459 | "%s does not contain a valid host identifier\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host identifier\n" , "/usr/local/etc" "/nvme/hostid") | ||||
| 460 | NVMF_HOSTID_FILE)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s does not contain a valid host identifier\n" , "/usr/local/etc" "/nvme/hostid"); | ||||
| 461 | free(val); | ||||
| 462 | } | ||||
| 463 | |||||
| 464 | return NULL((void*)0); | ||||
| 465 | } | ||||
| 466 | |||||
| 467 | __shr_public__attribute__((visibility("default"))) int libnvmf_host_get_ids(struct libnvme_global_ctx *ctx, | ||||
| 468 | const char *hostnqn_arg, const char *hostid_arg, | ||||
| 469 | char **hostnqn, char **hostid) | ||||
| 470 | { | ||||
| 471 | __cleanup_free__attribute__((cleanup(shr_freep))) char *nqn = NULL((void*)0); | ||||
| 472 | __cleanup_free__attribute__((cleanup(shr_freep))) char *hid = NULL((void*)0); | ||||
| 473 | __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0); | ||||
| 474 | struct libnvme_host *h; | ||||
| 475 | |||||
| 476 | if (!ctx) | ||||
| |||||
| 477 | return -EINVAL22; | ||||
| 478 | |||||
| 479 | /* command line argumments */ | ||||
| 480 | if (hostid_arg) | ||||
| 481 | hid = strdup(hostid_arg); | ||||
| 482 | if (hostnqn_arg) | ||||
| 483 | hnqn = strdup(hostnqn_arg); | ||||
| 484 | |||||
| 485 | /* first host already resolved in ctx->hosts, if any */ | ||||
| 486 | h = libnvme_first_host(ctx); | ||||
| 487 | if (h) { | ||||
| 488 | if (!hid
| ||||
| 489 | hid = shr_xstrdup(libnvme_host_get_hostid(h)); | ||||
| 490 | if (!hnqn) | ||||
| 491 | hnqn = shr_xstrdup(libnvme_host_get_hostnqn(h)); | ||||
| 492 | } | ||||
| 493 | |||||
| 494 | /* /etc/nvme/hostid and/or /etc/nvme/hostnqn */ | ||||
| 495 | if (!hid) | ||||
| 496 | hid = libnvmf_read_hostid(ctx); | ||||
| 497 | if (!hnqn
| ||||
| 498 | hnqn = libnvmf_read_hostnqn(ctx); | ||||
| 499 | |||||
| 500 | /* incomplete configuration, thus derive hostid from hostnqn */ | ||||
| 501 | if (!hid
| ||||
| 502 | hid = libnvme_hostid_from_hostnqn(hnqn); | ||||
| 503 | |||||
| 504 | /* | ||||
| 505 | * fallback to use either DMI information or device-tree. If all | ||||
| 506 | * fails generate one | ||||
| 507 | */ | ||||
| 508 | if (!hid
| ||||
| 509 | hid = libnvmf_generate_hostid(ctx); | ||||
| 510 | if (!hid) | ||||
| 511 | return -ENOMEM12; | ||||
| 512 | |||||
| 513 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: using auto generated hostid and hostnqn\n" ) | ||||
| 514 | "warning: using auto generated hostid and hostnqn\n")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: using auto generated hostid and hostnqn\n" ); | ||||
| 515 | } | ||||
| 516 | |||||
| 517 | /* incomplete configuration, thus derive hostnqn from hostid */ | ||||
| 518 | if (!hnqn
| ||||
| 519 | hnqn = libnvmf_generate_hostnqn_from_hostid(ctx, hid); | ||||
| 520 | if (!hnqn) | ||||
| 521 | return -ENOMEM12; | ||||
| 522 | } | ||||
| 523 | |||||
| 524 | /* sanity checks */ | ||||
| 525 | nqn = libnvme_hostid_from_hostnqn(hnqn); | ||||
| 526 | if (nqn && strcmp(nqn, hid)) { | ||||
| 527 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n" , hid, hnqn) | ||||
| 528 | "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n" , hid, hnqn) | ||||
| 529 | hid, hnqn)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "warning: use hostid '%s' which does not match uuid in hostnqn '%s'\n" , hid, hnqn); | ||||
| 530 | } | ||||
| 531 | |||||
| 532 | if (hostid) { | ||||
| 533 | *hostid = hid; | ||||
| 534 | hid = NULL((void*)0); | ||||
| 535 | } | ||||
| 536 | if (hostnqn) { | ||||
| |||||
| 537 | *hostnqn = hnqn; | ||||
| 538 | hnqn = NULL((void*)0); | ||||
| 539 | } | ||||
| 540 | |||||
| 541 | return 0; | ||||
| 542 | } | ||||
| 543 | |||||
| 544 | int _libnvmf_persistent_from_str(const char *str, enum libnvmf_persistent *val) | ||||
| 545 | { | ||||
| 546 | if (!str || !val) | ||||
| 547 | return -EINVAL22; | ||||
| 548 | |||||
| 549 | if (!strcasecmp(str, "no")) | ||||
| 550 | *val = LIBNVMF_PERSISTENT_NO; | ||||
| 551 | else if (!strcasecmp(str, "auto")) | ||||
| 552 | *val = LIBNVMF_PERSISTENT_AUTO; | ||||
| 553 | else if (!strcasecmp(str, "force")) | ||||
| 554 | *val = LIBNVMF_PERSISTENT_FORCE; | ||||
| 555 | else | ||||
| 556 | return -EINVAL22; | ||||
| 557 | |||||
| 558 | return 0; | ||||
| 559 | } | ||||
| 560 | |||||
| 561 | /** | ||||
| 562 | * libnvmf_context_set_persistent() - Set the discovery controller | ||||
| 563 | * persistence mode. | ||||
| 564 | * @fctx: The &struct libnvmf_context instance to update. | ||||
| 565 | * @persistent: One of "no", "auto", "force" (case-insensitive). | ||||
| 566 | * | ||||
| 567 | * Return: 0 on success, -EINVAL if @persistent matches none of the | ||||
| 568 | * accepted values. | ||||
| 569 | */ | ||||
| 570 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_persistent(struct libnvmf_context *fctx, | ||||
| 571 | const char *persistent) | ||||
| 572 | { | ||||
| 573 | if (!fctx) | ||||
| 574 | return -EINVAL22; | ||||
| 575 | |||||
| 576 | return _libnvmf_persistent_from_str(persistent, &fctx->persistent); | ||||
| 577 | } | ||||
| 578 | |||||
| 579 | /** | ||||
| 580 | * libnvmf_context_get_persistent() - Get the discovery controller | ||||
| 581 | * persistence mode. | ||||
| 582 | * @fctx: The &struct libnvmf_context instance to query. | ||||
| 583 | * | ||||
| 584 | * Return: "no", "auto", or "force" if explicitly configured; NULL if not | ||||
| 585 | * (behaves the same as "no" when applied to a live connection, or if | ||||
| 586 | * @fctx is NULL). | ||||
| 587 | */ | ||||
| 588 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_context_get_persistent( | ||||
| 589 | const struct libnvmf_context *fctx) | ||||
| 590 | { | ||||
| 591 | if (!fctx) | ||||
| 592 | return NULL((void*)0); | ||||
| 593 | |||||
| 594 | switch (fctx->persistent) { | ||||
| 595 | case LIBNVMF_PERSISTENT_NO: | ||||
| 596 | return "no"; | ||||
| 597 | case LIBNVMF_PERSISTENT_AUTO: | ||||
| 598 | return "auto"; | ||||
| 599 | case LIBNVMF_PERSISTENT_FORCE: | ||||
| 600 | return "force"; | ||||
| 601 | case LIBNVMF_PERSISTENT_UNSET: | ||||
| 602 | default: | ||||
| 603 | return NULL((void*)0); | ||||
| 604 | } | ||||
| 605 | } | ||||
| 606 | |||||
| 607 | const char * const trtypes[] = { | ||||
| 608 | [NVMF_TRTYPE_RDMA] = "rdma", | ||||
| 609 | [NVMF_TRTYPE_FC] = "fc", | ||||
| 610 | [NVMF_TRTYPE_TCP] = "tcp", | ||||
| 611 | [NVMF_TRTYPE_LOOP] = "loop", | ||||
| 612 | }; | ||||
| 613 | |||||
| 614 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_trtype_str(__u8 trtype) | ||||
| 615 | { | ||||
| 616 | return arg_str(trtypes, ARRAY_SIZE(trtypes)(sizeof(trtypes) / sizeof((trtypes)[0]) + 0), trtype); | ||||
| 617 | } | ||||
| 618 | |||||
| 619 | static const char * const adrfams[] = { | ||||
| 620 | [NVMF_ADDR_FAMILY_PCI] = "pci", | ||||
| 621 | [NVMF_ADDR_FAMILY_IP4] = "ipv4", | ||||
| 622 | [NVMF_ADDR_FAMILY_IP6] = "ipv6", | ||||
| 623 | [NVMF_ADDR_FAMILY_IB] = "infiniband", | ||||
| 624 | [NVMF_ADDR_FAMILY_FC] = "fibre-channel", | ||||
| 625 | }; | ||||
| 626 | |||||
| 627 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_adrfam_str(__u8 adrfam) | ||||
| 628 | { | ||||
| 629 | return arg_str(adrfams, ARRAY_SIZE(adrfams)(sizeof(adrfams) / sizeof((adrfams)[0]) + 0), adrfam); | ||||
| 630 | } | ||||
| 631 | |||||
| 632 | static const char * const subtypes[] = { | ||||
| 633 | [NVME_NQN_DISC] = "discovery subsystem referral", | ||||
| 634 | [NVME_NQN_NVME] = "nvme subsystem", | ||||
| 635 | [NVME_NQN_CURR] = "current discovery subsystem", | ||||
| 636 | }; | ||||
| 637 | |||||
| 638 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_subtype_str(__u8 subtype) | ||||
| 639 | { | ||||
| 640 | return arg_str(subtypes, ARRAY_SIZE(subtypes)(sizeof(subtypes) / sizeof((subtypes)[0]) + 0), subtype); | ||||
| 641 | } | ||||
| 642 | |||||
| 643 | static const char * const treqs[] = { | ||||
| 644 | [NVMF_TREQ_NOT_SPECIFIED] = "not specified", | ||||
| 645 | [NVMF_TREQ_REQUIRED] = "required", | ||||
| 646 | [NVMF_TREQ_NOT_REQUIRED] = "not required", | ||||
| 647 | [NVMF_TREQ_NOT_SPECIFIED | | ||||
| 648 | NVMF_TREQ_DISABLE_SQFLOW] = "not specified, " | ||||
| 649 | "sq flow control disable supported", | ||||
| 650 | [NVMF_TREQ_REQUIRED | | ||||
| 651 | NVMF_TREQ_DISABLE_SQFLOW] = "required, " | ||||
| 652 | "sq flow control disable supported", | ||||
| 653 | [NVMF_TREQ_NOT_REQUIRED | | ||||
| 654 | NVMF_TREQ_DISABLE_SQFLOW] = "not required, " | ||||
| 655 | "sq flow control disable supported", | ||||
| 656 | }; | ||||
| 657 | |||||
| 658 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_treq_str(__u8 treq) | ||||
| 659 | { | ||||
| 660 | return arg_str(treqs, ARRAY_SIZE(treqs)(sizeof(treqs) / sizeof((treqs)[0]) + 0), treq); | ||||
| 661 | } | ||||
| 662 | |||||
| 663 | static const char * const eflags_strings[] = { | ||||
| 664 | [NVMF_DISC_EFLAGS_NONE] = "none", | ||||
| 665 | [NVMF_DISC_EFLAGS_EPCSD] = "explicit discovery connections", | ||||
| 666 | [NVMF_DISC_EFLAGS_DUPRETINFO] = "duplicate discovery information", | ||||
| 667 | [NVMF_DISC_EFLAGS_EPCSD | | ||||
| 668 | NVMF_DISC_EFLAGS_DUPRETINFO] = "explicit discovery connections, " | ||||
| 669 | "duplicate discovery information", | ||||
| 670 | [NVMF_DISC_EFLAGS_NCC] = "no cdc connectivity", | ||||
| 671 | [NVMF_DISC_EFLAGS_EPCSD | | ||||
| 672 | NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, " | ||||
| 673 | "no cdc connectivity", | ||||
| 674 | [NVMF_DISC_EFLAGS_DUPRETINFO | | ||||
| 675 | NVMF_DISC_EFLAGS_NCC] = "duplicate discovery information, " | ||||
| 676 | "no cdc connectivity", | ||||
| 677 | [NVMF_DISC_EFLAGS_EPCSD | | ||||
| 678 | NVMF_DISC_EFLAGS_DUPRETINFO | | ||||
| 679 | NVMF_DISC_EFLAGS_NCC] = "explicit discovery connections, " | ||||
| 680 | "duplicate discovery information, " | ||||
| 681 | "no cdc connectivity", | ||||
| 682 | }; | ||||
| 683 | |||||
| 684 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_eflags_str(__u16 eflags) | ||||
| 685 | { | ||||
| 686 | return arg_str(eflags_strings, ARRAY_SIZE(eflags_strings)(sizeof(eflags_strings) / sizeof((eflags_strings)[0]) + 0), eflags); | ||||
| 687 | } | ||||
| 688 | |||||
| 689 | static const char * const sectypes[] = { | ||||
| 690 | [NVMF_TCP_SECTYPE_NONE] = "none", | ||||
| 691 | [NVMF_TCP_SECTYPE_TLS] = "tls", | ||||
| 692 | [NVMF_TCP_SECTYPE_TLS13] = "tls13", | ||||
| 693 | }; | ||||
| 694 | |||||
| 695 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_sectype_str(__u8 sectype) | ||||
| 696 | { | ||||
| 697 | return arg_str(sectypes, ARRAY_SIZE(sectypes)(sizeof(sectypes) / sizeof((sectypes)[0]) + 0), sectype); | ||||
| 698 | } | ||||
| 699 | |||||
| 700 | static const char * const prtypes[] = { | ||||
| 701 | [NVMF_RDMA_PRTYPE_NOT_SPECIFIED] = "not specified", | ||||
| 702 | [NVMF_RDMA_PRTYPE_IB] = "infiniband", | ||||
| 703 | [NVMF_RDMA_PRTYPE_ROCE] = "roce", | ||||
| 704 | [NVMF_RDMA_PRTYPE_ROCEV2] = "roce-v2", | ||||
| 705 | [NVMF_RDMA_PRTYPE_IWARP] = "iwarp", | ||||
| 706 | }; | ||||
| 707 | |||||
| 708 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_prtype_str(__u8 prtype) | ||||
| 709 | { | ||||
| 710 | return arg_str(prtypes, ARRAY_SIZE(prtypes)(sizeof(prtypes) / sizeof((prtypes)[0]) + 0), prtype); | ||||
| 711 | } | ||||
| 712 | |||||
| 713 | static const char * const qptypes[] = { | ||||
| 714 | [NVMF_RDMA_QPTYPE_CONNECTED] = "connected", | ||||
| 715 | [NVMF_RDMA_QPTYPE_DATAGRAM] = "datagram", | ||||
| 716 | }; | ||||
| 717 | |||||
| 718 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_qptype_str(__u8 qptype) | ||||
| 719 | { | ||||
| 720 | return arg_str(qptypes, ARRAY_SIZE(qptypes)(sizeof(qptypes) / sizeof((qptypes)[0]) + 0), qptype); | ||||
| 721 | } | ||||
| 722 | |||||
| 723 | static const char * const cms[] = { | ||||
| 724 | [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm", | ||||
| 725 | }; | ||||
| 726 | |||||
| 727 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_cms_str(__u8 cm) | ||||
| 728 | { | ||||
| 729 | return arg_str(cms, ARRAY_SIZE(cms)(sizeof(cms) / sizeof((cms)[0]) + 0), cm); | ||||
| 730 | } | ||||
| 731 | |||||
| 732 | void libnvmf_default_config(struct libnvme_fabrics_config *cfg) | ||||
| 733 | { | ||||
| 734 | cfg->tos = -1; | ||||
| 735 | cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO600; | ||||
| 736 | } | ||||
| 737 | |||||
| 738 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_create(struct libnvme_global_ctx *ctx, | ||||
| 739 | bool_Bool (*decide_retry)(struct libnvmf_context *fctx, int err, | ||||
| 740 | void *user_data), | ||||
| 741 | void (*connected)(struct libnvmf_context *fctx, | ||||
| 742 | struct libnvme_ctrl *c, void *user_data), | ||||
| 743 | void (*already_connected)(struct libnvmf_context *fctx, | ||||
| 744 | struct libnvme_host *host, const char *subsysnqn, | ||||
| 745 | const char *transport, const char *traddr, | ||||
| 746 | const char *trsvcid, void *user_data), | ||||
| 747 | void *user_data, struct libnvmf_context **fctxp) | ||||
| 748 | { | ||||
| 749 | struct libnvmf_context *fctx; | ||||
| 750 | |||||
| 751 | fctx = calloc(1, sizeof(*fctx)); | ||||
| 752 | if (!fctx) | ||||
| 753 | return -ENOMEM12; | ||||
| 754 | |||||
| 755 | fctx->ctx = ctx; | ||||
| 756 | |||||
| 757 | libnvmf_default_config(&fctx->ctrl_params.cfg); | ||||
| 758 | |||||
| 759 | fctx->hooks.decide_retry = decide_retry; | ||||
| 760 | fctx->hooks.connected = connected; | ||||
| 761 | fctx->hooks.already_connected = already_connected; | ||||
| 762 | |||||
| 763 | fctx->hooks.user_data = user_data; | ||||
| 764 | |||||
| 765 | *fctxp = fctx; | ||||
| 766 | return 0; | ||||
| 767 | } | ||||
| 768 | |||||
| 769 | __shr_public__attribute__((visibility("default"))) void libnvmf_context_free(struct libnvmf_context *fctx) | ||||
| 770 | { | ||||
| 771 | if (!fctx) | ||||
| 772 | return; | ||||
| 773 | |||||
| 774 | free(fctx->nbft_path); | ||||
| 775 | free(fctx->hostnqn); | ||||
| 776 | free(fctx->hostid); | ||||
| 777 | free(fctx->tls_key); | ||||
| 778 | free(fctx); | ||||
| 779 | } | ||||
| 780 | |||||
| 781 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_discovery_hooks( | ||||
| 782 | struct libnvmf_context *fctx, | ||||
| 783 | void (*discovery_log)(struct libnvmf_context *fctx, | ||||
| 784 | const struct nvmf_discovery_log *log, | ||||
| 785 | uint64_t numrec, void *user_data)) | ||||
| 786 | { | ||||
| 787 | fctx->hooks.discovery_log = discovery_log; | ||||
| 788 | |||||
| 789 | return 0; | ||||
| 790 | } | ||||
| 791 | |||||
| 792 | |||||
| 793 | |||||
| 794 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_connection( | ||||
| 795 | struct libnvmf_context *fctx, const char *subsysnqn, | ||||
| 796 | const char *transport, const char *traddr, const char *trsvcid, | ||||
| 797 | const char *host_traddr, const char *host_iface) | ||||
| 798 | { | ||||
| 799 | fctx->ctrl_params.subsysnqn = subsysnqn; | ||||
| 800 | fctx->ctrl_params.transport = transport; | ||||
| 801 | fctx->ctrl_params.traddr = traddr; | ||||
| 802 | fctx->ctrl_params.trsvcid = trsvcid; | ||||
| 803 | fctx->ctrl_params.host_traddr = host_traddr; | ||||
| 804 | fctx->ctrl_params.host_iface = host_iface; | ||||
| 805 | |||||
| 806 | return 0; | ||||
| 807 | } | ||||
| 808 | |||||
| 809 | static const char *hostid_from_hostnqn(const char *hostnqn) | ||||
| 810 | { | ||||
| 811 | const char *match; | ||||
| 812 | |||||
| 813 | if (!hostnqn) | ||||
| 814 | return NULL((void*)0); | ||||
| 815 | |||||
| 816 | match = strstr(hostnqn, "uuid:")_Generic (0 ? (hostnqn) : (void *) 1, const void *: (const char *) (strstr (hostnqn, "uuid:")), default: strstr (hostnqn, "uuid:" )); | ||||
| 817 | if (!match) | ||||
| 818 | return NULL((void*)0); | ||||
| 819 | |||||
| 820 | return match + strlen("uuid:"); | ||||
| 821 | } | ||||
| 822 | |||||
| 823 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_hostnqn(struct libnvmf_context *fctx, | ||||
| 824 | const char *hostnqn, const char *hostid) | ||||
| 825 | { | ||||
| 826 | char *hnqn; | ||||
| 827 | char *hid; | ||||
| 828 | |||||
| 829 | if (!hostnqn) | ||||
| 830 | return -EINVAL22; | ||||
| 831 | |||||
| 832 | hnqn = strdup(hostnqn); | ||||
| 833 | if (!hnqn) | ||||
| 834 | return -ENOMEM12; | ||||
| 835 | |||||
| 836 | if (!hostid) | ||||
| 837 | hostid = hostid_from_hostnqn(hostnqn); | ||||
| 838 | |||||
| 839 | if (!hostid) { | ||||
| 840 | free(hnqn); | ||||
| 841 | return -EINVAL22; | ||||
| 842 | } | ||||
| 843 | |||||
| 844 | hid = strdup(hostid); | ||||
| 845 | if (!hid) { | ||||
| 846 | free(hnqn); | ||||
| 847 | return -ENOMEM12; | ||||
| 848 | } | ||||
| 849 | |||||
| 850 | free(fctx->hostnqn); | ||||
| 851 | free(fctx->hostid); | ||||
| 852 | fctx->hostnqn = hnqn; | ||||
| 853 | fctx->hostid = hid; | ||||
| 854 | |||||
| 855 | return 0; | ||||
| 856 | } | ||||
| 857 | |||||
| 858 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_connection_from_tid( | ||||
| 859 | struct libnvmf_context *fctx, const struct libnvmf_tid *tid) | ||||
| 860 | { | ||||
| 861 | if (!fctx || !tid) | ||||
| 862 | return -EINVAL22; | ||||
| 863 | |||||
| 864 | fctx->ctrl_params.subsysnqn = tid->subsysnqn; | ||||
| 865 | fctx->ctrl_params.transport = tid->transport; | ||||
| 866 | fctx->ctrl_params.traddr = tid->traddr; | ||||
| 867 | fctx->ctrl_params.trsvcid = tid->trsvcid; | ||||
| 868 | fctx->ctrl_params.host_traddr = tid->host_traddr; | ||||
| 869 | fctx->ctrl_params.host_iface = tid->host_iface; | ||||
| 870 | |||||
| 871 | return libnvmf_context_set_hostnqn(fctx, tid->hostnqn, tid->hostid); | ||||
| 872 | } | ||||
| 873 | |||||
| 874 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_crypto(struct libnvmf_context *fctx, | ||||
| 875 | const char *hostkey, const char *ctrlkey, | ||||
| 876 | const char *keyring, const char *tls_key, | ||||
| 877 | const char *tls_key_identity) | ||||
| 878 | { | ||||
| 879 | int err; | ||||
| 880 | |||||
| 881 | fctx->hostkey = hostkey; | ||||
| 882 | fctx->ctrlkey = ctrlkey; | ||||
| 883 | fctx->keyring = keyring; | ||||
| 884 | fctx->tls_key_identity = tls_key_identity; | ||||
| 885 | |||||
| 886 | if (!tls_key) | ||||
| 887 | return 0; | ||||
| 888 | |||||
| 889 | if (!strncmp(tls_key, "pin:", 4)) { | ||||
| 890 | __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *raw_secret = NULL((void*)0); | ||||
| 891 | __cleanup_free__attribute__((cleanup(shr_freep))) char *encoded_key = NULL((void*)0); | ||||
| 892 | int key_len = 32; | ||||
| 893 | |||||
| 894 | err = libnvmf_create_raw_secret(fctx->ctx, tls_key, | ||||
| 895 | key_len, &raw_secret); | ||||
| 896 | if (err) | ||||
| 897 | return err; | ||||
| 898 | |||||
| 899 | err = libnvmf_export_tls_key(fctx->ctx, raw_secret, | ||||
| 900 | key_len, &encoded_key); | ||||
| 901 | if (err) | ||||
| 902 | return err; | ||||
| 903 | |||||
| 904 | fctx->tls_key = encoded_key; | ||||
| 905 | encoded_key = NULL((void*)0); | ||||
| 906 | return 0; | ||||
| 907 | } | ||||
| 908 | |||||
| 909 | free(fctx->tls_key); | ||||
| 910 | fctx->tls_key = strdup(tls_key); | ||||
| 911 | return 0; | ||||
| 912 | } | ||||
| 913 | |||||
| 914 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_device( | ||||
| 915 | struct libnvmf_context *fctx, const char *device) | ||||
| 916 | { | ||||
| 917 | fctx->device = device; | ||||
| 918 | |||||
| 919 | return 0; | ||||
| 920 | } | ||||
| 921 | |||||
| 922 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_devid_file( | ||||
| 923 | struct libnvmf_context *fctx, const char *devid_file) | ||||
| 924 | { | ||||
| 925 | fctx->devid_file = devid_file; | ||||
| 926 | |||||
| 927 | return 0; | ||||
| 928 | } | ||||
| 929 | |||||
| 930 | /* | ||||
| 931 | * O_NOFOLLOW guards the one hazard the caller can't see: a symlink at the | ||||
| 932 | * final path component. O_TRUNC is deliberately absent -- a name recorded | ||||
| 933 | * by an earlier successful connect must survive a failed one. | ||||
| 934 | * | ||||
| 935 | * Return: an open fd, or -errno. | ||||
| 936 | */ | ||||
| 937 | static int open_devid_file(struct libnvmf_context *fctx) | ||||
| 938 | { | ||||
| 939 | int fd; | ||||
| 940 | |||||
| 941 | fd = open(fctx->devid_file, | ||||
| 942 | O_WRONLY01 | O_CREAT0100 | O_CLOEXEC02000000 | O_NOFOLLOW0400000, 0644); | ||||
| 943 | if (fd < 0) { | ||||
| 944 | libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR,__libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open devid-file %s: %s\n" , fctx->devid_file, libnvme_strerror((*__errno_location () ))) | ||||
| 945 | "failed to open devid-file %s: %s\n",__libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open devid-file %s: %s\n" , fctx->devid_file, libnvme_strerror((*__errno_location () ))) | ||||
| 946 | fctx->devid_file, libnvme_strerror(errno))__libnvme_msg(fctx->ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open devid-file %s: %s\n" , fctx->devid_file, libnvme_strerror((*__errno_location () ))); | ||||
| 947 | return -errno(*__errno_location ()); | ||||
| 948 | } | ||||
| 949 | |||||
| 950 | return fd; | ||||
| 951 | } | ||||
| 952 | |||||
| 953 | static void write_devid_file(struct libnvmf_context *fctx, int fd, | ||||
| 954 | struct libnvme_ctrl *c) | ||||
| 955 | { | ||||
| 956 | if (fd < 0 || !c) | ||||
| 957 | return; | ||||
| 958 | |||||
| 959 | if (ftruncate(fd, 0) < 0 || | ||||
| 960 | dprintf(fd, "%s\n", libnvme_ctrl_get_name(c)) < 0) | ||||
| 961 | libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN,__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to write devid-file %s: %s\n" , fctx->devid_file, libnvme_strerror((*__errno_location () ))) | ||||
| 962 | "failed to write devid-file %s: %s\n",__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to write devid-file %s: %s\n" , fctx->devid_file, libnvme_strerror((*__errno_location () ))) | ||||
| 963 | fctx->devid_file, libnvme_strerror(errno))__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "failed to write devid-file %s: %s\n" , fctx->devid_file, libnvme_strerror((*__errno_location () ))); | ||||
| 964 | } | ||||
| 965 | |||||
| 966 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_io_queues( | ||||
| 967 | struct libnvmf_context *fctx, int nr_io_queues, | ||||
| 968 | int nr_write_queues, int nr_poll_queues, | ||||
| 969 | int queue_size, bool_Bool disable_sqflow) | ||||
| 970 | { | ||||
| 971 | fctx->ctrl_params.cfg.nr_io_queues = nr_io_queues; | ||||
| 972 | fctx->ctrl_params.cfg.nr_write_queues = nr_write_queues; | ||||
| 973 | fctx->ctrl_params.cfg.nr_poll_queues = nr_poll_queues; | ||||
| 974 | fctx->ctrl_params.cfg.queue_size = queue_size; | ||||
| 975 | fctx->ctrl_params.cfg.disable_sqflow = disable_sqflow; | ||||
| 976 | |||||
| 977 | return 0; | ||||
| 978 | } | ||||
| 979 | |||||
| 980 | __shr_public__attribute__((visibility("default"))) int libnvmf_context_set_reconnect_policy( | ||||
| 981 | struct libnvmf_context *fctx, int ctrl_loss_tmo, | ||||
| 982 | int reconnect_delay, int fast_io_fail_tmo) | ||||
| 983 | { | ||||
| 984 | fctx->ctrl_params.cfg.ctrl_loss_tmo = ctrl_loss_tmo; | ||||
| 985 | fctx->ctrl_params.cfg.reconnect_delay = reconnect_delay; | ||||
| 986 | fctx->ctrl_params.cfg.fast_io_fail_tmo = fast_io_fail_tmo; | ||||
| 987 | |||||
| 988 | return 0; | ||||
| 989 | } | ||||
| 990 | |||||
| 991 | /* | ||||
| 992 | * Derived from Linux's supported options (the opt_tokens table) | ||||
| 993 | * when the mechanism to report supported options was added (f18ee3d988157). | ||||
| 994 | * Not all of these options may actually be supported, | ||||
| 995 | * but we retain the old behavior of passing all that might be. | ||||
| 996 | */ | ||||
| 997 | static const struct libnvme_fabric_options default_supported_options = { | ||||
| 998 | .ctrl_loss_tmo = true1, | ||||
| 999 | .data_digest = true1, | ||||
| 1000 | .disable_sqflow = true1, | ||||
| 1001 | .discovery = true1, | ||||
| 1002 | .duplicate_connect = true1, | ||||
| 1003 | .fast_io_fail_tmo = true1, | ||||
| 1004 | .hdr_digest = true1, | ||||
| 1005 | .host_iface = true1, | ||||
| 1006 | .host_traddr = true1, | ||||
| 1007 | .hostid = true1, | ||||
| 1008 | .hostnqn = true1, | ||||
| 1009 | .keep_alive_tmo = true1, | ||||
| 1010 | .nqn = true1, | ||||
| 1011 | .nr_io_queues = true1, | ||||
| 1012 | .nr_poll_queues = true1, | ||||
| 1013 | .nr_write_queues = true1, | ||||
| 1014 | .queue_size = true1, | ||||
| 1015 | .reconnect_delay = true1, | ||||
| 1016 | .tos = true1, | ||||
| 1017 | .traddr = true1, | ||||
| 1018 | .transport = true1, | ||||
| 1019 | .trsvcid = true1, | ||||
| 1020 | }; | ||||
| 1021 | |||||
| 1022 | #define MERGE_CFG_OPTION(c, n, o, d)if ((c)->o == d) (c)->o = (n)->o \ | ||||
| 1023 | if ((c)->o == d) (c)->o = (n)->o | ||||
| 1024 | static void merge_config(struct libnvme_ctrl *c, | ||||
| 1025 | const struct libnvme_fabrics_config *cfg) | ||||
| 1026 | { | ||||
| 1027 | MERGE_CFG_OPTION(&c->cfg, cfg, nr_io_queues, 0)if ((&c->cfg)->nr_io_queues == 0) (&c->cfg)-> nr_io_queues = (cfg)->nr_io_queues; | ||||
| 1028 | MERGE_CFG_OPTION(&c->cfg, cfg, nr_write_queues, 0)if ((&c->cfg)->nr_write_queues == 0) (&c->cfg )->nr_write_queues = (cfg)->nr_write_queues; | ||||
| 1029 | MERGE_CFG_OPTION(&c->cfg, cfg, nr_poll_queues, 0)if ((&c->cfg)->nr_poll_queues == 0) (&c->cfg )->nr_poll_queues = (cfg)->nr_poll_queues; | ||||
| 1030 | MERGE_CFG_OPTION(&c->cfg, cfg, queue_size, 0)if ((&c->cfg)->queue_size == 0) (&c->cfg)-> queue_size = (cfg)->queue_size; | ||||
| 1031 | MERGE_CFG_OPTION(&c->cfg, cfg, keep_alive_tmo, 0)if ((&c->cfg)->keep_alive_tmo == 0) (&c->cfg )->keep_alive_tmo = (cfg)->keep_alive_tmo; | ||||
| 1032 | MERGE_CFG_OPTION(&c->cfg, cfg, reconnect_delay, 0)if ((&c->cfg)->reconnect_delay == 0) (&c->cfg )->reconnect_delay = (cfg)->reconnect_delay; | ||||
| 1033 | MERGE_CFG_OPTION(&c->cfg, cfg, ctrl_loss_tmo,if ((&c->cfg)->ctrl_loss_tmo == 600) (&c->cfg )->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo | ||||
| 1034 | NVMF_DEF_CTRL_LOSS_TMO)if ((&c->cfg)->ctrl_loss_tmo == 600) (&c->cfg )->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo; | ||||
| 1035 | MERGE_CFG_OPTION(&c->cfg, cfg, fast_io_fail_tmo, 0)if ((&c->cfg)->fast_io_fail_tmo == 0) (&c->cfg )->fast_io_fail_tmo = (cfg)->fast_io_fail_tmo; | ||||
| 1036 | MERGE_CFG_OPTION(&c->cfg, cfg, tos, -1)if ((&c->cfg)->tos == -1) (&c->cfg)->tos = (cfg)->tos; | ||||
| 1037 | MERGE_CFG_OPTION(&c->cfg, cfg, keyring_id, 0)if ((&c->cfg)->keyring_id == 0) (&c->cfg)-> keyring_id = (cfg)->keyring_id; | ||||
| 1038 | MERGE_CFG_OPTION(&c->cfg, cfg, tls_key_id, 0)if ((&c->cfg)->tls_key_id == 0) (&c->cfg)-> tls_key_id = (cfg)->tls_key_id; | ||||
| 1039 | MERGE_CFG_OPTION(&c->cfg, cfg, tls_configured_key_id, 0)if ((&c->cfg)->tls_configured_key_id == 0) (&c-> cfg)->tls_configured_key_id = (cfg)->tls_configured_key_id; | ||||
| 1040 | MERGE_CFG_OPTION(&c->cfg, cfg, duplicate_connect, false)if ((&c->cfg)->duplicate_connect == 0) (&c-> cfg)->duplicate_connect = (cfg)->duplicate_connect; | ||||
| 1041 | MERGE_CFG_OPTION(&c->cfg, cfg, disable_sqflow, false)if ((&c->cfg)->disable_sqflow == 0) (&c->cfg )->disable_sqflow = (cfg)->disable_sqflow; | ||||
| 1042 | MERGE_CFG_OPTION(&c->cfg, cfg, hdr_digest, false)if ((&c->cfg)->hdr_digest == 0) (&c->cfg)-> hdr_digest = (cfg)->hdr_digest; | ||||
| 1043 | MERGE_CFG_OPTION(&c->cfg, cfg, data_digest, false)if ((&c->cfg)->data_digest == 0) (&c->cfg)-> data_digest = (cfg)->data_digest; | ||||
| 1044 | MERGE_CFG_OPTION(&c->cfg, cfg, tls, false)if ((&c->cfg)->tls == 0) (&c->cfg)->tls = (cfg)->tls; | ||||
| 1045 | MERGE_CFG_OPTION(&c->cfg, cfg, concat, false)if ((&c->cfg)->concat == 0) (&c->cfg)->concat = (cfg)->concat; | ||||
| 1046 | } | ||||
| 1047 | |||||
| 1048 | #define UPDATE_CFG_OPTION(c, n, o, d)if ((n)->o != d) (c)->o = (n)->o \ | ||||
| 1049 | if ((n)->o != d) (c)->o = (n)->o | ||||
| 1050 | static void update_config(struct libnvme_ctrl *c, | ||||
| 1051 | const struct libnvme_fabrics_config *cfg) | ||||
| 1052 | { | ||||
| 1053 | UPDATE_CFG_OPTION(&c->cfg, cfg, nr_io_queues, 0)if ((cfg)->nr_io_queues != 0) (&c->cfg)->nr_io_queues = (cfg)->nr_io_queues; | ||||
| 1054 | UPDATE_CFG_OPTION(&c->cfg, cfg, nr_write_queues, 0)if ((cfg)->nr_write_queues != 0) (&c->cfg)->nr_write_queues = (cfg)->nr_write_queues; | ||||
| 1055 | UPDATE_CFG_OPTION(&c->cfg, cfg, nr_poll_queues, 0)if ((cfg)->nr_poll_queues != 0) (&c->cfg)->nr_poll_queues = (cfg)->nr_poll_queues; | ||||
| 1056 | UPDATE_CFG_OPTION(&c->cfg, cfg, queue_size, 0)if ((cfg)->queue_size != 0) (&c->cfg)->queue_size = (cfg)->queue_size; | ||||
| 1057 | UPDATE_CFG_OPTION(&c->cfg, cfg, keep_alive_tmo, 0)if ((cfg)->keep_alive_tmo != 0) (&c->cfg)->keep_alive_tmo = (cfg)->keep_alive_tmo; | ||||
| 1058 | UPDATE_CFG_OPTION(&c->cfg, cfg, reconnect_delay, 0)if ((cfg)->reconnect_delay != 0) (&c->cfg)->reconnect_delay = (cfg)->reconnect_delay; | ||||
| 1059 | UPDATE_CFG_OPTION(&c->cfg, cfg, ctrl_loss_tmo,if ((cfg)->ctrl_loss_tmo != 600) (&c->cfg)->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo | ||||
| 1060 | NVMF_DEF_CTRL_LOSS_TMO)if ((cfg)->ctrl_loss_tmo != 600) (&c->cfg)->ctrl_loss_tmo = (cfg)->ctrl_loss_tmo; | ||||
| 1061 | UPDATE_CFG_OPTION(&c->cfg, cfg, fast_io_fail_tmo, 0)if ((cfg)->fast_io_fail_tmo != 0) (&c->cfg)->fast_io_fail_tmo = (cfg)->fast_io_fail_tmo; | ||||
| 1062 | UPDATE_CFG_OPTION(&c->cfg, cfg, tos, -1)if ((cfg)->tos != -1) (&c->cfg)->tos = (cfg)-> tos; | ||||
| 1063 | UPDATE_CFG_OPTION(&c->cfg, cfg, keyring_id, 0)if ((cfg)->keyring_id != 0) (&c->cfg)->keyring_id = (cfg)->keyring_id; | ||||
| 1064 | UPDATE_CFG_OPTION(&c->cfg, cfg, tls_key_id, 0)if ((cfg)->tls_key_id != 0) (&c->cfg)->tls_key_id = (cfg)->tls_key_id; | ||||
| 1065 | UPDATE_CFG_OPTION(&c->cfg, cfg, tls_configured_key_id, 0)if ((cfg)->tls_configured_key_id != 0) (&c->cfg)-> tls_configured_key_id = (cfg)->tls_configured_key_id; | ||||
| 1066 | UPDATE_CFG_OPTION(&c->cfg, cfg, duplicate_connect, false)if ((cfg)->duplicate_connect != 0) (&c->cfg)->duplicate_connect = (cfg)->duplicate_connect; | ||||
| 1067 | UPDATE_CFG_OPTION(&c->cfg, cfg, disable_sqflow, false)if ((cfg)->disable_sqflow != 0) (&c->cfg)->disable_sqflow = (cfg)->disable_sqflow; | ||||
| 1068 | UPDATE_CFG_OPTION(&c->cfg, cfg, hdr_digest, false)if ((cfg)->hdr_digest != 0) (&c->cfg)->hdr_digest = (cfg)->hdr_digest; | ||||
| 1069 | UPDATE_CFG_OPTION(&c->cfg, cfg, data_digest, false)if ((cfg)->data_digest != 0) (&c->cfg)->data_digest = (cfg)->data_digest; | ||||
| 1070 | UPDATE_CFG_OPTION(&c->cfg, cfg, tls, false)if ((cfg)->tls != 0) (&c->cfg)->tls = (cfg)-> tls; | ||||
| 1071 | UPDATE_CFG_OPTION(&c->cfg, cfg, concat, false)if ((cfg)->concat != 0) (&c->cfg)->concat = (cfg )->concat; | ||||
| 1072 | } | ||||
| 1073 | |||||
| 1074 | static int __add_bool_argument(char **argstr, char *tok, bool_Bool arg) | ||||
| 1075 | { | ||||
| 1076 | char *nstr; | ||||
| 1077 | |||||
| 1078 | if (!arg) | ||||
| 1079 | return 0; | ||||
| 1080 | if (asprintf(&nstr, "%s,%s", *argstr, tok) < 0) { | ||||
| 1081 | return -ENOMEM12; | ||||
| 1082 | } | ||||
| 1083 | free(*argstr); | ||||
| 1084 | *argstr = nstr; | ||||
| 1085 | |||||
| 1086 | return 0; | ||||
| 1087 | } | ||||
| 1088 | |||||
| 1089 | static int __add_hex_argument(char **argstr, char *tok, int arg, bool_Bool allow_zero) | ||||
| 1090 | { | ||||
| 1091 | char *nstr; | ||||
| 1092 | |||||
| 1093 | if (arg < 0 || (!arg && !allow_zero)) | ||||
| 1094 | return 0; | ||||
| 1095 | if (asprintf(&nstr, "%s,%s=0x%08x", *argstr, tok, arg) < 0) { | ||||
| 1096 | return -ENOMEM12; | ||||
| 1097 | } | ||||
| 1098 | free(*argstr); | ||||
| 1099 | *argstr = nstr; | ||||
| 1100 | |||||
| 1101 | return 0; | ||||
| 1102 | } | ||||
| 1103 | |||||
| 1104 | static int __add_int_argument(char **argstr, char *tok, int arg, bool_Bool allow_zero) | ||||
| 1105 | { | ||||
| 1106 | char *nstr; | ||||
| 1107 | |||||
| 1108 | if (arg < 0 || (!arg && !allow_zero)) | ||||
| 1109 | return 0; | ||||
| 1110 | if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) { | ||||
| 1111 | return -ENOMEM12; | ||||
| 1112 | } | ||||
| 1113 | free(*argstr); | ||||
| 1114 | *argstr = nstr; | ||||
| 1115 | |||||
| 1116 | return 0; | ||||
| 1117 | } | ||||
| 1118 | |||||
| 1119 | static int __add_int_or_minus_one_argument(char **argstr, char *tok, int arg) | ||||
| 1120 | { | ||||
| 1121 | char *nstr; | ||||
| 1122 | |||||
| 1123 | if (arg < -1) | ||||
| 1124 | return 0; | ||||
| 1125 | if (asprintf(&nstr, "%s,%s=%d", *argstr, tok, arg) < 0) { | ||||
| 1126 | return -ENOMEM12; | ||||
| 1127 | } | ||||
| 1128 | free(*argstr); | ||||
| 1129 | *argstr = nstr; | ||||
| 1130 | |||||
| 1131 | return 0; | ||||
| 1132 | } | ||||
| 1133 | |||||
| 1134 | static int __add_argument(char **argstr, const char *tok, const char *arg) | ||||
| 1135 | { | ||||
| 1136 | char *nstr; | ||||
| 1137 | |||||
| 1138 | if (!arg || arg[0] == '\0' || !strcmp(arg, "none")) | ||||
| 1139 | return 0; | ||||
| 1140 | if (asprintf(&nstr, "%s,%s=%s", *argstr, tok, arg) < 0) { | ||||
| 1141 | return -ENOMEM12; | ||||
| 1142 | } | ||||
| 1143 | free(*argstr); | ||||
| 1144 | *argstr = nstr; | ||||
| 1145 | |||||
| 1146 | return 0; | ||||
| 1147 | } | ||||
| 1148 | |||||
| 1149 | static int __nvmf_supported_options(struct libnvme_global_ctx *ctx); | ||||
| 1150 | #define nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options-> tok; }) \ | ||||
| 1151 | ({ \ | ||||
| 1152 | !__nvmf_supported_options(ctx) && ctx->options->tok; \ | ||||
| 1153 | }) | ||||
| 1154 | |||||
| 1155 | #define add_bool_argument(ctx, argstr, tok, arg)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tok; })) { ret = __add_bool_argument(argstr, "tok", arg); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ( (void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret; }) \ | ||||
| 1156 | ({ \ | ||||
| 1157 | int ret; \ | ||||
| 1158 | if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options-> tok; })) { \ | ||||
| 1159 | ret = __add_bool_argument(argstr, \ | ||||
| 1160 | stringify(tok)"tok", \ | ||||
| 1161 | arg); \ | ||||
| 1162 | } else { \ | ||||
| 1163 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1164 | "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1165 | stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok"); \ | ||||
| 1166 | ret = 0; \ | ||||
| 1167 | } \ | ||||
| 1168 | ret; \ | ||||
| 1169 | }) | ||||
| 1170 | |||||
| 1171 | #define add_hex_argument(ctx, argstr, tok, arg, allow_zero)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tok; })) { ret = __add_hex_argument(argstr, "tok" , arg, allow_zero); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret ; }) \ | ||||
| 1172 | ({ \ | ||||
| 1173 | int ret; \ | ||||
| 1174 | if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options-> tok; })) { \ | ||||
| 1175 | ret = __add_hex_argument(argstr, \ | ||||
| 1176 | stringify(tok)"tok", \ | ||||
| 1177 | arg, \ | ||||
| 1178 | allow_zero); \ | ||||
| 1179 | } else { \ | ||||
| 1180 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1181 | "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1182 | stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok"); \ | ||||
| 1183 | ret = 0; \ | ||||
| 1184 | } \ | ||||
| 1185 | ret; \ | ||||
| 1186 | }) | ||||
| 1187 | |||||
| 1188 | #define add_int_argument(ctx, argstr, tok, arg, allow_zero)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tok; })) { ret = __add_int_argument(argstr, "tok" , arg, allow_zero); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret ; }) \ | ||||
| 1189 | ({ \ | ||||
| 1190 | int ret; \ | ||||
| 1191 | if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options-> tok; })) { \ | ||||
| 1192 | ret = __add_int_argument(argstr, \ | ||||
| 1193 | stringify(tok)"tok", \ | ||||
| 1194 | arg, \ | ||||
| 1195 | allow_zero); \ | ||||
| 1196 | } else { \ | ||||
| 1197 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1198 | "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1199 | stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok"); \ | ||||
| 1200 | ret = 0; \ | ||||
| 1201 | } \ | ||||
| 1202 | ret; \ | ||||
| 1203 | }) | ||||
| 1204 | |||||
| 1205 | #define add_int_or_minus_one_argument(ctx, argstr, tok, arg)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tok; })) { ret = __add_int_or_minus_one_argument (argstr, "tok", arg); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret ; }) \ | ||||
| 1206 | ({ \ | ||||
| 1207 | int ret; \ | ||||
| 1208 | if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options-> tok; })) { \ | ||||
| 1209 | ret = __add_int_or_minus_one_argument(argstr, \ | ||||
| 1210 | stringify(tok)"tok", \ | ||||
| 1211 | arg); \ | ||||
| 1212 | } else { \ | ||||
| 1213 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1214 | "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1215 | stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "tok"); \ | ||||
| 1216 | ret = 0; \ | ||||
| 1217 | } \ | ||||
| 1218 | ret; \ | ||||
| 1219 | }) | ||||
| 1220 | |||||
| 1221 | #define add_argument(ctx, argstr, tok, arg)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tok; })) { ret = __add_argument(argstr, "tok" , arg); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void* )0), "option \"%s\" ignored\n", "tok"); ret = 0; } ret; }) \ | ||||
| 1222 | ({ \ | ||||
| 1223 | int ret; \ | ||||
| 1224 | if (nvmf_check_option(ctx, tok)({ !__nvmf_supported_options(ctx) && ctx->options-> tok; })) { \ | ||||
| 1225 | ret = __add_argument(argstr, \ | ||||
| 1226 | stringify(tok)"tok", \ | ||||
| 1227 | arg); \ | ||||
| 1228 | } else { \ | ||||
| 1229 | libnvme_msg(ctx, LIBNVME_LOG_WARN, \__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1230 | "option \"%s\" ignored\n", \__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "tok") | ||||
| 1231 | stringify(tok))__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "tok"); \ | ||||
| 1232 | ret = 0; \ | ||||
| 1233 | } \ | ||||
| 1234 | ret; \ | ||||
| 1235 | }) | ||||
| 1236 | |||||
| 1237 | static int inet4_pton(const char *src, uint16_t port, | ||||
| 1238 | struct sockaddr_storage *addr) | ||||
| 1239 | { | ||||
| 1240 | struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; | ||||
| 1241 | |||||
| 1242 | if (strlen(src) > INET_ADDRSTRLEN16) | ||||
| 1243 | return -EINVAL22; | ||||
| 1244 | |||||
| 1245 | if (inet_pton(AF_INET2, src, &addr4->sin_addr.s_addr) <= 0) | ||||
| 1246 | return -EINVAL22; | ||||
| 1247 | |||||
| 1248 | addr4->sin_family = AF_INET2; | ||||
| 1249 | addr4->sin_port = htons(port)__bswap_16 (port); | ||||
| 1250 | |||||
| 1251 | return 0; | ||||
| 1252 | } | ||||
| 1253 | |||||
| 1254 | static int inet6_pton(struct libnvme_global_ctx *ctx, const char *src, uint16_t port, | ||||
| 1255 | struct sockaddr_storage *addr) | ||||
| 1256 | { | ||||
| 1257 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; | ||||
| 1258 | const char *scope = NULL((void*)0); | ||||
| 1259 | char *p; | ||||
| 1260 | |||||
| 1261 | if (strlen(src) > INET6_ADDRSTRLEN46) | ||||
| 1262 | return -EINVAL22; | ||||
| 1263 | |||||
| 1264 | __cleanup_free__attribute__((cleanup(shr_freep))) char *tmp = strdup(src); | ||||
| 1265 | if (!tmp) { | ||||
| 1266 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "cannot copy: %s\n", src)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "cannot copy: %s\n" , src); | ||||
| 1267 | return -ENOMEM12; | ||||
| 1268 | } | ||||
| 1269 | |||||
| 1270 | p = strchr(tmp, '%')_Generic (0 ? (tmp) : (void *) 1, const void *: (const char * ) (strchr (tmp, '%')), default: strchr (tmp, '%')); | ||||
| 1271 | if (p) { | ||||
| 1272 | *p = '\0'; | ||||
| 1273 | scope = src + (p - tmp) + 1; | ||||
| 1274 | } | ||||
| 1275 | |||||
| 1276 | if (inet_pton(AF_INET610, tmp, &addr6->sin6_addr) != 1) | ||||
| 1277 | return -EINVAL22; | ||||
| 1278 | |||||
| 1279 | if (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)(__extension__ ({ const struct in6_addr *__a = (const struct in6_addr *) (&addr6->sin6_addr); (__a->__in6_u.__u6_addr32[ 0] & __bswap_32 (0xffc00000)) == __bswap_32 (0xfe800000); })) && scope) { | ||||
| 1280 | addr6->sin6_scope_id = if_nametoindex(scope); | ||||
| 1281 | if (addr6->sin6_scope_id == 0) { | ||||
| 1282 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "can't find iface index for: %s (%m)\n" , scope) | ||||
| 1283 | "can't find iface index for: %s (%m)\n", scope)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "can't find iface index for: %s (%m)\n" , scope); | ||||
| 1284 | return -EINVAL22; | ||||
| 1285 | } | ||||
| 1286 | } | ||||
| 1287 | |||||
| 1288 | addr6->sin6_family = AF_INET610; | ||||
| 1289 | addr6->sin6_port = htons(port)__bswap_16 (port); | ||||
| 1290 | return 0; | ||||
| 1291 | } | ||||
| 1292 | |||||
| 1293 | /** | ||||
| 1294 | * inet_pton_with_scope - convert an IPv4/IPv6 to socket address | ||||
| 1295 | * @ctx: Global context | ||||
| 1296 | * @af: address family, AF_INET, AF_INET6 or AF_UNSPEC for either | ||||
| 1297 | * @src: the start of the address string | ||||
| 1298 | * @trsvcid: transport service identifier | ||||
| 1299 | * @addr: output socket address | ||||
| 1300 | * | ||||
| 1301 | * Return 0 on success, errno otherwise. | ||||
| 1302 | */ | ||||
| 1303 | static int inet_pton_with_scope(struct libnvme_global_ctx *ctx, int af, | ||||
| 1304 | const char *src, const char * trsvcid, | ||||
| 1305 | struct sockaddr_storage *addr) | ||||
| 1306 | { | ||||
| 1307 | int ret = -EINVAL22; | ||||
| 1308 | uint16_t port = 0; | ||||
| 1309 | |||||
| 1310 | if (trsvcid) { | ||||
| 1311 | unsigned long long tmp = strtoull(trsvcid, NULL((void*)0), 0); | ||||
| 1312 | port = (uint16_t)tmp; | ||||
| 1313 | if (tmp != port) { | ||||
| 1314 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "trsvcid out of range: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "trsvcid out of range: %s\n" , trsvcid) | ||||
| 1315 | trsvcid)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "trsvcid out of range: %s\n" , trsvcid); | ||||
| 1316 | return -ERANGE34; | ||||
| 1317 | } | ||||
| 1318 | } else { | ||||
| 1319 | port = 0; | ||||
| 1320 | } | ||||
| 1321 | |||||
| 1322 | switch (af) { | ||||
| 1323 | case AF_INET2: | ||||
| 1324 | ret = inet4_pton(src, port, addr); | ||||
| 1325 | break; | ||||
| 1326 | case AF_INET610: | ||||
| 1327 | ret = inet6_pton(ctx, src, port, addr); | ||||
| 1328 | break; | ||||
| 1329 | case AF_UNSPEC0: | ||||
| 1330 | ret = inet4_pton(src, port, addr); | ||||
| 1331 | if (ret) | ||||
| 1332 | ret = inet6_pton(ctx, src, port, addr); | ||||
| 1333 | break; | ||||
| 1334 | default: | ||||
| 1335 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "unexpected address family %d\n", af)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "unexpected address family %d\n" , af); | ||||
| 1336 | } | ||||
| 1337 | |||||
| 1338 | return ret; | ||||
| 1339 | } | ||||
| 1340 | |||||
| 1341 | bool_Bool traddr_is_hostname(struct libnvme_global_ctx *ctx, | ||||
| 1342 | const char *transport, const char *traddr) | ||||
| 1343 | { | ||||
| 1344 | if (!traddr || !transport) | ||||
| 1345 | return false0; | ||||
| 1346 | if (!strcmp(traddr, "none")) | ||||
| 1347 | return false0; | ||||
| 1348 | if (strcmp(transport, "tcp") && strcmp(transport, "rdma")) | ||||
| 1349 | return false0; | ||||
| 1350 | |||||
| 1351 | return !libnvmf_traddr_is_numeric(traddr); | ||||
| 1352 | } | ||||
| 1353 | |||||
| 1354 | /* | ||||
| 1355 | * Reject a hostname traddr/host_traddr and canonicalize a numeric one, | ||||
| 1356 | * routing the check through the TID constructor so the tree keeps one | ||||
| 1357 | * definition of acceptable and canonical addressing. Resolving a | ||||
| 1358 | * hostname is the caller's job, not libnvme's, so the connect paths | ||||
| 1359 | * simply refuse one instead of resolving it. | ||||
| 1360 | */ | ||||
| 1361 | static int nvmf_sanitize_addrs(struct libnvme_global_ctx *ctx, struct libnvme_ctrl *c) | ||||
| 1362 | { | ||||
| 1363 | struct libnvmf_tid *tid; | ||||
| 1364 | const char *canon; | ||||
| 1365 | char *dup; | ||||
| 1366 | int rc; | ||||
| 1367 | |||||
| 1368 | if (traddr_is_hostname(ctx, c->transport, c->traddr)) { | ||||
| 1369 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n" , c->traddr) | ||||
| 1370 | "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n" , c->traddr) | ||||
| 1371 | c->traddr)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n" , c->traddr); | ||||
| 1372 | return -ENVME_CONNECT_TRADDR; | ||||
| 1373 | } | ||||
| 1374 | |||||
| 1375 | if (traddr_is_hostname(ctx, c->transport, c->host_traddr)) { | ||||
| 1376 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n" , c->host_traddr) | ||||
| 1377 | "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n" , c->host_traddr) | ||||
| 1378 | c->host_traddr)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "host-traddr '%s' is not a numeric address; hostname resolution is the caller's responsibility\n" , c->host_traddr); | ||||
| 1379 | return -ENVME_CONNECT_TRADDR; | ||||
| 1380 | } | ||||
| 1381 | |||||
| 1382 | rc = libnvmf_tid_from_fields(c->transport, c->traddr, c->trsvcid, | ||||
| 1383 | NULL((void*)0), c->host_traddr, c->host_iface, NULL((void*)0), NULL((void*)0), &tid); | ||||
| 1384 | if (rc) | ||||
| 1385 | return rc; | ||||
| 1386 | |||||
| 1387 | canon = libnvmf_tid_get_traddr(tid); | ||||
| 1388 | if (canon) { | ||||
| 1389 | dup = strdup(canon); | ||||
| 1390 | if (!dup) { | ||||
| 1391 | libnvmf_tid_free(tid); | ||||
| 1392 | return -ENOMEM12; | ||||
| 1393 | } | ||||
| 1394 | free(c->traddr); | ||||
| 1395 | c->traddr = dup; | ||||
| 1396 | } | ||||
| 1397 | |||||
| 1398 | canon = libnvmf_tid_get_host_traddr(tid); | ||||
| 1399 | if (canon) { | ||||
| 1400 | dup = strdup(canon); | ||||
| 1401 | if (!dup) { | ||||
| 1402 | libnvmf_tid_free(tid); | ||||
| 1403 | return -ENOMEM12; | ||||
| 1404 | } | ||||
| 1405 | free(c->host_traddr); | ||||
| 1406 | c->host_traddr = dup; | ||||
| 1407 | } | ||||
| 1408 | |||||
| 1409 | libnvmf_tid_free(tid); | ||||
| 1410 | |||||
| 1411 | return 0; | ||||
| 1412 | } | ||||
| 1413 | |||||
| 1414 | static int build_options(struct libnvme_host *h, struct libnvme_ctrl *c, char **argstr) | ||||
| 1415 | { | ||||
| 1416 | const char *transport = libnvme_ctrl_get_transport(c); | ||||
| 1417 | const char *hostnqn, *hostid, *hostkey, *ctrlkey = NULL((void*)0); | ||||
| 1418 | bool_Bool discover = false0, discovery_nqn = false0; | ||||
| 1419 | struct libnvme_global_ctx *ctx = h->ctx; | ||||
| 1420 | long keyring_id = 0; | ||||
| 1421 | long key_id = 0; | ||||
| 1422 | int ret; | ||||
| 1423 | |||||
| 1424 | if (!transport) { | ||||
| 1425 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "need a transport (-t) argument\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "need a transport (-t) argument\n" ); | ||||
| 1426 | return -ENVME_CONNECT_TARG; | ||||
| 1427 | } | ||||
| 1428 | |||||
| 1429 | if (strncmp(transport, "loop", 4)) { | ||||
| 1430 | if (!libnvme_ctrl_get_traddr(c)) { | ||||
| 1431 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "need a address (-a) argument\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "need a address (-a) argument\n" ); | ||||
| 1432 | return -ENVME_CONNECT_AARG; | ||||
| 1433 | } | ||||
| 1434 | } | ||||
| 1435 | |||||
| 1436 | /* always specify nqn as first arg - this will init the string */ | ||||
| 1437 | if (asprintf(argstr, "nqn=%s", | ||||
| 1438 | libnvme_ctrl_get_subsysnqn(c)) < 0) { | ||||
| 1439 | return -ENOMEM12; | ||||
| 1440 | } | ||||
| 1441 | |||||
| 1442 | if (!strcmp(libnvme_ctrl_get_subsysnqn(c), NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) { | ||||
| 1443 | libnvme_ctrl_set_discovery_ctrl(c, true1); | ||||
| 1444 | libnvme_ctrl_set_unique_discovery_ctrl(c, false0); | ||||
| 1445 | discovery_nqn = true1; | ||||
| 1446 | } | ||||
| 1447 | |||||
| 1448 | if (libnvme_ctrl_get_discovery_ctrl(c)) | ||||
| 1449 | discover = true1; | ||||
| 1450 | |||||
| 1451 | hostnqn = libnvme_host_get_hostnqn(h); | ||||
| 1452 | hostid = libnvme_host_get_hostid(h); | ||||
| 1453 | hostkey = libnvme_host_get_kxchap_host_key(h); | ||||
| 1454 | if (!hostkey) | ||||
| 1455 | libnvme_ctrl_get_kxchap_host_key(c, &hostkey, NULL((void*)0)); | ||||
| 1456 | |||||
| 1457 | if (hostkey) | ||||
| 1458 | libnvme_ctrl_get_kxchap_ctrl_key(c, &ctrlkey, NULL((void*)0)); | ||||
| 1459 | |||||
| 1460 | if (c->cfg.tls && c->cfg.concat) { | ||||
| 1461 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "cannot specify --tls and --concat together\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "cannot specify --tls and --concat together\n" ); | ||||
| 1462 | return -ENVME_CONNECT_INVAL; | ||||
| 1463 | } | ||||
| 1464 | |||||
| 1465 | if (c->cfg.concat && !hostkey) { | ||||
| 1466 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "required argument [--kxchap-secret | -S] not specified with --concat\n")__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "required argument [--kxchap-secret | -S] not specified with --concat\n" ); | ||||
| 1467 | return -ENVME_CONNECT_INVAL; | ||||
| 1468 | } | ||||
| 1469 | |||||
| 1470 | if (c->cfg.tls) { | ||||
| 1471 | ret = __libnvmf_import_keys_from_config(h, c, | ||||
| 1472 | &keyring_id, &key_id); | ||||
| 1473 | if (ret) | ||||
| 1474 | return ret; | ||||
| 1475 | |||||
| 1476 | if (key_id == 0) { | ||||
| 1477 | if (c->cfg.tls_configured_key_id) | ||||
| 1478 | key_id = c->cfg.tls_configured_key_id; | ||||
| 1479 | else | ||||
| 1480 | key_id = c->cfg.tls_key_id; | ||||
| 1481 | } | ||||
| 1482 | } | ||||
| 1483 | |||||
| 1484 | if (add_argument(ctx, argstr, transport, transport)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->transport; })) { ret = __add_argument(argstr , "transport", transport); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN , ((void*)0), "option \"%s\" ignored\n", "transport"); ret = 0 ; } ret; }) || | ||||
| 1485 | add_argument(ctx, argstr, traddr,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->traddr; })) { ret = __add_argument(argstr, "traddr" , libnvme_ctrl_get_traddr(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN , ((void*)0), "option \"%s\" ignored\n", "traddr"); ret = 0; } ret; }) | ||||
| 1486 | libnvme_ctrl_get_traddr(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->traddr; })) { ret = __add_argument(argstr, "traddr" , libnvme_ctrl_get_traddr(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN , ((void*)0), "option \"%s\" ignored\n", "traddr"); ret = 0; } ret; }) || | ||||
| 1487 | add_argument(ctx, argstr, host_traddr,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->host_traddr; })) { ret = __add_argument(argstr , "host_traddr", libnvme_ctrl_get_host_traddr(c)); } else { __libnvme_msg (ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "host_traddr"); ret = 0; } ret; }) | ||||
| 1488 | libnvme_ctrl_get_host_traddr(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->host_traddr; })) { ret = __add_argument(argstr , "host_traddr", libnvme_ctrl_get_host_traddr(c)); } else { __libnvme_msg (ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "host_traddr"); ret = 0; } ret; }) || | ||||
| 1489 | add_argument(ctx, argstr, host_iface,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->host_iface; })) { ret = __add_argument(argstr , "host_iface", libnvme_ctrl_get_host_iface(c)); } else { __libnvme_msg (ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "host_iface"); ret = 0; } ret; }) | ||||
| 1490 | libnvme_ctrl_get_host_iface(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->host_iface; })) { ret = __add_argument(argstr , "host_iface", libnvme_ctrl_get_host_iface(c)); } else { __libnvme_msg (ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "host_iface"); ret = 0; } ret; }) || | ||||
| 1491 | add_argument(ctx, argstr, trsvcid,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->trsvcid; })) { ret = __add_argument(argstr, "trsvcid" , libnvme_ctrl_get_trsvcid(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN , ((void*)0), "option \"%s\" ignored\n", "trsvcid"); ret = 0; } ret; }) | ||||
| 1492 | libnvme_ctrl_get_trsvcid(c))({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->trsvcid; })) { ret = __add_argument(argstr, "trsvcid" , libnvme_ctrl_get_trsvcid(c)); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN , ((void*)0), "option \"%s\" ignored\n", "trsvcid"); ret = 0; } ret; }) || | ||||
| 1493 | (hostnqn && add_argument(ctx, argstr, hostnqn, hostnqn)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->hostnqn; })) { ret = __add_argument(argstr, "hostnqn" , hostnqn); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void *)0), "option \"%s\" ignored\n", "hostnqn"); ret = 0; } ret; } )) || | ||||
| 1494 | (hostid && add_argument(ctx, argstr, hostid, hostid)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->hostid; })) { ret = __add_argument(argstr, "hostid" , hostid); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void *)0), "option \"%s\" ignored\n", "hostid"); ret = 0; } ret; } )) || | ||||
| 1495 | (discover && !discovery_nqn && | ||||
| 1496 | add_bool_argument(ctx, argstr, discovery, true)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->discovery; })) { ret = __add_bool_argument(argstr , "discovery", 1); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "discovery"); ret = 0 ; } ret; })) || | ||||
| 1497 | (hostkey && | ||||
| 1498 | add_argument(ctx, argstr, dhchap_secret, hostkey)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->dhchap_secret; })) { ret = __add_argument(argstr , "dhchap_secret", hostkey); } else { __libnvme_msg(ctx, LIBNVME_LOG_WARN , ((void*)0), "option \"%s\" ignored\n", "dhchap_secret"); ret = 0; } ret; })) || | ||||
| 1499 | (ctrlkey && | ||||
| 1500 | add_argument(ctx, argstr, dhchap_ctrl_secret, ctrlkey)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->dhchap_ctrl_secret; })) { ret = __add_argument (argstr, "dhchap_ctrl_secret", ctrlkey); } else { __libnvme_msg (ctx, LIBNVME_LOG_WARN, ((void*)0), "option \"%s\" ignored\n" , "dhchap_ctrl_secret"); ret = 0; } ret; })) || | ||||
| 1501 | (!discover && | ||||
| 1502 | add_int_argument(ctx, argstr, nr_io_queues,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->nr_io_queues; })) { ret = __add_int_argument (argstr, "nr_io_queues", c->cfg.nr_io_queues, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "nr_io_queues"); ret = 0; } ret; }) | ||||
| 1503 | c->cfg.nr_io_queues, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->nr_io_queues; })) { ret = __add_int_argument (argstr, "nr_io_queues", c->cfg.nr_io_queues, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "nr_io_queues"); ret = 0; } ret; })) || | ||||
| 1504 | (!discover && | ||||
| 1505 | add_int_argument(ctx, argstr, nr_write_queues,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->nr_write_queues; })) { ret = __add_int_argument (argstr, "nr_write_queues", c->cfg.nr_write_queues, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "nr_write_queues"); ret = 0; } ret; }) | ||||
| 1506 | c->cfg.nr_write_queues, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->nr_write_queues; })) { ret = __add_int_argument (argstr, "nr_write_queues", c->cfg.nr_write_queues, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "nr_write_queues"); ret = 0; } ret; })) || | ||||
| 1507 | (!discover && | ||||
| 1508 | add_int_argument(ctx, argstr, nr_poll_queues,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->nr_poll_queues; })) { ret = __add_int_argument (argstr, "nr_poll_queues", c->cfg.nr_poll_queues, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "nr_poll_queues"); ret = 0; } ret; }) | ||||
| 1509 | c->cfg.nr_poll_queues, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->nr_poll_queues; })) { ret = __add_int_argument (argstr, "nr_poll_queues", c->cfg.nr_poll_queues, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "nr_poll_queues"); ret = 0; } ret; })) || | ||||
| 1510 | (!discover && | ||||
| 1511 | add_int_argument(ctx, argstr, queue_size,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->queue_size; })) { ret = __add_int_argument(argstr , "queue_size", c->cfg.queue_size, 0); } else { __libnvme_msg (ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "queue_size"); ret = 0; } ret; }) | ||||
| 1512 | c->cfg.queue_size, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->queue_size; })) { ret = __add_int_argument(argstr , "queue_size", c->cfg.queue_size, 0); } else { __libnvme_msg (ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "queue_size"); ret = 0; } ret; })) || | ||||
| 1513 | add_int_argument(ctx, argstr, keep_alive_tmo,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->keep_alive_tmo; })) { ret = __add_int_argument (argstr, "keep_alive_tmo", c->cfg.keep_alive_tmo, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "keep_alive_tmo"); ret = 0; } ret; }) | ||||
| 1514 | c->cfg.keep_alive_tmo, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->keep_alive_tmo; })) { ret = __add_int_argument (argstr, "keep_alive_tmo", c->cfg.keep_alive_tmo, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "keep_alive_tmo"); ret = 0; } ret; }) || | ||||
| 1515 | add_int_argument(ctx, argstr, reconnect_delay,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->reconnect_delay; })) { ret = __add_int_argument (argstr, "reconnect_delay", c->cfg.reconnect_delay, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "reconnect_delay"); ret = 0; } ret; }) | ||||
| 1516 | c->cfg.reconnect_delay, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->reconnect_delay; })) { ret = __add_int_argument (argstr, "reconnect_delay", c->cfg.reconnect_delay, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "reconnect_delay"); ret = 0; } ret; }) || | ||||
| 1517 | (strcmp(transport, "loop") && | ||||
| 1518 | add_int_or_minus_one_argument(ctx, argstr, ctrl_loss_tmo,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->ctrl_loss_tmo; })) { ret = __add_int_or_minus_one_argument (argstr, "ctrl_loss_tmo", c->cfg.ctrl_loss_tmo); } else { __libnvme_msg (ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "ctrl_loss_tmo"); ret = 0; } ret; }) | ||||
| 1519 | c->cfg.ctrl_loss_tmo)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->ctrl_loss_tmo; })) { ret = __add_int_or_minus_one_argument (argstr, "ctrl_loss_tmo", c->cfg.ctrl_loss_tmo); } else { __libnvme_msg (ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "ctrl_loss_tmo"); ret = 0; } ret; })) || | ||||
| 1520 | (strcmp(transport, "loop") && | ||||
| 1521 | add_int_argument(ctx, argstr, fast_io_fail_tmo,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->fast_io_fail_tmo; })) { ret = __add_int_argument (argstr, "fast_io_fail_tmo", c->cfg.fast_io_fail_tmo, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "fast_io_fail_tmo"); ret = 0; } ret; }) | ||||
| 1522 | c->cfg.fast_io_fail_tmo, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->fast_io_fail_tmo; })) { ret = __add_int_argument (argstr, "fast_io_fail_tmo", c->cfg.fast_io_fail_tmo, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "fast_io_fail_tmo"); ret = 0; } ret; })) || | ||||
| 1523 | (strcmp(transport, "loop") && | ||||
| 1524 | add_int_argument(ctx, argstr, tos, c->cfg.tos, true)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tos; })) { ret = __add_int_argument(argstr, "tos" , c->cfg.tos, 1); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "tos"); ret = 0; } ret ; })) || | ||||
| 1525 | add_hex_argument(ctx, argstr, keyring, keyring_id, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->keyring; })) { ret = __add_hex_argument(argstr , "keyring", keyring_id, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "keyring"); ret = 0; } ret; }) || | ||||
| 1526 | (!strcmp(transport, "tcp") && | ||||
| 1527 | add_hex_argument(ctx, argstr, tls_key, key_id, false)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tls_key; })) { ret = __add_hex_argument(argstr , "tls_key", key_id, 0); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "tls_key"); ret = 0; } ret; })) || | ||||
| 1528 | add_bool_argument(ctx, argstr, duplicate_connect,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->duplicate_connect; })) { ret = __add_bool_argument (argstr, "duplicate_connect", c->cfg.duplicate_connect); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "duplicate_connect"); ret = 0; } ret; }) | ||||
| 1529 | c->cfg.duplicate_connect)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->duplicate_connect; })) { ret = __add_bool_argument (argstr, "duplicate_connect", c->cfg.duplicate_connect); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "duplicate_connect"); ret = 0; } ret; }) || | ||||
| 1530 | add_bool_argument(ctx, argstr, disable_sqflow,({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->disable_sqflow; })) { ret = __add_bool_argument (argstr, "disable_sqflow", c->cfg.disable_sqflow); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "disable_sqflow"); ret = 0; } ret; }) | ||||
| 1531 | c->cfg.disable_sqflow)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->disable_sqflow; })) { ret = __add_bool_argument (argstr, "disable_sqflow", c->cfg.disable_sqflow); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "disable_sqflow"); ret = 0; } ret; }) || | ||||
| 1532 | (!strcmp(transport, "tcp") && | ||||
| 1533 | add_bool_argument(ctx, argstr, hdr_digest, c->cfg.hdr_digest)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->hdr_digest; })) { ret = __add_bool_argument( argstr, "hdr_digest", c->cfg.hdr_digest); } else { __libnvme_msg (ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "hdr_digest"); ret = 0; } ret; })) || | ||||
| 1534 | (!strcmp(transport, "tcp") && | ||||
| 1535 | add_bool_argument(ctx, argstr, data_digest, c->cfg.data_digest)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->data_digest; })) { ret = __add_bool_argument (argstr, "data_digest", c->cfg.data_digest); } else { __libnvme_msg (ctx, LIBNVME_LOG_DEBUG, ((void*)0), "option \"%s\" ignored\n" , "data_digest"); ret = 0; } ret; })) || | ||||
| 1536 | (!strcmp(transport, "tcp") && | ||||
| 1537 | add_bool_argument(ctx, argstr, tls, c->cfg.tls)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->tls; })) { ret = __add_bool_argument(argstr, "tls", c->cfg.tls); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "tls"); ret = 0; } ret ; })) || | ||||
| 1538 | (!strcmp(transport, "tcp") && | ||||
| 1539 | add_bool_argument(ctx, argstr, concat, c->cfg.concat)({ int ret; if (({ !__nvmf_supported_options(ctx) && ctx ->options->concat; })) { ret = __add_bool_argument(argstr , "concat", c->cfg.concat); } else { __libnvme_msg(ctx, LIBNVME_LOG_DEBUG , ((void*)0), "option \"%s\" ignored\n", "concat"); ret = 0; } ret; }))) | ||||
| 1540 | return -1; | ||||
| 1541 | |||||
| 1542 | return 0; | ||||
| 1543 | } | ||||
| 1544 | |||||
| 1545 | #define parse_option(ctx, v, name)if (!strcmp(v, "name")) { ctx->options->name = 1; continue ; } \ | ||||
| 1546 | if (!strcmp(v, stringify(name)"name")) { \ | ||||
| 1547 | ctx->options->name = true1; \ | ||||
| 1548 | continue; \ | ||||
| 1549 | } | ||||
| 1550 | |||||
| 1551 | static int __nvmf_supported_options(struct libnvme_global_ctx *ctx) | ||||
| 1552 | { | ||||
| 1553 | char buf[0x1000], *options, *p, *v; | ||||
| 1554 | __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1; | ||||
| 1555 | ssize_t len; | ||||
| 1556 | |||||
| 1557 | if (ctx->options) | ||||
| 1558 | return 0; | ||||
| 1559 | |||||
| 1560 | ctx->options = calloc(1, sizeof(*ctx->options)); | ||||
| 1561 | if (!ctx->options) | ||||
| 1562 | return -ENOMEM12; | ||||
| 1563 | |||||
| 1564 | fd = open(nvmf_dev, O_RDONLY00); | ||||
| 1565 | if (fd < 0) { | ||||
| 1566 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))) | ||||
| 1567 | nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))); | ||||
| 1568 | return -ENVME_CONNECT_OPEN; | ||||
| 1569 | } | ||||
| 1570 | |||||
| 1571 | memset(buf, 0x0, sizeof(buf)); | ||||
| 1572 | len = read(fd, buf, sizeof(buf) - 1); | ||||
| 1573 | if (len < 0) { | ||||
| 1574 | if (errno(*__errno_location ()) == EINVAL22) { | ||||
| 1575 | /* | ||||
| 1576 | * Older Linux kernels don't allow reading from nvmf_dev | ||||
| 1577 | * to get supported options, so use a default set | ||||
| 1578 | */ | ||||
| 1579 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n" , nvmf_dev) | ||||
| 1580 | "Cannot read %s, using default options\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n" , nvmf_dev) | ||||
| 1581 | nvmf_dev)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "Cannot read %s, using default options\n" , nvmf_dev); | ||||
| 1582 | *ctx->options = default_supported_options; | ||||
| 1583 | return 0; | ||||
| 1584 | } | ||||
| 1585 | |||||
| 1586 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))) | ||||
| 1587 | nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))); | ||||
| 1588 | return -ENVME_CONNECT_READ; | ||||
| 1589 | } | ||||
| 1590 | |||||
| 1591 | buf[len] = '\0'; | ||||
| 1592 | options = buf; | ||||
| 1593 | |||||
| 1594 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "kernel supports: ")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "kernel supports: " ); | ||||
| 1595 | |||||
| 1596 | while ((p = strsep(&options, ",\n")) != NULL((void*)0)) { | ||||
| 1597 | if (!*p) | ||||
| 1598 | continue; | ||||
| 1599 | v = strsep(&p, "= "); | ||||
| 1600 | if (!v) | ||||
| 1601 | continue; | ||||
| 1602 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s ", v)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s ", v); | ||||
| 1603 | |||||
| 1604 | parse_option(ctx, v, cntlid)if (!strcmp(v, "cntlid")) { ctx->options->cntlid = 1; continue ; }; | ||||
| 1605 | parse_option(ctx, v, concat)if (!strcmp(v, "concat")) { ctx->options->concat = 1; continue ; }; | ||||
| 1606 | parse_option(ctx, v, ctrl_loss_tmo)if (!strcmp(v, "ctrl_loss_tmo")) { ctx->options->ctrl_loss_tmo = 1; continue; }; | ||||
| 1607 | parse_option(ctx, v, data_digest)if (!strcmp(v, "data_digest")) { ctx->options->data_digest = 1; continue; }; | ||||
| 1608 | parse_option(ctx, v, dhchap_ctrl_secret)if (!strcmp(v, "dhchap_ctrl_secret")) { ctx->options->dhchap_ctrl_secret = 1; continue; }; | ||||
| 1609 | parse_option(ctx, v, dhchap_secret)if (!strcmp(v, "dhchap_secret")) { ctx->options->dhchap_secret = 1; continue; }; | ||||
| 1610 | parse_option(ctx, v, disable_sqflow)if (!strcmp(v, "disable_sqflow")) { ctx->options->disable_sqflow = 1; continue; }; | ||||
| 1611 | parse_option(ctx, v, discovery)if (!strcmp(v, "discovery")) { ctx->options->discovery = 1; continue; }; | ||||
| 1612 | parse_option(ctx, v, duplicate_connect)if (!strcmp(v, "duplicate_connect")) { ctx->options->duplicate_connect = 1; continue; }; | ||||
| 1613 | parse_option(ctx, v, fast_io_fail_tmo)if (!strcmp(v, "fast_io_fail_tmo")) { ctx->options->fast_io_fail_tmo = 1; continue; }; | ||||
| 1614 | parse_option(ctx, v, hdr_digest)if (!strcmp(v, "hdr_digest")) { ctx->options->hdr_digest = 1; continue; }; | ||||
| 1615 | parse_option(ctx, v, host_iface)if (!strcmp(v, "host_iface")) { ctx->options->host_iface = 1; continue; }; | ||||
| 1616 | parse_option(ctx, v, host_traddr)if (!strcmp(v, "host_traddr")) { ctx->options->host_traddr = 1; continue; }; | ||||
| 1617 | parse_option(ctx, v, hostid)if (!strcmp(v, "hostid")) { ctx->options->hostid = 1; continue ; }; | ||||
| 1618 | parse_option(ctx, v, hostnqn)if (!strcmp(v, "hostnqn")) { ctx->options->hostnqn = 1; continue; }; | ||||
| 1619 | parse_option(ctx, v, instance)if (!strcmp(v, "instance")) { ctx->options->instance = 1 ; continue; }; | ||||
| 1620 | parse_option(ctx, v, keep_alive_tmo)if (!strcmp(v, "keep_alive_tmo")) { ctx->options->keep_alive_tmo = 1; continue; }; | ||||
| 1621 | parse_option(ctx, v, keyring)if (!strcmp(v, "keyring")) { ctx->options->keyring = 1; continue; }; | ||||
| 1622 | parse_option(ctx, v, nqn)if (!strcmp(v, "nqn")) { ctx->options->nqn = 1; continue ; }; | ||||
| 1623 | parse_option(ctx, v, nr_io_queues)if (!strcmp(v, "nr_io_queues")) { ctx->options->nr_io_queues = 1; continue; }; | ||||
| 1624 | parse_option(ctx, v, nr_poll_queues)if (!strcmp(v, "nr_poll_queues")) { ctx->options->nr_poll_queues = 1; continue; }; | ||||
| 1625 | parse_option(ctx, v, nr_write_queues)if (!strcmp(v, "nr_write_queues")) { ctx->options->nr_write_queues = 1; continue; }; | ||||
| 1626 | parse_option(ctx, v, queue_size)if (!strcmp(v, "queue_size")) { ctx->options->queue_size = 1; continue; }; | ||||
| 1627 | parse_option(ctx, v, reconnect_delay)if (!strcmp(v, "reconnect_delay")) { ctx->options->reconnect_delay = 1; continue; }; | ||||
| 1628 | parse_option(ctx, v, tls)if (!strcmp(v, "tls")) { ctx->options->tls = 1; continue ; }; | ||||
| 1629 | parse_option(ctx, v, tls_key)if (!strcmp(v, "tls_key")) { ctx->options->tls_key = 1; continue; }; | ||||
| 1630 | parse_option(ctx, v, tos)if (!strcmp(v, "tos")) { ctx->options->tos = 1; continue ; }; | ||||
| 1631 | parse_option(ctx, v, traddr)if (!strcmp(v, "traddr")) { ctx->options->traddr = 1; continue ; }; | ||||
| 1632 | parse_option(ctx, v, transport)if (!strcmp(v, "transport")) { ctx->options->transport = 1; continue; }; | ||||
| 1633 | parse_option(ctx, v, trsvcid)if (!strcmp(v, "trsvcid")) { ctx->options->trsvcid = 1; continue; }; | ||||
| 1634 | } | ||||
| 1635 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "\n")__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "\n"); | ||||
| 1636 | return 0; | ||||
| 1637 | } | ||||
| 1638 | |||||
| 1639 | /* Parse the kernel instance number out of a ctrl's name ("nvme3" -> 3). */ | ||||
| 1640 | static int ctrl_instance(struct libnvme_ctrl *c) | ||||
| 1641 | { | ||||
| 1642 | int instance = -1; | ||||
| 1643 | const char *name; | ||||
| 1644 | |||||
| 1645 | if (!c) | ||||
| 1646 | return instance; | ||||
| 1647 | |||||
| 1648 | name = libnvme_ctrl_get_name(c); | ||||
| 1649 | if (name) | ||||
| 1650 | if (sscanf(name, "nvme%d", &instance) != 1) | ||||
| 1651 | instance = -1; | ||||
| 1652 | |||||
| 1653 | return instance; | ||||
| 1654 | } | ||||
| 1655 | |||||
| 1656 | enum registry_action { | ||||
| 1657 | REG_NONE, /* leave the entry alone */ | ||||
| 1658 | REG_CLAIM, /* record @owner, overwriting any entry */ | ||||
| 1659 | REG_CLEAR, /* delete the entry */ | ||||
| 1660 | }; | ||||
| 1661 | |||||
| 1662 | /* | ||||
| 1663 | * Decide what a successful connect does to the registry entry. Pure | ||||
| 1664 | * function, no I/O, so it is directly unit-testable. | ||||
| 1665 | * | ||||
| 1666 | * @owner: ctx->owner. NULL means --owner was never given, "" means an | ||||
| 1667 | * explicit disown, and a name means a claim. | ||||
| 1668 | * @fresh: whether the kernel just created this controller for us. A | ||||
| 1669 | * controller that already existed may belong to another | ||||
| 1670 | * orchestrator, so an unstated intent must not touch its entry. | ||||
| 1671 | * A fresh one cannot legitimately be owned by anybody, so its | ||||
| 1672 | * entry is cleared to drop what a recycled instance number left | ||||
| 1673 | * behind. | ||||
| 1674 | */ | ||||
| 1675 | static enum registry_action registry_action_on_connect(const char *owner, | ||||
| 1676 | bool_Bool fresh) | ||||
| 1677 | { | ||||
| 1678 | if (owner && *owner) | ||||
| 1679 | return REG_CLAIM; | ||||
| 1680 | |||||
| 1681 | if (fresh || owner) | ||||
| 1682 | return REG_CLEAR; | ||||
| 1683 | |||||
| 1684 | return REG_NONE; | ||||
| 1685 | } | ||||
| 1686 | |||||
| 1687 | /* | ||||
| 1688 | * Best-effort registry update after a successful connect. Failures are | ||||
| 1689 | * logged but never fail the connection. | ||||
| 1690 | */ | ||||
| 1691 | static void registry_update_on_connect(struct libnvme_global_ctx *ctx, | ||||
| 1692 | int instance, bool_Bool fresh) | ||||
| 1693 | { | ||||
| 1694 | int ret; | ||||
| 1695 | |||||
| 1696 | switch (registry_action_on_connect(ctx->owner, fresh)) { | ||||
| 1697 | case REG_CLAIM: | ||||
| 1698 | ret = libnvmf_registry_create_instance(ctx, instance, | ||||
| 1699 | ctx->owner); | ||||
| 1700 | break; | ||||
| 1701 | case REG_CLEAR: | ||||
| 1702 | ret = libnvmf_registry_delete_instance(ctx, instance); | ||||
| 1703 | break; | ||||
| 1704 | case REG_NONE: | ||||
| 1705 | default: | ||||
| 1706 | return; | ||||
| 1707 | } | ||||
| 1708 | |||||
| 1709 | if (ret) | ||||
| 1710 | libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n" , instance, libnvme_strerror(-ret)) | ||||
| 1711 | "nvme%d: registry update failed: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n" , instance, libnvme_strerror(-ret)) | ||||
| 1712 | instance, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "nvme%d: registry update failed: %s\n" , instance, libnvme_strerror(-ret)); | ||||
| 1713 | } | ||||
| 1714 | |||||
| 1715 | static int __nvmf_add_ctrl(struct libnvme_global_ctx *ctx, const char *argstr) | ||||
| 1716 | { | ||||
| 1717 | __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int fd = -1; | ||||
| 1718 | int ret, len = strlen(argstr); | ||||
| 1719 | int instance; | ||||
| 1720 | char buf[0x1000], *options, *p; | ||||
| 1721 | |||||
| 1722 | fd = open(nvmf_dev, O_RDWR02); | ||||
| 1723 | if (fd < 0) { | ||||
| 1724 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))) | ||||
| 1725 | nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to open %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))); | ||||
| 1726 | return -ENVME_CONNECT_OPEN; | ||||
| 1727 | } | ||||
| 1728 | |||||
| 1729 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, '%.*s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, '%.*s'\n" , (int)strcspn(argstr,"\n"), argstr) | ||||
| 1730 | (int)strcspn(argstr,"\n"), argstr)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, '%.*s'\n" , (int)strcspn(argstr,"\n"), argstr); | ||||
| 1731 | ret = write(fd, argstr, len); | ||||
| 1732 | if (ret != len) { | ||||
| 1733 | libnvme_msg(ctx, LIBNVME_LOG_INFO, "Failed to write to %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Failed to write to %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))) | ||||
| 1734 | nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Failed to write to %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))); | ||||
| 1735 | switch (errno(*__errno_location ())) { | ||||
| 1736 | case EALREADY114: | ||||
| 1737 | return -ENVME_CONNECT_ALREADY; | ||||
| 1738 | case EINVAL22: | ||||
| 1739 | return -ENVME_CONNECT_INVAL; | ||||
| 1740 | case EADDRINUSE98: | ||||
| 1741 | return -ENVME_CONNECT_ADDRINUSE; | ||||
| 1742 | case ENODEV19: | ||||
| 1743 | return -ENVME_CONNECT_NODEV; | ||||
| 1744 | case EOPNOTSUPP95: | ||||
| 1745 | return -ENVME_CONNECT_OPNOTSUPP; | ||||
| 1746 | case ECONNREFUSED111: | ||||
| 1747 | return -ENVME_CONNECT_CONNREFUSED; | ||||
| 1748 | case EADDRNOTAVAIL99: | ||||
| 1749 | return -ENVME_CONNECT_ADDRNOTAVAIL; | ||||
| 1750 | case ENOKEY126: | ||||
| 1751 | return -ENVME_CONNECT_NOKEY; | ||||
| 1752 | default: | ||||
| 1753 | return -ENVME_CONNECT_WRITE; | ||||
| 1754 | } | ||||
| 1755 | } | ||||
| 1756 | |||||
| 1757 | memset(buf, 0x0, sizeof(buf)); | ||||
| 1758 | len = read(fd, buf, sizeof(buf) - 1); | ||||
| 1759 | if (len < 0) { | ||||
| 1760 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to read from %s: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))) | ||||
| 1761 | nvmf_dev, libnvme_strerror(errno))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to read from %s: %s\n" , nvmf_dev, libnvme_strerror((*__errno_location ()))); | ||||
| 1762 | return -ENVME_CONNECT_READ; | ||||
| 1763 | } | ||||
| 1764 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "connect ctrl, response '%.*s'\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, response '%.*s'\n" , (int)strcspn(buf, "\n"), buf) | ||||
| 1765 | (int)strcspn(buf, "\n"), buf)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "connect ctrl, response '%.*s'\n" , (int)strcspn(buf, "\n"), buf); | ||||
| 1766 | buf[len] = '\0'; | ||||
| 1767 | options = buf; | ||||
| 1768 | while ((p = strsep(&options, ",\n")) != NULL((void*)0)) { | ||||
| 1769 | if (!*p) | ||||
| 1770 | continue; | ||||
| 1771 | if (sscanf(p, "instance=%d", &instance) == 1) { | ||||
| 1772 | registry_update_on_connect(ctx, instance, true1); | ||||
| 1773 | return instance; | ||||
| 1774 | } | ||||
| 1775 | } | ||||
| 1776 | |||||
| 1777 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to parse ctrl info for \"%s\"\n", argstr)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to parse ctrl info for \"%s\"\n" , argstr); | ||||
| 1778 | return -ENVME_CONNECT_PARSE; | ||||
| 1779 | } | ||||
| 1780 | |||||
| 1781 | |||||
| 1782 | static void nvme_parse_tls_args(const char *keyring, const char *tls_key, | ||||
| 1783 | const char *tls_key_identity, | ||||
| 1784 | struct libnvme_fabrics_config *cfg, struct libnvme_ctrl *c) | ||||
| 1785 | { | ||||
| 1786 | if (keyring) { | ||||
| 1787 | char *endptr; | ||||
| 1788 | long id = strtol(keyring, &endptr, 0); | ||||
| 1789 | |||||
| 1790 | if (endptr != keyring) | ||||
| 1791 | cfg->keyring_id = id; | ||||
| 1792 | else | ||||
| 1793 | libnvme_ctrl_set_keyring(c, keyring); | ||||
| 1794 | } | ||||
| 1795 | |||||
| 1796 | if (tls_key_identity) | ||||
| 1797 | libnvme_ctrl_set_tls_key_identity(c, tls_key_identity); | ||||
| 1798 | |||||
| 1799 | if (tls_key) { | ||||
| 1800 | char *endptr; | ||||
| 1801 | long id = strtol(tls_key, &endptr, 0); | ||||
| 1802 | |||||
| 1803 | if (endptr != tls_key) | ||||
| 1804 | cfg->tls_key_id = id; | ||||
| 1805 | else | ||||
| 1806 | libnvme_ctrl_set_tls_key(c, tls_key); | ||||
| 1807 | } | ||||
| 1808 | } | ||||
| 1809 | |||||
| 1810 | __shr_public__attribute__((visibility("default"))) int libnvmf_create_ctrl(struct libnvme_global_ctx *ctx, | ||||
| 1811 | struct libnvmf_context *fctx, struct libnvme_ctrl **cp) | ||||
| 1812 | { | ||||
| 1813 | struct libnvme_ctrl *c; | ||||
| 1814 | int ret; | ||||
| 1815 | |||||
| 1816 | ret = libnvme_create_ctrl(ctx, &fctx->ctrl_params, &c); | ||||
| 1817 | if (ret) | ||||
| 1818 | return ret; | ||||
| 1819 | |||||
| 1820 | /* | ||||
| 1821 | * The credentials live on @fctx next to, not inside, ctrl_params, | ||||
| 1822 | * so libnvme_create_ctrl() cannot carry them over. Apply them here | ||||
| 1823 | * or a controller created from a context that has them would | ||||
| 1824 | * silently connect without. | ||||
| 1825 | */ | ||||
| 1826 | if (fctx->hostkey) { | ||||
| 1827 | libnvme_ctrl_set_kxchap_host_key(c, fctx->hostkey); | ||||
| 1828 | if (fctx->ctrlkey) | ||||
| 1829 | libnvme_ctrl_set_kxchap_ctrl_key(c, fctx->ctrlkey); | ||||
| 1830 | } | ||||
| 1831 | |||||
| 1832 | nvme_parse_tls_args(fctx->keyring, fctx->tls_key, | ||||
| 1833 | fctx->tls_key_identity, &fctx->ctrl_params.cfg, c); | ||||
| 1834 | update_config(c, &fctx->ctrl_params.cfg); | ||||
| 1835 | |||||
| 1836 | *cp = c; | ||||
| 1837 | |||||
| 1838 | return 0; | ||||
| 1839 | } | ||||
| 1840 | |||||
| 1841 | __shr_public__attribute__((visibility("default"))) int libnvmf_add_ctrl(struct libnvme_host *h, struct libnvme_ctrl *c) | ||||
| 1842 | { | ||||
| 1843 | struct libnvme_subsystem *s; | ||||
| 1844 | __cleanup_free__attribute__((cleanup(shr_freep))) char *argstr = NULL((void*)0); | ||||
| 1845 | int ret; | ||||
| 1846 | |||||
| 1847 | /* Are duplicate connections allowed on existing controller */ | ||||
| 1848 | if (libnvme_ctrl_get_name(c) && !c->cfg.duplicate_connect) | ||||
| 1849 | return -ENVME_CONNECT_ALREADY; | ||||
| 1850 | |||||
| 1851 | /* carry over config from an existing ctrl on the same subsystem */ | ||||
| 1852 | s = libnvme_lookup_subsystem(h, NULL((void*)0), libnvme_ctrl_get_subsysnqn(c)); | ||||
| 1853 | if (s) { | ||||
| 1854 | struct libnvme_ctrl *fc; | ||||
| 1855 | struct libnvme_ctrl_params params = { | ||||
| 1856 | .transport = libnvme_ctrl_get_transport(c), | ||||
| 1857 | .traddr = libnvme_ctrl_get_traddr(c), | ||||
| 1858 | .host_traddr = libnvme_ctrl_get_host_traddr(c), | ||||
| 1859 | .host_iface = libnvme_ctrl_get_host_iface(c), | ||||
| 1860 | .trsvcid = libnvme_ctrl_get_trsvcid(c), | ||||
| 1861 | }; | ||||
| 1862 | |||||
| 1863 | fc = libnvme_ctrl_find(s, ¶ms, NULL((void*)0)); | ||||
| 1864 | if (fc) { | ||||
| 1865 | const char *key; | ||||
| 1866 | |||||
| 1867 | merge_config(c, &fc->cfg); | ||||
| 1868 | /* | ||||
| 1869 | * An authentication key might already been set | ||||
| 1870 | * in @cfg, so ensure to update @c with the correct | ||||
| 1871 | * controller key. | ||||
| 1872 | */ | ||||
| 1873 | if (libnvme_ctrl_get_kxchap_host_key(fc, &key, | ||||
| 1874 | NULL((void*)0)) == 0) | ||||
| 1875 | libnvme_ctrl_set_kxchap_host_key(c, key); | ||||
| 1876 | if (libnvme_ctrl_get_kxchap_ctrl_key(fc, &key, | ||||
| 1877 | NULL((void*)0)) == 0) | ||||
| 1878 | libnvme_ctrl_set_kxchap_ctrl_key(c, key); | ||||
| 1879 | if (libnvme_ctrl_get_keyring(fc, &key, NULL((void*)0)) == 0) | ||||
| 1880 | libnvme_ctrl_set_keyring(c, key); | ||||
| 1881 | key = libnvme_ctrl_get_tls_key_identity(fc); | ||||
| 1882 | if (key) | ||||
| 1883 | libnvme_ctrl_set_tls_key_identity(c, key); | ||||
| 1884 | key = libnvme_ctrl_get_tls_key(fc); | ||||
| 1885 | if (key) | ||||
| 1886 | libnvme_ctrl_set_tls_key(c, key); | ||||
| 1887 | } | ||||
| 1888 | |||||
| 1889 | } | ||||
| 1890 | |||||
| 1891 | libnvme_ctrl_set_discovered(c, true1); | ||||
| 1892 | ret = nvmf_sanitize_addrs(h->ctx, c); | ||||
| 1893 | if (ret) | ||||
| 1894 | return ret; | ||||
| 1895 | |||||
| 1896 | ret = build_options(h, c, &argstr); | ||||
| 1897 | if (ret) | ||||
| 1898 | return ret; | ||||
| 1899 | |||||
| 1900 | ret = __nvmf_add_ctrl(h->ctx, argstr); | ||||
| 1901 | if (ret < 0) | ||||
| 1902 | return ret; | ||||
| 1903 | |||||
| 1904 | libnvme_msg(h->ctx, LIBNVME_LOG_INFO, "nvme%d: %s connected\n", ret,__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "nvme%d: %s connected\n" , ret, libnvme_ctrl_get_subsysnqn(c)) | ||||
| 1905 | libnvme_ctrl_get_subsysnqn(c))__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "nvme%d: %s connected\n" , ret, libnvme_ctrl_get_subsysnqn(c)); | ||||
| 1906 | return libnvme_init_ctrl(h, c, ret); | ||||
| 1907 | } | ||||
| 1908 | |||||
| 1909 | __shr_public__attribute__((visibility("default"))) int libnvmf_connect_ctrl(struct libnvme_ctrl *c) | ||||
| 1910 | { | ||||
| 1911 | __cleanup_free__attribute__((cleanup(shr_freep))) char *argstr = NULL((void*)0); | ||||
| 1912 | int ret; | ||||
| 1913 | |||||
| 1914 | ret = nvmf_sanitize_addrs(c->s->h->ctx, c); | ||||
| 1915 | if (ret) | ||||
| 1916 | return ret; | ||||
| 1917 | |||||
| 1918 | ret = build_options(c->s->h, c, &argstr); | ||||
| 1919 | if (ret) | ||||
| 1920 | return ret; | ||||
| 1921 | |||||
| 1922 | ret = __nvmf_add_ctrl(c->s->h->ctx, argstr); | ||||
| 1923 | if (ret < 0) | ||||
| 1924 | return ret; | ||||
| 1925 | |||||
| 1926 | return 0; | ||||
| 1927 | } | ||||
| 1928 | |||||
| 1929 | __shr_public__attribute__((visibility("default"))) int libnvmf_disconnect_ctrl(struct libnvme_ctrl *c) | ||||
| 1930 | { | ||||
| 1931 | struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0); | ||||
| 1932 | int ret; | ||||
| 1933 | |||||
| 1934 | ret = libnvme_set_attr(libnvme_ctrl_get_sysfs_dir(c), | ||||
| 1935 | "delete_controller", "1"); | ||||
| 1936 | if (ret < 0) { | ||||
| 1937 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to disconnect, error %d\n" , c->name, (*__errno_location ())) | ||||
| 1938 | "%s: failed to disconnect, error %d\n", c->name, errno)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to disconnect, error %d\n" , c->name, (*__errno_location ())); | ||||
| 1939 | return ret; | ||||
| 1940 | } | ||||
| 1941 | libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: %s disconnected\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: %s disconnected\n" , c->name, c->subsysnqn) | ||||
| 1942 | c->name, c->subsysnqn)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: %s disconnected\n" , c->name, c->subsysnqn); | ||||
| 1943 | libnvme_deconfigure_ctrl(c); | ||||
| 1944 | return 0; | ||||
| 1945 | } | ||||
| 1946 | |||||
| 1947 | static void nvmf_update_tls_concat(struct nvmf_disc_log_entry *e, | ||||
| 1948 | struct libnvme_ctrl *c, struct libnvme_host *h) | ||||
| 1949 | { | ||||
| 1950 | if (!e) | ||||
| 1951 | return; | ||||
| 1952 | |||||
| 1953 | if (e->trtype != NVMF_TRTYPE_TCP || | ||||
| 1954 | e->tsas.tcp.sectype == NVMF_TCP_SECTYPE_NONE) | ||||
| 1955 | return; | ||||
| 1956 | |||||
| 1957 | if (e->treq & NVMF_TREQ_REQUIRED) { | ||||
| 1958 | libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)) | ||||
| 1959 | "setting --tls due to treq %s and sectype %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)) | ||||
| 1960 | libnvmf_treq_str(e->treq),__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)) | ||||
| 1961 | libnvmf_sectype_str(e->tsas.tcp.sectype))__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --tls due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)); | ||||
| 1962 | |||||
| 1963 | c->cfg.tls = true1; | ||||
| 1964 | return; | ||||
| 1965 | } | ||||
| 1966 | |||||
| 1967 | if (e->treq & NVMF_TREQ_NOT_REQUIRED) { | ||||
| 1968 | libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)) | ||||
| 1969 | "setting --concat due to treq %s and sectype %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)) | ||||
| 1970 | libnvmf_treq_str(e->treq),__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)) | ||||
| 1971 | libnvmf_sectype_str(e->tsas.tcp.sectype))__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "setting --concat due to treq %s and sectype %s\n" , libnvmf_treq_str(e->treq), libnvmf_sectype_str(e->tsas .tcp.sectype)); | ||||
| 1972 | |||||
| 1973 | c->cfg.concat = true1; | ||||
| 1974 | return; | ||||
| 1975 | } | ||||
| 1976 | } | ||||
| 1977 | |||||
| 1978 | /* | ||||
| 1979 | * Enumerated-connect gate: consult the exclusion list before connecting a | ||||
| 1980 | * controller that was enumerated from a Discovery Log Page, the NBFT table, | ||||
| 1981 | * or a configuration file. Controllers named explicitly by the user | ||||
| 1982 | * ("nvme connect", "nvme discover" with an address) are deliberately not | ||||
| 1983 | * checked -- a targeted human action overrides the list. | ||||
| 1984 | */ | ||||
| 1985 | static bool_Bool nvmf_excluded(struct libnvme_global_ctx *ctx, | ||||
| 1986 | const char *transport, const char *traddr, | ||||
| 1987 | const char *trsvcid, const char *subsysnqn, | ||||
| 1988 | const char *host_traddr, const char *host_iface, | ||||
| 1989 | const char *hostnqn, const char *hostid) | ||||
| 1990 | { | ||||
| 1991 | struct libnvmf_tid *tid; | ||||
| 1992 | bool_Bool excluded; | ||||
| 1993 | |||||
| 1994 | libnvmf_tid_from_fields(transport, traddr, trsvcid, subsysnqn, | ||||
| 1995 | host_traddr, host_iface, hostnqn, hostid, &tid); | ||||
| 1996 | if (!tid) | ||||
| 1997 | return false0; /* fail-safe: never block on allocation failure */ | ||||
| 1998 | |||||
| 1999 | excluded = libnvmf_exclusion_match(ctx, tid); | ||||
| 2000 | if (excluded) { | ||||
| 2001 | const char *rendered = libnvmf_tid_str(tid); | ||||
| 2002 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "skipping excluded controller %s\n" , rendered ? rendered : subsysnqn) | ||||
| 2003 | "skipping excluded controller %s\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "skipping excluded controller %s\n" , rendered ? rendered : subsysnqn) | ||||
| 2004 | rendered ? rendered : subsysnqn)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "skipping excluded controller %s\n" , rendered ? rendered : subsysnqn); | ||||
| 2005 | } | ||||
| 2006 | libnvmf_tid_free(tid); | ||||
| 2007 | |||||
| 2008 | return excluded; | ||||
| 2009 | } | ||||
| 2010 | |||||
| 2011 | static int nvmf_connect_disc_entry(struct libnvme_host *h, | ||||
| 2012 | struct nvmf_disc_log_entry *e, | ||||
| 2013 | struct libnvme_ctrl_params *params, | ||||
| 2014 | struct libnvme_ctrl **cp) | ||||
| 2015 | { | ||||
| 2016 | struct libnvme_ctrl *c; | ||||
| 2017 | int ret; | ||||
| 2018 | |||||
| 2019 | switch (e->trtype) { | ||||
| 2020 | case NVMF_TRTYPE_RDMA: | ||||
| 2021 | case NVMF_TRTYPE_TCP: | ||||
| 2022 | switch (e->adrfam) { | ||||
| 2023 | case NVMF_ADDR_FAMILY_IP4: | ||||
| 2024 | case NVMF_ADDR_FAMILY_IP6: | ||||
| 2025 | params->traddr = e->traddr; | ||||
| 2026 | params->trsvcid = e->trsvcid; | ||||
| 2027 | break; | ||||
| 2028 | default: | ||||
| 2029 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n" , e->adrfam) | ||||
| 2030 | "skipping unsupported adrfam %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n" , e->adrfam) | ||||
| 2031 | e->adrfam)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n" , e->adrfam); | ||||
| 2032 | return -EINVAL22; | ||||
| 2033 | } | ||||
| 2034 | break; | ||||
| 2035 | case NVMF_TRTYPE_FC: | ||||
| 2036 | switch (e->adrfam) { | ||||
| 2037 | case NVMF_ADDR_FAMILY_FC: | ||||
| 2038 | params->traddr = e->traddr; | ||||
| 2039 | break; | ||||
| 2040 | default: | ||||
| 2041 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR,__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n" , e->adrfam) | ||||
| 2042 | "skipping unsupported adrfam %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n" , e->adrfam) | ||||
| 2043 | e->adrfam)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported adrfam %d\n" , e->adrfam); | ||||
| 2044 | return -EINVAL22; | ||||
| 2045 | } | ||||
| 2046 | break; | ||||
| 2047 | case NVMF_TRTYPE_LOOP: | ||||
| 2048 | params->traddr = strlen(e->traddr) ? e->traddr : NULL((void*)0); | ||||
| 2049 | break; | ||||
| 2050 | default: | ||||
| 2051 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "skipping unsupported transport %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported transport %d\n" , e->trtype) | ||||
| 2052 | e->trtype)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "skipping unsupported transport %d\n" , e->trtype); | ||||
| 2053 | return -EINVAL22; | ||||
| 2054 | } | ||||
| 2055 | |||||
| 2056 | params->transport = libnvmf_trtype_str(e->trtype); | ||||
| 2057 | params->subsysnqn = e->subnqn; | ||||
| 2058 | |||||
| 2059 | libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n" , params->transport, params->traddr, params->trsvcid ) | ||||
| 2060 | "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n" , params->transport, params->traddr, params->trsvcid ) | ||||
| 2061 | params->transport, params->traddr,__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n" , params->transport, params->traddr, params->trsvcid ) | ||||
| 2062 | params->trsvcid)__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "lookup ctrl (transport: %s, traddr: %s, trsvcid %s)\n" , params->transport, params->traddr, params->trsvcid ); | ||||
| 2063 | |||||
| 2064 | if (nvmf_excluded(h->ctx, params->transport, | ||||
| 2065 | params->traddr, params->trsvcid, | ||||
| 2066 | params->subsysnqn, | ||||
| 2067 | params->host_traddr, | ||||
| 2068 | params->host_iface, | ||||
| 2069 | libnvme_host_get_hostnqn(h), | ||||
| 2070 | libnvme_host_get_hostid(h))) | ||||
| 2071 | return -EPERM1; | ||||
| 2072 | |||||
| 2073 | ret = libnvme_create_ctrl(h->ctx, params, &c); | ||||
| 2074 | if (ret) { | ||||
| 2075 | libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, "skipping discovery entry, "__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, " "failed to allocate %s controller with traddr %s\n", params-> transport, params->traddr) | ||||
| 2076 | "failed to allocate %s controller with traddr %s\n",__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, " "failed to allocate %s controller with traddr %s\n", params-> transport, params->traddr) | ||||
| 2077 | params->transport, params->traddr)__libnvme_msg(h->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "skipping discovery entry, " "failed to allocate %s controller with traddr %s\n", params-> transport, params->traddr); | ||||
| 2078 | return ret; | ||||
| 2079 | } | ||||
| 2080 | |||||
| 2081 | /* | ||||
| 2082 | * Self entries (SUBTYPE 03h) are filtered out by the caller before | ||||
| 2083 | * this function is ever reached -- they never need a connection of | ||||
| 2084 | * their own. | ||||
| 2085 | */ | ||||
| 2086 | switch (e->subtype) { | ||||
| 2087 | case NVME_NQN_DISC: | ||||
| 2088 | libnvme_ctrl_set_discovery_ctrl(c, true1); | ||||
| 2089 | libnvme_ctrl_set_unique_discovery_ctrl(c, | ||||
| 2090 | strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")); | ||||
| 2091 | break; | ||||
| 2092 | default: | ||||
| 2093 | libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "unsupported subtype %d\n",__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "unsupported subtype %d\n" , e->subtype) | ||||
| 2094 | e->subtype)__libnvme_msg(h->ctx, LIBNVME_LOG_ERR, ((void*)0), "unsupported subtype %d\n" , e->subtype); | ||||
| 2095 | fallthrough__attribute__((fallthrough)); | ||||
| 2096 | case NVME_NQN_NVME: | ||||
| 2097 | libnvme_ctrl_set_discovery_ctrl(c, false0); | ||||
| 2098 | libnvme_ctrl_set_unique_discovery_ctrl(c, false0); | ||||
| 2099 | break; | ||||
| 2100 | } | ||||
| 2101 | |||||
| 2102 | if (e->treq & NVMF_TREQ_DISABLE_SQFLOW && | ||||
| 2103 | nvmf_check_option(h->ctx, disable_sqflow)({ !__nvmf_supported_options(h->ctx) && h->ctx-> options->disable_sqflow; })) | ||||
| 2104 | c->cfg.disable_sqflow = true1; | ||||
| 2105 | |||||
| 2106 | /* update tls or concat */ | ||||
| 2107 | nvmf_update_tls_concat(e, c, h); | ||||
| 2108 | |||||
| 2109 | ret = libnvmf_add_ctrl(h, c); | ||||
| 2110 | if (!ret) { | ||||
| 2111 | *cp = c; | ||||
| 2112 | return 0; | ||||
| 2113 | } | ||||
| 2114 | |||||
| 2115 | if (ret == EINVAL22 && c->cfg.disable_sqflow) { | ||||
| 2116 | /* disable_sqflow is unrecognized option on older kernels */ | ||||
| 2117 | libnvme_msg(h->ctx, LIBNVME_LOG_INFO, "failed to connect controller, "__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "failed to connect controller, " "retry with disabling SQ flow control\n") | ||||
| 2118 | "retry with disabling SQ flow control\n")__libnvme_msg(h->ctx, LIBNVME_LOG_INFO, ((void*)0), "failed to connect controller, " "retry with disabling SQ flow control\n"); | ||||
| 2119 | c->cfg.disable_sqflow = false0; | ||||
| 2120 | ret = libnvmf_add_ctrl(h, c); | ||||
| 2121 | if (!ret) { | ||||
| 2122 | *cp = c; | ||||
| 2123 | return 0; | ||||
| 2124 | } | ||||
| 2125 | } | ||||
| 2126 | libnvme_free_ctrl(c); | ||||
| 2127 | return -ENOENT2; | ||||
| 2128 | } | ||||
| 2129 | |||||
| 2130 | /* | ||||
| 2131 | * Most of nvmf_discovery_log is reserved, so only fetch the initial bytes. | ||||
| 2132 | * 8 bytes for GENCTR, 8 for NUMREC, and 2 for RECFMT. | ||||
| 2133 | * Since only multiples of 4 bytes are allowed, round 18 up to 20. | ||||
| 2134 | */ | ||||
| 2135 | #define DISCOVERY_HEADER_LEN20 20 | ||||
| 2136 | |||||
| 2137 | static int nvme_discovery_log(struct libnvme_ctrl *ctrl, | ||||
| 2138 | const struct libnvmf_discovery_args *args, | ||||
| 2139 | struct nvmf_discovery_log **logp) | ||||
| 2140 | { | ||||
| 2141 | struct libnvme_global_ctx *ctx = ctrl->ctx; | ||||
| 2142 | struct nvmf_discovery_log *log; | ||||
| 2143 | int retries = 0; | ||||
| 2144 | int err; | ||||
| 2145 | const char *name = libnvme_ctrl_get_name(ctrl); | ||||
| 2146 | uint64_t genctr, numrec; | ||||
| 2147 | struct libnvme_transport_handle *hdl; | ||||
| 2148 | |||||
| 2149 | hdl = libnvme_ctrl_get_transport_handle(ctrl); | ||||
| 2150 | struct libnvme_passthru_cmd cmd; | ||||
| 2151 | |||||
| 2152 | log = libnvme_alloc(sizeof(*log)); | ||||
| 2153 | if (!log) { | ||||
| 2154 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not allocate memory for discovery log header\n" ) | ||||
| 2155 | "could not allocate memory for discovery log header\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not allocate memory for discovery log header\n" ); | ||||
| 2156 | return -ENOMEM12; | ||||
| 2157 | } | ||||
| 2158 | |||||
| 2159 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s: get header (try %d/%d)\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get header (try %d/%d)\n" , name, retries, args->max_retries) | ||||
| 2160 | name, retries, args->max_retries)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get header (try %d/%d)\n" , name, retries, args->max_retries); | ||||
| 2161 | nvme_init_get_log_discovery(&cmd, 0, log, DISCOVERY_HEADER_LEN20); | ||||
| 2162 | err = libnvme_get_log(hdl, &cmd, false0, DISCOVERY_HEADER_LEN20); | ||||
| 2163 | if (err) { | ||||
| 2164 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err) | ||||
| 2165 | "%s: discover try %d/%d failed, errno %d status 0x%x\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err) | ||||
| 2166 | name, retries, args->max_retries, errno, err)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err); | ||||
| 2167 | goto out_free_log; | ||||
| 2168 | } | ||||
| 2169 | |||||
| 2170 | do { | ||||
| 2171 | size_t entries_size; | ||||
| 2172 | |||||
| 2173 | numrec = le64_to_cpu(log->numrec); | ||||
| 2174 | genctr = le64_to_cpu(log->genctr); | ||||
| 2175 | |||||
| 2176 | if (numrec == 0) | ||||
| 2177 | break; | ||||
| 2178 | |||||
| 2179 | if (numrec > (SIZE_MAX(18446744073709551615UL) - sizeof(*log)) / sizeof(*log->entries)) { | ||||
| 2180 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: don't trust record count %" "l" "u" "\n", name, numrec) | ||||
| 2181 | "%s: don't trust record count %" PRIu64 "\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: don't trust record count %" "l" "u" "\n", name, numrec) | ||||
| 2182 | name, numrec)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: don't trust record count %" "l" "u" "\n", name, numrec); | ||||
| 2183 | err = -EINVAL22; | ||||
| 2184 | goto out_free_log; | ||||
| 2185 | } | ||||
| 2186 | |||||
| 2187 | libnvme_free(log); | ||||
| 2188 | entries_size = sizeof(*log->entries) * numrec; | ||||
| 2189 | log = libnvme_alloc(sizeof(*log) + entries_size); | ||||
| 2190 | if (!log) { | ||||
| 2191 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not alloc memory for discovery log page\n" ) | ||||
| 2192 | "could not alloc memory for discovery log page\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not alloc memory for discovery log page\n" ); | ||||
| 2193 | return -ENOMEM12; | ||||
| 2194 | } | ||||
| 2195 | |||||
| 2196 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %" "l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr ) | ||||
| 2197 | "%s: get %" PRIu64 " records (genctr %" PRIu64 ")\n",__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %" "l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr ) | ||||
| 2198 | name, numrec, genctr)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get %" "l" "u" " records (genctr %" "l" "u" ")\n", name, numrec, genctr ); | ||||
| 2199 | |||||
| 2200 | nvme_init_get_log_discovery(&cmd, sizeof(*log), log->entries, entries_size); | ||||
| 2201 | cmd.cdw10 |= NVME_FIELD_ENCODE(args->lsp,(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) << (NVME_LOG_CDW10_LSP_SHIFT)) | ||||
| 2202 | NVME_LOG_CDW10_LSP_SHIFT,(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) << (NVME_LOG_CDW10_LSP_SHIFT)) | ||||
| 2203 | NVME_LOG_CDW10_LSP_MASK)(((__u32)(args->lsp) & (NVME_LOG_CDW10_LSP_MASK)) << (NVME_LOG_CDW10_LSP_SHIFT)); | ||||
| 2204 | err = libnvme_get_log(hdl, &cmd, false0, NVME_LOG_PAGE_PDU_SIZE4096); | ||||
| 2205 | if (err) { | ||||
| 2206 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err) | ||||
| 2207 | "%s: discover try %d/%d failed, errno %d status 0x%x\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err) | ||||
| 2208 | name, retries, args->max_retries, errno, err)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err); | ||||
| 2209 | goto out_free_log; | ||||
| 2210 | } | ||||
| 2211 | |||||
| 2212 | /* | ||||
| 2213 | * If the log page was read with multiple Get Log Page commands, | ||||
| 2214 | * genctr must be checked afterwards to ensure atomicity | ||||
| 2215 | */ | ||||
| 2216 | libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "%s: get header again\n", name)__libnvme_msg(ctx, LIBNVME_LOG_DEBUG, ((void*)0), "%s: get header again\n" , name); | ||||
| 2217 | |||||
| 2218 | nvme_init_get_log_discovery(&cmd, 0, log, DISCOVERY_HEADER_LEN20); | ||||
| 2219 | err = libnvme_get_log(hdl, &cmd, false0, DISCOVERY_HEADER_LEN20); | ||||
| 2220 | if (err) { | ||||
| 2221 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err) | ||||
| 2222 | "%s: discover try %d/%d failed, errno %d status 0x%x\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err) | ||||
| 2223 | name, retries, args->max_retries, errno, err)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover try %d/%d failed, errno %d status 0x%x\n" , name, retries, args->max_retries, (*__errno_location ()) , err); | ||||
| 2224 | goto out_free_log; | ||||
| 2225 | } | ||||
| 2226 | } while (genctr != le64_to_cpu(log->genctr) && | ||||
| 2227 | ++retries < args->max_retries); | ||||
| 2228 | |||||
| 2229 | if (genctr != le64_to_cpu(log->genctr)) { | ||||
| 2230 | libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: discover genctr mismatch\n", name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: discover genctr mismatch\n" , name); | ||||
| 2231 | err = -EAGAIN11; | ||||
| 2232 | } else if (numrec != le64_to_cpu(log->numrec)) { | ||||
| 2233 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly " "from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu (log->numrec)) | ||||
| 2234 | "%s: numrec changed unexpectedly "__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly " "from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu (log->numrec)) | ||||
| 2235 | "from %" PRIu64 " to %" PRIu64 "\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly " "from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu (log->numrec)) | ||||
| 2236 | name, numrec, le64_to_cpu(log->numrec))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: numrec changed unexpectedly " "from %" "l" "u" " to %" "l" "u" "\n", name, numrec, le64_to_cpu (log->numrec)); | ||||
| 2237 | err = -EBADSLT57; | ||||
| 2238 | } else { | ||||
| 2239 | *logp = log; | ||||
| 2240 | return 0; | ||||
| 2241 | } | ||||
| 2242 | |||||
| 2243 | out_free_log: | ||||
| 2244 | libnvme_free(log); | ||||
| 2245 | return err; | ||||
| 2246 | } | ||||
| 2247 | |||||
| 2248 | static void sanitize_discovery_log_entry(struct libnvme_global_ctx *ctx, | ||||
| 2249 | struct nvmf_disc_log_entry *e) | ||||
| 2250 | { | ||||
| 2251 | /* | ||||
| 2252 | * Force a NUL terminator into the last byte. Every buffer here is | ||||
| 2253 | * far larger than any value a compliant peer can send, so this only | ||||
| 2254 | * ever truncates a non-compliant one. The purpose is to keep the | ||||
| 2255 | * field from being read as an unbounded char *. This must run | ||||
| 2256 | * before shr_rtrim() below, which relies on the field already being | ||||
| 2257 | * terminated. | ||||
| 2258 | */ | ||||
| 2259 | e->trsvcid[sizeof(e->trsvcid) - 1] = '\0'; | ||||
| 2260 | e->subnqn[sizeof(e->subnqn) - 1] = '\0'; | ||||
| 2261 | e->traddr[sizeof(e->traddr) - 1] = '\0'; | ||||
| 2262 | |||||
| 2263 | shr_rtrim(e->trsvcid); | ||||
| 2264 | shr_rtrim(e->traddr); | ||||
| 2265 | shr_rtrim(e->subnqn); | ||||
| 2266 | |||||
| 2267 | /* | ||||
| 2268 | * Report traddr always in 'nn-0x:pn-0x' format, but some discovery logs | ||||
| 2269 | * provide 'nn-0x,pn-0x'. | ||||
| 2270 | */ | ||||
| 2271 | if (e->trtype == NVMF_TRTYPE_FC) { | ||||
| 2272 | char *comma = strchr(e->traddr, ',')_Generic (0 ? (e->traddr) : (void *) 1, const void *: (const char *) (strchr (e->traddr, ',')), default: strchr (e-> traddr, ',')); | ||||
| 2273 | |||||
| 2274 | if (comma) { | ||||
| 2275 | libnvme_msg(ctx, LIBNVME_LOG_WARN,__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "invalid traddr separator ',' instead ':', fixing it" ) | ||||
| 2276 | "invalid traddr separator ',' instead ':', fixing it")__libnvme_msg(ctx, LIBNVME_LOG_WARN, ((void*)0), "invalid traddr separator ',' instead ':', fixing it" ); | ||||
| 2277 | *comma = ':'; | ||||
| 2278 | } | ||||
| 2279 | } | ||||
| 2280 | } | ||||
| 2281 | |||||
| 2282 | __shr_public__attribute__((visibility("default"))) int libnvmf_get_discovery_log(struct libnvme_ctrl *ctrl, | ||||
| 2283 | const struct libnvmf_discovery_args *args, | ||||
| 2284 | struct nvmf_discovery_log **logp) | ||||
| 2285 | { | ||||
| 2286 | static const struct libnvmf_discovery_args defaults = { | ||||
| 2287 | .max_retries = 6, | ||||
| 2288 | .lsp = NVMF_LOG_DISC_LSP_NONE, | ||||
| 2289 | }; | ||||
| 2290 | struct nvmf_discovery_log *log; | ||||
| 2291 | int err; | ||||
| 2292 | |||||
| 2293 | if (!args) | ||||
| 2294 | args = &defaults; | ||||
| 2295 | |||||
| 2296 | err = nvme_discovery_log(ctrl, args, &log); | ||||
| 2297 | if (err) | ||||
| 2298 | return err; | ||||
| 2299 | |||||
| 2300 | for (int i = 0; i < le64_to_cpu(log->numrec); i++) | ||||
| 2301 | sanitize_discovery_log_entry(ctrl->ctx, &log->entries[i]); | ||||
| 2302 | |||||
| 2303 | *logp = log; | ||||
| 2304 | return 0; | ||||
| 2305 | } | ||||
| 2306 | |||||
| 2307 | |||||
| 2308 | /** | ||||
| 2309 | * nvmf_get_tel() - Calculate the amount of memory needed for a DIE. | ||||
| 2310 | * @hostsymname: Symbolic name (may be NULL) | ||||
| 2311 | * | ||||
| 2312 | * Each Discovery Information Entry (DIE) must contain at a minimum an | ||||
| 2313 | * Extended Attribute for the HostID. The Entry may optionally contain an | ||||
| 2314 | * Extended Attribute for the Symbolic Name. | ||||
| 2315 | * | ||||
| 2316 | * Return: Total Entry Length | ||||
| 2317 | */ | ||||
| 2318 | static __u32 nvmf_get_tel(const char *hostsymname) | ||||
| 2319 | { | ||||
| 2320 | __u32 tel = sizeof(struct nvmf_ext_die); | ||||
| 2321 | __u16 len; | ||||
| 2322 | |||||
| 2323 | /* Host ID is mandatory */ | ||||
| 2324 | tel += libnvmf_exat_size(NVME_UUID_LEN16); | ||||
| 2325 | |||||
| 2326 | /* Symbolic name is optional */ | ||||
| 2327 | len = hostsymname ? strlen(hostsymname) : 0; | ||||
| 2328 | if (len) | ||||
| 2329 | tel += libnvmf_exat_size(len); | ||||
| 2330 | |||||
| 2331 | return tel; | ||||
| 2332 | } | ||||
| 2333 | |||||
| 2334 | /** | ||||
| 2335 | * nvmf_fill_die() - Fill a Discovery Information Entry. | ||||
| 2336 | * @die: Pointer to Discovery Information Entry to be filled | ||||
| 2337 | * @h: Pointer to the host data structure | ||||
| 2338 | * @tel: Length of the DIE | ||||
| 2339 | * @trtype: Transport type | ||||
| 2340 | * @adrfam: Address family | ||||
| 2341 | * @reg_addr: Address to register. Setting this to an empty string tells | ||||
| 2342 | * the DC to infer address from the source address of the socket. | ||||
| 2343 | * @tsas: Transport Specific Address Subtype for the address being | ||||
| 2344 | * registered. | ||||
| 2345 | */ | ||||
| 2346 | static void nvmf_fill_die(struct nvmf_ext_die *die, struct libnvme_host *h, | ||||
| 2347 | __u32 tel, __u8 trtype, __u8 adrfam, | ||||
| 2348 | const char *reg_addr, union nvmf_tsas *tsas) | ||||
| 2349 | { | ||||
| 2350 | __u16 numexat = 0; | ||||
| 2351 | size_t symname_len; | ||||
| 2352 | struct nvmf_ext_attr *exat; | ||||
| 2353 | |||||
| 2354 | die->tel = cpu_to_le32(tel); | ||||
| 2355 | die->trtype = trtype; | ||||
| 2356 | die->adrfam = adrfam; | ||||
| 2357 | |||||
| 2358 | memcpy(die->nqn, h->hostnqn, MIN(sizeof(die->nqn), strlen(h->hostnqn))(((sizeof(die->nqn))<(strlen(h->hostnqn)))?(sizeof(die ->nqn)):(strlen(h->hostnqn)))); | ||||
| 2359 | memcpy(die->traddr, reg_addr, MIN(sizeof(die->traddr), strlen(reg_addr))(((sizeof(die->traddr))<(strlen(reg_addr)))?(sizeof(die ->traddr)):(strlen(reg_addr)))); | ||||
| 2360 | |||||
| 2361 | if (tsas) | ||||
| 2362 | memcpy(&die->tsas, tsas, sizeof(die->tsas)); | ||||
| 2363 | |||||
| 2364 | /* Extended Attribute for the HostID (mandatory) */ | ||||
| 2365 | numexat++; | ||||
| 2366 | exat = die->exat; | ||||
| 2367 | exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID); | ||||
| 2368 | exat->exatlen = cpu_to_le16(libnvmf_exat_len(NVME_UUID_LEN16)); | ||||
| 2369 | libnvme_uuid_from_string(h->hostid, exat->exatval); | ||||
| 2370 | |||||
| 2371 | /* Extended Attribute for the Symbolic Name (optional) */ | ||||
| 2372 | symname_len = h->hostsymname ? strlen(h->hostsymname) : 0; | ||||
| 2373 | if (symname_len) { | ||||
| 2374 | __u16 exatlen = libnvmf_exat_len(symname_len); | ||||
| 2375 | |||||
| 2376 | numexat++; | ||||
| 2377 | exat = libnvmf_exat_ptr_next(exat); | ||||
| 2378 | exat->exattype = cpu_to_le16(NVMF_EXATTYPE_SYMNAME); | ||||
| 2379 | exat->exatlen = cpu_to_le16(exatlen); | ||||
| 2380 | memcpy(exat->exatval, h->hostsymname, symname_len); | ||||
| 2381 | /* Per Base specs, ASCII strings must be padded with spaces */ | ||||
| 2382 | memset(&exat->exatval[symname_len], ' ', exatlen - symname_len); | ||||
| 2383 | } | ||||
| 2384 | |||||
| 2385 | die->numexat = cpu_to_le16(numexat); | ||||
| 2386 | } | ||||
| 2387 | |||||
| 2388 | /** | ||||
| 2389 | * nvmf_dim() - Explicit reg, dereg, reg-update issuing DIM | ||||
| 2390 | * @c: Host NVMe controller instance maintaining the admin queue used to | ||||
| 2391 | * submit the DIM command to the DC. | ||||
| 2392 | * @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to | ||||
| 2393 | * perform a Registration, Deregistration, or Registration-update. | ||||
| 2394 | * @trtype: Transport type (&enum nvmf_trtype - must be NVMF_TRTYPE_TCP) | ||||
| 2395 | * @adrfam: Address family (&enum nvmf_addr_family) | ||||
| 2396 | * @reg_addr: Address to register. Setting this to an empty string tells | ||||
| 2397 | * the DC to infer address from the source address of the socket. | ||||
| 2398 | * @tsas: Transport Specific Address Subtype for the address being | ||||
| 2399 | * registered. | ||||
| 2400 | * @result: Location where to save the command-specific result returned by | ||||
| 2401 | * the discovery controller. | ||||
| 2402 | * | ||||
| 2403 | * Perform explicit registration, deregistration, or | ||||
| 2404 | * registration-update (specified by @tas) by sending a Discovery | ||||
| 2405 | * Information Management (DIM) command to the Discovery Controller | ||||
| 2406 | * (DC). | ||||
| 2407 | * | ||||
| 2408 | * Return: 0 on success; on failure -1 is returned and errno is set | ||||
| 2409 | */ | ||||
| 2410 | static int nvmf_dim(struct libnvme_ctrl *c, enum nvmf_dim_tas tas, __u8 trtype, | ||||
| 2411 | __u8 adrfam, const char *reg_addr, union nvmf_tsas *tsas, | ||||
| 2412 | __u32 *result) | ||||
| 2413 | { | ||||
| 2414 | struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0); | ||||
| 2415 | __cleanup_free__attribute__((cleanup(shr_freep))) struct nvmf_dim_data *dim = NULL((void*)0); | ||||
| 2416 | struct libnvme_transport_handle *hdl = libnvme_ctrl_get_transport_handle(c); | ||||
| 2417 | struct libnvme_passthru_cmd cmd; | ||||
| 2418 | struct nvmf_ext_die *die; | ||||
| 2419 | __u32 tdl; | ||||
| 2420 | __u32 tel; | ||||
| 2421 | int ret; | ||||
| 2422 | |||||
| 2423 | if (!c->s) { | ||||
| 2424 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n" , c->name) | ||||
| 2425 | "%s: failed to perform DIM. subsystem undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n" , c->name) | ||||
| 2426 | c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. subsystem undefined.\n" , c->name); | ||||
| 2427 | return -EINVAL22; | ||||
| 2428 | } | ||||
| 2429 | |||||
| 2430 | if (!c->s->h) { | ||||
| 2431 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n" , c->name) | ||||
| 2432 | "%s: failed to perform DIM. host undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n" , c->name) | ||||
| 2433 | c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. host undefined.\n" , c->name); | ||||
| 2434 | return -EINVAL22; | ||||
| 2435 | } | ||||
| 2436 | |||||
| 2437 | if (!c->s->h->hostid) { | ||||
| 2438 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n" , c->name) | ||||
| 2439 | "%s: failed to perform DIM. hostid undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n" , c->name) | ||||
| 2440 | c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostid undefined.\n" , c->name); | ||||
| 2441 | return -EINVAL22; | ||||
| 2442 | } | ||||
| 2443 | |||||
| 2444 | if (!c->s->h->hostnqn) { | ||||
| 2445 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n" , c->name) | ||||
| 2446 | "%s: failed to perform DIM. hostnqn undefined.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n" , c->name) | ||||
| 2447 | c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: failed to perform DIM. hostnqn undefined.\n" , c->name); | ||||
| 2448 | return -EINVAL22; | ||||
| 2449 | } | ||||
| 2450 | |||||
| 2451 | if (strcmp(c->transport, "tcp")) { | ||||
| 2452 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n" , c->name) | ||||
| 2453 | "%s: DIM only supported for TCP connections.\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n" , c->name) | ||||
| 2454 | c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "%s: DIM only supported for TCP connections.\n" , c->name); | ||||
| 2455 | return -EINVAL22; | ||||
| 2456 | } | ||||
| 2457 | |||||
| 2458 | /* Register one Discovery Information Entry (DIE) of size TEL */ | ||||
| 2459 | tel = nvmf_get_tel(c->s->h->hostsymname); | ||||
| 2460 | tdl = sizeof(struct nvmf_dim_data) + tel; | ||||
| 2461 | |||||
| 2462 | dim = (struct nvmf_dim_data *)calloc(1, tdl); | ||||
| 2463 | if (!dim) { | ||||
| 2464 | return -ENOMEM12; | ||||
| 2465 | } | ||||
| 2466 | |||||
| 2467 | dim->tdl = cpu_to_le32(tdl); | ||||
| 2468 | dim->nument = cpu_to_le64(1); /* only one DIE to register */ | ||||
| 2469 | dim->entfmt = cpu_to_le16(NVMF_DIM_ENTFMT_EXTENDED); | ||||
| 2470 | dim->etype = cpu_to_le16(NVMF_DIM_ETYPE_HOST); | ||||
| 2471 | dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */ | ||||
| 2472 | |||||
| 2473 | memcpy(dim->eid, c->s->h->hostnqn, | ||||
| 2474 | MIN(sizeof(dim->eid), strlen(c->s->h->hostnqn))(((sizeof(dim->eid))<(strlen(c->s->h->hostnqn) ))?(sizeof(dim->eid)):(strlen(c->s->h->hostnqn)))); | ||||
| 2475 | |||||
| 2476 | ret = libnvmf_get_entity_name(dim->ename, sizeof(dim->ename)); | ||||
| 2477 | if (ret < 0) | ||||
| 2478 | libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: Failed to retrieve ENAME. %s.\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME. %s.\n" , c->name, libnvme_strerror(-ret)) | ||||
| 2479 | c->name, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME. %s.\n" , c->name, libnvme_strerror(-ret)); | ||||
| 2480 | else if (ret == 0) | ||||
| 2481 | libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: Failed to retrieve ENAME.\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME.\n" , c->name) | ||||
| 2482 | c->name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve ENAME.\n" , c->name); | ||||
| 2483 | |||||
| 2484 | ret = libnvmf_get_entity_version(dim->ever, sizeof(dim->ever)); | ||||
| 2485 | if (ret <= 0) | ||||
| 2486 | libnvme_msg(ctx, LIBNVME_LOG_INFO, "%s: Failed to retrieve EVER.\n", c->name)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "%s: Failed to retrieve EVER.\n" , c->name); | ||||
| 2487 | |||||
| 2488 | die = &dim->die->extended; | ||||
| 2489 | nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas); | ||||
| 2490 | |||||
| 2491 | nvme_init_dim_send(&cmd, tas, dim, tdl); | ||||
| 2492 | return libnvme_exec_admin_passthru(hdl, &cmd); | ||||
| 2493 | } | ||||
| 2494 | |||||
| 2495 | /** | ||||
| 2496 | * nvme_get_adrfam() - Get address family for the address we're registering | ||||
| 2497 | * with the DC. | ||||
| 2498 | * | ||||
| 2499 | * We retrieve this info from the socket itself. If we can't get the source | ||||
| 2500 | * address from the socket, then we'll infer the address family from the | ||||
| 2501 | * address of the DC since the DC address has the same address family. | ||||
| 2502 | * | ||||
| 2503 | * @c: Host NVMe controller instance maintaining the admin queue used to | ||||
| 2504 | * submit the DIM command to the DC. | ||||
| 2505 | * | ||||
| 2506 | * Return: The address family of the source address associated with the | ||||
| 2507 | * socket connected to the DC. | ||||
| 2508 | */ | ||||
| 2509 | static __u8 nvme_get_adrfam(struct libnvme_ctrl *c) | ||||
| 2510 | { | ||||
| 2511 | struct sockaddr_storage addr; | ||||
| 2512 | __u8 adrfam = NVMF_ADDR_FAMILY_IP4; | ||||
| 2513 | struct libnvme_global_ctx *ctx = c->s && c->s->h ? c->s->h->ctx : NULL((void*)0); | ||||
| 2514 | |||||
| 2515 | if (!inet_pton_with_scope(ctx, AF_UNSPEC0, c->traddr, c->trsvcid, &addr)) { | ||||
| 2516 | if (addr.ss_family == AF_INET610) | ||||
| 2517 | adrfam = NVMF_ADDR_FAMILY_IP6; | ||||
| 2518 | } | ||||
| 2519 | |||||
| 2520 | return adrfam; | ||||
| 2521 | } | ||||
| 2522 | |||||
| 2523 | /* These string definitions must match with the kernel */ | ||||
| 2524 | static const char *cntrltype_str[] = { | ||||
| 2525 | [NVME_CTRL_CNTRLTYPE_IO] = "io", | ||||
| 2526 | [NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery", | ||||
| 2527 | [NVME_CTRL_CNTRLTYPE_ADMIN] = "admin", | ||||
| 2528 | }; | ||||
| 2529 | |||||
| 2530 | static const char *dctype_str[] = { | ||||
| 2531 | [NVME_CTRL_DCTYPE_NOT_REPORTED] = "none", | ||||
| 2532 | [NVME_CTRL_DCTYPE_DDC] = "ddc", | ||||
| 2533 | [NVME_CTRL_DCTYPE_CDC] = "cdc", | ||||
| 2534 | }; | ||||
| 2535 | |||||
| 2536 | /** | ||||
| 2537 | * nvme_fetch_cntrltype_dctype_from_id - Get cntrltype and dctype from identify command | ||||
| 2538 | * @c: Controller instance | ||||
| 2539 | * | ||||
| 2540 | * On legacy kernels the cntrltype and dctype are not exposed through the | ||||
| 2541 | * sysfs. We must get them directly from the controller by performing an | ||||
| 2542 | * identify command. | ||||
| 2543 | */ | ||||
| 2544 | static int nvme_fetch_cntrltype_dctype_from_id(struct libnvme_ctrl *c) | ||||
| 2545 | { | ||||
| 2546 | __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id = NULL((void*)0); | ||||
| 2547 | const char *val; | ||||
| 2548 | int ret; | ||||
| 2549 | |||||
| 2550 | id = libnvme_alloc(sizeof(*id)); | ||||
| 2551 | if (!id) | ||||
| 2552 | return -ENOMEM12; | ||||
| 2553 | |||||
| 2554 | ret = libnvme_ctrl_identify(c, id); | ||||
| 2555 | if (ret) | ||||
| 2556 | return ret; | ||||
| 2557 | |||||
| 2558 | if (libnvme_ctrl_get_cntrltype(c, &val, NULL((void*)0))) { | ||||
| 2559 | if (id->cntrltype > NVME_CTRL_CNTRLTYPE_ADMIN || !cntrltype_str[id->cntrltype]) | ||||
| 2560 | libnvme_ctrl_set_cntrltype(c, "reserved"); | ||||
| 2561 | else | ||||
| 2562 | libnvme_ctrl_set_cntrltype(c, | ||||
| 2563 | cntrltype_str[id->cntrltype]); | ||||
| 2564 | } | ||||
| 2565 | |||||
| 2566 | if (libnvme_ctrl_get_dctype(c, &val, NULL((void*)0))) { | ||||
| 2567 | if (id->dctype > NVME_CTRL_DCTYPE_CDC || !dctype_str[id->dctype]) | ||||
| 2568 | libnvme_ctrl_set_dctype(c, "reserved"); | ||||
| 2569 | else | ||||
| 2570 | libnvme_ctrl_set_dctype(c, dctype_str[id->dctype]); | ||||
| 2571 | } | ||||
| 2572 | return 0; | ||||
| 2573 | } | ||||
| 2574 | |||||
| 2575 | __shr_public__attribute__((visibility("default"))) bool_Bool libnvmf_is_registration_supported(struct libnvme_ctrl *c) | ||||
| 2576 | { | ||||
| 2577 | const char *cntrltype, *dctype; | ||||
| 2578 | |||||
| 2579 | if (libnvme_ctrl_get_cntrltype(c, &cntrltype, NULL((void*)0)) || | ||||
| 2580 | libnvme_ctrl_get_dctype(c, &dctype, NULL((void*)0))) | ||||
| 2581 | if (nvme_fetch_cntrltype_dctype_from_id(c)) | ||||
| 2582 | return false0; | ||||
| 2583 | |||||
| 2584 | libnvme_ctrl_get_dctype(c, &dctype, NULL((void*)0)); | ||||
| 2585 | return shr_streq0(dctype, "ddc") || shr_streq0(dctype, "cdc"); | ||||
| 2586 | } | ||||
| 2587 | |||||
| 2588 | __shr_public__attribute__((visibility("default"))) int libnvmf_register_ctrl( | ||||
| 2589 | struct libnvme_ctrl *c, enum nvmf_dim_tas tas, __u32 *result) | ||||
| 2590 | { | ||||
| 2591 | if (!libnvmf_is_registration_supported(c)) | ||||
| 2592 | return -ENOTSUP95; | ||||
| 2593 | |||||
| 2594 | /* We're registering our source address with the DC. To do | ||||
| 2595 | * that, we can simply send an empty string. This tells the DC | ||||
| 2596 | * to retrieve the source address from the socket and use that | ||||
| 2597 | * as the registration address. | ||||
| 2598 | */ | ||||
| 2599 | return nvmf_dim(c, tas, NVMF_TRTYPE_TCP, nvme_get_adrfam(c), "", NULL((void*)0), result); | ||||
| 2600 | } | ||||
| 2601 | |||||
| 2602 | #define IS_XDIGIT(c)((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) ((c >= '0' && c <= '9') || \ | ||||
| 2603 | (c >= 'A' && c <= 'F') || \ | ||||
| 2604 | (c >= 'a' && c <= 'f')) | ||||
| 2605 | #define XDIGIT_VAL(c)((c >= '0' && c <= '9') ? c - '0' : ( (c >= 'A' && c <= 'F') ? c - 'A' + 10 : c - 'a' + 10)) ((c >= '0' && c <= '9') ? c - '0' : ( \ | ||||
| 2606 | (c >= 'A' && c <= 'F') ? c - 'A' + 10 : c - 'a' + 10)) | ||||
| 2607 | |||||
| 2608 | /* returns newly allocated string */ | ||||
| 2609 | static char *unescape_uri(const char *str, int len) | ||||
| 2610 | { | ||||
| 2611 | char *dst; | ||||
| 2612 | int l; | ||||
| 2613 | int i, j; | ||||
| 2614 | |||||
| 2615 | l = len > 0 ? len : strlen(str); | ||||
| 2616 | dst = malloc(l + 1); | ||||
| 2617 | for (i = 0, j = 0; i < l; i++, j++) { | ||||
| 2618 | if (str[i] == '%' && i + 2 < l && | ||||
| 2619 | IS_XDIGIT(str[i + 1])((str[i + 1] >= '0' && str[i + 1] <= '9') || (str [i + 1] >= 'A' && str[i + 1] <= 'F') || (str[i + 1] >= 'a' && str[i + 1] <= 'f')) && IS_XDIGIT(str[i + 2])((str[i + 2] >= '0' && str[i + 2] <= '9') || (str [i + 2] >= 'A' && str[i + 2] <= 'F') || (str[i + 2] >= 'a' && str[i + 2] <= 'f'))) { | ||||
| 2620 | dst[j] = (XDIGIT_VAL(str[i + 1])((str[i + 1] >= '0' && str[i + 1] <= '9') ? str [i + 1] - '0' : ( (str[i + 1] >= 'A' && str[i + 1] <= 'F') ? str[i + 1] - 'A' + 10 : str[i + 1] - 'a' + 10)) << 4) + | ||||
| 2621 | XDIGIT_VAL(str[i + 2])((str[i + 2] >= '0' && str[i + 2] <= '9') ? str [i + 2] - '0' : ( (str[i + 2] >= 'A' && str[i + 2] <= 'F') ? str[i + 2] - 'A' + 10 : str[i + 2] - 'a' + 10)); | ||||
| 2622 | i += 2; | ||||
| 2623 | } else | ||||
| 2624 | dst[j] = str[i]; | ||||
| 2625 | } | ||||
| 2626 | dst[j] = '\0'; | ||||
| 2627 | return dst; | ||||
| 2628 | } | ||||
| 2629 | |||||
| 2630 | __shr_public__attribute__((visibility("default"))) int libnvmf_uri_parse( | ||||
| 2631 | const char *str, struct libnvmf_uri **urip) | ||||
| 2632 | { | ||||
| 2633 | __cleanup_uri__attribute__((cleanup(free_uri))) struct libnvmf_uri *uri = NULL((void*)0); | ||||
| 2634 | __cleanup_free__attribute__((cleanup(shr_freep))) char *scheme = NULL((void*)0); | ||||
| 2635 | __cleanup_free__attribute__((cleanup(shr_freep))) char *authority = NULL((void*)0); | ||||
| 2636 | __cleanup_free__attribute__((cleanup(shr_freep))) char *path = NULL((void*)0); | ||||
| 2637 | __cleanup_free__attribute__((cleanup(shr_freep))) char *h = NULL((void*)0); | ||||
| 2638 | const char *host; | ||||
| 2639 | int i; | ||||
| 2640 | |||||
| 2641 | /* As defined in Boot Specification rev. 1.0: | ||||
| 2642 | * | ||||
| 2643 | * section 1.5.7: NVMe-oF URI Format | ||||
| 2644 | * nvme+tcp://192.168.1.1:4420/ | ||||
| 2645 | * nvme+tcp://[FE80::1010]:4420/ | ||||
| 2646 | * | ||||
| 2647 | * section 3.1.2.5.3: DHCP Root-Path - a hierarchical NVMe-oF URI Format | ||||
| 2648 | * NVME<+PROTOCOL>://<SERVERNAME/IP>[:TRANSPORT PORT]/<SUBSYS NQN>/<NID> | ||||
| 2649 | * or | ||||
| 2650 | * NVME<+PROTOCOL>://<DISCOVERY CONTROLLER ADDRESS>[:DISCOVERY- | ||||
| 2651 | * -CONTROLLER PORT]/NQN.2014-08.ORG.NVMEXPRESS.DISCOVERY/<NID> | ||||
| 2652 | */ | ||||
| 2653 | |||||
| 2654 | uri = calloc(1, sizeof(struct libnvmf_uri)); | ||||
| 2655 | if (!uri) | ||||
| 2656 | return -ENOMEM12; | ||||
| 2657 | |||||
| 2658 | if (sscanf(str, "%m[^:/]://%m[^/?#]%ms", | ||||
| 2659 | &scheme, &authority, &path) < 2) | ||||
| 2660 | return -EINVAL22; | ||||
| 2661 | |||||
| 2662 | if (sscanf(scheme, "%m[^+]+%ms", | ||||
| 2663 | &uri->scheme, &uri->protocol) < 1) | ||||
| 2664 | return -EINVAL22; | ||||
| 2665 | |||||
| 2666 | /* | ||||
| 2667 | * The scheme and transport protocol are a fixed, small vocabulary | ||||
| 2668 | * ("nvme", "tcp"/"rdma"/"fc"), unlike the host/subsystem NQNs that can | ||||
| 2669 | * also appear in this URI -- so, unlike those, normalizing case here | ||||
| 2670 | * costs nothing. Boot Specification section 3.1.2.5.3 spells the DHCP | ||||
| 2671 | * root-path form in upper case ("NVME<+PROTOCOL>://..."), and both that | ||||
| 2672 | * and mixed case are otherwise rejected by validate_uri()'s | ||||
| 2673 | * case-sensitive comparisons below. | ||||
| 2674 | */ | ||||
| 2675 | shr_strtolower(uri->scheme); | ||||
| 2676 | if (uri->protocol) | ||||
| 2677 | shr_strtolower(uri->protocol); | ||||
| 2678 | |||||
| 2679 | /* split userinfo */ | ||||
| 2680 | host = strrchr(authority, '@')_Generic (0 ? (authority) : (void *) 1, const void *: (const char *) (strrchr (authority, '@')), default: strrchr (authority, '@' )); | ||||
| 2681 | if (host) { | ||||
| 2682 | host++; | ||||
| 2683 | uri->userinfo = unescape_uri(authority, host - authority); | ||||
| 2684 | } else | ||||
| 2685 | host = authority; | ||||
| 2686 | |||||
| 2687 | /* try matching IPv6 address first */ | ||||
| 2688 | if (sscanf(host, "[%m[^]]]:%d", | ||||
| 2689 | &uri->host, &uri->port) < 1) { | ||||
| 2690 | /* treat it as IPv4/hostname */ | ||||
| 2691 | if (sscanf(host, "%m[^:]:%d", | ||||
| 2692 | &h, &uri->port) < 1) | ||||
| 2693 | return -EINVAL22; | ||||
| 2694 | uri->host = unescape_uri(h, 0); | ||||
| 2695 | } | ||||
| 2696 | |||||
| 2697 | /* split path into elements */ | ||||
| 2698 | if (path) { | ||||
| 2699 | char *e, *elem; | ||||
| 2700 | |||||
| 2701 | /* separate the fragment */ | ||||
| 2702 | e = strrchr(path, '#')_Generic (0 ? (path) : (void *) 1, const void *: (const char * ) (strrchr (path, '#')), default: strrchr (path, '#')); | ||||
| 2703 | if (e) { | ||||
| 2704 | uri->fragment = unescape_uri(e + 1, 0); | ||||
| 2705 | *e = '\0'; | ||||
| 2706 | } | ||||
| 2707 | /* separate the query string */ | ||||
| 2708 | e = strrchr(path, '?')_Generic (0 ? (path) : (void *) 1, const void *: (const char * ) (strrchr (path, '?')), default: strrchr (path, '?')); | ||||
| 2709 | if (e) { | ||||
| 2710 | uri->query = unescape_uri(e + 1, 0); | ||||
| 2711 | *e = '\0'; | ||||
| 2712 | } | ||||
| 2713 | |||||
| 2714 | /* count elements first */ | ||||
| 2715 | for (i = 0, e = path; *e; e++) | ||||
| 2716 | if (*e == '/' && *(e + 1) != '/') | ||||
| 2717 | i++; | ||||
| 2718 | uri->path_segments = calloc(i + 2, sizeof(char *)); | ||||
| 2719 | if (!uri->path_segments) | ||||
| 2720 | return -ENOMEM12; | ||||
| 2721 | |||||
| 2722 | /* | ||||
| 2723 | * libnvmf_uri_free() walks path_segments until the first NULL | ||||
| 2724 | * entry, so a failed unescape_uri() partway through must abort | ||||
| 2725 | * the whole parse instead of leaving a NULL hole followed by | ||||
| 2726 | * more entries that would then never be freed. | ||||
| 2727 | */ | ||||
| 2728 | i = 0; | ||||
| 2729 | elem = strtok_r(path, "/", &e); | ||||
| 2730 | if (elem) { | ||||
| 2731 | uri->path_segments[i] = unescape_uri(elem, 0); | ||||
| 2732 | if (!uri->path_segments[i]) | ||||
| 2733 | return -ENOMEM12; | ||||
| 2734 | i++; | ||||
| 2735 | } | ||||
| 2736 | while (elem && strlen(elem)) { | ||||
| 2737 | elem = strtok_r(NULL((void*)0), "/", &e); | ||||
| 2738 | if (elem) { | ||||
| 2739 | uri->path_segments[i] = unescape_uri(elem, 0); | ||||
| 2740 | if (!uri->path_segments[i]) | ||||
| 2741 | return -ENOMEM12; | ||||
| 2742 | i++; | ||||
| 2743 | } | ||||
| 2744 | } | ||||
| 2745 | } | ||||
| 2746 | |||||
| 2747 | *urip = uri; | ||||
| 2748 | uri = NULL((void*)0); | ||||
| 2749 | |||||
| 2750 | return 0; | ||||
| 2751 | } | ||||
| 2752 | |||||
| 2753 | __shr_public__attribute__((visibility("default"))) void libnvmf_uri_free(struct libnvmf_uri *uri) | ||||
| 2754 | { | ||||
| 2755 | char **s; | ||||
| 2756 | |||||
| 2757 | if (!uri) | ||||
| 2758 | return; | ||||
| 2759 | free(uri->scheme); | ||||
| 2760 | free(uri->protocol); | ||||
| 2761 | free(uri->userinfo); | ||||
| 2762 | free(uri->host); | ||||
| 2763 | for (s = uri->path_segments; s && *s; s++) | ||||
| 2764 | free(*s); | ||||
| 2765 | free(uri->path_segments); | ||||
| 2766 | free(uri->query); | ||||
| 2767 | free(uri->fragment); | ||||
| 2768 | free(uri); | ||||
| 2769 | } | ||||
| 2770 | |||||
| 2771 | static struct libnvme_ctrl *lookup_ctrl(struct libnvme_host *h, | ||||
| 2772 | const struct libnvme_ctrl_params *params) | ||||
| 2773 | { | ||||
| 2774 | struct libnvme_subsystem *s; | ||||
| 2775 | struct libnvme_ctrl *c; | ||||
| 2776 | |||||
| 2777 | libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem (h, s)) { | ||||
| 2778 | c = libnvme_ctrl_find(s, params, NULL((void*)0)); | ||||
| 2779 | if (c) | ||||
| 2780 | return c; | ||||
| 2781 | } | ||||
| 2782 | |||||
| 2783 | return NULL((void*)0); | ||||
| 2784 | } | ||||
| 2785 | |||||
| 2786 | /* | ||||
| 2787 | * Same as lookup_ctrl(), but only returns a controller that's actually | ||||
| 2788 | * connected. lookup_ctrl() can also match a scanned-but-unconnected | ||||
| 2789 | * draft, which has no kernel-assigned name yet. | ||||
| 2790 | */ | ||||
| 2791 | static struct libnvme_ctrl *lookup_live_ctrl(struct libnvme_host *h, | ||||
| 2792 | const struct libnvme_ctrl_params *params) | ||||
| 2793 | { | ||||
| 2794 | struct libnvme_ctrl *c = lookup_ctrl(h, params); | ||||
| 2795 | |||||
| 2796 | return (c && libnvme_ctrl_get_name(c)) ? c : NULL((void*)0); | ||||
| 2797 | } | ||||
| 2798 | |||||
| 2799 | __shr_public__attribute__((visibility("default"))) int libnvmf_get_owner_from_tid(struct libnvme_global_ctx *ctx, | ||||
| 2800 | const struct libnvmf_tid *tid, char **owner) | ||||
| 2801 | { | ||||
| 2802 | struct libnvme_ctrl_params params = { 0 }; | ||||
| 2803 | struct libnvme_subsystem *s; | ||||
| 2804 | struct libnvme_host *h; | ||||
| 2805 | struct libnvme_ctrl *c = NULL((void*)0); | ||||
| 2806 | int ret; | ||||
| 2807 | |||||
| 2808 | if (!ctx || !tid || !owner) | ||||
| 2809 | return -EINVAL22; | ||||
| 2810 | |||||
| 2811 | *owner = NULL((void*)0); | ||||
| 2812 | |||||
| 2813 | h = libnvme_lookup_host(ctx, libnvmf_tid_get_hostnqn(tid), | ||||
| 2814 | libnvmf_tid_get_hostid(tid)); | ||||
| 2815 | if (!h) | ||||
| 2816 | return 0; | ||||
| 2817 | |||||
| 2818 | params.transport = libnvmf_tid_get_transport(tid); | ||||
| 2819 | params.traddr = libnvmf_tid_get_traddr(tid); | ||||
| 2820 | params.trsvcid = libnvmf_tid_get_trsvcid(tid); | ||||
| 2821 | params.subsysnqn = libnvmf_tid_get_subsysnqn(tid); | ||||
| 2822 | params.host_traddr = libnvmf_tid_get_host_traddr(tid); | ||||
| 2823 | params.host_iface = libnvmf_tid_get_host_iface(tid); | ||||
| 2824 | |||||
| 2825 | libnvme_for_each_subsystem(h, s)for (s = libnvme_first_subsystem(h); s != ((void*)0); s = libnvme_next_subsystem (h, s)) { | ||||
| 2826 | c = libnvme_ctrl_find(s, ¶ms, NULL((void*)0)); | ||||
| 2827 | if (c) | ||||
| 2828 | break; | ||||
| 2829 | } | ||||
| 2830 | if (!c) | ||||
| 2831 | return 0; | ||||
| 2832 | |||||
| 2833 | ret = libnvmf_registry_retrieve(ctx, libnvme_ctrl_get_name(c), | ||||
| 2834 | "owner", owner); | ||||
| 2835 | return (ret == -ENOENT2) ? 0 : ret; | ||||
| 2836 | } | ||||
| 2837 | |||||
| 2838 | __shr_public__attribute__((visibility("default"))) int libnvmf_get_owner_from_fctx(struct libnvme_global_ctx *ctx, | ||||
| 2839 | struct libnvmf_context *fctx, char **owner) | ||||
| 2840 | { | ||||
| 2841 | struct libnvmf_tid *tid; | ||||
| 2842 | const char *name; | ||||
| 2843 | int ret; | ||||
| 2844 | |||||
| 2845 | if (!ctx || !fctx || !owner) | ||||
| 2846 | return -EINVAL22; | ||||
| 2847 | |||||
| 2848 | *owner = NULL((void*)0); | ||||
| 2849 | |||||
| 2850 | name = libnvmf_context_get_device(fctx); | ||||
| 2851 | if (name) { | ||||
| 2852 | ret = libnvmf_registry_retrieve(ctx, name, "owner", owner); | ||||
| 2853 | return (ret == -ENOENT2) ? 0 : ret; | ||||
| 2854 | } | ||||
| 2855 | |||||
| 2856 | ret = libnvmf_tid_from_fields( | ||||
| 2857 | libnvmf_context_get_transport(fctx), | ||||
| 2858 | libnvmf_context_get_traddr(fctx), | ||||
| 2859 | libnvmf_context_get_trsvcid(fctx), | ||||
| 2860 | libnvmf_context_get_subsysnqn(fctx), | ||||
| 2861 | libnvmf_context_get_host_traddr(fctx), | ||||
| 2862 | libnvmf_context_get_host_iface(fctx), | ||||
| 2863 | libnvmf_context_get_hostnqn(fctx), | ||||
| 2864 | libnvmf_context_get_hostid(fctx), &tid); | ||||
| 2865 | if (ret) | ||||
| 2866 | return ret; | ||||
| 2867 | |||||
| 2868 | ret = libnvmf_get_owner_from_tid(ctx, tid, owner); | ||||
| 2869 | libnvmf_tid_free(tid); | ||||
| 2870 | |||||
| 2871 | return ret; | ||||
| 2872 | } | ||||
| 2873 | |||||
| 2874 | static int setup_connection(struct libnvmf_context *fctx, struct libnvme_host *h, | ||||
| 2875 | bool_Bool discovery) | ||||
| 2876 | { | ||||
| 2877 | if (fctx->hostkey) | ||||
| 2878 | libnvme_host_set_kxchap_host_key(h, fctx->hostkey); | ||||
| 2879 | |||||
| 2880 | if (!fctx->ctrl_params.trsvcid) | ||||
| 2881 | fctx->ctrl_params.trsvcid = | ||||
| 2882 | libnvmf_get_default_trsvcid(fctx->ctrl_params.transport, | ||||
| 2883 | discovery); | ||||
| 2884 | |||||
| 2885 | return 0; | ||||
| 2886 | } | ||||
| 2887 | |||||
| 2888 | |||||
| 2889 | static int set_discovery_kato(struct libnvmf_context *fctx, | ||||
| 2890 | struct libnvme_ctrl_params *params) | ||||
| 2891 | { | ||||
| 2892 | int tmo = params->cfg.keep_alive_tmo; | ||||
| 2893 | /* | ||||
| 2894 | * EPCSD isn't known until after the Discovery Log Page comes back, so | ||||
| 2895 | * auto mode optimistically requests a KATO here; dc_decide() | ||||
| 2896 | * disconnects per entry afterward if EPCSD turns out to be unset. | ||||
| 2897 | */ | ||||
| 2898 | bool_Bool wants_kato = fctx->persistent == LIBNVMF_PERSISTENT_AUTO || | ||||
| 2899 | fctx->persistent == LIBNVMF_PERSISTENT_FORCE; | ||||
| 2900 | |||||
| 2901 | /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */ | ||||
| 2902 | if (wants_kato && !params->cfg.keep_alive_tmo) | ||||
| 2903 | params->cfg.keep_alive_tmo = fctx->default_keep_alive_timeout; | ||||
| 2904 | /* Set kato to zero for non-persistent controllers */ | ||||
| 2905 | else if (!wants_kato && params->cfg.keep_alive_tmo > 0) | ||||
| 2906 | params->cfg.keep_alive_tmo = 0; | ||||
| 2907 | |||||
| 2908 | return tmo; | ||||
| 2909 | } | ||||
| 2910 | |||||
| 2911 | enum dc_ownership { | ||||
| 2912 | DC_OWNED, | ||||
| 2913 | DC_BORROWED, | ||||
| 2914 | }; | ||||
| 2915 | |||||
| 2916 | /* | ||||
| 2917 | * Decide whether a discovery controller connection should be disconnected | ||||
| 2918 | * once its own Discovery Log Page has been fully walked. Pure function, | ||||
| 2919 | * no I/O, so it is directly unit-testable. | ||||
| 2920 | * | ||||
| 2921 | * @own: DC_BORROWED means this connection pre-existed the walk and is | ||||
| 2922 | * never ours to disconnect, regardless of persistence mode. | ||||
| 2923 | * @self_seen: whether this DC's own self entry (SUBTYPE 03h, "current | ||||
| 2924 | * discovery subsystem") was found in its own Discovery Log | ||||
| 2925 | * Page. | ||||
| 2926 | * @self_eflags: that self entry's EFLAGS; meaningful only if @self_seen. | ||||
| 2927 | * @parent_eflags: the EFLAGS this DC's own referral entry carried in its | ||||
| 2928 | * parent's Discovery Log Page, or NULL if this DC has no | ||||
| 2929 | * parent (the primary). Used only as a fallback when | ||||
| 2930 | * @self_seen is false. | ||||
| 2931 | */ | ||||
| 2932 | static bool_Bool dc_decide(struct libnvmf_context *fctx, const char *subnqn, | ||||
| 2933 | enum dc_ownership own, bool_Bool self_seen, __u16 self_eflags, | ||||
| 2934 | const __u16 *parent_eflags) | ||||
| 2935 | { | ||||
| 2936 | __u16 eflags; | ||||
| 2937 | bool_Bool disconnect; | ||||
| 2938 | |||||
| 2939 | if (own == DC_BORROWED) | ||||
| 2940 | return false0; | ||||
| 2941 | |||||
| 2942 | if (self_seen) | ||||
| 2943 | eflags = self_eflags; | ||||
| 2944 | else if (parent_eflags) | ||||
| 2945 | eflags = *parent_eflags; | ||||
| 2946 | else | ||||
| 2947 | eflags = 0; | ||||
| 2948 | |||||
| 2949 | switch (fctx->persistent) { | ||||
| 2950 | case LIBNVMF_PERSISTENT_FORCE: | ||||
| 2951 | /* Persist regardless of what EPCSD reports. */ | ||||
| 2952 | disconnect = false0; | ||||
| 2953 | break; | ||||
| 2954 | case LIBNVMF_PERSISTENT_AUTO: | ||||
| 2955 | /* Persist only where the entry's own EPCSD flag says so. */ | ||||
| 2956 | disconnect = !(eflags & NVMF_DISC_EFLAGS_EPCSD); | ||||
| 2957 | if (disconnect) | ||||
| 2958 | libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN,__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "%s: not persisting, EPCSD=0\n" , subnqn) | ||||
| 2959 | "%s: not persisting, EPCSD=0\n", subnqn)__libnvme_msg(fctx->ctx, LIBNVME_LOG_WARN, ((void*)0), "%s: not persisting, EPCSD=0\n" , subnqn); | ||||
| 2960 | break; | ||||
| 2961 | case LIBNVMF_PERSISTENT_NO: | ||||
| 2962 | case LIBNVMF_PERSISTENT_UNSET: | ||||
| 2963 | default: | ||||
| 2964 | disconnect = true1; | ||||
| 2965 | break; | ||||
| 2966 | } | ||||
| 2967 | |||||
| 2968 | return disconnect; | ||||
| 2969 | } | ||||
| 2970 | |||||
| 2971 | /* | ||||
| 2972 | * Bundles the controller and the decisions made about it while walking a | ||||
| 2973 | * Discovery Log Page, so the whole outcome for one entry (or, with @e | ||||
| 2974 | * NULL, for the primary's own self entry) can be logged in one place. | ||||
| 2975 | */ | ||||
| 2976 | struct dc_decision { | ||||
| 2977 | struct libnvme_ctrl *c; | ||||
| 2978 | bool_Bool primary; | ||||
| 2979 | bool_Bool already_connected; | ||||
| 2980 | bool_Bool connect; | ||||
| 2981 | bool_Bool disconnect; | ||||
| 2982 | }; | ||||
| 2983 | |||||
| 2984 | static void dc_log_decision(struct libnvmf_context *fctx, | ||||
| 2985 | struct nvmf_disc_log_entry *e, const struct dc_decision *d, | ||||
| 2986 | const char *reason) | ||||
| 2987 | { | ||||
| 2988 | const char *subnqn, *transport, *traddr, *trsvcid; | ||||
| 2989 | const char *ctrl_name = d->c ? libnvme_ctrl_get_name(d->c) : NULL((void*)0); | ||||
| 2990 | __u16 eflags = 0; | ||||
| 2991 | |||||
| 2992 | if (e) { | ||||
| 2993 | subnqn = e->subnqn; | ||||
| 2994 | transport = libnvmf_trtype_str(e->trtype); | ||||
| 2995 | traddr = e->traddr; | ||||
| 2996 | trsvcid = e->trsvcid; | ||||
| 2997 | eflags = le16_to_cpu(e->eflags); | ||||
| 2998 | } else if (d->c) { | ||||
| 2999 | subnqn = libnvme_ctrl_get_subsysnqn(d->c); | ||||
| 3000 | transport = libnvme_ctrl_get_transport(d->c); | ||||
| 3001 | traddr = libnvme_ctrl_get_traddr(d->c); | ||||
| 3002 | trsvcid = libnvme_ctrl_get_trsvcid(d->c); | ||||
| 3003 | } else { | ||||
| 3004 | subnqn = transport = traddr = trsvcid = "-"; | ||||
| 3005 | } | ||||
| 3006 | |||||
| 3007 | libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG,__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n" , subnqn, transport, traddr, trsvcid, eflags, d->primary, d ->already_connected, d->connect, d->disconnect, ctrl_name ? ctrl_name : "-", reason ? " reason=" : "", reason ? reason : "") | ||||
| 3008 | "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n",__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n" , subnqn, transport, traddr, trsvcid, eflags, d->primary, d ->already_connected, d->connect, d->disconnect, ctrl_name ? ctrl_name : "-", reason ? " reason=" : "", reason ? reason : "") | ||||
| 3009 | subnqn, transport, traddr, trsvcid, eflags, d->primary,__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n" , subnqn, transport, traddr, trsvcid, eflags, d->primary, d ->already_connected, d->connect, d->disconnect, ctrl_name ? ctrl_name : "-", reason ? " reason=" : "", reason ? reason : "") | ||||
| 3010 | d->already_connected, d->connect, d->disconnect,__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n" , subnqn, transport, traddr, trsvcid, eflags, d->primary, d ->already_connected, d->connect, d->disconnect, ctrl_name ? ctrl_name : "-", reason ? " reason=" : "", reason ? reason : "") | ||||
| 3011 | ctrl_name ? ctrl_name : "-", reason ? " reason=" : "",__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n" , subnqn, transport, traddr, trsvcid, eflags, d->primary, d ->already_connected, d->connect, d->disconnect, ctrl_name ? ctrl_name : "-", reason ? " reason=" : "", reason ? reason : "") | ||||
| 3012 | reason ? reason : "")__libnvme_msg(fctx->ctx, LIBNVME_LOG_DEBUG, ((void*)0), "discover: %s transport %s traddr %s trsvcid %s eflags 0x%04x primary=%d already_connected=%d connect=%d disconnect=%d ctrl=%s%s%s\n" , subnqn, transport, traddr, trsvcid, eflags, d->primary, d ->already_connected, d->connect, d->disconnect, ctrl_name ? ctrl_name : "-", reason ? " reason=" : "", reason ? reason : ""); | ||||
| 3013 | } | ||||
| 3014 | |||||
| 3015 | static void dc_already_connected(struct libnvmf_context *fctx, | ||||
| 3016 | struct libnvme_host *h, struct nvmf_disc_log_entry *e) | ||||
| 3017 | { | ||||
| 3018 | if (fctx->hooks.already_connected) | ||||
| 3019 | fctx->hooks.already_connected(fctx, h, e->subnqn, | ||||
| 3020 | libnvmf_trtype_str(e->trtype), e->traddr, | ||||
| 3021 | e->trsvcid, fctx->hooks.user_data); | ||||
| 3022 | } | ||||
| 3023 | |||||
| 3024 | /* | ||||
| 3025 | * The spec does not require processing referral entries deeper than | ||||
| 3026 | * eight levels. | ||||
| 3027 | */ | ||||
| 3028 | #define NVMF_MAX_REFERRAL_DEPTH8 8 | ||||
| 3029 | |||||
| 3030 | /* | ||||
| 3031 | * Every discovery controller visited during one top-level walk, keyed by | ||||
| 3032 | * TID. The depth cap alone is not a cycle guard -- a referral graph can | ||||
| 3033 | * cycle back to an earlier DC within eight hops -- so this is what | ||||
| 3034 | * actually stops the walk from looping. | ||||
| 3035 | */ | ||||
| 3036 | SHR_PTRARRAY_DEFINE(dc_visited, struct libnvmf_tid)struct dc_visited { struct libnvmf_tid **items; size_t len; size_t cap; }; static inline int dc_visited_append(struct dc_visited *a, struct libnvmf_tid *item) { do { (void) sizeof(char [1 - 2*!(sizeof(struct dc_visited) == sizeof(struct shr_ptrarray) )]); } while(0); return shr_ptrarray_append( (struct shr_ptrarray *)a, item); } static inline void dc_visited_free(struct dc_visited *a) { shr_ptrarray_free((struct shr_ptrarray *)a); }; | ||||
| 3037 | |||||
| 3038 | static bool_Bool dc_visited_has(const struct dc_visited *v, | ||||
| 3039 | const struct libnvmf_tid *tid) | ||||
| 3040 | { | ||||
| 3041 | const char *canon = libnvmf_tid_get_canonical(tid); | ||||
| 3042 | |||||
| 3043 | if (!canon) | ||||
| 3044 | return false0; | ||||
| 3045 | |||||
| 3046 | for (size_t i = 0; i < v->len; i++) { | ||||
| 3047 | const char *seen = libnvmf_tid_get_canonical(v->items[i]); | ||||
| 3048 | |||||
| 3049 | if (seen && !strcmp(seen, canon)) | ||||
| 3050 | return true1; | ||||
| 3051 | } | ||||
| 3052 | |||||
| 3053 | return false0; | ||||
| 3054 | } | ||||
| 3055 | |||||
| 3056 | /* | ||||
| 3057 | * Registers c's own real, connected identity -- not whatever a referring | ||||
| 3058 | * entry claimed about it -- so a cycle that loops back to c (including | ||||
| 3059 | * back to the primary) is recognized even if reached via a differently | ||||
| 3060 | * reported path. Best-effort: a TID construction failure here just means | ||||
| 3061 | * this one DC isn't deduplicated, not a fatal error for the walk. | ||||
| 3062 | */ | ||||
| 3063 | static void dc_visited_register(struct dc_visited *visited, | ||||
| 3064 | struct libnvme_ctrl *c, struct libnvmf_context *fctx) | ||||
| 3065 | { | ||||
| 3066 | struct libnvmf_tid *tid; | ||||
| 3067 | int err; | ||||
| 3068 | |||||
| 3069 | err = libnvmf_tid_from_fields(libnvme_ctrl_get_transport(c), | ||||
| 3070 | libnvme_ctrl_get_traddr(c), libnvme_ctrl_get_trsvcid(c), | ||||
| 3071 | libnvme_ctrl_get_subsysnqn(c), | ||||
| 3072 | libnvme_ctrl_get_host_traddr(c), | ||||
| 3073 | libnvme_ctrl_get_host_iface(c), | ||||
| 3074 | fctx->hostnqn, fctx->hostid, &tid); | ||||
| 3075 | if (err) | ||||
| 3076 | return; | ||||
| 3077 | |||||
| 3078 | if (dc_visited_append(visited, tid)) | ||||
| 3079 | libnvmf_tid_free(tid); | ||||
| 3080 | } | ||||
| 3081 | |||||
| 3082 | static inline void free_libnvmf_tid(struct libnvmf_tid **tid) | ||||
| 3083 | { | ||||
| 3084 | libnvmf_tid_free(*tid); | ||||
| 3085 | } | ||||
| 3086 | #define __cleanup_libnvmf_tid__attribute__((cleanup(free_libnvmf_tid))) __cleanup(free_libnvmf_tid)__attribute__((cleanup(free_libnvmf_tid))) | ||||
| 3087 | |||||
| 3088 | /* | ||||
| 3089 | * A leaf entry (NVME_NQN_NVME, an I/O controller) is a dead end for the | ||||
| 3090 | * walk: connect it if --connect was requested, and there is nothing | ||||
| 3091 | * further to recurse into. | ||||
| 3092 | */ | ||||
| 3093 | static void dc_connect_leaf_entry(struct libnvme_global_ctx *ctx, | ||||
| 3094 | struct libnvmf_context *fctx, struct libnvme_host *h, | ||||
| 3095 | struct nvmf_disc_log_entry *e, | ||||
| 3096 | struct libnvme_ctrl_params *params) | ||||
| 3097 | { | ||||
| 3098 | struct dc_decision d = { 0 }; | ||||
| 3099 | int err; | ||||
| 3100 | |||||
| 3101 | if (!fctx->connect) { | ||||
| 3102 | dc_log_decision(fctx, e, &d, "connect not requested"); | ||||
| 3103 | return; | ||||
| 3104 | } | ||||
| 3105 | |||||
| 3106 | if (fctx->hooks.connect_leaf) { | ||||
| 3107 | /* | ||||
| 3108 | * NBFT's own leaf-connect quirks (DHCP retry, firing | ||||
| 3109 | * hooks.connected) apply instead -- it fires the hook | ||||
| 3110 | * itself. | ||||
| 3111 | */ | ||||
| 3112 | err = fctx->hooks.connect_leaf(ctx, fctx, h, e, params, &d.c); | ||||
| 3113 | } else { | ||||
| 3114 | err = nvmf_connect_disc_entry(h, e, params, &d.c); | ||||
| 3115 | if (d.c && fctx->hooks.connected) | ||||
| 3116 | fctx->hooks.connected(fctx, d.c, fctx->hooks.user_data); | ||||
| 3117 | } | ||||
| 3118 | if (d.c) { | ||||
| 3119 | d.connect = true1; | ||||
| 3120 | dc_log_decision(fctx, e, &d, NULL((void*)0)); | ||||
| 3121 | } else if (err == -ENVME_CONNECT_ALREADY) { | ||||
| 3122 | dc_already_connected(fctx, h, e); | ||||
| 3123 | } else { | ||||
| 3124 | dc_log_decision(fctx, e, &d, libnvme_strerror(-err)); | ||||
| 3125 | } | ||||
| 3126 | } | ||||
| 3127 | |||||
| 3128 | static int _nvmf_discover(struct libnvme_global_ctx *ctx, | ||||
| 3129 | struct libnvmf_context *fctx, | ||||
| 3130 | const struct libnvme_ctrl_params *ctrl_params, | ||||
| 3131 | struct libnvme_ctrl *c, enum dc_ownership own, int depth, | ||||
| 3132 | struct dc_visited *visited, const __u16 *parent_eflags, | ||||
| 3133 | bool_Bool *disconnected); | ||||
| 3134 | |||||
| 3135 | /* | ||||
| 3136 | * A referral entry (NVME_NQN_DISC) is a branch point: it opens up another | ||||
| 3137 | * Discovery Service to walk, unlike a leaf entry. | ||||
| 3138 | */ | ||||
| 3139 | static void dc_walk_referral(struct libnvme_global_ctx *ctx, | ||||
| 3140 | struct libnvmf_context *fctx, struct libnvme_host *h, | ||||
| 3141 | struct nvmf_disc_log_entry *e, | ||||
| 3142 | struct libnvme_ctrl_params *params, int depth, | ||||
| 3143 | struct dc_visited *visited, __u16 eflags) | ||||
| 3144 | { | ||||
| 3145 | __cleanup_libnvmf_tid__attribute__((cleanup(free_libnvmf_tid))) struct libnvmf_tid *tid = NULL((void*)0); | ||||
| 3146 | struct dc_decision d = { 0 }; | ||||
| 3147 | enum dc_ownership child_own; | ||||
| 3148 | bool_Bool child_disconnected = false0; | ||||
| 3149 | struct libnvme_ctrl *cl; | ||||
| 3150 | int err; | ||||
| 3151 | |||||
| 3152 | if (depth >= NVMF_MAX_REFERRAL_DEPTH8) { | ||||
| 3153 | dc_log_decision(fctx, e, &d, | ||||
| 3154 | "referral depth limit reached, not descending"); | ||||
| 3155 | return; | ||||
| 3156 | } | ||||
| 3157 | |||||
| 3158 | if (libnvmf_tid_from_fields(params->transport, params->traddr, | ||||
| 3159 | params->trsvcid, params->subsysnqn, | ||||
| 3160 | params->host_traddr, params->host_iface, | ||||
| 3161 | fctx->hostnqn, fctx->hostid, &tid)) { | ||||
| 3162 | dc_log_decision(fctx, e, &d, "invalid entry"); | ||||
| 3163 | return; | ||||
| 3164 | } | ||||
| 3165 | |||||
| 3166 | if (dc_visited_has(visited, tid)) { | ||||
| 3167 | dc_log_decision(fctx, e, &d, "already visited this walk"); | ||||
| 3168 | return; | ||||
| 3169 | } | ||||
| 3170 | |||||
| 3171 | cl = lookup_live_ctrl(h, params); | ||||
| 3172 | if (cl) { | ||||
| 3173 | d.c = cl; | ||||
| 3174 | d.already_connected = true1; | ||||
| 3175 | child_own = DC_BORROWED; | ||||
| 3176 | } else { | ||||
| 3177 | set_discovery_kato(fctx, params); | ||||
| 3178 | err = nvmf_connect_disc_entry(h, e, params, &d.c); | ||||
| 3179 | if (!d.c) { | ||||
| 3180 | if (err == -ENVME_CONNECT_ALREADY) | ||||
| 3181 | dc_already_connected(fctx, h, e); | ||||
| 3182 | else | ||||
| 3183 | dc_log_decision(fctx, e, &d, | ||||
| 3184 | libnvme_strerror(-err)); | ||||
| 3185 | return; | ||||
| 3186 | } | ||||
| 3187 | child_own = DC_OWNED; | ||||
| 3188 | if (fctx->hooks.connected) | ||||
| 3189 | fctx->hooks.connected(fctx, d.c, fctx->hooks.user_data); | ||||
| 3190 | } | ||||
| 3191 | d.connect = true1; | ||||
| 3192 | dc_log_decision(fctx, e, &d, NULL((void*)0)); | ||||
| 3193 | |||||
| 3194 | if (tid) { | ||||
| 3195 | if (!dc_visited_append(visited, tid)) | ||||
| 3196 | tid = NULL((void*)0); /* ownership transferred */ | ||||
| 3197 | } | ||||
| 3198 | |||||
| 3199 | /* | ||||
| 3200 | * The child decides its own fate (disconnect or persist) | ||||
| 3201 | * internally, the same way this DC just did above for | ||||
| 3202 | * itself. Only free it if it actually disconnected -- | ||||
| 3203 | * a persisted child must stay in the tree, since a sibling | ||||
| 3204 | * or later referral elsewhere in this same walk may still | ||||
| 3205 | * need to find it via lookup_live_ctrl()/dc_visited_has(). | ||||
| 3206 | */ | ||||
| 3207 | _nvmf_discover(ctx, fctx, params, d.c, child_own, depth + 1, | ||||
| 3208 | visited, &eflags, &child_disconnected); | ||||
| 3209 | if (child_disconnected) | ||||
| 3210 | libnvme_free_ctrl(d.c); | ||||
| 3211 | } | ||||
| 3212 | |||||
| 3213 | /* | ||||
| 3214 | * Is @e the self entry for the DC's own connection (@c)? A multi-homed DC may | ||||
| 3215 | * report one self entry per port (Base spec 2.4, Figure 320, subtype 03h); | ||||
| 3216 | * only the entry matching @c's transport, traddr and trsvcid is this same | ||||
| 3217 | * connection, so only its EFLAGS apply here. The others describe the DC's | ||||
| 3218 | * other ports. | ||||
| 3219 | */ | ||||
| 3220 | static bool_Bool dc_entry_is_self(const struct libnvme_ctrl *c, | ||||
| 3221 | const struct nvmf_disc_log_entry *e) | ||||
| 3222 | { | ||||
| 3223 | return e->subtype == NVME_NQN_CURR && | ||||
| 3224 | shr_streq0(c->transport, libnvmf_trtype_str(e->trtype)) && | ||||
| 3225 | shr_streq0(c->traddr, e->traddr) && | ||||
| 3226 | shr_streq0(c->trsvcid, e->trsvcid); | ||||
| 3227 | } | ||||
| 3228 | |||||
| 3229 | /* | ||||
| 3230 | * Pass 1: a first pass over the DLP entries to sanitize them and survey | ||||
| 3231 | * this DC's own self entry (SUBTYPE 03h) -- the only place this DC's own | ||||
| 3232 | * EPCSD is ever reported. Returns whether c should be disconnected once | ||||
| 3233 | * its own Discovery Log Page has been fully walked. | ||||
| 3234 | * | ||||
| 3235 | * Sanitizing here, not right after the fetch, is deliberate: fctx->hooks | ||||
| 3236 | * .discovery_log fires before this runs, and it must see the log page | ||||
| 3237 | * exactly as the DC returned it (e.g. for --raw). Only Pass 2's connect | ||||
| 3238 | * logic, which runs after this pass completes, needs the guaranteed- | ||||
| 3239 | * terminated strings this pass produces. | ||||
| 3240 | */ | ||||
| 3241 | static bool_Bool dc_survey_self_entry(struct libnvmf_context *fctx, | ||||
| 3242 | struct libnvme_ctrl *c, enum dc_ownership own, bool_Bool primary, | ||||
| 3243 | const __u16 *parent_eflags, struct nvmf_discovery_log *log, | ||||
| 3244 | uint64_t numrec) | ||||
| 3245 | { | ||||
| 3246 | struct nvmf_disc_log_entry *self_entry = NULL((void*)0); | ||||
| 3247 | struct dc_decision d = { | ||||
| 3248 | .c = c, | ||||
| 3249 | .primary = primary, | ||||
| 3250 | .already_connected = own == DC_BORROWED, | ||||
| 3251 | }; | ||||
| 3252 | bool_Bool self_seen; | ||||
| 3253 | __u16 self_eflags; | ||||
| 3254 | |||||
| 3255 | for (uint64_t i = 0; i < numrec; i++) { | ||||
| 3256 | struct nvmf_disc_log_entry *e = &log->entries[i]; | ||||
| 3257 | |||||
| 3258 | sanitize_discovery_log_entry(c->ctx, e); | ||||
| 3259 | if (dc_entry_is_self(c, e)) | ||||
| 3260 | self_entry = e; | ||||
| 3261 | } | ||||
| 3262 | |||||
| 3263 | self_seen = self_entry != NULL((void*)0); | ||||
| 3264 | self_eflags = self_seen ? le16_to_cpu(self_entry->eflags) : 0; | ||||
| 3265 | |||||
| 3266 | d.disconnect = dc_decide(fctx, libnvme_ctrl_get_subsysnqn(c), own, | ||||
| 3267 | self_seen, self_eflags, parent_eflags); | ||||
| 3268 | |||||
| 3269 | if (self_entry) { | ||||
| 3270 | dc_log_decision(fctx, self_entry, &d, NULL((void*)0)); | ||||
| 3271 | } else if (own == DC_BORROWED) { | ||||
| 3272 | dc_log_decision(fctx, NULL((void*)0), &d, | ||||
| 3273 | "pre-existing, not ours to disconnect"); | ||||
| 3274 | } else if (parent_eflags) { | ||||
| 3275 | dc_log_decision(fctx, NULL((void*)0), &d, | ||||
| 3276 | "no self entry, using parent's view"); | ||||
| 3277 | } else { | ||||
| 3278 | dc_log_decision(fctx, NULL((void*)0), &d, | ||||
| 3279 | "no self entry, EPCSD assumed 0"); | ||||
| 3280 | } | ||||
| 3281 | |||||
| 3282 | return d.disconnect; | ||||
| 3283 | } | ||||
| 3284 | |||||
| 3285 | static int _nvmf_discover(struct libnvme_global_ctx *ctx, | ||||
| 3286 | struct libnvmf_context *fctx, | ||||
| 3287 | const struct libnvme_ctrl_params *ctrl_params, | ||||
| 3288 | struct libnvme_ctrl *c, enum dc_ownership own, int depth, | ||||
| 3289 | struct dc_visited *visited, const __u16 *parent_eflags, | ||||
| 3290 | bool_Bool *disconnected) | ||||
| 3291 | { | ||||
| 3292 | __cleanup_free__attribute__((cleanup(shr_freep))) struct nvmf_discovery_log *log = NULL((void*)0); | ||||
| 3293 | struct libnvme_subsystem *s = libnvme_ctrl_get_subsystem(c); | ||||
| 3294 | struct libnvme_host *h = libnvme_subsystem_get_host(s); | ||||
| 3295 | uint64_t numrec; | ||||
| 3296 | bool_Bool disconnect; | ||||
| 3297 | int err; | ||||
| 3298 | |||||
| 3299 | if (disconnected) | ||||
| 3300 | *disconnected = false0; | ||||
| 3301 | |||||
| 3302 | struct libnvmf_discovery_args args = { | ||||
| 3303 | .max_retries = fctx->default_max_discovery_retries, | ||||
| 3304 | .lsp = NVMF_LOG_DISC_LSP_NONE, | ||||
| 3305 | }; | ||||
| 3306 | |||||
| 3307 | err = nvme_discovery_log(c, &args, &log); | ||||
| 3308 | if (err) { | ||||
| 3309 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n" , libnvme_strerror(-err)) | ||||
| 3310 | "failed to get discovery log: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n" , libnvme_strerror(-err)) | ||||
| 3311 | libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to get discovery log: %s\n" , libnvme_strerror(-err)); | ||||
| 3312 | return err; | ||||
| 3313 | } | ||||
| 3314 | |||||
| 3315 | numrec = le64_to_cpu(log->numrec); | ||||
| 3316 | |||||
| 3317 | if (fctx->hooks.discovery_log) | ||||
| 3318 | fctx->hooks.discovery_log(fctx, log, numrec, | ||||
| 3319 | fctx->hooks.user_data); | ||||
| 3320 | |||||
| 3321 | dc_visited_register(visited, c, fctx); | ||||
| 3322 | |||||
| 3323 | /* | ||||
| 3324 | * Pass 1: survey c's own self entry, and determine if safe to | ||||
| 3325 | * disconnect. | ||||
| 3326 | */ | ||||
| 3327 | disconnect = dc_survey_self_entry(fctx, c, own, !parent_eflags, | ||||
| 3328 | parent_eflags, log, numrec); | ||||
| 3329 | |||||
| 3330 | /* | ||||
| 3331 | * Safe to disconnect now, before walking c's own referrals: this | ||||
| 3332 | * connection is only ever needed to fetch c's own DLP, and | ||||
| 3333 | * dc_visited already recorded c, independent of whether the | ||||
| 3334 | * connection stays open. | ||||
| 3335 | */ | ||||
| 3336 | if (disconnect) { | ||||
| 3337 | libnvmf_disconnect_ctrl(c); | ||||
| 3338 | if (disconnected) | ||||
| 3339 | *disconnected = true1; | ||||
| 3340 | } | ||||
| 3341 | |||||
| 3342 | /* | ||||
| 3343 | * Pass 2: everything else -- referrals to walk, NVM subsystem | ||||
| 3344 | * entries to connect per --connect. Self entries are already | ||||
| 3345 | * handled above. | ||||
| 3346 | */ | ||||
| 3347 | for (uint64_t i = 0; i < numrec; i++) { | ||||
| 3348 | struct nvmf_disc_log_entry *e = &log->entries[i]; | ||||
| 3349 | __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) struct libnvme_ctrl_params params = | ||||
| 3350 | ctrl_params_dup(ctrl_params); | ||||
| 3351 | struct dc_decision d = { 0 }; | ||||
| 3352 | const char *transport; | ||||
| 3353 | __u16 eflags; | ||||
| 3354 | |||||
| 3355 | /* | ||||
| 3356 | * A self entry describes this same Discovery subsystem. | ||||
| 3357 | * Real DCs publish one for the port already connected; | ||||
| 3358 | * a multi-port DC may also list its other ports. Base spec | ||||
| 3359 | * 2.4 makes acting on either a "may", and a log page entry | ||||
| 3360 | * carries no source address or interface, so the only local | ||||
| 3361 | * path available is the one this connection already uses. | ||||
| 3362 | */ | ||||
| 3363 | if (e->subtype == NVME_NQN_CURR) | ||||
| 3364 | continue; | ||||
| 3365 | |||||
| 3366 | /* Skip connect if the transport types don't match */ | ||||
| 3367 | transport = libnvmf_trtype_str(e->trtype); | ||||
| 3368 | if (strcmp(libnvme_ctrl_get_transport(c), transport)) { | ||||
| 3369 | dc_log_decision(fctx, e, &d, "transport mismatch"); | ||||
| 3370 | continue; | ||||
| 3371 | } | ||||
| 3372 | |||||
| 3373 | params.subsysnqn = e->subnqn; | ||||
| 3374 | params.transport = transport; | ||||
| 3375 | params.traddr = e->traddr; | ||||
| 3376 | params.trsvcid = e->trsvcid; | ||||
| 3377 | eflags = le16_to_cpu(e->eflags); | ||||
| 3378 | |||||
| 3379 | if (e->subtype == NVME_NQN_NVME) { | ||||
| 3380 | dc_connect_leaf_entry(ctx, fctx, h, e, ¶ms); | ||||
| 3381 | continue; | ||||
| 3382 | } | ||||
| 3383 | |||||
| 3384 | /* NVME_NQN_DISC: a referral to another Discovery Service. */ | ||||
| 3385 | dc_walk_referral(ctx, fctx, h, e, ¶ms, depth, visited, | ||||
| 3386 | eflags); | ||||
| 3387 | } | ||||
| 3388 | |||||
| 3389 | return 0; | ||||
| 3390 | } | ||||
| 3391 | |||||
| 3392 | /* | ||||
| 3393 | * Owns the whole-walk visited-set lifetime, so callers don't need to know | ||||
| 3394 | * it exists. c has no parent -- it is the primary discovery controller | ||||
| 3395 | * connection -- so the walk starts at depth 0 with no parent eflags. | ||||
| 3396 | */ | ||||
| 3397 | static int dc_walk(struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx, | ||||
| 3398 | struct libnvme_ctrl *c, enum dc_ownership own) | ||||
| 3399 | { | ||||
| 3400 | struct dc_visited visited = { 0 }; | ||||
| 3401 | int err; | ||||
| 3402 | |||||
| 3403 | err = _nvmf_discover(ctx, fctx, &fctx->ctrl_params, c, own, 0, | ||||
| 3404 | &visited, NULL((void*)0), NULL((void*)0)); | ||||
| 3405 | |||||
| 3406 | /* dc_visited owns only the backing array, not the TIDs in it. */ | ||||
| 3407 | for (size_t i = 0; i < visited.len; i++) | ||||
| 3408 | libnvmf_tid_free(visited.items[i]); | ||||
| 3409 | dc_visited_free(&visited); | ||||
| 3410 | |||||
| 3411 | return err; | ||||
| 3412 | } | ||||
| 3413 | |||||
| 3414 | __shr_public__attribute__((visibility("default"))) const char *libnvmf_get_default_trsvcid(const char *transport, | ||||
| 3415 | bool_Bool discovery_ctrl) | ||||
| 3416 | { | ||||
| 3417 | if (!transport) | ||||
| 3418 | return NULL((void*)0); | ||||
| 3419 | if (!strcmp(transport, "tcp")) { | ||||
| 3420 | if (discovery_ctrl) | ||||
| 3421 | /* Default port for NVMe/TCP discovery controllers */ | ||||
| 3422 | return stringify(NVME_DISC_IP_PORT)"8009"; | ||||
| 3423 | /* Default port for NVMe/TCP io controllers */ | ||||
| 3424 | return stringify(NVME_RDMA_IP_PORT)"4420"; | ||||
| 3425 | } else if (!strcmp(transport, "rdma")) { | ||||
| 3426 | /* Default port for NVMe/RDMA controllers */ | ||||
| 3427 | return stringify(NVME_RDMA_IP_PORT)"4420"; | ||||
| 3428 | } | ||||
| 3429 | |||||
| 3430 | return NULL((void*)0); | ||||
| 3431 | } | ||||
| 3432 | |||||
| 3433 | static int libnvme_add_ctrl(struct libnvmf_context *fctx, | ||||
| 3434 | struct libnvme_host *h, struct libnvme_ctrl *c) | ||||
| 3435 | { | ||||
| 3436 | int err; | ||||
| 3437 | |||||
| 3438 | retry: | ||||
| 3439 | err = libnvmf_add_ctrl(h, c); | ||||
| 3440 | if (!err) | ||||
| 3441 | return 0; | ||||
| 3442 | if (fctx->hooks.decide_retry && | ||||
| 3443 | fctx->hooks.decide_retry(fctx, err, fctx->hooks.user_data)) | ||||
| 3444 | goto retry; | ||||
| 3445 | |||||
| 3446 | return err; | ||||
| 3447 | } | ||||
| 3448 | |||||
| 3449 | static int __create_discovery_ctrl(struct libnvme_global_ctx *ctx, | ||||
| 3450 | struct libnvmf_context *fctx, struct libnvme_ctrl_params *params, | ||||
| 3451 | struct libnvme_host *h, struct libnvme_ctrl **ctrl) | ||||
| 3452 | { | ||||
| 3453 | struct libnvme_ctrl *c; | ||||
| 3454 | int tmo, ret; | ||||
| 3455 | |||||
| 3456 | ret = libnvme_create_ctrl(ctx, params, &c); | ||||
| 3457 | if (ret) | ||||
| 3458 | return ret; | ||||
| 3459 | |||||
| 3460 | libnvme_ctrl_set_discovery_ctrl(c, true1); | ||||
| 3461 | libnvme_ctrl_set_unique_discovery_ctrl(c, | ||||
| 3462 | strcmp(params->subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")); | ||||
| 3463 | tmo = set_discovery_kato(fctx, params); | ||||
| 3464 | |||||
| 3465 | if (libnvme_ctrl_get_unique_discovery_ctrl(c) && fctx->hostkey) { | ||||
| 3466 | libnvme_ctrl_set_kxchap_host_key(c, fctx->hostkey); | ||||
| 3467 | if (fctx->ctrlkey) | ||||
| 3468 | libnvme_ctrl_set_kxchap_ctrl_key(c, fctx->ctrlkey); | ||||
| 3469 | } | ||||
| 3470 | |||||
| 3471 | ret = libnvme_add_ctrl(fctx, h, c); | ||||
| 3472 | params->cfg.keep_alive_tmo = tmo; | ||||
| 3473 | if (ret) { | ||||
| 3474 | libnvme_free_ctrl(c); | ||||
| 3475 | return ret; | ||||
| 3476 | } | ||||
| 3477 | |||||
| 3478 | *ctrl = c; | ||||
| 3479 | return 0; | ||||
| 3480 | } | ||||
| 3481 | |||||
| 3482 | static int nvmf_create_discovery_ctrl(struct libnvme_global_ctx *ctx, | ||||
| 3483 | struct libnvmf_context *fctx, struct libnvme_ctrl_params *params, | ||||
| 3484 | struct libnvme_host *h, struct libnvme_ctrl **ctrl) | ||||
| 3485 | { | ||||
| 3486 | __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id = NULL((void*)0); | ||||
| 3487 | struct libnvme_ctrl *c; | ||||
| 3488 | int ret; | ||||
| 3489 | |||||
| 3490 | ret = __create_discovery_ctrl(ctx, fctx, params, h, &c); | ||||
| 3491 | if (ret) | ||||
| 3492 | return ret; | ||||
| 3493 | |||||
| 3494 | if (libnvme_ctrl_get_unique_discovery_ctrl(c)) { | ||||
| 3495 | *ctrl = c; | ||||
| 3496 | return 0; | ||||
| 3497 | } | ||||
| 3498 | |||||
| 3499 | id = libnvme_alloc(sizeof(*id)); | ||||
| 3500 | if (!id) { | ||||
| 3501 | libnvme_free_ctrl(c); | ||||
| 3502 | return -ENOMEM12; | ||||
| 3503 | } | ||||
| 3504 | |||||
| 3505 | ret = libnvme_open(ctx, c->name, O_RDONLY00, &c->hdl); | ||||
| 3506 | if (ret) { | ||||
| 3507 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "failed to open %s\n", c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open %s\n" , c->name); | ||||
| 3508 | return ret; | ||||
| 3509 | } | ||||
| 3510 | |||||
| 3511 | /* Find out the name of discovery controller */ | ||||
| 3512 | ret = libnvme_ctrl_identify(c, id); | ||||
| 3513 | if (ret) { | ||||
| 3514 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n" , libnvme_strerror(-ret)) | ||||
| 3515 | "failed to identify controller, error %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n" , libnvme_strerror(-ret)) | ||||
| 3516 | libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to identify controller, error %s\n" , libnvme_strerror(-ret)); | ||||
| 3517 | libnvmf_disconnect_ctrl(c); | ||||
| 3518 | libnvme_free_ctrl(c); | ||||
| 3519 | return ret; | ||||
| 3520 | } | ||||
| 3521 | |||||
| 3522 | if (!strcmp(id->subnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) { | ||||
| 3523 | *ctrl = c; | ||||
| 3524 | return 0; | ||||
| 3525 | } | ||||
| 3526 | |||||
| 3527 | /* | ||||
| 3528 | * The subsysnqn is not the well-known name. Prefer the unique | ||||
| 3529 | * subsysnqn over the well-known one. | ||||
| 3530 | */ | ||||
| 3531 | libnvmf_disconnect_ctrl(c); | ||||
| 3532 | libnvme_free_ctrl(c); | ||||
| 3533 | |||||
| 3534 | params->subsysnqn = id->subnqn; | ||||
| 3535 | ret = __create_discovery_ctrl(ctx, fctx, params, h, &c); | ||||
| 3536 | if (ret) | ||||
| 3537 | return ret; | ||||
| 3538 | |||||
| 3539 | *ctrl = c; | ||||
| 3540 | return 0; | ||||
| 3541 | } | ||||
| 3542 | |||||
| 3543 | static struct libnvme_ctrl *dc_open_by_device(struct libnvme_global_ctx *ctx, | ||||
| 3544 | struct libnvmf_context *fctx, | ||||
| 3545 | struct libnvme_ctrl_params *params, enum dc_ownership *own) | ||||
| 3546 | { | ||||
| 3547 | struct libnvme_ctrl *c; | ||||
| 3548 | int err; | ||||
| 3549 | |||||
| 3550 | err = libnvme_scan_ctrl(ctx, fctx->device, &c); | ||||
| 3551 | if (err) { | ||||
| 3552 | /* No controller found, fall back to creating one. */ | ||||
| 3553 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found\n" , fctx->device) | ||||
| 3554 | "ctrl device %s not found\n", fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s not found\n" , fctx->device); | ||||
| 3555 | return NULL((void*)0); | ||||
| 3556 | } | ||||
| 3557 | |||||
| 3558 | /* Check if device matches command-line options */ | ||||
| 3559 | if (!libnvmf_ctrl_match_config(c, params)) { | ||||
| 3560 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n" , fctx->device) | ||||
| 3561 | "ctrl device %s found, ignoring non matching command-line options\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n" , fctx->device) | ||||
| 3562 | fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non matching command-line options\n" , fctx->device); | ||||
| 3563 | } | ||||
| 3564 | |||||
| 3565 | if (!libnvme_ctrl_get_discovery_ctrl(c)) { | ||||
| 3566 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n" , fctx->device) | ||||
| 3567 | "ctrl device %s found, ignoring non discovery controller\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n" , fctx->device) | ||||
| 3568 | fctx->device)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "ctrl device %s found, ignoring non discovery controller\n" , fctx->device); | ||||
| 3569 | |||||
| 3570 | libnvme_free_ctrl(c); | ||||
| 3571 | return NULL((void*)0); | ||||
| 3572 | } | ||||
| 3573 | |||||
| 3574 | /* | ||||
| 3575 | * The controller device was found, so this is an already-existing | ||||
| 3576 | * connection: it must not be disconnected on exit. Record that fact | ||||
| 3577 | * locally instead of overriding fctx->persistent, which must keep | ||||
| 3578 | * reflecting what the user actually asked for. | ||||
| 3579 | */ | ||||
| 3580 | *own = DC_BORROWED; | ||||
| 3581 | |||||
| 3582 | /* | ||||
| 3583 | * When --host-traddr/--host-iface are not specified on the | ||||
| 3584 | * command line, use the discovery controller's (c) host- | ||||
| 3585 | * traddr/host-iface for the connections to controllers | ||||
| 3586 | * returned in the Discovery Log Pages. This is essential | ||||
| 3587 | * when invoking "connect-all" with --device to reuse an | ||||
| 3588 | * existing persistent discovery controller (as is done | ||||
| 3589 | * for the udev rules). This ensures that host-traddr/ | ||||
| 3590 | * host-iface are consistent with the discovery controller (c). | ||||
| 3591 | */ | ||||
| 3592 | if (!params->host_traddr) | ||||
| 3593 | params->host_traddr = (char *)libnvme_ctrl_get_host_traddr(c); | ||||
| 3594 | if (!params->host_iface) | ||||
| 3595 | params->host_iface = (char *)libnvme_ctrl_get_host_iface(c); | ||||
| 3596 | |||||
| 3597 | return c; | ||||
| 3598 | } | ||||
| 3599 | |||||
| 3600 | /* | ||||
| 3601 | * Ownership is reported separately (@own) rather than folded into | ||||
| 3602 | * fctx->persistent, which must keep reflecting what the user actually | ||||
| 3603 | * asked for regardless of whether this connection is reused or fresh. | ||||
| 3604 | * | ||||
| 3605 | * @honor_no_reuse: false makes this ignore --no-reuse and always attempt | ||||
| 3606 | * reuse first. NBFT's boot-time auto-connect has no | ||||
| 3607 | * concept of --no-reuse and always tries to reuse an | ||||
| 3608 | * existing connection first. | ||||
| 3609 | */ | ||||
| 3610 | static int dc_open(struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx, | ||||
| 3611 | struct libnvme_host *h, struct libnvme_ctrl_params *params, | ||||
| 3612 | bool_Bool honor_no_reuse, enum dc_ownership *own, | ||||
| 3613 | struct libnvme_ctrl **ctrl) | ||||
| 3614 | { | ||||
| 3615 | struct libnvme_ctrl *c = NULL((void*)0); | ||||
| 3616 | enum dc_ownership local_own = DC_OWNED; | ||||
| 3617 | int err; | ||||
| 3618 | |||||
| 3619 | if (!honor_no_reuse || !fctx->no_reuse) { | ||||
| 3620 | if (fctx->device) | ||||
| 3621 | c = dc_open_by_device(ctx, fctx, params, &local_own); | ||||
| 3622 | |||||
| 3623 | if (!c) { | ||||
| 3624 | c = lookup_ctrl(h, params); | ||||
| 3625 | if (c) | ||||
| 3626 | local_own = DC_BORROWED; | ||||
| 3627 | } | ||||
| 3628 | } | ||||
| 3629 | |||||
| 3630 | if (c) { | ||||
| 3631 | if (!libnvme_ctrl_get_transport_handle(c)) { | ||||
| 3632 | /* | ||||
| 3633 | * When we found an existing controller it might not | ||||
| 3634 | * have a device handle yet | ||||
| 3635 | */ | ||||
| 3636 | err = libnvme_open(ctx, c->name, O_RDONLY00, &c->hdl); | ||||
| 3637 | if (err) { | ||||
| 3638 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open %s\n" , c->name) | ||||
| 3639 | "failed to open %s\n", c->name)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to open %s\n" , c->name); | ||||
| 3640 | return err; | ||||
| 3641 | } | ||||
| 3642 | } | ||||
| 3643 | } else { | ||||
| 3644 | /* | ||||
| 3645 | * No existing controller, or --no-reuse was given: create a | ||||
| 3646 | * new one. | ||||
| 3647 | */ | ||||
| 3648 | err = nvmf_create_discovery_ctrl(ctx, fctx, params, h, &c); | ||||
| 3649 | |||||
| 3650 | /* | ||||
| 3651 | * NBFT only: the OS's own DHCP client can obtain a different | ||||
| 3652 | * local address for this HFI than the firmware had when it | ||||
| 3653 | * built the NBFT table. Retry once without host_traddr. | ||||
| 3654 | * fctx->nbft_hfi is NULL for every non-NBFT caller, so this | ||||
| 3655 | * is a complete no-op for the general path. | ||||
| 3656 | */ | ||||
| 3657 | if (err == -ENVME_CONNECT_ADDRNOTAVAIL && fctx->nbft_hfi && | ||||
| 3658 | !strcmp(params->transport, "tcp") && | ||||
| 3659 | strlen(fctx->nbft_hfi->tcp_info.dhcp_server_ipaddr) > 0) { | ||||
| 3660 | params->host_traddr = NULL((void*)0); | ||||
| 3661 | err = nvmf_create_discovery_ctrl(ctx, fctx, params, | ||||
| 3662 | h, &c); | ||||
| 3663 | } | ||||
| 3664 | |||||
| 3665 | if (err) | ||||
| 3666 | return err; | ||||
| 3667 | } | ||||
| 3668 | |||||
| 3669 | *own = local_own; | ||||
| 3670 | *ctrl = c; | ||||
| 3671 | return 0; | ||||
| 3672 | } | ||||
| 3673 | |||||
| 3674 | #define NBFT_SYSFS_FILENAME"NBFT*" "NBFT*" | ||||
| 3675 | |||||
| 3676 | static int nbft_filter(const struct dirent *dent) | ||||
| 3677 | { | ||||
| 3678 | return !fnmatch(NBFT_SYSFS_FILENAME"NBFT*", dent->d_name, FNM_PATHNAME(1 << 0)); | ||||
| 3679 | } | ||||
| 3680 | |||||
| 3681 | __shr_public__attribute__((visibility("default"))) int libnvmf_nbft_read_files( | ||||
| 3682 | struct libnvme_global_ctx *ctx, char *path, | ||||
| 3683 | struct nbft_file_entry **head) | ||||
| 3684 | { | ||||
| 3685 | struct nbft_file_entry *entry = NULL((void*)0); | ||||
| 3686 | struct libnbft_info *nbft = NULL((void*)0); | ||||
| 3687 | struct dirent **dent; | ||||
| 3688 | char filename[PATH_MAX4096]; | ||||
| 3689 | int i, count, ret; | ||||
| 3690 | |||||
| 3691 | *head = NULL((void*)0); | ||||
| 3692 | |||||
| 3693 | count = scandir(path, &dent, nbft_filter, NULL((void*)0)); | ||||
| 3694 | if (count < 0) | ||||
| 3695 | return -errno(*__errno_location ()); | ||||
| 3696 | |||||
| 3697 | for (i = 0; i < count; i++) { | ||||
| 3698 | snprintf(filename, sizeof(filename), "%s/%s", path, | ||||
| 3699 | dent[i]->d_name); | ||||
| 3700 | |||||
| 3701 | ret = libnvmf_read_nbft(ctx, &nbft, filename); | ||||
| 3702 | if (!ret) { | ||||
| 3703 | struct nbft_file_entry *new; | ||||
| 3704 | |||||
| 3705 | new = calloc(1, sizeof(*new)); | ||||
| 3706 | if (!new) | ||||
| 3707 | return -ENOMEM12; | ||||
| 3708 | new->nbft = nbft; | ||||
| 3709 | if (entry) { | ||||
| 3710 | entry->next = new; | ||||
| 3711 | entry = entry->next; | ||||
| 3712 | } else { | ||||
| 3713 | entry = new; | ||||
| 3714 | *head = entry; | ||||
| 3715 | } | ||||
| 3716 | } | ||||
| 3717 | free(dent[i]); | ||||
| 3718 | } | ||||
| 3719 | free(dent); | ||||
| 3720 | return 0; | ||||
| 3721 | } | ||||
| 3722 | |||||
| 3723 | __shr_public__attribute__((visibility("default"))) void libnvmf_nbft_free( | ||||
| 3724 | struct libnvme_global_ctx *ctx, struct nbft_file_entry *head) | ||||
| 3725 | { | ||||
| 3726 | if (!head) | ||||
| 3727 | return; | ||||
| 3728 | |||||
| 3729 | while (head) { | ||||
| 3730 | struct nbft_file_entry *next = head->next; | ||||
| 3731 | |||||
| 3732 | libnvmf_free_nbft(ctx, head->nbft); | ||||
| 3733 | free(head); | ||||
| 3734 | |||||
| 3735 | head = next; | ||||
| 3736 | } | ||||
| 3737 | } | ||||
| 3738 | |||||
| 3739 | static bool_Bool validate_uri(struct libnvme_global_ctx *ctx, | ||||
| 3740 | struct libnbft_discovery *dd, | ||||
| 3741 | struct libnvmf_uri *uri) | ||||
| 3742 | { | ||||
| 3743 | if (!uri) { | ||||
| 3744 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to parse URI %s\n" , dd->index, dd->uri) | ||||
| 3745 | "Discovery Descriptor %d: failed to parse URI %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to parse URI %s\n" , dd->index, dd->uri) | ||||
| 3746 | dd->index, dd->uri)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to parse URI %s\n" , dd->index, dd->uri); | ||||
| 3747 | return false0; | ||||
| 3748 | } | ||||
| 3749 | if (strcmp(uri->scheme, "nvme") != 0) { | ||||
| 3750 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n" , dd->index, uri->scheme) | ||||
| 3751 | "Discovery Descriptor %d: unsupported scheme '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n" , dd->index, uri->scheme) | ||||
| 3752 | dd->index, uri->scheme)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported scheme '%s'\n" , dd->index, uri->scheme); | ||||
| 3753 | return false0; | ||||
| 3754 | } | ||||
| 3755 | if (!uri->protocol || strcmp(uri->protocol, "tcp") != 0) { | ||||
| 3756 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n" , dd->index, uri->protocol) | ||||
| 3757 | "Discovery Descriptor %d: unsupported transport '%s'\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n" , dd->index, uri->protocol) | ||||
| 3758 | dd->index, uri->protocol)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: unsupported transport '%s'\n" , dd->index, uri->protocol); | ||||
| 3759 | return false0; | ||||
| 3760 | } | ||||
| 3761 | |||||
| 3762 | return true1; | ||||
| 3763 | } | ||||
| 3764 | |||||
| 3765 | static int nbft_connect(struct libnvme_global_ctx *ctx, | ||||
| 3766 | struct libnvmf_context *fctx, struct libnvme_ctrl_params *params, | ||||
| 3767 | struct libnvme_host *h, struct nvmf_disc_log_entry *e, | ||||
| 3768 | struct libnbft_subsystem_ns *ss, struct libnvme_ctrl **cp) | ||||
| 3769 | { | ||||
| 3770 | struct libnvme_ctrl *c; | ||||
| 3771 | int saved_log_level; | ||||
| 3772 | bool_Bool saved_log_tstamp; | ||||
| 3773 | bool_Bool saved_log_pid; | ||||
| 3774 | int ret; | ||||
| 3775 | |||||
| 3776 | if (cp) | ||||
| 3777 | *cp = NULL((void*)0); | ||||
| 3778 | |||||
| 3779 | saved_log_level = libnvme_get_logging_level(ctx, &saved_log_pid, | ||||
| 3780 | &saved_log_tstamp); | ||||
| 3781 | |||||
| 3782 | c = lookup_live_ctrl(h, params); | ||||
| 3783 | if (c) { | ||||
| 3784 | if (cp) | ||||
| 3785 | *cp = c; | ||||
| 3786 | return 0; | ||||
| 3787 | } | ||||
| 3788 | |||||
| 3789 | if (nvmf_excluded(ctx, params->transport, | ||||
| 3790 | params->traddr, params->trsvcid, | ||||
| 3791 | params->subsysnqn, | ||||
| 3792 | params->host_traddr, | ||||
| 3793 | params->host_iface, | ||||
| 3794 | libnvme_host_get_hostnqn(h), | ||||
| 3795 | libnvme_host_get_hostid(h))) | ||||
| 3796 | return 0; | ||||
| 3797 | |||||
| 3798 | ret = libnvme_create_ctrl(ctx, params, &c); | ||||
| 3799 | if (ret) | ||||
| 3800 | return ret; | ||||
| 3801 | |||||
| 3802 | /* Pause logging for unavailable SSNSs */ | ||||
| 3803 | if (ss && (ss->flags & NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL) && saved_log_level < 1) | ||||
| 3804 | libnvme_set_logging_level(ctx, -1, false0, false0); | ||||
| 3805 | |||||
| 3806 | /* Update tls or concat */ | ||||
| 3807 | nvmf_update_tls_concat(e, c, h); | ||||
| 3808 | |||||
| 3809 | ret = libnvmf_add_ctrl(h, c); | ||||
| 3810 | |||||
| 3811 | /* Resume logging */ | ||||
| 3812 | if (ss && (ss->flags & NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL) && saved_log_level < 1) | ||||
| 3813 | libnvme_set_logging_level(ctx, | ||||
| 3814 | saved_log_level, | ||||
| 3815 | saved_log_pid, | ||||
| 3816 | saved_log_tstamp); | ||||
| 3817 | |||||
| 3818 | if (ret) { | ||||
| 3819 | libnvme_free_ctrl(c); | ||||
| 3820 | /* | ||||
| 3821 | * In case this SSNS was marked as 'unavailable' and | ||||
| 3822 | * our connection attempt has failed, ignore it. | ||||
| 3823 | */ | ||||
| 3824 | if (ss && (ss->flags & NBFT_SSNS_UNAVAIL_NAMESPACE_UNAVAIL)) { | ||||
| 3825 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n" , ss->index) | ||||
| 3826 | "SSNS %d reported as unavailable, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n" , ss->index) | ||||
| 3827 | ss->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d reported as unavailable, skipping\n" , ss->index); | ||||
| 3828 | return 0; | ||||
| 3829 | } | ||||
| 3830 | return ret; | ||||
| 3831 | } | ||||
| 3832 | |||||
| 3833 | if (fctx->hooks.connected) | ||||
| 3834 | fctx->hooks.connected(fctx, c, fctx->hooks.user_data); | ||||
| 3835 | |||||
| 3836 | if (cp) | ||||
| 3837 | *cp = c; | ||||
| 3838 | return 0; | ||||
| 3839 | } | ||||
| 3840 | |||||
| 3841 | /* | ||||
| 3842 | * connect_leaf hook for the NBFT-driven walk: nbft_connect() plus the | ||||
| 3843 | * checks _nvmf_discover()'s general leaf-connect path gets for free from | ||||
| 3844 | * distinguishable lookup_ctrl()/nvmf_excluded() outcomes. nbft_connect() | ||||
| 3845 | * has its own copies of both checks internally, but folds every "nothing | ||||
| 3846 | * to do" outcome into a plain 0 -- fine for its other caller (the SSNS | ||||
| 3847 | * loop below), but not enough to fit dc_log_decision()'s | ||||
| 3848 | * connected/already-connected/reason contract. Re-checking here, before | ||||
| 3849 | * calling nbft_connect(), keeps nbft_connect() itself untouched for that | ||||
| 3850 | * other caller. | ||||
| 3851 | */ | ||||
| 3852 | static int nbft_connect_leaf(struct libnvme_global_ctx *ctx, | ||||
| 3853 | struct libnvmf_context *fctx, struct libnvme_host *h, | ||||
| 3854 | struct nvmf_disc_log_entry *e, | ||||
| 3855 | struct libnvme_ctrl_params *params, struct libnvme_ctrl **cp) | ||||
| 3856 | { | ||||
| 3857 | struct libnvme_ctrl *c; | ||||
| 3858 | int ret; | ||||
| 3859 | |||||
| 3860 | *cp = NULL((void*)0); | ||||
| 3861 | |||||
| 3862 | c = lookup_live_ctrl(h, params); | ||||
| 3863 | if (c) { | ||||
| 3864 | *cp = c; | ||||
| 3865 | return -ENVME_CONNECT_ALREADY; | ||||
| 3866 | } | ||||
| 3867 | |||||
| 3868 | if (nvmf_excluded(ctx, params->transport, params->traddr, | ||||
| 3869 | params->trsvcid, params->subsysnqn, | ||||
| 3870 | params->host_traddr, params->host_iface, | ||||
| 3871 | libnvme_host_get_hostnqn(h), | ||||
| 3872 | libnvme_host_get_hostid(h))) | ||||
| 3873 | return -EPERM1; | ||||
| 3874 | |||||
| 3875 | ret = nbft_connect(ctx, fctx, params, h, e, NULL((void*)0), &c); | ||||
| 3876 | |||||
| 3877 | /* | ||||
| 3878 | * With TCP/DHCP, the OS's own DHCP client can obtain a different | ||||
| 3879 | * local address for this HFI than the firmware had. Retry once | ||||
| 3880 | * without host_traddr. | ||||
| 3881 | */ | ||||
| 3882 | if (ret == -ENVME_CONNECT_ADDRNOTAVAIL && | ||||
| 3883 | !strcmp(params->transport, "tcp") && fctx->nbft_hfi && | ||||
| 3884 | strlen(fctx->nbft_hfi->tcp_info.dhcp_server_ipaddr) > 0) { | ||||
| 3885 | params->host_traddr = NULL((void*)0); | ||||
| 3886 | ret = nbft_connect(ctx, fctx, params, h, e, NULL((void*)0), &c); | ||||
| 3887 | } | ||||
| 3888 | |||||
| 3889 | if (!ret) | ||||
| 3890 | *cp = c; | ||||
| 3891 | |||||
| 3892 | return ret; | ||||
| 3893 | } | ||||
| 3894 | |||||
| 3895 | #define VLAN_PROC_PATH"/proc/net/vlan" "/proc/net/vlan" | ||||
| 3896 | |||||
| 3897 | /* | ||||
| 3898 | * Return 0 for no vlan_id, to be consistent with the NBFT spec. | ||||
| 3899 | */ | ||||
| 3900 | static int get_vlan_id(const char *ifname) | ||||
| 3901 | { | ||||
| 3902 | char path[256], line[256]; | ||||
| 3903 | int vlan_id = 0; | ||||
| 3904 | FILE *f; | ||||
| 3905 | |||||
| 3906 | snprintf(path, sizeof(path), "%s/%s", VLAN_PROC_PATH"/proc/net/vlan", ifname); | ||||
| 3907 | f = fopen(path, "r"); | ||||
| 3908 | if (!f) | ||||
| 3909 | return 0; | ||||
| 3910 | |||||
| 3911 | while (fgets(line, sizeof(line), f)) { | ||||
| 3912 | if (sscanf(line, " VID: %d", &vlan_id) == 1) { | ||||
| 3913 | fclose(f); | ||||
| 3914 | return vlan_id; | ||||
| 3915 | } | ||||
| 3916 | } | ||||
| 3917 | |||||
| 3918 | fclose(f); | ||||
| 3919 | return 0; | ||||
| 3920 | } | ||||
| 3921 | |||||
| 3922 | /* | ||||
| 3923 | * Find network interface corresponding to the NBFT HFI | ||||
| 3924 | * by looking for mac address and vlan id. | ||||
| 3925 | */ | ||||
| 3926 | static char *nbft_find_hfi_iface(struct libnbft_hfi *hfi) | ||||
| 3927 | { | ||||
| 3928 | struct ifaddrs *ifaddr, *ifa; | ||||
| 3929 | char *result = NULL((void*)0); | ||||
| 3930 | |||||
| 3931 | if (strcmp((char *)hfi->transport, "tcp")) | ||||
| 3932 | return NULL((void*)0); | ||||
| 3933 | |||||
| 3934 | if (getifaddrs(&ifaddr) != 0) | ||||
| 3935 | return NULL((void*)0); | ||||
| 3936 | |||||
| 3937 | for (ifa = ifaddr; ifa != NULL((void*)0); ifa = ifa->ifa_next) { | ||||
| 3938 | struct sockaddr_ll *sll; | ||||
| 3939 | |||||
| 3940 | if (!ifa->ifa_addr) | ||||
| 3941 | continue; | ||||
| 3942 | |||||
| 3943 | if (ifa->ifa_addr->sa_family != AF_PACKET17) | ||||
| 3944 | continue; | ||||
| 3945 | |||||
| 3946 | sll = (struct sockaddr_ll *)ifa->ifa_addr; | ||||
| 3947 | |||||
| 3948 | if (sll->sll_halen != ETH_ALEN6) | ||||
| 3949 | continue; | ||||
| 3950 | |||||
| 3951 | if (!memcmp(sll->sll_addr, hfi->tcp_info.mac_addr, ETH_ALEN6)) { | ||||
| 3952 | int vlan_id = get_vlan_id(ifa->ifa_name); | ||||
| 3953 | |||||
| 3954 | if (vlan_id == hfi->tcp_info.vlan) { | ||||
| 3955 | result = strdup(ifa->ifa_name); | ||||
| 3956 | break; | ||||
| 3957 | } | ||||
| 3958 | } | ||||
| 3959 | } | ||||
| 3960 | |||||
| 3961 | freeifaddrs(ifaddr); | ||||
| 3962 | return result; | ||||
| 3963 | } | ||||
| 3964 | |||||
| 3965 | __shr_public__attribute__((visibility("default"))) int libnvmf_discover_nbft(struct libnvme_global_ctx *ctx, | ||||
| 3966 | struct libnvmf_context *fctx) | ||||
| 3967 | { | ||||
| 3968 | const char *hostnqn = NULL((void*)0), *hostid = NULL((void*)0), *host_traddr = NULL((void*)0); | ||||
| 3969 | char uuid[NVME_UUID_LEN_STRING37]; | ||||
| 3970 | struct nbft_file_entry *head = NULL((void*)0), *entry; | ||||
| 3971 | struct libnbft_subsystem_ns **ss; | ||||
| 3972 | struct libnbft_hfi *hfi; | ||||
| 3973 | struct libnbft_discovery **dd; | ||||
| 3974 | struct libnvme_host *h; | ||||
| 3975 | int ret, rr, i; | ||||
| 3976 | |||||
| 3977 | if (!fctx->nbft_path) | ||||
| 3978 | return -EINVAL22; | ||||
| 3979 | |||||
| 3980 | ret = libnvme_get_host(ctx, fctx->hostnqn, fctx->hostid, &h); | ||||
| 3981 | if (ret) | ||||
| 3982 | return ret; | ||||
| 3983 | |||||
| 3984 | ret = setup_connection(fctx, h, false0); | ||||
| 3985 | if (ret) | ||||
| 3986 | return ret; | ||||
| 3987 | |||||
| 3988 | if (!fctx->connect) | ||||
| 3989 | /* TODO: print discovery-type info from NBFT tables */ | ||||
| 3990 | return 0; | ||||
| 3991 | |||||
| 3992 | ret = libnvmf_nbft_read_files(ctx, fctx->nbft_path, &head); | ||||
| 3993 | if (ret) { | ||||
| 3994 | if (ret != -ENOENT2) | ||||
| 3995 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to access ACPI tables directory\n" ) | ||||
| 3996 | "Failed to access ACPI tables directory\n")__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Failed to access ACPI tables directory\n" ); | ||||
| 3997 | else | ||||
| 3998 | ret = 0; /* nothing to connect */ | ||||
| 3999 | goto out_free; | ||||
| 4000 | } | ||||
| 4001 | |||||
| 4002 | for (entry = head; entry; entry = entry->next) { | ||||
| 4003 | hostid = fctx->hostid; | ||||
| 4004 | if (fctx->hostnqn) | ||||
| 4005 | hostnqn = fctx->hostnqn; | ||||
| 4006 | else { | ||||
| 4007 | hostnqn = entry->nbft->host.nqn; | ||||
| 4008 | if (!hostnqn) | ||||
| 4009 | hostnqn = fctx->hostnqn; | ||||
| 4010 | } | ||||
| 4011 | |||||
| 4012 | if (!hostid && entry->nbft->host.id && *entry->nbft->host.id) { | ||||
| 4013 | ret = libnvme_uuid_to_string(entry->nbft->host.id, uuid); | ||||
| 4014 | if (!ret) | ||||
| 4015 | hostid = uuid; | ||||
| 4016 | } | ||||
| 4017 | |||||
| 4018 | ret = libnvme_get_host(ctx, hostnqn, hostid, &h); | ||||
| 4019 | if (ret) | ||||
| 4020 | goto out_free; | ||||
| 4021 | |||||
| 4022 | /* Subsystem Namespace Descriptor List */ | ||||
| 4023 | for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++) | ||||
| 4024 | for (i = 0; i < (*ss)->num_hfis; i++) { | ||||
| 4025 | __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) struct libnvme_ctrl_params params = | ||||
| 4026 | ctrl_params_copy(&fctx->ctrl_params); | ||||
| 4027 | |||||
| 4028 | hfi = (*ss)->hfis[i]; | ||||
| 4029 | if (!hfi) { | ||||
| 4030 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has no HFI at position %d\n" , (*ss)->index, i) | ||||
| 4031 | "SSNS %d has no HFI at position %d\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has no HFI at position %d\n" , (*ss)->index, i) | ||||
| 4032 | (*ss)->index, i)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has no HFI at position %d\n" , (*ss)->index, i); | ||||
| 4033 | ret = -EINVAL22; | ||||
| 4034 | continue; | ||||
| 4035 | } | ||||
| 4036 | |||||
| 4037 | /* Skip discovery NQN records */ | ||||
| 4038 | if (strcmp((*ss)->subsys_nqn, | ||||
| 4039 | NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery") == 0) { | ||||
| 4040 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n" , (*ss)->index) | ||||
| 4041 | "SSNS %d points to well-known discovery NQN, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n" , (*ss)->index) | ||||
| 4042 | (*ss)->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d points to well-known discovery NQN, skipping\n" , (*ss)->index); | ||||
| 4043 | continue; | ||||
| 4044 | } | ||||
| 4045 | |||||
| 4046 | if ((*ss)->security) { | ||||
| 4047 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*ss)->index, (*ss)->security->index) | ||||
| 4048 | "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*ss)->index, (*ss)->security->index) | ||||
| 4049 | (*ss)->index,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*ss)->index, (*ss)->security->index) | ||||
| 4050 | (*ss)->security->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*ss)->index, (*ss)->security->index); | ||||
| 4051 | continue; | ||||
| 4052 | } | ||||
| 4053 | |||||
| 4054 | params.host_traddr = NULL((void*)0); | ||||
| 4055 | if (!fctx->ctrl_params.host_traddr && | ||||
| 4056 | !strncmp((*ss)->transport, "tcp", 3)) | ||||
| 4057 | params.host_traddr = | ||||
| 4058 | hfi->tcp_info.ipaddr; | ||||
| 4059 | |||||
| 4060 | params.subsysnqn = (*ss)->subsys_nqn; | ||||
| 4061 | params.transport = (*ss)->transport; | ||||
| 4062 | params.traddr = (*ss)->traddr; | ||||
| 4063 | params.trsvcid = (*ss)->trsvcid; | ||||
| 4064 | params.host_iface = nbft_find_hfi_iface(hfi); | ||||
| 4065 | if (!params.host_iface) | ||||
| 4066 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: could not find host interface for HFI %d\n" , (*ss)->index, hfi->index) | ||||
| 4067 | "SSNS %d: could not find host interface for HFI %d\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: could not find host interface for HFI %d\n" , (*ss)->index, hfi->index) | ||||
| 4068 | (*ss)->index, hfi->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: could not find host interface for HFI %d\n" , (*ss)->index, hfi->index); | ||||
| 4069 | |||||
| 4070 | rr = nbft_connect(ctx, fctx, ¶ms, h, NULL((void*)0), | ||||
| 4071 | *ss, NULL((void*)0)); | ||||
| 4072 | |||||
| 4073 | /* | ||||
| 4074 | * With TCP/DHCP, it can happen that the OS | ||||
| 4075 | * obtains a different local IP address than the | ||||
| 4076 | * firmware had. Retry without host_traddr. | ||||
| 4077 | */ | ||||
| 4078 | if (rr == -ENVME_CONNECT_ADDRNOTAVAIL && | ||||
| 4079 | !strcmp(params.transport, | ||||
| 4080 | "tcp") && | ||||
| 4081 | strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) { | ||||
| 4082 | params.host_traddr = NULL((void*)0); | ||||
| 4083 | |||||
| 4084 | rr = nbft_connect(ctx, fctx, ¶ms, h, | ||||
| 4085 | NULL((void*)0), *ss, NULL((void*)0)); | ||||
| 4086 | |||||
| 4087 | if (rr == 0) | ||||
| 4088 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n" , (*ss)->index, host_traddr) | ||||
| 4089 | "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n" , (*ss)->index, host_traddr) | ||||
| 4090 | (*ss)->index,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n" , (*ss)->index, host_traddr) | ||||
| 4091 | host_traddr)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n" , (*ss)->index, host_traddr); | ||||
| 4092 | } | ||||
| 4093 | |||||
| 4094 | if (rr) { | ||||
| 4095 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n" , (*ss)->index) | ||||
| 4096 | "SSNS %d: no controller found\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n" , (*ss)->index) | ||||
| 4097 | (*ss)->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "SSNS %d: no controller found\n" , (*ss)->index); | ||||
| 4098 | /* report an error */ | ||||
| 4099 | ret = rr; | ||||
| 4100 | } | ||||
| 4101 | |||||
| 4102 | if (rr == -ENOMEM12) | ||||
| 4103 | goto out_free; | ||||
| 4104 | } | ||||
| 4105 | |||||
| 4106 | /* Discovery Descriptor List */ | ||||
| 4107 | for (dd = entry->nbft->discovery_list; dd && *dd; dd++) { | ||||
| 4108 | __cleanup_uri__attribute__((cleanup(free_uri))) struct libnvmf_uri *uri = NULL((void*)0); | ||||
| 4109 | __cleanup_free__attribute__((cleanup(shr_freep))) char *trsvcid = NULL((void*)0); | ||||
| 4110 | __cleanup_ctrl_params__attribute__((cleanup(cleanup_ctrl_params))) struct libnvme_ctrl_params params = | ||||
| 4111 | ctrl_params_copy(&fctx->ctrl_params); | ||||
| 4112 | enum dc_ownership own; | ||||
| 4113 | bool_Bool linked = false0; | ||||
| 4114 | struct libnvme_ctrl *c; | ||||
| 4115 | |||||
| 4116 | /* only perform discovery when no SSNS record references it */ | ||||
| 4117 | for (ss = entry->nbft->subsystem_ns_list; | ||||
| 4118 | ss && *ss; ss++) | ||||
| 4119 | if ((*ss)->discovery && | ||||
| 4120 | (*ss)->discovery->index == (*dd)->index && | ||||
| 4121 | /* unavailable boot attempts are not discovered | ||||
| 4122 | * and may get transferred along with a well-known | ||||
| 4123 | * discovery NQN into an SSNS record. | ||||
| 4124 | */ | ||||
| 4125 | strcmp((*ss)->subsys_nqn, | ||||
| 4126 | NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery") != 0) { | ||||
| 4127 | linked = true1; | ||||
| 4128 | break; | ||||
| 4129 | } | ||||
| 4130 | if (linked) | ||||
| 4131 | continue; | ||||
| 4132 | |||||
| 4133 | if ((*dd)->security) { | ||||
| 4134 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*dd)->index, (*dd)->security->index) | ||||
| 4135 | "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*dd)->index, (*dd)->security->index) | ||||
| 4136 | (*dd)->index,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*dd)->index, (*dd)->security->index) | ||||
| 4137 | (*dd)->security->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has Security Profile Descriptor %d associated, security profile descriptors are currently unimplemented, skipping\n" , (*dd)->index, (*dd)->security->index); | ||||
| 4138 | continue; | ||||
| 4139 | } | ||||
| 4140 | |||||
| 4141 | hfi = (*dd)->hfi; | ||||
| 4142 | if (!hfi) { | ||||
| 4143 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has no HFI\n" , (*dd)->index) | ||||
| 4144 | "Discovery Descriptor %d has no HFI\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has no HFI\n" , (*dd)->index) | ||||
| 4145 | (*dd)->index)__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d has no HFI\n" , (*dd)->index); | ||||
| 4146 | ret = -EINVAL22; | ||||
| 4147 | continue; | ||||
| 4148 | } | ||||
| 4149 | ret = libnvmf_uri_parse((*dd)->uri, &uri); | ||||
| 4150 | if (ret) | ||||
| 4151 | continue; | ||||
| 4152 | if (!validate_uri(ctx, *dd, uri)) | ||||
| 4153 | continue; | ||||
| 4154 | |||||
| 4155 | host_traddr = NULL((void*)0); | ||||
| 4156 | if (!fctx->ctrl_params.host_traddr && | ||||
| 4157 | !strncmp(uri->protocol, "tcp", 3)) | ||||
| 4158 | host_traddr = hfi->tcp_info.ipaddr; | ||||
| 4159 | if (uri->port > 0) { | ||||
| 4160 | if (asprintf(&trsvcid, "%d", uri->port) < 0) { | ||||
| 4161 | ret = -ENOMEM12; | ||||
| 4162 | goto out_free; | ||||
| 4163 | } | ||||
| 4164 | } else | ||||
| 4165 | trsvcid = | ||||
| 4166 | strdup(libnvmf_get_default_trsvcid( | ||||
| 4167 | uri->protocol, true1)); | ||||
| 4168 | |||||
| 4169 | params.subsysnqn = (*dd)->nqn ? | ||||
| 4170 | (*dd)->nqn : NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery"; | ||||
| 4171 | params.transport = uri->protocol; | ||||
| 4172 | params.traddr = uri->host; | ||||
| 4173 | params.trsvcid = trsvcid; | ||||
| 4174 | params.host_traddr = host_traddr; | ||||
| 4175 | params.host_iface = nbft_find_hfi_iface(hfi); | ||||
| 4176 | if (!params.host_iface) | ||||
| 4177 | libnvme_msg(ctx, LIBNVME_LOG_INFO,__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: could not find host interface for HFI %d\n" , (*dd)->index, hfi->index) | ||||
| 4178 | "Discovery Descriptor %d: could not find host interface for HFI %d\n",__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: could not find host interface for HFI %d\n" , (*dd)->index, hfi->index) | ||||
| 4179 | (*dd)->index, hfi->index)__libnvme_msg(ctx, LIBNVME_LOG_INFO, ((void*)0), "Discovery Descriptor %d: could not find host interface for HFI %d\n" , (*dd)->index, hfi->index); | ||||
| 4180 | |||||
| 4181 | /* | ||||
| 4182 | * NBFT boot discovery is a one-shot operation: never | ||||
| 4183 | * honor --no-reuse (dc_open()'s honor_no_reuse=false | ||||
| 4184 | * below) and never keep a freshly created connection | ||||
| 4185 | * alive past this walk (--persistent has no meaning | ||||
| 4186 | * here either). nbft_hfi and connect_leaf give | ||||
| 4187 | * dc_open() and the shared walker's leaf-connect step | ||||
| 4188 | * access to this DC's own HFI/leaf-connect quirks, | ||||
| 4189 | * several stack frames removed from this loop. | ||||
| 4190 | */ | ||||
| 4191 | fctx->nbft_hfi = hfi; | ||||
| 4192 | fctx->hooks.connect_leaf = nbft_connect_leaf; | ||||
| 4193 | fctx->persistent = LIBNVMF_PERSISTENT_NO; | ||||
| 4194 | |||||
| 4195 | ret = dc_open(ctx, fctx, h, ¶ms, false0, &own, &c); | ||||
| 4196 | if (ret) { | ||||
| 4197 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to add discovery controller: %s\n" , (*dd)->index, libnvme_strerror(-ret)) | ||||
| 4198 | "Discovery Descriptor %d: failed to add discovery controller: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to add discovery controller: %s\n" , (*dd)->index, libnvme_strerror(-ret)) | ||||
| 4199 | (*dd)->index, libnvme_strerror(-ret))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "Discovery Descriptor %d: failed to add discovery controller: %s\n" , (*dd)->index, libnvme_strerror(-ret)); | ||||
| 4200 | goto out_free; | ||||
| 4201 | } | ||||
| 4202 | |||||
| 4203 | rr = dc_walk(ctx, fctx, c, own); | ||||
| 4204 | libnvme_free_ctrl(c); | ||||
| 4205 | if (rr == -ENOMEM12) { | ||||
| 4206 | ret = rr; | ||||
| 4207 | goto out_free; | ||||
| 4208 | } | ||||
| 4209 | } | ||||
| 4210 | } | ||||
| 4211 | out_free: | ||||
| 4212 | libnvmf_nbft_free(ctx, head); | ||||
| 4213 | return ret; | ||||
| 4214 | } | ||||
| 4215 | |||||
| 4216 | __shr_public__attribute__((visibility("default"))) int libnvmf_discover(struct libnvme_global_ctx *ctx, | ||||
| 4217 | struct libnvmf_context *fctx) | ||||
| 4218 | { | ||||
| 4219 | struct libnvme_ctrl *c = NULL((void*)0); | ||||
| 4220 | struct libnvme_host *h; | ||||
| 4221 | enum dc_ownership own; | ||||
| 4222 | int err; | ||||
| 4223 | |||||
| 4224 | err = libnvme_get_host(ctx, fctx->hostnqn, fctx->hostid, &h); | ||||
| 4225 | if (err) | ||||
| 4226 | return err; | ||||
| 4227 | |||||
| 4228 | err = setup_connection(fctx, h, true1); | ||||
| 4229 | if (err) | ||||
| 4230 | return err; | ||||
| 4231 | |||||
| 4232 | err = dc_open(ctx, fctx, h, &fctx->ctrl_params, true1, &own, &c); | ||||
| 4233 | if (err) { | ||||
| 4234 | if (err != -ENVME_CONNECT_IGNORED) | ||||
| 4235 | libnvme_msg(ctx, LIBNVME_LOG_ERR,__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n" , libnvme_strerror(-err)) | ||||
| 4236 | "failed to add controller, error %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n" , libnvme_strerror(-err)) | ||||
| 4237 | libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "failed to add controller, error %s\n" , libnvme_strerror(-err)); | ||||
| 4238 | return err; | ||||
| 4239 | } | ||||
| 4240 | |||||
| 4241 | err = dc_walk(ctx, fctx, c, own); | ||||
| 4242 | libnvme_free_ctrl(c); | ||||
| 4243 | |||||
| 4244 | return err; | ||||
| 4245 | } | ||||
| 4246 | |||||
| 4247 | __shr_public__attribute__((visibility("default"))) int libnvmf_connect( | ||||
| 4248 | struct libnvme_global_ctx *ctx, struct libnvmf_context *fctx) | ||||
| 4249 | { | ||||
| 4250 | __cleanup_fd__attribute__((cleanup(shr_cleanup_fd))) int devid_fd = -1; | ||||
| 4251 | struct libnvme_host *h; | ||||
| 4252 | struct libnvme_ctrl *c; | ||||
| 4253 | int err; | ||||
| 4254 | |||||
| 4255 | /* Open before touching kernel state, so a bad path fails fast. */ | ||||
| 4256 | if (fctx->devid_file) { | ||||
| 4257 | devid_fd = open_devid_file(fctx); | ||||
| 4258 | if (devid_fd < 0) | ||||
| 4259 | return devid_fd; | ||||
| 4260 | } | ||||
| 4261 | |||||
| 4262 | err = libnvme_get_host(ctx, fctx->hostnqn, fctx->hostid, &h); | ||||
| 4263 | if (err) | ||||
| 4264 | return err; | ||||
| 4265 | |||||
| 4266 | err = setup_connection(fctx, h, false0); | ||||
| 4267 | if (err) | ||||
| 4268 | return err; | ||||
| 4269 | |||||
| 4270 | c = lookup_live_ctrl(h, &fctx->ctrl_params); | ||||
| 4271 | if (c && !fctx->ctrl_params.cfg.duplicate_connect) { | ||||
| 4272 | int instance = ctrl_instance(c); | ||||
| 4273 | |||||
| 4274 | write_devid_file(fctx, devid_fd, c); | ||||
| 4275 | if (instance >= 0) | ||||
| 4276 | registry_update_on_connect(ctx, instance, false0); | ||||
| 4277 | if (fctx->hooks.already_connected) | ||||
| 4278 | fctx->hooks.already_connected(fctx, h, | ||||
| 4279 | libnvme_ctrl_get_subsysnqn(c), | ||||
| 4280 | libnvme_ctrl_get_transport(c), | ||||
| 4281 | libnvme_ctrl_get_traddr(c), | ||||
| 4282 | libnvme_ctrl_get_trsvcid(c), | ||||
| 4283 | fctx->hooks.user_data); | ||||
| 4284 | return -ENVME_CONNECT_ALREADY; | ||||
| 4285 | } | ||||
| 4286 | |||||
| 4287 | err = libnvmf_create_ctrl(ctx, fctx, &c); | ||||
| 4288 | if (err) | ||||
| 4289 | return err; | ||||
| 4290 | |||||
| 4291 | /* | ||||
| 4292 | * We are connecting to a discovery controller, so let's treat | ||||
| 4293 | * this as a persistent connection and specify a KATO. | ||||
| 4294 | */ | ||||
| 4295 | if (!strcmp(fctx->ctrl_params.subsysnqn, NVME_DISC_SUBSYS_NAME"nqn.2014-08.org.nvmexpress.discovery")) { | ||||
| 4296 | fctx->persistent = LIBNVMF_PERSISTENT_FORCE; | ||||
| 4297 | |||||
| 4298 | set_discovery_kato(fctx, &fctx->ctrl_params); | ||||
| 4299 | } | ||||
| 4300 | |||||
| 4301 | err = libnvme_add_ctrl(fctx, h, c); | ||||
| 4302 | if (err) { | ||||
| 4303 | /* | ||||
| 4304 | * Kernel-level race: something else connected between our | ||||
| 4305 | * scan and this ioctl. @c is our own unconnected draft, not | ||||
| 4306 | * the winner. | ||||
| 4307 | */ | ||||
| 4308 | if (err == -ENVME_CONNECT_ALREADY) { | ||||
| 4309 | if (libnvme_scan_topology(ctx, NULL((void*)0), NULL((void*)0)) == 0) { | ||||
| 4310 | struct libnvme_ctrl *winner; | ||||
| 4311 | |||||
| 4312 | winner = lookup_ctrl(h, &fctx->ctrl_params); | ||||
| 4313 | if (winner) { | ||||
| 4314 | int instance = ctrl_instance(winner); | ||||
| 4315 | |||||
| 4316 | write_devid_file(fctx, devid_fd, winner); | ||||
| 4317 | if (instance >= 0) | ||||
| 4318 | registry_update_on_connect(ctx, | ||||
| 4319 | instance, false0); | ||||
| 4320 | } | ||||
| 4321 | } | ||||
| 4322 | |||||
| 4323 | if (fctx->hooks.already_connected) | ||||
| 4324 | fctx->hooks.already_connected(fctx, h, | ||||
| 4325 | fctx->ctrl_params.subsysnqn, | ||||
| 4326 | fctx->ctrl_params.transport, | ||||
| 4327 | fctx->ctrl_params.traddr, | ||||
| 4328 | fctx->ctrl_params.trsvcid, | ||||
| 4329 | fctx->hooks.user_data); | ||||
| 4330 | } else { | ||||
| 4331 | libnvme_msg(ctx, LIBNVME_LOG_ERR, "could not add new controller: %s\n",__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not add new controller: %s\n" , libnvme_strerror(-err)) | ||||
| 4332 | libnvme_strerror(-err))__libnvme_msg(ctx, LIBNVME_LOG_ERR, ((void*)0), "could not add new controller: %s\n" , libnvme_strerror(-err)); | ||||
| 4333 | } | ||||
| 4334 | |||||
| 4335 | libnvme_free_ctrl(c); | ||||
| 4336 | return err; | ||||
| 4337 | } | ||||
| 4338 | |||||
| 4339 | write_devid_file(fctx, devid_fd, c); | ||||
| 4340 | if (fctx->hooks.connected) | ||||
| 4341 | fctx->hooks.connected(fctx, c, fctx->hooks.user_data); | ||||
| 4342 | |||||
| 4343 | return 0; | ||||
| 4344 | } |
| 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| 2 | /* |
| 3 | * This file is part of nvme-cli. |
| 4 | * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries. |
| 5 | * |
| 6 | * Authors: Martin Belanger <martin.belanger@dell.com> |
| 7 | */ |
| 8 | #pragma once |
| 9 | |
| 10 | #include <ctype.h> |
| 11 | #include <stdbool.h> |
| 12 | #include <stdio.h> |
| 13 | #include <string.h> |
| 14 | #include <strings.h> |
| 15 | |
| 16 | #ifndef HAVE_STRSEP |
| 17 | /* strsep() is missing on some platforms (e.g. mingw/MSVC runtimes). */ |
| 18 | static inline char *strsep(char **stringp, const char *delim) |
| 19 | { |
| 20 | char *s, *end; |
| 21 | |
| 22 | if (!stringp || !*stringp) |
| 23 | return NULL((void*)0); |
| 24 | |
| 25 | s = *stringp; |
| 26 | end = s + strcspn(s, delim); |
| 27 | |
| 28 | if (*end) |
| 29 | *end++ = '\0'; |
| 30 | else |
| 31 | end = NULL((void*)0); |
| 32 | |
| 33 | *stringp = end; |
| 34 | return s; |
| 35 | } |
| 36 | #endif |
| 37 | |
| 38 | /* |
| 39 | * NULL-safe string equality: two NULLs are equal, one NULL and one |
| 40 | * non-NULL are never equal, otherwise this is strcmp() == 0. |
| 41 | */ |
| 42 | static inline bool_Bool shr_streq0(const char *s1, const char *s2) |
| 43 | { |
| 44 | if (s1 == s2) |
| 45 | return true1; |
| 46 | if (!s1 || !s2) |
| 47 | return false0; |
| 48 | return !strcmp(s1, s2); |
| 49 | } |
| 50 | |
| 51 | /* Case-insensitive sibling of shr_streq0(). */ |
| 52 | static inline bool_Bool shr_streqcase0(const char *s1, const char *s2) |
| 53 | { |
| 54 | if (s1 == s2) |
| 55 | return true1; |
| 56 | if (!s1 || !s2) |
| 57 | return false0; |
| 58 | return !strcasecmp(s1, s2); |
| 59 | } |
| 60 | |
| 61 | /* Allocation-checking strdup() that returns NULL for a NULL input. */ |
| 62 | static inline char *shr_xstrdup(const char *s) |
| 63 | { |
| 64 | return s ? strdup(s) : NULL((void*)0); |
| 65 | } |
| 66 | |
| 67 | /* Lower-cases str in place. */ |
| 68 | static inline void shr_strtolower(char *str) |
| 69 | { |
| 70 | for (; *str; str++) |
| 71 | *str = tolower((unsigned char)*str)(__extension__ ({ int __res; if (sizeof ((unsigned char)*str) > 1) { if (__builtin_constant_p ((unsigned char)*str)) { int __c = ((unsigned char)*str); __res = __c < -128 || __c > 255 ? __c : (*__ctype_tolower_loc ())[__c]; } else __res = tolower ((unsigned char)*str); } else __res = (*__ctype_tolower_loc ( ))[(int) ((unsigned char)*str)]; __res; })); |
| 72 | } |
| 73 | |
| 74 | /* |
| 75 | * Trim trailing whitespace from s in place: the byte after the last |
| 76 | * non-whitespace character is overwritten with '\0'. Returns s. |
| 77 | */ |
| 78 | static inline char *shr_rtrim(char *s) |
| 79 | { |
| 80 | char *end = s + strlen(s); |
| 81 | |
| 82 | while (end > s && isspace((unsigned char)end[-1])((*__ctype_b_loc ())[(int) (((unsigned char)end[-1]))] & ( unsigned short int) _ISspace)) |
| 83 | end--; |
| 84 | *end = '\0'; |
| 85 | return s; |
| 86 | } |
| 87 | |
| 88 | /* |
| 89 | * Copy a fixed-size, not-necessarily-NUL-terminated wire field @s of |
| 90 | * size @sz into a newly allocated, NUL-terminated, right-trimmed C |
| 91 | * string. "%.*s" bounds the read to @sz regardless of whether s |
| 92 | * contains a NUL. Returns NULL on allocation failure. |
| 93 | */ |
| 94 | static inline char *shr_buf2str(const char s[], size_t sz) |
| 95 | { |
| 96 | char *p; |
| 97 | |
| 98 | if (asprintf(&p, "%.*s", (int)sz, s) < 0) |
| 99 | return NULL((void*)0); |
| 100 | |
| 101 | return shr_rtrim(p); |
| 102 | } |
| 103 | |
| 104 | /* |
| 105 | * Return a pointer to the first non-whitespace character in s. s itself |
| 106 | * is not modified. |
| 107 | */ |
| 108 | static inline char *shr_ltrim(char *s) |
| 109 | { |
| 110 | return s + strspn(s, " \t\n\r\v\f"); |
| 111 | } |
| 112 | |
| 113 | /* |
| 114 | * Trim leading and trailing whitespace from s in place and return a |
| 115 | * pointer to the first non-whitespace character. s itself is modified: |
| 116 | * the byte after the last non-whitespace character is overwritten with |
| 117 | * '\0'. |
| 118 | */ |
| 119 | static inline char *shr_trim(char *s) |
| 120 | { |
| 121 | return shr_ltrim(shr_rtrim(s)); |
| 122 | } |
| 123 | |
| 124 | /* True if c may appear in a name: alphanumeric, '_', or '-'. */ |
| 125 | static inline bool_Bool shr_name_char(char c) |
| 126 | { |
| 127 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || |
| 128 | (c >= '0' && c <= '9') || c == '_' || c == '-'; |
| 129 | } |
| 130 | |
| 131 | /* True if s is non-empty and every character is alphanumeric, '_', or '-'. */ |
| 132 | static inline bool_Bool shr_valid_name(const char *s) |
| 133 | { |
| 134 | const char *p; |
| 135 | |
| 136 | if (!s || !*s) |
| 137 | return false0; |
| 138 | for (p = s; *p; p++) { |
| 139 | if (!shr_name_char(*p)) |
| 140 | return false0; |
| 141 | } |
| 142 | return true1; |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | * Replace every character shr_valid_name() would reject with '_', in place, |
| 147 | * and return s. This turns a field a device supplies, such as a serial |
| 148 | * number, into something that can go into a file name; pair it with |
| 149 | * shr_rtrim() so that trailing padding does not become underscores. |
| 150 | * A NULL s is returned unchanged. |
| 151 | */ |
| 152 | static inline char *shr_sanitize_name(char *s) |
| 153 | { |
| 154 | char *p; |
| 155 | |
| 156 | if (!s) |
| 157 | return s; |
| 158 | for (p = s; *p; p++) { |
| 159 | if (!shr_name_char(*p)) |
| 160 | *p = '_'; |
| 161 | } |
| 162 | return s; |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | * If s starts with prefix, return a pointer within s just past the match. |
| 167 | * NULL otherwise. |
| 168 | */ |
| 169 | static inline char *shr_startswith(const char *s, const char *prefix) |
| 170 | { |
| 171 | size_t l = strlen(prefix); |
| 172 | |
| 173 | if (!strncmp(s, prefix, l)) |
| 174 | return (char *)s + l; |
| 175 | |
| 176 | return NULL((void*)0); |
| 177 | } |
| 178 | |
| 179 | /* |
| 180 | * Strip leading/trailing blanks and a trailing "# comment" from the |
| 181 | * key=value line kv, in place. Return a pointer to the stripped string. |
| 182 | */ |
| 183 | static inline char *shr_kv_strip(char *kv) |
| 184 | { |
| 185 | char *s; |
| 186 | |
| 187 | kv[strcspn(kv, "\n\r")] = '\0'; |
| 188 | |
| 189 | /* Remove leading newline and spaces */ |
| 190 | kv += strspn(kv, " \t\n\r"); |
| 191 | |
| 192 | /* Skip comments and empty lines */ |
| 193 | if (*kv == '#' || *kv == '\0') { |
| 194 | *kv = '\0'; |
| 195 | return kv; |
| 196 | } |
| 197 | |
| 198 | /* Remove trailing newline chars */ |
| 199 | kv[strcspn(kv, "\n\r")] = '\0'; |
| 200 | |
| 201 | /* Delete trailing comments (including spaces/tabs that precede the #)*/ |
| 202 | s = &kv[strcspn(kv, "#")]; |
| 203 | *s-- = '\0'; |
| 204 | while (s >= kv && (*s == ' ' || *s == '\t')) |
| 205 | *s-- = '\0'; |
| 206 | |
| 207 | return kv; |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | * If kv is a whole-word match for "key" at the start of a key=value line, |
| 212 | * return a pointer to the first character of value (past spaces/tabs/'='). |
| 213 | * NULL otherwise. |
| 214 | */ |
| 215 | static inline char *shr_kv_keymatch(const char *kv, const char *key) |
| 216 | { |
| 217 | char *value; |
| 218 | |
| 219 | value = shr_startswith(kv, key); |
| 220 | if (value && (*value == ' ' || *value == '\t' || *value == '=')) |
| 221 | return value + strspn(value, " \t="); |
| 222 | |
| 223 | return NULL((void*)0); |
| 224 | } |