This source file includes following definitions.
- load2str
 
- find_cib_loadfile
 
- throttle_cib_load
 
- throttle_load_avg
 
- throttle_check_thresholds
 
- throttle_handle_load
 
- throttle_mode
 
- throttle_send_command
 
- throttle_timer_cb
 
- throttle_record_free
 
- throttle_set_load_target
 
- throttle_update_job_max
 
- throttle_init
 
- throttle_fini
 
- throttle_get_total_job_limit
 
- throttle_get_job_limit
 
- throttle_update
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <sys/types.h>
  13 #include <sys/stat.h>
  14 
  15 #include <unistd.h>
  16 #include <ctype.h>
  17 #include <dirent.h>
  18 
  19 #include <crm/crm.h>
  20 #include <crm/msg_xml.h>
  21 #include <crm/cluster.h>
  22 
  23 #include <pacemaker-controld.h>
  24 
  25 
  26 
  27 
  28 enum throttle_state_e {
  29     throttle_none       = 0x0000,
  30     throttle_low        = 0x0001,
  31     throttle_med        = 0x0010,
  32     throttle_high       = 0x0100,
  33     throttle_extreme    = 0x1000,
  34 };
  35 
  36 struct throttle_record_s {
  37     int max;
  38     enum throttle_state_e mode;
  39     char *node;
  40 };
  41 
  42 static int throttle_job_max = 0;
  43 static float throttle_load_target = 0.0;
  44 
  45 #define THROTTLE_FACTOR_LOW    1.2
  46 #define THROTTLE_FACTOR_MEDIUM 1.6
  47 #define THROTTLE_FACTOR_HIGH   2.0
  48 
  49 static GHashTable *throttle_records = NULL;
  50 static mainloop_timer_t *throttle_timer = NULL;
  51 
  52 static const char *
  53 load2str(enum throttle_state_e mode)
     
  54 {
  55     switch (mode) {
  56         case throttle_extreme:  return "extreme";
  57         case throttle_high:     return "high";
  58         case throttle_med:      return "medium";
  59         case throttle_low:      return "low";
  60         case throttle_none:     return "negligible";
  61         default:                return "undetermined";
  62     }
  63 }
  64 
  65 #if SUPPORT_PROCFS
  66 
  67 
  68 
  69 
  70 
  71 
  72 
  73 
  74 
  75 
  76 static char *
  77 find_cib_loadfile(void)
     
  78 {
  79     pid_t pid = pcmk__procfs_pid_of("pacemaker-based");
  80 
  81     return pid? crm_strdup_printf("/proc/%lld/stat", (long long) pid) : NULL;
  82 }
  83 
  84 static bool
  85 throttle_cib_load(float *load)
     
  86 {
  87 
  88 
  89 
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 
  98 
  99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125     static char *loadfile = NULL;
 126     static time_t last_call = 0;
 127     static long ticks_per_s = 0;
 128     static unsigned long last_utime, last_stime;
 129 
 130     char buffer[64*1024];
 131     FILE *stream = NULL;
 132     time_t now = time(NULL);
 133 
 134     if(load == NULL) {
 135         return FALSE;
 136     } else {
 137         *load = 0.0;
 138     }
 139 
 140     if(loadfile == NULL) {
 141         last_call = 0;
 142         last_utime = 0;
 143         last_stime = 0;
 144         loadfile = find_cib_loadfile();
 145         if (loadfile == NULL) {
 146             crm_warn("Couldn't find CIB load file");
 147             return FALSE;
 148         }
 149         ticks_per_s = sysconf(_SC_CLK_TCK);
 150         crm_trace("Found %s", loadfile);
 151     }
 152 
 153     stream = fopen(loadfile, "r");
 154     if(stream == NULL) {
 155         int rc = errno;
 156 
 157         crm_warn("Couldn't read %s: %s (%d)", loadfile, pcmk_strerror(rc), rc);
 158         free(loadfile); loadfile = NULL;
 159         return FALSE;
 160     }
 161 
 162     if(fgets(buffer, sizeof(buffer), stream)) {
 163         char *comm = calloc(1, 256);
 164         char state = 0;
 165         int rc = 0, pid = 0, ppid = 0, pgrp = 0, session = 0, tty_nr = 0, tpgid = 0;
 166         unsigned long flags = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0, utime = 0, stime = 0;
 167 
 168         rc = sscanf(buffer,  "%d %[^ ] %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu",
 169                     &pid, comm, &state,
 170                     &ppid, &pgrp, &session, &tty_nr, &tpgid,
 171                     &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime);
 172         free(comm);
 173 
 174         if(rc != 15) {
 175             crm_err("Only %d of 15 fields found in %s", rc, loadfile);
 176             fclose(stream);
 177             return FALSE;
 178 
 179         } else if(last_call > 0
 180            && last_call < now
 181            && last_utime <= utime
 182            && last_stime <= stime) {
 183 
 184             time_t elapsed = now - last_call;
 185             unsigned long delta_utime = utime - last_utime;
 186             unsigned long delta_stime = stime - last_stime;
 187 
 188             *load = (delta_utime + delta_stime); 
 189             *load /= ticks_per_s;
 190             *load /= elapsed;
 191             crm_debug("cib load: %f (%lu ticks in %lds)", *load, delta_utime + delta_stime, (long)elapsed);
 192 
 193         } else {
 194             crm_debug("Init %lu + %lu ticks at %ld (%lu tps)", utime, stime, (long)now, ticks_per_s);
 195         }
 196 
 197         last_call = now;
 198         last_utime = utime;
 199         last_stime = stime;
 200 
 201         fclose(stream);
 202         return TRUE;
 203     }
 204 
 205     fclose(stream);
 206     return FALSE;
 207 }
 208 
 209 static bool
 210 throttle_load_avg(float *load)
     
 211 {
 212     char buffer[256];
 213     FILE *stream = NULL;
 214     const char *loadfile = "/proc/loadavg";
 215 
 216     if(load == NULL) {
 217         return FALSE;
 218     }
 219 
 220     stream = fopen(loadfile, "r");
 221     if(stream == NULL) {
 222         int rc = errno;
 223         crm_warn("Couldn't read %s: %s (%d)", loadfile, pcmk_strerror(rc), rc);
 224         return FALSE;
 225     }
 226 
 227     if(fgets(buffer, sizeof(buffer), stream)) {
 228         char *nl = strstr(buffer, "\n");
 229 
 230         
 231         *load = strtof(buffer, NULL);
 232         if(nl) { nl[0] = 0; }
 233 
 234         fclose(stream);
 235         return TRUE;
 236     }
 237 
 238     fclose(stream);
 239     return FALSE;
 240 }
 241 
 242 
 243 
 244 
 245 
 246 
 247 
 248 
 249 
 250 
 251 
 252 static enum throttle_state_e
 253 throttle_check_thresholds(float load, const char *desc, float thresholds[4])
     
 254 {
 255     if (load > thresholds[3]) {
 256         crm_notice("Extreme %s detected: %f", desc, load);
 257         return throttle_extreme;
 258 
 259     } else if (load > thresholds[2]) {
 260         crm_notice("High %s detected: %f", desc, load);
 261         return throttle_high;
 262 
 263     } else if (load > thresholds[1]) {
 264         crm_info("Moderate %s detected: %f", desc, load);
 265         return throttle_med;
 266 
 267     } else if (load > thresholds[0]) {
 268         crm_debug("Noticeable %s detected: %f", desc, load);
 269         return throttle_low;
 270     }
 271 
 272     crm_trace("Negligible %s detected: %f", desc, load);
 273     return throttle_none;
 274 }
 275 
 276 static enum throttle_state_e
 277 throttle_handle_load(float load, const char *desc, int cores)
     
 278 {
 279     float normalize;
 280     float thresholds[4];
 281 
 282     if (cores == 1) {
 283         
 284         normalize = 0.6;
 285 
 286     } else {
 287         
 288         normalize = cores;
 289     }
 290     thresholds[0] = throttle_load_target * normalize * THROTTLE_FACTOR_LOW;
 291     thresholds[1] = throttle_load_target * normalize * THROTTLE_FACTOR_MEDIUM;
 292     thresholds[2] = throttle_load_target * normalize * THROTTLE_FACTOR_HIGH;
 293     thresholds[3] = load + 1.0; 
 294 
 295     return throttle_check_thresholds(load, desc, thresholds);
 296 }
 297 #endif
 298 
 299 static enum throttle_state_e
 300 throttle_mode(void)
     
 301 {
 302     enum throttle_state_e mode = throttle_none;
 303 
 304 #if SUPPORT_PROCFS
 305     unsigned int cores;
 306     float load;
 307     float thresholds[4];
 308 
 309     cores = pcmk__procfs_num_cores();
 310     if(throttle_cib_load(&load)) {
 311         float cib_max_cpu = 0.95;
 312 
 313         
 314 
 315 
 316 
 317 
 318 
 319 
 320 
 321 
 322 
 323 
 324         if(cores == 1) {
 325             cib_max_cpu = 0.4;
 326         }
 327         if(throttle_load_target > 0.0 && throttle_load_target < cib_max_cpu) {
 328             cib_max_cpu = throttle_load_target;
 329         }
 330 
 331         thresholds[0] = cib_max_cpu * 0.8;
 332         thresholds[1] = cib_max_cpu * 0.9;
 333         thresholds[2] = cib_max_cpu;
 334         
 335         thresholds[3] = cib_max_cpu * 1.5;
 336 
 337         mode = throttle_check_thresholds(load, "CIB load", thresholds);
 338     }
 339 
 340     if(throttle_load_target <= 0) {
 341         
 342         return mode;
 343     }
 344 
 345     if(throttle_load_avg(&load)) {
 346         enum throttle_state_e cpu_load;
 347 
 348         cpu_load = throttle_handle_load(load, "CPU load", cores);
 349         if (cpu_load > mode) {
 350             mode = cpu_load;
 351         }
 352         crm_debug("Current load is %f across %u core(s)", load, cores);
 353     }
 354 #endif 
 355     return mode;
 356 }
 357 
 358 static void
 359 throttle_send_command(enum throttle_state_e mode)
     
 360 {
 361     xmlNode *xml = NULL;
 362     static enum throttle_state_e last = -1;
 363 
 364     if(mode != last) {
 365         crm_info("New throttle mode: %s load (was %s)",
 366                  load2str(mode), load2str(last));
 367         last = mode;
 368 
 369         xml = create_request(CRM_OP_THROTTLE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
 370         crm_xml_add_int(xml, F_CRM_THROTTLE_MODE, mode);
 371         crm_xml_add_int(xml, F_CRM_THROTTLE_MAX, throttle_job_max);
 372 
 373         send_cluster_message(NULL, crm_msg_crmd, xml, TRUE);
 374         free_xml(xml);
 375     }
 376 }
 377 
 378 static gboolean
 379 throttle_timer_cb(gpointer data)
     
 380 {
 381     throttle_send_command(throttle_mode());
 382     return TRUE;
 383 }
 384 
 385 static void
 386 throttle_record_free(gpointer p)
     
 387 {
 388     struct throttle_record_s *r = p;
 389     free(r->node);
 390     free(r);
 391 }
 392 
 393 void
 394 throttle_set_load_target(float target)
     
 395 {
 396     throttle_load_target = target;
 397 }
 398 
 399 
 400 
 401 
 402 
 403 
 404 
 405 void
 406 throttle_update_job_max(const char *preference)
     
 407 {
 408     long long max = -1;
 409     const char *env_limit = getenv("PCMK_node_action_limit");
 410 
 411     if (env_limit != NULL) {
 412         preference = env_limit; 
 413     }
 414     if (preference) {
 415         max = crm_parse_ll(preference, NULL);
 416     }
 417     if (max > 0) {
 418         throttle_job_max = (int) max;
 419     } else {
 420         
 421         throttle_job_max = 2 * pcmk__procfs_num_cores();
 422     }
 423 }
 424 
 425 void
 426 throttle_init(void)
     
 427 {
 428     if(throttle_records == NULL) {
 429         throttle_records = g_hash_table_new_full(
 430             crm_str_hash, g_str_equal, NULL, throttle_record_free);
 431         throttle_timer = mainloop_timer_add("throttle", 30 * 1000, TRUE, throttle_timer_cb, NULL);
 432     }
 433 
 434     throttle_update_job_max(NULL);
 435     mainloop_timer_start(throttle_timer);
 436 }
 437 
 438 void
 439 throttle_fini(void)
     
 440 {
 441     if (throttle_timer != NULL) {
 442         mainloop_timer_del(throttle_timer);
 443         throttle_timer = NULL;
 444     }
 445     if (throttle_records != NULL) {
 446         g_hash_table_destroy(throttle_records);
 447         throttle_records = NULL;
 448     }
 449 }
 450 
 451 int
 452 throttle_get_total_job_limit(int l)
     
 453 {
 454     
 455     GHashTableIter iter;
 456     int limit = l;
 457     int peers = crm_active_peers();
 458     struct throttle_record_s *r = NULL;
 459 
 460     g_hash_table_iter_init(&iter, throttle_records);
 461 
 462     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &r)) {
 463         switch(r->mode) {
 464 
 465             case throttle_extreme:
 466                 if(limit == 0 || limit > peers/4) {
 467                     limit = QB_MAX(1, peers/4);
 468                 }
 469                 break;
 470 
 471             case throttle_high:
 472                 if(limit == 0 || limit > peers/2) {
 473                     limit = QB_MAX(1, peers/2);
 474                 }
 475                 break;
 476             default:
 477                 break;
 478         }
 479     }
 480     if(limit == l) {
 481         
 482 
 483     } else if(l == 0) {
 484         crm_trace("Using batch-limit=%d", limit);
 485 
 486     } else {
 487         crm_trace("Using batch-limit=%d instead of %d", limit, l);
 488     }
 489     return limit;
 490 }
 491 
 492 int
 493 throttle_get_job_limit(const char *node)
     
 494 {
 495     int jobs = 1;
 496     struct throttle_record_s *r = NULL;
 497 
 498     r = g_hash_table_lookup(throttle_records, node);
 499     if(r == NULL) {
 500         r = calloc(1, sizeof(struct throttle_record_s));
 501         r->node = strdup(node);
 502         r->mode = throttle_low;
 503         r->max = throttle_job_max;
 504         crm_trace("Defaulting to local values for unknown node %s", node);
 505 
 506         g_hash_table_insert(throttle_records, r->node, r);
 507     }
 508 
 509     switch(r->mode) {
 510         case throttle_extreme:
 511         case throttle_high:
 512             jobs = 1; 
 513             break;
 514         case throttle_med:
 515             jobs = QB_MAX(1, r->max / 4);
 516             break;
 517         case throttle_low:
 518             jobs = QB_MAX(1, r->max / 2);
 519             break;
 520         case throttle_none:
 521             jobs = QB_MAX(1, r->max);
 522             break;
 523         default:
 524             crm_err("Unknown throttle mode %.4x on %s", r->mode, node);
 525             break;
 526     }
 527     return jobs;
 528 }
 529 
 530 void
 531 throttle_update(xmlNode *xml)
     
 532 {
 533     int max = 0;
 534     int mode = 0;
 535     struct throttle_record_s *r = NULL;
 536     const char *from = crm_element_value(xml, F_CRM_HOST_FROM);
 537 
 538     crm_element_value_int(xml, F_CRM_THROTTLE_MODE, &mode);
 539     crm_element_value_int(xml, F_CRM_THROTTLE_MAX, &max);
 540 
 541     r = g_hash_table_lookup(throttle_records, from);
 542 
 543     if(r == NULL) {
 544         r = calloc(1, sizeof(struct throttle_record_s));
 545         r->node = strdup(from);
 546         g_hash_table_insert(throttle_records, r->node, r);
 547     }
 548 
 549     r->max = max;
 550     r->mode = (enum throttle_state_e) mode;
 551 
 552     crm_debug("Node %s has %s load and supports at most %d jobs; new job limit %d",
 553               from, load2str((enum throttle_state_e) mode), max,
 554               throttle_get_job_limit(from));
 555 }