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