xref: /spdk/lib/sock/sock.c (revision 800b18d028b95f92738315af634b8101a4c84031)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation. All rights reserved.
5  *   Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk/log.h"
37 #include "spdk/sock.h"
38 #include "spdk_internal/sock.h"
39 #include "spdk/queue.h"
40 
41 #define SPDK_SOCK_DEFAULT_PRIORITY 0
42 #define SPDK_SOCK_OPTS_FIELD_OK(opts, field) (offsetof(struct spdk_sock_opts, field) + sizeof(opts->field) <= (opts->opts_size))
43 
44 static STAILQ_HEAD(, spdk_net_impl) g_net_impls = STAILQ_HEAD_INITIALIZER(g_net_impls);
45 
46 struct spdk_sock_placement_id_entry {
47 	int placement_id;
48 	uint32_t ref;
49 	struct spdk_sock_group *group;
50 	STAILQ_ENTRY(spdk_sock_placement_id_entry) link;
51 };
52 
53 static STAILQ_HEAD(, spdk_sock_placement_id_entry) g_placement_id_map = STAILQ_HEAD_INITIALIZER(
54 			g_placement_id_map);
55 static pthread_mutex_t g_map_table_mutex = PTHREAD_MUTEX_INITIALIZER;
56 
57 /* Insert a group into the placement map.
58  * If the group is already in the map, take a reference.
59  */
60 static int
61 sock_map_insert(int placement_id, struct spdk_sock_group *group)
62 {
63 	struct spdk_sock_placement_id_entry *entry;
64 
65 	pthread_mutex_lock(&g_map_table_mutex);
66 	STAILQ_FOREACH(entry, &g_placement_id_map, link) {
67 		if (placement_id == entry->placement_id) {
68 			/* The mapping already exists, it means that different sockets have
69 			 * the same placement_ids.
70 			 */
71 			entry->ref++;
72 			pthread_mutex_unlock(&g_map_table_mutex);
73 			return 0;
74 		}
75 	}
76 
77 	entry = calloc(1, sizeof(*entry));
78 	if (!entry) {
79 		SPDK_ERRLOG("Cannot allocate an entry for placement_id=%u\n", placement_id);
80 		pthread_mutex_unlock(&g_map_table_mutex);
81 		return -ENOMEM;
82 	}
83 
84 	entry->placement_id = placement_id;
85 	entry->group = group;
86 	entry->ref++;
87 
88 	STAILQ_INSERT_TAIL(&g_placement_id_map, entry, link);
89 	pthread_mutex_unlock(&g_map_table_mutex);
90 
91 	return 0;
92 }
93 
94 /* Release a reference to the group for a given placement_id.
95  * If the reference count is 0, remove the group.
96  */
97 static void
98 sock_map_release(int placement_id)
99 {
100 	struct spdk_sock_placement_id_entry *entry;
101 
102 	pthread_mutex_lock(&g_map_table_mutex);
103 	STAILQ_FOREACH(entry, &g_placement_id_map, link) {
104 		if (placement_id == entry->placement_id) {
105 			assert(entry->ref > 0);
106 			entry->ref--;
107 			break;
108 		}
109 	}
110 
111 	pthread_mutex_unlock(&g_map_table_mutex);
112 }
113 
114 /* Look up the group for a placement_id. */
115 static void
116 sock_map_lookup(int placement_id, struct spdk_sock_group **group)
117 {
118 	struct spdk_sock_placement_id_entry *entry;
119 
120 	*group = NULL;
121 	pthread_mutex_lock(&g_map_table_mutex);
122 	STAILQ_FOREACH(entry, &g_placement_id_map, link) {
123 		if (placement_id == entry->placement_id) {
124 			assert(entry->group != NULL);
125 			*group = entry->group;
126 			break;
127 		}
128 	}
129 	pthread_mutex_unlock(&g_map_table_mutex);
130 }
131 
132 /* Remove the socket group from the map table */
133 static void
134 sock_remove_sock_group_from_map_table(struct spdk_sock_group *group)
135 {
136 	struct spdk_sock_placement_id_entry *entry, *tmp;
137 
138 	pthread_mutex_lock(&g_map_table_mutex);
139 	STAILQ_FOREACH_SAFE(entry, &g_placement_id_map, link, tmp) {
140 		if (entry->group == group) {
141 			STAILQ_REMOVE(&g_placement_id_map, entry, spdk_sock_placement_id_entry, link);
142 			free(entry);
143 		}
144 	}
145 	pthread_mutex_unlock(&g_map_table_mutex);
146 
147 }
148 
149 int
150 spdk_sock_get_optimal_sock_group(struct spdk_sock *sock, struct spdk_sock_group **group)
151 {
152 	int placement_id = 0, rc;
153 
154 	rc = sock->net_impl->get_placement_id(sock, &placement_id);
155 	if (!rc && (placement_id != 0)) {
156 		sock_map_lookup(placement_id, group);
157 		return 0;
158 	} else {
159 		return -1;
160 	}
161 }
162 
163 int
164 spdk_sock_getaddr(struct spdk_sock *sock, char *saddr, int slen, uint16_t *sport,
165 		  char *caddr, int clen, uint16_t *cport)
166 {
167 	return sock->net_impl->getaddr(sock, saddr, slen, sport, caddr, clen, cport);
168 }
169 
170 void
171 spdk_sock_get_default_opts(struct spdk_sock_opts *opts)
172 {
173 	assert(opts);
174 
175 	if (SPDK_SOCK_OPTS_FIELD_OK(opts, priority)) {
176 		opts->priority = SPDK_SOCK_DEFAULT_PRIORITY;
177 	}
178 }
179 
180 /*
181  * opts The opts allocated in the current library.
182  * opts_user The opts passed by the caller.
183  * */
184 static void
185 sock_init_opts(struct spdk_sock_opts *opts, struct spdk_sock_opts *opts_user)
186 {
187 	assert(opts);
188 	assert(opts_user);
189 
190 	opts->opts_size = sizeof(*opts);
191 	spdk_sock_get_default_opts(opts);
192 
193 	/* reset the size according to the user */
194 	opts->opts_size = opts_user->opts_size;
195 	if (SPDK_SOCK_OPTS_FIELD_OK(opts, priority)) {
196 		opts->priority = opts_user->priority;
197 	}
198 }
199 
200 struct spdk_sock *
201 spdk_sock_connect(const char *ip, int port, char *impl_name)
202 {
203 	struct spdk_sock_opts opts;
204 
205 	opts.opts_size = sizeof(opts);
206 	spdk_sock_get_default_opts(&opts);
207 	return spdk_sock_connect_ext(ip, port, impl_name, &opts);
208 }
209 
210 struct spdk_sock *
211 spdk_sock_connect_ext(const char *ip, int port, char *impl_name, struct spdk_sock_opts *opts)
212 {
213 	struct spdk_net_impl *impl = NULL;
214 	struct spdk_sock *sock;
215 	struct spdk_sock_opts opts_local;
216 
217 	if (opts == NULL) {
218 		SPDK_ERRLOG("the opts should not be NULL pointer\n");
219 		return NULL;
220 	}
221 
222 	STAILQ_FOREACH_FROM(impl, &g_net_impls, link) {
223 		if (impl_name && strncmp(impl_name, impl->name, strlen(impl->name) + 1)) {
224 			continue;
225 		}
226 
227 		sock_init_opts(&opts_local, opts);
228 		sock = impl->connect(ip, port, &opts_local);
229 		if (sock != NULL) {
230 			/* Copy the contents, both the two structures are the same ABI version */
231 			memcpy(&sock->opts, &opts_local, sizeof(sock->opts));
232 			sock->net_impl = impl;
233 			TAILQ_INIT(&sock->queued_reqs);
234 			TAILQ_INIT(&sock->pending_reqs);
235 			return sock;
236 		}
237 	}
238 
239 	return NULL;
240 }
241 
242 struct spdk_sock *
243 spdk_sock_listen(const char *ip, int port, char *impl_name)
244 {
245 	struct spdk_sock_opts opts;
246 
247 	opts.opts_size = sizeof(opts);
248 	spdk_sock_get_default_opts(&opts);
249 	return spdk_sock_listen_ext(ip, port, impl_name, &opts);
250 }
251 
252 struct spdk_sock *
253 spdk_sock_listen_ext(const char *ip, int port, char *impl_name, struct spdk_sock_opts *opts)
254 {
255 	struct spdk_net_impl *impl = NULL;
256 	struct spdk_sock *sock;
257 	struct spdk_sock_opts opts_local;
258 
259 	if (opts == NULL) {
260 		SPDK_ERRLOG("the opts should not be NULL pointer\n");
261 		return NULL;
262 	}
263 
264 	STAILQ_FOREACH_FROM(impl, &g_net_impls, link) {
265 		if (impl_name && strncmp(impl_name, impl->name, strlen(impl->name) + 1)) {
266 			continue;
267 		}
268 
269 		sock_init_opts(&opts_local, opts);
270 		sock = impl->listen(ip, port, &opts_local);
271 		if (sock != NULL) {
272 			/* Copy the contents, both the two structures are the same ABI version */
273 			memcpy(&sock->opts, &opts_local, sizeof(sock->opts));
274 			sock->net_impl = impl;
275 			/* Don't need to initialize the request queues for listen
276 			 * sockets. */
277 			return sock;
278 		}
279 	}
280 
281 	return NULL;
282 }
283 
284 struct spdk_sock *
285 spdk_sock_accept(struct spdk_sock *sock)
286 {
287 	struct spdk_sock *new_sock;
288 
289 	new_sock = sock->net_impl->accept(sock);
290 	if (new_sock != NULL) {
291 		/* Inherit the opts from the "accept sock" */
292 		new_sock->opts = sock->opts;
293 		memcpy(&new_sock->opts, &sock->opts, sizeof(new_sock->opts));
294 		new_sock->net_impl = sock->net_impl;
295 		TAILQ_INIT(&new_sock->queued_reqs);
296 		TAILQ_INIT(&new_sock->pending_reqs);
297 	}
298 
299 	return new_sock;
300 }
301 
302 int
303 spdk_sock_close(struct spdk_sock **_sock)
304 {
305 	struct spdk_sock *sock = *_sock;
306 	int rc;
307 
308 	if (sock == NULL) {
309 		errno = EBADF;
310 		return -1;
311 	}
312 
313 	if (sock->cb_fn != NULL) {
314 		/* This sock is still part of a sock_group. */
315 		errno = EBUSY;
316 		return -1;
317 	}
318 
319 	sock->flags.closed = true;
320 
321 	if (sock->cb_cnt > 0) {
322 		/* Let the callback unwind before destroying the socket */
323 		return 0;
324 	}
325 
326 	spdk_sock_abort_requests(sock);
327 
328 	rc = sock->net_impl->close(sock);
329 	if (rc == 0) {
330 		*_sock = NULL;
331 	}
332 
333 	return rc;
334 }
335 
336 ssize_t
337 spdk_sock_recv(struct spdk_sock *sock, void *buf, size_t len)
338 {
339 	if (sock == NULL) {
340 		errno = EBADF;
341 		return -1;
342 	}
343 
344 	if (sock->flags.closed) {
345 		errno = EBADF;
346 		return -1;
347 	}
348 
349 	return sock->net_impl->recv(sock, buf, len);
350 }
351 
352 ssize_t
353 spdk_sock_readv(struct spdk_sock *sock, struct iovec *iov, int iovcnt)
354 {
355 	if (sock == NULL) {
356 		errno = EBADF;
357 		return -1;
358 	}
359 
360 	if (sock->flags.closed) {
361 		errno = EBADF;
362 		return -1;
363 	}
364 
365 	return sock->net_impl->readv(sock, iov, iovcnt);
366 }
367 
368 ssize_t
369 spdk_sock_writev(struct spdk_sock *sock, struct iovec *iov, int iovcnt)
370 {
371 	if (sock == NULL) {
372 		errno = EBADF;
373 		return -1;
374 	}
375 
376 	if (sock->flags.closed) {
377 		errno = EBADF;
378 		return -1;
379 	}
380 
381 	return sock->net_impl->writev(sock, iov, iovcnt);
382 }
383 
384 void
385 spdk_sock_writev_async(struct spdk_sock *sock, struct spdk_sock_request *req)
386 {
387 	assert(req->cb_fn != NULL);
388 
389 	if (sock == NULL) {
390 		req->cb_fn(req->cb_arg, -EBADF);
391 		return;
392 	}
393 
394 	if (sock->flags.closed) {
395 		req->cb_fn(req->cb_arg, -EBADF);
396 		return;
397 	}
398 
399 	sock->net_impl->writev_async(sock, req);
400 }
401 
402 int
403 spdk_sock_flush(struct spdk_sock *sock)
404 {
405 	if (sock == NULL) {
406 		return -EBADF;
407 	}
408 
409 	if (sock->flags.closed) {
410 		return -EBADF;
411 	}
412 
413 	return sock->net_impl->flush(sock);
414 }
415 
416 int
417 spdk_sock_set_recvlowat(struct spdk_sock *sock, int nbytes)
418 {
419 	return sock->net_impl->set_recvlowat(sock, nbytes);
420 }
421 
422 int
423 spdk_sock_set_recvbuf(struct spdk_sock *sock, int sz)
424 {
425 	return sock->net_impl->set_recvbuf(sock, sz);
426 }
427 
428 int
429 spdk_sock_set_sendbuf(struct spdk_sock *sock, int sz)
430 {
431 	return sock->net_impl->set_sendbuf(sock, sz);
432 }
433 
434 bool
435 spdk_sock_is_ipv6(struct spdk_sock *sock)
436 {
437 	return sock->net_impl->is_ipv6(sock);
438 }
439 
440 bool
441 spdk_sock_is_ipv4(struct spdk_sock *sock)
442 {
443 	return sock->net_impl->is_ipv4(sock);
444 }
445 
446 bool
447 spdk_sock_is_connected(struct spdk_sock *sock)
448 {
449 	return sock->net_impl->is_connected(sock);
450 }
451 
452 struct spdk_sock_group *
453 spdk_sock_group_create(void *ctx)
454 {
455 	struct spdk_net_impl *impl = NULL;
456 	struct spdk_sock_group *group;
457 	struct spdk_sock_group_impl *group_impl;
458 
459 	group = calloc(1, sizeof(*group));
460 	if (group == NULL) {
461 		return NULL;
462 	}
463 
464 	STAILQ_INIT(&group->group_impls);
465 
466 	STAILQ_FOREACH_FROM(impl, &g_net_impls, link) {
467 		group_impl = impl->group_impl_create();
468 		if (group_impl != NULL) {
469 			STAILQ_INSERT_TAIL(&group->group_impls, group_impl, link);
470 			TAILQ_INIT(&group_impl->socks);
471 			group_impl->num_removed_socks = 0;
472 			group_impl->net_impl = impl;
473 		}
474 	}
475 
476 	group->ctx = ctx;
477 	return group;
478 }
479 
480 void *
481 spdk_sock_group_get_ctx(struct spdk_sock_group *group)
482 {
483 	if (group == NULL) {
484 		return NULL;
485 	}
486 
487 	return group->ctx;
488 }
489 
490 int
491 spdk_sock_group_add_sock(struct spdk_sock_group *group, struct spdk_sock *sock,
492 			 spdk_sock_cb cb_fn, void *cb_arg)
493 {
494 	struct spdk_sock_group_impl *group_impl = NULL;
495 	int rc, placement_id = 0;
496 
497 	if (cb_fn == NULL) {
498 		errno = EINVAL;
499 		return -1;
500 	}
501 
502 	if (sock->group_impl != NULL) {
503 		/*
504 		 * This sock is already part of a sock_group.  Currently we don't
505 		 *  support this.
506 		 */
507 		errno = EBUSY;
508 		return -1;
509 	}
510 
511 	rc = sock->net_impl->get_placement_id(sock, &placement_id);
512 	if (!rc && (placement_id != 0)) {
513 		rc = sock_map_insert(placement_id, group);
514 		if (rc < 0) {
515 			return -1;
516 		}
517 	}
518 
519 	STAILQ_FOREACH_FROM(group_impl, &group->group_impls, link) {
520 		if (sock->net_impl == group_impl->net_impl) {
521 			break;
522 		}
523 	}
524 
525 	if (group_impl == NULL) {
526 		errno = EINVAL;
527 		return -1;
528 	}
529 
530 	rc = group_impl->net_impl->group_impl_add_sock(group_impl, sock);
531 	if (rc == 0) {
532 		TAILQ_INSERT_TAIL(&group_impl->socks, sock, link);
533 		sock->group_impl = group_impl;
534 		sock->cb_fn = cb_fn;
535 		sock->cb_arg = cb_arg;
536 	}
537 
538 	return rc;
539 }
540 
541 int
542 spdk_sock_group_remove_sock(struct spdk_sock_group *group, struct spdk_sock *sock)
543 {
544 	struct spdk_sock_group_impl *group_impl = NULL;
545 	int rc, placement_id = 0;
546 
547 	STAILQ_FOREACH_FROM(group_impl, &group->group_impls, link) {
548 		if (sock->net_impl == group_impl->net_impl) {
549 			break;
550 		}
551 	}
552 
553 	if (group_impl == NULL) {
554 		errno = EINVAL;
555 		return -1;
556 	}
557 
558 	assert(group_impl == sock->group_impl);
559 
560 	rc = sock->net_impl->get_placement_id(sock, &placement_id);
561 	if (!rc && (placement_id != 0)) {
562 		sock_map_release(placement_id);
563 	}
564 
565 	rc = group_impl->net_impl->group_impl_remove_sock(group_impl, sock);
566 	if (rc == 0) {
567 		TAILQ_REMOVE(&group_impl->socks, sock, link);
568 		assert(group_impl->num_removed_socks < MAX_EVENTS_PER_POLL);
569 		group_impl->removed_socks[group_impl->num_removed_socks] = (uintptr_t)sock;
570 		group_impl->num_removed_socks++;
571 		sock->group_impl = NULL;
572 		sock->cb_fn = NULL;
573 		sock->cb_arg = NULL;
574 	}
575 
576 	return rc;
577 }
578 
579 int
580 spdk_sock_group_poll(struct spdk_sock_group *group)
581 {
582 	return spdk_sock_group_poll_count(group, MAX_EVENTS_PER_POLL);
583 }
584 
585 static int
586 sock_group_impl_poll_count(struct spdk_sock_group_impl *group_impl,
587 			   struct spdk_sock_group *group,
588 			   int max_events)
589 {
590 	struct spdk_sock *socks[MAX_EVENTS_PER_POLL];
591 	int num_events, i;
592 
593 	if (TAILQ_EMPTY(&group_impl->socks)) {
594 		return 0;
595 	}
596 
597 	/* The number of removed sockets should be reset for each call to poll. */
598 	group_impl->num_removed_socks = 0;
599 
600 	num_events = group_impl->net_impl->group_impl_poll(group_impl, max_events, socks);
601 	if (num_events == -1) {
602 		return -1;
603 	}
604 
605 	for (i = 0; i < num_events; i++) {
606 		struct spdk_sock *sock = socks[i];
607 		int j;
608 		bool valid = true;
609 		for (j = 0; j < group_impl->num_removed_socks; j++) {
610 			if ((uintptr_t)sock == group_impl->removed_socks[j]) {
611 				valid = false;
612 				break;
613 			}
614 		}
615 
616 		if (valid) {
617 			assert(sock->cb_fn != NULL);
618 			sock->cb_fn(sock->cb_arg, group, sock);
619 		}
620 	}
621 
622 	return num_events;
623 }
624 
625 int
626 spdk_sock_group_poll_count(struct spdk_sock_group *group, int max_events)
627 {
628 	struct spdk_sock_group_impl *group_impl = NULL;
629 	int rc, num_events = 0;
630 
631 	if (max_events < 1) {
632 		errno = -EINVAL;
633 		return -1;
634 	}
635 
636 	/*
637 	 * Only poll for up to 32 events at a time - if more events are pending,
638 	 *  the next call to this function will reap them.
639 	 */
640 	if (max_events > MAX_EVENTS_PER_POLL) {
641 		max_events = MAX_EVENTS_PER_POLL;
642 	}
643 
644 	STAILQ_FOREACH_FROM(group_impl, &group->group_impls, link) {
645 		rc = sock_group_impl_poll_count(group_impl, group, max_events);
646 		if (rc < 0) {
647 			num_events = -1;
648 			SPDK_ERRLOG("group_impl_poll_count for net(%s) failed\n",
649 				    group_impl->net_impl->name);
650 		} else if (num_events >= 0) {
651 			num_events += rc;
652 		}
653 	}
654 
655 	return num_events;
656 }
657 
658 int
659 spdk_sock_group_close(struct spdk_sock_group **group)
660 {
661 	struct spdk_sock_group_impl *group_impl = NULL, *tmp;
662 	int rc;
663 
664 	if (*group == NULL) {
665 		errno = EBADF;
666 		return -1;
667 	}
668 
669 	STAILQ_FOREACH_SAFE(group_impl, &(*group)->group_impls, link, tmp) {
670 		if (!TAILQ_EMPTY(&group_impl->socks)) {
671 			errno = EBUSY;
672 			return -1;
673 		}
674 	}
675 
676 	STAILQ_FOREACH_SAFE(group_impl, &(*group)->group_impls, link, tmp) {
677 		rc = group_impl->net_impl->group_impl_close(group_impl);
678 		if (rc != 0) {
679 			SPDK_ERRLOG("group_impl_close for net(%s) failed\n",
680 				    group_impl->net_impl->name);
681 		}
682 	}
683 
684 	sock_remove_sock_group_from_map_table(*group);
685 	free(*group);
686 	*group = NULL;
687 
688 	return 0;
689 }
690 
691 static inline struct spdk_net_impl *
692 sock_get_impl_by_name(const char *impl_name)
693 {
694 	struct spdk_net_impl *impl;
695 
696 	assert(impl_name != NULL);
697 	STAILQ_FOREACH(impl, &g_net_impls, link) {
698 		if (0 == strcmp(impl_name, impl->name)) {
699 			return impl;
700 		}
701 	}
702 
703 	return NULL;
704 }
705 
706 int
707 spdk_sock_impl_get_opts(const char *impl_name, struct spdk_sock_impl_opts *opts, size_t *len)
708 {
709 	struct spdk_net_impl *impl;
710 
711 	if (!impl_name || !opts || !len) {
712 		errno = EINVAL;
713 		return -1;
714 	}
715 
716 	impl = sock_get_impl_by_name(impl_name);
717 	if (!impl) {
718 		errno = EINVAL;
719 		return -1;
720 	}
721 
722 	if (!impl->get_opts) {
723 		errno = ENOTSUP;
724 		return -1;
725 	}
726 
727 	return impl->get_opts(opts, len);
728 }
729 
730 int
731 spdk_sock_impl_set_opts(const char *impl_name, const struct spdk_sock_impl_opts *opts, size_t len)
732 {
733 	struct spdk_net_impl *impl;
734 
735 	if (!impl_name || !opts) {
736 		errno = EINVAL;
737 		return -1;
738 	}
739 
740 	impl = sock_get_impl_by_name(impl_name);
741 	if (!impl) {
742 		errno = EINVAL;
743 		return -1;
744 	}
745 
746 	if (!impl->set_opts) {
747 		errno = ENOTSUP;
748 		return -1;
749 	}
750 
751 	return impl->set_opts(opts, len);
752 }
753 
754 void
755 spdk_sock_write_config_json(struct spdk_json_write_ctx *w)
756 {
757 	struct spdk_net_impl *impl;
758 	struct spdk_sock_impl_opts opts;
759 	size_t len;
760 
761 	assert(w != NULL);
762 
763 	spdk_json_write_array_begin(w);
764 
765 	STAILQ_FOREACH(impl, &g_net_impls, link) {
766 		if (!impl->get_opts) {
767 			continue;
768 		}
769 
770 		len = sizeof(opts);
771 		if (impl->get_opts(&opts, &len) == 0) {
772 			spdk_json_write_object_begin(w);
773 			spdk_json_write_named_string(w, "method", "sock_impl_set_options");
774 			spdk_json_write_named_object_begin(w, "params");
775 			spdk_json_write_named_string(w, "impl_name", impl->name);
776 			spdk_json_write_named_uint32(w, "recv_buf_size", opts.recv_buf_size);
777 			spdk_json_write_named_uint32(w, "send_buf_size", opts.send_buf_size);
778 			spdk_json_write_object_end(w);
779 			spdk_json_write_object_end(w);
780 		} else {
781 			SPDK_ERRLOG("Failed to get socket options for socket implementation %s\n", impl->name);
782 		}
783 	}
784 
785 	spdk_json_write_array_end(w);
786 }
787 
788 void
789 spdk_net_impl_register(struct spdk_net_impl *impl, int priority)
790 {
791 	struct spdk_net_impl *cur, *prev;
792 
793 	impl->priority = priority;
794 	prev = NULL;
795 	STAILQ_FOREACH(cur, &g_net_impls, link) {
796 		if (impl->priority > cur->priority) {
797 			break;
798 		}
799 		prev = cur;
800 	}
801 
802 	if (prev) {
803 		STAILQ_INSERT_AFTER(&g_net_impls, prev, impl, link);
804 	} else {
805 		STAILQ_INSERT_HEAD(&g_net_impls, impl, link);
806 	}
807 }
808