pax_global_header 0000666 0000000 0000000 00000000064 14561114601 0014511 g ustar 00root root 0000000 0000000 52 comment=54ca02b3d0ed83871198c4607d6660e5047307bd
intel-lpmd-0.0.3/ 0000775 0000000 0000000 00000000000 14561114601 0013556 5 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/AUTHORS 0000664 0000000 0000000 00000000132 14561114601 0014622 0 ustar 00root root 0000000 0000000 Zhang Rui
Srinivas Pandruvada
intel-lpmd-0.0.3/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000012476 14561114601 0016367 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
CommunityCodeOfConduct AT intel DOT com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
intel-lpmd-0.0.3/CONTRIBUTING.md 0000664 0000000 0000000 00000004371 14561114601 0016014 0 ustar 00root root 0000000 0000000 # Contributing
### License
is licensed under the terms in [LICENSE]. By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms.
### Sign your work
Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify
the below (from [developercertificate.org](http://developercertificate.org/)):
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
Then you just add a line to every git commit message:
Signed-off-by: Joe Smith
Use your real name (sorry, no pseudonyms or anonymous contributions.)
If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`.
intel-lpmd-0.0.3/COPYING 0000664 0000000 0000000 00000043100 14561114601 0014607 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
intel-lpmd-0.0.3/ChangeLog 0000664 0000000 0000000 00000000000 14561114601 0015316 0 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/Makefile.am 0000664 0000000 0000000 00000002560 14561114601 0015615 0 ustar 00root root 0000000 0000000 include $(GLIB_MAKEFILE)
SUBDIRS = . data tools
ACLOCAL_AMFLAGS =
# Global C Flags
AM_CFLAGS = \
${DBUS_CFLAGS} \
$(XML_CFLAGS) \
-DTDRUNDIR=\"$(lpmd_rundir)\" \
-DTDCONFDIR=\"$(lpmd_confdir)\" \
$(CFLAGS) \
$(libnl30_CFLAGS)\
$(libnlgenl30_CFLAGS) \
$(SYSTEMD_CFLAGS) \
-I src
EXTRA_DIST=Makefile.glib \
intel_lpmd.pc.in
# Programs to build
sbin_PROGRAMS = intel_lpmd
intel_lpmd_CPPFLAGS = \
-I@top_srcdir@/src \
-DTDLOCALEDIR=\"$(datadir)/locale\" \
-DGLIB_SUPPORT
intel_lpmd_includedir = @top_srcdir@
intel_lpmd_LDADD = \
$(DBUS_LIBS) \
$(GLIB_LIBS) \
$(LIBNL_LIBS) \
$(LIBM) \
$(LIBDL) \
$(XML_LIBS) \
$(libnlgenl30_LIBS) \
$(SYSTEMD_LIBS)
BUILT_SOURCES = \
intel_lpmd_dbus_interface.h \
lpmd-resource.c
intel_lpmd_SOURCES = \
src/lpmd_main.c \
src/lpmd_proc.c \
src/lpmd_dbus_server.c \
src/lpmd_config.c \
src/lpmd_cpu.c \
src/lpmd_helpers.c \
src/lpmd_hfi.c \
src/lpmd_irq.c \
src/lpmd_socket.c \
src/lpmd_util.c \
lpmd-resource.c
man8_MANS = man/intel_lpmd.8
man5_MANS = man/intel_lpmd_config.xml.5
intel_lpmd_dbus_interface.h: $(top_srcdir)/src/intel_lpmd_dbus_interface.xml
$(AM_V_GEN) dbus-binding-tool --prefix=dbus_interface --mode=glib-server --output=$@ $<
lpmd-resource.c: $(top_srcdir)/lpmd-resource.gresource.xml
$(AM_V_GEN) glib-compile-resources --generate-source lpmd-resource.gresource.xml
CLEANFILES = $(BUILT_SOURCES)
intel-lpmd-0.0.3/NEWS 0000664 0000000 0000000 00000000004 14561114601 0014247 0 ustar 00root root 0000000 0000000 TBD
intel-lpmd-0.0.3/README.md 0000664 0000000 0000000 00000004672 14561114601 0015046 0 ustar 00root root 0000000 0000000 # Intel Low Power Mode Daemon
Intel Low Power Model Daemon is a Linux daemon used to optimize active idle power.
It selects a set of most power efficient CPUs based on configuration file or CPU topology. Based on system utilization and other hints, it puts the system into Low Power Mode by activate the power efficient CPUs and disable the rest, and restore the system from Low Power Mode by activating all CPUs.
Use man pages to check command line arguments and xml configurations
The prefix value depends on the distribution version.
This can be "/" or "/usr". So please check existing
path of intel_lpmd install, if present to update and
add appropriate prefix.
For build and run, follow the same procedure as Fedora.
### Building and executing on openSUSE
1. Install
zypper in automake
zypper in gcc
TBD
For build and run, follow the same procedure as Fedora.
Releases
## Release 0.0.1
## Security
See Intel's [Security Center](https://www.intel.com/content/www/us/en/security-center/default.html)
for information on how to report a potential security issue or vulnerability.
See also: [Security Policy](SECURITY.md)
intel-lpmd-0.0.3/autogen.sh 0000775 0000000 0000000 00000000522 14561114601 0015556 0 ustar 00root root 0000000 0000000 #!/bin/sh
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
olddir=`pwd`
cd "$srcdir"
aclocal --install || exit 1
gtkdocize --copy --flavour no-tmpl || exit 1
autoreconf --install --verbose || exit 1
cd "$olddir"
if test -z "$NO_CONFIGURE"; then
$srcdir/configure "$@" && echo "Now type 'make' to compile `basename $srcdir`."
fi
intel-lpmd-0.0.3/configure.ac 0000664 0000000 0000000 00000006716 14561114601 0016056 0 ustar 00root root 0000000 0000000 AC_PREREQ(1.0)
m4_define([lpmd_major_version], [0])
m4_define([lpmd_minor_version], [0.3])
m4_define([lpmd_version],
[lpmd_major_version.lpmd_minor_version])
AC_INIT([intel_lpmd], [lpmd_version], [], [intel_lpmd])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.11 foreign no-define subdir-objects])
AM_MAINTAINER_MODE([enable])
GTK_DOC_CHECK([1.11],[--flavour no-tmpl])
AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is]))
if test -n "$with_dbus_sys_dir" ; then
DBUS_SYS_DIR="$with_dbus_sys_dir"
else
DBUS_SYS_DIR="/etc/dbus-1/system.d"
fi
AC_SUBST(DBUS_SYS_DIR)
# paths
AC_SUBST(lpmd_binary, "$sbindir/$PACKAGE", [Binary executable])
AC_SUBST(lpmd_confdir, "$sysconfdir/$PACKAGE", [Configuration directory])
AC_SUBST(lpmd_rundir, "$localstatedir/run/$PACKAGE", [Runtime state directory])
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
if test "x$with_systemdsystemunitdir" != xno; then
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
# print configuration
echo
echo "System paths:"
echo " prefix: $prefix"
echo " exec_prefix: $exec_prefix"
echo " systemdunitdir: $with_systemdsystemunitdir"
echo " lpmd_binary: $lpmd_binary"
echo " lpmd_confdir: $lpmd_confdir"
echo " lpmd_rundir: $lpmd_rundir"
echo
GETTEXT_PACKAGE=intel_lpmd
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
dnl
dnl Checks for new dbus-glib property access function
dnl
AC_CHECK_LIB([dbus-glib-1], [dbus_glib_global_set_disable_legacy_property_access], ac_have_dg_prop="1", ac_have_dg_prop="0")
AC_DEFINE_UNQUOTED(HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS, $ac_have_dg_prop, [Define if you have a dbus-glib with dbus_glib_global_set_disable_legacy_property_access()])
PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1 dbus-glib-1 >= 0.94)
AC_SUBST(DBUS_CFLAGS)
AC_SUBST(DBUS_LIBS)
GLIB_VERSION_DEFINES="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26"
DBUS_CFLAGS="$DBUS_CFLAGS $GLIB_VERSION_DEFINES"
PKG_CHECK_MODULES(GLIB, gio-unix-2.0 >= 2.22 gmodule-2.0)
GLIB_CFLAGS="$GLIB_CFLAGS $GLIB_VERSION_DEFINES"
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
PKG_CHECK_MODULES(XML, libxml-2.0 >= 2.4)
PKG_CHECK_MODULES(libnl30, [libnl-3.0], libnl30=yes, libnl30=no)
PKG_CHECK_MODULES(libnlgenl30, [libnl-genl-3.0], libnlgenl30=yes, libnlgenl30=no)
PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [],
[PKG_CHECK_MODULES([SYSTEMD], [libsystemd-daemon], [],
AC_MSG_ERROR([libsystemd support requested but found]))])
AC_PATH_PROG([GDBUS_CODEGEN],[gdbus-codegen])
AC_PROG_CC
AC_PROG_INSTALL
AC_C_CONST
AC_C_INLINE
AC_TYPE_SIZE_T
AC_ARG_ENABLE(werror, AS_HELP_STRING([--disable-werror], [Disable -Werror]))
AS_IF([test "x$enable_werror" != "xno"], [CFLAGS="$CFLAGS -Werror"])
AC_CONFIG_FILES([Makefile
data/Makefile])
AC_ARG_ENABLE(gdbus,
[AS_HELP_STRING([--disable-gdbus],
[Switch DBus backend to glib-dbus. (Default: GDBus)])],
[],
[AC_DEFINE([GDBUS], [1], [Enable GDBus support])])
AC_OUTPUT
intel-lpmd-0.0.3/data/ 0000775 0000000 0000000 00000000000 14561114601 0014467 5 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/data/Makefile.am 0000664 0000000 0000000 00000001564 14561114601 0016531 0 ustar 00root root 0000000 0000000 include $(GLIB_MAKEFILE)
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
intel_lpmd.service
intel_lpmd.service: intel_lpmd.service.in
@$(edit) $< >$@
servicedir = $(datadir)/dbus-1/system-services
service_in_files = org.freedesktop.intel_lpmd.service.in
service_DATA = $(service_in_files:.service.in=.service)
$(service_DATA): $(service_in_files) Makefile
@$(edit) $< >$@
endif
edit = sed \
-e 's|@bindir[@]|$(bindir)|g' \
-e 's|@sbindir[@]|$(sbindir)|g' \
-e 's|@sysconfdir[@]|$(sysconfdir)|g' \
-e 's|@localstatedir[@]|$(localstatedir)|g'
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.intel_lpmd.conf
lpmd_configdir = $(lpmd_confdir)
lpmd_config_DATA = \
intel_lpmd_config.xml
EXTRA_DIST = \
intel_lpmd.service.in \
org.freedesktop.intel_lpmd.service.in \
$(dbusservice_DATA)
CLEANFILES = intel_lpmd.service org.freedesktop.intel_lpmd.service
intel-lpmd-0.0.3/data/intel_lpmd.service.in 0000664 0000000 0000000 00000000463 14561114601 0020610 0 ustar 00root root 0000000 0000000 [Unit]
Description= Intel Low Power Daemon Service
ConditionVirtualization=no
[Service]
Type=dbus
SuccessExitStatus=2
BusName=org.freedesktop.intel_lpmd
ExecStart=@sbindir@/intel_lpmd --systemd --dbus-enable
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=org.freedesktop.intel_lpmd.service
intel-lpmd-0.0.3/data/intel_lpmd_config.xml 0000664 0000000 0000000 00000004506 14561114601 0020672 0 ustar 00root root 0000000 0000000
0-1-1-100109500000
intel-lpmd-0.0.3/data/org.freedesktop.intel_lpmd.conf 0000664 0000000 0000000 00000001660 14561114601 0022570 0 ustar 00root root 0000000 0000000
intel-lpmd-0.0.3/data/org.freedesktop.intel_lpmd.service.in 0000664 0000000 0000000 00000000174 14561114601 0023707 0 ustar 00root root 0000000 0000000 [D-BUS Service]
Name=org.freedesktop.intel_lpmd
Exec=/bin/false
User=root
SystemdService=org.freedesktop.intel_lpmd.service
intel-lpmd-0.0.3/lpmd-resource.gresource.xml 0000664 0000000 0000000 00000000324 14561114601 0021055 0 ustar 00root root 0000000 0000000
src/intel_lpmd_dbus_interface.xml
intel-lpmd-0.0.3/man/ 0000775 0000000 0000000 00000000000 14561114601 0014331 5 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/man/intel_lpmd.8 0000664 0000000 0000000 00000005407 14561114601 0016557 0 ustar 00root root 0000000 0000000 .\" intel_lpmd (8) manual page
.\"
.\" This is free documentation; you can redistribute it and/or
.\" modify it under the terms of the GNU General Public License as
.\" published by the Free Software Foundation; either version 2 of
.\" the License, or (at your option) any later version.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public Licence along
.\" with this manual; if not, write to the Free Software Foundation, Inc.,
.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
.\"
.\" Copyright (C) 2012 Intel Corporation. All rights reserved.
.\"
.TH intel_lpmd "8" "1 Jun 2023"
.SH NAME
intel_lpmd \- Intel Low Power Mode Daemon
.SH SYNOPSIS
.B intel_lpmd
.RI " [ " OPTIONS " ]
.SH DESCRIPTION
.B intel_lpmd
is a Linux daemon used to optimize active idle power. This
daemon uses a configuration file "intel_lpmd_config.xml".
Based on the configuration, it will choose right set of CPUs
to enable. For example, this daemon can monitor system utilization
and choose a set of low power CPUs to enable and disable the rest.
This enable disable of CPUs are done using Linux cpuset feature
of intel power clamp driver.
There is a control utility distributed along with this daemon.
This control utility is called "intel_lpmd_control". This utility
can be used to set different modes for this daemon.
For example:
intel_lpmd_control ON
To turn on low power mode operation.
intel_lpmd_control OFF
To turn off low power mode operation.
intel_lpmd_control AUTO
To turn on low power mode operation in auto mode, which
allows low power mode based on system utilization.
.SH OPTIONS
.TP
.B -h --help
Print the help message
.TP
.B --version
Print intel_lpmd version and exit
.TP
.B --no-daemon
Don't run as a daemon: Default is daemon mode
.TP
.B --systemd
Assume daemon is started by systemd
.TP
.B --loglevel=
log severity: can be info or debug
.TP
.B --dbus-enable
Enable Dbus server to receive requests via Dbus
.TP
.B --dry-run
Dry run without taking any action for debugging purpose.
.SH EXAMPLES
.TP
.B intel_lpmd --loglevel=info --no-daemon --dbus-enable
Run intel_lpmd with log directed to stdout
.TP
.B intel_lpmd --systemd --dbus-enable
Run intel_lpmd as a service with logs directed to system journal
.SH SEE ALSO
intel_lpmd_config.xml(5)
.SH AUTHOR
Written by Zhang Rui
intel-lpmd-0.0.3/man/intel_lpmd_config.xml.5 0000664 0000000 0000000 00000023507 14561114601 0020701 0 ustar 00root root 0000000 0000000 .\" intel_lpmd_config.xml(5) manual page
.\"
.\" This is free documentation; you can redistribute it and/or
.\" modify it under the terms of the GNU General Public License as
.\" published by the Free Software Foundation; either version 2 of
.\" the License, or (at your option) any later version.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public Licence along
.\" with this manual; if not, write to the Free Software Foundation, Inc.,
.\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
.\"
.\" Copyright (C) 2012 Intel Corporation. All rights reserved.
.\"
.TH intel_lpmd_config.xml "5" "1 Jun 2023"
.SH NAME
intel_lpmd_config.xml \- Configuration file for intel_lpmd
.SH SYNOPSIS
$(TDCONFDIR)/etc/intel_lpmd/intel_lpmd_config.xml
.SH DESCRIPTION
.B intel_lpmd_config.xml
is a configuration file for the Intel Low Power Mode Daemon.
It is used to describe the lp_mode_cpus to use in Low Power Mode,
as well as the way to restrict work to those CPUs. It also describes
if and how the HFI monitor and utilization monitor works. The location
of this file depends on the configuration option used during build time.
.PP
.B lp_mode_cpus
is a set of active CPUs when system is in Low Power Mode.
This usually equals a group of most power efficient CPUs on a platform to
achieve best power saving. When not specified, intel_lpmd tool can detect this
automatically. E.g. it uses an E-core Module on Intel Alderlake platform, and
it uses the Low Power E-cores on SoC Die on Intel Meteorlake platform.
.PP
.B Mode
specifies the way to migrate the tasks to the lp_mode_cpus.
.IP \(bu 2
Mode 0: set cpuset to the lp_mode_cpus for systemd. All tasks created by
systemd will run on these CPUs only. This is supported for cgroup v2 based
systemd only.
.IP \(bu 2
Mode 1: Isolate the non-lp_mode_cpus so that tasks are scheduled to the
lp_mode_cpus only.
.IP \(bu 2
Mode 2: Force idle injection to the non-lp_mode_cpus and leverage the
scheduler to schedule the other tasks to the lp_mode_cpus.
.PP
.B PerformanceDef
specifies the default behavior when power setting is set to Performance.
.IP \(bu 2
-1 : Never enter Low Power Mode.
.IP \(bu 2
0 : opportunistic Low Power Mode enter/exit based on HFI/Utilization request.
.IP \(bu 2
1 : Always stay in Low Power Mode.
.PP
.B BalancedDef
specifies the default behavior when power setting is set to Balanced.
.PP
.B PowersaverDef
specifies the default behavior when power setting is set to Power saver.
.PP
.B HfiLpmEnable
specifies if the HFI monitor can capture the HFI hints for Low Power Mode.
.PP
.B HfiSuvEnable
specifies if the HFI monitor can capture the HFI hints for survivability mode.
.PP
.B util_entry_threshold
specifies the system utilization threshold for entering Low Power Mode.
The system workload is considered to fit the lp_mode_cpus capacity when system
utilization is under this threshold.
Setting to 0 or leaving this empty disables the utilization monitor.
.PP
.B util_exit_threshold
specifies the CPU utilization threshold for exiting Low Power Mode.
The system workload is considered to not fit the lp_mode_cpus capacity when
the utilization of the busiest lp_mode_cpus is above this threshold.
Setting to 0 or leaving this empty disables the utilization monitor.
.PP
.B EntryDelayMS
specifies the sample interval used by the utilization Monitor when system
wants to enter Low Power Mode based on system utilization.
Setting to 0 or leaving this empty will cause the utilization Monitor to use
the default interval, 1000 milli seconds.
.PP
.B ExitDelayMS
specifies the sample interval used by the utilization Monitor when system
wants to exit Low Power Mode based on CPU utilization.
Setting to 0 or leaving this empty will cause the utilization Monitor to use
the adaptive value. The adaptive interval is based on CPU utilization.
The busier the CPU is, the shorter interval the utilization monitor uses.
.PP
.B EntryHystMS
specifies a hysteresis threshold when system is in Low Power Mode.
If set, when the previous average time stayed in Low Power Mode is lower than
this value, the current enter Low Power Mode request will be ignored because
it is expected that the system will exit Low Power Mode soon.
Setting to 0 or leaving this empty disables this hysteresis algorithm.
.PP
.B ExitHystMS
specifies a hysteresis threshold when system is not in Low Power Mode.
If set, when the previous average time stayed out of Low-Power-Mode is lower
than this value, the current exit Low Power Mode request will be ignored
because it is expected that the system will enter Low Power Mode soon.
Setting to 0 or leaving this empty disables this hysteresis algorithm.
.SH FILE FORMAT
The configuration file format conforms to XML specifications.
.sp 1
.EX
Example CPUs0|1|2-1|0|1-1|0|1-1|0|10|10|1Example thresholdExample thresholdExample delayExample delayExample hystExample hyst
.EE
.SH EXAMPLE CONFIGURATIONS
.PP
.B Example 1:
This is the minimum configuration.
.IP \(bu 2
lp_mode_cpus: not set. Detects the lp_mode_cpus automatically.
.IP \(bu 2
Mode: 0. Use cgroup-v2 systemd for task migration.
.IP \(bu 2
HfiLpmEnable: 0. Ignore HFI Low Power mode hints.
.IP \(bu 2
HfiSuvEnable: 0. Ignore HFI Survivability mode hints. With both HfiLpmEnable and HfiSuvEnable cleared, the HFI monitor will be disabled.
.IP \(bu 2
util_entry_threshold: 0. Disable utilization monitor.
.IP \(bu 2
util_exit_threshold: 0. Disable utilization monitor.
.IP \(bu 2
EntryDelayMS: 0. Do not take effect when utilization monitor is disabled.
.IP \(bu 2
ExitDelayMS: 0. Do not take effect when utilization monitor is disabled.
.IP \(bu 2
EntryHystMS: 0. Do not take effect when utilization monitor is disabled.
.IP \(bu 2
ExitHystMS: 0. Do not take effect when utilization monitor is disabled.
.sp 1
.EX
000000000
.PP
.B Example 2:
This is the typical configuration. The utilization thresholds and delays may be different based on requirement.
.IP \(bu 2
lp_mode_cpus: not set. Detects the lp_mode_cpus automatically.
.IP \(bu 2
Mode: 0. Use cgroup-v2 systemd for task migration.
.IP \(bu 2
HfiLpmEnable: 1. Enter/Exit Low Power Mode based on HFI hints.
.IP \(bu 2
HfiSuvEnable: 1. Enter/Exit Survivability mode based on HFI hints.
.IP \(bu 2
util_entry_threshold: 5. Enter Low Power Mode when system utilization is lower than 5%.
.IP \(bu 2
util_exit_threshold: 95. Exit Low Power Mode when the utilization of any of the lp_mode_cpus is higher than 95%.
.IP \(bu 2
EntryDelayMS: 0. Resample every 1000ms when system is out of Low Power Mode.
.IP \(bu 2
ExitDelayMS: 0. Resample adaptively based on the utilization of lp_mode_cpus when system is in Low Power Mode.
.IP \(bu 2
EntryHystMS: 2000. Ignore the current Enter Low Power Mode request when the previous average time stayed in Low Power Mode is lower than 2000ms.
.IP \(bu 2
ExitHystMS: 3000. Ignore the current Exit Low Power Mode request when the previous average time stayed out of Low Power Mode is lower than 3000ms.
.sp 1
.EX
0115950020003000
.EE
intel-lpmd-0.0.3/security.md 0000664 0000000 0000000 00000000626 14561114601 0015753 0 ustar 00root root 0000000 0000000 # Security Policy
Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation.
## Reporting a Vulnerability
Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html).
intel-lpmd-0.0.3/src/ 0000775 0000000 0000000 00000000000 14561114601 0014345 5 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/src/intel_lpmd_dbus_interface.xml 0000664 0000000 0000000 00000001013 14561114601 0022246 0 ustar 00root root 0000000 0000000
intel-lpmd-0.0.3/src/lpmd.h 0000664 0000000 0000000 00000014461 14561114601 0015460 0 ustar 00root root 0000000 0000000 /*
* intel_lpmd.h: Intel Low Power Daemon common header file
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef LPMD_INTEL_LPMD_H
#define LPMD_INTEL_LPMD_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "config.h"
#include "thermal.h"
#define LOG_DEBUG_INFO 1
#define LOCKF_SUPPORT
#ifdef GLIB_SUPPORT
#include
#include
#include
#include
#include
#include
// Log macros
enum log_level {
LPMD_LOG_NONE,
LPMD_LOG_INFO,
LPMD_LOG_DEBUG,
LPMD_LOG_MSG,
LPMD_LOG_WARN,
LPMD_LOG_ERROR,
LPMD_LOG_FATAL,
};
#define lpmd_log_fatal g_error // Print error and terminate
#define lpmd_log_error g_critical
#define lpmd_log_warn g_warning
#define lpmd_log_msg g_message
#define lpmd_log_debug g_debug
#define lpmd_log_info(...) g_log(NULL, G_LOG_LEVEL_INFO, __VA_ARGS__)
#else
static int dummy_printf(const char *__restrict __format, ...)
{
return 0;
}
#define lpmd_log_fatal printf
#define lpmd_log_error printf
#define lpmd_log_warn printf
#define lpmd_log_msg printf
#define lpmd_log_debug dummy_printf
#define lpmd_log_info printf
#endif
// Common return value defines
#define LPMD_SUCCESS 0
#define LPMD_ERROR -1
#define LPMD_FATAL_ERROR -2
// Dbus related
/* Well-known name for this service. */
#define INTEL_LPMD_SERVICE_NAME "org.freedesktop.intel_lpmd"
#define INTEL_LPMD_SERVICE_OBJECT_PATH "/org/freedesktop/intel_lpmd"
#define INTEL_LPMD_SERVICE_INTERFACE "org.freedesktop.intel_lpmd"
typedef enum {
TERMINATE, LPM_FORCE_ON, LPM_FORCE_OFF, LPM_AUTO, SUV_MODE_ENTER, SUV_MODE_EXIT, HFI_EVENT,
} message_name_t;
#define MAX_MSG_SIZE 512
typedef struct {
message_name_t msg_id;
int msg_size;
unsigned long msg[MAX_MSG_SIZE];
} message_capsul_t;
#define MAX_STR_LENGTH 256
// lpmd config data
typedef struct {
int mode;
int performance_def;
int balanced_def;
int powersaver_def;
int hfi_lpm_enable;
int hfi_suv_enable;
int util_enable;
int util_entry_threshold;
int util_exit_threshold;
int util_entry_delay;
int util_exit_delay;
int util_entry_hyst;
int util_exit_hyst;
int ignore_itmt;
char lp_mode_cpus[MAX_STR_LENGTH];
} lpmd_config_t;
enum lpm_cpu_process_mode {
LPM_CPU_CGROUPV2,
LPM_CPU_ISOLATE,
LPM_CPU_POWERCLAMP,
LPM_CPU_OFFLINE,
LPM_CPU_MODE_MAX = LPM_CPU_POWERCLAMP,
};
enum lpm_command {
USER_ENTER, /* Force enter LPM and always stay in LPM */
USER_AUTO, /* Allow oppotunistic LPM based on util/hfi request */
USER_EXIT, /* Force exit LPM and never enter LPM */
HFI_ENTER,
HFI_EXIT,
HFI_SUV_ENTER,
HFI_SUV_EXIT,
DBUS_SUV_ENTER,
DBUS_SUV_EXIT,
UTIL_ENTER,
UTIL_EXIT,
LPM_CMD_MAX,
};
enum cpumask_idx {
CPUMASK_LPM_DEFAULT, CPUMASK_ONLINE, CPUMASK_HFI, CPUMASK_HFI_SUV, /* HFI Survivability mode */
CPUMASK_MAX,
};
#define MAX_LPM_CPUS 8
#define UTIL_DELAY_MAX 5000
#define UTIL_HYST_MAX 10000
/* lpmd_main.c */
int in_debug_mode(void);
/* lpmd_proc.c: interfaces */
int lpmd_lock(void);
int lpmd_unlock(void);
int in_lpm(void);
int get_idle_percentage(void);
int get_idle_duration(void);
int get_cpu_mode(void);
int has_hfi_lpm_monitor(void);
int has_hfi_suv_monitor(void);
int has_util_monitor(void);
int get_util_entry_interval(void);
int get_util_exit_interval(void);
int get_util_entry_threshold(void);
int get_util_exit_threshold(void);
int get_util_entry_hyst(void);
int get_util_exit_hyst(void);
void set_ignore_itmt(void);
int process_lpm(enum lpm_command cmd);
int process_lpm_unlock(enum lpm_command cmd);
int freeze_lpm(void);
int restore_lpm(void);
void lpmd_terminate(void);
void lpmd_force_on(void);
void lpmd_force_off(void);
void lpmd_set_auto(void);
void lpmd_suv_enter(void);
void lpmd_suv_exit(void);
void lpmd_notify_hfi_event(void);
/* lpmd_proc.c: init func */
int lpmd_main(void);
/* lpmd_dbus_server.c */
int intel_dbus_server_init(gboolean (*exit_handler)(void));
/* lpmd_config.c */
int lpmd_get_config(lpmd_config_t *lpmd_config);
/* util.c */
int periodic_util_update(void);
/* cpu.c */
int init_cpu(char *cmd_cpus, enum lpm_cpu_process_mode mode);
int process_cpus(int enter, enum lpm_cpu_process_mode mode);
/* cpu.c: helpers */
int is_cpu_online(int cpu);
int is_cpu_for_lpm(int cpu);
int get_max_cpus(void);
int get_max_online_cpu(void);
char* get_lpm_cpus_hexstr(void);
int has_lpm_cpus(void);
int has_cpus(enum cpumask_idx idx);
int add_cpu(int cpu, enum cpumask_idx idx);
void reset_cpus(enum cpumask_idx idx);
int set_lpm_cpus(enum cpumask_idx new);
int uevent_init(void);
int check_cpu_hotplug(void);
/* cpu.c : APIs for SUV mode support */
int process_suv_mode(enum lpm_command cmd);
int has_suv_support(void);
int in_hfi_suv_mode(void);
/* irq.c */
int init_irq(void);
int process_irqs(int enter, enum lpm_cpu_process_mode mode);
/* hfi.c */
int hfi_init(void);
int hfi_kill(void);
void hfi_receive(void);
/* socket.c */
int socket_init_connection(char *name);
int socket_send_cmd(char *name, char *data);
/* helper */
int lpmd_write_str(const char *name, char *str, int print_level);
int lpmd_write_str_verbose(const char *name, char *str, int print_level);
int lpmd_write_str_append(const char *name, char *str, int print_level);
int lpmd_write_int(const char *name, int val, int print_level);
int lpmd_open(const char *name, int print_level);
int lpmd_read_int(const char *name, int *val, int print_level);
char* get_time(void);
void time_start(void);
char* time_delta(void);
#endif
intel-lpmd-0.0.3/src/lpmd_config.c 0000664 0000000 0000000 00000021663 14561114601 0017002 0 ustar 00root root 0000000 0000000 /*
* lpmd_config.c: xml config file parser
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lpmd.h"
#include
#include
#define CONFIG_FILE_NAME "intel_lpmd_config.xml"
#define MAX_FILE_NAME_PATH 64
static void lpmd_dump_config(lpmd_config_t *lpmd_config)
{
if (!lpmd_config)
return;
lpmd_log_info ("Mode:%d\n", lpmd_config->mode);
lpmd_log_info ("HFI LPM Enable:%d\n", lpmd_config->hfi_lpm_enable);
lpmd_log_info ("HFI SUV Enable:%d\n", lpmd_config->hfi_suv_enable);
lpmd_log_info ("Util entry threshold:%d\n", lpmd_config->util_entry_threshold);
lpmd_log_info ("Util exit threshold:%d\n", lpmd_config->util_exit_threshold);
lpmd_log_info ("Util LP Mode CPUs:%s\n", lpmd_config->lp_mode_cpus);
}
static int lpmd_fill_config(xmlDoc *doc, xmlNode *a_node, lpmd_config_t *lpmd_config)
{
xmlNode *cur_node = NULL;
char *tmp_value;
char *pos;
if (!doc || !a_node || !lpmd_config)
return LPMD_ERROR;
lpmd_config->performance_def = lpmd_config->balanced_def = lpmd_config->powersaver_def = LPM_FORCE_OFF;
for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE) {
tmp_value = (char*) xmlNodeListGetString (doc, cur_node->xmlChildrenNode, 1);
if (tmp_value) {
lpmd_log_info ("node type: Element, name: %s, value: %s\n", cur_node->name,
tmp_value);
if (!strncmp((const char*)cur_node->name, "Mode", strlen("Mode"))) {
errno = 0;
lpmd_config->mode = strtol (tmp_value, &pos, 10);
lpmd_log_info ("mode %d, errno %d, tmp_value %p, pos %p\n", lpmd_config->mode,
errno,
tmp_value, pos);
if (errno || *pos != '\0' || lpmd_config->mode > LPM_CPU_MODE_MAX
|| lpmd_config->mode < 0)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "HfiLpmEnable", strlen("HfiEnable"))) {
errno = 0;
lpmd_config->hfi_lpm_enable = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0'
|| (lpmd_config->hfi_lpm_enable != 1 && lpmd_config->hfi_lpm_enable != 0))
goto err;
}
else if (!strncmp((const char*)cur_node->name, "HfiSuvEnable", strlen("HfiEnable"))) {
errno = 0;
lpmd_config->hfi_suv_enable = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0'
|| (lpmd_config->hfi_suv_enable != 1 && lpmd_config->hfi_suv_enable != 0))
goto err;
}
else if (!strncmp((const char*)cur_node->name, "EntryDelayMS", strlen ("EntryDelayMS"))) {
errno = 0;
lpmd_config->util_entry_delay = strtol (tmp_value, &pos, 10);
if (errno
|| *pos
!= '\0'|| lpmd_config->util_entry_delay < 0 || lpmd_config->util_entry_delay > UTIL_DELAY_MAX)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "ExitDelayMS", strlen ("ExitDelayMS"))) {
errno = 0;
lpmd_config->util_exit_delay = strtol (tmp_value, &pos, 10);
if (errno
|| *pos
!= '\0'|| lpmd_config->util_exit_delay < 0 || lpmd_config->util_exit_delay > UTIL_DELAY_MAX)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "util_entry_threshold",
strlen ("util_entry_threshold"))) {
errno = 0;
lpmd_config->util_entry_threshold = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0' || lpmd_config->util_entry_threshold < 0
|| lpmd_config->util_entry_threshold > 100)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "util_exit_threshold",
strlen ("util_exit_threshold"))) {
errno = 0;
lpmd_config->util_exit_threshold = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0' || lpmd_config->util_exit_threshold < 0
|| lpmd_config->util_exit_threshold > 100)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "EntryHystMS", strlen ("EntryHystMS"))) {
errno = 0;
lpmd_config->util_entry_hyst = strtol (tmp_value, &pos, 10);
if (errno
|| *pos
!= '\0'|| lpmd_config->util_entry_hyst < 0 || lpmd_config->util_entry_hyst > UTIL_HYST_MAX)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "ExitHystMS", strlen ("ExitHystMS"))) {
errno = 0;
lpmd_config->util_exit_hyst = strtol (tmp_value, &pos, 10);
if (errno
|| *pos
!= '\0'|| lpmd_config->util_exit_hyst < 0 || lpmd_config->util_exit_hyst > UTIL_HYST_MAX)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "IgnoreITMT", strlen ("IgnoreITMT"))) {
errno = 0;
lpmd_config->ignore_itmt = strtol (tmp_value, &pos, 10);
if (errno
|| *pos
!= '\0'|| lpmd_config->ignore_itmt < 0 || lpmd_config->ignore_itmt > 1)
goto err;
}
else if (!strncmp((const char*)cur_node->name, "lp_mode_cpus", strlen ("lp_mode_cpus"))) {
if (!strncmp (tmp_value, "-1", strlen ("-1")))
lpmd_config->lp_mode_cpus[0] = '\0';
else
snprintf (lpmd_config->lp_mode_cpus, sizeof(lpmd_config->lp_mode_cpus),
"%s", tmp_value);
}
else if (!strncmp((const char*)cur_node->name, "PerformanceDef", strlen ("PerformanceDef"))) {
errno = 0;
lpmd_config->performance_def = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0')
goto err;
if (lpmd_config->performance_def == -1)
lpmd_config->performance_def = LPM_FORCE_OFF;
else if (lpmd_config->performance_def == 1)
lpmd_config->performance_def = LPM_FORCE_ON;
else if (!lpmd_config->performance_def)
lpmd_config->performance_def = LPM_AUTO;
else
goto err;
}
else if (!strncmp((const char*)cur_node->name, "BalancedDef", strlen ("BalancedDef"))) {
errno = 0;
lpmd_config->balanced_def = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0')
goto err;
if (lpmd_config->balanced_def == -1)
lpmd_config->balanced_def = LPM_FORCE_OFF;
else if (lpmd_config->balanced_def == 1)
lpmd_config->balanced_def = LPM_FORCE_ON;
else if (!lpmd_config->balanced_def)
lpmd_config->balanced_def = LPM_AUTO;
else
goto err;
}
else if (!strncmp((const char*)cur_node->name, "PowersaverDef", strlen ("PowersaverDef"))) {
errno = 0;
lpmd_config->powersaver_def = strtol (tmp_value, &pos, 10);
if (errno || *pos != '\0')
goto err;
if (lpmd_config->powersaver_def == -1)
lpmd_config->powersaver_def = LPM_FORCE_OFF;
else if (lpmd_config->powersaver_def == 1)
lpmd_config->powersaver_def = LPM_FORCE_ON;
else if (!lpmd_config->powersaver_def)
lpmd_config->powersaver_def = LPM_AUTO;
else
goto err;
}
else {
lpmd_log_info ("Invalid configuration data\n");
goto err;
}
xmlFree (tmp_value);
continue;
err: xmlFree (tmp_value);
lpmd_log_error ("node type: Element, name: %s value: %s\n", cur_node->name,
tmp_value);
return LPMD_ERROR;
}
}
}
/* use entry_threshold == 0 or exit_threshold == 0 to effectively disable util monitor */
if (lpmd_config->util_entry_threshold && lpmd_config->util_exit_threshold)
lpmd_config->util_enable = 1;
else
lpmd_config->util_enable = 0;
return LPMD_SUCCESS;
}
int lpmd_get_config(lpmd_config_t *lpmd_config)
{
char file_name[MAX_FILE_NAME_PATH];
xmlNode *root_element;
xmlNode *cur_node;
struct stat s;
xmlDoc *doc;
if (!lpmd_config)
return LPMD_ERROR;
snprintf (file_name, MAX_FILE_NAME_PATH, "%s/%s", TDCONFDIR, CONFIG_FILE_NAME);
lpmd_log_msg ("Reading configuration file %s\n", file_name);
if (stat (file_name, &s)) {
lpmd_log_msg ("error: could not find file %s\n", file_name);
return LPMD_ERROR;
}
doc = xmlReadFile (file_name, NULL, 0);
if (doc == NULL) {
lpmd_log_msg ("error: could not parse file %s\n", file_name);
return LPMD_ERROR;
}
root_element = xmlDocGetRootElement (doc);
if (root_element == NULL) {
lpmd_log_warn ("error: could not get root element \n");
return LPMD_ERROR;
}
cur_node = NULL;
for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE) {
if (!strncmp ((const char*) cur_node->name, "Configuration",
strlen ("Configuration"))) {
if (lpmd_fill_config (doc, cur_node->children, lpmd_config) != LPMD_SUCCESS) {
xmlFreeDoc (doc);
return LPMD_ERROR;
}
}
}
}
xmlFreeDoc (doc);
lpmd_dump_config (lpmd_config);
return LPMD_SUCCESS;
}
intel-lpmd-0.0.3/src/lpmd_cpu.c 0000664 0000000 0000000 00000102055 14561114601 0016317 0 ustar 00root root 0000000 0000000 /*
* lpmd_cpu.c: CPU related processing
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This file contain functions to manage Linux cpuset for LP CPUs. Also using
* power clamp in lieu of Linux cpuset. There are helper functions to format
* cpuset strings based on the which cpuset method is used or power clamp low
* power cpumask.
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lpmd.h"
static int topo_max_cpus;
static int max_online_cpu;
static size_t size_cpumask;
struct lpm_cpus {
cpu_set_t *mask;
char *name;
char *str;
char *str_reverse;
char *hexstr;
char *hexstr_reverse;
};
static struct lpm_cpus cpumasks[CPUMASK_MAX] = {
[CPUMASK_LPM_DEFAULT] = { .name = "Low Power", },
[CPUMASK_ONLINE] = { .name = "Online", },
[CPUMASK_HFI] = { .name = "HFI Low Power", },
[CPUMASK_HFI_SUV] = { .name = "HFI SUV", },
};
static enum cpumask_idx lpm_cpus_cur = CPUMASK_LPM_DEFAULT;
int is_cpu_online(int cpu)
{
if (cpu < 0 || cpu >= topo_max_cpus)
return 0;
if (!cpumasks[CPUMASK_ONLINE].mask)
return 0;
return CPU_ISSET_S(cpu, size_cpumask, cpumasks[CPUMASK_ONLINE].mask);
}
int is_cpu_for_lpm(int cpu)
{
if (cpu < 0 || cpu >= topo_max_cpus)
return 0;
if (!cpumasks[lpm_cpus_cur].mask)
return 0;
return !!CPU_ISSET_S(cpu, size_cpumask, cpumasks[lpm_cpus_cur].mask);
}
int get_max_cpus(void)
{
return topo_max_cpus;
}
int get_max_online_cpu(void)
{
return max_online_cpu;
}
static size_t alloc_cpu_set(cpu_set_t **cpu_set)
{
cpu_set_t *_cpu_set;
size_t size;
_cpu_set = CPU_ALLOC((topo_max_cpus + 1));
if (_cpu_set == NULL)
err (3, "CPU_ALLOC");
size = CPU_ALLOC_SIZE((topo_max_cpus + 1));
CPU_ZERO_S(size, _cpu_set);
*cpu_set = _cpu_set;
if (!size_cpumask)
size_cpumask = size;
if (size_cpumask && size_cpumask != size) {
lpmd_log_error ("Conflict cpumask size %zu vs. %zu\n", size, size_cpumask);
exit (-1);
}
return size;
}
static int cpu_migrate(int cpu)
{
cpu_set_t *mask;
int ret;
alloc_cpu_set (&mask);
CPU_SET_S(cpu, size_cpumask, mask);
ret = sched_setaffinity(0, size_cpumask, mask);
CPU_FREE(mask);
if (ret == -1)
return -1;
else
return 0;
}
static int cpumask_to_str(cpu_set_t *mask, char *buf, int length)
{
int i;
int offset = 0;
for (i = 0; i < topo_max_cpus; i++) {
if (!CPU_ISSET_S(i, size_cpumask, mask))
continue;
if (length - 1 < offset) {
lpmd_log_debug ("cpumask_to_str: Too many cpus\n");
return 1;
}
offset += snprintf (buf + offset, length - 1 - offset, "%d,", i);
}
buf[offset - 1] = '\0';
return 0;
}
static char to_hexchar(int val)
{
if (val <= 9)
return val + '0';
if (val >= 16)
return -1;
return val - 10 + 'a';
}
static int cpumask_to_hexstr(cpu_set_t *mask, char *str, int size)
{
int cpu;
int i;
int pos = 0;
char c = 0;
for (cpu = 0; cpu < topo_max_cpus; cpu++) {
i = cpu % 4;
if (!i)
c = 0;
if (CPU_ISSET_S(cpu, size_cpumask, mask))
c += (1 << i);
if (i == 3) {
str[pos] = to_hexchar (c);
pos++;
if (pos >= size)
return -1;
}
}
str[pos] = '\0';
pos--;
for (i = 0; i <= pos / 2; i++) {
c = str[i];
str[i] = str[pos - i];
str[pos - i] = c;
}
return 0;
}
static char* get_cpus_str(enum cpumask_idx idx)
{
if (!cpumasks[idx].mask)
return NULL;
if (!CPU_COUNT_S(size_cpumask, cpumasks[idx].mask))
return NULL;
if (cpumasks[idx].str)
return cpumasks[idx].str;
cpumasks[idx].str = calloc (1, MAX_STR_LENGTH);
if (!cpumasks[idx].str)
err (3, "STR_ALLOC");
cpumask_to_str (cpumasks[idx].mask, cpumasks[idx].str, MAX_STR_LENGTH);
return cpumasks[idx].str;
}
static char* get_cpus_hexstr(enum cpumask_idx idx)
{
if (!cpumasks[idx].mask)
return NULL;
if (!CPU_COUNT_S(size_cpumask, cpumasks[idx].mask))
return NULL;
if (cpumasks[idx].hexstr)
return cpumasks[idx].hexstr;
cpumasks[idx].hexstr = calloc (1, MAX_STR_LENGTH);
if (!cpumasks[idx].hexstr)
err (3, "STR_ALLOC");
cpumask_to_hexstr (cpumasks[idx].mask, cpumasks[idx].hexstr, MAX_STR_LENGTH);
return cpumasks[idx].hexstr;
}
char* get_lpm_cpus_hexstr(void)
{
return get_cpus_hexstr (lpm_cpus_cur);
}
static char* get_cpus_hexstr_reverse(enum cpumask_idx idx)
{
cpu_set_t *mask;
if (!cpumasks[idx].mask)
return NULL;
if (!CPU_COUNT_S(size_cpumask, cpumasks[idx].mask))
return NULL;
if (cpumasks[idx].hexstr_reverse)
return cpumasks[idx].hexstr_reverse;
cpumasks[idx].hexstr_reverse = calloc (1, MAX_STR_LENGTH);
if (!cpumasks[idx].hexstr_reverse)
err (3, "STR_ALLOC");
alloc_cpu_set (&mask);
CPU_XOR_S(size_cpumask, mask, cpumasks[idx].mask, cpumasks[CPUMASK_ONLINE].mask);
cpumask_to_hexstr (mask, cpumasks[idx].hexstr_reverse, MAX_STR_LENGTH);
CPU_FREE(mask);
return cpumasks[idx].hexstr_reverse;
}
static char* get_cpus_str_reverse(enum cpumask_idx idx)
{
cpu_set_t *mask;
if (!cpumasks[idx].mask)
return NULL;
if (!CPU_COUNT_S(size_cpumask, cpumasks[idx].mask))
return NULL;
if (cpumasks[idx].str_reverse)
return cpumasks[idx].str_reverse;
cpumasks[idx].str_reverse = calloc (1, MAX_STR_LENGTH);
if (!cpumasks[idx].str_reverse)
err (3, "STR_ALLOC");
alloc_cpu_set (&mask);
CPU_XOR_S(size_cpumask, mask, cpumasks[idx].mask, cpumasks[CPUMASK_ONLINE].mask);
cpumask_to_str (mask, cpumasks[idx].str_reverse, MAX_STR_LENGTH);
CPU_FREE(mask);
return cpumasks[idx].str_reverse;
}
static int get_cpus_hexvals(enum cpumask_idx idx, uint8_t *vals, int size)
{
int i, j, k;
uint8_t v = 0;
if (!cpumasks[idx].mask)
return -1;
for (i = 0; i < topo_max_cpus; i++) {
j = i % 8;
k = i / 8;
if (k >= size) {
lpmd_log_error ("size too big\n");
return -1;
}
if (!CPU_ISSET_S(i, size_cpumask, cpumasks[idx].mask))
goto set_val;
v |= 1 << j;
set_val: if (j == 7) {
vals[k] = v;
v = 0;
}
}
return 0;
}
int has_cpus(enum cpumask_idx idx)
{
if (!cpumasks[idx].mask)
return 0;
return CPU_COUNT_S(size_cpumask, cpumasks[idx].mask);
}
int has_lpm_cpus(void)
{
return has_cpus (lpm_cpus_cur);
}
static int _add_cpu(int cpu, enum cpumask_idx idx)
{
if (idx != CPUMASK_ONLINE && !is_cpu_online (cpu))
return 0;
if (!cpumasks[idx].mask)
alloc_cpu_set (&cpumasks[idx].mask);
if (idx != CPUMASK_ONLINE && CPU_COUNT_S(size_cpumask, cpumasks[idx].mask) >= MAX_LPM_CPUS)
return LPMD_FATAL_ERROR;
CPU_SET_S(cpu, size_cpumask, cpumasks[idx].mask);
return LPMD_SUCCESS;
}
int add_cpu(int cpu, enum cpumask_idx idx)
{
if (cpu < 0 || cpu >= topo_max_cpus)
return 0;
_add_cpu (cpu, idx);
if (idx == CPUMASK_LPM_DEFAULT)
lpmd_log_info ("\tDetected %s CPU%d\n", cpumasks[idx].name, cpu);
else
lpmd_log_debug ("\tDetected %s CPU%d\n", cpumasks[idx].name, cpu);
return 0;
}
void reset_cpus(enum cpumask_idx idx)
{
if (cpumasks[idx].mask)
CPU_ZERO_S(size_cpumask, cpumasks[idx].mask);
free (cpumasks[idx].str);
free (cpumasks[idx].str_reverse);
free (cpumasks[idx].hexstr);
free (cpumasks[idx].hexstr_reverse);
cpumasks[idx].str = NULL;
cpumasks[idx].str_reverse = NULL;
cpumasks[idx].hexstr = NULL;
cpumasks[idx].hexstr_reverse = NULL;
lpm_cpus_cur = CPUMASK_LPM_DEFAULT;
}
int set_lpm_cpus(enum cpumask_idx new)
{
if (lpm_cpus_cur == new)
return 0;
if (new == CPUMASK_HFI_SUV)
CPU_XOR_S(size_cpumask, cpumasks[new].mask, cpumasks[CPUMASK_ONLINE].mask,
cpumasks[new].mask);
lpm_cpus_cur = new;
return 0;
}
#define BITMASK_SIZE 32
static int set_max_cpu_num(void)
{
FILE *filep;
unsigned long dummy;
int i;
topo_max_cpus = 0;
for (i = 0; i < 256; ++i) {
char path[MAX_STR_LENGTH];
snprintf (path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i);
filep = fopen (path, "r");
if (filep)
break;
}
if (!filep) {
lpmd_log_error ("Can't get max cpu number\n");
return -1;
}
while (fscanf (filep, "%lx,", &dummy) == 1)
topo_max_cpus += BITMASK_SIZE;
fclose (filep);
lpmd_log_debug ("\t%d CPUs supported in maximum\n", topo_max_cpus);
return 0;
}
static int uevent_fd = -1;
int uevent_init(void)
{
struct sockaddr_nl nls;
memset (&nls, 0, sizeof(struct sockaddr_nl));
nls.nl_family = AF_NETLINK;
nls.nl_pid = getpid();
nls.nl_groups = -1;
uevent_fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (uevent_fd < 0)
return uevent_fd;
if (bind (uevent_fd, (struct sockaddr*) &nls, sizeof(struct sockaddr_nl))) {
lpmd_log_warn ("kob_uevent bind failed \n");
close (uevent_fd);
return -1;
}
lpmd_log_debug ("Uevent binded\n");
return uevent_fd;
}
static int has_cpu_uevent(void)
{
ssize_t i = 0;
ssize_t len;
const char *dev_path = "DEVPATH=";
unsigned int dev_path_len = strlen(dev_path);
const char *cpu_path = "/devices/system/cpu/cpu";
char buffer[MAX_STR_LENGTH];
len = recv (uevent_fd, buffer, sizeof(buffer) - 1, MSG_DONTWAIT);
if (len <= 0)
return 0;
buffer[len] = '\0';
lpmd_log_debug ("Receive uevent: %s\n", buffer);
while (i < len) {
if (strlen (buffer + i) > dev_path_len
&& !strncmp (buffer + i, dev_path, dev_path_len)) {
if (!strncmp (buffer + i + dev_path_len, cpu_path,
strlen (cpu_path))) {
lpmd_log_debug ("\tMatches: %s\n", buffer + i + dev_path_len);
return 1;
}
}
i += strlen (buffer + i) + 1;
}
return 0;
}
#define PATH_PROC_STAT "/proc/stat"
int check_cpu_hotplug(void)
{
FILE *filep;
static cpu_set_t *curr;
static cpu_set_t *prev;
cpu_set_t *tmp;
if (!has_cpu_uevent ())
return 0;
if (!curr) {
alloc_cpu_set (&curr);
alloc_cpu_set (&prev);
CPU_OR_S (size_cpumask, curr, cpumasks[CPUMASK_ONLINE].mask, cpumasks[CPUMASK_ONLINE].mask);
}
tmp = prev;
prev = curr;
curr = tmp;
CPU_ZERO_S (size_cpumask, curr);
filep = fopen (PATH_PROC_STAT, "r");
if (!filep)
return 0;
while (!feof (filep)) {
char *tmpline = NULL;
size_t size = 0;
char *line;
int cpu;
char *p;
int ret;
tmpline = NULL;
size = 0;
if (getline (&tmpline, &size, filep) <= 0) {
free (tmpline);
break;
}
line = strdup (tmpline);
p = strtok (line, " ");
ret = sscanf (p, "cpu%d", &cpu);
if (ret != 1)
goto free;
CPU_SET_S (cpu, size_cpumask, curr);
free:
free (tmpline);
free (line);
}
fclose (filep);
/* CPU Hotplug detected, should freeze lpmd */
if (!CPU_EQUAL_S (size_cpumask, curr, cpumasks[CPUMASK_ONLINE].mask)) {
lpmd_log_debug ("check_cpu_hotplug: CPU Hotplug detected, freeze lpmd\n");
return freeze_lpm ();
}
/* CPU restored to original state, should restore lpmd */
if (CPU_EQUAL_S (size_cpumask, curr, cpumasks[CPUMASK_ONLINE].mask) &&
!CPU_EQUAL_S (size_cpumask, curr, prev)) {
lpmd_log_debug ("check_cpu_hotplug: CPU Hotplug restored, restore lpmd\n");
return restore_lpm ();
}
/* No update since last change */
return 0;
}
/* Bit 15 of CPUID.7 EDX stands for Hybrid support */
#define CPUFEATURE_HYBRID (1 << 15)
#define PATH_PM_PROFILE "/sys/firmware/acpi/pm_profile"
struct cpu_model_entry {
unsigned int family;
unsigned int model;
};
static struct cpu_model_entry id_table[] = {
{ 6, 0x97 }, // Alderlake
{ 6, 0x9a }, // Alderlake
{ 6, 0xb7 }, // Raptorlake
{ 6, 0xba }, // Raptorlake
{ 6, 0xbf }, // Raptorlake S
{ 6, 0xaa }, // Meteorlake
{ 6, 0xac }, // Meteorlake
{ 0, 0 } // Last Invalid entry
};
#define cpuid(leaf, eax, ebx, ecx, edx) \
__cpuid(leaf, eax, ebx, ecx, edx); \
lpmd_log_debug("CPUID 0x%08x: eax = 0x%08x ebx = 0x%08x ecx = 0x%08x edx = 0x%08x\n", \
leaf, eax, ebx, ecx, edx);
#define cpuid_count(leaf, subleaf, eax, ebx, ecx, edx) \
__cpuid_count(leaf, subleaf, eax, ebx, ecx, edx); \
lpmd_log_debug("CPUID 0x%08x subleaf 0x%08x: eax = 0x%08x ebx = 0x%08x ecx = 0x%08x" \
"edx = 0x%08x\n", leaf, subleaf, eax, ebx, ecx, edx);
static int detect_supported_cpu(void)
{
unsigned int eax, ebx, ecx, edx;
unsigned int max_level, family, model, stepping;
int val;
cpuid(0, eax, ebx, ecx, edx);
/* Unsupported vendor */
if (ebx != 0x756e6547 || edx != 0x49656e69 || ecx != 0x6c65746e)
return -1;
max_level = eax;
cpuid(1, eax, ebx, ecx, edx);
family = (eax >> 8) & 0xf;
model = (eax >> 4) & 0xf;
stepping = eax & 0xf;
if (family == 6)
model += ((eax >> 16) & 0xf) << 4;
lpmd_log_info("%u CPUID levels; family:model:stepping 0x%x:%x:%x (%u:%u:%u)\n",
max_level, family, model, stepping, family, model, stepping);
val = 0;
while (id_table[val].family) {
if (id_table[val].family == family && id_table[val].model == model)
break;
val++;
}
/* Unsupported model */
if (!id_table[val].family || max_level < 0x1a) {
lpmd_log_info("Unsupported platform\n");
return -1;
}
cpuid_count(7, 0, eax, ebx, ecx, edx);
/* Run on Hybrid platforms only */
if (!(edx & CPUFEATURE_HYBRID)) {
lpmd_log_debug("Non-Hybrid platform detected\n");
return -1;
}
/* /sys/firmware/acpi/pm_profile is mandatory */
if (lpmd_read_int(PATH_PM_PROFILE, &val, -1)) {
lpmd_log_debug("Failed to read %s\n", PATH_PM_PROFILE);
return -1;
}
if (val != 2) {
lpmd_log_debug("Non Mobile platform detected\n");
return -1;
}
return 0;
}
static int parse_cpu_topology(void)
{
FILE *filep;
int i;
char path[MAX_STR_LENGTH];
int ret;
ret = detect_supported_cpu();
if (ret) {
lpmd_log_info("Unsupported CPU type\n");
return ret;
}
ret = set_max_cpu_num ();
if (ret)
return ret;
reset_cpus (CPUMASK_ONLINE);
for (i = 0; i < topo_max_cpus; i++) {
unsigned int online;
snprintf (path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", i);
filep = fopen (path, "r");
if (filep) {
if (fscanf (filep, "%u", &online) != 1)
lpmd_log_warn ("fread failed for %s\n", path);
fclose (filep);
}
else if (!i)
online = 1;
else
break;
if (!online)
continue;
add_cpu (i, CPUMASK_ONLINE);
}
max_online_cpu = i;
return 0;
}
/* Run intel_lpmd on the LP-Mode CPUs only */
static void lpmd_set_cpu_affinity(void)
{
if (!cpumasks[CPUMASK_LPM_DEFAULT].mask)
return;
if (!CPU_COUNT_S (size_cpumask, cpumasks[CPUMASK_LPM_DEFAULT].mask))
return;
if (!sched_setaffinity (0, size_cpumask, cpumasks[CPUMASK_LPM_DEFAULT].mask))
lpmd_log_info ("\tSet intel_lpmd cpu affinity to CPU %s\n", get_cpus_str (CPUMASK_LPM_DEFAULT));
else
lpmd_log_warn ("\tFailed to set intel_lpmd cpu affinity\n");
}
/*
* Detect LPM cpus
* parse cpuset with following syntax
* 1,2,4..6,8-10 and set bits in cpu_subset
*/
static int parse_cpu_str(char *buf, enum cpumask_idx idx)
{
unsigned int start, end;
char *next;
int nr_cpus = 0;
if (buf[0] == '\0')
return 0;
next = buf;
while (next && *next) {
if (*next == '\n')
*next = '\0';
next++;
}
next = buf;
while (next && *next) {
if (*next == '\n')
*next = '\0';
if (*next == '-') /* no negative cpu numbers */
goto error;
start = strtoul (next, &next, 10);
_add_cpu (start, idx);
nr_cpus++;
if (*next == '\0')
break;
if (*next == ',') {
next += 1;
continue;
}
if (*next == '-') {
next += 1; /* start range */
}
else if (*next == '.') {
next += 1;
if (*next == '.')
next += 1; /* start range */
else
goto error;
}
end = strtoul (next, &next, 10);
if (end <= start)
goto error;
while (++start <= end) {
_add_cpu (start, idx);
nr_cpus++;
}
if (*next == ',')
next += 1;
else if (*next != '\0')
goto error;
}
return nr_cpus;
error:
lpmd_log_error ("CPU string malformed: %s\n", buf);
return -1;
}
static int detect_lpm_cpus_cmd(char *cmd)
{
int ret;
ret = parse_cpu_str (cmd, CPUMASK_LPM_DEFAULT);
if (ret <= 0)
reset_cpus (CPUMASK_LPM_DEFAULT);
return ret;
}
/*
* Use one Ecore Module as LPM CPUs.
* Applies on Hybrid platforms like AlderLake/RaptorLake.
*/
static int is_cpu_atom(int cpu)
{
unsigned int eax, ebx, ecx, edx, subleaf;
unsigned int type;
if (cpu_migrate(cpu) < 0) {
lpmd_log_error("Failed to migrated to cpu%d\n", cpu);
return -1;
}
cpuid(0x1a, eax, ebx, ecx, edx);
type = (eax >> 24) & 0xFF;
return type == 0x20;
}
static int detect_lpm_cpus_cluster(void)
{
FILE *filep;
char path[MAX_STR_LENGTH];
char str[MAX_STR_LENGTH];
int i, ret;
for (i = topo_max_cpus; i >= 0; i--) {
if (!is_cpu_online (i))
continue;
snprintf (path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/cluster_cpus_list",
i);
path[MAX_STR_LENGTH - 1] = '\0';
filep = fopen (path, "r");
if (!filep)
continue;
ret = fread (str, 1, MAX_STR_LENGTH - 1, filep);
fclose (filep);
if (ret <= 0)
continue;
str[ret] = '\0';
if (parse_cpu_str (str, CPUMASK_LPM_DEFAULT) <= 0)
continue;
/* An Ecore module contains 4 Atom cores */
if (CPU_COUNT_S(size_cpumask, cpumasks[CPUMASK_LPM_DEFAULT].mask) == 4 && is_cpu_atom(i))
break;
reset_cpus (CPUMASK_LPM_DEFAULT);
}
if (!has_cpus (CPUMASK_LPM_DEFAULT)) {
reset_cpus (CPUMASK_LPM_DEFAULT);
return 0;
}
return CPU_COUNT_S(size_cpumask, cpumasks[CPUMASK_LPM_DEFAULT].mask);
}
static int detect_cpu_l3(int cpu)
{
unsigned int eax, ebx, ecx, edx, subleaf;
if (cpu_migrate(cpu) < 0) {
lpmd_log_error("Failed to migrated to cpu%d\n", cpu);
return -1;
}
for(subleaf = 0;; subleaf++) {
unsigned int type, level;
cpuid_count(4, subleaf, eax, ebx, ecx, edx);
type = eax & 0x1f;
level = (eax >> 5) & 0x7;
/* No more caches */
if (!type)
break;
/* Unified Cache */
if (type !=3 )
continue;
/* L3 */
if (level != 3)
continue;
/* Do nothing about CPUs that have L3 */
return 0;
}
/* Use CPUs don't have L3 as LPM CPUs */
_add_cpu (cpu, CPUMASK_LPM_DEFAULT);
return 0;
}
/*
* Use CPUs that don't have L3 as LPM CPUs.
* Applies on platforms like MeteorLake.
*/
static int detect_lpm_cpus_l3(void)
{
char path[MAX_STR_LENGTH];
int i;
for (i = 0; i < topo_max_cpus; i++) {
if (!is_cpu_online (i))
continue;
if (detect_cpu_l3(i) < 0)
return -1;
}
/* All cpus has L3 */
if (!has_cpus (CPUMASK_LPM_DEFAULT))
return 0;
/* All online cpus don't have L3 */
if (CPU_EQUAL_S(size_cpumask, cpumasks[CPUMASK_LPM_DEFAULT].mask,
cpumasks[CPUMASK_ONLINE].mask))
goto err;
return CPU_COUNT_S(size_cpumask, cpumasks[CPUMASK_LPM_DEFAULT].mask);
err: reset_cpus (CPUMASK_LPM_DEFAULT);
return 0;
}
static int detect_lpm_cpus(char *cmd_cpus)
{
int ret;
char *str;
if (cmd_cpus && cmd_cpus[0] != '\0') {
ret = detect_lpm_cpus_cmd (cmd_cpus);
if (ret <= 0) {
lpmd_log_error ("\tInvalid -c parameter: %s\n", cmd_cpus);
exit (-1);
}
str = "CommandLine";
goto end;
}
ret = detect_lpm_cpus_l3 ();
if (ret < 0)
return ret;
if (ret > 0) {
str = "Lcores";
goto end;
}
if (detect_lpm_cpus_cluster ()) {
str = "Ecores";
goto end;
}
if (has_hfi_lpm_monitor () || has_hfi_suv_monitor ()) {
lpmd_log_info (
"\tNo valid Low Power CPUs detected, use dynamic Low Power CPUs from HFI hints\n");
return 0;
}
else {
lpmd_log_error ("\tNo valid Low Power CPUs detected, exit\n");
exit (1);
}
end: if (has_cpus (CPUMASK_LPM_DEFAULT))
lpmd_log_info ("\tUse CPU %s as Default Low Power CPUs (%s)\n",
get_cpus_str (CPUMASK_LPM_DEFAULT), str);
lpmd_set_cpu_affinity ();
return 0;
}
static int check_cpu_offline_support(void)
{
return lpmd_open ("/sys/devices/system/cpu/cpu0/online", 1);
}
static int online_cpu(int cpu, int val)
{
char path[MAX_STR_LENGTH];
snprintf (path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", cpu);
return lpmd_write_int (path, val, LPMD_LOG_INFO);
}
static int process_cpu_offline(int enter)
{
int cpu;
lpmd_log_info ("\t%s CPUs\n", enter ? "Offline" : "Online");
for (cpu = 0; cpu < topo_max_cpus; cpu++) {
if (!is_cpu_online (cpu))
continue;
if (!is_cpu_for_lpm (cpu)) {
if (enter)
online_cpu (cpu, 0);
else
online_cpu (cpu, 1);
}
else {
online_cpu (cpu, 1);
}
}
return 0;
}
/* Support for LPM_CPU_CGROUPV2 */
#define PATH_CGROUP "/sys/fs/cgroup"
#define PATH_CG2_SUBTREE_CONTROL PATH_CGROUP "/cgroup.subtree_control"
static int update_allowed_cpus(const char *unit, uint8_t *vals, int size)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
char buf[MAX_STR_LENGTH];
int offset;
int ret;
int i;
// creates a new, independent bus connection to the system bus
ret = sd_bus_open_system (&bus);
if (ret < 0) {
fprintf (stderr, "Failed to connect to system bus: %s\n", strerror (-ret));
goto finish;
}
/*
* creates a new bus message object that encapsulates a D-Bus method call,
* and returns it in the m output parameter.
* The call will be made on the destination, path, on the interface, member.
*/
/* Issue the method call and store the response message in m */
ret = sd_bus_message_new_method_call (bus, &m, "org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SetUnitProperties");
if (ret < 0) {
fprintf (stderr, "Failed to issue method call: %s\n", error.message);
goto finish;
}
// Attach fields to a D-Bus message based on a type string
ret = sd_bus_message_append (m, "sb", unit, 1);
if (ret < 0) {
fprintf (stderr, "Failed to append unit: %s\n", error.message);
goto finish;
}
/*
* appends a new container to the message m.
* After opening a new container, it can be filled with content using
* sd_bus_message_append(3) and similar functions.
* Containers behave like a stack. To nest containers inside each other,
* call sd_bus_message_open_container() multiple times without calling
* sd_bus_message_close_container() in between. Each container will be
* nested inside the previous container.
* Instead of literals, the corresponding constants SD_BUS_TYPE_STRUCT,
* SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_VARIANT or SD_BUS_TYPE_DICT_ENTRY can also be used.
*/
ret = sd_bus_message_open_container (m, SD_BUS_TYPE_ARRAY, "(sv)");
if (ret < 0) {
fprintf (stderr, "Failed to append array: %s\n", error.message);
goto finish;
}
ret = sd_bus_message_open_container (m, SD_BUS_TYPE_STRUCT, "sv");
if (ret < 0) {
fprintf (stderr, "Failed to open container struct: %s\n", error.message);
goto finish;
}
/*
* appends a single field to the message m.
* The parameter type determines how the pointer p is interpreted.
*/
ret = sd_bus_message_append_basic (m, SD_BUS_TYPE_STRING, "AllowedCPUs");
if (ret < 0) {
fprintf (stderr, "Failed to append string: %s\n", error.message);
goto finish_1;
}
ret = sd_bus_message_open_container (m, 'v', "ay");
if (ret < 0) {
fprintf (stderr, "Failed to open container: %s\n", error.message);
goto finish_2;
}
/*
* appends an array to a D-Bus message m. A container will be opened,
* the array contents appended, and the container closed.
*/
ret = sd_bus_message_append_array (m, 'y', vals, size);
if (ret < 0) {
fprintf (stderr, "Failed to append allowed_cpus: %s\n", error.message);
goto finish_2;
}
offset = snprintf (buf, MAX_STR_LENGTH, "\tSending Dbus message to systemd: %s: ", unit);
for (i = 0; i < size; i++) {
if (offset < MAX_STR_LENGTH)
offset += snprintf (buf + offset, MAX_STR_LENGTH - offset, "0x%02x ", vals[i]);
}
buf[MAX_STR_LENGTH - 1] = '\0';
lpmd_log_info ("%s\n", buf);
sd_bus_message_close_container (m);
finish_2: sd_bus_message_close_container (m);
finish_1: sd_bus_message_close_container (m);
finish: if (ret >= 0) {
ret = sd_bus_call (bus, m, 0, &error, NULL);
if (ret < 0) {
fprintf (stderr, "Failed to call: %s\n", error.message);
}
}
sd_bus_error_free (&error);
sd_bus_message_unref (m);
sd_bus_unref (bus);
return ret < 0 ? -1 : 0;
}
static int restore_systemd_cgroup()
{
int size = topo_max_cpus / 8;
uint8_t *vals;
vals = calloc (size, 1);
get_cpus_hexvals (CPUMASK_ONLINE, vals, size);
update_allowed_cpus ("system.slice", vals, size);
update_allowed_cpus ("user.slice", vals, size);
update_allowed_cpus ("machine.slice", vals, size);
free (vals);
return 0;
}
static int update_systemd_cgroup()
{
int size = topo_max_cpus / 8;
uint8_t *vals;
int ret;
vals = calloc (size, 1);
get_cpus_hexvals (lpm_cpus_cur, vals, size);
ret = update_allowed_cpus ("system.slice", vals, size);
if (ret)
goto restore;
ret = update_allowed_cpus ("user.slice", vals, size);
if (ret)
goto restore;
ret = update_allowed_cpus ("machine.slice", vals, size);
if (ret)
goto restore;
free (vals);
return 0;
restore: free (vals);
restore_systemd_cgroup ();
return ret;
}
static int check_cpu_cgroupv2_support(void)
{
if (lpmd_write_str (PATH_CG2_SUBTREE_CONTROL, "+cpuset", LPMD_LOG_DEBUG))
return 1;
return 0;
}
static int process_cpu_cgroupv2_enter(void)
{
if (lpmd_write_str (PATH_CG2_SUBTREE_CONTROL, "+cpuset", LPMD_LOG_INFO))
return 1;
return update_systemd_cgroup ();
}
static int process_cpu_cgroupv2_exit(void)
{
restore_systemd_cgroup ();
return lpmd_write_str (PATH_CG2_SUBTREE_CONTROL, "-cpuset", LPMD_LOG_INFO);
}
static int process_cpu_cgroupv2(int enter)
{
if (enter)
return process_cpu_cgroupv2_enter ();
else
return process_cpu_cgroupv2_exit ();
}
/*
* Support for LPM_CPU_POWERCLAMP:
* /sys/module/intel_powerclamp/parameters/cpumask
* /sys/module/intel_powerclamp/parameters/max_idle
*/
#define PATH_CPUMASK "/sys/module/intel_powerclamp/parameters/cpumask"
#define PATH_MAXIDLE "/sys/module/intel_powerclamp/parameters/max_idle"
#define PATH_DURATION "/sys/module/intel_powerclamp/parameters/duration"
#define PATH_THERMAL "/sys/class/thermal"
static char path_powerclamp[MAX_STR_LENGTH * 2];
static int check_cpu_powerclamp_support(void)
{
FILE *filep;
DIR *dir;
struct dirent *entry;
char *name = "intel_powerclamp";
char str[20];
int ret;
if (lpmd_open (PATH_CPUMASK, 0))
return 1;
if ((dir = opendir (PATH_THERMAL)) == NULL) {
perror ("opendir() error");
return 1;
}
while ((entry = readdir (dir)) != NULL) {
if (strlen (entry->d_name) > 100)
continue;
snprintf (path_powerclamp, MAX_STR_LENGTH * 2, "%s/%s/type", PATH_THERMAL, entry->d_name);
filep = fopen (path_powerclamp, "r");
if (!filep)
continue;
ret = fread (str, strlen (name), 1, filep);
fclose (filep);
if (ret != 1)
continue;
if (!strncmp (str, name, strlen (name))) {
snprintf (path_powerclamp, MAX_STR_LENGTH * 2, "%s/%s/cur_state", PATH_THERMAL,
entry->d_name);
lpmd_log_info ("\tFound %s device at %s/%s\n", name, PATH_THERMAL, entry->d_name);
break;
}
}
closedir (dir);
if (path_powerclamp[0] == '\0')
return 1;
return 0;
}
static int default_dur = -1;
static int _process_cpu_powerclamp_enter(char *cpumask_str, int pct, int dur)
{
if (lpmd_write_str (PATH_CPUMASK, cpumask_str, LPMD_LOG_INFO))
return 1;
if (dur > 0) {
if (lpmd_read_int (PATH_DURATION, &default_dur, LPMD_LOG_INFO))
return 1;
if (lpmd_write_int (PATH_DURATION, dur, LPMD_LOG_INFO))
return 1;
}
if (lpmd_write_int (PATH_MAXIDLE, pct, LPMD_LOG_INFO))
return 1;
if (lpmd_write_int (path_powerclamp, pct, LPMD_LOG_INFO))
return 1;
return 0;
}
static int process_cpu_powerclamp_enter(void)
{
int pct = get_idle_percentage ();
int dur = get_idle_duration ();
return _process_cpu_powerclamp_enter (get_cpus_hexstr_reverse (lpm_cpus_cur), pct, dur);
}
static int process_cpu_powerclamp_exit()
{
if (lpmd_write_int (PATH_DURATION, default_dur, LPMD_LOG_INFO))
return 1;
return lpmd_write_int (path_powerclamp, 0, LPMD_LOG_INFO);
}
static int process_cpu_powerclamp(int enter)
{
if (enter)
return process_cpu_powerclamp_enter ();
else
return process_cpu_powerclamp_exit ();
}
// Support for SUV mode, which uses powerclamp
#define SUV_IDLE_PCT 50
static int in_suv;
static int enter_suv_mode(enum lpm_command cmd)
{
int ret;
char *cpumask_str;
char *name;
// in_suv can either be HFI_SUV or DBUS_SUV, can not be both
if (in_suv)
return 0;
if (cmd == HFI_SUV_ENTER) {
cpumask_str = get_cpus_hexstr (CPUMASK_HFI_SUV);
name = "HFI";
}
else {
cpumask_str = get_cpus_hexstr (CPUMASK_ONLINE);
name = "DBUS";
}
/*
* When system is in LPM and it uses idle injection for LPM,
* we need to exit LPM first because we need to reset the cpumask
* of the intel_powerclamp sysfs I/F.
*
* In order to make the logic simpler, always exit LPM when Idle
* injection is used for LPM.
* The downside is that we need to do an extra LPM exit but this
* should be rare because it is abnormal to get an SUV request when
* system is already in LPM.
*/
if (get_cpu_mode () == LPM_CPU_POWERCLAMP)
process_lpm_unlock (cmd);
lpmd_log_info ("------ Enter %s Survivability Mode ---\n", name);
ret = _process_cpu_powerclamp_enter (cpumask_str, SUV_IDLE_PCT, -1);
if (!ret)
in_suv = cmd;
return ret;
}
static int exit_suv_mode(enum lpm_command cmd)
{
int cmd_enter;
char *name;
// If SUV mode is disabled or exited
if (in_suv == -1 || in_suv == 0)
return 0;
if (cmd == HFI_SUV_EXIT) {
cmd_enter = HFI_SUV_ENTER;
name = "HFI";
}
else {
cmd_enter = DBUS_SUV_ENTER;
name = "DBUS";
}
if (in_suv != cmd_enter)
return 0;
lpmd_log_info ("------ Exit %s Survivability Mode ---\n", name);
process_cpu_powerclamp_exit ();
// Try to re-enter in case it was FORCED ON
if (get_cpu_mode () == LPM_CPU_POWERCLAMP)
process_lpm_unlock (cmd);
in_suv = 0;
return LPMD_SUCCESS;
}
int process_suv_mode(enum lpm_command cmd)
{
int ret;
lpmd_lock ();
if (cmd == HFI_SUV_ENTER || cmd == DBUS_SUV_ENTER)
ret = enter_suv_mode (cmd);
else if (cmd == HFI_SUV_EXIT || cmd == DBUS_SUV_EXIT)
ret = exit_suv_mode (cmd);
else
ret = -1;
lpmd_unlock ();
return ret;
}
int has_suv_support(void)
{
return !(in_suv == -1);
}
int in_hfi_suv_mode(void)
{
int ret;
lpmd_lock ();
ret = in_suv;
lpmd_unlock ();
return ret == HFI_SUV_ENTER;
}
static int check_cpu_isolate_support(void)
{
return check_cpu_cgroupv2_support ();
}
static int process_cpu_isolate_enter(void)
{
DIR *dir;
int ret;
dir = opendir ("/sys/fs/cgroup/lpm");
if (!dir) {
ret = mkdir ("/sys/fs/cgroup/lpm", 0744);
if (ret) {
printf ("Can't create dir:%s errno:%d\n", "/sys/fs/cgroup/lpm", errno);
return ret;
}
lpmd_log_info ("\tCreate %s\n", "/sys/fs/cgroup/lpm");
} else {
closedir (dir);
}
if (lpmd_write_str ("/sys/fs/cgroup/lpm/cpuset.cpus.partition", "member", LPMD_LOG_INFO))
return 1;
if (lpmd_write_str ("/sys/fs/cgroup/lpm/cpuset.cpus", get_cpus_str_reverse (lpm_cpus_cur),
LPMD_LOG_INFO))
return 1;
if (lpmd_write_str ("/sys/fs/cgroup/lpm/cpuset.cpus.partition", "isolated", LPMD_LOG_INFO))
return 1;
return 0;
}
static int process_cpu_isolate_exit(void)
{
if (lpmd_write_str ("/sys/fs/cgroup/lpm/cpuset.cpus.partition", "member", LPMD_LOG_INFO))
return 1;
if (lpmd_write_str ("/sys/fs/cgroup/lpm/cpuset.cpus", get_cpus_str (CPUMASK_ONLINE),
LPMD_LOG_INFO))
return 1;
return 0;
}
static int process_cpu_isolate(int enter)
{
if (enter)
return process_cpu_isolate_enter ();
else
return process_cpu_isolate_exit ();
}
static int check_cpu_mode_support(enum lpm_cpu_process_mode mode)
{
int ret;
switch (mode) {
case LPM_CPU_OFFLINE:
ret = check_cpu_offline_support ();
break;
case LPM_CPU_CGROUPV2:
ret = check_cpu_cgroupv2_support ();
break;
case LPM_CPU_POWERCLAMP:
ret = check_cpu_powerclamp_support ();
break;
case LPM_CPU_ISOLATE:
ret = check_cpu_isolate_support ();
break;
default:
lpmd_log_error ("Invalid CPU process mode %d\n", mode);
exit (-1);
}
if (ret) {
lpmd_log_error ("Mode %d not supported\n", mode);
return ret;
}
// Extra checks for SUV mode support
if (mode != LPM_CPU_POWERCLAMP) {
if (check_cpu_powerclamp_support ()) {
in_suv = -1;
lpmd_log_info ("Idle injection interface not detected, disable SUV mode support\n");
}
}
return ret;
}
int init_cpu(char *cmd_cpus, enum lpm_cpu_process_mode mode)
{
int ret;
lpmd_log_info ("Detecting CPUs ...\n");
ret = parse_cpu_topology ();
if (ret)
return ret;
ret = detect_lpm_cpus (cmd_cpus);
if (ret)
return ret;
ret = check_cpu_mode_support (mode);
if (ret)
return ret;
return 0;
}
int process_cpus(int enter, enum lpm_cpu_process_mode mode)
{
int ret;
if (enter != 1 && enter != 0)
return LPMD_ERROR;
lpmd_log_info ("Process CPUs ...\n");
switch (mode) {
case LPM_CPU_OFFLINE:
ret = process_cpu_offline (enter);
break;
case LPM_CPU_CGROUPV2:
ret = process_cpu_cgroupv2 (enter);
break;
case LPM_CPU_POWERCLAMP:
ret = process_cpu_powerclamp (enter);
break;
case LPM_CPU_ISOLATE:
ret = process_cpu_isolate (enter);
break;
default:
exit (-1);
}
return ret;
}
intel-lpmd-0.0.3/src/lpmd_dbus_server.c 0000664 0000000 0000000 00000024052 14561114601 0020053 0 ustar 00root root 0000000 0000000 /*
* lpmd_dbus_server.c: Dbus server for intel_lpmd
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This file contains function to start dbus server and provide callbacks for
* dbus messages.
*/
#include
#include
#include
#include
#include "lpmd.h"
struct _PrefObject {
GObject parent;
};
#define PREF_TYPE_OBJECT (pref_object_get_type())
G_DECLARE_FINAL_TYPE(PrefObject, pref_object, PREF, OBJECT, GObject)
#define MAX_DBUS_REPLY_STR_LEN 100
G_DEFINE_TYPE(PrefObject, pref_object, G_TYPE_OBJECT)
static gboolean
dbus_interface_terminate(PrefObject *obj, GError **error);
static gboolean
dbus_interface_l_pm__fo_rc_e__on(PrefObject *obj, GError **error);
static gboolean
dbus_interface_l_pm__fo_rc_e__of_f(PrefObject *obj, GError **error);
static gboolean
dbus_interface_l_pm__au_to(PrefObject *obj, GError **error);
static gboolean
dbus_interface_s_uv__mo_de__en_te_r(PrefObject *obj, GError **error);
static gboolean
dbus_interface_s_uv__mo_de__ex_it(PrefObject *obj, GError **error);
#include "intel_lpmd_dbus_interface.h"
static gboolean
(*intel_lpmd_dbus_exit_callback)(void);
// Dbus object initialization
static void pref_object_init(PrefObject *obj)
{
g_assert (obj != NULL);
}
// Dbus object class initialization
static void pref_object_class_init(PrefObjectClass *_class)
{
g_assert (_class != NULL);
dbus_g_object_type_install_info ( PREF_TYPE_OBJECT, &dbus_glib_dbus_interface_object_info);
}
static gboolean dbus_interface_terminate(PrefObject *obj, GError **error)
{
lpmd_log_debug ("intel_lpmd_dbus_interface_terminate\n");
lpmd_terminate ();
if (intel_lpmd_dbus_exit_callback)
intel_lpmd_dbus_exit_callback ();
return TRUE;
}
static gboolean dbus_interface_l_pm__fo_rc_e__on(PrefObject *obj, GError **error)
{
lpmd_log_debug ("intel_lpmd_dbus_interface_lpm_enter\n");
lpmd_force_on ();
return TRUE;
}
static gboolean dbus_interface_l_pm__fo_rc_e__of_f(PrefObject *obj, GError **error)
{
lpmd_log_debug ("intel_lpmd_dbus_interface_lpm_exit\n");
lpmd_force_off ();
return TRUE;
}
static gboolean dbus_interface_l_pm__au_to(PrefObject *obj, GError **error)
{
lpmd_set_auto ();
return TRUE;
}
static gboolean dbus_interface_s_uv__mo_de__en_te_r(PrefObject *obj, GError **error)
{
lpmd_log_debug ("intel_lpmd_dbus_interface_suv_enter\n");
if (!has_suv_support ())
return FALSE;
lpmd_suv_enter ();
return TRUE;
}
static gboolean dbus_interface_s_uv__mo_de__ex_it(PrefObject *obj, GError **error)
{
if (!has_suv_support ())
return FALSE;
lpmd_log_debug ("intel_lpmd_dbus_interface_suv_exit\n");
lpmd_suv_exit ();
return TRUE;
}
#ifdef GDBUS
#pragma GCC diagnostic push
static GDBusInterfaceVTable interface_vtable;
extern gint watcher_id;
static GDBusNodeInfo *
lpmd_dbus_load_introspection(const gchar *filename, GError **error)
{
g_autoptr(GBytes) data = NULL;
g_autofree gchar *path = NULL;
path = g_build_filename("/org/freedesktop/intel_lpmd", filename, NULL);
data = g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
if (data == NULL)
return NULL;
return g_dbus_node_info_new_for_xml((gchar *)g_bytes_get_data(data, NULL), error);
}
static void
lpmd_dbus_handle_method_call(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
PrefObject *obj = PREF_OBJECT(user_data);
g_autoptr(GError) error = NULL;
lpmd_log_debug("Dbus method called %s %s.\n", interface_name, method_name);
if (g_strcmp0(method_name, "Terminate") == 0) {
g_dbus_method_invocation_return_value(invocation, NULL);
dbus_interface_terminate(obj, &error);
return;
}
if (g_strcmp0(method_name, "LPM_FORCE_ON") == 0) {
g_dbus_method_invocation_return_value(invocation, NULL);
dbus_interface_l_pm__fo_rc_e__on(obj, &error);
return;
}
if (g_strcmp0(method_name, "LPM_FORCE_OFF") == 0) {
g_dbus_method_invocation_return_value(invocation, NULL);
dbus_interface_l_pm__fo_rc_e__of_f(obj, &error);
return;
}
if (g_strcmp0(method_name, "LPM_AUTO") == 0) {
g_dbus_method_invocation_return_value(invocation, NULL);
dbus_interface_l_pm__au_to(obj, &error);
return;
}
if (g_strcmp0(method_name, "SUV_MODE_ENTER") == 0) {
g_dbus_method_invocation_return_value(invocation, NULL);
dbus_interface_s_uv__mo_de__en_te_r(obj, &error);
return;
}
if (g_strcmp0(method_name, "SUV_MODE_EXIT") == 0) {
g_dbus_method_invocation_return_value(invocation, NULL);
dbus_interface_s_uv__mo_de__ex_it(obj, &error);
return;
}
g_set_error(&error,
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD,
"no such method %s",
method_name);
g_dbus_method_invocation_return_gerror(invocation, error);
}
static GVariant *
lpmd_dbus_handle_get_property(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
return NULL;
}
static gboolean
lpmd_dbus_handle_set_property(GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data) {
return TRUE;
}
static void
lpmd_dbus_on_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data) {
guint registration_id;
GDBusProxy *proxy_id = NULL;
GError *error = NULL;
GDBusNodeInfo *introspection_data = NULL;
if (user_data == NULL) {
lpmd_log_error("user_data is NULL\n");
return;
}
introspection_data = lpmd_dbus_load_introspection("src/intel_lpmd_dbus_interface.xml",
&error);
if (error != NULL) {
lpmd_log_error("Couldn't create introspection data: %s:\n",
error->message);
return;
}
registration_id = g_dbus_connection_register_object(connection,
"/org/freedesktop/intel_lpmd",
introspection_data->interfaces[0],
&interface_vtable,
user_data,
NULL,
&error);
proxy_id = g_dbus_proxy_new_sync(connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
NULL,
&error);
g_assert(registration_id > 0);
g_assert(proxy_id != NULL);
}
static void
lpmd_dbus_on_name_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data) {
}
static void
lpmd_dbus_on_name_lost(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_warning("Lost the name %s\n", name);
exit(1);
}
// Set up Dbus server with GDBus
int intel_dbus_server_init(gboolean (*exit_handler)(void)) {
PrefObject *value_obj;
intel_lpmd_dbus_exit_callback = exit_handler;
value_obj = PREF_OBJECT(g_object_new(PREF_TYPE_OBJECT, NULL));
if (value_obj == NULL) {
lpmd_log_error("Failed to create one Value instance:\n");
return LPMD_FATAL_ERROR;
}
interface_vtable.method_call = lpmd_dbus_handle_method_call;
interface_vtable.get_property = lpmd_dbus_handle_get_property;
interface_vtable.set_property = lpmd_dbus_handle_set_property;
watcher_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
"org.freedesktop.intel_lpmd",
G_BUS_NAME_OWNER_FLAGS_REPLACE,
lpmd_dbus_on_bus_acquired,
lpmd_dbus_on_name_acquired,
lpmd_dbus_on_name_lost,
g_object_ref(value_obj),
NULL);
return LPMD_SUCCESS;
}
#pragma GCC diagnostic pop
#else
int intel_dbus_server_init(gboolean (*exit_handler)(void))
{
DBusGConnection *bus;
DBusGProxy *bus_proxy;
GError *error = NULL;
guint result;
PrefObject *value_obj;
intel_lpmd_dbus_exit_callback = exit_handler;
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (error != NULL) {
lpmd_log_error ("Couldn't connect to session bus: %s:\n", error->message);
return LPMD_FATAL_ERROR;
}
// Get a bus proxy instance
bus_proxy = dbus_g_proxy_new_for_name (bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
if (bus_proxy == NULL) {
lpmd_log_error ("Failed to get a proxy for D-Bus:\n");
return LPMD_FATAL_ERROR;
}
lpmd_log_debug ("Registering the well-known name (%s)\n", INTEL_LPMD_SERVICE_NAME);
// register the well-known name
if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error, G_TYPE_STRING,
INTEL_LPMD_SERVICE_NAME, G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT,
&result, G_TYPE_INVALID)) {
lpmd_log_error ("D-Bus.RequestName RPC failed: %s\n", error->message);
return LPMD_FATAL_ERROR;
}
lpmd_log_debug ("RequestName returned %d.\n", result);
if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
lpmd_log_error ("Failed to get the primary well-known name:\n");
return LPMD_FATAL_ERROR;
}
value_obj = (PrefObject*) g_object_new (PREF_TYPE_OBJECT, NULL);
if (value_obj == NULL) {
lpmd_log_error ("Failed to create one Value instance:\n");
return LPMD_FATAL_ERROR;
}
lpmd_log_debug ("Registering it on the D-Bus.\n");
dbus_g_connection_register_g_object (bus, INTEL_LPMD_SERVICE_OBJECT_PATH, G_OBJECT (value_obj));
return LPMD_SUCCESS;
}
#endif
intel-lpmd-0.0.3/src/lpmd_helpers.c 0000664 0000000 0000000 00000012762 14561114601 0017177 0 ustar 00root root 0000000 0000000 /*
* lpmd_helper.c: helper functions
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include "lpmd.h"
static int _write_str(const char *name, char *str, int print_level, int log_level, const char *mode)
{
FILE *filep;
char prefix[16];
int i, ret;
if (print_level >= 15)
return 1;
if (print_level <= 0) {
prefix[0] = '\0';
}
else {
for (i = 0; i < print_level; i++)
prefix[i] = '\t';
prefix[i] = '\0';
}
filep = fopen (name, mode);
if (!filep) {
lpmd_log_error ("%sOpen %s failed\n", prefix, name);
return 1;
}
ret = fprintf (filep, "%s", str);
if (ret <= 0) {
lpmd_log_error ("%sWrite \"%s\" to %s failed, strlen %zu, ret %d\n", prefix, str, name,
strlen (str), ret);
fclose (filep);
return 1;
}
switch (print_level) {
case LPMD_LOG_INFO:
lpmd_log_info ("%sWrite \"%s\" to %s\n", prefix, str, name);
break;
case LPMD_LOG_DEBUG:
lpmd_log_debug ("%sWrite \"%s\" to %s\n", prefix, str, name);
break;
case LPMD_LOG_MSG:
lpmd_log_msg ("%sWrite \"%s\" to %s\n", prefix, str, name);
break;
default:
break;
}
fclose (filep);
return 0;
}
int lpmd_write_str(const char *name, char *str, int print_level)
{
if (!name || !str)
return 0;
return _write_str (name, str, print_level, 2, "r+");
}
int lpmd_write_str_append(const char *name, char *str, int print_level)
{
if (!name || !str)
return 0;
return _write_str (name, str, print_level, 2, "a+");
}
int lpmd_write_str_verbose(const char *name, char *str, int print_level)
{
if (!name || !str)
return 0;
return _write_str (name, str, print_level, 3, "r+");
}
int lpmd_write_int(const char *name, int val, int print_level)
{
FILE *filep;
char prefix[16];
int i, ret;
struct timespec tp1 = { }, tp2 = { };
if (!name)
return 1;
clock_gettime (CLOCK_MONOTONIC, &tp1);
if (print_level >= 15)
return 1;
if (print_level < 0) {
prefix[0] = '\0';
}
else {
for (i = 0; i < print_level; i++)
prefix[i] = '\t';
prefix[i] = '\0';
}
filep = fopen (name, "r+");
if (!filep) {
lpmd_log_error ("%sOpen %s failed\n", prefix, name);
return 1;
}
ret = fprintf (filep, "%d", val);
if (ret <= 0) {
lpmd_log_error ("%sWrite \"%d\" to %s failed, ret %d\n", prefix, val, name, ret);
fclose (filep);
return 1;
}
clock_gettime (CLOCK_MONOTONIC, &tp2);
switch (print_level) {
case LPMD_LOG_INFO:
lpmd_log_info ("%sWrite \"%d\" to %s (%lu ns)\n", prefix, val, name,
1000000000 * (tp2.tv_sec - tp1.tv_sec) + tp2.tv_nsec - tp1.tv_nsec);
break;
case LPMD_LOG_DEBUG:
lpmd_log_debug ("%sWrite \"%d\" to %s (%lu ns)\n", prefix, val, name,
1000000000 * (tp2.tv_sec - tp1.tv_sec) + tp2.tv_nsec - tp1.tv_nsec);
break;
case LPMD_LOG_MSG:
lpmd_log_msg ("%sWrite \"%d\" to %s (%lu ns)\n", prefix, val, name,
1000000000 * (tp2.tv_sec - tp1.tv_sec) + tp2.tv_nsec - tp1.tv_nsec);
break;
default:
break;
}
fclose (filep);
return 0;
}
int lpmd_read_int(const char *name, int *val, int print_level)
{
FILE *filep;
char prefix[16];
int i, t, ret;
if (!name || !val)
return 1;
if (print_level >= 15)
return 1;
if (print_level < 0) {
prefix[0] = '\0';
}
else {
for (i = 0; i < print_level; i++)
prefix[i] = '\t';
prefix[i] = '\0';
}
filep = fopen (name, "r");
if (!filep) {
lpmd_log_error ("%sOpen %s failed\n", prefix, name);
return 1;
}
ret = fscanf (filep, "%d", &t);
if (ret != 1) {
lpmd_log_error ("%sRead %s failed, ret %d\n", prefix, name, ret);
fclose (filep);
return 1;
}
fclose (filep);
*val = t;
if (print_level >= 0)
lpmd_log_debug ("%sRead \"%d\" from %s\n", prefix, *val, name);
return 0;
}
/*
* lpmd_open does not require print on success
* print_level: -1: don't print on error
*/
int lpmd_open(const char *name, int print_level)
{
FILE *filep;
char prefix[16];
int i;
if (!name)
return 1;
if (print_level >= 15)
return 1;
if (print_level < 0) {
prefix[0] = '\0';
}
else {
for (i = 0; i < print_level; i++)
prefix[i] = '\t';
prefix[i] = '\0';
}
filep = fopen (name, "r");
if (!filep) {
if (print_level >= 0)
lpmd_log_error ("%sOpen %s failed\n", prefix, name);
return 1;
}
fclose (filep);
return 0;
}
char* get_time(void)
{
static time_t time_cur;
time_cur = time (NULL);
return ctime (&time_cur);
}
static struct timespec timespec;
static char time_buf[MAX_STR_LENGTH];
void time_start(void)
{
clock_gettime (CLOCK_MONOTONIC, ×pec);
}
char* time_delta(void)
{
static struct timespec tp1;
clock_gettime (CLOCK_MONOTONIC, &tp1);
snprintf (time_buf, MAX_STR_LENGTH, "%ld ns",
1000000000 * (tp1.tv_sec - timespec.tv_sec) + tp1.tv_nsec - timespec.tv_nsec);
memset (×pec, 0, sizeof(timespec));
return time_buf;
}
intel-lpmd-0.0.3/src/lpmd_hfi.c 0000664 0000000 0000000 00000021051 14561114601 0016272 0 ustar 00root root 0000000 0000000 /*
* lpmd_hfi.c: intel_lpmd HFI monitor
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This file processes HFI messages from the firmware. When the EE column for
* a CPU is 255, that CPU will be in allowed list to run all thread.
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "thermal.h"
#include "lpmd.h"
struct hfi_event_data {
struct nl_sock *nl_handle;
struct nl_cb *nl_cb;
};
struct hfi_event_data drv;
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *err = arg;
*err = 0;
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_SKIP;
}
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
{
int *ret = arg;
*ret = err->error;
return NL_SKIP;
}
static int seq_check_handler(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static int send_and_recv_msgs(struct hfi_event_data *drv, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg*, void*), void *valid_data)
{
struct nl_cb *cb;
int err = -ENOMEM;
cb = nl_cb_clone (drv->nl_cb);
if (!cb)
goto out;
err = nl_send_auto_complete (drv->nl_handle, msg);
if (err < 0)
goto out;
err = 1;
nl_cb_err (cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set (cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
nl_cb_set (cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
if (valid_handler)
nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data);
while (err > 0)
err = nl_recvmsgs (drv->nl_handle, cb);
out: nl_cb_put (cb);
nlmsg_free (msg);
return err;
}
struct family_data {
const char *group;
int id;
};
static int family_handler(struct nl_msg *msg, void *arg)
{
struct family_data *res = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg));
struct nlattr *mcgrp;
int i;
nla_parse (tb, CTRL_ATTR_MAX, genlmsg_attrdata (gnlh, 0), genlmsg_attrlen (gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return NL_SKIP;
nla_for_each_nested (mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i)
{
struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse (tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data (mcgrp), nla_len (mcgrp), NULL);
if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || !tb2[CTRL_ATTR_MCAST_GRP_ID]
|| strncmp (nla_data (tb2[CTRL_ATTR_MCAST_GRP_NAME]), res->group,
nla_len (tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
continue;
res->id = nla_get_u32 (tb2[CTRL_ATTR_MCAST_GRP_ID]);
break;
};
return 0;
}
static int nl_get_multicast_id(struct hfi_event_data *drv, const char *family, const char *group)
{
struct nl_msg *msg;
int ret = -1;
struct family_data res = { group, -ENOENT };
msg = nlmsg_alloc ();
if (!msg)
return -ENOMEM;
genlmsg_put (msg, 0, 0, genl_ctrl_resolve (drv->nl_handle, "nlctrl"), 0, 0, CTRL_CMD_GETFAMILY,
0);
NLA_PUT_STRING (msg, CTRL_ATTR_FAMILY_NAME, family);
ret = send_and_recv_msgs (drv, msg, family_handler, &res);
msg = NULL;
if (ret == 0)
ret = res.id;
nla_put_failure: nlmsg_free (msg);
return ret;
}
/* Process HFI event */
struct perf_cap {
int cpu;
int perf;
int eff;
};
static int suv_bit_set(void)
{
// Depends on kernel patch to export kernel knobs for this
return 0;
}
static void update_one_cpu(struct perf_cap *perf_cap)
{
if (perf_cap->cpu < 0)
return;
if (perf_cap->eff == 255 * 4 && has_hfi_lpm_monitor ())
add_cpu (perf_cap->cpu, CPUMASK_HFI);
if (!perf_cap->perf && !perf_cap->eff && has_hfi_suv_monitor () && suv_bit_set ())
add_cpu (perf_cap->cpu, CPUMASK_HFI_SUV);
}
static void process_one_event(int first, int last, int nr)
{
static int in_lpm = 0;
if (nr < 16 || last >= get_max_online_cpu () - 1) {
if (has_cpus (CPUMASK_HFI)) {
if (in_lpm) {
lpmd_log_debug ("\tRedundant HFI LPM event ignored\n\n");
}
else {
lpmd_log_debug ("\tHFI LPM hints detected\n");
process_lpm (HFI_ENTER);
in_lpm = 1;
}
reset_cpus (CPUMASK_HFI);
}
else if (has_cpus (CPUMASK_HFI_SUV)) {
if (in_hfi_suv_mode ()) {
lpmd_log_debug ("\tRedundant HFI SUV event ignored\n\n");
}
else {
lpmd_log_debug ("\tHFI SUV hints detected\n");
process_suv_mode (HFI_SUV_ENTER);
}
reset_cpus (CPUMASK_HFI_SUV);
}
else if (in_lpm) {
lpmd_log_debug ("\tHFI LPM recover\n");
// Don't override the DETECT_LPM_CPU_DEFAULT so it is auto recovered
process_lpm (HFI_EXIT);
in_lpm = 0;
}
else if (in_hfi_suv_mode ()) {
lpmd_log_debug ("\tHFI SUV recover\n");
// Don't override the DETECT_LPM_CPU_DEFAULT so it is auto recovered
process_suv_mode (HFI_SUV_EXIT);
}
else {
lpmd_log_info ("\t\t\tUnsupported HFI event ignored\n");
}
}
}
static int handle_event(struct nl_msg *n, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr (n);
struct genlmsghdr *genlhdr = genlmsg_hdr (nlh);
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
int first_cpu = -1, last_cpu = -1, nr_cpus = 0;
struct perf_cap perf_cap;
int ret;
ret = genlmsg_parse (nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
if (ret)
return -1;
perf_cap.cpu = perf_cap.perf = perf_cap.eff = -1;
if (genlhdr->cmd == THERMAL_GENL_EVENT_CAPACITY_CHANGE) {
struct nlattr *cap;
int j, index = 0, offset = 0;
char buf[MAX_STR_LENGTH];
nla_for_each_nested (cap, attrs[THERMAL_GENL_ATTR_CAPACITY], j)
{
switch (index) {
case 0:
offset += snprintf (buf + offset, MAX_STR_LENGTH - offset, "\tCPU %3d: ",
nla_get_u32 (cap));
perf_cap.cpu = nla_get_u32 (cap);
break;
case 1:
offset += snprintf (buf + offset, MAX_STR_LENGTH - offset, " PERF %4d: ",
nla_get_u32 (cap));
perf_cap.perf = nla_get_u32 (cap);
break;
case 2:
offset += snprintf (buf + offset, MAX_STR_LENGTH - offset, " PERF %4d ",
nla_get_u32 (cap));
perf_cap.eff = nla_get_u32 (cap);
break;
default:
break;
}
index++;
if (index == 3) {
index = 0;
offset = 0;
buf[MAX_STR_LENGTH - 1] = '\0';
lpmd_log_debug ("\t\t\t%s\n", buf);
update_one_cpu (&perf_cap);
if (first_cpu == -1)
first_cpu = perf_cap.cpu;
last_cpu = perf_cap.cpu;
nr_cpus++;
}
}
}
process_one_event (first_cpu, last_cpu, nr_cpus);
return 0;
}
static int done = 0;
int hfi_kill(void)
{
nl_socket_free (drv.nl_handle);
done = 1;
return 0;
}
void hfi_receive(void)
{
int err = 0;
while (!err)
err = nl_recvmsgs (drv.nl_handle, drv.nl_cb);
}
int hfi_init(void)
{
struct nl_sock *sock;
struct nl_cb *cb;
int mcast_id;
signal (SIGPIPE, SIG_IGN);
sock = nl_socket_alloc ();
if (!sock) {
lpmd_log_error ("nl_socket_alloc failed\n");
goto err_proc;
}
if (genl_connect (sock)) {
lpmd_log_error ("genl_connect(sk_event) failed\n");
goto err_proc;
}
drv.nl_handle = sock;
drv.nl_cb = cb = nl_cb_alloc (NL_CB_DEFAULT);
if (drv.nl_cb == NULL) {
lpmd_log_error ("Failed to allocate netlink callbacks");
goto err_proc;
}
mcast_id = nl_get_multicast_id (&drv, THERMAL_GENL_FAMILY_NAME,
THERMAL_GENL_EVENT_GROUP_NAME);
if (mcast_id < 0) {
lpmd_log_error ("nl_get_multicast_id failed\n");
goto err_proc;
}
if (nl_socket_add_membership (sock, mcast_id)) {
lpmd_log_error ("nl_socket_add_membership failed");
goto err_proc;
}
nl_cb_set (cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_handler, &done);
nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, handle_event, NULL);
nl_socket_set_nonblocking (sock);
if (drv.nl_handle)
return nl_socket_get_fd (drv.nl_handle);
err_proc: return -1;
}
intel-lpmd-0.0.3/src/lpmd_irq.c 0000664 0000000 0000000 00000016257 14561114601 0016333 0 ustar 00root root 0000000 0000000 /*
* irq.c: irq related processing
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lpmd.h"
static char irq_socket_name[64];
#define MAX_IRQS 128
struct info_irq {
int irq;
char affinity[MAX_STR_LENGTH];
};
struct info_irqs {
/* Cached IRQ smp_affinity info */
int nr_irqs;
struct info_irq irq[MAX_IRQS];
};
struct info_irqs info_irqs;
struct info_irqs *info = &info_irqs;
static int dump_smp_affinity(void)
{
FILE *filep;
DIR *dir;
struct dirent *d;
char path[MAX_STR_LENGTH * 2];
char str[MAX_STR_LENGTH];
size_t ret;
if (!in_debug_mode())
return 0;
dir = opendir("/proc/irq");
if (dir) {
while ((d = readdir(dir)) != NULL) {
snprintf(path, MAX_STR_LENGTH * 2, "/proc/irq/%s/smp_affinity", d->d_name);
filep = fopen(path, "r+");
if (!filep)
continue;
str[0] = '\0';
ret = fread(str, 1, MAX_STR_LENGTH, filep);
if (ret)
lpmd_log_debug("%s:%s", path, str);
fclose(filep);
}
closedir(dir);
}
return 0;
}
/* Interrupt Management */
#define SOCKET_PATH "irqbalance"
#define SOCKET_TMPFS "/run/irqbalance"
static int irqbalance_pid = -1;
static int irqbalance_ban_cpus(int enter)
{
char socket_cmd[MAX_STR_LENGTH];
struct timespec tp1, tp2;
int cpu;
int offset;
int first = 1;
dump_smp_affinity();
if (enter)
lpmd_log_info ("\tUpdate IRQ affinity (irqbalance)\n");
else
lpmd_log_info ("\tRestore IRQ affinity (irqbalance)\n");
offset = snprintf (socket_cmd, sizeof("settings cpus "), "settings cpus ");
if (!enter) {
offset += snprintf (socket_cmd + offset, sizeof("NULL"), "NULL");
goto end;
}
for (cpu = 0; cpu < get_max_cpus (); cpu++) {
if (!is_cpu_online (cpu))
continue;
if (!is_cpu_for_lpm (cpu)) {
if (MAX_STR_LENGTH <= offset) {
lpmd_log_error ("Too many CPUs for socket message\n");
return -1;
}
if (first)
offset += snprintf (socket_cmd + offset, MAX_STR_LENGTH - offset, "%d", cpu);
else
offset += snprintf (socket_cmd + offset, MAX_STR_LENGTH - offset, ",%d", cpu);
first = 0;
}
}
end: socket_cmd[offset] = '\0';
clock_gettime (CLOCK_MONOTONIC, &tp1);
socket_send_cmd (irq_socket_name, socket_cmd);
clock_gettime (CLOCK_MONOTONIC, &tp2);
lpmd_log_info ("\tSend socket command %s (%lu ns)\n", socket_cmd,
1000000000UL * (tp2.tv_sec - tp1.tv_sec) + tp2.tv_nsec - tp1.tv_nsec);
return 0;
}
static int native_restore_irqs(void)
{
char path[MAX_STR_LENGTH];
int i;
lpmd_log_info ("\tRestore IRQ affinity (native)\n");
for (i = 0; i < info->nr_irqs; i++) {
char *str = info->irq[i].affinity;
snprintf (path, MAX_STR_LENGTH, "/proc/irq/%i/smp_affinity", info->irq[i].irq);
lpmd_write_str (path, str, LPMD_LOG_DEBUG);
}
memset (info, 0, sizeof(*info));
return 0;
}
static int update_one_irq(int irq)
{
FILE *filep;
size_t size = 0;
char path[MAX_STR_LENGTH];
char *str = NULL;
if (info->nr_irqs >= (MAX_IRQS - 1)) {
lpmd_log_error ("Too many IRQs\n");
return -1;
}
info->irq[info->nr_irqs].irq = irq;
snprintf (path, MAX_STR_LENGTH, "/proc/irq/%i/smp_affinity", irq);
filep = fopen (path, "r");
if (!filep)
return -1;
if (getline (&str, &size, filep) <= 0) {
lpmd_log_error ("Failed to get IRQ%d smp_affinity\n", irq);
free (str);
fclose (filep);
return -1;
}
fclose (filep);
snprintf (info->irq[info->nr_irqs].affinity, MAX_STR_LENGTH, "%s", str);
free (str);
/* Remove the Newline */
size = strnlen (info->irq[info->nr_irqs].affinity, MAX_STR_LENGTH);
info->irq[info->nr_irqs].affinity[size - 1] = '\0';
if (lpmd_write_str (path, get_lpm_cpus_hexstr (), LPMD_LOG_DEBUG))
return 1;
info->nr_irqs++;
return 0;
}
static int native_update_irqs(void)
{
FILE *filep;
char *line = NULL;
size_t size = 0;
lpmd_log_info ("\tUpdate IRQ affinity (native)\n");
filep = fopen ("/proc/interrupts", "r");
if (!filep) {
perror ("Error open /proc/interrupts\n");
return -1;
}
/* first line is the header we don't need; nuke it */
if (getline (&line, &size, filep) <= 0) {
perror ("Error getline\n");
free (line);
fclose (filep);
return -1;
}
free (line);
while (!feof (filep)) {
int number;
char *c;
line = NULL;
size = 0;
if (getline (&line, &size, filep) <= 0) {
free (line);
break;
}
/* lines with letters in front are special, like NMI count. Ignore */
c = line;
while (isblank(*(c)))
c++;
if (!isdigit(*c)) {
free (line);
break;
}
c = strchr (line, ':');
if (!c) {
free (line);
continue;
}
*c = 0;
number = strtoul (line, NULL, 10);
update_one_irq (number);
free (line);
}
fclose (filep);
return 0;
}
static int native_process_irqs(int enter)
{
dump_smp_affinity();
if (enter)
return native_update_irqs ();
else
return native_restore_irqs ();
}
int process_irqs(int enter, enum lpm_cpu_process_mode mode)
{
/* No need to handle IRQs in offline mode */
if (mode == LPM_CPU_OFFLINE)
return 0;
lpmd_log_info ("Process IRQs ...\n");
if (irqbalance_pid == -1)
return native_process_irqs (enter);
else
return irqbalance_ban_cpus (enter);
}
int init_irq(void)
{
DIR *dir;
int socket_fd;
int ret;
lpmd_log_info ("Detecting IRQs ...\n");
dir = opendir ("/run/irqbalance");
if (dir) {
struct dirent *entry;
do {
entry = readdir (dir);
if (entry) {
if (!strncmp (entry->d_name, "irqbalance", 10)) {
ret = sscanf (entry->d_name, "irqbalance%d.sock", &irqbalance_pid);
if (!ret)
irqbalance_pid = -1;
}
}
}
while ((entry) && (irqbalance_pid == -1));
closedir (dir);
}
if (irqbalance_pid == -1) {
lpmd_log_info ("\tirqbalance not running, run in native mode\n");
return LPMD_SUCCESS;
}
snprintf (irq_socket_name, 64, "%s/%s%d.sock", SOCKET_TMPFS, SOCKET_PATH, irqbalance_pid);
socket_fd = socket_init_connection (irq_socket_name);
if (socket_fd < 0) {
lpmd_log_error ("Can not connect to irqbalance socket /run/irqbalance/irqbalance%d.sock\n",
irqbalance_pid);
return LPMD_ERROR;
}
close (socket_fd);
lpmd_log_info ("\tFind irqbalance socket %s\n", irq_socket_name);
return LPMD_SUCCESS;
}
intel-lpmd-0.0.3/src/lpmd_main.c 0000664 0000000 0000000 00000020654 14561114601 0016460 0 ustar 00root root 0000000 0000000 /*
* lpmd_main.c: Intel Low Power Daemon main entry point
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This source file contains main() function, which parses command line
* option. Call lpmd init function. Provide logging support.
* Also allow to daemonize.
*/
#include
#include
#include
#include
#include "lpmd.h"
#if !defined(INTEL_LPMD_DIST_VERSION)
#define INTEL_LPMD_DIST_VERSION PACKAGE_VERSION
#endif
#define EXIT_UNSUPPORTED 2
extern int intel_lpmd_dbus_server_init(gboolean (*exit_handler)(void));
// Lock file
static int lock_file_handle = -1;
static const char *lock_file = TDRUNDIR "/intel_lpmd.pid";
// Default log level
static int lpmd_log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
| G_LOG_LEVEL_MESSAGE;
int in_debug_mode(void)
{
return !!(lpmd_log_level & G_LOG_LEVEL_DEBUG);
}
// Daemonize or not
static gboolean intel_lpmd_daemonize;
static gboolean use_syslog;
// Disable dbus
static gboolean dbus_enable;
static GMainLoop *g_main_loop;
#ifdef GDBUS
gint watcher_id = 0;
#endif
// g_log handler. All logs will be directed here
static void intel_lpmd_logger(const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
if (!(lpmd_log_level & log_level))
return;
int syslog_priority;
const char *prefix;
time_t seconds;
switch (log_level) {
case G_LOG_LEVEL_ERROR:
prefix = "[CRIT]";
syslog_priority = LOG_CRIT;
break;
case G_LOG_LEVEL_CRITICAL:
prefix = "[ERR]";
syslog_priority = LOG_ERR;
break;
case G_LOG_LEVEL_WARNING:
prefix = "[WARN]";
syslog_priority = LOG_WARNING;
break;
case G_LOG_LEVEL_MESSAGE:
prefix = "[MSG]";
syslog_priority = LOG_NOTICE;
break;
case G_LOG_LEVEL_DEBUG:
prefix = "[DEBUG]";
syslog_priority = LOG_DEBUG;
break;
case G_LOG_LEVEL_INFO:
default:
prefix = "[INFO]";
syslog_priority = LOG_INFO;
break;
}
seconds = time (NULL);
if (use_syslog)
syslog (syslog_priority, "%s", message);
else
g_print ("[%lld]%s%s", (long long) seconds, prefix, message);
}
static void clean_up_lockfile(void)
{
if (lock_file_handle != -1) {
(void) close (lock_file_handle);
(void) unlink (lock_file);
}
}
static bool check_intel_lpmd_running(void)
{
lock_file_handle = open (lock_file, O_RDWR | O_CREAT, 0600);
if (lock_file_handle == -1) {
// Couldn't open lock file
lpmd_log_error ("Could not open PID lock file %s, exiting\n", lock_file);
return false;
}
// Try to lock file
if (lockf (lock_file_handle, F_TLOCK, 0) == -1) {
// Couldn't get lock on lock file
lpmd_log_error ("Couldn't get lock file %d\n", getpid ());
close (lock_file_handle);
return true;
}
return false;
}
// SIGTERM & SIGINT handler
static gboolean sig_int_handler(void)
{
// Call terminate function
lpmd_terminate ();
sleep (1);
if (g_main_loop)
g_main_loop_quit (g_main_loop);
// Clean up if any
clean_up_lockfile ();
exit (EXIT_SUCCESS);
return FALSE;
}
int main(int argc, char *argv[])
{
gboolean show_version = FALSE;
gboolean log_info = FALSE;
gboolean log_debug = FALSE;
gboolean no_daemon = FALSE;
gboolean systemd = FALSE;
gboolean success;
GOptionContext *opt_ctx;
int ret;
intel_lpmd_daemonize = TRUE;
use_syslog = TRUE;
dbus_enable = FALSE;
GOptionEntry options[] =
{ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_ ("Print intel_lpmd version and exit"), NULL },
{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_ ("Don't become a daemon: Default is daemon mode"), NULL },
{ "systemd", 0, 0, G_OPTION_ARG_NONE, &systemd, N_ ("Assume daemon is started by systemd, always run in non-daemon mode when using this parameter"), NULL },
{ "loglevel=info", 0, 0, G_OPTION_ARG_NONE, &log_info, N_ ("Log severity: info level and up"), NULL },
{ "loglevel=debug", 0, 0, G_OPTION_ARG_NONE, &log_debug, N_ ("Log severity: debug level and up: Max logging"), NULL },
{ "dbus-enable", 0, 0, G_OPTION_ARG_NONE, &dbus_enable, N_ ( "Enable Dbus"), NULL },
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } };
if (!g_module_supported ()) {
fprintf (stderr, "GModules are not supported on your platform!\n");
exit (EXIT_FAILURE);
}
// Set locale to be able to use environment variables
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, TDLOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
// Parse options
opt_ctx = g_option_context_new (NULL);
g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
g_option_context_set_help_enabled (opt_ctx, TRUE);
g_option_context_add_main_entries (opt_ctx, options, NULL);
g_option_context_set_summary (opt_ctx,
"Intel Low Power Daemon based on system usage takes action "
"to reduce active power of the system.\n\n"
"Copyright (c) 2023, Intel Corporation\n"
"This program comes with ABSOLUTELY NO WARRANTY.\n"
"This work is licensed under GPL v2.\n\n"
"Use \"man intel_lpmd\" to get more details.");
success = g_option_context_parse (opt_ctx, &argc, &argv, NULL);
g_option_context_free (opt_ctx);
if (!success) {
fprintf (stderr, "Invalid option. Please use --help to see a list of valid options.\n");
exit (EXIT_FAILURE);
}
if (show_version) {
fprintf (stdout, INTEL_LPMD_DIST_VERSION "\n");
exit (EXIT_SUCCESS);
}
if (getuid () != 0) {
fprintf (stderr, "You must be root to run intel_lpmd!\n");
exit (EXIT_FAILURE);
}
if (g_mkdir_with_parents (TDRUNDIR, 0755) != 0) {
fprintf (stderr, "Cannot create '%s': %s", TDRUNDIR, strerror (errno));
exit (EXIT_FAILURE);
}
if (g_mkdir_with_parents (TDCONFDIR, 0755) != 0) {
fprintf (stderr, "Cannot create '%s': %s", TDCONFDIR, strerror (errno));
exit (EXIT_FAILURE);
}
if (log_info) {
lpmd_log_level |= G_LOG_LEVEL_INFO;
}
if (log_debug) {
lpmd_log_level |= G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG;
}
openlog ("intel_lpmd", LOG_PID, LOG_USER | LOG_DAEMON | LOG_SYSLOG);
// Don't care return val
intel_lpmd_daemonize = !no_daemon && !systemd;
use_syslog = !no_daemon || systemd;
g_log_set_handler (NULL, G_LOG_LEVEL_MASK, intel_lpmd_logger, NULL);
if (check_intel_lpmd_running ()) {
lpmd_log_error ("An instance of intel_lpmd is already running, exiting ...\n");
exit (EXIT_FAILURE);
}
if (!intel_lpmd_daemonize) {
g_unix_signal_add (SIGINT, G_SOURCE_FUNC (sig_int_handler), NULL);
g_unix_signal_add (SIGTERM, G_SOURCE_FUNC (sig_int_handler), NULL);
}
/*
* Initialize the GType/GObject system
* Since GLib 2.36, the type system is initialised automatically and this function
* does nothing. Deprecated since: 2.36
*/
g_type_init ();
// Create a main loop that will dispatch callbacks
g_main_loop = g_main_loop_new (NULL, FALSE);
if (g_main_loop == NULL) {
clean_up_lockfile ();
lpmd_log_error ("Couldn't create GMainLoop:\n");
return LPMD_FATAL_ERROR;
}
if (intel_lpmd_daemonize) {
printf ("Ready to serve requests: Daemonizing..\n");
lpmd_log_info ("intel_lpmd ver %s: Ready to serve requests: Daemonizing..\n",
INTEL_LPMD_DIST_VERSION);
if (daemon (0, 0) != 0) {
clean_up_lockfile ();
lpmd_log_error ("Failed to daemonize.\n");
return LPMD_FATAL_ERROR;
}
}
if (dbus_enable)
intel_dbus_server_init (sig_int_handler);
ret = lpmd_main ();
if (ret != LPMD_SUCCESS) {
clean_up_lockfile ();
closelog ();
if (ret == LPMD_ERROR)
exit (EXIT_UNSUPPORTED);
else
exit (EXIT_FAILURE);
}
// Start service requests on the D-Bus
lpmd_log_debug ("Start main loop\n");
g_main_loop_run (g_main_loop);
lpmd_log_warn ("Oops g main loop exit..\n");
#ifdef GDBUS
g_bus_unwatch_name (watcher_id);
#endif
fprintf (stdout, "Exiting ..\n");
clean_up_lockfile ();
closelog ();
}
intel-lpmd-0.0.3/src/lpmd_proc.c 0000664 0000000 0000000 00000037510 14561114601 0016476 0 ustar 00root root 0000000 0000000 /*
* lpmd_proc.c: Intel Low Power Daemon core processing
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This file contains the main LPMD thread and poll loop. Call correct
* processing function on receiving user or system command.
*/
#include "lpmd.h"
static lpmd_config_t lpmd_config;
char *lpm_cmd_str[LPM_CMD_MAX] = {
[USER_ENTER] = "usr enter",
[USER_EXIT] = "usr exit",
[USER_AUTO] = "usr auto",
[HFI_ENTER] = "hfi enter",
[HFI_EXIT] = "hfi exit",
[UTIL_ENTER] = "utl enter",
[UTIL_EXIT] = "utl exit",
};
static int in_low_power_mode = 0;
static pthread_mutex_t lpmd_mutex;
int lpmd_lock(void)
{
return pthread_mutex_lock (&lpmd_mutex);
}
int lpmd_unlock(void)
{
return pthread_mutex_unlock (&lpmd_mutex);
}
/*
* It may take a relatively long time to enter/exit low power mode.
* Hold lpmd_lock to make sure there is no state change ongoing.
*/
int in_lpm(void)
{
int ret;
lpmd_lock ();
ret = in_low_power_mode;
lpmd_unlock ();
return !!ret;
}
/* Can be configurable */
int get_idle_percentage(void)
{
return 90;
}
/* Can be configurable */
int get_idle_duration(void)
{
return -1;
}
int get_cpu_mode(void)
{
return lpmd_config.mode;
}
int has_hfi_lpm_monitor(void)
{
return !!lpmd_config.hfi_lpm_enable;
}
int has_hfi_suv_monitor(void)
{
return !!lpmd_config.hfi_suv_enable;
}
int has_util_monitor(void)
{
return !!lpmd_config.util_enable;
}
int get_util_entry_interval(void)
{
return lpmd_config.util_entry_delay;
}
int get_util_exit_interval(void)
{
return lpmd_config.util_exit_delay;
}
int get_util_entry_threshold(void)
{
return lpmd_config.util_entry_threshold;
}
int get_util_exit_threshold(void)
{
return lpmd_config.util_exit_threshold;
}
int get_util_entry_hyst(void)
{
return lpmd_config.util_entry_hyst;
}
int get_util_exit_hyst(void)
{
return lpmd_config.util_exit_hyst;
}
/* ITMT Management */
#define PATH_ITMT_CONTROL "/proc/sys/kernel/sched_itmt_enabled"
static int process_itmt(int enter)
{
if (lpmd_config.ignore_itmt)
return 0;
lpmd_log_info ("Process ITMT ...\n");
return lpmd_write_int (PATH_ITMT_CONTROL, enter > 0 ? 0 : 1, LPMD_LOG_INFO);
}
/* Main functions */
enum lpm_state {
LPM_USER_ON = 1 << 0,
LPM_USER_OFF = 1 << 1,
LPM_SUV_ON = 1 << 2,
LPM_HFI_ON = 1 << 3,
LPM_UTIL_ON = 1 << 4,
};
/* Force off by default */
int lpm_state = LPM_USER_OFF;
/*
* 1: request valid and already satisfied. 0: respond valid and need to continue to process. -1: request invalid
*/
static int lpm_can_process(enum lpm_command cmd)
{
switch (cmd) {
case USER_ENTER:
lpm_state &= ~LPM_USER_OFF;
lpm_state |= LPM_USER_ON;
/* Set the flag but do not proceed when in SUV mode */
if (lpm_state & LPM_SUV_ON)
return 0;
return 1;
case USER_EXIT:
lpm_state &= ~LPM_USER_ON;
lpm_state |= LPM_USER_OFF;
/* Set the flag but do not proceed when in SUV mode */
if (lpm_state & LPM_SUV_ON)
return 0;
return 1;
case USER_AUTO:
lpm_state &= ~LPM_USER_ON;
lpm_state &= ~LPM_USER_OFF;
/* Do nothing but just clear the flag */
return 0;
case HFI_ENTER:
if (lpm_state & LPM_USER_OFF)
return 0;
/* Ignore HFI LPM hints when in SUV mode */
if (lpm_state & LPM_SUV_ON)
return 0;
lpm_state |= LPM_HFI_ON;
return 1;
case HFI_EXIT:
lpm_state &= ~LPM_HFI_ON;
if (lpm_state & LPM_USER_ON)
return 0;
/* Do not proceed when in SUV mode */
if (lpm_state & LPM_SUV_ON)
return 0;
return 1;
case UTIL_ENTER:
if (lpm_state & (LPM_USER_OFF))
return 0;
/* Do not proceed when in SUV mode */
if (lpm_state & LPM_SUV_ON)
return 0;
return 1;
case UTIL_EXIT:
if (lpm_state & LPM_USER_ON)
return 0;
/* Do not proceed when in SUV mode */
if (lpm_state & LPM_SUV_ON)
return 0;
/* Trust HFI LPM hints over utilization monitor */
if (lpm_state & LPM_HFI_ON)
return 0;
return 1;
/* Quit LPM because of SUV mode */
case HFI_SUV_ENTER:
lpm_state &= ~LPM_HFI_ON; /* HFI SUV hints means LPM hints is invalid */
/* Fallthrough */
case DBUS_SUV_ENTER:
lpm_state |= LPM_SUV_ON;
return 1;
/* Re-enter LPM when quitting SUV mode */
case HFI_SUV_EXIT:
case DBUS_SUV_EXIT:
lpm_state &= ~LPM_SUV_ON;
/* Re-enter LPM because it is forced by user */
if (lpm_state & LPM_USER_ON)
return 1;
/* Do oppoturnistic LPM based on util/hfi requests */
return 0;
default:
return 1;
}
}
static int dry_run = 0;
/* Must be invoked with lpmd_lock held */
int enter_lpm(enum lpm_command cmd)
{
lpmd_log_debug ("Request %d (%10s). lpm_state 0x%x\n", cmd, lpm_cmd_str[cmd], lpm_state);
if (!lpm_can_process (cmd)) {
lpmd_log_debug ("Request stopped. lpm_state 0x%x\n", lpm_state);
return 1;
}
if (in_low_power_mode) {
lpmd_log_debug ("Request skipped because the system is already in Low Power Mode ---\n");
return 0;
}
time_start ();
switch (cmd) {
case USER_ENTER:
case UTIL_ENTER:
set_lpm_cpus (CPUMASK_LPM_DEFAULT);
break;
case HFI_ENTER:
set_lpm_cpus (CPUMASK_HFI);
break;
default:
lpmd_log_info ("Unsupported LPM reason %d\n", cmd);
return 1;
}
if (!has_lpm_cpus ()) {
lpmd_log_error ("No LPM CPUs available\n");
return 1;
}
lpmd_log_msg ("------ Enter Low Power Mode (%10s) --- %s", lpm_cmd_str[cmd], get_time ());
if (dry_run) {
lpmd_log_debug ("----- Dry Run -----\n");
goto end;
}
process_itmt (1);
process_irqs (1, get_cpu_mode ());
process_cpus (1, get_cpu_mode ());
end:
lpmd_log_info ("----- Done (%s) ---\n", time_delta ());
in_low_power_mode = 1;
return 0;
}
/* Must be invoked with lpmd_lock held */
int exit_lpm(enum lpm_command cmd)
{
lpmd_log_debug ("Request %d (%10s). lpm_state 0x%x\n", cmd, lpm_cmd_str[cmd], lpm_state);
if (!lpm_can_process (cmd)) {
lpmd_log_debug ("Request stopped. lpm_state 0x%x\n", lpm_state);
return 1;
}
if (!in_low_power_mode) {
lpmd_log_debug (
"Request skipped because the system is already out of Low Power Mode ---\n");
return 0;
}
time_start ();
lpmd_log_msg ("------ Exit Low Power Mode (%10s) --- %s", lpm_cmd_str[cmd], get_time ());
if (dry_run) {
lpmd_log_debug ("----- Dry Run -----\n");
goto end;
}
process_cpus (0, get_cpu_mode ());
process_irqs (0, get_cpu_mode ());
process_itmt (0);
end:
lpmd_log_info ("----- Done (%s) ---\n", time_delta ());
in_low_power_mode = 0;
return 0;
}
static int lpmd_freezed = 0;
/* should be invoked without lock held */
int process_lpm_unlock(enum lpm_command cmd)
{
int ret;
if (lpmd_freezed) {
lpmd_log_error("lpmd freezed, command (%s) ignored\n", lpm_cmd_str[cmd]);
return 0;
}
switch (cmd) {
case USER_ENTER:
case HFI_ENTER:
case UTIL_ENTER:
case USER_AUTO:
case HFI_SUV_EXIT:
case DBUS_SUV_EXIT:
ret = enter_lpm (cmd);
break;
case USER_EXIT:
case HFI_EXIT:
case UTIL_EXIT:
case HFI_SUV_ENTER:
case DBUS_SUV_ENTER:
ret = exit_lpm (cmd);
break;
default:
ret = -1;
break;
}
return ret;
}
int process_lpm(enum lpm_command cmd)
{
int ret;
lpmd_lock ();
ret = process_lpm_unlock (cmd);
lpmd_unlock ();
return ret;
}
static int saved_lpm_state = -1;
int freeze_lpm(void)
{
lpmd_lock ();
if (lpmd_freezed)
goto end;
if (saved_lpm_state < 0)
saved_lpm_state = lpm_state & (LPM_USER_ON | LPM_USER_OFF);
process_lpm_unlock (USER_EXIT);
/* Set lpmd_freezed later to allow process_lpm () */
lpmd_freezed = 1;
end:
lpmd_unlock ();
return 0;
}
int restore_lpm(void)
{
lpmd_lock ();
if (!lpmd_freezed)
goto end;
if (saved_lpm_state >= 0) {
lpm_state = saved_lpm_state;
saved_lpm_state = -1;
}
/* Clear lpmd_freezed to allow process_lpm () */
lpmd_freezed = 0;
/* Restore previous USER_* cmd */
if (lpm_state & LPM_USER_ON) {
process_lpm_unlock (USER_ENTER);
goto end;
}
if (lpm_state & LPM_USER_OFF) {
process_lpm_unlock (USER_EXIT);
goto end;
}
process_lpm_unlock (USER_AUTO);
end:
lpmd_unlock ();
return 0;
}
static int
proc_message(message_capsul_t *msg);
static int write_pipe_fd;
static void lpmd_send_message(message_name_t msg_id, int size, unsigned char *msg)
{
message_capsul_t msg_cap;
int result;
memset (&msg_cap, 0, sizeof(message_capsul_t));
msg_cap.msg_id = msg_id;
msg_cap.msg_size = (size > MAX_MSG_SIZE) ? MAX_MSG_SIZE : size;
if (msg)
memcpy (msg_cap.msg, msg, msg_cap.msg_size);
result = write (write_pipe_fd, &msg_cap, sizeof(message_capsul_t));
if (result < 0)
lpmd_log_warn ("Write to pipe failed \n");
}
void lpmd_terminate(void)
{
lpmd_send_message (TERMINATE, 0, NULL);
sleep (1);
}
void lpmd_force_on(void)
{
lpmd_send_message (LPM_FORCE_ON, 0, NULL);
}
void lpmd_force_off(void)
{
lpmd_send_message (LPM_FORCE_OFF, 0, NULL);
}
void lpmd_set_auto(void)
{
lpmd_send_message (LPM_AUTO, 0, NULL);
}
void lpmd_suv_enter(void)
{
lpmd_send_message (SUV_MODE_ENTER, 0, NULL);
}
void lpmd_suv_exit(void)
{
lpmd_send_message (SUV_MODE_EXIT, 0, NULL);
}
void lpmd_notify_hfi_event(void)
{
lpmd_send_message (HFI_EVENT, 0, NULL);
sleep (1);
}
#define LPMD_NUM_OF_POLL_FDS 4
static pthread_t lpmd_core_main;
static pthread_attr_t lpmd_attr;
static struct pollfd poll_fds[LPMD_NUM_OF_POLL_FDS];
static int poll_fd_cnt;
static int idx_pipe_fd = -1;
static int idx_uevent_fd = -1;
static int idx_hfi_fd = -1;
#include
static GDBusProxy *power_profiles_daemon;
static void power_profiles_changed_cb(void)
{
g_autoptr (GVariant)
active_profile_v = NULL;
active_profile_v = g_dbus_proxy_get_cached_property (power_profiles_daemon, "ActiveProfile");
if (active_profile_v && g_variant_is_of_type (active_profile_v, G_VARIANT_TYPE_STRING)) {
const char *active_profile = g_variant_get_string (active_profile_v, NULL);
lpmd_log_debug ("power_profiles_changed_cb: %s\n", active_profile);
if (strcmp (active_profile, "power-saver") == 0)
lpmd_send_message (lpmd_config.powersaver_def, 0, NULL);
else if (strcmp (active_profile, "performance") == 0)
lpmd_send_message (lpmd_config.performance_def, 0, NULL);
else if (strcmp (active_profile, "balanced") == 0)
lpmd_send_message (lpmd_config.balanced_def, 0, NULL);
else
lpmd_log_warn("Ignore unsupported power profile: %s\n", active_profile);
}
}
static void connect_to_power_profile_daemon(void)
{
g_autoptr (GDBusConnection)
bus = NULL;
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
if (bus) {
power_profiles_daemon = g_dbus_proxy_new_sync (bus, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
"net.hadess.PowerProfiles",
"/net/hadess/PowerProfiles",
"net.hadess.PowerProfiles",
NULL,
NULL);
if (power_profiles_daemon) {
g_signal_connect_swapped (power_profiles_daemon, "g-properties-changed",
(GCallback) power_profiles_changed_cb,
NULL);
power_profiles_changed_cb ();
}
else {
lpmd_log_info ("Could not setup DBus watch for power-profiles-daemon");
}
}
}
/* Poll time out default */
#define POLL_TIMEOUT_DEFAULT_SECONDS 1
static bool main_loop_terminate;
// called from LPMD main thread to process user and system messages
static int proc_message(message_capsul_t *msg)
{
int ret = 0;
lpmd_log_debug ("Received message %d\n", msg->msg_id);
switch (msg->msg_id) {
case TERMINATE:
lpmd_log_msg ("Terminating ...\n");
ret = -1;
main_loop_terminate = true;
hfi_kill ();
process_lpm (USER_EXIT);
break;
case LPM_FORCE_ON:
// Always stay in LPM mode
process_lpm (USER_ENTER);
break;
case LPM_FORCE_OFF:
// Never enter LPM mode
process_lpm (USER_EXIT);
break;
case LPM_AUTO:
// Enable oppotunistic LPM
process_lpm (USER_AUTO);
break;
case SUV_MODE_ENTER:
// Call function to enter SUV mode
process_suv_mode (DBUS_SUV_ENTER);
break;
case SUV_MODE_EXIT:
// Call function to exit SUV mode
process_suv_mode (DBUS_SUV_EXIT);
break;
case HFI_EVENT:
// Call the HFI callback from here
break;
default:
break;
}
return ret;
}
// LPMD processing thread. This is callback to pthread lpmd_core_main
static void* lpmd_core_main_loop(void *arg)
{
int interval, n;
static int first_try = 1;
for (;;) {
if (main_loop_terminate)
break;
if (first_try) {
interval = 100;
first_try = 0;
} else {
// Opportunistic LPM is disabled in below cases
if (lpm_state & (LPM_USER_ON | LPM_USER_OFF | LPM_SUV_ON))
interval = -1;
else
interval = periodic_util_update ();
}
n = poll (poll_fds, poll_fd_cnt, interval);
if (n < 0) {
lpmd_log_warn ("Write to pipe failed \n");
continue;
}
if (idx_pipe_fd >= 0 && (poll_fds[idx_pipe_fd].revents & POLLIN)) {
// process message written on pipe here
message_capsul_t msg;
int result = read (poll_fds[idx_pipe_fd].fd, &msg, sizeof(message_capsul_t));
if (result < 0) {
lpmd_log_warn ("read on wakeup fd failed\n");
poll_fds[idx_pipe_fd].revents = 0;
continue;
}
if (proc_message (&msg) < 0) {
lpmd_log_debug ("Terminating thread..\n");
}
}
if (idx_uevent_fd >= 0 && (poll_fds[idx_uevent_fd].revents & POLLIN)) {
check_cpu_hotplug ();
}
if (idx_hfi_fd >= 0 && (poll_fds[idx_hfi_fd].revents & POLLIN)) {
hfi_receive ();
}
}
return NULL;
}
int lpmd_main(void)
{
int wake_fds[2];
int ret;
lpmd_log_debug ("lpmd_main begin\n");
// Call all lpmd related functions here
ret = lpmd_get_config (&lpmd_config);
if (ret)
return ret;
pthread_mutex_init (&lpmd_mutex, NULL);
ret = init_cpu (lpmd_config.lp_mode_cpus, lpmd_config.mode);
if (ret)
return ret;
if (!has_suv_support () && lpmd_config.hfi_suv_enable)
lpmd_config.hfi_suv_enable = 0;
ret = init_irq ();
if (ret)
return ret;
// Pipe is used for communication between two processes
ret = pipe (wake_fds);
if (ret) {
lpmd_log_error ("pipe creation failed %d:\n", ret);
return LPMD_FATAL_ERROR;
}
if (fcntl (wake_fds[0], F_SETFL, O_NONBLOCK) < 0) {
lpmd_log_error ("Cannot set non-blocking on pipe: %s\n", strerror (errno));
return LPMD_FATAL_ERROR;
}
if (fcntl (wake_fds[1], F_SETFL, O_NONBLOCK) < 0) {
lpmd_log_error ("Cannot set non-blocking on pipe: %s\n", strerror (errno));
return LPMD_FATAL_ERROR;
}
write_pipe_fd = wake_fds[1];
memset (poll_fds, 0, sizeof(poll_fds));
idx_pipe_fd = poll_fd_cnt;
poll_fds[idx_pipe_fd].fd = wake_fds[0];
poll_fds[idx_pipe_fd].events = POLLIN;
poll_fds[idx_pipe_fd].revents = 0;
poll_fd_cnt++;
poll_fds[poll_fd_cnt].fd = uevent_init ();
if (poll_fds[poll_fd_cnt].fd > 0) {
idx_uevent_fd = poll_fd_cnt;
poll_fds[idx_uevent_fd].events = POLLIN;
poll_fds[idx_uevent_fd].revents = 0;
poll_fd_cnt++;
}
if (lpmd_config.hfi_lpm_enable || lpmd_config.hfi_suv_enable) {
poll_fds[poll_fd_cnt].fd = hfi_init ();
if (poll_fds[poll_fd_cnt].fd > 0) {
idx_hfi_fd = poll_fd_cnt;
poll_fds[idx_hfi_fd].events = POLLIN;
poll_fds[idx_hfi_fd].revents = 0;
poll_fd_cnt++;
}
}
pthread_attr_init (&lpmd_attr);
pthread_attr_setdetachstate (&lpmd_attr, PTHREAD_CREATE_DETACHED);
/*
* lpmd_core_main_loop: is the thread where all LPMD actions take place.
* All other thread send message via pipe to trigger processing
*/
ret = pthread_create (&lpmd_core_main, &lpmd_attr, lpmd_core_main_loop, NULL);
if (ret)
return LPMD_FATAL_ERROR;
connect_to_power_profile_daemon ();
lpmd_log_debug ("lpmd_init succeeds\n");
return LPMD_SUCCESS;
}
intel-lpmd-0.0.3/src/lpmd_socket.c 0000664 0000000 0000000 00000007406 14561114601 0017024 0 ustar 00root root 0000000 0000000 /*
* lpmd_socket.c: Intel Low Power Daemon socket helpers
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This file is used to send messages to IRQ daemon via sockets.
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lpmd.h"
/* socket helpers */
int socket_init_connection(char *name)
{
struct sockaddr_un addr;
static int socket_fd;
if (!name)
return 0;
memset (&addr, 0, sizeof(struct sockaddr_un));
socket_fd = socket (AF_LOCAL, SOCK_STREAM, 0);
if (socket_fd < 0) {
perror ("Error opening socket");
return 0;
}
addr.sun_family = AF_UNIX;
snprintf (addr.sun_path, sizeof(addr.sun_path), "%s", name);
if (connect (socket_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
/* Try connect to abstract */
memset (&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
if (connect (socket_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
close (socket_fd);
return 0;
}
}
return socket_fd;
}
static struct msghdr* create_credentials_msg()
{
struct ucred *credentials;
struct msghdr *msg;
struct cmsghdr *cmsg;
credentials = malloc (sizeof(struct ucred));
if (!credentials)
return NULL;
credentials->pid = getpid ();
credentials->uid = geteuid ();
credentials->gid = getegid ();
msg = malloc (sizeof(struct msghdr));
if (!msg) {
free (credentials);
return msg;
}
memset (msg, 0, sizeof(struct msghdr));
msg->msg_iovlen = 1;
msg->msg_control = malloc (CMSG_SPACE(sizeof(struct ucred)));
if (!msg->msg_control) {
free (credentials);
free (msg);
return NULL;
}
msg->msg_controllen = CMSG_SPACE(sizeof(struct ucred));
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
memcpy (CMSG_DATA(cmsg), credentials, sizeof(struct ucred));
free (credentials);
return msg;
}
int socket_send_cmd(char *name, char *data)
{
int socket_fd;
struct msghdr *msg;
struct iovec iov;
char buf[MAX_STR_LENGTH];
int ret;
if (!name || !data)
return LPMD_ERROR;
socket_fd = socket_init_connection (name);
if (!socket_fd)
return LPMD_ERROR;
msg = create_credentials_msg ();
if (!msg)
return LPMD_ERROR;
iov.iov_base = (void*) data;
iov.iov_len = strlen (data);
msg->msg_iov = &iov;
if (sendmsg (socket_fd, msg, 0) < 0) {
free (msg->msg_control);
free (msg);
return LPMD_ERROR;
}
ret = read (socket_fd, buf, MAX_STR_LENGTH);
if (ret < 0)
lpmd_log_debug ("read failed\n");
close (socket_fd);
free (msg->msg_control);
free (msg);
return LPMD_SUCCESS;
}
intel-lpmd-0.0.3/src/lpmd_util.c 0000664 0000000 0000000 00000017473 14561114601 0016516 0 ustar 00root root 0000000 0000000 /*
* util.c: intel_lpmd utilization monitor
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This file contains logic similar to "top" program to get utilization from
* /proc/sys kernel interface.
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lpmd.h"
/* System should quit Low Power Mode when it is overloaded */
#define PATH_PROC_STAT "/proc/stat"
enum type_stat {
STAT_CPU,
STAT_USER,
STAT_NICE,
STAT_SYSTEM,
STAT_IDLE,
STAT_IOWAIT,
STAT_IRQ,
STAT_SOFTIRQ,
STAT_STEAL,
STAT_GUEST,
STAT_GUEST_NICE,
STAT_MAX,
STAT_VALID = STAT_MAX,
STAT_EXT_MAX,
};
// One for system utilization, MAX_LPM_CPUS for LPM cpu utilization
unsigned long long proc_stat_prev[MAX_LPM_CPUS + 1][STAT_EXT_MAX];
unsigned long long proc_stat_cur[MAX_LPM_CPUS + 1][STAT_EXT_MAX];
static int busy_sys = -1;
static int busy_cpu = -1;
static int calculate_busypct(unsigned long long *cur, unsigned long long *prev)
{
int idx;
unsigned long long busy = 0, total = 0;
for (idx = STAT_USER; idx < STAT_MAX; idx++) {
total += (cur[idx] - prev[idx]);
// Align with the "top" utility logic
if (idx != STAT_IDLE && idx != STAT_IOWAIT)
busy += (cur[idx] - prev[idx]);
}
if (total)
return busy * 10000 / total;
else
return 0;
}
static int parse_proc_stat(void)
{
FILE *filep;
int i;
int val;
int pos = 0;
int size = sizeof(unsigned long long) * (MAX_LPM_CPUS + 1) * STAT_MAX;
filep = fopen (PATH_PROC_STAT, "r");
if (!filep)
return 1;
memcpy (proc_stat_prev, proc_stat_cur, size);
memset (proc_stat_cur, 0, size);
while (!feof (filep)) {
int idx;
char *tmpline = NULL;
size_t size = 0;
char *line;
int cpu;
char *p;
int ret;
tmpline = NULL;
size = 0;
if (getline (&tmpline, &size, filep) <= 0) {
free (tmpline);
break;
}
line = strdup (tmpline);
p = strtok (line, " ");
if (strncmp (p, "cpu", 3)) {
free (tmpline);
free (line);
continue;
}
ret = sscanf (p, "cpu%d", &cpu);
if (ret == -1 && !(strncmp (p, "cpu", 3))) {
proc_stat_cur[pos][STAT_VALID] = 1;
proc_stat_cur[pos++][STAT_CPU] = -1;
}
else if (ret == 1) {
if (!is_cpu_for_lpm (cpu)) {
free (tmpline);
free (line);
continue;
}
// array 0 is always for system utilization
if (!pos)
pos = 1;
if (pos > MAX_LPM_CPUS) {
free (tmpline);
free (line);
break;;
}
proc_stat_cur[pos][STAT_VALID] = 1;
proc_stat_cur[pos++][STAT_CPU] = cpu;
}
else {
free (tmpline);
free (line);
continue;
}
idx = STAT_CPU;
while (p != NULL) {
if (idx >= STAT_MAX)
break;
if (idx == STAT_CPU) {
idx++;
p = strtok (NULL, " ");
continue;
}
sscanf (p, "%llu", &proc_stat_cur[pos - 1][idx]);
p = strtok (NULL, " ");
idx++;
}
free (tmpline);
free (line);
}
fclose (filep);
busy_sys = calculate_busypct (proc_stat_cur[0], proc_stat_prev[0]);
busy_cpu = 0;
for (i = 1; i < MAX_LPM_CPUS + 1; i++) {
if (!proc_stat_cur[i][STAT_VALID])
continue;
val = calculate_busypct (proc_stat_cur[i], proc_stat_prev[i]);
if (busy_cpu < val)
busy_cpu = val;
}
return 0;
}
enum system_status {
SYS_IDLE, SYS_NORMAL, SYS_OVERLOAD, SYS_UNKNOWN,
};
static enum system_status sys_stat = SYS_NORMAL;
static int first_run = 1;
static enum system_status get_sys_stat(void)
{
if (first_run)
return SYS_NORMAL;
if (!in_lpm () && busy_sys <= (get_util_entry_threshold () * 100))
return SYS_IDLE;
else if (in_lpm () && busy_cpu > (get_util_exit_threshold () * 100))
return SYS_OVERLOAD;
return SYS_NORMAL;
}
/*
* Support for hyst statistics
* Ignore the current request if:
* a. stay in current state too short
* b. average time of the target state is too low
* Note: This is not well tuned yet, set either util_in_hyst or util_out_hyst to 0
* to avoid the hyst algorithm.
*/
#define DECAY_PERIOD 5
static struct timespec tp_last_in, tp_last_out;
static unsigned long util_out_hyst, util_in_hyst;
static unsigned long util_in_min, util_out_min;
static unsigned long avg_in, avg_out;
static int util_should_proceed(enum system_status status)
{
struct timespec tp_now;
unsigned long cur_in, cur_out;
if (!util_out_hyst && !util_in_hyst)
return 1;
clock_gettime (CLOCK_MONOTONIC, &tp_now);
if (status == SYS_IDLE) {
cur_out = (tp_now.tv_sec - tp_last_out.tv_sec) * 1000000000 + tp_now.tv_nsec
- tp_last_out.tv_nsec;
// in msec
cur_out /= 1000000;
avg_out = avg_out * (DECAY_PERIOD - 1) / DECAY_PERIOD + cur_out / DECAY_PERIOD;
if (avg_in >= util_in_hyst && cur_out >= util_out_min)
return 1;
lpmd_log_info ("\t\t\tIgnore SYS_IDLE: avg_in %lu, avg_out %lu, cur_out %lu\n", avg_in,
avg_out, cur_out);
avg_in = avg_in * (DECAY_PERIOD + 1) / DECAY_PERIOD;
return 0;
}
else if (status == SYS_OVERLOAD) {
cur_in = (tp_now.tv_sec - tp_last_in.tv_sec) * 1000000000 + tp_now.tv_nsec
- tp_last_in.tv_nsec;
cur_in /= 1000000;
avg_in = avg_in * (DECAY_PERIOD - 1) / DECAY_PERIOD + cur_in / DECAY_PERIOD;
if (avg_out >= util_out_hyst && cur_in >= util_in_min)
return 1;
lpmd_log_info ("\t\t\tIgnore SYS_OVERLOAD: avg_in %lu, avg_out %lu, cur_in %lu\n", avg_in,
avg_out, cur_in);
avg_out = avg_out * (DECAY_PERIOD + 1) / DECAY_PERIOD;
return 0;
}
return 0;
}
static int get_util_interval(void)
{
int interval;
if (in_lpm ()) {
interval = get_util_exit_interval ();
if (interval || busy_cpu < 0)
return interval;
if (first_run)
return 1000;
interval = 1000 * (10000 - busy_cpu) / 10000;
}
else {
interval = get_util_entry_interval ();
if (interval)
return interval;
interval = 1000;
}
interval = (interval / 100) * 100;
if (!interval)
interval = 100;
return interval;
}
int periodic_util_update(void)
{
int interval;
static int initialized;
// poll() timeout should be -1 when util monitor not enabled
if (!has_util_monitor ())
return -1;
if (!initialized) {
clock_gettime (CLOCK_MONOTONIC, &tp_last_in);
clock_gettime (CLOCK_MONOTONIC, &tp_last_out);
avg_in = util_in_hyst = get_util_entry_hyst ();
avg_out = util_out_hyst = get_util_exit_hyst ();
util_in_min = util_in_hyst / 2;
util_out_min = util_out_hyst / 2;
initialized = 1;
}
parse_proc_stat ();
sys_stat = get_sys_stat ();
interval = get_util_interval ();
lpmd_log_info (
"\t\tSYS util %3d.%02d (Entry threshold : %3d ),"
" CPU util %3d.%02d ( Exit threshold : %3d ), resample after"
" %4d ms\n", busy_sys / 100, busy_sys % 100, get_util_entry_threshold (),
busy_cpu / 100, busy_cpu % 100, get_util_exit_threshold (), interval);
first_run = 0;
if (!util_should_proceed (sys_stat))
return interval;
switch (sys_stat) {
case SYS_IDLE:
process_lpm (UTIL_ENTER);
first_run = 1;
clock_gettime (CLOCK_MONOTONIC, &tp_last_in);
interval = 1000;
break;
case SYS_OVERLOAD:
process_lpm (UTIL_EXIT);
first_run = 1;
clock_gettime (CLOCK_MONOTONIC, &tp_last_out);
break;
default:
break;
}
return interval;
}
intel-lpmd-0.0.3/src/thermal.h 0000664 0000000 0000000 00000007764 14561114601 0016170 0 ustar 00root root 0000000 0000000 /*
* thermal.c: thermal netlink event header
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _UAPI_LINUX_THERMAL_H
#define _UAPI_LINUX_THERMAL_H
#define THERMAL_NAME_LENGTH 20
enum thermal_device_mode {
THERMAL_DEVICE_DISABLED = 0, THERMAL_DEVICE_ENABLED,
};
enum thermal_trip_type {
THERMAL_TRIP_ACTIVE = 0, THERMAL_TRIP_PASSIVE, THERMAL_TRIP_HOT, THERMAL_TRIP_CRITICAL,
};
/* Adding event notification support elements */
#define THERMAL_GENL_FAMILY_NAME "thermal"
#define THERMAL_GENL_VERSION 0x01
#define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling"
#define THERMAL_GENL_EVENT_GROUP_NAME "event"
/* Attributes of thermal_genl_family */
enum thermal_genl_attr {
THERMAL_GENL_ATTR_UNSPEC,
THERMAL_GENL_ATTR_TZ,
THERMAL_GENL_ATTR_TZ_ID,
THERMAL_GENL_ATTR_TZ_TEMP,
THERMAL_GENL_ATTR_TZ_TRIP,
THERMAL_GENL_ATTR_TZ_TRIP_ID,
THERMAL_GENL_ATTR_TZ_TRIP_TYPE,
THERMAL_GENL_ATTR_TZ_TRIP_TEMP,
THERMAL_GENL_ATTR_TZ_TRIP_HYST,
THERMAL_GENL_ATTR_TZ_MODE,
THERMAL_GENL_ATTR_TZ_NAME,
THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT,
THERMAL_GENL_ATTR_TZ_GOV,
THERMAL_GENL_ATTR_TZ_GOV_NAME,
THERMAL_GENL_ATTR_CDEV,
THERMAL_GENL_ATTR_CDEV_ID,
THERMAL_GENL_ATTR_CDEV_CUR_STATE,
THERMAL_GENL_ATTR_CDEV_MAX_STATE,
THERMAL_GENL_ATTR_CDEV_NAME,
THERMAL_GENL_ATTR_GOV_NAME,
THERMAL_GENL_ATTR_CAPACITY,
THERMAL_GENL_ATTR_CAPACITY_CPU_COUNT,
THERMAL_GENL_ATTR_CAPACITY_CPU_ID,
THERMAL_GENL_ATTR_CAPACITY_CPU_PERF,
THERMAL_GENL_ATTR_CAPACITY_CPU_EFF,
__THERMAL_GENL_ATTR_MAX,
};
#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1)
enum thermal_genl_sampling {
THERMAL_GENL_SAMPLING_TEMP, __THERMAL_GENL_SAMPLING_MAX,
};
#define THERMAL_GENL_SAMPLING_MAX (__THERMAL_GENL_SAMPLING_MAX - 1)
/* Events of thermal_genl_family */
enum thermal_genl_event {
THERMAL_GENL_EVENT_UNSPEC, THERMAL_GENL_EVENT_TZ_CREATE, /* Thermal zone creation */
THERMAL_GENL_EVENT_TZ_DELETE, /* Thermal zone deletion */
THERMAL_GENL_EVENT_TZ_DISABLE, /* Thermal zone disabled */
THERMAL_GENL_EVENT_TZ_ENABLE, /* Thermal zone enabled */
THERMAL_GENL_EVENT_TZ_TRIP_UP, /* Trip point crossed the way up */
THERMAL_GENL_EVENT_TZ_TRIP_DOWN, /* Trip point crossed the way down */
THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, /* Trip point changed */
THERMAL_GENL_EVENT_TZ_TRIP_ADD, /* Trip point added */
THERMAL_GENL_EVENT_TZ_TRIP_DELETE, /* Trip point deleted */
THERMAL_GENL_EVENT_CDEV_ADD, /* Cdev bound to the thermal zone */
THERMAL_GENL_EVENT_CDEV_DELETE, /* Cdev unbound */
THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */
THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */
THERMAL_GENL_EVENT_CAPACITY_CHANGE, /* CPU capacity changed */
__THERMAL_GENL_EVENT_MAX,
};
#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1)
/* Commands supported by the thermal_genl_family */
enum thermal_genl_cmd {
THERMAL_GENL_CMD_UNSPEC, THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id */
THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */
THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone temperature */
THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone governor */
THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone mode */
THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */
__THERMAL_GENL_CMD_MAX,
};
#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1)
#endif /* _UAPI_LINUX_THERMAL_H */
intel-lpmd-0.0.3/tests/ 0000775 0000000 0000000 00000000000 14561114601 0014720 5 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/tests/lpm_test_interface.sh 0000775 0000000 0000000 00000003477 14561114601 0021141 0 ustar 00root root 0000000 0000000 #!/bin/bash
if [ "$(whoami)" != "root" ]
then
echo "This script must be run as root"
exit 1
fi
opt_no=
if [ ! -z "$1" ]
then
opt_no=$1
fi
while true;
do
if [ -z "$1" ]
then
echo "****options****"
echo "0 : Allow All CPUs"
echo "1 : Terminate"
echo "2 : LPM force on"
echo "3 : LPM force off"
echo "4 : LPM auto"
echo "5 : SUV_MODE Enter"
echo "6 : SUV_MODE Exit"
echo "7 : Quit"
echo -n " Enter choice: "
read opt_no
fi
case $opt_no in
0)
echo "0 : Allow All CPUs"
echo -n " Enter Maximum CPU number"
read max_cpu
sudo systemctl set-property --runtime user.slice AllowedCPUs=0-$max_cpu
sudo systemctl set-property --runtime system.slice AllowedCPUs=0-$max_cpu
;;
1)
echo "1 : Terminate"
dbus-send --system --dest=org.freedesktop.intel_lpmd --print-reply /org/freedesktop/intel_lpmd org.freedesktop.intel_lpmd.Terminate
;;
2)
echo "2 : LPM force on"
dbus-send --system --dest=org.freedesktop.intel_lpmd --print-reply /org/freedesktop/intel_lpmd org.freedesktop.intel_lpmd.LPM_FORCE_ON
;;
3)
echo "3 : LPM force off"
dbus-send --system --dest=org.freedesktop.intel_lpmd --print-reply /org/freedesktop/intel_lpmd org.freedesktop.intel_lpmd.LPM_FORCE_OFF
;;
4)
echo "4 : LPM auto"
dbus-send --system --dest=org.freedesktop.intel_lpmd --print-reply /org/freedesktop/intel_lpmd org.freedesktop.intel_lpmd.LPM_AUTO
;;
5)
echo "5 : SUV_MODE Enter"
dbus-send --system --dest=org.freedesktop.intel_lpmd --print-reply /org/freedesktop/intel_lpmd org.freedesktop.intel_lpmd.SUV_MODE_ENTER
;;
6)
echo "6 : SUV_MODE Exit"
dbus-send --system --dest=org.freedesktop.intel_lpmd --print-reply /org/freedesktop/intel_lpmd org.freedesktop.intel_lpmd.SUV_MODE_EXIT
;;
7)
exit 0
;;
*)
echo "7 : Quit"
echo "Invalid option"
esac
[ ! -z "$1" ] && break
done
intel-lpmd-0.0.3/tools/ 0000775 0000000 0000000 00000000000 14561114601 0014716 5 ustar 00root root 0000000 0000000 intel-lpmd-0.0.3/tools/Makefile 0000775 0000000 0000000 00000000640 14561114601 0016361 0 ustar 00root root 0000000 0000000 CFLAGS_DBUS_GLIB = $(shell pkg-config --cflags --libs dbus-glib-1)
bindir ?= /usr/bin
CFLAGS ?= -g -Wall -Werror
all: intel_lpmd_control
intel_lpmd_control: intel_lpmd_control.c
gcc $< -o $@ $(CFLAGS) $(CFLAGS_DBUS_GLIB) $(LDFLAGS)
clean:
rm -f intel_lpmd_control
install: $(ALL_PROGRAMS)
install -d -m 755 $(DESTDIR)$(bindir); \
install intel_lpmd_control $(DESTDIR)$(bindir);
.PHONY: all clean
intel-lpmd-0.0.3/tools/intel_lpmd_control.c 0000664 0000000 0000000 00000004553 14561114601 0020760 0 ustar 00root root 0000000 0000000 /*
* intel_lpmd_control.c: Intel Low Power Daemon control utility
*
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* This program can be used to control modes of Low power mode daemon
*/
#include
#include
#include
#include
#define INTEL_LPMD_SERVICE_NAME "org.freedesktop.intel_lpmd"
#define INTEL_LPMD_SERVICE_OBJECT_PATH "/org/freedesktop/intel_lpmd"
#define INTEL_LPMD_SERVICE_INTERFACE "org.freedesktop.intel_lpmd"
int main(int argc, char **argv)
{
GError *error = NULL;
DBusGConnection *bus;
DBusGProxy *proxy;
char command[20];
if (geteuid () != 0) {
fprintf (stderr, "Must run as root\n");
exit (0);
}
if (argc < 2) {
fprintf (stderr, "intel_lpmd_control: missing control command\n");
fprintf (stderr, "syntax:\n");
fprintf (stderr, "intel_lpmd_control ON|OFF|AUTO\n");
exit (0);
}
if (!strncmp (argv[1], "ON", 2))
strcpy (command, "LPM_FORCE_ON");
else if (!strncmp (argv[1], "OFF", 3))
strcpy (command, "LPM_FORCE_OFF");
else if (!strncmp (argv[1], "AUTO", 4))
strcpy (command, "LPM_AUTO");
else {
fprintf (stderr, "intel_lpmd_control: Invalid command\n");
exit (0);
}
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (!bus) {
g_warning ("Unable to connect to system bus: %s", error->message);
g_error_free (error);
}
proxy = dbus_g_proxy_new_for_name (bus, INTEL_LPMD_SERVICE_NAME,
INTEL_LPMD_SERVICE_OBJECT_PATH,
INTEL_LPMD_SERVICE_INTERFACE);
if (!dbus_g_proxy_call (proxy, command, &error, G_TYPE_INVALID, G_TYPE_INVALID)) {
g_warning ("Failed to send message: %s", error->message);
g_error_free (error);
}
}