Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2022 Dell Inc, or its subsidiaries.
3 : : * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
4 : : * All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : : #include "spdk/env.h"
9 : : #include "spdk/thread.h"
10 : : #include "nvmf_internal.h"
11 : : #include "spdk/log.h"
12 : : #include "spdk/config.h"
13 : : #include "spdk/nvme.h"
14 : : #include "spdk/string.h"
15 : :
16 : : #ifdef SPDK_CONFIG_AVAHI
17 : : #include <avahi-client/client.h>
18 : : #include <avahi-client/publish.h>
19 : : #include <avahi-client/lookup.h>
20 : : #include <avahi-common/simple-watch.h>
21 : : #include <avahi-common/malloc.h>
22 : : #include <avahi-common/error.h>
23 : :
24 : : #define NVMF_MAX_DNS_NAME_LENGTH 255
25 : :
26 : : static AvahiSimplePoll *g_avahi_publish_simple_poll = NULL;
27 : : static AvahiClient *g_avahi_publish_client = NULL;
28 : : static AvahiEntryGroup *g_avahi_entry_group = NULL;
29 : :
30 : : struct mdns_publish_ctx {
31 : : struct spdk_poller *poller;
32 : : struct spdk_nvmf_subsystem *subsystem;
33 : : struct spdk_nvmf_tgt *tgt;
34 : : };
35 : :
36 : : static struct mdns_publish_ctx *g_mdns_publish_ctx = NULL;
37 : :
38 : : static void
39 : 1 : nvmf_avahi_publish_destroy(struct mdns_publish_ctx *ctx)
40 : : {
41 [ + - ]: 1 : if (g_avahi_entry_group) {
42 : 1 : avahi_entry_group_free(g_avahi_entry_group);
43 : 1 : g_avahi_entry_group = NULL;
44 : : }
45 : :
46 [ + - ]: 1 : if (g_avahi_publish_client) {
47 : 1 : avahi_client_free(g_avahi_publish_client);
48 : 1 : g_avahi_publish_client = NULL;
49 : : }
50 : :
51 [ + - ]: 1 : if (g_avahi_publish_simple_poll) {
52 : 1 : avahi_simple_poll_free(g_avahi_publish_simple_poll);
53 : 1 : g_avahi_publish_simple_poll = NULL;
54 : : }
55 : :
56 : 1 : g_mdns_publish_ctx = NULL;
57 : 1 : free(ctx);
58 : 1 : }
59 : :
60 : : static int
61 : 157 : nvmf_avahi_publish_iterate(void *arg)
62 : : {
63 : 157 : struct mdns_publish_ctx *ctx = arg;
64 : : int rc;
65 : :
66 [ - + ]: 157 : if (ctx == NULL) {
67 : 0 : assert(false);
68 : : return SPDK_POLLER_IDLE;
69 : : }
70 : :
71 : 157 : rc = avahi_simple_poll_iterate(g_avahi_publish_simple_poll, 0);
72 [ - + - - ]: 157 : if (rc && rc != -EAGAIN) {
73 : 0 : SPDK_ERRLOG("avahi publish poll returned error\n");
74 : 0 : spdk_poller_unregister(&ctx->poller);
75 : 0 : nvmf_avahi_publish_destroy(ctx);
76 : 0 : return SPDK_POLLER_BUSY;
77 : : }
78 : :
79 : 157 : return SPDK_POLLER_BUSY;
80 : : }
81 : :
82 : : static void
83 : 1 : nvmf_ctx_stop_mdns_prr(struct mdns_publish_ctx *ctx)
84 : : {
85 [ - + - + ]: 1 : SPDK_INFOLOG(nvmf, "Stopping avahi publish poller\n");
86 : 1 : spdk_poller_unregister(&ctx->poller);
87 : 1 : nvmf_avahi_publish_destroy(ctx);
88 : 1 : }
89 : :
90 : : void
91 : 1 : nvmf_tgt_stop_mdns_prr(struct spdk_nvmf_tgt *tgt)
92 : : {
93 [ + - + - ]: 1 : if (g_mdns_publish_ctx && g_mdns_publish_ctx->tgt == tgt) {
94 : 1 : nvmf_ctx_stop_mdns_prr(g_mdns_publish_ctx);
95 : 1 : return;
96 : : }
97 : :
98 : 0 : SPDK_ERRLOG("Failed to stop mDNS PRR. It is not running on target %s.\n", tgt->name);
99 : : }
100 : :
101 : : static int
102 : 1 : publish_pull_registration_request(AvahiClient *client, struct mdns_publish_ctx *publish_ctx)
103 : : {
104 : 1 : struct spdk_nvmf_subsystem *subsystem = publish_ctx->subsystem;
105 : : struct spdk_nvmf_subsystem_listener *listener;
106 : 1 : const char *name_base = "spdk";
107 : 1 : const char *type_base = "_nvme-disc";
108 : 1 : const char *domain = "local";
109 : : char *protocol;
110 : : char name[NVMF_MAX_DNS_NAME_LENGTH];
111 : : char type[NVMF_MAX_DNS_NAME_LENGTH];
112 : : char txt_protocol[NVMF_MAX_DNS_NAME_LENGTH];
113 : : char txt_nqn[NVMF_MAX_DNS_NAME_LENGTH];
114 : 1 : AvahiStringList *txt = NULL;
115 : : uint16_t port;
116 : 1 : uint16_t id = 0;
117 : :
118 [ - + ]: 1 : if (g_avahi_entry_group != NULL) {
119 : 0 : return 0;
120 : : }
121 : :
122 : 1 : g_avahi_entry_group = avahi_entry_group_new(client, NULL, NULL);
123 [ - + ]: 1 : if (g_avahi_entry_group == NULL) {
124 : 0 : SPDK_ERRLOG("avahi_entry_group_new failure: %s\n", avahi_strerror(avahi_client_errno(client)));
125 : 0 : return -1;
126 : : }
127 : :
128 [ + + ]: 3 : TAILQ_FOREACH(listener, &subsystem->listeners, link) {
129 [ + - ]: 2 : if (listener->trid->trtype == SPDK_NVME_TRANSPORT_TCP) {
130 : 2 : protocol = "tcp";
131 [ # # ]: 0 : } else if (listener->trid->trtype == SPDK_NVME_TRANSPORT_RDMA) {
132 : 0 : SPDK_ERRLOG("Current SPDK doesn't distinguish RoCE(udp) and iWARP(tcp). Skip adding listener id %d to avahi entry",
133 : : listener->id);
134 : 0 : continue;
135 : : } else {
136 : 0 : SPDK_ERRLOG("mDNS PRR does not support trtype %d", listener->trid->trtype);
137 : 0 : continue;
138 : : }
139 : :
140 [ - + ]: 2 : snprintf(type, sizeof(type), "%s._%s", type_base, protocol);
141 [ - + ]: 2 : snprintf(name, sizeof(name), "%s%d", name_base, id++);
142 [ - + ]: 2 : snprintf(txt_protocol, sizeof(txt_protocol), "p=%s", protocol);
143 [ - + ]: 2 : snprintf(txt_nqn, sizeof(txt_nqn), "nqn=%s", SPDK_NVMF_DISCOVERY_NQN);
144 : 2 : txt = avahi_string_list_add(txt, txt_protocol);
145 : 2 : txt = avahi_string_list_add(txt, txt_nqn);
146 : 2 : port = spdk_strtol(listener->trid->trsvcid, 10);
147 : :
148 [ - + ]: 2 : if (avahi_entry_group_add_service_strlst(g_avahi_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
149 : : 0, name, type, domain, NULL, port, txt) < 0) {
150 : 0 : SPDK_ERRLOG("Failed to add avahi service name: %s, type: %s, domain: %s, port: %d, error: %s\n",
151 : : name, type, domain, port, avahi_strerror(avahi_client_errno(client)));
152 : : }
153 : 2 : avahi_string_list_free(txt);
154 : 2 : txt = NULL;
155 : : }
156 : :
157 : 1 : avahi_entry_group_commit(g_avahi_entry_group);
158 : :
159 : 1 : return 0;
160 : : }
161 : :
162 : : static void
163 : 1 : publish_client_new_callback(AvahiClient *client, AvahiClientState avahi_state,
164 : : AVAHI_GCC_UNUSED void *user_data)
165 : : {
166 : : int rc;
167 : 1 : struct mdns_publish_ctx *publish_ctx = user_data;
168 : :
169 [ + - - - : 1 : switch (avahi_state) {
- - ]
170 : 1 : case AVAHI_CLIENT_S_RUNNING:
171 : 1 : rc = publish_pull_registration_request(client, publish_ctx);
172 [ - + ]: 1 : if (rc) {
173 : 0 : nvmf_ctx_stop_mdns_prr(publish_ctx);
174 : : }
175 : 1 : break;
176 : 0 : case AVAHI_CLIENT_CONNECTING:
177 [ # # # # ]: 0 : SPDK_INFOLOG(nvmf, "Avahi client waiting for avahi-daemon");
178 : 0 : break;
179 : 0 : case AVAHI_CLIENT_S_REGISTERING:
180 [ # # # # ]: 0 : SPDK_INFOLOG(nvmf, "Avahi client registering service");
181 : 0 : break;
182 : 0 : case AVAHI_CLIENT_FAILURE:
183 : 0 : SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(client)));
184 : 0 : nvmf_ctx_stop_mdns_prr(publish_ctx);
185 : 0 : break;
186 : 0 : case AVAHI_CLIENT_S_COLLISION:
187 : 0 : SPDK_ERRLOG("Avahi client name is already used in the mDNS");
188 : 0 : nvmf_ctx_stop_mdns_prr(publish_ctx);
189 : 0 : break;
190 : 0 : default:
191 : 0 : SPDK_ERRLOG("Avahi client is in unsupported state");
192 : 0 : break;
193 : : }
194 : 1 : }
195 : :
196 : : int
197 : 1 : nvmf_publish_mdns_prr(struct spdk_nvmf_tgt *tgt)
198 : : {
199 : : int error;
200 : 1 : struct mdns_publish_ctx *publish_ctx = NULL;
201 : 1 : struct spdk_nvmf_subsystem *subsystem = NULL;
202 : :
203 [ - + ]: 1 : if (g_mdns_publish_ctx != NULL) {
204 [ # # ]: 0 : if (g_mdns_publish_ctx->tgt == tgt) {
205 : 0 : SPDK_ERRLOG("mDNS server is already running on target %s.\n", tgt->name);
206 : 0 : return -EEXIST;
207 : : }
208 : 0 : SPDK_ERRLOG("mDNS server does not support publishing multiple targets simultaneously.");
209 : 0 : return -EINVAL;
210 : : }
211 : :
212 : 1 : subsystem = spdk_nvmf_tgt_find_subsystem(tgt, SPDK_NVMF_DISCOVERY_NQN);
213 [ - + ]: 1 : if (TAILQ_EMPTY(&subsystem->listeners)) {
214 : 0 : SPDK_ERRLOG("Discovery subsystem has no listeners.\n");
215 : 0 : return -EINVAL;
216 : : }
217 : :
218 : 1 : publish_ctx = calloc(1, sizeof(*publish_ctx));
219 [ - + ]: 1 : if (publish_ctx == NULL) {
220 : 0 : SPDK_ERRLOG("Error creating mDNS publish ctx\n");
221 : 0 : return -ENOMEM;
222 : : }
223 : 1 : publish_ctx->subsystem = subsystem;
224 : 1 : publish_ctx->tgt = tgt;
225 : : /* Allocate main loop object */
226 : 1 : g_avahi_publish_simple_poll = avahi_simple_poll_new();
227 [ - + ]: 1 : if (g_avahi_publish_simple_poll == NULL) {
228 : 0 : SPDK_ERRLOG("Failed to create poll object for mDNS publish.\n");
229 : 0 : nvmf_avahi_publish_destroy(publish_ctx);
230 : 0 : return -ENOMEM;
231 : : }
232 : :
233 [ - + ]: 1 : assert(g_avahi_publish_client == NULL);
234 : :
235 : : /* Allocate a new client */
236 : 1 : g_avahi_publish_client = avahi_client_new(avahi_simple_poll_get(g_avahi_publish_simple_poll),
237 : : 0, publish_client_new_callback, publish_ctx, &error);
238 : : /* Check whether creating the client object succeeded */
239 [ - + ]: 1 : if (g_avahi_publish_client == NULL) {
240 : 0 : SPDK_ERRLOG("Failed to create mDNS client Error: %s\n", avahi_strerror(error));
241 : 0 : nvmf_avahi_publish_destroy(publish_ctx);
242 : 0 : return -ENOMEM;
243 : : }
244 : :
245 : 1 : g_mdns_publish_ctx = publish_ctx;
246 : 1 : publish_ctx->poller = SPDK_POLLER_REGISTER(nvmf_avahi_publish_iterate, publish_ctx, 100 * 1000);
247 : 1 : return 0;
248 : : }
249 : :
250 : : #endif
|