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 : 178 : nvmf_avahi_publish_iterate(void *arg)
62 : : {
63 : 178 : struct mdns_publish_ctx *ctx = arg;
64 : : int rc;
65 : :
66 [ - + ]: 178 : if (ctx == NULL) {
67 : 0 : assert(false);
68 : : return SPDK_POLLER_IDLE;
69 : : }
70 : :
71 : 178 : rc = avahi_simple_poll_iterate(g_avahi_publish_simple_poll, 0);
72 [ - + - - ]: 178 : 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 : 178 : 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 : : static bool
91 : 121 : nvmf_tgt_is_mdns_running(struct spdk_nvmf_tgt *tgt)
92 : : {
93 [ + + + - ]: 121 : if (g_mdns_publish_ctx && g_mdns_publish_ctx->tgt == tgt) {
94 : 3 : return true;
95 : : }
96 : 118 : return false;
97 : : }
98 : :
99 : : void
100 : 93 : nvmf_tgt_stop_mdns_prr(struct spdk_nvmf_tgt *tgt)
101 : : {
102 [ + + ]: 93 : if (nvmf_tgt_is_mdns_running(tgt) == true) {
103 : 1 : nvmf_ctx_stop_mdns_prr(g_mdns_publish_ctx);
104 : 1 : return;
105 : : }
106 : : }
107 : :
108 : : static void
109 : 3 : avahi_entry_group_add_listeners(AvahiEntryGroup *avahi_entry_group,
110 : : struct spdk_nvmf_subsystem *subsystem)
111 : : {
112 : : struct spdk_nvmf_subsystem_listener *listener;
113 : 3 : const char *name_base = "spdk";
114 : 3 : const char *type_base = "_nvme-disc";
115 : 3 : const char *domain = "local";
116 : : char *protocol;
117 : : char name[NVMF_MAX_DNS_NAME_LENGTH];
118 : : char type[NVMF_MAX_DNS_NAME_LENGTH];
119 : : char txt_protocol[NVMF_MAX_DNS_NAME_LENGTH];
120 : : char txt_nqn[NVMF_MAX_DNS_NAME_LENGTH];
121 : 3 : AvahiStringList *txt = NULL;
122 : : uint16_t port;
123 : 3 : uint16_t id = 0;
124 : :
125 [ + + ]: 7 : TAILQ_FOREACH(listener, &subsystem->listeners, link) {
126 [ + - ]: 4 : if (listener->trid->trtype == SPDK_NVME_TRANSPORT_TCP) {
127 : 4 : protocol = "tcp";
128 [ # # ]: 0 : } else if (listener->trid->trtype == SPDK_NVME_TRANSPORT_RDMA) {
129 : 0 : SPDK_ERRLOG("Current SPDK doesn't distinguish RoCE(udp) and iWARP(tcp). Skip adding listener id %d to avahi entry",
130 : : listener->id);
131 : 0 : continue;
132 : : } else {
133 : 0 : SPDK_ERRLOG("mDNS PRR does not support trtype %d", listener->trid->trtype);
134 : 0 : continue;
135 : : }
136 : :
137 [ - + ]: 4 : snprintf(type, sizeof(type), "%s._%s", type_base, protocol);
138 [ - + ]: 4 : snprintf(name, sizeof(name), "%s%d", name_base, id++);
139 [ - + ]: 4 : snprintf(txt_protocol, sizeof(txt_protocol), "p=%s", protocol);
140 [ - + ]: 4 : snprintf(txt_nqn, sizeof(txt_nqn), "nqn=%s", SPDK_NVMF_DISCOVERY_NQN);
141 : 4 : txt = avahi_string_list_add(txt, txt_protocol);
142 : 4 : txt = avahi_string_list_add(txt, txt_nqn);
143 : 4 : port = spdk_strtol(listener->trid->trsvcid, 10);
144 : :
145 [ - + ]: 4 : if (avahi_entry_group_add_service_strlst(avahi_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
146 : : 0, name, type, domain, NULL, port, txt) < 0) {
147 : 0 : SPDK_ERRLOG("Failed to add avahi service name: %s, type: %s, domain: %s, port: %d",
148 : : name, type, domain, port);
149 : : }
150 : 4 : avahi_string_list_free(txt);
151 : 4 : txt = NULL;
152 : : }
153 : :
154 : 3 : avahi_entry_group_commit(avahi_entry_group);
155 : 3 : }
156 : :
157 : : int
158 : 28 : nvmf_tgt_update_mdns_prr(struct spdk_nvmf_tgt *tgt)
159 : : {
160 : : int rc;
161 : :
162 [ + + - + ]: 28 : if (nvmf_tgt_is_mdns_running(tgt) == false || g_avahi_entry_group == NULL) {
163 [ - + - + ]: 26 : SPDK_INFOLOG(nvmf,
164 : : "nvmf_tgt_update_mdns_prr is only supported when mDNS servier is running on target\n");
165 : 26 : return 0;
166 : : }
167 : :
168 : 2 : rc = avahi_entry_group_reset(g_avahi_entry_group);
169 [ - + ]: 2 : if (rc) {
170 : 0 : SPDK_ERRLOG("Failed to reset avahi_entry_group");
171 : 0 : return -EINVAL;
172 : : }
173 : :
174 : 2 : avahi_entry_group_add_listeners(g_avahi_entry_group, g_mdns_publish_ctx->subsystem);
175 : :
176 : 2 : return 0;
177 : : }
178 : :
179 : : static int
180 : 1 : publish_pull_registration_request(AvahiClient *client, struct mdns_publish_ctx *publish_ctx)
181 : : {
182 : 1 : struct spdk_nvmf_subsystem *subsystem = publish_ctx->subsystem;
183 : :
184 [ - + ]: 1 : if (g_avahi_entry_group != NULL) {
185 : 0 : return 0;
186 : : }
187 : :
188 : 1 : g_avahi_entry_group = avahi_entry_group_new(client, NULL, NULL);
189 [ - + ]: 1 : if (g_avahi_entry_group == NULL) {
190 : 0 : SPDK_ERRLOG("avahi_entry_group_new failure: %s\n", avahi_strerror(avahi_client_errno(client)));
191 : 0 : return -1;
192 : : }
193 : :
194 : 1 : avahi_entry_group_add_listeners(g_avahi_entry_group, subsystem);
195 : :
196 : 1 : return 0;
197 : : }
198 : :
199 : : static void
200 : 1 : publish_client_new_callback(AvahiClient *client, AvahiClientState avahi_state,
201 : : AVAHI_GCC_UNUSED void *user_data)
202 : : {
203 : : int rc;
204 : 1 : struct mdns_publish_ctx *publish_ctx = user_data;
205 : :
206 [ + - - - : 1 : switch (avahi_state) {
- - ]
207 : 1 : case AVAHI_CLIENT_S_RUNNING:
208 : 1 : rc = publish_pull_registration_request(client, publish_ctx);
209 [ - + ]: 1 : if (rc) {
210 : 0 : nvmf_ctx_stop_mdns_prr(publish_ctx);
211 : : }
212 : 1 : break;
213 : 0 : case AVAHI_CLIENT_CONNECTING:
214 [ # # # # ]: 0 : SPDK_INFOLOG(nvmf, "Avahi client waiting for avahi-daemon");
215 : 0 : break;
216 : 0 : case AVAHI_CLIENT_S_REGISTERING:
217 [ # # # # ]: 0 : SPDK_INFOLOG(nvmf, "Avahi client registering service");
218 : 0 : break;
219 : 0 : case AVAHI_CLIENT_FAILURE:
220 : 0 : SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(client)));
221 : 0 : nvmf_ctx_stop_mdns_prr(publish_ctx);
222 : 0 : break;
223 : 0 : case AVAHI_CLIENT_S_COLLISION:
224 : 0 : SPDK_ERRLOG("Avahi client name is already used in the mDNS");
225 : 0 : nvmf_ctx_stop_mdns_prr(publish_ctx);
226 : 0 : break;
227 : 0 : default:
228 : 0 : SPDK_ERRLOG("Avahi client is in unsupported state");
229 : 0 : break;
230 : : }
231 : 1 : }
232 : :
233 : : int
234 : 1 : nvmf_publish_mdns_prr(struct spdk_nvmf_tgt *tgt)
235 : : {
236 : : int error;
237 : 1 : struct mdns_publish_ctx *publish_ctx = NULL;
238 : 1 : struct spdk_nvmf_subsystem *subsystem = NULL;
239 : :
240 [ - + ]: 1 : if (g_mdns_publish_ctx != NULL) {
241 [ # # ]: 0 : if (g_mdns_publish_ctx->tgt == tgt) {
242 : 0 : SPDK_ERRLOG("mDNS server is already running on target %s.\n", tgt->name);
243 : 0 : return -EEXIST;
244 : : }
245 : 0 : SPDK_ERRLOG("mDNS server does not support publishing multiple targets simultaneously.");
246 : 0 : return -EINVAL;
247 : : }
248 : :
249 : 1 : subsystem = spdk_nvmf_tgt_find_subsystem(tgt, SPDK_NVMF_DISCOVERY_NQN);
250 [ - + ]: 1 : if (TAILQ_EMPTY(&subsystem->listeners)) {
251 : 0 : SPDK_ERRLOG("Discovery subsystem has no listeners.\n");
252 : 0 : return -EINVAL;
253 : : }
254 : :
255 : 1 : publish_ctx = calloc(1, sizeof(*publish_ctx));
256 [ - + ]: 1 : if (publish_ctx == NULL) {
257 : 0 : SPDK_ERRLOG("Error creating mDNS publish ctx\n");
258 : 0 : return -ENOMEM;
259 : : }
260 : 1 : publish_ctx->subsystem = subsystem;
261 : 1 : publish_ctx->tgt = tgt;
262 : : /* Allocate main loop object */
263 : 1 : g_avahi_publish_simple_poll = avahi_simple_poll_new();
264 [ - + ]: 1 : if (g_avahi_publish_simple_poll == NULL) {
265 : 0 : SPDK_ERRLOG("Failed to create poll object for mDNS publish.\n");
266 : 0 : nvmf_avahi_publish_destroy(publish_ctx);
267 : 0 : return -ENOMEM;
268 : : }
269 : :
270 [ - + ]: 1 : assert(g_avahi_publish_client == NULL);
271 : :
272 : : /* Allocate a new client */
273 : 1 : g_avahi_publish_client = avahi_client_new(avahi_simple_poll_get(g_avahi_publish_simple_poll),
274 : : 0, publish_client_new_callback, publish_ctx, &error);
275 : : /* Check whether creating the client object succeeded */
276 [ - + ]: 1 : if (g_avahi_publish_client == NULL) {
277 : 0 : SPDK_ERRLOG("Failed to create mDNS client Error: %s\n", avahi_strerror(error));
278 : 0 : nvmf_avahi_publish_destroy(publish_ctx);
279 : 0 : return -ENOMEM;
280 : : }
281 : :
282 : 1 : g_mdns_publish_ctx = publish_ctx;
283 : 1 : publish_ctx->poller = SPDK_POLLER_REGISTER(nvmf_avahi_publish_iterate, publish_ctx, 100 * 1000);
284 : 1 : return 0;
285 : : }
286 : : #endif
|