1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21
22%%%-------------------------------------------------------------------
23%%% File    : scheduler_SUITE.erl
24%%% Author  : Rickard Green
25%%% Description :
26%%%
27%%% Created : 27 Oct 2008 by Rickard Green
28%%%-------------------------------------------------------------------
29-module(scheduler_SUITE).
30
31
32%-define(line_trace, 1).
33
34-include_lib("common_test/include/ct.hrl").
35
36%-compile(export_all).
37-export([all/0, suite/0, groups/0,
38         init_per_suite/1, end_per_suite/1,
39         init_per_testcase/2, end_per_testcase/2]).
40
41-export([equal/1,
42	 few_low/1,
43	 many_low/1,
44	 equal_with_part_time_high/1,
45	 equal_with_part_time_max/1,
46	 equal_and_high_with_part_time_max/1,
47	 equal_with_high/1,
48	 equal_with_high_max/1,
49	 bound_process/1,
50
51	 scheduler_bind_types/1,
52	 cpu_topology/1,
53	 update_cpu_info/1,
54	 sct_cmd/1,
55	 sbt_cmd/1,
56	 scheduler_threads/1,
57	 scheduler_suspend_basic/1,
58	 scheduler_suspend/1,
59	 dirty_scheduler_threads/1,
60         poll_threads/1,
61	 reader_groups/1,
62         otp_16446/1,
63         simultaneously_change_schedulers_online/1,
64         simultaneously_change_schedulers_online_with_exits/1]).
65
66suite() ->
67    [{ct_hooks,[ts_install_cth]},
68     {timetrap, {minutes, 15}}].
69
70all() ->
71    [equal, few_low, many_low, equal_with_part_time_high,
72     equal_with_part_time_max,
73     equal_and_high_with_part_time_max, equal_with_high,
74     equal_with_high_max,
75     bound_process,
76     {group, scheduler_bind}, scheduler_threads,
77     scheduler_suspend_basic, scheduler_suspend,
78     dirty_scheduler_threads,
79     poll_threads,
80     reader_groups,
81     otp_16446,
82     simultaneously_change_schedulers_online,
83     simultaneously_change_schedulers_online_with_exits].
84
85groups() ->
86    [{scheduler_bind, [],
87      [scheduler_bind_types, cpu_topology, update_cpu_info,
88       sct_cmd, sbt_cmd]}].
89
90init_per_suite(Config) ->
91    [{schedulers_online, erlang:system_info(schedulers_online)} | Config].
92
93end_per_suite(Config) ->
94    catch erts_debug:set_internal_state(available_internal_state, false),
95    SchedOnln = proplists:get_value(schedulers_online, Config),
96    erlang:system_flag(schedulers_online, SchedOnln),
97    Config.
98
99init_per_testcase(update_cpu_info, Config) ->
100    case os:find_executable("taskset") of
101	false ->
102	    {skip,"Could not find 'taskset' in path"};
103	_ ->
104	    init_per_tc(update_cpu_info, Config)
105    end;
106init_per_testcase(Case, Config) when is_list(Config) ->
107    init_per_tc(Case, Config).
108
109init_per_tc(Case, Config) ->
110    process_flag(priority, max),
111    erlang:display({'------------', ?MODULE, Case, '------------'}),
112    OkRes = ok,
113    [{testcase, Case}, {ok_res, OkRes} |Config].
114
115end_per_testcase(_Case, Config) when is_list(Config) ->
116    ok.
117
118-define(ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED, (2000*2000)).
119-define(DEFAULT_TEST_REDS_PER_SCHED, 200000000).
120
121%%
122%% Test cases
123%%
124
125equal(Config) when is_list(Config) ->
126    low_normal_test(Config, 500, 500).
127
128few_low(Config) when is_list(Config) ->
129    low_normal_test(Config, 1000, 2*active_schedulers()).
130
131many_low(Config) when is_list(Config) ->
132    low_normal_test(Config, 2*active_schedulers(), 1000).
133
134low_normal_test(Config, NW, LW) ->
135    Tracer = start_tracer(),
136    Low = workers(LW, low),
137    Normal = workers(NW, normal),
138    Res = do_it(Tracer, Low, Normal, [], []),
139    chk_result(Res, LW, NW, 0, 0, true, false, false),
140    workers_exit([Low, Normal]),
141    ok(Res, Config).
142
143equal_with_part_time_high(Config) when is_list(Config) ->
144    NW = 500,
145    LW = 500,
146    HW = 1,
147    Tracer = start_tracer(),
148    Normal = workers(NW, normal),
149    Low = workers(LW, low),
150    High = part_time_workers(HW, high),
151    Res = do_it(Tracer, Low, Normal, High, []),
152    chk_result(Res, LW, NW, HW, 0, true, true, false),
153    workers_exit([Low, Normal, High]),
154    ok(Res, Config).
155
156equal_and_high_with_part_time_max(Config) when is_list(Config) ->
157    NW = 500,
158    LW = 500,
159    HW = 500,
160    MW = 1,
161    Tracer = start_tracer(),
162    Low = workers(LW, low),
163    Normal = workers(NW, normal),
164    High = workers(HW, high),
165    Max = part_time_workers(MW, max),
166    Res = do_it(Tracer, Low, Normal, High, Max),
167    chk_result(Res, LW, NW, HW, MW, false, true, true),
168    workers_exit([Low, Normal, Max]),
169    ok(Res, Config).
170
171equal_with_part_time_max(Config) when is_list(Config) ->
172    NW = 500,
173    LW = 500,
174    MW = 1,
175    Tracer = start_tracer(),
176    Low = workers(LW, low),
177    Normal = workers(NW, normal),
178    Max = part_time_workers(MW, max),
179    Res = do_it(Tracer, Low, Normal, [], Max),
180    chk_result(Res, LW, NW, 0, MW, true, false, true),
181    workers_exit([Low, Normal, Max]),
182    ok(Res, Config).
183
184equal_with_high(Config) when is_list(Config) ->
185    NW = 500,
186    LW = 500,
187    HW = 1,
188    Tracer = start_tracer(),
189    Low = workers(LW, low),
190    Normal = workers(NW, normal),
191    High = workers(HW, high),
192    Res = do_it(Tracer, Low, Normal, High, []),
193    LNExe = case active_schedulers() of
194		      S when S =< HW -> false;
195		      _ -> true
196		  end,
197    chk_result(Res, LW, NW, HW, 0, LNExe, true, false),
198    workers_exit([Low, Normal, High]),
199    ok(Res, Config).
200
201equal_with_high_max(Config) when is_list(Config) ->
202    NW = 500,
203    LW = 500,
204    HW = 1,
205    MW = 1,
206    Tracer = start_tracer(),
207    Normal = workers(NW, normal),
208    Low = workers(LW, low),
209    High = workers(HW, high),
210    Max = workers(MW, max),
211    Res = do_it(Tracer, Low, Normal, High, Max),
212    {LNExe, HExe} = case active_schedulers() of
213			      S when S =< MW -> {false, false};
214			      S when S =< (MW + HW) -> {false, true};
215			      _ -> {true, true}
216			  end,
217    chk_result(Res, LW, NW, HW, MW, LNExe, HExe, true),
218    workers_exit([Low, Normal, Max]),
219    ok(Res, Config).
220
221bound_process(Config) when is_list(Config) ->
222    case erlang:system_info(run_queues) == erlang:system_info(schedulers) of
223        true ->
224            NStartBase = 20000,
225            NStart = case {erlang:system_info(debug_compiled),
226                           erlang:system_info(lock_checking)} of
227                         {true, true} -> NStartBase div 100;
228                         {_, true} -> NStartBase div 10;
229                         _ -> NStartBase
230                     end,
231            MStart = 100,
232            Seq = lists:seq(1, 100),
233            Tester = self(),
234            Procs = lists:map(
235                      fun (N) when N rem 2 == 0 ->
236                              spawn_opt(fun () ->
237                                                bound_loop(NStart,
238                                                           NStart,
239                                                           MStart,
240                                                           1),
241                                                Tester ! {self(), done}
242                                        end,
243                                        [{scheduler, 1}, link]);
244                          (_N) ->
245                              spawn_link(fun () ->
246                                                 bound_loop(NStart,
247                                                            NStart,
248                                                            MStart,
249                                                            false),
250                                                 Tester ! {self(), done}
251                                         end)
252                      end,
253                      Seq),
254            lists:foreach(fun (P) -> receive {P, done} -> ok end end,
255                          Procs),
256            ok;
257        false ->
258            {skipped, "Functionality not supported"}
259    end.
260
261bound_loop(_, 0, 0, _) ->
262    ok;
263bound_loop(NS, 0, M, false) ->
264    bound_loop(NS, NS, M-1, false);
265bound_loop(NS, N, M, false) ->
266    erlang:system_info(scheduler_id),
267    bound_loop(NS, N-1, M, false);
268bound_loop(NS, 0, M, Sched) ->
269    NewSched = (Sched rem erlang:system_info(schedulers_online)) + 1,
270    Sched = process_flag(scheduler, NewSched),
271    NewSched = erlang:system_info(scheduler_id),
272    bound_loop(NS, NS, M-1, NewSched);
273bound_loop(NS, N, M, Sched) ->
274    Sched = erlang:system_info(scheduler_id),
275    bound_loop(NS, N-1, M, Sched).
276
277
278-define(TOPOLOGY_A_CMD,
279	"+sct"
280	"L0-1t0-1c0p0n0"
281	":L2-3t0-1c1p0n0"
282	":L4-5t0-1c0p1n0"
283	":L6-7t0-1c1p1n0"
284	":L8-9t0-1c0p2n1"
285	":L10-11t0-1c1p2n1"
286	":L12-13t0-1c0p3n1"
287	":L14-15t0-1c1p3n1").
288
289-define(TOPOLOGY_A_TERM,
290	[{node,[{processor,[{core,[{thread,{logical,0}},
291				   {thread,{logical,1}}]},
292			    {core,[{thread,{logical,2}},
293				   {thread,{logical,3}}]}]},
294		{processor,[{core,[{thread,{logical,4}},
295				   {thread,{logical,5}}]},
296			    {core,[{thread,{logical,6}},
297				   {thread,{logical,7}}]}]}]},
298	 {node,[{processor,[{core,[{thread,{logical,8}},
299				   {thread,{logical,9}}]},
300			    {core,[{thread,{logical,10}},
301				   {thread,{logical,11}}]}]},
302		{processor,[{core,[{thread,{logical,12}},
303				   {thread,{logical,13}}]},
304			    {core,[{thread,{logical,14}},
305				   {thread,{logical,15}}]}]}]}]).
306
307-define(TOPOLOGY_B_CMD,
308	"+sct"
309	"L0-1t0-1c0n0p0"
310	":L2-3t0-1c1n0p0"
311	":L4-5t0-1c2n1p0"
312	":L6-7t0-1c3n1p0"
313	":L8-9t0-1c0n2p1"
314	":L10-11t0-1c1n2p1"
315	":L12-13t0-1c2n3p1"
316	":L14-15t0-1c3n3p1").
317
318-define(TOPOLOGY_B_TERM,
319	[{processor,[{node,[{core,[{thread,{logical,0}},
320				   {thread,{logical,1}}]},
321			    {core,[{thread,{logical,2}},
322				   {thread,{logical,3}}]}]},
323		     {node,[{core,[{thread,{logical,4}},
324				   {thread,{logical,5}}]},
325			    {core,[{thread,{logical,6}},
326				   {thread,{logical,7}}]}]}]},
327	 {processor,[{node,[{core,[{thread,{logical,8}},
328				   {thread,{logical,9}}]},
329			    {core,[{thread,{logical,10}},
330				   {thread,{logical,11}}]}]},
331		     {node,[{core,[{thread,{logical,12}},
332				   {thread,{logical,13}}]},
333			    {core,[{thread,{logical,14}},
334				   {thread,{logical,15}}]}]}]}]).
335
336-define(TOPOLOGY_C_TERM,
337	[{node,[{processor,[{core,[{thread,{logical,0}},
338				   {thread,{logical,1}}]},
339			    {core,[{thread,{logical,2}},
340				   {thread,{logical,3}}]}]},
341		{processor,[{core,[{thread,{logical,4}},
342				   {thread,{logical,5}}]},
343			    {core,[{thread,{logical,6}},
344				   {thread,{logical,7}}]}]}]},
345	 {processor,[{node,[{core,[{thread,{logical,8}},
346				   {thread,{logical,9}}]},
347			    {core,[{thread,{logical,10}},
348				   {thread,{logical,11}}]}]},
349		     {node,[{core,[{thread,{logical,12}},
350				   {thread,{logical,13}}]},
351			    {core,[{thread,{logical,14}},
352				   {thread,{logical,15}}]}]}]},
353	 {node,[{processor,[{core,[{thread,{logical,16}},
354				   {thread,{logical,17}}]},
355			    {core,[{thread,{logical,18}},
356				   {thread,{logical,19}}]}]},
357		{processor,[{core,[{thread,{logical,20}},
358				   {thread,{logical,21}}]},
359			    {core,[{thread,{logical,22}},
360				   {thread,{logical,23}}]}]}]},
361	 {processor,[{node,[{core,[{thread,{logical,24}},
362				   {thread,{logical,25}}]},
363			    {core,[{thread,{logical,26}},
364				   {thread,{logical,27}}]}]},
365		     {node,[{core,[{thread,{logical,28}},
366				   {thread,{logical,29}}]},
367			    {core,[{thread,{logical,30}},
368				   {thread,{logical,31}}]}]}]}]).
369
370
371-define(TOPOLOGY_C_CMD,
372	"+sct"
373	"L0-1t0-1c0p0n0"
374	":L2-3t0-1c1p0n0"
375	":L4-5t0-1c0p1n0"
376	":L6-7t0-1c1p1n0"
377	":L8-9t0-1c0n1p2"
378	":L10-11t0-1c1n1p2"
379	":L12-13t0-1c2n2p2"
380	":L14-15t0-1c3n2p2"
381	":L16-17t0-1c0p3n3"
382	":L18-19t0-1c1p3n3"
383	":L20-21t0-1c0p4n3"
384	":L22-23t0-1c1p4n3"
385	":L24-25t0-1c0n4p5"
386	":L26-27t0-1c1n4p5"
387	":L28-29t0-1c2n5p5"
388	":L30-31t0-1c3n5p5").
389
390-define(TOPOLOGY_D_TERM,
391	[{processor,[{node,[{core,[{thread,{logical,0}},
392				   {thread,{logical,1}}]},
393			    {core,[{thread,{logical,2}},
394				   {thread,{logical,3}}]}]},
395		     {node,[{core,[{thread,{logical,4}},
396				   {thread,{logical,5}}]},
397			    {core,[{thread,{logical,6}},
398				   {thread,{logical,7}}]}]}]},
399	 {node,[{processor,[{core,[{thread,{logical,8}},
400				   {thread,{logical,9}}]},
401			    {core,[{thread,{logical,10}},
402				   {thread,{logical,11}}]}]},
403		{processor,[{core,[{thread,{logical,12}},
404				   {thread,{logical,13}}]},
405			    {core,[{thread,{logical,14}},
406				   {thread,{logical,15}}]}]}]},
407	 {processor,[{node,[{core,[{thread,{logical,16}},
408				   {thread,{logical,17}}]},
409			    {core,[{thread,{logical,18}},
410				   {thread,{logical,19}}]}]},
411		     {node,[{core,[{thread,{logical,20}},
412				   {thread,{logical,21}}]},
413			    {core,[{thread,{logical,22}},
414				   {thread,{logical,23}}]}]}]},
415	 {node,[{processor,[{core,[{thread,{logical,24}},
416				   {thread,{logical,25}}]},
417			    {core,[{thread,{logical,26}},
418				   {thread,{logical,27}}]}]},
419		{processor,[{core,[{thread,{logical,28}},
420				   {thread,{logical,29}}]},
421			    {core,[{thread,{logical,30}},
422				   {thread,{logical,31}}]}]}]}]).
423
424-define(TOPOLOGY_D_CMD,
425	"+sct"
426	"L0-1t0-1c0n0p0"
427	":L2-3t0-1c1n0p0"
428	":L4-5t0-1c2n1p0"
429	":L6-7t0-1c3n1p0"
430	":L8-9t0-1c0p1n2"
431	":L10-11t0-1c1p1n2"
432	":L12-13t0-1c0p2n2"
433	":L14-15t0-1c1p2n2"
434	":L16-17t0-1c0n3p3"
435	":L18-19t0-1c1n3p3"
436	":L20-21t0-1c2n4p3"
437	":L22-23t0-1c3n4p3"
438	":L24-25t0-1c0p4n5"
439	":L26-27t0-1c1p4n5"
440	":L28-29t0-1c0p5n5"
441	":L30-31t0-1c1p5n5").
442
443-define(TOPOLOGY_E_CMD,
444	"+sct"
445	"L0-1t0-1c0p0n0"
446	":L2-3t0-1c1p0n0"
447	":L4-5t0-1c2p0n0"
448	":L6-7t0-1c3p0n0"
449	":L8-9t0-1c0p1n1"
450	":L10-11t0-1c1p1n1"
451	":L12-13t0-1c2p1n1"
452	":L14-15t0-1c3p1n1").
453
454-define(TOPOLOGY_E_TERM,
455	[{node,[{processor,[{core,[{thread,{logical,0}},
456				   {thread,{logical,1}}]},
457			    {core,[{thread,{logical,2}},
458				   {thread,{logical,3}}]},
459			    {core,[{thread,{logical,4}},
460				   {thread,{logical,5}}]},
461			    {core,[{thread,{logical,6}},
462				   {thread,{logical,7}}]}]}]},
463	 {node,[{processor,[{core,[{thread,{logical,8}},
464				   {thread,{logical,9}}]},
465			    {core,[{thread,{logical,10}},
466				   {thread,{logical,11}}]},
467			    {core,[{thread,{logical,12}},
468				   {thread,{logical,13}}]},
469			    {core,[{thread,{logical,14}},
470				   {thread,{logical,15}}]}]}]}]).
471
472-define(TOPOLOGY_F_CMD,
473	"+sct"
474	"L0-1t0-1c0n0p0"
475	":L2-3t0-1c1n0p0"
476	":L4-5t0-1c2n0p0"
477	":L6-7t0-1c3n0p0"
478	":L8-9t0-1c4n1p0"
479	":L10-11t0-1c5n1p0"
480	":L12-13t0-1c6n1p0"
481	":L14-15t0-1c7n1p0"
482	":L16-17t0-1c8n2p0"
483	":L18-19t0-1c9n2p0"
484	":L20-21t0-1c10n2p0"
485	":L22-23t0-1c11n2p0"
486	":L24-25t0-1c12n3p0"
487	":L26-27t0-1c13n3p0"
488	":L28-29t0-1c14n3p0"
489	":L30-31t0-1c15n3p0").
490
491-define(TOPOLOGY_F_TERM,
492        [{processor,[{node,[{core,[{thread,{logical,0}},
493                                   {thread,{logical,1}}]},
494                            {core,[{thread,{logical,2}},
495                                   {thread,{logical,3}}]},
496                            {core,[{thread,{logical,4}},
497                                   {thread,{logical,5}}]},
498                            {core,[{thread,{logical,6}},
499                                   {thread,{logical,7}}]}]},
500                     {node,[{core,[{thread,{logical,8}},
501                                   {thread,{logical,9}}]},
502                            {core,[{thread,{logical,10}},
503                                   {thread,{logical,11}}]},
504                            {core,[{thread,{logical,12}},
505                                   {thread,{logical,13}}]},
506                            {core,[{thread,{logical,14}},
507                                   {thread,{logical,15}}]}]},
508                     {node,[{core,[{thread,{logical,16}},
509                                   {thread,{logical,17}}]},
510                            {core,[{thread,{logical,18}},
511                                   {thread,{logical,19}}]},
512                            {core,[{thread,{logical,20}},
513                                   {thread,{logical,21}}]},
514                            {core,[{thread,{logical,22}},
515                                   {thread,{logical,23}}]}]},
516                     {node,[{core,[{thread,{logical,24}},
517                                   {thread,{logical,25}}]},
518                            {core,[{thread,{logical,26}},
519                                   {thread,{logical,27}}]},
520                            {core,[{thread,{logical,28}},
521                                   {thread,{logical,29}}]},
522                            {core,[{thread,{logical,30}},
523                                   {thread,{logical,31}}]}]}]}]).
524
525bindings(Node, BindType) ->
526    Parent = self(),
527    Ref = make_ref(),
528    Pid = spawn_link(Node,
529                     fun () ->
530                             enable_internal_state(),
531                             Res = (catch erts_debug:get_internal_state(
532                                            {fake_scheduler_bindings,
533                                             BindType})),
534                             Parent ! {Ref, Res}
535                     end),
536    receive
537        {Ref, Res} ->
538            io:format("~p: ~p~n", [BindType, Res]),
539            unlink(Pid),
540            Res
541    end.
542
543scheduler_bind_types(Config) when is_list(Config) ->
544    OldRelFlags = clear_erl_rel_flags(),
545    try
546	scheduler_bind_types_test(Config,
547				  ?TOPOLOGY_A_TERM,
548				  ?TOPOLOGY_A_CMD,
549				  a),
550	scheduler_bind_types_test(Config,
551				  ?TOPOLOGY_B_TERM,
552				  ?TOPOLOGY_B_CMD,
553				  b),
554	scheduler_bind_types_test(Config,
555				  ?TOPOLOGY_C_TERM,
556				  ?TOPOLOGY_C_CMD,
557				  c),
558	scheduler_bind_types_test(Config,
559				  ?TOPOLOGY_D_TERM,
560				  ?TOPOLOGY_D_CMD,
561				  d),
562	scheduler_bind_types_test(Config,
563				  ?TOPOLOGY_E_TERM,
564				  ?TOPOLOGY_E_CMD,
565				  e),
566	scheduler_bind_types_test(Config,
567				  ?TOPOLOGY_F_TERM,
568				  ?TOPOLOGY_F_CMD,
569				  f)
570    after
571	restore_erl_rel_flags(OldRelFlags)
572    end,
573    ok.
574
575scheduler_bind_types_test(Config, Topology, CmdLine, TermLetter) ->
576    io:format("Testing (~p): ~p~n", [TermLetter, Topology]),
577    {ok, Node0} = start_node(Config),
578    _ = rpc:call(Node0, erlang, system_flag, [cpu_topology, Topology]),
579    cmp(Topology, rpc:call(Node0, erlang, system_info, [cpu_topology])),
580    check_bind_types(Node0, TermLetter),
581    stop_node(Node0),
582    {ok, Node1} = start_node(Config, CmdLine),
583    cmp(Topology, rpc:call(Node1, erlang, system_info, [cpu_topology])),
584    check_bind_types(Node1, TermLetter),
585    stop_node(Node1).
586
587check_bind_types(Node, a) ->
588    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
589    = bindings(Node, no_spread),
590    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
591    = bindings(Node, thread_spread),
592    {0,4,8,12,2,6,10,14,1,5,9,13,3,7,11,15}
593    = bindings(Node, processor_spread),
594    {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}
595    = bindings(Node, spread),
596    {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
597    = bindings(Node, no_node_thread_spread),
598    {0,4,2,6,1,5,3,7,8,12,10,14,9,13,11,15}
599    = bindings(Node, no_node_processor_spread),
600    {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15}
601    = bindings(Node, thread_no_node_processor_spread),
602    {0,4,2,6,8,12,10,14,1,5,3,7,9,13,11,15}
603    = bindings(Node, default_bind),
604    ok;
605check_bind_types(Node, b) ->
606    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
607    = bindings(Node, no_spread),
608    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
609    = bindings(Node, thread_spread),
610    {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
611    = bindings(Node, processor_spread),
612    {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}
613    = bindings(Node, spread),
614    {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15}
615    = bindings(Node, no_node_thread_spread),
616    {0,2,1,3,4,6,5,7,8,10,9,11,12,14,13,15}
617    = bindings(Node, no_node_processor_spread),
618    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
619    = bindings(Node, thread_no_node_processor_spread),
620    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
621    = bindings(Node, default_bind),
622    ok;
623check_bind_types(Node, c) ->
624    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
625     25,26,27,28,29,30,31} = bindings(Node, no_spread),
626    {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
627     17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
628    {0,4,8,16,20,24,2,6,10,18,22,26,12,28,14,30,1,5,9,17,21,25,
629     3,7,11,19,23,27,13,29,15,31} = bindings(Node, processor_spread),
630    {0,8,16,24,4,20,12,28,2,10,18,26,6,22,14,30,1,9,17,25,5,21,13,29,3,11,
631     19,27,7,23,15,31} = bindings(Node, spread),
632    {0,2,4,6,1,3,5,7,8,10,9,11,12,14,13,15,16,18,20,22,17,19,21,23,24,26,
633     25,27,28,30,29,31} = bindings(Node, no_node_thread_spread),
634    {0,4,2,6,1,5,3,7,8,10,9,11,12,14,13,15,16,20,18,22,17,21,19,23,24,26,
635     25,27,28,30,29,31} = bindings(Node, no_node_processor_spread),
636    {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21,
637     19,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread),
638    {0,4,2,6,8,10,12,14,16,20,18,22,24,26,28,30,1,5,3,7,9,11,13,15,17,21,
639     19,23,25,27,29,31} = bindings(Node, default_bind),
640    ok;
641check_bind_types(Node, d) ->
642    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
643     25,26,27,28,29,30,31} = bindings(Node, no_spread),
644    {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
645     17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
646    {0,8,12,16,24,28,2,10,14,18,26,30,4,20,6,22,1,9,13,17,25,29,3,11,15,
647     19,27,31,5,21,7,23} = bindings(Node, processor_spread),
648    {0,8,16,24,12,28,4,20,2,10,18,26,14,30,6,22,1,9,17,25,13,29,5,21,3,11,
649     19,27,15,31,7,23} = bindings(Node, spread),
650    {0,2,1,3,4,6,5,7,8,10,12,14,9,11,13,15,16,18,17,19,20,22,21,23,24,26,
651     28,30,25,27,29,31} = bindings(Node, no_node_thread_spread),
652    {0,2,1,3,4,6,5,7,8,12,10,14,9,13,11,15,16,18,17,19,20,22,21,23,24,28,
653     26,30,25,29,27,31} = bindings(Node, no_node_processor_spread),
654    {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19,
655     21,23,25,29,27,31} = bindings(Node, thread_no_node_processor_spread),
656    {0,2,4,6,8,12,10,14,16,18,20,22,24,28,26,30,1,3,5,7,9,13,11,15,17,19,
657     21,23,25,29,27,31} = bindings(Node, default_bind),
658    ok;
659check_bind_types(Node, e) ->
660    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}
661    = bindings(Node, no_spread),
662    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
663    = bindings(Node, thread_spread),
664    {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
665    = bindings(Node, processor_spread),
666    {0,8,2,10,4,12,6,14,1,9,3,11,5,13,7,15}
667    = bindings(Node, spread),
668    {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
669    = bindings(Node, no_node_thread_spread),
670    {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15}
671    = bindings(Node, no_node_processor_spread),
672    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
673    = bindings(Node, thread_no_node_processor_spread),
674    {0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,15}
675    = bindings(Node, default_bind),
676    ok;
677check_bind_types(Node, f) ->
678    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
679     25,26,27,28,29,30,31} = bindings(Node, no_spread),
680    {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,
681     17,19,21,23,25,27,29,31} = bindings(Node, thread_spread),
682    {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,
683     15,17,19,21,23,25,27,29,31} = bindings(Node, processor_spread),
684    {0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30,1,9,17,25,3,11,19,27,5,13,
685     21,29,7,15,23,31} = bindings(Node, spread),
686    {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26,
687     28,30,25,27,29,31} = bindings(Node, no_node_thread_spread),
688    {0,2,4,6,1,3,5,7,8,10,12,14,9,11,13,15,16,18,20,22,17,19,21,23,24,26,
689     28,30,25,27,29,31} = bindings(Node, no_node_processor_spread),
690    {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19,
691     21,23,25,27,29,31} = bindings(Node, thread_no_node_processor_spread),
692    {0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,1,3,5,7,9,11,13,15,17,19,
693     21,23,25,27,29,31} = bindings(Node, default_bind),
694    ok;
695check_bind_types(Node, _) ->
696    bindings(Node, no_spread),
697    bindings(Node, thread_spread),
698    bindings(Node, processor_spread),
699    bindings(Node, spread),
700    bindings(Node, no_node_thread_spread),
701    bindings(Node, no_node_processor_spread),
702    bindings(Node, thread_no_node_processor_spread),
703    bindings(Node, default_bind),
704    ok.
705
706cpu_topology(Config) when is_list(Config) ->
707    OldRelFlags = clear_erl_rel_flags(),
708    try
709        cpu_topology_test(
710          Config,
711          [{node,[{processor,[{core,{logical,0}},
712                              {core,{logical,1}}]}]},
713           {processor,[{node,[{core,{logical,2}},
714                              {core,{logical,3}}]}]},
715           {node,[{processor,[{core,{logical,4}},
716                              {core,{logical,5}}]}]},
717           {processor,[{node,[{core,{logical,6}},
718                              {core,{logical,7}}]}]}],
719          "+sct "
720          "L0-1c0-1p0n0"
721          ":L2-3c0-1n1p1"
722          ":L4-5c0-1p2n2"
723          ":L6-7c0-1n3p3"),
724        cpu_topology_test(
725          Config,
726          [{node,[{processor,[{core,{logical,0}},
727                              {core,{logical,1}}]},
728                  {processor,[{core,{logical,2}},
729                              {core,{logical,3}}]}]},
730           {processor,[{node,[{core,{logical,4}},
731                              {core,{logical,5}}]},
732                       {node,[{core,{logical,6}},
733                              {core,{logical,7}}]}]},
734           {node,[{processor,[{core,{logical,8}},
735                              {core,{logical,9}}]},
736                  {processor,[{core,{logical,10}},
737                              {core,{logical,11}}]}]},
738           {processor,[{node,[{core,{logical,12}},
739                              {core,{logical,13}}]},
740                       {node,[{core,{logical,14}},
741                              {core,{logical,15}}]}]}],
742          "+sct "
743          "L0-1c0-1p0n0"
744          ":L2-3c0-1p1n0"
745          ":L4-5c0-1n1p2"
746          ":L6-7c2-3n2p2"
747          ":L8-9c0-1p3n3"
748          ":L10-11c0-1p4n3"
749          ":L12-13c0-1n4p5"
750          ":L14-15c2-3n5p5"),
751        cpu_topology_test(
752          Config,
753          [{node,[{processor,[{core,{logical,0}},
754                              {core,{logical,1}}]}]},
755           {processor,[{node,[{core,{logical,2}},
756                              {core,{logical,3}}]}]},
757           {processor,[{node,[{core,{logical,4}},
758                              {core,{logical,5}}]}]},
759           {node,[{processor,[{core,{logical,6}},
760                              {core,{logical,7}}]}]},
761           {node,[{processor,[{core,{logical,8}},
762                              {core,{logical,9}}]}]},
763           {processor,[{node,[{core,{logical,10}},
764                              {core,{logical,11}}]}]}],
765          "+sct "
766          "L0-1c0-1p0n0"
767          ":L2-3c0-1n1p1"
768          ":L4-5c0-1n2p2"
769          ":L6-7c0-1p3n3"
770          ":L8-9c0-1p4n4"
771          ":L10-11c0-1n5p5")
772    after
773        restore_erl_rel_flags(OldRelFlags)
774    end,
775    ok.
776
777cpu_topology_test(Config, Topology, Cmd) ->
778    io:format("Testing~n ~p~n ~p~n", [Topology, Cmd]),
779    cpu_topology_bif_test(Config, Topology),
780    cpu_topology_cmdline_test(Config, Topology, Cmd),
781    ok.
782
783cpu_topology_bif_test(_Config, false) ->
784    ok;
785cpu_topology_bif_test(Config, Topology) ->
786    {ok, Node} = start_node(Config),
787    _ = rpc:call(Node, erlang, system_flag, [cpu_topology, Topology]),
788    cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])),
789    stop_node(Node),
790    ok.
791
792cpu_topology_cmdline_test(_Config, _Topology, false) ->
793    ok;
794cpu_topology_cmdline_test(Config, Topology, Cmd) ->
795    {ok, Node} = start_node(Config, Cmd),
796    cmp(Topology, rpc:call(Node, erlang, system_info, [cpu_topology])),
797    stop_node(Node),
798    ok.
799
800update_cpu_info(Config) when is_list(Config) ->
801    OldOnline = erlang:system_info(schedulers_online),
802    OldAff = get_affinity_mask(),
803    io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
804		    [OldAff, OldOnline, erlang:system_info(scheduler_bindings)]),
805    case {erlang:system_info(logical_processors_available), OldAff} of
806	      {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 ->
807		  %% Nothing much to test; just a smoke test
808		  case erlang:system_info(update_cpu_info) of
809		      unchanged -> ok;
810		      changed -> ok
811		  end;
812	      {_Avail, _} ->
813		  try
814		      adjust_schedulers_online(),
815		      case erlang:system_info(schedulers_online) of
816			  1 ->
817			      %% Nothing much to test; just a smoke test
818			      ok;
819			  Onln0 ->
820			      Cpus = bits_in_mask(OldAff),
821			      RmCpus = case Cpus > Onln0 of
822					   true -> Cpus - Onln0 + 1;
823					   false -> Onln0 - Cpus + 1
824				       end,
825			      Onln1 = Cpus - RmCpus,
826			      case Onln1 > 0 of
827				  false ->
828				      %% Nothing much to test; just a smoke test
829				      ok;
830				  true ->
831				      Aff = restrict_affinity_mask(OldAff, RmCpus),
832				      set_affinity_mask(Aff),
833				      case adjust_schedulers_online() of
834					  {Onln0, Onln1} ->
835					      Onln1 = erlang:system_info(schedulers_online),
836					      receive after 500 -> ok end,
837					      io:format("TEST - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
838							[Aff, Onln1, erlang:system_info(scheduler_bindings)]),
839					      unchanged = adjust_schedulers_online(),
840					      ok;
841					  Fail ->
842					      ct:fail(Fail)
843				      end
844			      end
845		      end
846		  after
847		      set_affinity_mask(OldAff),
848		      adjust_schedulers_online(),
849		      erlang:system_flag(schedulers_online, OldOnline),
850		      receive after 500 -> ok end,
851		      io:format("END - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n",
852				[get_affinity_mask(),
853				 erlang:system_info(schedulers_online),
854				 erlang:system_info(scheduler_bindings)])
855		  end
856	  end.
857
858bits_in_mask(Mask) ->
859    bits_in_mask(Mask, 0, 0).
860
861bits_in_mask(0, _Shift, N) ->
862    N;
863bits_in_mask(Mask, Shift, N) ->
864    case Mask band (1 bsl Shift) of
865	0 -> bits_in_mask(Mask, Shift+1, N);
866	_ -> bits_in_mask(Mask band (bnot (1 bsl Shift)),
867			  Shift+1, N+1)
868    end.
869
870restrict_affinity_mask(Mask, N) ->
871    try
872	restrict_affinity_mask(Mask, 0, N)
873    catch
874	throw : Reason ->
875	    exit({Reason, Mask, N})
876    end.
877
878restrict_affinity_mask(Mask, _Shift, 0) ->
879    Mask;
880restrict_affinity_mask(0, _Shift, _N) ->
881    throw(overresticted_affinity_mask);
882restrict_affinity_mask(Mask, Shift, N) ->
883    case Mask band (1 bsl Shift) of
884	0 -> restrict_affinity_mask(Mask, Shift+1, N);
885	_ -> restrict_affinity_mask(Mask band (bnot (1 bsl Shift)),
886				    Shift+1, N-1)
887    end.
888
889adjust_schedulers_online() ->
890    case erlang:system_info(update_cpu_info) of
891	unchanged ->
892	    unchanged;
893	changed ->
894	    Avail = erlang:system_info(logical_processors_available),
895	    Scheds = erlang:system_info(schedulers),
896	    SOnln = case Avail > Scheds of
897	    	    	 true -> Scheds;
898			 false -> Avail
899		    end,
900	    {erlang:system_flag(schedulers_online, SOnln), SOnln}
901    end.
902
903read_affinity(Data) ->
904    Exp = "pid " ++ os:getpid() ++ "'s current affinity mask",
905    case string:lexemes(Data, ":") of
906	[Exp, DirtyAffinityStr] ->
907	    AffinityStr = string:trim(DirtyAffinityStr),
908	    case catch erlang:list_to_integer(AffinityStr, 16) of
909		Affinity when is_integer(Affinity) ->
910		    Affinity;
911		_ ->
912		    bad
913	    end;
914	_ ->
915	    bad
916    end.
917
918get_affinity_mask(Port, Status, Affinity) when Status == unknown;
919					       Affinity == unknown ->
920    receive
921	{Port,{data, Data}} ->
922	    get_affinity_mask(Port, Status, read_affinity(Data));
923	{Port,{exit_status,S}} ->
924	    get_affinity_mask(Port, S, Affinity)
925    end;
926get_affinity_mask(_Port, _Status, bad) ->
927    unknown;
928get_affinity_mask(_Port, _Status, Affinity) ->
929    Affinity.
930
931get_affinity_mask() ->
932    case os:type() of
933	{unix, linux} ->
934	    case catch open_port({spawn, "taskset -p " ++ os:getpid()},
935				 [exit_status]) of
936		Port when is_port(Port) ->
937		    get_affinity_mask(Port, unknown, unknown);
938		_ ->
939		    unknown
940	    end;
941	_ ->
942	    unknown
943    end.
944
945set_affinity_mask(Port, unknown) ->
946    receive
947	{Port,{data, _}} ->
948	    set_affinity_mask(Port, unknown);
949	{Port,{exit_status,Status}} ->
950	    set_affinity_mask(Port, Status)
951    end;
952set_affinity_mask(Port, Status) ->
953    receive
954	{Port,{data, _}} ->
955	    set_affinity_mask(Port, unknown)
956    after 0 ->
957	    Status
958    end.
959
960set_affinity_mask(Mask) ->
961    Cmd = lists:flatten(["taskset -p ",
962			 io_lib:format("~.16b", [Mask]),
963			 " ",
964			 os:getpid()]),
965    case catch open_port({spawn, Cmd}, [exit_status]) of
966	Port when is_port(Port) ->
967	    case set_affinity_mask(Port, unknown) of
968		0 -> ok;
969		_ -> exit(failed_to_set_affinity)
970	    end;
971	_ ->
972	    exit(failed_to_set_affinity)
973    end.
974
975sct_cmd(Config) when is_list(Config) ->
976    Topology = ?TOPOLOGY_A_TERM,
977    OldRelFlags = clear_erl_rel_flags(),
978    try
979	{ok, Node} = start_node(Config, ?TOPOLOGY_A_CMD),
980	cmp(Topology,
981		  rpc:call(Node, erlang, system_info, [cpu_topology])),
982	cmp(Topology,
983		  rpc:call(Node, erlang, system_flag, [cpu_topology, Topology])),
984	cmp(Topology,
985		  rpc:call(Node, erlang, system_info, [cpu_topology])),
986	stop_node(Node)
987    after
988	restore_erl_rel_flags(OldRelFlags)
989    end,
990    ok.
991
992-define(BIND_TYPES,
993	[{"u", unbound},
994	 {"ns", no_spread},
995	 {"ts", thread_spread},
996	 {"ps", processor_spread},
997	 {"s", spread},
998	 {"nnts", no_node_thread_spread},
999	 {"nnps", no_node_processor_spread},
1000	 {"tnnps", thread_no_node_processor_spread},
1001	 {"db", thread_no_node_processor_spread}]).
1002
1003sbt_cmd(Config) when is_list(Config) ->
1004    case sbt_check_prereqs() of
1005        {skipped, _Reason}=Skipped ->
1006            Skipped;
1007        ok ->
1008            case sbt_make_topology_args() of
1009                false ->
1010                    {skipped, "Don't know how to create cpu topology"};
1011                CpuTCmd ->
1012                    LP = erlang:system_info(logical_processors),
1013                    OldRelFlags = clear_erl_rel_flags(),
1014                    try
1015                        lists:foreach(fun ({ClBt, Bt}) ->
1016                                              sbt_test(Config, CpuTCmd,
1017                                                       ClBt, Bt, LP)
1018                                      end,
1019                                      ?BIND_TYPES)
1020                    after
1021                        restore_erl_rel_flags(OldRelFlags)
1022                    end,
1023                    ok
1024            end
1025    end.
1026
1027sbt_make_topology_args() ->
1028    case erlang:system_info({cpu_topology,detected}) of
1029        undefined ->
1030            case os:type() of
1031                linux ->
1032                    case erlang:system_info(logical_processors) of
1033                        1 ->
1034                            "+sctL0";
1035                        N ->
1036                            NS = integer_to_list(N - 1),
1037                            "+sctL0-"++NS++"p0-"++NS
1038                    end;
1039                _ ->
1040                    false
1041            end;
1042        _ ->
1043            ""
1044    end.
1045
1046sbt_check_prereqs() ->
1047    try
1048        Available = erlang:system_info(logical_processors_available),
1049        Quota = erlang:system_info(cpu_quota),
1050        if
1051            Quota =:= unknown; Quota >= Available ->
1052                ok;
1053            Quota < Available ->
1054                throw({skipped, "Test requires that CPU quota is greater than "
1055                                "the number of available processors."})
1056        end,
1057
1058        try
1059            OldVal = erlang:system_flag(scheduler_bind_type, default_bind),
1060            erlang:system_flag(scheduler_bind_type, OldVal)
1061        catch
1062            error:notsup ->
1063                throw({skipped, "Scheduler binding not supported."});
1064            error:_ ->
1065                %% ?!
1066                ok
1067        end,
1068
1069        case erlang:system_info(logical_processors) of
1070            Count when is_integer(Count) ->
1071                ok;
1072            unknown ->
1073                throw({skipped, "Can't detect number of logical processors."})
1074        end,
1075
1076        ok
1077    catch
1078        throw:{skip,_Reason}=Skip -> Skip
1079    end.
1080
1081sbt_test(Config, CpuTCmd, ClBt, Bt, LP) ->
1082    io:format("Testing +sbt ~s (~p)~n", [ClBt, Bt]),
1083    LPS = integer_to_list(LP),
1084    Cmd = CpuTCmd++" +sbt "++ClBt++" +S"++LPS++":"++LPS,
1085    {ok, Node} = start_node(Config, Cmd),
1086    Bt = rpc:call(Node,
1087			erlang,
1088			system_info,
1089			[scheduler_bind_type]),
1090    SB = rpc:call(Node,
1091			erlang,
1092			system_info,
1093			[scheduler_bindings]),
1094    io:format("scheduler bindings: ~p~n", [SB]),
1095    BS = case {Bt, erlang:system_info(logical_processors_available)} of
1096		   {unbound, _} -> 0;
1097		   {_, Int} when is_integer(Int) -> Int;
1098		   {_, _} -> LP
1099		end,
1100    lists:foldl(fun (S, 0) ->
1101			      unbound = S,
1102			      0;
1103			  (S, N) ->
1104			      true = is_integer(S),
1105			      N-1
1106		      end,
1107		      BS,
1108		      tuple_to_list(SB)),
1109    stop_node(Node),
1110    ok.
1111
1112scheduler_threads(Config) when is_list(Config) ->
1113    {Sched, SchedOnln, _} = get_sstate(Config, ""),
1114    %% Configure half the number of both the scheduler threads and
1115    %% the scheduler threads online.
1116    {HalfSched, HalfSchedOnln} = {lists:max([1,Sched div 2]),
1117                                  lists:max([1,SchedOnln div 2])},
1118    {HalfSched, HalfSchedOnln, _} = get_sstate(Config, "+SP 50:50"),
1119    %% Use +S to configure 4x the number of scheduler threads and
1120    %% 4x the number of scheduler threads online, but alter that
1121    %% setting using +SP to 50% scheduler threads and 25% scheduler
1122    %% threads online. The result should be 2x scheduler threads and
1123    %% 1x scheduler threads online.
1124    TwiceSched = Sched*2,
1125    FourSched = integer_to_list(Sched*4),
1126    FourSchedOnln = integer_to_list(SchedOnln*4),
1127    CombinedCmd1 = "+S "++FourSched++":"++FourSchedOnln++" +SP50:25",
1128    {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd1),
1129    %% Now do the same test but with the +S and +SP options in the
1130    %% opposite order, since order shouldn't matter.
1131    CombinedCmd2 = "+SP50:25 +S "++FourSched++":"++FourSchedOnln,
1132    {TwiceSched, SchedOnln, _} = get_sstate(Config, CombinedCmd2),
1133    %% Apply two +SP options to make sure the second overrides the first
1134    TwoCmd = "+SP 25:25 +SP 100:100",
1135    {Sched, SchedOnln, _} = get_sstate(Config, TwoCmd),
1136    %% Configure 50% of scheduler threads online only
1137    {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"),
1138    %% Configure 2x scheduler threads only
1139    {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"),
1140
1141    LProc = erlang:system_info(logical_processors),
1142    LProcAvail = erlang:system_info(logical_processors_available),
1143    Quota = erlang:system_info(cpu_quota),
1144
1145    if
1146        not is_integer(LProc); not is_integer(LProcAvail) ->
1147            {comment, "Skipped reset amount of schedulers test, and reduced "
1148                      "amount of schedulers test due to too unknown amount of "
1149                      "logical processors"};
1150        is_integer(LProc); is_integer(LProcAvail) ->
1151            ExpectedOnln = st_expected_onln(LProcAvail, Quota),
1152
1153            st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln),
1154
1155            if
1156                LProc =:= 1; LProcAvail =:= 1 ->
1157                    {comment, "Skipped reduced amount of schedulers test due "
1158                              "to too few logical processors"};
1159                LProc > 1, LProcAvail > 1 ->
1160                    st_reduced(Config, LProc, ExpectedOnln)
1161            end
1162    end.
1163
1164st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln) ->
1165    %% Test resetting # of schedulers.
1166    ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0",
1167    {LProc, ExpectedOnln, _} = get_sstate(Config, ResetCmd),
1168    ok.
1169
1170st_reduced(Config, LProc, ExpectedOnln) ->
1171    %% Test negative +S settings
1172    SchedMinus1 = LProc-1,
1173    SchedOnlnMinus1 = ExpectedOnln-1,
1174    {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"),
1175    {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"),
1176    {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"),
1177    ok.
1178
1179st_expected_onln(LProcAvail, unknown) -> LProcAvail;
1180st_expected_onln(LProcAvail, Quota) -> min(LProcAvail, Quota).
1181
1182dirty_scheduler_threads(Config) when is_list(Config) ->
1183    case erlang:system_info(dirty_cpu_schedulers) of
1184        0 -> {skipped, "No dirty scheduler support"};
1185        _ -> dirty_scheduler_threads_test(Config)
1186    end.
1187
1188dirty_scheduler_threads_test(Config) ->
1189    {Sched, SchedOnln, _} = get_dsstate(Config, ""),
1190    {HalfSched, HalfSchedOnln} = {lists:max([1,Sched div 2]),
1191                                  lists:max([1,SchedOnln div 2])},
1192    Cmd1 = "+SDcpu "++integer_to_list(HalfSched)++":"++
1193	integer_to_list(HalfSchedOnln),
1194    {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, Cmd1),
1195    {HalfSched, HalfSchedOnln, _} = get_dsstate(Config, "+SDPcpu 50:50"),
1196    IOSched = 20,
1197    {_, _, IOSched} = get_dsstate(Config, "+SDio "++integer_to_list(IOSched)),
1198    {ok, Node} = start_node(Config, ""),
1199    [ok] = mcall(Node, [fun() -> dirty_schedulers_online_test() end]),
1200    ok.
1201
1202dirty_schedulers_online_test() ->
1203    dirty_schedulers_online_smp_test(erlang:system_info(schedulers_online)).
1204dirty_schedulers_online_smp_test(SchedOnln) when SchedOnln < 4 -> ok;
1205dirty_schedulers_online_smp_test(SchedOnln) ->
1206    receive after 500 -> ok end,
1207    DirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online),
1208    SchedOnln = DirtyCPUSchedOnln,
1209    HalfSchedOnln = SchedOnln div 2,
1210    SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln),
1211    HalfDirtyCPUSchedOnln = DirtyCPUSchedOnln div 2,
1212    HalfDirtyCPUSchedOnln = erlang:system_flag(schedulers_online, SchedOnln),
1213    DirtyCPUSchedOnln = erlang:system_flag(dirty_cpu_schedulers_online,
1214					   HalfDirtyCPUSchedOnln),
1215    receive after 500 -> ok end,
1216    HalfDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online),
1217    QrtrDirtyCPUSchedOnln = HalfDirtyCPUSchedOnln div 2,
1218    SchedOnln = erlang:system_flag(schedulers_online, HalfSchedOnln),
1219    receive after 500 -> ok end,
1220    QrtrDirtyCPUSchedOnln = erlang:system_info(dirty_cpu_schedulers_online),
1221    ok.
1222
1223get_sstate(Config, Cmd) ->
1224    {ok, Node} = start_node(Config, Cmd),
1225    [SState] = mcall(Node, [fun () ->
1226                                    erlang:system_info(schedulers_state)
1227                            end]),
1228    stop_node(Node),
1229    SState.
1230
1231get_dsstate(Config, Cmd) ->
1232    {ok, Node} = start_node(Config, Cmd),
1233    [DSCPU] = mcall(Node, [fun () ->
1234				   erlang:system_info(dirty_cpu_schedulers)
1235			   end]),
1236    [DSCPUOnln] = mcall(Node, [fun () ->
1237				       erlang:system_info(dirty_cpu_schedulers_online)
1238			       end]),
1239    [DSIO] = mcall(Node, [fun () ->
1240				  erlang:system_info(dirty_io_schedulers)
1241			  end]),
1242    stop_node(Node),
1243    {DSCPU, DSCPUOnln, DSIO}.
1244
1245scheduler_suspend_basic(Config) when is_list(Config) ->
1246    case erlang:system_info(multi_scheduling) of
1247	disabled ->
1248	    {skip, "Nothing to test"};
1249	_ ->
1250	    Onln = erlang:system_info(schedulers_online),
1251	    try
1252		scheduler_suspend_basic_test()
1253	    after
1254		erlang:system_flag(schedulers_online, Onln)
1255	    end
1256    end.
1257
1258scheduler_suspend_basic_test() ->
1259    %% The receives after setting scheduler states are there
1260    %% since the operation is not fully synchronous. For example,
1261    %% we do not wait for dirty cpu schedulers online to complete
1262    %% before returning from erlang:system_flag(schedulers_online, _).
1263
1264    erlang:system_flag(schedulers_online,
1265		       erlang:system_info(schedulers)),
1266    try
1267	erlang:system_flag(dirty_cpu_schedulers_online,
1268			   erlang:system_info(dirty_cpu_schedulers)),
1269	receive after 500 -> ok end
1270    catch
1271	_ : _ ->
1272	    ok
1273    end,
1274
1275    S0 = sched_state(),
1276    io:format("~p~n", [S0]),
1277    {{normal,NTot0,NOnln0,NAct0},
1278     {dirty_cpu,DCTot0,DCOnln0,DCAct0},
1279     {dirty_io,DITot0,DIOnln0,DIAct0}} = S0,
1280    enabled = erlang:system_info(multi_scheduling),
1281
1282    DCOne = case DCTot0 of
1283		0 -> 0;
1284		_ -> 1
1285	    end,
1286
1287    blocked_normal = erlang:system_flag(multi_scheduling, block_normal),
1288    blocked_normal = erlang:system_info(multi_scheduling),
1289    {{normal,NTot0,NOnln0,1},
1290     {dirty_cpu,DCTot0,DCOnln0,DCAct0},
1291     {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
1292
1293    NOnln0 = erlang:system_flag(schedulers_online, 1),
1294    receive after 500 -> ok end,
1295    {{normal,NTot0,1,1},
1296     {dirty_cpu,DCTot0,DCOne,DCOne},
1297     {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
1298
1299    1 = erlang:system_flag(schedulers_online, NOnln0),
1300    receive after 500 -> ok end,
1301    {{normal,NTot0,NOnln0,1},
1302     {dirty_cpu,DCTot0,DCOnln0,DCAct0},
1303     {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
1304
1305    blocked = erlang:system_flag(multi_scheduling, block),
1306    blocked = erlang:system_info(multi_scheduling),
1307    receive after 500 -> ok end,
1308    {{normal,NTot0,NOnln0,1},
1309     {dirty_cpu,DCTot0,DCOnln0,0},
1310     {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
1311
1312    NOnln0 = erlang:system_flag(schedulers_online, 1),
1313    receive after 500 -> ok end,
1314    {{normal,NTot0,1,1},
1315     {dirty_cpu,DCTot0,DCOne,0},
1316     {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
1317
1318    1 = erlang:system_flag(schedulers_online, NOnln0),
1319    receive after 500 -> ok end,
1320    {{normal,NTot0,NOnln0,1},
1321     {dirty_cpu,DCTot0,DCOnln0,0},
1322     {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
1323
1324    blocked = erlang:system_flag(multi_scheduling, unblock_normal),
1325    blocked = erlang:system_info(multi_scheduling),
1326    {{normal,NTot0,NOnln0,1},
1327     {dirty_cpu,DCTot0,DCOnln0,0},
1328     {dirty_io,DITot0,DIOnln0,0}} = sched_state(),
1329
1330    enabled = erlang:system_flag(multi_scheduling, unblock),
1331    enabled = erlang:system_info(multi_scheduling),
1332    receive after 500 -> ok end,
1333    {{normal,NTot0,NOnln0,NAct0},
1334     {dirty_cpu,DCTot0,DCOnln0,DCAct0},
1335     {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
1336
1337    NOnln0 = erlang:system_flag(schedulers_online, 1),
1338    receive after 500 -> ok end,
1339    {{normal,NTot0,1,1},
1340     {dirty_cpu,DCTot0,DCOne,DCOne},
1341     {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
1342
1343    1 = erlang:system_flag(schedulers_online, NOnln0),
1344    receive after 500 -> ok end,
1345    {{normal,NTot0,NOnln0,NAct0},
1346     {dirty_cpu,DCTot0,DCOnln0,DCAct0},
1347     {dirty_io,DITot0,DIOnln0,DIAct0}} = sched_state(),
1348
1349    ok.
1350
1351
1352scheduler_suspend(Config) when is_list(Config) ->
1353    ct:timetrap({minutes, 5}),
1354    lists:foreach(fun (S) -> scheduler_suspend_test(Config, S) end,
1355			[64, 32, 16, default]),
1356    ok.
1357scheduler_suspend_test(Config, Schedulers) ->
1358    Cmd = case Schedulers of
1359		    default ->
1360			"";
1361		    _ ->
1362			S = integer_to_list(Schedulers),
1363			"+S"++S++":"++S
1364		end,
1365    {ok, Node} = start_node(Config, Cmd),
1366    [SState] = mcall(Node, [fun () ->
1367                                    erlang:system_info(schedulers_state)
1368                            end]),
1369
1370    io:format("SState=~p~n", [SState]),
1371    {Sched, SchedOnln, _SchedAvail} = SState,
1372    true = is_integer(Sched),
1373    [ok] = mcall(Node, [fun () -> sst0_loop(300) end]),
1374    [ok] = mcall(Node, [fun () -> sst1_loop(300) end]),
1375    [ok] = mcall(Node, [fun () -> sst2_loop(300) end]),
1376    [ok] = mcall(Node, [fun () -> sst4_loop(300) end]),
1377    [ok] = mcall(Node, [fun () -> sst5_loop(300) end]),
1378    [ok, ok, ok, ok,
1379     ok, ok, ok] = mcall(Node,
1380                         [fun () -> sst0_loop(200) end,
1381                          fun () -> sst1_loop(200) end,
1382                          fun () -> sst2_loop(200) end,
1383                          fun () -> sst2_loop(200) end,
1384                          fun () -> sst3_loop(Sched, 200) end,
1385                          fun () -> sst4_loop(200) end,
1386                          fun () -> sst5_loop(200) end]),
1387    [SState] = mcall(Node, [fun () ->
1388                                    case Sched == SchedOnln of
1389                                        false ->
1390                                            Sched = erlang:system_flag(
1391                                                      schedulers_online,
1392                                                      SchedOnln);
1393                                        true ->
1394                                            ok
1395                                    end,
1396                                    until(fun () ->
1397						  {_A, B, C} = erlang:system_info(
1398								 schedulers_state),
1399                                              B == C
1400                                          end,
1401                                          erlang:monotonic_time()
1402                                          + erlang:convert_time_unit(1,
1403								     seconds,
1404								     native)),
1405				    erlang:system_info(schedulers_state)
1406                            end]),
1407    stop_node(Node),
1408    ok.
1409
1410until(Pred, MaxTime) ->
1411    case Pred() of
1412	true ->
1413	    true;
1414	false ->
1415	    case erlang:monotonic_time() > MaxTime of
1416		true ->
1417		    false;
1418		false ->
1419		    receive after 100 -> ok end,
1420		    until(Pred, MaxTime)
1421	    end
1422    end.
1423
1424sst0_loop(0) ->
1425    ok;
1426sst0_loop(N) ->
1427    erlang:system_flag(multi_scheduling, block),
1428    erlang:system_flag(multi_scheduling, unblock),
1429    erlang:yield(),
1430    sst0_loop(N-1).
1431
1432sst1_loop(0) ->
1433    ok;
1434sst1_loop(N) ->
1435    erlang:system_flag(multi_scheduling, block),
1436    erlang:system_flag(multi_scheduling, unblock),
1437    sst1_loop(N-1).
1438
1439sst2_loop(0) ->
1440    ok;
1441sst2_loop(N) ->
1442    erlang:system_flag(multi_scheduling, block),
1443    erlang:system_flag(multi_scheduling, block),
1444    erlang:system_flag(multi_scheduling, block),
1445    erlang:system_flag(multi_scheduling, unblock),
1446    erlang:system_flag(multi_scheduling, unblock),
1447    erlang:system_flag(multi_scheduling, unblock),
1448    sst2_loop(N-1).
1449
1450sst3_loop(S, N) ->
1451    case erlang:system_info(dirty_cpu_schedulers) of
1452        0 -> sst3_loop_normal_schedulers_only(S, N);
1453	DS -> sst3_loop_with_dirty_schedulers(S, DS, N)
1454    end.
1455
1456sst3_loop_normal_schedulers_only(_S, 0) ->
1457    ok;
1458sst3_loop_normal_schedulers_only(S, N) ->
1459    erlang:system_flag(schedulers_online, (S div 2)+1),
1460    erlang:system_flag(schedulers_online, 1),
1461    erlang:system_flag(schedulers_online, (S div 2)+1),
1462    erlang:system_flag(schedulers_online, S),
1463    erlang:system_flag(schedulers_online, 1),
1464    erlang:system_flag(schedulers_online, S),
1465    sst3_loop_normal_schedulers_only(S, N-1).
1466
1467sst3_loop_with_dirty_schedulers(_S, _DS, 0) ->
1468    ok;
1469sst3_loop_with_dirty_schedulers(S, DS, N) ->
1470    erlang:system_flag(schedulers_online, (S div 2)+1),
1471    erlang:system_flag(dirty_cpu_schedulers_online, (DS div 2)+1),
1472    erlang:system_flag(schedulers_online, 1),
1473    erlang:system_flag(schedulers_online, (S div 2)+1),
1474    erlang:system_flag(dirty_cpu_schedulers_online, 1),
1475    erlang:system_flag(schedulers_online, S),
1476    erlang:system_flag(dirty_cpu_schedulers_online, DS),
1477    erlang:system_flag(schedulers_online, 1),
1478    erlang:system_flag(schedulers_online, S),
1479    erlang:system_flag(dirty_cpu_schedulers_online, DS),
1480    sst3_loop_with_dirty_schedulers(S, DS, N-1).
1481
1482sst4_loop(0) ->
1483    ok;
1484sst4_loop(N) ->
1485    erlang:system_flag(multi_scheduling, block_normal),
1486    erlang:system_flag(multi_scheduling, unblock_normal),
1487    sst4_loop(N-1).
1488
1489sst5_loop(0) ->
1490    ok;
1491sst5_loop(N) ->
1492    erlang:system_flag(multi_scheduling, block_normal),
1493    erlang:system_flag(multi_scheduling, unblock_normal),
1494    sst5_loop(N-1).
1495
1496poll_threads(Config) when is_list(Config) ->
1497    {Conc, PollType, KP} = get_ioconfig(Config),
1498    {Sched, SchedOnln, _} = get_sstate(Config, ""),
1499
1500    [1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"),
1501    [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"),
1502    [1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"),
1503
1504    if
1505        Conc ->
1506
1507            [5] = get_ionum(Config,"+IOt 5 +IOp 1"),
1508            [3, 2] = get_ionum(Config,"+IOt 5 +IOp 2"),
1509            [2, 2, 2, 2, 2] = get_ionum(Config,"+IOt 10 +IOPp 50"),
1510
1511            [2] = get_ionum(Config, "+S 2 +IOPt 100"),
1512            [4] = get_ionum(Config, "+S 4 +IOPt 100"),
1513            [4] = get_ionum(Config, "+S 4:2 +IOPt 100"),
1514            [4, 4] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"),
1515
1516            fail = get_ionum(Config, "+IOt 1 +IOp 2"),
1517
1518            ok;
1519        not Conc ->
1520
1521            [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 1"),
1522            [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 2"),
1523            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 10 +IOPp 50"),
1524
1525            [1, 1] = get_ionum(Config, "+S 2 +IOPt 100"),
1526            [1, 1, 1, 1] = get_ionum(Config, "+S 4 +IOPt 100"),
1527            [1, 1, 1, 1] = get_ionum(Config, "+S 4:2 +IOPt 100"),
1528            [1, 1, 1, 1, 1, 1, 1, 1] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"),
1529
1530            [1] = get_ionum(Config, "+IOt 1 +IOp 2"),
1531
1532            ok
1533    end,
1534
1535    fail = get_ionum(Config, "+IOt 1 +IOPp 101"),
1536    fail = get_ionum(Config, "+IOt 0"),
1537    fail = get_ionum(Config, "+IOPt 101"),
1538
1539    ok.
1540
1541get_ioconfig(Config) ->
1542    [PS | _] = get_iostate(Config, ""),
1543    {proplists:get_value(concurrent_updates, PS),
1544     proplists:get_value(primary, PS),
1545     proplists:get_value(kernel_poll, PS)}.
1546
1547get_ionum(Config, Cmd) ->
1548    case get_iostate(Config, Cmd) of
1549        fail -> fail;
1550        PSs ->
1551            lists:reverse(
1552              lists:sort(
1553                [proplists:get_value(poll_threads, PS) || PS <- PSs]))
1554    end.
1555
1556get_iostate(Config, Cmd)->
1557    case start_node(Config, Cmd) of
1558        {ok, Node} ->
1559            [IOStates] = mcall(Node,[fun () ->
1560                                             erlang:system_info(check_io)
1561                                     end]),
1562            IO = [IOState || IOState <- IOStates,
1563                             proplists:get_value(fallback, IOState) == false,
1564                             proplists:get_value(poll_threads, IOState) /= 0],
1565            stop_node(Node),
1566            IO;
1567        {error,timeout} ->
1568            fail
1569    end.
1570
1571reader_groups(Config) when is_list(Config) ->
1572    %% White box testing. These results are correct, but other results
1573    %% could be too...
1574
1575    %% The actual tilepro64 topology
1576    CPUT0 = [{processor,[{node,[{core,{logical,0}},
1577                                {core,{logical,1}},
1578                                {core,{logical,2}},
1579                                {core,{logical,8}},
1580                                {core,{logical,9}},
1581                                {core,{logical,10}},
1582                                {core,{logical,11}},
1583                                {core,{logical,16}},
1584                                {core,{logical,17}},
1585                                {core,{logical,18}},
1586                                {core,{logical,19}},
1587                                {core,{logical,24}},
1588                                {core,{logical,25}},
1589                                {core,{logical,27}},
1590                                {core,{logical,29}}]},
1591                         {node,[{core,{logical,3}},
1592                                {core,{logical,4}},
1593                                {core,{logical,5}},
1594                                {core,{logical,6}},
1595                                {core,{logical,7}},
1596                                {core,{logical,12}},
1597                                {core,{logical,13}},
1598                                {core,{logical,14}},
1599                                {core,{logical,15}},
1600                                {core,{logical,20}},
1601                                {core,{logical,21}},
1602                                {core,{logical,22}},
1603                                {core,{logical,23}},
1604                                {core,{logical,28}},
1605                                {core,{logical,30}}]},
1606                         {node,[{core,{logical,31}},
1607                                {core,{logical,36}},
1608                                {core,{logical,37}},
1609                                {core,{logical,38}},
1610                                {core,{logical,44}},
1611                                {core,{logical,45}},
1612                                {core,{logical,46}},
1613                                {core,{logical,47}},
1614                                {core,{logical,51}},
1615                                {core,{logical,52}},
1616                                {core,{logical,53}},
1617                                {core,{logical,54}},
1618                                {core,{logical,55}},
1619                                {core,{logical,60}},
1620                                {core,{logical,61}}]},
1621                         {node,[{core,{logical,26}},
1622                                {core,{logical,32}},
1623                                {core,{logical,33}},
1624                                {core,{logical,34}},
1625                                {core,{logical,35}},
1626                                {core,{logical,39}},
1627                                {core,{logical,40}},
1628                                {core,{logical,41}},
1629                                {core,{logical,42}},
1630                                {core,{logical,43}},
1631                                {core,{logical,48}},
1632                                {core,{logical,49}},
1633                                {core,{logical,50}},
1634                                {core,{logical,58}}]}]}],
1635
1636    [{0,1},{1,1},{2,1},{3,3},{4,3},{5,3},{6,3},{7,3},{8,1},{9,1},{10,1},
1637     {11,1},{12,3},{13,3},{14,4},{15,4},{16,2},{17,2},{18,2},{19,2},
1638     {20,4},{21,4},{22,4},{23,4},{24,2},{25,2},{26,7},{27,2},{28,4},
1639     {29,2},{30,4},{31,5},{32,7},{33,7},{34,7},{35,7},{36,5},{37,5},
1640     {38,5},{39,7},{40,7},{41,8},{42,8},{43,8},{44,5},{45,5},{46,5},
1641     {47,6},{48,8},{49,8},{50,8},{51,6},{52,6},{53,6},{54,6},{55,6},
1642     {58,8},{60,6},{61,6}]
1643    = reader_groups_map(CPUT0, 8),
1644
1645    CPUT1 = [n([p([c([t(l(0)),t(l(1)),t(l(2)),t(l(3))]),
1646                   c([t(l(4)),t(l(5)),t(l(6)),t(l(7))]),
1647                   c([t(l(8)),t(l(9)),t(l(10)),t(l(11))]),
1648                   c([t(l(12)),t(l(13)),t(l(14)),t(l(15))])]),
1649                p([c([t(l(16)),t(l(17)),t(l(18)),t(l(19))]),
1650                   c([t(l(20)),t(l(21)),t(l(22)),t(l(23))]),
1651                   c([t(l(24)),t(l(25)),t(l(26)),t(l(27))]),
1652                   c([t(l(28)),t(l(29)),t(l(30)),t(l(31))])])]),
1653             n([p([c([t(l(32)),t(l(33)),t(l(34)),t(l(35))]),
1654                   c([t(l(36)),t(l(37)),t(l(38)),t(l(39))]),
1655                   c([t(l(40)),t(l(41)),t(l(42)),t(l(43))]),
1656                   c([t(l(44)),t(l(45)),t(l(46)),t(l(47))])]),
1657                p([c([t(l(48)),t(l(49)),t(l(50)),t(l(51))]),
1658                   c([t(l(52)),t(l(53)),t(l(54)),t(l(55))]),
1659                   c([t(l(56)),t(l(57)),t(l(58)),t(l(59))]),
1660                   c([t(l(60)),t(l(61)),t(l(62)),t(l(63))])])]),
1661             n([p([c([t(l(64)),t(l(65)),t(l(66)),t(l(67))]),
1662                   c([t(l(68)),t(l(69)),t(l(70)),t(l(71))]),
1663                   c([t(l(72)),t(l(73)),t(l(74)),t(l(75))]),
1664                   c([t(l(76)),t(l(77)),t(l(78)),t(l(79))])]),
1665                p([c([t(l(80)),t(l(81)),t(l(82)),t(l(83))]),
1666                   c([t(l(84)),t(l(85)),t(l(86)),t(l(87))]),
1667                   c([t(l(88)),t(l(89)),t(l(90)),t(l(91))]),
1668                   c([t(l(92)),t(l(93)),t(l(94)),t(l(95))])])]),
1669             n([p([c([t(l(96)),t(l(97)),t(l(98)),t(l(99))]),
1670                   c([t(l(100)),t(l(101)),t(l(102)),t(l(103))]),
1671                   c([t(l(104)),t(l(105)),t(l(106)),t(l(107))]),
1672                   c([t(l(108)),t(l(109)),t(l(110)),t(l(111))])]),
1673                p([c([t(l(112)),t(l(113)),t(l(114)),t(l(115))]),
1674                   c([t(l(116)),t(l(117)),t(l(118)),t(l(119))]),
1675                   c([t(l(120)),t(l(121)),t(l(122)),t(l(123))]),
1676                   c([t(l(124)),t(l(125)),t(l(126)),t(l(127))])])])],
1677
1678    [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},
1679     {10,3},{11,3},{12,4},{13,4},{14,4},{15,4},{16,5},{17,5},{18,5},
1680     {19,5},{20,6},{21,6},{22,6},{23,6},{24,7},{25,7},{26,7},{27,7},
1681     {28,8},{29,8},{30,8},{31,8},{32,9},{33,9},{34,9},{35,9},{36,10},
1682     {37,10},{38,10},{39,10},{40,11},{41,11},{42,11},{43,11},{44,12},
1683     {45,12},{46,12},{47,12},{48,13},{49,13},{50,13},{51,13},{52,14},
1684     {53,14},{54,14},{55,14},{56,15},{57,15},{58,15},{59,15},{60,16},
1685     {61,16},{62,16},{63,16},{64,17},{65,17},{66,17},{67,17},{68,18},
1686     {69,18},{70,18},{71,18},{72,19},{73,19},{74,19},{75,19},{76,20},
1687     {77,20},{78,20},{79,20},{80,21},{81,21},{82,21},{83,21},{84,22},
1688     {85,22},{86,22},{87,22},{88,23},{89,23},{90,23},{91,23},{92,24},
1689     {93,24},{94,24},{95,24},{96,25},{97,25},{98,25},{99,25},{100,26},
1690     {101,26},{102,26},{103,26},{104,27},{105,27},{106,27},{107,27},
1691     {108,28},{109,28},{110,28},{111,28},{112,29},{113,29},{114,29},
1692     {115,29},{116,30},{117,30},{118,30},{119,30},{120,31},{121,31},
1693     {122,31},{123,31},{124,32},{125,32},{126,32},{127,32}]
1694    = reader_groups_map(CPUT1, 128),
1695
1696    [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1},
1697     {11,1},{12,1},{13,1},{14,1},{15,1},{16,1},{17,1},{18,1},{19,1},
1698     {20,1},{21,1},{22,1},{23,1},{24,1},{25,1},{26,1},{27,1},{28,1},
1699     {29,1},{30,1},{31,1},{32,1},{33,1},{34,1},{35,1},{36,1},{37,1},
1700     {38,1},{39,1},{40,1},{41,1},{42,1},{43,1},{44,1},{45,1},{46,1},
1701     {47,1},{48,1},{49,1},{50,1},{51,1},{52,1},{53,1},{54,1},{55,1},
1702     {56,1},{57,1},{58,1},{59,1},{60,1},{61,1},{62,1},{63,1},{64,2},
1703     {65,2},{66,2},{67,2},{68,2},{69,2},{70,2},{71,2},{72,2},{73,2},
1704     {74,2},{75,2},{76,2},{77,2},{78,2},{79,2},{80,2},{81,2},{82,2},
1705     {83,2},{84,2},{85,2},{86,2},{87,2},{88,2},{89,2},{90,2},{91,2},
1706     {92,2},{93,2},{94,2},{95,2},{96,2},{97,2},{98,2},{99,2},{100,2},
1707     {101,2},{102,2},{103,2},{104,2},{105,2},{106,2},{107,2},{108,2},
1708     {109,2},{110,2},{111,2},{112,2},{113,2},{114,2},{115,2},{116,2},
1709     {117,2},{118,2},{119,2},{120,2},{121,2},{122,2},{123,2},{124,2},
1710     {125,2},{126,2},{127,2}]
1711    = reader_groups_map(CPUT1, 2),
1712
1713    [{0,1},{1,1},{2,1},{3,1},{4,2},{5,2},{6,2},{7,2},{8,3},{9,3},{10,3},
1714     {11,3},{12,3},{13,3},{14,3},{15,3},{16,4},{17,4},{18,4},{19,4},
1715     {20,4},{21,4},{22,4},{23,4},{24,5},{25,5},{26,5},{27,5},{28,5},
1716     {29,5},{30,5},{31,5},{32,6},{33,6},{34,6},{35,6},{36,6},{37,6},
1717     {38,6},{39,6},{40,7},{41,7},{42,7},{43,7},{44,7},{45,7},{46,7},
1718     {47,7},{48,8},{49,8},{50,8},{51,8},{52,8},{53,8},{54,8},{55,8},
1719     {56,9},{57,9},{58,9},{59,9},{60,9},{61,9},{62,9},{63,9},{64,10},
1720     {65,10},{66,10},{67,10},{68,10},{69,10},{70,10},{71,10},{72,11},
1721     {73,11},{74,11},{75,11},{76,11},{77,11},{78,11},{79,11},{80,12},
1722     {81,12},{82,12},{83,12},{84,12},{85,12},{86,12},{87,12},{88,13},
1723     {89,13},{90,13},{91,13},{92,13},{93,13},{94,13},{95,13},{96,14},
1724     {97,14},{98,14},{99,14},{100,14},{101,14},{102,14},{103,14},
1725     {104,15},{105,15},{106,15},{107,15},{108,15},{109,15},{110,15},
1726     {111,15},{112,16},{113,16},{114,16},{115,16},{116,16},{117,16},
1727     {118,16},{119,16},{120,17},{121,17},{122,17},{123,17},{124,17},
1728     {125,17},{126,17},{127,17}]
1729    = reader_groups_map(CPUT1, 17),
1730
1731    [{0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},{8,1},{9,1},{10,1},
1732     {11,1},{12,1},{13,1},{14,1},{15,1},{16,2},{17,2},{18,2},{19,2},
1733     {20,2},{21,2},{22,2},{23,2},{24,2},{25,2},{26,2},{27,2},{28,2},
1734     {29,2},{30,2},{31,2},{32,3},{33,3},{34,3},{35,3},{36,3},{37,3},
1735     {38,3},{39,3},{40,3},{41,3},{42,3},{43,3},{44,3},{45,3},{46,3},
1736     {47,3},{48,4},{49,4},{50,4},{51,4},{52,4},{53,4},{54,4},{55,4},
1737     {56,4},{57,4},{58,4},{59,4},{60,4},{61,4},{62,4},{63,4},{64,5},
1738     {65,5},{66,5},{67,5},{68,5},{69,5},{70,5},{71,5},{72,5},{73,5},
1739     {74,5},{75,5},{76,5},{77,5},{78,5},{79,5},{80,6},{81,6},{82,6},
1740     {83,6},{84,6},{85,6},{86,6},{87,6},{88,6},{89,6},{90,6},{91,6},
1741     {92,6},{93,6},{94,6},{95,6},{96,7},{97,7},{98,7},{99,7},{100,7},
1742     {101,7},{102,7},{103,7},{104,7},{105,7},{106,7},{107,7},{108,7},
1743     {109,7},{110,7},{111,7},{112,7},{113,7},{114,7},{115,7},{116,7},
1744     {117,7},{118,7},{119,7},{120,7},{121,7},{122,7},{123,7},{124,7},
1745     {125,7},{126,7},{127,7}]
1746    = reader_groups_map(CPUT1, 7),
1747
1748    CPUT2 = [p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))]),
1749             p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]),
1750             p([t(l(10))]),
1751             p([c(l(11)),c(l(12)),c(l(13))]),
1752             p([c(l(14)),c(l(15))])],
1753
1754    [{0,1},{1,1},{2,1},{3,1},{4,1},
1755     {5,2},{6,2},{7,2},{8,2},{9,2},
1756     {10,3},
1757     {11,4},{12,4},{13,4},
1758     {14,5},{15,5}] = reader_groups_map(CPUT2, 5),
1759
1760
1761    [{0,1},{1,1},{2,2},{3,2},{4,2},
1762     {5,3},{6,3},{7,3},{8,3},{9,3},
1763     {10,4},
1764     {11,5},{12,5},{13,5},
1765     {14,6},{15,6}] = reader_groups_map(CPUT2, 6),
1766
1767    [{0,1},{1,1},{2,2},{3,2},{4,2},
1768     {5,3},{6,3},{7,3},{8,3},{9,3},
1769     {10,4},
1770     {11,5},{12,6},{13,6},
1771     {14,7},{15,7}] = reader_groups_map(CPUT2, 7),
1772
1773    [{0,1},{1,1},{2,2},{3,2},{4,2},
1774     {5,3},{6,3},{7,3},{8,3},{9,3},
1775     {10,4},
1776     {11,5},{12,6},{13,6},
1777     {14,7},{15,8}] = reader_groups_map(CPUT2, 8),
1778
1779    [{0,1},{1,2},{2,2},{3,3},{4,3},
1780     {5,4},{6,4},{7,4},{8,4},{9,4},
1781     {10,5},
1782     {11,6},{12,7},{13,7},
1783     {14,8},{15,9}] = reader_groups_map(CPUT2, 9),
1784
1785    [{0,1},{1,2},{2,2},{3,3},{4,3},
1786     {5,4},{6,4},{7,4},{8,4},{9,4},
1787     {10,5},
1788     {11,6},{12,7},{13,8},
1789     {14,9},{15,10}] = reader_groups_map(CPUT2, 10),
1790
1791    [{0,1},{1,2},{2,3},{3,4},{4,4},
1792     {5,5},{6,5},{7,5},{8,5},{9,5},
1793     {10,6},
1794     {11,7},{12,8},{13,9},
1795     {14,10},{15,11}] = reader_groups_map(CPUT2, 11),
1796
1797    [{0,1},{1,2},{2,3},{3,4},{4,5},
1798     {5,6},{6,6},{7,6},{8,6},{9,6},
1799     {10,7},
1800     {11,8},{12,9},{13,10},
1801     {14,11},{15,12}] = reader_groups_map(CPUT2, 100),
1802
1803    CPUT3 = [p([t(l(5)),t(l(6)),t(l(7)),t(l(8)),t(l(9))]),
1804             p([t(l(10))]),
1805             p([c(l(11)),c(l(12)),c(l(13))]),
1806             p([c(l(14)),c(l(15))]),
1807             p([c(l(0)),c(l(1)),c(l(2)),c(l(3)),c(l(4))])],
1808
1809    [{0,5},{1,5},{2,6},{3,6},{4,6},
1810     {5,1},{6,1},{7,1},{8,1},{9,1},
1811     {10,2},{11,3},{12,3},{13,3},
1812     {14,4},{15,4}] = reader_groups_map(CPUT3, 6),
1813
1814    CPUT4 = [p([t(l(0)),t(l(1)),t(l(2)),t(l(3)),t(l(4))]),
1815             p([t(l(5))]),
1816             p([c(l(6)),c(l(7)),c(l(8))]),
1817             p([c(l(9)),c(l(10))]),
1818             p([c(l(11)),c(l(12)),c(l(13)),c(l(14)),c(l(15))])],
1819
1820    [{0,1},{1,1},{2,1},{3,1},{4,1},
1821     {5,2},
1822     {6,3},{7,3},{8,3},
1823     {9,4},{10,4},
1824     {11,5},{12,5},{13,6},{14,6},{15,6}] = reader_groups_map(CPUT4, 6),
1825
1826    [{0,1},{1,1},{2,1},{3,1},{4,1},
1827     {5,2},
1828     {6,3},{7,4},{8,4},
1829     {9,5},{10,5},
1830     {11,6},{12,6},{13,7},{14,7},{15,7}] = reader_groups_map(CPUT4, 7),
1831
1832    [{0,1},{65535,2}] = reader_groups_map([c(l(0)),c(l(65535))], 10),
1833    ok.
1834
1835
1836reader_groups_map(CPUT, Groups) ->
1837    Old = erlang:system_info({cpu_topology, defined}),
1838    erlang:system_flag(cpu_topology, CPUT),
1839    enable_internal_state(),
1840    Res = erts_debug:get_internal_state({reader_groups_map, Groups}),
1841    erlang:system_flag(cpu_topology, Old),
1842    lists:sort(Res).
1843
1844otp_16446(Config) when is_list(Config) ->
1845    ct:timetrap({minutes, 1}),
1846
1847    process_flag(priority, high),
1848
1849    DIO = erlang:system_info(dirty_io_schedulers),
1850    NoPrioProcs = 10*DIO,
1851    io:format("DIO = ~p~nNoPrioProcs = ~p~n", [DIO, NoPrioProcs]),
1852
1853    DirtyLoop = fun Loop(P, N) ->
1854                        erts_debug:dirty_io(wait,1),
1855                        receive {get, From} -> From ! {P, N}
1856                        after 0 -> Loop(P,N+1)
1857                        end
1858                end,
1859
1860    Spawn = fun SpawnLoop(_Prio, 0, Acc) ->
1861                    Acc;
1862                SpawnLoop(Prio, N, Acc) ->
1863                    Pid = spawn_opt(fun () -> DirtyLoop(Prio, 0) end,
1864                                    [link, {priority, Prio}]),
1865                    SpawnLoop(Prio, N-1, [Pid|Acc])
1866            end,
1867
1868    Ns = Spawn(normal, NoPrioProcs, []),
1869    Ls = Spawn(low, NoPrioProcs, []),
1870
1871    receive after 10000 -> ok end,
1872
1873    RequestInfo = fun (P) -> P ! {get, self()} end,
1874    lists:foreach(RequestInfo, Ns),
1875    lists:foreach(RequestInfo, Ls),
1876
1877    Collect = fun CollectFun(0, LLs, NLs) ->
1878                      {LLs, NLs};
1879                  CollectFun(N, LLs, NLs) ->
1880                      receive
1881                          {low, Calls} ->
1882                              CollectFun(N-1, LLs+Calls, NLs);
1883                          {normal, Calls} ->
1884                              CollectFun(N-1, LLs, NLs+Calls)
1885                      end
1886              end,
1887
1888    {LLs, NLs} = Collect(2*NoPrioProcs, 0, 0),
1889
1890    %% expected ratio 0.125, but this is not especially exact...
1891    Ratio = LLs / NLs,
1892
1893    io:format("LLs = ~p~nNLs = ~p~nRatio = ~p~n", [LLs, NLs, Ratio]),
1894
1895    true = Ratio > 0.05,
1896    true = Ratio < 0.5,
1897
1898    WaitUntilDead = fun (P) ->
1899                            case is_process_alive(P) of
1900                                false ->
1901                                    ok;
1902                                true ->
1903                                    unlink(P),
1904                                    exit(P, kill),
1905                                    false = is_process_alive(P)
1906                            end
1907                    end,
1908
1909    lists:foreach(WaitUntilDead, Ns),
1910    lists:foreach(WaitUntilDead, Ls),
1911    Comment = "low/normal ratio: " ++ erlang:float_to_list(Ratio,[{decimals,4}]),
1912    erlang:display(Comment),
1913    {comment, Comment}.
1914
1915simultaneously_change_schedulers_online(Config) when is_list(Config) ->
1916    SchedOnline = erlang:system_info(schedulers_online),
1917    Change = fun Change (0) ->
1918                     ok;
1919                 Change (N) ->
1920                     %timer:sleep(rand:uniform(100)),
1921                     erlang:system_flag(schedulers_online,
1922                                        rand:uniform(erlang:system_info(schedulers))),
1923                     Change(N-1)
1924             end,
1925    PMs = lists:map(fun (_) ->
1926                            spawn_monitor(fun () -> Change(10) end)
1927                    end, lists:seq(1,2500)),
1928    lists:foreach(fun ({P, M}) ->
1929                          receive
1930                              {'DOWN', M, process, P, normal} ->
1931                                  ok
1932                          end
1933                  end,
1934                  PMs),
1935    erlang:system_flag(schedulers_online, SchedOnline),
1936    ok.
1937
1938simultaneously_change_schedulers_online_with_exits(Config) when is_list(Config) ->
1939    SchedOnline = erlang:system_info(schedulers_online),
1940    Change = fun Change (0) ->
1941                     exit(bye);
1942                 Change (N) ->
1943                     %timer:sleep(rand:uniform(100)),
1944                     erlang:system_flag(schedulers_online,
1945                                        rand:uniform(erlang:system_info(schedulers))),
1946                     Change(N-1)
1947             end,
1948    PMs = lists:map(fun (_) ->
1949                            spawn_monitor(fun () -> Change(10) end)
1950                    end, lists:seq(1,2500)),
1951    %% Kill every 10:th process...
1952    _ = lists:foldl(fun ({P, _M}, 0) ->
1953                            exit(P, bye),
1954                            10;
1955                        (_PM, N) ->
1956                            N-1
1957                    end,
1958                    10,
1959                    PMs),
1960    lists:foreach(fun ({P, M}) ->
1961                          receive
1962                              {'DOWN', M, process, P, Reason} ->
1963                                  bye = Reason
1964                          end
1965                  end,
1966                  PMs),
1967    erlang:system_flag(schedulers_online, SchedOnline),
1968    ok.
1969
1970
1971%%
1972%% Utils
1973%%
1974
1975sched_state() ->
1976    sched_state(erlang:system_info(all_schedulers_state),
1977		undefined,
1978		{dirty_cpu,0,0,0},
1979		{dirty_io,0,0,0}).
1980
1981
1982sched_state([], N, DC, DI) ->
1983    try
1984	chk_basic(N),
1985	chk_basic(DC),
1986	chk_basic(DI),
1987	{N, DC, DI}
1988    catch
1989	_ : _ ->
1990	    ct:fail({inconsisten_scheduler_state, {N, DC, DI}})
1991    end;
1992sched_state([{normal, _, _, _} = S | Rest], _S, DC, DI) ->
1993    sched_state(Rest, S, DC, DI);
1994sched_state([{dirty_cpu, _, _, _} = DC | Rest], S, _DC, DI) ->
1995    sched_state(Rest, S, DC, DI);
1996sched_state([{dirty_io, _, _, _} = DI | Rest], S, DC, _DI) ->
1997    sched_state(Rest, S, DC, DI).
1998
1999chk_basic({_Type, Tot, Onln, Act}) ->
2000    true = Tot >= Onln,
2001    true = Onln >= Act.
2002
2003l(Id) ->
2004    {logical, Id}.
2005
2006t(X) ->
2007    {thread, X}.
2008
2009c(X) ->
2010    {core, X}.
2011
2012p(X) ->
2013    {processor, X}.
2014
2015n(X) ->
2016    {node, X}.
2017
2018mcall(Node, Funs) ->
2019    Parent = self(),
2020    Refs = lists:map(fun (Fun) ->
2021                             Ref = make_ref(),
2022                             Pid = spawn(Node,
2023                                         fun () ->
2024                                                 Res = Fun(),
2025                                                 unlink(Parent),
2026                                                 Parent ! {Ref, Res}
2027                                         end),
2028                             MRef = erlang:monitor(process, Pid),
2029                             {Ref, MRef}
2030                     end, Funs),
2031    lists:map(fun ({Ref, MRef}) ->
2032                      receive
2033                          {Ref, Res} ->
2034                              receive
2035                                  {'DOWN',MRef,_,_,_} ->
2036                                      Res
2037                              end;
2038                          {'DOWN',MRef,_,_,Reason} ->
2039                              Reason
2040                      end
2041              end, Refs).
2042
2043erl_rel_flag_var() ->
2044    "ERL_OTP"++erlang:system_info(otp_release)++"_FLAGS".
2045
2046clear_erl_rel_flags() ->
2047    EnvVar = erl_rel_flag_var(),
2048    case os:getenv(EnvVar) of
2049	false ->
2050	    false;
2051	Value ->
2052	    os:putenv(EnvVar, ""),
2053	    Value
2054    end.
2055
2056restore_erl_rel_flags(false) ->
2057    ok;
2058restore_erl_rel_flags(OldValue) ->
2059    os:putenv(erl_rel_flag_var(), OldValue),
2060    ok.
2061
2062ok(too_slow, _Config) ->
2063    {comment, "Too slow system to do any actual testing..."};
2064ok(_Res, Config) ->
2065    proplists:get_value(ok_res, Config).
2066
2067chk_result(too_slow,
2068           _LWorkers,
2069           _NWorkers,
2070           _HWorkers,
2071           _MWorkers,
2072           _LNShouldWork,
2073           _HShouldWork,
2074           _MShouldWork) ->
2075    ok;
2076chk_result([{low, L, Lmin, _Lmax},
2077            {normal, N, Nmin, _Nmax},
2078            {high, H, Hmin, _Hmax},
2079            {max, M, Mmin, _Mmax}] = Res,
2080           LWorkers,
2081           NWorkers,
2082           HWorkers,
2083           MWorkers,
2084           LNShouldWork,
2085           HShouldWork,
2086           MShouldWork) ->
2087    io:format("~p~n", [Res]),
2088    Relax = relax_limits(),
2089    case {L, N} of
2090        {0, 0} ->
2091            false = LNShouldWork;
2092        _ ->
2093            {LminRatioLim,
2094             NminRatioLim,
2095             LNRatioLimMin,
2096             LNRatioLimMax} = case Relax of
2097                                  false -> {0.5, 0.5, 0.05, 0.25};
2098                                  true -> {0.05, 0.05, 0.01, 0.4}
2099                              end,
2100            Lavg = L/LWorkers,
2101            Navg = N/NWorkers,
2102            Ratio = Lavg/Navg,
2103            LminRatio = Lmin/Lavg,
2104            NminRatio = Nmin/Navg,
2105            io:format("low min ratio=~p~n"
2106                      "normal min ratio=~p~n"
2107                      "low avg=~p~n"
2108                      "normal avg=~p~n"
2109                      "low/normal ratio=~p~n",
2110                      [LminRatio, NminRatio, Lavg, Navg, Ratio]),
2111            erlang:display({low_min_ratio, LminRatio}),
2112            erlang:display({normal_min_ratio, NminRatio}),
2113            erlang:display({low_avg, Lavg}),
2114            erlang:display({normal_avg, Navg}),
2115            erlang:display({low_normal_ratio, Ratio}),
2116            chk_lim(LminRatioLim, LminRatio, 1.0, low_min_ratio),
2117            chk_lim(NminRatioLim, NminRatio, 1.0, normal_min_ratio),
2118            chk_lim(LNRatioLimMin, Ratio, LNRatioLimMax, low_normal_ratio),
2119            true = LNShouldWork,
2120            ok
2121    end,
2122    case H of
2123        0 ->
2124            false = HShouldWork;
2125        _ ->
2126            HminRatioLim = case Relax of
2127                               false -> 0.5;
2128                               true -> 0.1
2129                           end,
2130            Havg = H/HWorkers,
2131            HminRatio = Hmin/Havg,
2132            erlang:display({high_min_ratio, HminRatio}),
2133            chk_lim(HminRatioLim, HminRatio, 1.0, high_min_ratio),
2134            true = HShouldWork,
2135            ok
2136    end,
2137    case M of
2138        0 ->
2139            false = MShouldWork;
2140        _ ->
2141            MminRatioLim = case Relax of
2142                               false -> 0.5;
2143                               true -> 0.1
2144                           end,
2145            Mavg = M/MWorkers,
2146            MminRatio = Mmin/Mavg,
2147            erlang:display({max_min_ratio, MminRatio}),
2148            chk_lim(MminRatioLim, MminRatio, 1.0, max_min_ratio),
2149            true = MShouldWork,
2150            ok
2151    end,
2152    ok.
2153
2154
2155
2156chk_lim(Min, V, Max, _What) when Min =< V, V =< Max ->
2157    ok;
2158chk_lim(_Min, V, _Max, What) ->
2159    ct:fail({bad, What, V}).
2160
2161snd(_Msg, []) ->
2162    [];
2163snd(Msg, [P|Ps]) ->
2164    P ! Msg,
2165    Ps.
2166
2167relax_limits() ->
2168    case strange_system_scale() of
2169	Scale when Scale > 1 ->
2170	    io:format("Relaxing limits~n", []),
2171	    true;
2172	_ ->
2173	    false
2174    end.
2175
2176strange_system_scale() ->
2177    S0 = 1,
2178    S1 = case erlang:system_info(schedulers_online)
2179	     > erlang:system_info(logical_processors) of
2180	     true -> S0*2;
2181	     false -> S0
2182	 end,
2183    S2 = case erlang:system_info(debug_compiled) of
2184	     true -> S1*10;
2185	     false ->
2186		 case erlang:system_info(lock_checking) of
2187		     true -> S1*2;
2188		     false -> S1
2189		 end
2190	 end,
2191    S3 = case lock_counting() of
2192	     true -> S2*2;
2193	     false -> S2
2194	 end,
2195    S3.
2196
2197lock_counting() ->
2198    lock_counting(erlang:system_info(system_version)).
2199
2200lock_counting([]) ->
2201    false;
2202lock_counting([$[,$l,$o,$c,$k,$-,$c,$o,$u,$n,$t,$i,$n,$g,$],_]) ->
2203    true;
2204lock_counting([_C|Cs]) ->
2205    lock_counting(Cs).
2206
2207go_work([], [], [], []) ->
2208    [];
2209go_work(L, N, [], []) ->
2210    go_work(snd(go_work, L), snd(go_work, N), [], []);
2211go_work(L, N, H, []) ->
2212    go_work(L, N, snd(go_work, H), []);
2213go_work(L, N, H, M) ->
2214    go_work(L, N, H, snd(go_work, M)).
2215
2216stop_work([], [], [], []) ->
2217    [];
2218stop_work([], [], [], M) ->
2219    stop_work([], [], [], snd(stop_work, M));
2220stop_work([], [], H, M) ->
2221    stop_work([], [], snd(stop_work, H), M);
2222stop_work(L, N, H, M) ->
2223    stop_work(snd(stop_work, L), snd(stop_work, N), H, M).
2224
2225wait_balance(N) when is_integer(N) ->
2226    case erlang:system_info(schedulers_active) of
2227	1 ->
2228	    done;
2229	_ ->
2230	    erts_debug:set_internal_state(available_internal_state,true),
2231	    Start = erts_debug:get_internal_state(nbalance),
2232	    End = (Start + N) band ((1 bsl (8*erlang:system_info(wordsize)))-1),
2233	    wait_balance(Start, End),
2234	    erts_debug:set_internal_state(available_internal_state,false)
2235    end.
2236
2237wait_balance(Start, End) ->
2238    X = erts_debug:get_internal_state(nbalance),
2239    case End =< X of
2240	true ->
2241	    case Start =< End of
2242		true ->
2243		    done;
2244		false ->
2245		    case X < Start of
2246			true ->
2247			    done;
2248			false ->
2249			    receive after 250 -> ok end,
2250			    wait_balance(Start, End)
2251		    end
2252	    end;
2253	false ->
2254	    receive after 250 -> ok end,
2255	    wait_balance(Start, End)
2256    end.
2257
2258wait_reds(RedsLimit, Timeout) ->
2259    Stop = erlang:start_timer(Timeout, self(), stop),
2260    statistics(reductions),
2261    wait_reds(0, RedsLimit, Stop).
2262
2263wait_reds(Reds, RedsLimit, Stop) when Reds < RedsLimit ->
2264    receive
2265	{timeout, Stop, stop} ->
2266	    erlang:display(timeout),
2267	    erlang:display({reduction_limit, RedsLimit}),
2268	    erlang:display({reductions, Reds}),
2269	    done
2270    after 10000 ->
2271	    {_, NewReds} = statistics(reductions),
2272	    wait_reds(NewReds+Reds, RedsLimit, Stop)
2273    end;
2274wait_reds(Reds, RedsLimit, Stop) when is_reference(Stop) ->
2275    erlang:cancel_timer(Stop),
2276    receive {timeout, Stop, stop} -> ok after 0 -> ok end,
2277    wait_reds(Reds, RedsLimit, false);
2278wait_reds(Reds, RedsLimit, _Stop) ->
2279    erlang:display({reduction_limit, RedsLimit}),
2280    erlang:display({reductions, Reds}),
2281    done.
2282
2283do_it(Tracer, Low, Normal, High, Max) ->
2284    do_it(Tracer, Low, Normal, High, Max, ?DEFAULT_TEST_REDS_PER_SCHED).
2285
2286do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) ->
2287    OldPrio = process_flag(priority, max),
2288    go_work(Low, Normal, High, Max),
2289    StartWait = erlang:monotonic_time(millisecond),
2290    %% Give the emulator a chance to balance the load...
2291    wait_balance(5),
2292    EndWait = erlang:monotonic_time(millisecond),
2293    BalanceWait = EndWait-StartWait,
2294    erlang:display({balance_wait, BalanceWait}),
2295    Timeout = (15 - 4)*60*1000 - BalanceWait,
2296    Res = case Timeout < 60*1000 of
2297	      true ->
2298		  stop_work(Low, Normal, High, Max),
2299		  too_slow;
2300	      false ->
2301		  set_tracing(true, Tracer, normal, Normal),
2302		  set_tracing(true, Tracer, low, Low),
2303		  set_tracing(true, Tracer, high, High),
2304		  set_tracing(true, Tracer, max, Max),
2305		  wait_reds(RedsPerSchedLimit
2306			    * erlang:system_info(schedulers_online),
2307			    Timeout),
2308		  set_tracing(false, Tracer, normal, Normal),
2309		  set_tracing(false, Tracer, low, Low),
2310		  set_tracing(false, Tracer, high, High),
2311		  set_tracing(false, Tracer, max, Max),
2312		  stop_work(Low, Normal, High, Max),
2313		  get_trace_result(Tracer)
2314	  end,
2315    process_flag(priority, OldPrio),
2316    Res.
2317
2318workers_exit([]) ->
2319    ok;
2320workers_exit([P|Ps]) when is_pid(P) ->
2321    Mon = erlang:monitor(process, P),
2322    unlink(P),
2323    exit(P, kill),
2324    workers_exit(Ps),
2325    receive {'DOWN', Mon, process, P, _} -> ok end,
2326    ok;
2327workers_exit([[]]) ->
2328    ok;
2329workers_exit([Ps|Pss])  ->
2330    workers_exit(Ps),
2331    workers_exit(Pss).
2332
2333do_work(PartTime) ->
2334    _ = id(lists:seq(1, 50)),
2335    receive stop_work -> receive after infinity -> ok end after 0 -> ok end,
2336    case PartTime of
2337	true -> receive after 1 -> ok end;
2338	false -> ok
2339    end,
2340    do_work(PartTime).
2341
2342id(I) -> I.
2343
2344workers(N, _Prio, _PartTime) when N =< 0 ->
2345    [];
2346workers(N, Prio, PartTime) ->
2347    Parent = self(),
2348    W = spawn_opt(fun () ->
2349			  Parent ! {ready, self()},
2350			  receive
2351			      go_work ->
2352				  do_work(PartTime)
2353			  end
2354		  end,
2355		  [{priority, Prio}, link]),
2356    Ws = workers(N-1, Prio, PartTime),
2357    receive {ready, W} -> ok end,
2358    [W|Ws].
2359
2360workers(N, Prio) ->
2361    workers(N, Prio, false).
2362
2363part_time_workers(N, Prio) ->
2364    workers(N, Prio, true).
2365
2366tracer(Low, Normal, High, Max) ->
2367    receive
2368        {tracees, Prio, Tracees} ->
2369            save_tracees(Prio, Tracees),
2370            case Prio of
2371                low -> tracer(Tracees++Low, Normal, High, Max);
2372                normal -> tracer(Low, Tracees++Normal, High, Max);
2373                high -> tracer(Low, Normal, Tracees++High, Max);
2374                max -> tracer(Low, Normal, High, Tracees++Max)
2375            end;
2376        {get_result, Ref, Who} ->
2377            Delivered = erlang:trace_delivered(all),
2378            receive
2379                {trace_delivered, all, Delivered} ->
2380                    ok
2381            end,
2382            {Lc, Nc, Hc, Mc} = read_trace(),
2383            GetMinMax
2384            = fun (Prio, Procs) ->
2385                      LargeNum = 1 bsl 64,
2386                      case lists:foldl(fun (P, {Mn, Mx} = MnMx) ->
2387                                               {Prio, C} = get(P),
2388                                               case C < Mn of
2389                                                   true ->
2390                                                       case C > Mx of
2391                                                           true ->
2392                                                               {C, C};
2393                                                           false ->
2394                                                               {C, Mx}
2395                                                       end;
2396                                                   false ->
2397                                                       case C > Mx of
2398                                                           true -> {Mn, C};
2399                                                           false -> MnMx
2400                                                       end
2401                                               end
2402                                       end,
2403                                       {LargeNum, 0},
2404                                       Procs) of
2405                          {LargeNum, 0} -> {0, 0};
2406                          Res -> Res
2407                      end
2408              end,
2409            {Lmin, Lmax} = GetMinMax(low, Low),
2410            {Nmin, Nmax} = GetMinMax(normal, Normal),
2411            {Hmin, Hmax} = GetMinMax(high, High),
2412            {Mmin, Mmax} = GetMinMax(max, Max),
2413            Who ! {trace_result, Ref, [{low, Lc, Lmin, Lmax},
2414                                       {normal, Nc, Nmin, Nmax},
2415                                       {high, Hc, Hmin, Hmax},
2416                                       {max, Mc, Mmin, Mmax}]}
2417    end.
2418
2419read_trace() ->
2420    read_trace(0,0,0,0).
2421
2422read_trace(Low, Normal, High, Max) ->
2423    receive
2424	{trace, Proc, in, _} ->
2425	    {Prio, Count} = get(Proc),
2426	    put(Proc, {Prio, Count+1}),
2427	    case Prio of
2428		low -> read_trace(Low+1, Normal, High, Max);
2429		normal -> read_trace(Low, Normal+1, High, Max);
2430		high -> read_trace(Low, Normal, High+1, Max);
2431		max -> read_trace(Low, Normal, High, Max+1)
2432	    end;
2433	{trace, _Proc, out, _} ->
2434	    read_trace(Low, Normal, High, Max)
2435    after 0 ->
2436	    {Low, Normal, High, Max}
2437    end.
2438
2439save_tracees(_Prio, []) ->
2440    ok;
2441save_tracees(Prio, [T|Ts]) ->
2442    put(T, {Prio, 0}),
2443    save_tracees(Prio, Ts).
2444
2445start_tracer() ->
2446    Tracer = spawn_link(fun () -> tracer([], [], [], []) end),
2447    true = erlang:suspend_process(Tracer),
2448    Tracer.
2449
2450get_trace_result(Tracer) ->
2451    erlang:resume_process(Tracer),
2452    Ref = make_ref(),
2453    Tracer ! {get_result, Ref, self()},
2454    receive
2455	{trace_result, Ref, Res} ->
2456	    Res
2457    end.
2458
2459
2460set_tracing(_On, _Tracer, _Prio, []) ->
2461    ok;
2462set_tracing(true, Tracer, Prio, Pids) ->
2463    Tracer ! {tracees, Prio, Pids},
2464    set_tracing(true, Tracer, Pids);
2465set_tracing(false, Tracer, _Prio, Pids) ->
2466    set_tracing(false, Tracer, Pids).
2467
2468set_tracing(_On, _Tracer, []) ->
2469    ok;
2470set_tracing(On, Tracer, [Pid|Pids]) ->
2471    1 = erlang:trace(Pid, On, [running, {tracer, Tracer}]),
2472    set_tracing(On, Tracer, Pids).
2473
2474active_schedulers() ->
2475    case erlang:system_info(schedulers_online) of
2476	1 ->
2477	    1;
2478	N ->
2479	    case erlang:system_info(multi_scheduling) of
2480		blocked -> 1;
2481		enabled -> N
2482	    end
2483    end.
2484
2485start_node(Config) ->
2486    start_node(Config, "").
2487
2488start_node(Config, Args) when is_list(Config) ->
2489    Pa = filename:dirname(code:which(?MODULE)),
2490    Name = list_to_atom(atom_to_list(?MODULE)
2491			++ "-"
2492			++ atom_to_list(proplists:get_value(testcase, Config))
2493			++ "-"
2494			++ integer_to_list(erlang:system_time(second))
2495			++ "-"
2496			++ integer_to_list(erlang:unique_integer([positive]))),
2497    test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
2498
2499stop_node(Node) ->
2500    test_server:stop_node(Node).
2501
2502
2503enable_internal_state() ->
2504    case catch erts_debug:get_internal_state(available_internal_state) of
2505	true -> true;
2506	_ -> erts_debug:set_internal_state(available_internal_state, true)
2507    end.
2508
2509cmp(X, X) ->
2510    ok;
2511cmp(X, Y) ->
2512    io:format("cmp failed:~n X=~p~n Y=~p~n", [X,Y]),
2513    cmp_aux(X, Y).
2514
2515
2516cmp_aux([X0|Y0], [X1|Y1]) ->
2517    cmp_aux(X0, X1),
2518    cmp_aux(Y0, Y1);
2519cmp_aux(T0, T1) when is_tuple(T0), is_tuple(T1), size(T0) == size(T1) ->
2520    cmp_tuple(T0, T1, 1, size(T0));
2521cmp_aux(X, X) ->
2522    ok;
2523cmp_aux(F0, F1) ->
2524    ct:fail({no_match, F0, F1}).
2525
2526cmp_tuple(_T0, _T1, N, Sz) when N > Sz ->
2527    ok;
2528cmp_tuple(T0, T1, N, Sz) ->
2529    cmp_aux(element(N, T0), element(N, T1)),
2530    cmp_tuple(T0, T1, N+1, Sz).
2531