xref: /spdk/module/bdev/nvme/bdev_mdns_client.c (revision 60982c759db49b4f4579f16e3b24df0725ba4b94)
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