Coverage Report

Created: 2026-11-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/io/channel.c
Line
Count
Source
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
 */
25
RCSID("$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
45
typedef enum : uint8_t {
46
  TO_RESPONDER = 0,
47
  TO_REQUESTOR = 1
48
} fr_channel_direction_t;
49
50
#ifdef DEBUG_CHANNEL
51
static fr_table_num_sorted_t const channel_direction[] = {
52
  { L("to responder"),  TO_RESPONDER },
53
  { L("to requestor"),  TO_REQUESTOR },
54
};
55
size_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
0
#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
 */
79
typedef 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.
83
} fr_channel_control_t;
84
85
/** The callbacks one control plane's owner registered for channel events
86
 */
87
typedef 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
91
} fr_channel_hooks_t;
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
 */
99
typedef 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
120
} fr_channel_end_t;
121
122
typedef struct fr_channel_s fr_channel_t;
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
 */
129
struct fr_channel_s {
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
138
fr_table_num_sorted_t const channel_signals[] = {
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
};
146
size_t channel_signals_len = NUM_ELEMENTS(channel_signals);
147
148
fr_table_num_sorted_t const channel_packet_priority[] = {
149
  { L("high"),  PRIORITY_HIGH   },
150
  { L("low"), PRIORITY_LOW    },
151
  { L("normal"),  PRIORITY_NORMAL   },
152
  { L("now"), PRIORITY_NOW    }
153
};
154
size_t channel_packet_priority_len = NUM_ELEMENTS(channel_packet_priority);
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
 */
167
fr_channel_t *fr_channel_create(TALLOC_CTX *ctx, fr_control_t *requestor, fr_control_t *responder, bool same)
168
0
{
169
0
  fr_time_t now;
170
0
  fr_channel_t *ch;
171
172
0
  ch = talloc_zero(ctx, fr_channel_t);
173
0
  if (!ch) {
174
0
  nomem:
175
0
    fr_strerror_const("Failed allocating memory");
176
0
    return NULL;
177
0
  }
178
179
0
  ch->same_thread = same;
180
181
0
  ch->end[TO_RESPONDER].direction = TO_RESPONDER;
182
0
  ch->end[TO_REQUESTOR].direction = TO_REQUESTOR;
183
184
0
  ch->end[TO_RESPONDER].aq = fr_atomic_queue_talloc(ch, ATOMIC_QUEUE_SIZE, sizeof(uintptr_t));
185
0
  if (!ch->end[TO_RESPONDER].aq) {
186
0
    talloc_free(ch);
187
0
    goto nomem;
188
0
  }
189
190
0
  ch->end[TO_REQUESTOR].aq = fr_atomic_queue_talloc(ch, ATOMIC_QUEUE_SIZE, sizeof(uintptr_t));
191
0
  if (!ch->end[TO_REQUESTOR].aq) {
192
0
    talloc_free(ch);
193
0
    goto nomem;
194
0
  }
195
196
0
  ch->end[TO_RESPONDER].control = responder;
197
0
  ch->end[TO_REQUESTOR].control = requestor;
198
199
  /*
200
   *  Initialize all of the timers to now.
201
   */
202
0
  now = fr_time();
203
204
0
  ch->end[TO_RESPONDER].stats.last_write = now;
205
0
  ch->end[TO_RESPONDER].stats.last_read_other = now;
206
0
  ch->end[TO_RESPONDER].stats.last_sent_signal = now;
207
0
  atomic_store(&ch->end[TO_RESPONDER].active, true);
208
209
0
  ch->end[TO_REQUESTOR].stats.last_write = now;
210
0
  ch->end[TO_REQUESTOR].stats.last_read_other = now;
211
0
  ch->end[TO_REQUESTOR].stats.last_sent_signal = now;
212
0
  atomic_store(&ch->end[TO_REQUESTOR].active, true);
213
214
0
  return ch;
215
0
}
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
 */
238
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)
239
0
{
240
0
  fr_channel_control_t cc;
241
242
0
  end->stats.last_sent_signal = when;
243
0
  end->stats.signals++;
244
245
0
  cc.ack = end->ack;
246
0
  cc.ch = ch;
247
0
  cc.reply_ms = NULL;
248
249
0
  MPRINT("Signalling %s, with %s\n",
250
0
         fr_table_str_by_value(channel_direction, end->direction, "<INVALID>"),
251
0
         fr_table_str_by_value(channel_signals, ev, "<INVALID>"));
252
253
0
  return fr_control_message_send(end->control, FR_CONTROL_EVENTSET_CHANNEL, ev, &cc, sizeof(cc));
254
0
}
255
256
#define IALPHA (8)
257
0
#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
 */
271
int fr_channel_send_request(fr_channel_t *ch, fr_channel_data_t *cd)
272
0
{
273
0
  uint64_t sequence;
274
0
  fr_time_t when;
275
0
  fr_time_delta_t message_interval;
276
0
  fr_channel_end_t *requestor;
277
278
0
  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
0
  if (ch->same_thread) {
284
0
    ch->end[TO_REQUESTOR].recv(ch, cd, ch->end[TO_REQUESTOR].recv_uctx);
285
0
    return 0;
286
0
  }
287
288
0
  requestor = &(ch->end[TO_RESPONDER]);
289
0
  when = cd->m.when;
290
291
0
  sequence = requestor->sequence + 1;
292
0
  cd->live.sequence = sequence;
293
0
  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
0
  if (!fr_atomic_queue_spsc_push(requestor->aq, &cd)) {
300
0
    fr_strerror_printf("Failed pushing to atomic queue - full.  Queue contains %zu items",
301
0
           fr_atomic_queue_size(requestor->aq));
302
0
    while (fr_channel_recv_reply(ch));
303
0
    return -1;
304
0
  }
305
306
0
  requestor->sequence = sequence;
307
0
  message_interval = fr_time_sub(when, requestor->stats.last_write);
308
309
0
  if (!fr_time_delta_ispos(requestor->stats.message_interval)) {
310
0
    requestor->stats.message_interval = message_interval;
311
0
  } else {
312
0
    requestor->stats.message_interval = RTT(requestor->stats.message_interval, message_interval);
313
0
  }
314
315
0
  fr_assert_msg(fr_time_lteq(requestor->stats.last_write, when),
316
0
          "Channel data timestamp (%" PRId64") older than last channel data sent (%" PRId64 ")",
317
0
          fr_time_unwrap(when), fr_time_unwrap(requestor->stats.last_write));
318
0
  requestor->stats.last_write = when;
319
320
0
  requestor->stats.outstanding++;
321
0
  requestor->stats.packets++;
322
323
0
  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
0
  if (!fr_control_is_sleeping(requestor->control)) {
331
0
    MPRINT("REQUESTOR SKIPS signal\n");
332
0
    return 0;
333
0
  }
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
0
  MPRINT("REQUESTOR SIGNALS\n");
343
0
  (void) fr_channel_data_ready(ch, when, requestor, FR_CHANNEL_CONTROL_DATA_TO_RESPONDER);
344
0
  return 0;
345
0
}
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
 */
354
bool fr_channel_recv_reply(fr_channel_t *ch)
355
0
{
356
0
  fr_channel_data_t *cd;
357
0
  fr_channel_end_t *requestor;
358
0
  fr_atomic_queue_t *aq;
359
360
0
  fr_assert(ch->end[TO_RESPONDER].recv != NULL);
361
362
0
  aq = ch->end[TO_REQUESTOR].aq;
363
0
  requestor = &(ch->end[TO_RESPONDER]);
364
365
  /*
366
   *  It's OK for the queue to be empty.
367
   */
368
0
  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
0
  if (fr_time_delta_ispos(cd->reply.processing_time)) {
385
0
    ch->processing_time = RTT(ch->processing_time, cd->reply.processing_time);
386
0
  }
387
0
  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
0
  fr_assert(requestor->stats.outstanding > 0);
395
0
  fr_assert(cd->live.sequence > requestor->ack);
396
0
  fr_assert(cd->live.sequence <= requestor->sequence); /* must have fewer replies than requests */
397
398
0
  requestor->stats.outstanding--;
399
0
  requestor->ack = cd->live.sequence;
400
0
  requestor->their_view_of_my_sequence = cd->live.ack;
401
402
0
  fr_assert(fr_time_lteq(requestor->stats.last_read_other, cd->m.when));
403
0
  requestor->stats.last_read_other = cd->m.when;
404
405
0
  ch->end[TO_RESPONDER].recv(ch, cd, ch->end[TO_RESPONDER].recv_uctx);
406
407
0
  return true;
408
0
}
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
 */
418
bool fr_channel_recv_request(fr_channel_t *ch)
419
0
{
420
0
  fr_channel_data_t *cd;
421
0
  fr_channel_end_t *responder;
422
0
  fr_atomic_queue_t *aq;
423
424
0
  aq = ch->end[TO_RESPONDER].aq;
425
0
  responder = &(ch->end[TO_REQUESTOR]);
426
427
  /*
428
   *  It's OK for the queue to be empty.
429
   */
430
0
  if (!fr_atomic_queue_spsc_pop(aq, &cd)) return false;
431
432
0
  fr_assert(cd->live.sequence > responder->ack);
433
0
  fr_assert(cd->live.sequence >= responder->sequence); /* must have more requests than replies */
434
435
0
  responder->stats.outstanding++;
436
0
  responder->ack = cd->live.sequence;
437
0
  responder->their_view_of_my_sequence = cd->live.ack;
438
439
0
  fr_assert(fr_time_lteq(responder->stats.last_read_other, cd->m.when));
440
0
  responder->stats.last_read_other = cd->m.when;
441
442
0
  ch->end[TO_REQUESTOR].recv(ch, cd, ch->end[TO_REQUESTOR].recv_uctx);
443
444
0
  return true;
445
0
}
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
 */
457
int fr_channel_send_reply(fr_channel_t *ch, fr_channel_data_t *cd)
458
0
{
459
0
  uint64_t    sequence;
460
0
  fr_time_t   when;
461
0
  fr_time_delta_t   message_interval;
462
0
  fr_channel_end_t  *responder;
463
464
0
  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
0
  if (ch->same_thread) {
470
0
    ch->end[TO_RESPONDER].recv(ch, cd, ch->end[TO_RESPONDER].recv_uctx);
471
0
    return 0;
472
0
  }
473
474
0
  responder = &(ch->end[TO_REQUESTOR]);
475
476
0
  when = cd->m.when;
477
478
0
  sequence = responder->sequence + 1;
479
0
  cd->live.sequence = sequence;
480
0
  cd->live.ack = responder->ack;
481
482
0
  if (!fr_atomic_queue_spsc_push(responder->aq, &cd)) {
483
0
    fr_strerror_printf("Failed pushing to atomic queue - full.  Queue contains %zu items",
484
0
           fr_atomic_queue_size(responder->aq));
485
0
    while (fr_channel_recv_request(ch));
486
0
    return -1;
487
0
  }
488
489
0
  fr_assert(responder->stats.outstanding > 0);
490
0
  responder->stats.outstanding--;
491
0
  responder->stats.packets++;
492
493
0
  MPRINT("\tRESPONDER replies %"PRIu64", num_outstanding %"PRIu64"\n", responder->stats.packets, responder->stats.outstanding);
494
495
0
  responder->sequence = sequence;
496
0
  message_interval = fr_time_sub(when, responder->stats.last_write);
497
0
  if (!fr_time_delta_ispos(responder->stats.message_interval)) {
498
0
    responder->stats.message_interval = message_interval;
499
0
  } else {
500
0
    responder->stats.message_interval = RTT(responder->stats.message_interval, message_interval);
501
0
  }
502
503
0
  fr_assert_msg(fr_time_lteq(responder->stats.last_write, when),
504
0
          "Channel data timestamp (%" PRId64") older than last channel data sent (%" PRId64 ")",
505
0
          fr_time_unwrap(when), fr_time_unwrap(responder->stats.last_write));
506
0
  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
0
  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
0
  if (!fr_control_is_sleeping(responder->control)) {
522
0
    MPRINT("\tRESPONDER SKIPS signal\n");
523
0
    return 0;
524
0
  }
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
0
  if (responder->stats.outstanding == 0) {
531
0
    (void) fr_channel_data_ready(ch, when, responder, FR_CHANNEL_CONTROL_DATA_DONE_RESPONDER);
532
0
    return 0;
533
0
  }
534
535
0
  MPRINT("\twhen - last_read_other = %"PRIu64" - %"PRIu64" = %"PRIu64"\n", when, responder->stats.last_read_other, when - responder->stats.last_read_other);
536
0
  MPRINT("\twhen - last signal = %"PRIu64" - %"PRIu64" = %"PRIu64"\n", when, responder->stats.last_sent_signal, when - responder->stats.last_sent_signal);
537
0
  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
0
  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
0
  MPRINT("\tRESPONDER SIGNALS num_outstanding %"PRIu64"\n", responder->stats.outstanding);
571
0
  (void) fr_channel_data_ready(ch, when, responder, FR_CHANNEL_CONTROL_DATA_TO_REQUESTOR);
572
0
  return 0;
573
0
}
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
 */
585
int fr_channel_null_reply(fr_channel_t *ch)
586
0
{
587
0
  fr_channel_end_t *responder;
588
589
0
  responder = &(ch->end[TO_REQUESTOR]);
590
591
0
  responder->sequence++;
592
0
  return 0;
593
0
}
594
595
596
597
/** Unpack the payload of a channel control message
598
 */
599
static inline void channel_message_unpack(fr_channel_control_t *cc, void const *data, NDEBUG_UNUSED size_t data_size)
600
0
{
601
0
  fr_assert(data_size == sizeof(*cc));
602
0
  memcpy(cc, data, sizeof(*cc));
603
0
}
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
 */
611
static inline void channel_drain_requests(fr_channel_t *ch)
612
0
{
613
0
  if (!fr_channel_recv_request(ch)) {
614
0
    ch->end[TO_REQUESTOR].stats.spurious++;
615
0
    return;
616
0
  }
617
0
  while (fr_channel_recv_request(ch));
618
0
}
619
620
/** Drain the replies queued for the requestor
621
 *
622
 * Runs on the requestor's thread, see channel_drain_requests().
623
 */
624
static inline void channel_drain_replies(fr_channel_t *ch)
625
0
{
626
0
  if (!fr_channel_recv_reply(ch)) {
627
0
    ch->end[TO_RESPONDER].stats.spurious++;
628
0
    return;
629
0
  }
630
0
  while (fr_channel_recv_reply(ch));
631
0
}
632
633
/** The requestor has queued requests for the responder
634
 *
635
 * Must arrive on the responder's control plane.
636
 */
637
static 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);
640
  fr_channel_control_t  cc;
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;
646
  channel_drain_requests(cc.ch);
647
}
648
649
/** The responder has queued replies for the requestor
650
 *
651
 * Must arrive on the requestor's control plane.
652
 */
653
static 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);
656
  fr_channel_control_t  cc;
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;
662
  channel_drain_replies(cc.ch);
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
 */
671
static void channel_data_done_responder(void const *data, size_t data_size, fr_time_t now, void *uctx)
672
0
{
673
0
  fr_channel_hooks_t  *hooks = talloc_get_type_abort(uctx, fr_channel_hooks_t);
674
0
  fr_channel_control_t  cc;
675
0
  fr_channel_t    *ch;
676
0
  fr_channel_end_t  *requestor;
677
678
0
  channel_message_unpack(&cc, data, data_size);
679
0
  ch = cc.ch;
680
0
  MPRINT("channel got data_done_responder\n");
681
682
0
  if (!fr_cond_assert(hooks->control == ch->end[TO_REQUESTOR].control)) return;
683
684
0
  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
0
  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
0
  fr_assert(cc.ack <= requestor->sequence);
705
0
  if (cc.ack == requestor->sequence) {
706
0
    MPRINT("REQUESTOR SKIPS signal AFTER data_done_responder num_outstanding %"PRIu64"\n",
707
0
           requestor->stats.outstanding);
708
0
    goto drain;
709
0
  }
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
0
  if (!fr_control_is_sleeping(requestor->control)) goto drain;
719
720
0
  requestor->stats.resignals++;
721
722
0
  MPRINT("REQUESTOR SIGNALS AFTER data_done_responder\n");
723
0
  if (fr_channel_data_ready(ch, now, requestor, FR_CHANNEL_CONTROL_DATA_TO_RESPONDER) < 0) return;
724
725
0
drain:
726
0
  channel_drain_replies(ch);
727
0
}
728
729
/** A requestor has opened a channel to us
730
 */
731
static 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);
734
  fr_channel_control_t  cc;
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
 */
745
static 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);
748
  fr_channel_control_t  cc;
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
 */
759
static 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);
762
  fr_channel_control_t  cc;
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
 */
784
int fr_channel_callbacks_add(fr_control_t *c, fr_channel_callbacks_t const *callbacks, void *uctx)
785
0
{
786
0
  fr_channel_hooks_t *hooks;
787
788
0
  MEM(hooks = talloc_zero(c, fr_channel_hooks_t));
789
0
  hooks->control = c;
790
0
  hooks->cb = *callbacks;
791
0
  hooks->uctx = uctx;
792
793
0
  if ((fr_control_callback_add(c, FR_CONTROL_EVENTSET_CHANNEL, FR_CHANNEL_CONTROL_DATA_TO_RESPONDER,
794
0
             channel_data_to_responder, hooks) < 0) ||
795
0
      (fr_control_callback_add(c, FR_CONTROL_EVENTSET_CHANNEL, FR_CHANNEL_CONTROL_DATA_TO_REQUESTOR,
796
0
             channel_data_to_requestor, hooks) < 0) ||
797
0
      (fr_control_callback_add(c, FR_CONTROL_EVENTSET_CHANNEL, FR_CHANNEL_CONTROL_DATA_DONE_RESPONDER,
798
0
             channel_data_done_responder, hooks) < 0) ||
799
0
      (fr_control_callback_add(c, FR_CONTROL_EVENTSET_CHANNEL, FR_CHANNEL_CONTROL_OPEN,
800
0
             channel_open, hooks) < 0) ||
801
0
      (fr_control_callback_add(c, FR_CONTROL_EVENTSET_CHANNEL, FR_CHANNEL_CONTROL_CLOSE,
802
0
             channel_close, hooks) < 0) ||
803
0
      (fr_control_callback_add(c, FR_CONTROL_EVENTSET_CHANNEL, FR_CHANNEL_CONTROL_CLOSE_ACK,
804
0
             channel_close_ack, hooks) < 0)) {
805
0
    talloc_free(hooks);
806
0
    return -1;
807
0
  }
808
809
0
  return 0;
810
0
}
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
 */
826
int fr_channel_service_kevent(fr_channel_t *ch, fr_control_t *c, UNUSED struct kevent const *kev)
827
0
{
828
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
829
830
0
  if (c == ch->end[TO_RESPONDER].control) {
831
0
    ch->end[TO_RESPONDER].stats.kevents++;
832
0
  } else {
833
0
    ch->end[TO_REQUESTOR].stats.kevents++;
834
0
  }
835
836
0
  return 0;
837
0
}
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
 */
850
bool fr_channel_active(fr_channel_t *ch)
851
0
{
852
0
  return atomic_load(&ch->end[TO_REQUESTOR].active) && atomic_load(&ch->end[TO_RESPONDER].active);
853
0
}
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
 */
862
int fr_channel_signal_responder_close(fr_channel_t *ch)
863
0
{
864
0
  int ret;
865
0
  bool active;
866
0
  fr_channel_control_t cc;
867
868
0
  active = atomic_load(&ch->end[TO_RESPONDER].active);
869
0
  if (!active) return 0;         /* Already signalled to close */
870
871
0
  atomic_store(&ch->end[TO_RESPONDER].active, false); /* Prevent further requests */
872
873
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
874
875
0
  cc.ack = 0;
876
0
  cc.ch = ch;
877
0
  cc.reply_ms = NULL;
878
879
0
  ret = fr_control_message_send(ch->end[TO_RESPONDER].control, FR_CONTROL_EVENTSET_CHANNEL,
880
0
              FR_CHANNEL_CONTROL_CLOSE, &cc, sizeof(cc));
881
882
0
  return ret;
883
0
}
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
 */
895
unsigned int fr_channel_responder_discard(fr_channel_t *ch)
896
0
{
897
0
  fr_channel_data_t *cd;
898
0
  fr_atomic_queue_t *aq = ch->end[TO_RESPONDER].aq;
899
0
  unsigned int    num = 0;
900
901
0
  while (fr_atomic_queue_spsc_pop(aq, &cd)) {
902
0
    fr_message_done(&cd->m);
903
0
    num++;
904
0
  }
905
906
0
  return num;
907
0
}
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
 */
916
int fr_channel_responder_ack_close(fr_channel_t *ch)
917
0
{
918
0
  int ret;
919
0
  bool active;
920
921
0
  fr_channel_control_t cc;
922
923
0
  active = atomic_load(&ch->end[TO_REQUESTOR].active);
924
0
  if (!active) return 0;         /* Already signalled to close */
925
926
0
  atomic_store(&ch->end[TO_REQUESTOR].active, false); /* Prevent further responses */
927
928
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
929
930
0
  cc.ack = 0;
931
0
  cc.ch = ch;
932
0
  cc.reply_ms = NULL;
933
934
0
  ret = fr_control_message_send(ch->end[TO_REQUESTOR].control, FR_CONTROL_EVENTSET_CHANNEL,
935
0
              FR_CHANNEL_CONTROL_CLOSE_ACK, &cc, sizeof(cc));
936
937
0
  return ret;
938
0
}
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
 */
945
void fr_channel_responder_uctx_add(fr_channel_t *ch, void *uctx)
946
0
{
947
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
948
949
0
  ch->end[TO_REQUESTOR].uctx = uctx;
950
0
}
951
952
953
/** Get responder-specific data from a channel
954
 *
955
 * @param[in] ch  The channel.
956
 */
957
void *fr_channel_responder_uctx_get(fr_channel_t *ch)
958
0
{
959
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
960
961
0
  return ch->end[TO_REQUESTOR].uctx;
962
0
}
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
 */
970
void fr_channel_requestor_uctx_add(fr_channel_t *ch, void *uctx)
971
0
{
972
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
973
974
0
  ch->end[TO_RESPONDER].uctx = uctx;
975
0
}
976
977
978
/** Get network-specific data from a channel
979
 *
980
 * @param[in] ch  The channel.
981
 */
982
void *fr_channel_requestor_uctx_get(fr_channel_t *ch)
983
0
{
984
0
  (void) talloc_get_type_abort(ch, fr_channel_t);
985
986
0
  return ch->end[TO_RESPONDER].uctx;
987
0
}
988
989
990
int fr_channel_set_recv_reply(fr_channel_t *ch, fr_channel_recv_callback_t recv_reply, void *uctx)
991
0
{
992
0
  ch->end[TO_RESPONDER].recv = recv_reply;
993
0
  ch->end[TO_RESPONDER].recv_uctx = uctx;
994
995
0
  return 0;
996
0
}
997
998
int fr_channel_set_recv_request(fr_channel_t *ch, fr_channel_recv_callback_t recv_request, void *uctx)
999
0
{
1000
0
  ch->end[TO_REQUESTOR].recv = recv_request;
1001
0
  ch->end[TO_REQUESTOR].recv_uctx = uctx;
1002
0
  return 0;
1003
0
}
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
 */
1013
int fr_channel_signal_open(fr_channel_t *ch, fr_message_set_t *reply_ms)
1014
0
{
1015
0
  fr_channel_control_t cc;
1016
1017
0
  cc.ack = 0;
1018
0
  cc.ch = ch;
1019
0
  cc.reply_ms = reply_ms;
1020
1021
0
  return fr_control_message_send(ch->end[TO_RESPONDER].control, FR_CONTROL_EVENTSET_CHANNEL,
1022
0
               FR_CHANNEL_CONTROL_OPEN, &cc, sizeof(cc));
1023
0
}
1024
1025
void fr_channel_stats_log(fr_channel_t const *ch, fr_log_t const *log, char const *file, int line)
1026
0
{
1027
0
  fr_log(log, L_INFO, file, line, "requestor\n");
1028
0
  fr_log(log, L_INFO, file, line, "\tsignals sent = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.signals);
1029
0
  fr_log(log, L_INFO, file, line, "\tsignals re-sent = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.resignals);
1030
0
  fr_log(log, L_INFO, file, line, "\tsignals received empty = %" PRIu64 "\n",
1031
0
         ch->end[TO_RESPONDER].stats.spurious);
1032
0
  fr_log(log, L_INFO, file, line, "\tkevents checked = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.kevents);
1033
0
  fr_log(log, L_INFO, file, line, "\toutstanding = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.outstanding);
1034
0
  fr_log(log, L_INFO, file, line, "\tpackets processed = %" PRIu64 "\n", ch->end[TO_RESPONDER].stats.packets);
1035
0
  fr_log(log, L_INFO, file, line, "\tmessage interval (RTT) = %" PRId64 "\n", fr_time_delta_unwrap(ch->end[TO_RESPONDER].stats.message_interval));
1036
0
  fr_log(log, L_INFO, file, line, "\tlast write = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_RESPONDER].stats.last_write));
1037
0
  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
0
  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
0
  fr_log(log, L_INFO, file, line, "responder\n");
1041
0
  fr_log(log, L_INFO, file, line, "\tsignals sent = %" PRIu64"\n", ch->end[TO_REQUESTOR].stats.signals);
1042
0
  fr_log(log, L_INFO, file, line, "\tsignals received empty = %" PRIu64 "\n",
1043
0
         ch->end[TO_REQUESTOR].stats.spurious);
1044
0
  fr_log(log, L_INFO, file, line, "\tkevents checked = %" PRIu64 "\n", ch->end[TO_REQUESTOR].stats.kevents);
1045
0
  fr_log(log, L_INFO, file, line, "\tpackets processed = %" PRIu64 "\n", ch->end[TO_REQUESTOR].stats.packets);
1046
0
  fr_log(log, L_INFO, file, line, "\tmessage interval (RTT) = %" PRId64 "\n", fr_time_delta_unwrap(ch->end[TO_REQUESTOR].stats.message_interval));
1047
0
  fr_log(log, L_INFO, file, line, "\tlast write = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_REQUESTOR].stats.last_write));
1048
0
  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
0
  fr_log(log, L_INFO, file, line, "\tlast signal other = %" PRId64 "\n", fr_time_unwrap(ch->end[TO_REQUESTOR].stats.last_sent_signal));
1050
0
}
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
 */
1056
fr_channel_stats_t const *fr_channel_requestor_stats(fr_channel_t const *ch)
1057
0
{
1058
0
  return &ch->end[TO_RESPONDER].stats;
1059
0
}
1060
1061
/** Statistics for the responder's end of a channel
1062
 */
1063
fr_channel_stats_t const *fr_channel_responder_stats(fr_channel_t const *ch)
1064
0
{
1065
0
  return &ch->end[TO_REQUESTOR].stats;
1066
0
}