Line data Source code
1 : #ifndef HEADER_fd_src_waltz_aio_fd_aio_h
2 : #define HEADER_fd_src_waltz_aio_fd_aio_h
3 :
4 : #include "../fd_waltz_base.h"
5 :
6 : /* fd_aio defines a simple abstraction for asynchronous sending and
7 : receiving packets. It abstracts away many details so the same code
8 : can work performant and transparent with different low level I/O
9 : libraries and hardware.
10 :
11 : FIXME: update the below documentation. Also, given fd_aio_t almost
12 : certainly is behaves like an abstract base class, doing a declaration
13 : like the below for a fd_aio_t is probably wrong because different
14 : implementations likely will need different footprints and the like
15 : long term.
16 :
17 : ### Example: Setup
18 :
19 : fd_quic_t * quic = get_quic(); // get an initialized quic instance
20 : fd_xdp_t * xdp = get_xdp(); // get an initialized xdp instance
21 :
22 : fd_aio_t aio_xdp_to_quic;
23 : fd_aio_t aio_quic_to_xdp;
24 :
25 : fd_quic_set_aio( quic, aio_xdp_to_quic, aio_quic_to_xdp );
26 : fd_xdp_set_aio ( xdp, aio_quic_to_xdp, aio_xdp_to_quic );
27 :
28 : // the two *set_aio calls have the following effect:
29 : // aio_xdp_to_quic.recv = fd_aio_cb_receive;
30 : // aio_xdp_to_quic.ctx = quic;
31 :
32 : // aio_quic_to_xdp.recv = fd_xdp_aio_cb_receive;
33 : // aio_quic_to_xdp.ctx = xdp;
34 :
35 : // now whenever fd_quic_process is called on quic, quic will
36 : // be able to send data to xdp via fd_aio_send(...)
37 : // and vice versa
38 :
39 : ### Example: Sending
40 :
41 : fd_aio_t aio;
42 :
43 : aio.recv = my_cb_receive;
44 : aio.ctx quic;
45 :
46 : fd_aio_pkt_info_t batch[10] = {{ .data = data, .data_sz = data_sz }};
47 :
48 : fd_aio_pkt_info_t cur_batch = batch;
49 : ulong cur_batch_sz = 10;
50 : while( cur_batch_sz ) {
51 : int send_rc = fd_aio_send( aio, cur_batch, cur_batch_sz ); // send a batch of buffers to the peer
52 : if( send_rc < 0 ) {
53 : fprintf( stderr, "error occurred during send\n" );
54 : break;
55 : }
56 : cur_batch += send_rc;
57 : cur_batch_sz -= send_rc;
58 :
59 : // possibly do some other process that might free up resources
60 : // to avoid deadlock
61 : } */
62 :
63 : /* FD_AIO_SUCCESS, FD_AIO_ERR_* give a number of integer error codes
64 : used by AIO operations. FD_AIO_ERR_* will be negative integers.
65 :
66 : FIXME: these current values are largely common placeholders. These
67 : should be revamped for specific AIO instance needs and harmonized
68 : with other fd error codes long term. */
69 :
70 69079687 : #define FD_AIO_SUCCESS ( 0) /* Success */
71 9 : #define FD_AIO_ERR_INVAL (-1) /* Bad input args */
72 19406603 : #define FD_AIO_ERR_AGAIN (-2) /* Try again later */
73 :
74 : /* An fd_aio_pkt_info_t is used to describe a memory region in the
75 : caller's local address space for sending and receiving packets. Note
76 : that, as this is only used by AIO APIs to box up info the caller
77 : communications with AIO instances, this is not an object (i.e. it has
78 : no need for object creation/destruction/accessor semantics). */
79 :
80 : /* FD_AIO_PKT_INFO_{ALIGN,FOOTPRINT} specify the alignment and footprint
81 : needed for an fd_aio_pkt_info_t. ALIGN will be positive integer
82 : power of 2. FOOTPRINT will be an integer multiple of align. */
83 :
84 : #define FD_AIO_PKT_INFO_ALIGN (16UL)
85 : #define FD_AIO_PKT_INFO_FOOTPRINT (16UL)
86 :
87 : /* FD_AIO_PKT_INFO_BUF_MAX specifies the largest buffer supported by an
88 : fd_aio_pkt_info_t. FIXME: fine tune this to be the smallest multiple
89 : of something cache line pair-ish to supporting needed functionality
90 : of AIO instances and the application needs. */
91 :
92 : #define FD_AIO_PKT_INFO_BUF_MAX (4096UL)
93 :
94 : struct __attribute__((aligned(FD_AIO_PKT_INFO_ALIGN))) fd_aio_pkt_info {
95 :
96 : /* buf is a pointer in the thread group's local address space to the
97 : first byte of a memory region:
98 :
99 : - Holding a packet received in the background by an AIO instance.
100 : - Holding a packet to be sent in the background by an AIO instance.
101 : - Holding a packet sent in the background by an AIO instance.
102 : - To be used by an AIO instance for receiving future packets.
103 : - ...
104 :
105 : The readability, writability, lifetime, footprint, alignment, ...
106 : requirements of memory region here depending on the specific APIs
107 : and/or AIO instance. */
108 :
109 : void * buf;
110 :
111 : /* buf_sz is the number of bytes in the memory region. The exact
112 : limitations on buf_sz can also depend on specific AIO instance. In
113 : general, this will be in [0,FD_AIO_PKT_INFO_DATA_MAX] and a zero
114 : buf_sz (and buf==NULL for such cases) can be a possibility for some
115 : APIs. */
116 :
117 : ushort buf_sz;
118 :
119 : /* Padding to FD_AIO_PKT_INFO_ALIGN here (reserved for potential
120 : future use and/or use by specific AIO instances). */
121 :
122 : };
123 :
124 : typedef struct fd_aio_pkt_info fd_aio_pkt_info_t;
125 :
126 : /* A fd_aio_send_func_t is used to tell an AIO instance to do a best
127 : effort packet batch send. Unless otherwise noted by a specific API
128 : or implementation:
129 :
130 : - The AIO instance will queue up, in order, the given packet batch
131 : for transmission. Packets in the batch are indexed in
132 : [0,batch_cnt) and info about the packet to transmit is in
133 : batch[idx].
134 :
135 : - batch[idx].buf_sz==0 (and, if so, possibly batch[idx].buf==NULL) is
136 : valid. It will be treated as successfully transmitted and
137 : otherwise ignored.
138 :
139 : - batch_cnt==0 is a valid and returns success immediately.
140 :
141 : - There is no restriction on the packet buffers used by the send
142 : function. For example, specifying the same buffer multiple times
143 : in the batch and/or using overlapping buffers in the batch are
144 : valid.
145 :
146 : - On success, this will return zero (FD_AIO_SUCCESS) and
147 : opt_batch_idx will be ignored.
148 :
149 : - If an error occurs, this will return a negative error code
150 : (FD_AIO_ERR_*).
151 :
152 : - If an error occurs and opt_batch_idx is non-NULL, *opt_batch_idx
153 : will contain the index of the first packet in the batch that was
154 : not "sent" from the caller's POV.
155 :
156 : - As such, in this case, all packets indexed [0,*opt_batch_idx) will
157 : have been "sent" from the caller's POV and those in
158 : [*opt_batch_idx,batch_cnt) were not.
159 :
160 : - Further, in this case and the error reason is specific to a packet,
161 : packets indexed [0,*opt_batch_idx) were seemingly transmissible,
162 : the packet indexed *opt_batch_idx was untransmissible and packets
163 : indexed (*opt_batch_idx,batch_cnt) had unexamined transmissibility.
164 :
165 : - The batch array and memory regions covered by the batch array and
166 : any *opt_batch_idx must not be modified while this is running.
167 : The batch array itself and the memory regions referred to by the
168 : batch array are not modified by this function. The AIO retains no
169 : interest in the batch array, the packet buffers referred to in the
170 : array on return.
171 :
172 : - flush requests an asynchronous best-effort transmit of packets
173 : buffered from this and prior send operations. Actual flush
174 : semantics are implementation-defined.
175 :
176 : Note the reception of any packets "sent" by this call is not
177 : guaranteed. Reasons for failed reception could include local AIO
178 : instance failures not diagnosable at time of call and/or failures,
179 : for whatever reason, in the connectivity between the sender and
180 : receiver. Likewise, though the AIO will send packets in the order
181 : specified, it there is no general guarantee they will be received in
182 : any particular order (within this batch or between batches). */
183 :
184 : /* FIXME: consider passing an fd_aio_t instead of the aio_t ctx so
185 : that send function naturally has access to all other AIO
186 : functionality? */
187 :
188 : typedef int
189 : (*fd_aio_send_func_t)( void * ctx,
190 : fd_aio_pkt_info_t const * batch,
191 : ulong batch_cnt,
192 : ulong * opt_batch_idx,
193 : int flush );
194 :
195 : /* An fd_aio_t * is an opaque handle of an AIO instance. (It
196 : technically isn't here to facilitate inlining of fd_aio operations.) */
197 :
198 : struct fd_aio_private {
199 : void * ctx; /* AIO specific context */
200 : fd_aio_send_func_t send_func; /* Send_func for specific AIO */
201 :
202 : /* FIXME: probably AIO specific functionality state follows here as
203 : per FIXME above (this might also clean up some of the ctx messiness
204 : below too, give the callbacks more power and slightly reduce
205 : overhead for the actual callback invocation because it doesn't need
206 : to do an aio->ctx load as part of the user API). */
207 :
208 : };
209 :
210 : typedef struct fd_aio_private fd_aio_t;
211 :
212 6 : #define FD_AIO_ALIGN (alignof(fd_aio_t))
213 6 : #define FD_AIO_FOOTPRINT (sizeof(fd_aio_t))
214 :
215 : FD_PROTOTYPES_BEGIN
216 :
217 : /* FIXME: document these. Also fd_aio_{align,footprint,new} are
218 : probably not things that should be exposed as per FIXME above. That
219 : is, probably should be more like
220 : fd_aio_{xdp,quic}_{align,footprint,new} and similar for other
221 : specific AIO implementations (e.g. io_uring, etc). Probably implies
222 : that fd_aio_private like have their own delete_func too. Likewise,
223 : if the ctx gets included in the actual aio, the fd_aio_ctx function
224 : probably does away. */
225 :
226 : FD_FN_CONST ulong fd_aio_align ( void );
227 : FD_FN_CONST ulong fd_aio_footprint( void );
228 :
229 : void *
230 : fd_aio_new( void * shmem,
231 : void * ctx, /* FIXME: AIO currently has a R/W interest in ctx for lifetime of AIO */
232 : fd_aio_send_func_t send_func );
233 :
234 : fd_aio_t * fd_aio_join ( void * shaio );
235 : void * fd_aio_leave ( fd_aio_t * aio );
236 : void * fd_aio_delete( void * shaio ); /* FIXME: No interest in the ctx on delete */
237 :
238 6 : FD_FN_PURE static inline void * fd_aio_ctx ( fd_aio_t * aio ) { return FD_LIKELY( aio ) ? aio->ctx : NULL; }
239 6 : FD_FN_PURE static inline fd_aio_send_func_t fd_aio_send_func( fd_aio_t * aio ) { return FD_LIKELY( aio ) ? aio->send_func : NULL; }
240 :
241 : /* fd_aio_send sends a batch of packets. Assumes aio is a current local
242 : join to an AIO instance. The batch, batch_cnt, opt_batch_idx and
243 : return value are as described in fd_aio_send_func_t with any
244 : additional restrictions that might be imposed by the specific AIO
245 : instance. */
246 :
247 : /* TODO: This would ideally be extern inline but that causes issues with
248 : the build system. */
249 :
250 : static inline int
251 : fd_aio_send( fd_aio_t const * aio,
252 : fd_aio_pkt_info_t const * batch,
253 : ulong batch_cnt,
254 : ulong * opt_batch_idx,
255 30250683 : int flush ) {
256 30250683 : return aio->send_func( aio->ctx, batch, batch_cnt, opt_batch_idx, flush );
257 30250683 : }
258 :
259 : /* fd_aio_strerror converts an FD_AIO_SUCCESS / FD_AIO_ERR_* code into
260 : a human readable cstr. The lifetime of the returned pointer is
261 : infinite. The returned pointer is always to a non-NULL cstr. */
262 :
263 : FD_FN_CONST char const *
264 : fd_aio_strerror( int err );
265 :
266 : FD_PROTOTYPES_END
267 :
268 : #endif /* HEADER_fd_src_waltz_aio_fd_aio_h */
|