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