The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
channel.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 693d7f9ff179420537b600091c13173c70ef1eda $
19 *
20 * @brief Two-way thread-safe channels.
21 * @file io/channel.c
22 *
23 * @copyright 2016 Alan DeKok (aland@freeradius.org)
24 */
25RCSID("$Id: 693d7f9ff179420537b600091c13173c70ef1eda $")
26
27#include <freeradius-devel/io/channel.h>
28#include <freeradius-devel/util/debug.h>
29
30#ifdef HAVE_STDATOMIC_H
31# include <stdatomic.h>
32#else
33# include <freeradius-devel/util/stdatomic.h>
34#endif
35
36/*
37 * Debugging, mainly for channel_test
38 */
39#ifdef DEBUG_CHANNEL
40#define MPRINT(...) fprintf(stdout, __VA_ARGS__)
41#else
42#define MPRINT(...)
43#endif
44
49
50#ifdef DEBUG_CHANNEL
51static fr_table_num_sorted_t const channel_direction[] = {
52 { L("to responder"), TO_RESPONDER },
53 { L("to requestor"), TO_REQUESTOR },
54};
55size_t channel_direction_len = NUM_ELEMENTS(channel_direction);
56#endif
57
58#if 0
59#define SIGNAL_INTERVAL (1000000) //!< The minimum interval between responder signals.
60#endif
61
62/** Size of the atomic queues
63 *
64 * The queue reader MUST service the queue occasionally,
65 * otherwise the writer will not be able to write. If it's too
66 * low, the writer will fail. If it's too high, it will
67 * unnecessarily use memory. So we're better off putting it on
68 * the high side.
69 *
70 * The reader SHOULD service the queues at inter-packet latency.
71 * i.e. at 1M pps, the queue will get serviced every microsecond.
72 */
73#define ATOMIC_QUEUE_SIZE (1024)
74
75/** The payload of every channel control message
76 *
77 * Which event it is comes from the control message, not from the payload.
78 */
79typedef struct {
80 uint64_t ack; //!< the sender's ack sequence, for data done.
81 fr_channel_t *ch; //!< the channel
82 fr_message_set_t *reply_ms; //!< the reply message set, for open only.
84
85/** The callbacks one control plane's owner registered for channel events
86 */
87typedef struct {
88 fr_control_t *control; //!< the control plane these were registered on
89 fr_channel_callbacks_t cb; //!< what the owner does with each event
90 void *uctx; //!< passed to each callback
92
93/** One end of a channel
94 *
95 * Consists of a kqueue descriptor, and an atomic queue.
96 * The atomic queue is there to get bulk data through, because it's more efficient
97 * than pushing 1M+ events per second through a kqueue.
98 */
99typedef struct {
100 fr_channel_direction_t direction; //!< Use for debug messages.
101
102 fr_control_t *control; //!< The control plane we signal this end through.
103
104 void *uctx; //!< Worker context.
105
106 fr_channel_recv_callback_t recv; //!< callback for receiving messages
107 void *recv_uctx; //!< context for receiving messages
108
109 uint64_t sequence; //!< Sequence number for this channel.
110 uint64_t ack; //!< Sequence number of the other end.
111 uint64_t their_view_of_my_sequence; //!< Should be clear.
112
113 uint64_t sequence_at_last_signal; //!< When we last signaled.
114
115 fr_atomic_queue_t *aq; //!< The queue of messages - visible only to this channel.
116
117 atomic_bool active; //!< Whether the channel is active.
118
119 fr_channel_stats_t stats; //!< channel statistics
121
123
124/** A full channel, which consists of two ends
125 *
126 * A channel consists of an I/O identifier that can be placed in kequeue
127 * and an atomic queue in each direction to allow for bidirectional communication.
128 */
130 fr_time_delta_t cpu_time; //!< Total time used by the responder for this channel.
131 fr_time_delta_t processing_time; //!< Time spent by the responder processing requests.
132
133 bool same_thread; //!< are both ends in the same thread?
134
135 fr_channel_end_t end[2]; //!< Two ends of the channel.
136};
137
139 { L("close"), FR_CHANNEL_CONTROL_CLOSE },
140 { L("close-ack"), FR_CHANNEL_CONTROL_CLOSE_ACK },
141 { L("data-done-responder"), FR_CHANNEL_CONTROL_DATA_DONE_RESPONDER },
142 { L("data-to-requestor"), FR_CHANNEL_CONTROL_DATA_TO_REQUESTOR },
143 { L("data-to-responder"), FR_CHANNEL_CONTROL_DATA_TO_RESPONDER },
144 { L("open"), FR_CHANNEL_CONTROL_OPEN },
145};
147
149 { L("high"), PRIORITY_HIGH },
150 { L("low"), PRIORITY_LOW },
151 { L("normal"), PRIORITY_NORMAL },
152 { L("now"), PRIORITY_NOW }
153};
155
156
157/** Create a new channel
158 *
159 * @param[in] ctx The talloc_ctx to allocate channel data in.
160 * @param[in] requestor control plane.
161 * @param[in] responder control plane.
162 * @param[in] same whether or not the channel is for the same thread
163 * @return
164 * - NULL on error
165 * - channel on success
166 */
167fr_channel_t *fr_channel_create(TALLOC_CTX *ctx, fr_control_t *requestor, fr_control_t *responder, bool same)
168{
169 fr_time_t now;
170 fr_channel_t *ch;
171
172 ch = talloc_zero(ctx, fr_channel_t);
173 if (!ch) {
174 nomem:
175 fr_strerror_const("Failed allocating memory");
176 return NULL;
177 }
178
179 ch->same_thread = same;
180
183
184 ch->end[TO_RESPONDER].aq = fr_atomic_queue_talloc(ch, ATOMIC_QUEUE_SIZE, sizeof(uintptr_t));
185 if (!ch->end[TO_RESPONDER].aq) {
186 talloc_free(ch);
187 goto nomem;
188 }
189
190 ch->end[TO_REQUESTOR].aq = fr_atomic_queue_talloc(ch, ATOMIC_QUEUE_SIZE, sizeof(uintptr_t));
191 if (!ch->end[TO_REQUESTOR].aq) {
192 talloc_free(ch);
193 goto nomem;
194 }
195
196 ch->end[TO_RESPONDER].control = responder;
197 ch->end[TO_REQUESTOR].control = requestor;
198
199 /*
200 * Initialize all of the timers to now.
201 */
202 now = fr_time();
203
204 ch->end[TO_RESPONDER].stats.last_write = now;
207 atomic_store(&ch->end[TO_RESPONDER].active, true);
208
209 ch->end[TO_REQUESTOR].stats.last_write = now;
212 atomic_store(&ch->end[TO_REQUESTOR].active, true);
213
214 return ch;
215}
216
217
218/** Send a message via a kq user signal
219 *
220 * Note that the caller doesn't care about data in the event, that is
221 * sent via the atomic queue. The kevent code takes care of
222 * delivering the signal once, even if it's sent by multiple requestor
223 * threads.
224 *
225 * The thread watching the KQ knows which end it is. So when it gets
226 * the signal (and the channel pointer) it knows to look at end[0] or
227 * end[1]. We also send which end in 'which' (0, 1) to further help
228 * the recipient.
229 *
230 * @param[in] ch the channel.
231 * @param[in] when the data was ready. Typically taken from the message.
232 * @param[in] end of the channel that the message was written to.
233 * @param[in] ev the event to send.
234 * @return
235 * - <0 on error
236 * - 0 on success
237 */
239{
241
242 end->stats.last_sent_signal = when;
243 end->stats.signals++;
244
245 cc.ack = end->ack;
246 cc.ch = ch;
247 cc.reply_ms = NULL;
248
249 MPRINT("Signalling %s, with %s\n",
250 fr_table_str_by_value(channel_direction, end->direction, "<INVALID>"),
251 fr_table_str_by_value(channel_signals, ev, "<INVALID>"));
252
253 return fr_control_message_send(end->control, FR_CONTROL_EVENTSET_CHANNEL, ev, &cc, sizeof(cc));
254}
255
256#define IALPHA (8)
257#define RTT(_old, _new) fr_time_delta_wrap((fr_time_delta_unwrap(_new) + (fr_time_delta_unwrap(_old) * (IALPHA - 1))) / IALPHA)
258
259/** Send a request message into the channel
260 *
261 * The message should be initialized, other than "sequence" and "ack".
262 *
263 * This function automatically calls the recv_reply callback if there is a reply.
264 *
265 * @param[in] ch the channel to send the request on.
266 * @param[in] cd the message to send.
267 * @return
268 * - <0 on error
269 * - 0 on success
270 */
272{
273 uint64_t sequence;
274 fr_time_t when;
275 fr_time_delta_t message_interval;
276 fr_channel_end_t *requestor;
277
278 if (!fr_cond_assert_msg(atomic_load(&ch->end[TO_RESPONDER].active), "Channel not active")) return -1;
279
280 /*
281 * Same thread? Just call the "recv" function directly.
282 */
283 if (ch->same_thread) {
284 ch->end[TO_REQUESTOR].recv(ch, cd, ch->end[TO_REQUESTOR].recv_uctx);
285 return 0;
286 }
287
288 requestor = &(ch->end[TO_RESPONDER]);
289 when = cd->m.when;
290
291 sequence = requestor->sequence + 1;
292 cd->live.sequence = sequence;
293 cd->live.ack = requestor->ack;
294
295 /*
296 * Push the message onto the queue for the other end. If
297 * the push fails, the caller should try another queue.
298 */
299 if (!fr_atomic_queue_spsc_push(requestor->aq, &cd)) {
300 fr_strerror_printf("Failed pushing to atomic queue - full. Queue contains %zu items",
301 fr_atomic_queue_size(requestor->aq));
302 while (fr_channel_recv_reply(ch));
303 return -1;
304 }
305
306 requestor->sequence = sequence;
307 message_interval = fr_time_sub(when, requestor->stats.last_write);
308
309 if (!fr_time_delta_ispos(requestor->stats.message_interval)) {
310 requestor->stats.message_interval = message_interval;
311 } else {
312 requestor->stats.message_interval = RTT(requestor->stats.message_interval, message_interval);
313 }
314
315 fr_assert_msg(fr_time_lteq(requestor->stats.last_write, when),
316 "Channel data timestamp (%" PRId64") older than last channel data sent (%" PRId64 ")",
317 fr_time_unwrap(when), fr_time_unwrap(requestor->stats.last_write));
318 requestor->stats.last_write = when;
319
320 requestor->stats.outstanding++;
321 requestor->stats.packets++;
322
323 MPRINT("REQUESTOR requests %"PRIu64", num_outstanding %"PRIu64"\n", requestor->stats.packets, requestor->stats.outstanding);
324
325 /*
326 * The message is in the queue. If the responder is
327 * awake it will find it before it next bsleeps, so
328 * the signal is only needed if it is already asleep.
329 */
330 if (!fr_control_is_sleeping(requestor->control)) {
331 MPRINT("REQUESTOR SKIPS signal\n");
332 return 0;
333 }
334
335 /*
336 * Tell the other end that there is new data ready.
337 *
338 * Ignore errors on signalling. The responder already has
339 * the packet in its inbound queue, so at some point, it
340 * will pick up the message.
341 */
342 MPRINT("REQUESTOR SIGNALS\n");
344 return 0;
345}
346
347/** Receive a reply message from the channel
348 *
349 * @param[in] ch the channel to read data from.
350 * @return
351 * - true if there was a message received
352 * - false if there are no more messages
353 */
355{
357 fr_channel_end_t *requestor;
359
360 fr_assert(ch->end[TO_RESPONDER].recv != NULL);
361
362 aq = ch->end[TO_REQUESTOR].aq;
363 requestor = &(ch->end[TO_RESPONDER]);
364
365 /*
366 * It's OK for the queue to be empty.
367 */
368 if (!fr_atomic_queue_spsc_pop(aq, &cd)) return false;
369
370 /*
371 * We want an exponential moving average for round trip
372 * time, where "alpha" is a number between [0,1)
373 *
374 * RTT_new = alpha * RTT_old + (1 - alpha) * RTT_sample
375 *
376 * BUT we use fixed-point arithmetic, so we need to use inverse alpha,
377 * which works out to the following equation:
378 *
379 * RTT_new = (RTT_sample + (ialpha - 1) * RTT_old) / ialpha
380 *
381 * NAKs have zero processing time, so we ignore them for
382 * the purpose of RTT.
383 */
384 if (fr_time_delta_ispos(cd->reply.processing_time)) {
385 ch->processing_time = RTT(ch->processing_time, cd->reply.processing_time);
386 }
387 ch->cpu_time = cd->reply.cpu_time;
388
389 /*
390 * Update the outbound channel with the knowledge that
391 * we've received one more reply, and with the responders
392 * ACK.
393 */
394 fr_assert(requestor->stats.outstanding > 0);
395 fr_assert(cd->live.sequence > requestor->ack);
396 fr_assert(cd->live.sequence <= requestor->sequence); /* must have fewer replies than requests */
397
398 requestor->stats.outstanding--;
399 requestor->ack = cd->live.sequence;
400 requestor->their_view_of_my_sequence = cd->live.ack;
401
403 requestor->stats.last_read_other = cd->m.when;
404
405 ch->end[TO_RESPONDER].recv(ch, cd, ch->end[TO_RESPONDER].recv_uctx);
406
407 return true;
408}
409
410
411/** Receive a request message from the channel
412 *
413 * @param[in] ch the channel
414 * @return
415 * - true if there was a message received
416 * - false if there are no more messages
417 */
419{
421 fr_channel_end_t *responder;
423
424 aq = ch->end[TO_RESPONDER].aq;
425 responder = &(ch->end[TO_REQUESTOR]);
426
427 /*
428 * It's OK for the queue to be empty.
429 */
430 if (!fr_atomic_queue_spsc_pop(aq, &cd)) return false;
431
432 fr_assert(cd->live.sequence > responder->ack);
433 fr_assert(cd->live.sequence >= responder->sequence); /* must have more requests than replies */
434
435 responder->stats.outstanding++;
436 responder->ack = cd->live.sequence;
437 responder->their_view_of_my_sequence = cd->live.ack;
438
440 responder->stats.last_read_other = cd->m.when;
441
442 ch->end[TO_REQUESTOR].recv(ch, cd, ch->end[TO_REQUESTOR].recv_uctx);
443
444 return true;
445}
446
447/** Send a reply message into the channel
448 *
449 * The message should be initialized, other than "sequence" and "ack".
450 *
451 * @param[in] ch the channel to send the reply on.
452 * @param[in] cd the message to send
453 * @return
454 * - <0 on error
455 * - 0 on success
456 */
458{
459 uint64_t sequence;
460 fr_time_t when;
461 fr_time_delta_t message_interval;
462 fr_channel_end_t *responder;
463
464 if (!fr_cond_assert_msg(atomic_load(&ch->end[TO_REQUESTOR].active), "Channel not active")) return -1;
465
466 /*
467 * Same thread? Just call the "recv" function directly.
468 */
469 if (ch->same_thread) {
470 ch->end[TO_RESPONDER].recv(ch, cd, ch->end[TO_RESPONDER].recv_uctx);
471 return 0;
472 }
473
474 responder = &(ch->end[TO_REQUESTOR]);
475
476 when = cd->m.when;
477
478 sequence = responder->sequence + 1;
479 cd->live.sequence = sequence;
480 cd->live.ack = responder->ack;
481
482 if (!fr_atomic_queue_spsc_push(responder->aq, &cd)) {
483 fr_strerror_printf("Failed pushing to atomic queue - full. Queue contains %zu items",
484 fr_atomic_queue_size(responder->aq));
485 while (fr_channel_recv_request(ch));
486 return -1;
487 }
488
489 fr_assert(responder->stats.outstanding > 0);
490 responder->stats.outstanding--;
491 responder->stats.packets++;
492
493 MPRINT("\tRESPONDER replies %"PRIu64", num_outstanding %"PRIu64"\n", responder->stats.packets, responder->stats.outstanding);
494
495 responder->sequence = sequence;
496 message_interval = fr_time_sub(when, responder->stats.last_write);
497 if (!fr_time_delta_ispos(responder->stats.message_interval)) {
498 responder->stats.message_interval = message_interval;
499 } else {
500 responder->stats.message_interval = RTT(responder->stats.message_interval, message_interval);
501 }
502
503 fr_assert_msg(fr_time_lteq(responder->stats.last_write, when),
504 "Channel data timestamp (%" PRId64") older than last channel data sent (%" PRId64 ")",
505 fr_time_unwrap(when), fr_time_unwrap(responder->stats.last_write));
506 responder->stats.last_write = when;
507
508 /*
509 * Even if we think we have no more packets to process,
510 * the caller may have sent us one. Go check the input
511 * channel.
512 */
513 while (fr_channel_recv_request(ch));
514
515 /*
516 * The reply is in the queue. If the requestor is awake
517 * it will find it before it next blocks, so the signal is
518 * only needed if it is already asleep. The mirror of the
519 * check in fr_channel_send_request().
520 */
521 if (!fr_control_is_sleeping(responder->control)) {
522 MPRINT("\tRESPONDER SKIPS signal\n");
523 return 0;
524 }
525
526 /*
527 * No packets outstanding. Tell the requestor how far we
528 * got, so it can re-signal us if it has sent more.
529 */
530 if (responder->stats.outstanding == 0) {
532 return 0;
533 }
534
535 MPRINT("\twhen - last_read_other = %"PRIu64" - %"PRIu64" = %"PRIu64"\n", when, responder->stats.last_read_other, when - responder->stats.last_read_other);
536 MPRINT("\twhen - last signal = %"PRIu64" - %"PRIu64" = %"PRIu64"\n", when, responder->stats.last_sent_signal, when - responder->stats.last_sent_signal);
537 MPRINT("\tsequence - ack = %"PRIu64" - %"PRIu64" = %"PRIu64"\n", responder->sequence, responder->their_view_of_my_sequence, responder->sequence - responder->their_view_of_my_sequence);
538
539#ifdef __APPLE__
540 /*
541 * If we've sent them a signal since the last ACK, they
542 * will receive it, and process the packets. So we don't
543 * need to signal them again.
544 *
545 * But... this doesn't appear to work on the Linux
546 * libkqueue implementation.
547 */
548 if (responder->sequence_at_last_signal > responder->their_view_of_my_sequence) return 0;
549#endif
550
551 /*
552 * If we've received a new packet in the last while, OR
553 * we've sent a signal in the last while, then we don't
554 * need to send a new signal. But we DO send a signal if
555 * we haven't seen an ACK for a few packets.
556 *
557 * FIXME: make these limits configurable, or include
558 * predictions about packet processing time?
559 */
560 fr_assert(responder->their_view_of_my_sequence <= responder->sequence);
561#if 0
562 if (((responder->sequence - their_view_of_my_sequence) <= 1000) &&
563 ((when - responder->stats.last_read_other < SIGNAL_INTERVAL) ||
564 ((when - responder->stats.last_sent_signal) < SIGNAL_INTERVAL))) {
565 MPRINT("\tRESPONDER SKIPS signal\n");
566 return 0;
567 }
568#endif
569
570 MPRINT("\tRESPONDER SIGNALS num_outstanding %"PRIu64"\n", responder->stats.outstanding);
572 return 0;
573}
574
575
576/** Don't send a reply message into the channel
577 *
578 * The message should be the one we received from the network.
579 *
580 * @param[in] ch the channel on which we're dropping a packet
581 * @return
582 * - <0 on error
583 * - 0 on success
584 */
586{
587 fr_channel_end_t *responder;
588
589 responder = &(ch->end[TO_REQUESTOR]);
590
591 responder->sequence++;
592 return 0;
593}
594
595
596
597/** Unpack the payload of a channel control message
598 */
599static inline void channel_message_unpack(fr_channel_control_t *cc, void const *data, NDEBUG_UNUSED size_t data_size)
600{
601 fr_assert(data_size == sizeof(*cc));
602 memcpy(cc, data, sizeof(*cc));
603}
604
605/** Drain the requests queued for the responder
606 *
607 * Runs on the responder's thread, so a signal which finds nothing is
608 * counted in the responder's own stats. Each end's stats are only
609 * ever written by that end's thread.
610 */
612{
613 if (!fr_channel_recv_request(ch)) {
615 return;
616 }
617 while (fr_channel_recv_request(ch));
618}
619
620/** Drain the replies queued for the requestor
621 *
622 * Runs on the requestor's thread, see channel_drain_requests().
623 */
624static inline void channel_drain_replies(fr_channel_t *ch)
625{
626 if (!fr_channel_recv_reply(ch)) {
628 return;
629 }
630 while (fr_channel_recv_reply(ch));
631}
632
633/** The requestor has queued requests for the responder
634 *
635 * Must arrive on the responder's control plane.
636 */
637static void channel_data_to_responder(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
638{
639 fr_channel_hooks_t *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
641
642 channel_message_unpack(&cc, data, data_size);
643 MPRINT("channel got data_to_responder\n");
644
645 if (!fr_cond_assert(hooks->control == cc.ch->end[TO_RESPONDER].control)) return;
647}
648
649/** The responder has queued replies for the requestor
650 *
651 * Must arrive on the requestor's control plane.
652 */
653static void channel_data_to_requestor(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
654{
655 fr_channel_hooks_t *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
657
658 channel_message_unpack(&cc, data, data_size);
659 MPRINT("channel got data_to_requestor\n");
660
661 if (!fr_cond_assert(hooks->control == cc.ch->end[TO_REQUESTOR].control)) return;
663}
664
665/** The responder has serviced its requests, and says how many it has seen
666 *
667 * Only sent by the responder. Replies may be waiting, so drain them.
668 * First compare their ACK to the last sequence we sent, and if they
669 * have not seen our latest, signal the responder to wake up.
670 */
671static void channel_data_done_responder(void const *data, size_t data_size, fr_time_t now, void *uctx)
672{
673 fr_channel_hooks_t *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
675 fr_channel_t *ch;
676 fr_channel_end_t *requestor;
677
678 channel_message_unpack(&cc, data, data_size);
679 ch = cc.ch;
680 MPRINT("channel got data_done_responder\n");
681
682 if (!fr_cond_assert(hooks->control == ch->end[TO_REQUESTOR].control)) return;
683
684 requestor = &ch->end[TO_RESPONDER];
685
686 /*
687 * We have told this end to close, so there is nothing left to
688 * wake it for: fr_channel_send_request() refuses on an inactive
689 * end, so no more data can be queued. Signalling it anyway
690 * writes into the control plane which the responder may have
691 * already freed (this has been observed and caused a crash-on-exit).
692 *
693 * Servicing the message is still correct: it was in flight
694 * before the close, and we must keep servicing to receive the
695 * ack at all. The load is relaxed because only this thread
696 * clears "active" for this end.
697 */
698 if (unlikely(!atomic_load_explicit(&requestor->active, memory_order_relaxed))) goto drain;
699
700 /*
701 * Their ack covers everything we have sent. Anything
702 * sent later signals for itself.
703 */
704 fr_assert(cc.ack <= requestor->sequence);
705 if (cc.ack == requestor->sequence) {
706 MPRINT("REQUESTOR SKIPS signal AFTER data_done_responder num_outstanding %"PRIu64"\n",
707 requestor->stats.outstanding);
708 goto drain;
709 }
710
711 /*
712 * They have not seen our latest. Each of those sends
713 * already signalled, or was pushed while the responder was
714 * awake and so will be found by its final drain. Signal
715 * again only if it has since blocked: cheap insurance
716 * against a dropped wakeup, free when it is running.
717 */
718 if (!fr_control_is_sleeping(requestor->control)) goto drain;
719
720 requestor->stats.resignals++;
721
722 MPRINT("REQUESTOR SIGNALS AFTER data_done_responder\n");
723 if (fr_channel_data_ready(ch, now, requestor, FR_CHANNEL_CONTROL_DATA_TO_RESPONDER) < 0) return;
724
725drain:
727}
728
729/** A requestor has opened a channel to us
730 */
731static void channel_open(void const *data, size_t data_size, fr_time_t now, void *uctx)
732{
733 fr_channel_hooks_t *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
735
736 channel_message_unpack(&cc, data, data_size);
737 MPRINT("channel got open\n");
738
739 if (!fr_cond_assert(hooks->cb.open)) return;
740 hooks->cb.open(cc.ch, cc.reply_ms, now, hooks->uctx);
741}
742
743/** The requestor is closing a channel to us
744 */
745static void channel_close(void const *data, size_t data_size, fr_time_t now, void *uctx)
746{
747 fr_channel_hooks_t *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
749
750 channel_message_unpack(&cc, data, data_size);
751 MPRINT("channel got close\n");
752
753 if (!fr_cond_assert(hooks->cb.close)) return;
754 hooks->cb.close(cc.ch, now, hooks->uctx);
755}
756
757/** The responder has finished closing a channel we asked it to close
758 */
759static void channel_close_ack(void const *data, size_t data_size, fr_time_t now, void *uctx)
760{
761 fr_channel_hooks_t *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
763
764 channel_message_unpack(&cc, data, data_size);
765 MPRINT("channel got close_ack\n");
766
767 if (!fr_cond_assert(hooks->cb.closed)) return;
768 hooks->cb.closed(cc.ch, now, hooks->uctx);
769}
770
771/** Register the owner's channel callbacks on its control plane
772 *
773 * Call before fr_control_open(). Every channel event gets its
774 * own entry in the FR_CONTROL_EVENTSET_CHANNEL event set, so the control
775 * plane dispatches straight to the handler for that event.
776 *
777 * @param[in] c the control plane channels will signal.
778 * @param[in] callbacks what the owner does with each event. Copied.
779 * @param[in] uctx passed to each callback.
780 * @return
781 * - <0 on error
782 * - 0 on success
783 */
811
812
813/** Service a control-plane event.
814 *
815 * The channels use control planes for internal signaling. Note that
816 * the caller does NOT pass the channel into this function. Instead,
817 * the channel is taken from the kevent.
818 *
819 * @param[in] ch The channel to service.
820 * @param[in] c The control plane on which we received the kev.
821 * @param[in] kev The kevent data, should get passed to the control plane.
822 * @return
823 * - <0 on error
824 * - 0 on success
825 */
826int fr_channel_service_kevent(fr_channel_t *ch, fr_control_t *c, UNUSED struct kevent const *kev)
827{
828 (void) talloc_get_type_abort(ch, fr_channel_t);
829
830 if (c == ch->end[TO_RESPONDER].control) {
832 } else {
834 }
835
836 return 0;
837}
838
839
840/** Check if a channel is active.
841 *
842 * A channel may be closed by either end. If so, it stays alive (but
843 * inactive) until both ends acknowledge the close.
844 *
845 * @param[in] ch the channel
846 * @return
847 * - false the channel is closing.
848 * - true the channel is active
849 */
854
855/** Signal a responder that the channel is closing
856 *
857 * @param[in] ch The channel.
858 * @return
859 * - <0 on error
860 * - 0 on success
861 */
863{
864 int ret;
865 bool active;
867
868 active = atomic_load(&ch->end[TO_RESPONDER].active);
869 if (!active) return 0; /* Already signalled to close */
870
871 atomic_store(&ch->end[TO_RESPONDER].active, false); /* Prevent further requests */
872
873 (void) talloc_get_type_abort(ch, fr_channel_t);
874
875 cc.ack = 0;
876 cc.ch = ch;
877 cc.reply_ms = NULL;
878
880 FR_CHANNEL_CONTROL_CLOSE, &cc, sizeof(cc));
881
882 return ret;
883}
884
885/** Discard any requests the requestor queued but we never received
886 *
887 * The messages belong to the requestor's message set, and it cannot reclaim them
888 * until they are marked done. A responder that closes with requests still in
889 * the queue would strand them there, and with them the memory they were
890 * allocated from.
891 *
892 * @param[in] ch to discard the queued requests of.
893 * @return the number of requests discarded.
894 */
896{
899 unsigned int num = 0;
900
901 while (fr_atomic_queue_spsc_pop(aq, &cd)) {
902 fr_message_done(&cd->m);
903 num++;
904 }
905
906 return num;
907}
908
909/** Acknowledge that the channel is closing
910 *
911 * @param[in] ch The channel.
912 * @return
913 * - <0 on error
914 * - 0 on success
915 */
917{
918 int ret;
919 bool active;
920
922
923 active = atomic_load(&ch->end[TO_REQUESTOR].active);
924 if (!active) return 0; /* Already signalled to close */
925
926 atomic_store(&ch->end[TO_REQUESTOR].active, false); /* Prevent further responses */
927
928 (void) talloc_get_type_abort(ch, fr_channel_t);
929
930 cc.ack = 0;
931 cc.ch = ch;
932 cc.reply_ms = NULL;
933
935 FR_CHANNEL_CONTROL_CLOSE_ACK, &cc, sizeof(cc));
936
937 return ret;
938}
939
940/** Add responder-specific data to a channel
941 *
942 * @param[in] ch The channel.
943 * @param[in] uctx The context to add.
944 */
946{
947 (void) talloc_get_type_abort(ch, fr_channel_t);
948
949 ch->end[TO_REQUESTOR].uctx = uctx;
950}
951
952
953/** Get responder-specific data from a channel
954 *
955 * @param[in] ch The channel.
956 */
958{
959 (void) talloc_get_type_abort(ch, fr_channel_t);
960
961 return ch->end[TO_REQUESTOR].uctx;
962}
963
964
965/** Add network-specific data to a channel
966 *
967 * @param[in] ch The channel.
968 * @param[in] uctx The context to add.
969 */
971{
972 (void) talloc_get_type_abort(ch, fr_channel_t);
973
974 ch->end[TO_RESPONDER].uctx = uctx;
975}
976
977
978/** Get network-specific data from a channel
979 *
980 * @param[in] ch The channel.
981 */
983{
984 (void) talloc_get_type_abort(ch, fr_channel_t);
985
986 return ch->end[TO_RESPONDER].uctx;
987}
988
989
991{
992 ch->end[TO_RESPONDER].recv = recv_reply;
993 ch->end[TO_RESPONDER].recv_uctx = uctx;
994
995 return 0;
996}
997
999{
1000 ch->end[TO_REQUESTOR].recv = recv_request;
1001 ch->end[TO_REQUESTOR].recv_uctx = uctx;
1002 return 0;
1003}
1004
1005/** Send a channel to a responder
1006 *
1007 * @param[in] ch The channel.
1008 * @param[in] reply_ms The message set the responder allocates replies from.
1009 * @return
1010 * - <0 on error
1011 * - 0 on success
1012 */
1014{
1016
1017 cc.ack = 0;
1018 cc.ch = ch;
1019 cc.reply_ms = reply_ms;
1020
1022 FR_CHANNEL_CONTROL_OPEN, &cc, sizeof(cc));
1023}
1024
1025void fr_channel_stats_log(fr_channel_t const *ch, fr_log_t const *log, char const *file, int line)
1026{
1027 fr_log(log, L_INFO, file, line, "requestor\n");
1028 fr_log(log, L_INFO, file, line, "\tsignals sent = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.signals);
1029 fr_log(log, L_INFO, file, line, "\tsignals re-sent = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.resignals);
1030 fr_log(log, L_INFO, file, line, "\tsignals received empty = %" PRIu64 "\n",
1032 fr_log(log, L_INFO, file, line, "\tkevents checked = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.kevents);
1033 fr_log(log, L_INFO, file, line, "\toutstanding = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.outstanding);
1034 fr_log(log, L_INFO, file, line, "\tpackets processed = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.packets);
1035 fr_log(log, L_INFO, file, line, "\tmessage interval (RTT) = %" PRId64 "\n", fr_time_delta_unwrap(ch->end[TO_RESPONDER].stats.message_interval));
1036 fr_log(log, L_INFO, file, line, "\tlast write = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_RESPONDER].stats.last_write));
1037 fr_log(log, L_INFO, file, line, "\tlast read other end = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_RESPONDER].stats.last_read_other));
1038 fr_log(log, L_INFO, file, line, "\tlast signal other = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_RESPONDER].stats.last_sent_signal));
1039
1040 fr_log(log, L_INFO, file, line, "responder\n");
1041 fr_log(log, L_INFO, file, line, "\tsignals sent = %" PRIu64"\n", ch->end[TO_REQUESTOR].stats.signals);
1042 fr_log(log, L_INFO, file, line, "\tsignals received empty = %" PRIu64 "\n",
1044 fr_log(log, L_INFO, file, line, "\tkevents checked = %" PRIu64 "\n", ch->end[TO_REQUESTOR].stats.kevents);
1045 fr_log(log, L_INFO, file, line, "\tpackets processed = %" PRIu64 "\n", ch->end[TO_REQUESTOR].stats.packets);
1046 fr_log(log, L_INFO, file, line, "\tmessage interval (RTT) = %" PRId64 "\n", fr_time_delta_unwrap(ch->end[TO_REQUESTOR].stats.message_interval));
1047 fr_log(log, L_INFO, file, line, "\tlast write = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_REQUESTOR].stats.last_write));
1048 fr_log(log, L_INFO, file, line, "\tlast read other end = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_REQUESTOR].stats.last_read_other));
1049 fr_log(log, L_INFO, file, line, "\tlast signal other = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_REQUESTOR].stats.last_sent_signal));
1050}
1051
1052/** Statistics for the requestor's end of a channel
1053 *
1054 * Signals the requestor sent, and signals it received which found nothing.
1055 */
1057{
1058 return &ch->end[TO_RESPONDER].stats;
1059}
1060
1061/** Statistics for the responder's end of a channel
1062 */
1064{
1065 return &ch->end[TO_REQUESTOR].stats;
1066}
int const char * file
Definition acutest.h:855
void int line
Definition acutest.h:810
size_t fr_atomic_queue_size(fr_atomic_queue_t *aq)
bool fr_atomic_queue_spsc_push(fr_atomic_queue_t *aq, void const *data)
Copy an element into the atomic queue, one producer only.
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t num, size_t elem_len)
Create a fixed-size atomic queue.
bool fr_atomic_queue_spsc_pop(fr_atomic_queue_t *aq, void *data)
Copy an element out of the atomic queue, one consumer only.
Structure to hold the atomic queue.
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define NDEBUG_UNUSED
Definition build.h:426
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
static void channel_message_unpack(fr_channel_control_t *cc, void const *data, NDEBUG_UNUSED size_t data_size)
Unpack the payload of a channel control message.
Definition channel.c:599
fr_channel_direction_t
Definition channel.c:45
@ TO_RESPONDER
Definition channel.c:46
@ TO_REQUESTOR
Definition channel.c:47
fr_atomic_queue_t * aq
The queue of messages - visible only to this channel.
Definition channel.c:115
atomic_bool active
Whether the channel is active.
Definition channel.c:117
fr_control_t * control
the control plane these were registered on
Definition channel.c:88
#define MPRINT(...)
Definition channel.c:42
void * fr_channel_requestor_uctx_get(fr_channel_t *ch)
Get network-specific data from a channel.
Definition channel.c:982
uint64_t sequence_at_last_signal
When we last signaled.
Definition channel.c:113
fr_channel_stats_t const * fr_channel_responder_stats(fr_channel_t const *ch)
Statistics for the responder's end of a channel.
Definition channel.c:1063
fr_message_set_t * reply_ms
the reply message set, for open only.
Definition channel.c:82
uint64_t sequence
Sequence number for this channel.
Definition channel.c:109
fr_table_num_sorted_t const channel_signals[]
Definition channel.c:138
static void channel_drain_replies(fr_channel_t *ch)
Drain the replies queued for the requestor.
Definition channel.c:624
bool fr_channel_recv_reply(fr_channel_t *ch)
Receive a reply message from the channel.
Definition channel.c:354
fr_channel_direction_t direction
Use for debug messages.
Definition channel.c:100
unsigned int fr_channel_responder_discard(fr_channel_t *ch)
Discard any requests the requestor queued but we never received.
Definition channel.c:895
size_t channel_signals_len
Definition channel.c:146
#define RTT(_old, _new)
Definition channel.c:257
void * uctx
Worker context.
Definition channel.c:104
size_t channel_packet_priority_len
Definition channel.c:154
int fr_channel_signal_open(fr_channel_t *ch, fr_message_set_t *reply_ms)
Send a channel to a responder.
Definition channel.c:1013
static void channel_data_done_responder(void const *data, size_t data_size, fr_time_t now, void *uctx)
The responder has serviced its requests, and says how many it has seen.
Definition channel.c:671
int fr_channel_signal_responder_close(fr_channel_t *ch)
Signal a responder that the channel is closing.
Definition channel.c:862
int fr_channel_send_request(fr_channel_t *ch, fr_channel_data_t *cd)
Send a request message into the channel.
Definition channel.c:271
fr_channel_t * fr_channel_create(TALLOC_CTX *ctx, fr_control_t *requestor, fr_control_t *responder, bool same)
Create a new channel.
Definition channel.c:167
static int fr_channel_data_ready(fr_channel_t *ch, fr_time_t when, fr_channel_end_t *end, fr_channel_control_event_t ev)
Send a message via a kq user signal.
Definition channel.c:238
fr_table_num_sorted_t const channel_packet_priority[]
Definition channel.c:148
fr_channel_stats_t const * fr_channel_requestor_stats(fr_channel_t const *ch)
Statistics for the requestor's end of a channel.
Definition channel.c:1056
static void channel_close_ack(void const *data, size_t data_size, fr_time_t now, void *uctx)
The responder has finished closing a channel we asked it to close.
Definition channel.c:759
#define ATOMIC_QUEUE_SIZE
Size of the atomic queues.
Definition channel.c:73
int fr_channel_callbacks_add(fr_control_t *c, fr_channel_callbacks_t const *callbacks, void *uctx)
Register the owner's channel callbacks on its control plane.
Definition channel.c:784
static void channel_drain_requests(fr_channel_t *ch)
Drain the requests queued for the responder.
Definition channel.c:611
fr_channel_stats_t stats
channel statistics
Definition channel.c:119
fr_control_t * control
The control plane we signal this end through.
Definition channel.c:102
static void channel_open(void const *data, size_t data_size, fr_time_t now, void *uctx)
A requestor has opened a channel to us.
Definition channel.c:731
static void channel_close(void const *data, size_t data_size, fr_time_t now, void *uctx)
The requestor is closing a channel to us.
Definition channel.c:745
bool same_thread
are both ends in the same thread?
Definition channel.c:133
static void channel_data_to_responder(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
The requestor has queued requests for the responder.
Definition channel.c:637
int fr_channel_set_recv_reply(fr_channel_t *ch, fr_channel_recv_callback_t recv_reply, void *uctx)
Definition channel.c:990
void * uctx
passed to each callback
Definition channel.c:90
uint64_t ack
the sender's ack sequence, for data done.
Definition channel.c:80
void * fr_channel_responder_uctx_get(fr_channel_t *ch)
Get responder-specific data from a channel.
Definition channel.c:957
bool fr_channel_recv_request(fr_channel_t *ch)
Receive a request message from the channel.
Definition channel.c:418
int fr_channel_null_reply(fr_channel_t *ch)
Don't send a reply message into the channel.
Definition channel.c:585
void fr_channel_requestor_uctx_add(fr_channel_t *ch, void *uctx)
Add network-specific data to a channel.
Definition channel.c:970
fr_time_delta_t cpu_time
Total time used by the responder for this channel.
Definition channel.c:130
fr_channel_callbacks_t cb
what the owner does with each event
Definition channel.c:89
int fr_channel_service_kevent(fr_channel_t *ch, fr_control_t *c, UNUSED struct kevent const *kev)
Service a control-plane event.
Definition channel.c:826
void fr_channel_responder_uctx_add(fr_channel_t *ch, void *uctx)
Add responder-specific data to a channel.
Definition channel.c:945
int fr_channel_set_recv_request(fr_channel_t *ch, fr_channel_recv_callback_t recv_request, void *uctx)
Definition channel.c:998
int fr_channel_send_reply(fr_channel_t *ch, fr_channel_data_t *cd)
Send a reply message into the channel.
Definition channel.c:457
static void channel_data_to_requestor(void const *data, size_t data_size, UNUSED fr_time_t now, void *uctx)
The responder has queued replies for the requestor.
Definition channel.c:653
fr_channel_end_t end[2]
Two ends of the channel.
Definition channel.c:135
void fr_channel_stats_log(fr_channel_t const *ch, fr_log_t const *log, char const *file, int line)
Definition channel.c:1025
fr_channel_t * ch
the channel
Definition channel.c:81
uint64_t their_view_of_my_sequence
Should be clear.
Definition channel.c:111
bool fr_channel_active(fr_channel_t *ch)
Check if a channel is active.
Definition channel.c:850
fr_time_delta_t processing_time
Time spent by the responder processing requests.
Definition channel.c:131
int fr_channel_responder_ack_close(fr_channel_t *ch)
Acknowledge that the channel is closing.
Definition channel.c:916
uint64_t ack
Sequence number of the other end.
Definition channel.c:110
fr_channel_recv_callback_t recv
callback for receiving messages
Definition channel.c:106
void * recv_uctx
context for receiving messages
Definition channel.c:107
The payload of every channel control message.
Definition channel.c:79
One end of a channel.
Definition channel.c:99
The callbacks one control plane's owner registered for channel events.
Definition channel.c:87
A full channel, which consists of two ends.
Definition channel.c:129
fr_message_t m
the message header
Definition channel.h:112
fr_time_delta_t message_interval
Interval between messages.
Definition channel.h:99
#define PRIORITY_NORMAL
Definition channel.h:158
uint64_t packets
Number of actual data packets.
Definition channel.h:93
fr_channel_control_event_t
Events channels send, in the FR_CONTROL_EVENTSET_CHANNEL event set.
Definition channel.h:74
@ FR_CHANNEL_CONTROL_OPEN
A requestor has opened a channel to the responder.
Definition channel.h:79
@ FR_CHANNEL_CONTROL_DATA_TO_REQUESTOR
Replies are queued for the requestor.
Definition channel.h:76
@ FR_CHANNEL_CONTROL_DATA_DONE_RESPONDER
The responder has serviced its requests, and says how many it has seen.
Definition channel.h:77
@ FR_CHANNEL_CONTROL_CLOSE_ACK
The responder has finished closing it.
Definition channel.h:81
@ FR_CHANNEL_CONTROL_CLOSE
The requestor is closing the channel.
Definition channel.h:80
@ FR_CHANNEL_CONTROL_DATA_TO_RESPONDER
Requests are queued for the responder.
Definition channel.h:75
fr_channel_close_t close
Definition channel.h:220
fr_channel_open_t open
Definition channel.h:219
uint64_t resignals
Number of signals resent.
Definition channel.h:90
void(* fr_channel_recv_callback_t)(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
Definition channel.h:176
uint64_t outstanding
Number of outstanding requests with no reply.
Definition channel.h:88
fr_time_t last_sent_signal
The last time when we signaled the other end.
Definition channel.h:101
#define PRIORITY_NOW
Definition channel.h:156
fr_time_t last_read_other
Last time we successfully read a message from the other the channel.
Definition channel.h:98
fr_channel_closed_t closed
Definition channel.h:221
#define PRIORITY_HIGH
Definition channel.h:157
fr_time_t last_write
Last write to the channel.
Definition channel.h:97
uint64_t kevents
Number of times we've looked at kevents.
Definition channel.h:95
uint64_t signals
Number of kevent signals we've sent.
Definition channel.h:89
#define PRIORITY_LOW
Definition channel.h:159
uint64_t spurious
Number of signals we received which found no data waiting.
Definition channel.h:91
What the owner of a control plane does with channel lifecycle events.
Definition channel.h:218
Channel information which is added to a message.
Definition channel.h:111
Statistics for the channel.
Definition channel.h:87
@ FR_CONTROL_EVENTSET_CHANNEL
Events channels define.
Definition control.h:65
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:248
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:194
#define MEM(x)
Definition debug.h:38
talloc_free(hp)
bool fr_control_is_sleeping(fr_control_t *c)
Check whether the reader of this control plane is blocked.
Definition control.c:875
int fr_control_callback_add(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, fr_control_callback_t callback, void *uctx)
Register a callback for a message.
Definition control.c:659
int fr_control_message_send(fr_control_t *c, fr_control_eventset_t es, uint16_t ev, void const *data, size_t data_size)
Send a control-plane message.
Definition control.c:544
The control structure.
Definition control.c:211
#define fr_time()
Definition event.c:58
#define fr_log(_log, _lvl, _file, _line, _fmt,...)
Definition log.h:172
@ L_INFO
Informational message.
Definition log.h:52
unsigned char uint8_t
int fr_message_done(fr_message_t *m)
Mark a message as done.
Definition message.c:195
A Message set, composed of message headers and ring buffer data.
Definition message.c:94
fr_time_t when
when this message was sent
Definition message.h:47
#define fr_assert(_expr)
Definition rad_assert.h:37
#define atomic_store(object, desired)
Definition stdatomic.h:345
@ memory_order_relaxed
Definition stdatomic.h:127
#define atomic_load_explicit(object, order)
Definition stdatomic.h:312
#define atomic_load(object)
Definition stdatomic.h:343
Definition log.h:93
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
static int64_t fr_time_unwrap(fr_time_t time)
Definition time.h:146
#define fr_time_lteq(_a, _b)
Definition time.h:240
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1366