1 /* (c) 2009 Jerome Loyet */
2
3 #include "php.h"
4 #include "SAPI.h"
5 #include <stdio.h>
6 #include <time.h>
7
8 #include "fpm_config.h"
9 #include "fpm_children.h"
10 #include "fpm_scoreboard.h"
11 #include "fpm_shm.h"
12 #include "fpm_sockets.h"
13 #include "fpm_worker_pool.h"
14 #include "fpm_clock.h"
15 #include "zlog.h"
16
17 static struct fpm_scoreboard_s *fpm_scoreboard = NULL;
18 static int fpm_scoreboard_i = -1;
19 #ifdef HAVE_TIMES
20 static float fpm_scoreboard_tick;
21 #endif
22
23
fpm_scoreboard_init_main(void)24 int fpm_scoreboard_init_main(void)
25 {
26 struct fpm_worker_pool_s *wp;
27
28 #ifdef HAVE_TIMES
29 #if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK))
30 fpm_scoreboard_tick = sysconf(_SC_CLK_TCK);
31 #else /* _SC_CLK_TCK */
32 #ifdef HZ
33 fpm_scoreboard_tick = HZ;
34 #else /* HZ */
35 fpm_scoreboard_tick = 100;
36 #endif /* HZ */
37 #endif /* _SC_CLK_TCK */
38 zlog(ZLOG_DEBUG, "got clock tick '%.0f'", fpm_scoreboard_tick);
39 #endif /* HAVE_TIMES */
40
41
42 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
43 size_t scoreboard_procs_size;
44 void *shm_mem;
45
46 if (wp->config->pm_max_children < 1) {
47 zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because max_client is not set", wp->config->name);
48 return -1;
49 }
50
51 if (wp->scoreboard) {
52 zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because it already exists", wp->config->name);
53 return -1;
54 }
55
56 scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
57 shm_mem = fpm_shm_alloc(sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
58
59 if (!shm_mem) {
60 return -1;
61 }
62 wp->scoreboard = shm_mem;
63 wp->scoreboard->pm = wp->config->pm;
64 wp->scoreboard->nprocs = wp->config->pm_max_children;
65 wp->scoreboard->start_epoch = time(NULL);
66 strlcpy(wp->scoreboard->pool, wp->config->name, sizeof(wp->scoreboard->pool));
67
68 if (wp->shared) {
69 /* shared pool is added after non shared ones so the shared scoreboard is allocated */
70 wp->scoreboard->shared = wp->shared->scoreboard;
71 }
72 }
73 return 0;
74 }
75
fpm_scoreboard_get_for_update(struct fpm_scoreboard_s * scoreboard)76 static struct fpm_scoreboard_s *fpm_scoreboard_get_for_update(struct fpm_scoreboard_s *scoreboard) /* {{{ */
77 {
78 if (!scoreboard) {
79 scoreboard = fpm_scoreboard;
80 }
81 if (!scoreboard) {
82 zlog(ZLOG_WARNING, "Unable to update scoreboard: the SHM has not been found");
83 }
84
85 return scoreboard;
86 }
87 /* }}} */
88
fpm_scoreboard_update_begin(struct fpm_scoreboard_s * scoreboard)89 void fpm_scoreboard_update_begin(struct fpm_scoreboard_s *scoreboard) /* {{{ */
90 {
91 scoreboard = fpm_scoreboard_get_for_update(scoreboard);
92 if (!scoreboard) {
93 return;
94 }
95
96 fpm_spinlock(&scoreboard->lock, 0);
97 }
98 /* }}} */
99
fpm_scoreboard_update_commit(int idle,int active,int lq,int lq_len,int requests,int max_children_reached,int slow_rq,int action,struct fpm_scoreboard_s * scoreboard)100 void fpm_scoreboard_update_commit(
101 int idle, int active, int lq, int lq_len, int requests, int max_children_reached,
102 int slow_rq, int action, struct fpm_scoreboard_s *scoreboard) /* {{{ */
103 {
104 scoreboard = fpm_scoreboard_get_for_update(scoreboard);
105 if (!scoreboard) {
106 return;
107 }
108
109 if (action == FPM_SCOREBOARD_ACTION_SET) {
110 if (idle >= 0) {
111 scoreboard->idle = idle;
112 }
113 if (active >= 0) {
114 scoreboard->active = active;
115 }
116 if (lq >= 0) {
117 scoreboard->lq = lq;
118 }
119 if (lq_len >= 0) {
120 scoreboard->lq_len = lq_len;
121 }
122 #if HAVE_FPM_LQ /* prevent unnecessary test */
123 if (scoreboard->lq > scoreboard->lq_max) {
124 scoreboard->lq_max = scoreboard->lq;
125 }
126 #endif
127 if (requests >= 0) {
128 scoreboard->requests = requests;
129 }
130
131 if (max_children_reached >= 0) {
132 scoreboard->max_children_reached = max_children_reached;
133 }
134 if (slow_rq > 0) {
135 scoreboard->slow_rq = slow_rq;
136 }
137 } else {
138 if (scoreboard->idle + idle > 0) {
139 scoreboard->idle += idle;
140 } else {
141 scoreboard->idle = 0;
142 }
143
144 if (scoreboard->active + active > 0) {
145 scoreboard->active += active;
146 } else {
147 scoreboard->active = 0;
148 }
149
150 if (scoreboard->requests + requests > 0) {
151 scoreboard->requests += requests;
152 } else {
153 scoreboard->requests = 0;
154 }
155
156 if (scoreboard->max_children_reached + max_children_reached > 0) {
157 scoreboard->max_children_reached += max_children_reached;
158 } else {
159 scoreboard->max_children_reached = 0;
160 }
161
162 if (scoreboard->slow_rq + slow_rq > 0) {
163 scoreboard->slow_rq += slow_rq;
164 } else {
165 scoreboard->slow_rq = 0;
166 }
167 }
168
169 if (scoreboard->active > scoreboard->active_max) {
170 scoreboard->active_max = scoreboard->active;
171 }
172
173 fpm_unlock(scoreboard->lock);
174 }
175 /* }}} */
176
177
fpm_scoreboard_update(int idle,int active,int lq,int lq_len,int requests,int max_children_reached,int slow_rq,int action,struct fpm_scoreboard_s * scoreboard)178 void fpm_scoreboard_update(
179 int idle, int active, int lq, int lq_len, int requests, int max_children_reached,
180 int slow_rq, int action, struct fpm_scoreboard_s *scoreboard) /* {{{ */
181 {
182 fpm_scoreboard_update_begin(scoreboard);
183 fpm_scoreboard_update_commit(
184 idle, active, lq, lq_len, requests, max_children_reached, slow_rq, action, scoreboard);
185 }
186 /* }}} */
187
fpm_scoreboard_get(void)188 struct fpm_scoreboard_s *fpm_scoreboard_get(void)
189 {
190 return fpm_scoreboard;
191 }
192
fpm_scoreboard_proc_get_ex(struct fpm_scoreboard_s * scoreboard,int child_index,unsigned int nprocs)193 static inline struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_ex(
194 struct fpm_scoreboard_s *scoreboard, int child_index, unsigned int nprocs) /* {{{*/
195 {
196 if (!scoreboard) {
197 return NULL;
198 }
199
200 if (child_index < 0 || (unsigned int)child_index >= nprocs) {
201 return NULL;
202 }
203
204 return &scoreboard->procs[child_index];
205 }
206 /* }}} */
207
fpm_scoreboard_proc_get(struct fpm_scoreboard_s * scoreboard,int child_index)208 struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(
209 struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
210 {
211 if (!scoreboard) {
212 scoreboard = fpm_scoreboard;
213 }
214
215 if (child_index < 0) {
216 child_index = fpm_scoreboard_i;
217 }
218
219 return fpm_scoreboard_proc_get_ex(scoreboard, child_index, scoreboard->nprocs);
220 }
221 /* }}} */
222
fpm_scoreboard_proc_get_from_child(struct fpm_child_s * child)223 struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_from_child(struct fpm_child_s *child) /* {{{*/
224 {
225 struct fpm_worker_pool_s *wp = child->wp;
226 unsigned int nprocs = wp->config->pm_max_children;
227 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
228 int child_index = child->scoreboard_i;
229
230 return fpm_scoreboard_proc_get_ex(scoreboard, child_index, nprocs);
231 }
232 /* }}} */
233
234
fpm_scoreboard_acquire(struct fpm_scoreboard_s * scoreboard,int nohang)235 struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */
236 {
237 struct fpm_scoreboard_s *s;
238
239 s = scoreboard ? scoreboard : fpm_scoreboard;
240 if (!s) {
241 return NULL;
242 }
243
244 if (!fpm_spinlock(&s->lock, nohang)) {
245 return NULL;
246 }
247 return s;
248 }
249 /* }}} */
250
fpm_scoreboard_release(struct fpm_scoreboard_s * scoreboard)251 void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard) {
252 if (!scoreboard) {
253 return;
254 }
255
256 scoreboard->lock = 0;
257 }
258
fpm_scoreboard_copy(struct fpm_scoreboard_s * scoreboard,int copy_procs)259 struct fpm_scoreboard_s *fpm_scoreboard_copy(struct fpm_scoreboard_s *scoreboard, int copy_procs)
260 {
261 struct fpm_scoreboard_s *scoreboard_copy;
262 struct fpm_scoreboard_proc_s *scoreboard_proc_p;
263 size_t scoreboard_size, scoreboard_nprocs_size;
264 int i;
265 void *mem;
266
267 if (!scoreboard) {
268 scoreboard = fpm_scoreboard_get();
269 }
270
271 if (copy_procs) {
272 scoreboard_size = sizeof(struct fpm_scoreboard_s);
273 scoreboard_nprocs_size = sizeof(struct fpm_scoreboard_proc_s) * scoreboard->nprocs;
274
275 mem = malloc(scoreboard_size + scoreboard_nprocs_size);
276 } else {
277 mem = malloc(sizeof(struct fpm_scoreboard_s));
278 }
279
280 if (!mem) {
281 zlog(ZLOG_ERROR, "scoreboard: failed to allocate memory for copy");
282 return NULL;
283 }
284
285 scoreboard_copy = mem;
286
287 scoreboard = fpm_scoreboard_acquire(scoreboard, FPM_SCOREBOARD_LOCK_NOHANG);
288 if (!scoreboard) {
289 free(mem);
290 zlog(ZLOG_ERROR, "scoreboard: failed to lock (already locked)");
291 return NULL;
292 }
293
294 *scoreboard_copy = *scoreboard;
295
296 if (copy_procs) {
297 mem += scoreboard_size;
298
299 for (i = 0; i < scoreboard->nprocs; i++, mem += sizeof(struct fpm_scoreboard_proc_s)) {
300 scoreboard_proc_p = fpm_scoreboard_proc_acquire(scoreboard, i, FPM_SCOREBOARD_LOCK_HANG);
301 scoreboard_copy->procs[i] = *scoreboard_proc_p;
302 fpm_scoreboard_proc_release(scoreboard_proc_p);
303 }
304 }
305
306 fpm_scoreboard_release(scoreboard);
307
308 return scoreboard_copy;
309 }
310
fpm_scoreboard_free_copy(struct fpm_scoreboard_s * scoreboard)311 void fpm_scoreboard_free_copy(struct fpm_scoreboard_s *scoreboard)
312 {
313 free(scoreboard);
314 }
315
fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s * scoreboard,int child_index,int nohang)316 struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang) /* {{{ */
317 {
318 struct fpm_scoreboard_proc_s *proc;
319
320 proc = fpm_scoreboard_proc_get(scoreboard, child_index);
321 if (!proc) {
322 return NULL;
323 }
324
325 if (!fpm_spinlock(&proc->lock, nohang)) {
326 return NULL;
327 }
328
329 return proc;
330 }
331 /* }}} */
332
fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s * proc)333 void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc) /* {{{ */
334 {
335 if (!proc) {
336 return;
337 }
338
339 proc->lock = 0;
340 }
341
fpm_scoreboard_free(struct fpm_worker_pool_s * wp)342 void fpm_scoreboard_free(struct fpm_worker_pool_s *wp) /* {{{ */
343 {
344 size_t scoreboard_procs_size;
345 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
346
347 if (!scoreboard) {
348 zlog(ZLOG_ERROR, "**scoreboard is NULL");
349 return;
350 }
351
352 scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
353
354 fpm_shm_free(scoreboard, sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
355 }
356 /* }}} */
357
fpm_scoreboard_child_use(struct fpm_child_s * child,pid_t pid)358 void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid) /* {{{ */
359 {
360 struct fpm_scoreboard_proc_s *proc;
361 fpm_scoreboard = child->wp->scoreboard;
362 fpm_scoreboard_i = child->scoreboard_i;
363 proc = fpm_scoreboard_proc_get_from_child(child);
364 if (!proc) {
365 return;
366 }
367 proc->pid = pid;
368 proc->start_epoch = time(NULL);
369 }
370 /* }}} */
371
fpm_scoreboard_proc_free(struct fpm_child_s * child)372 void fpm_scoreboard_proc_free(struct fpm_child_s *child) /* {{{ */
373 {
374 struct fpm_worker_pool_s *wp = child->wp;
375 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
376 int child_index = child->scoreboard_i;
377
378 if (!scoreboard) {
379 return;
380 }
381
382 if (child_index < 0 || child_index >= wp->config->pm_max_children) {
383 return;
384 }
385
386 if (scoreboard->procs[child_index].used > 0) {
387 memset(&scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
388 }
389
390 /* set this slot as free to avoid search on next alloc */
391 scoreboard->free_proc = child_index;
392 }
393 /* }}} */
394
fpm_scoreboard_proc_alloc(struct fpm_child_s * child)395 int fpm_scoreboard_proc_alloc(struct fpm_child_s *child) /* {{{ */
396 {
397 int i = -1;
398 struct fpm_worker_pool_s *wp = child->wp;
399 struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
400 int nprocs = wp->config->pm_max_children;
401
402 if (!scoreboard) {
403 return -1;
404 }
405
406 /* first try the slot which is supposed to be free */
407 if (scoreboard->free_proc >= 0 && scoreboard->free_proc < nprocs) {
408 if (!scoreboard->procs[scoreboard->free_proc].used) {
409 i = scoreboard->free_proc;
410 }
411 }
412
413 if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
414 zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
415 for (i = 0; i < nprocs; i++) {
416 if (!scoreboard->procs[i].used) { /* found */
417 break;
418 }
419 }
420 }
421
422 /* no free slot */
423 if (i < 0 || i >= nprocs) {
424 zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
425 return -1;
426 }
427
428 scoreboard->procs[i].used = 1;
429 child->scoreboard_i = i;
430
431 /* supposed next slot is free */
432 if (i + 1 >= nprocs) {
433 scoreboard->free_proc = 0;
434 } else {
435 scoreboard->free_proc = i + 1;
436 }
437
438 return 0;
439 }
440 /* }}} */
441
442 #ifdef HAVE_TIMES
fpm_scoreboard_get_tick(void)443 float fpm_scoreboard_get_tick(void)
444 {
445 return fpm_scoreboard_tick;
446 }
447 #endif
448