The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
worker.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: 808d90cec61df1c5483d6c2fc1f3798be46d9fbd $
19 *
20 * @brief Worker thread functions.
21 * @file io/worker.c
22 *
23 * The "worker" thread is the one responsible for the bulk of the
24 * work done when processing a request. Workers are spawned by the
25 * scheduler, and create a kqueue (KQ) and control-plane
26 * Atomic Queue (AQ) for control-plane communication.
27 *
28 * When a network thread discovers that it needs more workers, it
29 * asks the scheduler for a KQ/AQ combination. The network thread
30 * then creates a channel dedicated to that worker, and sends the
31 * channel to the worker in a "new channel" message. The worker
32 * receives the channel, and sends an ACK back to the network thread.
33 *
34 * The network thread then sends the worker new packets, which the
35 * worker receives and processes.
36 *
37 * When a packet is decoded, it is put into the "runnable" heap, and
38 * also into the timeout sublist. The main loop fr_worker() then
39 * pulls new requests off of this heap and runs them. The main event
40 * loop checks the head of the timeout sublist, and forcefully terminates
41 * any requests which have been running for too long.
42 *
43 * If a request is yielded, it is placed onto the yielded list in
44 * the worker "tracking" data structure.
45 *
46 * @copyright 2016 Alan DeKok (aland@freeradius.org)
47 */
48
49RCSID("$Id: 808d90cec61df1c5483d6c2fc1f3798be46d9fbd $")
50
51#define LOG_PREFIX worker->name
52#define LOG_DST worker->log
53
54#include <freeradius-devel/io/channel.h>
55#include <freeradius-devel/io/listen.h>
56#include <freeradius-devel/io/worker.h>
57#include <freeradius-devel/unlang/base.h>
58#include <freeradius-devel/unlang/finally.h>
59#include <freeradius-devel/util/minmax_heap.h>
60#include <freeradius-devel/util/timer.h>
61
62#include <stdalign.h>
63
64#ifdef WITH_VERIFY_PTR
65static void worker_verify(fr_worker_t *worker);
66#define WORKER_VERIFY worker_verify(worker)
67#else
68#define WORKER_VERIFY
69#endif
70
71static _Atomic(uint64_t) request_number = 0;
72
75
76static _Thread_local fr_ring_buffer_t *fr_worker_rb;
77
78typedef struct {
79 fr_worker_t *worker; //!< the worker that owns this channel slot,
80 ///< so channel callbacks can reach back without
81 ///< threading `worker` through every layer.
82 fr_channel_t *ch;
83 fr_message_set_t *ms; //!< messages for this channel
84
85 fr_dlist_head_t dlist; //!< of requests received on this channel
87
88/**
89 * A worker which takes packets from a master, and processes them.
90 */
92 char const *name; //!< name of this worker
93 fr_worker_config_t config; //!< external configuration
94
95 unlang_interpret_t *intp; //!< Worker's local interpreter.
96
97 pthread_t thread_id; //!< my thread ID
98
99 fr_log_t const *log; //!< log destination
100 fr_log_lvl_t lvl; //!< log level
101
102 fr_atomic_queue_t *aq_control; //!< atomic queue for control messages sent to me
103
104 fr_control_t *control; //!< the control plane
105
106 fr_event_list_t *el; //!< our event list
107
108 int num_channels; //!< actual number of channels
109 int num_closing; //!< number of channels closing
110
111 fr_heap_t *runnable; //!< current runnable requests which we've spent time processing
112
113 fr_timer_list_t *timeout; //!< Track when requests timeout using a dlist.
114 fr_time_delta_t max_request_time; //!< maximum time a request can be processed
115
116 fr_rb_tree_t *dedup; //!< de-dup tree
117
118 fr_rb_tree_t *listeners; //!< so we can cancel requests when a listener goes away
119
120 fr_io_stats_t stats; //!< input / output stats
121 fr_time_elapsed_t cpu_time; //!< histogram of total CPU time per request
122 fr_time_elapsed_t wall_clock; //!< histogram of wall clock time per request
123
124 uint64_t num_naks; //!< number of messages which were nak'd
125 uint64_t num_active; //!< number of active requests
126
127 fr_time_delta_t predicted; //!< How long we predict a request will take to execute.
128 fr_time_tracking_t tracking; //!< how much time the worker has spent doing things.
129
130 bool was_sleeping; //!< used to suppress multiple sleep signals in a row
131 bool exiting; //!< are we exiting?
132
133 fr_worker_channel_t *channel; //!< list of channels
134
135 request_slab_list_t *slab; //!< slab allocator for request_t
136};
137
138typedef struct {
139 fr_listen_t const *listener; //!< incoming packets
140
141 fr_rb_node_t node; //!< in tree of listeners
142
143 /*
144 * To save time, we don't care about num_elements here. Which means that we don't
145 * need to cache or lookup the fr_worker_listen_t when we free a request.
146 */
147 fr_dlist_head_t dlist; //!< of requests associated with this listener.
149
150
151static fr_cmp_ret_t worker_listener_cmp(void const *one, void const *two)
152{
153 fr_worker_listen_t const *a = one, *b = two;
154
155 return CMP(a->listener, b->listener);
156}
157
158
159/*
160 * Explicitly cleanup the memory allocated to the ring buffer,
161 * just in case valgrind complains about it.
162 */
163static int _fr_worker_rb_free(void *arg)
164{
165 return talloc_free(arg);
166}
167
168/** Initialise thread local storage
169 *
170 * @return fr_ring_buffer_t for messages
171 */
173{
175
176 rb = fr_worker_rb;
177 if (rb) return rb;
178
180 if (!rb) {
181 fr_perror("Failed allocating memory for worker ring buffer");
182 return NULL;
183 }
184
186
187 return rb;
188}
189
190static inline bool is_worker_thread(fr_worker_t const *worker)
191{
192 return (pthread_equal(pthread_self(), worker->thread_id) != 0);
193}
194
196static void worker_send_reply(fr_worker_t *worker, request_t *request, bool do_not_respond, fr_time_t now);
197
198/** Callback which handles a message being received on the worker side.
199 *
200 * @param[in] ch the channel to drain
201 * @param[in] cd the message (if any) to start with
202 * @param[in] uctx the worker channel slot the message came in on
203 */
204static void worker_recv_request(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
205{
206 fr_worker_channel_t *wc = uctx;
207 fr_worker_t *worker = wc->worker;
208
209 worker->stats.in++;
210 DEBUG3("Received request %" PRIu64 "", worker->stats.in);
211 cd->channel.ch = ch;
213}
214
215static inline int worker_cancelled_run(fr_worker_t *worker)
216{
217 int cancelled = 0;
218 request_t *request;
219
220 while ((request = fr_heap_peek(worker->runnable)) && (unlang_request_is_cancelled(request))) {
221 fr_heap_extract(&worker->runnable, request);
222
223 REQUEST_VERIFY(request);
224 fr_assert(!fr_heap_entry_inserted(request->runnable));
225
227 cancelled++;
228 }
229
230 return cancelled;
231}
232
234{
235 fr_async_t *async = NULL;
236 int cancelled = 0;
237
238 while ((async = fr_dlist_next(&ch->dlist, async)) != NULL) {
239 if (unlang_finally_entered(async->request)) continue;
241 async->request->rcode = RLM_MODULE_TIMEOUT;
242 cancelled++;
243 }
244
245 DEBUG("%d requests cancelled", cancelled);
246}
247
248static void worker_exit(fr_worker_t *worker)
249{
250 worker->exiting = true;
251}
252
253/** Handle a control plane message sent to the worker via a channel
254 *
255 * @param[in] data the message
256 * @param[in] data_size size of the data
257 * @param[in] now the current time
258 * @param[in] uctx the worker
259 */
260static void worker_channel_callback(void const *data, size_t data_size, fr_time_t now, void *uctx)
261{
262 int i;
263 unsigned int num;
264 bool ok, was_sleeping;
265 fr_channel_t *ch;
268 fr_worker_t *worker = uctx;
269 void *ch_uctx;
270
271 was_sleeping = worker->was_sleeping;
272 worker->was_sleeping = false;
273
274 /*
275 * We were woken up by a signal to do something. We're
276 * not sleeping.
277 */
278 ce = fr_channel_service_message(now, &ch, &ch_uctx, data, data_size);
279 DEBUG3("Channel %s",
280 fr_table_str_by_value(channel_signals, ce, "<INVALID>"));
281 switch (ce) {
282 case FR_CHANNEL_ERROR:
283 return;
284
285 case FR_CHANNEL_EMPTY:
286 return;
287
288 case FR_CHANNEL_NOOP:
289 return;
290
292 fr_assert(0 == 1);
293 break;
294
296 fr_assert(ch != NULL);
297
298 if (!fr_channel_recv_request(ch)) {
299 worker->was_sleeping = was_sleeping;
300
301 } else while (fr_channel_recv_request(ch));
302 break;
303
304 case FR_CHANNEL_OPEN:
305 fr_assert(ch != NULL);
306
307 ok = false;
308 for (i = 0; i < worker->config.max_channels; i++) {
309 fr_assert(worker->channel[i].ch != ch);
310
311 if (worker->channel[i].ch != NULL) continue;
312
313 worker->channel[i].worker = worker;
314 worker->channel[i].ch = ch;
315 fr_dlist_init(&worker->channel[i].dlist, fr_async_t, entry);
316
317 DEBUG3("Received channel %p into array entry %d", ch, i);
318
319 ms = talloc_get_type_abort(ch_uctx, fr_message_set_t);
320
321 worker->channel[i].ms = ms;
322
323 /*
324 * Hand the channel a pointer to the slot rather than to
325 * any one field of it, so a callback holding only the
326 * channel reaches the message set and the request list
327 * alike. Neither can be set in
328 * fr_worker_channel_create() because the slot has not
329 * been claimed at that point.
330 */
331 fr_channel_responder_uctx_add(ch, &worker->channel[i]);
333
334 worker->num_channels++;
335 ok = true;
336 break;
337 }
338
339 fr_cond_assert(ok);
340 break;
341
342 case FR_CHANNEL_CLOSE:
343 fr_assert(ch != NULL);
344
345 ok = false;
346
347 /*
348 * Locate the signalling channel in the list
349 * of channels.
350 */
351 for (i = 0; i < worker->config.max_channels; i++) {
352 if (!worker->channel[i].ch) continue;
353
354 if (worker->channel[i].ch != ch) continue;
355
356 worker_requests_cancel(worker, &worker->channel[i]);
357
358 ms = worker->channel[i].ms;
359
360 /*
361 * Should be nothing left: the network is not supposed
362 * to enqueue anything once it has signalled the close,
363 * which is what the assert above claims. Hand back
364 * whatever we find anyway, so the messages do not
365 * strand the ring buffer they came from, and complain,
366 * because these produce no reply and so the network
367 * never decrements its outstanding count for them.
368 */
370 if (num > 0) PWARN("Discarded %u request(s) still queued at close", num);
371
372 fr_assert(ms != NULL);
374
375 fr_assert(worker->num_channels > 0);
376
377 worker->num_channels--;
378 worker->num_closing++;
379 ok = true;
380 break;
381 }
382
383 fr_cond_assert(ok);
384
385 /*
386 * Our last input channel closed,
387 * time to die.
388 */
389 if (worker->num_channels == 0) worker_exit(worker);
390 break;
391 }
392}
393
395{
397 request_t *request;
398
399 fr_rb_find((void **)&wl, worker->listeners, &(fr_worker_listen_t) { .listener = li });
400 if (!wl) return -1;
401
402 while ((request = fr_dlist_pop_head(&wl->dlist)) != NULL) {
403 RERROR("Cancelling request due to socket being closed");
405 }
406
407 (void) fr_rb_delete(worker->listeners, wl);
408 talloc_free(wl);
409
410 return 0;
411}
412
413
414/** A socket is going away, so clean up any requests which use this socket.
415 *
416 * @param[in] data the message
417 * @param[in] data_size size of the data
418 * @param[in] now the current time
419 * @param[in] uctx the worker
420 */
421static void worker_listen_cancel_callback(void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, void *uctx)
422{
423 fr_listen_t const *li;
424 fr_worker_t *worker = uctx;
425
426 fr_assert(data_size == sizeof(li));
427
428 memcpy(&li, data, sizeof(li));
429
430 (void) fr_worker_listen_cancel_self(worker, li);
431}
432
433/** Send a NAK to the network thread
434 *
435 * The network thread believes that a worker is running a request until that request has been NAK'd.
436 * We typically NAK requests when they've been hanging around in the worker's backlog too long,
437 * or there was an error executing the request.
438 *
439 * @param[in] worker the worker
440 * @param[in] cd the message to NAK
441 * @param[in] now when the message is NAKd
442 */
443static void worker_nak(fr_worker_t *worker, fr_channel_data_t *cd, fr_time_t now)
444{
445 size_t size;
446 fr_channel_data_t *reply;
447 fr_channel_t *ch;
450 fr_listen_t *listen;
451
452 worker->num_naks++;
453
454 /*
455 * Cache the outbound channel. We'll need it later.
456 */
457 ch = cd->channel.ch;
458 listen = cd->listen;
459
460 /*
461 * If the channel has been closed, but we haven't
462 * been informed, that is extremely bad.
463 *
464 * Try to continue working... but we'll likely
465 * leak memory or SEGV soon.
466 */
467 if (!fr_cond_assert_msg(fr_channel_active(ch), "Wanted to send NAK but channel has been closed")) {
468 fr_message_done(&cd->m);
469 return;
470 }
471
473 ms = wc->ms;
474 fr_assert(ms != NULL);
475
476 size = listen->app_io->default_reply_size;
477 if (!size) size = listen->default_message_size;
478
479 /*
480 * Allocate a default message size.
481 */
483
484 /*
485 * Encode a NAK
486 */
487 if (listen->app_io->nak) {
488 size = listen->app_io->nak(listen, cd->packet_ctx, cd->m.data,
489 cd->m.data_size, reply->m.data, reply->m.rb_size);
490 } else {
491 size = 1; /* rely on them to figure it the heck out */
492 }
493
494 (void) fr_message_and_data_commit(ms, &reply->m, size);
495
496 /*
497 * Fill in the NAK.
498 */
499 reply->m.when = now;
500 reply->reply.cpu_time = worker->tracking.running_total;
501 reply->reply.processing_time = fr_time_delta_from_msec(1); /* @todo - set to something better? */
502 reply->reply.request_time = cd->request.recv_time;
503
504 reply->listen = cd->listen;
505 reply->packet_ctx = cd->packet_ctx;
506
507 /*
508 * Mark the original message as done.
509 */
510 fr_message_done(&cd->m);
511
512 /*
513 * Send the reply, which also polls the request queue.
514 */
515 if (fr_channel_send_reply(ch, reply) < 0) {
516 DEBUG2("Failed sending reply to channel");
517 }
518
519 worker->stats.out++;
520}
521
522/** Signal the unlang interpreter that it needs to stop running the request
523 *
524 * Signalling is a synchronous operation. Whatever I/O requests the request
525 * is currently performing are signalled to be cancelled. Depending on the
526 * specific back end, the actual cancellation may be asynchronously handled
527 * by the trunk code.
528 *
529 * Modules and unlang keywords explicitly register signal handlers to deal
530 * with their yield points being cancelled/interrupted via this function.
531 *
532 * Following this, the request still needs to be run through the interpreter
533 * to tidy up and potentially run a `finally` section.
534 *
535 * @param[in] request request to cancel. The request may still run to completion.
536 */
537static void worker_stop_request(request_t *request)
538{
539 /*
540 * Also marks the request as done and runs
541 * the internal/external callbacs.
542 */
544}
545
546/** Enforce max_request_time
547 *
548 * Run periodically, and tries to clean up requests which were received by the network
549 * thread more than max_request_time seconds ago. In the interest of not adding a
550 * timer for every packet, the requests are given a 1 second leeway.
551 *
552 * @param[in] tl the worker's timer list.
553 * @param[in] when the current time
554 * @param[in] uctx the request_t timing out.
555 */
557{
558 request_t *request = talloc_get_type_abort(uctx, request_t);
559
560 /*
561 * Waiting too long, delete it.
562 */
563 REDEBUG("Request has reached max_request_time - signalling it to stop");
564 worker_stop_request(request);
565
566 /*
567 * This ensures the finally section can run timeout specific policies
568 */
569 request->rcode = RLM_MODULE_TIMEOUT;
570}
571
572
573/** Start time tracking for a request, and mark it as runnable.
574 *
575 */
577{
578 /*
579 * New requests are inserted into the time order heap in
580 * strict time priority. Once they are in the list, they
581 * are only removed when the request is done / free'd.
582 */
583 fr_assert(!fr_timer_armed(request->timeout));
584
585 if (unlikely(fr_timer_in(request, worker->timeout, &request->timeout, worker->config.max_request_time,
586 true, _worker_request_timeout, request) < 0)) {
587 RERROR("Failed to set request timeout timer");
588 return -1;
589 }
590
591 /*
592 * Bootstrap the async state machine with the initial
593 * state of the request.
594 */
595 RDEBUG3("Time tracking started in yielded state");
596 fr_time_tracking_start(&worker->tracking, &request->async->tracking, now);
597 fr_time_tracking_yield(&request->async->tracking, now);
598 worker->num_active++;
599
600 fr_assert(!fr_heap_entry_inserted(request->runnable));
601 (void) fr_heap_insert(&worker->runnable, request);
602
603 return 0;
604}
605
607{
608 RDEBUG3("Time tracking ended");
609 fr_time_tracking_end(&worker->predicted, &request->async->tracking, now);
610 fr_assert(worker->num_active > 0);
611 worker->num_active--;
612
613 TALLOC_FREE(request->timeout); /* Disarm the reques timer */
614}
615
616/** Send a response packet to the network side
617 *
618 * @param[in] worker This worker.
619 * @param[in] request we're sending a reply for.
620 * @param[in] send_reply whether the network side sends a reply
621 * @param[in] now The current time
622 */
623static void worker_send_reply(fr_worker_t *worker, request_t *request, bool send_reply, fr_time_t now)
624{
625 fr_channel_data_t *reply;
626 fr_channel_t *ch;
629 size_t size = 1;
630
631 REQUEST_VERIFY(request);
632
633 /*
634 * If we're sending a reply, then it's no longer runnable.
635 */
636 fr_assert(!fr_heap_entry_inserted(request->runnable));
637
638 if (send_reply) {
639 size = request->async->listen->app_io->default_reply_size;
640 if (!size) size = request->async->listen->default_message_size;
641 }
642
643 /*
644 * Allocate and send the reply.
645 */
646 ch = request->async->channel;
647 fr_assert(ch != NULL);
648
649 /*
650 * If the channel has been closed, but we haven't
651 * been informed, that is extremely bad.
652 *
653 * Try to continue working... but we'll likely
654 * leak memory or SEGV soon.
655 */
656 if (!fr_cond_assert_msg(fr_channel_active(ch), "Wanted to send reply but channel has been closed")) {
657 return;
658 }
659
661 ms = wc->ms;
662 fr_assert(ms != NULL);
663
665 fr_assert(reply != NULL);
666
667 /*
668 * Encode it, if required.
669 */
670 if (send_reply) {
671 ssize_t slen = 0;
672 fr_listen_t const *listen = request->async->listen;
673
674 if (listen->app_io->encode) {
675 slen = listen->app_io->encode(listen->app_io_instance, request,
676 reply->m.data, reply->m.rb_size);
677 } else if (listen->app->encode) {
678 slen = listen->app->encode(listen->app_instance, request,
679 reply->m.data, reply->m.rb_size);
680 }
681 if (slen < 0) {
682 RPERROR("Failed encoding request");
683 *reply->m.data = 0;
684 slen = 1;
685 }
686
687 /*
688 * Shrink the buffer to the actual packet size.
689 *
690 * This will ALWAYS return the same message as we put in.
691 */
692 fr_assert((size_t) slen <= reply->m.rb_size);
693 (void) fr_message_and_data_commit(ms, &reply->m, slen);
694 } else {
695 (void) fr_message_and_data_commit(ms, &reply->m, 0);
696 }
697
698 /*
699 * Fill in the rest of the fields in the channel message.
700 *
701 * sequence / ack will be filled in by fr_channel_send_reply()
702 */
703 reply->m.when = now;
704 reply->reply.cpu_time = worker->tracking.running_total;
705 reply->reply.processing_time = request->async->tracking.running_total;
706 reply->reply.request_time = request->async->recv_time;
707
708 reply->listen = request->async->listen;
709 reply->packet_ctx = request->async->packet_ctx;
710
711 /*
712 * Update the various timers.
713 */
714 fr_time_elapsed_update(&worker->cpu_time, now, fr_time_add(now, reply->reply.processing_time));
715 fr_time_elapsed_update(&worker->wall_clock, reply->reply.request_time, now);
716
717 RDEBUG("Finished request");
718
719 /*
720 * Send the reply, which also polls the request queue.
721 */
722 if (fr_channel_send_reply(ch, reply) < 0) {
723 /*
724 * Should only happen if the TO_REQUESTOR
725 * channel is full, or it's not yet active.
726 *
727 * Not much we can do except complain
728 * loudly and cleanup the request.
729 */
730 RPERROR("Failed sending reply to network thread");
731 }
732
733 worker->stats.out++;
734
735 fr_assert(!fr_timer_armed(request->timeout));
736 fr_assert(!fr_heap_entry_inserted(request->runnable));
737
738 fr_dlist_entry_unlink(&request->listen_entry);
739
740#ifndef NDEBUG
741 request->async->el = NULL;
742 request->async->channel = NULL;
743 request->async->packet_ctx = NULL;
744 request->async->listen = NULL;
745#endif
746}
747
748/*
749 * talloc_typed_asprintf() is horrifically slow for printing
750 * simple numbers.
751 */
752static char *itoa_internal(TALLOC_CTX *ctx, uint64_t number)
753{
754 char buffer[32];
755 char *p;
756 char const *numbers = "0123456789";
757
758 p = buffer + 30;
759 *(p--) = '\0';
760
761 while (number > 0) {
762 *(p--) = numbers[number % 10];
763 number /= 10;
764 }
765
766 if (p[1]) return talloc_strdup(ctx, p + 1);
767
768 return talloc_strdup(ctx, "0");
769}
770
771/** Initialize various request fields needed by the worker.
772 *
773 */
774static inline CC_HINT(always_inline)
776{
777 /*
778 * For internal requests request->packet
779 * and request->reply are already populated.
780 */
781 if (!request->packet) MEM(request->packet = fr_packet_alloc(request, false));
782 if (!request->reply) MEM(request->reply = fr_packet_alloc(request, false));
783
784 request->packet->timestamp = now;
785 request->async = talloc_zero(request, fr_async_t);
786 request->async->request = request;
787 request->async->recv_time = now;
788 request->async->el = worker->el;
789 fr_dlist_entry_init(&request->async->entry);
790}
791
792static inline CC_HINT(always_inline)
794{
795 request->number = atomic_fetch_add_explicit(&request_number, 1, memory_order_seq_cst);
796 if (request->name) talloc_const_free(request->name);
797 request->name = itoa_internal(request, request->number);
798}
799
800static inline CC_HINT(always_inline)
802{
803 return fr_timer_list_num_events(worker->timeout);
804}
805
806static int _worker_request_deinit(request_t *request, UNUSED void *uctx)
807{
808 return request_slab_deinit(request);
809}
810
812{
813 fr_worker_t *worker = wc->worker;
814 int ret = -1;
815 request_t *request;
816 fr_listen_t *listen = cd->listen;
817
818 if (worker_num_requests(worker) >= (uint32_t) worker->config.max_requests) {
819 RATE_LIMIT_GLOBAL(ERROR, "Worker at max requests");
820 goto nak;
821 }
822
823 /*
824 * Receive a message to the worker queue, and decode it
825 * to a request.
826 */
827 fr_assert(listen != NULL);
828
829 request = request_slab_reserve(worker->slab);
830 if (!request) {
831 RATE_LIMIT_GLOBAL(ERROR, "Worker failed allocating new request");
832 goto nak;
833 }
834 /*
835 * Ensures that both the deinit function runs AND
836 * the request is returned to the slab if something
837 * calls talloc_free() on it.
838 */
839 request_slab_element_set_destructor(request, _worker_request_deinit, worker);
840
841 /*
842 * Have to initialise the request manually because namspace
843 * changes based on the listener that allocated it.
844 */
846 (&(request_init_args_t){ .namespace = listen->dict })) < 0) {
847 request_slab_release(request);
848 goto nak;
849 }
850
851 /*
852 * Do normal worker init that's shared between internal
853 * and external requests.
854 */
855 worker_request_init(worker, request, now);
857
858 /*
859 * Associate our interpreter with the request
860 */
861 unlang_interpret_set(request, worker->intp);
862
863 request->packet->timestamp = cd->request.recv_time; /* Legacy - Remove once everything looks at request->async */
864
865 /*
866 * Update the transport-specific fields.
867 */
868 request->async->channel = cd->channel.ch;
869
870 request->async->recv_time = cd->request.recv_time;
871
872 request->async->listen = listen;
873 request->async->packet_ctx = cd->packet_ctx;
874 request->priority = cd->priority;
875
876 /*
877 * Now that the "request" structure has been initialized, go decode the packet.
878 *
879 * Note that this also sets the "async process" function.
880 */
881 if (listen->app->decode) {
882 ret = listen->app->decode(listen->app_instance, request, cd->m.data, cd->m.data_size);
883 } else if (listen->app_io->decode) {
884 ret = listen->app_io->decode(listen->app_io_instance, request, cd->m.data, cd->m.data_size);
885 }
886
887 if (ret < 0) {
888 fail:
889 fr_assert(talloc_parent(request->stack) == request);
890 request_slab_release(request);
891
892 nak:
893 worker_nak(worker, cd, now);
894 return;
895 }
896
897 /*
898 * Set the entry point for this virtual server.
899 */
900 if (unlang_call_push(NULL, request, cd->listen->server_cs, UNLANG_TOP_FRAME) < 0) {
901 RERROR("Protocol failed to set 'process' function");
902 goto fail;
903 }
904
905 /*
906 * Look for conflicting / duplicate packets, but only if
907 * requested to do so.
908 */
909 if (request->async->listen->track_duplicates) {
910 request_t *old;
911
912 fr_rb_find((void **)&old, worker->dedup, request);
913 if (!old) {
914 goto insert_new;
915 }
916
917 fr_assert(old->async->listen == request->async->listen);
918 fr_assert(old->async->channel == request->async->channel);
919
920 /*
921 * There's a new packet. Do we keep the old one,
922 * or the new one? This decision is made by
923 * checking the recv_time, which is a
924 * nanosecond-resolution timer. If the time is
925 * identical, then the new packet is the same as
926 * the old one.
927 *
928 * If the new packet is a duplicate of the old
929 * one, then we can just discard the new one. We
930 * have to tell the channel that we've "eaten"
931 * this reply, so the sequence number should
932 * increase.
933 *
934 * @todo - fix the channel code to do queue
935 * depth, and not sequence / ack.
936 */
937 if (fr_time_eq(old->async->recv_time, request->async->recv_time)) {
938 RWARN("Discarding duplicate of request (%"PRIu64")", old->number);
939
940 fr_channel_null_reply(request->async->channel);
941 request_slab_release(request);
942
943 /*
944 * Signal there's a dup, and ignore the
945 * return code. We don't bother replying
946 * here, as an FD event or timer will
947 * wake up the request, and cause it to
948 * continue.
949 *
950 * @todo - the old request is NOT
951 * running, but is yielded. It MAY clean
952 * itself up, or do something...
953 */
955 worker->stats.dup++;
956
957 fr_message_done(&cd->m);
958 return;
959 }
960
961 /*
962 * Stop the old request, and decrement the number
963 * of active requests.
964 */
965 RWARN("Got conflicting packet for request (%" PRIu64 "), telling old request to stop", old->number);
966
968 worker->stats.dropped++;
969 (void) fr_rb_remove(NULL, worker->dedup, old); /* remove, but do NOT free it */
970
971 insert_new:
972 (void) fr_rb_insert(worker->dedup, request);
973 }
974
975 if (worker_request_time_tracking_start(worker, request, now) < 0) {
976 if (request->async->listen->track_duplicates) (void) fr_rb_remove(NULL, worker->dedup, request);
977 goto fail;
978 }
979
980 /*
981 * We're done with this message.
982 */
983 fr_message_done(&cd->m);
984
985 {
987
988 fr_rb_find((void **)&wl, worker->listeners, &(fr_worker_listen_t) { .listener = listen });
989 if (!wl) {
990 MEM(wl = talloc_zero(worker, fr_worker_listen_t));
991 fr_dlist_init(&wl->dlist, request_t, listen_entry);
992 wl->listener = listen;
993
994 (void) fr_rb_insert(worker->listeners, wl);
995 }
996
997 fr_dlist_insert_tail(&wl->dlist, request);
998 }
999
1000 /*
1001 * Track this request against the channel it came in on so
1002 * worker_requests_cancel() has something to walk when the
1003 * network signals CHANNEL_CLOSE.
1004 */
1005 fr_dlist_insert_tail(&wc->dlist, request->async);
1006}
1007
1008/**
1009 * Track a request_t in the "runnable" heap.
1010 * Higher priorities take precedence, followed by lower sequence numbers
1011 */
1012static fr_cmp_ret_t worker_runnable_cmp(void const *one, void const *two)
1013{
1014 request_t const *a = one, *b = two;
1015 int ret;
1016
1017 /*
1018 * Prefer higher priority packets.
1019 */
1020 ret = CMP_PREFER_LARGER(a->priority, b->priority);
1021 if (ret != 0) return ret;
1022
1023 /*
1024 * Prefer packets which are further along in their processing sequence.
1025 */
1026 ret = CMP_PREFER_LARGER(a->sequence, b->sequence);
1027 if (ret != 0) return ret;
1028
1029 /*
1030 * Smaller timestamp (i.e. earlier) is more important.
1031 */
1032 return fr_time_cmp(a->async->recv_time, b->async->recv_time);
1033}
1034
1035/**
1036 * Track a request_t in the "dedup" tree
1037 */
1038static fr_cmp_ret_t worker_dedup_cmp(void const *one, void const *two)
1039{
1040 int ret;
1041 request_t const *a = one, *b = two;
1042
1043 ret = CMP(a->async->listen, b->async->listen);
1044 if (ret) return ret;
1045
1046 return CMP(a->async->packet_ctx, b->async->packet_ctx);
1047}
1048
1049/** Destroy a worker
1050 *
1051 * The input channels are signaled, and local messages are cleaned up.
1052 *
1053 * This should be called to _EXPLICITLY_ destroy a worker, when some fatal
1054 * error has occurred on the worker side, and we need to destroy it.
1055 *
1056 * We signal all pending requests in the backlog to stop, and tell the
1057 * network side that it should not send us any more requests.
1058 *
1059 * @param[in] worker the worker to destroy.
1060 */
1062{
1063 int i, count, ret;
1064
1065// WORKER_VERIFY;
1066
1067 /*
1068 * Stop any new requests running with this interpreter
1069 */
1071
1072 /*
1073 * Destroy all of the active requests. These are ones
1074 * which are still waiting for timers or file descriptor
1075 * events.
1076 */
1077 count = 0;
1078
1079 /*
1080 * Force the timeout event to fire for all requests that
1081 * are still running.
1082 */
1083 ret = fr_timer_list_force_run(worker->timeout);
1084 if (unlikely(ret < 0)) {
1085 fr_assert_msg(0, "Failed to force run the timeout list");
1086 } else {
1087 count += ret;
1088 }
1089 worker_cancelled_run(worker);
1090
1092
1093 DEBUG("Worker is exiting - stopped %u requests", count);
1094
1095 /*
1096 * Signal the channels that we're closing.
1097 *
1098 * The other end owns the channel, and will take care of
1099 * popping messages in the TO_RESPONDER queue, and marking
1100 * them FR_MESSAGE_DONE. It will ignore the messages in
1101 * the TO_REQUESTOR queue, as we own those. They will be
1102 * automatically freed when our talloc context is freed.
1103 */
1104 for (i = 0; i < worker->config.max_channels; i++) {
1105 if (!worker->channel[i].ch) continue;
1106
1107 worker_requests_cancel(worker, &worker->channel[i]);
1108
1109 fr_assert_msg(fr_dlist_num_elements(&worker->channel[i].dlist) == 0,
1110 "Pending messages in channel after cancelling request");
1111
1113 }
1114
1115 talloc_free(worker);
1116}
1117
1118/** Tidy up a worker on normal exit
1119 */
1121{
1124 fr_assert(worker->num_channels == 0);
1125 DEBUG("Worker is exiting");
1126 talloc_free(worker);
1127}
1128
1129/** Internal request (i.e. one generated by the interpreter) is now complete
1130 *
1131 */
1132static void _worker_request_internal_init(request_t *request, void *uctx)
1133{
1134 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1135 fr_time_t now = fr_time();
1136
1137 worker_request_init(worker, request, now);
1138
1139 /*
1140 * Requests generated by the interpreter
1141 * are always marked up as internal.
1142 */
1144 if (worker_request_time_tracking_start(worker, request, now) < 0) {
1146 }
1147}
1148
1149
1150/** External request is now complete
1151 *
1152 */
1153static void _worker_request_done_external(request_t *request, UNUSED rlm_rcode_t rcode, void *uctx)
1154{
1155 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1156 fr_time_t now = fr_time();
1157
1158 /*
1159 * All external requests MUST have a listener.
1160 */
1162 fr_assert(request->async->listen != NULL);
1163
1164 /*
1165 * Only real packets are in the dedup tree. And even
1166 * then, only some of the time.
1167 */
1168 if (request->async->listen->track_duplicates && fr_rb_node_inline_in_tree(&request->dedup_node)) {
1169 (void) fr_rb_delete(worker->dedup, request);
1170 }
1171
1172 /*
1173 * If we're running a real request, then the final
1174 * indentation MUST be zero. Otherwise we skipped
1175 * something!
1176 *
1177 * Also check that the request is NOT marked as
1178 * "yielded", but is in fact done.
1179 *
1180 * @todo - check that the stack is at frame 0, otherwise
1181 * more things have gone wrong.
1182 */
1183 fr_assert_msg(request_is_internal(request) || request_is_detached(request) || (request->log.indent.unlang == 0),
1184 "Request %s bad log indentation - expected 0 got %u", request->name, request->log.indent.unlang);
1186 "Request %s is marked as yielded at end of processing", request->name);
1188 "Request %s stack depth %u > 0", request->name, unlang_interpret_stack_depth(request));
1189 RDEBUG("Done request");
1190
1191 /*
1192 * The request is done. Track that.
1193 */
1194 worker_request_time_tracking_end(worker, request, now);
1195
1196 /*
1197 * Remove it from the list of requests associated with this channel.
1198 */
1199 if (fr_dlist_entry_in_list(&request->async->entry)) {
1200 fr_worker_channel_t *wc = fr_channel_responder_uctx_get(request->async->channel);
1201
1202 fr_dlist_remove(&wc->dlist, request->async);
1203 }
1204
1205 /*
1206 * These conditions are true when the server is
1207 * exiting and we're stopping all the requests.
1208 *
1209 * This should never happen otherwise.
1210 */
1211 if (unlikely(!fr_channel_active(request->async->channel))) {
1212 fr_dlist_entry_unlink(&request->listen_entry);
1213 request_slab_release(request);
1214 return;
1215 }
1216
1217 worker_send_reply(worker, request, !unlang_request_is_cancelled(request), now);
1218 request_slab_release(request);
1219}
1220
1221/** Internal request (i.e. one generated by the interpreter) is now complete
1222 *
1223 * Whatever generated the request is now responsible for freeing it.
1224 */
1225static void _worker_request_done_internal(request_t *request, UNUSED rlm_rcode_t rcode, void *uctx)
1226{
1227 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1228
1229 worker_request_time_tracking_end(worker, request, fr_time());
1230
1231 fr_assert(!fr_heap_entry_inserted(request->runnable));
1232 fr_assert(!fr_timer_armed(request->timeout));
1233 fr_assert(!fr_dlist_entry_in_list(&request->async->entry));
1234}
1235
1236/** Detached request (i.e. one generated by the interpreter with no parent) is now complete
1237 *
1238 * As the request has no parent, then there's nothing to free it
1239 * so we have to.
1240 */
1241static void _worker_request_done_detached(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
1242{
1243 /*
1244 * No time tracking for detached requests
1245 * so we don't need to call
1246 * worker_request_time_tracking_end.
1247 */
1248 fr_assert(!fr_heap_entry_inserted(request->runnable));
1249
1250 /*
1251 * Normally worker_request_time_tracking_end
1252 * would remove the request from the time
1253 * order heap, but we need to do that for
1254 * detached requests.
1255 */
1256 TALLOC_FREE(request->timeout);
1257
1258 fr_assert(!fr_dlist_entry_in_list(&request->async->entry));
1259
1260 /*
1261 * Detached requests have to be freed by us
1262 * as nothing else can free them.
1263 *
1264 * All other requests must be freed by the
1265 * code which allocated them.
1266 */
1267 talloc_free(request);
1268}
1269
1270
1271/** Make us responsible for running the request
1272 *
1273 */
1274static void _worker_request_detach(request_t *request, void *uctx)
1275{
1276 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1277 fr_time_t now = fr_time();
1278
1279 RDEBUG4("%s - Request detaching", __FUNCTION__);
1280
1281 if (request_is_detachable(request)) {
1282 /*
1283 * End the time tracking... We don't track detached requests,
1284 * because they don't contribute for the time consumed by an
1285 * external request.
1286 */
1287 if (request->async->tracking.state == FR_TIME_TRACKING_YIELDED) {
1288 RDEBUG3("Forcing time tracking to running state, from yielded, for request detach");
1289 fr_time_tracking_resume(&request->async->tracking, now);
1290 }
1291 worker_request_time_tracking_end(worker, request, now);
1292
1293 if (request_detach(request) < 0) RPEDEBUG("Failed detaching request");
1294
1295 RDEBUG3("Request is detached");
1296 } else {
1297 fr_assert_msg(0, "Request is not detachable");
1298 }
1299
1300 return;
1301}
1302
1303/** Request is now runnable
1304 *
1305 */
1306static void _worker_request_runnable(request_t *request, void *uctx)
1307{
1308 fr_worker_t *worker = uctx;
1309
1310 RDEBUG4("%s - Request marked as runnable", __FUNCTION__);
1311 fr_heap_insert(&worker->runnable, request);
1312}
1313
1314/** Interpreter yielded request
1315 *
1316 */
1317static void _worker_request_yield(request_t *request, UNUSED void *uctx)
1318{
1319 RDEBUG4("%s - Request yielded", __FUNCTION__);
1320 if (likely(!request_is_detached(request))) fr_time_tracking_yield(&request->async->tracking, fr_time());
1321}
1322
1323/** Interpreter is starting to work on request again
1324 *
1325 */
1326static void _worker_request_resume(request_t *request, UNUSED void *uctx)
1327{
1328 RDEBUG4("%s - Request resuming", __FUNCTION__);
1329 if (likely(!request_is_detached(request))) fr_time_tracking_resume(&request->async->tracking, fr_time());
1330}
1331
1332/** Check if a request is scheduled
1333 *
1334 */
1335static bool _worker_request_scheduled(request_t const *request, UNUSED void *uctx)
1336{
1337 return fr_heap_entry_inserted(request->runnable);
1338}
1339
1340/** Update a request's priority
1341 *
1342 */
1343static void _worker_request_prioritise(request_t *request, void *uctx)
1344{
1345 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1346
1347 RDEBUG4("%s - Request priority changed", __FUNCTION__);
1348
1349 /* Extract the request from the runnable queue _if_ it's in the runnable queue */
1350 if (fr_heap_extract(&worker->runnable, request) < 0) return;
1351
1352 /* Reinsert it to re-evaluate its new priority */
1353 fr_heap_insert(&worker->runnable, request);
1354}
1355
1356/** Run a request
1357 *
1358 * Until it either yields, or is done.
1359 *
1360 * This function is also responsible for sending replies, and
1361 * cleaning up the request.
1362 *
1363 * @param[in] worker the worker
1364 * @param[in] start the current time
1365 */
1366static inline CC_HINT(always_inline) void worker_run_request(fr_worker_t *worker, fr_time_t start)
1367{
1368 request_t *request;
1369 fr_time_t now;
1370
1372
1373 now = start;
1374
1375 /*
1376 * Busy-loop running requests for 1ms. We still poll the
1377 * event loop 1000 times a second, OR when there's no
1378 * more work to do. This allows us to make progress with
1379 * ongoing requests, at the expense of sometimes ignoring
1380 * new ones.
1381 */
1382 while (fr_time_delta_lt(fr_time_sub(now, start), fr_time_delta_from_msec(1)) &&
1383 (fr_heap_pop((void **)&request, &worker->runnable) == 0) && request) {
1384
1385 REQUEST_VERIFY(request);
1386 fr_assert(!fr_heap_entry_inserted(request->runnable));
1387
1389
1390 now = fr_time();
1391 }
1392}
1393
1394/** Create a worker
1395 *
1396 * @param[in] ctx the talloc context
1397 * @param[in] name the name of this worker
1398 * @param[in] el the event list
1399 * @param[in] logger the destination for all logging messages
1400 * @param[in] lvl log level
1401 * @param[in] config various configuration parameters
1402 * @return
1403 * - NULL on error
1404 * - fr_worker_t on success
1405 */
1406fr_worker_t *fr_worker_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name, fr_log_t const *logger, fr_log_lvl_t lvl,
1408{
1409 fr_worker_t *worker;
1410
1411 worker = talloc_zero(ctx, fr_worker_t);
1412 if (!worker) {
1413nomem:
1414 fr_strerror_const("Failed allocating memory");
1415 return NULL;
1416 }
1417
1418 worker->name = talloc_strdup(worker, name); /* thread locality */
1419
1420 if (config) worker->config = *config;
1421
1422#define CHECK_CONFIG(_x, _min, _max) do { \
1423 if (!worker->config._x) worker->config._x = _min; \
1424 if (worker->config._x < _min) worker->config._x = _min; \
1425 if (worker->config._x > _max) worker->config._x = _max; \
1426 } while (0)
1427
1428#define CHECK_CONFIG_TIME_DELTA(_x, _min, _max) do { \
1429 if (fr_time_delta_lt(worker->config._x, _min)) worker->config._x = _min; \
1430 if (fr_time_delta_gt(worker->config._x, _max)) worker->config._x = _max; \
1431 } while (0)
1432
1433 CHECK_CONFIG(max_requests,1024,(1 << 30));
1434 CHECK_CONFIG(max_channels, 64, 1024);
1435 CHECK_CONFIG(reuse.child_pool_size, 4096, 65536);
1436 CHECK_CONFIG(message_set_size, 1024, 8192);
1437 CHECK_CONFIG(ring_buffer_size, (1 << 17), (1 << 20));
1439
1440 worker->channel = talloc_zero_array(worker, fr_worker_channel_t, worker->config.max_channels);
1441 if (!worker->channel) {
1442 talloc_free(worker);
1443 goto nomem;
1444 }
1445
1446 worker->thread_id = pthread_self();
1447 worker->el = el;
1448 worker->log = logger;
1449 worker->lvl = lvl;
1450
1451 /*
1452 * The worker thread starts now. Manually initialize it,
1453 * because we're tracking request time, not the time that
1454 * the worker thread is running.
1455 */
1456 memset(&worker->tracking, 0, sizeof(worker->tracking));
1457
1458 worker->aq_control = fr_atomic_queue_talloc(worker, 1024);
1459 if (!worker->aq_control) {
1460 fr_strerror_const("Failed creating atomic queue");
1461 fail:
1462 talloc_free(worker);
1463 return NULL;
1464 }
1465
1466 worker->control = fr_control_create(worker, el, worker->aq_control, 7);
1467 if (!worker->control) {
1468 fr_strerror_const_push("Failed creating control plane");
1469 goto fail;
1470 }
1471
1473 fr_strerror_const_push("Failed adding control channel");
1474 goto fail;
1475 }
1476
1478 fr_strerror_const_push("Failed adding callback for listeners");
1479 goto fail;
1480 }
1481
1482 if (fr_control_open(worker->control) < 0) {
1483 fr_strerror_const_push("Failed opening control plane");
1484 goto fail;
1485 }
1486
1487 worker->runnable = fr_heap_talloc_alloc(worker, worker_runnable_cmp, request_t, runnable, 0);
1488 if (!worker->runnable) {
1489 fr_strerror_const("Failed creating runnable heap");
1490 goto fail;
1491 }
1492
1493 worker->timeout = fr_timer_list_ordered_alloc(worker, el->tl);
1494 if (!worker->timeout) {
1495 fr_strerror_const("Failed creating timeouts list");
1496 goto fail;
1497 }
1498
1499 worker->dedup = fr_rb_inline_talloc_alloc(worker, request_t, dedup_node, worker_dedup_cmp, NULL);
1500 if (!worker->dedup) {
1501 fr_strerror_const("Failed creating de_dup tree");
1502 goto fail;
1503 }
1504
1506 if (!worker->listeners) {
1507 fr_strerror_const("Failed creating listener tree");
1508 goto fail;
1509 }
1510
1511 worker->intp = unlang_interpret_init(worker, el,
1513 .init_internal = _worker_request_internal_init,
1514
1515 .done_external = _worker_request_done_external,
1516 .done_internal = _worker_request_done_internal,
1517 .done_detached = _worker_request_done_detached,
1518
1519 .detach = _worker_request_detach,
1520 .yield = _worker_request_yield,
1521 .resume = _worker_request_resume,
1522 .mark_runnable = _worker_request_runnable,
1523
1524 .scheduled = _worker_request_scheduled,
1525 .prioritise = _worker_request_prioritise
1526 },
1527 worker);
1528 if (!worker->intp){
1529 fr_strerror_const("Failed initialising interpreter");
1530 goto fail;
1531 }
1532
1533 {
1536
1537 if (!(worker->slab = request_slab_list_alloc(worker, el, &worker->config.reuse, NULL, NULL,
1538 UNCONST(void *, worker), true, false))) {
1539 fr_strerror_const("Failed creating request slab list");
1540 goto fail;
1541 }
1542 }
1543
1545
1546 return worker;
1547}
1548
1549/** Acknowledge channel close after requests are completed
1550 *
1551 * When the channel close is acknowledged, the network frees the channel
1552 * which then causes issues with any requests still referencing the channel.
1553 */
1554static inline void worker_channel_close_ack(fr_worker_t *worker)
1555{
1556 if (unlikely(worker->num_closing > 0)) {
1557 int i;
1558 for (i = 0; i < worker->config.max_channels; i++) {
1559 if (!worker->channel[i].ch) continue;
1560 if (fr_channel_active(worker->channel[i].ch)) continue;
1561 if (fr_dlist_num_elements(&worker->channel[i].dlist) > 0) continue;
1562
1564 worker->channel[i].ch = NULL;
1565 worker->num_closing--;
1566 }
1567 }
1568}
1569
1570/** The main loop and entry point of the stand-alone worker thread.
1571 *
1572 * Where there is only one thread, the event loop runs fr_worker_pre_event() and fr_worker_post_event()
1573 * instead, And then fr_worker_post_event() takes care of calling worker_run_request() to actually run the
1574 * request.
1575 *
1576 * @param[in] worker the worker data structure to manage
1577 */
1579{
1581
1582 while (true) {
1583 bool wait_for_event;
1584 int num_events;
1585
1587
1588 /*
1589 * There are runnable requests. We still service
1590 * the event loop, but we don't wait for events.
1591 */
1592 wait_for_event = (fr_heap_num_elements(worker->runnable) == 0);
1593 if (wait_for_event) {
1595
1596 if (unlikely(worker->exiting && (worker_num_requests(worker) == 0))) break;
1597
1598 DEBUG4("Ready to process requests");
1599 }
1600
1601 /*
1602 * Check the event list. If there's an error
1603 * (e.g. exit), we stop looping and clean up.
1604 */
1605 DEBUG4("Gathering events - %s", wait_for_event ? "will wait" : "Will not wait");
1606 num_events = fr_event_corral(worker->el, fr_time(), wait_for_event);
1607 if (num_events < 0) {
1608 if (fr_event_loop_exiting(worker->el)) {
1609 DEBUG4("Event loop exiting");
1610 break;
1611 }
1612
1613 PERROR("Failed retrieving events");
1614 break;
1615 }
1616
1617 DEBUG4("%u event(s) pending", num_events);
1618
1619 /*
1620 * Service outstanding events.
1621 */
1622 if (num_events > 0) {
1623 DEBUG4("Servicing event(s)");
1624 fr_event_service(worker->el);
1625 }
1626
1627 /*
1628 * Run any outstanding requests.
1629 */
1630 worker_run_request(worker, fr_time());
1631 }
1632}
1633
1634/** Pre-event handler
1635 *
1636 * This should be run ONLY in single-threaded mode!
1637 */
1639{
1640 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1641 request_t *request;
1642
1643 request = fr_heap_peek(worker->runnable);
1644 if (!request) return 0;
1645
1646 /*
1647 * There's work to do. Tell the event handler to poll
1648 * for IO / timers, but also immediately return to the
1649 * calling function, which has more work to do.
1650 */
1651 return 1;
1652}
1653
1654
1655/** Post-event handler
1656 *
1657 * This should be run ONLY in single-threaded mode!
1658 */
1660{
1661 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1662
1663 worker_run_request(worker, fr_time()); /* Event loop time can be too old, and trigger asserts */
1664}
1665
1667{
1668 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1669
1670 return worker->num_closing;
1671}
1672
1674{
1675 fr_worker_t *worker = talloc_get_type_abort(uctx, fr_worker_t);
1676
1678}
1679
1680/** Add events to the loop which will send close ack to the network
1681 */
1683{
1684 if (fr_event_pre_insert(worker->el, fr_worker_pre_close_event, worker) < 0) {
1685 fr_strerror_const("Failed adding close pre-event to event list");
1686 return -1;
1687 }
1688 if (fr_event_post_insert(worker->el, fr_worker_post_close_event, worker) < 0) {
1689 fr_strerror_const("Failed adding close post-event to event list");
1690 return -1;
1691 }
1692 return 0;
1693}
1694
1695/** Print debug information about the worker structure
1696 *
1697 * @param[in] worker the worker
1698 * @param[in] fp the file where the debug output is printed.
1699 */
1700void fr_worker_debug(fr_worker_t *worker, FILE *fp)
1701{
1703
1704 fprintf(fp, "\tnum_channels = %d\n", worker->num_channels);
1705 fprintf(fp, "\tstats.in = %" PRIu64 "\n", worker->stats.in);
1706
1707 fprintf(fp, "\tcalculated (predicted) total CPU time = %" PRIu64 "\n",
1708 fr_time_delta_unwrap(worker->predicted) * worker->stats.in);
1709 if (worker->stats.in) {
1710 fprintf(fp, "\tcalculated (counted) per request time = %" PRIu64 "\n",
1712 }
1713
1714 fr_time_tracking_debug(&worker->tracking, fp);
1715
1716}
1717
1718/** Create a channel to the worker
1719 *
1720 * Called by the master (i.e. network) thread when it needs to create
1721 * a new channel to a particular worker.
1722 *
1723 * @param[in] worker the worker
1724 * @param[in] ctx the context in which the channel will be created
1725 * @param[in] master the control plane of the master
1726 * @param[in] uctx to include in the channel open message
1727 */
1728fr_channel_t *fr_worker_channel_create(fr_worker_t *worker, TALLOC_CTX *ctx, fr_control_t *master, void *uctx)
1729{
1730 fr_channel_t *ch;
1731 pthread_t id;
1732 bool same;
1733
1735
1736 id = pthread_self();
1737 same = (pthread_equal(id, worker->thread_id) != 0);
1738
1739 ch = fr_channel_create(ctx, master, worker->control, same);
1740 if (!ch) return NULL;
1741
1742
1743 /*
1744 * Tell the worker about the channel
1745 */
1746 if (fr_channel_signal_open(ch, uctx) < 0) {
1747 talloc_free(ch);
1748 return NULL;
1749 }
1750
1751 return ch;
1752}
1753
1755{
1756 fr_ring_buffer_t *rb;
1757
1758 /*
1759 * Skip a bunch of work if we're already in the worker thread.
1760 */
1761 if (is_worker_thread(worker)) {
1762 return fr_worker_listen_cancel_self(worker, li);
1763 }
1764
1765 rb = fr_worker_rb_init();
1766 if (!rb) return -1;
1767
1768 return fr_control_message_send(worker->control, rb, FR_CONTROL_ID_LISTEN_DEAD, &li, sizeof(li));
1769}
1770
1771#ifdef WITH_VERIFY_PTR
1772/** Verify the worker data structures.
1773 *
1774 * @param[in] worker the worker
1775 */
1776static void worker_verify(fr_worker_t *worker)
1777{
1778 int i;
1779
1780 (void) talloc_get_type_abort(worker, fr_worker_t);
1781 fr_atomic_queue_verify(worker->aq_control);
1782
1783 fr_assert(worker->control != NULL);
1784 (void) talloc_get_type_abort(worker->control, fr_control_t);
1785
1786 fr_assert(worker->el != NULL);
1787 (void) talloc_get_type_abort(worker->el, fr_event_list_t);
1788
1789 fr_assert(worker->runnable != NULL);
1790 (void) talloc_get_type_abort(worker->runnable, fr_heap_t);
1791
1792 fr_assert(worker->dedup != NULL);
1793 (void) talloc_get_type_abort(worker->dedup, fr_rb_tree_t);
1794
1795 for (i = 0; i < worker->config.max_channels; i++) {
1796 if (!worker->channel[i].ch) continue;
1797
1798 (void) talloc_get_type_abort(worker->channel[i].ch, fr_channel_t);
1799 }
1800}
1801#endif
1802
1803int fr_worker_stats(fr_worker_t const *worker, int num, uint64_t *stats)
1804{
1805 if (num < 0) return -1;
1806 if (num == 0) return 0;
1807
1808 stats[0] = worker->stats.in;
1809 if (num >= 2) stats[1] = worker->stats.out;
1810 if (num >= 3) stats[2] = worker->stats.dup;
1811 if (num >= 4) stats[3] = worker->stats.dropped;
1812 if (num >= 5) stats[4] = worker->num_naks;
1813 if (num >= 6) stats[5] = worker->num_active;
1814
1815 if (num <= 6) return num;
1816
1817 return 6;
1818}
1819
1821{
1822 return &worker->config;
1823}
1824
1825static int cmd_stats_worker(FILE *fp, UNUSED FILE *fp_err, void *ctx, fr_cmd_info_t const *info)
1826{
1827 fr_worker_t const *worker = ctx;
1828 fr_time_delta_t when;
1829
1830 if ((info->argc == 0) || (strcmp(info->argv[0], "count") == 0)) {
1831 fprintf(fp, "count.in\t\t\t%" PRIu64 "\n", worker->stats.in);
1832 fprintf(fp, "count.out\t\t\t%" PRIu64 "\n", worker->stats.out);
1833 fprintf(fp, "count.dup\t\t\t%" PRIu64 "\n", worker->stats.dup);
1834 fprintf(fp, "count.dropped\t\t\t%" PRIu64 "\n", worker->stats.dropped);
1835 fprintf(fp, "count.naks\t\t\t%" PRIu64 "\n", worker->num_naks);
1836 fprintf(fp, "count.active\t\t\t%" PRIu64 "\n", worker->num_active);
1837 fprintf(fp, "count.runnable\t\t\t%u\n", fr_heap_num_elements(worker->runnable));
1838 }
1839
1840 if ((info->argc == 0) || (strcmp(info->argv[0], "cpu") == 0)) {
1841 when = worker->predicted;
1842 fprintf(fp, "cpu.request_time_rtt\t\t%.9f\n", fr_time_delta_unwrap(when) / (double)NSEC);
1843
1844 when = worker->tracking.running_total;
1845 if (fr_time_delta_ispos(when) && (worker->stats.in > worker->stats.dropped)) {
1846 when = fr_time_delta_div(when, fr_time_delta_wrap(worker->stats.in - worker->stats.dropped));
1847 }
1848 fprintf(fp, "cpu.average_request_time\t%.9f\n", fr_time_delta_unwrap(when) / (double)NSEC);
1849
1850 when = worker->tracking.running_total;
1851 fprintf(fp, "cpu.used\t\t\t%.6f\n", fr_time_delta_unwrap(when) / (double)NSEC);
1852
1853 when = worker->tracking.waiting_total;
1854 fprintf(fp, "cpu.waiting\t\t\t%.3f\n", fr_time_delta_unwrap(when) / (double)NSEC);
1855
1856 fr_time_elapsed_fprint(fp, &worker->cpu_time, "cpu.requests", 4);
1857 fr_time_elapsed_fprint(fp, &worker->wall_clock, "time.requests", 4);
1858 }
1859
1860 return 0;
1861}
1862
1864 {
1865 .parent = "stats",
1866 .name = "worker",
1867 .help = "Statistics for workers threads.",
1868 .read_only = true
1869 },
1870
1871 {
1872 .parent = "stats worker",
1873 .add_name = true,
1874 .name = "self",
1875 .syntax = "[(count|cpu)]",
1876 .func = cmd_stats_worker,
1877 .help = "Show statistics for a specific worker thread.",
1878 .read_only = true
1879 },
1880
1882};
static int const char char buffer[256]
Definition acutest.h:576
fr_io_encode_t encode
Pack fr_pair_ts back into a byte array.
Definition app_io.h:55
size_t default_reply_size
same for replies
Definition app_io.h:40
fr_io_nak_t nak
Function to send a NAK.
Definition app_io.h:62
fr_io_decode_t decode
Translate raw bytes into fr_pair_ts and metadata.
Definition app_io.h:54
fr_io_decode_t decode
Translate raw bytes into fr_pair_ts and metadata.
Definition application.h:80
fr_io_encode_t encode
Pack fr_pair_ts back into a byte array.
Definition application.h:85
#define _Thread_local
Definition atexit.h:213
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:224
fr_atomic_queue_t * fr_atomic_queue_talloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
Structure to hold the atomic queue.
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:560
#define NDEBUG_UNUSED
Definition build.h:395
#define CMP_PREFER_LARGER(_a, _b)
Evaluates to -1 for a > b, and +1 for a < b.
Definition build.h:109
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
unlang_action_t unlang_call_push(unlang_result_t *p_result, request_t *request, CONF_SECTION *server_cs, bool top_frame)
Push a virtual server CONF_SECTION as a call frame onto the stack.
Definition call.c:151
fr_table_num_sorted_t const channel_signals[]
Definition channel.c:152
unsigned int fr_channel_responder_discard(fr_channel_t *ch)
Discard any requests the requestor queued but we never received.
Definition channel.c:877
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:182
int fr_channel_signal_open(fr_channel_t *ch, void *uctx)
Send a channel to a responder.
Definition channel.c:994
void * fr_channel_responder_uctx_get(fr_channel_t *ch)
Get responder-specific data from a channel.
Definition channel.c:939
bool fr_channel_recv_request(fr_channel_t *ch)
Receive a request message from the channel.
Definition channel.c:471
int fr_channel_null_reply(fr_channel_t *ch)
Don't send a reply message into the channel.
Definition channel.c:627
void fr_channel_responder_uctx_add(fr_channel_t *ch, void *uctx)
Add responder-specific data to a channel.
Definition channel.c:927
int fr_channel_set_recv_request(fr_channel_t *ch, fr_channel_recv_callback_t recv_request, void *uctx)
Definition channel.c:980
int fr_channel_send_reply(fr_channel_t *ch, fr_channel_data_t *cd)
Send a reply message into the channel.
Definition channel.c:510
fr_channel_event_t fr_channel_service_message(fr_time_t when, fr_channel_t **p_channel, void **uctx_out, void const *data, size_t data_size)
Service a control-plane message.
Definition channel.c:689
bool fr_channel_active(fr_channel_t *ch)
Check if a channel is active.
Definition channel.c:832
int fr_channel_responder_ack_close(fr_channel_t *ch)
Acknowledge that the channel is closing.
Definition channel.c:898
A full channel, which consists of two ends.
Definition channel.c:143
fr_message_t m
the message header
Definition channel.h:107
fr_channel_event_t
Definition channel.h:69
@ FR_CHANNEL_NOOP
Definition channel.h:76
@ FR_CHANNEL_EMPTY
Definition channel.h:77
@ FR_CHANNEL_CLOSE
Definition channel.h:74
@ FR_CHANNEL_ERROR
Definition channel.h:70
@ FR_CHANNEL_DATA_READY_REQUESTOR
Definition channel.h:72
@ FR_CHANNEL_OPEN
Definition channel.h:73
@ FR_CHANNEL_DATA_READY_RESPONDER
Definition channel.h:71
void * packet_ctx
Packet specific context for holding client information, and other proto_* specific information that n...
Definition channel.h:144
fr_listen_t * listen
for tracking packet transport, etc.
Definition channel.h:148
#define FR_CONTROL_ID_CHANNEL
Definition channel.h:67
uint32_t priority
Priority of this packet.
Definition channel.h:142
Channel information which is added to a message.
Definition channel.h:106
char const * parent
e.g. "show module"
Definition command.h:52
#define CMD_TABLE_END
Definition command.h:62
#define FR_CONTROL_MAX_SIZE
Definition control.h:51
#define FR_CONTROL_MAX_MESSAGES
Definition control.h:50
#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
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition dlist.h:145
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition dlist.h:128
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:921
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
static void fr_dlist_entry_init(fr_dlist_t *entry)
Initialise a linked list without metadata.
Definition dlist.h:120
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
Head of a doubly linked list.
Definition dlist.h:51
bool unlang_finally_entered(request_t *request)
Definition finally.c:191
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:149
int fr_heap_pop(void **out, fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:359
int fr_heap_extract(fr_heap_t **hp, void *data)
Remove a node from the heap.
Definition heap.c:259
static void * fr_heap_peek(fr_heap_t *h)
Return the item from the top of the heap but don't pop it.
Definition heap.h:138
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition heap.h:126
static unsigned int fr_heap_num_elements(fr_heap_t *h)
Return the number of elements in the heap.
Definition heap.h:181
#define fr_heap_talloc_alloc(_ctx, _cmp, _talloc_type, _field, _init)
Creates a heap that verifies elements are of a specific talloc type.
Definition heap.h:117
The main heap structure.
Definition heap.h:68
talloc_free(hp)
rlm_rcode_t unlang_interpret(request_t *request, bool running)
Run the interpreter for a current request.
Definition interpret.c:1302
void unlang_interpret_set(request_t *request, unlang_interpret_t *intp)
Set a specific interpreter for a request.
Definition interpret.c:2519
int unlang_interpret_stack_depth(request_t *request)
Return the depth of the request's stack.
Definition interpret.c:1921
void unlang_interpret_set_thread_default(unlang_interpret_t *intp)
Set the default interpreter for this thread.
Definition interpret.c:2550
unlang_interpret_t * unlang_interpret_init(TALLOC_CTX *ctx, fr_event_list_t *el, unlang_request_func_t *funcs, void *uctx)
Initialize a unlang compiler / interpret.
Definition interpret.c:2478
bool unlang_request_is_cancelled(request_t const *request)
Return whether a request has been cancelled.
Definition interpret.c:1971
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition interpret.c:1789
bool unlang_interpret_is_resumable(request_t *request)
Check if a request as resumable.
Definition interpret.c:1990
#define UNLANG_REQUEST_RESUME
Definition interpret.h:48
#define UNLANG_TOP_FRAME
Definition interpret.h:36
External functions provided by the owner of the interpret.
Definition interpret.h:116
uint64_t out
Definition base.h:43
uint64_t dup
Definition base.h:44
uint64_t dropped
Definition base.h:45
uint64_t in
Definition base.h:42
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_atomic_queue_t *aq, size_t num_callbacks)
Create a control-plane signaling path.
Definition control.c:152
int fr_control_open(fr_control_t *c)
Open the control-plane signalling path.
Definition control.c:176
int fr_control_message_send(fr_control_t *c, fr_ring_buffer_t *rb, uint32_t id, void *data, size_t data_size)
Send a control-plane message.
Definition control.c:355
int fr_control_callback_add(fr_control_t **c, uint32_t id, fr_control_callback_t callback, void *uctx)
Register a callback for an ID.
Definition control.c:444
The control structure.
Definition control.c:76
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG3(_fmt,...)
Definition log.h:271
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define PWARN(_fmt,...)
Definition log.h:232
#define RERROR(fmt,...)
Definition log.h:315
#define DEBUG4(_fmt,...)
Definition log.h:272
#define RPERROR(fmt,...)
Definition log.h:319
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RDEBUG4(fmt,...)
Definition log.h:361
#define RATE_LIMIT_GLOBAL(_log, _fmt,...)
Rate limit messages using a global limiting entry.
Definition log.h:658
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2205
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition event.c:2073
#define fr_time()
Definition event.c:60
bool fr_event_loop_exiting(fr_event_list_t *el)
Check to see whether the event loop is in the process of exiting.
Definition event.c:2394
int fr_event_pre_insert(fr_event_list_t *el, fr_event_status_cb_t callback, void *uctx)
Add a pre-event callback to the event list.
Definition event.c:1981
int fr_event_post_insert(fr_event_list_t *el, fr_event_post_cb_t callback, void *uctx)
Add a post-event callback to the event list.
Definition event.c:2028
Stores all information relating to an event list.
Definition event.c:377
fr_log_lvl_t
Definition log.h:64
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition packet.c:38
request_t * request
back-pointer to the owning request so anything that pops this async off its dlist can reach the reque...
Definition listen.h:71
void const * app_instance
Definition listen.h:39
size_t default_message_size
copied from app_io, but may be changed
Definition listen.h:56
fr_app_t const * app
Definition listen.h:38
void const * app_io_instance
I/O path configuration context.
Definition listen.h:33
CONF_SECTION * server_cs
CONF_SECTION of the server.
Definition listen.h:42
fr_dict_t const * dict
dictionary for this listener
Definition listen.h:30
fr_app_io_t const * app_io
I/O path functions.
Definition listen.h:32
Minimal data structure to use the new code.
Definition listen.h:63
unsigned int uint32_t
long int ssize_t
int fr_message_done(fr_message_t *m)
Mark a message as done.
Definition message.c:195
fr_message_t * fr_message_and_data_commit(fr_message_set_t *ms, fr_message_t *m, size_t total_size)
Commit a previously reserved message, allocating exactly total_size bytes of packet data.
Definition message.c:1051
void fr_message_set_gc(fr_message_set_t *ms)
Garbage collect the message set.
Definition message.c:1321
fr_message_t * fr_message_and_data_reserve(fr_message_set_t *ms, size_t reserve_size)
Reserve a message.
Definition message.c:973
A Message set, composed of message headers and ring buffer data.
Definition message.c:94
size_t rb_size
cache-aligned size in the ring buffer
Definition message.h:51
fr_time_t when
when this message was sent
Definition message.h:47
uint8_t * data
pointer to the data in the ring buffer
Definition message.h:49
size_t data_size
size of the data in the ring buffer
Definition message.h:50
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
static const conf_parser_t config[]
Definition base.c:162
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG(fmt,...)
#define DEBUG2(fmt,...)
static void send_reply(int sockfd, fr_channel_data_t *reply)
int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:718
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:767
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:244
static bool fr_rb_node_inline_in_tree(fr_rb_node_t const *node)
Check to see if an item is in a tree by examining its inline fr_rb_node_t.
Definition rb.h:312
The main red black tree structure.
Definition rb.h:71
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:56
int request_slab_deinit(request_t *request)
Callback for slabs to deinitialise the request.
Definition request.c:385
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition request.c:544
#define REQUEST_VERIFY(_x)
Definition request.h:310
#define request_is_detached(_x)
Definition request.h:187
#define request_is_external(_x)
Definition request.h:185
#define request_is_internal(_x)
Definition request.h:186
@ REQUEST_TYPE_EXTERNAL
A request received on the wire.
Definition request.h:179
#define request_is_detachable(_x)
Definition request.h:188
#define REQUEST_POOL_NUM_OBJECTS
Definition request.h:68
#define request_init(_ctx, _type, _args)
Definition request.h:322
#define REQUEST_POOL_SIZE
Definition request.h:81
Optional arguments for initialising requests.
Definition request.h:288
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition ring_buffer.c:64
static char const * name
@ FR_SIGNAL_DUP
A duplicate request was received.
Definition signal.h:44
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
Definition slab.h:124
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition slab.h:75
unsigned int num_children
How many child allocations are expected off each element.
Definition slab.h:48
size_t child_pool_size
Size of pool space to be allocated to each element.
Definition slab.h:49
@ memory_order_seq_cst
Definition stdatomic.h:132
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:302
#define _Atomic(T)
Definition stdatomic.h:77
Definition log.h:93
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:288
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
Definition testlib.h:54
void fr_time_elapsed_update(fr_time_elapsed_t *elapsed, fr_time_t start, fr_time_t end)
Definition time.c:577
void fr_time_elapsed_fprint(FILE *fp, fr_time_elapsed_t const *elapsed, char const *prefix, int tab_offset)
Definition time.c:622
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
#define fr_time_delta_lt(_a, _b)
Definition time.h:285
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_eq(_a, _b)
Definition time.h:241
#define NSEC
Definition time.h:379
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
static fr_time_delta_t fr_time_delta_div(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:267
static int8_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
Definition time.h:916
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ FR_TIME_TRACKING_YIELDED
We're currently tracking time in the yielded state.
static void fr_time_tracking_yield(fr_time_tracking_t *tt, fr_time_t now)
Transition to the yielded state, recording the time we just spent running.
static void fr_time_tracking_end(fr_time_delta_t *predicted, fr_time_tracking_t *tt, fr_time_t now)
End time tracking for this entity.
fr_time_delta_t waiting_total
total time spent waiting
fr_time_delta_t running_total
total time spent running
static void fr_time_tracking_start(fr_time_tracking_t *parent, fr_time_tracking_t *tt, fr_time_t now)
Start time tracking for a tracked entity.
static void fr_time_tracking_resume(fr_time_tracking_t *tt, fr_time_t now)
Track that a request resumed.
static void fr_time_tracking_debug(fr_time_tracking_t *tt, FILE *fp)
Print debug information about the time tracking structure.
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
Definition timer.c:1154
fr_timer_list_t * fr_timer_list_ordered_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new sorted event timer list.
Definition timer.c:1296
int fr_timer_list_force_run(fr_timer_list_t *tl)
Forcibly run all events in an event loop.
Definition timer.c:922
An event timer list.
Definition timer.c:49
#define fr_timer_in(...)
Definition timer.h:87
static bool fr_timer_armed(fr_timer_t *ev)
Definition timer.h:120
static fr_event_list_t * el
static unsigned count
Definition unittest.c:47
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1367
static void worker_channel_callback(void const *data, size_t data_size, fr_time_t now, void *uctx)
Handle a control plane message sent to the worker via a channel.
Definition worker.c:260
fr_heap_t * runnable
current runnable requests which we've spent time processing
Definition worker.c:111
static void worker_request_time_tracking_end(fr_worker_t *worker, request_t *request, fr_time_t now)
Definition worker.c:606
static void _worker_request_yield(request_t *request, UNUSED void *uctx)
Interpreter yielded request.
Definition worker.c:1317
fr_event_list_t * el
our event list
Definition worker.c:106
int fr_worker_pre_event(UNUSED fr_time_t now, UNUSED fr_time_delta_t wake, void *uctx)
Pre-event handler.
Definition worker.c:1638
static void worker_send_reply(fr_worker_t *worker, request_t *request, bool do_not_respond, fr_time_t now)
Send a response packet to the network side.
Definition worker.c:623
fr_channel_t * fr_worker_channel_create(fr_worker_t *worker, TALLOC_CTX *ctx, fr_control_t *master, void *uctx)
Create a channel to the worker.
Definition worker.c:1728
fr_rb_tree_t * listeners
so we can cancel requests when a listener goes away
Definition worker.c:118
static void worker_run_request(fr_worker_t *worker, fr_time_t start)
Run a request.
Definition worker.c:1366
int num_closing
number of channels closing
Definition worker.c:109
static void worker_exit(fr_worker_t *worker)
Definition worker.c:248
#define WORKER_VERIFY
Definition worker.c:68
bool was_sleeping
used to suppress multiple sleep signals in a row
Definition worker.c:130
static int cmd_stats_worker(FILE *fp, UNUSED FILE *fp_err, void *ctx, fr_cmd_info_t const *info)
Definition worker.c:1825
static void _worker_request_runnable(request_t *request, void *uctx)
Request is now runnable.
Definition worker.c:1306
static char * itoa_internal(TALLOC_CTX *ctx, uint64_t number)
Definition worker.c:752
fr_worker_t * fr_worker_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name, fr_log_t const *logger, fr_log_lvl_t lvl, fr_worker_config_t *config)
Create a worker.
Definition worker.c:1406
fr_worker_channel_t * channel
list of channels
Definition worker.c:133
char const * name
name of this worker
Definition worker.c:92
uint64_t num_active
number of active requests
Definition worker.c:125
fr_cmd_table_t cmd_worker_table[]
Definition worker.c:1863
static int worker_request_time_tracking_start(fr_worker_t *worker, request_t *request, fr_time_t now)
Start time tracking for a request, and mark it as runnable.
Definition worker.c:576
int fr_worker_stats(fr_worker_t const *worker, int num, uint64_t *stats)
Definition worker.c:1803
static int _worker_request_deinit(request_t *request, UNUSED void *uctx)
Definition worker.c:806
static void _worker_request_done_detached(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
Detached request (i.e.
Definition worker.c:1241
static void _worker_request_resume(request_t *request, UNUSED void *uctx)
Interpreter is starting to work on request again.
Definition worker.c:1326
static fr_cmp_ret_t worker_dedup_cmp(void const *one, void const *two)
Track a request_t in the "dedup" tree.
Definition worker.c:1038
fr_rb_tree_t * dedup
de-dup tree
Definition worker.c:116
fr_atomic_queue_t * aq_control
atomic queue for control messages sent to me
Definition worker.c:102
static void worker_nak(fr_worker_t *worker, fr_channel_data_t *cd, fr_time_t now)
Send a NAK to the network thread.
Definition worker.c:443
static void worker_request_name_number(request_t *request)
Definition worker.c:793
static void _worker_request_timeout(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t when, void *uctx)
Enforce max_request_time.
Definition worker.c:556
fr_log_t const * log
log destination
Definition worker.c:99
fr_io_stats_t stats
input / output stats
Definition worker.c:120
#define CHECK_CONFIG(_x, _min, _max)
static void _worker_request_detach(request_t *request, void *uctx)
Make us responsible for running the request.
Definition worker.c:1274
static int _fr_worker_rb_free(void *arg)
Definition worker.c:163
fr_time_tracking_t tracking
how much time the worker has spent doing things.
Definition worker.c:128
static void _worker_request_done_external(request_t *request, UNUSED rlm_rcode_t rcode, void *uctx)
External request is now complete.
Definition worker.c:1153
void fr_worker_destroy(fr_worker_t *worker)
Destroy a worker.
Definition worker.c:1061
static fr_cmp_ret_t worker_runnable_cmp(void const *one, void const *two)
Track a request_t in the "runnable" heap.
Definition worker.c:1012
uint64_t num_naks
number of messages which were nak'd
Definition worker.c:124
static void worker_requests_cancel(fr_worker_t *worker, fr_worker_channel_t *ch)
Definition worker.c:233
static void worker_request_init(fr_worker_t *worker, request_t *request, fr_time_t now)
Initialize various request fields needed by the worker.
Definition worker.c:775
fr_worker_config_t config
external configuration
Definition worker.c:93
fr_listen_t const * listener
incoming packets
Definition worker.c:139
unlang_interpret_t * intp
Worker's local interpreter.
Definition worker.c:95
static int fr_worker_listen_cancel_self(fr_worker_t *worker, fr_listen_t const *li)
Definition worker.c:394
static void worker_stop_request(request_t *request)
Signal the unlang interpreter that it needs to stop running the request.
Definition worker.c:537
static void _worker_request_prioritise(request_t *request, void *uctx)
Update a request's priority.
Definition worker.c:1343
bool exiting
are we exiting?
Definition worker.c:131
fr_log_lvl_t lvl
log level
Definition worker.c:100
static int fr_worker_pre_close_event(UNUSED fr_time_t now, UNUSED fr_time_delta_t wake, void *uctx)
Definition worker.c:1666
int num_channels
actual number of channels
Definition worker.c:108
fr_time_delta_t max_request_time
maximum time a request can be processed
Definition worker.c:114
static void fr_worker_post_close_event(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Definition worker.c:1673
static void worker_recv_request(fr_channel_t *ch, fr_channel_data_t *cd, void *uctx)
Callback which handles a message being received on the worker side.
Definition worker.c:204
static void worker_request_bootstrap(fr_worker_channel_t *wc, fr_channel_data_t *cd, fr_time_t now)
Definition worker.c:811
fr_time_elapsed_t cpu_time
histogram of total CPU time per request
Definition worker.c:121
fr_rb_node_t node
in tree of listeners
Definition worker.c:141
int fr_worker_listen_cancel(fr_worker_t *worker, fr_listen_t const *li)
Definition worker.c:1754
void fr_worker_post_event(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Post-event handler.
Definition worker.c:1659
fr_dlist_head_t dlist
of requests associated with this listener.
Definition worker.c:147
int fr_worker_close_event_insert(fr_worker_t *worker)
Add events to the loop which will send close ack to the network.
Definition worker.c:1682
void fr_worker(fr_worker_t *worker)
The main loop and entry point of the stand-alone worker thread.
Definition worker.c:1578
request_slab_list_t * slab
slab allocator for request_t
Definition worker.c:135
static uint32_t worker_num_requests(fr_worker_t *worker)
Definition worker.c:801
fr_time_delta_t predicted
How long we predict a request will take to execute.
Definition worker.c:127
pthread_t thread_id
my thread ID
Definition worker.c:97
fr_time_elapsed_t wall_clock
histogram of wall clock time per request
Definition worker.c:122
static bool is_worker_thread(fr_worker_t const *worker)
Definition worker.c:190
fr_worker_config_t const * fr_worker_config(fr_worker_t *worker)
Definition worker.c:1820
fr_worker_channel_t
Definition worker.c:86
fr_timer_list_t * timeout
Track when requests timeout using a dlist.
Definition worker.c:113
static void worker_channel_close_ack(fr_worker_t *worker)
Acknowledge channel close after requests are completed.
Definition worker.c:1554
static fr_ring_buffer_t * fr_worker_rb_init(void)
Initialise thread local storage.
Definition worker.c:172
void fr_worker_exit(fr_worker_t *worker)
Tidy up a worker on normal exit.
Definition worker.c:1120
fr_control_t * control
the control plane
Definition worker.c:104
static bool _worker_request_scheduled(request_t const *request, UNUSED void *uctx)
Check if a request is scheduled.
Definition worker.c:1335
static void _worker_request_done_internal(request_t *request, UNUSED rlm_rcode_t rcode, void *uctx)
Internal request (i.e.
Definition worker.c:1225
static void worker_listen_cancel_callback(void const *data, NDEBUG_UNUSED size_t data_size, UNUSED fr_time_t now, void *uctx)
A socket is going away, so clean up any requests which use this socket.
Definition worker.c:421
static int worker_cancelled_run(fr_worker_t *worker)
Definition worker.c:215
void fr_worker_debug(fr_worker_t *worker, FILE *fp)
Print debug information about the worker structure.
Definition worker.c:1700
static void _worker_request_internal_init(request_t *request, void *uctx)
Internal request (i.e.
Definition worker.c:1132
static fr_cmp_ret_t worker_listener_cmp(void const *one, void const *two)
Definition worker.c:151
#define CHECK_CONFIG_TIME_DELTA(_x, _min, _max)
A worker which takes packets from a master, and processes them.
Definition worker.c:91
#define FR_CONTROL_ID_LISTEN_DEAD
Definition worker.h:40
int max_requests
max requests this worker will handle
Definition worker.h:69
int max_channels
maximum number of channels
Definition worker.h:71
fr_slab_config_t reuse
slab allocator configuration
Definition worker.h:78
fr_time_delta_t max_request_time
maximum time a request can be processed
Definition worker.h:76