1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2022 Dell Inc, or its subsidiaries. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 #include "spdk/version.h" 8 9 #include "spdk_internal/event.h" 10 11 #include "spdk/assert.h" 12 #include "spdk/config.h" 13 #include "spdk/env.h" 14 #include "spdk/init.h" 15 #include "spdk/log.h" 16 #include "spdk/thread.h" 17 #include "spdk/trace.h" 18 #include "spdk/string.h" 19 #include "spdk/scheduler.h" 20 #include "spdk/rpc.h" 21 #include "spdk/util.h" 22 #include "spdk/nvme.h" 23 #include "spdk/module/bdev/nvme.h" 24 #include "bdev_nvme.h" 25 26 #ifdef SPDK_CONFIG_AVAHI 27 #include <avahi-client/client.h> 28 #include <avahi-client/lookup.h> 29 #include <avahi-common/simple-watch.h> 30 #include <avahi-common/malloc.h> 31 #include <avahi-common/error.h> 32 33 static AvahiSimplePoll *g_avahi_simple_poll = NULL; 34 static AvahiClient *g_avahi_client = NULL; 35 36 struct mdns_discovery_entry_ctx { 37 char name[256]; 38 struct spdk_nvme_transport_id trid; 39 struct spdk_nvme_ctrlr_opts drv_opts; 40 TAILQ_ENTRY(mdns_discovery_entry_ctx) tailq; 41 struct mdns_discovery_ctx *ctx; 42 }; 43 44 struct mdns_discovery_ctx { 45 char *name; 46 char *svcname; 47 char *hostnqn; 48 AvahiServiceBrowser *sb; 49 struct spdk_poller *poller; 50 struct spdk_nvme_ctrlr_opts drv_opts; 51 struct spdk_bdev_nvme_ctrlr_opts bdev_opts; 52 uint32_t seqno; 53 bool stop; 54 struct spdk_thread *calling_thread; 55 TAILQ_ENTRY(mdns_discovery_ctx) tailq; 56 TAILQ_HEAD(, mdns_discovery_entry_ctx) mdns_discovery_entry_ctxs; 57 }; 58 59 TAILQ_HEAD(mdns_discovery_ctxs, mdns_discovery_ctx); 60 static struct mdns_discovery_ctxs g_mdns_discovery_ctxs = TAILQ_HEAD_INITIALIZER( 61 g_mdns_discovery_ctxs); 62 63 static struct mdns_discovery_entry_ctx * 64 create_mdns_discovery_entry_ctx(struct mdns_discovery_ctx *ctx, struct spdk_nvme_transport_id *trid) 65 { 66 struct mdns_discovery_entry_ctx *new_ctx; 67 68 assert(ctx); 69 assert(trid); 70 new_ctx = calloc(1, sizeof(*new_ctx)); 71 if (new_ctx == NULL) { 72 SPDK_ERRLOG("could not allocate new mdns_entry_ctx\n"); 73 return NULL; 74 } 75 76 new_ctx->ctx = ctx; 77 memcpy(&new_ctx->trid, trid, sizeof(struct spdk_nvme_transport_id)); 78 snprintf(new_ctx->name, sizeof(new_ctx->name), "%s%u_nvme", ctx->name, ctx->seqno); 79 memcpy(&new_ctx->drv_opts, &ctx->drv_opts, sizeof(ctx->drv_opts)); 80 snprintf(new_ctx->drv_opts.hostnqn, sizeof(ctx->drv_opts.hostnqn), "%s", ctx->hostnqn); 81 ctx->seqno = ctx->seqno + 1; 82 return new_ctx; 83 } 84 85 static void 86 mdns_bdev_nvme_start_discovery(void *_entry_ctx) 87 { 88 int status; 89 struct mdns_discovery_entry_ctx *entry_ctx = _entry_ctx; 90 91 assert(_entry_ctx); 92 status = bdev_nvme_start_discovery(&entry_ctx->trid, entry_ctx->name, 93 &entry_ctx->ctx->drv_opts, 94 &entry_ctx->ctx->bdev_opts, 95 0, true, NULL, NULL); 96 if (status) { 97 SPDK_ERRLOG("Error starting discovery for name %s addr %s port %s subnqn %s &trid %p\n", 98 entry_ctx->ctx->name, entry_ctx->trid.traddr, entry_ctx->trid.trsvcid, 99 entry_ctx->trid.subnqn, &entry_ctx->trid); 100 } 101 } 102 103 static void 104 free_mdns_discovery_entry_ctx(struct mdns_discovery_ctx *ctx) 105 { 106 struct mdns_discovery_entry_ctx *entry_ctx, *tmp; 107 108 if (!ctx) { 109 return; 110 } 111 112 TAILQ_FOREACH_SAFE(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq, tmp) { 113 TAILQ_REMOVE(&ctx->mdns_discovery_entry_ctxs, entry_ctx, tailq); 114 free(entry_ctx); 115 } 116 } 117 118 static void 119 free_mdns_discovery_ctx(struct mdns_discovery_ctx *ctx) 120 { 121 if (!ctx) { 122 return; 123 } 124 125 free(ctx->name); 126 free(ctx->svcname); 127 free(ctx->hostnqn); 128 avahi_service_browser_free(ctx->sb); 129 free_mdns_discovery_entry_ctx(ctx); 130 free(ctx); 131 } 132 133 /* get_key_val_avahi_resolve_txt - Search for the key string in the TXT received 134 * from Avavi daemon and return its value. 135 * input 136 * txt: TXT returned by Ahavi daemon will be of format 137 * "NQN=nqn.1988-11.com.dell:SFSS:1:20221122170722e8" "p=tcp foo" and the 138 * AvahiStringList txt is a linked list with each node holding a 139 * key-value pair like key:p value:tcp 140 * 141 * key: Key string to search in the txt list 142 * output 143 * Returns the value for the key or NULL if key is not present 144 * Returned string needs to be freed with avahi_free() 145 */ 146 static char * 147 get_key_val_avahi_resolve_txt(AvahiStringList *txt, const char *key) 148 { 149 char *k = NULL, *v = NULL; 150 AvahiStringList *p = NULL; 151 int r; 152 153 if (!txt || !key) { 154 return NULL; 155 } 156 157 p = avahi_string_list_find(txt, key); 158 if (!p) { 159 return NULL; 160 } 161 162 r = avahi_string_list_get_pair(p, &k, &v, NULL); 163 if (r < 0) { 164 return NULL; 165 } 166 167 avahi_free(k); 168 return v; 169 } 170 171 static int 172 get_spdk_nvme_transport_from_proto_str(char *protocol, enum spdk_nvme_transport_type *trtype) 173 { 174 int status = -1; 175 176 if (!protocol || !trtype) { 177 return status; 178 } 179 180 if (strcmp("tcp", protocol) == 0) { 181 *trtype = SPDK_NVME_TRANSPORT_TCP; 182 return 0; 183 } 184 185 return status; 186 } 187 188 static enum spdk_nvmf_adrfam 189 get_spdk_nvme_adrfam_from_avahi_addr(const AvahiAddress *address) { 190 191 if (!address) 192 { 193 /* Return ipv4 by default */ 194 return SPDK_NVMF_ADRFAM_IPV4; 195 } 196 197 switch (address->proto) 198 { 199 case AVAHI_PROTO_INET: 200 return SPDK_NVMF_ADRFAM_IPV4; 201 case AVAHI_PROTO_INET6: 202 return SPDK_NVMF_ADRFAM_IPV6; 203 default: 204 return SPDK_NVMF_ADRFAM_IPV4; 205 } 206 } 207 208 static struct mdns_discovery_ctx * 209 get_mdns_discovery_ctx_by_svcname(const char *svcname) 210 { 211 struct mdns_discovery_ctx *ctx = NULL, *tmp_ctx = NULL; 212 213 if (!svcname) { 214 return NULL; 215 } 216 217 TAILQ_FOREACH_SAFE(ctx, &g_mdns_discovery_ctxs, tailq, tmp_ctx) { 218 if (strcmp(ctx->svcname, svcname) == 0) { 219 return ctx; 220 } 221 } 222 return NULL; 223 } 224 225 static void 226 mdns_resolve_handler( 227 AvahiServiceResolver *resolver, 228 AVAHI_GCC_UNUSED AvahiIfIndex intf, 229 AVAHI_GCC_UNUSED AvahiProtocol avahi_protocol, 230 AvahiResolverEvent resolve_event, 231 const char *svc_name, 232 const char *svc_type, 233 const char *svc_domain, 234 const char *host_name, 235 const AvahiAddress *host_address, 236 uint16_t port, 237 AvahiStringList *txt, 238 AvahiLookupResultFlags result_flags, 239 AVAHI_GCC_UNUSED void *user_data) 240 { 241 assert(resolver); 242 /* The handler gets called whenever a service has been resolved 243 successfully or timed out */ 244 switch (resolve_event) { 245 case AVAHI_RESOLVER_FOUND: { 246 char ipaddr[SPDK_NVMF_TRADDR_MAX_LEN + 1], port_str[SPDK_NVMF_TRSVCID_MAX_LEN + 1], *str; 247 struct spdk_nvme_transport_id *trid = NULL; 248 char *subnqn = NULL, *proto = NULL; 249 struct mdns_discovery_ctx *ctx = NULL; 250 struct mdns_discovery_entry_ctx *entry_ctx = NULL; 251 int status = -1; 252 253 memset(ipaddr, 0, sizeof(ipaddr)); 254 memset(port_str, 0, sizeof(port_str)); 255 SPDK_INFOLOG(bdev_nvme, "Service '%s' of type '%s' in domain '%s'\n", svc_name, svc_type, 256 svc_domain); 257 avahi_address_snprint(ipaddr, sizeof(ipaddr), host_address); 258 snprintf(port_str, sizeof(port_str), "%d", port); 259 str = avahi_string_list_to_string(txt); 260 SPDK_INFOLOG(bdev_nvme, 261 "\t%s:%u (%s)\n" 262 "\tTXT=%s\n" 263 "\tcookie is %u\n" 264 "\tis_local: %i\n" 265 "\tour_own: %i\n" 266 "\twide_area: %i\n" 267 "\tmulticast: %i\n" 268 "\tcached: %i\n", 269 host_name, port, ipaddr, 270 str, 271 avahi_string_list_get_service_cookie(txt), 272 !!(result_flags & AVAHI_LOOKUP_RESULT_LOCAL), 273 !!(result_flags & AVAHI_LOOKUP_RESULT_OUR_OWN), 274 !!(result_flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), 275 !!(result_flags & AVAHI_LOOKUP_RESULT_MULTICAST), 276 !!(result_flags & AVAHI_LOOKUP_RESULT_CACHED)); 277 avahi_free(str); 278 279 ctx = get_mdns_discovery_ctx_by_svcname(svc_type); 280 if (!ctx) { 281 SPDK_ERRLOG("Unknown Service '%s'\n", svc_type); 282 break; 283 } 284 285 trid = (struct spdk_nvme_transport_id *) calloc(1, sizeof(struct spdk_nvme_transport_id)); 286 if (!trid) { 287 SPDK_ERRLOG(" Error allocating memory for trid\n"); 288 break; 289 } 290 trid->adrfam = get_spdk_nvme_adrfam_from_avahi_addr(host_address); 291 if (trid->adrfam != SPDK_NVMF_ADRFAM_IPV4) { 292 /* TODO: For now process only ipv4 addresses */ 293 SPDK_INFOLOG(bdev_nvme, "trid family is not IPV4 %d\n", trid->adrfam); 294 free(trid); 295 break; 296 } 297 subnqn = get_key_val_avahi_resolve_txt(txt, "NQN"); 298 if (!subnqn) { 299 free(trid); 300 SPDK_ERRLOG("subnqn received is empty for service %s\n", ctx->svcname); 301 break; 302 } 303 proto = get_key_val_avahi_resolve_txt(txt, "p"); 304 if (!proto) { 305 free(trid); 306 avahi_free(subnqn); 307 SPDK_ERRLOG("Protocol not received for service %s\n", ctx->svcname); 308 break; 309 } 310 status = get_spdk_nvme_transport_from_proto_str(proto, &trid->trtype); 311 if (status) { 312 free(trid); 313 avahi_free(subnqn); 314 avahi_free(proto); 315 SPDK_ERRLOG("Unable to derive nvme transport type for service %s\n", ctx->svcname); 316 break; 317 } 318 snprintf(trid->traddr, sizeof(trid->traddr), "%s", ipaddr); 319 snprintf(trid->trsvcid, sizeof(trid->trsvcid), "%s", port_str); 320 snprintf(trid->subnqn, sizeof(trid->subnqn), "%s", subnqn); 321 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 322 if (!spdk_nvme_transport_id_compare(trid, &entry_ctx->trid)) { 323 SPDK_ERRLOG("mDNS discovery entry exists already. trid->traddr: %s trid->trsvcid: %s\n", 324 trid->traddr, trid->trsvcid); 325 free(trid); 326 avahi_free(subnqn); 327 avahi_free(proto); 328 avahi_service_resolver_free(resolver); 329 return; 330 } 331 } 332 entry_ctx = create_mdns_discovery_entry_ctx(ctx, trid); 333 TAILQ_INSERT_TAIL(&ctx->mdns_discovery_entry_ctxs, entry_ctx, tailq); 334 spdk_thread_send_msg(ctx->calling_thread, mdns_bdev_nvme_start_discovery, entry_ctx); 335 free(trid); 336 avahi_free(subnqn); 337 avahi_free(proto); 338 break; 339 } 340 case AVAHI_RESOLVER_FAILURE: 341 SPDK_ERRLOG("(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", 342 svc_name, svc_type, svc_domain, 343 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver)))); 344 break; 345 default: 346 SPDK_ERRLOG("Unknown Avahi resolver event: %d", resolve_event); 347 } 348 avahi_service_resolver_free(resolver); 349 } 350 351 static void 352 mdns_browse_handler( 353 AvahiServiceBrowser *browser, 354 AvahiIfIndex intf, 355 AvahiProtocol avahi_protocol, 356 AvahiBrowserEvent browser_event, 357 const char *svc_name, 358 const char *svc_type, 359 const char *svc_domain, 360 AVAHI_GCC_UNUSED AvahiLookupResultFlags result_flags, 361 void *user_data) 362 { 363 AvahiClient *client = user_data; 364 365 assert(browser); 366 /* The handler gets called whenever a new service becomes available 367 or removed from the LAN */ 368 switch (browser_event) { 369 case AVAHI_BROWSER_NEW: 370 SPDK_DEBUGLOG(bdev_nvme, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", svc_name, 371 svc_type, 372 svc_domain); 373 /* We ignore the returned resolver object. In the callback 374 function we free it. If the server is terminated before 375 the callback function is called the server will free 376 the resolver for us. */ 377 if (!(avahi_service_resolver_new(client, intf, avahi_protocol, svc_name, svc_type, svc_domain, 378 AVAHI_PROTO_UNSPEC, 0, 379 mdns_resolve_handler, client))) { 380 SPDK_ERRLOG("Failed to resolve service '%s': %s\n", svc_name, 381 avahi_strerror(avahi_client_errno(client))); 382 } 383 break; 384 case AVAHI_BROWSER_REMOVE: 385 SPDK_ERRLOG("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", svc_name, svc_type, 386 svc_domain); 387 /* On remove, we are not doing the automatic cleanup of connections 388 * to the targets that were learnt from the CDC, for which remove event has 389 * been received. If required, user can clear the connections manually by 390 * invoking bdev_nvme_stop_discovery. We can implement the automatic cleanup 391 * later, if there is a requirement in the future. 392 */ 393 break; 394 case AVAHI_BROWSER_ALL_FOR_NOW: 395 case AVAHI_BROWSER_CACHE_EXHAUSTED: 396 SPDK_INFOLOG(bdev_nvme, "(Browser) %s\n", 397 browser_event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); 398 break; 399 case AVAHI_BROWSER_FAILURE: 400 SPDK_ERRLOG("(Browser) Failure: %s\n", 401 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(browser)))); 402 return; 403 default: 404 SPDK_ERRLOG("Unknown Avahi browser event: %d", browser_event); 405 } 406 } 407 408 static void 409 client_handler(AvahiClient *client, AvahiClientState avahi_state, AVAHI_GCC_UNUSED void *user_data) 410 { 411 assert(client); 412 /* The handler gets called whenever the client or server state changes */ 413 if (avahi_state == AVAHI_CLIENT_FAILURE) { 414 SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(client))); 415 } 416 } 417 418 static int 419 bdev_nvme_avahi_iterate(void *arg) 420 { 421 struct mdns_discovery_ctx *ctx = arg; 422 int rc; 423 424 if (ctx->stop) { 425 SPDK_INFOLOG(bdev_nvme, "Stopping avahi poller for service %s\n", ctx->svcname); 426 spdk_poller_unregister(&ctx->poller); 427 TAILQ_REMOVE(&g_mdns_discovery_ctxs, ctx, tailq); 428 free_mdns_discovery_ctx(ctx); 429 return SPDK_POLLER_IDLE; 430 } 431 432 if (g_avahi_simple_poll == NULL) { 433 spdk_poller_unregister(&ctx->poller); 434 return SPDK_POLLER_IDLE; 435 } 436 437 rc = avahi_simple_poll_iterate(g_avahi_simple_poll, 0); 438 if (rc && rc != -EAGAIN) { 439 SPDK_ERRLOG("avahi poll returned error for service: %s/n", ctx->svcname); 440 return SPDK_POLLER_IDLE; 441 } 442 443 return SPDK_POLLER_BUSY; 444 } 445 446 static void 447 start_mdns_discovery_poller(void *arg) 448 { 449 struct mdns_discovery_ctx *ctx = arg; 450 451 assert(arg); 452 TAILQ_INSERT_TAIL(&g_mdns_discovery_ctxs, ctx, tailq); 453 ctx->poller = SPDK_POLLER_REGISTER(bdev_nvme_avahi_iterate, ctx, 100 * 1000); 454 } 455 456 int 457 bdev_nvme_start_mdns_discovery(const char *base_name, 458 const char *svcname, 459 struct spdk_nvme_ctrlr_opts *drv_opts, 460 struct spdk_bdev_nvme_ctrlr_opts *bdev_opts) 461 { 462 AvahiServiceBrowser *sb = NULL; 463 int error; 464 struct mdns_discovery_ctx *ctx; 465 466 assert(base_name); 467 assert(svcname); 468 469 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 470 if (strcmp(ctx->name, base_name) == 0) { 471 SPDK_ERRLOG("mDNS discovery already running with name %s\n", base_name); 472 return -EEXIST; 473 } 474 475 if (strcmp(ctx->svcname, svcname) == 0) { 476 SPDK_ERRLOG("mDNS discovery already running for service %s\n", svcname); 477 return -EEXIST; 478 } 479 } 480 481 if (g_avahi_simple_poll == NULL) { 482 483 /* Allocate main loop object */ 484 if (!(g_avahi_simple_poll = avahi_simple_poll_new())) { 485 SPDK_ERRLOG("Failed to create poll object for mDNS discovery for service: %s.\n", svcname); 486 return -ENOMEM; 487 } 488 } 489 490 if (g_avahi_client == NULL) { 491 492 /* Allocate a new client */ 493 g_avahi_client = avahi_client_new(avahi_simple_poll_get(g_avahi_simple_poll), 0, client_handler, 494 NULL, &error); 495 /* Check whether creating the client object succeeded */ 496 if (!g_avahi_client) { 497 SPDK_ERRLOG("Failed to create mDNS client for service:%s Error: %s\n", svcname, 498 avahi_strerror(error)); 499 return -ENOMEM; 500 } 501 } 502 503 /* Create the service browser */ 504 if (!(sb = avahi_service_browser_new(g_avahi_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, svcname, 505 NULL, 0, mdns_browse_handler, g_avahi_client))) { 506 SPDK_ERRLOG("Failed to create service browser for service: %s Error: %s\n", svcname, 507 avahi_strerror(avahi_client_errno(g_avahi_client))); 508 return -ENOMEM; 509 } 510 511 ctx = calloc(1, sizeof(*ctx)); 512 if (ctx == NULL) { 513 SPDK_ERRLOG("Error creating mDNS discovery ctx for service: %s\n", svcname); 514 avahi_service_browser_free(sb); 515 return -ENOMEM; 516 } 517 518 ctx->svcname = strdup(svcname); 519 if (ctx->svcname == NULL) { 520 SPDK_ERRLOG("Error creating mDNS discovery ctx svcname for service: %s\n", svcname); 521 free_mdns_discovery_ctx(ctx); 522 avahi_service_browser_free(sb); 523 return -ENOMEM; 524 } 525 ctx->name = strdup(base_name); 526 if (ctx->name == NULL) { 527 SPDK_ERRLOG("Error creating mDNS discovery ctx name for service: %s\n", svcname); 528 free_mdns_discovery_ctx(ctx); 529 avahi_service_browser_free(sb); 530 return -ENOMEM; 531 } 532 memcpy(&ctx->drv_opts, drv_opts, sizeof(*drv_opts)); 533 memcpy(&ctx->bdev_opts, bdev_opts, sizeof(*bdev_opts)); 534 ctx->sb = sb; 535 ctx->calling_thread = spdk_get_thread(); 536 TAILQ_INIT(&ctx->mdns_discovery_entry_ctxs); 537 /* Even if user did not specify hostnqn, we can still strdup("\0"); */ 538 ctx->hostnqn = strdup(ctx->drv_opts.hostnqn); 539 if (ctx->hostnqn == NULL) { 540 SPDK_ERRLOG("Error creating mDNS discovery ctx hostnqn for service: %s\n", svcname); 541 free_mdns_discovery_ctx(ctx); 542 return -ENOMEM; 543 } 544 /* Start the poller for the Avahi client browser in g_bdev_nvme_init_thread */ 545 spdk_thread_send_msg(g_bdev_nvme_init_thread, start_mdns_discovery_poller, ctx); 546 return 0; 547 } 548 549 static void 550 mdns_stop_discovery_entry(struct mdns_discovery_ctx *ctx) 551 { 552 struct mdns_discovery_entry_ctx *entry_ctx = NULL; 553 554 assert(ctx); 555 556 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 557 bdev_nvme_stop_discovery(entry_ctx->name, NULL, NULL); 558 } 559 } 560 561 int 562 bdev_nvme_stop_mdns_discovery(const char *name) 563 { 564 struct mdns_discovery_ctx *ctx; 565 566 assert(name); 567 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 568 if (strcmp(name, ctx->name) == 0) { 569 if (ctx->stop) { 570 return -EALREADY; 571 } 572 /* set stop to true to stop the mdns poller instance */ 573 ctx->stop = true; 574 mdns_stop_discovery_entry(ctx); 575 return 0; 576 } 577 } 578 579 return -ENOENT; 580 } 581 582 void 583 bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) 584 { 585 struct mdns_discovery_ctx *ctx; 586 struct mdns_discovery_entry_ctx *entry_ctx; 587 struct spdk_json_write_ctx *w; 588 589 w = spdk_jsonrpc_begin_result(request); 590 spdk_json_write_array_begin(w); 591 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 592 spdk_json_write_object_begin(w); 593 spdk_json_write_named_string(w, "name", ctx->name); 594 spdk_json_write_named_string(w, "svcname", ctx->svcname); 595 596 spdk_json_write_named_array_begin(w, "referrals"); 597 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 598 spdk_json_write_object_begin(w); 599 spdk_json_write_named_string(w, "name", entry_ctx->name); 600 spdk_json_write_named_object_begin(w, "trid"); 601 nvme_bdev_dump_trid_json(&entry_ctx->trid, w); 602 spdk_json_write_object_end(w); 603 spdk_json_write_object_end(w); 604 } 605 spdk_json_write_array_end(w); 606 607 spdk_json_write_object_end(w); 608 } 609 spdk_json_write_array_end(w); 610 spdk_jsonrpc_end_result(request, w); 611 } 612 613 void 614 bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) 615 { 616 struct mdns_discovery_ctx *ctx; 617 618 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 619 spdk_json_write_object_begin(w); 620 621 spdk_json_write_named_string(w, "method", "bdev_nvme_start_mdns_discovery"); 622 623 spdk_json_write_named_object_begin(w, "params"); 624 spdk_json_write_named_string(w, "name", ctx->name); 625 spdk_json_write_named_string(w, "svcname", ctx->svcname); 626 spdk_json_write_named_string(w, "hostnqn", ctx->hostnqn); 627 spdk_json_write_object_end(w); 628 629 spdk_json_write_object_end(w); 630 } 631 } 632 633 #else /* SPDK_CONFIG_AVAHI */ 634 635 int 636 bdev_nvme_start_mdns_discovery(const char *base_name, 637 const char *svcname, 638 struct spdk_nvme_ctrlr_opts *drv_opts, 639 struct spdk_bdev_nvme_ctrlr_opts *bdev_opts) 640 { 641 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 642 return -ENOTSUP; 643 } 644 645 int 646 bdev_nvme_stop_mdns_discovery(const char *name) 647 { 648 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 649 return -ENOTSUP; 650 } 651 652 void 653 bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) 654 { 655 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 656 spdk_jsonrpc_send_error_response(request, -ENOTSUP, spdk_strerror(ENOTSUP)); 657 } 658 659 void 660 bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) 661 { 662 /* Empty function to be invoked, when SPDK is built without --with-avahi */ 663 } 664 665 #endif 666