xref: /php-src/sapi/fpm/fpm/fpm_scoreboard.c (revision 96031995)
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 #ifdef _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