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