24RCSID(
"$Id: bd6d6452cbf184ae0d15e904e7d183d31ec2719e $")
26#include <freeradius-devel/server/base.h>
27#include <freeradius-devel/io/channel.h>
28#include <freeradius-devel/io/control.h>
29#include <freeradius-devel/io/message.h>
30#include <freeradius-devel/util/debug.h>
31#include <freeradius-devel/util/event.h>
32#include <freeradius-devel/util/rand.h>
33#include <freeradius-devel/util/syserror.h>
34#include <freeradius-devel/util/talloc.h>
36#ifdef HAVE_STDATOMIC_H
39# include <freeradius-devel/util/stdatomic.h>
48#define MPRINT1 if (debug_lvl) printf
49#define MPRINT2 if (debug_lvl > 1) printf
59#define PAYLOAD_SIZE (sizeof(payload_t))
134static pthread_mutex_t
wd_mutex = PTHREAD_MUTEX_INITIALIZER;
135static pthread_cond_t
wd_cond = PTHREAD_COND_INITIALIZER;
140 fprintf(stderr,
"usage: channel_test [OPTS]\n");
141 fprintf(stderr,
" -r <requestors> Number of requestor threads (default %zu).\n",
num_requestors);
142 fprintf(stderr,
" -w <responders> Number of responder threads (default %zu).\n",
num_responders);
143 fprintf(stderr,
" -m <messages> Requests to send per channel (default %" PRIu64
").\n",
max_messages);
144 fprintf(stderr,
" -o <outstanding> Requests in flight per channel (default %" PRIu64
").\n",
max_outstanding);
145 fprintf(stderr,
" -W <usec> Max busy work per reply (default %u).\n",
max_work_usec);
146 fprintf(stderr,
" -P <usec> Max pause between requestor bursts (default %u).\n",
max_pause_usec);
147 fprintf(stderr,
" -t <secs> Watchdog interval, abort if no progress (default %u).\n",
watchdog_secs);
148 fprintf(stderr,
" -b Block without announcing, to prove the watchdog catches a stall.\n");
149 fprintf(stderr,
" -M <messages> Messages per set, power of two (default: twice the window plus 64).\n");
150 fprintf(stderr,
" -x Debugging mode.\n");
161 if (!max_usec)
return;
178 memcpy(&pl, cd->
m.
data,
sizeof(pl));
180 "requestor %zu channel %zu: reply belongs to requestor %" PRIu64
" channel %" PRIu64,
183 "requestor %zu channel %zu: reply %" PRIu64
" after %" PRIu64
" (sent %" PRIu64
" replied %" PRIu64
")",
217 MPRINT1(
"requestor %zu: started\n", r->
id);
221 fprintf(stderr,
"requestor %zu: failed signalling open: %s\n", r->
id,
fr_strerror());
249 memcpy(cd->
m.
data, &pl,
sizeof(pl));
253 MPRINT2(
"requestor %zu: channel %zu full\n", r->
id, i);
263 MPRINT1(
"requestor %zu: closing channel to responder %zu\n", r->
id, i);
266 fprintf(stderr,
"requestor %zu: failed signalling close: %s\n", r->
id,
fr_strerror());
293 if (num_events < 0) {
294 fprintf(stderr,
"requestor %zu: failed waiting for events: %s\n", r->
id,
fr_syserror(errno));
300 MPRINT1(
"requestor %zu: exiting\n", r->
id);
338 MPRINT1(
"responder %zu: channel %zu opened\n", w->
id, i);
356 fprintf(stderr,
"responder %zu: %u requests queued at close\n", w->
id, num);
429 reply->reply.cpu_time =
reply->reply.processing_time;
433 MPRINT2(
"responder %zu: reply queue full\n", w->
id);
448 MPRINT1(
"responder %zu: started\n", w->
id);
451 bool wait, announced =
false;
464 if (num_events < 0) {
465 fprintf(stderr,
"responder %zu: failed waiting for events: %s\n", w->
id,
fr_syserror(errno));
473 MPRINT1(
"responder %zu: exiting\n", w->
id);
490 fprintf(stderr,
"requestor %zu -> responder %zu: sent %" PRIu64
" replied %" PRIu64
491 " outstanding %" PRIu64
"%s%s\n",
500 fprintf(stderr,
"responder %zu: received %" PRIu64
" pending %zu"
501 " sleeps %" PRIu64
" %s\n",
516 clock_gettime(CLOCK_REALTIME, &ts);
521 if (ret != ETIMEDOUT)
continue;
527 fprintf(stderr,
"STALL: no replies in %u seconds\n",
watchdog_secs);
531 MPRINT1(
"watchdog: %" PRIu64
" replies\n", now);
544int main(
int argc,
char *argv[])
550 pthread_t *req_id, *resp_id, wd_id;
552 int64_t elapsed_usec;
553 uint64_t sent = 0, replied = 0, signals = 0, spurious_req = 0, spurious_rep = 0, sleeps = 0, rescued = 0;
556 while ((c = getopt(argc, argv,
"bhm:M:o:P:r:t:w:W:x")) != -1)
switch (c) {
639 fprintf(stderr,
"failed setting up responder %zu control plane: %s\n", i,
fr_strerror());
655 fprintf(stderr,
"failed setting up requestor %zu control plane: %s\n", i,
fr_strerror());
659 size_t n = num_messages;
672 num_messages * 256,
false));
679 (void) pthread_attr_init(&attr);
680 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
691 for (i = 0; i <
num_requestors; i++) (
void) pthread_join(req_id[i], NULL);
692 for (i = 0; i <
num_responders; i++) (
void) pthread_join(resp_id[i], NULL);
698 (void) pthread_join(wd_id, NULL);
743 printf(
"requests %" PRIu64
"\n", sent);
744 printf(
"replies %" PRIu64
"\n", replied);
745 printf(
"request signals %" PRIu64
" (%.1f%% of requests skipped the signal)\n",
746 signals, 100.0 * (
double) (sent - signals) / (
double) sent);
747 printf(
"spurious signals %" PRIu64
" request, %" PRIu64
" reply\n", spurious_req, spurious_rep);
748 printf(
"responder sleeps %" PRIu64
"\n", sleeps);
749 printf(
"rescued by drain %" PRIu64
"\n", rescued);
750 printf(
"elapsed %.3fs, %.0f requests/s\n",
751 (
double) elapsed_usec / 1e6, ((
double) sent * 1e6) / (
double) elapsed_usec);
#define NEVER_RETURNS
Should be placed before the function return type.
void * fr_channel_requestor_uctx_get(fr_channel_t *ch)
Get network-specific data from a channel.
fr_channel_stats_t const * fr_channel_responder_stats(fr_channel_t const *ch)
Statistics for the responder's end of a channel.
bool fr_channel_recv_reply(fr_channel_t *ch)
Receive a reply message from the channel.
unsigned int fr_channel_responder_discard(fr_channel_t *ch)
Discard any requests the requestor queued but we never received.
int fr_channel_signal_open(fr_channel_t *ch, fr_message_set_t *reply_ms)
Send a channel to a responder.
int fr_channel_signal_responder_close(fr_channel_t *ch)
Signal a responder that the channel is closing.
int fr_channel_send_request(fr_channel_t *ch, fr_channel_data_t *cd)
Send a request message into the channel.
fr_channel_t * fr_channel_create(TALLOC_CTX *ctx, fr_control_t *requestor, fr_control_t *responder, bool same)
Create a new channel.
fr_channel_stats_t const * fr_channel_requestor_stats(fr_channel_t const *ch)
Statistics for the requestor's end of a channel.
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.
int fr_channel_set_recv_reply(fr_channel_t *ch, fr_channel_recv_callback_t recv_reply, void *uctx)
void * fr_channel_responder_uctx_get(fr_channel_t *ch)
Get responder-specific data from a channel.
bool fr_channel_recv_request(fr_channel_t *ch)
Receive a request message from the channel.
void fr_channel_requestor_uctx_add(fr_channel_t *ch, void *uctx)
Add network-specific data to a channel.
void fr_channel_responder_uctx_add(fr_channel_t *ch, void *uctx)
Add responder-specific data to a channel.
int fr_channel_set_recv_request(fr_channel_t *ch, fr_channel_recv_callback_t recv_request, void *uctx)
int fr_channel_send_reply(fr_channel_t *ch, fr_channel_data_t *cd)
Send a reply message into the channel.
bool fr_channel_active(fr_channel_t *ch)
Check if a channel is active.
int fr_channel_responder_ack_close(fr_channel_t *ch)
Acknowledge that the channel is closing.
A full channel, which consists of two ends.
fr_message_t m
the message header
fr_channel_closed_t closed
uint64_t signals
Number of kevent signals we've sent.
uint64_t spurious
Number of signals we received which found no data waiting.
What the owner of a control plane does with channel lifecycle events.
Channel information which is added to a message.
resp_channel_t * channel
one slot per requestor.
int main(int argc, char *argv[])
static void * requestor_thread(void *arg)
static uint32_t max_work_usec
static size_t num_responders
uint64_t sleeps
times we blocked.
static unsigned int watchdog_secs
fr_channel_data_t ** pending
requests received but not yet replied to.
static void responder_recv_request(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
static uint64_t max_outstanding
uint64_t rescued
final drains which found work, i.e. skipped signals we caught.
static void * watchdog_thread(UNUSED void *arg)
fr_message_set_t * reply_ms
replies are allocated from this by the responder.
static void * responder_thread(void *arg)
static void dump_state(void)
static uint32_t max_pause_usec
fr_message_set_t * ms
requests are allocated from this.
static void responder_open(fr_channel_t *ch, fr_message_set_t *reply_ms, UNUSED fr_time_t now, void *uctx)
static uint64_t max_messages
static void requestor_closed(fr_channel_t *ch, UNUSED fr_time_t now, void *uctx)
size_t responder
index of the responder at the other end.
static void spin(uint32_t max_usec)
Burn a random amount of time without a syscall.
req_channel_t * channel
one per responder.
static size_t num_requestors
static size_t set_messages
message set size override, 0 for automatic.
static responder_t * responders
uint64_t last_seq
of the last reply, replies must arrive in order.
static void responder_close(fr_channel_t *ch, UNUSED fr_time_t now, void *uctx)
static fr_channel_callbacks_t const responder_callbacks
static bool responder_sleeping(responder_t *w)
The responder half of the handshake, as fr_worker() does it.
static void responder_run(responder_t *w)
Reply to everything pending at the start of this pass.
static fr_channel_callbacks_t const requestor_callbacks
static requestor_t * requestors
fr_message_set_t * reply_ms
static void requestor_recv_reply(fr_channel_t *ch, fr_channel_data_t *cd, UNUSED void *uctx)
What travels in each request, and is echoed back in its reply.
A requestor's view of one channel.
A responder's view of one channel.
#define FR_CONTROL_MAX_SIZE
#define FR_CONTROL_MAX_MESSAGES
static pthread_cond_t wd_cond
static pthread_mutex_t wd_mutex
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
static NEVER_RETURNS void usage(void)
bool fr_control_sleeping(fr_control_t *c)
Record that the reader of this control plane is about to block.
void fr_control_awake(fr_control_t *c)
Record that the reader of this control plane is running again.
bool fr_control_is_sleeping(fr_control_t *c)
Check whether the reader of this control plane is blocked.
int fr_control_open(fr_control_t *c)
Open the control-plane signalling path.
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, uint16_t slots, uint16_t max_size)
Create a control-plane signaling path.
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Stores all information relating to an event list.
fr_message_set_t * fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size, bool unlimited_size)
Create a message set.
int fr_message_done(fr_message_t *m)
Mark a message as done.
int fr_message_set_messages_used(fr_message_set_t *ms)
Count the number of used messages.
void fr_message_set_gc(fr_message_set_t *ms)
Garbage collect the message set.
fr_message_t * fr_message_and_data_alloc(fr_message_set_t *ms, size_t size)
Reserve and commit a message atomically.
A Message set, composed of message headers and ring buffer data.
fr_time_t when
when this message was sent
uint8_t * data
pointer to the data in the ring buffer
uint32_t fr_rand(void)
Return a 32-bit random number.
#define atomic_fetch_add_explicit(object, operand, order)
#define atomic_load_explicit(object, order)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
int fr_time_start(void)
Initialize the local time.
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
#define fr_time_add(_a, _b)
Add a time/time delta together.
#define fr_time_sub(_a, _b)
Subtract one time from another.
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
#define fr_time_lt(_a, _b)
char const * fr_strerror(void)
Get the last library error.