examples/performance-thread: remove
Remove sample application which is not clear if it is still relevant. Signed-off-by: Ferruh Yigit <ferruh.yigit@intel.com>
This commit is contained in:
parent
65ac1464ff
commit
1dcbc676d5
@ -1753,11 +1753,6 @@ Link status interrupt example
|
||||
F: examples/link_status_interrupt/
|
||||
F: doc/guides/sample_app_ug/link_status_intr.rst
|
||||
|
||||
L-threads - EXPERIMENTAL
|
||||
M: John McNamara <john.mcnamara@intel.com>
|
||||
F: examples/performance-thread/
|
||||
F: doc/guides/sample_app_ug/performance_thread.rst
|
||||
|
||||
PTP client example
|
||||
M: Kirill Rybalchenko <kirill.rybalchenko@intel.com>
|
||||
F: examples/ptpclient/
|
||||
|
@ -99,7 +99,6 @@ arm_64_sources()
|
||||
|
||||
x86_common()
|
||||
{
|
||||
find_sources "examples/performance-thread/common/arch/x86" '*.[chS]'
|
||||
find_sources "$source_dirs" '*_sse*.[chS]'
|
||||
find_sources "$source_dirs" '*_avx*.[chS]'
|
||||
find_sources "$source_dirs" '*x86.[chS]'
|
||||
|
@ -68,6 +68,8 @@ Removed Items
|
||||
Also, make sure to start the actual text at the margin.
|
||||
=======================================================
|
||||
|
||||
* **Removed experimental performance thread example application.**
|
||||
|
||||
|
||||
API Changes
|
||||
-----------
|
||||
|
@ -1,799 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="449.57141"
|
||||
height="187.34319"
|
||||
viewBox="0 0 449.57143 187.34319"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="performance_thread_1.svg"
|
||||
inkscape:export-filename="C:\Users\tkulasex\Documents\L-threads\model-v2.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11487"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11489"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11285"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11287"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11107"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11109"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10757"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10759"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10421"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10423"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10273"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10275"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9983"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path9985"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9853"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path9855"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Lend-6"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4248-0"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4992-4"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path4994-2" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4254-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4992-4-0"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path4994-2-9" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Lend-6-8"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4248-0-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker5952-2"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path5954-4" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker5952-2-1"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path5954-4-2" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6881-5"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path6883-0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3-0"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4-2"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3-0-4"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4-2-4"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4-6"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10119-2"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10121-6"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11487-0"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11489-6"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10585"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path10587"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10273-9"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10275-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10421-3"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10423-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-2"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-5"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10119"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10121"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10923"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10925"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10757-4"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10759-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="138.23152"
|
||||
inkscape:cy="-30.946457"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4142-7"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
width="744.09px" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-40.428564,-78.569476)">
|
||||
<g
|
||||
transform="translate(7.9156519e-7,106.78572)"
|
||||
id="g4142-7">
|
||||
<g
|
||||
transform="translate(162.14285,0.35714094)"
|
||||
id="g4177-1">
|
||||
<g
|
||||
transform="translate(-160.49999,-56.592401)"
|
||||
id="g4142-55-1">
|
||||
<rect
|
||||
y="43.076488"
|
||||
x="39.285713"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-65-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-4-8"
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
id="tspan4140-2-4"
|
||||
sodipodi:role="line">Port 1</tspan></text>
|
||||
</g>
|
||||
<rect
|
||||
y="93.269798"
|
||||
x="-121.21429"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-8-3-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 1.00000002;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-8-7-3"
|
||||
y="-98.052498"
|
||||
x="-145.70891"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="-98.052498"
|
||||
x="-145.70891"
|
||||
id="tspan4140-5-8-3"
|
||||
sodipodi:role="line">Port 2</tspan></text>
|
||||
<g
|
||||
transform="translate(-158.35713,1.6218895)"
|
||||
id="g4177-7-6">
|
||||
<rect
|
||||
y="1.2907723"
|
||||
x="132.85715"
|
||||
height="46.42857"
|
||||
width="94.285713"
|
||||
id="rect4171-1-9"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4173-0-0"
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
id="tspan4175-6-1"
|
||||
sodipodi:role="line">rx-thread</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="86.642853"
|
||||
y="78.626976"
|
||||
id="text5627-0-5"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5629-8-6"
|
||||
x="86.642853"
|
||||
y="78.626976">rings</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10757)"
|
||||
d="m -83.357144,17.912679 56.42858,4.28571"
|
||||
id="path4239-3-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10923)"
|
||||
d="m -82.808124,125.71821 53.57145,-9.28573"
|
||||
id="path4239-0-3-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 2.00000004;stroke-dashoffset:0;marker-end:url(#marker10119)"
|
||||
d="m 68.78571,29.341249 62.5,28.21429"
|
||||
id="path5457-1-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<g
|
||||
transform="translate(-161.92858,95.100119)"
|
||||
id="g4177-7-6-7">
|
||||
<rect
|
||||
y="1.2907723"
|
||||
x="132.85715"
|
||||
height="46.42857"
|
||||
width="94.285713"
|
||||
id="rect4171-1-9-8"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4173-0-0-6"
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
id="tspan4175-6-1-8"
|
||||
sodipodi:role="line">rx-thread</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(249.5,-71.149881)"
|
||||
id="g4142-5-1-2">
|
||||
<rect
|
||||
y="43.076488"
|
||||
x="39.285713"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-6-5-3"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-3-3-5"
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
id="tspan4140-7-3-5"
|
||||
sodipodi:role="line">Port 1</tspan></text>
|
||||
</g>
|
||||
<rect
|
||||
y="74.426659"
|
||||
x="288.07141"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-8-4-7-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 1.00000002;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-8-2-5-8"
|
||||
y="311.23318"
|
||||
x="-126.86578"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="311.23318"
|
||||
x="-126.86578"
|
||||
id="tspan4140-5-4-9-6"
|
||||
sodipodi:role="line">Port 2</tspan></text>
|
||||
<g
|
||||
id="g5905-4"
|
||||
transform="translate(-1.2142913,-215.16774)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-thread</tspan></text>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10431)"
|
||||
d="M 226.28573,52.462339 287.7143,2.8194795"
|
||||
id="path4984-4-07"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10421)"
|
||||
d="m 227.09388,122.75669 60.35714,9.64286"
|
||||
id="path4984-1-6-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<g
|
||||
id="g5905-6-0"
|
||||
transform="translate(0.21427875,-156.1499)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-9-1"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-0-0"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-7-8"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-thread</tspan></text>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10273)"
|
||||
d="m 227.19687,67.801919 58.92857,41.071411"
|
||||
id="path4984-4-0-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10585)"
|
||||
d="M 227.30382,110.24508 286.94667,24.530799"
|
||||
id="path4984-4-0-0-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 2.00000004;stroke-dashoffset:0;marker-end:url(#marker11487)"
|
||||
d="m 66.28572,118.8909 65.71429,-2.14285"
|
||||
id="path5457-1-2-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<g
|
||||
id="g5905-4-6"
|
||||
transform="translate(-3.5000113,-277.43173)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-thread</tspan></text>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 2.00000004;stroke-dashoffset:0;marker-end:url(#marker10119-2)"
|
||||
d="M 68.35772,16.118199 127.64343,-6.3818105"
|
||||
id="path5457-1-2-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10431-3)"
|
||||
d="m 224.52079,-13.531251 64.28571,2.14286"
|
||||
id="path4984-4-07-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10431-3-0)"
|
||||
d="M 224.17025,2.1505695 287.02739,87.864849"
|
||||
id="path4984-4-07-4-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 29 KiB |
@ -1,865 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="449.57141"
|
||||
height="187.34319"
|
||||
viewBox="0 0 449.57143 187.34319"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="performance_thread_2.svg"
|
||||
inkscape:export-filename="C:\Users\tkulasex\Documents\L-threads\model-v2.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11487"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11489"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11285"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11287"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11107"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11109"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10757"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10759"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10421"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10423"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10273"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10275"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9983"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path9985"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker9853"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path9855"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Lend-6"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4248-0"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4992-4"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path4994-2" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4254-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker4992-4-0"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path4994-2-9" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Lend-6-8"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4248-0-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker5952-2"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path5954-4" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker5952-2-1"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path5954-4-2" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker6881-5"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path6883-0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3-0"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4-2"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3-0-4"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4-2-4"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-3-1"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-4-6"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10119-2"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10121-6"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker11487-0"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path11489-6"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10585"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
id="path10587"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10273-9"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10275-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10421-3"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10423-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="marker10431-2"
|
||||
style="overflow:visible"
|
||||
inkscape:isstock="true">
|
||||
<path
|
||||
id="path10433-5"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10119"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10121"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10923"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend"
|
||||
inkscape:collect="always">
|
||||
<path
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10925"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:isstock="true"
|
||||
style="overflow:visible"
|
||||
id="marker10757-4"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto"
|
||||
inkscape:stockid="Arrow1Lend">
|
||||
<path
|
||||
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
id="path10759-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="138.23152"
|
||||
inkscape:cy="-30.946457"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="g4177-1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1148"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
width="744.09px" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-40.428564,-78.569476)">
|
||||
<g
|
||||
transform="translate(7.9156519e-7,106.78572)"
|
||||
id="g4142-7">
|
||||
<g
|
||||
transform="translate(162.14285,0.35714094)"
|
||||
id="g4177-1">
|
||||
<g
|
||||
transform="translate(-160.49999,-56.592401)"
|
||||
id="g4142-55-1">
|
||||
<rect
|
||||
y="43.076488"
|
||||
x="39.285713"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-65-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-4-8"
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
id="tspan4140-2-4"
|
||||
sodipodi:role="line">Port 1</tspan></text>
|
||||
</g>
|
||||
<rect
|
||||
y="93.269798"
|
||||
x="-121.21429"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-8-3-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 1.00000002;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-8-7-3"
|
||||
y="-98.052498"
|
||||
x="-145.70891"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="-98.052498"
|
||||
x="-145.70891"
|
||||
id="tspan4140-5-8-3"
|
||||
sodipodi:role="line">Port 2</tspan></text>
|
||||
<g
|
||||
transform="translate(-158.35713,1.6218895)"
|
||||
id="g4177-7-6">
|
||||
<rect
|
||||
y="1.2907723"
|
||||
x="132.85715"
|
||||
height="46.42857"
|
||||
width="94.285713"
|
||||
id="rect4171-1-9"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4173-0-0"
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
id="tspan4175-6-1"
|
||||
sodipodi:role="line">rx-thread</tspan></text>
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="86.642853"
|
||||
y="78.626976"
|
||||
id="text5627-0-5"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5629-8-6"
|
||||
x="86.642853"
|
||||
y="78.626976">rings</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10757)"
|
||||
d="m -83.357144,17.912679 56.42858,4.28571"
|
||||
id="path4239-3-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10923)"
|
||||
d="m -82.808124,125.71821 53.57145,-9.28573"
|
||||
id="path4239-0-3-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 2.00000004;stroke-dashoffset:0;marker-end:url(#marker10119)"
|
||||
d="m 68.78571,29.341249 62.5,28.21429"
|
||||
id="path5457-1-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<g
|
||||
transform="translate(-161.92858,95.100119)"
|
||||
id="g4177-7-6-7">
|
||||
<rect
|
||||
y="1.2907723"
|
||||
x="132.85715"
|
||||
height="46.42857"
|
||||
width="94.285713"
|
||||
id="rect4171-1-9-8"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4173-0-0-6"
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="29.147915"
|
||||
x="146.42856"
|
||||
id="tspan4175-6-1-8"
|
||||
sodipodi:role="line">rx-thread</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(249.5,-71.149881)"
|
||||
id="g4142-5-1-2">
|
||||
<rect
|
||||
y="43.076488"
|
||||
x="39.285713"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-6-5-3"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-3-3-5"
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="62.447506"
|
||||
x="-95.515633"
|
||||
id="tspan4140-7-3-5"
|
||||
sodipodi:role="line">Port 1</tspan></text>
|
||||
</g>
|
||||
<rect
|
||||
y="74.426659"
|
||||
x="288.07141"
|
||||
height="65"
|
||||
width="38.57143"
|
||||
id="rect4136-8-4-7-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 1.00000002;stroke-dashoffset:0" />
|
||||
<text
|
||||
transform="matrix(0,-1,1,0,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4138-8-2-5-8"
|
||||
y="311.23318"
|
||||
x="-126.86578"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
xml:space="preserve"><tspan
|
||||
y="311.23318"
|
||||
x="-126.86578"
|
||||
id="tspan4140-5-4-9-6"
|
||||
sodipodi:role="line">Port 2</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10431)"
|
||||
d="M 226.28573,52.462339 287.7143,2.8194795"
|
||||
id="path4984-4-07"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10421)"
|
||||
d="m 227.09388,122.75669 60.35714,9.64286"
|
||||
id="path4984-1-6-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10273)"
|
||||
d="m 227.19687,67.801919 58.92857,41.071411"
|
||||
id="path4984-4-0-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10585)"
|
||||
d="M 228.01811,113.10222 287.66096,27.387942"
|
||||
id="path4984-4-0-0-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 2.00000004;stroke-dashoffset:0;marker-end:url(#marker11487)"
|
||||
d="m 66.28572,118.8909 65.71429,-2.14285"
|
||||
id="path5457-1-2-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<g
|
||||
id="g5905-4-6"
|
||||
transform="matrix(1,0,0,0.48279909,-0.64286832,-142.16523)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-thread</tspan></text>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.00000008, 2.00000004;stroke-dashoffset:0;marker-end:url(#marker10119-2)"
|
||||
d="M 68.35772,16.118199 127.64343,-6.3818105"
|
||||
id="path5457-1-2-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10431-3)"
|
||||
d="m 224.52079,-13.531251 64.28571,2.14286"
|
||||
id="path4984-4-07-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker10431-3-0)"
|
||||
d="M 224.17025,2.1505695 287.02739,87.864849"
|
||||
id="path4984-4-07-4-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<g
|
||||
id="g5905-4-6-5"
|
||||
transform="matrix(1,0,0,0.45244466,-0.99999222,-110.73112)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7-6"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8-7"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5-0"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-drain</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g5905-4-6-2"
|
||||
transform="matrix(1,0,0,0.48279909,1.3158755,-80.292458)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7-8"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8-0"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5-6"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-thread</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g5905-4-6-5-9"
|
||||
transform="matrix(1,0,0,0.45244466,0.95875552,-48.858358)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7-6-6"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8-7-4"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5-0-0"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-drain</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g5905-4-6-6"
|
||||
transform="matrix(1,0,0,0.48279909,1.315876,-24.578174)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7-3"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8-78"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5-9"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-thread</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g5905-4-6-5-0"
|
||||
transform="matrix(1,0,0,0.45244466,0.958756,6.8559263)">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 1;stroke-dashoffset:0"
|
||||
id="rect4171-9-0-0-7-6-0"
|
||||
width="94.285713"
|
||||
height="46.42857"
|
||||
x="132.85715"
|
||||
y="250.48721" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:15px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="146.42856"
|
||||
y="278.34433"
|
||||
id="text4173-9-2-6-8-7-0"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4175-0-7-3-5-0-3"
|
||||
x="146.42856"
|
||||
y="278.34433">tx-drain</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 32 KiB |
@ -53,7 +53,6 @@ Sample Applications User Guides
|
||||
dist_app
|
||||
vm_power_management
|
||||
ptpclient
|
||||
performance_thread
|
||||
fips_validation
|
||||
ipsec_secgw
|
||||
bbdev_app
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -43,8 +43,6 @@ all_examples = [
|
||||
'multi_process/symmetric_mp',
|
||||
'ntb',
|
||||
'packet_ordering',
|
||||
'performance-thread/l3fwd-thread',
|
||||
'performance-thread/pthread_shim',
|
||||
'pipeline',
|
||||
'ptpclient',
|
||||
'qos_meter',
|
||||
|
@ -1,14 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2015-2020 Intel Corporation
|
||||
|
||||
subdirs := l3fwd-thread pthread_shim
|
||||
|
||||
.PHONY: all static shared clean $(subdirs)
|
||||
all static shared clean: $(subdirs)
|
||||
|
||||
ifeq ($(filter $(shell uname -m),x86_64 arm64),)
|
||||
$(error This application is only supported for x86_64 and arm64 targets)
|
||||
endif
|
||||
|
||||
$(subdirs):
|
||||
$(MAKE) -C $@ $(MAKECMDGOALS)
|
@ -1,62 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2017 Cavium, Inc
|
||||
*/
|
||||
|
||||
#include <rte_common.h>
|
||||
#include <ctx.h>
|
||||
|
||||
void
|
||||
ctx_switch(struct ctx *new_ctx __rte_unused, struct ctx *curr_ctx __rte_unused)
|
||||
{
|
||||
/* SAVE CURRENT CONTEXT */
|
||||
asm volatile (
|
||||
/* Save SP */
|
||||
"mov x3, sp\n"
|
||||
"str x3, [x1, #0]\n"
|
||||
|
||||
/* Save FP and LR */
|
||||
"stp x29, x30, [x1, #8]\n"
|
||||
|
||||
/* Save Callee Saved Regs x19 - x28 */
|
||||
"stp x19, x20, [x1, #24]\n"
|
||||
"stp x21, x22, [x1, #40]\n"
|
||||
"stp x23, x24, [x1, #56]\n"
|
||||
"stp x25, x26, [x1, #72]\n"
|
||||
"stp x27, x28, [x1, #88]\n"
|
||||
|
||||
/*
|
||||
* Save bottom 64-bits of Callee Saved
|
||||
* SIMD Regs v8 - v15
|
||||
*/
|
||||
"stp d8, d9, [x1, #104]\n"
|
||||
"stp d10, d11, [x1, #120]\n"
|
||||
"stp d12, d13, [x1, #136]\n"
|
||||
"stp d14, d15, [x1, #152]\n"
|
||||
);
|
||||
|
||||
/* RESTORE NEW CONTEXT */
|
||||
asm volatile (
|
||||
/* Restore SP */
|
||||
"ldr x3, [x0, #0]\n"
|
||||
"mov sp, x3\n"
|
||||
|
||||
/* Restore FP and LR */
|
||||
"ldp x29, x30, [x0, #8]\n"
|
||||
|
||||
/* Restore Callee Saved Regs x19 - x28 */
|
||||
"ldp x19, x20, [x0, #24]\n"
|
||||
"ldp x21, x22, [x0, #40]\n"
|
||||
"ldp x23, x24, [x0, #56]\n"
|
||||
"ldp x25, x26, [x0, #72]\n"
|
||||
"ldp x27, x28, [x0, #88]\n"
|
||||
|
||||
/*
|
||||
* Restore bottom 64-bits of Callee Saved
|
||||
* SIMD Regs v8 - v15
|
||||
*/
|
||||
"ldp d8, d9, [x0, #104]\n"
|
||||
"ldp d10, d11, [x0, #120]\n"
|
||||
"ldp d12, d13, [x0, #136]\n"
|
||||
"ldp d14, d15, [x0, #152]\n"
|
||||
);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2017 Cavium, Inc
|
||||
*/
|
||||
|
||||
#ifndef CTX_H
|
||||
#define CTX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CPU context registers
|
||||
*/
|
||||
struct ctx {
|
||||
void *sp; /* 0 */
|
||||
void *fp; /* 8 */
|
||||
void *lr; /* 16 */
|
||||
|
||||
/* Callee Saved Generic Registers */
|
||||
void *r19; /* 24 */
|
||||
void *r20; /* 32 */
|
||||
void *r21; /* 40 */
|
||||
void *r22; /* 48 */
|
||||
void *r23; /* 56 */
|
||||
void *r24; /* 64 */
|
||||
void *r25; /* 72 */
|
||||
void *r26; /* 80 */
|
||||
void *r27; /* 88 */
|
||||
void *r28; /* 96 */
|
||||
|
||||
/*
|
||||
* Callee Saved SIMD Registers. Only the bottom 64-bits
|
||||
* of these registers needs to be saved.
|
||||
*/
|
||||
void *v8; /* 104 */
|
||||
void *v9; /* 112 */
|
||||
void *v10; /* 120 */
|
||||
void *v11; /* 128 */
|
||||
void *v12; /* 136 */
|
||||
void *v13; /* 144 */
|
||||
void *v14; /* 152 */
|
||||
void *v15; /* 160 */
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
ctx_switch(struct ctx *new_ctx, struct ctx *curr_ctx);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RTE_CTX_H_ */
|
@ -1,56 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2017 Cavium, Inc
|
||||
*/
|
||||
|
||||
#ifndef STACK_H
|
||||
#define STACK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_int.h"
|
||||
|
||||
/*
|
||||
* Sets up the initial stack for the lthread.
|
||||
*/
|
||||
static inline void
|
||||
arch_set_stack(struct lthread *lt, void *func)
|
||||
{
|
||||
void **stack_top = (void *)((char *)(lt->stack) + lt->stack_size);
|
||||
|
||||
/*
|
||||
* Align stack_top to 16 bytes. Arm64 has the constraint that the
|
||||
* stack pointer must always be quad-word aligned.
|
||||
*/
|
||||
stack_top = (void **)(((unsigned long)(stack_top)) & ~0xfUL);
|
||||
|
||||
/*
|
||||
* First Stack Frame
|
||||
*/
|
||||
stack_top[0] = NULL;
|
||||
stack_top[-1] = NULL;
|
||||
|
||||
/*
|
||||
* Initialize the context
|
||||
*/
|
||||
lt->ctx.fp = &stack_top[-1];
|
||||
lt->ctx.sp = &stack_top[-2];
|
||||
|
||||
/*
|
||||
* Here only the address of _lthread_exec is saved as the link
|
||||
* register value. The argument to _lthread_exec i.e the address of
|
||||
* the lthread struct is not saved. This is because the first
|
||||
* argument to ctx_switch is the address of the new context,
|
||||
* which also happens to be the address of required lthread struct.
|
||||
* So while returning from ctx_switch into _thread_exec, parameter
|
||||
* register x0 will always contain the required value.
|
||||
*/
|
||||
lt->ctx.lr = func;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* STACK_H_ */
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
|
||||
#if defined(__x86_64__)
|
||||
__asm__ (
|
||||
".text\n"
|
||||
".p2align 4,,15\n"
|
||||
".globl ctx_switch\n"
|
||||
".globl _ctx_switch\n"
|
||||
"ctx_switch:\n"
|
||||
"_ctx_switch:\n"
|
||||
" movq %rsp, 0(%rsi) # save stack_pointer\n"
|
||||
" movq %rbp, 8(%rsi) # save frame_pointer\n"
|
||||
" movq (%rsp), %rax # save insn_pointer\n"
|
||||
" movq %rax, 16(%rsi)\n"
|
||||
" movq %rbx, 24(%rsi)\n # save rbx,r12-r15\n"
|
||||
" movq 24(%rdi), %rbx\n"
|
||||
" movq %r15, 56(%rsi)\n"
|
||||
" movq %r14, 48(%rsi)\n"
|
||||
" movq 48(%rdi), %r14\n"
|
||||
" movq 56(%rdi), %r15\n"
|
||||
" movq %r13, 40(%rsi)\n"
|
||||
" movq %r12, 32(%rsi)\n"
|
||||
" movq 32(%rdi), %r12\n"
|
||||
" movq 40(%rdi), %r13\n"
|
||||
" movq 0(%rdi), %rsp # restore stack_pointer\n"
|
||||
" movq 16(%rdi), %rax # restore insn_pointer\n"
|
||||
" movq 8(%rdi), %rbp # restore frame_pointer\n"
|
||||
" movq %rax, (%rsp)\n"
|
||||
" ret\n"
|
||||
);
|
||||
#else
|
||||
#pragma GCC error "__x86_64__ is not defined"
|
||||
#endif
|
@ -1,36 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CTX_H
|
||||
#define CTX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CPU context registers
|
||||
*/
|
||||
struct ctx {
|
||||
void *rsp; /* 0 */
|
||||
void *rbp; /* 8 */
|
||||
void *rip; /* 16 */
|
||||
void *rbx; /* 24 */
|
||||
void *r12; /* 32 */
|
||||
void *r13; /* 40 */
|
||||
void *r14; /* 48 */
|
||||
void *r15; /* 56 */
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
ctx_switch(struct ctx *new_ctx, struct ctx *curr_ctx);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RTE_CTX_H_ */
|
@ -1,40 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation.
|
||||
* Copyright(c) Cavium, Inc. 2017.
|
||||
* All rights reserved
|
||||
* Copyright (C) 2012, Hasan Alayli <halayli@gmail.com>
|
||||
* Portions derived from: https://github.com/halayli/lthread
|
||||
* With permissions from Hasan Alayli to use them as BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef STACK_H
|
||||
#define STACK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_int.h"
|
||||
|
||||
/*
|
||||
* Sets up the initial stack for the lthread.
|
||||
*/
|
||||
static inline void
|
||||
arch_set_stack(struct lthread *lt, void *func)
|
||||
{
|
||||
char *stack_top = (char *)(lt->stack) + lt->stack_size;
|
||||
void **s = (void **)stack_top;
|
||||
|
||||
/* set initial context */
|
||||
s[-3] = NULL;
|
||||
s[-2] = (void *)lt;
|
||||
lt->ctx.rsp = (void *)(stack_top - (4 * sizeof(void *)));
|
||||
lt->ctx.rbp = (void *)(stack_top - (3 * sizeof(void *)));
|
||||
lt->ctx.rip = func;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* STACK_H_ */
|
@ -1,21 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2015 Intel Corporation
|
||||
|
||||
# list the C files belonging to the lthread subsystem, these are common to all
|
||||
# lthread apps. Any makefile including this should set VPATH to include this
|
||||
# directory path
|
||||
#
|
||||
|
||||
MKFILE_PATH=$(abspath $(dir $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
ifeq ($(shell uname -m),x86_64)
|
||||
ARCH_PATH += $(MKFILE_PATH)/arch/x86
|
||||
else ifeq ($(shell uname -m),arm64)
|
||||
ARCH_PATH += $(MKFILE_PATH)/arch/arm64
|
||||
endif
|
||||
|
||||
VPATH := $(MKFILE_PATH) $(ARCH_PATH)
|
||||
|
||||
SRCS-y += lthread.c lthread_sched.c lthread_cond.c lthread_tls.c lthread_mutex.c lthread_diag.c ctx.c
|
||||
|
||||
CFLAGS += -I$(MKFILE_PATH) -I$(ARCH_PATH)
|
@ -1,470 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
|
||||
#define RTE_MEM 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <rte_log.h>
|
||||
#include <rte_string_fns.h>
|
||||
#include <ctx.h>
|
||||
#include <stack.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread.h"
|
||||
#include "lthread_timer.h"
|
||||
#include "lthread_tls.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_diag.h"
|
||||
|
||||
|
||||
/*
|
||||
* This function gets called after an lthread function has returned.
|
||||
*/
|
||||
void _lthread_exit_handler(struct lthread *lt)
|
||||
{
|
||||
|
||||
lt->state |= BIT(ST_LT_EXITED);
|
||||
|
||||
if (!(lt->state & BIT(ST_LT_DETACH))) {
|
||||
/* thread is this not explicitly detached
|
||||
* it must be joinable, so we call lthread_exit().
|
||||
*/
|
||||
lthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* if we get here the thread is detached so we can reschedule it,
|
||||
* allowing the scheduler to free it
|
||||
*/
|
||||
_reschedule();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free resources allocated to an lthread
|
||||
*/
|
||||
void _lthread_free(struct lthread *lt)
|
||||
{
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_FREE, lt, 0);
|
||||
|
||||
/* invoke any user TLS destructor functions */
|
||||
_lthread_tls_destroy(lt);
|
||||
|
||||
/* free memory allocated for TLS defined using RTE_PER_LTHREAD macros */
|
||||
if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE)
|
||||
_lthread_objcache_free(lt->tls->root_sched->per_lthread_cache,
|
||||
lt->per_lthread_data);
|
||||
|
||||
/* free pthread style TLS memory */
|
||||
_lthread_objcache_free(lt->tls->root_sched->tls_cache, lt->tls);
|
||||
|
||||
/* free the stack */
|
||||
_lthread_objcache_free(lt->stack_container->root_sched->stack_cache,
|
||||
lt->stack_container);
|
||||
|
||||
/* now free the thread */
|
||||
_lthread_objcache_free(lt->root_sched->lthread_cache, lt);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a stack and maintain a cache of stacks
|
||||
*/
|
||||
struct lthread_stack *_stack_alloc(void)
|
||||
{
|
||||
struct lthread_stack *s;
|
||||
|
||||
s = _lthread_objcache_alloc((THIS_SCHED)->stack_cache);
|
||||
RTE_ASSERT(s != NULL);
|
||||
|
||||
s->root_sched = THIS_SCHED;
|
||||
s->stack_size = LTHREAD_MAX_STACK_SIZE;
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute a ctx by invoking the start function
|
||||
* On return call an exit handler if the user has provided one
|
||||
*/
|
||||
static void _lthread_exec(void *arg)
|
||||
{
|
||||
struct lthread *lt = (struct lthread *)arg;
|
||||
|
||||
/* invoke the contexts function */
|
||||
lt->fun(lt->arg);
|
||||
/* do exit handling */
|
||||
if (lt->exit_handler != NULL)
|
||||
lt->exit_handler(lt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an lthread
|
||||
* Set its function, args, and exit handler
|
||||
*/
|
||||
void
|
||||
_lthread_init(struct lthread *lt,
|
||||
lthread_func_t fun, void *arg, lthread_exit_func exit_handler)
|
||||
{
|
||||
|
||||
/* set ctx func and args */
|
||||
lt->fun = fun;
|
||||
lt->arg = arg;
|
||||
lt->exit_handler = exit_handler;
|
||||
|
||||
/* set initial state */
|
||||
lt->birth = _sched_now();
|
||||
lt->state = BIT(ST_LT_INIT);
|
||||
lt->join = LT_JOIN_INITIAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the lthread stack
|
||||
*/
|
||||
void _lthread_set_stack(struct lthread *lt, void *stack, size_t stack_size)
|
||||
{
|
||||
/* set stack */
|
||||
lt->stack = stack;
|
||||
lt->stack_size = stack_size;
|
||||
|
||||
arch_set_stack(lt, _lthread_exec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an lthread on the current scheduler
|
||||
* If there is no current scheduler on this pthread then first create one
|
||||
*/
|
||||
int
|
||||
lthread_create(struct lthread **new_lt, int lcore_id,
|
||||
lthread_func_t fun, void *arg)
|
||||
{
|
||||
if ((new_lt == NULL) || (fun == NULL))
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
if (lcore_id < 0)
|
||||
lcore_id = rte_lcore_id();
|
||||
else if (lcore_id > LTHREAD_MAX_LCORES)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
struct lthread *lt = NULL;
|
||||
|
||||
if (THIS_SCHED == NULL) {
|
||||
THIS_SCHED = _lthread_sched_create(0);
|
||||
if (THIS_SCHED == NULL) {
|
||||
perror("Failed to create scheduler");
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate a thread structure */
|
||||
lt = _lthread_objcache_alloc((THIS_SCHED)->lthread_cache);
|
||||
if (lt == NULL)
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
|
||||
bzero(lt, sizeof(struct lthread));
|
||||
lt->root_sched = THIS_SCHED;
|
||||
|
||||
/* set the function args and exit handler */
|
||||
_lthread_init(lt, fun, arg, _lthread_exit_handler);
|
||||
|
||||
/* put it in the ready queue */
|
||||
*new_lt = lt;
|
||||
|
||||
if (lcore_id < 0)
|
||||
lcore_id = rte_lcore_id();
|
||||
|
||||
DIAG_CREATE_EVENT(lt, LT_DIAG_LTHREAD_CREATE);
|
||||
|
||||
rte_wmb();
|
||||
_ready_queue_insert(_lthread_sched_get(lcore_id), lt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedules lthread to sleep for `nsecs`
|
||||
* setting the lthread state to LT_ST_SLEEPING.
|
||||
* lthread state is cleared upon resumption or expiry.
|
||||
*/
|
||||
static inline void _lthread_sched_sleep(struct lthread *lt, uint64_t nsecs)
|
||||
{
|
||||
uint64_t state = lt->state;
|
||||
uint64_t clks = _ns_to_clks(nsecs);
|
||||
|
||||
if (clks) {
|
||||
_timer_start(lt, clks);
|
||||
lt->state = state | BIT(ST_LT_SLEEPING);
|
||||
}
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0);
|
||||
_suspend();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Cancels any running timer.
|
||||
* This can be called multiple times on the same lthread regardless if it was
|
||||
* sleeping or not.
|
||||
*/
|
||||
int _lthread_desched_sleep(struct lthread *lt)
|
||||
{
|
||||
uint64_t state = lt->state;
|
||||
|
||||
if (state & BIT(ST_LT_SLEEPING)) {
|
||||
_timer_stop(lt);
|
||||
state &= (CLEARBIT(ST_LT_SLEEPING) & CLEARBIT(ST_LT_EXPIRED));
|
||||
lt->state = state | BIT(ST_LT_READY);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* set user data pointer in an lthread
|
||||
*/
|
||||
void lthread_set_data(void *data)
|
||||
{
|
||||
if (sizeof(void *) == RTE_PER_LTHREAD_SECTION_SIZE)
|
||||
THIS_LTHREAD->per_lthread_data = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve user data pointer from an lthread
|
||||
*/
|
||||
void *lthread_get_data(void)
|
||||
{
|
||||
return THIS_LTHREAD->per_lthread_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current lthread handle
|
||||
*/
|
||||
struct lthread *lthread_current(void)
|
||||
{
|
||||
struct lthread_sched *sched = THIS_SCHED;
|
||||
|
||||
if (sched)
|
||||
return sched->current_lthread;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Tasklet to cancel a thread
|
||||
*/
|
||||
static void *
|
||||
_cancel(void *arg)
|
||||
{
|
||||
struct lthread *lt = (struct lthread *) arg;
|
||||
|
||||
lt->state |= BIT(ST_LT_CANCELLED);
|
||||
lthread_detach();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Mark the specified as canceled
|
||||
*/
|
||||
int lthread_cancel(struct lthread *cancel_lt)
|
||||
{
|
||||
struct lthread *lt;
|
||||
|
||||
if ((cancel_lt == NULL) || (cancel_lt == THIS_LTHREAD))
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
DIAG_EVENT(cancel_lt, LT_DIAG_LTHREAD_CANCEL, cancel_lt, 0);
|
||||
|
||||
if (cancel_lt->sched != THIS_SCHED) {
|
||||
|
||||
/* spawn task-let to cancel the thread */
|
||||
lthread_create(<,
|
||||
cancel_lt->sched->lcore_id,
|
||||
_cancel,
|
||||
cancel_lt);
|
||||
return 0;
|
||||
}
|
||||
cancel_lt->state |= BIT(ST_LT_CANCELLED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend the current lthread for specified time
|
||||
*/
|
||||
void lthread_sleep(uint64_t nsecs)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
_lthread_sched_sleep(lt, nsecs);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend the current lthread for specified time
|
||||
*/
|
||||
void lthread_sleep_clks(uint64_t clks)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
uint64_t state = lt->state;
|
||||
|
||||
if (clks) {
|
||||
_timer_start(lt, clks);
|
||||
lt->state = state | BIT(ST_LT_SLEEPING);
|
||||
}
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0);
|
||||
_suspend();
|
||||
}
|
||||
|
||||
/*
|
||||
* Requeue the current thread to the back of the ready queue
|
||||
*/
|
||||
void lthread_yield(void)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_YIELD, 0, 0);
|
||||
|
||||
_ready_queue_insert(THIS_SCHED, lt);
|
||||
ctx_switch(&(THIS_SCHED)->ctx, <->ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit the current lthread
|
||||
* If a thread is joining pass the user pointer to it
|
||||
*/
|
||||
void lthread_exit(void *ptr)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
/* if thread is detached (this is not valid) just exit */
|
||||
if (lt->state & BIT(ST_LT_DETACH))
|
||||
return;
|
||||
|
||||
/* There is a race between lthread_join() and lthread_exit()
|
||||
* - if exit before join then we suspend and resume on join
|
||||
* - if join before exit then we resume the joining thread
|
||||
*/
|
||||
uint64_t join_initial = LT_JOIN_INITIAL;
|
||||
if ((lt->join == LT_JOIN_INITIAL)
|
||||
&& __atomic_compare_exchange_n(<->join, &join_initial,
|
||||
LT_JOIN_EXITING, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 1, 0);
|
||||
_suspend();
|
||||
/* set the exit value */
|
||||
if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL))
|
||||
*(lt->lt_join->lt_exit_ptr) = ptr;
|
||||
|
||||
/* let the joining thread know we have set the exit value */
|
||||
lt->join = LT_JOIN_EXIT_VAL_SET;
|
||||
} else {
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 0, 0);
|
||||
/* set the exit value */
|
||||
if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL))
|
||||
*(lt->lt_join->lt_exit_ptr) = ptr;
|
||||
/* let the joining thread know we have set the exit value */
|
||||
lt->join = LT_JOIN_EXIT_VAL_SET;
|
||||
_ready_queue_insert(lt->lt_join->sched,
|
||||
(struct lthread *)lt->lt_join);
|
||||
}
|
||||
|
||||
|
||||
/* wait until the joining thread has collected the exit value */
|
||||
while (lt->join != LT_JOIN_EXIT_VAL_READ)
|
||||
_reschedule();
|
||||
|
||||
/* reset join state */
|
||||
lt->join = LT_JOIN_INITIAL;
|
||||
|
||||
/* detach it so its resources can be released */
|
||||
lt->state |= (BIT(ST_LT_DETACH) | BIT(ST_LT_EXITED));
|
||||
}
|
||||
|
||||
/*
|
||||
* Join an lthread
|
||||
* Suspend until the joined thread returns
|
||||
*/
|
||||
int lthread_join(struct lthread *lt, void **ptr)
|
||||
{
|
||||
if (lt == NULL)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
struct lthread *current = THIS_LTHREAD;
|
||||
uint64_t lt_state = lt->state;
|
||||
|
||||
/* invalid to join a detached thread, or a thread that is joined */
|
||||
if ((lt_state & BIT(ST_LT_DETACH)) || (lt->join == LT_JOIN_THREAD_SET))
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
/* pointer to the joining thread and a pointer to return a value */
|
||||
lt->lt_join = current;
|
||||
current->lt_exit_ptr = ptr;
|
||||
/* There is a race between lthread_join() and lthread_exit()
|
||||
* - if join before exit we suspend and will resume when exit is called
|
||||
* - if exit before join we resume the exiting thread
|
||||
*/
|
||||
uint64_t join_initial = LT_JOIN_INITIAL;
|
||||
if ((lt->join == LT_JOIN_INITIAL)
|
||||
&& __atomic_compare_exchange_n(<->join, &join_initial,
|
||||
LT_JOIN_THREAD_SET, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
|
||||
DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 1);
|
||||
_suspend();
|
||||
} else {
|
||||
DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 0);
|
||||
_ready_queue_insert(lt->sched, lt);
|
||||
}
|
||||
|
||||
/* wait for exiting thread to set return value */
|
||||
while (lt->join != LT_JOIN_EXIT_VAL_SET)
|
||||
_reschedule();
|
||||
|
||||
/* collect the return value */
|
||||
if (ptr != NULL)
|
||||
*ptr = *current->lt_exit_ptr;
|
||||
|
||||
/* let the exiting thread proceed to exit */
|
||||
lt->join = LT_JOIN_EXIT_VAL_READ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Detach current lthread
|
||||
* A detached thread cannot be joined
|
||||
*/
|
||||
void lthread_detach(void)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_DETACH, 0, 0);
|
||||
|
||||
uint64_t state = lt->state;
|
||||
|
||||
lt->state = state | BIT(ST_LT_DETACH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set function name of an lthread
|
||||
* this is a debug aid
|
||||
*/
|
||||
void lthread_set_funcname(const char *f)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
strlcpy(lt->funcname, f, sizeof(lt->funcname));
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
#ifndef LTHREAD_H_
|
||||
#define LTHREAD_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <rte_per_lcore.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread_diag.h"
|
||||
|
||||
struct lthread;
|
||||
struct lthread_sched;
|
||||
|
||||
/* function to be called when a context function returns */
|
||||
typedef void (*lthread_exit_func) (struct lthread *);
|
||||
|
||||
void _lthread_exit_handler(struct lthread *lt);
|
||||
|
||||
void lthread_set_funcname(const char *f);
|
||||
|
||||
void _lthread_sched_busy_sleep(struct lthread *lt, uint64_t nsecs);
|
||||
|
||||
int _lthread_desched_sleep(struct lthread *lt);
|
||||
|
||||
void _lthread_free(struct lthread *lt);
|
||||
|
||||
struct lthread_sched *_lthread_sched_get(unsigned int lcore_id);
|
||||
|
||||
struct lthread_stack *_stack_alloc(void);
|
||||
|
||||
struct
|
||||
lthread_sched *_lthread_sched_create(size_t stack_size);
|
||||
|
||||
void
|
||||
_lthread_init(struct lthread *lt,
|
||||
lthread_func_t fun, void *arg, lthread_exit_func exit_handler);
|
||||
|
||||
void _lthread_set_stack(struct lthread *lt, void *stack, size_t stack_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_H_ */
|
@ -1,784 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
/**
|
||||
* @file lthread_api.h
|
||||
*
|
||||
* @warning
|
||||
* @b EXPERIMENTAL: this API may change without prior notice
|
||||
*
|
||||
* This file contains the public API for the L-thread subsystem
|
||||
*
|
||||
* The L_thread subsystem provides a simple cooperative scheduler to
|
||||
* enable arbitrary functions to run as cooperative threads within a
|
||||
* single P-thread.
|
||||
*
|
||||
* The subsystem provides a P-thread like API that is intended to assist in
|
||||
* reuse of legacy code written for POSIX p_threads.
|
||||
*
|
||||
* The L-thread subsystem relies on cooperative multitasking, as such
|
||||
* an L-thread must possess frequent rescheduling points. Often these
|
||||
* rescheduling points are provided transparently when the application
|
||||
* invokes an L-thread API.
|
||||
*
|
||||
* In some applications it is possible that the program may enter a loop the
|
||||
* exit condition for which depends on the action of another thread or a
|
||||
* response from hardware. In such a case it is necessary to yield the thread
|
||||
* periodically in the loop body, to allow other threads an opportunity to
|
||||
* run. This can be done by inserting a call to lthread_yield() or
|
||||
* lthread_sleep(n) in the body of the loop.
|
||||
*
|
||||
* If the application makes expensive / blocking system calls or does other
|
||||
* work that would take an inordinate amount of time to complete, this will
|
||||
* stall the cooperative scheduler resulting in very poor performance.
|
||||
*
|
||||
* In such cases an L-thread can be migrated temporarily to another scheduler
|
||||
* running in a different P-thread on another core. When the expensive or
|
||||
* blocking operation is completed it can be migrated back to the original
|
||||
* scheduler. In this way other threads can continue to run on the original
|
||||
* scheduler and will be completely unaffected by the blocking behaviour.
|
||||
* To migrate an L-thread to another scheduler the API lthread_set_affinity()
|
||||
* is provided.
|
||||
*
|
||||
* If L-threads that share data are running on the same core it is possible
|
||||
* to design programs where mutual exclusion mechanisms to protect shared data
|
||||
* can be avoided. This is due to the fact that the cooperative threads cannot
|
||||
* preempt each other.
|
||||
*
|
||||
* There are two cases where mutual exclusion mechanisms are necessary.
|
||||
*
|
||||
* a) Where the L-threads sharing data are running on different cores.
|
||||
* b) Where code must yield while updating data shared with another thread.
|
||||
*
|
||||
* The L-thread subsystem provides a set of mutex APIs to help with such
|
||||
* scenarios, however excessive reliance on on these will impact performance
|
||||
* and is best avoided if possible.
|
||||
*
|
||||
* L-threads can synchronise using a fast condition variable implementation
|
||||
* that supports signal and broadcast. An L-thread running on any core can
|
||||
* wait on a condition.
|
||||
*
|
||||
* L-threads can have L-thread local storage with an API modelled on either the
|
||||
* P-thread get/set specific API or using PER_LTHREAD macros modelled on the
|
||||
* RTE_PER_LCORE macros. Alternatively a simple user data pointer may be set
|
||||
* and retrieved from a thread.
|
||||
*/
|
||||
#ifndef LTHREAD_H
|
||||
#define LTHREAD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <rte_cycles.h>
|
||||
|
||||
|
||||
struct lthread;
|
||||
struct lthread_cond;
|
||||
struct lthread_mutex;
|
||||
|
||||
struct lthread_condattr;
|
||||
struct lthread_mutexattr;
|
||||
|
||||
typedef void *(*lthread_func_t) (void *);
|
||||
|
||||
/*
|
||||
* Define the size of stack for an lthread
|
||||
* Then this is the size that will be allocated on lthread creation
|
||||
* This is a fixed size and will not grow.
|
||||
*/
|
||||
#define LTHREAD_MAX_STACK_SIZE (1024*64)
|
||||
|
||||
/**
|
||||
* Define the maximum number of TLS keys that can be created
|
||||
*
|
||||
*/
|
||||
#define LTHREAD_MAX_KEYS 1024
|
||||
|
||||
/**
|
||||
* Define the maximum number of attempts to destroy an lthread's
|
||||
* TLS data on thread exit
|
||||
*/
|
||||
#define LTHREAD_DESTRUCTOR_ITERATIONS 4
|
||||
|
||||
|
||||
/**
|
||||
* Define the maximum number of lcores that will support lthreads
|
||||
*/
|
||||
#define LTHREAD_MAX_LCORES RTE_MAX_LCORE
|
||||
|
||||
/**
|
||||
* How many lthread objects to pre-allocate as the system grows
|
||||
* applies to lthreads + stacks, TLS, mutexs, cond vars.
|
||||
*
|
||||
* @see _lthread_alloc()
|
||||
* @see _cond_alloc()
|
||||
* @see _mutex_alloc()
|
||||
*
|
||||
*/
|
||||
#define LTHREAD_PREALLOC 100
|
||||
|
||||
/**
|
||||
* Set the number of schedulers in the system.
|
||||
*
|
||||
* This function may optionally be called before starting schedulers.
|
||||
*
|
||||
* If the number of schedulers is not set, or set to 0 then each scheduler
|
||||
* will begin scheduling lthreads immediately it is started.
|
||||
|
||||
* If the number of schedulers is set to greater than 0, then each scheduler
|
||||
* will wait until all schedulers have started before beginning to schedule
|
||||
* lthreads.
|
||||
*
|
||||
* If an application wishes to have threads migrate between cores using
|
||||
* lthread_set_affinity(), or join threads running on other cores using
|
||||
* lthread_join(), then it is prudent to set the number of schedulers to ensure
|
||||
* that all schedulers are initialised beforehand.
|
||||
*
|
||||
* @param num
|
||||
* the number of schedulers in the system
|
||||
* @return
|
||||
* the number of schedulers in the system
|
||||
*/
|
||||
int lthread_num_schedulers_set(int num);
|
||||
|
||||
/**
|
||||
* Return the number of schedulers currently running
|
||||
* @return
|
||||
* the number of schedulers in the system
|
||||
*/
|
||||
int lthread_active_schedulers(void);
|
||||
|
||||
/**
|
||||
* Shutdown the specified scheduler
|
||||
*
|
||||
* This function tells the specified scheduler to
|
||||
* exit if/when there is no more work to do.
|
||||
*
|
||||
* Note that although the scheduler will stop
|
||||
* resources are not freed.
|
||||
*
|
||||
* @param lcore
|
||||
* The lcore of the scheduler to shutdown
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_scheduler_shutdown(unsigned lcore);
|
||||
|
||||
/**
|
||||
* Shutdown all schedulers
|
||||
*
|
||||
* This function tells all schedulers including the current scheduler to
|
||||
* exit if/when there is no more work to do.
|
||||
*
|
||||
* Note that although the schedulers will stop
|
||||
* resources are not freed.
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_scheduler_shutdown_all(void);
|
||||
|
||||
/**
|
||||
* Run the lthread scheduler
|
||||
*
|
||||
* Runs the lthread scheduler.
|
||||
* This function returns only if/when all lthreads have exited.
|
||||
* This function must be the main loop of an EAL thread.
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
|
||||
void lthread_run(void);
|
||||
|
||||
/**
|
||||
* Create an lthread
|
||||
*
|
||||
* Creates an lthread and places it in the ready queue on a particular
|
||||
* lcore.
|
||||
*
|
||||
* If no scheduler exists yet on the current lcore then one is created.
|
||||
*
|
||||
* @param new_lt
|
||||
* Pointer to an lthread pointer that will be initialized
|
||||
* @param lcore
|
||||
* the lcore the thread should be started on or the current lcore
|
||||
* -1 the current lcore
|
||||
* 0 - LTHREAD_MAX_LCORES any other lcore
|
||||
* @param lthread_func
|
||||
* Pointer to the function the for the thread to run
|
||||
* @param arg
|
||||
* Pointer to args that will be passed to the thread
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EAGAIN no resources available
|
||||
* EINVAL NULL thread or function pointer, or lcore_id out of range
|
||||
*/
|
||||
int
|
||||
lthread_create(struct lthread **new_lt,
|
||||
int lcore, lthread_func_t func, void *arg);
|
||||
|
||||
/**
|
||||
* Cancel an lthread
|
||||
*
|
||||
* Cancels an lthread and causes it to be terminated
|
||||
* If the lthread is detached it will be freed immediately
|
||||
* otherwise its resources will not be released until it is joined.
|
||||
*
|
||||
* @param new_lt
|
||||
* Pointer to an lthread that will be cancelled
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL thread was NULL
|
||||
*/
|
||||
int lthread_cancel(struct lthread *lt);
|
||||
|
||||
/**
|
||||
* Join an lthread
|
||||
*
|
||||
* Joins the current thread with the specified lthread, and waits for that
|
||||
* thread to exit.
|
||||
* Passes an optional pointer to collect returned data.
|
||||
*
|
||||
* @param lt
|
||||
* Pointer to the lthread to be joined
|
||||
* @param ptr
|
||||
* Pointer to pointer to collect returned data
|
||||
*
|
||||
0 * @return
|
||||
* 0 success
|
||||
* EINVAL lthread could not be joined.
|
||||
*/
|
||||
int lthread_join(struct lthread *lt, void **ptr);
|
||||
|
||||
/**
|
||||
* Detach an lthread
|
||||
*
|
||||
* Detaches the current thread
|
||||
* On exit a detached lthread will be freed immediately and will not wait
|
||||
* to be joined. The default state for a thread is not detached.
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_detach(void);
|
||||
|
||||
/**
|
||||
* Exit an lthread
|
||||
*
|
||||
* Terminate the current thread, optionally return data.
|
||||
* The data may be collected by lthread_join()
|
||||
*
|
||||
* After calling this function the lthread will be suspended until it is
|
||||
* joined. After it is joined then its resources will be freed.
|
||||
*
|
||||
* @param ptr
|
||||
* Pointer to pointer to data to be returned
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_exit(void *val);
|
||||
|
||||
/**
|
||||
* Cause the current lthread to sleep for n nanoseconds
|
||||
*
|
||||
* The current thread will be suspended until the specified time has elapsed
|
||||
* or has been exceeded.
|
||||
*
|
||||
* Execution will switch to the next lthread that is ready to run
|
||||
*
|
||||
* @param nsecs
|
||||
* Number of nanoseconds to sleep
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_sleep(uint64_t nsecs);
|
||||
|
||||
/**
|
||||
* Cause the current lthread to sleep for n cpu clock ticks
|
||||
*
|
||||
* The current thread will be suspended until the specified time has elapsed
|
||||
* or has been exceeded.
|
||||
*
|
||||
* Execution will switch to the next lthread that is ready to run
|
||||
*
|
||||
* @param clks
|
||||
* Number of clock ticks to sleep
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_sleep_clks(uint64_t clks);
|
||||
|
||||
/**
|
||||
* Yield the current lthread
|
||||
*
|
||||
* The current thread will yield and execution will switch to the
|
||||
* next lthread that is ready to run
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_yield(void);
|
||||
|
||||
/**
|
||||
* Migrate the current thread to another scheduler
|
||||
*
|
||||
* This function migrates the current thread to another scheduler.
|
||||
* Execution will switch to the next lthread that is ready to run on the
|
||||
* current scheduler. The current thread will be resumed on the new scheduler.
|
||||
*
|
||||
* @param lcore
|
||||
* The lcore to migrate to
|
||||
*
|
||||
* @return
|
||||
* 0 success we are now running on the specified core
|
||||
* EINVAL the destination lcore was not valid
|
||||
*/
|
||||
int lthread_set_affinity(unsigned lcore);
|
||||
|
||||
/**
|
||||
* Return the current lthread
|
||||
*
|
||||
* Returns the current lthread
|
||||
*
|
||||
* @return
|
||||
* pointer to the current lthread
|
||||
*/
|
||||
struct lthread
|
||||
*lthread_current(void);
|
||||
|
||||
/**
|
||||
* Associate user data with an lthread
|
||||
*
|
||||
* This function sets a user data pointer in the current lthread
|
||||
* The pointer can be retrieved with lthread_get_data()
|
||||
* It is the users responsibility to allocate and free any data referenced
|
||||
* by the user pointer.
|
||||
*
|
||||
* @param data
|
||||
* pointer to user data
|
||||
*
|
||||
* @return
|
||||
* none
|
||||
*/
|
||||
void lthread_set_data(void *data);
|
||||
|
||||
/**
|
||||
* Get user data for the current lthread
|
||||
*
|
||||
* This function returns a user data pointer for the current lthread
|
||||
* The pointer must first be set with lthread_set_data()
|
||||
* It is the users responsibility to allocate and free any data referenced
|
||||
* by the user pointer.
|
||||
*
|
||||
* @return
|
||||
* pointer to user data
|
||||
*/
|
||||
void
|
||||
*lthread_get_data(void);
|
||||
|
||||
struct lthread_key;
|
||||
typedef void (*tls_destructor_func) (void *);
|
||||
|
||||
/**
|
||||
* Create a key for lthread TLS
|
||||
*
|
||||
* This function is modelled on pthread_key_create
|
||||
* It creates a thread-specific data key visible to all lthreads on the
|
||||
* current scheduler.
|
||||
*
|
||||
* Key values may be used to locate thread-specific data.
|
||||
* The same key value may be used by different threads, the values bound
|
||||
* to the key by lthread_setspecific() are maintained on a per-thread
|
||||
* basis and persist for the life of the calling thread.
|
||||
*
|
||||
* An optional destructor function may be associated with each key value.
|
||||
* At thread exit, if a key value has a non-NULL destructor pointer, and the
|
||||
* thread has a non-NULL value associated with the key, the function pointed
|
||||
* to is called with the current associated value as its sole argument.
|
||||
*
|
||||
* @param key
|
||||
* Pointer to the key to be created
|
||||
* @param destructor
|
||||
* Pointer to destructor function
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL the key ptr was NULL
|
||||
* EAGAIN no resources available
|
||||
*/
|
||||
int lthread_key_create(unsigned int *key, tls_destructor_func destructor);
|
||||
|
||||
/**
|
||||
* Delete key for lthread TLS
|
||||
*
|
||||
* This function is modelled on pthread_key_delete().
|
||||
* It deletes a thread-specific data key previously returned by
|
||||
* lthread_key_create().
|
||||
* The thread-specific data values associated with the key need not be NULL
|
||||
* at the time that lthread_key_delete is called.
|
||||
* It is the responsibility of the application to free any application
|
||||
* storage or perform any cleanup actions for data structures related to the
|
||||
* deleted key. This cleanup can be done either before or after
|
||||
* lthread_key_delete is called.
|
||||
*
|
||||
* @param key
|
||||
* The key to be deleted
|
||||
*
|
||||
* @return
|
||||
* 0 Success
|
||||
* EINVAL the key was invalid
|
||||
*/
|
||||
int lthread_key_delete(unsigned int key);
|
||||
|
||||
/**
|
||||
* Get lthread TLS
|
||||
*
|
||||
* This function is modelled on pthread_get_specific().
|
||||
* It returns the value currently bound to the specified key on behalf of the
|
||||
* calling thread. Calling lthread_getspecific() with a key value not
|
||||
* obtained from lthread_key_create() or after key has been deleted with
|
||||
* lthread_key_delete() will result in undefined behaviour.
|
||||
* lthread_getspecific() may be called from a thread-specific data destructor
|
||||
* function.
|
||||
*
|
||||
* @param key
|
||||
* The key for which data is requested
|
||||
*
|
||||
* @return
|
||||
* Pointer to the thread specific data associated with that key
|
||||
* or NULL if no data has been set.
|
||||
*/
|
||||
void
|
||||
*lthread_getspecific(unsigned int key);
|
||||
|
||||
/**
|
||||
* Set lthread TLS
|
||||
*
|
||||
* This function is modelled on pthread_set_specific()
|
||||
* It associates a thread-specific value with a key obtained via a previous
|
||||
* call to lthread_key_create().
|
||||
* Different threads may bind different values to the same key. These values
|
||||
* are typically pointers to dynamically allocated memory that have been
|
||||
* reserved by the calling thread. Calling lthread_setspecific with a key
|
||||
* value not obtained from lthread_key_create or after the key has been
|
||||
* deleted with lthread_key_delete will result in undefined behaviour.
|
||||
*
|
||||
* @param key
|
||||
* The key for which data is to be set
|
||||
* @param key
|
||||
* Pointer to the user data
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL the key was invalid
|
||||
*/
|
||||
|
||||
int lthread_setspecific(unsigned int key, const void *value);
|
||||
|
||||
/**
|
||||
* The macros below provide an alternative mechanism to access lthread local
|
||||
* storage.
|
||||
*
|
||||
* The macros can be used to declare define and access per lthread local
|
||||
* storage in a similar way to the RTE_PER_LCORE macros which control storage
|
||||
* local to an lcore.
|
||||
*
|
||||
* Memory for per lthread variables declared in this way is allocated when the
|
||||
* lthread is created and a pointer to this memory is stored in the lthread.
|
||||
* The per lthread variables are accessed via the pointer + the offset of the
|
||||
* particular variable.
|
||||
*
|
||||
* The total size of per lthread storage, and the variable offsets are found by
|
||||
* defining the variables in a unique global memory section, the start and end
|
||||
* of which is known. This global memory section is used only in the
|
||||
* computation of the addresses of the lthread variables, and is never actually
|
||||
* used to store any data.
|
||||
*
|
||||
* Due to the fact that variables declared this way may be scattered across
|
||||
* many files, the start and end of the section and variable offsets are only
|
||||
* known after linking, thus the computation of section size and variable
|
||||
* addresses is performed at run time.
|
||||
*
|
||||
* These macros are primarily provided to aid porting of code that makes use
|
||||
* of the existing RTE_PER_LCORE macros. In principle it would be more efficient
|
||||
* to gather all lthread local variables into a single structure and
|
||||
* set/retrieve a pointer to that struct using the alternative
|
||||
* lthread_data_set/get APIs.
|
||||
*
|
||||
* These macros are mutually exclusive with the lthread_data_set/get APIs.
|
||||
* If you define storage using these macros then the lthread_data_set/get APIs
|
||||
* will not perform as expected, the lthread_data_set API does nothing, and the
|
||||
* lthread_data_get API returns the start of global section.
|
||||
*
|
||||
*/
|
||||
/* start and end of per lthread section */
|
||||
extern char __start_per_lt;
|
||||
extern char __stop_per_lt;
|
||||
|
||||
|
||||
#define RTE_DEFINE_PER_LTHREAD(type, name) \
|
||||
__typeof__(type)__attribute((section("per_lt"))) per_lt_##name
|
||||
|
||||
/**
|
||||
* Macro to declare an extern per lthread variable "var" of type "type"
|
||||
*/
|
||||
#define RTE_DECLARE_PER_LTHREAD(type, name) \
|
||||
extern __typeof__(type)__attribute((section("per_lt"))) per_lt_##name
|
||||
|
||||
/**
|
||||
* Read/write the per-lcore variable value
|
||||
*/
|
||||
#define RTE_PER_LTHREAD(name) ((typeof(per_lt_##name) *)\
|
||||
((char *)lthread_get_data() +\
|
||||
((char *) &per_lt_##name - &__start_per_lt)))
|
||||
|
||||
/**
|
||||
* Initialize a mutex
|
||||
*
|
||||
* This function provides a mutual exclusion device, the need for which
|
||||
* can normally be avoided in a cooperative multitasking environment.
|
||||
* It is provided to aid porting of legacy code originally written for
|
||||
* preemptive multitasking environments such as pthreads.
|
||||
*
|
||||
* A mutex may be unlocked (not owned by any thread), or locked (owned by
|
||||
* one thread).
|
||||
*
|
||||
* A mutex can never be owned by more than one thread simultaneously.
|
||||
* A thread attempting to lock a mutex that is already locked by another
|
||||
* thread is suspended until the owning thread unlocks the mutex.
|
||||
*
|
||||
* lthread_mutex_init() initializes the mutex object pointed to by mutex
|
||||
* Optional mutex attributes specified in mutexattr, are reserved for future
|
||||
* use and are currently ignored.
|
||||
*
|
||||
* If a thread calls lthread_mutex_lock() on the mutex, then if the mutex
|
||||
* is currently unlocked, it becomes locked and owned by the calling
|
||||
* thread, and lthread_mutex_lock returns immediately. If the mutex is
|
||||
* already locked by another thread, lthread_mutex_lock suspends the calling
|
||||
* thread until the mutex is unlocked.
|
||||
*
|
||||
* lthread_mutex_trylock behaves identically to rte_thread_mutex_lock, except
|
||||
* that it does not block the calling thread if the mutex is already locked
|
||||
* by another thread.
|
||||
*
|
||||
* lthread_mutex_unlock() unlocks the specified mutex. The mutex is assumed
|
||||
* to be locked and owned by the calling thread.
|
||||
*
|
||||
* lthread_mutex_destroy() destroys a mutex object, freeing its resources.
|
||||
* The mutex must be unlocked with nothing blocked on it before calling
|
||||
* lthread_mutex_destroy.
|
||||
*
|
||||
* @param name
|
||||
* Optional pointer to string describing the mutex
|
||||
* @param mutex
|
||||
* Pointer to pointer to the mutex to be initialized
|
||||
* @param attribute
|
||||
* Pointer to attribute - unused reserved
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL mutex was not a valid pointer
|
||||
* EAGAIN insufficient resources
|
||||
*/
|
||||
|
||||
int
|
||||
lthread_mutex_init(char *name, struct lthread_mutex **mutex,
|
||||
const struct lthread_mutexattr *attr);
|
||||
|
||||
/**
|
||||
* Destroy a mutex
|
||||
*
|
||||
* This function destroys the specified mutex freeing its resources.
|
||||
* The mutex must be unlocked before calling lthread_mutex_destroy.
|
||||
*
|
||||
* @see lthread_mutex_init()
|
||||
*
|
||||
* @param mutex
|
||||
* Pointer to pointer to the mutex to be initialized
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL mutex was not an initialized mutex
|
||||
* EBUSY mutex was still in use
|
||||
*/
|
||||
int lthread_mutex_destroy(struct lthread_mutex *mutex);
|
||||
|
||||
/**
|
||||
* Lock a mutex
|
||||
*
|
||||
* This function attempts to lock a mutex.
|
||||
* If a thread calls lthread_mutex_lock() on the mutex, then if the mutex
|
||||
* is currently unlocked, it becomes locked and owned by the calling
|
||||
* thread, and lthread_mutex_lock returns immediately. If the mutex is
|
||||
* already locked by another thread, lthread_mutex_lock suspends the calling
|
||||
* thread until the mutex is unlocked.
|
||||
*
|
||||
* @see lthread_mutex_init()
|
||||
*
|
||||
* @param mutex
|
||||
* Pointer to pointer to the mutex to be initialized
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL mutex was not an initialized mutex
|
||||
* EDEADLOCK the mutex was already owned by the calling thread
|
||||
*/
|
||||
|
||||
int lthread_mutex_lock(struct lthread_mutex *mutex);
|
||||
|
||||
/**
|
||||
* Try to lock a mutex
|
||||
*
|
||||
* This function attempts to lock a mutex.
|
||||
* lthread_mutex_trylock behaves identically to rte_thread_mutex_lock, except
|
||||
* that it does not block the calling thread if the mutex is already locked
|
||||
* by another thread.
|
||||
*
|
||||
*
|
||||
* @see lthread_mutex_init()
|
||||
*
|
||||
* @param mutex
|
||||
* Pointer to pointer to the mutex to be initialized
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL mutex was not an initialized mutex
|
||||
* EBUSY the mutex was already locked by another thread
|
||||
*/
|
||||
int lthread_mutex_trylock(struct lthread_mutex *mutex);
|
||||
|
||||
/**
|
||||
* Unlock a mutex
|
||||
*
|
||||
* This function attempts to unlock the specified mutex. The mutex is assumed
|
||||
* to be locked and owned by the calling thread.
|
||||
*
|
||||
* The oldest of any threads blocked on the mutex is made ready and may
|
||||
* compete with any other running thread to gain the mutex, it fails it will
|
||||
* be blocked again.
|
||||
*
|
||||
* @param mutex
|
||||
* Pointer to pointer to the mutex to be initialized
|
||||
*
|
||||
* @return
|
||||
* 0 mutex was unlocked
|
||||
* EINVAL mutex was not an initialized mutex
|
||||
* EPERM the mutex was not owned by the calling thread
|
||||
*/
|
||||
|
||||
int lthread_mutex_unlock(struct lthread_mutex *mutex);
|
||||
|
||||
/**
|
||||
* Initialize a condition variable
|
||||
*
|
||||
* This function initializes a condition variable.
|
||||
*
|
||||
* Condition variables can be used to communicate changes in the state of data
|
||||
* shared between threads.
|
||||
*
|
||||
* @see lthread_cond_wait()
|
||||
*
|
||||
* @param name
|
||||
* Pointer to optional string describing the condition variable
|
||||
* @param c
|
||||
* Pointer to pointer to the condition variable to be initialized
|
||||
* @param attr
|
||||
* Pointer to optional attribute reserved for future use, currently ignored
|
||||
*
|
||||
* @return
|
||||
* 0 success
|
||||
* EINVAL cond was not a valid pointer
|
||||
* EAGAIN insufficient resources
|
||||
*/
|
||||
int
|
||||
lthread_cond_init(char *name, struct lthread_cond **c,
|
||||
const struct lthread_condattr *attr);
|
||||
|
||||
/**
|
||||
* Destroy a condition variable
|
||||
*
|
||||
* This function destroys a condition variable that was created with
|
||||
* lthread_cond_init() and releases its resources.
|
||||
*
|
||||
* @param cond
|
||||
* Pointer to pointer to the condition variable to be destroyed
|
||||
*
|
||||
* @return
|
||||
* 0 Success
|
||||
* EBUSY condition variable was still in use
|
||||
* EINVAL was not an initialised condition variable
|
||||
*/
|
||||
int lthread_cond_destroy(struct lthread_cond *cond);
|
||||
|
||||
/**
|
||||
* Wait on a condition variable
|
||||
*
|
||||
* The function blocks the current thread waiting on the condition variable
|
||||
* specified by cond. The waiting thread unblocks only after another thread
|
||||
* calls lthread_cond_signal, or lthread_cond_broadcast, specifying the
|
||||
* same condition variable.
|
||||
*
|
||||
* @param cond
|
||||
* Pointer to pointer to the condition variable to be waited on
|
||||
*
|
||||
* @param reserved
|
||||
* reserved for future use
|
||||
*
|
||||
* @return
|
||||
* 0 The condition was signalled ( Success )
|
||||
* EINVAL was not a an initialised condition variable
|
||||
*/
|
||||
int lthread_cond_wait(struct lthread_cond *c, uint64_t reserved);
|
||||
|
||||
/**
|
||||
* Signal a condition variable
|
||||
*
|
||||
* The function unblocks one thread waiting for the condition variable cond.
|
||||
* If no threads are waiting on cond, the rte_lthread_cond_signal() function
|
||||
* has no effect.
|
||||
*
|
||||
* @param cond
|
||||
* Pointer to pointer to the condition variable to be signalled
|
||||
*
|
||||
* @return
|
||||
* 0 The condition was signalled ( Success )
|
||||
* EINVAL was not a an initialised condition variable
|
||||
*/
|
||||
int lthread_cond_signal(struct lthread_cond *c);
|
||||
|
||||
/**
|
||||
* Broadcast a condition variable
|
||||
*
|
||||
* The function unblocks all threads waiting for the condition variable cond.
|
||||
* If no threads are waiting on cond, the rte_lathed_cond_broadcast()
|
||||
* function has no effect.
|
||||
*
|
||||
* @param cond
|
||||
* Pointer to pointer to the condition variable to be signalled
|
||||
*
|
||||
* @return
|
||||
* 0 The condition was signalled ( Success )
|
||||
* EINVAL was not a an initialised condition variable
|
||||
*/
|
||||
int lthread_cond_broadcast(struct lthread_cond *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_H */
|
@ -1,184 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <rte_log.h>
|
||||
#include <rte_common.h>
|
||||
#include <rte_string_fns.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread_diag_api.h"
|
||||
#include "lthread_diag.h"
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_sched.h"
|
||||
#include "lthread_queue.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_timer.h"
|
||||
#include "lthread_mutex.h"
|
||||
#include "lthread_cond.h"
|
||||
|
||||
/*
|
||||
* Create a condition variable
|
||||
*/
|
||||
int
|
||||
lthread_cond_init(char *name, struct lthread_cond **cond,
|
||||
__rte_unused const struct lthread_condattr *attr)
|
||||
{
|
||||
struct lthread_cond *c;
|
||||
|
||||
if (cond == NULL)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
/* allocate a condition variable from cache */
|
||||
c = _lthread_objcache_alloc((THIS_SCHED)->cond_cache);
|
||||
|
||||
if (c == NULL)
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
|
||||
c->blocked = _lthread_queue_create("blocked");
|
||||
if (c->blocked == NULL) {
|
||||
_lthread_objcache_free((THIS_SCHED)->cond_cache, (void *)c);
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
strlcpy(c->name, "no name", sizeof(c->name));
|
||||
else
|
||||
strlcpy(c->name, name, sizeof(c->name));
|
||||
|
||||
c->root_sched = THIS_SCHED;
|
||||
|
||||
(*cond) = c;
|
||||
DIAG_CREATE_EVENT((*cond), LT_DIAG_COND_CREATE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a condition variable
|
||||
*/
|
||||
int lthread_cond_destroy(struct lthread_cond *c)
|
||||
{
|
||||
if (c == NULL) {
|
||||
DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
/* try to free it */
|
||||
if (_lthread_queue_destroy(c->blocked) < 0) {
|
||||
/* queue in use */
|
||||
DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, POSIX_ERRNO(EBUSY));
|
||||
return POSIX_ERRNO(EBUSY);
|
||||
}
|
||||
|
||||
/* okay free it */
|
||||
_lthread_objcache_free(c->root_sched->cond_cache, c);
|
||||
DIAG_EVENT(c, LT_DIAG_COND_DESTROY, c, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait on a condition variable
|
||||
*/
|
||||
int lthread_cond_wait(struct lthread_cond *c, __rte_unused uint64_t reserved)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
if (c == NULL) {
|
||||
DIAG_EVENT(c, LT_DIAG_COND_WAIT, c, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
|
||||
DIAG_EVENT(c, LT_DIAG_COND_WAIT, c, 0);
|
||||
|
||||
/* queue the current thread in the blocked queue
|
||||
* this will be written when we return to the scheduler
|
||||
* to ensure that the current thread context is saved
|
||||
* before any signal could result in it being dequeued and
|
||||
* resumed
|
||||
*/
|
||||
lt->pending_wr_queue = c->blocked;
|
||||
_suspend();
|
||||
|
||||
/* the condition happened */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal a condition variable
|
||||
* attempt to resume any blocked thread
|
||||
*/
|
||||
int lthread_cond_signal(struct lthread_cond *c)
|
||||
{
|
||||
struct lthread *lt;
|
||||
|
||||
if (c == NULL) {
|
||||
DIAG_EVENT(c, LT_DIAG_COND_SIGNAL, c, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
lt = _lthread_queue_remove(c->blocked);
|
||||
|
||||
if (lt != NULL) {
|
||||
/* okay wake up this thread */
|
||||
DIAG_EVENT(c, LT_DIAG_COND_SIGNAL, c, lt);
|
||||
_ready_queue_insert((struct lthread_sched *)lt->sched, lt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadcast a condition variable
|
||||
*/
|
||||
int lthread_cond_broadcast(struct lthread_cond *c)
|
||||
{
|
||||
struct lthread *lt;
|
||||
|
||||
if (c == NULL) {
|
||||
DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, 0);
|
||||
do {
|
||||
/* drain the queue waking everybody */
|
||||
lt = _lthread_queue_remove(c->blocked);
|
||||
|
||||
if (lt != NULL) {
|
||||
DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, lt);
|
||||
/* wake up */
|
||||
_ready_queue_insert((struct lthread_sched *)lt->sched,
|
||||
lt);
|
||||
}
|
||||
} while (!_lthread_queue_empty(c->blocked));
|
||||
_reschedule();
|
||||
DIAG_EVENT(c, LT_DIAG_COND_BROADCAST, c, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the diagnostic ref val stored in a condition var
|
||||
*/
|
||||
uint64_t
|
||||
lthread_cond_diag_ref(struct lthread_cond *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
return 0;
|
||||
return c->diag_ref;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef LTHREAD_COND_H_
|
||||
#define LTHREAD_COND_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_queue.h"
|
||||
|
||||
#define MAX_COND_NAME_SIZE 64
|
||||
|
||||
struct lthread_cond {
|
||||
struct lthread_queue *blocked;
|
||||
struct lthread_sched *root_sched;
|
||||
int count;
|
||||
char name[MAX_COND_NAME_SIZE];
|
||||
uint64_t diag_ref; /* optional ref to user diag data */
|
||||
} __rte_cache_aligned;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_COND_H_ */
|
@ -1,293 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <rte_log.h>
|
||||
#include <rte_common.h>
|
||||
|
||||
#include "lthread_diag.h"
|
||||
#include "lthread_queue.h"
|
||||
#include "lthread_pool.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_sched.h"
|
||||
#include "lthread_diag_api.h"
|
||||
|
||||
|
||||
/* dummy ref value of default diagnostic callback */
|
||||
static uint64_t dummy_ref;
|
||||
|
||||
#define DIAG_SCHED_STATS_FORMAT \
|
||||
"core %d\n%33s %12s %12s %12s %12s\n"
|
||||
|
||||
#define DIAG_CACHE_STATS_FORMAT \
|
||||
"%20s %12lu %12lu %12lu %12lu %12lu\n"
|
||||
|
||||
#define DIAG_QUEUE_STATS_FORMAT \
|
||||
"%20s %12lu %12lu %12lu\n"
|
||||
|
||||
|
||||
/*
|
||||
* texts used in diagnostic events,
|
||||
* corresponding diagnostic mask bit positions are given as comment
|
||||
*/
|
||||
const char *diag_event_text[] = {
|
||||
"LTHREAD_CREATE ", /* 00 */
|
||||
"LTHREAD_EXIT ", /* 01 */
|
||||
"LTHREAD_JOIN ", /* 02 */
|
||||
"LTHREAD_CANCEL ", /* 03 */
|
||||
"LTHREAD_DETACH ", /* 04 */
|
||||
"LTHREAD_FREE ", /* 05 */
|
||||
"LTHREAD_SUSPENDED ", /* 06 */
|
||||
"LTHREAD_YIELD ", /* 07 */
|
||||
"LTHREAD_RESCHEDULED", /* 08 */
|
||||
"LTHREAD_SLEEP ", /* 09 */
|
||||
"LTHREAD_RESUMED ", /* 10 */
|
||||
"LTHREAD_AFFINITY ", /* 11 */
|
||||
"LTHREAD_TMR_START ", /* 12 */
|
||||
"LTHREAD_TMR_DELETE ", /* 13 */
|
||||
"LTHREAD_TMR_EXPIRED", /* 14 */
|
||||
"COND_CREATE ", /* 15 */
|
||||
"COND_DESTROY ", /* 16 */
|
||||
"COND_WAIT ", /* 17 */
|
||||
"COND_SIGNAL ", /* 18 */
|
||||
"COND_BROADCAST ", /* 19 */
|
||||
"MUTEX_CREATE ", /* 20 */
|
||||
"MUTEX_DESTROY ", /* 21 */
|
||||
"MUTEX_LOCK ", /* 22 */
|
||||
"MUTEX_TRYLOCK ", /* 23 */
|
||||
"MUTEX_BLOCKED ", /* 24 */
|
||||
"MUTEX_UNLOCKED ", /* 25 */
|
||||
"SCHED_CREATE ", /* 26 */
|
||||
"SCHED_SHUTDOWN " /* 27 */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* set diagnostic ,ask
|
||||
*/
|
||||
void lthread_diagnostic_set_mask(DIAG_USED uint64_t mask)
|
||||
{
|
||||
#if LTHREAD_DIAG
|
||||
diag_mask = mask;
|
||||
#else
|
||||
RTE_LOG(INFO, LTHREAD,
|
||||
"LTHREAD_DIAG is not set, see lthread_diag_api.h\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check consistency of the scheduler stats
|
||||
* Only sensible run after the schedulers are stopped
|
||||
* Count the number of objects lying in caches and queues
|
||||
* and available in the qnode pool.
|
||||
* This should be equal to the total capacity of all
|
||||
* qnode pools.
|
||||
*/
|
||||
void
|
||||
_sched_stats_consistency_check(void);
|
||||
void
|
||||
_sched_stats_consistency_check(void)
|
||||
{
|
||||
#if LTHREAD_DIAG
|
||||
int i;
|
||||
struct lthread_sched *sched;
|
||||
uint64_t count = 0;
|
||||
uint64_t capacity = 0;
|
||||
|
||||
for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
|
||||
sched = schedcore[i];
|
||||
if (sched == NULL)
|
||||
continue;
|
||||
|
||||
/* each of these queues consumes a stub node */
|
||||
count += 8;
|
||||
count += DIAG_COUNT(sched->ready, size);
|
||||
count += DIAG_COUNT(sched->pready, size);
|
||||
count += DIAG_COUNT(sched->lthread_cache, available);
|
||||
count += DIAG_COUNT(sched->stack_cache, available);
|
||||
count += DIAG_COUNT(sched->tls_cache, available);
|
||||
count += DIAG_COUNT(sched->per_lthread_cache, available);
|
||||
count += DIAG_COUNT(sched->cond_cache, available);
|
||||
count += DIAG_COUNT(sched->mutex_cache, available);
|
||||
|
||||
/* the node pool does not consume a stub node */
|
||||
if (sched->qnode_pool->fast_alloc != NULL)
|
||||
count++;
|
||||
count += DIAG_COUNT(sched->qnode_pool, available);
|
||||
|
||||
capacity += DIAG_COUNT(sched->qnode_pool, capacity);
|
||||
}
|
||||
if (count != capacity) {
|
||||
RTE_LOG(CRIT, LTHREAD,
|
||||
"Scheduler caches are inconsistent\n");
|
||||
} else {
|
||||
RTE_LOG(INFO, LTHREAD,
|
||||
"Scheduler caches are ok\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if LTHREAD_DIAG
|
||||
/*
|
||||
* Display node pool stats
|
||||
*/
|
||||
static inline void
|
||||
_qnode_pool_display(DIAG_USED struct qnode_pool *p)
|
||||
{
|
||||
|
||||
printf(DIAG_CACHE_STATS_FORMAT,
|
||||
p->name,
|
||||
DIAG_COUNT(p, rd),
|
||||
DIAG_COUNT(p, wr),
|
||||
DIAG_COUNT(p, available),
|
||||
DIAG_COUNT(p, prealloc),
|
||||
DIAG_COUNT(p, capacity));
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if LTHREAD_DIAG
|
||||
/*
|
||||
* Display queue stats
|
||||
*/
|
||||
static inline void
|
||||
_lthread_queue_display(DIAG_USED struct lthread_queue *q)
|
||||
{
|
||||
#if DISPLAY_OBJCACHE_QUEUES
|
||||
printf(DIAG_QUEUE_STATS_FORMAT,
|
||||
q->name,
|
||||
DIAG_COUNT(q, rd),
|
||||
DIAG_COUNT(q, wr),
|
||||
DIAG_COUNT(q, size));
|
||||
fflush(stdout);
|
||||
#else
|
||||
printf("%s: queue stats disabled\n",
|
||||
q->name);
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LTHREAD_DIAG
|
||||
/*
|
||||
* Display objcache stats
|
||||
*/
|
||||
static inline void
|
||||
_objcache_display(DIAG_USED struct lthread_objcache *c)
|
||||
{
|
||||
|
||||
printf(DIAG_CACHE_STATS_FORMAT,
|
||||
c->name,
|
||||
DIAG_COUNT(c, rd),
|
||||
DIAG_COUNT(c, wr),
|
||||
DIAG_COUNT(c, available),
|
||||
DIAG_COUNT(c, prealloc),
|
||||
DIAG_COUNT(c, capacity));
|
||||
_lthread_queue_display(c->q);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Display sched stats
|
||||
*/
|
||||
void
|
||||
lthread_sched_stats_display(void)
|
||||
{
|
||||
#if LTHREAD_DIAG
|
||||
int i;
|
||||
struct lthread_sched *sched;
|
||||
|
||||
for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
|
||||
sched = schedcore[i];
|
||||
if (sched != NULL) {
|
||||
printf(DIAG_SCHED_STATS_FORMAT,
|
||||
sched->lcore_id,
|
||||
"rd",
|
||||
"wr",
|
||||
"present",
|
||||
"nb preallocs",
|
||||
"capacity");
|
||||
_lthread_queue_display(sched->ready);
|
||||
_lthread_queue_display(sched->pready);
|
||||
_qnode_pool_display(sched->qnode_pool);
|
||||
_objcache_display(sched->lthread_cache);
|
||||
_objcache_display(sched->stack_cache);
|
||||
_objcache_display(sched->tls_cache);
|
||||
_objcache_display(sched->per_lthread_cache);
|
||||
_objcache_display(sched->cond_cache);
|
||||
_objcache_display(sched->mutex_cache);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
_sched_stats_consistency_check();
|
||||
#else
|
||||
RTE_LOG(INFO, LTHREAD,
|
||||
"lthread diagnostics disabled\n"
|
||||
"hint - set LTHREAD_DIAG in lthread_diag_api.h\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Default diagnostic callback
|
||||
*/
|
||||
static uint64_t
|
||||
_lthread_diag_default_cb(uint64_t time, struct lthread *lt, int diag_event,
|
||||
uint64_t diag_ref, const char *text, uint64_t p1, uint64_t p2)
|
||||
{
|
||||
uint64_t _p2;
|
||||
int lcore = (int) rte_lcore_id();
|
||||
|
||||
switch (diag_event) {
|
||||
case LT_DIAG_LTHREAD_CREATE:
|
||||
case LT_DIAG_MUTEX_CREATE:
|
||||
case LT_DIAG_COND_CREATE:
|
||||
_p2 = dummy_ref;
|
||||
break;
|
||||
default:
|
||||
_p2 = p2;
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%"PRIu64" %d %8.8lx %8.8lx %s %8.8lx %8.8lx\n",
|
||||
time,
|
||||
lcore,
|
||||
(uint64_t) lt,
|
||||
diag_ref,
|
||||
text,
|
||||
p1,
|
||||
_p2);
|
||||
|
||||
return dummy_ref++;
|
||||
}
|
||||
|
||||
/*
|
||||
* plug in default diag callback with mask off
|
||||
*/
|
||||
RTE_INIT(_lthread_diag_ctor)
|
||||
{
|
||||
diag_cb = _lthread_diag_default_cb;
|
||||
diag_mask = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* enable diagnostics
|
||||
*/
|
||||
void lthread_diagnostic_enable(DIAG_USED diag_callback cb,
|
||||
DIAG_USED uint64_t mask)
|
||||
{
|
||||
#if LTHREAD_DIAG
|
||||
if (cb == NULL)
|
||||
diag_cb = _lthread_diag_default_cb;
|
||||
else
|
||||
diag_cb = cb;
|
||||
diag_mask = mask;
|
||||
#else
|
||||
RTE_LOG(INFO, LTHREAD,
|
||||
"LTHREAD_DIAG is not set, see lthread_diag_api.h\n");
|
||||
#endif
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef LTHREAD_DIAG_H_
|
||||
#define LTHREAD_DIAG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <rte_log.h>
|
||||
#include <rte_common.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread_diag_api.h"
|
||||
|
||||
extern diag_callback diag_cb;
|
||||
|
||||
extern const char *diag_event_text[];
|
||||
extern uint64_t diag_mask;
|
||||
|
||||
/* max size of name strings */
|
||||
#define LT_MAX_NAME_SIZE 64
|
||||
|
||||
#if LTHREAD_DIAG
|
||||
#define DISPLAY_OBJCACHE_QUEUES 1
|
||||
|
||||
/*
|
||||
* Generate a diagnostic trace or event in the case where an object is created.
|
||||
*
|
||||
* The value returned by the callback is stored in the object.
|
||||
*
|
||||
* @ param obj
|
||||
* pointer to the object that was created
|
||||
* @ param ev
|
||||
* the event code
|
||||
*
|
||||
*/
|
||||
#define DIAG_CREATE_EVENT(obj, ev) do { \
|
||||
struct lthread *ct = RTE_PER_LCORE(this_sched)->current_lthread;\
|
||||
if ((BIT(ev) & diag_mask) && (ev < LT_DIAG_EVENT_MAX)) { \
|
||||
(obj)->diag_ref = (diag_cb)(rte_rdtsc(), \
|
||||
ct, \
|
||||
(ev), \
|
||||
0, \
|
||||
diag_event_text[(ev)], \
|
||||
(uint64_t)obj, \
|
||||
0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Generate a diagnostic trace event.
|
||||
*
|
||||
* @ param obj
|
||||
* pointer to the lthread, cond or mutex object
|
||||
* @ param ev
|
||||
* the event code
|
||||
* @ param p1
|
||||
* object specific value ( see lthread_diag_api.h )
|
||||
* @ param p2
|
||||
* object specific value ( see lthread_diag_api.h )
|
||||
*/
|
||||
#define DIAG_EVENT(obj, ev, p1, p2) do { \
|
||||
struct lthread *ct = RTE_PER_LCORE(this_sched)->current_lthread;\
|
||||
if ((BIT(ev) & diag_mask) && (ev < LT_DIAG_EVENT_MAX)) { \
|
||||
(diag_cb)(rte_rdtsc(), \
|
||||
ct, \
|
||||
ev, \
|
||||
(obj)->diag_ref, \
|
||||
diag_event_text[(ev)], \
|
||||
(uint64_t)(p1), \
|
||||
(uint64_t)(p2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DIAG_COUNT_DEFINE(x) uint64_t count_##x
|
||||
#define DIAG_COUNT_INIT(o, x) __atomic_store_n(&((o)->count_##x), 0, __ATOMIC_RELAXED)
|
||||
#define DIAG_COUNT_INC(o, x) __atomic_fetch_add(&((o)->count_##x), 1, __ATOMIC_RELAXED)
|
||||
#define DIAG_COUNT_DEC(o, x) __atomic_fetch_sub(&((o)->count_##x), 1, __ATOMIC_RELAXED)
|
||||
#define DIAG_COUNT(o, x) __atomic_load_n(&((o)->count_##x), __ATOMIC_RELAXED)
|
||||
|
||||
#define DIAG_USED
|
||||
|
||||
#else
|
||||
|
||||
/* no diagnostics configured */
|
||||
|
||||
#define DISPLAY_OBJCACHE_QUEUES 0
|
||||
|
||||
#define DIAG_CREATE_EVENT(obj, ev)
|
||||
#define DIAG_EVENT(obj, ev, p1, p)
|
||||
|
||||
#define DIAG_COUNT_DEFINE(x)
|
||||
#define DIAG_COUNT_INIT(o, x) do {} while (0)
|
||||
#define DIAG_COUNT_INC(o, x) do {} while (0)
|
||||
#define DIAG_COUNT_DEC(o, x) do {} while (0)
|
||||
#define DIAG_COUNT(o, x) 0
|
||||
|
||||
#define DIAG_USED __rte_unused
|
||||
|
||||
#endif /* LTHREAD_DIAG */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_DIAG_H_ */
|
@ -1,304 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
#ifndef LTHREAD_DIAG_API_H_
|
||||
#define LTHREAD_DIAG_API_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/*
|
||||
* Enable diagnostics
|
||||
* 0 = conditionally compiled out
|
||||
* 1 = compiled in and maskable at run time, see below for details
|
||||
*/
|
||||
#define LTHREAD_DIAG 0
|
||||
|
||||
/**
|
||||
*
|
||||
* @file lthread_diag_api.h
|
||||
*
|
||||
* @warning
|
||||
* @b EXPERIMENTAL: this API may change without prior notice
|
||||
*
|
||||
* lthread diagnostic interface
|
||||
*
|
||||
* If enabled via configuration file option ( tbd ) the lthread subsystem
|
||||
* can generate selected trace information, either RTE_LOG (INFO) messages,
|
||||
* or else invoke a user supplied callback function when any of the events
|
||||
* listed below occur.
|
||||
*
|
||||
* Reporting of events can be selectively masked, the bit position in the
|
||||
* mask is determined by the corresponding event identifier listed below.
|
||||
*
|
||||
* Diagnostics are enabled by registering the callback function and mask
|
||||
* using the API lthread_diagnostic_enable().
|
||||
*
|
||||
* Various interesting parameters are passed to the callback, including the
|
||||
* time in cpu clks, the lthread id, the diagnostic event id, a user ref value,
|
||||
* event text string, object being traced, and two context dependent parameters
|
||||
* (p1 and p2). The meaning of the two parameters p1 and p2 depends on
|
||||
* the specific event.
|
||||
*
|
||||
* The events LT_DIAG_LTHREAD_CREATE, LT_DIAG_MUTEX_CREATE and
|
||||
* LT_DIAG_COND_CREATE are implicitly enabled if the event mask includes any of
|
||||
* the LT_DIAG_LTHREAD_XXX, LT_DIAG_MUTEX_XXX or LT_DIAG_COND_XXX events
|
||||
* respectively.
|
||||
*
|
||||
* These create events may also be included in the mask discreetly if it is
|
||||
* desired to monitor only create events.
|
||||
*
|
||||
* @param time
|
||||
* The time in cpu clks at which the event occurred
|
||||
*
|
||||
* @param lthread
|
||||
* The current lthread
|
||||
*
|
||||
* @param diag_event
|
||||
* The diagnostic event id (bit position in the mask)
|
||||
*
|
||||
* @param diag_ref
|
||||
*
|
||||
* For LT_DIAG_LTHREAD_CREATE, LT_DIAG_MUTEX_CREATE or LT_DIAG_COND_CREATE
|
||||
* this parameter is not used and set to 0.
|
||||
* All other events diag_ref contains the user ref value returned by the
|
||||
* callback function when lthread is created.
|
||||
*
|
||||
* The diag_ref values assigned to mutex and cond var can be retrieved
|
||||
* using the APIs lthread_mutex_diag_ref(), and lthread_cond_diag_ref()
|
||||
* respectively.
|
||||
*
|
||||
* @param p1
|
||||
* see below
|
||||
*
|
||||
* @param p1
|
||||
* see below
|
||||
*
|
||||
* @returns
|
||||
* For LT_DIAG_LTHREAD_CREATE, LT_DIAG_MUTEX_CREATE or LT_DIAG_COND_CREATE
|
||||
* expects a user diagnostic ref value that will be saved in the lthread, mutex
|
||||
* or cond var.
|
||||
*
|
||||
* For all other events return value is ignored.
|
||||
*
|
||||
* LT_DIAG_SCHED_CREATE - Invoked when a scheduler is created
|
||||
* p1 = the scheduler that was created
|
||||
* p2 = not used
|
||||
* return value will be ignored
|
||||
*
|
||||
* LT_DIAG_SCHED_SHUTDOWN - Invoked when a shutdown request is received
|
||||
* p1 = the scheduler to be shutdown
|
||||
* p2 = not used
|
||||
* return value will be ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_CREATE - Invoked when a thread is created
|
||||
* p1 = the lthread that was created
|
||||
* p2 = not used
|
||||
* return value will be stored in the lthread
|
||||
*
|
||||
* LT_DIAG_LTHREAD_EXIT - Invoked when a lthread exits
|
||||
* p2 = 0 if the thread was already joined
|
||||
* p2 = 1 if the thread was not already joined
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_JOIN - Invoked when a lthread exits
|
||||
* p1 = the lthread that is being joined
|
||||
* p2 = 0 if the thread was already exited
|
||||
* p2 = 1 if the thread was not already exited
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_CANCELLED - Invoked when an lthread is cancelled
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_DETACH - Invoked when an lthread is detached
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_FREE - Invoked when an lthread is freed
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_SUSPENDED - Invoked when an lthread is suspended
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_YIELD - Invoked when an lthread explicitly yields
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_RESCHEDULED - Invoked when an lthread is rescheduled
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_RESUMED - Invoked when an lthread is resumed
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_AFFINITY - Invoked when an lthread is affinitised
|
||||
* p1 = the destination lcore_id
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_TMR_START - Invoked when an lthread starts a timer
|
||||
* p1 = address of timer node
|
||||
* p2 = the timeout value
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_TMR_DELETE - Invoked when an lthread deletes a timer
|
||||
* p1 = address of the timer node
|
||||
* p2 = 0 the timer and the was successfully deleted
|
||||
* p2 = not usee
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_LTHREAD_TMR_EXPIRED - Invoked when an lthread timer expires
|
||||
* p1 = address of scheduler the timer expired on
|
||||
* p2 = the thread associated with the timer
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_COND_CREATE - Invoked when a condition variable is created
|
||||
* p1 = address of cond var that was created
|
||||
* p2 = not used
|
||||
* return diag ref value will be stored in the condition variable
|
||||
*
|
||||
* LT_DIAG_COND_DESTROY - Invoked when a condition variable is destroyed
|
||||
* p1 = not used
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_COND_WAIT - Invoked when an lthread waits on a cond var
|
||||
* p1 = the address of the condition variable
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_COND_SIGNAL - Invoked when an lthread signals a cond var
|
||||
* p1 = the address of the cond var
|
||||
* p2 = the lthread that was signalled, or error code
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_COND_BROADCAST - Invoked when an lthread broadcasts a cond var
|
||||
* p1 = the address of the condition variable
|
||||
* p2 = the lthread(s) that are signalled, or error code
|
||||
*
|
||||
* LT_DIAG_MUTEX_CREATE - Invoked when a mutex is created
|
||||
* p1 = address of muex
|
||||
* p2 = not used
|
||||
* return diag ref value will be stored in the mutex variable
|
||||
*
|
||||
* LT_DIAG_MUTEX_DESTROY - Invoked when a mutex is destroyed
|
||||
* p1 = address of mutex
|
||||
* p2 = not used
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_MUTEX_LOCK - Invoked when a mutex lock is obtained
|
||||
* p1 = address of mutex
|
||||
* p2 = function return value
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_MUTEX_BLOCKED - Invoked when an lthread blocks on a mutex
|
||||
* p1 = address of mutex
|
||||
* p2 = function return value
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_MUTEX_TRYLOCK - Invoked when a mutex try lock is attempted
|
||||
* p1 = address of mutex
|
||||
* p2 = the function return value
|
||||
* return val ignored
|
||||
*
|
||||
* LT_DIAG_MUTEX_UNLOCKED - Invoked when a mutex is unlocked
|
||||
* p1 = address of mutex
|
||||
* p2 = the thread that was unlocked, or error code
|
||||
* return val ignored
|
||||
*/
|
||||
typedef uint64_t (*diag_callback) (uint64_t time, struct lthread *lt,
|
||||
int diag_event, uint64_t diag_ref,
|
||||
const char *text, uint64_t p1, uint64_t p2);
|
||||
|
||||
/*
|
||||
* Set user diagnostic callback and mask
|
||||
* If the callback function pointer is NULL the default
|
||||
* callback handler will be restored.
|
||||
*/
|
||||
void lthread_diagnostic_enable(diag_callback cb, uint64_t diag_mask);
|
||||
|
||||
/*
|
||||
* Set diagnostic mask
|
||||
*/
|
||||
void lthread_diagnostic_set_mask(uint64_t mask);
|
||||
|
||||
/*
|
||||
* lthread diagnostic callback
|
||||
*/
|
||||
enum lthread_diag_ev {
|
||||
/* bits 0 - 14 lthread flag group */
|
||||
LT_DIAG_LTHREAD_CREATE, /* 00 mask 0x00000001 */
|
||||
LT_DIAG_LTHREAD_EXIT, /* 01 mask 0x00000002 */
|
||||
LT_DIAG_LTHREAD_JOIN, /* 02 mask 0x00000004 */
|
||||
LT_DIAG_LTHREAD_CANCEL, /* 03 mask 0x00000008 */
|
||||
LT_DIAG_LTHREAD_DETACH, /* 04 mask 0x00000010 */
|
||||
LT_DIAG_LTHREAD_FREE, /* 05 mask 0x00000020 */
|
||||
LT_DIAG_LTHREAD_SUSPENDED, /* 06 mask 0x00000040 */
|
||||
LT_DIAG_LTHREAD_YIELD, /* 07 mask 0x00000080 */
|
||||
LT_DIAG_LTHREAD_RESCHEDULED, /* 08 mask 0x00000100 */
|
||||
LT_DIAG_LTHREAD_SLEEP, /* 09 mask 0x00000200 */
|
||||
LT_DIAG_LTHREAD_RESUMED, /* 10 mask 0x00000400 */
|
||||
LT_DIAG_LTHREAD_AFFINITY, /* 11 mask 0x00000800 */
|
||||
LT_DIAG_LTHREAD_TMR_START, /* 12 mask 0x00001000 */
|
||||
LT_DIAG_LTHREAD_TMR_DELETE, /* 13 mask 0x00002000 */
|
||||
LT_DIAG_LTHREAD_TMR_EXPIRED, /* 14 mask 0x00004000 */
|
||||
/* bits 15 - 19 conditional variable flag group */
|
||||
LT_DIAG_COND_CREATE, /* 15 mask 0x00008000 */
|
||||
LT_DIAG_COND_DESTROY, /* 16 mask 0x00010000 */
|
||||
LT_DIAG_COND_WAIT, /* 17 mask 0x00020000 */
|
||||
LT_DIAG_COND_SIGNAL, /* 18 mask 0x00040000 */
|
||||
LT_DIAG_COND_BROADCAST, /* 19 mask 0x00080000 */
|
||||
/* bits 20 - 25 mutex flag group */
|
||||
LT_DIAG_MUTEX_CREATE, /* 20 mask 0x00100000 */
|
||||
LT_DIAG_MUTEX_DESTROY, /* 21 mask 0x00200000 */
|
||||
LT_DIAG_MUTEX_LOCK, /* 22 mask 0x00400000 */
|
||||
LT_DIAG_MUTEX_TRYLOCK, /* 23 mask 0x00800000 */
|
||||
LT_DIAG_MUTEX_BLOCKED, /* 24 mask 0x01000000 */
|
||||
LT_DIAG_MUTEX_UNLOCKED, /* 25 mask 0x02000000 */
|
||||
/* bits 26 - 27 scheduler flag group - 8 bits */
|
||||
LT_DIAG_SCHED_CREATE, /* 26 mask 0x04000000 */
|
||||
LT_DIAG_SCHED_SHUTDOWN, /* 27 mask 0x08000000 */
|
||||
LT_DIAG_EVENT_MAX
|
||||
};
|
||||
|
||||
#define LT_DIAG_ALL 0xffffffffffffffff
|
||||
|
||||
|
||||
/*
|
||||
* Display scheduler stats
|
||||
*/
|
||||
void
|
||||
lthread_sched_stats_display(void);
|
||||
|
||||
/*
|
||||
* return the diagnostic ref val stored in a condition var
|
||||
*/
|
||||
uint64_t
|
||||
lthread_cond_diag_ref(struct lthread_cond *c);
|
||||
|
||||
/*
|
||||
* return the diagnostic ref val stored in a mutex
|
||||
*/
|
||||
uint64_t
|
||||
lthread_mutex_diag_ref(struct lthread_mutex *m);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_DIAG_API_H_ */
|
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
#ifndef LTHREAD_INT_H
|
||||
#define LTHREAD_INT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <rte_memory.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_timer.h>
|
||||
#include <rte_spinlock.h>
|
||||
#include <ctx.h>
|
||||
|
||||
#include <lthread_api.h>
|
||||
#include "lthread.h"
|
||||
#include "lthread_diag.h"
|
||||
#include "lthread_tls.h"
|
||||
|
||||
struct lthread;
|
||||
struct lthread_sched;
|
||||
struct lthread_cond;
|
||||
struct lthread_mutex;
|
||||
struct lthread_key;
|
||||
|
||||
struct key_pool;
|
||||
struct qnode;
|
||||
struct qnode_pool;
|
||||
struct lthread_sched;
|
||||
struct lthread_tls;
|
||||
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
#define CLEARBIT(x) ~(1 << (x))
|
||||
|
||||
#define POSIX_ERRNO(x) (x)
|
||||
|
||||
#define MAX_LTHREAD_NAME_SIZE 64
|
||||
|
||||
#define RTE_LOGTYPE_LTHREAD RTE_LOGTYPE_USER1
|
||||
|
||||
|
||||
/* define some shorthand for current scheduler and current thread */
|
||||
#define THIS_SCHED RTE_PER_LCORE(this_sched)
|
||||
#define THIS_LTHREAD RTE_PER_LCORE(this_sched)->current_lthread
|
||||
|
||||
/*
|
||||
* Definition of an scheduler struct
|
||||
*/
|
||||
struct lthread_sched {
|
||||
struct ctx ctx; /* cpu context */
|
||||
uint64_t birth; /* time created */
|
||||
struct lthread *current_lthread; /* running thread */
|
||||
unsigned lcore_id; /* this sched lcore */
|
||||
int run_flag; /* sched shutdown */
|
||||
uint64_t nb_blocked_threads; /* blocked threads */
|
||||
struct lthread_queue *ready; /* local ready queue */
|
||||
struct lthread_queue *pready; /* peer ready queue */
|
||||
struct lthread_objcache *lthread_cache; /* free lthreads */
|
||||
struct lthread_objcache *stack_cache; /* free stacks */
|
||||
struct lthread_objcache *per_lthread_cache; /* free per lthread */
|
||||
struct lthread_objcache *tls_cache; /* free TLS */
|
||||
struct lthread_objcache *cond_cache; /* free cond vars */
|
||||
struct lthread_objcache *mutex_cache; /* free mutexes */
|
||||
struct qnode_pool *qnode_pool; /* pool of queue nodes */
|
||||
struct key_pool *key_pool; /* pool of free TLS keys */
|
||||
size_t stack_size;
|
||||
uint64_t diag_ref; /* diag ref */
|
||||
} __rte_cache_aligned;
|
||||
|
||||
RTE_DECLARE_PER_LCORE(struct lthread_sched *, this_sched);
|
||||
|
||||
|
||||
/*
|
||||
* State for an lthread
|
||||
*/
|
||||
enum lthread_st {
|
||||
ST_LT_INIT, /* initial state */
|
||||
ST_LT_READY, /* lthread is ready to run */
|
||||
ST_LT_SLEEPING, /* lthread is sleeping */
|
||||
ST_LT_EXPIRED, /* lthread timeout has expired */
|
||||
ST_LT_EXITED, /* lthread has exited and needs cleanup */
|
||||
ST_LT_DETACH, /* lthread frees on exit*/
|
||||
ST_LT_CANCELLED, /* lthread has been cancelled */
|
||||
};
|
||||
|
||||
/*
|
||||
* lthread sub states for exit/join
|
||||
*/
|
||||
enum join_st {
|
||||
LT_JOIN_INITIAL, /* initial state */
|
||||
LT_JOIN_EXITING, /* thread is exiting */
|
||||
LT_JOIN_THREAD_SET, /* joining thread has been set */
|
||||
LT_JOIN_EXIT_VAL_SET, /* exiting thread has set ret val */
|
||||
LT_JOIN_EXIT_VAL_READ, /* joining thread has collected ret val */
|
||||
};
|
||||
|
||||
/* definition of an lthread stack object */
|
||||
struct lthread_stack {
|
||||
uint8_t stack[LTHREAD_MAX_STACK_SIZE];
|
||||
size_t stack_size;
|
||||
struct lthread_sched *root_sched;
|
||||
} __rte_cache_aligned;
|
||||
|
||||
/*
|
||||
* Definition of an lthread
|
||||
*/
|
||||
struct lthread {
|
||||
struct ctx ctx; /* cpu context */
|
||||
|
||||
uint64_t state; /* current lthread state */
|
||||
|
||||
struct lthread_sched *sched; /* current scheduler */
|
||||
void *stack; /* ptr to actual stack */
|
||||
size_t stack_size; /* current stack_size */
|
||||
size_t last_stack_size; /* last yield stack_size */
|
||||
lthread_func_t fun; /* func ctx is running */
|
||||
void *arg; /* func args passed to func */
|
||||
void *per_lthread_data; /* per lthread user data */
|
||||
lthread_exit_func exit_handler; /* called when thread exits */
|
||||
uint64_t birth; /* time lthread was born */
|
||||
struct lthread_queue *pending_wr_queue; /* deferred queue to write */
|
||||
struct lthread *lt_join; /* lthread to join on */
|
||||
uint64_t join; /* state for joining */
|
||||
void **lt_exit_ptr; /* exit ptr for lthread_join */
|
||||
struct lthread_sched *root_sched; /* thread was created here*/
|
||||
struct queue_node *qnode; /* node when in a queue */
|
||||
struct rte_timer tim; /* sleep timer */
|
||||
struct lthread_tls *tls; /* keys in use by the thread */
|
||||
struct lthread_stack *stack_container; /* stack */
|
||||
char funcname[MAX_LTHREAD_NAME_SIZE]; /* thread func name */
|
||||
uint64_t diag_ref; /* ref to user diag data */
|
||||
} __rte_cache_aligned;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_INT_H */
|
@ -1,226 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_spinlock.h>
|
||||
#include <rte_common.h>
|
||||
#include <rte_string_fns.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_mutex.h"
|
||||
#include "lthread_sched.h"
|
||||
#include "lthread_queue.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_diag.h"
|
||||
|
||||
/*
|
||||
* Create a mutex
|
||||
*/
|
||||
int
|
||||
lthread_mutex_init(char *name, struct lthread_mutex **mutex,
|
||||
__rte_unused const struct lthread_mutexattr *attr)
|
||||
{
|
||||
struct lthread_mutex *m;
|
||||
|
||||
if (mutex == NULL)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
|
||||
m = _lthread_objcache_alloc((THIS_SCHED)->mutex_cache);
|
||||
if (m == NULL)
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
|
||||
m->blocked = _lthread_queue_create("blocked queue");
|
||||
if (m->blocked == NULL) {
|
||||
_lthread_objcache_free((THIS_SCHED)->mutex_cache, m);
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
strlcpy(m->name, "no name", sizeof(m->name));
|
||||
else
|
||||
strlcpy(m->name, name, sizeof(m->name));
|
||||
|
||||
m->root_sched = THIS_SCHED;
|
||||
m->owner = NULL;
|
||||
|
||||
__atomic_store_n(&m->count, 0, __ATOMIC_RELAXED);
|
||||
|
||||
DIAG_CREATE_EVENT(m, LT_DIAG_MUTEX_CREATE);
|
||||
/* success */
|
||||
(*mutex) = m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a mutex
|
||||
*/
|
||||
int lthread_mutex_destroy(struct lthread_mutex *m)
|
||||
{
|
||||
if ((m == NULL) || (m->blocked == NULL)) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, m, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
if (m->owner == NULL) {
|
||||
/* try to delete the blocked queue */
|
||||
if (_lthread_queue_destroy(m->blocked) < 0) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY,
|
||||
m, POSIX_ERRNO(EBUSY));
|
||||
return POSIX_ERRNO(EBUSY);
|
||||
}
|
||||
|
||||
/* free the mutex to cache */
|
||||
_lthread_objcache_free(m->root_sched->mutex_cache, m);
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, m, 0);
|
||||
return 0;
|
||||
}
|
||||
/* can't do its still in use */
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_DESTROY, m, POSIX_ERRNO(EBUSY));
|
||||
return POSIX_ERRNO(EBUSY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to obtain a mutex
|
||||
*/
|
||||
int lthread_mutex_lock(struct lthread_mutex *m)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
if ((m == NULL) || (m->blocked == NULL)) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_LOCK, m, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
/* allow no recursion */
|
||||
if (m->owner == lt) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_LOCK, m, POSIX_ERRNO(EDEADLK));
|
||||
return POSIX_ERRNO(EDEADLK);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
__atomic_fetch_add(&m->count, 1, __ATOMIC_RELAXED);
|
||||
do {
|
||||
uint64_t lt_init = 0;
|
||||
if (__atomic_compare_exchange_n((uint64_t *) &m->owner, <_init,
|
||||
(uint64_t) lt, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
/* happy days, we got the lock */
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_LOCK, m, 0);
|
||||
return 0;
|
||||
}
|
||||
/* spin due to race with unlock when
|
||||
* nothing was blocked
|
||||
*/
|
||||
} while ((__atomic_load_n(&m->count, __ATOMIC_RELAXED) == 1) &&
|
||||
(m->owner == NULL));
|
||||
|
||||
/* queue the current thread in the blocked queue
|
||||
* we defer this to after we return to the scheduler
|
||||
* to ensure that the current thread context is saved
|
||||
* before unlock could result in it being dequeued and
|
||||
* resumed
|
||||
*/
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_BLOCKED, m, lt);
|
||||
lt->pending_wr_queue = m->blocked;
|
||||
/* now relinquish cpu */
|
||||
_suspend();
|
||||
/* resumed, must loop and compete for the lock again */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to lock a mutex but don't block */
|
||||
int lthread_mutex_trylock(struct lthread_mutex *m)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
if ((m == NULL) || (m->blocked == NULL)) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
if (m->owner == lt) {
|
||||
/* no recursion */
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, POSIX_ERRNO(EDEADLK));
|
||||
return POSIX_ERRNO(EDEADLK);
|
||||
}
|
||||
|
||||
__atomic_fetch_add(&m->count, 1, __ATOMIC_RELAXED);
|
||||
uint64_t lt_init = 0;
|
||||
if (__atomic_compare_exchange_n((uint64_t *) &m->owner, <_init,
|
||||
(uint64_t) lt, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
/* got the lock */
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* failed so return busy */
|
||||
__atomic_fetch_sub(&m->count, 1, __ATOMIC_RELAXED);
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_TRYLOCK, m, POSIX_ERRNO(EBUSY));
|
||||
return POSIX_ERRNO(EBUSY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock a mutex
|
||||
*/
|
||||
int lthread_mutex_unlock(struct lthread_mutex *m)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
struct lthread *unblocked;
|
||||
|
||||
if ((m == NULL) || (m->blocked == NULL)) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_UNLOCKED, m, POSIX_ERRNO(EINVAL));
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
}
|
||||
|
||||
/* fail if its owned */
|
||||
if (m->owner != lt || m->owner == NULL) {
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_UNLOCKED, m, POSIX_ERRNO(EPERM));
|
||||
return POSIX_ERRNO(EPERM);
|
||||
}
|
||||
|
||||
__atomic_fetch_sub(&m->count, 1, __ATOMIC_RELAXED);
|
||||
/* if there are blocked threads then make one ready */
|
||||
while (__atomic_load_n(&m->count, __ATOMIC_RELAXED) > 0) {
|
||||
unblocked = _lthread_queue_remove(m->blocked);
|
||||
|
||||
if (unblocked != NULL) {
|
||||
__atomic_fetch_sub(&m->count, 1, __ATOMIC_RELAXED);
|
||||
DIAG_EVENT(m, LT_DIAG_MUTEX_UNLOCKED, m, unblocked);
|
||||
RTE_ASSERT(unblocked->sched != NULL);
|
||||
_ready_queue_insert((struct lthread_sched *)
|
||||
unblocked->sched, unblocked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* release the lock */
|
||||
m->owner = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the diagnostic ref val stored in a mutex
|
||||
*/
|
||||
uint64_t
|
||||
lthread_mutex_diag_ref(struct lthread_mutex *m)
|
||||
{
|
||||
if (m == NULL)
|
||||
return 0;
|
||||
return m->diag_ref;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LTHREAD_MUTEX_H_
|
||||
#define LTHREAD_MUTEX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_queue.h"
|
||||
|
||||
|
||||
#define MAX_MUTEX_NAME_SIZE 64
|
||||
|
||||
struct lthread_mutex {
|
||||
struct lthread *owner;
|
||||
uint64_t count;
|
||||
struct lthread_queue *blocked __rte_cache_aligned;
|
||||
struct lthread_sched *root_sched;
|
||||
char name[MAX_MUTEX_NAME_SIZE];
|
||||
uint64_t diag_ref; /* optional ref to user diag data */
|
||||
} __rte_cache_aligned;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_MUTEX_H_ */
|
@ -1,136 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
#ifndef LTHREAD_OBJCACHE_H_
|
||||
#define LTHREAD_OBJCACHE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_malloc.h>
|
||||
#include <rte_memory.h>
|
||||
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_diag.h"
|
||||
#include "lthread_queue.h"
|
||||
|
||||
|
||||
RTE_DECLARE_PER_LCORE(struct lthread_sched *, this_sched);
|
||||
|
||||
struct lthread_objcache {
|
||||
struct lthread_queue *q;
|
||||
size_t obj_size;
|
||||
int prealloc_size;
|
||||
char name[LT_MAX_NAME_SIZE];
|
||||
|
||||
DIAG_COUNT_DEFINE(rd);
|
||||
DIAG_COUNT_DEFINE(wr);
|
||||
DIAG_COUNT_DEFINE(prealloc);
|
||||
DIAG_COUNT_DEFINE(capacity);
|
||||
DIAG_COUNT_DEFINE(available);
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a cache
|
||||
*/
|
||||
static inline struct
|
||||
lthread_objcache *_lthread_objcache_create(const char *name,
|
||||
size_t obj_size,
|
||||
int prealloc_size)
|
||||
{
|
||||
struct lthread_objcache *c =
|
||||
rte_malloc_socket(NULL, sizeof(struct lthread_objcache),
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->q = _lthread_queue_create("cache queue");
|
||||
if (c->q == NULL) {
|
||||
rte_free(c);
|
||||
return NULL;
|
||||
}
|
||||
c->obj_size = obj_size;
|
||||
c->prealloc_size = prealloc_size;
|
||||
|
||||
if (name != NULL)
|
||||
strncpy(c->name, name, LT_MAX_NAME_SIZE);
|
||||
c->name[sizeof(c->name)-1] = 0;
|
||||
|
||||
DIAG_COUNT_INIT(c, rd);
|
||||
DIAG_COUNT_INIT(c, wr);
|
||||
DIAG_COUNT_INIT(c, prealloc);
|
||||
DIAG_COUNT_INIT(c, capacity);
|
||||
DIAG_COUNT_INIT(c, available);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an objcache
|
||||
*/
|
||||
static inline int
|
||||
_lthread_objcache_destroy(struct lthread_objcache *c)
|
||||
{
|
||||
if (_lthread_queue_destroy(c->q) == 0) {
|
||||
rte_free(c);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an object from an object cache
|
||||
*/
|
||||
static inline void *
|
||||
_lthread_objcache_alloc(struct lthread_objcache *c)
|
||||
{
|
||||
int i;
|
||||
void *data;
|
||||
struct lthread_queue *q = c->q;
|
||||
size_t obj_size = c->obj_size;
|
||||
int prealloc_size = c->prealloc_size;
|
||||
|
||||
data = _lthread_queue_remove(q);
|
||||
|
||||
if (data == NULL) {
|
||||
DIAG_COUNT_INC(c, prealloc);
|
||||
for (i = 0; i < prealloc_size; i++) {
|
||||
data =
|
||||
rte_zmalloc_socket(NULL, obj_size,
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
DIAG_COUNT_INC(c, available);
|
||||
DIAG_COUNT_INC(c, capacity);
|
||||
_lthread_queue_insert_mp(q, data);
|
||||
}
|
||||
data = _lthread_queue_remove(q);
|
||||
}
|
||||
DIAG_COUNT_INC(c, rd);
|
||||
DIAG_COUNT_DEC(c, available);
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* free an object to a cache
|
||||
*/
|
||||
static inline void
|
||||
_lthread_objcache_free(struct lthread_objcache *c, void *obj)
|
||||
{
|
||||
DIAG_COUNT_INC(c, wr);
|
||||
DIAG_COUNT_INC(c, available);
|
||||
_lthread_queue_insert_mp(c->q, obj);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_OBJCACHE_H_ */
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2010-2011 Dmitry Vyukov
|
||||
*/
|
||||
|
||||
#ifndef LTHREAD_POOL_H_
|
||||
#define LTHREAD_POOL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <rte_malloc.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_log.h>
|
||||
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_diag.h"
|
||||
|
||||
/*
|
||||
* This file implements pool of queue nodes used by the queue implemented
|
||||
* in lthread_queue.h.
|
||||
*
|
||||
* The pool is an intrusive lock free MPSC queue.
|
||||
*
|
||||
* The pool is created empty and populated lazily, i.e. on first attempt to
|
||||
* allocate a the pool.
|
||||
*
|
||||
* Whenever the pool is empty more nodes are added to the pool
|
||||
* The number of nodes preallocated in this way is a parameter of
|
||||
* _qnode_pool_create. Freeing an object returns it to the pool.
|
||||
*
|
||||
* Each lthread scheduler maintains its own pool of nodes. L-threads must always
|
||||
* allocate from this local pool ( because it is a single consumer queue ).
|
||||
* L-threads can free nodes to any pool (because it is a multi producer queue)
|
||||
* This enables threads that have affined to a different scheduler to free
|
||||
* nodes safely.
|
||||
*/
|
||||
|
||||
struct qnode;
|
||||
struct qnode_cache;
|
||||
|
||||
/*
|
||||
* define intermediate node
|
||||
*/
|
||||
struct qnode {
|
||||
struct qnode *next;
|
||||
void *data;
|
||||
struct qnode_pool *pool;
|
||||
} __rte_cache_aligned;
|
||||
|
||||
/*
|
||||
* a pool structure
|
||||
*/
|
||||
struct qnode_pool {
|
||||
struct qnode *head;
|
||||
struct qnode *stub;
|
||||
struct qnode *fast_alloc;
|
||||
struct qnode *tail __rte_cache_aligned;
|
||||
int pre_alloc;
|
||||
char name[LT_MAX_NAME_SIZE];
|
||||
|
||||
DIAG_COUNT_DEFINE(rd);
|
||||
DIAG_COUNT_DEFINE(wr);
|
||||
DIAG_COUNT_DEFINE(available);
|
||||
DIAG_COUNT_DEFINE(prealloc);
|
||||
DIAG_COUNT_DEFINE(capacity);
|
||||
} __rte_cache_aligned;
|
||||
|
||||
/*
|
||||
* Create a pool of qnodes
|
||||
*/
|
||||
|
||||
static inline struct qnode_pool *
|
||||
_qnode_pool_create(const char *name, int prealloc_size) {
|
||||
|
||||
struct qnode_pool *p = rte_malloc_socket(NULL,
|
||||
sizeof(struct qnode_pool),
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
|
||||
RTE_ASSERT(p);
|
||||
|
||||
p->stub = rte_malloc_socket(NULL,
|
||||
sizeof(struct qnode),
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
|
||||
RTE_ASSERT(p->stub);
|
||||
|
||||
if (name != NULL)
|
||||
strncpy(p->name, name, LT_MAX_NAME_SIZE);
|
||||
p->name[sizeof(p->name)-1] = 0;
|
||||
|
||||
p->stub->pool = p;
|
||||
p->stub->next = NULL;
|
||||
p->tail = p->stub;
|
||||
p->head = p->stub;
|
||||
p->pre_alloc = prealloc_size;
|
||||
|
||||
DIAG_COUNT_INIT(p, rd);
|
||||
DIAG_COUNT_INIT(p, wr);
|
||||
DIAG_COUNT_INIT(p, available);
|
||||
DIAG_COUNT_INIT(p, prealloc);
|
||||
DIAG_COUNT_INIT(p, capacity);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Insert a node into the pool
|
||||
*/
|
||||
static __rte_always_inline void
|
||||
_qnode_pool_insert(struct qnode_pool *p, struct qnode *n)
|
||||
{
|
||||
n->next = NULL;
|
||||
struct qnode *prev = n;
|
||||
/* We insert at the head */
|
||||
prev = (struct qnode *) __sync_lock_test_and_set((uint64_t *)&p->head,
|
||||
(uint64_t) prev);
|
||||
/* there is a window of inconsistency until prev next is set */
|
||||
/* which is why remove must retry */
|
||||
prev->next = (n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a node from the pool
|
||||
*
|
||||
* There is a race with _qnode_pool_insert() whereby the queue could appear
|
||||
* empty during a concurrent insert, this is handled by retrying
|
||||
*
|
||||
* The queue uses a stub node, which must be swung as the queue becomes
|
||||
* empty, this requires an insert of the stub, which means that removing the
|
||||
* last item from the queue incurs the penalty of an atomic exchange. Since the
|
||||
* pool is maintained with a bulk pre-allocation the cost of this is amortised.
|
||||
*/
|
||||
static __rte_always_inline struct qnode *
|
||||
_pool_remove(struct qnode_pool *p)
|
||||
{
|
||||
struct qnode *head;
|
||||
struct qnode *tail = p->tail;
|
||||
struct qnode *next = tail->next;
|
||||
|
||||
/* we remove from the tail */
|
||||
if (tail == p->stub) {
|
||||
if (next == NULL)
|
||||
return NULL;
|
||||
/* advance the tail */
|
||||
p->tail = next;
|
||||
tail = next;
|
||||
next = next->next;
|
||||
}
|
||||
if (likely(next != NULL)) {
|
||||
p->tail = next;
|
||||
return tail;
|
||||
}
|
||||
|
||||
head = p->head;
|
||||
if (tail == head)
|
||||
return NULL;
|
||||
|
||||
/* swing stub node */
|
||||
_qnode_pool_insert(p, p->stub);
|
||||
|
||||
next = tail->next;
|
||||
if (next) {
|
||||
p->tail = next;
|
||||
return tail;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This adds a retry to the _pool_remove function
|
||||
* defined above
|
||||
*/
|
||||
static __rte_always_inline struct qnode *
|
||||
_qnode_pool_remove(struct qnode_pool *p)
|
||||
{
|
||||
struct qnode *n;
|
||||
|
||||
do {
|
||||
n = _pool_remove(p);
|
||||
if (likely(n != NULL))
|
||||
return n;
|
||||
|
||||
rte_compiler_barrier();
|
||||
} while ((p->head != p->tail) &&
|
||||
(p->tail != p->stub));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a node from the pool
|
||||
* If the pool is empty add mode nodes
|
||||
*/
|
||||
static __rte_always_inline struct qnode *
|
||||
_qnode_alloc(void)
|
||||
{
|
||||
struct qnode_pool *p = (THIS_SCHED)->qnode_pool;
|
||||
int prealloc_size = p->pre_alloc;
|
||||
struct qnode *n;
|
||||
int i;
|
||||
|
||||
if (likely(p->fast_alloc != NULL)) {
|
||||
n = p->fast_alloc;
|
||||
p->fast_alloc = NULL;
|
||||
return n;
|
||||
}
|
||||
|
||||
n = _qnode_pool_remove(p);
|
||||
|
||||
if (unlikely(n == NULL)) {
|
||||
DIAG_COUNT_INC(p, prealloc);
|
||||
for (i = 0; i < prealloc_size; i++) {
|
||||
n = rte_malloc_socket(NULL,
|
||||
sizeof(struct qnode),
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
|
||||
DIAG_COUNT_INC(p, available);
|
||||
DIAG_COUNT_INC(p, capacity);
|
||||
|
||||
n->pool = p;
|
||||
_qnode_pool_insert(p, n);
|
||||
}
|
||||
n = _qnode_pool_remove(p);
|
||||
}
|
||||
n->pool = p;
|
||||
DIAG_COUNT_INC(p, rd);
|
||||
DIAG_COUNT_DEC(p, available);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* free a queue node to the per scheduler pool from which it came
|
||||
*/
|
||||
static __rte_always_inline void
|
||||
_qnode_free(struct qnode *n)
|
||||
{
|
||||
struct qnode_pool *p = n->pool;
|
||||
|
||||
|
||||
if (unlikely(p->fast_alloc != NULL) ||
|
||||
unlikely(n->pool != (THIS_SCHED)->qnode_pool)) {
|
||||
DIAG_COUNT_INC(p, wr);
|
||||
DIAG_COUNT_INC(p, available);
|
||||
_qnode_pool_insert(p, n);
|
||||
return;
|
||||
}
|
||||
p->fast_alloc = n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an qnode pool
|
||||
* queue must be empty when this is called
|
||||
*/
|
||||
static inline int
|
||||
_qnode_pool_destroy(struct qnode_pool *p)
|
||||
{
|
||||
rte_free(p->stub);
|
||||
rte_free(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_POOL_H_ */
|
@ -1,247 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2010-2011 Dmitry Vyukov
|
||||
*/
|
||||
|
||||
#ifndef LTHREAD_QUEUE_H_
|
||||
#define LTHREAD_QUEUE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_per_lcore.h>
|
||||
|
||||
#include "lthread_int.h"
|
||||
#include "lthread.h"
|
||||
#include "lthread_diag.h"
|
||||
#include "lthread_pool.h"
|
||||
|
||||
struct lthread_queue;
|
||||
|
||||
/*
|
||||
* This file implements an unbounded FIFO queue based on a lock free
|
||||
* linked list.
|
||||
*
|
||||
* The queue is non-intrusive in that it uses intermediate nodes, and does
|
||||
* not require these nodes to be inserted into the object being placed
|
||||
* in the queue.
|
||||
*
|
||||
* This is slightly more efficient than the very similar queue in lthread_pool
|
||||
* in that it does not have to swing a stub node as the queue becomes empty.
|
||||
*
|
||||
* The queue access functions allocate and free intermediate node
|
||||
* transparently from/to a per scheduler pool ( see lthread_pool.h ).
|
||||
*
|
||||
* The queue provides both MPSC and SPSC insert methods
|
||||
*/
|
||||
|
||||
/*
|
||||
* define a queue of lthread nodes
|
||||
*/
|
||||
struct lthread_queue {
|
||||
struct qnode *head;
|
||||
struct qnode *tail __rte_cache_aligned;
|
||||
struct lthread_queue *p;
|
||||
char name[LT_MAX_NAME_SIZE];
|
||||
|
||||
DIAG_COUNT_DEFINE(rd);
|
||||
DIAG_COUNT_DEFINE(wr);
|
||||
DIAG_COUNT_DEFINE(size);
|
||||
|
||||
} __rte_cache_aligned;
|
||||
|
||||
|
||||
|
||||
static inline struct lthread_queue *
|
||||
_lthread_queue_create(const char *name)
|
||||
{
|
||||
struct qnode *stub;
|
||||
struct lthread_queue *new_queue;
|
||||
|
||||
new_queue = rte_malloc_socket(NULL, sizeof(struct lthread_queue),
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
if (new_queue == NULL)
|
||||
return NULL;
|
||||
|
||||
/* allocated stub node */
|
||||
stub = _qnode_alloc();
|
||||
RTE_ASSERT(stub);
|
||||
|
||||
if (name != NULL)
|
||||
strncpy(new_queue->name, name, sizeof(new_queue->name));
|
||||
new_queue->name[sizeof(new_queue->name)-1] = 0;
|
||||
|
||||
/* initialize queue as empty */
|
||||
stub->next = NULL;
|
||||
new_queue->head = stub;
|
||||
new_queue->tail = stub;
|
||||
|
||||
DIAG_COUNT_INIT(new_queue, rd);
|
||||
DIAG_COUNT_INIT(new_queue, wr);
|
||||
DIAG_COUNT_INIT(new_queue, size);
|
||||
|
||||
return new_queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the queue is empty
|
||||
*/
|
||||
static __rte_always_inline int
|
||||
_lthread_queue_empty(struct lthread_queue *q)
|
||||
{
|
||||
return q->tail == q->head;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Destroy a queue
|
||||
* fail if queue is not empty
|
||||
*/
|
||||
static inline int _lthread_queue_destroy(struct lthread_queue *q)
|
||||
{
|
||||
if (q == NULL)
|
||||
return -1;
|
||||
|
||||
if (!_lthread_queue_empty(q))
|
||||
return -1;
|
||||
|
||||
_qnode_free(q->head);
|
||||
rte_free(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
RTE_DECLARE_PER_LCORE(struct lthread_sched *, this_sched);
|
||||
|
||||
/*
|
||||
* Insert a node into a queue
|
||||
* this implementation is multi producer safe
|
||||
*/
|
||||
static __rte_always_inline struct qnode *
|
||||
_lthread_queue_insert_mp(struct lthread_queue
|
||||
*q, void *data)
|
||||
{
|
||||
struct qnode *prev;
|
||||
struct qnode *n = _qnode_alloc();
|
||||
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
|
||||
/* set object in node */
|
||||
n->data = data;
|
||||
n->next = NULL;
|
||||
|
||||
/* this is an MPSC method, perform a locked update */
|
||||
prev = n;
|
||||
prev =
|
||||
(struct qnode *)__sync_lock_test_and_set((uint64_t *) &(q)->head,
|
||||
(uint64_t) prev);
|
||||
/* there is a window of inconsistency until prev next is set,
|
||||
* which is why remove must retry
|
||||
*/
|
||||
prev->next = n;
|
||||
|
||||
DIAG_COUNT_INC(q, wr);
|
||||
DIAG_COUNT_INC(q, size);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert an node into a queue in single producer mode
|
||||
* this implementation is NOT mult producer safe
|
||||
*/
|
||||
static __rte_always_inline struct qnode *
|
||||
_lthread_queue_insert_sp(struct lthread_queue
|
||||
*q, void *data)
|
||||
{
|
||||
/* allocate a queue node */
|
||||
struct qnode *prev;
|
||||
struct qnode *n = _qnode_alloc();
|
||||
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
|
||||
/* set data in node */
|
||||
n->data = data;
|
||||
n->next = NULL;
|
||||
|
||||
/* this is an SPSC method, no need for locked exchange operation */
|
||||
prev = q->head;
|
||||
prev->next = q->head = n;
|
||||
|
||||
DIAG_COUNT_INC(q, wr);
|
||||
DIAG_COUNT_INC(q, size);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a node from a queue
|
||||
*/
|
||||
static __rte_always_inline void *
|
||||
_lthread_queue_poll(struct lthread_queue *q)
|
||||
{
|
||||
void *data = NULL;
|
||||
struct qnode *tail = q->tail;
|
||||
struct qnode *next = (struct qnode *)tail->next;
|
||||
/*
|
||||
* There is a small window of inconsistency between producer and
|
||||
* consumer whereby the queue may appear empty if consumer and
|
||||
* producer access it at the same time.
|
||||
* The consumer must handle this by retrying
|
||||
*/
|
||||
|
||||
if (likely(next != NULL)) {
|
||||
q->tail = next;
|
||||
tail->data = next->data;
|
||||
data = tail->data;
|
||||
|
||||
/* free the node */
|
||||
_qnode_free(tail);
|
||||
|
||||
DIAG_COUNT_INC(q, rd);
|
||||
DIAG_COUNT_DEC(q, size);
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a node from a queue
|
||||
*/
|
||||
static __rte_always_inline void *
|
||||
_lthread_queue_remove(struct lthread_queue *q)
|
||||
{
|
||||
void *data = NULL;
|
||||
|
||||
/*
|
||||
* There is a small window of inconsistency between producer and
|
||||
* consumer whereby the queue may appear empty if consumer and
|
||||
* producer access it at the same time. We handle this by retrying
|
||||
*/
|
||||
do {
|
||||
data = _lthread_queue_poll(q);
|
||||
|
||||
if (likely(data != NULL)) {
|
||||
|
||||
DIAG_COUNT_INC(q, rd);
|
||||
DIAG_COUNT_DEC(q, size);
|
||||
return data;
|
||||
}
|
||||
rte_compiler_barrier();
|
||||
} while (unlikely(!_lthread_queue_empty(q)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_QUEUE_H_ */
|
@ -1,540 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
|
||||
#define RTE_MEM 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_common.h>
|
||||
#include <rte_branch_prediction.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_sched.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_timer.h"
|
||||
#include "lthread_mutex.h"
|
||||
#include "lthread_cond.h"
|
||||
#include "lthread_tls.h"
|
||||
#include "lthread_diag.h"
|
||||
|
||||
/*
|
||||
* This file implements the lthread scheduler
|
||||
* The scheduler is the function lthread_run()
|
||||
* This must be run as the main loop of an EAL thread.
|
||||
*
|
||||
* Currently once a scheduler is created it cannot be destroyed
|
||||
* When a scheduler shuts down it is assumed that the application is terminating
|
||||
*/
|
||||
|
||||
static uint16_t num_schedulers;
|
||||
static uint16_t active_schedulers;
|
||||
|
||||
/* one scheduler per lcore */
|
||||
RTE_DEFINE_PER_LCORE(struct lthread_sched *, this_sched) = NULL;
|
||||
|
||||
struct lthread_sched *schedcore[LTHREAD_MAX_LCORES];
|
||||
|
||||
diag_callback diag_cb;
|
||||
|
||||
uint64_t diag_mask;
|
||||
|
||||
|
||||
/* constructor */
|
||||
RTE_INIT(lthread_sched_ctor)
|
||||
{
|
||||
memset(schedcore, 0, sizeof(schedcore));
|
||||
__atomic_store_n(&num_schedulers, 1, __ATOMIC_RELAXED);
|
||||
__atomic_store_n(&active_schedulers, 0, __ATOMIC_RELAXED);
|
||||
diag_cb = NULL;
|
||||
}
|
||||
|
||||
|
||||
enum sched_alloc_phase {
|
||||
SCHED_ALLOC_OK,
|
||||
SCHED_ALLOC_QNODE_POOL,
|
||||
SCHED_ALLOC_READY_QUEUE,
|
||||
SCHED_ALLOC_PREADY_QUEUE,
|
||||
SCHED_ALLOC_LTHREAD_CACHE,
|
||||
SCHED_ALLOC_STACK_CACHE,
|
||||
SCHED_ALLOC_PERLT_CACHE,
|
||||
SCHED_ALLOC_TLS_CACHE,
|
||||
SCHED_ALLOC_COND_CACHE,
|
||||
SCHED_ALLOC_MUTEX_CACHE,
|
||||
};
|
||||
|
||||
static int
|
||||
_lthread_sched_alloc_resources(struct lthread_sched *new_sched)
|
||||
{
|
||||
int alloc_status;
|
||||
|
||||
do {
|
||||
/* Initialize per scheduler queue node pool */
|
||||
alloc_status = SCHED_ALLOC_QNODE_POOL;
|
||||
new_sched->qnode_pool =
|
||||
_qnode_pool_create("qnode pool", LTHREAD_PREALLOC);
|
||||
if (new_sched->qnode_pool == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local ready queue */
|
||||
alloc_status = SCHED_ALLOC_READY_QUEUE;
|
||||
new_sched->ready = _lthread_queue_create("ready queue");
|
||||
if (new_sched->ready == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local peer ready queue */
|
||||
alloc_status = SCHED_ALLOC_PREADY_QUEUE;
|
||||
new_sched->pready = _lthread_queue_create("pready queue");
|
||||
if (new_sched->pready == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local free lthread cache */
|
||||
alloc_status = SCHED_ALLOC_LTHREAD_CACHE;
|
||||
new_sched->lthread_cache =
|
||||
_lthread_objcache_create("lthread cache",
|
||||
sizeof(struct lthread),
|
||||
LTHREAD_PREALLOC);
|
||||
if (new_sched->lthread_cache == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local free stack cache */
|
||||
alloc_status = SCHED_ALLOC_STACK_CACHE;
|
||||
new_sched->stack_cache =
|
||||
_lthread_objcache_create("stack_cache",
|
||||
sizeof(struct lthread_stack),
|
||||
LTHREAD_PREALLOC);
|
||||
if (new_sched->stack_cache == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local free per lthread data cache */
|
||||
alloc_status = SCHED_ALLOC_PERLT_CACHE;
|
||||
new_sched->per_lthread_cache =
|
||||
_lthread_objcache_create("per_lt cache",
|
||||
RTE_PER_LTHREAD_SECTION_SIZE,
|
||||
LTHREAD_PREALLOC);
|
||||
if (new_sched->per_lthread_cache == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local free tls cache */
|
||||
alloc_status = SCHED_ALLOC_TLS_CACHE;
|
||||
new_sched->tls_cache =
|
||||
_lthread_objcache_create("TLS cache",
|
||||
sizeof(struct lthread_tls),
|
||||
LTHREAD_PREALLOC);
|
||||
if (new_sched->tls_cache == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local free cond var cache */
|
||||
alloc_status = SCHED_ALLOC_COND_CACHE;
|
||||
new_sched->cond_cache =
|
||||
_lthread_objcache_create("cond cache",
|
||||
sizeof(struct lthread_cond),
|
||||
LTHREAD_PREALLOC);
|
||||
if (new_sched->cond_cache == NULL)
|
||||
break;
|
||||
|
||||
/* Initialize per scheduler local free mutex cache */
|
||||
alloc_status = SCHED_ALLOC_MUTEX_CACHE;
|
||||
new_sched->mutex_cache =
|
||||
_lthread_objcache_create("mutex cache",
|
||||
sizeof(struct lthread_mutex),
|
||||
LTHREAD_PREALLOC);
|
||||
if (new_sched->mutex_cache == NULL)
|
||||
break;
|
||||
|
||||
alloc_status = SCHED_ALLOC_OK;
|
||||
} while (0);
|
||||
|
||||
/* roll back on any failure */
|
||||
switch (alloc_status) {
|
||||
case SCHED_ALLOC_MUTEX_CACHE:
|
||||
_lthread_objcache_destroy(new_sched->cond_cache);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_COND_CACHE:
|
||||
_lthread_objcache_destroy(new_sched->tls_cache);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_TLS_CACHE:
|
||||
_lthread_objcache_destroy(new_sched->per_lthread_cache);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_PERLT_CACHE:
|
||||
_lthread_objcache_destroy(new_sched->stack_cache);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_STACK_CACHE:
|
||||
_lthread_objcache_destroy(new_sched->lthread_cache);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_LTHREAD_CACHE:
|
||||
_lthread_queue_destroy(new_sched->pready);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_PREADY_QUEUE:
|
||||
_lthread_queue_destroy(new_sched->ready);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_READY_QUEUE:
|
||||
_qnode_pool_destroy(new_sched->qnode_pool);
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_QNODE_POOL:
|
||||
/* fall through */
|
||||
case SCHED_ALLOC_OK:
|
||||
break;
|
||||
}
|
||||
return alloc_status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a scheduler on the current lcore
|
||||
*/
|
||||
struct lthread_sched *_lthread_sched_create(size_t stack_size)
|
||||
{
|
||||
int status;
|
||||
struct lthread_sched *new_sched;
|
||||
unsigned lcoreid = rte_lcore_id();
|
||||
|
||||
RTE_ASSERT(stack_size <= LTHREAD_MAX_STACK_SIZE);
|
||||
|
||||
if (stack_size == 0)
|
||||
stack_size = LTHREAD_MAX_STACK_SIZE;
|
||||
|
||||
new_sched =
|
||||
rte_calloc_socket(NULL, 1, sizeof(struct lthread_sched),
|
||||
RTE_CACHE_LINE_SIZE,
|
||||
rte_socket_id());
|
||||
if (new_sched == NULL) {
|
||||
RTE_LOG(CRIT, LTHREAD,
|
||||
"Failed to allocate memory for scheduler\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_lthread_key_pool_init();
|
||||
|
||||
new_sched->stack_size = stack_size;
|
||||
new_sched->birth = rte_rdtsc();
|
||||
THIS_SCHED = new_sched;
|
||||
|
||||
status = _lthread_sched_alloc_resources(new_sched);
|
||||
if (status != SCHED_ALLOC_OK) {
|
||||
RTE_LOG(CRIT, LTHREAD,
|
||||
"Failed to allocate resources for scheduler code = %d\n",
|
||||
status);
|
||||
rte_free(new_sched);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(&new_sched->ctx, sizeof(struct ctx));
|
||||
|
||||
new_sched->lcore_id = lcoreid;
|
||||
|
||||
schedcore[lcoreid] = new_sched;
|
||||
|
||||
new_sched->run_flag = 1;
|
||||
|
||||
DIAG_EVENT(new_sched, LT_DIAG_SCHED_CREATE, rte_lcore_id(), 0);
|
||||
|
||||
rte_wmb();
|
||||
return new_sched;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the number of schedulers in the system
|
||||
*/
|
||||
int lthread_num_schedulers_set(int num)
|
||||
{
|
||||
__atomic_store_n(&num_schedulers, num, __ATOMIC_RELAXED);
|
||||
return (int)__atomic_load_n(&num_schedulers, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of schedulers active
|
||||
*/
|
||||
int lthread_active_schedulers(void)
|
||||
{
|
||||
return (int)__atomic_load_n(&active_schedulers, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* shutdown the scheduler running on the specified lcore
|
||||
*/
|
||||
void lthread_scheduler_shutdown(unsigned lcoreid)
|
||||
{
|
||||
uint64_t coreid = (uint64_t) lcoreid;
|
||||
|
||||
if (coreid < LTHREAD_MAX_LCORES) {
|
||||
if (schedcore[coreid] != NULL)
|
||||
schedcore[coreid]->run_flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shutdown all schedulers
|
||||
*/
|
||||
void lthread_scheduler_shutdown_all(void)
|
||||
{
|
||||
uint64_t i;
|
||||
|
||||
/*
|
||||
* give time for all schedulers to have started
|
||||
* Note we use sched_yield() rather than pthread_yield() to allow
|
||||
* for the possibility of a pthread wrapper on lthread_yield(),
|
||||
* something that is not possible unless the scheduler is running.
|
||||
*/
|
||||
while (__atomic_load_n(&active_schedulers, __ATOMIC_RELAXED) <
|
||||
__atomic_load_n(&num_schedulers, __ATOMIC_RELAXED))
|
||||
sched_yield();
|
||||
|
||||
for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
|
||||
if (schedcore[i] != NULL)
|
||||
schedcore[i]->run_flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resume a suspended lthread
|
||||
*/
|
||||
static __rte_always_inline void
|
||||
_lthread_resume(struct lthread *lt);
|
||||
static inline void _lthread_resume(struct lthread *lt)
|
||||
{
|
||||
struct lthread_sched *sched = THIS_SCHED;
|
||||
struct lthread_stack *s;
|
||||
uint64_t state = lt->state;
|
||||
#if LTHREAD_DIAG
|
||||
int init = 0;
|
||||
#endif
|
||||
|
||||
sched->current_lthread = lt;
|
||||
|
||||
if (state & (BIT(ST_LT_CANCELLED) | BIT(ST_LT_EXITED))) {
|
||||
/* if detached we can free the thread now */
|
||||
if (state & BIT(ST_LT_DETACH)) {
|
||||
_lthread_free(lt);
|
||||
sched->current_lthread = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (state & BIT(ST_LT_INIT)) {
|
||||
/* first time this thread has been run */
|
||||
/* assign thread to this scheduler */
|
||||
lt->sched = THIS_SCHED;
|
||||
|
||||
/* allocate stack */
|
||||
s = _stack_alloc();
|
||||
|
||||
lt->stack_container = s;
|
||||
_lthread_set_stack(lt, s->stack, s->stack_size);
|
||||
|
||||
/* allocate memory for TLS used by this thread */
|
||||
_lthread_tls_alloc(lt);
|
||||
|
||||
lt->state = BIT(ST_LT_READY);
|
||||
#if LTHREAD_DIAG
|
||||
init = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_RESUMED, init, lt);
|
||||
|
||||
/* switch to the new thread */
|
||||
ctx_switch(<->ctx, &sched->ctx);
|
||||
|
||||
/* If posting to a queue that could be read by another lcore
|
||||
* we defer the queue write till now to ensure the context has been
|
||||
* saved before the other core tries to resume it
|
||||
* This applies to blocking on mutex, cond, and to set_affinity
|
||||
*/
|
||||
if (lt->pending_wr_queue != NULL) {
|
||||
struct lthread_queue *dest = lt->pending_wr_queue;
|
||||
|
||||
lt->pending_wr_queue = NULL;
|
||||
|
||||
/* queue the current thread to the specified queue */
|
||||
_lthread_queue_insert_mp(dest, lt);
|
||||
}
|
||||
|
||||
sched->current_lthread = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle sleep timer expiry
|
||||
*/
|
||||
void
|
||||
_sched_timer_cb(struct rte_timer *tim, void *arg)
|
||||
{
|
||||
struct lthread *lt = (struct lthread *) arg;
|
||||
uint64_t state = lt->state;
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_TMR_EXPIRED, <->tim, 0);
|
||||
|
||||
rte_timer_stop(tim);
|
||||
|
||||
if (lt->state & BIT(ST_LT_CANCELLED))
|
||||
(THIS_SCHED)->nb_blocked_threads--;
|
||||
|
||||
lt->state = state | BIT(ST_LT_EXPIRED);
|
||||
_lthread_resume(lt);
|
||||
lt->state = state & CLEARBIT(ST_LT_EXPIRED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Returns 0 if there is a pending job in scheduler or 1 if done and can exit.
|
||||
*/
|
||||
static inline int _lthread_sched_isdone(struct lthread_sched *sched)
|
||||
{
|
||||
return (sched->run_flag == 0) &&
|
||||
(_lthread_queue_empty(sched->ready)) &&
|
||||
(_lthread_queue_empty(sched->pready)) &&
|
||||
(sched->nb_blocked_threads == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for all schedulers to start
|
||||
*/
|
||||
static inline void _lthread_schedulers_sync_start(void)
|
||||
{
|
||||
__atomic_fetch_add(&active_schedulers, 1, __ATOMIC_RELAXED);
|
||||
|
||||
/* wait for lthread schedulers
|
||||
* Note we use sched_yield() rather than pthread_yield() to allow
|
||||
* for the possibility of a pthread wrapper on lthread_yield(),
|
||||
* something that is not possible unless the scheduler is running.
|
||||
*/
|
||||
while (__atomic_load_n(&active_schedulers, __ATOMIC_RELAXED) <
|
||||
__atomic_load_n(&num_schedulers, __ATOMIC_RELAXED))
|
||||
sched_yield();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for all schedulers to stop
|
||||
*/
|
||||
static inline void _lthread_schedulers_sync_stop(void)
|
||||
{
|
||||
__atomic_fetch_sub(&active_schedulers, 1, __ATOMIC_RELAXED);
|
||||
__atomic_fetch_sub(&num_schedulers, 1, __ATOMIC_RELAXED);
|
||||
|
||||
/* wait for schedulers
|
||||
* Note we use sched_yield() rather than pthread_yield() to allow
|
||||
* for the possibility of a pthread wrapper on lthread_yield(),
|
||||
* something that is not possible unless the scheduler is running.
|
||||
*/
|
||||
while (__atomic_load_n(&active_schedulers, __ATOMIC_RELAXED) > 0)
|
||||
sched_yield();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run the lthread scheduler
|
||||
* This loop is the heart of the system
|
||||
*/
|
||||
void lthread_run(void)
|
||||
{
|
||||
|
||||
struct lthread_sched *sched = THIS_SCHED;
|
||||
struct lthread *lt = NULL;
|
||||
|
||||
RTE_LOG(INFO, LTHREAD,
|
||||
"starting scheduler %p on lcore %u phys core %u\n",
|
||||
sched, rte_lcore_id(),
|
||||
rte_lcore_index(rte_lcore_id()));
|
||||
|
||||
/* if more than one, wait for all schedulers to start */
|
||||
_lthread_schedulers_sync_start();
|
||||
|
||||
|
||||
/*
|
||||
* This is the main scheduling loop
|
||||
* So long as there are tasks in existence we run this loop.
|
||||
* We check for:-
|
||||
* expired timers,
|
||||
* the local ready queue,
|
||||
* and the peer ready queue,
|
||||
*
|
||||
* and resume lthreads ad infinitum.
|
||||
*/
|
||||
while (!_lthread_sched_isdone(sched)) {
|
||||
|
||||
rte_timer_manage();
|
||||
|
||||
lt = _lthread_queue_poll(sched->ready);
|
||||
if (lt != NULL)
|
||||
_lthread_resume(lt);
|
||||
lt = _lthread_queue_poll(sched->pready);
|
||||
if (lt != NULL)
|
||||
_lthread_resume(lt);
|
||||
}
|
||||
|
||||
|
||||
/* if more than one wait for all schedulers to stop */
|
||||
_lthread_schedulers_sync_stop();
|
||||
|
||||
(THIS_SCHED) = NULL;
|
||||
|
||||
RTE_LOG(INFO, LTHREAD,
|
||||
"stopping scheduler %p on lcore %u phys core %u\n",
|
||||
sched, rte_lcore_id(),
|
||||
rte_lcore_index(rte_lcore_id()));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the scheduler for this lcore
|
||||
*
|
||||
*/
|
||||
struct lthread_sched *_lthread_sched_get(unsigned int lcore_id)
|
||||
{
|
||||
struct lthread_sched *res = NULL;
|
||||
|
||||
if (lcore_id < LTHREAD_MAX_LCORES)
|
||||
res = schedcore[lcore_id];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* migrate the current thread to another scheduler running
|
||||
* on the specified lcore.
|
||||
*/
|
||||
int lthread_set_affinity(unsigned lcoreid)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
struct lthread_sched *dest_sched;
|
||||
|
||||
if (unlikely(lcoreid >= LTHREAD_MAX_LCORES))
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_AFFINITY, lcoreid, 0);
|
||||
|
||||
dest_sched = schedcore[lcoreid];
|
||||
|
||||
if (unlikely(dest_sched == NULL))
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
if (likely(dest_sched != THIS_SCHED)) {
|
||||
lt->sched = dest_sched;
|
||||
lt->pending_wr_queue = dest_sched->pready;
|
||||
_affinitize();
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright 2015 Intel Corporation.
|
||||
* Copyright 2012 Hasan Alayli <halayli@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef LTHREAD_SCHED_H_
|
||||
#define LTHREAD_SCHED_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_queue.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_diag.h"
|
||||
#include "ctx.h"
|
||||
|
||||
/*
|
||||
* insert an lthread into a queue
|
||||
*/
|
||||
static inline void
|
||||
_ready_queue_insert(struct lthread_sched *sched, struct lthread *lt)
|
||||
{
|
||||
if (sched == THIS_SCHED)
|
||||
_lthread_queue_insert_sp((THIS_SCHED)->ready, lt);
|
||||
else
|
||||
_lthread_queue_insert_mp(sched->pready, lt);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove an lthread from a queue
|
||||
*/
|
||||
static inline struct lthread *_ready_queue_remove(struct lthread_queue *q)
|
||||
{
|
||||
return _lthread_queue_remove(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the ready queue is empty
|
||||
*/
|
||||
static inline int _ready_queue_empty(struct lthread_queue *q)
|
||||
{
|
||||
return _lthread_queue_empty(q);
|
||||
}
|
||||
|
||||
static inline uint64_t _sched_now(void)
|
||||
{
|
||||
uint64_t now = rte_rdtsc();
|
||||
|
||||
if (now > (THIS_SCHED)->birth)
|
||||
return now - (THIS_SCHED)->birth;
|
||||
if (now < (THIS_SCHED)->birth)
|
||||
return (THIS_SCHED)->birth - now;
|
||||
/* never return 0 because this means sleep forever */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __rte_always_inline void
|
||||
_affinitize(void);
|
||||
static inline void
|
||||
_affinitize(void)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_SUSPENDED, 0, 0);
|
||||
ctx_switch(&(THIS_SCHED)->ctx, <->ctx);
|
||||
}
|
||||
|
||||
static __rte_always_inline void
|
||||
_suspend(void);
|
||||
static inline void
|
||||
_suspend(void)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
(THIS_SCHED)->nb_blocked_threads++;
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_SUSPENDED, 0, 0);
|
||||
ctx_switch(&(THIS_SCHED)->ctx, <->ctx);
|
||||
(THIS_SCHED)->nb_blocked_threads--;
|
||||
}
|
||||
|
||||
static __rte_always_inline void
|
||||
_reschedule(void);
|
||||
static inline void
|
||||
_reschedule(void)
|
||||
{
|
||||
struct lthread *lt = THIS_LTHREAD;
|
||||
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_RESCHEDULED, 0, 0);
|
||||
_ready_queue_insert(THIS_SCHED, lt);
|
||||
ctx_switch(&(THIS_SCHED)->ctx, <->ctx);
|
||||
}
|
||||
|
||||
extern struct lthread_sched *schedcore[];
|
||||
void _sched_timer_cb(struct rte_timer *tim, void *arg);
|
||||
void _sched_shutdown(__rte_unused void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_SCHED_H_ */
|
@ -1,68 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LTHREAD_TIMER_H_
|
||||
#define LTHREAD_TIMER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_int.h"
|
||||
#include "lthread_sched.h"
|
||||
|
||||
|
||||
static inline uint64_t
|
||||
_ns_to_clks(uint64_t ns)
|
||||
{
|
||||
/*
|
||||
* clkns needs to be divided by 1E9 to get ns clocks. However,
|
||||
* dividing by this first would lose a lot of accuracy.
|
||||
* Dividing after a multiply by ns, could cause overflow of
|
||||
* uint64_t if ns is about 5 seconds [if we assume a max tsc
|
||||
* rate of 4GHz]. Therefore we first divide by 1E4, then
|
||||
* multiply and finally divide by 1E5. This allows ns to be
|
||||
* values many hours long, without overflow, while still keeping
|
||||
* reasonable accuracy.
|
||||
*/
|
||||
uint64_t clkns = rte_get_tsc_hz() / 1e4;
|
||||
|
||||
clkns *= ns;
|
||||
clkns /= 1e5;
|
||||
|
||||
return clkns;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
_timer_start(struct lthread *lt, uint64_t clks)
|
||||
{
|
||||
if (clks > 0) {
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_TMR_START, <->tim, clks);
|
||||
rte_timer_init(<->tim);
|
||||
rte_timer_reset(<->tim,
|
||||
clks,
|
||||
SINGLE,
|
||||
rte_lcore_id(),
|
||||
_sched_timer_cb,
|
||||
(void *)lt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
_timer_stop(struct lthread *lt)
|
||||
{
|
||||
if (lt != NULL) {
|
||||
DIAG_EVENT(lt, LT_DIAG_LTHREAD_TMR_DELETE, <->tim, 0);
|
||||
rte_timer_stop(<->tim);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_TIMER_H_ */
|
@ -1,223 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <rte_malloc.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_ring.h>
|
||||
|
||||
#include "lthread_tls.h"
|
||||
#include "lthread_queue.h"
|
||||
#include "lthread_objcache.h"
|
||||
#include "lthread_sched.h"
|
||||
|
||||
static struct rte_ring *key_pool;
|
||||
static uint64_t key_pool_init;
|
||||
|
||||
/* needed to cause section start and end to be defined */
|
||||
RTE_DEFINE_PER_LTHREAD(void *, dummy);
|
||||
|
||||
static struct lthread_key key_table[LTHREAD_MAX_KEYS];
|
||||
|
||||
RTE_INIT(thread_tls_ctor)
|
||||
{
|
||||
key_pool = NULL;
|
||||
key_pool_init = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a pool of keys
|
||||
* These are unique tokens that can be obtained by threads
|
||||
* calling lthread_key_create()
|
||||
*/
|
||||
void _lthread_key_pool_init(void)
|
||||
{
|
||||
static struct rte_ring *pool;
|
||||
struct lthread_key *new_key;
|
||||
char name[MAX_LTHREAD_NAME_SIZE];
|
||||
|
||||
bzero(key_table, sizeof(key_table));
|
||||
|
||||
uint64_t pool_init = 0;
|
||||
/* only one lcore should do this */
|
||||
if (__atomic_compare_exchange_n(&key_pool_init, &pool_init, 1, 0,
|
||||
__ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
|
||||
snprintf(name,
|
||||
MAX_LTHREAD_NAME_SIZE,
|
||||
"lthread_key_pool_%d",
|
||||
getpid());
|
||||
|
||||
pool = rte_ring_create(name,
|
||||
LTHREAD_MAX_KEYS, 0, 0);
|
||||
RTE_ASSERT(pool);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 1; i < LTHREAD_MAX_KEYS; i++) {
|
||||
new_key = &key_table[i];
|
||||
rte_ring_mp_enqueue((struct rte_ring *)pool,
|
||||
(void *)new_key);
|
||||
}
|
||||
key_pool = pool;
|
||||
}
|
||||
/* other lcores wait here till done */
|
||||
while (key_pool == NULL) {
|
||||
rte_compiler_barrier();
|
||||
sched_yield();
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a key
|
||||
* this means getting a key from the pool
|
||||
*/
|
||||
int lthread_key_create(unsigned int *key, tls_destructor_func destructor)
|
||||
{
|
||||
if (key == NULL)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
struct lthread_key *new_key;
|
||||
|
||||
if (rte_ring_mc_dequeue((struct rte_ring *)key_pool, (void **)&new_key)
|
||||
== 0) {
|
||||
new_key->destructor = destructor;
|
||||
*key = (new_key - key_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return POSIX_ERRNO(EAGAIN);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delete a key
|
||||
*/
|
||||
int lthread_key_delete(unsigned int k)
|
||||
{
|
||||
struct lthread_key *key;
|
||||
|
||||
key = (struct lthread_key *) &key_table[k];
|
||||
|
||||
if (k > LTHREAD_MAX_KEYS)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
key->destructor = NULL;
|
||||
rte_ring_mp_enqueue((struct rte_ring *)key_pool,
|
||||
(void *)key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Break association for all keys in use by this thread
|
||||
* invoke the destructor if available.
|
||||
* Since a destructor can create keys we could enter an infinite loop
|
||||
* therefore we give up after LTHREAD_DESTRUCTOR_ITERATIONS
|
||||
* the behavior is modelled on pthread
|
||||
*/
|
||||
void _lthread_tls_destroy(struct lthread *lt)
|
||||
{
|
||||
int i, k;
|
||||
int nb_keys;
|
||||
void *data;
|
||||
|
||||
for (i = 0; i < LTHREAD_DESTRUCTOR_ITERATIONS; i++) {
|
||||
|
||||
for (k = 1; k < LTHREAD_MAX_KEYS; k++) {
|
||||
|
||||
/* no keys in use ? */
|
||||
nb_keys = lt->tls->nb_keys_inuse;
|
||||
if (nb_keys == 0)
|
||||
return;
|
||||
|
||||
/* this key not in use ? */
|
||||
if (lt->tls->data[k] == NULL)
|
||||
continue;
|
||||
|
||||
/* remove this key */
|
||||
data = lt->tls->data[k];
|
||||
lt->tls->data[k] = NULL;
|
||||
lt->tls->nb_keys_inuse = nb_keys-1;
|
||||
|
||||
/* invoke destructor */
|
||||
if (key_table[k].destructor != NULL)
|
||||
key_table[k].destructor(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the pointer associated with a key
|
||||
* If the key is no longer valid return NULL
|
||||
*/
|
||||
void
|
||||
*lthread_getspecific(unsigned int k)
|
||||
{
|
||||
void *res = NULL;
|
||||
|
||||
if (k < LTHREAD_MAX_KEYS)
|
||||
res = THIS_LTHREAD->tls->data[k];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a value against a key
|
||||
* If the key is no longer valid return an error
|
||||
* when storing value
|
||||
*/
|
||||
int lthread_setspecific(unsigned int k, const void *data)
|
||||
{
|
||||
if (k >= LTHREAD_MAX_KEYS)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
int n = THIS_LTHREAD->tls->nb_keys_inuse;
|
||||
|
||||
/* discard const qualifier */
|
||||
char *p = (char *) (uintptr_t) data;
|
||||
|
||||
|
||||
if (data != NULL) {
|
||||
if (THIS_LTHREAD->tls->data[k] == NULL)
|
||||
THIS_LTHREAD->tls->nb_keys_inuse = n+1;
|
||||
}
|
||||
|
||||
THIS_LTHREAD->tls->data[k] = (void *) p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate data for TLS cache
|
||||
*/
|
||||
void _lthread_tls_alloc(struct lthread *lt)
|
||||
{
|
||||
struct lthread_tls *tls;
|
||||
|
||||
tls = _lthread_objcache_alloc((THIS_SCHED)->tls_cache);
|
||||
|
||||
RTE_ASSERT(tls != NULL);
|
||||
|
||||
tls->root_sched = (THIS_SCHED);
|
||||
lt->tls = tls;
|
||||
|
||||
/* allocate data for TLS variables using RTE_PER_LTHREAD macros */
|
||||
if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE) {
|
||||
lt->per_lthread_data =
|
||||
_lthread_objcache_alloc((THIS_SCHED)->per_lthread_cache);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef LTHREAD_TLS_H_
|
||||
#define LTHREAD_TLS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lthread_api.h"
|
||||
|
||||
#define RTE_PER_LTHREAD_SECTION_SIZE \
|
||||
(&__stop_per_lt - &__start_per_lt)
|
||||
|
||||
struct lthread_key {
|
||||
tls_destructor_func destructor;
|
||||
};
|
||||
|
||||
struct lthread_tls {
|
||||
void *data[LTHREAD_MAX_KEYS];
|
||||
int nb_keys_inuse;
|
||||
struct lthread_sched *root_sched;
|
||||
};
|
||||
|
||||
void _lthread_tls_destroy(struct lthread *lt);
|
||||
void _lthread_key_pool_init(void);
|
||||
void _lthread_tls_alloc(struct lthread *lt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LTHREAD_TLS_H_ */
|
@ -1,54 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2010-2020 Intel Corporation
|
||||
|
||||
# binary name
|
||||
APP = l3fwd-thread
|
||||
|
||||
# all source are stored in SRCS-y
|
||||
SRCS-y := main.c
|
||||
|
||||
include ../common/common.mk
|
||||
|
||||
ifeq ($(MAKECMDGOALS),static)
|
||||
# check for broken pkg-config
|
||||
ifeq ($(shell echo $(LDFLAGS_STATIC) | grep 'whole-archive.*l:lib.*no-whole-archive'),)
|
||||
$(warning "pkg-config output list does not contain drivers between 'whole-archive'/'no-whole-archive' flags.")
|
||||
$(error "Cannot generate statically-linked binaries with this version of pkg-config")
|
||||
endif
|
||||
endif
|
||||
|
||||
PKGCONF ?= pkg-config
|
||||
|
||||
CFLAGS += -DALLOW_EXPERIMENTAL_API
|
||||
|
||||
# Build using pkg-config variables if possible
|
||||
ifneq ($(shell $(PKGCONF) --exists libdpdk && echo 0),0)
|
||||
$(error "no installation of DPDK found")
|
||||
endif
|
||||
|
||||
all: shared
|
||||
.PHONY: shared static
|
||||
shared: build/$(APP)-shared
|
||||
ln -sf $(APP)-shared build/$(APP)
|
||||
static: build/$(APP)-static
|
||||
ln -sf $(APP)-static build/$(APP)
|
||||
|
||||
|
||||
PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null)
|
||||
CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)
|
||||
LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
|
||||
LDFLAGS_STATIC = $(shell $(PKGCONF) --static --libs libdpdk)
|
||||
|
||||
build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
|
||||
|
||||
build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
|
||||
|
||||
build:
|
||||
@mkdir -p $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
|
||||
test -d build && rmdir -p build || true
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2019 Intel Corporation
|
||||
|
||||
# meson file, for building this example as part of a main DPDK build.
|
||||
#
|
||||
# To build this example as a standalone application with an already-installed
|
||||
# DPDK instance, use 'make'
|
||||
|
||||
build = dpdk_conf.has('RTE_ARCH_X86_64')
|
||||
if not build
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
deps += ['timer', 'lpm']
|
||||
allow_experimental_apis = true
|
||||
|
||||
# get the performance thread (pt) architecture subdir
|
||||
if dpdk_conf.has('RTE_ARCH_ARM64')
|
||||
pt_arch_dir = '../common/arch/arm64'
|
||||
else
|
||||
pt_arch_dir = '../common/arch/x86'
|
||||
endif
|
||||
sources += files('main.c',
|
||||
'../common/lthread.c',
|
||||
'../common/lthread_cond.c',
|
||||
'../common/lthread_diag.c',
|
||||
'../common/lthread_mutex.c',
|
||||
'../common/lthread_sched.c',
|
||||
'../common/lthread_tls.c',
|
||||
pt_arch_dir + '/ctx.c')
|
||||
|
||||
includes += include_directories('../common', pt_arch_dir)
|
@ -1,150 +0,0 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
case "$1" in
|
||||
|
||||
######################
|
||||
# 1 L-core per pcore #
|
||||
######################
|
||||
|
||||
"1.1")
|
||||
echo "1.1 1 L-core per pcore (N=2)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(1,0,0,0)" \
|
||||
--tx="(1,0)" \
|
||||
--stat-lcore 2 \
|
||||
--no-lthread
|
||||
|
||||
;;
|
||||
|
||||
"1.2")
|
||||
echo "1.2 1 L-core per pcore (N=4)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(1,0,1,1)" \
|
||||
--tx="(2,0)(3,1)" \
|
||||
--stat-lcore 4 \
|
||||
--no-lthread
|
||||
;;
|
||||
|
||||
"1.3")
|
||||
echo "1.3 1 L-core per pcore (N=8)"
|
||||
|
||||
./build/l3fwd-thread -c 1ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(0,1,1,1)(1,0,2,2)(1,1,3,3)" \
|
||||
--tx="(4,0)(5,1)(6,2)(7,3)" \
|
||||
--stat-lcore 8 \
|
||||
--no-lthread
|
||||
;;
|
||||
|
||||
"1.4")
|
||||
echo "1.3 1 L-core per pcore (N=16)"
|
||||
|
||||
./build/l3fwd-thread -c 3ffff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(0,1,1,1)(0,2,2,2)(0,3,3,3)(1,0,4,4)(1,1,5,5)(1,2,6,6)(1,3,7,7)" \
|
||||
--tx="(8,0)(9,1)(10,2)(11,3)(12,4)(13,5)(14,6)(15,7)" \
|
||||
--stat-lcore 16 \
|
||||
--no-lthread
|
||||
;;
|
||||
|
||||
|
||||
######################
|
||||
# N L-core per pcore #
|
||||
######################
|
||||
|
||||
"2.1")
|
||||
echo "2.1 N L-core per pcore (N=2)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 --lcores="2,(0-1)@0" -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(1,0,0,0)" \
|
||||
--tx="(1,0)" \
|
||||
--stat-lcore 2 \
|
||||
--no-lthread
|
||||
|
||||
;;
|
||||
|
||||
"2.2")
|
||||
echo "2.2 N L-core per pcore (N=4)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 --lcores="(0-3)@0,4" -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(1,0,1,1)" \
|
||||
--tx="(2,0)(3,1)" \
|
||||
--stat-lcore 4 \
|
||||
--no-lthread
|
||||
;;
|
||||
|
||||
"2.3")
|
||||
echo "2.3 N L-core per pcore (N=8)"
|
||||
|
||||
./build/l3fwd-thread -c 3ffff -n 2 --lcores="(0-7)@0,8" -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(0,1,1,1)(1,0,2,2)(1,1,3,3)" \
|
||||
--tx="(4,0)(5,1)(6,2)(7,3)" \
|
||||
--stat-lcore 8 \
|
||||
--no-lthread
|
||||
;;
|
||||
|
||||
"2.4")
|
||||
echo "2.3 N L-core per pcore (N=16)"
|
||||
|
||||
./build/l3fwd-thread -c 3ffff -n 2 --lcores="(0-15)@0,16" -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(0,1,1,1)(0,2,2,2)(0,3,3,3)(1,0,4,4)(1,1,5,5)(1,2,6,6)(1,3,7,7)" \
|
||||
--tx="(8,0)(9,1)(10,2)(11,3)(12,4)(13,5)(14,6)(15,7)" \
|
||||
--stat-lcore 16 \
|
||||
--no-lthread
|
||||
;;
|
||||
|
||||
|
||||
#########################
|
||||
# N L-threads per pcore #
|
||||
#########################
|
||||
|
||||
"3.1")
|
||||
echo "3.1 N L-threads per pcore (N=2)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(1,0,0,0)" \
|
||||
--tx="(0,0)" \
|
||||
--stat-lcore 1
|
||||
;;
|
||||
|
||||
"3.2")
|
||||
echo "3.2 N L-threads per pcore (N=4)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(1,0,0,1)" \
|
||||
--tx="(0,0)(0,1)" \
|
||||
--stat-lcore 1
|
||||
;;
|
||||
|
||||
"3.3")
|
||||
echo "3.2 N L-threads per pcore (N=8)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(0,1,0,1)(1,0,0,2)(1,1,0,3)" \
|
||||
--tx="(0,0)(0,1)(0,2)(0,3)" \
|
||||
--stat-lcore 1
|
||||
;;
|
||||
|
||||
"3.4")
|
||||
echo "3.2 N L-threads per pcore (N=16)"
|
||||
|
||||
./build/l3fwd-thread -c ff -n 2 -- -P -p 3 \
|
||||
--max-pkt-len 1500 \
|
||||
--rx="(0,0,0,0)(0,1,0,1)(0,2,0,2)(0,0,0,3)(1,0,0,4)(1,1,0,5)(1,2,0,6)(1,3,0,7)" \
|
||||
--tx="(0,0)(0,1)(0,2)(0,3)(0,4)(0,5)(0,6)(0,7)" \
|
||||
--stat-lcore 1
|
||||
;;
|
||||
|
||||
esac
|
@ -1,63 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2010-2020 Intel Corporation
|
||||
|
||||
# binary name
|
||||
APP = lthread_pthread_shim
|
||||
|
||||
# all source are stored in SRCS-y
|
||||
SRCS-y := main.c pthread_shim.c
|
||||
|
||||
include ../common/common.mk
|
||||
|
||||
ifeq ($(MAKECMDGOALS),static)
|
||||
# check for broken pkg-config
|
||||
ifeq ($(shell echo $(LDFLAGS_STATIC) | grep 'whole-archive.*l:lib.*no-whole-archive'),)
|
||||
$(warning "pkg-config output list does not contain drivers between 'whole-archive'/'no-whole-archive' flags.")
|
||||
$(error "Cannot generate statically-linked binaries with this version of pkg-config")
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS += -DALLOW_EXPERIMENTAL_API
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
LDFLAGS += "-Wl,--copy-dt-needed-entries"
|
||||
|
||||
PKGCONF ?= pkg-config
|
||||
|
||||
# Build using pkg-config variables if possible
|
||||
ifneq ($(shell $(PKGCONF) --exists libdpdk && echo 0),0)
|
||||
$(error "no installation of DPDK found")
|
||||
endif
|
||||
|
||||
all: shared
|
||||
.PHONY: shared static
|
||||
shared: build/$(APP)-shared
|
||||
ln -sf $(APP)-shared build/$(APP)
|
||||
static: build/$(APP)-static
|
||||
ln -sf $(APP)-static build/$(APP)
|
||||
|
||||
LDFLAGS += -lpthread
|
||||
|
||||
PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null)
|
||||
CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)
|
||||
LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
|
||||
LDFLAGS_STATIC = $(shell $(PKGCONF) --static --libs libdpdk)
|
||||
|
||||
build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
|
||||
|
||||
build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
|
||||
|
||||
# workaround for a gcc bug with noreturn attribute
|
||||
# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
|
||||
ifeq ($(shell gcc -dumpversion),-gt 0)
|
||||
CFLAGS_main.o += -Wno-return-type
|
||||
endif
|
||||
|
||||
build:
|
||||
@mkdir -p $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
|
||||
test -d build && rmdir -p build || true
|
@ -1,271 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <rte_common.h>
|
||||
#include <rte_lcore.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_timer.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "lthread_diag_api.h"
|
||||
#include "pthread_shim.h"
|
||||
|
||||
#define DEBUG_APP 0
|
||||
#define HELLOW_WORLD_MAX_LTHREADS 10
|
||||
#define THREAD_NAME_LEN 16
|
||||
|
||||
#ifndef __GLIBC__ /* sched_getcpu() is glibc-specific */
|
||||
#define sched_getcpu() rte_lcore_id()
|
||||
#endif
|
||||
|
||||
__thread int print_count;
|
||||
__thread pthread_mutex_t print_lock;
|
||||
|
||||
__thread pthread_mutex_t exit_lock;
|
||||
__thread pthread_cond_t exit_cond;
|
||||
|
||||
/*
|
||||
* A simple thread that demonstrates use of a mutex, a condition
|
||||
* variable, thread local storage, explicit yield, and thread exit.
|
||||
*
|
||||
* The thread uses a mutex to protect a shared counter which is incremented
|
||||
* and then it waits on condition variable before exiting.
|
||||
*
|
||||
* The thread argument is stored in and retrieved from TLS, using
|
||||
* the pthread key create, get and set specific APIs.
|
||||
*
|
||||
* The thread yields while holding the mutex, to provide opportunity
|
||||
* for other threads to contend.
|
||||
*
|
||||
* All of the pthread API functions used by this thread are actually
|
||||
* resolved to corresponding lthread functions by the pthread shim
|
||||
* implemented in pthread_shim.c
|
||||
*/
|
||||
void *helloworld_pthread(void *arg);
|
||||
void *helloworld_pthread(void *arg)
|
||||
{
|
||||
pthread_key_t key;
|
||||
|
||||
/* create a key for TLS */
|
||||
pthread_key_create(&key, NULL);
|
||||
|
||||
/* store the arg in TLS */
|
||||
pthread_setspecific(key, arg);
|
||||
|
||||
/* grab lock and increment shared counter */
|
||||
pthread_mutex_lock(&print_lock);
|
||||
print_count++;
|
||||
|
||||
/* yield thread to give opportunity for lock contention */
|
||||
sched_yield();
|
||||
|
||||
/* retrieve arg from TLS */
|
||||
uint64_t thread_no = (uint64_t) pthread_getspecific(key);
|
||||
|
||||
printf("Hello - lcore = %d count = %d thread_no = %d thread_id = %p\n",
|
||||
sched_getcpu(),
|
||||
print_count,
|
||||
(int) thread_no,
|
||||
(void *)pthread_self());
|
||||
|
||||
/* release the lock */
|
||||
pthread_mutex_unlock(&print_lock);
|
||||
|
||||
/*
|
||||
* wait on condition variable
|
||||
* before exiting
|
||||
*/
|
||||
pthread_mutex_lock(&exit_lock);
|
||||
pthread_cond_wait(&exit_cond, &exit_lock);
|
||||
pthread_mutex_unlock(&exit_lock);
|
||||
|
||||
/* exit */
|
||||
pthread_exit((void *) thread_no);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is the initial thread
|
||||
*
|
||||
* It demonstrates pthread, mutex and condition variable creation,
|
||||
* broadcast and pthread join APIs.
|
||||
*
|
||||
* This initial thread must always start life as an lthread.
|
||||
*
|
||||
* This thread creates many more threads then waits a short time
|
||||
* before signalling them to exit using a broadcast.
|
||||
*
|
||||
* All of the pthread API functions used by this thread are actually
|
||||
* resolved to corresponding lthread functions by the pthread shim
|
||||
* implemented in pthread_shim.c
|
||||
*
|
||||
* After all threads have finished the lthread scheduler is shutdown
|
||||
* and normal pthread operation is restored
|
||||
*/
|
||||
__thread pthread_t tid[HELLOW_WORLD_MAX_LTHREADS];
|
||||
|
||||
static void *initial_lthread(void *args __rte_unused)
|
||||
{
|
||||
int lcore = (int) rte_lcore_id();
|
||||
/*
|
||||
*
|
||||
* We can now enable pthread API override
|
||||
* and start to use the pthread APIs
|
||||
*/
|
||||
pthread_override_set(1);
|
||||
|
||||
uint64_t i;
|
||||
int ret;
|
||||
|
||||
/* initialize mutex for shared counter */
|
||||
print_count = 0;
|
||||
pthread_mutex_init(&print_lock, NULL);
|
||||
|
||||
/* initialize mutex and condition variable controlling thread exit */
|
||||
pthread_mutex_init(&exit_lock, NULL);
|
||||
pthread_cond_init(&exit_cond, NULL);
|
||||
|
||||
/* spawn a number of threads */
|
||||
for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
|
||||
|
||||
/*
|
||||
* Not strictly necessary but
|
||||
* for the sake of this example
|
||||
* use an attribute to pass the desired lcore
|
||||
*/
|
||||
pthread_attr_t attr;
|
||||
rte_cpuset_t cpuset;
|
||||
char name[THREAD_NAME_LEN];
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(lcore, &cpuset);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setaffinity_np(&attr, sizeof(rte_cpuset_t), &cpuset);
|
||||
|
||||
/* create the thread */
|
||||
ret = pthread_create(&tid[i], &attr,
|
||||
helloworld_pthread, (void *) i);
|
||||
if (ret != 0)
|
||||
rte_exit(EXIT_FAILURE, "Cannot create helloworld thread\n");
|
||||
|
||||
snprintf(name, sizeof(name), "helloworld-%u", (uint32_t)i);
|
||||
rte_thread_setname(tid[i], name);
|
||||
}
|
||||
|
||||
/* wait for 1s to allow threads
|
||||
* to block on the condition variable
|
||||
* N.B. nanosleep() is resolved to lthread_sleep()
|
||||
* by the shim.
|
||||
*/
|
||||
struct timespec time;
|
||||
|
||||
time.tv_sec = 1;
|
||||
time.tv_nsec = 0;
|
||||
nanosleep(&time, NULL);
|
||||
|
||||
/* wake up all the threads */
|
||||
pthread_cond_broadcast(&exit_cond);
|
||||
|
||||
/* wait for them to finish */
|
||||
for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
|
||||
|
||||
uint64_t thread_no;
|
||||
|
||||
pthread_join(tid[i], (void *) &thread_no);
|
||||
if (thread_no != i)
|
||||
printf("error on thread exit\n");
|
||||
}
|
||||
|
||||
pthread_cond_destroy(&exit_cond);
|
||||
pthread_mutex_destroy(&print_lock);
|
||||
pthread_mutex_destroy(&exit_lock);
|
||||
|
||||
/* shutdown the lthread scheduler */
|
||||
lthread_scheduler_shutdown(rte_lcore_id());
|
||||
lthread_detach();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This thread creates a single initial lthread
|
||||
* and then runs the scheduler
|
||||
* An instance of this thread is created on each thread
|
||||
* in the core mask
|
||||
*/
|
||||
static int
|
||||
lthread_scheduler(void *args __rte_unused)
|
||||
{
|
||||
/* create initial thread */
|
||||
struct lthread *lt;
|
||||
|
||||
lthread_create(<, -1, initial_lthread, (void *) NULL);
|
||||
|
||||
/* run the lthread scheduler */
|
||||
lthread_run();
|
||||
|
||||
/* restore genuine pthread operation */
|
||||
pthread_override_set(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int num_sched = 0;
|
||||
|
||||
/* basic DPDK initialization is all that is necessary to run lthreads*/
|
||||
int ret = rte_eal_init(argc, argv);
|
||||
|
||||
if (ret < 0)
|
||||
rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
|
||||
|
||||
/* enable timer subsystem */
|
||||
rte_timer_subsystem_init();
|
||||
|
||||
#if DEBUG_APP
|
||||
lthread_diagnostic_set_mask(LT_DIAG_ALL);
|
||||
#endif
|
||||
|
||||
/* create a scheduler on every core in the core mask
|
||||
* and launch an initial lthread that will spawn many more.
|
||||
*/
|
||||
unsigned lcore_id;
|
||||
|
||||
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
|
||||
if (rte_lcore_is_enabled(lcore_id))
|
||||
num_sched++;
|
||||
}
|
||||
|
||||
/* set the number of schedulers, this forces all schedulers synchronize
|
||||
* before entering their main loop
|
||||
*/
|
||||
lthread_num_schedulers_set(num_sched);
|
||||
|
||||
/* launch all threads */
|
||||
rte_eal_mp_remote_launch(lthread_scheduler, (void *)NULL, CALL_MAIN);
|
||||
|
||||
/* wait for threads to stop */
|
||||
RTE_LCORE_FOREACH_WORKER(lcore_id) {
|
||||
rte_eal_wait_lcore(lcore_id);
|
||||
}
|
||||
|
||||
/* clean up the EAL */
|
||||
rte_eal_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2019 Intel Corporation
|
||||
|
||||
# meson file, for building this example as part of a main DPDK build.
|
||||
#
|
||||
# To build this example as a standalone application with an already-installed
|
||||
# DPDK instance, use 'make'
|
||||
|
||||
build = dpdk_conf.has('RTE_ARCH_X86_64') or dpdk_conf.has('RTE_ARCH_ARM64')
|
||||
if not build
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
deps += ['timer']
|
||||
allow_experimental_apis = true
|
||||
|
||||
# get the performance thread (pt) architecture subdir
|
||||
if dpdk_conf.has('RTE_ARCH_ARM64')
|
||||
pt_arch_dir = '../common/arch/arm64'
|
||||
else
|
||||
pt_arch_dir = '../common/arch/x86'
|
||||
endif
|
||||
sources += files('main.c',
|
||||
'pthread_shim.c',
|
||||
'../common/lthread.c',
|
||||
'../common/lthread_cond.c',
|
||||
'../common/lthread_diag.c',
|
||||
'../common/lthread_mutex.c',
|
||||
'../common/lthread_sched.c',
|
||||
'../common/lthread_tls.c',
|
||||
pt_arch_dir + '/ctx.c')
|
||||
|
||||
includes += include_directories('../common', pt_arch_dir)
|
@ -1,713 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <rte_log.h>
|
||||
|
||||
#include "lthread_api.h"
|
||||
#include "pthread_shim.h"
|
||||
|
||||
#define RTE_LOGTYPE_PTHREAD_SHIM RTE_LOGTYPE_USER3
|
||||
|
||||
#define POSIX_ERRNO(x) (x)
|
||||
|
||||
/* some releases of FreeBSD 10, e.g. 10.0, don't have CPU_COUNT macro */
|
||||
#ifndef CPU_COUNT
|
||||
#define CPU_COUNT(x) __cpu_count(x)
|
||||
|
||||
static inline unsigned int
|
||||
__cpu_count(const rte_cpuset_t *cpuset)
|
||||
{
|
||||
unsigned int i, count = 0;
|
||||
for (i = 0; i < RTE_MAX_LCORE; i++)
|
||||
if (CPU_ISSET(i, cpuset))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* this flag determines at run time if we override pthread
|
||||
* calls and map then to equivalent lthread calls
|
||||
* or of we call the standard pthread function
|
||||
*/
|
||||
static __thread int override;
|
||||
|
||||
|
||||
/*
|
||||
* this structures contains function pointers that will be
|
||||
* initialised to the loaded address of the real
|
||||
* pthread library API functions
|
||||
*/
|
||||
struct pthread_lib_funcs {
|
||||
int (*f_pthread_barrier_destroy)
|
||||
(pthread_barrier_t *);
|
||||
int (*f_pthread_barrier_init)
|
||||
(pthread_barrier_t *, const pthread_barrierattr_t *, unsigned);
|
||||
int (*f_pthread_barrier_wait)
|
||||
(pthread_barrier_t *);
|
||||
int (*f_pthread_cond_broadcast)
|
||||
(pthread_cond_t *);
|
||||
int (*f_pthread_cond_destroy)
|
||||
(pthread_cond_t *);
|
||||
int (*f_pthread_cond_init)
|
||||
(pthread_cond_t *, const pthread_condattr_t *);
|
||||
int (*f_pthread_cond_signal)
|
||||
(pthread_cond_t *);
|
||||
int (*f_pthread_cond_timedwait)
|
||||
(pthread_cond_t *, pthread_mutex_t *, const struct timespec *);
|
||||
int (*f_pthread_cond_wait)
|
||||
(pthread_cond_t *, pthread_mutex_t *);
|
||||
int (*f_pthread_create)
|
||||
(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);
|
||||
int (*f_pthread_detach)
|
||||
(pthread_t);
|
||||
int (*f_pthread_equal)
|
||||
(pthread_t, pthread_t);
|
||||
void (*f_pthread_exit)
|
||||
(void *);
|
||||
void * (*f_pthread_getspecific)
|
||||
(pthread_key_t);
|
||||
int (*f_pthread_getcpuclockid)
|
||||
(pthread_t, clockid_t *);
|
||||
int (*f_pthread_join)
|
||||
(pthread_t, void **);
|
||||
int (*f_pthread_key_create)
|
||||
(pthread_key_t *, void (*) (void *));
|
||||
int (*f_pthread_key_delete)
|
||||
(pthread_key_t);
|
||||
int (*f_pthread_mutex_destroy)
|
||||
(pthread_mutex_t *__mutex);
|
||||
int (*f_pthread_mutex_init)
|
||||
(pthread_mutex_t *__mutex, const pthread_mutexattr_t *);
|
||||
int (*f_pthread_mutex_lock)
|
||||
(pthread_mutex_t *__mutex);
|
||||
int (*f_pthread_mutex_trylock)
|
||||
(pthread_mutex_t *__mutex);
|
||||
int (*f_pthread_mutex_timedlock)
|
||||
(pthread_mutex_t *__mutex, const struct timespec *);
|
||||
int (*f_pthread_mutex_unlock)
|
||||
(pthread_mutex_t *__mutex);
|
||||
int (*f_pthread_once)
|
||||
(pthread_once_t *, void (*) (void));
|
||||
int (*f_pthread_rwlock_destroy)
|
||||
(pthread_rwlock_t *__rwlock);
|
||||
int (*f_pthread_rwlock_init)
|
||||
(pthread_rwlock_t *__rwlock, const pthread_rwlockattr_t *);
|
||||
int (*f_pthread_rwlock_rdlock)
|
||||
(pthread_rwlock_t *__rwlock);
|
||||
int (*f_pthread_rwlock_timedrdlock)
|
||||
(pthread_rwlock_t *__rwlock, const struct timespec *);
|
||||
int (*f_pthread_rwlock_timedwrlock)
|
||||
(pthread_rwlock_t *__rwlock, const struct timespec *);
|
||||
int (*f_pthread_rwlock_tryrdlock)
|
||||
(pthread_rwlock_t *__rwlock);
|
||||
int (*f_pthread_rwlock_trywrlock)
|
||||
(pthread_rwlock_t *__rwlock);
|
||||
int (*f_pthread_rwlock_unlock)
|
||||
(pthread_rwlock_t *__rwlock);
|
||||
int (*f_pthread_rwlock_wrlock)
|
||||
(pthread_rwlock_t *__rwlock);
|
||||
pthread_t (*f_pthread_self)
|
||||
(void);
|
||||
int (*f_pthread_setspecific)
|
||||
(pthread_key_t, const void *);
|
||||
int (*f_pthread_spin_init)
|
||||
(pthread_spinlock_t *__spin, int);
|
||||
int (*f_pthread_spin_destroy)
|
||||
(pthread_spinlock_t *__spin);
|
||||
int (*f_pthread_spin_lock)
|
||||
(pthread_spinlock_t *__spin);
|
||||
int (*f_pthread_spin_trylock)
|
||||
(pthread_spinlock_t *__spin);
|
||||
int (*f_pthread_spin_unlock)
|
||||
(pthread_spinlock_t *__spin);
|
||||
int (*f_pthread_cancel)
|
||||
(pthread_t);
|
||||
int (*f_pthread_setcancelstate)
|
||||
(int, int *);
|
||||
int (*f_pthread_setcanceltype)
|
||||
(int, int *);
|
||||
void (*f_pthread_testcancel)
|
||||
(void);
|
||||
int (*f_pthread_getschedparam)
|
||||
(pthread_t pthread, int *, struct sched_param *);
|
||||
int (*f_pthread_setschedparam)
|
||||
(pthread_t, int, const struct sched_param *);
|
||||
int (*f_pthread_yield)
|
||||
(void);
|
||||
int (*f_pthread_setaffinity_np)
|
||||
(pthread_t thread, size_t cpusetsize, const rte_cpuset_t *cpuset);
|
||||
int (*f_nanosleep)
|
||||
(const struct timespec *req, struct timespec *rem);
|
||||
} _sys_pthread_funcs = {
|
||||
.f_pthread_barrier_destroy = NULL,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* this macro obtains the loaded address of a library function
|
||||
* and saves it.
|
||||
*/
|
||||
static void *__libc_dl_handle = RTLD_NEXT;
|
||||
|
||||
#define get_addr_of_loaded_symbol(name) do { \
|
||||
char *error_str; \
|
||||
_sys_pthread_funcs.f_##name = dlsym(__libc_dl_handle, (#name)); \
|
||||
error_str = dlerror(); \
|
||||
if (error_str != NULL) { \
|
||||
fprintf(stderr, "%s\n", error_str); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* The constructor function initialises the
|
||||
* function pointers for pthread library functions
|
||||
*/
|
||||
RTE_INIT(pthread_intercept_ctor)
|
||||
{
|
||||
override = 0;
|
||||
/*
|
||||
* Get the original functions
|
||||
*/
|
||||
get_addr_of_loaded_symbol(pthread_barrier_destroy);
|
||||
get_addr_of_loaded_symbol(pthread_barrier_init);
|
||||
get_addr_of_loaded_symbol(pthread_barrier_wait);
|
||||
get_addr_of_loaded_symbol(pthread_cond_broadcast);
|
||||
get_addr_of_loaded_symbol(pthread_cond_destroy);
|
||||
get_addr_of_loaded_symbol(pthread_cond_init);
|
||||
get_addr_of_loaded_symbol(pthread_cond_signal);
|
||||
get_addr_of_loaded_symbol(pthread_cond_timedwait);
|
||||
get_addr_of_loaded_symbol(pthread_cond_wait);
|
||||
get_addr_of_loaded_symbol(pthread_create);
|
||||
get_addr_of_loaded_symbol(pthread_detach);
|
||||
get_addr_of_loaded_symbol(pthread_equal);
|
||||
get_addr_of_loaded_symbol(pthread_exit);
|
||||
get_addr_of_loaded_symbol(pthread_getspecific);
|
||||
get_addr_of_loaded_symbol(pthread_getcpuclockid);
|
||||
get_addr_of_loaded_symbol(pthread_join);
|
||||
get_addr_of_loaded_symbol(pthread_key_create);
|
||||
get_addr_of_loaded_symbol(pthread_key_delete);
|
||||
get_addr_of_loaded_symbol(pthread_mutex_destroy);
|
||||
get_addr_of_loaded_symbol(pthread_mutex_init);
|
||||
get_addr_of_loaded_symbol(pthread_mutex_lock);
|
||||
get_addr_of_loaded_symbol(pthread_mutex_trylock);
|
||||
get_addr_of_loaded_symbol(pthread_mutex_timedlock);
|
||||
get_addr_of_loaded_symbol(pthread_mutex_unlock);
|
||||
get_addr_of_loaded_symbol(pthread_once);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_destroy);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_init);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_rdlock);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_timedrdlock);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_timedwrlock);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_tryrdlock);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_trywrlock);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_unlock);
|
||||
get_addr_of_loaded_symbol(pthread_rwlock_wrlock);
|
||||
get_addr_of_loaded_symbol(pthread_self);
|
||||
get_addr_of_loaded_symbol(pthread_setspecific);
|
||||
get_addr_of_loaded_symbol(pthread_spin_init);
|
||||
get_addr_of_loaded_symbol(pthread_spin_destroy);
|
||||
get_addr_of_loaded_symbol(pthread_spin_lock);
|
||||
get_addr_of_loaded_symbol(pthread_spin_trylock);
|
||||
get_addr_of_loaded_symbol(pthread_spin_unlock);
|
||||
get_addr_of_loaded_symbol(pthread_cancel);
|
||||
get_addr_of_loaded_symbol(pthread_setcancelstate);
|
||||
get_addr_of_loaded_symbol(pthread_setcanceltype);
|
||||
get_addr_of_loaded_symbol(pthread_testcancel);
|
||||
get_addr_of_loaded_symbol(pthread_getschedparam);
|
||||
get_addr_of_loaded_symbol(pthread_setschedparam);
|
||||
get_addr_of_loaded_symbol(pthread_yield);
|
||||
get_addr_of_loaded_symbol(pthread_setaffinity_np);
|
||||
get_addr_of_loaded_symbol(nanosleep);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Enable/Disable pthread override
|
||||
* state
|
||||
* 0 disable
|
||||
* 1 enable
|
||||
*/
|
||||
void pthread_override_set(int state)
|
||||
{
|
||||
override = state;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return pthread override state
|
||||
* return
|
||||
* 0 disable
|
||||
* 1 enable
|
||||
*/
|
||||
int pthread_override_get(void)
|
||||
{
|
||||
return override;
|
||||
}
|
||||
|
||||
/*
|
||||
* This macro is used to catch and log
|
||||
* invocation of stubs for unimplemented pthread
|
||||
* API functions.
|
||||
*/
|
||||
#define NOT_IMPLEMENTED do { \
|
||||
if (override) { \
|
||||
RTE_LOG(WARNING, \
|
||||
PTHREAD_SHIM, \
|
||||
"WARNING %s NOT IMPLEMENTED\n", \
|
||||
__func__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* pthread API override functions follow
|
||||
* Note in this example code only a subset of functions are
|
||||
* implemented.
|
||||
*
|
||||
* The stub functions provided will issue a warning log
|
||||
* message if an unimplemented function is invoked
|
||||
*
|
||||
*/
|
||||
|
||||
int pthread_barrier_destroy(pthread_barrier_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_barrier_destroy(a);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_barrier_init(pthread_barrier_t *a,
|
||||
const pthread_barrierattr_t *b, unsigned c)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_barrier_init(a, b, c);
|
||||
}
|
||||
|
||||
int pthread_barrier_wait(pthread_barrier_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_barrier_wait(a);
|
||||
}
|
||||
|
||||
int pthread_cond_broadcast(pthread_cond_t *cond)
|
||||
{
|
||||
if (override) {
|
||||
|
||||
lthread_cond_broadcast(*(struct lthread_cond **)cond);
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_cond_broadcast(cond);
|
||||
}
|
||||
|
||||
int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
||||
{
|
||||
if (override)
|
||||
return lthread_mutex_destroy(*(struct lthread_mutex **)mutex);
|
||||
return _sys_pthread_funcs.f_pthread_mutex_destroy(mutex);
|
||||
}
|
||||
|
||||
int pthread_cond_destroy(pthread_cond_t *cond)
|
||||
{
|
||||
if (override)
|
||||
return lthread_cond_destroy(*(struct lthread_cond **)cond);
|
||||
return _sys_pthread_funcs.f_pthread_cond_destroy(cond);
|
||||
}
|
||||
|
||||
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
|
||||
{
|
||||
if (override)
|
||||
return lthread_cond_init(NULL,
|
||||
(struct lthread_cond **)cond,
|
||||
(const struct lthread_condattr *) attr);
|
||||
return _sys_pthread_funcs.f_pthread_cond_init(cond, attr);
|
||||
}
|
||||
|
||||
int pthread_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
if (override) {
|
||||
lthread_cond_signal(*(struct lthread_cond **)cond);
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_cond_signal(cond);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_cond_timedwait(pthread_cond_t *__rte_restrict cond,
|
||||
pthread_mutex_t *__rte_restrict mutex,
|
||||
const struct timespec *__rte_restrict time)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_cond_timedwait(cond, mutex, time);
|
||||
}
|
||||
|
||||
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
|
||||
{
|
||||
if (override) {
|
||||
pthread_mutex_unlock(mutex);
|
||||
int rv = lthread_cond_wait(*(struct lthread_cond **)cond, 0);
|
||||
|
||||
pthread_mutex_lock(mutex);
|
||||
return rv;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_cond_wait(cond, mutex);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_create(pthread_t *__rte_restrict tid,
|
||||
const pthread_attr_t *__rte_restrict attr,
|
||||
lthread_func_t func,
|
||||
void *__rte_restrict arg)
|
||||
{
|
||||
if (override) {
|
||||
int lcore = -1;
|
||||
|
||||
if (attr != NULL) {
|
||||
/* determine CPU being requested */
|
||||
rte_cpuset_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
pthread_attr_getaffinity_np(attr,
|
||||
sizeof(rte_cpuset_t),
|
||||
&cpuset);
|
||||
|
||||
if (CPU_COUNT(&cpuset) != 1)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
for (lcore = 0; lcore < LTHREAD_MAX_LCORES; lcore++) {
|
||||
if (!CPU_ISSET(lcore, &cpuset))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lthread_create((struct lthread **)tid, lcore,
|
||||
func, arg);
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_create(tid, attr, func, arg);
|
||||
}
|
||||
|
||||
int pthread_detach(pthread_t tid)
|
||||
{
|
||||
if (override) {
|
||||
struct lthread *lt = (struct lthread *)tid;
|
||||
|
||||
if (lt == lthread_current()) {
|
||||
lthread_detach();
|
||||
return 0;
|
||||
}
|
||||
NOT_IMPLEMENTED;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_detach(tid);
|
||||
}
|
||||
|
||||
int pthread_equal(pthread_t a, pthread_t b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_equal(a, b);
|
||||
}
|
||||
|
||||
void pthread_exit_override(void *v)
|
||||
{
|
||||
if (override) {
|
||||
lthread_exit(v);
|
||||
return;
|
||||
}
|
||||
_sys_pthread_funcs.f_pthread_exit(v);
|
||||
}
|
||||
|
||||
void
|
||||
*pthread_getspecific(pthread_key_t key)
|
||||
{
|
||||
if (override)
|
||||
return lthread_getspecific((unsigned int) key);
|
||||
return _sys_pthread_funcs.f_pthread_getspecific(key);
|
||||
}
|
||||
|
||||
int pthread_getcpuclockid(pthread_t a, clockid_t *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_getcpuclockid(a, b);
|
||||
}
|
||||
|
||||
int pthread_join(pthread_t tid, void **val)
|
||||
{
|
||||
if (override)
|
||||
return lthread_join((struct lthread *)tid, val);
|
||||
return _sys_pthread_funcs.f_pthread_join(tid, val);
|
||||
}
|
||||
|
||||
int pthread_key_create(pthread_key_t *keyptr, void (*dtor) (void *))
|
||||
{
|
||||
if (override)
|
||||
return lthread_key_create((unsigned int *)keyptr, dtor);
|
||||
return _sys_pthread_funcs.f_pthread_key_create(keyptr, dtor);
|
||||
}
|
||||
|
||||
int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
if (override) {
|
||||
lthread_key_delete((unsigned int) key);
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_key_delete(key);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
|
||||
{
|
||||
if (override)
|
||||
return lthread_mutex_init(NULL,
|
||||
(struct lthread_mutex **)mutex,
|
||||
(const struct lthread_mutexattr *)attr);
|
||||
return _sys_pthread_funcs.f_pthread_mutex_init(mutex, attr);
|
||||
}
|
||||
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex)
|
||||
{
|
||||
if (override)
|
||||
return lthread_mutex_lock(*(struct lthread_mutex **)mutex);
|
||||
return _sys_pthread_funcs.f_pthread_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
int pthread_mutex_trylock(pthread_mutex_t *mutex)
|
||||
{
|
||||
if (override)
|
||||
return lthread_mutex_trylock(*(struct lthread_mutex **)mutex);
|
||||
return _sys_pthread_funcs.f_pthread_mutex_trylock(mutex);
|
||||
}
|
||||
|
||||
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_mutex_timedlock(mutex, b);
|
||||
}
|
||||
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
||||
{
|
||||
if (override)
|
||||
return lthread_mutex_unlock(*(struct lthread_mutex **)mutex);
|
||||
return _sys_pthread_funcs.f_pthread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
int pthread_once(pthread_once_t *a, void (b) (void))
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_once(a, b);
|
||||
}
|
||||
|
||||
int pthread_rwlock_destroy(pthread_rwlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_destroy(a);
|
||||
}
|
||||
|
||||
int pthread_rwlock_init(pthread_rwlock_t *a, const pthread_rwlockattr_t *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_init(a, b);
|
||||
}
|
||||
|
||||
int pthread_rwlock_rdlock(pthread_rwlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_rdlock(a);
|
||||
}
|
||||
|
||||
int pthread_rwlock_timedrdlock(pthread_rwlock_t *a, const struct timespec *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_timedrdlock(a, b);
|
||||
}
|
||||
|
||||
int pthread_rwlock_timedwrlock(pthread_rwlock_t *a, const struct timespec *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_timedwrlock(a, b);
|
||||
}
|
||||
|
||||
int pthread_rwlock_tryrdlock(pthread_rwlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_tryrdlock(a);
|
||||
}
|
||||
|
||||
int pthread_rwlock_trywrlock(pthread_rwlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_trywrlock(a);
|
||||
}
|
||||
|
||||
int pthread_rwlock_unlock(pthread_rwlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_unlock(a);
|
||||
}
|
||||
|
||||
int pthread_rwlock_wrlock(pthread_rwlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_rwlock_wrlock(a);
|
||||
}
|
||||
|
||||
#ifdef RTE_EXEC_ENV_LINUX
|
||||
int
|
||||
pthread_yield(void)
|
||||
{
|
||||
if (override) {
|
||||
lthread_yield();
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_yield();
|
||||
}
|
||||
#else
|
||||
void
|
||||
pthread_yield(void)
|
||||
{
|
||||
if (override)
|
||||
lthread_yield();
|
||||
else
|
||||
_sys_pthread_funcs.f_pthread_yield();
|
||||
}
|
||||
#endif
|
||||
|
||||
pthread_t pthread_self(void)
|
||||
{
|
||||
if (override)
|
||||
return (pthread_t) lthread_current();
|
||||
return _sys_pthread_funcs.f_pthread_self();
|
||||
}
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void *data)
|
||||
{
|
||||
if (override) {
|
||||
int rv = lthread_setspecific((unsigned int)key, data);
|
||||
return rv;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_setspecific(key, data);
|
||||
}
|
||||
|
||||
int pthread_spin_init(pthread_spinlock_t *a, int b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_spin_init(a, b);
|
||||
}
|
||||
|
||||
int pthread_spin_destroy(pthread_spinlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_spin_destroy(a);
|
||||
}
|
||||
|
||||
int pthread_spin_lock(pthread_spinlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_spin_lock(a);
|
||||
}
|
||||
|
||||
int pthread_spin_trylock(pthread_spinlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_spin_trylock(a);
|
||||
}
|
||||
|
||||
int pthread_spin_unlock(pthread_spinlock_t *a)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_spin_unlock(a);
|
||||
}
|
||||
|
||||
int pthread_cancel(pthread_t tid)
|
||||
{
|
||||
if (override) {
|
||||
lthread_cancel(*(struct lthread **)tid);
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_cancel(tid);
|
||||
}
|
||||
|
||||
int pthread_setcancelstate(int a, int *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_setcancelstate(a, b);
|
||||
}
|
||||
|
||||
int pthread_setcanceltype(int a, int *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_setcanceltype(a, b);
|
||||
}
|
||||
|
||||
void pthread_testcancel(void)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_testcancel();
|
||||
}
|
||||
|
||||
|
||||
int pthread_getschedparam(pthread_t tid, int *a, struct sched_param *b)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_getschedparam(tid, a, b);
|
||||
}
|
||||
|
||||
int pthread_setschedparam(pthread_t a, int b, const struct sched_param *c)
|
||||
{
|
||||
NOT_IMPLEMENTED;
|
||||
return _sys_pthread_funcs.f_pthread_setschedparam(a, b, c);
|
||||
}
|
||||
|
||||
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem)
|
||||
{
|
||||
if (override) {
|
||||
uint64_t ns = req->tv_sec * 1000000000 + req->tv_nsec;
|
||||
|
||||
lthread_sleep(ns);
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_nanosleep(req, rem);
|
||||
}
|
||||
|
||||
int
|
||||
pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
|
||||
const rte_cpuset_t *cpuset)
|
||||
{
|
||||
if (override) {
|
||||
/* we only allow affinity with a single CPU */
|
||||
if (CPU_COUNT(cpuset) != 1)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
/* we only allow the current thread to sets its own affinity */
|
||||
struct lthread *lt = (struct lthread *)thread;
|
||||
|
||||
if (lthread_current() != lt)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
/* determine the CPU being requested */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LTHREAD_MAX_LCORES; i++) {
|
||||
if (!CPU_ISSET(i, cpuset))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
/* check requested core is allowed */
|
||||
if (i == LTHREAD_MAX_LCORES)
|
||||
return POSIX_ERRNO(EINVAL);
|
||||
|
||||
/* finally we can set affinity to the requested lcore */
|
||||
lthread_set_affinity(i);
|
||||
return 0;
|
||||
}
|
||||
return _sys_pthread_funcs.f_pthread_setaffinity_np(thread, cpusetsize,
|
||||
cpuset);
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2015 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _PTHREAD_SHIM_H_
|
||||
#define _PTHREAD_SHIM_H_
|
||||
|
||||
#include <rte_lcore.h>
|
||||
|
||||
/*
|
||||
* This pthread shim is an example that demonstrates how legacy code
|
||||
* that makes use of POSIX pthread services can make use of lthreads
|
||||
* with reduced porting effort.
|
||||
*
|
||||
* N.B. The example is not a complete implementation, only a subset of
|
||||
* pthread APIs sufficient to demonstrate the principle of operation
|
||||
* are implemented.
|
||||
*
|
||||
* In general pthread attribute objects do not have equivalent functions
|
||||
* in lthreads, and are ignored.
|
||||
*
|
||||
* There is one exception and that is the use of attr to specify a
|
||||
* core affinity in calls to pthread_create.
|
||||
*
|
||||
* The shim operates as follows:-
|
||||
*
|
||||
* On initialisation a constructor function uses dlsym to obtain and
|
||||
* save the loaded address of the full set of pthread APIs that will
|
||||
* be overridden.
|
||||
*
|
||||
* For each function there is a stub provided that will invoke either
|
||||
* the genuine pthread library function saved saved by the constructor,
|
||||
* or else the corresponding equivalent lthread function.
|
||||
*
|
||||
* The stub functions are implemented in pthread_shim.c
|
||||
*
|
||||
* The stub will take care of adapting parameters, and any police
|
||||
* any constraints where lthread functionality differs.
|
||||
*
|
||||
* The initial thread must always be a pure lthread.
|
||||
*
|
||||
* The decision whether to invoke the real library function or the lthread
|
||||
* function is controlled by a per pthread flag that can be switched
|
||||
* on of off by the pthread_override_set() API described below. Typically
|
||||
* this should be done as the first action of the initial lthread.
|
||||
*
|
||||
* N.B In general it would be poor practice to revert to invoke a real
|
||||
* pthread function when running as an lthread, since these may block and
|
||||
* effectively stall the lthread scheduler.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* An exiting lthread must not terminate the pthread it is running in
|
||||
* since this would mean terminating the lthread scheduler.
|
||||
* We override pthread_exit() with a macro because it is typically declared with
|
||||
* __rte_noreturn
|
||||
*/
|
||||
void pthread_exit_override(void *v);
|
||||
|
||||
#define pthread_exit(v) do { \
|
||||
pthread_exit_override((v)); \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Enable/Disable pthread override
|
||||
* state
|
||||
* 0 disable
|
||||
* 1 enable
|
||||
*/
|
||||
void pthread_override_set(int state);
|
||||
|
||||
|
||||
/*
|
||||
* Return pthread override state
|
||||
* return
|
||||
* 0 disable
|
||||
* 1 enable
|
||||
*/
|
||||
int pthread_override_get(void);
|
||||
|
||||
|
||||
#endif /* _PTHREAD_SHIM_H_ */
|
Loading…
x
Reference in New Issue
Block a user