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 avahi_free(t); 280 281 ctx = get_mdns_discovery_ctx_by_svcname(type); 282 if (!ctx) { 283 SPDK_ERRLOG("Unknown Service '%s'\n", type); 284 break; 285 } 286 287 trid = (struct spdk_nvme_transport_id *) calloc(1, sizeof(struct spdk_nvme_transport_id)); 288 if (!trid) { 289 SPDK_ERRLOG(" Error allocating memory for trid\n"); 290 break; 291 } 292 trid->adrfam = get_spdk_nvme_adrfam_from_avahi_addr(address); 293 if (trid->adrfam != SPDK_NVMF_ADRFAM_IPV4) { 294 /* TODO: For now process only ipv4 addresses */ 295 SPDK_INFOLOG(bdev_nvme, "trid family is not IPV4 %d\n", trid->adrfam); 296 free(trid); 297 break; 298 } 299 subnqn = get_key_val_avahi_resolve_txt(txt, "NQN"); 300 if (!subnqn) { 301 free(trid); 302 SPDK_ERRLOG("subnqn received is empty for service %s\n", ctx->svcname); 303 break; 304 } 305 proto = get_key_val_avahi_resolve_txt(txt, "p"); 306 if (!proto) { 307 free(trid); 308 avahi_free(subnqn); 309 SPDK_ERRLOG("Protocol not received for service %s\n", ctx->svcname); 310 break; 311 } 312 status = get_spdk_nvme_transport_from_proto_str(proto, &trid->trtype); 313 if (status) { 314 free(trid); 315 avahi_free(subnqn); 316 avahi_free(proto); 317 SPDK_ERRLOG("Unable to derive nvme transport type for service %s\n", ctx->svcname); 318 break; 319 } 320 snprintf(trid->traddr, sizeof(trid->traddr), "%s", ipaddr); 321 snprintf(trid->trsvcid, sizeof(trid->trsvcid), "%s", port_str); 322 snprintf(trid->subnqn, sizeof(trid->subnqn), "%s", subnqn); 323 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 324 if (!spdk_nvme_transport_id_compare(trid, &entry_ctx->trid)) { 325 SPDK_ERRLOG("mDNS discovery entry exists already. trid->traddr: %s trid->trsvcid: %s\n", 326 trid->traddr, trid->trsvcid); 327 free(trid); 328 avahi_free(subnqn); 329 avahi_free(proto); 330 avahi_service_resolver_free(r); 331 return; 332 } 333 } 334 entry_ctx = create_mdns_discovery_entry_ctx(ctx, trid); 335 TAILQ_INSERT_TAIL(&ctx->mdns_discovery_entry_ctxs, entry_ctx, tailq); 336 spdk_thread_send_msg(ctx->calling_thread, mdns_bdev_nvme_start_discovery, entry_ctx); 337 free(trid); 338 avahi_free(subnqn); 339 avahi_free(proto); 340 break; 341 } 342 default: 343 SPDK_ERRLOG("Unknown Avahi resolver event: %d", event); 344 } 345 avahi_service_resolver_free(r); 346 } 347 348 static void 349 mdns_browse_callback( 350 AvahiServiceBrowser *b, 351 AvahiIfIndex interface, 352 AvahiProtocol protocol, 353 AvahiBrowserEvent event, 354 const char *name, 355 const char *type, 356 const char *domain, 357 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 358 void *userdata) 359 { 360 AvahiClient *c = userdata; 361 362 assert(b); 363 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ 364 switch (event) { 365 case AVAHI_BROWSER_FAILURE: 366 SPDK_ERRLOG("(Browser) Failure: %s\n", 367 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); 368 return; 369 case AVAHI_BROWSER_NEW: 370 SPDK_DEBUGLOG(bdev_nvme, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, 371 domain); 372 /* We ignore the returned resolver object. In the callback 373 function we free it. If the server is terminated before 374 the callback function is called the server will free 375 the resolver for us. */ 376 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, 377 mdns_resolve_callback, c))) { 378 SPDK_ERRLOG("Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); 379 } 380 break; 381 case AVAHI_BROWSER_REMOVE: 382 SPDK_ERRLOG("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); 383 /* On remove, we are not doing the automatic cleanup of connections 384 * to the targets that were learnt from the CDC, for which remove event has 385 * been received. If required, user can clear the connections manually by 386 * invoking bdev_nvme_stop_discovery. We can implement the automatic cleanup 387 * later, if there is a requirement in the future. 388 */ 389 break; 390 case AVAHI_BROWSER_ALL_FOR_NOW: 391 case AVAHI_BROWSER_CACHE_EXHAUSTED: 392 SPDK_INFOLOG(bdev_nvme, "(Browser) %s\n", 393 event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); 394 break; 395 default: 396 SPDK_ERRLOG("Unknown Avahi browser event: %d", event); 397 } 398 } 399 400 static void 401 client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) 402 { 403 assert(c); 404 /* Called whenever the client or server state changes */ 405 if (state == AVAHI_CLIENT_FAILURE) { 406 SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); 407 } 408 } 409 410 static int 411 bdev_nvme_avahi_iterate(void *arg) 412 { 413 struct mdns_discovery_ctx *ctx = arg; 414 int rc; 415 416 if (ctx->stop) { 417 SPDK_INFOLOG(bdev_nvme, "Stopping avahi poller for service %s\n", ctx->svcname); 418 spdk_poller_unregister(&ctx->poller); 419 TAILQ_REMOVE(&g_mdns_discovery_ctxs, ctx, tailq); 420 free_mdns_discovery_ctx(ctx); 421 return SPDK_POLLER_IDLE; 422 } 423 424 if (g_avahi_simple_poll == NULL) { 425 spdk_poller_unregister(&ctx->poller); 426 return SPDK_POLLER_IDLE; 427 } 428 429 rc = avahi_simple_poll_iterate(g_avahi_simple_poll, 0); 430 if (rc && rc != -EAGAIN) { 431 SPDK_ERRLOG("avahi poll returned error for service: %s/n", ctx->svcname); 432 return SPDK_POLLER_IDLE; 433 } 434 435 return SPDK_POLLER_BUSY; 436 } 437 438 static void 439 start_mdns_discovery_poller(void *arg) 440 { 441 struct mdns_discovery_ctx *ctx = arg; 442 443 assert(arg); 444 TAILQ_INSERT_TAIL(&g_mdns_discovery_ctxs, ctx, tailq); 445 ctx->poller = SPDK_POLLER_REGISTER(bdev_nvme_avahi_iterate, ctx, 100 * 1000); 446 } 447 448 int 449 bdev_nvme_start_mdns_discovery(const char *base_name, 450 const char *svcname, 451 struct spdk_nvme_ctrlr_opts *drv_opts, 452 struct nvme_ctrlr_opts *bdev_opts) 453 { 454 AvahiServiceBrowser *sb = NULL; 455 int error; 456 struct mdns_discovery_ctx *ctx; 457 458 assert(base_name); 459 assert(svcname); 460 461 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 462 if (strcmp(ctx->name, base_name) == 0) { 463 SPDK_ERRLOG("mDNS discovery already running with name %s\n", base_name); 464 return -EEXIST; 465 } 466 467 if (strcmp(ctx->svcname, svcname) == 0) { 468 SPDK_ERRLOG("mDNS discovery already running for service %s\n", svcname); 469 return -EEXIST; 470 } 471 } 472 473 if (g_avahi_simple_poll == NULL) { 474 475 /* Allocate main loop object */ 476 if (!(g_avahi_simple_poll = avahi_simple_poll_new())) { 477 SPDK_ERRLOG("Failed to create poll object for mDNS discovery for service: %s.\n", svcname); 478 return -ENOMEM; 479 } 480 } 481 482 if (g_avahi_client == NULL) { 483 484 /* Allocate a new client */ 485 g_avahi_client = avahi_client_new(avahi_simple_poll_get(g_avahi_simple_poll), 0, client_callback, 486 NULL, &error); 487 /* Check whether creating the client object succeeded */ 488 if (!g_avahi_client) { 489 SPDK_ERRLOG("Failed to create mDNS client for service:%s Error: %s\n", svcname, 490 avahi_strerror(error)); 491 return -ENOMEM; 492 } 493 } 494 495 /* Create the service browser */ 496 if (!(sb = avahi_service_browser_new(g_avahi_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, svcname, 497 NULL, 0, mdns_browse_callback, g_avahi_client))) { 498 SPDK_ERRLOG("Failed to create service browser for service: %s Error: %s\n", svcname, 499 avahi_strerror(avahi_client_errno(g_avahi_client))); 500 return -ENOMEM; 501 } 502 503 ctx = calloc(1, sizeof(*ctx)); 504 if (ctx == NULL) { 505 SPDK_ERRLOG("Error creating mDNS discovery ctx for service: %s\n", svcname); 506 avahi_service_browser_free(sb); 507 return -ENOMEM; 508 } 509 510 ctx->svcname = strdup(svcname); 511 if (ctx->svcname == NULL) { 512 SPDK_ERRLOG("Error creating mDNS discovery ctx svcname for service: %s\n", svcname); 513 free_mdns_discovery_ctx(ctx); 514 avahi_service_browser_free(sb); 515 return -ENOMEM; 516 } 517 ctx->name = strdup(base_name); 518 if (ctx->name == NULL) { 519 SPDK_ERRLOG("Error creating mDNS discovery ctx name for service: %s\n", svcname); 520 free_mdns_discovery_ctx(ctx); 521 avahi_service_browser_free(sb); 522 return -ENOMEM; 523 } 524 memcpy(&ctx->drv_opts, drv_opts, sizeof(*drv_opts)); 525 memcpy(&ctx->bdev_opts, bdev_opts, sizeof(*bdev_opts)); 526 ctx->sb = sb; 527 ctx->calling_thread = spdk_get_thread(); 528 TAILQ_INIT(&ctx->mdns_discovery_entry_ctxs); 529 /* Even if user did not specify hostnqn, we can still strdup("\0"); */ 530 ctx->hostnqn = strdup(ctx->drv_opts.hostnqn); 531 if (ctx->hostnqn == NULL) { 532 SPDK_ERRLOG("Error creating mDNS discovery ctx hostnqn for service: %s\n", svcname); 533 free_mdns_discovery_ctx(ctx); 534 return -ENOMEM; 535 } 536 /* Start the poller for the Avahi client browser in g_bdev_nvme_init_thread */ 537 spdk_thread_send_msg(g_bdev_nvme_init_thread, start_mdns_discovery_poller, ctx); 538 return 0; 539 } 540 541 static void 542 mdns_stop_discovery_entry(struct mdns_discovery_ctx *ctx) 543 { 544 struct mdns_discovery_entry_ctx *entry_ctx = NULL; 545 546 assert(ctx); 547 548 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 549 bdev_nvme_stop_discovery(entry_ctx->name, NULL, NULL); 550 } 551 } 552 553 int 554 bdev_nvme_stop_mdns_discovery(const char *name) 555 { 556 struct mdns_discovery_ctx *ctx; 557 558 assert(name); 559 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 560 if (strcmp(name, ctx->name) == 0) { 561 if (ctx->stop) { 562 return -EALREADY; 563 } 564 /* set stop to true to stop the mdns poller instance */ 565 ctx->stop = true; 566 mdns_stop_discovery_entry(ctx); 567 return 0; 568 } 569 } 570 571 return -ENOENT; 572 } 573 574 void 575 bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) 576 { 577 struct mdns_discovery_ctx *ctx; 578 struct mdns_discovery_entry_ctx *entry_ctx; 579 struct spdk_json_write_ctx *w; 580 581 w = spdk_jsonrpc_begin_result(request); 582 spdk_json_write_array_begin(w); 583 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 584 spdk_json_write_object_begin(w); 585 spdk_json_write_named_string(w, "name", ctx->name); 586 spdk_json_write_named_string(w, "svcname", ctx->svcname); 587 588 spdk_json_write_named_array_begin(w, "referrals"); 589 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 590 spdk_json_write_object_begin(w); 591 spdk_json_write_named_string(w, "name", entry_ctx->name); 592 spdk_json_write_named_object_begin(w, "trid"); 593 nvme_bdev_dump_trid_json(&entry_ctx->trid, w); 594 spdk_json_write_object_end(w); 595 spdk_json_write_object_end(w); 596 } 597 spdk_json_write_array_end(w); 598 599 spdk_json_write_object_end(w); 600 } 601 spdk_json_write_array_end(w); 602 spdk_jsonrpc_end_result(request, w); 603 } 604 605 void 606 bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) 607 { 608 struct mdns_discovery_ctx *ctx; 609 610 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 611 spdk_json_write_object_begin(w); 612 613 spdk_json_write_named_string(w, "method", "bdev_nvme_start_mdns_discovery"); 614 615 spdk_json_write_named_object_begin(w, "params"); 616 spdk_json_write_named_string(w, "name", ctx->name); 617 spdk_json_write_named_string(w, "svcname", ctx->svcname); 618 spdk_json_write_named_string(w, "hostnqn", ctx->hostnqn); 619 spdk_json_write_object_end(w); 620 621 spdk_json_write_object_end(w); 622 } 623 } 624 625 #else /* SPDK_CONFIG_AVAHI */ 626 627 int 628 bdev_nvme_start_mdns_discovery(const char *base_name, 629 const char *svcname, 630 struct spdk_nvme_ctrlr_opts *drv_opts, 631 struct nvme_ctrlr_opts *bdev_opts) 632 { 633 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 634 return -ENOTSUP; 635 } 636 637 int 638 bdev_nvme_stop_mdns_discovery(const char *name) 639 { 640 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 641 return -ENOTSUP; 642 } 643 644 void 645 bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) 646 { 647 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 648 spdk_jsonrpc_send_error_response(request, -ENOTSUP, spdk_strerror(ENOTSUP)); 649 } 650 651 void 652 bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) 653 { 654 /* Empty function to be invoked, when SPDK is built without --with-avahi */ 655 } 656 657 #endif 658