source: trunk/module-stat.c@ 3181

Last change on this file since 3181 was 3181, checked in by dingo35, 10 years ago

Adding threadsafety FIXMEs, feel free to join checking..

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