source: trunk/module-stat.c@ 4149

Last change on this file since 4149 was 4098, checked in by schlocke, 13 years ago

cccam: removed non-working preferred card, changed keepconnected mgcamd
compatibility

File size: 13.0 KB
Line 
1#include "module-stat.h"
2#include "module-cccam.h"
3
4#define UNDEF_AVG_TIME 80000
5#define MAX_ECM_SEND_CACHE 16
6
7static int stat_load_save;
8static struct timeb nulltime;
9
10void init_stat()
11{
12 cs_ftime(&nulltime);
13 stat_load_save = -100;
14
15 //checking config
16 if (cfg->lb_nbest_readers < 2)
17 cfg->lb_nbest_readers = DEFAULT_NBEST;
18 if (cfg->lb_nfb_readers < 2)
19 cfg->lb_nfb_readers = DEFAULT_NFB;
20 if (cfg->lb_min_ecmcount < 1)
21 cfg->lb_min_ecmcount = DEFAULT_MIN_ECM_COUNT;
22 if (cfg->lb_max_ecmcount < 2)
23 cfg->lb_max_ecmcount = DEFAULT_MAX_ECM_COUNT;
24 if (cfg->lb_reopen_seconds < 10)
25 cfg->lb_reopen_seconds = DEFAULT_REOPEN_SECONDS;
26}
27
28void load_stat_from_file()
29{
30 stat_load_save = 0;
31 char buf[256];
32 sprintf(buf, "%s/stat", get_tmp_dir());
33 FILE *file = fopen(buf, "r");
34 if (!file) {
35 cs_log("can't read from file %s", buf);
36 return;
37 }
38 cs_debug_mask(D_TRACE, "loadbalancer load statistics from %s", buf);
39
40 struct s_reader *rdr = NULL;
41
42 int i=1;
43 int count=0;
44 do
45 {
46 READER_STAT *stat = malloc(sizeof(READER_STAT));
47 memset(stat, 0, sizeof(READER_STAT));
48 i = fscanf(file, "%s rc %d caid %04hX prid %06lX srvid %04hX time avg %dms ecms %d last %ld\n",
49 buf, &stat->rc, &stat->caid, &stat->prid, &stat->srvid,
50 &stat->time_avg, &stat->ecm_count, &stat->last_received);
51
52
53 if (i > 5) {
54 if (rdr == NULL || strcmp(buf, rdr->label) != 0) {
55 for (rdr=first_reader; rdr ; rdr=rdr->next) {
56 if (strcmp(rdr->label, buf) == 0) {
57 break;
58 }
59 }
60 }
61
62 if (rdr != NULL && strcmp(buf, rdr->label) == 0) {
63 if (!rdr->lb_stat)
64 rdr->lb_stat = ll_create();
65 ll_append(rdr->lb_stat, stat);
66 count++;
67 }
68 else
69 {
70 cs_log("loadbalancer statistics could not be loaded for %s", buf);
71 free(stat);
72 }
73 }
74 else
75 {
76 cs_debug_mask(D_TRACE, "loadbalancer statistics ERROR %s rc=%d i=%d", buf, stat->rc, i);
77 free(stat);
78 }
79 } while(i != EOF && i > 0);
80 fclose(file);
81 cs_debug_mask(D_TRACE, "loadbalancer statistic loaded %d records", count);
82}
83/**
84 * get statistic values for reader ridx and caid/prid/srvid
85 */
86READER_STAT *get_stat(struct s_reader *rdr, ushort caid, ulong prid, ushort srvid)
87{
88 if (stat_load_save < 0 && cfg->lb_save)
89 load_stat_from_file();
90 if (!rdr->lb_stat)
91 rdr->lb_stat = ll_create();
92
93 LL_ITER *it = ll_iter_create(rdr->lb_stat);
94 READER_STAT *stat = NULL;
95 while ((stat = ll_iter_next(it))) {
96 if (stat->caid==caid && stat->prid==prid && stat->srvid==srvid) {
97 break;
98 }
99 }
100 ll_iter_release(it);
101 return stat;
102}
103
104/**
105 * removes caid/prid/srvid from stat-list of reader ridx
106 */
107int remove_stat(struct s_reader *rdr, ushort caid, ulong prid, ushort srvid)
108{
109 if (!rdr->lb_stat)
110 return 0;
111
112 int c = 0;
113 LL_ITER *it = ll_iter_create(rdr->lb_stat);
114 READER_STAT *stat;
115 while ((stat = ll_iter_next(it))) {
116 if (stat->caid==caid && stat->prid==prid && stat->srvid==srvid) {
117 ll_iter_remove_data(it);
118 c++;
119 }
120 }
121 ll_iter_release(it);
122 return c;
123}
124
125/**
126 * Calculates average time
127 */
128void calc_stat(READER_STAT *stat)
129{
130 int i;
131 int c=0;
132 long t = 0;
133 for (i = 0; i < LB_MAX_STAT_TIME; i++) {
134 if (stat->time_stat[i] > 0) {
135 t += (long)stat->time_stat[i];
136 c++;
137 }
138 }
139 if (!c)
140 stat->time_avg = UNDEF_AVG_TIME;
141 else
142 stat->time_avg = t / c;
143}
144
145/**
146 * Saves statistik to /tmp/.oscam/stat.n where n is reader-index
147 */
148void save_stat_to_file()
149{
150 stat_load_save = 0;
151 char buf[256];
152 sprintf(buf, "%s/stat", get_tmp_dir());
153
154 FILE *file = fopen(buf, "w");
155 if (!file) {
156 cs_log("can't write to file %s", buf);
157 return;
158 }
159
160 int count=0;
161 struct s_reader *rdr;
162 for (rdr=first_reader; rdr ; rdr=rdr->next) {
163
164 if (rdr->lb_stat) {
165 LL_ITER *it = ll_iter_create(rdr->lb_stat);
166 READER_STAT *stat;
167 while ((stat = ll_iter_next(it))) {
168
169 fprintf(file, "%s rc %d caid %04hX prid %06lX srvid %04hX time avg %dms ecms %d last %ld\n",
170 rdr->label, stat->rc, stat->caid, stat->prid,
171 stat->srvid, stat->time_avg, stat->ecm_count, stat->last_received);
172 count++;
173 }
174 ll_iter_release(it);
175 }
176 }
177
178 fclose(file);
179 cs_log("loadbalacer statistic saved %d records", count);
180}
181
182/**
183 * Adds caid/prid/srvid to stat-list for reader ridx with time/rc
184 */
185void add_stat(struct s_reader *rdr, ushort caid, ulong prid, ushort srvid, int ecm_time, int rc)
186{
187 if (!rdr)
188 return;
189 READER_STAT *stat = get_stat(rdr, caid, prid, srvid);
190 if (!stat) {
191 stat = malloc(sizeof(READER_STAT));
192 memset(stat, 0, sizeof(READER_STAT));
193 stat->caid = caid;
194 stat->prid = prid;
195 stat->srvid = srvid;
196 stat->time_avg = UNDEF_AVG_TIME; //dummy placeholder
197 ll_append(rdr->lb_stat, stat);
198 }
199
200 //inc ecm_count if found, drop to 0 if not found:
201 // rc codes:
202 // 0 = found +
203 // 1 = cache1 #
204 // 2 = cache2 #
205 // 3 = emu +
206 // 4 = not found -
207 // 5 = timeout #
208 // 6 = sleeping #
209 // 7 = fake #
210 // 8 = invalid -
211 // 9 = corrupt -
212 // 10= no card #
213 // 11= expdate #
214 // 12= disabled #
215 // 13= stopped #
216 // 100= unhandled #
217 // + = adds statistic values
218 // # = ignored because of duplicate values, temporary failures or softblocks
219 // - = causes loadbalancer to block this reader for this caid/prov/sid
220
221 if (stat->ecm_count < 0)
222 stat->ecm_count=0;
223
224 if (rc == 0 || rc == 3) {
225 stat->rc = 0;
226 stat->ecm_count++;
227 stat->time_idx++;
228 stat->last_received = time(NULL);
229 stat->request_count = 0;
230
231 //FASTEST READER:
232 if (stat->time_idx >= LB_MAX_STAT_TIME)
233 stat->time_idx = 0;
234 stat->time_stat[stat->time_idx] = ecm_time;
235 calc_stat(stat);
236
237 //OLDEST READER now set by get best reader!
238
239
240 //USAGELEVEL:
241 int ule = rdr->lb_usagelevel_ecmcount;
242 if (ule > 0 && ((ule / cfg->lb_min_ecmcount) > 0)) //update every MIN_ECM_COUNT usagelevel:
243 {
244 time_t t = (time(NULL)-rdr->lb_usagelevel_time);
245 rdr->lb_usagelevel = 1000/(t<1?1:t);
246 ule = 0;
247 }
248 if (ule == 0)
249 rdr->lb_usagelevel_time = time(NULL);
250 rdr->lb_usagelevel_ecmcount = ule+1;
251 }
252 else if (rc == 4 || rc == 8 || rc == 9) { //not found+errors+etc
253 stat->rc = rc;
254 //stat->ecm_count = 0; Keep ecm_count!
255 }
256 else if (rc == 5) { //timeout
257 stat->request_count++;
258 }
259
260 //cs_debug_mask(D_TRACE, "adding stat for reader %s (%d): rc %d caid %04hX prid %06lX srvid %04hX time %dms usagelevel %d",
261 // rdr->label, ridx, rc, caid, prid, srvid, ecm_time, rdr->lb_usagelevel);
262
263 //debug only:
264 if (cfg->lb_save) {
265 stat_load_save++;
266 if (stat_load_save > cfg->lb_save) {
267 save_stat_to_file();
268 }
269 }
270}
271
272void reset_stat(ushort caid, ulong prid, ushort srvid)
273{
274 //cs_debug_mask(D_TRACE, "loadbalance: resetting ecm count");
275 struct s_reader *rdr;
276 for (rdr=first_reader; rdr ; rdr=rdr->next) {
277 if (rdr->lb_stat && rdr->client) {
278 READER_STAT *stat = get_stat(rdr, caid, prid, srvid);
279 if (stat) {
280 if (stat->ecm_count > 0)
281 stat->ecm_count = 1; //not zero, so we know it's decodeable
282 stat->rc = 0;
283 stat->request_count = 0;
284 }
285 }
286 }
287}
288
289
290/**
291 * Gets best reader for caid/prid/srvid.
292 * Best reader is evaluated by lowest avg time but only if ecm_count > cfg->lb_min_ecmcount (5)
293 * Also the reader is asked if he is "available"
294 * returns ridx when found or -1 when not found
295 */
296int get_best_reader(ECM_REQUEST *er)
297{
298 int i;
299 int result[CS_MAXREADER];
300 memset(result, 0, sizeof(result));
301 int re[CS_MAXREADER];
302
303 //resulting values:
304 memset(re, 0, sizeof(re));
305
306 struct timeb new_nulltime;
307 memset(&new_nulltime, 0, sizeof(new_nulltime));
308 time_t current_time = time(NULL);
309
310 int current = -1;
311
312 READER_STAT *stat = NULL;
313 struct s_reader *rdr;
314
315 int nlocal_readers = 0;
316
317 for (i=0,rdr=first_reader; rdr ; rdr=rdr->next, i++) {
318 if (er->matching_rdr[i]) {
319 int weight = rdr->lb_weight <= 0?100:rdr->lb_weight;
320
321 stat = get_stat(rdr, er->caid, er->prid, er->srvid);
322 if (!stat) {
323 cs_debug_mask(D_TRACE, "loadbalancer: starting statistics for reader %s", rdr->label);
324 add_stat(rdr, er->caid, er->prid, er->srvid, 1, -1);
325 result[i] = 1; //no statistics, this reader is active (now) but we need statistics first!
326 continue;
327 }
328
329 if (stat->ecm_count < 0||(stat->ecm_count > cfg->lb_max_ecmcount && stat->time_avg > (int)cfg->ftimeout)) {
330 cs_debug_mask(D_TRACE, "loadbalancer: max ecms (%d) reached by reader %s, resetting statistics", cfg->lb_max_ecmcount, rdr->label);
331 reset_stat(er->caid, er->prid, er->srvid);
332 result[i] = 1;//max ecm reached, get new statistics
333 continue;
334 }
335
336 if (stat->rc == 0 && stat->ecm_count < cfg->lb_min_ecmcount) {
337 cs_debug_mask(D_TRACE, "loadbalancer: reader %s needs more statistics", rdr->label);
338 result[i] = 1; //need more statistics!
339 continue;
340 }
341
342 if (stat->rc == 0 && stat->request_count > cfg->lb_min_ecmcount) { // 5 unanswered requests or timeouts?
343 add_stat(rdr, er->caid, er->prid, er->srvid, 1, 4); //reader marked as unuseable
344 result[i] = 0;
345 continue;
346 }
347
348
349 //Reader can decode this service (rc==0) and has lb_min_ecmcount ecms:
350 if (stat->rc == 0) {
351 if (cfg->preferlocalcards && !(rdr->typ & R_IS_NETWORK))
352 nlocal_readers++; //Prefer local readers!
353
354 switch (cfg->lb_mode) {
355 default:
356 case LB_NONE:
357 //cs_debug_mask(D_TRACE, "loadbalance disabled");
358 result[i] = 1;
359 current = 1;
360 break;
361 case LB_FASTEST_READER_FIRST:
362 current = stat->time_avg * 100 / weight;
363 break;
364 case LB_OLDEST_READER_FIRST:
365 if (!rdr->lb_last.time)
366 rdr->lb_last = nulltime;
367 current = (1000*(rdr->lb_last.time-nulltime.time)+
368 rdr->lb_last.millitm-nulltime.millitm);
369 if (!new_nulltime.time || (1000*(rdr->lb_last.time-new_nulltime.time)+
370 rdr->lb_last.millitm-new_nulltime.millitm) < 0)
371 new_nulltime = rdr->lb_last;
372 break;
373 case LB_LOWEST_USAGELEVEL:
374 current = rdr->lb_usagelevel * 100 / weight;
375 break;
376 }
377#ifdef WEBIF
378 rdr->lbvalue = current;
379#endif
380 if (!rdr->ph.c_available
381 || rdr->ph.c_available(rdr,
382 AVAIL_CHECK_LOADBALANCE)) {
383 current=current/2;
384 }
385 if (current < 1)
386 re[i]=1;
387 else
388 re[i] = current;
389 }
390 else
391 {
392 int seconds = cfg->lb_reopen_seconds;
393 if (!rdr->audisabled && (er->client->autoau || er->client->aureader == rdr))
394 seconds = seconds/10;
395
396 if (stat->last_received+seconds < current_time) { //Retrying reader every (900/conf) seconds
397 stat->last_received = current_time;
398 result[i] = 1;
399 cs_log("loadbalancer: retrying reader %s", rdr->label);
400 }
401 }
402 }
403 }
404
405 int nbest_readers = cfg->lb_nbest_readers;
406 int nfb_readers = cfg->lb_nfb_readers;
407 if (nlocal_readers > nbest_readers) { //if we have local readers, we prefer them!
408 nlocal_readers = nbest_readers;
409 nbest_readers = 0;
410 }
411 else
412 nbest_readers = nbest_readers-nlocal_readers;
413
414 int best_ridx = -1;
415 struct s_reader *best_rdr = NULL;
416 struct s_reader *best_rdri = NULL;
417
418 int n=0;
419 while (1) {
420 int best_idx=-1;
421 int best=0;
422 int j;
423 struct s_reader *rdr2;
424 for (j=0,rdr2=first_reader; rdr2 ; rdr2=rdr2->next, j++) {
425 if (nlocal_readers && (rdr2->typ & R_IS_NETWORK))
426 continue;
427
428 if (re[j] && (!best || re[j] < best)) {
429 best_idx=j;
430 best=re[j];
431 best_rdri = rdr2;
432 }
433 }
434 if (best_idx<0)
435 break;
436 else {
437 n++;
438 re[best_idx]=0;
439 if (nlocal_readers) {
440 nlocal_readers--;
441 result[best_idx] = 1;
442 //OLDEST_READER:
443 cs_ftime(&best_rdri->lb_last);
444 if (best_ridx <0) {
445 best_ridx = best_idx;
446 best_rdr = best_rdri;
447 }
448 }
449 else if (nbest_readers) {
450 nbest_readers--;
451 result[best_idx] = 1;
452 //OLDEST_READER:
453 cs_ftime(&best_rdri->lb_last);
454 if (best_ridx <0) {
455 best_ridx = best_idx;
456 best_rdr = best_rdri;
457 }
458 }
459 else if (nfb_readers) {
460 nfb_readers--;
461 if (!result[best_idx])
462 result[best_idx] = 2;
463 }
464 else
465 break;
466 }
467 }
468
469 if (n)
470 memcpy(er->matching_rdr, result, sizeof(result));
471#ifdef WITH_DEBUG
472 else
473 cs_debug_mask(D_TRACE, "loadbalancer: no best reader found, trying all readers");
474
475 cs_debug_mask(D_TRACE, "loadbalancer: client %s for %04X/%06X/%04X: %s readers: %d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
476 username(er->client), er->caid, er->prid, er->srvid,
477 best_rdr?best_rdr->label:"NONE",
478 er->matching_rdr[0], er->matching_rdr[1], er->matching_rdr[2], er->matching_rdr[3],
479 er->matching_rdr[4], er->matching_rdr[5], er->matching_rdr[6], er->matching_rdr[7],
480 er->matching_rdr[8], er->matching_rdr[9], er->matching_rdr[10], er->matching_rdr[11],
481 er->matching_rdr[12], er->matching_rdr[13], er->matching_rdr[14], er->matching_rdr[15]);
482#endif
483
484 for (i=0,rdr=first_reader; rdr ; rdr=rdr->next, i++) {
485 if (result[i] == 1) { //primary readers
486 stat = get_stat(rdr, er->caid, er->prid, er->srvid);
487 if (stat) {
488 stat->request_count++;
489 //algo for finding unanswered requests (newcamd reader for example:)
490 if (stat->request_count > cfg->lb_min_ecmcount) { //5 unanswered requests?
491 add_stat(rdr, er->caid, er->prid, er->srvid, 1, 4); //reader marked as unuseable
492 result[i] = 0;
493 }
494 }
495 }
496 }
497
498 if (new_nulltime.time)
499 nulltime = new_nulltime;
500
501 return 1;
502}
503
504/**
505 * clears statistic of reader ridx.
506 **/
507void clear_reader_stat(struct s_reader *rdr)
508{
509 if (!rdr->lb_stat)
510 return;
511
512 ll_clear_data(rdr->lb_stat);
513}
Note: See TracBrowser for help on using the repository browser.