1 /*
2 Copyright (c) 2011, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "mt_thr_config.hpp"
26 #include <kernel/ndb_limits.h>
27 #include "../../common/util/parse_mask.hpp"
28 #include <NdbLockCpuUtil.h>
29
30 static const struct THRConfig::Entries m_entries[] =
31 {
32 // name type min max
33 { "main", THRConfig::T_MAIN, 1, 1 },
34 { "ldm", THRConfig::T_LDM, 1, MAX_NDBMT_LQH_THREADS },
35 { "recv", THRConfig::T_RECV, 1, MAX_NDBMT_RECEIVE_THREADS },
36 { "rep", THRConfig::T_REP, 1, 1 },
37 { "io", THRConfig::T_IO, 1, 1 },
38 { "watchdog", THRConfig::T_WD, 1, 1 },
39 { "tc", THRConfig::T_TC, 0, MAX_NDBMT_TC_THREADS },
40 { "send", THRConfig::T_SEND, 0, MAX_NDBMT_SEND_THREADS }
41 };
42
43 static const struct THRConfig::Param m_params[] =
44 {
45 { "count", THRConfig::Param::S_UNSIGNED },
46 { "cpubind", THRConfig::Param::S_BITMASK },
47 { "cpuset", THRConfig::Param::S_BITMASK },
48 { "realtime", THRConfig::Param::S_UNSIGNED },
49 { "spintime", THRConfig::Param::S_UNSIGNED }
50 };
51
52 #define IX_COUNT 0
53 #define IX_CPUBOUND 1
54 #define IX_CPUSET 2
55 #define IX_REALTIME 3
56 #define IX_SPINTIME 4
57
58 static
59 unsigned
getMaxEntries(Uint32 type)60 getMaxEntries(Uint32 type)
61 {
62 for (Uint32 i = 0; i<NDB_ARRAY_SIZE(m_entries); i++)
63 {
64 if (m_entries[i].m_type == type)
65 return m_entries[i].m_max_cnt;
66 }
67 return 0;
68 }
69
70 static
71 const char *
getEntryName(Uint32 type)72 getEntryName(Uint32 type)
73 {
74 for (Uint32 i = 0; i<NDB_ARRAY_SIZE(m_entries); i++)
75 {
76 if (m_entries[i].m_type == type)
77 return m_entries[i].m_name;
78 }
79 return 0;
80 }
81
82 static
83 Uint32
getEntryType(const char * type)84 getEntryType(const char * type)
85 {
86 for (Uint32 i = 0; i<NDB_ARRAY_SIZE(m_entries); i++)
87 {
88 if (native_strcasecmp(type, m_entries[i].m_name) == 0)
89 return i;
90 }
91
92 return THRConfig::T_END;
93 }
94
THRConfig()95 THRConfig::THRConfig()
96 {
97 m_classic = false;
98 }
99
~THRConfig()100 THRConfig::~THRConfig()
101 {
102 }
103
104 int
setLockExecuteThreadToCPU(const char * mask)105 THRConfig::setLockExecuteThreadToCPU(const char * mask)
106 {
107 int res = parse_mask(mask, m_LockExecuteThreadToCPU);
108 if (res < 0)
109 {
110 m_err_msg.assfmt("failed to parse 'LockExecuteThreadToCPU=%s' "
111 "(error: %d)",
112 mask, res);
113 return -1;
114 }
115 return 0;
116 }
117
118 int
setLockIoThreadsToCPU(unsigned val)119 THRConfig::setLockIoThreadsToCPU(unsigned val)
120 {
121 m_LockIoThreadsToCPU.set(val);
122 return 0;
123 }
124
125 void
add(T_Type t,unsigned realtime,unsigned spintime)126 THRConfig::add(T_Type t, unsigned realtime, unsigned spintime)
127 {
128 T_Thread tmp;
129 tmp.m_type = t;
130 tmp.m_bind_type = T_Thread::B_UNBOUND;
131 tmp.m_no = m_threads[t].size();
132 tmp.m_realtime = realtime;
133 if (spintime > 500)
134 spintime = 500;
135 tmp.m_spintime = spintime;
136 m_threads[t].push_back(tmp);
137 }
138
139 static
140 void
computeThreadConfig(Uint32 MaxNoOfExecutionThreads,Uint32 & tcthreads,Uint32 & lqhthreads,Uint32 & sendthreads,Uint32 & recvthreads)141 computeThreadConfig(Uint32 MaxNoOfExecutionThreads,
142 Uint32 & tcthreads,
143 Uint32 & lqhthreads,
144 Uint32 & sendthreads,
145 Uint32 & recvthreads)
146 {
147 assert(MaxNoOfExecutionThreads >= 9);
148 static const struct entry
149 {
150 Uint32 M;
151 Uint32 lqh;
152 Uint32 tc;
153 Uint32 send;
154 Uint32 recv;
155 } table[] = {
156 { 9, 4, 2, 0, 1 },
157 { 10, 4, 2, 1, 1 },
158 { 11, 4, 3, 1, 1 },
159 { 12, 6, 2, 1, 1 },
160 { 13, 6, 3, 1, 1 },
161 { 14, 6, 3, 1, 2 },
162 { 15, 6, 3, 2, 2 },
163 { 16, 8, 3, 1, 2 },
164 { 17, 8, 4, 1, 2 },
165 { 18, 8, 4, 2, 2 },
166 { 19, 8, 5, 2, 2 },
167 { 20, 10, 4, 2, 2 },
168 { 21, 10, 5, 2, 2 },
169 { 22, 10, 5, 2, 3 },
170 { 23, 10, 6, 2, 3 },
171 { 24, 12, 5, 2, 3 },
172 { 25, 12, 6, 2, 3 },
173 { 26, 12, 6, 3, 3 },
174 { 27, 12, 7, 3, 3 },
175 { 28, 12, 7, 3, 4 },
176 { 29, 12, 8, 3, 4 },
177 { 30, 12, 8, 4, 4 },
178 { 31, 12, 9, 4, 4 },
179 { 32, 16, 8, 3, 3 },
180 { 33, 16, 8, 3, 4 },
181 { 34, 16, 8, 4, 4 },
182 { 35, 16, 9, 4, 4 },
183 { 36, 16, 10, 4, 4 },
184 { 37, 16, 10, 4, 5 },
185 { 38, 16, 11, 4, 5 },
186 { 39, 16, 11, 5, 5 },
187 { 40, 20, 10, 4, 4 },
188 { 41, 20, 10, 4, 5 },
189 { 42, 20, 11, 4, 5 },
190 { 43, 20, 11, 5, 5 },
191 { 44, 20, 12, 5, 5 },
192 { 45, 20, 12, 5, 6 },
193 { 46, 20, 13, 5, 6 },
194 { 47, 20, 13, 6, 6 },
195 { 48, 24, 12, 5, 5 },
196 { 49, 24, 12, 5, 6 },
197 { 50, 24, 13, 5, 6 },
198 { 51, 24, 13, 6, 6 },
199 { 52, 24, 14, 6, 6 },
200 { 53, 24, 14, 6, 7 },
201 { 54, 24, 15, 6, 7 },
202 { 55, 24, 15, 7, 7 },
203 { 56, 24, 16, 7, 7 },
204 { 57, 24, 16, 7, 8 },
205 { 58, 24, 17, 7, 8 },
206 { 59, 24, 17, 8, 8 },
207 { 60, 24, 18, 8, 8 },
208 { 61, 24, 18, 8, 9 },
209 { 62, 24, 19, 8, 9 },
210 { 63, 24, 19, 9, 9 },
211 { 64, 32, 16, 7, 7 },
212 { 65, 32, 16, 7, 8 },
213 { 66, 32, 17, 7, 8 },
214 { 67, 32, 17, 8, 8 },
215 { 68, 32, 18, 8, 8 },
216 { 69, 32, 18, 8, 9 },
217 { 70, 32, 19, 8, 9 },
218 { 71, 32, 20, 8, 9 },
219 { 72, 32, 20, 8, 10 }
220 };
221
222 Uint32 P = MaxNoOfExecutionThreads - 9;
223 if (P >= NDB_ARRAY_SIZE(table))
224 {
225 P = NDB_ARRAY_SIZE(table) - 1;
226 }
227
228 lqhthreads = table[P].lqh;
229 tcthreads = table[P].tc;
230 sendthreads = table[P].send;
231 recvthreads = table[P].recv;
232 }
233
234 int
do_parse(unsigned MaxNoOfExecutionThreads,unsigned __ndbmt_lqh_threads,unsigned __ndbmt_classic,unsigned realtime,unsigned spintime)235 THRConfig::do_parse(unsigned MaxNoOfExecutionThreads,
236 unsigned __ndbmt_lqh_threads,
237 unsigned __ndbmt_classic,
238 unsigned realtime,
239 unsigned spintime)
240 {
241 /**
242 * This is old ndbd.cpp : get_multithreaded_config
243 */
244 if (__ndbmt_classic)
245 {
246 m_classic = true;
247 add(T_LDM, realtime, spintime);
248 add(T_MAIN, realtime, spintime);
249 add(T_IO, realtime, 0);
250 add(T_WD, realtime, 0);
251 const bool allow_too_few_cpus = true;
252 return do_bindings(allow_too_few_cpus);
253 }
254
255 Uint32 tcthreads = 0;
256 Uint32 lqhthreads = 0;
257 Uint32 sendthreads = 0;
258 Uint32 recvthreads = 1;
259 switch(MaxNoOfExecutionThreads){
260 case 0:
261 case 1:
262 case 2:
263 case 3:
264 lqhthreads = 1; // TC + receiver + SUMA + LQH
265 break;
266 case 4:
267 case 5:
268 case 6:
269 lqhthreads = 2; // TC + receiver + SUMA + 2 * LQH
270 break;
271 case 7:
272 case 8:
273 lqhthreads = 4; // TC + receiver + SUMA + 4 * LQH
274 break;
275 default:
276 computeThreadConfig(MaxNoOfExecutionThreads,
277 tcthreads,
278 lqhthreads,
279 sendthreads,
280 recvthreads);
281 }
282
283 if (__ndbmt_lqh_threads)
284 {
285 lqhthreads = __ndbmt_lqh_threads;
286 }
287
288 add(T_MAIN, realtime, spintime); /* Global */
289 add(T_REP, realtime, spintime); /* Local, main consumer is SUMA */
290 for(Uint32 i = 0; i < recvthreads; i++)
291 {
292 add(T_RECV, realtime, spintime);
293 }
294 add(T_IO, realtime, 0);
295 add(T_WD, realtime, 0);
296 for(Uint32 i = 0; i < lqhthreads; i++)
297 {
298 add(T_LDM, realtime, spintime);
299 }
300 for(Uint32 i = 0; i < tcthreads; i++)
301 {
302 add(T_TC, realtime, spintime);
303 }
304 for(Uint32 i = 0; i < sendthreads; i++)
305 {
306 add(T_SEND, realtime, spintime);
307 }
308
309 // If we have set TC-threads...we say that this is "new" code
310 // and give error for having too few CPU's in mask compared to #threads
311 // started
312 const bool allow_too_few_cpus = (tcthreads == 0 &&
313 sendthreads == 0 &&
314 recvthreads == 1);
315 return do_bindings(allow_too_few_cpus) || do_validate();
316 }
317
318 int
do_bindings(bool allow_too_few_cpus)319 THRConfig::do_bindings(bool allow_too_few_cpus)
320 {
321 /**
322 * Use LockIoThreadsToCPU also to lock to Watchdog, SocketServer
323 * and SocketClient for backwards compatibility reasons,
324 * preferred manner is to only use ThreadConfig
325 */
326 if (m_LockIoThreadsToCPU.count() == 1)
327 {
328 m_threads[T_IO][0].m_bind_type = T_Thread::B_CPU_BOUND;
329 m_threads[T_IO][0].m_bind_no = m_LockIoThreadsToCPU.getBitNo(0);
330 m_threads[T_WD][0].m_bind_type = T_Thread::B_CPU_BOUND;
331 m_threads[T_WD][0].m_bind_no = m_LockIoThreadsToCPU.getBitNo(0);
332 }
333 else if (m_LockIoThreadsToCPU.count() > 1)
334 {
335 unsigned no = createCpuSet(m_LockIoThreadsToCPU);
336 m_threads[T_IO][0].m_bind_type = T_Thread::B_CPUSET_BOUND;
337 m_threads[T_IO][0].m_bind_no = no;
338 m_threads[T_WD][0].m_bind_type = T_Thread::B_CPUSET_BOUND;
339 m_threads[T_WD][0].m_bind_no = no;
340 }
341
342 /**
343 * Check that no cpu_sets overlap
344 */
345 for (unsigned i = 0; i<m_cpu_sets.size(); i++)
346 {
347 for (unsigned j = i + 1; j < m_cpu_sets.size(); j++)
348 {
349 if (m_cpu_sets[i].overlaps(m_cpu_sets[j]))
350 {
351 m_err_msg.assfmt("Overlapping cpuset's [ %s ] and [ %s ]",
352 m_cpu_sets[i].str().c_str(),
353 m_cpu_sets[j].str().c_str());
354 return -1;
355 }
356 }
357 }
358
359 /**
360 * Check that no cpu_sets overlap with cpu_bound
361 */
362 for (unsigned i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
363 {
364 for (unsigned j = 0; j < m_threads[i].size(); j++)
365 {
366 if (m_threads[i][j].m_bind_type == T_Thread::B_CPU_BOUND)
367 {
368 unsigned cpu = m_threads[i][j].m_bind_no;
369 for (unsigned k = 0; k<m_cpu_sets.size(); k++)
370 {
371 if (m_cpu_sets[k].get(cpu))
372 {
373 m_err_msg.assfmt("Overlapping cpubind %u with cpuset [ %s ]",
374 cpu,
375 m_cpu_sets[k].str().c_str());
376
377 return -1;
378 }
379 }
380 }
381 }
382 }
383
384 /**
385 * Remove all already bound threads from LockExecuteThreadToCPU-mask
386 */
387 for (unsigned i = 0; i<m_cpu_sets.size(); i++)
388 {
389 for (unsigned j = 0; j < m_cpu_sets[i].count(); j++)
390 {
391 m_LockExecuteThreadToCPU.clear(m_cpu_sets[i].getBitNo(j));
392 }
393 }
394
395 unsigned cnt_unbound = 0;
396 for (unsigned i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
397 {
398 if (i == T_IO || i == T_WD)
399 {
400 /* IO and Watchdog threads aren't execute threads */
401 continue;
402 }
403 for (unsigned j = 0; j < m_threads[i].size(); j++)
404 {
405 if (m_threads[i][j].m_bind_type == T_Thread::B_CPU_BOUND)
406 {
407 unsigned cpu = m_threads[i][j].m_bind_no;
408 m_LockExecuteThreadToCPU.clear(cpu);
409 }
410 else if (m_threads[i][j].m_bind_type == T_Thread::B_UNBOUND)
411 {
412 cnt_unbound ++;
413 }
414 }
415 }
416
417 if (m_LockExecuteThreadToCPU.count())
418 {
419 /**
420 * This is old mt.cpp : setcpuaffinity
421 */
422 SparseBitmask& mask = m_LockExecuteThreadToCPU;
423 unsigned cnt = mask.count();
424 unsigned num_threads = cnt_unbound;
425 bool isMtLqh = !m_classic;
426
427 if (cnt < num_threads)
428 {
429 m_info_msg.assfmt("WARNING: Too few CPU's specified with "
430 "LockExecuteThreadToCPU. Only %u specified "
431 " but %u was needed, this may cause contention.\n",
432 cnt, num_threads);
433
434 if (!allow_too_few_cpus)
435 {
436 m_err_msg.assfmt("Too few CPU's specifed with LockExecuteThreadToCPU. "
437 "This is not supported when using multiple TC threads");
438 return -1;
439 }
440 }
441
442 if (cnt >= num_threads)
443 {
444 m_info_msg.appfmt("Assigning each thread its own CPU\n");
445 unsigned no = 0;
446 for (unsigned i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
447 {
448 if (i == T_IO || i == T_WD)
449 continue;
450 for (unsigned j = 0; j < m_threads[i].size(); j++)
451 {
452 if (m_threads[i][j].m_bind_type == T_Thread::B_UNBOUND)
453 {
454 m_threads[i][j].m_bind_type = T_Thread::B_CPU_BOUND;
455 m_threads[i][j].m_bind_no = mask.getBitNo(no);
456 no++;
457 }
458 }
459 }
460 }
461 else if (cnt == 1)
462 {
463 unsigned cpu = mask.getBitNo(0);
464 m_info_msg.appfmt("Assigning all threads to CPU %u\n", cpu);
465 for (unsigned i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
466 {
467 if (i == T_IO || i == T_WD)
468 continue;
469 bind_unbound(m_threads[i], cpu);
470 }
471 }
472 else if (isMtLqh)
473 {
474 unsigned unbound_ldm = count_unbound(m_threads[T_LDM]);
475 if (cnt > unbound_ldm)
476 {
477 /**
478 * let each LQH have it's own CPU and rest share...
479 */
480 m_info_msg.append("Assigning LQH threads to dedicated CPU(s) and "
481 "other threads will share remaining\n");
482 unsigned cpu = mask.find(0);
483 for (unsigned i = 0; i < m_threads[T_LDM].size(); i++)
484 {
485 if (m_threads[T_LDM][i].m_bind_type == T_Thread::B_UNBOUND)
486 {
487 m_threads[T_LDM][i].m_bind_type = T_Thread::B_CPU_BOUND;
488 m_threads[T_LDM][i].m_bind_no = cpu;
489 mask.clear(cpu);
490 cpu = mask.find(cpu + 1);
491 }
492 }
493
494 cpu = mask.find(0);
495 bind_unbound(m_threads[T_MAIN], cpu);
496 bind_unbound(m_threads[T_REP], cpu);
497 if ((cpu = mask.find(cpu + 1)) == mask.NotFound)
498 {
499 cpu = mask.find(0);
500 }
501 bind_unbound(m_threads[T_RECV], cpu);
502 }
503 else
504 {
505 // put receiver, tc, backup/suma in 1 thread,
506 // and round robin LQH for rest
507 unsigned cpu = mask.find(0);
508 m_info_msg.appfmt("Assigning LQH threads round robin to CPU(s) and "
509 "other threads will share CPU %u\n", cpu);
510 bind_unbound(m_threads[T_MAIN], cpu); // TC
511 bind_unbound(m_threads[T_REP], cpu);
512 bind_unbound(m_threads[T_RECV], cpu);
513 mask.clear(cpu);
514
515 cpu = mask.find(0);
516 for (unsigned i = 0; i < m_threads[T_LDM].size(); i++)
517 {
518 if (m_threads[T_LDM][i].m_bind_type == T_Thread::B_UNBOUND)
519 {
520 m_threads[T_LDM][i].m_bind_type = T_Thread::B_CPU_BOUND;
521 m_threads[T_LDM][i].m_bind_no = cpu;
522 if ((cpu = mask.find(cpu + 1)) == mask.NotFound)
523 {
524 cpu = mask.find(0);
525 }
526 }
527 }
528 }
529 }
530 else
531 {
532 unsigned cpu = mask.find(0);
533 m_info_msg.appfmt("Assigning LQH thread to CPU %u and "
534 "other threads will share\n", cpu);
535 bind_unbound(m_threads[T_LDM], cpu);
536 cpu = mask.find(cpu + 1);
537 bind_unbound(m_threads[T_MAIN], cpu);
538 bind_unbound(m_threads[T_RECV], cpu);
539 }
540 }
541
542 return 0;
543 }
544
545 unsigned
count_unbound(const Vector<T_Thread> & vec) const546 THRConfig::count_unbound(const Vector<T_Thread>& vec) const
547 {
548 unsigned cnt = 0;
549 for (unsigned i = 0; i < vec.size(); i++)
550 {
551 if (vec[i].m_bind_type == T_Thread::B_UNBOUND)
552 cnt ++;
553 }
554 return cnt;
555 }
556
557 void
bind_unbound(Vector<T_Thread> & vec,unsigned cpu)558 THRConfig::bind_unbound(Vector<T_Thread>& vec, unsigned cpu)
559 {
560 for (unsigned i = 0; i < vec.size(); i++)
561 {
562 if (vec[i].m_bind_type == T_Thread::B_UNBOUND)
563 {
564 vec[i].m_bind_type = T_Thread::B_CPU_BOUND;
565 vec[i].m_bind_no = cpu;
566 }
567 }
568 }
569
570 int
do_validate()571 THRConfig::do_validate()
572 {
573 /**
574 * Check that there aren't too many of any thread type
575 */
576 for (unsigned i = 0; i< NDB_ARRAY_SIZE(m_threads); i++)
577 {
578 if (m_threads[i].size() > getMaxEntries(i))
579 {
580 m_err_msg.assfmt("Too many instances(%u) of %s max supported: %u",
581 m_threads[i].size(),
582 getEntryName(i),
583 getMaxEntries(i));
584 return -1;
585 }
586 }
587
588 /**
589 * LDM can be 1 2 4 6 8 10 12 16 20 24 32
590 */
591 if (m_threads[T_LDM].size() != 1 &&
592 m_threads[T_LDM].size() != 2 &&
593 m_threads[T_LDM].size() != 4 &&
594 m_threads[T_LDM].size() != 6 &&
595 m_threads[T_LDM].size() != 8 &&
596 m_threads[T_LDM].size() != 10 &&
597 m_threads[T_LDM].size() != 12 &&
598 m_threads[T_LDM].size() != 16 &&
599 m_threads[T_LDM].size() != 20 &&
600 m_threads[T_LDM].size() != 24 &&
601 m_threads[T_LDM].size() != 32)
602 {
603 m_err_msg.assfmt("No of LDM-instances can be 1,2,4,6,8,12,16,24 or 32. Specified: %u",
604 m_threads[T_LDM].size());
605 return -1;
606 }
607
608 return 0;
609 }
610
611 void
append_name(const char * name,const char * sep,bool & append_name_flag)612 THRConfig::append_name(const char *name,
613 const char *sep,
614 bool & append_name_flag)
615 {
616 if (!append_name_flag)
617 {
618 m_cfg_string.append(sep);
619 m_cfg_string.append(name);
620 append_name_flag = true;
621 }
622 }
623
624 const char *
getConfigString()625 THRConfig::getConfigString()
626 {
627 m_cfg_string.clear();
628 const char * sep = "";
629 const char * end_sep;
630 const char * start_sep;
631 const char * between_sep;
632 bool append_name_flag;
633 for (unsigned i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
634 {
635 if (m_threads[i].size())
636 {
637 const char * name = getEntryName(i);
638 for (unsigned j = 0; j < m_threads[i].size(); j++)
639 {
640 start_sep = "={";
641 end_sep = "";
642 between_sep="";
643 append_name_flag = false;
644 if (i != T_IO && i != T_WD)
645 {
646 append_name(name, sep, append_name_flag);
647 sep=",";
648 }
649 if (m_threads[i][j].m_bind_type != T_Thread::B_UNBOUND)
650 {
651 append_name(name, sep, append_name_flag);
652 sep=",";
653 m_cfg_string.append(start_sep);
654 end_sep = "}";
655 start_sep="";
656 if (m_threads[i][j].m_bind_type == T_Thread::B_CPU_BOUND)
657 {
658 m_cfg_string.appfmt("cpubind=%u", m_threads[i][j].m_bind_no);
659 between_sep=",";
660 }
661 else if (m_threads[i][j].m_bind_type == T_Thread::B_CPUSET_BOUND)
662 {
663 m_cfg_string.appfmt("cpuset=%s",
664 m_cpu_sets[m_threads[i][j].m_bind_no].str().c_str());
665 between_sep=",";
666 }
667 }
668 if (m_threads[i][j].m_spintime || m_threads[i][j].m_realtime)
669 {
670 append_name(name, sep, append_name_flag);
671 sep=",";
672 m_cfg_string.append(start_sep);
673 end_sep = "}";
674 if (m_threads[i][j].m_spintime)
675 {
676 m_cfg_string.append(between_sep);
677 m_cfg_string.appfmt("spintime=%u",
678 m_threads[i][j].m_spintime);
679 between_sep=",";
680 }
681 if (m_threads[i][j].m_realtime)
682 {
683 m_cfg_string.append(between_sep);
684 m_cfg_string.appfmt("realtime=%u",
685 m_threads[i][j].m_realtime);
686 between_sep=",";
687 }
688 }
689 m_cfg_string.append(end_sep);
690 }
691 }
692 }
693 return m_cfg_string.c_str();
694 }
695
696 Uint32
getThreadCount() const697 THRConfig::getThreadCount() const
698 {
699 // Note! not counting T_IO
700 Uint32 cnt = 0;
701 for (Uint32 i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
702 {
703 if (i != T_IO && i != T_WD)
704 {
705 cnt += m_threads[i].size();
706 }
707 }
708 return cnt;
709 }
710
711 Uint32
getThreadCount(T_Type type) const712 THRConfig::getThreadCount(T_Type type) const
713 {
714 for (Uint32 i = 0; i < NDB_ARRAY_SIZE(m_threads); i++)
715 {
716 if (i == (Uint32)type)
717 {
718 return m_threads[i].size();
719 }
720 }
721 return 0;
722 }
723
724 const char *
getErrorMessage() const725 THRConfig::getErrorMessage() const
726 {
727 if (m_err_msg.empty())
728 return 0;
729 return m_err_msg.c_str();
730 }
731
732 const char *
getInfoMessage() const733 THRConfig::getInfoMessage() const
734 {
735 if (m_info_msg.empty())
736 return 0;
737 return m_info_msg.c_str();
738 }
739
740 static
741 char *
skipblank(char * str)742 skipblank(char * str)
743 {
744 while (isspace(* str))
745 str++;
746 return str;
747 }
748
749 Uint32
find_type(char * & str)750 THRConfig::find_type(char *& str)
751 {
752 str = skipblank(str);
753
754 char * name = str;
755 if (* name == 0)
756 {
757 m_err_msg.assfmt("empty thread specification");
758 return 0;
759 }
760 char * end = name;
761 while(isalpha(* end))
762 end++;
763
764 char save = * end;
765 * end = 0;
766 Uint32 t = getEntryType(name);
767 if (t == T_END)
768 {
769 m_err_msg.assfmt("unknown thread type '%s'", name);
770 }
771 * end = save;
772 str = end;
773 return t;
774 }
775
776 struct ParamValue
777 {
ParamValueParamValue778 ParamValue() { found = false;}
779 bool found;
780 const char * string_val;
781 unsigned unsigned_val;
782 SparseBitmask mask_val;
783 };
784
785 static
786 int
parseUnsigned(char * & str,unsigned * dst)787 parseUnsigned(char *& str, unsigned * dst)
788 {
789 str = skipblank(str);
790 char * endptr = 0;
791 errno = 0;
792 long val = strtol(str, &endptr, 0);
793 if (errno == ERANGE)
794 return -1;
795 if (val < 0 || Int64(val) > 0xFFFFFFFF)
796 return -1;
797 if (endptr == str)
798 return -1;
799 str = endptr;
800 *dst = (unsigned)val;
801 return 0;
802 }
803
804 static
805 int
parseBitmask(char * & str,SparseBitmask * mask)806 parseBitmask(char *& str, SparseBitmask * mask)
807 {
808 str = skipblank(str);
809 size_t len = strspn(str, "0123456789-, ");
810 if (len == 0)
811 return -1;
812
813 while (isspace(str[len-1]))
814 len--;
815 if (str[len-1] == ',')
816 len--;
817 char save = str[len];
818 str[len] = 0;
819 int res = parse_mask(str, *mask);
820 str[len] = save;
821 str = str + len;
822 return res;
823 }
824
825 static
826 int
parseParams(char * str,ParamValue values[],BaseString & err)827 parseParams(char * str, ParamValue values[], BaseString& err)
828 {
829 const char * const save = str;
830 while (* str)
831 {
832 str = skipblank(str);
833
834 unsigned idx = 0;
835 for (; idx < NDB_ARRAY_SIZE(m_params); idx++)
836 {
837 if (native_strncasecmp(str, m_params[idx].name, strlen(m_params[idx].name)) == 0)
838 {
839 str += strlen(m_params[idx].name);
840 break;
841 }
842 }
843
844 if (idx == NDB_ARRAY_SIZE(m_params))
845 {
846 err.assfmt("Unknown param near: '%s'", str);
847 return -1;
848 }
849
850 if (values[idx].found == true)
851 {
852 err.assfmt("Param '%s' found twice", m_params[idx].name);
853 return -1;
854 }
855
856 str = skipblank(str);
857 if (* str != '=')
858 {
859 err.assfmt("Missing '=' after %s in '%s'", m_params[idx].name, save);
860 return -1;
861 }
862 str++;
863 str = skipblank(str);
864
865 int res = 0;
866 switch(m_params[idx].type){
867 case THRConfig::Param::S_UNSIGNED:
868 res = parseUnsigned(str, &values[idx].unsigned_val);
869 break;
870 case THRConfig::Param::S_BITMASK:
871 res = parseBitmask(str, &values[idx].mask_val);
872 break;
873 default:
874 err.assfmt("Internal error, unknown type for param: '%s'",
875 m_params[idx].name);
876 return -1;
877 }
878 if (res == -1)
879 {
880 err.assfmt("Unable to parse %s=%s", m_params[idx].name, str);
881 return -1;
882 }
883 values[idx].found = true;
884 str = skipblank(str);
885
886 if (* str == 0)
887 break;
888
889 if (* str != ',')
890 {
891 err.assfmt("Unable to parse near '%s'", str);
892 return -1;
893 }
894 str++;
895 }
896 return 0;
897 }
898
899 int
find_spec(char * & str,T_Type type,unsigned realtime,unsigned spintime)900 THRConfig::find_spec(char *& str,
901 T_Type type,
902 unsigned realtime,
903 unsigned spintime)
904 {
905 str = skipblank(str);
906
907 switch(* str){
908 case ',':
909 case 0:
910 if (type != T_IO && type != T_WD)
911 add(type, realtime, spintime);
912 else
913 add(type, realtime, 0);
914 return 0;
915 }
916
917 if (* str != '=')
918 {
919 err:
920 int len = (int)strlen(str);
921 m_err_msg.assfmt("Invalid format near: '%.*s'",
922 (len > 10) ? 10 : len, str);
923 return -1;
924 }
925
926 str++; // skip over =
927 str = skipblank(str);
928
929 if (* str != '{')
930 {
931 goto err;
932 }
933
934 str++;
935 char * start = str;
936
937 /**
938 * Find end
939 */
940 while (* str && (* str) != '}')
941 str++;
942
943 if (* str != '}')
944 {
945 goto err;
946 }
947
948 char * end = str;
949 char save = * end;
950 * end = 0;
951
952 ParamValue values[NDB_ARRAY_SIZE(m_params)];
953 values[IX_COUNT].unsigned_val = 1;
954 values[IX_REALTIME].unsigned_val = realtime;
955 values[IX_SPINTIME].unsigned_val = spintime;
956 int res = parseParams(start, values, m_err_msg);
957 * end = save;
958
959 if (res != 0)
960 {
961 return -1;
962 }
963
964 if (values[IX_CPUBOUND].found && values[IX_CPUSET].found)
965 {
966 m_err_msg.assfmt("Both cpuset and cpubind specified!");
967 return -1;
968 }
969
970 if (values[IX_SPINTIME].found &&
971 (type == T_IO || type == T_WD))
972 {
973 m_err_msg.assfmt("Cannot set spintime on IO threads and watchdog threads");
974 return -1;
975 }
976
977 unsigned cnt = values[IX_COUNT].unsigned_val;
978 const int index = m_threads[type].size();
979 for (unsigned i = 0; i < cnt; i++)
980 {
981 add(type,
982 values[IX_REALTIME].unsigned_val,
983 values[IX_SPINTIME].unsigned_val);
984 }
985
986 assert(m_threads[type].size() == index + cnt);
987 if (values[IX_CPUSET].found)
988 {
989 SparseBitmask & mask = values[IX_CPUSET].mask_val;
990 unsigned no = createCpuSet(mask);
991 for (unsigned i = 0; i < cnt; i++)
992 {
993 m_threads[type][index+i].m_bind_type = T_Thread::B_CPUSET_BOUND;
994 m_threads[type][index+i].m_bind_no = no;
995 }
996 }
997 else if (values[IX_CPUBOUND].found)
998 {
999 SparseBitmask & mask = values[IX_CPUBOUND].mask_val;
1000 if (mask.count() < cnt)
1001 {
1002 m_err_msg.assfmt("%s: trying to bind %u threads to %u cpus [%s]",
1003 getEntryName(type),
1004 cnt,
1005 mask.count(),
1006 mask.str().c_str());
1007 return -1;
1008 }
1009 for (unsigned i = 0; i < cnt; i++)
1010 {
1011 m_threads[type][index+i].m_bind_type = T_Thread::B_CPU_BOUND;
1012 m_threads[type][index+i].m_bind_no = mask.getBitNo(i % mask.count());
1013 }
1014 }
1015
1016 str++; // skip over }
1017 return 0;
1018 }
1019
1020 int
find_next(char * & str)1021 THRConfig::find_next(char *& str)
1022 {
1023 str = skipblank(str);
1024
1025 if (* str == 0)
1026 {
1027 return 0;
1028 }
1029 else if (* str == ',')
1030 {
1031 str++;
1032 return 1;
1033 }
1034
1035 int len = (int)strlen(str);
1036 m_err_msg.assfmt("Invalid format near: '%.*s'",
1037 (len > 10) ? 10 : len, str);
1038 return -1;
1039 }
1040
1041 int
do_parse(const char * ThreadConfig,unsigned realtime,unsigned spintime)1042 THRConfig::do_parse(const char * ThreadConfig,
1043 unsigned realtime,
1044 unsigned spintime)
1045 {
1046 BaseString str(ThreadConfig);
1047 char * ptr = (char*)str.c_str();
1048 while (* ptr)
1049 {
1050 Uint32 type = find_type(ptr);
1051 if (type == T_END)
1052 return -1;
1053
1054 if (find_spec(ptr, (T_Type)type, realtime, spintime) < 0)
1055 return -1;
1056
1057 int ret = find_next(ptr);
1058 if (ret < 0)
1059 return ret;
1060
1061 if (ret == 0)
1062 break;
1063 }
1064
1065 for (Uint32 i = 0; i < T_END; i++)
1066 {
1067 while (m_threads[i].size() < m_entries[i].m_min_cnt)
1068 add((T_Type)i, realtime, spintime);
1069 }
1070
1071 const bool allow_too_few_cpus =
1072 m_threads[T_TC].size() == 0 &&
1073 m_threads[T_SEND].size() == 0 &&
1074 m_threads[T_RECV].size() == 1;
1075
1076 int res = do_bindings(allow_too_few_cpus);
1077 if (res != 0)
1078 {
1079 return res;
1080 }
1081
1082 return do_validate();
1083 }
1084
1085 unsigned
createCpuSet(const SparseBitmask & mask)1086 THRConfig::createCpuSet(const SparseBitmask& mask)
1087 {
1088 for (unsigned i = 0; i < m_cpu_sets.size(); i++)
1089 if (m_cpu_sets[i].equal(mask))
1090 return i;
1091
1092 m_cpu_sets.push_back(mask);
1093 return m_cpu_sets.size() - 1;
1094 }
1095
1096 template class Vector<SparseBitmask>;
1097 template class Vector<THRConfig::T_Thread>;
1098
1099 #ifndef TEST_MT_THR_CONFIG
1100 #include <BlockNumbers.h>
1101 #include <NdbThread.h>
1102
1103 static
1104 int
findBlock(Uint32 blockNo,const unsigned short list[],unsigned cnt)1105 findBlock(Uint32 blockNo, const unsigned short list[], unsigned cnt)
1106 {
1107 for (Uint32 i = 0; i < cnt; i++)
1108 {
1109 if (blockToMain(list[i]) == blockNo)
1110 return blockToInstance(list[i]);
1111 }
1112 return -1;
1113 }
1114
1115 const THRConfig::T_Thread*
find_thread(const unsigned short instancelist[],unsigned cnt) const1116 THRConfigApplier::find_thread(const unsigned short instancelist[], unsigned cnt) const
1117 {
1118 int instanceNo;
1119 if ((instanceNo = findBlock(SUMA, instancelist, cnt)) >= 0)
1120 {
1121 return &m_threads[T_REP][instanceNo];
1122 }
1123 else if ((instanceNo = findBlock(DBDIH, instancelist, cnt)) >= 0)
1124 {
1125 return &m_threads[T_MAIN][instanceNo];
1126 }
1127 else if ((instanceNo = findBlock(DBTC, instancelist, cnt)) >= 0)
1128 {
1129 return &m_threads[T_TC][instanceNo - 1]; // remove proxy
1130 }
1131 else if ((instanceNo = findBlock(DBLQH, instancelist, cnt)) >= 0)
1132 {
1133 return &m_threads[T_LDM][instanceNo - 1]; // remove proxy...
1134 }
1135 else if ((instanceNo = findBlock(TRPMAN, instancelist, cnt)) >= 0)
1136 {
1137 return &m_threads[T_RECV][instanceNo - 1]; // remove proxy
1138 }
1139 return 0;
1140 }
1141
1142 void
appendInfo(BaseString & str,const unsigned short list[],unsigned cnt) const1143 THRConfigApplier::appendInfo(BaseString& str,
1144 const unsigned short list[], unsigned cnt) const
1145 {
1146 const T_Thread* thr = find_thread(list, cnt);
1147 appendInfo(str, thr);
1148 }
1149
1150 void
appendInfoSendThread(BaseString & str,unsigned instance_no) const1151 THRConfigApplier::appendInfoSendThread(BaseString& str,
1152 unsigned instance_no) const
1153 {
1154 const T_Thread* thr = &m_threads[T_SEND][instance_no];
1155 appendInfo(str, thr);
1156 }
1157
1158 void
appendInfo(BaseString & str,const T_Thread * thr) const1159 THRConfigApplier::appendInfo(BaseString& str,
1160 const T_Thread* thr) const
1161 {
1162 assert(thr != 0);
1163 str.appfmt("(%s) ", getEntryName(thr->m_type));
1164 if (thr->m_bind_type == T_Thread::B_CPU_BOUND)
1165 {
1166 str.appfmt("cpu: %u ", thr->m_bind_no);
1167 }
1168 else if (thr->m_bind_type == T_Thread::B_CPUSET_BOUND)
1169 {
1170 str.appfmt("cpuset: [ %s ] ", m_cpu_sets[thr->m_bind_no].str().c_str());
1171 }
1172 }
1173
1174 const char *
getName(const unsigned short list[],unsigned cnt) const1175 THRConfigApplier::getName(const unsigned short list[], unsigned cnt) const
1176 {
1177 const T_Thread* thr = find_thread(list, cnt);
1178 assert(thr != 0);
1179 return getEntryName(thr->m_type);
1180 }
1181
1182 int
do_bind(NdbThread * thread,const unsigned short list[],unsigned cnt)1183 THRConfigApplier::do_bind(NdbThread* thread,
1184 const unsigned short list[], unsigned cnt)
1185 {
1186 const T_Thread* thr = find_thread(list, cnt);
1187 return do_bind(thread, thr);
1188 }
1189
1190 int
do_bind_io(NdbThread * thread)1191 THRConfigApplier::do_bind_io(NdbThread* thread)
1192 {
1193 const T_Thread* thr = &m_threads[T_IO][0];
1194 return do_bind(thread, thr);
1195 }
1196
1197 int
do_bind_watchdog(NdbThread * thread)1198 THRConfigApplier::do_bind_watchdog(NdbThread* thread)
1199 {
1200 const T_Thread* thr = &m_threads[T_WD][0];
1201 return do_bind(thread, thr);
1202 }
1203
1204 int
do_bind_send(NdbThread * thread,unsigned instance)1205 THRConfigApplier::do_bind_send(NdbThread* thread, unsigned instance)
1206 {
1207 const T_Thread* thr = &m_threads[T_SEND][instance];
1208 return do_bind(thread, thr);
1209 }
1210
1211 bool
do_get_realtime(const unsigned short list[],unsigned cnt) const1212 THRConfigApplier::do_get_realtime(const unsigned short list[],
1213 unsigned cnt) const
1214 {
1215 const T_Thread* thr = find_thread(list, cnt);
1216 return (bool)thr->m_realtime;
1217 }
1218
1219 unsigned
do_get_spintime(const unsigned short list[],unsigned cnt) const1220 THRConfigApplier::do_get_spintime(const unsigned short list[],
1221 unsigned cnt) const
1222 {
1223 const T_Thread* thr = find_thread(list, cnt);
1224 return (bool)thr->m_spintime;
1225 }
1226
1227 bool
do_get_realtime_io() const1228 THRConfigApplier::do_get_realtime_io() const
1229 {
1230 const T_Thread* thr = &m_threads[T_IO][0];
1231 return (bool)thr->m_realtime;
1232 }
1233
1234 bool
do_get_realtime_wd() const1235 THRConfigApplier::do_get_realtime_wd() const
1236 {
1237 const T_Thread* thr = &m_threads[T_WD][0];
1238 return (bool)thr->m_realtime;
1239 }
1240
1241 bool
do_get_realtime_send(unsigned instance) const1242 THRConfigApplier::do_get_realtime_send(unsigned instance) const
1243 {
1244 const T_Thread* thr = &m_threads[T_SEND][instance];
1245 return (bool)thr->m_realtime;
1246 }
1247
1248 unsigned
do_get_spintime_send(unsigned instance) const1249 THRConfigApplier::do_get_spintime_send(unsigned instance) const
1250 {
1251 const T_Thread* thr = &m_threads[T_SEND][instance];
1252 return thr->m_spintime;
1253 }
1254
1255 int
do_bind(NdbThread * thread,const T_Thread * thr)1256 THRConfigApplier::do_bind(NdbThread* thread,
1257 const T_Thread* thr)
1258 {
1259 int res;
1260 if (thr->m_bind_type == T_Thread::B_CPU_BOUND)
1261 {
1262 res = Ndb_LockCPU(thread, thr->m_bind_no);
1263 }
1264 else if (thr->m_bind_type == T_Thread::B_CPUSET_BOUND)
1265 {
1266 SparseBitmask & tmp = m_cpu_sets[thr->m_bind_no];
1267 unsigned num_bits_set = tmp.count();
1268 Uint32 *cpu_ids = (Uint32*)malloc(sizeof(Uint32) * num_bits_set);
1269 Uint32 num_cpu_ids = 0;
1270 if (!cpu_ids)
1271 {
1272 return -errno;
1273 }
1274 for (unsigned i = 0; i < tmp.max_size(); i++)
1275 {
1276 if (tmp.get(i))
1277 {
1278 cpu_ids[num_cpu_ids] = i;
1279 num_cpu_ids++;
1280 }
1281 }
1282 require(num_cpu_ids == num_bits_set);
1283 res = Ndb_LockCPUSet(thread, cpu_ids, num_cpu_ids);
1284 free((void*)cpu_ids);
1285 }
1286 else
1287 {
1288 return 0;
1289 }
1290 if (res == 0)
1291 return 1;
1292 else
1293 return -res;
1294 }
1295 #endif
1296
1297 #ifdef TEST_MT_THR_CONFIG
1298
1299 #include <NdbTap.hpp>
1300
TAPTEST(mt_thr_config)1301 TAPTEST(mt_thr_config)
1302 {
1303 {
1304 THRConfig tmp;
1305 OK(tmp.do_parse(8, 0, 0, 0, 0) == 0);
1306 }
1307
1308 /**
1309 * BASIC test
1310 */
1311 {
1312 const char * ok[] =
1313 {
1314 "ldm,ldm",
1315 "ldm={count=3},ldm",
1316 "ldm={cpubind=1-2,5,count=3},ldm",
1317 "ldm={ cpubind = 1- 2, 5 , count = 3 },ldm",
1318 "ldm={count=3,cpubind=1-2,5 }, ldm",
1319 "ldm={cpuset=1-3,count=3,realtime=0,spintime=0 },ldm",
1320 "ldm={cpuset=1-3,count=3,realtime=1,spintime=0 },ldm",
1321 "ldm={cpuset=1-3,count=3,realtime=0,spintime=1 },ldm",
1322 "ldm={cpuset=1-3,count=3,realtime=1,spintime=1 },ldm",
1323 "io={cpuset=3,4,6}",
1324 "main,ldm={},ldm",
1325 "main,ldm={},ldm,tc",
1326 "main,ldm={},ldm,tc,tc",
1327 0
1328 };
1329
1330 const char * fail [] =
1331 {
1332 "ldm,ldm,ldm",
1333 "ldm={cpubind= 1 , cpuset=2 },ldm",
1334 "ldm={count=4,cpubind=1-3},ldm",
1335 "main,main,ldm,ldm",
1336 "main={ keso=88, count=23},ldm,ldm",
1337 "main={ cpuset=1-3 }, ldm={cpuset=3-4}",
1338 "main={ cpuset=1-3 }, ldm={cpubind=2}",
1339 "io={ spintime = 0 }",
1340 "tc,tc,tc={count=31}",
1341 0
1342 };
1343
1344 for (Uint32 i = 0; ok[i]; i++)
1345 {
1346 THRConfig tmp;
1347 int res = tmp.do_parse(ok[i], 0, 0);
1348 printf("do_parse(%s) => %s - %s\n", ok[i],
1349 res == 0 ? "OK" : "FAIL",
1350 res == 0 ? "" : tmp.getErrorMessage());
1351 OK(res == 0);
1352 {
1353 BaseString out(tmp.getConfigString());
1354 THRConfig check;
1355 OK(check.do_parse(out.c_str(), 0, 0) == 0);
1356 OK(strcmp(out.c_str(), check.getConfigString()) == 0);
1357 }
1358 }
1359
1360 for (Uint32 i = 0; fail[i]; i++)
1361 {
1362 THRConfig tmp;
1363 int res = tmp.do_parse(fail[i], 0, 0);
1364 printf("do_parse(%s) => %s - %s\n", fail[i],
1365 res == 0 ? "OK" : "FAIL",
1366 res == 0 ? "" : tmp.getErrorMessage());
1367 OK(res != 0);
1368 }
1369 }
1370
1371 {
1372 /**
1373 * Test interaction with LockExecuteThreadToCPU
1374 */
1375 const char * t[] =
1376 {
1377 /** threads, LockExecuteThreadToCPU, answer */
1378 "1-8",
1379 "ldm={count=4}",
1380 "OK",
1381 "main={cpubind=1},ldm={cpubind=2},ldm={cpubind=3},ldm={cpubind=4},ldm={cpubind=5},recv={cpubind=6},rep={cpubind=7}",
1382
1383 "1-5",
1384 "ldm={count=4}",
1385 "OK",
1386 "main={cpubind=5},ldm={cpubind=1},ldm={cpubind=2},ldm={cpubind=3},ldm={cpubind=4},recv={cpubind=5},rep={cpubind=5}",
1387
1388 "1-3",
1389 "ldm={count=4}",
1390 "OK",
1391 "main={cpubind=1},ldm={cpubind=2},ldm={cpubind=3},ldm={cpubind=2},ldm={cpubind=3},recv={cpubind=1},rep={cpubind=1}",
1392
1393 "1-4",
1394 "ldm={count=4}",
1395 "OK",
1396 "main={cpubind=1},ldm={cpubind=2},ldm={cpubind=3},ldm={cpubind=4},ldm={cpubind=2},recv={cpubind=1},rep={cpubind=1}",
1397
1398 "1-8",
1399 "ldm={count=4},io={cpubind=8}",
1400 "OK",
1401 "main={cpubind=1},ldm={cpubind=2},ldm={cpubind=3},ldm={cpubind=4},ldm={cpubind=5},recv={cpubind=6},rep={cpubind=7},io={cpubind=8}",
1402
1403 "1-8",
1404 "ldm={count=4,cpubind=1,4,5,6}",
1405 "OK",
1406 "main={cpubind=2},ldm={cpubind=1},ldm={cpubind=4},ldm={cpubind=5},ldm={cpubind=6},recv={cpubind=3},rep={cpubind=7}",
1407
1408 "1-9",
1409 "ldm={count=4,cpubind=1,4,5,6},tc,tc",
1410 "OK",
1411 "main={cpubind=2},ldm={cpubind=1},ldm={cpubind=4},ldm={cpubind=5},ldm={cpubind=6},recv={cpubind=3},rep={cpubind=7},tc={cpubind=8},tc={cpubind=9}",
1412
1413 "1-8",
1414 "ldm={count=4,cpubind=1,4,5,6},tc",
1415 "OK",
1416 "main={cpubind=2},ldm={cpubind=1},ldm={cpubind=4},ldm={cpubind=5},ldm={cpubind=6},recv={cpubind=3},rep={cpubind=7},tc={cpubind=8}",
1417
1418 "1-8",
1419 "ldm={count=4,cpubind=1,4,5,6},tc,tc",
1420 "FAIL",
1421 "Too few CPU's specifed with LockExecuteThreadToCPU. This is not supported when using multiple TC threads",
1422
1423 // END
1424 0
1425 };
1426
1427 for (unsigned i = 0; t[i]; i+= 4)
1428 {
1429 THRConfig tmp;
1430 tmp.setLockExecuteThreadToCPU(t[i+0]);
1431 const int _res = tmp.do_parse(t[i+1], 0, 0);
1432 const int expect_res = strcmp(t[i+2], "OK") == 0 ? 0 : -1;
1433 const int res = _res == expect_res ? 0 : -1;
1434 int ok = expect_res == 0 ?
1435 strcmp(tmp.getConfigString(), t[i+3]) == 0:
1436 strcmp(tmp.getErrorMessage(), t[i+3]) == 0;
1437 printf("mask: %s conf: %s => %s(%s) - %s - %s\n",
1438 t[i+0],
1439 t[i+1],
1440 _res == 0 ? "OK" : "FAIL",
1441 _res == 0 ? "" : tmp.getErrorMessage(),
1442 tmp.getConfigString(),
1443 ok == 1 ? "CORRECT" : "INCORRECT");
1444
1445 OK(res == 0);
1446 OK(ok == 1);
1447 }
1448 }
1449
1450 for (Uint32 i = 9; i < 48; i++)
1451 {
1452 Uint32 t,l,s,r;
1453 computeThreadConfig(i, t, l, s, r);
1454 printf("MaxNoOfExecutionThreads: %u lqh: %u tc: %u send: %u recv: %u main: 1 rep: 1 => sum: %u\n",
1455 i, l, t, s, r,
1456 2 + l + t + s + r);
1457 }
1458
1459 return 1;
1460 }
1461
1462 #endif
1463 #if 0
1464
1465 /**
1466 * This C-program was written by Mikael Ronstrom to
1467 * produce good distribution of threads, given MaxNoOfExecutionThreads
1468 *
1469 * Good is based on his experience experimenting/benchmarking
1470 */
1471 #include <stdio.h>
1472
1473 #define Uint32 unsigned int
1474 #define TC_THREAD_INDEX 0
1475 #define SEND_THREAD_INDEX 1
1476 #define RECV_THREAD_INDEX 2
1477 #define LQH_THREAD_INDEX 3
1478 #define MAIN_THREAD_INDEX 4
1479 #define REP_THREAD_INDEX 5
1480
1481 #define NUM_CHANGE_INDEXES 3
1482 #define NUM_INDEXES 6
1483
1484 static double mult_factor[NUM_CHANGE_INDEXES];
1485
1486 static void
1487 set_changeable_thread(Uint32 num_threads[NUM_INDEXES],
1488 double float_num_threads[NUM_CHANGE_INDEXES],
1489 Uint32 index)
1490 {
1491 num_threads[index] = (Uint32)(float_num_threads[index]);
1492 float_num_threads[index] -= num_threads[index];
1493 }
1494
1495 static Uint32
1496 calculate_total(Uint32 num_threads[NUM_INDEXES])
1497 {
1498 Uint32 total = 0;
1499 Uint32 i;
1500 for (i = 0; i < NUM_INDEXES; i++)
1501 {
1502 total += num_threads[i];
1503 }
1504 return total;
1505 }
1506
1507 static Uint32
1508 find_min_index(double float_num_threads[NUM_CHANGE_INDEXES])
1509 {
1510 Uint32 min_index = 0;
1511 Uint32 i;
1512 double min = float_num_threads[0];
1513
1514 for (i = 1; i < NUM_CHANGE_INDEXES; i++)
1515 {
1516 if (min > float_num_threads[i])
1517 {
1518 min = float_num_threads[i];
1519 min_index = i;
1520 }
1521 }
1522 return min_index;
1523 }
1524
1525 static Uint32
1526 find_max_index(double float_num_threads[NUM_CHANGE_INDEXES])
1527 {
1528 Uint32 max_index = 0;
1529 Uint32 i;
1530 double max = float_num_threads[0];
1531
1532 for (i = 1; i < NUM_CHANGE_INDEXES; i++)
1533 {
1534 if (max < float_num_threads[i])
1535 {
1536 max = float_num_threads[i];
1537 max_index = i;
1538 }
1539 }
1540 return max_index;
1541 }
1542
1543 static void
1544 add_thread(Uint32 num_threads[NUM_INDEXES],
1545 double float_num_threads[NUM_CHANGE_INDEXES])
1546 {
1547 Uint32 i;
1548 Uint32 max_index = find_max_index(float_num_threads);
1549 num_threads[max_index]++;
1550 float_num_threads[max_index] -= (double)1;
1551 for (i = 0; i < NUM_CHANGE_INDEXES; i++)
1552 float_num_threads[i] += mult_factor[i];
1553 }
1554
1555 static void
1556 remove_thread(Uint32 num_threads[NUM_INDEXES],
1557 double float_num_threads[NUM_CHANGE_INDEXES])
1558 {
1559 Uint32 i;
1560 Uint32 min_index = find_min_index(float_num_threads);
1561 num_threads[min_index]--;
1562 float_num_threads[min_index] += (double)1;
1563 for (i = 0; i < NUM_CHANGE_INDEXES; i++)
1564 float_num_threads[i] -= mult_factor[i];
1565 }
1566
1567 static void
1568 define_num_threads_per_type(Uint32 max_no_exec_threads,
1569 Uint32 num_threads[NUM_INDEXES])
1570 {
1571 Uint32 total_threads;
1572 Uint32 num_lqh_threads;
1573 Uint32 i;
1574 double float_num_threads[NUM_CHANGE_INDEXES];
1575
1576 /* Baseline to start calculations at */
1577 num_threads[MAIN_THREAD_INDEX] = 1; /* Fixed */
1578 num_threads[REP_THREAD_INDEX] = 1; /* Fixed */
1579 num_lqh_threads = (max_no_exec_threads / 4) * 2;
1580 if (num_lqh_threads > 32)
1581 num_lqh_threads = 32;
1582 switch (num_lqh_threads)
1583 {
1584 case 4:
1585 case 6:
1586 case 8:
1587 case 10:
1588 case 12:
1589 case 16:
1590 case 20:
1591 case 24:
1592 case 32:
1593 break;
1594 case 14:
1595 num_lqh_threads = 12;
1596 break;
1597 case 22:
1598 num_lqh_threads = 20;
1599 break;
1600 case 18:
1601 num_lqh_threads = 16;
1602 break;
1603 case 26:
1604 case 28:
1605 case 30:
1606 num_lqh_threads = 24;
1607 break;
1608 }
1609 num_threads[LQH_THREAD_INDEX] = num_lqh_threads;
1610
1611 /**
1612 * Rest of calculations are about calculating number of tc threads,
1613 * send threads and receive threads based on this input.
1614 * We do this by calculating a floating point number and using this to
1615 * select the next thread group to have one more added/removed.
1616 */
1617 mult_factor[TC_THREAD_INDEX] = 0.465;
1618 mult_factor[SEND_THREAD_INDEX] = 0.19;
1619 mult_factor[RECV_THREAD_INDEX] = 0.215;
1620 for (i = 0; i < NUM_CHANGE_INDEXES; i++)
1621 float_num_threads[i] = 0.5 + (mult_factor[i] * num_lqh_threads);
1622
1623 set_changeable_thread(num_threads, float_num_threads, TC_THREAD_INDEX);
1624 set_changeable_thread(num_threads, float_num_threads, SEND_THREAD_INDEX);
1625 set_changeable_thread(num_threads, float_num_threads, RECV_THREAD_INDEX);
1626
1627 total_threads = calculate_total(num_threads);
1628
1629 while (total_threads != max_no_exec_threads)
1630 {
1631 if (total_threads < max_no_exec_threads)
1632 add_thread(num_threads, float_num_threads);
1633 else
1634 remove_thread(num_threads, float_num_threads);
1635 total_threads = calculate_total(num_threads);
1636 }
1637 }
1638
1639 int main(int argc, char *argv)
1640 {
1641 Uint32 num_threads[NUM_INDEXES];
1642 Uint32 i;
1643
1644 printf("MaxNoOfExecutionThreads,LQH,TC,send,recv\n");
1645 for (i = 9; i <= 72; i++)
1646 {
1647 define_num_threads_per_type(i, num_threads);
1648 printf("{ %u, %u, %u, %u, %u },\n",
1649 i,
1650 num_threads[LQH_THREAD_INDEX],
1651 num_threads[TC_THREAD_INDEX],
1652 num_threads[SEND_THREAD_INDEX],
1653 num_threads[RECV_THREAD_INDEX]);
1654 }
1655 return 0;
1656 }
1657
1658 #endif
1659
1660 #define JAM_FILE_ID 297
1661
1662