The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
control_test.c
Go to the documentation of this file.
1/*
2 * control_test.c Tests for control planes
3 *
4 * Version: $Id: ec016b71088a9227871f7a05f0089a2daf98acf3 $
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 * @copyright 2016 Alan DeKok (aland@freeradius.org)
21 */
22
23RCSID("$Id: ec016b71088a9227871f7a05f0089a2daf98acf3 $")
24
25#include <freeradius-devel/server/base.h>
26#include <freeradius-devel/io/control.h>
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/util/syserror.h>
29#include <freeradius-devel/util/talloc.h>
30#include <freeradius-devel/util/semaphore.h>
31
32#include <sys/event.h>
33#include <pthread.h>
34
35#ifdef HAVE_GETOPT_H
36# include <getopt.h>
37#endif
38
39#undef MEM
40#define MEM(x) if (!(x)) { fprintf(stderr, "%s[%u] OUT OF MEMORY\n", __FILE__, __LINE__); _exit(EXIT_FAILURE); }
41#define MPRINT1 if (debug_lvl) printf
42#define MPRINT2 if (debug_lvl >= 2) printf
43#define MPRINT3 if (debug_lvl >= 3) printf
44#define CONTROL_MAGIC 0xabcd6809
45
46typedef struct {
47 size_t id;
50
51static int debug_lvl = 0;
53static size_t max_messages = 10;
54static size_t num_workers = 1;
55static int aq_size = 16;
56static fr_control_t **control = NULL;
57static fr_event_list_t *el = NULL;
58static bool single_aq = true;
59static size_t num_aq = 1;
60
61/**********************************************************************/
62
63static NEVER_RETURNS void usage(void)
64{
65 fprintf(stderr, "usage: control_test [OPTS]\n");
66 fprintf(stderr, " -m <messages> Send number of messages.\n");
67 fprintf(stderr, " -w <workers> Number of workers.\n");
68 fprintf(stderr, " -q Use per-worker atomic queues.\n");
69 fprintf(stderr, " -x Debugging mode.\n");
70
71 fr_exit_now(EXIT_SUCCESS);
72}
73
74typedef struct {
76 size_t counter;
77 size_t worker;
79
80typedef struct {
83
84static void recv_control_callback(void *ctx, void const *data, size_t data_size, UNUSED fr_time_t now)
85{
86 my_message_t const *m = data;
87 master_ctx_t *master_ctx = ctx;
88
90 fr_assert(data_size == sizeof(*m));
91
92 MPRINT2("Master got worker %ld message %zu, size %ld.\n", m->worker, m->counter, data_size);
93 if (m->counter == (max_messages - 1)) MPRINT1("Master seen all messages from worker %ld\n", m->worker);
94 master_ctx->num_messages++;
95}
96
97static void *control_master(void *arg)
98{
99 TALLOC_CTX *ctx;
100 master_ctx_t *master_ctx;
101 size_t i;
102 fr_time_t start;
103 fr_sem_t *master_sem = arg;
104
105 MEM(ctx = talloc_init_const("control_master"));
106
107 master_ctx = talloc_zero(ctx, master_ctx_t);
108
109 MPRINT1("Master started.\n");
110
111 for (i = 0; i < num_aq; i++) {
114 }
115
116 sem_post(master_sem);
117
118 start = fr_time();
119 while (master_ctx->num_messages < (max_messages * num_workers)) {
120 int num_events;
121
122 MPRINT3("Master waiting for events (seen %ld).\n", master_ctx->num_messages);
123
124 num_events = fr_event_corral(el, fr_time(), true);
125 if (num_events < 0) {
126 fprintf(stderr, "Failed reading kevent: %s\n", fr_syserror(errno));
127 fr_exit_now(EXIT_FAILURE);
128 }
129 if (num_events > 0) {
131 }
132 }
133 MPRINT1("Master exiting. Seen %zu messages. %.2f per second\n",
134 master_ctx->num_messages,
135 ((double)master_ctx->num_messages * 1e6) /
137
138 talloc_free(ctx);
139
140 return NULL;
141}
142
143static void *control_worker(void *arg)
144{
145 size_t i, aq_num;
146 TALLOC_CTX *ctx;
147 worker_args_t *wa = (worker_args_t *) arg;
148
149 MEM(ctx = talloc_init_const("control_worker"));
150
151 aq_num = single_aq ? 0 : wa->id;
152
153 MPRINT1("\tWorker %ld started using queue %ld.\n", wa->id, aq_num);
154
155 for (i = 0; i < max_messages; i++) {
156 my_message_t m;
157 int delay = 0;
158
160 m.counter = i;
161 m.worker = wa->id;
162
163retry:
164 if (fr_control_message_send(control[aq_num], wa->rb, FR_CONTROL_ID_CHANNEL, &m, sizeof(m)) < 0) {
165 char const *err;
166 MPRINT1("\tWorker %ld retrying message %zu\n", wa->id, i);
167 while ((err = fr_strerror_pop())) {
168 MPRINT1("\t%s\n", err);
169 }
170 delay += 10;
171 usleep(delay);
172 goto retry;
173 }
174
175 MPRINT2("\tWorker %ld sent message %zu\n", wa->id, i);
176 }
177
178 MPRINT1("\tWorker %ld exiting.\n", wa->id);
179
180 talloc_free(ctx);
181
182 return NULL;
183}
184
185
186
187int main(int argc, char *argv[])
188{
189 int c;
190 TALLOC_CTX *autofree = talloc_autofree_context();
191 pthread_attr_t attr;
192 pthread_t master_id, *worker_id;
193 size_t i;
194 worker_args_t *worker_args;
195 fr_sem_t *master_sem;
196
198
199 while ((c = getopt(argc, argv, "hm:qw:x")) != -1) switch (c) {
200 case 'x':
201 debug_lvl++;
202 break;
203
204 case 'm':
205 max_messages = atoi(optarg);
206 break;
207
208 case 'q':
209 single_aq = false;
210 break;
211
212 case 'w':
213 num_workers = atoi(optarg);
214 break;
215
216 case 'h':
217 default:
218 usage();
219 }
220
221#if 0
222 argc -= (optind - 1);
223 argv += (optind - 1);
224#endif
225
228
231 aq = talloc_array(autofree, fr_atomic_queue_t *, num_aq);
232 for (i = 0; i < num_aq; i++) {
234 fr_assert(aq[i] != NULL);
235 }
236
237 control = talloc_array(autofree, fr_control_t *, num_aq);
238 for (i = 0; i < num_aq; i++) {
239 control[i] = fr_control_create(control, el, aq[i], 2);
240 if (!control[i]) {
241 fprintf(stderr, "control_test: Failed to create control plane\n");
242 fr_exit_now(EXIT_FAILURE);
243 }
244 }
245
246 /*
247 * Start the threads, with the channel.
248 */
249 (void) pthread_attr_init(&attr);
250 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
251
252 master_sem = fr_sem_alloc();
253 fr_assert(master_sem != NULL);
254 (void) pthread_create(&master_id, &attr, control_master, master_sem);
255 SEM_WAIT_INTR(master_sem);
256
257 worker_id = talloc_array(autofree, pthread_t, num_workers);
258 worker_args = talloc_array(autofree, worker_args_t, num_workers);
259 for (i = 0; i < num_workers; i++) {
260 worker_args[i].id = i;
262 (void) pthread_create(&worker_id[i], &attr, control_worker, &worker_args[i]);
263 }
264
265 (void) pthread_join(master_id, NULL);
266 for (i = 0; i < num_workers; i++) {
267 (void) pthread_join(worker_id[i], NULL);
268 }
269
270 fr_sem_free(master_sem);
272
274
275 fr_exit_now(EXIT_SUCCESS);
276}
fr_atomic_queue_t * fr_atomic_queue_alloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
Structure to hold the atomic queue.
static TALLOC_CTX * autofree
Definition fuzzer.c:44
#define RCSID(id)
Definition build.h:506
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:334
#define UNUSED
Definition build.h:336
#define FR_CONTROL_ID_CHANNEL
Definition channel.h:67
static fr_control_t * control_worker
static fr_control_t * control_master
#define FR_CONTROL_MAX_SIZE
Definition control.h:51
#define FR_CONTROL_MAX_MESSAGES
Definition control.h:50
int main(int argc, char *argv[])
static size_t num_workers
#define CONTROL_MAGIC
static bool single_aq
static int aq_size
static size_t num_aq
static void recv_control_callback(void *ctx, void const *data, size_t data_size, UNUSED fr_time_t now)
#define MPRINT3
size_t num_messages
#define MPRINT2
static fr_event_list_t * el
uint32_t header
static fr_control_t ** control
#define MPRINT1
static size_t max_messages
#define MEM(x)
fr_ring_buffer_t * rb
static NEVER_RETURNS void usage(void)
static fr_atomic_queue_t ** aq
static int debug_lvl
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:236
static fr_slen_t err
Definition dict.h:882
talloc_free(hp)
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_callback_add(fr_control_t **c, uint32_t id, void *ctx, fr_control_callback_t callback)
Register a callback for an ID.
Definition control.c:443
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
The control structure.
Definition control.c:76
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2177
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:2045
#define fr_time()
Definition event.c:60
Stores all information relating to an event list.
Definition event.c:377
fr_event_list_t * main_loop_event_list(void)
Return the main loop event list.
Definition main_loop.c:164
int main_loop_init(void)
Initialise the main event loop, setting up signal handlers.
Definition main_loop.c:253
void main_loop_free(void)
Definition main_loop.c:189
unsigned int uint32_t
#define fr_assert(_expr)
Definition rad_assert.h:37
fr_ring_buffer_t * fr_ring_buffer_create(TALLOC_CTX *ctx, size_t size)
Create a ring buffer.
Definition ring_buffer.c:63
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition schedule.c:104
void fr_sem_free(fr_sem_t *sem)
Definition semaphore.c:50
fr_sem_t * fr_sem_alloc(void)
Definition semaphore.c:30
#define SEM_WAIT_INTR(_x)
Definition semaphore.h:56
sem_t fr_sem_t
Definition semaphore.h:53
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:120
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:48
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
static fr_time_delta_t fr_time_sub_time_time(fr_time_t a, fr_time_t b)
Definition time.h:207
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
Definition time.h:632
"server local" time.
Definition time.h:69
char const * fr_strerror_pop(void)
Pop the last library error.
Definition strerror.c:680
static fr_slen_t data
Definition value.h:1340