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