1 | #include "globals.h"
|
---|
2 |
|
---|
3 | #ifdef WITH_LB
|
---|
4 | #include "module-cacheex.h"
|
---|
5 | #include "module-cccam.h"
|
---|
6 | #include "oscam-chk.h"
|
---|
7 | #include "oscam-client.h"
|
---|
8 | #include "oscam-ecm.h"
|
---|
9 | #include "oscam-files.h"
|
---|
10 | #include "oscam-lock.h"
|
---|
11 | #include "oscam-string.h"
|
---|
12 | #include "oscam-time.h"
|
---|
13 |
|
---|
14 | #define UNDEF_AVG_TIME 80000
|
---|
15 | #define MAX_ECM_SEND_CACHE 16
|
---|
16 |
|
---|
17 | #define LB_REOPEN_MODE_STANDARD 0
|
---|
18 | #define LB_REOPEN_MODE_FAST 1
|
---|
19 |
|
---|
20 | #define LB_NONE 0
|
---|
21 | #define LB_FASTEST_READER_FIRST 1
|
---|
22 | #define LB_OLDEST_READER_FIRST 2
|
---|
23 | #define LB_LOWEST_USAGELEVEL 3
|
---|
24 | #define LB_LOG_ONLY 10
|
---|
25 |
|
---|
26 | #define DEFAULT_LOCK_TIMEOUT 1000
|
---|
27 |
|
---|
28 | static int32_t stat_load_save;
|
---|
29 | static time_t last_housekeeping = 0;
|
---|
30 |
|
---|
31 | void init_stat(void)
|
---|
32 | {
|
---|
33 | stat_load_save = -100;
|
---|
34 |
|
---|
35 | //checking config
|
---|
36 | if (cfg.lb_nbest_readers < 2)
|
---|
37 | cfg.lb_nbest_readers = DEFAULT_NBEST;
|
---|
38 | if (cfg.lb_nfb_readers < 2)
|
---|
39 | cfg.lb_nfb_readers = DEFAULT_NFB;
|
---|
40 | if (cfg.lb_min_ecmcount < 2)
|
---|
41 | cfg.lb_min_ecmcount = DEFAULT_MIN_ECM_COUNT;
|
---|
42 | if (cfg.lb_max_ecmcount < 3)
|
---|
43 | cfg.lb_max_ecmcount = DEFAULT_MAX_ECM_COUNT;
|
---|
44 | if (cfg.lb_reopen_seconds < 10)
|
---|
45 | cfg.lb_reopen_seconds = DEFAULT_REOPEN_SECONDS;
|
---|
46 | if (cfg.lb_retrylimit <= 0)
|
---|
47 | cfg.lb_retrylimit = DEFAULT_RETRYLIMIT;
|
---|
48 | if (cfg.lb_stat_cleanup <= 0)
|
---|
49 | cfg.lb_stat_cleanup = DEFAULT_LB_STAT_CLEANUP;
|
---|
50 | }
|
---|
51 |
|
---|
52 | #define LINESIZE 1024
|
---|
53 |
|
---|
54 | static uint32_t get_prid(uint16_t caid, uint32_t prid)
|
---|
55 | {
|
---|
56 | int32_t i;
|
---|
57 | for (i=0;i<CS_MAXCAIDTAB;i++) {
|
---|
58 | uint16_t tcaid = cfg.lb_noproviderforcaid.caid[i];
|
---|
59 | if (!tcaid) break;
|
---|
60 | if ((tcaid == caid) || (tcaid < 0x0100 && (caid >> 8) == tcaid)) {
|
---|
61 | prid = 0;
|
---|
62 | break;
|
---|
63 | }
|
---|
64 |
|
---|
65 | }
|
---|
66 | return prid;
|
---|
67 | }
|
---|
68 |
|
---|
69 | static uint32_t get_subid(ECM_REQUEST *er)
|
---|
70 | {
|
---|
71 | if (!er->ecmlen)
|
---|
72 | return 0;
|
---|
73 |
|
---|
74 | uint32_t id = 0;
|
---|
75 | switch (er->caid>>8)
|
---|
76 | {
|
---|
77 | case 0x01: id = b2i(2, er->ecm+7); break;
|
---|
78 | case 0x06: id = b2i(2, er->ecm+6); break;
|
---|
79 | case 0x09: id = b2i(2, er->ecm+11); break;
|
---|
80 | case 0x4A: // DRE-Crypt, Bulcrypt, others?
|
---|
81 | if (er->caid != 0x4AEE) // Bulcrypt
|
---|
82 | id = er->ecm[7];
|
---|
83 | break;
|
---|
84 | }
|
---|
85 | return id;
|
---|
86 | }
|
---|
87 |
|
---|
88 |
|
---|
89 | static void get_stat_query(ECM_REQUEST *er, STAT_QUERY *q)
|
---|
90 | {
|
---|
91 | memset(q, 0, sizeof(STAT_QUERY));
|
---|
92 |
|
---|
93 | q->caid = er->caid;
|
---|
94 | q->prid = get_prid(er->caid, er->prid);
|
---|
95 | q->srvid = er->srvid;
|
---|
96 | q->chid = get_subid(er);
|
---|
97 | q->ecmlen = er->ecmlen;
|
---|
98 | }
|
---|
99 |
|
---|
100 | void load_stat_from_file(void)
|
---|
101 | {
|
---|
102 | stat_load_save = 0;
|
---|
103 | char buf[256];
|
---|
104 | char *line;
|
---|
105 | char *fname;
|
---|
106 | FILE *file;
|
---|
107 |
|
---|
108 | if (!cfg.lb_savepath) {
|
---|
109 | get_tmp_dir_filename(buf, sizeof(buf), "stat");
|
---|
110 | fname = buf;
|
---|
111 | }
|
---|
112 | else
|
---|
113 | fname = cfg.lb_savepath;
|
---|
114 |
|
---|
115 | file = fopen(fname, "r");
|
---|
116 |
|
---|
117 | if (!file) {
|
---|
118 | cs_log("loadbalancer: can't read from file %s", fname);
|
---|
119 | return;
|
---|
120 | }
|
---|
121 |
|
---|
122 | if (!cs_malloc(&line, LINESIZE))
|
---|
123 | return;
|
---|
124 |
|
---|
125 | setvbuf(file, NULL, _IOFBF, 128*1024);
|
---|
126 |
|
---|
127 | cs_debug_mask(D_LB, "loadbalancer: load statistics from %s", fname);
|
---|
128 |
|
---|
129 | struct timeb ts, te;
|
---|
130 | cs_ftime(&ts);
|
---|
131 |
|
---|
132 | struct s_reader *rdr = NULL;
|
---|
133 | READER_STAT *s;
|
---|
134 |
|
---|
135 | int32_t i=1;
|
---|
136 | int32_t valid=0;
|
---|
137 | int32_t count=0;
|
---|
138 | int32_t type=0;
|
---|
139 | char *ptr, *saveptr1 = NULL;
|
---|
140 | char *split[12];
|
---|
141 |
|
---|
142 | while (fgets(line, LINESIZE, file))
|
---|
143 | {
|
---|
144 | if (!line[0] || line[0] == '#' || line[0] == ';')
|
---|
145 | continue;
|
---|
146 |
|
---|
147 | if (!cs_malloc(&s, sizeof(READER_STAT)))
|
---|
148 | continue;
|
---|
149 |
|
---|
150 | //get type by evaluating first line:
|
---|
151 | if (type==0) {
|
---|
152 | if (strstr(line, " rc ")) type = 2;
|
---|
153 | else type = 1;
|
---|
154 | }
|
---|
155 |
|
---|
156 | if (type==1) { //New format - faster parsing:
|
---|
157 | for (i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i<12 ; ptr = strtok_r(NULL, ",", &saveptr1), i++)
|
---|
158 | split[i] = ptr;
|
---|
159 | valid = (i==11);
|
---|
160 | if (valid) {
|
---|
161 | strncpy(buf, split[0], sizeof(buf)-1);
|
---|
162 | s->rc = atoi(split[1]);
|
---|
163 | s->caid = a2i(split[2], 4);
|
---|
164 | s->prid = a2i(split[3], 6);
|
---|
165 | s->srvid = a2i(split[4], 4);
|
---|
166 | s->chid = a2i(split[5], 4);
|
---|
167 | s->time_avg = atoi(split[6]);
|
---|
168 | s->ecm_count = atoi(split[7]);
|
---|
169 | s->last_received = atol(split[8]);
|
---|
170 | s->fail_factor = atoi(split[9]);
|
---|
171 | s->ecmlen = a2i(split[10], 2);
|
---|
172 | }
|
---|
173 | } else { //Old format - keep for compatibility:
|
---|
174 | i = sscanf(line, "%255s rc %04d caid %04hX prid %06X srvid %04hX time avg %dms ecms %d last %ld fail %d len %02hX\n",
|
---|
175 | buf, &s->rc, &s->caid, &s->prid, &s->srvid,
|
---|
176 | &s->time_avg, &s->ecm_count, &s->last_received, &s->fail_factor, &s->ecmlen);
|
---|
177 | valid = i>5;
|
---|
178 | }
|
---|
179 |
|
---|
180 | if (valid && s->ecmlen > 0) {
|
---|
181 | if (rdr == NULL || strcmp(buf, rdr->label) != 0) {
|
---|
182 | LL_ITER itr = ll_iter_create(configured_readers);
|
---|
183 | while ((rdr=ll_iter_next(&itr))) {
|
---|
184 | if (strcmp(rdr->label, buf) == 0) {
|
---|
185 | break;
|
---|
186 | }
|
---|
187 | }
|
---|
188 | }
|
---|
189 |
|
---|
190 | if (rdr != NULL && strcmp(buf, rdr->label) == 0) {
|
---|
191 | if (!rdr->lb_stat) {
|
---|
192 | rdr->lb_stat = ll_create("lb_stat");
|
---|
193 | cs_lock_create(&rdr->lb_stat_lock, DEFAULT_LOCK_TIMEOUT, rdr->label);
|
---|
194 | }
|
---|
195 |
|
---|
196 | ll_append(rdr->lb_stat, s);
|
---|
197 | count++;
|
---|
198 | }
|
---|
199 | else
|
---|
200 | {
|
---|
201 | cs_log("loadbalancer: statistics could not be loaded for %s", buf);
|
---|
202 | free(s);
|
---|
203 | }
|
---|
204 | }
|
---|
205 | else
|
---|
206 | {
|
---|
207 | cs_debug_mask(D_LB, "loadbalancer: statistics ERROR: %s rc=%d i=%d", buf, s->rc, i);
|
---|
208 | free(s);
|
---|
209 | }
|
---|
210 | }
|
---|
211 | fclose(file);
|
---|
212 | free(line);
|
---|
213 |
|
---|
214 | cs_ftime(&te);
|
---|
215 | #ifdef WITH_DEBUG
|
---|
216 | int32_t load_time = 1000*(te.time-ts.time)+te.millitm-ts.millitm;
|
---|
217 |
|
---|
218 | cs_debug_mask(D_LB, "loadbalancer: statistics loaded %d records in %dms", count, load_time);
|
---|
219 | #endif
|
---|
220 | }
|
---|
221 |
|
---|
222 | /**
|
---|
223 | * get statistic values for reader ridx and caid/prid/srvid/ecmlen
|
---|
224 | **/
|
---|
225 | static READER_STAT *get_stat_lock(struct s_reader *rdr, STAT_QUERY *q, int8_t lock)
|
---|
226 | {
|
---|
227 | if (!rdr->lb_stat) {
|
---|
228 | rdr->lb_stat = ll_create("lb_stat");
|
---|
229 | cs_lock_create(&rdr->lb_stat_lock, DEFAULT_LOCK_TIMEOUT, rdr->label);
|
---|
230 | }
|
---|
231 |
|
---|
232 | if (lock) cs_readlock(&rdr->lb_stat_lock);
|
---|
233 |
|
---|
234 | LL_ITER it = ll_iter_create(rdr->lb_stat);
|
---|
235 | READER_STAT *s;
|
---|
236 | int32_t i = 0;
|
---|
237 | while ((s = ll_iter_next(&it))) {
|
---|
238 | i++;
|
---|
239 | if (s->caid==q->caid && s->prid==q->prid && s->srvid==q->srvid && s->chid==q->chid) {
|
---|
240 | if (s->ecmlen == q->ecmlen)
|
---|
241 | break;
|
---|
242 | if (!s->ecmlen) {
|
---|
243 | s->ecmlen = q->ecmlen;
|
---|
244 | break;
|
---|
245 | }
|
---|
246 | if (!q->ecmlen) //Query without ecmlen from dvbapi
|
---|
247 | break;
|
---|
248 | }
|
---|
249 | }
|
---|
250 | if (lock) cs_readunlock(&rdr->lb_stat_lock);
|
---|
251 |
|
---|
252 | //Move stat to list start for faster access:
|
---|
253 | // if (i > 10 && s) {
|
---|
254 | // if (lock) cs_writelock(&rdr->lb_stat_lock);
|
---|
255 | // ll_iter_move_first(&it);
|
---|
256 | // if (lock) cs_writeunlock(&rdr->lb_stat_lock);
|
---|
257 | // } Corsair removed, could cause crashes!
|
---|
258 |
|
---|
259 | return s;
|
---|
260 | }
|
---|
261 |
|
---|
262 | /**
|
---|
263 | * get statistic values for reader ridx and caid/prid/srvid/ecmlen
|
---|
264 | **/
|
---|
265 | static READER_STAT *get_stat(struct s_reader *rdr, STAT_QUERY *q)
|
---|
266 | {
|
---|
267 | return get_stat_lock(rdr, q, 1);
|
---|
268 | }
|
---|
269 |
|
---|
270 | /**
|
---|
271 | * Calculates average time
|
---|
272 | */
|
---|
273 | static void calc_stat(READER_STAT *s)
|
---|
274 | {
|
---|
275 | int32_t i, c=0, t = 0;
|
---|
276 | for (i = 0; i < LB_MAX_STAT_TIME; i++) {
|
---|
277 | if (s->time_stat[i] > 0) {
|
---|
278 | t += (int32_t)s->time_stat[i];
|
---|
279 | c++;
|
---|
280 | }
|
---|
281 | }
|
---|
282 | if (!c)
|
---|
283 | s->time_avg = UNDEF_AVG_TIME;
|
---|
284 | else
|
---|
285 | s->time_avg = t / c;
|
---|
286 | }
|
---|
287 |
|
---|
288 | /**
|
---|
289 | * Saves statistik to /tmp/.oscam/stat.n where n is reader-index
|
---|
290 | */
|
---|
291 | static void save_stat_to_file_thread(void)
|
---|
292 | {
|
---|
293 | stat_load_save = 0;
|
---|
294 | char buf[256];
|
---|
295 |
|
---|
296 | set_thread_name(__func__);
|
---|
297 |
|
---|
298 | char *fname;
|
---|
299 | if (!cfg.lb_savepath) {
|
---|
300 | get_tmp_dir_filename(buf, sizeof(buf), "stat");
|
---|
301 | fname = buf;
|
---|
302 | }
|
---|
303 | else
|
---|
304 | fname = cfg.lb_savepath;
|
---|
305 |
|
---|
306 | FILE *file = fopen(fname, "w");
|
---|
307 |
|
---|
308 | if (!file) {
|
---|
309 | cs_log("can't write to file %s", fname);
|
---|
310 | return;
|
---|
311 | }
|
---|
312 |
|
---|
313 | setvbuf(file, NULL, _IOFBF, 128*1024);
|
---|
314 |
|
---|
315 | struct timeb ts, te;
|
---|
316 | cs_ftime(&ts);
|
---|
317 |
|
---|
318 | time_t cleanup_time = time(NULL) - (cfg.lb_stat_cleanup*60*60);
|
---|
319 |
|
---|
320 | int32_t count=0;
|
---|
321 | struct s_reader *rdr;
|
---|
322 | LL_ITER itr = ll_iter_create(configured_readers);
|
---|
323 | while ((rdr=ll_iter_next(&itr))) {
|
---|
324 |
|
---|
325 | if (rdr->lb_stat) {
|
---|
326 | cs_writelock(&rdr->lb_stat_lock);
|
---|
327 | LL_ITER it = ll_iter_create(rdr->lb_stat);
|
---|
328 | READER_STAT *s;
|
---|
329 | while ((s = ll_iter_next(&it))) {
|
---|
330 |
|
---|
331 | if (s->last_received < cleanup_time || !s->ecmlen) { //cleanup old stats
|
---|
332 | ll_iter_remove_data(&it);
|
---|
333 | continue;
|
---|
334 | }
|
---|
335 |
|
---|
336 | //Old version, too slow to parse:
|
---|
337 | //fprintf(file, "%s rc %d caid %04hX prid %06X srvid %04hX time avg %dms ecms %d last %ld fail %d len %02hX\n",
|
---|
338 | // rdr->label, s->rc, s->caid, s->prid,
|
---|
339 | // s->srvid, s->time_avg, s->ecm_count, s->last_received, s->fail_factor, s->ecmlen);
|
---|
340 |
|
---|
341 | //New version:
|
---|
342 | fprintf(file, "%s,%d,%04hX,%06X,%04hX,%04hX,%d,%d,%ld,%d,%02hX\n",
|
---|
343 | rdr->label, s->rc, s->caid, s->prid,
|
---|
344 | s->srvid, (uint16_t)s->chid, s->time_avg, s->ecm_count, s->last_received, s->fail_factor, s->ecmlen);
|
---|
345 |
|
---|
346 | count++;
|
---|
347 | // if (count % 500 == 0) { //Saving stats is using too much cpu and causes high file load. so we need a break
|
---|
348 | // cs_readunlock(&rdr->lb_stat_lock);
|
---|
349 | // cs_sleepms(100);
|
---|
350 | // cs_readlock(&rdr->lb_stat_lock);
|
---|
351 | // }
|
---|
352 | }
|
---|
353 | cs_writeunlock(&rdr->lb_stat_lock);
|
---|
354 | }
|
---|
355 | }
|
---|
356 |
|
---|
357 | fclose(file);
|
---|
358 |
|
---|
359 | cs_ftime(&te);
|
---|
360 | int32_t load_time = 1000*(te.time-ts.time)+te.millitm-ts.millitm;
|
---|
361 |
|
---|
362 |
|
---|
363 | cs_log("loadbalancer: statistic saved %d records to %s in %dms", count, fname, load_time);
|
---|
364 | }
|
---|
365 |
|
---|
366 | void save_stat_to_file(int32_t thread)
|
---|
367 | {
|
---|
368 | stat_load_save = 0;
|
---|
369 | if (thread)
|
---|
370 | start_thread((void*)&save_stat_to_file_thread, "save lb stats");
|
---|
371 | else
|
---|
372 | save_stat_to_file_thread();
|
---|
373 | }
|
---|
374 |
|
---|
375 | /**
|
---|
376 | * fail_factor is multiplied to the reopen_time. This function increases the fail_factor
|
---|
377 | **/
|
---|
378 | static void inc_fail(READER_STAT *s)
|
---|
379 | {
|
---|
380 | if (s->fail_factor <= 0)
|
---|
381 | s->fail_factor = 1;
|
---|
382 | else
|
---|
383 | s->fail_factor *= 2;
|
---|
384 | }
|
---|
385 |
|
---|
386 | static READER_STAT *get_add_stat(struct s_reader *rdr, STAT_QUERY *q)
|
---|
387 | {
|
---|
388 | if (!rdr->lb_stat) {
|
---|
389 | rdr->lb_stat = ll_create("lb_stat");
|
---|
390 | cs_lock_create(&rdr->lb_stat_lock, DEFAULT_LOCK_TIMEOUT, rdr->label);
|
---|
391 | }
|
---|
392 |
|
---|
393 | cs_writelock(&rdr->lb_stat_lock);
|
---|
394 |
|
---|
395 | READER_STAT *s = get_stat_lock(rdr, q, 0);
|
---|
396 | if (!s) {
|
---|
397 | if (cs_malloc(&s,sizeof(READER_STAT))) {
|
---|
398 | s->caid = q->caid;
|
---|
399 | s->prid = q->prid;
|
---|
400 | s->srvid = q->srvid;
|
---|
401 | s->chid = q->chid;
|
---|
402 | s->ecmlen = q->ecmlen;
|
---|
403 | s->time_avg = UNDEF_AVG_TIME; //dummy placeholder
|
---|
404 | s->rc = E_NOTFOUND;
|
---|
405 | s->last_received = time(NULL);
|
---|
406 | ll_append(rdr->lb_stat, s);
|
---|
407 | }
|
---|
408 | }
|
---|
409 |
|
---|
410 | if (s->ecm_count < 0)
|
---|
411 | s->ecm_count=0;
|
---|
412 |
|
---|
413 | cs_writeunlock(&rdr->lb_stat_lock);
|
---|
414 |
|
---|
415 | return s;
|
---|
416 | }
|
---|
417 |
|
---|
418 | static void housekeeping_stat(int32_t force);
|
---|
419 |
|
---|
420 | /**
|
---|
421 | * Adds caid/prid/srvid/ecmlen to stat-list for reader ridx with time/rc
|
---|
422 | */
|
---|
423 | static void add_stat(struct s_reader *rdr, ECM_REQUEST *er, int32_t ecm_time, int32_t rc)
|
---|
424 | {
|
---|
425 | if (!rdr || !er || !cfg.lb_mode ||!er->ecmlen || !er->client)
|
---|
426 | return;
|
---|
427 |
|
---|
428 | struct s_client *cl = rdr->client;
|
---|
429 | if (!cl)
|
---|
430 | return;
|
---|
431 |
|
---|
432 | READER_STAT *s;
|
---|
433 |
|
---|
434 | //inc ecm_count if found, drop to 0 if not found:
|
---|
435 | // rc codes:
|
---|
436 | // 0 = found +
|
---|
437 | // 1 = cache1 #
|
---|
438 | // 2 = cache2 #
|
---|
439 | // 3 = cacheex #
|
---|
440 | // 4 = not found -
|
---|
441 | // 5 = timeout -2
|
---|
442 | // 6 = sleeping #
|
---|
443 | // 7 = fake #
|
---|
444 | // 8 = invalid #
|
---|
445 | // 9 = corrupt #
|
---|
446 | // 10= no card #
|
---|
447 | // 11= expdate #
|
---|
448 | // 12= disabled #
|
---|
449 | // 13= stopped #
|
---|
450 | // 100= unhandled #
|
---|
451 | // + = adds statistic values
|
---|
452 | // # = ignored because of duplicate values, temporary failures or softblocks
|
---|
453 | // - = causes loadbalancer to block this reader for this caid/prov/sid
|
---|
454 | // -2 = causes loadbalancer to block if happens too often
|
---|
455 |
|
---|
456 |
|
---|
457 | if (rc == E_NOTFOUND && (uint32_t)ecm_time >= cfg.ctimeout) //Map "not found" to "timeout" if ecm_time>client time out
|
---|
458 | rc = E_TIMEOUT;
|
---|
459 |
|
---|
460 | if ((uint32_t)ecm_time >= 3*cfg.ctimeout) //ignore too old ecms
|
---|
461 | return;
|
---|
462 |
|
---|
463 | STAT_QUERY q;
|
---|
464 | get_stat_query(er, &q);
|
---|
465 |
|
---|
466 | time_t now = time(NULL);
|
---|
467 |
|
---|
468 | if (rc == E_FOUND) { //found
|
---|
469 | s = get_add_stat(rdr, &q);
|
---|
470 | s->rc = E_FOUND;
|
---|
471 | s->ecm_count++;
|
---|
472 | s->last_received = now;
|
---|
473 | s->fail_factor = 0;
|
---|
474 |
|
---|
475 | //FASTEST READER:
|
---|
476 | s->time_idx++;
|
---|
477 | if (s->time_idx >= LB_MAX_STAT_TIME)
|
---|
478 | s->time_idx = 0;
|
---|
479 | s->time_stat[s->time_idx] = ecm_time;
|
---|
480 | calc_stat(s);
|
---|
481 |
|
---|
482 | //OLDEST READER now set by get best reader!
|
---|
483 |
|
---|
484 |
|
---|
485 | //USAGELEVEL:
|
---|
486 | /* Assign a value to rdr->lb_usagelevel_ecmcount,
|
---|
487 | because no determined value was assigned before. */
|
---|
488 | if (rdr->lb_usagelevel_ecmcount < 0)
|
---|
489 | rdr->lb_usagelevel_ecmcount = 0;
|
---|
490 |
|
---|
491 | rdr->lb_usagelevel_ecmcount++; /* ecm is found so counter should increase */
|
---|
492 | if ((rdr->lb_usagelevel_ecmcount % cfg.lb_min_ecmcount) == 0) //update every MIN_ECM_COUNT usagelevel:
|
---|
493 | {
|
---|
494 | time_t t = (now - rdr->lb_usagelevel_time);
|
---|
495 | rdr->lb_usagelevel = 1000 / (t<1?1:t);
|
---|
496 | /* Reset of usagelevel time and counter */
|
---|
497 | rdr->lb_usagelevel_time = now;
|
---|
498 | rdr->lb_usagelevel_ecmcount = 0;
|
---|
499 | }
|
---|
500 |
|
---|
501 | }
|
---|
502 | else if (rc < E_NOTFOUND ) { //cache1+2+3
|
---|
503 | //no increase of statistics here, cachetime is not real time
|
---|
504 | s = get_stat(rdr, &q);
|
---|
505 | if (s != NULL)
|
---|
506 | s->last_received = now;
|
---|
507 | return;
|
---|
508 | }
|
---|
509 | else if (rc == E_NOTFOUND||rc == E_INVALID) { //not found / invalid
|
---|
510 | //special rcEx codes means temporary problems, so do not block!
|
---|
511 | //CCcam card can't decode, 0x28=NOK1, 0x29=NOK2
|
---|
512 | //CCcam loop detection = E2_CCCAM_LOOP
|
---|
513 | if (er->rcEx >= LB_NONBLOCK_E2_FIRST) {
|
---|
514 | s = get_stat(rdr, &q);
|
---|
515 | if (s != NULL)
|
---|
516 | s->last_received = now; //to avoid timeouts
|
---|
517 | return;
|
---|
518 | }
|
---|
519 |
|
---|
520 | s = get_add_stat(rdr, &q);
|
---|
521 | if (s->rc == E_NOTFOUND) { //we have already "not found", so we change the time. In some cases (with services/ident set) the failing reader is selected again:
|
---|
522 | if (ecm_time < 100)
|
---|
523 | ecm_time = 100;
|
---|
524 | s->time_avg += ecm_time;
|
---|
525 | }
|
---|
526 |
|
---|
527 | if (s->ecm_count > cfg.lb_min_ecmcount) //there were many founds? Do not close, give them another chance
|
---|
528 | s->ecm_count = 0;
|
---|
529 | else
|
---|
530 | s->rc = rc;
|
---|
531 |
|
---|
532 | inc_fail(s);
|
---|
533 | s->last_received = now;
|
---|
534 |
|
---|
535 | //reduce ecm_count step by step
|
---|
536 | if (!cfg.lb_reopen_mode)
|
---|
537 | s->ecm_count /= 10;
|
---|
538 | }
|
---|
539 | else if (rc == E_TIMEOUT) { //timeout
|
---|
540 | s = get_add_stat(rdr, &q);
|
---|
541 |
|
---|
542 | //catch suddenly occuring timeouts and block reader:
|
---|
543 | // if ((int)(now-s->last_received) < (int)(5*cfg.ctimeout) &&
|
---|
544 | // s->rc == E_FOUND && s->ecm_count == 0) {
|
---|
545 | // s->rc = E_TIMEOUT;
|
---|
546 | // //inc_fail(s); //do not inc fail factor in this case
|
---|
547 | // }
|
---|
548 | //reader is longer than 5s connected && not more then 5 pending ecms:
|
---|
549 | // else if ((cl->login+(int)(2*cfg.ctimeout/1000)) < now && cl->pending < 5 &&
|
---|
550 | // s->rc == E_FOUND && s->ecm_count == 0) {
|
---|
551 | // s->rc = E_TIMEOUT;
|
---|
552 | // inc_fail(s);
|
---|
553 | // }
|
---|
554 | // else
|
---|
555 | if (!s->ecm_count)
|
---|
556 | s->rc = E_TIMEOUT;
|
---|
557 | else if (s->rc == E_FOUND && now > s->last_received+1) {
|
---|
558 | //search for alternate readers. If we have one, block this reader:
|
---|
559 | int8_t n = 0;
|
---|
560 | struct s_ecm_answer *ea;
|
---|
561 | for (ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
562 | if (ea->reader != rdr && ea->rc < E_NOTFOUND){
|
---|
563 | n = 1;
|
---|
564 | break;
|
---|
565 | }
|
---|
566 | }
|
---|
567 | if (n > 0) //We have alternative readers, so we can block this one:
|
---|
568 | s->rc = E_TIMEOUT;
|
---|
569 | else { //No other reader found. Inc fail factor and retry lb_min_ecmount times:
|
---|
570 | inc_fail(s);
|
---|
571 | if (s->fail_factor > cfg.lb_min_ecmcount) {
|
---|
572 | s->fail_factor = 0;
|
---|
573 | s->rc = E_TIMEOUT;
|
---|
574 | }
|
---|
575 | }
|
---|
576 | }
|
---|
577 |
|
---|
578 | s->last_received = now;
|
---|
579 |
|
---|
580 | //add timeout to s:
|
---|
581 | if (ecm_time<=0 || ecm_time > (int)cfg.ctimeout)
|
---|
582 | ecm_time = cfg.ctimeout;
|
---|
583 | s->time_idx++;
|
---|
584 | if (s->time_idx >= LB_MAX_STAT_TIME)
|
---|
585 | s->time_idx = 0;
|
---|
586 | s->time_stat[s->time_idx] = ecm_time;
|
---|
587 | calc_stat(s);
|
---|
588 | }
|
---|
589 | else
|
---|
590 | {
|
---|
591 | #ifdef WITH_DEBUG
|
---|
592 | if (rc >= E_FOUND && (D_LB & cs_dblevel)) {
|
---|
593 | char buf[ECM_FMT_LEN];
|
---|
594 | format_ecm(er, buf, ECM_FMT_LEN);
|
---|
595 | cs_debug_mask(D_LB, "loadbalancer: not handled stat for reader %s: rc %d %s time %dms",
|
---|
596 | rdr->label, rc, buf, ecm_time);
|
---|
597 | }
|
---|
598 | #endif
|
---|
599 | return;
|
---|
600 | }
|
---|
601 |
|
---|
602 | housekeeping_stat(0);
|
---|
603 |
|
---|
604 | #ifdef WITH_DEBUG
|
---|
605 | if (D_LB & cs_dblevel) {
|
---|
606 | char buf[ECM_FMT_LEN];
|
---|
607 | format_ecm(er, buf, ECM_FMT_LEN);
|
---|
608 | cs_debug_mask(D_LB, "loadbalancer: adding stat for reader %s: rc %d %s time %dms fail %d",
|
---|
609 | rdr->label, rc, buf, ecm_time, s->fail_factor);
|
---|
610 | }
|
---|
611 | #endif
|
---|
612 |
|
---|
613 | if (cfg.lb_save) {
|
---|
614 | stat_load_save++;
|
---|
615 | if (stat_load_save > cfg.lb_save)
|
---|
616 | save_stat_to_file(1);
|
---|
617 | }
|
---|
618 | }
|
---|
619 |
|
---|
620 | static void reset_stat(STAT_QUERY *q)
|
---|
621 | {
|
---|
622 | //cs_debug_mask(D_LB, "loadbalance: resetting ecm count");
|
---|
623 | struct s_reader *rdr;
|
---|
624 | cs_readlock(&readerlist_lock);
|
---|
625 | for (rdr=first_active_reader; rdr ; rdr=rdr->next) {
|
---|
626 | if (rdr->lb_stat && rdr->client) {
|
---|
627 | READER_STAT *s = get_stat(rdr, q);
|
---|
628 | if (s) {
|
---|
629 | if (s->ecm_count > 0)
|
---|
630 | s->ecm_count = 1; //not zero, so we know it's decodeable
|
---|
631 | s->rc = E_FOUND;
|
---|
632 | s->fail_factor = 0;
|
---|
633 | }
|
---|
634 | }
|
---|
635 | }
|
---|
636 | cs_readunlock(&readerlist_lock);
|
---|
637 | }
|
---|
638 |
|
---|
639 | int32_t clean_stat_by_rc(struct s_reader *rdr, int8_t rc, int8_t inverse)
|
---|
640 | {
|
---|
641 | int32_t count = 0;
|
---|
642 | if (rdr && rdr->lb_stat) {
|
---|
643 | cs_writelock(&rdr->lb_stat_lock);
|
---|
644 | READER_STAT *s;
|
---|
645 | LL_ITER itr = ll_iter_create(rdr->lb_stat);
|
---|
646 | while ((s = ll_iter_next(&itr))) {
|
---|
647 | if ((!inverse && s->rc == rc) || (inverse && s->rc != rc)) {
|
---|
648 | ll_iter_remove_data(&itr);
|
---|
649 | count++;
|
---|
650 | }
|
---|
651 | }
|
---|
652 | cs_writeunlock(&rdr->lb_stat_lock);
|
---|
653 | }
|
---|
654 | return count;
|
---|
655 | }
|
---|
656 |
|
---|
657 | int32_t clean_all_stats_by_rc(int8_t rc, int8_t inverse)
|
---|
658 | {
|
---|
659 | int32_t count = 0;
|
---|
660 | LL_ITER itr = ll_iter_create(configured_readers);
|
---|
661 | struct s_reader *rdr;
|
---|
662 | while ((rdr = ll_iter_next(&itr))) {
|
---|
663 | count += clean_stat_by_rc(rdr, rc, inverse);
|
---|
664 | }
|
---|
665 | save_stat_to_file(0);
|
---|
666 | return count;
|
---|
667 | }
|
---|
668 |
|
---|
669 | int32_t clean_stat_by_id(struct s_reader *rdr, uint16_t caid, uint32_t prid, uint16_t srvid, uint16_t chid, uint16_t ecmlen)
|
---|
670 | {
|
---|
671 | int32_t count = 0;
|
---|
672 | if (rdr && rdr->lb_stat) {
|
---|
673 |
|
---|
674 | cs_writelock(&rdr->lb_stat_lock);
|
---|
675 | READER_STAT *s;
|
---|
676 | LL_ITER itr = ll_iter_create(rdr->lb_stat);
|
---|
677 | while ((s = ll_iter_next(&itr))) {
|
---|
678 | if (s->caid == caid &&
|
---|
679 | s->prid == prid &&
|
---|
680 | s->srvid == srvid &&
|
---|
681 | s->chid == chid &&
|
---|
682 | s->ecmlen == ecmlen) {
|
---|
683 | ll_iter_remove_data(&itr);
|
---|
684 | count++;
|
---|
685 | break; // because the entry should unique we can left here
|
---|
686 | }
|
---|
687 | }
|
---|
688 | cs_writeunlock(&rdr->lb_stat_lock);
|
---|
689 | }
|
---|
690 | return count;
|
---|
691 | }
|
---|
692 |
|
---|
693 |
|
---|
694 | static int32_t has_ident(FTAB *ftab, ECM_REQUEST *er) {
|
---|
695 |
|
---|
696 | if (!ftab || !ftab->filts)
|
---|
697 | return 0;
|
---|
698 |
|
---|
699 | int32_t j, k;
|
---|
700 |
|
---|
701 | for (j = 0; j < ftab->nfilts; j++) {
|
---|
702 | if (ftab->filts[j].caid) {
|
---|
703 | if (ftab->filts[j].caid==er->caid) { //caid matches!
|
---|
704 | int32_t nprids = ftab->filts[j].nprids;
|
---|
705 | if (!nprids) // No Provider ->Ok
|
---|
706 | return 1;
|
---|
707 |
|
---|
708 | for (k = 0; k < nprids; k++) {
|
---|
709 | uint32_t prid = ftab->filts[j].prids[k];
|
---|
710 | if (prid == er->prid) { //Provider matches
|
---|
711 | return 1;
|
---|
712 | }
|
---|
713 | }
|
---|
714 | }
|
---|
715 | }
|
---|
716 | }
|
---|
717 | return 0; //No match!
|
---|
718 | }
|
---|
719 |
|
---|
720 | static int32_t get_retrylimit(ECM_REQUEST *er) {
|
---|
721 | int32_t i;
|
---|
722 | for (i = 0; i < cfg.lb_retrylimittab.n; i++) {
|
---|
723 | if (cfg.lb_retrylimittab.caid[i] == er->caid || cfg.lb_retrylimittab.caid[i] == er->caid>>8)
|
---|
724 | return cfg.lb_retrylimittab.value[i];
|
---|
725 | }
|
---|
726 | return cfg.lb_retrylimit;
|
---|
727 | }
|
---|
728 |
|
---|
729 | static int32_t get_nbest_readers(ECM_REQUEST *er) {
|
---|
730 | int32_t i;
|
---|
731 | for (i = 0; i < cfg.lb_nbest_readers_tab.n; i++) {
|
---|
732 | if (cfg.lb_nbest_readers_tab.caid[i] == er->caid || cfg.lb_nbest_readers_tab.caid[i] == er->caid>>8)
|
---|
733 | return cfg.lb_nbest_readers_tab.value[i];
|
---|
734 | }
|
---|
735 | return cfg.lb_nbest_readers;
|
---|
736 | }
|
---|
737 |
|
---|
738 | static time_t get_reopen_seconds(READER_STAT *s)
|
---|
739 | {
|
---|
740 | int32_t max = (INT_MAX / cfg.lb_reopen_seconds);
|
---|
741 | if (max > 9999) max = 9999;
|
---|
742 | if (s->fail_factor > max)
|
---|
743 | s->fail_factor = max;
|
---|
744 | if (!s->fail_factor)
|
---|
745 | return cfg.lb_reopen_seconds;
|
---|
746 | return (time_t)s->fail_factor * (time_t)cfg.lb_reopen_seconds;
|
---|
747 | }
|
---|
748 |
|
---|
749 | static void convert_to_beta_int(ECM_REQUEST *er, uint16_t caid_to)
|
---|
750 | {
|
---|
751 | unsigned char md5tmp[MD5_DIGEST_LENGTH];
|
---|
752 | convert_to_beta(er->client, er, caid_to);
|
---|
753 | // update ecmd5 for store ECM in cache
|
---|
754 | memcpy(er->ecmd5, MD5(er->ecm+13, er->ecmlen-13, md5tmp), CS_ECMSTORESIZE);
|
---|
755 | cacheex_update_hash(er);
|
---|
756 | er->btun = 2; //marked as auto-betatunnel converted. Also for fixing recursive lock in get_cw
|
---|
757 | }
|
---|
758 |
|
---|
759 |
|
---|
760 | static void convert_to_nagra_int(ECM_REQUEST *er, uint16_t caid_to)
|
---|
761 | {
|
---|
762 | unsigned char md5tmp[MD5_DIGEST_LENGTH];
|
---|
763 | convert_to_nagra(er->client, er, caid_to);
|
---|
764 | // update ecmd5 for store ECM in cache
|
---|
765 | memcpy(er->ecmd5, MD5(er->ecm+3, er->ecmlen-3, md5tmp), CS_ECMSTORESIZE);
|
---|
766 | cacheex_update_hash(er);
|
---|
767 | er->btun = 2; //marked as auto-betatunnel converted. Also for fixing recursive lock in get_cw
|
---|
768 | }
|
---|
769 |
|
---|
770 | uint16_t get_betatunnel_caid_to(uint16_t caid){
|
---|
771 | int32_t lbbm = cfg.lb_auto_betatunnel_mode;
|
---|
772 | if (lbbm <=3) {
|
---|
773 | if (caid == 0x1801) return 0x1722;
|
---|
774 | if (caid == 0x1833) return 0x1702;
|
---|
775 | if (caid == 0x1834) return 0x1722;
|
---|
776 | if (caid == 0x1835) return 0x1722;
|
---|
777 | }
|
---|
778 | if (lbbm >=1) {
|
---|
779 | if (caid == 0x1702) return 0x1833;
|
---|
780 | }
|
---|
781 | if (lbbm == 1 || lbbm == 4 ) {
|
---|
782 | if (caid == 0x1722) return 0x1801;
|
---|
783 | } else if (lbbm == 2 || lbbm == 5 ) {
|
---|
784 | if (caid == 0x1722) return 0x1834;
|
---|
785 | } else if (lbbm == 3 || lbbm == 6 ) {
|
---|
786 | if (caid == 0x1722) return 0x1835;
|
---|
787 | }
|
---|
788 | return 0;
|
---|
789 | }
|
---|
790 |
|
---|
791 | void check_lb_auto_betatunnel_mode(ECM_REQUEST *er) {
|
---|
792 | int32_t lbbm = cfg.lb_auto_betatunnel_mode;
|
---|
793 | if ( lbbm == 1 || lbbm == 4) {
|
---|
794 | er->caid = 0x1801;
|
---|
795 | } else if ( lbbm == 2 || lbbm == 5) {
|
---|
796 | er->caid = 0x1834;
|
---|
797 | } else if ( lbbm == 3 || lbbm == 6) {
|
---|
798 | er->caid = 0x1835;
|
---|
799 | }
|
---|
800 | ////no other way to autodetect is 1801,1834 or 1835
|
---|
801 | }
|
---|
802 |
|
---|
803 | uint16_t get_rdr_caid(struct s_reader *rdr) {
|
---|
804 | if (is_network_reader(rdr)) {
|
---|
805 | return 0; //reader caid is not real caid
|
---|
806 | } else {
|
---|
807 | return rdr->caid;
|
---|
808 | }
|
---|
809 | }
|
---|
810 |
|
---|
811 | uint16_t is_betatunnel_caid(uint16_t caid){
|
---|
812 | if (caid == 0x1702 || caid == 0x1722 || caid == 0x1801 || caid == 0x1833 || caid == 0x1834 || caid == 0x1835) return 1;
|
---|
813 | return 0;
|
---|
814 | }
|
---|
815 |
|
---|
816 | /**
|
---|
817 | * Gets best reader for caid/prid/srvid/ecmlen.
|
---|
818 | * Best reader is evaluated by lowest avg time but only if ecm_count > cfg.lb_min_ecmcount (5)
|
---|
819 | * Also the reader is asked if he is "available"
|
---|
820 | * returns ridx when found or -1 when not found
|
---|
821 | */
|
---|
822 | void stat_get_best_reader(ECM_REQUEST *er)
|
---|
823 | {
|
---|
824 | if (!cfg.lb_mode || cfg.lb_mode==LB_LOG_ONLY)
|
---|
825 | return;
|
---|
826 |
|
---|
827 | if (!er->reader_avail)
|
---|
828 | return;
|
---|
829 |
|
---|
830 | struct s_reader *rdr;
|
---|
831 | struct s_ecm_answer *ea;
|
---|
832 |
|
---|
833 | //preferred card forwarding (CCcam client):
|
---|
834 | if (cccam_forward_origin_card(er))
|
---|
835 | return;
|
---|
836 |
|
---|
837 | STAT_QUERY q;
|
---|
838 | get_stat_query(er, &q);
|
---|
839 |
|
---|
840 | //auto-betatunnel: The trick is: "let the loadbalancer decide"!
|
---|
841 | if (cfg.lb_auto_betatunnel && er->caid >> 8 == 0x18 && er->ecmlen) { //nagra
|
---|
842 | uint16_t caid_to = get_betatunnel_caid_to(er->caid);
|
---|
843 | if (caid_to) {
|
---|
844 | int8_t needs_stats_nagra = 1, needs_stats_beta = 1;
|
---|
845 |
|
---|
846 | //Clone query parameters for beta:
|
---|
847 | STAT_QUERY qbeta = q;
|
---|
848 | qbeta.caid = caid_to;
|
---|
849 | qbeta.prid = 0;
|
---|
850 | qbeta.ecmlen = er->ecm[2] + 3 + 10;
|
---|
851 |
|
---|
852 | int32_t time_nagra = 0;
|
---|
853 | int32_t time_beta = 0;
|
---|
854 | int32_t weight;
|
---|
855 | int32_t ntime;
|
---|
856 |
|
---|
857 | READER_STAT *stat_nagra = NULL;
|
---|
858 | READER_STAT *stat_beta = NULL;
|
---|
859 |
|
---|
860 | //What is faster? nagra or beta?
|
---|
861 | int8_t isn;
|
---|
862 | int8_t isb;
|
---|
863 | int8_t overall_valid = 0;
|
---|
864 | int8_t overall_nvalid = 0;
|
---|
865 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
866 | isn = 0;
|
---|
867 | isb = 0;
|
---|
868 | rdr = ea->reader;
|
---|
869 | weight = rdr->lb_weight;
|
---|
870 | if (weight <= 0) weight = 1;
|
---|
871 |
|
---|
872 |
|
---|
873 | //Check if betatunnel is allowed on this reader:
|
---|
874 | int8_t valid = chk_ctab(caid_to, &rdr->ctab) //Check caid
|
---|
875 | && chk_rfilter2(caid_to, 0, rdr) //Ident
|
---|
876 | && chk_srvid_by_caid_prov_rdr(rdr, caid_to, 0) //Services
|
---|
877 | && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr,caid_to)); //rdr-caid
|
---|
878 | if (valid) {
|
---|
879 | stat_beta = get_stat(rdr, &qbeta);
|
---|
880 | overall_valid = 1;
|
---|
881 | }
|
---|
882 | //else
|
---|
883 | //stat_beta = NULL;
|
---|
884 |
|
---|
885 | //Check if nagra is allowed on this reader:
|
---|
886 | int8_t nvalid = chk_ctab(er->caid, &rdr->ctab)//Check caid
|
---|
887 | && chk_rfilter2(er->caid, 0, rdr) //Ident
|
---|
888 | && chk_srvid_by_caid_prov_rdr(rdr, er->caid, 0) //Services
|
---|
889 | && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr,er->caid)); //rdr-caid
|
---|
890 | if (nvalid) {
|
---|
891 | stat_nagra = get_stat(rdr, &q);
|
---|
892 | overall_nvalid = 1;
|
---|
893 | }
|
---|
894 |
|
---|
895 | //calculate nagra data:
|
---|
896 | if (stat_nagra && stat_nagra->rc == E_FOUND) {
|
---|
897 | ntime = stat_nagra->time_avg*100/weight;
|
---|
898 | if (!time_nagra || ntime < time_nagra)
|
---|
899 | time_nagra = ntime;
|
---|
900 | }
|
---|
901 |
|
---|
902 | //calculate beta data:
|
---|
903 | if (stat_beta && stat_beta->rc == E_FOUND) {
|
---|
904 | ntime = stat_beta->time_avg*100/weight;
|
---|
905 | if (!time_beta || ntime < time_beta)
|
---|
906 | time_beta = ntime;
|
---|
907 | }
|
---|
908 |
|
---|
909 | //Uncomplete reader evaluation, we need more stats!
|
---|
910 | if (stat_nagra){
|
---|
911 | needs_stats_nagra = 0;
|
---|
912 | isn = 1;
|
---|
913 | }
|
---|
914 | if (stat_beta){
|
---|
915 | needs_stats_beta = 0;
|
---|
916 | isb = 1;
|
---|
917 | }
|
---|
918 | cs_debug_mask(D_LB, "loadbalancer-betatunnel valid %d, stat_nagra %d, stat_beta %d, (%04X,%04X)", valid, isn, isb ,get_rdr_caid(rdr),caid_to);
|
---|
919 | }
|
---|
920 |
|
---|
921 | if (!overall_valid)//we have no valid betatunnel reader also we don't needs stats (converted)
|
---|
922 | needs_stats_beta = 0;
|
---|
923 |
|
---|
924 | if (!overall_nvalid) //we have no valid reader also we don't needs stats (unconverted)
|
---|
925 | needs_stats_nagra = 0;
|
---|
926 |
|
---|
927 | if (cfg.lb_auto_betatunnel_prefer_beta && time_beta){
|
---|
928 | time_beta = time_beta * cfg.lb_auto_betatunnel_prefer_beta/100;
|
---|
929 | if (time_beta <= 0)
|
---|
930 | time_beta = 1;
|
---|
931 | }
|
---|
932 |
|
---|
933 | if (needs_stats_nagra || needs_stats_beta) {
|
---|
934 | cs_debug_mask(D_LB, "loadbalancer-betatunnel %04X:%04X (%d/%d) needs more statistics...", er->caid, caid_to,
|
---|
935 | needs_stats_nagra, needs_stats_beta);
|
---|
936 | if (needs_stats_beta) { //try beta first
|
---|
937 |
|
---|
938 | convert_to_beta_int(er, caid_to);
|
---|
939 | get_stat_query(er, &q);
|
---|
940 | }
|
---|
941 | }
|
---|
942 | else if (time_beta && (!time_nagra || time_beta <= time_nagra)) {
|
---|
943 | cs_debug_mask(D_LB, "loadbalancer-betatunnel %04X:%04X selected beta: n%dms > b%dms", er->caid, caid_to, time_nagra, time_beta);
|
---|
944 | convert_to_beta_int(er, caid_to);
|
---|
945 | get_stat_query(er, &q);
|
---|
946 | }
|
---|
947 | else {
|
---|
948 | cs_debug_mask(D_LB, "loadbalancer-betatunnel %04X:%04X selected nagra: n%dms < b%dms", er->caid, caid_to, time_nagra, time_beta);
|
---|
949 | }
|
---|
950 | // else nagra is faster or no beta, so continue unmodified
|
---|
951 | }
|
---|
952 | } else
|
---|
953 |
|
---|
954 |
|
---|
955 | if (cfg.lb_auto_betatunnel && (er->caid == 0x1702 || er->caid == 0x1722) && er->ocaid == 0x0000 && er->ecmlen) { //beta
|
---|
956 | uint16_t caid_to = get_betatunnel_caid_to(er->caid);
|
---|
957 | if (caid_to) {
|
---|
958 | int8_t needs_stats_nagra = 1, needs_stats_beta = 1;
|
---|
959 |
|
---|
960 | //Clone query parameters for beta:
|
---|
961 | STAT_QUERY qnagra = q;
|
---|
962 | qnagra.caid = caid_to;
|
---|
963 | qnagra.prid = 0;
|
---|
964 | qnagra.ecmlen = er->ecm[2] - 7;
|
---|
965 |
|
---|
966 | int32_t time_nagra = 0;
|
---|
967 | int32_t time_beta = 0;
|
---|
968 | int32_t weight;
|
---|
969 | int32_t avg_time;
|
---|
970 |
|
---|
971 | READER_STAT *stat_nagra = NULL;
|
---|
972 | READER_STAT *stat_beta = NULL;
|
---|
973 | //What is faster? nagra or beta?
|
---|
974 | int8_t isb;
|
---|
975 | int8_t isn;
|
---|
976 | int8_t overall_valid = 0;
|
---|
977 | int8_t overall_bvalid = 0;
|
---|
978 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
979 | isb = 0;
|
---|
980 | isn = 0;
|
---|
981 | rdr = ea->reader;
|
---|
982 | weight = rdr->lb_weight;
|
---|
983 | if (weight <= 0) weight = 1;
|
---|
984 |
|
---|
985 |
|
---|
986 |
|
---|
987 | //Check if reverse betatunnel is allowed on this reader:
|
---|
988 | int8_t valid = chk_ctab(caid_to, &rdr->ctab)//, rdr->typ) //Check caid
|
---|
989 | && chk_rfilter2(caid_to, 0, rdr) //Ident
|
---|
990 | && chk_srvid_by_caid_prov_rdr(rdr, caid_to, 0) //Services
|
---|
991 | && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr,caid_to)); //rdr-caid
|
---|
992 | if (valid) {
|
---|
993 | stat_nagra = get_stat(rdr, &qnagra);
|
---|
994 | overall_valid = 1;
|
---|
995 | }
|
---|
996 | //else
|
---|
997 | //stat_nagra = NULL;
|
---|
998 |
|
---|
999 | //Check if beta is allowed on this reader:
|
---|
1000 | int8_t bvalid = chk_ctab(er->caid, &rdr->ctab)//, rdr->typ) //Check caid
|
---|
1001 | && chk_rfilter2(er->caid, 0, rdr) //Ident
|
---|
1002 | && chk_srvid_by_caid_prov_rdr(rdr, er->caid, 0) //Services
|
---|
1003 | && (!get_rdr_caid(rdr) || chk_caid_rdr(rdr,er->caid)); //rdr-caid
|
---|
1004 | if (bvalid) {
|
---|
1005 | stat_beta = get_stat(rdr, &q);
|
---|
1006 | overall_bvalid = 1;
|
---|
1007 | }
|
---|
1008 |
|
---|
1009 | //calculate nagra data:
|
---|
1010 | if (stat_nagra && stat_nagra->rc == E_FOUND) {
|
---|
1011 | avg_time = stat_nagra->time_avg*100/weight;
|
---|
1012 | if (!time_nagra || avg_time < time_nagra)
|
---|
1013 | time_nagra = avg_time;
|
---|
1014 | }
|
---|
1015 |
|
---|
1016 | //calculate beta data:
|
---|
1017 | if (stat_beta && stat_beta->rc == E_FOUND) {
|
---|
1018 | avg_time = stat_beta->time_avg*100/weight;
|
---|
1019 | if (!time_beta || avg_time < time_beta)
|
---|
1020 | time_beta = avg_time;
|
---|
1021 | }
|
---|
1022 |
|
---|
1023 | //Uncomplete reader evaluation, we need more stats!
|
---|
1024 | if (stat_beta){
|
---|
1025 | needs_stats_beta = 0;
|
---|
1026 | isb = 1;
|
---|
1027 | }
|
---|
1028 | if (stat_nagra){
|
---|
1029 | needs_stats_nagra = 0;
|
---|
1030 | isn = 1;
|
---|
1031 | }
|
---|
1032 | cs_debug_mask(D_LB, "loadbalancer-betatunnel valid %d, stat_beta %d, stat_nagra %d, (%04X,%04X)", valid, isb, isn ,get_rdr_caid(rdr),caid_to);
|
---|
1033 | }
|
---|
1034 |
|
---|
1035 | if (!overall_valid)//we have no valid reverse betatunnel reader also we don't needs stats (converted)
|
---|
1036 | needs_stats_nagra = 0;
|
---|
1037 |
|
---|
1038 | if (!overall_bvalid) //we have no valid reader also we don't needs stats (unconverted)
|
---|
1039 | needs_stats_beta = 0;
|
---|
1040 |
|
---|
1041 | if (cfg.lb_auto_betatunnel_prefer_beta && time_beta) {
|
---|
1042 | time_beta = time_beta * cfg.lb_auto_betatunnel_prefer_beta/100;
|
---|
1043 | if (time_beta < 0)
|
---|
1044 | time_beta = 0;
|
---|
1045 | }
|
---|
1046 |
|
---|
1047 | //if we needs stats, we send 2 ecm requests: 18xx and 17xx:
|
---|
1048 | if (needs_stats_nagra || needs_stats_beta) {
|
---|
1049 | cs_debug_mask(D_LB, "loadbalancer-betatunnel %04X:%04X (%d/%d) needs more statistics...", er->caid, caid_to,
|
---|
1050 | needs_stats_beta, needs_stats_nagra);
|
---|
1051 | if (needs_stats_nagra){// try nagra frist
|
---|
1052 |
|
---|
1053 | convert_to_nagra_int(er, caid_to);
|
---|
1054 | get_stat_query(er, &q);
|
---|
1055 |
|
---|
1056 | }
|
---|
1057 | }
|
---|
1058 | else if (time_nagra && (!time_beta || time_nagra <= time_beta)) {
|
---|
1059 | cs_debug_mask(D_LB, "loadbalancer-betatunnel %04X:%04X selected nagra: b%dms > n%dms", er->caid, caid_to, time_beta, time_nagra);
|
---|
1060 | convert_to_nagra_int(er, caid_to);
|
---|
1061 | get_stat_query(er, &q);
|
---|
1062 | }
|
---|
1063 | else {
|
---|
1064 | cs_debug_mask(D_LB, "loadbalancer-betatunnel %04X:%04X selected beta: b%dms < n%dms", er->caid, caid_to, time_beta, time_nagra);
|
---|
1065 | }
|
---|
1066 |
|
---|
1067 | }
|
---|
1068 | }
|
---|
1069 |
|
---|
1070 | if (cfg.lb_auto_betatunnel && is_betatunnel_caid(er->caid)) {
|
---|
1071 | //check again is caid valied to reader
|
---|
1072 | //with both caid on local readers or with proxy
|
---|
1073 | //(both caid will setup to reader for make tunnel caid in share (ccc) visible)
|
---|
1074 | //make sure dosn't send a beta ecm to nagra reader (or reverse)
|
---|
1075 | struct s_ecm_answer *prv = NULL;
|
---|
1076 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1077 | rdr = ea->reader;
|
---|
1078 | if (is_network_reader(rdr)) { //reader caid is not real caid
|
---|
1079 | prv = ea;
|
---|
1080 | continue; // proxy can convert or reject
|
---|
1081 | }
|
---|
1082 | cs_debug_mask(D_LB, "check again caid %04X on reader %s", er->caid, rdr->label);
|
---|
1083 | if ( !get_rdr_caid(ea->reader) || chk_caid_rdr(ea->reader,er->caid)) {
|
---|
1084 | prv = ea;
|
---|
1085 | } else {
|
---|
1086 | if (!rdr->fallback) er->reader_avail--;
|
---|
1087 | cs_debug_mask(D_LB, "caid %04X not found in caidlist, reader %s removed from request reader list", er->caid, rdr->label);
|
---|
1088 | if (prv){
|
---|
1089 | prv->next = ea->next;
|
---|
1090 | } else
|
---|
1091 | er->matching_rdr = ea->next;
|
---|
1092 | }
|
---|
1093 | }
|
---|
1094 | if (!er->reader_avail)
|
---|
1095 | return;
|
---|
1096 | }
|
---|
1097 |
|
---|
1098 | struct timeb check_time;
|
---|
1099 | cs_ftime(&check_time);
|
---|
1100 | time_t current_time = time(NULL);
|
---|
1101 | int32_t current = -1;
|
---|
1102 | READER_STAT *s = NULL;
|
---|
1103 | int32_t retrylimit = get_retrylimit(er);
|
---|
1104 | int32_t reader_count = 0;
|
---|
1105 | int32_t new_stats = 0;
|
---|
1106 | int32_t nlocal_readers = 0;
|
---|
1107 | int32_t nbest_readers = get_nbest_readers(er);
|
---|
1108 | int32_t nfb_readers = cfg.lb_nfb_readers;
|
---|
1109 | int32_t nreaders = cfg.lb_max_readers;
|
---|
1110 | if (!nreaders)
|
---|
1111 | nreaders = -1;
|
---|
1112 | else if (nreaders <= cfg.lb_nbest_readers)
|
---|
1113 | nreaders = cfg.lb_nbest_readers+1;
|
---|
1114 | int32_t nmaxreopen = nreaders-nbest_readers;
|
---|
1115 | if (nmaxreopen < 1)
|
---|
1116 | {
|
---|
1117 | if (nreaders > 0) nmaxreopen = 1;
|
---|
1118 | else nmaxreopen = er->reader_avail;
|
---|
1119 | }
|
---|
1120 |
|
---|
1121 | #ifdef WITH_DEBUG
|
---|
1122 | if (cs_dblevel & D_LB) {
|
---|
1123 | //loadbalancer debug output:
|
---|
1124 | int32_t nr = 0;
|
---|
1125 | char buf[512];
|
---|
1126 | int n, l=512;
|
---|
1127 | char *rptr = buf;
|
---|
1128 | *rptr = 0;
|
---|
1129 |
|
---|
1130 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1131 | nr++;
|
---|
1132 |
|
---|
1133 | if (nr>5) continue;
|
---|
1134 |
|
---|
1135 | if (!(ea->status & READER_FALLBACK))
|
---|
1136 | n = snprintf(rptr, l, "%s%s%s ", ea->reader->label, (ea->status&READER_CACHEEX)?"*":"", (ea->status&READER_LOCAL)?"L":"");
|
---|
1137 | else
|
---|
1138 | n = snprintf(rptr, l, "[%s%s%s] ", ea->reader->label, (ea->status&READER_CACHEEX)?"*":"", (ea->status&READER_LOCAL)?"L":"");
|
---|
1139 | rptr+=n;
|
---|
1140 | l-=n;
|
---|
1141 | }
|
---|
1142 |
|
---|
1143 | if (nr>5)
|
---|
1144 | snprintf(rptr, l, "...(%d more)", nr - 5);
|
---|
1145 |
|
---|
1146 | char ecmbuf[ECM_FMT_LEN];
|
---|
1147 | format_ecm(er, ecmbuf, ECM_FMT_LEN);
|
---|
1148 |
|
---|
1149 | cs_debug_mask(D_LB, "loadbalancer: client %s for %s: n=%d valid readers: %s",
|
---|
1150 | username(er->client), ecmbuf, nr, buf);
|
---|
1151 | }
|
---|
1152 | #endif
|
---|
1153 |
|
---|
1154 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1155 | ea->status &= ~(READER_ACTIVE|READER_FALLBACK);
|
---|
1156 | ea->value = 0;
|
---|
1157 | }
|
---|
1158 |
|
---|
1159 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1160 | rdr = ea->reader;
|
---|
1161 | #ifdef CS_CACHEEX
|
---|
1162 | int8_t cacheex = rdr->cacheex.mode;
|
---|
1163 | if (cacheex == 1) {
|
---|
1164 | ea->status |= READER_ACTIVE; //no statistics, this reader is a cacheex reader and so always active
|
---|
1165 | continue;
|
---|
1166 | }
|
---|
1167 | #endif
|
---|
1168 | struct s_client *cl = rdr->client;
|
---|
1169 | reader_count++;
|
---|
1170 |
|
---|
1171 | int32_t weight = rdr->lb_weight <= 0?100:rdr->lb_weight;
|
---|
1172 |
|
---|
1173 | s = get_stat(rdr, &q);
|
---|
1174 | if (!s) {
|
---|
1175 | if (nmaxreopen>0) {
|
---|
1176 | cs_debug_mask(D_LB, "loadbalancer: starting statistics for reader %s", rdr->label);
|
---|
1177 | ea->status |= READER_ACTIVE; //no statistics, this reader is active (now) but we need statistics first!
|
---|
1178 | new_stats = 1;
|
---|
1179 | //nreaders--;
|
---|
1180 | if (!cfg.lb_reopen_mode)
|
---|
1181 | nmaxreopen--;
|
---|
1182 | }
|
---|
1183 | continue;
|
---|
1184 | }
|
---|
1185 |
|
---|
1186 | if (nmaxreopen>0 && (s->ecm_count < 0||(s->ecm_count > cfg.lb_max_ecmcount && s->time_avg > retrylimit))) {
|
---|
1187 | cs_debug_mask(D_LB, "loadbalancer: max ecms (%d) reached by reader %s, resetting statistics", cfg.lb_max_ecmcount, rdr->label);
|
---|
1188 | reset_stat(&q);
|
---|
1189 | ea->status |= READER_ACTIVE; //max ecm reached, get new statistics
|
---|
1190 | nreaders--;
|
---|
1191 | nmaxreopen--;
|
---|
1192 | continue;
|
---|
1193 | }
|
---|
1194 |
|
---|
1195 | // if (nreopen_readers && s->rc != E_FOUND && s->last_received+get_reopen_seconds(s) < current_time) {
|
---|
1196 | // cs_debug_mask(D_LB, "loadbalancer: reopen reader %s", rdr->label);
|
---|
1197 | // reset_stat(er->caid, prid, er->srvid, er->chid, er->ecmlen);
|
---|
1198 | // ea->status |= READER_ACTIVE; //max ecm reached, get new statistics
|
---|
1199 | // nreopen_readers--;
|
---|
1200 | // continue;
|
---|
1201 | // }
|
---|
1202 |
|
---|
1203 | int32_t hassrvid;
|
---|
1204 | if(cl)
|
---|
1205 | hassrvid = has_srvid(cl, er) || has_ident(&rdr->ftab, er);
|
---|
1206 | else
|
---|
1207 | hassrvid = 0;
|
---|
1208 |
|
---|
1209 | if (nmaxreopen>0 && s->rc == E_FOUND && s->ecm_count < cfg.lb_min_ecmcount) {
|
---|
1210 | cs_debug_mask(D_LB, "loadbalancer: reader %s needs more statistics", rdr->label);
|
---|
1211 | ea->status |= READER_ACTIVE; //need more statistics!
|
---|
1212 | new_stats = 1;
|
---|
1213 | nreaders--;
|
---|
1214 | nmaxreopen--;
|
---|
1215 | continue;
|
---|
1216 | }
|
---|
1217 |
|
---|
1218 | //Reader can decode this service (rc==0) and has lb_min_ecmcount ecms:
|
---|
1219 | if (s->rc == E_FOUND || hassrvid) {
|
---|
1220 | if (cfg.preferlocalcards && (ea->status & READER_LOCAL))
|
---|
1221 | nlocal_readers++; //Prefer local readers!
|
---|
1222 |
|
---|
1223 | switch (cfg.lb_mode) {
|
---|
1224 | default:
|
---|
1225 | case LB_NONE:
|
---|
1226 | case LB_LOG_ONLY:
|
---|
1227 | //cs_debug_mask(D_LB, "loadbalance disabled");
|
---|
1228 | ea->status |= READER_ACTIVE;
|
---|
1229 | if (rdr->fallback)
|
---|
1230 | ea->status |= READER_FALLBACK;
|
---|
1231 | continue;
|
---|
1232 |
|
---|
1233 | case LB_FASTEST_READER_FIRST:
|
---|
1234 | current = s->time_avg * 100 / weight;
|
---|
1235 | break;
|
---|
1236 |
|
---|
1237 | case LB_OLDEST_READER_FIRST:
|
---|
1238 | if (!rdr->lb_last.time)
|
---|
1239 | rdr->lb_last = check_time;
|
---|
1240 |
|
---|
1241 | //current is negative here! the older, the bigger is the difference
|
---|
1242 | current = 1000 * (rdr->lb_last.time - check_time.time) + (rdr->lb_last.millitm - check_time.millitm) - 10;
|
---|
1243 | // current /= weight; /* The others are divided by weight only OLDEST not??*/
|
---|
1244 | if (!current)
|
---|
1245 | current = -1;
|
---|
1246 | break;
|
---|
1247 |
|
---|
1248 | case LB_LOWEST_USAGELEVEL:
|
---|
1249 | current = rdr->lb_usagelevel * 100 / weight;
|
---|
1250 | break;
|
---|
1251 | }
|
---|
1252 | cs_debug_mask(D_LB, "rdr %s lbvalue = %d", rdr->label, abs(current));
|
---|
1253 |
|
---|
1254 | #if defined(WEBIF) || defined(LCDSUPPORT)
|
---|
1255 | rdr->lbvalue = abs(current);
|
---|
1256 | #endif
|
---|
1257 |
|
---|
1258 | if (cfg.lb_mode != LB_OLDEST_READER_FIRST) { //Adjust selection to reader load:
|
---|
1259 | if (rdr->ph.c_available && !rdr->ph.c_available(rdr, AVAIL_CHECK_LOADBALANCE, er)) {
|
---|
1260 | current=current*2;
|
---|
1261 | }
|
---|
1262 |
|
---|
1263 | if (cl && cl->pending)
|
---|
1264 | current=current*cl->pending;
|
---|
1265 |
|
---|
1266 | if (s->rc >= E_NOTFOUND) { //when reader has service this is possible
|
---|
1267 | current=current*(s->fail_factor+2); //Mark als slow
|
---|
1268 | }
|
---|
1269 | if (current < 1)
|
---|
1270 | current=1;
|
---|
1271 | }
|
---|
1272 |
|
---|
1273 | ea->value = current;
|
---|
1274 | ea->time = s->time_avg;
|
---|
1275 | }
|
---|
1276 | }
|
---|
1277 |
|
---|
1278 | if (nlocal_readers > nbest_readers) { //if we have local readers, we prefer them!
|
---|
1279 | nlocal_readers = nbest_readers;
|
---|
1280 | nbest_readers = 0;
|
---|
1281 | }
|
---|
1282 | else
|
---|
1283 | nbest_readers = nbest_readers - nlocal_readers;
|
---|
1284 |
|
---|
1285 | struct s_reader *best_rdr = NULL;
|
---|
1286 | struct s_reader *best_rdri = NULL;
|
---|
1287 | int32_t best_time = 0;
|
---|
1288 | int32_t result_count = 0;
|
---|
1289 |
|
---|
1290 | int32_t n=0;
|
---|
1291 | while (nreaders) {
|
---|
1292 | struct s_ecm_answer *best = NULL;
|
---|
1293 |
|
---|
1294 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1295 | if (nlocal_readers && !(ea->status & READER_LOCAL))
|
---|
1296 | continue;
|
---|
1297 |
|
---|
1298 | if (ea->value && (!best || ea->value < best->value))
|
---|
1299 | best=ea;
|
---|
1300 | }
|
---|
1301 | if (!best)
|
---|
1302 | break;
|
---|
1303 |
|
---|
1304 | n++;
|
---|
1305 | best_rdri = best->reader;
|
---|
1306 | if (!best_rdr) {
|
---|
1307 | best_rdr = best_rdri;
|
---|
1308 | best_time = best->time;
|
---|
1309 | }
|
---|
1310 | best->value = 0;
|
---|
1311 |
|
---|
1312 | if (nlocal_readers) {//primary readers, local
|
---|
1313 | nlocal_readers--;
|
---|
1314 | nreaders--;
|
---|
1315 | best->status |= READER_ACTIVE;
|
---|
1316 | }
|
---|
1317 | else if (nbest_readers) {//primary readers, other
|
---|
1318 | nbest_readers--;
|
---|
1319 | nreaders--;
|
---|
1320 | best->status |= READER_ACTIVE;
|
---|
1321 | }
|
---|
1322 | else if (nfb_readers) { //fallbacks:
|
---|
1323 | nfb_readers--;
|
---|
1324 | best->status |= (READER_ACTIVE|READER_FALLBACK);
|
---|
1325 | }
|
---|
1326 | else
|
---|
1327 | break;
|
---|
1328 | result_count++;
|
---|
1329 | }
|
---|
1330 |
|
---|
1331 | if (!new_stats && result_count < reader_count) {
|
---|
1332 | if (!n) //no best reader found? reopen if we have ecm_count>0
|
---|
1333 | {
|
---|
1334 | cs_debug_mask(D_LB, "loadbalancer: NO MATCHING READER FOUND, reopen last valid:");
|
---|
1335 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1336 | if (!(ea->status&READER_ACTIVE)) {
|
---|
1337 | rdr = ea->reader;
|
---|
1338 | s = get_stat(rdr, &q);
|
---|
1339 | if (s && s->rc != E_FOUND && s->last_received+get_reopen_seconds(s) < current_time) {
|
---|
1340 | if (nreaders) {
|
---|
1341 | ea->status |= READER_ACTIVE;
|
---|
1342 | nreaders--;
|
---|
1343 | cs_debug_mask(D_LB, "loadbalancer: reopened reader %s", rdr->label);
|
---|
1344 | }
|
---|
1345 | n++;
|
---|
1346 | }
|
---|
1347 | }
|
---|
1348 | }
|
---|
1349 | cs_debug_mask(D_LB, "loadbalancer: reopened %d readers", n);
|
---|
1350 | }
|
---|
1351 |
|
---|
1352 | //algo for reopen other reader only if responsetime>retrylimit:
|
---|
1353 | int32_t reopen = !best_rdr || (best_time && (best_time > retrylimit));
|
---|
1354 | if (reopen) {
|
---|
1355 | #ifdef WITH_DEBUG
|
---|
1356 | if (best_rdr)
|
---|
1357 | cs_debug_mask(D_LB, "loadbalancer: reader %s reached retrylimit (%dms), reopening other readers", best_rdr->label, best_time);
|
---|
1358 | else
|
---|
1359 | cs_debug_mask(D_LB, "loadbalancer: no best reader found, reopening other readers");
|
---|
1360 | #endif
|
---|
1361 | for(ea = er->matching_rdr; ea && nreaders; ea = ea->next) {
|
---|
1362 | if (!(ea->status&READER_ACTIVE)) {
|
---|
1363 | rdr = ea->reader;
|
---|
1364 | s = get_stat(rdr, &q);
|
---|
1365 |
|
---|
1366 | if (s && s->rc != E_FOUND) { //retrylimit reached:
|
---|
1367 | if (!cfg.lb_reopen_mode) cs_debug_mask(D_LB, "loadbalancer: reader %s need %ld seconds to reopen", ea->reader->label, (s->last_received+get_reopen_seconds(s))-current_time);
|
---|
1368 | if (cfg.lb_reopen_mode || s->last_received+get_reopen_seconds(s) < current_time) { //Retrying reader every (900/conf) seconds
|
---|
1369 | if (cfg.lb_reopen_mode) {
|
---|
1370 | cs_debug_mask(D_LB, "loadbalancer: reader %s reopen fast", rdr->label);
|
---|
1371 | } else {
|
---|
1372 | cs_debug_mask(D_LB, "loadbalancer: reader %s reopen after %ld sec.", rdr->label, get_reopen_seconds(s));
|
---|
1373 | }
|
---|
1374 | s->last_received = current_time;
|
---|
1375 | ea->status |= READER_ACTIVE;
|
---|
1376 | nreaders--;
|
---|
1377 | cs_debug_mask(D_LB, "loadbalancer: retrying reader %s (fail %d)", rdr->label, s->fail_factor);
|
---|
1378 | }
|
---|
1379 | }
|
---|
1380 | }
|
---|
1381 | }
|
---|
1382 | }
|
---|
1383 | }
|
---|
1384 |
|
---|
1385 | #ifdef WITH_DEBUG
|
---|
1386 | if (cs_dblevel & D_LB) {
|
---|
1387 | //loadbalancer debug output:
|
---|
1388 | int32_t nr = 0;
|
---|
1389 | char buf[512];
|
---|
1390 | int32_t l=512;
|
---|
1391 | char *rptr = buf;
|
---|
1392 | *rptr = 0;
|
---|
1393 |
|
---|
1394 | for(ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1395 | if (!(ea->status & READER_ACTIVE))
|
---|
1396 | continue;
|
---|
1397 |
|
---|
1398 | nr++;
|
---|
1399 |
|
---|
1400 | if (nr>5) continue;
|
---|
1401 |
|
---|
1402 | if (!(ea->status & READER_FALLBACK))
|
---|
1403 | n = snprintf(rptr, l, "%s%s%s ", ea->reader->label, (ea->status&READER_CACHEEX)?"*":"", (ea->status&READER_LOCAL)?"L":"");
|
---|
1404 | else
|
---|
1405 | n = snprintf(rptr, l, "[%s%s%s] ", ea->reader->label, (ea->status&READER_CACHEEX)?"*":"", (ea->status&READER_LOCAL)?"L":"");
|
---|
1406 | rptr+=n;
|
---|
1407 | l-=n;
|
---|
1408 | }
|
---|
1409 |
|
---|
1410 | if (nr>5)
|
---|
1411 | snprintf(rptr, l, "...(%d more)", nr - 5);
|
---|
1412 |
|
---|
1413 | char ecmbuf[ECM_FMT_LEN];
|
---|
1414 | format_ecm(er, ecmbuf, ECM_FMT_LEN);
|
---|
1415 |
|
---|
1416 | cs_debug_mask(D_LB, "loadbalancer: client %s for %s: n=%d selected readers: %s",
|
---|
1417 | username(er->client), ecmbuf, nr, buf);
|
---|
1418 | }
|
---|
1419 | #endif
|
---|
1420 | return;
|
---|
1421 | }
|
---|
1422 |
|
---|
1423 | /**
|
---|
1424 | * clears statistic of reader ridx.
|
---|
1425 | **/
|
---|
1426 | void clear_reader_stat(struct s_reader *rdr)
|
---|
1427 | {
|
---|
1428 | if (!rdr->lb_stat)
|
---|
1429 | return;
|
---|
1430 |
|
---|
1431 | ll_clear_data(rdr->lb_stat);
|
---|
1432 | }
|
---|
1433 |
|
---|
1434 | void clear_all_stat(void)
|
---|
1435 | {
|
---|
1436 | struct s_reader *rdr;
|
---|
1437 | LL_ITER itr = ll_iter_create(configured_readers);
|
---|
1438 | while ((rdr = ll_iter_next(&itr))) {
|
---|
1439 | clear_reader_stat(rdr);
|
---|
1440 | }
|
---|
1441 | }
|
---|
1442 |
|
---|
1443 | static void housekeeping_stat_thread(void)
|
---|
1444 | {
|
---|
1445 | time_t cleanup_time = time(NULL) - (cfg.lb_stat_cleanup*60*60);
|
---|
1446 | int32_t cleaned = 0;
|
---|
1447 | struct s_reader *rdr;
|
---|
1448 | set_thread_name(__func__);
|
---|
1449 | LL_ITER itr = ll_iter_create(configured_readers);
|
---|
1450 | cs_readlock(&readerlist_lock); //this avoids cleaning a reading during writing
|
---|
1451 | while ((rdr = ll_iter_next(&itr))) {
|
---|
1452 | if (rdr->lb_stat) {
|
---|
1453 | cs_writelock(&rdr->lb_stat_lock);
|
---|
1454 | LL_ITER it = ll_iter_create(rdr->lb_stat);
|
---|
1455 | READER_STAT *s;
|
---|
1456 | while ((s=ll_iter_next(&it))) {
|
---|
1457 |
|
---|
1458 | if (s->last_received < cleanup_time) {
|
---|
1459 | ll_iter_remove_data(&it);
|
---|
1460 | cleaned++;
|
---|
1461 | }
|
---|
1462 | }
|
---|
1463 | cs_writeunlock(&rdr->lb_stat_lock);
|
---|
1464 | }
|
---|
1465 | }
|
---|
1466 | cs_readunlock(&readerlist_lock);
|
---|
1467 | cs_debug_mask(D_LB, "loadbalancer cleanup: removed %d entries", cleaned);
|
---|
1468 | }
|
---|
1469 |
|
---|
1470 | static void housekeeping_stat(int32_t force)
|
---|
1471 | {
|
---|
1472 | time_t now = time(NULL);
|
---|
1473 | if (!force && last_housekeeping + 60*60 > now) //only clean once in an hour
|
---|
1474 | return;
|
---|
1475 |
|
---|
1476 | last_housekeeping = now;
|
---|
1477 | start_thread((void*)&housekeeping_stat_thread, "housekeeping lb stats");
|
---|
1478 | }
|
---|
1479 |
|
---|
1480 | static int compare_stat(READER_STAT **ps1, READER_STAT **ps2) {
|
---|
1481 | READER_STAT *s1 = (*ps1), *s2 = (*ps2);
|
---|
1482 | int res = s1->rc - s2->rc;
|
---|
1483 | if (res) return res;
|
---|
1484 | res = s1->caid - s2->caid;
|
---|
1485 | if (res) return res;
|
---|
1486 | res = s1->prid - s2->prid;
|
---|
1487 | if (res) return res;
|
---|
1488 | res = s1->srvid - s2->srvid;
|
---|
1489 | if (res) return res;
|
---|
1490 | res = s1->chid - s2->chid;
|
---|
1491 | if (res) return res;
|
---|
1492 | res = s1->ecmlen - s2->ecmlen;
|
---|
1493 | if (res) return res;
|
---|
1494 | res = s1->last_received - s2->last_received;
|
---|
1495 | return res;
|
---|
1496 | }
|
---|
1497 |
|
---|
1498 | static int compare_stat_r(READER_STAT **ps1, READER_STAT **ps2) {
|
---|
1499 | return -compare_stat(ps1, ps2);
|
---|
1500 | }
|
---|
1501 |
|
---|
1502 | READER_STAT **get_sorted_stat_copy(struct s_reader *rdr, int32_t reverse, int32_t *size)
|
---|
1503 | {
|
---|
1504 | if (reverse)
|
---|
1505 | return (READER_STAT **)ll_sort(rdr->lb_stat, compare_stat_r, size);
|
---|
1506 | else
|
---|
1507 | return (READER_STAT **)ll_sort(rdr->lb_stat, compare_stat, size);
|
---|
1508 | }
|
---|
1509 |
|
---|
1510 | static int8_t stat_in_ecmlen(struct s_reader *rdr, READER_STAT *s)
|
---|
1511 | {
|
---|
1512 | struct s_ecmWhitelist *tmp;
|
---|
1513 | struct s_ecmWhitelistIdent *tmpIdent;
|
---|
1514 | struct s_ecmWhitelistLen *tmpLen;
|
---|
1515 | for (tmp = rdr->ecmWhitelist; tmp; tmp = tmp->next) {
|
---|
1516 | if (tmp->caid == 0 || (tmp->caid == s->caid)) {
|
---|
1517 | for (tmpIdent = tmp->idents; tmpIdent; tmpIdent = tmpIdent->next) {
|
---|
1518 | if (tmpIdent->ident == 0 || tmpIdent->ident == s->prid) {
|
---|
1519 | for (tmpLen = tmpIdent->lengths; tmpLen; tmpLen = tmpLen->next) {
|
---|
1520 | if (tmpLen->len == s->ecmlen) {
|
---|
1521 | return 1;
|
---|
1522 | }
|
---|
1523 | }
|
---|
1524 | }
|
---|
1525 | }
|
---|
1526 | }
|
---|
1527 | }
|
---|
1528 | return 0;
|
---|
1529 | }
|
---|
1530 |
|
---|
1531 | static int8_t add_to_ecmlen(struct s_reader *rdr, READER_STAT *s)
|
---|
1532 | {
|
---|
1533 | struct s_ecmWhitelist *tmp = NULL;
|
---|
1534 | struct s_ecmWhitelistIdent *tmpIdent = NULL;
|
---|
1535 | struct s_ecmWhitelistLen *tmpLen = NULL;
|
---|
1536 |
|
---|
1537 | for (tmp = rdr->ecmWhitelist; tmp; tmp = tmp->next) {
|
---|
1538 | if (tmp->caid == s->caid) {
|
---|
1539 | for (tmpIdent = tmp->idents; tmpIdent; tmpIdent = tmpIdent->next) {
|
---|
1540 | if (tmpIdent->ident == s->prid) {
|
---|
1541 | for (tmpLen = tmpIdent->lengths; tmpLen; tmpLen = tmpLen->next) {
|
---|
1542 | if (tmpLen->len == s->ecmlen) {
|
---|
1543 | return 1;
|
---|
1544 | }
|
---|
1545 | }
|
---|
1546 | break;
|
---|
1547 | }
|
---|
1548 | }
|
---|
1549 | break;
|
---|
1550 | }
|
---|
1551 | }
|
---|
1552 |
|
---|
1553 | if (!tmp) {
|
---|
1554 | if (cs_malloc(&tmp, sizeof(struct s_ecmWhitelist))) {
|
---|
1555 | tmp->caid = s->caid;
|
---|
1556 | tmp->next = rdr->ecmWhitelist;
|
---|
1557 | rdr->ecmWhitelist = tmp;
|
---|
1558 | }
|
---|
1559 | }
|
---|
1560 |
|
---|
1561 | if (!tmpIdent && tmp) {
|
---|
1562 | if (cs_malloc(&tmpIdent, sizeof(struct s_ecmWhitelistIdent))) {
|
---|
1563 | tmpIdent->ident = s->prid;
|
---|
1564 | tmpIdent->next = tmp->idents;
|
---|
1565 | tmp->idents = tmpIdent;
|
---|
1566 | }
|
---|
1567 | }
|
---|
1568 |
|
---|
1569 | if (!tmpLen && tmpIdent) {
|
---|
1570 | if (cs_malloc(&tmpLen, sizeof(struct s_ecmWhitelistLen))) {
|
---|
1571 | tmpLen->len = s->ecmlen;
|
---|
1572 | tmpLen->next = tmpIdent->lengths;
|
---|
1573 | tmpIdent->lengths = tmpLen;
|
---|
1574 | }
|
---|
1575 | }
|
---|
1576 |
|
---|
1577 | return 0;
|
---|
1578 | }
|
---|
1579 |
|
---|
1580 | void update_ecmlen_from_stat(struct s_reader *rdr)
|
---|
1581 | {
|
---|
1582 | if (!rdr || &rdr->lb_stat)
|
---|
1583 | return;
|
---|
1584 |
|
---|
1585 | cs_readlock(&rdr->lb_stat_lock);
|
---|
1586 | LL_ITER it = ll_iter_create(rdr->lb_stat);
|
---|
1587 | READER_STAT *s;
|
---|
1588 | while ((s = ll_iter_next(&it))) {
|
---|
1589 | if (s->rc ==E_FOUND) {
|
---|
1590 | if (!stat_in_ecmlen(rdr, s))
|
---|
1591 | add_to_ecmlen(rdr, s);
|
---|
1592 | }
|
---|
1593 | }
|
---|
1594 | cs_readunlock(&rdr->lb_stat_lock);
|
---|
1595 | }
|
---|
1596 |
|
---|
1597 | int32_t lb_valid_btun(ECM_REQUEST *er, uint16_t caidto)
|
---|
1598 | {
|
---|
1599 | STAT_QUERY q;
|
---|
1600 | READER_STAT *s;
|
---|
1601 | struct s_reader *rdr;
|
---|
1602 |
|
---|
1603 | get_stat_query(er, &q);
|
---|
1604 | q.caid = caidto;
|
---|
1605 |
|
---|
1606 | cs_readlock(&readerlist_lock);
|
---|
1607 | for (rdr=first_active_reader; rdr ; rdr=rdr->next) {
|
---|
1608 | if (rdr->lb_stat && rdr->client) {
|
---|
1609 | s = get_stat(rdr, &q);
|
---|
1610 | if (s && s->rc == E_FOUND) {
|
---|
1611 | cs_readunlock(&readerlist_lock);
|
---|
1612 | return 1;
|
---|
1613 | }
|
---|
1614 | }
|
---|
1615 | }
|
---|
1616 | cs_readunlock(&readerlist_lock);
|
---|
1617 | return 0;
|
---|
1618 | }
|
---|
1619 |
|
---|
1620 | /**
|
---|
1621 | * mark as last reader after checked for cache requests:
|
---|
1622 | **/
|
---|
1623 | void lb_mark_last_reader(ECM_REQUEST *er)
|
---|
1624 | {
|
---|
1625 | //OLDEST_READER: set lb_last
|
---|
1626 | struct s_ecm_answer *ea;
|
---|
1627 | for (ea=er->matching_rdr; ea; ea=ea->next) {
|
---|
1628 | if ((ea->status&(READER_ACTIVE|READER_FALLBACK)) == READER_ACTIVE)
|
---|
1629 | cs_ftime(&ea->reader->lb_last);
|
---|
1630 | }
|
---|
1631 | }
|
---|
1632 |
|
---|
1633 | /**
|
---|
1634 | * Automatic timeout feature depending on statistik values
|
---|
1635 | **/
|
---|
1636 | uint32_t lb_auto_timeout(ECM_REQUEST *er, uint32_t ctimeout) {
|
---|
1637 | STAT_QUERY q;
|
---|
1638 | READER_STAT *s = NULL;
|
---|
1639 |
|
---|
1640 | struct s_reader *rdr = NULL;
|
---|
1641 | struct s_ecm_answer *ea;
|
---|
1642 |
|
---|
1643 | for (ea = er->matching_rdr; ea; ea = ea->next) {
|
---|
1644 | if ((ea->status&(READER_ACTIVE|READER_FALLBACK)) == READER_ACTIVE) {
|
---|
1645 | rdr = ea->reader;
|
---|
1646 | get_stat_query(er, &q);
|
---|
1647 | s = get_stat(rdr, &q);
|
---|
1648 | if (s) break;
|
---|
1649 | }
|
---|
1650 | }
|
---|
1651 | if (!s) return ctimeout;
|
---|
1652 |
|
---|
1653 | uint32_t t;
|
---|
1654 | if (s->rc == E_TIMEOUT)
|
---|
1655 | t = ctimeout/2; //timeout known, early timeout!
|
---|
1656 | else {
|
---|
1657 | if (s->ecm_count < cfg.lb_min_ecmcount) return ctimeout;
|
---|
1658 |
|
---|
1659 | t = s->time_avg*(100+cfg.lb_auto_timeout_p)/100;
|
---|
1660 | if ((int32_t)(t-s->time_avg) < cfg.lb_auto_timeout_t) t = s->time_avg+cfg.lb_auto_timeout_t;
|
---|
1661 | }
|
---|
1662 | if (t > ctimeout) t = ctimeout;
|
---|
1663 | #ifdef WITH_DEBUG
|
---|
1664 | if (D_TRACE & cs_dblevel) {
|
---|
1665 | char buf[ECM_FMT_LEN];
|
---|
1666 | format_ecm(er, buf, ECM_FMT_LEN);
|
---|
1667 | cs_debug_mask(D_TRACE, "auto-timeout for %s %s set rdr %s to %d", username(er->client), buf, rdr->label, t);
|
---|
1668 | }
|
---|
1669 | #endif
|
---|
1670 | return t;
|
---|
1671 | }
|
---|
1672 |
|
---|
1673 | void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc)
|
---|
1674 | {
|
---|
1675 | if (!rdr || rc >= E_99 || cacheex_reader(rdr))
|
---|
1676 | return;
|
---|
1677 | if (er->ecmcacheptr) //ignore cache answer
|
---|
1678 | return;
|
---|
1679 |
|
---|
1680 | struct timeb tpe;
|
---|
1681 | cs_ftime(&tpe);
|
---|
1682 | #ifndef CS_CACHEEX
|
---|
1683 | int32_t ntime = comp_timeb(&tpe,&er->tps);
|
---|
1684 | #else
|
---|
1685 | int32_t ntime = comp_timeb(&tpe, &er->cacheex_wait);
|
---|
1686 | #endif
|
---|
1687 | if (ntime < 1)
|
---|
1688 | ntime = 1;
|
---|
1689 |
|
---|
1690 | if (ea && (ea->status & READER_FALLBACK) && ntime > (int32_t)cfg.ftimeout)
|
---|
1691 | ntime = ntime - cfg.ftimeout;
|
---|
1692 | add_stat(rdr, er, ntime, rc);
|
---|
1693 | }
|
---|
1694 |
|
---|
1695 | void stat_finish(void) {
|
---|
1696 | if (cfg.lb_mode && cfg.lb_save) {
|
---|
1697 | save_stat_to_file(0);
|
---|
1698 | if (cfg.lb_savepath)
|
---|
1699 | cs_log("stats saved to file %s", cfg.lb_savepath);
|
---|
1700 | cfg.lb_save = 0; //this is for avoiding duplicate saves
|
---|
1701 | }
|
---|
1702 | }
|
---|
1703 |
|
---|
1704 | #endif
|
---|