Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP5:Update
rasdaemon.35133
rasdaemon-0.6.7.18.git+7ccf12f.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File rasdaemon-0.6.7.18.git+7ccf12f.obscpio of Package rasdaemon.35133
07070100000000000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002700000000rasdaemon-0.6.7.18.git+7ccf12f/.github07070100000001000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/.github/workflows07070100000002000081A4000000000000000000000001611B9790000001F2000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/.github/workflows/ci.ymlname: CI on: workflow_dispatch: push: pull_request: jobs: Ubuntu: name: Ubuntu runs-on: ubuntu-latest strategy: matrix: arch: [x64_64, aarch64, ppc64le] steps: - uses: actions/checkout@v2 - name: prepare run: | sudo apt-get update sudo apt-get install -y build-essential sqlite3 - name: build run: | autoreconf -vfi ./configure --enable-all make sudo make install 07070100000003000081A4000000000000000000000001611B97900000014D000000000000000000000000000000000000002A00000000rasdaemon-0.6.7.18.git+7ccf12f/.gitignore.deps/ autom4te.cache/ misc/rasdaemon.spec misc/ras-mc-ctl.service misc/rasdaemon.service Makefile Makefile.in config.h config.h.in config.h.in~ stamp-h1 aclocal.m4 config.guess config.log config.status config.sub configure depcomp install-sh libtool ltmain.sh missing rasdaemon *.o *.c~ *.h~ rasdaemon-*.tar.bz2 rasdaemon-*.src.rpm 07070100000004000081A4000000000000000000000001611B979000000254000000000000000000000000000000000000002B00000000rasdaemon-0.6.7.18.git+7ccf12f/.travis.ymllanguage: cpp compiler: gcc dist: bionic notifications: email: recipients: - mchehab@kernel.org on_success: change on_failure: always cache: directories: - $HOME/.ccache - $HOME/pbuilder-bases matrix: include: - env: TARGET_OS=bionic - compiler: clang env: TARGET_OS=bionic #powerjobs - env: TARGET_OS=bionic arch: ppc64le - compiler: clang arch: ppc64le env: TARGET_OS=bionic before_install: - sudo apt-get install -y sqlite3 install: - autoreconf -vfi - ./configure --enable-all script: - make && sudo make install after_script: - ccache -s 07070100000005000081A4000000000000000000000001611B97900000002C000000000000000000000000000000000000002700000000rasdaemon-0.6.7.18.git+7ccf12f/AUTHORSMauro Carvalho Chehab <mchehab@kernel.org> 07070100000006000081A4000000000000000000000001611B9790000040ED000000000000000000000000000000000000002700000000rasdaemon-0.6.7.18.git+7ccf12f/COPYING GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, 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 Lesser 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. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. 07070100000007000081A4000000000000000000000001611B9790000017EB000000000000000000000000000000000000002900000000rasdaemon-0.6.7.18.git+7ccf12f/ChangeLog2021-05-26 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> - Version 0.6.7 * Support for Ice Lake and Sapphire Rapids * Support for HiSilicon Kunpeng9xx * Support for Ampere * Support for memory failure events * Support for ARM processor error information * Support for decoding for new SMCA Load Store bank type * Add 8 channel decoding for SMCA systems * Improvements at the page isolation logic * New labels: A2SDi-8C-HLN4F, A2SDi-8C+-HLN4F, ASUS PRIME X570-PRO * New labels: Supermicro X10SRA-F and H8DGU * Added support to specify SYSCONFDEFDIR * RASSTATEDIR is now created at runtime * Use a linked list for non-standard error decoding interface * PCIe AER now displas PCIe dev name * Fixed a memory leak * Several fixes * Added ppc64le to travis build 2020-07-21 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> - Version 0.6.6 * Support for new AMD SMCA bank types * Add decoders for more hip08 events * Add support for memory Corrected Error predictive failure analysis * Some bugs fixed 2019-11-20 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> - Version 0.6.5 * Several fixes for error handling logic * Alter tables on SQL in case of errors during update * store PCIe dev name and TLP header for the aer event 2019-10-10 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.6.4 * Change DB for hip08 in order to better handle some OEM data * Fix an issue of sqlite3 integer bind parameter mismatch * Update instructions about sending patches * Fix URLs to git.kernel.org repositories in README file * Fix file descriptor leak in ras-report.c:setup_report_socket() * Initialize record.cpu before pevent_print_event(). * Flush trace buffer immediately, not on next call * Replace whitespaces by tabs * Fix build with musl 2019-08-23 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.6.3 * Added support for ARM Scalable MCA * Added support for HiSilicon HIP08 * Added support for Hygon Dhyana family 18h processor * Added support for disk I/O error monitoring * Added devlink events * Integrate rasdaemon build tests with Travis CI * Fixed asdaemon high CPU usage when part of CPUs offline * Fixed mcgstatus message print * Some other minor fixes 2018-08-14 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.6.2 * Update INSTALL from the auto-tools generated one * Reorder this ChangeLog place new stuff at the beginning * add option to show error counts at ras-mc-ctl * Do some new gcc 8.1 warning cleanups * Use separate string array for PCIe AER error status * Fix PCIe AER error type 2018-04-25 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.6.1 * Update DIMM labels for 2-socket servers * Add Skylake Xeon MSCOD values * ARM: fully initialize ras_arm_event * Update my email 2017-10-14 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.6.0 * Added support for non-standard CPER error sections * Added support for Hisilicon HIP07 SAS HW module * Added support for ARM events * Updated DIMM labels for Intel Skylake servers 2016-06-08 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.5.9 * Add Knights Mill and updated DELL labels * Configure now reports enabled options 2016-04-15 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.5.8 * Add Broadwell EP/EX MSCOD and Broadwell DE MSCOD values 2016-02-05 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.5.7 * Add model numbers for Broadwell-EP/EX and -DE * Add support for Knights Landing processor 2015-07-03 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.5.6 * Add internal errors of IA32_MC4_STATUS for Haswell * Use MCA error msg as error_msg * Unnecessary comma for empty mc_location string * Remove a space from mcgstatus_msg * Add support to log Local Machine Check Exception (LMCE) 2015-06-03 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> - Version 0.5.5 * Improve INSTALL summary instructions * Add support to match the machine by system's product name * Add support for Haswell/Broadwell/Knights Landing * Some bug fixes on some MCE handlers 2014-08-15 Mauro Carvalho Chehab <m.chehab@samsung.com> - Version 0.5.4 * Fix a bug while parsing dimm labels on amd64 * Enable database recording by default on systemd service file * Correct range while parsing top, middle and lower layers 2014-08-10 Mauro Carvalho Chehab <m.chehab@samsung.com> - Version 0.5.3 * Add support for extlog trace events * Some fixes affecting sqlite handling * Handle failures of snprintf() * Fix mce numfield decoded error 2014-04-03 Mauro Carvalho Chehab <m.chehab@samsung.com> - Version 0.5.2 * Some fixes for ABRT report support 2014-03-25 Mauro Carvalho Chehab <m.chehab@samsung.com> - Version 0.5.1 * Fix patches at *.service files * Some fixes and documentation for --record option 2014-02-16 Mauro Carvalho Chehab <m.chehab@samsung.com> - Version 0.5.0 * Initial ABRT support 2013-09-10 Mauro Carvalho Chehab <m.chehab@samsung.com> - Version 0.4.2 * Fixes ras-mc-ctl layout 2013-05-29 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> - Version 0.4.1 * Some fixes, mostly at sqlite3 code * Add support at ras-mc-ctl to query database 2013-05-28 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> - Version 0.4.0 * Several fixes * Get rid of pthreads, to avoid troubles with sqllite3 (requires Kernel 3.10 or upper) * Add memory error decoding on MCE traces 2013-05-20 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> - Version 0.3.0 * Several fixes * Add support for MCE traces * Add support for PCI AER traces * Add a target to build it on rpm-based distros 2013-05-08 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> - Version 0.2.0 * Add support to log via syslog * Add ras-mc-ctl script to handle dimm labels * Add a rpm spec file * Make sqlite3 code experimental * Add manpages and systemd services * Update to take advantage of tracing features on Kernel 3.10 2013-03-12 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> - Version 0.1.0 * Initial version 07070100000008000081A4000000000000000000000001611B979000003D8C000000000000000000000000000000000000002700000000rasdaemon-0.6.7.18.git+7ccf12f/INSTALLInstallation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command './configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the 'README' file for instructions specific to this package. Some packages provide this 'INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The 'configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a 'Makefile' in each directory of the package. It may also create one or more '.h' files containing system-dependent definitions. Finally, it creates a shell script 'config.status' that you can run in the future to recreate the current configuration, and a file 'config.log' containing compiler output (useful mainly for debugging 'configure'). It can also use an optional file (typically called 'config.cache' and enabled with '--cache-file=config.cache' or simply '-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how 'configure' could check whether to do them, and mail diffs or instructions to the address given in the 'README' so they can be considered for the next release. If you are using the cache, and at some point 'config.cache' contains results you don't want to keep, you may remove or edit it. The file 'configure.ac' (or 'configure.in') is used to create 'configure' by a program called 'autoconf'. You need 'configure.ac' if you want to change it or regenerate 'configure' using a newer version of 'autoconf'. The simplest way to compile this package is: 1. 'cd' to the directory containing the package's source code and type './configure' to configure the package for your system. Running 'configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type 'make' to compile the package. 3. Optionally, type 'make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type 'make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the 'make install' phase executed with root privileges. 5. Optionally, type 'make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior 'make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing 'make clean'. To also remove the files that 'configure' created (so you can compile the package for a different kind of computer), type 'make distclean'. There is also a 'make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type 'make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide 'make distcheck', which can by used by developers to test that all other targets like 'make install' and 'make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the 'configure' script does not know about. Run './configure --help' for details on some of the pertinent environment variables. You can give 'configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU 'make'. 'cd' to the directory where you want the object files and executables to go and run the 'configure' script. 'configure' automatically checks for the source code in the directory that 'configure' is in and in '..'. This is known as a "VPATH" build. With a non-GNU 'make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use 'make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple '-arch' options to the compiler but only a single '-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the 'lipo' tool if you have problems. Installation Names ================== By default, 'make install' installs the package's commands under '/usr/local/bin', include files under '/usr/local/include', etc. You can specify an installation prefix other than '/usr/local' by giving 'configure' the option '--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option '--exec-prefix=PREFIX' to 'configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like '--bindir=DIR' to specify different values for particular kinds of files. Run 'configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of '${prefix}', so that specifying just '--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to 'configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the 'make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, 'make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of '${prefix}'. Any directories that were specified during 'configure', but not in terms of '${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the 'DESTDIR' variable. For example, 'make install DESTDIR=/alternate/directory' will prepend '/alternate/directory' before all installation names. The approach of 'DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of '${prefix}' at 'configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving 'configure' the option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. Some packages pay attention to '--enable-FEATURE' options to 'configure', where FEATURE indicates an optional part of the package. They may also pay attention to '--with-PACKAGE' options, where PACKAGE is something like 'gnu-as' or 'x' (for the X Window System). The 'README' should mention any '--enable-' and '--with-' options that the package recognizes. For packages that use the X Window System, 'configure' can usually find the X include and library files automatically, but if it doesn't, you can use the 'configure' options '--x-includes=DIR' and '--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of 'make' will be. For these packages, running './configure --enable-silent-rules' sets the default to minimal output, which can be overridden with 'make V=1'; while running './configure --disable-silent-rules' sets the default to verbose, which can be overridden with 'make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX 'make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as 'configure' are involved. Use GNU 'make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its '<wchar.h>' header file. The option '-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put '/usr/ucb' early in your 'PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in '/usr/bin'. So, if you need '/usr/ucb' in your 'PATH', put it _after_ '/usr/bin'. On Haiku, software installed for all users goes in '/boot/common', not '/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features 'configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, 'configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the '--build=TYPE' option. TYPE can either be a short name for the system type, such as 'sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file 'config.sub' for the possible values of each field. If 'config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option '--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with '--host=TYPE'. Sharing Defaults ================ If you want to set default values for 'configure' scripts to share, you can create a site shell script called 'config.site' that gives default values for variables like 'CC', 'cache_file', and 'prefix'. 'configure' looks for 'PREFIX/share/config.site' if it exists, then 'PREFIX/etc/config.site' if it exists. Or, you can set the 'CONFIG_SITE' environment variable to the location of the site script. A warning: not all 'configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to 'configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the 'configure' command line, using 'VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified 'gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 'configure' Invocation ====================== 'configure' recognizes the following options to control how it operates. '--help' '-h' Print a summary of all of the options to 'configure', and exit. '--help=short' '--help=recursive' Print a summary of the options unique to this package's 'configure', and exit. The 'short' variant lists options used only in the top level, while the 'recursive' variant lists options also present in any nested packages. '--version' '-V' Print the version of Autoconf used to generate the 'configure' script, and exit. '--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally 'config.cache'. FILE defaults to '/dev/null' to disable caching. '--config-cache' '-C' Alias for '--cache-file=config.cache'. '--quiet' '--silent' '-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to '/dev/null' (any error messages will still be shown). '--srcdir=DIR' Look for the package's source code in directory DIR. Usually 'configure' can determine that directory automatically. '--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. '--no-create' '-n' Run the configure checks, but stop before creating any output files. 'configure' also accepts some other, not widely useful, options. Run 'configure --help' for more details. 07070100000009000081A4000000000000000000000001611B979000000EE5000000000000000000000000000000000000002B00000000rasdaemon-0.6.7.18.git+7ccf12f/Makefile.amACLOCAL_AMFLAGS=-I m4 SUBDIRS = libtrace util man SYSTEMD_SERVICES_IN = misc/rasdaemon.service.in misc/ras-mc-ctl.service.in SYSTEMD_SERVICES = $(SYSTEMD_SERVICES_IN:.service.in=.service) EXTRA_DIST = $(SYSTEMD_SERVICES_IN) misc/rasdaemon.env # This rule is needed because \@sbindir\@ is expanded to \${exec_prefix\}/sbin # during ./configure phase, therefore it is not possible to add .service.in # files to AC_CONFIG_FILES in configure.ac SUFFIXES = .service.in .service .service.in.service: sed -e s,\@sbindir\@,$(sbindir),g -e s,\@SYSCONFDEFDIR\@,@SYSCONFDEFDIR@,g $< > $@ # This rule is needed because the service files must be generated on target # system after ./configure phase all-local: $(SYSTEMD_SERVICES) sbin_PROGRAMS = rasdaemon rasdaemon_SOURCES = rasdaemon.c ras-events.c ras-mc-handler.c \ bitfield.c if WITH_SQLITE3 rasdaemon_SOURCES += ras-record.c endif if WITH_AER rasdaemon_SOURCES += ras-aer-handler.c endif if WITH_NON_STANDARD rasdaemon_SOURCES += ras-non-standard-handler.c endif if WITH_ARM rasdaemon_SOURCES += ras-arm-handler.c endif if WITH_MCE rasdaemon_SOURCES += ras-mce-handler.c mce-intel.c mce-amd.c \ mce-intel-p4-p6.c mce-intel-nehalem.c \ mce-intel-dunnington.c mce-intel-tulsa.c \ mce-intel-sb.c mce-intel-ivb.c mce-intel-haswell.c \ mce-intel-knl.c mce-intel-broadwell-de.c \ mce-intel-broadwell-epex.c mce-intel-skylake-xeon.c \ mce-amd-k8.c mce-amd-smca.c mce-intel-i10nm.c endif if WITH_EXTLOG rasdaemon_SOURCES += ras-extlog-handler.c endif if WITH_DEVLINK rasdaemon_SOURCES += ras-devlink-handler.c endif if WITH_DISKERROR rasdaemon_SOURCES += ras-diskerror-handler.c endif if WITH_MEMORY_FAILURE rasdaemon_SOURCES += ras-memory-failure-handler.c endif if WITH_ABRT_REPORT rasdaemon_SOURCES += ras-report.c endif if WITH_HISI_NS_DECODE rasdaemon_SOURCES += non-standard-hisi_hip08.c non-standard-hisilicon.c endif if WITH_MEMORY_CE_PFA rasdaemon_SOURCES += rbtree.c ras-page-isolation.c endif if WITH_AMP_NS_DECODE rasdaemon_SOURCES += non-standard-ampere.c endif rasdaemon_LDADD = -lpthread $(SQLITE3_LIBS) libtrace/libtrace.a include_HEADERS = config.h ras-events.h ras-logger.h ras-mc-handler.h \ ras-aer-handler.h ras-mce-handler.h ras-record.h bitfield.h ras-report.h \ ras-extlog-handler.h ras-arm-handler.h ras-non-standard-handler.h \ ras-devlink-handler.h ras-diskerror-handler.h rbtree.h ras-page-isolation.h \ non-standard-hisilicon.h non-standard-ampere.h ras-memory-failure-handler.h # This rule can't be called with more than one Makefile job (like make -j8) # I can't figure out a way to fix that dist-rpm: dist-bzip2 if [ ! -d "`rpm --eval %{_topdir}`/SOURCES/" ]; then mkdir "`rpm --eval %{_topdir}`/SOURCES/"; fi cp @PACKAGE@-@PACKAGE_VERSION@.tar.bz2 `rpm --eval %{_topdir}`/SOURCES/ rpmbuild -ba misc/@PACKAGE@.spec cp `rpm --eval %{_topdir}`/SRPMS/@PACKAGE@-@PACKAGE_VERSION@*.src.rpm . srpm: dist-bzip2 if [ ! -d "`rpm --eval %{_topdir}`/SOURCES/" ]; then mkdir "`rpm --eval %{_topdir}`/SOURCES/"; fi cp @PACKAGE@-@PACKAGE_VERSION@.tar.bz2 `rpm --eval %{_topdir}`/SOURCES/ rpmbuild -bs misc/@PACKAGE@.spec mock: srpm mock `rpm --eval %{_topdir}`/SRPMS/@PACKAGE@-@PACKAGE_VERSION@*.src.rpm rpmlint: rpmlint misc/@PACKAGE@.spec `rpm --eval %{_topdir}`/SRPMS/@PACKAGE@-@PACKAGE_VERSION@*.src.rpm `rpm --eval %{_topdir}`/RPMS/*/@PACKAGE@-@PACKAGE_VERSION@*.rpm upload: scp `rpm --eval %{_topdir}`/SRPMS/@PACKAGE@-@PACKAGE_VERSION@*.src.rpm @PACKAGE@-@PACKAGE_VERSION@.tar.bz2 misc/rasdaemon.spec www.infradead.org:public_html/rasdaemon # custom target install-data-local: $(install_sh) -d "$(DESTDIR)@sysconfdir@/ras/dimm_labels.d" if WITH_MEMORY_CE_PFA $(install_sh) @abs_srcdir@/misc/rasdaemon.env "$(DESTDIR)@SYSCONFDEFDIR@/rasdaemon" endif 0707010000000A000081A4000000000000000000000001611B979000000B3B000000000000000000000000000000000000002400000000rasdaemon-0.6.7.18.git+7ccf12f/NEWSRAS DAEMON ========== In Kernel 3.5 we've started to address the long-discussed need of having a better way to handle platform Reliability, Availability and Serviceability (RAS). Basically, a tracepoint event that handles memory errors called ras:mc_event was added there, together with HERM/EDAC version 3.0 patches. In Kernel 3.8, a new event was added, to handle PCIe AER events (ras:aer_event) [1]. On kernel 3.9, a new driver was added to report hardware memory errors that comes from the BIOS via ras:mc_event (the new ghes_edac driver). It is still on my TODO list to add a RAS trace event for non-memory related errors that come via the MCA machine check handler (mcelog). While progress made was made at Kernel infrastructure, the needed userspace tools were still lacking. So, I decided to start materializing the userspace counterpart for what it was informally named as rasdaemon on some discussions. The rasdaemon tool is available at: http://git.infradead.org/users/mchehab/rasdaemon.git The current version is on very early stages, and it has a copy on it of the library that Steven Rostedt's is writing for trace-cmd tool. The plan is to use the trace-cmd library, when it starts to packaged as a separate library. I'd like to thanks Steven for the help he gave me to write this initial version. The current version of the tool enables the ras:mc_event log, and reads it via the raw trace debugfs node: /sys/kernel/debug/tracing/per_cpu/cpu*/trace_pipe_raw It also has a code that allows recording the errors via an sqlite3 database. The long term plan is to provide a tool that will catch and handle all ras:* error events that comes from the Kernel tracing infrastructure, logging them and providing tools to report it, being able to detect burst errors (like the ones caused by a solar storm at memories) or sparsed errors, in a way that would provide a glue to the users about the root cause of the error. Of course, there are much to do there. It is a natural evolution of the tool to add support there for the ras:aer_event traces that can come from PCIe AER. While it currently works with current Kernels since kernel 3.5, there are a number of interesting changes at tracing that are planned to be merged for Kernel 3.10: - poll() support for per_cpu trace_pipe_raw; - a timestamp that could more easily associated with machine's uptime; - support for a separate ringbuffer for RAS. So, it is planned the minimal requirement for the final version (v1.0) would be kernel 3.10. This is currently on very early staging. Help is needed ;) So, please send us suggestions, patches etc to the EDAC mailing list: linux-edac@vger.kernel.org Thanks, Mauro Carvalho Chehab 2013-03-14 - [1] Currently, ras:mc_event is at include/ras/. It is on my todo list to move it to be together with ras:aer_event, at include/trace/events/ras.h. 0707010000000B000081A4000000000000000000000001611B97900000234F000000000000000000000000000000000000002600000000rasdaemon-0.6.7.18.git+7ccf12f/READMERAS Daemon ========== Those tools provide a way to get Platform Reliability, Availability and Serviceability (RAS) reports made via the Kernel tracing events. The main repository for the rasdaemon is at Fedora hosted: http://git.infradead.org/users/mchehab/rasdaemon.git And two mirrors are available: https://github.com/mchehab/rasdaemon https://gitlab.com/mchehab_kernel/rasdaemon Tarballs for each release can be found at: http://www.infradead.org/~mchehab/rasdaemon/ GOALS ===== Its initial goal is to replace the edac-tools that got bitroted after the addition of the HERM (Hardware Events Report Method )patches[1] at the EDAC Kernel drivers. [1] http://lkml.indiana.edu/hypermail/linux/kernel/1205.1/02075.html Its long term goal is to be the userspace tool that will collect all hardware error events reported by the Linux Kernel from several sources (EDAC, MCE, PCI, ...) into one common framework. It is not meant to provide tools for doing error injection, as there are other tools already covering it, like: git://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/mce-test.git Yet, a few set of testing scripts are provided under /contrib dir. When the final version of the HERM patches was merged upstream, it was decided to not expose the memory error counters to userspace. This is one of the differences from what it was provided by edac-utils, as EDAC 2.0.0 exports errors via a set of sysfs nodes that sums the amount of errors per DIMM, per memory channel and per memory controller. However, those counters are monotonically increased, and there's no way to detect if they're very sparsed in time, if the occurrence is increasing over time, or if they're due to some burst, perhaps due to a Solar Storm hitting the ionosphere. In other words, the rationale for not exposing such the information is that: 1) can be easily accounted on userspace; 2) they're not really meaningful. E. g. one system with, let's say 10 corrected errors can be fine, while another one with the same amount of errors can have problems, as the error counters don't take into account things like system uptime, memory error bursts (that could be caused by a solar storm, for example), etc. So, the idea since them was to make the kernel-userspace interface simpler and move the policy to the userspace daemon. It is up to the userspace daemon to correlate the data about the RAS events and provide the system administrator a comprehensive report, presenting him a better hint if he needs to contact the hardware vendor to replace a component that is working degraded, or to simply discard the error. So, the approach taken here is to allow storing those errors on a SQLite database, in order to allow those data to be latter mining. It is currently not part of the scope to do sophiscicated data mininy analysis, as that would require enough statistitical data about hardware MTBF. In other words, an abnormal component that needs to be replaced shoud be statistically compared with a similar component that operates under a normal condition. To do such checks, the analysis tool would need to know the probability density function(p. d. f.) of that component, and its rellevant parameters (like mean and standard derivation, if the p. d. f. funcion is a Normal distribution). While this tool works since Kernel 3.5 (where HERM patches got added), in order to get the full benefit of this tool, Kernel 3.10 or upper is needed. COMPILING AND INSTALLING ======================== sqlite3 and autoconf needs to be installed. On Fedora, this is done by installing the following packages: make gcc autoconf automake libtool tar sqlite-devel (if sqlite3 will be used) perl-DBD-SQLite (if sqlite3 will be used) To install then on Fedora, run: yum install -y make gcc autoconf automake libtool tar perl-dbd-sqlite Or, if sqlite3 database will be used to store data: yum install -y make gcc autoconf automake libtool tar sqlite-devel There are currently 3 features that are enabled optionally, via ./configure parameters: --enable-sqlite3 enable storing data at SQL lite database (currently experimental) --enable-aer enable PCIe AER events (currently experimental) --enable-mce enable MCE events (currently experimental) In order to compile it, run: $ autoreconf -vfi $ ./configure [parameters] $ make So, for example, to enable everything but sqlite3: $ autoreconf -vfi && ./configure --enable-aer --enable-mce && make After compiling, run, as root: # make install COMPILING AND INSTALLING ======================== If the distribution is rpm-based, an alternative method would be to do: $ autoreconf -vfi && ./configure The above procedure will generate a file at misc/rasdaemon.spec. You may edit it, in order to add/remove the --enable-[option] parameters. To generate the rpm files, do: $ make dist-rpm To install the rpm files, run, as root: # rpm -i `rpm --eval %{_topdir}`/RPMS/x86_64/rasdaemon-0.*.fc18.x86_64.rpm RUNNING ======= The daemon generally requires root permission, in order to read the needed debugfs trace nodes, with needs to be previously mounted. The rasdaemon will check at /proc/mounts where the debugfs partition is mounted and use it while running. To run the rasdaemon in background, just call it without any parameters: # rasdaemon The output will be available via syslog. Or, to run it in foreground and see the logs in console, run it as: # rasdaemon -f or, if you also want to record errors at the database (--enable-sqlite3 is required): # rasdaemon -f -r You may also start it via systemd: # systemctl start rasdaemon The rasdaemon will then output the messages to journald. TESTING ======= A script is provided under /contrib, in order to test the daemon EDAC handler. While the daemon is running, just run: # contrib/edac-fake-inject The script requires a Kernel compiled with CONFIG_EDAC_DEBUG and a running EDAC driver. MCE error handling can use the MCE inject: https://git.kernel.org/pub/scm/utils/cpu/mce/mce-inject.git For it to work, Kernel mce-inject module should be compiled and loaded. APEI error injection can use this tool: https://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/mce-test.git/ AER error injection can use this tool: https://git.kernel.org/pub/scm/linux/kernel/git/gong.chen/aer-inject.git/ SUBMITTING PATCHES ================== If you want to help improving this tool, be my guest! We try to follow the Kernel's CodingStyle and submission rules as a reference. Please send patches enclosed in an email, in plain text, to: linux-edac@vger.kernel.org With a copy to: Mauro Carvalho Chehab <mchehab@kernel.org> Or, alternatively, send a pull request against github or gitlab repositories at: https://github.com/mchehab/rasdaemon https://gitlab.com/mchehab_kernel/rasdaemon (github preferred) Don't foget to add a description of the patch in the body of the email, adding a Signed-off-by: at the end of the patch description (before the unified diff with the patch). We use Signed-off-by the same way as in kernel, so I'm transcribing bellow the same text as found under Kernel's Documentation/SubmittingPatches: "To improve tracking of who did what, especially with patches that can percolate to their final resting place in the kernel through several layers of maintainers, we've introduced a "sign-off" procedure on patches that are being emailed around. The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it 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: 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 saying Signed-off-by: Random J Developer <random@developer.example.org> using your real name (sorry, no pseudonyms or anonymous contributions.)" 0707010000000C000081A4000000000000000000000001611B9790000000F3000000000000000000000000000000000000002400000000rasdaemon-0.6.7.18.git+7ccf12f/TODO1) Handle signals. 2) Better handle error conditions to be sure that events won't be lost. 3) Test support for PCIe AER trace records. 4) Better parse mce trace records. 5) Make it work fine with offline CPUs. 6) Handle CPU hotplugs. 0707010000000D000081A4000000000000000000000001611B979000000A57000000000000000000000000000000000000002A00000000rasdaemon-0.6.7.18.git+7ccf12f/bitfield.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * The code below were adapted from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" unsigned bitfield_msg(char *buf, size_t len, const char **bitarray, unsigned array_len, unsigned bit_offset, unsigned ignore_bits, uint64_t status) { int i, n; char *p = buf; len--; for (i = 0; i < array_len; i++) { if (status & ignore_bits) continue; if (status & (1 << (i + bit_offset))) { if (p != buf) { n = snprintf(p, len, ", "); if (n < 0) break; len -= n; p += n; } if (!bitarray[i]) n = snprintf(p, len, "BIT%d", i + bit_offset); else n = snprintf(p, len, "%s", bitarray[i]); if (n < 0) break; len -= n; p += n; } } *p = 0; return p - buf; } static uint64_t bitmask(uint64_t i) { uint64_t mask = 1; while (mask < i) mask = (mask << 1) | 1; return mask; } void decode_bitfield(struct mce_event *e, uint64_t status, struct field *fields) { struct field *f; for (f = fields; f->str; f++) { uint64_t v = (status >> f->start_bit) & bitmask(f->stringlen - 1); char *s = NULL; if (v < f->stringlen) s = f->str[v]; if (!s) { if (v == 0) continue; mce_snprintf(e->error_msg, "<%u:%llx>", f->start_bit, (long long)v); } else mce_snprintf(e->error_msg, "%s", s); } } void decode_numfield(struct mce_event *e, uint64_t status, struct numfield *fields) { struct numfield *f; for (f = fields; f->name; f++) { uint64_t mask = (1ULL << (f->end - f->start + 1)) - 1; uint64_t v = (status >> f->start) & mask; if (v > 0 || f->force) { char fmt[32] = {0}; snprintf(fmt, 32, "%%s: %s\n", f->fmt ? f->fmt : "%Lu"); mce_snprintf(e->error_msg, fmt, f->name, v); } } } 0707010000000E000081A4000000000000000000000001611B979000000846000000000000000000000000000000000000002A00000000rasdaemon-0.6.7.18.git+7ccf12f/bitfield.h/* * The code below came from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdint.h> /* Generic bitfield decoder */ struct field { unsigned start_bit; char **str; unsigned stringlen; }; struct numfield { unsigned start, end; char *name; char *fmt; int force; }; #define FIELD(start_bit, name) { start_bit, name, ARRAY_SIZE(name) } #define FIELD_NULL(start_bit) { start_bit, NULL, 0 } #define SBITFIELD(start_bit, string) { start_bit, ((char * [2]) { NULL, string }), 2 } #define NUMBER(start, end, name) { start, end, name, "%Lu", 0 } #define NUMBERFORCE(start, end, name) { start, end, name, "%Lu", 1 } #define HEXNUMBER(start, end, name) { start, end, name, "%Lx", 0 } #define HEXNUMBERFORCE(start, end, name) { start, end, name, "%Lx", 1 } struct mce_event; void decode_bitfield(struct mce_event *e, uint64_t status, struct field *fields); void decode_numfield(struct mce_event *e, uint64_t status, struct numfield *fields); #define MASK(x) ((1ULL << (1 + (x))) - 1) #define EXTRACT(v, a, b) (((v) >> (a)) & MASK((b)-(a))) static inline int test_prefix(int nr, uint32_t value) { return ((value >> nr) == 1); } /* Ancillary routines */ unsigned bitfield_msg(char *buf, size_t len, const char **bitarray, unsigned array_len, unsigned bit_offset, unsigned ignore_bits, uint64_t status); 0707010000000F000081A4000000000000000000000001611B979000001F56000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/configure.acAC_INIT([RASdaemon], 0.6.7) AM_SILENT_RULES([yes]) AC_CANONICAL_SYSTEM AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE AC_PROG_CC AC_PROG_INSTALL AC_PROG_LIBTOOL X_AC_META AC_CONFIG_FILES([ Makefile libtrace/Makefile man/Makefile man/ras-mc-ctl.8 man/rasdaemon.1 misc/rasdaemon.spec util/Makefile util/ras-mc-ctl ]) AC_ARG_ENABLE([all], AS_HELP_STRING([--enable-all], [enable all features])) AC_ARG_ENABLE([sqlite3], AS_HELP_STRING([--enable-sqlite3], [enable storing data at SQL lite database (currently experimental)])) AS_IF([test "x$enable_sqlite3" = "xyes" || test "x$enable_all" == "xyes"], [ AC_CHECK_LIB(sqlite3, sqlite3_open,[echo "found sqlite3"] , AC_MSG_ERROR([*** Unable to find sqlite3 library]), ) SQLITE3_LIBS="-lsqlite3" AC_DEFINE(HAVE_SQLITE3,1,"have sqlite3") AC_SUBST([WITH_SQLITE3]) ]) AM_CONDITIONAL([WITH_SQLITE3], [test x$enable_sqlite3 = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_SQLITE3], [USE_SQLITE3="yes"], [USE_SQLITE3="no"]) AC_SUBST([SQLITE3_LIBS]) AC_ARG_ENABLE([aer], AS_HELP_STRING([--enable-aer], [enable PCIe AER events (currently experimental)])) AS_IF([test "x$enable_aer" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_AER,1,"have PCIe AER events collect") AC_SUBST([WITH_AER]) ]) AM_CONDITIONAL([WITH_AER], [test x$enable_aer = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_AER], [USE_AER="yes"], [USE_AER="no"]) AC_ARG_ENABLE([non_standard], AS_HELP_STRING([--enable-non-standard], [enable NON_STANDARD events (currently experimental)])) AS_IF([test "x$enable_non_standard" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_NON_STANDARD,1,"have UNKNOWN_SEC events collect") AC_SUBST([WITH_NON_STANDARD]) ]) AM_CONDITIONAL([WITH_NON_STANDARD], [test x$enable_non_standard = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_NON_STANDARD], [USE_NON_STANDARD="yes"], [USE_NON_STANDARD="no"]) AC_ARG_ENABLE([arm], AS_HELP_STRING([--enable-arm], [enable ARM events (currently experimental)])) AS_IF([test "x$enable_arm" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_ARM,1,"have ARM events collect") AC_SUBST([WITH_ARM]) ]) AM_CONDITIONAL([WITH_ARM], [test x$enable_arm = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_ARM], [USE_ARM="yes"], [USE_ARM="no"]) AC_ARG_ENABLE([mce], AS_HELP_STRING([--enable-mce], [enable MCE events (currently experimental)])) AS_IF([test "x$enable_mce" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_MCE,1,"have PCIe MCE events collect") AC_SUBST([WITH_MCE]) ]) AM_CONDITIONAL([WITH_MCE], [test x$enable_mce = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_MCE], [USE_MCE="yes"], [USE_MCE="no"]) AC_ARG_ENABLE([extlog], AS_HELP_STRING([--enable-extlog], [enable EXTLOG events (currently experimental)])) AS_IF([test "x$enable_extlog" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_EXTLOG,1,"have EXTLOG events collect") AC_SUBST([WITH_EXTLOG]) ]) AM_CONDITIONAL([WITH_EXTLOG], [test x$enable_extlog = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_EXTLOG], [USE_EXTLOG="yes"], [USE_EXTLOG="no"]) AC_ARG_ENABLE([devlink], AS_HELP_STRING([--enable-devlink], [enable devlink health events (currently experimental)])) AS_IF([test "x$enable_devlink" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_DEVLINK,1,"have devlink health events collect") AC_SUBST([WITH_DEVLINK]) ]) AM_CONDITIONAL([WITH_DEVLINK], [test x$enable_devlink = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_DEVLINK], [USE_DEVLINK="yes"], [USE_DEVLINK="no"]) AC_ARG_ENABLE([diskerror], AS_HELP_STRING([--enable-diskerror], [enable disk I/O error events (currently experimental)])) AS_IF([test "x$enable_diskerror" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_DISKERROR,1,"have disk I/O errors collect") AC_SUBST([WITH_DISKERROR]) ]) AM_CONDITIONAL([WITH_DISKERROR], [test x$enable_diskerror = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_DISKERROR], [USE_DISKERROR="yes"], [USE_DISKERROR="no"]) AC_ARG_ENABLE([memory_failure], AS_HELP_STRING([--enable-memory-failure], [enable memory failure events (currently experimental)])) AS_IF([test "x$enable_memory_failure" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_MEMORY_FAILURE,1,"have memory failure events collect") AC_SUBST([WITH_MEMORY_FAILURE]) ]) AM_CONDITIONAL([WITH_MEMORY_FAILURE], [test x$enable_memory_failure = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_MEMORY_FAILURE], [USE_MEMORY_FAILURE="yes"], [USE_MEMORY_FAILURE="no"]) AC_ARG_ENABLE([abrt_report], AS_HELP_STRING([--enable-abrt-report], [enable report event to ABRT (currently experimental)])) AS_IF([test "x$enable_abrt_report" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_ABRT_REPORT,1,"have report event to ABRT") AC_SUBST([WITH_ABRT_REPORT]) ]) AM_CONDITIONAL([WITH_ABRT_REPORT], [test x$enable_abrt_report = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_ABRT_REPORT], [USE_ABRT_REPORT="yes"], [USE_ABRT_REPORT="no"]) AC_ARG_ENABLE([hisi_ns_decode], AS_HELP_STRING([--enable-hisi-ns-decode], [enable HISI_NS_DECODE events (currently experimental)])) AS_IF([test "x$enable_hisi_ns_decode" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_HISI_NS_DECODE,1,"have HISI UNKNOWN_SEC events decode") AC_SUBST([WITH_HISI_NS_DECODE]) ]) AM_CONDITIONAL([WITH_HISI_NS_DECODE], [test x$enable_hisi_ns_decode = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_HISI_NS_DECODE], [USE_HISI_NS_DECODE="yes"], [USE_HISI_NS_DECODE="no"]) AC_ARG_ENABLE([memory_ce_pfa], AS_HELP_STRING([--enable-memory-ce-pfa], [enable memory Corrected Error predictive failure analysis])) AS_IF([test "x$enable_memory_ce_pfa" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_MEMORY_CE_PFA,1,"have memory corrected error predictive failure analysis") AC_SUBST([WITH_MEMORY_CE_PFA]) ]) AM_CONDITIONAL([WITH_MEMORY_CE_PFA], [test x$enable_memory_ce_pfa = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_MEMORY_CE_PFA], [USE_MEMORY_CE_PFA="yes"], [USE_MEMORY_CE_PFA="no"]) AC_ARG_ENABLE([amp_ns_decode], AS_HELP_STRING([--enable-amp-ns-decode], [enable AMP_NS_DECODE events (currently experimental)])) AS_IF([test "x$enable_amp_ns_decode" = "xyes" || test "x$enable_all" == "xyes"], [ AC_DEFINE(HAVE_AMP_NS_DECODE,1,"have AMP UNKNOWN_SEC events decode") AC_SUBST([WITH_AMP_NS_DECODE]) ]) AM_CONDITIONAL([WITH_AMP_NS_DECODE], [test x$enable_amp_ns_decode = xyes || test x$enable_all == xyes]) AM_COND_IF([WITH_AMP_NS_DECODE], [USE_AMP_NS_DECODE="yes"], [USE_AMP_NS_DECODE="no"]) test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -Wstrict-prototypes" AC_SUBST([rasstatedir], [$localstatedir/lib/rasdaemon]) AC_DEFINE_DIR([RASSTATEDIR], [rasstatedir], [rasdaemon db store state dir]) AC_SUBST([RASSTATEDIR]) AC_ARG_WITH(sysconfdefdir, AC_HELP_STRING([--with-sysconfdefdir=DIR], [rasdaemon environment file dir]), [SYSCONFDEFDIR=$withval], [SYSCONFDEFDIR=/etc/sysconfig]) AC_SUBST([SYSCONFDEFDIR]) AC_DEFINE([RAS_DB_FNAME], ["ras-mc_event.db"], [ras events database]) AC_SUBST([RAS_DB_FNAME], ["ras-mc_event.db"]) AC_OUTPUT dnl --------------------------------------------------------------------- dnl compile time options summary cat <<EOF compile time options summary ============================ Sqlite3 : $USE_SQLITE3 AER : $USE_AER MCE : $USE_MCE EXTLOG : $USE_EXTLOG CPER non-standard : $USE_NON_STANDARD ABRT report : $USE_ABRT_REPORT HISI Kunpeng errors : $USE_HISI_NS_DECODE ARM events : $USE_ARM DEVLINK : $USE_DEVLINK Disk I/O errors : $USE_DISKERROR Memory Failure : $USE_MEMORY_FAILURE Memory CE PFA : $USE_MEMORY_CE_PFA AMP RAS errors : $USE_AMP_NS_DECODE EOF 07070100000010000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002700000000rasdaemon-0.6.7.18.git+7ccf12f/contrib07070100000011000081ED000000000000000000000001611B979000000551000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/contrib/edac-fake-inject#!/bin/bash MC="$(ls -d /sys/devices/system/edac/mc/mc? |sed -e s,.*/mc,,)" SYSFS="$(cat /proc/mounts|grep debugfs|cut -d' ' -f 2)" if [ ! -e $SYSFS/edac/ ]; then echo "$SYSFS/edac not found." echo " It seems that your Kernel was not compiled with CONFIG_EDAC_DEBUG."; exit -1; fi for i in $MC; do LAYER1=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d ' ' -f 1) LAYER2=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d ' ' -f 3) LAYER3=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d ' ' -f 5) DEBUGFS=$SYSFS/edac/mc$i/ MAX=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d' ' -f 2) for j in `seq 0 $MAX`; do echo $j > $DEBUGFS/fake_inject_$LAYER1 if [ "$LAYER2" == "" ]; then echo "Injecting errors at mc#$i $j" echo > $DEBUGFS/fake_inject else MAX=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d' ' -f 4) for k in `seq 0 $MAX`; do echo $k > $DEBUGFS/fake_inject_$LAYER2 if [ "$LAYER3" == "" ]; then echo "Injecting errors at mc#$i $j:$k" echo > $DEBUGFS/fake_inject else MAX=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d' ' -f 6) for l in `seq 0 $MAX`; do echo $l > $DEBUGFS/fake_inject_$LAYER3 echo "Injecting errors at mc#$i $j:$k:$l" echo > $DEBUGFS/fake_inject done fi done fi done done 07070100000012000081ED000000000000000000000001611B979000000953000000000000000000000000000000000000003200000000rasdaemon-0.6.7.18.git+7ccf12f/contrib/edac-tests#!/bin/bash FILE=new-$(hostname)-$(date +"%Y-%m-%d-%H-%M-%S").txt run() { echo "# $@" >> $FILE $@ 2>> $FILE >> $FILE if [ "$?" != "0" ]; then ERR=$? echo "Error on $0# $@" echo "Error $ERR" >> $FILE tail -f $FILE exit -1 fi } run_noerror() { echo "# $@" >> $FILE $@ 2>> $FILE >> $FILE } DRIVER=$(lsmod|grep edac_core|cut -b 33-) echo "RUNNING driver $DRIVER on kernel `uname -r`, hostname `hostname`, at `date`" >$FILE mount -t debugfs debugfs /sys/kernel/debug/ run ras-mc-ctl --layout run ras-mc-ctl --guess-labels run free -l run dmidecode run_noerror tree /sys/devices/system/edac/ run_noerror grep . /sys/devices/system/edac/mc/mc?/dimm*/* /sys/devices/system/edac/mc/mc?/rank*/* run_noerror grep . /sys/devices/system/edac/mc/mc?/csrow*/* run_noerror dmesg for i in /sys/devices/system/edac/mc/mc?/reset_counters; do echo 1 >$i done echo "ras:*" >/sys/kernel/debug/tracing/set_event run echo "Enabled events: " run cat /sys/kernel/debug/tracing/set_event MC="$(ls -d /sys/devices/system/edac/mc/mc? |sed -e s,.*/mc,,)" for i in $MC; do LAYER1=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d ' ' -f 1) LAYER2=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d ' ' -f 3) LAYER3=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d ' ' -f 5) DEBUGFS=/sys/kernel/debug/edac/mc$i/ MAX=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d' ' -f 2) for j in `seq 0 $MAX`; do MAX=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d' ' -f 4) for k in `seq 0 $MAX`; do MAX=$(cat /sys/devices/system/edac/mc/mc$i/max_location |cut -d' ' -f 6) echo $j > $DEBUGFS/fake_inject_$LAYER1 echo $k > $DEBUGFS/fake_inject_$LAYER2 if [ "$MAX" == "" ]; then echo "Injecting errors at mc#$i $j:$k" echo "Injecting errors at mc#$i $j:$k" >> $FILE echo > $DEBUGFS/fake_inject dmesg |tail -3 >> $FILE else for l in `seq 0 $MAX`; do echo "Injecting errors at mc#$i $j:$k:$l" echo "Injecting errors at mc#$i $j:$k:$l" >> $FILE echo $l > $DEBUGFS/fake_inject_$LAYER3 echo > $DEBUGFS/fake_inject dmesg |tail -3 >> $FILE done fi done done done run grep . /sys/devices/system/edac/mc/mc?/*e_* run cat /sys/kernel/debug/tracing/trace # FIXME: need to add some logic there to check if the proper error # counts are incremented, without producing a very long log 07070100000013000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002600000000rasdaemon-0.6.7.18.git+7ccf12f/labels07070100000014000081A4000000000000000000000001611B979000000225000000000000000000000000000000000000002B00000000rasdaemon-0.6.7.18.git+7ccf12f/labels/asus# RASDAEMON Motherboard DIMM labels Database file. # # Vendor-name and model-name are found from the program 'dmidecode' # labels are found from the silk screen on the motherboard. # #Vendor: <vendor-name> # Product: <product-name> # Model: <model-name> # <label>: <mc>.<top>.<mid>.<low> # # #Vendor: <vendor-name> # Model: <model-name> # <label>: <mc>.<row>.<channel> # Vendor: ASUSTeK COMPUTER INC. Model: PRIME X570-PRO DIMM_A1: 0.0.1, 0.1.1; DIMM_A2: 0.2.1, 0.3.1; DIMM_B1: 0.0.0, 0.1.0; DIMM_B2: 0.2.0, 0.3.0; 07070100000015000081A4000000000000000000000001611B979000001A85000000000000000000000000000000000000002B00000000rasdaemon-0.6.7.18.git+7ccf12f/labels/dell# RASDAEMON Motherboard DIMM labels Database file. # # Vendor-name and model-name are found from the program 'dmidecode' # labels are found from the silk screen on the motherboard. # #Vendor: <vendor-name> # Product: <product-name> # Model: <model-name> # <label>: <mc>.<top>.<mid>.<low> # Vendor: Dell Inc. # 1-socket Product: PowerEdge R220, PowerEdge R330, PowerEdge T330, PowerEdge R230, PowerEdge T130, PowerEdge T30 DIMM_A1: 0.0.0; DIMM_A2: 0.0.1; DIMM_A3: 0.1.0; DIMM_A4: 0.1.1; Product: PowerEdge T110 II, PowerEdge T20 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_B1: 0.0.1; DIMM_B2: 0.1.1; Product: PowerEdge R320, PowerEdge T320 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_A3: 0.2.0; DIMM_A4: 0.0.1; DIMM_A5: 0.1.1; DIMM_A6: 0.2.1; # 2-socket Product: PowerEdge R610 DIMM_A1: 0.0.0; DIMM_A2: 0.0.1; DIMM_A3: 0.0.2; DIMM_A4: 0.1.0; DIMM_A5: 0.1.1; DIMM_A6: 0.1.2; DIMM_B1: 1.0.0; DIMM_B2: 1.0.1; DIMM_B3: 1.0.2; DIMM_B4: 1.1.0; DIMM_B5: 1.1.1; DIMM_B6: 1.1.2; Product: PowerEdge T710, PowerEdge R710 DIMM_A3: 0.0.0; DIMM_A2: 0.1.0; DIMM_A1: 0.2.0; DIMM_A6: 0.0.1; DIMM_A5: 0.1.1; DIMM_A4: 0.2.1; DIMM_A9: 0.0.2; DIMM_A8: 0.1.2; DIMM_A7: 0.2.2; DIMM_B3: 1.0.0; DIMM_B2: 1.1.0; DIMM_B1: 1.2.0; DIMM_B6: 1.0.1; DIMM_B5: 1.1.1; DIMM_B4: 1.2.1; DIMM_B9: 1.0.2; DIMM_B8: 1.1.2; DIMM_B7: 1.2.2; Product: PowerEdge R620, PowerEdge T620, PowerEdge R720xd, PowerEdge R730xd, PowerEdge T630, PowerEdge R730, PowerEdge R630, PowerEdge T620, PowerEdge M620, PowerEdge FC620, PowerEdge M630, PowerEdge FC630 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_A3: 0.2.0; DIMM_A4: 0.3.0; DIMM_A5: 0.0.1; DIMM_A6: 0.1.1; DIMM_A7: 0.2.1; DIMM_A8: 0.3.1; DIMM_A9: 0.0.2; DIMM_A10: 0.1.2; DIMM_A11: 0.2.2; DIMM_A12: 0.3.2; DIMM_B1: 1.0.0; DIMM_B2: 1.1.0; DIMM_B3: 1.2.0; DIMM_B4: 1.3.0; DIMM_B5: 1.0.1; DIMM_B6: 1.1.1; DIMM_B7: 1.2.1; DIMM_B8: 1.3.1; DIMM_B9: 1.0.2; DIMM_B10: 1.1.2; DIMM_B11: 1.2.2; DIMM_B12: 1.3.2; Product: PowerEdge R640, PowerEdge R740, PowerEdge R740xd, PowerEdge T640 A1: 0.0.0; A2: 0.1.0; A3: 0.2.0; A4: 1.0.0; A5: 1.1.0; A6: 1.2.0; A7: 0.0.1; A8: 0.1.1; A9: 0.2.1; A10: 1.0.1; A11: 1.1.1; A12: 1.2.1; B1: 2.0.0; B2: 2.1.0; B3: 2.2.0; B4: 3.0.0; B5: 3.1.0; B6: 3.2.0; B7: 2.0.1; B8: 2.1.1; B9: 2.2.1; B10: 3.0.1; B11: 3.1.1; B12: 3.2.1; Product: PowerEdge M520, PowerEdge R420, PowerEdge T420 DIMM_A1: 0.1.0; DIMM_A2: 0.2.0; DIMM_A3: 0.3.0; DIMM_A4: 0.1.1; DIMM_A5: 0.2.1; DIMM_A6: 0.3.1; DIMM_B1: 1.1.0; DIMM_B2: 1.2.0; DIMM_B3: 1.3.0; DIMM_B4: 1.1.1; DIMM_B5: 1.2.1; DIMM_B6: 1.3.1; Product: PowerEdge FC420, PowerEdge M420 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_A3: 0.2.0; DIMM_B1: 1.0.0; DIMM_B2: 1.1.0; DIMM_B3: 1.2.0; Product: PowerEdge C6320, PowerEdge C4130 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_A3: 0.2.0; DIMM_A4: 0.3.0; DIMM_A5: 0.0.1; DIMM_A6: 0.1.1; DIMM_A7: 0.2.1; DIMM_A8: 0.3.1; DIMM_B1: 1.0.0; DIMM_B2: 1.1.0; DIMM_B3: 1.2.0; DIMM_B4: 1.3.0; DIMM_B5: 1.0.1; DIMM_B6: 1.1.1; DIMM_B7: 1.2.1; DIMM_B8: 1.3.1; Product: PowerEdge C6320p A1: 0.0.0; B1: 0.1.0; C1: 0.2.0; D1: 1.0.0; E1: 1.1.0; F1: 1.2.0; Product: PowerEdge C6420 A1: 0.0.0; A2: 0.1.0; A3: 0.2.0; A4: 1.0.0; A5: 1.1.0; A6: 1.2.0; A7: 0.0.1; A8: 1.0.1; B1: 2.0.0; B2: 2.1.0; B3: 2.2.0; B4: 3.0.0; B5: 3.1.0; B6: 3.2.0; B7: 2.0.1; B8: 3.0.1; Product: PowerEdge R430, PowerEdge T430, PowerEdge R530 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_A3: 0.2.0; DIMM_A4: 0.3.0; DIMM_A5: 0.0.1; DIMM_A6: 0.1.1; DIMM_A7: 0.2.1; DIMM_A8: 0.3.1; DIMM_B1: 1.0.0; DIMM_B2: 1.1.0; DIMM_B3: 1.2.0; DIMM_B4: 1.3.0; Product: PowerEdge FC430 DIMM_A1: 0.1.0; DIMM_A2: 0.0.0; DIMM_A3: 0.2.0; DIMM_A4: 0.3.0; DIMM_B1: 1.1.0; DIMM_B2: 1.0.0; DIMM_B3: 1.2.0; DIMM_B4: 1.3.0; # 4-socket Product: PowerEdge M820, PowerEdge R830, PowerEdge M830, PowerEdge R930, PowerEdge FC830 DIMM_A1: 0.0.0; DIMM_A2: 0.1.0; DIMM_A3: 0.2.0; DIMM_A4: 0.3.0; DIMM_A5: 0.0.1; DIMM_A6: 0.1.1; DIMM_A7: 0.2.1; DIMM_A8: 0.3.1; DIMM_A9: 0.0.2; DIMM_A10: 0.1.2; DIMM_A11: 0.2.2; DIMM_A12: 0.3.2; DIMM_B1: 1.0.0; DIMM_B2: 1.1.0; DIMM_B3: 1.2.0; DIMM_B4: 1.3.0; DIMM_B5: 1.0.1; DIMM_B6: 1.1.1; DIMM_B7: 1.2.1; DIMM_B8: 1.3.1; DIMM_B9: 1.0.2; DIMM_B10: 1.1.2; DIMM_B11: 1.2.2; DIMM_B12: 1.3.2; DIMM_C1: 2.0.0; DIMM_C2: 2.1.0; DIMM_C3: 2.2.0; DIMM_C4: 2.3.0; DIMM_C5: 2.0.1; DIMM_C6: 2.1.1; DIMM_C7: 2.2.1; DIMM_C8: 2.3.1; DIMM_C9: 2.0.2; DIMM_C10: 2.1.2; DIMM_C11: 2.2.2; DIMM_C12: 2.3.2; DIMM_D1: 3.0.0; DIMM_D2: 3.1.0; DIMM_D3: 3.2.0; DIMM_D4: 3.3.0; DIMM_D5: 3.0.1; DIMM_D6: 3.1.1; DIMM_D7: 3.2.1; DIMM_D8: 3.3.1; DIMM_D9: 3.0.2; DIMM_D10: 3.1.2; DIMM_D11: 3.2.2; DIMM_D12: 3.3.2; Product: PowerEdge FM120x4 DIMM_A_A1: 0.1.0; DIMM_A_A2: 0.2.0; DIMM_B_A1: 1.1.0; DIMM_B_A2: 1.2.0; DIMM_C_A1: 2.1.0; DIMM_C_A2: 2.2.0; DIMM_D_A1: 3.1.0; DIMM_D_A2: 3.2.0; Product: PowerEdge R940 A1: 0.0.0; A2: 0.1.0; A3: 0.2.0; A4: 1.0.0; A5: 1.1.0; A6: 1.2.0; A7: 0.0.1; A8: 0.1.1; A9: 0.2.1; A10: 1.0.1; A11: 1.1.1; A12: 1.2.1; B1: 2.0.0; B2: 2.1.0; B3: 2.2.0; B4: 3.0.0; B5: 3.1.0; B6: 3.2.0; B7: 2.0.1; B8: 2.1.1; B9: 2.2.1; B10: 3.0.1; B11: 3.1.1; B12: 3.2.1; C1: 4.0.0; C2: 4.1.0; C3: 4.2.0; C4: 5.0.0; C5: 5.1.0; C6: 5.2.0; C7: 4.0.1; C8: 4.1.1; C9: 4.2.1; C10: 5.0.1; C11: 5.1.1; C12: 5.2.1; D1: 6.0.0; D2: 6.1.0; D3: 6.2.0; D4: 7.0.0; D5: 7.1.0; D6: 7.2.0; D7: 6.0.1; D8: 6.1.1; D9: 6.2.1; D10: 7.0.1; D11: 7.1.1; D12: 7.2.1; Product: PowerEdge R440, PowerEdge R540 A1: 0.0.0; A2: 0.1.0; A3: 0.2.0; A4: 1.0.0; A5: 1.1.0; A6: 1.2.0; A7: 0.0.1; A8: 0.1.1; A9: 1.0.1; A10: 1.1.1; B1: 2.0.0; B2: 2.1.0; B3: 2.2.0; B4: 3.0.0; B5: 3.1.0; B6: 3.2.0; Product: PowerEdge M640, PowerEdge FC640 A1: 0.0.0; A2: 0.1.0; A3: 0.2.0; A4: 1.0.0; A5: 1.1.0; A6: 1.2.0; A7: 0.0.1; A8: 1.0.1; B1: 2.0.0; B2: 2.1.0; B3: 2.2.0; B4: 3.0.0; B5: 3.1.0; B6: 3.2.0; B7: 2.0.1; B8: 3.0.1; 07070100000016000081A4000000000000000000000001611B979000000C16000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/labels/supermicro# RASDAEMON Motherboard DIMM labels Database file. # # Vendor-name and model-name are found from the program 'dmidecode' # labels are found from the silk screen on the motherboard. # #Vendor: <vendor-name> # Product: <product-name> # Model: <model-name> # <label>: <mc>.<top>.<mid>.<low> # Vendor: Supermicro Model: A2SDi-8C-HLN4F, A2SDi-8C+-HLN4F DIMMA1: 0.0.0; DIMMA2: 0.0.1; DIMMB1: 0.1.0; DIMMB2: 0.1.1; Model: X10SRA-F DIMMA1: 0.0.0; DIMMA2: 0.0.1; DIMMB1: 0.1.0; DIMMB2: 0.1.1; DIMMC1: 1.0.0; DIMMC2: 1.0.1; DIMMD1: 1.1.0; DIMMD2: 1.1.1; Model: H8DGU P1_DIMM1A: 0.2.0; P1_DIMM1A: 0.3.0; P2_DIMM1A: 3.2.0; P2_DIMM1A: 3.3.0; P1_DIMM2A: 0.2.1; P1_DIMM2A: 0.3.1; P2_DIMM2A: 3.2.1; P2_DIMM2A: 3.3.1; P1_DIMM3A: 1.2.0; P1_DIMM3A: 1.3.0; P2_DIMM3A: 2.2.0; P2_DIMM3A: 2.3.0; P1_DIMM4A: 1.2.1; P1_DIMM4A: 1.3.1; P2_DIMM4A: 2.2.1; P2_DIMM4A: 2.3.1; P1_DIMM1B: 0.0.0; P1_DIMM1B: 0.2.0; P2_DIMM1B: 3.0.0; P2_DIMM1B: 3.1.0; P1_DIMM2B: 0.0.1; P1_DIMM2B: 0.1.1; P2_DIMM2B: 3.0.1; P2_DIMM2B: 3.1.1; P1_DIMM3B: 1.0.0; P1_DIMM3B: 1.1.0; P2_DIMM3B: 2.0.0; P2_DIMM3B: 2.1.0; P1_DIMM4B: 1.0.1; P1_DIMM4B: 1.1.1; P2_DIMM4B: 2.0.1; P2_DIMM4B: 2.1.1; Model: X11DPH-i, X11DPH-T, X11DPH-TQ P1-DIMMA1: 0.0.0; P1-DIMMA2: 0.0.1; P1-DIMMB1: 0.1.0; P1-DIMMC1: 0.2.0; P1-DIMMD1: 1.0.0; P1-DIMMD2: 1.0.1; P1-DIMME1: 1.1.0; P1-DIMMF1: 1.2.0; P2-DIMMA1: 2.0.0; P2-DIMMA2: 2.0.1; P2-DIMMB1: 2.1.0; P2-DIMMC1: 2.2.0; P2-DIMMD1: 3.0.0; P2-DIMMD2: 3.0.1; P2-DIMME1: 3.1.0; P2-DIMMF1: 3.2.0; Model: X10DRI, X10DRI-T P1-DIMMA1: 0.0.0; P1-DIMMA2: 0.0.1; P1-DIMMB1: 0.1.0; P1-DIMMB2: 0.1.1; P1-DIMMC1: 0.2.0; P1-DIMMC2: 0.2.1; P1-DIMMD1: 0.3.0; P1-DIMMD2: 0.3.1; P2-DIMME1: 1.0.0; P2-DIMME2: 1.0.1; P2-DIMMF1: 1.1.0; P2-DIMMF2: 1.1.1; P2-DIMMG1: 1.2.0; P2-DIMMG2: 1.2.1; P2-DIMMH1: 1.3.0; P2-DIMMH2: 1.3.1; Model: X10DRL-i P1-DIMMA1: 0.0.0; P1-DIMMB1: 0.1.0; P1-DIMMC1: 0.2.0; P1-DIMMD1: 0.3.0; P2-DIMME1: 1.0.0; P2-DIMMF1: 1.1.0; P2-DIMMG1: 1.2.0; P2-DIMMH1: 1.3.0; Model: X11DDW-NT, X11DDW-L P1-DIMMA1: 0.0.0; P1-DIMMB1: 0.1.0; P1-DIMMC1: 0.2.0; P1-DIMMD1: 1.0.0; P1-DIMME1: 1.1.0; P1-DIMMF1: 1.2.0; P2-DIMMA1: 2.0.0; P2-DIMMB1: 2.1.0; P2-DIMMC1: 2.2.0; P2-DIMMD1: 3.0.0; P2-DIMME1: 3.1.0; P2-DIMMF1: 3.2.0; Model: X11SPM-F, X11SPM-TF, X11SPM-TPF DIMMA1: 0.0.0; DIMMB1: 0.1.0; DIMMC1: 0.2.0; DIMMD1: 1.0.0; DIMME1: 1.1.0; DIMMF1: 1.2.0; Model: B1DRi P1_DIMMA1: 0.0.0; P1_DIMMB1: 0.1.0; P1_DIMMC1: 0.2.0; P1_DIMMD1: 0.3.0; P2_DIMME1: 1.0.0; P2_DIMMF1: 1.1.0; P2_DIMMG1: 1.2.0; P2_DIMMH1: 1.3.0; Model: X11SCA, X11SCA-F DIMMA1: 0.0.0, 0.1.0; DIMMA2: 0.2.0, 0.3.0; DIMMB1: 0.0.1, 0.1.1; DIMMB2: 0.2.1, 0.3.1; Model: X11SCW-F DIMMA1: 0.1.0; DIMMA2: 0.0.0; DIMMB1: 0.1.1; DIMMB2: 0.0.1;07070100000017000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002800000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace07070100000018000081A4000000000000000000000001611B979000000024000000000000000000000000000000000000003300000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/.gitignoreMakefile Makefile.in *.deps *.o *.a 07070100000019000081A4000000000000000000000001611B9790000000C3000000000000000000000000000000000000003400000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/Makefile.amnoinst_LIBRARIES = libtrace.a libtrace_a_SOURCES = event-parse.c parse-filter.c \ kbuffer-parse.c parse-utils.c trace-seq.c include_HEADERS = event-parse.h event-utils.h kbuffer.h 0707010000001A000081A4000000000000000000000001611B9790000000B2000000000000000000000000000000000000002F00000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/READMEThis library is part of the trace-cmd tools, available at: git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git Written by Steven Rostedt <srostedt@redhat.com> 0707010000001B000081A4000000000000000000000001611B97900001DAE7000000000000000000000000000000000000003600000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/event-parse.c/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * The parts for function graph printing was taken and modified from the * Linux Kernel that were written by * - Copyright (C) 2009 Frederic Weisbecker, * Frederic Weisbecker gave his permission to relicense the code to * the Lesser General Public License. */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include <errno.h> #include <stdint.h> #include <limits.h> #include "event-parse.h" #include "event-utils.h" static const char *input_buf; static unsigned long long input_buf_ptr; static unsigned long long input_buf_siz; static int is_flag_field; static int is_symbolic_field; static int show_warning = 1; #define do_warning(fmt, ...) \ do { \ if (show_warning) \ warning(fmt, ##__VA_ARGS__); \ } while (0) static void init_input_buf(const char *buf, unsigned long long size) { input_buf = buf; input_buf_siz = size; input_buf_ptr = 0; } const char *pevent_get_input_buf(void) { return input_buf; } unsigned long long pevent_get_input_buf_ptr(void) { return input_buf_ptr; } struct event_handler { struct event_handler *next; int id; const char *sys_name; const char *event_name; pevent_event_handler_func func; void *context; }; struct pevent_func_params { struct pevent_func_params *next; enum pevent_func_arg_type type; }; struct pevent_function_handler { struct pevent_function_handler *next; enum pevent_func_arg_type ret_type; char *name; pevent_func_handler func; struct pevent_func_params *params; int nr_args; }; static unsigned long long process_defined_func(struct trace_seq *s, void *data, int size, struct event_format *event, struct print_arg *arg); static void free_func_handle(struct pevent_function_handler *func); /** * pevent_buffer_init - init buffer for parsing * @buf: buffer to parse * @size: the size of the buffer * * For use with pevent_read_token(), this initializes the internal * buffer that pevent_read_token() will parse. */ void pevent_buffer_init(const char *buf, unsigned long long size) { init_input_buf(buf, size); } static struct print_arg *alloc_arg(void) { return calloc(1, sizeof(struct print_arg)); } struct cmdline { char *comm; int pid; }; static int cmdline_cmp(const void *a, const void *b) { const struct cmdline *ca = a; const struct cmdline *cb = b; if (ca->pid < cb->pid) return -1; if (ca->pid > cb->pid) return 1; return 0; } struct cmdline_list { struct cmdline_list *next; char *comm; int pid; }; static int cmdline_init(struct pevent *pevent) { struct cmdline_list *cmdlist = pevent->cmdlist; struct cmdline_list *item; struct cmdline *cmdlines; int i; cmdlines = malloc(sizeof(*cmdlines) * pevent->cmdline_count); if (!cmdlines) return -1; i = 0; while (cmdlist) { cmdlines[i].pid = cmdlist->pid; cmdlines[i].comm = cmdlist->comm; i++; item = cmdlist; cmdlist = cmdlist->next; free(item); } qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); pevent->cmdlines = cmdlines; pevent->cmdlist = NULL; return 0; } static char *find_cmdline(struct pevent *pevent, int pid) { const struct cmdline *comm; struct cmdline key; if (!pid) return "<idle>"; if (!pevent->cmdlines && cmdline_init(pevent)) return "<not enough memory for cmdlines!>"; key.pid = pid; comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, sizeof(*pevent->cmdlines), cmdline_cmp); if (comm) return comm->comm; return "<...>"; } /** * pevent_pid_is_registered - return if a pid has a cmdline registered * @pevent: handle for the pevent * @pid: The pid to check if it has a cmdline registered with. * * Returns 1 if the pid has a cmdline mapped to it * 0 otherwise. */ int pevent_pid_is_registered(struct pevent *pevent, int pid) { const struct cmdline *comm; struct cmdline key; if (!pid) return 1; if (!pevent->cmdlines && cmdline_init(pevent)) return 0; key.pid = pid; comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, sizeof(*pevent->cmdlines), cmdline_cmp); if (comm) return 1; return 0; } /* * If the command lines have been converted to an array, then * we must add this pid. This is much slower than when cmdlines * are added before the array is initialized. */ static int add_new_comm(struct pevent *pevent, const char *comm, int pid) { struct cmdline *cmdlines = pevent->cmdlines; const struct cmdline *cmdline; struct cmdline key; if (!pid) return 0; /* avoid duplicates */ key.pid = pid; cmdline = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, sizeof(*pevent->cmdlines), cmdline_cmp); if (cmdline) { errno = EEXIST; return -1; } cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1)); if (!cmdlines) { errno = ENOMEM; return -1; } cmdlines[pevent->cmdline_count].comm = strdup(comm); if (!cmdlines[pevent->cmdline_count].comm) { free(cmdlines); errno = ENOMEM; return -1; } cmdlines[pevent->cmdline_count].pid = pid; if (cmdlines[pevent->cmdline_count].comm) pevent->cmdline_count++; qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); pevent->cmdlines = cmdlines; return 0; } /** * pevent_register_comm - register a pid / comm mapping * @pevent: handle for the pevent * @comm: the command line to register * @pid: the pid to map the command line to * * This adds a mapping to search for command line names with * a given pid. The comm is duplicated. */ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) { struct cmdline_list *item; if (pevent->cmdlines) return add_new_comm(pevent, comm, pid); item = malloc(sizeof(*item)); if (!item) return -1; item->comm = strdup(comm); if (!item->comm) { free(item); return -1; } item->pid = pid; item->next = pevent->cmdlist; pevent->cmdlist = item; pevent->cmdline_count++; return 0; } struct func_map { unsigned long long addr; char *func; char *mod; }; struct func_list { struct func_list *next; unsigned long long addr; char *func; char *mod; }; static int func_cmp(const void *a, const void *b) { const struct func_map *fa = a; const struct func_map *fb = b; if (fa->addr < fb->addr) return -1; if (fa->addr > fb->addr) return 1; return 0; } /* * We are searching for a record in between, not an exact * match. */ static int func_bcmp(const void *a, const void *b) { const struct func_map *fa = a; const struct func_map *fb = b; if ((fa->addr == fb->addr) || (fa->addr > fb->addr && fa->addr < (fb+1)->addr)) return 0; if (fa->addr < fb->addr) return -1; return 1; } static int func_map_init(struct pevent *pevent) { struct func_list *funclist; struct func_list *item; struct func_map *func_map; int i; func_map = malloc(sizeof(*func_map) * (pevent->func_count + 1)); if (!func_map) return -1; funclist = pevent->funclist; i = 0; while (funclist) { func_map[i].func = funclist->func; func_map[i].addr = funclist->addr; func_map[i].mod = funclist->mod; i++; item = funclist; funclist = funclist->next; free(item); } qsort(func_map, pevent->func_count, sizeof(*func_map), func_cmp); /* * Add a special record at the end. */ func_map[pevent->func_count].func = NULL; func_map[pevent->func_count].addr = 0; func_map[pevent->func_count].mod = NULL; pevent->func_map = func_map; pevent->funclist = NULL; return 0; } static struct func_map * find_func(struct pevent *pevent, unsigned long long addr) { struct func_map *func; struct func_map key; if (!pevent->func_map) func_map_init(pevent); key.addr = addr; func = bsearch(&key, pevent->func_map, pevent->func_count, sizeof(*pevent->func_map), func_bcmp); return func; } /** * pevent_find_function - find a function by a given address * @pevent: handle for the pevent * @addr: the address to find the function with * * Returns a pointer to the function stored that has the given * address. Note, the address does not have to be exact, it * will select the function that would contain the address. */ const char *pevent_find_function(struct pevent *pevent, unsigned long long addr) { struct func_map *map; map = find_func(pevent, addr); if (!map) return NULL; return map->func; } /** * pevent_find_function_address - find a function address by a given address * @pevent: handle for the pevent * @addr: the address to find the function with * * Returns the address the function starts at. This can be used in * conjunction with pevent_find_function to print both the function * name and the function offset. */ unsigned long long pevent_find_function_address(struct pevent *pevent, unsigned long long addr) { struct func_map *map; map = find_func(pevent, addr); if (!map) return 0; return map->addr; } /** * pevent_register_function - register a function with a given address * @pevent: handle for the pevent * @function: the function name to register * @addr: the address the function starts at * @mod: the kernel module the function may be in (NULL for none) * * This registers a function name with an address and module. * The @func passed in is duplicated. */ int pevent_register_function(struct pevent *pevent, char *func, unsigned long long addr, char *mod) { struct func_list *item = malloc(sizeof(*item)); if (!item) return -1; item->next = pevent->funclist; item->func = strdup(func); if (!item->func) goto out_free; if (mod) { item->mod = strdup(mod); if (!item->mod) goto out_free_func; } else item->mod = NULL; item->addr = addr; pevent->funclist = item; pevent->func_count++; return 0; out_free_func: free(item->func); item->func = NULL; out_free: free(item); errno = ENOMEM; return -1; } /** * pevent_print_funcs - print out the stored functions * @pevent: handle for the pevent * * This prints out the stored functions. */ void pevent_print_funcs(struct pevent *pevent) { int i; if (!pevent->func_map) func_map_init(pevent); for (i = 0; i < (int)pevent->func_count; i++) { printf("%016llx %s", pevent->func_map[i].addr, pevent->func_map[i].func); if (pevent->func_map[i].mod) printf(" [%s]\n", pevent->func_map[i].mod); else printf("\n"); } } struct printk_map { unsigned long long addr; char *printk; }; struct printk_list { struct printk_list *next; unsigned long long addr; char *printk; }; static int printk_cmp(const void *a, const void *b) { const struct printk_map *pa = a; const struct printk_map *pb = b; if (pa->addr < pb->addr) return -1; if (pa->addr > pb->addr) return 1; return 0; } static int printk_map_init(struct pevent *pevent) { struct printk_list *printklist; struct printk_list *item; struct printk_map *printk_map; int i; printk_map = malloc(sizeof(*printk_map) * (pevent->printk_count + 1)); if (!printk_map) return -1; printklist = pevent->printklist; i = 0; while (printklist) { printk_map[i].printk = printklist->printk; printk_map[i].addr = printklist->addr; i++; item = printklist; printklist = printklist->next; free(item); } qsort(printk_map, pevent->printk_count, sizeof(*printk_map), printk_cmp); pevent->printk_map = printk_map; pevent->printklist = NULL; return 0; } static struct printk_map * find_printk(struct pevent *pevent, unsigned long long addr) { struct printk_map *printk; struct printk_map key; if (!pevent->printk_map && printk_map_init(pevent)) return NULL; key.addr = addr; printk = bsearch(&key, pevent->printk_map, pevent->printk_count, sizeof(*pevent->printk_map), printk_cmp); return printk; } /** * pevent_register_print_string - register a string by its address * @pevent: handle for the pevent * @fmt: the string format to register * @addr: the address the string was located at * * This registers a string by the address it was stored in the kernel. * The @fmt passed in is duplicated. */ int pevent_register_print_string(struct pevent *pevent, char *fmt, unsigned long long addr) { struct printk_list *item = malloc(sizeof(*item)); if (!item) return -1; item->next = pevent->printklist; item->addr = addr; item->printk = strdup(fmt); if (!item->printk) goto out_free; pevent->printklist = item; pevent->printk_count++; return 0; out_free: free(item); errno = ENOMEM; return -1; } /** * pevent_print_printk - print out the stored strings * @pevent: handle for the pevent * * This prints the string formats that were stored. */ void pevent_print_printk(struct pevent *pevent) { int i; if (!pevent->printk_map) printk_map_init(pevent); for (i = 0; i < (int)pevent->printk_count; i++) { printf("%016llx %s\n", pevent->printk_map[i].addr, pevent->printk_map[i].printk); } } static struct event_format *alloc_event(void) { return calloc(1, sizeof(struct event_format)); } static int add_event(struct pevent *pevent, struct event_format *event) { int i; struct event_format **events = realloc(pevent->events, sizeof(event) * (pevent->nr_events + 1)); if (!events) return -1; pevent->events = events; for (i = 0; i < pevent->nr_events; i++) { if (pevent->events[i]->id > event->id) break; } if (i < pevent->nr_events) memmove(&pevent->events[i + 1], &pevent->events[i], sizeof(event) * (pevent->nr_events - i)); pevent->events[i] = event; pevent->nr_events++; event->pevent = pevent; return 0; } static int event_item_type(enum event_type type) { switch (type) { case EVENT_ITEM ... EVENT_SQUOTE: return 1; case EVENT_ERROR ... EVENT_DELIM: default: return 0; } } static void free_flag_sym(struct print_flag_sym *fsym) { struct print_flag_sym *next; while (fsym) { next = fsym->next; free(fsym->value); free(fsym->str); free(fsym); fsym = next; } } static void free_arg(struct print_arg *arg) { struct print_arg *farg; if (!arg) return; switch (arg->type) { case PRINT_ATOM: free(arg->atom.atom); break; case PRINT_FIELD: free(arg->field.name); break; case PRINT_FLAGS: free_arg(arg->flags.field); free(arg->flags.delim); free_flag_sym(arg->flags.flags); break; case PRINT_SYMBOL: free_arg(arg->symbol.field); free_flag_sym(arg->symbol.symbols); break; case PRINT_HEX: free_arg(arg->hex.field); free_arg(arg->hex.size); break; case PRINT_TYPE: free(arg->typecast.type); free_arg(arg->typecast.item); break; case PRINT_STRING: case PRINT_BSTRING: free(arg->string.string); break; case PRINT_DYNAMIC_ARRAY: free(arg->dynarray.index); break; case PRINT_OP: free(arg->op.op); free_arg(arg->op.left); free_arg(arg->op.right); break; case PRINT_FUNC: while (arg->func.args) { farg = arg->func.args; arg->func.args = farg->next; free_arg(farg); } break; case PRINT_NULL: default: break; } free(arg); } static enum event_type get_type(int ch) { if (ch == '\n') return EVENT_NEWLINE; if (isspace(ch)) return EVENT_SPACE; if (isalnum(ch) || ch == '_') return EVENT_ITEM; if (ch == '\'') return EVENT_SQUOTE; if (ch == '"') return EVENT_DQUOTE; if (!isprint(ch)) return EVENT_NONE; if (ch == '(' || ch == ')' || ch == ',') return EVENT_DELIM; return EVENT_OP; } static int __read_char(void) { if (input_buf_ptr >= input_buf_siz) return -1; return input_buf[input_buf_ptr++]; } static int __peek_char(void) { if (input_buf_ptr >= input_buf_siz) return -1; return input_buf[input_buf_ptr]; } /** * pevent_peek_char - peek at the next character that will be read * * Returns the next character read, or -1 if end of buffer. */ int pevent_peek_char(void) { return __peek_char(); } static int extend_token(char **tok, char *buf, int size) { char *newtok = realloc(*tok, size); if (!newtok) { free(*tok); *tok = NULL; return -1; } if (!*tok) strcpy(newtok, buf); else strcat(newtok, buf); *tok = newtok; return 0; } static enum event_type force_token(const char *str, char **tok); static enum event_type __read_token(char **tok) { char buf[BUFSIZ]; int ch, last_ch, quote_ch, next_ch; int i = 0; int tok_size = 0; enum event_type type; *tok = NULL; ch = __read_char(); if (ch < 0) return EVENT_NONE; type = get_type(ch); if (type == EVENT_NONE) return type; buf[i++] = ch; switch (type) { case EVENT_NEWLINE: case EVENT_DELIM: if (asprintf(tok, "%c", ch) < 0) return EVENT_ERROR; return type; case EVENT_OP: switch (ch) { case '-': next_ch = __peek_char(); if (next_ch == '>') { buf[i++] = __read_char(); break; } /* fall through */ case '+': case '|': case '&': case '>': case '<': last_ch = ch; ch = __peek_char(); if (ch != last_ch) goto test_equal; buf[i++] = __read_char(); switch (last_ch) { case '>': case '<': goto test_equal; default: break; } break; case '!': case '=': goto test_equal; default: /* what should we do instead? */ break; } buf[i] = 0; *tok = strdup(buf); return type; test_equal: ch = __peek_char(); if (ch == '=') buf[i++] = __read_char(); goto out; case EVENT_DQUOTE: case EVENT_SQUOTE: /* don't keep quotes */ i--; quote_ch = ch; last_ch = 0; concat: do { if (i == (BUFSIZ - 1)) { buf[i] = 0; tok_size += BUFSIZ; if (extend_token(tok, buf, tok_size) < 0) return EVENT_NONE; i = 0; } last_ch = ch; ch = __read_char(); buf[i++] = ch; /* the '\' '\' will cancel itself */ if (ch == '\\' && last_ch == '\\') last_ch = 0; } while (ch != quote_ch || last_ch == '\\'); /* remove the last quote */ i--; /* * For strings (double quotes) check the next token. * If it is another string, concatinate the two. */ if (type == EVENT_DQUOTE) { unsigned long long save_input_buf_ptr = input_buf_ptr; do { ch = __read_char(); } while (isspace(ch)); if (ch == '"') goto concat; input_buf_ptr = save_input_buf_ptr; } goto out; case EVENT_ERROR ... EVENT_SPACE: case EVENT_ITEM: default: break; } while (get_type(__peek_char()) == type) { if (i == (BUFSIZ - 1)) { buf[i] = 0; tok_size += BUFSIZ; if (extend_token(tok, buf, tok_size) < 0) return EVENT_NONE; i = 0; } ch = __read_char(); buf[i++] = ch; } out: buf[i] = 0; if (extend_token(tok, buf, tok_size + i + 1) < 0) return EVENT_NONE; if (type == EVENT_ITEM) { /* * Older versions of the kernel has a bug that * creates invalid symbols and will break the mac80211 * parsing. This is a work around to that bug. * * See Linux kernel commit: * 811cb50baf63461ce0bdb234927046131fc7fa8b */ if (strcmp(*tok, "LOCAL_PR_FMT") == 0) { free(*tok); *tok = NULL; return force_token("\"\%s\" ", tok); } else if (strcmp(*tok, "STA_PR_FMT") == 0) { free(*tok); *tok = NULL; return force_token("\" sta:%pM\" ", tok); } else if (strcmp(*tok, "VIF_PR_FMT") == 0) { free(*tok); *tok = NULL; return force_token("\" vif:%p(%d)\" ", tok); } } return type; } static enum event_type force_token(const char *str, char **tok) { const char *save_input_buf; unsigned long long save_input_buf_ptr; unsigned long long save_input_buf_siz; enum event_type type; /* save off the current input pointers */ save_input_buf = input_buf; save_input_buf_ptr = input_buf_ptr; save_input_buf_siz = input_buf_siz; init_input_buf(str, strlen(str)); type = __read_token(tok); /* reset back to original token */ input_buf = save_input_buf; input_buf_ptr = save_input_buf_ptr; input_buf_siz = save_input_buf_siz; return type; } static void free_token(char *tok) { if (tok) free(tok); } static enum event_type read_token(char **tok) { enum event_type type; for (;;) { type = __read_token(tok); if (type != EVENT_SPACE) return type; free_token(*tok); } /* not reached */ *tok = NULL; return EVENT_NONE; } /** * pevent_read_token - access to utilites to use the pevent parser * @tok: The token to return * * This will parse tokens from the string given by * pevent_init_data(). * * Returns the token type. */ enum event_type pevent_read_token(char **tok) { return read_token(tok); } /** * pevent_free_token - free a token returned by pevent_read_token * @token: the token to free */ void pevent_free_token(char *token) { free_token(token); } /* no newline */ static enum event_type read_token_item(char **tok) { enum event_type type; for (;;) { type = __read_token(tok); if (type != EVENT_SPACE && type != EVENT_NEWLINE) return type; free_token(*tok); *tok = NULL; } /* not reached */ *tok = NULL; return EVENT_NONE; } static int test_type(enum event_type type, enum event_type expect) { if (type != expect) { do_warning("Error: expected type %d but read %d", expect, type); return -1; } return 0; } static int test_type_token(enum event_type type, const char *token, enum event_type expect, const char *expect_tok) { if (type != expect) { do_warning("Error: expected type %d but read %d", expect, type); return -1; } if (strcmp(token, expect_tok) != 0) { do_warning("Error: expected '%s' but read '%s'", expect_tok, token); return -1; } return 0; } static int __read_expect_type(enum event_type expect, char **tok, int newline_ok) { enum event_type type; if (newline_ok) type = read_token(tok); else type = read_token_item(tok); return test_type(type, expect); } static int read_expect_type(enum event_type expect, char **tok) { return __read_expect_type(expect, tok, 1); } static int __read_expected(enum event_type expect, const char *str, int newline_ok) { enum event_type type; char *token; int ret; if (newline_ok) type = read_token(&token); else type = read_token_item(&token); ret = test_type_token(type, token, expect, str); free_token(token); return ret; } static int read_expected(enum event_type expect, const char *str) { return __read_expected(expect, str, 1); } static int read_expected_item(enum event_type expect, const char *str) { return __read_expected(expect, str, 0); } static char *event_read_name(void) { char *token; if (read_expected(EVENT_ITEM, "name") < 0) return NULL; if (read_expected(EVENT_OP, ":") < 0) return NULL; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; return token; fail: free_token(token); return NULL; } static int event_read_id(void) { char *token; int id; if (read_expected_item(EVENT_ITEM, "ID") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; id = strtoul(token, NULL, 0); free_token(token); return id; fail: free_token(token); return -1; } static int field_is_string(struct format_field *field) { if ((field->flags & FIELD_IS_ARRAY) && (strstr(field->type, "char") || strstr(field->type, "u8") || strstr(field->type, "s8"))) return 1; return 0; } static int field_is_dynamic(struct format_field *field) { if (strncmp(field->type, "__data_loc", 10) == 0) return 1; return 0; } static int field_is_long(struct format_field *field) { /* includes long long */ if (strstr(field->type, "long")) return 1; return 0; } static int event_read_fields(struct event_format *event, struct format_field **fields) { struct format_field *field = NULL; enum event_type type; char *token; char *last_token; int count = 0; do { type = read_token(&token); if (type == EVENT_NEWLINE) { free_token(token); return count; } count++; if (test_type_token(type, token, EVENT_ITEM, "field")) goto fail; free_token(token); type = read_token(&token); /* * The ftrace fields may still use the "special" name. * Just ignore it. */ if (event->flags & EVENT_FL_ISFTRACE && type == EVENT_ITEM && strcmp(token, "special") == 0) { free_token(token); type = read_token(&token); } if (test_type_token(type, token, EVENT_OP, ":") < 0) goto fail; free_token(token); if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; last_token = token; field = calloc(1, sizeof(*field)); if (!field) goto fail; field->event = event; /* read the rest of the type */ for (;;) { type = read_token(&token); if (type == EVENT_ITEM || (type == EVENT_OP && strcmp(token, "*") == 0) || /* * Some of the ftrace fields are broken and have * an illegal "." in them. */ (event->flags & EVENT_FL_ISFTRACE && type == EVENT_OP && strcmp(token, ".") == 0)) { if (strcmp(token, "*") == 0) field->flags |= FIELD_IS_POINTER; if (field->type) { char *new_type; new_type = realloc(field->type, strlen(field->type) + strlen(last_token) + 2); if (!new_type) { free(last_token); goto fail; } field->type = new_type; strcat(field->type, " "); strcat(field->type, last_token); free(last_token); } else field->type = last_token; last_token = token; continue; } break; } if (!field->type) { do_warning("%s: no type found", __func__); goto fail; } field->name = last_token; if (test_type(type, EVENT_OP)) goto fail; if (strcmp(token, "[") == 0) { enum event_type last_type = type; char *brackets = token; char *new_brackets; int len; field->flags |= FIELD_IS_ARRAY; type = read_token(&token); if (type == EVENT_ITEM) field->arraylen = strtoul(token, NULL, 0); else field->arraylen = 0; while (strcmp(token, "]") != 0) { if (last_type == EVENT_ITEM && type == EVENT_ITEM) len = 2; else len = 1; last_type = type; new_brackets = realloc(brackets, strlen(brackets) + strlen(token) + len); if (!new_brackets) { free(brackets); goto fail; } brackets = new_brackets; if (len == 2) strcat(brackets, " "); strcat(brackets, token); /* We only care about the last token */ field->arraylen = strtoul(token, NULL, 0); free_token(token); type = read_token(&token); if (type == EVENT_NONE) { do_warning("failed to find token"); goto fail; } } free_token(token); new_brackets = realloc(brackets, strlen(brackets) + 2); if (!new_brackets) { free(brackets); goto fail; } brackets = new_brackets; strcat(brackets, "]"); /* add brackets to type */ type = read_token(&token); /* * If the next token is not an OP, then it is of * the format: type [] item; */ if (type == EVENT_ITEM) { char *new_type; new_type = realloc(field->type, strlen(field->type) + strlen(field->name) + strlen(brackets) + 2); if (!new_type) { free(brackets); goto fail; } field->type = new_type; strcat(field->type, " "); strcat(field->type, field->name); free_token(field->name); strcat(field->type, brackets); field->name = token; type = read_token(&token); } else { char *new_type; new_type = realloc(field->type, strlen(field->type) + strlen(brackets) + 1); if (!new_type) { free(brackets); goto fail; } field->type = new_type; strcat(field->type, brackets); } free(brackets); } if (field_is_string(field)) field->flags |= FIELD_IS_STRING; if (field_is_dynamic(field)) field->flags |= FIELD_IS_DYNAMIC; if (field_is_long(field)) field->flags |= FIELD_IS_LONG; if (test_type_token(type, token, EVENT_OP, ";")) goto fail; free_token(token); if (read_expected(EVENT_ITEM, "offset") < 0) goto fail_expect; if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; field->offset = strtoul(token, NULL, 0); free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; if (read_expected(EVENT_ITEM, "size") < 0) goto fail_expect; if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; field->size = strtoul(token, NULL, 0); free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; type = read_token(&token); if (type != EVENT_NEWLINE) { /* newer versions of the kernel have a "signed" type */ if (test_type_token(type, token, EVENT_ITEM, "signed")) goto fail; free_token(token); if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; /* add signed type */ free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; } free_token(token); if (field->flags & FIELD_IS_ARRAY) { if (field->arraylen) field->elementsize = field->size / field->arraylen; else if (field->flags & FIELD_IS_STRING) field->elementsize = 1; else field->elementsize = event->pevent->long_size; } else field->elementsize = field->size; *fields = field; fields = &field->next; } while (1); return 0; fail: free_token(token); fail_expect: if (field) { free(field->type); free(field->name); free(field); } return -1; } static int event_read_format(struct event_format *event) { char *token; int ret; if (read_expected_item(EVENT_ITEM, "format") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; free_token(token); ret = event_read_fields(event, &event->format.common_fields); if (ret < 0) return ret; event->format.nr_common = ret; ret = event_read_fields(event, &event->format.fields); if (ret < 0) return ret; event->format.nr_fields = ret; return 0; fail: free_token(token); return -1; } static enum event_type process_arg_token(struct event_format *event, struct print_arg *arg, char **tok, enum event_type type); static enum event_type process_arg(struct event_format *event, struct print_arg *arg, char **tok) { enum event_type type; char *token; type = read_token(&token); *tok = token; return process_arg_token(event, arg, tok, type); } static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok); static enum event_type process_cond(struct event_format *event, struct print_arg *top, char **tok) { struct print_arg *arg, *left, *right; enum event_type type; char *token = NULL; arg = alloc_arg(); left = alloc_arg(); right = alloc_arg(); if (!arg || !left || !right) { do_warning("%s: not enough memory!", __func__); /* arg will be freed at out_free */ free_arg(left); free_arg(right); goto out_free; } arg->type = PRINT_OP; arg->op.left = left; arg->op.right = right; *tok = NULL; type = process_arg(event, left, &token); again: /* Handle other operations in the arguments */ if (type == EVENT_OP && strcmp(token, ":") != 0) { type = process_op(event, left, &token); goto again; } if (test_type_token(type, token, EVENT_OP, ":")) goto out_free; arg->op.op = token; type = process_arg(event, right, &token); top->op.right = arg; *tok = token; return type; out_free: /* Top may point to itself */ top->op.right = NULL; free_token(token); free_arg(arg); return EVENT_ERROR; } static enum event_type process_array(struct event_format *event, struct print_arg *top, char **tok) { struct print_arg *arg; enum event_type type; char *token = NULL; arg = alloc_arg(); if (!arg) { do_warning("%s: not enough memory!", __func__); /* '*tok' is set to top->op.op. No need to free. */ *tok = NULL; return EVENT_ERROR; } *tok = NULL; type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "]")) goto out_free; top->op.right = arg; free_token(token); type = read_token_item(&token); *tok = token; return type; out_free: free_token(token); free_arg(arg); return EVENT_ERROR; } static int get_op_prio(char *op) { if (!op[1]) { switch (op[0]) { case '~': case '!': return 4; case '*': case '/': case '%': return 6; case '+': case '-': return 7; /* '>>' and '<<' are 8 */ case '<': case '>': return 9; /* '==' and '!=' are 10 */ case '&': return 11; case '^': return 12; case '|': return 13; case '?': return 16; default: do_warning("unknown op '%c'", op[0]); return -1; } } else { if (strcmp(op, "++") == 0 || strcmp(op, "--") == 0) { return 3; } else if (strcmp(op, ">>") == 0 || strcmp(op, "<<") == 0) { return 8; } else if (strcmp(op, ">=") == 0 || strcmp(op, "<=") == 0) { return 9; } else if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0) { return 10; } else if (strcmp(op, "&&") == 0) { return 14; } else if (strcmp(op, "||") == 0) { return 15; } else { do_warning("unknown op '%s'", op); return -1; } } } static int set_op_prio(struct print_arg *arg) { /* single ops are the greatest */ if (!arg->op.left || arg->op.left->type == PRINT_NULL) arg->op.prio = 0; else arg->op.prio = get_op_prio(arg->op.op); return arg->op.prio; } /* Note, *tok does not get freed, but will most likely be saved */ static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *left, *right = NULL; enum event_type type; char *token; /* the op is passed in via tok */ token = *tok; if (arg->type == PRINT_OP && !arg->op.left) { /* handle single op */ if (token[1]) { do_warning("bad op token %s", token); goto out_free; } switch (token[0]) { case '~': case '!': case '+': case '-': break; default: do_warning("bad op token %s", token); goto out_free; } /* make an empty left */ left = alloc_arg(); if (!left) goto out_warn_free; left->type = PRINT_NULL; arg->op.left = left; right = alloc_arg(); if (!right) goto out_warn_free; arg->op.right = right; /* do not free the token, it belongs to an op */ *tok = NULL; type = process_arg(event, right, tok); } else if (strcmp(token, "?") == 0) { left = alloc_arg(); if (!left) goto out_warn_free; /* copy the top arg to the left */ *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.prio = 0; /* it will set arg->op.right */ type = process_cond(event, arg, tok); } else if (strcmp(token, ">>") == 0 || strcmp(token, "<<") == 0 || strcmp(token, "&") == 0 || strcmp(token, "|") == 0 || strcmp(token, "&&") == 0 || strcmp(token, "||") == 0 || strcmp(token, "-") == 0 || strcmp(token, "+") == 0 || strcmp(token, "*") == 0 || strcmp(token, "^") == 0 || strcmp(token, "/") == 0 || strcmp(token, "<") == 0 || strcmp(token, ">") == 0 || strcmp(token, "==") == 0 || strcmp(token, "!=") == 0) { left = alloc_arg(); if (!left) goto out_warn_free; /* copy the top arg to the left */ *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.right = NULL; if (set_op_prio(arg) == -1) { event->flags |= EVENT_FL_FAILED; /* arg->op.op (= token) will be freed at out_free */ arg->op.op = NULL; goto out_free; } type = read_token_item(&token); *tok = token; /* could just be a type pointer */ if ((strcmp(arg->op.op, "*") == 0) && type == EVENT_DELIM && (strcmp(token, ")") == 0)) { char *new_atom; if (left->type != PRINT_ATOM) { do_warning("bad pointer type"); goto out_free; } new_atom = realloc(left->atom.atom, strlen(left->atom.atom) + 3); if (!new_atom) goto out_warn_free; left->atom.atom = new_atom; strcat(left->atom.atom, " *"); free(arg->op.op); *arg = *left; free(left); return type; } right = alloc_arg(); if (!right) goto out_warn_free; type = process_arg_token(event, right, tok, type); arg->op.right = right; } else if (strcmp(token, "[") == 0) { left = alloc_arg(); if (!left) goto out_warn_free; *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.prio = 0; /* it will set arg->op.right */ type = process_array(event, arg, tok); } else { do_warning("unknown op '%s'", token); event->flags |= EVENT_FL_FAILED; /* the arg is now the left side */ goto out_free; } if (type == EVENT_OP && strcmp(*tok, ":") != 0) { int prio; /* higher prios need to be closer to the root */ prio = get_op_prio(*tok); if (prio > arg->op.prio) return process_op(event, arg, tok); return process_op(event, right, tok); } return type; out_warn_free: do_warning("%s: not enough memory!", __func__); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_entry(struct event_format *event __maybe_unused, struct print_arg *arg, char **tok) { enum event_type type; char *field; char *token; if (read_expected(EVENT_OP, "->") < 0) goto out_err; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; field = token; arg->type = PRINT_FIELD; arg->field.name = field; if (is_flag_field) { arg->field.field = pevent_find_any_field(event, arg->field.name); arg->field.field->flags |= FIELD_IS_FLAG; is_flag_field = 0; } else if (is_symbolic_field) { arg->field.field = pevent_find_any_field(event, arg->field.name); arg->field.field->flags |= FIELD_IS_SYMBOLIC; is_symbolic_field = 0; } type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static char *arg_eval (struct print_arg *arg); static unsigned long long eval_type_str(unsigned long long val, const char *type, int pointer) { int sign = 0; char *ref; int len; len = strlen(type); if (pointer) { if (type[len-1] != '*') { do_warning("pointer expected with non pointer type"); return val; } ref = malloc(len); if (!ref) { do_warning("%s: not enough memory!", __func__); return val; } memcpy(ref, type, len); /* chop off the " *" */ ref[len - 2] = 0; val = eval_type_str(val, ref, 0); free(ref); return val; } /* check if this is a pointer */ if (type[len - 1] == '*') return val; /* Try to figure out the arg size*/ if (strncmp(type, "struct", 6) == 0) /* all bets off */ return val; if (strcmp(type, "u8") == 0) return val & 0xff; if (strcmp(type, "u16") == 0) return val & 0xffff; if (strcmp(type, "u32") == 0) return val & 0xffffffff; if (strcmp(type, "u64") == 0 || strcmp(type, "s64")) return val; if (strcmp(type, "s8") == 0) return (unsigned long long)(char)val & 0xff; if (strcmp(type, "s16") == 0) return (unsigned long long)(short)val & 0xffff; if (strcmp(type, "s32") == 0) return (unsigned long long)(int)val & 0xffffffff; if (strncmp(type, "unsigned ", 9) == 0) { sign = 0; type += 9; } if (strcmp(type, "char") == 0) { if (sign) return (unsigned long long)(char)val & 0xff; else return val & 0xff; } if (strcmp(type, "short") == 0) { if (sign) return (unsigned long long)(short)val & 0xffff; else return val & 0xffff; } if (strcmp(type, "int") == 0) { if (sign) return (unsigned long long)(int)val & 0xffffffff; else return val & 0xffffffff; } return val; } /* * Try to figure out the type. */ static unsigned long long eval_type(unsigned long long val, struct print_arg *arg, int pointer) { if (arg->type != PRINT_TYPE) { do_warning("expected type argument"); return 0; } return eval_type_str(val, arg->typecast.type, pointer); } static int arg_num_eval(struct print_arg *arg, long long *val) { long long left, right; int ret = 1; switch (arg->type) { case PRINT_ATOM: *val = strtoll(arg->atom.atom, NULL, 0); break; case PRINT_TYPE: ret = arg_num_eval(arg->typecast.item, val); if (!ret) break; *val = eval_type(*val, arg, 0); break; case PRINT_OP: switch (arg->op.op[0]) { case '|': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; if (arg->op.op[1]) *val = left || right; else *val = left | right; break; case '&': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; if (arg->op.op[1]) *val = left && right; else *val = left & right; break; case '<': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; switch (arg->op.op[1]) { case 0: *val = left < right; break; case '<': *val = left << right; break; case '=': *val = left <= right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case '>': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; switch (arg->op.op[1]) { case 0: *val = left > right; break; case '>': *val = left >> right; break; case '=': *val = left >= right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case '=': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; if (arg->op.op[1] != '=') { do_warning("unknown op '%s'", arg->op.op); ret = 0; } else *val = left == right; break; case '!': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; switch (arg->op.op[1]) { case '=': *val = left != right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case '-': /* check for negative */ if (arg->op.left->type == PRINT_NULL) left = 0; else ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; *val = left - right; break; case '+': if (arg->op.left->type == PRINT_NULL) left = 0; else ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; *val = left + right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case PRINT_NULL: case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: case PRINT_BSTRING: default: do_warning("invalid eval type %d", arg->type); ret = 0; } return ret; } static char *arg_eval (struct print_arg *arg) { long long val; static char buf[20]; switch (arg->type) { case PRINT_ATOM: return arg->atom.atom; case PRINT_TYPE: return arg_eval(arg->typecast.item); case PRINT_OP: if (!arg_num_eval(arg, &val)) break; sprintf(buf, "%lld", val); return buf; case PRINT_NULL: case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: case PRINT_BSTRING: default: do_warning("invalid eval type %d", arg->type); break; } return NULL; } static enum event_type process_fields(struct event_format *event, struct print_flag_sym **list, char **tok) { enum event_type type; struct print_arg *arg = NULL; struct print_flag_sym *field; char *token = *tok; char *value; do { free_token(token); type = read_token_item(&token); if (test_type_token(type, token, EVENT_OP, "{")) break; arg = alloc_arg(); if (!arg) goto out_free; free_token(token); type = process_arg(event, arg, &token); if (type == EVENT_OP) type = process_op(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; field = calloc(1, sizeof(*field)); if (!field) goto out_free; value = arg_eval(arg); if (value == NULL) goto out_free_field; field->value = strdup(value); if (field->value == NULL) goto out_free_field; free_arg(arg); arg = alloc_arg(); if (!arg) goto out_free; free_token(token); type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "}")) goto out_free_field; value = arg_eval(arg); if (value == NULL) goto out_free_field; field->str = strdup(value); if (field->str == NULL) goto out_free_field; free_arg(arg); arg = NULL; *list = field; list = &field->next; free_token(token); type = read_token_item(&token); } while (type == EVENT_DELIM && strcmp(token, ",") == 0); *tok = token; return type; out_free_field: free_flag_sym(field); out_free: free_arg(arg); free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_flags(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_FLAGS; field = alloc_arg(); if (!field) { do_warning("%s: not enough memory!", __func__); goto out_free; } type = process_arg(event, field, &token); /* Handle operations in the first argument */ while (type == EVENT_OP) type = process_op(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free_field; free_token(token); arg->flags.field = field; type = read_token_item(&token); if (event_item_type(type)) { arg->flags.delim = token; type = read_token_item(&token); } if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; type = process_fields(event, &arg->flags.flags, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free_field: free_arg(field); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_symbols(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_SYMBOL; field = alloc_arg(); if (!field) { do_warning("%s: not enough memory!", __func__); goto out_free; } type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free_field; arg->symbol.field = field; type = process_fields(event, &arg->symbol.symbols, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free_field: free_arg(field); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_hex(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_HEX; field = alloc_arg(); if (!field) { do_warning("%s: not enough memory!", __func__); goto out_free; } type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; arg->hex.field = field; free_token(token); field = alloc_arg(); if (!field) { do_warning("%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; } type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; arg->hex.size = field; free_token(token); type = read_token_item(tok); return type; out_free: free_arg(field); free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) { struct format_field *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_DYNAMIC_ARRAY; /* * The item within the parenthesis is another field that holds * the index into where the array starts. */ type = read_token(&token); *tok = token; if (type != EVENT_ITEM) goto out_free; /* Find the field */ field = pevent_find_field(event, token); if (!field) goto out_free; arg->dynarray.field = field; arg->dynarray.index = 0; if (read_expected(EVENT_DELIM, ")") < 0) goto out_free; free_token(token); type = read_token_item(&token); *tok = token; if (type != EVENT_OP || strcmp(token, "[") != 0) return type; free_token(token); arg = alloc_arg(); if (!field) { do_warning("%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; } type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free_arg; if (!test_type_token(type, token, EVENT_OP, "]")) goto out_free_arg; free_token(token); type = read_token_item(tok); return type; out_free_arg: free_arg(arg); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_paren(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *item_arg; enum event_type type; char *token; type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (type == EVENT_OP) type = process_op(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(&token); /* * If the next token is an item or another open paren, then * this was a typecast. */ if (event_item_type(type) || (type == EVENT_DELIM && strcmp(token, "(") == 0)) { /* make this a typecast and contine */ /* prevous must be an atom */ if (arg->type != PRINT_ATOM) { do_warning("previous needed to be PRINT_ATOM"); goto out_free; } item_arg = alloc_arg(); if (!item_arg) { do_warning("%s: not enough memory!", __func__); goto out_free; } arg->type = PRINT_TYPE; arg->typecast.type = arg->atom.atom; arg->typecast.item = item_arg; type = process_arg_token(event, item_arg, &token, type); } *tok = token; return type; out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_str(struct event_format *event __maybe_unused, struct print_arg *arg, char **tok) { enum event_type type; char *token; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; arg->type = PRINT_STRING; arg->string.string = token; arg->string.offset = -1; if (read_expected(EVENT_DELIM, ")") < 0) goto out_err; type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static struct pevent_function_handler * find_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; if (!pevent) return NULL; for (func = pevent->func_handlers; func; func = func->next) { if (strcmp(func->name, func_name) == 0) break; } return func; } static void remove_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; struct pevent_function_handler **next; next = &pevent->func_handlers; while ((func = *next)) { if (strcmp(func->name, func_name) == 0) { *next = func->next; free_func_handle(func); break; } next = &func->next; } } static enum event_type process_func_handler(struct event_format *event, struct pevent_function_handler *func, struct print_arg *arg, char **tok) { struct print_arg **next_arg; struct print_arg *farg; enum event_type type; char *token; char *test; int i; arg->type = PRINT_FUNC; arg->func.func = func; *tok = NULL; next_arg = &(arg->func.args); for (i = 0; i < func->nr_args; i++) { farg = alloc_arg(); if (!farg) { do_warning("%s: not enough memory!", __func__); return EVENT_ERROR; } type = process_arg(event, farg, &token); if (i < (func->nr_args - 1)) test = ","; else test = ")"; if (test_type_token(type, token, EVENT_DELIM, test)) { free_arg(farg); free_token(token); return EVENT_ERROR; } *next_arg = farg; next_arg = &(farg->next); free_token(token); } type = read_token(&token); *tok = token; return type; } static enum event_type process_function(struct event_format *event, struct print_arg *arg, char *token, char **tok) { struct pevent_function_handler *func; if (strcmp(token, "__print_flags") == 0) { free_token(token); is_flag_field = 1; return process_flags(event, arg, tok); } if (strcmp(token, "__print_symbolic") == 0) { free_token(token); is_symbolic_field = 1; return process_symbols(event, arg, tok); } if (strcmp(token, "__print_hex") == 0) { free_token(token); return process_hex(event, arg, tok); } if (strcmp(token, "__get_str") == 0) { free_token(token); return process_str(event, arg, tok); } if (strcmp(token, "__get_dynamic_array") == 0) { free_token(token); return process_dynamic_array(event, arg, tok); } func = find_func_handler(event->pevent, token); if (func) { free_token(token); return process_func_handler(event, func, arg, tok); } do_warning("function %s not defined", token); free_token(token); return EVENT_ERROR; } static enum event_type process_arg_token(struct event_format *event, struct print_arg *arg, char **tok, enum event_type type) { char *token; char *atom; token = *tok; switch (type) { case EVENT_ITEM: if (strcmp(token, "REC") == 0) { free_token(token); type = process_entry(event, arg, &token); break; } atom = token; /* test the next token */ type = read_token_item(&token); /* * If the next token is a parenthesis, then this * is a function. */ if (type == EVENT_DELIM && strcmp(token, "(") == 0) { free_token(token); token = NULL; /* this will free atom. */ type = process_function(event, arg, atom, &token); break; } /* atoms can be more than one token long */ while (type == EVENT_ITEM) { char *new_atom; new_atom = realloc(atom, strlen(atom) + strlen(token) + 2); if (!new_atom) { free(atom); *tok = NULL; free_token(token); return EVENT_ERROR; } atom = new_atom; strcat(atom, " "); strcat(atom, token); free_token(token); type = read_token_item(&token); } arg->type = PRINT_ATOM; arg->atom.atom = atom; break; case EVENT_DQUOTE: case EVENT_SQUOTE: arg->type = PRINT_ATOM; arg->atom.atom = token; type = read_token_item(&token); break; case EVENT_DELIM: if (strcmp(token, "(") == 0) { free_token(token); type = process_paren(event, arg, &token); break; } case EVENT_OP: /* handle single ops */ arg->type = PRINT_OP; arg->op.op = token; arg->op.left = NULL; type = process_op(event, arg, &token); /* On error, the op is freed */ if (type == EVENT_ERROR) arg->op.op = NULL; /* return error type if errored */ break; case EVENT_ERROR ... EVENT_NEWLINE: default: do_warning("unexpected type %d", type); return EVENT_ERROR; } *tok = token; return type; } static int event_read_print_args(struct event_format *event, struct print_arg **list) { enum event_type type = EVENT_ERROR; struct print_arg *arg; char *token; int args = 0; do { if (type == EVENT_NEWLINE) { type = read_token_item(&token); continue; } arg = alloc_arg(); if (!arg) { do_warning("%s: not enough memory!", __func__); return -1; } type = process_arg(event, arg, &token); if (type == EVENT_ERROR) { free_token(token); free_arg(arg); return -1; } *list = arg; args++; if (type == EVENT_OP) { type = process_op(event, arg, &token); free_token(token); if (type == EVENT_ERROR) { *list = NULL; free_arg(arg); return -1; } list = &arg->next; continue; } if (type == EVENT_DELIM && strcmp(token, ",") == 0) { free_token(token); *list = arg; list = &arg->next; continue; } break; } while (type != EVENT_NONE); if (type != EVENT_NONE && type != EVENT_ERROR) free_token(token); return args; } static int event_read_print(struct event_format *event) { enum event_type type; char *token; int ret; if (read_expected_item(EVENT_ITEM, "print") < 0) return -1; if (read_expected(EVENT_ITEM, "fmt") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_DQUOTE, &token) < 0) goto fail; concat: event->print_fmt.format = token; event->print_fmt.args = NULL; /* ok to have no arg */ type = read_token_item(&token); if (type == EVENT_NONE) return 0; /* Handle concatenation of print lines */ if (type == EVENT_DQUOTE) { char *cat; if (asprintf(&cat, "%s%s", event->print_fmt.format, token) < 0) goto fail; free_token(token); free_token(event->print_fmt.format); event->print_fmt.format = NULL; token = cat; goto concat; } if (test_type_token(type, token, EVENT_DELIM, ",")) goto fail; free_token(token); ret = event_read_print_args(event, &event->print_fmt.args); if (ret < 0) return -1; return ret; fail: free_token(token); return -1; } /** * pevent_find_common_field - return a common field by event * @event: handle for the event * @name: the name of the common field to return * * Returns a common field from the event by the given @name. * This only searchs the common fields and not all field. */ struct format_field * pevent_find_common_field(struct event_format *event, const char *name) { struct format_field *format; for (format = event->format.common_fields; format; format = format->next) { if (strcmp(format->name, name) == 0) break; } return format; } /** * pevent_find_field - find a non-common field * @event: handle for the event * @name: the name of the non-common field * * Returns a non-common field by the given @name. * This does not search common fields. */ struct format_field * pevent_find_field(struct event_format *event, const char *name) { struct format_field *format; for (format = event->format.fields; format; format = format->next) { if (strcmp(format->name, name) == 0) break; } return format; } /** * pevent_find_any_field - find any field by name * @event: handle for the event * @name: the name of the field * * Returns a field by the given @name. * This searchs the common field names first, then * the non-common ones if a common one was not found. */ struct format_field * pevent_find_any_field(struct event_format *event, const char *name) { struct format_field *format; format = pevent_find_common_field(event, name); if (format) return format; return pevent_find_field(event, name); } /** * pevent_read_number - read a number from data * @pevent: handle for the pevent * @ptr: the raw data * @size: the size of the data that holds the number * * Returns the number (converted to host) from the * raw data. */ unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size) { switch (size) { case 1: return *(unsigned char *)ptr; case 2: return data2host2(pevent, ptr); case 4: return data2host4(pevent, ptr); case 8: return data2host8(pevent, ptr); default: /* BUG! */ return 0; } } /** * pevent_read_number_field - read a number from data * @field: a handle to the field * @data: the raw data to read * @value: the value to place the number in * * Reads raw data according to a field offset and size, * and translates it into @value. * * Returns 0 on success, -1 otherwise. */ int pevent_read_number_field(struct format_field *field, const void *data, unsigned long long *value) { if (!field) return -1; switch (field->size) { case 1: case 2: case 4: case 8: *value = pevent_read_number(field->event->pevent, data + field->offset, field->size); return 0; default: return -1; } } static int get_common_info(struct pevent *pevent, const char *type, int *offset, int *size) { struct event_format *event; struct format_field *field; /* * All events should have the same common elements. * Pick any event to find where the type is; */ if (!pevent->events) { do_warning("no event_list!"); return -1; } event = pevent->events[0]; field = pevent_find_common_field(event, type); if (!field) return -1; *offset = field->offset; *size = field->size; return 0; } static int __parse_common(struct pevent *pevent, void *data, int *size, int *offset, const char *name) { int ret; if (!*size) { ret = get_common_info(pevent, name, offset, size); if (ret < 0) return ret; } return pevent_read_number(pevent, data + *offset, *size); } static int trace_parse_common_type(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->type_size, &pevent->type_offset, "common_type"); } static int parse_common_pid(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->pid_size, &pevent->pid_offset, "common_pid"); } static int parse_common_pc(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->pc_size, &pevent->pc_offset, "common_preempt_count"); } static int parse_common_flags(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->flags_size, &pevent->flags_offset, "common_flags"); } static int parse_common_lock_depth(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->ld_size, &pevent->ld_offset, "common_lock_depth"); } static int parse_common_migrate_disable(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->ld_size, &pevent->ld_offset, "common_migrate_disable"); } static int events_id_cmp(const void *a, const void *b); /** * pevent_find_event - find an event by given id * @pevent: a handle to the pevent * @id: the id of the event * * Returns an event that has a given @id. */ struct event_format *pevent_find_event(struct pevent *pevent, int id) { struct event_format **eventptr; struct event_format key; struct event_format *pkey = &key; /* Check cache first */ if (pevent->last_event && pevent->last_event->id == id) return pevent->last_event; key.id = id; eventptr = bsearch(&pkey, pevent->events, pevent->nr_events, sizeof(*pevent->events), events_id_cmp); if (eventptr) { pevent->last_event = *eventptr; return *eventptr; } return NULL; } /** * pevent_find_event_by_name - find an event by given name * @pevent: a handle to the pevent * @sys: the system name to search for * @name: the name of the event to search for * * This returns an event with a given @name and under the system * @sys. If @sys is NULL the first event with @name is returned. */ struct event_format * pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name) { struct event_format *event = NULL; int i; if (pevent->last_event && strcmp(pevent->last_event->name, name) == 0 && (!sys || strcmp(pevent->last_event->system, sys) == 0)) return pevent->last_event; for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (strcmp(event->name, name) == 0) { if (!sys) break; if (strcmp(event->system, sys) == 0) break; } } if (i == pevent->nr_events) event = NULL; pevent->last_event = event; return event; } static unsigned long long eval_num_arg(void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent *pevent = event->pevent; unsigned long long val = 0; unsigned long long left, right; struct print_arg *typearg = NULL; struct print_arg *larg; unsigned long offset; unsigned int field_size; switch (arg->type) { case PRINT_NULL: /* ?? */ return 0; case PRINT_ATOM: return strtoull(arg->atom.atom, NULL, 0); case PRINT_FIELD: if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) goto out_warning_field; } /* must be a number */ val = pevent_read_number(pevent, data + arg->field.field->offset, arg->field.field->size); break; case PRINT_FLAGS: case PRINT_SYMBOL: case PRINT_HEX: break; case PRINT_TYPE: val = eval_num_arg(data, size, event, arg->typecast.item); return eval_type(val, arg, 0); case PRINT_STRING: case PRINT_BSTRING: return 0; case PRINT_FUNC: { struct trace_seq s; trace_seq_init(&s); val = process_defined_func(&s, data, size, event, arg); trace_seq_destroy(&s); return val; } case PRINT_OP: if (strcmp(arg->op.op, "[") == 0) { /* * Arrays are special, since we don't want * to read the arg as is. */ right = eval_num_arg(data, size, event, arg->op.right); /* handle typecasts */ larg = arg->op.left; while (larg->type == PRINT_TYPE) { if (!typearg) typearg = larg; larg = larg->typecast.item; } /* Default to long size */ field_size = pevent->long_size; switch (larg->type) { case PRINT_DYNAMIC_ARRAY: offset = pevent_read_number(pevent, data + larg->dynarray.field->offset, larg->dynarray.field->size); if (larg->dynarray.field->elementsize) field_size = larg->dynarray.field->elementsize; /* * The actual length of the dynamic array is stored * in the top half of the field, and the offset * is in the bottom half of the 32 bit field. */ offset &= 0xffff; offset += right; break; case PRINT_FIELD: if (!larg->field.field) { larg->field.field = pevent_find_any_field(event, larg->field.name); if (!larg->field.field) { arg = larg; goto out_warning_field; } } field_size = larg->field.field->elementsize; offset = larg->field.field->offset + right * larg->field.field->elementsize; break; default: goto default_op; /* oops, all bets off */ } val = pevent_read_number(pevent, data + offset, field_size); if (typearg) val = eval_type(val, typearg, 1); break; } else if (strcmp(arg->op.op, "?") == 0) { left = eval_num_arg(data, size, event, arg->op.left); arg = arg->op.right; if (left) val = eval_num_arg(data, size, event, arg->op.left); else val = eval_num_arg(data, size, event, arg->op.right); break; } default_op: left = eval_num_arg(data, size, event, arg->op.left); right = eval_num_arg(data, size, event, arg->op.right); switch (arg->op.op[0]) { case '!': switch (arg->op.op[1]) { case 0: val = !right; break; case '=': val = left != right; break; default: goto out_warning_op; } break; case '~': val = ~right; break; case '|': if (arg->op.op[1]) val = left || right; else val = left | right; break; case '&': if (arg->op.op[1]) val = left && right; else val = left & right; break; case '<': switch (arg->op.op[1]) { case 0: val = left < right; break; case '<': val = left << right; break; case '=': val = left <= right; break; default: goto out_warning_op; } break; case '>': switch (arg->op.op[1]) { case 0: val = left > right; break; case '>': val = left >> right; break; case '=': val = left >= right; break; default: goto out_warning_op; } break; case '=': if (arg->op.op[1] != '=') goto out_warning_op; val = left == right; break; case '-': val = left - right; break; case '+': val = left + right; break; case '/': val = left / right; break; case '*': val = left * right; break; default: goto out_warning_op; } break; default: /* not sure what to do there */ return 0; } return val; out_warning_op: do_warning("%s: unknown op '%s'", __func__, arg->op.op); return 0; out_warning_field: do_warning("%s: field %s not found", __func__, arg->field.name); return 0; } struct flag { const char *name; unsigned long long value; }; static const struct flag flags[] = { { "HI_SOFTIRQ", 0 }, { "TIMER_SOFTIRQ", 1 }, { "NET_TX_SOFTIRQ", 2 }, { "NET_RX_SOFTIRQ", 3 }, { "BLOCK_SOFTIRQ", 4 }, { "BLOCK_IOPOLL_SOFTIRQ", 5 }, { "TASKLET_SOFTIRQ", 6 }, { "SCHED_SOFTIRQ", 7 }, { "HRTIMER_SOFTIRQ", 8 }, { "RCU_SOFTIRQ", 9 }, { "HRTIMER_NORESTART", 0 }, { "HRTIMER_RESTART", 1 }, }; static unsigned long long eval_flag(const char *flag) { int i; /* * Some flags in the format files do not get converted. * If the flag is not numeric, see if it is something that * we already know about. */ if (isdigit(flag[0])) return strtoull(flag, NULL, 0); for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) if (strcmp(flags[i].name, flag) == 0) return flags[i].value; return 0; } static void print_str_to_seq(struct trace_seq *s, const char *format, int len_arg, const char *str) { if (len_arg >= 0) trace_seq_printf(s, format, len_arg, str); else trace_seq_printf(s, format, str); } static void print_str_arg(struct trace_seq *s, void *data, int size, struct event_format *event, const char *format, int len_arg, struct print_arg *arg) { struct pevent *pevent = event->pevent; struct print_flag_sym *flag; struct format_field *field; unsigned long long val, fval; unsigned long addr; char *str; unsigned char *hex; int print; int i, len; switch (arg->type) { case PRINT_NULL: /* ?? */ return; case PRINT_ATOM: print_str_to_seq(s, format, len_arg, arg->atom.atom); return; case PRINT_FIELD: field = arg->field.field; if (!field) { field = pevent_find_any_field(event, arg->field.name); if (!field) { str = arg->field.name; goto out_warning_field; } arg->field.field = field; } /* Zero sized fields, mean the rest of the data */ len = field->size ? : size - field->offset; /* * Some events pass in pointers. If this is not an array * and the size is the same as long_size, assume that it * is a pointer. */ if (!(field->flags & FIELD_IS_ARRAY) && field->size == pevent->long_size) { addr = *(unsigned long *)(data + field->offset); trace_seq_printf(s, "%lx", addr); break; } str = malloc(len + 1); if (!str) { do_warning("%s: not enough memory!", __func__); return; } memcpy(str, data + field->offset, len); str[len] = 0; print_str_to_seq(s, format, len_arg, str); free(str); break; case PRINT_FLAGS: val = eval_num_arg(data, size, event, arg->flags.field); print = 0; for (flag = arg->flags.flags; flag; flag = flag->next) { fval = eval_flag(flag->value); if (!val && !fval) { print_str_to_seq(s, format, len_arg, flag->str); break; } if (fval && (val & fval) == fval) { if (print && arg->flags.delim) trace_seq_puts(s, arg->flags.delim); print_str_to_seq(s, format, len_arg, flag->str); print = 1; val &= ~fval; } } break; case PRINT_SYMBOL: val = eval_num_arg(data, size, event, arg->symbol.field); for (flag = arg->symbol.symbols; flag; flag = flag->next) { fval = eval_flag(flag->value); if (val == fval) { print_str_to_seq(s, format, len_arg, flag->str); break; } } break; case PRINT_HEX: field = arg->hex.field->field.field; if (!field) { str = arg->hex.field->field.name; field = pevent_find_any_field(event, str); if (!field) goto out_warning_field; arg->hex.field->field.field = field; } hex = data + field->offset; len = eval_num_arg(data, size, event, arg->hex.size); for (i = 0; i < len; i++) { if (i) trace_seq_putc(s, ' '); trace_seq_printf(s, "%02x", hex[i]); } break; case PRINT_TYPE: break; case PRINT_STRING: { int str_offset; if (arg->string.offset == -1) { struct format_field *f; f = pevent_find_any_field(event, arg->string.string); arg->string.offset = f->offset; } str_offset = data2host4(pevent, data + arg->string.offset); str_offset &= 0xffff; print_str_to_seq(s, format, len_arg, ((char *)data) + str_offset); break; } case PRINT_BSTRING: print_str_to_seq(s, format, len_arg, arg->string.string); break; case PRINT_OP: /* * The only op for string should be ? : */ if (arg->op.op[0] != '?') return; val = eval_num_arg(data, size, event, arg->op.left); if (val) print_str_arg(s, data, size, event, format, len_arg, arg->op.right->op.left); else print_str_arg(s, data, size, event, format, len_arg, arg->op.right->op.right); break; case PRINT_FUNC: process_defined_func(s, data, size, event, arg); break; default: /* well... */ break; } return; out_warning_field: do_warning("%s: field %s not found", __func__, arg->field.name); } static unsigned long long process_defined_func(struct trace_seq *s, void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent_function_handler *func_handle = arg->func.func; struct pevent_func_params *param; unsigned long long *args; unsigned long long ret; struct print_arg *farg; struct trace_seq str; struct save_str { struct save_str *next; char *str; } *strings = NULL, *string; int i; if (!func_handle->nr_args) { ret = (*func_handle->func)(s, NULL); goto out; } farg = arg->func.args; param = func_handle->params; ret = ULLONG_MAX; args = malloc(sizeof(*args) * func_handle->nr_args); if (!args) goto out; for (i = 0; i < func_handle->nr_args; i++) { switch (param->type) { case PEVENT_FUNC_ARG_INT: case PEVENT_FUNC_ARG_LONG: case PEVENT_FUNC_ARG_PTR: args[i] = eval_num_arg(data, size, event, farg); break; case PEVENT_FUNC_ARG_STRING: trace_seq_init(&str); print_str_arg(&str, data, size, event, "%s", -1, farg); trace_seq_terminate(&str); string = malloc(sizeof(*string)); if (!string) { do_warning("%s(%d): malloc str", __func__, __LINE__); goto out_free; } string->next = strings; string->str = strdup(str.buffer); if (!string->str) { free(string); do_warning("%s(%d): malloc str", __func__, __LINE__); goto out_free; } args[i] = (uintptr_t)string->str; strings = string; trace_seq_destroy(&str); break; default: /* * Something went totally wrong, this is not * an input error, something in this code broke. */ do_warning("Unexpected end of arguments\n"); goto out_free; } farg = farg->next; param = param->next; } ret = (*func_handle->func)(s, args); out_free: free(args); while (strings) { string = strings; strings = string->next; free(string->str); free(string); } out: /* TBD : handle return type here */ return ret; } static void free_args(struct print_arg *args) { struct print_arg *next; while (args) { next = args->next; free_arg(args); args = next; } } static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; struct format_field *field, *ip_field; struct print_arg *args, *arg, **next; unsigned long long ip, val; char *ptr; void *bptr; int vsize; field = pevent->bprint_buf_field; ip_field = pevent->bprint_ip_field; if (!field) { field = pevent_find_field(event, "buf"); if (!field) { do_warning("can't find buffer field for binary printk"); return NULL; } ip_field = pevent_find_field(event, "ip"); if (!ip_field) { do_warning("can't find ip field for binary printk"); return NULL; } pevent->bprint_buf_field = field; pevent->bprint_ip_field = ip_field; } ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size); /* * The first arg is the IP pointer. */ args = alloc_arg(); if (!args) { do_warning("%s(%d): not enough memory!", __func__, __LINE__); return NULL; } arg = args; arg->next = NULL; next = &arg->next; arg->type = PRINT_ATOM; if (asprintf(&arg->atom.atom, "%lld", ip) < 0) goto out_free; /* skip the first "%pf : " */ for (ptr = fmt + 6, bptr = data + field->offset; bptr < data + size && *ptr; ptr++) { int ls = 0; if (*ptr == '%') { process_again: ptr++; switch (*ptr) { case '%': break; case 'l': ls++; goto process_again; case 'L': ls = 2; goto process_again; case '0' ... '9': goto process_again; case '.': goto process_again; case 'p': ls = 1; /* fall through */ case 'd': case 'u': case 'x': case 'i': switch (ls) { case 0: vsize = 4; break; case 1: vsize = pevent->long_size; break; case 2: vsize = 8; break; default: vsize = ls; /* ? */ break; } /* fall through */ case '*': if (*ptr == '*') vsize = 4; /* the pointers are always 4 bytes aligned */ bptr = (void *)(((unsigned long)bptr + 3) & ~3); val = pevent_read_number(pevent, bptr, vsize); bptr += vsize; arg = alloc_arg(); if (!arg) { do_warning("%s(%d): not enough memory!", __func__, __LINE__); goto out_free; } arg->next = NULL; arg->type = PRINT_ATOM; if (asprintf(&arg->atom.atom, "%lld", val) < 0) { free(arg); goto out_free; } *next = arg; next = &arg->next; /* * The '*' case means that an arg is used as the length. * We need to continue to figure out for what. */ if (*ptr == '*') goto process_again; break; case 's': arg = alloc_arg(); if (!arg) { do_warning("%s(%d): not enough memory!", __func__, __LINE__); goto out_free; } arg->next = NULL; arg->type = PRINT_BSTRING; arg->string.string = strdup(bptr); if (!arg->string.string) goto out_free; bptr += strlen(bptr) + 1; *next = arg; next = &arg->next; default: break; } } } return args; out_free: free_args(args); return NULL; } static char * get_bprint_format(void *data, int size __maybe_unused, struct event_format *event) { struct pevent *pevent = event->pevent; unsigned long long addr; struct format_field *field; struct printk_map *printk; char *format; char *p; field = pevent->bprint_fmt_field; if (!field) { field = pevent_find_field(event, "fmt"); if (!field) { do_warning("can't find format field for binary printk"); return NULL; } pevent->bprint_fmt_field = field; } addr = pevent_read_number(pevent, data + field->offset, field->size); printk = find_printk(pevent, addr); if (!printk) { if (asprintf(&format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr) < 0) return NULL; return format; } p = printk->printk; /* Remove any quotes. */ if (*p == '"') p++; if (asprintf(&format, "%s : %s", "%pf", p) < 0) return NULL; /* remove ending quotes and new line since we will add one too */ p = format + strlen(format) - 1; if (*p == '"') *p = 0; p -= 2; if (strcmp(p, "\\n") == 0) *p = 0; return format; } static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, struct event_format *event, struct print_arg *arg) { unsigned char *buf; char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); return; } if (arg->type != PRINT_FIELD) { trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); return; } if (mac == 'm') fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) { do_warning("%s: field %s not found", __func__, arg->field.name); return; } } if (arg->field.field->size != 6) { trace_seq_printf(s, "INVALIDMAC"); return; } buf = data + arg->field.field->offset; trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); } static int is_printable_array(char *p, unsigned int len) { unsigned int i; for (i = 0; i < len && p[i]; i++) if (!isprint(p[i])) return 0; return 1; } static void print_event_fields(struct trace_seq *s, void *data, int size, struct event_format *event) { struct format_field *field; unsigned long long val; unsigned int offset, len, i; field = event->format.fields; while (field) { trace_seq_printf(s, " %s=", field->name); if (field->flags & FIELD_IS_ARRAY) { offset = field->offset; len = field->size; if (field->flags & FIELD_IS_DYNAMIC) { val = pevent_read_number(event->pevent, data + offset, len); offset = val; len = offset >> 16; offset &= 0xffff; } if (field->flags & FIELD_IS_STRING && is_printable_array(data + offset, len)) { trace_seq_printf(s, "%s", (char *)data + offset); } else { trace_seq_puts(s, "ARRAY["); for (i = 0; i < len; i++) { if (i) trace_seq_puts(s, ", "); trace_seq_printf(s, "%02x", *((unsigned char *)data + offset + i)); } trace_seq_putc(s, ']'); field->flags &= ~FIELD_IS_STRING; } } else { val = pevent_read_number(event->pevent, data + field->offset, field->size); if (field->flags & FIELD_IS_POINTER) { trace_seq_printf(s, "0x%llx", val); } else if (field->flags & FIELD_IS_SIGNED) { switch (field->size) { case 4: /* * If field is long then print it in hex. * A long usually stores pointers. */ if (field->flags & FIELD_IS_LONG) trace_seq_printf(s, "0x%x", (int)val); else trace_seq_printf(s, "%d", (int)val); break; case 2: trace_seq_printf(s, "%2d", (short)val); break; case 1: trace_seq_printf(s, "%1d", (char)val); break; default: trace_seq_printf(s, "%lld", val); } } else { if (field->flags & FIELD_IS_LONG) trace_seq_printf(s, "0x%llx", val); else trace_seq_printf(s, "%llu", val); } } field = field->next; } } static void pretty_print(struct trace_seq *s, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; struct print_fmt *print_fmt = &event->print_fmt; struct print_arg *arg = print_fmt->args; struct print_arg *args = NULL; const char *ptr = print_fmt->format; unsigned long long val; struct func_map *func; const char *saveptr; char *bprint_fmt = NULL; char format[32]; int show_func; int len_as_arg; int len_arg = 0; int len; int ls; if (event->flags & EVENT_FL_FAILED) { trace_seq_printf(s, "[FAILED TO PARSE]"); print_event_fields(s, data, size, event); return; } if (event->flags & EVENT_FL_ISBPRINT) { bprint_fmt = get_bprint_format(data, size, event); args = make_bprint_args(bprint_fmt, data, size, event); arg = args; ptr = bprint_fmt; } for (; *ptr; ptr++) { ls = 0; if (*ptr == '\\') { ptr++; switch (*ptr) { case 'n': trace_seq_putc(s, '\n'); break; case 't': trace_seq_putc(s, '\t'); break; case 'r': trace_seq_putc(s, '\r'); break; case '\\': trace_seq_putc(s, '\\'); break; default: trace_seq_putc(s, *ptr); break; } } else if (*ptr == '%') { saveptr = ptr; show_func = 0; len_as_arg = 0; cont_process: ptr++; switch (*ptr) { case '%': trace_seq_putc(s, '%'); break; case '#': /* FIXME: need to handle properly */ goto cont_process; case 'h': ls--; goto cont_process; case 'l': ls++; goto cont_process; case 'L': ls = 2; goto cont_process; case '*': /* The argument is the length. */ if (!arg) { do_warning("no argument match"); event->flags |= EVENT_FL_FAILED; goto out_failed; } len_arg = eval_num_arg(data, size, event, arg); len_as_arg = 1; arg = arg->next; goto cont_process; case '.': case 'z': case 'Z': case '0' ... '9': goto cont_process; case 'p': if (pevent->long_size == 4) ls = 1; else ls = 2; if (*(ptr+1) == 'F' || *(ptr+1) == 'f') { ptr++; show_func = *ptr; } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { print_mac_arg(s, *(ptr+1), data, size, event, arg); ptr++; arg = arg->next; break; } /* fall through */ case 'd': case 'i': case 'x': case 'X': case 'u': if (!arg) { do_warning("no argument match"); event->flags |= EVENT_FL_FAILED; goto out_failed; } len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ if (len > 31) { do_warning("bad format!"); event->flags |= EVENT_FL_FAILED; len = 31; } memcpy(format, saveptr, len); format[len] = 0; val = eval_num_arg(data, size, event, arg); arg = arg->next; if (show_func) { func = find_func(pevent, val); if (func) { trace_seq_puts(s, func->func); if (show_func == 'F') trace_seq_printf(s, "+0x%llx", val - func->addr); break; } } if (pevent->long_size == 8 && ls && sizeof(long) != 8) { char *p; ls = 2; /* make %l into %ll */ p = strchr(format, 'l'); if (p) memmove(p+1, p, strlen(p)+1); else if (strcmp(format, "%p") == 0) strcpy(format, "0x%llx"); } switch (ls) { case -2: if (len_as_arg) trace_seq_printf(s, format, len_arg, (char)val); else trace_seq_printf(s, format, (char)val); break; case -1: if (len_as_arg) trace_seq_printf(s, format, len_arg, (short)val); else trace_seq_printf(s, format, (short)val); break; case 0: if (len_as_arg) trace_seq_printf(s, format, len_arg, (int)val); else trace_seq_printf(s, format, (int)val); break; case 1: if (len_as_arg) trace_seq_printf(s, format, len_arg, (long)val); else trace_seq_printf(s, format, (long)val); break; case 2: if (len_as_arg) trace_seq_printf(s, format, len_arg, (long long)val); else trace_seq_printf(s, format, (long long)val); break; default: do_warning("bad count (%d)", ls); event->flags |= EVENT_FL_FAILED; } break; case 's': if (!arg) { do_warning("no matching argument"); event->flags |= EVENT_FL_FAILED; goto out_failed; } len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ if (len > 31) { do_warning("bad format!"); event->flags |= EVENT_FL_FAILED; len = 31; } memcpy(format, saveptr, len); format[len] = 0; if (!len_as_arg) len_arg = -1; print_str_arg(s, data, size, event, format, len_arg, arg); arg = arg->next; break; default: trace_seq_printf(s, ">%c<", *ptr); } } else trace_seq_putc(s, *ptr); } if (event->flags & EVENT_FL_FAILED) { out_failed: trace_seq_printf(s, "[FAILED TO PARSE]"); } if (args) { free_args(args); free(bprint_fmt); } } /** * pevent_data_lat_fmt - parse the data for the latency format * @pevent: a handle to the pevent * @s: the trace_seq to write to * @record: the record to read from * * This parses out the Latency format (interrupts disabled, * need rescheduling, in hard/soft interrupt, preempt count * and lock depth) and places it into the trace_seq. */ void pevent_data_lat_fmt(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record) { static int check_lock_depth = 1; static int check_migrate_disable = 1; static int lock_depth_exists; static int migrate_disable_exists; unsigned int lat_flags; unsigned int pc; int lock_depth = 0; int migrate_disable = 0; int hardirq; int softirq; void *data = record->data; lat_flags = parse_common_flags(pevent, data); pc = parse_common_pc(pevent, data); /* lock_depth may not always exist */ if (lock_depth_exists) lock_depth = parse_common_lock_depth(pevent, data); else if (check_lock_depth) { lock_depth = parse_common_lock_depth(pevent, data); if (lock_depth < 0) check_lock_depth = 0; else lock_depth_exists = 1; } /* migrate_disable may not always exist */ if (migrate_disable_exists) migrate_disable = parse_common_migrate_disable(pevent, data); else if (check_migrate_disable) { migrate_disable = parse_common_migrate_disable(pevent, data); if (migrate_disable < 0) check_migrate_disable = 0; else migrate_disable_exists = 1; } hardirq = lat_flags & TRACE_FLAG_HARDIRQ; softirq = lat_flags & TRACE_FLAG_SOFTIRQ; trace_seq_printf(s, "%c%c%c", (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : '.', (lat_flags & TRACE_FLAG_NEED_RESCHED) ? 'N' : '.', (hardirq && softirq) ? 'H' : hardirq ? 'h' : softirq ? 's' : '.'); if (pc) trace_seq_printf(s, "%x", pc); else trace_seq_putc(s, '.'); if (migrate_disable_exists) { if (migrate_disable < 0) trace_seq_putc(s, '.'); else trace_seq_printf(s, "%d", migrate_disable); } if (lock_depth_exists) { if (lock_depth < 0) trace_seq_putc(s, '.'); else trace_seq_printf(s, "%d", lock_depth); } trace_seq_terminate(s); } /** * pevent_data_type - parse out the given event type * @pevent: a handle to the pevent * @rec: the record to read from * * This returns the event id from the @rec. */ int pevent_data_type(struct pevent *pevent, struct pevent_record *rec) { return trace_parse_common_type(pevent, rec->data); } /** * pevent_data_event_from_type - find the event by a given type * @pevent: a handle to the pevent * @type: the type of the event. * * This returns the event form a given @type; */ struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type) { return pevent_find_event(pevent, type); } /** * pevent_data_pid - parse the PID from raw data * @pevent: a handle to the pevent * @rec: the record to parse * * This returns the PID from a raw data. */ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec) { return parse_common_pid(pevent, rec->data); } /** * pevent_data_comm_from_pid - return the command line from PID * @pevent: a handle to the pevent * @pid: the PID of the task to search for * * This returns a pointer to the command line that has the given * @pid. */ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) { const char *comm; comm = find_cmdline(pevent, pid); return comm; } /** * pevent_data_comm_from_pid - parse the data into the print format * @s: the trace_seq to write to * @event: the handle to the event * @record: the record to read from * * This parses the raw @data using the given @event information and * writes the print format into the trace_seq. */ void pevent_event_info(struct trace_seq *s, struct event_format *event, struct pevent_record *record) { int print_pretty = 1; if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW)) print_event_fields(s, record->data, record->size, event); else { if (event->handler && !(event->flags & EVENT_FL_NOHANDLE)) print_pretty = event->handler(s, record, event, event->context); if (print_pretty) pretty_print(s, record->data, record->size, event); } trace_seq_terminate(s); } void pevent_print_event(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record) { static char *spaces = " "; /* 20 spaces */ struct event_format *event; unsigned long secs; unsigned long usecs; unsigned long nsecs; const char *comm; void *data = record->data; int type; int pid; int len; int p; secs = record->ts / NSECS_PER_SEC; nsecs = record->ts - secs * NSECS_PER_SEC; if (record->size < 0) { do_warning("ug! negative record size %d", record->size); return; } type = trace_parse_common_type(pevent, data); event = pevent_find_event(pevent, type); if (!event) { do_warning("ug! no event found for type %d", type); return; } pid = parse_common_pid(pevent, data); comm = find_cmdline(pevent, pid); if (pevent->latency_format) { trace_seq_printf(s, "%8.8s-%-5d %3d", comm, pid, record->cpu); pevent_data_lat_fmt(pevent, s, record); } else trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); if (pevent->flags & PEVENT_NSEC_OUTPUT) { usecs = nsecs; p = 9; } else { usecs = (nsecs + 500) / NSECS_PER_USEC; p = 6; } trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name); /* Space out the event names evenly. */ len = strlen(event->name); if (len < 20) trace_seq_printf(s, "%.*s", 20 - len, spaces); pevent_event_info(s, event, record); } static int events_id_cmp(const void *a, const void *b) { struct event_format * const * ea = a; struct event_format * const * eb = b; if ((*ea)->id < (*eb)->id) return -1; if ((*ea)->id > (*eb)->id) return 1; return 0; } static int events_name_cmp(const void *a, const void *b) { struct event_format * const * ea = a; struct event_format * const * eb = b; int res; res = strcmp((*ea)->name, (*eb)->name); if (res) return res; res = strcmp((*ea)->system, (*eb)->system); if (res) return res; return events_id_cmp(a, b); } static int events_system_cmp(const void *a, const void *b) { struct event_format * const * ea = a; struct event_format * const * eb = b; int res; res = strcmp((*ea)->system, (*eb)->system); if (res) return res; res = strcmp((*ea)->name, (*eb)->name); if (res) return res; return events_id_cmp(a, b); } struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type sort_type) { struct event_format **events; int (*sort)(const void *a, const void *b); events = pevent->sort_events; if (events && pevent->last_type == sort_type) return events; if (!events) { events = malloc(sizeof(*events) * (pevent->nr_events + 1)); if (!events) return NULL; memcpy(events, pevent->events, sizeof(*events) * pevent->nr_events); events[pevent->nr_events] = NULL; pevent->sort_events = events; /* the internal events are sorted by id */ if (sort_type == EVENT_SORT_ID) { pevent->last_type = sort_type; return events; } } switch (sort_type) { case EVENT_SORT_ID: sort = events_id_cmp; break; case EVENT_SORT_NAME: sort = events_name_cmp; break; case EVENT_SORT_SYSTEM: sort = events_system_cmp; break; default: return events; } qsort(events, pevent->nr_events, sizeof(*events), sort); pevent->last_type = sort_type; return events; } static struct format_field ** get_event_fields(const char *type, const char *name, int count, struct format_field *list) { struct format_field **fields; struct format_field *field; int i = 0; fields = malloc(sizeof(*fields) * (count + 1)); if (!fields) return NULL; for (field = list; field; field = field->next) { fields[i++] = field; if (i == count + 1) { do_warning("event %s has more %s fields than specified", name, type); i--; break; } } if (i != count) do_warning("event %s has less %s fields than specified", name, type); fields[i] = NULL; return fields; } /** * pevent_event_common_fields - return a list of common fields for an event * @event: the event to return the common fields of. * * Returns an allocated array of fields. The last item in the array is NULL. * The array must be freed with free(). */ struct format_field **pevent_event_common_fields(struct event_format *event) { return get_event_fields("common", event->name, event->format.nr_common, event->format.common_fields); } /** * pevent_event_fields - return a list of event specific fields for an event * @event: the event to return the fields of. * * Returns an allocated array of fields. The last item in the array is NULL. * The array must be freed with free(). */ struct format_field **pevent_event_fields(struct event_format *event) { return get_event_fields("event", event->name, event->format.nr_fields, event->format.fields); } static void print_fields(struct trace_seq *s, struct print_flag_sym *field) { trace_seq_printf(s, "{ %s, %s }", field->value, field->str); if (field->next) { trace_seq_puts(s, ", "); print_fields(s, field->next); } } /* for debugging */ static void print_args(struct print_arg *args) { int print_paren = 1; struct trace_seq s; switch (args->type) { case PRINT_NULL: printf("null"); break; case PRINT_ATOM: printf("%s", args->atom.atom); break; case PRINT_FIELD: printf("REC->%s", args->field.name); break; case PRINT_FLAGS: printf("__print_flags("); print_args(args->flags.field); printf(", %s, ", args->flags.delim); trace_seq_init(&s); print_fields(&s, args->flags.flags); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf(")"); break; case PRINT_SYMBOL: printf("__print_symbolic("); print_args(args->symbol.field); printf(", "); trace_seq_init(&s); print_fields(&s, args->symbol.symbols); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf(")"); break; case PRINT_HEX: printf("__print_hex("); print_args(args->hex.field); printf(", "); print_args(args->hex.size); printf(")"); break; case PRINT_STRING: case PRINT_BSTRING: printf("__get_str(%s)", args->string.string); break; case PRINT_TYPE: printf("(%s)", args->typecast.type); print_args(args->typecast.item); break; case PRINT_OP: if (strcmp(args->op.op, ":") == 0) print_paren = 0; if (print_paren) printf("("); print_args(args->op.left); printf(" %s ", args->op.op); print_args(args->op.right); if (print_paren) printf(")"); break; default: /* we should warn... */ return; } if (args->next) { printf("\n"); print_args(args->next); } } static void parse_header_field(const char *field, int *offset, int *size, int mandatory) { unsigned long long save_input_buf_ptr; unsigned long long save_input_buf_siz; char *token; int type; save_input_buf_ptr = input_buf_ptr; save_input_buf_siz = input_buf_siz; if (read_expected(EVENT_ITEM, "field") < 0) return; if (read_expected(EVENT_OP, ":") < 0) return; /* type */ if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; free_token(token); /* * If this is not a mandatory field, then test it first. */ if (mandatory) { if (read_expected(EVENT_ITEM, field) < 0) return; } else { if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; if (strcmp(token, field) != 0) goto discard; free_token(token); } if (read_expected(EVENT_OP, ";") < 0) return; if (read_expected(EVENT_ITEM, "offset") < 0) return; if (read_expected(EVENT_OP, ":") < 0) return; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; *offset = atoi(token); free_token(token); if (read_expected(EVENT_OP, ";") < 0) return; if (read_expected(EVENT_ITEM, "size") < 0) return; if (read_expected(EVENT_OP, ":") < 0) return; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; *size = atoi(token); free_token(token); if (read_expected(EVENT_OP, ";") < 0) return; type = read_token(&token); if (type != EVENT_NEWLINE) { /* newer versions of the kernel have a "signed" type */ if (type != EVENT_ITEM) goto fail; if (strcmp(token, "signed") != 0) goto fail; free_token(token); if (read_expected(EVENT_OP, ":") < 0) return; if (read_expect_type(EVENT_ITEM, &token)) goto fail; free_token(token); if (read_expected(EVENT_OP, ";") < 0) return; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; } fail: free_token(token); return; discard: input_buf_ptr = save_input_buf_ptr; input_buf_siz = save_input_buf_siz; *offset = 0; *size = 0; free_token(token); } /** * pevent_parse_header_page - parse the data stored in the header page * @pevent: the handle to the pevent * @buf: the buffer storing the header page format string * @size: the size of @buf * @long_size: the long size to use if there is no header * * This parses the header page format for information on the * ring buffer used. The @buf should be copied from * * /sys/kernel/debug/tracing/events/header_page */ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size) { int ignore; if (!size) { /* * Old kernels did not have header page info. * Sorry but we just use what we find here in user space. */ pevent->header_page_ts_size = sizeof(long long); pevent->header_page_size_size = long_size; pevent->header_page_data_offset = sizeof(long long) + long_size; pevent->old_format = 1; return -1; } init_input_buf(buf, size); parse_header_field("timestamp", &pevent->header_page_ts_offset, &pevent->header_page_ts_size, 1); parse_header_field("commit", &pevent->header_page_size_offset, &pevent->header_page_size_size, 1); parse_header_field("overwrite", &pevent->header_page_overwrite, &ignore, 0); parse_header_field("data", &pevent->header_page_data_offset, &pevent->header_page_data_size, 1); return 0; } static int event_matches(struct event_format *event, int id, const char *sys_name, const char *event_name) { if (id >= 0 && id != event->id) return 0; if (event_name && (strcmp(event_name, event->name) != 0)) return 0; if (sys_name && (strcmp(sys_name, event->system) != 0)) return 0; return 1; } static void free_handler(struct event_handler *handle) { free((void *)handle->sys_name); free((void *)handle->event_name); free(handle); } static int find_event_handle(struct pevent *pevent, struct event_format *event) { struct event_handler *handle, **next; for (next = &pevent->handlers; *next; next = &(*next)->next) { handle = *next; if (event_matches(event, handle->id, handle->sys_name, handle->event_name)) break; } if (!(*next)) return 0; pr_stat("overriding event (%d) %s:%s with new print handler", event->id, event->system, event->name); event->handler = handle->func; event->context = handle->context; *next = handle->next; free_handler(handle); return 1; } /** * __pevent_parse_format - parse the event format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to * * This parses the event format and creates an event structure * to quickly parse raw data for a given event. * * These files currently come from: * * /sys/kernel/debug/tracing/events/.../.../format */ static enum pevent_errno __pevent_parse_format(struct event_format **eventp, struct pevent *pevent, const char *buf, unsigned long size, const char *sys) { struct event_format *event; int ret; init_input_buf(buf, size); *eventp = event = alloc_event(); if (!event) return PEVENT_ERRNO__MEM_ALLOC_FAILED; event->name = event_read_name(); if (!event->name) { /* Bad event? */ ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_alloc_failed; } if (strcmp(sys, "ftrace") == 0) { event->flags |= EVENT_FL_ISFTRACE; if (strcmp(event->name, "bprint") == 0) event->flags |= EVENT_FL_ISBPRINT; } event->id = event_read_id(); if (event->id < 0) { ret = PEVENT_ERRNO__READ_ID_FAILED; /* * This isn't an allocation error actually. * But as the ID is critical, just bail out. */ goto event_alloc_failed; } event->system = strdup(sys); if (!event->system) { ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_alloc_failed; } /* Add pevent to event so that it can be referenced */ event->pevent = pevent; ret = event_read_format(event); if (ret < 0) { ret = PEVENT_ERRNO__READ_FORMAT_FAILED; goto event_parse_failed; } /* * If the event has an override, don't print warnings if the event * print format fails to parse. */ if (pevent && find_event_handle(pevent, event)) show_warning = 0; ret = event_read_print(event); show_warning = 1; if (ret < 0) { ret = PEVENT_ERRNO__READ_PRINT_FAILED; goto event_parse_failed; } if (!ret && (event->flags & EVENT_FL_ISFTRACE)) { struct format_field *field; struct print_arg *arg, **list; /* old ftrace had no args */ list = &event->print_fmt.args; for (field = event->format.fields; field; field = field->next) { arg = alloc_arg(); if (!arg) { event->flags |= EVENT_FL_FAILED; return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; } arg->type = PRINT_FIELD; arg->field.name = strdup(field->name); if (!arg->field.name) { event->flags |= EVENT_FL_FAILED; free_arg(arg); return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; } arg->field.field = field; *list = arg; list = &arg->next; } return 0; } return 0; event_parse_failed: event->flags |= EVENT_FL_FAILED; return ret; event_alloc_failed: free(event->system); free(event->name); free(event); *eventp = NULL; return ret; } /** * pevent_parse_format - parse the event format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to * * This parses the event format and creates an event structure * to quickly parse raw data for a given event. * * These files currently come from: * * /sys/kernel/debug/tracing/events/.../.../format */ enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, unsigned long size, const char *sys) { return __pevent_parse_format(eventp, NULL, buf, size, sys); } /** * pevent_parse_event - parse the event format * @pevent: the handle to the pevent * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to * * This parses the event format and creates an event structure * to quickly parse raw data for a given event. * * These files currently come from: * * /sys/kernel/debug/tracing/events/.../.../format */ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys) { struct event_format *event = NULL; int ret = __pevent_parse_format(&event, pevent, buf, size, sys); if (event == NULL) return ret; if (add_event(pevent, event)) { ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_add_failed; } #define PRINT_ARGS 0 if (PRINT_ARGS && event->print_fmt.args) print_args(event->print_fmt.args); return 0; event_add_failed: pevent_free_format(event); return ret; } #undef _PE #define _PE(code, str) str static const char * const pevent_error_str[] = { PEVENT_ERRORS }; #undef _PE int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, char *buf, size_t buflen) { int idx; const char *msg; if (errnum >= 0) { msg = strerror_r(errnum, buf, buflen); if (msg != buf) { size_t len = strlen(msg); memcpy(buf, msg, min(buflen - 1, len)); *(buf + min(buflen - 1, len)) = '\0'; } return 0; } if (errnum <= __PEVENT_ERRNO__START || errnum >= __PEVENT_ERRNO__END) return -1; idx = errnum - __PEVENT_ERRNO__START - 1; msg = pevent_error_str[idx]; switch (errnum) { case PEVENT_ERRNO__MEM_ALLOC_FAILED: case PEVENT_ERRNO__PARSE_EVENT_FAILED: case PEVENT_ERRNO__READ_ID_FAILED: case PEVENT_ERRNO__READ_FORMAT_FAILED: case PEVENT_ERRNO__READ_PRINT_FAILED: case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: snprintf(buf, buflen, "%s", msg); break; default: /* cannot reach here */ break; } return 0; } static int get_field_val(struct trace_seq *s, struct format_field *field, const char *name, struct pevent_record *record, unsigned long long *val, int err) { if (!field) { if (err) trace_seq_printf(s, "<CANT FIND FIELD %s>", name); return -1; } if (pevent_read_number_field(field, record->data, val)) { if (err) trace_seq_printf(s, " %s=INVALID", name); return -1; } return 0; } /** * pevent_get_field_raw - return the raw pointer into the data field * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @len: place to store the field length. * @err: print default error if failed. * * Returns a pointer into record->data of the field and places * the length of the field in @len. * * On failure, it returns NULL. */ void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, int *len, int err) { struct format_field *field; void *data = record->data; unsigned offset; int dummy; if (!event) return NULL; field = pevent_find_field(event, name); if (!field) { if (err) trace_seq_printf(s, "<CANT FIND FIELD %s>", name); return NULL; } /* Allow @len to be NULL */ if (!len) len = &dummy; offset = field->offset; if (field->flags & FIELD_IS_DYNAMIC) { offset = pevent_read_number(event->pevent, data + offset, field->size); *len = offset >> 16; offset &= 0xffff; } else *len = field->size; return data + offset; } /** * pevent_get_field_val - find a field and return its value * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @val: place to store the value of the field. * @err: print default error if failed. * * Returns 0 on success -1 on field not found. */ int pevent_get_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; if (!event) return -1; field = pevent_find_field(event, name); return get_field_val(s, field, name, record, val, err); } /** * pevent_get_common_field_val - find a common field and return its value * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @val: place to store the value of the field. * @err: print default error if failed. * * Returns 0 on success -1 on field not found. */ int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; if (!event) return -1; field = pevent_find_common_field(event, name); return get_field_val(s, field, name, record, val, err); } /** * pevent_get_any_field_val - find a any field and return its value * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @val: place to store the value of the field. * @err: print default error if failed. * * Returns 0 on success -1 on field not found. */ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; if (!event) return -1; field = pevent_find_any_field(event, name); return get_field_val(s, field, name, record, val, err); } /** * pevent_print_num_field - print a field and a format * @s: The seq to print to * @fmt: The printf format to print the field with. * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @err: print default error if failed. * * Returns: 0 on success, -1 field not found, or 1 if buffer is full. */ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err) { struct format_field *field = pevent_find_field(event, name); unsigned long long val; if (!field) goto failed; if (pevent_read_number_field(field, record->data, &val)) goto failed; return trace_seq_printf(s, fmt, val); failed: if (err) trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); return -1; } /** * pevent_print_func_field - print a field and a format for function pointers * @s: The seq to print to * @fmt: The printf format to print the field with. * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @err: print default error if failed. * * Returns: 0 on success, -1 field not found, or 1 if buffer is full. */ int pevent_print_func_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err) { struct format_field *field = pevent_find_field(event, name); struct pevent *pevent = event->pevent; unsigned long long val; struct func_map *func; char tmp[128]; if (!field) goto failed; if (pevent_read_number_field(field, record->data, &val)) goto failed; func = find_func(pevent, val); if (func) snprintf(tmp, 128, "%s/0x%llx", func->func, func->addr - val); else sprintf(tmp, "0x%08llx", val); return trace_seq_printf(s, fmt, tmp); failed: if (err) trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); return -1; } static void free_func_handle(struct pevent_function_handler *func) { struct pevent_func_params *params; free(func->name); while (func->params) { params = func->params; func->params = params->next; free(params); } free(func); } /** * pevent_register_print_function - register a helper function * @pevent: the handle to the pevent * @func: the function to process the helper function * @ret_type: the return type of the helper function * @name: the name of the helper function * @parameters: A list of enum pevent_func_arg_type * * Some events may have helper functions in the print format arguments. * This allows a plugin to dynamically create a way to process one * of these functions. * * The @parameters is a variable list of pevent_func_arg_type enums that * must end with PEVENT_FUNC_ARG_VOID. */ int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, char *name, ...) { struct pevent_function_handler *func_handle; struct pevent_func_params **next_param; struct pevent_func_params *param; enum pevent_func_arg_type type; va_list ap; int ret; func_handle = find_func_handler(pevent, name); if (func_handle) { /* * This is most like caused by the users own * plugins updating the function. This overrides the * system defaults. */ pr_stat("override of function helper '%s'", name); remove_func_handler(pevent, name); } func_handle = calloc(1, sizeof(*func_handle)); if (!func_handle) { do_warning("Failed to allocate function handler"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } func_handle->ret_type = ret_type; func_handle->name = strdup(name); func_handle->func = func; if (!func_handle->name) { do_warning("Failed to allocate function name"); free(func_handle); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } next_param = &(func_handle->params); va_start(ap, name); for (;;) { type = va_arg(ap, enum pevent_func_arg_type); if (type == PEVENT_FUNC_ARG_VOID) break; if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) { do_warning("Invalid argument type %d", type); ret = PEVENT_ERRNO__INVALID_ARG_TYPE; goto out_free; } param = malloc(sizeof(*param)); if (!param) { do_warning("Failed to allocate function param"); ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto out_free; } param->type = type; param->next = NULL; *next_param = param; next_param = &(param->next); func_handle->nr_args++; } va_end(ap); func_handle->next = pevent->func_handlers; pevent->func_handlers = func_handle; return 0; out_free: va_end(ap); free_func_handle(func_handle); return ret; } /** * pevent_register_event_handler - register a way to parse an event * @pevent: the handle to the pevent * @id: the id of the event to register * @sys_name: the system name the event belongs to * @event_name: the name of the event * @func: the function to call to parse the event information * @context: the data to be passed to @func * * This function allows a developer to override the parsing of * a given event. If for some reason the default print format * is not sufficient, this function will register a function * for an event to be used to parse the data instead. * * If @id is >= 0, then it is used to find the event. * else @sys_name and @event_name are used. */ int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name, pevent_event_handler_func func, void *context) { struct event_format *event; struct event_handler *handle; if (id >= 0) { /* search by id */ event = pevent_find_event(pevent, id); if (!event) goto not_found; if (event_name && (strcmp(event_name, event->name) != 0)) goto not_found; if (sys_name && (strcmp(sys_name, event->system) != 0)) goto not_found; } else { event = pevent_find_event_by_name(pevent, sys_name, event_name); if (!event) goto not_found; } pr_stat("overriding event (%d) %s:%s with new print handler", event->id, event->system, event->name); event->handler = func; event->context = context; return 0; not_found: /* Save for later use. */ handle = calloc(1, sizeof(*handle)); if (!handle) { do_warning("Failed to allocate event handler"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } handle->id = id; if (event_name) handle->event_name = strdup(event_name); if (sys_name) handle->sys_name = strdup(sys_name); if ((event_name && !handle->event_name) || (sys_name && !handle->sys_name)) { do_warning("Failed to allocate event/sys name"); free((void *)handle->event_name); free((void *)handle->sys_name); free(handle); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } handle->func = func; handle->next = pevent->handlers; pevent->handlers = handle; handle->context = context; return -1; } /** * pevent_alloc - create a pevent handle */ struct pevent *pevent_alloc(void) { struct pevent *pevent = calloc(1, sizeof(*pevent)); if (pevent) pevent->ref_count = 1; return pevent; } void pevent_ref(struct pevent *pevent) { pevent->ref_count++; } static void free_format_fields(struct format_field *field) { struct format_field *next; while (field) { next = field->next; free(field->type); free(field->name); free(field); field = next; } } static void free_formats(struct format *format) { free_format_fields(format->common_fields); free_format_fields(format->fields); } void pevent_free_format(struct event_format *event) { free(event->name); free(event->system); free_formats(&event->format); free(event->print_fmt.format); free_args(event->print_fmt.args); free(event); } /** * pevent_free - free a pevent handle * @pevent: the pevent handle to free */ void pevent_free(struct pevent *pevent) { struct cmdline_list *cmdlist, *cmdnext; struct func_list *funclist, *funcnext; struct printk_list *printklist, *printknext; struct pevent_function_handler *func_handler; struct event_handler *handle; int i; if (!pevent) return; cmdlist = pevent->cmdlist; funclist = pevent->funclist; printklist = pevent->printklist; pevent->ref_count--; if (pevent->ref_count) return; if (pevent->cmdlines) { for (i = 0; i < pevent->cmdline_count; i++) free(pevent->cmdlines[i].comm); free(pevent->cmdlines); } while (cmdlist) { cmdnext = cmdlist->next; free(cmdlist->comm); free(cmdlist); cmdlist = cmdnext; } if (pevent->func_map) { for (i = 0; i < pevent->func_count; i++) { free(pevent->func_map[i].func); free(pevent->func_map[i].mod); } free(pevent->func_map); } while (funclist) { funcnext = funclist->next; free(funclist->func); free(funclist->mod); free(funclist); funclist = funcnext; } while (pevent->func_handlers) { func_handler = pevent->func_handlers; pevent->func_handlers = func_handler->next; free_func_handle(func_handler); } if (pevent->printk_map) { for (i = 0; i < pevent->printk_count; i++) free(pevent->printk_map[i].printk); free(pevent->printk_map); } while (printklist) { printknext = printklist->next; free(printklist->printk); free(printklist); printklist = printknext; } for (i = 0; i < pevent->nr_events; i++) pevent_free_format(pevent->events[i]); while (pevent->handlers) { handle = pevent->handlers; pevent->handlers = handle->next; free_handler(handle); } free(pevent->events); free(pevent->sort_events); free(pevent); } void pevent_unref(struct pevent *pevent) { pevent_free(pevent); } 0707010000001C000081A4000000000000000000000001611B979000005209000000000000000000000000000000000000003600000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/event-parse.h/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _PARSE_EVENTS_H #define _PARSE_EVENTS_H #include <stdarg.h> #include <regex.h> #ifndef __maybe_unused #define __maybe_unused __attribute__((unused)) #endif /* ----------------------- trace_seq ----------------------- */ #ifndef TRACE_SEQ_BUF_SIZE #define TRACE_SEQ_BUF_SIZE 4096 #endif #ifndef DEBUG_RECORD #define DEBUG_RECORD 0 #endif struct pevent_record { unsigned long long ts; unsigned long long offset; long long missed_events; /* buffer dropped events before */ int record_size; /* size of binary record */ int size; /* size of data */ void *data; int cpu; int ref_count; int locked; /* Do not free, even if ref_count is zero */ void *priv; #if DEBUG_RECORD struct pevent_record *prev; struct pevent_record *next; long alloc_addr; #endif }; /* * Trace sequences are used to allow a function to call several other functions * to create a string of data to use (up to a max of PAGE_SIZE). */ struct trace_seq { char *buffer; unsigned int buffer_size; unsigned int len; unsigned int readpos; }; void trace_seq_init(struct trace_seq *s); void trace_seq_destroy(struct trace_seq *s); extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) __attribute__ ((format (printf, 2, 0))); extern int trace_seq_puts(struct trace_seq *s, const char *str); extern int trace_seq_putc(struct trace_seq *s, unsigned char c); extern void trace_seq_terminate(struct trace_seq *s); extern int trace_seq_do_printf(struct trace_seq *s); /* ----------------------- pevent ----------------------- */ struct pevent; struct event_format; typedef int (*pevent_event_handler_func)(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); typedef int (*pevent_plugin_load_func)(struct pevent *pevent); typedef int (*pevent_plugin_unload_func)(void); struct plugin_option { struct plugin_option *next; void *handle; char *file; char *name; char *plugin_alias; char *description; const char *value; void *priv; int set; }; /* * Plugin hooks that can be called: * * PEVENT_PLUGIN_LOADER: (required) * The function name to initialized the plugin. * * int PEVENT_PLUGIN_LOADER(struct pevent *pevent) * * PEVENT_PLUGIN_UNLOADER: (optional) * The function called just before unloading * * int PEVENT_PLUGIN_UNLOADER(void) * * PEVENT_PLUGIN_OPTIONS: (optional) * Plugin options that can be set before loading * * struct plugin_option PEVENT_PLUGIN_OPTIONS[] = { * { * .name = "option-name", * .plugin_alias = "overide-file-name", (optional) * .description = "description of option to show users", * }, * { * .name = NULL, * }, * }; * * Array must end with .name = NULL; * * * .plugin_alias is used to give a shorter name to access * the vairable. Useful if a plugin handles more than one event. * * If .value is not set, then it is considered a boolean and only * .set will be processed. If .value is defined, then it is considered * a string option and .set will be ignored. * * PEVENT_PLUGIN_ALIAS: (optional) * The name to use for finding options (uses filename if not defined) */ #define PEVENT_PLUGIN_LOADER pevent_plugin_loader #define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader #define PEVENT_PLUGIN_OPTIONS pevent_plugin_options #define PEVENT_PLUGIN_ALIAS pevent_plugin_alias #define _MAKE_STR(x) #x #define MAKE_STR(x) _MAKE_STR(x) #define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER) #define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER) #define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS) #define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS) #define NSECS_PER_SEC 1000000000ULL #define NSECS_PER_USEC 1000ULL enum format_flags { FIELD_IS_ARRAY = 1, FIELD_IS_POINTER = 2, FIELD_IS_SIGNED = 4, FIELD_IS_STRING = 8, FIELD_IS_DYNAMIC = 16, FIELD_IS_LONG = 32, FIELD_IS_FLAG = 64, FIELD_IS_SYMBOLIC = 128, }; struct format_field { struct format_field *next; struct event_format *event; char *type; char *name; int offset; int size; unsigned int arraylen; unsigned int elementsize; unsigned long flags; }; struct format { int nr_common; int nr_fields; struct format_field *common_fields; struct format_field *fields; }; struct print_arg_atom { char *atom; }; struct print_arg_string { char *string; int offset; }; struct print_arg_field { char *name; struct format_field *field; }; struct print_flag_sym { struct print_flag_sym *next; char *value; char *str; }; struct print_arg_typecast { char *type; struct print_arg *item; }; struct print_arg_flags { struct print_arg *field; char *delim; struct print_flag_sym *flags; }; struct print_arg_symbol { struct print_arg *field; struct print_flag_sym *symbols; }; struct print_arg_hex { struct print_arg *field; struct print_arg *size; }; struct print_arg_dynarray { struct format_field *field; struct print_arg *index; }; struct print_arg; struct print_arg_op { char *op; int prio; struct print_arg *left; struct print_arg *right; }; struct pevent_function_handler; struct print_arg_func { struct pevent_function_handler *func; struct print_arg *args; }; enum print_arg_type { PRINT_NULL, PRINT_ATOM, PRINT_FIELD, PRINT_FLAGS, PRINT_SYMBOL, PRINT_HEX, PRINT_TYPE, PRINT_STRING, PRINT_BSTRING, PRINT_DYNAMIC_ARRAY, PRINT_OP, PRINT_FUNC, }; struct print_arg { struct print_arg *next; enum print_arg_type type; union { struct print_arg_atom atom; struct print_arg_field field; struct print_arg_typecast typecast; struct print_arg_flags flags; struct print_arg_symbol symbol; struct print_arg_hex hex; struct print_arg_func func; struct print_arg_string string; struct print_arg_op op; struct print_arg_dynarray dynarray; }; }; struct print_fmt { char *format; struct print_arg *args; }; struct event_format { struct pevent *pevent; char *name; int id; int flags; struct format format; struct print_fmt print_fmt; char *system; pevent_event_handler_func handler; void *context; }; enum { EVENT_FL_ISFTRACE = 0x01, EVENT_FL_ISPRINT = 0x02, EVENT_FL_ISBPRINT = 0x04, EVENT_FL_ISFUNCENT = 0x10, EVENT_FL_ISFUNCRET = 0x20, EVENT_FL_NOHANDLE = 0x40, EVENT_FL_PRINTRAW = 0x80, EVENT_FL_FAILED = 0x80000000 }; enum event_sort_type { EVENT_SORT_ID, EVENT_SORT_NAME, EVENT_SORT_SYSTEM, }; enum event_type { EVENT_ERROR, EVENT_NONE, EVENT_SPACE, EVENT_NEWLINE, EVENT_OP, EVENT_DELIM, EVENT_ITEM, EVENT_DQUOTE, EVENT_SQUOTE, }; typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s, unsigned long long *args); enum pevent_func_arg_type { PEVENT_FUNC_ARG_VOID, PEVENT_FUNC_ARG_INT, PEVENT_FUNC_ARG_LONG, PEVENT_FUNC_ARG_STRING, PEVENT_FUNC_ARG_PTR, PEVENT_FUNC_ARG_MAX_TYPES }; enum pevent_flag { PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ }; #define PEVENT_ERRORS \ _PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \ _PE(PARSE_EVENT_FAILED, "failed to parse event"), \ _PE(READ_ID_FAILED, "failed to read event id"), \ _PE(READ_FORMAT_FAILED, "failed to read event format"), \ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ _PE(INVALID_ARG_TYPE, "invalid argument type") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code enum pevent_errno { PEVENT_ERRNO__SUCCESS = 0, /* * Choose an arbitrary negative big number not to clash with standard * errno since SUS requires the errno has distinct positive values. * See 'Issue 6' in the link below. * * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html */ __PEVENT_ERRNO__START = -100000, PEVENT_ERRORS, __PEVENT_ERRNO__END, }; #undef _PE struct cmdline; struct cmdline_list; struct func_map; struct func_list; struct event_handler; struct pevent { int ref_count; int header_page_ts_offset; int header_page_ts_size; int header_page_size_offset; int header_page_size_size; int header_page_data_offset; int header_page_data_size; int header_page_overwrite; int file_bigendian; int host_bigendian; int latency_format; int old_format; int cpus; int long_size; struct cmdline *cmdlines; struct cmdline_list *cmdlist; int cmdline_count; struct func_map *func_map; struct func_list *funclist; unsigned int func_count; struct printk_map *printk_map; struct printk_list *printklist; unsigned int printk_count; struct event_format **events; int nr_events; struct event_format **sort_events; enum event_sort_type last_type; int type_offset; int type_size; int pid_offset; int pid_size; int pc_offset; int pc_size; int flags_offset; int flags_size; int ld_offset; int ld_size; int print_raw; int test_filters; int flags; struct format_field *bprint_ip_field; struct format_field *bprint_fmt_field; struct format_field *bprint_buf_field; struct event_handler *handlers; struct pevent_function_handler *func_handlers; int parsing_failures; /* cache */ struct event_format *last_event; }; static inline void pevent_set_flag(struct pevent *pevent, int flag) { pevent->flags |= flag; } static inline unsigned short __data2host2(struct pevent *pevent, unsigned short data) { unsigned short swap; if (pevent->host_bigendian == pevent->file_bigendian) return data; swap = ((data & 0xffULL) << 8) | ((data & (0xffULL << 8)) >> 8); return swap; } static inline unsigned int __data2host4(struct pevent *pevent, unsigned int data) { unsigned int swap; if (pevent->host_bigendian == pevent->file_bigendian) return data; swap = ((data & 0xffULL) << 24) | ((data & (0xffULL << 8)) << 8) | ((data & (0xffULL << 16)) >> 8) | ((data & (0xffULL << 24)) >> 24); return swap; } static inline unsigned long long __data2host8(struct pevent *pevent, unsigned long long data) { unsigned long long swap; if (pevent->host_bigendian == pevent->file_bigendian) return data; swap = ((data & 0xffULL) << 56) | ((data & (0xffULL << 8)) << 40) | ((data & (0xffULL << 16)) << 24) | ((data & (0xffULL << 24)) << 8) | ((data & (0xffULL << 32)) >> 8) | ((data & (0xffULL << 40)) >> 24) | ((data & (0xffULL << 48)) >> 40) | ((data & (0xffULL << 56)) >> 56); return swap; } #define data2host2(pevent, ptr) __data2host2(pevent, *(unsigned short *)(ptr)) #define data2host4(pevent, ptr) __data2host4(pevent, *(unsigned int *)(ptr)) #define data2host8(pevent, ptr) \ ({ \ unsigned long long __val; \ \ memcpy(&__val, (ptr), sizeof(unsigned long long)); \ __data2host8(pevent, __val); \ }) /* taken from kernel/trace/trace.h */ enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, TRACE_FLAG_IRQS_NOSUPPORT = 0x02, TRACE_FLAG_NEED_RESCHED = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, }; int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); int pevent_register_function(struct pevent *pevent, char *name, unsigned long long addr, char *mod); int pevent_register_print_string(struct pevent *pevent, char *fmt, unsigned long long addr); int pevent_pid_is_registered(struct pevent *pevent, int pid); void pevent_print_event(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record); int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size); enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys); enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, unsigned long size, const char *sys); void pevent_free_format(struct event_format *event); void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, int *len, int err); int pevent_get_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); int pevent_print_func_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name, pevent_event_handler_func func, void *context); int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, char *name, ...); struct format_field *pevent_find_common_field(struct event_format *event, const char *name); struct format_field *pevent_find_field(struct event_format *event, const char *name); struct format_field *pevent_find_any_field(struct event_format *event, const char *name); const char *pevent_find_function(struct pevent *pevent, unsigned long long addr); unsigned long long pevent_find_function_address(struct pevent *pevent, unsigned long long addr); unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size); int pevent_read_number_field(struct format_field *field, const void *data, unsigned long long *value); struct event_format *pevent_find_event(struct pevent *pevent, int id); struct event_format * pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name); void pevent_data_lat_fmt(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record); int pevent_data_type(struct pevent *pevent, struct pevent_record *rec); struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); void pevent_event_info(struct trace_seq *s, struct event_format *event, struct pevent_record *record); int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, char *buf, size_t buflen); struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); struct format_field **pevent_event_common_fields(struct event_format *event); struct format_field **pevent_event_fields(struct event_format *event); static inline int pevent_get_cpus(struct pevent *pevent) { return pevent->cpus; } static inline void pevent_set_cpus(struct pevent *pevent, int cpus) { pevent->cpus = cpus; } static inline int pevent_get_long_size(struct pevent *pevent) { return pevent->long_size; } static inline void pevent_set_long_size(struct pevent *pevent, int long_size) { pevent->long_size = long_size; } static inline int pevent_is_file_bigendian(struct pevent *pevent) { return pevent->file_bigendian; } static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian) { pevent->file_bigendian = endian; } static inline int pevent_is_host_bigendian(struct pevent *pevent) { return pevent->host_bigendian; } static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian) { pevent->host_bigendian = endian; } static inline int pevent_is_latency_format(struct pevent *pevent) { return pevent->latency_format; } static inline void pevent_set_latency_format(struct pevent *pevent, int lat) { pevent->latency_format = lat; } struct pevent *pevent_alloc(void); void pevent_free(struct pevent *pevent); void pevent_ref(struct pevent *pevent); void pevent_unref(struct pevent *pevent); /* access to the internal parser */ void pevent_buffer_init(const char *buf, unsigned long long size); enum event_type pevent_read_token(char **tok); void pevent_free_token(char *token); int pevent_peek_char(void); const char *pevent_get_input_buf(void); unsigned long long pevent_get_input_buf_ptr(void); /* for debugging */ void pevent_print_funcs(struct pevent *pevent); void pevent_print_printk(struct pevent *pevent); /* ----------------------- filtering ----------------------- */ enum filter_boolean_type { FILTER_FALSE, FILTER_TRUE, }; enum filter_op_type { FILTER_OP_AND = 1, FILTER_OP_OR, FILTER_OP_NOT, }; enum filter_cmp_type { FILTER_CMP_NONE, FILTER_CMP_EQ, FILTER_CMP_NE, FILTER_CMP_GT, FILTER_CMP_LT, FILTER_CMP_GE, FILTER_CMP_LE, FILTER_CMP_MATCH, FILTER_CMP_NOT_MATCH, FILTER_CMP_REGEX, FILTER_CMP_NOT_REGEX, }; enum filter_exp_type { FILTER_EXP_NONE, FILTER_EXP_ADD, FILTER_EXP_SUB, FILTER_EXP_MUL, FILTER_EXP_DIV, FILTER_EXP_MOD, FILTER_EXP_RSHIFT, FILTER_EXP_LSHIFT, FILTER_EXP_AND, FILTER_EXP_OR, FILTER_EXP_XOR, FILTER_EXP_NOT, }; enum filter_arg_type { FILTER_ARG_NONE, FILTER_ARG_BOOLEAN, FILTER_ARG_VALUE, FILTER_ARG_FIELD, FILTER_ARG_EXP, FILTER_ARG_OP, FILTER_ARG_NUM, FILTER_ARG_STR, }; enum filter_value_type { FILTER_NUMBER, FILTER_STRING, FILTER_CHAR }; struct fliter_arg; struct filter_arg_boolean { enum filter_boolean_type value; }; struct filter_arg_field { struct format_field *field; }; struct filter_arg_value { enum filter_value_type type; union { char *str; unsigned long long val; }; }; struct filter_arg_op { enum filter_op_type type; struct filter_arg *left; struct filter_arg *right; }; struct filter_arg_exp { enum filter_exp_type type; struct filter_arg *left; struct filter_arg *right; }; struct filter_arg_num { enum filter_cmp_type type; struct filter_arg *left; struct filter_arg *right; }; struct filter_arg_str { enum filter_cmp_type type; struct format_field *field; char *val; char *buffer; regex_t reg; }; struct filter_arg { enum filter_arg_type type; union { struct filter_arg_boolean boolean; struct filter_arg_field field; struct filter_arg_value value; struct filter_arg_op op; struct filter_arg_exp exp; struct filter_arg_num num; struct filter_arg_str str; }; }; struct filter_type { int event_id; struct event_format *event; struct filter_arg *filter; }; struct event_filter { struct pevent *pevent; int filters; struct filter_type *event_filters; }; struct event_filter *pevent_filter_alloc(struct pevent *pevent); #define FILTER_NONE -2 #define FILTER_NOEXIST -1 #define FILTER_MISS 0 #define FILTER_MATCH 1 enum filter_trivial_type { FILTER_TRIVIAL_FALSE, FILTER_TRIVIAL_TRUE, FILTER_TRIVIAL_BOTH, }; int pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str, char **error_str); int pevent_filter_match(struct event_filter *filter, struct pevent_record *record); int pevent_event_filtered(struct event_filter *filter, int event_id); void pevent_filter_reset(struct event_filter *filter); void pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type); void pevent_filter_free(struct event_filter *filter); char *pevent_filter_make_string(struct event_filter *filter, int event_id); int pevent_filter_remove_event(struct event_filter *filter, int event_id); int pevent_filter_event_has_trivial(struct event_filter *filter, int event_id, enum filter_trivial_type type); int pevent_filter_copy(struct event_filter *dest, struct event_filter *source); int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, enum filter_trivial_type type); int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2); #endif /* _PARSE_EVENTS_H */ 0707010000001D000081A4000000000000000000000001611B979000000815000000000000000000000000000000000000003600000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/event-utils.h/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef __UTIL_H #define __UTIL_H #include <string.h> #include <ctype.h> #include <stdarg.h> /* Can be overridden */ void die(const char *fmt, ...); void *malloc_or_die(unsigned int size); void warning(const char *fmt, ...); void pr_stat(const char *fmt, ...); void vpr_stat(const char *fmt, va_list ap); /* Always available */ void __die(const char *fmt, ...); void __warning(const char *fmt, ...); void __pr_stat(const char *fmt, ...); void __vdie(const char *fmt, va_list ap); void __vwarning(const char *fmt, va_list ap); void __vpr_stat(const char *fmt, va_list ap); #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) static inline char *strim(char *string) { char *ret; if (!string) return NULL; while (*string) { if (!isspace(*string)) break; string++; } ret = string; string = ret + strlen(ret) - 1; while (string > ret) { if (!isspace(*string)) break; string--; } string[1] = 0; return ret; } static inline int has_text(const char *text) { if (!text) return 0; while (*text) { if (!isspace(*text)) return 1; text++; } return 0; } #endif 0707010000001E000081A4000000000000000000000001611B9790000043E9000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/kbuffer-parse.c/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "kbuffer.h" #define MISSING_EVENTS (1 << 31) #define MISSING_STORED (1 << 30) #define COMMIT_MASK ((1 << 27) - 1) enum { KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0), KBUFFER_FL_BIG_ENDIAN = (1<<1), KBUFFER_FL_LONG_8 = (1<<2), KBUFFER_FL_OLD_FORMAT = (1<<3), }; #define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN) /** kbuffer * @timestamp - timestamp of current event * @lost_events - # of lost events between this subbuffer and previous * @flags - special flags of the kbuffer * @subbuffer - pointer to the sub-buffer page * @data - pointer to the start of data on the sub-buffer page * @index - index from @data to the @curr event data * @curr - offset from @data to the start of current event * (includes metadata) * @next - offset from @data to the start of next event * @size - The size of data on @data * @start - The offset from @subbuffer where @data lives * * @read_4 - Function to read 4 raw bytes (may swap) * @read_8 - Function to read 8 raw bytes (may swap) * @read_long - Function to read a long word (4 or 8 bytes with needed swap) */ struct kbuffer { unsigned long long timestamp; long long lost_events; unsigned long flags; void *subbuffer; void *data; unsigned int index; unsigned int curr; unsigned int next; unsigned int size; unsigned int start; unsigned int (*read_4)(void *ptr); unsigned long long (*read_8)(void *ptr); unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr); int (*next_event)(struct kbuffer *kbuf); }; static void *zmalloc(size_t size) { return calloc(1, size); } static int host_is_bigendian(void) { unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; unsigned int *ptr; ptr = (unsigned int *)str; return *ptr == 0x01020304; } static int do_swap(struct kbuffer *kbuf) { return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) & ENDIAN_MASK; } static unsigned long long __read_8(void *ptr) { unsigned long long data = *(unsigned long long *)ptr; return data; } static unsigned long long __read_8_sw(void *ptr) { unsigned long long data = *(unsigned long long *)ptr; unsigned long long swap; swap = ((data & 0xffULL) << 56) | ((data & (0xffULL << 8)) << 40) | ((data & (0xffULL << 16)) << 24) | ((data & (0xffULL << 24)) << 8) | ((data & (0xffULL << 32)) >> 8) | ((data & (0xffULL << 40)) >> 24) | ((data & (0xffULL << 48)) >> 40) | ((data & (0xffULL << 56)) >> 56); return swap; } static unsigned int __read_4(void *ptr) { unsigned int data = *(unsigned int *)ptr; return data; } static unsigned int __read_4_sw(void *ptr) { unsigned int data = *(unsigned int *)ptr; unsigned int swap; swap = ((data & 0xffULL) << 24) | ((data & (0xffULL << 8)) << 8) | ((data & (0xffULL << 16)) >> 8) | ((data & (0xffULL << 24)) >> 24); return swap; } static unsigned long long read_8(struct kbuffer *kbuf, void *ptr) { return kbuf->read_8(ptr); } static unsigned int read_4(struct kbuffer *kbuf, void *ptr) { return kbuf->read_4(ptr); } static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr) { return kbuf->read_8(ptr); } static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr) { return kbuf->read_4(ptr); } static unsigned long long read_long(struct kbuffer *kbuf, void *ptr) { return kbuf->read_long(kbuf, ptr); } static int calc_index(struct kbuffer *kbuf, void *ptr) { return (unsigned long)ptr - (unsigned long)kbuf->data; } static int __next_event(struct kbuffer *kbuf); /** * kbuffer_alloc - allocat a new kbuffer * @size; enum to denote size of word * @endian: enum to denote endianness * * Allocates and returns a new kbuffer. */ struct kbuffer * kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian) { struct kbuffer *kbuf; int flags = 0; switch (size) { case KBUFFER_LSIZE_4: break; case KBUFFER_LSIZE_8: flags |= KBUFFER_FL_LONG_8; break; default: fprintf(stderr, "buffer size %d is invalid\n", size); return NULL; } switch (endian) { case KBUFFER_ENDIAN_LITTLE: break; case KBUFFER_ENDIAN_BIG: flags |= KBUFFER_FL_BIG_ENDIAN; break; default: fprintf(stderr, "buffer endian %d is invalid\n", endian); return NULL; } kbuf = zmalloc(sizeof(*kbuf)); if (!kbuf) return NULL; kbuf->flags = flags; if (host_is_bigendian()) kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN; if (do_swap(kbuf)) { kbuf->read_8 = __read_8_sw; kbuf->read_4 = __read_4_sw; } else { kbuf->read_8 = __read_8; kbuf->read_4 = __read_4; } if (kbuf->flags & KBUFFER_FL_LONG_8) kbuf->read_long = __read_long_8; else kbuf->read_long = __read_long_4; /* May be changed by kbuffer_set_old_format() */ kbuf->next_event = __next_event; return kbuf; } /** kbuffer_free - free an allocated kbuffer * @kbuf: The kbuffer to free * * Can take NULL as a parameter. */ void kbuffer_free(struct kbuffer *kbuf) { free(kbuf); } static unsigned int type4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return (type_len_ts >> 29) & 3; else return type_len_ts & 3; } static unsigned int len4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return (type_len_ts >> 27) & 7; else return (type_len_ts >> 2) & 7; } static unsigned int type_len4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return (type_len_ts >> 27) & ((1 << 5) - 1); else return type_len_ts & ((1 << 5) - 1); } static unsigned int ts4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return type_len_ts & ((1 << 27) - 1); else return type_len_ts >> 5; } /* * Linux 2.6.30 and earlier (not much ealier) had a different * ring buffer format. It should be obsolete, but we handle it anyway. */ enum old_ring_buffer_type { OLD_RINGBUF_TYPE_PADDING, OLD_RINGBUF_TYPE_TIME_EXTEND, OLD_RINGBUF_TYPE_TIME_STAMP, OLD_RINGBUF_TYPE_DATA, }; static unsigned int old_update_pointers(struct kbuffer *kbuf) { unsigned long long extend; unsigned int type_len_ts; unsigned int type; unsigned int len; unsigned int delta; unsigned int length = 0; void *ptr = kbuf->data + kbuf->curr; type_len_ts = read_4(kbuf, ptr); ptr += 4; type = type4host(kbuf, type_len_ts); len = len4host(kbuf, type_len_ts); delta = ts4host(kbuf, type_len_ts); switch (type) { case OLD_RINGBUF_TYPE_PADDING: kbuf->next = kbuf->size; return 0; case OLD_RINGBUF_TYPE_TIME_EXTEND: extend = read_4(kbuf, ptr); extend <<= TS_SHIFT; extend += delta; delta = extend; ptr += 4; break; case OLD_RINGBUF_TYPE_TIME_STAMP: /* should never happen! */ kbuf->curr = kbuf->size; kbuf->next = kbuf->size; kbuf->index = kbuf->size; return -1; default: if (len) length = len * 4; else { length = read_4(kbuf, ptr); length -= 4; ptr += 4; } break; } kbuf->timestamp += delta; kbuf->index = calc_index(kbuf, ptr); kbuf->next = kbuf->index + length; return type; } static int __old_next_event(struct kbuffer *kbuf) { int type; do { kbuf->curr = kbuf->next; if (kbuf->next >= kbuf->size) return -1; type = old_update_pointers(kbuf); } while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING); return 0; } static unsigned int translate_data(struct kbuffer *kbuf, void *data, void **rptr, unsigned long long *delta, int *length) { unsigned long long extend; unsigned int type_len_ts; unsigned int type_len; type_len_ts = read_4(kbuf, data); data += 4; type_len = type_len4host(kbuf, type_len_ts); *delta = ts4host(kbuf, type_len_ts); switch (type_len) { case KBUFFER_TYPE_PADDING: *length = read_4(kbuf, data); data += *length; break; case KBUFFER_TYPE_TIME_EXTEND: extend = read_4(kbuf, data); data += 4; extend <<= TS_SHIFT; extend += *delta; *delta = extend; *length = 0; break; case KBUFFER_TYPE_TIME_STAMP: data += 12; *length = 0; break; case 0: *length = read_4(kbuf, data) - 4; *length = (*length + 3) & ~3; data += 4; break; default: *length = type_len * 4; break; } *rptr = data; return type_len; } static unsigned int update_pointers(struct kbuffer *kbuf) { unsigned long long delta; unsigned int type_len; int length; void *ptr = kbuf->data + kbuf->curr; type_len = translate_data(kbuf, ptr, &ptr, &delta, &length); kbuf->timestamp += delta; kbuf->index = calc_index(kbuf, ptr); kbuf->next = kbuf->index + length; return type_len; } /** * kbuffer_translate_data - read raw data to get a record * @swap: Set to 1 if bytes in words need to be swapped when read * @data: The raw data to read * @size: Address to store the size of the event data. * * Returns a pointer to the event data. To determine the entire * record size (record metadata + data) just add the difference between * @data and the returned value to @size. */ void *kbuffer_translate_data(int swap, void *data, unsigned int *size) { unsigned long long delta; struct kbuffer kbuf; int type_len; int length; void *ptr; if (swap) { kbuf.read_8 = __read_8_sw; kbuf.read_4 = __read_4_sw; kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN; } else { kbuf.read_8 = __read_8; kbuf.read_4 = __read_4; kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0; } type_len = translate_data(&kbuf, data, &ptr, &delta, &length); switch (type_len) { case KBUFFER_TYPE_PADDING: case KBUFFER_TYPE_TIME_EXTEND: case KBUFFER_TYPE_TIME_STAMP: return NULL; }; *size = length; return ptr; } static int __next_event(struct kbuffer *kbuf) { int type; do { kbuf->curr = kbuf->next; if (kbuf->next >= kbuf->size) return -1; type = update_pointers(kbuf); } while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING); return 0; } static int next_event(struct kbuffer *kbuf) { return kbuf->next_event(kbuf); } /** * kbuffer_next_event - increment the current pointer * @kbuf: The kbuffer to read * @ts: Address to store the next record's timestamp (may be NULL to ignore) * * Increments the pointers into the subbuffer of the kbuffer to point to the * next event so that the next kbuffer_read_event() will return a * new event. * * Returns the data of the next event if a new event exists on the subbuffer, * NULL otherwise. */ void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts) { int ret; if (!kbuf || !kbuf->subbuffer) return NULL; ret = next_event(kbuf); if (ret < 0) return NULL; if (ts) *ts = kbuf->timestamp; return kbuf->data + kbuf->index; } /** * kbuffer_load_subbuffer - load a new subbuffer into the kbuffer * @kbuf: The kbuffer to load * @subbuffer: The subbuffer to load into @kbuf. * * Load a new subbuffer (page) into @kbuf. This will reset all * the pointers and update the @kbuf timestamp. The next read will * return the first event on @subbuffer. * * Returns 0 on succes, -1 otherwise. */ int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer) { unsigned long long flags; void *ptr = subbuffer; if (!kbuf || !subbuffer) return -1; kbuf->subbuffer = subbuffer; kbuf->timestamp = read_8(kbuf, ptr); ptr += 8; kbuf->curr = 0; if (kbuf->flags & KBUFFER_FL_LONG_8) kbuf->start = 16; else kbuf->start = 12; kbuf->data = subbuffer + kbuf->start; flags = read_long(kbuf, ptr); kbuf->size = (unsigned int)flags & COMMIT_MASK; if (flags & MISSING_EVENTS) { if (flags & MISSING_STORED) { ptr = kbuf->data + kbuf->size; kbuf->lost_events = read_long(kbuf, ptr); } else kbuf->lost_events = -1; } else kbuf->lost_events = 0; kbuf->index = 0; kbuf->next = 0; next_event(kbuf); return 0; } /** * kbuffer_read_event - read the next event in the kbuffer subbuffer * @kbuf: The kbuffer to read from * @ts: The address to store the timestamp of the event (may be NULL to ignore) * * Returns a pointer to the data part of the current event. * NULL if no event is left on the subbuffer. */ void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts) { if (!kbuf || !kbuf->subbuffer) return NULL; if (kbuf->curr >= kbuf->size) return NULL; if (ts) *ts = kbuf->timestamp; return kbuf->data + kbuf->index; } /** * kbuffer_timestamp - Return the timestamp of the current event * @kbuf: The kbuffer to read from * * Returns the timestamp of the current (next) event. */ unsigned long long kbuffer_timestamp(struct kbuffer *kbuf) { return kbuf->timestamp; } /** * kbuffer_read_at_offset - read the event that is at offset * @kbuf: The kbuffer to read from * @offset: The offset into the subbuffer * @ts: The address to store the timestamp of the event (may be NULL to ignore) * * The @offset must be an index from the @kbuf subbuffer beginning. * If @offset is bigger than the stored subbuffer, NULL will be returned. * * Returns the data of the record that is at @offset. Note, @offset does * not need to be the start of the record, the offset just needs to be * in the record (or beginning of it). * * Note, the kbuf timestamp and pointers are updated to the * returned record. That is, kbuffer_read_event() will return the same * data and timestamp, and kbuffer_next_event() will increment from * this record. */ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts) { void *data = NULL; if (offset < kbuf->start) offset = 0; else offset -= kbuf->start; /* Reset the buffer */ kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); while (kbuf->curr < offset) { data = kbuffer_next_event(kbuf, ts); if (!data) break; } return data; } /** * kbuffer_subbuffer_size - the size of the loaded subbuffer * @kbuf: The kbuffer to read from * * Returns the size of the subbuffer. Note, this size is * where the last event resides. The stored subbuffer may actually be * bigger due to padding and such. */ int kbuffer_subbuffer_size(struct kbuffer *kbuf) { return kbuf->size; } /** * kbuffer_curr_index - Return the index of the record * @kbuf: The kbuffer to read from * * Returns the index from the start of the data part of * the subbuffer to the current location. Note this is not * from the start of the subbuffer. An index of zero will * point to the first record. Use kbuffer_curr_offset() for * the actually offset (that can be used by kbuffer_read_at_offset()) */ int kbuffer_curr_index(struct kbuffer *kbuf) { return kbuf->curr; } /** * kbuffer_curr_offset - Return the offset of the record * @kbuf: The kbuffer to read from * * Returns the offset from the start of the subbuffer to the * current location. */ int kbuffer_curr_offset(struct kbuffer *kbuf) { return kbuf->curr + kbuf->start; } /** * kbuffer_event_size - return the size of the event data * @kbuf: The kbuffer to read * * Returns the size of the event data (the payload not counting * the meta data of the record) of the current event. */ int kbuffer_event_size(struct kbuffer *kbuf) { return kbuf->next - kbuf->index; } /** * kbuffer_curr_size - return the size of the entire record * @kbuf: The kbuffer to read * * Returns the size of the entire record (meta data and payload) * of the current event. */ int kbuffer_curr_size(struct kbuffer *kbuf) { return kbuf->next - kbuf->curr; } /** * kbuffer_missed_events - return the # of missed events from last event. * @kbuf: The kbuffer to read from * * Returns the # of missed events (if recorded) before the current * event. Note, only events on the beginning of a subbuffer can * have missed events, all other events within the buffer will be * zero. */ int kbuffer_missed_events(struct kbuffer *kbuf) { /* Only the first event can have missed events */ if (kbuf->curr) return 0; return kbuf->lost_events; } /** * kbuffer_set_old_forma - set the kbuffer to use the old format parsing * @kbuf: The kbuffer to set * * This is obsolete (or should be). The first kernels to use the * new ring buffer had a slightly different ring buffer format * (2.6.30 and earlier). It is still somewhat supported by kbuffer, * but should not be counted on in the future. */ void kbuffer_set_old_format(struct kbuffer *kbuf) { kbuf->flags |= KBUFFER_FL_OLD_FORMAT; kbuf->next_event = __old_next_event; } 0707010000001F000081A4000000000000000000000001611B9790000008A6000000000000000000000000000000000000003200000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/kbuffer.h/* * Copyright (C) 2012 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _KBUFFER_H #define _KBUFFER_H #ifndef TS_SHIFT #define TS_SHIFT 27 #endif enum kbuffer_endian { KBUFFER_ENDIAN_BIG, KBUFFER_ENDIAN_LITTLE, }; enum kbuffer_long_size { KBUFFER_LSIZE_4, KBUFFER_LSIZE_8, }; enum { KBUFFER_TYPE_PADDING = 29, KBUFFER_TYPE_TIME_EXTEND = 30, KBUFFER_TYPE_TIME_STAMP = 31, }; struct kbuffer; struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian); void kbuffer_free(struct kbuffer *kbuf); int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer); void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts); void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts); unsigned long long kbuffer_timestamp(struct kbuffer *kbuf); void *kbuffer_translate_data(int swap, void *data, unsigned int *size); void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts); int kbuffer_curr_index(struct kbuffer *kbuf); int kbuffer_curr_offset(struct kbuffer *kbuf); int kbuffer_curr_size(struct kbuffer *kbuf); int kbuffer_event_size(struct kbuffer *kbuf); int kbuffer_missed_events(struct kbuffer *kbuf); int kbuffer_subbuffer_size(struct kbuffer *kbuf); void kbuffer_set_old_format(struct kbuffer *kbuf); #endif /* _K_BUFFER_H */ 07070100000020000081A4000000000000000000000001611B97900000BF22000000000000000000000000000000000000003700000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/parse-filter.c/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <errno.h> #include <sys/types.h> #include "event-parse.h" #include "event-utils.h" #define COMM "COMM" static struct format_field comm = { .name = "COMM", }; struct event_list { struct event_list *next; struct event_format *event; }; #define MAX_ERR_STR_SIZE 256 static void show_error(char **error_str, const char *fmt, ...) { unsigned long long index; const char *input; char *error; va_list ap; int len; int i; if (!error_str) return; input = pevent_get_input_buf(); index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; error = malloc_or_die(MAX_ERR_STR_SIZE + (len*2) + 3); if (len) { strcpy(error, input); error[len] = '\n'; for (i = 1; i < len && i < index; i++) error[len+i] = ' '; error[len + i] = '^'; error[len + i + 1] = '\n'; len += i+2; } va_start(ap, fmt); vsnprintf(error + len, MAX_ERR_STR_SIZE, fmt, ap); va_end(ap); *error_str = error; } static void free_token(char *token) { pevent_free_token(token); } static enum event_type read_token(char **tok) { enum event_type type; char *token = NULL; do { free_token(token); type = pevent_read_token(&token); } while (type == EVENT_NEWLINE || type == EVENT_SPACE); /* If token is = or ! check to see if the next char is ~ */ if (token && (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ *tok = malloc_or_die(3); sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ pevent_read_token(&token); free_token(token); } else *tok = token; return type; } static int filter_cmp(const void *a, const void *b) { const struct filter_type *ea = a; const struct filter_type *eb = b; if (ea->event_id < eb->event_id) return -1; if (ea->event_id > eb->event_id) return 1; return 0; } static struct filter_type * find_filter_type(struct event_filter *filter, int id) { struct filter_type *filter_type; struct filter_type key; key.event_id = id; filter_type = bsearch(&key, filter->event_filters, filter->filters, sizeof(*filter->event_filters), filter_cmp); return filter_type; } static struct filter_type * add_filter_type(struct event_filter *filter, int id) { struct filter_type *filter_type; int i; filter_type = find_filter_type(filter, id); if (filter_type) return filter_type; filter->event_filters = realloc(filter->event_filters, sizeof(*filter->event_filters) * (filter->filters + 1)); if (!filter->event_filters) die("Could not allocate filter"); for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) break; } if (i < filter->filters) memmove(&filter->event_filters[i+1], &filter->event_filters[i], sizeof(*filter->event_filters) * (filter->filters - i)); filter_type = &filter->event_filters[i]; filter_type->event_id = id; filter_type->event = pevent_find_event(filter->pevent, id); filter_type->filter = NULL; filter->filters++; return filter_type; } /** * pevent_filter_alloc - create a new event filter * @pevent: The pevent that this filter is associated with */ struct event_filter *pevent_filter_alloc(struct pevent *pevent) { struct event_filter *filter; filter = malloc_or_die(sizeof(*filter)); memset(filter, 0, sizeof(*filter)); filter->pevent = pevent; pevent_ref(pevent); return filter; } static struct filter_arg *allocate_arg(void) { struct filter_arg *arg; arg = malloc_or_die(sizeof(*arg)); memset(arg, 0, sizeof(*arg)); return arg; } static void free_arg(struct filter_arg *arg) { if (!arg) return; switch (arg->type) { case FILTER_ARG_NONE: case FILTER_ARG_BOOLEAN: break; case FILTER_ARG_NUM: free_arg(arg->num.left); free_arg(arg->num.right); break; case FILTER_ARG_EXP: free_arg(arg->exp.left); free_arg(arg->exp.right); break; case FILTER_ARG_STR: free(arg->str.val); regfree(&arg->str.reg); free(arg->str.buffer); break; case FILTER_ARG_VALUE: if (arg->value.type == FILTER_STRING || arg->value.type == FILTER_CHAR) free(arg->value.str); break; case FILTER_ARG_OP: free_arg(arg->op.left); free_arg(arg->op.right); default: break; } free(arg); } static void add_event(struct event_list **events, struct event_format *event) { struct event_list *list; list = malloc_or_die(sizeof(*list)); list->next = *events; *events = list; list->event = event; } static int event_match(struct event_format *event, regex_t *sreg, regex_t *ereg) { if (sreg) { return !regexec(sreg, event->system, 0, NULL, 0) && !regexec(ereg, event->name, 0, NULL, 0); } return !regexec(ereg, event->system, 0, NULL, 0) || !regexec(ereg, event->name, 0, NULL, 0); } static int find_event(struct pevent *pevent, struct event_list **events, char *sys_name, char *event_name) { struct event_format *event; regex_t ereg; regex_t sreg; int match = 0; char *reg; int ret; int i; if (!event_name) { /* if no name is given, then swap sys and name */ event_name = sys_name; sys_name = NULL; } reg = malloc_or_die(strlen(event_name) + 3); sprintf(reg, "^%s$", event_name); ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) return -1; if (sys_name) { reg = malloc_or_die(strlen(sys_name) + 3); sprintf(reg, "^%s$", sys_name); ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) { regfree(&ereg); return -1; } } for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { match = 1; add_event(events, event); } } regfree(&ereg); if (sys_name) regfree(&sreg); if (!match) return -1; return 0; } static void free_events(struct event_list *events) { struct event_list *event; while (events) { event = events; events = events->next; free(event); } } static struct filter_arg * create_arg_item(struct event_format *event, const char *token, enum event_type type, char **error_str) { struct format_field *field; struct filter_arg *arg; arg = allocate_arg(); switch (type) { case EVENT_SQUOTE: case EVENT_DQUOTE: arg->type = FILTER_ARG_VALUE; arg->value.type = type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; arg->value.str = strdup(token); if (!arg->value.str) die("malloc string"); break; case EVENT_ITEM: /* if it is a number, then convert it */ if (isdigit(token[0])) { arg->type = FILTER_ARG_VALUE; arg->value.type = FILTER_NUMBER; arg->value.val = strtoull(token, NULL, 0); break; } /* Consider this a field */ field = pevent_find_any_field(event, token); if (!field) { if (strcmp(token, COMM) != 0) { /* not a field, Make it false */ arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_FALSE; break; } /* If token is 'COMM' then it is special */ field = &comm; } arg->type = FILTER_ARG_FIELD; arg->field.field = field; break; default: free_arg(arg); show_error(error_str, "expected a value but found %s", token); return NULL; } return arg; } static struct filter_arg * create_arg_op(enum filter_op_type btype) { struct filter_arg *arg; arg = allocate_arg(); arg->type = FILTER_ARG_OP; arg->op.type = btype; return arg; } static struct filter_arg * create_arg_exp(enum filter_exp_type etype) { struct filter_arg *arg; arg = allocate_arg(); arg->type = FILTER_ARG_EXP; arg->op.type = etype; return arg; } static struct filter_arg * create_arg_cmp(enum filter_exp_type etype) { struct filter_arg *arg; arg = allocate_arg(); /* Use NUM and change if necessary */ arg->type = FILTER_ARG_NUM; arg->op.type = etype; return arg; } static int add_right(struct filter_arg *op, struct filter_arg *arg, char **error_str) { struct filter_arg *left; char *str; int op_type; int ret; switch (op->type) { case FILTER_ARG_EXP: if (op->exp.right) goto out_fail; op->exp.right = arg; break; case FILTER_ARG_OP: if (op->op.right) goto out_fail; op->op.right = arg; break; case FILTER_ARG_NUM: if (op->op.right) goto out_fail; /* * The arg must be num, str, or field */ switch (arg->type) { case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: break; default: show_error(error_str, "Illegal rvalue"); return -1; } /* * Depending on the type, we may need to * convert this to a string or regex. */ switch (arg->value.type) { case FILTER_CHAR: /* * A char should be converted to number if * the string is 1 byte, and the compare * is not a REGEX. */ if (strlen(arg->value.str) == 1 && op->num.type != FILTER_CMP_REGEX && op->num.type != FILTER_CMP_NOT_REGEX) { arg->value.type = FILTER_NUMBER; goto do_int; } /* fall through */ case FILTER_STRING: /* convert op to a string arg */ op_type = op->num.type; left = op->num.left; str = arg->value.str; /* reset the op for the new field */ memset(op, 0, sizeof(*op)); /* * If left arg was a field not found then * NULL the entire op. */ if (left->type == FILTER_ARG_BOOLEAN) { free_arg(left); free_arg(arg); op->type = FILTER_ARG_BOOLEAN; op->boolean.value = FILTER_FALSE; break; } /* Left arg must be a field */ if (left->type != FILTER_ARG_FIELD) { show_error(error_str, "Illegal lvalue for string comparison"); return -1; } /* Make sure this is a valid string compare */ switch (op_type) { case FILTER_CMP_EQ: op_type = FILTER_CMP_MATCH; break; case FILTER_CMP_NE: op_type = FILTER_CMP_NOT_MATCH; break; case FILTER_CMP_REGEX: case FILTER_CMP_NOT_REGEX: ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB); if (ret) { show_error(error_str, "RegEx '%s' did not compute", str); return -1; } break; default: show_error(error_str, "Illegal comparison for string"); return -1; } op->type = FILTER_ARG_STR; op->str.type = op_type; op->str.field = left->field.field; op->str.val = strdup(str); if (!op->str.val) die("malloc string"); /* * Need a buffer to copy data for tests */ op->str.buffer = malloc_or_die(op->str.field->size + 1); /* Null terminate this buffer */ op->str.buffer[op->str.field->size] = 0; /* We no longer have left or right args */ free_arg(arg); free_arg(left); break; case FILTER_NUMBER: do_int: switch (op->num.type) { case FILTER_CMP_REGEX: case FILTER_CMP_NOT_REGEX: show_error(error_str, "Op not allowed with integers"); return -1; default: break; } /* numeric compare */ op->num.right = arg; break; default: goto out_fail; } break; default: goto out_fail; } return 0; out_fail: show_error(error_str, "Syntax error"); return -1; } static struct filter_arg * rotate_op_right(struct filter_arg *a, struct filter_arg *b) { struct filter_arg *arg; arg = a->op.right; a->op.right = b; return arg; } static int add_left(struct filter_arg *op, struct filter_arg *arg) { switch (op->type) { case FILTER_ARG_EXP: if (arg->type == FILTER_ARG_OP) arg = rotate_op_right(arg, op); op->exp.left = arg; break; case FILTER_ARG_OP: op->op.left = arg; break; case FILTER_ARG_NUM: if (arg->type == FILTER_ARG_OP) arg = rotate_op_right(arg, op); /* left arg of compares must be a field */ if (arg->type != FILTER_ARG_FIELD && arg->type != FILTER_ARG_BOOLEAN) return -1; op->num.left = arg; break; default: return -1; } return 0; } enum op_type { OP_NONE, OP_BOOL, OP_NOT, OP_EXP, OP_CMP, }; static enum op_type process_op(const char *token, enum filter_op_type *btype, enum filter_cmp_type *ctype, enum filter_exp_type *etype) { *btype = FILTER_OP_NOT; *etype = FILTER_EXP_NONE; *ctype = FILTER_CMP_NONE; if (strcmp(token, "&&") == 0) *btype = FILTER_OP_AND; else if (strcmp(token, "||") == 0) *btype = FILTER_OP_OR; else if (strcmp(token, "!") == 0) return OP_NOT; if (*btype != FILTER_OP_NOT) return OP_BOOL; /* Check for value expressions */ if (strcmp(token, "+") == 0) { *etype = FILTER_EXP_ADD; } else if (strcmp(token, "-") == 0) { *etype = FILTER_EXP_SUB; } else if (strcmp(token, "*") == 0) { *etype = FILTER_EXP_MUL; } else if (strcmp(token, "/") == 0) { *etype = FILTER_EXP_DIV; } else if (strcmp(token, "%") == 0) { *etype = FILTER_EXP_MOD; } else if (strcmp(token, ">>") == 0) { *etype = FILTER_EXP_RSHIFT; } else if (strcmp(token, "<<") == 0) { *etype = FILTER_EXP_LSHIFT; } else if (strcmp(token, "&") == 0) { *etype = FILTER_EXP_AND; } else if (strcmp(token, "|") == 0) { *etype = FILTER_EXP_OR; } else if (strcmp(token, "^") == 0) { *etype = FILTER_EXP_XOR; } else if (strcmp(token, "~") == 0) *etype = FILTER_EXP_NOT; if (*etype != FILTER_EXP_NONE) return OP_EXP; /* Check for compares */ if (strcmp(token, "==") == 0) *ctype = FILTER_CMP_EQ; else if (strcmp(token, "!=") == 0) *ctype = FILTER_CMP_NE; else if (strcmp(token, "<") == 0) *ctype = FILTER_CMP_LT; else if (strcmp(token, ">") == 0) *ctype = FILTER_CMP_GT; else if (strcmp(token, "<=") == 0) *ctype = FILTER_CMP_LE; else if (strcmp(token, ">=") == 0) *ctype = FILTER_CMP_GE; else if (strcmp(token, "=~") == 0) *ctype = FILTER_CMP_REGEX; else if (strcmp(token, "!~") == 0) *ctype = FILTER_CMP_NOT_REGEX; else return OP_NONE; return OP_CMP; } static int check_op_done(struct filter_arg *arg) { switch (arg->type) { case FILTER_ARG_EXP: return arg->exp.right != NULL; case FILTER_ARG_OP: return arg->op.right != NULL; case FILTER_ARG_NUM: return arg->num.right != NULL; case FILTER_ARG_STR: /* A string conversion is always done */ return 1; case FILTER_ARG_BOOLEAN: /* field not found, is ok */ return 1; default: return 0; } } enum filter_vals { FILTER_VAL_NORM, FILTER_VAL_FALSE, FILTER_VAL_TRUE, }; static void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, struct filter_arg *arg) { struct filter_arg *other_child = NULL; struct filter_arg **ptr = NULL; if (parent->type != FILTER_ARG_OP && arg->type != FILTER_ARG_OP) die("can not reparent other than OP"); /* Get the sibling */ if (old_child->op.right == arg) { ptr = &old_child->op.right; other_child = old_child->op.left; } else if (old_child->op.left == arg) { ptr = &old_child->op.left; other_child = old_child->op.right; } else die("Error in reparent op, find other child"); /* Detach arg from old_child */ *ptr = NULL; /* Check for root */ if (parent == old_child) { free_arg(other_child); *parent = *arg; /* Free arg without recussion */ free(arg); return; } if (parent->op.right == old_child) ptr = &parent->op.right; else if (parent->op.left == old_child) ptr = &parent->op.left; else die("Error in reparent op"); *ptr = arg; free_arg(old_child); } static enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) { enum filter_vals lval, rval; switch (arg->type) { /* bad case */ case FILTER_ARG_BOOLEAN: return FILTER_VAL_FALSE + arg->boolean.value; /* good cases: */ case FILTER_ARG_STR: case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: return FILTER_VAL_NORM; case FILTER_ARG_EXP: lval = test_arg(arg, arg->exp.left); if (lval != FILTER_VAL_NORM) return lval; rval = test_arg(arg, arg->exp.right); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_NUM: lval = test_arg(arg, arg->num.left); if (lval != FILTER_VAL_NORM) return lval; rval = test_arg(arg, arg->num.right); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_OP: if (arg->op.type != FILTER_OP_NOT) { lval = test_arg(arg, arg->op.left); switch (lval) { case FILTER_VAL_NORM: break; case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; rval = test_arg(arg, arg->op.right); if (rval != FILTER_VAL_NORM) return rval; reparent_op_arg(parent, arg, arg->op.right); return FILTER_VAL_NORM; case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) return FILTER_VAL_FALSE; rval = test_arg(arg, arg->op.right); if (rval != FILTER_VAL_NORM) return rval; reparent_op_arg(parent, arg, arg->op.right); return FILTER_VAL_NORM; } } rval = test_arg(arg, arg->op.right); switch (rval) { case FILTER_VAL_NORM: break; case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_FALSE; reparent_op_arg(parent, arg, arg->op.left); return FILTER_VAL_NORM; case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) return FILTER_VAL_FALSE; if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_TRUE; reparent_op_arg(parent, arg, arg->op.left); return FILTER_VAL_NORM; } return FILTER_VAL_NORM; default: die("bad arg in filter tree"); } return FILTER_VAL_NORM; } /* Remove any unknown event fields */ static struct filter_arg *collapse_tree(struct filter_arg *arg) { enum filter_vals ret; ret = test_arg(arg, arg); switch (ret) { case FILTER_VAL_NORM: return arg; case FILTER_VAL_TRUE: case FILTER_VAL_FALSE: free_arg(arg); arg = allocate_arg(); arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = ret == FILTER_VAL_TRUE; } return arg; } static int process_filter(struct event_format *event, struct filter_arg **parg, char **error_str, int not) { enum event_type type; char *token = NULL; struct filter_arg *current_op = NULL; struct filter_arg *current_exp = NULL; struct filter_arg *left_item = NULL; struct filter_arg *arg = NULL; enum op_type op_type; enum filter_op_type btype; enum filter_exp_type etype; enum filter_cmp_type ctype; int ret; *parg = NULL; do { free(token); type = read_token(&token); switch (type) { case EVENT_SQUOTE: case EVENT_DQUOTE: case EVENT_ITEM: arg = create_arg_item(event, token, type, error_str); if (!arg) goto fail; if (!left_item) left_item = arg; else if (current_exp) { ret = add_right(current_exp, arg, error_str); if (ret < 0) goto fail; left_item = NULL; /* Not's only one one expression */ if (not) { arg = NULL; if (current_op) goto fail_print; free(token); *parg = current_exp; return 0; } } else goto fail_print; arg = NULL; break; case EVENT_DELIM: if (*token == ',') { show_error(error_str, "Illegal token ','"); goto fail; } if (*token == '(') { if (left_item) { show_error(error_str, "Open paren can not come after item"); goto fail; } if (current_exp) { show_error(error_str, "Open paren can not come after expression"); goto fail; } ret = process_filter(event, &arg, error_str, 0); if (ret != 1) { if (ret == 0) show_error(error_str, "Unbalanced number of '('"); goto fail; } ret = 0; /* A not wants just one expression */ if (not) { if (current_op) goto fail_print; *parg = arg; return 0; } if (current_op) ret = add_right(current_op, arg, error_str); else current_exp = arg; if (ret < 0) goto fail; } else { /* ')' */ if (!current_op && !current_exp) goto fail_print; /* Make sure everything is finished at this level */ if (current_exp && !check_op_done(current_exp)) goto fail_print; if (current_op && !check_op_done(current_op)) goto fail_print; if (current_op) *parg = current_op; else *parg = current_exp; return 1; } break; case EVENT_OP: op_type = process_op(token, &btype, &ctype, &etype); /* All expect a left arg except for NOT */ switch (op_type) { case OP_BOOL: /* Logic ops need a left expression */ if (!current_exp && !current_op) goto fail_print; /* fall through */ case OP_NOT: /* logic only processes ops and exp */ if (left_item) goto fail_print; break; case OP_EXP: case OP_CMP: if (!left_item) goto fail_print; break; case OP_NONE: show_error(error_str, "Unknown op token %s", token); goto fail; } ret = 0; switch (op_type) { case OP_BOOL: arg = create_arg_op(btype); if (current_op) ret = add_left(arg, current_op); else ret = add_left(arg, current_exp); current_op = arg; current_exp = NULL; break; case OP_NOT: arg = create_arg_op(btype); if (current_op) ret = add_right(current_op, arg, error_str); if (ret < 0) goto fail; current_exp = arg; ret = process_filter(event, &arg, error_str, 1); if (ret < 0) goto fail; ret = add_right(current_exp, arg, error_str); if (ret < 0) goto fail; break; case OP_EXP: case OP_CMP: if (op_type == OP_EXP) arg = create_arg_exp(etype); else arg = create_arg_cmp(ctype); if (current_op) ret = add_right(current_op, arg, error_str); if (ret < 0) goto fail; ret = add_left(arg, left_item); if (ret < 0) { arg = NULL; goto fail_print; } current_exp = arg; break; default: break; } arg = NULL; if (ret < 0) goto fail_print; break; case EVENT_NONE: break; default: goto fail_print; } } while (type != EVENT_NONE); if (!current_op && !current_exp) goto fail_print; if (!current_op) current_op = current_exp; current_op = collapse_tree(current_op); *parg = current_op; return 0; fail_print: show_error(error_str, "Syntax error"); fail: free_arg(current_op); free_arg(current_exp); free_arg(arg); free(token); return -1; } static int process_event(struct event_format *event, const char *filter_str, struct filter_arg **parg, char **error_str) { int ret; pevent_buffer_init(filter_str, strlen(filter_str)); ret = process_filter(event, parg, error_str, 0); if (ret == 1) { show_error(error_str, "Unbalanced number of ')'"); return -1; } if (ret < 0) return ret; /* If parg is NULL, then make it into FALSE */ if (!*parg) { *parg = allocate_arg(); (*parg)->type = FILTER_ARG_BOOLEAN; (*parg)->boolean.value = FILTER_FALSE; } return 0; } static int filter_event(struct event_filter *filter, struct event_format *event, const char *filter_str, char **error_str) { struct filter_type *filter_type; struct filter_arg *arg; int ret; if (filter_str) { ret = process_event(event, filter_str, &arg, error_str); if (ret < 0) return ret; } else { /* just add a TRUE arg */ arg = allocate_arg(); arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_TRUE; } filter_type = add_filter_type(filter, event->id); if (filter_type->filter) free_arg(filter_type->filter); filter_type->filter = arg; return 0; } /** * pevent_filter_add_filter_str - add a new filter * @filter: the event filter to add to * @filter_str: the filter string that contains the filter * @error_str: string containing reason for failed filter * * Returns 0 if the filter was successfully added * -1 if there was an error. * * On error, if @error_str points to a string pointer, * it is set to the reason that the filter failed. * This string must be freed with "free". */ int pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str, char **error_str) { struct pevent *pevent = filter->pevent; struct event_list *event; struct event_list *events = NULL; const char *filter_start; const char *next_event; char *this_event; char *event_name = NULL; char *sys_name = NULL; char *sp; int rtn = 0; int len; int ret; /* clear buffer to reset show error */ pevent_buffer_init("", 0); if (error_str) *error_str = NULL; filter_start = strchr(filter_str, ':'); if (filter_start) len = filter_start - filter_str; else len = strlen(filter_str); do { next_event = strchr(filter_str, ','); if (next_event && (!filter_start || next_event < filter_start)) len = next_event - filter_str; else if (filter_start) len = filter_start - filter_str; else len = strlen(filter_str); this_event = malloc_or_die(len + 1); memcpy(this_event, filter_str, len); this_event[len] = 0; if (next_event) next_event++; filter_str = next_event; sys_name = strtok_r(this_event, "/", &sp); event_name = strtok_r(NULL, "/", &sp); if (!sys_name) { show_error(error_str, "No filter found"); /* This can only happen when events is NULL, but still */ free_events(events); free(this_event); return -1; } /* Find this event */ ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); if (ret < 0) { if (event_name) show_error(error_str, "No event found under '%s.%s'", sys_name, event_name); else show_error(error_str, "No event found under '%s'", sys_name); free_events(events); free(this_event); return -1; } free(this_event); } while (filter_str); /* Skip the ':' */ if (filter_start) filter_start++; /* filter starts here */ for (event = events; event; event = event->next) { ret = filter_event(filter, event->event, filter_start, error_str); /* Failures are returned if a parse error happened */ if (ret < 0) rtn = ret; if (ret >= 0 && pevent->test_filters) { char *test; test = pevent_filter_make_string(filter, event->event->id); printf(" '%s: %s'\n", event->event->name, test); free(test); } } free_events(events); if (rtn >= 0 && pevent->test_filters) exit(0); return rtn; } static void free_filter_type(struct filter_type *filter_type) { free_arg(filter_type->filter); } /** * pevent_filter_remove_event - remove a filter for an event * @filter: the event filter to remove from * @event_id: the event to remove a filter for * * Removes the filter saved for an event defined by @event_id * from the @filter. * * Returns 1: if an event was removed * 0: if the event was not found */ int pevent_filter_remove_event(struct event_filter *filter, int event_id) { struct filter_type *filter_type; unsigned long len; if (!filter->filters) return 0; filter_type = find_filter_type(filter, event_id); if (!filter_type) return 0; free_filter_type(filter_type); /* The filter_type points into the event_filters array */ len = (unsigned long)(filter->event_filters + filter->filters) - (unsigned long)(filter_type + 1); memmove(filter_type, filter_type + 1, len); filter->filters--; memset(&filter->event_filters[filter->filters], 0, sizeof(*filter_type)); return 1; } /** * pevent_filter_reset - clear all filters in a filter * @filter: the event filter to reset * * Removes all filters from a filter and resets it. */ void pevent_filter_reset(struct event_filter *filter) { int i; for (i = 0; i < filter->filters; i++) free_filter_type(&filter->event_filters[i]); free(filter->event_filters); filter->filters = 0; filter->event_filters = NULL; } void pevent_filter_free(struct event_filter *filter) { pevent_unref(filter->pevent); pevent_filter_reset(filter); free(filter); } static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg); static int copy_filter_type(struct event_filter *filter, struct event_filter *source, struct filter_type *filter_type) { struct filter_arg *arg; struct event_format *event; const char *sys; const char *name; char *str; /* Can't assume that the pevent's are the same */ sys = filter_type->event->system; name = filter_type->event->name; event = pevent_find_event_by_name(filter->pevent, sys, name); if (!event) return -1; str = arg_to_str(source, filter_type->filter); if (!str) return -1; if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { /* Add trivial event */ arg = allocate_arg(); arg->type = FILTER_ARG_BOOLEAN; if (strcmp(str, "TRUE") == 0) arg->boolean.value = 1; else arg->boolean.value = 0; filter_type = add_filter_type(filter, event->id); filter_type->filter = arg; free(str); return 0; } filter_event(filter, event, str, NULL); free(str); return 0; } /** * pevent_filter_copy - copy a filter using another filter * @dest - the filter to copy to * @source - the filter to copy from * * Returns 0 on success and -1 if not all filters were copied */ int pevent_filter_copy(struct event_filter *dest, struct event_filter *source) { int ret = 0; int i; pevent_filter_reset(dest); for (i = 0; i < source->filters; i++) { if (copy_filter_type(dest, source, &source->event_filters[i])) ret = -1; } return ret; } /** * pevent_update_trivial - update the trivial filters with the given filter * @dest - the filter to update * @source - the filter as the source of the update * @type - the type of trivial filter to update. * * Scan dest for trivial events matching @type to replace with the source. * * Returns 0 on success and -1 if there was a problem updating, but * events may have still been updated on error. */ int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, enum filter_trivial_type type) { struct pevent *src_pevent; struct pevent *dest_pevent; struct event_format *event; struct filter_type *filter_type; struct filter_arg *arg; char *str; int i; src_pevent = source->pevent; dest_pevent = dest->pevent; /* Do nothing if either of the filters has nothing to filter */ if (!dest->filters || !source->filters) return 0; for (i = 0; i < dest->filters; i++) { filter_type = &dest->event_filters[i]; arg = filter_type->filter; if (arg->type != FILTER_ARG_BOOLEAN) continue; if ((arg->boolean.value && type == FILTER_TRIVIAL_FALSE) || (!arg->boolean.value && type == FILTER_TRIVIAL_TRUE)) continue; event = filter_type->event; if (src_pevent != dest_pevent) { /* do a look up */ event = pevent_find_event_by_name(src_pevent, event->system, event->name); if (!event) return -1; } str = pevent_filter_make_string(source, event->id); if (!str) continue; /* Don't bother if the filter is trivial too */ if (strcmp(str, "TRUE") != 0 && strcmp(str, "FALSE") != 0) filter_event(dest, event, str, NULL); free(str); } return 0; } /** * pevent_filter_clear_trivial - clear TRUE and FALSE filters * @filter: the filter to remove trivial filters from * @type: remove only true, false, or both * * Removes filters that only contain a TRUE or FALES boolean arg. */ void pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type) { struct filter_type *filter_type; int count = 0; int *ids = NULL; int i; if (!filter->filters) return; /* * Two steps, first get all ids with trivial filters. * then remove those ids. */ for (i = 0; i < filter->filters; i++) { filter_type = &filter->event_filters[i]; if (filter_type->filter->type != FILTER_ARG_BOOLEAN) continue; switch (type) { case FILTER_TRIVIAL_FALSE: if (filter_type->filter->boolean.value) continue; case FILTER_TRIVIAL_TRUE: if (!filter_type->filter->boolean.value) continue; default: break; } ids = realloc(ids, sizeof(*ids) * (count + 1)); if (!ids) die("Can't allocate ids"); ids[count++] = filter_type->event_id; } if (!count) return; for (i = 0; i < count; i++) pevent_filter_remove_event(filter, ids[i]); free(ids); } /** * pevent_filter_event_has_trivial - return true event contains trivial filter * @filter: the filter with the information * @event_id: the id of the event to test * @type: trivial type to test for (TRUE, FALSE, EITHER) * * Returns 1 if the event contains a matching trivial type * otherwise 0. */ int pevent_filter_event_has_trivial(struct event_filter *filter, int event_id, enum filter_trivial_type type) { struct filter_type *filter_type; if (!filter->filters) return 0; filter_type = find_filter_type(filter, event_id); if (!filter_type) return 0; if (filter_type->filter->type != FILTER_ARG_BOOLEAN) return 0; switch (type) { case FILTER_TRIVIAL_FALSE: return !filter_type->filter->boolean.value; case FILTER_TRIVIAL_TRUE: return filter_type->filter->boolean.value; default: return 1; } } static int test_filter(struct event_format *event, struct filter_arg *arg, struct pevent_record *record); static const char * get_comm(struct event_format *event, struct pevent_record *record) { const char *comm; int pid; pid = pevent_data_pid(event->pevent, record); comm = pevent_data_comm_from_pid(event->pevent, pid); return comm; } static unsigned long long get_value(struct event_format *event, struct format_field *field, struct pevent_record *record) { unsigned long long val; /* Handle our dummy "comm" field */ if (field == &comm) { const char *name; name = get_comm(event, record); return (unsigned long)name; } pevent_read_number_field(field, record->data, &val); if (!(field->flags & FIELD_IS_SIGNED)) return val; switch (field->size) { case 1: return (char)val; case 2: return (short)val; case 4: return (int)val; case 8: return (long long)val; } return val; } static unsigned long long get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record); static unsigned long long get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { unsigned long long lval, rval; lval = get_arg_value(event, arg->exp.left, record); rval = get_arg_value(event, arg->exp.right, record); switch (arg->exp.type) { case FILTER_EXP_ADD: return lval + rval; case FILTER_EXP_SUB: return lval - rval; case FILTER_EXP_MUL: return lval * rval; case FILTER_EXP_DIV: return lval / rval; case FILTER_EXP_MOD: return lval % rval; case FILTER_EXP_RSHIFT: return lval >> rval; case FILTER_EXP_LSHIFT: return lval << rval; case FILTER_EXP_AND: return lval & rval; case FILTER_EXP_OR: return lval | rval; case FILTER_EXP_XOR: return lval ^ rval; case FILTER_EXP_NOT: default: die("error in exp"); } return 0; } static unsigned long long get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { switch (arg->type) { case FILTER_ARG_FIELD: return get_value(event, arg->field.field, record); case FILTER_ARG_VALUE: if (arg->value.type != FILTER_NUMBER) die("must have number field!"); return arg->value.val; case FILTER_ARG_EXP: return get_exp_value(event, arg, record); default: die("oops in filter"); } return 0; } static int test_num(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { unsigned long long lval, rval; lval = get_arg_value(event, arg->num.left, record); rval = get_arg_value(event, arg->num.right, record); switch (arg->num.type) { case FILTER_CMP_EQ: return lval == rval; case FILTER_CMP_NE: return lval != rval; case FILTER_CMP_GT: return lval > rval; case FILTER_CMP_LT: return lval < rval; case FILTER_CMP_GE: return lval >= rval; case FILTER_CMP_LE: return lval <= rval; default: /* ?? */ return 0; } } static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record) { struct event_format *event; struct pevent *pevent; unsigned long long addr; const char *val = NULL; unsigned int size; char hex[64]; /* If the field is not a string convert it */ if (arg->str.field->flags & FIELD_IS_STRING) { val = record->data + arg->str.field->offset; size = arg->str.field->size; if (arg->str.field->flags & FIELD_IS_DYNAMIC) { addr = *(unsigned int *)val; val = record->data + (addr & 0xffff); size = addr >> 16; } /* * We need to copy the data since we can't be sure the field * is null terminated. */ if (*(val + size - 1)) { /* copy it */ memcpy(arg->str.buffer, val, arg->str.field->size); /* the buffer is already NULL terminated */ val = arg->str.buffer; } } else { event = arg->str.field->event; pevent = event->pevent; addr = get_value(event, arg->str.field, record); if (arg->str.field->flags & (FIELD_IS_POINTER | FIELD_IS_LONG)) /* convert to a kernel symbol */ val = pevent_find_function(pevent, addr); if (val == NULL) { /* just use the hex of the string name */ snprintf(hex, 64, "0x%llx", addr); val = hex; } } return val; } static int test_str(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { const char *val; if (arg->str.field == &comm) val = get_comm(event, record); else val = get_field_str(arg, record); switch (arg->str.type) { case FILTER_CMP_MATCH: return strcmp(val, arg->str.val) == 0; case FILTER_CMP_NOT_MATCH: return strcmp(val, arg->str.val) != 0; case FILTER_CMP_REGEX: /* Returns zero on match */ return !regexec(&arg->str.reg, val, 0, NULL, 0); case FILTER_CMP_NOT_REGEX: return regexec(&arg->str.reg, val, 0, NULL, 0); default: /* ?? */ return 0; } } static int test_op(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { switch (arg->op.type) { case FILTER_OP_AND: return test_filter(event, arg->op.left, record) && test_filter(event, arg->op.right, record); case FILTER_OP_OR: return test_filter(event, arg->op.left, record) || test_filter(event, arg->op.right, record); case FILTER_OP_NOT: return !test_filter(event, arg->op.right, record); default: /* ?? */ return 0; } } static int test_filter(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { switch (arg->type) { case FILTER_ARG_BOOLEAN: /* easy case */ return arg->boolean.value; case FILTER_ARG_OP: return test_op(event, arg, record); case FILTER_ARG_NUM: return test_num(event, arg, record); case FILTER_ARG_STR: return test_str(event, arg, record); case FILTER_ARG_EXP: case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: /* * Expressions, fields and values evaluate * to true if they return non zero */ return !!get_arg_value(event, arg, record); default: die("oops!"); /* ?? */ return 0; } } /** * pevent_event_filtered - return true if event has filter * @filter: filter struct with filter information * @event_id: event id to test if filter exists * * Returns 1 if filter found for @event_id * otherwise 0; */ int pevent_event_filtered(struct event_filter *filter, int event_id) { struct filter_type *filter_type; if (!filter->filters) return 0; filter_type = find_filter_type(filter, event_id); return filter_type ? 1 : 0; } /** * pevent_filter_match - test if a record matches a filter * @filter: filter struct with filter information * @record: the record to test against the filter * * Returns: * 1 - filter found for event and @record matches * 0 - filter found for event and @record does not match * -1 - no filter found for @record's event * -2 - if no filters exist */ int pevent_filter_match(struct event_filter *filter, struct pevent_record *record) { struct pevent *pevent = filter->pevent; struct filter_type *filter_type; int event_id; if (!filter->filters) return FILTER_NONE; event_id = pevent_data_type(pevent, record); filter_type = find_filter_type(filter, event_id); if (!filter_type) return FILTER_NOEXIST; return test_filter(filter_type->event, filter_type->filter, record) ? FILTER_MATCH : FILTER_MISS; } static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *left = NULL; char *right = NULL; char *op = NULL; int left_val = -1; int right_val = -1; int val; int len; switch (arg->op.type) { case FILTER_OP_AND: op = "&&"; /* fall through */ case FILTER_OP_OR: if (!op) op = "||"; left = arg_to_str(filter, arg->op.left); right = arg_to_str(filter, arg->op.right); if (!left || !right) break; /* Try to consolidate boolean values */ if (strcmp(left, "TRUE") == 0) left_val = 1; else if (strcmp(left, "FALSE") == 0) left_val = 0; if (strcmp(right, "TRUE") == 0) right_val = 1; else if (strcmp(right, "FALSE") == 0) right_val = 0; if (left_val >= 0) { if ((arg->op.type == FILTER_OP_AND && !left_val) || (arg->op.type == FILTER_OP_OR && left_val)) { /* Just return left value */ str = left; left = NULL; break; } if (right_val >= 0) { /* just evaluate this. */ val = 0; switch (arg->op.type) { case FILTER_OP_AND: val = left_val && right_val; break; case FILTER_OP_OR: val = left_val || right_val; break; default: break; } str = malloc_or_die(6); if (val) strcpy(str, "TRUE"); else strcpy(str, "FALSE"); break; } } if (right_val >= 0) { if ((arg->op.type == FILTER_OP_AND && !right_val) || (arg->op.type == FILTER_OP_OR && right_val)) { /* Just return right value */ str = right; right = NULL; break; } /* The right value is meaningless */ str = left; left = NULL; break; } len = strlen(left) + strlen(right) + strlen(op) + 10; str = malloc_or_die(len); snprintf(str, len, "(%s) %s (%s)", left, op, right); break; case FILTER_OP_NOT: op = "!"; right = arg_to_str(filter, arg->op.right); if (!right) break; /* See if we can consolidate */ if (strcmp(right, "TRUE") == 0) right_val = 1; else if (strcmp(right, "FALSE") == 0) right_val = 0; if (right_val >= 0) { /* just return the opposite */ str = malloc_or_die(6); if (right_val) strcpy(str, "FALSE"); else strcpy(str, "TRUE"); break; } len = strlen(right) + strlen(op) + 3; str = malloc_or_die(len); snprintf(str, len, "%s(%s)", op, right); break; default: /* ?? */ break; } free(left); free(right); return str; } static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str; str = malloc_or_die(30); snprintf(str, 30, "%lld", arg->value.val); return str; } static char *field_to_str(struct event_filter *filter, struct filter_arg *arg) { return strdup(arg->field.field->name); } static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) { char *lstr; char *rstr; char *op = NULL; char *str = NULL; int len; lstr = arg_to_str(filter, arg->exp.left); rstr = arg_to_str(filter, arg->exp.right); if (!lstr || !rstr) goto out; switch (arg->exp.type) { case FILTER_EXP_ADD: op = "+"; break; case FILTER_EXP_SUB: op = "-"; break; case FILTER_EXP_MUL: op = "*"; break; case FILTER_EXP_DIV: op = "/"; break; case FILTER_EXP_MOD: op = "%"; break; case FILTER_EXP_RSHIFT: op = ">>"; break; case FILTER_EXP_LSHIFT: op = "<<"; break; case FILTER_EXP_AND: op = "&"; break; case FILTER_EXP_OR: op = "|"; break; case FILTER_EXP_XOR: op = "^"; break; default: die("oops in exp"); } len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; str = malloc_or_die(len); snprintf(str, len, "%s %s %s", lstr, op, rstr); out: free(lstr); free(rstr); return str; } static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) { char *lstr; char *rstr; char *str = NULL; char *op = NULL; int len; lstr = arg_to_str(filter, arg->num.left); rstr = arg_to_str(filter, arg->num.right); if (!lstr || !rstr) goto out; switch (arg->num.type) { case FILTER_CMP_EQ: op = "=="; /* fall through */ case FILTER_CMP_NE: if (!op) op = "!="; /* fall through */ case FILTER_CMP_GT: if (!op) op = ">"; /* fall through */ case FILTER_CMP_LT: if (!op) op = "<"; /* fall through */ case FILTER_CMP_GE: if (!op) op = ">="; /* fall through */ case FILTER_CMP_LE: if (!op) op = "<="; len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; str = malloc_or_die(len); sprintf(str, "%s %s %s", lstr, op, rstr); break; default: /* ?? */ break; } out: free(lstr); free(rstr); return str; } static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *op = NULL; int len; switch (arg->str.type) { case FILTER_CMP_MATCH: op = "=="; /* fall through */ case FILTER_CMP_NOT_MATCH: if (!op) op = "!="; /* fall through */ case FILTER_CMP_REGEX: if (!op) op = "=~"; /* fall through */ case FILTER_CMP_NOT_REGEX: if (!op) op = "!~"; len = strlen(arg->str.field->name) + strlen(op) + strlen(arg->str.val) + 6; str = malloc_or_die(len); snprintf(str, len, "%s %s \"%s\"", arg->str.field->name, op, arg->str.val); break; default: /* ?? */ break; } return str; } static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str; switch (arg->type) { case FILTER_ARG_BOOLEAN: str = malloc_or_die(6); if (arg->boolean.value) strcpy(str, "TRUE"); else strcpy(str, "FALSE"); return str; case FILTER_ARG_OP: return op_to_str(filter, arg); case FILTER_ARG_NUM: return num_to_str(filter, arg); case FILTER_ARG_STR: return str_to_str(filter, arg); case FILTER_ARG_VALUE: return val_to_str(filter, arg); case FILTER_ARG_FIELD: return field_to_str(filter, arg); case FILTER_ARG_EXP: return exp_to_str(filter, arg); default: /* ?? */ return NULL; } } /** * pevent_filter_make_string - return a string showing the filter * @filter: filter struct with filter information * @event_id: the event id to return the filter string with * * Returns a string that displays the filter contents. * This string must be freed with free(str). * NULL is returned if no filter is found. */ char * pevent_filter_make_string(struct event_filter *filter, int event_id) { struct filter_type *filter_type; if (!filter->filters) return NULL; filter_type = find_filter_type(filter, event_id); if (!filter_type) return NULL; return arg_to_str(filter, filter_type->filter); } /** * pevent_filter_compare - compare two filters and return if they are the same * @filter1: Filter to compare with @filter2 * @filter2: Filter to compare with @filter1 * * Returns: * 1 if the two filters hold the same content. * 0 if they do not. */ int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2) { struct filter_type *filter_type1; struct filter_type *filter_type2; char *str1, *str2; int result; int i; /* Do the easy checks first */ if (filter1->filters != filter2->filters) return 0; if (!filter1->filters && !filter2->filters) return 1; /* * Now take a look at each of the events to see if they have the same * filters to them. */ for (i = 0; i < filter1->filters; i++) { filter_type1 = &filter1->event_filters[i]; filter_type2 = find_filter_type(filter2, filter_type1->event_id); if (!filter_type2) break; if (filter_type1->filter->type != filter_type2->filter->type) break; switch (filter_type1->filter->type) { case FILTER_TRIVIAL_FALSE: case FILTER_TRIVIAL_TRUE: /* trivial types just need the type compared */ continue; default: break; } /* The best way to compare complex filters is with strings */ str1 = arg_to_str(filter1, filter_type1->filter); str2 = arg_to_str(filter2, filter_type2->filter); if (str1 && str2) result = strcmp(str1, str2) != 0; else /* bail out if allocation fails */ result = 1; free(str1); free(str2); if (result) break; } if (i < filter1->filters) return 0; return 1; } 07070100000021000081A4000000000000000000000001611B9790000005DD000000000000000000000000000000000000003600000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/parse-utils.c#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <errno.h> #include "event-utils.h" #define __weak __attribute__((weak)) void __vdie(const char *fmt, va_list ap) { int ret = errno; if (errno) perror("trace-cmd"); else ret = -1; fprintf(stderr, " "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); exit(ret); } void __die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vdie(fmt, ap); va_end(ap); } void __weak die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vdie(fmt, ap); va_end(ap); } void __vwarning(const char *fmt, va_list ap) { if (errno) perror("trace-cmd"); errno = 0; fprintf(stderr, " "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } void __warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vwarning(fmt, ap); va_end(ap); } void __weak warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vwarning(fmt, ap); va_end(ap); } void __vpr_stat(const char *fmt, va_list ap) { vprintf(fmt, ap); printf("\n"); } void __pr_stat(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vpr_stat(fmt, ap); va_end(ap); } void __weak vpr_stat(const char *fmt, va_list ap) { __vpr_stat(fmt, ap); } void __weak pr_stat(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vpr_stat(fmt, ap); va_end(ap); } void __weak *malloc_or_die(unsigned int size) { void *data; data = malloc(size); if (!data) die("malloc"); return data; } 07070100000022000081A4000000000000000000000001611B979000001296000000000000000000000000000000000000003400000000rasdaemon-0.6.7.18.git+7ccf12f/libtrace/trace-seq.c/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include "event-parse.h" #include "event-utils.h" /* * The TRACE_SEQ_POISON is to catch the use of using * a trace_seq structure after it was destroyed. */ #define TRACE_SEQ_POISON ((void *)0xdeadbeef) #define TRACE_SEQ_CHECK(s) \ do { \ if ((s)->buffer == TRACE_SEQ_POISON) \ die("Usage of trace_seq after it was destroyed"); \ } while (0) /** * trace_seq_init - initialize the trace_seq structure * @s: a pointer to the trace_seq structure to initialize */ void trace_seq_init(struct trace_seq *s) { s->len = 0; s->readpos = 0; s->buffer_size = TRACE_SEQ_BUF_SIZE; s->buffer = malloc_or_die(s->buffer_size); } /** * trace_seq_destroy - free up memory of a trace_seq * @s: a pointer to the trace_seq to free the buffer * * Only frees the buffer, not the trace_seq struct itself. */ void trace_seq_destroy(struct trace_seq *s) { if (!s) return; TRACE_SEQ_CHECK(s); free(s->buffer); s->buffer = TRACE_SEQ_POISON; } static void expand_buffer(struct trace_seq *s) { s->buffer_size += TRACE_SEQ_BUF_SIZE; s->buffer = realloc(s->buffer, s->buffer_size); if (!s->buffer) die("Can't allocate trace_seq buffer memory"); } /** * trace_seq_printf - sequence printing of trace information * @s: trace sequence descriptor * @fmt: printf format string * * It returns 0 if the trace oversizes the buffer's free * space, 1 otherwise. * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. */ int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { va_list ap; int len; int ret; TRACE_SEQ_CHECK(s); try_again: len = (s->buffer_size - 1) - s->len; va_start(ap, fmt); ret = vsnprintf(s->buffer + s->len, len, fmt, ap); va_end(ap); if (ret >= len) { expand_buffer(s); goto try_again; } s->len += ret; return 1; } /** * trace_seq_vprintf - sequence printing of trace information * @s: trace sequence descriptor * @fmt: printf format string * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. */ int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) { int len; int ret; TRACE_SEQ_CHECK(s); try_again: len = (s->buffer_size - 1) - s->len; ret = vsnprintf(s->buffer + s->len, len, fmt, args); if (ret >= len) { expand_buffer(s); goto try_again; } s->len += ret; return len; } /** * trace_seq_puts - trace sequence printing of simple string * @s: trace sequence descriptor * @str: simple string to record * * The tracer may use either the sequence operations or its own * copy to user routines. This function records a simple string * into a special buffer (@s) for later retrieval by a sequencer * or other mechanism. */ int trace_seq_puts(struct trace_seq *s, const char *str) { int len; TRACE_SEQ_CHECK(s); len = strlen(str); while (len > ((s->buffer_size - 1) - s->len)) expand_buffer(s); memcpy(s->buffer + s->len, str, len); s->len += len; return len; } int trace_seq_putc(struct trace_seq *s, unsigned char c) { TRACE_SEQ_CHECK(s); while (s->len >= (s->buffer_size - 1)) expand_buffer(s); s->buffer[s->len++] = c; return 1; } void trace_seq_terminate(struct trace_seq *s) { TRACE_SEQ_CHECK(s); /* There's always one character left on the buffer */ s->buffer[s->len] = 0; } int trace_seq_do_printf(struct trace_seq *s) { TRACE_SEQ_CHECK(s); return printf("%.*s", s->len, s->buffer); } 07070100000023000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002200000000rasdaemon-0.6.7.18.git+7ccf12f/m407070100000024000081A4000000000000000000000001611B979000000057000000000000000000000000000000000000002D00000000rasdaemon-0.6.7.18.git+7ccf12f/m4/.gitignore# Autoreconf adds those libtool.m4 lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 07070100000025000081A4000000000000000000000001611B9790000004A2000000000000000000000000000000000000003300000000rasdaemon-0.6.7.18.git+7ccf12f/m4/ac_define_dir.m4dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) dnl dnl This macro sets VARNAME to the expansion of the DIR variable, dnl taking care of fixing up ${prefix} and such. dnl dnl VARNAME is then offered as both an output variable and a C dnl preprocessor symbol. dnl dnl Example: dnl dnl AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.]) dnl dnl @category Misc dnl @author Stepan Kasal <kasal@ucw.cz> dnl @author Andreas Schwab <schwab@suse.de> dnl @author Guido U. Draheim <guidod@gmx.de> dnl @author Alexandre Oliva dnl @version 2006-10-13 dnl @license AllPermissive AC_DEFUN([AC_DEFINE_DIR], [ prefix_NONE= exec_prefix_NONE= test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn dnl refers to ${prefix}. Thus we have to use `eval' twice. eval ac_define_dir="\"[$]$2\"" eval ac_define_dir="\"$ac_define_dir\"" AC_SUBST($1, "$ac_define_dir") AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) test "$prefix_NONE" && prefix=NONE test "$exec_prefix_NONE" && exec_prefix=NONE ]) 07070100000026000081A4000000000000000000000001611B979000000DB5000000000000000000000000000000000000002F00000000rasdaemon-0.6.7.18.git+7ccf12f/m4/x_ac_meta.m4##***************************************************************************** ## $Id: x_ac_meta.m4 416 2005-07-20 17:50:27Z dun $ ##***************************************************************************** # AUTHOR: # Chris Dunlap <cdunlap@llnl.gov> # # SYNOPSIS: # X_AC_META # # DESCRIPTION: # Read metadata from the META file. ##***************************************************************************** AC_DEFUN([X_AC_META], [ AC_MSG_CHECKING([metadata]) META="$srcdir/META" _x_ac_meta_got_file=no if test -f "$META"; then _x_ac_meta_got_file=yes META_NAME=_X_AC_META_GETVAL([(?:NAME|PROJECT|PACKAGE)]); if test -n "$META_NAME"; then AC_DEFINE_UNQUOTED([META_NAME], ["$META_NAME"], [Define the project name.] ) AC_SUBST([META_NAME]) fi META_VERSION=_X_AC_META_GETVAL([VERSION]); if test -n "$META_VERSION"; then AC_DEFINE_UNQUOTED([META_VERSION], ["$META_VERSION"], [Define the project version.] ) AC_SUBST([META_VERSION]) fi META_RELEASE=_X_AC_META_GETVAL([RELEASE]); if test -n "$META_RELEASE"; then AC_DEFINE_UNQUOTED([META_RELEASE], ["$META_RELEASE"], [Define the project release.] ) AC_SUBST([META_RELEASE]) fi if test -n "$META_NAME" -a -n "$META_VERSION"; then META_ALIAS="$META_NAME-$META_VERSION" test -n "$META_RELEASE" && META_ALIAS="$META_ALIAS-$META_RELEASE" AC_DEFINE_UNQUOTED([META_ALIAS], ["$META_ALIAS"], [Define the project alias string (name-ver or name-ver-rel).] ) AC_SUBST([META_ALIAS]) fi META_DATE=_X_AC_META_GETVAL([DATE]); if test -n "$META_DATE"; then AC_DEFINE_UNQUOTED([META_DATE], ["$META_DATE"], [Define the project release date.] ) AC_SUBST([META_DATE]) fi META_AUTHOR=_X_AC_META_GETVAL([AUTHOR]); if test -n "$META_AUTHOR"; then AC_DEFINE_UNQUOTED([META_AUTHOR], ["$META_AUTHOR"], [Define the project author.] ) AC_SUBST([META_AUTHOR]) fi m4_pattern_allow([^LT_(CURRENT|REVISION|AGE)$]) META_LT_CURRENT=_X_AC_META_GETVAL([LT_CURRENT]); META_LT_REVISION=_X_AC_META_GETVAL([LT_REVISION]); META_LT_AGE=_X_AC_META_GETVAL([LT_AGE]); if test -n "$META_LT_CURRENT" \ -o -n "$META_LT_REVISION" \ -o -n "$META_LT_AGE"; then test -n "$META_LT_CURRENT" || META_LT_CURRENT="0" test -n "$META_LT_REVISION" || META_LT_REVISION="0" test -n "$META_LT_AGE" || META_LT_AGE="0" AC_DEFINE_UNQUOTED([META_LT_CURRENT], ["$META_LT_CURRENT"], [Define the libtool library 'current' version information.] ) AC_DEFINE_UNQUOTED([META_LT_REVISION], ["$META_LT_REVISION"], [Define the libtool library 'revision' version information.] ) AC_DEFINE_UNQUOTED([META_LT_AGE], ["$META_LT_AGE"], [Define the libtool library 'age' version information.] ) AC_SUBST([META_LT_CURRENT]) AC_SUBST([META_LT_REVISION]) AC_SUBST([META_LT_AGE]) fi fi AC_MSG_RESULT([$_x_ac_meta_got_file]) ] ) AC_DEFUN([_X_AC_META_GETVAL], [`perl -n\ -e "BEGIN { \\$key=shift @ARGV; }"\ -e "next unless s/^\s*\\$key@<:@:=@:>@//i;"\ -e "s/^((?:@<:@^'\"#@:>@*(?:(@<:@'\"@:>@)@<:@^\2@:>@*\2)*)*)#.*/\\@S|@1/;"\ -e "s/^\s+//;"\ -e "s/\s+$//;"\ -e "s/^(@<:@'\"@:>@)(.*)\1/\\@S|@2/;"\ -e "\\$val=\\$_;"\ -e "END { print \\$val if defined \\$val; }"\ '$1' $META`]dnl ) 07070100000027000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002300000000rasdaemon-0.6.7.18.git+7ccf12f/man07070100000028000081A4000000000000000000000001611B979000000019000000000000000000000000000000000000002E00000000rasdaemon-0.6.7.18.git+7ccf12f/man/.gitignorerasdaemon.1 ras-mc-ctl.8 07070100000029000081A4000000000000000000000001611B979000000025000000000000000000000000000000000000002F00000000rasdaemon-0.6.7.18.git+7ccf12f/man/Makefile.amman_MANS = ras-mc-ctl.8 rasdaemon.1 0707010000002A000081A4000000000000000000000001611B9790000011C4000000000000000000000000000000000000003300000000rasdaemon-0.6.7.18.git+7ccf12f/man/ras-mc-ctl.8.in.\"**************************************************************************** .\" $Id$ .\"**************************************************************************** .\"Copyright (c) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> .\"This tool is a modification of the edac-ctl, written as part of the .\"edac-utils: .\" Copyright (C) 2006-2007 The Regents of the University of California. .\" Produced at Lawrence Livermore National Laboratory. .\" Written by Mark Grondona <mgrondona@llnl.gov> .\" UCRL-CODE-230739. .\" .\" This 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 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. .\"**************************************************************************** .TH RAS-MC-CTL 8 "@META_DATE@" "@META_ALIAS@" "RAS memory controller admin utility" .SH NAME ras-mc-ctl \- RAS memory controller admin utility .SH SYNOPSIS .B ras-mc-ctl [\fIOPTION\fR]... .SH DESCRIPTION The \fBras-mc-ctl\fR program is a \fBperl\fR(1) script which performs some useful RAS administration tasks on EDAC (Error Detection and Correction) drivers. .SH OPTIONS .TP .BI "--help" Display a brief usage message. .TP .BI "--mainboard" Print mainboard vendor and model for this hardware, if available. The method used by \fBras-mc-ctl\fR to obtain the mainboard vendor and model information for the current system is described below in the \fIMAINBOARD CONFIGURATION\fR section. .TP .BI "--status" Print the status of EDAC drivers (loaded or unloaded). .TP .BI "--quiet" Be less verbose when executing an operation. .TP .BI "--register-labels" Register motherboard DIMM labels into EDAC driver sysfs files. This option uses the detected mainboard manufacturer and model number in combination with a "labels database" found in any of the files under @sysconfdir@/ras/dimm_labels.d/* or in the labels.db file at @sysconfdir@/ras/dimm_labels.db. An entry for the current hardware must exist in the labels database for this option to do anything. .TP .BI "--print-labels" Display the configured labels for the current hardware, as well as the current labels registered with EDAC. .TP .BI "--guess-labels" Print DMI labels, when bank locator is available in the DMI table. It helps to fill the labels database at @sysconfdir@/ras/dimm_labels.d/. .TP .BI "--labeldb="DB Specify an alternate location for the labels database. .TP .BI "--delay="time Specify a delay of \fBtime\fR seconds before registering DIMM labels. Only meaninful if used together with --register-labels. .TP .BI "--layout Prints the memory layout as detected by the EDAC driver. Useful to check if the EDAC driver is properly detecting the memory controller architecture. .SH MAINBOARD CONFIGURATION .PP The \fBras-mc-ctl\fR script uses the following method to determine the current system's mainboard vendor and model information: .IP "1." 4 If the config file @sysconfdir@/edac/mainboard exists, then it is parsed by \fBras-mc-ctl\fR. The mainboard config file has the following simple syntax: .nf vendor = <mainboard vendor string> model = <mainboard model string> script = <script to gather mainboard information> .fi Where anything after a '#' character on a line is considered a comment. If the keyword \fBscript\fR is specified, then that script or executable is run by \fBras-mc-ctl\fR to gather the mainboard vendor and model information. The script should write the resulting information on stdout in the same format as the mainboard config file. .IP "2." If no mainboard config file exists, then \fBras-mc-ctl\fR will attempt to read DMI information from the sysfs files .nf /sys/class/dmi/id/board_vendor /sys/class/dmi/id/board_name .fi .IP "3." If the sysfs files above do not exist, then \fBras-mc-ctl\fR will fall back to parsing output of the \fBdmidecode\fR(8) utility. Use of this utility will most often require that \fBras-mc-ctl\fR be run as root. .SH SEE ALSO \fBrasdaemon\fR(1) 0707010000002B000081A4000000000000000000000001611B9790000009D1000000000000000000000000000000000000003200000000rasdaemon-0.6.7.18.git+7ccf12f/man/rasdaemon.1.in.\"**************************************************************************** .\" $Id$ .\"**************************************************************************** .\"Copyright (c) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> .\" .\" This 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 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. .\"**************************************************************************** .TH RASDAEMON 8 "@META_DATE@" "@META_ALIAS@" "RAS memory controller admin utility" .SH NAME rasdaemon \- RAS daemon to log the RAS events. .SH SYNOPSIS .B rasdaemon [\fIOPTION\fR]... .SH DESCRIPTION The \fBrasdaemon\fR program is a daemon which monitors the platform Reliablity, Availability and Serviceability (RAS) reports from the Linux kernel trace events. These trace events are logged in /sys/kernel/debug/tracing, reporting them via syslog/journald. .SH OPTIONS .TP .BI "--usage" Display a brief usage message and exit. .TP .BI "--help" Display a help message and exit. .TP .BI "--disable" Disable RAS tracing events and exit. .TP .BI "--enable" Enable RAS tracing events and exit. .TP .BI "--foreground" Executes in foreground, printing the events at console. Useful for testing it, and to be used by systemd or Unix System V respan. If not specified, the program runs in daemon mode. .TP .BI "--record" Record RAS events via Sqlite3. The Sqlite3 database has the benefit of keeping a persistent record of the RAS events. This feature is used with the ras-mc-ctl utility. Note that rasdaemon may be compiled without this feature. .TP .BI "--version" Print the program version and exit. .SH CONFIG FILE The \fBrasdaemon\fR program supports a config file to set rasdaemon systemd service environment variables. By default the config file is read from /etc/sysconfig/rasdaemon. The general format is environmentname=value. .SH SEE ALSO \fBras-mc-ctl\fR(8) 0707010000002C000081A4000000000000000000000001611B97900000206F000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-amd-k8.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * The code below were adapted from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdio.h> #include <string.h> #include "ras-mce-handler.h" #include "bitfield.h" #define K8_MCE_THRESHOLD_BASE (MCE_EXTENDED_BANK + 1) /* MCE_AMD */ #define K8_MCE_THRESHOLD_TOP (K8_MCE_THRESHOLD_BASE + 6 * 9) #define K8_MCELOG_THRESHOLD_DRAM_ECC (4 * 9 + 0) #define K8_MCELOG_THRESHOLD_LINK (4 * 9 + 1) #define K8_MCELOG_THRESHOLD_L3_CACHE (4 * 9 + 2) #define K8_MCELOG_THRESHOLD_FBDIMM (4 * 9 + 3) static const char *k8bank[] = { "data cache", "instruction cache", "bus unit", "load/store unit", "northbridge", "fixed-issue reoder" }; static const char *k8threshold[] = { [0 ... K8_MCELOG_THRESHOLD_DRAM_ECC - 1] = "Unknow threshold counter", [K8_MCELOG_THRESHOLD_DRAM_ECC] = "MC4_MISC0 DRAM threshold", [K8_MCELOG_THRESHOLD_LINK] = "MC4_MISC1 Link threshold", [K8_MCELOG_THRESHOLD_L3_CACHE] = "MC4_MISC2 L3 Cache threshold", [K8_MCELOG_THRESHOLD_FBDIMM] = "MC4_MISC3 FBDIMM threshold", [K8_MCELOG_THRESHOLD_FBDIMM + 1 ... K8_MCE_THRESHOLD_TOP - K8_MCE_THRESHOLD_BASE - 1] = "Unknown threshold counter", }; static const char *transaction[] = { "instruction", "data", "generic", "reserved" }; static const char *cachelevel[] = { "0", "1", "2", "generic" }; static const char *memtrans[] = { "generic error", "generic read", "generic write", "data read", "data write", "instruction fetch", "prefetch", "evict", "snoop", "?", "?", "?", "?", "?", "?", "?" }; static const char *partproc[] = { "local node origin", "local node response", "local node observed", "generic participation" }; static const char *timeout[] = { "request didn't time out", "request timed out" }; static const char *memoryio[] = { "memory", "res.", "i/o", "generic" }; static const char *nbextendederr[] = { "RAM ECC error", "CRC error", "Sync error", "Master abort", "Target abort", "GART error", "RMW error", "Watchdog error", "RAM Chipkill ECC error", "DEV Error", "Link Data Error", "Link Protocol Error", "NB Array Error", "DRAM Parity Error", "Link Retry", "Tablew Walk Data Error", "L3 Cache Data Error", "L3 Cache Tag Error", "L3 Cache LRU Error" }; static const char *highbits[32] = { [31] = "valid", [30] = "error overflow (multiple errors)", [29] = "error uncorrected", [28] = "error enable", [27] = "misc error valid", [26] = "error address valid", [25] = "processor context corrupt", [24] = "res24", [23] = "res23", /* 22-15 ecc syndrome bits */ [14] = "corrected ecc error", [13] = "uncorrected ecc error", [12] = "res12", [11] = "L3 subcache in error bit 1", [10] = "L3 subcache in error bit 0", [9] = "sublink or DRAM channel", [8] = "error found by scrub", /* 7-4 ht link number of error */ [3] = "err cpu3", [2] = "err cpu2", [1] = "err cpu1", [0] = "err cpu0", }; #define IGNORE_HIGHBITS ((1 << 31) || (1 << 28) || (1 << 26)) static void decode_k8_generic_errcode(struct mce_event *e) { char tmp_buf[4092]; unsigned short errcode = e->status & 0xffff; int n; /* Translate the highest bits */ n = bitfield_msg(tmp_buf, sizeof(tmp_buf), highbits, 32, IGNORE_HIGHBITS, 32, e->status); if (n) mce_snprintf(e->error_msg, "(%s) ", tmp_buf); if ((errcode & 0xfff0) == 0x0010) mce_snprintf(e->error_msg, "LB error '%s transaction, level %s'", transaction[(errcode >> 2) & 3], cachelevel[errcode & 3]); else if ((errcode & 0xff00) == 0x0100) mce_snprintf(e->error_msg, "memory/cache error '%s mem transaction, %s transaction, level %s'", memtrans[(errcode >> 4) & 0xf], transaction[(errcode >> 2) & 3], cachelevel[errcode & 3]); else if ((errcode & 0xf800) == 0x0800) mce_snprintf(e->error_msg, "bus error '%s, %s: %s mem transaction, %s access, level %s'", partproc[(errcode >> 9) & 0x3], timeout[(errcode >> 8) & 1], memtrans[(errcode >> 4) & 0xf], memoryio[(errcode >> 2) & 0x3], cachelevel[(errcode & 0x3)]); } static void decode_k8_dc_mc(struct mce_event *e) { unsigned short exterrcode = (e->status >> 16) & 0x0f; unsigned short errcode = e->status & 0xffff; if (e->status & (3ULL << 45)) { mce_snprintf(e->error_msg, "Data cache ECC error (syndrome %x)", (uint32_t) (e->status >> 47) & 0xff); if (e->status & (1ULL << 40)) mce_snprintf(e->error_msg, "found by scrubber"); } if ((errcode & 0xfff0) == 0x0010) mce_snprintf(e->error_msg, "TLB parity error in %s array", (exterrcode == 0) ? "physical" : "virtual"); } static void decode_k8_ic_mc(struct mce_event *e) { unsigned short exterrcode = (e->status >> 16) & 0x0f; unsigned short errcode = e->status & 0xffff; if (e->status & (3ULL << 45)) mce_snprintf(e->error_msg, "Instruction cache ECC error"); if ((errcode & 0xfff0) == 0x0010) mce_snprintf(e->error_msg, "TLB parity error in %s array", (exterrcode == 0) ? "physical" : "virtual"); } static void decode_k8_bu_mc(struct mce_event *e) { unsigned short exterrcode = (e->status >> 16) & 0x0f; if (e->status & (3ULL << 45)) mce_snprintf(e->error_msg, "L2 cache ECC error"); mce_snprintf(e->error_msg, "%s array error", !exterrcode ? "Bus or cache" : "Cache tag"); } static void decode_k8_nb_mc(struct mce_event *e, unsigned *memerr) { unsigned short exterrcode = (e->status >> 16) & 0x0f; mce_snprintf(e->error_msg, "Northbridge %s", nbextendederr[exterrcode]); switch (exterrcode) { case 0: *memerr = 1; mce_snprintf(e->error_msg, "ECC syndrome = %x", (uint32_t) (e->status >> 47) & 0xff); break; case 8: *memerr = 1; mce_snprintf(e->error_msg, "Chipkill ECC syndrome = %x", (uint32_t) ((((e->status >> 24) & 0xff) << 8) | ((e->status >> 47) & 0xff))); break; case 1: case 2: case 3: case 4: case 6: mce_snprintf(e->error_msg, "link number = %x", (uint32_t) (e->status >> 36) & 0xf); break; } } static void decode_k8_threashold(struct mce_event *e) { if (e->misc & MCI_THRESHOLD_OVER) mce_snprintf(e->error_msg, "Threshold error count overflow"); } static void bank_name(struct mce_event *e) { const char *s; if (e->bank < ARRAY_SIZE(k8bank)) s = k8bank[e->bank]; else if (e->bank >= K8_MCE_THRESHOLD_BASE && e->bank < K8_MCE_THRESHOLD_TOP) s = k8threshold[e->bank - K8_MCE_THRESHOLD_BASE]; else return; /* Use the generic parser for bank */ mce_snprintf(e->bank_name, "%s (bank=%d)", s, e->bank); } int parse_amd_k8_event(struct ras_events *ras, struct mce_event *e) { unsigned ismemerr = 0; /* Don't handle GART errors */ if (e->bank == 4) { unsigned short exterrcode = (e->status >> 16) & 0x0f; if (exterrcode == 5 && (e->status & (1ULL << 61))) { return -1; } } bank_name(e); switch (e->bank) { case 0: decode_k8_dc_mc(e); decode_k8_generic_errcode(e); break; case 1: decode_k8_ic_mc(e); decode_k8_generic_errcode(e); break; case 2: decode_k8_bu_mc(e); decode_k8_generic_errcode(e); break; case 3: /* LS */ decode_k8_generic_errcode(e); break; case 4: decode_k8_nb_mc(e, &ismemerr); decode_k8_generic_errcode(e); break; case 5: /* FR */ decode_k8_generic_errcode(e); break; case K8_MCE_THRESHOLD_BASE ... K8_MCE_THRESHOLD_TOP: decode_k8_threashold(e); break; default: strcpy(e->error_msg, "Don't know how to decode this bank"); } /* IP doesn't matter on memory errors */ if (ismemerr) e->ip = 0; return 0; } 0707010000002D000081A4000000000000000000000001611B979000005264000000000000000000000000000000000000002E00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-amd-smca.c/* * Copyright (c) 2018, AMD, Inc. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include <stdio.h> #include <string.h> #include "ras-mce-handler.h" #include "bitfield.h" /* MCA_STATUS REGISTER FOR FAMILY 17H *********************** Higher 32-bits ***************************** * 63: VALIDERROR, 62: OVERFLOW, 61: UC, 60: Err ENABLE, * 59: Misc Valid, 58: Addr Valid, 57: PCC, 56: ErrCoreID Valid, * 55: TCC, 54: RES, 53: Syndrom Valid, 52: Transparanet, * 51: RES, 50: RES, 49: RES, 48: RES, * 47: RES, 46: CECC, 45: UECC, 44: Deferred, * 43: Poison, 42: RES, 41: RES, 40: RES, * 39: RES, 38: RES, 37: ErrCoreID[5], 36: ErrCoreID[4], * 35: ErrCoreID[3], 34: ErrCoreID[2] 33: ErrCoreID[1] 32: ErrCoreID[0] *********************** Lower 32-bits ****************************** * 31: RES, 30: RES, 29: RES, 28: RES, * 27: RES, 26: RES, 25: RES, 24: RES * 23: RES, 22: RES, 21: XEC[5], 20: XEC[4], * 19: XEC[3], 18: XEC[2], 17: XEC[1], 16: XEC[0] * 15: EC[15], 14: EC[14], 13: EC[13], 12: EC[12], * 11: EC[11], 10: EC[10], 09: EC[9], 08: EC[8], * 07: EC[7], 06: EC[6], 05: EC[5], 04: EC[4], * 03: EC[3], 02: EC[2], 01: EC[1], 00: EC[0] */ /* MCA_STATUS REGISTER FOR FAMILY 19H * The bits 24 ~ 29 contains AddressLsb * 29: ADDRLS[5], 28: ADDRLS[4], 27: ADDRLS[3], * 26: ADDRLS[2], 25: ADDRLS[1], 24: ADDRLS[0] */ /* These may be used by multiple smca_hwid_mcatypes */ enum smca_bank_types { SMCA_LS = 0, /* Load Store */ SMCA_LS_V2, SMCA_IF, /* Instruction Fetch */ SMCA_L2_CACHE, /* L2 Cache */ SMCA_DE, /* Decoder Unit */ SMCA_RESERVED, /* Reserved */ SMCA_EX, /* Execution Unit */ SMCA_FP, /* Floating Point */ SMCA_L3_CACHE, /* L3 Cache */ SMCA_CS, /* Coherent Slave */ SMCA_CS_V2, SMCA_PIE, /* Power, Interrupts, etc. */ SMCA_UMC, /* Unified Memory Controller */ SMCA_UMC_V2, SMCA_PB, /* Parameter Block */ SMCA_PSP, /* Platform Security Processor */ SMCA_PSP_V2, SMCA_SMU, /* System Management Unit */ SMCA_SMU_V2, SMCA_MP5, /* Microprocessor 5 Unit */ SMCA_NBIO, /* Northbridge IO Unit */ SMCA_PCIE, /* PCI Express Unit */ SMCA_PCIE_V2, SMCA_XGMI_PCS, /* xGMI PCS Unit */ SMCA_XGMI_PHY, /* xGMI PHY Unit */ SMCA_WAFL_PHY, /* WAFL PHY Unit */ N_SMCA_BANK_TYPES }; /* Maximum number of MCA banks per CPU. */ #define MAX_NR_BANKS 64 /* * On Newer heterogeneous systems from AMD with CPU and GPU nodes connected * via xGMI links, the NON CPU Nodes are enumerated from index 8 */ #define NONCPU_NODE_INDEX 8 /* SMCA Extended error strings */ /* Load Store */ static const char * const smca_ls_mce_desc[] = { "Load queue parity", "Store queue parity", "Miss address buffer payload parity", "L1 TLB parity", "Reserved", "DC tag error type 6", "DC tag error type 1", "Internal error type 1", "Internal error type 2", "Sys Read data error thread 0", "Sys read data error thread 1", "DC tag error type 2", "DC data error type 1 (poison consumption)", "DC data error type 2", "DC data error type 3", "DC tag error type 4", "L2 TLB parity", "PDC parity error", "DC tag error type 3", "DC tag error type 5", "L2 fill data error", }; static const char * const smca_ls2_mce_desc[] = { "An ECC error was detected on a data cache read by a probe or victimization", "An ECC error or L2 poison was detected on a data cache read by a load", "An ECC error was detected on a data cache read-modify-write by a store", "An ECC error or poison bit mismatch was detected on a tag read by a probe or victimization", "An ECC error or poison bit mismatch was detected on a tag read by a load", "An ECC error or poison bit mismatch was detected on a tag read by a store", "An ECC error was detected on an EMEM read by a load", "An ECC error was detected on an EMEM read-modify-write by a store", "A parity error was detected in an L1 TLB entry by any access", "A parity error was detected in an L2 TLB entry by any access", "A parity error was detected in a PWC entry by any access", "A parity error was detected in an STQ entry by any access", "A parity error was detected in an LDQ entry by any access", "A parity error was detected in a MAB entry by any access", "A parity error was detected in an SCB entry state field by any access", "A parity error was detected in an SCB entry address field by any access", "A parity error was detected in an SCB entry data field by any access", "A parity error was detected in a WCB entry by any access", "A poisoned line was detected in an SCB entry by any access", "A SystemReadDataError error was reported on read data returned from L2 for a load", "A SystemReadDataError error was reported on read data returned from L2 for an SCB store", "A SystemReadDataError error was reported on read data returned from L2 for a WCB store", "A hardware assertion error was reported", "A parity error was detected in an STLF, SCB EMEM entry or SRB store data by any access", }; /* Instruction Fetch */ static const char * const smca_if_mce_desc[] = { "microtag probe port parity error", "IC microtag or full tag multi-hit error", "IC full tag parity", "IC data array parity", "Decoupling queue phys addr parity error", "L0 ITLB parity error", "L1 ITLB parity error", "L2 ITLB parity error", "BPQ snoop parity on Thread 0", "BPQ snoop parity on Thread 1", "L1 BTB multi-match error", "L2 BTB multi-match error", "L2 Cache Response Poison error", "System Read Data error", }; /* L2 Cache */ static const char * const smca_l2_mce_desc[] = { "L2M tag multi-way-hit error", "L2M tag ECC error", "L2M data ECC error", "HW assert", }; /* Decoder Unit */ static const char * const smca_de_mce_desc[] = { "uop cache tag parity error", "uop cache data parity error", "Insn buffer parity error", "uop queue parity error", "Insn dispatch queue parity error", "Fetch address FIFO parity", "Patch RAM data parity", "Patch RAM sequencer parity", "uop buffer parity" }; /* Execution Unit */ static const char * const smca_ex_mce_desc[] = { "Watchdog timeout error", "Phy register file parity", "Flag register file parity", "Immediate displacement register file parity", "Address generator payload parity", "EX payload parity", "Checkpoint queue parity", "Retire dispatch queue parity", "Retire status queue parity error", "Scheduling queue parity error", "Branch buffer queue parity error", }; /* Floating Point Unit */ static const char * const smca_fp_mce_desc[] = { "Physical register file parity", "Freelist parity error", "Schedule queue parity", "NSQ parity error", "Retire queue parity", "Status register file parity", "Hardware assertion", }; /* L3 Cache */ static const char * const smca_l3_mce_desc[] = { "Shadow tag macro ECC error", "Shadow tag macro multi-way-hit error", "L3M tag ECC error", "L3M tag multi-way-hit error", "L3M data ECC error", "XI parity, L3 fill done channel error", "L3 victim queue parity", "L3 HW assert", }; /* Coherent Slave Unit */ static const char * const smca_cs_mce_desc[] = { "Illegal request from transport layer", "Address violation", "Security violation", "Illegal response from transport layer", "Unexpected response", "Parity error on incoming request or probe response data", "Parity error on incoming read response data", "Atomic request parity", "ECC error on probe filter access", }; /* Coherent Slave Unit V2 */ static const char * const smca_cs2_mce_desc[] = { "Illegal Request", "Address Violation", "Security Violation", "Illegal Response", "Unexpected Response", "Request or Probe Parity Error", "Read Response Parity Error", "Atomic Request Parity Error", "SDP read response had no match in the CS queue", "Probe Filter Protocol Error", "Probe Filter ECC Error", "SDP read response had an unexpected RETRY error", "Counter overflow error", "Counter underflow error", }; /* Power, Interrupt, etc.. */ static const char * const smca_pie_mce_desc[] = { "HW assert", "Internal PIE register security violation", "Error on GMI link", "Poison data written to internal PIE register", }; /* Unified Memory Controller */ static const char * const smca_umc_mce_desc[] = { "DRAM ECC error", "Data poison error on DRAM", "SDP parity error", "Advanced peripheral bus error", "Command/address parity error", "Write data CRC error", }; static const char * const smca_umc2_mce_desc[] = { "DRAM ECC error", "Data poison error", "SDP parity error", "Reserved", "Address/Command parity error", "Write data parity error", "DCQ SRAM ECC error", "Reserved", "Read data parity error", "Rdb SRAM ECC error", "RdRsp SRAM ECC error", "LM32 MP errors", }; /* Parameter Block */ static const char * const smca_pb_mce_desc[] = { "Parameter Block RAM ECC error", }; /* Platform Security Processor */ static const char * const smca_psp_mce_desc[] = { "PSP RAM ECC or parity error", }; /* Platform Security Processor V2 */ static const char * const smca_psp2_mce_desc[] = { "High SRAM ECC or parity error", "Low SRAM ECC or parity error", "Instruction Cache Bank 0 ECC or parity error", "Instruction Cache Bank 1 ECC or parity error", "Instruction Tag Ram 0 parity error", "Instruction Tag Ram 1 parity error", "Data Cache Bank 0 ECC or parity error", "Data Cache Bank 1 ECC or parity error", "Data Cache Bank 2 ECC or parity error", "Data Cache Bank 3 ECC or parity error", "Data Tag Bank 0 parity error", "Data Tag Bank 1 parity error", "Data Tag Bank 2 parity error", "Data Tag Bank 3 parity error", "Dirty Data Ram parity error", "TLB Bank 0 parity error", "TLB Bank 1 parity error", "System Hub Read Buffer ECC or parity error", }; /* System Management Unit */ static const char * const smca_smu_mce_desc[] = { "SMU RAM ECC or parity error", }; /* System Management Unit V2 */ static const char * const smca_smu2_mce_desc[] = { "High SRAM ECC or parity error", "Low SRAM ECC or parity error", "Data Cache Bank A ECC or parity error", "Data Cache Bank B ECC or parity error", "Data Tag Cache Bank A ECC or parity error", "Data Tag Cache Bank B ECC or parity error", "Instruction Cache Bank A ECC or parity error", "Instruction Cache Bank B ECC or parity error", "Instruction Tag Cache Bank A ECC or parity error", "Instruction Tag Cache Bank B ECC or parity error", "System Hub Read Buffer ECC or parity error", }; /* Microprocessor 5 Unit */ static const char * const smca_mp5_mce_desc[] = { "High SRAM ECC or parity error", "Low SRAM ECC or parity error", "Data Cache Bank A ECC or parity error", "Data Cache Bank B ECC or parity error", "Data Tag Cache Bank A ECC or parity error", "Data Tag Cache Bank B ECC or parity error", "Instruction Cache Bank A ECC or parity error", "Instruction Cache Bank B ECC or parity error", "Instruction Tag Cache Bank A ECC or parity error", "Instruction Tag Cache Bank B ECC or parity error", }; /* Northbridge IO Unit */ static const char * const smca_nbio_mce_desc[] = { "ECC or Parity error", "PCIE error", "SDP ErrEvent error", "SDP Egress Poison Error", "IOHC Internal Poison Error", }; /* PCI Express Unit */ static const char * const smca_pcie_mce_desc[] = { "CCIX PER Message logging", "CCIX Read Response with Status: Non-Data Error", "CCIX Write Response with Status: Non-Data Error", "CCIX Read Response with Status: Data Error", "CCIX Non-okay write response with data error", }; static const char * const smca_pcie2_mce_desc[] = { "SDP Parity Error logging", }; static const char * const smca_xgmipcs_mce_desc[] = { "Data Loss Error", "Training Error", "Flow Control Acknowledge Error", "Rx Fifo Underflow Error", "Rx Fifo Overflow Error", "CRC Error", "BER Exceeded Error", "Tx Vcid Data Error", "Replay Buffer Parity Error", "Data Parity Error", "Replay Fifo Overflow Error", "Replay Fifo Underflow Error", "Elastic Fifo Overflow Error", "Deskew Error", "Flow Control CRC Error", "Data Startup Limit Error", "FC Init Timeout Error", "Recovery Timeout Error", "Ready Serial Timeout Error", "Ready Serial Attempt Error", "Recovery Attempt Error", "Recovery Relock Attempt Error", "Replay Attempt Error", "Sync Header Error", "Tx Replay Timeout Error", "Rx Replay Timeout Error", "LinkSub Tx Timeout Error", "LinkSub Rx Timeout Error", "Rx CMD Pocket Error", }; static const char * const smca_xgmiphy_mce_desc[] = { "RAM ECC Error", "ARC instruction buffer parity error", "ARC data buffer parity error", "PHY APB error", }; static const char * const smca_waflphy_mce_desc[] = { "RAM ECC Error", "ARC instruction buffer parity error", "ARC data buffer parity error", "PHY APB error", }; struct smca_mce_desc { const char * const *descs; unsigned int num_descs; }; static struct smca_mce_desc smca_mce_descs[] = { [SMCA_LS] = { smca_ls_mce_desc, ARRAY_SIZE(smca_ls_mce_desc) }, [SMCA_LS_V2] = { smca_ls2_mce_desc, ARRAY_SIZE(smca_ls2_mce_desc) }, [SMCA_IF] = { smca_if_mce_desc, ARRAY_SIZE(smca_if_mce_desc) }, [SMCA_L2_CACHE] = { smca_l2_mce_desc, ARRAY_SIZE(smca_l2_mce_desc) }, [SMCA_DE] = { smca_de_mce_desc, ARRAY_SIZE(smca_de_mce_desc) }, [SMCA_EX] = { smca_ex_mce_desc, ARRAY_SIZE(smca_ex_mce_desc) }, [SMCA_FP] = { smca_fp_mce_desc, ARRAY_SIZE(smca_fp_mce_desc) }, [SMCA_L3_CACHE] = { smca_l3_mce_desc, ARRAY_SIZE(smca_l3_mce_desc) }, [SMCA_CS] = { smca_cs_mce_desc, ARRAY_SIZE(smca_cs_mce_desc) }, [SMCA_CS_V2] = { smca_cs2_mce_desc, ARRAY_SIZE(smca_cs2_mce_desc) }, [SMCA_PIE] = { smca_pie_mce_desc, ARRAY_SIZE(smca_pie_mce_desc) }, [SMCA_UMC] = { smca_umc_mce_desc, ARRAY_SIZE(smca_umc_mce_desc) }, [SMCA_UMC_V2] = { smca_umc2_mce_desc, ARRAY_SIZE(smca_umc2_mce_desc) }, [SMCA_PB] = { smca_pb_mce_desc, ARRAY_SIZE(smca_pb_mce_desc) }, [SMCA_PSP] = { smca_psp_mce_desc, ARRAY_SIZE(smca_psp_mce_desc) }, [SMCA_PSP_V2] = { smca_psp2_mce_desc, ARRAY_SIZE(smca_psp2_mce_desc)}, [SMCA_SMU] = { smca_smu_mce_desc, ARRAY_SIZE(smca_smu_mce_desc) }, [SMCA_SMU_V2] = { smca_smu2_mce_desc, ARRAY_SIZE(smca_smu2_mce_desc)}, [SMCA_MP5] = { smca_mp5_mce_desc, ARRAY_SIZE(smca_mp5_mce_desc) }, [SMCA_NBIO] = { smca_nbio_mce_desc, ARRAY_SIZE(smca_nbio_mce_desc)}, [SMCA_PCIE] = { smca_pcie_mce_desc, ARRAY_SIZE(smca_pcie_mce_desc)}, [SMCA_PCIE_V2] = { smca_pcie2_mce_desc, ARRAY_SIZE(smca_pcie2_mce_desc) }, [SMCA_XGMI_PCS] = { smca_xgmipcs_mce_desc, ARRAY_SIZE(smca_xgmipcs_mce_desc) }, [SMCA_XGMI_PHY] = { smca_xgmiphy_mce_desc, ARRAY_SIZE(smca_xgmiphy_mce_desc) }, [SMCA_WAFL_PHY] = { smca_waflphy_mce_desc, ARRAY_SIZE(smca_waflphy_mce_desc) }, }; struct smca_hwid { unsigned int bank_type; /* Use with smca_bank_types for easy indexing.*/ uint32_t mcatype_hwid; /* mcatype,hwid bit 63-32 in MCx_IPID Register*/ }; static struct smca_hwid smca_hwid_mcatypes[] = { /* { bank_type, mcatype_hwid } */ /* ZN Core (HWID=0xB0) MCA types */ { SMCA_LS, 0x000000B0 }, { SMCA_LS_V2, 0x001000B0 }, { SMCA_IF, 0x000100B0 }, { SMCA_L2_CACHE, 0x000200B0 }, { SMCA_DE, 0x000300B0 }, /* HWID 0xB0 MCATYPE 0x4 is Reserved */ { SMCA_EX, 0x000500B0 }, { SMCA_FP, 0x000600B0 }, { SMCA_L3_CACHE, 0x000700B0 }, /* Data Fabric MCA types */ { SMCA_CS, 0x0000002E }, { SMCA_CS_V2, 0x0002002E }, { SMCA_PIE, 0x0001002E }, /* Unified Memory Controller MCA type */ { SMCA_UMC, 0x00000096 }, /* Heterogeneous systems may have both UMC and UMC_v2 types on the same node. */ { SMCA_UMC_V2, 0x00010096 }, /* Parameter Block MCA type */ { SMCA_PB, 0x00000005 }, /* Platform Security Processor MCA type */ { SMCA_PSP, 0x000000FF }, { SMCA_PSP_V2, 0x000100FF }, /* System Management Unit MCA type */ { SMCA_SMU, 0x00000001 }, { SMCA_SMU_V2, 0x00010001 }, /* Microprocessor 5 Unit MCA type */ { SMCA_MP5, 0x00020001 }, /* Northbridge IO Unit MCA type */ { SMCA_NBIO, 0x00000018 }, /* PCI Express Unit MCA type */ { SMCA_PCIE, 0x00000046 }, { SMCA_PCIE_V2, 0x00010046 }, /* Ext Global Memory Interconnect PCS MCA type */ { SMCA_XGMI_PCS, 0x00000050 }, /* Ext Global Memory Interconnect PHY MCA type */ { SMCA_XGMI_PHY, 0x00000259 }, /* WAFL PHY MCA type */ { SMCA_WAFL_PHY, 0x00000267 }, }; struct smca_bank_name { const char *name; }; static struct smca_bank_name smca_names[] = { [SMCA_LS ... SMCA_LS_V2] = { "Load Store Unit" }, [SMCA_IF] = { "Instruction Fetch Unit" }, [SMCA_L2_CACHE] = { "L2 Cache" }, [SMCA_DE] = { "Decode Unit" }, [SMCA_RESERVED] = { "Reserved" }, [SMCA_EX] = { "Execution Unit" }, [SMCA_FP] = { "Floating Point Unit" }, [SMCA_L3_CACHE] = { "L3 Cache" }, [SMCA_CS ... SMCA_CS_V2] = { "Coherent Slave" }, [SMCA_PIE] = { "Power, Interrupts, etc." }, [SMCA_UMC] = { "Unified Memory Controller" }, [SMCA_UMC_V2] = { "Unified Memory Controller V2" }, [SMCA_PB] = { "Parameter Block" }, [SMCA_PSP ... SMCA_PSP_V2] = { "Platform Security Processor" }, [SMCA_SMU ... SMCA_SMU_V2] = { "System Management Unit" }, [SMCA_MP5] = { "Microprocessor 5 Unit" }, [SMCA_NBIO] = { "Northbridge IO Unit" }, [SMCA_PCIE ... SMCA_PCIE_V2] = { "PCI Express Unit" }, [SMCA_XGMI_PCS] = { "Ext Global Memory Interconnect PCS Unit" }, [SMCA_XGMI_PHY] = { "Ext Global Memory Interconnect PHY Unit" }, [SMCA_WAFL_PHY] = { "WAFL PHY Unit" }, }; static void amd_decode_errcode(struct mce_event *e) { decode_amd_errcode(e); if (e->status & MCI_STATUS_POISON) mce_snprintf(e->mcistatus_msg, "Poison consumed"); if (e->status & MCI_STATUS_TCC) mce_snprintf(e->mcistatus_msg, "Task_context_corrupt"); } /* * To find the UMC channel represented by this bank we need to match on its * instance_id. The instance_id of a bank is held in the lower 32 bits of its * IPID. */ static int find_umc_channel(struct mce_event *e) { return EXTRACT(e->ipid, 0, 31) >> 20; } /* * The HBM memory managed by the UMCCH of the noncpu node * can be calculated based on the [15:12]bits of IPID */ static int find_hbm_channel(struct mce_event *e) { int umc, tmp; umc = EXTRACT(e->ipid, 0, 31) >> 20; /* * The HBM channel managed by the UMC of the noncpu node * can be calculated based on the [15:12]bits of IPID as follows */ tmp = ((e->ipid >> 12) & 0xf); return (umc % 2) ? tmp + 4 : tmp; } /* Decode extended errors according to Scalable MCA specification */ static void decode_smca_error(struct mce_event *e) { enum smca_bank_types bank_type; const char *ip_name; unsigned short xec = (e->status >> 16) & 0x3f; const struct smca_hwid *s_hwid; uint32_t mcatype_hwid = EXTRACT(e->ipid, 32, 63); uint8_t mcatype_instancehi = EXTRACT(e->ipid, 44, 47); unsigned int csrow = -1, channel = -1; unsigned int i; for (i = 0; i < ARRAY_SIZE(smca_hwid_mcatypes); i++) { s_hwid = &smca_hwid_mcatypes[i]; if (mcatype_hwid == s_hwid->mcatype_hwid) { bank_type = s_hwid->bank_type; break; } if (mcatype_instancehi >= NONCPU_NODE_INDEX) bank_type = SMCA_UMC_V2; } if (i >= MAX_NR_BANKS) { strcpy(e->mcastatus_msg, "Couldn't find bank type with IPID"); return; } if (bank_type >= MAX_NR_BANKS) { strcpy(e->mcastatus_msg, "Don't know how to decode this bank"); return; } if (bank_type == SMCA_RESERVED) { strcpy(e->mcastatus_msg, "Bank 4 is reserved.\n"); return; } ip_name = smca_names[bank_type].name; mce_snprintf(e->bank_name, "%s (bank=%d)", ip_name, e->bank); /* Only print the descriptor of valid extended error code */ if (xec < smca_mce_descs[bank_type].num_descs) mce_snprintf(e->mcastatus_msg, " %s.\n", smca_mce_descs[bank_type].descs[xec]); if (bank_type == SMCA_UMC && xec == 0) { channel = find_umc_channel(e); csrow = e->synd & 0x7; /* Bit 0, 1 ,2 */ mce_snprintf(e->mc_location, "memory_channel=%d,csrow=%d", channel, csrow); } if (bank_type == SMCA_UMC_V2 && xec == 0) { /* The UMCPHY is reported as csrow in case of noncpu nodes */ csrow = find_umc_channel(e) / 2; /* UMCCH is managing the HBM memory */ channel = find_hbm_channel(e); mce_snprintf(e->mc_location, "memory_channel=%d,csrow=%d", channel, csrow); } } int parse_amd_smca_event(struct ras_events *ras, struct mce_event *e) { uint64_t mcgstatus = e->mcgstatus; mce_snprintf(e->mcgstatus_msg, "mcgstatus=%lld", (long long)e->mcgstatus); if (mcgstatus & MCG_STATUS_RIPV) mce_snprintf(e->mcgstatus_msg, "RIPV"); if (mcgstatus & MCG_STATUS_EIPV) mce_snprintf(e->mcgstatus_msg, "EIPV"); if (mcgstatus & MCG_STATUS_MCIP) mce_snprintf(e->mcgstatus_msg, "MCIP"); decode_smca_error(e); amd_decode_errcode(e); return 0; } 0707010000002E000081A4000000000000000000000001611B979000000F56000000000000000000000000000000000000002900000000rasdaemon-0.6.7.18.git+7ccf12f/mce-amd.c/* * Copyright (c) 2018, The AMD, Inc. 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 version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include <stdio.h> #include <string.h> #include "ras-mce-handler.h" /* Error Code Types */ #define TLB_ERROR(x) (((x) & 0xFFF0) == 0x0010) #define MEM_ERROR(x) (((x) & 0xFF00) == 0x0100) #define BUS_ERROR(x) (((x) & 0xF800) == 0x0800) #define INT_ERROR(x) (((x) & 0xF4FF) == 0x0400) /* Error code: transaction type (TT) */ static char *transaction[] = { "instruction", "data", "generic", "reserved" }; /* Error codes: cache level (LL) */ static char *cachelevel[] = { "reserved", "L1", "L2", "L3/generic" }; /* Error codes: memory transaction type (RRRR) */ static char *memtrans[] = { "generic", "generic read", "generic write", "data read", "data write", "instruction fetch", "prefetch", "evict", "snoop", "?", "?", "?", "?", "?", "?", "?" }; /* Participation Processor */ static char *partproc[] = { "local node origin", "local node response", "local node observed", "generic participation" }; /* Timeout */ static char *timeout[] = { "request didn't time out", "request timed out" }; /* internal unclassified error code */ static char *internal[] = { "reserved", "reserved", "hardware assert", "reserved" }; #define TT(x) (((x) >> 2) & 0x3) /*bit 2, bit 3*/ #define TT_MSG(x) transaction[TT(x)] #define LL(x) ((x) & 0x3) /*bit 0, bit 1*/ #define LL_MSG(x) cachelevel[LL(x)] #define R4(x) (((x) >> 4) & 0xF) /*bit 4, bit 5, bit 6, bit 7 */ #define R4_MSG(x) ((R4(x) < 9) ? memtrans[R4(x)] : "Wrong R4!") #define TO(x) (((x) >> 8) & 0x1) /*bit 8*/ #define TO_MSG(x) timeout[TO(x)] #define PP(x) (((x) >> 9) & 0x3) /*bit 9, bit 10*/ #define PP_MSG(x) partproc[PP(x)] #define UU(x) (((x) >> 8) & 0x3) /*bit 8, bit 9*/ #define UU_MSG(x) internal[UU(x)] void decode_amd_errcode(struct mce_event *e) { uint16_t ec = e->status & 0xffff; uint16_t ecc = (e->status >> 45) & 0x3; if (e->status & MCI_STATUS_UC) { if (e->status & MCI_STATUS_PCC) strcpy(e->error_msg, "System Fatal error."); if (e->mcgstatus & MCG_STATUS_RIPV) strcpy(e->error_msg, "Uncorrected, software restartable error."); strcpy(e->error_msg, "Uncorrected, software containable error."); } else if (e->status & MCI_STATUS_DEFERRED) strcpy(e->error_msg, "Deferred error, no action required."); else strcpy(e->error_msg, "Corrected error, no action required."); if (!(e->status & MCI_STATUS_VAL)) mce_snprintf(e->mcistatus_msg, "MCE_INVALID"); if (e->status & MCI_STATUS_OVER) mce_snprintf(e->mcistatus_msg, "Error_overflow"); if (e->status & MCI_STATUS_PCC) mce_snprintf(e->mcistatus_msg, "Processor_context_corrupt"); if (ecc) mce_snprintf(e->mcistatus_msg, "%sECC", ((ecc == 2) ? "C" : "U")); if (INT_ERROR(ec)) { mce_snprintf(e->mcastatus_msg, "Internal '%s'", UU_MSG(ec)); return; } if (TLB_ERROR(ec)) mce_snprintf(e->mcastatus_msg, "TLB Error 'tx: %s, level: %s'", TT_MSG(ec), LL_MSG(ec)); else if (MEM_ERROR(ec)) mce_snprintf(e->mcastatus_msg, "Memory Error 'mem-tx: %s, tx: %s, level: %s'", R4_MSG(ec), TT_MSG(ec), LL_MSG(ec)); else if (BUS_ERROR(ec)) mce_snprintf(e->mcastatus_msg, "Bus Error '%s, %s, mem-tx: %s, level: %s'", PP_MSG(ec), TO_MSG(ec), R4_MSG(ec), LL_MSG(ec)); return; } 0707010000002F000081A4000000000000000000000001611B979000001151000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-broadwell-de.c/* * The code below came from Tony Luck's mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Table 16-24 */ static char *pcu_1[] = { [0x00] = "No Error", [0x09] = "MC_MESSAGE_CHANNEL_TIMEOUT", [0x13] = "MC_DMI_TRAINING_TIMEOUT", [0x15] = "MC_DMI_CPU_RESET_ACK_TIMEOUT", [0x1E] = "MC_VR_ICC_MAX_LT_FUSED_ICC_MAX", [0x25] = "MC_SVID_COMMAN_TIMEOUT", [0x26] = "MCA_PKGC_DIRECT_WAKE_RING_TIMEOUT", [0x29] = "MC_VR_VOUT_MAC_LT_FUSED_SVID", [0x2B] = "MC_PKGC_WATCHDOG_HANG_CBZ_DOWN", [0x2C] = "MC_PKGC_WATCHDOG_HANG_CBZ_UP", [0x44] = "MC_CRITICAL_VR_FAILED", [0x46] = "MC_VID_RAMP_DOWN_FAILED", [0x49] = "MC_SVID_WRITE_REG_VOUT_MAX_FAILED", [0x4B] = "MC_BOOT_VID_TIMEOUT_DRAM_0", [0x4F] = "MC_SVID_COMMAND_ERROR", [0x52] = "MC_FIVR_CATAS_OVERVOL_FAULT", [0x53] = "MC_FIVR_CATAS_OVERCUR_FAULT", [0x57] = "MC_SVID_PKGC_REQUEST_FAILED", [0x58] = "MC_SVID_IMON_REQUEST_FAILED", [0x59] = "MC_SVID_ALERT_REQUEST_FAILED", [0x62] = "MC_INVALID_PKGS_RSP_QPI", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x67] = "MC_HA_IMC_RW_BLOCK_ACK_TIMEOUT", [0x6A] = "MC_MSGCH_PMREQ_CMP_TIMEOUT", [0x72] = "MC_WATCHDOG_TIMEOUT_PKGS_MASTER", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT" }; static struct field pcu_mc4[] = { FIELD(24, pcu_1), {} }; /* See IA32 SDM Vol3B Table 16-18 */ static struct field memctrl_mc9[] = { SBITFIELD(16, "Address parity error"), SBITFIELD(17, "HA Wrt buffer Data parity error"), SBITFIELD(18, "HA Wrt byte enable parity error"), SBITFIELD(19, "Corrected patrol scrub error"), SBITFIELD(20, "Uncorrected patrol scrub error"), SBITFIELD(21, "Corrected spare error"), SBITFIELD(22, "Uncorrected spare error"), SBITFIELD(23, "Corrected memory read error"), SBITFIELD(24, "iMC, WDB, parity errors"), {} }; void broadwell_de_decode_model(struct ras_events *ras, struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan; switch (e->bank) { case 4: switch (EXTRACT(status, 0, 15) & ~(1ull << 12)) { case 0x402: case 0x403: mce_snprintf(e->mcastatus_msg, "Internal errors "); break; case 0x406: mce_snprintf(e->mcastatus_msg, "Intel TXT errors "); break; case 0x407: mce_snprintf(e->mcastatus_msg, "Other UBOX Internal errors "); break; } if (EXTRACT(status, 16, 19) & 3) mce_snprintf(e->mcastatus_msg, "PCU internal error "); if (EXTRACT(status, 20, 23) & 4) mce_snprintf(e->mcastatus_msg, "Ubox error "); decode_bitfield(e, status, pcu_mc4); break; case 9: case 10: mce_snprintf(e->mcastatus_msg, "MemCtrl: "); decode_bitfield(e, status, memctrl_mc9); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 9 || e->bank > 16 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) { rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); } /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 != -1 && rank1 != -1) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 != -1) mce_snprintf(e->mc_location, "rank=%d", rank0); } 07070100000030000081A4000000000000000000000001611B9790000017E9000000000000000000000000000000000000003A00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-broadwell-epex.c/* * The code below came from Tony Luck's mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Table 16-20 */ static char *pcu_1[] = { [0x00] = "No Error", [0x09] = "MC_MESSAGE_CHANNEL_TIMEOUT", [0x0D] = "MC_IMC_FORCE_SR_S3_TIMEOUT", [0x0E] = "MC_CPD_UNCPD_SD_TIMEOUT", [0x13] = "MC_DMI_TRAINING_TIMEOUT", [0x15] = "MC_DMI_CPU_RESET_ACK_TIMEOUT", [0x1E] = "MC_VR_ICC_MAX_LT_FUSED_ICC_MAX", [0x25] = "MC_SVID_COMMAN_TIMEOUT", [0x29] = "MC_VR_VOUT_MAC_LT_FUSED_SVID", [0x2B] = "MC_PKGC_WATCHDOG_HANG_CBZ_DOWN", [0x2C] = "MC_PKGC_WATCHDOG_HANG_CBZ_UP", [0x39] = "MC_PKGC_WATCHDOG_HANG_C3_UP_SF", [0x44] = "MC_CRITICAL_VR_FAILED", [0x45] = "MC_ICC_MAX_NOTSUPPORTED", [0x46] = "MC_VID_RAMP_DOWN_FAILED", [0x47] = "MC_EXCL_MODE_NO_PMREQ_CMP", [0x48] = "MC_SVID_READ_REG_ICC_MAX_FAILED", [0x49] = "MC_SVID_WRITE_REG_VOUT_MAX_FAILED", [0x4B] = "MC_BOOT_VID_TIMEOUT_DRAM_0", [0x4C] = "MC_BOOT_VID_TIMEOUT_DRAM_1", [0x4D] = "MC_BOOT_VID_TIMEOUT_DRAM_2", [0x4E] = "MC_BOOT_VID_TIMEOUT_DRAM_3", [0x4F] = "MC_SVID_COMMAND_ERROR", [0x52] = "MC_FIVR_CATAS_OVERVOL_FAULT", [0x53] = "MC_FIVR_CATAS_OVERCUR_FAULT", [0x57] = "MC_SVID_PKGC_REQUEST_FAILED", [0x58] = "MC_SVID_IMON_REQUEST_FAILED", [0x59] = "MC_SVID_ALERT_REQUEST_FAILED", [0x60] = "MC_INVALID_PKGS_REQ_PCH", [0x61] = "MC_INVALID_PKGS_REQ_QPI", [0x62] = "MC_INVALID_PKGS_RSP_QPI", [0x63] = "MC_INVALID_PKGS_RSP_PCH", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x67] = "MC_HA_IMC_RW_BLOCK_ACK_TIMEOUT", [0x68] = "MC_IMC_RW_SMBUS_TIMEOUT", [0x69] = "MC_HA_FAILSTS_CHANGE_DETECTED", [0x6A] = "MC_MSGCH_PMREQ_CMP_TIMEOUT", [0x70] = "MC_WATCHDOG_TIMEOUT_PKGC_SLAVE", [0x71] = "MC_WATCHDOG_TIMEOUT_PKGC_MASTER", [0x72] = "MC_WATCHDOG_TIMEOUT_PKGS_MASTER", [0x7C] = "MC_BIOS_RST_CPL_INVALID_SEQ", [0x7D] = "MC_MORE_THAN_ONE_TXT_AGENT", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT" }; static struct field pcu_mc4[] = { FIELD(24, pcu_1), {} }; /* See IA32 SDM Vol3B Table 16-21 */ static char *qpi[] = { [0x02] = "Intel QPI physical layer detected drift buffer alarm", [0x03] = "Intel QPI physical layer detected latency buffer rollover", [0x10] = "Intel QPI link layer detected control error from R3QPI", [0x11] = "Rx entered LLR abort state on CRC error", [0x12] = "Unsupported or undefined packet", [0x13] = "Intel QPI link layer control error", [0x15] = "RBT used un-initialized value", [0x20] = "Intel QPI physical layer detected a QPI in-band reset but aborted initialization", [0x21] = "Link failover data self healing", [0x22] = "Phy detected in-band reset (no width change)", [0x23] = "Link failover clock failover", [0x30] = "Rx detected CRC error - successful LLR after Phy re-init", [0x31] = "Rx detected CRC error - successful LLR wihout Phy re-init", }; static struct field qpi_mc[] = { FIELD(16, qpi), {} }; /* See IA32 SDM Vol3B Table 16-26 */ static struct field memctrl_mc9[] = { SBITFIELD(16, "DDR3 address parity error"), SBITFIELD(17, "Uncorrected HA write data error"), SBITFIELD(18, "Uncorrected HA data byte enable error"), SBITFIELD(19, "Corrected patrol scrub error"), SBITFIELD(20, "Uncorrected patrol scrub error"), SBITFIELD(21, "Corrected spare error"), SBITFIELD(22, "Uncorrected spare error"), SBITFIELD(24, "iMC write data buffer parity error"), SBITFIELD(25, "DDR4 command address parity error"), {} }; void broadwell_epex_decode_model(struct ras_events *ras, struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan; switch (e->bank) { case 4: switch (EXTRACT(status, 0, 15) & ~(1ull << 12)) { case 0x402: case 0x403: mce_snprintf(e->mcastatus_msg, "Internal errors "); break; case 0x406: mce_snprintf(e->mcastatus_msg, "Intel TXT errors "); break; case 0x407: mce_snprintf(e->mcastatus_msg, "Other UBOX Internal errors "); break; } if (EXTRACT(status, 16, 19)) mce_snprintf(e->mcastatus_msg, "PCU internal error "); decode_bitfield(e, status, pcu_mc4); break; case 5: case 20: case 21: mce_snprintf(e->mcastatus_msg, "QPI: "); decode_bitfield(e, status, qpi_mc); break; case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: mce_snprintf(e->mcastatus_msg, "MemCtrl: "); decode_bitfield(e, status, memctrl_mc9); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 9 || e->bank > 16 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) { rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); } /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 != -1 && rank1 != -1) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 != -1) mce_snprintf(e->mc_location, "rank=%d", rank0); } 07070100000031000081A4000000000000000000000001611B979000000D99000000000000000000000000000000000000003600000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-dunnington.c/* * The code below came from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* Follows Intel IA32 SDM 3b Appendix E.2.1 ++ */ static struct field dunnington_bus_status[] = { SBITFIELD(16, "Parity error detected during FSB request phase"), FIELD_NULL(17), SBITFIELD(20, "Hard Failure response received for a local transaction"), SBITFIELD(21, "Parity error on FSB response field detected"), SBITFIELD(22, "Parity data error on inbound data detected"), FIELD_NULL(23), FIELD_NULL(25), FIELD_NULL(28), FIELD_NULL(31), {} }; static char *dnt_front_error[0xf] = { [0x1] = "Inclusion error from core 0", [0x2] = "Inclusion error from core 1", [0x3] = "Write Exclusive error from core 0", [0x4] = "Write Exclusive error from core 1", [0x5] = "Inclusion error from FSB", [0x6] = "SNP stall error from FSB", [0x7] = "Write stall error from FSB", [0x8] = "FSB Arbiter Timeout error", [0xA] = "Inclusion error from core 2", [0xB] = "Write exclusive error from core 2", }; static char *dnt_int_error[0xf] = { [0x2] = "Internal timeout error", [0x3] = "Internal timeout error", [0x4] = "Intel Cache Safe Technology Queue full error\n" "or disabled ways in a set overflow", [0x5] = "Quiet cycle timeout error (correctable)", }; struct field dnt_int_status[] = { FIELD(8, dnt_int_error), {} }; struct field dnt_front_status[] = { FIELD(0, dnt_front_error), {} }; struct field dnt_cecc[] = { SBITFIELD(1, "Correctable ECC event on outgoing core 0 data"), SBITFIELD(2, "Correctable ECC event on outgoing core 1 data"), SBITFIELD(3, "Correctable ECC event on outgoing core 2 data"), {} }; struct field dnt_uecc[] = { SBITFIELD(1, "Uncorrectable ECC event on outgoing core 0 data"), SBITFIELD(2, "Uncorrectable ECC event on outgoing core 1 data"), SBITFIELD(3, "Uncorrectable ECC event on outgoing core 2 data"), {} }; static void dunnington_decode_bus(struct mce_event *e, uint64_t status) { decode_bitfield(e, status, dunnington_bus_status); } static void dunnington_decode_internal(struct mce_event *e, uint64_t status) { uint32_t mca = (status >> 16) & 0xffff; if ((mca & 0xfff0) == 0) decode_bitfield(e, mca, dnt_front_status); else if ((mca & 0xf0ff) == 0) decode_bitfield(e, mca, dnt_int_status); else if ((mca & 0xfff0) == 0xc000) decode_bitfield(e, mca, dnt_cecc); else if ((mca & 0xfff0) == 0xe000) decode_bitfield(e, mca, dnt_uecc); } void dunnington_decode_model(struct mce_event *e) { uint64_t status = e->status; if ((status & 0xffff) == 0xe0f) dunnington_decode_bus(e, status); else if ((status & 0xffff) == (1 << 10)) dunnington_decode_internal(e, status); } 07070100000032000081A4000000000000000000000001611B9790000017CB000000000000000000000000000000000000003300000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-haswell.c/* * The code below came from Tony Luck mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Table 16-20 */ static char *pcu_1[] = { [0x00] = "No Error", [0x09] = "MC_MESSAGE_CHANNEL_TIMEOUT", [0x0D] = "MC_IMC_FORCE_SR_S3_TIMEOUT", [0x0E] = "MC_CPD_UNCPD_SD_TIMEOUT", [0x13] = "MC_DMI_TRAINING_TIMEOUT", [0x15] = "MC_DMI_CPU_RESET_ACK_TIMEOUT", [0x1E] = "MC_VR_ICC_MAX_LT_FUSED_ICC_MAX", [0x25] = "MC_SVID_COMMAN_TIMEOUT", [0x29] = "MC_VR_VOUT_MAC_LT_FUSED_SVID", [0x2B] = "MC_PKGC_WATCHDOG_HANG_CBZ_DOWN", [0x2C] = "MC_PKGC_WATCHDOG_HANG_CBZ_UP", [0x39] = "MC_PKGC_WATCHDOG_HANG_C3_UP_SF", [0x44] = "MC_CRITICAL_VR_FAILED", [0x45] = "MC_ICC_MAX_NOTSUPPORTED", [0x46] = "MC_VID_RAMP_DOWN_FAILED", [0x47] = "MC_EXCL_MODE_NO_PMREQ_CMP", [0x48] = "MC_SVID_READ_REG_ICC_MAX_FAILED", [0x49] = "MC_SVID_WRITE_REG_VOUT_MAX_FAILED", [0x4B] = "MC_BOOT_VID_TIMEOUT_DRAM_0", [0x4C] = "MC_BOOT_VID_TIMEOUT_DRAM_1", [0x4D] = "MC_BOOT_VID_TIMEOUT_DRAM_2", [0x4E] = "MC_BOOT_VID_TIMEOUT_DRAM_3", [0x4F] = "MC_SVID_COMMAND_ERROR", [0x52] = "MC_FIVR_CATAS_OVERVOL_FAULT", [0x53] = "MC_FIVR_CATAS_OVERCUR_FAULT", [0x57] = "MC_SVID_PKGC_REQUEST_FAILED", [0x58] = "MC_SVID_IMON_REQUEST_FAILED", [0x59] = "MC_SVID_ALERT_REQUEST_FAILED", [0x60] = "MC_INVALID_PKGS_REQ_PCH", [0x61] = "MC_INVALID_PKGS_REQ_QPI", [0x62] = "MC_INVALID_PKGS_RSP_QPI", [0x63] = "MC_INVALID_PKGS_RSP_PCH", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x67] = "MC_HA_IMC_RW_BLOCK_ACK_TIMEOUT", [0x68] = "MC_IMC_RW_SMBUS_TIMEOUT", [0x69] = "MC_HA_FAILSTS_CHANGE_DETECTED", [0x6A] = "MC_MSGCH_PMREQ_CMP_TIMEOUT", [0x70] = "MC_WATCHDOG_TIMEOUT_PKGC_SLAVE", [0x71] = "MC_WATCHDOG_TIMEOUT_PKGC_MASTER", [0x72] = "MC_WATCHDOG_TIMEOUT_PKGS_MASTER", [0x7C] = "MC_BIOS_RST_CPL_INVALID_SEQ", [0x7D] = "MC_MORE_THAN_ONE_TXT_AGENT", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT" }; static struct field pcu_mc4[] = { FIELD(24, pcu_1), {} }; /* See IA32 SDM Vol3B Table 16-21 */ static char *qpi[] = { [0x02] = "Intel QPI physical layer detected drift buffer alarm", [0x03] = "Intel QPI physical layer detected latency buffer rollover", [0x10] = "Intel QPI link layer detected control error from R3QPI", [0x11] = "Rx entered LLR abort state on CRC error", [0x12] = "Unsupported or undefined packet", [0x13] = "Intel QPI link layer control error", [0x15] = "RBT used un-initialized value", [0x20] = "Intel QPI physical layer detected a QPI in-band reset but aborted initialization", [0x21] = "Link failover data self healing", [0x22] = "Phy detected in-band reset (no width change)", [0x23] = "Link failover clock failover", [0x30] = "Rx detected CRC error - successful LLR after Phy re-init", [0x31] = "Rx detected CRC error - successful LLR wihout Phy re-init", }; static struct field qpi_mc[] = { FIELD(16, qpi), {} }; /* See IA32 SDM Vol3B Table 16-22 */ static struct field memctrl_mc9[] = { SBITFIELD(16, "DDR3 address parity error"), SBITFIELD(17, "Uncorrected HA write data error"), SBITFIELD(18, "Uncorrected HA data byte enable error"), SBITFIELD(19, "Corrected patrol scrub error"), SBITFIELD(20, "Uncorrected patrol scrub error"), SBITFIELD(21, "Corrected spare error"), SBITFIELD(22, "Uncorrected spare error"), SBITFIELD(23, "Corrected memory read error"), SBITFIELD(24, "iMC write data buffer parity error"), SBITFIELD(25, "DDR4 command address parity error"), {} }; void hsw_decode_model(struct ras_events *ras, struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan; switch (e->bank) { case 4: switch (EXTRACT(status, 0, 15) & ~(1ull << 12)) { case 0x402: case 0x403: mce_snprintf(e->mcastatus_msg, "PCU Internal Errors"); break; case 0x406: mce_snprintf(e->mcastatus_msg, "Intel TXT Errors"); break; case 0x407: mce_snprintf(e->mcastatus_msg, "Other UBOX Internal Errors"); break; } if (EXTRACT(status, 16, 17) && !EXTRACT(status, 18, 19)) mce_snprintf(e->error_msg, "PCU Internal error"); decode_bitfield(e, status, pcu_mc4); break; case 5: case 20: case 21: decode_bitfield(e, status, qpi_mc); break; case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: decode_bitfield(e, status, memctrl_mc9); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 9 || e->bank > 16 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) { rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); } /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 != -1 && rank1 != -1) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 != -1) mce_snprintf(e->mc_location, "rank=%d", rank0); } 07070100000033000081A4000000000000000000000001611B979000003851000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-i10nm.c/* * The code below came from Tony Luck's mcelog code, * released under GNU Public General License, v.2 * * Copyright (C) 2019 Intel Corporation * Decode Intel 10nm specific machine check errors. * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <inttypes.h> #include <stdio.h> #include <string.h> #include "ras-mce-handler.h" #include "bitfield.h" static char *pcu_1[] = { [0x0D] = "MCA_LLC_BIST_ACTIVE_TIMEOUT", [0x0E] = "MCA_DMI_TRAINING_TIMEOUT", [0x0F] = "MCA_DMI_STRAP_SET_ARRIVAL_TIMEOUT", [0x10] = "MCA_DMI_CPU_RESET_ACK_TIMEOUT", [0x11] = "MCA_MORE_THAN_ONE_LT_AGENT", [0x14] = "MCA_INCOMPATIBLE_PCH_TYPE", [0x1E] = "MCA_BIOS_RST_CPL_INVALID_SEQ", [0x1F] = "MCA_BIOS_INVALID_PKG_STATE_CONFIG", [0x2D] = "MCA_PCU_PMAX_CALIB_ERROR", [0x2E] = "MCA_TSC100_SYNC_TIMEOUT", [0x3A] = "MCA_GPSB_TIMEOUT", [0x3B] = "MCA_PMSB_TIMEOUT", [0x3E] = "MCA_IOSFSB_PMREQ_CMP_TIMEOUT", [0x40] = "MCA_SVID_VCCIN_VR_ICC_MAX_FAILURE", [0x42] = "MCA_SVID_VCCIN_VR_VOUT_FAILURE", [0x43] = "MCA_SVID_CPU_VR_CAPABILITY_ERROR", [0x44] = "MCA_SVID_CRITICAL_VR_FAILED", [0x45] = "MCA_SVID_SA_ITD_ERROR", [0x46] = "MCA_SVID_READ_REG_FAILED", [0x47] = "MCA_SVID_WRITE_REG_FAILED", [0x4A] = "MCA_SVID_PKGC_REQUEST_FAILED", [0x4B] = "MCA_SVID_IMON_REQUEST_FAILED", [0x4C] = "MCA_SVID_ALERT_REQUEST_FAILED", [0x4D] = "MCA_SVID_MCP_VR_RAMP_ERROR", [0x56] = "MCA_FIVR_PD_HARDERR", [0x58] = "MCA_WATCHDOG_TIMEOUT_PKGC_SLAVE", [0x59] = "MCA_WATCHDOG_TIMEOUT_PKGC_MASTER", [0x5A] = "MCA_WATCHDOG_TIMEOUT_PKGS_MASTER", [0x5B] = "MCA_WATCHDOG_TIMEOUT_MSG_CH_FSM", [0x5C] = "MCA_WATCHDOG_TIMEOUT_BULK_CR_FSM", [0x5D] = "MCA_WATCHDOG_TIMEOUT_IOSFSB_FSM", [0x60] = "MCA_PKGS_SAFE_WP_TIMEOUT", [0x61] = "MCA_PKGS_CPD_UNCPD_TIMEOUT", [0x62] = "MCA_PKGS_INVALID_REQ_PCH", [0x63] = "MCA_PKGS_INVALID_REQ_INTERNAL", [0x64] = "MCA_PKGS_INVALID_RSP_INTERNAL", [0x65 ... 0x7A] = "MCA_PKGS_RESET_PREP_TIMEOUT", [0x7B] = "MCA_PKGS_SMBUS_VPP_PAUSE_TIMEOUT", [0x7C] = "MCA_PKGS_SMBUS_MCP_PAUSE_TIMEOUT", [0x7D] = "MCA_PKGS_SMBUS_SPD_PAUSE_TIMEOUT", [0x80] = "MCA_PKGC_DISP_BUSY_TIMEOUT", [0x81] = "MCA_PKGC_INVALID_RSP_PCH", [0x83] = "MCA_PKGC_WATCHDOG_HANG_CBZ_DOWN", [0x84] = "MCA_PKGC_WATCHDOG_HANG_CBZ_UP", [0x87] = "MCA_PKGC_WATCHDOG_HANG_C2_BLKMASTER", [0x88] = "MCA_PKGC_WATCHDOG_HANG_C2_PSLIMIT", [0x89] = "MCA_PKGC_WATCHDOG_HANG_SETDISP", [0x8B] = "MCA_PKGC_ALLOW_L1_ERROR", [0x90] = "MCA_RECOVERABLE_DIE_THERMAL_TOO_HOT", [0xA0] = "MCA_ADR_SIGNAL_TIMEOUT", [0xA1] = "MCA_BCLK_FREQ_OC_ABOVE_THRESHOLD", [0xB0] = "MCA_DISPATCHER_RUN_BUSY_TIMEOUT", }; static char *pcu_2[] = { [0x04] = "Clock/power IP response timeout", [0x05] = "SMBus controller raised SMI", [0x09] = "PM controller received invalid transaction", }; static char *pcu_3[] = { [0x01] = "Instruction address out of valid space", [0x02] = "Double bit RAM error on Instruction Fetch", [0x03] = "Invalid OpCode seen", [0x04] = "Stack Underflow", [0x05] = "Stack Overflow", [0x06] = "Data address out of valid space", [0x07] = "Double bit RAM error on Data Fetch", }; static struct field pcu1[] = { FIELD(0, pcu_1), {} }; static struct field pcu2[] = { FIELD(0, pcu_2), {} }; static struct field pcu3[] = { FIELD(0, pcu_3), {} }; static struct field upi1[] = { SBITFIELD(22, "Phy Control Error"), SBITFIELD(23, "Unexpected Retry.Ack flit"), SBITFIELD(24, "Unexpected Retry.Req flit"), SBITFIELD(25, "RF parity error"), SBITFIELD(26, "Routeback Table error"), SBITFIELD(27, "Unexpected Tx Protocol flit (EOP, Header or Data)"), SBITFIELD(28, "Rx Header-or-Credit BGF credit overflow/underflow"), SBITFIELD(29, "Link Layer Reset still in progress when Phy enters L0"), SBITFIELD(30, "Link Layer reset initiated while protocol traffic not idle"), SBITFIELD(31, "Link Layer Tx Parity Error"), {} }; static char *upi_2[] = { [0x00] = "Phy Initialization Failure (NumInit)", [0x01] = "Phy Detected Drift Buffer Alarm", [0x02] = "Phy Detected Latency Buffer Rollover", [0x10] = "LL Rx detected CRC error: unsuccessful LLR (entered Abort state)", [0x11] = "LL Rx Unsupported/Undefined packet", [0x12] = "LL or Phy Control Error", [0x13] = "LL Rx Parameter Exception", [0x1F] = "LL Detected Control Error", [0x20] = "Phy Initialization Abort", [0x21] = "Phy Inband Reset", [0x22] = "Phy Lane failure, recovery in x8 width", [0x23] = "Phy L0c error corrected without Phy reset", [0x24] = "Phy L0c error triggering Phy reset", [0x25] = "Phy L0p exit error corrected with reset", [0x30] = "LL Rx detected CRC error: successful LLR without Phy Reinit", [0x31] = "LL Rx detected CRC error: successful LLR with Phy Reinit", [0x32] = "Tx received LLR", }; static struct field upi2[] = { FIELD(0, upi_2), {} }; static struct field m2m[] = { SBITFIELD(16, "MC read data error"), SBITFIELD(17, "Reserved"), SBITFIELD(18, "MC partial write data error"), SBITFIELD(19, "Full write data error"), SBITFIELD(20, "M2M clock-domain-crossing buffer (BGF) error"), SBITFIELD(21, "M2M time out"), SBITFIELD(22, "M2M tracker parity error"), SBITFIELD(23, "fatal Bucket1 error"), {} }; static char *imc_0[] = { [0x01] = "Address parity error", [0x02] = "Data parity error", [0x03] = "Data ECC error", [0x04] = "Data byte enable parity error", [0x07] = "Transaction ID parity error", [0x08] = "Corrected patrol scrub error", [0x10] = "Uncorrected patrol scrub error", [0x20] = "Corrected spare error", [0x40] = "Uncorrected spare error", [0x80] = "Corrected read error", [0xA0] = "Uncorrected read error", [0xC0] = "Uncorrected metadata", }; static char *imc_1[] = { [0x00] = "WDB read parity error", [0x03] = "RPA parity error", [0x06] = "DDR_T_DPPP data BE error", [0x07] = "DDR_T_DPPP data error", [0x08] = "DDR link failure", [0x11] = "PCLS CAM error", [0x12] = "PCLS data error", }; static char *imc_2[] = { [0x00] = "DDR4 command / address parity error", [0x20] = "HBM command / address parity error", [0x21] = "HBM data parity error", }; static char *imc_4[] = { [0x00] = "RPQ parity (primary) error", }; static char *imc_8[] = { [0x00] = "DDR-T bad request", [0x01] = "DDR Data response to an invalid entry", [0x02] = "DDR data response to an entry not expecting data", [0x03] = "DDR4 completion to an invalid entry", [0x04] = "DDR-T completion to an invalid entry", [0x05] = "DDR data/completion FIFO overflow", [0x06] = "DDR-T ERID correctable parity error", [0x07] = "DDR-T ERID uncorrectable error", [0x08] = "DDR-T interrupt received while outstanding interrupt was not ACKed", [0x09] = "ERID FI FO overflow", [0x0A] = "DDR-T error on FNV write credits", [0x0B] = "DDR-T error on FNV read credits", [0x0C] = "DDR-T scheduler error", [0x0D] = "DDR-T FNV error event", [0x0E] = "DDR-T FNV thermal event", [0x0F] = "CMI packet while idle", [0x10] = "DDR_T_RPQ_REQ_PARITY_ERR", [0x11] = "DDR_T_WPQ_REQ_PARITY_ERR", [0x12] = "2LM_NMFILLWR_CAM_ERR", [0x13] = "CMI_CREDIT_OVERSUB_ERR", [0x14] = "CMI_CREDIT_TOTAL_ERR", [0x15] = "CMI_CREDIT_RSVD_POOL_ERR", [0x16] = "DDR_T_RD_ERROR", [0x17] = "WDB_FIFO_ERR", [0x18] = "CMI_REQ_FIFO_OVERFLOW", [0x19] = "CMI_REQ_FIFO_UNDERFLOW", [0x1A] = "CMI_RSP_FIFO_OVERFLOW", [0x1B] = "CMI_RSP_FIFO_UNDERFLOW", [0x1C] = "CMI _MISC_MC_CRDT_ERRORS", [0x1D] = "CMI_MISC_MC_ARB_ERRORS", [0x1E] = "DDR_T_WR_CMPL_FI FO_OVERFLOW", [0x1F] = "DDR_T_WR_CMPL_FI FO_UNDERFLOW", [0x20] = "CMI_RD_CPL_FIFO_OVERFLOW", [0x21] = "CMI_RD_CPL_FIFO_UNDERFLOW", [0x22] = "TME_KEY_PAR_ERR", [0x23] = "TME_CMI_MISC_ERR", [0x24] = "TME_CMI_OVFL_ERR", [0x25] = "TME_CMI_UFL_ERR", [0x26] = "TME_TEM_SECURE_ERR", [0x27] = "TME_UFILL_PAR_ERR", [0x29] = "INTERNAL_ERR", [0x2A] = "TME_INTEGRITY_ERR", [0x2B] = "TME_TDX_ERR", [0x2C] = "TME_UFILL_TEM_SECURE_ERR", [0x2D] = "TME_KEY_POISON_ERR", [0x2E] = "TME_SECURITY_ENGINE_ERR", }; static char *imc_10[] = { [0x08] = "CORR_PATSCRUB_MIRR2ND_ERR", [0x10] = "UC_PATSCRUB_MIRR2ND_ERR", [0x20] = "COR_SPARE_MIRR2ND_ERR", [0x40] = "UC_SPARE_MIRR2ND_ERR", [0x80] = "HA_RD_MIRR2ND_ERR", [0xA0] = "HA_UNCORR_RD_MIRR2ND_ERR", }; static struct field imc0[] = { FIELD(0, imc_0), {} }; static struct field imc1[] = { FIELD(0, imc_1), {} }; static struct field imc2[] = { FIELD(0, imc_2), {} }; static struct field imc4[] = { FIELD(0, imc_4), {} }; static struct field imc8[] = { FIELD(0, imc_8), {} }; static struct field imc10[] = { FIELD(0, imc_10), {} }; static void i10nm_imc_misc(struct mce_event *e) { uint32_t column = EXTRACT(e->misc, 9, 18) << 2; uint32_t row = EXTRACT(e->misc, 19, 39); uint32_t bank = EXTRACT(e->misc, 42, 43); uint32_t bankgroup = EXTRACT(e->misc, 40, 41) | (EXTRACT(e->misc, 44, 44) << 2); uint32_t fdevice = EXTRACT(e->misc, 46, 51); uint32_t subrank = EXTRACT(e->misc, 52, 55); uint32_t rank = EXTRACT(e->misc, 56, 58); uint32_t eccmode = EXTRACT(e->misc, 59, 62); uint32_t transient = EXTRACT(e->misc, 63, 63); mce_snprintf(e->error_msg, "bank: 0x%x bankgroup: 0x%x row: 0x%x column: 0x%x", bank, bankgroup, row, column); if (!transient && !EXTRACT(e->status, 61, 61)) mce_snprintf(e->error_msg, "failed device: 0x%x", fdevice); mce_snprintf(e->error_msg, "rank: 0x%x subrank: 0x%x", rank, subrank); mce_snprintf(e->error_msg, "ecc mode: "); switch (eccmode) { case 0: mce_snprintf(e->error_msg, "SDDC memory mode"); break; case 1: mce_snprintf(e->error_msg, "SDDC"); break; case 4: mce_snprintf(e->error_msg, "ADDDC memory mode"); break; case 5: mce_snprintf(e->error_msg, "ADDDC"); break; case 8: mce_snprintf(e->error_msg, "DDRT read"); break; default: mce_snprintf(e->error_msg, "unknown"); break; } if (transient) mce_snprintf(e->error_msg, "transient"); } enum banktype { BT_UNKNOWN, BT_PCU, BT_UPI, BT_M2M, BT_IMC, }; static enum banktype icelake[32] = { [4] = BT_PCU, [5] = BT_UPI, [7 ... 8] = BT_UPI, [12] = BT_M2M, [16] = BT_M2M, [20] = BT_M2M, [24] = BT_M2M, [13 ... 15] = BT_IMC, [17 ... 19] = BT_IMC, [21 ... 23] = BT_IMC, [25 ... 27] = BT_IMC, }; static enum banktype icelake_de[32] = { [4] = BT_PCU, [12] = BT_M2M, [16] = BT_M2M, [13 ... 15] = BT_IMC, [17 ... 19] = BT_IMC, }; static enum banktype tremont[32] = { [4] = BT_PCU, [12] = BT_M2M, [13 ... 15] = BT_IMC, }; static enum banktype sapphire[32] = { [4] = BT_PCU, [5] = BT_UPI, [12] = BT_M2M, [13 ... 20] = BT_IMC, }; void i10nm_memerr_misc(struct mce_event *e, int *channel); void i10nm_decode_model(enum cputype cputype, struct ras_events *ras, struct mce_event *e) { enum banktype banktype; uint64_t f, status = e->status; uint32_t mca = status & 0xffff; int channel = -1; switch (cputype) { case CPU_ICELAKE_XEON: banktype = icelake[e->bank]; break; case CPU_ICELAKE_DE: banktype = icelake_de[e->bank]; break; case CPU_TREMONT_D: banktype = tremont[e->bank]; break; case CPU_SAPPHIRERAPIDS: banktype = sapphire[e->bank]; break; default: return; } switch (banktype) { case BT_UNKNOWN: break; case BT_PCU: mce_snprintf(e->error_msg, "PCU: "); f = EXTRACT(status, 24, 31); if (f) decode_bitfield(e, f, pcu1); f = EXTRACT(status, 20, 23); if (f) decode_bitfield(e, f, pcu2); f = EXTRACT(status, 16, 19); if (f) decode_bitfield(e, f, pcu3); break; case BT_UPI: mce_snprintf(e->error_msg, "UPI: "); f = EXTRACT(status, 22, 31); if (f) decode_bitfield(e, status, upi1); f = EXTRACT(status, 16, 21); decode_bitfield(e, f, upi2); break; case BT_M2M: mce_snprintf(e->error_msg, "M2M: "); f = EXTRACT(status, 24, 25); mce_snprintf(e->error_msg, "MscodDDRType=0x%" PRIx64, f); f = EXTRACT(status, 26, 31); mce_snprintf(e->error_msg, "MscodMiscErrs=0x%" PRIx64, f); decode_bitfield(e, status, m2m); break; case BT_IMC: mce_snprintf(e->error_msg, "MemCtrl: "); f = EXTRACT(status, 16, 23); switch (EXTRACT(status, 24, 31)) { case 0: decode_bitfield(e, f, imc0); break; case 1: decode_bitfield(e, f, imc1); break; case 2: decode_bitfield(e, f, imc2); break; case 4: decode_bitfield(e, f, imc4); break; case 8: decode_bitfield(e, f, imc8); break; case 0x10: decode_bitfield(e, f, imc10); break; } i10nm_imc_misc(e); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (banktype != BT_IMC || (status & MCI_STATUS_UC)) return; /* * Parse the reported channel */ i10nm_memerr_misc(e, &channel); if (channel == -1) return; mce_snprintf(e->mc_location, "memory_channel=%d", channel); } /* * There isn't enough information to identify the DIMM. But * we can derive the channel from the bank number. * There can be four memory controllers with two channels each. */ void i10nm_memerr_misc(struct mce_event *e, int *channel) { uint64_t status = e->status; unsigned int chan, imc; /* Check this is a memory error */ if (!test_prefix(7, status & 0xefff)) return; chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; switch (e->bank) { case 12: /* M2M 0 */ case 13: /* IMC 0, Channel 0 */ case 14: /* IMC 0, Channel 1 */ case 15: /* IMC 0, Channel 2 */ imc = 0; break; case 16: /* M2M 1 */ case 17: /* IMC 1, Channel 0 */ case 18: /* IMC 1, Channel 1 */ case 19: /* IMC 1, Channel 2 */ imc = 1; break; case 20: /* M2M 2 */ case 21: /* IMC 2, Channel 0 */ case 22: /* IMC 2, Channel 1 */ case 23: /* IMC 2, Channel 2 */ imc = 2; break; case 24: /* M2M 3 */ case 25: /* IMC 3, Channel 0 */ case 26: /* IMC 3, Channel 1 */ case 27: /* IMC 3, Channel 2 */ imc = 3; break; default: return; } channel[0] = imc * 3 + chan; } 07070100000034000081A4000000000000000000000001611B97900000133B000000000000000000000000000000000000002F00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-ivb.c/* * The code below came from Tony Luck mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Table 16-17 */ static char *pcu_1[] = { [0] = "No error", [1] = "Non_IMem_Sel", [2] = "I_Parity_Error", [3] = "Bad_OpCode", [4] = "I_Stack_Underflow", [5] = "I_Stack_Overflow", [6] = "D_Stack_Underflow", [7] = "D_Stack_Overflow", [8] = "Non-DMem_Sel", [9] = "D_Parity_Error" }; static char *pcu_2[] = { [0x00] = "No Error", [0x0D] = "MC_IMC_FORCE_SR_S3_TIMEOUT", [0x0E] = "MC_MC_CPD_UNCPD_ST_TIMEOUT", [0x0F] = "MC_PKGS_SAFE_WP_TIMEOUT", [0x43] = "MC_PECI_MAILBOX_QUIESCE_TIMEOUT", [0x44] = "MC_CRITICAL_VR_FAILED", [0x45] = "MC_ICC_MAX-NOTSUPPORTED", [0x5C] = "MC_MORE_THAN_ONE_LT_AGENT", [0x60] = "MC_INVALID_PKGS_REQ_PCH", [0x61] = "MC_INVALID_PKGS_REQ_QPI", [0x62] = "MC_INVALID_PKGS_RES_QPI", [0x63] = "MC_INVALID_PKGC_RES_PCH", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x70] = "MC_WATCHDG_TIMEOUT_PKGC_SLAVE", [0x71] = "MC_WATCHDG_TIMEOUT_PKGC_MASTER", [0x72] = "MC_WATCHDG_TIMEOUT_PKGS_MASTER", [0x7A] = "MC_HA_FAILSTS_CHANGE_DETECTED", [0x7B] = "MC_PCIE_R2PCIE-RW_BLOCK_ACK_TIMEOUT", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT", }; static struct field pcu_mc4[] = { FIELD(16, pcu_1), FIELD(24, pcu_2), {} }; /* See IA32 SDM Vol3B Table 16-18 */ static char *memctrl_1[] = { [0x001] = "Address parity error", [0x002] = "HA Wrt buffer Data parity error", [0x004] = "HA Wrt byte enable parity error", [0x008] = "Corrected patrol scrub error", [0x010] = "Uncorrected patrol scrub error", [0x020] = "Corrected spare error", [0x040] = "Uncorrected spare error", [0x080] = "Corrected memory read error", [0x100] = "iMC, WDB, parity errors", }; static struct field memctrl_mc9[] = { FIELD(16, memctrl_1), {} }; void ivb_decode_model(struct ras_events *ras, struct mce_event *e) { struct mce_priv *mce = ras->mce_priv; uint64_t status = e->status; uint32_t mca = status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan; switch (e->bank) { case 4: // Wprintf("PCU: "); decode_bitfield(e, e->status, pcu_mc4); // Wprintf("\n"); break; case 5: if (mce->cputype == CPU_IVY_BRIDGE_EPEX) { /* MCACOD already decoded */ mce_snprintf(e->bank_name, "QPI"); } break; case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: // Wprintf("MemCtrl: "); decode_bitfield(e, e->status, memctrl_mc9); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 9 || e->bank > 16 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 >= 0 && rank1 >= 0) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 >= 0) mce_snprintf(e->mc_location, "rank=%d", rank0); else mce_snprintf(e->mc_location, "rank=%d", rank1); } /* * Ivy Bridge EP and EX processors (family 6, model 62) support additional * logging for corrected errors in the integrated memory controller (IMC) * banks. The mode is off by default, but can be enabled by setting the * "MemError Log Enable" * bit in MSR_ERROR_CONTROL (MSR 0x17f). * The SDM leaves it as an exercise for the reader to convert the * faling rank to a DIMM slot. */ #if 0 static int failrank2dimm(unsigned failrank, int socket, int channel) { switch (failrank) { case 0: case 1: case 2: case 3: return 0; case 4: case 5: return 1; case 6: case 7: if (get_memdimm(socket, channel, 2, 0)) return 2; else return 1; } return -1; } #endif 07070100000035000081A4000000000000000000000001611B979000000E9D000000000000000000000000000000000000002F00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-knl.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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" static struct field memctrl_mc7[] = { SBITFIELD(16, "CA Parity error"), SBITFIELD(17, "Internal Parity error except WDB"), SBITFIELD(18, "Internal Parity error from WDB"), SBITFIELD(19, "Correctable Patrol Scrub"), SBITFIELD(20, "Uncorrectable Patrol Scrub"), SBITFIELD(21, "Spare Correctable Error"), SBITFIELD(22, "Spare UC Error"), SBITFIELD(23, "CORR Chip fail even MC only, 4 bit burst error EDC only"), {} }; void knl_decode_model(struct ras_events *ras, struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan = 0; switch (e->bank) { case 5: switch (EXTRACT(status, 0, 15)) { case 0x402: mce_snprintf(e->mcastatus_msg, "PCU Internal Errors"); break; case 0x403: mce_snprintf(e->mcastatus_msg, "VCU Internal Errors"); break; case 0x407: mce_snprintf(e->mcastatus_msg, "Other UBOX Internal Errors"); break; } break; case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: if ((EXTRACT(status, 0, 15)) == 0x5) { mce_snprintf(e->mcastatus_msg, "Internal Parity error"); } else { chan = (EXTRACT(status, 0, 3)) + 3 * (e->bank == 15); switch (EXTRACT(status, 4, 7)) { case 0x0: mce_snprintf(e->mcastatus_msg, "Undefined request on channel %d", chan); break; case 0x1: mce_snprintf(e->mcastatus_msg, "Read on channel %d", chan); break; case 0x2: mce_snprintf(e->mcastatus_msg, "Write on channel %d", chan); break; case 0x3: mce_snprintf(e->mcastatus_msg, "CA error on channel %d", chan); break; case 0x4: mce_snprintf(e->mcastatus_msg, "Scrub error on channel %d", chan); break; } } decode_bitfield(e, status, memctrl_mc7); break; default: break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 7 || e->bank > 16 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(status, 0, 3); if (chan == 0xf) { mce_snprintf(e->mc_location, "memory_channel=unspecified"); } else { chan = chan + 3 * (e->bank == 15); mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 != -1 && rank1 != -1) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 != -1) mce_snprintf(e->mc_location, "rank=%d", rank0); } } 07070100000036000081A4000000000000000000000001611B979000001402000000000000000000000000000000000000003300000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-nehalem.c/* * The code below came from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Appendix E.3.2 ff */ /* MC1_STATUS error */ static struct field qpi_status[] = { SBITFIELD(16, "QPI header had bad parity"), SBITFIELD(17, "QPI Data packet had bad parity"), SBITFIELD(18, "Number of QPI retries exceeded"), SBITFIELD(19, "Received QPI data packet that was poisoned by sender"), SBITFIELD(20, "QPI reserved 20"), SBITFIELD(21, "QPI reserved 21"), SBITFIELD(22, "QPI received unsupported message encoding"), SBITFIELD(23, "QPI credit type is not supported"), SBITFIELD(24, "Sender sent too many QPI flits to the receiver"), SBITFIELD(25, "QPI Sender sent a failed response to receiver"), SBITFIELD(26, "Clock jitter detected in internal QPI clocking"), {} }; static struct field qpi_misc[] = { SBITFIELD(14, "QPI misc reserved 14"), SBITFIELD(15, "QPI misc reserved 15"), SBITFIELD(24, "QPI Interleave/Head Indication Bit (IIB)"), {} }; static struct numfield qpi_numbers[] = { HEXNUMBER(0, 7, "QPI class and opcode of packet with error"), HEXNUMBER(8, 13, "QPI Request Transaction ID"), NUMBERFORCE(16, 18, "QPI Requestor/Home Node ID (RHNID)"), HEXNUMBER(19, 23, "QPI miscreserved 19-23"), {}, }; static struct field nhm_memory_status[] = { SBITFIELD(16, "Memory read ECC error"), SBITFIELD(17, "Memory ECC error occurred during scrub"), SBITFIELD(18, "Memory write parity error"), SBITFIELD(19, "Memory error in half of redundant memory"), SBITFIELD(20, "Memory reserved 20"), SBITFIELD(21, "Memory access out of range"), SBITFIELD(22, "Memory internal RTID invalid"), SBITFIELD(23, "Memory address parity error"), SBITFIELD(24, "Memory byte enable parity error"), {} }; static struct numfield nhm_memory_status_numbers[] = { HEXNUMBER(25, 37, "Memory MISC reserved 25..37"), NUMBERFORCE(38, 52, "Memory corrected error count (CORE_ERR_CNT)"), HEXNUMBER(53, 56, "Memory MISC reserved 53..56"), {} }; static struct numfield nhm_memory_misc_numbers[] = { HEXNUMBERFORCE(0, 7, "Memory transaction Tracker ID (RTId)"), NUMBERFORCE(16, 17, "Memory DIMM ID of error"), NUMBERFORCE(18, 19, "Memory channel ID of error"), HEXNUMBERFORCE(32, 63, "Memory ECC syndrome"), {} }; static char *internal_errors[] = { [0x0] = "No Error", [0x3] = "Reset firmware did not complete", [0x8] = "Received an invalid CMPD", [0xa] = "Invalid Power Management Request", [0xd] = "Invalid S-state transition", [0x11] = "VID controller does not match POC controller selected", [0x1a] = "MSID from POC does not match CPU MSID", }; static struct field internal_error_status[] = { FIELD(24, internal_errors), {} }; static struct numfield internal_error_numbers[] = { HEXNUMBER(16, 23, "Internal machine check status reserved 16..23"), HEXNUMBER(32, 56, "Internal machine check status reserved 32..56"), {}, }; /* Generic architectural memory controller encoding */ void nehalem_decode_model(struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; uint64_t misc = e->misc; unsigned channel, dimm; if ((mca >> 11) == 1) { /* bus and interconnect QPI */ decode_bitfield(e, status, qpi_status); if (status & MCI_STATUS_MISCV) { decode_numfield(e, misc, qpi_numbers); decode_bitfield(e, misc, qpi_misc); } } else if (mca == 0x0001) { /* internal unspecified */ decode_bitfield(e, status, internal_error_status); decode_numfield(e, status, internal_error_numbers); } else if ((mca >> 7) == 1) { /* memory controller */ decode_bitfield(e, status, nhm_memory_status); decode_numfield(e, status, nhm_memory_status_numbers); if (status & MCI_STATUS_MISCV) decode_numfield(e, misc, nhm_memory_misc_numbers); } if ((((status & 0xffff) >> 7) == 1) && (status & MCI_STATUS_MISCV)) { channel = EXTRACT(e->misc, 18, 19); dimm = EXTRACT(e->misc, 16, 17); mce_snprintf(e->mc_location, "channel=%d, dimm=%d", channel, dimm); } } /* Only core errors supported. Same as Nehalem */ void xeon75xx_decode_model(struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; if (mca == 0x0001) { /* internal unspecified */ decode_bitfield(e, status, internal_error_status); decode_numfield(e, status, internal_error_numbers); } } 07070100000037000081A4000000000000000000000001611B979000001116000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-p4-p6.c/* * The code below came from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* Decode P4 and P6 family (p6old and Core2) model specific errors */ /* [19..24] */ static char *bus_queue_req_type[] = { [0] = "BQ_DCU_READ_TYPE", [2] = "BQ_IFU_DEMAND_TYPE", [3] = "BQ_IFU_DEMAND_NC_TYPE", [4] = "BQ_DCU_RFO_TYPE", [5] = "BQ_DCU_RFO_LOCK_TYPE", [6] = "BQ_DCU_ITOM_TYPE", [8] = "BQ_DCU_WB_TYPE", [10] = "BC_DCU_WCEVICT_TYPE", [11] = "BQ_DCU_WCLINE_TYPE", [12] = "BQ_DCU_BTM_TYPE", [13] = "BQ_DCU_INTACK_TYPE", [14] = "BQ_DCU_INVALL2_TYPE", [15] = "BQ_DCU_FLUSHL2_TYPE", [16] = "BQ_DCU_PART_RD_TYPE", [18] = "BQ_DCU_PART_WR_TYPE", [20] = "BQ_DCU_SPEC_CYC_TYPE", [24] = "BQ_DCU_IO_RD_TYPE", [25] = "BQ_DCU_IO_WR_TYPE", [28] = "BQ_DCU_LOCK_RD_TYPE", [30] = "BQ_DCU_SPLOCK_RD_TYPE", [29] = "BQ_DCU_LOCK_WR_TYPE", }; /* [25..27] */ static char *bus_queue_error_type[] = { [0] = "BQ_ERR_HARD_TYPE", [1] = "BQ_ERR_DOUBLE_TYPE", [2] = "BQ_ERR_AERR2_TYPE", [4] = "BQ_ERR_SINGLE_TYPE", [5] = "BQ_ERR_AERR1_TYPE", }; static struct field p6_shared_status[] = { FIELD_NULL(16), FIELD(19, bus_queue_req_type), FIELD(25, bus_queue_error_type), FIELD(25, bus_queue_error_type), SBITFIELD(30, "internal BINIT"), SBITFIELD(36, "received parity error on response transaction"), SBITFIELD(38, "timeout BINIT (ROB timeout)." " No micro-instruction retired for some time"), FIELD_NULL(39), SBITFIELD(42, "bus transaction received hard error response"), SBITFIELD(43, "failure that caused IERR"), /* The following are reserved for Core in the SDM. Let's keep them here anyways*/ SBITFIELD(44, "two failing bus transactions with address parity error (AERR)"), SBITFIELD(45, "uncorrectable ECC error"), SBITFIELD(46, "correctable ECC error"), /* [47..54]: ECC syndrome */ FIELD_NULL(55), {}, }; static struct field p6old_status[] = { SBITFIELD(28, "FRC error"), SBITFIELD(29, "BERR on this CPU"), FIELD_NULL(31), FIELD_NULL(32), SBITFIELD(35, "BINIT received from external bus"), SBITFIELD(37, "Received hard error reponse on split transaction (Bus BINIT)"), {} }; static struct field core2_status[] = { SBITFIELD(28, "MCE driven"), SBITFIELD(29, "MCE is observed"), SBITFIELD(31, "BINIT observed"), FIELD_NULL(32), SBITFIELD(34, "PIC or FSB data parity error"), FIELD_NULL(35), SBITFIELD(37, "FSB address parity error detected"), {} }; static struct numfield p6old_status_numbers[] = { HEXNUMBER(47, 54, "ECC syndrome"), {} }; static struct { int value; char *str; } p4_model []= { {16, "FSB address parity"}, {17, "Response hard fail"}, {18, "Response parity"}, {19, "PIC and FSB data parity"}, {20, "Invalid PIC request(Signature=0xF04H)"}, {21, "Pad state machine"}, {22, "Pad strobe glitch"}, {23, "Pad address glitch"} }; void p4_decode_model(struct mce_event *e) { uint32_t model = e->status & 0xffff0000L; unsigned i; for (i = 0; i < ARRAY_SIZE(p4_model); i++) { if (model & (1 << p4_model[i].value)) mce_snprintf(e->error_msg, "%s", p4_model[i].str); } } void core2_decode_model(struct mce_event *e) { uint64_t status = e->status; decode_bitfield(e, status, p6_shared_status); decode_bitfield(e, status, core2_status); /* Normally reserved, but let's parse anyways: */ decode_numfield(e, status, p6old_status_numbers); } void p6old_decode_model(struct mce_event *e) { uint64_t status = e->status; decode_bitfield(e, status, p6_shared_status); decode_bitfield(e, status, p6old_status); decode_numfield(e, status, p6old_status_numbers); } 07070100000038000081A4000000000000000000000001611B979000001416000000000000000000000000000000000000002E00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-sb.c/* * The code below came from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Table 16.4.1 */ static char *pcu_1[] = { [0] = "No error", [1] = "Non_IMem_Sel", [2] = "I_Parity_Error", [3] = "Bad_OpCode", [4] = "I_Stack_Underflow", [5] = "I_Stack_Overflow", [6] = "D_Stack_Underflow", [7] = "D_Stack_Overflow", [8] = "Non-DMem_Sel", [9] = "D_Parity_Error" }; static char *pcu_2[] = { [0x00] = "No Error", [0x0D] = "MC_IMC_FORCE_SR_S3_TIMEOUT", [0x0E] = "MC_MC_CPD_UNCPD_ST_TIMEOUT", [0x0F] = "MC_PKGS_SAFE_WP_TIMEOUT", [0x43] = "MC_PECI_MAILBOX_QUIESCE_TIMEOUT", [0x5C] = "MC_MORE_THAN_ONE_LT_AGENT", [0x60] = "MC_INVALID_PKGS_REQ_PCH", [0x61] = "MC_INVALID_PKGS_REQ_QPI", [0x62] = "MC_INVALID_PKGS_RES_QPI", [0x63] = "MC_INVALID_PKGC_RES_PCH", [0x64] = "MC_INVALID_PKG_STATE_CONFIG", [0x70] = "MC_WATCHDG_TIMEOUT_PKGC_SLAVE", [0x71] = "MC_WATCHDG_TIMEOUT_PKGC_MASTER", [0x72] = "MC_WATCHDG_TIMEOUT_PKGS_MASTER", [0x7A] = "MC_HA_FAILSTS_CHANGE_DETECTED", [0x81] = "MC_RECOVERABLE_DIE_THERMAL_TOO_HOT", }; static struct field pcu_mc4[] = { FIELD(16, pcu_1), FIELD(24, pcu_2), {} }; static char *memctrl_1[] = { [0x001] = "Address parity error", [0x002] = "HA Wrt buffer Data parity error", [0x004] = "HA Wrt byte enable parity error", [0x008] = "Corrected patrol scrub error", [0x010] = "Uncorrected patrol scrub error", [0x020] = "Corrected spare error", [0x040] = "Uncorrected spare error", }; static struct field memctrl_mc8[] = { FIELD(16, memctrl_1), {} }; void snb_decode_model(struct ras_events *ras, struct mce_event *e) { struct mce_priv *mce = ras->mce_priv; uint32_t mca = e->status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan; switch (e->bank) { case 4: decode_bitfield(e, e->status, pcu_mc4); break; case 6: case 7: /* MCACOD already decoded */ if (mce->cputype == CPU_SANDY_BRIDGE_EP) mce_snprintf(e->bank_name, "QPI"); break; case 8: case 9: case 10: case 11: // Wprintf("MemCtrl: "); decode_bitfield(e, e->status, memctrl_mc8); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 8 || e->bank > 11 || (e->status & MCI_STATUS_UC) || !test_prefix(7, e->status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(e->status, 0, 3); if (chan == 0xf) return; mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 >= 0 && rank1 >= 0) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 >= 0) mce_snprintf(e->mc_location, "rank=%d", rank0); else mce_snprintf(e->mc_location, "rank=%d", rank1); } #if 0 /* * Sandy Bridge EP and EP4S processors (family 6, model 45) support additional * logging for corrected errors in the integrated memory controller (IMC) * banks. The mode is off by default, but can be enabled by setting the * "MemError Log Enable" * bit in MSR_ERROR_CONTROL (MSR 0x17f). * The documentation in the August 2012 edition of Intel's Software developer * manual has some minor errors because the worng version of table 16-16 * "Intel IMC MC Error Codes for IA32_MCi_MISC (i= 8, 11)" was included. * Corrections are: * Bit 62 is the "VALID" bit for the "first-device" bits in MISC and STATUS * Bit 63 is the "VALID" bit for the "second-device" bits in MISC * Bits 58:56 and 61:59 should be marked as "reserved". * There should also be a footnote explaining how the "failing rank" fields * can be converted to a DIMM number within a channel for systems with either * two or three DIMMs per channel. */ static int failrank2dimm(unsigned failrank, int socket, int channel) { switch (failrank) { case 0: case 1: case 2: case 3: return 0; case 4: case 5: return 1; case 6: case 7: if (get_memdimm(socket, channel, 2, 0)) return 2; else return 1; } return -1; } #endif 07070100000039000081A4000000000000000000000001611B979000001FAF000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-skylake-xeon.c/* * The code below came from Tony Luck's mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Table 16-27 */ static char *pcu_1[] = { [0x00] = "No Error", [0x0d] = "MCA_DMI_TRAINING_TIMEOUT", [0x0f] = "MCA_DMI_CPU_RESET_ACK_TIMEOUT", [0x10] = "MCA_MORE_THAN_ONE_LT_AGENT", [0x1e] = "MCA_BIOS_RST_CPL_INVALID_SEQ", [0x1f] = "MCA_BIOS_INVALID_PKG_STATE_CONFIG", [0x25] = "MCA_MESSAGE_CHANNEL_TIMEOUT", [0x27] = "MCA_MSGCH_PMREQ_CMP_TIMEOUT", [0x30] = "MCA_PKGC_DIRECT_WAKE_RING_TIMEOUT", [0x31] = "MCA_PKGC_INVALID_RSP_PCH", [0x33] = "MCA_PKGC_WATCHDOG_HANG_CBZ_DOWN", [0x34] = "MCA_PKGC_WATCHDOG_HANG_CBZ_UP", [0x38] = "MCA_PKGC_WATCHDOG_HANG_C3_UP_SF", [0x40] = "MCA_SVID_VCCIN_VR_ICC_MAX_FAILURE", [0x41] = "MCA_SVID_COMMAND_TIMEOUT", [0x42] = "MCA_SVID_VCCIN_VR_VOUT_FAILURE", [0x43] = "MCA_SVID_CPU_VR_CAPABILITY_ERROR", [0x44] = "MCA_SVID_CRITICAL_VR_FAILED", [0x45] = "MCA_SVID_SA_ITD_ERROR", [0x46] = "MCA_SVID_READ_REG_FAILED", [0x47] = "MCA_SVID_WRITE_REG_FAILED", [0x48] = "MCA_SVID_PKGC_INIT_FAILED", [0x49] = "MCA_SVID_PKGC_CONFIG_FAILED", [0x4a] = "MCA_SVID_PKGC_REQUEST_FAILED", [0x4b] = "MCA_SVID_IMON_REQUEST_FAILED", [0x4c] = "MCA_SVID_ALERT_REQUEST_FAILED", [0x4d] = "MCA_SVID_MCP_VR_ABSENT_OR_RAMP_ERROR", [0x4e] = "MCA_SVID_UNEXPECTED_MCP_VR_DETECTED", [0x51] = "MCA_FIVR_CATAS_OVERVOL_FAULT", [0x52] = "MCA_FIVR_CATAS_OVERCUR_FAULT", [0x58] = "MCA_WATCHDOG_TIMEOUT_PKGC_SLAVE", [0x59] = "MCA_WATCHDOG_TIMEOUT_PKGC_MASTER", [0x5a] = "MCA_WATCHDOG_TIMEOUT_PKGS_MASTER", [0x61] = "MCA_PKGS_CPD_UNCPD_TIMEOUT", [0x63] = "MCA_PKGS_INVALID_REQ_PCH", [0x64] = "MCA_PKGS_INVALID_REQ_INTERNAL", [0x65] = "MCA_PKGS_INVALID_RSP_INTERNAL", [0x6b] = "MCA_PKGS_SMBUS_VPP_PAUSE_TIMEOUT", [0x81] = "MCA_RECOVERABLE_DIE_THERMAL_TOO_HOT", }; static struct field pcu_mc4[] = { FIELD(24, pcu_1), {} }; /* See IA32 SDM Vol3B Table 16-28 */ static char *upi[] = { [0x00] = "UC Phy Initialization Failure", [0x01] = "UC Phy detected drift buffer alarm", [0x02] = "UC Phy detected latency buffer rollover", [0x10] = "UC LL Rx detected CRC error: unsuccessful LLR: entered abort state", [0x11] = "UC LL Rx unsupported or undefined packet", [0x12] = "UC LL or Phy control error", [0x13] = "UC LL Rx parameter exchange exception", [0x1F] = "UC LL detected control error from the link-mesh interface", [0x20] = "COR Phy initialization abort", [0x21] = "COR Phy reset", [0x22] = "COR Phy lane failure, recovery in x8 width", [0x23] = "COR Phy L0c error corrected without Phy reset", [0x24] = "COR Phy L0c error triggering Phy Reset", [0x25] = "COR Phy L0p exit error corrected with Phy reset", [0x30] = "COR LL Rx detected CRC error - successful LLR without Phy Reinit", [0x31] = "COR LL Rx detected CRC error - successful LLR with Phy Reinit", }; static struct field upi_mc[] = { FIELD(16, upi), {} }; /* These apply to MSCOD 0x12 "UC LL or Phy control error" */ static struct field upi_0x12[] = { SBITFIELD(22, "Phy Control Error"), SBITFIELD(23, "Unexpected Retry.Ack flit"), SBITFIELD(24, "Unexpected Retry.Req flit"), SBITFIELD(25, "RF parity error"), SBITFIELD(26, "Routeback Table error"), SBITFIELD(27, "unexpected Tx Protocol flit (EOP, Header or Data)"), SBITFIELD(28, "Rx Header-or-Credit BGF credit overflow/underflow"), SBITFIELD(29, "Link Layer Reset still in progress when Phy enters L0"), SBITFIELD(30, "Link Layer reset initiated while protocol traffic not idle"), SBITFIELD(31, "Link Layer Tx Parity Error"), {} }; /* See IA32 SDM Vol3B Table 16-29 */ static struct field mc_bits[] = { SBITFIELD(16, "Address parity error"), SBITFIELD(17, "HA write data parity error"), SBITFIELD(18, "HA write byte enable parity error"), SBITFIELD(19, "Corrected patrol scrub error"), SBITFIELD(20, "Uncorrected patrol scrub error"), SBITFIELD(21, "Corrected spare error"), SBITFIELD(22, "Uncorrected spare error"), SBITFIELD(23, "Any HA read error"), SBITFIELD(24, "WDB read parity error"), SBITFIELD(25, "DDR4 command address parity error"), SBITFIELD(26, "Uncorrected address parity error"), {} }; static char *mc_0x8xx[] = { [0x0] = "Unrecognized request type", [0x1] = "Read response to an invalid scoreboard entry", [0x2] = "Unexpected read response", [0x3] = "DDR4 completion to an invalid scoreboard entry", [0x4] = "Completion to an invalid scoreboard entry", [0x5] = "Completion FIFO overflow", [0x6] = "Correctable parity error", [0x7] = "Uncorrectable error", [0x8] = "Interrupt received while outstanding interrupt was not ACKed", [0x9] = "ERID FIFO overflow", [0xa] = "Error on Write credits", [0xb] = "Error on Read credits", [0xc] = "Scheduler error", [0xd] = "Error event", }; static struct field memctrl_mc13[] = { FIELD(16, mc_0x8xx), {} }; /* See IA32 SDM Vol3B Table 16-30 */ static struct field m2m[] = { SBITFIELD(16, "MscodDataRdErr"), SBITFIELD(17, "Reserved"), SBITFIELD(18, "MscodPtlWrErr"), SBITFIELD(19, "MscodFullWrErr"), SBITFIELD(20, "MscodBgfErr"), SBITFIELD(21, "MscodTimeout"), SBITFIELD(22, "MscodParErr"), SBITFIELD(23, "MscodBucket1Err"), {} }; void skylake_s_decode_model(struct ras_events *ras, struct mce_event *e) { uint64_t status = e->status; uint32_t mca = status & 0xffff; unsigned rank0 = -1, rank1 = -1, chan; switch (e->bank) { case 4: switch (EXTRACT(status, 0, 15) & ~(1ull << 12)) { case 0x402: case 0x403: mce_snprintf(e->mcastatus_msg, "Internal errors "); break; case 0x406: mce_snprintf(e->mcastatus_msg, "Intel TXT errors "); break; case 0x407: mce_snprintf(e->mcastatus_msg, "Other UBOX Internal errors "); break; } if (EXTRACT(status, 16, 19)) mce_snprintf(e->mcastatus_msg, "PCU internal error "); decode_bitfield(e, status, pcu_mc4); break; case 5: case 12: case 19: mce_snprintf(e->mcastatus_msg, "UPI: "); decode_bitfield(e, status, upi_mc); if (EXTRACT(status, 16, 21) == 0x12) decode_bitfield(e, status, upi_0x12); break; case 7: case 8: mce_snprintf(e->mcastatus_msg, "M2M: "); decode_bitfield(e, status, m2m); break; case 13: case 14: case 15: case 16: case 17: case 18: mce_snprintf(e->mcastatus_msg, "MemCtrl: "); if (EXTRACT(status, 27, 27)) decode_bitfield(e, status, memctrl_mc13); else decode_bitfield(e, status, mc_bits); break; } /* * Memory error specific code. Returns if the error is not a MC one */ /* Check if the error is at the memory controller */ if ((mca >> 7) != 1) return; /* Ignore unless this is an corrected extended error from an iMC bank */ if (e->bank < 13 || e->bank > 18 || (status & MCI_STATUS_UC) || !test_prefix(7, status & 0xefff)) return; /* * Parse the reported channel and ranks */ chan = EXTRACT(status, 0, 3); if (chan == 0xf) return; mce_snprintf(e->mc_location, "memory_channel=%d", chan); if (EXTRACT(e->misc, 62, 62)) { rank0 = EXTRACT(e->misc, 46, 50); if (EXTRACT(e->misc, 63, 63)) rank1 = EXTRACT(e->misc, 51, 55); } /* * FIXME: The conversion from rank to dimm requires to parse the * DMI tables and call failrank2dimm(). */ if (rank0 != -1 && rank1 != -1) mce_snprintf(e->mc_location, "ranks=%d and %d", rank0, rank1); else if (rank0 != -1) mce_snprintf(e->mc_location, "rank=%d", rank0); } 0707010000003A000081A4000000000000000000000001611B979000001163000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel-tulsa.c/* * The code below came from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> #include <stdio.h> #include "ras-mce-handler.h" #include "bitfield.h" /* See IA32 SDM Vol3B Appendix E.4.1 ff */ static struct numfield corr_numbers[] = { NUMBER(32, 39, "Corrected events"), {} }; static struct numfield ecc_numbers[] = { HEXNUMBER(44, 51, "ECC syndrome"), {}, }; static struct field tls_bus_status[] = { SBITFIELD(16, "Parity error detected during FSB request phase"), SBITFIELD(17, "Partity error detected on Core 0 request's address field"), SBITFIELD(18, "Partity error detected on Core 1 request's address field"), FIELD_NULL(19), SBITFIELD(20, "Parity error on FSB response field detected"), SBITFIELD(21, "FSB data parity error on inbound date detected"), SBITFIELD(22, "Data parity error on data received from Core 0 detected"), SBITFIELD(23, "Data parity error on data received from Core 1 detected"), SBITFIELD(24, "Detected an Enhanced Defer parity error phase A or phase B"), SBITFIELD(25, "Data ECC event to error on inbound data correctable or uncorrectable"), SBITFIELD(26, "Pad logic detected a data strobe glitch or sequencing error"), SBITFIELD(27, "Pad logic detected a request strobe glitch or sequencing error"), FIELD_NULL(28), FIELD_NULL(31), {} }; static char *tls_front_error[0xf] = { [0x1] = "Inclusion error from core 0", [0x2] = "Inclusion error from core 1", [0x3] = "Write Exclusive error from core 0", [0x4] = "Write Exclusive error from core 1", [0x5] = "Inclusion error from FSB", [0x6] = "SNP stall error from FSB", [0x7] = "Write stall error from FSB", [0x8] = "FSB Arbiter Timeout error", [0x9] = "CBC OOD Queue Underflow/overflow", }; static char *tls_int_error[0xf] = { [0x1] = "Enhanced Intel SpeedStep Technology TM1-TM2 Error", [0x2] = "Internal timeout error", [0x3] = "Internal timeout error", [0x4] = "Intel Cache Safe Technology Queue full error\n" "or disabled ways in a set overflow", }; struct field tls_int_status[] = { FIELD(8, tls_int_error), {} }; struct field tls_front_status[] = { FIELD(0, tls_front_error), {} }; struct field tls_cecc[] = { SBITFIELD(0, "Correctable ECC event on outgoing FSB data"), SBITFIELD(1, "Correctable ECC event on outgoing core 0 data"), SBITFIELD(2, "Correctable ECC event on outgoing core 1 data"), {} }; struct field tls_uecc[] = { SBITFIELD(0, "Uncorrectable ECC event on outgoing FSB data"), SBITFIELD(1, "Uncorrectable ECC event on outgoing core 0 data"), SBITFIELD(2, "Uncorrectable ECC event on outgoing core 1 data"), {} }; static void tulsa_decode_bus(struct mce_event *e, uint64_t status) { decode_bitfield(e, status, tls_bus_status); } static void tulsa_decode_internal(struct mce_event *e, uint64_t status) { uint32_t mca = (status >> 16) & 0xffff; if ((mca & 0xfff0) == 0) decode_bitfield(e, mca, tls_front_status); else if ((mca & 0xf0ff) == 0) decode_bitfield(e, mca, tls_int_status); else if ((mca & 0xfff0) == 0xc000) decode_bitfield(e, mca, tls_cecc); else if ((mca & 0xfff0) == 0xe000) decode_bitfield(e, mca, tls_uecc); } void tulsa_decode_model(struct mce_event *e) { decode_numfield(e, e->status, corr_numbers); if (e->status & (1ULL << 52)) decode_numfield(e, e->status, ecc_numbers); /* MISC register not documented in the SDM. Let's just dump hex for now. */ if (e->status & MCI_STATUS_MISCV) mce_snprintf(e->mcistatus_msg, "MISC format %llx value %llx\n", (long long)(e->status >> 40) & 3, (long long)e->misc); if ((e->status & 0xffff) == 0xe0f) tulsa_decode_bus(e, e->status); else if ((e->status & 0xffff) == (1 << 10)) tulsa_decode_internal(e, e->status); } 0707010000003B000081A4000000000000000000000001611B979000002F25000000000000000000000000000000000000002B00000000rasdaemon-0.6.7.18.git+7ccf12f/mce-intel.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * The code below were adapted from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <errno.h> #include <fcntl.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include "ras-logger.h" #include "ras-mce-handler.h" #include "bitfield.h" #define MCE_THERMAL_BANK (MCE_EXTENDED_BANK + 0) #define MCE_TIMEOUT_BANK (MCE_EXTENDED_BANK + 90) #define TLB_LL_MASK 0x3 /*bit 0, bit 1*/ #define TLB_LL_SHIFT 0x0 #define TLB_TT_MASK 0xc /*bit 2, bit 3*/ #define TLB_TT_SHIFT 0x2 #define CACHE_LL_MASK 0x3 /*bit 0, bit 1*/ #define CACHE_LL_SHIFT 0x0 #define CACHE_TT_MASK 0xc /*bit 2, bit 3*/ #define CACHE_TT_SHIFT 0x2 #define CACHE_RRRR_MASK 0xF0 /*bit 4, bit 5, bit 6, bit 7 */ #define CACHE_RRRR_SHIFT 0x4 #define BUS_LL_MASK 0x3 /* bit 0, bit 1*/ #define BUS_LL_SHIFT 0x0 #define BUS_II_MASK 0xc /*bit 2, bit 3*/ #define BUS_II_SHIFT 0x2 #define BUS_RRRR_MASK 0xF0 /*bit 4, bit 5, bit 6, bit 7 */ #define BUS_RRRR_SHIFT 0x4 #define BUS_T_MASK 0x100 /*bit 8*/ #define BUS_T_SHIFT 0x8 #define BUS_PP_MASK 0x600 /*bit 9, bit 10*/ #define BUS_PP_SHIFT 0x9 #define MCG_TES_P (1ULL<<11) /* Yellow bit cache threshold supported */ static char *TT[] = { "Instruction", "Data", "Generic", "Unknown" }; static char *LL[] = { "Level-0", "Level-1", "Level-2", "Level-3" }; static struct { uint8_t value; char* str; } RRRR [] = { {0, "Generic"}, {1, "Read"}, {2, "Write" }, {3, "Data-Read"}, {4, "Data-Write"}, {5, "Instruction-Fetch"}, {6, "Prefetch"}, {7, "Eviction"}, {8, "Snoop"} }; static char *PP[] = { "Local-CPU-originated-request", "Responed-to-request", "Observed-error-as-third-party", "Generic" }; static char *T[] = { "Request-did-not-timeout", "Request-timed-out" }; static char *II[] = { "Memory-access", "Reserved", "IO", "Other-transaction" }; static char *mca_msg[] = { [0] = "No Error", [1] = "Unclassified", [2] = "Microcode ROM parity error", [3] = "External error", [4] = "FRC error", [5] = "Internal parity error", [6] = "SMM Handler Code Access Violation", }; static char *tracking_msg[] = { [1] = "green", [2] = "yellow", [3] ="res3" }; static const char *arstate[4] = { [0] = "UCNA", [1] = "AR", [2] = "SRAO", [3] = "SRAR" }; static char *mmm_mnemonic[] = { "GEN", "RD", "WR", "AC", "MS", "RES5", "RES6", "RES7" }; static char *mmm_desc[] = { "Generic undefined request", "Memory read error", "Memory write error", "Address/Command error", "Memory scrubbing error", "Reserved 5", "Reserved 6", "Reserved 7" }; static void decode_memory_controller(struct mce_event *e, uint32_t status) { char channel[30]; if ((status & 0xf) == 0xf) sprintf(channel, "unspecified"); else sprintf(channel, "%u", status & 0xf); mce_snprintf(e->error_msg, "MEMORY CONTROLLER %s_CHANNEL%s_ERR", mmm_mnemonic[(status >> 4) & 7], channel); mce_snprintf(e->error_msg, "Transaction: %s", mmm_desc[(status >> 4) & 7]); } static void decode_termal_bank(struct mce_event *e) { if (e->status & 1) { mce_snprintf(e->mcgstatus_msg, "Processor %d heated above trip temperature. Throttling enabled.", e->cpu); mce_snprintf(e->user_action, "Please check your system cooling. Performance will be impacted"); } else { mce_snprintf(e->error_msg, "Processor %d below trip temperature. Throttling disabled", e->cpu); } } static void decode_mcg(struct mce_event *e) { uint64_t mcgstatus = e->mcgstatus; mce_snprintf(e->mcgstatus_msg, "mcgstatus=%lld", (long long)e->mcgstatus); if (mcgstatus & MCG_STATUS_RIPV) mce_snprintf(e->mcgstatus_msg, "RIPV"); if (mcgstatus & MCG_STATUS_EIPV) mce_snprintf(e->mcgstatus_msg, "EIPV"); if (mcgstatus & MCG_STATUS_MCIP) mce_snprintf(e->mcgstatus_msg, "MCIP"); if (mcgstatus & MCG_STATUS_LMCE) mce_snprintf(e->mcgstatus_msg, "LMCE"); } static void bank_name(struct mce_event *e) { char *buf = e->bank_name; switch (e->bank) { case MCE_THERMAL_BANK: strcpy(buf, "THERMAL EVENT"); break; case MCE_TIMEOUT_BANK: strcpy(buf, "Timeout waiting for exception on other CPUs"); break; default: break; } } static char *get_RRRR_str(uint8_t rrrr) { unsigned i; for (i = 0; i < ARRAY_SIZE(RRRR); i++) { if (RRRR[i].value == rrrr) { return RRRR[i].str; } } return "UNKNOWN"; } #define decode_attr(arr, val) ({ \ char *__str; \ if ((unsigned)(val) >= ARRAY_SIZE(arr)) \ __str = "UNKNOWN"; \ else \ __str = (arr)[val]; \ __str; \ }) static void decode_mca(struct mce_event *e, uint64_t track, int *ismemerr) { uint32_t mca = e->status & 0xffffL; if (mca & (1UL << 12)) { mce_snprintf(e->mcastatus_msg, "corrected filtering (some unreported errors in same region)"); mca &= ~(1UL << 12); } if (mca < ARRAY_SIZE(mca_msg)) { mce_snprintf(e->mcastatus_msg, "%s", mca_msg[mca]); return; } if ((mca >> 2) == 3) { mce_snprintf(e->mcastatus_msg, "%s Generic memory hierarchy error", decode_attr(LL, mca & 3)); } else if (test_prefix(4, mca)) { mce_snprintf(e->mcastatus_msg, "%s TLB %s Error", decode_attr(TT, (mca & TLB_TT_MASK) >> TLB_TT_SHIFT), decode_attr(LL, (mca & TLB_LL_MASK) >> TLB_LL_SHIFT)); } else if (test_prefix(8, mca)) { unsigned typenum = (mca & CACHE_TT_MASK) >> CACHE_TT_SHIFT; unsigned levelnum = (mca & CACHE_LL_MASK) >> CACHE_LL_SHIFT; char *type = decode_attr(TT, typenum); char *level = decode_attr(LL, levelnum); mce_snprintf(e->mcastatus_msg, "%s CACHE %s %s Error", type, level, get_RRRR_str((mca & CACHE_RRRR_MASK) >> CACHE_RRRR_SHIFT)); #if 0 /* FIXME: We shouldn't mix parsing with actions */ if (track == 2) run_yellow_trigger(e->cpu, typenum, levelnum, type, level, e->socket); #endif } else if (test_prefix(10, mca)) { if (mca == 0x400) mce_snprintf(e->mcastatus_msg, "Internal Timer error"); else mce_snprintf(e->mcastatus_msg, "Internal unclassified error: %x", mca); } else if (test_prefix(11, mca)) { mce_snprintf(e->mcastatus_msg, "BUS %s %s %s %s %s Error", decode_attr(LL, (mca & BUS_LL_MASK) >> BUS_LL_SHIFT), decode_attr(PP, (mca & BUS_PP_MASK) >> BUS_PP_SHIFT), get_RRRR_str((mca & BUS_RRRR_MASK) >> BUS_RRRR_SHIFT), decode_attr(II, (mca & BUS_II_MASK) >> BUS_II_SHIFT), decode_attr(T, (mca & BUS_T_MASK) >> BUS_T_SHIFT)); } else if (test_prefix(7, mca)) { decode_memory_controller(e, mca); *ismemerr = 1; } else mce_snprintf(e->mcastatus_msg, "Unknown Error %x", mca); } static void decode_tracking(struct mce_event *e, uint64_t track) { if (track == 1) mce_snprintf(e->user_action, "Large number of corrected cache errors. System operating, but might leadto uncorrected errors soon"); if (track) mce_snprintf(e->mcistatus_msg, "Threshold based error status: %s", tracking_msg[track]); } static void decode_mci(struct mce_event *e, int *ismemerr) { uint64_t track = 0; if (!(e->status & MCI_STATUS_VAL)) mce_snprintf(e->mcistatus_msg, "MCE_INVALID"); if (e->status & MCI_STATUS_OVER) mce_snprintf(e->mcistatus_msg, "Error_overflow"); /* FIXME: convert into severity */ if (e->status & MCI_STATUS_UC) mce_snprintf(e->mcistatus_msg, "Uncorrected_error"); else mce_snprintf(e->mcistatus_msg, "Corrected_error"); if (e->status & MCI_STATUS_EN) mce_snprintf(e->mcistatus_msg, "Error_enabled"); if (e->status & MCI_STATUS_PCC) mce_snprintf(e->mcistatus_msg, "Processor_context_corrupt"); if (e->status & (MCI_STATUS_S|MCI_STATUS_AR)) mce_snprintf(e->mcistatus_msg, "%s", arstate[(e->status >> 55) & 3]); if ((e->mcgcap == 0 || (e->mcgcap & MCG_TES_P)) && !(e->status & MCI_STATUS_UC)) { track = (e->status >> 53) & 3; decode_tracking(e, track); } decode_mca(e, track, ismemerr); } int parse_intel_event(struct ras_events *ras, struct mce_event *e) { struct mce_priv *mce = ras->mce_priv; int ismemerr; bank_name(e); if (e->bank == MCE_THERMAL_BANK) { decode_termal_bank(e); return 0; } decode_mcg(e); decode_mci(e, &ismemerr); /* Check if the error is at the memory controller */ if (((e->status & 0xffff) >> 7) == 1) { unsigned corr_err_cnt; corr_err_cnt = EXTRACT(e->status, 38, 52); mce_snprintf(e->mc_location, "n_errors=%d", corr_err_cnt); } if (test_prefix(11, (e->status & 0xffffL))) { switch(mce->cputype) { case CPU_P6OLD: p6old_decode_model(e); break; case CPU_DUNNINGTON: case CPU_CORE2: case CPU_NEHALEM: case CPU_XEON75XX: core2_decode_model(e); break; case CPU_TULSA: case CPU_P4: p4_decode_model(e); break; default: break; } } switch(mce->cputype) { case CPU_NEHALEM: nehalem_decode_model(e); break; case CPU_XEON75XX: xeon75xx_decode_model(e); break; case CPU_DUNNINGTON: dunnington_decode_model(e); break; case CPU_TULSA: tulsa_decode_model(e); break; case CPU_SANDY_BRIDGE: case CPU_SANDY_BRIDGE_EP: snb_decode_model(ras, e); break; case CPU_IVY_BRIDGE_EPEX: ivb_decode_model(ras, e); break; case CPU_HASWELL_EPEX: hsw_decode_model(ras, e); break; case CPU_KNIGHTS_LANDING: case CPU_KNIGHTS_MILL: knl_decode_model(ras, e); break; case CPU_BROADWELL_DE: broadwell_de_decode_model(ras, e); break; case CPU_BROADWELL_EPEX: broadwell_epex_decode_model(ras, e); break; case CPU_SKYLAKE_XEON: skylake_s_decode_model(ras, e); break; case CPU_ICELAKE_XEON: case CPU_ICELAKE_DE: case CPU_TREMONT_D: case CPU_SAPPHIRERAPIDS: i10nm_decode_model(mce->cputype, ras, e); default: break; } return 0; } /* * Code to enable iMC logs */ static int domsr(int cpu, int msr, int bit) { char fpath[32]; unsigned long long data; int fd; sprintf(fpath, "/dev/cpu/%d/msr", cpu); fd = open(fpath, O_RDWR); if (fd == -1) { switch (errno) { case ENOENT: log(ALL, LOG_ERR, "Warning: cpu %d offline?, imc_log not set\n", cpu); return -EINVAL; default: log(ALL, LOG_ERR, "Cannot open %s to set imc_log\n", fpath); return -EINVAL; } } if (pread(fd, &data, sizeof data, msr) != sizeof data) { log(ALL, LOG_ERR, "Cannot read MSR_ERROR_CONTROL from %s\n", fpath); return -EINVAL; } data |= bit; if (pwrite(fd, &data, sizeof data, msr) != sizeof data) { log(ALL, LOG_ERR, "Cannot write MSR_ERROR_CONTROL to %s\n", fpath); return -EINVAL; } if (pread(fd, &data, sizeof data, msr) != sizeof data) { log(ALL, LOG_ERR, "Cannot re-read MSR_ERROR_CONTROL from %s\n", fpath); return -EINVAL; } if ((data & bit) == 0) { log(ALL, LOG_ERR, "Failed to set imc_log on cpu %d\n", cpu); return -EINVAL; } close(fd); return 0; } int set_intel_imc_log(enum cputype cputype, unsigned ncpus) { int cpu, msr, bit, rc; switch (cputype) { case CPU_SANDY_BRIDGE_EP: case CPU_IVY_BRIDGE_EPEX: case CPU_HASWELL_EPEX: case CPU_KNIGHTS_LANDING: case CPU_KNIGHTS_MILL: msr = 0x17f; /* MSR_ERROR_CONTROL */ bit = 0x2; /* MemError Log Enable */ break; default: return 0; } for (cpu = 0; cpu < ncpus; cpu++) { rc = domsr(cpu, msr, bit); if (rc) return rc; } return 0; } 0707010000003C000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002400000000rasdaemon-0.6.7.18.git+7ccf12f/misc0707010000003D000081A4000000000000000000000001611B9790000000CA000000000000000000000000000000000000003A00000000rasdaemon-0.6.7.18.git+7ccf12f/misc/ras-mc-ctl.service.in[Unit] Description=Initialize EDAC v3.0.0 Drivers For Machine Hardware [Service] Type=oneshot ExecStart=@sbindir@/ras-mc-ctl --register-labels RemainAfterExit=yes [Install] WantedBy=multi-user.target 0707010000003E000081A4000000000000000000000001611B979000000487000000000000000000000000000000000000003200000000rasdaemon-0.6.7.18.git+7ccf12f/misc/rasdaemon.env# Page Isolation # Note: Run-time configuration is unsupported, service restart needed. # Note: this file should be installed at /etc/sysconfig/rasdaemon # Specify the threshold of isolating buggy pages. # # Format: # [0-9]+[unit] # Notice: please make sure match this format, rasdaemon will use default value for exception input cases. # # Supported units: # PAGE_CE_REFRESH_CYCLE: D|d (day), H|h (hour), M|m (min), default is in hour # PAGE_CE_THRESHOLD: K|k (x1000), M|m (x1000k), default is none # # The two configs will only take no effect when PAGE_CE_ACTION is "off". PAGE_CE_REFRESH_CYCLE="24h" PAGE_CE_THRESHOLD="50" # Specify the internal action in rasdaemon to exceeding a page error threshold. # # off no action # account only account errors # soft try to soft-offline page without killing any processes # This requires an uptodate kernel. Might not be successfull. # hard try to hard-offline page by killing processes # Requires an uptodate kernel. Might not be successfull. # soft-then-hard First try to soft offline, then try hard offlining. # Note: default offline choice is "soft". PAGE_CE_ACTION="soft" 0707010000003F000081A4000000000000000000000001611B97900000012A000000000000000000000000000000000000003900000000rasdaemon-0.6.7.18.git+7ccf12f/misc/rasdaemon.service.in[Unit] Description=RAS daemon to log the RAS events After=syslog.target [Service] EnvironmentFile=@SYSCONFDEFDIR@/rasdaemon ExecStart=@sbindir@/rasdaemon -f -r ExecStartPost=@sbindir@/rasdaemon --enable ExecStop=@sbindir@/rasdaemon --disable Restart=on-abort [Install] WantedBy=multi-user.target 07070100000040000081A4000000000000000000000001611B97900000129A000000000000000000000000000000000000003600000000rasdaemon-0.6.7.18.git+7ccf12f/misc/rasdaemon.spec.inName: @PACKAGE@ Version: @PACKAGE_VERSION@ Release: 1%{?dist} Summary: Utility to receive RAS error tracings Group: Applications/System License: GPLv2 URL: http://git.infradead.org/users/mchehab/rasdaemon.git Source0: http://www.infradead.org/~mchehab/rasdaemon/%{name}-%{version}.tar.bz2 ExcludeArch: s390 s390x BuildRequires: gcc BuildRequires: gettext-devel BuildRequires: perl-generators BuildRequires: sqlite-devel BuildRequires: systemd Provides: bundled(kernel-event-lib) Requires: hwdata Requires: perl-DBD-SQLite %ifarch %{ix86} x86_64 Requires: dmidecode %endif Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %description %{name} is a RAS (Reliability, Availability and Serviceability) logging tool. It currently records memory errors, using the EDAC tracing events. EDAC is drivers in the Linux kernel that handle detection of ECC errors from memory controllers for most chipsets on i386 and x86_64 architectures. EDAC drivers for other architectures like arm also exists. This userspace component consists of an init script which makes sure EDAC drivers and DIMM labels are loaded at system startup, as well as an utility for reporting current error counts from the EDAC sysfs files. %prep %setup -q %build %configure --enable-all make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} install -D -p -m 0644 misc/rasdaemon.service %{buildroot}/%{_unitdir}/rasdaemon.service install -D -p -m 0644 misc/ras-mc-ctl.service %{buildroot}%{_unitdir}/ras-mc-ctl.service rm INSTALL %{buildroot}/usr/include/*.h %files %doc AUTHORS ChangeLog COPYING README TODO %{_sbindir}/rasdaemon %{_sbindir}/ras-mc-ctl %{_mandir}/*/* %{_unitdir}/*.service %{_sysconfdir}/ras/dimm_labels.d @SYSCONFDEFDIR@/%{name} %config(noreplace) @SYSCONFDEFDIR@/%{name} %changelog * Wed May 26 2021 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 0.6.7-1 - Bump to version 0.6.7 with several fixes and additions * Tue Jul 21 2020 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 0.6.6-1 - Bump to version 0.6.6 with several fixes, new hip08 events and memory prediction analysis * Wed Nov 20 2019 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 0.6.5-1 - Bump to version 0.6.5 with several fixes and improves PCIe events record * Thu Oct 10 2019 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.6.4-1 - Bump to version 0.6.4 with some DB changes for hip08 and some fixes * Fri Aug 23 2019 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.6.3-1 - Bump to version 0.6.3 with new ARM events, plus disk I/O and netlink support * Tue Aug 14 2018 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.6.2-1 - Bump to version 0.6.2 with improvements for PCIe AER parsing and at ras-mc-ctl tool * Wed Apr 25 2018 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.6.1-1 - Bump to version 0.6.1 adding support for Skylake Xeon MSCOD, a bug fix and some new DELL labels * Sat Oct 14 2017 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.6.0-1 - Bump to version 0.6.0 adding support for Arm and Hisilicon events and update Dell Skylate labels * Thu Jun 08 2017 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.5.9-1 - Bump to version 0.5.9 adding support for Knights Mill and update DELL labels * Fri Apr 15 2016 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.5.8-1 - Bump to version 0.5.8 adding support for Broadwell EP/EX MSCOD and Broadwell DE MSCOD * Fri Feb 05 2016 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.5.7-1 - Bump to version 0.5.7 adding support for Broadwell-EP/EX and -DE and Knights Landing processors * Fri Jul 03 2015 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.5.6-1 - Bump to version 0.5.6 with support for LMCE and some fixes * Wed Jun 03 2015 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> 0.5.5-1 - Bump to version 0.5.5 with support for newer Intel platforms & some fixes * Fri Aug 15 2014 Mauro Carvalho Chehab <m.chehab@samsung.com> 0.5.4-1 - Bump to version 0.5.4 with some fixes, mainly for amd64 * Sun Aug 10 2014 Mauro Carvalho Chehab <m.chehab@samsung.com> 0.5.3-1 - Bump to version 0.5.3 and enable ABRT and ExtLog * Tue Sep 10 2013 Mauro Carvalho Chehab <m.chehab@samsung.com> 0.4.2-1 - Fix ras-mc-ctl layout filling * Wed May 29 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> 0.4.1-2 - Fix the name of perl-DBD-SQLite package * Wed May 29 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> 0.4.1-1 - Updated to version 0.4.1 with contains some bug fixes * Tue May 28 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> 0.4.0-1 - Updated to version 0.4.0 and added support for mce, aer and sqlite3 storage * Mon May 20 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> 0.3.0-1 - Package created 07070100000041000081ED000000000000000000000001611B9790000001DF000000000000000000000000000000000000002A00000000rasdaemon-0.6.7.18.git+7ccf12f/new_ver.sh#!/bin/bash autoreconf && ./configure --enable-all VER="`perl -ne 'print "$1\n" if (/Version:\s*(.*)/);' misc/rasdaemon.spec`" if [ "x$VER" == "x" ]; then echo "Can't parse rasdaemon version" exit -1 fi echo echo "************************************************************************" echo "Building RPM files for version: $VER" echo "************************************************************************" echo git tag v$VER -f && make mock && make upload && git push 07070100000042000081A4000000000000000000000001611B979000008265000000000000000000000000000000000000003500000000rasdaemon-0.6.7.18.git+7ccf12f/non-standard-ampere.c/* * Copyright (c) 2020, Ampere Computing LLC. * * 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. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" #include "ras-non-standard-handler.h" #include "non-standard-ampere.h" /*Armv8 RAS compicant Error Record(APEI and BMC Reporting) Payload Type 0*/ static const char * const disp_payload0_err_reg_name[] = { "Error Type:", "Error SubType:", "Error Instance:", "Processor Socket:", "Status:", "Address:", "MISC0:", "MISC1:", "MISC2:", "MISC3:", }; /*PCIe AER Error Payload Type 1*/ static const char * const disp_payload1_err_reg_name[] = { "Error Type:", "Error Subtype:", "Error Instance:", "Processor Socket:", "AER_UNCORR_ERR_STATUS:", "AER_UNCORR_ERR_MASK:", "AER_UNCORR_ERR_SEV:", "AER_CORR_ERR_STATUS:", "AER_CORR_ERR_MASK:", "AER_ROOT_ERR_CMD:", "AER_ROOT_ERR_STATUS:", "AER_ERR_SRC_ID:", "Reserved:", "Reserved:", }; /*PCIe RAS Dat Path(RASDP), Payload Type 2 */ static const char * const disp_payload2_err_reg_name[] = { "Error Type:", "Error Subtype:", "Error Instance:", "Processor Socket:", "CE Report Register:", "CE Location Register:", "CE Address:", "UE Reprot Register:", "UE Location Register:", "UE Address:", "Reserved:", "Reserved:", "Reserved:", }; /*Firmware-Specific Data(ATF, SMPro, PMpro, and BERT), Payload Type 3 */ static const char * const disp_payload3_err_reg_name[] = { "Error Type:", "Error Subtype:", "Error Instance:", "Processor Socket:", "Firmware-Specific Data 0:", "Firmware-Specific Data 1:", "Firmware-Specific Data 2:", "Firmware-Specific Data 3:", "Firmware-Specific Data 4:", "Firmware-Specific Data 5:", }; static const char * const err_cpm_sub_type[] = { "Snoop-Logic", "ARMv8 Core 0", "ARMv8 Core 1", }; static const char * const err_mcu_sub_type[] = { "ERR0", "ERR1", "ERR2", "ERR3", "ERR4", "ERR5", "ERR6", "Link Error", }; static const char * const err_mesh_sub_type[] = { "Cross Point", "Home Node(IO)", "Home Node(Memory)", "CCIX Node", }; static const char * const err_2p_link_ms_sub_type[] = { "ERR0", "ERR1", "ERR2", "ERR3", }; static const char * const err_gic_sub_type[] = { "ERR0", "ERR1", "ERR2", "ERR3", "ERR4", "ERR5", "ERR6", "ERR7", "ERR8", "ERR9", "ERR10", "ERR11", "ERR12", "ERR13(GIC ITS 0)", "ERR14(GIC ITS 1)", "ERR15(GIC ITS 2)", "ERR16(GIC ITS 3)", "ERR17(GIC ITS 4)", "ERR18(GIC ITS 5)", "ERR19(GIC ITS 6)", "ERR20(GIC ITS 7)", }; /*as the SMMU's subtype value is consistent, using switch for type0*/ static char *err_smmu_sub_type(int etype) { switch (etype) { case 0x00: return "TBU0"; case 0x01: return "TBU1"; case 0x02: return "TBU2"; case 0x03: return "TBU3"; case 0x04: return "TBU4"; case 0x05: return "TBU5"; case 0x06: return "TBU6"; case 0x07: return "TBU7"; case 0x08: return "TBU8"; case 0x09: return "TBU9"; case 0x64: return "TCU"; } return "unknown error"; } static const char * const err_pcie_aer_sub_type[] = { "Root Port", "Device", }; /*as the PCIe RASDP's subtype value is consistent, using switch for type0/2*/ static char *err_peci_rasdp_sub_type(int etype) { switch (etype) { case 0x00: return "RCA HB Error"; case 0x01: return "RCB HB Error"; case 0x08: return "RASDP Error"; } return "unknown error"; } static const char * const err_ocm_sub_type[] = { "ERR0", "ERR1", "ERR2", }; static const char * const err_smpro_sub_type[] = { "ERR0", "ERR1", "MPA_ERR", }; static const char * const err_pmpro_sub_type[] = { "ERR0", "ERR1", "MPA_ERR", }; static const char * const err_atf_fw_sub_type[] = { "EL3", "SPM", "Secure Partition(SEL0/SEL1)", }; static const char * const err_smpro_fw_sub_type[] = { "RAS_MSG_ERR", "", }; static const char * const err_pmpro_fw_sub_type[] = { "RAS_MSG_ERR", "", }; static const char * const err_bert_sub_type[] = { "Default", "Watchdog", "ATF Fatal", "SMPRO Fatal", "PMPRO Fatal", }; static char *sqlite3_table_list[] = { "amp_payload0_event_tab", "amp_payload1_event_tab", "amp_payload2_event_tab", "amp_payload3_event_tab", }; struct amp_ras_type_info { int id; const char *name; const char * const *sub; int sub_num; }; static const struct amp_ras_type_info amp_payload_error_type[] = { { .id = AMP_RAS_TYPE_CPU, .name = "CPM", .sub = err_cpm_sub_type, .sub_num = ARRAY_SIZE(err_cpm_sub_type), }, { .id = AMP_RAS_TYPE_MCU, .name = "MCU", .sub = err_mcu_sub_type, .sub_num = ARRAY_SIZE(err_mcu_sub_type), }, { .id = AMP_RAS_TYPE_MESH, .name = "MESH", .sub = err_mesh_sub_type, .sub_num = ARRAY_SIZE(err_mesh_sub_type), }, { .id = AMP_RAS_TYPE_2P_LINK_QS, .name = "2P Link(Altra)", }, { .id = AMP_RAS_TYPE_2P_LINK_MQ, .name = "2P Link(Altra Max)", .sub = err_2p_link_ms_sub_type, .sub_num = ARRAY_SIZE(err_2p_link_ms_sub_type), }, { .id = AMP_RAS_TYPE_GIC, .name = "GIC", .sub = err_gic_sub_type, .sub_num = ARRAY_SIZE(err_gic_sub_type), }, { .id = AMP_RAS_TYPE_SMMU, .name = "SMMU", }, { .id = AMP_RAS_TYPE_PCIE_AER, .name = "PCIe AER", .sub = err_pcie_aer_sub_type, .sub_num = ARRAY_SIZE(err_pcie_aer_sub_type), }, { .id = AMP_RAS_TYPE_PCIE_RASDP, .name = "PCIe RASDP", }, { .id = AMP_RAS_TYPE_OCM, .name = "OCM", .sub = err_ocm_sub_type, .sub_num = ARRAY_SIZE(err_ocm_sub_type), }, { .id = AMP_RAS_TYPE_SMPRO, .name = "SMPRO", .sub = err_smpro_sub_type, .sub_num = ARRAY_SIZE(err_smpro_sub_type), }, { .id = AMP_RAS_TYPE_PMPRO, .name = "PMPRO", .sub = err_pmpro_sub_type, .sub_num = ARRAY_SIZE(err_pmpro_sub_type), }, { .id = AMP_RAS_TYPE_ATF_FW, .name = "ATF FW", .sub = err_atf_fw_sub_type, .sub_num = ARRAY_SIZE(err_atf_fw_sub_type), }, { .id = AMP_RAS_TYPE_SMPRO_FW, .name = "SMPRO FW", .sub = err_smpro_fw_sub_type, .sub_num = ARRAY_SIZE(err_smpro_fw_sub_type), }, { .id = AMP_RAS_TYPE_PMPRO_FW, .name = "PMPRO FW", .sub = err_pmpro_fw_sub_type, .sub_num = ARRAY_SIZE(err_pmpro_fw_sub_type), }, { .id = AMP_RAS_TYPE_BERT, .name = "BERT", .sub = err_bert_sub_type, .sub_num = ARRAY_SIZE(err_bert_sub_type), }, { } }; /*get the error type name*/ static const char *oem_type_name(const struct amp_ras_type_info *info, uint8_t type_id) { const struct amp_ras_type_info *type = &info[0]; for (; type->name; type++) { if (type->id != type_id) continue; return type->name; } return "unknown"; } /*get the error subtype*/ static const char *oem_subtype_name(const struct amp_ras_type_info *info, uint8_t type_id, uint8_t sub_type_id) { const struct amp_ras_type_info *type = &info[0]; for (; type->name; type++) { const char * const *submodule = type->sub; if (type->id != type_id) continue; if (type->sub == NULL) return type->name; if (sub_type_id >= type->sub_num) return "unknown"; return submodule[sub_type_id]; } return "unknown"; } #ifdef HAVE_SQLITE3 /*key pair definition for ampere specific error payload type 0*/ static const struct db_fields amp_payload0_event_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "type", .type = "TEXT" }, { .name = "subtype", .type = "TEXT" }, { .name = "instance", .type = "INTEGER" }, { .name = "socket_num", .type = "INTEGER" }, { .name = "status_reg", .type = "INTEGER" }, { .name = "addr_reg", .type = "INTEGER" }, { .name = "misc0", .type = "INTEGER" }, { .name = "misc1", .type = "INTEGER" }, { .name = "misc2", .type = "INTEGER" }, { .name = "misc3", .type = "INTEGER" }, }; static const struct db_table_descriptor amp_payload0_event_tab = { .name = "amp_payload0_event", .fields = amp_payload0_event_fields, .num_fields = ARRAY_SIZE(amp_payload0_event_fields), }; /*key pair definition for ampere specific error payload type 1*/ static const struct db_fields amp_payload1_event_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "type", .type = "TEXT" }, { .name = "subtype", .type = "TEXT" }, { .name = "instance", .type = "INTEGER" }, { .name = "socket_num", .type = "INTEGER" }, { .name = "uncore_err_status", .type = "INTEGER" }, { .name = "uncore_err_mask", .type = "INTEGER" }, { .name = "uncore_err_sev", .type = "INTEGER" }, { .name = "core_err_status", .type = "INTEGER" }, { .name = "core_err_mask", .type = "INTEGER" }, { .name = "root_err_cmd", .type = "INTEGER" }, { .name = "root_err_status", .type = "INTEGER" }, { .name = "src_id", .type = "INTEGER" }, { .name = "reserved1", .type = "INTEGER" }, { .name = "reserverd2", .type = "INTEGER" }, }; static const struct db_table_descriptor amp_payload1_event_tab = { .name = "amp_payload1_event", .fields = amp_payload1_event_fields, .num_fields = ARRAY_SIZE(amp_payload1_event_fields), }; /*key pair definition for ampere specific error payload type 2*/ static const struct db_fields amp_payload2_event_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "type", .type = "TEXT" }, { .name = "subtype", .type = "TEXT" }, { .name = "instance", .type = "INTEGER" }, { .name = "socket_num", .type = "INTEGER" }, { .name = "ce_report_reg", .type = "INTEGER" }, { .name = "ce_location", .type = "INTEGER" }, { .name = "ce_addr", .type = "INTEGER" }, { .name = "ue_report_reg", .type = "INTEGER" }, { .name = "ue_location", .type = "INTEGER" }, { .name = "ue_addr", .type = "INTEGER" }, { .name = "reserved1", .type = "INTEGER" }, { .name = "reserved2", .type = "INTEGER" }, { .name = "reserved2", .type = "INTEGER" }, }; static const struct db_table_descriptor amp_payload2_event_tab = { .name = "amp_payload2_event", .fields = amp_payload2_event_fields, .num_fields = ARRAY_SIZE(amp_payload2_event_fields), }; /*key pair definition for ampere specific error payload type 3*/ static const struct db_fields amp_payload3_event_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "type", .type = "TEXT" }, { .name = "subtype", .type = "TEXT" }, { .name = "instance", .type = "INTEGER" }, { .name = "socket_num", .type = "INTEGER" }, { .name = "fw_spec_data0", .type = "INTEGER" }, { .name = "fw_spec_data1", .type = "INTEGER" }, { .name = "fw_spec_data2", .type = "INTEGER" }, { .name = "fw_spec_data3", .type = "INTEGER" }, { .name = "fw_spec_data4", .type = "INTEGER" }, { .name = "fw_spec_data5", .type = "INTEGER" }, }; static const struct db_table_descriptor amp_payload3_event_tab = { .name = "amp_payload3_event", .fields = amp_payload3_event_fields, .num_fields = ARRAY_SIZE(amp_payload3_event_fields), }; /*Save data with different type into sqlite3 db*/ static void record_amp_data(struct ras_ns_ev_decoder *ev_decoder, enum amp_oem_data_type data_type, int id, int64_t data, const char *text) { switch (data_type) { case AMP_OEM_DATA_TYPE_INT: sqlite3_bind_int(ev_decoder->stmt_dec_record, id, data); break; case AMP_OEM_DATA_TYPE_INT64: sqlite3_bind_int64(ev_decoder->stmt_dec_record, id, data); break; case AMP_OEM_DATA_TYPE_TEXT: sqlite3_bind_text(ev_decoder->stmt_dec_record, id, text, -1, NULL); break; default: break; } } static int store_amp_err_data(struct ras_ns_ev_decoder *ev_decoder, const char *name) { int rc; rc = sqlite3_step(ev_decoder->stmt_dec_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do %s step on sqlite: error = %d\n", name, rc); rc = sqlite3_reset(ev_decoder->stmt_dec_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to reset %s on sqlite: error = %d\n", name, rc); rc = sqlite3_clear_bindings(ev_decoder->stmt_dec_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to clear bindings %s on sqlite: error = %d\n", name, rc); return rc; } /*save all Ampere Specific Error Payload type 0 to sqlite3 database*/ static void record_amp_payload0_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload0_type_sec *err) { if (ev_decoder != NULL) { record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD0_FIELD_TYPE, 0, type_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD0_FIELD_SUB_TYPE, 0, subtype_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD0_FIELD_INS, INSTANCE(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD0_FIELD_SOCKET_NUM, SOCKET_NUM(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD0_FIELD_STATUS_REG, err->err_status, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD0_FIELD_ADDR_REG, err->err_addr, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD0_FIELD_MISC0, err->err_misc_0, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD0_FIELD_MISC1, err->err_misc_1, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD0_FIELD_MISC2, err->err_misc_2, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD0_FIELD_MISC3, err->err_misc_3, NULL); store_amp_err_data(ev_decoder, "amp_payload0_event_tab"); } } /*save all Ampere Specific Error Payload type 1 to sqlite3 database*/ static void record_amp_payload1_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload1_type_sec *err) { if (ev_decoder != NULL) { record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD1_FIELD_TYPE, 0, type_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD1_FIELD_SUB_TYPE, 0, subtype_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_INS, INSTANCE(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_SOCKET_NUM, SOCKET_NUM(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_UNCORE_ERR_STATUS, err->uncore_status, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_UNCORE_ERR_MASK, err->uncore_mask, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_UNCORE_ERR_SEV, err->uncore_sev, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_CORE_ERR_STATUS, err->core_status, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_CORE_ERR_MASK, err->core_mask, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_ROOT_ERR_CMD, err->root_err_cmd, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_ROOT_ERR_STATUS, err->root_status, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_SRC_ID, err->src_id, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD1_FIELD_RESERVED1, err->reserved1, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD1_FIELD_RESERVED2, err->reserved2, NULL); store_amp_err_data(ev_decoder, "amp_payload1_event_tab"); } } /*save all Ampere Specific Error Payload type 2 to sqlite3 database*/ static void record_amp_payload2_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload2_type_sec *err) { if (ev_decoder != NULL) { record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD2_FIELD_TYPE, 0, type_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD2_FIELD_SUB_TYPE, 0, subtype_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_INS, INSTANCE(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_SOCKET_NUM, SOCKET_NUM(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_CE_REPORT_REG, err->ce_register, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_CE_LOACATION, err->ce_location, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_CE_ADDR, err->ce_addr, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_UE_REPORT_REG, err->ue_register, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_UE_LOCATION, err->ue_location, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_UE_ADDR, err->ue_addr, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD2_FIELD_RESERVED1, err->reserved1, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD2_FIELD_RESERVED2, err->reserved2, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD2_FIELD_RESERVED3, err->reserved3, NULL); store_amp_err_data(ev_decoder, "amp_payload2_event_tab"); } } /*save all Ampere Specific Error Payload type 3 to sqlite3 database*/ static void record_amp_payload3_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload3_type_sec *err) { if (ev_decoder != NULL) { record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD3_FIELD_TYPE, 0, type_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, AMP_PAYLOAD3_FIELD_SUB_TYPE, 0, subtype_str); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD3_FIELD_INS, INSTANCE(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD3_FIELD_SOCKET_NUM, SOCKET_NUM(err->instance), NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA0, err->fw_speci_data0, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA1, err->fw_speci_data1, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA2, err->fw_speci_data2, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA3, err->fw_speci_data3, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA4, err->fw_speci_data4, NULL); record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_INT64, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA5, err->fw_speci_data5, NULL); store_amp_err_data(ev_decoder, "amp_payload3_event_tab"); } } #else static void record_amp_data(struct ras_ns_ev_decoder *ev_decoder, enum amp_oem_data_type data_type, int id, int64_t data, const char *text) { return 0; } static void record_amp_payload0_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload0_type_sec *err) { return 0; } static void record_amp_payload1_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload1_type_sec *err) { return 0; } static void record_amp_payload2_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload2_type_sec *err) { return 0; } static void record_amp_payload3_err(struct ras_ns_ev_decoder *ev_decoder, const char *type_str, const char *subtype_str, const struct amp_payload3_type_sec *err) { return 0; } static int store_amp_err_data(struct ras_ns_ev_decoder *ev_decoder, char *name) { return 0; } #endif /*decode ampere specific error payload type 0, the CPU's data is save*/ /*to sqlite by ras-arm-handler, others are saved by this function.*/ void decode_amp_payload0_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct amp_payload0_type_sec *err) { char buf[AMP_PAYLOAD0_BUF_LEN]; char *p = buf; char *end = buf + AMP_PAYLOAD0_BUF_LEN; int i = 0, core_num = 0; const char *subtype_str; const char *type_str = oem_type_name(amp_payload_error_type, TYPE(err->type)); if (TYPE(err->type) == AMP_RAS_TYPE_SMMU) subtype_str = err_smmu_sub_type(err->subtype); else subtype_str = oem_subtype_name(amp_payload_error_type, TYPE(err->type), err->subtype); //display error type p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", type_str); //display error subtype p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", subtype_str); //display error instance p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", INSTANCE(err->instance)); //display socket number if ((TYPE(err->type) == 0) && ((err->subtype == 0x01) || (err->subtype == 0x02))) { core_num = INSTANCE(err->instance) * 2 + err->subtype - 1; p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %d, Core Number is:%d\n", SOCKET_NUM(err->instance), core_num); } else { p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %d\n", SOCKET_NUM(err->instance)); } //display status register p += snprintf(p, end - p, " %s", disp_payload0_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->err_status); //display address register p += snprintf(p, end - p, " %s", disp_payload0_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->err_addr); //display MISC0 p += snprintf(p, end - p, " %s", disp_payload0_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->err_misc_0); //display MISC1 p += snprintf(p, end - p, " %s", disp_payload0_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->err_misc_1); //display MISC2 p += snprintf(p, end - p, " %s", disp_payload0_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->err_misc_2); //display MISC3 p += snprintf(p, end - p, " %s", disp_payload0_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->err_misc_3); if (p > buf && p < end) { p--; *p = '\0'; } record_amp_payload0_err(ev_decoder, type_str, subtype_str, err); i = 0; p = NULL; end = NULL; trace_seq_printf(s, "%s\n", buf); } /*decode ampere specific error payload type 1 and save to sqlite db*/ static void decode_amp_payload1_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct amp_payload1_type_sec *err) { char buf[AMP_PAYLOAD0_BUF_LEN]; char *p = buf; char *end = buf + AMP_PAYLOAD0_BUF_LEN; int i = 0; const char *type_str = oem_type_name(amp_payload_error_type, TYPE(err->type)); const char *subtype_str = oem_subtype_name(amp_payload_error_type, TYPE(err->type), err->subtype); //display error type p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", type_str); //display error subtype p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %s", subtype_str); //display error instance p += snprintf(p, end - p, "\n%s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", INSTANCE(err->instance)); //display socket number p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " %d\n", SOCKET_NUM(err->instance)); //display AER_UNCORR_ERR_STATUS p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->uncore_status); //display AER_UNCORR_ERR_MASK p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->uncore_mask); //display AER_UNCORR_ERR_SEV p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->uncore_sev); //display AER_CORR_ERR_STATUS p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->core_status); //display AER_CORR_ERR_MASK p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->core_mask); //display AER_ROOT_ERR_CMD p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->root_err_cmd); //display AER_ROOT_ERR_STATUS p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->root_status); //display AER_ERR_SRC_ID p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->src_id); //display Reserved p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->reserved1); //display Reserved p += snprintf(p, end - p, " %s", disp_payload1_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->reserved2); if (p > buf && p < end) { p--; *p = '\0'; } record_amp_payload1_err(ev_decoder, type_str, subtype_str, err); i = 0; p = NULL; end = NULL; trace_seq_printf(s, "%s\n", buf); } /*decode ampere specific error payload type 2 and save to sqlite db*/ static void decode_amp_payload2_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct amp_payload2_type_sec *err) { char buf[AMP_PAYLOAD0_BUF_LEN]; char *p = buf; char *end = buf + AMP_PAYLOAD0_BUF_LEN; int i = 0; const char *subtype_str; const char *type_str = oem_type_name(amp_payload_error_type, TYPE(err->type)); if (TYPE(err->type) == AMP_RAS_TYPE_PCIE_RASDP) subtype_str = err_peci_rasdp_sub_type(err->subtype); else subtype_str = oem_subtype_name(amp_payload_error_type, TYPE(err->type), err->subtype); //display error type p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", type_str); //display error subtype p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", subtype_str); //display error instance p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", INSTANCE(err->instance)); //display socket number p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " %d\n", SOCKET_NUM(err->instance)); //display CE Report Register p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->ce_register); //display CE Location Register p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->ce_location); //display CE Address p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->ce_addr); //display UE Reprot Register p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->ue_register); //display UE Location Register p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->ue_location); //display UE Address p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->ue_addr); //display Reserved p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->reserved1); //display Reserved p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->reserved2); //display Reserved p += snprintf(p, end - p, " %s", disp_payload2_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->reserved3); if (p > buf && p < end) { p--; *p = '\0'; } record_amp_payload2_err(ev_decoder, type_str, subtype_str, err); i = 0; p = NULL; end = NULL; trace_seq_printf(s, "%s\n", buf); } /*decode ampere specific error payload type 3 and save to sqlite db*/ static void decode_amp_payload3_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct amp_payload3_type_sec *err) { char buf[AMP_PAYLOAD0_BUF_LEN]; char *p = buf; char *end = buf + AMP_PAYLOAD0_BUF_LEN; int i = 0; const char *type_str = oem_type_name(amp_payload_error_type, TYPE(err->type)); const char *subtype_str = oem_subtype_name(amp_payload_error_type, TYPE(err->type), err->subtype); //display error type p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", type_str); //display error subtype p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " %s\n", subtype_str); //display error instance p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", INSTANCE(err->instance)); //display socket number p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " %d\n", SOCKET_NUM(err->instance)); //display Firmware-Specific Data 0 p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%x\n", err->fw_speci_data0); //display Firmware-Specific Data 1 p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->fw_speci_data1); //display Firmware-Specific Data 2 p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->fw_speci_data2); //display Firmware-Specific Data 3 p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->fw_speci_data3); //display Firmware-Specific Data 4 p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->fw_speci_data4); //display Firmware-Specific Data 5 p += snprintf(p, end - p, " %s", disp_payload3_err_reg_name[i++]); p += snprintf(p, end - p, " 0x%llx\n", (unsigned long long)err->fw_speci_data5); if (p > buf && p < end) { p--; *p = '\0'; } record_amp_payload3_err(ev_decoder, type_str, subtype_str, err); i = 0; p = NULL; end = NULL; trace_seq_printf(s, "%s\n", buf); } /* error data decoding functions */ static int decode_amp_oem_type_error(struct ras_events *ras, struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, struct ras_non_standard_event *event) { int payload_type = PAYLOAD_TYPE(event->error[0]); #ifdef HAVE_SQLITE3 struct db_table_descriptor db_tab; int id = 0; if (payload_type == PAYLOAD_TYPE_0) { db_tab = amp_payload0_event_tab; id = AMP_PAYLOAD0_FIELD_TIMESTAMP; } else if (payload_type == PAYLOAD_TYPE_1) { db_tab = amp_payload1_event_tab; id = AMP_PAYLOAD1_FIELD_TIMESTAMP; } else if (payload_type == PAYLOAD_TYPE_2) { db_tab = amp_payload2_event_tab; id = AMP_PAYLOAD2_FIELD_TIMESTAMP; } else if (payload_type == PAYLOAD_TYPE_3) { db_tab = amp_payload3_event_tab; id = AMP_PAYLOAD3_FIELD_TIMESTAMP; } else return -1; if (!ev_decoder->stmt_dec_record) { if (ras_mc_add_vendor_table(ras, &ev_decoder->stmt_dec_record, &db_tab) != SQLITE_OK) { trace_seq_printf(s, "create sql %s fail\n", sqlite3_table_list[payload_type]); return -1; } } record_amp_data(ev_decoder, AMP_OEM_DATA_TYPE_TEXT, id, 0, event->timestamp); #endif if (payload_type == PAYLOAD_TYPE_0) { const struct amp_payload0_type_sec *err = (struct amp_payload0_type_sec *)event->error; decode_amp_payload0_err_regs(ev_decoder, s, err); } else if (payload_type == PAYLOAD_TYPE_1) { const struct amp_payload1_type_sec *err = (struct amp_payload1_type_sec *)event->error; decode_amp_payload1_err_regs(ev_decoder, s, err); } else if (payload_type == PAYLOAD_TYPE_2) { const struct amp_payload2_type_sec *err = (struct amp_payload2_type_sec *)event->error; decode_amp_payload2_err_regs(ev_decoder, s, err); } else if (payload_type == PAYLOAD_TYPE_3) { const struct amp_payload3_type_sec *err = (struct amp_payload3_type_sec *)event->error; decode_amp_payload3_err_regs(ev_decoder, s, err); } else { trace_seq_printf(s, "%s: wrong payload type\n", __func__); return -1; } return 0; } struct ras_ns_ev_decoder amp_ns_oem_decoder[] = { { .sec_type = "e8ed898ddf1643cc8ecc54f060ef157f", .decode = decode_amp_oem_type_error, }, }; static void __attribute__((constructor)) amp_init(void) { register_ns_ev_decoder(amp_ns_oem_decoder); } 07070100000043000081A4000000000000000000000001611B9790000011F6000000000000000000000000000000000000003500000000rasdaemon-0.6.7.18.git+7ccf12f/non-standard-ampere.h/* * Copyright (c) 2020, Ampere Computing LLC. * * 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. * */ #ifndef __NON_STANDARD_AMPERE_H #define __NON_STANDARD_AMPERE_H #include "ras-events.h" #include "libtrace/event-parse.h" #define SOCKET_NUM(x) ((x >> 14) & 0x3) #define PAYLOAD_TYPE(x) ((x >> 6) & 0x3) #define TYPE(x) (x & 0x3f) #define INSTANCE(x) (x & 0x3fff) #define AMP_PAYLOAD0_BUF_LEN 1024 #define PAYLOAD_TYPE_0 0x00 #define PAYLOAD_TYPE_1 0x01 #define PAYLOAD_TYPE_2 0x02 #define PAYLOAD_TYPE_3 0x03 /* Ampere RAS Error type definitions */ #define AMP_RAS_TYPE_CPU 0 #define AMP_RAS_TYPE_MCU 1 #define AMP_RAS_TYPE_MESH 2 #define AMP_RAS_TYPE_2P_LINK_QS 3 #define AMP_RAS_TYPE_2P_LINK_MQ 4 #define AMP_RAS_TYPE_GIC 5 #define AMP_RAS_TYPE_SMMU 6 #define AMP_RAS_TYPE_PCIE_AER 7 #define AMP_RAS_TYPE_PCIE_RASDP 8 #define AMP_RAS_TYPE_OCM 9 #define AMP_RAS_TYPE_SMPRO 10 #define AMP_RAS_TYPE_PMPRO 11 #define AMP_RAS_TYPE_ATF_FW 12 #define AMP_RAS_TYPE_SMPRO_FW 13 #define AMP_RAS_TYPE_PMPRO_FW 14 #define AMP_RAS_TYPE_BERT 63 /* ARMv8 RAS Compliant Error Record(APEI and BMC Reporting)*/ struct amp_payload0_type_sec { uint8_t type; uint8_t subtype; uint16_t instance; uint32_t err_status; uint64_t err_addr; uint64_t err_misc_0; uint64_t err_misc_1; uint64_t err_misc_2; uint64_t err_misc_3; }; /*PCIe AER format*/ struct amp_payload1_type_sec { uint8_t type; uint8_t subtype; uint16_t instance; uint32_t uncore_status; uint32_t uncore_mask; uint32_t uncore_sev; uint32_t core_status; uint32_t core_mask; uint32_t root_err_cmd; uint32_t root_status; uint32_t src_id; uint32_t reserved1; uint64_t reserved2; }; /*PCIe RAS Data Path(RASDP) format */ struct amp_payload2_type_sec { uint8_t type; uint8_t subtype; uint16_t instance; uint32_t ce_register; uint32_t ce_location; uint32_t ce_addr; uint32_t ue_register; uint32_t ue_location; uint32_t ue_addr; uint32_t reserved1; uint64_t reserved2; uint64_t reserved3; }; /*Firmware-Specific Data(ATF,SMPro, and BERT) */ struct amp_payload3_type_sec { uint8_t type; uint8_t subtype; uint16_t instance; uint32_t fw_speci_data0; uint64_t fw_speci_data1; uint64_t fw_speci_data2; uint64_t fw_speci_data3; uint64_t fw_speci_data4; uint64_t fw_speci_data5; }; enum amp_oem_data_type { AMP_OEM_DATA_TYPE_INT, AMP_OEM_DATA_TYPE_INT64, AMP_OEM_DATA_TYPE_TEXT, }; enum { AMP_PAYLOAD0_FIELD_ID, AMP_PAYLOAD0_FIELD_TIMESTAMP, AMP_PAYLOAD0_FIELD_TYPE, AMP_PAYLOAD0_FIELD_SUB_TYPE, AMP_PAYLOAD0_FIELD_INS, AMP_PAYLOAD0_FIELD_SOCKET_NUM, AMP_PAYLOAD0_FIELD_STATUS_REG, AMP_PAYLOAD0_FIELD_ADDR_REG, AMP_PAYLOAD0_FIELD_MISC0, AMP_PAYLOAD0_FIELD_MISC1, AMP_PAYLOAD0_FIELD_MISC2, AMP_PAYLOAD0_FIELD_MISC3, }; enum { AMP_PAYLOAD1_FIELD_ID, AMP_PAYLOAD1_FIELD_TIMESTAMP, AMP_PAYLOAD1_FIELD_TYPE, AMP_PAYLOAD1_FIELD_SUB_TYPE, AMP_PAYLOAD1_FIELD_INS, AMP_PAYLOAD1_FIELD_SOCKET_NUM, AMP_PAYLOAD1_FIELD_UNCORE_ERR_STATUS, AMP_PAYLOAD1_FIELD_UNCORE_ERR_MASK, AMP_PAYLOAD1_FIELD_UNCORE_ERR_SEV, AMP_PAYLOAD1_FIELD_CORE_ERR_STATUS, AMP_PAYLOAD1_FIELD_CORE_ERR_MASK, AMP_PAYLOAD1_FIELD_ROOT_ERR_CMD, AMP_PAYLOAD1_FIELD_ROOT_ERR_STATUS, AMP_PAYLOAD1_FIELD_SRC_ID, AMP_PAYLOAD1_FIELD_RESERVED1, AMP_PAYLOAD1_FIELD_RESERVED2, }; enum { AMP_PAYLOAD2_FIELD_ID, AMP_PAYLOAD2_FIELD_TIMESTAMP, AMP_PAYLOAD2_FIELD_TYPE, AMP_PAYLOAD2_FIELD_SUB_TYPE, AMP_PAYLOAD2_FIELD_INS, AMP_PAYLOAD2_FIELD_SOCKET_NUM, AMP_PAYLOAD2_FIELD_CE_REPORT_REG, AMP_PAYLOAD2_FIELD_CE_LOACATION, AMP_PAYLOAD2_FIELD_CE_ADDR, AMP_PAYLOAD2_FIELD_UE_REPORT_REG, AMP_PAYLOAD2_FIELD_UE_LOCATION, AMP_PAYLOAD2_FIELD_UE_ADDR, AMP_PAYLOAD2_FIELD_RESERVED1, AMP_PAYLOAD2_FIELD_RESERVED2, AMP_PAYLOAD2_FIELD_RESERVED3, }; enum { AMP_PAYLOAD3_FIELD_ID, AMP_PAYLOAD3_FIELD_TIMESTAMP, AMP_PAYLOAD3_FIELD_TYPE, AMP_PAYLOAD3_FIELD_SUB_TYPE, AMP_PAYLOAD3_FIELD_INS, AMP_PAYLOAD3_FIELD_SOCKET_NUM, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA0, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA1, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA2, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA3, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA4, AMP_PAYLOAD3_FIELD_FW_SPEC_DATA5 }; void decode_amp_payload0_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct amp_payload0_type_sec *err); #endif 07070100000044000081A4000000000000000000000001611B979000006ED9000000000000000000000000000000000000003900000000rasdaemon-0.6.7.18.git+7ccf12f/non-standard-hisi_hip08.c/* * Copyright (c) 2019 Hisilicon Limited. * * 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. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" #include "ras-non-standard-handler.h" #include "non-standard-hisilicon.h" /* HISI OEM error definitions */ /* HISI OEM format1 error definitions */ #define HISI_OEM_MODULE_ID_MN 0 #define HISI_OEM_MODULE_ID_PLL 1 #define HISI_OEM_MODULE_ID_SLLC 2 #define HISI_OEM_MODULE_ID_AA 3 #define HISI_OEM_MODULE_ID_SIOE 4 #define HISI_OEM_MODULE_ID_POE 5 #define HISI_OEM_MODULE_ID_DISP 8 #define HISI_OEM_MODULE_ID_LPC 9 #define HISI_OEM_MODULE_ID_GIC 13 #define HISI_OEM_MODULE_ID_RDE 14 #define HISI_OEM_MODULE_ID_SAS 15 #define HISI_OEM_MODULE_ID_SATA 16 #define HISI_OEM_MODULE_ID_USB 17 #define HISI_OEM_VALID_SOC_ID BIT(0) #define HISI_OEM_VALID_SOCKET_ID BIT(1) #define HISI_OEM_VALID_NIMBUS_ID BIT(2) #define HISI_OEM_VALID_MODULE_ID BIT(3) #define HISI_OEM_VALID_SUB_MODULE_ID BIT(4) #define HISI_OEM_VALID_ERR_SEVERITY BIT(5) #define HISI_OEM_TYPE1_VALID_ERR_MISC_0 BIT(6) #define HISI_OEM_TYPE1_VALID_ERR_MISC_1 BIT(7) #define HISI_OEM_TYPE1_VALID_ERR_MISC_2 BIT(8) #define HISI_OEM_TYPE1_VALID_ERR_MISC_3 BIT(9) #define HISI_OEM_TYPE1_VALID_ERR_MISC_4 BIT(10) #define HISI_OEM_TYPE1_VALID_ERR_ADDR BIT(11) /* HISI OEM format2 error definitions */ #define HISI_OEM_MODULE_ID_SMMU 0 #define HISI_OEM_MODULE_ID_HHA 1 #define HISI_OEM_MODULE_ID_PA 2 #define HISI_OEM_MODULE_ID_HLLC 3 #define HISI_OEM_MODULE_ID_DDRC 4 #define HISI_OEM_MODULE_ID_L3T 5 #define HISI_OEM_MODULE_ID_L3D 6 #define HISI_OEM_TYPE2_VALID_ERR_FR BIT(6) #define HISI_OEM_TYPE2_VALID_ERR_CTRL BIT(7) #define HISI_OEM_TYPE2_VALID_ERR_STATUS BIT(8) #define HISI_OEM_TYPE2_VALID_ERR_ADDR BIT(9) #define HISI_OEM_TYPE2_VALID_ERR_MISC_0 BIT(10) #define HISI_OEM_TYPE2_VALID_ERR_MISC_1 BIT(11) /* HISI PCIe Local error definitions */ #define HISI_PCIE_SUB_MODULE_ID_AP 0 #define HISI_PCIE_SUB_MODULE_ID_TL 1 #define HISI_PCIE_SUB_MODULE_ID_MAC 2 #define HISI_PCIE_SUB_MODULE_ID_DL 3 #define HISI_PCIE_SUB_MODULE_ID_SDI 4 #define HISI_PCIE_LOCAL_VALID_VERSION BIT(0) #define HISI_PCIE_LOCAL_VALID_SOC_ID BIT(1) #define HISI_PCIE_LOCAL_VALID_SOCKET_ID BIT(2) #define HISI_PCIE_LOCAL_VALID_NIMBUS_ID BIT(3) #define HISI_PCIE_LOCAL_VALID_SUB_MODULE_ID BIT(4) #define HISI_PCIE_LOCAL_VALID_CORE_ID BIT(5) #define HISI_PCIE_LOCAL_VALID_PORT_ID BIT(6) #define HISI_PCIE_LOCAL_VALID_ERR_TYPE BIT(7) #define HISI_PCIE_LOCAL_VALID_ERR_SEVERITY BIT(8) #define HISI_PCIE_LOCAL_VALID_ERR_MISC 9 #define HISI_PCIE_LOCAL_ERR_MISC_MAX 33 #define HISI_BUF_LEN 1024 struct hisi_oem_type1_err_sec { uint32_t val_bits; uint8_t version; uint8_t soc_id; uint8_t socket_id; uint8_t nimbus_id; uint8_t module_id; uint8_t sub_module_id; uint8_t err_severity; uint8_t reserv; uint32_t err_misc_0; uint32_t err_misc_1; uint32_t err_misc_2; uint32_t err_misc_3; uint32_t err_misc_4; uint64_t err_addr; }; struct hisi_oem_type2_err_sec { uint32_t val_bits; uint8_t version; uint8_t soc_id; uint8_t socket_id; uint8_t nimbus_id; uint8_t module_id; uint8_t sub_module_id; uint8_t err_severity; uint8_t reserv; uint32_t err_fr_0; uint32_t err_fr_1; uint32_t err_ctrl_0; uint32_t err_ctrl_1; uint32_t err_status_0; uint32_t err_status_1; uint32_t err_addr_0; uint32_t err_addr_1; uint32_t err_misc0_0; uint32_t err_misc0_1; uint32_t err_misc1_0; uint32_t err_misc1_1; }; struct hisi_pcie_local_err_sec { uint64_t val_bits; uint8_t version; uint8_t soc_id; uint8_t socket_id; uint8_t nimbus_id; uint8_t sub_module_id; uint8_t core_id; uint8_t port_id; uint8_t err_severity; uint16_t err_type; uint8_t reserv[2]; uint32_t err_misc[HISI_PCIE_LOCAL_ERR_MISC_MAX]; }; enum { HIP08_OEM_TYPE1_FIELD_ID, HIP08_OEM_TYPE1_FIELD_TIMESTAMP, HIP08_OEM_TYPE1_FIELD_VERSION, HIP08_OEM_TYPE1_FIELD_SOC_ID, HIP08_OEM_TYPE1_FIELD_SOCKET_ID, HIP08_OEM_TYPE1_FIELD_NIMBUS_ID, HIP08_OEM_TYPE1_FIELD_MODULE_ID, HIP08_OEM_TYPE1_FIELD_SUB_MODULE_ID, HIP08_OEM_TYPE1_FIELD_ERR_SEV, HIP08_OEM_TYPE1_FIELD_REGS_DUMP, }; enum { HIP08_OEM_TYPE2_FIELD_ID, HIP08_OEM_TYPE2_FIELD_TIMESTAMP, HIP08_OEM_TYPE2_FIELD_VERSION, HIP08_OEM_TYPE2_FIELD_SOC_ID, HIP08_OEM_TYPE2_FIELD_SOCKET_ID, HIP08_OEM_TYPE2_FIELD_NIMBUS_ID, HIP08_OEM_TYPE2_FIELD_MODULE_ID, HIP08_OEM_TYPE2_FIELD_SUB_MODULE_ID, HIP08_OEM_TYPE2_FIELD_ERR_SEV, HIP08_OEM_TYPE2_FIELD_REGS_DUMP, }; enum { HIP08_PCIE_LOCAL_FIELD_ID, HIP08_PCIE_LOCAL_FIELD_TIMESTAMP, HIP08_PCIE_LOCAL_FIELD_VERSION, HIP08_PCIE_LOCAL_FIELD_SOC_ID, HIP08_PCIE_LOCAL_FIELD_SOCKET_ID, HIP08_PCIE_LOCAL_FIELD_NIMBUS_ID, HIP08_PCIE_LOCAL_FIELD_SUB_MODULE_ID, HIP08_PCIE_LOCAL_FIELD_CORE_ID, HIP08_PCIE_LOCAL_FIELD_PORT_ID, HIP08_PCIE_LOCAL_FIELD_ERR_SEV, HIP08_PCIE_LOCAL_FIELD_ERR_TYPE, HIP08_PCIE_LOCAL_FIELD_REGS_DUMP, }; struct hisi_module_info { int id; const char *name; const char **sub; int sub_num; }; static const char *pll_submodule_name[] = { "TB_PLL0", "TB_PLL1", "TB_PLL2", "TB_PLL3", "TA_PLL0", "TA_PLL1", "TA_PLL2", "TA_PLL3", "NIMBUS_PLL0", "NIMBUS_PLL1", "NIMBUS_PLL2", "NIMBUS_PLL3", "NIMBUS_PLL4", }; static const char *sllc_submodule_name[] = { "TB_SLLC0", "TB_SLLC1", "TB_SLLC2", "TA_SLLC0", "TA_SLLC1", "TA_SLLC2", "NIMBUS_SLLC0", "NIMBUS_SLLC1", }; static const char *sioe_submodule_name[] = { "TB_SIOE0", "TB_SIOE1", "TB_SIOE2", "TB_SIOE3", "TA_SIOE0", "TA_SIOE1", "TA_SIOE2", "TA_SIOE3", "NIMBUS_SIOE0", "NIMBUS_SIOE1", }; static const char *poe_submodule_name[] = { "TB_POE", "TA_POE", }; static const char *disp_submodule_name[] = { "TB_PERI_DISP", "TB_POE_DISP", "TB_GIC_DISP", "TA_PERI_DISP", "TA_POE_DISP", "TA_GIC_DISP", "HAC_DISP", "PCIE_DISP", "IO_MGMT_DISP", "NETWORK_DISP", }; static const char *sas_submodule_name[] = { "SAS0", "SAS1", }; static const struct hisi_module_info hisi_oem_type1_module[] = { { .id = HISI_OEM_MODULE_ID_PLL, .name = "PLL", .sub = pll_submodule_name, .sub_num = ARRAY_SIZE(pll_submodule_name), }, { .id = HISI_OEM_MODULE_ID_SAS, .name = "SAS", .sub = sas_submodule_name, .sub_num = ARRAY_SIZE(sas_submodule_name), }, { .id = HISI_OEM_MODULE_ID_POE, .name = "POE", .sub = poe_submodule_name, .sub_num = ARRAY_SIZE(poe_submodule_name), }, { .id = HISI_OEM_MODULE_ID_SLLC, .name = "SLLC", .sub = sllc_submodule_name, .sub_num = ARRAY_SIZE(sllc_submodule_name), }, { .id = HISI_OEM_MODULE_ID_SIOE, .name = "SIOE", .sub = sioe_submodule_name, .sub_num = ARRAY_SIZE(sioe_submodule_name), }, { .id = HISI_OEM_MODULE_ID_DISP, .name = "DISP", .sub = disp_submodule_name, .sub_num = ARRAY_SIZE(disp_submodule_name), }, { .id = HISI_OEM_MODULE_ID_MN, .name = "MN", }, { .id = HISI_OEM_MODULE_ID_AA, .name = "AA", }, { .id = HISI_OEM_MODULE_ID_LPC, .name = "LPC", }, { .id = HISI_OEM_MODULE_ID_GIC, .name = "GIC", }, { .id = HISI_OEM_MODULE_ID_RDE, .name = "RDE", }, { .id = HISI_OEM_MODULE_ID_SATA, .name = "SATA", }, { .id = HISI_OEM_MODULE_ID_USB, .name = "USB", }, { } }; static const char *smmu_submodule_name[] = { "HAC_SMMU", "PCIE_SMMU", "MGMT_SMMU", "NIC_SMMU", }; static const char *hllc_submodule_name[] = { "HLLC0", "HLLC1", "HLLC2", }; static const char *hha_submodule_name[] = { "TB_HHA0", "TB_HHA1", "TA_HHA0", "TA_HHA1" }; static const char *ddrc_submodule_name[] = { "TB_DDRC0", "TB_DDRC1", "TB_DDRC2", "TB_DDRC3", "TA_DDRC0", "TA_DDRC1", "TA_DDRC2", "TA_DDRC3", }; static const char *l3tag_submodule_name[] = { "TB_PARTITION0", "TB_PARTITION1", "TB_PARTITION2", "TB_PARTITION3", "TB_PARTITION4", "TB_PARTITION5", "TB_PARTITION6", "TB_PARTITION7", "TA_PARTITION0", "TA_PARTITION1", "TA_PARTITION2", "TA_PARTITION3", "TA_PARTITION4", "TA_PARTITION5", "TA_PARTITION6", "TA_PARTITION7", }; static const char *l3data_submodule_name[] = { "TB_BANK0", "TB_BANK1", "TB_BANK2", "TB_BANK3", "TA_BANK0", "TA_BANK1", "TA_BANK2", "TA_BANK3", }; static const struct hisi_module_info hisi_oem_type2_module[] = { { .id = HISI_OEM_MODULE_ID_SMMU, .name = "SMMU", .sub = smmu_submodule_name, .sub_num = ARRAY_SIZE(smmu_submodule_name), }, { .id = HISI_OEM_MODULE_ID_HHA, .name = "HHA", .sub = hha_submodule_name, .sub_num = ARRAY_SIZE(hha_submodule_name), }, { .id = HISI_OEM_MODULE_ID_PA, .name = "PA", }, { .id = HISI_OEM_MODULE_ID_HLLC, .name = "HLLC", .sub = hllc_submodule_name, .sub_num = ARRAY_SIZE(hllc_submodule_name), }, { .id = HISI_OEM_MODULE_ID_DDRC, .name = "DDRC", .sub = ddrc_submodule_name, .sub_num = ARRAY_SIZE(ddrc_submodule_name), }, { .id = HISI_OEM_MODULE_ID_L3T, .name = "L3TAG", .sub = l3tag_submodule_name, .sub_num = ARRAY_SIZE(l3tag_submodule_name), }, { .id = HISI_OEM_MODULE_ID_L3D, .name = "L3DATA", .sub = l3data_submodule_name, .sub_num = ARRAY_SIZE(l3data_submodule_name), }, { } }; static const char *oem_module_name(const struct hisi_module_info *info, uint8_t module_id) { const struct hisi_module_info *module = &info[0]; for (; module->name; module++) { if (module->id != module_id) continue; return module->name; } return "unknown"; } static const char *oem_submodule_name(const struct hisi_module_info *info, uint8_t module_id, uint8_t sub_module_id) { const struct hisi_module_info *module = &info[0]; for (; module->name; module++) { const char **submodule = module->sub; if (module->id != module_id) continue; if (module->sub == NULL) return module->name; if (sub_module_id >= module->sub_num) return "unknown"; return submodule[sub_module_id]; } return "unknown"; } static char *pcie_local_sub_module_name(uint8_t id) { switch (id) { case HISI_PCIE_SUB_MODULE_ID_AP: return "AP_Layer"; case HISI_PCIE_SUB_MODULE_ID_TL: return "TL_Layer"; case HISI_PCIE_SUB_MODULE_ID_MAC: return "MAC_Layer"; case HISI_PCIE_SUB_MODULE_ID_DL: return "DL_Layer"; case HISI_PCIE_SUB_MODULE_ID_SDI: return "SDI_Layer"; default: break; } return "unknown"; } #ifdef HAVE_SQLITE3 static const struct db_fields hip08_oem_event_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "version", .type = "INTEGER" }, { .name = "soc_id", .type = "INTEGER" }, { .name = "socket_id", .type = "INTEGER" }, { .name = "nimbus_id", .type = "INTEGER" }, { .name = "module_id", .type = "TEXT" }, { .name = "sub_module_id", .type = "TEXT" }, { .name = "err_severity", .type = "TEXT" }, { .name = "regs_dump", .type = "TEXT" }, }; static const struct db_table_descriptor hip08_oem_type1_event_tab = { .name = "hip08_oem_type1_event_v2", .fields = hip08_oem_event_fields, .num_fields = ARRAY_SIZE(hip08_oem_event_fields), }; static const struct db_table_descriptor hip08_oem_type2_event_tab = { .name = "hip08_oem_type2_event_v2", .fields = hip08_oem_event_fields, .num_fields = ARRAY_SIZE(hip08_oem_event_fields), }; static const struct db_fields hip08_pcie_local_event_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "version", .type = "INTEGER" }, { .name = "soc_id", .type = "INTEGER" }, { .name = "socket_id", .type = "INTEGER" }, { .name = "nimbus_id", .type = "INTEGER" }, { .name = "sub_module_id", .type = "TEXT" }, { .name = "core_id", .type = "INTEGER" }, { .name = "port_id", .type = "INTEGER" }, { .name = "err_severity", .type = "TEXT" }, { .name = "err_type", .type = "INTEGER" }, { .name = "regs_dump", .type = "TEXT" }, }; static const struct db_table_descriptor hip08_pcie_local_event_tab = { .name = "hip08_pcie_local_event_v2", .fields = hip08_pcie_local_event_fields, .num_fields = ARRAY_SIZE(hip08_pcie_local_event_fields), }; #endif #define IN_RANGE(p, start, end) ((p) >= (start) && (p) < (end)) static void decode_oem_type1_err_hdr(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct hisi_oem_type1_err_sec *err) { char buf[HISI_BUF_LEN]; char *p = buf; char *end = buf + HISI_BUF_LEN; p += snprintf(p, end - p, "[ table_version=%d ", err->version); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE1_FIELD_VERSION, err->version, NULL); if (err->val_bits & HISI_OEM_VALID_SOC_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "SOC_ID=%d ", err->soc_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE1_FIELD_SOC_ID, err->soc_id, NULL); } if (err->val_bits & HISI_OEM_VALID_SOCKET_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "socket_ID=%d ", err->socket_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE1_FIELD_SOCKET_ID, err->socket_id, NULL); } if (err->val_bits & HISI_OEM_VALID_NIMBUS_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "nimbus_ID=%d ", err->nimbus_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE1_FIELD_NIMBUS_ID, err->nimbus_id, NULL); } if (err->val_bits & HISI_OEM_VALID_MODULE_ID && IN_RANGE(p, buf, end)) { const char *str = oem_module_name(hisi_oem_type1_module, err->module_id); p += snprintf(p, end - p, "module=%s ", str); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE1_FIELD_MODULE_ID, 0, str); } if (err->val_bits & HISI_OEM_VALID_SUB_MODULE_ID && IN_RANGE(p, buf, end)) { const char *str = oem_submodule_name(hisi_oem_type1_module, err->module_id, err->sub_module_id); p += snprintf(p, end - p, "submodule=%s ", str); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE1_FIELD_SUB_MODULE_ID, 0, str); } if (err->val_bits & HISI_OEM_VALID_ERR_SEVERITY && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "error_severity=%s ", err_severity(err->err_severity)); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE1_FIELD_ERR_SEV, 0, err_severity(err->err_severity)); } if (IN_RANGE(p, buf, end)) p += snprintf(p, end - p, "]"); trace_seq_printf(s, "%s\n", buf); } static void decode_oem_type1_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct hisi_oem_type1_err_sec *err) { char buf[HISI_BUF_LEN]; char *p = buf; char *end = buf + HISI_BUF_LEN; trace_seq_printf(s, "Reg Dump:\n"); if (err->val_bits & HISI_OEM_TYPE1_VALID_ERR_MISC_0) { trace_seq_printf(s, "ERR_MISC0=0x%x\n", err->err_misc_0); p += snprintf(p, end - p, "ERR_MISC0=0x%x ", err->err_misc_0); } if (err->val_bits & HISI_OEM_TYPE1_VALID_ERR_MISC_1 && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC1=0x%x\n", err->err_misc_1); p += snprintf(p, end - p, "ERR_MISC1=0x%x ", err->err_misc_1); } if (err->val_bits & HISI_OEM_TYPE1_VALID_ERR_MISC_2 && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC2=0x%x\n", err->err_misc_2); p += snprintf(p, end - p, "ERR_MISC2=0x%x ", err->err_misc_2); } if (err->val_bits & HISI_OEM_TYPE1_VALID_ERR_MISC_3 && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC3=0x%x\n", err->err_misc_3); p += snprintf(p, end - p, "ERR_MISC3=0x%x ", err->err_misc_3); } if (err->val_bits & HISI_OEM_TYPE1_VALID_ERR_MISC_4 && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC4=0x%x\n", err->err_misc_4); p += snprintf(p, end - p, "ERR_MISC4=0x%x ", err->err_misc_4); } if (err->val_bits & HISI_OEM_TYPE1_VALID_ERR_ADDR && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_ADDR=0x%llx\n", (unsigned long long)err->err_addr); p += snprintf(p, end - p, "ERR_ADDR=0x%llx ", (unsigned long long)err->err_addr); } if (p > buf && p < end) { p--; *p = '\0'; } record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE1_FIELD_REGS_DUMP, 0, buf); step_vendor_data_tab(ev_decoder, "hip08_oem_type1_event_tab"); } /* error data decoding functions */ static int decode_hip08_oem_type1_error(struct ras_events *ras, struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, struct ras_non_standard_event *event) { const struct hisi_oem_type1_err_sec *err = (struct hisi_oem_type1_err_sec*)event->error; if (err->val_bits == 0) { trace_seq_printf(s, "%s: no valid error information\n", __func__); return -1; } #ifdef HAVE_SQLITE3 if (!ev_decoder->stmt_dec_record) { if (ras_mc_add_vendor_table(ras, &ev_decoder->stmt_dec_record, &hip08_oem_type1_event_tab) != SQLITE_OK) { trace_seq_printf(s, "create sql hip08_oem_type1_event_tab fail\n"); return -1; } } #endif record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE1_FIELD_TIMESTAMP, 0, event->timestamp); trace_seq_printf(s, "\nHISI HIP08: OEM Type-1 Error\n"); decode_oem_type1_err_hdr(ev_decoder, s, err); decode_oem_type1_err_regs(ev_decoder, s, err); return 0; } static void decode_oem_type2_err_hdr(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct hisi_oem_type2_err_sec *err) { char buf[HISI_BUF_LEN]; char *p = buf; char *end = buf + HISI_BUF_LEN; p += snprintf(p, end - p, "[ table_version=%d ", err->version); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE2_FIELD_VERSION, err->version, NULL); if (err->val_bits & HISI_OEM_VALID_SOC_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "SOC_ID=%d ", err->soc_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE2_FIELD_SOC_ID, err->soc_id, NULL); } if (err->val_bits & HISI_OEM_VALID_SOCKET_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "socket_ID=%d ", err->socket_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE2_FIELD_SOCKET_ID, err->socket_id, NULL); } if (err->val_bits & HISI_OEM_VALID_NIMBUS_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "nimbus_ID=%d ", err->nimbus_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_OEM_TYPE2_FIELD_NIMBUS_ID, err->nimbus_id, NULL); } if (err->val_bits & HISI_OEM_VALID_MODULE_ID && IN_RANGE(p, buf, end)) { const char *str = oem_module_name(hisi_oem_type2_module, err->module_id); p += snprintf(p, end - p, "module=%s ", str); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE2_FIELD_MODULE_ID, 0, str); } if (err->val_bits & HISI_OEM_VALID_SUB_MODULE_ID && IN_RANGE(p, buf, end)) { const char *str = oem_submodule_name(hisi_oem_type2_module, err->module_id, err->sub_module_id); p += snprintf(p, end - p, "submodule=%s ", str); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE2_FIELD_SUB_MODULE_ID, 0, str); } if (err->val_bits & HISI_OEM_VALID_ERR_SEVERITY && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "error_severity=%s ", err_severity(err->err_severity)); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE2_FIELD_ERR_SEV, 0, err_severity(err->err_severity)); } if (IN_RANGE(p, buf, end)) p += snprintf(p, end - p, "]"); trace_seq_printf(s, "%s\n", buf); } static void decode_oem_type2_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct hisi_oem_type2_err_sec *err) { char buf[HISI_BUF_LEN]; char *p = buf; char *end = buf + HISI_BUF_LEN; trace_seq_printf(s, "Reg Dump:\n"); if (err->val_bits & HISI_OEM_TYPE2_VALID_ERR_FR) { trace_seq_printf(s, "ERR_FR_0=0x%x\n", err->err_fr_0); trace_seq_printf(s, "ERR_FR_1=0x%x\n", err->err_fr_1); p += snprintf(p, end - p, "ERR_FR_0=0x%x ERR_FR_1=0x%x ", err->err_fr_0, err->err_fr_1); } if (err->val_bits & HISI_OEM_TYPE2_VALID_ERR_CTRL && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_CTRL_0=0x%x\n", err->err_ctrl_0); trace_seq_printf(s, "ERR_CTRL_1=0x%x\n", err->err_ctrl_1); p += snprintf(p, end - p, "ERR_CTRL_0=0x%x ERR_CTRL_1=0x%x ", err->err_ctrl_0, err->err_ctrl_1); } if (err->val_bits & HISI_OEM_TYPE2_VALID_ERR_STATUS && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_STATUS_0=0x%x\n", err->err_status_0); trace_seq_printf(s, "ERR_STATUS_1=0x%x\n", err->err_status_1); p += snprintf(p, end - p, "ERR_STATUS_0=0x%x ERR_STATUS_1=0x%x ", err->err_status_0, err->err_status_1); } if (err->val_bits & HISI_OEM_TYPE2_VALID_ERR_ADDR && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_ADDR_0=0x%x\n", err->err_addr_0); trace_seq_printf(s, "ERR_ADDR_1=0x%x\n", err->err_addr_1); p += snprintf(p, end - p, "ERR_ADDR_0=0x%x ERR_ADDR_1=0x%x ", err->err_addr_0, err->err_addr_1); } if (err->val_bits & HISI_OEM_TYPE2_VALID_ERR_MISC_0 && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC0_0=0x%x\n", err->err_misc0_0); trace_seq_printf(s, "ERR_MISC0_1=0x%x\n", err->err_misc0_1); p += snprintf(p, end - p, "ERR_MISC0_0=0x%x ERR_MISC0_1=0x%x ", err->err_misc0_0, err->err_misc0_1); } if (err->val_bits & HISI_OEM_TYPE2_VALID_ERR_MISC_1 && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC1_0=0x%x\n", err->err_misc1_0); trace_seq_printf(s, "ERR_MISC1_1=0x%x\n", err->err_misc1_1); p += snprintf(p, end - p, "ERR_MISC1_0=0x%x ERR_MISC1_1=0x%x ", err->err_misc1_0, err->err_misc1_1); } if (p > buf && p < end) { p--; *p = '\0'; } record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE2_FIELD_REGS_DUMP, 0, buf); step_vendor_data_tab(ev_decoder, "hip08_oem_type2_event_tab"); } static int decode_hip08_oem_type2_error(struct ras_events *ras, struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, struct ras_non_standard_event *event) { const struct hisi_oem_type2_err_sec *err = (struct hisi_oem_type2_err_sec *)event->error; if (err->val_bits == 0) { trace_seq_printf(s, "%s: no valid error information\n", __func__); return -1; } #ifdef HAVE_SQLITE3 if (!ev_decoder->stmt_dec_record) { if (ras_mc_add_vendor_table(ras, &ev_decoder->stmt_dec_record, &hip08_oem_type2_event_tab) != SQLITE_OK) { trace_seq_printf(s, "create sql hip08_oem_type2_event_tab fail\n"); return -1; } } #endif record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_OEM_TYPE2_FIELD_TIMESTAMP, 0, event->timestamp); trace_seq_printf(s, "\nHISI HIP08: OEM Type-2 Error\n"); decode_oem_type2_err_hdr(ev_decoder, s, err); decode_oem_type2_err_regs(ev_decoder, s, err); return 0; } static void decode_pcie_local_err_hdr(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct hisi_pcie_local_err_sec *err) { char buf[HISI_BUF_LEN]; char *p = buf; char *end = buf + HISI_BUF_LEN; p += snprintf(p, end - p, "[ table_version=%d ", err->version); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_VERSION, err->version, NULL); if (err->val_bits & HISI_PCIE_LOCAL_VALID_SOC_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "SOC_ID=%d ", err->soc_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_SOC_ID, err->soc_id, NULL); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_SOCKET_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "socket_ID=%d ", err->socket_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_SOCKET_ID, err->socket_id, NULL); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_NIMBUS_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "nimbus_ID=%d ", err->nimbus_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_NIMBUS_ID, err->nimbus_id, NULL); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_SUB_MODULE_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "submodule=%s ", pcie_local_sub_module_name(err->sub_module_id)); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_PCIE_LOCAL_FIELD_SUB_MODULE_ID, 0, pcie_local_sub_module_name(err->sub_module_id)); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_CORE_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "core_ID=core%d ", err->core_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_CORE_ID, err->core_id, NULL); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_PORT_ID && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "port_ID=port%d ", err->port_id); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_PORT_ID, err->port_id, NULL); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_ERR_SEVERITY && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "error_severity=%s ", err_severity(err->err_severity)); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_PCIE_LOCAL_FIELD_ERR_SEV, 0, err_severity(err->err_severity)); } if (err->val_bits & HISI_PCIE_LOCAL_VALID_ERR_TYPE && IN_RANGE(p, buf, end)) { p += snprintf(p, end - p, "error_type=0x%x ", err->err_type); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_INT, HIP08_PCIE_LOCAL_FIELD_ERR_TYPE, err->err_type, NULL); } if (IN_RANGE(p, buf, end)) p += snprintf(p, end - p, "]"); trace_seq_printf(s, "%s\n", buf); } static void decode_pcie_local_err_regs(struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, const struct hisi_pcie_local_err_sec *err) { char buf[HISI_BUF_LEN]; char *p = buf; char *end = buf + HISI_BUF_LEN; uint32_t i; trace_seq_printf(s, "Reg Dump:\n"); for (i = 0; i < HISI_PCIE_LOCAL_ERR_MISC_MAX; i++) { if (err->val_bits & BIT(HISI_PCIE_LOCAL_VALID_ERR_MISC + i) && IN_RANGE(p, buf, end)) { trace_seq_printf(s, "ERR_MISC_%d=0x%x\n", i, err->err_misc[i]); p += snprintf(p, end - p, "ERR_MISC_%d=0x%x ", i, err->err_misc[i]); } } if (p > buf && p < end) { p--; *p = '\0'; } record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_PCIE_LOCAL_FIELD_REGS_DUMP, 0, buf); step_vendor_data_tab(ev_decoder, "hip08_pcie_local_event_tab"); } static int decode_hip08_pcie_local_error(struct ras_events *ras, struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, struct ras_non_standard_event *event) { const struct hisi_pcie_local_err_sec *err = (struct hisi_pcie_local_err_sec *)event->error; if (err->val_bits == 0) { trace_seq_printf(s, "%s: no valid error information\n", __func__); return -1; } #ifdef HAVE_SQLITE3 if (!ev_decoder->stmt_dec_record) { if (ras_mc_add_vendor_table(ras, &ev_decoder->stmt_dec_record, &hip08_pcie_local_event_tab) != SQLITE_OK) { trace_seq_printf(s, "create sql hip08_pcie_local_event_tab fail\n"); return -1; } } #endif record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HIP08_PCIE_LOCAL_FIELD_TIMESTAMP, 0, event->timestamp); trace_seq_printf(s, "\nHISI HIP08: PCIe local error\n"); decode_pcie_local_err_hdr(ev_decoder, s, err); decode_pcie_local_err_regs(ev_decoder, s, err); return 0; } static struct ras_ns_ev_decoder hip08_ns_ev_decoder[] = { { .sec_type = "1f8161e155d641e6bd107afd1dc5f7c5", .decode = decode_hip08_oem_type1_error, }, { .sec_type = "45534ea6ce2341158535e07ab3aef91d", .decode = decode_hip08_oem_type2_error, }, { .sec_type = "b2889fc9e7d74f9da867af42e98be772", .decode = decode_hip08_pcie_local_error, }, }; static void __attribute__((constructor)) hip08_init(void) { int i; for (i = 0; i < ARRAY_SIZE(hip08_ns_ev_decoder); i++) register_ns_ev_decoder(&hip08_ns_ev_decoder[i]); } 07070100000045000081A4000000000000000000000001611B97900000207D000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/non-standard-hisilicon.c/* * Copyright (c) 2020 Hisilicon Limited. * * 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. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" #include "non-standard-hisilicon.h" #define HISI_BUF_LEN 2048 struct hisi_common_error_section { uint32_t val_bits; uint8_t version; uint8_t soc_id; uint8_t socket_id; uint8_t totem_id; uint8_t nimbus_id; uint8_t subsystem_id; uint8_t module_id; uint8_t submodule_id; uint8_t core_id; uint8_t port_id; uint16_t err_type; struct { uint8_t function; uint8_t device; uint16_t segment; uint8_t bus; uint8_t reserved[3]; } pcie_info; uint8_t err_severity; uint8_t reserved[3]; uint32_t reg_array_size; uint32_t reg_array[]; }; enum { HISI_COMMON_VALID_SOC_ID, HISI_COMMON_VALID_SOCKET_ID, HISI_COMMON_VALID_TOTEM_ID, HISI_COMMON_VALID_NIMBUS_ID, HISI_COMMON_VALID_SUBSYSTEM_ID, HISI_COMMON_VALID_MODULE_ID, HISI_COMMON_VALID_SUBMODULE_ID, HISI_COMMON_VALID_CORE_ID, HISI_COMMON_VALID_PORT_ID, HISI_COMMON_VALID_ERR_TYPE, HISI_COMMON_VALID_PCIE_INFO, HISI_COMMON_VALID_ERR_SEVERITY, HISI_COMMON_VALID_REG_ARRAY_SIZE, }; enum { HISI_COMMON_FIELD_ID, HISI_COMMON_FIELD_TIMESTAMP, HISI_COMMON_FIELD_ERR_INFO, HISI_COMMON_FIELD_REGS_DUMP, }; struct hisi_event { char error_msg[HISI_BUF_LEN]; char reg_msg[HISI_BUF_LEN]; }; #ifdef HAVE_SQLITE3 void record_vendor_data(struct ras_ns_ev_decoder *ev_decoder, enum hisi_oem_data_type data_type, int id, int64_t data, const char *text) { switch (data_type) { case HISI_OEM_DATA_TYPE_INT: sqlite3_bind_int(ev_decoder->stmt_dec_record, id, data); break; case HISI_OEM_DATA_TYPE_INT64: sqlite3_bind_int64(ev_decoder->stmt_dec_record, id, data); break; case HISI_OEM_DATA_TYPE_TEXT: sqlite3_bind_text(ev_decoder->stmt_dec_record, id, text, -1, NULL); break; } } int step_vendor_data_tab(struct ras_ns_ev_decoder *ev_decoder, const char *name) { int rc; rc = sqlite3_step(ev_decoder->stmt_dec_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do %s step on sqlite: error = %d\n", name, rc); rc = sqlite3_reset(ev_decoder->stmt_dec_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to reset %s on sqlite: error = %d\n", name, rc); rc = sqlite3_clear_bindings(ev_decoder->stmt_dec_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to clear bindings %s on sqlite: error = %d\n", name, rc); return rc; } #else void record_vendor_data(struct ras_ns_ev_decoder *ev_decoder, enum hisi_oem_data_type data_type, int id, int64_t data, const char *text) { } int step_vendor_data_tab(struct ras_ns_ev_decoder *ev_decoder, const char *name) { return 0; } #endif #ifdef HAVE_SQLITE3 static const struct db_fields hisi_common_section_fields[] = { { .name = "id", .type = "INTEGER PRIMARY KEY" }, { .name = "timestamp", .type = "TEXT" }, { .name = "err_info", .type = "TEXT" }, { .name = "regs_dump", .type = "TEXT" }, }; static const struct db_table_descriptor hisi_common_section_tab = { .name = "hisi_common_section", .fields = hisi_common_section_fields, .num_fields = ARRAY_SIZE(hisi_common_section_fields), }; #endif static const char* soc_desc[] = { "Kunpeng916", "Kunpeng920", "Kunpeng930", }; static const char* module_name[] = { "MN", "PLL", "SLLC", "AA", "SIOE", "POE", "CPA", "DISP", "GIC", "ITS", "AVSBUS", "CS", "PPU", "SMMU", "PA", "HLLC", "DDRC", "L3TAG", "L3DATA", "PCS", "MATA", "PCIe Local", "SAS", "SATA", "NIC", "RoCE", "USB", "ZIP", "HPRE", "SEC", "RDE", "MEE", "HHA", }; static const char* get_soc_desc(uint8_t soc_id) { if (soc_id >= sizeof(soc_desc)/sizeof(char *)) return "unknown"; return soc_desc[soc_id]; } static void decode_module(struct hisi_event *event, uint8_t module_id) { if (module_id >= sizeof(module_name)/sizeof(char *)) HISI_SNPRINTF(event->error_msg, "module=unknown(id=%d) ", module_id); else HISI_SNPRINTF(event->error_msg, "module=%s ", module_name[module_id]); } static void decode_hisi_common_section_hdr(struct ras_ns_ev_decoder *ev_decoder, const struct hisi_common_error_section *err, struct hisi_event *event) { HISI_SNPRINTF(event->error_msg, "[ table_version=%d", err->version); if (err->val_bits & BIT(HISI_COMMON_VALID_SOC_ID)) HISI_SNPRINTF(event->error_msg, "soc=%s", get_soc_desc(err->soc_id)); if (err->val_bits & BIT(HISI_COMMON_VALID_SOCKET_ID)) HISI_SNPRINTF(event->error_msg, "socket_id=%d", err->socket_id); if (err->val_bits & BIT(HISI_COMMON_VALID_TOTEM_ID)) HISI_SNPRINTF(event->error_msg, "totem_id=%d", err->totem_id); if (err->val_bits & BIT(HISI_COMMON_VALID_NIMBUS_ID)) HISI_SNPRINTF(event->error_msg, "nimbus_id=%d", err->nimbus_id); if (err->val_bits & BIT(HISI_COMMON_VALID_SUBSYSTEM_ID)) HISI_SNPRINTF(event->error_msg, "subsystem_id=%d", err->subsystem_id); if (err->val_bits & BIT(HISI_COMMON_VALID_MODULE_ID)) decode_module(event, err->module_id); if (err->val_bits & BIT(HISI_COMMON_VALID_SUBMODULE_ID)) HISI_SNPRINTF(event->error_msg, "submodule_id=%d", err->submodule_id); if (err->val_bits & BIT(HISI_COMMON_VALID_CORE_ID)) HISI_SNPRINTF(event->error_msg, "core_id=%d", err->core_id); if (err->val_bits & BIT(HISI_COMMON_VALID_PORT_ID)) HISI_SNPRINTF(event->error_msg, "port_id=%d", err->port_id); if (err->val_bits & BIT(HISI_COMMON_VALID_ERR_TYPE)) HISI_SNPRINTF(event->error_msg, "err_type=%d", err->err_type); if (err->val_bits & BIT(HISI_COMMON_VALID_PCIE_INFO)) HISI_SNPRINTF(event->error_msg, "pcie_device_id=%04x:%02x:%02x.%x", err->pcie_info.segment, err->pcie_info.bus, err->pcie_info.device, err->pcie_info.function); if (err->val_bits & BIT(HISI_COMMON_VALID_ERR_SEVERITY)) HISI_SNPRINTF(event->error_msg, "err_severity=%s", err_severity(err->err_severity)); HISI_SNPRINTF(event->error_msg, "]"); } static int decode_hisi_common_section(struct ras_events *ras, struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, struct ras_non_standard_event *event) { const struct hisi_common_error_section *err = (struct hisi_common_error_section *)event->error; struct hisi_event hevent; #ifdef HAVE_SQLITE3 if (ras->record_events && !ev_decoder->stmt_dec_record) { if (ras_mc_add_vendor_table(ras, &ev_decoder->stmt_dec_record, &hisi_common_section_tab) != SQLITE_OK) { trace_seq_printf(s, "create sql hisi_common_section_tab fail\n"); return -1; } } #endif memset(&hevent, 0, sizeof(struct hisi_event)); trace_seq_printf(s, "\nHisilicon Common Error Section:\n"); decode_hisi_common_section_hdr(ev_decoder, err, &hevent); trace_seq_printf(s, "%s\n", hevent.error_msg); if (err->val_bits & BIT(HISI_COMMON_VALID_REG_ARRAY_SIZE) && err->reg_array_size > 0) { int i; trace_seq_printf(s, "Register Dump:\n"); for (i = 0; i < err->reg_array_size / sizeof(uint32_t); i++) { trace_seq_printf(s, "reg%02d=0x%08x\n", i, err->reg_array[i]); HISI_SNPRINTF(hevent.reg_msg, "reg%02d=0x%08x", i, err->reg_array[i]); } } if (ras->record_events) { record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HISI_COMMON_FIELD_TIMESTAMP, 0, event->timestamp); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HISI_COMMON_FIELD_ERR_INFO, 0, hevent.error_msg); record_vendor_data(ev_decoder, HISI_OEM_DATA_TYPE_TEXT, HISI_COMMON_FIELD_REGS_DUMP, 0, hevent.reg_msg); step_vendor_data_tab(ev_decoder, "hisi_common_section_tab"); } return 0; } static struct ras_ns_ev_decoder hisi_section_ns_ev_decoder[] = { { .sec_type = "c8b328a899174af69a132e08ab2e7586", .decode = decode_hisi_common_section, }, }; static void __attribute__((constructor)) hisi_ns_init(void) { int i; for (i = 0; i < ARRAY_SIZE(hisi_section_ns_ev_decoder); i++) register_ns_ev_decoder(&hisi_section_ns_ev_decoder[i]); } 07070100000046000081A4000000000000000000000001611B9790000004FE000000000000000000000000000000000000003800000000rasdaemon-0.6.7.18.git+7ccf12f/non-standard-hisilicon.h/* * Copyright (c) 2020 Hisilicon Limited. * * 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. * */ #ifndef __NON_STANDARD_HISILICON_H #define __NON_STANDARD_HISILICON_H #include "ras-non-standard-handler.h" #include "ras-mc-handler.h" #define HISI_SNPRINTF mce_snprintf #define HISI_ERR_SEVERITY_NFE 0 #define HISI_ERR_SEVERITY_FE 1 #define HISI_ERR_SEVERITY_CE 2 #define HISI_ERR_SEVERITY_NONE 3 enum hisi_oem_data_type { HISI_OEM_DATA_TYPE_INT, HISI_OEM_DATA_TYPE_INT64, HISI_OEM_DATA_TYPE_TEXT, }; /* helper functions */ static inline char *err_severity(uint8_t err_sev) { switch (err_sev) { case HISI_ERR_SEVERITY_NFE: return "recoverable"; case HISI_ERR_SEVERITY_FE: return "fatal"; case HISI_ERR_SEVERITY_CE: return "corrected"; case HISI_ERR_SEVERITY_NONE: return "none"; default: break; } return "unknown"; } void record_vendor_data(struct ras_ns_ev_decoder *ev_decoder, enum hisi_oem_data_type data_type, int id, int64_t data, const char *text); int step_vendor_data_tab(struct ras_ns_ev_decoder *ev_decoder, const char *name); #endif 07070100000047000081A4000000000000000000000001611B979000001598000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/ras-aer-handler.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "libtrace/kbuffer.h" #include "ras-aer-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "bitfield.h" #include "ras-report.h" /* bit field meaning for correctable error */ static const char *aer_cor_errors[32] = { /* Correctable errors */ [0] = "Receiver Error", [6] = "Bad TLP", [7] = "Bad DLLP", [8] = "RELAY_NUM Rollover", [12] = "Replay Timer Timeout", [13] = "Advisory Non-Fatal", }; /* bit field meaning for uncorrectable error */ static const char *aer_uncor_errors[32] = { /* Uncorrectable errors */ [4] = "Data Link Protocol", [12] = "Poisoned TLP", [13] = "Flow Control Protocol", [14] = "Completion Timeout", [15] = "Completer Abort", [16] = "Unexpected Completion", [17] = "Receiver Overflow", [18] = "Malformed TLP", [19] = "ECRC", [20] = "Unsupported Request", }; #define BUF_LEN 1024 int ras_aer_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { int len; unsigned long long severity_val; unsigned long long status_val; unsigned long long val; struct ras_events *ras = context; time_t now; struct tm *tm; struct ras_aer_event ev; char buf[BUF_LEN]; char ipmi_add_sel[105]; uint8_t sel_data[5]; int seg, bus, dev, fn; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); ev.dev_name = pevent_get_field_raw(s, event, "dev_name", record, &len, 1); if (!ev.dev_name) return -1; trace_seq_printf(s, "%s ", ev.dev_name); if (pevent_get_field_val(s, event, "status", record, &status_val, 1) < 0) return -1; if (pevent_get_field_val(s, event, "severity", record, &severity_val, 1) < 0) return -1; /* Fills the error buffer. If it is a correctable error then use the * aer_cor_errors bit field. Otherwise use aer_uncor_errors. */ if (severity_val == HW_EVENT_AER_CORRECTED) bitfield_msg(buf, sizeof(buf), aer_cor_errors, 32, 0, 0, status_val); else bitfield_msg(buf, sizeof(buf), aer_uncor_errors, 32, 0, 0, status_val); ev.msg = buf; if (pevent_get_field_val(s, event, "tlp_header_valid", record, &val, 1) < 0) return -1; ev.tlp_header_valid = val; if (ev.tlp_header_valid) { ev.tlp_header = pevent_get_field_raw(s, event, "tlp_header", record, &len, 1); snprintf((buf + strlen(ev.msg)), BUF_LEN - strlen(ev.msg), " TLP Header: %08x %08x %08x %08x", ev.tlp_header[0], ev.tlp_header[1], ev.tlp_header[2], ev.tlp_header[3]); } trace_seq_printf(s, "%s ", ev.msg); /* Use hw_event_aer_err_type switch between different severity_val */ switch (severity_val) { case HW_EVENT_AER_UNCORRECTED_NON_FATAL: ev.error_type = "Uncorrected (Non-Fatal)"; sel_data[0] = 0xca; break; case HW_EVENT_AER_UNCORRECTED_FATAL: ev.error_type = "Uncorrected (Fatal)"; sel_data[0] = 0xca; break; case HW_EVENT_AER_CORRECTED: ev.error_type = "Corrected"; sel_data[0] = 0xbf; break; default: ev.error_type = "Unknown severity"; sel_data[0] = 0xbf; } trace_seq_puts(s, ev.error_type); /* Insert data into the SGBD */ #ifdef HAVE_SQLITE3 ras_store_aer_event(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_aer_event(ras, &ev); #endif #ifdef HAVE_AMP_NS_DECODE /* * Get PCIe AER error source seg/bus/dev/fn and save it into * BMC OEM SEL, ipmitool raw 0x0a 0x44 is IPMI command-Add SEL * entry, please refer IPMI specificaiton chapter 31.6. 0xcd3a * is manufactuer ID(ampere),byte 12 is sensor num(CE is 0xBF, * UE is 0xCA), byte 13~14 is segment number, byte 15 is bus * number, byte 16[7:3] is device number, byte 16[2:0] is * function number */ sscanf(ev.dev_name, "%x:%x:%x.%x", &seg, &bus, &dev, &fn); sel_data[1] = seg & 0xff; sel_data[2] = (seg & 0xff00) >> 8; sel_data[3] = bus; sel_data[4] = (((dev & 0x1f) << 3) | (fn & 0x7)); sprintf(ipmi_add_sel, "ipmitool raw 0x0a 0x44 0x00 0x00 0xc0 0x00 0x00 0x00 0x00 0x3a 0xcd 0x00 0xc0 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", sel_data[0], sel_data[1], sel_data[2], sel_data[3], sel_data[4]); system(ipmi_add_sel); #endif return 0; } 07070100000048000081A4000000000000000000000001611B97900000041B000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/ras-aer-handler.h/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_AER_HANDLER_H #define __RAS_AER_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" int ras_aer_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); #endif 07070100000049000081A4000000000000000000000001611B9790000010E4000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/ras-arm-handler.c/* * Copyright (c) 2016, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "libtrace/kbuffer.h" #include "ras-arm-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" #include "ras-non-standard-handler.h" #include "non-standard-ampere.h" void display_raw_data(struct trace_seq *s, const uint8_t *buf, uint32_t datalen) { int i = 0, line_count = 0; trace_seq_printf(s, " %08x: ", i); while (datalen >= 4) { print_le_hex(s, buf, i); i += 4; datalen -= 4; if (++line_count == 4) { trace_seq_printf(s, "\n %08x: ", i); line_count = 0; } else trace_seq_printf(s, " "); } } int ras_arm_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; struct ras_events *ras = context; time_t now; struct tm *tm; struct ras_arm_event ev; int len = 0; memset(&ev, 0, sizeof(ev)); /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s\n", ev.timestamp); if (pevent_get_field_val(s, event, "affinity", record, &val, 1) < 0) return -1; ev.affinity = val; trace_seq_printf(s, " affinity: %d", ev.affinity); if (pevent_get_field_val(s, event, "mpidr", record, &val, 1) < 0) return -1; ev.mpidr = val; trace_seq_printf(s, "\n MPIDR: 0x%llx", (unsigned long long)ev.mpidr); if (pevent_get_field_val(s, event, "midr", record, &val, 1) < 0) return -1; ev.midr = val; trace_seq_printf(s, "\n MIDR: 0x%llx", (unsigned long long)ev.midr); if (pevent_get_field_val(s, event, "running_state", record, &val, 1) < 0) return -1; ev.running_state = val; trace_seq_printf(s, "\n running_state: %d", ev.running_state); if (pevent_get_field_val(s, event, "psci_state", record, &val, 1) < 0) return -1; ev.psci_state = val; trace_seq_printf(s, "\n psci_state: %d", ev.psci_state); if (pevent_get_field_val(s, event, "pei_len", record, &val, 1) < 0) return -1; ev.pei_len = val; trace_seq_printf(s, "\n ARM Processor Err Info data len: %d\n", ev.pei_len); ev.pei_error = pevent_get_field_raw(s, event, "buf", record, &len, 1); if (!ev.pei_error) return -1; display_raw_data(s, ev.pei_error, ev.pei_len); if (pevent_get_field_val(s, event, "ctx_len", record, &val, 1) < 0) return -1; ev.ctx_len = val; trace_seq_printf(s, "\n ARM Processor Err Context Info data len: %d\n", ev.ctx_len); ev.ctx_error = pevent_get_field_raw(s, event, "buf1", record, &len, 1); if (!ev.ctx_error) return -1; display_raw_data(s, ev.ctx_error, ev.ctx_len); if (pevent_get_field_val(s, event, "oem_len", record, &val, 1) < 0) return -1; ev.oem_len = val; trace_seq_printf(s, "\n Vendor Specific Err Info data len: %d\n", ev.oem_len); ev.vsei_error = pevent_get_field_raw(s, event, "buf2", record, &len, 1); if (!ev.vsei_error) return -1; #ifdef HAVE_AMP_NS_DECODE //decode ampere specific error decode_amp_payload0_err_regs(NULL, s, (struct amp_payload0_type_sec *)ev.vsei_error); #else display_raw_data(s, ev.vsei_error, ev.oem_len); #endif /* Insert data into the SGBD */ #ifdef HAVE_SQLITE3 ras_store_arm_record(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_arm_event(ras, &ev); #endif return 0; } 0707010000004A000081A4000000000000000000000001611B979000000364000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/ras-arm-handler.h/* * Copyright (c) 2016, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ #ifndef __RAS_ARM_HANDLER_H #define __RAS_ARM_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" int ras_arm_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); void display_raw_data(struct trace_seq *s, const uint8_t *buf, uint32_t datalen); #endif 0707010000004B000081A4000000000000000000000001611B979000000FA3000000000000000000000000000000000000003500000000rasdaemon-0.6.7.18.git+7ccf12f/ras-devlink-handler.c/* * Copyright (C) 2019 Cong Wang <xiyou.wangcong@gmail.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "libtrace/kbuffer.h" #include "ras-devlink-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" int ras_net_xmit_timeout_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; int len; struct ras_events *ras = context; time_t now; struct tm *tm; struct devlink_event ev; if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); ev.bus_name = ""; ev.reporter_name = ""; ev.dev_name = pevent_get_field_raw(s, event, "name", record, &len, 1); if (!ev.dev_name) return -1; ev.driver_name = pevent_get_field_raw(s, event, "driver", record, &len, 1); if (!ev.driver_name) return -1; if (pevent_get_field_val(s, event, "queue_index", record, &val, 1) < 0) return -1; if (asprintf(&ev.msg, "TX timeout on queue: %d\n", (int)val) < 0) return -1; /* Insert data into the SGBD */ #ifdef HAVE_SQLITE3 ras_store_devlink_event(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_devlink_event(ras, &ev); #endif free(ev.msg); return 0; } int ras_devlink_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { int len; struct ras_events *ras = context; time_t now; struct tm *tm; struct devlink_event ev; if (ras->filters[DEVLINK_EVENT] && pevent_filter_match(ras->filters[DEVLINK_EVENT], record) == FILTER_MATCH) return 0; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); ev.bus_name = pevent_get_field_raw(s, event, "bus_name", record, &len, 1); if (!ev.bus_name) return -1; ev.dev_name = pevent_get_field_raw(s, event, "dev_name", record, &len, 1); if (!ev.dev_name) return -1; ev.driver_name = pevent_get_field_raw(s, event, "driver_name", record, &len, 1); if (!ev.driver_name) return -1; ev.reporter_name = pevent_get_field_raw(s, event, "reporter_name", record, &len, 1); if (!ev.reporter_name) return -1; ev.msg = pevent_get_field_raw(s, event, "msg", record, &len, 1); if (!ev.msg) return -1; /* Insert data into the SGBD */ #ifdef HAVE_SQLITE3 ras_store_devlink_event(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_devlink_event(ras, &ev); #endif return 0; } 0707010000004C000081A4000000000000000000000001611B9790000004AF000000000000000000000000000000000000003500000000rasdaemon-0.6.7.18.git+7ccf12f/ras-devlink-handler.h/* * Copyright (C) 2019 Cong Wang <xiyou.wangcong@gmail.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_DEVLINK_HANDLER_H #define __RAS_DEVLINK_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" int ras_net_xmit_timeout_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); int ras_devlink_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); #endif 0707010000004D000081A4000000000000000000000001611B979000000ED4000000000000000000000000000000000000003700000000rasdaemon-0.6.7.18.git+7ccf12f/ras-diskerror-handler.c/* * Copyright (C) 2019 Cong Wang <xiyou.wangcong@gmail.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #ifndef __dev_t_defined #include <sys/types.h> #endif /* __dev_t_defined */ #include <string.h> #include <errno.h> #include <sys/sysmacros.h> #include "libtrace/kbuffer.h" #include "ras-diskerror-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" static const struct { int error; const char *name; } blk_errors[] = { { -EOPNOTSUPP, "operation not supported error" }, { -ETIMEDOUT, "timeout error" }, { -ENOSPC, "critical space allocation error" }, { -ENOLINK, "recoverable transport error" }, { -EREMOTEIO, "critical target error" }, { -EBADE, "critical nexus error" }, { -ENODATA, "critical medium error" }, { -EILSEQ, "protection error" }, { -ENOMEM, "kernel resource error" }, { -EBUSY, "device resource error" }, { -EAGAIN, "nonblocking retry error" }, { -EREMCHG, "dm internal retry error" }, { -EIO, "I/O error" }, }; static const char *get_blk_error(int err) { int i; for (i = 0; i < ARRAY_SIZE(blk_errors); i++) if (blk_errors[i].error == err) return blk_errors[i].name; return "unknown block error"; } int ras_diskerror_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; int len; struct ras_events *ras = context; time_t now; struct tm *tm; struct diskerror_event ev; dev_t dev; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); if (pevent_get_field_val(s, event, "dev", record, &val, 1) < 0) return -1; dev = (dev_t)val; if (asprintf(&ev.dev, "%u:%u", major(dev), minor(dev)) < 0) return -1; if (pevent_get_field_val(s, event, "sector", record, &val, 1) < 0) return -1; ev.sector = val; if (pevent_get_field_val(s, event, "nr_sector", record, &val, 1) < 0) return -1; ev.nr_sector = (unsigned int)val; if (pevent_get_field_val(s, event, "error", record, &val, 1) < 0) return -1; ev.error = get_blk_error((int)val); ev.rwbs = pevent_get_field_raw(s, event, "rwbs", record, &len, 1); if (!ev.rwbs) return -1; ev.cmd = pevent_get_field_raw(s, event, "cmd", record, &len, 1); if (!ev.cmd) return -1; /* Insert data into the SGBD */ #ifdef HAVE_SQLITE3 ras_store_diskerror_event(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_diskerror_event(ras, &ev); #endif free(ev.dev); return 0; } 0707010000004E000081A4000000000000000000000001611B979000000420000000000000000000000000000000000000003700000000rasdaemon-0.6.7.18.git+7ccf12f/ras-diskerror-handler.h/* * Copyright (C) 2019 Cong Wang <xiyou.wangcong@gmail.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_DISKERROR_HANDLER_H #define __RAS_DISKERROR_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" int ras_diskerror_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); #endif 0707010000004F000081A4000000000000000000000001611B97900000589A000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-events.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/poll.h> #include <signal.h> #include <sys/signalfd.h> #include "libtrace/kbuffer.h" #include "libtrace/event-parse.h" #include "ras-mc-handler.h" #include "ras-aer-handler.h" #include "ras-non-standard-handler.h" #include "ras-arm-handler.h" #include "ras-mce-handler.h" #include "ras-extlog-handler.h" #include "ras-devlink-handler.h" #include "ras-diskerror-handler.h" #include "ras-memory-failure-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-page-isolation.h" /* * Polling time, if read() doesn't block. Currently, trace_pipe_raw never * blocks on read(). So, we need to sleep for a while, to avoid spending * too much CPU cycles. A fix for it is expected for 3.10. */ #define POLLING_TIME 3 /* Test for a little-endian machine */ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define ENDIAN KBUFFER_ENDIAN_LITTLE #else #define ENDIAN KBUFFER_ENDIAN_BIG #endif static int get_debugfs_dir(char *tracing_dir, size_t len) { FILE *fp; char line[MAX_PATH + 1 + 256]; char *p, *type, *dir; fp = fopen("/proc/mounts","r"); if (!fp) { log(ALL, LOG_INFO, "Can't open /proc/mounts"); return errno; } do { if (!fgets(line, sizeof(line), fp)) break; p = strtok(line, " \t"); if (!p) break; dir = strtok(NULL, " \t"); if (!dir) break; type = strtok(NULL, " \t"); if (!type) break; if (!strcmp(type, "debugfs")) { fclose(fp); strncpy(tracing_dir, dir, len - 1); tracing_dir[len - 1] = '\0'; return 0; } } while(1); fclose(fp); log(ALL, LOG_INFO, "Can't find debugfs\n"); return ENOENT; } static int open_trace(struct ras_events *ras, char *name, int flags) { char fname[MAX_PATH + 1]; strcpy(fname, ras->tracing); strcat(fname, "/"); strcat(fname, name); return open(fname, flags); } static int get_tracing_dir(struct ras_events *ras) { char fname[MAX_PATH + 1]; int rc, has_instances = 0; DIR *dir; struct dirent *entry; get_debugfs_dir(ras->debugfs, sizeof(ras->debugfs)); strcpy(fname, ras->debugfs); strcat(fname, "/tracing"); dir = opendir(fname); if (!dir) return -1; for (entry = readdir(dir); entry; entry = readdir(dir)) { if (strstr(entry->d_name, "instances")) { has_instances = 1; break; } } closedir(dir); strcpy(ras->tracing, ras->debugfs); strcat(ras->tracing, "/tracing"); if (has_instances) { strcat(ras->tracing, "/instances/" TOOL_NAME); rc = mkdir(ras->tracing, S_IRWXU); if (rc < 0 && errno != EEXIST) { log(ALL, LOG_INFO, "Unable to create " TOOL_NAME " instance at %s\n", ras->tracing); return -1; } } return 0; } /* * Tracing enable/disable code */ static int __toggle_ras_mc_event(struct ras_events *ras, char *group, char *event, int enable) { int fd, rc; char fname[MAX_PATH + 1]; snprintf(fname, sizeof(fname), "%s%s:%s\n", enable ? "" : "!", group, event); /* Enable RAS events */ fd = open_trace(ras, "set_event", O_RDWR | O_APPEND); if (fd < 0) { log(ALL, LOG_WARNING, "Can't open set_event\n"); return errno; } rc = write(fd, fname,strlen(fname)); if (rc < 0) { log(ALL, LOG_WARNING, "Can't write to set_event\n"); close(fd); return rc; } close(fd); if (!rc) { log(ALL, LOG_WARNING, "Nothing was written on set_event\n"); return EIO; } log(ALL, LOG_INFO, "%s:%s event %s\n", group, event, enable ? "enabled" : "disabled"); return 0; } int toggle_ras_mc_event(int enable) { struct ras_events *ras; int rc = 0; ras = calloc(1, sizeof(*ras)); if (!ras) { log(TERM, LOG_ERR, "Can't allocate memory for ras struct\n"); return errno; } rc = get_tracing_dir(ras); if (rc < 0) { log(TERM, LOG_ERR, "Can't locate a mounted debugfs\n"); goto free_ras; } rc = __toggle_ras_mc_event(ras, "ras", "mc_event", enable); #ifdef HAVE_AER rc |= __toggle_ras_mc_event(ras, "ras", "aer_event", enable); #endif #ifdef HAVE_MCE rc |= __toggle_ras_mc_event(ras, "mce", "mce_record", enable); #endif #ifdef HAVE_EXTLOG rc |= __toggle_ras_mc_event(ras, "ras", "extlog_mem_event", enable); #endif #ifdef HAVE_NON_STANDARD rc |= __toggle_ras_mc_event(ras, "ras", "non_standard_event", enable); #endif #ifdef HAVE_ARM rc |= __toggle_ras_mc_event(ras, "ras", "arm_event", enable); #endif #ifdef HAVE_DEVLINK rc |= __toggle_ras_mc_event(ras, "devlink", "devlink_health_report", enable); #endif #ifdef HAVE_DISKERROR rc |= __toggle_ras_mc_event(ras, "block", "block_rq_complete", enable); #endif #ifdef HAVE_MEMORY_FAILURE rc |= __toggle_ras_mc_event(ras, "ras", "memory_failure_event", enable); #endif free_ras: free(ras); return rc; } /* * Set kernel filter. libtrace doesn't provide an API for setting filters * in kernel, we have to implement it here. */ static int filter_ras_mc_event(struct ras_events *ras, char *group, char *event, const char *filter_str) { int fd, rc; char fname[MAX_PATH + 1]; snprintf(fname, sizeof(fname), "events/%s/%s/filter", group, event); fd = open_trace(ras, fname, O_RDWR | O_APPEND); if (fd < 0) { log(ALL, LOG_WARNING, "Can't open filter file\n"); return errno; } rc = write(fd, filter_str ,strlen(filter_str)); if (rc < 0) { log(ALL, LOG_WARNING, "Can't write to filter file\n"); close(fd); return rc; } close(fd); if (!rc) { log(ALL, LOG_WARNING, "Nothing was written on filter file\n"); return EIO; } return 0; } /* * Tracing read code */ static int get_pagesize(struct ras_events *ras, struct pevent *pevent) { int fd, len, page_size = 4096; char buf[page_size]; fd = open_trace(ras, "events/header_page", O_RDONLY); if (fd < 0) return page_size; len = read(fd, buf, page_size); if (len <= 0) goto error; if (pevent_parse_header_page(pevent, buf, len, sizeof(long))) goto error; page_size = pevent->header_page_data_offset + pevent->header_page_data_size; error: close(fd); return page_size; } static void parse_ras_data(struct pthread_data *pdata, struct kbuffer *kbuf, void *data, unsigned long long time_stamp) { struct pevent_record record; struct trace_seq s; record.ts = time_stamp; record.size = kbuffer_event_size(kbuf); record.data = data; record.offset = kbuffer_curr_offset(kbuf); record.cpu = pdata->cpu; /* note offset is just offset in subbuffer */ record.missed_events = kbuffer_missed_events(kbuf); record.record_size = kbuffer_curr_size(kbuf); /* TODO - logging */ trace_seq_init(&s); pevent_print_event(pdata->ras->pevent, &s, &record); trace_seq_do_printf(&s); printf("\n"); fflush(stdout); trace_seq_destroy(&s); } static int get_num_cpus(struct ras_events *ras) { return sysconf(_SC_NPROCESSORS_CONF); #if 0 char fname[MAX_PATH + 1]; int num_cpus = 0; DIR *dir; struct dirent *entry; strcpy(fname, ras->debugfs); strcat(fname, "/tracing/per_cpu/"); dir = opendir(fname); if (!dir) return -1; for (entry = readdir(dir); entry; entry = readdir(dir)) { if (strstr(entry->d_name, "cpu")) num_cpus++; } closedir(dir); return num_cpus; #endif } static int read_ras_event_all_cpus(struct pthread_data *pdata, unsigned n_cpus) { unsigned size; unsigned long long time_stamp; void *data; int ready, i, count_nready; struct kbuffer *kbuf; void *page; struct pollfd fds[n_cpus + 1]; struct signalfd_siginfo fdsiginfo; sigset_t mask; int warnonce[n_cpus]; char pipe_raw[PATH_MAX]; int legacy_kernel = 0; #if 0 int need_sleep = 0; #endif memset(&warnonce, 0, sizeof(warnonce)); page = malloc(pdata[0].ras->page_size); if (!page) { log(TERM, LOG_ERR, "Can't allocate page\n"); return -ENOMEM; } kbuf = kbuffer_alloc(KBUFFER_LSIZE_8, ENDIAN); if (!kbuf) { log(TERM, LOG_ERR, "Can't allocate kbuf\n"); free(page); return -ENOMEM; } for (i = 0; i < (n_cpus + 1); i++) fds[i].fd = -1; for (i = 0; i < n_cpus; i++) { fds[i].events = POLLIN; /* FIXME: use select to open for all CPUs */ snprintf(pipe_raw, sizeof(pipe_raw), "per_cpu/cpu%d/trace_pipe_raw", i); fds[i].fd = open_trace(pdata[0].ras, pipe_raw, O_RDONLY); if (fds[i].fd < 0) { log(TERM, LOG_ERR, "Can't open trace_pipe_raw\n"); goto error; } } sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGQUIT); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) log(TERM, LOG_WARNING, "sigprocmask\n"); fds[n_cpus].events = POLLIN; fds[n_cpus].fd = signalfd(-1, &mask, 0); if (fds[n_cpus].fd < 0) { log(TERM, LOG_WARNING, "signalfd\n"); goto error; } log(TERM, LOG_INFO, "Listening to events for cpus 0 to %d\n", n_cpus - 1); if (pdata[0].ras->record_events) { if (ras_mc_event_opendb(pdata[0].cpu, pdata[0].ras)) goto error; } do { ready = poll(fds, (n_cpus + 1), -1); if (ready < 0) { log(TERM, LOG_WARNING, "poll\n"); } /* check for the signal */ if (fds[n_cpus].revents & POLLIN) { size = read(fds[n_cpus].fd, &fdsiginfo, sizeof(struct signalfd_siginfo)); if (size != sizeof(struct signalfd_siginfo)) log(TERM, LOG_WARNING, "signalfd read\n"); if (fdsiginfo.ssi_signo == SIGINT || fdsiginfo.ssi_signo == SIGTERM || fdsiginfo.ssi_signo == SIGHUP || fdsiginfo.ssi_signo == SIGQUIT) { log(TERM, LOG_INFO, "Recevied signal=%d\n", fdsiginfo.ssi_signo); goto cleanup; } else { log(TERM, LOG_INFO, "Received unexpected signal=%d\n", fdsiginfo.ssi_signo); } } count_nready = 0; for (i = 0; i < n_cpus; i++) { if (fds[i].revents & POLLERR) { if (!warnonce[i]) { log(TERM, LOG_INFO, "Error on CPU %i\n", i); warnonce[i]++; #if 0 need_sleep = 1; #endif } } if (!(fds[i].revents & POLLIN)) { count_nready++; continue; } size = read(fds[i].fd, page, pdata[i].ras->page_size); if (size < 0) { log(TERM, LOG_WARNING, "read\n"); goto cleanup; } else if (size > 0) { kbuffer_load_subbuffer(kbuf, page); while ((data = kbuffer_read_event(kbuf, &time_stamp))) { parse_ras_data(&pdata[i], kbuf, data, time_stamp); /* increment to read next event */ kbuffer_next_event(kbuf, NULL); } } else { count_nready++; } } #if 0 if (need_sleep) sleep(POLLING_TIME); #else /* * If we enable fallback mode, it will always be used, as * poll is still not working fine, IMHO */ if (count_nready == n_cpus) { /* Should only happen with legacy kernels */ legacy_kernel = 1; break; } #endif } while (1); /* poll() is not supported. We need to fallback to the old way */ log(TERM, LOG_INFO, "Old kernel detected. Stop listening and fall back to pthread way.\n"); cleanup: if (pdata[0].ras->record_events) ras_mc_event_closedb(pdata[0].cpu, pdata[0].ras); error: kbuffer_free(kbuf); free(page); sigprocmask(SIG_UNBLOCK, &mask, NULL); for (i = 0; i < (n_cpus + 1); i++) { if (fds[i].fd > 0) close(fds[i].fd); } if (legacy_kernel) return -255; else return -1; } static int read_ras_event(int fd, struct pthread_data *pdata, struct kbuffer *kbuf, void *page) { int size; unsigned long long time_stamp; void *data; /* * read() never blocks. We can't call poll() here, as it is * not supported on kernels below 3.10. So, the better is to just * sleep for a while, to avoid eating too much CPU here. */ do { size = read(fd, page, pdata->ras->page_size); if (size < 0) { log(TERM, LOG_WARNING, "read\n"); return -1; } else if (size > 0) { kbuffer_load_subbuffer(kbuf, page); while ((data = kbuffer_read_event(kbuf, &time_stamp))) { parse_ras_data(pdata, kbuf, data, time_stamp); /* increment to read next event */ kbuffer_next_event(kbuf, NULL); } } else { sleep(POLLING_TIME); } } while (1); } static void *handle_ras_events_cpu(void *priv) { int fd; struct kbuffer *kbuf; void *page; char pipe_raw[PATH_MAX]; struct pthread_data *pdata = priv; page = malloc(pdata->ras->page_size); if (!page) { log(TERM, LOG_ERR, "Can't allocate page\n"); return NULL; } kbuf = kbuffer_alloc(KBUFFER_LSIZE_8, ENDIAN); if (!kbuf) { log(TERM, LOG_ERR, "Can't allocate kbuf"); free(page); return NULL; } /* FIXME: use select to open for all CPUs */ snprintf(pipe_raw, sizeof(pipe_raw), "per_cpu/cpu%d/trace_pipe_raw", pdata->cpu); fd = open_trace(pdata->ras, pipe_raw, O_RDONLY); if (fd < 0) { log(TERM, LOG_ERR, "Can't open trace_pipe_raw\n"); kbuffer_free(kbuf); free(page); return NULL; } log(TERM, LOG_INFO, "Listening to events on cpu %d\n", pdata->cpu); if (pdata->ras->record_events) { if (ras_mc_event_opendb(pdata->cpu, pdata->ras)) { log(TERM, LOG_ERR, "Can't open database\n"); close(fd); kbuffer_free(kbuf); free(page); return 0; } } read_ras_event(fd, pdata, kbuf, page); if (pdata->ras->record_events) ras_mc_event_closedb(pdata->cpu, pdata->ras); close(fd); kbuffer_free(kbuf); free(page); return NULL; } #define UPTIME "uptime" static int select_tracing_timestamp(struct ras_events *ras) { FILE *fp; int fd, rc; time_t uptime, now; size_t size; unsigned j1; char buf[4096]; /* Check if uptime is supported (kernel 3.10-rc1 or upper) */ fd = open_trace(ras, "trace_clock", O_RDONLY); if (fd < 0) { log(TERM, LOG_ERR, "Can't open trace_clock\n"); return -1; } size = read(fd, buf, sizeof(buf)); close(fd); if (!size) { log(TERM, LOG_ERR, "trace_clock is empty!\n"); return -1; } if (!strstr(buf, UPTIME)) { log(TERM, LOG_INFO, "Kernel doesn't support uptime clock\n"); return 0; } /* Select uptime tracing */ fd = open_trace(ras, "trace_clock", O_WRONLY); if (!fd) { log(TERM, LOG_ERR, "Kernel didn't allow writing to trace_clock\n"); return 0; } rc = write(fd, UPTIME, sizeof(UPTIME)); close(fd); if (rc < 0) { log(TERM, LOG_ERR, "Kernel didn't allow selecting uptime on trace_clock\n"); return 0; } /* Reference uptime with localtime */ fp = fopen("/proc/uptime", "r"); if (!fp) { log(TERM, LOG_ERR, "Couldn't read from /proc/uptime\n"); return 0; } rc = fscanf(fp, "%zu.%u ", &uptime, &j1); fclose(fp); if (rc <= 0) { log(TERM, LOG_ERR, "Can't parse /proc/uptime!\n"); return -1; } now = time(NULL); ras->use_uptime = 1; ras->uptime_diff = now - uptime; return 0; } static int add_event_handler(struct ras_events *ras, struct pevent *pevent, unsigned page_size, char *group, char *event, pevent_event_handler_func func, char *filter_str, int id) { int fd, size, rc; char *page, fname[MAX_PATH + 1]; struct event_filter * filter = NULL; snprintf(fname, sizeof(fname), "events/%s/%s/format", group, event); fd = open_trace(ras, fname, O_RDONLY); if (fd < 0) { log(TERM, LOG_ERR, "Can't get %s:%s traces. Perhaps this feature is not supported on your system.\n", group, event); return errno; } page = malloc(page_size); if (!page) { log(TERM, LOG_ERR, "Can't allocate page to read %s:%s format\n", group, event); rc = errno; close(fd); return rc; } size = read(fd, page, page_size); close(fd); if (size < 0) { log(TERM, LOG_ERR, "Can't get arch page size\n"); free(page); return size; } /* Registers the special event handlers */ rc = pevent_register_event_handler(pevent, -1, group, event, func, ras); if (rc == PEVENT_ERRNO__MEM_ALLOC_FAILED) { log(TERM, LOG_ERR, "Can't register event handler for %s:%s\n", group, event); free(page); return EINVAL; } rc = pevent_parse_event(pevent, page, size, group); if (rc) { log(TERM, LOG_ERR, "Can't parse event %s:%s\n", group, event); free(page); return EINVAL; } if (filter_str) { char *error; filter = pevent_filter_alloc(pevent); if (!filter) { log(TERM, LOG_ERR, "Failed to allocate filter for %s/%s.\n", group, event); free(page); return EINVAL; } rc = pevent_filter_add_filter_str(filter, filter_str, &error); if (rc) { log(TERM, LOG_ERR, "Failed to install filter for %s/%s: %s\n", group, event, error); pevent_filter_free(filter); free(page); return rc; } } ras->filters[id] = filter; /* Enable RAS events */ rc = __toggle_ras_mc_event(ras, group, event, 1); free(page); if (rc < 0) { log(TERM, LOG_ERR, "Can't enable %s:%s tracing\n", group, event); return EINVAL; } log(ALL, LOG_INFO, "Enabled event %s:%s\n", group, event); return 0; } int handle_ras_events(int record_events) { int rc, page_size, i; int num_events = 0; unsigned cpus; struct pevent *pevent = NULL; struct pthread_data *data = NULL; struct ras_events *ras = NULL; #ifdef HAVE_DEVLINK char *filter_str = NULL; #endif ras = calloc(1, sizeof(*ras)); if (!ras) { log(TERM, LOG_ERR, "Can't allocate memory for ras struct\n"); return errno; } rc = get_tracing_dir(ras); if (rc < 0) { log(TERM, LOG_ERR, "Can't locate a mounted debugfs\n"); goto err; } rc = select_tracing_timestamp(ras); if (rc < 0) { log(TERM, LOG_ERR, "Can't select a timestamp for tracing\n"); goto err; } pevent = pevent_alloc(); if (!pevent) { log(TERM, LOG_ERR, "Can't allocate pevent\n"); rc = errno; goto err; } page_size = get_pagesize(ras, pevent); ras->pevent = pevent; ras->page_size = page_size; ras->record_events = record_events; #ifdef HAVE_MEMORY_CE_PFA /* FIXME: enable memory isolation unconditionally */ ras_page_account_init(); #endif rc = add_event_handler(ras, pevent, page_size, "ras", "mc_event", ras_mc_event_handler, NULL, MC_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "ras", "mc_event"); #ifdef HAVE_AER rc = add_event_handler(ras, pevent, page_size, "ras", "aer_event", ras_aer_event_handler, NULL, AER_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "ras", "aer_event"); #endif #ifdef HAVE_NON_STANDARD rc = add_event_handler(ras, pevent, page_size, "ras", "non_standard_event", ras_non_standard_event_handler, NULL, NON_STANDARD_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "ras", "non_standard_event"); #endif #ifdef HAVE_ARM rc = add_event_handler(ras, pevent, page_size, "ras", "arm_event", ras_arm_event_handler, NULL, ARM_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "ras", "arm_event"); #endif cpus = get_num_cpus(ras); #ifdef HAVE_MCE rc = register_mce_handler(ras, cpus); if (rc) log(ALL, LOG_INFO, "Can't register mce handler\n"); if (ras->mce_priv) { rc = add_event_handler(ras, pevent, page_size, "mce", "mce_record", ras_mce_event_handler, NULL, MCE_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "mce", "mce_record"); } #endif #ifdef HAVE_EXTLOG rc = add_event_handler(ras, pevent, page_size, "ras", "extlog_mem_event", ras_extlog_mem_event_handler, NULL, EXTLOG_EVENT); if (!rc) { /* tell kernel we are listening, so don't printk to console */ (void)open("/sys/kernel/debug/ras/daemon_active", 0); num_events++; } else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "ras", "extlog_mem_event"); #endif #ifdef HAVE_DEVLINK rc = add_event_handler(ras, pevent, page_size, "net", "net_dev_xmit_timeout", ras_net_xmit_timeout_handler, NULL, DEVLINK_EVENT); if (!rc) filter_str = "devlink/devlink_health_report:msg=~\'TX timeout*\'"; rc = add_event_handler(ras, pevent, page_size, "devlink", "devlink_health_report", ras_devlink_event_handler, filter_str, DEVLINK_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "devlink", "devlink_health_report"); #endif #ifdef HAVE_DISKERROR rc = filter_ras_mc_event(ras, "block", "block_rq_complete", "error != 0"); if (!rc) { rc = add_event_handler(ras, pevent, page_size, "block", "block_rq_complete", ras_diskerror_event_handler, NULL, DISKERROR_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "block", "block_rq_complete"); } #endif #ifdef HAVE_MEMORY_FAILURE rc = add_event_handler(ras, pevent, page_size, "ras", "memory_failure_event", ras_memory_failure_event_handler, NULL, MF_EVENT); if (!rc) num_events++; else log(ALL, LOG_ERR, "Can't get traces from %s:%s\n", "ras", "memory_failure_event"); #endif if (!num_events) { log(ALL, LOG_INFO, "Failed to trace all supported RAS events. Aborting.\n"); rc = -EINVAL; goto err; } data = calloc(sizeof(*data), cpus); if (!data) goto err; for (i = 0; i < cpus; i++) { data[i].ras = ras; data[i].cpu = i; } rc = read_ras_event_all_cpus(data, cpus); /* Poll doesn't work on this kernel. Fallback to pthread way */ if (rc == -255) { log(SYSLOG, LOG_INFO, "Opening one thread per cpu (%d threads)\n", cpus); for (i = 0; i < cpus; i++) { rc = pthread_create(&data[i].thread, NULL, handle_ras_events_cpu, (void *)&data[i]); if (rc) { log(SYSLOG, LOG_INFO, "Failed to create thread for cpu %d. Aborting.\n", i); while (--i) pthread_cancel(data[i].thread); goto err; } } /* Wait for all threads to complete */ for (i = 0; i < cpus; i++) pthread_join(data[i].thread, NULL); } log(SYSLOG, LOG_INFO, "Huh! something got wrong. Aborting.\n"); err: if (data) free(data); if (pevent) pevent_free(pevent); if (ras) { for (i = 0; i < NR_EVENTS; i++) { if (ras->filters[i]) pevent_filter_free(ras->filters[i]); } free(ras); } return rc; } 07070100000050000081A4000000000000000000000001611B97900000090C000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-events.h/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_EVENTS_H #define __RAS_EVENTS_H #include "ras-record.h" #include <pthread.h> #include <time.h> #define MAX_PATH 1024 #define STR(x) #x struct mce_priv; enum { MC_EVENT, MCE_EVENT, AER_EVENT, NON_STANDARD_EVENT, ARM_EVENT, EXTLOG_EVENT, DEVLINK_EVENT, DISKERROR_EVENT, MF_EVENT, NR_EVENTS }; struct ras_events { char debugfs[MAX_PATH + 1]; char tracing[MAX_PATH + 1]; struct pevent *pevent; int page_size; /* Booleans */ unsigned use_uptime: 1; unsigned record_events: 1; /* For timestamp */ time_t uptime_diff; /* For ras-record */ void *db_priv; /* For the mce handler */ struct mce_priv *mce_priv; /* For ABRT socket*/ int socketfd; struct event_filter *filters[NR_EVENTS]; }; struct pthread_data { pthread_t thread; struct pevent *pevent; struct ras_events *ras; int cpu; }; /* Should match the code at Kernel's include/linux/edac.c */ enum hw_event_mc_err_type { HW_EVENT_ERR_CORRECTED, HW_EVENT_ERR_UNCORRECTED, HW_EVENT_ERR_FATAL, HW_EVENT_ERR_INFO, }; /* Should match the code at Kernel's /drivers/pci/pcie/aer/aerdrv_errprint.c */ enum hw_event_aer_err_type { HW_EVENT_AER_UNCORRECTED_NON_FATAL, HW_EVENT_AER_UNCORRECTED_FATAL, HW_EVENT_AER_CORRECTED, }; /* Should match the code at Kernel's include/acpi/ghes.h */ enum ghes_severity { GHES_SEV_NO, GHES_SEV_CORRECTED, GHES_SEV_RECOVERABLE, GHES_SEV_PANIC, }; /* Function prototypes */ int toggle_ras_mc_event(int enable); int handle_ras_events(int record_events); #endif 07070100000051000081A4000000000000000000000001611B979000001CE2000000000000000000000000000000000000003400000000rasdaemon-0.6.7.18.git+7ccf12f/ras-extlog-handler.c/* * Copyright (C) 2014 Tony Luck <tony.luck@intel.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> #include "libtrace/kbuffer.h" #include "ras-extlog-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" static char *err_type(int etype) { switch (etype) { case 0: return "unknown"; case 1: return "no error"; case 2: return "single-bit ECC"; case 3: return "multi-bit ECC"; case 4: return "single-symbol chipkill ECC"; case 5: return "multi-symbol chipkill ECC"; case 6: return "master abort"; case 7: return "target abort"; case 8: return "parity error"; case 9: return "watchdog timeout"; case 10: return "invalid address"; case 11: return "mirror Broken"; case 12: return "memory sparing"; case 13: return "scrub corrected error"; case 14: return "scrub uncorrected error"; case 15: return "physical memory map-out event"; } return "unknown-type"; } static char *err_severity(int severity) { switch (severity) { case 0: return "recoverable"; case 1: return "fatal"; case 2: return "corrected"; case 3: return "informational"; } return "unknown-severity"; } static unsigned long long err_mask(int lsb) { if (lsb == 0xff) return ~0ull; return ~((1ull << lsb) - 1); } #define CPER_MEM_VALID_NODE 0x0008 #define CPER_MEM_VALID_CARD 0x0010 #define CPER_MEM_VALID_MODULE 0x0020 #define CPER_MEM_VALID_BANK 0x0040 #define CPER_MEM_VALID_DEVICE 0x0080 #define CPER_MEM_VALID_ROW 0x0100 #define CPER_MEM_VALID_COLUMN 0x0200 #define CPER_MEM_VALID_BIT_POSITION 0x0400 #define CPER_MEM_VALID_REQUESTOR_ID 0x0800 #define CPER_MEM_VALID_RESPONDER_ID 0x1000 #define CPER_MEM_VALID_TARGET_ID 0x2000 #define CPER_MEM_VALID_RANK_NUMBER 0x8000 #define CPER_MEM_VALID_CARD_HANDLE 0x10000 #define CPER_MEM_VALID_MODULE_HANDLE 0x20000 struct cper_mem_err_compact { unsigned long long validation_bits; unsigned short node; unsigned short card; unsigned short module; unsigned short bank; unsigned short device; unsigned short row; unsigned short column; unsigned short bit_pos; unsigned long long requestor_id; unsigned long long responder_id; unsigned long long target_id; unsigned short rank; unsigned short mem_array_handle; unsigned short mem_dev_handle; }; static char *err_cper_data(const char *c) { const struct cper_mem_err_compact *cpd = (struct cper_mem_err_compact *)c; static char buf[256]; char *p = buf; if (cpd->validation_bits == 0) return ""; p += sprintf(p, " ("); if (cpd->validation_bits & CPER_MEM_VALID_NODE) p += sprintf(p, "node: %d ", cpd->node); if (cpd->validation_bits & CPER_MEM_VALID_CARD) p += sprintf(p, "card: %d ", cpd->card); if (cpd->validation_bits & CPER_MEM_VALID_MODULE) p += sprintf(p, "module: %d ", cpd->module); if (cpd->validation_bits & CPER_MEM_VALID_BANK) p += sprintf(p, "bank: %d ", cpd->bank); if (cpd->validation_bits & CPER_MEM_VALID_DEVICE) p += sprintf(p, "device: %d ", cpd->device); if (cpd->validation_bits & CPER_MEM_VALID_ROW) p += sprintf(p, "row: %d ", cpd->row); if (cpd->validation_bits & CPER_MEM_VALID_COLUMN) p += sprintf(p, "column: %d ", cpd->column); if (cpd->validation_bits & CPER_MEM_VALID_BIT_POSITION) p += sprintf(p, "bit_pos: %d ", cpd->bit_pos); if (cpd->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) p += sprintf(p, "req_id: 0x%llx ", cpd->requestor_id); if (cpd->validation_bits & CPER_MEM_VALID_RESPONDER_ID) p += sprintf(p, "resp_id: 0x%llx ", cpd->responder_id); if (cpd->validation_bits & CPER_MEM_VALID_TARGET_ID) p += sprintf(p, "tgt_id: 0x%llx ", cpd->target_id); if (cpd->validation_bits & CPER_MEM_VALID_RANK_NUMBER) p += sprintf(p, "rank: %d ", cpd->rank); if (cpd->validation_bits & CPER_MEM_VALID_CARD_HANDLE) p += sprintf(p, "card_handle: %d ", cpd->mem_array_handle); if (cpd->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) p += sprintf(p, "module_handle: %d ", cpd->mem_dev_handle); p += sprintf(p-1, ")"); return buf; } static char *uuid_le(const char *uu) { static char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]; char *p = uuid; int i; static const unsigned char le[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15}; for (i = 0; i < 16; i++) { p += sprintf(p, "%.2x", uu[le[i]]); switch (i) { case 3: case 5: case 7: case 9: *p++ = '-'; break; } } *p = 0; return uuid; } static void report_extlog_mem_event(struct ras_events *ras, struct pevent_record *record, struct trace_seq *s, struct ras_extlog_event *ev) { trace_seq_printf(s, "%d %s error: %s physical addr: 0x%llx mask: 0x%llx%s %s %s", ev->error_seq, err_severity(ev->severity), err_type(ev->etype), ev->address, err_mask(ev->pa_mask_lsb), err_cper_data(ev->cper_data), ev->fru_text, uuid_le(ev->fru_id)); } int ras_extlog_mem_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { int len; unsigned long long val; struct ras_events *ras = context; time_t now; struct tm *tm; struct ras_extlog_event ev; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); if (pevent_get_field_val(s, event, "etype", record, &val, 1) < 0) return -1; ev.etype = val; if (pevent_get_field_val(s, event, "err_seq", record, &val, 1) < 0) return -1; ev.error_seq = val; if (pevent_get_field_val(s, event, "sev", record, &val, 1) < 0) return -1; ev.severity = val; if (pevent_get_field_val(s, event, "pa", record, &val, 1) < 0) return -1; ev.address = val; if (pevent_get_field_val(s, event, "pa_mask_lsb", record, &val, 1) < 0) return -1; ev.pa_mask_lsb = val; ev.cper_data = pevent_get_field_raw(s, event, "data", record, &len, 1); ev.cper_data_length = len; ev.fru_text = pevent_get_field_raw(s, event, "fru_text", record, &len, 1); ev.fru_id = pevent_get_field_raw(s, event, "fru_id", record, &len, 1); report_extlog_mem_event(ras, record, s, &ev); ras_store_extlog_mem_record(ras, &ev); return 0; } 07070100000052000081A4000000000000000000000001611B979000000434000000000000000000000000000000000000003400000000rasdaemon-0.6.7.18.git+7ccf12f/ras-extlog-handler.h/* * Copyright (C) 2014 Tony Luck <tony.luck@intel.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_EXTLOG_HANDLER_H #define __RAS_EXTLOG_HANDLER_H #include <stdint.h> #include "ras-events.h" #include "libtrace/event-parse.h" extern int ras_extlog_mem_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); #endif 07070100000053000081A4000000000000000000000001611B979000000503000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-logger.h/* * Copyright (C) 2013 Petr Holasek <pholasek@redhat.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_LOGGER_H #include <syslog.h> /* * Logging macros */ #ifndef TOOL_NAME #define TOOL_NAME "rasdaemon" #endif #define SYSLOG (1 << 0) #define TERM (1 << 1) #define ALL (SYSLOG | TERM) /* TODO: global logging limit mask */ #define log(where, level, fmt, args...) do {\ if (where & SYSLOG)\ syslog(level, fmt, ##args);\ if (where & TERM) {\ fprintf(stderr, "%s: ", TOOL_NAME);\ fprintf(stderr, fmt, ##args);\ fflush(stderr);\ }\ } while (0) #define __RAS_LOGGER_H #endif 07070100000054000081A4000000000000000000000001611B979000001561000000000000000000000000000000000000003000000000rasdaemon-0.6.7.18.git+7ccf12f/ras-mc-handler.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "libtrace/kbuffer.h" #include "ras-mc-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-page-isolation.h" #include "ras-report.h" int ras_mc_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { int len; unsigned long long val; struct ras_events *ras = context; time_t now; struct tm *tm; struct ras_mc_event ev; int parsed_fields = 0; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); if (pevent_get_field_val(s, event, "error_count", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.error_count = val; trace_seq_printf(s, "%d ", ev.error_count); if (pevent_get_field_val(s, event, "error_type", record, &val, 1) < 0) goto parse_error; parsed_fields++; switch (val) { case HW_EVENT_ERR_CORRECTED: ev.error_type = "Corrected"; break; case HW_EVENT_ERR_UNCORRECTED: ev.error_type = "Uncorrected"; break; case HW_EVENT_ERR_FATAL: ev.error_type = "Fatal"; break; default: case HW_EVENT_ERR_INFO: ev.error_type = "Info"; } trace_seq_puts(s, ev.error_type); if (ev.error_count > 1) trace_seq_puts(s, " errors:"); else trace_seq_puts(s, " error:"); ev.msg = pevent_get_field_raw(s, event, "msg", record, &len, 1); if (!ev.msg) goto parse_error; parsed_fields++; if (*ev.msg) { trace_seq_puts(s, " "); trace_seq_puts(s, ev.msg); } ev.label = pevent_get_field_raw(s, event, "label", record, &len, 1); if (!ev.label) goto parse_error; parsed_fields++; if (*ev.label) { trace_seq_puts(s, " on "); trace_seq_puts(s, ev.label); } trace_seq_puts(s, " ("); if (pevent_get_field_val(s, event, "mc_index", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.mc_index = val; trace_seq_printf(s, "mc: %d", ev.mc_index); if (pevent_get_field_val(s, event, "top_layer", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.top_layer = (signed char) val; if (pevent_get_field_val(s, event, "middle_layer", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.middle_layer = (signed char) val; if (pevent_get_field_val(s, event, "lower_layer", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.lower_layer = (signed char) val; if (ev.top_layer >= 0 || ev.middle_layer >= 0 || ev.lower_layer >= 0) { if (ev.lower_layer >= 0) trace_seq_printf(s, " location: %d:%d:%d", ev.top_layer, ev.middle_layer, ev.lower_layer); else if (ev.middle_layer >= 0) trace_seq_printf(s, " location: %d:%d", ev.top_layer, ev.middle_layer); else trace_seq_printf(s, " location: %d", ev.top_layer); } if (pevent_get_field_val(s, event, "address", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.address = val; if (ev.address) trace_seq_printf(s, " address: 0x%08llx", ev.address); if (pevent_get_field_val(s, event, "grain_bits", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.grain = val; trace_seq_printf(s, " grain: %lld", ev.grain); if (pevent_get_field_val(s, event, "syndrome", record, &val, 1) < 0) goto parse_error; parsed_fields++; ev.syndrome = val; if (val) trace_seq_printf(s, " syndrome: 0x%08llx", ev.syndrome); ev.driver_detail = pevent_get_field_raw(s, event, "driver_detail", record, &len, 1); if (!ev.driver_detail) goto parse_error; parsed_fields++; if (*ev.driver_detail) { trace_seq_puts(s, " "); trace_seq_puts(s, ev.driver_detail); } trace_seq_puts(s, ")"); /* Insert data into the SGBD */ ras_store_mc_event(ras, &ev); #ifdef HAVE_MEMORY_CE_PFA /* Account page corrected errors */ if (!strcmp(ev.error_type, "Corrected")) ras_record_page_error(ev.address, ev.error_count, now); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_mc_event(ras, &ev); #endif return 0; parse_error: /* FIXME: add a logic here to also store parse errors to SDBD */ log(ALL, LOG_ERR, "MC error handler: can't parse field #%d\n", parsed_fields); return 0; } 07070100000055000081A4000000000000000000000001611B979000000418000000000000000000000000000000000000003000000000rasdaemon-0.6.7.18.git+7ccf12f/ras-mc-handler.h/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_MC_HANDLER_H #define __RAS_MC_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" int ras_mc_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); #endif 07070100000056000081A4000000000000000000000001611B9790000034A2000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/ras-mce-handler.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> #include "libtrace/kbuffer.h" #include "ras-mce-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" /* * The code below were adapted from Andi Kleen/Intel/SuSe mcelog code, * released under GNU Public General License, v.2 */ static char *cputype_name[] = { [CPU_GENERIC] = "generic CPU", [CPU_P6OLD] = "Intel PPro/P2/P3/old Xeon", [CPU_CORE2] = "Intel Core", /* 65nm and 45nm */ [CPU_K8] = "AMD K8 and derivates", [CPU_P4] = "Intel P4", [CPU_NEHALEM] = "Intel Xeon 5500 series / Core i3/5/7 (\"Nehalem/Westmere\")", [CPU_DUNNINGTON] = "Intel Xeon 7400 series", [CPU_TULSA] = "Intel Xeon 7100 series", [CPU_INTEL] = "Intel generic architectural MCA", [CPU_XEON75XX] = "Intel Xeon 7500 series", [CPU_SANDY_BRIDGE] = "Sandy Bridge", /* Fill in better name */ [CPU_SANDY_BRIDGE_EP] = "Sandy Bridge EP", /* Fill in better name */ [CPU_IVY_BRIDGE] = "Ivy Bridge", /* Fill in better name */ [CPU_IVY_BRIDGE_EPEX] = "Ivy Bridge EP/EX", /* Fill in better name */ [CPU_HASWELL] = "Haswell", [CPU_HASWELL_EPEX] = "Intel Xeon v3 (Haswell) EP/EX", [CPU_BROADWELL] = "Broadwell", [CPU_BROADWELL_DE] = "Broadwell DE", [CPU_BROADWELL_EPEX] = "Broadwell EP/EX", [CPU_KNIGHTS_LANDING] = "Knights Landing", [CPU_KNIGHTS_MILL] = "Knights Mill", [CPU_SKYLAKE_XEON] = "Skylake server", [CPU_AMD_SMCA] = "AMD Scalable MCA", [CPU_DHYANA] = "Hygon Family 18h Moksha", [CPU_ICELAKE_XEON] = "Icelake server", [CPU_ICELAKE_DE] = "Icelake server D Family", [CPU_TREMONT_D] = "Tremont microserver", [CPU_SAPPHIRERAPIDS] = "Sapphirerapids server", }; static enum cputype select_intel_cputype(struct ras_events *ras) { struct mce_priv *mce = ras->mce_priv; if (mce->family == 15) { if (mce->model == 6) return CPU_TULSA; return CPU_P4; } if (mce->family == 6) { if (mce->model >= 0x1a && mce->model != 28) mce->mc_error_support = 1; if (mce->model < 0xf) return CPU_P6OLD; else if (mce->model == 0xf || mce->model == 0x17) /* Merom/Penryn */ return CPU_CORE2; else if (mce->model == 0x1d) return CPU_DUNNINGTON; else if (mce->model == 0x1a || mce->model == 0x2c || mce->model == 0x1e || mce->model == 0x25) return CPU_NEHALEM; else if (mce->model == 0x2e || mce->model == 0x2f) return CPU_XEON75XX; else if (mce->model == 0x2a) return CPU_SANDY_BRIDGE; else if (mce->model == 0x2d) return CPU_SANDY_BRIDGE_EP; else if (mce->model == 0x3a) return CPU_IVY_BRIDGE; else if (mce->model == 0x3e) return CPU_IVY_BRIDGE_EPEX; else if (mce->model == 0x3c || mce->model == 0x45 || mce->model == 0x46) return CPU_HASWELL; else if (mce->model == 0x3f) return CPU_HASWELL_EPEX; else if (mce->model == 0x56) return CPU_BROADWELL_DE; else if (mce->model == 0x4f) return CPU_BROADWELL_EPEX; else if (mce->model == 0x3d) return CPU_BROADWELL; else if (mce->model == 0x57) return CPU_KNIGHTS_LANDING; else if (mce->model == 0x85) return CPU_KNIGHTS_MILL; else if (mce->model == 0x55) return CPU_SKYLAKE_XEON; else if (mce->model == 0x6a) return CPU_ICELAKE_XEON; else if (mce->model == 0x6c) return CPU_ICELAKE_DE; else if (mce->model == 0x86) return CPU_TREMONT_D; else if (mce->model == 0x8f) return CPU_SAPPHIRERAPIDS; if (mce->model > 0x1a) { log(ALL, LOG_INFO, "Family 6 Model %x CPU: only decoding architectural errors\n", mce->model); return CPU_INTEL; } } if (mce->family > 6) { log(ALL, LOG_INFO, "Family %u Model %x CPU: only decoding architectural errors\n", mce->family, mce->model); return CPU_INTEL; } log(ALL, LOG_INFO, "Unknown Intel CPU type Family %x Model %x\n", mce->family, mce->model); return mce->family == 6 ? CPU_P6OLD : CPU_GENERIC; } static int detect_cpu(struct ras_events *ras) { struct mce_priv *mce = ras->mce_priv; FILE *f; int ret = 0; char *line = NULL; size_t linelen = 0; enum { CPU_VENDOR = 1, CPU_FAMILY = 2, CPU_MODEL = 4, CPU_MHZ = 8, CPU_FLAGS = 16, CPU_ALL = 0x1f } seen = 0; mce->family = 0; mce->model = 0; mce->mhz = 0; mce->vendor[0] = '\0'; f = fopen("/proc/cpuinfo","r"); if (!f) { log(ALL, LOG_INFO, "Can't open /proc/cpuinfo\n"); return errno; } while (seen != CPU_ALL && getdelim(&line, &linelen, '\n', f) > 0) { if (sscanf(line, "vendor_id : %63[^\n]", (char *)&mce->vendor) == 1) seen |= CPU_VENDOR; else if (sscanf(line, "cpu family : %d", &mce->family) == 1) seen |= CPU_FAMILY; else if (sscanf(line, "model : %d", &mce->model) == 1) seen |= CPU_MODEL; else if (sscanf(line, "cpu MHz : %lf", &mce->mhz) == 1) seen |= CPU_MHZ; else if (!strncmp(line, "flags", 5) && isspace(line[6])) { if (mce->processor_flags) free(mce->processor_flags); mce->processor_flags = line; line = NULL; linelen = 0; seen |= CPU_FLAGS; } } if (seen != CPU_ALL) { log(ALL, LOG_INFO, "Can't parse /proc/cpuinfo: missing%s%s%s%s%s\n", (seen & CPU_VENDOR) ? "" : " [vendor_id]", (seen & CPU_FAMILY) ? "" : " [cpu family]", (seen & CPU_MODEL) ? "" : " [model]", (seen & CPU_MHZ) ? "" : " [cpu MHz]", (seen & CPU_FLAGS) ? "" : " [flags]"); ret = EINVAL; goto ret; } /* Handle only Intel and AMD CPUs */ ret = 0; if (!strcmp(mce->vendor, "AuthenticAMD")) { if (mce->family == 15) mce->cputype = CPU_K8; if (strstr(mce->processor_flags, "smca")) { mce->cputype = CPU_AMD_SMCA; goto ret; } if (mce->family > 25) { log(ALL, LOG_INFO, "Can't parse MCE for this AMD CPU yet %d\n", mce->family); ret = EINVAL; } goto ret; } else if (!strcmp(mce->vendor,"HygonGenuine")) { if (mce->family == 24) { mce->cputype = CPU_DHYANA; } goto ret; } else if (!strcmp(mce->vendor,"GenuineIntel")) { mce->cputype = select_intel_cputype(ras); } else { ret = EINVAL; } ret: fclose(f); free(line); return ret; } int register_mce_handler(struct ras_events *ras, unsigned ncpus) { int rc; struct mce_priv *mce; ras->mce_priv = calloc(1, sizeof(struct mce_priv)); if (!ras->mce_priv) { log(ALL, LOG_INFO, "Can't allocate memory MCE data\n"); return ENOMEM; } mce = ras->mce_priv; rc = detect_cpu(ras); if (rc) { if (mce->processor_flags) free (mce->processor_flags); free (ras->mce_priv); ras->mce_priv = NULL; return (rc); } switch (mce->cputype) { case CPU_SANDY_BRIDGE_EP: case CPU_IVY_BRIDGE_EPEX: case CPU_HASWELL_EPEX: case CPU_KNIGHTS_LANDING: case CPU_KNIGHTS_MILL: set_intel_imc_log(mce->cputype, ncpus); default: break; } return rc; } /* * End of mcelog's code */ static void report_mce_event(struct ras_events *ras, struct pevent_record *record, struct trace_seq *s, struct mce_event *e) { time_t now; struct tm *tm; struct mce_priv *mce = ras->mce_priv; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(e->timestamp, sizeof(e->timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", e->timestamp); if (*e->bank_name) trace_seq_printf(s, "%s", e->bank_name); else trace_seq_printf(s, "bank=%x", e->bank); trace_seq_printf(s, ", status= %llx", (long long)e->status); if (*e->error_msg) trace_seq_printf(s, ", %s", e->error_msg); if (*e->mcistatus_msg) trace_seq_printf(s, ", mci=%s", e->mcistatus_msg); if (*e->mcastatus_msg) trace_seq_printf(s, ", mca=%s", e->mcastatus_msg); if (*e->user_action) trace_seq_printf(s, " %s", e->user_action); if (*e->mc_location) trace_seq_printf(s, ", %s", e->mc_location); #if 0 /* * While the logic for decoding tsc is there at mcelog, why to * decode/print it, if we already got the uptime from the * tracing event? Let's just discard it for now. */ trace_seq_printf(s, ", tsc= %d", e->tsc); trace_seq_printf(s, ", walltime= %d", e->walltime); #endif trace_seq_printf(s, ", cpu_type= %s", cputype_name[mce->cputype]); trace_seq_printf(s, ", cpu= %d", e->cpu); trace_seq_printf(s, ", socketid= %d", e->socketid); #if 0 /* * The CPU vendor is already reported from mce->cputype */ trace_seq_printf(s, ", cpuvendor= %d", e->cpuvendor); trace_seq_printf(s, ", cpuid= %d", e->cpuid); #endif if (e->ip) trace_seq_printf(s, ", ip= %llx%s", (long long)e->ip, !(e->mcgstatus & MCG_STATUS_EIPV) ? " (INEXACT)" : ""); if (e->cs) trace_seq_printf(s, ", cs= %x", e->cs); if (e->status & MCI_STATUS_MISCV) trace_seq_printf(s, ", misc= %llx", (long long)e->misc); if (e->status & MCI_STATUS_ADDRV) trace_seq_printf(s, ", addr= %llx", (long long)e->addr); if (e->status & MCI_STATUS_SYNDV) trace_seq_printf(s, ", synd= %llx", (long long)e->synd); if (e->ipid) trace_seq_printf(s, ", ipid= %llx", (long long)e->ipid); if (*e->mcgstatus_msg) trace_seq_printf(s, ", %s", e->mcgstatus_msg); else trace_seq_printf(s, ", mcgstatus= %llx", (long long)e->mcgstatus); if (e->mcgcap) trace_seq_printf(s, ", mcgcap= %llx", (long long)e->mcgcap); trace_seq_printf(s, ", apicid= %x", e->apicid); /* * FIXME: The original mcelog userspace tool uses DMI to map from * address to DIMM. From the comments there, the code there doesn't * take interleaving sets into account. Also, it is known that * BIOS is generally not reliable enough to associate DIMM labels * with addresses. * As, in thesis, we shouldn't be receiving memory error reports via * MCE, as they should go via EDAC traces, let's not do it. */ } int ras_mce_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; struct ras_events *ras = context; struct mce_priv *mce = ras->mce_priv; struct mce_event e; int rc = 0; memset(&e, 0, sizeof(e)); /* Parse the MCE error data */ if (pevent_get_field_val(s, event, "mcgcap", record, &val, 1) < 0) return -1; e.mcgcap = val; if (pevent_get_field_val(s, event, "mcgstatus", record, &val, 1) < 0) return -1; e.mcgstatus = val; if (pevent_get_field_val(s, event, "status", record, &val, 1) < 0) return -1; e.status = val; if (pevent_get_field_val(s, event, "addr", record, &val, 1) < 0) return -1; e.addr = val; if (pevent_get_field_val(s, event, "misc", record, &val, 1) < 0) return -1; e.misc = val; if (pevent_get_field_val(s, event, "ip", record, &val, 1) < 0) return -1; e.ip = val; if (pevent_get_field_val(s, event, "tsc", record, &val, 1) < 0) return -1; e.tsc = val; if (pevent_get_field_val(s, event, "walltime", record, &val, 1) < 0) return -1; e.walltime = val; if (pevent_get_field_val(s, event, "cpu", record, &val, 1) < 0) return -1; e.cpu = val; if (pevent_get_field_val(s, event, "cpuid", record, &val, 1) < 0) return -1; e.cpuid = val; if (pevent_get_field_val(s, event, "apicid", record, &val, 1) < 0) return -1; e.apicid = val; if (pevent_get_field_val(s, event, "socketid", record, &val, 1) < 0) return -1; e.socketid = val; if (pevent_get_field_val(s, event, "cs", record, &val, 1) < 0) return -1; e.cs = val; if (pevent_get_field_val(s, event, "bank", record, &val, 1) < 0) return -1; e.bank = val; if (pevent_get_field_val(s, event, "cpuvendor", record, &val, 1) < 0) return -1; e.cpuvendor = val; /* Get New entries */ if (pevent_get_field_val(s, event, "synd", record, &val, 1) < 0) return -1; e.synd = val; if (pevent_get_field_val(s, event, "ipid", record, &val, 1) < 0) return -1; e.ipid = val; switch (mce->cputype) { case CPU_GENERIC: break; case CPU_K8: rc = parse_amd_k8_event(ras, &e); break; case CPU_AMD_SMCA: case CPU_DHYANA: rc = parse_amd_smca_event(ras, &e); break; default: /* All other CPU types are Intel */ rc = parse_intel_event(ras, &e); } if (rc) return rc; if (!*e.error_msg && *e.mcastatus_msg) mce_snprintf(e.error_msg, "%s", e.mcastatus_msg); report_mce_event(ras, record, s, &e); #ifdef HAVE_SQLITE3 ras_store_mce_record(ras, &e); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_mce_event(ras, &e); #endif return 0; } 07070100000057000081A4000000000000000000000001611B9790000015D7000000000000000000000000000000000000003100000000rasdaemon-0.6.7.18.git+7ccf12f/ras-mce-handler.h/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_MCE_HANDLER_H #define __RAS_MCE_HANDLER_H #include <stdint.h> #include "ras-events.h" #include "libtrace/event-parse.h" enum cputype { CPU_GENERIC, CPU_P6OLD, CPU_CORE2, /* 65nm and 45nm */ CPU_K8, CPU_P4, CPU_NEHALEM, CPU_DUNNINGTON, CPU_TULSA, CPU_INTEL, /* Intel architectural errors */ CPU_XEON75XX, CPU_SANDY_BRIDGE, CPU_SANDY_BRIDGE_EP, CPU_IVY_BRIDGE, CPU_IVY_BRIDGE_EPEX, CPU_HASWELL, CPU_HASWELL_EPEX, CPU_BROADWELL, CPU_BROADWELL_DE, CPU_BROADWELL_EPEX, CPU_KNIGHTS_LANDING, CPU_KNIGHTS_MILL, CPU_SKYLAKE_XEON, CPU_AMD_SMCA, CPU_DHYANA, CPU_ICELAKE_XEON, CPU_ICELAKE_DE, CPU_TREMONT_D, CPU_SAPPHIRERAPIDS, }; struct mce_event { /* Unparsed data, obtained directly from MCE tracing */ uint64_t mcgcap; uint64_t mcgstatus; uint64_t status; uint64_t addr; uint64_t misc; uint64_t ip; uint64_t tsc; uint64_t walltime; uint32_t cpu; uint32_t cpuid; uint32_t apicid; uint32_t socketid; uint8_t cs; uint8_t bank; uint8_t cpuvendor; uint64_t synd; /* MCA_SYND MSR: only valid on SMCA systems */ uint64_t ipid; /* MCA_IPID MSR: only valid on SMCA systems */ /* Parsed data */ char timestamp[64]; char bank_name[64]; char error_msg[4096]; char mcgstatus_msg[256]; char mcistatus_msg[1024]; char mcastatus_msg[1024]; char user_action[4096]; char mc_location[256]; }; struct mce_priv { /* CPU Info */ char vendor[64]; unsigned int family, model; double mhz; enum cputype cputype; unsigned mc_error_support:1; char *processor_flags; }; #define mce_snprintf(buf, fmt, arg...) do { \ unsigned __n = strlen(buf); \ unsigned __len = sizeof(buf) - __n; \ if (!__len) \ break; \ if (__n) { \ snprintf(buf + __n, __len, " "); \ __len--; \ __n++; \ } \ snprintf(buf + __n, __len, fmt, ##arg); \ } while (0) /* register and handling routines */ int register_mce_handler(struct ras_events *ras, unsigned ncpus); int ras_mce_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); /* enables intel iMC logs */ int set_intel_imc_log(enum cputype cputype, unsigned ncpus); /* Per-CPU-type decoders for Intel CPUs */ void p4_decode_model(struct mce_event *e); void core2_decode_model(struct mce_event *e); void p6old_decode_model(struct mce_event *e); void nehalem_decode_model(struct mce_event *e); void xeon75xx_decode_model(struct mce_event *e); void dunnington_decode_model(struct mce_event *e); void snb_decode_model(struct ras_events *ras, struct mce_event *e); void ivb_decode_model(struct ras_events *ras, struct mce_event *e); void hsw_decode_model(struct ras_events *ras, struct mce_event *e); void knl_decode_model(struct ras_events *ras, struct mce_event *e); void tulsa_decode_model(struct mce_event *e); void broadwell_de_decode_model(struct ras_events *ras, struct mce_event *e); void broadwell_epex_decode_model(struct ras_events *ras, struct mce_event *e); void skylake_s_decode_model(struct ras_events *ras, struct mce_event *e); void i10nm_decode_model(enum cputype cputype, struct ras_events *ras, struct mce_event *e); /* AMD error code decode function */ void decode_amd_errcode(struct mce_event *e); /* Software defined banks */ #define MCE_EXTENDED_BANK 128 #define MCI_THRESHOLD_OVER (1ULL<<48) /* threshold error count overflow */ #define MCI_STATUS_VAL (1ULL<<63) /* valid error */ #define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */ #define MCI_STATUS_UC (1ULL<<61) /* uncorrected error */ #define MCI_STATUS_EN (1ULL<<60) /* error enabled */ #define MCI_STATUS_MISCV (1ULL<<59) /* misc error reg. valid */ #define MCI_STATUS_ADDRV (1ULL<<58) /* addr reg. valid */ #define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */ #define MCI_STATUS_S (1ULL<<56) /* signalled */ #define MCI_STATUS_AR (1ULL<<55) /* action-required */ /* AMD-specific bits */ #define MCI_STATUS_TCC (1ULL<<55) /* Task context corrupt */ #define MCI_STATUS_SYNDV (1ULL<<53) /* synd reg. valid */ /* uncorrected error,deferred exception */ #define MCI_STATUS_DEFERRED (1ULL<<44) #define MCI_STATUS_POISON (1ULL<<43) /* access poisonous data */ #define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ #define MCG_STATUS_EIPV (1ULL<<1) /* eip points to correct instruction */ #define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */ #define MCG_STATUS_LMCE (1ULL<<3) /* local machine check signaled */ /* Those functions are defined on per-cpu vendor C files */ int parse_intel_event(struct ras_events *ras, struct mce_event *e); int parse_amd_k8_event(struct ras_events *ras, struct mce_event *e); int parse_amd_smca_event(struct ras_events *ras, struct mce_event *e); #endif 07070100000058000081A4000000000000000000000001611B979000001419000000000000000000000000000000000000003C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-memory-failure-handler.c/* * Copyright (c) Huawei Technologies Co., Ltd. 2020. 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. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "libtrace/kbuffer.h" #include "ras-memory-failure-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" /* Memory failure - various types of pages */ enum mf_action_page_type { MF_MSG_KERNEL, MF_MSG_KERNEL_HIGH_ORDER, MF_MSG_SLAB, MF_MSG_DIFFERENT_COMPOUND, MF_MSG_POISONED_HUGE, MF_MSG_HUGE, MF_MSG_FREE_HUGE, MF_MSG_NON_PMD_HUGE, MF_MSG_UNMAP_FAILED, MF_MSG_DIRTY_SWAPCACHE, MF_MSG_CLEAN_SWAPCACHE, MF_MSG_DIRTY_MLOCKED_LRU, MF_MSG_CLEAN_MLOCKED_LRU, MF_MSG_DIRTY_UNEVICTABLE_LRU, MF_MSG_CLEAN_UNEVICTABLE_LRU, MF_MSG_DIRTY_LRU, MF_MSG_CLEAN_LRU, MF_MSG_TRUNCATED_LRU, MF_MSG_BUDDY, MF_MSG_BUDDY_2ND, MF_MSG_DAX, MF_MSG_UNSPLIT_THP, MF_MSG_UNKNOWN, }; /* Action results for various types of pages */ enum mf_action_result { MF_IGNORED, /* Error: cannot be handled */ MF_FAILED, /* Error: handling failed */ MF_DELAYED, /* Will be handled later */ MF_RECOVERED, /* Successfully recovered */ }; /* memory failure page types */ static const struct { int type; const char *page_type; } mf_page_type[] = { { MF_MSG_KERNEL, "reserved kernel page" }, { MF_MSG_KERNEL_HIGH_ORDER, "high-order kernel page"}, { MF_MSG_SLAB, "kernel slab page"}, { MF_MSG_DIFFERENT_COMPOUND, "different compound page after locking"}, { MF_MSG_POISONED_HUGE, "huge page already hardware poisoned"}, { MF_MSG_HUGE, "huge page"}, { MF_MSG_FREE_HUGE, "free huge page"}, { MF_MSG_NON_PMD_HUGE, "non-pmd-sized huge page"}, { MF_MSG_UNMAP_FAILED, "unmapping failed page"}, { MF_MSG_DIRTY_SWAPCACHE, "dirty swapcache page"}, { MF_MSG_CLEAN_SWAPCACHE, "clean swapcache page"}, { MF_MSG_DIRTY_MLOCKED_LRU, "dirty mlocked LRU page"}, { MF_MSG_CLEAN_MLOCKED_LRU, "clean mlocked LRU page"}, { MF_MSG_DIRTY_UNEVICTABLE_LRU, "dirty unevictable LRU page"}, { MF_MSG_CLEAN_UNEVICTABLE_LRU, "clean unevictable LRU page"}, { MF_MSG_DIRTY_LRU, "dirty LRU page"}, { MF_MSG_CLEAN_LRU, "clean LRU page"}, { MF_MSG_TRUNCATED_LRU, "already truncated LRU page"}, { MF_MSG_BUDDY, "free buddy page"}, { MF_MSG_BUDDY_2ND, "free buddy page (2nd try)"}, { MF_MSG_DAX, "dax page"}, { MF_MSG_UNSPLIT_THP, "unsplit thp"}, { MF_MSG_UNKNOWN, "unknown page"}, }; /* memory failure action results */ static const struct { int result; const char *action_result; } mf_action_result[] = { { MF_IGNORED, "Ignored" }, { MF_FAILED, "Failed" }, { MF_DELAYED, "Delayed" }, { MF_RECOVERED, "Recovered" }, }; static const char *get_page_type(int page_type) { int i; for (i = 0; i < ARRAY_SIZE(mf_page_type); i++) if (mf_page_type[i].type == page_type) return mf_page_type[i].page_type; return "unknown page"; } static const char *get_action_result(int result) { int i; for (i = 0; i < ARRAY_SIZE(mf_action_result); i++) if (mf_action_result[i].result == result) return mf_action_result[i].action_result; return "unknown"; } int ras_memory_failure_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; struct ras_events *ras = context; time_t now; struct tm *tm; struct ras_mf_event ev; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); if (pevent_get_field_val(s, event, "pfn", record, &val, 1) < 0) return -1; sprintf(ev.pfn, "0x%llx", val); trace_seq_printf(s, "pfn=0x%llx ", val); if (pevent_get_field_val(s, event, "type", record, &val, 1) < 0) return -1; ev.page_type = get_page_type(val); trace_seq_printf(s, "page_type=%s ", ev.page_type); if (pevent_get_field_val(s, event, "result", record, &val, 1) < 0) return -1; ev.action_result = get_action_result(val); trace_seq_printf(s, "action_result=%s ", ev.action_result); /* Store data into the SQLite DB */ #ifdef HAVE_SQLITE3 ras_store_mf_event(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_mf_event(ras, &ev); #endif return 0; } 07070100000059000081A4000000000000000000000001611B979000000371000000000000000000000000000000000000003C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-memory-failure-handler.h/* * Copyright (c) Huawei Technologies Co., Ltd. 2020. 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. */ #ifndef __RAS_MEMORY_FAILURE_HANDLER_H #define __RAS_MEMORY_FAILURE_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" int ras_memory_failure_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); #endif 0707010000005A000081A4000000000000000000000001611B97900000170D000000000000000000000000000000000000003A00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-non-standard-handler.c/* * Copyright (c) 2016, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <unistd.h> #include "libtrace/kbuffer.h" #include "ras-non-standard-handler.h" #include "ras-record.h" #include "ras-logger.h" #include "ras-report.h" static struct ras_ns_ev_decoder *ras_ns_ev_dec_list; void print_le_hex(struct trace_seq *s, const uint8_t *buf, int index) { trace_seq_printf(s, "%02x%02x%02x%02x", buf[index+3], buf[index+2], buf[index+1], buf[index]); } static char *uuid_le(const char *uu) { static char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]; char *p = uuid; int i; static const unsigned char le[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15}; for (i = 0; i < 16; i++) { p += sprintf(p, "%.2x", uu[le[i]]); switch (i) { case 3: case 5: case 7: case 9: *p++ = '-'; break; } } *p = 0; return uuid; } static int uuid_le_cmp(const char *sec_type, const char *uuid2) { static char uuid1[32]; char *p = uuid1; int i; static const unsigned char le[16] = { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15}; for (i = 0; i < 16; i++) p += sprintf(p, "%.2x", sec_type[le[i]]); *p = 0; return strncmp(uuid1, uuid2, 32); } int register_ns_ev_decoder(struct ras_ns_ev_decoder *ns_ev_decoder) { struct ras_ns_ev_decoder *list; if (!ns_ev_decoder) return -1; ns_ev_decoder->next = NULL; #ifdef HAVE_SQLITE3 ns_ev_decoder->stmt_dec_record = NULL; #endif if (!ras_ns_ev_dec_list) { ras_ns_ev_dec_list = ns_ev_decoder; } else { list = ras_ns_ev_dec_list; while (list->next) list = list->next; list->next = ns_ev_decoder; } return 0; } static int find_ns_ev_decoder(const char *sec_type, struct ras_ns_ev_decoder **p_ns_ev_dec) { struct ras_ns_ev_decoder *ns_ev_decoder; int match = 0; ns_ev_decoder = ras_ns_ev_dec_list; while (ns_ev_decoder) { if (uuid_le_cmp(sec_type, ns_ev_decoder->sec_type) == 0) { *p_ns_ev_dec = ns_ev_decoder; match = 1; break; } ns_ev_decoder = ns_ev_decoder->next; } if (!match) return -1; return 0; } static void unregister_ns_ev_decoder(void) { #ifdef HAVE_SQLITE3 struct ras_ns_ev_decoder *ns_ev_decoder = ras_ns_ev_dec_list; while (ns_ev_decoder) { if (ns_ev_decoder->stmt_dec_record) { ras_mc_finalize_vendor_table(ns_ev_decoder->stmt_dec_record); ns_ev_decoder->stmt_dec_record = NULL; } ns_ev_decoder = ns_ev_decoder->next; } #endif ras_ns_ev_dec_list = NULL; } int ras_non_standard_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { int len, i, line_count; unsigned long long val; struct ras_events *ras = context; time_t now; struct tm *tm; struct ras_non_standard_event ev; struct ras_ns_ev_decoder *ns_ev_decoder; /* * Newer kernels (3.10-rc1 or upper) provide an uptime clock. * On previous kernels, the way to properly generate an event would * be to inject a fake one, measure its timestamp and diff it against * gettimeofday. We won't do it here. Instead, let's use uptime, * falling-back to the event report's time, if "uptime" clock is * not available (legacy kernels). */ if (ras->use_uptime) now = record->ts/user_hz + ras->uptime_diff; else now = time(NULL); tm = localtime(&now); if (tm) strftime(ev.timestamp, sizeof(ev.timestamp), "%Y-%m-%d %H:%M:%S %z", tm); trace_seq_printf(s, "%s ", ev.timestamp); if (pevent_get_field_val(s, event, "sev", record, &val, 1) < 0) return -1; switch (val) { case GHES_SEV_NO: ev.severity = "Informational"; break; case GHES_SEV_CORRECTED: ev.severity = "Corrected"; break; case GHES_SEV_RECOVERABLE: ev.severity = "Recoverable"; break; default: case GHES_SEV_PANIC: ev.severity = "Fatal"; } trace_seq_printf(s, "\n %s", ev.severity); ev.sec_type = pevent_get_field_raw(s, event, "sec_type", record, &len, 1); if(!ev.sec_type) return -1; if (strcmp(uuid_le(ev.sec_type), "e8ed898d-df16-43cc-8ecc-54f060ef157f") == 0) trace_seq_printf(s, "\n section type: %s", "Ampere Specific Error\n"); else trace_seq_printf(s, "\n section type: %s", uuid_le(ev.sec_type)); ev.fru_text = pevent_get_field_raw(s, event, "fru_text", record, &len, 1); ev.fru_id = pevent_get_field_raw(s, event, "fru_id", record, &len, 1); trace_seq_printf(s, " fru text: %s fru id: %s ", ev.fru_text, uuid_le(ev.fru_id)); if (pevent_get_field_val(s, event, "len", record, &val, 1) < 0) return -1; ev.length = val; trace_seq_printf(s, "\n length: %d\n", ev.length); ev.error = pevent_get_field_raw(s, event, "buf", record, &len, 1); if(!ev.error) return -1; if (!find_ns_ev_decoder(ev.sec_type, &ns_ev_decoder)) { ns_ev_decoder->decode(ras, ns_ev_decoder, s, &ev); } else { len = ev.length; i = 0; line_count = 0; trace_seq_printf(s, " error:\n %08x: ", i); while (len >= 4) { print_le_hex(s, ev.error, i); i += 4; len -= 4; if (++line_count == 4) { trace_seq_printf(s, "\n %08x: ", i); line_count = 0; } else trace_seq_printf(s, " "); } } /* Insert data into the SGBD */ #ifdef HAVE_SQLITE3 ras_store_non_standard_record(ras, &ev); #endif #ifdef HAVE_ABRT_REPORT /* Report event to ABRT */ ras_report_non_standard_event(ras, &ev); #endif return 0; } __attribute__((destructor)) static void ns_exit(void) { unregister_ns_ev_decoder(); } 0707010000005B000081A4000000000000000000000001611B9790000005D5000000000000000000000000000000000000003A00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-non-standard-handler.h/* * Copyright (c) 2016, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ #ifndef __RAS_NON_STANDARD_HANDLER_H #define __RAS_NON_STANDARD_HANDLER_H #include "ras-events.h" #include "libtrace/event-parse.h" #define BIT(nr) (1UL << (nr)) #define BIT_ULL(nr) (1ULL << (nr)) struct ras_ns_ev_decoder { struct ras_ns_ev_decoder *next; const char *sec_type; int (*decode)(struct ras_events *ras, struct ras_ns_ev_decoder *ev_decoder, struct trace_seq *s, struct ras_non_standard_event *event); #ifdef HAVE_SQLITE3 #include <sqlite3.h> sqlite3_stmt *stmt_dec_record; #endif }; int ras_non_standard_event_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); void print_le_hex(struct trace_seq *s, const uint8_t *buf, int index); #ifdef HAVE_NON_STANDARD int register_ns_ev_decoder(struct ras_ns_ev_decoder *ns_ev_decoder); #else static inline int register_ns_ev_decoder(struct ras_ns_ev_decoder *ns_ev_decoder) { return 0; }; #endif #endif 0707010000005C000081A4000000000000000000000001611B9790000021E8000000000000000000000000000000000000003400000000rasdaemon-0.6.7.18.git+7ccf12f/ras-page-isolation.c/* * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. 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. */ #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include "ras-logger.h" #include "ras-page-isolation.h" #define PARSED_ENV_LEN 50 static const struct config threshold_units[] = { { "m", 1000 }, { "k", 1000 }, { "", 1 }, {} }; static const struct config cycle_units[] = { { "d", 24 }, { "h", 60 }, { "m", 60 }, { "s", 1 }, {} }; static struct isolation threshold = { .name = "PAGE_CE_THRESHOLD", .units = threshold_units, .env = "50", .unit = "", }; static struct isolation cycle = { .name = "PAGE_CE_REFRESH_CYCLE", .units = cycle_units, .env = "24h", .unit = "h", }; static const char *kernel_offline[] = { [OFFLINE_SOFT] = "/sys/devices/system/memory/soft_offline_page", [OFFLINE_HARD] = "/sys/devices/system/memory/hard_offline_page", [OFFLINE_SOFT_THEN_HARD] = "/sys/devices/system/memory/soft_offline_page", }; static const struct config offline_choice[] = { { "off", OFFLINE_OFF }, { "account", OFFLINE_ACCOUNT }, { "soft", OFFLINE_SOFT }, { "hard", OFFLINE_HARD }, { "soft-then-hard", OFFLINE_SOFT_THEN_HARD }, {} }; static const char *page_state[] = { [PAGE_ONLINE] = "online", [PAGE_OFFLINE] = "offlined", [PAGE_OFFLINE_FAILED] = "offline-failed", }; static enum otype offline = OFFLINE_SOFT; static struct rb_root page_records; static void page_offline_init(void) { const char *env = "PAGE_CE_ACTION"; char *choice = getenv(env); const struct config *c = NULL; int matched = 0; if (choice) { for (c = offline_choice; c->name; c++) { if (!strcasecmp(choice, c->name)) { offline = c->val; matched = 1; break; } } } if (!matched) log(TERM, LOG_INFO, "Improper %s, set to default soft\n", env); if (offline > OFFLINE_ACCOUNT && access(kernel_offline[offline], W_OK)) { log(TERM, LOG_INFO, "Kernel does not support page offline interface\n"); offline = OFFLINE_ACCOUNT; } log(TERM, LOG_INFO, "Page offline choice on Corrected Errors is %s\n", offline_choice[offline].name); } static void parse_isolation_env(struct isolation *config) { char *env = getenv(config->name); char *unit = NULL; const struct config *units = NULL; int i, no_unit; int valid = 0; int unit_matched = 0; unsigned long value, tmp; /* check if env is vaild */ if (env && strlen(env)) { /* All the character before unit must be digit */ for (i = 0; i < strlen(env) - 1; i++) { if (!isdigit(env[i])) goto parse; } if (sscanf(env, "%lu", &value) < 1 || !value) goto parse; /* check if the unit is vaild */ unit = env + strlen(env) - 1; /* no unit, all the character are value character */ if (isdigit(*unit)) { valid = 1; no_unit = 1; goto parse; } for (units = config->units; units->name; units++) { /* value character and unit character are both valid */ if (!strcasecmp(unit, units->name)) { valid = 1; no_unit = 0; break; } } } parse: /* if invalid, use default env */ if (valid) { config->env = env; if (!no_unit) config->unit = unit; } else { log(TERM, LOG_INFO, "Improper %s, set to default %s.\n", config->name, config->env); } /* if env value string is greater than ulong_max, truncate the last digit */ sscanf(config->env, "%lu", &value); for (units = config->units; units->name; units++) { if (!strcasecmp(config->unit, units->name)) unit_matched = 1; if (unit_matched) { tmp = value; value *= units->val; if (tmp != 0 && value / tmp != units->val) config->overflow = true; } } config->val = value; /* In order to output value and unit perfectly */ config->unit = no_unit ? config->unit : ""; } static void parse_env_string(struct isolation *config, char *str) { int i; if (config->overflow) { /* when overflow, use basic unit */ for (i = 0; config->units[i].name; i++) ; sprintf(str, "%lu%s", config->val, config->units[i-1].name); log(TERM, LOG_INFO, "%s is set overflow(%s), truncate it\n", config->name, config->env); } else { sprintf(str, "%s%s", config->env, config->unit); } } static void page_isolation_init(void) { char threshold_string[PARSED_ENV_LEN]; char cycle_string[PARSED_ENV_LEN]; /** * It's unnecessary to parse threshold configuration when offline * choice is off. */ if (offline == OFFLINE_OFF) return; parse_isolation_env(&threshold); parse_isolation_env(&cycle); parse_env_string(&threshold, threshold_string); parse_env_string(&cycle, cycle_string); log(TERM, LOG_INFO, "Threshold of memory Corrected Errors is %s / %s\n", threshold_string, cycle_string); } void ras_page_account_init(void) { page_offline_init(); page_isolation_init(); } static int do_page_offline(unsigned long long addr, enum otype type) { int fd, rc; char buf[20]; fd = open(kernel_offline[type], O_WRONLY); if (fd == -1) { log(TERM, LOG_ERR, "[%s]:open file: %s failed\n", __func__, kernel_offline[type]); return -1; } sprintf(buf, "%#llx", addr); rc = write(fd, buf, strlen(buf)); if (rc < 0) { log(TERM, LOG_ERR, "page offline addr(%s) by %s failed, errno:%d\n", buf, kernel_offline[type], errno); } close(fd); return rc; } static void page_offline(struct page_record *pr) { unsigned long long addr = pr->addr; int ret; /* Offlining page is not required */ if (offline <= OFFLINE_ACCOUNT) { log(TERM, LOG_INFO, "PAGE_CE_ACTION=%s, ignore to offline page at %#llx\n", offline_choice[offline].name, addr); return; } /* Ignore offlined pages */ if (pr->offlined == PAGE_OFFLINE) { log(TERM, LOG_INFO, "page at %#llx is already offlined, ignore\n", addr); return; } /* Time to silence this noisy page */ if (offline == OFFLINE_SOFT_THEN_HARD) { ret = do_page_offline(addr, OFFLINE_SOFT); if (ret < 0) ret = do_page_offline(addr, OFFLINE_HARD); } else { ret = do_page_offline(addr, offline); } pr->offlined = ret < 0 ? PAGE_OFFLINE_FAILED : PAGE_OFFLINE; log(TERM, LOG_INFO, "Result of offlining page at %#llx: %s\n", addr, page_state[pr->offlined]); } static void page_record(struct page_record *pr, unsigned count, time_t time) { unsigned long period = time - pr->start; unsigned long tolerate; if (period >= cycle.val) { /** * Since we don't refresh automatically, it is possible that the period * between two occurences will be longer than the pre-configured refresh cycle. * In this case, we tolerate the frequency of the whole period up to * the pre-configured threshold. */ tolerate = (period / (double)cycle.val) * threshold.val; pr->count -= (tolerate > pr->count) ? pr->count : tolerate; pr->start = time; pr->excess = 0; } pr->count += count; if (pr->count >= threshold.val) { log(TERM, LOG_INFO, "Corrected Errors at %#llx exceeded threshold\n", pr->addr); /** * Backup ce count of current cycle to enable next round, which actually * should never happen if we can disable overflow completely in the same * time unit (but sadly we can't). */ pr->excess += pr->count; pr->count = 0; page_offline(pr); } } static struct page_record *page_lookup_insert(unsigned long long addr) { struct rb_node **entry = &page_records.rb_node; struct rb_node *parent = NULL; struct page_record *pr = NULL, *find = NULL; while (*entry) { parent = *entry; pr = rb_entry(parent, struct page_record, entry); if (addr == pr->addr) { return pr; } else if (addr < pr->addr) { entry = &(*entry)->rb_left; } else { entry = &(*entry)->rb_right; } } find = calloc(1, sizeof(struct page_record)); if (!find) { log(TERM, LOG_ERR, "No memory for page records\n"); return NULL; } find->addr = addr; rb_link_node(&find->entry, parent, entry); rb_insert_color(&find->entry, &page_records); return find; } void ras_record_page_error(unsigned long long addr, unsigned count, time_t time) { struct page_record *pr = NULL; if (offline == OFFLINE_OFF) return; pr = page_lookup_insert(addr & PAGE_MASK); if (pr) { if (!pr->start) pr->start = time; page_record(pr, count, time); } } 0707010000005D000081A4000000000000000000000001611B9790000005B6000000000000000000000000000000000000003400000000rasdaemon-0.6.7.18.git+7ccf12f/ras-page-isolation.h/* * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. 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. */ #ifndef __RAS_PAGE_ISOLATION_H #define __RAS_PAGE_ISOLATION_H #include <time.h> #include <stdbool.h> #include "rbtree.h" #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) struct config { char *name; unsigned long val; }; enum otype { OFFLINE_OFF, OFFLINE_ACCOUNT, OFFLINE_SOFT, OFFLINE_HARD, OFFLINE_SOFT_THEN_HARD, }; enum pstate { PAGE_ONLINE, PAGE_OFFLINE, PAGE_OFFLINE_FAILED, }; struct page_record { struct rb_node entry; unsigned long long addr; time_t start; enum pstate offlined; unsigned long count; unsigned long excess; }; struct isolation { char *name; char *env; const struct config *units; unsigned long val; bool overflow; char *unit; }; void ras_page_account_init(void); void ras_record_page_error(unsigned long long addr, unsigned count, time_t time); #endif 0707010000005E000081A4000000000000000000000001611B9790000075A0000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-record.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * Copyright (c) 2016, The Linux Foundation. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * BuildRequires: sqlite-devel */ #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/stat.h> #include "ras-events.h" #include "ras-mc-handler.h" #include "ras-aer-handler.h" #include "ras-mce-handler.h" #include "ras-logger.h" /* #define DEBUG_SQL 1 */ #define SQLITE_RAS_DB RASSTATEDIR "/" RAS_DB_FNAME /* * Table and functions to handle ras:mc_event */ static const struct db_fields mc_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="err_count", .type="INTEGER" }, { .name="err_type", .type="TEXT" }, { .name="err_msg", .type="TEXT" }, { .name="label", .type="TEXT" }, { .name="mc", .type="INTEGER" }, { .name="top_layer", .type="INTEGER" }, { .name="middle_layer", .type="INTEGER" }, { .name="lower_layer", .type="INTEGER" }, { .name="address", .type="INTEGER" }, { .name="grain", .type="INTEGER" }, { .name="syndrome", .type="INTEGER" }, { .name="driver_detail", .type="TEXT" }, }; static const struct db_table_descriptor mc_event_tab = { .name = "mc_event", .fields = mc_event_fields, .num_fields = ARRAY_SIZE(mc_event_fields), }; int ras_store_mc_event(struct ras_events *ras, struct ras_mc_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_mc_event) return 0; log(TERM, LOG_INFO, "mc_event store: %p\n", priv->stmt_mc_event); sqlite3_bind_text(priv->stmt_mc_event, 1, ev->timestamp, -1, NULL); sqlite3_bind_int (priv->stmt_mc_event, 2, ev->error_count); sqlite3_bind_text(priv->stmt_mc_event, 3, ev->error_type, -1, NULL); sqlite3_bind_text(priv->stmt_mc_event, 4, ev->msg, -1, NULL); sqlite3_bind_text(priv->stmt_mc_event, 5, ev->label, -1, NULL); sqlite3_bind_int (priv->stmt_mc_event, 6, ev->mc_index); sqlite3_bind_int (priv->stmt_mc_event, 7, ev->top_layer); sqlite3_bind_int (priv->stmt_mc_event, 8, ev->middle_layer); sqlite3_bind_int (priv->stmt_mc_event, 9, ev->lower_layer); sqlite3_bind_int64 (priv->stmt_mc_event, 10, ev->address); sqlite3_bind_int64 (priv->stmt_mc_event, 11, ev->grain); sqlite3_bind_int64 (priv->stmt_mc_event, 12, ev->syndrome); sqlite3_bind_text(priv->stmt_mc_event, 13, ev->driver_detail, -1, NULL); rc = sqlite3_step(priv->stmt_mc_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do mc_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_mc_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset mc_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } /* * Table and functions to handle ras:aer */ #ifdef HAVE_AER static const struct db_fields aer_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="dev_name", .type="TEXT" }, { .name="err_type", .type="TEXT" }, { .name="err_msg", .type="TEXT" }, }; static const struct db_table_descriptor aer_event_tab = { .name = "aer_event", .fields = aer_event_fields, .num_fields = ARRAY_SIZE(aer_event_fields), }; int ras_store_aer_event(struct ras_events *ras, struct ras_aer_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_aer_event) return 0; log(TERM, LOG_INFO, "aer_event store: %p\n", priv->stmt_aer_event); sqlite3_bind_text(priv->stmt_aer_event, 1, ev->timestamp, -1, NULL); sqlite3_bind_text(priv->stmt_aer_event, 2, ev->dev_name, -1, NULL); sqlite3_bind_text(priv->stmt_aer_event, 3, ev->error_type, -1, NULL); sqlite3_bind_text(priv->stmt_aer_event, 4, ev->msg, -1, NULL); rc = sqlite3_step(priv->stmt_aer_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do aer_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_aer_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset aer_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Table and functions to handle ras:non standard */ #ifdef HAVE_NON_STANDARD static const struct db_fields non_standard_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="sec_type", .type="BLOB" }, { .name="fru_id", .type="BLOB" }, { .name="fru_text", .type="TEXT" }, { .name="severity", .type="TEXT" }, { .name="error", .type="BLOB" }, }; static const struct db_table_descriptor non_standard_event_tab = { .name = "non_standard_event", .fields = non_standard_event_fields, .num_fields = ARRAY_SIZE(non_standard_event_fields), }; int ras_store_non_standard_record(struct ras_events *ras, struct ras_non_standard_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_non_standard_record) return 0; log(TERM, LOG_INFO, "non_standard_event store: %p\n", priv->stmt_non_standard_record); sqlite3_bind_text (priv->stmt_non_standard_record, 1, ev->timestamp, -1, NULL); sqlite3_bind_blob (priv->stmt_non_standard_record, 2, ev->sec_type, -1, NULL); sqlite3_bind_blob (priv->stmt_non_standard_record, 3, ev->fru_id, 16, NULL); sqlite3_bind_text (priv->stmt_non_standard_record, 4, ev->fru_text, -1, NULL); sqlite3_bind_text (priv->stmt_non_standard_record, 5, ev->severity, -1, NULL); sqlite3_bind_blob (priv->stmt_non_standard_record, 6, ev->error, ev->length, NULL); rc = sqlite3_step(priv->stmt_non_standard_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do non_standard_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_non_standard_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset non_standard_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Table and functions to handle ras:arm */ #ifdef HAVE_ARM static const struct db_fields arm_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="error_count", .type="INTEGER" }, { .name="affinity", .type="INTEGER" }, { .name="mpidr", .type="INTEGER" }, { .name="running_state", .type="INTEGER" }, { .name="psci_state", .type="INTEGER" }, { .name="err_info", .type="BLOB" }, { .name="context_info", .type="BLOB" }, { .name="vendor_info", .type="BLOB" }, }; static const struct db_table_descriptor arm_event_tab = { .name = "arm_event", .fields = arm_event_fields, .num_fields = ARRAY_SIZE(arm_event_fields), }; int ras_store_arm_record(struct ras_events *ras, struct ras_arm_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_arm_record) return 0; log(TERM, LOG_INFO, "arm_event store: %p\n", priv->stmt_arm_record); sqlite3_bind_text (priv->stmt_arm_record, 1, ev->timestamp, -1, NULL); sqlite3_bind_int (priv->stmt_arm_record, 2, ev->error_count); sqlite3_bind_int (priv->stmt_arm_record, 3, ev->affinity); sqlite3_bind_int64 (priv->stmt_arm_record, 4, ev->mpidr); sqlite3_bind_int (priv->stmt_arm_record, 5, ev->running_state); sqlite3_bind_int (priv->stmt_arm_record, 6, ev->psci_state); sqlite3_bind_blob (priv->stmt_arm_record, 7, ev->pei_error, ev->pei_len, NULL); sqlite3_bind_blob (priv->stmt_arm_record, 8, ev->ctx_error, ev->ctx_len, NULL); sqlite3_bind_blob (priv->stmt_arm_record, 9, ev->vsei_error, ev->oem_len, NULL); rc = sqlite3_step(priv->stmt_arm_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do arm_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_arm_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset arm_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif #ifdef HAVE_EXTLOG static const struct db_fields extlog_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="etype", .type="INTEGER" }, { .name="error_count", .type="INTEGER" }, { .name="severity", .type="INTEGER" }, { .name="address", .type="INTEGER" }, { .name="fru_id", .type="BLOB" }, { .name="fru_text", .type="TEXT" }, { .name="cper_data", .type="BLOB" }, }; static const struct db_table_descriptor extlog_event_tab = { .name = "extlog_event", .fields = extlog_event_fields, .num_fields = ARRAY_SIZE(extlog_event_fields), }; int ras_store_extlog_mem_record(struct ras_events *ras, struct ras_extlog_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_extlog_record) return 0; log(TERM, LOG_INFO, "extlog_record store: %p\n", priv->stmt_extlog_record); sqlite3_bind_text (priv->stmt_extlog_record, 1, ev->timestamp, -1, NULL); sqlite3_bind_int (priv->stmt_extlog_record, 2, ev->etype); sqlite3_bind_int (priv->stmt_extlog_record, 3, ev->error_seq); sqlite3_bind_int (priv->stmt_extlog_record, 4, ev->severity); sqlite3_bind_int64 (priv->stmt_extlog_record, 5, ev->address); sqlite3_bind_blob (priv->stmt_extlog_record, 6, ev->fru_id, 16, NULL); sqlite3_bind_text (priv->stmt_extlog_record, 7, ev->fru_text, -1, NULL); sqlite3_bind_blob (priv->stmt_extlog_record, 8, ev->cper_data, ev->cper_data_length, NULL); rc = sqlite3_step(priv->stmt_extlog_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do extlog_mem_record step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_extlog_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset extlog_mem_record on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Table and functions to handle mce:mce_record */ #ifdef HAVE_MCE static const struct db_fields mce_record_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, /* MCE registers */ { .name="mcgcap", .type="INTEGER" }, { .name="mcgstatus", .type="INTEGER" }, { .name="status", .type="INTEGER" }, { .name="addr", .type="INTEGER" }, // 5 { .name="misc", .type="INTEGER" }, { .name="ip", .type="INTEGER" }, { .name="tsc", .type="INTEGER" }, { .name="walltime", .type="INTEGER" }, { .name="cpu", .type="INTEGER" }, // 10 { .name="cpuid", .type="INTEGER" }, { .name="apicid", .type="INTEGER" }, { .name="socketid", .type="INTEGER" }, { .name="cs", .type="INTEGER" }, { .name="bank", .type="INTEGER" }, //15 { .name="cpuvendor", .type="INTEGER" }, /* Parsed data - will likely change */ { .name="bank_name", .type="TEXT" }, { .name="error_msg", .type="TEXT" }, { .name="mcgstatus_msg", .type="TEXT" }, { .name="mcistatus_msg", .type="TEXT" }, // 20 { .name="mcastatus_msg", .type="TEXT" }, { .name="user_action", .type="TEXT" }, { .name="mc_location", .type="TEXT" }, }; static const struct db_table_descriptor mce_record_tab = { .name = "mce_record", .fields = mce_record_fields, .num_fields = ARRAY_SIZE(mce_record_fields), }; int ras_store_mce_record(struct ras_events *ras, struct mce_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_mce_record) return 0; log(TERM, LOG_INFO, "mce_record store: %p\n", priv->stmt_mce_record); sqlite3_bind_text (priv->stmt_mce_record, 1, ev->timestamp, -1, NULL); sqlite3_bind_int (priv->stmt_mce_record, 2, ev->mcgcap); sqlite3_bind_int (priv->stmt_mce_record, 3, ev->mcgstatus); sqlite3_bind_int64 (priv->stmt_mce_record, 4, ev->status); sqlite3_bind_int64 (priv->stmt_mce_record, 5, ev->addr); sqlite3_bind_int64 (priv->stmt_mce_record, 6, ev->misc); sqlite3_bind_int64 (priv->stmt_mce_record, 7, ev->ip); sqlite3_bind_int64 (priv->stmt_mce_record, 8, ev->tsc); sqlite3_bind_int64 (priv->stmt_mce_record, 9, ev->walltime); sqlite3_bind_int (priv->stmt_mce_record, 10, ev->cpu); sqlite3_bind_int (priv->stmt_mce_record, 11, ev->cpuid); sqlite3_bind_int (priv->stmt_mce_record, 12, ev->apicid); sqlite3_bind_int (priv->stmt_mce_record, 13, ev->socketid); sqlite3_bind_int (priv->stmt_mce_record, 14, ev->cs); sqlite3_bind_int (priv->stmt_mce_record, 15, ev->bank); sqlite3_bind_int (priv->stmt_mce_record, 16, ev->cpuvendor); sqlite3_bind_text(priv->stmt_mce_record, 17, ev->bank_name, -1, NULL); sqlite3_bind_text(priv->stmt_mce_record, 18, ev->error_msg, -1, NULL); sqlite3_bind_text(priv->stmt_mce_record, 19, ev->mcgstatus_msg, -1, NULL); sqlite3_bind_text(priv->stmt_mce_record, 20, ev->mcistatus_msg, -1, NULL); sqlite3_bind_text(priv->stmt_mce_record, 21, ev->mcastatus_msg, -1, NULL); sqlite3_bind_text(priv->stmt_mce_record, 22, ev->user_action, -1, NULL); sqlite3_bind_text(priv->stmt_mce_record, 23, ev->mc_location, -1, NULL); rc = sqlite3_step(priv->stmt_mce_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do mce_record step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_mce_record); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset mce_record on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Table and functions to handle devlink:devlink_health_report */ #ifdef HAVE_DEVLINK static const struct db_fields devlink_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="bus_name", .type="TEXT" }, { .name="dev_name", .type="TEXT" }, { .name="driver_name", .type="TEXT" }, { .name="reporter_name", .type="TEXT" }, { .name="msg", .type="TEXT" }, }; static const struct db_table_descriptor devlink_event_tab = { .name = "devlink_event", .fields = devlink_event_fields, .num_fields = ARRAY_SIZE(devlink_event_fields), }; int ras_store_devlink_event(struct ras_events *ras, struct devlink_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_devlink_event) return 0; log(TERM, LOG_INFO, "devlink_event store: %p\n", priv->stmt_devlink_event); sqlite3_bind_text(priv->stmt_devlink_event, 1, ev->timestamp, -1, NULL); sqlite3_bind_text(priv->stmt_devlink_event, 2, ev->bus_name, -1, NULL); sqlite3_bind_text(priv->stmt_devlink_event, 3, ev->dev_name, -1, NULL); sqlite3_bind_text(priv->stmt_devlink_event, 4, ev->driver_name, -1, NULL); sqlite3_bind_text(priv->stmt_devlink_event, 5, ev->reporter_name, -1, NULL); sqlite3_bind_text(priv->stmt_devlink_event, 6, ev->msg, -1, NULL); rc = sqlite3_step(priv->stmt_devlink_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do devlink_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_devlink_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset devlink_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Table and functions to handle block:block_rq_complete */ #ifdef HAVE_DISKERROR static const struct db_fields diskerror_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="dev", .type="TEXT" }, { .name="sector", .type="INTEGER" }, { .name="nr_sector", .type="INTEGER" }, { .name="error", .type="TEXT" }, { .name="rwbs", .type="TEXT" }, { .name="cmd", .type="TEXT" }, }; static const struct db_table_descriptor diskerror_event_tab = { .name = "disk_errors", .fields = diskerror_event_fields, .num_fields = ARRAY_SIZE(diskerror_event_fields), }; int ras_store_diskerror_event(struct ras_events *ras, struct diskerror_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_diskerror_event) return 0; log(TERM, LOG_INFO, "diskerror_eventstore: %p\n", priv->stmt_diskerror_event); sqlite3_bind_text(priv->stmt_diskerror_event, 1, ev->timestamp, -1, NULL); sqlite3_bind_text(priv->stmt_diskerror_event, 2, ev->dev, -1, NULL); sqlite3_bind_int64(priv->stmt_diskerror_event, 3, ev->sector); sqlite3_bind_int(priv->stmt_diskerror_event, 4, ev->nr_sector); sqlite3_bind_text(priv->stmt_diskerror_event, 5, ev->error, -1, NULL); sqlite3_bind_text(priv->stmt_diskerror_event, 6, ev->rwbs, -1, NULL); sqlite3_bind_text(priv->stmt_diskerror_event, 7, ev->cmd, -1, NULL); rc = sqlite3_step(priv->stmt_diskerror_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do diskerror_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_diskerror_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset diskerror_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Table and functions to handle ras:memory_failure */ #ifdef HAVE_MEMORY_FAILURE static const struct db_fields mf_event_fields[] = { { .name="id", .type="INTEGER PRIMARY KEY" }, { .name="timestamp", .type="TEXT" }, { .name="pfn", .type="TEXT" }, { .name="page_type", .type="TEXT" }, { .name="action_result", .type="TEXT" }, }; static const struct db_table_descriptor mf_event_tab = { .name = "memory_failure_event", .fields = mf_event_fields, .num_fields = ARRAY_SIZE(mf_event_fields), }; int ras_store_mf_event(struct ras_events *ras, struct ras_mf_event *ev) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv || !priv->stmt_mf_event) return 0; log(TERM, LOG_INFO, "memory_failure_event store: %p\n", priv->stmt_mf_event); sqlite3_bind_text(priv->stmt_mf_event, 1, ev->timestamp, -1, NULL); sqlite3_bind_text(priv->stmt_mf_event, 2, ev->pfn, -1, NULL); sqlite3_bind_text(priv->stmt_mf_event, 3, ev->page_type, -1, NULL); sqlite3_bind_text(priv->stmt_mf_event, 4, ev->action_result, -1, NULL); rc = sqlite3_step(priv->stmt_mf_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed to do memory_failure_event step on sqlite: error = %d\n", rc); rc = sqlite3_reset(priv->stmt_mf_event); if (rc != SQLITE_OK && rc != SQLITE_DONE) log(TERM, LOG_ERR, "Failed reset memory_failure_event on sqlite: error = %d\n", rc); log(TERM, LOG_INFO, "register inserted at db\n"); return rc; } #endif /* * Generic code */ static int __ras_mc_prepare_stmt(struct sqlite3_priv *priv, sqlite3_stmt **stmt, const struct db_table_descriptor *db_tab) { int i, rc; char sql[1024], *p = sql, *end = sql + sizeof(sql); const struct db_fields *field; p += snprintf(p, end - p, "INSERT INTO %s (", db_tab->name); for (i = 0; i < db_tab->num_fields; i++) { field = &db_tab->fields[i]; p += snprintf(p, end - p, "%s", field->name); if (i < db_tab->num_fields - 1) p += snprintf(p, end - p, ", "); } p += snprintf(p, end - p, ") VALUES ( NULL, "); for (i = 1; i < db_tab->num_fields; i++) { if (i < db_tab->num_fields - 1) strcat(sql, "?, "); else strcat(sql, "?)"); } #ifdef DEBUG_SQL log(TERM, LOG_INFO, "SQL: %s\n", sql); #endif rc = sqlite3_prepare_v2(priv->db, sql, -1, stmt, NULL); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "Failed to prepare insert db at table %s (db %s): error = %s\n", db_tab->name, SQLITE_RAS_DB, sqlite3_errmsg(priv->db)); stmt = NULL; } else { log(TERM, LOG_INFO, "Recording %s events\n", db_tab->name); } return rc; } static int ras_mc_create_table(struct sqlite3_priv *priv, const struct db_table_descriptor *db_tab) { const struct db_fields *field; char sql[1024], *p = sql, *end = sql + sizeof(sql); int i, rc; p += snprintf(p, end - p, "CREATE TABLE IF NOT EXISTS %s (", db_tab->name); for (i = 0; i < db_tab->num_fields; i++) { field = &db_tab->fields[i]; p += snprintf(p, end - p, "%s %s", field->name, field->type); if (i < db_tab->num_fields - 1) p += snprintf(p, end - p, ", "); } p += snprintf(p, end - p, ")"); #ifdef DEBUG_SQL log(TERM, LOG_INFO, "SQL: %s\n", sql); #endif rc = sqlite3_exec(priv->db, sql, NULL, NULL, NULL); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "Failed to create table %s on %s: error = %d\n", db_tab->name, SQLITE_RAS_DB, rc); } return rc; } static int ras_mc_alter_table(struct sqlite3_priv *priv, sqlite3_stmt **stmt, const struct db_table_descriptor *db_tab) { char sql[1024], *p = sql, *end = sql + sizeof(sql); const struct db_fields *field; int col_count; int i, j, rc, found; snprintf(p, end - p, "SELECT * FROM %s", db_tab->name); rc = sqlite3_prepare_v2(priv->db, sql, -1, stmt, NULL); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "Failed to query fields from the table %s on %s: error = %d\n", db_tab->name, SQLITE_RAS_DB, rc); return rc; } col_count = sqlite3_column_count(*stmt); for (i = 0; i < db_tab->num_fields; i++) { field = &db_tab->fields[i]; found = 0; for (j = 0; j < col_count; j++) { if (!strcmp(field->name, sqlite3_column_name(*stmt, j))) { found = 1; break; } } if (!found) { /* add new field */ p += snprintf(p, end - p, "ALTER TABLE %s ADD ", db_tab->name); p += snprintf(p, end - p, "%s %s", field->name, field->type); #ifdef DEBUG_SQL log(TERM, LOG_INFO, "SQL: %s\n", sql); #endif rc = sqlite3_exec(priv->db, sql, NULL, NULL, NULL); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "Failed to add new field %s to the table %s on %s: error = %d\n", field->name, db_tab->name, SQLITE_RAS_DB, rc); return rc; } p = sql; memset(sql, 0, sizeof(sql)); } } return rc; } static int ras_mc_prepare_stmt(struct sqlite3_priv *priv, sqlite3_stmt **stmt, const struct db_table_descriptor *db_tab) { int rc; rc = __ras_mc_prepare_stmt(priv, stmt, db_tab); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "Failed to prepare insert db at table %s (db %s): error = %s\n", db_tab->name, SQLITE_RAS_DB, sqlite3_errmsg(priv->db)); log(TERM, LOG_INFO, "Trying to alter db at table %s (db %s)\n", db_tab->name, SQLITE_RAS_DB); rc = ras_mc_alter_table(priv, stmt, db_tab); if (rc != SQLITE_OK && rc != SQLITE_DONE) { log(TERM, LOG_ERR, "Failed to alter db at table %s (db %s): error = %s\n", db_tab->name, SQLITE_RAS_DB, sqlite3_errmsg(priv->db)); stmt = NULL; return rc; } rc = __ras_mc_prepare_stmt(priv, stmt, db_tab); } return rc; } int ras_mc_add_vendor_table(struct ras_events *ras, sqlite3_stmt **stmt, const struct db_table_descriptor *db_tab) { int rc; struct sqlite3_priv *priv = ras->db_priv; if (!priv) return -1; rc = ras_mc_create_table(priv, db_tab); if (rc == SQLITE_OK) rc = ras_mc_prepare_stmt(priv, stmt, db_tab); return rc; } int ras_mc_finalize_vendor_table(sqlite3_stmt *stmt) { int rc; rc = sqlite3_finalize(stmt); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "Failed to finalize sqlite: error = %d\n", rc); return rc; } int ras_mc_event_opendb(unsigned cpu, struct ras_events *ras) { int rc; sqlite3 *db; struct sqlite3_priv *priv; printf("Calling %s()\n", __FUNCTION__); ras->db_priv = NULL; priv = calloc(1, sizeof(*priv)); if (!priv) return -1; struct stat st = {0}; if (stat(RASSTATEDIR, &st) == -1) { if (errno != ENOENT) { log(TERM, LOG_ERR, "Failed to read state directory " RASSTATEDIR); goto error; } if (mkdir(RASSTATEDIR, 0700) == -1) { log(TERM, LOG_ERR, "Failed to create state directory " RASSTATEDIR); goto error; } } rc = sqlite3_initialize(); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "cpu %u: Failed to initialize sqlite: error = %d\n", cpu, rc); goto error; } do { rc = sqlite3_open_v2(SQLITE_RAS_DB, &db, SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if (rc == SQLITE_BUSY) usleep(10000); } while (rc == SQLITE_BUSY); if (rc != SQLITE_OK) { log(TERM, LOG_ERR, "cpu %u: Failed to connect to %s: error = %d\n", cpu, SQLITE_RAS_DB, rc); goto error; } priv->db = db; rc = ras_mc_create_table(priv, &mc_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_mc_event, &mc_event_tab); if (rc != SQLITE_OK) goto error; } #ifdef HAVE_AER rc = ras_mc_create_table(priv, &aer_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_aer_event, &aer_event_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_EXTLOG rc = ras_mc_create_table(priv, &extlog_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_extlog_record, &extlog_event_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_MCE rc = ras_mc_create_table(priv, &mce_record_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_mce_record, &mce_record_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_NON_STANDARD rc = ras_mc_create_table(priv, &non_standard_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_non_standard_record, &non_standard_event_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_ARM rc = ras_mc_create_table(priv, &arm_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_arm_record, &arm_event_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_DEVLINK rc = ras_mc_create_table(priv, &devlink_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_devlink_event, &devlink_event_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_DISKERROR rc = ras_mc_create_table(priv, &diskerror_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_diskerror_event, &diskerror_event_tab); if (rc != SQLITE_OK) goto error; } #endif #ifdef HAVE_MEMORY_FAILURE rc = ras_mc_create_table(priv, &mf_event_tab); if (rc == SQLITE_OK) { rc = ras_mc_prepare_stmt(priv, &priv->stmt_mf_event, &mf_event_tab); if (rc != SQLITE_OK) goto error; } #endif ras->db_priv = priv; return 0; error: free(priv); return -1; } int ras_mc_event_closedb(unsigned int cpu, struct ras_events *ras) { int rc; sqlite3 *db; struct sqlite3_priv *priv = ras->db_priv; printf("Calling %s()\n", __func__); if (!priv) return -1; db = priv->db; if (!db) return -1; if (priv->stmt_mc_event) { rc = sqlite3_finalize(priv->stmt_mc_event); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize mc_event sqlite: error = %d\n", cpu, rc); } #ifdef HAVE_AER if (priv->stmt_aer_event) { rc = sqlite3_finalize(priv->stmt_aer_event); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize aer_event sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_EXTLOG if (priv->stmt_extlog_record) { rc = sqlite3_finalize(priv->stmt_extlog_record); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize extlog_record sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_MCE if (priv->stmt_mce_record) { rc = sqlite3_finalize(priv->stmt_mce_record); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize mce_record sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_NON_STANDARD if (priv->stmt_non_standard_record) { rc = sqlite3_finalize(priv->stmt_non_standard_record); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize non_standard_record sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_ARM if (priv->stmt_arm_record) { rc = sqlite3_finalize(priv->stmt_arm_record); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize arm_record sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_DEVLINK if (priv->stmt_devlink_event) { rc = sqlite3_finalize(priv->stmt_devlink_event); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize devlink_event sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_DISKERROR if (priv->stmt_diskerror_event) { rc = sqlite3_finalize(priv->stmt_diskerror_event); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize diskerror_event sqlite: error = %d\n", cpu, rc); } #endif #ifdef HAVE_MEMORY_FAILURE if (priv->stmt_mf_event) { rc = sqlite3_finalize(priv->stmt_mf_event); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to finalize mf_event sqlite: error = %d\n", cpu, rc); } #endif rc = sqlite3_close_v2(db); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to close sqlite: error = %d\n", cpu, rc); rc = sqlite3_shutdown(); if (rc != SQLITE_OK) log(TERM, LOG_ERR, "cpu %u: Failed to shutdown sqlite: error = %d\n", cpu, rc); free(priv); return 0; } 0707010000005F000081A4000000000000000000000001611B9790000016B9000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-record.h/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * Copyright (c) 2016, The Linux Foundation. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __RAS_RECORD_H #define __RAS_RECORD_H #include <stdint.h> #include "config.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x))) extern long user_hz; struct ras_events; struct ras_mc_event { char timestamp[64]; int error_count; const char *error_type, *msg, *label; unsigned char mc_index; signed char top_layer, middle_layer, lower_layer; unsigned long long address, grain, syndrome; const char *driver_detail; }; struct ras_aer_event { char timestamp[64]; const char *error_type; const char *dev_name; uint8_t tlp_header_valid; uint32_t *tlp_header; const char *msg; }; struct ras_extlog_event { char timestamp[64]; int32_t error_seq; int8_t etype; int8_t severity; unsigned long long address; int8_t pa_mask_lsb; const char *fru_id; const char *fru_text; const char *cper_data; unsigned short cper_data_length; }; struct ras_non_standard_event { char timestamp[64]; const char *sec_type, *fru_id, *fru_text; const char *severity; const uint8_t *error; uint32_t length; }; struct ras_arm_event { char timestamp[64]; int32_t error_count; int8_t affinity; int64_t mpidr; int64_t midr; int32_t running_state; int32_t psci_state; const uint8_t *pei_error; uint32_t pei_len; const uint8_t *ctx_error; uint32_t ctx_len; const uint8_t *vsei_error; uint32_t oem_len; }; struct devlink_event { char timestamp[64]; const char *bus_name; const char *dev_name; const char *driver_name; const char *reporter_name; char *msg; }; struct diskerror_event { char timestamp[64]; char *dev; unsigned long long sector; unsigned int nr_sector; const char *error; const char *rwbs; const char *cmd; }; struct ras_mf_event { char timestamp[64]; char pfn[30]; const char *page_type; const char *action_result; }; struct ras_mc_event; struct ras_aer_event; struct ras_extlog_event; struct ras_non_standard_event; struct ras_arm_event; struct mce_event; struct devlink_event; struct diskerror_event; struct ras_mf_event; #ifdef HAVE_SQLITE3 #include <sqlite3.h> struct sqlite3_priv { sqlite3 *db; sqlite3_stmt *stmt_mc_event; #ifdef HAVE_AER sqlite3_stmt *stmt_aer_event; #endif #ifdef HAVE_MCE sqlite3_stmt *stmt_mce_record; #endif #ifdef HAVE_EXTLOG sqlite3_stmt *stmt_extlog_record; #endif #ifdef HAVE_NON_STANDARD sqlite3_stmt *stmt_non_standard_record; #endif #ifdef HAVE_ARM sqlite3_stmt *stmt_arm_record; #endif #ifdef HAVE_DEVLINK sqlite3_stmt *stmt_devlink_event; #endif #ifdef HAVE_DISKERROR sqlite3_stmt *stmt_diskerror_event; #endif #ifdef HAVE_MEMORY_FAILURE sqlite3_stmt *stmt_mf_event; #endif }; struct db_fields { char *name; char *type; }; struct db_table_descriptor { char *name; const struct db_fields *fields; size_t num_fields; }; int ras_mc_event_opendb(unsigned cpu, struct ras_events *ras); int ras_mc_event_closedb(unsigned int cpu, struct ras_events *ras); int ras_mc_add_vendor_table(struct ras_events *ras, sqlite3_stmt **stmt, const struct db_table_descriptor *db_tab); int ras_mc_finalize_vendor_table(sqlite3_stmt *stmt); int ras_store_mc_event(struct ras_events *ras, struct ras_mc_event *ev); int ras_store_aer_event(struct ras_events *ras, struct ras_aer_event *ev); int ras_store_mce_record(struct ras_events *ras, struct mce_event *ev); int ras_store_extlog_mem_record(struct ras_events *ras, struct ras_extlog_event *ev); int ras_store_non_standard_record(struct ras_events *ras, struct ras_non_standard_event *ev); int ras_store_arm_record(struct ras_events *ras, struct ras_arm_event *ev); int ras_store_devlink_event(struct ras_events *ras, struct devlink_event *ev); int ras_store_diskerror_event(struct ras_events *ras, struct diskerror_event *ev); int ras_store_mf_event(struct ras_events *ras, struct ras_mf_event *ev); #else static inline int ras_mc_event_opendb(unsigned cpu, struct ras_events *ras) { return 0; }; static inline int ras_mc_event_closedb(unsigned int cpu, struct ras_events *ras) { return 0; }; static inline int ras_store_mc_event(struct ras_events *ras, struct ras_mc_event *ev) { return 0; }; static inline int ras_store_aer_event(struct ras_events *ras, struct ras_aer_event *ev) { return 0; }; static inline int ras_store_mce_record(struct ras_events *ras, struct mce_event *ev) { return 0; }; static inline int ras_store_extlog_mem_record(struct ras_events *ras, struct ras_extlog_event *ev) { return 0; }; static inline int ras_store_non_standard_record(struct ras_events *ras, struct ras_non_standard_event *ev) { return 0; }; static inline int ras_store_arm_record(struct ras_events *ras, struct ras_arm_event *ev) { return 0; }; static inline int ras_store_devlink_event(struct ras_events *ras, struct devlink_event *ev) { return 0; }; static inline int ras_store_diskerror_event(struct ras_events *ras, struct diskerror_event *ev) { return 0; }; static inline int ras_store_mf_event(struct ras_events *ras, struct ras_mf_event *ev) { return 0; }; #endif #endif 07070100000060000081A4000000000000000000000001611B979000003ABD000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-report.c/* * Copyright (c) 2016, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/utsname.h> #include <sys/socket.h> #include <sys/un.h> #include "ras-report.h" static int setup_report_socket(void){ int sockfd = -1; int rc = -1; struct sockaddr_un addr; sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0){ return -1; } memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, ABRT_SOCKET, sizeof(addr.sun_path)); addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; rc = connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); if (rc < 0) { close(sockfd); return -1; } return sockfd; } static int commit_report_basic(int sockfd){ char buf[INPUT_BUFFER_SIZE]; struct utsname un; int rc = -1; if(sockfd < 0){ return rc; } memset(buf, 0, INPUT_BUFFER_SIZE); memset(&un, 0, sizeof(struct utsname)); rc = uname(&un); if(rc < 0){ return rc; } /* * ABRT server protocol */ sprintf(buf, "PUT / HTTP/1.1\r\n\r\n"); rc = write(sockfd, buf, strlen(buf)); if(rc < strlen(buf)){ return -1; } sprintf(buf, "PID=%d", (int)getpid()); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ return -1; } sprintf(buf, "EXECUTABLE=/boot/vmlinuz-%s", un.release); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ return -1; } sprintf(buf, "TYPE=%s", "ras"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ return -1; } return 0; } static int set_mc_event_backtrace(char *buf, struct ras_mc_event *ev){ char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "error_count=%d\n" \ "error_type=%s\n" \ "msg=%s\n" \ "label=%s\n" \ "mc_index=%c\n" \ "top_layer=%c\n" \ "middle_layer=%c\n" \ "lower_layer=%c\n" \ "address=%llu\n" \ "grain=%llu\n" \ "syndrome=%llu\n" \ "driver_detail=%s\n", \ ev->timestamp, \ ev->error_count, \ ev->error_type, \ ev->msg, \ ev->label, \ ev->mc_index, \ ev->top_layer, \ ev->middle_layer, \ ev->lower_layer, \ ev->address, \ ev->grain, \ ev->syndrome, \ ev->driver_detail); strcat(buf, bt_buf); return 0; } static int set_mce_event_backtrace(char *buf, struct mce_event *ev){ char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "bank_name=%s\n" \ "error_msg=%s\n" \ "mcgstatus_msg=%s\n" \ "mcistatus_msg=%s\n" \ "mcastatus_msg=%s\n" \ "user_action=%s\n" \ "mc_location=%s\n" \ "mcgcap=%lu\n" \ "mcgstatus=%lu\n" \ "status=%lu\n" \ "addr=%lu\n" \ "misc=%lu\n" \ "ip=%lu\n" \ "tsc=%lu\n" \ "walltime=%lu\n" \ "cpu=%u\n" \ "cpuid=%u\n" \ "apicid=%u\n" \ "socketid=%u\n" \ "cs=%d\n" \ "bank=%d\n" \ "cpuvendor=%d\n", \ ev->timestamp, \ ev->bank_name, \ ev->error_msg, \ ev->mcgstatus_msg, \ ev->mcistatus_msg, \ ev->mcastatus_msg, \ ev->user_action, \ ev->mc_location, \ ev->mcgcap, \ ev->mcgstatus, \ ev->status, \ ev->addr, \ ev->misc, \ ev->ip, \ ev->tsc, \ ev->walltime, \ ev->cpu, \ ev->cpuid, \ ev->apicid, \ ev->socketid, \ ev->cs, \ ev->bank, \ ev->cpuvendor); strcat(buf, bt_buf); return 0; } static int set_aer_event_backtrace(char *buf, struct ras_aer_event *ev){ char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "error_type=%s\n" \ "dev_name=%s\n" \ "msg=%s\n", \ ev->timestamp, \ ev->error_type, \ ev->dev_name, \ ev->msg); strcat(buf, bt_buf); return 0; } static int set_non_standard_event_backtrace(char *buf, struct ras_non_standard_event *ev){ char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "severity=%s\n" \ "length=%d\n", \ ev->timestamp, \ ev->severity, \ ev->length); strcat(buf, bt_buf); return 0; } static int set_arm_event_backtrace(char *buf, struct ras_arm_event *ev){ char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "error_count=%d\n" \ "affinity=%d\n" \ "mpidr=0x%lx\n" \ "midr=0x%lx\n" \ "running_state=%d\n" \ "psci_state=%d\n", \ ev->timestamp, \ ev->error_count, \ ev->affinity, \ ev->mpidr, \ ev->midr, \ ev->running_state, \ ev->psci_state); strcat(buf, bt_buf); return 0; } static int set_devlink_event_backtrace(char *buf, struct devlink_event *ev){ char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "bus_name=%s\n" \ "dev_name=%s\n" \ "driver_name=%s\n" \ "reporter_name=%s\n" \ "msg=%s\n", \ ev->timestamp, \ ev->bus_name, \ ev->dev_name, \ ev->driver_name, \ ev->reporter_name, \ ev->msg); strcat(buf, bt_buf); return 0; } static int set_diskerror_event_backtrace(char *buf, struct diskerror_event *ev) { char bt_buf[MAX_BACKTRACE_SIZE]; if(!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "dev=%s\n" \ "sector=%llu\n" \ "nr_sector=%u\n" \ "error=%s\n" \ "rwbs=%s\n" \ "cmd=%s\n", \ ev->timestamp, \ ev->dev, \ ev->sector, \ ev->nr_sector, \ ev->error, \ ev->rwbs, \ ev->cmd); strcat(buf, bt_buf); return 0; } static int set_mf_event_backtrace(char *buf, struct ras_mf_event *ev) { char bt_buf[MAX_BACKTRACE_SIZE]; if (!buf || !ev) return -1; sprintf(bt_buf, "BACKTRACE=" \ "timestamp=%s\n" \ "pfn=%s\n" \ "page_type=%s\n" \ "action_result=%s\n", \ ev->timestamp, \ ev->pfn, \ ev->page_type, \ ev->action_result); strcat(buf, bt_buf); return 0; } static int commit_report_backtrace(int sockfd, int type, void *ev){ char buf[MAX_BACKTRACE_SIZE]; char *pbuf = buf; int rc = -1; int buf_len = 0; if(sockfd < 0 || !ev){ return -1; } memset(buf, 0, MAX_BACKTRACE_SIZE); switch(type){ case MC_EVENT: rc = set_mc_event_backtrace(buf, (struct ras_mc_event *)ev); break; case AER_EVENT: rc = set_aer_event_backtrace(buf, (struct ras_aer_event *)ev); break; case MCE_EVENT: rc = set_mce_event_backtrace(buf, (struct mce_event *)ev); break; case NON_STANDARD_EVENT: rc = set_non_standard_event_backtrace(buf, (struct ras_non_standard_event *)ev); break; case ARM_EVENT: rc = set_arm_event_backtrace(buf, (struct ras_arm_event *)ev); break; case DEVLINK_EVENT: rc = set_devlink_event_backtrace(buf, (struct devlink_event *)ev); break; case DISKERROR_EVENT: rc = set_diskerror_event_backtrace(buf, (struct diskerror_event *)ev); break; case MF_EVENT: rc = set_mf_event_backtrace(buf, (struct ras_mf_event *)ev); break; default: return -1; } if(rc < 0){ return -1; } buf_len = strlen(buf); for(;buf_len > INPUT_BUFFER_SIZE - 1; buf_len -= (INPUT_BUFFER_SIZE - 1)){ rc = write(sockfd, pbuf, INPUT_BUFFER_SIZE - 1); if(rc < INPUT_BUFFER_SIZE - 1){ return -1; } pbuf = pbuf + INPUT_BUFFER_SIZE - 1; } rc = write(sockfd, pbuf, buf_len + 1); if(rc < buf_len){ return -1; } return 0; } int ras_report_mc_event(struct ras_events *ras, struct ras_mc_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = -1; int done = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return -1; } rc = commit_report_basic(sockfd); if(rc < 0){ goto mc_fail; } rc = commit_report_backtrace(sockfd, MC_EVENT, ev); if(rc < 0){ goto mc_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-mc"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto mc_fail; } sprintf(buf, "REASON=%s", "EDAC driver report problem"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto mc_fail; } done = 1; mc_fail: if(sockfd > 0){ close(sockfd); } if(done){ return 0; }else{ return -1; } } int ras_report_aer_event(struct ras_events *ras, struct ras_aer_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int done = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return -1; } rc = commit_report_basic(sockfd); if(rc < 0){ goto aer_fail; } rc = commit_report_backtrace(sockfd, AER_EVENT, ev); if(rc < 0){ goto aer_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-aer"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto aer_fail; } sprintf(buf, "REASON=%s", "PCIe AER driver report problem"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto aer_fail; } done = 1; aer_fail: if(sockfd > 0){ close(sockfd); } if(done){ return 0; }else{ return -1; } } int ras_report_non_standard_event(struct ras_events *ras, struct ras_non_standard_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return rc; } rc = commit_report_basic(sockfd); if(rc < 0){ goto non_standard_fail; } rc = commit_report_backtrace(sockfd, NON_STANDARD_EVENT, ev); if(rc < 0){ goto non_standard_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-non-standard"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto non_standard_fail; } sprintf(buf, "REASON=%s", "Unknown CPER section problem"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto non_standard_fail; } rc = 0; non_standard_fail: if(sockfd > 0){ close(sockfd); } return rc; } int ras_report_arm_event(struct ras_events *ras, struct ras_arm_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return rc; } rc = commit_report_basic(sockfd); if(rc < 0){ goto arm_fail; } rc = commit_report_backtrace(sockfd, ARM_EVENT, ev); if(rc < 0){ goto arm_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-arm"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto arm_fail; } sprintf(buf, "REASON=%s", "ARM CPU report problem"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto arm_fail; } rc = 0; arm_fail: if(sockfd > 0){ close(sockfd); } return rc; } int ras_report_mce_event(struct ras_events *ras, struct mce_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int done = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return -1; } rc = commit_report_basic(sockfd); if(rc < 0){ goto mce_fail; } rc = commit_report_backtrace(sockfd, MCE_EVENT, ev); if(rc < 0){ goto mce_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-mce"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto mce_fail; } sprintf(buf, "REASON=%s", "Machine Check driver report problem"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto mce_fail; } done = 1; mce_fail: if(sockfd > 0){ close(sockfd); } if(done){ return 0; }else{ return -1; } } int ras_report_devlink_event(struct ras_events *ras, struct devlink_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int done = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return -1; } rc = commit_report_basic(sockfd); if(rc < 0){ goto devlink_fail; } rc = commit_report_backtrace(sockfd, DEVLINK_EVENT, ev); if(rc < 0){ goto devlink_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-devlink"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto devlink_fail; } sprintf(buf, "REASON=%s", "devlink health report problem"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto devlink_fail; } done = 1; devlink_fail: if(sockfd > 0){ close(sockfd); } if(done){ return 0; }else{ return -1; } } int ras_report_diskerror_event(struct ras_events *ras, struct diskerror_event *ev){ char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int done = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if(sockfd < 0){ return -1; } rc = commit_report_basic(sockfd); if(rc < 0){ goto diskerror_fail; } rc = commit_report_backtrace(sockfd, DISKERROR_EVENT, ev); if(rc < 0){ goto diskerror_fail; } sprintf(buf, "ANALYZER=%s", "rasdaemon-diskerror"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto diskerror_fail; } sprintf(buf, "REASON=%s", "disk I/O error"); rc = write(sockfd, buf, strlen(buf) + 1); if(rc < strlen(buf) + 1){ goto diskerror_fail; } done = 1; diskerror_fail: if(sockfd > 0){ close(sockfd); } if(done){ return 0; }else{ return -1; } } int ras_report_mf_event(struct ras_events *ras, struct ras_mf_event *ev) { char buf[MAX_MESSAGE_SIZE]; int sockfd = 0; int done = 0; int rc = -1; memset(buf, 0, sizeof(buf)); sockfd = setup_report_socket(); if (sockfd < 0) return -1; rc = commit_report_basic(sockfd); if (rc < 0) goto mf_fail; rc = commit_report_backtrace(sockfd, MF_EVENT, ev); if (rc < 0) goto mf_fail; sprintf(buf, "ANALYZER=%s", "rasdaemon-memory_failure"); rc = write(sockfd, buf, strlen(buf) + 1); if (rc < strlen(buf) + 1) goto mf_fail; sprintf(buf, "REASON=%s", "memory failure problem"); rc = write(sockfd, buf, strlen(buf) + 1); if (rc < strlen(buf) + 1) goto mf_fail; done = 1; mf_fail: if (sockfd > 0) close(sockfd); if (done) return 0; else return -1; } 07070100000061000081A4000000000000000000000001611B979000000A3B000000000000000000000000000000000000002C00000000rasdaemon-0.6.7.18.git+7ccf12f/ras-report.h/* * Copyright (c) 2016, The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ #ifndef __RAS_REPORT_H #define __RAS_REPORT_H #include "ras-record.h" #include "ras-events.h" #include "ras-mc-handler.h" #include "ras-mce-handler.h" #include "ras-aer-handler.h" /* Maximal length of backtrace. */ #define MAX_BACKTRACE_SIZE (1024*1024) /* Amount of data received from one client for a message before reporting error. */ #define MAX_MESSAGE_SIZE (4*MAX_BACKTRACE_SIZE) /* Maximal number of characters read from socket at once. */ #define INPUT_BUFFER_SIZE (8*1024) /* ABRT socket file */ #define ABRT_SOCKET "/var/run/abrt/abrt.socket" #ifdef HAVE_ABRT_REPORT int ras_report_mc_event(struct ras_events *ras, struct ras_mc_event *ev); int ras_report_aer_event(struct ras_events *ras, struct ras_aer_event *ev); int ras_report_mce_event(struct ras_events *ras, struct mce_event *ev); int ras_report_non_standard_event(struct ras_events *ras, struct ras_non_standard_event *ev); int ras_report_arm_event(struct ras_events *ras, struct ras_arm_event *ev); int ras_report_devlink_event(struct ras_events *ras, struct devlink_event *ev); int ras_report_diskerror_event(struct ras_events *ras, struct diskerror_event *ev); int ras_report_mf_event(struct ras_events *ras, struct ras_mf_event *ev); #else static inline int ras_report_mc_event(struct ras_events *ras, struct ras_mc_event *ev) { return 0; }; static inline int ras_report_aer_event(struct ras_events *ras, struct ras_aer_event *ev) { return 0; }; static inline int ras_report_mce_event(struct ras_events *ras, struct mce_event *ev) { return 0; }; static inline int ras_report_non_standard_event(struct ras_events *ras, struct ras_non_standard_event *ev) { return 0; }; static inline int ras_report_arm_event(struct ras_events *ras, struct ras_arm_event *ev) { return 0; }; static inline int ras_report_devlink_event(struct ras_events *ras, struct devlink_event *ev) { return 0; }; static inline int ras_report_diskerror_event(struct ras_events *ras, struct diskerror_event *ev) { return 0; }; static inline int ras_report_mf_event(struct ras_events *ras, struct ras_mf_event *ev) { return 0; }; #endif #endif 07070100000062000081A4000000000000000000000001611B979000000AE4000000000000000000000000000000000000002B00000000rasdaemon-0.6.7.18.git+7ccf12f/rasdaemon.c/* * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <argp.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "ras-record.h" #include "ras-logger.h" #include "ras-events.h" /* * Arguments(argp) handling logic and main */ #define TOOL_NAME "rasdaemon" #define TOOL_DESCRIPTION "RAS daemon to log the RAS events." #define ARGS_DOC "<options>" const char *argp_program_version = TOOL_NAME " " VERSION; const char *argp_program_bug_address = "Mauro Carvalho Chehab <mchehab@kernel.org>"; struct arguments { int record_events; int enable_ras; int foreground; }; static error_t parse_opt(int k, char *arg, struct argp_state *state) { struct arguments *args = state->input; switch (k) { case 'e': args->enable_ras++; break; case 'd': args->enable_ras--; break; #ifdef HAVE_SQLITE3 case 'r': args->record_events++; break; #endif case 'f': args->foreground++; break; default: return ARGP_ERR_UNKNOWN; } return 0; } long user_hz; int main(int argc, char *argv[]) { struct arguments args; int idx = -1; const struct argp_option options[] = { {"enable", 'e', 0, 0, "enable RAS events and exit", 0}, {"disable", 'd', 0, 0, "disable RAS events and exit", 0}, #ifdef HAVE_SQLITE3 {"record", 'r', 0, 0, "record events via sqlite3", 0}, #endif {"foreground", 'f', 0, 0, "run foreground, not daemonize"}, { 0, 0, 0, 0, 0, 0 } }; const struct argp argp = { .options = options, .parser = parse_opt, .doc = TOOL_DESCRIPTION, .args_doc = ARGS_DOC, }; memset (&args, 0, sizeof(args)); user_hz = sysconf(_SC_CLK_TCK); argp_parse(&argp, argc, argv, 0, &idx, &args); if (idx < 0) { argp_help(&argp, stderr, ARGP_HELP_STD_HELP, TOOL_NAME); return -1; } if (args.enable_ras) { int enable; enable = (args.enable_ras > 0) ? 1 : 0; toggle_ras_mc_event(enable); return 0; } openlog(TOOL_NAME, 0, LOG_DAEMON); if (!args.foreground) if (daemon(0,0)) exit(EXIT_FAILURE); handle_ras_events(args.record_events); return 0; } 07070100000063000081A4000000000000000000000001611B979000002149000000000000000000000000000000000000002800000000rasdaemon-0.6.7.18.git+7ccf12f/rbtree.c/* Red Black Trees (C) 1999 Andrea Arcangeli <andrea@suse.de> (C) 2002 David Woodhouse <dwmw2@infradead.org> Taken from the Linux 2.6.30 source with some minor modificatons. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA linux/lib/rbtree.c */ #include "rbtree.h" static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) { struct rb_node *right = node->rb_right; struct rb_node *parent = rb_parent(node); if ((node->rb_right = right->rb_left)) rb_set_parent(right->rb_left, node); right->rb_left = node; rb_set_parent(right, parent); if (parent) { if (node == parent->rb_left) parent->rb_left = right; else parent->rb_right = right; } else root->rb_node = right; rb_set_parent(node, right); } static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) { struct rb_node *left = node->rb_left; struct rb_node *parent = rb_parent(node); if ((node->rb_left = left->rb_right)) rb_set_parent(left->rb_right, node); left->rb_right = node; rb_set_parent(left, parent); if (parent) { if (node == parent->rb_right) parent->rb_right = left; else parent->rb_left = left; } else root->rb_node = left; rb_set_parent(node, left); } void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent; while ((parent = rb_parent(node)) && rb_is_red(parent)) { gparent = rb_parent(parent); if (parent == gparent->rb_left) { { register struct rb_node *uncle = gparent->rb_right; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_right == node) { struct rb_node *tmp; __rb_rotate_left(parent, root); tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(gparent); __rb_rotate_right(gparent, root); } else { { struct rb_node *uncle = gparent->rb_left; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_left == node) { struct rb_node *tmp; __rb_rotate_right(parent, root); tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(gparent); __rb_rotate_left(gparent, root); } } rb_set_black(root->rb_node); } static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root) { struct rb_node *other; while ((!node || rb_is_black(node)) && node != root->rb_node) { if (parent->rb_left == node) { other = parent->rb_right; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_left(parent, root); other = parent->rb_right; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_right || rb_is_black(other->rb_right)) { rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; } } else { other = parent->rb_left; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_right(parent, root); other = parent->rb_left; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_left || rb_is_black(other->rb_left)) { rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; } } } if (node) rb_set_black(node); } void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *child, *parent; int color; if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; else { struct rb_node *old = node, *left; node = node->rb_right; while ((left = node->rb_left) != NULL) node = left; child = node->rb_right; parent = rb_parent(node); color = rb_color(node); if (child) rb_set_parent(child, parent); if (parent == old) { parent->rb_right = child; parent = node; } else parent->rb_left = child; node->rb_parent_color = old->rb_parent_color; node->rb_right = old->rb_right; node->rb_left = old->rb_left; if (rb_parent(old)) { if (rb_parent(old)->rb_left == old) rb_parent(old)->rb_left = node; else rb_parent(old)->rb_right = node; } else root->rb_node = node; rb_set_parent(old->rb_left, node); if (old->rb_right) rb_set_parent(old->rb_right, node); goto color; } parent = rb_parent(node); color = rb_color(node); if (child) rb_set_parent(child, parent); if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else root->rb_node = child; color: if (color == RB_BLACK) __rb_erase_color(child, parent, root); } /* * This function returns the first node (in sort order) of the tree. */ struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_left) n = n->rb_left; return n; } struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_right) n = n->rb_right; return n; } struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent; if (rb_parent(node) == node) return NULL; /* If we have a right-hand child, go down and then left as far as we can. */ if (node->rb_right) { node = node->rb_right; while (node->rb_left) node=node->rb_left; return (struct rb_node *)node; } /* No right-hand children. Everything down and left is smaller than us, so any 'next' node must be in the general direction of our parent. Go up the tree; any time the ancestor is a right-hand child of its parent, keep going up. First time it's a left-hand child of its parent, said parent is our 'next' node. */ while ((parent = rb_parent(node)) && node == parent->rb_right) node = parent; return parent; } struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent; if (rb_parent(node) == node) return NULL; /* If we have a left-hand child, go down and then right as far as we can. */ if (node->rb_left) { node = node->rb_left; while (node->rb_right) node=node->rb_right; return (struct rb_node *)node; } /* No left-hand children. Go up till we find an ancestor which is a right-hand child of its parent */ while ((parent = rb_parent(node)) && node == parent->rb_left) node = parent; return parent; } void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = rb_parent(victim); /* Set the surrounding nodes to point to the replacement */ if (parent) { if (victim == parent->rb_left) parent->rb_left = new; else parent->rb_right = new; } else { root->rb_node = new; } if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) rb_set_parent(victim->rb_right, new); /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; } 07070100000064000081A4000000000000000000000001611B9790000013EB000000000000000000000000000000000000002800000000rasdaemon-0.6.7.18.git+7ccf12f/rbtree.h/* Red Black Trees (C) 1999 Andrea Arcangeli <andrea@suse.de> Taken from the Linux 2.6.30 source. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA linux/include/linux/rbtree.h To use rbtrees you'll have to implement your own insert and search cores. This will avoid us to use callbacks and to drop drammatically performances. I know it's not the cleaner way, but in C (not in C++) to get performances and genericity... Some example of insert and search follows here. The search is a plain normal search over an ordered tree. The insert instead must be implemented int two steps: as first thing the code must insert the element in order as a red leaf in the tree, then the support library function rb_insert_color() must be called. Such function will do the not trivial work to rebalance the rbtree if necessary. ----------------------------------------------------------------------- static inline struct page * rb_search_page_cache(struct inode * inode, unsigned long offset) { struct rb_node * n = inode->i_rb_page_cache.rb_node; struct page * page; while (n) { page = rb_entry(n, struct page, rb_page_cache); if (offset < page->offset) n = n->rb_left; else if (offset > page->offset) n = n->rb_right; else return page; } return NULL; } static inline struct page * __rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct rb_node ** p = &inode->i_rb_page_cache.rb_node; struct rb_node * parent = NULL; struct page * page; while (*p) { parent = *p; page = rb_entry(parent, struct page, rb_page_cache); if (offset < page->offset) p = &(*p)->rb_left; else if (offset > page->offset) p = &(*p)->rb_right; else return page; } rb_link_node(node, parent, p); return NULL; } static inline struct page * rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct page * ret; if ((ret = __rb_insert_page_cache(inode, offset, node))) goto out; rb_insert_color(node, &inode->i_rb_page_cache); out: return ret; } ----------------------------------------------------------------------- */ #ifndef _LINUX_RBTREE_H #define _LINUX_RBTREE_H #include <stddef.h> #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct rb_node { unsigned long rb_parent_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); /* The alignment might seem pointless, but allegedly CRIS needs it */ struct rb_root { struct rb_node *rb_node; }; #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) #define rb_color(r) ((r)->rb_parent_color & 1) #define rb_is_red(r) (!rb_color(r)) #define rb_is_black(r) rb_color(r) #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) { rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; } static inline void rb_set_color(struct rb_node *rb, int color) { rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; } #define RB_ROOT (struct rb_root) { NULL, } #define rb_entry(ptr, type, member) container_of(ptr, type, member) #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) #define RB_EMPTY_NODE(node) (rb_parent(node) == node) #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(const struct rb_node *); extern struct rb_node *rb_prev(const struct rb_node *); extern struct rb_node *rb_first(const struct rb_root *); extern struct rb_node *rb_last(const struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { node->rb_parent_color = (unsigned long )parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } #endif /* _LINUX_RBTREE_H */ 07070100000065000041ED000000000000000000000002611B979000000000000000000000000000000000000000000000002400000000rasdaemon-0.6.7.18.git+7ccf12f/util07070100000066000081A4000000000000000000000001611B97900000000C000000000000000000000000000000000000002F00000000rasdaemon-0.6.7.18.git+7ccf12f/util/.gitignoreras-mc-ctl 07070100000067000081A4000000000000000000000001611B979000000022000000000000000000000000000000000000003000000000rasdaemon-0.6.7.18.git+7ccf12f/util/Makefile.amdist_sbin_SCRIPTS = \ ras-mc-ctl 07070100000068000081ED000000000000000000000001611B97900000DF05000000000000000000000000000000000000003200000000rasdaemon-0.6.7.18.git+7ccf12f/util/ras-mc-ctl.in#!/usr/bin/perl -w #****************************************************************************** # Copyright (c) 2013 Mauro Carvalho Chehab <mchehab+redhat@kernel.org> # # This tool is a modification of the edac-ctl, written as part of the # edac-utils: # Copyright (C) 2003-2006 The Regents of the University of California. # Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). # Written by Mark Grondona <mgrondona@llnl.gov> # UCRL-CODE-230739. # # This version uses the new EDAC v 3.0.0 and upper API, with adds proper # representation for the memory controllers found on Intel designs after # 2002. It requires Linux Kernel 3.5 or upper to work. # # This 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 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. #****************************************************************************/ use strict; use File::Basename; use File::Find; use Getopt::Long; use POSIX; my $dbname = "@RASSTATEDIR@/@RAS_DB_FNAME@"; my $prefix = "@prefix@"; my $sysconfdir = "@sysconfdir@"; my $dmidecode = find_prog ("dmidecode"); my $modprobe = find_prog ("modprobe") or exit (1); my $has_aer = 0; my $has_arm = 0; my $has_devlink = 0; my $has_disk_errors = 0; my $has_extlog = 0; my $has_mem_failure = 0; my $has_mce = 0; @WITH_AER_TRUE@$has_aer = 1; @WITH_ARM_TRUE@$has_arm = 1; @WITH_DEVLINK_TRUE@$has_devlink = 1; @WITH_DISKERROR_TRUE@$has_disk_errors = 1; @WITH_EXTLOG_TRUE@$has_extlog = 1; @WITH_MEMORY_FAILURE_TRUE@$has_mem_failure = 1; @WITH_MCE_TRUE@$has_mce = 1; my %conf = (); my %bus = (); my %dimm_size = (); my %dimm_node = (); my %dimm_label_file = (); my %dimm_location = (); my %csrow_size = (); my %rank_size = (); my %csrow_ranks = (); my %dimm_ce_count = (); my %dimm_ue_count = (); my @layers; my @max_pos; my @max_csrow; my $item_size; my $prog = basename $0; $conf{labeldb} = "$sysconfdir/ras/dimm_labels.db"; $conf{labeldir} = "$sysconfdir/ras/dimm_labels.d"; $conf{mbconfig} = "$sysconfdir/ras/mainboard"; my $status = 0; my $usage = <<EOF; Usage: $prog [OPTIONS...] --quiet Quiet operation. --mainboard Print mainboard vendor and model for this hardware. --status Print status of EDAC drivers. --print-labels Print Motherboard DIMM labels to stdout. --guess-labels Print DMI labels, when bank locator is available. --register-labels Load Motherboard DIMM labels into EDAC driver. --delay=N Delay N seconds before writing DIMM labels. --labeldb=DB Load label database from file DB. --layout Display the memory layout. --summary Presents a summary of the logged errors. --errors Shows the errors stored at the error database. --error-count Shows the corrected and uncorrected error counts using sysfs. --vendor-errors-summary <platform-id> Presents a summary of the vendor-specific logged errors. --vendor-errors <platform-id> Shows the vendor-specific errors stored in the error database. --vendor-platforms Shows the supported platforms with platform-ids for the vendor-specific errors. --help This help message. EOF parse_cmdline(); if ( $conf{opt}{mainboard} || $conf{opt}{print_labels} || $conf{opt}{register_labels} || $conf{opt}{display_memory_layout} || $conf{opt}{guess_dimm_label} || $conf{opt}{error_count}) { get_mainboard_info(); if ($conf{opt}{mainboard} eq "report") { print "$prog: mainboard: ", "$conf{mainboard}{vendor} model $conf{mainboard}{model}\n"; } if ($conf{opt}{print_labels}) { print_dimm_labels (); } if ($conf{opt}{register_labels}) { register_dimm_labels (); } if ($conf{opt}{display_memory_layout}) { display_memory_layout (); } if ($conf{opt}{guess_dimm_label}) { guess_dimm_label (); } if ($conf{opt}{error_count}) { display_error_count (); } } if ($conf{opt}{status}) { $status = print_status (); exit ($status ? 0 : 1); } if ($conf{opt}{summary}) { summary (); } if ($conf{opt}{errors}) { errors (); } if ($conf{opt}{vendor_errors_summary}) { vendor_errors_summary (); } if ($conf{opt}{vendor_errors}) { vendor_errors (); } if ($conf{opt}{vendor_platforms}) { vendor_platforms (); } exit (0); sub parse_cmdline { $conf{opt}{mainboard} = ''; $conf{opt}{print_labels} = 0; $conf{opt}{register_labels} = 0; $conf{opt}{status} = 0; $conf{opt}{quiet} = 0; $conf{opt}{delay} = 0; $conf{opt}{display_memory_layout} = 0; $conf{opt}{guess_dimm_label} = 0; $conf{opt}{summary} = 0; $conf{opt}{errors} = 0; $conf{opt}{error_count} = 0; $conf{opt}{vendor_errors_summary} = 0; $conf{opt}{vendor_errors} = 0; $conf{opt}{vendor_platforms} = 0; my $rref = \$conf{opt}{report}; my $mref = \$conf{opt}{mainboard}; Getopt::Long::Configure ("bundling"); my $rc = GetOptions ("mainboard:s" => sub { $$mref = $_[1]||"report" }, "help" => sub {usage (0)}, "quiet" => \$conf{opt}{quiet}, "print-labels" => \$conf{opt}{print_labels}, "guess-labels" => \$conf{opt}{guess_dimm_label}, "register-labels" => \$conf{opt}{register_labels}, "delay:s" => \$conf{opt}{delay}, "labeldb=s" => \$conf{labeldb}, "status" => \$conf{opt}{status}, "layout" => \$conf{opt}{display_memory_layout}, "summary" => \$conf{opt}{summary}, "errors" => \$conf{opt}{errors}, "error-count" => \$conf{opt}{error_count}, "vendor-errors-summary" => \$conf{opt}{vendor_errors_summary}, "vendor-errors" => \$conf{opt}{vendor_errors}, "vendor-platforms" => \$conf{opt}{vendor_platforms}, ); usage(1) if !$rc; usage (0) if !grep $conf{opt}{$_}, keys %{$conf{opt}}; if ($conf{opt}{delay} && !$conf{opt}{register_labels}) { log_error ("Only use --delay with --register-labels\n"); exit (1); } } sub usage { my ($rc) = @_; print "$usage\n"; exit ($rc); } sub run_cmd { my @args = @_; system ("@args"); return ($?>>8); } sub print_status { my $status = 0; open (MODULES, "/proc/modules") or die "Unable to open /proc/modules: $!\n"; while (<MODULES>) { $status = 1 if /_edac/; } print "$prog: drivers ", ($status ? "are" : "not"), " loaded.\n" unless $conf{opt}{quiet}; return ($status); } sub parse_dimm_nodes { my $file = $File::Find::name; if (($file =~ /max_location$/)) { open IN, $file; my $location = <IN>; close IN; my @temp = split(/ /, $location); $layers[0] = "mc"; if (m,/mc/mc(\d+),) { $max_pos[0] = $1 if (!exists($max_pos[0]) || $1 > $max_pos[0]); } else { $max_pos[0] = 0 if (!exists($max_pos[0])); } for (my $i = 0; $i < scalar(@temp); $i += 2) { $layers[$i / 2 + 1] = $temp[$i]; $max_pos[$i / 2 + 1] = $temp[$i + 1]; } return; } if ($file =~ /size_mb$/) { my $mc = $file; $mc =~ s,.*mc(\d+).*,$1,; my $csrow = $file; $csrow =~ s,.*csrow(\d+).*,$1,; open IN, $file; my $size = <IN>; close IN; my $str_loc = join(':', $mc, $csrow); $csrow_size{$str_loc} = $size; return; } if ($file =~ /location$/) { my $mc = $file; $mc =~ s,.*mc(\d+).*,$1,; my $dimm = $file; $dimm =~ s,.*(rank|dimm)(\d+).*,$2,; open IN, $file; my $location = <IN>; close IN; my @pos; # Get the name of the hierarchy labels if (!@layers) { my @temp = split(/ /, $location); $max_pos[0] = 0; $layers[0] = "mc"; for (my $i = 0; $i < scalar(@temp); $i += 2) { $layers[$i / 2 + 1] = $temp[$i]; $max_pos[$i / 2 + 1] = 0; } } my @temp = split(/ /, $location); for (my $i = 1; $i < scalar(@temp); $i += 2) { $pos[$i / 2] = $temp[$i]; if ($pos[$i / 2] > $max_pos[$i / 2 + 1]) { $max_pos[$i / 2 + 1] = $pos[$i / 2]; } } if ($mc > $max_pos[0]) { $max_pos[0] = $mc; } # Get DIMM size $file =~ s/dimm_location/size/; open IN, $file; my $size = <IN>; close IN; my $str_loc = join(':', $mc, @pos); $dimm_size{$str_loc} = $size; $dimm_node{$str_loc} = $dimm; $file =~ s/size/dimm_label/; $dimm_label_file{$str_loc} = $file; $dimm_location{$str_loc} = $location; my $count; $file =~s/dimm_label/dimm_ce_count/; if (-e $file) { open IN, $file; chomp($count = <IN>); close IN; } else { log_error ("dimm_ce_count not found in sysfs. Old kernel?\n"); exit -1; } $dimm_ce_count{$str_loc} = $count; $file =~s/dimm_ce_count/dimm_ue_count/; if (-e $file) { open IN, $file; chomp($count = <IN>); close IN; } else { log_error ("dimm_ue_count not found in sysfs. Old kernel?\n"); exit -1; } $dimm_ue_count{$str_loc} = $count; return; } } sub guess_product { my $pvendor = undef; my $pname = undef; if (open (VENDOR, "/sys/class/dmi/id/product_vendor")) { $pvendor = <VENDOR>; close VENDOR; chomp($pvendor); } if (open (NAME, "/sys/class/dmi/id/product_name")) { $pname = <NAME>; close NAME; chomp($pname); } return ($pvendor, $pname); } sub get_mainboard_info { my ($vendor, $model); my ($pvendor, $pname); if ($conf{opt}{mainboard} && $conf{opt}{mainboard} ne "report") { ($vendor, $model) = split (/[: ]/, $conf{opt}{mainboard}, 2); } if (!$vendor || !$model) { ($vendor, $model) = guess_vendor_model (); } $conf{mainboard}{vendor} = $vendor; $conf{mainboard}{model} = $model; ($pvendor, $pname) = guess_product (); # since product vendor is rare, use mainboard's vendor if ($pvendor) { $conf{mainboard}{product_vendor} = $pvendor; } else { $conf{mainboard}{product_vendor} = $vendor; } $conf{mainboard}{product_name} = $pname if $pname; } sub guess_vendor_model_dmidecode { my ($vendor, $model); my ($system_vendor, $system_model); my $line = 0; $< == 0 || die "Must be root to run dmidecode\n"; open (DMI, "$dmidecode |") or die "failed to run $dmidecode: $!\n"; $vendor = $model = ""; LINE: while (<DMI>) { $line++; /^(\s*)(board|base board|system) information/i || next LINE; my $indent = $1; my $type = $2; while ( <DMI> ) { /^(\s*)/; $1 lt $indent && last LINE; $indent = $1; if ($type eq "system") { /(?:manufacturer|vendor):\s*(.*\S)\s*/i && ( $system_vendor = $1 ); /product(?: name)?:\s*(.*\S)\s*/i && ( $system_model = $1 ); } else { /(?:manufacturer|vendor):\s*(.*\S)\s*/i && ( $vendor = $1 ); /product(?: name)?:\s*(.*\S)\s*/i && ( $model = $1 ); } last LINE if ($vendor && $model); } } close (DMI); $vendor = $system_vendor if ($vendor eq ""); $model = $system_model if ($model eq ""); return ($vendor, $model); } sub guess_vendor_model_sysfs { # # Try to look up DMI information in sysfs # open (VENDOR, "/sys/class/dmi/id/board_vendor") or return undef; open (MODEL, "/sys/class/dmi/id/board_name") or return undef; my ($vendor, $model) = (<VENDOR>, <MODEL>); close (VENDOR); close (MODEL); return undef unless ($vendor && $model); chomp ($vendor, $model); return ($vendor, $model); } sub parse_mainboard_config { my ($file) = @_; my %hash = (); my $line = 0; open (CFG, "$file") or die "Failed to read mainboard config: $file: $!\n"; while (<CFG>) { $line++; chomp; # remove newline s/^((?:[^'"#]*(?:(['"])[^\2]*\2)*)*)#.*/$1/; # remove comments s/^\s+//; # remove leading space s/\s+$//; # remove trailing space next unless length; # skip blank lines if (my ($key, $val) = /^\s*([-\w]+)\s*=\s*(.*)/) { $hash{$key}{val} = $val; $hash{$key}{line} = $line; next; } return undef; } close (CFG) or &log_error ("close $file: $!\n"); return \%hash; } sub guess_vendor_model { my ($vendor, $model); # # If mainboard config file exists then parse it # to get the vendor and model information. # if (-f $conf{mbconfig} ) { my $cfg = &parse_mainboard_config ($conf{mbconfig}); # If mainboard config file specified a script, then try to # run the specified script or executable: # if ($cfg->{"script"}) { $cfg = &parse_mainboard_config ("$cfg->{script}{val} |"); die "Failed to run mainboard script\n" if (!$cfg); } return ($cfg->{vendor}{val}, $cfg->{model}{val}); } ($vendor, $model) = &guess_vendor_model_sysfs (); return ($vendor, $model) if ($vendor && $model); return (&guess_vendor_model_dmidecode ()); } sub guess_dimm_label { open (DMI, "$dmidecode |") or die "failed to run $dmidecode: $!\n"; LINE: while (<DMI>) { /^(\s*)memory device$/i || next LINE; my ($dimm_label, $dimm_addr); while (<DMI>) { if (/^\s*(locator|bank locator)/i) { my $indent = $1; $indent =~ tr/A-Z/a-z/; if ($indent eq "locator") { /(?:locator):\s*(.*\S)\s*/i && ( $dimm_label = $1 ); } if ($indent eq "bank locator") { /(?:bank locator):\s*(.*\S)\s*/i && ( $dimm_addr = $1 ); } } if ($dimm_label && $dimm_addr) { printf "memory stick '%s' is located at '%s'\n", $dimm_label, $dimm_addr; next LINE; } next LINE if (/^\s*\n/); } } close (DMI); } sub parse_dimm_labels_file { my ($lh, $num_layers, $lh_prod, $num_layers_prod, $file) = (@_); my $line = -1; my $vendor = ""; my @models = (); my @products = (); my $num; open (LABELS, "$file") or die "Unable to open label database: $file: $!\n"; while (<LABELS>) { $line++; next if /^#/; chomp; s/^\s+//; s/\s+$//; next unless length; if (/vendor\s*:\s*(.*\S)\s*/i) { $vendor = lc $1; @models = (); @products = (); $num = 0; next; } if (/(model|board)\s*:\s*(.*)$/i) { !$vendor && die "$file: line $line: MB model without vendor\n"; @models = grep { s/\s*(.*)\s*$/$1/ } split(/[,;]+/, $2); @products = (); $num = 0; next; } if (/(product)\s*:\s*(.*)$/i) { !$vendor && die "$file: line $line: product without vendor\n"; @models = (); @products = grep { s/\s*(.*)\s*$/$1/ } split(/[,;]+/, $2); $num = 0; next; } # Allow multiple labels to be specified on a single line, # separated by ; for my $str (split /;/) { $str =~ s/^\s*(.*)\s*$/$1/; next unless (my ($label, $info) = ($str =~ /^(.*)\s*:\s*(.*)$/i)); unless ($info =~ /\d+(?:[\.\:]\d+)*/) { log_error ("$file: $line: Invalid syntax, ignoring: \"$_\"\n"); next; } for my $target (split (/[, ]+/, $info)) { my $n; my ($mc, $top, $mid, $low, $extra) = ($target =~ /(\d+)(?:[\.\:](\d+)){0,1}(?:[\.\:](\d+)){0,1}(?:[\.\:](\d+)){0,1}(?:[\.\:](\d+)){0,1}/); if (defined($extra)) { die ("Error: Only up to 3 layers are currently supported on label db \"$file\"\n"); return; } elsif (!defined($top)) { die ("Error: The label db \"$file\" is defining a zero-layers machine\n"); return; } else { $n = 3; if (!defined($low)) { $low = 0; $n--; } if (!defined($mid)) { $mid = 0; $n--; } map { $lh->{$vendor}{lc $_}{$mc}{$top}{$mid}{$low} = $label } @models; map { $lh_prod->{$vendor}{lc $_}{$mc}{$top}{$mid}{$low} = $label } @products; } if (!$num) { $num = $n; map { $num_layers->{$vendor}{lc $_} = $num } @models; map { $num_layers_prod->{$vendor}{lc $_} = $num } @products; } elsif ($num != $n) { die ("Error: Inconsistent number of layers at label db \"$file\"\n"); } } } } close (LABELS) or die "Error from label db \"$file\" : $!\n"; } sub parse_dimm_labels { my %labels = (); my %num_layers = (); my %labels_prod = (); my %num_layers_prod = (); # # Accrue all DIMM labels from the labels.db file, as # well as any files under the labels dir # for my $file ($conf{labeldb}, <$conf{labeldir}/*>) { next unless -r $file; parse_dimm_labels_file (\%labels, \%num_layers, \%labels_prod, \%num_layers_prod, $file); } return (\%labels, \%num_layers, \%labels_prod, \%num_layers_prod); } sub read_dimm_label { my ($num_layers, $mc, $top, $mid, $low) = @_; my $sysfs = "/sys/devices/system/edac/mc"; my $pos; $pos = "$mc:$top:$mid:$low" if ($num_layers == 3); $pos = "$mc:$top:$mid" if ($num_layers == 2); $pos = "$mc:$top" if ($num_layers == 1); if (!defined($dimm_node{$pos})) { my $label = "$pos missing"; $pos = ""; return ($label, $pos); } my $dimm = $dimm_node{$pos}; my $dimm_label_file = $dimm_label_file{$pos}; my $location = $dimm_location{$pos}; return ("label missing", "$pos missing") unless -f $dimm_label_file; if (!open (LABEL, "$dimm_label_file")) { warn "Failed to open $dimm_label_file: $!\n"; return ("Error"); } chomp (my $label = <LABEL> || ""); close (LABEL); $pos = "mc$mc $location"; return ($label, $pos); } sub get_dimm_label_node { my ($num_layers, $mc, $top, $mid, $low) = @_; my $sysfs = "/sys/devices/system/edac/mc"; my $pos = "$mc:$top:$mid:$low"; $pos = "$mc:$top:$mid:$low" if ($num_layers == 3); $pos = "$mc:$top:$mid" if ($num_layers == 2); $pos = "$mc:$top" if ($num_layers == 1); return "" if (!defined($dimm_node{$pos})); return "$dimm_label_file{$pos}"; } sub _print_dimm_labels { my ($lref, $num_layers, $vendor, $model, $fh, $format) = @_; for my $mc (sort keys %{$$lref{$vendor}{$model}}) { for my $top (sort keys %{$$lref{$vendor}{$model}{$mc}}) { for my $mid (sort keys %{$$lref{$vendor}{$model}{$mc}{$top}}) { for my $low (sort keys %{$$lref{$vendor}{$model}{$mc}{$top}{$mid}}) { my $label = $$lref{$vendor}{$model}{$mc}{$top}{$mid}{$low}; my ($rlabel,$loc) = read_dimm_label ($$num_layers{$vendor}{$model}, $mc, $top, $mid, $low); printf $fh $format, $loc, $label, $rlabel; } } } } print $fh "\n"; } sub print_dimm_labels { my $fh = shift || *STDOUT; my ($lref, $num_layers, $lref_prod, $num_layers_prod) = parse_dimm_labels (); my $vendor = lc $conf{mainboard}{vendor}; my $model = lc $conf{mainboard}{model}; my $pvendor = lc $conf{mainboard}{product_vendor}; my $pname = lc $conf{mainboard}{product_name}; my $format = "%-35s %-20s %-20s\n"; if (!exists $$lref{$vendor}{$model} && !exists $$lref_prod{$pvendor}{$pname}) { log_error ("No dimm labels for $conf{mainboard}{vendor} " . "model $conf{mainboard}{model}\n"); return; } my $sysfs_dir = "/sys/devices/system/edac/mc"; find({wanted => \&parse_dimm_nodes, no_chdir => 1}, $sysfs_dir); printf $fh $format, "LOCATION", "CONFIGURED LABEL", "SYSFS CONTENTS"; if (exists $$lref{$vendor}{$model}) { _print_dimm_labels($lref, $num_layers, $vendor, $model, $fh, $format); } elsif (exists $$lref_prod{$pvendor}{$pname}) { _print_dimm_labels($lref_prod, $num_layers_prod, $pvendor, $pname, $fh, $format); } } sub write_dimm_labels { my ($lref, $num_layers, $vendor, $model) = @_; for my $mc (sort keys %{$$lref{$vendor}{$model}}) { for my $top (sort keys %{$$lref{$vendor}{$model}{$mc}}) { for my $mid (sort keys %{$$lref{$vendor}{$model}{$mc}{$top}}) { for my $low (sort keys %{$$lref{$vendor}{$model}{$mc}{$top}{$mid}}) { my $file = get_dimm_label_node($$num_layers{$vendor}{$model}, $mc, $top, $mid, $low); # Ignore sysfs files that don't exist. Might just be # unpopulated bank. next unless -f $file; if (!open (DL, ">$file")) { warn ("Unable to open $file\n"); next; } syswrite DL, $$lref{$vendor}{$model}{$mc}{$top}{$mid}{$low}; close (DL); } } } } } sub register_dimm_labels { my ($lref, $num_layers, $lref_prod, $num_layers_prod) = parse_dimm_labels (); my $vendor = lc $conf{mainboard}{vendor}; my $model = lc $conf{mainboard}{model}; my $pvendor = lc $conf{mainboard}{product_vendor}; my $pname = lc $conf{mainboard}{product_name}; my $sysfs = "/sys/devices/system/edac/mc"; if (!exists $$lref{$vendor}{$model} && !exists $$lref_prod{$pvendor}{$pname}) { log_error ("No dimm labels for $conf{mainboard}{vendor} " . "model $conf{mainboard}{model}\n"); return 0; } my $sysfs_dir = "/sys/devices/system/edac/mc"; find({wanted => \&parse_dimm_nodes, no_chdir => 1}, $sysfs_dir); select (undef, undef, undef, $conf{opt}{delay}); if (exists $$lref{$vendor}{$model}) { write_dimm_labels($lref, $num_layers, $vendor, $model); } else { write_dimm_labels($lref_prod, $num_layers_prod, $pvendor, $pname); } return 1; } sub dimm_display_layer_rev($@); sub dimm_display_layer_rev($@) { my $layer = shift; my @pos = @_; $layer++; if ($layer >= scalar(@pos) - 1) { my $str_loc = join(':', @pos); my $size = $dimm_size{$str_loc}; if (!$size) { $size = 0; } my $s = sprintf " %4i MB |", $size; $item_size = length($s); return $s; } my $s; for (my $i = 0; $i <= $max_pos[$layer]; $i++) { $pos[$layer] = $i; $s .= dimm_display_layer_rev($layer, @pos); } return $s; } sub dimm_display_layer(@) { my @pos = @_; my $s; for (my $i = 0; $i <= $max_pos[0]; $i++) { $pos[0] = $i; $s .= dimm_display_layer_rev(0, @pos); } return $s; } sub dimm_display_layer_header($$) { my $n_items = 1; my $scale; my $layer = shift; my $tot_items = shift; my $s; for (my $i = 0; $i <= $layer; $i++) { $n_items *= $max_pos[$i] + 1; } $scale = $tot_items / $n_items; my $d = 0; for (my $i = 0; $i < $n_items; $i++) { my $val = sprintf("%s%d", $layers[$layer], $d); $val = substr($val, 0, $scale * $item_size - 2); my $fillsize = $scale * $item_size - 1 - length($val); $s .= "|"; $s .= " " x ($fillsize / 2); $s .= $val; $s .= " " x ($fillsize - floor($fillsize / 2)); $d++; if ($d > $max_pos[$layer]) { $d = 0; } } $s .= "|"; return $s; } sub dimm_display_mem() { my @pos = @max_pos; my $sep = ""; my $tot_items = 1; my $first = 1; for (my $i = 0; $i < scalar(@pos) - 1; $i++) { $pos[$i] = 0; $tot_items *= $max_pos[$i] + 1; } my $is_even = $max_pos[scalar(@max_pos) - 1] % 2; for (my $d = $max_pos[scalar(@max_pos) - 1]; $d >= 0; $d--) { my $len; my $s = sprintf("%s%d: |", $layers[scalar(@max_pos) - 1], $d); my $p1 = length($s) - 1; $pos[scalar(@pos) - 1] = $d; $s .= dimm_display_layer(@pos); $len += length($s); $sep = "-" x $p1; $sep .= "+"; $sep .= "-" x ($len - $p1 - 2); $sep .= "+"; if ($first) { my $sep1 = " " x $p1; $sep1 .= "+"; $sep1 .= "-" x ($len - $p1 - 2); $sep1 .= "+"; printf "$sep1\n"; for (my $layer = 0; $layer < scalar(@pos) - 1; $layer++) { my $s = sprintf("%s%d: |", $layers[scalar(@max_pos) - 1], 0); my $p1 = length($s) - 1; my $msg = " " x $p1; $msg .= dimm_display_layer_header($layer, $tot_items); printf "$msg\n"; } printf "$sep\n" if (!$is_even); $first = 0; } if ($is_even && (($max_pos[scalar(@max_pos) - 1] - $d) % 2 == 0)) { printf "$sep\n"; } printf "$s\n"; } printf "$sep\n"; } sub fill_csrow_size() { foreach my $str_loc (keys %rank_size) { my @temp = split(/:/, $str_loc); my $csrow = join(':', $temp[0], $temp[1]); if ($csrow_ranks{$csrow}) { $rank_size{$str_loc} = $csrow_size{$csrow} / $csrow_ranks{$csrow}; } } } sub display_memory_layout { my $sysfs_dir = "/sys/devices/system/edac/mc"; find({wanted => \&parse_dimm_nodes, no_chdir => 1}, $sysfs_dir); if (!scalar(%csrow_size)) { log_error ("No memories found at via edac.\n"); exit -1; } elsif (!scalar(%dimm_size)) { fill_csrow_size; $layers[0] = "mc"; $layers[1] = "csrow"; $layers[2] = "channel"; @max_pos = @max_csrow; %dimm_size = %rank_size; } dimm_display_mem(); } sub display_error_count { my $sysfs_dir = "/sys/devices/system/edac/mc"; my $key; my $max_width = 0; my %dimm_labels = (); find ({wanted => \&parse_dimm_nodes, no_chdir => 1}, $sysfs_dir); if (!scalar(keys %dimm_node)) { log_error ("No DIMMs found in /sys or new sysfs EDAC interface not found.\n"); exit -1; } foreach $key (keys %dimm_node) { my $label_width; open IN, $dimm_label_file{$key}; chomp(my $label = <IN>); close IN; $label_width = length $label; if ($label_width > $max_width) { $max_width = $label_width; } $dimm_labels{$key} = $label; } my $string = "Label"; $string .= " " x ($max_width - length $string); print($string . "\tCE\tUE\n"); foreach $key (keys %dimm_node) { my $ce_count = $dimm_ce_count{$key}; my $ue_count = $dimm_ue_count{$key}; print("$dimm_labels{$key}\t$ce_count\t$ue_count\n"); } } sub find_prog { my ($file) = @_; for my $dir ("/sbin", "/usr/sbin", split ':', $ENV{PATH}) { return "$dir/$file" if -x "$dir/$file"; } # log_error ("Failed to find $file in PATH\n"); return ""; } sub get_extlog_type { my @types; if ($_[0] < 0 || $_[0] > 15) { return "unknown-type"; } @types = ("unknown", "no error", "single-bit ECC", "multi-bit ECC", "single-symbol chipkill ECC", "multi-symbol chipkill ECC", "master abort", "target abort", "parity error", "watchdog timeout", "invalid address", "mirror Broken", "memory sparing", "scrub corrected error", "scrub uncorrected error", "physical memory map-out event", "unknown-type"); return $types[$_[0]]; } sub get_extlog_severity { my @sev; if ($_[0] < 0 || $_[0] > 3) { return "unknown-severity"; } @sev = ("recoverable", "fatal", "corrected", "informational", "unknown-severity"); return $sev[$_[0]]; } use constant { CPER_MEM_VALID_NODE => 0x0008, CPER_MEM_VALID_CARD => 0x0010, CPER_MEM_VALID_MODULE => 0x0020, CPER_MEM_VALID_BANK => 0x0040, CPER_MEM_VALID_DEVICE => 0x0080, CPER_MEM_VALID_ROW => 0x0100, CPER_MEM_VALID_COLUMN => 0x0200, CPER_MEM_VALID_BIT_POSITION => 0x0400, CPER_MEM_VALID_REQUESTOR_ID => 0x0800, CPER_MEM_VALID_RESPONDER_ID => 0x1000, CPER_MEM_VALID_TARGET_ID => 0x2000, CPER_MEM_VALID_ERROR_TYPE => 0x4000, CPER_MEM_VALID_RANK_NUMBER => 0x8000, CPER_MEM_VALID_CARD_HANDLE => 0x10000, CPER_MEM_VALID_MODULE_HANDLE => 0x20000, }; sub get_cper_data_text { my $cper_data = $_[0]; my ($validation_bits, $node, $card, $module, $bank, $device, $row, $column, $bit_pos, $requestor_id, $responder_id, $target_id, $rank, $mem_array_handle, $mem_dev_handle) = unpack 'QSSSSSSSSQQQSSS', $cper_data; my @out; if ($validation_bits & CPER_MEM_VALID_NODE) { push @out, (sprintf "node=%d", $node); } if ($validation_bits & CPER_MEM_VALID_CARD) { push @out, (sprintf "card=%d", $card); } if ($validation_bits & CPER_MEM_VALID_MODULE) { push @out, (sprintf "module=%d", $module); } if ($validation_bits & CPER_MEM_VALID_BANK) { push @out, (sprintf "bank=%d", $bank); } if ($validation_bits & CPER_MEM_VALID_DEVICE) { push @out, (sprintf "device=%d", $device); } if ($validation_bits & CPER_MEM_VALID_ROW) { push @out, (sprintf "row=%d", $row); } if ($validation_bits & CPER_MEM_VALID_COLUMN) { push @out, (sprintf "column=%d", $column); } if ($validation_bits & CPER_MEM_VALID_BIT_POSITION) { push @out, (sprintf "bit_position=%d", $bit_pos); } if ($validation_bits & CPER_MEM_VALID_REQUESTOR_ID) { push @out, (sprintf "0x%08x", $requestor_id); } if ($validation_bits & CPER_MEM_VALID_RESPONDER_ID) { push @out, (sprintf "0x%08x", $responder_id); } if ($validation_bits & CPER_MEM_VALID_TARGET_ID) { push @out, (sprintf "0x%08x", $target_id); } if ($validation_bits & CPER_MEM_VALID_RANK_NUMBER) { push @out, (sprintf "rank=%d", $rank); } if ($validation_bits & CPER_MEM_VALID_CARD_HANDLE) { push @out, (sprintf "mem_array_handle=%d", $mem_array_handle); } if ($validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { push @out, (sprintf "mem_dev_handle=%d", $mem_dev_handle); } return join (", ", @out); } sub get_uuid_le { my $out = ""; my @bytes = unpack "C*", $_[0]; my @le16_table = (3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15); for (my $i = 0; $i < 16; $i++) { $out .= sprintf "%.2x", $bytes[$le16_table[$i]]; if ($i == 3 or $i == 5 or $i == 7 or $i == 9) { $out .= "-"; } } return $out; } sub summary { require DBI; my ($query, $query_handle, $out); my ($err_type, $label, $mc, $top, $mid, $low, $count, $msg, $action_result); my ($etype, $severity, $etype_string, $severity_string); my ($dev_name, $dev); my ($mpidr); my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname", "", "", {}); # Memory controller mc_event errors $query = "select err_type, label, mc, top_layer,middle_layer,lower_layer, count(*) from mc_event group by err_type, label, mc, top_layer, middle_layer, lower_layer"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($err_type, $label, $mc, $top, $mid, $low, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\t$err_type on DIMM Label(s): '$label' location: $mc:$top:$mid:$low errors: $count\n"; } if ($out ne "") { print "Memory controller events summary:\n$out\n"; } else { print "No Memory errors.\n\n"; } $query_handle->finish; # PCIe AER aer_event errors if ($has_aer == 1) { $query = "select err_type, err_msg, count(*) from aer_event group by err_type, err_msg"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($err_type, $msg, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\t$count $err_type errors: $msg\n"; } if ($out ne "") { print "PCIe AER events summary:\n$out\n"; } else { print "No PCIe AER errors.\n\n"; } $query_handle->finish; } # ARM processor arm_event errors if ($has_arm == 1) { $query = "select mpidr, count(*) from arm_event group by mpidr"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($mpidr, $count)); $out = ""; while($query_handle->fetch()) { $out .= sprintf "\tCPU(mpidr=0x%x) has %d errors\n", $mpidr, $count; } if ($out ne "") { print "ARM processor events summary:\n$out\n"; } else { print "No ARM processor errors.\n\n"; } $query_handle->finish; } # extlog errors if ($has_extlog == 1) { $query = "select etype, severity, count(*) from extlog_event group by etype, severity"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($etype, $severity, $count)); $out = ""; while($query_handle->fetch()) { $etype_string = get_extlog_type($etype); $severity_string = get_extlog_severity($severity); $out .= "\t$count $etype_string $severity_string errors\n"; } if ($out ne "") { print "Extlog records summary:\n$out"; } else { print "No Extlog errors.\n\n"; } $query_handle->finish; } # devlink errors if ($has_devlink == 1) { $query = "select dev_name, count(*) from devlink_event group by dev_name"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($dev_name, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\t$dev_name has $count errors\n"; } if ($out ne "") { print "Devlink records summary:\n$out"; } else { print "No devlink errors.\n"; } $query_handle->finish; } # Disk errors if ($has_disk_errors == 1) { $query = "select dev, count(*) from disk_errors group by dev"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($dev, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\t$dev has $count errors\n"; } if ($out ne "") { print "Disk errors summary:\n$out"; } else { print "No disk errors.\n"; } $query_handle->finish; } # Memory failure errors if ($has_mem_failure == 1) { $query = "select action_result, count(*) from memory_failure_event group by action_result"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($action_result, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\t$action_result errors: $count\n"; } if ($out ne "") { print "Memory failure events summary:\n$out\n"; } else { print "No Memory failure errors.\n\n"; } $query_handle->finish; } # MCE mce_record errors if ($has_mce == 1) { $query = "select error_msg, count(*) from mce_record group by error_msg"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($msg, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\t$count $msg errors\n"; } if ($out ne "") { print "MCE records summary:\n$out"; } else { print "No MCE errors.\n"; } $query_handle->finish; } undef($dbh); } sub errors { require DBI; my ($query, $query_handle, $id, $time, $devname, $count, $type, $msg, $label, $mc, $top, $mid, $low, $addr, $grain, $syndrome, $detail, $out); my ($mcgcap,$mcgstatus, $status, $misc, $ip, $tsc, $walltime, $cpu, $cpuid, $apicid, $socketid, $cs, $bank, $cpuvendor, $bank_name, $mcgstatus_msg, $mcistatus_msg, $user_action, $mc_location); my ($timestamp, $etype, $severity, $etype_string, $severity_string, $fru_id, $fru_text, $cper_data); my ($bus_name, $dev_name, $driver_name, $reporter_name); my ($dev, $sector, $nr_sector, $error, $rwbs, $cmd); my ($error_count, $affinity, $mpidr, $r_state, $psci_state); my ($pfn, $page_type, $action_result); my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname", "", "", {}); # Memory controller mc_event errors $query = "select id, timestamp, err_count, err_type, err_msg, label, mc, top_layer,middle_layer,lower_layer, address, grain, syndrome, driver_detail from mc_event order by id"; $query_handle = $dbh->prepare($query); if (!$query_handle) { log_error ("mc_event table missing from $dbname. Run 'rasdaemon --record'.\n"); exit -1 } $query_handle->execute(); $query_handle->bind_columns(\($id, $time, $count, $type, $msg, $label, $mc, $top, $mid, $low, $addr, $grain, $syndrome, $detail)); $out = ""; while($query_handle->fetch()) { $out .= "$id $time $count $type error(s): $msg at $label location: $mc:$top:$mid:$low, addr $addr, grain $grain, syndrome $syndrome $detail\n"; } if ($out ne "") { print "Memory controller events:\n$out\n"; } else { print "No Memory errors.\n\n"; } $query_handle->finish; # PCIe AER aer_event errors if ($has_aer == 1) { $query = "select id, timestamp, dev_name, err_type, err_msg from aer_event order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $time, $devname, $type, $msg)); $out = ""; while($query_handle->fetch()) { $out .= "$id $time $devname $type error: $msg\n"; } if ($out ne "") { print "PCIe AER events:\n$out\n"; } else { print "No PCIe AER errors.\n\n"; } $query_handle->finish; } # ARM processor arm_event errors if ($has_arm == 1) { $query = "select id, timestamp, error_count, affinity, mpidr, running_state, psci_state from arm_event order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $error_count, $affinity, $mpidr, $r_state, $psci_state)); $out = ""; while($query_handle->fetch()) { $out .= "$id $timestamp error: "; $out .= "error_count=$error_count, " if ($error_count); $out .= "affinity_level=$affinity, "; $out .= sprintf "mpidr=0x%x, ", $mpidr; $out .= sprintf "running_state=0x%x, ", $r_state; $out .= sprintf "psci_state=0x%x", $psci_state; $out .= "\n"; } if ($out ne "") { print "ARM processor events:\n$out\n"; } else { print "No ARM processor errors.\n\n"; } $query_handle->finish; } # Extlog errors if ($has_extlog == 1) { $query = "select id, timestamp, etype, severity, address, fru_id, fru_text, cper_data from extlog_event order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $etype, $severity, $addr, $fru_id, $fru_text, $cper_data)); $out = ""; while($query_handle->fetch()) { $etype_string = get_extlog_type($etype); $severity_string = get_extlog_severity($severity); $out .= "$id $timestamp error: "; $out .= "type=$etype_string, "; $out .= "severity=$severity_string, "; $out .= sprintf "address=0x%08x, ", $addr; $out .= sprintf "fru_id=%s, ", get_uuid_le($fru_id); $out .= "fru_text='$fru_text', "; $out .= get_cper_data_text($cper_data) if ($cper_data); $out .= "\n"; } if ($out ne "") { print "Extlog events:\n$out\n"; } else { print "No Extlog errors.\n\n"; } $query_handle->finish; } # devlink errors if ($has_devlink == 1) { $query = "select id, timestamp, bus_name, dev_name, driver_name, reporter_name, msg from devlink_event order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $bus_name, $dev_name, $driver_name, $reporter_name, $msg)); $out = ""; while($query_handle->fetch()) { $out .= "$id $timestamp error: "; $out .= "bus_name=$bus_name, "; $out .= "dev_name=$dev_name, "; $out .= "driver_name=$driver_name, "; $out .= "reporter_name=$reporter_name, "; $out .= "message='$msg', "; $out .= "\n"; } if ($out ne "") { print "Devlink events:\n$out\n"; } else { print "No devlink errors.\n\n"; } $query_handle->finish; } # Disk errors if ($has_disk_errors == 1) { $query = "select id, timestamp, dev, sector, nr_sector, error, rwbs, cmd from disk_errors order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $dev, $sector, $nr_sector, $error, $rwbs, $cmd)); $out = ""; while($query_handle->fetch()) { $out .= "$id $timestamp error: "; $out .= "dev=$dev, "; $out .= "sector=$sector, "; $out .= "nr_sector=$nr_sector, "; $out .= "error='$error', "; $out .= "rwbs='$rwbs', "; $out .= "cmd='$cmd', "; $out .= "\n"; } if ($out ne "") { print "Disk errors\n$out\n"; } else { print "No disk errors.\n\n"; } $query_handle->finish; } # Memory failure errors if ($has_mem_failure == 1) { $query = "select id, timestamp, pfn, page_type, action_result from memory_failure_event order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $pfn, $page_type, $action_result)); $out = ""; while($query_handle->fetch()) { $out .= "$id $timestamp error: "; $out .= "pfn=$pfn, page_type=$page_type, action_result=$action_result\n"; } if ($out ne "") { print "Memory failure events:\n$out\n"; } else { print "No Memory failure errors.\n\n"; } $query_handle->finish; } # MCE mce_record errors if ($has_mce == 1) { $query = "select id, timestamp, mcgcap, mcgstatus, status, addr, misc, ip, tsc, walltime, cpu, cpuid, apicid, socketid, cs, bank, cpuvendor, bank_name, error_msg, mcgstatus_msg, mcistatus_msg, user_action, mc_location from mce_record order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $time, $mcgcap,$mcgstatus, $status, $addr, $misc, $ip, $tsc, $walltime, $cpu, $cpuid, $apicid, $socketid, $cs, $bank, $cpuvendor, $bank_name, $msg, $mcgstatus_msg, $mcistatus_msg, $user_action, $mc_location)); $out = ""; while($query_handle->fetch()) { $out .= "$id $time error: $msg"; $out .= ", CPU $cpuvendor" if ($cpuvendor); $out .= ", bank $bank_name" if ($bank_name); $out .= ", mcg $mcgstatus_msg" if ($mcgstatus_msg); $out .= ", mci $mcistatus_msg" if ($mcistatus_msg); $out .= ", $mc_location" if ($mc_location); $out .= ", $user_action" if ($user_action); $out .= sprintf ", mcgcap=0x%08x", $mcgcap if ($mcgcap); $out .= sprintf ", mcgstatus=0x%08x", $mcgstatus if ($mcgstatus); $out .= sprintf ", status=0x%08x", $status if ($status); $out .= sprintf ", addr=0x%08x", $addr if ($addr); $out .= sprintf ", misc=0x%08x", $misc if ($misc); $out .= sprintf ", ip=0x%08x", $ip if ($ip); $out .= sprintf ", tsc=0x%08x", $tsc if ($tsc); $out .= sprintf ", walltime=0x%08x", $walltime if ($walltime); $out .= sprintf ", cpu=0x%08x", $cpu if ($cpu); $out .= sprintf ", cpuid=0x%08x", $cpuid if ($cpuid); $out .= sprintf ", apicid=0x%08x", $apicid if ($apicid); $out .= sprintf ", socketid=0x%08x", $socketid if ($socketid); $out .= sprintf ", cs=0x%08x", $cs if ($cs); $out .= sprintf ", bank=0x%08x", $bank if ($bank); $out .= "\n"; } if ($out ne "") { print "MCE events:\n$out\n"; } else { print "No MCE errors.\n\n"; } $query_handle->finish; } undef($dbh); } # Definitions of the vendor platform IDs. use constant { HISILICON_KUNPENG_920 => "Kunpeng920", HISILICON_KUNPENG_9XX => "Kunpeng9xx", }; sub vendor_errors_summary { require DBI; my ($num_args, $platform_id); my ($query, $query_handle, $count, $out); my ($module_id, $sub_module_id, $err_severity, $err_sev, $err_info); $num_args = $#ARGV + 1; $platform_id = 0; if ($num_args ne 0) { $platform_id = $ARGV[0]; } else { return; } my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname", "", "", {}); # HiSilicon Kunpeng920 errors if ($platform_id eq HISILICON_KUNPENG_920) { $query = "select err_severity, module_id, count(*) from hip08_oem_type1_event_v2 group by err_severity, module_id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($err_severity, $module_id, $count)); $out = ""; $err_sev = ""; while($query_handle->fetch()) { if ($err_severity ne $err_sev) { $out .= "$err_severity errors:\n"; $err_sev = $err_severity; } $out .= "\t$module_id: $count\n"; } if ($out ne "") { print "HiSilicon Kunpeng920 OEM type1 error events summary:\n$out\n"; } else { print "No HiSilicon Kunpeng920 OEM type1 errors.\n\n"; } $query_handle->finish; $query = "select err_severity, module_id, count(*) from hip08_oem_type2_event_v2 group by err_severity, module_id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($err_severity, $module_id, $count)); $out = ""; $err_sev = ""; while($query_handle->fetch()) { if ($err_severity ne $err_sev) { $out .= "$err_severity errors:\n"; $err_sev = $err_severity; } $out .= "\t$module_id: $count\n"; } if ($out ne "") { print "HiSilicon Kunpeng920 OEM type2 error events summary:\n$out\n"; } else { print "No HiSilicon Kunpeng920 OEM type2 errors.\n\n"; } $query_handle->finish; $query = "select err_severity, sub_module_id, count(*) from hip08_pcie_local_event_v2 group by err_severity, sub_module_id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($err_severity, $sub_module_id, $count)); $out = ""; $err_sev = ""; while($query_handle->fetch()) { if ($err_severity ne $err_sev) { $out .= "$err_severity errors:\n"; $err_sev = $err_severity; } $out .= "\t$sub_module_id: $count\n"; } if ($out ne "") { print "HiSilicon Kunpeng920 PCIe controller error events summary:\n$out\n"; } else { print "No HiSilicon Kunpeng920 PCIe controller errors.\n\n"; } $query_handle->finish; } # HiSilicon Kunpeng9xx common errors if ($platform_id eq HISILICON_KUNPENG_9XX) { $query = "select err_info, count(*) from hisi_common_section"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($err_info, $count)); $out = ""; while($query_handle->fetch()) { $out .= "\terrors: $count\n"; } if ($out ne "") { print "HiSilicon Kunpeng9xx common error events summary:\n$out\n"; } else { print "No HiSilicon Kunpeng9xx common errors.\n\n"; } $query_handle->finish; } undef($dbh); } sub vendor_errors { require DBI; my ($num_args, $platform_id); my ($query, $query_handle, $id, $timestamp, $out); my ($version, $soc_id, $socket_id, $nimbus_id, $core_id, $port_id); my ($module_id, $sub_module_id, $err_severity, $err_type, $err_info, $regs); $num_args = $#ARGV + 1; $platform_id = 0; if ($num_args ne 0) { $platform_id = $ARGV[0]; } else { return; } my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname", "", "", {}); # HiSilicon Kunpeng920 errors if ($platform_id eq HISILICON_KUNPENG_920) { $query = "select id, timestamp, version, soc_id, socket_id, nimbus_id, module_id, sub_module_id, err_severity, regs_dump from hip08_oem_type1_event_v2 order by id, module_id, err_severity"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $version, $soc_id, $socket_id, $nimbus_id, $module_id, $sub_module_id, $err_severity, $regs)); $out = ""; while($query_handle->fetch()) { $out .= "$id. $timestamp Error Info: "; $out .= "version=$version, "; $out .= "soc_id=$soc_id, " if ($soc_id); $out .= "socket_id=$socket_id, " if ($socket_id); $out .= "nimbus_id=$nimbus_id, " if ($nimbus_id); $out .= "module_id=$module_id, " if ($module_id); $out .= "sub_module_id=$sub_module_id, " if ($sub_module_id); $out .= "err_severity=$err_severity, \n" if ($err_severity); $out .= "Error Registers: $regs\n\n" if ($regs); } if ($out ne "") { print "HiSilicon Kunpeng920 OEM type1 error events:\n$out\n"; } else { print "No HiSilicon Kunpeng920 OEM type1 errors.\n"; } $query_handle->finish; $query = "select id, timestamp, version, soc_id, socket_id, nimbus_id, module_id, sub_module_id, err_severity, regs_dump from hip08_oem_type2_event_v2 order by id, module_id, err_severity"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $version, $soc_id, $socket_id, $nimbus_id, $module_id, $sub_module_id, $err_severity, $regs)); $out = ""; while($query_handle->fetch()) { $out .= "$id. $timestamp Error Info: "; $out .= "version=$version, "; $out .= "soc_id=$soc_id, " if ($soc_id); $out .= "socket_id=$socket_id, " if ($socket_id); $out .= "nimbus_id=$nimbus_id, " if ($nimbus_id); $out .= "module_id=$module_id, " if ($module_id); $out .= "sub_module_id=$sub_module_id, " if ($sub_module_id); $out .= "err_severity=$err_severity, \n" if ($err_severity); $out .= "Error Registers: $regs\n\n" if ($regs); } if ($out ne "") { print "HiSilicon Kunpeng920 OEM type2 error events:\n$out\n"; } else { print "No HiSilicon Kunpeng920 OEM type2 errors.\n"; } $query_handle->finish; $query = "select id, timestamp, version, soc_id, socket_id, nimbus_id, sub_module_id, core_id, port_id, err_severity, err_type, regs_dump from hip08_pcie_local_event_v2 order by id, sub_module_id, err_severity"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $version, $soc_id, $socket_id, $nimbus_id, $sub_module_id, $core_id, $port_id, $err_severity, $err_type, $regs)); $out = ""; while($query_handle->fetch()) { $out .= "$id. $timestamp Error Info: "; $out .= "version=$version, "; $out .= "soc_id=$soc_id, " if ($soc_id); $out .= "socket_id=$socket_id, " if ($socket_id); $out .= "nimbus_id=$nimbus_id, " if ($nimbus_id); $out .= "sub_module_id=$sub_module_id, " if ($sub_module_id); $out .= "core_id=$core_id, " if ($core_id); $out .= "port_id=$port_id, " if ($port_id); $out .= "err_severity=$err_severity, " if ($err_severity); $out .= "err_type=$err_type, \n" if ($err_type); $out .= "Error Registers: $regs\n\n" if ($regs); } if ($out ne "") { print "HiSilicon Kunpeng920 PCIe controller error events:\n$out\n"; } else { print "No HiSilicon Kunpeng920 PCIe controller errors.\n"; } $query_handle->finish; } # HiSilicon Kunpeng9xx common errors if ($platform_id eq HISILICON_KUNPENG_9XX) { $query = "select id, timestamp, err_info, regs_dump from hisi_common_section order by id"; $query_handle = $dbh->prepare($query); $query_handle->execute(); $query_handle->bind_columns(\($id, $timestamp, $err_info, $regs)); $out = ""; while($query_handle->fetch()) { $out .= "$id. $timestamp "; $out .= "Error Info:$err_info \n" if ($err_info); $out .= "Error Registers: $regs\n\n" if ($regs); } if ($out ne "") { print "HiSilicon Kunpeng9xx common error events:\n$out\n"; } else { print "No HiSilicon Kunpeng9xx common errors.\n"; } $query_handle->finish; } undef($dbh); } sub vendor_platforms { print "\nSupported platforms for the vendor-specific errors:\n"; print "\tHiSilicon Kunpeng920, platform-id=\"", HISILICON_KUNPENG_920, "\"\n"; print "\tHiSilicon Kunpeng9xx, platform-id=\"", HISILICON_KUNPENG_9XX, "\"\n"; print "\n"; } sub log_msg { print STDERR "$prog: ", @_ unless $conf{opt}{quiet}; } sub log_error { log_msg ("Error: @_"); } # vi: ts=4 sw=4 expandtab 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1491 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor