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