已开启
新增嵌入式rt-test实时测试用例 #92
Algernon67创建于 2024年12月19日
新增嵌入式rt-test实时测试用例 #92
已开启
Algernon67创建于 2024年12月19日
refs/pull/92/head合入到master
78 个文件变更+20936-0
@@ -0,0 +1,339 @@
1+ GNU GENERAL PUBLIC LICENSE
2+ Version 2, June 1991
3+ 
4+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6+ Everyone is permitted to copy and distribute verbatim copies
7+ of this license document, but changing it is not allowed.
8+ 
9+ Preamble
10+ 
11+ The licenses for most software are designed to take away your
12+freedom to share and change it. By contrast, the GNU General Public
13+License is intended to guarantee your freedom to share and change free
14+software--to make sure the software is free for all its users. This
15+General Public License applies to most of the Free Software
16+Foundation's software and to any other program whose authors commit to
17+using it. (Some other Free Software Foundation software is covered by
18+the GNU Lesser General Public License instead.) You can apply it to
19+your programs, too.
20+ 
21+ When we speak of free software, we are referring to freedom, not
22+price. Our General Public Licenses are designed to make sure that you
23+have the freedom to distribute copies of free software (and charge for
24+this service if you wish), that you receive source code or can get it
25+if you want it, that you can change the software or use pieces of it
26+in new free programs; and that you know you can do these things.
27+ 
28+ To protect your rights, we need to make restrictions that forbid
29+anyone to deny you these rights or to ask you to surrender the rights.
30+These restrictions translate to certain responsibilities for you if you
31+distribute copies of the software, or if you modify it.
32+ 
33+ For example, if you distribute copies of such a program, whether
34+gratis or for a fee, you must give the recipients all the rights that
35+you have. You must make sure that they, too, receive or can get the
36+source code. And you must show them these terms so they know their
37+rights.
38+ 
39+ We protect your rights with two steps: (1) copyright the software, and
40+(2) offer you this license which gives you legal permission to copy,
41+distribute and/or modify the software.
42+ 
43+ Also, for each author's protection and ours, we want to make certain
44+that everyone understands that there is no warranty for this free
45+software. If the software is modified by someone else and passed on, we
46+want its recipients to know that what they have is not the original, so
47+that any problems introduced by others will not reflect on the original
48+authors' reputations.
49+ 
50+ Finally, any free program is threatened constantly by software
51+patents. We wish to avoid the danger that redistributors of a free
52+program will individually obtain patent licenses, in effect making the
53+program proprietary. To prevent this, we have made it clear that any
54+patent must be licensed for everyone's free use or not licensed at all.
55+ 
56+ The precise terms and conditions for copying, distribution and
57+modification follow.
58+ 
59+ GNU GENERAL PUBLIC LICENSE
60+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61+ 
62+ 0. This License applies to any program or other work which contains
63+a notice placed by the copyright holder saying it may be distributed
64+under the terms of this General Public License. The "Program", below,
65+refers to any such program or work, and a "work based on the Program"
66+means either the Program or any derivative work under copyright law:
67+that is to say, a work containing the Program or a portion of it,
68+either verbatim or with modifications and/or translated into another
69+language. (Hereinafter, translation is included without limitation in
70+the term "modification".) Each licensee is addressed as "you".
71+ 
72+Activities other than copying, distribution and modification are not
73+covered by this License; they are outside its scope. The act of
74+running the Program is not restricted, and the output from the Program
75+is covered only if its contents constitute a work based on the
76+Program (independent of having been made by running the Program).
77+Whether that is true depends on what the Program does.
78+ 
79+ 1. You may copy and distribute verbatim copies of the Program's
80+source code as you receive it, in any medium, provided that you
81+conspicuously and appropriately publish on each copy an appropriate
82+copyright notice and disclaimer of warranty; keep intact all the
83+notices that refer to this License and to the absence of any warranty;
84+and give any other recipients of the Program a copy of this License
85+along with the Program.
86+ 
87+You may charge a fee for the physical act of transferring a copy, and
88+you may at your option offer warranty protection in exchange for a fee.
89+ 
90+ 2. You may modify your copy or copies of the Program or any portion
91+of it, thus forming a work based on the Program, and copy and
92+distribute such modifications or work under the terms of Section 1
93+above, provided that you also meet all of these conditions:
94+ 
95+ a) You must cause the modified files to carry prominent notices
96+ stating that you changed the files and the date of any change.
97+ 
98+ b) You must cause any work that you distribute or publish, that in
99+ whole or in part contains or is derived from the Program or any
100+ part thereof, to be licensed as a whole at no charge to all third
101+ parties under the terms of this License.
102+ 
103+ c) If the modified program normally reads commands interactively
104+ when run, you must cause it, when started running for such
105+ interactive use in the most ordinary way, to print or display an
106+ announcement including an appropriate copyright notice and a
107+ notice that there is no warranty (or else, saying that you provide
108+ a warranty) and that users may redistribute the program under
109+ these conditions, and telling the user how to view a copy of this
110+ License. (Exception: if the Program itself is interactive but
111+ does not normally print such an announcement, your work based on
112+ the Program is not required to print an announcement.)
113+ 
114+These requirements apply to the modified work as a whole. If
115+identifiable sections of that work are not derived from the Program,
116+and can be reasonably considered independent and separate works in
117+themselves, then this License, and its terms, do not apply to those
118+sections when you distribute them as separate works. But when you
119+distribute the same sections as part of a whole which is a work based
120+on the Program, the distribution of the whole must be on the terms of
121+this License, whose permissions for other licensees extend to the
122+entire whole, and thus to each and every part regardless of who wrote it.
123+ 
124+Thus, it is not the intent of this section to claim rights or contest
125+your rights to work written entirely by you; rather, the intent is to
126+exercise the right to control the distribution of derivative or
127+collective works based on the Program.
128+ 
129+In addition, mere aggregation of another work not based on the Program
130+with the Program (or with a work based on the Program) on a volume of
131+a storage or distribution medium does not bring the other work under
132+the scope of this License.
133+ 
134+ 3. You may copy and distribute the Program (or a work based on it,
135+under Section 2) in object code or executable form under the terms of
136+Sections 1 and 2 above provided that you also do one of the following:
137+ 
138+ a) Accompany it with the complete corresponding machine-readable
139+ source code, which must be distributed under the terms of Sections
140+ 1 and 2 above on a medium customarily used for software interchange; or,
141+ 
142+ b) Accompany it with a written offer, valid for at least three
143+ years, to give any third party, for a charge no more than your
144+ cost of physically performing source distribution, a complete
145+ machine-readable copy of the corresponding source code, to be
146+ distributed under the terms of Sections 1 and 2 above on a medium
147+ customarily used for software interchange; or,
148+ 
149+ c) Accompany it with the information you received as to the offer
150+ to distribute corresponding source code. (This alternative is
151+ allowed only for noncommercial distribution and only if you
152+ received the program in object code or executable form with such
153+ an offer, in accord with Subsection b above.)
154+ 
155+The source code for a work means the preferred form of the work for
156+making modifications to it. For an executable work, complete source
157+code means all the source code for all modules it contains, plus any
158+associated interface definition files, plus the scripts used to
159+control compilation and installation of the executable. However, as a
160+special exception, the source code distributed need not include
161+anything that is normally distributed (in either source or binary
162+form) with the major components (compiler, kernel, and so on) of the
163+operating system on which the executable runs, unless that component
164+itself accompanies the executable.
165+ 
166+If distribution of executable or object code is made by offering
167+access to copy from a designated place, then offering equivalent
168+access to copy the source code from the same place counts as
169+distribution of the source code, even though third parties are not
170+compelled to copy the source along with the object code.
171+ 
172+ 4. You may not copy, modify, sublicense, or distribute the Program
173+except as expressly provided under this License. Any attempt
174+otherwise to copy, modify, sublicense or distribute the Program is
175+void, and will automatically terminate your rights under this License.
176+However, parties who have received copies, or rights, from you under
177+this License will not have their licenses terminated so long as such
178+parties remain in full compliance.
179+ 
180+ 5. You are not required to accept this License, since you have not
181+signed it. However, nothing else grants you permission to modify or
182+distribute the Program or its derivative works. These actions are
183+prohibited by law if you do not accept this License. Therefore, by
184+modifying or distributing the Program (or any work based on the
185+Program), you indicate your acceptance of this License to do so, and
186+all its terms and conditions for copying, distributing or modifying
187+the Program or works based on it.
188+ 
189+ 6. Each time you redistribute the Program (or any work based on the
190+Program), the recipient automatically receives a license from the
191+original licensor to copy, distribute or modify the Program subject to
192+these terms and conditions. You may not impose any further
193+restrictions on the recipients' exercise of the rights granted herein.
194+You are not responsible for enforcing compliance by third parties to
195+this License.
196+ 
197+ 7. If, as a consequence of a court judgment or allegation of patent
198+infringement or for any other reason (not limited to patent issues),
199+conditions are imposed on you (whether by court order, agreement or
200+otherwise) that contradict the conditions of this License, they do not
201+excuse you from the conditions of this License. If you cannot
202+distribute so as to satisfy simultaneously your obligations under this
203+License and any other pertinent obligations, then as a consequence you
204+may not distribute the Program at all. For example, if a patent
205+license would not permit royalty-free redistribution of the Program by
206+all those who receive copies directly or indirectly through you, then
207+the only way you could satisfy both it and this License would be to
208+refrain entirely from distribution of the Program.
209+ 
210+If any portion of this section is held invalid or unenforceable under
211+any particular circumstance, the balance of the section is intended to
212+apply and the section as a whole is intended to apply in other
213+circumstances.
214+ 
215+It is not the purpose of this section to induce you to infringe any
216+patents or other property right claims or to contest validity of any
217+such claims; this section has the sole purpose of protecting the
218+integrity of the free software distribution system, which is
219+implemented by public license practices. Many people have made
220+generous contributions to the wide range of software distributed
221+through that system in reliance on consistent application of that
222+system; it is up to the author/donor to decide if he or she is willing
223+to distribute software through any other system and a licensee cannot
224+impose that choice.
225+ 
226+This section is intended to make thoroughly clear what is believed to
227+be a consequence of the rest of this License.
228+ 
229+ 8. If the distribution and/or use of the Program is restricted in
230+certain countries either by patents or by copyrighted interfaces, the
231+original copyright holder who places the Program under this License
232+may add an explicit geographical distribution limitation excluding
233+those countries, so that distribution is permitted only in or among
234+countries not thus excluded. In such case, this License incorporates
235+the limitation as if written in the body of this License.
236+ 
237+ 9. The Free Software Foundation may publish revised and/or new versions
238+of the General Public License from time to time. Such new versions will
239+be similar in spirit to the present version, but may differ in detail to
240+address new problems or concerns.
241+ 
242+Each version is given a distinguishing version number. If the Program
243+specifies a version number of this License which applies to it and "any
244+later version", you have the option of following the terms and conditions
245+either of that version or of any later version published by the Free
246+Software Foundation. If the Program does not specify a version number of
247+this License, you may choose any version ever published by the Free Software
248+Foundation.
249+ 
250+ 10. If you wish to incorporate parts of the Program into other free
251+programs whose distribution conditions are different, write to the author
252+to ask for permission. For software which is copyrighted by the Free
253+Software Foundation, write to the Free Software Foundation; we sometimes
254+make exceptions for this. Our decision will be guided by the two goals
255+of preserving the free status of all derivatives of our free software and
256+of promoting the sharing and reuse of software generally.
257+ 
258+ NO WARRANTY
259+ 
260+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268+REPAIR OR CORRECTION.
269+ 
270+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278+POSSIBILITY OF SUCH DAMAGES.
279+ 
280+ END OF TERMS AND CONDITIONS
281+ 
282+ How to Apply These Terms to Your New Programs
283+ 
284+ If you develop a new program, and you want it to be of the greatest
285+possible use to the public, the best way to achieve this is to make it
286+free software which everyone can redistribute and change under these terms.
287+ 
288+ To do so, attach the following notices to the program. It is safest
289+to attach them to the start of each source file to most effectively
290+convey the exclusion of warranty; and each file should have at least
291+the "copyright" line and a pointer to where the full notice is found.
292+ 
293+ <one line to give the program's name and a brief idea of what it does.>
294+ Copyright (C) <year> <name of author>
295+ 
296+ This program is free software; you can redistribute it and/or modify
297+ it under the terms of the GNU General Public License as published by
298+ the Free Software Foundation; either version 2 of the License, or
299+ (at your option) any later version.
300+ 
301+ This program is distributed in the hope that it will be useful,
302+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304+ GNU General Public License for more details.
305+ 
306+ You should have received a copy of the GNU General Public License along
307+ with this program; if not, write to the Free Software Foundation, Inc.,
308+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309+ 
310+Also add information on how to contact you by electronic and paper mail.
311+ 
312+If the program is interactive, make it output a short notice like this
313+when it starts in an interactive mode:
314+ 
315+ Gnomovision version 69, Copyright (C) year name of author
316+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317+ This is free software, and you are welcome to redistribute it
318+ under certain conditions; type `show c' for details.
319+ 
320+The hypothetical commands `show w' and `show c' should show the appropriate
321+parts of the General Public License. Of course, the commands you use may
322+be called something other than `show w' and `show c'; they could even be
323+mouse-clicks or menu items--whatever suits your program.
324+ 
325+You should also get your employer (if you work as a programmer) or your
326+school, if any, to sign a "copyright disclaimer" for the program, if
327+necessary. Here is a sample; alter the names:
328+ 
329+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331+ 
332+ <signature of Ty Coon>, 1 April 1989
333+ Ty Coon, President of Vice
334+ 
335+This General Public License does not permit incorporating your program into
336+proprietary programs. If your program is a subroutine library, you may
337+consider it more useful to permit linking proprietary applications with the
338+library. If this is what you want to do, use the GNU Lesser General
339+Public License instead of this License.
@@ -0,0 +1,26 @@
1+# Use CentOS Stream 9 as base image
L
Llinqian03222024年12月20日

这里应该要刷新我们oe的镜像?

likedislike
2+FROM centos:stream9
3+ 
4+# Install required packages for RT-Tests
5+RUN dnf -y update && \
6+ dnf install -y \
7+ gcc \
8+ make \
9+ numactl-devel \
10+ util-linux \
11+ git && \
12+ git clone https://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git /opt/rt-tests && \
L
Llinqian03222024年12月20日

这是上游的地址吗 拉下来是否要换成当前的地址

likedislike
13+ cd /opt/rt-tests && \
14+ make && \
15+ make install && \
16+ dnf remove -y \
17+ git \
18+ make && \
19+ dnf clean all
20+ 
21+ 
22+# Set the working directory to the RT-Tests directory
23+WORKDIR /opt/rt-tests
24+ 
25+# Set the entrypoint to a shell
26+ENTRYPOINT ["/bin/bash"]
@@ -0,0 +1,278 @@
1+# SPDX-License-Identifier: GPL-2.0-or-later
2+include feature/test-feature.mak
3+ 
4+VERSION = 2.8
5+CC = $(CROSS_COMPILE)gcc
6+AR = $(CROSS_COMPILE)ar
7+ 
8+OBJDIR = bld
9+ 
10+sources = cyclictest.c \
11+ hackbench.c \
12+ pip_stress.c \
13+ pi_stress.c \
14+ pmqtest.c \
15+ ptsematest.c \
16+ rt-migrate-test.c \
17+ signaltest.c \
18+ sigwaittest.c \
19+ svsematest.c \
20+ cyclicdeadline.c \
21+ deadline_test.c \
22+ queuelat.c \
23+ ssdd.c \
24+ oslat.c
25+ 
26+TARGETS = $(sources:.c=)
27+LIBS = -lrt -lpthread
28+RTTESTLIB = -lrttest -L$(OBJDIR)
29+EXTRA_LIBS ?= -ldl # for get_cpu
30+RTTESTNUMA = -lrttestnuma -lnuma
31+DESTDIR ?=
32+prefix ?= /usr/local
33+bindir ?= $(prefix)/bin
34+mandir ?= $(prefix)/share/man
35+ 
36+CFLAGS ?= -Wall -Wno-nonnull -Wextra -Wno-sign-compare
37+CPPFLAGS += -D_GNU_SOURCE -Isrc/include
38+LDFLAGS ?=
39+ 
40+PYLIB ?= $(shell python3 -m get_pylib)
41+ 
42+# Check for optional libcpupower dependency
43+ifneq ($(no_libcpupower), 1)
44+ifeq ($(call test-feature,libcpupower), 0)
45+CPPFLAGS += -DHAVE_LIBCPUPOWER_SUPPORT
46+LIBCPUPOWER += -lcpupower
47+else
48+$(warning libcpupower is missing, building without --deepest-idle-state support.)
49+$(warning Please install libcpupower-dev/kernel-tools-libs-devel)
50+endif
51+else
52+$(warning libcpupower disabled, building without --deepest-idle-state support.)
53+endif
54+ 
55+# Check for errors, such as python3 not available
56+ifeq (${PYLIB},)
57+ undefine PYLIB
58+endif
59+ 
60+MANPAGES = src/cyclictest/cyclictest.8 \
61+ src/pi_tests/pi_stress.8 \
62+ src/ptsematest/ptsematest.8 \
63+ src/rt-migrate-test/rt-migrate-test.8 \
64+ src/sigwaittest/sigwaittest.8 \
65+ src/svsematest/svsematest.8 \
66+ src/pmqtest/pmqtest.8 \
67+ src/hackbench/hackbench.8 \
68+ src/signaltest/signaltest.8 \
69+ src/pi_tests/pip_stress.8 \
70+ src/queuelat/queuelat.8 \
71+ src/queuelat/determine_maximum_mpps.8 \
72+ src/sched_deadline/deadline_test.8 \
73+ src/ssdd/ssdd.8 \
74+ src/sched_deadline/cyclicdeadline.8 \
75+ src/oslat/oslat.8
76+ 
77+ifdef PYLIB
78+ MANPAGES += src/cyclictest/get_cyclictest_snapshot.8 \
79+ src/hwlatdetect/hwlatdetect.8
80+endif
81+ 
82+ifeq ($(MAN_COMPRESSION),gzip)
83+ MANPAGES := $(MANPAGES:.8=.8.gz)
84+else ifeq ($(MAN_COMPRESSION),bzip2)
85+ MANPAGES := $(MANPAGES:.8=.8.bz2)
86+endif
87+ 
88+ifndef DEBUG
89+ CFLAGS += -O2 -g
90+else
91+ CFLAGS += -O0 -g
92+endif
93+ 
94+# We make some gueses on how to compile rt-tests based on the machine type
95+# and the ostype. These can often be overridden.
96+dumpmachine := $(shell $(CC) -dumpmachine)
97+ 
98+# The ostype is typically something like linux or android
99+ostype := $(lastword $(subst -, ,$(dumpmachine)))
100+ 
101+machinetype := $(shell echo $(dumpmachine)| \
102+ sed -e 's/-.*//' -e 's/i.86/i386/' -e 's/mips.*/mips/' -e 's/ppc.*/powerpc/')
103+ 
104+include src/arch/android/Makefile
105+ 
106+VPATH = src/cyclictest:
107+VPATH += src/signaltest:
108+VPATH += src/pi_tests:
109+VPATH += src/rt-migrate-test:
110+VPATH += src/ptsematest:
111+VPATH += src/sigwaittest:
112+VPATH += src/svsematest:
113+VPATH += src/pmqtest:
114+VPATH += src/lib:
115+VPATH += src/hackbench:
116+VPATH += src/sched_deadline:
117+VPATH += src/queuelat:
118+VPATH += src/ssdd:
119+VPATH += src/oslat:
120+ 
121+$(OBJDIR)/%.o: %.c | $(OBJDIR)
122+ $(CC) -D VERSION=$(VERSION) -c $< $(CFLAGS) $(CPPFLAGS) -o $@
123+ 
124+# Pattern rule to generate dependency files from .c files
125+$(OBJDIR)/%.d: %.c | $(OBJDIR)
126+ @$(CC) -MM $(CFLAGS) $(CPPFLAGS) $< | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' > $@ || rm -f $@
127+ 
128+.PHONY: all
129+all: $(TARGETS) hwlatdetect get_cyclictest_snapshot | $(OBJDIR)
130+ 
131+$(OBJDIR):
132+ mkdir $(OBJDIR)
133+ 
134+# Include dependency files, automatically generate them if needed.
135+-include $(addprefix $(OBJDIR)/,$(sources:.c=.d))
136+ 
137+cyclictest: $(OBJDIR)/cyclictest.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a
138+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA) $(LIBCPUPOWER)
139+ 
140+cyclicdeadline: $(OBJDIR)/cyclicdeadline.o $(OBJDIR)/librttest.a
141+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
142+ 
143+deadline_test: $(OBJDIR)/deadline_test.o $(OBJDIR)/librttest.a
144+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
145+ 
146+signaltest: $(OBJDIR)/signaltest.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a
147+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA)
148+ 
149+pi_stress: $(OBJDIR)/pi_stress.o $(OBJDIR)/librttest.a
150+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
151+ 
152+hwlatdetect: src/hwlatdetect/hwlatdetect.py
153+ chmod +x src/hwlatdetect/hwlatdetect.py
154+ ln -s src/hwlatdetect/hwlatdetect.py hwlatdetect
155+ 
156+get_cyclictest_snapshot: src/cyclictest/get_cyclictest_snapshot.py
157+ chmod +x src/cyclictest/get_cyclictest_snapshot.py
158+ ln -s src/cyclictest/get_cyclictest_snapshot.py get_cyclictest_snapshot
159+ 
160+rt-migrate-test: $(OBJDIR)/rt-migrate-test.o $(OBJDIR)/librttest.a
161+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
162+ 
163+ptsematest: $(OBJDIR)/ptsematest.o $(OBJDIR)/librttest.a
164+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS)
165+ 
166+sigwaittest: $(OBJDIR)/sigwaittest.o $(OBJDIR)/librttest.a
167+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS)
168+ 
169+svsematest: $(OBJDIR)/svsematest.o $(OBJDIR)/librttest.a
170+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS)
171+ 
172+pmqtest: $(OBJDIR)/pmqtest.o $(OBJDIR)/librttest.a
173+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS)
174+ 
175+pip_stress: $(OBJDIR)/pip_stress.o $(OBJDIR)/librttest.a
176+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
177+ 
178+hackbench: $(OBJDIR)/hackbench.o
179+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
180+ 
181+queuelat: $(OBJDIR)/queuelat.o $(OBJDIR)/librttest.a
182+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
183+ 
184+ssdd: $(OBJDIR)/ssdd.o $(OBJDIR)/librttest.a
185+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB)
186+ 
187+oslat: $(OBJDIR)/oslat.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a
188+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA)
189+ 
190+%.8.gz: %.8
191+ gzip -nc $< > $@
192+ 
193+%.8.bz2: %.8
194+ bzip2 -c $< > $@
195+ 
196+LIBOBJS =$(addprefix $(OBJDIR)/,rt-error.o rt-get_cpu.o rt-sched.o rt-utils.o \
197+ histogram.o)
198+$(OBJDIR)/librttest.a: $(LIBOBJS)
199+ $(AR) rcs $@ $^
200+ 
201+LIBNUMAOBJS =$(addprefix $(OBJDIR)/,rt-numa.o)
202+$(OBJDIR)/librttestnuma.a: $(LIBNUMAOBJS)
203+ $(AR) rcs $@ $^
204+ 
205+CLEANUP = $(TARGETS) *.o .depend *.*~ *.orig *.rej *.d *.a *.8.gz *.8.bz2
206+CLEANUP += $(if $(wildcard .git), ChangeLog)
207+ 
208+.PHONY: clean
209+clean:
210+ for F in $(CLEANUP); do find -type f -name $$F | xargs rm -f; done
211+ rm -f rt-tests-*.tar
212+ rm -f hwlatdetect
213+ rm -f get_cyclictest_snapshot
214+ rm -f tags
215+ 
216+RPMDIRS = BUILD BUILDROOT RPMS SRPMS SPECS
217+.PHONY: distclean
218+distclean: clean
219+ rm -rf $(RPMDIRS) releases *.tar.gz *.tar.asc tmp
220+ 
221+.PHONY: rebuild
222+rebuild:
223+ $(MAKE) clean
224+ $(MAKE) all
225+ 
226+.PHONY: install
227+install: all install_manpages install_hwlatdetect install_get_cyclictest_snapshot
228+ mkdir -p "$(DESTDIR)$(bindir)"
229+ cp $(TARGETS) "$(DESTDIR)$(bindir)"
230+ install src/queuelat/determine_maximum_mpps.sh "${DESTDIR}${bindir}"
231+ 
232+.PHONY: install_hwlatdetect
233+install_hwlatdetect: hwlatdetect
234+ if test -n "$(PYLIB)" ; then \
235+ mkdir -p "$(DESTDIR)$(bindir)" ; \
236+ install -D -m 755 src/hwlatdetect/hwlatdetect.py $(DESTDIR)$(PYLIB)/hwlatdetect.py ; \
237+ rm -f "$(DESTDIR)$(bindir)/hwlatdetect" ; \
238+ ln -s $(PYLIB)/hwlatdetect.py "$(DESTDIR)$(bindir)/hwlatdetect" ; \
239+ fi
240+ 
241+.PHONY: install_get_cyclictest_snapshot
242+install_get_cyclictest_snapshot: get_cyclictest_snapshot
243+ if test -n "${PYLIB}" ; then \
244+ mkdir -p "${DESTDIR}${bindir}" ; \
245+ install -D -m 755 src/cyclictest/get_cyclictest_snapshot.py ${DESTDIR}${PYLIB}/get_cyclictest_snapshot.py ; \
246+ rm -f "${DESTDIR}${bindir}/get_cyclictest_snapshot" ; \
247+ ln -s ${PYLIB}/get_cyclictest_snapshot.py "${DESTDIR}${bindir}/get_cyclictest_snapshot" ; \
248+ fi
249+ 
250+.PHONY: install_manpages
251+install_manpages: $(MANPAGES)
252+ mkdir -p "$(DESTDIR)$(mandir)/man8"
253+ cp $(MANPAGES) "$(DESTDIR)$(mandir)/man8"
254+ 
255+.PHONY: tarball
256+tarball:
257+ git archive --worktree-attributes --prefix=rt-tests-${VERSION}/ -o rt-tests-${VERSION}.tar v${VERSION}
258+ 
259+.PHONY: help
260+help:
261+ @echo ""
262+ @echo " rt-tests useful Makefile targets:"
263+ @echo ""
264+ @echo " all : build all tests (default"
265+ @echo " install : install tests to local filesystem"
266+ @echo " clean : remove object files"
267+ @echo " distclean : remove all generated files"
268+ @echo " tarball : make a rt-tests tarball suitable for release"
269+ @echo " help : print this message"
270+ 
271+# Universal Ctags warns about the backward compatible option '--extra' and
272+# wants it to be called '--extras'.
273+CTAGS_BIN = ctags
274+CTAGS_EXTRA := $(shell $(CTAGS_BIN) --version 2>&1 | grep -iq universal && echo extras || echo extra)
275+ 
276+.PHONY: tags
277+tags:
278+ $(CTAGS_BIN) -R --$(CTAGS_EXTRA)=+f --c-kinds=+p --exclude=tmp --exclude=BUILD *
The file is empty
@@ -0,0 +1,12 @@
1+# SPDX-License-Identifier: GPL-2.0-or-later
2+all: feature-libcpupower
3+ 
4+feature-libcpupower: $(OBJDIR)/test-libcpupower.o
5+ 
6+$(OBJDIR)/test-libcpupower.o: feature/test-libcpupower.c
7+ @$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -lcpupower -o $@
8+ 
9+.PHONY: clean
10+ 
11+clean:
12+ rm -f $(OBJDIR)/test-*.o
@@ -0,0 +1,5 @@
1+# SPDX-License-Identifier: GPL-2.0-or-later
2+define test-feature
3+$(shell $(MAKE) OBJDIR="$(OBJDIR)" CFLAGS="$(CFLAGS)" CPPFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS)" \
4+-f feature/Makefile feature-$1 clean >/dev/null 2>/dev/null; echo $$?)
5+endef
@@ -0,0 +1,8 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#include <cpuidle.h>
3+ 
4+int main(void)
5+{
6+ int rv = cpuidle_state_count(0);
7+ return rv;
8+}
@@ -0,0 +1,16 @@
1+#!/usr/bin/python3
2+ 
3+# This file is used during the make process
4+ 
5+import sysconfig
6+ 
7+# Older versions of python don't have this scheme
8+# but produce the answer we expect with 'posix_prefix'
9+# Newer versions of python insert 'local' in the path unless we use rpm_prefix
10+SCHEME = 'rpm_prefix'
11+ 
12+if not SCHEME in sysconfig.get_scheme_names():
13+ SCHEME = 'posix_prefix'
14+ 
15+PYLIB = sysconfig.get_path('purelib', SCHEME)
16+print(PYLIB)
@@ -0,0 +1,24 @@
1+ 
2+# Android use Bionic for libc, and this does not have
3+# - pthread barriers
4+# - pthread_[gs]etaffinity
5+#
6+# This is all handled by bionic.h based on flags we set here.
7+ 
8+# Typically see something like "aarch64-linux-android". However, in some
9+# buildsystems, it will be a variation of -androidabe.
10+ost=$(findstring android, $(ostype))
11+ifeq (android,$(ost))
12+ USE_BIONIC := 1
13+ CFLAGS += -DPTHREAD_BIONIC
14+ 
15+ LDFLAGS += -pie
16+# -lrt and -lpthread is in standard bionic library, no standalone library
17+ LIBS := $(filter-out -lrt,$(LIBS))
18+ LIBS := $(filter-out -lpthread,$(LIBS))
19+ 
20+# Currently, only these binaries will compile and link properly for android
21+# - cyclictest
22+# - hackbench
23+ sources := cyclictest.c hackbench.c
24+endif
@@ -0,0 +1,5 @@
1+Module.symvers
2+backfire.ko
3+backfire.mod.c
4+modules.order
5+ 
@@ -0,0 +1,17 @@
1+# If KERNELRELEASE is defined, we've been invoked from the
2+# kernel build system and can use its language
3+ifneq ($(KERNELRELEASE),)
4+obj-m := backfire.o
5+ 
6+# otherwise we were called directly from the command
7+# line; invoke the kernel build system.
8+else
9+KERNELDIR ?= /lib/modules/$(shell uname -r)/build
10+ 
11+modules modules_install clean::
12+ make -C $(KERNELDIR) M=$(CURDIR) $@
13+ 
14+clean::
15+ rm -f *.o Module.markers modules.order
16+ 
17+endif
@@ -0,0 +1,6 @@
1+obj-m := backfire.o
2+KDIR := /lib/modules/$(shell uname -r)/build
3+PWD := $(shell pwd)
4+ 
5+default:
6+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
@@ -0,0 +1,37 @@
1+.TH "backfire" "4" "0.1" "" "Driver"
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+.SH "NAME"
4+.LP
5+backfire \- send a signal from driver to user
6+.SH "DESCRIPTION"
7+.LP
8+The \fBbackfire\fR driver reads a numerical string that is sent to the
9+\fB/dev/backfire\fR device and sends the corresponding signal to the calling
10+user program. Reading from \fB/dev/backfire\fR returns the time of the day
11+when the most recent sent request was serviced or 0, if a sent request was
12+not yet received. The time of the day is displayed in seconds since
13+1970-01-01 00:00:00 UTC followed by the fraction of the second in
14+microseconds separated by a comma.
15+.SH "PURPOSE"
16+.LP
17+The \fBbackfire\fR driver is normally used in combination with the program
18+\fBsendme\fR to benchmark the performance of the kernel's signal sending
19+capabilities.
20+.SH "EXAMPLES"
21+.LP
22+.nf
23+head -1 /dev/backfire
24+0,0
25+trap "echo Got signal 7" 7
26+echo 7 >/dev/backfire
27+Got signal 7
28+head -1 /dev/backfire
29+1234567890,123456
30+.fi
31+.LP
32+.SH "AUTHORS"
33+.LP
34+Carsten Emde <C.Emde@osadl.org>
35+.SH "SEE ALSO"
36+.LP
37+sendme(8)
@@ -0,0 +1,135 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * backfire - send signal back to caller
4+ * Copyright (C) 2007 Carsten Emde <C.Emde@osadl.org>
5+ */
6+ 
7+#include <linux/module.h>
8+ 
9+#include <linux/sched.h>
10+#include <linux/cpumask.h>
11+#include <linux/time.h>
12+#include <linux/smp_lock.h>
13+#include <linux/types.h>
14+#include <linux/errno.h>
15+#include <linux/miscdevice.h>
16+#include <linux/proc_fs.h>
17+#include <linux/spinlock.h>
18+#include <linux/uaccess.h>
19+ 
20+#include <asm/system.h>
21+ 
22+#define BACKFIRE_MINOR MISC_DYNAMIC_MINOR
23+ 
24+static spinlock_t backfire_state_lock = SPIN_LOCK_UNLOCKED;
25+static int backfire_open_cnt; /* #times opened */
26+static int backfire_open_mode; /* special open modes */
27+static struct timeval sendtime; /* when the most recent signal was sent */
28+#define BACKFIRE_WRITE 1 /* opened for writing (exclusive) */
29+#define BACKFIRE_EXCL 2 /* opened with O_EXCL */
30+ 
31+/*
32+ * These are the file operation function for user access to /dev/backfire
33+ */
34+static ssize_t
35+backfire_read(struct file *file, char *buf, size_t count, loff_t *ppos)
36+{
37+ return snprintf(buf, count, "%d,%d\n", (int) sendtime.tv_sec,
38+ (int) sendtime.tv_usec);
39+}
40+ 
41+static ssize_t
42+backfire_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
43+{
44+ int signo;
45+ struct pid *pid;
46+ 
47+ if (sscanf(buf, "%d", &signo) >= 1) {
48+ if (signo > 0 && signo < 32) {
49+ pid = get_pid(task_pid(current));
50+ do_gettimeofday(&sendtime);
51+ kill_pid(pid, signo, 1);
52+ } else
53+ printk(KERN_ERR "Invalid signal no. %d\n", signo);
54+ }
55+ return strlen(buf);
56+}
57+ 
58+static int
59+backfire_open(struct inode *inode, struct file *file)
60+{
61+ spin_lock(&backfire_state_lock);
62+ 
63+ if ((backfire_open_cnt && (file->f_flags & O_EXCL)) ||
64+ (backfire_open_mode & BACKFIRE_EXCL)) {
65+ spin_unlock(&backfire_state_lock);
66+ return -EBUSY;
67+ }
68+ 
69+ if (file->f_flags & O_EXCL)
70+ backfire_open_mode |= BACKFIRE_EXCL;
71+ if (file->f_mode & 2)
72+ backfire_open_mode |= BACKFIRE_WRITE;
73+ backfire_open_cnt++;
74+ 
75+ spin_unlock(&backfire_state_lock);
76+ 
77+ return 0;
78+}
79+ 
80+static int
81+backfire_release(struct inode *inode, struct file *file)
82+{
83+ spin_lock(&backfire_state_lock);
84+ 
85+ backfire_open_cnt--;
86+ 
87+ if (backfire_open_cnt == 1 && backfire_open_mode & BACKFIRE_EXCL)
88+ backfire_open_mode &= ~BACKFIRE_EXCL;
89+ if (file->f_mode & 2)
90+ backfire_open_mode &= ~BACKFIRE_WRITE;
91+ 
92+ spin_unlock(&backfire_state_lock);
93+ 
94+ return 0;
95+}
96+ 
97+static struct file_operations backfire_fops = {
98+ .owner = THIS_MODULE,
99+ .llseek = no_llseek,
100+ .open = backfire_open,
101+ .read = backfire_read,
102+ .write = backfire_write,
103+ .release = backfire_release,
104+};
105+ 
106+static struct miscdevice backfire_dev = {
107+ BACKFIRE_MINOR,
108+ "backfire",
109+ &backfire_fops
110+};
111+ 
112+static int __init backfire_init(void)
113+{
114+ int ret;
115+ 
116+ ret = misc_register(&backfire_dev);
117+ if (ret)
118+ printk(KERN_ERR "backfire: can't register dynamic misc device\n");
119+ else
120+ printk(KERN_INFO "backfire driver misc device %d\n",
121+ backfire_dev.minor);
122+ return ret;
123+}
124+ 
125+static void __exit backfire_exit(void)
126+{
127+ misc_deregister(&backfire_dev);
128+}
129+ 
130+module_init(backfire_init);
131+module_exit(backfire_exit);
132+ 
133+MODULE_LICENSE("GPL");
134+MODULE_AUTHOR("Carsten Emde <C.Emde@osadl.org>");
135+MODULE_DESCRIPTION("Send signal back to caller");
@@ -0,0 +1,43 @@
1+.TH "sendme" "8" "0.2" "" ""
2+# SPDX-License-Identifier: GPL-2.0-only
3+.SH "NAME"
4+.LP
5+\fBsendme\fR \- Send a signal from driver to user and measure time intervals
6+.SH "SYNTAX"
7+.LP
8+sendme [-a|-a PROC] [-b USEC] [-l loops] [-p PRIO]
9+.br
10+.SH "DESCRIPTION"
11+.LP
12+The program \fBsendme\fR uses the \fBbackfire\fR driver to send a signal from driver to user. It then reads the timestamp from the driver and calculates the time intervals to call the driver and to receive the signal from the driver.
13+.SH "OPTIONS"
14+.TP
15+.B \-a, \-\-affinity[=PROC]
16+Run on processor number PROC. If PROC is not specified, run on current processor.
17+.TP
18+.B \-b, \-\-breaktrace=USEC
19+Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch.
20+It is useful to track down unexpected large latencies on a system.
21+.TP
22+.B \-l, \-\-loops=LOOPS
23+Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. Sendme is stopped once the number of timer intervals has been reached.
24+.TP
25+.B \-p, \-\-prio=PRIO
26+Set the priority of the process.
27+.SH "FILES"
28+backfire.ko
29+.SH "EXAMPLES"
30+.LP
31+.nf
32+# modprobe backfire
33+# sendme -a -p99 -l1000000
34+Samples: 1000000
35+To: Min 0, Cur 0, Avg 1, Max 11
36+From: Min 2, Cur 3, Avg 3, Max 43
37+.fi
38+.SH "AUTHORS"
39+.LP
40+Carsten Emde <C.Emde@osadl.org>
41+.SH "SEE ALSO"
42+.LP
43+backfire(4)
@@ -0,0 +1,289 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * sendme.c
4+ *
5+ * Copyright (C) 2009 Carsten Emde <C.Emde@osadl.org>
6+ *
7+ */
8+#include <stdio.h>
9+#include <stdlib.h>
10+#include <unistd.h>
11+#include <limits.h>
12+#include <fcntl.h>
13+#include <getopt.h>
14+#include <signal.h>
15+#include <sched.h>
16+#include <string.h>
17+#include <time.h>
18+#include <errno.h>
19+#include "rt-utils.h"
20+#include "rt-get_cpu.h"
21+ 
22+#include <utmpx.h>
23+#include <sys/types.h>
24+#include <sys/stat.h>
25+#include <sys/time.h>
26+#include <sys/mman.h>
27+ 
28+#define SIGTEST SIGHUP
29+ 
30+enum {
31+ AFFINITY_UNSPECIFIED,
32+ AFFINITY_SPECIFIED,
33+ AFFINITY_USECURRENT
34+};
35+static int setaffinity = AFFINITY_UNSPECIFIED;
36+ 
37+static int affinity;
38+static int tracelimit;
39+static int priority;
40+static int shutdown;
41+static int max_cycles;
42+static volatile struct timeval after;
43+static int interval = 1000;
44+ 
45+static int kernvar(int mode, const char *name, char *value, size_t sizeofvalue)
46+{
47+ char filename[128];
48+ char *fileprefix = get_debugfileprefix();
49+ int retval = 1;
50+ int path;
51+ size_t len_prefix = strlen(fileprefix), len_name = strlen(name);
52+ 
53+ if (len_prefix + len_name + 1 > sizeof(filename)) {
54+ errno = ENOMEM;
55+ return 1;
56+ }
57+ 
58+ memcpy(filename, fileprefix, len_prefix);
59+ memcpy(filename + len_prefix, name, len_name + 1);
60+ 
61+ path = open(filename, mode);
62+ if (path >= 0) {
63+ if (mode == O_RDONLY) {
64+ int got;
65+ if ((got = read(path, value, sizeofvalue)) > 0) {
66+ retval = 0;
67+ value[got-1] = '\0';
68+ }
69+ } else if (mode == O_WRONLY) {
70+ if (write(path, value, sizeofvalue) == sizeofvalue)
71+ retval = 0;
72+ }
73+ close(path);
74+ }
75+ return retval;
76+}
77+ 
78+void signalhandler(int signo)
79+{
80+ struct timeval tv;
81+ 
82+ gettimeofday(&tv, NULL);
83+ after = tv;
84+ if (signo == SIGINT || signo == SIGTERM)
85+ shutdown = 1;
86+}
87+ 
88+void stop_tracing(void)
89+{
90+ kernvar(O_WRONLY, "tracing_enabled", "0", 1);
91+}
92+ 
93+static void display_help(void)
94+{
95+ printf("sendme V %1.2f\n", VERSION);
96+ puts("Usage: sendme <options>");
97+ puts("Function: send a signal from driver to userspace");
98+ puts(
99+ "Options:\n"
100+ "-a [NUM] --affinity pin to current processor\n"
101+ " with NUM pin to processor NUM\n"
102+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
103+ "-i INTV --interval=INTV base interval of thread in us default=1000\n"
104+ "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n"
105+ "-p PRIO --prio=PRIO priority\n");
106+ exit(1);
107+}
108+ 
109+static void process_options (int argc, char *argv[])
110+{
111+ int error = 0;
112+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
113+ 
114+ for (;;) {
115+ int option_index = 0;
116+ /** Options for getopt */
117+ static struct option long_options[] = {
118+ {"affinity", optional_argument, NULL, 'a'},
119+ {"breaktrace", required_argument, NULL, 'b'},
120+ {"interval", required_argument, NULL, 'i'},
121+ {"loops", required_argument, NULL, 'l'},
122+ {"priority", required_argument, NULL, 'p'},
123+ {"help", no_argument, NULL, '?'},
124+ {NULL, 0, NULL, 0}
125+ };
126+ int c = getopt_long (argc, argv, "a::b:i:l:p:",
127+ long_options, &option_index);
128+ if (c == -1)
129+ break;
130+ switch (c) {
131+ case 'a':
132+ if (optarg != NULL) {
133+ affinity = atoi(optarg);
134+ setaffinity = AFFINITY_SPECIFIED;
135+ } else if (optind < argc && atoi(argv[optind])) {
136+ affinity = atoi(argv[optind]);
137+ setaffinity = AFFINITY_SPECIFIED;
138+ } else
139+ setaffinity = AFFINITY_USECURRENT;
140+ break;
141+ case 'b': tracelimit = atoi(optarg); break;
142+ case 'i': interval = atoi(optarg); break;
143+ case 'l': max_cycles = atoi(optarg); break;
144+ case 'p': priority = atoi(optarg); break;
145+ case '?': error = 1; break;
146+ }
147+ }
148+ 
149+ if (setaffinity == AFFINITY_SPECIFIED) {
150+ if (affinity < 0)
151+ error = 1;
152+ if (affinity >= max_cpus) {
153+ fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n",
154+ affinity, max_cpus);
155+ error = 1;
156+ }
157+ }
158+ 
159+ if (priority < 0 || priority > 99)
160+ error = 1;
161+ 
162+ if (error)
163+ display_help ();
164+}
165+ 
166+int main(int argc, char *argv[])
167+{
168+ int path;
169+ cpu_set_t mask;
170+ int policy = SCHED_FIFO;
171+ struct sched_param schedp;
172+ struct flock fl;
173+ int retval = 0;
174+ 
175+ process_options(argc, argv);
176+ 
177+ if (check_privs())
178+ return 1;
179+ 
180+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
181+ perror("mlockall");
182+ return 1;
183+ }
184+ 
185+ memset(&schedp, 0, sizeof(schedp));
186+ schedp.sched_priority = priority;
187+ sched_setscheduler(0, policy, &schedp);
188+ 
189+ if (setaffinity != AFFINITY_UNSPECIFIED) {
190+ CPU_ZERO(&mask);
191+ if (setaffinity == AFFINITY_USECURRENT) {
192+ get_cpu_setup();
193+ affinity = get_cpu();
194+ }
195+ CPU_SET(affinity, &mask);
196+ if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
197+ fprintf(stderr, "WARNING: Could not set CPU affinity "
198+ "to CPU #%d\n", affinity);
199+ }
200+ 
201+ path = open("/dev/backfire", O_RDWR);
202+ if (path < 0) {
203+ fprintf(stderr, "ERROR: Could not access backfire device, "
204+ "try 'modprobe backfire'\n"
205+ "If the module backfire can't be loaded, "
206+ "it may need to be built first.\n"
207+ "Execute 'cd src/backfire; make' in the "
208+ "rt-tests directory (requires rt-tests\n"
209+ "sources and kernel-devel package).\n");
210+ return 1;
211+ }
212+ fl.l_type = F_WRLCK;
213+ fl.l_whence = SEEK_SET;
214+ fl.l_start = 0;
215+ fl.l_len = 1;
216+ if (fcntl(path, F_SETLK, &fl) == -1) {
217+ fprintf(stderr, "ERRROR: backfire device locked\n");
218+ retval = 1;
219+ } else {
220+ char sigtest[8];
221+ char timestamp[32];
222+ struct timeval before, sendtime, diff;
223+ unsigned int diffno = 0;
224+ unsigned int mindiff1 = UINT_MAX, maxdiff1 = 0;
225+ unsigned int mindiff2 = UINT_MAX, maxdiff2 = 0;
226+ double sumdiff1 = 0.0, sumdiff2 = 0.0;
227+ 
228+ if (tracelimit)
229+ kernvar(O_WRONLY, "tracing_enabled", "1", 1);
230+ 
231+ sprintf(sigtest, "%d", SIGTEST);
232+ signal(SIGTEST, signalhandler);
233+ signal(SIGINT, signalhandler);
234+ signal(SIGTERM, signalhandler);
235+ 
236+ while (1) {
237+ struct timespec ts;
238+ 
239+ ts.tv_sec = interval / USEC_PER_SEC;
240+ ts.tv_nsec = (interval % USEC_PER_SEC) * 1000;
241+ 
242+ gettimeofday(&before, NULL);
243+ write(path, sigtest, strlen(sigtest));
244+ while (after.tv_sec == 0);
245+ read(path, timestamp, sizeof(timestamp));
246+ if (sscanf(timestamp, "%lu,%lu\n", &sendtime.tv_sec,
247+ &sendtime.tv_usec) != 2)
248+ break;
249+ diffno++;
250+ if(max_cycles && diffno >= max_cycles)
251+ shutdown = 1;
252+ 
253+ printf("Samples: %8d\n", diffno);
254+ timersub(&sendtime, &before, &diff);
255+ if (diff.tv_usec < mindiff1)
256+ mindiff1 = diff.tv_usec;
257+ if (diff.tv_usec > maxdiff1)
258+ maxdiff1 = diff.tv_usec;
259+ sumdiff1 += (double) diff.tv_usec;
260+ printf("To: Min %4d, Cur %4d, Avg %4d, Max %4d\n",
261+ mindiff1, (int) diff.tv_usec,
262+ (int) ((sumdiff1 / diffno) + 0.5),
263+ maxdiff1);
264+ 
265+ timersub(&after, &sendtime, &diff);
266+ if (diff.tv_usec < mindiff2)
267+ mindiff2 = diff.tv_usec;
268+ if (diff.tv_usec > maxdiff2)
269+ maxdiff2 = diff.tv_usec;
270+ sumdiff2 += (double) diff.tv_usec;
271+ printf("From: Min %4d, Cur %4d, Avg %4d, Max %4d\n",
272+ mindiff2, (int) diff.tv_usec,
273+ (int) ((sumdiff2 / diffno) + 0.5),
274+ maxdiff2);
275+ after.tv_sec = 0;
276+ if ((tracelimit && diff.tv_usec > tracelimit) ||
277+ shutdown) {
278+ if (tracelimit)
279+ stop_tracing();
280+ break;
281+ }
282+ nanosleep(&ts, NULL);
283+ printf("\033[3A");
284+ }
285+ }
286+ 
287+ close(path);
288+ return retval;
289+}
@@ -0,0 +1,198 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH CYCLICTEST 8 "April 22, 2016"
3+# SPDX-License-Identifier: GPL-2.0-only
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+cyclictest \- High resolution test program
18+.SH SYNOPSIS
19+.SY cyclictest
20+.RI "[ \-hfmnqrsvMS ] [\-a " proc " ] [\-A " align " ] [\-b " usec " ] [\-c " clock " ] [\-d " dist " ] \
21+[\-h " histogram " ] [\-i " intv " ] [\-\-json " filename " ] [\-l " loop " ] [\-o " red " ] \
22+[\-p " prio " ] [\-t " num " ] [\-D " time "] [\-w] [\-W] [\-y " policy " ] [ \-S | \-U ]"
23+ 
24+.\" .SH DESCRIPTION
25+.\" This manual page documents briefly the
26+.\" .B cyclictest commands.
27+.\" .PP
28+.\" \fI<whatever>\fP escape sequences to invode bold face and italics, respectively.
29+.\" \fBcyclictest\fP is a program that...
30+.SH OPTIONS
31+These programs follow the usual GNU command line syntax, with long
32+options starting with two dashes ('\-\-').
33+.br
34+A summary of options is included below.
35+.\" For a complete description, see the Info files.
36+.TP
37+.B \-a, \-\-affinity[=PROC-SET]
38+Run threads on the set of processors given by PROC-SET. If PROC-SET is not
39+specified, all processors will be used. Threads will be assigned to processors
40+in the set in numeric order, in a round\-robin fashion.
41+.br
42+The set of processors can be specified as A,B,C, or A-C, or A-B,D-F, and so on*.
43+The ! character can be used to negate a set. For example, !B-D means to use all
44+available CPUs except B through D. The cpu numbers are the same as shown in the
45+.I processor
46+field in /proc/cpuinfo. See numa(3) for more information on specifying CPU sets.
47+* Support for CPU sets requires libnuma version >= 2. For libnuma v1, PROC-SET,
48+if specified, must be a single CPU number.
49+.TP
50+.B \-A, \-\-align=USEC
51+Align thread wakeups to a specific offset in microseconds
52+.TP
53+.B \-b, \-\-breaktrace=USEC
54+Send break trace command when latency > USEC
55+.TP
56+.B \-c, \-\-clock=CLOCK
57+select clock
58+.br
59+0 = CLOCK_MONOTONIC (default)
60+.br
61+1 = CLOCK_REALTIME
62+.TP
63+.B \-\-deepest\-idle\-state=n
64+Reduce exit from idle latency by limiting idle state up to n on used cpus (-1 disables all idle states). Power management is not suppresed on other cpus.
65+.TP
66+.B \-\-default\-system
67+Don't attempt to tune the system from cyclictest. Power management is not suppressed. This might give poorer results, but will allow you to discover if you need to tune the system.
68+.TP
69+.B \-d, \-\-distance=DIST
70+Distance of thread intervals in us, default = 500
71+.TP
72+.B \-D, \-\-duration=TIME
73+Specify a length for the test run.
74+.br
75+Append 'm', 'h', or 'd' to specify minutes, hours or days.
76+.TP
77+.B \-F, \-\-fifo=<path>
78+Create a named pipe at path and write stats to it
79+.TP
80+.B \-h, \-\-histogram=US
81+Dump latency histogram to stdout after the run. US is the max latency time to be be tracked in microseconds. This option runs all threads at the same priority.
82+.TP
83+.B \-H, \-\-histofall=MAXLATENCYINUS
84+Same as -h except that an additional histogram column is displayed at the right that contains summary data of all thread histograms. If cyclictest runs a single thread only, the -H option is equivalent to -h.
85+.TP
86+.B \-\-histfile=<path>
87+Dump the latency histogram to <path> instead of stdout.
88+.TP
89+.B \-i, \-\-interval=INTV
90+Set the base interval of the thread(s) in microseconds (default is 1000us). This sets the interval of the first thread. See also \-d.
91+.TP
92+.B \-\-json=FILENAME
93+Write final results into FILENAME, JSON formatted.
94+.TP
95+.B \-\-laptop
96+Save battery when running cyclictest. This will give you poorer realtime results, but will not drain your battery so quickly.
97+.TP
98+.B \-\-latency=PM_Q0S
99+power management latency target value. This value is written to /dev/cpu_dma_latency and affects c-states. The default is 0
100+.TP
101+.B \-l, \-\-loops=LOOPS
102+Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. Cyclictest is stopped once the number of timer intervals has been reached.
103+.TP
104+.B \-\-mainaffinity=CPUSET
105+Run the main thread on CPU #N. This only affects the main thread and not the measurement threads
106+.TP
107+.B \-m, \-\-mlockall
108+Lock current and future memory allocations to prevent being paged out
109+.TP
110+.B \\-M, \-\-refresh_on_max
111+Delay updating the screen until a new max latency is hit. (useful for
112+running cyclictest on low-bandwidth connections)
113+.TP
114+.B \-N, \-\-nsecs
115+Show results in nanoseconds instead of microseconds, which is the default unit.
116+.TP
117+.B \-o, \-\-oscope=RED
118+Oscilloscope mode, reduce verbose output by RED.
119+.TP
120+.B \-p, \-\-prio=PRIO
121+Set the priority of the first thread. The given priority is set to the first test thread. Each further thread gets a lower priority:
122+Priority(Thread N) = max(Priority(Thread N\-1) \- 1, 0)
123+.TP
124+.B \-\-policy=NAME
125+set the scheduler policy of the measurement threads
126+where NAME is one of: other, normal, batch, idle, fifo, rr
127+.TP
128+.B \-\-priospread
129+spread priority levels starting at a specified value
130+.TP
131+.B \-q, \-\-quiet
132+Print a summary only on exit. Useful for automated tests, where only the summary output needs to be captured.
133+.TP
134+.B \-r, \-\-relative
135+Use relative timers instead of absolute. The default behaviour of the tests is to use absolute timers. This option is there for completeness and should not be used for reproducible tests.
136+.TP
137+.B \-R, \-\-resolution
138+Check clock resolution, calling clock_gettime() many times. List of lock_gettime() values will be reported with -X
139+.TP
140+.B \-\-secaligned [USEC]
141+align thread wakeups to the next full second and apply the optional offset.
142+.TP
143+.B \-s, \-\-system
144+Use sys_nanosleep and sys_setitimer instead of posix timers. Note, that \-s can only be used with one thread because itimers are per process and not per thread. \-s uses the nanosleep syscall and is not restricted to one thread.
145+.TP
146+.B \\-S, \-\-smp
147+Set options for standard testing on SMP systems. Equivalent to using
148+the options: "\-t \-a" as well keeping any specified priority
149+equal across all threads
150+.TP
151+.B \-\-spike=<trigger>
152+record all spikes > trigger
153+.TP
154+.B \-\-spike-nodes=[num of nodes]
155+These are the maximum number of spikes we can record.
156+.br
157+The default is 1024 if not specified.
158+.TP
159+.B \\-\-smi
160+Enable SMI count/detection on processors with SMI count support.
161+.TP
162+.B \-t, \-\-threads[=NUM]
163+Set the number of test threads (default is 1). Create NUM test threads. If NUM is not specified, NUM is set to
164+the number of available CPUs. See \-d, \-i and \-p for further information.
165+.TP
166+.B \-\-tracemark
167+write a trace mark when \-b latency is exceeded.
168+.TP
169+.B \-u, \-\-unbuffered
170+force unbuffered output for live processing
171+.TP
172+.B \-v, \-\-verbose
173+Output values on stdout for statistics. This option is used to gather statistical information about the latency distribution. The output is sent to stdout. The output format is:
174+ 
175+n:c:v
176+ 
177+where n=task number c=count v=latency value in us.
178+.TP
179+.B \-\-dbg_cyclictest
180+Print info userful for debugging cyclictest
181+.TP
182+.B \-x, \-\-posix_timers
183+Use POSIX timers instead of clock_nanosleep.
184+ 
185+.SH SEE ALSO
186+.BR numa (3),
187+.BR numactl (8),
188+.\" .br
189+.\" The programs are documented fully by
190+.\" .IR "The Rise and Fall of a Fooish Bar" ,
191+.\" available via the Info system.
192+.SH AUTHOR
193+cyclictest was written by Thomas Gleixner <tglx@linuxtronix.de>.
194+.PP
195+This manual page was written by Alessio Igor Bogani <abogani@texware.it>,
196+for the Debian project (but may be used by others).
197+.br
198+Updated by John Kacur <jkacur@redhat.com>
@@ -0,0 +1,45 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH GET_CYCLICTEST_SNAPSHOT 8 "July 6, 2020"
3+# SPDX-License-Identifier: GPL-2.0-or-later
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+get_cyclictest_snapshot \- Get a snapshot of running instances of cyclictest
18+.SH SYNOPSIS
19+.SY get_cyclictest_snapshot
20+.OP [\-h]\ [\-l]\ [\-s\ [pid [pid ...]]]
21+.br
22+.OP [\-p\ [pid\ [pid ...]]]
23+.SH OPTIONS
24+Without options, this program will send USR2 to cyclictest to create a snapshot and then print it out.
25+.br
26+.TP
27+Optional arguments:
28+.TP
29+.B -h, --help
30+show this help message and exit
31+.TP
32+.B -l, --list
33+list the main pid(s) of running instances of cyclictest
34+.TP
35+.B -s [pid [pid ...]], --snapshot [pid [pid ...]]
36+take a snapshot of running instances of cyclictest
37+.br
38+by sending USR2 to cyclictest
39+.TP
40+.B -p [pid [pid ...]], --print [pid [pid ...]]
41+print the snapshots
42+.SH SEE ALSO
43+.BR cyclictest (8),
44+.SH AUTHOR
45+get_cyclictest_snapshot was written by John Kacur <jkacur at redhat dot com>
@@ -0,0 +1,102 @@
1+#!/usr/bin/env python3
2+""" Program to get a snapshot of a running instance of cyclictest """
3+ 
4+# SPDX-License-Identifier: GPL-2.0-or-later
5+# Copyright (C) 2020 John Kacur <jkacur@redhat.com>
6+ 
7+import subprocess
8+import argparse
9+import re
10+import glob
11+import sys
12+ 
13+parser = argparse.ArgumentParser(description='Get a snapshot of running instances of cyclictest')
14+parser.add_argument('-l', '--list', action='store_true', help='list the main pid(s) of running instances of cyclictest')
15+parser.add_argument('-s', '--snapshot', nargs='*', metavar='pid', help='take a snapshot of running instances of cyclictest')
16+parser.add_argument('-p', '--print', nargs='*', metavar='pid', help='print the snapshots')
17+args = parser.parse_args()
18+ 
19+class Snapshot:
20+ """ Class for getting a snapshot of a running cyclictest instance """
21+ 
22+ warned = False
23+ 
24+ @classmethod
25+ def print_warning(cls):
26+ """ print a warning one time only even if called multiple times """
27+ if not cls.warned:
28+ cls.warned = True
29+ print("No cyclictest instance found")
30+ 
31+ def __init__(self):
32+ self.pids = []
33+ self.shm_files = []
34+ self.refresh()
35+ 
36+ def refresh(self):
37+ """ Create a list of running cyclictest instances. """
38+ self.pids = []
39+ self.shm_files = glob.glob('/dev/shm/cyclictest*')
40+ self.shm_files.sort()
41+ for shm_file in self.shm_files:
42+ pid = re.search('[0-9]*$', shm_file).group()
43+ self.pids += [pid]
44+ 
45+ def take_snapshot(self, spids=None):
46+ """ Send USR2 to all running instances of cyclictest,
47+ or just to a specific pid (spids) if specified. """
48+ if spids is None:
49+ if not self.pids:
50+ Snapshot.print_warning()
51+ for pid in self.pids:
52+ subprocess.run(["kill", "-s", "USR2", pid])
53+ else:
54+ for pid in spids:
55+ subprocess.run(["kill", "-s", "USR2", pid])
56+ 
57+ def print_pids(self):
58+ """ Print the list of pids of running cyclictest instances. """
59+ if not self.pids:
60+ Snapshot.print_warning()
61+ for pid in self.pids:
62+ print(pid)
63+ 
64+ def print(self, spids=None):
65+ """ Print the data in /dev/shm/cyclictest* """
66+ if spids is None:
67+ if not self.shm_files:
68+ Snapshot.print_warning()
69+ for shm_file in self.shm_files:
70+ with open(shm_file, 'r') as f:
71+ data = f.read()
72+ print(data)
73+ else:
74+ for spid in spids:
75+ if spid in self.pids:
76+ shm_file = '/dev/shm/cyclictest' + spid
77+ with open(shm_file, 'r') as f:
78+ data = f.read()
79+ print(data)
80+ else:
81+ Snapshot.print_warning()
82+ 
83+snapshot = Snapshot()
84+ 
85+if args.list:
86+ snapshot.print_pids()
87+ 
88+if args.snapshot is not None:
89+ if args.snapshot:
90+ snapshot.take_snapshot(args.snapshot)
91+ else:
92+ snapshot.take_snapshot()
93+ 
94+if args.print is not None:
95+ if args.print:
96+ snapshot.print(args.print)
97+ else:
98+ snapshot.print()
99+ 
100+if len(sys.argv) == 1:
101+ snapshot.take_snapshot()
102+ snapshot.print()
@@ -0,0 +1,85 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * A numa library for cyclictest.
4+ *
5+ * (C) 2010 John Kacur <jkacur@redhat.com>
6+ * (C) 2010 Clark Williams <williams@redhat.com>
7+ *
8+ */
9+ 
10+#ifndef _RT_NUMA_H
11+#define _RT_NUMA_H
12+ 
13+#include "rt-utils.h"
14+#include "rt-error.h"
15+ 
16+static int numa = 0;
17+ 
18+#include <numa.h>
19+ 
20+static void *
21+threadalloc(size_t size, int node)
22+{
23+ if (node == -1)
24+ return malloc(size);
25+ return numa_alloc_onnode(size, node);
26+}
27+ 
28+static void
29+threadfree(void *ptr, size_t size, int node)
30+{
31+ if (node == -1)
32+ free(ptr);
33+ else
34+ numa_free(ptr, size);
35+}
36+ 
37+static void rt_numa_set_numa_run_on_node(int node, int cpu)
38+{
39+ int res;
40+ res = numa_run_on_node(node);
41+ if (res)
42+ warn("Could not set NUMA node %d for thread %d: %s\n",
43+ node, cpu, strerror(errno));
44+ return;
45+}
46+ 
47+static void *rt_numa_numa_alloc_onnode(size_t size, int node, int cpu)
48+{
49+ void *stack;
50+ stack = numa_alloc_onnode(size, node);
51+ if (stack == NULL)
52+ fatal("failed to allocate %d bytes on node %d for cpu %d\n",
53+ size, node, cpu);
54+ return stack;
55+}
56+ 
57+/*
58+ * Use new bit mask CPU affinity behavior
59+ */
60+static int rt_numa_numa_node_of_cpu(int cpu)
61+{
62+ int node;
63+ node = numa_node_of_cpu(cpu);
64+ if (node == -1)
65+ fatal("invalid cpu passed to numa_node_of_cpu(%d)\n", cpu);
66+ return node;
67+}
68+ 
69+static inline unsigned int rt_numa_bitmask_isbitset( const struct bitmask *mask,
70+ unsigned long i)
71+{
72+ return numa_bitmask_isbitset(mask,i);
73+}
74+ 
75+static inline struct bitmask* rt_numa_parse_cpustring(const char* s)
76+{
77+ return numa_parse_cpustring_all(s);
78+}
79+ 
80+static inline void rt_bitmask_free(struct bitmask *mask)
81+{
82+ numa_bitmask_free(mask);
83+}
84+ 
85+#endif /* _RT_NUMA_H */
@@ -0,0 +1,114 @@
1+.TH "hackbench" "8" "September 19, 2020" "" ""
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+.SH "NAME"
4+hackbench \- scheduler benchmark/stress test
5+.SH "SYNOPSIS"
6+.B hackbench
7+.RI "[\-f|\-\-fds NUM] "
8+.RI "[\-F|\-\-fifo] "
9+.RI "[\-g|\-\-groups NUM] "
10+.RI "[\-h|\-\-help] "
11+.RI "[\-l|\-\-loops LOOPS] "
12+.RI "[\-p|\-\-pipe] "
13+.RI "[\-s|\-\-datasize SIZE] "
14+.RI "[\-T|\-\-threads]"
15+.RI "[\-P|\-\-process]"
16+ 
17+.SH "DESCRIPTION"
18+Hackbench is both a benchmark and a stress test for the Linux kernel
19+scheduler. It's main job is to create a specified number of pairs of
20+schedulable entities (either threads or traditional processes) which
21+communicate via either sockets or pipes and time how long it takes for
22+each pair to send data back and forth.
23+ 
24+.SH "OPTIONS"
25+These programs follow the usual GNU command line syntax, with long
26+options starting with two dashes ("\-\-").
27+.br
28+A summary of options is included below.
29+.TP
30+.B \-f, \-\-fds=NUM
31+Defines how many file descriptors each child should use.
32+Note that the effective number will be twice the amount you set here,
33+as the sender and receiver children will each open the given amount of
34+file descriptors.
35+.TP
36+.B \-F,\-\-fifo
37+Change the main thread to SCHED_FIFO after creating workers.
38+This allows the management thread to run after many workers are created.
39+.TP
40+.B \-g, \-\-groups=NUM
41+Defines how many groups of senders and receivers should be started
42+.TP
43+.B \-h, \-\-help
44+.TP
45+.B \-l, \-\-loops=LOOPS
46+How many messages each sender/receiver pair should send
47+.TP
48+.B \-p, \-\-pipe
49+Sends the data via a pipe instead of the socket (default)
50+.TP
51+.B \-s, \-\-datasize=SIZE
52+Sets the amount of data to send in each message
53+.TP
54+.B \-T, \-\-threads
55+Each sender/receiver child will be a POSIX thread of the parent.
56+.TP
57+.B \-P, \-\-process
58+Hackbench will use fork() on all children (default behaviour)
59+.br
60+Shows a simple help screen
61+.SH "EXAMPLES"
62+.LP
63+Running hackbench without any options will give default behaviour,
64+using fork() and sending data between senders and receivers via sockets.
65+.LP
66+user@host: ~ $ hackbench
67+.br
68+Running in process mode with 10 groups using 40 file descriptors each (== 400 tasks)
69+.br
70+Each sender will pass 100 messages of 100 bytes
71+.br
72+Time: 0.890
73+.LP
74+To use pipes between senders and receivers and using threads instead of fork(), run
75+.LP
76+user@host: ~ $ hackbench \-\-pipe \-\-threads (or hackbench \-p \-T)
77+.br
78+Running in threaded mode with 10 groups using 40 file descriptors each (== 400 tasks)
79+.br
80+Each sender will pass 100 messages of 100 bytes
81+.br
82+Time: 0.497
83+.LP
84+Set the datasize to 512 bytes, do 200 messages per sender/receiver pairs and use 15 groups
85+using 25 file descriptors per child, in process mode.
86+.LP
87+user@host: ~ $ hackbench \-s 512 \-l 200 \-g 15 \-f 25 \-P
88+.br
89+Running in process mode with 15 groups using 50 file descriptors each (== 750 tasks)
90+.br
91+Each sender will pass 200 messages of 512 bytes
92+.br
93+Time: 4.497
94+.SH "AUTHORS"
95+.LP
96+hackbench was written by Rusty Russell <rusty@rustcorp.com.au>
97+with contributions from Yanmin Zhang <yanmin_zhang@linux.intel.com>,
98+Ingo Molnar <mingo@elte.hu> and David Sommerseth <davids@redhat.com>
99+ 
100+This manual page was written by Clark Williams <williams@redhat.com>
101+and David Sommerseth <davids@redhat.com>
102+.SH "HISTORY"
103+This version of hackbench is based on the code downloaded from http://people.redhat.com/mingo/cfs\-scheduler/tools/hackbench.c.
104+Yanmin Zhang merged the original hackbench code from
105+.br
106+http://devresources.linuxfoundation.org/craiger/hackbench/src/hackbench.c
107+which uses fork() and a modified version from
108+.br
109+http://www.bullopensource.org/posix/pi\-futex/hackbench_pth.c
110+which uses pthread only and gave the possibility to change
111+behaviour at run time. Hackbench have since then gone through some
112+more rewriting to improve error handling and proper tracking of fork()ed
113+children, to avoid leaving zombies on the system if hackbench stops
114+unexpectedly.
@@ -0,0 +1,576 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * This is the latest version of hackbench.c, that tests scheduler and
4+ * unix-socket (or pipe) performance.
5+ *
6+ * Usage: hackbench [-pipe] <num groups> [process|thread] [loops]
7+ *
8+ * Build it with:
9+ * gcc -g -Wall -O2 -o hackbench hackbench.c -lpthread
10+ *
11+ * Downloaded from http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c
12+ * February 19 2010.
13+ *
14+ */
15+ 
16+/* Test groups of 20 processes spraying to 20 receivers */
17+#include <pthread.h>
18+#include <stdio.h>
19+#include <stdlib.h>
20+#include <string.h>
21+#include <errno.h>
22+#include <unistd.h>
23+#include <sys/ioctl.h>
24+#include <sys/types.h>
25+#include <sys/socket.h>
26+#include <sys/wait.h>
27+#include <sys/time.h>
28+#include <sys/poll.h>
29+#include <netinet/in.h>
30+#include <arpa/inet.h>
31+#include <limits.h>
32+#include <getopt.h>
33+#include <signal.h>
34+#include <setjmp.h>
35+#include <sched.h>
36+ 
37+static unsigned int datasize = 100;
38+static unsigned int loops = 100;
39+static unsigned int num_groups = 10;
40+static unsigned int num_fds = 20;
41+static unsigned int fifo = 0;
42+ 
43+/*
44+ * 0 means thread mode and others mean process (default)
45+ */
46+#define THREAD_MODE 0
47+#define PROCESS_MODE 1
48+ 
49+static unsigned int process_mode = PROCESS_MODE;
50+ 
51+static int use_pipes = 0;
52+static int use_inet = 0;
53+ 
54+struct sender_context {
55+ unsigned int num_fds;
56+ int ready_out;
57+ int wakefd;
58+ int out_fds[0];
59+};
60+ 
61+struct receiver_context {
62+ unsigned int num_packets;
63+ int in_fds[2];
64+ int ready_out;
65+ int wakefd;
66+};
67+ 
68+ 
69+typedef union {
70+ pthread_t threadid;
71+ pid_t pid;
72+} childinfo_t;
73+ 
74+childinfo_t *child_tab = NULL;
75+unsigned int total_children = 0;
76+unsigned int signal_caught = 0;
77+ 
78+static jmp_buf jmpbuf;
79+ 
80+inline static void sneeze(const char *msg) {
81+ /* Avoid calling these functions when called from a code path
82+ * which involves sigcatcher(), as they are not reentrant safe.
83+ */
84+ if( !signal_caught ) {
85+ fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
86+ }
87+}
88+ 
89+static void barf(const char *msg)
90+{
91+ sneeze(msg);
92+ exit(1);
93+}
94+ 
95+static void print_usage_exit(int error)
96+{
97+ printf("hackbench V %1.2f\n", VERSION);
98+ printf("Usage:\n"
99+ "hackbench <options>\n\n"
100+ "-f --fds=NUM number of fds\n"
101+ "-F --fifo use SCHED_FIFO for main thread\n"
102+ "-g --groups=NUM number of groups to be used\n"
103+ "-h --help print this message\n"
104+ "-l --loops=LOOPS how many message should be send\n"
105+ "-p --pipe send data via a pipe\n"
106+ "-i --inet send data via a inet tcp connection\n"
107+ "-s --datasize=SIZE message size\n"
108+ "-T --threads use POSIX threads\n"
109+ "-P --process use fork (default)\n"
110+ );
111+ exit(error);
112+}
113+ 
114+static int inet_socketpair(int fds[2])
115+{
116+ int s1, s2;
117+ struct sockaddr_in sin;
118+ unsigned long ul = 1;
119+ 
120+ if ((s1 = socket(AF_INET, SOCK_STREAM, 0)) < 0)
121+ barf("socket");
122+ if ((s2 = socket(AF_INET, SOCK_STREAM, 0)) < 0)
123+ barf("socket");
124+ 
125+ socklen_t len = sizeof(sin);
126+ bzero(&sin, len);
127+ sin.sin_family = AF_INET;
128+ sin.sin_port = 0;
129+ sin.sin_addr.s_addr = inet_addr("127.0.0.1");
130+ 
131+ if (bind(s1, &sin, len) < 0)
132+ barf("bind");
133+ if (getsockname(s1, &sin, &len) < 0)
134+ barf("getsockname");
135+ if (listen(s1, 10) < 0)
136+ barf("listen");
137+ if (ioctl(s2, FIONBIO, &ul) < 0)
138+ barf("ioctl");
139+ if (ioctl(s1, FIONBIO, &ul) < 0)
140+ barf("ioctl");
141+ if (connect(s2, &sin, len) < 0)
142+ barf("connect");
143+ if ((fds[0] = accept(s1, &sin, &len)) < 0)
144+ barf("accept");
145+ ul = 0;
146+ if (ioctl(s2, FIONBIO, &ul) < 0)
147+ barf("ioctl");
148+ fds[1] = s2;
149+ close(s1);
150+ 
151+ return 0;
152+}
153+ 
154+static void fdpair(int fds[2])
155+{
156+ if (use_pipes) {
157+ if (pipe(fds) == 0)
158+ return;
159+ } else if (use_inet) {
160+ if (inet_socketpair(fds) == 0)
161+ return;
162+ } else {
163+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
164+ return;
165+ }
166+ barf("Creating fdpair");
167+}
168+ 
169+/* Block until we're ready to go */
170+static void ready(int ready_out, int wakefd)
171+{
172+ char dummy = '*';
173+ struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
174+ 
175+ /* Tell them we're ready. */
176+ if (write(ready_out, &dummy, 1) != 1)
177+ barf("CLIENT: ready write");
178+ 
179+ /* Wait for "GO" signal */
180+ if (poll(&pollfd, 1, -1) != 1)
181+ barf("poll");
182+}
183+ 
184+static void reset_worker_signals(void)
185+{
186+ signal(SIGTERM, SIG_DFL);
187+ signal(SIGINT, SIG_DFL);
188+}
189+ 
190+/* Sender sprays loops messages down each file descriptor */
191+static void *sender(struct sender_context *ctx)
192+{
193+ char data[datasize];
194+ unsigned int i, j;
195+ 
196+ reset_worker_signals();
197+ ready(ctx->ready_out, ctx->wakefd);
198+ memset(&data, '-', datasize);
199+ 
200+ /* Now pump to every receiver. */
201+ for (i = 0; i < loops; i++) {
202+ for (j = 0; j < ctx->num_fds; j++) {
203+ int ret;
204+ size_t done = 0;
205+ 
206+again:
207+ ret = write(ctx->out_fds[j], data + done, sizeof(data)-done);
208+ if (ret < 0)
209+ barf("SENDER: write");
210+ done += ret;
211+ if (done < sizeof(data))
212+ goto again;
213+ }
214+ }
215+ 
216+ return NULL;
217+}
218+ 
219+ 
220+/* One receiver per fd */
221+static void *receiver(struct receiver_context* ctx)
222+{
223+ unsigned int i;
224+ 
225+ reset_worker_signals();
226+ if (process_mode == PROCESS_MODE)
227+ close(ctx->in_fds[1]);
228+ 
229+ /* Wait for start... */
230+ ready(ctx->ready_out, ctx->wakefd);
231+ 
232+ /* Receive them all */
233+ for (i = 0; i < ctx->num_packets; i++) {
234+ char data[datasize];
235+ int ret;
236+ size_t done = 0;
237+ 
238+again:
239+ ret = read(ctx->in_fds[0], data + done, datasize - done);
240+ if (ret < 0)
241+ barf("SERVER: read");
242+ done += ret;
243+ if (done < datasize)
244+ goto again;
245+ }
246+ if (ctx) {
247+ free(ctx);
248+ }
249+ return NULL;
250+}
251+ 
252+static int create_worker(childinfo_t *child, void *ctx, void *(*func)(void *))
253+{
254+ pthread_attr_t attr;
255+ int err;
256+ 
257+ switch (process_mode) {
258+ case PROCESS_MODE: /* process mode */
259+ /* Fork the sender/receiver child. */
260+ switch ((child->pid = fork())) {
261+ case -1:
262+ sneeze("fork()");
263+ return -1;
264+ case 0:
265+ (*func) (ctx);
266+ exit(0);
267+ }
268+ break;
269+ 
270+ case THREAD_MODE: /* threaded mode */
271+ if (pthread_attr_init(&attr) != 0) {
272+ sneeze("pthread_attr_init()");
273+ return -1;
274+ }
275+ 
276+#ifndef __ia64__
277+ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) {
278+ sneeze("pthread_attr_setstacksize()");
279+ return -1;
280+ }
281+#endif
282+ 
283+ if ((err=pthread_create(&child->threadid, &attr, func, ctx)) != 0) {
284+ sneeze("pthread_create failed()");
285+ return -1;
286+ }
287+ break;
288+ }
289+ return 0;
290+}
291+ 
292+void signal_workers(childinfo_t *children, unsigned int num_children)
293+{
294+ unsigned int i;
295+ printf("signaling %d worker threads to terminate\n", num_children);
296+ for (i=0; i < num_children; i++) {
297+ kill(children[i].pid, SIGTERM);
298+ }
299+}
300+ 
301+unsigned int reap_workers(childinfo_t *child, unsigned int totchld, unsigned int dokill)
302+{
303+ unsigned int i, rc = 0;
304+ int status, err;
305+ void *thr_status;
306+ 
307+ if (dokill) {
308+ fprintf(stderr, "sending SIGTERM to all child processes\n");
309+ signal(SIGTERM, SIG_IGN);
310+ signal_workers(child, totchld);
311+ }
312+ 
313+ for( i = 0; i < totchld; i++ ) {
314+ int pid;
315+ switch( process_mode ) {
316+ case PROCESS_MODE: /* process mode */
317+ fflush(stdout);
318+ pid = wait(&status);
319+ if (pid == -1 && errno == ECHILD)
320+ break;
321+ if (!WIFEXITED(status))
322+ rc++;
323+ break;
324+ case THREAD_MODE: /* threaded mode */
325+ err = pthread_join(child[i].threadid, &thr_status);
326+ if( err != 0 ) {
327+ sneeze("pthread_join()");
328+ rc++;
329+ }
330+ break;
331+ }
332+ }
333+ return rc;
334+}
335+ 
336+/* One group of senders and receivers */
337+static unsigned int group(childinfo_t *child,
338+ unsigned int tab_offset,
339+ unsigned int num_fds,
340+ int ready_out,
341+ int wakefd)
342+{
343+ unsigned int i;
344+ struct sender_context* snd_ctx = malloc (sizeof(struct sender_context)
345+ +num_fds*sizeof(int));
346+ int err;
347+ 
348+ if (!snd_ctx) {
349+ sneeze("malloc() [sender ctx]");
350+ return 0;
351+ }
352+ 
353+ 
354+ for (i = 0; i < num_fds; i++) {
355+ int fds[2];
356+ struct receiver_context* ctx = malloc (sizeof(*ctx));
357+ 
358+ if (!ctx) {
359+ sneeze("malloc() [receiver ctx]");
360+ return (i > 0 ? i-1 : 0);
361+ }
362+ 
363+ 
364+ /* Create the pipe between client and server */
365+ fdpair(fds);
366+ 
367+ ctx->num_packets = num_fds*loops;
368+ ctx->in_fds[0] = fds[0];
369+ ctx->in_fds[1] = fds[1];
370+ ctx->ready_out = ready_out;
371+ ctx->wakefd = wakefd;
372+ 
373+ err = create_worker(&child[tab_offset+i], ctx,
374+ (void *)(void *)receiver);
375+ if(err) {
376+ return (i > 0 ? i-1 : 0);
377+ }
378+ snd_ctx->out_fds[i] = fds[1];
379+ if (process_mode == PROCESS_MODE)
380+ close(fds[0]);
381+ }
382+ 
383+ snd_ctx->ready_out = ready_out;
384+ snd_ctx->wakefd = wakefd;
385+ snd_ctx->num_fds = num_fds;
386+ 
387+ /* Now we have all the fds, fork the senders */
388+ for (i = 0; i < num_fds; i++) {
389+ err = create_worker(&child[tab_offset+num_fds+i], snd_ctx,
390+ (void *)(void *)sender);
391+ if(err) {
392+ return (num_fds+i)-1;
393+ }
394+ }
395+ 
396+ /* Close the fds we have left */
397+ if (process_mode == PROCESS_MODE)
398+ for (i = 0; i < num_fds; i++)
399+ close(snd_ctx->out_fds[i]);
400+ 
401+ /* Return number of children to reap */
402+ return num_fds * 2;
403+}
404+ 
405+static void process_options(int argc, char *argv[])
406+{
407+ for(;;) {
408+ static struct option longopts[] = {
409+ {"fds", required_argument, NULL, 'f'},
410+ {"fifo", no_argument, NULL, 'F'},
411+ {"groups", required_argument, NULL, 'g'},
412+ {"help", no_argument, NULL, 'h'},
413+ {"loops", required_argument, NULL, 'l'},
414+ {"pipe", no_argument, NULL, 'p'},
415+ {"inet", no_argument, NULL, 'i'},
416+ {"datasize", required_argument, NULL, 's'},
417+ {"threads", no_argument, NULL, 'T'},
418+ {"processes", no_argument, NULL, 'P'},
419+ {NULL, 0, NULL, 0}
420+ };
421+ 
422+ int c = getopt_long(argc, argv, "f:Fg:hl:pis:TP",
423+ longopts, NULL);
424+ if (c == -1) {
425+ break;
426+ }
427+ switch (c) {
428+ case 'f':
429+ num_fds = atoi(optarg);
430+ if (atoi(optarg) <= 0) {
431+ fprintf(stderr, "%s: --fds|-f requires an integer > 0\n", argv[0]);
432+ print_usage_exit(1);
433+ }
434+ break;
435+ case 'F':
436+ fifo = 1;
437+ break;
438+ case 'g':
439+ num_groups = atoi(optarg);
440+ if (atoi(optarg) <= 0) {
441+ fprintf(stderr, "%s: --groups|-g requires an integer > 0\n", argv[0]);
442+ print_usage_exit(1);
443+ }
444+ break;
445+ case 'h':
446+ print_usage_exit(0);
447+ break;
448+ case 'l':
449+ loops = atoi(optarg);
450+ if (atoi(optarg) <= 0) {
451+ fprintf(stderr, "%s: --loops|-l requires an integer > 0\n", argv[0]);
452+ print_usage_exit(1);
453+ }
454+ break;
455+ case 'p':
456+ use_pipes = 1;
457+ break;
458+ case 'i':
459+ use_inet = 1;
460+ break;
461+ case 's':
462+ datasize = atoi(optarg);
463+ if (atoi(optarg) <= 0) {
464+ fprintf(stderr, "%s: --datasize|-s requires an integer > 0\n", argv[0]);
465+ print_usage_exit(1);
466+ }
467+ break;
468+ case 'T':
469+ process_mode = THREAD_MODE;
470+ break;
471+ case 'P':
472+ process_mode = PROCESS_MODE;
473+ break;
474+ default:
475+ print_usage_exit(1);
476+ }
477+ }
478+ 
479+ if (use_pipes && use_inet) {
480+ fprintf(stderr, "%s: --pipe|-p and --inet|-i cannot be used together\n", argv[0]);
481+ print_usage_exit(1);
482+ }
483+}
484+ 
485+void sigcatcher(int sig) {
486+ /* All caught signals will cause the program to exit */
487+ signal_caught = 1;
488+ fprintf(stderr, "Signal %d caught, longjmp'ing out!\n", sig);
489+ signal(sig, SIG_IGN);
490+ longjmp(jmpbuf, 1);
491+}
492+ 
493+int main(int argc, char *argv[])
494+{
495+ unsigned int i;
496+ struct timeval start, stop, diff;
497+ int readyfds[2], wakefds[2];
498+ char dummy;
499+ volatile int timer_started = 0;
500+ struct sched_param sp;
501+ 
502+ process_options (argc, argv);
503+ 
504+ printf("Running in %s mode with %d groups using %d file descriptors each (== %d tasks)\n",
505+ (process_mode == THREAD_MODE ? "threaded" : "process"),
506+ num_groups, 2*num_fds, num_groups*(num_fds*2));
507+ printf("Each sender will pass %d messages of %d bytes\n", loops, datasize);
508+ fflush(NULL);
509+ 
510+ child_tab = calloc(num_fds * 2 * num_groups, sizeof(childinfo_t));
511+ if (!child_tab)
512+ barf("main:malloc()");
513+ 
514+ fdpair(readyfds);
515+ fdpair(wakefds);
516+ 
517+ /* Catch some signals */
518+ signal(SIGINT, sigcatcher);
519+ signal(SIGTERM, sigcatcher);
520+ signal(SIGHUP, SIG_IGN);
521+ 
522+ if (setjmp(jmpbuf) == 0) {
523+ total_children = 0;
524+ for (i = 0; i < num_groups; i++) {
525+ unsigned int c = group(child_tab, total_children, num_fds, readyfds[1], wakefds[0]);
526+ if( c != (num_fds*2) ) {
527+ fprintf(stderr, "%i children started. Expected %i\n", c, num_fds*2);
528+ reap_workers(child_tab, total_children + c, 1);
529+ barf("Creating workers");
530+ }
531+ total_children += c;
532+ }
533+ if (fifo) {
534+ /* make main a realtime task so that we can manage the workers */
535+ sp.sched_priority = 1;
536+ if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0)
537+ barf("can't change to fifo in main");
538+ }
539+ 
540+ /* Wait for everyone to be ready */
541+ for (i = 0; i < total_children; i++)
542+ if (read(readyfds[0], &dummy, 1) != 1) {
543+ reap_workers(child_tab, total_children, 1);
544+ barf("Reading for readyfds");
545+ }
546+ 
547+ gettimeofday(&start, NULL);
548+ timer_started = 1;
549+ 
550+ /* Kick them off */
551+ if (write(wakefds[1], &dummy, 1) != 1) {
552+ reap_workers(child_tab, total_children, 1);
553+ barf("Writing to start senders");
554+ }
555+ }
556+ else {
557+ fprintf(stderr, "longjmp'ed out, reaping children\n");
558+ signal(SIGINT, SIG_IGN);
559+ signal(SIGTERM, SIG_IGN);
560+ }
561+ 
562+ /* Reap them all */
563+ reap_workers(child_tab, total_children, signal_caught);
564+ 
565+ gettimeofday(&stop, NULL);
566+ 
567+ /* Print time... */
568+ if (timer_started) {
569+ timersub(&stop, &start, &diff);
570+ printf("Time: %lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000);
571+ }
572+ else
573+ fprintf(stderr, "No measurements available\n");
574+ free(child_tab);
575+ exit(0);
576+}
@@ -0,0 +1,138 @@
1+Introduction:
2+-------------
3+ 
4+The module hwlat is a special purpose kernel module that is
5+used to detect if System Management Interrupts (SMIs) are causing event
6+latencies in the Linux RT kernel.
7+ 
8+SMIs are usually not serviced by the Linux kernel. They are set up by
9+BIOS code and are serviced by BIOS code, usually for critical events
10+such as management of thermal sensors and fans. Sometimes though, SMIs
11+are used for other tasks and those tasks can spend an inordinate
12+amount of time in the handler (sometimes measured in milliseconds).
13+Obviously if you are trying to keep event service latencies down in the
14+microsecond range, this is a problem.
15+ 
16+The SMI detector works by hogging the cpu for configurable amounts of
17+time (by calling stop_machine()), polling the Time Stamp Counter (TSC)
18+register for some period, then looking for gaps in the TSC data. Any
19+gap indicates a time when the polling was interrupted and since the
20+machine is stopped and interrupts turned off the only thing that could
21+do that would be an SMI.
22+ 
23+Note that the SMI detector should *NEVER* be used in a production
24+environment. It is intended to be run manually to determine if the
25+hardware platform has a problem with long SMI service routines.
26+ 
27+Usage:
28+------
29+ 
30+Loading the module hwlat passing the parameter "enabled=1" is the only
31+step required to start the hwlat. It is possible to define a threshold
32+in microseconds (us) above which latency spikes will be taken in account
33+(parameter "threshold=").
34+ 
35+Example:
36+ 
37+ # insmod ./hwlat.ko enabled=1 threshold=100
38+ 
39+After the module is loaded, it creates a directory named "hwlat" under
40+the debugfs mountpoint, "/debugfs/hwlat" for this text. It is necessary
41+to have debugfs mounted.
42+ 
43+avg_smi_interval_us - average interval (usecs) between SMI latency spikes
44+latency_threshold_us - minimum latency value to be considered (usecs)
45+max_sample_us - maximum SMI latency spike observed (usecs)
46+ms_between_samples - interval between samples (ms)
47+ms_per_sample - sampling time (ms)
48+sample_us - last sample (usecs). continuously updated, may be used
49+ to plot graphs or to create histograms
50+smi_count - how many latency spikes have been observed
51+ 
52+ 
53+ # cd /debugfs/hwlat
54+ 
55+ # cat *
56+ 0 (avg_smi_interval_us)
57+ 100 (latency_threshold_us)
58+ 0 (latency_threshold_us)
59+ 5000 (ms_between_samples)
60+ 1 (ms_per_sample)
61+ 3 (sample_us)
62+ 0 (smi_count)
63+ 
64+ 
65+The default values for ms_between_samples and ms_per_sample define that every
66+5000ms (5s) samples will be collected during 1ms. It is possible to change the
67+sampling time or the sampling interval writing to the related files. To collect
68+samples during 5ms every 50ms:
69+ 
70+ # echo 5 > ms_per_sample
71+ # echo 50 > ms_between_samples
72+ 
73+ 
74+ # cat ms_between_samples
75+ 50
76+ # cat ms_per_sample
77+ 5
78+ 
79+After a while, data may be verified to test the existence of SMI induced
80+latencies:
81+ 
82+ # cat smi_count
83+ 2
84+ # cat max_sample_us
85+ 468
86+ # cat avg_smi_interval_us
87+ 1356306050
88+ 
89+Depending on the latencies observed it is important to better adjust the
90+sampling intervals to obtain more accurate measurements. Care must be taken
91+to not create a continuous sampling situation, that might be perceived by the
92+kernel as a deadlock.
93+ 
94+Python Script
95+-------------
96+ 
97+A python script is provided in in the kernel scripts directory named
98+'hwlatdetect'. This script handles mounting/unmounting the debugfs,
99+loading/unloading the hwlat module, setting various parameters
100+for the SMI detector and then starting and stopping the detection. The
101+hwlatdetect script handles the following arguments:
102+ 
103+Usage: hwlatdetect [options]
104+ 
105+Options:
106+ -h, --help show this help message and exit
107+ --duration=DURATION total time to test for SMIs (<n>{smdw})
108+ --threshold=THRESHOLD
109+ value above which is considered an SMI (microseconds
110+ --interval=INTERVAL time between samples (milliseconds)
111+ --sample_width=SAMPLE_WIDTH
112+ time to actually measure (milliseconds)
113+ --report=REPORT filename for sample data
114+ --cleanup force unload of module and umount of debugfs
115+ --debug turn on debugging prints
116+ --quiet turn off all screen output
117+ --watch print sample data to stdout as it arrives
118+ 
119+ 
120+An example run looks like this:
121+ 
122+ $ sudo hwlatdetect --duration=1m --report=smi.txt
123+ hwlatdetect: test duration 60 seconds
124+ parameters:
125+ Latency threshold: 100us
126+ Non-sampling gap: 5ms
127+ Sample length: 20ms
128+ Output File: smi.txt
129+ Starting test
130+ test finished
131+ Max Latency: 427us
132+ Samples recorded: 2201
133+ Samples exceeding threshold: 7
134+ sample data written to smi.txt
135+ $
136+ 
137+The above run generated 2201 samples of which 27 exceeded the 100
138+microsecond threshold.
@@ -0,0 +1,116 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH HWLATDETECT 8 "May 12, 2009"
3+# SPDX-License-Identifier: GPL-2.0-only
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+hwlatdetect \- program to control the ftrace kernel hardware latency detection
18+.SH SYNOPSIS
19+.B hwlatdetect
20+.RI "[ \-\-duration=<time> ] [\-\-threshold=<usecs> ] \
21+[\-\-window=<time interval> ] [\-\-width=<time interval> ] \
22+[\-\-hardlimit=<microsecond value> ] [\-\-report=<path> ] \
23+[\-\-debug ] [\-\-quiet ] [--watch]
24+ 
25+.\" .SH DESCRIPTION
26+.\" This manual page documents briefly the
27+.\" .B hwlatdetect commands.
28+.\" .PP
29+.\" \fI<whatever>\fP escape sequences to invoke bold face and italics, respectively.
30+.\" \fBhwlatdetect\fP is a program that...
31+.SH DESCRIPTION
32+\fBhwlatdetect\fP is a program that controls the ftrace kernel hardware
33+latency detector (hwlatdetector).hwlatdetector is used to detect large
34+system latencies induced by the behavior of certain underlying hardware
35+or firmware, independent of Linux itself.
36+The code was developed originally to detect SMIs (System Management Interrupts)
37+on x86 systems, however there is nothing x86 specific about it.
38+It was originally written for use by the "RT" patch set
39+since the Real Time kernel is highly latency sensitive.
40+ 
41+SMIs are usually not serviced by the Linux kernel, which typically does not
42+even know that they are occurring. SMIs are instead are set up by BIOS code
43+and are serviced by BIOS code, usually for "critical" events such as
44+management of thermal sensors and fans. Sometimes though, SMIs are used for
45+other tasks and those tasks can spend an inordinate amount of time in the
46+handler (sometimes measured in milliseconds). Obviously this is a problem if
47+you are trying to keep event service latencies down in the microsecond range.
48+ 
49+The ftrace hardware latency detector works by hogging all of the cpus
50+for configurable amounts of time (by calling stop_machine()), polling
51+the CPU Time Stamp Counter for some period, then looking for gaps in
52+the TSC data. Any gap indicates a time when the polling was
53+interrupted and since the machine is stopped and interrupts turned off
54+the only thing that could do that would be an SMI.
55+ 
56+The hwlatdetector script manages the mounting/unmounting of the
57+debugfs as well as interacting with the ftrace hwlatdetector
58+If the debugfs is already mounted then hwlatdetector will not
59+unmount it after a run.
60+ 
61+.SH OPTIONS
62+.TP
63+.B \-\-duration=<time>{s,m,d}
64+Run the detector logic in for the specified duration. The duration is
65+a base 10 integer number that defaults to a value in seconds. An
66+optional suffix may be specified to indicate minutes, hours or days.
67+ 
68+.TP
69+.B \-\-threshold=<microsecond value>
70+Specify the TSC gap used to detect an SMI. Any gap value greater than
71+<threshold> is considered to be the result of an SMI occurring.
72+ 
73+.TP
74+.B \-\-hardlimit=<microsecond value>
75+The test is considered to fail if a value above the hardlimit occurs.
76+This affects the exit value of hwlatdetect
77+ 
78+.TP
79+.B \-\-window=<time value>{us,ms,s,m,d}
80+specify the size of the sample window. Converted to microseconds when
81+passed to the kernel.
82+ 
83+.TP
84+.B \-\-width=<time value>{us,ms,s,m,d}
85+The amount of time within the sample window where the detector is
86+actually sampling. Must be less than the \-\-window value.
87+.TP
88+.B \-\-report=FILENAME
89+Specify the output filename of the detector report. Default
90+behavior is to print to standard output
91+.TP
92+.B \-\-cpu-list=CPU-LIST
93+Specify the CPUs for hwlat thread to move across.
94+.TP
95+.B \-\-debug
96+Turn on debug prints
97+.TP
98+.B \-\-quiet
99+Turn off all information prints
100+.TP
101+.B \-\-watch
102+print sample data to stdout as it arrives
103+ 
104+.\" .SH SEE ALSO
105+.\" .BR bar (1),
106+.\" .BR baz (1).
107+.\" .br
108+.\" The programs are documented fully by
109+.\" .IR "The Rise and Fall of a Fooish Bar" ,
110+.\" available via the Info system.
111+.SH AUTHOR
112+.B hwlatdetect
113+was written by Clark Williams <williams@redhat.com>
114+.br
115+.B hwlat_detector.ko
116+was written by Jon Masters <jcm@redhat.com>
@@ -0,0 +1,544 @@
1+#!/usr/bin/python3
2+""" Module to detect smis """
3+ 
4+# SPDX-License-Identifier: GPL-2.0-only
5+ 
6+# (C) 2018,2019 Clark Williams <williams@redhat.com>
7+# (C) 2015,2016 Clark Williams <williams@redhat.com>
8+# (C) 2009 Clark Williams <williams@redhat.com>
9+ 
10+import abc
11+import errno
12+import os
13+import os.path
14+import subprocess
15+import sys
16+import time
17+ 
18+version = "0.8"
19+debugging = False
20+quiet = False
21+watch = False
22+ 
23+ 
24+def debug(dstr):
25+ """ print debugging string if debugging is on """
26+ if debugging:
27+ print(dstr)
28+ 
29+ 
30+def info(istr):
31+ " print info string if quiet is not on """
32+ if not quiet:
33+ print(istr)
34+ 
35+ 
36+#
37+# Class used to manage mounting and umounting the debugfs
38+# filesystem. Note that if an instance of this class mounts
39+# the debugfs, it will unmount when cleaning up, but if it
40+# discovers that debugfs is already mounted, it will leave
41+# it mounted.
42+#
43+class DebugFS:
44+ """ class to manage mounting/umounting the debugfs """
45+ def __init__(self):
46+ self.premounted = False
47+ self.mounted = False
48+ self.mountpoint = ''
49+ with open('/proc/mounts') as f:
50+ for l in f:
51+ field = l.split()
52+ if field[2] == "debugfs":
53+ self.premounted = True
54+ self.mountpoint = field[1]
55+ break
56+ 
57+ def mount(self, path='/sys/kernel/debug'):
58+ """ mount debugfs unless already mounted """
59+ if self.premounted or self.mounted:
60+ debug("not mounting debugfs")
61+ return True
62+ debug(f"mounting debugfs at {path}")
63+ self.mountpoint = path
64+ cmd = ['/bin/mount', '-t', 'debugfs', 'none', path]
65+ self.mounted = (subprocess.call(cmd) == 0)
66+ if not self.mounted:
67+ raise RuntimeError("Failed to mount debugfs")
68+ return self.mounted
69+ 
70+ def umount(self):
71+ """ unmount debugfs if not premounted """
72+ if self.premounted or not self.mounted:
73+ debug("not umounting debugfs")
74+ return True
75+ debug("umounting debugfs")
76+ cmd = ['/bin/umount', self.mountpoint]
77+ self.mounted = subprocess.call(cmd) != 0
78+ if self.mounted:
79+ raise RuntimeError("Failed to umount debugfs")
80+ return not self.mounted
81+ 
82+ def getval(self, item, nonblocking=False):
83+ """ get value of mountpoint/item """
84+ path = os.path.join(self.mountpoint, item)
85+ if nonblocking is False:
86+ with open(path) as f:
87+ val = f.readline()
88+ else:
89+ f = os.fdopen(os.open(path, os.O_RDONLY | os.O_NONBLOCK), "r")
90+ try:
91+ val = f.readline()
92+ except OSError as e:
93+ print(f"errno: {e}")
94+ if e.errno == errno.EAGAIN:
95+ val = None
96+ else:
97+ raise
98+ f.close()
99+ return val
100+ 
101+ def putval(self, item, value):
102+ """ write value to mountpoint/item """
103+ path = os.path.join(self.mountpoint, item)
104+ with open(path, "w") as f:
105+ f.write(str(value))
106+ f.flush()
107+ 
108+ def getpath(self, item):
109+ """ get mountpoint/item """
110+ return os.path.join(self.mountpoint, item)
111+ 
112+ 
113+class DetectorNotAvailable(Exception):
114+ """ Exception class for when tracer is not available """
115+ 
116+ def __init__(self, name, msg):
117+ self.args = (name, msg)
118+ self.name = name
119+ self.msg = msg
120+ 
121+ 
122+class Detector:
123+ """ base class for detector modules """
124+ 
125+ def __init__(self):
126+ self.type = "unknown"
127+ if os.getuid() != 0:
128+ raise RuntimeError("Must be root")
129+ self.debugfs = DebugFS()
130+ if not self.debugfs.mount():
131+ raise RuntimeError("failed to mount debugfs")
132+ self.samples = []
133+ self.testduration = 30 # in seconds
134+ self.have_msr = False
135+ self.initsmi = []
136+ if os.path.exists('/usr/sbin/rdmsr'):
137+ self.have_msr = True
138+ self.initsmi = self.getsmicounts()
139+ self.dma_latency_handle = None
140+ 
141+ def getsmicounts(self):
142+ counts = []
143+ if self.have_msr:
144+ p = subprocess.Popen(['/usr/sbin/rdmsr', '-a', '-d', '0x34'], stdout=subprocess.PIPE)
145+ p.wait()
146+ counts = [int(x.strip()) for x in p.stdout.readlines()]
147+ return counts
148+ 
149+ # methods for preventing/enabling c-state transitions
150+ #
151+ # opening /dev/cpu_dma_latency and writing a 32-bit zero to that file will
152+ # prevent c-state transitions while the file descriptor is open.
153+ #
154+ # use c_states_off() to disable c-state transitions
155+ # use c_states_on() to close the file descriptor and re-enable c-states
156+ #
157+ def c_states_off(self):
158+ """ disable c-state transitions """
159+ if os.path.exists("/dev/cpu_dma_latency"):
160+ self.dma_latency_handle = os.open("/dev/cpu_dma_latency", os.O_WRONLY)
161+ os.write(self.dma_latency_handle, b'\x00\x00\x00\x00')
162+ debug("c-states disabled")
163+ 
164+ def c_states_on(self):
165+ """ close the file descriptor and re-enable c-states """
166+ if self.dma_latency_handle:
167+ os.close(self.dma_latency_handle)
168+ debug("c-states enabled")
169+ 
170+ @abc.abstractmethod
171+ def cleanup(self):
172+ ''' abstract cleanup method, must override '''
173+ 
174+ @abc.abstractmethod
175+ def get(self, field):
176+ ''' get the value of a debugfs field '''
177+ 
178+ @abc.abstractmethod
179+ def set(self, field, val):
180+ ''' set a value in a debugfs field '''
181+ 
182+ @abc.abstractmethod
183+ def save(self, output=None):
184+ ''' save sample data to output '''
185+ 
186+ @abc.abstractmethod
187+ def display(self):
188+ ''' output the sample data as a string '''
189+ 
190+ def start(self):
191+ """ enable the detector """
192+ count = 0
193+ threshold = int(self.get("threshold"))
194+ self.c_states_off()
195+ debug(f"enabling detector module (threshold: {threshold})")
196+ self.set("enable", 1)
197+ while self.get("enable") == 0:
198+ debug("still disabled, retrying in a bit")
199+ count += 1
200+ time.sleep(0.1)
201+ debug(f"retrying enable of detector module ({count})")
202+ self.set("enable", 1)
203+ if self.get("threshold") != threshold:
204+ debug("start: threshold reset by start, fixing")
205+ self.set("threshold", threshold)
206+ debug(f"detector module enabled (threshold: {int(self.get('threshold'))})")
207+ 
208+ def stop(self):
209+ """ disable the detector """
210+ count = 0
211+ debug("disabling detector module")
212+ self.set("enable", 0)
213+ while self.get("enable") == 1:
214+ debug("still enabled, retrying in a bit")
215+ count += 1
216+ time.sleep(0.1)
217+ debug(f"retrying disable of detector module({count})")
218+ self.set("enable", 0)
219+ self.c_states_on()
220+ debug("detector module disabled")
221+ 
222+ @abc.abstractmethod
223+ def detect(self):
224+ ''' get detector output '''
225+ 
226+ 
227+class Tracer(Detector):
228+ """ Class to handle running the hwlat tracer module of ftrace """
229+ 
230+ __field_translation = {
231+ 'width' : "hwlat_detector/width",
232+ 'window' : "hwlat_detector/window",
233+ 'enable' : "tracing_on",
234+ 'threshold' : "tracing_thresh",
235+ 'cpumask' : "tracing_cpumask",
236+ }
237+ 
238+ class Sample:
239+ 'private class for tracer sample data'
240+ __slots__ = 'cpu', 'timestamp', 'inner', 'outer'
241+ 
242+ def __init__(self, line):
243+ fields = line.split()
244+ self.cpu = int(fields[1][1:-1])
245+ i, o = fields[6].split('/')
246+ ts = fields[7][3:]
247+ self.timestamp = str(ts)
248+ self.inner = int(i)
249+ self.outer = int(o)
250+ 
251+ def __str__(self):
252+ return f"ts: {self.timestamp}, inner:{self.inner}, outer:{self.outer}, cpu:{self.cpu}"
253+ 
254+ def display(self):
255+ """ convert object to string and print """
256+ print(str(self))
257+ 
258+ def largest(self):
259+ """ return largest value of inner or outer """
260+ return max(self.inner, self.outer)
261+ 
262+ def translate(self, field):
263+ path = self.debugfs.getpath('tracing')
264+ if field not in Tracer.__field_translation:
265+ return os.path.join(path, field)
266+ return os.path.join(path, Tracer.__field_translation[field])
267+ 
268+ def __init__(self):
269+ super().__init__()
270+ path = self.debugfs.getpath('tracing/hwlat_detector')
271+ if not os.path.exists(path):
272+ raise DetectorNotAvailable("hwlat", "hwlat tracer not available")
273+ self.type = "tracer"
274+ self.samples = []
275+ self.set("enable", 0)
276+ self.set('current_tracer', 'hwlat')
277+ 
278+ def set(self, field, val):
279+ path = self.translate(field)
280+ self.debugfs.putval(path, str(val))
281+ 
282+ def get(self, field):
283+ if field == "count":
284+ return len(self.samples)
285+ if field == "max":
286+ max = 0
287+ for values in self.samples:
288+ s = int(values.largest())
289+ if s > max:
290+ max = s
291+ return max
292+ return self.debugfs.getval(self.translate(field))
293+ 
294+ def detect(self):
295+ self.samples = []
296+ testend = time.time() + self.testduration
297+ pollcnt = 0
298+ self.start()
299+ try:
300+ while time.time() < testend:
301+ pollcnt += 1
302+ val = self.get_sample()
303+ while val:
304+ self.samples.append(val)
305+ if watch:
306+ val.display()
307+ val = self.get_sample()
308+ time.sleep(0.1)
309+ except KeyboardInterrupt as e:
310+ print("interrupted")
311+ self.stop()
312+ return self.samples
313+ 
314+ def get_sample(self):
315+ val = None
316+ line = self.debugfs.getval("tracing/trace_pipe", nonblocking=True)
317+ if line:
318+ val = self.Sample(line)
319+ return val
320+ 
321+ def save(self, output=None):
322+ """ save samples """
323+ if output:
324+ with open(output, "w") as f:
325+ for s in self.samples:
326+ f.write(f"{s}\n")
327+ print(f"report saved to {output} ({len(self.samples)} samples)")
328+ 
329+ def display(self):
330+ for s in self.samples:
331+ s.display()
332+ 
333+ def cleanup(self):
334+ self.set("tracing_on", "0")
335+ self.set("current_tracer", "nop")
336+ if not self.debugfs.umount():
337+ raise RuntimeError("Failed to unmount debugfs")
338+ 
339+ 
340+def seconds(sval):
341+ "convert input string to value in seconds"
342+ if sval.isdigit():
343+ return int(sval)
344+ if sval[-2].isalpha():
345+ raise RuntimeError(f"illegal suffix for seconds: '{sval[-2:-1]}'")
346+ if sval[-1:] == 's':
347+ return int(sval[0:-1])
348+ if sval[-1:] == 'm':
349+ return int(sval[0:-1]) * 60
350+ if sval[-1:] == 'h':
351+ return int(sval[0:-1]) * 3600
352+ if sval[-1:] == 'd':
353+ return int(sval[0:-1]) * 86400
354+ if sval[-1:] == 'w':
355+ return int(sval[0:-1]) * 86400 * 7
356+ raise RuntimeError(f"invalid input for seconds: '{sval}'")
357+ 
358+ 
359+def milliseconds(sval):
360+ "convert input string to millsecond value"
361+ if sval.isdigit():
362+ return int(sval)
363+ if sval[-2:] == 'ms':
364+ return int(sval[0:-2])
365+ if sval[-1] == 's':
366+ return int(sval[0:-2]) * 1000
367+ if sval[-1] == 'm':
368+ return int(sval[0:-1]) * 1000 * 60
369+ if sval[-1] == 'h':
370+ return int(sval[0:-1]) * 1000 * 60 * 60
371+ raise RuntimeError(f"invalid input for milliseconds: {sval}")
372+ 
373+ 
374+def microseconds(sval):
375+ "convert input string to microsecond value"
376+ if sval.isdigit():
377+ return int(sval)
378+ if sval[-2:] == 'ms':
379+ return int(sval[0:-2]) * 1000
380+ if sval[-2:] == 'us':
381+ return int(sval[0:-2])
382+ if sval[-1:] == 's':
383+ return int(sval[0:-1]) * 1000 * 1000
384+ raise RuntimeError(f"invalid input for microseconds: '{sval}'")
385+ 
386+ 
387+if __name__ == '__main__':
388+ from argparse import ArgumentParser
389+ 
390+ parser = ArgumentParser()
391+ parser.add_argument("--duration", default=None,
392+ dest="duration",
393+ help="total time to test for hardware latency: <n>{smdw}")
394+ 
395+ parser.add_argument("--threshold", default=None,
396+ dest="threshold",
397+ help="value above which is considered an hardware latency")
398+ 
399+ parser.add_argument("--hardlimit", default=None,
400+ dest="hardlimit",
401+ help="value above which the test is considered to fail")
402+ 
403+ parser.add_argument("--window", default=None,
404+ dest="window",
405+ help="time between samples")
406+ 
407+ parser.add_argument("--width", default=None,
408+ dest="width",
409+ help="time to actually measure")
410+ 
411+ parser.add_argument("--report", default=None,
412+ dest="report",
413+ help="filename for sample data")
414+ 
415+ parser.add_argument("--cpu-list", default=None,
416+ dest="cpulist",
417+ help="the CPUs for hwlat thread to move across")
418+ 
419+ parser.add_argument("--debug", action="store_true", default=False,
420+ dest="debug",
421+ help="turn on debugging prints")
422+ 
423+ parser.add_argument("--quiet", action="store_true", default=False,
424+ dest="quiet",
425+ help="turn off all screen output")
426+ 
427+ parser.add_argument("--watch", action="store_true", default=False,
428+ dest="watch",
429+ help="print sample data to stdout as it arrives")
430+ 
431+ args = parser.parse_args()
432+ 
433+ # need these before creating detector instance
434+ if args.debug:
435+ debugging = True
436+ quiet = False
437+ debug("debugging prints turned on")
438+ 
439+ if args.quiet:
440+ quiet = True
441+ debugging = False
442+ 
443+ detect = Tracer()
444+ 
445+ if args.threshold:
446+ t = microseconds(args.threshold)
447+ detect.set("threshold", t)
448+ debug(f"threshold set to {t}us")
449+ 
450+ if args.hardlimit:
451+ hardlimit = microseconds(args.hardlimit)
452+ else:
453+ hardlimit = int(detect.get("threshold"))
454+ debug(f"hardlimit set to {hardlimit}us")
455+ 
456+ if args.window:
457+ w = microseconds(args.window)
458+ width = w//2
459+ if w < int(detect.get("width")):
460+ debug(f"shrinking width to {width} for new window of {w}")
461+ detect.set("width", width)
462+ debug(f"window parameter = {w}")
463+ detect.set("window", w)
464+ debug(f"window for sampling set to {w}us")
465+ 
466+ if args.width:
467+ w = microseconds(args.width)
468+ if w > int(detect.get("window")):
469+ debug(f"widening window to {w*2} for new width of {w}")
470+ detect.set("window", w*2)
471+ debug(f"width parameter = {w}")
472+ detect.set("width", w)
473+ debug(f"sample width set to {w}us")
474+ 
475+ if args.duration:
476+ detect.testduration = seconds(args.duration)
477+ else:
478+ detect.testduration = 120 # 2 minutes
479+ debug(f"test duration is {detect.testduration}s")
480+ 
481+ if args.watch:
482+ watch = True
483+ 
484+ reportfile = args.report
485+ 
486+ if args.cpulist:
487+ cpumask = 0
488+ for c in args.cpulist.split(','):
489+ l, r = 0, 0
490+ if '-' in c:
491+ l, r = map(int, c.split('-'))
492+ else:
493+ l, r = map(int, [c, c])
494+ for i in range(l, r + 1):
495+ cpumask |= (1 << i)
496+ debug(f"set tracing_cpumask to {cpumask:x}")
497+ detect.set("cpumask", f"{cpumask:x}")
498+ 
499+ info(f"hwlatdetect: test duration {detect.testduration} seconds")
500+ info(f" detector: {detect.type}")
501+ info(" parameters:")
502+ info(f" CPU list: {args.cpulist}")
503+ info(f" Latency threshold: {int(detect.get('threshold'))}us")
504+ info(f" Sample window: {int(detect.get('window'))}us")
505+ info(f" Sample width: {int(detect.get('width'))}us")
506+ info(f" Non-sampling period: {(int(detect.get('window')) - int(detect.get('width')))}us")
507+ info(f" Output File: {reportfile}")
508+ info("\nStarting test")
509+ 
510+ detect.detect()
511+ 
512+ info("test finished")
513+ 
514+ max_latency = detect.get("max")
515+ if max_latency == 0:
516+ info("Max Latency: Below threshold")
517+ else:
518+ info(f"Max Latency: {int(max_latency)}us")
519+ 
520+ info(f"Samples recorded: {len(detect.samples)}")
521+ 
522+ exceeding = detect.get("count")
523+ info(f"Samples exceeding threshold: {exceeding}")
524+ 
525+ if detect.have_msr:
526+ finishsmi = detect.getsmicounts()
527+ total_smis = 0
528+ for i, count in enumerate(finishsmi):
529+ if count > detect.initsmi[i]:
530+ smis = count - detect.initsmi[i]
531+ total_smis += smis
532+ print(f"{smis} SMIs occured on cpu {i}")
533+ info(f"SMIs during run: {total_smis}")
534+ 
535+ maxlatency = int(detect.get("max"))
536+ 
537+ if reportfile:
538+ detect.save(reportfile)
539+ 
540+ if not watch:
541+ detect.display()
542+ 
543+ detect.cleanup()
544+ sys.exit(maxlatency > hardlimit)
@@ -0,0 +1,43 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef BIONIC_H
3+#define BIONIC_H
4+ 
5+#ifdef PTHREAD_BIONIC
6+#warning Program is being compiled with PTHREAD_BIONIC, some options may behave erratically.
7+ 
8+/*
9+ * We do not have pthread_barrier_t available, but since we are not
10+ * going to use them for anything useful, just typedef them to int
11+ */
12+typedef int pthread_barrier_t;
13+typedef int pthread_barrierattr_t;
14+ 
15+#ifndef PTHREAD_BARRIER_SERIAL_THREAD
16+#define PTHREAD_BARRIER_SERIAL_THREAD 0
17+#endif
18+ 
19+static inline int pthread_barrier_wait(pthread_barrier_t *barrier)
20+{
21+ return PTHREAD_BARRIER_SERIAL_THREAD;
22+}
23+ 
24+static inline int pthread_barrier_destroy(pthread_barrier_t *barrier)
25+{
26+ return 0;
27+}
28+static inline int pthread_barrier_init(pthread_barrier_t * barrier,
29+ const pthread_barrierattr_t * attr,
30+ unsigned count)
31+{
32+ return 0;
33+}
34+ 
35+static inline int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
36+ const cpu_set_t *cpuset)
37+{
38+ return sched_setaffinity(0, cpusetsize, cpuset);
39+}
40+ 
41+#endif /* PTHREAD_BIONIC */
42+ 
43+#endif /* BIONIC_H */
@@ -0,0 +1,42 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#include <stdint.h>
3+#include <stdio.h>
4+ 
5+struct histogram {
6+ unsigned long *buckets;
7+ unsigned long width; // interval covered by one bucket
8+ unsigned long num; // number of buckets
9+ unsigned long events; // number of events logged
10+ 
11+ unsigned long *oflows; // events when overflow happened
12+ unsigned long oflow_bufsize; // number of overflows that can be logged
13+ unsigned long oflow_count; // number of events that overflowed
14+ uint64_t oflow_magnitude; // sum of how many buckets overflowed by
15+};
16+ 
17+struct histoset {
18+ struct histogram *histos; // Group of related histograms (e.g. per cpu)
19+ struct histogram *sum; // Accumulates events from all histos
20+ unsigned long num_histos; // Not including sum
21+ unsigned long num_buckets;
22+};
23+ 
24+#define HIST_OVERFLOW 1
25+#define HIST_OVERFLOW_MAG 2
26+#define HIST_OVERFLOW_LOG 4
27+ 
28+int hist_init(struct histogram *h, unsigned long width, unsigned long num);
29+int hist_init_oflow(struct histogram *h, unsigned long num);
30+void hist_destroy(struct histogram *h);
31+int hist_sample(struct histogram *h, uint64_t sample);
32+ 
33+#define HSET_PRINT_SUM 1
34+#define HSET_PRINT_JSON 2
35+ 
36+int hset_init(struct histoset *hs, unsigned long histos, unsigned long bucket_width,
37+ unsigned long num_buckets, unsigned long overflow);
38+void hset_destroy(struct histoset *hs);
39+void hset_print_bucket(struct histoset *hs, FILE *f, const char *pre,
40+ unsigned long bucket, unsigned long flags);
41+void hist_print_json(struct histogram *h, FILE *f);
42+void hist_print_oflows(struct histogram *h, FILE *f);
@@ -0,0 +1,133 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef __PI_STRESS_H
3+#define __PI_STRESS_H
4+#include <stdio.h>
5+#include <errno.h>
6+#include <stdlib.h>
7+#include <stdarg.h>
8+#include <pthread.h>
9+#include <sched.h>
10+#include <unistd.h>
11+#include <string.h>
12+#include <signal.h>
13+#include <getopt.h>
14+#include <sys/types.h>
15+#include <sys/mman.h>
16+#include <sys/wait.h>
17+#include <sys/syscall.h>
18+#include <termios.h>
19+#include <stdint.h>
20+#include <inttypes.h>
21+#include <limits.h>
22+ 
23+#include "rt-sched.h"
24+#include "rt-utils.h"
25+#include "rt-error.h"
26+ 
27+/* test timeout */
28+#define TIMEOUT 2
29+ 
30+/* determine if the C library supports Priority Inheritance mutexes */
31+#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT != -1
32+#define HAVE_PI_MUTEX 1
33+#else
34+#define HAVE_PI_MUTEX 0
35+#endif
36+ 
37+#if HAVE_PI_MUTEX == 0
38+#error "Can't run this test without PI Mutex support"
39+#endif
40+ 
41+#define SUCCESS 0
42+#define FAILURE 1
43+ 
44+/* cursor control */
45+#define UP_ONE "\033[1A"
46+#define DOWN_ONE "\033[1B"
47+ 
48+#define pi_info(fmt, arg...) \
49+ do { info(verbose, fmt, ## arg); } while (0)
50+#define pi_debug(fmt, arg...) \
51+ do { debug(debugging, fmt, ## arg); } while (0)
52+#define pi_error(fmt, arg...) \
53+ do { err_msg(fmt, ## arg); have_errors = 1; } while (0)
54+ 
55+#define NUM_TEST_THREADS 3
56+#define NUM_ADMIN_THREADS 1
57+ 
58+#define SA_INIT_LOW (1 << 0)
59+#define SA_INIT_MED (1 << 1)
60+#define SA_INIT_HIGH (1 << 2)
61+#define SA_INIT_ADMIN (1 << 3)
62+ 
63+/* number of consecutive watchdog hits before quitting */
64+#define WATCHDOG_LIMIT 5
65+ 
66+struct group_parameters {
67+ 
68+ /* group id (index) */
69+ int id;
70+ 
71+ /* cpu this group is bound to */
72+ long cpu;
73+ 
74+ /* threads in the group */
75+ pthread_t low_tid;
76+ pthread_t med_tid;
77+ pthread_t high_tid;
78+ 
79+ /* number of machine iterations to perform */
80+ int inversions;
81+ 
82+ /* group mutex */
83+ pthread_mutex_t mutex;
84+ 
85+ /* state barriers */
86+ pthread_barrier_t start_barrier;
87+ pthread_barrier_t locked_barrier;
88+ pthread_barrier_t elevate_barrier;
89+ pthread_barrier_t finish_barrier;
90+ 
91+ /* Either everyone goes through the loop, or else no-ones does */
92+ pthread_barrier_t loop_barr;
93+ pthread_mutex_t loop_mtx; /* Protect access to int loop */
94+ int loop; /* boolean, loop or not, connected to shutdown */
95+ 
96+ /* state variables */
97+ volatile int watchdog;
98+ 
99+ /* total number of inversions performed */
100+ unsigned long total;
101+ 
102+ /* total watchdog hits */
103+ int watchdog_hits;
104+ 
105+};
106+ 
107+/* forward prototypes */
108+void *low_priority(void *arg);
109+void *med_priority(void *arg);
110+void *high_priority(void *arg);
111+void *reporter(void *arg);
112+void *watchdog(void *arg);
113+int setup_thread_attr(pthread_attr_t *attr, struct sched_attr *sa,
114+ cpu_set_t *mask);
115+int set_cpu_affinity(cpu_set_t *test_mask, cpu_set_t *admin_mask);
116+void process_command_line(int argc, char **argv);
117+void usage(int error);
118+int block_signals(void);
119+int allow_sigterm(void);
120+void set_shutdown_flag(void);
121+int initialize_group(struct group_parameters *group);
122+int create_group(struct group_parameters *group);
123+unsigned long total_inversions(void);
124+void banner(void);
125+void summary(void);
126+void write_stats(FILE *f, void *data);
127+void wait_for_termination(void);
128+int barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *attr,
129+ unsigned int count, const char *name);
130+void setup_sched_attr(struct sched_attr *attr, int policy, int prio);
131+void setup_sched_config(int policy);
132+ 
133+#endif /* __PI_STRESS_H */
@@ -0,0 +1,42 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef __PIP_STRESS_H
3+#define __PIP_STRESS_H
4+ 
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <unistd.h>
8+#include <errno.h>
9+#include <sys/mman.h>
10+#include <string.h>
11+#include <stdarg.h>
12+#include <pthread.h>
13+#include <sys/types.h>
14+#include <sys/wait.h>
15+#include <signal.h>
16+#include <sched.h>
17+ 
18+#include "rt-utils.h"
19+#include "rt-error.h"
20+ 
21+void low(pid_t pid); /* low priority process */
22+void medium(void); /* medium priority process */
23+void high(pid_t pid); /* high priority process */
24+void init_state(void);
25+ 
26+void *mmap_page(void);
27+long process_shared_mutex_available(void);
28+void Pthread_mutexattr_init(pthread_mutexattr_t *attr);
29+void Pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
30+void Pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);
31+void Pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
32+void Pthread_mutex_lock(pthread_mutex_t *mutex);
33+void Pthread_mutex_unlock(pthread_mutex_t *mutex);
34+ 
35+void init_shared_pthread_mutex(pthread_mutex_t *mutex, int protocol);
36+int set_rt_prio(pid_t pid, int prio, int policy);
37+int get_rt_prio(pid_t pid);
38+ 
39+#define PROTRW PROT_READ|PROT_WRITE
40+#define MMAP_FLAGS MAP_SHARED|MAP_ANONYMOUS
41+ 
42+#endif /* __PIP_STRESS_H */
@@ -0,0 +1,20 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef __ERROR_H
3+#define __ERROR_H
4+ 
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <stdarg.h>
8+#include <string.h>
9+ 
10+void err_exit(int err, char *fmt, ...) __attribute__((noreturn));
11+void err_msg(char *fmt, ...);
12+void err_msg_n(int err, char *fmt, ...);
13+void err_quit(char *fmt, ...) __attribute__((noreturn));
14+void debug(int enable, char *fmt, ...);
15+void info(int enable, char *fmt, ...);
16+void warn(char *fmt, ...);
17+void fatal(char *fmt, ...) __attribute__((noreturn));
18+void err_doit(int err, const char *fmt, va_list ap);
19+ 
20+#endif /* __ERROR_H */
@@ -0,0 +1,47 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef __RT_GET_CPU_H
3+#define __RT_GET_CPU_H
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <errno.h>
7+#include <unistd.h>
8+#include <sys/syscall.h> /* For SYS_xxx definitions */
9+#include <sched.h>
10+#include <dlfcn.h>
11+#ifdef __NR_getcpu
12+static inline int get_cpu_setup(void) { return 0; }
13+static inline int get_cpu(void)
14+{
15+ int c,s;
16+ /* Show the source of get_cpu */
17+#ifdef DEBUG
18+ fprintf(stderr, "__NR_getcpu\n");
19+#endif
20+ s = syscall(__NR_getcpu, &c, NULL, NULL);
21+ return (s == -1) ? s : c;
22+}
23+#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
24+ && __GLIBC__>=2 && __GLIBC_MINOR__>=6
25+#include <utmpx.h>
26+static inline int get_cpu_setup(void) { return 0; }
27+static inline int get_cpu(void) { return sched_getcpu(); }
28+#else
29+extern int get_cpu_setup(void);
30+extern int (*get_cpu)(void);
31+extern int (*get_cpu_vdsop)(unsigned int *, unsigned int *, void *);
32+ 
33+static inline int getcpu_vdso(void)
34+{
35+ unsigned int c,s;
36+ /* Show the source of get_cpu */
37+#ifdef DEBUG
38+ fprintf(stderr, "getcpu_vdso\n");
39+#endif
40+ s = get_cpu_vdsop(&c, NULL, NULL);
41+ return (s == -1) ? s : c;
42+}
43+ 
44+#endif
45+ 
46+#endif /* __RT_GET_CPU_H */
47+ 
@@ -0,0 +1,21 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef __RT_NUMA_H
3+#define __RT_NUMA_H
4+ 
5+#include <numa.h>
6+ 
7+enum {
8+ AFFINITY_UNSPECIFIED,
9+ AFFINITY_SPECIFIED,
10+ AFFINITY_USEALL
11+};
12+ 
13+int numa_initialize(void);
14+ 
15+int get_available_cpus(struct bitmask *cpumask);
16+int cpu_for_thread_sp(int thread_num, int max_cpus, struct bitmask *cpumask);
17+int cpu_for_thread_ua(int thread_num, int max_cpus);
18+ 
19+int parse_cpumask(char *str, int max_cpus, struct bitmask **cpumask);
20+ 
21+#endif
@@ -0,0 +1,71 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * rt-sched.h - sched_setattr() and sched_getattr() API
4+ * (C) Dario Faggioli <raistlin@linux.it>, 2009, 2010
5+ * Copyright (C) 2014 BMW Car IT GmbH, Daniel Wagner <daniel.wagner@bmw-carit.de
6+ */
7+ 
8+/* This file is based on Dario Faggioli's libdl. Eventually it will be
9+ replaced by a proper implemenation of this API. */
10+ 
11+#ifndef __RT_SCHED_H__
12+#define __RT_SCHED_H__
13+ 
14+#include <stdint.h>
15+#include <sys/types.h>
16+ 
17+#ifndef SCHED_DEADLINE
18+#define SCHED_DEADLINE 6
19+#endif
20+ 
21+#ifdef __x86_64__
22+#define __NR_sched_setattr 314
23+#define __NR_sched_getattr 315
24+#endif
25+ 
26+#ifdef __i386__
27+#define __NR_sched_setattr 351
28+#define __NR_sched_getattr 352
29+#endif
30+ 
31+#ifdef __arm__
32+#ifndef __NR_sched_setattr
33+#define __NR_sched_setattr 380
34+#endif
35+#ifndef __NR_sched_getattr
36+#define __NR_sched_getattr 381
37+#endif
38+#endif
39+ 
40+#ifdef __tilegx__
41+#define __NR_sched_setattr 274
42+#define __NR_sched_getattr 275
43+#endif
44+ 
45+struct sched_attr {
46+ uint32_t size;
47+ uint32_t sched_policy;
48+ uint64_t sched_flags;
49+ 
50+ /* SCHED_NORMAL, SCHED_BATCH */
51+ int32_t sched_nice;
52+ 
53+ /* SCHED_FIFO, SCHED_RR */
54+ uint32_t sched_priority;
55+ 
56+ /* SCHED_DEADLINE */
57+ uint64_t sched_runtime;
58+ uint64_t sched_deadline;
59+ uint64_t sched_period;
60+};
61+ 
62+int sched_setattr(pid_t pid,
63+ const struct sched_attr *attr,
64+ unsigned int flags);
65+ 
66+int sched_getattr(pid_t pid,
67+ struct sched_attr *attr,
68+ unsigned int size,
69+ unsigned int flags);
70+ 
71+#endif /* __RT_SCHED_H__ */
@@ -0,0 +1,89 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+#ifndef __RT_UTILS_H
3+#define __RT_UTILS_H
4+ 
5+#include <stdint.h>
6+ 
7+#define _STR(x) #x
8+#define STR(x) _STR(x)
9+#define MAX_PATH 256
10+ 
11+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
12+ 
13+int check_privs(void);
14+char *get_debugfileprefix(void);
15+int mount_debugfs(char *);
16+int get_tracers(char ***);
17+int valid_tracer(char *);
18+ 
19+int setevent(char *event, char *val);
20+int event_enable(char *event);
21+int event_disable(char *event);
22+int event_enable_all(void);
23+int event_disable_all(void);
24+ 
25+const char *policy_to_string(int policy);
26+uint32_t string_to_policy(const char *str);
27+ 
28+pid_t gettid(void);
29+ 
30+int parse_time_string(char *val);
31+int parse_mem_string(char *str, uint64_t *val);
32+ 
33+void enable_trace_mark(void);
34+void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
35+void disable_trace_mark(void);
36+ 
37+#define MSEC_PER_SEC 1000
38+#define USEC_PER_SEC 1000000
39+#define NSEC_PER_SEC 1000000000
40+#define USEC_TO_NSEC(u) ((u) * 1000)
41+#define USEC_TO_SEC(u) (u) / USEC_PER_SEC)
42+#define NSEC_TO_USEC(n) ((n) / 1000)
43+#define SEC_TO_NSEC(s) ((s) * NSEC_PER_SEC)
44+#define SEC_TO_USEC(s) ((s) * USEC_PER_SEC)
45+ 
46+static inline void tsnorm(struct timespec *ts)
47+{
48+ while (ts->tv_nsec >= NSEC_PER_SEC) {
49+ ts->tv_nsec -= NSEC_PER_SEC;
50+ ts->tv_sec++;
51+ }
52+}
53+ 
54+static inline int tsgreater(struct timespec *a, struct timespec *b)
55+{
56+ return ((a->tv_sec > b->tv_sec) ||
57+ (a->tv_sec == b->tv_sec && a->tv_nsec > b->tv_nsec));
58+}
59+ 
60+static inline int64_t calcdiff(struct timespec t1, struct timespec t2)
61+{
62+ int64_t diff = USEC_PER_SEC * (long long)((int) t1.tv_sec - (int) t2.tv_sec);
63+ diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000;
64+ return diff;
65+}
66+ 
67+static inline int64_t calcdiff_ns(struct timespec t1, struct timespec t2)
68+{
69+ int64_t diff;
70+ diff = NSEC_PER_SEC * (int64_t)((int) t1.tv_sec - (int) t2.tv_sec);
71+ diff += ((int) t1.tv_nsec - (int) t2.tv_nsec);
72+ return diff;
73+}
74+ 
75+static inline int64_t calctime(struct timespec t)
76+{
77+ int64_t time;
78+ time = USEC_PER_SEC * t.tv_sec;
79+ time += ((int) t.tv_nsec) / 1000;
80+ return time;
81+}
82+ 
83+void rt_init(int argc, char *argv[]);
84+ 
85+void rt_write_json(const char *filename, int return_code,
86+ void (*cb)(FILE *, void *),
87+ void *data);
88+ 
89+#endif /* __RT_UTILS.H */
@@ -0,0 +1,181 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Latency histograms
4+ *
5+ * Copyright 2023 Red Hat Inc.
6+ */
7+ 
8+#include <errno.h>
9+#include <stdbool.h>
10+#include <stdlib.h>
11+#include <string.h>
12+#include "histogram.h"
13+ 
14+int hist_init(struct histogram *h, unsigned long width, unsigned long num)
15+{
16+ memset(h, 0, sizeof(*h));
17+ h->width = width;
18+ h->num = num;
19+ 
20+ h->buckets = calloc(num, sizeof(unsigned long));
21+ if (!h->buckets)
22+ return -ENOMEM;
23+ 
24+ return 0;
25+}
26+ 
27+int hist_init_oflow(struct histogram *h, unsigned long num)
28+{
29+ h->oflow_bufsize = num;
30+ h->oflows = calloc(num, sizeof(unsigned long));
31+ if (!h->oflows)
32+ return -ENOMEM;
33+ 
34+ return 0;
35+}
36+ 
37+void hist_destroy(struct histogram *h)
38+{
39+ free(h->oflows);
40+ h->oflows = NULL;
41+ free(h->buckets);
42+ h->buckets = NULL;
43+}
44+ 
45+int hist_sample(struct histogram *h, uint64_t sample)
46+{
47+ unsigned long bucket = sample / h->width;
48+ unsigned long extra;
49+ unsigned long event = h->events++;
50+ int ret;
51+ 
52+ if (bucket < h->num) {
53+ h->buckets[bucket]++;
54+ return 0;
55+ }
56+ 
57+ ret = HIST_OVERFLOW;
58+ extra = bucket - h->num;
59+ if (h->oflow_magnitude + extra > h->oflow_magnitude)
60+ h->oflow_magnitude += extra;
61+ else
62+ ret |= HIST_OVERFLOW_MAG;
63+ 
64+ if (h->oflows) {
65+ if (h->oflow_count < h->oflow_bufsize)
66+ h->oflows[h->oflow_count] = event;
67+ else
68+ ret |= HIST_OVERFLOW_LOG;
69+ }
70+ 
71+ h->oflow_count++;
72+ return ret;
73+}
74+ 
75+int hset_init(struct histoset *hs, unsigned long num_histos,
76+ unsigned long bucket_width, unsigned long num_buckets,
77+ unsigned long overflow)
78+{
79+ unsigned long i;
80+ 
81+ if (num_histos == 0)
82+ return -EINVAL;
83+ 
84+ hs->num_histos = num_histos;
85+ hs->num_buckets = num_buckets;
86+ hs->histos = calloc(num_histos, sizeof(struct histogram));
87+ if (!hs->histos)
88+ return -ENOMEM;
89+ 
90+ for (i = 0; i < num_histos; i++) {
91+ if (hist_init(&hs->histos[i], bucket_width, num_buckets))
92+ goto fail;
93+ if (overflow && hist_init_oflow(&hs->histos[i], overflow))
94+ goto fail;
95+ }
96+ 
97+ return 0;
98+ 
99+fail:
100+ hset_destroy(hs);
101+ return -ENOMEM;
102+}
103+ 
104+void hset_destroy(struct histoset *hs)
105+{
106+ unsigned long i;
107+ 
108+ if (hs->histos) {
109+ for (i = 0; i < hs->num_histos; i++)
110+ hist_destroy(&hs->histos[i]);
111+ }
112+ 
113+ free(hs->histos);
114+ hs->histos = NULL;
115+}
116+ 
117+void hset_print_bucket(struct histoset *hs, FILE *f, const char *pre,
118+ unsigned long bucket, unsigned long flags)
119+{
120+ unsigned long long sum = 0;
121+ unsigned long i;
122+ 
123+ if (bucket >= hs->num_buckets)
124+ return;
125+ 
126+ for (i = 0; i < hs->num_histos; i++)
127+ sum += hs->histos[i].buckets[bucket];
128+ 
129+ if (sum == 0)
130+ return;
131+ if (pre)
132+ fprintf(f, "%s", pre);
133+ 
134+ for (i = 0; i < hs->num_histos; i++) {
135+ unsigned long val = hs->histos[i].buckets[bucket];
136+ 
137+ if (i != 0)
138+ fprintf(f, "\t");
139+ fprintf(f, "%06lu", val);
140+ }
141+ 
142+ if (flags & HSET_PRINT_SUM)
143+ fprintf(f, "\t%06llu", sum);
144+ 
145+ fprintf(f, "\n");
146+}
147+ 
148+void hist_print_json(struct histogram *h, FILE *f)
149+{
150+ unsigned long i;
151+ bool comma = false;
152+ 
153+ for (i = 0; i < h->num; i++) {
154+ unsigned long val = h->buckets[i];
155+ 
156+ if (val != 0) {
157+ if (comma)
158+ fprintf(f, ",");
159+ fprintf(f, "\n \"%lu\": %lu", i, val);
160+ comma = true;
161+ }
162+ }
163+ 
164+ fprintf(f, "\n");
165+}
166+ 
167+void hist_print_oflows(struct histogram *h, FILE *f)
168+{
169+ unsigned long i;
170+ 
171+ for (i = 0; i < h->oflow_count; i++) {
172+ if (i >= h->oflow_bufsize)
173+ break;
174+ if (i != 0)
175+ fprintf(f, " ");
176+ fprintf(f, "%05lu", h->oflows[i]);
177+ }
178+ 
179+ if (i >= h->oflow_bufsize)
180+ fprintf(f, " # %05lu others", h->oflow_count - h->oflow_bufsize);
181+}
@@ -0,0 +1,101 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Copyright (C) 2009 John Kacur <jkacur@redhat.com>
4+ *
5+ * error routines, similar to those found in
6+ * Advanced Programming in the UNIX Environment 2nd ed.
7+ */
8+#include "rt-error.h"
9+ 
10+/* Print an error message, plus a message for err and exit with error err */
11+void err_exit(int err, char *fmt, ...)
12+{
13+ va_list ap;
14+ va_start(ap, fmt);
15+ err_doit(err, fmt, ap);
16+ va_end(ap);
17+ exit(err);
18+}
19+ 
20+/* print an error message and return */
21+void err_msg(char *fmt, ...)
22+{
23+ va_list ap;
24+ va_start(ap, fmt);
25+ err_doit(0, fmt, ap);
26+ va_end(ap);
27+ return;
28+}
29+ 
30+/* Print an error message, plus a message for err, and return */
31+void err_msg_n(int err, char *fmt, ...)
32+{
33+ va_list ap;
34+ va_start(ap, fmt);
35+ err_doit(err, fmt, ap);
36+ va_end(ap);
37+ return;
38+}
39+ 
40+/* print an error message and quit */
41+void err_quit(char *fmt, ...)
42+{
43+ va_list ap;
44+ va_start(ap, fmt);
45+ err_doit(0, fmt, ap);
46+ va_end(ap);
47+ exit(1);
48+}
49+ 
50+void debug(int enable, char *fmt, ...)
51+{
52+ if (enable) {
53+ va_list ap;
54+ 
55+ va_start(ap, fmt);
56+ fputs("DEBUG: ", stderr);
57+ err_doit(0, fmt, ap);
58+ va_end(ap);
59+ }
60+}
61+ 
62+void info(int enable, char *fmt, ...)
63+{
64+ if (enable) {
65+ va_list ap;
66+ 
67+ va_start(ap, fmt);
68+ fputs("INFO: ", stderr);
69+ err_doit(0, fmt, ap);
70+ va_end(ap);
71+ }
72+}
73+ 
74+void warn(char *fmt, ...)
75+{
76+ va_list ap;
77+ 
78+ va_start(ap, fmt);
79+ fputs("WARN: ", stderr);
80+ err_doit(0, fmt, ap);
81+ va_end(ap);
82+}
83+ 
84+void fatal(char *fmt, ...)
85+{
86+ va_list ap;
87+ 
88+ va_start(ap, fmt);
89+ fputs("FATAL: ", stderr);
90+ err_doit(0, fmt, ap);
91+ va_end(ap);
92+ exit(EXIT_FAILURE);
93+}
94+ 
95+void err_doit(int err, const char *fmt, va_list ap)
96+{
97+ vfprintf(stderr, fmt, ap);
98+ if (err)
99+ fprintf(stderr, ": %s\n", strerror(err));
100+ return;
101+}
@@ -0,0 +1,28 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Copyright (C) 2009 John Kacur <jkacur@redhat.com>
4+ */
5+#include "rt-get_cpu.h"
6+#ifdef __NR_getcpu
7+#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
8+ && __GLIBC__>=2 && __GLIBC_MINOR__>=6
9+#else
10+int (*get_cpu)(void);
11+int (*get_cpu_vdsop)(unsigned int *, unsigned int *, void *);
12+ 
13+int get_cpu_setup(void)
14+{
15+ void *handle = dlopen("linux-vdso.so.1", RTLD_LAZY);
16+ get_cpu_vdsop = NULL;
17+ if (handle) {
18+ get_cpu_vdsop = dlsym(handle, "getcpu");
19+ dlclose(handle);
20+ if (get_cpu_vdsop) {
21+ get_cpu = getcpu_vdso;
22+ return 0;
23+ }
24+ }
25+ return -1;
26+}
27+ 
28+#endif
@@ -0,0 +1,148 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Copyright 2020 Daniel Wagner <dwagner@suse.de>
4+ * Copyright 2020 John Kacur <jkacur@redhat.com>
5+ */
6+ 
7+#include <sys/types.h>
8+#include <unistd.h>
9+#include <errno.h>
10+#include <sched.h>
11+#include <pthread.h>
12+ 
13+#include "rt-error.h"
14+#include "rt-numa.h"
15+ 
16+/*
17+ * numa_available() must be called before any other calls to the numa library
18+ * returns 1 if numa is available, or 0 if numa is not available
19+ */
20+int numa_initialize(void)
21+{
22+ static int is_initialized; // Only call numa_available once
23+ static int numa;
24+ 
25+ if (is_initialized == 1)
26+ return numa;
27+ 
28+ if (numa_available() != -1)
29+ numa = 1;
30+ 
31+ is_initialized = 1;
32+ 
33+ return numa;
34+}
35+ 
36+int get_available_cpus(struct bitmask *cpumask)
37+{
38+ cpu_set_t cpuset;
39+ int ret;
40+ 
41+ if (cpumask)
42+ return numa_bitmask_weight(cpumask);
43+ 
44+ CPU_ZERO(&cpuset);
45+ 
46+ ret = sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
47+ if (ret < 0)
48+ fatal("sched_getaffinity failed: %m\n");
49+ 
50+ return CPU_COUNT(&cpuset);
51+}
52+ 
53+int cpu_for_thread_sp(int thread_num, int max_cpus, struct bitmask *cpumask)
54+{
55+ int m, cpu, i, num_cpus;
56+ 
57+ num_cpus = numa_bitmask_weight(cpumask);
58+ 
59+ if (num_cpus == 0)
60+ fatal("No allowable cpus to run on\n");
61+ 
62+ m = thread_num % num_cpus;
63+ 
64+ /* there are num_cpus bits set, we want position of m'th one */
65+ for (i = 0, cpu = 0; i < max_cpus; i++) {
66+ if (numa_bitmask_isbitset(cpumask, i)) {
67+ if (cpu == m)
68+ return i;
69+ cpu++;
70+ }
71+ }
72+ warn("Bug in cpu mask handling code.\n");
73+ return 0;
74+}
75+ 
76+/* cpu_for_thread AFFINITY_USEALL */
77+int cpu_for_thread_ua(int thread_num, int max_cpus)
78+{
79+ int res, num_cpus, i, m, cpu;
80+ cpu_set_t cpuset;
81+ 
82+ CPU_ZERO(&cpuset);
83+ 
84+ res = sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
85+ if (res != 0)
86+ fatal("sched_getaffinity failed: %s\n", strerror(res));
87+ 
88+ num_cpus = CPU_COUNT(&cpuset);
89+ m = thread_num % num_cpus;
90+ 
91+ for (i = 0, cpu = 0; i < max_cpus; i++) {
92+ if (CPU_ISSET(i, &cpuset)) {
93+ if (cpu == m)
94+ return i;
95+ cpu++;
96+ }
97+ }
98+ 
99+ warn("Bug in cpu mask handling code.\n");
100+ return 0;
101+}
102+ 
103+/*
104+ * After this function is called, affinity_mask is the intersection of
105+ * the user supplied affinity mask and the affinity mask from the run
106+ * time environment
107+ */
108+static void use_current_cpuset(int max_cpus, struct bitmask *cpumask)
109+{
110+ struct bitmask *curmask;
111+ int i;
112+ 
113+ curmask = numa_allocate_cpumask();
114+ numa_sched_getaffinity(getpid(), curmask);
115+ 
116+ /*
117+ * Clear bits that are not set in both the cpuset from the
118+ * environment, and in the user specified affinity.
119+ */
120+ for (i = 0; i < max_cpus; i++) {
121+ if ((!numa_bitmask_isbitset(cpumask, i)) ||
122+ (!numa_bitmask_isbitset(curmask, i)))
123+ numa_bitmask_clearbit(cpumask, i);
124+ }
125+ 
126+ numa_bitmask_free(curmask);
127+}
128+ 
129+int parse_cpumask(char *str, int max_cpus, struct bitmask **cpumask)
130+{
131+ struct bitmask *mask;
132+ 
133+ mask = numa_parse_cpustring_all(str);
134+ if (!mask)
135+ return -ENOMEM;
136+ 
137+ if (numa_bitmask_weight(mask) == 0) {
138+ numa_bitmask_free(mask);
139+ *cpumask = NULL;
140+ return 0;
141+ }
142+ 
143+ if (strchr(str, '!') != NULL || strchr(str, '+') != NULL)
144+ use_current_cpuset(max_cpus, mask);
145+ *cpumask = mask;
146+ 
147+ return 0;
148+}
@@ -0,0 +1,30 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * rt-sched.h - sched_setattr() and sched_getattr() API
4+ *
5+ * (C) Dario Faggioli <raistlin@linux.it>, 2009, 2010
6+ * Copyright (C) 2014 BMW Car IT GmbH, Daniel Wagner <daniel.wagner@bmw-carit.de
7+ */
8+ 
9+/* This file is based on Dario Faggioli's libdl. Eventually it will be
10+ replaced by a proper implemenation of this API. */
11+ 
12+#include <unistd.h>
13+#include <sys/syscall.h>
14+ 
15+#include "rt-sched.h"
16+ 
17+int sched_setattr(pid_t pid,
18+ const struct sched_attr *attr,
19+ unsigned int flags)
20+{
21+ return syscall(__NR_sched_setattr, pid, attr, flags);
22+}
23+ 
24+int sched_getattr(pid_t pid,
25+ struct sched_attr *attr,
26+ unsigned int size,
27+ unsigned int flags)
28+{
29+ return syscall(__NR_sched_getattr, pid, attr, size, flags);
30+}
@@ -0,0 +1,595 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Copyright (C) 2009 Carsten Emde <carsten.emde@osadl.org>
4+ * Copyright (C) 2010 Clark Williams <williams@redhat.com>
5+ * Copyright (C) 2015 John Kacur <jkacur@redhat.com>
6+ *
7+ * based on functions from cyclictest that has
8+ * (C) 2008-2009 Clark Williams <williams@redhat.com>
9+ * (C) 2005-2007 Thomas Gleixner <tglx@linutronix.de>
10+ */
11+#include <stdio.h>
12+#include <stdlib.h>
13+#include <string.h>
14+#include <sched.h>
15+#include <stdarg.h>
16+#include <errno.h>
17+#include <ctype.h>
18+#include <fcntl.h>
19+#include <sys/types.h>
20+#include <sys/stat.h>
21+#include <unistd.h>
22+#include <sys/syscall.h> /* For SYS_gettid definitions */
23+#include <sys/utsname.h>
24+#include <time.h>
25+#include <sys/time.h>
26+ 
27+#include "rt-utils.h"
28+#include "rt-sched.h"
29+#include "rt-error.h"
30+ 
31+#define TRACEBUFSIZ 1024
32+#define MAX_COMMAND_LINE 4096
33+#define MAX_TS_SIZE 64
34+ 
35+static char debugfileprefix[MAX_PATH];
36+static char *fileprefix;
37+static int trace_fd = -1;
38+static int tracemark_fd = -1;
39+static __thread char tracebuf[TRACEBUFSIZ];
40+static char test_cmdline[MAX_COMMAND_LINE];
41+static char ts_start[MAX_TS_SIZE];
42+ 
43+/*
44+ * Finds the tracing directory in a mounted debugfs
45+ */
46+char *get_debugfileprefix(void)
47+{
48+ char type[100];
49+ FILE *fp;
50+ int size;
51+ int found = 0;
52+ struct stat s;
53+ 
54+ if (debugfileprefix[0] != '\0')
55+ goto out;
56+ 
57+ /* look in the "standard" mount point first */
58+ if ((stat("/sys/kernel/debug/tracing", &s) == 0) && S_ISDIR(s.st_mode)) {
59+ strcpy(debugfileprefix, "/sys/kernel/debug/tracing/");
60+ goto out;
61+ }
62+ 
63+ /* now look in the "other standard" place */
64+ if ((stat("/debug/tracing", &s) == 0) && S_ISDIR(s.st_mode)) {
65+ strcpy(debugfileprefix, "/debug/tracing/");
66+ goto out;
67+ }
68+ 
69+ /* oh well, parse /proc/mounts and see if it's there */
70+ if ((fp = fopen("/proc/mounts", "r")) == NULL)
71+ goto out;
72+ 
73+ while (fscanf(fp, "%*s %"
74+ STR(MAX_PATH)
75+ "s %99s %*s %*d %*d\n",
76+ debugfileprefix, type) == 2) {
77+ if (strcmp(type, "debugfs") == 0) {
78+ found = 1;
79+ break;
80+ }
81+ /* stupid check for systemd-style autofs mount */
82+ if ((strcmp(debugfileprefix, "/sys/kernel/debug") == 0) &&
83+ (strcmp(type, "systemd") == 0)) {
84+ found = 1;
85+ break;
86+ }
87+ }
88+ fclose(fp);
89+ 
90+ if (!found) {
91+ debugfileprefix[0] = '\0';
92+ goto out;
93+ }
94+ 
95+ size = sizeof(debugfileprefix) - strlen(debugfileprefix);
96+ strncat(debugfileprefix, "/tracing/", size);
97+ 
98+out:
99+ return debugfileprefix;
100+}
101+ 
102+int mount_debugfs(char *path)
103+{
104+ char *mountpoint = path;
105+ char cmd[MAX_PATH];
106+ char *prefix;
107+ int ret;
108+ 
109+ /* if it's already mounted just return */
110+ prefix = get_debugfileprefix();
111+ if (strlen(prefix) != 0) {
112+ info(1, "debugfs mountpoint: %s\n", prefix);
113+ return 0;
114+ }
115+ if (!mountpoint)
116+ mountpoint = "/sys/kernel/debug";
117+ 
118+ sprintf(cmd, "mount -t debugfs debugfs %s", mountpoint);
119+ ret = system(cmd);
120+ if (ret != 0) {
121+ fprintf(stderr, "Error mounting debugfs at %s: %s\n",
122+ mountpoint, strerror(errno));
123+ return -1;
124+ }
125+ return 0;
126+}
127+ 
128+static char **tracer_list;
129+static char *tracer_buffer;
130+static int num_tracers;
131+#define CHUNKSZ 1024
132+ 
133+/*
134+ * return a list of the tracers configured into the running kernel
135+ */
136+ 
137+int get_tracers(char ***list)
138+{
139+ int ret;
140+ FILE *fp;
141+ char buffer[CHUNKSZ];
142+ char *prefix = get_debugfileprefix();
143+ char *tmpbuf = NULL;
144+ char *ptr;
145+ int tmpsz = 0;
146+ 
147+ /* if we've already parse it, return what we have */
148+ if (tracer_list) {
149+ *list = tracer_list;
150+ return num_tracers;
151+ }
152+ 
153+ /* open the tracing file available_tracers */
154+ sprintf(buffer, "%savailable_tracers", prefix);
155+ if ((fp = fopen(buffer, "r")) == NULL)
156+ fatal("Can't open %s for reading\n", buffer);
157+ 
158+ /* allocate initial buffer */
159+ ptr = tmpbuf = malloc(CHUNKSZ);
160+ if (ptr == NULL)
161+ fatal("error allocating initial space for tracer list\n");
162+ 
163+ /* read in the list of available tracers */
164+ while ((ret = fread(buffer, sizeof(char), CHUNKSZ, fp))) {
165+ if ((ptr+ret+1) > (tmpbuf+tmpsz)) {
166+ tmpbuf = realloc(tmpbuf, tmpsz + CHUNKSZ);
167+ if (tmpbuf == NULL)
168+ fatal("error allocating space for list of valid tracers\n");
169+ tmpsz += CHUNKSZ;
170+ }
171+ strncpy(ptr, buffer, ret);
172+ ptr += ret;
173+ }
174+ fclose(fp);
175+ if (tmpsz == 0)
176+ fatal("error reading available tracers\n");
177+ 
178+ tracer_buffer = tmpbuf;
179+ 
180+ /* get a buffer for the pointers to tracers */
181+ if (!(tracer_list = malloc(sizeof(char *))))
182+ fatal("error allocatinging tracer list buffer\n");
183+ 
184+ /* parse the buffer */
185+ ptr = strtok(tmpbuf, " \t\n\r");
186+ do {
187+ tracer_list[num_tracers++] = ptr;
188+ tracer_list = realloc(tracer_list, sizeof(char*)*(num_tracers+1));
189+ tracer_list[num_tracers] = NULL;
190+ } while ((ptr = strtok(NULL, " \t\n\r")) != NULL);
191+ 
192+ /* return the list and number of tracers */
193+ *list = tracer_list;
194+ return num_tracers;
195+}
196+ 
197+ 
198+/*
199+ * return zero if tracername is not a valid tracer, non-zero if it is
200+ */
201+ 
202+int valid_tracer(char *tracername)
203+{
204+ char **list;
205+ int ntracers;
206+ int i;
207+ 
208+ ntracers = get_tracers(&list);
209+ if (ntracers == 0 || tracername == NULL)
210+ return 0;
211+ for (i = 0; i < ntracers; i++)
212+ if (strncmp(list[i], tracername, strlen(list[i])) == 0)
213+ return 1;
214+ return 0;
215+}
216+ 
217+/*
218+ * enable event tracepoint
219+ */
220+int setevent(char *event, char *val)
221+{
222+ char *prefix = get_debugfileprefix();
223+ char buffer[MAX_PATH];
224+ int fd;
225+ int ret;
226+ 
227+ sprintf(buffer, "%s%s", prefix, event);
228+ if ((fd = open(buffer, O_WRONLY)) < 0) {
229+ warn("unable to open %s\n", buffer);
230+ return -1;
231+ }
232+ if ((ret = write(fd, val, strlen(val))) < 0) {
233+ warn("unable to write %s to %s\n", val, buffer);
234+ close(fd);
235+ return -1;
236+ }
237+ close(fd);
238+ return 0;
239+}
240+ 
241+int event_enable_all(void)
242+{
243+ return setevent("events/enable", "1");
244+}
245+ 
246+int event_disable_all(void)
247+{
248+ return setevent("events/enable", "0");
249+}
250+ 
251+int event_enable(char *event)
252+{
253+ char path[MAX_PATH];
254+ 
255+ sprintf(path, "events/%s/enable", event);
256+ return setevent(path, "1");
257+}
258+ 
259+int event_disable(char *event)
260+{
261+ char path[MAX_PATH];
262+ 
263+ sprintf(path, "events/%s/enable", event);
264+ return setevent(path, "0");
265+}
266+ 
267+int check_privs(void)
268+{
269+ int policy = sched_getscheduler(0);
270+ struct sched_param param, old_param;
271+ 
272+ /* if we're already running a realtime scheduler
273+ * then we *should* be able to change things later
274+ */
275+ if (policy == SCHED_FIFO || policy == SCHED_RR)
276+ return 0;
277+ 
278+ /* first get the current parameters */
279+ if (sched_getparam(0, &old_param)) {
280+ fprintf(stderr, "unable to get scheduler parameters\n");
281+ return 1;
282+ }
283+ param = old_param;
284+ 
285+ /* try to change to SCHED_FIFO */
286+ param.sched_priority = 1;
287+ if (sched_setscheduler(0, SCHED_FIFO, &param)) {
288+ fprintf(stderr, "Unable to change scheduling policy!\n");
289+ fprintf(stderr, "either run as root or join realtime group\n");
290+ return 1;
291+ }
292+ 
293+ /* we're good; change back and return success */
294+ return sched_setscheduler(0, policy, &old_param);
295+}
296+ 
297+const char *policy_to_string(int policy)
298+{
299+ switch (policy) {
300+ case SCHED_OTHER:
301+ return "SCHED_OTHER";
302+ case SCHED_FIFO:
303+ return "SCHED_FIFO";
304+ case SCHED_RR:
305+ return "SCHED_RR";
306+ case SCHED_BATCH:
307+ return "SCHED_BATCH";
308+ case SCHED_IDLE:
309+ return "SCHED_IDLE";
310+ case SCHED_DEADLINE:
311+ return "SCHED_DEADLINE";
312+ }
313+ 
314+ return "unknown";
315+}
316+ 
317+uint32_t string_to_policy(const char *str)
318+{
319+ if (!strcmp(str, "other"))
320+ return SCHED_OTHER;
321+ else if (!strcmp(str, "fifo"))
322+ return SCHED_FIFO;
323+ else if (!strcmp(str, "rr"))
324+ return SCHED_RR;
325+ else if (!strcmp(str, "batch"))
326+ return SCHED_BATCH;
327+ else if (!strcmp(str, "idle"))
328+ return SCHED_IDLE;
329+ else if (!strcmp(str, "deadline"))
330+ return SCHED_DEADLINE;
331+ 
332+ return 0;
333+}
334+ 
335+pid_t gettid(void)
336+{
337+ return syscall(SYS_gettid);
338+}
339+ 
340+/*
341+ * parse an input value as a base10 value followed by an optional
342+ * suffix. The input value is presumed to be in seconds, unless
343+ * followed by a modifier suffix: m=minutes, h=hours, d=days
344+ *
345+ * the return value is a value in seconds
346+ */
347+int parse_time_string(char *val)
348+{
349+ char *end;
350+ int t = strtol(val, &end, 10);
351+ if (end) {
352+ switch (*end) {
353+ case 'm':
354+ case 'M':
355+ t *= 60;
356+ break;
357+ 
358+ case 'h':
359+ case 'H':
360+ t *= 60*60;
361+ break;
362+ 
363+ case 'd':
364+ case 'D':
365+ t *= 24*60*60;
366+ break;
367+ 
368+ }
369+ }
370+ return t;
371+}
372+ 
373+int parse_mem_string(char *str, uint64_t *val)
374+{
375+ char *endptr;
376+ int v = strtol(str, &endptr, 10);
377+ 
378+ if (!*endptr)
379+ return v;
380+ 
381+ switch (*endptr) {
382+ case 'g':
383+ case 'G':
384+ v *= 1024;
385+ __attribute__ ((fallthrough));
386+ case 'm':
387+ case 'M':
388+ v *= 1024;
389+ __attribute__ ((fallthrough));
390+ case 'k':
391+ case 'K':
392+ v *= 1024;
393+ __attribute__ ((fallthrough));
394+ case 'b':
395+ case 'B':
396+ break;
397+ default:
398+ return -1;
399+ }
400+ 
401+ *val = v;
402+ 
403+ return 0;
404+}
405+ 
406+static void open_tracemark_fd(void)
407+{
408+ char path[MAX_PATH];
409+ 
410+ /*
411+ * open the tracemark file if it's not already open
412+ */
413+ if (tracemark_fd < 0) {
414+ sprintf(path, "%s/%s", fileprefix, "trace_marker");
415+ tracemark_fd = open(path, O_WRONLY);
416+ if (tracemark_fd < 0) {
417+ warn("unable to open trace_marker file: %s\n", path);
418+ return;
419+ }
420+ }
421+ 
422+ /*
423+ * if we're not tracing and the tracing_on fd is not open,
424+ * open the tracing_on file so that we can stop the trace
425+ * if we hit a breaktrace threshold
426+ */
427+ if (trace_fd < 0) {
428+ sprintf(path, "%s/%s", fileprefix, "tracing_on");
429+ if ((trace_fd = open(path, O_WRONLY)) < 0)
430+ warn("unable to open tracing_on file: %s\n", path);
431+ }
432+}
433+ 
434+static void close_tracemark_fd(void)
435+{
436+ if (tracemark_fd > 0)
437+ close(tracemark_fd);
438+ 
439+ if (trace_fd > 0)
440+ close(trace_fd);
441+}
442+ 
443+static int trace_file_exists(char *name)
444+{
445+ struct stat sbuf;
446+ char *tracing_prefix = get_debugfileprefix();
447+ char path[MAX_PATH];
448+ strcat(strcpy(path, tracing_prefix), name);
449+ return stat(path, &sbuf) ? 0 : 1;
450+}
451+ 
452+static void debugfs_prepare(void)
453+{
454+ if (mount_debugfs(NULL))
455+ fatal("could not mount debugfs");
456+ 
457+ fileprefix = get_debugfileprefix();
458+ if (!trace_file_exists("tracing_enabled") &&
459+ !trace_file_exists("tracing_on"))
460+ warn("tracing_enabled or tracing_on not found\n"
461+ "debug fs not mounted");
462+}
463+ 
464+void tracemark(char *fmt, ...)
465+{
466+ va_list ap;
467+ int len;
468+ 
469+ /* bail out if we're not tracing */
470+ /* or if the kernel doesn't support trace_mark */
471+ if (tracemark_fd < 0 || trace_fd < 0)
472+ return;
473+ 
474+ va_start(ap, fmt);
475+ len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
476+ va_end(ap);
477+ 
478+ /* write the tracemark message */
479+ write(tracemark_fd, tracebuf, len);
480+ 
481+ /* now stop any trace */
482+ write(trace_fd, "0\n", 2);
483+}
484+ 
485+void enable_trace_mark(void)
486+{
487+ debugfs_prepare();
488+ open_tracemark_fd();
489+}
490+ 
491+void disable_trace_mark(void)
492+{
493+ close_tracemark_fd();
494+}
495+ 
496+static void get_timestamp(char *tsbuf)
497+{
498+ struct timeval tv;
499+ struct tm *tm;
500+ time_t t;
501+ 
502+ gettimeofday(&tv, NULL);
503+ t = tv.tv_sec;
504+ tm = localtime(&t);
505+ /* RFC 2822-compliant date format */
506+ strftime(tsbuf, MAX_TS_SIZE, "%a, %d %b %Y %T %z", tm);
507+}
508+ 
509+void rt_init(int argc, char *argv[])
510+{
511+ int offset = 0;
512+ int len, i;
513+ 
514+ test_cmdline[0] = '\0';
515+ 
516+ /*
517+ * getopt_long() permutes the contents of argv as it scans, so
518+ * that eventually all the nonoptions are at the end. Make a
519+ * copy before calling getopt_long().
520+ */
521+ for (i = 0; i < argc;) {
522+ len = strlen(argv[i]);
523+ if (offset + len + 1 >= MAX_COMMAND_LINE)
524+ break;
525+ 
526+ strcat(test_cmdline, argv[i]);
527+ i++;
528+ if (i < argc)
529+ strcat(test_cmdline, " ");
530+ 
531+ offset += len + 1;
532+ }
533+ 
534+ get_timestamp(ts_start);
535+}
536+ 
537+void rt_write_json(const char *filename, int return_code,
538+ void (*cb)(FILE *, void *),
539+ void *data)
540+{
541+ unsigned char buf[1];
542+ struct utsname uts;
543+ char ts_end[MAX_TS_SIZE];
544+ FILE *f, *s;
545+ size_t n;
546+ int rt = 0;
547+ 
548+ if (!filename || !strcmp("-", filename)) {
549+ f = stdout;
550+ } else {
551+ f = fopen(filename, "w");
552+ if (!f)
553+ err_exit(errno, "Failed to open '%s'\n", filename);
554+ }
555+ 
556+ get_timestamp(ts_end);
557+ 
558+ s = fopen("/sys/kernel/realtime", "r");
559+ if (s) {
560+ n = fread(buf, 1, 1, s);
561+ if (n == 1 && buf[0] == '1')
562+ rt = 1;
563+ fclose(s);
564+ }
565+ 
566+ if (uname(&uts))
567+ err_exit(errno, "Could not retrieve system information");
568+ 
569+ fprintf(f, "{\n");
570+ fprintf(f, " \"file_version\": 1,\n");
571+ fprintf(f, " \"cmdline:\": \"%s\",\n", test_cmdline);
572+ fprintf(f, " \"rt_test_version:\": \"%1.2f\",\n", VERSION);
573+ fprintf(f, " \"start_time\": \"%s\",\n", ts_start);
574+ fprintf(f, " \"end_time\": \"%s\",\n", ts_end);
575+ fprintf(f, " \"return_code\": %d,\n", return_code);
576+ fprintf(f, " \"sysinfo\": {\n");
577+ fprintf(f, " \"sysname\": \"%s\",\n", uts.sysname);
578+ fprintf(f, " \"nodename\": \"%s\",\n", uts.nodename);
579+ fprintf(f, " \"release\": \"%s\",\n", uts.release);
580+ fprintf(f, " \"version\": \"%s\",\n", uts.version);
581+ fprintf(f, " \"machine\": \"%s\",\n", uts.machine);
582+ fprintf(f, " \"realtime\": %d\n", rt);
583+ 
584+ if (cb) {
585+ fprintf(f, " },\n");
586+ (cb)(f, data);
587+ } else {
588+ fprintf(f, " }\n");
589+ }
590+ 
591+ fprintf(f, "}\n");
592+ 
593+ if (!filename || strcmp("-", filename))
594+ fclose(f);
595+}
@@ -0,0 +1,78 @@
1+.TH OSLAT 8 "August 17, 2020"
2+# SPDX-License-Identifier: GPL-3.0-only
3+.\" for manpage-specific macros, see man(7)
4+.SH NAME
5+oslat \- OS Latency Detector
6+.SH SYNOPSIS
7+.SY oslat
8+.RI "[ \-shvz ] [ \-b " bucket-size " ] [ \-B " bias " ] [ \-c " cpu-list " ] \
9+[ \-C " cpu-main-thread " ] [ \-f " rt-prio " ] [ \-\-json " filename " ] \
10+[ \-m " workload-mem " ] [\-t " runtime " ] [ \-T " trace-threshold " ] \
11+[ \-w " workload " ] [ \-W " bucket-width " ]"
12+.SH DESCRIPTION
13+.B oslat
14+is an open source userspace polling mode stress program to detect OS level
15+latency. The program runs a busy loop with no or various workloads, collecting
16+TSC information and measuring the time frequently during the process.
17+.SH OPTIONS
18+.TP
19+.B \-b, \-\-bucket-size=N
20+Specify the number of the buckets (4-1024).
21+.TP
22+.B \-B, \-\-bias
23+Add a bias to all the buckets using the estimated mininum.
24+.TP
25+.B \-c, \-\-cpu-list=CPULIST
26+Specify CPUs to run on. For example, '1,3,5,7-15'.
27+.TP
28+.B \-C, \-\-cpu-main-thread=CORE
29+Specify which CPU the main thread runs on. Default is cpu0.
30+.TP
31+.B \-f, \-\-rtprio=PRIORITY
32+Using specific SCHED_FIFO priority (1-99). Otherwise use the default
33+priority, normally it will be SCHED_OTHER.
34+.TP
35+.B \-\-json=FILENAME
36+Write final results into FILENAME, JSON formatted.
37+.TP
38+.B \-m, \-\-workload-mem=SIZE
39+Size of the memory to use for the workload (e.g., 4K, 1M).
40+Total memory usage will be this value multiplies 2*N,
41+because there will be src/dst buffers for each thread, and
42+N is the number of processors for testing.
43+.TP
44+.B \-D, \-\-duration=TIME
45+Specify test duration, e.g., 60, 20m, 2H (m/M: minutes, h/H: hours, d/D: days).
46+By default the unit is s/second.
47+.TP
48+.B \-T, \-\-trace-threshold=THRESHOLD
49+Stop the test when threshold triggered (in USEC). At the meantime, print a
50+marker in ftrace and stop ftrace too.
51+.TP
52+.B \-w, \-\-workload=WORKLOAD
53+Specify a kind of workload, default is no workload. Options: "no", "memmove".
54+.TP
55+.B \-s, \-\-single-preheat
56+Use a single thread when measuring latency at preheat stage
57+NOTE: please make sure the CPU frequency on all testing cores
58+are locked before using this parmater. If you don't know how
59+to lock the freq then please don't use this parameter.
60+.TP
61+.B \-W, \-\-bucket-width
62+Interval between buckets in nanoseconds
63+ 
64+NOTE: Widths not a multiple of 1000 cause ns-precision output
65+You are responsible for considering the impact of measurement
66+overhead at the nanosecond scale.
67+.TP
68+.B \-h, \-\-help
69+Show the help message.
70+.TP
71+.B \-v, \-\-version
72+Show the version of the program.
73+.TP
74+.B \-z, \-\-zero-omit
75+Don't display buckets in the output histogram if all zeros.
76+.SH AUTHOR
77+.B oslat
78+was written by Peter Xu <peterx@redhat.com>.
@@ -0,0 +1,956 @@
1+// SPDX-License-Identifier: GPL-3.0-only
2+/*
3+ * oslat - OS latency detector
4+ *
5+ * Copyright 2020 Red Hat Inc.
6+ *
7+ * Authors: Peter Xu <peterx@redhat.com>
8+ *
9+ * Some of the utility code based on sysjitter-1.3:
10+ * Copyright 2010-2015 David Riddoch <david@riddoch.org.uk>
11+ */
12+ 
13+#include <assert.h>
14+#include <inttypes.h>
15+#include <ctype.h>
16+#include <stdio.h>
17+#include <stdlib.h>
18+#include <stdint.h>
19+#include <stdarg.h>
20+#include <unistd.h>
21+#include <fcntl.h>
22+#include <getopt.h>
23+#include <pthread.h>
24+#include <signal.h>
25+#include <sched.h>
26+#include <string.h>
27+#include <time.h>
28+#include <errno.h>
29+#include <numa.h>
30+#include <math.h>
31+#include <limits.h>
32+#include <stdbool.h>
33+ 
34+#include <sys/prctl.h>
35+#include <sys/stat.h>
36+#include <sys/sysinfo.h>
37+#include <sys/types.h>
38+#include <sys/time.h>
39+#include <sys/resource.h>
40+#include <sys/utsname.h>
41+#include <sys/mman.h>
42+#include <sys/syscall.h>
43+ 
44+#include <linux/unistd.h>
45+ 
46+#include "rt-utils.h"
47+#include "rt-numa.h"
48+#include "rt-error.h"
49+ 
50+#ifdef __GNUC__
51+# define atomic_inc(ptr) __sync_add_and_fetch((ptr), 1)
52+# if defined(__x86_64__)
53+# define relax() __asm__ __volatile__("pause" ::: "memory")
54+static inline void frc(uint64_t *pval)
55+{
56+ uint32_t low, high;
57+ /* See rdtsc_ordered() of Linux */
58+ __asm__ __volatile__("lfence");
59+ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high));
60+ *pval = ((uint64_t) high << 32) | low;
61+}
62+# elif defined(__i386__)
63+# define relax() __asm__ __volatile__("pause" ::: "memory")
64+static inline void frc(uint64_t *pval)
65+{
66+ __asm__ __volatile__("rdtsc" : "=A" (*pval));
67+}
68+# elif defined(__PPC64__)
69+# define relax() do { } while (0)
70+static inline void frc(uint64_t *pval)
71+{
72+ __asm__ __volatile__("mfspr %0, 268\n" : "=r" (*pval));
73+}
74+# elif defined(__aarch64__)
75+# define relax() __asm__ __volatile("yield" : : : "memory")
76+ 
77+#define arch_measure_counter_mhz
78+static unsigned int measure_counter_mhz(void)
79+{
80+ unsigned int val;
81+ 
82+ __asm__ __volatile__("mrs %0, cntfrq_el0" : "=r" (val));
83+ 
84+ return val / 1e6;
85+}
86+ 
87+static inline void frc(uint64_t *pval)
88+{
89+ /*
90+ * This isb() is required to prevent that the counter value
91+ * is speculated.
92+ */
93+ __asm__ __volatile__("isb" : : : "memory");
94+ __asm__ __volatile__("mrs %0, cntvct_el0" : "=r" (*pval) :: "memory");
95+ /*
96+ * This isb() is required to prevent the processor from accessing
97+ * memory appearing in program order after the read of the counter
98+ * before the counter has been read. Which would skew the counter value
99+ * to a later point than intended.
100+ */
101+ __asm__ __volatile__("isb" : : : "memory");
102+ 
103+}
104+# else
105+# define relax() do { } while (0)
106+# define frc(x)
107+# define FRC_MISSING
108+# endif
109+#else
110+# error Need to add support for this compiler.
111+#endif
112+ 
113+typedef uint64_t stamp_t; /* timestamp */
114+typedef uint64_t cycles_t; /* number of cycles */
115+ 
116+#define true 1
117+#define false 0
118+ 
119+enum command {
120+ WAIT,
121+ GO,
122+ STOP
123+};
124+ 
125+enum workload_type {
126+ WORKLOAD_NONE = 0,
127+ WORKLOAD_MEMMOVE,
128+ WORKLOAD_NUM,
129+};
130+ 
131+/* This workload needs pre-allocated memory */
132+#define WORK_NEED_MEM (1UL << 0)
133+ 
134+typedef void (*workload_fn)(char *src, char *dst, size_t size);
135+ 
136+struct workload {
137+ const char *w_name;
138+ uint64_t w_flags;
139+ workload_fn w_fn;
140+};
141+ 
142+/* We'll have buckets 1us, 2us, ..., (BUCKET_SIZE) us. */
143+#define BUCKET_SIZE (32)
144+ 
145+/* Default size of the workloads per thread (in bytes, which is 16KB) */
146+#define WORKLOAD_MEM_SIZE (16UL << 10)
147+ 
148+/* By default, no workload */
149+#define WORKLOAD_DEFAULT WORKLOAD_NONE
150+ 
151+struct thread {
152+ int core_i;
153+ pthread_t thread_id;
154+ 
155+ /* NOTE! this is also how many ticks per us */
156+ unsigned int counter_mhz;
157+ cycles_t int_total;
158+ stamp_t frc_start;
159+ stamp_t frc_stop;
160+ cycles_t runtime;
161+ stamp_t *buckets;
162+ uint64_t minlat;
163+ /* Maximum latency detected */
164+ uint64_t maxlat;
165+ /*
166+ * The extra part of the interruptions that cannot be put into even the
167+ * biggest bucket. We'll use this to calculate a more accurate average at
168+ * the end of the tests.
169+ */
170+ uint64_t overflow_sum;
171+ int memory_allocated;
172+ 
173+ /* Buffers used for the workloads */
174+ char *src_buf;
175+ char *dst_buf;
176+ 
177+ /* These variables are calculated after the test */
178+ double average;
179+};
180+ 
181+struct global {
182+ /* Configuration. */
183+ unsigned int runtime_secs;
184+ bool preheat;
185+ /*
186+ * Number of threads running for current test
187+ * (either pre heat or real run)
188+ */
189+ unsigned int n_threads;
190+ /* Number of threads to test for the real run */
191+ unsigned int n_threads_total;
192+ struct timeval tv_start;
193+ int rtprio;
194+ int bucket_size;
195+ bool bucket_size_param;
196+ int bucket_width;
197+ int unit_per_us;
198+ int precision;
199+ int trace_threshold;
200+ int runtime;
201+ /* The core that we run the main thread. Default is cpu0 */
202+ int cpu_main_thread;
203+ char *cpu_list;
204+ char *app_name;
205+ struct workload *workload;
206+ uint64_t workload_mem_size;
207+ int enable_bias;
208+ uint64_t bias;
209+ int quiet;
210+ int single_preheat_thread;
211+ int output_omit_zero_buckets;
212+ char jsonfile[MAX_PATH];
213+ 
214+ /* Mutable state. */
215+ volatile enum command cmd;
216+ volatile unsigned int n_threads_started;
217+ volatile unsigned int n_threads_running;
218+ volatile unsigned int n_threads_finished;
219+};
220+ 
221+static struct global g;
222+ 
223+static void workload_nop(char *dst __attribute__ ((unused)), char *src __attribute__ ((unused)), size_t size __attribute__ ((unused)))
224+{
225+ /* Nop */
226+}
227+ 
228+static void workload_memmove(char *dst, char *src, size_t size)
229+{
230+ memmove(dst, src, size);
231+}
232+ 
233+struct workload workload_list[WORKLOAD_NUM] = {
234+ { "no", 0, workload_nop },
235+ { "memmove", WORK_NEED_MEM, workload_memmove },
236+};
237+ 
238+#define TEST(x) \
239+ do { \
240+ if (!(x)) \
241+ test_fail(#x, __LINE__); \
242+ } while (0)
243+ 
244+#define TEST0(x) TEST((x) == 0)
245+ 
246+static void test_fail(const char *what, int line)
247+{
248+ fprintf(stderr, "ERROR:\n");
249+ fprintf(stderr, "ERROR: TEST(%s)\n", what);
250+ fprintf(stderr, "ERROR: at line %d\n", line);
251+ fprintf(stderr, "ERROR: errno=%d (%s)\n", errno, strerror(errno));
252+ fprintf(stderr, "ERROR:\n");
253+ exit(1);
254+}
255+ 
256+static int move_to_core(int core_i)
257+{
258+ cpu_set_t cpus;
259+ 
260+ CPU_ZERO(&cpus);
261+ CPU_SET(core_i, &cpus);
262+ return sched_setaffinity(0, sizeof(cpus), &cpus);
263+}
264+ 
265+#ifndef arch_measure_counter_mhz
266+static cycles_t __measure_counter_hz(void)
267+{
268+ struct timeval tvs, tve;
269+ stamp_t s, e;
270+ double sec;
271+ 
272+ frc(&s);
273+ e = s;
274+ gettimeofday(&tvs, NULL);
275+ while (e - s < 1000000)
276+ frc(&e);
277+ gettimeofday(&tve, NULL);
278+ sec = tve.tv_sec - tvs.tv_sec + (tve.tv_usec - tvs.tv_usec) / 1e6;
279+ return (cycles_t) ((e - s) / sec);
280+}
281+ 
282+static unsigned int measure_counter_mhz(void)
283+{
284+ cycles_t m, mprev, d;
285+ 
286+ mprev = __measure_counter_hz();
287+ do {
288+ m = __measure_counter_hz();
289+ if (m > mprev)
290+ d = m - mprev;
291+ else
292+ d = mprev - m;
293+ mprev = m;
294+ } while (d > m / 1000);
295+ 
296+ return (unsigned int) (m / 1000000);
297+}
298+#endif
299+ 
300+static void thread_init(struct thread *t)
301+{
302+ t->counter_mhz = measure_counter_mhz();
303+ t->maxlat = 0;
304+ t->overflow_sum = 0;
305+ t->minlat = (uint64_t)-1;
306+ 
307+ /* NOTE: all the buffers are not freed until the process quits. */
308+ if (!t->memory_allocated) {
309+ TEST(t->buckets = calloc(1, sizeof(t->buckets[0]) * g.bucket_size));
310+ if (g.workload->w_flags & WORK_NEED_MEM) {
311+ TEST0(posix_memalign((void **)&t->src_buf, getpagesize(),
312+ g.workload_mem_size));
313+ memset(t->src_buf, 0, g.workload_mem_size);
314+ TEST0(posix_memalign((void **)&t->dst_buf, getpagesize(),
315+ g.workload_mem_size));
316+ memset(t->dst_buf, 0, g.workload_mem_size);
317+ }
318+ t->memory_allocated = 1;
319+ } else {
320+ /* Clear the buckets */
321+ memset(t->buckets, 0, sizeof(t->buckets[0]) * g.bucket_size);
322+ }
323+}
324+ 
325+static float cycles_to_sec(const struct thread *t, uint64_t cycles)
326+{
327+ return cycles / (t->counter_mhz * 1e6);
328+}
329+ 
330+static void insert_bucket(struct thread *t, stamp_t value)
331+{
332+ int index;
333+ unsigned int lat;
334+ uint64_t extra;
335+ double us;
336+ 
337+ lat = (value * g.unit_per_us) / t->counter_mhz;
338+ us = (double)lat / g.unit_per_us;
339+ if (!g.preheat && g.trace_threshold && us >= g.trace_threshold) {
340+ char *line = "%s: Trace threshold (%d us) triggered on cpu %d with %.*f us!\n";
341+ tracemark(line, g.app_name, g.trace_threshold, t->core_i,
342+ g.precision, us);
343+ err_quit(line, g.app_name, g.trace_threshold, t->core_i,
344+ g.precision, us);
345+ }
346+ 
347+ /* Update max latency */
348+ if (lat > t->maxlat)
349+ t->maxlat = lat;
350+ 
351+ if (lat < t->minlat)
352+ t->minlat = lat;
353+ 
354+ if (g.bias) {
355+ /* t->bias will be set after pre-heat if user enabled it */
356+ lat -= g.bias;
357+ /*
358+ * Negative should hardly happen, but if it happens, we assume we're in
359+ * the smallest bucket.
360+ */
361+ if (lat <= 0)
362+ lat = 1;
363+ }
364+ 
365+ index = lat / g.bucket_width;
366+ assert(index >= 0);
367+ 
368+ /* Too big the jitter; put into the last bucket */
369+ if (index >= g.bucket_size) {
370+ /* Keep the extra bit (in bucket width multiples) */
371+ extra = index - g.bucket_size;
372+ if (t->overflow_sum + extra < t->overflow_sum) {
373+ /* The uint64_t even overflowed itself; bail out */
374+ printf("Accumulated overflow too much!\n");
375+ exit(1);
376+ }
377+ t->overflow_sum += extra;
378+ index = g.bucket_size - 1;
379+ }
380+ 
381+ t->buckets[index]++;
382+ if (t->buckets[index] == 0) {
383+ printf("Bucket %d overflowed\n", index);
384+ exit(1);
385+ }
386+}
387+ 
388+static void doit(struct thread *t)
389+{
390+ stamp_t ts1, ts2;
391+ workload_fn workload_fn = g.workload->w_fn;
392+ 
393+ frc(&ts2);
394+ do {
395+ workload_fn(t->dst_buf, t->src_buf, g.workload_mem_size);
396+ frc(&ts1);
397+ insert_bucket(t, ts1 - ts2);
398+ ts2 = ts1;
399+ } while (g.cmd == GO);
400+}
401+ 
402+static int set_fifo_prio(int prio)
403+{
404+ struct sched_param param;
405+ 
406+ memset(&param, 0, sizeof(param));
407+ param.sched_priority = prio;
408+ return sched_setscheduler(0, SCHED_FIFO, &param);
409+}
410+ 
411+static void *thread_main(void *arg)
412+{
413+ /* Important thing to note here is that once we start bashing the CPU, we
414+ * need to keep doing so to prevent the core from changing frequency or
415+ * dropping into a low power state.
416+ */
417+ struct thread *t = arg;
418+ 
419+ /* Alloc memory in the thread itself after setting affinity to get the
420+ * best chance of getting numa-local memory. Doesn't matter so much for
421+ * the "struct thread" since we expect that to stay cache resident.
422+ */
423+ TEST(move_to_core(t->core_i) == 0);
424+ if (g.rtprio)
425+ TEST(set_fifo_prio(g.rtprio) == 0);
426+ 
427+ /* Don't bash the cpu until all threads have got going. */
428+ atomic_inc(&g.n_threads_started);
429+ while (g.cmd == WAIT)
430+ usleep(1000);
431+ 
432+ thread_init(t);
433+ 
434+ /* Ensure we all start at the same time. */
435+ atomic_inc(&g.n_threads_running);
436+ while (g.n_threads_running != g.n_threads)
437+ relax();
438+ 
439+ frc(&t->frc_start);
440+ doit(t);
441+ frc(&t->frc_stop);
442+ 
443+ t->runtime = t->frc_stop - t->frc_start;
444+ 
445+ /* Wait for everyone to finish so we don't disturb them by exiting and
446+ * waking the main thread.
447+ */
448+ atomic_inc(&g.n_threads_finished);
449+ while (g.n_threads_finished != g.n_threads)
450+ relax();
451+ 
452+ return NULL;
453+}
454+ 
455+#define putfield(label, val, fmt, end) do { \
456+ printf("%12s:\t", label); \
457+ for (i = 0; i < g.n_threads; ++i) \
458+ printf(" %"fmt, val); \
459+ printf("%s\n", end); \
460+ } while (0)
461+ 
462+#define putfieldp(label, val, end) do { \
463+ printf("%12s:\t", label); \
464+ for (i = 0; i < g.n_threads; ++i) \
465+ printf(" %.*f", g.precision, \
466+ (double)(val) / g.unit_per_us); \
467+ printf("%s\n", end); \
468+ } while (0)
469+ 
470+static double bucket_to_lat(int bucket)
471+{
472+ return (g.bias + (bucket + 1) * (double)g.bucket_width) / g.unit_per_us;
473+}
474+ 
475+void calculate(struct thread *t)
476+{
477+ int j;
478+ unsigned int i;
479+ double sum;
480+ uint64_t count;
481+ 
482+ for (i = 0; i < g.n_threads; ++i) {
483+ /* Calculate average */
484+ sum = count = 0;
485+ for (j = 0; j < g.bucket_size; j++) {
486+ sum += t[i].buckets[j] * bucket_to_lat(j);
487+ count += t[i].buckets[j];
488+ }
489+ /* Add the extra amount of huge spikes in */
490+ sum += t->overflow_sum * g.bucket_width;
491+ t[i].average = sum / count;
492+ }
493+}
494+ 
495+static void write_summary(struct thread *t)
496+{
497+ int j, print_dotdotdot = 0;
498+ unsigned long int i, k;
499+ char bucket_name[64];
500+ 
501+ calculate(t);
502+ 
503+ putfield("Core", t[i].core_i, "d", "");
504+ putfield("Counter Freq", t[i].counter_mhz, "u", " (MHz)");
505+ 
506+ for (j = 0; j < g.bucket_size; j++) {
507+ if (j < g.bucket_size-1 && g.output_omit_zero_buckets) {
508+ for (k = 0; k < g.n_threads; k++) {
509+ if (t[k].buckets[j] != 0)
510+ break;
511+ }
512+ if (k == g.n_threads) {
513+ print_dotdotdot = 1;
514+ continue;
515+ }
516+ }
517+ 
518+ if (print_dotdotdot) {
519+ printf(" ...\n");
520+ print_dotdotdot = 0;
521+ }
522+ 
523+ snprintf(bucket_name, sizeof(bucket_name), "%03.*f (us)",
524+ g.precision, bucket_to_lat(j));
525+ putfield(bucket_name, t[i].buckets[j], PRIu64,
526+ (j == g.bucket_size - 1) ? " (including overflows)" : "");
527+ }
528+ 
529+ putfieldp("Minimum", t[i].minlat, " (us)");
530+ putfield("Average", t[i].average, ".3lf", " (us)");
531+ putfieldp("Maximum", t[i].maxlat, " (us)");
532+ putfieldp("Max-Min", t[i].maxlat - t[i].minlat, " (us)");
533+ putfield("Duration", cycles_to_sec(&(t[i]), t[i].runtime),
534+ ".3f", " (sec)");
535+ printf("\n");
536+}
537+ 
538+static void write_summary_json(FILE *f, void *data)
539+{
540+ struct thread *t = data;
541+ int j, comma;
542+ unsigned long int i;
543+ 
544+ fprintf(f, " \"num_threads\": %d,\n", g.n_threads);
545+ fprintf(f, " \"thread\": {\n");
546+ for (i = 0; i < g.n_threads; ++i) {
547+ fprintf(f, " \"%lu\": {\n", i);
548+ fprintf(f, " \"cpu\": %d,\n", t[i].core_i);
549+ fprintf(f, " \"freq\": %d,\n", t[i].counter_mhz);
550+ fprintf(f, " \"min\": %" PRIu64 ",\n", t[i].minlat);
551+ fprintf(f, " \"avg\": %3lf,\n", t[i].average);
552+ fprintf(f, " \"max\": %" PRIu64 ",\n", t[i].maxlat);
553+ fprintf(f, " \"duration\": %.3f,\n",
554+ cycles_to_sec(&(t[i]), t[i].runtime));
555+ fprintf(f, " \"histogram\": {");
556+ for (j = 0, comma = 0; j < g.bucket_size; j++) {
557+ if (t[i].buckets[j] == 0)
558+ continue;
559+ fprintf(f, "%s", comma ? ",\n" : "\n");
560+ fprintf(f, " \"%.*f\": %" PRIu64,
561+ g.precision, bucket_to_lat(j), t[i].buckets[j]);
562+ comma = 1;
563+ }
564+ if (comma)
565+ fprintf(f, "\n");
566+ fprintf(f, " }\n");
567+ fprintf(f, " }%s\n", i == g.n_threads - 1 ? "" : ",");
568+ }
569+ fprintf(f, " }\n");
570+}
571+ 
572+static void run_expt(struct thread *threads, int runtime_secs, bool preheat)
573+{
574+ unsigned long int i;
575+ 
576+ g.runtime_secs = runtime_secs;
577+ g.preheat = preheat;
578+ g.n_threads_started = 0;
579+ g.n_threads_running = 0;
580+ g.n_threads_finished = 0;
581+ g.cmd = WAIT;
582+ 
583+ for (i = 0; i < g.n_threads; ++i)
584+ TEST0(pthread_create(&(threads[i].thread_id), NULL,
585+ thread_main, &(threads[i])));
586+ while (g.n_threads_started != g.n_threads)
587+ usleep(1000);
588+ 
589+ gettimeofday(&g.tv_start, NULL);
590+ g.cmd = GO;
591+ 
592+ alarm(runtime_secs);
593+ 
594+ /* Go to sleep until the threads have done their stuff. */
595+ for (i = 0; i < g.n_threads; ++i)
596+ pthread_join(threads[i].thread_id, NULL);
597+}
598+ 
599+static void handle_alarm(int code __attribute__ ((unused)))
600+{
601+ g.cmd = STOP;
602+}
603+ 
604+static void usage(int error)
605+{
606+ printf("Usage:\n"
607+ "oslat <options>\n\n"
608+ "This is an OS latency detector by running busy loops on specified cores.\n"
609+ "Please run this tool using root.\n\n"
610+ "Available options:\n\n"
611+ "-b, --bucket-size Specify the number of the buckets (4-1024)\n"
612+ "-B, --bias Add a bias to all the buckets using the estimated mininum\n"
613+ "-c, --cpu-list Specify CPUs to run on, e.g. '1,3,5,7-15'\n"
614+ "-C, --cpu-main-thread Specify which CPU the main thread runs on. Default is cpu0.\n"
615+ "-D, --duration Specify test duration, e.g., 60, 20m, 2H\n"
616+ " (m/M: minutes, h/H: hours, d/D: days)\n"
617+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
618+ "-f, --rtprio Using SCHED_FIFO priority (1-99)\n"
619+ "-m, --workload-mem Size of the memory to use for the workload (e.g., 4K, 1M).\n"
620+ " Total memory usage will be this value multiplies 2*N,\n"
621+ " because there will be src/dst buffers for each thread, and\n"
622+ " N is the number of processors for testing.\n"
623+ "-q --quiet print a summary only on exit\n"
624+ "-s, --single-preheat Use a single thread when measuring latency at preheat stage\n"
625+ " NOTE: please make sure the CPU frequency on all testing cores\n"
626+ " are locked before using this parmater. If you don't know how\n"
627+ " to lock the freq then please don't use this parameter.\n"
628+ "-T, --trace-threshold Stop the test when threshold triggered (in us),\n"
629+ " print a marker in ftrace and stop ftrace too.\n"
630+ "-v, --version Display the version of the software.\n"
631+ "-w, --workload Specify a kind of workload, default is no workload\n"
632+ " (options: no, memmove)\n"
633+ "-W, --bucket-width Interval between buckets in nanoseconds\n"
634+ " NOTE: Widths not a multiple of 1000 cause ns-precision output\n"
635+ " You are responsible for considering the impact of measurement\n"
636+ " overhead at the nanosecond scale.\n"
637+ "-z, --zero-omit Don't display buckets in the output histogram if all zeros.\n"
638+ );
639+ exit(error);
640+}
641+ 
642+static int workload_select(char *name)
643+{
644+ int i = 0;
645+ 
646+ for (i = 0; i < WORKLOAD_NUM; i++) {
647+ if (!strcmp(name, workload_list[i].w_name)) {
648+ g.workload = &workload_list[i];
649+ return 0;
650+ }
651+ }
652+ 
653+ return -1;
654+}
655+ 
656+enum option_value {
657+ OPT_BUCKETSIZE = 1, OPT_BUCKETWIDTH, OPT_CPU_LIST, OPT_CPU_MAIN_THREAD,
658+ OPT_DURATION, OPT_JSON, OPT_RT_PRIO, OPT_HELP, OPT_TRACE_TH,
659+ OPT_WORKLOAD, OPT_WORKLOAD_MEM, OPT_BIAS,
660+ OPT_QUIET, OPT_SINGLE_PREHEAT, OPT_ZERO_OMIT,
661+ OPT_VERSION
662+};
663+ 
664+/* Process commandline options */
665+static void parse_options(int argc, char *argv[])
666+{
667+ while (1) {
668+ int option_index = 0;
669+ static struct option options[] = {
670+ { "bucket-size", required_argument, NULL, OPT_BUCKETSIZE },
671+ { "bucket-width", required_argument, NULL, OPT_BUCKETWIDTH },
672+ { "cpu-list", required_argument, NULL, OPT_CPU_LIST },
673+ { "cpu-main-thread", required_argument, NULL, OPT_CPU_MAIN_THREAD},
674+ { "duration", required_argument, NULL, OPT_DURATION },
675+ { "json", required_argument, NULL, OPT_JSON },
676+ { "rtprio", required_argument, NULL, OPT_RT_PRIO },
677+ { "help", no_argument, NULL, OPT_HELP },
678+ { "trace-threshold", required_argument, NULL, OPT_TRACE_TH },
679+ { "workload", required_argument, NULL, OPT_WORKLOAD },
680+ { "workload-mem", required_argument, NULL, OPT_WORKLOAD_MEM },
681+ { "bias", no_argument, NULL, OPT_BIAS },
682+ { "quiet", no_argument, NULL, OPT_QUIET },
683+ { "single-preheat", no_argument, NULL, OPT_SINGLE_PREHEAT },
684+ { "zero-omit", no_argument, NULL, OPT_ZERO_OMIT },
685+ { "version", no_argument, NULL, OPT_VERSION },
686+ { NULL, 0, NULL, 0 },
687+ };
688+ int i, c = getopt_long(argc, argv, "b:Bc:C:D:f:hm:qsw:W:T:vz",
689+ options, &option_index);
690+ long ncores;
691+ 
692+ if (c == -1)
693+ break;
694+ 
695+ switch (c) {
696+ case OPT_BUCKETSIZE:
697+ case 'b':
698+ g.bucket_size_param = true;
699+ g.bucket_size = strtol(optarg, NULL, 10);
700+ if (g.bucket_size > 1024 || g.bucket_size <= 4) {
701+ printf("Illegal bucket size: %s (should be: 4-1024)\n",
702+ optarg);
703+ exit(1);
704+ }
705+ break;
706+ case OPT_BUCKETWIDTH:
707+ case 'W':
708+ g.bucket_width = strtol(optarg, NULL, 10);
709+ if (g.bucket_width <= 0) {
710+ printf("Illegal bucket width: %s\n", optarg);
711+ exit(1);
712+ }
713+ if (g.bucket_width % 1000) {
714+ g.unit_per_us = 1000;
715+ g.precision = 3;
716+ } else {
717+ g.bucket_width /= 1000;
718+ }
719+ break;
720+ case OPT_BIAS:
721+ case 'B':
722+ g.enable_bias = 1;
723+ break;
724+ case OPT_CPU_LIST:
725+ case 'c':
726+ g.cpu_list = strdup(optarg);
727+ break;
728+ case OPT_CPU_MAIN_THREAD:
729+ case 'C':
730+ ncores = sysconf(_SC_NPROCESSORS_CONF);
731+ g.cpu_main_thread = strtol(optarg, NULL, 10);
732+ if (g.cpu_main_thread < 0 || g.cpu_main_thread > ncores) {
733+ printf("Illegal core for main thread: %s (should be: 0-%ld)\n",
734+ optarg, ncores);
735+ exit(1);
736+ }
737+ break;
738+ case OPT_DURATION:
739+ case 'D':
740+ g.runtime = parse_time_string(optarg);
741+ if (!g.runtime) {
742+ printf("Illegal runtime: %s\n", optarg);
743+ exit(1);
744+ }
745+ break;
746+ case OPT_RT_PRIO:
747+ case 'f':
748+ g.rtprio = strtol(optarg, NULL, 10);
749+ if (g.rtprio < 1 || g.rtprio > 99) {
750+ printf("Illegal RT priority: %s (should be: 1-99)\n", optarg);
751+ exit(1);
752+ }
753+ break;
754+ case OPT_JSON:
755+ strncpy(g.jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
756+ break;
757+ case OPT_TRACE_TH:
758+ case 'T':
759+ g.trace_threshold = strtol(optarg, NULL, 10);
760+ if (g.trace_threshold <= 0) {
761+ printf("Parameter --trace-threshold needs to be positive\n");
762+ exit(1);
763+ }
764+ enable_trace_mark();
765+ break;
766+ case OPT_WORKLOAD:
767+ case 'w':
768+ if (workload_select(optarg)) {
769+ printf("Unknown workload '%s'. Please choose from: ", optarg);
770+ for (i = 0; i < WORKLOAD_NUM; i++) {
771+ printf("'%s'", workload_list[i].w_name);
772+ if (i != WORKLOAD_NUM - 1)
773+ printf(", ");
774+ }
775+ printf("\n\n");
776+ exit(1);
777+ }
778+ break;
779+ case OPT_WORKLOAD_MEM:
780+ case 'm':
781+ if (parse_mem_string(optarg, &g.workload_mem_size)) {
782+ printf("Unknown workload memory size '%s'.\n\n", optarg);
783+ exit(1);
784+ }
785+ break;
786+ case OPT_QUIET:
787+ case 'q':
788+ g.quiet = 1;
789+ break;
790+ case OPT_SINGLE_PREHEAT:
791+ case 's':
792+ /*
793+ * Only use one core for pre-heat. Then if --bias is used, the
794+ * bias will be exactly the min value of the pre-heat core.
795+ */
796+ g.single_preheat_thread = true;
797+ break;
798+ case OPT_VERSION:
799+ case 'v':
800+ /*
801+ * We always print the version before parsing options
802+ * so just exit
803+ */
804+ exit(0);
805+ break;
806+ case OPT_ZERO_OMIT:
807+ case 'z':
808+ g.output_omit_zero_buckets = 1;
809+ break;
810+ case OPT_HELP:
811+ case 'h':
812+ usage(0);
813+ break;
814+ default:
815+ usage(1);
816+ break;
817+ }
818+ }
819+ 
820+ if (!g.bucket_size_param && g.precision == 3 && g.bucket_width < 1000)
821+ g.bucket_size = BUCKET_SIZE * 1000 / g.bucket_width;
822+}
823+ 
824+void dump_globals(void)
825+{
826+ printf("Total runtime: \t\t%d seconds\n", g.runtime);
827+ printf("Thread priority: \t");
828+ if (g.rtprio)
829+ printf("SCHED_FIFO:%d\n", g.rtprio);
830+ else
831+ printf("default\n");
832+ printf("CPU list: \t\t%s\n", g.cpu_list ?: "(all cores)");
833+ printf("CPU for main thread: \t%d\n", g.cpu_main_thread);
834+ printf("Workload: \t\t%s\n", g.workload->w_name);
835+ printf("Workload mem: \t\t%"PRIu64" (KiB)\n",
836+ (g.workload->w_flags & WORK_NEED_MEM) ?
837+ (g.workload_mem_size / 1024) : 0);
838+ printf("Preheat cores: \t\t%d\n", g.single_preheat_thread ?
839+ 1 : g.n_threads_total);
840+ printf("\n");
841+}
842+ 
843+static void record_bias(struct thread *t)
844+{
845+ unsigned long int i;
846+ uint64_t bias = (uint64_t)-1;
847+ 
848+ if (!g.enable_bias)
849+ return;
850+ 
851+ /* Record the min value of minlat on all the threads */
852+ for (i = 0; i < g.n_threads; ++i) {
853+ if (t[i].minlat < bias)
854+ bias = t[i].minlat;
855+ }
856+ g.bias = bias;
857+ printf("Global bias set to %.*f (us)\n", g.precision,
858+ (double)bias / g.unit_per_us);
859+}
860+ 
861+int main(int argc, char *argv[])
862+{
863+ struct thread *threads;
864+ int n_cores;
865+ unsigned long int i;
866+ struct bitmask *cpu_set = NULL;
867+ 
868+#ifdef FRC_MISSING
869+ printf("This architecture is not yet supported. "
870+ "Please implement frc() function first for %s.\n", argv[0]);
871+ return 0;
872+#endif
873+ if (numa_available() == -1) {
874+ printf("ERROR: Could not initialize libnuma\n");
875+ exit(1);
876+ }
877+ 
878+ rt_init(argc, argv);
879+ 
880+ g.app_name = argv[0];
881+ g.rtprio = 0;
882+ g.bucket_size = BUCKET_SIZE;
883+ g.bucket_width = 1;
884+ g.unit_per_us = 1;
885+ g.runtime = 1;
886+ g.workload = &workload_list[WORKLOAD_DEFAULT];
887+ g.workload_mem_size = WORKLOAD_MEM_SIZE;
888+ /* Run the main thread on cpu0 by default */
889+ g.cpu_main_thread = 0;
890+ printf("oslat V %1.2f\n", VERSION);
891+ parse_options(argc, argv);
892+ 
893+ TEST(mlockall(MCL_CURRENT | MCL_FUTURE) == 0);
894+ 
895+ if (!g.cpu_list)
896+ g.cpu_list = strdup("all");
897+ 
898+ cpu_set = numa_parse_cpustring_all(g.cpu_list);
899+ if (!cpu_set)
900+ fatal("oslat: numa_parse_cpustring_all failed.\n");
901+ n_cores = numa_bitmask_weight(cpu_set);
902+ 
903+ TEST(threads = calloc(1, n_cores * sizeof(threads[0])));
904+ for (i = 0; n_cores && i < cpu_set->size; i++) {
905+ if (numa_bitmask_isbitset(cpu_set, i) && move_to_core(i) == 0) {
906+ threads[g.n_threads_total++].core_i = i;
907+ n_cores--;
908+ }
909+ }
910+ 
911+ if (numa_bitmask_isbitset(cpu_set, 0) && g.rtprio)
912+ printf("WARNING: Running SCHED_FIFO workload on CPU 0 may hang the thread\n");
913+ 
914+ numa_bitmask_free(cpu_set);
915+ 
916+ TEST(move_to_core(g.cpu_main_thread) == 0);
917+ 
918+ signal(SIGALRM, handle_alarm);
919+ signal(SIGINT, handle_alarm);
920+ signal(SIGTERM, handle_alarm);
921+ 
922+ if (!g.quiet)
923+ dump_globals();
924+ 
925+ if (!g.quiet)
926+ printf("Pre-heat for 1 seconds...\n");
927+ if (g.single_preheat_thread)
928+ g.n_threads = 1;
929+ else
930+ g.n_threads = g.n_threads_total;
931+ run_expt(threads, 1, true);
932+ record_bias(threads);
933+ 
934+ if (!g.quiet)
935+ printf("Test starts...\n");
936+ /* Reset n_threads to always run on all the cores */
937+ g.n_threads = g.n_threads_total;
938+ run_expt(threads, g.runtime, false);
939+ 
940+ if (!g.quiet)
941+ printf("Test completed.\n\n");
942+ 
943+ write_summary(threads);
944+ 
945+ if (strlen(g.jsonfile) != 0)
946+ rt_write_json(g.jsonfile, 0, write_summary_json, threads);
947+ 
948+ if (g.cpu_list) {
949+ free(g.cpu_list);
950+ g.cpu_list = NULL;
951+ }
952+ 
953+ disable_trace_mark();
954+ 
955+ return 0;
956+}
@@ -0,0 +1,339 @@
1+ GNU GENERAL PUBLIC LICENSE
2+ Version 2, June 1991
3+ 
4+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5+ 675 Mass Ave, Cambridge, MA 02139, USA
6+ Everyone is permitted to copy and distribute verbatim copies
7+ of this license document, but changing it is not allowed.
8+ 
9+ Preamble
10+ 
11+ The licenses for most software are designed to take away your
12+freedom to share and change it. By contrast, the GNU General Public
13+License is intended to guarantee your freedom to share and change free
14+software--to make sure the software is free for all its users. This
15+General Public License applies to most of the Free Software
16+Foundation's software and to any other program whose authors commit to
17+using it. (Some other Free Software Foundation software is covered by
18+the GNU Library General Public License instead.) You can apply it to
19+your programs, too.
20+ 
21+ When we speak of free software, we are referring to freedom, not
22+price. Our General Public Licenses are designed to make sure that you
23+have the freedom to distribute copies of free software (and charge for
24+this service if you wish), that you receive source code or can get it
25+if you want it, that you can change the software or use pieces of it
26+in new free programs; and that you know you can do these things.
27+ 
28+ To protect your rights, we need to make restrictions that forbid
29+anyone to deny you these rights or to ask you to surrender the rights.
30+These restrictions translate to certain responsibilities for you if you
31+distribute copies of the software, or if you modify it.
32+ 
33+ For example, if you distribute copies of such a program, whether
34+gratis or for a fee, you must give the recipients all the rights that
35+you have. You must make sure that they, too, receive or can get the
36+source code. And you must show them these terms so they know their
37+rights.
38+ 
39+ We protect your rights with two steps: (1) copyright the software, and
40+(2) offer you this license which gives you legal permission to copy,
41+distribute and/or modify the software.
42+ 
43+ Also, for each author's protection and ours, we want to make certain
44+that everyone understands that there is no warranty for this free
45+software. If the software is modified by someone else and passed on, we
46+want its recipients to know that what they have is not the original, so
47+that any problems introduced by others will not reflect on the original
48+authors' reputations.
49+ 
50+ Finally, any free program is threatened constantly by software
51+patents. We wish to avoid the danger that redistributors of a free
52+program will individually obtain patent licenses, in effect making the
53+program proprietary. To prevent this, we have made it clear that any
54+patent must be licensed for everyone's free use or not licensed at all.
55+ 
56+ The precise terms and conditions for copying, distribution and
57+modification follow.
58+
59+ GNU GENERAL PUBLIC LICENSE
60+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61+ 
62+ 0. This License applies to any program or other work which contains
63+a notice placed by the copyright holder saying it may be distributed
64+under the terms of this General Public License. The "Program", below,
65+refers to any such program or work, and a "work based on the Program"
66+means either the Program or any derivative work under copyright law:
67+that is to say, a work containing the Program or a portion of it,
68+either verbatim or with modifications and/or translated into another
69+language. (Hereinafter, translation is included without limitation in
70+the term "modification".) Each licensee is addressed as "you".
71+ 
72+Activities other than copying, distribution and modification are not
73+covered by this License; they are outside its scope. The act of
74+running the Program is not restricted, and the output from the Program
75+is covered only if its contents constitute a work based on the
76+Program (independent of having been made by running the Program).
77+Whether that is true depends on what the Program does.
78+ 
79+ 1. You may copy and distribute verbatim copies of the Program's
80+source code as you receive it, in any medium, provided that you
81+conspicuously and appropriately publish on each copy an appropriate
82+copyright notice and disclaimer of warranty; keep intact all the
83+notices that refer to this License and to the absence of any warranty;
84+and give any other recipients of the Program a copy of this License
85+along with the Program.
86+ 
87+You may charge a fee for the physical act of transferring a copy, and
88+you may at your option offer warranty protection in exchange for a fee.
89+ 
90+ 2. You may modify your copy or copies of the Program or any portion
91+of it, thus forming a work based on the Program, and copy and
92+distribute such modifications or work under the terms of Section 1
93+above, provided that you also meet all of these conditions:
94+ 
95+ a) You must cause the modified files to carry prominent notices
96+ stating that you changed the files and the date of any change.
97+ 
98+ b) You must cause any work that you distribute or publish, that in
99+ whole or in part contains or is derived from the Program or any
100+ part thereof, to be licensed as a whole at no charge to all third
101+ parties under the terms of this License.
102+ 
103+ c) If the modified program normally reads commands interactively
104+ when run, you must cause it, when started running for such
105+ interactive use in the most ordinary way, to print or display an
106+ announcement including an appropriate copyright notice and a
107+ notice that there is no warranty (or else, saying that you provide
108+ a warranty) and that users may redistribute the program under
109+ these conditions, and telling the user how to view a copy of this
110+ License. (Exception: if the Program itself is interactive but
111+ does not normally print such an announcement, your work based on
112+ the Program is not required to print an announcement.)
113+
114+These requirements apply to the modified work as a whole. If
115+identifiable sections of that work are not derived from the Program,
116+and can be reasonably considered independent and separate works in
117+themselves, then this License, and its terms, do not apply to those
118+sections when you distribute them as separate works. But when you
119+distribute the same sections as part of a whole which is a work based
120+on the Program, the distribution of the whole must be on the terms of
121+this License, whose permissions for other licensees extend to the
122+entire whole, and thus to each and every part regardless of who wrote it.
123+ 
124+Thus, it is not the intent of this section to claim rights or contest
125+your rights to work written entirely by you; rather, the intent is to
126+exercise the right to control the distribution of derivative or
127+collective works based on the Program.
128+ 
129+In addition, mere aggregation of another work not based on the Program
130+with the Program (or with a work based on the Program) on a volume of
131+a storage or distribution medium does not bring the other work under
132+the scope of this License.
133+ 
134+ 3. You may copy and distribute the Program (or a work based on it,
135+under Section 2) in object code or executable form under the terms of
136+Sections 1 and 2 above provided that you also do one of the following:
137+ 
138+ a) Accompany it with the complete corresponding machine-readable
139+ source code, which must be distributed under the terms of Sections
140+ 1 and 2 above on a medium customarily used for software interchange; or,
141+ 
142+ b) Accompany it with a written offer, valid for at least three
143+ years, to give any third party, for a charge no more than your
144+ cost of physically performing source distribution, a complete
145+ machine-readable copy of the corresponding source code, to be
146+ distributed under the terms of Sections 1 and 2 above on a medium
147+ customarily used for software interchange; or,
148+ 
149+ c) Accompany it with the information you received as to the offer
150+ to distribute corresponding source code. (This alternative is
151+ allowed only for noncommercial distribution and only if you
152+ received the program in object code or executable form with such
153+ an offer, in accord with Subsection b above.)
154+ 
155+The source code for a work means the preferred form of the work for
156+making modifications to it. For an executable work, complete source
157+code means all the source code for all modules it contains, plus any
158+associated interface definition files, plus the scripts used to
159+control compilation and installation of the executable. However, as a
160+special exception, the source code distributed need not include
161+anything that is normally distributed (in either source or binary
162+form) with the major components (compiler, kernel, and so on) of the
163+operating system on which the executable runs, unless that component
164+itself accompanies the executable.
165+ 
166+If distribution of executable or object code is made by offering
167+access to copy from a designated place, then offering equivalent
168+access to copy the source code from the same place counts as
169+distribution of the source code, even though third parties are not
170+compelled to copy the source along with the object code.
171+
172+ 4. You may not copy, modify, sublicense, or distribute the Program
173+except as expressly provided under this License. Any attempt
174+otherwise to copy, modify, sublicense or distribute the Program is
175+void, and will automatically terminate your rights under this License.
176+However, parties who have received copies, or rights, from you under
177+this License will not have their licenses terminated so long as such
178+parties remain in full compliance.
179+ 
180+ 5. You are not required to accept this License, since you have not
181+signed it. However, nothing else grants you permission to modify or
182+distribute the Program or its derivative works. These actions are
183+prohibited by law if you do not accept this License. Therefore, by
184+modifying or distributing the Program (or any work based on the
185+Program), you indicate your acceptance of this License to do so, and
186+all its terms and conditions for copying, distributing or modifying
187+the Program or works based on it.
188+ 
189+ 6. Each time you redistribute the Program (or any work based on the
190+Program), the recipient automatically receives a license from the
191+original licensor to copy, distribute or modify the Program subject to
192+these terms and conditions. You may not impose any further
193+restrictions on the recipients' exercise of the rights granted herein.
194+You are not responsible for enforcing compliance by third parties to
195+this License.
196+ 
197+ 7. If, as a consequence of a court judgment or allegation of patent
198+infringement or for any other reason (not limited to patent issues),
199+conditions are imposed on you (whether by court order, agreement or
200+otherwise) that contradict the conditions of this License, they do not
201+excuse you from the conditions of this License. If you cannot
202+distribute so as to satisfy simultaneously your obligations under this
203+License and any other pertinent obligations, then as a consequence you
204+may not distribute the Program at all. For example, if a patent
205+license would not permit royalty-free redistribution of the Program by
206+all those who receive copies directly or indirectly through you, then
207+the only way you could satisfy both it and this License would be to
208+refrain entirely from distribution of the Program.
209+ 
210+If any portion of this section is held invalid or unenforceable under
211+any particular circumstance, the balance of the section is intended to
212+apply and the section as a whole is intended to apply in other
213+circumstances.
214+ 
215+It is not the purpose of this section to induce you to infringe any
216+patents or other property right claims or to contest validity of any
217+such claims; this section has the sole purpose of protecting the
218+integrity of the free software distribution system, which is
219+implemented by public license practices. Many people have made
220+generous contributions to the wide range of software distributed
221+through that system in reliance on consistent application of that
222+system; it is up to the author/donor to decide if he or she is willing
223+to distribute software through any other system and a licensee cannot
224+impose that choice.
225+ 
226+This section is intended to make thoroughly clear what is believed to
227+be a consequence of the rest of this License.
228+
229+ 8. If the distribution and/or use of the Program is restricted in
230+certain countries either by patents or by copyrighted interfaces, the
231+original copyright holder who places the Program under this License
232+may add an explicit geographical distribution limitation excluding
233+those countries, so that distribution is permitted only in or among
234+countries not thus excluded. In such case, this License incorporates
235+the limitation as if written in the body of this License.
236+ 
237+ 9. The Free Software Foundation may publish revised and/or new versions
238+of the General Public License from time to time. Such new versions will
239+be similar in spirit to the present version, but may differ in detail to
240+address new problems or concerns.
241+ 
242+Each version is given a distinguishing version number. If the Program
243+specifies a version number of this License which applies to it and "any
244+later version", you have the option of following the terms and conditions
245+either of that version or of any later version published by the Free
246+Software Foundation. If the Program does not specify a version number of
247+this License, you may choose any version ever published by the Free Software
248+Foundation.
249+ 
250+ 10. If you wish to incorporate parts of the Program into other free
251+programs whose distribution conditions are different, write to the author
252+to ask for permission. For software which is copyrighted by the Free
253+Software Foundation, write to the Free Software Foundation; we sometimes
254+make exceptions for this. Our decision will be guided by the two goals
255+of preserving the free status of all derivatives of our free software and
256+of promoting the sharing and reuse of software generally.
257+ 
258+ NO WARRANTY
259+ 
260+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268+REPAIR OR CORRECTION.
269+ 
270+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278+POSSIBILITY OF SUCH DAMAGES.
279+ 
280+ END OF TERMS AND CONDITIONS
281+
282+ How to Apply These Terms to Your New Programs
283+ 
284+ If you develop a new program, and you want it to be of the greatest
285+possible use to the public, the best way to achieve this is to make it
286+free software which everyone can redistribute and change under these terms.
287+ 
288+ To do so, attach the following notices to the program. It is safest
289+to attach them to the start of each source file to most effectively
290+convey the exclusion of warranty; and each file should have at least
291+the "copyright" line and a pointer to where the full notice is found.
292+ 
293+ <one line to give the program's name and a brief idea of what it does.>
294+ Copyright (C) 19yy <name of author>
295+ 
296+ This program is free software; you can redistribute it and/or modify
297+ it under the terms of the GNU General Public License as published by
298+ the Free Software Foundation; either version 2 of the License, or
299+ (at your option) any later version.
300+ 
301+ This program is distributed in the hope that it will be useful,
302+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304+ GNU General Public License for more details.
305+ 
306+ You should have received a copy of the GNU General Public License
307+ along with this program; if not, write to the Free Software
308+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
309+ 
310+Also add information on how to contact you by electronic and paper mail.
311+ 
312+If the program is interactive, make it output a short notice like this
313+when it starts in an interactive mode:
314+ 
315+ Gnomovision version 69, Copyright (C) 19yy name of author
316+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317+ This is free software, and you are welcome to redistribute it
318+ under certain conditions; type `show c' for details.
319+ 
320+The hypothetical commands `show w' and `show c' should show the appropriate
321+parts of the General Public License. Of course, the commands you use may
322+be called something other than `show w' and `show c'; they could even be
323+mouse-clicks or menu items--whatever suits your program.
324+ 
325+You should also get your employer (if you work as a programmer) or your
326+school, if any, to sign a "copyright disclaimer" for the program, if
327+necessary. Here is a sample; alter the names:
328+ 
329+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331+ 
332+ <signature of Ty Coon>, 1 April 1989
333+ Ty Coon, President of Vice
334+ 
335+This General Public License does not permit incorporating your program into
336+proprietary programs. If your program is a subroutine library, you may
337+consider it more useful to permit linking proprietary applications with the
338+library. If this is what you want to do, use the GNU Library General
339+Public License instead of this License.
@@ -0,0 +1,80 @@
1+DESCRIPTION
2+ 
3+The pi_stress program is a stress test that is intended to exercise
4+kernel and C library code paths for POSIX mutexes using the Priority
5+Inheritance attribute (PTHREAD_PRIO_INHERIT).
6+ 
7+The program consists of an admin thread (main), a reporter thread and
8+some number of groups of three threads called "inversion
9+groups". These thread groups are called that because they cause a
10+condition called Priority Inversion, where a high priority thread is
11+blocked due to a low-priority thread holding a shared
12+resource. Priority inversion with no contravening logic is a deadlock
13+condition.
14+ 
15+Each inversion group consists of three threads:
16+ 
17+1. A high-priority thread
18+2. A medium-priority thread
19+3. A low-priority thread
20+ 
21+The threads run through a state machine designed to guarantee that a
22+low-priority thread holds a mutex while a medium priority thread runs
23+(keeping the low-priority thread from releasing the mutex). The
24+high-priority thread attempts to acquire the mutex and is blocked
25+because of the low-priority thread holding it. If priority inheritence
26+is working, the low-priority thread will receive a priority boost
27+(will inherit the high-priority thread's priority) and will then run
28+and release the mutex, averting a deadlock.
29+ 
30+On a multi-processor system, the admin and reporter threads are run
31+one one processor while the inversion groups are run on another
32+processor.
33+ 
34+ 
35+STEPS TO RUN
36+ 
37+Install the pi_tests rpm. If you are running Fedora Core, the simplest
38+way do to that is to fetchthe rt-userspace.repo file from:
39+ 
40+http://people.redhat.com/~williams/rt-userspace.repo
41+ 
42+and install that file in /etc/yum.repos.d, then run:
43+ 
44+$ yum install pi_stress
45+ 
46+If you are not running Fedora, you can fetch the SRPM and build it, or
47+fetch the tarball from:
48+ 
49+http://people.redhat.com/~williams/tests
50+ 
51+Once you've installed it, to see the options provided by pi_stress you
52+can run it with the --help option to get a usage message or read the
53+man page for more detail.
54+ 
55+Since the stress test runs as a number of realtime threads, it's
56+usually a good idea to run the test while logged into the test machine
57+from another system. Its always a good idea to have a window up
58+that's either a serial console to the test machine or something that's
59+continually printing the contents of the test system's syslog
60+(e.g. 'tail -f /var/log/messages').
61+ 
62+Login to the test machine and run pi_stress as root:
63+ 
64+$ sudo /usr/sbin/pi_stress
65+ 
66+This will run the test with the defaults of:
67+ 
68+ - no stop time
69+ - 10 inversion groups
70+ 
71+A banner will be printed indicating what defaults are in use and then
72+a display will be updated showing the current number of inversions
73+that have been performed. To stop the test, press any key and a
74+summary will be printed to show how many inversions were performed and
75+how long the test ran.
76+ 
77+To run the test for a specifed time, use the --duraton=<seconds>
78+option.
79+ 
80+ 
@@ -0,0 +1,104 @@
1+.\" Process this file with
2+.\" groff -man -Tascii pi_stress.8
3+.\"
4+.\"{{{}}}
5+.\"{{{ Title
6+.TH pi_stress 8 "Nov 27, 2006" "" "Linux System Administrator's Manual"
7+# SPDX-License-Identifier: GPL-2.0-or-later
8+.\"}}}
9+.\"{{{ Name
10+.SH NAME
11+pi_stress \- a stress test for POSIX Priority Inheritance mutexes
12+.\"}}}
13+.\"{{{ Synopsis
14+.\" Usage: pi_stress [-d] [-D TIME] [-g N] [-h] [-i N ] [--json FILENAME ] [-m] [-p] [-q] [-r] [-s OPTS] [-u] [-v] [-V]
15+.SH SYNOPSIS
16+.B pi_stress
17+.RB [ \-d|\-\-debug]
18+.RB [ \-D|\-\-duration
19+.iR TIME ]
20+.RB [ \-g|\-\-groups
21+.IR N ]
22+.RB [ \-i|\-\-inversions
23+.IR INV ]
24+.RB [ \-\-json
25+.IR FILENAME ]
26+.RB [ \-m|\-\-mlockall ]
27+.RB [ \-p|\-\-prompt ]
28+.RB [ \-q|\-\-quiet ]
29+.RB [ \-r|\-\-rr ]
30+.RB [ \-s|\-\-sched
31+.IR OPTS ]
32+.RB [ \-u|\-\-uniprocessor ]
33+.RB [ \-v|\-\-verbose ]
34+.RB [ \-V|\-\-version ]
35+.br
36+.SH DESCRIPTION
37+.B pi_stress
38+is a program used to stress the
39+.IR priority-inheritance
40+code paths for POSIX mutexes, in both the Linux kernel and the C
41+library. It runs as a realtime-priority task and launches
42+.IR "inversion machine"
43+thread groups. Each inversion group causes a
44+.IR "priority inversion"
45+condition that will deadlock if
46+.IR "priority inheritance"
47+doesn't work.
48+ 
49+.SH OPTIONS
50+.IP "\-d|\-\-debug"
51+Run in debug mode; lots of extra prints
52+.IP "\-D TIME, \-\-duration=TIME"
53+Specify a length for the test run.
54+.br
55+Append 'm', 'h', or 'd' to specify minutes, hours or days.
56+.IP "\-g N|\-\-groups=N"
57+The number of inversion groups to run. Defaults to 10.
58+.IP "\-h|\-\-help"
59+Display a short help message and options.
60+.IP "\-i N|\-\-inversions=N"
61+.I N
62+number of inversion conditions. This is the total number of inversions
63+for all inversion groups. Default is \-1 for infinite.
64+.IP "\-\-json=FILENAME"
65+Write final results into
66+.I FILENAME
67+, JSON formatted.
68+.IP "\-m|\-\-mlockall"
69+Call mlockall to lock current and future memory allocations and
70+prevent being paged out
71+.IP "\-p|\-\-prompt"
72+Prompt before actually starting the stress test
73+.IP "\-q|\-\-quiet"
74+Suppress running output
75+.IP "\-r|\-\-rr"
76+Run inversion group threads as SCHED_RR (round-robin). The default is
77+to run the inversion threads as SCHED_FIFO.
78+.IP "\-s OPTS|\-\-sched OPTS"
79+scheduling options per thread type:
80+.br
81+id=[high|med|low],
82+.br
83+policy=[fifo,rr],priority=N,
84+.br
85+policy=deadline,runtime=N,deadline=N,period=N
86+.IP "\-u|\-\-uniprocessor"
87+Run all threads on one processor. The default is to run all inversion
88+group threads on one processor and the admin threads (reporting
89+thread, keyboard reader, etc.) on a different processor.
90+.IP "\-v|\-\-verbose"
91+Run with verbose messages
92+.IP "\-V|\-\-version"
93+Print version number
94+.SH CAVEATS
95+The pi_stress test threads run as SCHED_FIFO or SCHED_RR threads,
96+which means that they can starve critical system threads. It is
97+advisable to change the scheduling policy of critical system threads
98+to be SCHED_FIFO prior to running pi_stress and use a priority of 10
99+or higher, to prevent those threads from being starved by the stress
100+test.
101+.SH BUGS
102+No documented bugs.
103+.SH AUTHOR
104+Clark Williams <williams@redhat.com>
@@ -0,0 +1,1378 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * pi_stress - Priority Inheritance stress test
4+ * Copyright (C) 2006, 2007 Clark Williams <williams@redhat.com>
5+ */
6+ 
7+/*
8+ * This program stress tests pthreads priority inheritance mutexes
9+ 
10+ * The logic is built upon the state machine that performs the "classic_pi"
11+ * deadlock scenario. A state machine or "inversion group" is a group of three
12+ * threads as described below.
13+ *
14+ * The basic premise here is to set up a deadlock scenario and confirm that PI
15+ * mutexes resolve the situation. Three worker threads will be created from the
16+ * main thread: low, medium and high priority threads that use SCHED_FIFO as
17+ * their scheduling policy. The low priority thread claims a mutex and then
18+ * starts "working". The medium priority thread starts and preempts the low
19+ * priority thread. Then the high priority thread runs and attempts to claim
20+ * the mutex owned by the low priority thread. Without priority inheritance,
21+ * this will deadlock the program. With priority inheritance, the low priority
22+ * thread receives a priority boost, finishes it's "work" and releases the mutex
23+ * which allows the high priority thread to run and finish and then the medium
24+ * priority thread finishes.
25+ *
26+ * That's the theory, anyway...
27+ *
28+ * CW - 2006
29+ */
30+ 
31+#include "pi_stress.h"
32+ 
33+/* the length of the test */
34+/* default is infinite */
35+int duration = -1;
36+ 
37+/* times for starting and finishing the stress test */
38+time_t start, finish;
39+ 
40+/* the number of groups to create */
41+int ngroups;
42+ 
43+/* the number of times a group causes a priority inversion situation */
44+/* default to infinite */
45+int inversions = -1;
46+ 
47+/* turn on lots of prints */
48+int verbose;
49+ 
50+/* turn on pi_debugging prints */
51+int debugging;
52+ 
53+/* turn off all prints, default = 0 (off) */
54+int quiet;
55+ 
56+/* filename for JSON output */
57+char jsonfile[MAX_PATH];
58+ 
59+/* prompt to start test */
60+int prompt;
61+ 
62+/* report interval */
63+unsigned long report_interval = (unsigned long)SEC_TO_USEC(0.75);
64+ 
65+int shutdown; /* global indicating we should shut down */
66+pthread_mutex_t shutdown_mtx; /* associated mutex */
67+ 
68+/* indicate if errors have occurred */
69+int have_errors;
70+ 
71+/* indicated that keyboard interrupt has happened */
72+int interrupted;
73+ 
74+/* force running on one cpu */
75+int uniprocessor;
76+ 
77+/* lock all memory */
78+int lockall;
79+ 
80+pthread_barrier_t all_threads_ready;
81+pthread_barrier_t all_threads_done;
82+ 
83+cpu_set_t test_cpu_mask, admin_cpu_mask;
84+ 
85+int policy = SCHED_FIFO;
86+ 
87+/* scheduling attributes per thread */
88+struct sched_attr low_sa;
89+struct sched_attr med_sa;
90+struct sched_attr high_sa;
91+struct sched_attr admin_sa;
92+ 
93+unsigned int sa_initialized;
94+ 
95+struct group_parameters *groups;
96+ 
97+/* number of online processors */
98+long num_processors;
99+ 
100+int main(int argc, char **argv)
101+{
102+ int status;
103+ struct sched_param thread_param;
104+ int i;
105+ int retval = FAILURE;
106+ int core;
107+ int nthreads;
108+ 
109+ /* Make sure we see all message, even those on stdout. */
110+ setvbuf(stdout, NULL, _IONBF, 0);
111+ 
112+ /* get the number of processors */
113+ num_processors = sysconf(_SC_NPROCESSORS_ONLN);
114+ 
115+ /* calculate the number of inversion groups to run */
116+ ngroups = num_processors == 1 ? 1 : num_processors - 1;
117+ 
118+ 
119+ /* process command line arguments */
120+ rt_init(argc, argv);
121+ process_command_line(argc, argv);
122+ 
123+ /* set default sched attributes */
124+ setup_sched_config(policy);
125+ 
126+ /* lock memory */
127+ if (lockall)
128+ if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
129+ pi_error("mlockall failed\n");
130+ return FAILURE;
131+ }
132+ /* boost main's priority (so we keep running) :) */
133+ thread_param.sched_priority = admin_sa.sched_priority;
134+ status = pthread_setschedparam(pthread_self(), admin_sa.sched_policy,
135+ &thread_param);
136+ if (status) {
137+ pi_error("%s: boosting to max priority: 0x%x\n", __func__,
138+ status);
139+ return FAILURE;
140+ }
141+ /* block unwanted signals */
142+ block_signals();
143+ 
144+ /* allocate our groups array */
145+ groups = calloc(ngroups, sizeof(struct group_parameters));
146+ if (groups == NULL) {
147+ pi_error("%s: failed to allocate %d groups\n", __func__,
148+ ngroups);
149+ return FAILURE;
150+ }
151+ /* set up CPU affinity masks */
152+ if (set_cpu_affinity(&test_cpu_mask, &admin_cpu_mask))
153+ return FAILURE;
154+ 
155+ nthreads = ngroups * NUM_TEST_THREADS + NUM_ADMIN_THREADS;
156+ 
157+ /* set up our ready barrier */
158+ if (barrier_init(&all_threads_ready, NULL, nthreads,
159+ "all_threads_ready"))
160+ return FAILURE;
161+ 
162+ /* set up our done barrier */
163+ if (barrier_init(&all_threads_done, NULL, nthreads, "all_threads_done"))
164+ return FAILURE;
165+ 
166+ /* create the groups */
167+ pi_info("Creating %d test groups\n", ngroups);
168+ for (core = 0; core < num_processors; core++)
169+ if (CPU_ISSET(core, &test_cpu_mask))
170+ break;
171+ for (i = 0; i < ngroups; i++) {
172+ groups[i].id = i;
173+ groups[i].cpu = core++;
174+ if (core >= num_processors)
175+ core = 0;
176+ if (create_group(&groups[i]) != SUCCESS)
177+ return FAILURE;
178+ }
179+ 
180+ /* prompt if requested */
181+ if (prompt) {
182+ printf("Press return to start test: ");
183+ getchar();
184+ }
185+ /* report */
186+ banner();
187+ start = time(NULL);
188+ 
189+ /* turn loose the threads */
190+ pi_info("Releasing all threads\n");
191+ status = pthread_barrier_wait(&all_threads_ready);
192+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
193+ pi_error("%s: pthread_barrier_wait(all_threads_ready): 0x%x\n",
194+ __func__, status);
195+ set_shutdown_flag();
196+ return FAILURE;
197+ }
198+ 
199+ reporter(NULL);
200+ 
201+ if (!quiet) {
202+ fputs(DOWN_ONE, stdout);
203+ printf("Stopping test\n");
204+ }
205+ set_shutdown_flag();
206+ 
207+ /* wait for all threads to notice the shutdown flag */
208+ if (have_errors == 0 && interrupted == 0) {
209+ pi_info("waiting for all threads to complete\n");
210+ status = pthread_barrier_wait(&all_threads_done);
211+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
212+ pi_error("%s: pthread_barrier_wait(all_threads_ready): 0x%x\n", __func__, status);
213+ return FAILURE;
214+ }
215+ pi_info("All threads terminated!\n");
216+ retval = SUCCESS;
217+ } else
218+ kill(0, SIGTERM);
219+ finish = time(NULL);
220+ summary();
221+ 
222+ if (strlen(jsonfile) != 0)
223+ rt_write_json(jsonfile, retval, write_stats, NULL);
224+ 
225+ if (lockall)
226+ munlockall();
227+ exit(retval);
228+}
229+ 
230+int
231+setup_thread_attr(pthread_attr_t *attr, struct sched_attr *sa,
232+ cpu_set_t *mask)
233+{
234+ int status;
235+ struct sched_param thread_param;
236+ 
237+ status = pthread_attr_init(attr);
238+ if (status) {
239+ pi_error
240+ ("%s: initializing thread attribute: 0x%x\n", __func__,
241+ status);
242+ return FAILURE;
243+ }
244+ status = pthread_attr_setaffinity_np(attr, sizeof(cpu_set_t), mask);
245+ if (status) {
246+ pi_error("%s: setting affinity attribute: 0x%x\n", __func__,
247+ status);
248+ return FAILURE;
249+ }
250+ 
251+ /*
252+ * The pthread API does not yet support SCHED_DEADLINE, defer the
253+ * thread configuration to setup_thread()
254+ */
255+ if (sa->sched_policy == SCHED_DEADLINE)
256+ return SUCCESS;
257+ 
258+ status = pthread_attr_setschedpolicy(attr, sa->sched_policy);
259+ if (status) {
260+ pi_error
261+ ("%s: setting attribute policy to %s: 0x%x\n", __func__,
262+ policy_to_string(sa->sched_policy),
263+ status);
264+ return FAILURE;
265+ }
266+ status = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
267+ if (status) {
268+ pi_error
269+ ("%s: setting explicit scheduling inheritance: 0x%x\n",
270+ __func__, status);
271+ return FAILURE;
272+ }
273+ thread_param.sched_priority = sa->sched_priority;
274+ status = pthread_attr_setschedparam(attr, &thread_param);
275+ if (status) {
276+ pi_error("%s: setting scheduler param: 0x%x\n", __func__,
277+ status);
278+ return FAILURE;
279+ }
280+ return SUCCESS;
281+}
282+ 
283+int set_cpu_affinity(cpu_set_t *test_mask, cpu_set_t *admin_mask)
284+{
285+ int status, i, admin_proc;
286+ cpu_set_t current_mask;
287+ 
288+ /* handle uniprocessor case */
289+ if (num_processors == 1 || uniprocessor) {
290+ CPU_ZERO(admin_mask);
291+ CPU_ZERO(test_mask);
292+ CPU_SET(0, admin_mask);
293+ CPU_SET(0, test_mask);
294+ pi_info("admin and test threads running on one processor\n");
295+ return SUCCESS;
296+ }
297+ /*
298+ * first set our main thread to run on the first
299+ * scheduleable processor we can find
300+ */
301+ status = sched_getaffinity(0, sizeof(cpu_set_t), &current_mask);
302+ if (status) {
303+ pi_error("failed getting CPU affinity mask: 0x%x\n", status);
304+ return FAILURE;
305+ }
306+ for (i = 0; i < num_processors; i++) {
307+ if (CPU_ISSET(i, &current_mask))
308+ break;
309+ }
310+ if (i >= num_processors) {
311+ pi_error("No schedulable CPU found for main!\n");
312+ return FAILURE;
313+ }
314+ admin_proc = i;
315+ CPU_ZERO(admin_mask);
316+ CPU_SET(admin_proc, admin_mask);
317+ status = sched_setaffinity(0, sizeof(cpu_set_t), admin_mask);
318+ if (status) {
319+ pi_error("%s: setting CPU affinity mask: 0x%x\n", __func__,
320+ status);
321+ return FAILURE;
322+ }
323+ pi_info("Admin thread running on processor: %d\n", i);
324+ 
325+ /* Set test affinity so that tests run on the non-admin processors */
326+ CPU_ZERO(test_mask);
327+ for (i = admin_proc + 1; i < num_processors; i++)
328+ CPU_SET(i, test_mask);
329+ 
330+ if (admin_proc + 1 == num_processors - 1)
331+ pi_info("Test threads running on processor: %ld\n",
332+ num_processors - 1);
333+ else
334+ pi_info("Test threads running on processors: %d-%d\n",
335+ admin_proc + 1, (int)num_processors - 1);
336+ 
337+ return SUCCESS;
338+}
339+ 
340+/* clear all watchdog counters */
341+void watchdog_clear(void)
342+{
343+ int i;
344+ 
345+ for (i = 0; i < ngroups; i++)
346+ groups[i].watchdog = 0;
347+}
348+ 
349+/* check for zero watchdog counters */
350+int watchdog_check(void)
351+{
352+ int i;
353+ int failures = 0;
354+ struct group_parameters *g;
355+ 
356+ for (i = 0; i < ngroups; i++) {
357+ g = &groups[i];
358+ if (g->watchdog == 0) {
359+ /* don't report deadlock if group is finished */
360+ if (g->inversions == g->total)
361+ continue;
362+ if (++g->watchdog_hits >= WATCHDOG_LIMIT) {
363+ pi_error
364+ ("WATCHDOG triggered: group %d is deadlocked!\n",
365+ i);
366+ failures++;
367+ }
368+ } else
369+ g->watchdog_hits = 0;
370+ }
371+ return failures ? FAILURE : SUCCESS;
372+}
373+ 
374+int pending_interrupt(void)
375+{
376+ sigset_t pending;
377+ 
378+ if (sigpending(&pending) < 0) {
379+ pi_error("from sigpending: %s\n", strerror(errno));
380+ return 0;
381+ }
382+ 
383+ return interrupted = sigismember(&pending, SIGINT);
384+}
385+ 
386+/*
387+ * this routine serves two purposes:
388+ * 1. report progress
389+ * 2. check for deadlocks
390+ */
391+void *reporter(void *arg __attribute__ ((unused)))
392+{
393+ int status;
394+ int end = 0;
395+ struct timespec ts;
396+ 
397+ ts.tv_sec = 0;
398+ ts.tv_nsec = USEC_TO_NSEC(report_interval);
399+ 
400+ tsnorm(&ts);
401+ 
402+ if (duration >= 0)
403+ end = duration + time(NULL);
404+ 
405+ /* sleep initially to let everything get up and running */
406+ status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
407+ if (status) {
408+ pi_error("from clock_nanosleep: %s\n", strerror(status));
409+ return NULL;
410+ }
411+ 
412+ pi_debug("%s: starting report loop\n", __func__);
413+ pi_info("Press Control-C to stop test\nCurrent Inversions:\n");
414+ 
415+ for (;;) {
416+ pthread_mutex_lock(&shutdown_mtx);
417+ if (shutdown) {
418+ pthread_mutex_unlock(&shutdown_mtx);
419+ break;
420+ }
421+ pthread_mutex_unlock(&shutdown_mtx);
422+ 
423+ /* wait for our reporting interval */
424+ status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
425+ if (status) {
426+ pi_error("from clock_nanosleep: %s\n", strerror(status));
427+ break;
428+ }
429+ 
430+ /* check for signaled shutdown */
431+ if (!quiet) {
432+ pthread_mutex_lock(&shutdown_mtx);
433+ if (shutdown == 0) {
434+ fputs(UP_ONE, stdout);
435+ printf("Current Inversions: %lu\n",
436+ total_inversions());
437+ }
438+ pthread_mutex_unlock(&shutdown_mtx);
439+ }
440+ 
441+ /* if we specified a duration, see if it has expired */
442+ if (end && time(NULL) > end) {
443+ pi_info("duration reached (%d seconds)\n", duration);
444+ set_shutdown_flag();
445+ continue;
446+ }
447+ /* check for a pending SIGINT */
448+ if (pending_interrupt()) {
449+ pi_info("Keyboard Interrupt!\n");
450+ break;
451+ }
452+ /* check watchdog stuff */
453+ if ((watchdog_check())) {
454+ pi_error("%s stopping due to watchdog event\n", __func__);
455+ set_shutdown_flag();
456+ break;
457+ }
458+ /* clear watchdog counters */
459+ watchdog_clear();
460+ 
461+ }
462+ pi_debug("%s: finished\n", __func__);
463+ set_shutdown_flag();
464+ return NULL;
465+}
466+ 
467+int verify_cpu(int cpu)
468+{
469+ int status;
470+ int err;
471+ cpu_set_t mask;
472+ 
473+ CPU_ZERO(&mask);
474+ 
475+ status = sched_getaffinity(0, sizeof(cpu_set_t), &mask);
476+ if (status == -1) {
477+ err = errno;
478+ fprintf(stderr, "sched_getaffinity %s\n", strerror(err));
479+ exit(1);
480+ }
481+ 
482+ if (CPU_ISSET(cpu, &mask))
483+ return SUCCESS;
484+ return FAILURE;
485+}
486+ 
487+void *low_priority(void *arg)
488+{
489+ int status;
490+ int unbounded;
491+ unsigned long count = 0;
492+ struct group_parameters *p = (struct group_parameters *)arg;
493+ pthread_barrier_t *loop_barr = &p->loop_barr;
494+ pthread_mutex_t *loop_mtx = &p->loop_mtx;
495+ int *loop = &p->loop;
496+ 
497+ allow_sigterm();
498+ 
499+ if (verify_cpu(p->cpu) != SUCCESS) {
500+ pi_error("low_priority[%d]: not bound to %ld\n", p->id, p->cpu);
501+ return NULL;
502+ }
503+ 
504+ pi_debug("low_priority[%d]: entering ready state\n", p->id);
505+ /* wait for all threads to be ready */
506+ status = pthread_barrier_wait(&all_threads_ready);
507+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
508+ pi_error
509+ ("low_priority[%d]: pthread_barrier_wait(all_threads_ready): %x",
510+ p->id, status);
511+ return NULL;
512+ }
513+ 
514+ unbounded = (p->inversions < 0);
515+ 
516+ pi_debug("low_priority[%d]: starting inversion loop\n", p->id);
517+ 
518+ for (;;) {
519+ /*
520+ * We can't set the 'loop' boolean here, because some flags
521+ * may have already reached the loop_barr
522+ */
523+ if (!unbounded && (p->total >= (unsigned int)p->inversions))
524+ set_shutdown_flag();
525+ 
526+ /* Either all threads go through the loop_barr, or none do */
527+ pthread_mutex_lock(loop_mtx);
528+ if (*loop == 0) {
529+ pthread_mutex_unlock(loop_mtx);
530+ break;
531+ }
532+ pthread_mutex_unlock(loop_mtx);
533+ 
534+ status = pthread_barrier_wait(loop_barr);
535+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
536+ pi_error("%s[%d]: pthread_barrier_wait(loop): %x\n",
537+ __func__, p->id, status);
538+ return NULL;
539+ }
540+ 
541+ /* Only one Thread needs to check the shutdown status */
542+ if (status == PTHREAD_BARRIER_SERIAL_THREAD) {
543+ pthread_mutex_lock(&shutdown_mtx);
544+ if (shutdown) {
545+ pthread_mutex_lock(loop_mtx);
546+ *loop = 0;
547+ pthread_mutex_unlock(loop_mtx);
548+ }
549+ pthread_mutex_unlock(&shutdown_mtx);
550+ }
551+ 
552+ /* initial state */
553+ pi_debug("low_priority[%d]: entering start wait (%ld)\n", p->id,
554+ count++);
555+ status = pthread_barrier_wait(&p->start_barrier);
556+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
557+ pi_error
558+ ("low_priority[%d]: pthread_barrier_wait(start): %x\n",
559+ p->id, status);
560+ return NULL;
561+ }
562+ 
563+ pi_debug("low_priority[%d]: claiming mutex\n", p->id);
564+ pthread_mutex_lock(&p->mutex);
565+ pi_debug("low_priority[%d]: mutex locked\n", p->id);
566+ 
567+ pi_debug("low_priority[%d]: entering locked wait\n", p->id);
568+ status = pthread_barrier_wait(&p->locked_barrier);
569+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
570+ pi_error
571+ ("low_priority[%d]: pthread_barrier_wait(locked): %x\n",
572+ p->id, status);
573+ /* release the mutex */
574+ pi_debug("low_priority[%d]: unlocking mutex\n", p->id);
575+ pthread_mutex_unlock(&p->mutex);
576+ return NULL;
577+ }
578+ 
579+ /* wait for priority boost */
580+ pi_debug("low_priority[%d]: entering elevated wait\n", p->id);
581+ status = pthread_barrier_wait(&p->elevate_barrier);
582+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
583+ pi_error
584+ ("low_priority[%d]: pthread_barrier_wait(elevate): %x\n",
585+ p->id, status);
586+ /* release the mutex */
587+ pi_debug("low_priority[%d]: unlocking mutex\n", p->id);
588+ pthread_mutex_unlock(&p->mutex);
589+ return NULL;
590+ }
591+ 
592+ /* release the mutex */
593+ pi_debug("low_priority[%d]: unlocking mutex\n", p->id);
594+ pthread_mutex_unlock(&p->mutex);
595+ 
596+ /* finish state */
597+ pi_debug("low_priority[%d]: entering finish wait\n", p->id);
598+ status = pthread_barrier_wait(&p->finish_barrier);
599+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
600+ pi_error
601+ ("low_priority[%d]: pthread_barrier_wait(elevate): %x\n",
602+ p->id, status);
603+ return NULL;
604+ }
605+ }
606+ set_shutdown_flag();
607+ pi_debug("low_priority[%d]: entering done barrier\n", p->id);
608+ /* wait for all threads to finish */
609+ status = pthread_barrier_wait(&all_threads_done);
610+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
611+ pi_error
612+ ("low_priority[%d]: pthread_barrier_wait(all_threads_done): %x",
613+ p->id, status);
614+ return NULL;
615+ }
616+ pi_debug("low_priority[%d]: exiting\n", p->id);
617+ return NULL;
618+}
619+ 
620+void *med_priority(void *arg)
621+{
622+ int status;
623+ int unbounded;
624+ unsigned long count = 0;
625+ struct group_parameters *p = (struct group_parameters *)arg;
626+ pthread_barrier_t *loop_barr = &p->loop_barr;
627+ pthread_mutex_t *loop_mtx = &p->loop_mtx;
628+ int *loop = &p->loop;
629+ 
630+ allow_sigterm();
631+ 
632+ if (verify_cpu(p->cpu) != SUCCESS) {
633+ pi_error("med_priority[%d]: not bound to %ld\n", p->id, p->cpu);
634+ return NULL;
635+ }
636+ 
637+ pi_debug("med_priority[%d]: entering ready state\n", p->id);
638+ /* wait for all threads to be ready */
639+ status = pthread_barrier_wait(&all_threads_ready);
640+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
641+ pi_error
642+ ("med_priority[%d]: pthread_barrier_wait(all_threads_ready): %x",
643+ p->id, status);
644+ return NULL;
645+ }
646+ 
647+ unbounded = (p->inversions < 0);
648+ 
649+ pi_debug("med_priority[%d]: starting inversion loop\n", p->id);
650+ for (;;) {
651+ if (!unbounded && (p->total >= (unsigned int)p->inversions))
652+ set_shutdown_flag();
653+ 
654+ /* Either all threads go through the loop_barr, or none do */
655+ pthread_mutex_lock(loop_mtx);
656+ if (*loop == 0) {
657+ pthread_mutex_unlock(loop_mtx);
658+ break;
659+ }
660+ pthread_mutex_unlock(loop_mtx);
661+ 
662+ status = pthread_barrier_wait(loop_barr);
663+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
664+ pi_error("%s[%d]: pthread_barrier_wait(loop): %x\n",
665+ __func__, p->id, status);
666+ return NULL;
667+ }
668+ 
669+ /* Only one Thread needs to check the shutdown status */
670+ if (status == PTHREAD_BARRIER_SERIAL_THREAD) {
671+ pthread_mutex_lock(&shutdown_mtx);
672+ if (shutdown) {
673+ pthread_mutex_lock(loop_mtx);
674+ *loop = 0;
675+ pthread_mutex_unlock(loop_mtx);
676+ }
677+ pthread_mutex_unlock(&shutdown_mtx);
678+ }
679+ 
680+ /* start state */
681+ pi_debug("med_priority[%d]: entering start state (%ld)\n", p->id,
682+ count++);
683+ status = pthread_barrier_wait(&p->start_barrier);
684+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
685+ pi_error
686+ ("med_priority[%d]: pthread_barrier_wait(start): %x",
687+ p->id, status);
688+ return NULL;
689+ }
690+ pi_debug("med_priority[%d]: entering elevate state\n", p->id);
691+ status = pthread_barrier_wait(&p->elevate_barrier);
692+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
693+ pi_error ("med_priority[%d]: pthread_barrier_wait(elevate): %x", p->id, status);
694+ return NULL;
695+ }
696+ 
697+ pi_debug("med_priority[%d]: entering finish state\n", p->id);
698+ status = pthread_barrier_wait(&p->finish_barrier);
699+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
700+ pi_error
701+ ("med_priority[%d]: pthread_barrier_wait(finished): %x",
702+ p->id, status);
703+ return NULL;
704+ }
705+ }
706+ set_shutdown_flag();
707+ 
708+ pi_debug("med_priority[%d]: entering done barrier\n", p->id);
709+ /* wait for all threads to finish */
710+ if (have_errors == 0) {
711+ status = pthread_barrier_wait(&all_threads_done);
712+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
713+ pi_error
714+ ("med_priority[%d]: pthread_barrier_wait(all_threads_done): %x",
715+ p->id, status);
716+ return NULL;
717+ }
718+ }
719+ /* exit */
720+ pi_debug("med_priority[%d]: exiting\n", p->id);
721+ return NULL;
722+}
723+ 
724+void *high_priority(void *arg)
725+{
726+ int status;
727+ int unbounded;
728+ unsigned long count = 0;
729+ struct group_parameters *p = (struct group_parameters *)arg;
730+ pthread_barrier_t *loop_barr = &p->loop_barr;
731+ pthread_mutex_t *loop_mtx = &p->loop_mtx;
732+ int *loop = &p->loop;
733+ cpu_set_t cpu_mask;
734+ int i;
735+ 
736+ if (high_sa.sched_policy == SCHED_DEADLINE) {
737+ CPU_ZERO(&cpu_mask);
738+ for (i = 0; i < num_processors; i++)
739+ CPU_SET(i, &cpu_mask);
740+ status = sched_setaffinity(0, sizeof(cpu_set_t), &cpu_mask);
741+ if (status < 0) {
742+ pi_error
743+ ("high_priority[%d]: set cpu affinity*dl): %x\n",
744+ p->id, status);
745+ return NULL;
746+ }
747+ 
748+ status = sched_setattr(gettid(), &high_sa, 0);
749+ if (status < 0) {
750+ pi_error
751+ ("high_priority[%d]: sched_setattr(dl): %x\n",
752+ p->id, status);
753+ return NULL;
754+ }
755+ }
756+ 
757+ allow_sigterm();
758+ if (verify_cpu(p->cpu) != SUCCESS) {
759+ pi_error("high_priority[%d]: not bound to %ld\n", p->id, p->cpu);
760+ return NULL;
761+ }
762+ 
763+ pi_debug("high_priority[%d]: entering ready state\n", p->id);
764+ 
765+ /* wait for all threads to be ready */
766+ status = pthread_barrier_wait(&all_threads_ready);
767+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
768+ pi_error
769+ ("high_priority[%d]: pthread_barrier_wait(all_threads_ready): %x",
770+ p->id, status);
771+ return NULL;
772+ }
773+ unbounded = (p->inversions < 0);
774+ pi_debug("high_priority[%d]: starting inversion loop\n", p->id);
775+ for (;;) {
776+ if (!unbounded && (p->total >= (unsigned int)p->inversions)) {
777+ set_shutdown_flag();
778+ }
779+ 
780+ /* Either all threads go through the loop_barr, or none do */
781+ pthread_mutex_lock(loop_mtx);
782+ if (*loop == 0) {
783+ pthread_mutex_unlock(loop_mtx);
784+ break;
785+ }
786+ pthread_mutex_unlock(loop_mtx);
787+ 
788+ status = pthread_barrier_wait(loop_barr);
789+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
790+ pi_error("%s[%d]: pthread_barrier_wait(loop): %x\n",
791+ __func__, p->id, status);
792+ return NULL;
793+ }
794+ 
795+ /* Only one Thread needs to check the shutdown status */
796+ if (status == PTHREAD_BARRIER_SERIAL_THREAD) {
797+ pthread_mutex_lock(&shutdown_mtx);
798+ if (shutdown) {
799+ pthread_mutex_lock(loop_mtx);
800+ *loop = 0;
801+ pthread_mutex_unlock(loop_mtx);
802+ }
803+ pthread_mutex_unlock(&shutdown_mtx);
804+ }
805+ pi_debug("high_priority[%d]: entering start state (%d)\n", p->id,
806+ count++);
807+ status = pthread_barrier_wait(&p->start_barrier);
808+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
809+ pi_error
810+ ("high_priority[%d]: pthread_barrier_wait(start): %x",
811+ p->id, status);
812+ return NULL;
813+ }
814+ 
815+ pi_debug("high_priority[%d]: entering running state\n", p->id);
816+ status = pthread_barrier_wait(&p->locked_barrier);
817+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
818+ pi_error
819+ ("high_priority[%d]: pthread_barrier_wait(running): %x",
820+ p->id, status);
821+ return NULL;
822+ }
823+ pi_debug("high_priority[%d]: locking mutex\n", p->id);
824+ pthread_mutex_lock(&p->mutex);
825+ pi_debug("high_priority[%d]: got mutex\n", p->id);
826+ 
827+ pi_debug("high_priority[%d]: unlocking mutex\n", p->id);
828+ pthread_mutex_unlock(&p->mutex);
829+ pi_debug("high_priority[%d]: entering finish state\n", p->id);
830+ 
831+ status = pthread_barrier_wait(&p->finish_barrier);
832+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
833+ pi_error
834+ ("high_priority[%d]: pthread_barrier_wait(finish): %x", p->id, status);
835+ return NULL;
836+ }
837+ /* update the group stats */
838+ p->total++;
839+ 
840+ /* update the watchdog counter */
841+ p->watchdog++;
842+ 
843+ }
844+ set_shutdown_flag();
845+ 
846+ pi_debug("high_priority[%d]: entering done barrier\n", p->id);
847+ 
848+ if (have_errors == 0) {
849+ /* wait for all threads to finish */
850+ status = pthread_barrier_wait(&all_threads_done);
851+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) {
852+ pi_error
853+ ("high_priority[%d]: pthread_barrier_wait(all_threads_done): %x",
854+ p->id, status);
855+ return NULL;
856+ }
857+ }
858+ /* exit */
859+ pi_debug("high_priority[%d]: exiting\n", p->id);
860+ return NULL;
861+}
862+ 
863+void usage(int error)
864+{
865+ printf("pi_stress V %1.2f\n", VERSION);
866+ printf("Usage:\n"
867+ "pi_stress <options>\n\n"
868+ "-d --debug turn on debug prints\n"
869+ "-D TIME --duration=TIME length of test run in seconds (default is infinite)\n"
870+ " Append 'm', 'h', or 'd'\n"
871+ " to specify minutes, hours or days.\n"
872+ "-g N --groups=N set the number of inversion groups\n"
873+ "-h --help print this message\n"
874+ "-i INV --inversions=INV number of inversions per group (default is infinite)\n"
875+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
876+ "-m --mlockall lock current and future memory\n"
877+ "-p --prompt prompt before starting the test\n"
878+ "-q --quiet suppress running output\n"
879+ "-r --rr use SCHED_RR for test threads [SCHED_FIFO]\n"
880+ "-s OPTS --sched OPTS scheduling options per thread type:\n"
881+ " id=[high|med|low] select thread\n"
882+ " ,policy=[fifo,rr] scheduling class [SCHED_FIFO, SCHED_RR]\n"
883+ " ,priority=N scheduling priority\n"
884+ " ,policy=[deadline] scheduling class [DEADLINE]\n"
885+ " ,runtime=N\n"
886+ " ,deadline=N\n"
887+ " ,period=N\n"
888+ "-u --uniprocessor force all threads to run on one processor\n"
889+ "-v --verbose lots of output\n"
890+ "-V --version print version number on output\n"
891+ );
892+ exit(error);
893+}
894+ 
895+/* block all signals (called from main) */
896+int block_signals(void)
897+{
898+ int status;
899+ sigset_t sigset;
900+ 
901+ /* mask off all signals */
902+ status = sigfillset(&sigset);
903+ if (status) {
904+ pi_error("setting up full signal set %s\n", strerror(status));
905+ return FAILURE;
906+ }
907+ status = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
908+ if (status) {
909+ pi_error("setting signal mask: %s\n", strerror(status));
910+ return FAILURE;
911+ }
912+ return SUCCESS;
913+}
914+ 
915+/* allow SIGTERM delivery (called from worker threads) */
916+int allow_sigterm(void)
917+{
918+ int status;
919+ sigset_t sigset;
920+ 
921+ status = sigemptyset(&sigset);
922+ if (status) {
923+ pi_error("creating empty signal set: %s\n", strerror(status));
924+ return FAILURE;
925+ }
926+ status = sigaddset(&sigset, SIGTERM);
927+ if (status) {
928+ pi_error("adding SIGTERM to signal set: %s\n", strerror(status));
929+ return FAILURE;
930+ }
931+ status = pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
932+ if (status) {
933+ pi_error("unblocking SIGTERM: %s\n", strerror(status));
934+ return FAILURE;
935+ }
936+ return SUCCESS;
937+}
938+ 
939+/* clean up before exiting */
940+void set_shutdown_flag(void)
941+{
942+ pthread_mutex_lock(&shutdown_mtx);
943+ if (shutdown == 0) {
944+ /* tell anyone that's looking that we're done */
945+ pi_info("setting shutdown flag\n");
946+ shutdown = 1;
947+ }
948+ pthread_mutex_unlock(&shutdown_mtx);
949+}
950+ 
951+/* set up a test group */
952+int initialize_group(struct group_parameters *group)
953+{
954+ int status;
955+ pthread_mutexattr_t mutex_attr;
956+ 
957+ group->inversions = inversions;
958+ 
959+ /* setup default attributes for the group mutex */
960+ /* (make it a PI mutex) */
961+ status = pthread_mutexattr_init(&mutex_attr);
962+ if (status) {
963+ pi_error("initializing mutex attribute: %s\n", strerror(status));
964+ return FAILURE;
965+ }
966+ 
967+ /* set priority inheritance attribute for mutex */
968+ status = pthread_mutexattr_setprotocol(&mutex_attr,
969+ PTHREAD_PRIO_INHERIT);
970+ if (status) {
971+ pi_error("setting mutex attribute policy: %s\n", strerror(status));
972+ return FAILURE;
973+ }
974+ /* initialize the group mutex */
975+ status = pthread_mutex_init(&group->mutex, &mutex_attr);
976+ if (status) {
977+ pi_error("initializing mutex: %s\n", strerror(status));
978+ return FAILURE;
979+ }
980+ 
981+ /* initialize the group barriers */
982+ if (barrier_init(&group->start_barrier, NULL, NUM_TEST_THREADS,
983+ "start_barrier"))
984+ return FAILURE;
985+ 
986+ if (barrier_init(&group->locked_barrier, NULL, 2, "locked_barrier"))
987+ return FAILURE;
988+ 
989+ if (barrier_init(&group->elevate_barrier, NULL, 2, "elevate_barrier"))
990+ return FAILURE;
991+ 
992+ if (barrier_init
993+ (&group->finish_barrier, NULL, NUM_TEST_THREADS, "finish_barrier"))
994+ return FAILURE;
995+ 
996+ if (barrier_init(&group->loop_barr, NULL, NUM_TEST_THREADS,
997+ "loop_barrier"))
998+ return FAILURE;
999+ 
1000+ status = pthread_mutex_init(&group->loop_mtx, NULL);
1001+ if (status) {
1002+ pi_error("pthread_mutex_init, status = %d\n", status);
1003+ return FAILURE;
1004+ }
1005+ 
1006+ status = pthread_mutex_lock(&group->loop_mtx);
1007+ if (status) {
1008+ pi_error("pthread_mutex_lock, status = %d\n", status);
1009+ return FAILURE;
1010+ }
1011+ 
1012+ group->loop = 1;
1013+ 
1014+ status = pthread_mutex_unlock(&group->loop_mtx);
1015+ if (status) {
1016+ pi_error("pthread_mutex_unlock, status = %d\n", status);
1017+ return FAILURE;
1018+ }
1019+ 
1020+ return SUCCESS;
1021+}
1022+ 
1023+/* setup and create a groups threads */
1024+int create_group(struct group_parameters *group)
1025+{
1026+ int status;
1027+ pthread_attr_t thread_attr;
1028+ cpu_set_t mask;
1029+ 
1030+ /* initialize group structure */
1031+ status = initialize_group(group);
1032+ if (status) {
1033+ pi_error("initializing group %d\n", group->id);
1034+ return FAILURE;
1035+ }
1036+ 
1037+ CPU_ZERO(&mask);
1038+ CPU_SET(group->cpu, &mask);
1039+ 
1040+ pi_debug("group %d bound to cpu %ld\n", group->id, group->cpu);
1041+ 
1042+ /* start the low priority thread */
1043+ pi_debug("creating low priority thread\n");
1044+ if (setup_thread_attr(&thread_attr, &low_sa, &mask))
1045+ return FAILURE;
1046+ status = pthread_create(&group->low_tid,
1047+ &thread_attr, low_priority, group);
1048+ if (status != 0) {
1049+ pi_error("creating low_priority thread: %s\n", strerror(status));
1050+ return FAILURE;
1051+ }
1052+ 
1053+ /* create the medium priority thread */
1054+ pi_debug("creating medium priority thread\n");
1055+ if (setup_thread_attr(&thread_attr, &med_sa, &mask))
1056+ return FAILURE;
1057+ status = pthread_create(&group->med_tid,
1058+ &thread_attr, med_priority, group);
1059+ if (status != 0) {
1060+ pi_error("creating med_priority thread: %s\n", strerror(status));
1061+ return FAILURE;
1062+ }
1063+ 
1064+ /* create the high priority thread */
1065+ pi_debug("creating high priority thread\n");
1066+ if (setup_thread_attr(&thread_attr, &high_sa, &mask))
1067+ return FAILURE;
1068+ status = pthread_create(&group->high_tid,
1069+ &thread_attr, high_priority, group);
1070+ if (status != 0) {
1071+ pi_error("creating high_priority thread: %s\n", strerror(status));
1072+ set_shutdown_flag();
1073+ return FAILURE;
1074+ }
1075+ return SUCCESS;
1076+}
1077+ 
1078+unsigned long parse_unsigned(const char *str)
1079+{
1080+ unsigned long n;
1081+ char *p;
1082+ 
1083+ errno = 0;
1084+ n = strtoul(str, &p, 10);
1085+ 
1086+ if ((errno == ERANGE && n == ULONG_MAX)
1087+ || (errno != 0 && n == 0)) {
1088+ pi_error("parsing number failed: %s\n", str);
1089+ exit(EXIT_FAILURE);
1090+ }
1091+ 
1092+ return n;
1093+}
1094+ 
1095+long parse_signed(const char *str)
1096+{
1097+ long n;
1098+ char *p;
1099+ 
1100+ errno = 0;
1101+ n = strtol(str, &p, 10);
1102+ 
1103+ if ((errno == ERANGE && (n == LONG_MAX || n == LONG_MIN))
1104+ || (errno != 0 && n == 0)) {
1105+ pi_error("parsing number failed: %s\n", str);
1106+ exit(EXIT_FAILURE);
1107+ }
1108+ 
1109+ return n;
1110+}
1111+ 
1112+int process_sched_line(const char *arg)
1113+{
1114+ char *buf, *k, *v;
1115+ const char del[] = ",=";
1116+ struct sched_attr sa = { 0, };
1117+ char *id = NULL;
1118+ int retval = SUCCESS;
1119+ 
1120+ buf = strdupa(arg);
1121+ 
1122+ k = strsep(&buf, del);
1123+ while (k) {
1124+ v = strsep(&buf, del);
1125+ if (!v)
1126+ break;
1127+ 
1128+ if (!strcmp(k, "id"))
1129+ id = v;
1130+ else if (!strcmp(k, "policy"))
1131+ sa.sched_policy = string_to_policy(v);
1132+ else if (!strcmp(k, "nice"))
1133+ sa.sched_nice = parse_signed(v);
1134+ else if (!strcmp(k, "priority"))
1135+ sa.sched_priority = parse_unsigned(v);
1136+ else if (!strcmp(k, "runtime"))
1137+ sa.sched_runtime = parse_unsigned(v);
1138+ else if (!strcmp(k, "deadline"))
1139+ sa.sched_deadline = parse_unsigned(v);
1140+ else if (!strcmp(k, "period"))
1141+ sa.sched_period = parse_unsigned(v);
1142+ 
1143+ k = strsep(&buf, del);
1144+ }
1145+ 
1146+ if (!id)
1147+ return FAILURE;
1148+ 
1149+ /*
1150+ * We do not validate the options, instead we pass all garbage
1151+ * to the kernel and see what's happening
1152+ */
1153+ 
1154+ if (!strcmp(id, "low")) {
1155+ memcpy(&low_sa, &sa, sizeof(struct sched_attr));
1156+ sa_initialized |= SA_INIT_LOW;
1157+ } else if (!strcmp(id, "med")) {
1158+ memcpy(&med_sa, &sa, sizeof(struct sched_attr));
1159+ sa_initialized |= SA_INIT_MED;
1160+ } else if (!strcmp(id, "high")) {
1161+ memcpy(&high_sa, &sa, sizeof(struct sched_attr));
1162+ sa_initialized |= SA_INIT_HIGH;
1163+ } else {
1164+ retval = FAILURE;
1165+ }
1166+ 
1167+ return retval;
1168+}
1169+ 
1170+enum option_values {
1171+ OPT_DEBUG=1, OPT_DURATION, OPT_GROUPS, OPT_HELP, OPT_INVERSIONS,
1172+ OPT_JSON, OPT_MLOCKALL, OPT_PROMPT, OPT_QUIET, OPT_RR, OPT_SCHED,
1173+ OPT_UNIPROCESSOR, OPT_VERBOSE, OPT_VERSION,
1174+};
1175+ 
1176+void process_command_line(int argc, char **argv)
1177+{
1178+ for (;;) {
1179+ struct option options[] = {
1180+ {"debug", no_argument, NULL, OPT_DEBUG},
1181+ {"duration", required_argument, NULL, OPT_DURATION},
1182+ {"groups", required_argument, NULL, OPT_GROUPS},
1183+ {"help", no_argument, NULL, OPT_HELP},
1184+ {"inversions", required_argument, NULL, OPT_INVERSIONS},
1185+ {"json", required_argument, NULL, OPT_JSON},
1186+ {"mlockall", no_argument, NULL, OPT_MLOCKALL},
1187+ {"prompt", no_argument, NULL, OPT_PROMPT},
1188+ {"quiet", no_argument, NULL, OPT_QUIET},
1189+ {"rr", no_argument, NULL, OPT_RR},
1190+ {"sched", required_argument, NULL, OPT_SCHED},
1191+ {"uniprocessor", no_argument, NULL, OPT_UNIPROCESSOR},
1192+ {"verbose", no_argument, NULL, OPT_VERBOSE},
1193+ {"version", no_argument, NULL, OPT_VERSION},
1194+ {NULL, 0, NULL, 0},
1195+ };
1196+ 
1197+ int c = getopt_long(argc, argv, "+hD:vqi:g:rs:pdVum", options, NULL);
1198+ if (c == -1)
1199+ break;
1200+ switch (c) {
1201+ case OPT_DEBUG:
1202+ case 'd':
1203+ debugging = 1;
1204+ break;
1205+ case OPT_DURATION:
1206+ case 'D':
1207+ duration = parse_time_string(optarg);
1208+ break;
1209+ case OPT_GROUPS:
1210+ case 'g':
1211+ ngroups = strtol(optarg, NULL, 10);
1212+ if (ngroups > num_processors) {
1213+ pi_error("the number of groups cannot exceed the number of online processors (%ld)\n", num_processors);
1214+ exit(-1);
1215+ }
1216+ pi_info("number of groups set to %d\n", ngroups);
1217+ break;
1218+ case OPT_HELP:
1219+ case 'h':
1220+ usage(0);
1221+ break;
1222+ case OPT_INVERSIONS:
1223+ case 'i':
1224+ inversions = strtol(optarg, NULL, 10);
1225+ pi_info("doing %d inversion per group\n", inversions);
1226+ break;
1227+ case OPT_JSON:
1228+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
1229+ break;
1230+ case OPT_MLOCKALL:
1231+ case 'm':
1232+ lockall = 1;
1233+ break;
1234+ case OPT_PROMPT:
1235+ case 'p':
1236+ prompt = 1;
1237+ break;
1238+ case OPT_QUIET:
1239+ case 'q':
1240+ verbose = 0;
1241+ quiet = 1;
1242+ break;
1243+ case OPT_RR:
1244+ case 'r':
1245+ policy = SCHED_RR;
1246+ break;
1247+ case OPT_SCHED:
1248+ case 's':
1249+ if (process_sched_line(optarg))
1250+ pi_error("ignoring invalid options '%s'\n", optarg);
1251+ break;
1252+ case OPT_UNIPROCESSOR:
1253+ case 'u':
1254+ uniprocessor = 1;
1255+ break;
1256+ case OPT_VERBOSE:
1257+ case 'v':
1258+ verbose = 1;
1259+ quiet = 0;
1260+ break;
1261+ case OPT_VERSION:
1262+ case 'V':
1263+ printf("pi_stress v%1.2f ", VERSION);
1264+ exit(0);
1265+ default:
1266+ usage(1);
1267+ break;
1268+ }
1269+ }
1270+}
1271+ 
1272+/* total the number of inversions that have been performed */
1273+unsigned long total_inversions(void)
1274+{
1275+ int i;
1276+ unsigned long total = 0;
1277+ 
1278+ for (i = 0; i < ngroups; i++)
1279+ total += groups[i].total;
1280+ return total;
1281+}
1282+ 
1283+void print_sched_attr(const char *name, struct sched_attr *sa)
1284+{
1285+ printf(" %6s thread", name);
1286+ printf(" %s", policy_to_string(sa->sched_policy));
1287+ 
1288+ switch (sa->sched_policy) {
1289+ case SCHED_OTHER:
1290+ printf(" nice %d\n", sa->sched_nice);
1291+ break;
1292+ case SCHED_FIFO:
1293+ case SCHED_RR:
1294+ printf(" priority %d\n", sa->sched_priority);
1295+ break;
1296+ case SCHED_DEADLINE:
1297+ printf(" runtime %" PRIu64 " deadline %" PRIu64 " period %" PRIu64 "\n",
1298+ sa->sched_runtime, sa->sched_deadline,
1299+ sa->sched_period);
1300+ break;
1301+ }
1302+}
1303+ 
1304+void banner(void)
1305+{
1306+ if (quiet)
1307+ return;
1308+ 
1309+ printf("Starting PI Stress Test\n");
1310+ printf("Number of thread groups: %d\n", ngroups);
1311+ if (duration >= 0)
1312+ printf("Duration of test run: %d seconds\n", duration);
1313+ else
1314+ printf("Duration of test run: infinite\n");
1315+ if (inversions < 0)
1316+ printf("Number of inversions per group: unlimited\n");
1317+ else
1318+ printf("Number of inversions per group: %d\n", inversions);
1319+ print_sched_attr("Admin", &admin_sa);
1320+ printf("%d groups of 3 threads will be created\n", ngroups);
1321+ print_sched_attr("High", &high_sa);
1322+ print_sched_attr("Med", &med_sa);
1323+ print_sched_attr("Low", &low_sa);
1324+ printf("\n");
1325+}
1326+ 
1327+void summary(void)
1328+{
1329+ time_t interval = finish - start;
1330+ struct tm *t = gmtime(&interval);
1331+ 
1332+ printf("Total inversion performed: %lu\n", total_inversions());
1333+ printf("Test Duration: %d days, %d hours, %d minutes, %d seconds\n",
1334+ t->tm_yday, t->tm_hour, t->tm_min, t->tm_sec);
1335+}
1336+ 
1337+void write_stats(FILE *f, void *data __attribute__ ((unused)))
1338+{
1339+ fprintf(f, " \"inversion\": %lu\n", total_inversions());
1340+}
1341+ 
1342+int
1343+barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *attr,
1344+ unsigned int count, const char *name)
1345+{
1346+ int status;
1347+ 
1348+ status = pthread_barrier_init(b, attr, count);
1349+ if (status != 0) {
1350+ pi_error("barrier_init: failed to initialize: %s\n", name);
1351+ pi_error("status = %d\n", status);
1352+ return FAILURE;
1353+ }
1354+ 
1355+ return SUCCESS;
1356+}
1357+ 
1358+void setup_sched_attr(struct sched_attr *attr, int policy, int prio)
1359+{
1360+ attr->sched_policy = policy;
1361+ attr->sched_priority = prio;
1362+}
1363+ 
1364+void setup_sched_config(int policy)
1365+{
1366+ int prio_min;
1367+ 
1368+ prio_min = sched_get_priority_min(policy);
1369+ 
1370+ if (!(sa_initialized & SA_INIT_LOW))
1371+ setup_sched_attr(&low_sa, policy, prio_min + 0);
1372+ if (!(sa_initialized & SA_INIT_MED))
1373+ setup_sched_attr(&med_sa, policy, prio_min + 1);
1374+ if (!(sa_initialized & SA_INIT_HIGH))
1375+ setup_sched_attr(&high_sa, policy, prio_min + 2);
1376+ if (!(sa_initialized & SA_INIT_ADMIN))
1377+ setup_sched_attr(&admin_sa, policy, prio_min + 3);
1378+}
@@ -0,0 +1,48 @@
1+.\"
2+.TH PIP\ STRESS 8 "September 17, 2018"
3+# SPDX-License-Identifier: GPL-2.0-or-later
4+.SH NAME
5+.B pip_stress \- Priority Inheritance with processes
6+.SH SYNOPSIS
7+.B pip_stress
8+ 
9+.SH DESCRIPTION
10+This program demonstrates the technique of using priority inheritance (PI)
11+mutexes with processes instead of threads.
12+The way to do this is to obtain some shared memory - in this case with
13+mmap that backs a pthread_mutex_t since this will support PI.
14+Pay particular attention to how this is intialized to support processes.
15+Function init_shared_pthread_mutex() does this by setting the
16+pthread_mutexattr to PTHREAD_PROCESS_SHARED and the mutex protocol to
17+PTHREAD_PRIO_INHERIT.
18+In this program we purposely try to invoke a classic priority inversion.
19+A low priority process grabs the mutex and does some work.
20+A high priority process comes a long and is blocked since the mutex is taken.
21+A medium priority process that doesn't require the mutex then takes the
22+processor. Because the processes are restricted to one cpu, the low priority
23+processes never makes any progress because the medium priority process
24+runs in an infinite loop. This is a priority inversion because the
25+medium priority process is running at the expensive of the high priority
26+process. However, since we have used PRIO_INHERIT and are running on a
27+machine that supports preemption, the high priority process will lend it's
28+priority to the low priority process which will preempt the medium priority
29+process. The low priority process will then release the mutex which the
30+high priority process can obtain. When the high priority process gets to run
31+it kills the medium priority process.
32+The state structure keeps track of the progress. Although this program
33+is set up to likely trigger an inversion, there is no guarantee that
34+scheduling will make that happen. After the program completes it reports
35+whether a priority inversion occurred or not. In either case this program
36+demonstrates how to use priority inheritance mutexes with processes.
37+In fact, you would be better off to avoid scenarios in which a priority
38+inversion occurs if possible - this program tries to trigger them just
39+to show that it works. If you are having difficulty triggering an inversion,
40+merely increase the time that the low priority process sleeps while
41+holding the lock. (usleep);
42+Also note that you have to run as a user with permission to change
43+scheduling priorities.
44+.BR
45+.SH AUTHOR
46+pip_stress was written by John Kacur <jkacur at redhat.com>
47+.PP
48+This manual page was also written by John Kacur
@@ -0,0 +1,368 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ Pip stress - Priority Inheritance with processes
4+ 
5+ Copyright (C) 2009, John Kacur <jkacur@redhat.com>
6+*/
7+ 
8+/*
9+ * This program demonstrates the technique of using priority inheritance (PI)
10+ * mutexes with processes instead of threads.
11+ * The way to do this is to obtain some shared memory - in this case with
12+ * mmap that backs a pthread_mutex_t since this will support PI.
13+ * Pay particular attention to how this is intialized to support processes.
14+ * Function init_shared_pthread_mutex() does this by setting the
15+ * pthread_mutexattr to PTHREAD_PROCESS_SHARED and the mutex protocol to
16+ * PTHREAD_PRIO_INHERIT.
17+ * In this program we purposely try to invoke a classic priority inversion.
18+ * A low priority process grabs the mutex and does some work.
19+ * A high priority process comes a long and is blocked since the mutex is taken.
20+ * A medium priority process that doesn't require the mutex then takes the
21+ * processor. Because the processes are restricted to one cpu, the low priority
22+ * processes never makes any progress because the medium priority process
23+ * runs in an infinite loop. This is a priority inversion because the
24+ * medium priority process is running at the expensive of the high priority
25+ * process. However, since we have used PRIO_INHERIT and are running on a
26+ * machine that supports preemption, the high priority process will lend it's
27+ * priority to the low priority process which will preempt the medium priority
28+ * process. The low priority process will then release the mutex which the
29+ * high priority process can obtain. When the high priority process gets to run
30+ * it kills the medium priority process.
31+ * The state structure keeps track of the progress. Although this program
32+ * is set up to likely trigger an inversion, there is no guarantee that
33+ * scheduling will make that happen. After the program completes it reports
34+ * whether a priority inversion occurred or not. In either case this program
35+ * demonstrates how to use priority inheritance mutexes with processes.
36+ * In fact, you would be better off to avoid scenarios in which a priority
37+ * inversion occurs if possible - this program tries to trigger them just
38+ * to show that it works. If you are having difficulty triggering an inversion,
39+ * merely increase the time that the low priority process sleeps while
40+ * holding the lock. (usleep);
41+ * Also note that you have to run as a user with permission to change
42+ * scheduling priorities.
43+ */
44+ 
45+#include "pip_stress.h"
46+ 
47+#include <unistd.h>
48+#include <getopt.h>
49+ 
50+/* default time for low priority thread usleep */
51+useconds_t usleep_val = 500;
52+ 
53+pthread_mutex_t *resource;
54+ 
55+/* This records the state to determine whether a priority inversion occurred */
56+struct State {
57+ int low_owns_resource;
58+ int high_started;
59+ int high_owns_resource;
60+ int medium_started;
61+ int inversion;
62+ pthread_mutex_t *mutex;
63+};
64+ 
65+struct State *statep;
66+ 
67+const int policy = SCHED_FIFO;
68+static int prio_min; /* Initialized for the minimum priority of policy */
69+ 
70+static void usage(int error)
71+{
72+ printf("pip_stress V %1.2f\n", VERSION);
73+ printf("Usage:\n"
74+ "pip_stress <options>\n"\
75+ "-h --help Show this help menu.\n"
76+ );
77+ exit(error);
78+}
79+ 
80+int main(int argc, char *argv[])
81+{
82+ void *mptr; /* memory pointer */
83+ pid_t pid1, pid2;
84+ cpu_set_t set, *setp = &set;
85+ int res;
86+ int *minimum_priority = (int*)&prio_min;
87+ 
88+ for (;;) {
89+ struct option long_options[] = {
90+ { "help", no_argument, NULL, 'h' },
91+ { NULL, 0, NULL, 0 },
92+ };
93+ 
94+ int c = getopt_long(argc, argv, "s:h", long_options, NULL);
95+ if (c == -1)
96+ break;
97+ switch (c) {
98+ case 'h':
99+ usage(0);
100+ break;
101+ default:
102+ usage(1);
103+ break;
104+ };
105+ }
106+ 
107+ *minimum_priority = sched_get_priority_min(policy);
108+ 
109+ if (check_privs())
110+ exit(1);
111+ 
112+ mptr = mmap_page(); /* Get a page of shared memory */
113+ resource = (pthread_mutex_t*)mptr; /* point our lock to it */
114+ mptr += sizeof(pthread_mutex_t); /* advance the memory pointer */
115+ 
116+ /* Initialize our mutex via the resource pointer */
117+ init_shared_pthread_mutex(resource, PTHREAD_PRIO_INHERIT);
118+ 
119+ statep = (struct State*)mptr;
120+ mptr += sizeof(struct State);
121+ 
122+ init_state(); /* Initialize the state structure */
123+ 
124+ statep->mutex = (pthread_mutex_t*)mptr; /* point the next lock to it */
125+ mptr += sizeof(pthread_mutex_t); /* advance the memory pointer */
126+ 
127+ /* Initialize our State mutex */
128+ init_shared_pthread_mutex(statep->mutex, PTHREAD_PRIO_NONE);
129+ 
130+ set_rt_prio(0, prio_min, policy);
131+ 
132+ /* We restrict this program to the first cpu, inorder to increase
133+ * the likelihood of a priority inversion */
134+ CPU_ZERO(setp);
135+ CPU_SET(0, setp);
136+ res = sched_setaffinity(0, sizeof(set), setp);
137+ if (res == -1) {
138+ int err = errno;
139+ err_msg("sched_setaffinity: ");
140+ err_exit(err, NULL);
141+ }
142+ 
143+ pid1 = fork();
144+ if (pid1 == -1) {
145+ perror("fork");
146+ exit(1);
147+ } else if (pid1 != 0) { /* parent code */
148+ low(pid1);
149+ } else { /* child code */
150+ pid2 = fork(); /* parent code */
151+ if (pid2 == -1) {
152+ perror("fork: ");
153+ exit(1);
154+ } else if (pid2 != 0) { /* parent code */
155+ high(pid2);
156+ } else { /* child code */
157+ medium();
158+ }
159+ }
160+ 
161+ exit(0);
162+}
163+ 
164+/* Initialize the structure that tracks when a priority inversion occurs */
165+void init_state(void)
166+{
167+ /* Init the State structure */
168+ statep->low_owns_resource = 0;
169+ statep->high_started = 0;
170+ statep->high_owns_resource = 0;
171+ statep->medium_started = 0;
172+ /* Assume an inversion will occur until proven false */
173+ statep->inversion = 1;
174+}
175+ 
176+/* @pid = high priority process pid */
177+void low(pid_t pid)
178+{
179+ int status;
180+ Pthread_mutex_lock(resource);
181+ Pthread_mutex_lock(statep->mutex);
182+ statep->low_owns_resource = 1;
183+ if (statep->high_owns_resource ||
184+ statep->medium_started) {
185+ statep->inversion = 0;
186+ }
187+ Pthread_mutex_unlock(statep->mutex);
188+ usleep(usleep_val);
189+ Pthread_mutex_unlock(resource);
190+ waitpid(pid, &status, 0);
191+}
192+ 
193+void medium(void)
194+{
195+ set_rt_prio(0, prio_min+1, policy);
196+ Pthread_mutex_lock(statep->mutex);
197+ statep->medium_started = 1;
198+ if (!statep->high_started)
199+ statep->inversion = 0;
200+ Pthread_mutex_unlock(statep->mutex);
201+ 
202+ for(;;); /* infinite loop */
203+}
204+ 
205+/* @pid = medium priority process pid */
206+void high(pid_t pid)
207+{
208+ int status;
209+ set_rt_prio(0, prio_min+2, policy);
210+ 
211+ /* Must come after raising the priority */
212+ Pthread_mutex_lock(statep->mutex);
213+ statep->high_started = 1;
214+ Pthread_mutex_unlock(statep->mutex);
215+ 
216+ Pthread_mutex_lock(resource);
217+ Pthread_mutex_lock(statep->mutex);
218+ statep->high_owns_resource = 1;
219+ if (!statep->low_owns_resource || !statep->medium_started)
220+ statep->inversion = 0;
221+ Pthread_mutex_unlock(statep->mutex);
222+ Pthread_mutex_unlock(resource);
223+ kill(pid, SIGKILL); /* kill the medium thread */
224+ waitpid(pid, &status, 0);
225+ 
226+ Pthread_mutex_lock(statep->mutex);
227+ 
228+ if (statep->inversion)
229+ printf("Successfully used priority inheritance to handle an inversion\n");
230+ else
231+ printf("No inversion incurred\n");
232+ Pthread_mutex_unlock(statep->mutex);
233+}
234+ 
235+/* mmap a page of anonymous shared memory */
236+void *mmap_page(void)
237+{
238+ void *mptr;
239+ long pgsize = sysconf(_SC_PAGE_SIZE);
240+ 
241+ mptr = mmap(NULL, pgsize, PROTRW, MMAP_FLAGS, 0, 0);
242+ if (mptr == MAP_FAILED) {
243+ perror("In function mmap_page - mmap");
244+ exit(1);
245+ }
246+ 
247+ return mptr;
248+}
249+ 
250+long process_shared_mutex_available(void)
251+{
252+ long res = -1; /* undefined */
253+#ifdef _POSIX_THREAD_PROCESS_SHARED
254+ res = sysconf(_SC_THREAD_PROCESS_SHARED);
255+ if (res == -1) {
256+ int err = errno; /* save the error number */
257+ err_msg("%s: sysconf(_SC_THREAD_PROCESS_SHARED): ", __func__);
258+ err_exit(err, NULL);
259+ }
260+#else
261+#error _POSIX_THREAD_PROCESS_SHARED is not defined
262+#endif
263+ return res;
264+}
265+ 
266+void Pthread_mutexattr_init(pthread_mutexattr_t *attr)
267+{
268+ int err;
269+ err = pthread_mutexattr_init(attr);
270+ if (err) {
271+ err_msg("%s: pthread_mutexattr_init(): ", __func__);
272+ err_exit(err, NULL);
273+ }
274+}
275+ 
276+void Pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)
277+{
278+ int err;
279+ err = pthread_mutexattr_setpshared(attr, pshared);
280+ if (err) {
281+ err_msg("%s: pthread_mutexattr_setpshared(): ", __func__);
282+ err_exit(err, NULL);
283+ }
284+}
285+ 
286+void Pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol)
287+{
288+ int err;
289+ err = pthread_mutexattr_setprotocol(attr, protocol);
290+ if (err) {
291+ err_msg("%s: pthread_mutexattr_setprotocol(): ", __func__);
292+ err_exit(err, NULL);
293+ }
294+}
295+ 
296+void Pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
297+{
298+ int err;
299+ err = pthread_mutex_init(mutex, attr);
300+ if (err) {
301+ err_msg("%s: pthread_mutex_init(): ", __func__);
302+ err_exit(err, NULL);
303+ }
304+}
305+ 
306+void Pthread_mutex_lock(pthread_mutex_t *mutex)
307+{
308+ int err;
309+ err = pthread_mutex_lock(mutex);
310+ if (err) {
311+ err_msg("%s: pthread_mutex_lock(): ", __func__);
312+ err_exit(err, NULL);
313+ }
314+}
315+ 
316+void Pthread_mutex_unlock(pthread_mutex_t *mutex)
317+{
318+ int err;
319+ err = pthread_mutex_unlock(mutex);
320+ if (err) {
321+ err_msg("%s: pthread_mutex_unlock(): ", __func__);
322+ err_exit(err, NULL);
323+ }
324+}
325+ 
326+void init_shared_pthread_mutex(pthread_mutex_t *mutex, int protocol)
327+{
328+ pthread_mutexattr_t attr;
329+ 
330+ process_shared_mutex_available();
331+ 
332+ Pthread_mutexattr_init(&attr);
333+ Pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
334+ Pthread_mutexattr_setprotocol(&attr, protocol);
335+ 
336+ Pthread_mutex_init(mutex, &attr);
337+}
338+ 
339+/* Set the priority and policy of a process */
340+int set_rt_prio(pid_t pid, int prio, int policy)
341+{
342+ int err;
343+ struct sched_param param;
344+ struct sched_param *pparam = &param;
345+ pparam->sched_priority = prio;
346+ err = sched_setscheduler(pid, policy, pparam);
347+ if (err) {
348+ err = errno; /* save the errno */
349+ err_msg_n(err, "%s: sched_setscheduler(): ", __func__);
350+ err_msg("%s: prio = %d\n", __func__, prio);
351+ err_msg("%s: pparam->sched_priority = %d\n", __func__, pparam->sched_priority);
352+ err_msg("%s: policy = %d\n", __func__, policy);
353+ }
354+ return err; /* 0 on success */
355+}
356+ 
357+int get_rt_prio(pid_t pid)
358+{
359+ int err;
360+ struct sched_param param;
361+ err = sched_getparam(pid, &param);
362+ if (err) {
363+ err = errno; /* save the errno */
364+ err_msg_n(err, "%s: get_rt_prio(): ", __func__);
365+ return -1;
366+ }
367+ return param.sched_priority;
368+}
@@ -0,0 +1,128 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * sigtest - simple little program to verify signal behavior
4+ * Copyright (C) 2006, 2007 Clark Williams <williams@redhat.com>
5+*/
6+ 
7+#include <stdio.h>
8+#include <signal.h>
9+#include <time.h>
10+#include <memory.h>
11+#include <errno.h>
12+#include <pthread.h>
13+ 
14+#define TIMER_SIGNAL (SIGRTMIN+1)
15+#define SUCCESS 0
16+#define FAILURE -1
17+ 
18+int
19+setup_timer(timer_t *timer)
20+{
21+ int status;
22+ struct sigevent sigev;
23+ 
24+ memset(&sigev, 0, sizeof(sigev));
25+ sigev.sigev_notify = SIGEV_SIGNAL;
26+ sigev.sigev_signo = TIMER_SIGNAL;
27+ status = timer_create(CLOCK_MONOTONIC, &sigev, timer);
28+ if (status) {
29+ fprintf(stderr,"error from timer_create: %s\n", strerror(errno));
30+ return FAILURE;
31+ }
32+ return SUCCESS;
33+}
34+
35+int
36+start_timer(timer_t t, int sec, int nsec)
37+{
38+ int status;
39+ struct itimerspec it;
40+ 
41+ // set up as a one-shot
42+ memset(&it, 0, sizeof(it));
43+ it.it_value.tv_sec = sec;
44+ it.it_value.tv_nsec = nsec;
45+ status = timer_settime(t, 0, &it, NULL);
46+ if (status)
47+ fprintf(stderr,"starting timer: %s\n", strerror(errno));
48+ return status;
49+}
50+ 
51+int
52+wait_for_signal(void)
53+{
54+ int signo;
55+ sigset_t sigset;
56+ 
57+ if (sigemptyset(&sigset)) {
58+ fprintf(stderr,"creating empty signal wait set: %s\n", strerror(errno));
59+ return -1;
60+ }
61+ if (sigaddset(&sigset, SIGINT)) {
62+ fprintf(stderr,"adding SIGINT to signal set: %s\n", strerror(errno));
63+ return -1;
64+ }
65+ if (sigaddset(&sigset, TIMER_SIGNAL)) {
66+ fprintf(stderr,"adding TIMER_SIGNAL to signal set: %s\n", strerror(errno));
67+ return -1;
68+ }
69+
70+ if (sigwait(&sigset, &signo)) {
71+ fprintf(stderr,"waiting for signal: %s\n", strerror(errno));
72+ return -1;
73+ }
74+ return signo;
75+}
76+ 
77+int
78+block_signals(void)
79+{
80+ int status;
81+ sigset_t sigset;
82+ 
83+ // mask off all signals
84+ status = sigfillset(&sigset);
85+ if (status) {
86+ fprintf(stderr,"setting up full signal set %d\n", status);
87+ return FAILURE;
88+ }
89+ status = pthread_sigmask(SIG_BLOCK, &sigset, NULL);
90+ if (status) {
91+ fprintf(stderr,"setting signal mask: %d\n", status);
92+ return FAILURE;
93+ }
94+ return SUCCESS;
95+}
96+ 
97+int
98+main(int argc, char **argv)
99+{
100+ int status;
101+ timer_t timer;
102+ unsigned long count = 0;
103+ int stop_test = 0;
104+ 
105+ block_signals();
106+ setup_timer(&timer);
107+ printf("Press Ctrl-C to stop\n");
108+ while (stop_test == 0) {
109+ if (start_timer(timer, 0, 500000000)) {
110+ stop_test = 1;
111+ continue;
112+ }
113+ status = wait_for_signal();
114+ if (status == SIGINT) {
115+ fputs("\033[1B", stdout);
116+ stop_test = 1;
117+ }
118+ else if (status == TIMER_SIGNAL) {
119+ printf("count: %lu\n", ++count);
120+ fputs("\033[1A", stdout);
121+ }
122+ else {
123+ fprintf(stderr, "WTF?\n");
124+ return -1;
125+ }
126+ }
127+ return 0;
128+}
@@ -0,0 +1,665 @@
1+// SPDX-License-Identifier: LGPL-2.1-or-later
2+/*
3+ * Classic Priority Inversion deadlock test case
4+ * Copyright (C) 2006 Free Software Foundation, Inc.
5+ * This file is part of the GNU C Library.
6+ * Contributed by Clark Williams<williams@redhat.com>, 2006
7+ */
8+ 
9+/* This program tests Priority Inheritance mutexes and their ability
10+ to avoid Priority Inversion deadlocks
11+
12+ The basic premise here is to set up a deadlock scenario and confirm that PI
13+ mutexes resolve the situation. Three worker threads will be created from the
14+ main thread: low, medium and high priority threads that use SCHED_FIFO as
15+ their scheduling policy. The low priority thread claims a mutex and then
16+ starts "working". The medium priority thread starts and preempts the low
17+ priority thread. Then the high priority thread runs and attempts to claim
18+ the mutex owned by the low priority thread. Without priority inheritance,
19+ this will deadlock the program. With priority inheritance, the low priority
20+ thread receives a priority boost, finishes it's "work" and releases the mutex,
21+ which allows the high priority thread to run and finish and then the medium
22+ priority thread finishes.
23+
24+ That's the theory, anyway...
25+
26+ CW - 2006 */
27+ 
28+#include <stdio.h>
29+#include <errno.h>
30+#include <stdlib.h>
31+#include <stdarg.h>
32+#include <pthread.h>
33+#include <sched.h>
34+#include <unistd.h>
35+#include <string.h>
36+#include <signal.h>
37+ 
38+/* test timeout */
39+#define TIMEOUT 2
40+ 
41+/* determine if the C library supports Priority Inheritance mutexes */
42+#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT != -1
43+#define HAVE_PI_MUTEX 1
44+#else
45+#define HAVE_PI_MUTEX 0
46+#endif
47+ 
48+int use_pi_mutex = HAVE_PI_MUTEX;
49+ 
50+#define SUCCESS 0
51+#define FAILURE 1
52+ 
53+/* the number of times we cause a priority inversion situation */
54+int inversions = 1;
55+ 
56+/* the file handle used by the error reporting routine */
57+FILE *errout;
58+ 
59+#define MAIN do_test
60+#define TEST_FUNCTION do_test(argc, argv)
61+#define CMDLINE_OPTIONS { "verbose", no_argument, NULL, 1002 }, \
62+ { "no-pi", no_argument, NULL, 1003},
63+#define CMDLINE_PROCESS \
64+ case 1002: verbose=1; break; \
65+ case 1003: use_pi_mutex = 0; break;
66+#define CLEANUP_HANDLER cleanup()
67+int verbose = 0;
68+ 
69+/* define priorities for the threads */
70+#define SKEL_PRIO(x) (x)
71+#define MAIN_PRIO(x) (x - 1)
72+#define HIGH_PRIO(x) (x - 2)
73+#define MED_PRIO(x) (x - 3)
74+#define LOW_PRIO(x) (x - 4)
75+ 
76+enum thread_names
77+{ LOW = 0, MEDIUM, HIGH, NUM_WORKER_THREADS };
78+ 
79+pthread_mutex_t mutex;
80+pthread_mutexattr_t mutex_attr;
81+ 
82+pthread_barrier_t all_threads_ready;
83+pthread_barrier_t all_threads_done;
84+ 
85+/* state barriers */
86+pthread_barrier_t start_barrier;
87+pthread_barrier_t locked_barrier;
88+pthread_barrier_t elevate_barrier;
89+pthread_barrier_t finish_barrier;
90+ 
91+volatile int deadlocked = 0;
92+volatile int high_has_run = 0;
93+volatile int low_unlocked = 0;
94+ 
95+cpu_set_t cpu_mask;
96+ 
97+struct thread_parameters
98+{
99+ pthread_t tid;
100+ int inversions;
101+} thread_parameters[NUM_WORKER_THREADS];
102+ 
103+/* forward prototypes */
104+void *low_priority (void *arg);
105+void *med_priority (void *arg);
106+void *high_priority (void *arg);
107+int setup_thread_attr (pthread_attr_t * attr, int prio, cpu_set_t * mask);
108+int set_cpu_affinity (cpu_set_t * mask);
109+void error (char *, ...);
110+void info (char *, ...);
111+ 
112+void
113+prepare (int argc, char **argv)
114+{
115+ struct sched_param thread_param;
116+ int max = sched_get_priority_max (SCHED_FIFO);
117+ int status;
118+ 
119+ errout = stdout;
120+ 
121+ /* boost test skeleton to max priority (so we keep running) :) */
122+ thread_param.sched_priority = SKEL_PRIO (max);
123+ status = pthread_setschedparam (pthread_self (), SCHED_FIFO, &thread_param);
124+ if (status)
125+ error ("main: boosting to max priority: 0x%x\n", status);
126+}
127+ 
128+#define PREPARE prepare
129+ 
130+int
131+initialize_barriers (void)
132+{
133+ int status;
134+ 
135+ status =
136+ pthread_barrier_init (&all_threads_ready, NULL, NUM_WORKER_THREADS + 1);
137+ if (status)
138+ {
139+ error ("initialize_barriers: failed to initialize all_threads_ready\n");
140+ return FAILURE;
141+ }
142+ status =
143+ pthread_barrier_init (&all_threads_done, NULL, NUM_WORKER_THREADS + 1);
144+ if (status)
145+ {
146+ error ("initialize_barriers: failed to initialize all_threads_done\n");
147+ return FAILURE;
148+ }
149+ status = pthread_barrier_init (&start_barrier, NULL, NUM_WORKER_THREADS);
150+ if (status)
151+ {
152+ error ("initialize_barriers: failed to initialize start_barrier\n");
153+ return FAILURE;
154+ }
155+ status = pthread_barrier_init (&locked_barrier, NULL, 2);
156+ if (status)
157+ {
158+ error ("initializing_barriers: failed to intialize locked_barrier\n");
159+ return FAILURE;
160+ }
161+ status = pthread_barrier_init (&elevate_barrier, NULL, 2);
162+ if (status)
163+ {
164+ error ("initializing_barriers: failed to initialize elevate_barrier\n");
165+ return FAILURE;
166+ }
167+ status = pthread_barrier_init (&finish_barrier, NULL, NUM_WORKER_THREADS);
168+ if (status)
169+ {
170+ error ("initializing_barriers: failed to initialize finish_barrier\n");
171+ return FAILURE;
172+ }
173+ return SUCCESS;
174+}
175+ 
176+void
177+cleanup (void)
178+{
179+ int i;
180+ int status;
181+ for (i = 0; i < NUM_WORKER_THREADS; i++)
182+ {
183+ status = pthread_kill (thread_parameters[i].tid, SIGQUIT);
184+ if (status)
185+ error ("cleanup: error sending SIGQUIT to thread %d\n",
186+ thread_parameters[i].tid);
187+ }
188+}
189+ 
190+void
191+handler (int signal)
192+{
193+ info ("handler: %s fired\n", sys_siglist[signal]);
194+ cleanup ();
195+ if (signal == SIGALRM)
196+ {
197+ error ("handler: DEADLOCK detected!\n");
198+ deadlocked = 1;
199+ }
200+}
201+ 
202+int
203+MAIN (int argc, char **argv)
204+{
205+ int status;
206+ int prio_max;
207+ pthread_attr_t thread_attr;
208+ struct sched_param thread_param;
209+ 
210+ errout = stdout;
211+ 
212+ /* initialize default attributes for the mutex */
213+ status = pthread_mutexattr_init (&mutex_attr);
214+ if (status)
215+ {
216+ error ("main: initializing mutex attribute: 0x%x\n", status);
217+ return FAILURE;
218+ }
219+ 
220+ if (use_pi_mutex)
221+ {
222+ /* set priority inheritance attribute for mutex */
223+ status = pthread_mutexattr_setprotocol (&mutex_attr,
224+ PTHREAD_PRIO_INHERIT);
225+ if (status)
226+ {
227+ error ("main: setting mutex attribute policy: 0x%x\n", status);
228+ return FAILURE;
229+ }
230+ }
231+ info ("main: Priority Inheritance turned %s\n",
232+ use_pi_mutex ? "on" : "off");
233+ 
234+ /* initialize our mutex */
235+ status = pthread_mutex_init (&mutex, &mutex_attr);
236+ if (status)
237+ {
238+ error ("main: initializing mutex: 0x%x\n", status);
239+ return FAILURE;
240+ }
241+ 
242+ /* set up our barriers */
243+ status = initialize_barriers ();
244+ if (status)
245+ return FAILURE;
246+ 
247+ /* set up CPU affinity so we only use one processor */
248+ if (set_cpu_affinity (&cpu_mask))
249+ return FAILURE;
250+ 
251+ /* boost us to max priority (so we keep running) :) */
252+ prio_max = sched_get_priority_max (SCHED_FIFO);
253+ thread_param.sched_priority = MAIN_PRIO (prio_max);
254+ status = pthread_setschedparam (pthread_self (), SCHED_FIFO, &thread_param);
255+ if (status)
256+ {
257+ error ("main: boosting to max priority: 0x%x\n", status);
258+ /* Don't fail if we don't have the right privledges */
259+ return SUCCESS;
260+ }
261+ 
262+ /* start the low priority thread */
263+ info ("main: creating low priority thread\n");
264+ setup_thread_attr (&thread_attr, LOW_PRIO (prio_max), &cpu_mask);
265+ thread_parameters[LOW].inversions = inversions;
266+ status = pthread_create (&thread_parameters[LOW].tid,
267+ &thread_attr,
268+ low_priority, &thread_parameters[LOW]);
269+ if (status != 0)
270+ {
271+ error ("main: creating low_priority thread: 0x%x\n", status);
272+ return FAILURE;
273+ }
274+ 
275+ /* create the medium priority thread */
276+ info ("main: creating medium priority thread\n");
277+ setup_thread_attr (&thread_attr, MED_PRIO (prio_max), &cpu_mask);
278+ thread_parameters[MEDIUM].inversions = inversions;
279+ status = pthread_create (&thread_parameters[MEDIUM].tid,
280+ &thread_attr,
281+ med_priority, &thread_parameters[MEDIUM]);
282+ if (status != 0)
283+ {
284+ error ("main: creating med_priority thread: 0x%x\n", status);
285+ return FAILURE;
286+ }
287+ 
288+ /* create the high priority thread */
289+ info ("main: creating high priority thread\n");
290+ if (setup_thread_attr (&thread_attr, HIGH_PRIO (prio_max), &cpu_mask))
291+ return FAILURE;
292+ thread_parameters[HIGH].inversions = inversions;
293+ status = pthread_create (&thread_parameters[HIGH].tid,
294+ &thread_attr,
295+ high_priority, &thread_parameters[HIGH]);
296+ if (status != 0)
297+ {
298+ error ("main: creating high_priority thread: 0x%x\n", status);
299+ cleanup ();
300+ return FAILURE;
301+ }
302+ 
303+ signal (SIGINT, handler);
304+ 
305+ info ("main: releasing all threads\n");
306+ status = pthread_barrier_wait (&all_threads_ready);
307+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
308+ {
309+ error ("main: pthread_barrier_wait(all_threads_ready): 0x%x\n", status);
310+ cleanup ();
311+ return FAILURE;
312+ }
313+ info ("main: all threads initialized\n");
314+ 
315+ info ("main: waiting for threads to finish\n");
316+ 
317+ status = pthread_barrier_wait (&all_threads_done);
318+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
319+ {
320+ error ("main: pthread_barrier_wait(all_threads_done): 0x%x\n", status);
321+ cleanup ();
322+ return FAILURE;
323+ }
324+ info ("main: all threads terminated!\n");
325+ if (deadlocked)
326+ {
327+ info ("main: test failed\n");
328+ return FAILURE;
329+ }
330+ info ("main: test passed\n");
331+ return SUCCESS;
332+}
333+ 
334+ 
335+int
336+setup_thread_attr (pthread_attr_t * attr, int prio, cpu_set_t * mask)
337+{
338+ int status;
339+ struct sched_param thread_param;
340+ 
341+ status = pthread_attr_init (attr);
342+ if (status)
343+ {
344+ error ("setup_thread_attr: initializing thread attribute: 0x%x\n",
345+ status);
346+ return FAILURE;
347+ }
348+ status = pthread_attr_setschedpolicy (attr, SCHED_FIFO);
349+ if (status)
350+ {
351+ error
352+ ("setup_thread_attr: setting attribute policy to SCHED_FIFO: 0x%x\n",
353+ status);
354+ return FAILURE;
355+ }
356+ status = pthread_attr_setinheritsched (attr, PTHREAD_EXPLICIT_SCHED);
357+ if (status)
358+ {
359+ error
360+ ("setup_thread_attr: setting explicit scheduling inheritance: 0x%x\n",
361+ status);
362+ return FAILURE;
363+ }
364+ thread_param.sched_priority = prio;
365+ status = pthread_attr_setschedparam (attr, &thread_param);
366+ if (status)
367+ {
368+ error ("setup_thread_attr: setting scheduler param: 0x%x\n", status);
369+ return FAILURE;
370+ }
371+ status = pthread_attr_setaffinity_np (attr, sizeof (cpu_set_t), mask);
372+ if (status)
373+ {
374+ error ("setup_thread_attr: setting affinity attribute: 0x%x\n", status);
375+ return FAILURE;
376+ }
377+ return SUCCESS;
378+}
379+ 
380+int
381+set_cpu_affinity (cpu_set_t * cpu_set)
382+{
383+ int status, i;
384+ cpu_set_t current_mask, new_mask;
385+ 
386+ /* Now set our CPU affinity to only run one one processor */
387+ status = sched_getaffinity (0, sizeof (cpu_set_t), &current_mask);
388+ if (status)
389+ {
390+ error ("set_cpu_affinity: getting CPU affinity mask: 0x%x\n", status);
391+ return FAILURE;
392+ }
393+ for (i = 0; i < sizeof (cpu_set_t) * 8; i++)
394+ {
395+ if (CPU_ISSET (i, &current_mask))
396+ break;
397+ }
398+ if (i >= sizeof (cpu_set_t) * 8)
399+ {
400+ error ("set_cpu_affinity: No schedulable CPU found!\n");
401+ return FAILURE;
402+ }
403+ CPU_ZERO (&new_mask);
404+ CPU_SET (i, &new_mask);
405+ status = sched_setaffinity (0, sizeof (cpu_set_t), &new_mask);
406+ if (status)
407+ {
408+ error ("set_cpu_affinity: setting CPU affinity mask: 0x%x\n", status);
409+ return FAILURE;
410+ }
411+ info ("set_cpu_affinity: using processr %d\n", i);
412+ *cpu_set = new_mask;
413+ return SUCCESS;
414+}
415+ 
416+void
417+report_threadinfo (char *name)
418+{
419+ int status;
420+ struct sched_param thread_param;
421+ int thread_policy;
422+ 
423+ status =
424+ pthread_getschedparam (pthread_self (), &thread_policy, &thread_param);
425+ if (status)
426+ {
427+ error ("report_threadinfo: failed to get scheduler param: 0x%x\n",
428+ status);
429+ pthread_mutex_unlock (&mutex);
430+ exit (FAILURE);
431+ }
432+ info ("%s: running as %s thread at priority %d\n",
433+ name, thread_policy == SCHED_FIFO ? "FIFO" :
434+ thread_policy == SCHED_RR ? "RR" : "OTHER",
435+ thread_param.sched_priority);
436+}
437+ 
438+void *
439+low_priority (void *arg)
440+{
441+ int status;
442+ struct thread_parameters *p = (struct thread_parameters *) arg;
443+ 
444+ report_threadinfo ("low_priority");
445+ 
446+ info ("low_priority: entering ready state\n");
447+ 
448+ /* wait for all threads to be ready */
449+ status = pthread_barrier_wait (&all_threads_ready);
450+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
451+ {
452+ error ("low_priority: pthread_barrier_wait(all_threads_ready): %x",
453+ status);
454+ return NULL;
455+ }
456+ 
457+ info ("low_priority: starting inversion loop (%d)\n", p->inversions);
458+ while (p->inversions-- > 0)
459+ {
460+ /* initial state */
461+ info ("low_priority: entering start wait (%d)\n", p->inversions + 1);
462+ status = pthread_barrier_wait (&start_barrier);
463+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
464+ {
465+ error ("low_priority: pthread_barrier_wait(start): %x\n", status);
466+ return NULL;
467+ }
468+ info ("low_priority: claiming mutex\n");
469+ pthread_mutex_lock (&mutex);
470+ info ("low_priority: mutex locked\n");
471+ 
472+ info ("low_priority: entering locked wait\n");
473+ status = pthread_barrier_wait (&locked_barrier);
474+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
475+ {
476+ error ("low_priority: pthread_barrier_wait(locked): %x\n", status);
477+ return NULL;
478+ }
479+ 
480+ /* wait for priority boost */
481+ info ("low_priority: entering elevated wait\n");
482+ low_unlocked = 0; /* prevent race with med_priority */
483+ status = pthread_barrier_wait (&elevate_barrier);
484+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
485+ {
486+ error ("low_priority: pthread_barrier_wait(elevate): %x\n", status);
487+ return NULL;
488+ }
489+ low_unlocked = 1;
490+ 
491+ /* release the mutex */
492+ info ("low_priority: unlocking mutex\n");
493+ pthread_mutex_unlock (&mutex);
494+ 
495+ /* finish state */
496+ info ("low_priority: entering finish wait\n");
497+ status = pthread_barrier_wait (&finish_barrier);
498+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
499+ {
500+ error ("low_priority: pthread_barrier_wait(elevate): %x\n", status);
501+ return NULL;
502+ }
503+ 
504+ }
505+ /* let main know we're done */
506+ info ("low_priority: entering exit state\n");
507+ status = pthread_barrier_wait (&all_threads_done);
508+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
509+ {
510+ error ("low_priority: pthread_barrier_wait(all_threads_done): %x",
511+ status);
512+ return NULL;
513+ }
514+ info ("low_priority: exiting\n");
515+ return NULL;
516+}
517+ 
518+void *
519+med_priority (void *arg)
520+{
521+ int status;
522+ struct thread_parameters *p = (struct thread_parameters *) arg;
523+ 
524+ report_threadinfo ("med_priority");
525+ 
526+ info ("med_priority: entering ready state\n");
527+ /* wait for all threads to be ready */
528+ status = pthread_barrier_wait (&all_threads_ready);
529+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
530+ {
531+ error ("med_priority: pthread_barrier_wait(all_threads_ready): %x",
532+ status);
533+ return NULL;
534+ }
535+ 
536+ info ("med_priority: starting inversion loop (%d)\n", p->inversions);
537+ while (p->inversions-- > 0)
538+ {
539+ /* start state */
540+ info ("med_priority: entering start state (%d)\n", p->inversions + 1);
541+ status = pthread_barrier_wait (&start_barrier);
542+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
543+ {
544+ error ("med_priority: pthread_barrier_wait(start): %x", status);
545+ return NULL;
546+ }
547+ info ("med_priority: entering elevate state\n");
548+ do
549+ {
550+ status = pthread_barrier_wait (&elevate_barrier);
551+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
552+ {
553+ error ("med_priority: pthread_barrier_wait(elevate): %x",
554+ status);
555+ return NULL;
556+ }
557+ }
558+ while (!high_has_run && !low_unlocked);
559+ info ("med_priority: entering finish state\n");
560+ status = pthread_barrier_wait (&finish_barrier);
561+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
562+ {
563+ error ("med_priority: pthread_barrier_wait(finished): %x", status);
564+ return NULL;
565+ }
566+ }
567+ 
568+ info ("med_priority: entering exit state\n");
569+ status = pthread_barrier_wait (&all_threads_done);
570+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
571+ {
572+ error ("med_priority: pthread_barrier_wait(all_threads_done): %x",
573+ status);
574+ return NULL;
575+ }
576+ info ("med_priority: exiting\n");
577+ return NULL;
578+}
579+ 
580+void *
581+high_priority (void *arg)
582+{
583+ int status;
584+ struct thread_parameters *p = (struct thread_parameters *) arg;
585+ 
586+ report_threadinfo ("high_priority");
587+ 
588+ info ("high_priority: entering ready state\n");
589+ 
590+ /* wait for all threads to be ready */
591+ status = pthread_barrier_wait (&all_threads_ready);
592+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
593+ {
594+ error ("high_priority: pthread_barrier_wait(all_threads_ready): %x",
595+ status);
596+ return NULL;
597+ }
598+ 
599+ info ("high_priority: starting inversion loop (%d)\n", p->inversions);
600+ while (p->inversions-- > 0)
601+ {
602+ high_has_run = 0;
603+ info ("high_priority: entering start state (%d)\n", p->inversions + 1);
604+ status = pthread_barrier_wait (&start_barrier);
605+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
606+ {
607+ error ("high_priority: pthread_barrier_wait(start): %x", status);
608+ return NULL;
609+ }
610+ info ("high_priority: entering running state\n");
611+ status = pthread_barrier_wait (&locked_barrier);
612+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
613+ {
614+ error ("high_priority: pthread_barrier_wait(running): %x", status);
615+ return NULL;
616+ }
617+ info ("high_priority: locking mutex\n");
618+ pthread_mutex_lock (&mutex);
619+ info ("high_priority: got mutex\n");
620+ high_has_run = 1;
621+ info ("high_priority: unlocking mutex\n");
622+ pthread_mutex_unlock (&mutex);
623+ info ("high_priority: entering finish state\n");
624+ status = pthread_barrier_wait (&finish_barrier);
625+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
626+ {
627+ error ("high_priority: pthread_barrier_wait(finish): %x", status);
628+ return NULL;
629+ }
630+ }
631+ 
632+ info ("high_priority: entering exit state\n");
633+ status = pthread_barrier_wait (&all_threads_done);
634+ if (status && status != PTHREAD_BARRIER_SERIAL_THREAD)
635+ {
636+ error ("high_priority: pthread_barrier_wait(all_threads_done): %x",
637+ status);
638+ return NULL;
639+ }
640+ info ("high_priority: exiting\n");
641+ return NULL;
642+}
643+ 
644+void
645+error (char *fmt, ...)
646+{
647+ va_list ap;
648+ fputs ("ERROR: ", errout);
649+ va_start (ap, fmt);
650+ vfprintf (errout, fmt, ap);
651+ va_end (ap);
652+}
653+ 
654+void
655+info (char *fmt, ...)
656+{
657+ if (verbose)
658+ {
659+ va_list ap;
660+ va_start (ap, fmt);
661+ vprintf (fmt, ap);
662+ va_end (ap);
663+ }
664+}
665+#include "../test-skeleton.c"
@@ -0,0 +1,86 @@
1+.TH "pmqtest" "8" "0.1" "" ""
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+.SH "NAME"
4+.LP
5+\fBpmqtest\fR \- Start pairs of threads and measure the latency of interprocess communication with POSIX messages queues
6+.SH "SYNTAX"
7+.LP
8+pmqtest [-a|-a PROC] [-b USEC] [-d DIST] [-D TIME] [-f TO] [-h] [-i INTV] [--json FILENAME] [-l LOOPS] [-p PRIO] [-q] [-S] [-t|-t NUM]
9+.br
10+.SH "DESCRIPTION"
11+.LP
12+The program \fBpmqtest\fR starts pairs of threads that are synchronized via mq_send/mw_receive() and measures the latency between sending and receiving the message.
13+.SH "OPTIONS"
14+.TP
15+.B \-a, \-\-affinity[=PROC]
16+Run on processor number PROC. If PROC is not specified, run on current processor.
17+.TP
18+.B \-b, \-\-breaktrace=USEC
19+Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch.
20+It is useful to track down unexpected large latencies of a system.
21+.TP
22+.B \-d, \-\-distance=DIST
23+Set the distance of thread intervals in microseconds (default is 500 us). When pmqtest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST
24+.TP
25+.B \-D, \-\-duration=TIME
26+Specify a length for the test run.
27+.br
28+Append 'm', 'h', or 'd' to specify minutes, hours or days.
29+.TP
30+.B \-h, \-\-help
31+Print help message
32+.TP
33+.B \-f, \-\-forcetimeout=TO
34+Set an artificial delay of the send function to force timeout of the receiver, requires the -T option
35+.TP
36+.B \-i, \-\-interval=INTV
37+Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d.
38+.TP
39+.B \-\-json=FILENAME
40+Write final results into FILENAME, JSON formatted.
41+.TP
42+.B \-l, \-\-loops=LOOPS
43+Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. pmqtest is stopped once the number of timer intervals has been reached.
44+.TP
45+.B \-p, \-\-prio=PRIO
46+Set the priority of the process.
47+.TP
48+.B \-q, \-\-quiet
49+Print a summary only on exit. Useful for automated tests, where only the summary output needs to be captured.
50+.TP
51+.B \-S, \-\-smp
52+Test mode for symmetric multi-processing, implies -a and -t and uses the same priority on all threads.
53+.TP
54+.B \-t, \-\-threads[=NUM]
55+Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specified, NUM is set to the number of available CPUs.
56+.TP
57+.B \-T, \-\-timeout=TO
58+Use mq_timedreceive() instead of mq_receive() and specify timeout TO in seconds.
59+.SH "EXAMPLES"
60+The following example was running on an 8-way processor:
61+.LP
62+.nf
63+# pmqtest -Sp99 -i100 -d0
64+#0: ID10047, P99, CPU0, I100; #1: ID10048, P99, CPU0, Cycles 153695
65+#2: ID10049, P99, CPU1, I100; #3: ID10050, P99, CPU1, Cycles 154211
66+#4: ID10051, P99, CPU2, I100; #5: ID10052, P99, CPU2, Cycles 156823
67+#6: ID10053, P99, CPU3, I100; #7: ID10054, P99, CPU3, Cycles 158202
68+#8: ID10055, P99, CPU4, I100; #9: ID10056, P99, CPU4, Cycles 153399
69+#10: ID10057, P99, CPU5, I100; #11: ID10058, P99, CPU5, Cycles 153992
70+#12: ID10059, P99, CPU6, I100; #13: ID10060, P99, CPU6, Cycles 156576
71+#14: ID10061, P99, CPU7, I100; #15: ID10062, P99, CPU7, Cycles 157957
72+#1 -> #0, Min 1, Cur 8, Avg 5, Max 18
73+#3 -> #2, Min 1, Cur 4, Avg 5, Max 18
74+#5 -> #4, Min 1, Cur 5, Avg 5, Max 19
75+#7 -> #6, Min 1, Cur 4, Avg 4, Max 17
76+#9 -> #8, Min 1, Cur 9, Avg 5, Max 18
77+#11 -> #10, Min 1, Cur 8, Avg 5, Max 18
78+#13 -> #12, Min 1, Cur 4, Avg 5, Max 29
79+#15 -> #14, Min 1, Cur 8, Avg 4, Max 17
80+.fi
81+.SH "AUTHORS"
82+.LP
83+Carsten Emde <C.Emde@osadl.org>
84+.SH "SEE ALSO"
85+.LP
86+mq_send(3p), mq_receive(3p)
@@ -0,0 +1,660 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * pmqtest.c
4+ *
5+ * Copyright (C) 2009 Carsten Emde <C.Emde@osadl.org>
6+ */
7+ 
8+#include <stdio.h>
9+#include <stdlib.h>
10+#include <unistd.h>
11+#include <limits.h>
12+#include <fcntl.h>
13+#include <getopt.h>
14+#include <signal.h>
15+#include <string.h>
16+#include <time.h>
17+#include <sys/types.h>
18+#include <sys/stat.h>
19+#include <sys/time.h>
20+#include <sys/mman.h>
21+#include <linux/unistd.h>
22+#include <utmpx.h>
23+#include <mqueue.h>
24+#include <pthread.h>
25+#include <inttypes.h>
26+ 
27+#include "rt-utils.h"
28+#include "rt-get_cpu.h"
29+#include "rt-error.h"
30+ 
31+#define SYNCMQ_NAME "/syncmsg%d"
32+#define TESTMQ_NAME "/testmsg%d"
33+#define MSG_SIZE 8
34+ 
35+char *syncmsg = "Syncing";
36+char *testmsg = "Testing";
37+ 
38+enum {
39+ AFFINITY_UNSPECIFIED,
40+ AFFINITY_SPECIFIED,
41+ AFFINITY_USEALL
42+};
43+ 
44+struct params {
45+ int num;
46+ int cpu;
47+ int priority;
48+ int affinity;
49+ int sender;
50+ int samples;
51+ int max_cycles;
52+ int tracelimit;
53+ int tid;
54+ int shutdown;
55+ int stopped;
56+ struct timespec delay;
57+ unsigned int mindiff, maxdiff;
58+ double sumdiff;
59+ struct timeval sent, received, diff;
60+ pthread_t threadid;
61+ int timeout;
62+ int forcetimeout;
63+ int timeoutcount;
64+ mqd_t syncmq, testmq;
65+ char recvsyncmsg[MSG_SIZE];
66+ char recvtestmsg[MSG_SIZE];
67+ struct params *neighbor;
68+};
69+ 
70+void *pmqthread(void *param)
71+{
72+ int mustgetcpu = 0;
73+ struct params *par = param;
74+ cpu_set_t mask;
75+ int policy = SCHED_FIFO;
76+ struct sched_param schedp;
77+ struct timespec ts;
78+ 
79+ memset(&schedp, 0, sizeof(schedp));
80+ schedp.sched_priority = par->priority;
81+ sched_setscheduler(0, policy, &schedp);
82+ 
83+ if (par->cpu != -1) {
84+ CPU_ZERO(&mask);
85+ CPU_SET(par->cpu, &mask);
86+ if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
87+ fprintf(stderr, "WARNING: Could not set CPU affinity "
88+ "to CPU #%d\n", par->cpu);
89+ } else
90+ mustgetcpu = 1;
91+ 
92+ par->tid = gettid();
93+ 
94+ while (!par->shutdown) {
95+ if (par->sender) {
96+ 
97+ /* Optionally force receiver timeout */
98+ if (par->forcetimeout) {
99+ struct timespec senddelay;
100+ 
101+ senddelay.tv_sec = par->forcetimeout;
102+ senddelay.tv_nsec = 0;
103+ clock_nanosleep(CLOCK_MONOTONIC, 0, &senddelay, NULL);
104+ }
105+ 
106+ /* Send message: Start of latency measurement ... */
107+ gettimeofday(&par->sent, NULL);
108+ if (mq_send(par->testmq, testmsg, strlen(testmsg), 1) != 0) {
109+ fprintf(stderr, "could not send test message\n");
110+ par->shutdown = 1;
111+ }
112+ par->samples++;
113+ if (par->max_cycles && par->samples >= par->max_cycles)
114+ par->shutdown = 1;
115+ if (mustgetcpu)
116+ par->cpu = get_cpu();
117+ /* Wait until receiver ready */
118+ if (par->timeout) {
119+ clock_gettime(CLOCK_REALTIME, &ts);
120+ ts.tv_sec += par->timeout;
121+ 
122+ if (mq_timedreceive(par->syncmq, par->recvsyncmsg, MSG_SIZE, NULL, &ts)
123+ != strlen(syncmsg)) {
124+ fprintf(stderr, "could not receive sync message\n");
125+ par->shutdown = 1;
126+ }
127+ }
128+ if (mq_receive(par->syncmq, par->recvsyncmsg, MSG_SIZE, NULL) !=
129+ strlen(syncmsg)) {
130+ perror("could not receive sync message");
131+ par->shutdown = 1;
132+ }
133+ if (!par->shutdown && strcmp(syncmsg, par->recvsyncmsg)) {
134+ fprintf(stderr, "ERROR: Sync message mismatch detected\n");
135+ fprintf(stderr, " %s != %s\n", syncmsg, par->recvsyncmsg);
136+ par->shutdown = 1;
137+ }
138+ } else {
139+ /* Receiver */
140+ if (par->timeout) {
141+ clock_gettime(CLOCK_REALTIME, &ts);
142+ par->timeoutcount = 0;
143+ ts.tv_sec += par->timeout;
144+ do {
145+ if (mq_timedreceive(par->testmq, par->recvtestmsg,
146+ MSG_SIZE, NULL, &ts) != strlen(testmsg)) {
147+ if (!par->forcetimeout || errno != ETIMEDOUT) {
148+ perror("could not receive test message");
149+ par->shutdown = 1;
150+ break;
151+ }
152+ if (errno == ETIMEDOUT) {
153+ par->timeoutcount++;
154+ clock_gettime(CLOCK_REALTIME, &ts);
155+ ts.tv_sec += par->timeout;
156+ }
157+ } else
158+ break;
159+ } while (1);
160+ } else {
161+ if (mq_receive(par->testmq, par->recvtestmsg, MSG_SIZE, NULL) !=
162+ strlen(testmsg)) {
163+ perror("could not receive test message");
164+ par->shutdown = 1;
165+ }
166+ }
167+ /* ... Received the message: End of latency measurement */
168+ gettimeofday(&par->received, NULL);
169+ 
170+ if (!par->shutdown && strcmp(testmsg, par->recvtestmsg)) {
171+ fprintf(stderr, "ERROR: Test message mismatch detected\n");
172+ fprintf(stderr, " %s != %s\n", testmsg, par->recvtestmsg);
173+ par->shutdown = 1;
174+ }
175+ par->samples++;
176+ timersub(&par->received, &par->neighbor->sent,
177+ &par->diff);
178+ 
179+ if (par->diff.tv_usec < par->mindiff)
180+ par->mindiff = par->diff.tv_usec;
181+ if (par->diff.tv_usec > par->maxdiff)
182+ par->maxdiff = par->diff.tv_usec;
183+ par->sumdiff += (double) par->diff.tv_usec;
184+ if (par->tracelimit && par->maxdiff > par->tracelimit) {
185+ char tracing_enabled_file[MAX_PATH];
186+ 
187+ strcpy(tracing_enabled_file, get_debugfileprefix());
188+ strcat(tracing_enabled_file, "tracing_on");
189+ int tracing_enabled =
190+ open(tracing_enabled_file, O_WRONLY);
191+ if (tracing_enabled >= 0) {
192+ write(tracing_enabled, "0", 1);
193+ close(tracing_enabled);
194+ } else
195+ fatal("Could not access %s\n", tracing_enabled_file);
196+ par->shutdown = 1;
197+ par->neighbor->shutdown = 1;
198+ }
199+ 
200+ if (par->max_cycles && par->samples >= par->max_cycles)
201+ par->shutdown = 1;
202+ if (mustgetcpu)
203+ par->cpu = get_cpu();
204+ clock_nanosleep(CLOCK_MONOTONIC, 0, &par->delay, NULL);
205+ 
206+ /* Tell receiver that we are ready for the next measurement */
207+ if (mq_send(par->syncmq, syncmsg, strlen(syncmsg), 1) != 0) {
208+ fprintf(stderr, "could not send sync message\n");
209+ par->shutdown = 1;
210+ }
211+ }
212+ }
213+ par->stopped = 1;
214+ return NULL;
215+}
216+ 
217+static void display_help(int error)
218+{
219+ printf("pmqtest V %1.2f\n", VERSION);
220+ printf("Usage:\n"
221+ "pmqtest <options>\n\n"
222+ "Function: test POSIX message queue latency\n\n"
223+ "Available options:\n\n"
224+ "-a [NUM] --affinity run thread #N on processor #N, if possible\n"
225+ " with NUM pin all threads to the processor NUM\n"
226+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
227+ "-d DIST --distance=DIST distance of thread intervals in us default=500\n"
228+ "-D TIME --duration=TIME specify a length for the test run.\n"
229+ " Append 'm', 'h', or 'd' to specify\n"
230+ " minutes, hours or days.\n"
231+ "-f TO --forcetimeout=TO force timeout of mq_timedreceive(), requires -T\n"
232+ "-h --help print this help message\n"
233+ "-i INTV --interval=INTV base interval of thread in us default=1000\n"
234+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
235+ "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n"
236+ "-p PRIO --prio=PRIO priority\n"
237+ "-q --quiet print a summary only on exit\n"
238+ "-S --smp SMP testing: options -a -t and same priority\n"
239+ " of all threads\n"
240+ "-t --threads one thread per available processor\n"
241+ "-t [NUM] --threads=NUM number of threads:\n"
242+ " without NUM, threads = max_cpus\n"
243+ " without -t default = 1\n"
244+ "-T TO --timeout=TO use mq_timedreceive() instead of mq_receive()\n"
245+ " with timeout TO in seconds\n");
246+ exit(error);
247+}
248+ 
249+static int setaffinity = AFFINITY_UNSPECIFIED;
250+static int affinity;
251+static int tracelimit;
252+static int priority;
253+static int num_threads = 1;
254+static int max_cycles;
255+static int duration;
256+static int interval = 1000;
257+static int distance = 500;
258+static int smp;
259+static int sameprio;
260+static int timeout;
261+static int forcetimeout;
262+static int quiet;
263+static char jsonfile[MAX_PATH];
264+ 
265+enum option_value {
266+ OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_DISTANCE, OPT_DURATION,
267+ OPT_FORCETIMEOUT, OPT_HELP, OPT_INTERVAL, OPT_JSON, OPT_LOOPS,
268+ OPT_PRIORITY, OPT_QUIET, OPT_SMP, OPT_THREADS, OPT_TIMEOUT
269+};
270+ 
271+static void process_options(int argc, char *argv[])
272+{
273+ int error = 0;
274+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
275+ 
276+ for (;;) {
277+ int option_index = 0;
278+ /** Options for getopt */
279+ static struct option long_options[] = {
280+ {"affinity", optional_argument, NULL, OPT_AFFINITY},
281+ {"breaktrace", required_argument, NULL, OPT_BREAKTRACE},
282+ {"distance", required_argument, NULL, OPT_DISTANCE},
283+ {"duration", required_argument, NULL, OPT_DURATION},
284+ {"forcetimeout",required_argument, NULL, OPT_FORCETIMEOUT},
285+ {"help", no_argument, NULL, OPT_HELP},
286+ {"interval", required_argument, NULL, OPT_INTERVAL},
287+ {"json", required_argument, NULL, OPT_JSON },
288+ {"loops", required_argument, NULL, OPT_LOOPS},
289+ {"priority", required_argument, NULL, OPT_PRIORITY},
290+ {"quiet", no_argument, NULL, OPT_QUIET},
291+ {"smp", no_argument, NULL, OPT_SMP},
292+ {"threads", optional_argument, NULL, OPT_THREADS},
293+ {"timeout", required_argument, NULL, OPT_TIMEOUT},
294+ {NULL, 0, NULL, 0}
295+ };
296+ int c = getopt_long (argc, argv, "a::b:d:D:f:i:l:p:qSt::T:",
297+ long_options, &option_index);
298+ if (c == -1)
299+ break;
300+ switch (c) {
301+ case OPT_AFFINITY:
302+ case 'a':
303+ if (smp) {
304+ warn("-a ignored due to --smp\n");
305+ break;
306+ }
307+ if (optarg != NULL) {
308+ affinity = atoi(optarg);
309+ setaffinity = AFFINITY_SPECIFIED;
310+ } else if (optind < argc && atoi(argv[optind])) {
311+ affinity = atoi(argv[optind]);
312+ setaffinity = AFFINITY_SPECIFIED;
313+ } else {
314+ setaffinity = AFFINITY_USEALL;
315+ }
316+ break;
317+ case OPT_BREAKTRACE:
318+ case 'b':
319+ tracelimit = atoi(optarg);
320+ break;
321+ case OPT_DISTANCE:
322+ case 'd':
323+ distance = atoi(optarg);
324+ break;
325+ case OPT_DURATION:
326+ case 'D':
327+ duration = parse_time_string(optarg);
328+ break;
329+ case OPT_FORCETIMEOUT:
330+ case 'f':
331+ forcetimeout = atoi(optarg);
332+ break;
333+ case OPT_HELP:
334+ case '?':
335+ case 'h':
336+ display_help(0);
337+ break;
338+ case OPT_INTERVAL:
339+ case 'i':
340+ interval = atoi(optarg);
341+ break;
342+ case OPT_JSON:
343+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
344+ break;
345+ case OPT_LOOPS:
346+ case 'l':
347+ max_cycles = atoi(optarg);
348+ break;
349+ case OPT_PRIORITY:
350+ case 'p':
351+ priority = atoi(optarg);
352+ break;
353+ case OPT_QUIET:
354+ case 'q':
355+ quiet = 1;
356+ break;
357+ case OPT_SMP:
358+ case 'S':
359+ smp = 1;
360+ num_threads = max_cpus;
361+ setaffinity = AFFINITY_USEALL;
362+ break;
363+ case OPT_THREADS:
364+ case 't':
365+ if (smp) {
366+ warn("-t ignored due to --smp\n");
367+ break;
368+ }
369+ if (optarg != NULL)
370+ num_threads = atoi(optarg);
371+ else if (optind < argc && atoi(argv[optind]))
372+ num_threads = atoi(argv[optind]);
373+ else
374+ num_threads = max_cpus;
375+ break;
376+ case OPT_TIMEOUT:
377+ case 'T':
378+ timeout = atoi(optarg);
379+ break;
380+ default:
381+ display_help(1);
382+ break;
383+ }
384+ }
385+ 
386+ if (setaffinity == AFFINITY_SPECIFIED) {
387+ if (affinity < 0)
388+ error = 1;
389+ if (affinity >= max_cpus) {
390+ fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n",
391+ affinity, max_cpus);
392+ error = 1;
393+ }
394+ }
395+ 
396+ if (num_threads < 0)
397+ error = 1;
398+ 
399+ if (priority < 0 || priority > 99)
400+ error = 1;
401+ 
402+ if (num_threads < 1)
403+ error = 1;
404+ 
405+ if (forcetimeout && !timeout)
406+ error = 1;
407+ 
408+ if (duration < 0)
409+ error = 1;
410+ 
411+ if (priority && smp)
412+ sameprio = 1;
413+ 
414+ if (error)
415+ display_help(error);
416+}
417+ 
418+ 
419+static int volatile shutdown;
420+ 
421+static void sighand(int sig __attribute__ ((unused)))
422+{
423+ shutdown = 1;
424+}
425+ 
426+static void print_stat(FILE *fp __attribute__ ((unused)),
427+ struct params *receiver, struct params *sender,
428+ int verbose __attribute__ ((unused)), int quiet)
429+{
430+ int i;
431+ 
432+ if (quiet)
433+ return;
434+ 
435+ for (i = 0; i < num_threads; i++) {
436+ printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: ID%d, P%d, CPU%d, TO %d, Cycles %d\n",
437+ i*2, receiver[i].tid, receiver[i].priority, receiver[i].cpu,
438+ receiver[i].delay.tv_nsec / 1000,
439+ i*2+1, sender[i].tid, sender[i].priority, sender[i].cpu,
440+ receiver[i].timeoutcount, sender[i].samples);
441+ }
442+ for (i = 0; i < num_threads; i++) {
443+ printf("#%d -> #%d, Min %4d, Cur %4d, Avg %4d, Max %4d\n",
444+ i*2+1, i*2,
445+ receiver[i].mindiff, (int) receiver[i].diff.tv_usec,
446+ (int) ((receiver[i].sumdiff / receiver[i].samples) + 0.5),
447+ receiver[i].maxdiff);
448+ }
449+}
450+ 
451+struct params_stats {
452+ struct params *receiver;
453+ struct params *sender;
454+};
455+ 
456+static void write_stats(FILE *f, void *data)
457+{
458+ struct params_stats *ps = data;
459+ struct params *s, *r;
460+ int i;
461+ 
462+ fprintf(f, " \"num_threads\": %d,\n", num_threads);
463+ fprintf(f, " \"thread\": {\n");
464+ for (i = 0; i < num_threads; i++) {
465+ s = &ps->sender[i];
466+ r = &ps->receiver[i];
467+ fprintf(f, " \"%u\": {\n", i);
468+ fprintf(f, " \"sender\": {\n");
469+ fprintf(f, " \"cpu\": %d,\n", s->cpu);
470+ fprintf(f, " \"priority\": %d,\n", s->priority);
471+ fprintf(f, " \"samples\": %d,\n", s->samples);
472+ fprintf(f, " \"interval\": %ld\n", r->delay.tv_nsec/1000);
473+ fprintf(f, " },\n");
474+ fprintf(f, " \"receiver\": {\n");
475+ fprintf(f, " \"cpu\": %d,\n", r->cpu);
476+ fprintf(f, " \"priority\": %d,\n", r->priority);
477+ fprintf(f, " \"timeoutcount\": %d,\n", r->timeoutcount);
478+ fprintf(f, " \"min\": %d,\n", r->mindiff);
479+ fprintf(f, " \"avg\": %.2f,\n", r->sumdiff/r->samples);
480+ fprintf(f, " \"max\": %d\n", r->maxdiff);
481+ fprintf(f, " }\n");
482+ fprintf(f, " }%s\n", i == num_threads - 1 ? "" : ",");
483+ }
484+ fprintf(f, " }\n");
485+}
486+ 
487+int main(int argc, char *argv[])
488+{
489+ int i;
490+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
491+ struct params *receiver = NULL;
492+ struct params *sender = NULL;
493+ sigset_t sigset;
494+ int oldsamples = INT_MAX;
495+ int oldtimeoutcount = INT_MAX;
496+ struct timespec maindelay;
497+ int oflag = O_CREAT|O_RDWR;
498+ struct mq_attr mqstat;
499+ 
500+ memset(&mqstat, 0, sizeof(mqstat));
501+ mqstat.mq_maxmsg = 1;
502+ mqstat.mq_msgsize = 8;
503+ mqstat.mq_flags = 0;
504+ 
505+ rt_init(argc, argv);
506+ process_options(argc, argv);
507+ 
508+ if (check_privs())
509+ return 1;
510+ 
511+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
512+ perror("mlockall");
513+ return 1;
514+ }
515+ 
516+ sigemptyset(&sigset);
517+ sigaddset(&sigset, SIGTERM);
518+ sigaddset(&sigset, SIGINT);
519+ sigaddset(&sigset, SIGALRM);
520+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
521+ 
522+ signal(SIGINT, sighand);
523+ signal(SIGTERM, sighand);
524+ signal(SIGALRM, sighand);
525+ 
526+ if (duration)
527+ alarm(duration);
528+ 
529+ receiver = calloc(num_threads, sizeof(struct params));
530+ sender = calloc(num_threads, sizeof(struct params));
531+ if (receiver == NULL || sender == NULL)
532+ goto nomem;
533+ 
534+ for (i = 0; i < num_threads; i++) {
535+ char mqname[19];
536+ 
537+ sprintf(mqname, SYNCMQ_NAME, i);
538+ receiver[i].syncmq = mq_open(mqname, oflag, 0777, &mqstat);
539+ if (receiver[i].syncmq == (mqd_t) -1) {
540+ fprintf(stderr, "could not open POSIX message queue #1\n");
541+ return 1;
542+ }
543+ sprintf(mqname, TESTMQ_NAME, i);
544+ receiver[i].testmq = mq_open(mqname, oflag, 0777, &mqstat);
545+ if (receiver[i].testmq == (mqd_t) -1) {
546+ fprintf(stderr, "could not open POSIX message queue #2\n");
547+ return 1;
548+ }
549+ 
550+ receiver[i].mindiff = UINT_MAX;
551+ receiver[i].maxdiff = 0;
552+ receiver[i].sumdiff = 0.0;
553+ 
554+ receiver[i].num = i;
555+ receiver[i].cpu = i;
556+ switch (setaffinity) {
557+ case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break;
558+ case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break;
559+ case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break;
560+ }
561+ receiver[i].priority = priority;
562+ receiver[i].tracelimit = tracelimit;
563+ if (priority > 1 && !sameprio)
564+ priority--;
565+ receiver[i].delay.tv_sec = interval / USEC_PER_SEC;
566+ receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000;
567+ interval += distance;
568+ receiver[i].max_cycles = max_cycles;
569+ receiver[i].sender = 0;
570+ receiver[i].neighbor = &sender[i];
571+ receiver[i].timeout = timeout;
572+ receiver[i].forcetimeout = forcetimeout;
573+ pthread_create(&receiver[i].threadid, NULL, pmqthread, &receiver[i]);
574+ memcpy(&sender[i], &receiver[i], sizeof(receiver[0]));
575+ sender[i].sender = 1;
576+ sender[i].neighbor = &receiver[i];
577+ pthread_create(&sender[i].threadid, NULL, pmqthread, &sender[i]);
578+ }
579+ 
580+ maindelay.tv_sec = 0;
581+ maindelay.tv_nsec = 50000000; /* 50 ms */
582+ 
583+ sigemptyset(&sigset);
584+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
585+ 
586+ do {
587+ int newsamples = 0, newtimeoutcount = 0;
588+ int minsamples = INT_MAX;
589+ 
590+ for (i = 0; i < num_threads; i++) {
591+ newsamples += receiver[i].samples;
592+ newtimeoutcount += receiver[i].timeoutcount;
593+ if (receiver[i].samples < minsamples)
594+ minsamples = receiver[i].samples;
595+ }
596+ 
597+ if (minsamples > 1 && (shutdown || newsamples > oldsamples ||
598+ newtimeoutcount > oldtimeoutcount)) {
599+ print_stat(stdout, receiver, sender, 0, quiet);
600+ if (!quiet)
601+ printf("\033[%dA", num_threads*2);
602+ }
603+ 
604+ fflush(NULL);
605+ 
606+ oldsamples = 0;
607+ oldtimeoutcount = 0;
608+ for (i = 0; i < num_threads; i++) {
609+ oldsamples += receiver[i].samples;
610+ oldtimeoutcount += receiver[i].timeoutcount;
611+ }
612+ 
613+ nanosleep(&maindelay, NULL);
614+ 
615+ for (i = 0; i < num_threads; i++)
616+ shutdown |= receiver[i].shutdown | sender[i].shutdown;
617+ 
618+ } while (!shutdown);
619+ 
620+ if (!quiet)
621+ printf("\033[%dB", num_threads*2 + 2);
622+ else
623+ print_stat(stdout, receiver, sender, 0, 0);
624+ 
625+ for (i = 0; i < num_threads; i++) {
626+ receiver[i].shutdown = 1;
627+ sender[i].shutdown = 1;
628+ }
629+ 
630+ for (i = 0; i < num_threads; i++) {
631+ if (!receiver[i].stopped)
632+ pthread_kill(receiver[i].threadid, SIGTERM);
633+ if (!sender[i].stopped)
634+ pthread_kill(sender[i].threadid, SIGTERM);
635+ }
636+ nanosleep(&maindelay, NULL);
637+ for (i = 0; i < num_threads; i++) {
638+ char mqname[19];
639+ 
640+ mq_close(receiver[i].syncmq);
641+ sprintf(mqname, SYNCMQ_NAME, i);
642+ mq_unlink(mqname);
643+ 
644+ mq_close(receiver[i].testmq);
645+ sprintf(mqname, TESTMQ_NAME, i);
646+ mq_unlink(mqname);
647+ }
648+ 
649+ if (strlen(jsonfile) != 0) {
650+ struct params_stats ps = {
651+ .receiver = receiver,
652+ .sender = sender,
653+ };
654+ rt_write_json(jsonfile, 0, write_stats, &ps);
655+ }
656+ 
657+nomem:
658+ 
659+ return 0;
660+}
@@ -0,0 +1,72 @@
1+.TH "ptsematest" "8" "0.1" "" ""
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+.SH "NAME"
4+.LP
5+\fBptsematest\fR \- Start two threads and measure the latency of interprocess communication with POSIX mutex.
6+.SH "SYNOPSIS"
7+.LP
8+ptsematest [-a|--affinity [PROC]] [-b|--breaktrace USEC] [-d|--distance DIST] [-D|--duration TIME] [-h|--help] [-i|--interval INTV] [--json FILENAME] [-l|--loops LOOPS] [-p|--prio PRIO] [-q|--quiet] [-S|--smp] [-t|--threads [NUM]]
9+.br
10+.SH "DESCRIPTION"
11+.LP
12+The program \fBptsematest\fR starts two threads that are synchronized via pthread_mutex_unlock()/pthread_mutex_lock() and measures the latency between releasing and getting the lock.
13+.SH "OPTIONS"
14+.TP
15+.B \-a, \-\-affinity[=PROC]
16+Run on processor number PROC. If PROC is not specified, run on current processor.
17+.TP
18+.B \-b, \-\-breaktrace=USEC
19+Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch.
20+It is useful to track down unexpected large latencies of a system.
21+.TP
22+.B \-d, \-\-distance=DIST
23+Set the distance of thread intervals in microseconds (default is 500 us). When cyclictest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST
24+.TP
25+.B \-D, \-\-duration=TIME
26+Specify a length for the test run.
27+.br
28+Append 'm', 'h', or 'd' to specify minutes, hours or days.
29+.TP
30+.B \-h, \-\-help
31+Print help message.
32+.TP
33+.B \-i, \-\-interval=INTV
34+Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d.
35+.TP
36+.B \-\-json=FILENAME
37+Write final results into FILENAME, JSON formatted.
38+.TP
39+.B \-l, \-\-loops=LOOPS
40+Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. ptsematest is stopped once the number of timer intervals has been reached.
41+.TP
42+.B \-p, \-\-prio=PRIO
43+Set the priority of the process.
44+.TP
45+.B \-q, \-\-quiet
46+Print a summary only on exit. Useful for automated tests, where only the summary output needs to be captured.
47+.TP
48+.B \-S, \-\-smp
49+SMP testing: options -a -t and same priority
50+.TP
51+.B \-t, \-\-threads[=NUM]
52+Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specified, NUM is set to the number of available CPUs.
53+.SH "EXAMPLES"
54+The following example was running on a 4-way processor:
55+.LP
56+.nf
57+# ptsematest -a -t -p99 -i100 -d25 -l1000000
58+#0: ID8672, P99, CPU0, I100; #1: ID8673, P99, CPU0, Cycles 1000000
59+#2: ID8674, P98, CPU1, I125; #3: ID8675, P98, CPU1, Cycles 811035
60+#4: ID8676, P97, CPU2, I150; #5: ID8677, P97, CPU2, Cycles 668130
61+#6: ID8678, P96, CPU3, I175; #7: ID8679, P96, CPU3, Cycles 589423
62+#1 -> #0, Min 1, Cur 1, Avg 2, Max 11
63+#3 -> #2, Min 1, Cur 2, Avg 2, Max 13
64+#5 -> #4, Min 1, Cur 4, Avg 3, Max 12
65+#7 -> #6, Min 1, Cur 4, Avg 2, Max 12
66+.fi
67+.SH "AUTHORS"
68+.LP
69+Carsten Emde <C.Emde@osadl.org>
70+.SH "SEE ALSO"
71+.LP
72+pthread_mutex_lock(3p), pthread_mutex_unlock(3p)
@@ -0,0 +1,523 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * ptsematest.c
4+ * Copyright (C) 2009 Carsten Emde <C.Emde@osadl.org>
5+ */
6+ 
7+#include <stdio.h>
8+#include <stdlib.h>
9+#include <unistd.h>
10+#include <limits.h>
11+#include <fcntl.h>
12+#include <getopt.h>
13+#include <signal.h>
14+#include <string.h>
15+#include <time.h>
16+#include <sys/types.h>
17+#include <sys/stat.h>
18+#include <sys/time.h>
19+#include <sys/mman.h>
20+#include <linux/unistd.h>
21+#include <utmpx.h>
22+#include <pthread.h>
23+#include <inttypes.h>
24+ 
25+#include "rt-utils.h"
26+#include "rt-get_cpu.h"
27+#include "rt-error.h"
28+ 
29+enum {
30+ AFFINITY_UNSPECIFIED,
31+ AFFINITY_SPECIFIED,
32+ AFFINITY_USEALL
33+};
34+ 
35+static pthread_mutex_t *testmutex;
36+static pthread_mutex_t *syncmutex;
37+ 
38+struct params {
39+ int num;
40+ int cpu;
41+ int priority;
42+ int affinity;
43+ int sender;
44+ int samples;
45+ int max_cycles;
46+ int tracelimit;
47+ int tid;
48+ int shutdown;
49+ int stopped;
50+ struct timespec delay;
51+ unsigned int mindiff, maxdiff;
52+ double sumdiff;
53+ struct timeval unblocked, received, diff;
54+ pthread_t threadid;
55+ struct params *neighbor;
56+};
57+ 
58+void *semathread(void *param)
59+{
60+ int mustgetcpu = 0;
61+ struct params *par = param;
62+ cpu_set_t mask;
63+ int policy = SCHED_FIFO;
64+ struct sched_param schedp;
65+ 
66+ memset(&schedp, 0, sizeof(schedp));
67+ schedp.sched_priority = par->priority;
68+ sched_setscheduler(0, policy, &schedp);
69+ 
70+ if (par->cpu != -1) {
71+ CPU_ZERO(&mask);
72+ CPU_SET(par->cpu, &mask);
73+ if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
74+ fprintf(stderr, "WARNING: Could not set CPU affinity "
75+ "to CPU #%d\n", par->cpu);
76+ } else
77+ mustgetcpu = 1;
78+ 
79+ par->tid = gettid();
80+ 
81+ while (!par->shutdown) {
82+ if (par->sender) {
83+ pthread_mutex_lock(&syncmutex[par->num]);
84+ 
85+ /* Release lock: Start of latency measurement ... */
86+ gettimeofday(&par->unblocked, NULL);
87+ pthread_mutex_unlock(&testmutex[par->num]);
88+ par->samples++;
89+ if (par->max_cycles && par->samples >= par->max_cycles)
90+ par->shutdown = 1;
91+ if (mustgetcpu)
92+ par->cpu = get_cpu();
93+ } else {
94+ pthread_mutex_lock(&testmutex[par->num]);
95+ 
96+ /* ... Got the lock: End of latency measurement */
97+ gettimeofday(&par->received, NULL);
98+ par->samples++;
99+ timersub(&par->received, &par->neighbor->unblocked,
100+ &par->diff);
101+ 
102+ if (par->diff.tv_usec < par->mindiff)
103+ par->mindiff = par->diff.tv_usec;
104+ if (par->diff.tv_usec > par->maxdiff)
105+ par->maxdiff = par->diff.tv_usec;
106+ par->sumdiff += (double) par->diff.tv_usec;
107+ if (par->tracelimit && par->maxdiff > par->tracelimit) {
108+ char tracing_enabled_file[MAX_PATH];
109+ 
110+ strcpy(tracing_enabled_file, get_debugfileprefix());
111+ strcat(tracing_enabled_file, "tracing_on");
112+ int tracing_enabled =
113+ open(tracing_enabled_file, O_WRONLY);
114+ if (tracing_enabled >= 0) {
115+ write(tracing_enabled, "0", 1);
116+ close(tracing_enabled);
117+ } else
118+ fatal("Could not access %s\n",
119+ tracing_enabled_file);
120+ par->shutdown = 1;
121+ par->neighbor->shutdown = 1;
122+ }
123+ 
124+ if (par->max_cycles && par->samples >= par->max_cycles)
125+ par->shutdown = 1;
126+ if (mustgetcpu)
127+ par->cpu = get_cpu();
128+ nanosleep(&par->delay, NULL);
129+ pthread_mutex_unlock(&syncmutex[par->num]);
130+ }
131+ }
132+ par->stopped = 1;
133+ return NULL;
134+}
135+ 
136+ 
137+static void display_help(int error)
138+{
139+ printf("ptsematest V %1.2f\n", VERSION);
140+ printf("Usage:\n"
141+ "ptsematest <options>\n\n"
142+ "Function: test POSIX threads mutex latency\n\n"
143+ "Available options:\n"
144+ "-a [NUM] --affinity run thread #N on processor #N, if possible\n"
145+ " with NUM pin all threads to the processor NUM\n"
146+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
147+ "-d DIST --distance=DIST distance of thread intervals in us default=500\n"
148+ "-D --duration=TIME specify a length for the test run.\n"
149+ " Append 'm', 'h', or 'd' to specify minutes, hours or\n"
150+ " days.\n"
151+ "-i INTV --interval=INTV base interval of thread in us default=1000\n"
152+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
153+ "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n"
154+ "-p PRIO --prio=PRIO priority\n"
155+ "-q --quiet print a summary only on exit\n"
156+ "-S --smp SMP testing: options -a -t and same priority\n"
157+ " of all threads\n"
158+ "-t --threads one thread per available processor\n"
159+ "-t [NUM] --threads=NUM number of threads:\n"
160+ " without NUM, threads = max_cpus\n"
161+ " without -t default = 1\n"
162+ );
163+ exit(error);
164+}
165+ 
166+ 
167+static int setaffinity = AFFINITY_UNSPECIFIED;
168+static int affinity;
169+static int tracelimit;
170+static int priority;
171+static int num_threads = 1;
172+static int max_cycles;
173+static int duration;
174+static int interval = 1000;
175+static int distance = 500;
176+static int smp;
177+static int sameprio;
178+static int quiet;
179+static char jsonfile[MAX_PATH];
180+ 
181+enum option_value {
182+ OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_DISTANCE, OPT_DURATION,
183+ OPT_HELP, OPT_INTERVAL, OPT_JSON, OPT_LOOPS, OPT_PRIORITY,
184+ OPT_QUIET, OPT_SMP, OPT_THREADS
185+};
186+ 
187+static void process_options(int argc, char *argv[])
188+{
189+ int error = 0;
190+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
191+ 
192+ for (;;) {
193+ int option_index = 0;
194+ /** Options for getopt */
195+ static struct option long_options[] = {
196+ {"affinity", optional_argument, NULL, OPT_AFFINITY},
197+ {"breaktrace", required_argument, NULL, OPT_BREAKTRACE},
198+ {"distance", required_argument, NULL, OPT_DISTANCE},
199+ {"duration", required_argument, NULL, OPT_DURATION},
200+ {"help", no_argument, NULL, OPT_HELP},
201+ {"interval", required_argument, NULL, OPT_INTERVAL},
202+ {"json", required_argument, NULL, OPT_JSON },
203+ {"loops", required_argument, NULL, OPT_LOOPS},
204+ {"priority", required_argument, NULL, OPT_PRIORITY},
205+ {"quiet", no_argument , NULL, OPT_QUIET},
206+ {"smp", no_argument, NULL, OPT_SMP},
207+ {"threads", optional_argument, NULL, OPT_THREADS},
208+ {NULL, 0, NULL, 0}
209+ };
210+ int c = getopt_long (argc, argv, "a::b:d:i:l:D:p:qSt::h",
211+ long_options, &option_index);
212+ if (c == -1)
213+ break;
214+ switch (c) {
215+ case OPT_AFFINITY:
216+ case 'a':
217+ if (smp) {
218+ warn("-a ignored due to --smp\n");
219+ break;
220+ }
221+ if (optarg != NULL) {
222+ affinity = atoi(optarg);
223+ setaffinity = AFFINITY_SPECIFIED;
224+ } else if (optind < argc && atoi(argv[optind])) {
225+ affinity = atoi(argv[optind]);
226+ setaffinity = AFFINITY_SPECIFIED;
227+ } else {
228+ setaffinity = AFFINITY_USEALL;
229+ }
230+ break;
231+ case OPT_BREAKTRACE:
232+ case 'b':
233+ tracelimit = atoi(optarg);
234+ break;
235+ case OPT_DISTANCE:
236+ case 'd':
237+ distance = atoi(optarg);
238+ break;
239+ case OPT_DURATION:
240+ case 'D':
241+ duration = parse_time_string(optarg);
242+ break;
243+ case OPT_INTERVAL:
244+ case 'i':
245+ interval = atoi(optarg);
246+ break;
247+ case OPT_JSON:
248+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
249+ break;
250+ case OPT_HELP:
251+ case '?':
252+ case 'h':
253+ display_help(0);
254+ break;
255+ case OPT_LOOPS:
256+ case 'l':
257+ max_cycles = atoi(optarg);
258+ break;
259+ case OPT_PRIORITY:
260+ case 'p':
261+ priority = atoi(optarg);
262+ break;
263+ case OPT_QUIET:
264+ case 'q':
265+ quiet = 1;
266+ break;
267+ case OPT_SMP:
268+ case 'S':
269+ smp = 1;
270+ num_threads = max_cpus;
271+ setaffinity = AFFINITY_USEALL;
272+ break;
273+ case OPT_THREADS:
274+ case 't':
275+ if (smp) {
276+ warn("-t ignored due to --smp\n");
277+ break;
278+ }
279+ if (optarg != NULL)
280+ num_threads = atoi(optarg);
281+ else if (optind < argc && atoi(argv[optind]))
282+ num_threads = atoi(argv[optind]);
283+ else
284+ num_threads = max_cpus;
285+ break;
286+ default:
287+ display_help(1);
288+ break;
289+ }
290+ }
291+ 
292+ if (setaffinity == AFFINITY_SPECIFIED) {
293+ if (affinity < 0)
294+ error = 1;
295+ if (affinity >= max_cpus) {
296+ fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n",
297+ affinity, max_cpus);
298+ error = 1;
299+ }
300+ }
301+ 
302+ if (num_threads < 0)
303+ error = 1;
304+ 
305+ if (priority < 0 || priority > 99)
306+ error = 1;
307+ 
308+ if (num_threads < 1)
309+ error = 1;
310+ 
311+ if (duration < 0)
312+ error = 1;
313+ 
314+ if (priority && smp)
315+ sameprio = 1;
316+ 
317+ if (error)
318+ display_help(error);
319+}
320+ 
321+ 
322+static int volatile shutdown;
323+ 
324+static void sighand(int sig __attribute__ ((unused)))
325+{
326+ shutdown = 1;
327+}
328+ 
329+static void print_stat(FILE *fp __attribute__ ((unused)),
330+ struct params *receiver, struct params *sender,
331+ int verbose __attribute__ ((unused)), int quiet)
332+{
333+ int i;
334+ 
335+ if (quiet)
336+ return;
337+ 
338+ for (i = 0; i < num_threads; i++) {
339+ printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: ID%d, P%d, CPU%d, Cycles %d\n",
340+ i*2, receiver[i].tid, receiver[i].priority, receiver[i].cpu,
341+ receiver[i].delay.tv_nsec / 1000,
342+ i*2+1, sender[i].tid, sender[i].priority, sender[i].cpu,
343+ sender[i].samples);
344+ }
345+ for (i = 0; i < num_threads; i++) {
346+ printf("#%d -> #%d, Min %4d, Cur %4d, Avg %4d, Max %4d\n",
347+ i*2+1, i*2,
348+ receiver[i].mindiff, (int) receiver[i].diff.tv_usec,
349+ (int) ((receiver[i].sumdiff / receiver[i].samples) + 0.5),
350+ receiver[i].maxdiff);
351+ }
352+}
353+ 
354+struct params_stats {
355+ struct params *receiver;
356+ struct params *sender;
357+};
358+ 
359+static void write_stats(FILE *f, void *data)
360+{
361+ struct params_stats *ps = data;
362+ struct params *s, *r;
363+ int i;
364+ 
365+ fprintf(f, " \"num_threads\": %d,\n", num_threads);
366+ fprintf(f, " \"thread\": {\n");
367+ for (i = 0; i < num_threads; i++) {
368+ s = &ps->sender[i];
369+ r = &ps->receiver[i];
370+ fprintf(f, " \"%u\": {\n", i);
371+ fprintf(f, " \"sender\": {\n");
372+ fprintf(f, " \"cpu\": %d,\n", s->cpu);
373+ fprintf(f, " \"priority\": %d,\n", s->priority);
374+ fprintf(f, " \"samples\": %d,\n", s->samples);
375+ fprintf(f, " \"interval\": %ld\n", r->delay.tv_nsec/1000);
376+ fprintf(f, " },\n");
377+ fprintf(f, " \"receiver\": {\n");
378+ fprintf(f, " \"cpu\": %d,\n", r->cpu);
379+ fprintf(f, " \"priority\": %d,\n", r->priority);
380+ fprintf(f, " \"min\": %d,\n", r->mindiff);
381+ fprintf(f, " \"avg\": %.2f,\n", r->sumdiff/r->samples);
382+ fprintf(f, " \"max\": %d\n", r->maxdiff);
383+ fprintf(f, " }\n");
384+ fprintf(f, " }%s\n", i == num_threads - 1 ? "" : ",");
385+ }
386+ fprintf(f, " }\n");
387+}
388+ 
389+int main(int argc, char *argv[])
390+{
391+ int i;
392+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
393+ int oldsamples = 1;
394+ struct params *receiver = NULL;
395+ struct params *sender = NULL;
396+ sigset_t sigset;
397+ struct timespec maindelay;
398+ 
399+ rt_init(argc, argv);
400+ process_options(argc, argv);
401+ 
402+ if (check_privs())
403+ return 1;
404+ 
405+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
406+ perror("mlockall");
407+ return 1;
408+ }
409+ 
410+ signal(SIGINT, sighand);
411+ signal(SIGTERM, sighand);
412+ signal(SIGALRM, sighand);
413+ 
414+ if (duration)
415+ alarm(duration);
416+ 
417+ receiver = calloc(num_threads, sizeof(struct params));
418+ sender = calloc(num_threads, sizeof(struct params));
419+ if (receiver == NULL || sender == NULL)
420+ goto nomem;
421+ 
422+ testmutex = (pthread_mutex_t *) calloc(num_threads, sizeof(pthread_mutex_t));
423+ syncmutex = (pthread_mutex_t *) calloc(num_threads, sizeof(pthread_mutex_t));
424+ if (testmutex == NULL || syncmutex == NULL)
425+ goto nomem;
426+ 
427+ for (i = 0; i < num_threads; i++) {
428+ receiver[i].mindiff = UINT_MAX;
429+ receiver[i].maxdiff = 0;
430+ receiver[i].sumdiff = 0.0;
431+ 
432+ 
433+ pthread_mutex_init(&testmutex[i], NULL);
434+ pthread_mutex_init(&syncmutex[i], NULL);
435+ 
436+ /* Wait on first attempt */
437+ pthread_mutex_lock(&testmutex[i]);
438+ 
439+ receiver[i].num = i;
440+ receiver[i].cpu = i;
441+ switch (setaffinity) {
442+ case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break;
443+ case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break;
444+ case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break;
445+ }
446+ receiver[i].priority = priority;
447+ receiver[i].tracelimit = tracelimit;
448+ if (priority > 1 && !sameprio)
449+ priority--;
450+ receiver[i].delay.tv_sec = interval / USEC_PER_SEC;
451+ receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000;
452+ interval += distance;
453+ receiver[i].max_cycles = max_cycles;
454+ receiver[i].sender = 0;
455+ receiver[i].neighbor = &sender[i];
456+ pthread_create(&receiver[i].threadid, NULL, semathread, &receiver[i]);
457+ memcpy(&sender[i], &receiver[i], sizeof(receiver[0]));
458+ sender[i].sender = 1;
459+ sender[i].neighbor = &receiver[i];
460+ pthread_create(&sender[i].threadid, NULL, semathread, &sender[i]);
461+ }
462+ 
463+ maindelay.tv_sec = 0;
464+ maindelay.tv_nsec = 50000000; /* 50 ms */
465+ 
466+ while (!shutdown) {
467+ for (i = 0; i < num_threads; i++)
468+ shutdown |= receiver[i].shutdown | sender[i].shutdown;
469+ 
470+ if (receiver[0].samples > oldsamples || shutdown) {
471+ print_stat(stdout, receiver, sender, 0, quiet);
472+ if (!quiet)
473+ printf("\033[%dA", num_threads*2);
474+ }
475+ 
476+ sigemptyset(&sigset);
477+ sigaddset(&sigset, SIGTERM);
478+ sigaddset(&sigset, SIGINT);
479+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
480+ 
481+ nanosleep(&maindelay, NULL);
482+ 
483+ sigemptyset(&sigset);
484+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
485+ }
486+ 
487+ if (!quiet)
488+ printf("\033[%dB", num_threads*2 + 2);
489+ else
490+ print_stat(stdout, receiver, sender, 0, 0);
491+ 
492+ for (i = 0; i < num_threads; i++) {
493+ receiver[i].shutdown = 1;
494+ sender[i].shutdown = 1;
495+ pthread_mutex_unlock(&testmutex[i]);
496+ pthread_mutex_unlock(&syncmutex[i]);
497+ }
498+ nanosleep(&receiver[0].delay, NULL);
499+ 
500+ for (i = 0; i < num_threads; i++) {
501+ if (!receiver[i].stopped)
502+ pthread_kill(receiver[i].threadid, SIGTERM);
503+ if (!sender[i].stopped)
504+ pthread_kill(sender[i].threadid, SIGTERM);
505+ }
506+ 
507+ for (i = 0; i < num_threads; i++) {
508+ pthread_mutex_destroy(&testmutex[i]);
509+ pthread_mutex_destroy(&syncmutex[i]);
510+ }
511+ 
512+ if (strlen(jsonfile) != 0) {
513+ struct params_stats ps = {
514+ .receiver = receiver,
515+ .sender = sender,
516+ };
517+ rt_write_json(jsonfile, 0, write_stats, &ps);
518+ }
519+ 
520+nomem:
521+ 
522+ return 0;
523+}
@@ -0,0 +1,143 @@
1+ 
2+cyclictest does not catch all cases where packet forwarding
3+latency can exceed a given threshold.
4+ 
5+Example:
6+ 
7+# taskset -c 3 ./queuelat -m 20000 -c 100 -p 13 -f `sh ./get_cpuinfo_mhz.sh`
8+ 
9+# rmmod targeted-ipi; insmod ./targeted-ipi.ko ipidest=3 nripis=200 interval=2 delay=10
10+ 
11+ <...>-4566 [003] .....11 4474.559880: tracing_mark_write: memmove block queue_size=28 queue_dec=279 queue_inc=307 delta=23685 ns
12+ <...>-4566 [003] .....11 4474.559912: tracing_mark_write: memmove block queue_size=63 queue_dec=279 queue_inc=314 delta=24198 ns
13+ <...>-4566 [003] .....11 4474.559937: tracing_mark_write: memmove block queue_size=97 queue_dec=279 queue_inc=313 delta=24090 ns
14+ <...>-4566 [003] .....11 4474.559965: tracing_mark_write: memmove block queue_size=130 queue_dec=279 queue_inc=312 delta=24048 ns
15+ <...>-4566 [003] .....11 4474.559993: tracing_mark_write: memmove block queue_size=162 queue_dec=279 queue_inc=311 delta=23957 ns
16+ <...>-4566 [003] .....11 4474.560018: tracing_mark_write: memmove block queue_size=193 queue_dec=279 queue_inc=310 delta=23912 ns
17+ <...>-4566 [003] .....11 4474.560046: tracing_mark_write: memmove block queue_size=225 queue_dec=279 queue_inc=311 delta=23965 ns
18+ <...>-4566 [003] .....11 4474.560074: tracing_mark_write: memmove block queue_size=257 queue_dec=279 queue_inc=311 delta=23971 ns
19+ <...>-4566 [003] .....11 4474.560102: tracing_mark_write: memmove block queue_size=288 queue_dec=279 queue_inc=310 delta=23902 ns
20+ <...>-4566 [003] .....11 4474.560127: tracing_mark_write: memmove block queue_size=320 queue_dec=279 queue_inc=311 delta=23945 ns
21+ <...>-4566 [003] .....11 4474.560155: tracing_mark_write: memmove block queue_size=351 queue_dec=279 queue_inc=310 delta=23921 ns
22+ <...>-4566 [003] .....11 4474.560180: tracing_mark_write: memmove block queue_size=381 queue_dec=279 queue_inc=309 delta=23839 ns
23+ <...>-4566 [003] .....11 4474.560208: tracing_mark_write: memmove block queue_size=412 queue_dec=279 queue_inc=310 delta=23876 ns
24+ <...>-4566 [003] .....11 4474.560236: tracing_mark_write: memmove block queue_size=443 queue_dec=279 queue_inc=310 delta=23886 ns
25+ <...>-4566 [003] .....11 4474.560261: tracing_mark_write: memmove block queue_size=474 queue_dec=279 queue_inc=310 delta=23901 ns
26+ <...>-4566 [003] .....11 4474.560288: tracing_mark_write: memmove block queue_size=505 queue_dec=279 queue_inc=310 delta=23891 ns
27+ <...>-4566 [003] .....11 4474.560316: tracing_mark_write: memmove block queue_size=535 queue_dec=279 queue_inc=309 delta=23822 ns
28+ <...>-4566 [003] .....11 4474.560341: tracing_mark_write: memmove block queue_size=565 queue_dec=279 queue_inc=309 delta=23815 ns
29+ <...>-4566 [003] .....11 4474.560353: tracing_mark_write: queue length exceeded: queue_size=565 max_queue_len=559
30+ 
31+# taskset -c 3 cyclictest -m -n -q -p95 -D 60m -h60 -i 200
32+ 
33+# rmmod targeted-ipi; insmod ./targeted-ipi.ko ipidest=3 nripis=20000 interval=2 delay=10
34+ 
35+Cyclictest results:
36+ 
37+# Total: 000068099
38+# Min Latencies: 00001
39+# Avg Latencies: 00002
40+# Max Latencies: 00008
41+# Histogram Overflows: 00000
42+ 
43+ 
44+----- queuelat basics:
45+ 
46+Queuelat simulates a DPDK queue. From queuelat.c:
47+ 
48+Program parameters:
49+max_queue_len: maximum latency allowed, in nanoseconds (int).
50+cycles_per_packet: number of cycles to process one packet (int).
51+mpps(million-packet-per-sec): million packets per second (float).
52+tsc_freq_mhz: TSC frequency in MHz, as measured by TSC PIT calibration
53+(search for "Detected XXX MHz processor" in dmesg, and use the integer part).
54+timeout: timeout (in seconds).
55+ 
56+How it works
57+============
58+ 
59+ The program in essence does:
60+ 
61+ b = rdtsc();
62+ memmove(dest, src, n);
63+ a = rdtsc();
64+ 
65+ delay = convert_to_ns(a - b);
66+ 
67+ queue_size += packets_queued_in(delay);
68+ queue_size -= packets_processed;
69+
70+ if (queue_size > max_queue_len)
71+ FAIL();
72+ 
73+packets_processed is fixed, and is estimated as follows:
74+n is determined first, so that the stats bucket with highest count
75+takes max_latency/2.
76+for max_latency/2, we calculate how many packets can be drained
77+in that time (using cycles_per_packet).
78+ 
79+Queuelat output
80+===============
81+ 
82+During calibration, queuelat outputs the following table:
83+ 
84+[9600 - 9699] = 0 packetfillrates=[67 - 67]
85+[9700 - 9799] = 7907 packetfillrates=[67 - 68]
86+[9800 - 9899] = 42085 packetfillrates=[68 - 69]
87+[9900 - 9999] = 7 packetfillrates=[69 - 69]
88+[10000 - 10099] = 1 packetfillrates=[70 - 70]
89+ | | | |
90+ | | | |
91+ | | | |_________ [min - max] number of
92+ | | | packets the queue will reach
93+ | | | with specified mpps in this
94+ | | | time (without draining)
95+ | | |
96+ | | |______________________________ number of hits for this
97+ | | bucket
98+ | |
99+ | |______________________________________ min amount of time (ns)
100+ | this bucket accepts
101+ |
102+ |______________________________________________ max amount of time (ns)
103+ this bucket accepts
104+ 
105+ 
106+On success, queuelat outputs a table similar to cyclictest:
107+ 
108+[7000 - 7099] = 0
109+[7100 - 7199] = 2
110+[7200 - 7299] = 2457
111+[7300 - 7399] = 21058
112+ | | |
113+ | | |___________ Number of processing loops that hit this
114+ | | bucket.
115+ | |
116+ | |____________________ Maximum number of nanoseconds of this bucket.
117+ |
118+ |
119+ |___________________________ Minimum number of nanoseconds in this bucket.
120+ 
121+That is a processing loop will account into a bucket if its duration
122+is
123+ 
124+ min_number_ns_in_bucket < duration < max_number_ns_in_bucket
125+ 
126+ 
127+Automatic determination of Mpps
128+===============================
129+ 
130+There is a script called determine_maximum_mpps.sh, which should be edited
131+to include the pinning and -RT priority configuration for your machine.
132+ 
133+PREAMBLE="taskset -c 2 chrt -f 1"
134+MAXLAT="20000"
135+CYCLES_PER_PACKET="300"
136+ 
137+This script will find the maximum mpps parameter which can sustain:
138+ 
139+ 1) 10 consecutive 30 second runs.
140+ 2) 1 run of 10 minutes.
141+ 
142+Without violating the latency specified with $MAXLAT.
143+ 
@@ -0,0 +1,65 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH DETERMINE_MAXIMUM_MPPS 8 "Dec 4, 2020"
3+# SPDX-License-Identifier: GPL-2.0-or-later
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+determine_maximum_mpps \- Determine the maximum mpps the machine can handle
18+.SH SYNOPSIS
19+.LP
20+determine_maximum_mpps [-a cpulist] [-m maxlat] [-n cycles] [-f | -r] [-p priority] [-h]
21+.SH DESCRIPTION
22+determine_maximum_mpps will find the maximum mpps parameter which can sustain
23+.PP
24+.RS
25+1. 10 consecutive 30 second runs.
26+.br
27+2. 1 run of 10 minutes.
28+.PP
29+.RE
30+Without violating the latency specified with $MAXLAT (default 20000)
31+.PP
32+.SH TERMINOLOGY
33+mpps : million-packets-per-second
34+.br
35+.SH OPTIONS
36+.TP
37+.B \-a cpulist
38+List of processors to run on. The default is processor 0
39+.br
40+Numbers are separated by commas and may include ranges. Eg. 0,3,7\-11
41+.TP
42+.B \-m maxlat
43+Maximum latency in nanoseconds. The default is 20000. If the maximum is exceeded, that run of queuelat quits.
44+.TP
45+.B \-n cycles
46+Extimated number of cycles it takes to process one packet. The default is 300
47+.TP
48+.B \-f
49+Set the scheduling policy to SCHED_FIFO. This is the default if not specified.
50+.TP
51+.B \-r
52+Set the scheduling policy to SCHED_RR.
53+.TP
54+.B \-p priority
55+default priority = 1. Valid numbers are from 1 to 99
56+.TP
57+.B \-h
58+help
59+.LP
60+.SH AUTHOR
61+determine_maximum_mpps was written by
62+.br
63+Marcelo Tosatti <mtosatti@redhat.com>
64+.PP
65+This man page was written by John Kacur <jkacur@redhat.com>
@@ -0,0 +1,212 @@
1+#!/bin/bash
2+ 
3+# SPDX-License-Identifier: GPL-2.0-or-later
4+# Copyright (C) 2018 Marcelo Tosatti <mtosatti@redhat.com>
5+ 
6+# A script to determine the maximum mpps. Logic:
7+# Increase mpps in 0.5 units
8+#
9+MAXLAT="20000"
10+CYCLES_PER_PACKET="300"
11+OUTFILE=/usr/tmp/outfile
12+PRIO=1
13+CPULIST=0
14+SCHED=""
15+ 
16+usage()
17+{
18+ echo "Usage:"
19+ echo "$(basename $0) [OPTIONS]"
20+ echo
21+ echo "-a cpulist"
22+ echo " List of processors to run on. The default is processor 0"
23+ echo " Numbers are separated by commas and may include ranges. Eg. 0,3,7-11"
24+ echo "-m maxlat"
25+ echo " maximum latency in nanoseconds. The default is 20000"
26+ echo " if the maximum is exceeded, that run of queuelat quits"
27+ echo "-n cycles"
28+ echo " Estimated number of cycles it takes to process one packet"
29+ echo " The default is 300"
30+ echo "-f"
31+ echo " Set the scheduling policy to SCHED_FIFO."
32+ echo " This is the default if not specified"
33+ echo "-r"
34+ echo " Set the scheduling policy to SCHED_RR".
35+ echo "-p priority"
36+ echo " default priority = 1. Valid numbers are from 1 to 99"
37+ echo "-h"
38+ echo " help"
39+ echo " print this help message and exit"
40+ exit
41+}
42+ 
43+get_cpuinfo_mhz()
44+{
45+ # Try to determine MHz from /proc/cpuinfo
46+ FREQ_MHZ=$(grep "cpu MHz" /proc/cpuinfo | cut -f 3 -d " " | sort -rn | head -n1)
47+ 
48+ # Try to determine MHz from /sys/firmware/devicetree/base/cpus/timebase-frequency
49+ if [ -z $FREQ_MHZ ]; then
50+ FREQ_MHZ=$(($((16#$(hexdump -e '1/1 "%02X"' /sys/firmware/devicetree/base/cpus/timebase-frequency)))/1000000))
51+ fi
52+ 
53+ echo "$FREQ_MHZ"
54+}
55+ 
56+# Check that the scheduling policy hasn't already been set
57+# Exit with an error message if it has
58+check_sched()
59+{
60+ if [ "${SCHED}" != "" ]; then
61+ echo "Specify -f or -r, but not both"
62+ usage
63+ fi
64+}
65+ 
66+# Process command line options
67+while getopts ":a:frp:m:n:h" opt; do
68+ case ${opt} in
69+ a ) CPULIST="${OPTARG}" ;;
70+ m ) MAXLAT="${OPTARG}" ;;
71+ n ) CYCLES_PER_PACKET="${OPTARG}" ;;
72+ f ) check_sched; SCHED="-f" ;;
73+ r ) check_sched; SCHED="-r" ;;
74+ p ) PRIO="${OPTARG}" ;;
75+ h ) usage ;;
76+ * ) echo "no such option"; usage ;;
77+ esac
78+done
79+ 
80+shift $((OPTIND -1 ))
81+ 
82+# If the user hasn't specified a scheduling policy
83+# then set it to the default SCHED_FIFO
84+if [ "${SCHED}" == "" ]; then
85+ SCHED="-f"
86+fi
87+ 
88+# Error checking that the user entered a priority between 1 and 99
89+if [[ "${PRIO}" -lt "1" ]] || [[ "${PRIO}" -gt "99" ]]; then
90+ echo "PRIO must be a number between 1 and 99"
91+ usage
92+fi
93+ 
94+PREAMBLE="taskset -c ${CPULIST} chrt ${SCHED} ${PRIO}"
95+ 
96+echo "Determining maximum mpps the machine can handle"
97+echo "Will take a few minutes to determine mpps value"
98+echo "And 10 minutes run to confirm the final mpps value is stable"
99+ 
100+for mpps in $(seq 3 3 50); do
101+ echo testing "$mpps" Mpps
102+ 
103+ OUTFILE=$(mktemp)
104+ $PREAMBLE queuelat -m $MAXLAT -c $CYCLES_PER_PACKET -f "$(get_cpuinfo_mhz)" -p "$mpps" -t 30 > "$OUTFILE"
105+ 
106+ exceeded=$(grep exceeded "$OUTFILE")
107+ if [ ! -z "$exceeded" ]; then
108+ echo mpps failed: "$mpps"
109+ break;
110+ fi
111+ echo success
112+done
113+echo first loop mpps: "$mpps"
114+ 
115+first_mpps=$(($mpps - 1))
116+for mpps in $(seq $first_mpps -1 3); do
117+ echo testing "$mpps" Mpps
118+ 
119+ OUTFILE=$(mktemp)
120+ $PREAMBLE queuelat -m $MAXLAT -c $CYCLES_PER_PACKET -f "$(get_cpuinfo_mhz)" -p "$mpps" -t 30 > "$OUTFILE"
121+ 
122+ exceeded=$(grep exceeded "$OUTFILE")
123+ if [ -z "$exceeded" ]; then
124+ echo mpps success "$mpps"
125+ break;
126+ fi
127+ echo failure
128+done
129+ 
130+second_mpps=$(echo "$mpps + 0.3" | bc)
131+echo second loop mpps: "$mpps"
132+ 
133+for mpps in $(seq "$second_mpps" 0.3 $first_mpps); do
134+ echo testing "$mpps" Mpps
135+ 
136+ OUTFILE=$(mktemp)
137+ $PREAMBLE queuelat -m $MAXLAT -c $CYCLES_PER_PACKET -f "$(get_cpuinfo_mhz)" -p "$mpps" -t 30 > "$OUTFILE"
138+ 
139+ exceeded=$(grep exceeded "$OUTFILE")
140+ if [ ! -z "$exceeded" ]; then
141+ echo mpps failure "$mpps"
142+ break;
143+ fi
144+ echo success
145+done
146+ 
147+echo third loop mpps: "$mpps"
148+third_mpps=$(echo "$mpps -0.1" | bc)
149+ 
150+for mpps in $(seq "$third_mpps" -0.1 3); do
151+ echo testing "$mpps" Mpps
152+ 
153+ OUTFILE=$(mktemp)
154+ $PREAMBLE queuelat -m $MAXLAT -c $CYCLES_PER_PACKET -f "$(get_cpuinfo_mhz)" -p "$mpps" -t 30 > "$OUTFILE"
155+ 
156+ exceeded=$(grep exceeded "$OUTFILE")
157+ if [ -z "$exceeded" ]; then
158+ echo mpps success "$mpps"
159+ break;
160+ fi
161+ echo failure
162+done
163+ 
164+export queuelat_failure=1
165+while [ $queuelat_failure == 1 ]; do
166+ 
167+ export queuelat_failure=0
168+ 
169+ echo -n "Starting 10 runs of 30 seconds with "
170+ echo "$mpps Mpps"
171+ 
172+ for i in $(seq 1 10); do
173+ $PREAMBLE queuelat -m $MAXLAT -c $CYCLES_PER_PACKET -f "$(get_cpuinfo_mhz)" -p "$mpps" -t 30 > "$OUTFILE"
174+ exceeded=$(grep exceeded "$OUTFILE")
175+ 
176+ if [ ! -z "$exceeded" ]; then
177+ echo "mpps failure (run $i) $mpps"
178+ export queuelat_failure=1
179+ mpps=$(echo "$mpps" - 0.1 | bc)
180+ export mpps
181+ break
182+ fi
183+ echo "run $i success"
184+ done
185+ 
186+done
187+ 
188+export queuelat_failure=1
189+while [ $queuelat_failure == 1 ]; do
190+ 
191+ export queuelat_failure=0
192+ 
193+ echo -n "Starting 10 minutes run with "
194+ echo "$mpps Mpps"
195+ 
196+ $PREAMBLE queuelat -m $MAXLAT -c $CYCLES_PER_PACKET -f "$(get_cpuinfo_mhz)" -p "$mpps" -t 600 > "$OUTFILE"
197+ exceeded=$(grep exceeded "$OUTFILE")
198+ 
199+ if [ ! -z "$exceeded" ]; then
200+ echo "mpps failure (run $i) $mpps"
201+ export queuelat_failure=1
202+ mpps=$(echo "$mpps" - 0.1 | bc)
203+ export mpps
204+ continue
205+ fi
206+ echo "run $i success"
207+done
208+ 
209+echo Final mpps is: "$mpps"
210+ 
211+unset queuelat_failure
212+unset mpps
@@ -0,0 +1,51 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH QUEUELAT 8 "Sept 3, 2018"
3+# SPDX-License-Identifier: GPL-2.0-or-later
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+queuelat \- Queue latency test program
18+.SH SYNOPSIS
19+.LP
20+queuelat [-c|--cycles N] [-f|--freq F] [-h|--help] [-m|--max-len LEN] [-p|--packets F] [-q|--queue-len N] [-t|--timeout TIME]
21+.SH DESCRIPTION
22+queuelat simulates a network queue checking for latency
23+violations in packet processing.
24+ 
25+.SH OPTIONS
26+A summary of options is included below.
27+.TP
28+.B \-c, \-\-cycles=N
29+Estimated number of cycles it takes to process one packet. This value should come from the envisioned packet forwarding application being simulated.
30+.TP
31+.B \-f, \-\-freq=F
32+TSC frequency in MHz.
33+.TP
34+.B \-h, \-\-help
35+Show help
36+.TP
37+.B \-m, \-\-max-len=N
38+Maximum allowed latency, in nanoseconds. If latency to process any packet exceeds this value, the program quits, writing a message to the trace buffer.
39+.TP
40+.B \-p, \-\-packets=F
41+Million packets per second that arrive for processing.
42+.TP
43+.B \-q, \-\-queue-len=N
44+Minimum queue length to print in the trace
45+.TP
46+.B \-t, \-\-timeout=TIME
47+Timeout in seconds to quit the program.
48+ 
49+.SH AUTHOR
50+queuelat was written by Marcelo Tosatti <mtosatti@redhat.com>
51+.br
@@ -0,0 +1,673 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+ 
3+/*
4+ * Copyright (C) 2018 Marcelo Tosatti <mtosatti@redhat.com>
5+ * Copyright (C) 2019 John Kacur <jkacur@redhat.com>
6+ * Copyright (C) 2019 Clark Williams <williams@redhat.com>
7+ */
8+ 
9+#include <string.h>
10+#include <stdlib.h>
11+#include <sys/types.h>
12+#include <sys/stat.h>
13+#include <fcntl.h>
14+#include <ctype.h>
15+#include <stdio.h>
16+#include <unistd.h>
17+#include <signal.h>
18+#include <time.h>
19+#include <getopt.h>
20+ 
21+#include "rt-utils.h"
22+ 
23+/* Program parameters:
24+ * max_queue_len: maximum latency allowed, in nanoseconds (int).
25+ * cycles_per_packet: number of cycles to process one packet (int).
26+ * mpps(million-packet-per-sec): million packets per second (float).
27+ * tsc_freq_mhz: TSC frequency in MHz, as measured by TSC PIT calibration
28+ * (search for "Detected XXX MHz processor" in dmesg, and use the integer part).
29+ *
30+ * How it works
31+ * ============
32+ *
33+ * The program in essence does:
34+ *
35+ * b = rdtsc();
36+ * memmove(dest, src, n);
37+ * a = rdtsc();
38+ *
39+ * delay = convert_to_ns(a - b);
40+ *
41+ * queue_size += packets_queued_in(delay);
42+ * queue_size -= packets_processed;
43+ *
44+ * if (queue_size > max_queue_len)
45+ * FAIL();
46+ *
47+ * packets_processed is fixed, and is estimated as follows:
48+ * n is determined first, so that the stats bucket with highest count
49+ * takes max_latency/2.
50+ * for max_latency/2, we calculate how many packets can be drained
51+ * in that time (using cycles_per_packet).
52+ *
53+ */
54+ 
55+int maxlatency;
56+int cycles_per_packet;
57+float mpps;
58+int timeout_secs;
59+int min_queue_size_to_print;
60+ 
61+/* Derived constants */
62+ 
63+float cycles_to_ns;
64+int max_queue_len;
65+ 
66+int default_n;
67+int nr_packets_drain_per_block;
68+ 
69+/*
70+ * Parameters for the stats collection buckets
71+ */
72+ 
73+#define LAST_VAL 70000
74+#define VALS_PER_BUCKET 100
75+#define NR_BUCKETS LAST_VAL/VALS_PER_BUCKET
76+ 
77+unsigned long long buckets[NR_BUCKETS+1];
78+unsigned long long total_count;
79+ 
80+#define OUTLIER_BUCKET NR_BUCKETS
81+ 
82+static int val_to_bucket(unsigned long long val)
83+{
84+ int bucket_nr = val / VALS_PER_BUCKET;
85+ if (bucket_nr >= NR_BUCKETS)
86+ return OUTLIER_BUCKET;
87+ return bucket_nr;
88+}
89+ 
90+static void account(unsigned long long val)
91+{
92+ int bucket_nr = val_to_bucket(val);
93+ buckets[bucket_nr]++;
94+ total_count++;
95+}
96+ 
97+static unsigned long long total_samples(void)
98+{
99+ int i;
100+ unsigned long long total = 0;
101+ 
102+ for (i = 0; i <= OUTLIER_BUCKET; i++)
103+ total += buckets[i];
104+ 
105+ return total;
106+}
107+ 
108+static void print_all_buckets(void)
109+{
110+ int i, print_dotdotdot = 0;
111+ 
112+ for (i = 0; i <= OUTLIER_BUCKET; i++) {
113+ int bucket_nr;
114+ unsigned long long val = i*VALS_PER_BUCKET;
115+ 
116+ bucket_nr = val_to_bucket(val);
117+ 
118+ if (bucket_nr != OUTLIER_BUCKET) {
119+ int n_bucketnr = bucket_nr+1;
120+ if (buckets[bucket_nr] == buckets[n_bucketnr]) {
121+ print_dotdotdot = 1;
122+ continue;
123+ }
124+ if (print_dotdotdot) {
125+ printf("...\n");
126+ print_dotdotdot = 0;
127+ }
128+ printf("[%lld - %lld] = %lld\n", val,
129+ val + VALS_PER_BUCKET-1,
130+ buckets[bucket_nr]);
131+ } else {
132+ if (print_dotdotdot) {
133+ printf("...\n");
134+ print_dotdotdot = 0;
135+ }
136+ printf("[%lld - END] = %lld\n", val,
137+ buckets[bucket_nr]);
138+ }
139+ }
140+}
141+ 
142+static void print_max_bucketsec(void)
143+{
144+ int i, bucket_nr;
145+ unsigned long long highest_val = 0;
146+ 
147+ for (i = 0; i <= OUTLIER_BUCKET; i++) {
148+ unsigned long long val = i*VALS_PER_BUCKET;
149+ 
150+ bucket_nr = val_to_bucket(val);
151+ 
152+ if (buckets[bucket_nr] != 0)
153+ highest_val = val;
154+ }
155+ 
156+ bucket_nr = val_to_bucket(highest_val);
157+ printf("Max loop processing time: [%lld - %lld] = %lld\n", highest_val,
158+ highest_val + VALS_PER_BUCKET-1,
159+ buckets[bucket_nr]);
160+ 
161+ return;
162+}
163+ 
164+static void print_min_bucketsec(void)
165+{
166+ int i, bucket_nr;
167+ unsigned long long min_val = 0;
168+ 
169+ for (i = 0; i <= OUTLIER_BUCKET; i++) {
170+ unsigned long long val = i*VALS_PER_BUCKET;
171+ 
172+ bucket_nr = val_to_bucket(val);
173+ 
174+ if (buckets[bucket_nr] != 0) {
175+ min_val = val;
176+ break;
177+ }
178+ }
179+ 
180+ bucket_nr = val_to_bucket(min_val);
181+ printf("Min loop processing time: [%lld - %lld] = %lld\n", min_val,
182+ min_val + VALS_PER_BUCKET-1,
183+ buckets[bucket_nr]);
184+ 
185+ return;
186+}
187+ 
188+static void print_avg_bucketsec(void)
189+{
190+ int i, bucket_nr;
191+ unsigned long long total_sum = 0;
192+ unsigned long long nr_hits = 0;
193+ 
194+ for (i = 0; i <= OUTLIER_BUCKET; i++) {
195+ unsigned long long val = i*VALS_PER_BUCKET;
196+ unsigned long long maxtime;
197+ 
198+ bucket_nr = val_to_bucket(val);
199+ 
200+ maxtime = val + VALS_PER_BUCKET-1;
201+ total_sum = total_sum + maxtime*buckets[bucket_nr];
202+ 
203+ nr_hits = nr_hits + buckets[bucket_nr];
204+ }
205+ 
206+ printf("Avg loop processing time: %lld\n", total_sum / nr_hits);
207+}
208+ 
209+static void print_all_buckets_drainlength(void)
210+{
211+ int i, print_dotdotdot = 0;
212+ 
213+ for (i = 0; i <= OUTLIER_BUCKET; i++) {
214+ int bucket_nr;
215+ unsigned long long val = i*VALS_PER_BUCKET;
216+ 
217+ bucket_nr = val_to_bucket(val);
218+ 
219+ if (bucket_nr != OUTLIER_BUCKET) {
220+ unsigned long long mindelta, maxdelta;
221+ int nr_packets_minfill, nr_packets_maxfill;
222+ int n_bucketnr = bucket_nr+1;
223+ 
224+ if (buckets[bucket_nr] == buckets[n_bucketnr]) {
225+ print_dotdotdot = 1;
226+ continue;
227+ }
228+ if (print_dotdotdot) {
229+ printf("...\n");
230+ print_dotdotdot = 0;
231+ }
232+ 
233+ mindelta = val;
234+ maxdelta = val + VALS_PER_BUCKET-1;
235+ 
236+ nr_packets_minfill = mindelta * mpps * 1000000 / NSEC_PER_SEC;
237+ nr_packets_maxfill = maxdelta * mpps * 1000000 / NSEC_PER_SEC;
238+ 
239+ printf("[%lld - %lld] = %lld packetfillrates=[%d - %d]\n", val,
240+ val + VALS_PER_BUCKET-1,
241+ buckets[bucket_nr],
242+ nr_packets_minfill,
243+ nr_packets_maxfill);
244+ } else {
245+ if (print_dotdotdot) {
246+ printf("...\n");
247+ print_dotdotdot = 0;
248+ }
249+ printf("[%lld - END] = %lld\n", val,
250+ buckets[bucket_nr]);
251+ }
252+ }
253+}
254+ 
255+typedef unsigned long long cycles_t;
256+typedef unsigned long long usecs_t;
257+typedef unsigned long long u64;
258+ 
259+#if defined __x86_64__ || defined __i386__
260+ 
261+#ifdef __x86_64__
262+#define DECLARE_ARGS(val, low, high) unsigned low, high
263+#define EAX_EDX_VAL(val, low, high) ((low) | ((u64)(high) << 32))
264+#define EAX_EDX_ARGS(val, low, high) "a" (low), "d" (high)
265+#define EAX_EDX_RET(val, low, high) "=a" (low), "=d" (high)
266+#else
267+#define DECLARE_ARGS(val, low, high) unsigned long long val
268+#define EAX_EDX_VAL(val, low, high) (val)
269+#define EAX_EDX_ARGS(val, low, high) "A" (val)
270+#define EAX_EDX_RET(val, low, high) "=A" (val)
271+#endif
272+ 
273+static inline unsigned long long __rdtscll(void)
274+{
275+ DECLARE_ARGS(val, low, high);
276+ 
277+ asm volatile("mfence; rdtsc" : EAX_EDX_RET(val, low, high));
278+ 
279+ return EAX_EDX_VAL(val, low, high);
280+}
281+ 
282+#define gettick(val) do { (val) = __rdtscll(); } while (0)
283+ 
284+#else
285+ 
286+static inline unsigned long long __clock_gettime(void)
287+{
288+ struct timespec now;
289+ int ret;
290+ 
291+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
292+ if (ret < 0)
293+ return 0;
294+ 
295+ // Combine seconds and nanoseconds into a single value in nanoseconds
296+ return (unsigned long long)now.tv_sec * 1000000000ULL + now.tv_nsec;
297+}
298+ 
299+#define gettick(val) do { (val) = __clock_gettime(); } while (0)
300+ 
301+#endif
302+ 
303+static void init_buckets(void)
304+{
305+ int i;
306+ 
307+ for (i=0; i <= NR_BUCKETS; i++)
308+ buckets[i] = 0;
309+ 
310+ total_count = 0;
311+}
312+ 
313+static int find_highest_count_bucket(void)
314+{
315+ int i;
316+ int max_bucket = 0;
317+ unsigned long long max_val = 0;
318+ 
319+ for (i=0; i <= NR_BUCKETS; i++) {
320+ if (buckets[i] > max_val) {
321+ max_bucket = i;
322+ max_val = buckets[i];
323+ }
324+ }
325+ 
326+ return max_bucket;
327+}
328+ 
329+int tracing_mark_fd;
330+static void trace_open(void)
331+{
332+ int fd;
333+ 
334+ fd = open("/sys/kernel/debug/tracing/trace_marker", O_RDWR);
335+ 
336+ if (fd == -1) {
337+ perror("open");
338+ exit(0);
339+ }
340+ tracing_mark_fd = fd;
341+}
342+ 
343+static void trace_write(char *buf, int len)
344+{
345+ int ret;
346+ 
347+ ret = write(tracing_mark_fd, buf, len);
348+ if (ret == -1) {
349+ perror("write");
350+ exit(0);
351+ }
352+}
353+ 
354+static void run_n(int n)
355+{
356+ u64 a, b, delta;
357+ void *dest, *src;
358+ int i, loops = 50000;
359+ 
360+ init_buckets();
361+ 
362+ dest = malloc(n);
363+ if (dest == NULL) {
364+ printf("failure to allocate %d bytes "
365+ " for dest\n", n);
366+ exit(0);
367+ }
368+ src = malloc(n);
369+ if (src == NULL) {
370+ printf("failure to allocate %d bytes "
371+ " for src\n", n);
372+ exit(0);
373+ }
374+ 
375+ memset(src, 0, n);
376+ 
377+ memmove(dest, src, n);
378+ for (i = 0; i < loops; i++) {
379+ gettick(b);
380+ memmove(dest, src, n);
381+ gettick(a);
382+ delta = (a - b) * cycles_to_ns;
383+ account(delta);
384+ }
385+ 
386+ free(dest);
387+ free(src);
388+ 
389+ return;
390+}
391+ 
392+/*
393+ * Find the size of n such that the stats for the
394+ * function call
395+ *
396+ * memmove(dest, src, n).
397+ *
398+ * Takes MaximumLat/2 in the bucket that has most
399+ * entries.
400+ *
401+ */
402+static int measure_n(void)
403+{
404+ int time, bucket_nr;
405+ int n = 100000, delta = 0;
406+ 
407+ do {
408+ if (delta > 0)
409+ n = n+1000;
410+ else if (delta < 0)
411+ n = n-1000;
412+ 
413+ run_n(n);
414+ bucket_nr = find_highest_count_bucket();
415+ 
416+ time = bucket_nr * VALS_PER_BUCKET;
417+ 
418+ delta = maxlatency/2 - time;
419+ } while (abs(delta) > VALS_PER_BUCKET*2);
420+ 
421+ return n;
422+}
423+ 
424+static void convert_to_ghz(double tsc_freq_mhz)
425+{
426+ float tsc_freq_ghz = tsc_freq_mhz/1000;
427+ 
428+ cycles_to_ns = 1/tsc_freq_ghz;
429+ 
430+ printf("tsc_freq_ghz = %f, cycles_to_ns = %f\n", tsc_freq_ghz,
431+ cycles_to_ns);
432+}
433+ 
434+ 
435+static void print_exit_info(void)
436+{
437+ print_all_buckets();
438+ printf("\n ---------------- \n");
439+ print_min_bucketsec();
440+ print_max_bucketsec();
441+ print_avg_bucketsec();
442+ 
443+}
444+ 
445+void main_loop(void)
446+{
447+ u64 a, b, delta;
448+ void *dest, *src;
449+ int queue_size = 0;
450+ 
451+ trace_open();
452+ 
453+ init_buckets();
454+ 
455+ dest = malloc(default_n);
456+ if (dest == NULL) {
457+ printf("failure to allocate %d bytes "
458+ " for dest\n", default_n);
459+ exit(0);
460+ }
461+ src = malloc(default_n);
462+ if (src == NULL) {
463+ printf("failure to allocate %d bytes "
464+ " for src\n", default_n);
465+ exit(0);
466+ }
467+ 
468+ memset(src, 0, default_n);
469+ memmove(dest, src, default_n);
470+ 
471+ while (1) {
472+ char buf[500];
473+ int ret;
474+ int nr_packets_fill;
475+ 
476+ gettick(b);
477+ memmove(dest, src, default_n);
478+ gettick(a);
479+ delta = (a - b) * cycles_to_ns;
480+ account(delta);
481+ 
482+ /* fill up the queue by the amount of
483+ * time that passed */
484+ nr_packets_fill = delta * mpps * 1000000 / NSEC_PER_SEC;
485+ queue_size += nr_packets_fill;
486+ 
487+ /* decrease the queue by the amount of packets
488+ * processed in maxlatency/2 nanoseconds of
489+ * full processing.
490+ */
491+ 
492+ queue_size -= nr_packets_drain_per_block;
493+ 
494+ if (queue_size < 0)
495+ queue_size = 0;
496+ 
497+ if (queue_size <= min_queue_size_to_print)
498+ continue;
499+ 
500+ ret = sprintf(buf, "memmove block queue_size=%d queue_dec=%d"
501+ " queue_inc=%d delta=%llu ns\n", queue_size,
502+ nr_packets_drain_per_block,
503+ nr_packets_fill, delta);
504+ trace_write(buf, ret);
505+ 
506+ if (queue_size > max_queue_len) {
507+ printf("queue length exceeded: "
508+ " queue_size=%d max_queue_len=%d\n",
509+ queue_size, max_queue_len);
510+ ret = sprintf(buf, "queue length exceeded: "
511+ "queue_size=%d max_queue_len=%d\n",
512+ queue_size, max_queue_len);
513+ trace_write(buf, ret);
514+ print_exit_info();
515+ exit(0);
516+ }
517+ }
518+ 
519+ free(dest);
520+ free(src);
521+}
522+ 
523+void sig_handler(int sig __attribute__ ((unused)))
524+{
525+ print_exit_info();
526+ exit(0);
527+}
528+ 
529+static void install_signals(void)
530+{
531+ signal(SIGALRM, sig_handler);
532+ signal(SIGINT, sig_handler);
533+}
534+ 
535+int calculate_nr_packets_drain_per_block(void)
536+{
537+ unsigned long long maxcount;
538+ int i, time;
539+ int found = 0;
540+ int bucket_nr = find_highest_count_bucket();
541+ 
542+ maxcount = total_samples() / 40;
543+ 
544+ for (i = bucket_nr+1; i <= NR_BUCKETS; i++) {
545+ if (buckets[i] < maxcount) {
546+ found = 1;
547+ break;
548+ }
549+ }
550+ 
551+ if (found == 0) {
552+ printf("error, did not find right bucket with < 10%% of total\n");
553+ exit(0);
554+ }
555+ 
556+ time = i*VALS_PER_BUCKET + VALS_PER_BUCKET-1;
557+ nr_packets_drain_per_block = time / (cycles_per_packet*cycles_to_ns);
558+ 
559+ return nr_packets_drain_per_block;
560+}
561+ 
562+static void print_help(int error)
563+{
564+ printf("queuelat V %1.2f\n", VERSION);
565+ printf("Usage:\n"
566+ "queuelat <options>\n\n"
567+ "-c N --cycles N number of cycles to process one packet (int)\n"
568+ "-f F --freq F TSC frequency in MHz (float)\n"
569+ "-h --help show this help menu\n"
570+ "-m LEN --max-len LEN maximum latency allowed, in nanoseconds (int)\n"
571+ "-p F --packets F million packets per second (float)\n"
572+ "-q N --queue-len N minimum queue len to print trace (int)\n"
573+ "-t TIME --timeout TIME timeout, in seconds (int)\n"
574+ );
575+ exit(error);
576+}
577+ 
578+int main(int argc, char **argv)
579+{
580+ double tsc_freq_mhz;
581+ float max_queue_len_f;
582+ char *mvalue = NULL;
583+ char *cvalue = NULL;
584+ char *pvalue = NULL;
585+ char *fvalue = NULL;
586+ char *tvalue = NULL;
587+ char *qvalue = NULL;
588+ 
589+ opterr = 0;
590+ 
591+ for (;;) {
592+ static struct option options[] = {
593+ {"cycles", required_argument, NULL, 'c'},
594+ {"freq", required_argument, NULL, 'f'},
595+ {"help", no_argument, NULL, 'h'},
596+ {"max-len", required_argument, NULL, 'm'},
597+ {"packets", required_argument, NULL, 'p'},
598+ {"queue-len", required_argument, NULL, 'q'},
599+ {"timeout", required_argument, NULL, 't'},
600+ {NULL, 0, NULL, 0}
601+ };
602+ int c = getopt_long(argc, argv, "c:f:hm:p:q:t:", options, NULL);
603+ if (c == -1)
604+ break;
605+ switch (c) {
606+ case 'c':
607+ cvalue = optarg;
608+ break;
609+ case 'f':
610+ fvalue = optarg;
611+ break;
612+ case '?':
613+ case 'h':
614+ print_help(0);
615+ break;
616+ case 'm':
617+ mvalue = optarg;
618+ break;
619+ case 'p':
620+ pvalue = optarg;
621+ break;
622+ case 'q':
623+ qvalue = optarg;
624+ break;
625+ case 't':
626+ tvalue = optarg;
627+ break;
628+ default:
629+ print_help(1);
630+ break;
631+ }
632+ }
633+ 
634+ if (mvalue == NULL || cvalue == NULL || pvalue == NULL || fvalue == NULL) {
635+ printf("options -m, -c, -p and -f are required\n");
636+ exit(1);
637+ }
638+ 
639+ install_signals();
640+ 
641+ maxlatency = atoi(mvalue);
642+ cycles_per_packet = atoi(cvalue);
643+ mpps = atof(pvalue);
644+ tsc_freq_mhz = atof(fvalue);
645+ 
646+ if (tvalue) {
647+ int alarm_secs;
648+ alarm_secs = atoi(tvalue);
649+ alarm(alarm_secs);
650+ }
651+ 
652+ if (qvalue)
653+ min_queue_size_to_print = atoi(qvalue);
654+ 
655+ convert_to_ghz(tsc_freq_mhz);
656+ 
657+ max_queue_len_f = maxlatency / (cycles_per_packet*cycles_to_ns);
658+ max_queue_len = max_queue_len_f;
659+ 
660+ printf("max_queue_len = %d\n", max_queue_len);
661+ default_n = measure_n();
662+ 
663+ nr_packets_drain_per_block = calculate_nr_packets_drain_per_block();
664+ print_all_buckets_drainlength();
665+ 
666+ printf("default_n=%d nr_packets_drain_per_block=%d\n", default_n,
667+ nr_packets_drain_per_block);
668+ 
669+ main_loop();
670+ 
671+ return 0;
672+}
673+ 
@@ -0,0 +1,3 @@
1+# SPDX-License-Identifier: GPL-2.0-or-later
2+obj-m := targeted-ipi.o
3+ 
@@ -0,0 +1,2 @@
1+# SPDX-License-Identifier: GPL-2.0-or-later
2+obj-$(CONFIG_TARGETED_IPI) += targeted-ipi.o
@@ -0,0 +1,12 @@
1+ 
2+To build:
3+ 
4+make -C <path_to_kernel_src> M=$PWD
5+ 
6+To build against the running kernel use:
7+ 
8+make -C /lib/modules/`uname -r`/build M=$PWD
9+ 
10+ 
11+Read targeted-ipi.c for the parameters.
12+ 
@@ -0,0 +1,58 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+/*
3+ * Copyright (C) 2018 Marcelo Tosatti <mtosatti@redhat.com>
4+ * Copyright (C) 2019 John Kacur <jkacur@redhat.com>
5+ * Copyright (C) 2019 Clark Williams <williams@redhat.com>
6+ */
7+ 
8+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9+ 
10+#include <linux/module.h>
11+#include <linux/kernel.h>
12+#include <linux/smp.h>
13+#include <asm-generic/delay.h>
14+ 
15+static int ipidest;
16+module_param(ipidest, int, S_IRUGO);
17+ 
18+/* number of ipis */
19+static int nripis;
20+module_param(nripis, int, S_IRUGO);
21+ 
22+/* interval between consecutive IPI calls */
23+static int interval;
24+module_param(interval, int, S_IRUGO);
25+ 
26+/* how many microseconds to delay in IPI handler */
27+static int delay;
28+module_param(delay, int, S_IRUGO);
29+ 
30+static void ipi_handler(void *info)
31+{
32+ udelay(interval);
33+}
34+ 
35+static int targeted_ipi_init(void)
36+{
37+ int ret, i;
38+ 
39+ for (i=0; i < nripis; i++)
40+ {
41+ ret = smp_call_function_single(ipidest, ipi_handler, NULL, 1);
42+ if (ret) {
43+ printk(KERN_ERR "i=%d smp_call_function_single ret=%d\n", i, ret);
44+ return 0;
45+ }
46+ udelay(interval);
47+ }
48+ 
49+ return 0;
50+}
51+ 
52+static void targeted_ipi_exit(void)
53+{
54+}
55+ 
56+module_init(targeted_ipi_init);
57+module_exit(targeted_ipi_exit);
58+MODULE_LICENSE("GPL");
@@ -0,0 +1,65 @@
1+.\"
2+.TH RT-MIGRATE-TEST 8 "September 18, 2020"
3+# SPDX-License-Identifier: GPL-2.0-only
4+.\" Please adjust this date whenever editing this manpage
5+.SH NAME
6+rt-migrate-test \- real-time task migration program
7+.SH SYNOPSIS
8+.LP
9+rt-migrate-test [-c|--check] [-D|--duration TIME] [-e|--equal] [-h|--help] [--json FILENAME] [-l|--loops LOOPS] [-m|--maxerr TIME] [-p|--prio PRIO] [-r|--run-time TIME] [-s|--sleep-time TIME] [NR_TASKS]
10+.SH DESCRIPTION
11+Test real-time multiprocessor scheduling of tasks to ensure the highest priority tasks are running on all available CPUs
12+.SH OPTIONS
13+This program follows the usual GNU command line syntax, with long options starting with two dashes ('\-\-').
14+.br
15+.TP
16+In the summary of options, a value in brackets (), indicates a default value
17+.br
18+.TP
19+.B \-c, \-\-check
20+Stop if lower prio task is quicker than higher (off)
21+.TP
22+.B \-D, \-\-duration=TIME
23+Specify a length for the test run.
24+.br
25+Append 'm', 'h', or 'd' to specify minutes, hours or days.
26+.TP
27+.B \-e, \-\-equal
28+Use equal prio for #CPU-1 tasks (requires > 2 CPUS)
29+.br
30+.TP
31+.B \-h, \-\-help
32+Display usage
33+.br
34+.TP
35+.B \-\-json=FILENAME
36+Write final results into FILENAME, JSON formatted.
37+.br
38+.TP
39+.B \-l \-\-loops=LOOPS
40+Number of iterations to run (50)
41+.br
42+.TP
43+.B \-m, \-\-maxerr=TIME
44+Max allowed error (microsecs)
45+.br
46+.TP
47+.B \-p, \-\-prio=PRIO
48+base priority to start RT tasks with (2)
49+.br
50+.TP
51+.B \-r, \-\-run\-time=TIME
52+Run time (ms) to busy loop the threads (20)
53+.br
54+.TP
55+.B \-s, \-\-sleep\-time=TIME
56+Sleep time (ms) between intervals (100)
57+.br
58+.TP
59+.B [NR_TASKS]
60+number of tasks to run (number of cpus + 1)
61+.br
62+.SH AUTHOR
63+rt-migrate-test was written by Steven Rostedt <srostedt@redhat.com>
64+.TP
65+This manual page was written by John Kacur <jkacur@redhat.com>
@@ -0,0 +1,685 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * rt-migrate-test.c
4+ * Copyright (C) 2007-2009 Steven Rostedt <srostedt@redhat.com>
5+ */
6+#include <stdio.h>
7+#ifndef __USE_XOPEN2K
8+# define __USE_XOPEN2K
9+#endif
10+#include <stdlib.h>
11+#include <string.h>
12+#include <getopt.h>
13+#include <stdarg.h>
14+#include <unistd.h>
15+#include <ctype.h>
16+#include <time.h>
17+#include <sys/types.h>
18+#include <sys/stat.h>
19+#include <fcntl.h>
20+#include <signal.h>
21+#include <sys/time.h>
22+#include <sys/syscall.h>
23+#include <errno.h>
24+#include <sched.h>
25+#include <pthread.h>
26+#include <inttypes.h>
27+ 
28+#include <linux/unistd.h>
29+ 
30+#include "rt-utils.h"
31+ 
32+int nr_tasks;
33+int lfd;
34+ 
35+static int mark_fd = -1;
36+static __thread char buff[BUFSIZ+1];
37+ 
38+static void setup_ftrace_marker(void)
39+{
40+ struct stat st;
41+ char *files[] = {
42+ "/sys/kernel/debug/tracing/trace_marker",
43+ "/debug/tracing/trace_marker",
44+ "/debugfs/tracing/trace_marker",
45+ };
46+ int ret;
47+ unsigned int i;
48+ 
49+ for (i = 0; i < (sizeof(files) / sizeof(char *)); i++) {
50+ ret = stat(files[i], &st);
51+ if (ret >= 0)
52+ goto found;
53+ }
54+ /* todo, check mounts system */
55+ return;
56+found:
57+ mark_fd = open(files[i], O_WRONLY);
58+}
59+ 
60+static void ftrace_write(const char *fmt, ...)
61+{
62+ va_list ap;
63+ int n;
64+ 
65+ if (mark_fd < 0)
66+ return;
67+ 
68+ va_start(ap, fmt);
69+ n = vsnprintf(buff, BUFSIZ, fmt, ap);
70+ va_end(ap);
71+ 
72+ write(mark_fd, buff, n);
73+}
74+ 
75+#define nano2sec(nan) (nan / 1000000000ULL)
76+#define nano2ms(nan) (nan / 1000000ULL)
77+#define nano2usec(nan) (nan / 1000ULL)
78+#define usec2nano(sec) (sec * 1000ULL)
79+#define ms2nano(ms) (ms * 1000000ULL)
80+#define sec2nano(sec) (sec * 1000000000ULL)
81+#define INTERVAL ms2nano(100ULL)
82+#define RUN_INTERVAL ms2nano(20ULL)
83+#define NR_RUNS 50
84+#define PRIO_START 2
85+/* 1 millisec off */
86+#define MAX_ERR usec2nano(1000)
87+ 
88+#define PROGRESS_CHARS 70
89+ 
90+static unsigned long long interval = INTERVAL;
91+static unsigned long long run_interval = RUN_INTERVAL;
92+static unsigned long long max_err = MAX_ERR;
93+static int nr_runs = NR_RUNS;
94+static int prio_start = PRIO_START;
95+static int check;
96+static int stop;
97+static int equal;
98+ 
99+static unsigned long long now;
100+ 
101+static int done;
102+static int loop;
103+static int duration;
104+static int quiet;
105+static char jsonfile[MAX_PATH];
106+ 
107+static pthread_barrier_t start_barrier;
108+static pthread_barrier_t end_barrier;
109+static unsigned long long **intervals;
110+static unsigned long long **intervals_length;
111+static unsigned long **intervals_loops;
112+static long *thread_pids;
113+ 
114+static char buffer[BUFSIZ];
115+ 
116+static void perr(char *fmt, ...)
117+{
118+ va_list ap;
119+ 
120+ va_start(ap, fmt);
121+ vsnprintf(buffer, BUFSIZ, fmt, ap);
122+ va_end(ap);
123+ 
124+ perror(buffer);
125+ fflush(stderr);
126+ exit(-1);
127+}
128+ 
129+static void print_progress_bar(int percent)
130+{
131+ int i;
132+ int p;
133+ 
134+ if (percent > 100)
135+ percent = 100;
136+ 
137+ /* Use stderr, so we don't capture it */
138+ putc('\r', stderr);
139+ putc('|', stderr);
140+ for (i=0; i < PROGRESS_CHARS; i++)
141+ putc(' ', stderr);
142+ putc('|', stderr);
143+ putc('\r', stderr);
144+ putc('|', stderr);
145+ 
146+ p = PROGRESS_CHARS * percent / 100;
147+ 
148+ for (i=0; i < p; i++)
149+ putc('-', stderr);
150+ 
151+ fflush(stderr);
152+}
153+ 
154+static void usage(int error)
155+{
156+ printf("rt-migrate-test %1.2f\n", VERSION);
157+ printf("Usage:\n"
158+ "rt-migrate-test <options> [NR_TASKS]\n\n"
159+ "-c --check Stop if lower prio task is quicker than higher (off)\n"
160+ "-D TIME --duration=TIME Specify a length for the test run.\n"
161+ " Append 'm', 'h', or 'd' to specify minutes, hours or\n"
162+ " days.\n"
163+ "-e --equal Use equal prio for #CPU-1 tasks (requires > 2 CPUS)\n"
164+ "-h --help Print this help message\n"
165+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
166+ "-l LOOPS --loops=LOOPS Number of iterations to run (50)\n"
167+ "-m TIME --maxerr=TIME Max allowed error (microsecs)\n"
168+ "-p PRIO --prio=PRIO base priority to start RT tasks with (2)\n"
169+ "-q --quiet print a summary only on exit\n"
170+ "-r TIME --run-time=TIME Run time (ms) to busy loop the threads (20)\n"
171+ "-s TIME --sleep-time=TIME Sleep time (ms) between intervals (100)\n\n"
172+ " () above are defaults \n"
173+ );
174+ exit(error);
175+}
176+ 
177+enum option_value {
178+ OPT_CHECK=1, OPT_DURATION, OPT_EQUAL, OPT_HELP, OPT_JSON,
179+ OPT_LOOPS, OPT_MAXERR, OPT_PRIO, OPT_QUIET, OPT_RUN_TIME,
180+ OPT_SLEEP_TIME
181+};
182+ 
183+static void parse_options(int argc, char *argv[])
184+{
185+ for (;;) {
186+ int option_index = 0;
187+ /** Options for getopt */
188+ static struct option long_options[] = {
189+ {"check", no_argument, NULL, OPT_CHECK},
190+ {"duration", required_argument, NULL, OPT_DURATION},
191+ {"equal", no_argument, NULL, OPT_EQUAL},
192+ {"help", no_argument, NULL, OPT_HELP},
193+ {"json", required_argument, NULL, OPT_JSON},
194+ {"loops", required_argument, NULL, OPT_LOOPS},
195+ {"maxerr", required_argument, NULL, OPT_MAXERR},
196+ {"prio", required_argument, NULL, OPT_PRIO},
197+ {"quiet", no_argument, NULL, OPT_QUIET},
198+ {"run-time", required_argument, NULL, OPT_RUN_TIME},
199+ {"sleep-time", required_argument, NULL, OPT_SLEEP_TIME},
200+ {NULL, 0, NULL, 0}
201+ };
202+ int c = getopt_long(argc, argv, "cD:ehl:m:p:qr:s:",
203+ long_options, &option_index);
204+ if (c == -1)
205+ break;
206+ switch (c) {
207+ case OPT_CHECK:
208+ case 'c':
209+ check = 1;
210+ break;
211+ case OPT_DURATION:
212+ case 'D':
213+ duration = parse_time_string(optarg);
214+ break;
215+ case OPT_EQUAL:
216+ case 'e':
217+ equal = 1;
218+ break;
219+ case OPT_HELP:
220+ case '?':
221+ case 'h':
222+ usage(0);
223+ break;
224+ case OPT_JSON:
225+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
226+ break;
227+ case OPT_LOOPS:
228+ case 'l':
229+ nr_runs = atoi(optarg);
230+ break;
231+ case OPT_MAXERR:
232+ case 'm':
233+ max_err = usec2nano(atoi(optarg));
234+ break;
235+ case OPT_PRIO:
236+ case 'p':
237+ prio_start = atoi(optarg);
238+ break;
239+ case OPT_QUIET:
240+ case 'q':
241+ quiet = 1;
242+ break;
243+ case OPT_RUN_TIME:
244+ case 'r':
245+ run_interval = atoi(optarg);
246+ break;
247+ case OPT_SLEEP_TIME:
248+ case 's':
249+ interval = atoi(optarg);
250+ break;
251+ default:
252+ usage(1);
253+ }
254+ }
255+}
256+ 
257+static unsigned long long get_time(void)
258+{
259+ struct timeval tv;
260+ unsigned long long time;
261+ 
262+ gettimeofday(&tv, NULL);
263+ 
264+ time = sec2nano(tv.tv_sec);
265+ time += usec2nano(tv.tv_usec);
266+ 
267+ return time;
268+}
269+ 
270+static void record_time(int id, unsigned long long time, unsigned long l)
271+{
272+ unsigned long long ltime;
273+ 
274+ if (loop >= nr_runs)
275+ return;
276+ time -= now;
277+ ltime = get_time();
278+ ltime -= now;
279+ intervals[loop][id] = time;
280+ intervals_length[loop][id] = ltime;
281+ intervals_loops[loop][id] = l;
282+}
283+ 
284+static int calc_prio(int id)
285+{
286+ int prio = equal && id && (id < nr_tasks - 1) ? 1 : id;
287+ return prio + prio_start;
288+}
289+ 
290+static void print_results(void)
291+{
292+ int i;
293+ int t;
294+ unsigned long long tasks_max[nr_tasks];
295+ unsigned long long tasks_min[nr_tasks];
296+ unsigned long long tasks_avg[nr_tasks];
297+ 
298+ memset(tasks_max, 0, sizeof(tasks_max[0])*nr_tasks);
299+ memset(tasks_min, 0xff, sizeof(tasks_min[0])*nr_tasks);
300+ memset(tasks_avg, 0, sizeof(tasks_avg[0])*nr_tasks);
301+ 
302+ printf("Iter: ");
303+ for (t=0; t < nr_tasks; t++)
304+ printf("%6d ", t);
305+ printf("\n");
306+ 
307+ for (i=0; i < nr_runs; i++) {
308+ printf("%4d: ", i);
309+ for (t=0; t < nr_tasks; t++) {
310+ unsigned long long itv = intervals[i][t];
311+ 
312+ if (tasks_max[t] < itv)
313+ tasks_max[t] = itv;
314+ if (tasks_min[t] > itv)
315+ tasks_min[t] = itv;
316+ tasks_avg[t] += itv;
317+ printf("%6lld ", nano2usec(itv));
318+ }
319+ printf("\n");
320+ printf(" len: ");
321+ for (t=0; t < nr_tasks; t++) {
322+ unsigned long long len = intervals_length[i][t];
323+ 
324+ printf("%6lld ", nano2usec(len));
325+ }
326+ printf("\n");
327+ printf(" loops: ");
328+ for (t=0; t < nr_tasks; t++) {
329+ unsigned long loops = intervals_loops[i][t];
330+ 
331+ printf("%6ld ", loops);
332+ }
333+ printf("\n");
334+ printf("\n");
335+ }
336+ 
337+ printf("Parent pid: %d\n", getpid());
338+ 
339+ for (t=0; t < nr_tasks; t++) {
340+ printf(" Task %d (prio %d) (pid %ld):\n", t, calc_prio(t),
341+ thread_pids[t]);
342+ printf(" Max: %lld us\n", nano2usec(tasks_max[t]));
343+ printf(" Min: %lld us\n", nano2usec(tasks_min[t]));
344+ printf(" Tot: %lld us\n", nano2usec(tasks_avg[t]));
345+ printf(" Avg: %lld us\n", nano2usec(tasks_avg[t] / nr_runs));
346+ printf("\n");
347+ }
348+ 
349+ if (check) {
350+ if (check < 0)
351+ printf(" Failed!\n");
352+ else
353+ printf(" Passed!\n");
354+ }
355+}
356+ 
357+static void write_stats(FILE *f, void *data __attribute__ ((unused)))
358+{
359+ int i;
360+ int t;
361+ unsigned long long tasks_max[nr_tasks];
362+ unsigned long long tasks_min[nr_tasks];
363+ unsigned long long tasks_avg[nr_tasks];
364+ 
365+ memset(tasks_max, 0, sizeof(tasks_max[0])*nr_tasks);
366+ memset(tasks_min, 0xff, sizeof(tasks_min[0])*nr_tasks);
367+ memset(tasks_avg, 0, sizeof(tasks_avg[0])*nr_tasks);
368+ 
369+ for (i=0; i < nr_runs; i++) {
370+ for (t=0; t < nr_tasks; t++) {
371+ unsigned long long itv = intervals[i][t];
372+ 
373+ if (tasks_max[t] < itv)
374+ tasks_max[t] = itv;
375+ if (tasks_min[t] > itv)
376+ tasks_min[t] = itv;
377+ tasks_avg[t] += itv;
378+ }
379+ }
380+ 
381+ fprintf(f, " \"num_threads\": %d,\n", nr_tasks);
382+ fprintf(f, " \"thread\": {\n");
383+ for (i = 0; i < nr_tasks; i++) {
384+ fprintf(f, " \"%u\": {\n", i);
385+ fprintf(f, " \"prio\": %d,\n", calc_prio(i));
386+ fprintf(f, " \"min\": %lld,\n", nano2usec(tasks_min[i]));
387+ fprintf(f, " \"avg\": %lld,\n", nano2usec(tasks_avg[i]) / nr_runs);
388+ fprintf(f, " \"max\": %lld,\n", nano2usec(tasks_max[i]));
389+ fprintf(f, " \"total\": %lld\n", nano2usec(tasks_avg[i]));
390+ fprintf(f, " }%s\n", i == nr_tasks - 1 ? "" : ",");
391+ }
392+ fprintf(f, " }\n");
393+}
394+ 
395+static unsigned long busy_loop(unsigned long long start_time)
396+{
397+ unsigned long long time;
398+ unsigned long l = 0;
399+ 
400+ do {
401+ l++;
402+ time = get_time();
403+ } while ((time - start_time) < RUN_INTERVAL);
404+ 
405+ return l;
406+}
407+ 
408+void *start_task(void *data)
409+{
410+ long id = (long)data;
411+ unsigned long long start_time;
412+ int prio = calc_prio(id);
413+ struct sched_param param = {
414+ .sched_priority = prio,
415+ };
416+ int ret;
417+ int high = 0;
418+ cpu_set_t cpumask;
419+ cpu_set_t save_cpumask;
420+ int cpu = 0;
421+ unsigned long l;
422+ long pid;
423+ 
424+ ret = sched_getaffinity(0, sizeof(save_cpumask), &save_cpumask);
425+ if (ret < 0)
426+ perr("getting affinity");
427+ 
428+ pid = gettid();
429+ thread_pids[id] = pid;
430+ 
431+ /* Check if we are the highest prio task */
432+ if (id == nr_tasks-1)
433+ high = 1;
434+ 
435+ ret = sched_setscheduler(0, SCHED_FIFO, &param);
436+ if (ret < 0 && !id)
437+ fprintf(stderr, "Warning, can't set priorities\n");
438+ 
439+ while (!done) {
440+ if (high) {
441+ /* rotate around the CPUS */
442+ if (!CPU_ISSET(cpu, &save_cpumask))
443+ cpu = 0;
444+ CPU_ZERO(&cpumask);
445+ CPU_SET(cpu, &cpumask); cpu++;
446+ sched_setaffinity(0, sizeof(cpumask), &cpumask);
447+ }
448+ pthread_barrier_wait(&start_barrier);
449+ start_time = get_time();
450+ ftrace_write("Thread %d: started %lld diff %lld\n",
451+ pid, start_time, start_time - now);
452+ l = busy_loop(start_time);
453+ record_time(id, start_time, l);
454+ pthread_barrier_wait(&end_barrier);
455+ }
456+ 
457+ return (void*)pid;
458+}
459+ 
460+static int check_times(int l)
461+{
462+ int i;
463+ unsigned long long last;
464+ unsigned long long last_loops;
465+ unsigned long long last_length;
466+ 
467+ for (i=0; i < nr_tasks; i++) {
468+ if (i && last < intervals[l][i] &&
469+ ((intervals[l][i] - last) > max_err)) {
470+ /*
471+ * May be a false positive.
472+ * Make sure that we did more loops
473+ * our start is before the end
474+ * and the end should be tested.
475+ */
476+ if (intervals_loops[l][i] < last_loops ||
477+ intervals[l][i] > last_length ||
478+ (intervals_length[l][i] > last_length &&
479+ intervals_length[l][i] - last_length > max_err)) {
480+ ftrace_write("Task %d FAILED\n", thread_pids[i]);
481+ check = -1;
482+ return 1;
483+ }
484+ }
485+ last = intervals[l][i];
486+ last_loops = intervals_loops[l][i];
487+ last_length = intervals_length[l][i];
488+ }
489+ return 0;
490+}
491+ 
492+static void stop_log(int sig __attribute__ ((unused)))
493+{
494+ stop = 1;
495+}
496+ 
497+static int count_cpus(void)
498+{
499+ FILE *fp;
500+ char buf[2048];
501+ int cpus = 0;
502+ char *pbuf;
503+ size_t *pn;
504+ size_t n;
505+ int r;
506+ 
507+ n = 2048;
508+ pn = &n;
509+ pbuf = buf;
510+ 
511+ fp = fopen("/proc/cpuinfo", "r");
512+ if (!fp)
513+ perr("Can not read cpuinfo");
514+ 
515+ while ((r = getline(&pbuf, pn, fp)) >= 0) {
516+ char *p;
517+ 
518+ if (strncmp(buf, "processor", 9) != 0)
519+ continue;
520+ for (p = buf+9; isspace(*p); p++)
521+ ;
522+ if (*p == ':')
523+ cpus++;
524+ }
525+ fclose(fp);
526+ 
527+ return cpus;
528+}
529+ 
530+int main (int argc, char **argv)
531+{
532+ pthread_t *threads;
533+ long i;
534+ int ret;
535+ struct timespec intv;
536+ struct sched_param param;
537+ 
538+ rt_init(argc, argv);
539+ parse_options(argc, argv);
540+ 
541+ signal(SIGINT, stop_log);
542+ signal(SIGALRM, stop_log);
543+ 
544+ if (duration)
545+ alarm(duration);
546+ 
547+ if (argc >= (optind + 1))
548+ nr_tasks = atoi(argv[optind]);
549+ else
550+ nr_tasks = count_cpus() + 1;
551+ 
552+ threads = malloc(sizeof(*threads) * nr_tasks);
553+ if (!threads)
554+ perr("malloc");
555+ memset(threads, 0, sizeof(*threads) * nr_tasks);
556+ 
557+ ret = pthread_barrier_init(&start_barrier, NULL, nr_tasks + 1);
558+ ret = pthread_barrier_init(&end_barrier, NULL, nr_tasks + 1);
559+ if (ret < 0)
560+ perr("pthread_barrier_init");
561+ 
562+ intervals = malloc(sizeof(void*) * nr_runs);
563+ if (!intervals)
564+ perr("malloc intervals array");
565+ 
566+ intervals_length = malloc(sizeof(void*) * nr_runs);
567+ if (!intervals_length)
568+ perr("malloc intervals length array");
569+ 
570+ intervals_loops = malloc(sizeof(void*) * nr_runs);
571+ if (!intervals_loops)
572+ perr("malloc intervals loops array");
573+ 
574+ thread_pids = malloc(sizeof(long) * nr_tasks);
575+ if (!thread_pids)
576+ perr("malloc thread_pids");
577+ 
578+ for (i=0; i < nr_runs; i++) {
579+ intervals[i] = malloc(sizeof(unsigned long long)*nr_tasks);
580+ if (!intervals[i])
581+ perr("malloc intervals");
582+ memset(intervals[i], 0, sizeof(unsigned long long)*nr_tasks);
583+ 
584+ intervals_length[i] = malloc(sizeof(unsigned long long)*nr_tasks);
585+ if (!intervals_length[i])
586+ perr("malloc length intervals");
587+ memset(intervals_length[i], 0, sizeof(unsigned long long)*nr_tasks);
588+ 
589+ intervals_loops[i] = malloc(sizeof(unsigned long)*nr_tasks);
590+ if (!intervals_loops[i])
591+ perr("malloc loops intervals");
592+ memset(intervals_loops[i], 0, sizeof(unsigned long)*nr_tasks);
593+ }
594+ 
595+ for (i=0; i < nr_tasks; i++) {
596+ if (pthread_create(&threads[i], NULL, start_task, (void *)i))
597+ perr("pthread_create");
598+ 
599+ }
600+ 
601+ /*
602+ * Progress bar uses stderr to let users see it when
603+ * redirecting output. So we convert stderr to use line
604+ * buffering so the progress bar doesn't flicker.
605+ */
606+ setlinebuf(stderr);
607+ 
608+ /* up our prio above all tasks */
609+ memset(&param, 0, sizeof(param));
610+ param.sched_priority = nr_tasks + prio_start;
611+ if (sched_setscheduler(0, SCHED_FIFO, &param))
612+ fprintf(stderr, "Warning, can't set priority of main thread!\n");
613+ 
614+ 
615+ 
616+ intv.tv_sec = nano2sec(INTERVAL);
617+ intv.tv_nsec = INTERVAL % sec2nano(1);
618+ 
619+ if (!quiet)
620+ print_progress_bar(0);
621+ 
622+ setup_ftrace_marker();
623+ 
624+ for (loop=0; loop < nr_runs; loop++) {
625+ unsigned long long end;
626+ 
627+ /* Release the CPU so all can get to the next barrier */
628+ nanosleep(&intv, NULL);
629+ 
630+ now = get_time();
631+ 
632+ ftrace_write("Loop %d now=%lld\n", loop, now);
633+ 
634+ pthread_barrier_wait(&start_barrier);
635+ 
636+ ftrace_write("All running!!!\n");
637+ 
638+ nanosleep(&intv, NULL);
639+ 
640+ if (!quiet)
641+ print_progress_bar((loop * 100)/nr_runs);
642+ 
643+ end = get_time();
644+ ftrace_write("Loop %d end now=%lld diff=%lld\n", loop, end, end - now);
645+ 
646+ pthread_barrier_wait(&end_barrier);
647+ 
648+ if (stop || (check && check_times(loop))) {
649+ loop++;
650+ nr_runs = loop;
651+ break;
652+ }
653+ }
654+ putc('\n', stderr);
655+ 
656+ pthread_barrier_wait(&start_barrier);
657+ done = 1;
658+ pthread_barrier_wait(&end_barrier);
659+ 
660+ for (i=0; i < nr_tasks; i++)
661+ pthread_join(threads[i], (void*)&thread_pids[i]);
662+ 
663+ print_results();
664+ 
665+ if (strlen(jsonfile) != 0)
666+ rt_write_json(jsonfile, check < 0, write_stats, NULL);
667+ 
668+ if (stop) {
669+ /*
670+ * We use this test in bash while loops
671+ * So if we hit Ctrl-C then let the while
672+ * loop know to break.
673+ */
674+ if (check < 0)
675+ exit(-1);
676+ else
677+ exit(1);
678+ }
679+ if (check < 0)
680+ exit(-1);
681+ else
682+ exit(0);
683+ 
684+ return 0;
685+}
@@ -0,0 +1,64 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH CYCLICDEADLINE 8 "January 16, 2020"
3+# SPDX-License-Identifier: GPL-2.0-only
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+cyclicdeadline \- This program is used to test the deadline scheduler (SCHED_DEADLINE) using a cyclictest style program
18+.PP
19+.SH SYNOPSIS
20+.B cyclicdeadline
21+.RI "[-a [CPUSET]] [-D TIME] [-h] [-i INTV] [--json FILENAME] [-s STEP] [-t NUM] [-q]"
22+.PP
23+.SH DESCRIPTION
24+.B cyclicdeadline
25+is a cyclictest style program for testing the deadline scheduler
26+.PP
27+.SH OPTIONS
28+.TP
29+.B \-a \-\-affinity [CPUSET]
30+Comma / hypen separated list of CPUs to run deadline tasks on
31+.TP
32+.B \-D \-\-duration TIME
33+Specify a length for the test to run
34+Append 'm', 'h', or 'd' to specify minutes, hours, or days
35+.TP
36+.B \-h \-\-help
37+Show this help menu
38+.TP
39+.B \-i \-\-interval INTV
40+The shortest deadline for the tasks in us. (default 1000us)
41+.TP
42+.B \-\-json=FILENAME
43+Write final results into FILENAME, JSON formatted.
44+.TP
45+.B \-s \-\-step STEP
46+The amount to increase the deadline for each task in us. (default 500us)
47+.TP
48+.B \-t \-\-threads NUM
49+The number of threads to run as deadline (default 1)
50+.TP
51+.B \-q, \-\-quiet
52+Print a summary only on exit. Useful for automated tests, where only
53+the summary output needs to be captured.
54+.TP
55+.B \-b, \-\-breaktrace=USEC
56+Send break trace command when latency > USEC
57+.TP
58+.B \-\-tracemark
59+write a trace mark when \-b latency is exceeded.
60+.br
61+.SH AUTHOR
62+cyclicdeadline was written by Steven Rostedt <rostedt@goodmis.org>
63+.PP
64+This manual page was written by John Kacur <jkacur@redhat.com>
@@ -0,0 +1,1439 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+ 
3+/*
4+ * Copyright (C) 2016 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
5+ * Copyright (C) 2019 John Kacur <jkacur@redhat.com>
6+ * Copyright (C) 2019 Clark Williams <williams@redhat.com>
7+ */
8+ 
9+#include <pthread.h>
10+#include <stdarg.h>
11+#include <stdlib.h>
12+#include <string.h>
13+#include <unistd.h>
14+#include <stdio.h>
15+#include <fcntl.h>
16+#include <sched.h>
17+#include <ctype.h>
18+#include <errno.h>
19+#include <signal.h>
20+#include <getopt.h>
21+ 
22+#include <sys/syscall.h>
23+#include <sys/types.h>
24+#include <sys/mount.h>
25+#include <sys/mman.h>
26+#include <sys/time.h>
27+#include <sys/stat.h>
28+#include <sys/vfs.h>
29+ 
30+#include <linux/unistd.h>
31+#include <linux/magic.h>
32+ 
33+#include "rt-utils.h"
34+#include "rt-sched.h"
35+#include "rt-error.h"
36+#include "histogram.h"
37+ 
38+#define _STR(x) #x
39+#define STR(x) _STR(x)
40+#ifndef MAXPATH
41+#define MAXPATH 1024
42+#endif
43+ 
44+#define HIST_MAX 1000000
45+ 
46+#define CPUSET_ALL "my_cpuset_all"
47+#define CPUSET_LOCAL "my_cpuset"
48+ 
49+typedef unsigned long long u64;
50+typedef unsigned int u32;
51+typedef int s32;
52+ 
53+/* Struct for statistics */
54+struct thread_stat {
55+ unsigned long cycles;
56+ long min;
57+ long max;
58+ long act;
59+ double avg;
60+ long *values;
61+ struct histogram *hist;
62+ pthread_t thread;
63+ int threadstarted;
64+ int tid;
65+ long reduce;
66+};
67+ 
68+struct sched_data {
69+ u64 runtime_us;
70+ u64 deadline_us;
71+ 
72+ int bufmsk;
73+ 
74+ struct thread_stat stat;
75+ 
76+ char buff[BUFSIZ+1];
77+};
78+ 
79+static int shutdown;
80+static int info_enable;
81+static int debug_enable;
82+static int tracelimit;
83+static int trace_marker;
84+static int histogram;
85+static FILE *histfile;
86+static pthread_mutex_t break_thread_id_lock = PTHREAD_MUTEX_INITIALIZER;
87+static pid_t break_thread_id;
88+static uint64_t break_thread_value;
89+static pthread_barrier_t barrier;
90+ 
91+static int cpu_count;
92+static int all_cpus;
93+static int nr_threads;
94+static int use_nsecs;
95+static int mark_fd;
96+static int quiet;
97+static char jsonfile[MAX_PATH];
98+ 
99+static struct histoset hset;
100+ 
101+static int find_mount(const char *mount, char *debugfs)
102+{
103+ char type[100];
104+ FILE *fp;
105+ 
106+ if ((fp = fopen("/proc/mounts", "r")) == NULL)
107+ return 0;
108+ 
109+ while (fscanf(fp, "%*s %"
110+ STR(MAXPATH)
111+ "s %99s %*s %*d %*d\n",
112+ debugfs, type) == 2) {
113+ if (strcmp(type, mount) == 0)
114+ break;
115+ }
116+ fclose(fp);
117+ 
118+ if (strcmp(type, mount) != 0)
119+ return 0;
120+ return 1;
121+}
122+ 
123+static const char *find_debugfs(void)
124+{
125+ static int debugfs_found;
126+ static char debugfs[MAXPATH+1];
127+ 
128+ if (debugfs_found)
129+ return debugfs;
130+ 
131+ if (!find_mount("debugfs", debugfs))
132+ return "";
133+ 
134+ debugfs_found = 1;
135+ 
136+ return debugfs;
137+}
138+ 
139+static int my_vsprintf(char *buf, int size, const char *fmt, va_list ap)
140+{
141+ const char *p;
142+ char tmp[100];
143+ char *s = buf;
144+ char *end = buf + size;
145+ char *str;
146+ long long lng;
147+ int l;
148+ int i;
149+ 
150+ end[-1] = 0;
151+ 
152+ for (p = fmt; *p && s < end; p++) {
153+ if (*p == '%') {
154+ l = 0;
155+ again:
156+ p++;
157+ switch (*p) {
158+ case 's':
159+ if (l)
160+ fatal("Illegal print format l used with %%s\n");
161+ str = va_arg(ap, char *);
162+ l = strlen(str);
163+ strncpy(s, str, end - s);
164+ s += l;
165+ break;
166+ case 'l':
167+ l++;
168+ goto again;
169+ case 'd':
170+ if (l == 1) {
171+ if (sizeof(long) == 8)
172+ l = 2;
173+ }
174+ if (l == 2)
175+ lng = va_arg(ap, long long);
176+ else if (l > 2)
177+ fatal("Illegal print format l=%d\n", l);
178+ else
179+ lng = va_arg(ap, int);
180+ i = 0;
181+ while (lng > 0) {
182+ tmp[i++] = (lng % 10) + '0';
183+ lng /= 10;
184+ }
185+ tmp[i] = 0;
186+ l = strlen(tmp);
187+ if (!l) {
188+ *s++ = '0';
189+ } else {
190+ while (l)
191+ *s++ = tmp[--l];
192+ }
193+ break;
194+ default:
195+ fatal("Illegal print format '%c'\n", *p);
196+ }
197+ continue;
198+ }
199+ *s++ = *p;
200+ }
201+ 
202+ return s - buf;
203+}
204+ 
205+static void ftrace_write(char *buf, const char *fmt, ...)
206+{
207+ va_list ap;
208+ int n;
209+ 
210+ if (mark_fd < 0)
211+ return;
212+ 
213+ va_start(ap, fmt);
214+ n = my_vsprintf(buf, BUFSIZ, fmt, ap);
215+ va_end(ap);
216+ 
217+ write(mark_fd, buf, n);
218+}
219+ 
220+static void setup_ftrace_marker(void)
221+{
222+ struct stat st;
223+ const char *debugfs = find_debugfs();
224+ char files[strlen(debugfs) + strlen("/tracing/trace_marker") + 1];
225+ int ret;
226+ 
227+ if (strlen(debugfs) == 0)
228+ return;
229+ 
230+ sprintf(files, "%s/tracing/trace_marker", debugfs);
231+ ret = stat(files, &st);
232+ if (ret >= 0)
233+ goto found;
234+ /* Do nothing if not mounted */
235+ return;
236+found:
237+ mark_fd = open(files, O_WRONLY);
238+}
239+ 
240+/*
241+ * Return true if file exists
242+ */
243+static int check_file_exists(char *path)
244+{
245+ int ret;
246+ struct stat st;
247+ 
248+ ret = !stat(path, &st);
249+ 
250+ return ret;
251+ 
252+}
253+ 
254+/*
255+ * Return 0 on success
256+ */
257+ 
258+static int fill_sched_features(char *path)
259+{
260+ int ret;
261+ const char *debugfs;
262+ 
263+ debugfs = find_debugfs();
264+ if (strlen(debugfs) == 0)
265+ return -1;
266+ 
267+ snprintf(path, MAX_PATH, "%s/sched/features", debugfs);
268+ ret = check_file_exists(path);
269+ if (ret)
270+ return 0;
271+ 
272+ snprintf(path, MAX_PATH, "%s/sched_features", debugfs);
273+ ret = check_file_exists(path);
274+ if (ret)
275+ return 0;
276+ 
277+ memset(path, 0, MAX_PATH);
278+ 
279+ return ret;
280+ 
281+}
282+ 
283+static int setup_hr_tick(void)
284+{
285+ char path[MAX_PATH];
286+ char buf[500];
287+ static int set = 0;
288+ int hrtick_dl = 0;
289+ char *p;
290+ int ret;
291+ int len;
292+ int fd;
293+ 
294+ if (set)
295+ return 1;
296+ set = 1;
297+ 
298+ ret = fill_sched_features(path);
299+ if (ret)
300+ return 0;
301+ 
302+ fd = open(path, O_RDWR);
303+ if (fd < 0) {
304+ perror(path);
305+ return 0;
306+ }
307+ 
308+ len = sizeof(buf);
309+ 
310+ ret = read(fd, buf, len);
311+ if (ret < 0) {
312+ perror(path);
313+ close(fd);
314+ return 0;
315+ }
316+ if (ret >= len)
317+ ret = len - 1;
318+ buf[ret] = 0;
319+ 
320+ ret = 1;
321+ 
322+ p = strstr(buf, "HRTICK_DL");
323+ if (p && p - 3 >= buf) {
324+ hrtick_dl = 1;
325+ p -= 3;
326+ if (strncmp(p, "NO_HRTICK_DL", 12) == 0) {
327+ ret = write(fd, "HRTICK_DL", 9);
328+ if (ret != 9)
329+ ret = 0;
330+ else
331+ ret = 1;
332+ }
333+ }
334+ 
335+ /* Backwards compatibility for kernel that only have HRTICK */
336+ if (!hrtick_dl) {
337+ p = strstr(buf, "HRTICK");
338+ if (p && p - 3 >= buf) {
339+ p -=3;
340+ if (strncmp(p, "NO_HRTICK", 9) == 0) {
341+ ret = write(fd, "HRTICK", 6);
342+ if (ret != 6)
343+ ret = 0;
344+ else
345+ ret = 1;
346+ }
347+ }
348+ }
349+ 
350+ close(fd);
351+ return ret;
352+}
353+ 
354+static int mounted(const char *path, long magic)
355+{
356+ struct statfs st_fs;
357+ 
358+ if (statfs(path, &st_fs) < 0)
359+ return -1;
360+ if ((long)st_fs.f_type != magic)
361+ return 0;
362+ return 1;
363+}
364+ 
365+#define CGROUP_PATH "/sys/fs/cgroup"
366+#define CPUSET_PATH CGROUP_PATH "/cpuset"
367+ 
368+/**
369+ * cgroup_mounted - test if the path /sys/fs/cgroup exists
370+ * and is a supported type
371+ *
372+ * Returns -1 if the path does not exist
373+ * Returns 0 if the path exists but is not a cgroup type
374+ * Returns 1 if the path exists and supports cgroups
375+ */
376+static int cgroup_mounted(void)
377+{
378+ int ret;
379+ 
380+ ret = mounted(CGROUP_PATH, TMPFS_MAGIC);
381+ if (ret == -1)
382+ return -1; /* path doesn't exist */
383+ if (ret == 1)
384+ return 1; /* tmpfs */
385+ ret = mounted(CGROUP_PATH, CGROUP_SUPER_MAGIC);
386+ if (ret == 1)
387+ return 1; /* cgroup v1 */
388+ ret = mounted(CGROUP_PATH, CGROUP2_SUPER_MAGIC);
389+ if (ret == 1)
390+ return 1; /* cgroup v2 */
391+ return 0; /* path exists but type is not recognized */
392+}
393+ 
394+static int open_cpuset(const char *path, const char *name)
395+{
396+ char buf[MAXPATH];
397+ struct stat st;
398+ int ret;
399+ int fd;
400+ 
401+ buf[MAXPATH - 1] = 0;
402+ snprintf(buf, MAXPATH - 1, "%s/%s", path, name);
403+ 
404+ ret = stat(buf, &st);
405+ if (ret < 0)
406+ return ret;
407+ 
408+ fd = open(buf, O_WRONLY);
409+ return fd;
410+}
411+ 
412+static int mount_cpuset(void)
413+{
414+ struct stat st;
415+ int ret;
416+ int fd;
417+ 
418+ /* Check if cgroups is already mounted. */
419+ ret = cgroup_mounted();
420+ if (ret < 0) /* /sys/fs/cgroup doesn't exist */
421+ return ret;
422+ 
423+ if (!ret) /* /sys/fs/cgroup exists, but we don't recognize the type */
424+ return -1;
425+ 
426+ ret = stat(CPUSET_PATH, &st);
427+ if (ret < 0) {
428+ ret = mkdir(CPUSET_PATH, 0755);
429+ if (ret < 0)
430+ return ret;
431+ }
432+ ret = mounted(CPUSET_PATH, CGROUP_SUPER_MAGIC);
433+ if (ret < 0)
434+ return ret;
435+ if (!ret) {
436+ ret = mount("cpuset", CPUSET_PATH, "cgroup", 0, "cpuset");
437+ if (ret < 0)
438+ return ret;
439+ }
440+ 
441+ fd = open_cpuset(CPUSET_PATH, "cpuset.cpu_exclusive");
442+ if (fd < 0)
443+ return fd;
444+ ret = write(fd, "1", 2);
445+ close(fd);
446+ 
447+ fd = open_cpuset(CPUSET_PATH, "cpuset.sched_load_balance");
448+ if (fd < 0)
449+ return fd;
450+ ret = write(fd, "0", 2);
451+ close(fd);
452+ 
453+ return 0;
454+}
455+ 
456+enum {
457+ CPUSET_FL_CPU_EXCLUSIVE = (1 << 0),
458+ CPUSET_FL_MEM_EXCLUSIVE = (1 << 1),
459+ CPUSET_FL_ALL_TASKS = (1 << 2),
460+ CPUSET_FL_TASKS = (1 << 3),
461+ CPUSET_FL_CLEAR_LOADBALANCE = (1 << 4),
462+ CPUSET_FL_SET_LOADBALANCE = (1 << 5),
463+ CPUSET_FL_CLONE_CHILDREN = (1 << 6),
464+};
465+ 
466+static void make_cpuset(const char *name, const char *cpus,
467+ const char *mems, unsigned int flags, ...)
468+{
469+ struct stat st;
470+ char path[MAXPATH];
471+ char buf[100];
472+ va_list ap;
473+ int ret;
474+ int fd;
475+ 
476+ printf("Creating cpuset '%s'\n", name);
477+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
478+ path[MAXPATH - 1] = 0;
479+ 
480+ ret = mount_cpuset();
481+ if (ret < 0)
482+ fatal("mount_cpuset");
483+ 
484+ ret = stat(path, &st);
485+ if (ret < 0) {
486+ ret = mkdir(path, 0755);
487+ if (ret < 0)
488+ fatal("mkdir");
489+ }
490+ 
491+ fd = open_cpuset(path, "cpuset.cpus");
492+ if (fd < 0)
493+ fatal("cset");
494+ ret = write(fd, cpus, strlen(cpus));
495+ close(fd);
496+ if (ret < 0)
497+ fatal("write cpus");
498+ 
499+ if (mems) {
500+ fd = open_cpuset(path, "cpuset.mems");
501+ if (fd < 0)
502+ fatal("open mems");
503+ ret = write(fd, mems, strlen(mems));
504+ close(fd);
505+ if (ret < 0)
506+ fatal("write mems");
507+ }
508+ 
509+ if (flags & CPUSET_FL_CPU_EXCLUSIVE) {
510+ fd = open_cpuset(path, "cpuset.cpu_exclusive");
511+ if (fd < 0)
512+ fatal("open cpu_exclusive");
513+ ret = write(fd, "1", 2);
514+ close(fd);
515+ if (ret < 0)
516+ fatal("write cpu_exclusive");
517+ }
518+ 
519+ if (flags & (CPUSET_FL_CLEAR_LOADBALANCE | CPUSET_FL_SET_LOADBALANCE)) {
520+ fd = open_cpuset(path, "cpuset.sched_load_balance");
521+ if (fd < 0)
522+ fatal("open sched_load_balance");
523+ if (flags & CPUSET_FL_SET_LOADBALANCE)
524+ ret = write(fd, "1", 2);
525+ else
526+ ret = write(fd, "0", 2);
527+ close(fd);
528+ if (ret < 0)
529+ fatal("write sched_load_balance");
530+ }
531+ 
532+ if (flags & CPUSET_FL_CLONE_CHILDREN) {
533+ fd = open_cpuset(path, "cgroup.clone_children");
534+ if (fd < 0)
535+ fatal("open clone_children");
536+ ret = write(fd, "1", 2);
537+ close(fd);
538+ if (ret < 0)
539+ fatal("write clone_children");
540+ }
541+ 
542+ 
543+ if (flags & CPUSET_FL_TASKS) {
544+ int *pids;
545+ int i;
546+ 
547+ va_start(ap, flags);
548+ 
549+ fd = open_cpuset(path, "tasks");
550+ if (fd < 0)
551+ fatal("open tasks");
552+ 
553+ ret = 0;
554+ pids = va_arg(ap, int *);
555+ for (i = 0; pids[i]; i++) {
556+ sprintf(buf, "%d ", pids[i]);
557+ ret = write(fd, buf, strlen(buf));
558+ }
559+ va_end(ap);
560+ close(fd);
561+ if (ret < 0)
562+ fatal("Failed on task %d\n", pids[i]);
563+ }
564+ 
565+ if (flags & CPUSET_FL_ALL_TASKS) {
566+ FILE *fp;
567+ int pid;
568+ 
569+ fd = open_cpuset(path, "tasks");
570+ 
571+ snprintf(path, MAXPATH - 1, "%s/tasks", CPUSET_PATH);
572+ if ((fp = fopen(path, "r")) == NULL) {
573+ close(fd);
574+ fatal("opening cpuset tasks");
575+ }
576+ 
577+ while (fscanf(fp, "%d", &pid) == 1) {
578+ sprintf(buf, "%d", pid);
579+ ret = write(fd, buf, strlen(buf));
580+ /*
581+ * Tasks can come and go, the only error we care
582+ * about is ENOSPC, as that means something went
583+ * wrong that we did not expect.
584+ */
585+ if (ret < 0 && errno == ENOSPC) {
586+ fclose(fp);
587+ close(fd);
588+ fatal("Can not move tasks");
589+ }
590+ }
591+ fclose(fp);
592+ close(fd);
593+ }
594+}
595+ 
596+static void destroy_cpuset(const char *name, int print)
597+{
598+ struct stat st;
599+ char path[MAXPATH];
600+ char buf[100];
601+ FILE *fp;
602+ int pid;
603+ int ret;
604+ int fd;
605+ int retry = 0;
606+ 
607+ printf("Removing %s\n", name);
608+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
609+ path[MAXPATH - 1] = 0;
610+ 
611+ ret = stat(path, &st);
612+ if (ret < 0)
613+ return;
614+ 
615+ again:
616+ strncat(path, "/tasks", MAXPATH - 1);
617+ if ((fp = fopen(path, "r")) == NULL) {
618+ fprintf(stderr, "Failed opening %s\n", path);
619+ perror("fopen");
620+ return;
621+ }
622+ snprintf(path, MAXPATH - 1, "%s/tasks", CPUSET_PATH);
623+ path[MAXPATH - 1] = 0;
624+ 
625+ fd = open(path, O_WRONLY);
626+ if (fd < 0) {
627+ fclose(fp);
628+ fprintf(stderr, "Failed opening %s\n", path);
629+ perror("open");
630+ return;
631+ }
632+ 
633+ while (fscanf(fp, "%d", &pid) == 1) {
634+ sprintf(buf, "%d", pid);
635+ if (print)
636+ printf("Moving %d out of %s\n", pid, name);
637+ write(fd, buf, strlen(buf));
638+ }
639+ fclose(fp);
640+ close(fd);
641+ 
642+ snprintf(path, MAXPATH - 1, "%s/%s", CPUSET_PATH, name);
643+ path[MAXPATH - 1] = 0;
644+ 
645+ sleep(1);
646+ ret = rmdir(path);
647+ if (ret < 0) {
648+ if (retry++ < 5) {
649+ err_msg("Trying again\n");
650+ goto again;
651+ }
652+ err_msg_n(errno, "Failed to remove %s\n", path);
653+ }
654+}
655+ 
656+static void teardown(void)
657+{
658+ int fd;
659+ 
660+ if (all_cpus)
661+ return;
662+ 
663+ fd = open_cpuset(CPUSET_PATH, "cpuset.cpu_exclusive");
664+ if (fd >= 0) {
665+ write(fd, "0", 2);
666+ close(fd);
667+ }
668+ 
669+ fd = open_cpuset(CPUSET_PATH, "cpuset.sched_load_balance");
670+ if (fd >= 0) {
671+ write(fd, "1", 2);
672+ close(fd);
673+ }
674+ 
675+ destroy_cpuset(CPUSET_ALL, 0);
676+ destroy_cpuset(CPUSET_LOCAL, 1);
677+ 
678+ /* close any tracer file descriptors */
679+ disable_trace_mark();
680+ 
681+}
682+ 
683+static void usage(int error)
684+{
685+ printf("cyclicdeadline V %1.2f\n", VERSION);
686+ printf("Usage:\n"
687+ "cyclicdeadline <options>\n\n"
688+ "-a [CPUSET] --affinity Comma/hyphen separated list of CPUs to run deadline\n"
689+ " tasks on. An empty CPUSET runs on all CPUs a deadline\n"
690+ " task.\n"
691+ "-D TIME --duration Specify a length for the test run.\n"
692+ " Append 'm', 'h', or 'd' to specify minutes, hours or\n"
693+ " days\n"
694+ "-h --help Show this help menu.\n"
695+ " --histogram=US dump a latency histogram to stdout after the run\n"
696+ " US is the max latency time to be tracked in microseconds\n"
697+ " This option runs all threads at the same priority.\n"
698+ " --histfile=<path> dump the latency histogram to <path> instead of stdout\n"
699+ "-i INTV --interval The shortest deadline for the tasks in us\n"
700+ " (default 1000us).\n"
701+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
702+ "-s STEP --step The amount to increase the deadline for each task in us\n"
703+ " (default 500us).\n"
704+ "-t NUM --threads The number of threads to run as deadline (default 1).\n"
705+ "-q --quiet print a summary only on exit\n"
706+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
707+ " --tracemark write a trace mark when -b latency is exceeded\n"
708+ " --debug Print debugging info for cyclicdeadline\n"
709+ " --verbose Print useful information about the test\n"
710+ );
711+ exit(error);
712+}
713+ 
714+static u64 get_time_us(void)
715+{
716+ struct timespec ts;
717+ u64 time;
718+ 
719+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
720+ time = ts.tv_sec * 1000000;
721+ time += ts.tv_nsec / 1000;
722+ 
723+ return time;
724+}
725+ 
726+static void print_hist(FILE *fp, struct sched_data *sd, int nthreads)
727+{
728+ int i;
729+ unsigned long maxmax, alloverflows;
730+ 
731+ fprintf(fp, "# Histogram\n");
732+ for (i = 0; i < histogram; i++) {
733+ unsigned long flags = 0;
734+ char buf[64];
735+ 
736+ snprintf(buf, sizeof(buf), "%06d ", i);
737+ hset_print_bucket(&hset, fp, buf, i, flags);
738+ }
739+ fprintf(fp, "# Min Latencies:");
740+ for (i = 0; i < nthreads; i++)
741+ fprintf(fp, " %05lu", sd[i].stat.min);
742+ fprintf(fp, "\n");
743+ fprintf(fp, "# Avg Latencies:");
744+ for (i = 0; i < nthreads; i++)
745+ fprintf(fp, " %05lu", sd[i].stat.cycles ?
746+ (long)(sd[i].stat.avg/sd[i].stat.cycles) : 0);
747+ fprintf(fp, "\n");
748+ fprintf(fp, "# Max Latencies:");
749+ maxmax = 0;
750+ for (i = 0; i < nthreads; i++) {
751+ fprintf(fp, " %05lu", sd[i].stat.max);
752+ if (sd[i].stat.max > maxmax)
753+ maxmax = sd[i].stat.max;
754+ }
755+ fprintf(fp, "\n");
756+ fprintf(fp, "# Histogram Overflows:");
757+ alloverflows = 0;
758+ for (i = 0; i < nthreads; i++) {
759+ fprintf(fp, " %05lu", sd[i].stat.hist->oflow_count);
760+ alloverflows += sd[i].stat.hist->oflow_count;
761+ }
762+ fprintf(fp, "\n");
763+ 
764+ fprintf(fp, "# Histogram Overflow at cycle number:\n");
765+ for (i = 0; i < nthreads; i++) {
766+ fprintf(fp, "# Thread %d: ", i);
767+ hist_print_oflows(sd[i].stat.hist, fp);
768+ fprintf(fp, "\n");
769+ }
770+ 
771+ fprintf(fp, "\n");
772+}
773+ 
774+static void print_stat(FILE *fp, struct sched_data *sd, int index, int quiet)
775+{
776+ struct thread_stat *stat = &sd->stat;
777+ char *fmt;
778+ 
779+ if (quiet)
780+ return;
781+ 
782+ if (use_nsecs)
783+ fmt = "T:%2d (%5d) I:%ld C:%7lu Min:%7ld Act:%8ld Avg:%8ld Max:%8ld\n";
784+ else
785+ fmt = "T:%2d (%5d) I:%ld C:%7lu Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n";
786+ 
787+ fprintf(fp, fmt, index, stat->tid,
788+ sd->deadline_us, stat->cycles, stat->min, stat->act,
789+ stat->cycles ?
790+ (long)(stat->avg/stat->cycles) : 0, stat->max);
791+}
792+ 
793+static u64 do_runtime(struct sched_data *sd, u64 period)
794+{
795+ struct thread_stat *stat = &sd->stat;
796+ u64 next_period = period + sd->deadline_us;
797+ u64 now = get_time_us();
798+ u64 diff;
799+ 
800+ if (now < period) {
801+ u64 delta = period - now;
802+ /*
803+ * The period could be off due to other deadline tasks
804+ * preempting us when we started. If that's the case then
805+ * adjust the current period.
806+ */
807+ ftrace_write(sd->buff,
808+ "Adjusting period: now: %lld period: %lld delta:%lld%s\n",
809+ now, period, delta, delta > sd->deadline_us / 2 ?
810+ " HUGE ADJUSTMENT" : "");
811+ period = now;
812+ next_period = period + sd->deadline_us;
813+ }
814+ 
815+ ftrace_write(sd->buff, "start at %lld off=%lld (period=%lld next=%lld)\n",
816+ now, now - period, period, next_period);
817+ 
818+ 
819+ diff = now - period;
820+ if (diff > stat->max)
821+ stat->max = diff;
822+ if (!stat->min || diff < stat->min)
823+ stat->min = diff;
824+ stat->act = diff;
825+ stat->avg += (double) diff;
826+ 
827+ if (histogram)
828+ hist_sample(stat->hist, diff);
829+ 
830+ stat->cycles++;
831+ 
832+ return next_period;
833+}
834+ 
835+void *run_deadline(void *data)
836+{
837+ struct sched_data *sd = data;
838+ struct thread_stat *stat = &sd->stat;
839+ struct sched_attr attr;
840+ long tid = gettid();
841+ u64 period;
842+ int ret;
843+ 
844+ debug(debug_enable, "deadline thread %ld\n", tid);
845+ // set up for each measurment thread
846+ stat->tid = tid;
847+ 
848+ ret = sched_getattr(0, &attr, sizeof(attr), 0);
849+ if (ret < 0) {
850+ err_msg_n(errno, "[%ld]", tid);
851+ shutdown = 1;
852+ pthread_barrier_wait(&barrier);
853+ pthread_exit("Failed sched_getattr");
854+ return NULL;
855+ }
856+ 
857+ attr.sched_policy = SCHED_DEADLINE;
858+ attr.sched_runtime = sd->runtime_us * 1000;
859+ attr.sched_deadline = sd->deadline_us * 1000;
860+ 
861+ debug(debug_enable, "thread[%d] runtime=%lldus deadline=%lldus\n",
862+ gettid(), sd->runtime_us, sd->deadline_us);
863+ 
864+ ret = sched_setattr(0, &attr, 0);
865+ if (ret < 0) {
866+ err_msg_n(errno, "[%ld]", tid);
867+ shutdown = 1;
868+ pthread_barrier_wait(&barrier);
869+ pthread_exit("Failed sched_setattr");
870+ return NULL;
871+ }
872+ 
873+ pthread_barrier_wait(&barrier);
874+ 
875+ sched_yield();
876+ period = get_time_us();
877+ 
878+ while (!shutdown) {
879+ period = do_runtime(sd, period);
880+ if (tracelimit && (stat->max > tracelimit)) {
881+ shutdown++;
882+ pthread_mutex_lock(&break_thread_id_lock);
883+ if (break_thread_id == 0) {
884+ break_thread_id = stat->tid;
885+ break_thread_value = stat->max;
886+ tracemark("hit latency threshold (%lld > %d)",
887+ (unsigned long long) stat->max, tracelimit);
888+ }
889+ pthread_mutex_unlock(&break_thread_id_lock);
890+ break;
891+ }
892+ sched_yield();
893+ }
894+ ret = sched_getattr(0, &attr, sizeof(attr), 0);
895+ if (ret < 0) {
896+ err_msg_n(errno, "sched_getattr");
897+ pthread_exit("Failed second sched_getattr");
898+ }
899+ 
900+ return NULL;
901+}
902+ 
903+struct cpu_list {
904+ struct cpu_list *next;
905+ int start_cpu;
906+ int end_cpu;
907+};
908+ 
909+static void add_cpus(struct cpu_list **cpu_list, int start_cpu, int end_cpu)
910+{
911+ struct cpu_list *list;
912+ 
913+ while (*cpu_list && (*cpu_list)->end_cpu + 1 < start_cpu)
914+ cpu_list = &(*cpu_list)->next;
915+ 
916+ if (!*cpu_list) {
917+ *cpu_list = malloc(sizeof(struct cpu_list));
918+ (*cpu_list)->start_cpu = start_cpu;
919+ (*cpu_list)->end_cpu = end_cpu;
920+ (*cpu_list)->next = NULL;
921+ return;
922+ }
923+ 
924+ /* Look to concatinate */
925+ if (end_cpu > (*cpu_list)->start_cpu &&
926+ start_cpu <= (*cpu_list)->end_cpu + 1) {
927+ if (start_cpu < (*cpu_list)->start_cpu)
928+ (*cpu_list)->start_cpu = start_cpu;
929+ list = (*cpu_list)->next;
930+ while (list && list->start_cpu <= end_cpu + 1) {
931+ (*cpu_list)->end_cpu = list->end_cpu;
932+ (*cpu_list)->next = list->next;
933+ free(list);
934+ list = (*cpu_list)->next;
935+ }
936+ if ((*cpu_list)->end_cpu < end_cpu)
937+ (*cpu_list)->end_cpu = end_cpu;
938+ return;
939+ }
940+ 
941+ /* Check for overlaps */
942+ if (end_cpu >= (*cpu_list)->start_cpu - 1) {
943+ (*cpu_list)->start_cpu = start_cpu;
944+ return;
945+ }
946+ 
947+ list = malloc(sizeof(struct cpu_list));
948+ list->start_cpu = start_cpu;
949+ list->end_cpu = end_cpu;
950+ list->next = (*cpu_list)->next;
951+ (*cpu_list)->next = list;
952+}
953+ 
954+static int count_cpus(struct cpu_list *cpu_list)
955+{
956+ struct cpu_list *list;
957+ int cpus = 0;
958+ int fail = 0;
959+ 
960+ while (cpu_list) {
961+ list = cpu_list;
962+ cpus += (list->end_cpu - list->start_cpu) + 1;
963+ if (list->end_cpu >= cpu_count)
964+ fail = 1;
965+ cpu_list = list->next;
966+ free(list);
967+ }
968+ return fail ? -1 : cpus;
969+}
970+ 
971+static char *append_cpus(char *buf, int start, int end,
972+ const char *comma, int *total)
973+{
974+ int len;
975+ 
976+ if (start == end) {
977+ len = snprintf(NULL, 0, "%s%d", comma, start);
978+ buf = realloc(buf, *total + len + 1);
979+ buf[*total] = 0;
980+ snprintf(buf + *total, len + 1, "%s%d", comma, start);
981+ } else {
982+ len = snprintf(NULL, 0, "%s%d-%d", comma, start, end);
983+ buf = realloc(buf, *total + len + 1);
984+ buf[*total] = 0;
985+ snprintf(buf + *total, len + 1, "%s%d-%d", comma,
986+ start, end);
987+ }
988+ *total += len;
989+ return buf;
990+}
991+ 
992+static void make_new_list(struct cpu_list *cpu_list, char **buf)
993+{
994+ char *comma = "";
995+ int total = 0;
996+ 
997+ while (cpu_list) {
998+ *buf = append_cpus(*buf, cpu_list->start_cpu, cpu_list->end_cpu,
999+ comma, &total);
1000+ comma = ",";
1001+ cpu_list = cpu_list->next;
1002+ }
1003+}
1004+ 
1005+static void make_other_cpu_list(const char *setcpu, char **cpus)
1006+{
1007+ const char *p = setcpu;
1008+ const char *comma = "";
1009+ int curr_cpu = 0;
1010+ int cpu;
1011+ int total = 0;
1012+ 
1013+ while (*p && curr_cpu < cpu_count) {
1014+ cpu = atoi(p);
1015+ if (cpu > curr_cpu) {
1016+ *cpus = append_cpus(*cpus, curr_cpu, cpu - 1,
1017+ comma, &total);
1018+ comma = ",";
1019+ }
1020+ while (isdigit(*p))
1021+ p++;
1022+ if (*p == '-') {
1023+ p++;
1024+ cpu = atoi(p);
1025+ while (isdigit(*p))
1026+ p++;
1027+ }
1028+ curr_cpu = cpu + 1;
1029+ if (*p)
1030+ p++;
1031+ }
1032+ 
1033+ if (curr_cpu < cpu_count) {
1034+ *cpus = append_cpus(*cpus, curr_cpu, cpu_count - 1,
1035+ comma, &total);
1036+ }
1037+}
1038+ 
1039+static int calc_nr_cpus(const char *setcpu, char **buf)
1040+{
1041+ struct cpu_list *cpu_list = NULL;
1042+ const char *p;
1043+ int end_cpu;
1044+ int cpu;
1045+ 
1046+ for (p = setcpu; *p; ) {
1047+ cpu = atoi(p);
1048+ if (cpu < 0 || (!cpu && *p != '0'))
1049+ goto err;
1050+ 
1051+ while (isdigit(*p))
1052+ p++;
1053+ if (*p == '-') {
1054+ p++;
1055+ end_cpu = atoi(p);
1056+ if (end_cpu < cpu || (!end_cpu && *p != '0'))
1057+ goto err;
1058+ while (isdigit(*p))
1059+ p++;
1060+ } else
1061+ end_cpu = cpu;
1062+ 
1063+ add_cpus(&cpu_list, cpu, end_cpu);
1064+ if (*p == ',')
1065+ p++;
1066+ }
1067+ 
1068+ make_new_list(cpu_list, buf);
1069+ return count_cpus(cpu_list);
1070+ err:
1071+ /* Frees the list */
1072+ count_cpus(cpu_list);
1073+ return -1;
1074+}
1075+ 
1076+static void sighand(int sig __attribute__ ((unused)))
1077+{
1078+ shutdown = 1;
1079+}
1080+ 
1081+static const char *join_thread(pthread_t *thread)
1082+{
1083+ void *result;
1084+ 
1085+ pthread_join(*thread, &result);
1086+ return result;
1087+}
1088+ 
1089+static void loop(struct sched_data *sched_data, int nr_threads)
1090+{
1091+ int i;
1092+ 
1093+ while (!shutdown) {
1094+ for (i = 0; i < nr_threads; i++)
1095+ print_stat(stdout, &sched_data[i], i, quiet);
1096+ usleep(10000);
1097+ if (!quiet)
1098+ printf("\033[%dA", nr_threads);
1099+ }
1100+ usleep(10000);
1101+ if (!quiet) {
1102+ printf("\033[%dB", nr_threads + 2);
1103+ } else if (!histogram) {
1104+ for (i = 0; i < nr_threads; ++i)
1105+ print_stat(stdout, &sched_data[i], i, 0);
1106+ }
1107+ 
1108+ if (histogram) {
1109+ FILE *out = histfile ? histfile : stdout;
1110+ 
1111+ print_hist(out, sched_data, nr_threads);
1112+ }
1113+}
1114+ 
1115+static void write_stats(FILE *f, void *data)
1116+{
1117+ struct sched_data *sd = data;
1118+ struct thread_stat *s;
1119+ int i;
1120+ 
1121+ fprintf(f, " \"num_threads\": %d,\n", nr_threads);
1122+ fprintf(f, " \"resolution_in_ns\": %u,\n", use_nsecs);
1123+ fprintf(f, " \"thread\": {\n");
1124+ for (i = 0; i < nr_threads; i++) {
1125+ s = &sd[i].stat;
1126+ fprintf(f, " \"%u\": {\n", i);
1127+ if (s->hist) {
1128+ fprintf(f, " \"histogram\": {");
1129+ hist_print_json(s->hist, f);
1130+ fprintf(f, " },\n");
1131+ }
1132+ fprintf(f, " \"cycles\": %ld,\n", s->cycles);
1133+ fprintf(f, " \"min\": %ld,\n", s->min);
1134+ fprintf(f, " \"max\": %ld,\n", s->max);
1135+ fprintf(f, " \"avg\": %.2f\n", s->avg/s->cycles);
1136+ fprintf(f, " }%s\n", i == nr_threads - 1 ? "" : ",");
1137+ }
1138+ fprintf(f, " }\n");
1139+}
1140+ 
1141+enum options_values {
1142+ OPT_AFFINITY=1, OPT_DURATION, OPT_HELP, OPT_INTERVAL,
1143+ OPT_JSON, OPT_STEP, OPT_THREADS, OPT_QUIET,
1144+ OPT_BREAKTRACE, OPT_TRACEMARK, OPT_INFO, OPT_DEBUG,
1145+ OPT_HISTOGRAM, OPT_HISTFILE
1146+};
1147+ 
1148+int main(int argc, char **argv)
1149+{
1150+ struct sched_data *sched_data;
1151+ struct sched_data *sd;
1152+ const char *res;
1153+ const char *setcpu = NULL;
1154+ char *setcpu_buf = NULL;
1155+ char *allcpu_buf = NULL;
1156+ pthread_t *thread;
1157+ unsigned int interval = 1000;
1158+ unsigned int step = 500;
1159+ int percent = 60;
1160+ int duration = 0;
1161+ u64 runtime;
1162+ u64 start_period;
1163+ u64 end_period;
1164+ int nr_cpus;
1165+ int i;
1166+ int c;
1167+ 
1168+ rt_init(argc, argv);
1169+ 
1170+ cpu_count = sysconf(_SC_NPROCESSORS_CONF);
1171+ if (cpu_count < 1)
1172+ err_quit("Can not calculate number of CPUS\n");
1173+ 
1174+ for (;;) {
1175+ static struct option options[] = {
1176+ { "affinity", optional_argument, NULL, OPT_AFFINITY },
1177+ { "duration", required_argument, NULL, OPT_DURATION },
1178+ { "help", no_argument, NULL, OPT_HELP },
1179+ { "interval", required_argument, NULL, OPT_INTERVAL },
1180+ { "json", required_argument, NULL, OPT_JSON },
1181+ { "step", required_argument, NULL, OPT_STEP },
1182+ { "threads", required_argument, NULL, OPT_THREADS },
1183+ { "quiet", no_argument, NULL, OPT_QUIET },
1184+ { "breaktrace", required_argument, NULL, OPT_BREAKTRACE },
1185+ { "tracemark", no_argument, NULL, OPT_TRACEMARK },
1186+ { "verbose", no_argument, NULL, OPT_INFO},
1187+ { "debug", no_argument, NULL, OPT_DEBUG},
1188+ { "histogram", required_argument, NULL, OPT_HISTOGRAM },
1189+ { "histfile", required_argument, NULL, OPT_HISTFILE },
1190+ { NULL, 0, NULL, 0 },
1191+ };
1192+ c = getopt_long(argc, argv, "a::c:D:hi:s:t:b:q", options, NULL);
1193+ if (c == -1)
1194+ break;
1195+ switch (c) {
1196+ case OPT_AFFINITY:
1197+ case 'a':
1198+ case 'c':
1199+ if (!nr_threads)
1200+ nr_threads = cpu_count;
1201+ if (optarg)
1202+ setcpu = optarg;
1203+ else if (optind < argc)
1204+ setcpu = argv[optind];
1205+ else
1206+ all_cpus = 1;
1207+ break;
1208+ case OPT_INTERVAL:
1209+ case 'i':
1210+ interval = atoi(optarg);
1211+ break;
1212+ case OPT_JSON:
1213+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
1214+ break;
1215+ case OPT_STEP:
1216+ case 's':
1217+ step = atoi(optarg);
1218+ break;
1219+ case OPT_THREADS:
1220+ case 't':
1221+ nr_threads = atoi(optarg);
1222+ break;
1223+ case OPT_DURATION:
1224+ case 'D':
1225+ duration = parse_time_string(optarg);
1226+ break;
1227+ case 'b':
1228+ case OPT_BREAKTRACE:
1229+ tracelimit = atoi(optarg);
1230+ break;
1231+ case OPT_QUIET:
1232+ case 'q':
1233+ quiet = 1;
1234+ break;
1235+ case OPT_HELP:
1236+ case 'h':
1237+ usage(0);
1238+ break;
1239+ case OPT_TRACEMARK:
1240+ trace_marker = 1;
1241+ break;
1242+ case OPT_INFO:
1243+ info_enable = 1;
1244+ break;
1245+ case OPT_DEBUG:
1246+ debug_enable = 1;
1247+ break;
1248+ case OPT_HISTOGRAM:
1249+ histogram = atoi(optarg);
1250+ if (histogram <= 0 || histogram > HIST_MAX)
1251+ usage(1);
1252+ break;
1253+ case OPT_HISTFILE:
1254+ histfile = fopen(optarg, "w");
1255+ if (!histfile)
1256+ fatal("Couldn\'t open histfile %s: %s\n",
1257+ optarg, strerror(errno));
1258+ break;
1259+ default:
1260+ usage(1);
1261+ }
1262+ }
1263+ 
1264+ if (!nr_threads)
1265+ nr_threads = 1;
1266+ 
1267+ if (setcpu) {
1268+ nr_cpus = calc_nr_cpus(setcpu, &setcpu_buf);
1269+ if (nr_cpus < 0 || nr_cpus > cpu_count)
1270+ fatal("Invalid cpu input '%s'\n", setcpu);
1271+ } else
1272+ nr_cpus = cpu_count;
1273+ 
1274+ if (!all_cpus && cpu_count == nr_cpus) {
1275+ printf("Using all CPUS\n");
1276+ all_cpus = 1;
1277+ }
1278+ 
1279+ /* Default cpu to use is the last one */
1280+ if (!all_cpus && !setcpu) {
1281+ setcpu_buf = malloc(12);
1282+ if (!setcpu_buf)
1283+ fatal("malloc");
1284+ sprintf(setcpu_buf, "%d", cpu_count - 1);
1285+ }
1286+ 
1287+ setcpu = setcpu_buf;
1288+ 
1289+ if (setcpu)
1290+ make_other_cpu_list(setcpu, &allcpu_buf);
1291+ 
1292+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1)
1293+ warn("mlockall");
1294+ 
1295+ setup_ftrace_marker();
1296+ if (tracelimit && trace_marker)
1297+ enable_trace_mark();
1298+ 
1299+ thread = calloc(nr_threads, sizeof(*thread));
1300+ sched_data = calloc(nr_threads, sizeof(*sched_data));
1301+ if (!thread || !sched_data)
1302+ fatal("allocating threads");
1303+ 
1304+ if (histogram && hset_init(&hset, nr_threads, 1, histogram, histogram))
1305+ fatal("failed to allocate histogram of size %d for %d threads\n",
1306+ histogram, nr_threads);
1307+ 
1308+ if (nr_threads > nr_cpus) {
1309+ /*
1310+ * More threads than CPUs, then have the total be
1311+ * no more than 80 percent.
1312+ */
1313+ percent = nr_cpus * 80 / nr_threads;
1314+ }
1315+ 
1316+ /* Set up the data while still in SCHED_FIFO */
1317+ for (i = 0; i < nr_threads; i++) {
1318+ sd = &sched_data[i];
1319+ /*
1320+ * Interval is the deadline/period
1321+ * The runtime is the percentage of that period.
1322+ */
1323+ runtime = interval * percent / 100;
1324+ 
1325+ if (runtime < 2000) {
1326+ /*
1327+ * If the runtime is less than 2ms, then we better
1328+ * have HRTICK enabled.
1329+ */
1330+ if (!setup_hr_tick())
1331+ fatal("For less than 2ms run times, you need to\n"
1332+ "have HRTICK enabled in debugfs/sched_features\n");
1333+ }
1334+ sd->runtime_us = runtime;
1335+ sd->deadline_us = interval;
1336+ 
1337+ if (histogram)
1338+ sd->stat.hist = &hset.histos[i];
1339+ 
1340+ info(info_enable, "interval: %lld:%lld\n", sd->runtime_us, sd->deadline_us);
1341+ 
1342+ /* Make sure that we can make our deadlines */
1343+ start_period = get_time_us();
1344+ do_runtime(sd, start_period);
1345+ end_period = get_time_us();
1346+ if (end_period - start_period > sd->runtime_us)
1347+ fatal("Failed to perform task within runtime: Missed by %lld us\n",
1348+ end_period - start_period - sd->runtime_us);
1349+ 
1350+ info(info_enable, " Tested at %lldus of %lldus\n",
1351+ end_period - start_period, sd->runtime_us);
1352+ 
1353+ interval += step;
1354+ }
1355+ 
1356+ 
1357+ pthread_barrier_init(&barrier, NULL, nr_threads + 1);
1358+ 
1359+ for (i = 0; i < nr_threads; i++) {
1360+ sd = &sched_data[i];
1361+ pthread_create(&thread[i], NULL, run_deadline, sd);
1362+ }
1363+ 
1364+ atexit(teardown);
1365+ 
1366+ if (shutdown)
1367+ fatal("failed to setup child threads at step 1\n");
1368+ 
1369+ if (!all_cpus) {
1370+ int *pids;
1371+ 
1372+ make_cpuset(CPUSET_ALL, allcpu_buf, "0",
1373+ CPUSET_FL_SET_LOADBALANCE |
1374+ CPUSET_FL_CLONE_CHILDREN |
1375+ CPUSET_FL_ALL_TASKS);
1376+ 
1377+ pids = calloc(nr_threads + 1, sizeof(int));
1378+ if (!pids)
1379+ fatal("Allocating pids");
1380+ 
1381+ for (i = 0; i < nr_threads; i++)
1382+ pids[i] = sched_data[i].stat.tid;
1383+ 
1384+ make_cpuset(CPUSET_LOCAL, setcpu, "0",
1385+ CPUSET_FL_CPU_EXCLUSIVE |
1386+ CPUSET_FL_SET_LOADBALANCE |
1387+ CPUSET_FL_CLONE_CHILDREN |
1388+ CPUSET_FL_TASKS, pids);
1389+ 
1390+ system("cat /sys/fs/cgroup/cpuset/my_cpuset/tasks");
1391+ }
1392+ 
1393+ debug(debug_enable, "main thread %d\n", gettid());
1394+ 
1395+ if (shutdown)
1396+ fatal("failed to setup child threads at step 2");
1397+ 
1398+ pthread_barrier_wait(&barrier);
1399+ 
1400+ signal(SIGINT, sighand);
1401+ signal(SIGTERM, sighand);
1402+ signal(SIGALRM, sighand);
1403+ 
1404+ if (duration)
1405+ alarm(duration);
1406+ 
1407+ loop(sched_data, nr_threads);
1408+ 
1409+ if (tracelimit) {
1410+ if (break_thread_id) {
1411+ printf("# Break thread: %d\n", break_thread_id);
1412+ printf("# Break value: %llu\n", (unsigned long long)break_thread_value);
1413+ }
1414+ }
1415+ 
1416+ for (i = 0; i < nr_threads; i++) {
1417+ 
1418+ sd = &sched_data[i];
1419+ 
1420+ res = join_thread(&thread[i]);
1421+ if (res) {
1422+ warn("Thread %d failed: %s\n", i, res);
1423+ continue;
1424+ }
1425+ }
1426+ 
1427+ if (strlen(jsonfile) != 0)
1428+ rt_write_json(jsonfile, 0, write_stats, sched_data);
1429+ 
1430+ if (setcpu_buf)
1431+ free(setcpu_buf);
1432+ free(thread);
1433+ free(sched_data);
1434+ if (histfile)
1435+ fclose(histfile);
1436+ hset_destroy(&hset);
1437+ 
1438+ return 0;
1439+}
@@ -0,0 +1,65 @@
1+.\" Hey, EMACS: -*- nroff -*-
2+.TH DEADLINE_TEST 8 "November 1, 2018"
3+# SPDX-License-Identifier: GPL-2.0-only
4+.\" Please adjust this date whenever revising the manpage.
5+.\"
6+.\" Some roff macros, for reference:
7+.\" .nh disable hyphenation
8+.\" .hy enable hyphenation
9+.\" .ad l left justify
10+.\" .ad b justify to both left and right margins
11+.\" .nf disable filling
12+.\" .fi enable filling
13+.\" .br insert line break
14+.\" .sp <n> insert n+1 empty lines
15+.\" for manpage-specific macros, see man(7)
16+.SH NAME
17+deadline_test \- High resolution test program
18+.SH DESCRIPTION
19+.PP
20+This program is used to test the deadline scheduler (SCHED_DEADLINE tasks)
21+.SH SYNOPSIS
22+.B deadline_test
23+.RI "[ \-hb ] [ \-c CPUSET ] [ \-i INTV ] [ \-p PERCENT ] [ \-P PERCENT ] \
24+[ \-r PRIO ] [ \-s STEP ] [ \-t NUM ]"
25+.SH OPTIONS
26+.TP
27+.B \-b
28+Bind on the last cpu. (shortcut for -c <lastcpu>)
29+.br
30+.TP
31+.B \-c CPUSET
32+Comma/hyphen separated list of CPUs to run deadline tasks on
33+.br
34+.TP
35+.B \-h
36+Show this help menu
37+.br
38+.TP
39+.B \-i INTV
40+The shortest deadline for the tasks
41+.br
42+.TP
43+.B \-p PERCENT
44+The percent of bandwidth to use (1-90%)
45+.br
46+.TP
47+.B \-P PERCENT
48+The percent of runtime for execution completion (default 100%)
49+.br
50+.TP
51+.B \-r PRIO
52+Add an RT task with given prio to stress system
53+.br
54+.TP
55+.B \-s STEP
56+The amount to increase the deadline for each task in us (default 500us)
57+.br
58+.TP
59+.B \-t NUM
60+The number of threads to run as deadline (default 1)
61+.br
62+.SH AUTHOR
63+Deadline test was written by Steven Rostedt <rostedt@goodmis.org>
64+.PP
65+This manual page was written by John Kacur <jkacur@redhat.com>
@@ -0,0 +1,76 @@
1+.\"
2+.TH SIGNALTEST 8 "November 15, 2020"
3+# SPDX-License-Identifier: GPL-2.0-only
4+.\" Please adjust this date whenever updating this manpage
5+.SH NAME
6+signaltest \- signal roundtrip test software
7+.SH SYNOPSIS
8+.LP
9+signaltest [ -a|--affinity NUM] [ -b|--backtrace USEC ] [-D|--duration TIME] [-h|--help] [--json FILENAME] [-l|--loops LOOPS ] [-p|--prio PRIO] [-q|--quiet] [-S|--smp] [-t|--threads NUM] [-m|--mlockall ] [-v|--verbose ]
10+.SH OPTIONS
11+These programs follow the usual GNU command line syntax, with long options
12+starting with two dashes ('\-\-').
13+.TP
14+.B \-a, \-\-affinity[=PROC-SET]
15+Run threads on the set of processors given by PROC-SET. If PROC-SET is not
16+specified, all processors will be used. Threads will be assigned to processors
17+in the set in numeric order, in a round\-robin fashion.
18+.br
19+The set of processors can be specified as A,B,C, or A-C, or A-B,D-F, and so on*.
20+The ! character can be used to negate a set. For example, !B-D means to use all
21+available CPUs except B through D. The cpu numbers are the same as shown in the
22+.I processor
23+field in /proc/cpuinfo. See numa(3) for more information on specifying CPU sets.
24+* Support for CPU sets requires libnuma version >= 2. For libnuma v1, PROC-SET,
25+if specified, must be a single CPU number.
26+.TP
27+.B \-b, \-\-breaktrace=USEC
28+Send break trace command when latency > USEC
29+.TP
30+.B \-D, \-\-duration=TIME
31+Specify a length for the test run.
32+.br
33+Append 'm', 'h', or 'd' to specify minutes, hours or days.
34+.TP
35+.B \-\-help
36+.br
37+display usage information
38+.TP
39+.B \-\-json=FILENAME
40+Write final results into FILENAME, JSON formatted.
41+.TP
42+.B \-l, \-\-loops=LOOPS
43+Number of loops: default=0 (endless)
44+.TP
45+.B \-p, \-\-priority=PRIO
46+Priority of highest priority thread
47+.TP
48+.B \-q, \-\-quiet
49+print a summary only on exit
50+.TP
51+.B \\-S, \-\-smp
52+Set options for standard testing on SMP systems. Equivalent to using
53+the options: "\-t \-a" as well keeping any specified priority
54+equal across all threads
55+.TP
56+.B \-t, \-\-threads=NUM
57+number of threads: default=2
58+.TP
59+.B \-m, \-\-mlockall
60+lock current and future memory allocations
61+.TP
62+.B \-v, \-\-verbose
63+output values on stdout for statistics
64+.br
65+format: n:c:v n=tasknum c=count v=value in us
66+.SH AUTHOR
67+signaltest was written by Thomas Gleixner <tglx@linuxtronix.de>.
68+.PP
69+This manual page was originally written by
70+.br
71+John Kacur <jkacur@redhat.com>
72+.PP
73+Copying and distribution of this file, with or without modification,
74+are permitted in any medium without royalty provided the copyright
75+notice and this notice are preserved. This file is offered as-is,
76+without any warranty.
@@ -0,0 +1,569 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+ 
3+/*
4+ * RT signal roundtrip test software
5+ *
6+ * (C) 2007 Thomas Gleixner <tglx@linutronix.de>
7+ *
8+ */
9+ 
10+#include <fcntl.h>
11+#include <getopt.h>
12+#include <pthread.h>
13+#include <signal.h>
14+#include <stdlib.h>
15+#include <stdio.h>
16+#include <string.h>
17+#include <time.h>
18+#include <unistd.h>
19+#include <errno.h>
20+#include <sched.h>
21+#include <inttypes.h>
22+ 
23+#include <linux/unistd.h>
24+ 
25+#include <sys/prctl.h>
26+#include <sys/stat.h>
27+#include <sys/types.h>
28+#include <sys/time.h>
29+#include <sys/mman.h>
30+ 
31+#include "rt-error.h"
32+#include "rt-utils.h"
33+#include "rt-numa.h"
34+ 
35+/* Must be power of 2 ! */
36+#define VALBUF_SIZE 16384
37+ 
38+/* Struct to transfer parameters to the thread */
39+struct thread_param {
40+ int id;
41+ int prio;
42+ int signal;
43+ unsigned long max_cycles;
44+ struct thread_stat *stats;
45+ int bufmsk;
46+ int cpu;
47+};
48+ 
49+/* Struct for statistics */
50+struct thread_stat {
51+ unsigned long cycles;
52+ unsigned long cyclesread;
53+ long min;
54+ long max;
55+ long act;
56+ double avg;
57+ long *values;
58+ pthread_t thread;
59+ pthread_t tothread;
60+ int threadstarted;
61+ int tid;
62+ int interrupted;
63+};
64+ 
65+static int shutdown;
66+static int tracelimit;
67+ 
68+ 
69+/*
70+ * signal thread
71+ *
72+ */
73+void *signalthread(void *param)
74+{
75+ struct thread_param *par = param;
76+ struct sched_param schedp;
77+ sigset_t sigset;
78+ struct timespec before, after;
79+ struct thread_stat *stat = par->stats;
80+ int policy = par->prio ? SCHED_FIFO : SCHED_OTHER;
81+ int stopped = 0;
82+ int first = 1;
83+ pthread_t thread;
84+ cpu_set_t mask;
85+ 
86+ stat->tid = gettid();
87+ 
88+ if (par->cpu != -1) {
89+ CPU_ZERO(&mask);
90+ CPU_SET(par->cpu, &mask);
91+ thread = pthread_self();
92+ if (pthread_setaffinity_np(thread, sizeof(mask), &mask) != 0)
93+ warn("Could not set CPU affinity to CPU #%d\n",
94+ par->cpu);
95+ }
96+ 
97+ sigemptyset(&sigset);
98+ sigaddset(&sigset, par->signal);
99+ sigprocmask(SIG_BLOCK, &sigset, NULL);
100+ 
101+ memset(&schedp, 0, sizeof(schedp));
102+ schedp.sched_priority = par->prio;
103+ sched_setscheduler(0, policy, &schedp);
104+ 
105+ stat->threadstarted++;
106+ 
107+ clock_gettime(CLOCK_MONOTONIC, &before);
108+ 
109+ while (!shutdown) {
110+ struct timespec now;
111+ long diff;
112+ int sigs;
113+ 
114+ if (sigwait(&sigset, &sigs) < 0)
115+ goto out;
116+ 
117+ clock_gettime(CLOCK_MONOTONIC, &after);
118+ 
119+ /*
120+ * If it is the first thread, sleep after every 16
121+ * round trips.
122+ */
123+ if (!par->id && !(stat->cycles & 0x0F))
124+ usleep(10000);
125+ 
126+ /* Get current time */
127+ clock_gettime(CLOCK_MONOTONIC, &now);
128+ pthread_kill(stat->tothread, SIGUSR1);
129+ 
130+ /* Skip the first cycle */
131+ if (first) {
132+ first = 0;
133+ before = now;
134+ continue;
135+ }
136+ 
137+ diff = calcdiff(after, before);
138+ before = now;
139+ 
140+ if (diff < stat->min)
141+ stat->min = diff;
142+ if (diff > stat->max)
143+ stat->max = diff;
144+ stat->avg += (double) diff;
145+ 
146+ if (!stopped && tracelimit && !par->id && (diff > tracelimit)) {
147+ stat->act = diff;
148+ stat->interrupted = 1;
149+ stopped++;
150+ shutdown++;
151+ }
152+ stat->act = diff;
153+ stat->cycles++;
154+ 
155+ if (par->bufmsk)
156+ stat->values[stat->cycles & par->bufmsk] = diff;
157+ 
158+ if (par->max_cycles && par->max_cycles == stat->cycles)
159+ break;
160+ }
161+ 
162+out:
163+ /* switch to normal */
164+ schedp.sched_priority = 0;
165+ sched_setscheduler(0, SCHED_OTHER, &schedp);
166+ 
167+ stat->threadstarted = -1;
168+ 
169+ return NULL;
170+}
171+ 
172+ 
173+/* Print usage information */
174+static void display_help(int error)
175+{
176+ printf("signaltest V %1.2f\n", VERSION);
177+ printf("Usage:\n"
178+ "signaltest <options>\n\n"
179+ "-a [NUM] --affinity run thread #N on processor #N, if possible\n"
180+ " with NUM pin all threads to the processor NUM\n"
181+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
182+ "-D --duration=TIME specify a length for the test run.\n"
183+ " Append 'm', 'h', or 'd' to specify minutes, hours or\n"
184+ " days.\n"
185+ "-h --help display usage information\n"
186+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
187+ "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n"
188+ "-m --mlockall lock current and future memory allocations\n"
189+ "-p PRIO --prio=PRIO priority of highest prio thread\n"
190+ "-q --quiet print a summary only on exit\n"
191+ "-t NUM --threads=NUM number of threads: default=2\n"
192+ "-v --verbose output values on stdout for statistics\n"
193+ " format: n:c:v n=tasknum c=count v=value in us\n"
194+ );
195+ exit(error);
196+}
197+ 
198+static int priority;
199+static int num_threads = 2;
200+static int max_cycles;
201+static int duration;
202+static int verbose;
203+static int quiet;
204+static int lockall;
205+static struct bitmask *affinity_mask = NULL;
206+static int smp = 0;
207+static int numa = 0;
208+static int setaffinity = AFFINITY_UNSPECIFIED;
209+static char jsonfile[MAX_PATH];
210+ 
211+enum option_values {
212+ OPT_AFFINITY=1, OPT_BREAKTRACE,
213+ OPT_DURATION, OPT_HELP, OPT_JSON,
214+ OPT_LOOPS, OPT_MLOCKALL, OPT_PRIORITY,
215+ OPT_QUIET, OPT_SMP, OPT_THREADS, OPT_VERBOSE
216+};
217+ 
218+/* Process commandline options */
219+static void process_options(int argc, char *argv[], unsigned int max_cpus)
220+{
221+ int option_affinity = 0;
222+ int error = 0;
223+ 
224+ for (;;) {
225+ int option_index = 0;
226+ /** Options for getopt */
227+ static struct option long_options[] = {
228+ {"affinity", optional_argument, NULL, OPT_AFFINITY},
229+ {"breaktrace", required_argument, NULL, OPT_BREAKTRACE},
230+ {"duration", required_argument, NULL, OPT_DURATION},
231+ {"help", no_argument, NULL, OPT_HELP},
232+ {"json", required_argument, NULL, OPT_JSON},
233+ {"loops", required_argument, NULL, OPT_LOOPS},
234+ {"mlockall", no_argument, NULL, OPT_MLOCKALL},
235+ {"priority", required_argument, NULL, OPT_PRIORITY},
236+ {"quiet", no_argument, NULL, OPT_QUIET},
237+ {"smp", no_argument, NULL, OPT_SMP},
238+ {"threads", required_argument, NULL, OPT_THREADS},
239+ {"verbose", no_argument, NULL, OPT_VERBOSE},
240+ {NULL, 0, NULL, 0}
241+ };
242+ int c = getopt_long(argc, argv, "a::b:D:hl:mp:qSt:v",
243+ long_options, &option_index);
244+ if (c == -1)
245+ break;
246+ switch (c) {
247+ case OPT_AFFINITY:
248+ case 'a':
249+ option_affinity = 1;
250+ /* smp sets AFFINITY_USEALL in OPT_SMP */
251+ if (smp)
252+ break;
253+ numa = numa_initialize();
254+ if (optarg) {
255+ parse_cpumask(optarg, max_cpus, &affinity_mask);
256+ setaffinity = AFFINITY_SPECIFIED;
257+ } else if (optind < argc &&
258+ (atoi(argv[optind]) ||
259+ argv[optind][0] == '0' ||
260+ argv[optind][0] == '!' ||
261+ argv[optind][0] == '+' ||
262+ argv[optind][0] == 'a')) {
263+ parse_cpumask(argv[optind], max_cpus, &affinity_mask);
264+ setaffinity = AFFINITY_SPECIFIED;
265+ } else {
266+ setaffinity = AFFINITY_USEALL;
267+ }
268+ 
269+ if (setaffinity == AFFINITY_SPECIFIED && !affinity_mask)
270+ display_help(1);
271+ if (verbose)
272+ printf("Using %u cpus.\n",
273+ numa_bitmask_weight(affinity_mask));
274+ break;
275+ case OPT_BREAKTRACE:
276+ case 'b':
277+ tracelimit = atoi(optarg);
278+ break;
279+ case OPT_DURATION:
280+ case 'D':
281+ duration = parse_time_string(optarg);
282+ break;
283+ case OPT_HELP:
284+ case '?':
285+ case 'h':
286+ display_help(0);
287+ break;
288+ case OPT_JSON:
289+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
290+ break;
291+ case OPT_LOOPS:
292+ case 'l':
293+ max_cycles = atoi(optarg);
294+ break;
295+ case OPT_MLOCKALL:
296+ case 'm':
297+ lockall = 1;
298+ break;
299+ case OPT_PRIORITY:
300+ case 'p':
301+ priority = atoi(optarg);
302+ break;
303+ case OPT_QUIET:
304+ case 'q':
305+ quiet = 1;
306+ break;
307+ case OPT_SMP:
308+ case 'S':
309+ if (numa)
310+ fatal("numa and smp options are mutually exclusive\n");
311+ smp = 1;
312+ num_threads = -1; /* update after parsing */
313+ setaffinity = AFFINITY_USEALL;
314+ break;
315+ case OPT_THREADS:
316+ case 't':
317+ num_threads = atoi(optarg);
318+ break;
319+ case OPT_VERBOSE:
320+ case 'v': verbose = 1;
321+ break;
322+ }
323+ }
324+ 
325+ if (duration < 0)
326+ error = 1;
327+ 
328+ if (priority < 0 || priority > 99)
329+ error = 1;
330+ 
331+ if (num_threads == -1)
332+ num_threads = get_available_cpus(affinity_mask);
333+ 
334+ if (num_threads < 2)
335+ error = 1;
336+ 
337+ /* if smp wasn't requested, test for numa automatically */
338+ if (!smp) {
339+ numa = numa_initialize();
340+ if (setaffinity == AFFINITY_UNSPECIFIED)
341+ setaffinity = AFFINITY_USEALL;
342+ }
343+ 
344+ if (option_affinity) {
345+ if (smp)
346+ warn("-a ignored due to smp mode\n");
347+ }
348+ 
349+ if (error) {
350+ if (affinity_mask)
351+ numa_bitmask_free(affinity_mask);
352+ display_help(error);
353+ }
354+}
355+ 
356+static void sighand(int sig __attribute__ ((unused)))
357+{
358+ shutdown = 1;
359+}
360+ 
361+static void print_stat(struct thread_param *par, int index, int verbose)
362+{
363+ struct thread_stat *stat = par->stats;
364+ 
365+ if (!verbose) {
366+ if (quiet != 1) {
367+ printf("T:%2d (%5d) P:%2d C:%7lu "
368+ "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n",
369+ index, stat->tid, par->prio,
370+ stat->cycles, stat->min, stat->act,
371+ stat->cycles ?
372+ (long)(stat->avg/stat->cycles) : 0, stat->max);
373+ }
374+ } else {
375+ while (stat->cycles != stat->cyclesread) {
376+ long diff = stat->values[stat->cyclesread & par->bufmsk];
377+ printf("%8d:%8lu:%8ld\n", index, stat->cyclesread, diff);
378+ stat->cyclesread++;
379+ }
380+ }
381+}
382+ 
383+static void write_stats(FILE *f, void *data)
384+{
385+ struct thread_param *par = data;
386+ struct thread_stat *s;
387+ int i;
388+ 
389+ fprintf(f, " \"num_threads\": %d,\n", num_threads);
390+ fprintf(f, " \"thread\": {\n");
391+ for (i = 0; i < num_threads; i++) {
392+ fprintf(f, " \"%u\": {\n", i);
393+ s = &par->stats[i];
394+ fprintf(f, " \"cycles\": %ld,\n", s->cycles);
395+ fprintf(f, " \"min\": %ld,\n", s->min);
396+ fprintf(f, " \"max\": %ld,\n", s->max);
397+ fprintf(f, " \"avg\": %.2f,\n", s->avg/s->cycles);
398+ fprintf(f, " \"cpu\": %d\n", par->cpu);
399+ fprintf(f, " }%s\n", i == num_threads - 1 ? "" : ",");
400+ 
401+ }
402+ fprintf(f, " }\n");
403+}
404+ 
405+int main(int argc, char **argv)
406+{
407+ sigset_t sigset;
408+ int signum = SIGUSR1;
409+ struct thread_param *par;
410+ struct thread_stat *stat;
411+ int i, ret = -1;
412+ int status, cpu;
413+ int max_cpus = sysconf(_SC_NPROCESSORS_ONLN);
414+ 
415+ rt_init(argc, argv);
416+ process_options(argc, argv, max_cpus);
417+ 
418+ if (check_privs())
419+ exit(1);
420+ 
421+ /* lock all memory (prevent paging) */
422+ if (lockall)
423+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
424+ perror("mlockall");
425+ goto out;
426+ }
427+ 
428+ /* Restrict the main pid to the affinity specified by the user */
429+ if (affinity_mask != NULL) {
430+ int res;
431+ 
432+ errno = 0;
433+ res = numa_sched_setaffinity(getpid(), affinity_mask);
434+ if (res != 0)
435+ warn("Couldn't setaffinity in main thread: %s\n", strerror(errno));
436+ }
437+ 
438+ sigemptyset(&sigset);
439+ sigaddset(&sigset, signum);
440+ sigprocmask(SIG_BLOCK, &sigset, NULL);
441+ 
442+ signal(SIGINT, sighand);
443+ signal(SIGTERM, sighand);
444+ signal(SIGALRM, sighand);
445+ 
446+ if (duration)
447+ alarm(duration);
448+ 
449+ par = calloc(num_threads, sizeof(struct thread_param));
450+ if (!par)
451+ goto out;
452+ stat = calloc(num_threads, sizeof(struct thread_stat));
453+ if (!stat)
454+ goto outpar;
455+ 
456+ for (i = 0; i < num_threads; i++) {
457+ if (verbose) {
458+ stat[i].values = calloc(VALBUF_SIZE, sizeof(long));
459+ if (!stat[i].values)
460+ goto outall;
461+ par[i].bufmsk = VALBUF_SIZE - 1;
462+ }
463+ 
464+ switch (setaffinity) {
465+ case AFFINITY_UNSPECIFIED:
466+ cpu = -1;
467+ break;
468+ case AFFINITY_SPECIFIED:
469+ cpu = cpu_for_thread_sp(i, max_cpus, affinity_mask);
470+ if (verbose)
471+ printf("Thread %d using cpu %d.\n", i, cpu);
472+ break;
473+ case AFFINITY_USEALL:
474+ cpu = cpu_for_thread_ua(i, max_cpus);
475+ break;
476+ default:
477+ cpu = -1;
478+ }
479+ 
480+ par[i].id = i;
481+ par[i].prio = priority;
482+#if 0
483+ if (priority)
484+ priority--;
485+#endif
486+ par[i].signal = signum;
487+ par[i].max_cycles = max_cycles;
488+ par[i].stats = &stat[i];
489+ par[i].cpu = cpu;
490+ stat[i].min = 1000000;
491+ stat[i].max = -1000000;
492+ stat[i].avg = 0.0;
493+ stat[i].threadstarted = 1;
494+ status = pthread_create(&stat[i].thread, NULL, signalthread,
495+ &par[i]);
496+ if (status)
497+ fatal("failed to create thread %d: %s\n", i,
498+ strerror(status));
499+ }
500+ 
501+ while (!shutdown) {
502+ int allstarted = 1;
503+ 
504+ for (i = 0; i < num_threads; i++) {
505+ if (stat[i].threadstarted != 2)
506+ allstarted = 0;
507+ }
508+ if (!allstarted)
509+ continue;
510+ 
511+ for (i = 0; i < num_threads - 1; i++)
512+ stat[i].tothread = stat[i+1].thread;
513+ stat[i].tothread = stat[0].thread;
514+ break;
515+ }
516+ pthread_kill(stat[0].thread, signum);
517+ 
518+ while (!shutdown) {
519+ char lavg[256];
520+ int fd, len, allstopped = 0;
521+ 
522+ if (!verbose && !quiet) {
523+ fd = open("/proc/loadavg", O_RDONLY, 0666);
524+ len = read(fd, &lavg, 255);
525+ close(fd);
526+ lavg[len-1] = 0x0;
527+ printf("%s \n\n", lavg);
528+ }
529+ 
530+ print_stat(&par[0], 0, verbose);
531+ if (max_cycles && stat[0].cycles >= max_cycles)
532+ allstopped++;
533+ 
534+ usleep(10000);
535+ if (shutdown || allstopped)
536+ break;
537+ if (!verbose && !quiet)
538+ printf("\033[%dA", 3);
539+ }
540+ ret = 0;
541+ outall:
542+ shutdown = 1;
543+ usleep(50000);
544+ if (quiet)
545+ quiet = 2;
546+ for (i = 0; i < num_threads; i++) {
547+ if (stat[i].threadstarted > 0)
548+ pthread_kill(stat[i].thread, SIGUSR1);
549+ if (stat[i].interrupted)
550+ printf("Thread %d exceeded trace limit.\n", i);
551+ if (stat[i].threadstarted) {
552+ pthread_join(stat[i].thread, NULL);
553+ print_stat(&par[i], i, 0);
554+ }
555+ if (stat[i].values)
556+ free(stat[i].values);
557+ }
558+ if (strlen(jsonfile) != 0)
559+ rt_write_json(jsonfile, ret, write_stats, par);
560+ 
561+ free(stat);
562+ outpar:
563+ free(par);
564+ out:
565+ if (lockall)
566+ munlockall();
567+ 
568+ exit(ret);
569+}
@@ -0,0 +1,66 @@
1+.TH "sigwaittest" "8" "0.1" "" ""
2+# SPDX-License-Identifier: GPL-2.0-only
3+.SH "NAME"
4+.LP
5+\fBsigwaittest\fR \- Start two threads or fork two processes and measure the latency between sending and receiving a signal
6+.SH "SYNTAX"
7+.LP
8+sigwaittest [-a|--affinity PROC] [-b|--breaktrace USEC] [-d|--distance DIST] [-D|--duration TIME] [-f|--fork [OPT]] [-i|--interval INTV] [--json FILENAME] [-l|--loops LOOPS] [-p|--prio PRIO] [-t|--threads [NUM]]
9+.br
10+.SH "DESCRIPTION"
11+.LP
12+The program \fBsigwaittest\fR starts two threads or, optionally, forks two processes that are synchronized via signals and measures the latency between sending a signal and returning from sigwait().
13+.SH "OPTIONS"
14+.TP
15+.B \-a, \-\-affinity[=PROC]
16+Run on processor number PROC. If PROC is not specified, run on current processor.
17+.TP
18+.B \-b, \-\-breaktrace=USEC
19+Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch.
20+It is useful to track down unexpected large latencies of a system.
21+.TP
22+.B \-d, \-\-distance=DIST
23+Set the distance of thread intervals in microseconds (default is 500 us). When cyclictest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST
24+.TP
25+.B \-D, \-\-duration=TIME
26+Specify a length for the test run.
27+.br
28+Append 'm', 'h', or 'd' to specify minutes, hours or days.
29+.TP
30+.B \-f, \-\-fork[=OPT]
31+Instead of creating threads (which is the default), fork new processes
32+.TP
33+.B \-i, \-\-interval=INTV
34+Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d.
35+.TP
36+.B \-\-json=FILENAME
37+Write final results into FILENAME, JSON formatted.
38+.TP
39+.B \-l, \-\-loops=LOOPS
40+Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. sigwaittest is stopped once the number of timer intervals has been reached.
41+.TP
42+.B \-p, \-\-prio=PRIO
43+Set the priority of the process.
44+.TP
45+.B \-t, \-\-threads[=NUM]
46+Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specified, NUM is set to the number of available CPUs.
47+.SH "EXAMPLES"
48+The following example was running on a 4-way CPU:
49+.LP
50+.nf
51+# sigwaittest -a -t -p99 -i100 -d25 -l1000000
52+#0: ID11510, P99, CPU0, I100; #1: ID11511, P99, CPU0, Cycles 1000000
53+#2: ID11512, P98, CPU1, I125; #3: ID11513, P98, CPU1, Cycles 817484
54+#4: ID11514, P97, CPU2, I150; #5: ID11515, P97, CPU2, Cycles 668213
55+#6: ID11516, P96, CPU3, I175; #7: ID11517, P96, CPU3, Cycles 597344
56+#1 -> #0, Min 1, Cur 2, Avg 3, Max 30
57+#3 -> #2, Min 1, Cur 26, Avg 3, Max 42
58+#5 -> #4, Min 1, Cur 46, Avg 4, Max 67
59+#7 -> #6, Min 1, Cur 2, Avg 3, Max 74
60+.fi
61+.SH "AUTHORS"
62+.LP
63+Carsten Emde <C.Emde@osadl.org>
64+.SH "SEE ALSO"
65+.LP
66+kill(2), sigwait(3)
@@ -0,0 +1,705 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+ 
3+/*
4+ * sigwaittest.c
5+ *
6+ * Copyright (C) 2009 Carsten Emde <C.Emde@osadl.org>
7+ *
8+ */
9+ 
10+#include <stdio.h>
11+#include <sys/stat.h>
12+#include <sys/mman.h>
13+#include <fcntl.h>
14+#include <stdlib.h>
15+#include <unistd.h>
16+#include <errno.h>
17+#include <limits.h>
18+#include <getopt.h>
19+#include <signal.h>
20+#include <string.h>
21+#include <time.h>
22+#include <utmpx.h>
23+#include <sys/types.h>
24+#include <sys/time.h>
25+#include <linux/unistd.h>
26+#include <utmpx.h>
27+#include <pthread.h>
28+#include <inttypes.h>
29+ 
30+#include "rt-utils.h"
31+#include "rt-get_cpu.h"
32+#include "rt-error.h"
33+ 
34+enum {
35+ AFFINITY_UNSPECIFIED,
36+ AFFINITY_SPECIFIED,
37+ AFFINITY_USEALL
38+};
39+ 
40+struct params {
41+ int num;
42+ int num_threads;
43+ int cpu;
44+ int priority;
45+ int affinity;
46+ int sender;
47+ int samples;
48+ int max_cycles;
49+ int tracelimit;
50+ int tid;
51+ pid_t pid;
52+ int shutdown;
53+ int stopped;
54+ struct timespec delay;
55+ unsigned int mindiff, maxdiff;
56+ double sumdiff;
57+ struct timeval unblocked, received, diff;
58+ pthread_t threadid;
59+ struct params *neighbor;
60+};
61+ 
62+static int mustfork;
63+static int wasforked;
64+static int wasforked_sender = -1;
65+static int wasforked_threadno = -1;
66+static int tracelimit;
67+ 
68+void *semathread(void *param)
69+{
70+ int mustgetcpu = 0;
71+ struct params *par = param;
72+ cpu_set_t mask;
73+ int policy = SCHED_FIFO;
74+ struct sched_param schedp;
75+ 
76+ memset(&schedp, 0, sizeof(schedp));
77+ schedp.sched_priority = par->priority;
78+ sched_setscheduler(0, policy, &schedp);
79+ 
80+ if (par->cpu != -1) {
81+ CPU_ZERO(&mask);
82+ CPU_SET(par->cpu, &mask);
83+ if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
84+ fprintf(stderr, "WARNING: Could not set CPU affinity "
85+ "to CPU #%d\n", par->cpu);
86+ } else {
87+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
88+ 
89+ if (max_cpus > 1)
90+ mustgetcpu = 1;
91+ else
92+ par->cpu = 0;
93+ }
94+ 
95+ if (!wasforked)
96+ par->tid = gettid();
97+ 
98+ while (!par->shutdown) {
99+ int sig;
100+ int first = 1;
101+ sigset_t sigset;
102+ struct params *neighbor = NULL;
103+ 
104+ if (par->sender) {
105+ if (wasforked)
106+ neighbor = par - par->num_threads;
107+ else
108+ neighbor = par->neighbor;
109+ if (first) {
110+ sigemptyset(&sigset);
111+ sigaddset(&sigset, SIGUSR1);
112+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
113+ first = 0;
114+ }
115+ 
116+ /* Sending signal: Start of latency measurement ... */
117+ gettimeofday(&par->unblocked, NULL);
118+ if (wasforked)
119+ kill(neighbor->pid, SIGUSR2);
120+ else
121+ pthread_kill(neighbor->threadid, SIGUSR2);
122+ par->samples++;
123+ if (par->max_cycles && par->samples >= par->max_cycles)
124+ par->shutdown = 1;
125+ 
126+ if (mustgetcpu)
127+ par->cpu = get_cpu();
128+ sigwait(&sigset, &sig);
129+ } else {
130+ /* Receiver */
131+ if (wasforked)
132+ neighbor = par + par->num_threads;
133+ else
134+ neighbor = par->neighbor;
135+ if (first) {
136+ sigemptyset(&sigset);
137+ sigaddset(&sigset, SIGUSR2);
138+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
139+ first = 0;
140+ }
141+ sigwait(&sigset, &sig);
142+ 
143+ /* ... Signal received: End of latency measurement */
144+ gettimeofday(&par->received, NULL);
145+ par->samples++;
146+ if (par->max_cycles && par->samples >= par->max_cycles)
147+ par->shutdown = 1;
148+ 
149+ if (mustgetcpu)
150+ par->cpu = get_cpu();
151+ /*
152+ * Latency is the time spent between sending and
153+ * receiving the signal.
154+ */
155+ timersub(&par->received, &neighbor->unblocked,
156+ &par->diff);
157+ 
158+ if (par->diff.tv_usec < par->mindiff)
159+ par->mindiff = par->diff.tv_usec;
160+ if (par->diff.tv_usec > par->maxdiff)
161+ par->maxdiff = par->diff.tv_usec;
162+ par->sumdiff += (double) par->diff.tv_usec;
163+ if (par->tracelimit && par->maxdiff > par->tracelimit) {
164+ char tracing_enabled_file[MAX_PATH];
165+ 
166+ strcpy(tracing_enabled_file, get_debugfileprefix());
167+ strcat(tracing_enabled_file, "tracing_on");
168+ int tracing_enabled =
169+ open(tracing_enabled_file, O_WRONLY);
170+ if (tracing_enabled >= 0) {
171+ write(tracing_enabled, "0", 1);
172+ close(tracing_enabled);
173+ } else
174+ fatal("Could not access %s\n",
175+ tracing_enabled_file);
176+ par->shutdown = 1;
177+ neighbor->shutdown = 1;
178+ }
179+ 
180+ nanosleep(&par->delay, NULL);
181+ 
182+ if (wasforked)
183+ kill(neighbor->pid, SIGUSR1);
184+ else
185+ pthread_kill(neighbor->threadid, SIGUSR1);
186+ }
187+ }
188+ par->stopped = 1;
189+ return NULL;
190+}
191+ 
192+ 
193+static void display_help(int error)
194+{
195+ printf("sigwaittest V %1.2f\n", VERSION);
196+ printf("Usage:\n"
197+ "sigwaittest <options>\n\n"
198+ "Function: test sigwait() latency\n\n"
199+ "Available options:\n"
200+ "-a [NUM] --affinity run thread #N on processor #N, if possible\n"
201+ " with NUM pin all threads to the processor NUM\n"
202+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
203+ "-d DIST --distance=DIST distance of thread intervals in us default=500\n"
204+ "-D --duration=TIME specify a length for the test run.\n"
205+ " Append 'm', 'h', or 'd' to specify minutes, hours or\n"
206+ " days.\n"
207+ "-f [OPT] --fork[=OPT] fork new processes instead of creating threads\n"
208+ "-i INTV --interval=INTV base interval of thread in us default=1000\n"
209+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
210+ "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n"
211+ "-p PRIO --prio=PRIO priority\n"
212+ "-q --quiet print a summary only on exit\n"
213+ "-t --threads one thread per available processor\n"
214+ "-t [NUM] --threads=NUM number of threads:\n"
215+ " without NUM, threads = max_cpus\n"
216+ " without -t default = 1\n"
217+ );
218+ exit(error);
219+}
220+ 
221+ 
222+static int setaffinity = AFFINITY_UNSPECIFIED;
223+static int affinity;
224+static int priority;
225+static int num_threads = 1;
226+static int max_cycles;
227+static int duration;
228+static int interval = 1000;
229+static int distance = 500;
230+static int quiet;
231+static char jsonfile[MAX_PATH];
232+ 
233+enum option_value {
234+ OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_DISTANCE, OPT_DURATION,
235+ OPT_FORK, OPT_HELP, OPT_INTERVAL, OPT_JSON, OPT_LOOPS,
236+ OPT_PRIORITY, OPT_QUIET, OPT_THREADS
237+};
238+ 
239+static void process_options(int argc, char *argv[])
240+{
241+ int error = 0;
242+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
243+ int thistracelimit = 0;
244+ 
245+ for (;;) {
246+ int option_index = 0;
247+ /** Options for getopt */
248+ static struct option long_options[] = {
249+ {"affinity", optional_argument, NULL, OPT_AFFINITY},
250+ {"breaktrace", required_argument, NULL, OPT_BREAKTRACE},
251+ {"distance", required_argument, NULL, OPT_DISTANCE},
252+ {"duration", required_argument, NULL, OPT_DURATION},
253+ {"fork", optional_argument, NULL, OPT_FORK},
254+ {"help", no_argument, NULL, OPT_HELP},
255+ {"interval", required_argument, NULL, OPT_INTERVAL},
256+ {"json", required_argument, NULL, OPT_JSON},
257+ {"loops", required_argument, NULL, OPT_LOOPS},
258+ {"priority", required_argument, NULL, OPT_PRIORITY},
259+ {"quiet", no_argument, NULL, OPT_QUIET},
260+ {"threads", optional_argument, NULL, OPT_THREADS},
261+ {NULL, 0, NULL, 0}
262+ };
263+ int c = getopt_long (argc, argv, "a::b:d:D:f::hi:l:p:qt::",
264+ long_options, &option_index);
265+ if (c == -1)
266+ break;
267+ switch (c) {
268+ case OPT_AFFINITY:
269+ case 'a':
270+ if (optarg != NULL) {
271+ affinity = atoi(optarg);
272+ setaffinity = AFFINITY_SPECIFIED;
273+ } else if (optind < argc && atoi(argv[optind])) {
274+ affinity = atoi(argv[optind]);
275+ setaffinity = AFFINITY_SPECIFIED;
276+ } else {
277+ setaffinity = AFFINITY_USEALL;
278+ }
279+ break;
280+ case OPT_BREAKTRACE:
281+ case 'b':
282+ thistracelimit = atoi(optarg);
283+ break;
284+ case OPT_DISTANCE:
285+ case 'd':
286+ distance = atoi(optarg);
287+ break;
288+ case OPT_DURATION:
289+ case 'D':
290+ duration = parse_time_string(optarg);
291+ break;
292+ case OPT_FORK:
293+ case 'f':
294+ if (optarg != NULL) {
295+ wasforked = 1;
296+ if (optarg[0] == 's')
297+ wasforked_sender = 1;
298+ else if (optarg[0] == 'r')
299+ wasforked_sender = 0;
300+ wasforked_threadno = atoi(optarg+1);
301+ } else
302+ mustfork = 1;
303+ break;
304+ case OPT_HELP:
305+ case '?':
306+ case 'h':
307+ display_help(0);
308+ break;
309+ case OPT_INTERVAL:
310+ case 'i':
311+ interval = atoi(optarg);
312+ break;
313+ case OPT_JSON:
314+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
315+ break;
316+ case OPT_LOOPS:
317+ case 'l':
318+ max_cycles = atoi(optarg);
319+ break;
320+ case OPT_PRIORITY:
321+ case 'p':
322+ priority = atoi(optarg);
323+ break;
324+ case OPT_QUIET:
325+ case 'q':
326+ quiet = 1;
327+ break;
328+ case OPT_THREADS:
329+ case 't':
330+ if (optarg != NULL)
331+ num_threads = atoi(optarg);
332+ else if (optind < argc && atoi(argv[optind]))
333+ num_threads = atoi(argv[optind]);
334+ else
335+ num_threads = max_cpus;
336+ break;
337+ default:
338+ display_help(1);
339+ break;
340+ }
341+ }
342+ 
343+ if (!wasforked) {
344+ if (setaffinity == AFFINITY_SPECIFIED) {
345+ if (affinity < 0)
346+ error = 1;
347+ if (affinity >= max_cpus) {
348+ fprintf(stderr, "ERROR: CPU #%d not found, "
349+ "only %d CPUs available\n",
350+ affinity, max_cpus);
351+ error = 1;
352+ }
353+ }
354+ 
355+ if (duration < 0)
356+ error = 1;
357+ 
358+ if (num_threads < 1)
359+ error = 1;
360+ 
361+ if (priority < 0 || priority > 99)
362+ error = 1;
363+ 
364+ tracelimit = thistracelimit;
365+ }
366+ if (error)
367+ display_help(error);
368+}
369+ 
370+ 
371+static int volatile mustshutdown;
372+ 
373+static void sighand(int sig __attribute__ ((unused)))
374+{
375+ mustshutdown = 1;
376+}
377+ 
378+static void print_stat(struct params *receiver, struct params *sender,
379+ int verbose __attribute__ ((unused)), int quiet)
380+{
381+ int i;
382+ 
383+ if (quiet)
384+ return;
385+ 
386+ for (i = 0; i < num_threads; i++) {
387+ int receiver_pid, sender_pid;
388+ if (mustfork) {
389+ receiver_pid = receiver[i].pid;
390+ sender_pid = sender[i].pid;
391+ } else {
392+ receiver_pid = receiver[i].tid;
393+ sender_pid = sender[i].tid;
394+ }
395+ printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: ID%d, P%d, CPU%d, Cycles %d\n",
396+ i*2, receiver_pid, receiver[i].priority,
397+ receiver[i].cpu, receiver[i].delay.tv_nsec /
398+ 1000, i*2+1, sender_pid, sender[i].priority,
399+ sender[i].cpu, sender[i].samples);
400+ }
401+ 
402+ for (i = 0; i < num_threads; i++) {
403+ if (receiver[i].mindiff == -1)
404+ printf("#%d -> #%d (not yet ready)\n", i*2+1, i*2);
405+ else
406+ printf("#%d -> #%d, Min %4d, Cur %4d, Avg %4d, Max %4d\n",
407+ i*2+1, i*2, receiver[i].mindiff,
408+ (int) receiver[i].diff.tv_usec,
409+ (int) ((receiver[i].sumdiff /
410+ receiver[i].samples) + 0.5),
411+ receiver[i].maxdiff);
412+ }
413+}
414+ 
415+struct params_stats {
416+ struct params *receiver;
417+ struct params *sender;
418+};
419+ 
420+static void write_stats(FILE *f, void *data)
421+{
422+ struct params_stats *ps = data;
423+ struct params *s, *r;
424+ int i;
425+ 
426+ fprintf(f, " \"num_threads\": %d,\n", num_threads);
427+ fprintf(f, " \"thread\": {\n");
428+ for (i = 0; i < num_threads; i++) {
429+ s = &ps->sender[i];
430+ r = &ps->receiver[i];
431+ fprintf(f, " \"%u\": {\n", i);
432+ fprintf(f, " \"sender\": {\n");
433+ fprintf(f, " \"cpu\": %d,\n", s->cpu);
434+ fprintf(f, " \"priority\": %d,\n", s->priority);
435+ fprintf(f, " \"samples\": %d,\n", s->samples);
436+ fprintf(f, " \"interval\": %ld\n", r->delay.tv_nsec/1000);
437+ fprintf(f, " },\n");
438+ fprintf(f, " \"receiver\": {\n");
439+ fprintf(f, " \"cpu\": %d,\n", r->cpu);
440+ fprintf(f, " \"priority\": %d,\n", r->priority);
441+ fprintf(f, " \"min\": %d,\n", r->mindiff);
442+ fprintf(f, " \"avg\": %.2f,\n", r->sumdiff/r->samples);
443+ fprintf(f, " \"max\": %d\n", r->maxdiff);
444+ fprintf(f, " }\n");
445+ fprintf(f, " }%s\n", i == num_threads - 1 ? "" : ",");
446+ }
447+ fprintf(f, " }\n");
448+}
449+ 
450+int main(int argc, char *argv[])
451+{
452+ int i, totalsize = 0;
453+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
454+ int oldsamples = 1;
455+ struct params *receiver = NULL;
456+ struct params *sender = NULL;
457+ sigset_t sigset;
458+ void *param = NULL;
459+ char f_opt[14];
460+ struct timespec launchdelay, maindelay;
461+ 
462+ rt_init(argc, argv);
463+ process_options(argc, argv);
464+ 
465+ if (check_privs())
466+ return 1;
467+ 
468+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
469+ perror("mlockall");
470+ return 1;
471+ }
472+ 
473+ get_cpu_setup(); /* init get_cpu() */
474+ 
475+ if (mustfork) {
476+ int shmem;
477+ 
478+ /*
479+ * In fork mode (-f), the shared memory contains two
480+ * subsequent arrays, receiver[num_threads] and
481+ * sender[num_threads].
482+ */
483+ totalsize = num_threads * sizeof(struct params) * 2;
484+ 
485+ shm_unlink("/sigwaittest");
486+ shmem = shm_open("/sigwaittest", O_CREAT|O_EXCL|O_RDWR,
487+ S_IRUSR|S_IWUSR);
488+ if (shmem < 0) {
489+ fprintf(stderr, "Could not create shared memory\n");
490+ return 1;
491+ }
492+ ftruncate(shmem, totalsize);
493+ param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED,
494+ shmem, 0);
495+ if (param == MAP_FAILED) {
496+ fprintf(stderr, "Could not map shared memory\n");
497+ close(shmem);
498+ return 1;
499+ }
500+ 
501+ receiver = (struct params *) param;
502+ sender = receiver + num_threads;
503+ } else if (wasforked) {
504+ struct stat buf;
505+ int shmem, totalsize, expect_totalsize;
506+ 
507+ if (wasforked_threadno == -1 || wasforked_sender == -1) {
508+ fprintf(stderr, "Invalid fork option\n");
509+ return 1;
510+ }
511+ shmem = shm_open("/sigwaittest", O_RDWR, S_IRUSR|S_IWUSR);
512+ if (fstat(shmem, &buf)) {
513+ fprintf(stderr,
514+ "Could not determine shared memory size\n");
515+ close(shmem);
516+ return 1;
517+ }
518+ totalsize = buf.st_size;
519+ param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED,
520+ shmem, 0);
521+ close(shmem);
522+ if (param == MAP_FAILED) {
523+ fprintf(stderr, "Could not map shared memory\n");
524+ return 1;
525+ }
526+ 
527+ receiver = (struct params *) param;
528+ expect_totalsize = receiver->num_threads *
529+ sizeof(struct params) * 2;
530+ if (totalsize != expect_totalsize) {
531+ fprintf(stderr, "Memory size problem (expected %d, "
532+ "found %d\n", expect_totalsize, totalsize);
533+ munmap(param, totalsize);
534+ return 1;
535+ }
536+ sender = receiver + receiver->num_threads;
537+ if (wasforked_sender)
538+ semathread(sender + wasforked_threadno);
539+ else
540+ semathread(receiver + wasforked_threadno);
541+ munmap(param, totalsize);
542+ return 0;
543+ }
544+ 
545+ signal(SIGINT, sighand);
546+ signal(SIGTERM, sighand);
547+ signal(SIGALRM, sighand);
548+ sigemptyset(&sigset);
549+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
550+ 
551+ if (duration)
552+ alarm(duration);
553+ 
554+ if (!mustfork && !wasforked) {
555+ receiver = calloc(num_threads, sizeof(struct params));
556+ sender = calloc(num_threads, sizeof(struct params));
557+ if (receiver == NULL || sender == NULL)
558+ goto nomem;
559+ }
560+ 
561+ launchdelay.tv_sec = 0;
562+ launchdelay.tv_nsec = 10000000; /* 10 ms */
563+ 
564+ maindelay.tv_sec = 0;
565+ maindelay.tv_nsec = 50000000; /* 50 ms */
566+ 
567+ for (i = 0; i < num_threads; i++) {
568+ receiver[i].mindiff = UINT_MAX;
569+ receiver[i].maxdiff = 0;
570+ receiver[i].sumdiff = 0.0;
571+ 
572+ receiver[i].num = i;
573+ receiver[i].cpu = i;
574+ receiver[i].priority = priority;
575+ receiver[i].tracelimit = tracelimit;
576+ if (priority > 0)
577+ priority--;
578+ switch (setaffinity) {
579+ case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break;
580+ case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break;
581+ case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break;
582+ }
583+ receiver[i].delay.tv_sec = interval / USEC_PER_SEC;
584+ receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000;
585+ interval += distance;
586+ receiver[i].max_cycles = max_cycles;
587+ receiver[i].sender = 0;
588+ receiver[i].neighbor = &sender[i];
589+ if (mustfork) {
590+ pid_t pid = fork();
591+ if (pid == -1) {
592+ fprintf(stderr, "Could not fork\n");
593+ return 1;
594+ } else if (pid == 0) {
595+ char *args[3];
596+ 
597+ receiver[i].num_threads = num_threads;
598+ receiver[i].pid = getpid();
599+ sprintf(f_opt, "-fr%d", i);
600+ args[0] = argv[0];
601+ args[1] = f_opt;
602+ args[2] = NULL;
603+ execvp(args[0], args);
604+ fprintf(stderr,
605+ "Could not execute receiver child process "
606+ "#%d\n", i);
607+ }
608+ } else
609+ pthread_create(&receiver[i].threadid, NULL,
610+ semathread, &receiver[i]);
611+ 
612+ nanosleep(&launchdelay, NULL);
613+ 
614+ memcpy(&sender[i], &receiver[i], sizeof(receiver[0]));
615+ sender[i].sender = 1;
616+ sender[i].neighbor = &receiver[i];
617+ if (mustfork) {
618+ pid_t pid = fork();
619+ if (pid == -1) {
620+ fprintf(stderr, "Could not fork\n");
621+ return 1;
622+ } else if (pid == 0) {
623+ char *args[3];
624+ 
625+ sender[i].num_threads = num_threads;
626+ sender[i].pid = getpid();
627+ sprintf(f_opt, "-fs%d", i);
628+ args[0] = argv[0];
629+ args[1] = f_opt;
630+ args[2] = NULL;
631+ execvp(args[0], args);
632+ fprintf(stderr,
633+ "Could not execute sender child process "
634+ "#%d\n", i);
635+ }
636+ } else
637+ pthread_create(&sender[i].threadid, NULL, semathread,
638+ &sender[i]);
639+ }
640+ 
641+ while (!mustshutdown) {
642+ for (i = 0; i < num_threads; i++)
643+ mustshutdown |= receiver[i].shutdown |
644+ sender[i].shutdown;
645+ 
646+ if (receiver[0].samples > oldsamples || mustshutdown) {
647+ print_stat(receiver, sender, 0, quiet);
648+ if (!quiet)
649+ printf("\033[%dA", num_threads*2);
650+ }
651+ 
652+ sigemptyset(&sigset);
653+ sigaddset(&sigset, SIGTERM);
654+ sigaddset(&sigset, SIGINT);
655+ sigaddset(&sigset, SIGALRM);
656+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
657+ 
658+ nanosleep(&maindelay, NULL);
659+ 
660+ sigemptyset(&sigset);
661+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
662+ }
663+ 
664+ if (!quiet)
665+ printf("\033[%dB", num_threads*2 + 2);
666+ else
667+ print_stat(receiver, sender, 0, 0);
668+ 
669+ for (i = 0; i < num_threads; i++) {
670+ receiver[i].shutdown = 1;
671+ sender[i].shutdown = 1;
672+ }
673+ nanosleep(&receiver[0].delay, NULL);
674+ 
675+ for (i = 0; i < num_threads; i++) {
676+ if (!receiver[i].stopped) {
677+ if (mustfork)
678+ kill(receiver[i].pid, SIGTERM);
679+ else
680+ pthread_kill(receiver[i].threadid, SIGTERM);
681+ }
682+ if (!sender[i].stopped) {
683+ if (mustfork)
684+ kill(sender[i].pid, SIGTERM);
685+ else
686+ pthread_kill(sender[i].threadid, SIGTERM);
687+ }
688+ }
689+ 
690+ if (strlen(jsonfile) != 0) {
691+ struct params_stats ps = {
692+ .receiver = receiver,
693+ .sender = sender,
694+ };
695+ rt_write_json(jsonfile, 0, write_stats, &ps);
696+ }
697+ 
698+nomem:
699+ if (mustfork) {
700+ munmap(param, totalsize);
701+ shm_unlink("/sigwaittest");
702+ }
703+ 
704+ return 0;
705+}
@@ -0,0 +1,38 @@
1+.TH SSDD 8 "September 19, 2020"
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+.SH NAME
4+ssdd \- have a tracer do a bunch of PTRACE_SINGLESTEPs
5+.SH SYNOPSIS
6+.LP
7+ssdd [-f|--forks NUM] [-h|--help] [-i|--iters NUM] [--json FILENAME]
8+.SH DESCRIPTION
9+Have a tracer do a bunch of PTRACE_SINGLESTEPs against a tracee as
10+fast as possible. Create several of these tracer/tracee pairs and
11+see if they can be made to interfere with each other. The tracer
12+waits on each PTRACE_SINGLESTEP with a waitpid(2) and checks that
13+waitpid's return values for correctness.
14+.SH OPTIONS
15+.TP
16+.B \-f, \-\-forks=NUM
17+number of tracer/tracee pairs to fork off.
18+.br
19+Default is 10.
20+.TP
21+.B \-h, \-\-help
22+Display usage
23+.TP
24+.B \-i, \-\-iters=NUM
25+number of PTRACE_SINGLESTEP iterations to do before declaring
26+success, for each tracer tracee pair set up.
27+.br
28+Default is 10,000.
29+.br
30+NUM must be at least 1
31+.TP
32+.B \-\-json=FILENAME
33+Write final results into FILENAME, JSON formatted.
34+.SH AUTHOR
35+ssdd was written by Joe Korty <joe.korty@concurrent-rt.com>
36+.PP
37+This manual page was written by John Kacur <jkacur@redhat.com>
38+ 
@@ -0,0 +1,381 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+ 
3+/*
4+ * Copyright (C) 2019 Joe Korty <joe.korty@concurrent-rt.com>
5+ */
6+ 
7+/*
8+ * Have a tracer do a bunch of PTRACE_SINGLESTEPs against
9+ * a tracee as fast as possible. Create several of these
10+ * tracer/tracee pairs and see if they can be made to
11+ * interfere with each other.
12+ *
13+ * Usage:
14+ * ssdd nforks niters
15+ * Where:
16+ * nforks - number of tracer/tracee pairs to fork off.
17+ * default 10.
18+ * niters - number of PTRACE_SINGLESTEP iterations to
19+ * do before declaring success, for each tracer/
20+ * tracee pair set up. Default 10,000.
21+ *
22+ * The tracer waits on each PTRACE_SINGLESTEP with a waitpid(2)
23+ * and checks that waitpid's return values for correctness.
24+ *
25+ */
26+#include <stdio.h>
27+#include <stdlib.h>
28+#include <stddef.h>
29+#include <unistd.h>
30+#include <getopt.h>
31+#include <string.h>
32+#include <signal.h>
33+#include <errno.h>
34+ 
35+#include <sys/types.h>
36+#include <sys/wait.h>
37+#include <sys/ptrace.h>
38+ 
39+#include "rt-utils.h"
40+ 
41+/* do_wait return values */
42+#define STATE_EXITED 1
43+#define STATE_STOPPED 2
44+#define STATE_SIGNALED 3
45+#define STATE_UNKNOWN 4
46+#define STATE_ECHILD 5
47+#define STATE_EXITED_TSIG 6 /* exited with termination signal */
48+#define STATE_EXITED_ERRSTAT 7 /* exited with non-zero status */
49+ 
50+static char *state_name[] = {
51+ [STATE_EXITED] = "STATE_EXITED",
52+ [STATE_STOPPED] = "STATE_STOPPED",
53+ [STATE_SIGNALED] = "STATE_SIGNALED",
54+ [STATE_UNKNOWN] = "STATE_UNKNOWN",
55+ [STATE_ECHILD] = "STATE_ECHILD",
56+ [STATE_EXITED_TSIG] = "STATE_EXITED_TSIG",
57+ [STATE_EXITED_ERRSTAT] = "STATE_EXITED_ERRSTAT"
58+};
59+ 
60+static const char *get_state_name(int state)
61+{
62+ if (state < STATE_EXITED || state > STATE_EXITED_ERRSTAT)
63+ return "?";
64+ return state_name[state];
65+}
66+ 
67+static int quiet;
68+static char jsonfile[MAX_PATH];
69+ 
70+static int got_sigchld;
71+ 
72+enum option_value { OPT_NFORKS=1, OPT_NITERS, OPT_HELP, OPT_JSON, OPT_QUIET };
73+ 
74+static void usage(int error)
75+{
76+ printf("ssdd V %1.2f\n", VERSION);
77+ printf("Usage:\n"
78+ "ssdd <options>\n\n"
79+ "-f --forks=NUM number of forks\n"
80+ "-h --help print this message\n"
81+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
82+ "-q --quiet suppress running output\n"
83+ "-i --iters=NUM number of iterations, NUM must be at least 1\n"
84+ );
85+ exit(error);
86+}
87+ 
88+static int do_wait(pid_t *wait_pid, int *ret_sig)
89+{
90+ int status, child_status;
91+ 
92+ *ret_sig = -1; /* initially mark 'nothing returned' */
93+ 
94+ while (1) {
95+ status = waitpid(-1, &child_status, WUNTRACED | __WALL);
96+ if (status == -1) {
97+ if (errno == EINTR)
98+ continue;
99+ if (errno == ECHILD) {
100+ *wait_pid = (pid_t)0;
101+ return STATE_ECHILD;
102+ }
103+ printf("do_wait/%d: EXITING, ERROR: "
104+ "waitpid() returned errno %d\n",
105+ getpid(), errno);
106+ exit(1);
107+ }
108+ break;
109+ }
110+ *wait_pid = (pid_t)status;
111+ 
112+ if (WIFEXITED(child_status)) {
113+ if (WIFSIGNALED(child_status))
114+ return STATE_EXITED_TSIG;
115+ if (WEXITSTATUS(child_status))
116+ return STATE_EXITED_ERRSTAT;
117+ return STATE_EXITED;
118+ }
119+ if (WIFSTOPPED(child_status)) {
120+ *ret_sig = WSTOPSIG(child_status);
121+ return STATE_STOPPED;
122+ }
123+ if (WIFSIGNALED(child_status)) {
124+ *ret_sig = WTERMSIG(child_status);
125+ return STATE_SIGNALED;
126+ }
127+ return STATE_UNKNOWN;
128+}
129+ 
130+static int check_sigchld(void)
131+{
132+ int i;
133+ /*
134+ * The signal is asynchronous so give it some
135+ * time to arrive.
136+ */
137+ for (i = 0; i < 10 && !got_sigchld; i++)
138+ usleep(1000); /* 10 msecs */
139+ for (i = 0; i < 10 && !got_sigchld; i++)
140+ usleep(2000); /* 20 + 10 = 30 msecs */
141+ for (i = 0; i < 10 && !got_sigchld; i++)
142+ usleep(4000); /* 40 + 30 = 70 msecs */
143+ for (i = 0; i < 10 && !got_sigchld; i++)
144+ usleep(8000); /* 80 + 70 = 150 msecs */
145+ for (i = 0; i < 10 && !got_sigchld; i++)
146+ usleep(16000); /* 160 + 150 = 310 msecs */
147+ 
148+ return got_sigchld;
149+}
150+ 
151+static pid_t parent;
152+static int nforks = 10;
153+static int nsteps = 10000;
154+ 
155+static void sigchld(int sig __attribute__ ((unused)), siginfo_t *info __attribute__ ((unused)), void *arg __attribute__ ((unused)))
156+{
157+ got_sigchld = 1;
158+}
159+ 
160+static void child_process(void)
161+{
162+ while (1)
163+ ;
164+}
165+ 
166+static int forktests(int testid)
167+{
168+ int i, status, ret_sig;
169+ long pstatus;
170+ pid_t child, wait_pid;
171+ struct sigaction act, oact;
172+ 
173+ parent = getpid();
174+ 
175+ child = fork();
176+ if (child == -1) {
177+ printf("forktest#%d/%d: EXITING, ERROR: "
178+ "fork returned errno %d\n", testid, parent, errno);
179+ exit(1);
180+ }
181+ if (!child)
182+ child_process();
183+ 
184+ if (!quiet)
185+ printf("forktest#%d/%d/%d: STARTING\n", testid, parent, child);
186+ 
187+ act.sa_sigaction = sigchld;
188+ sigemptyset(&act.sa_mask);
189+ act.sa_flags = SA_SIGINFO;
190+ status = sigaction(SIGCHLD, &act, &oact);
191+ if (status) {
192+ printf("forktest#%d/%d: EXITING, ERROR: "
193+ "sigaction returned %d, errno %d\n",
194+ testid, parent, status, errno);
195+ exit(1);
196+ }
197+ 
198+ /*
199+ * Attach to the child.
200+ */
201+ pstatus = ptrace(PTRACE_ATTACH, child, NULL, NULL);
202+ if (pstatus == ~0l) {
203+ printf("forktest#%d/%d: EXITING, ERROR: "
204+ "attach failed. errno %d\n",
205+ testid, getpid(), errno);
206+ exit(1);
207+ }
208+ 
209+ /*
210+ * The attach should cause the child to receive a signal.
211+ */
212+ status = do_wait(&wait_pid, &ret_sig);
213+ if (wait_pid != child) {
214+ printf("forktest#%d/%d: EXITING, ERROR: "
215+ "attach: Unexpected wait pid %d\n",
216+ testid, getpid(), wait_pid);
217+ exit(1);
218+ }
219+ if (status != STATE_STOPPED) {
220+ printf("forktest#%d/%d: EXITING, ERROR: "
221+ "attach: wait on PTRACE_ATTACH returned %d "
222+ "[%s, wanted STATE_STOPPED], signo %d\n",
223+ testid, getpid(), status, get_state_name(status),
224+ ret_sig);
225+ exit(1);
226+ }
227+ if (!check_sigchld()) {
228+ printf("forktest#%d/%d: EXITING, ERROR: "
229+ "wait on PTRACE_ATTACH saw a SIGCHLD count of %d, should be 1\n",
230+ testid, getpid(), got_sigchld);
231+ exit(1);
232+ }
233+ got_sigchld = 0;
234+ 
235+ 
236+ /*
237+ * Generate 'nsteps' PTRACE_SINGLESTEPs, make sure they all actually
238+ * step the tracee.
239+ */
240+ for (i = 0; i < nsteps; i++) {
241+ pstatus = ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
242+ 
243+ if (pstatus) {
244+ printf("forktest#%d/%d: EXITING, ERROR: "
245+ "PTRACE_SINGLESTEP #%d: returned status %ld, "
246+ "errno %d, signo %d\n",
247+ testid, getpid(), i, pstatus, errno, ret_sig);
248+ exit(1);
249+ }
250+ 
251+ status = do_wait(&wait_pid, &ret_sig);
252+ if (wait_pid != child) {
253+ printf("forktest#%d/%d: EXITING, ERROR: "
254+ "wait on PTRACE_SINGLESTEP #%d: returned wrong pid %d, "
255+ "expected %d\n",
256+ testid, getpid(), i, wait_pid, child);
257+ exit(1);
258+ }
259+ if (status != STATE_STOPPED) {
260+ printf("forktest#%d/%d: EXITING, ERROR: "
261+ "wait on PTRACE_SINGLESTEP #%d: wanted STATE_STOPPED, "
262+ "saw %s instead (and saw signo %d too)\n",
263+ testid, getpid(), i,
264+ get_state_name(status), ret_sig);
265+ exit(1);
266+ }
267+ if (ret_sig != SIGTRAP) {
268+ printf("forktest#%d/%d: EXITING, ERROR: "
269+ "wait on PTRACE_SINGLESTEP #%d: returned signal %d, "
270+ "wanted SIGTRAP\n",
271+ testid, getpid(), i, ret_sig);
272+ exit(1);
273+ }
274+ if (!check_sigchld()) {
275+ printf("forktest#%d/%d: EXITING, ERROR: "
276+ "wait on PTRACE_SINGLESTEP #%d: no SIGCHLD seen "
277+ "(signal count == 0), signo %d\n",
278+ testid, getpid(), i, ret_sig);
279+ exit(1);
280+ }
281+ got_sigchld = 0;
282+ }
283+ 
284+ /* There is no need for the tracer to kill the tracee. It will
285+ * automatically exit when its owner, ie, us, exits.
286+ */
287+ if (!quiet)
288+ printf("forktest#%d/%d: EXITING, no error\n", testid, parent);
289+ exit(0);
290+}
291+ 
292+int main(int argc, char **argv)
293+{
294+ int i, ret_sig, status;
295+ pid_t child = 0, wait_pid;
296+ int error = 0;
297+ 
298+ setbuf(stdout, NULL);
299+ 
300+ rt_init(argc, argv);
301+ for (;;) {
302+ int option_index = 0;
303+ 
304+ static struct option long_options[] = {
305+ {"forks", required_argument, NULL, OPT_NFORKS},
306+ {"help", no_argument, NULL, OPT_HELP},
307+ {"json", required_argument, NULL, OPT_JSON},
308+ {"quiet", no_argument, NULL, OPT_QUIET},
309+ {"iters", required_argument, NULL, OPT_NITERS},
310+ {NULL, 0, NULL, 0},
311+ };
312+ int c = getopt_long(argc, argv, "f:hqi:", long_options, &option_index);
313+ if (c == -1)
314+ break;
315+ switch(c) {
316+ case 'f':
317+ case OPT_NFORKS:
318+ nforks = atoi(optarg);
319+ break;
320+ case 'h':
321+ case OPT_HELP:
322+ usage(0);
323+ break;
324+ case OPT_JSON:
325+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
326+ break;
327+ case OPT_QUIET:
328+ case 'q':
329+ quiet = 1;
330+ break;
331+ case 'i':
332+ case OPT_NITERS:
333+ nsteps = atoi(optarg);
334+ if (nsteps < 1) {
335+ usage(0);
336+ }
337+ break;
338+ default:
339+ usage(1);
340+ break;
341+ }
342+ }
343+ 
344+ if (!quiet) {
345+ printf("#main : %d\n", getpid());
346+ printf("#forks: %d\n", nforks);
347+ printf("#steps: %d\n", nsteps);
348+ printf("\n");
349+ }
350+ 
351+ for (i = 0; i < nforks; i++) {
352+ child = fork();
353+ if (child == -1) {
354+ printf("main: fork returned errno %d\n", errno);
355+ exit(1);
356+ }
357+ if (!child)
358+ forktests(i);
359+ }
360+ 
361+ for (i = 0; i < nforks; i++) {
362+ status = do_wait(&wait_pid, &ret_sig);
363+ if (status != STATE_EXITED) {
364+ if (0) printf("main/%d: ERROR: "
365+ "forktest#%d unexpected do_wait status %d "
366+ "[%s, wanted STATE_EXITED]\n",
367+ getpid(), wait_pid, status,
368+ get_state_name(status));
369+ error = 1;
370+ }
371+ }
372+ 
373+ printf("%s.\n", error ?
374+ "One or more tests FAILED" :
375+ "All tests PASSED");
376+ 
377+ if (strlen(jsonfile) != 0)
378+ rt_write_json(jsonfile, error, NULL, NULL);
379+ 
380+ exit(error);
381+}
@@ -0,0 +1,72 @@
1+.TH "svsematest" "8" "0.1" "" ""
2+# SPDX-License-Identifier: GPL-2.0-or-later
3+.SH "NAME"
4+.LP
5+\fBsvsematest\fR \- Start two threads or fork two processes and measure the latency of SYSV semaphores
6+.SH "SYNTAX"
7+.LP
8+svsematest [-a|--affinity NUM] [-b|--breaktrace USEC] [-d|--distance DIST] [-D|--duration TIME] [-f|--fork [OPT]] [-i|--interval INTV] [--json FILENAME] [-l|--loops LOOPS] [-p|--prio PRIO] [-q|--quiet] [-S|--smp] [-t|--threads [NUM]]
9+.br
10+.SH "DESCRIPTION"
11+.LP
12+The program \fBsvsematest\fR starts two threads or, optionally, forks two processes that are synchronized via SYSV semaphores and measures the latency between releasing a semaphore on one side and getting it on the other side.
13+.SH "OPTIONS"
14+.TP
15+.B \-a, \-\-affinity[=NUM]
16+Run on processor number NUM. If PROC is not specified, run on current processor.
17+.TP
18+.B \-b, \-\-breaktrace=USEC
19+Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch.
20+It is useful to track down unexpected large latencies of a system.
21+.TP
22+.B \-d, \-\-distance=DIST
23+Set the distance of thread intervals in microseconds (default is 500 us). When cyclictest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST
24+.TP
25+.B \-D, \-\-duration=TIME
26+Specify a length for the test run.
27+.br
28+Append 'm', 'h', or 'd' to specify minutes, hours or days.
29+.TP
30+.B \-f, \-\-fork
31+Instead of creating threads (which is the default), fork new processes
32+.TP
33+.B \-i, \-\-interval=INTV
34+Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d.
35+.TP
36+.B \-\-json=FILENAME
37+Write final results into FILENAME, JSON formatted.
38+.TP
39+.B \-l, \-\-loops=LOOPS
40+Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. svsematest is stopped once the number of timer intervals has been reached.
41+.TP
42+.B \-p, \-\-prio=PRIO
43+Set the priority of the process.
44+.TP
45+.B \-q, \-\-quiet
46+Print a summary only on exit. Useful for automated tests, where only the summary output needs to be captured.
47+.TP
48+.B \-S, \-\-smp
49+SMP testing: options -a -t and same priority of all threads
50+.TP
51+.B \-t, \-\-threads[=NUM]
52+Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specified, NUM is set to the number of available CPUs.
53+.SH "EXAMPLES"
54+The following example was running on a 4-way CPU:
55+.LP
56+.nf
57+# svsematest -a -t -p99 -i100 -d25 -l1000000
58+#0: ID13110, P99, CPU0, I100; #1: ID13111, P99, CPU0, Cycles 1000000
59+#2: ID13112, P98, CPU1, I125; #3: ID13113, P98, CPU1, Cycles 813573
60+#4: ID13114, P97, CPU2, I150; #5: ID13115, P97, CPU2, Cycles 667285
61+#6: ID13116, P96, CPU3, I175; #7: ID13117, P96, CPU3, Cycles 591403
62+#1 -> #0, Min 1, Cur 2, Avg 2, Max 12
63+#3 -> #2, Min 1, Cur 3, Avg 2, Max 12
64+#5 -> #4, Min 1, Cur 3, Avg 3, Max 12
65+#7 -> #6, Min 1, Cur 2, Avg 3, Max 11
66+.fi
67+.SH "AUTHORS"
68+.LP
69+Carsten Emde <C.Emde@osadl.org>
70+.SH "SEE ALSO"
71+semop(2)
72+.LP
@@ -0,0 +1,794 @@
1+// SPDX-License-Identifier: GPL-2.0-or-later
2+ 
3+/*
4+ * svsematest.c
5+ *
6+ * Copyright (C) 2009 Carsten Emde <C.Emde@osadl.org>
7+ *
8+ */
9+ 
10+#include <stdio.h>
11+#include <fcntl.h>
12+#include <stdlib.h>
13+#include <unistd.h>
14+#include <limits.h>
15+#include <fcntl.h>
16+#include <getopt.h>
17+#include <pthread.h>
18+#include <signal.h>
19+#include <sched.h>
20+#include <string.h>
21+#include <time.h>
22+#include <utmpx.h>
23+#include <inttypes.h>
24+ 
25+#include <linux/unistd.h>
26+ 
27+#include <sys/types.h>
28+#include <sys/stat.h>
29+#include <sys/sem.h>
30+#include <sys/time.h>
31+#include <sys/mman.h>
32+ 
33+#include "rt-utils.h"
34+#include "rt-get_cpu.h"
35+#include "rt-error.h"
36+ 
37+#define SEM_WAIT_FOR_RECEIVER 0
38+#define SEM_WAIT_FOR_SENDER 1
39+ 
40+#define SEM_LOCK -1
41+#define SEM_UNLOCK 1
42+ 
43+enum {
44+ AFFINITY_UNSPECIFIED,
45+ AFFINITY_SPECIFIED,
46+ AFFINITY_USEALL
47+};
48+ 
49+struct params {
50+ int num;
51+ int num_threads;
52+ int cpu;
53+ int priority;
54+ int affinity;
55+ int semid;
56+ int sender;
57+ int samples;
58+ int max_cycles;
59+ int tracelimit;
60+ int tid;
61+ pid_t pid;
62+ int shutdown;
63+ int stopped;
64+ struct timespec delay;
65+ unsigned int mindiff, maxdiff;
66+ double sumdiff;
67+ struct timeval unblocked, received, diff;
68+ pthread_t threadid;
69+ struct params *neighbor;
70+};
71+ 
72+static int mustfork;
73+static int wasforked;
74+static int wasforked_sender = -1;
75+static int wasforked_threadno = -1;
76+static int tracelimit;
77+ 
78+void *semathread(void *param)
79+{
80+ int mustgetcpu = 0;
81+ struct params *par = param;
82+ cpu_set_t mask;
83+ int policy = SCHED_FIFO;
84+ struct sched_param schedp;
85+ struct sembuf sb = { 0, 0, 0};
86+ sigset_t sigset;
87+ 
88+ sigemptyset(&sigset);
89+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
90+ 
91+ memset(&schedp, 0, sizeof(schedp));
92+ schedp.sched_priority = par->priority;
93+ sched_setscheduler(0, policy, &schedp);
94+ 
95+ if (par->cpu != -1) {
96+ CPU_ZERO(&mask);
97+ CPU_SET(par->cpu, &mask);
98+ if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
99+ fatal("Could not set CPU affinity "
100+ "to CPU #%d\n", par->cpu);
101+ } else {
102+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
103+ 
104+ if (max_cpus > 1)
105+ mustgetcpu = 1;
106+ else
107+ par->cpu = 0;
108+ }
109+ 
110+ if (!wasforked)
111+ par->tid = gettid();
112+ 
113+ while (!par->shutdown) {
114+ if (par->sender) {
115+ sb.sem_num = SEM_WAIT_FOR_SENDER;
116+ sb.sem_op = SEM_UNLOCK;
117+ /*
118+ * Unlocking the semaphore:
119+ * Start of latency measurement ...
120+ */
121+ gettimeofday(&par->unblocked, NULL);
122+ semop(par->semid, &sb, 1);
123+ par->samples++;
124+ if (par->max_cycles && par->samples >= par->max_cycles)
125+ par->shutdown = 1;
126+ 
127+ if (mustgetcpu)
128+ par->cpu = get_cpu();
129+ 
130+ sb.sem_num = SEM_WAIT_FOR_RECEIVER;
131+ sb.sem_op = SEM_LOCK;
132+ semop(par->semid, &sb, 1);
133+ 
134+ sb.sem_num = SEM_WAIT_FOR_SENDER;
135+ sb.sem_op = SEM_LOCK;
136+ semop(par->semid, &sb, 1);
137+ } else {
138+ /* Receiver */
139+ struct params *neighbor;
140+ 
141+ if (wasforked)
142+ neighbor = par + par->num_threads;
143+ else
144+ neighbor = par->neighbor;
145+ 
146+ sb.sem_num = SEM_WAIT_FOR_SENDER;
147+ sb.sem_op = SEM_LOCK;
148+ semop(par->semid, &sb, 1);
149+ 
150+ /*
151+ * ... We got the lock:
152+ * End of latency measurement
153+ */
154+ gettimeofday(&par->received, NULL);
155+ par->samples++;
156+ if (par->max_cycles && par->samples >= par->max_cycles)
157+ par->shutdown = 1;
158+ 
159+ if (mustgetcpu)
160+ par->cpu = get_cpu();
161+ 
162+ timersub(&par->received, &neighbor->unblocked,
163+ &par->diff);
164+ 
165+ if (par->diff.tv_usec < par->mindiff)
166+ par->mindiff = par->diff.tv_usec;
167+ if (par->diff.tv_usec > par->maxdiff)
168+ par->maxdiff = par->diff.tv_usec;
169+ par->sumdiff += (double) par->diff.tv_usec;
170+ if (par->tracelimit && par->maxdiff > par->tracelimit) {
171+ char tracing_enabled_file[MAX_PATH];
172+ 
173+ strcpy(tracing_enabled_file, get_debugfileprefix());
174+ strcat(tracing_enabled_file, "tracing_on");
175+ int tracing_enabled =
176+ open(tracing_enabled_file, O_WRONLY);
177+ if (tracing_enabled >= 0) {
178+ write(tracing_enabled, "0", 1);
179+ close(tracing_enabled);
180+ } else
181+ fatal("Could not access %s\n",
182+ tracing_enabled_file);
183+ par->shutdown = 1;
184+ neighbor->shutdown = 1;
185+ }
186+ 
187+ sb.sem_num = SEM_WAIT_FOR_RECEIVER;
188+ sb.sem_op = SEM_UNLOCK;
189+ semop(par->semid, &sb, 1);
190+ 
191+ nanosleep(&par->delay, NULL);
192+ 
193+ sb.sem_num = SEM_WAIT_FOR_SENDER;
194+ sb.sem_op = SEM_UNLOCK;
195+ semop(par->semid, &sb, 1);
196+ }
197+ }
198+ if (par->sender) {
199+ sb.sem_num = SEM_WAIT_FOR_SENDER;
200+ sb.sem_op = SEM_UNLOCK;
201+ semop(par->semid, &sb, 1);
202+ 
203+ sb.sem_num = SEM_WAIT_FOR_RECEIVER;
204+ sb.sem_op = SEM_UNLOCK;
205+ semop(par->semid, &sb, 1);
206+ }
207+ par->stopped = 1;
208+ return NULL;
209+}
210+ 
211+ 
212+union semun {
213+ int val; /* Value for SETVAL */
214+ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
215+ unsigned short *array; /* Array for GETALL, SETALL */
216+ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
217+};
218+ 
219+ 
220+static void display_help(int error)
221+{
222+ printf("svsematest V %1.2f\n", VERSION);
223+ printf("Usage:\n"
224+ "svsematest <options>\n\n"
225+ "Function: test SYSV semaphore latency\n\n"
226+ "Avaiable options:\n"
227+ "-a [NUM] --affinity run thread #N on processor #N, if possible\n"
228+ " with NUM pin all threads to the processor NUM\n"
229+ "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n"
230+ "-d DIST --distance=DIST distance of thread intervals in us default=500\n"
231+ "-D --duration=TIME specify a length for the test run.\n"
232+ " Append 'm', 'h', or 'd' to specify minutes, hours or\n"
233+ " days.\n"
234+ "-f [OPT] --fork[=OPT] fork new processes instead of creating threads\n"
235+ "-i INTV --interval=INTV base interval of thread in us default=1000\n"
236+ " --json=FILENAME write final results into FILENAME, JSON formatted\n"
237+ "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n"
238+ "-p PRIO --prio=PRIO priority\n"
239+ "-S --smp SMP testing: options -a -t and same priority\n"
240+ " of all threads\n"
241+ "-t --threads one thread per available processor\n"
242+ "-t [NUM] --threads[=NUM] number of threads:\n"
243+ " without NUM, threads = max_cpus\n"
244+ " without -t default = 1\n"
245+ );
246+ exit(error);
247+}
248+ 
249+static int setaffinity = AFFINITY_UNSPECIFIED;
250+static int affinity;
251+static int priority;
252+static int num_threads = 1;
253+static int max_cycles;
254+static int duration;
255+static int interval = 1000;
256+static int distance = 500;
257+static int smp;
258+static int sameprio;
259+static int quiet;
260+static char jsonfile[MAX_PATH];
261+ 
262+enum option_value {
263+ OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_DISTANCE, OPT_DURATION,
264+ OPT_FORK, OPT_HELP, OPT_INTERVAL, OPT_JSON, OPT_LOOPS,
265+ OPT_PRIORITY, OPT_QUIET, OPT_SMP, OPT_THREADS
266+};
267+ 
268+static void process_options(int argc, char *argv[])
269+{
270+ int error = 0;
271+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
272+ int thistracelimit = 0;
273+ 
274+ for (;;) {
275+ int option_index = 0;
276+ /** Options for getopt */
277+ static struct option long_options[] = {
278+ {"affinity", optional_argument, NULL, OPT_AFFINITY},
279+ {"breaktrace", required_argument, NULL, OPT_BREAKTRACE},
280+ {"distance", required_argument, NULL, OPT_DISTANCE},
281+ {"duration", required_argument, NULL, OPT_DURATION},
282+ {"fork", optional_argument, NULL, OPT_FORK},
283+ {"help", no_argument, NULL, OPT_HELP},
284+ {"interval", required_argument, NULL, OPT_INTERVAL},
285+ {"json", required_argument, NULL, OPT_JSON},
286+ {"loops", required_argument, NULL, OPT_LOOPS},
287+ {"priority", required_argument, NULL, OPT_PRIORITY},
288+ {"quiet", no_argument, NULL, OPT_QUIET},
289+ {"smp", no_argument, NULL, OPT_SMP},
290+ {"threads", optional_argument, NULL, OPT_THREADS},
291+ {NULL, 0, NULL, 0}
292+ };
293+ int c = getopt_long (argc, argv, "a::b:d:D:f::hi:l:p:qSt::",
294+ long_options, &option_index);
295+ if (c == -1)
296+ break;
297+ switch (c) {
298+ case OPT_AFFINITY:
299+ case 'a':
300+ if (smp) {
301+ warn("-a ignored due to --smp\n");
302+ break;
303+ }
304+ if (optarg != NULL) {
305+ affinity = atoi(optarg);
306+ setaffinity = AFFINITY_SPECIFIED;
307+ } else if (optind < argc && atoi(argv[optind])) {
308+ affinity = atoi(argv[optind]);
309+ setaffinity = AFFINITY_SPECIFIED;
310+ } else {
311+ setaffinity = AFFINITY_USEALL;
312+ }
313+ break;
314+ case OPT_BREAKTRACE:
315+ case 'b':
316+ thistracelimit = atoi(optarg);
317+ break;
318+ case OPT_DISTANCE:
319+ case 'd':
320+ distance = atoi(optarg);
321+ break;
322+ case OPT_DURATION:
323+ case 'D':
324+ duration = parse_time_string(optarg);
325+ break;
326+ case OPT_FORK:
327+ case 'f':
328+ if (optarg != NULL) {
329+ wasforked = 1;
330+ if (optarg[0] == 's')
331+ wasforked_sender = 1;
332+ else if (optarg[0] == 'r')
333+ wasforked_sender = 0;
334+ wasforked_threadno = atoi(optarg+1);
335+ } else
336+ mustfork = 1;
337+ break;
338+ case OPT_HELP:
339+ case 'h':
340+ display_help(0);
341+ break;
342+ case OPT_INTERVAL:
343+ case 'i':
344+ interval = atoi(optarg);
345+ break;
346+ case OPT_JSON:
347+ strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1));
348+ break;
349+ case OPT_LOOPS:
350+ case 'l':
351+ max_cycles = atoi(optarg);
352+ break;
353+ case OPT_PRIORITY:
354+ case 'p':
355+ priority = atoi(optarg);
356+ break;
357+ case OPT_QUIET:
358+ case 'q':
359+ quiet = 1;
360+ break;
361+ case OPT_SMP:
362+ case 'S':
363+ smp = 1;
364+ num_threads = max_cpus;
365+ setaffinity = AFFINITY_USEALL;
366+ break;
367+ case OPT_THREADS:
368+ case 't':
369+ if (smp) {
370+ warn("-t ignored due to --smp\n");
371+ break;
372+ }
373+ if (optarg != NULL)
374+ num_threads = atoi(optarg);
375+ else if (optind < argc && atoi(argv[optind]))
376+ num_threads = atoi(argv[optind]);
377+ else
378+ num_threads = max_cpus;
379+ break;
380+ default:
381+ display_help(1);
382+ break;
383+ }
384+ }
385+ 
386+ if (!wasforked) {
387+ if (setaffinity == AFFINITY_SPECIFIED) {
388+ if (affinity < 0)
389+ error = 1;
390+ if (affinity >= max_cpus) {
391+ fprintf(stderr, "ERROR: CPU #%d not found, "
392+ "only %d CPUs available\n",
393+ affinity, max_cpus);
394+ error = 1;
395+ }
396+ }
397+ 
398+ if (duration < 0)
399+ error = 0;
400+ 
401+ if (num_threads < 1)
402+ error = 1;
403+ 
404+ if (priority < 0 || priority > 99)
405+ error = 1;
406+ 
407+ if (priority && smp)
408+ sameprio = 1;
409+ 
410+ tracelimit = thistracelimit;
411+ }
412+ if (error)
413+ display_help(error);
414+}
415+ 
416+ 
417+static int volatile mustshutdown;
418+ 
419+static void sighand(int sig __attribute__ ((unused)))
420+{
421+ mustshutdown = 1;
422+}
423+ 
424+struct params_stats {
425+ struct params *receiver;
426+ struct params *sender;
427+};
428+ 
429+static void write_stats(FILE *f, void *data)
430+{
431+ struct params_stats *ps = data;
432+ struct params *s, *r;
433+ int i;
434+ 
435+ fprintf(f, " \"num_threads\": %d,\n", num_threads);
436+ fprintf(f, " \"thread\": {\n");
437+ for (i = 0; i < num_threads; i++) {
438+ s = &ps->sender[i];
439+ r = &ps->receiver[i];
440+ fprintf(f, " \"%u\": {\n", i);
441+ fprintf(f, " \"sender\": {\n");
442+ fprintf(f, " \"cpu\": %d,\n", s->cpu);
443+ fprintf(f, " \"priority\": %d,\n", s->priority);
444+ fprintf(f, " \"samples\": %d,\n", s->samples);
445+ fprintf(f, " \"interval\": %ld\n", r->delay.tv_nsec/1000);
446+ fprintf(f, " },\n");
447+ fprintf(f, " \"receiver\": {\n");
448+ fprintf(f, " \"cpu\": %d,\n", r->cpu);
449+ fprintf(f, " \"priority\": %d,\n", r->priority);
450+ fprintf(f, " \"min\": %d,\n", r->mindiff);
451+ fprintf(f, " \"avg\": %.2f,\n", r->sumdiff/r->samples);
452+ fprintf(f, " \"max\": %d\n", r->maxdiff);
453+ fprintf(f, " }\n");
454+ fprintf(f, " }%s\n", i == num_threads - 1 ? "" : ",");
455+ }
456+ fprintf(f, " }\n");
457+}
458+ 
459+static void print_stat(FILE *fp __attribute__ ((unused)), struct params *receiver, struct params *sender, int verbose __attribute__ ((unused)), int quiet)
460+{
461+ int i;
462+ 
463+ if (quiet)
464+ return;
465+ 
466+ for (i = 0; i < num_threads; i++) {
467+ int receiver_pid, sender_pid;
468+ 
469+ if (mustfork) {
470+ receiver_pid = receiver[i].pid;
471+ sender_pid = sender[i].pid;
472+ } else {
473+ receiver_pid = receiver[i].tid;
474+ sender_pid = sender[i].tid;
475+ }
476+ printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: ID%d, P%d, CPU%d, Cycles %d\n",
477+ i*2, receiver_pid, receiver[i].priority,
478+ receiver[i].cpu, receiver[i].delay.tv_nsec /
479+ 1000, i*2+1, sender_pid, sender[i].priority,
480+ sender[i].cpu, sender[i].samples);
481+ }
482+ 
483+ for (i = 0; i < num_threads; i++) {
484+ if (receiver[i].mindiff == -1)
485+ printf("#%d -> #%d (not yet ready)\n",
486+ i*2+1, i*2);
487+ else
488+ printf("#%d -> #%d, Min %4d, Cur %4d, Avg %4d, Max %4d\n",
489+ i*2+1, i*2, receiver[i].mindiff,
490+ (int) receiver[i].diff.tv_usec,
491+ (int) ((receiver[i].sumdiff /
492+ receiver[i].samples) + 0.5),
493+ receiver[i].maxdiff);
494+ }
495+}
496+ 
497+int main(int argc, char *argv[])
498+{
499+ char *myfile;
500+ int i, totalsize = 0;
501+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
502+ int oldsamples = 1;
503+ key_t key;
504+ union semun args;
505+ struct params *receiver = NULL;
506+ struct params *sender = NULL;
507+ sigset_t sigset;
508+ void *param = NULL;
509+ char f_opt[14];
510+ struct timespec launchdelay, maindelay;
511+ 
512+ myfile = getenv("_");
513+ if (myfile == NULL)
514+ myfile = argv[0];
515+ 
516+ rt_init(argc, argv);
517+ process_options(argc, argv);
518+ 
519+ if (check_privs())
520+ return 1;
521+ 
522+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
523+ perror("mlockall");
524+ return 1;
525+ }
526+ 
527+ get_cpu_setup();
528+ 
529+ if (mustfork) {
530+ int shmem;
531+ 
532+ /*
533+ * In fork mode (-f), the shared memory contains two
534+ * subsequent arrays, receiver[num_threads] and
535+ * sender[num_threads].
536+ */
537+ totalsize = num_threads * sizeof(struct params) * 2;
538+ 
539+ shm_unlink("/sigwaittest");
540+ shmem = shm_open("/sigwaittest", O_CREAT|O_EXCL|O_RDWR,
541+ S_IRUSR|S_IWUSR);
542+ if (shmem < 0) {
543+ fprintf(stderr, "Could not create shared memory\n");
544+ return 1;
545+ }
546+ ftruncate(shmem, totalsize);
547+ param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED,
548+ shmem, 0);
549+ if (param == MAP_FAILED) {
550+ fprintf(stderr, "Could not map shared memory\n");
551+ close(shmem);
552+ return 1;
553+ }
554+ 
555+ receiver = (struct params *) param;
556+ sender = receiver + num_threads;
557+ } else if (wasforked) {
558+ struct stat buf;
559+ int shmem, totalsize, expect_totalsize;
560+ 
561+ if (wasforked_threadno == -1 || wasforked_sender == -1) {
562+ fprintf(stderr, "Invalid fork option\n");
563+ return 1;
564+ }
565+ shmem = shm_open("/sigwaittest", O_RDWR, S_IRUSR|S_IWUSR);
566+ if (fstat(shmem, &buf)) {
567+ fprintf(stderr,
568+ "Could not determine shared memory size\n");
569+ close(shmem);
570+ return 1;
571+ }
572+ totalsize = buf.st_size;
573+ param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED,
574+ shmem, 0);
575+ close(shmem);
576+ if (param == MAP_FAILED) {
577+ fprintf(stderr, "Could not map shared memory\n");
578+ return 1;
579+ }
580+ 
581+ receiver = (struct params *) param;
582+ expect_totalsize = receiver->num_threads *
583+ sizeof(struct params) * 2;
584+ if (totalsize != expect_totalsize) {
585+ fprintf(stderr, "Memory size problem (expected %d, "
586+ "found %d\n", expect_totalsize, totalsize);
587+ munmap(param, totalsize);
588+ return 1;
589+ }
590+ sender = receiver + receiver->num_threads;
591+ if (wasforked_sender)
592+ semathread(sender + wasforked_threadno);
593+ else
594+ semathread(receiver + wasforked_threadno);
595+ munmap(param, totalsize);
596+ return 0;
597+ }
598+ 
599+ signal(SIGINT, sighand);
600+ signal(SIGTERM, sighand);
601+ signal(SIGALRM, sighand);
602+ 
603+ sigemptyset(&sigset);
604+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
605+ 
606+ if (duration)
607+ alarm(duration);
608+ 
609+ if (!mustfork && !wasforked) {
610+ receiver = calloc(num_threads, sizeof(struct params));
611+ sender = calloc(num_threads, sizeof(struct params));
612+ if (receiver == NULL || sender == NULL)
613+ goto nomem;
614+ }
615+ 
616+ launchdelay.tv_sec = 0;
617+ launchdelay.tv_nsec = 10000000; /* 10 ms */
618+ 
619+ maindelay.tv_sec = 0;
620+ maindelay.tv_nsec = 50000000; /* 50 ms */
621+ 
622+ for (i = 0; i < num_threads; i++) {
623+ struct sembuf sb = { 0, 0, 0};
624+ 
625+ receiver[i].mindiff = UINT_MAX;
626+ receiver[i].maxdiff = 0;
627+ receiver[i].sumdiff = 0.0;
628+ 
629+ if ((key = ftok(myfile, i)) == -1) {
630+ perror("ftok");
631+ goto nosem;
632+ }
633+ 
634+ if ((receiver[i].semid = semget(key, 2, 0666 | IPC_CREAT)) == -1) {
635+ perror("semget");
636+ goto nosem;
637+ }
638+ 
639+ args.val = 1;
640+ if (semctl(receiver[i].semid, SEM_WAIT_FOR_RECEIVER, SETVAL, args) == -1) {
641+ perror("semctl sema #0");
642+ goto nosem;
643+ }
644+ 
645+ if (semctl(receiver[i].semid, SEM_WAIT_FOR_SENDER, SETVAL, args) == -1) {
646+ perror("semctl sema #1");
647+ goto nosem;
648+ }
649+ 
650+ sb.sem_num = SEM_WAIT_FOR_RECEIVER;
651+ sb.sem_op = SEM_LOCK;
652+ semop(receiver[i].semid, &sb, 1);
653+ 
654+ sb.sem_num = SEM_WAIT_FOR_SENDER;
655+ sb.sem_op = SEM_LOCK;
656+ semop(receiver[i].semid, &sb, 1);
657+ 
658+ receiver[i].cpu = i;
659+ switch (setaffinity) {
660+ case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break;
661+ case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break;
662+ case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break;
663+ }
664+ receiver[i].priority = priority;
665+ receiver[i].tracelimit = tracelimit;
666+ if (priority > 1 && !sameprio)
667+ priority--;
668+ receiver[i].delay.tv_sec = interval / USEC_PER_SEC;
669+ receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000;
670+ interval += distance;
671+ receiver[i].max_cycles = max_cycles;
672+ receiver[i].sender = 0;
673+ receiver[i].neighbor = &sender[i];
674+ if (mustfork) {
675+ pid_t pid = fork();
676+ if (pid == -1) {
677+ fprintf(stderr, "Could not fork\n");
678+ return 1;
679+ } else if (pid == 0) {
680+ char *args[3];
681+ 
682+ receiver[i].num_threads = num_threads;
683+ receiver[i].pid = getpid();
684+ sprintf(f_opt, "-fr%d", i);
685+ args[0] = argv[0];
686+ args[1] = f_opt;
687+ args[2] = NULL;
688+ execvp(args[0], args);
689+ fprintf(stderr,
690+ "Could not execute receiver child process "
691+ "#%d\n", i);
692+ }
693+ } else
694+ pthread_create(&receiver[i].threadid, NULL,
695+ semathread, &receiver[i]);
696+ 
697+ nanosleep(&launchdelay, NULL);
698+ 
699+ memcpy(&sender[i], &receiver[i], sizeof(receiver[0]));
700+ sender[i].sender = 1;
701+ sender[i].neighbor = &receiver[i];
702+ if (mustfork) {
703+ pid_t pid = fork();
704+ if (pid == -1) {
705+ fprintf(stderr, "Could not fork\n");
706+ return 1;
707+ } else if (pid == 0) {
708+ char *args[3];
709+ 
710+ sender[i].num_threads = num_threads;
711+ sender[i].pid = getpid();
712+ sprintf(f_opt, "-fs%d", i);
713+ args[0] = argv[0];
714+ args[1] = f_opt;
715+ args[2] = NULL;
716+ execvp(args[0], args);
717+ fprintf(stderr,
718+ "Could not execute sender child process "
719+ "#%d\n", i);
720+ }
721+ } else
722+ pthread_create(&sender[i].threadid, NULL, semathread,
723+ &sender[i]);
724+ }
725+ 
726+ while (!mustshutdown) {
727+ for (i = 0; i < num_threads; i++)
728+ mustshutdown |= receiver[i].shutdown |
729+ sender[i].shutdown;
730+ 
731+ if (receiver[0].samples > oldsamples || mustshutdown) {
732+ print_stat(stdout, receiver, sender, 0, quiet);
733+ if (!quiet)
734+ printf("\033[%dA", num_threads*2);
735+ }
736+ 
737+ sigemptyset(&sigset);
738+ sigaddset(&sigset, SIGTERM);
739+ sigaddset(&sigset, SIGINT);
740+ sigaddset(&sigset, SIGALRM);
741+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
742+ 
743+ nanosleep(&maindelay, NULL);
744+ 
745+ sigemptyset(&sigset);
746+ pthread_sigmask(SIG_SETMASK, &sigset, NULL);
747+ }
748+ 
749+ if (!quiet)
750+ printf("\033[%dB", num_threads*2 + 2);
751+ else
752+ print_stat(stdout, receiver, sender, 0, 0);
753+ 
754+ for (i = 0; i < num_threads; i++) {
755+ receiver[i].shutdown = 1;
756+ sender[i].shutdown = 1;
757+ }
758+ nanosleep(&receiver[0].delay, NULL);
759+ 
760+ for (i = 0; i < num_threads; i++) {
761+ if (!receiver[i].stopped) {
762+ if (mustfork)
763+ kill(receiver[i].pid, SIGTERM);
764+ else
765+ pthread_kill(receiver[i].threadid, SIGTERM);
766+ }
767+ if (!sender[i].stopped) {
768+ if (mustfork)
769+ kill(sender[i].pid, SIGTERM);
770+ else
771+ pthread_kill(sender[i].threadid, SIGTERM);
772+ }
773+ }
774+ 
775+ if (strlen(jsonfile) != 0) {
776+ struct params_stats ps = {
777+ .receiver = receiver,
778+ .sender = sender,
779+ };
780+ rt_write_json(jsonfile, 0, write_stats, &ps);
781+ }
782+ 
783+nosem:
784+ for (i = 0; i < num_threads; i++)
785+ semctl(receiver[i].semid, -1, IPC_RMID);
786+ 
787+nomem:
788+ if (mustfork) {
789+ munmap(param, totalsize);
790+ shm_unlink("/sigwaittest");
791+ }
792+ 
793+ return 0;
794+}