Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2018 Intel Corporation. All rights reserved.
3 : * Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved.
4 : */
5 :
6 : /** \file
7 : * TCP network implementation abstraction layer
8 : */
9 :
10 : #ifndef SPDK_INTERNAL_SOCK_H
11 : #define SPDK_INTERNAL_SOCK_H
12 :
13 : #include "spdk/stdinc.h"
14 : #include "spdk/sock.h"
15 : #include "spdk/queue.h"
16 : #include "spdk/likely.h"
17 : #include "spdk/log.h"
18 : #include "spdk/trace.h"
19 : #include "spdk_internal/trace_defs.h"
20 :
21 : #ifdef __cplusplus
22 : extern "C" {
23 : #endif
24 :
25 : #define MAX_EVENTS_PER_POLL 32
26 : #define DEFAULT_SOCK_PRIORITY 0
27 : #define MIN_SOCK_PIPE_SIZE 1024
28 : #define DEFAULT_SO_RCVBUF_SIZE (2 * 1024 * 1024)
29 : #define DEFAULT_SO_SNDBUF_SIZE (2 * 1024 * 1024)
30 : #define MIN_SO_RCVBUF_SIZE (4 * 1024)
31 : #define MIN_SO_SNDBUF_SIZE (4 * 1024)
32 : #define IOV_BATCH_SIZE 64
33 :
34 : struct spdk_sock {
35 : struct spdk_net_impl *net_impl;
36 : struct spdk_sock_opts opts;
37 : struct spdk_sock_group_impl *group_impl;
38 : TAILQ_ENTRY(spdk_sock) link;
39 :
40 : TAILQ_HEAD(, spdk_sock_request) queued_reqs;
41 : TAILQ_HEAD(, spdk_sock_request) pending_reqs;
42 : struct spdk_sock_request *read_req;
43 : int queued_iovcnt;
44 : int cb_cnt;
45 : spdk_sock_cb cb_fn;
46 : void *cb_arg;
47 : struct {
48 : uint8_t closed : 1;
49 : uint8_t reserved : 7;
50 : } flags;
51 : struct spdk_sock_impl_opts impl_opts;
52 : };
53 :
54 : struct spdk_sock_group_provided_buf {
55 : size_t len;
56 : void *ctx;
57 : STAILQ_ENTRY(spdk_sock_group_provided_buf) link;
58 : };
59 :
60 : struct spdk_sock_group {
61 : STAILQ_HEAD(, spdk_sock_group_impl) group_impls;
62 : STAILQ_HEAD(, spdk_sock_group_provided_buf) pool;
63 : void *ctx;
64 : };
65 :
66 : struct spdk_sock_group_impl {
67 : struct spdk_net_impl *net_impl;
68 : struct spdk_sock_group *group;
69 : TAILQ_HEAD(, spdk_sock) socks;
70 : STAILQ_ENTRY(spdk_sock_group_impl) link;
71 : };
72 :
73 : struct spdk_sock_map {
74 : STAILQ_HEAD(, spdk_sock_placement_id_entry) entries;
75 : pthread_mutex_t mtx;
76 : };
77 :
78 : struct spdk_net_impl {
79 : const char *name;
80 :
81 : int (*getaddr)(struct spdk_sock *sock, char *saddr, int slen, uint16_t *sport, char *caddr,
82 : int clen, uint16_t *cport);
83 : struct spdk_sock *(*connect)(const char *ip, int port, struct spdk_sock_opts *opts);
84 : struct spdk_sock *(*listen)(const char *ip, int port, struct spdk_sock_opts *opts);
85 : struct spdk_sock *(*accept)(struct spdk_sock *sock);
86 : int (*close)(struct spdk_sock *sock);
87 : ssize_t (*recv)(struct spdk_sock *sock, void *buf, size_t len);
88 : ssize_t (*readv)(struct spdk_sock *sock, struct iovec *iov, int iovcnt);
89 : ssize_t (*writev)(struct spdk_sock *sock, struct iovec *iov, int iovcnt);
90 :
91 : int (*recv_next)(struct spdk_sock *sock, void **buf, void **ctx);
92 : void (*writev_async)(struct spdk_sock *sock, struct spdk_sock_request *req);
93 : void (*readv_async)(struct spdk_sock *sock, struct spdk_sock_request *req);
94 : int (*flush)(struct spdk_sock *sock);
95 :
96 : int (*set_recvlowat)(struct spdk_sock *sock, int nbytes);
97 : int (*set_recvbuf)(struct spdk_sock *sock, int sz);
98 : int (*set_sendbuf)(struct spdk_sock *sock, int sz);
99 :
100 : bool (*is_ipv6)(struct spdk_sock *sock);
101 : bool (*is_ipv4)(struct spdk_sock *sock);
102 : bool (*is_connected)(struct spdk_sock *sock);
103 :
104 : struct spdk_sock_group_impl *(*group_impl_get_optimal)(struct spdk_sock *sock,
105 : struct spdk_sock_group_impl *hint);
106 : struct spdk_sock_group_impl *(*group_impl_create)(void);
107 : int (*group_impl_add_sock)(struct spdk_sock_group_impl *group, struct spdk_sock *sock);
108 : int (*group_impl_remove_sock)(struct spdk_sock_group_impl *group, struct spdk_sock *sock);
109 : int (*group_impl_poll)(struct spdk_sock_group_impl *group, int max_events,
110 : struct spdk_sock **socks);
111 : int (*group_impl_close)(struct spdk_sock_group_impl *group);
112 :
113 : int (*get_opts)(struct spdk_sock_impl_opts *opts, size_t *len);
114 : int (*set_opts)(const struct spdk_sock_impl_opts *opts, size_t len);
115 :
116 : STAILQ_ENTRY(spdk_net_impl) link;
117 : };
118 :
119 : void spdk_net_impl_register(struct spdk_net_impl *impl);
120 :
121 : #define SPDK_NET_IMPL_REGISTER(name, impl) \
122 : static void __attribute__((constructor)) net_impl_register_##name(void) \
123 : { \
124 : spdk_net_impl_register(impl); \
125 : }
126 :
127 : #define SPDK_NET_IMPL_REGISTER_DEFAULT(name, impl) \
128 : static void __attribute__((constructor)) net_impl_register_default_##name(void) \
129 : { \
130 : spdk_net_impl_register(impl); \
131 : spdk_sock_set_default_impl(SPDK_STRINGIFY(name)); \
132 : }
133 :
134 : size_t spdk_sock_group_get_buf(struct spdk_sock_group *group, void **buf, void **ctx);
135 :
136 : static inline void
137 8 : spdk_sock_request_queue(struct spdk_sock *sock, struct spdk_sock_request *req)
138 : {
139 8 : assert(req->internal.curr_list == NULL);
140 8 : if (spdk_trace_tpoint_enabled(TRACE_SOCK_REQ_QUEUE)) {
141 0 : uint64_t len = 0;
142 : int i;
143 :
144 0 : for (i = 0; i < req->iovcnt; i++) {
145 0 : len += SPDK_SOCK_REQUEST_IOV(req, i)->iov_len;
146 : }
147 0 : spdk_trace_record(TRACE_SOCK_REQ_QUEUE, 0, len, (uintptr_t)req, (uintptr_t)req->cb_arg);
148 : }
149 8 : TAILQ_INSERT_TAIL(&sock->queued_reqs, req, internal.link);
150 : #ifdef DEBUG
151 8 : req->internal.curr_list = &sock->queued_reqs;
152 : #endif
153 8 : sock->queued_iovcnt += req->iovcnt;
154 8 : }
155 :
156 : static inline void
157 6 : spdk_sock_request_pend(struct spdk_sock *sock, struct spdk_sock_request *req)
158 : {
159 6 : assert(req->internal.curr_list == &sock->queued_reqs);
160 6 : spdk_trace_record(TRACE_SOCK_REQ_PEND, 0, 0, (uintptr_t)req, (uintptr_t)req->cb_arg);
161 6 : TAILQ_REMOVE(&sock->queued_reqs, req, internal.link);
162 6 : assert(sock->queued_iovcnt >= req->iovcnt);
163 6 : sock->queued_iovcnt -= req->iovcnt;
164 6 : TAILQ_INSERT_TAIL(&sock->pending_reqs, req, internal.link);
165 : #ifdef DEBUG
166 6 : req->internal.curr_list = &sock->pending_reqs;
167 : #endif
168 6 : }
169 :
170 : static inline int
171 6 : spdk_sock_request_complete(struct spdk_sock *sock, struct spdk_sock_request *req, int err)
172 : {
173 : bool closed;
174 6 : int rc = 0;
175 :
176 6 : spdk_trace_record(TRACE_SOCK_REQ_COMPLETE, 0, 0, (uintptr_t)req, (uintptr_t)req->cb_arg);
177 6 : req->internal.offset = 0;
178 6 : req->internal.is_zcopy = 0;
179 :
180 6 : closed = sock->flags.closed;
181 6 : sock->cb_cnt++;
182 6 : req->cb_fn(req->cb_arg, err);
183 6 : assert(sock->cb_cnt > 0);
184 6 : sock->cb_cnt--;
185 :
186 6 : if (sock->cb_cnt == 0 && !closed && sock->flags.closed) {
187 : /* The user closed the socket in response to a callback above. */
188 1 : rc = -1;
189 1 : spdk_sock_close(&sock);
190 : }
191 :
192 6 : return rc;
193 : }
194 :
195 : static inline int
196 6 : spdk_sock_request_put(struct spdk_sock *sock, struct spdk_sock_request *req, int err)
197 : {
198 6 : assert(req->internal.curr_list == &sock->pending_reqs);
199 6 : TAILQ_REMOVE(&sock->pending_reqs, req, internal.link);
200 : #ifdef DEBUG
201 6 : req->internal.curr_list = NULL;
202 : #endif
203 6 : return spdk_sock_request_complete(sock, req, err);
204 : }
205 :
206 : static inline int
207 32 : spdk_sock_abort_requests(struct spdk_sock *sock)
208 : {
209 : struct spdk_sock_request *req;
210 : bool closed;
211 32 : int rc = 0;
212 :
213 32 : closed = sock->flags.closed;
214 32 : sock->cb_cnt++;
215 :
216 32 : req = TAILQ_FIRST(&sock->pending_reqs);
217 32 : while (req) {
218 0 : assert(req->internal.curr_list == &sock->pending_reqs);
219 0 : TAILQ_REMOVE(&sock->pending_reqs, req, internal.link);
220 : #ifdef DEBUG
221 0 : req->internal.curr_list = NULL;
222 : #endif
223 :
224 0 : req->cb_fn(req->cb_arg, -ECANCELED);
225 :
226 0 : req = TAILQ_FIRST(&sock->pending_reqs);
227 : }
228 :
229 32 : req = TAILQ_FIRST(&sock->queued_reqs);
230 33 : while (req) {
231 1 : assert(req->internal.curr_list == &sock->queued_reqs);
232 1 : TAILQ_REMOVE(&sock->queued_reqs, req, internal.link);
233 : #ifdef DEBUG
234 1 : req->internal.curr_list = NULL;
235 : #endif
236 :
237 1 : assert(sock->queued_iovcnt >= req->iovcnt);
238 1 : sock->queued_iovcnt -= req->iovcnt;
239 :
240 1 : req->cb_fn(req->cb_arg, -ECANCELED);
241 :
242 1 : req = TAILQ_FIRST(&sock->queued_reqs);
243 : }
244 :
245 32 : req = sock->read_req;
246 32 : if (req != NULL) {
247 0 : sock->read_req = NULL;
248 0 : req->cb_fn(req->cb_arg, -ECANCELED);
249 : }
250 32 : assert(sock->cb_cnt > 0);
251 32 : sock->cb_cnt--;
252 :
253 32 : assert(TAILQ_EMPTY(&sock->queued_reqs));
254 32 : assert(TAILQ_EMPTY(&sock->pending_reqs));
255 :
256 32 : if (sock->cb_cnt == 0 && !closed && sock->flags.closed) {
257 : /* The user closed the socket in response to a callback above. */
258 0 : rc = -1;
259 0 : spdk_sock_close(&sock);
260 : }
261 :
262 32 : return rc;
263 : }
264 :
265 : static inline int
266 10 : spdk_sock_prep_req(struct spdk_sock_request *req, struct iovec *iovs, int index,
267 : uint64_t *num_bytes)
268 : {
269 : unsigned int offset;
270 : int iovcnt, i;
271 :
272 10 : assert(index < IOV_BATCH_SIZE);
273 10 : offset = req->internal.offset;
274 10 : iovcnt = index;
275 :
276 28 : for (i = 0; i < req->iovcnt; i++) {
277 : /* Consume any offset first */
278 18 : if (offset >= SPDK_SOCK_REQUEST_IOV(req, i)->iov_len) {
279 1 : offset -= SPDK_SOCK_REQUEST_IOV(req, i)->iov_len;
280 1 : continue;
281 : }
282 :
283 17 : iovs[iovcnt].iov_base = (uint8_t *)SPDK_SOCK_REQUEST_IOV(req, i)->iov_base + offset;
284 17 : iovs[iovcnt].iov_len = SPDK_SOCK_REQUEST_IOV(req, i)->iov_len - offset;
285 17 : if (num_bytes != NULL) {
286 17 : *num_bytes += iovs[iovcnt].iov_len;
287 : }
288 :
289 17 : iovcnt++;
290 17 : offset = 0;
291 :
292 17 : if (iovcnt >= IOV_BATCH_SIZE) {
293 0 : break;
294 : }
295 : }
296 :
297 10 : return iovcnt;
298 : }
299 :
300 : static inline int
301 29 : spdk_sock_prep_reqs(struct spdk_sock *_sock, struct iovec *iovs, int index,
302 : struct spdk_sock_request **last_req, int *flags)
303 : {
304 : int iovcnt;
305 : struct spdk_sock_request *req;
306 29 : uint64_t total = 0;
307 :
308 : /* Gather an iov */
309 29 : iovcnt = index;
310 29 : if (spdk_unlikely(iovcnt >= IOV_BATCH_SIZE)) {
311 0 : goto end;
312 : }
313 :
314 29 : if (last_req != NULL && *last_req != NULL) {
315 0 : req = TAILQ_NEXT(*last_req, internal.link);
316 : } else {
317 29 : req = TAILQ_FIRST(&_sock->queued_reqs);
318 : }
319 :
320 39 : while (req) {
321 10 : iovcnt = spdk_sock_prep_req(req, iovs, iovcnt, &total);
322 10 : if (iovcnt >= IOV_BATCH_SIZE) {
323 0 : break;
324 : }
325 :
326 10 : if (last_req != NULL) {
327 0 : *last_req = req;
328 : }
329 10 : req = TAILQ_NEXT(req, internal.link);
330 : }
331 :
332 29 : end:
333 :
334 : #if defined(MSG_ZEROCOPY)
335 : /* if data size < zerocopy_threshold, remove MSG_ZEROCOPY flag */
336 29 : if (total < _sock->impl_opts.zerocopy_threshold && flags != NULL) {
337 0 : *flags = *flags & (~MSG_ZEROCOPY);
338 : }
339 : #endif
340 :
341 29 : return iovcnt;
342 : }
343 :
344 : static inline void
345 21 : spdk_sock_get_placement_id(int fd, enum spdk_placement_mode mode, int *placement_id)
346 : {
347 21 : *placement_id = -1;
348 :
349 21 : switch (mode) {
350 21 : case PLACEMENT_NONE:
351 21 : break;
352 0 : case PLACEMENT_MARK:
353 : case PLACEMENT_NAPI: {
354 : #if defined(SO_INCOMING_NAPI_ID)
355 0 : socklen_t len = sizeof(int);
356 :
357 0 : int rc = getsockopt(fd, SOL_SOCKET, SO_INCOMING_NAPI_ID, placement_id, &len);
358 0 : if (rc == -1) {
359 0 : SPDK_ERRLOG("getsockopt() failed: %s\n", strerror(errno));
360 0 : assert(false);
361 : }
362 : #endif
363 0 : break;
364 : }
365 0 : case PLACEMENT_CPU: {
366 : #if defined(SO_INCOMING_CPU)
367 0 : socklen_t len = sizeof(int);
368 :
369 0 : int rc = getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, placement_id, &len);
370 0 : if (rc == -1) {
371 0 : SPDK_ERRLOG("getsockopt() failed: %s\n", strerror(errno));
372 0 : assert(false);
373 : }
374 : #endif
375 0 : break;
376 : }
377 0 : default:
378 0 : break;
379 : }
380 21 : }
381 :
382 : /**
383 : * Insert a group into the placement map.
384 : * If the group is already in the map, take a reference.
385 : */
386 : int spdk_sock_map_insert(struct spdk_sock_map *map, int placement_id,
387 : struct spdk_sock_group_impl *group_impl);
388 :
389 : /**
390 : * Release a reference for the given placement_id. If the reference count goes to 0, the
391 : * entry will no longer be associated with a group.
392 : */
393 : void spdk_sock_map_release(struct spdk_sock_map *map, int placement_id);
394 :
395 : /**
396 : * Look up the group for the given placement_id.
397 : */
398 : int spdk_sock_map_lookup(struct spdk_sock_map *map, int placement_id,
399 : struct spdk_sock_group_impl **group_impl, struct spdk_sock_group_impl *hint);
400 :
401 : /**
402 : * Find a placement id with no associated group
403 : */
404 : int spdk_sock_map_find_free(struct spdk_sock_map *map);
405 :
406 : /**
407 : * Clean up all memory associated with the given map
408 : */
409 : void spdk_sock_map_cleanup(struct spdk_sock_map *map);
410 :
411 : #ifdef __cplusplus
412 : }
413 : #endif
414 :
415 : #endif /* SPDK_INTERNAL_SOCK_H */
|