pax_global_header00006660000000000000000000000064135556235770014534gustar00rootroot0000000000000052 comment=637bb424dc317a044c722a671355ef9df0e0d30f ndctl-67/000077500000000000000000000000001355562357700124365ustar00rootroot00000000000000ndctl-67/.gitignore000066400000000000000000000016441355562357700144330ustar00rootroot00000000000000*.o *.xml .deps/ .libs/ Makefile !contrib/Makefile Makefile.in /aclocal.m4 /autom4te.cache /build-aux /config.* /configure /libtool /stamp-h1 *.1 Documentation/daxctl/asciidoc.conf Documentation/ndctl/asciidoc.conf Documentation/daxctl/asciidoctor-extensions.rb Documentation/ndctl/asciidoctor-extensions.rb .dirstamp daxctl/config.h daxctl/daxctl daxctl/lib/libdaxctl.la daxctl/lib/libdaxctl.lo daxctl/lib/libdaxctl.pc *.a ndctl/config.h ndctl/lib/libndctl.pc ndctl/ndctl rhel/ sles/ndctl.spec util/log.lo util/sysfs.lo version.m4 *.swp cscope.files cscope*.out tags test/*.log test/*.trs test/blk-ns test/dax-dev test/dax-errors test/dax-pmd test/daxdev-errors test/device-dax test/dpa-alloc test/dsm-fail test/hugetlb test/image test/libndctl test/mmap test/multi-pmem test/parent-uuid test/pmem-ns test/smart-listen test/smart-notify test/fio.job test/local-write-0-verify.state test/ack-shutdown-count-set test/list-smart-dimm ndctl-67/.travis.yml000066400000000000000000000011621355562357700145470ustar00rootroot00000000000000dist: xenial language: c sudo: required ccache: ccache git: depth: 5 quiet: true before_install: - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends -y systemd dh-systemd libkmod2 libkmod-dev libudev1 libudev-dev keyutils libkeyutils-dev libjson-c-dev libuuid1 asciidoctor jq kmod dracut build-essential git-core libelf-dev asciidoc binutils-dev - sudo apt-get build-dep linux-image-$(uname -r) install: - ./autogen.sh - ./configure CFLAGS='-g -O2' --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib - make -j$(nproc --all) - sudo make install jobs: include: script: true ndctl-67/CONTRIBUTING.md000066400000000000000000000043261355562357700146740ustar00rootroot00000000000000# Contributing to ndctl Thank you for taking the time to contribute to ndctl. The following is a set of guidelines that we adhere to, and request that contributors follow. 1. The libnvdimm (kernel subsystem) and ndctl developers primarily use the [linux-nvdimm](https://lists.01.org/mailman/listinfo/linux-nvdimm) mailing list for everything. It is recommended to send patches to **```linux-nvdimm@lists.01.org```** 1. Github [issues](https://github.com/pmem/ndctl/issues) are an acceptable way to report a problem, but if you just have a question, [email](mailto:linux-nvdimm@lists.01.org) the above list. 1. We follow the Linux Kernel [Coding Style Guide][cs] as applicable. [cs]: https://www.kernel.org/doc/html/latest/process/coding-style.html 1. We follow the Linux Kernel [Submitting Patches Guide][sp] as applicable. [sp]: https://www.kernel.org/doc/html/latest/process/submitting-patches.html 1. We follow the Linux Kernel [DCO][dco] (Developer Certificate of Origin). The DCO is an attestation attached to every contribution made by every developer. In the commit message of the contribution, the developer simply adds a Signed-off-by statement and thereby agrees to the DCO. [dco]: https://developercertificate.org/ 1. Github Pull Requests are acceptable mainly for smaller, more obvious fixups, but won't be merged directly, as Github doesn't allow for the kernel style flow of patches where a maintainer also signs off on the patches they apply. Larger changes may need to be sent to the mailing list so that everyone gets an opportunity to review them. 1. **Misc Best Practices:** 1. Use a subject prefix of "ndctl PATCH" (or "ndctl PATCH vN" for a new revision). This can be automated for a ```git format-patch``` command by setting a repo-local git config setting: ```git config format.subjectprefix "ndctl PATCH"``` 1. For commit messages: Describe the change and why it was needed. Use a concise subject line, and a blank line between the subject and the body, as well as between paragraphs. Use present tense and the imperative mood (e.g. "Add support for.." instead of "Added support.." or "Adding support"). Word-wrap to 72 columns. ndctl-67/COPYING000066400000000000000000000636251355562357700135050ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library 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; either version 2.1 of the License, or (at your option) any later version. This library 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ndctl-67/Documentation/000077500000000000000000000000001355562357700152475ustar00rootroot00000000000000ndctl-67/Documentation/COPYING000066400000000000000000000433341355562357700163110ustar00rootroot00000000000000All files in this directory (Documentation/) unless otherwise noted in the file itself are licensed under the GPLv2. --------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ndctl-67/Documentation/asciidoc.conf.in000066400000000000000000000046551355562357700203130ustar00rootroot00000000000000## linkUTILITY: macro # # Copyright (c) 2005, Sergey Vlasov # Copyright (c) 2005, Jonas Fonseca # # Originally copied from GIT source (commit d1c2e113c5b6 "[PATCH] # Documentation: Add asciidoc.conf file and gitlink: macro") # # Usage: linkUTILITY:command[manpage-section] # # Note, {0} is the manpage section, while {target} is the command. # # Show PERF link as: (
); if section is defined, else just show # the command. [macros] (?su)[\\]?(?PlinkUTILITY):(?P\S*?)\[(?P.*?)\]= [attributes] asterisk=* plus=+ caret=^ startsb=[ endsb=] tilde=~ ifdef::backend-docbook[] [linkUTILITY-inlinemacro] {0%{target}} {0#} {0#{target}{0}} {0#} endif::backend-docbook[] ifdef::backend-docbook[] ifndef::UTILITY-asciidoc-no-roff[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. # v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] {title} ifdef::doctype-manpage[] .ft C endif::doctype-manpage[] | ifdef::doctype-manpage[] .ft endif::doctype-manpage[] {title#} endif::UTILITY-asciidoc-no-roff[] ifdef::UTILITY-asciidoc-no-roff[] ifdef::doctype-manpage[] # The following two small workarounds insert a simple paragraph after screen [listingblock] {title} | {title#} [verseblock] {title} {title%} {title#} | {title#} {title%} endif::doctype-manpage[] endif::UTILITY-asciidoc-no-roff[] endif::backend-docbook[] ifdef::doctype-manpage[] ifdef::backend-docbook[] [header] template::[header-declarations] {mantitle} {manvolnum} UTILITY {UTILITY_version} UTILITY Manual {manname} {manpurpose} endif::backend-docbook[] endif::doctype-manpage[] ifdef::backend-xhtml11[] [linkUTILITY-inlinemacro] {target}{0?({0})} endif::backend-xhtml11[] ndctl-67/Documentation/asciidoctor-extensions.rb.in000066400000000000000000000016341355562357700227050ustar00rootroot00000000000000require 'asciidoctor' require 'asciidoctor/extensions' module @Utility@ module Documentation class Link@Utility@Processor < Asciidoctor::Extensions::InlineMacroProcessor use_dsl named :chrome def process(parent, target, attrs) if parent.document.basebackend? 'html' prefix = parent.document.attr('@utility@-relative-html-prefix') %(#{target}(#{attrs[1]})\n) elsif parent.document.basebackend? 'manpage' "#{target}(#{attrs[1]})" elsif parent.document.basebackend? 'docbook' "\n" \ "#{target}" \ "#{attrs[1]}\n" \ "\n" end end end end end Asciidoctor::Extensions.register do inline_macro @Utility@::Documentation::Link@Utility@Processor, :link@utility@ end ndctl-67/Documentation/copyright.txt000066400000000000000000000004471355562357700200250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 COPYRIGHT --------- Copyright (c) 2016 - 2019, Intel Corporation. License GPLv2: GNU GPL version 2 . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. ndctl-67/Documentation/daxctl/000077500000000000000000000000001355562357700165265ustar00rootroot00000000000000ndctl-67/Documentation/daxctl/Makefile.am000066400000000000000000000032131355562357700205610ustar00rootroot00000000000000# Copyright(c) 2015-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License 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. if USE_ASCIIDOCTOR do_subst = sed -e 's,@Utility@,Daxctl,g' -e's,@utility@,daxctl,g' CONFFILE = asciidoctor-extensions.rb asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in $(AM_V_GEN) $(do_subst) < $< > $@ else do_subst = sed -e 's,UTILITY,daxctl,g' CONFFILE = asciidoc.conf asciidoc.conf: ../asciidoc.conf.in $(AM_V_GEN) $(do_subst) < $< > $@ endif man1_MANS = \ daxctl.1 \ daxctl-list.1 \ daxctl-migrate-device-model.1 \ daxctl-reconfigure-device.1 \ daxctl-online-memory.1 \ daxctl-offline-memory.1 CLEANFILES = $(man1_MANS) XML_DEPS = \ ../../version.m4 \ ../copyright.txt \ Makefile \ $(CONFFILE) RM ?= rm -f if USE_ASCIIDOCTOR %.1: %.txt $(XML_DEPS) $(AM_V_GEN)$(RM) $@+ $@ && \ $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ -I. -rasciidoctor-extensions \ -amansource=daxctl -amanmanual="daxctl Manual" \ -andctl_version=$(VERSION) -o $@+ $< && \ mv $@+ $@ else %.xml: %.txt $(XML_DEPS) $(AM_V_GEN)$(RM) $@+ $@ && \ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ --unsafe -adaxctl_version=$(VERSION) -o $@+ $< && \ mv $@+ $@ %.1: %.xml $(XML_DEPS) $(AM_V_GEN)$(RM) $@ && \ $(XMLTO) -o . -m ../manpage-normal.xsl man $< endif ndctl-67/Documentation/daxctl/daxctl-list.txt000066400000000000000000000035631355562357700215260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 daxctl-list(1) ============== NAME ---- daxctl-list - dump the platform Device-DAX regions, devices, and attributes in json. SYNOPSIS -------- [verse] 'daxctl list' [] Walk all the device-dax-regions in the system and list all device instances along with some of their major attributes. Options can be specified to limit the output to objects of a certain class. Where the classes are regions or devices. By default, 'daxctl list' with no options is equivalent to: [verse] daxctl list --devices EXAMPLE ------- ---- # daxctl list --regions --devices { "id":1, "devices":[ { "chardev":"dax1.0", "size":3233808384 } ] } ---- OPTIONS ------- -r:: --region=:: A device-dax region is a contiguous range of memory that hosts one or more /dev/daxX.Y devices, where X is the region id and Y is the device instance id. The keyword 'all' can be specified to carry out the operation on every region in the system. -d:: --dev=:: Specify a dax device name, . tuple, or keyword 'all' to filter the listing. For example to list the first device instance in region1: ---- # daxctl list --dev=1.0 { "chardev":"dax1.0", "size":3233808384 } ---- -D:: --devices:: Include device-dax instance info in the listing (default) -R:: --regions:: Include region info in the listing -i:: --idle:: Include idle (not enabled / zero-sized) devices in the listing -u:: --human:: By default 'daxctl list' will output machine-friendly raw-integer data. Instead, with this flag, numbers representing storage size will be formatted as human readable strings with units, other fields are converted to hexadecimal strings. Example: ---- # daxctl list { "chardev":"dax1.0", "size":32828817408 } # daxctl list --human { "chardev":"dax1.0", "size":"30.57 GiB (32.83 GB)" } ---- include::../copyright.txt[] ndctl-67/Documentation/daxctl/daxctl-migrate-device-model.txt000066400000000000000000000040011355562357700245220ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 daxctl-migrate-device-model(1) ============================== NAME ---- daxctl-migrate-device-model - Opt-in to the /sys/bus/dax device-model, allow for alternative Device-DAX instance drivers. SYNOPSIS -------- [verse] 'daxctl migrate-device-model' Arrange for modprobe to disable the dax_pmem_compat, if present, and instead deploy the dax_pmem module to convert to the /sys/bus/dax model. Kernel versions prior to v5.1 may not support /sys/bus/dax in which case the result of this command is a nop until the kernel is updated. The motivation for changing from /sys/class/dax to /sys/bus/dax is to allow for alternative drivers for Device-DAX instances, in particular the dax_kmem driver. By default device-dax publishes a /dev/daxX.Y character device for userspace to directly map performance differentiated memory. This is fine if the memory is to be exclusively consumed / managed by userspace. Alternatively an administrator may want the kernel to manage the memory, make it available via malloc(), allow for over-provisioning, and / or apply kernel-based resource control schemes to the memory. In that case the memory fronted by a given Device-DAX instance can be assigned to the dax_kmem driver which arranges for the core-kernel memory-management sub-system to assume management of the memory range. This behavior is opt-in for consideration of existing applications / scripts that may be hard coded to use /sys/class/dax. Fixes have been submitted to applications known to have these direct dependencies http://git.kernel.dk/cgit/fio/commit/?id=b08e7d6b18b4[FIO] https://github.com/pmem/pmdk/commit/91bc8620884e[PMDK], however, there may be others and a system-owner should be aware of the potential for regression of Device-DAX consuming scripts, applications, or older daxctl binaries. The modprobe policy established by this utility becomes effective after the next reboot, or after all DAX related modules have been removed and re-loaded with "udevadm trigger" include::../copyright.txt[] ndctl-67/Documentation/daxctl/daxctl-offline-memory.txt000066400000000000000000000034021355562357700234730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 daxctl-offline-memory(1) ======================== NAME ---- daxctl-offline-memory - Offline the memory for a device that is in system-ram mode SYNOPSIS -------- [verse] 'daxctl offline-memory' [...] [] EXAMPLES -------- * Reconfigure dax0.0 to system-ram mode ---- # daxctl reconfigure-device --mode=system-ram --human dax0.0 { "chardev":"dax0.0", "size":"7.87 GiB (8.45 GB)", "target_node":2, "mode":"system-ram" } ---- * Offline the memory ---- # daxctl offline-memory dax0.0 dax0.0: 62 sections offlined offlined memory for 1 device ---- DESCRIPTION ----------- Offline the memory sections associated with a device that has been converted to the system-ram mode. If one or more blocks are already offline, attempt to offline the remaining blocks. If all blocks were already offline, print a message and return success without actually doing anything. This is complementary to the 'daxctl-online-memory' command, and may be used when it is wished to offline the memory sections, but not convert the device back to 'devdax' mode. OPTIONS ------- -r:: --region=:: Restrict the operation to devices belonging to the specified region(s). A device-dax region is a contiguous range of memory that hosts one or more /dev/daxX.Y devices, where X is the region id and Y is the device instance id. -u:: --human:: By default the command will output machine-friendly raw-integer data. Instead, with this flag, numbers representing storage size will be formatted as human readable strings with units, other fields are converted to hexadecimal strings. -v:: --verbose:: Emit more debug messages include::../copyright.txt[] SEE ALSO -------- linkdaxctl:daxctl-reconfigure-device[1],daxctl-online-memory[1] ndctl-67/Documentation/daxctl/daxctl-online-memory.txt000066400000000000000000000040331355562357700233360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 daxctl-online-memory(1) ======================= NAME ---- daxctl-online-memory - Online the memory for a device that is in system-ram mode SYNOPSIS -------- [verse] 'daxctl online-memory' [...] [] EXAMPLES -------- * Reconfigure dax0.0 to system-ram mode, don't online the memory ---- # daxctl reconfigure-device --mode=system-ram --no-online --human dax0.0 { "chardev":"dax0.0", "size":"7.87 GiB (8.45 GB)", "target_node":2, "mode":"system-ram" } ---- * Online the memory separately ---- # daxctl online-memory dax0.0 dax0.0: 62 new sections onlined onlined memory for 1 device ---- * Onlining memory when some sections were already online ---- # daxctl online-memory dax0.0 dax0.0: 1 section already online dax0.0: 61 new sections onlined onlined memory for 1 device ---- DESCRIPTION ----------- Online the memory sections associated with a device that has been converted to the system-ram mode. If one or more blocks are already online, print a message about them, and attempt to online the remaining blocks. This is complementary to the 'daxctl-reconfigure-device' command, when used with the '--no-online' option to skip onlining memory sections immediately after the reconfigure. In these scenarios, the memory can be onlined at a later time using 'daxctl-online-memory'. OPTIONS ------- -r:: --region=:: Restrict the operation to devices belonging to the specified region(s). A device-dax region is a contiguous range of memory that hosts one or more /dev/daxX.Y devices, where X is the region id and Y is the device instance id. include::movable-options.txt[] -u:: --human:: By default the command will output machine-friendly raw-integer data. Instead, with this flag, numbers representing storage size will be formatted as human readable strings with units, other fields are converted to hexadecimal strings. -v:: --verbose:: Emit more debug messages include::../copyright.txt[] SEE ALSO -------- linkdaxctl:daxctl-reconfigure-device[1],daxctl-offline-memory[1] ndctl-67/Documentation/daxctl/daxctl-reconfigure-device.txt000066400000000000000000000115321355562357700243130ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 daxctl-reconfigure-device(1) ============================ NAME ---- daxctl-reconfigure-device - Reconfigure a dax device into a different mode SYNOPSIS -------- [verse] 'daxctl reconfigure-device' [...] [] EXAMPLES -------- * Reconfigure dax0.0 to system-ram mode, don't online the memory ---- # daxctl reconfigure-device --mode=system-ram --no-online dax0.0 [ { "chardev":"dax0.0", "size":16777216000, "target_node":2, "mode":"system-ram" } ] ---- * Reconfigure dax0.0 to devdax mode, attempt to offline the memory ---- # daxctl reconfigure-device --human --mode=devdax --force dax0.0 { "chardev":"dax0.0", "size":"15.63 GiB (16.78 GB)", "target_node":2, "mode":"devdax" } ---- * Reconfigure all dax devices on region0 to system-ram mode ---- # daxctl reconfigure-device --mode=system-ram --region=0 all [ { "chardev":"dax0.0", "size":16777216000, "target_node":2, "mode":"system-ram" }, { "chardev":"dax0.1", "size":16777216000, "target_node":3, "mode":"system-ram" } ] ---- * Run a process called 'some-service' using numactl to restrict its cpu nodes to '0' and '1', and memory allocations to node 2 (determined using daxctl_dev_get_target_node() or 'daxctl list') ---- # daxctl reconfigure-device --mode=system-ram dax0.0 [ { "chardev":"dax0.0", "size":16777216000, "target_node":2, "mode":"system-ram" } ] # numactl --cpunodebind=0-1 --membind=2 -- some-service --opt1 --opt2 ---- DESCRIPTION ----------- Reconfigure the operational mode of a dax device. This can be used to convert a regular 'devdax' mode device to the 'system-ram' mode which arranges for the dax range to be hot-plugged into the system as regular memory. NOTE: This is a destructive operation. Any data on the dax device *will* be lost. NOTE: Device reconfiguration depends on the dax-bus device model. See linkdaxctl:daxctl-migrate-device-model[1] for more information. If dax-class is in use (via the dax_pmem_compat driver), the reconfiguration will fail with an error such as the following: ---- # daxctl reconfigure-device --mode=system-ram --region=0 all libdaxctl: daxctl_dev_disable: dax3.0: error: device model is dax-class dax3.0: disable failed: Operation not supported error reconfiguring devices: Operation not supported reconfigured 0 devices ---- 'daxctl-reconfigure-device' nominally expects that it will online new memory blocks as 'movable', so that kernel data doesn't make it into this memory. However, there are other potential agents that may be configured to automatically online new hot-plugged memory as it appears. Most notably, these are the '/sys/devices/system/memory/auto_online_blocks' configuration, or system udev rules. If such an agent races to online memory sections, daxctl checks if the blocks were onlined as 'movable' memory. If this was not the case, and the memory blocks are found to be in a different zone, then a warning is displayed. If it is desired that a different agent control the onlining of memory blocks, and the associated memory zone, then it is recommended to use the --no-online option described below. This will abridge the device reconfiguration operation to just hotplugging the memory, and refrain from then onlining it. OPTIONS ------- -r:: --region=:: Restrict the operation to devices belonging to the specified region(s). A device-dax region is a contiguous range of memory that hosts one or more /dev/daxX.Y devices, where X is the region id and Y is the device instance id. -m:: --mode=:: Specify the mode to which the dax device(s) should be reconfigured. - "system-ram": hotplug the device into system memory. - "devdax": switch to the normal "device dax" mode. This requires the kernel to support hot-unplugging 'kmem' based memory. If this is not available, a reboot is the only way to switch back to 'devdax' mode. -N:: --no-online:: By default, memory sections provided by system-ram devices will be brought online automatically and immediately with the 'online_movable' policy. Use this option to disable the automatic onlining behavior. include::movable-options.txt[] -f:: --force:: When converting from "system-ram" mode to "devdax", it is expected that all the memory sections are first made offline. By default, daxctl won't touch online memory. However with this option, attempt to offline the memory on the NUMA node associated with the dax device before converting it back to "devdax" mode. -u:: --human:: By default the command will output machine-friendly raw-integer data. Instead, with this flag, numbers representing storage size will be formatted as human readable strings with units, other fields are converted to hexadecimal strings. -v:: --verbose:: Emit more debug messages include::../copyright.txt[] SEE ALSO -------- linkdaxctl:daxctl-list[1],daxctl-migrate-device-model[1] ndctl-67/Documentation/daxctl/daxctl.txt000066400000000000000000000012671355562357700205540ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 daxctl(1) ========= NAME ---- daxctl - Provides enumeration and provisioning commands for the Linux kernel Device-DAX facility SYNOPSIS -------- [verse] 'daxctl' [--version] [--help] COMMAND [ARGS] OPTIONS ------- -v:: --version:: Display daxctl version. -h:: --help:: Run daxctl help command. DESCRIPTION ----------- The daxctl utility provides enumeration and provisioning commands for the Linux kernel Device-DAX facility. This facility enables DAX mappings of performance / feature differentiated memory without need of a filesystem. include::../copyright.txt[] SEE ALSO -------- linkdaxctl:ndctl-create-namespace[1], linkdaxctl:ndctl-list[1] ndctl-67/Documentation/daxctl/movable-options.txt000066400000000000000000000006361355562357700224120ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 --no-movable:: '--movable' is the default. This can be overridden to online new memory such that is is not 'movable'. This allows any allocation to potentially be served from this memory. This may preclude subsequent removal. With the '--movable' behavior (which is default), kernel allocations will not consider this memory, and it will be reserved for application use. ndctl-67/Documentation/manpage-base.xsl000066400000000000000000000026721355562357700203260ustar00rootroot00000000000000 sp br ndctl-67/Documentation/manpage-normal.xsl000066400000000000000000000013551355562357700207010ustar00rootroot00000000000000 \ . ndctl-67/Documentation/ndctl/000077500000000000000000000000001355562357700163535ustar00rootroot00000000000000ndctl-67/Documentation/ndctl/Makefile.am000066400000000000000000000051721355562357700204140ustar00rootroot00000000000000# Copyright(c) 2015-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License 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. if USE_ASCIIDOCTOR do_subst = sed -e 's,@Utility@,Ndctl,g' -e's,@utility@,ndctl,g' CONFFILE = asciidoctor-extensions.rb asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in $(AM_V_GEN) $(do_subst) < $< > $@ else do_subst = sed -e 's,UTILITY,ndctl,g' CONFFILE = asciidoc.conf asciidoc.conf: ../asciidoc.conf.in $(AM_V_GEN) $(do_subst) < $< > $@ endif man1_MANS = \ ndctl.1 \ ndctl-wait-scrub.1 \ ndctl-start-scrub.1 \ ndctl-zero-labels.1 \ ndctl-read-labels.1 \ ndctl-write-labels.1 \ ndctl-init-labels.1 \ ndctl-check-labels.1 \ ndctl-enable-region.1 \ ndctl-disable-region.1 \ ndctl-enable-dimm.1 \ ndctl-disable-dimm.1 \ ndctl-enable-namespace.1 \ ndctl-disable-namespace.1 \ ndctl-create-namespace.1 \ ndctl-destroy-namespace.1 \ ndctl-check-namespace.1 \ ndctl-clear-errors.1 \ ndctl-inject-error.1 \ ndctl-inject-smart.1 \ ndctl-update-firmware.1 \ ndctl-list.1 \ ndctl-monitor.1 \ ndctl-setup-passphrase.1 \ ndctl-update-passphrase.1 \ ndctl-remove-passphrase.1 \ ndctl-freeze-security.1 \ ndctl-sanitize-dimm.1 \ ndctl-load-keys.1 \ ndctl-wait-overwrite.1 CLEANFILES = $(man1_MANS) .ONESHELL: attrs.adoc: $(srcdir)/Makefile.am $(AM_V_GEN) cat <<- EOF >$@ :ndctl_monitorconfdir: $(ndctl_monitorconfdir) :ndctl_monitorconf: $(ndctl_monitorconf) :ndctl_keysdir: $(ndctl_keysdir) EOF XML_DEPS = \ ../../version.m4 \ Makefile \ $(CONFFILE) \ ../copyright.txt \ region-description.txt \ xable-region-options.txt \ dimm-description.txt \ xable-dimm-options.txt \ xable-namespace-options.txt \ ars-description.txt \ labels-description.txt \ labels-options.txt \ attrs.adoc RM ?= rm -f if USE_ASCIIDOCTOR %.1: %.txt $(XML_DEPS) $(AM_V_GEN)$(RM) $@+ $@ && \ $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ -I. -rasciidoctor-extensions \ -amansource=ndctl -amanmanual="ndctl Manual" \ -andctl_version=$(VERSION) -o $@+ $< && \ mv $@+ $@ else %.xml: %.txt $(XML_DEPS) $(AM_V_GEN)$(RM) $@+ $@ && \ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ --unsafe -andctl_version=$(VERSION) -o $@+ $< && \ mv $@+ $@ %.1: %.xml $(XML_DEPS) $(AM_V_GEN)$(RM) $@ && \ $(XMLTO) -o . -m ../manpage-normal.xsl man $< endif ndctl-67/Documentation/ndctl/ars-description.txt000066400000000000000000000005731355562357700222270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 DESCRIPTION ----------- NVDIMM Address Range Scrub is a capability provided by platform firmware that allows for the discovery of memory errors by system software. It enables system software to pre-emptively avoid accesses that could lead to uncorrectable memory error handling events, and it otherwise allows memory errors to be enumerated. ndctl-67/Documentation/ndctl/dimm-description.txt000066400000000000000000000015141355562357700223640ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 DESCRIPTION ----------- A generic DIMM device object, named /dev/nmemX, is registered for each memory device indicated in the ACPI NFIT table, or other platform NVDIMM resource discovery mechanism. The LIBNVDIMM core provides a built-in driver for these DIMM devices. The driver is responsible for determining if the DIMM implements a namespace label area, and initializing the kernel's in-memory copy of that label data. The kernel performs runtime modifications of that data when namespace provisioning actions are taken, and actively blocks userspace from initiating label data changes while the DIMM is active in any region. Disabling a DIMM, after all the regions it is a member of have been disabled, allows userspace to manually update the label data to be consumed when the DIMM is next enabled. ndctl-67/Documentation/ndctl/human-option.txt000066400000000000000000000004031355562357700215270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 -u:: --human:: Format numbers representing storage sizes, or offsets as human readable strings with units instead of the default machine-friendly raw-integer data. Convert other numeric fields into hexadecimal strings. ndctl-67/Documentation/ndctl/intel-nvdimm-security.txt000066400000000000000000000146131355562357700233710ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 include::attrs.adoc[] THEORY OF OPERATION ------------------- The Intel Device Specific Methods (DSM) specification v1.7 and v1.8 [1] introduced the following security management operations: enable passhprase, update passphrase, unlock DIMM, disable security, freeze security, secure (crypto) erase, overwrite, master passphrase enable, master passphrase update, and master passphrase secure erase. The security management for NVDIMMs is comprised of two parts. The front end uses the Linux key management framework (trusted and encrypted keys [2]) to store the encrypted passphrases in the kernel-managed keyring. The interface for this is the 'keyutils' utility which uses the key management APIs in the Linux kernel. The back end takes the decrypted payload (which is the DIMM passphrase) and passes it to the DIMM. Unlike other DSMs which are composed by libndctl and sent to the kernel via an ioctl, the security DSMs are managed through the 'security' sysfs attribute under the 'dimm' device. A 'key-ID' is written to the 'security' attribute and the kernel pulls the associated key material from the user keyring that is maintained by the kernel. The security process begins with the generation of a 'master key' that is used to seal (encrypt) the passphrase for the DIMM. There can either be one common 'master key' that is used to encrypt every DIMM's passphrase, or a separate key can be generated for each DIMM. The 'master key' is also referred to as the 'key-encryption-key' (kek). The 'kek' can either be generated by the TPM (Trusted Platform Module) on the system, or alternatively, the 'System Master Key' can also be used as the 'kek' For testing purposes a user key with randomized payload can also be used as a 'kek'. See [2] for details. To perform any security operations, it is expected that the 'kek' has been added to the kernel's user keyring as shown in example below: ---- # keyctl show Session Keyring 736023423 --alswrv 0 0 keyring: _ses 675104189 --alswrv 0 65534 \_ keyring: _uid.0 680187394 --alswrv 0 0 \_ trusted: nvdimm-master ---- Before performing any of the security operations, all the regions associated with the DIMM in question need to be disabled. For the 'overwrite' operation, in addition to the 'regions', the 'dimm' also needs to be disabled. [1] http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf + [2] https://www.kernel.org/doc/Documentation/security/keys/trusted-encrypted.rst The following sub-sections describe specifics of each security feature. === UNLOCK Unlock is performed by the kernel, however a preparation step must happen before the unlock DSM can be issued by the kernel. It is expected that from the initramfs, a setup command (ndctl 'load-keys') is executed before the libnvdimm module is loaded by modprobe. This command will inject the 'kek' and the encrypted passphrases into the kernel's user keyring. During the 'probe' of the libnvdimm driver, it will: . Check the security state of the device and see if the DIMM is locked . Request the associated encrypted passphrase from the kernel's user key ring . Use the 'kek' to decrypt the passphrase . Create the unlock DSM, copy the decrypted payload into the DSM . Issue the DSM to unlock the DIMM If the DIMM is already unlocked, the kernel will attempt to revalidate the passphrase. If we fail to revalidate the passphrase, the kernel will freeze the security and disallow any further security configuration changes. A kernel module parameter is available to override this behavior. === SETUP USER PASSPHRASE To setup the passphrase for a DIMM, it is expected that the 'kek' to be used is present in the kernel's user keyring. The 'kek' encrypts the DIMM passphrase using the 'enc32' key format. The plaintext passphrase is never provided by or made visible to the user. It is instead randomly generated by the kernel and userspace does not have access to it. Upon encryption, a binary blob of the passphrase is written to the passphrase blob storage directory ({ndctl_keysdir}). The user is responsible for backing up the passphrase blobs to a secure location. === UPDATE USER PASSPHRASE The update user passphrase operation uses the same DSM command as enable user passphrase. Most of the work is done on the key management side. The user has the option of providing a new 'kek' for the new passphrase, but continuing to use the existing 'kek' is also acceptable. The following operations are performed for 'update-passphrase': . Remove the encrypted passphrase from the kernel's user keyring. . Rename the passphrase blob to old. . Load this old passphrase blob into the keyring with an "old" name. . Create the new passphrase and encrypt with the 'kek'. . Send DSM with the old and new decrypted passphrases. . Remove old passphrase and the passphrase blob from the keyring. === REMOVE USER PASSPHRASE The 'key-ID' for the passphrase to be removed is written to sysfs. The kernel then sends the DSM to disable security, and the passphrase is then removed from the keyring, and the associated passphrase blob is deleted. === CRYPTO (SECURE) ERASE This operation is similar to remove-passphrase. The kernel issues a WBINVD instruction before and after the operation to ensure no data corruption from a stale CPU cache. Use ndctl's sanitize-dimm command with the `--crypto-erase` option to perform this operation. === OVERWRITE This is invoked using `--overwrite` option for ndctl 'sanitize-dimm'. The overwrite operation wipes the entire NVDIMM. The operation can take a significant amount of time. NOTE: When the command returns successfully, it just means overwrite has been successfully started, and not that the overwrite is complete. Subsequently, 'ndctl wait-overwrite'can be used to wait for the NVDIMMs that are performing overwrite. Upon successful completion of an overwrite, the WBINVD instruction is issued by the kernel. If both --crypto-erase and --overwrite options are supplied, then crypto-erase is performed before overwrite. === SECURITY FREEZE This operation does not require a passphrase. This will cause any security command other than a status query to be locked out until the next boot. === MASTER PASSPHRASE SETUP, UPDATE, and CRYPTO ERASE These operations are similar to the user passphrase enable and update. The only difference is that a different passphrase is used. The master passphrase has no relation to the master key ('kek') which is used for encryption of either passphrase. ndctl-67/Documentation/ndctl/labels-description.txt000066400000000000000000000004271355562357700227020ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 DESCRIPTION ----------- The namespace label area is a small persistent partition of capacity available on some NVDIMM devices. The label area is used to resolve aliasing between 'pmem' and 'blk' capacity by delineating namespace boundaries. ndctl-67/Documentation/ndctl/labels-options.txt000066400000000000000000000012611355562357700220470ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 :: One or more 'nmemX' device names. The keyword 'all' can be specified to operate on every dimm in the system, optionally filtered by bus id (see --bus= option). -s:: --size=:: Limit the operation to the given number of bytes. A size of 0 indicates to operate over the entire label capacity. -O:: --offset=:: Begin the operation at the given offset into the label area. -b:: --bus=:: Limit operation to memory devices (dimms) that are on the given bus. Where 'bus' can be a provider name or a bus id number. -v:: Turn on verbose debug messages in the library (if ndctl was built with logging and debug enabled). ndctl-67/Documentation/ndctl/namespace-description.txt000066400000000000000000000063671355562357700234050ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 THEORY OF OPERATION ------------------- The capacity of an NVDIMM REGION (contiguous span of persistent memory) is accessed via one or more NAMESPACE devices. REGION is the Linux term for what ACPI and UEFI call a DIMM-interleave-set, or a system-physical-address-range that is striped (by the memory controller) across one or more memory modules. The UEFI specification defines the 'NVDIMM Label Protocol' as the combination of label area access methods and a data format for provisioning one or more NAMESPACE objects from a REGION. Note that label support is optional and if Linux does not detect the label capability it will automatically instantiate a "label-less" namespace per region. Examples of label-less namespaces are the ones created by the kernel's 'memmap=ss!nn' command line option (see the nvdimm wiki on kernel.org), or NVDIMMs without a valid 'namespace index' in their label area. NOTE: Label-less namespaces lack many of the features of their label-rich cousins. For example, their size cannot be modified, or they cannot be fully 'destroyed' (i.e. the space reclaimed). A destroy operation will zero any mode-specific metadata. Finally, for create-namespace operations on label-less namespaces, ndctl bypasses the region capacity availability checks, and always satisfies the request using the full region capacity. The only reconfiguration operation supported on a label-less namespace is changing its 'mode'. A namespace can be provisioned to operate in one of 4 modes, 'fsdax', 'devdax', 'sector', and 'raw'. Here are the expected usage models for these modes: - fsdax: Filesystem-DAX mode is the default mode of a namespace when specifying 'ndctl create-namespace' with no options. It creates a block device (/dev/pmemX[.Y]) that supports the DAX capabilities of Linux filesystems (xfs and ext4 to date). DAX removes the page cache from the I/O path and allows mmap(2) to establish direct mappings to persistent memory media. The DAX capability enables workloads / working-sets that would exceed the capacity of the page cache to scale up to the capacity of persistent memory. Workloads that fit in page cache or perform bulk data transfers may not see benefit from DAX. When in doubt, pick this mode. - devdax: Device-DAX mode enables similar mmap(2) DAX mapping capabilities as Filesystem-DAX. However, instead of a block-device that can support a DAX-enabled filesystem, this mode emits a single character device file (/dev/daxX.Y). Use this mode to assign persistent memory to a virtual-machine, register persistent memory for RDMA, or when gigantic mappings are needed. - sector: Use this mode to host legacy filesystems that do not checksum metadata or applications that are not prepared for torn sectors after a crash. Expected usage for this mode is for small boot volumes. This mode is compatible with other operating systems. - raw: Raw mode is effectively just a memory disk that does not support DAX. Typically this indicates a namespace that was created by tooling or another operating system that did not know how to create a Linux 'fsdax' or 'devdax' mode namespace. This mode is compatible with other operating systems, but again, does not support DAX operation. ndctl-67/Documentation/ndctl/ndctl-check-labels.txt000066400000000000000000000012051355562357700225310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-check-labels(1) ==================== NAME ---- ndctl-check-labels - determine if the given dimms have a valid namespace index block SYNOPSIS -------- [verse] 'ndctl check-labels' [..] [] include::labels-description.txt[] In addition to checking if a label area has a valid index block, running this command in verbose mode reports the reason the index block is deemed invalid. OPTIONS ------- include::labels-options.txt[] include::../copyright.txt[] SEE ALSO -------- http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-67/Documentation/ndctl/ndctl-check-namespace.txt000066400000000000000000000041611355562357700232270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-check-namespace(1) ========================= NAME ---- ndctl-check-namespace - check namespace metadata consistency SYNOPSIS -------- [verse] 'ndctl check-namespace' [] DESCRIPTION ----------- A namespace in the 'sector' mode will have metadata on it to describe the kernel BTT (Block Translation Table). The check-namespace command can be used to check the consistency of this metadata, and optionally, also attempt to repair it, if it has enough information to do so. The namespace being checked has to be disabled before initiating a check on it as a precautionary measure. The --force option can override this. EXAMPLES -------- Check a namespace (only report errors) [verse] ndctl disable-namespace namespace0.0 ndctl check-namespace namespace0.0 Check a namespace, and perform repairs if possible [verse] ndctl disable-namespace namespace0.0 ndctl check-namespace --repair namespace0.0 OPTIONS ------- -R:: --repair:: Perform metadata repairs if possible. Without this option, the raw namespace contents will not be touched. -L:: --rewrite-log:: Regenerate the BTT log and write it to media. This can be used to convert from the old (pre 4.15) padding format that was incompatible with other BTT implementations to the updated format. This requires the --repair option to be provided. WARNING: Do not interrupt this operation as it can potentially cause unrecoverable metadata corruption. It is highly recommended to create a backup of the raw namespace before attempting this. -f:: --force:: Unless this option is specified, a check-namespace operation will fail if the namespace is presently active. Specifying --force causes the namespace to be disabled before checking. -v:: --verbose:: Emit debug messages for the namespace check process. -r:: --region=:: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-namespace[1], linkndctl:ndctl-enable-namespace[1], http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-67/Documentation/ndctl/ndctl-clear-errors.txt000066400000000000000000000076161355562357700226300ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-clear-errors(1) ===================== NAME ---- ndctl-clear-errors - clear all errors (badblocks) on the given namespace SYNOPSIS -------- [verse] 'ndctl clear-errors' [] DESCRIPTION ----------- A namespace may have one or more 'media errors', either known to the kernel or in a latent state. These error locations, or 'badblocks' can cause poison consumption events if read in an unsafe manner. Moreover, these badblocks also indicate that due to media corruption, any data that may have been in these locations has been unrecoverably lost. Normally, in the presence of such errors, the administrator is expected to recover the data from out of band means (such as backups), destroy the namespace, recreate it, and then restore the data. When the data is re-written, the writes will allow any errors to be cleared as they are encountered. In such a workflow, one should *never* need to use the 'clear-errors' command. However, there may be special use cases, where the data currently on the namespace does not matter - for example, if a 'devdax' mode namespace is being prepared for use as 'system-ram'. In such cases, it may be desirable to clear any errors on the namespace prior to switching its mode to prevent disruptive machine checks due to poison consumption. NOTE: *Only* use this command when the data on the namespace is immaterial. For any blocks that are cleared via this command, any data on the blocks in question will be lost, and replaced with content that is implementation (platform) defined, and unpredictable. WARNING: This is a DANGEROUS command, and should only be used after fully understanding its implications and consequences. This WILL erase your data. For namespaces in one of 'fsdax' or 'devdax' modes, this command will only consider the 'data' area for error clearing. Namespace metadata, such as info-blocks, will not be touched. For namespaces in 'raw' mode, the full available capacity of the namespace is considered for error clearing. Namespaces that are in 'sector' mode are not supported, and will be skipped. NOTE: It is expected that the command is run with the namespace 'enabled'. A namespace in the 'disabled' state will appear as, and will be treated as a 'raw' namespace, and error clearing will be performed for the full available capacity of the namespace, including any potential metadata areas. If there happen to be errors in the metadata area, clearing them may result in unpredictable outcomes. You have been warned! Known errors are ones that the kernel has encountered before, either via a previous scrub, or by an attempted read from those locations. These can be listed by running 'ndctl list --media-errors' for a given namespace. Latent errors, as the name indicates, are unknown to the kernel. These can be found by running a scrub operation on the NVDIMMs in question. By default, the ndctl-clear-errors command only clears known errors. This can be overridden using the '--scrub' option to clear *all* errors. NOTE: If a scrub is in progress when the command is called, it will unconditionally wait for it to complete. EXAMPLES -------- Clear errors on namespace 0.0 [verse] ndctl clear-errors namespace0.0 Clear errors on all namespaces belonging to region1, including scrubbing for latent errors [verse] ndctl clear-errors --scrub --region=region1 all OPTIONS ------- -s:: --scrub:: Perform a 'scrub' on the bus prior to clearing errors. This allows for the clearing of any latent media errors in addition to errors the kernel already knows about. NOTE: This will cause the command to start and wait for a full scrub, and this can potentially be a very long-running operation. -v:: --verbose:: Emit debug messages. -r:: --region=:: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-start-scrub[1], linkndctl:ndctl-list[1] ndctl-67/Documentation/ndctl/ndctl-create-namespace.txt000066400000000000000000000213231355562357700234140ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-create-namespace(1) ========================= NAME ---- ndctl-create-namespace - provision or reconfigure a namespace SYNOPSIS -------- [verse] 'ndctl create-namespace' [] include::namespace-description.txt[] EXAMPLES -------- Create a maximally sized pmem namespace in 'fsdax' mode (the default) [verse] ndctl create-namespace Convert namespace0.0 to 'sector' mode [verse] ndctl create-namespace -f -e namespace0.0 --mode=sector OPTIONS ------- -t:: --type=:: Create a 'pmem' or 'blk' namespace (subject to available capacity). A pmem namespace supports the dax (direct access) capability to linkndctl:mmap[2] persistent memory directly into a process address space. A blk namespace access persistent memory through a block-window-aperture. Compared to pmem it supports a traditional storage error model (EIO on error rather than a cpu exception on a bad memory access), but it does not support dax. -m:: --mode=:: - "raw": expose the namespace capacity directly with limitations. Neither a raw pmem namepace nor raw blk namespace support sector atomicity by default (see "sector" mode below). A raw pmem namespace may have limited to no dax support depending the kernel. In other words operations like direct-I/O targeting a dax buffer may fail for a pmem namespace in raw mode or indirect through a page-cache buffer. See "fsdax" and "devdax" mode for dax operation. - "sector": persistent memory, given that it is byte addressable, does not support sector atomicity. The problematic aspect of sector tearing is that most applications do not know they have a atomic sector update dependency. At least a disk rarely ever tears sectors and if it does it almost certainly returns a checksum error on access. Persistent memory devices will always tear and always silently. Until an application is audited to be robust in the presence of sector-tearing "safe" mode is recommended. This imposes some performance overhead and disables the dax capability. (also known as "safe" or "btt" mode) - "fsdax": A pmem namespace in this mode supports dax operation with a block-device based filesystem (in previous ndctl releases this mode was named "memory" mode). This mode comes at the cost of allocating per-page metadata. The capacity can be allocated from "System RAM", or from a reserved portion of "Persistent Memory" (see the --map= option). NOTE: A filesystem that supports DAX is required for dax operation. If the raw block device (/dev/pmemX) is used directly without a filesystem, it will use the page cache. See "devdax" mode for raw device access that supports dax. - "devdax": The device-dax character device interface is a statically allocated / raw access analogue of filesystem-dax (in previous ndctl releases this mode was named "dax" mode). It allows memory ranges to be mapped without need of an intervening filesystem. The device-dax is interface strict, precise and predictable. Specifically the interface: * Guarantees fault granularity with respect to a given page size (4K, 2M, or 1G on x86) set at configuration time. * Enforces deterministic behavior by being strict about what fault scenarios are supported. I.e. if a device is configured with a 2M alignment an attempt to fault a 4K aligned offset will result in SIGBUS. -s:: --size=:: For NVDIMM devices that support namespace labels, set the namespace size in bytes. Otherwise it defaults to the maximum size specified by platform firmware. This option supports the suffixes "k" or "K" for KiB, "m" or "M" for MiB, "g" or "G" for GiB and "t" or "T" for TiB. For pmem namepsaces the size must be a multiple of the interleave-width and the namespace alignment (see below). -a:: --align:: Applications that want to establish dax memory mappings with page table entries greater than system base page size (4K on x86) need a persistent memory namespace that is sufficiently aligned. For "fsdax" and "devdax" mode this defaults to 2M. Note that "devdax" mode enforces all mappings to be aligned to this value, i.e. it fails unaligned mapping attempts. The "fsdax" alignment setting determines the starting alignment of filesystem extents and may limit the possible granularities, if a large mapping is not possible it will silently fall back to a smaller page size. -e:: --reconfig=:: Reconfigure an existing namespace (change the mode, sector size, etc...). All namespace parameters, save uuid, default to the current attributes of the specified namespace. The namespace is then re-created with the specified modifications. The uuid is refreshed to a new value by default whenever the data layout of a namespace is changed, see --uuid= to set a specific uuid. -u:: --uuid=:: This option is not recommended as a new uuid should be generated every time a namespace is (re-)created. For recovery scenarios however the uuid may be specified. -n:: --name=:: For NVDIMM devices that support namespace labels, specify a human friendly name for a namespace. This name is available as a device attribute for use in udev rules. -l:: --sector-size:: Specify the logical sector size (LBA size) of the Linux block device associated with an namespace. -M:: --map=:: A pmem namespace in "fsdax" or "devdax" mode requires allocation of per-page metadata. The allocation can be drawn from either: - "mem": typical system memory - "dev": persistent memory reserved from the namespace Given relative capacities of "Persistent Memory" to "System RAM" the allocation defaults to reserving space out of the namespace directly ("--map=dev"). The overhead is 64-bytes per 4K (16GB per 1TB) on x86. -c:: --continue:: Do not stop after creating one namespace. Instead, greedily create as many namespaces as possible within the given --bus and --region filter restrictions. This will abort if any creation attempt results in an error unless --force is also supplied. -f:: --force:: Unless this option is specified the 'reconfigure namespace' operation will fail if the namespace is presently active. Specifying --force causes the namespace to be disabled before the operation is attempted. However, if the namespace is mounted then the 'disable namespace' and 'reconfigure namespace' operations will be aborted. The namespace must be unmounted before being reconfigured. When used in conjunction with --continue, continue the namespace creation loop even if an error is encountered for intermediate namespaces. -L:: --autolabel:: --no-autolabel:: Legacy NVDIMM devices do not support namespace labels. In that case the kernel creates region-sized namespaces that can not be deleted. Their mode can be changed, but they can not be resized smaller than their parent region. This is termed a "label-less namespace". In contrast, NVDIMMs and hypervisors that support the ACPI 6.2 label area definition (ACPI 6.2 Section 6.5.10 NVDIMM Label Methods) support "labelled namespace" operation. - There are two cases where the kernel will default to label-less operation: * NVDIMM does not support labels * The NVDIMM supports labels, but the Label Index Block (see UEFI 2.7) is not present and there is no capacity aliasing between 'blk' and 'pmem' regions. - In the latter case the configuration can be upgraded to labelled operation by writing an index block on all DIMMs in a region and re-enabling that region. The 'autolabel' capability of 'ndctl create-namespace --reconfig' tries to do this by default if it can determine that all DIMM capacity is referenced by the namespace being reconfigured. It will otherwise fail to autolabel and remain in label-less mode if it finds a DIMM contributes capacity to more than one region. This check prevents inadvertent data loss of that other region is in active use. The --autolabel option is implied by default, the --no-autolabel option can be used to disable this behavior. When automatic labeling fails and labelled operation is still desired the safety policy can be bypassed by the following commands, note that all data on all regions is forfeited by running these commands: ndctl disable-region all ndctl init-labels all ndctl enable-region all -v:: --verbose:: Emit debug messages for the namespace creation process -r:: --region=:: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-zero-labels[1], linkndctl:ndctl-init-labels[1], linkndctl:ndctl-disable-namespace[1], linkndctl:ndctl-enable-namespace[1], http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] https://nvdimm.wiki.kernel.org[Linux Persistent Memory Wiki] ndctl-67/Documentation/ndctl/ndctl-destroy-namespace.txt000066400000000000000000000014561355562357700236470ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-destroy-namespace(1) ========================= NAME ---- ndctl-destroy-namespace - destroy the given namespace(s) SYNOPSIS -------- [verse] 'ndctl destroy-namespace' [] include::namespace-description.txt[] OPTIONS ------- include::xable-namespace-options.txt[] -f:: --force:: Unless this option is specified the 'destroy namespace' operation will fail if the namespace is presently active. Specifying --force causes the namespace to be disabled before the operation is attempted. However, if the namespace is mounted then the 'disable namespace' and 'destroy namespace' operations will be aborted. The namespace must be unmounted before being destroyed. include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-create-namespace[1] ndctl-67/Documentation/ndctl/ndctl-disable-dimm.txt000066400000000000000000000006571355562357700225550ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-disable-dimm(1) ===================== NAME ---- ndctl-disable-dimm - disable one or more idle dimms SYNOPSIS -------- [verse] 'ndctl disable-dimm' [] include::dimm-description.txt[] OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-enable-dimm[1] ndctl-67/Documentation/ndctl/ndctl-disable-namespace.txt000066400000000000000000000006361355562357700235600ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-disable-namespace(1) ========================= NAME ---- ndctl-disable-namespace - disable the given namespace(s) SYNOPSIS -------- [verse] 'ndctl disable-namespace' [] include::namespace-description.txt[] OPTIONS ------- include::xable-namespace-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-namespace[1] ndctl-67/Documentation/ndctl/ndctl-disable-region.txt000066400000000000000000000007341355562357700231060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-disable-region(1) ======================= NAME ---- ndctl-disable-region - disable the given region(s) and all descendant namespaces SYNOPSIS -------- [verse] 'ndctl disable-region' [] include::region-description.txt[] OPTIONS ------- :: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-enable-region[1] ndctl-67/Documentation/ndctl/ndctl-enable-dimm.txt000066400000000000000000000006431355562357700223730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-enable-dimm(1) ==================== NAME ---- ndctl-enable-dimm - enable one more dimms SYNOPSIS -------- [verse] 'ndctl enable-dimm' [] include::dimm-description.txt[] OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-dimm[1] ndctl-67/Documentation/ndctl/ndctl-enable-namespace.txt000066400000000000000000000006321355562357700233770ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-enable-namespace(1) ========================= NAME ---- ndctl-enable-namespace - enable the given namespace(s) SYNOPSIS -------- [verse] 'ndctl enable-namespace' [] include::namespace-description.txt[] OPTIONS ------- include::xable-namespace-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-namespace[1] ndctl-67/Documentation/ndctl/ndctl-enable-region.txt000066400000000000000000000007301355562357700227250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-enable-region(1) ====================== NAME ---- ndctl-enable-region - enable the given region(s) and all descendant namespaces SYNOPSIS -------- [verse] 'ndctl enable-region' [] include::region-description.txt[] OPTIONS ------- :: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-region[1] ndctl-67/Documentation/ndctl/ndctl-freeze-security.txt000066400000000000000000000022271355562357700233460ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-freeze-security(1) ======================== NAME ---- ndctl-freeze-security - Set the given DIMM(s) to reject future security operations SYNOPSIS -------- [verse] 'ndctl freeze-security' [..] [] DESCRIPTION ----------- Prevent any further security operations on the given DIMMs until the next reboot. This is used in scenarios where the administrator has taken all expected security actions for the current boot and wants the DIMM to enforce / lock the current state. EXAMPLES -------- ---- $ ndctl list -d nmem0 [ { "dev":"nmem0", "id":"cdab-0a-07e0-ffffffff", "handle":0, "phys_id":0, "security":"unlocked" } ] $ ndctl freeze-security nmem0 security froze 1 nmem. $ ndctl list -d nmem0 [ { "dev":"nmem0", "id":"cdab-0a-07e0-ffffffff", "handle":0, "phys_id":0, "security":"unlocked", "security_frozen":true }, ] ---- OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -v:: --verbose:: Emit debug messages. include::intel-nvdimm-security.txt[] include::../copyright.txt[] ndctl-67/Documentation/ndctl/ndctl-init-labels.txt000066400000000000000000000040401355562357700224170ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-init-labels(1) ==================== NAME ---- ndctl-init-labels - initialize the label data area on a dimm or set of dimms SYNOPSIS -------- [verse] 'ndctl init-labels' [..] [] include::labels-description.txt[] By default, and in kernels prior to v4.10, the kernel only honors labels when a DIMM aliases PMEM and BLK capacity. Starting with v4.10 the kernel will honor labels for sub-dividing PMEM if all the DIMMs in an interleave set / region have a valid namespace index block. This command can be used to initialize the namespace index block if it is missing or reinitialize it if it is damaged. Note that reinitialization effectively destroys all existing namespace labels on the DIMM. EXAMPLE ------- Find the DIMMs that comprise a given region: ---- # ndctl list -RD --region=region1 { "dimms":[ { "dev":"nmem0", "id":"8680-56341200" } ], "regions":[ { "dev":"region1", "size":268435456, "available_size":0, "type":"pmem", "mappings":[ { "dimm":"nmem0", "offset":13958643712, "length":268435456 } ] } ] } ---- Disable that region so the DIMM label area can be written from userspace: ---- # ndctl disable-region region1 ---- Initialize labels: ---- # ndctl init-labels nmem0 ---- Re-enable the region: ---- # ndctl enable-region region1 ---- Create a namespace in that region: ---- # ndctl create-namespace --region=region1 ---- OPTIONS ------- include::labels-options.txt[] -f:: --force:: Force initialization of the label space even if there appears to be an existing / valid namespace index. Warning, this will destroy all defined namespaces on the dimm. -V:: --label-version:: Initialize with a specific version of labels from the namespace label specification. Defaults to 1.1 include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-create-namespace[1], http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-67/Documentation/ndctl/ndctl-inject-error.txt000066400000000000000000000077401355562357700226310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-inject-error(1) ===================== NAME ---- ndctl-inject-error - inject media errors at a namespace offset SYNOPSIS -------- [verse] 'ndctl inject-error' [] include::namespace-description.txt[] ndctl-inject-error can be used to ask the platform to simulate media errors in the NVDIMM address space to aid debugging and development of features related to error handling. By default, injecting an error actually only injects an error to the first 'n' bytes of the block, where 'n' is the output of ndctl_cmd_ars_cap_get_size(). In other words, we only inject one 'ars_unit' per sector. This is sufficient for Linux to mark the whole sector as bad, and will show up as such in the various 'badblocks' lists in the kernel. If multiple blocks are being injected, only the first 'n' bytes of each block specified will be injected as errors. This can be overridden by the --saturate option, which will force the entire block to be injected as an error. WARNING: These commands are DANGEROUS and can cause data loss. They are only provided for testing and debugging purposes. EXAMPLES -------- Inject errors in namespace0.0 at block 12 for 2 blocks (i.e. 12, 13) [verse] ndctl inject-error --block=12 --count=2 namespace0.0 Check status of injected errors on namespace0.0 [verse] ndctl inject-error --status namespace0.0 Uninject errors at block 12 for 2 blocks on namespace0.0 [verse] ndctl inject-error --uninject --block=12 --count=2 namespace0.0 OPTIONS ------- -B:: --block=:: Namespace block offset in 512 byte sized blocks where the error is to be injected. NOTE: The offset is interpreted in different ways based on the "mode" of the namespace. For "raw" mode, the offset is the base namespace offset. For "fsdax" mode (i.e. a "pfn" namespace), the offset is relative to the user-visible part of the namespace, and the offset introduced by the kernel's metadata will be accounted for. For a "sector" mode namespace (i.e. a "BTT" namespace), the offset is relative to the base namespace, as the BTT translation details are internal to the kernel, and can't be accounted for while injecting errors. -n:: --count=:: Number of blocks to inject as errors. This is also in terms of fixed, 512 byte blocks. -d:: --uninject:: This option will ask the platform to remove any injected errors for the specified block offset, and count. WARNING: This will not clear the kernel's internal badblock tracking, those can only be cleared by doing a write to the affected locations. Hence use the --clear option only if you know exactly what you are doing. For normal usage, injected errors should only be cleared by doing writes. Do not expect have the original data intact after injecting an error, and clearing it using --clear - it will be lost, as the only "real" way to clear the error location is to write to it or zero it (truncate/hole-punch). -t:: --status:: This option will retrieve the status of injected errors. Note that this will not retrieve all known/latent errors (i.e. non injected ones), and is NOT equivalent to performing an Address Range Scrub. -N:: --no-notify:: This option is only valid when injecting errors. By default, the error inject command and will ask platform firmware to trigger a notification in the kernel, asking it to update its state of known errors. With this option, the error will still be injected, the kernel will not get a notification, and the error will appear as a latent media error when the location is accessed. If the platform firmware does not support this feature, this will have no effect. -S:: --saturate:: This option forces error injection or un-injection to cover the entire address range covered by the specified block(s). -v:: --verbose:: Emit debug messages for the error injection process include::human-option.txt[] -r:: --region=:: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-list[1], ndctl-67/Documentation/ndctl/ndctl-inject-smart.txt000066400000000000000000000053071355562357700226230ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-inject-smart(1) ===================== NAME ---- ndctl-inject-smart - perform smart threshold/injection operations on a DIMM SYNOPSIS -------- [verse] 'ndctl inject-smart' [] DESCRIPTION ----------- A generic DIMM device object, named /dev/nmemX, is registered for each memory device indicated in the ACPI NFIT table, or other platform NVDIMM resource discovery mechanism. ndctl-inject-smart can be used to set smart thresholds, and inject smart attributes. EXAMPLES -------- Set smart controller temperature and spares threshold for DIMM-0 to 32C, spares threshold to 8, and enable the spares alarm. [verse] ndctl inject-smart --ctrl-temperature-threshold=32 --spares-threshold=8 --spares-alarm nmem0 Inject a media temperature value of 52 and fatal health status flag for DIMM-0 [verse] ndctl inject-smart --media-temperature=52 --health=fatal nmem0 OPTIONS ------- -b:: --bus=:: Enforce that the operation only be carried on devices that are attached to the given bus. Where 'bus' can be a provider name or a bus id number. -m:: --media-temperature=:: Inject for the media temperature smart attribute. -M:: --media-temperature-threshold=:: Set for the smart media temperature threshold. --media-temperature-alarm=:: Enable or disable the smart media temperature alarm. Options are 'on' or 'off'. --media-temperature-uninject:: Uninject any media temperature previously injected. -c:: --ctrl-temperature=:: Inject for the controller temperature smart attribute. -C:: --ctrl-temperature-threshold=:: Set for the smart controller temperature threshold. --ctrl-temperature-alarm=:: Enable or disable the smart controller temperature alarm. Options are 'on' or 'off'. --ctrl-temperature-uninject:: Uninject any controller temperature previously injected. -s:: --spares=:: Inject for the spares smart attribute. -S:: --spares-threshold=:: Set for the smart spares threshold. --spares-alarm=:: Enable or disable the smart spares alarm. Options are 'on' or 'off'. --spares-uninject:: Uninject any spare percentage previously injected. -f:: --fatal:: Set the flag to spoof fatal health status. --fatal-uninject:: Uninject the fatal health status flag. -U:: --unsafe-shutdown:: Set the flag to spoof an unsafe shutdown on the next power down. --unsafe-shutdown-uninject:: Uninject the unsafe shutdown flag. -N:: --uninject-all:: Uninject all possible smart fields/values irrespective of whether they have been previously injected or not. -v:: --verbose:: Emit debug messages for the error injection process include::human-option.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-list[1], ndctl-67/Documentation/ndctl/ndctl-list.txt000066400000000000000000000122711355562357700211740ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-list(1) ============= NAME ---- ndctl-list - dump the platform nvdimm device topology and attributes in json SYNOPSIS -------- [verse] 'ndctl list' [] Walk all the nvdimm buses in the system and list all attached devices along with some of their major attributes. Options can be specified to limit the output to devices of a certain class. Where the classes are buses, dimms, regions, and namespaces. By default, 'ndctl list' with no options is equivalent to: [verse] ndctl list --namespaces --bus=all --region=all EXAMPLE ------- ---- # ndctl list --buses --namespaces { "provider":"nfit_test.1", "dev":"ndbus2", "namespaces":[ { "dev":"namespace9.0", "mode":"raw", "size":33554432, "blockdev":"pmem9" } ] } { "provider":"nfit_test.0", "dev":"ndbus1" } { "provider":"e820", "dev":"ndbus0", "namespaces":[ { "dev":"namespace0.0", "mode":"fsdax", "size":8589934592, "blockdev":"pmem0" } ] } ---- OPTIONS ------- -r:: --region=:: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -d:: --dimm=:: An 'nmemX' device name, or dimm id number. The dimm id number here is X in 'nmemX'. Filter listing by devices that reference the given dimm. For example to see all namespaces comprised of storage capacity on nmem0: ---- # ndctl list --dimm=nmem0 --namespaces ---- -n:: --namespace=:: An 'namespaceX.Y' device name, or namespace region plus id tuple 'X.Y'. Limit the namespace list to the single identified device if present. -t:: --type=:: Filter listing by region type ('pmem' or 'blk') -m:: --mode=:: Filter listing by the mode ('raw', 'fsdax', 'sector' or 'devdax') of the namespace(s). -U:: --numa-node=:: Filter listing by numa node -B:: --buses:: Include bus info in the listing -D:: --dimms:: Include dimm info in the listing [verse] { "dev":"nmem0", "id":"cdab-0a-07e0-ffffffff", "handle":0, "phys_id":0, "security:":"disabled" } -H:: --health:: Include dimm health info in the listing. For example: [verse] { "dev":"nmem0", "health":{ "health_state":"non-critical", "temperature_celsius":23, "spares_percentage":75, "alarm_temperature":true, "alarm_spares":true, "temperature_threshold":40, "spares_threshold":5, "life_used_percentage":5, "shutdown_state":"clean" } } -F:: --firmware:: Include dimm firmware info in the listing. For example: [verse] { "dev":"nmem0", "firmware":{ "current_version":0, "next_version":1, "need_powercycle":true } } -X:: --device-dax:: Include device-dax ("daxregion") details when a namespace is in "devdax" mode. [verse] { "dev":"namespace0.0", "mode":"devdax", "size":4225761280, "uuid":"18ae1bbb-bb62-4efc-86df-4a5caacb5dcc", "daxregion":{ "id":0, "size":4225761280, "align":2097152, "devices":[ { "chardev":"dax0.0", "size":4225761280 } ] } } -R:: --regions:: Include region info in the listing -N:: --namespaces:: Include namespace info in the listing. Namespace info is listed by default if no other options are specified to the command. -i:: --idle:: Include idle (not enabled) devices in the listing -C:: --capabilities:: Include region capabilities in the listing, i.e. supported namespace modes and variable properties like sector sizes and alignments. -M:: --media-errors:: Include media errors (badblocks) in the listing. Note that the 'badblock_count' property is included in the listing by default when the count is non-zero, otherwise it is hidden. Also, if the namespace is in 'sector' mode the 'badblocks' listing is not included and 'badblock_count' property may include blocks that are located in metadata, or unused capacity in the namespace. Convert a 'sector' namespace into 'raw' mode to list precise 'badblocks' offsets. [verse] { "dev":"namespace7.0", "mode":"raw", "size":33554432, "blockdev":"pmem7", "badblock_count":17, "badblocks":[ { "offset":4, "length":1 }, { "offset":32768, "length":8 }, { "offset":65528, "length":8 } ] } -v:: --verbose:: Increase verbosity of the output. This can be specified multiple times to be even more verbose on the informational and miscellaneous output, and can be used to override omitted flags for showing specific information. + - *-v* In addition to the enabled namespaces default output, show the numa_node, raw_uuid, and bad block media errors. + - *-vv* Everything '-v' provides, plus automatically enable --dimms, --buses, and --regions. + - *-vvv* Everything '-vv' provides, plus --health, --capabilities, --idle, and --firmware. include::human-option.txt[] ---- # ndctl list --region=7 { "dev":"region7", "size":67108864, "available_size":67108864, "type":"pmem", "iset_id":-6382611090938810793, "badblock_count":8 } ---- ---- # ndctl list --human --region=7 { "dev":"region7", "size":"64.00 MiB (67.11 MB)", "available_size":"64.00 MiB (67.11 MB)", "type":"pmem", "iset_id":"0xa76c6907811fae57", "badblock_count":8 } ---- include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-create-namespace[1] ndctl-67/Documentation/ndctl/ndctl-load-keys.txt000066400000000000000000000030321355562357700221040ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 include::attrs.adoc[] ndctl-load-keys(1) ================== NAME ---- ndctl-load-keys - load the kek and encrypted passphrases into the keyring SYNOPSIS -------- [verse] 'ndctl load-keys' [] DESCRIPTION ----------- The 'load-keys' command loads the master key ('kek') and the encrypted passphrases for all NVDIMMs into the user keyring maintained by the kernel. The command is expected to be called during initialization and before the libnvdimm kernel module is loaded, typically from an initrd. This is typically set up using a modprobe config that calls the command before module load. NOTE: All key files are expected to be in the format: nvdimm__hostname + The `'_`' character is used to delimit the different components in the file name. Within the hostname, the `'_`' character is allowed since it is the last component of the file name. NOTE: This command is typically never called directly by a user. OPTIONS ------- -p:: --key-path=:: Path to where key related files reside. This parameter is optional and the default location is {ndctl_keysdir}. -t:: --tpm-handle=:: Provide a TPM handle (should be a string such as 0x81000001). If the key path ({ndctl_keysdir}) contains a file called tpm.handle which contains the handle string, then this option may be left out, and the tpm handle will be obtained from the file. If both are present, then this option will override (but not overwrite) anything that is in the file. include::intel-nvdimm-security.txt[] include::../copyright.txt[] ndctl-67/Documentation/ndctl/ndctl-monitor.txt000066400000000000000000000070041355562357700217060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 include::attrs.adoc[] ndctl-monitor(1) ================ NAME ---- ndctl-monitor - Monitor the smart events of nvdimm objects SYNOPSIS -------- [verse] 'ndctl monitor' [] DESCRIPTION ----------- Ndctl monitor is used for monitoring the smart events of nvdimm objects and dumping the json format notifications to syslog, standard output or a logfile. The objects to monitor and smart events to notify can be selected by setting options and/or the configuration file at {ndctl_monitorconfdir}/{ndctl_monitorconf} Both, the values in configuration file and in options will work. If there is a conflict, the values in options will override the values in the configuration file. Any updated values in the configuration file will take effect only after the monitor process is restarted. EXAMPLES -------- Run a monitor as a daemon to monitor DIMMs on bus "nfit_test.1" [verse] ndctl monitor --bus=nfit_test.1 --daemon Run a monitor as a one-shot command and output the notifications to /var/log/ndctl.log [verse] ndctl monitor --log=/var/log/ndctl.log Run a monitor daemon as a system service [verse] systemctl start ndctl-monitor.service OPTIONS ------- -b:: --bus=:: Enforce that the operation only be carried on devices that are attached to the given bus. Where 'bus' can be a provider name or a bus id number. -d:: --dimm=:: A 'nmemX' device name, or dimm id number. Select the devices to monitor reference the given dimm. -r:: --region=:: A 'regionX' device name, or a region id number. The keyword 'all' can be specified to carry out the operation on every region in the system, optionally filtered by bus id (see --bus= option). -n:: --namespace=:: A 'namespaceX.Y' device name, or namespace region plus id tuple 'X.Y'. -l:: --log=:: Send log messages to the specified destination. - "": Send log messages to specified . When fopen() is not able to open , log messages will be forwarded to syslog. - "syslog": Send messages to syslog. - "standard": Send messages to standard output. The default log destination is 'syslog' if "--daemon" is specified, otherwise 'standard'. Note that standard and relative path for will not work if "--daemon" is specified. -c:: --config-file=:: Provide the config file to use. This overrides the default config typically found in {ndctl_monitorconfdir} --daemon:: Run a monitor as a daemon. -D:: --dimm-event=:: Name of an smart health event from the following: - "dimm-spares-remaining": Spare Blocks Remaining value has gone below the pre-programmed threshold. - "dimm-media-temperature": NVDIMM Media temperature value has gone above the pre-programmed threshold. - "dimm-controller-temperature": NVDIMM Controller temperature value has gone above the pre-programmed threshold. - "dimm-health-state": NVDIMM Normal Health Status has changed - "dimm-unclean-shutdown": NVDIMM Last Shutdown Status was a unclean shutdown. The monitor will attempt to enable the alarm control bits for all specified events. -u:: --human:: Output monitor notification as human friendly json format instead of the default machine friendly json format. -v:: --verbose:: Emit extra debug messages to log. COPYRIGHT --------- Copyright (c) 2018, FUJITSU LIMITED. License GPLv2: GNU GPL version 2 . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. SEE ALSO -------- linkndctl:ndctl-list[1], linkndctl:ndctl-inject-smart[1] ndctl-67/Documentation/ndctl/ndctl-read-labels.txt000066400000000000000000000022521355562357700223720ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-read-labels(1) ==================== NAME ---- ndctl-read-labels - read out the label area on a dimm or set of dimms SYNOPSIS -------- [verse] 'ndctl read-labels' [..] [] include::labels-description.txt[] This command dumps the raw binary data in a dimm's label area to stdout or a file. In the multi-dimm case the data is concatenated. OPTIONS ------- include::labels-options.txt[] -I:: --index:: Limit the span of the label operation to just the index-block area. This is useful to determine if the dimm label area is initialized. Note that this option and --size/--offset are mutually exclusive. -o:: --output:: output file -j:: --json:: parse the label data into json assuming the 'NVDIMM Namespace Specification' format. -u:: --human:: enable json output and convert number formats to human readable strings, for example show the size in terms of "KB", "MB", "GB", etc instead of a signed 64-bit numbers per the JSON interchange format (implies --json). include::../copyright.txt[] SEE ALSO -------- http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-67/Documentation/ndctl/ndctl-remove-passphrase.txt000066400000000000000000000016021355562357700236610ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-remove-passphrase(1) =========================== NAME ---- ndctl-remove-passphrase - Stop a DIMM from locking at power-loss and requiring a passphrase to access media SYNOPSIS -------- [verse] 'ndctl remove-passphrase' [..] [] DESCRIPTION ----------- Search the user keyring for an encrypted passphrase for the NVDIMM in question. If not found, attempt to load the passphrase blob. After disabling the passphrase, remove the 'key-ID' from the keyring as well as the passphrase blob from the file system. OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -v:: --verbose:: Emit debug messages. include::intel-nvdimm-security.txt[] include::../copyright.txt[] SEE ALSO: --------- linkndctl:ndctl-setup-passphrase[1], linkndctl:ndctl-update-passphrase[1] ndctl-67/Documentation/ndctl/ndctl-sanitize-dimm.txt000066400000000000000000000044511355562357700227740ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 include::attrs.adoc[] ndctl-sanitize-dimm(1) ====================== NAME ---- ndctl-sanitize-dimm - Perform a cryptographic destruction or overwrite of the contents of the given NVDIMM(s) SYNOPSIS -------- [verse] 'ndctl sanitize-dimm' [..] [] DESCRIPTION ----------- The 'sanitize-dimm' command performs a cryptographic destruction of the contents of the given NVDIMM. It scrambles the data, and any metadata or info-blocks, but it doesn't modify namespace labels. Therefore, any namespaces on regions associated with the given NVDIMM will be retained, but they will end up in the 'raw' mode. Additionally, after completion of this command, the security and passphrase for the given NVDIMM will be disabled, and the passphrase and any key material will also be removed from the keyring and the ndctl keys directory at {ndctl_keysdir} The command supports two different methods of performing the cryptographic erase. The default is 'crypto-erase', but additionally, an 'overwrite' option is available which overwrites not only the data area, but also the label area, thus losing record of any namespaces the given NVDIMM participates in. OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -c:: --crypto-erase:: Replace the media encryption key on the NVDIMM causing all existing data to read as cipher text with the new key. This does not change label data. Namespaces get reverted to raw mode. -o:: --ovewrite:: Wipe the entire DIMM, including label data. This can take significant time, and the command is non-blocking. With this option, the overwrite request is merely submitted to the NVDIMM, and the completion is asynchronous. Depending on the medium and capacity, overwrite may take tens of minutes to many hours. -m:: --master-passphrase:: Indicate that we are using the master passphrase to perform the erase. This only is applicable to the 'crypto-erase' option. -z:: --zero-key:: Passing in a key with payload that is just 0's. --verbose:: Emit debug messages. include::intel-nvdimm-security.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-wait-overwrite[1], https://trustedcomputinggroup.org/wp-content/uploads/TCG_SWG_SIIS_Version_1_07_Revision_1_00.pdf ndctl-67/Documentation/ndctl/ndctl-setup-passphrase.txt000066400000000000000000000032131355562357700235240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 include::attrs.adoc[] ndctl-setup-passphrase(1) ========================= NAME ---- ndctl-setup-passphrase - setup and enable the security passphrase for an NVDIMM SYNOPSIS -------- [verse] 'ndctl setup-passphrase' [..] -k [] DESCRIPTION ----------- Setup and enable a security passphrase for one or more NVDIMMs. For this command to succeed, it is expected that the master key has previously been loaded into the user keyring. More information on how this can be done can be found in the kernel documentation at: https://www.kernel.org/doc/html/latest/security/keys/trusted-encrypted.html The passphrase blobs are created in the {ndctl_keysdir} directory with a file name format of `"nvdimm__.blob"` The command will fail if the passphrase is already in the user keyring or if a passphrase blob already exists in {ndctl_keysdir}. OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -k:: --key_handle=:: Handle for the master 'kek' (key-encryption-key) that will be used for sealing the passphrase(s) for the given DIMM(s). The format is: `:` e.g. `trusted:nvdimm-master` + NOTE: The 'kek' is expected to have been loaded into the user keyring. -m:: --master-passphrase:: Indicates that we are managing the master passphrase instead of the user passphrase. -v:: --verbose:: Emit debug messages. include::intel-nvdimm-security.txt[] include::../copyright.txt[] SEE ALSO: --------- linkndctl:ndctl-update-passphrase[1], linkndctl:ndctl-remove-passphrase[1] ndctl-67/Documentation/ndctl/ndctl-start-scrub.txt000066400000000000000000000026531355562357700224750ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-start-scrub(1) ==================== NAME ---- ndctl-start-scrub - start an Address Range Scrub (ARS) operation SYNOPSIS -------- [verse] 'ndctl start-scrub' [ ... ] [] include::ars-description.txt[] The kernel provides a sysfs file ('scrub') that when written with the string "1\n" initiates an ARS operation. The 'ndctl start-scrub' operation starts an ARS, across all specified buses, and the kernel in turn proceeds to scrub every persistent memory address region on the specified buses. EXAMPLE ------- Start a scrub on all nvdimm buses in the system. The json listing report only includes the buses that support ARS operations. ---- # ndctl start-scrub [ { "provider":"nfit_test.1", "dev":"ndbus3", "scrub_state":"active" }, { "provider":"nfit_test.0", "dev":"ndbus2", "scrub_state":"active" } ] ---- When specifying an individual bus, or if there is only one bus in the system, the command reports whether ARS support is available. ---- # ndctl start-scrub e820 error starting scrub: Operation not supported ---- OPTIONS ------- -v:: --verbose:: Emit debug messages for the ARS start process include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-wait-scrub[1], http://www.uefi.org/sites/default/files/resources/ACPI%206_2_A_Sept29.pdf[ACPI 6.2 Specification Section 9.20.7.2 Address Range Scrubbing (ARS) Overview] ndctl-67/Documentation/ndctl/ndctl-update-firmware.txt000066400000000000000000000013021355562357700233060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-update-firmware(1) ======================== NAME ---- ndctl-update-firmware - provides for updating the firmware on an NVDIMM SYNOPSIS -------- [verse] 'ndctl update-firmware' [] DESCRIPTION ----------- Provide a generic interface for updating NVDIMM firmware. The use of this depends on support from the underlying libndctl, kernel, as well as the platform itself. OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -f:: --firmware:: firmware file used to perform the update -v:: --verbose:: Emit debug messages for the namespace check process. include::../copyright.txt[] ndctl-67/Documentation/ndctl/ndctl-update-passphrase.txt000066400000000000000000000026501355562357700236520ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 include::attrs.adoc[] ndctl-update-passphrase(1) ========================== NAME ---- ndctl-update-passphrase - update the security passphrase for an NVDIMM SYNOPSIS -------- [verse] 'ndctl update-passphrase' [..] [] DESCRIPTION ----------- Update the security passphrase for one or more NVDIMMs. Prerequisites for command to succeed: . The 'kek' has been loaded into the kernel's user keyring. . setup-passphrase has successfully been executed on the NVDIMM. * Alternatively, the NVDIMM is unlocked. The updated key blobs will be created in the {ndctl_keysdir} directory with a file name format of "nvdimm__.blob". OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -k:: --key_handle=:: Handle for the master 'kek' (key-encryption-key) that will be used for sealing the passphrase(s) for the given DIMM(s). The format is: `:` e.g. `trusted:nvdimm-master` + NOTE: The 'kek' is expected to have been loaded into the user keyring. -m:: --master-passphrase:: Indicates that we are managing the master passphrase instead of the user passphrase. -v:: --verbose:: Emit debug messages. include::intel-nvdimm-security.txt[] include::../copyright.txt[] SEE ALSO: --------- linkndctl:ndctl-setup-passphrase[1], linkndctl:ndctl-remove-passphrase[1] ndctl-67/Documentation/ndctl/ndctl-wait-overwrite.txt000066400000000000000000000013301355562357700232030ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-wait-overwrite(1) ======================= NAME ---- ndctl-wait-overwrite - wait for an overwrite operation to complete SYNOPSIS -------- [verse] 'ndctl wait-overwrite' [..] [] DESCRIPTION ----------- The kernel provides a POLL(2) capable sysfs file ('security') to indicate the state of overwrite. This command waits for a change in the state of this file across all specified dimms. OPTIONS ------- :: include::xable-dimm-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -v:: --verbose:: Emit debug messages. include::intel-nvdimm-security.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-sanitize-dimm[1] ndctl-67/Documentation/ndctl/ndctl-wait-scrub.txt000066400000000000000000000030441355562357700222770ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-wait-scrub(1) ==================== NAME ---- ndctl-wait-scrub - wait for an Address Range Scrub (ARS) operation to complete SYNOPSIS -------- [verse] 'ndctl wait-scrub' [ ... ] [] include::ars-description.txt[] The kernel provides a POLL(2) capable sysfs file ('scrub') to indicate the state of ARS. The 'scrub' file maintains a running count of ARS runs that have taken place. While a current run is in progress a '+' character is emitted along with the current count. The 'ndctl wait-scrub' operation waits for 'scrub', across all specified buses, to indicate not in-progress at least once. EXAMPLE ------- Wait for scrub on all nvdimm buses in the system. The json listing report at the end only includes the buses that support ARS operations. ---- # ndctl wait-scrub [ { "provider":"nfit_test.1", "dev":"ndbus3", "scrub_state":"idle" }, { "provider":"nfit_test.0", "dev":"ndbus2", "scrub_state":"idle" } ] ---- When specifying an individual bus, or if there is only one bus in the system, the command reports whether ARS support is available. ---- # ndctl wait-scrub e820 error waiting for scrub completion: Operation not supported ---- OPTIONS ------- -v:: --verbose:: Emit debug messages for the ARS wait process include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-start-scrub[1], http://www.uefi.org/sites/default/files/resources/ACPI%206_2_A_Sept29.pdf[ACPI 6.2 Specification Section 9.20.7.2 Address Range Scrubbing (ARS) Overview] ndctl-67/Documentation/ndctl/ndctl-write-labels.txt000066400000000000000000000012361355562357700226120ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-write-labels(1) ===================== NAME ---- ndctl-write-labels - write data to the label area on a dimm SYNOPSIS -------- [verse] 'ndctl write-labels [-i ]' include::labels-description.txt[] Read data from the input filename, or stdin, and write it to the given device. Note that the device must not be active in any region, otherwise the kernel will not allow write access to the device's label data area. OPTIONS ------- include::labels-options.txt[] -i:: --input:: input file SEE ALSO -------- http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-67/Documentation/ndctl/ndctl-zero-labels.txt000066400000000000000000000010441355562357700224340ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl-zero-labels(1) ==================== NAME ---- ndctl-zero-labels - zero out the label area on a dimm or set of dimms SYNOPSIS -------- [verse] 'ndctl zero-labels' [..] [] include::labels-description.txt[] This command resets the device to its default state by deleting all labels. OPTIONS ------- include::labels-options.txt[] include::../copyright.txt[] SEE ALSO -------- http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-67/Documentation/ndctl/ndctl.txt000066400000000000000000000026431355562357700202250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 ndctl(1) ======= NAME ---- ndctl - Manage "libnvdimm" subsystem devices (Non-volatile Memory) SYNOPSIS -------- [verse] 'ndctl' [--version] [--help] [OPTIONS] COMMAND [ARGS] OPTIONS ------- -v:: --version:: Display ndctl version. -h:: --help:: Run ndctl help command. DESCRIPTION ----------- ndctl is utility for managing the "libnvdimm" kernel subsystem. The "libnvdimm" subsystem defines a kernel device model and control message interface for platform NVDIMM resources like those defined by the ACPI 6.0 NFIT (NVDIMM Firmware Interface Table). Operations supported by the tool include, provisioning capacity (namespaces), as well as enumerating/enabling/disabling the devices (dimms, regions, namespaces) associated with an NVDIMM bus. include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-create-namespace[1], linkndctl:ndctl-destroy-namespace[1], linkndctl:ndctl-check-namespace[1], linkndctl:ndctl-enable-region[1], linkndctl:ndctl-disable-region[1], linkndctl:ndctl-enable-dimm[1], linkndctl:ndctl-disable-dimm[1], linkndctl:ndctl-enable-namespace[1], linkndctl:ndctl-disable-namespace[1], linkndctl:ndctl-zero-labels[1], linkndctl:ndctl-read-labels[1], linkndctl:ndctl-inject-error[1], linkndctl:ndctl-list[1], https://www.kernel.org/doc/Documentation/nvdimm/nvdimm.txt[LIBNVDIMM Overview], http://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf[NVDIMM Driver Writer's Guide] ndctl-67/Documentation/ndctl/region-description.txt000066400000000000000000000007071355562357700227240ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 DESCRIPTION ----------- A generic REGION device is registered for each PMEM range or BLK-aperture set. LIBNVDIMM provides a built-in driver for these REGION devices. This driver is responsible for reconciling the aliased DPA mappings across all regions, parsing the LABEL, if present, and then emitting NAMESPACE devices with the resolved/exclusive DPA-boundaries for the nd_pmem or nd_blk device driver to consume. ndctl-67/Documentation/ndctl/xable-bus-options.txt000066400000000000000000000004341355562357700224700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 A bus id number, or a provider string (e.g. "ACPI.NFIT"). Restrict the operation to the specified bus(es). The keyword 'all' can be specified to indicate the lack of any restriction, however this is the same as not supplying a --bus option at all. ndctl-67/Documentation/ndctl/xable-dimm-options.txt000066400000000000000000000004171355562357700226260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 A 'nmemX' device name, or a dimm id number. Restrict the operation to the specified dimm(s). The keyword 'all' can be specified to indicate the lack of any restriction, however this is the same as not supplying a --dimm option at all. ndctl-67/Documentation/ndctl/xable-namespace-options.txt000066400000000000000000000006161355562357700236350ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 :: A 'namespaceX.Y' device name. The keyword 'all' can be specified to carry out the operation on every namespace in the system, optionally filtered by region (see --region=option) -r:: --region=:: include::xable-region-options.txt[] -b:: --bus=:: include::xable-bus-options.txt[] -v:: --verbose:: Emit debug messages for the namespace operation ndctl-67/Documentation/ndctl/xable-region-options.txt000066400000000000000000000004271355562357700231640ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 A 'regionX' device name, or a region id number. Restrict the operation to the specified region(s). The keyword 'all' can be specified to indicate the lack of any restriction, however this is the same as not supplying a --region option at all. ndctl-67/Makefile.am000066400000000000000000000042141355562357700144730ustar00rootroot00000000000000include Makefile.am.in ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} SUBDIRS = . daxctl/lib ndctl/lib ndctl daxctl if ENABLE_DOCS SUBDIRS += Documentation/ndctl Documentation/daxctl endif SUBDIRS += test BUILT_SOURCES = version.m4 version.m4: FORCE $(AM_V_GEN)$(top_srcdir)/git-version-gen FORCE: noinst_SCRIPTS = rhel/ndctl.spec sles/ndctl.spec CLEANFILES += $(noinst_SCRIPTS) do_rhel_subst = sed -e 's,VERSION,$(VERSION),g' \ -e 's,DAX_DNAME,daxctl-devel,g' \ -e 's,DNAME,ndctl-devel,g' \ -e '/^%defattr.*/d' \ -e 's,DAX_LNAME,daxctl-libs,g' \ -e 's,LNAME,ndctl-libs,g' do_sles_subst = sed -e 's,VERSION,$(VERSION),g' \ -e 's,DAX_DNAME,libdaxctl-devel,g' \ -e 's,DNAME,libndctl-devel,g' \ -e 's,%license,%doc,g' \ -e 's,\(^License:.*GPL\)v2,\1-2.0,g' \ -e "s,DAX_LNAME,libdaxctl$$(($(LIBDAXCTL_CURRENT) - $(LIBDAXCTL_AGE))),g" \ -e "s,LNAME,libndctl$$(($(LIBNDCTL_CURRENT) - $(LIBNDCTL_AGE))),g" rhel/ndctl.spec: ndctl.spec.in Makefile.am version.m4 $(AM_V_GEN)$(MKDIR_P) rhel; $(do_rhel_subst) < $< > $@ sles/ndctl.spec: ndctl.spec.in Makefile.am version.m4 $(AM_V_GEN)$(MKDIR_P) sles; cat sles/header $< | $(do_sles_subst) > $@ if ENABLE_BASH_COMPLETION bashcompletiondir = $(BASH_COMPLETION_DIR) dist_bashcompletion_DATA = contrib/ndctl endif modprobe_file = contrib/nvdimm-security.conf modprobedir = $(sysconfdir)/modprobe.d/ modprobe_DATA = $(modprobe_file) noinst_LIBRARIES = libccan.a libccan_a_SOURCES = \ ccan/str/str.h \ ccan/str/str_debug.h \ ccan/str/str.c \ ccan/str/debug.c \ ccan/list/list.h \ ccan/list/list.c \ ccan/container_of/container_of.h \ ccan/check_type/check_type.h \ ccan/build_assert/build_assert.h \ ccan/array_size/array_size.h \ ccan/minmax/minmax.h \ ccan/short_types/short_types.h \ ccan/endian/endian.h noinst_LIBRARIES += libutil.a libutil_a_SOURCES = \ util/parse-options.c \ util/parse-options.h \ util/usage.c \ util/size.c \ util/main.c \ util/help.c \ util/strbuf.c \ util/wrapper.c \ util/filter.c \ util/bitmap.c \ util/abspath.c \ util/iomem.c nobase_include_HEADERS = daxctl/libdaxctl.h ndctl-67/Makefile.am.in000066400000000000000000000016271355562357700151050ustar00rootroot00000000000000EXTRA_DIST = CLEANFILES = AM_MAKEFLAGS = --no-print-directory AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DPREFIX=\""$(prefix)"\" \ -DNDCTL_MAN_PATH=\""$(mandir)"\" \ -I${top_srcdir}/ndctl/lib \ -I${top_srcdir}/ndctl \ -I${top_srcdir}/ \ $(KMOD_CFLAGS) \ $(UDEV_CFLAGS) \ $(UUID_CFLAGS) \ $(JSON_CFLAGS) AM_CFLAGS = ${my_CFLAGS} \ -fvisibility=hidden \ -ffunction-sections \ -fdata-sections AM_LDFLAGS = \ -Wl,--gc-sections \ -Wl,--as-needed SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(SED) \ -e 's,@VERSION\@,$(VERSION),g' \ -e 's,@prefix\@,$(prefix),g' \ -e 's,@exec_prefix\@,$(exec_prefix),g' \ -e 's,@libdir\@,$(libdir),g' \ -e 's,@includedir\@,$(includedir),g' \ < $< > $@ || rm $@ LIBNDCTL_CURRENT=22 LIBNDCTL_REVISION=0 LIBNDCTL_AGE=16 LIBDAXCTL_CURRENT=5 LIBDAXCTL_REVISION=0 LIBDAXCTL_AGE=4 ndctl-67/README.md000066400000000000000000000073511355562357700137230ustar00rootroot00000000000000# ndctl [![Build Status](https://travis-ci.org/pmem/ndctl.svg?branch=master)](https://travis-ci.org/pmem/ndctl) Utility library for managing the libnvdimm (non-volatile memory device) sub-system in the Linux kernel Build ===== ``` ./autogen.sh ./configure CFLAGS='-g -O2' --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib64 make make check sudo make install ``` There are a number of packages required for the build steps that may not be installed by default. For information about the required packages, see the "BuildRequires:" lines in ndctl.spec.in. https://github.com/pmem/ndctl/blob/master/ndctl.spec.in Documentation ============= See the latest documentation for the NVDIMM kernel sub-system here: https://git.kernel.org/cgit/linux/kernel/git/nvdimm/nvdimm.git/tree/Documentation/nvdimm/nvdimm.txt?h=libnvdimm-for-next A getting started guide is also available on the kernel.org nvdimm wiki: https://nvdimm.wiki.kernel.org/start Unit Tests ========== The unit tests run by `make check` require the nfit_test.ko module to be loaded. To build and install nfit_test.ko: 1. Obtain the kernel source. For example, `git clone -b libnvdimm-for-next git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git` 1. Skip to step 3 if the kernel version is >= v4.8. Otherwise, for kernel versions < v4.8, configure the kernel to make some memory available to CMA (contiguous memory allocator). This will be used to emulate DAX. ``` CONFIG_DMA_CMA=y CONFIG_CMA_SIZE_MBYTES=200 ``` **or** `cma=200M` on the kernel command line. 1. Compile the libnvdimm sub-system as a module, make sure "zone device" memory is enabled, and enable the btt, pfn, and dax features of the sub-system: ``` CONFIG_X86_PMEM_LEGACY=m CONFIG_ZONE_DEVICE=y CONFIG_LIBNVDIMM=m CONFIG_BLK_DEV_PMEM=m CONFIG_ND_BLK=m CONFIG_BTT=y CONFIG_NVDIMM_PFN=y CONFIG_NVDIMM_DAX=y CONFIG_DEV_DAX_PMEM=m ``` 1. Build and install the unit test enabled libnvdimm modules in the following order. The unit test modules need to be in place prior to the `depmod` that runs during the final `modules_install` ``` make M=tools/testing/nvdimm sudo make M=tools/testing/nvdimm modules_install sudo make modules_install ``` 1. Now run `make check` in the ndctl source directory, or `ndctl test`, if ndctl was built with `--enable-test`. Troubleshooting =============== The unit tests will validate that the environment is set up correctly before they try to run. If the platform is misconfigured, i.e. the unit test modules are not available, or the test versions of the modules are superseded by the "in-tree/production" version of the modules `make check` will skip tests and report a message like the following in test/test-suite.log: ``` SKIP: libndctl ============== test/init: nfit_test_init: nfit.ko: appears to be production version: /lib/modules/4.8.8-200.fc24.x86_64/kernel/drivers/acpi/nfit/nfit.ko.xz __ndctl_test_skip: explicit skip test_libndctl:2684 nfit_test unavailable skipping tests ``` If the unit test modules are indeed available in the modules 'extra' directory the default depmod policy can be overridden by adding a file to /etc/depmod.d with the following contents: ``` override nfit * extra override device_dax * extra override dax_pmem * extra override dax_pmem_core * extra override dax_pmem_compat * extra override libnvdimm * extra override nd_blk * extra override nd_btt * extra override nd_e820 * extra override nd_pmem * extra ``` The nfit_test module emulates pmem with memory allocated via vmalloc(). One of the side effects is that this breaks 'physically contiguous' assumptions in the driver. Use the '--align=4K option to 'ndctl create-namespace' to avoid these corner case scenarios. ndctl-67/autogen.sh000077500000000000000000000014261355562357700144420ustar00rootroot00000000000000#!/bin/sh -e if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ chmod +x .git/hooks/pre-commit && \ echo "Activated pre-commit hook." fi $(dirname $0)/git-version-gen reconf_args='' [ -n "$*" ] && reconf_args="$*" autoreconf --install --symlink $reconf_args libdir() { echo $(cd $1/$(gcc -print-multi-os-directory); pwd) } args="--prefix=/usr \ --sysconfdir=/etc \ --libdir=$(libdir /usr/lib)" echo echo "----------------------------------------------------------------" echo "Initialized build system. For a common configuration please run:" echo "----------------------------------------------------------------" echo echo "./configure CFLAGS='-g -O2' $args" echo ndctl-67/ccan/000077500000000000000000000000001355562357700133425ustar00rootroot00000000000000ndctl-67/ccan/array_size/000077500000000000000000000000001355562357700155125ustar00rootroot00000000000000ndctl-67/ccan/array_size/LICENSE000077700000000000000000000000001355562357700212352../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/array_size/array_size.h000066400000000000000000000015711355562357700200370ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_ARRAY_SIZE_H #define CCAN_ARRAY_SIZE_H #include "config.h" #include /** * ARRAY_SIZE - get the number of elements in a visible array * @arr: the array whose size you want. * * This does not work on pointers, or arrays declared as [], or * function parameters. With correct compiler support, such usage * will cause a build error (see build_assert). */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr)) #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF /* Two gcc extensions. * &a[0] degrades to a pointer: a different type from an array */ #define _array_size_chk(arr) \ BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(arr), \ typeof(&(arr)[0]))) #else #define _array_size_chk(arr) 0 #endif #endif /* CCAN_ALIGNOF_H */ ndctl-67/ccan/build_assert/000077500000000000000000000000001355562357700160225ustar00rootroot00000000000000ndctl-67/ccan/build_assert/LICENSE000077700000000000000000000000001355562357700215452../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/build_assert/build_assert.h000066400000000000000000000023141355562357700206530ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_BUILD_ASSERT_H #define CCAN_BUILD_ASSERT_H /** * BUILD_ASSERT - assert a build-time dependency. * @cond: the compile-time condition which must be true. * * Your compile will fail if the condition isn't true, or can't be evaluated * by the compiler. This can only be used within a function. * * Example: * #include * ... * static char *foo_to_char(struct foo *foo) * { * // This code needs string to be at start of foo. * BUILD_ASSERT(offsetof(struct foo, string) == 0); * return (char *)foo; * } */ #define BUILD_ASSERT(cond) \ do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) /** * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression. * @cond: the compile-time condition which must be true. * * Your compile will fail if the condition isn't true, or can't be evaluated * by the compiler. This can be used in an expression: its value is "0". * * Example: * #define foo_to_char(foo) \ * ((char *)(foo) \ * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0)) */ #define BUILD_ASSERT_OR_ZERO(cond) \ (sizeof(char [1 - 2*!(cond)]) - 1) #endif /* CCAN_BUILD_ASSERT_H */ ndctl-67/ccan/check_type/000077500000000000000000000000001355562357700154605ustar00rootroot00000000000000ndctl-67/ccan/check_type/LICENSE000077700000000000000000000000001355562357700212032../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/check_type/check_type.h000066400000000000000000000045051355562357700177530ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_CHECK_TYPE_H #define CCAN_CHECK_TYPE_H #include "config.h" /** * check_type - issue a warning or build failure if type is not correct. * @expr: the expression whose type we should check (not evaluated). * @type: the exact type we expect the expression to be. * * This macro is usually used within other macros to try to ensure that a macro * argument is of the expected type. No type promotion of the expression is * done: an unsigned int is not the same as an int! * * check_type() always evaluates to 0. * * If your compiler does not support typeof, then the best we can do is fail * to compile if the sizes of the types are unequal (a less complete check). * * Example: * // They should always pass a 64-bit value to _set_some_value! * #define set_some_value(expr) \ * _set_some_value((check_type((expr), uint64_t), (expr))) */ /** * check_types_match - issue a warning or build failure if types are not same. * @expr1: the first expression (not evaluated). * @expr2: the second expression (not evaluated). * * This macro is usually used within other macros to try to ensure that * arguments are of identical types. No type promotion of the expressions is * done: an unsigned int is not the same as an int! * * check_types_match() always evaluates to 0. * * If your compiler does not support typeof, then the best we can do is fail * to compile if the sizes of the types are unequal (a less complete check). * * Example: * // Do subtraction to get to enclosing type, but make sure that * // pointer is of correct type for that member. * #define container_of(mbr_ptr, encl_type, mbr) \ * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ * ((encl_type *) \ * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) */ #if HAVE_TYPEOF #define check_type(expr, type) \ ((typeof(expr) *)0 != (type *)0) #define check_types_match(expr1, expr2) \ ((typeof(expr1) *)0 != (typeof(expr2) *)0) #else #include /* Without typeof, we can only test the sizes. */ #define check_type(expr, type) \ BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) #define check_types_match(expr1, expr2) \ BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) #endif /* HAVE_TYPEOF */ #endif /* CCAN_CHECK_TYPE_H */ ndctl-67/ccan/container_of/000077500000000000000000000000001355562357700160105ustar00rootroot00000000000000ndctl-67/ccan/container_of/LICENSE000077700000000000000000000000001355562357700215332../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/container_of/container_of.h000066400000000000000000000061341355562357700206330ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_CONTAINER_OF_H #define CCAN_CONTAINER_OF_H #include #include "config.h" #include /** * container_of - get pointer to enclosing structure * @member_ptr: pointer to the structure member * @containing_type: the type this member is within * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does pointer * subtraction to return the pointer to the enclosing type. * * Example: * struct foo { * int fielda, fieldb; * // ... * }; * struct info { * int some_other_field; * struct foo my_foo; * }; * * static struct info *foo_to_info(struct foo *foo) * { * return container_of(foo, struct info, my_foo); * } */ #define container_of(member_ptr, containing_type, member) \ ((containing_type *) \ ((char *)(member_ptr) \ - container_off(containing_type, member)) \ + check_types_match(*(member_ptr), ((containing_type *)0)->member)) /** * container_off - get offset to enclosing structure * @containing_type: the type this member is within * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does * typechecking and figures out the offset to the enclosing type. * * Example: * struct foo { * int fielda, fieldb; * // ... * }; * struct info { * int some_other_field; * struct foo my_foo; * }; * * static struct info *foo_to_info(struct foo *foo) * { * size_t off = container_off(struct info, my_foo); * return (void *)((char *)foo - off); * } */ #define container_off(containing_type, member) \ offsetof(containing_type, member) /** * container_of_var - get pointer to enclosing structure using a variable * @member_ptr: pointer to the structure member * @container_var: a pointer of same type as this member's container * @member: the name of this member within the structure. * * Given a pointer to a member of a structure, this macro does pointer * subtraction to return the pointer to the enclosing type. * * Example: * static struct info *foo_to_i(struct foo *foo) * { * struct info *i = container_of_var(foo, i, my_foo); * return i; * } */ #if HAVE_TYPEOF #define container_of_var(member_ptr, container_var, member) \ container_of(member_ptr, typeof(*container_var), member) #else #define container_of_var(member_ptr, container_var, member) \ ((void *)((char *)(member_ptr) - \ container_off_var(container_var, member))) #endif /** * container_off_var - get offset of a field in enclosing structure * @container_var: a pointer to a container structure * @member: the name of a member within the structure. * * Given (any) pointer to a structure and a its member name, this * macro does pointer subtraction to return offset of member in a * structure memory layout. * */ #if HAVE_TYPEOF #define container_off_var(var, member) \ container_off(typeof(*var), member) #else #define container_off_var(var, member) \ ((const char *)&(var)->member - (const char *)(var)) #endif #endif /* CCAN_CONTAINER_OF_H */ ndctl-67/ccan/endian/000077500000000000000000000000001355562357700146005ustar00rootroot00000000000000ndctl-67/ccan/endian/LICENSE000077700000000000000000000000001355562357700203232../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/endian/endian.h000066400000000000000000000220031355562357700162040ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_ENDIAN_H #define CCAN_ENDIAN_H #include #include "config.h" /** * BSWAP_16 - reverse bytes in a constant uint16_t value. * @val: constant value whose bytes to swap. * * Designed to be usable in constant-requiring initializers. * * Example: * struct mystruct { * char buf[BSWAP_16(0x1234)]; * }; */ #define BSWAP_16(val) \ ((((uint16_t)(val) & 0x00ff) << 8) \ | (((uint16_t)(val) & 0xff00) >> 8)) /** * BSWAP_32 - reverse bytes in a constant uint32_t value. * @val: constant value whose bytes to swap. * * Designed to be usable in constant-requiring initializers. * * Example: * struct mystruct { * char buf[BSWAP_32(0xff000000)]; * }; */ #define BSWAP_32(val) \ ((((uint32_t)(val) & 0x000000ff) << 24) \ | (((uint32_t)(val) & 0x0000ff00) << 8) \ | (((uint32_t)(val) & 0x00ff0000) >> 8) \ | (((uint32_t)(val) & 0xff000000) >> 24)) /** * BSWAP_64 - reverse bytes in a constant uint64_t value. * @val: constantvalue whose bytes to swap. * * Designed to be usable in constant-requiring initializers. * * Example: * struct mystruct { * char buf[BSWAP_64(0xff00000000000000ULL)]; * }; */ #define BSWAP_64(val) \ ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \ | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \ | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \ | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \ | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \ | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \ | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \ | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56)) #if HAVE_BYTESWAP_H #include #else /** * bswap_16 - reverse bytes in a uint16_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 4 as two bytes reversed" * printf("1024 is %u as two bytes reversed\n", bswap_16(1024)); */ static inline uint16_t bswap_16(uint16_t val) { return BSWAP_16(val); } /** * bswap_32 - reverse bytes in a uint32_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 262144 as four bytes reversed" * printf("1024 is %u as four bytes reversed\n", bswap_32(1024)); */ static inline uint32_t bswap_32(uint32_t val) { return BSWAP_32(val); } #endif /* !HAVE_BYTESWAP_H */ #if !HAVE_BSWAP_64 /** * bswap_64 - reverse bytes in a uint64_t value. * @val: value whose bytes to swap. * * Example: * // Output contains "1024 is 1125899906842624 as eight bytes reversed" * printf("1024 is %llu as eight bytes reversed\n", * (unsigned long long)bswap_64(1024)); */ static inline uint64_t bswap_64(uint64_t val) { return BSWAP_64(val); } #endif /* Sanity check the defines. We don't handle weird endianness. */ #if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN #error "Unknown endian" #elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN #error "Can't compile for both big and little endian." #endif #ifdef __CHECKER__ /* sparse needs forcing to remove bitwise attribute from ccan/short_types */ #define ENDIAN_CAST __attribute__((force)) #define ENDIAN_TYPE __attribute__((bitwise)) #else #define ENDIAN_CAST #define ENDIAN_TYPE #endif typedef uint64_t ENDIAN_TYPE leint64_t; typedef uint64_t ENDIAN_TYPE beint64_t; typedef uint32_t ENDIAN_TYPE leint32_t; typedef uint32_t ENDIAN_TYPE beint32_t; typedef uint16_t ENDIAN_TYPE leint16_t; typedef uint16_t ENDIAN_TYPE beint16_t; #if HAVE_LITTLE_ENDIAN /** * CPU_TO_LE64 - convert a constant uint64_t value to little-endian * @native: constant to convert */ #define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)(native)) /** * CPU_TO_LE32 - convert a constant uint32_t value to little-endian * @native: constant to convert */ #define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)(native)) /** * CPU_TO_LE16 - convert a constant uint16_t value to little-endian * @native: constant to convert */ #define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)(native)) /** * LE64_TO_CPU - convert a little-endian uint64_t constant * @le_val: little-endian constant to convert */ #define LE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val)) /** * LE32_TO_CPU - convert a little-endian uint32_t constant * @le_val: little-endian constant to convert */ #define LE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val)) /** * LE16_TO_CPU - convert a little-endian uint16_t constant * @le_val: little-endian constant to convert */ #define LE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val)) #else /* ... HAVE_BIG_ENDIAN */ #define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)BSWAP_64(native)) #define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)BSWAP_32(native)) #define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)BSWAP_16(native)) #define LE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val) #define LE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val) #define LE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val) #endif /* HAVE_BIG_ENDIAN */ #if HAVE_BIG_ENDIAN /** * CPU_TO_BE64 - convert a constant uint64_t value to big-endian * @native: constant to convert */ #define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)(native)) /** * CPU_TO_BE32 - convert a constant uint32_t value to big-endian * @native: constant to convert */ #define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)(native)) /** * CPU_TO_BE16 - convert a constant uint16_t value to big-endian * @native: constant to convert */ #define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)(native)) /** * BE64_TO_CPU - convert a big-endian uint64_t constant * @le_val: big-endian constant to convert */ #define BE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val)) /** * BE32_TO_CPU - convert a big-endian uint32_t constant * @le_val: big-endian constant to convert */ #define BE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val)) /** * BE16_TO_CPU - convert a big-endian uint16_t constant * @le_val: big-endian constant to convert */ #define BE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val)) #else /* ... HAVE_LITTLE_ENDIAN */ #define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)BSWAP_64(native)) #define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)BSWAP_32(native)) #define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)BSWAP_16(native)) #define BE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val) #define BE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val) #define BE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val) #endif /* HAVE_LITTE_ENDIAN */ /** * cpu_to_le64 - convert a uint64_t value to little-endian * @native: value to convert */ static inline leint64_t cpu_to_le64(uint64_t native) { return CPU_TO_LE64(native); } /** * cpu_to_le32 - convert a uint32_t value to little-endian * @native: value to convert */ static inline leint32_t cpu_to_le32(uint32_t native) { return CPU_TO_LE32(native); } /** * cpu_to_le16 - convert a uint16_t value to little-endian * @native: value to convert */ static inline leint16_t cpu_to_le16(uint16_t native) { return CPU_TO_LE16(native); } /** * le64_to_cpu - convert a little-endian uint64_t value * @le_val: little-endian value to convert */ static inline uint64_t le64_to_cpu(leint64_t le_val) { return LE64_TO_CPU(le_val); } /** * le32_to_cpu - convert a little-endian uint32_t value * @le_val: little-endian value to convert */ static inline uint32_t le32_to_cpu(leint32_t le_val) { return LE32_TO_CPU(le_val); } /** * le16_to_cpu - convert a little-endian uint16_t value * @le_val: little-endian value to convert */ static inline uint16_t le16_to_cpu(leint16_t le_val) { return LE16_TO_CPU(le_val); } /** * cpu_to_be64 - convert a uint64_t value to big endian. * @native: value to convert */ static inline beint64_t cpu_to_be64(uint64_t native) { return ((ENDIAN_CAST beint64_t)BSWAP_64(native)); return CPU_TO_BE64(native); } /** * cpu_to_be32 - convert a uint32_t value to big endian. * @native: value to convert */ static inline beint32_t cpu_to_be32(uint32_t native) { return CPU_TO_BE32(native); } /** * cpu_to_be16 - convert a uint16_t value to big endian. * @native: value to convert */ static inline beint16_t cpu_to_be16(uint16_t native) { return CPU_TO_BE16(native); } /** * be64_to_cpu - convert a big-endian uint64_t value * @be_val: big-endian value to convert */ static inline uint64_t be64_to_cpu(beint64_t be_val) { return BE64_TO_CPU(be_val); } /** * be32_to_cpu - convert a big-endian uint32_t value * @be_val: big-endian value to convert */ static inline uint32_t be32_to_cpu(beint32_t be_val) { return BE32_TO_CPU(be_val); } /** * be16_to_cpu - convert a big-endian uint16_t value * @be_val: big-endian value to convert */ static inline uint16_t be16_to_cpu(beint16_t be_val) { return BE16_TO_CPU(be_val); } /* Whichever they include first, they get these definitions. */ #ifdef CCAN_SHORT_TYPES_H /** * be64/be32/be16 - 64/32/16 bit big-endian representation. */ typedef beint64_t be64; typedef beint32_t be32; typedef beint16_t be16; /** * le64/le32/le16 - 64/32/16 bit little-endian representation. */ typedef leint64_t le64; typedef leint32_t le32; typedef leint16_t le16; #endif #endif /* CCAN_ENDIAN_H */ ndctl-67/ccan/list/000077500000000000000000000000001355562357700143155ustar00rootroot00000000000000ndctl-67/ccan/list/LICENSE000077700000000000000000000000001355562357700205322../../licenses/BSD-MITustar00rootroot00000000000000ndctl-67/ccan/list/list.c000066400000000000000000000017501355562357700154370ustar00rootroot00000000000000/* Licensed under BSD-MIT - see LICENSE file for details */ #include #include #include "list.h" static void *corrupt(const char *abortstr, const struct list_node *head, const struct list_node *node, unsigned int count) { if (abortstr) { fprintf(stderr, "%s: prev corrupt in node %p (%u) of %p\n", abortstr, node, count, head); abort(); } return NULL; } struct list_node *list_check_node(const struct list_node *node, const char *abortstr) { const struct list_node *p, *n; int count = 0; for (p = node, n = node->next; n != node; p = n, n = n->next) { count++; if (n->prev != p) return corrupt(abortstr, node, n, count); } /* Check prev on head node. */ if (node->prev != p) return corrupt(abortstr, node, node, 0); return (struct list_node *)node; } struct list_head *list_check(const struct list_head *h, const char *abortstr) { if (!list_check_node(&h->n, abortstr)) return NULL; return (struct list_head *)h; } ndctl-67/ccan/list/list.h000066400000000000000000000447171355562357700154560ustar00rootroot00000000000000/* Licensed under BSD-MIT - see LICENSE file for details */ #ifndef CCAN_LIST_H #define CCAN_LIST_H //#define CCAN_LIST_DEBUG 1 #include #include #include #include #include /** * struct list_node - an entry in a doubly-linked list * @next: next entry (self if empty) * @prev: previous entry (self if empty) * * This is used as an entry in a linked list. * Example: * struct child { * const char *name; * // Linked list of all us children. * struct list_node list; * }; */ struct list_node { struct list_node *next, *prev; }; /** * struct list_head - the head of a doubly-linked list * @h: the list_head (containing next and prev pointers) * * This is used as the head of a linked list. * Example: * struct parent { * const char *name; * struct list_head children; * unsigned int num_children; * }; */ struct list_head { struct list_node n; }; /** * list_check - check head of a list for consistency * @h: the list_head * @abortstr: the location to print on aborting, or NULL. * * Because list_nodes have redundant information, consistency checking between * the back and forward links can be done. This is useful as a debugging check. * If @abortstr is non-NULL, that will be printed in a diagnostic if the list * is inconsistent, and the function will abort. * * Returns the list head if the list is consistent, NULL if not (it * can never return NULL if @abortstr is set). * * See also: list_check_node() * * Example: * static void dump_parent(struct parent *p) * { * struct child *c; * * printf("%s (%u children):\n", p->name, p->num_children); * list_check(&p->children, "bad child list"); * list_for_each(&p->children, c, list) * printf(" -> %s\n", c->name); * } */ struct list_head *list_check(const struct list_head *h, const char *abortstr); /** * list_check_node - check node of a list for consistency * @n: the list_node * @abortstr: the location to print on aborting, or NULL. * * Check consistency of the list node is in (it must be in one). * * See also: list_check() * * Example: * static void dump_child(const struct child *c) * { * list_check_node(&c->list, "bad child list"); * printf("%s\n", c->name); * } */ struct list_node *list_check_node(const struct list_node *n, const char *abortstr); #define LIST_LOC __FILE__ ":" stringify(__LINE__) #ifdef CCAN_LIST_DEBUG #define list_debug(h, loc) list_check((h), loc) #define list_debug_node(n, loc) list_check_node((n), loc) #else #define list_debug(h, loc) (h) #define list_debug_node(n, loc) (n) #endif /** * LIST_HEAD_INIT - initializer for an empty list_head * @name: the name of the list. * * Explicit initializer for an empty list. * * See also: * LIST_HEAD, list_head_init() * * Example: * static struct list_head my_list = LIST_HEAD_INIT(my_list); */ #define LIST_HEAD_INIT(name) { { &name.n, &name.n } } /** * LIST_HEAD - define and initialize an empty list_head * @name: the name of the list. * * The LIST_HEAD macro defines a list_head and initializes it to an empty * list. It can be prepended by "static" to define a static list_head. * * See also: * LIST_HEAD_INIT, list_head_init() * * Example: * static LIST_HEAD(my_global_list); */ #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) /** * list_head_init - initialize a list_head * @h: the list_head to set to the empty list * * Example: * ... * struct parent *parent = malloc(sizeof(*parent)); * * list_head_init(&parent->children); * parent->num_children = 0; */ static inline void list_head_init(struct list_head *h) { h->n.next = h->n.prev = &h->n; } /** * list_add - add an entry at the start of a linked list. * @h: the list_head to add the node to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * struct child *child = malloc(sizeof(*child)); * * child->name = "marvin"; * list_add(&parent->children, &child->list); * parent->num_children++; */ #define list_add(h, n) list_add_(h, n, LIST_LOC) static inline void list_add_(struct list_head *h, struct list_node *n, const char *abortstr) { n->next = h->n.next; n->prev = &h->n; h->n.next->prev = n; h->n.next = n; (void)list_debug(h, abortstr); } /** * list_add_tail - add an entry at the end of a linked list. * @h: the list_head to add the node to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * list_add_tail(&parent->children, &child->list); * parent->num_children++; */ #define list_add_tail(h, n) list_add_tail_(h, n, LIST_LOC) static inline void list_add_tail_(struct list_head *h, struct list_node *n, const char *abortstr) { n->next = &h->n; n->prev = h->n.prev; h->n.prev->next = n; h->n.prev = n; (void)list_debug(h, abortstr); } /** * list_empty - is a list empty? * @h: the list_head * * If the list is empty, returns true. * * Example: * assert(list_empty(&parent->children) == (parent->num_children == 0)); */ #define list_empty(h) list_empty_(h, LIST_LOC) static inline bool list_empty_(const struct list_head *h, const char* abortstr) { (void)list_debug(h, abortstr); return h->n.next == &h->n; } /** * list_empty_nodebug - is a list empty (and don't perform debug checks)? * @h: the list_head * * If the list is empty, returns true. * This differs from list_empty() in that if CCAN_LIST_DEBUG is set it * will NOT perform debug checks. Only use this function if you REALLY * know what you're doing. * * Example: * assert(list_empty_nodebug(&parent->children) == (parent->num_children == 0)); */ #ifndef CCAN_LIST_DEBUG #define list_empty_nodebug(h) list_empty(h) #else static inline bool list_empty_nodebug(const struct list_head *h) { return h->n.next == &h->n; } #endif /** * list_del - delete an entry from an (unknown) linked list. * @n: the list_node to delete from the list. * * Note that this leaves @n in an undefined state; it can be added to * another list, but not deleted again. * * See also: * list_del_from() * * Example: * list_del(&child->list); * parent->num_children--; */ #define list_del(n) list_del_(n, LIST_LOC) static inline void list_del_(struct list_node *n, const char* abortstr) { (void)list_debug_node(n, abortstr); n->next->prev = n->prev; n->prev->next = n->next; #ifdef CCAN_LIST_DEBUG /* Catch use-after-del. */ n->next = n->prev = NULL; #endif } /** * list_del_from - delete an entry from a known linked list. * @h: the list_head the node is in. * @n: the list_node to delete from the list. * * This explicitly indicates which list a node is expected to be in, * which is better documentation and can catch more bugs. * * See also: list_del() * * Example: * list_del_from(&parent->children, &child->list); * parent->num_children--; */ static inline void list_del_from(struct list_head *h, struct list_node *n) { #ifdef CCAN_LIST_DEBUG { /* Thorough check: make sure it was in list! */ struct list_node *i; for (i = h->n.next; i != n; i = i->next) assert(i != &h->n); } #endif /* CCAN_LIST_DEBUG */ /* Quick test that catches a surprising number of bugs. */ assert(!list_empty(h)); list_del(n); } /** * list_entry - convert a list_node back into the structure containing it. * @n: the list_node * @type: the type of the entry * @member: the list_node member of the type * * Example: * // First list entry is children.next; convert back to child. * child = list_entry(parent->children.n.next, struct child, list); * * See Also: * list_top(), list_for_each() */ #define list_entry(n, type, member) container_of(n, type, member) /** * list_top - get the first entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *first; * first = list_top(&parent->children, struct child, list); * if (!first) * printf("Empty list!\n"); */ #define list_top(h, type, member) \ ((type *)list_top_((h), list_off_(type, member))) static inline const void *list_top_(const struct list_head *h, size_t off) { if (list_empty(h)) return NULL; return (const char *)h->n.next - off; } /** * list_pop - remove the first entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *one; * one = list_pop(&parent->children, struct child, list); * if (!one) * printf("Empty list!\n"); */ #define list_pop(h, type, member) \ ((type *)list_pop_((h), list_off_(type, member))) static inline const void *list_pop_(const struct list_head *h, size_t off) { struct list_node *n; if (list_empty(h)) return NULL; n = h->n.next; list_del(n); return (const char *)n - off; } /** * list_tail - get the last entry in a list * @h: the list_head * @type: the type of the entry * @member: the list_node member of the type * * If the list is empty, returns NULL. * * Example: * struct child *last; * last = list_tail(&parent->children, struct child, list); * if (!last) * printf("Empty list!\n"); */ #define list_tail(h, type, member) \ ((type *)list_tail_((h), list_off_(type, member))) static inline const void *list_tail_(const struct list_head *h, size_t off) { if (list_empty(h)) return NULL; return (const char *)h->n.prev - off; } /** * list_for_each - iterate through a list. * @h: the list_head (warning: evaluated multiple times!) * @i: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. * * Example: * list_for_each(&parent->children, child, list) * printf("Name: %s\n", child->name); */ #define list_for_each(h, i, member) \ list_for_each_off(h, i, list_off_var_(i, member)) /** * list_for_each_rev - iterate through a list backwards. * @h: the list_head * @i: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. * * Example: * list_for_each_rev(&parent->children, child, list) * printf("Name: %s\n", child->name); */ #define list_for_each_rev(h, i, member) \ for (i = container_of_var(list_debug(h, LIST_LOC)->n.prev, i, member); \ &i->member != &(h)->n; \ i = container_of_var(i->member.prev, i, member)) /** * list_for_each_safe - iterate through a list, maybe during deletion * @h: the list_head * @i: the structure containing the list_node * @nxt: the structure containing the list_node * @member: the list_node member of the structure * * This is a convenient wrapper to iterate @i over the entire list. It's * a for loop, so you can break and continue as normal. The extra variable * @nxt is used to hold the next element, so you can delete @i from the list. * * Example: * struct child *next; * list_for_each_safe(&parent->children, child, next, list) { * list_del(&child->list); * parent->num_children--; * } */ #define list_for_each_safe(h, i, nxt, member) \ list_for_each_safe_off(h, i, nxt, list_off_var_(i, member)) /** * list_next - get the next entry in a list * @h: the list_head * @i: a pointer to an entry in the list. * @member: the list_node member of the structure * * If @i was the last entry in the list, returns NULL. * * Example: * struct child *second; * second = list_next(&parent->children, first, list); * if (!second) * printf("No second child!\n"); */ #define list_next(h, i, member) \ ((list_typeof(i))list_entry_or_null(list_debug(h, \ __FILE__ ":" stringify(__LINE__)), \ (i)->member.next, \ list_off_var_((i), member))) /** * list_prev - get the previous entry in a list * @h: the list_head * @i: a pointer to an entry in the list. * @member: the list_node member of the structure * * If @i was the first entry in the list, returns NULL. * * Example: * first = list_prev(&parent->children, second, list); * if (!first) * printf("Can't go back to first child?!\n"); */ #define list_prev(h, i, member) \ ((list_typeof(i))list_entry_or_null(list_debug(h, \ __FILE__ ":" stringify(__LINE__)), \ (i)->member.prev, \ list_off_var_((i), member))) /** * list_append_list - empty one list onto the end of another. * @to: the list to append into * @from: the list to empty. * * This takes the entire contents of @from and moves it to the end of * @to. After this @from will be empty. * * Example: * struct list_head adopter; * * list_append_list(&adopter, &parent->children); * assert(list_empty(&parent->children)); * parent->num_children = 0; */ #define list_append_list(t, f) list_append_list_(t, f, \ __FILE__ ":" stringify(__LINE__)) static inline void list_append_list_(struct list_head *to, struct list_head *from, const char *abortstr) { struct list_node *from_tail = list_debug(from, abortstr)->n.prev; struct list_node *to_tail = list_debug(to, abortstr)->n.prev; /* Sew in head and entire list. */ to->n.prev = from_tail; from_tail->next = &to->n; to_tail->next = &from->n; from->n.prev = to_tail; /* Now remove head. */ list_del(&from->n); list_head_init(from); } /** * list_prepend_list - empty one list into the start of another. * @to: the list to prepend into * @from: the list to empty. * * This takes the entire contents of @from and moves it to the start * of @to. After this @from will be empty. * * Example: * list_prepend_list(&adopter, &parent->children); * assert(list_empty(&parent->children)); * parent->num_children = 0; */ #define list_prepend_list(t, f) list_prepend_list_(t, f, LIST_LOC) static inline void list_prepend_list_(struct list_head *to, struct list_head *from, const char *abortstr) { struct list_node *from_tail = list_debug(from, abortstr)->n.prev; struct list_node *to_head = list_debug(to, abortstr)->n.next; /* Sew in head and entire list. */ to->n.next = &from->n; from->n.prev = &to->n; to_head->prev = from_tail; from_tail->next = to_head; /* Now remove head. */ list_del(&from->n); list_head_init(from); } /** * list_for_each_off - iterate through a list of memory regions. * @h: the list_head * @i: the pointer to a memory region wich contains list node data. * @off: offset(relative to @i) at which list node data resides. * * This is a low-level wrapper to iterate @i over the entire list, used to * implement all oher, more high-level, for-each constructs. It's a for loop, * so you can break and continue as normal. * * WARNING! Being the low-level macro that it is, this wrapper doesn't know * nor care about the type of @i. The only assumtion made is that @i points * to a chunk of memory that at some @offset, relative to @i, contains a * properly filled `struct node_list' which in turn contains pointers to * memory chunks and it's turtles all the way down. Whith all that in mind * remember that given the wrong pointer/offset couple this macro will * happilly churn all you memory untill SEGFAULT stops it, in other words * caveat emptor. * * It is worth mentioning that one of legitimate use-cases for that wrapper * is operation on opaque types with known offset for `struct list_node' * member(preferably 0), because it allows you not to disclose the type of * @i. * * Example: * list_for_each_off(&parent->children, child, * offsetof(struct child, list)) * printf("Name: %s\n", child->name); */ #define list_for_each_off(h, i, off) \ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next, \ (off)); \ list_node_from_off_((void *)i, (off)) != &(h)->n; \ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \ (off))) /** * list_for_each_safe_off - iterate through a list of memory regions, maybe * during deletion * @h: the list_head * @i: the pointer to a memory region wich contains list node data. * @nxt: the structure containing the list_node * @off: offset(relative to @i) at which list node data resides. * * For details see `list_for_each_off' and `list_for_each_safe' * descriptions. * * Example: * list_for_each_safe_off(&parent->children, child, * next, offsetof(struct child, list)) * printf("Name: %s\n", child->name); */ #define list_for_each_safe_off(h, i, nxt, off) \ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next, \ (off)), \ nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ (off)); \ list_node_from_off_(i, (off)) != &(h)->n; \ i = nxt, \ nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ (off))) /* Other -off variants. */ #define list_entry_off(n, type, off) \ ((type *)list_node_from_off_((n), (off))) #define list_head_off(h, type, off) \ ((type *)list_head_off((h), (off))) #define list_tail_off(h, type, off) \ ((type *)list_tail_((h), (off))) #define list_add_off(h, n, off) \ list_add((h), list_node_from_off_((n), (off))) #define list_del_off(n, off) \ list_del(list_node_from_off_((n), (off))) #define list_del_from_off(h, n, off) \ list_del_from(h, list_node_from_off_((n), (off))) /* Offset helper functions so we only single-evaluate. */ static inline void *list_node_to_off_(struct list_node *node, size_t off) { return (void *)((char *)node - off); } static inline struct list_node *list_node_from_off_(void *ptr, size_t off) { return (struct list_node *)((char *)ptr + off); } /* Get the offset of the member, but make sure it's a list_node. */ #define list_off_(type, member) \ (container_off(type, member) + \ check_type(((type *)0)->member, struct list_node)) #define list_off_var_(var, member) \ (container_off_var(var, member) + \ check_type(var->member, struct list_node)) #if HAVE_TYPEOF #define list_typeof(var) typeof(var) #else #define list_typeof(var) void * #endif /* Returns member, or NULL if at end of list. */ static inline void *list_entry_or_null(const struct list_head *h, const struct list_node *n, size_t off) { if (n == &h->n) return NULL; return (char *)n - off; } #endif /* CCAN_LIST_H */ ndctl-67/ccan/minmax/000077500000000000000000000000001355562357700146335ustar00rootroot00000000000000ndctl-67/ccan/minmax/LICENSE000077700000000000000000000000001355562357700203562../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/minmax/minmax.h000066400000000000000000000023721355562357700163010ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_MINMAX_H #define CCAN_MINMAX_H #include "config.h" #include #if !HAVE_STATEMENT_EXPR || !HAVE_TYPEOF /* * Without these, there's no way to avoid unsafe double evaluation of * the arguments */ #error Sorry, minmax module requires statement expressions and typeof #endif #if HAVE_BUILTIN_TYPES_COMPATIBLE_P #define MINMAX_ASSERT_COMPATIBLE(a, b) \ BUILD_ASSERT(__builtin_types_compatible_p(a, b)) #else #define MINMAX_ASSERT_COMPATIBLE(a, b) \ do { } while (0) #endif #define min(a, b) \ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \ _a < _b ? _a : _b; \ }) #define max(a, b) \ ({ \ typeof(a) __a = (a); \ typeof(b) __b = (b); \ MINMAX_ASSERT_COMPATIBLE(typeof(__a), typeof(__b)); \ __a > __b ? __a : __b; \ }) #define clamp(v, f, c) (max(min((v), (c)), (f))) #define min_t(t, a, b) \ ({ \ t _ta = (a); \ t _tb = (b); \ min(_ta, _tb); \ }) #define max_t(t, a, b) \ ({ \ t _ta = (a); \ t _tb = (b); \ max(_ta, _tb); \ }) #define clamp_t(t, v, f, c) \ ({ \ t _tv = (v); \ t _tf = (f); \ t _tc = (c); \ clamp(_tv, _tf, _tc); \ }) #endif /* CCAN_MINMAX_H */ ndctl-67/ccan/short_types/000077500000000000000000000000001355562357700157255ustar00rootroot00000000000000ndctl-67/ccan/short_types/LICENSE000077700000000000000000000000001355562357700214502../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/short_types/short_types.h000066400000000000000000000014311355562357700204600ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_SHORT_TYPES_H #define CCAN_SHORT_TYPES_H #include /** * u64/s64/u32/s32/u16/s16/u8/s8 - short names for explicitly-sized types. */ typedef uint64_t u64; typedef int64_t s64; typedef uint32_t u32; typedef int32_t s32; typedef uint16_t u16; typedef int16_t s16; typedef uint8_t u8; typedef int8_t s8; /* Whichever they include first, they get these definitions. */ #ifdef CCAN_ENDIAN_H /** * be64/be32/be16 - 64/32/16 bit big-endian representation. */ typedef beint64_t be64; typedef beint32_t be32; typedef beint16_t be16; /** * le64/le32/le16 - 64/32/16 bit little-endian representation. */ typedef leint64_t le64; typedef leint32_t le32; typedef leint16_t le16; #endif #endif /* CCAN_SHORT_TYPES_H */ ndctl-67/ccan/str/000077500000000000000000000000001355562357700141525ustar00rootroot00000000000000ndctl-67/ccan/str/LICENSE000077700000000000000000000000001355562357700176752../../licenses/CC0ustar00rootroot00000000000000ndctl-67/ccan/str/debug.c000066400000000000000000000031021355562357700154000ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #include "config.h" #include #include #include #include #ifdef CCAN_STR_DEBUG /* Because we mug the real ones with macros, we need our own wrappers. */ int str_isalnum(int i) { assert(i >= -1 && i < 256); return isalnum(i); } int str_isalpha(int i) { assert(i >= -1 && i < 256); return isalpha(i); } int str_isascii(int i) { assert(i >= -1 && i < 256); return isascii(i); } #if HAVE_ISBLANK int str_isblank(int i) { assert(i >= -1 && i < 256); return isblank(i); } #endif int str_iscntrl(int i) { assert(i >= -1 && i < 256); return iscntrl(i); } int str_isdigit(int i) { assert(i >= -1 && i < 256); return isdigit(i); } int str_isgraph(int i) { assert(i >= -1 && i < 256); return isgraph(i); } int str_islower(int i) { assert(i >= -1 && i < 256); return islower(i); } int str_isprint(int i) { assert(i >= -1 && i < 256); return isprint(i); } int str_ispunct(int i) { assert(i >= -1 && i < 256); return ispunct(i); } int str_isspace(int i) { assert(i >= -1 && i < 256); return isspace(i); } int str_isupper(int i) { assert(i >= -1 && i < 256); return isupper(i); } int str_isxdigit(int i) { assert(i >= -1 && i < 256); return isxdigit(i); } #undef strstr #undef strchr #undef strrchr char *str_strstr(const char *haystack, const char *needle) { return strstr(haystack, needle); } char *str_strchr(const char *haystack, int c) { return strchr(haystack, c); } char *str_strrchr(const char *haystack, int c) { return strrchr(haystack, c); } #endif ndctl-67/ccan/str/str.c000066400000000000000000000004331355562357700151260ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #include size_t strcount(const char *haystack, const char *needle) { size_t i = 0, nlen = strlen(needle); while ((haystack = strstr(haystack, needle)) != NULL) { i++; haystack += nlen; } return i; } ndctl-67/ccan/str/str.h000066400000000000000000000135031355562357700151350ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_STR_H #define CCAN_STR_H #include "config.h" #include #include #include #include /** * streq - Are two strings equal? * @a: first string * @b: first string * * This macro is arguably more readable than "!strcmp(a, b)". * * Example: * if (streq(somestring, "")) * printf("String is empty!\n"); */ #define streq(a,b) (strcmp((a),(b)) == 0) /** * strstarts - Does this string start with this prefix? * @str: string to test * @prefix: prefix to look for at start of str * * Example: * if (strstarts(somestring, "foo")) * printf("String %s begins with 'foo'!\n", somestring); */ #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) /** * strends - Does this string end with this postfix? * @str: string to test * @postfix: postfix to look for at end of str * * Example: * if (strends(somestring, "foo")) * printf("String %s end with 'foo'!\n", somestring); */ static inline bool strends(const char *str, const char *postfix) { if (strlen(str) < strlen(postfix)) return false; return streq(str + strlen(str) - strlen(postfix), postfix); } /** * stringify - Turn expression into a string literal * @expr: any C expression * * Example: * #define PRINT_COND_IF_FALSE(cond) \ * ((cond) || printf("%s is false!", stringify(cond))) */ #define stringify(expr) stringify_1(expr) /* Double-indirection required to stringify expansions */ #define stringify_1(expr) #expr /** * strcount - Count number of (non-overlapping) occurrences of a substring. * @haystack: a C string * @needle: a substring * * Example: * assert(strcount("aaa aaa", "a") == 6); * assert(strcount("aaa aaa", "ab") == 0); * assert(strcount("aaa aaa", "aa") == 2); */ size_t strcount(const char *haystack, const char *needle); /** * STR_MAX_CHARS - Maximum possible size of numeric string for this type. * @type_or_expr: a pointer or integer type or expression. * * This provides enough space for a nul-terminated string which represents the * largest possible value for the type or expression. * * Note: The implementation adds extra space so hex values or negative * values will fit (eg. sprintf(... "%p"). ) * * Example: * char str[STR_MAX_CHARS(int)]; * * sprintf(str, "%i", 7); */ #define STR_MAX_CHARS(type_or_expr) \ ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ + STR_MAX_CHARS_TCHECK_(type_or_expr)) #if HAVE_TYPEOF /* Only a simple type can have 0 assigned, so test that. */ #define STR_MAX_CHARS_TCHECK_(type_or_expr) \ ({ typeof(type_or_expr) x = 0; (void)x; 0; }) #else #define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 #endif /** * cisalnum - isalnum() which takes a char (and doesn't accept EOF) * @c: a character * * Surprisingly, the standard ctype.h isalnum() takes an int, which * must have the value of EOF (-1) or an unsigned char. This variant * takes a real char, and doesn't accept EOF. */ static inline bool cisalnum(char c) { return isalnum((unsigned char)c); } static inline bool cisalpha(char c) { return isalpha((unsigned char)c); } static inline bool cisascii(char c) { return isascii((unsigned char)c); } #if HAVE_ISBLANK static inline bool cisblank(char c) { return isblank((unsigned char)c); } #endif static inline bool ciscntrl(char c) { return iscntrl((unsigned char)c); } static inline bool cisdigit(char c) { return isdigit((unsigned char)c); } static inline bool cisgraph(char c) { return isgraph((unsigned char)c); } static inline bool cislower(char c) { return islower((unsigned char)c); } static inline bool cisprint(char c) { return isprint((unsigned char)c); } static inline bool cispunct(char c) { return ispunct((unsigned char)c); } static inline bool cisspace(char c) { return isspace((unsigned char)c); } static inline bool cisupper(char c) { return isupper((unsigned char)c); } static inline bool cisxdigit(char c) { return isxdigit((unsigned char)c); } #include /* These checks force things out of line, hence they are under DEBUG. */ #ifdef CCAN_STR_DEBUG #include /* These are commonly misused: they take -1 or an *unsigned* char value. */ #undef isalnum #undef isalpha #undef isascii #undef isblank #undef iscntrl #undef isdigit #undef isgraph #undef islower #undef isprint #undef ispunct #undef isspace #undef isupper #undef isxdigit /* You can use a char if char is unsigned. */ #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF #define str_check_arg_(i) \ ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \ char) \ || (char)255 > 0)) #else #define str_check_arg_(i) (i) #endif #define isalnum(i) str_isalnum(str_check_arg_(i)) #define isalpha(i) str_isalpha(str_check_arg_(i)) #define isascii(i) str_isascii(str_check_arg_(i)) #if HAVE_ISBLANK #define isblank(i) str_isblank(str_check_arg_(i)) #endif #define iscntrl(i) str_iscntrl(str_check_arg_(i)) #define isdigit(i) str_isdigit(str_check_arg_(i)) #define isgraph(i) str_isgraph(str_check_arg_(i)) #define islower(i) str_islower(str_check_arg_(i)) #define isprint(i) str_isprint(str_check_arg_(i)) #define ispunct(i) str_ispunct(str_check_arg_(i)) #define isspace(i) str_isspace(str_check_arg_(i)) #define isupper(i) str_isupper(str_check_arg_(i)) #define isxdigit(i) str_isxdigit(str_check_arg_(i)) #if HAVE_TYPEOF /* With GNU magic, we can make const-respecting standard string functions. */ #undef strstr #undef strchr #undef strrchr /* + 0 is needed to decay array into pointer. */ #define strstr(haystack, needle) \ ((typeof((haystack) + 0))str_strstr((haystack), (needle))) #define strchr(haystack, c) \ ((typeof((haystack) + 0))str_strchr((haystack), (c))) #define strrchr(haystack, c) \ ((typeof((haystack) + 0))str_strrchr((haystack), (c))) #endif #endif /* CCAN_STR_DEBUG */ #endif /* CCAN_STR_H */ ndctl-67/ccan/str/str_debug.h000066400000000000000000000014061355562357700163020ustar00rootroot00000000000000/* CC0 (Public domain) - see LICENSE file for details */ #ifndef CCAN_STR_DEBUG_H #define CCAN_STR_DEBUG_H /* #define CCAN_STR_DEBUG 1 */ #ifdef CCAN_STR_DEBUG /* Because we mug the real ones with macros, we need our own wrappers. */ int str_isalnum(int i); int str_isalpha(int i); int str_isascii(int i); #if HAVE_ISBLANK int str_isblank(int i); #endif int str_iscntrl(int i); int str_isdigit(int i); int str_isgraph(int i); int str_islower(int i); int str_isprint(int i); int str_ispunct(int i); int str_isspace(int i); int str_isupper(int i); int str_isxdigit(int i); char *str_strstr(const char *haystack, const char *needle); char *str_strchr(const char *s, int c); char *str_strrchr(const char *s, int c); #endif /* CCAN_STR_DEBUG */ #endif /* CCAN_STR_DEBUG_H */ ndctl-67/configure.ac000066400000000000000000000200201355562357700147160ustar00rootroot00000000000000AC_PREREQ(2.60) m4_include([version.m4]) AC_INIT([ndctl], GIT_VERSION, [linux-nvdimm@lists.01.org], [ndctl], [https://github.com/pmem/ndctl]) AC_CONFIG_SRCDIR([ndctl/lib/libndctl.c]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([ check-news foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects ]) AC_PROG_CC_STDC AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE AC_CONFIG_MACRO_DIR([m4]) AM_SILENT_RULES([yes]) LT_INIT([ disable-static pic-only ]) AC_PREFIX_DEFAULT([/usr]) AC_PROG_SED AC_PROG_MKDIR_P AC_ARG_ENABLE([docs], AS_HELP_STRING([--disable-docs], [disable documentation build @<:@default=enabled@:>@]), [], enable_docs=yes) AS_IF([test "x$enable_docs" = "xyes"], [ AC_DEFINE(ENABLE_DOCS, [1], [Documentation / man pages.]) ]) AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" = "xyes"]) AC_ARG_ENABLE([asciidoctor], AS_HELP_STRING([--enable-asciidoctor], [use asciidoctor for documentation build]), [], enable_asciidoctor=yes) AM_CONDITIONAL([USE_ASCIIDOCTOR], [test "x$enable_asciidoctor" = "xyes"]) if test "x$enable_asciidoctor" = "xyes"; then asciidoc="asciidoctor" else asciidoc="asciidoc" fi AC_CHECK_PROG(ASCIIDOC, [$asciidoc], [$(which $asciidoc)], [missing]) if test "x$ASCIIDOC" = xmissing -a "x$enable_docs" = "xyes"; then AC_MSG_ERROR([$asciidoc needed to build documentation]) fi AC_SUBST([ASCIIDOC]) if test x"$asciidoc" = x"asciidoc"; then AC_CHECK_PROG(XMLTO, [xmlto], [$(which xmlto)], [missing]) if test "x$XMLTO" = xmissing -a "x$enable_docs" = "xyes"; then AC_MSG_ERROR([xmlto needed to build documentation]) fi AC_SUBST([XMLTO]) fi AC_C_TYPEOF AC_DEFINE([HAVE_STATEMENT_EXPR], 1, [Define to 1 if you have statement expressions.]) AC_C_BIGENDIAN( AC_DEFINE(HAVE_BIG_ENDIAN, 1, [Define to 1 if big-endian-arch]), AC_DEFINE(HAVE_LITTLE_ENDIAN, 1, [Define to 1 if little-endian-arch]), [], []) AC_ARG_ENABLE([logging], AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), [], enable_logging=yes) AS_IF([test "x$enable_logging" = "xyes"], [ AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) ]) AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), [], [enable_debug=no]) AS_IF([test "x$enable_debug" = "xyes"], [ AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) ]) AC_ARG_ENABLE([destructive], AS_HELP_STRING([--enable-destructive], [enable destructive functional tests @<:@default=disabled@:>@]), [], [enable_destructive=no]) AS_IF([test "x$enable_destructive" = "xyes"], [AC_DEFINE([ENABLE_DESTRUCTIVE], [1], [destructive functional tests support])]) AM_CONDITIONAL([ENABLE_DESTRUCTIVE], [test "x$enable_destructive" = "xyes"]) AC_ARG_ENABLE([test], AS_HELP_STRING([--enable-test], [enable ndctl test command @<:@default=disabled@:>@]), [], [enable_test=$enable_destructive]) AS_IF([test "x$enable_test" = "xyes"], [AC_DEFINE([ENABLE_TEST], [1], [ndctl test support])]) AM_CONDITIONAL([ENABLE_TEST], [test "x$enable_test" = "xyes"]) AC_CHECK_DECLS([BUS_MCEERR_AR], [enable_bus_mc_err=yes], [], [[#include ]]) AC_CHECK_DECLS([MAP_SHARED_VALIDATE], [kernel_map_shared_validate=yes], [], [[#include ]]) AC_CHECK_DECLS([MAP_SYNC], [kernel_map_sync=yes], [], [[#include ]]) AS_UNSET([ac_cv_have_decl_MAP_SHARED_VALIDATE]) AS_UNSET([ac_cv_have_decl_MAP_SYNC]) AC_CHECK_DECLS([MAP_SHARED_VALIDATE], [enable_map_shared_validate=yes], [], [[#include ]]) AC_CHECK_DECLS([MAP_SYNC], [enable_map_sync=yes], [], [[#include ]]) if test "x$kernel_map_shared_validate" = "xyes" -a "x$enable_map_shared_validate" != "xyes" ; then AC_MSG_WARN([MAP_SHARED_VALIDATE supported by kernel but not by , consider installing glibc-2.28 or later.]) fi if test "x$kernel_map_shared_validate" != "xyes" -a "x$enable_map_shared_validate" != "xyes" ; then AC_MSG_WARN([MAP_SHARED_VALIDATE not supported by kernel, consider installing kernel-4.15 or later.]) fi if test "x$kernel_map_sync" = "xyes" -a "x$enable_map_sync" != "xyes" ; then AC_MSG_WARN([MAP_SYNC supported by kernel but not by , consider installing glibc-2.28 or later.]) fi if test "x$kernel_map_sync" != "xyes" -a "x$enable_map_sync" != "xyes" ; then AC_MSG_WARN([MAP_SYNC not supported by kernel or architecture, consider installing kernel-4.15 or later.]) fi AS_IF([test "x$enable_bus_mc_err" = "xyes" -a "x$enable_map_sync" = "xyes" -a "x$enable_map_shared_validate" = "xyes"], [AC_DEFINE([ENABLE_POISON], [1], [ndctl test poison support])]) AM_CONDITIONAL([ENABLE_POISON], [test "x$enable_bus_mc_err" = "xyes" -a "x$enable_map_sync" = "xyes" -a "x$enable_map_shared_validate" = "xyes"]) PKG_CHECK_MODULES([KMOD], [libkmod]) PKG_CHECK_MODULES([UDEV], [libudev]) PKG_CHECK_MODULES([UUID], [uuid], [AC_DEFINE([HAVE_UUID], [1], [Define to 1 if using libuuid])]) PKG_CHECK_MODULES([JSON], [json-c]) AC_ARG_WITH([bash], AS_HELP_STRING([--with-bash], [Enable bash auto-completion. @<:@default=yes@:>@]), [], [with_bash=yes]) if test "x$with_bash" = "xyes"; then PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], [BASH_COMPLETION_DIR=$($PKG_CONFIG --variable=completionsdir bash-completion)], []) fi AC_SUBST([BASH_COMPLETION_DIR]) AM_CONDITIONAL([ENABLE_BASH_COMPLETION], [test "x$with_bash" = "xyes"]) AC_ARG_ENABLE([local], AS_HELP_STRING([--disable-local], [build against kernel ndctl.h @<:@default=system@:>@]), [], [enable_local=yes]) AC_CHECK_HEADERS_ONCE([linux/version.h]) AC_CHECK_FUNCS([ \ __secure_getenv \ secure_getenv\ ]) AC_ARG_WITH([systemd], AS_HELP_STRING([--with-systemd], [Enable systemd functionality (monitor). @<:@default=yes@:>@]), [], [with_systemd=yes]) if test "x$with_systemd" = "xyes"; then PKG_CHECK_MODULES([SYSTEMD], [systemd], [systemd_unitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)], []) fi AC_SUBST([systemd_unitdir]) AM_CONDITIONAL([ENABLE_SYSTEMD_UNITS], [test "x$with_systemd" = "xyes"]) ndctl_monitorconfdir=${sysconfdir}/ndctl ndctl_monitorconf=monitor.conf AC_SUBST([ndctl_monitorconfdir]) AC_SUBST([ndctl_monitorconf]) daxctl_modprobe_datadir=${datadir}/daxctl daxctl_modprobe_data=daxctl.conf AC_SUBST([daxctl_modprobe_datadir]) AC_SUBST([daxctl_modprobe_data]) AC_ARG_WITH([keyutils], AS_HELP_STRING([--with-keyutils], [Enable keyutils functionality (security). @<:@default=yes@:>@]), [], [with_keyutils=yes]) if test "x$with_keyutils" = "xyes"; then AC_CHECK_HEADERS([keyutils.h],,[ AC_MSG_ERROR([keyutils.h not found, consider installing the keyutils library development package (variously named keyutils-libs-devel, keyutils-devel, or libkeyutils-dev).]) ]) fi AS_IF([test "x$with_keyutils" = "xyes"], [AC_DEFINE([ENABLE_KEYUTILS], [1], [Enable keyutils support])]) AM_CONDITIONAL([ENABLE_KEYUTILS], [test "x$with_keyutils" = "xyes"]) ndctl_keysdir=${sysconfdir}/ndctl/keys ndctl_keysreadme=keys.readme AC_SUBST([ndctl_keysdir]) AC_SUBST([ndctl_keysreadme]) my_CFLAGS="\ -Wall \ -Wchar-subscripts \ -Wformat-security \ -Wmissing-declarations \ -Wmissing-prototypes \ -Wnested-externs \ -Wshadow \ -Wsign-compare \ -Wstrict-prototypes \ -Wtype-limits \ -Wmaybe-uninitialized \ -Wdeclaration-after-statement \ -Wunused-result \ -D_FORTIFY_SOURCE=2 \ -O2 " AC_SUBST([my_CFLAGS]) AC_CONFIG_HEADERS(config.h) AC_CONFIG_FILES([ Makefile daxctl/lib/Makefile ndctl/lib/Makefile ndctl/Makefile daxctl/Makefile test/Makefile Documentation/ndctl/Makefile Documentation/daxctl/Makefile ]) AC_OUTPUT AC_MSG_RESULT([ $PACKAGE $VERSION ===== prefix: ${prefix} sysconfdir: ${sysconfdir} libdir: ${libdir} includedir: ${includedir} compiler: ${CC} cflags: ${CFLAGS} ldflags: ${LDFLAGS} logging: ${enable_logging} debug: ${enable_debug} ]) ndctl-67/contrib/000077500000000000000000000000001355562357700140765ustar00rootroot00000000000000ndctl-67/contrib/daxctl-qemu-hmat-setup000077500000000000000000000157361355562357700203510ustar00rootroot00000000000000#!/bin/bash -e # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2019 Intel Corporation. All rights reserved. KERNEL=${KERNEL:-$(uname -r)} ROOT_IMAGE=${ROOT_IMAGE:-root.img} INITIATORS=4 TARGETS=128 TARGET_SIZE_MB=128 TARGET_SIZE="$((TARGET_SIZE_MB * 1024 * 1024))" QEMU_MEM="$((TARGET_SIZE_MB * TARGETS))" for i in $(seq 0 $((TARGETS - 1))); do qemu-img create -f raw "dimm-$i" "${TARGET_SIZE}" done; # Address Range Structures cat < hmat.dsl Signature : "HMAT" [Heterogeneous Memory Attributes Table] Table Length : 00000200 Revision : 01 Checksum : F4 Oem ID : "BOCHS " Oem Table ID : "BXPCHMAT" Oem Revision : 00000001 Asl Compiler ID : "INTL" Asl Compiler Revision : 20170929 Reserved : 00000000 Structure Type : 0000 [Memory Subystem Address Range] Reserved : 0000 Length : 00000028 Flags (decoded below) : 0003 Processor Proximity Domain Valid : 1 Memory Proximity Domain Valid : 1 Reservation Hint : 0 Reserved1 : 0000 Processor Proximity Domain : 00000000 Memory Proximity Domain : 00000000 Reserved2 : 00000000 Physical Address Range Base : 0000000000000000 Physical Address Range Size : 00000000000A0000 Structure Type : 0000 [Memory Subystem Address Range] Reserved : 0000 Length : 00000028 Flags (decoded below) : 0003 Processor Proximity Domain Valid : 1 Memory Proximity Domain Valid : 1 Reservation Hint : 0 Reserved1 : 0000 Processor Proximity Domain : 00000000 Memory Proximity Domain : 00000000 Reserved2 : 00000000 Physical Address Range Base : 0000000000100000 Physical Address Range Size : 0000000007F00000 EOF for i in $(seq 1 $((TARGETS - 1))); do BASE=$(printf "%016x" $((0x8000000 * i))) cat <> hmat.dsl Structure Type : 0000 [Memory Subystem Address Range] Reserved : 0000 Length : 00000028 Flags (decoded below) : 0003 Processor Proximity Domain Valid : 1 Memory Proximity Domain Valid : 1 Reservation Hint : 0 Reserved1 : 0000 Processor Proximity Domain : $(printf "%08x" $((i % INITIATORS))) Memory Proximity Domain : $(printf "%08x" "${i}") Reserved2 : 00000000 Physical Address Range Base : ${BASE} Physical Address Range Size : 0000000008000000 EOF done # System Locality Latency TABLE_SIZE="$(printf "%08x" $((40 + 4 * INITIATORS + 4 * TARGETS + 2 * INITIATORS * TARGETS)))" cat <> hmat.dsl Structure Type : 0001 [System Locality Latency and Bandwidth Information] Reserved : 0000 Length : ${TABLE_SIZE} Flags (decoded below) : 00 Memory Hierarchy : 0 Data Type : 00 Reserved1 : 0000 Initiator Proximity Domains # : $(printf "%08x" ${INITIATORS}) Target Proximity Domains # : $(printf "%08x" ${TARGETS}) Reserved2 : 00000000 Entry Base Unit : 0000000000000001 EOF for i in $(seq 0 $((INITIATORS - 1))); do cat <> hmat.dsl Initiator Proximity Domain List : $(printf "%08x" "${i}") EOF done for i in $(seq 0 $((TARGETS - 1))); do cat <> hmat.dsl Target Proximity Domain List : $(printf "%08x" "${i}") EOF done LOCAL_START=0x10 REMOTE_START=0x20 for i in $(seq 0 $((INITIATORS - 1))); do for j in $(seq 0 $((TARGETS - 1))); do if [ "$((j % INITIATORS))" -eq "${i}" ]; then cat <> hmat.dsl Entry : $(printf "%04x" $((LOCAL_START + j * 10))) EOF else cat <> hmat.dsl Entry : $(printf "%04x" $((REMOTE_START + j * 10))) EOF fi done done # System Locality Bandwidth TABLE_SIZE=$(printf "%08x" $((40 + 4 * INITIATORS + 4 * TARGETS + 2 * INITIATORS * TARGETS))) cat <> hmat.dsl Structure Type : 0001 [System Locality Latency and Bandwidth Information] Reserved : 0000 Length : ${TABLE_SIZE} Flags (decoded below) : 00 Memory Hierarchy : 0 Data Type : 03 Reserved1 : 0000 Initiator Proximity Domains # : $(printf "%08x" ${INITIATORS}) Target Proximity Domains # : $(printf "%08x" ${TARGETS}) Reserved2 : 00000000 Entry Base Unit : 0000000000000400 EOF for i in $(seq 0 $((INITIATORS - 1))); do cat <> hmat.dsl Initiator Proximity Domain List : $(printf "%08x" "${i}") EOF done for i in $(seq 0 $((TARGETS - 1))); do cat <> hmat.dsl Target Proximity Domain List : $(printf "%08x" "${i}") EOF done LOCAL_START=0x2000 REMOTE_START=0x1000 for i in $(seq 0 $((INITIATORS - 1))); do for j in $(seq 0 $((TARGETS - 1))); do if [ "$((j % INITIATORS))" -eq "${i}" ]; then cat <> hmat.dsl Entry : $(printf "%04x" $((LOCAL_START - j * 32))) EOF else cat <> hmat.dsl Entry : $(printf "%04x" $((REMOTE_START - j * 32))) EOF fi done done # Side Caches for i in $(seq 0 ${TARGETS}); do cat <> hmat.dsl Structure Type : 0002 [Memory Side Cache Information Structure] Reserved : 0000 Length : 00000020 Memory Proximity Domain : $(printf "%08x" "${i}") Reserved1 : 00000000 Memory Side Cache Size : 0000000000100000 Cache Attributes (decoded below) : 01001113 Total Cache Levels : 1 Cache Level : 1 Cache Associativity : 1 Write Policy : 1 Cache Line Size : 0100 Reserved2 : 0000 SMBIOS Handle # : 0000 EOF done # Generate injected initrd iasl ./*dsl mkdir -p kernel/firmware/acpi/ rm -f kernel/firmware/acpi/*.aml hmat-initramfs cp ./*aml kernel/firmware/acpi/ find kernel | cpio -c -o > hmat-initramfs cat "/boot/initramfs-${KERNEL}.img" >> hmat-initramfs # Build and evaluate QEMU command line QEMU="qemu-system-x86_64 -m ${QEMU_MEM} -smp 8,sockets=${INITIATORS},maxcpus=8 -machine pc,accel=kvm " QEMU+="-enable-kvm -display none -nographic -snapshot -hda ${ROOT_IMAGE} " QEMU+="-kernel /boot/vmlinuz-${KERNEL} -initrd ./hmat-initramfs " QEMU+="-append \"console=tty0 console=ttyS0 root=/dev/sda rw\" " for i in $(seq 0 $((TARGETS - 1))); do QEMU+="-object memory-backend-file,id=mem${i},share,mem-path=dimm-${i},size=${TARGET_SIZE_MB}M " QEMU+="-numa node,nodeid=${i},memdev=mem${i} " done eval "${QEMU}" ndctl-67/contrib/do_abidiff000077500000000000000000000045061355562357700160770ustar00rootroot00000000000000#!/bin/bash -e range="$*" old="${range%%..*}" new="${range##*..}" err() { echo "$1" exit 1 } build_rpm() { local cur=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) local ref="$1" local version="" # prepare ndctl tree rm -rf results_ndctl git checkout -b rel_${ref} $ref ./autogen.sh ./configure CFLAGS='-g -O2' --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib64 make clean make rhel/ndctl.spec cp rhel/ndctl.spec . # build and copy RPMs version="$(./git-version)" release="f$(basename $(readlink -f /etc/mock/default.cfg) | cut -d- -f2)" git archive --format=tar --prefix="ndctl-${version}/" HEAD | gzip > ndctl-${version}.tar.gz fedpkg --release $release --module-name ndctl mockbuild [ "$?" -eq 0 ] || err "error building $ref" mkdir -p release/rel_${ref}/ cp results_ndctl/*/*/*.x86_64.rpm release/rel_${ref}/ # restore ndctl branch and cleanup git checkout $cur git branch -D rel_${ref} rm ndctl-${version}.tar.gz rm ndctl-${version}*.src.rpm rm -rf results_ndctl rm -f ndctl.spec } do_diff() { local pkg="$1" local old_base="$(find . -regex "./release/rel_${old}/${pkg}-[0-9]+.*" | head -1)" local new_base="$(find . -regex "./release/rel_${new}/${pkg}-[0-9]+.*" | head -1)" local old_dev="$(find . -regex "./release/rel_${old}/${pkg}-devel-[0-9]+.*" | head -1)" local new_dev="$(find . -regex "./release/rel_${new}/${pkg}-devel-[0-9]+.*" | head -1)" local old_lib="$(find . -regex "./release/rel_${old}/${pkg}-libs-[0-9]+.*" | head -1)" local new_lib="$(find . -regex "./release/rel_${new}/${pkg}-libs-[0-9]+.*" | head -1)" [ -n "$pkg" ] || err "specify a package for diff (ndctl, daxctl)" [ -n "$old_base" ] || err "$pkg: old_base empty, possible build failure" [ -n "$new_base" ] || err "$pkg: new_base empty, possible build failure" abipkgdiff --dso-only --no-added-syms --harmless --drop-private-types \ --devel1 "$old_dev" --devel2 "$new_dev" \ "$old_base" "$new_base" abipkgdiff --no-added-syms --harmless --drop-private-types \ --devel1 "$old_dev" --devel2 "$new_dev" \ "$old_lib" "$new_lib" } [ -e "COPYING" ] || err "Run from the top level of an ndctl tree" if ! command -v "abipkgdiff" >/dev/null; then err "missing abipkgdiff. Please install libabigail" fi rm -rf release/rel* build_rpm $old > release/buildlog_$old 2>&1 build_rpm $new > release/buildlog_$new 2>&1 do_diff ndctl do_diff daxctl ndctl-67/contrib/ndctl000077500000000000000000000320651355562357700151360ustar00rootroot00000000000000# ndctl bash and zsh completion # Taken from perf's completion script. ### common helpers between ndctl and daxctl __my_reassemble_comp_words_by_ref() { local exclude i j first # Which word separators to exclude? exclude="${1//[^$COMP_WORDBREAKS]}" cword_=$COMP_CWORD if [ -z "$exclude" ]; then words_=("${COMP_WORDS[@]}") return fi # List of word completion separators has shrunk; # re-assemble words to complete. for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do # Append each nonempty word consisting of just # word separator characters to the current word. first=t while [ $i -gt 0 ] && [ -n "${COMP_WORDS[$i]}" ] && # word consists of excluded word separators [ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ] do # Attach to the previous token, # unless the previous token is the command name. if [ $j -ge 2 ] && [ -n "$first" ]; then ((j--)) fi first= words_[$j]=${words_[j]}${COMP_WORDS[i]} if [ $i = $COMP_CWORD ]; then cword_=$j fi if (($i < ${#COMP_WORDS[@]} - 1)); then ((i++)) else # Done. return fi done words_[$j]=${words_[j]}${COMP_WORDS[i]} if [ $i = $COMP_CWORD ]; then cword_=$j fi done } # Define preload_get_comp_words_by_ref="false", if the function # __nd_common_get_comp_words_by_ref() is required instead. preload_get_comp_words_by_ref="true" if [ $preload_get_comp_words_by_ref = "true" ]; then type _get_comp_words_by_ref &>/dev/null || preload_get_comp_words_by_ref="false" fi [ $preload_get_comp_words_by_ref = "true" ] || __nd_common_get_comp_words_by_ref() { local exclude cur_ words_ cword_ if [ "$1" = "-n" ]; then exclude=$2 shift 2 fi __my_reassemble_comp_words_by_ref "$exclude" cur_=${words_[cword_]} while [ $# -gt 0 ]; do case "$1" in cur) cur=$cur_ ;; prev) prev=${words_[$cword_-1]} ;; words) words=("${words_[@]}") ;; cword) cword=$cword_ ;; esac shift done } __nd_common_prev_skip_opts () { local i cmd_ cmds_ let i=cword-1 cmds_=$($cmd $1 --list-cmds) prev_skip_opts=() while [ $i -ge 0 ]; do if [[ ${words[i]} == $1 ]]; then return fi for cmd_ in $cmds_; do if [[ ${words[i]} == $cmd_ ]]; then prev_skip_opts=${words[i]} return fi done ((i--)) done } ### ndctl __ndctlcomp() { local i=0 COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) for cword in "${COMPREPLY[@]}"; do if [[ "$cword" == @(--bus|--region|--type|--mode|--size|--dimm|--reconfig|--uuid|--name|--sector-size|--map|--namespace|--input|--output|--label-version|--align|--block|--count|--firmware|--media-temperature|--ctrl-temperature|--spares|--media-temperature-threshold|--ctrl-temperature-threshold|--spares-threshold|--media-temperature-alarm|--ctrl-temperature-alarm|--spares-alarm|--numa-node|--log|--dimm-event|--config-file|--key-handle|--key-path|--tpm-handle) ]]; then COMPREPLY[$i]="${cword}=" else COMPREPLY[$i]="${cword} " fi ((i++)) done } __ndctl_get_buses() { local opts="--buses $*" [ -n "$dimm_filter" ] && opts="$opts --dimm=$dimm_filter" [ -n "$reg_filter" ] && opts="$opts --region=$reg_filter" [ -n "$ns_filter" ] && opts="$opts --namespace=$ns_filter" echo "$(ndctl list $opts | grep -E "^\s*\"provider\":" | cut -d\" -f4)" } __ndctl_get_regions() { local opts="--regions $*" [ -n "$bus_filter" ] && opts="$opts --bus=$bus_filter" [ -n "$dimm_filter" ] && opts="$opts --dimm=$dimm_filter" [ -n "$ns_filter" ] && opts="$opts --namespace=$ns_filter" [ -n "$type_filter" ] && opts="$opts --type=$type_filter" [ -n "$idle_filter" ] && opts="$opts --idle" echo "$(ndctl list $opts | grep -E "^\s*\"dev\":" | cut -d\" -f4)" } __ndctl_get_ns() { opts="--namespaces $*" [ -n "$bus_filter" ] && opts="$opts --bus=$bus_filter" [ -n "$dimm_filter" ] && opts="$opts --dimm=$dimm_filter" [ -n "$reg_filter" ] && opts="$opts --region=$reg_filter" [ -n "$mode_filter" ] && opts="$opts --mode=$mode_filter" [ -n "$type_filter" ] && opts="$opts --type=$type_filter" [ -n "$idle_filter" ] && opts="$opts --idle" echo "$(ndctl list $opts | grep -E "^\s*\"dev\":" | cut -d\" -f4)" } __ndctl_get_dimms() { opts="--dimms $*" [ -n "$bus_filter" ] && opts="$opts --bus=$bus_filter" [ -n "$reg_filter" ] && opts="$opts --region=$reg_filter" [ -n "$ns_filter" ] && opts="$opts --namespace=$ns_filter" echo "$(ndctl list $opts | grep -E "^\s*\"dev\":" | cut -d\" -f4)" } __ndctl_get_sector_sizes() { if [[ "$mode_filter" == @(raw|memory|fsdax) ]]; then echo "512 4096" elif [[ "$mode_filter" == @(dax|devdax) ]]; then return else echo "512 520 528 4096 4104 4160 4224" fi } __ndctl_get_nodes() { local nlist="" for node in /sys/devices/system/node/node*; do node="$(basename $node)" if [[ $node =~ node([0-9]+) ]]; then nlist="$nlist ${BASH_REMATCH[1]}" else continue fi done echo "$nlist" } saveopts="" __ndctl_file_comp() { local cur="$1" _filedir saveopts="${COMPREPLY[*]}" } __ndctl_comp_options() { local cur=$1 local opts if [[ "$cur" == *=* ]]; then local cur_subopt=${cur%%=*} local cur_arg=${cur##*=} case $cur_subopt in --bus) opts=$(__ndctl_get_buses) ;; --region) opts=$(__ndctl_get_regions) ;; --dimm) opts=$(__ndctl_get_dimms -i) ;; --namespace) opts=$(__ndctl_get_ns) ;; --reconfig) # It is ok to reconfig disabled namespaces opts=$(__ndctl_get_ns -i) ;; --type) opts="pmem blk" if [[ "$mode_filter" == @(dax|memory|fsdax|devdax) ]]; then opts="pmem" fi ;; --mode) opts="raw sector fsdax devdax" if [[ "$type_filter" == "blk" ]]; then opts="raw sector" fi ;; --sector-size) opts=$(__ndctl_get_sector_sizes) ;; --map) opts="mem dev" ;; --output) ;& --input) ;& --firmware) __ndctl_file_comp "$cur_arg" return ;; --label-version) opts="1.1 1.2" ;; --media-temperature-alarm) ;& --ctrl-temperature-alarm) ;& --spares-alarm) opts="on off" ;; --numa-node) opts=$(__ndctl_get_nodes) ;; --log) local my_args=( "standard" "syslog" ) __ndctl_file_comp "$cur_arg" if [ -n "$cur_arg" ]; then # if $cur_arg is a substring of $my_args (as # determined by compgen), then we continue to # manually construct opts using $my_args, but # if it is not a substring (compgen returns # empty), then we defer to _ndctl_file_comp # and return immediately. (NOTE: subscripting # of the my_args array here is fragile). # If this pattern repeats, where we need to # complete with filenames and some custom # options, find a better way to generalize this if [ -z "$(compgen -W "${my_args[*]}" -- "$cur_arg")" ]; then return fi fi opts="${my_args[*]} $saveopts" ;; --dimm-event) opts="all \ dimm-spares-remaining \ dimm-media-temperature \ dimm-controller-temperature \ dimm-health-state \ dimm-unclean-shutdown \ " ;; --config-file) __ndctl_file_comp "$cur_arg" return ;; --key-path) __ndctl_file_comp "$cur_arg" return ;; *) return ;; esac __ndctlcomp "$opts" "$cur_arg" fi } __ndctl_comp_non_option_args() { local subcmd=$1 local cur=$2 local opts case $subcmd in enable-namespace) opts="$(__ndctl_get_ns -i) all" ;; disable-namespace) ;& destroy-namespace) opts="$(__ndctl_get_ns -i) all" ;; check-namespace) opts="$(__ndctl_get_ns -i) all" ;; clear-errors) opts="$(__ndctl_get_ns) all" ;; enable-region) opts="$(__ndctl_get_regions -i) all" ;; disable-region) opts="$(__ndctl_get_regions) all" ;; enable-dimm) opts="$(__ndctl_get_dimms -i) all" ;; disable-dimm) opts="$(__ndctl_get_dimms) all" ;; init-labels) ;& check-labels) ;& read-labels) ;& write-labels) ;& zero-labels) opts="$(__ndctl_get_dimms -i) all" ;; inject-error) opts="$(__ndctl_get_ns -i)" ;; inject-smart) opts="$(__ndctl_get_dimms -i)" ;; wait-scrub) ;& start-scrub) opts="$(__ndctl_get_buses) all" ;; setup-passphrase) ;& update-passphrase) ;& remove-passphrase) ;& freeze-security) ;& sanitize-dimm) ;& wait-overwrite) opts="$(__ndctl_get_dimms -i) all" ;; *) return ;; esac __ndctlcomp "$opts" "$cur" } __ndctl_add_special_opts() { local subcmd=$1 case $subcmd in create-namespace) opts="$opts --no-autolabel" ;; esac } __ndctl_init_filters() { bus_filter='' reg_filter='' ns_filter='' dimm_filter='' type_filter='' idle_filter='' mode_filter='' for __word in "${words[@]}"; do if [[ "$__word" =~ --bus=(.*) ]]; then bus_filter="${BASH_REMATCH[1]}" # lets make sure this is in the list of buses local buses=$(__ndctl_get_buses) [[ "$buses" == *"$bus_filter"* ]] || bus_filter='' fi if [[ "$__word" =~ --region=(.*) ]]; then reg_filter="${BASH_REMATCH[1]}" # lets make sure this is in the list of regions local regions=$(__ndctl_get_regions -i) [[ "$regions" == *"$reg_filter"* ]] || reg_filter='' fi if [[ "$__word" =~ --namespace=(.*) ]]; then ns_filter="${BASH_REMATCH[1]}" # lets make sure this is in the list of namespaces local nss=$(__ndctl_get_ns -i) [[ "$nss" == *"$ns_filter"* ]] || ns_filter='' fi if [[ "$__word" =~ --dimm=(.*) ]]; then dimm_filter="${BASH_REMATCH[1]}" # lets make sure this is in the list of dimms local dimms=$(__ndctl_get_dimms) [[ "$dimms" == *"$dimm_filter"* ]] || dimm_filter='' fi if [[ "$__word" =~ --idle ]]; then idle_filter="1" fi if [[ "$__word" =~ --type=(.*) ]]; then type_filter="${BASH_REMATCH[1]}" fi if [[ "$__word" =~ --mode=(.*) ]]; then mode_filter="${BASH_REMATCH[1]}" fi done } __ndctl_main() { local cmd subcmd cmd=${words[0]} COMPREPLY=() __ndctl_init_filters # Skip options backward and find the last ndctl command __nd_common_prev_skip_opts subcmd=$prev_skip_opts # List ndctl subcommands or long options if [ -z $subcmd ]; then if [[ $cur == --* ]]; then cmds="--version --help --list-cmds" else cmds=$($cmd --list-cmds) fi __ndctlcomp "$cmds" "$cur" else # List long option names if [[ $cur == --* ]]; then opts=$($cmd $subcmd --list-opts) __ndctl_add_special_opts "$subcmd" __ndctlcomp "$opts" "$cur" __ndctl_comp_options "$cur" else [ -z "$subcmd" ] && return __ndctl_comp_non_option_args "$subcmd" "$cur" fi fi } if [[ -n ${ZSH_VERSION-} ]]; then autoload -U +X compinit && compinit __ndctlcomp() { emulate -L zsh local c IFS=$' \t\n' local -a array for c in ${=1}; do case $c in --*=*|*.) ;; *) c="$c " ;; esac array[${#array[@]}+1]="$c" done compset -P '*[=:]' compadd -Q -S '' -a -- array && _ret=0 } _ndctl() { local _ret=1 cur cword prev cur=${words[CURRENT]} prev=${words[CURRENT-1]} let cword=CURRENT-1 emulate ksh -c __ndctl_main let _ret && _default && _ret=0 return _ret } compdef _ndctl ndctl return fi type ndctl &>/dev/null && _ndctl() { local cur words cword prev if [ $preload_get_comp_words_by_ref = "true" ]; then _get_comp_words_by_ref -n =: cur words cword prev else __nd_common_get_comp_words_by_ref -n =: cur words cword prev fi __ndctl_main } ### daxctl ### __daxctl_get_devs() { local opts=("--devices" "$*") daxctl list "${opts[@]}" | grep -E "^\s*\"chardev\":" | cut -d'"' -f4 } __daxctl_get_regions() { local opts=("--regions" "$*") daxctl list "${opts[@]}" | grep -E "^\s*\"id\":" | grep -Eo "[0-9]+" } __daxctlcomp() { local i=0 COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) for cword in "${COMPREPLY[@]}"; do if [[ "$cword" == @(--region|--dev|--mode) ]]; then COMPREPLY[$i]="${cword}=" else COMPREPLY[$i]="${cword} " fi ((i++)) done } __daxctl_comp_options() { local cur=$1 local opts if [[ "$cur" == *=* ]]; then local cur_subopt=${cur%%=*} local cur_arg=${cur##*=} case $cur_subopt in --region) opts="$(__daxctl_get_regions -i)" ;; --dev) opts="$(__daxctl_get_devs -i)" ;; --mode) opts="system-ram devdax" ;; *) return ;; esac __daxctlcomp "$opts" "$cur_arg" fi } __daxctl_comp_non_option_args() { local subcmd=$1 local cur=$2 local opts case $subcmd in reconfigure-device) ;& online-memory) ;& offline-memory) opts="$(__daxctl_get_devs -i) all" ;; *) return ;; esac __daxctlcomp "$opts" "$cur" } __daxctl_main() { local cmd subcmd cmd=${words[0]} COMPREPLY=() # Skip options backward and find the last daxctl command __nd_common_prev_skip_opts subcmd=$prev_skip_opts # List daxctl subcommands or long options if [ -z $subcmd ]; then if [[ $cur == --* ]]; then cmds="--version --help --list-cmds" else cmds=$($cmd --list-cmds) fi __daxctlcomp "$cmds" "$cur" else # List long option names if [[ $cur == --* ]]; then opts=$($cmd $subcmd --list-opts) __daxctlcomp "$opts" "$cur" __daxctl_comp_options "$cur" else [ -z "$subcmd" ] && return __daxctl_comp_non_option_args "$subcmd" "$cur" fi fi } type daxctl &>/dev/null && _daxctl() { local cur words cword prev if [ $preload_get_comp_words_by_ref = "true" ]; then _get_comp_words_by_ref -n =: cur words cword prev else __nd_common_get_comp_words_by_ref -n =: cur words cword prev fi __daxctl_main } complete -o nospace -F _ndctl ndctl complete -o nospace -F _daxctl daxctl ndctl-67/contrib/nfit_test_depmod.conf000066400000000000000000000005111355562357700202710ustar00rootroot00000000000000# For nfit_test module overriding - copy to /etc/depmod.d or so override nfit * extra override device_dax * extra override dax_pmem * extra override dax_pmem_core * extra override dax_pmem_compat * extra override libnvdimm * extra override nd_blk * extra override nd_btt * extra override nd_e820 * extra override nd_pmem * extra ndctl-67/contrib/nvdimm-security.conf000066400000000000000000000001451355562357700201040ustar00rootroot00000000000000install libnvdimm /usr/bin/ndctl load-keys ; /sbin/modprobe --ignore-install libnvdimm $CMDLINE_OPTS ndctl-67/contrib/prepare-release.sh000077500000000000000000000131511355562357700175120ustar00rootroot00000000000000#!/bin/bash -e # Arguments: # fix - fixup release instead of a full release # ignore_rev - ignore the check for _REVISION in libtool versioning checks # Notes: # - Checkout to the appropriate branch beforehand # master - for major release # ndctl-xx.y - for fixup release # This is important for generating the shortlog # - Add a temporary commit that updates the libtool versions as needed. # This will later become the release commit. Use --amend to add in the # git-version update and the message body. # Pre-reqs: # - libabigail (for abipkgdiff) # - fedpkg (for mock build) # TODO # - auto generate a release commit/tag message template # - determine the most recent kernel release and add it to the above # - perform documentation update for pmem.io/ndctl cleanup() { rm -rf release mkdir release/ } err() { echo "$1" exit 1 } parse_args() { local args="$*" grep -q "fix" <<< "$args" && rel_fix="1" || rel_fix="" grep -q "ignore_rev" <<< "$args" && ignore_rev="1" || ignore_rev="" } check_branch() { local cur=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) if [ -n "$rel_fix" ]; then # fixup release, expect ndctl-xx.y branch if ! grep -Eq "^ndctl.[0-9]+\.y$" <<< "$cur"; then err "expected an ndctl-xx.y branch for fixup release" fi else # major release, expect master branch if ! grep -Eq "^master$" <<< "$cur"; then err "expected master branch for a major release" fi fi if ! git diff-index --quiet HEAD --; then err "$cur has uncommitted/unstaged changes" fi } last_maj() { git tag | sort -V | grep -E "v[0-9]+$" | tail -1 } last_fix() { local base="$1" git tag | sort -V | grep -E "$base\.?[0-9]*$" | tail -1 } next_maj() { local last="$1" local num=${last#v} newnum="$((num + 1))" echo "v$newnum" } next_fix() { local last="$1" local num=${last##*.} local base=${last%%.*} newnum=$((num + 1)) echo "$base.$newnum" } gen_lists() { local range="$1" git shortlog "$range" > release/shortlog git log --pretty=format:"%s" "$range" > release/commits c_count=$(git log --pretty=format:"%s" "$range" | wc -l) } # Check libtool versions in Makefile.am.in # $1: lib name (currently libndctl or libdaxctl) check_libtool_vers() { local lib="$1" local lib_u="${lib^^}" local libdir="${lib##lib}/lib/" local symfile="${libdir}/${lib}.sym" local last_cur=$(git show $last_ref:Makefile.am.in | grep -E "^${lib_u}_CURRENT" | cut -d'=' -f2) local last_rev=$(git show $last_ref:Makefile.am.in | grep -E "^${lib_u}_REVISION" | cut -d'=' -f2) local last_age=$(git show $last_ref:Makefile.am.in | grep -E "^${lib_u}_AGE" | cut -d'=' -f2) local last_soname=$((last_cur - last_age)) local next_cur=$(git show HEAD:Makefile.am.in | grep -E "^${lib_u}_CURRENT" | cut -d'=' -f2) local next_rev=$(git show HEAD:Makefile.am.in | grep -E "^${lib_u}_REVISION" | cut -d'=' -f2) local next_age=$(git show HEAD:Makefile.am.in | grep -E "^${lib_u}_AGE" | cut -d'=' -f2) local next_soname=$((next_cur - next_age)) local soname_diff=$((next_soname - last_soname)) # generally libtool versions either reset to zero or increase only by one # _CURRENT monotonically increases (by one) if [ "$((next_cur - last_cur))" -gt 1 ]; then err "${lib_u}_CURRENT can increase at most by 1" fi if [ "$next_rev" -ne 0 ]; then if [ "$((next_rev - last_rev))" -gt 1 ]; then err "${lib_u}_REVISION can increase at most by 1" fi fi if [ "$next_age" -ne 0 ]; then if [ "$((next_age - last_age))" -gt 1 ]; then err "${lib_u}_AGE can increase at most by 1" fi fi # test for soname change if [ "$soname_diff" -ne 0 ]; then err "${lib}: expected soname to stay unchanged" fi # tests based on whether symfile changed # compatibility breaking changes are left for libabigail to detect test -s "$symfile" || err "$symfile: not found" if [ -n "$(git diff --name-only $last_ref..HEAD $symfile)" ]; then # symfile has changed, cur and age should increase if [ "$((next_cur - last_cur))" -ne 1 ]; then err "based on $symfile, ${lib_u}_CURRENT should've increased by 1" fi if [ "$((next_age - last_age))" -ne 1 ]; then err "based on $symfile, ${lib_u}_AGE should've increased by 1" fi else # no changes to symfile, revision should've increased if source changed if [ -n "$ignore_rev" ]; then : # skip elif [ -n "$(git diff --name-only $last_ref..HEAD $libdir/)" ]; then if [ "$((next_rev - last_rev))" -ne 1 ]; then err "based on $symfile, ${lib_u}_REVISION should've increased by 1" fi fi fi } # main cleanup parse_args "$*" check_branch [ -e "COPYING" ] || err "Run from the top level of an ndctl tree" last_maj=$(last_maj) test -n "$last_maj" || err "Unable to determine last release" last_fix=$(last_fix $last_maj) test -n "$last_fix" || err "Unable to determine last fixup tag for $last_maj" next_maj=$(next_maj "$last_maj") next_fix=$(next_fix "$last_fix") [ -n "$rel_fix" ] && last_ref="$last_fix" || last_ref="$last_maj" [ -n "$rel_fix" ] && next_ref="$next_fix" || next_ref="$next_maj" check_libtool_vers "libndctl" check_libtool_vers "libdaxctl" gen_lists ${last_ref}..HEAD # For ABI diff purposes, use the latest fixes tag contrib/do_abidiff ${last_fix}..HEAD # once everything passes, update the git-version sed -i -e "s/DEF_VER=[0-9]\+.*/DEF_VER=${next_ref#v}/" git-version echo "Ready to release ndctl-$next_ref with $c_count new commits." echo "Add git-version to the top commit to get the updated version." echo "Use release/commits and release/shortlog to compose the release message" echo "The release commit typically contains the Makefile.am.in libtool version" echo "update, and the git-version update." echo "Finally, ensure the release commit as well as the tag are PGP signed." ndctl-67/daxctl/000077500000000000000000000000001355562357700137155ustar00rootroot00000000000000ndctl-67/daxctl/Makefile.am000066400000000000000000000011351355562357700157510ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in bin_PROGRAMS = daxctl DISTCLEANFILES = config.h BUILT_SOURCES = config.h config.h: $(srcdir)/Makefile.am $(AM_V_GEN) echo "/* Autogenerated by daxctl/Makefile.am */" >$@ && \ echo '#define DAXCTL_MODPROBE_DATA \ "$(daxctl_modprobe_datadir)/$(daxctl_modprobe_data)"' >>$@ && \ echo '#define DAXCTL_MODPROBE_INSTALL \ "$(sysconfdir)/modprobe.d/$(daxctl_modprobe_data)"' >>$@ daxctl_SOURCES =\ daxctl.c \ list.c \ migrate.c \ device.c \ ../util/json.c daxctl_LDADD =\ lib/libdaxctl.la \ ../libutil.a \ $(UUID_LIBS) \ $(KMOD_LIBS) \ $(JSON_LIBS) ndctl-67/daxctl/builtin.h000066400000000000000000000011061355562357700155320ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2015-2018 Intel Corporation. All rights reserved. */ #ifndef _DAXCTL_BUILTIN_H_ #define _DAXCTL_BUILTIN_H_ struct daxctl_ctx; int cmd_list(int argc, const char **argv, struct daxctl_ctx *ctx); int cmd_migrate(int argc, const char **argv, struct daxctl_ctx *ctx); int cmd_reconfig_device(int argc, const char **argv, struct daxctl_ctx *ctx); int cmd_online_memory(int argc, const char **argv, struct daxctl_ctx *ctx); int cmd_offline_memory(int argc, const char **argv, struct daxctl_ctx *ctx); #endif /* _DAXCTL_BUILTIN_H_ */ ndctl-67/daxctl/daxctl.c000066400000000000000000000056451355562357700153520ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * Copyright(c) 2005 Andreas Ericsson. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char daxctl_usage_string[] = "daxctl [--version] [--help] COMMAND [ARGS]"; const char daxctl_more_info_string[] = "See 'daxctl help COMMAND' for more information on a specific command.\n" " daxctl --list-cmds to see all available commands"; static int cmd_version(int argc, const char **argv, struct daxctl_ctx *ctx) { printf("%s\n", VERSION); return 0; } static int cmd_help(int argc, const char **argv, struct daxctl_ctx *ctx) { const char * const builtin_help_subcommands[] = { "list", NULL, }; struct option builtin_help_options[] = { OPT_END(), }; const char *builtin_help_usage[] = { "daxctl help [command]", NULL }; argc = parse_options_subcommand(argc, argv, builtin_help_options, builtin_help_subcommands, builtin_help_usage, 0); if (!argv[0]) { printf("\n usage: %s\n\n", daxctl_usage_string); printf("\n %s\n\n", daxctl_more_info_string); return 0; } return help_show_man_page(argv[0], "daxctl", "DAXCTL_MAN_VIEWER"); } static struct cmd_struct commands[] = { { "version", .d_fn = cmd_version }, { "list", .d_fn = cmd_list }, { "help", .d_fn = cmd_help }, { "migrate-device-model", .d_fn = cmd_migrate }, { "reconfigure-device", .d_fn = cmd_reconfig_device }, { "online-memory", .d_fn = cmd_online_memory }, { "offline-memory", .d_fn = cmd_offline_memory }, }; int main(int argc, const char **argv) { struct daxctl_ctx *ctx; int rc; /* Look for flags.. */ argv++; argc--; main_handle_options(&argv, &argc, daxctl_usage_string, commands, ARRAY_SIZE(commands)); if (argc > 0) { if (!prefixcmp(argv[0], "--")) argv[0] += 2; } else { /* The user didn't specify a command; give them help */ printf("\n usage: %s\n\n", daxctl_usage_string); printf("\n %s\n\n", daxctl_more_info_string); goto out; } rc = daxctl_new(&ctx); if (rc) goto out; main_handle_internal_command(argc, argv, ctx, commands, ARRAY_SIZE(commands), PROG_DAXCTL); daxctl_unref(ctx); fprintf(stderr, "Unknown command: '%s'\n", argv[0]); out: return 1; } ndctl-67/daxctl/device.c000066400000000000000000000325331355562357700153260ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2019 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct { const char *dev; const char *mode; int region_id; bool no_online; bool no_movable; bool force; bool human; bool verbose; } param = { .region_id = -1, }; enum dev_mode { DAXCTL_DEV_MODE_UNKNOWN, DAXCTL_DEV_MODE_DEVDAX, DAXCTL_DEV_MODE_RAM, }; static enum dev_mode reconfig_mode = DAXCTL_DEV_MODE_UNKNOWN; static unsigned long flags; enum memory_zone { MEM_ZONE_MOVABLE, MEM_ZONE_NORMAL, }; static enum memory_zone mem_zone = MEM_ZONE_MOVABLE; enum device_action { ACTION_RECONFIG, ACTION_ONLINE, ACTION_OFFLINE, }; #define BASE_OPTIONS() \ OPT_INTEGER('r', "region", ¶m.region_id, "restrict to the given region"), \ OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats"), \ OPT_BOOLEAN('v', "verbose", ¶m.verbose, "emit more debug messages") #define RECONFIG_OPTIONS() \ OPT_STRING('m', "mode", ¶m.mode, "mode", "mode to switch the device to"), \ OPT_BOOLEAN('N', "no-online", ¶m.no_online, \ "don't auto-online memory sections"), \ OPT_BOOLEAN('f', "force", ¶m.force, \ "attempt to offline memory sections before reconfiguration") #define ZONE_OPTIONS() \ OPT_BOOLEAN('\0', "no-movable", ¶m.no_movable, \ "online memory in ZONE_NORMAL") static const struct option reconfig_options[] = { BASE_OPTIONS(), RECONFIG_OPTIONS(), ZONE_OPTIONS(), OPT_END(), }; static const struct option online_options[] = { BASE_OPTIONS(), ZONE_OPTIONS(), OPT_END(), }; static const struct option offline_options[] = { BASE_OPTIONS(), OPT_END(), }; static const char *parse_device_options(int argc, const char **argv, enum device_action action, const struct option *options, const char *usage, struct daxctl_ctx *ctx) { const char * const u[] = { usage, NULL }; int i, rc = 0; argc = parse_options(argc, argv, options, u, 0); /* Handle action-agnostic non-option arguments */ if (argc == 0) { char *action_string; switch (action) { case ACTION_RECONFIG: action_string = "reconfigure"; break; case ACTION_ONLINE: action_string = "online memory for"; break; case ACTION_OFFLINE: action_string = "offline memory for"; break; default: action_string = "<>"; break; } fprintf(stderr, "specify a device to %s, or \"all\"\n", action_string); rc = -EINVAL; } for (i = 1; i < argc; i++) { fprintf(stderr, "unknown extra parameter \"%s\"\n", argv[i]); rc = -EINVAL; } if (rc) { usage_with_options(u, options); return NULL; } /* Handle action-agnostic options */ if (param.verbose) daxctl_set_log_priority(ctx, LOG_DEBUG); if (param.human) flags |= UTIL_JSON_HUMAN; /* Handle action-specific options */ switch (action) { case ACTION_RECONFIG: if (!param.mode) { fprintf(stderr, "error: a 'mode' option is required\n"); usage_with_options(u, reconfig_options); rc = -EINVAL; } if (strcmp(param.mode, "system-ram") == 0) { reconfig_mode = DAXCTL_DEV_MODE_RAM; if (param.no_movable) mem_zone = MEM_ZONE_NORMAL; } else if (strcmp(param.mode, "devdax") == 0) { reconfig_mode = DAXCTL_DEV_MODE_DEVDAX; if (param.no_online) { fprintf(stderr, "--no-online is incompatible with --mode=devdax\n"); rc = -EINVAL; } } break; case ACTION_ONLINE: if (param.no_movable) mem_zone = MEM_ZONE_NORMAL; /* fall through */ case ACTION_OFFLINE: /* nothing special */ break; } if (rc) { usage_with_options(u, options); return NULL; } return argv[0]; } static int dev_online_memory(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int num_sections, num_on, rc; if (!mem) { fprintf(stderr, "%s: failed to get the memory object\n", devname); return -ENXIO; } /* get total number of sections and sections already online */ num_sections = daxctl_memory_num_sections(mem); if (num_sections < 0) { fprintf(stderr, "%s: failed to get number of memory sections\n", devname); return num_sections; } num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } if (num_on) fprintf(stderr, "%s:\n WARNING: detected a race while onlining memory\n" " Some memory may not be in the expected zone. It is\n" " recommended to disable any other onlining mechanisms,\n" " and retry. If onlining is to be left to other agents,\n" " use the --no-online option to suppress this warning\n", devname); if (num_on == num_sections) { fprintf(stderr, "%s: all memory sections (%d) already online\n", devname, num_on); return 1; } if (num_on > 0) fprintf(stderr, "%s: %d memory section%s already online\n", devname, num_on, num_on == 1 ? "" : "s"); /* online the remaining sections */ if (param.no_movable) rc = daxctl_memory_online_no_movable(mem); else rc = daxctl_memory_online(mem); if (rc < 0) { fprintf(stderr, "%s: failed to online memory: %s\n", devname, strerror(-rc)); return rc; } if (param.verbose) fprintf(stderr, "%s: %d memory section%s onlined\n", devname, rc, rc == 1 ? "" : "s"); /* all sections should now be online */ num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } if (num_on < num_sections) { fprintf(stderr, "%s: failed to online %d memory sections\n", devname, num_sections - num_on); return -ENXIO; } return 0; } static int dev_offline_memory(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int num_sections, num_on, num_off, rc; if (!mem) { fprintf(stderr, "%s: failed to get the memory object\n", devname); return -ENXIO; } /* get total number of sections and sections already offline */ num_sections = daxctl_memory_num_sections(mem); if (num_sections < 0) { fprintf(stderr, "%s: failed to get number of memory sections\n", devname); return num_sections; } num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } num_off = num_sections - num_on; if (num_off == num_sections) { fprintf(stderr, "%s: all memory sections (%d) already offline\n", devname, num_off); return 1; } if (num_off) fprintf(stderr, "%s: %d memory section%s already offline\n", devname, num_off, num_off == 1 ? "" : "s"); /* offline the remaining sections */ rc = daxctl_memory_offline(mem); if (rc < 0) { fprintf(stderr, "%s: failed to offline memory: %s\n", devname, strerror(-rc)); return rc; } if (param.verbose) fprintf(stderr, "%s: %d memory section%s offlined\n", devname, rc, rc == 1 ? "" : "s"); /* all sections should now be ofline */ num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } if (num_on) { fprintf(stderr, "%s: failed to offline %d memory sections\n", devname, num_on); return -ENXIO; } return 0; } static int disable_devdax_device(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc; if (mem) { fprintf(stderr, "%s was already in system-ram mode\n", devname); return 1; } rc = daxctl_dev_disable(dev); if (rc) { fprintf(stderr, "%s: disable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } return 0; } static int reconfig_mode_system_ram(struct daxctl_dev *dev) { int rc, skip_enable = 0; if (daxctl_dev_is_enabled(dev)) { rc = disable_devdax_device(dev); if (rc < 0) return rc; if (rc > 0) skip_enable = 1; } if (!skip_enable) { rc = daxctl_dev_enable_ram(dev); if (rc) return rc; } if (param.no_online) return 0; return dev_online_memory(dev); } static int disable_system_ram_device(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc; if (!mem) { fprintf(stderr, "%s was already in devdax mode\n", devname); return 1; } if (param.force) { rc = dev_offline_memory(dev); if (rc < 0) return rc; } rc = daxctl_memory_is_online(mem); if (rc < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-rc)); return rc; } if (rc > 0) { if (param.verbose) { fprintf(stderr, "%s: found %d memory sections online\n", devname, rc); fprintf(stderr, "%s: refusing to change modes\n", devname); } return -EBUSY; } rc = daxctl_dev_disable(dev); if (rc) { fprintf(stderr, "%s: disable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } return 0; } static int reconfig_mode_devdax(struct daxctl_dev *dev) { int rc; if (daxctl_dev_is_enabled(dev)) { rc = disable_system_ram_device(dev); if (rc) return rc; } rc = daxctl_dev_enable_devdax(dev); if (rc) return rc; return 0; } static int do_reconfig(struct daxctl_dev *dev, enum dev_mode mode, struct json_object **jdevs) { const char *devname = daxctl_dev_get_devname(dev); struct json_object *jdev; int rc = 0; switch (mode) { case DAXCTL_DEV_MODE_RAM: rc = reconfig_mode_system_ram(dev); break; case DAXCTL_DEV_MODE_DEVDAX: rc = reconfig_mode_devdax(dev); break; default: fprintf(stderr, "%s: unknown mode requested: %d\n", devname, mode); rc = -EINVAL; } if (rc < 0) return rc; *jdevs = json_object_new_array(); if (*jdevs) { jdev = util_daxctl_dev_to_json(dev, flags); if (jdev) json_object_array_add(*jdevs, jdev); } return 0; } static int do_xline(struct daxctl_dev *dev, enum device_action action) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc; if (!mem) { fprintf(stderr, "%s: memory operations are not applicable in devdax mode\n", devname); return -ENXIO; } switch (action) { case ACTION_ONLINE: rc = dev_online_memory(dev); break; case ACTION_OFFLINE: rc = dev_offline_memory(dev); break; default: fprintf(stderr, "%s: invalid action: %d\n", devname, action); rc = -EINVAL; } return rc; } static int do_xaction_device(const char *device, enum device_action action, struct daxctl_ctx *ctx, int *processed) { struct json_object *jdevs = NULL; struct daxctl_region *region; struct daxctl_dev *dev; int rc = -ENXIO; *processed = 0; daxctl_region_foreach(ctx, region) { if (param.region_id >= 0 && param.region_id != daxctl_region_get_id(region)) continue; daxctl_dev_foreach(region, dev) { if (!util_daxctl_dev_filter(dev, device)) continue; switch (action) { case ACTION_RECONFIG: rc = do_reconfig(dev, reconfig_mode, &jdevs); if (rc == 0) (*processed)++; break; case ACTION_ONLINE: rc = do_xline(dev, action); if (rc == 0) (*processed)++; break; case ACTION_OFFLINE: rc = do_xline(dev, action); if (rc == 0) (*processed)++; break; default: rc = -EINVAL; break; } } } /* * jdevs is the containing json array for all devices we are reporting * on. It therefore needs to be outside the region/device iterators, * and passed in to the do_ functions to add their objects to */ if (jdevs) util_display_json_array(stdout, jdevs, flags); return rc; } int cmd_reconfig_device(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl reconfigure-device []"; const char *device = parse_device_options(argc, argv, ACTION_RECONFIG, reconfig_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_RECONFIG, ctx, &processed); if (rc < 0) fprintf(stderr, "error reconfiguring devices: %s\n", strerror(-rc)); fprintf(stderr, "reconfigured %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_online_memory(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl online-memory []"; const char *device = parse_device_options(argc, argv, ACTION_ONLINE, online_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_ONLINE, ctx, &processed); if (rc < 0) fprintf(stderr, "error onlining memory: %s\n", strerror(-rc)); fprintf(stderr, "onlined memory for %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_offline_memory(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl offline-memory []"; const char *device = parse_device_options(argc, argv, ACTION_OFFLINE, offline_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_OFFLINE, ctx, &processed); if (rc < 0) fprintf(stderr, "error offlining memory: %s\n", strerror(-rc)); fprintf(stderr, "offlined memory for %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } ndctl-67/daxctl/lib/000077500000000000000000000000001355562357700144635ustar00rootroot00000000000000ndctl-67/daxctl/lib/Makefile.am000066400000000000000000000014631355562357700165230ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in %.pc: %.pc.in Makefile $(SED_PROCESS) pkginclude_HEADERS = ../libdaxctl.h lib_LTLIBRARIES = libdaxctl.la libdaxctl_la_SOURCES =\ ../libdaxctl.h \ libdaxctl-private.h \ ../../util/iomem.c \ ../../util/iomem.h \ ../../util/sysfs.c \ ../../util/sysfs.h \ ../../util/log.c \ ../../util/log.h \ libdaxctl.c libdaxctl_la_LIBADD =\ $(UUID_LIBS) \ $(KMOD_LIBS) daxctl_modprobe_data_DATA = daxctl.conf EXTRA_DIST += libdaxctl.sym libdaxctl_la_LDFLAGS = $(AM_LDFLAGS) \ -version-info $(LIBDAXCTL_CURRENT):$(LIBDAXCTL_REVISION):$(LIBDAXCTL_AGE) \ -Wl,--version-script=$(top_srcdir)/daxctl/lib/libdaxctl.sym libdaxctl_la_DEPENDENCIES = libdaxctl.sym pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libdaxctl.pc EXTRA_DIST += libdaxctl.pc.in CLEANFILES += libdaxctl.pc ndctl-67/daxctl/lib/daxctl.conf000066400000000000000000000000601355562357700166050ustar00rootroot00000000000000blacklist dax_pmem_compat alias nd:t7* dax_pmem ndctl-67/daxctl/lib/libdaxctl-private.h000066400000000000000000000050041355562357700202510ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef _LIBDAXCTL_PRIVATE_H_ #define _LIBDAXCTL_PRIVATE_H_ #include #define DAXCTL_EXPORT __attribute__ ((visibility("default"))) enum dax_subsystem { DAX_UNKNOWN, DAX_CLASS, DAX_BUS, }; static const char *dax_subsystems[] = { [DAX_CLASS] = "/sys/class/dax", [DAX_BUS] = "/sys/bus/dax/devices", }; enum daxctl_dev_mode { DAXCTL_DEV_MODE_DEVDAX = 0, DAXCTL_DEV_MODE_RAM, DAXCTL_DEV_MODE_END, }; static const char *dax_modules[] = { [DAXCTL_DEV_MODE_DEVDAX] = "device_dax", [DAXCTL_DEV_MODE_RAM] = "kmem", }; enum memory_op { MEM_SET_OFFLINE, MEM_SET_ONLINE, MEM_SET_ONLINE_NO_MOVABLE, MEM_IS_ONLINE, MEM_COUNT, MEM_GET_ZONE, }; /* OR-able flags, 1, 2, 4, 8 etc */ enum memory_op_status { MEM_ST_OK = 0, MEM_ST_ZONE_INCONSISTENT = 1, }; enum memory_zones { MEM_ZONE_UNKNOWN = 1, MEM_ZONE_MOVABLE, MEM_ZONE_NORMAL, }; static const char *zone_strings[] = { [MEM_ZONE_UNKNOWN] = "mixed", [MEM_ZONE_NORMAL] = "Normal", [MEM_ZONE_MOVABLE] = "Movable", }; static const char *state_strings[] = { [MEM_ZONE_NORMAL] = "online", [MEM_ZONE_MOVABLE] = "online_movable", }; /** * struct daxctl_region - container for dax_devices */ #define REGION_BUF_SIZE 50 struct daxctl_region { int id; uuid_t uuid; int refcount; char *devname; size_t buf_len; void *region_buf; int devices_init; char *region_path; unsigned long align; unsigned long long size; struct daxctl_ctx *ctx; struct list_node list; struct list_head devices; }; struct daxctl_dev { int id, major, minor; void *dev_buf; size_t buf_len; char *dev_path; struct list_node list; unsigned long long resource; unsigned long long size; struct kmod_module *module; struct daxctl_region *region; struct daxctl_memory *mem; int target_node; }; struct daxctl_memory { struct daxctl_dev *dev; void *mem_buf; size_t buf_len; char *node_path; unsigned long block_size; enum memory_zones zone; }; static inline int check_kmod(struct kmod_ctx *kmod_ctx) { return kmod_ctx ? 0 : -ENXIO; } #endif /* _LIBDAXCTL_PRIVATE_H_ */ ndctl-67/daxctl/lib/libdaxctl.c000066400000000000000000001016511355562357700166010ustar00rootroot00000000000000/* * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libdaxctl-private.h" static const char *attrs = "dax_region"; static void free_region(struct daxctl_region *region, struct list_head *head); /** * struct daxctl_ctx - library user context to find "nd" instances * * Instantiate with daxctl_new(), which takes an initial reference. Free * the context by dropping the reference count to zero with * daxctl_unref(), or take additional references with daxctl_ref() * @timeout: default library timeout in milliseconds */ struct daxctl_ctx { /* log_ctx must be first member for daxctl_set_log_fn compat */ struct log_ctx ctx; int refcount; void *userdata; int regions_init; struct list_head regions; struct kmod_ctx *kmod_ctx; }; /** * daxctl_get_userdata - retrieve stored data pointer from library context * @ctx: daxctl library context * * This might be useful to access from callbacks like a custom logging * function. */ DAXCTL_EXPORT void *daxctl_get_userdata(struct daxctl_ctx *ctx) { if (ctx == NULL) return NULL; return ctx->userdata; } /** * daxctl_set_userdata - store custom @userdata in the library context * @ctx: daxctl library context * @userdata: data pointer */ DAXCTL_EXPORT void daxctl_set_userdata(struct daxctl_ctx *ctx, void *userdata) { if (ctx == NULL) return; ctx->userdata = userdata; } /** * daxctl_new - instantiate a new library context * @ctx: context to establish * * Returns zero on success and stores an opaque pointer in ctx. The * context is freed by daxctl_unref(), i.e. daxctl_new() implies an * internal daxctl_ref(). */ DAXCTL_EXPORT int daxctl_new(struct daxctl_ctx **ctx) { struct kmod_ctx *kmod_ctx; struct daxctl_ctx *c; int rc = 0; c = calloc(1, sizeof(struct daxctl_ctx)); if (!c) return -ENOMEM; kmod_ctx = kmod_new(NULL, NULL); if (check_kmod(kmod_ctx) != 0) { rc = -ENXIO; goto out; } c->refcount = 1; log_init(&c->ctx, "libdaxctl", "DAXCTL_LOG"); info(c, "ctx %p created\n", c); dbg(c, "log_priority=%d\n", c->ctx.log_priority); *ctx = c; list_head_init(&c->regions); c->kmod_ctx = kmod_ctx; return 0; out: free(c); return rc; } /** * daxctl_ref - take an additional reference on the context * @ctx: context established by daxctl_new() */ DAXCTL_EXPORT struct daxctl_ctx *daxctl_ref(struct daxctl_ctx *ctx) { if (ctx == NULL) return NULL; ctx->refcount++; return ctx; } /** * daxctl_unref - drop a context reference count * @ctx: context established by daxctl_new() * * Drop a reference and if the resulting reference count is 0 destroy * the context. */ DAXCTL_EXPORT void daxctl_unref(struct daxctl_ctx *ctx) { struct daxctl_region *region, *_r; if (ctx == NULL) return; ctx->refcount--; if (ctx->refcount > 0) return; list_for_each_safe(&ctx->regions, region, _r, list) free_region(region, &ctx->regions); kmod_unref(ctx->kmod_ctx); info(ctx, "context %p released\n", ctx); free(ctx); } /** * daxctl_set_log_fn - override default log routine * @ctx: daxctl library context * @log_fn: function to be called for logging messages * * The built-in logging writes to stderr. It can be overridden by a * custom function, to plug log messages into the user's logging * functionality. */ DAXCTL_EXPORT void daxctl_set_log_fn(struct daxctl_ctx *ctx, void (*daxctl_log_fn)(struct daxctl_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args)) { ctx->ctx.log_fn = (log_fn) daxctl_log_fn; info(ctx, "custom logging function %p registered\n", daxctl_log_fn); } /** * daxctl_get_log_priority - retrieve current library loglevel (syslog) * @ctx: daxctl library context */ DAXCTL_EXPORT int daxctl_get_log_priority(struct daxctl_ctx *ctx) { return ctx->ctx.log_priority; } /** * daxctl_set_log_priority - set log verbosity * @priority: from syslog.h, LOG_ERR, LOG_INFO, LOG_DEBUG * * Note: LOG_DEBUG requires library be built with "configure --enable-debug" */ DAXCTL_EXPORT void daxctl_set_log_priority(struct daxctl_ctx *ctx, int priority) { ctx->ctx.log_priority = priority; } DAXCTL_EXPORT struct daxctl_ctx *daxctl_region_get_ctx( struct daxctl_region *region) { return region->ctx; } DAXCTL_EXPORT void daxctl_region_get_uuid(struct daxctl_region *region, uuid_t uu) { uuid_copy(uu, region->uuid); } static void free_mem(struct daxctl_dev *dev) { if (dev->mem) { free(dev->mem->node_path); free(dev->mem->mem_buf); free(dev->mem); dev->mem = NULL; } } static void free_dev(struct daxctl_dev *dev, struct list_head *head) { if (head) list_del_from(head, &dev->list); kmod_module_unref(dev->module); free(dev->dev_buf); free(dev->dev_path); free_mem(dev); free(dev); } static void free_region(struct daxctl_region *region, struct list_head *head) { struct daxctl_dev *dev, *_d; list_for_each_safe(®ion->devices, dev, _d, list) free_dev(dev, ®ion->devices); if (head) list_del_from(head, ®ion->list); free(region->region_path); free(region->region_buf); free(region->devname); free(region); } DAXCTL_EXPORT void daxctl_region_unref(struct daxctl_region *region) { struct daxctl_ctx *ctx; if (!region) return; region->refcount--; if (region->refcount) return; ctx = region->ctx; dbg(ctx, "%s: %s\n", __func__, daxctl_region_get_devname(region)); free_region(region, &ctx->regions); } DAXCTL_EXPORT void daxctl_region_ref(struct daxctl_region *region) { if (region) region->refcount++; } static struct daxctl_region *add_dax_region(void *parent, int id, const char *base) { struct daxctl_region *region, *region_dup; struct daxctl_ctx *ctx = parent; char buf[SYSFS_ATTR_SIZE]; char *path; dbg(ctx, "%s: \'%s\'\n", __func__, base); daxctl_region_foreach(ctx, region_dup) if (strcmp(region_dup->region_path, base) == 0) return region_dup; path = calloc(1, strlen(base) + 100); if (!path) return NULL; region = calloc(1, sizeof(*region)); if (!region) goto err_region; region->id = id; region->align = -1; region->size = -1; region->ctx = ctx; region->refcount = 1; list_head_init(®ion->devices); region->devname = strdup(devpath_to_devname(base)); sprintf(path, "%s/%s/size", base, attrs); if (sysfs_read_attr(ctx, path, buf) == 0) region->size = strtoull(buf, NULL, 0); sprintf(path, "%s/%s/align", base, attrs); if (sysfs_read_attr(ctx, path, buf) == 0) region->align = strtoul(buf, NULL, 0); region->region_path = strdup(base); if (!region->region_path) goto err_read; region->region_buf = calloc(1, strlen(path) + strlen(attrs) + REGION_BUF_SIZE); if (!region->region_buf) goto err_read; region->buf_len = strlen(path) + REGION_BUF_SIZE; list_add(&ctx->regions, ®ion->list); free(path); return region; err_read: free(region->region_buf); free(region->region_path); free(region); err_region: free(path); return NULL; } DAXCTL_EXPORT struct daxctl_region *daxctl_new_region(struct daxctl_ctx *ctx, int id, uuid_t uuid, const char *path) { struct daxctl_region *region; region = add_dax_region(ctx, id, path); if (!region) return NULL; uuid_copy(region->uuid, uuid); dbg(ctx, "%s: %s\n", __func__, daxctl_region_get_devname(region)); return region; } static bool device_model_is_dax_bus(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); char *path = dev->dev_buf, *resolved; size_t len = dev->buf_len; struct stat sb; if (snprintf(path, len, "/dev/%s", devname) < 0) return false; if (lstat(path, &sb) < 0) { err(ctx, "%s: stat for %s failed: %s\n", devname, path, strerror(errno)); return false; } if (snprintf(path, len, "/sys/dev/char/%d:%d/subsystem", major(sb.st_rdev), minor(sb.st_rdev)) < 0) return false; resolved = realpath(path, NULL); if (!resolved) { err(ctx, "%s: unable to determine subsys: %s\n", devname, strerror(errno)); return false; } if (strcmp(resolved, "/sys/bus/dax") == 0) { free(resolved); return true; } free(resolved); return false; } static int dev_is_system_ram_capable(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); char *mod_path, *mod_base; char path[200]; const int len = sizeof(path); if (!device_model_is_dax_bus(dev)) return false; if (!daxctl_dev_is_enabled(dev)) return false; if (snprintf(path, len, "%s/driver", dev->dev_path) >= len) { err(ctx, "%s: buffer too small!\n", devname); return false; } mod_path = realpath(path, NULL); if (!mod_path) return false; mod_base = basename(mod_path); if (strcmp(mod_base, dax_modules[DAXCTL_DEV_MODE_RAM]) == 0) { free(mod_path); return true; } free(mod_path); return false; } /* * This checks for the device to be in system-ram mode, so calling * daxctl_dev_get_memory() on a devdax mode device will always return NULL. */ static struct daxctl_memory *daxctl_dev_alloc_mem(struct daxctl_dev *dev) { const char *size_path = "/sys/devices/system/memory/block_size_bytes"; const char *node_base = "/sys/devices/system/node/node"; const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); struct daxctl_memory *mem; char buf[SYSFS_ATTR_SIZE]; int node_num; if (!dev_is_system_ram_capable(dev)) return NULL; mem = calloc(1, sizeof(*mem)); if (!mem) return NULL; mem->dev = dev; if (sysfs_read_attr(ctx, size_path, buf) == 0) { mem->block_size = strtoul(buf, NULL, 16); if (mem->block_size == 0 || mem->block_size == ULONG_MAX) { err(ctx, "%s: Unable to determine memblock size: %s\n", devname, strerror(errno)); mem->block_size = 0; } } node_num = daxctl_dev_get_target_node(dev); if (node_num >= 0) { if (asprintf(&mem->node_path, "%s%d", node_base, node_num) < 0) { err(ctx, "%s: Unable to set node_path\n", devname); goto err_mem; } } mem->mem_buf = calloc(1, strlen(node_base) + 256); if (!mem->mem_buf) goto err_node; mem->buf_len = strlen(node_base) + 256; dev->mem = mem; return mem; err_node: free(mem->node_path); err_mem: free(mem); return NULL; } static void *add_dax_dev(void *parent, int id, const char *daxdev_base) { const char *devname = devpath_to_devname(daxdev_base); char *path = calloc(1, strlen(daxdev_base) + 100); struct daxctl_region *region = parent; struct daxctl_ctx *ctx = region->ctx; struct daxctl_dev *dev, *dev_dup; char buf[SYSFS_ATTR_SIZE]; struct stat st; if (!path) return NULL; dbg(ctx, "%s: base: \'%s\'\n", __func__, daxdev_base); dev = calloc(1, sizeof(*dev)); if (!dev) goto err_dev; dev->id = id; dev->region = region; sprintf(path, "/dev/%s", devname); if (stat(path, &st) < 0) goto err_read; dev->major = major(st.st_rdev); dev->minor = minor(st.st_rdev); sprintf(path, "%s/resource", daxdev_base); if (sysfs_read_attr(ctx, path, buf) == 0) dev->resource = strtoull(buf, NULL, 0); else dev->resource = iomem_get_dev_resource(ctx, daxdev_base); sprintf(path, "%s/size", daxdev_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; dev->size = strtoull(buf, NULL, 0); dev->dev_path = strdup(daxdev_base); if (!dev->dev_path) goto err_read; dev->dev_buf = calloc(1, strlen(daxdev_base) + 50); if (!dev->dev_buf) goto err_read; dev->buf_len = strlen(daxdev_base) + 50; sprintf(path, "%s/target_node", daxdev_base); if (sysfs_read_attr(ctx, path, buf) == 0) dev->target_node = strtol(buf, NULL, 0); else dev->target_node = -1; daxctl_dev_foreach(region, dev_dup) if (dev_dup->id == dev->id) { free_dev(dev, NULL); free(path); return dev_dup; } list_add(®ion->devices, &dev->list); free(path); return dev; err_read: free(dev->dev_buf); free(dev->dev_path); free(dev); err_dev: free(path); return NULL; } DAXCTL_EXPORT int daxctl_region_get_id(struct daxctl_region *region) { return region->id; } DAXCTL_EXPORT unsigned long daxctl_region_get_align(struct daxctl_region *region) { return region->align; } DAXCTL_EXPORT unsigned long long daxctl_region_get_size(struct daxctl_region *region) { return region->size; } DAXCTL_EXPORT const char *daxctl_region_get_devname(struct daxctl_region *region) { return region->devname; } DAXCTL_EXPORT const char *daxctl_region_get_path(struct daxctl_region *region) { return region->region_path; } DAXCTL_EXPORT unsigned long long daxctl_region_get_available_size( struct daxctl_region *region) { struct daxctl_ctx *ctx = daxctl_region_get_ctx(region); char *path = region->region_buf; char buf[SYSFS_ATTR_SIZE], *end; int len = region->buf_len; unsigned long long avail; if (snprintf(path, len, "%s/%s/available_size", region->region_path, attrs) >= len) { err(ctx, "%s: buffer too small!\n", daxctl_region_get_devname(region)); return 0; } if (sysfs_read_attr(ctx, path, buf) < 0) return 0; avail = strtoull(buf, &end, 0); if (buf[0] && *end == '\0') return avail; return 0; } DAXCTL_EXPORT struct daxctl_dev *daxctl_region_get_dev_seed( struct daxctl_region *region) { struct daxctl_ctx *ctx = daxctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; struct daxctl_dev *dev; if (snprintf(path, len, "%s/%s/seed", region->region_path, attrs) >= len) { err(ctx, "%s: buffer too small!\n", daxctl_region_get_devname(region)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; daxctl_dev_foreach(region, dev) if (strcmp(buf, daxctl_dev_get_devname(dev)) == 0) return dev; return NULL; } static void dax_devices_init(struct daxctl_region *region) { struct daxctl_ctx *ctx = daxctl_region_get_ctx(region); char daxdev_fmt[50]; size_t i; if (region->devices_init) return; region->devices_init = 1; sprintf(daxdev_fmt, "dax%d.", region->id); for (i = 0; i < ARRAY_SIZE(dax_subsystems); i++) { char *region_path; if (i == DAX_BUS) region_path = region->region_path; else if (i == DAX_CLASS) { if (asprintf(®ion_path, "%s/dax", region->region_path) < 0) { dbg(ctx, "region path alloc fail\n"); continue; } } else continue; sysfs_device_parse(ctx, region_path, daxdev_fmt, region, add_dax_dev); if (i == DAX_CLASS) free(region_path); } } static char *dax_region_path(const char *device, enum dax_subsystem subsys) { char *path, *region_path, *c; if (asprintf(&path, "%s/%s", dax_subsystems[subsys], device) < 0) return NULL; /* dax_region must be the instance's direct parent */ region_path = realpath(path, NULL); free(path); if (!region_path) return NULL; /* * 'region_path' is now regionX/dax/daxX.Y' (DAX_CLASS), or * regionX/daxX.Y (DAX_BUS), trim it back to the regionX * component */ c = strrchr(region_path, '/'); if (!c) { free(region_path); return NULL; } *c = '\0'; if (subsys == DAX_BUS) return region_path; c = strrchr(region_path, '/'); if (!c) { free(region_path); return NULL; } *c = '\0'; return region_path; } static void __dax_regions_init(struct daxctl_ctx *ctx, enum dax_subsystem subsys) { struct dirent *de; DIR *dir = NULL; dir = opendir(dax_subsystems[subsys]); if (!dir) { dbg(ctx, "no dax regions found via: %s\n", dax_subsystems[subsys]); return; } while ((de = readdir(dir)) != NULL) { struct daxctl_region *region; int id, region_id; char *dev_path; if (de->d_ino == 0) continue; if (sscanf(de->d_name, "dax%d.%d", ®ion_id, &id) != 2) continue; dev_path = dax_region_path(de->d_name, subsys); if (!dev_path) { err(ctx, "dax region path allocation failure\n"); continue; } region = add_dax_region(ctx, region_id, dev_path); free(dev_path); if (!region) err(ctx, "add_dax_region() for %s failed\n", de->d_name); } closedir(dir); } static void dax_regions_init(struct daxctl_ctx *ctx) { size_t i; if (ctx->regions_init) return; ctx->regions_init = 1; for (i = 0; i < ARRAY_SIZE(dax_subsystems); i++) { if (i == DAX_UNKNOWN) continue; __dax_regions_init(ctx, i); } } static int is_enabled(const char *drvpath) { struct stat st; if (lstat(drvpath, &st) < 0 || !S_ISLNK(st.st_mode)) return 0; else return 1; } static int daxctl_bind(struct daxctl_ctx *ctx, const char *devname, const char *mod_name) { DIR *dir; int rc = 0; char path[200]; struct dirent *de; const int len = sizeof(path); if (!devname) { err(ctx, "missing devname\n"); return -EINVAL; } if (snprintf(path, len, "/sys/bus/dax/drivers") >= len) { err(ctx, "%s: buffer too small!\n", devname); return -ENXIO; } dir = opendir(path); if (!dir) { err(ctx, "%s: opendir(\"%s\") failed\n", devname, path); return -ENXIO; } while ((de = readdir(dir)) != NULL) { char *drv_path; if (de->d_ino == 0) continue; if (de->d_name[0] == '.') continue; if (strcmp(de->d_name, mod_name) != 0) continue; if (asprintf(&drv_path, "%s/%s/new_id", path, de->d_name) < 0) { err(ctx, "%s: path allocation failure\n", devname); rc = -ENOMEM; break; } rc = sysfs_write_attr_quiet(ctx, drv_path, devname); free(drv_path); if (asprintf(&drv_path, "%s/%s/bind", path, de->d_name) < 0) { err(ctx, "%s: path allocation failure\n", devname); rc = -ENOMEM; break; } rc = sysfs_write_attr_quiet(ctx, drv_path, devname); free(drv_path); break; } closedir(dir); if (rc) { dbg(ctx, "%s: bind failed\n", devname); return rc; } return 0; } static int daxctl_unbind(struct daxctl_ctx *ctx, const char *devpath) { const char *devname = devpath_to_devname(devpath); char path[200]; const int len = sizeof(path); int rc; if (snprintf(path, len, "%s/driver/remove_id", devpath) >= len) { err(ctx, "%s: buffer too small!\n", devname); return -ENXIO; } rc = sysfs_write_attr(ctx, path, devname); if (rc) return rc; if (snprintf(path, len, "%s/driver/unbind", devpath) >= len) { err(ctx, "%s: buffer too small!\n", devname); return -ENXIO; } return sysfs_write_attr(ctx, path, devname); } DAXCTL_EXPORT int daxctl_dev_is_enabled(struct daxctl_dev *dev) { struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); char *path = dev->dev_buf; int len = dev->buf_len; if (!device_model_is_dax_bus(dev)) return 1; if (snprintf(path, len, "%s/driver", dev->dev_path) >= len) { err(ctx, "%s: buffer too small!\n", daxctl_dev_get_devname(dev)); return 0; } return is_enabled(path); } static int daxctl_insert_kmod_for_mode(struct daxctl_dev *dev, const char *mod_name) { const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); struct kmod_module *kmod; int rc; rc = kmod_module_new_from_name(ctx->kmod_ctx, mod_name, &kmod); if (rc < 0) { err(ctx, "%s: failed getting module for: %s: %s\n", devname, mod_name, strerror(-rc)); return rc; } /* if the driver is builtin, this Just Works */ dbg(ctx, "%s inserting module: %s\n", devname, kmod_module_get_name(kmod)); rc = kmod_module_probe_insert_module(kmod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); if (rc < 0) { err(ctx, "%s: insert failure: %d\n", devname, rc); return rc; } dev->module = kmod; return 0; } static int daxctl_dev_enable(struct daxctl_dev *dev, enum daxctl_dev_mode mode) { struct daxctl_region *region = daxctl_dev_get_region(dev); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); const char *mod_name = dax_modules[mode]; int rc; if (!device_model_is_dax_bus(dev)) { err(ctx, "%s: error: device model is dax-class\n", devname); err(ctx, "%s: see man daxctl-migrate-device-model\n", devname); return -EOPNOTSUPP; } if (daxctl_dev_is_enabled(dev)) return 0; if (mode >= DAXCTL_DEV_MODE_END || mod_name == NULL) { err(ctx, "%s: Invalid mode: %d\n", devname, mode); return -EINVAL; } rc = daxctl_insert_kmod_for_mode(dev, mod_name); if (rc) return rc; rc = daxctl_bind(ctx, devname, mod_name); if (!daxctl_dev_is_enabled(dev)) { err(ctx, "%s: failed to enable\n", devname); return rc ? rc : -ENXIO; } region->devices_init = 0; dax_devices_init(region); rc = 0; dbg(ctx, "%s: enabled\n", devname); return rc; } DAXCTL_EXPORT int daxctl_dev_enable_devdax(struct daxctl_dev *dev) { return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_DEVDAX); } DAXCTL_EXPORT int daxctl_dev_enable_ram(struct daxctl_dev *dev) { return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_RAM); } DAXCTL_EXPORT int daxctl_dev_disable(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); if (!device_model_is_dax_bus(dev)) { err(ctx, "%s: error: device model is dax-class\n", devname); err(ctx, "%s: see man daxctl-migrate-device-model\n", devname); return -EOPNOTSUPP; } if (!daxctl_dev_is_enabled(dev)) return 0; /* If there is a memory object, first free that */ free_mem(dev); daxctl_unbind(ctx, dev->dev_path); if (daxctl_dev_is_enabled(dev)) { err(ctx, "%s: failed to disable\n", devname); return -EBUSY; } kmod_module_unref(dev->module); dbg(ctx, "%s: disabled\n", devname); return 0; } DAXCTL_EXPORT struct daxctl_ctx *daxctl_dev_get_ctx(struct daxctl_dev *dev) { return dev->region->ctx; } DAXCTL_EXPORT struct daxctl_dev *daxctl_dev_get_first(struct daxctl_region *region) { dax_devices_init(region); return list_top(®ion->devices, struct daxctl_dev, list); } DAXCTL_EXPORT struct daxctl_dev *daxctl_dev_get_next(struct daxctl_dev *dev) { struct daxctl_region *region = dev->region; return list_next(®ion->devices, dev, list); } DAXCTL_EXPORT struct daxctl_region *daxctl_region_get_first( struct daxctl_ctx *ctx) { dax_regions_init(ctx); return list_top(&ctx->regions, struct daxctl_region, list); } DAXCTL_EXPORT struct daxctl_region *daxctl_region_get_next( struct daxctl_region *region) { struct daxctl_ctx *ctx = region->ctx; return list_next(&ctx->regions, region, list); } DAXCTL_EXPORT struct daxctl_region *daxctl_dev_get_region(struct daxctl_dev *dev) { return dev->region; } DAXCTL_EXPORT int daxctl_dev_get_id(struct daxctl_dev *dev) { return dev->id; } DAXCTL_EXPORT const char *daxctl_dev_get_devname(struct daxctl_dev *dev) { return devpath_to_devname(dev->dev_path); } DAXCTL_EXPORT int daxctl_dev_get_major(struct daxctl_dev *dev) { return dev->major; } DAXCTL_EXPORT int daxctl_dev_get_minor(struct daxctl_dev *dev) { return dev->minor; } DAXCTL_EXPORT unsigned long long daxctl_dev_get_resource(struct daxctl_dev *dev) { return dev->resource; } DAXCTL_EXPORT unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev) { return dev->size; } DAXCTL_EXPORT int daxctl_dev_get_target_node(struct daxctl_dev *dev) { return dev->target_node; } DAXCTL_EXPORT struct daxctl_memory *daxctl_dev_get_memory(struct daxctl_dev *dev) { if (dev->mem) return dev->mem; else return daxctl_dev_alloc_mem(dev); } DAXCTL_EXPORT struct daxctl_dev *daxctl_memory_get_dev(struct daxctl_memory *mem) { return mem->dev; } DAXCTL_EXPORT const char *daxctl_memory_get_node_path(struct daxctl_memory *mem) { return mem->node_path; } DAXCTL_EXPORT unsigned long daxctl_memory_get_block_size(struct daxctl_memory *mem) { return mem->block_size; } static int memblock_is_online(struct daxctl_memory *mem, char *memblock) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); int len = mem->buf_len, rc; char buf[SYSFS_ATTR_SIZE]; char *path = mem->mem_buf; const char *node_path; node_path = daxctl_memory_get_node_path(mem); if (!node_path) return -ENXIO; rc = snprintf(path, len, "%s/%s/state", node_path, memblock); if (rc < 0) return -ENOMEM; rc = sysfs_read_attr(ctx, path, buf); if (rc) { err(ctx, "%s: Failed to read %s: %s\n", devname, path, strerror(-rc)); return rc; } if (strncmp(buf, "online", 6) == 0) return 1; /* offline */ return 0; } static int online_one_memblock(struct daxctl_memory *mem, char *memblock, enum memory_zones zone, int *status) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); int len = mem->buf_len, rc; char *path = mem->mem_buf; const char *node_path; node_path = daxctl_memory_get_node_path(mem); if (!node_path) return -ENXIO; rc = snprintf(path, len, "%s/%s/state", node_path, memblock); if (rc < 0) return -ENOMEM; rc = memblock_is_online(mem, memblock); if (rc) return rc; switch (zone) { case MEM_ZONE_MOVABLE: case MEM_ZONE_NORMAL: rc = sysfs_write_attr_quiet(ctx, path, state_strings[zone]); break; default: rc = -EINVAL; } if (rc) { /* * If the block got onlined, potentially by some other agent, * do nothing for now. There will be a full scan for zone * correctness later. */ if (memblock_is_online(mem, memblock) == 1) return 0; } return rc; } static int offline_one_memblock(struct daxctl_memory *mem, char *memblock) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); const char *mode = "offline"; int len = mem->buf_len, rc; char *path = mem->mem_buf; const char *node_path; node_path = daxctl_memory_get_node_path(mem); if (!node_path) return -ENXIO; rc = snprintf(path, len, "%s/%s/state", node_path, memblock); if (rc < 0) return -ENOMEM; /* if already offline, there is nothing to do */ rc = memblock_is_online(mem, memblock); if (rc < 0) return rc; if (!rc) return 1; rc = sysfs_write_attr_quiet(ctx, path, mode); if (rc) { /* check if something raced us to offline (unlikely) */ if (!memblock_is_online(mem, memblock)) return 1; err(ctx, "%s: Failed to offline %s: %s\n", devname, path, strerror(-rc)); } return rc; } static int memblock_find_zone(struct daxctl_memory *mem, char *memblock, int *status) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); enum memory_zones cur_zone; int len = mem->buf_len, rc; char buf[SYSFS_ATTR_SIZE]; char *path = mem->mem_buf; const char *node_path; rc = memblock_is_online(mem, memblock); if (rc < 0) return rc; if (rc == 0) return -ENXIO; node_path = daxctl_memory_get_node_path(mem); if (!node_path) return -ENXIO; rc = snprintf(path, len, "%s/%s/valid_zones", node_path, memblock); if (rc < 0) return -ENOMEM; rc = sysfs_read_attr(ctx, path, buf); if (rc) { err(ctx, "%s: Failed to read %s: %s\n", devname, path, strerror(-rc)); return rc; } if (strcmp(buf, zone_strings[MEM_ZONE_MOVABLE]) == 0) cur_zone = MEM_ZONE_MOVABLE; else if (strcmp(buf, zone_strings[MEM_ZONE_NORMAL]) == 0) cur_zone = MEM_ZONE_NORMAL; else cur_zone = MEM_ZONE_UNKNOWN; if (mem->zone) { if (mem->zone == cur_zone) return 0; else *status |= MEM_ST_ZONE_INCONSISTENT; } else { mem->zone = cur_zone; } return 0; } static int memblock_in_dev(struct daxctl_memory *mem, const char *memblock) { const char *mem_base = "/sys/devices/system/memory/"; struct daxctl_dev *dev = daxctl_memory_get_dev(mem); unsigned long long memblock_res, dev_start, dev_end; const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); int rc, path_len = mem->buf_len; unsigned long memblock_size; char buf[SYSFS_ATTR_SIZE]; unsigned long phys_index; char *path = mem->mem_buf; if (snprintf(path, path_len, "%s/%s/phys_index", mem_base, memblock) < 0) return -ENXIO; rc = sysfs_read_attr(ctx, path, buf); if (rc == 0) { phys_index = strtoul(buf, NULL, 16); if (phys_index == 0 || phys_index == ULONG_MAX) { rc = -errno; err(ctx, "%s: %s: Unable to determine phys_index: %s\n", devname, memblock, strerror(-rc)); return rc; } } else { err(ctx, "%s: %s: Unable to determine phys_index: %s\n", devname, memblock, strerror(-rc)); return rc; } dev_start = daxctl_dev_get_resource(dev); if (!dev_start) { err(ctx, "%s: Unable to determine resource\n", devname); return -EACCES; } dev_end = dev_start + daxctl_dev_get_size(dev); memblock_size = daxctl_memory_get_block_size(mem); if (!memblock_size) { err(ctx, "%s: Unable to determine memory block size\n", devname); return -ENXIO; } memblock_res = phys_index * memblock_size; if (memblock_res >= dev_start && memblock_res <= dev_end) return 1; return 0; } static int op_for_one_memblock(struct daxctl_memory *mem, char *memblock, enum memory_op op, int *status) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); int rc; switch (op) { case MEM_SET_ONLINE: return online_one_memblock(mem, memblock, MEM_ZONE_MOVABLE, status); case MEM_SET_ONLINE_NO_MOVABLE: return online_one_memblock(mem, memblock, MEM_ZONE_NORMAL, status); case MEM_SET_OFFLINE: return offline_one_memblock(mem, memblock); case MEM_IS_ONLINE: rc = memblock_is_online(mem, memblock); if (rc < 0) return rc; /* * Retain the 'normal' semantics for if (memblock_is_online()), * but since count needs rc == 0, we'll just flip rc for this op */ return !rc; case MEM_COUNT: return 0; case MEM_GET_ZONE: return memblock_find_zone(mem, memblock, status); } err(ctx, "%s: BUG: unknown op: %d\n", devname, op); return -EINVAL; } static int daxctl_memory_op(struct daxctl_memory *mem, enum memory_op op) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); int rc, count = 0, status_flags = 0; const char *node_path; struct dirent *de; DIR *node_dir; node_path = daxctl_memory_get_node_path(mem); if (!node_path) { err(ctx, "%s: Failed to get node_path\n", devname); return -ENXIO; } node_dir = opendir(node_path); if (!node_dir) return -errno; errno = 0; while ((de = readdir(node_dir)) != NULL) { if (strncmp(de->d_name, "memory", 6) == 0) { rc = memblock_in_dev(mem, de->d_name); if (rc < 0) goto out_dir; if (rc == 0) /* memblock not in dev */ continue; /* memblock is in dev, perform op */ rc = op_for_one_memblock(mem, de->d_name, op, &status_flags); if (rc < 0) goto out_dir; if (rc == 0) count++; } errno = 0; } if (status_flags & MEM_ST_ZONE_INCONSISTENT) mem->zone = MEM_ZONE_UNKNOWN; if (errno) { rc = -errno; goto out_dir; } rc = count; out_dir: closedir(node_dir); return rc; } /* * daxctl_memory_online() will online to ZONE_MOVABLE by default */ static int daxctl_memory_online_with_zone(struct daxctl_memory *mem, enum memory_zones zone) { struct daxctl_dev *dev = daxctl_memory_get_dev(mem); const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); int rc; switch (zone) { case MEM_ZONE_MOVABLE: rc = daxctl_memory_op(mem, MEM_SET_ONLINE); break; case MEM_ZONE_NORMAL: rc = daxctl_memory_op(mem, MEM_SET_ONLINE_NO_MOVABLE); break; default: err(ctx, "%s: BUG: invalid zone for onlining\n", devname); rc = -EINVAL; } if (rc) return rc; /* * Detect any potential races when blocks were being brought online by * checking the zone in which the memory blocks are at this point. If * any of the blocks are not in ZONE_MOVABLE, emit a warning. */ mem->zone = 0; rc = daxctl_memory_op(mem, MEM_GET_ZONE); if (rc) return rc; if (mem->zone != zone) { err(ctx, "%s:\n WARNING: detected a race while onlining memory\n" " See 'man daxctl-reconfigure-device' for more details\n", devname); return -EBUSY; } return rc; } DAXCTL_EXPORT int daxctl_memory_online(struct daxctl_memory *mem) { return daxctl_memory_online_with_zone(mem, MEM_ZONE_MOVABLE); } DAXCTL_EXPORT int daxctl_memory_online_no_movable(struct daxctl_memory *mem) { return daxctl_memory_online_with_zone(mem, MEM_ZONE_NORMAL); } DAXCTL_EXPORT int daxctl_memory_offline(struct daxctl_memory *mem) { return daxctl_memory_op(mem, MEM_SET_OFFLINE); } DAXCTL_EXPORT int daxctl_memory_is_online(struct daxctl_memory *mem) { return daxctl_memory_op(mem, MEM_IS_ONLINE); } DAXCTL_EXPORT int daxctl_memory_num_sections(struct daxctl_memory *mem) { return daxctl_memory_op(mem, MEM_COUNT); } DAXCTL_EXPORT int daxctl_memory_is_movable(struct daxctl_memory *mem) { int rc; /* Start a fresh zone scan, clear any previous info */ mem->zone = 0; rc = daxctl_memory_op(mem, MEM_GET_ZONE); if (rc < 0) return rc; return (mem->zone == MEM_ZONE_MOVABLE) ? 1 : 0; } ndctl-67/daxctl/lib/libdaxctl.pc.in000066400000000000000000000003401355562357700173570ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libdaxctl Description: Manage "Device DAX" devices Version: @VERSION@ Libs: -L${libdir} -ldaxctl Libs.private: Cflags: -I${includedir} ndctl-67/daxctl/lib/libdaxctl.sym000066400000000000000000000026251355562357700171700ustar00rootroot00000000000000LIBDAXCTL_1 { global: daxctl_get_userdata; daxctl_set_userdata; daxctl_ref; daxctl_get_log_priority; daxctl_set_log_fn; daxctl_unref; daxctl_set_log_priority; daxctl_new; local: *; }; LIBDAXCTL_2 { global: daxctl_region_unref; daxctl_new_region; daxctl_region_ref; daxctl_region_unref; daxctl_region_get_uuid; daxctl_region_get_id; daxctl_region_get_ctx; daxctl_dev_get_first; daxctl_dev_get_next; daxctl_dev_get_region; daxctl_dev_get_id; daxctl_dev_get_devname; daxctl_dev_get_major; daxctl_dev_get_minor; daxctl_dev_get_size; } LIBDAXCTL_1; LIBDAXCTL_3 { global: daxctl_region_get_available_size; daxctl_region_get_devname; daxctl_region_get_dev_seed; } LIBDAXCTL_2; LIBDAXCTL_4 { global: daxctl_region_get_size; daxctl_region_get_align; daxctl_region_get_first; daxctl_region_get_next; } LIBDAXCTL_3; LIBDAXCTL_5 { global: daxctl_region_get_path; } LIBDAXCTL_4; LIBDAXCTL_6 { global: daxctl_dev_get_ctx; daxctl_dev_is_enabled; daxctl_dev_disable; daxctl_dev_enable_devdax; daxctl_dev_enable_ram; daxctl_dev_get_resource; daxctl_dev_get_target_node; daxctl_dev_get_memory; daxctl_memory_get_dev; daxctl_memory_get_node_path; daxctl_memory_get_block_size; daxctl_memory_online; daxctl_memory_offline; daxctl_memory_is_online; daxctl_memory_num_sections; } LIBDAXCTL_5; LIBDAXCTL_7 { global: daxctl_memory_is_movable; daxctl_memory_online_no_movable; } LIBDAXCTL_6; ndctl-67/daxctl/libdaxctl.h000066400000000000000000000102631355562357700160360ustar00rootroot00000000000000/* * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef _LIBDAXCTL_H_ #define _LIBDAXCTL_H_ #include #include #ifdef HAVE_UUID #include #else typedef unsigned char uuid_t[16]; #endif #ifdef __cplusplus extern "C" { #endif struct daxctl_ctx; struct daxctl_ctx *daxctl_ref(struct daxctl_ctx *ctx); void daxctl_unref(struct daxctl_ctx *ctx); int daxctl_new(struct daxctl_ctx **ctx); void daxctl_set_log_fn(struct daxctl_ctx *ctx, void (*log_fn)(struct daxctl_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args)); int daxctl_get_log_priority(struct daxctl_ctx *ctx); void daxctl_set_log_priority(struct daxctl_ctx *ctx, int priority); void daxctl_set_userdata(struct daxctl_ctx *ctx, void *userdata); void *daxctl_get_userdata(struct daxctl_ctx *ctx); struct daxctl_region; struct daxctl_region *daxctl_new_region(struct daxctl_ctx *ctx, int id, uuid_t uuid, const char *path); struct daxctl_region *daxctl_region_get_first(struct daxctl_ctx *ctx); struct daxctl_region *daxctl_region_get_next(struct daxctl_region *region); void daxctl_region_ref(struct daxctl_region *region); void daxctl_region_unref(struct daxctl_region *region); void daxctl_region_get_uuid(struct daxctl_region *region, uuid_t uu); int daxctl_region_get_id(struct daxctl_region *region); struct daxctl_ctx *daxctl_region_get_ctx(struct daxctl_region *region); unsigned long long daxctl_region_get_available_size( struct daxctl_region *region); unsigned long long daxctl_region_get_size(struct daxctl_region *region); unsigned long daxctl_region_get_align(struct daxctl_region *region); const char *daxctl_region_get_devname(struct daxctl_region *region); const char *daxctl_region_get_path(struct daxctl_region *region); struct daxctl_dev *daxctl_region_get_dev_seed(struct daxctl_region *region); struct daxctl_dev; struct daxctl_dev *daxctl_dev_get_first(struct daxctl_region *region); struct daxctl_dev *daxctl_dev_get_next(struct daxctl_dev *dev); struct daxctl_region *daxctl_dev_get_region(struct daxctl_dev *dev); int daxctl_dev_get_id(struct daxctl_dev *dev); const char *daxctl_dev_get_devname(struct daxctl_dev *dev); int daxctl_dev_get_major(struct daxctl_dev *dev); int daxctl_dev_get_minor(struct daxctl_dev *dev); unsigned long long daxctl_dev_get_resource(struct daxctl_dev *dev); unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev); struct daxctl_ctx *daxctl_dev_get_ctx(struct daxctl_dev *dev); int daxctl_dev_is_enabled(struct daxctl_dev *dev); int daxctl_dev_disable(struct daxctl_dev *dev); int daxctl_dev_enable_devdax(struct daxctl_dev *dev); int daxctl_dev_enable_ram(struct daxctl_dev *dev); int daxctl_dev_get_target_node(struct daxctl_dev *dev); struct daxctl_memory; struct daxctl_memory *daxctl_dev_get_memory(struct daxctl_dev *dev); struct daxctl_dev *daxctl_memory_get_dev(struct daxctl_memory *mem); const char *daxctl_memory_get_node_path(struct daxctl_memory *mem); unsigned long daxctl_memory_get_block_size(struct daxctl_memory *mem); int daxctl_memory_online(struct daxctl_memory *mem); int daxctl_memory_offline(struct daxctl_memory *mem); int daxctl_memory_is_online(struct daxctl_memory *mem); int daxctl_memory_num_sections(struct daxctl_memory *mem); int daxctl_memory_is_movable(struct daxctl_memory *mem); int daxctl_memory_online_no_movable(struct daxctl_memory *mem); #define daxctl_dev_foreach(region, dev) \ for (dev = daxctl_dev_get_first(region); \ dev != NULL; \ dev = daxctl_dev_get_next(dev)) #define daxctl_region_foreach(ctx, region) \ for (region = daxctl_region_get_first(ctx); \ region != NULL; \ region = daxctl_region_get_next(region)) #ifdef __cplusplus } /* extern "C" */ #endif #endif ndctl-67/daxctl/list.c000066400000000000000000000064431355562357700150430ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include static struct { bool devs; bool regions; bool idle; bool human; } list; static unsigned long listopts_to_flags(void) { unsigned long flags = 0; if (list.devs) flags |= UTIL_JSON_DAX_DEVS; if (list.idle) flags |= UTIL_JSON_IDLE; if (list.human) flags |= UTIL_JSON_HUMAN; return flags; } static struct { const char *dev; int region_id; } param = { .region_id = -1, }; static int did_fail; #define fail(fmt, ...) \ do { \ did_fail = 1; \ fprintf(stderr, "daxctl-%s:%s:%d: " fmt, \ VERSION, __func__, __LINE__, ##__VA_ARGS__); \ } while (0) static int num_list_flags(void) { return list.regions + list.devs; } int cmd_list(int argc, const char **argv, struct daxctl_ctx *ctx) { const struct option options[] = { OPT_INTEGER('r', "region", ¶m.region_id, "filter by region"), OPT_STRING('d', "dev", ¶m.dev, "dev-id", "filter by dax device instance name"), OPT_BOOLEAN('D', "devices", &list.devs, "include dax device info"), OPT_BOOLEAN('R', "regions", &list.regions, "include dax region info"), OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"), OPT_BOOLEAN('u', "human", &list.human, "use human friendly number formats "), OPT_END(), }; const char * const u[] = { "daxctl list []", NULL }; struct json_object *jregions = NULL; struct json_object *jdevs = NULL; struct daxctl_region *region; unsigned long list_flags; int i; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); if (num_list_flags() == 0) { list.regions = param.region_id >= 0; list.devs = !!param.dev; } if (num_list_flags() == 0) list.devs = true; list_flags = listopts_to_flags(); daxctl_region_foreach(ctx, region) { struct json_object *jregion = NULL; if (param.region_id >= 0 && param.region_id != daxctl_region_get_id(region)) continue; if (list.regions) { if (!jregions) { jregions = json_object_new_array(); if (!jregions) { fail("\n"); continue; } } jregion = util_daxctl_region_to_json(region, param.dev, list_flags); if (!jregion) { fail("\n"); continue; } json_object_array_add(jregions, jregion); } else if (list.devs) jdevs = util_daxctl_devs_to_list(region, jdevs, param.dev, list_flags); } if (jregions) util_display_json_array(stdout, jregions, list_flags); else if (jdevs) util_display_json_array(stdout, jdevs, list_flags); if (did_fail) return -ENOMEM; return 0; } ndctl-67/daxctl/migrate.c000066400000000000000000000017461355562357700155210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2019 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include int cmd_migrate(int argc, const char **argv, struct daxctl_ctx *ctx) { int i; static const struct option options[] = { OPT_END(), }; const char * const u[] = { "daxctl migrate-device-model", NULL }; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); if (symlink(DAXCTL_MODPROBE_DATA, DAXCTL_MODPROBE_INSTALL) == 0) { fprintf(stderr, " success: installed %s\n", DAXCTL_MODPROBE_INSTALL); return EXIT_SUCCESS; } error("failed to install %s: %s\n", DAXCTL_MODPROBE_INSTALL, strerror(errno)); return EXIT_FAILURE; } ndctl-67/git-version000077500000000000000000000016471355562357700146420ustar00rootroot00000000000000#!/bin/bash to_ver() { VN=$1 #drop leading 'v' out of the version so its a pure number if [ ${VN:0:1} = "v" ]; then VN=${VN:1} fi echo $VN } dirty() { VN=$(to_ver $1) git update-index -q --refresh if test -z "$(git diff-index --name-only HEAD --)"; then echo "$VN" else echo "${VN}.dirty" fi } DEF_VER=67 LF=' ' # First see if there is a version file (included in release tarballs), # then try git-describe, then default. if test -f version; then VN=$(cat version) || VN="$DEF_VER" elif test -d ${GIT_DIR:-.git} -o -f .git && VN=$(git describe --match "v[0-9]*" --abbrev=7 HEAD 2>/dev/null) && case "$VN" in *$LF*) (exit 1) ;; v[0-9]*) VN="$(dirty $VN)" esac; then VN=$(echo "$VN" | sed -e 's/-/./g'); else read COMMIT COMMIT_SUBJECT </dev/null) EOF if [ -z $COMMIT ]; then VN="${DEF_VER}+" else VN="$(dirty ${DEF_VER}.git$COMMIT)" fi fi echo $VN ndctl-67/git-version-gen000077500000000000000000000004421355562357700154010ustar00rootroot00000000000000#!/bin/sh GVF=version.m4 if test -r $GVF; then VC=$(sed -e 's/m4_define(\[GIT_VERSION], \[//' <$GVF) VC=$(echo $VC | sed -e 's/\])//') else VC=unset fi VN=$(./git-version) test "$VN" = "$VC" || { echo >&2 "GIT_VERSION = $VN" echo "m4_define([GIT_VERSION], [$VN])" >$GVF exit 0 } ndctl-67/licenses/000077500000000000000000000000001355562357700142435ustar00rootroot00000000000000ndctl-67/licenses/BSD-MIT000066400000000000000000000017771355562357700152410ustar00rootroot00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ndctl-67/licenses/CC0000066400000000000000000000143571355562357700145450ustar00rootroot00000000000000Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. ndctl-67/m4/000077500000000000000000000000001355562357700127565ustar00rootroot00000000000000ndctl-67/m4/.gitignore000066400000000000000000000001001355562357700147350ustar00rootroot00000000000000libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 ndctl-67/make-git-snapshot.sh000077500000000000000000000013151355562357700163300ustar00rootroot00000000000000#!/bin/bash set -e NAME=ndctl if [ ! -x ./git-version ]; then echo "$0 : ERROR: Must run from top level of git tree" exit 1 fi REFDIR=$PWD UPSTREAM=$REFDIR #TODO update once we have a public upstream OUTDIR=$HOME/rpmbuild/SOURCES if [ ! -d $OUTDIR ]; then mkdir -p $OUTDIR fi [ -n "$1" ] && HEAD="$1" || HEAD="HEAD" WORKDIR="$(mktemp -d --tmpdir "$NAME.XXXXXXXXXX")" trap 'rm -rf $WORKDIR' exit [ -d "$REFDIR" ] && REFERENCE="--reference $REFDIR" git clone $REFERENCE "$UPSTREAM" "$WORKDIR" VERSION=$(./git-version) DIRNAME="ndctl-${VERSION}" git archive --remote="$WORKDIR" --format=tar --prefix="$DIRNAME/" HEAD | gzip > $OUTDIR/"ndctl-${VERSION}.tar.gz" echo "Written $OUTDIR/ndctl-${VERSION}.tar.gz" ndctl-67/ndctl.spec.in000066400000000000000000000101661355562357700150270ustar00rootroot00000000000000Name: ndctl Version: VERSION Release: 1%{?dist} Summary: Manage "libnvdimm" subsystem devices (Non-volatile Memory) License: GPLv2 Url: https://github.com/pmem/ndctl Source0: https://github.com/pmem/%{name}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz Requires: LNAME%{?_isa} = %{version}-%{release} Requires: DAX_LNAME%{?_isa} = %{version}-%{release} BuildRequires: autoconf BuildRequires: rubygem-asciidoctor BuildRequires: xmlto BuildRequires: automake BuildRequires: libtool BuildRequires: pkgconfig BuildRequires: pkgconfig(libkmod) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(uuid) BuildRequires: pkgconfig(json-c) BuildRequires: pkgconfig(bash-completion) BuildRequires: pkgconfig(systemd) BuildRequires: keyutils-libs-devel %description Utility library for managing the "libnvdimm" subsystem. The "libnvdimm" subsystem defines a kernel device model and control message interface for platform NVDIMM resources like those defined by the ACPI 6+ NFIT (NVDIMM Firmware Interface Table). %package -n DNAME Summary: Development files for libndctl License: LGPLv2 Requires: LNAME%{?_isa} = %{version}-%{release} %description -n DNAME The %{name}-devel package contains libraries and header files for developing applications that use %{name}. %package -n daxctl Summary: Manage Device-DAX instances License: GPLv2 Requires: DAX_LNAME%{?_isa} = %{version}-%{release} %description -n daxctl The daxctl utility provides enumeration and provisioning commands for the Linux kernel Device-DAX facility. This facility enables DAX mappings of performance / feature differentiated memory without need of a filesystem. %package -n DAX_DNAME Summary: Development files for libdaxctl License: LGPLv2 Requires: DAX_LNAME%{?_isa} = %{version}-%{release} %description -n DAX_DNAME The %{name}-devel package contains libraries and header files for developing applications that use %{name}, a library for enumerating "Device DAX" devices. Device DAX is a facility for establishing DAX mappings of performance / feature-differentiated memory. %package -n LNAME Summary: Management library for "libnvdimm" subsystem devices (Non-volatile Memory) License: LGPLv2 Requires: DAX_LNAME%{?_isa} = %{version}-%{release} %description -n LNAME Libraries for %{name}. %package -n DAX_LNAME Summary: Management library for "Device DAX" devices License: LGPLv2 %description -n DAX_LNAME Device DAX is a facility for establishing DAX mappings of performance / feature-differentiated memory. DAX_LNAME provides an enumeration / control API for these devices. %prep %setup -q ndctl-%{version} %build echo %{version} > version ./autogen.sh %configure --disable-static --disable-silent-rules make %{?_smp_mflags} %install %make_install find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %check make check %ldconfig_scriptlets -n LNAME %ldconfig_scriptlets -n DAX_LNAME %define bashcompdir %(pkg-config --variable=completionsdir bash-completion) %files %defattr(-,root,root) %license util/COPYING licenses/BSD-MIT licenses/CC0 %{_bindir}/ndctl %{_mandir}/man1/ndctl* %{bashcompdir}/ %{_unitdir}/ndctl-monitor.service %{_sysconfdir}/ndctl/keys/keys.readme %{_sysconfdir}/modprobe.d/nvdimm-security.conf %config(noreplace) %{_sysconfdir}/ndctl/monitor.conf %files -n daxctl %defattr(-,root,root) %license util/COPYING licenses/BSD-MIT licenses/CC0 %{_bindir}/daxctl %{_mandir}/man1/daxctl* %{_datadir}/daxctl/daxctl.conf %files -n LNAME %defattr(-,root,root) %doc README.md %license COPYING licenses/BSD-MIT licenses/CC0 %{_libdir}/libndctl.so.* %files -n DAX_LNAME %defattr(-,root,root) %doc README.md %license COPYING licenses/BSD-MIT licenses/CC0 %{_libdir}/libdaxctl.so.* %files -n DNAME %defattr(-,root,root) %license COPYING %{_includedir}/ndctl/ %{_libdir}/libndctl.so %{_libdir}/pkgconfig/libndctl.pc %files -n DAX_DNAME %defattr(-,root,root) %license COPYING %{_includedir}/daxctl/ %{_libdir}/libdaxctl.so %{_libdir}/pkgconfig/libdaxctl.pc %changelog * Fri May 27 2016 Dan Williams - 53-1 - add daxctl-libs + daxctl-devel packages - add bash completion * Mon Apr 04 2016 Dan Williams - 52-1 - Initial rpm submission to Fedora ndctl-67/ndctl/000077500000000000000000000000001355562357700135425ustar00rootroot00000000000000ndctl-67/ndctl/Makefile.am000066400000000000000000000026441355562357700156040ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in bin_PROGRAMS = ndctl DISTCLEANFILES = config.h BUILT_SOURCES = config.h config.h: $(srcdir)/Makefile.am $(AM_V_GEN) echo "/* Autogenerated by ndctl/Makefile.am */" >$@ && \ echo '#define NDCTL_CONF_FILE \ "$(ndctl_monitorconfdir)/$(ndctl_monitorconf)"' >>$@ $(AM_V_GEN) echo '#define NDCTL_KEYS_DIR "$(ndctl_keysdir)"' >>$@ ndctl_SOURCES = ndctl.c \ bus.c \ create-nfit.c \ namespace.c \ check.c \ region.c \ dimm.c \ ../util/log.c \ list.c \ ../util/json.c \ util/json-smart.c \ util/json-firmware.c \ inject-error.c \ inject-smart.c \ monitor.c if ENABLE_KEYUTILS ndctl_SOURCES += util/keys.c \ load-keys.c keys_configdir = $(ndctl_keysdir) keys_config_DATA = $(ndctl_keysreadme) endif if ENABLE_DESTRUCTIVE ndctl_SOURCES += ../test/blk_namespaces.c \ ../test/pmem_namespaces.c ndctl_SOURCES += bat.c endif ndctl_LDADD =\ lib/libndctl.la \ ../daxctl/lib/libdaxctl.la \ ../libutil.a \ $(UUID_LIBS) \ $(KMOD_LIBS) \ $(JSON_LIBS) if ENABLE_KEYUTILS ndctl_LDADD += -lkeyutils endif if ENABLE_TEST ndctl_SOURCES += ../test/libndctl.c \ ../test/dsm-fail.c \ ../util/sysfs.c \ ../test/dpa-alloc.c \ ../test/parent-uuid.c \ ../test/multi-pmem.c \ ../test/core.c \ test.c endif monitor_configdir = $(ndctl_monitorconfdir) monitor_config_DATA = $(ndctl_monitorconf) if ENABLE_SYSTEMD_UNITS systemd_unit_DATA = ndctl-monitor.service endif ndctl-67/ndctl/action.h000066400000000000000000000005331355562357700151710ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * SPDX-License-Identifier: GPL-2.0 */ #ifndef __NDCTL_ACTION_H__ #define __NDCTL_ACTION_H__ enum device_action { ACTION_ENABLE, ACTION_DISABLE, ACTION_CREATE, ACTION_DESTROY, ACTION_CHECK, ACTION_WAIT, ACTION_START, ACTION_CLEAR, }; #endif /* __NDCTL_ACTION_H__ */ ndctl-67/ndctl/bat.c000066400000000000000000000032751355562357700144630ustar00rootroot00000000000000/* * Copyright(c) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include int cmd_bat(int argc, const char **argv, struct ndctl_ctx *ctx) { int loglevel = LOG_DEBUG, i, rc; struct ndctl_test *test; bool force = false; const char * const u[] = { "ndctl bat []", NULL }; const struct option options[] = { OPT_INTEGER('l', "loglevel", &loglevel, "set the log level (default LOG_DEBUG)"), OPT_BOOLEAN('f', "force", &force, "force run all tests regardless of required kernel"), OPT_END(), }; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); if (force) test = ndctl_test_new(UINT_MAX); else test = ndctl_test_new(0); if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = test_blk_namespaces(loglevel, test, ctx); fprintf(stderr, "test_blk_namespaces: %s\n", rc ? "FAIL" : "PASS"); if (rc && rc != 77) return rc; rc = test_pmem_namespaces(loglevel, test, ctx); fprintf(stderr, "test_pmem_namespaces: %s\n", rc ? "FAIL" : "PASS"); return ndctl_test_result(test, rc); } ndctl-67/ndctl/builtin.h000066400000000000000000000051231355562357700153620ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2015-2018 Intel Corporation. All rights reserved. */ #ifndef _NDCTL_BUILTIN_H_ #define _NDCTL_BUILTIN_H_ struct ndctl_ctx; int cmd_create_nfit(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_enable_namespace(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_create_namespace(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_destroy_namespace(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_disable_namespace(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_check_namespace(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_clear_errors(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_enable_region(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_disable_region(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_enable_dimm(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_disable_dimm(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_zero_labels(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_read_labels(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_write_labels(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_init_labels(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_check_labels(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_inject_error(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_wait_scrub(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_start_scrub(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_list(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_monitor(int argc, const char **argv, struct ndctl_ctx *ctx); #ifdef ENABLE_TEST int cmd_test(int argc, const char **argv, struct ndctl_ctx *ctx); #endif #ifdef ENABLE_DESTRUCTIVE int cmd_bat(int argc, const char **argv, struct ndctl_ctx *ctx); #endif int cmd_update_firmware(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_inject_smart(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_setup_passphrase(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_update_passphrase(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_remove_passphrase(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_freeze_security(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_sanitize_dimm(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_load_keys(int argc, const char **argv, struct ndctl_ctx *ctx); int cmd_wait_overwrite(int argc, const char **argv, struct ndctl_ctx *ctx); #endif /* _NDCTL_BUILTIN_H_ */ ndctl-67/ndctl/bus.c000066400000000000000000000061301355562357700144770ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2015-2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include "action.h" #include #include #include #include #include #include #include #include static struct { bool verbose; unsigned int poll_interval; } param; #define BASE_OPTIONS() \ OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug") #define WAIT_OPTIONS() \ OPT_UINTEGER('p', "poll", ¶m.poll_interval, "poll interval (seconds)") static const struct option start_options[] = { BASE_OPTIONS(), OPT_END(), }; static const struct option wait_options[] = { BASE_OPTIONS(), WAIT_OPTIONS(), OPT_END(), }; static int scrub_action(struct ndctl_bus *bus, enum device_action action) { switch (action) { case ACTION_WAIT: return ndctl_bus_poll_scrub_completion(bus, param.poll_interval, 0); case ACTION_START: return ndctl_bus_start_scrub(bus); default: return -EINVAL; } } static int bus_action(int argc, const char **argv, const char *usage, const struct option *options, enum device_action action, struct ndctl_ctx *ctx) { const char * const u[] = { usage, NULL }; struct json_object *jbuses, *jbus; int i, rc, success = 0, fail = 0; struct ndctl_bus *bus; const char *all = "all"; argc = parse_options(argc, argv, options, u, 0); if (param.verbose) ndctl_set_log_priority(ctx, LOG_DEBUG); if (argc == 0) { argc = 1; argv = &all; } else for (i = 0; i < argc; i++) if (strcmp(argv[i], "all") == 0) { argv[0] = "all"; argc = 1; break; } jbuses = json_object_new_array(); if (!jbuses) return -ENOMEM; for (i = 0; i < argc; i++) { int found = 0; ndctl_bus_foreach(ctx, bus) { if (!util_bus_filter(bus, argv[i])) continue; found++; rc = scrub_action(bus, action); if (rc == 0) { success++; jbus = util_bus_to_json(bus); if (jbus) json_object_array_add(jbuses, jbus); } else if (!fail) fail = rc; } if (!found && param.verbose) fprintf(stderr, "no bus matches id: %s\n", argv[i]); } if (success) util_display_json_array(stdout, jbuses, 0); else json_object_put(jbuses); if (success) return success; return fail ? fail : -ENXIO; } int cmd_start_scrub(int argc, const char **argv, struct ndctl_ctx *ctx) { char *usage = "ndctl start-scrub [ ... ] []"; int start = bus_action(argc, argv, usage, start_options, ACTION_START, ctx); if (start <= 0) { fprintf(stderr, "error starting scrub: %s\n", strerror(-start)); return start; } else { return 0; } } int cmd_wait_scrub(int argc, const char **argv, struct ndctl_ctx *ctx) { char *usage = "ndctl wait-scrub [ ... ] []"; int wait = bus_action(argc, argv, usage, wait_options, ACTION_WAIT, ctx); if (wait <= 0) { fprintf(stderr, "error waiting for scrub completion: %s\n", strerror(-wait)); return wait; } else { return 0; } } ndctl-67/ndctl/check.c000066400000000000000000001040451355562357700147670ustar00rootroot00000000000000/* * Copyright(c) 2015-2016 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct check_opts { bool verbose; bool force; bool repair; bool logfix; }; struct btt_chk { char *path; int fd; uuid_t parent_uuid; unsigned long long rawsize; unsigned long long nlba; int start_off; int num_arenas; long sys_page_size; struct arena_info *arena; struct check_opts *opts; struct log_ctx ctx; }; struct arena_info { struct arena_map map; u64 size; /* Total bytes for this arena */ u64 external_lba_start; u32 internal_nlba; u32 internal_lbasize; u32 external_nlba; u32 external_lbasize; u32 nfree; u16 version_major; u16 version_minor; u64 nextoff; u64 infooff; u64 dataoff; u64 mapoff; u64 logoff; u64 info2off; u32 flags; int num; struct btt_chk *bttc; int log_index[2]; }; static sigjmp_buf sj_env; static void sigbus_hdl(int sig, siginfo_t *siginfo, void *ptr) { siglongjmp(sj_env, 1); } static int repair_msg(struct btt_chk *bttc) { info(bttc, " Run with --repair to make the changes\n"); return 0; } /** * btt_read_info - read an info block from a given offset * @bttc: the main btt_chk structure for this btt * @btt_sb: struct btt_sb where the info block will be copied into * @offset: offset in the raw namespace to read the info block from * * This will also use 'pread' to read the info block, and not mmap+loads * as this is used before the mappings are set up. */ static int btt_read_info(struct btt_chk *bttc, struct btt_sb *btt_sb, u64 off) { ssize_t size; size = pread(bttc->fd, btt_sb, sizeof(*btt_sb), off); if (size < 0) { err(bttc, "unable to read first info block: %s\n", strerror(errno)); return -errno; } if (size != sizeof(*btt_sb)) { err(bttc, "short read of first info block: %ld\n", size); return -ENXIO; } return 0; } /** * btt_write_info - write an info block to the given offset * @bttc: the main btt_chk structure for this btt * @btt_sb: struct btt_sb where the info block will be copied from * @offset: offset in the raw namespace to write the info block to * * This will also use 'pwrite' to write the info block, and not mmap+stores * as this is used before the mappings are set up. */ static int btt_write_info(struct btt_chk *bttc, struct btt_sb *btt_sb, u64 off) { ssize_t size; int rc; if (!bttc->opts->repair) { err(bttc, "BTT info block at offset %#lx needs to be restored\n", off); repair_msg(bttc); return -EIO; } info(bttc, "Restoring BTT info block at offset %#lx\n", off); size = pwrite(bttc->fd, btt_sb, sizeof(*btt_sb), off); if (size < 0) { err(bttc, "unable to write the info block: %s\n", strerror(errno)); return -errno; } if (size != sizeof(*btt_sb)) { err(bttc, "short write of the info block: %ld\n", size); return -ENXIO; } rc = fsync(bttc->fd); if (rc < 0) return -errno; return 0; } /** * btt_copy_to_info2 - restore the backup info block using the main one * @a: the arena_info handle for this arena * * Called when a corrupted backup info block is detected. Copies the * main info block over to the backup location. This is done using * mmap + stores, and thus needs a msync. */ static int btt_copy_to_info2(struct arena_info *a) { void *ms_align; size_t ms_size; if (!a->bttc->opts->repair) { err(a->bttc, "Arena %d: BTT info2 needs to be restored\n", a->num); return repair_msg(a->bttc); } printf("Arena %d: Restoring BTT info2\n", a->num); memcpy(a->map.info2, a->map.info, BTT_INFO_SIZE); ms_align = (void *)rounddown((u64)a->map.info2, a->bttc->sys_page_size); ms_size = max(BTT_INFO_SIZE, a->bttc->sys_page_size); if (msync(ms_align, ms_size, MS_SYNC) < 0) return -errno; return 0; } /* * btt_map_lookup - given a pre-map Arena Block Address, return the post-map ABA * @a: the arena_info handle for this arena * @lba: the logical block address for which we are performing the lookup * * This will correctly account for map entries in the 'initial state' */ static u32 btt_map_lookup(struct arena_info *a, u32 lba) { u32 raw_mapping; raw_mapping = le32_to_cpu(a->map.map[lba]); if (raw_mapping & MAP_ENT_NORMAL) return raw_mapping & MAP_LBA_MASK; else return lba; } static int btt_map_write(struct arena_info *a, u32 lba, u32 mapping) { void *ms_align; if (!a->bttc->opts->repair) { err(a->bttc, "Arena %d: map[%#x] needs to be updated to %#x\n", a->num, lba, mapping); return repair_msg(a->bttc); } info(a->bttc, "Arena %d: Updating map[%#x] to %#x\n", a->num, lba, mapping); /* * We want to set neither of the Z or E flags, and in the actual * layout, this means setting the bit positions of both to '1' to * indicate a 'normal' map entry */ mapping |= MAP_ENT_NORMAL; a->map.map[lba] = cpu_to_le32(mapping); ms_align = (void *)rounddown((u64)&a->map.map[lba], a->bttc->sys_page_size); if (msync(ms_align, a->bttc->sys_page_size, MS_SYNC) < 0) return -errno; return 0; } static void btt_log_group_read(struct arena_info *a, u32 lane, struct log_group *log) { memcpy(log, &a->map.log[lane], LOG_GRP_SIZE); } static void btt_log_group_write(struct arena_info *a, u32 lane, struct log_group *log) { memcpy(&a->map.log[lane], log, LOG_GRP_SIZE); } static u32 log_seq(struct log_group *log, int log_idx) { return le32_to_cpu(log->ent[log_idx].seq); } /* * This function accepts two log entries, and uses the sequence number to * find the 'older' entry. The return value indicates which of the two was * the 'old' entry */ static int btt_log_get_old(struct arena_info *a, struct log_group *log) { int idx0 = a->log_index[0]; int idx1 = a->log_index[1]; int old; if (log_seq(log, idx0) == 0) { log->ent[idx0].seq = cpu_to_le32(1); return 0; } if (log_seq(log, idx0) < log_seq(log, idx1)) { if ((log_seq(log, idx1) - log_seq(log, idx0)) == 1) old = 0; else old = 1; } else { if ((log_seq(log, idx0) - log_seq(log, idx1)) == 1) old = 1; else old = 0; } return old; } static int btt_log_read(struct arena_info *a, u32 lane, struct log_entry *ent) { int new_ent; struct log_group log; if (ent == NULL) return -EINVAL; btt_log_group_read(a, lane, &log); new_ent = 1 - btt_log_get_old(a, &log); memcpy(ent, &log.ent[a->log_index[new_ent]], LOG_ENT_SIZE); return 0; } static int btt_checksum_verify(struct btt_sb *btt_sb) { uint64_t sum; le64 sum_save; BUILD_BUG_ON(sizeof(struct btt_sb) != SZ_4K); sum_save = btt_sb->checksum; btt_sb->checksum = 0; sum = fletcher64(btt_sb, sizeof(*btt_sb), 1); if (sum != sum_save) return 1; /* restore the checksum in the buffer */ btt_sb->checksum = sum_save; return 0; } /* * Never pass a mmapped buffer to this as it will attempt to write to * the buffer, and we want writes to only happened in a controlled fashion. * In the non --repair case, even if such a buffer is passed, the write will * result in a fault due to the readonly mmap flags. */ static int btt_info_verify(struct btt_chk *bttc, struct btt_sb *btt_sb) { if (memcmp(btt_sb->signature, BTT_SIG, BTT_SIG_LEN) != 0) return -ENXIO; if (!uuid_is_null(btt_sb->parent_uuid)) if (uuid_compare(bttc->parent_uuid, btt_sb->parent_uuid) != 0) return -ENXIO; if (btt_checksum_verify(btt_sb)) return -ENXIO; return 0; } static int btt_info_read_verify(struct btt_chk *bttc, struct btt_sb *btt_sb, u64 off) { int rc; rc = btt_read_info(bttc, btt_sb, off); if (rc) return rc; rc = btt_info_verify(bttc, btt_sb); if (rc) return rc; return 0; } enum btt_errcodes { BTT_OK = 0, BTT_LOG_EQL_SEQ = 0x100, BTT_LOG_OOB_SEQ, BTT_LOG_OOB_LBA, BTT_LOG_OOB_OLD, BTT_LOG_OOB_NEW, BTT_LOG_MAP_ERR, BTT_MAP_OOB, BTT_BITMAP_ERROR, BTT_LOGFIX_ERR, }; static void btt_xlat_status(struct arena_info *a, int errcode) { switch(errcode) { case BTT_OK: break; case BTT_LOG_EQL_SEQ: err(a->bttc, "arena %d: found a pair of log entries with the same sequence number\n", a->num); break; case BTT_LOG_OOB_SEQ: err(a->bttc, "arena %d: found a log entry with an out of bounds sequence number\n", a->num); break; case BTT_LOG_OOB_LBA: err(a->bttc, "arena %d: found a log entry with an out of bounds LBA\n", a->num); break; case BTT_LOG_OOB_OLD: err(a->bttc, "arena %d: found a log entry with an out of bounds 'old' mapping\n", a->num); break; case BTT_LOG_OOB_NEW: err(a->bttc, "arena %d: found a log entry with an out of bounds 'new' mapping\n", a->num); break; case BTT_LOG_MAP_ERR: info(a->bttc, "arena %d: found a log entry that does not match with a map entry\n", a->num); break; case BTT_MAP_OOB: err(a->bttc, "arena %d: found a map entry that is out of bounds\n", a->num); break; case BTT_BITMAP_ERROR: err(a->bttc, "arena %d: bitmap error: internal blocks are incorrectly referenced\n", a->num); break; case BTT_LOGFIX_ERR: err(a->bttc, "arena %d: rewrite-log error: log may be in an unknown/unrecoverable state\n", a->num); break; default: err(a->bttc, "arena %d: unknown error: %d\n", a->num, errcode); } } /* Check that log entries are self consistent */ static int btt_check_log_entries(struct arena_info *a) { int idx0 = a->log_index[0]; int idx1 = a->log_index[1]; unsigned int i; int rc = 0; /* * First, check both 'slots' for sequence numbers being distinct * and in bounds */ for (i = 0; i < a->nfree; i++) { struct log_group *log = &a->map.log[i]; if (log_seq(log, idx0) == log_seq(log, idx1)) return BTT_LOG_EQL_SEQ; if (log_seq(log, idx0) > 3 || log_seq(log, idx1) > 3) return BTT_LOG_OOB_SEQ; } /* * Next, check only the 'new' slot in each lane for the remaining * fields being in bounds */ for (i = 0; i < a->nfree; i++) { struct log_entry ent; rc = btt_log_read(a, i, &ent); if (rc) return rc; if (ent.lba >= a->external_nlba) return BTT_LOG_OOB_LBA; if (ent.old_map >= a->internal_nlba) return BTT_LOG_OOB_OLD; if (ent.new_map >= a->internal_nlba) return BTT_LOG_OOB_NEW; } return rc; } /* Check that map entries are self consistent */ static int btt_check_map_entries(struct arena_info *a) { unsigned int i; u32 mapping; for (i = 0; i < a->external_nlba; i++) { mapping = btt_map_lookup(a, i); if (mapping >= a->internal_nlba) return BTT_MAP_OOB; } return 0; } /* Check that each flog entry has the correct corresponding map entry */ static int btt_check_log_map(struct arena_info *a) { unsigned int i; u32 mapping; int rc = 0, rc_saved = 0; for (i = 0; i < a->nfree; i++) { struct log_entry ent; rc = btt_log_read(a, i, &ent); if (rc) return rc; mapping = btt_map_lookup(a, ent.lba); /* * Case where the flog was written, but map couldn't be * updated. The kernel should also be able to detect and * fix this condition. */ if (ent.new_map != mapping && ent.old_map == mapping) { info(a->bttc, "arena %d: log[%d].new_map (%#x) doesn't match map[%#x] (%#x)\n", a->num, i, ent.new_map, ent.lba, mapping); rc = btt_map_write(a, ent.lba, ent.new_map); if (rc) rc_saved = rc; } } return rc_saved ? BTT_LOG_MAP_ERR : 0; } static int btt_check_info2(struct arena_info *a) { /* * Repair info2 if needed. The main info-block can be trusted * as it has been verified during arena discovery */ if(memcmp(a->map.info2, a->map.info, BTT_INFO_SIZE)) return btt_copy_to_info2(a); return 0; } /* * This will create a bitmap where each bit corresponds to an internal * 'block'. Between the BTT map and flog (representing 'free' blocks), * every single internal block must be represented exactly once. This * check will detect cases where either one or more blocks are never * referenced, or if a block is referenced more than once. */ static int btt_check_bitmap(struct arena_info *a) { unsigned long *bm; u32 i, btt_mapping; int rc = BTT_BITMAP_ERROR; bm = bitmap_alloc(a->internal_nlba); if (bm == NULL) return -ENOMEM; /* map 'external_nlba' number of map entries */ for (i = 0; i < a->external_nlba; i++) { btt_mapping = btt_map_lookup(a, i); if (test_bit(btt_mapping, bm)) { info(a->bttc, "arena %d: internal block %#x is referenced by two map entries\n", a->num, btt_mapping); goto out; } bitmap_set(bm, btt_mapping, 1); } /* map 'nfree' number of flog entries */ for (i = 0; i < a->nfree; i++) { struct log_entry ent; rc = btt_log_read(a, i, &ent); if (rc) goto out; if (test_bit(ent.old_map, bm)) { info(a->bttc, "arena %d: internal block %#x is referenced by two map/log entries\n", a->num, ent.old_map); rc = BTT_BITMAP_ERROR; goto out; } bitmap_set(bm, ent.old_map, 1); } /* check that the bitmap is full */ if (!bitmap_full(bm, a->internal_nlba)) rc = BTT_BITMAP_ERROR; out: free(bm); return rc; } static int btt_rewrite_log(struct arena_info *a) { struct log_group log; int rc; u32 i; info(a->bttc, "arena %d: rewriting log\n", a->num); /* * To rewrite the log, we implicitly use the 'new' padding scheme of * (0, 1) but resetting the log to a completely initial state (i.e. * slot-0 contains a made-up entry containing the 'free' block from * the existing current log entry, and a sequence number of '1'. All * other slots are zeroed. * * This way of rewriting the log is the most flexible as it can be * (ab)used to convert a new padding format back to the old one. * Since it only recreates slot-0, which is common between both * existing formats, an older kernel will simply initialize the free * list using those slot-0 entries, and run with it as though slot-2 * is the other valid slot. */ memset(&log, 0, LOG_GRP_SIZE); for (i = 0; i < a->nfree; i++) { struct log_entry ent; rc = btt_log_read(a, i, &ent); if (rc) return BTT_LOGFIX_ERR; log.ent[0].lba = ent.lba; log.ent[0].old_map = ent.old_map; log.ent[0].new_map = ent.new_map; log.ent[0].seq = 1; btt_log_group_write(a, i, &log); } return 0; } static int btt_check_arenas(struct btt_chk *bttc) { struct arena_info *a = NULL; int i, rc; for(i = 0; i < bttc->num_arenas; i++) { info(bttc, "checking arena %d\n", i); a = &bttc->arena[i]; rc = btt_check_log_entries(a); if (rc) break; rc = btt_check_map_entries(a); if (rc) break; rc = btt_check_log_map(a); if (rc) break; rc = btt_check_info2(a); if (rc) break; /* * bitmap test has to be after check_log_map so that any * pending log updates have been performed. Otherwise the * bitmap test may result in a false positive */ rc = btt_check_bitmap(a); if (rc) break; if (bttc->opts->logfix) { rc = btt_rewrite_log(a); if (rc) break; } } if (a && rc != BTT_OK) { btt_xlat_status(a, rc); return -ENXIO; } return 0; } /* * This copies over information from the info block to the arena_info struct. * The main difference is that all the offsets (infooff, mapoff etc) were * relative to the arena in the info block, but in arena_info, we use * arena_off to make these offsets absolute, i.e. relative to the start of * the raw namespace. */ static int btt_parse_meta(struct arena_info *arena, struct btt_sb *btt_sb, u64 arena_off) { arena->internal_nlba = le32_to_cpu(btt_sb->internal_nlba); arena->internal_lbasize = le32_to_cpu(btt_sb->internal_lbasize); arena->external_nlba = le32_to_cpu(btt_sb->external_nlba); arena->external_lbasize = le32_to_cpu(btt_sb->external_lbasize); arena->nfree = le32_to_cpu(btt_sb->nfree); if (arena->internal_nlba - arena->external_nlba != arena->nfree) return -ENXIO; if (arena->internal_lbasize != arena->external_lbasize) return -ENXIO; arena->version_major = le16_to_cpu(btt_sb->version_major); arena->version_minor = le16_to_cpu(btt_sb->version_minor); arena->nextoff = (btt_sb->nextoff == 0) ? 0 : (arena_off + le64_to_cpu(btt_sb->nextoff)); arena->infooff = arena_off; arena->dataoff = arena_off + le64_to_cpu(btt_sb->dataoff); arena->mapoff = arena_off + le64_to_cpu(btt_sb->mapoff); arena->logoff = arena_off + le64_to_cpu(btt_sb->logoff); arena->info2off = arena_off + le64_to_cpu(btt_sb->info2off); arena->size = (le64_to_cpu(btt_sb->nextoff) > 0) ? (le64_to_cpu(btt_sb->nextoff)) : (arena->info2off - arena->infooff + BTT_INFO_SIZE); arena->flags = le32_to_cpu(btt_sb->flags); if (btt_sb->flags & IB_FLAG_ERROR_MASK) { err(arena->bttc, "Info block error flag is set, aborting\n"); return -ENXIO; } return 0; } static bool ent_is_padding(struct log_entry *ent) { return (ent->lba == 0) && (ent->old_map == 0) && (ent->new_map == 0) && (ent->seq == 0); } /* * Detecting valid log indices: We read a log group, and iterate over its * four slots. We expect that a padding slot will be all-zeroes, and use this * to detect a padding slot vs. an actual entry. * * If a log_group is in the initial state, i.e. hasn't been used since the * creation of this BTT layout, it will have three of the four slots with * zeroes. We skip over these log_groups for the detection of log_index. If * all log_groups are in the initial state (i.e. the BTT has never been * written to), it is safe to assume the 'new format' of log entries in slots * (0, 1). */ static int log_set_indices(struct arena_info *arena) { bool idx_set = false, initial_state = true; int log_index[2] = {-1, -1}; struct log_group log; int j, next_idx = 0; u32 pad_count = 0; u32 i; for (i = 0; i < arena->nfree; i++) { btt_log_group_read(arena, i, &log); for (j = 0; j < 4; j++) { if (!idx_set) { if (ent_is_padding(&log.ent[j])) { pad_count++; continue; } else { /* Skip if index has been recorded */ if ((next_idx == 1) && (j == log_index[0])) continue; /* valid entry, record index */ log_index[next_idx] = j; next_idx++; } if (next_idx == 2) { /* two valid entries found */ idx_set = true; } else if (next_idx > 2) { /* too many valid indices */ return -ENXIO; } } else { /* * once the indices have been set, just verify * that all subsequent log groups are either in * their initial state or follow the same * indices. */ if (j == log_index[0]) { /* entry must be 'valid' */ if (ent_is_padding(&log.ent[j])) return -ENXIO; } else if (j == log_index[1]) { ; /* * log_index[1] can be padding if the * lane never got used and it is still * in the initial state (three 'padding' * entries) */ } else { /* entry must be invalid (padding) */ if (!ent_is_padding(&log.ent[j])) return -ENXIO; } } } /* * If any of the log_groups have more than one valid, * non-padding entry, then the we are no longer in the * initial_state */ if (pad_count < 3) initial_state = false; pad_count = 0; } if (!initial_state && !idx_set) return -ENXIO; /* * If all the entries in the log were in the initial state, * assume new padding scheme */ if (initial_state) log_index[1] = 1; /* * Only allow the known permutations of log/padding indices, * i.e. (0, 1), and (0, 2) */ if ((log_index[0] == 0) && ((log_index[1] == 1) || (log_index[1] == 2))) ; /* known index possibilities */ else { err(arena->bttc, "Found an unknown padding scheme\n"); return -ENXIO; } arena->log_index[0] = log_index[0]; arena->log_index[1] = log_index[1]; info(arena->bttc, "arena[%d]: log_index_0 = %d\n", arena->num, log_index[0]); info(arena->bttc, "arena[%d]: log_index_1 = %d\n", arena->num, log_index[1]); return 0; } static int btt_discover_arenas(struct btt_chk *bttc) { int ret = 0; struct arena_info *arena; struct btt_sb *btt_sb; size_t remaining = bttc->rawsize; size_t cur_off = bttc->start_off; u64 cur_nlba = 0; int i = 0; btt_sb = calloc(1, sizeof(*btt_sb)); if (!btt_sb) return -ENOMEM; while (remaining) { /* Alloc memory for arena */ arena = realloc(bttc->arena, (i + 1) * sizeof(*arena)); if (!arena) { ret = -ENOMEM; goto out; } else { bttc->arena = arena; arena = &bttc->arena[i]; /* zero the new memory */ memset(arena, 0, sizeof(*arena)); } arena->infooff = cur_off; ret = btt_read_info(bttc, btt_sb, cur_off); if (ret) goto out; if (btt_info_verify(bttc, btt_sb) != 0) { u64 offset; /* Try to find the backup info block */ if (remaining <= ARENA_MAX_SIZE) offset = rounddown(bttc->rawsize, SZ_4K) - BTT_INFO_SIZE; else offset = cur_off + ARENA_MAX_SIZE - BTT_INFO_SIZE; info(bttc, "Arena %d: Attempting recover info-block using info2\n", i); ret = btt_read_info(bttc, btt_sb, offset); if (ret) { err(bttc, "Unable to read backup info block (offset %#lx)\n", offset); goto out; } ret = btt_info_verify(bttc, btt_sb); if (ret) { err(bttc, "Backup info block (offset %#lx) verification failed\n", offset); goto out; } ret = btt_write_info(bttc, btt_sb, cur_off); if (ret) { err(bttc, "Restoration of the info block failed: %s (%d)\n", strerror(abs(ret)), ret); goto out; } } arena->num = i; arena->bttc = bttc; arena->external_lba_start = cur_nlba; ret = btt_parse_meta(arena, btt_sb, cur_off); if (ret) { err(bttc, "Problem parsing arena[%d] metadata\n", i); goto out; } remaining -= arena->size; cur_off += arena->size; cur_nlba += arena->external_nlba; i++; if (arena->nextoff == 0) break; } bttc->num_arenas = i; bttc->nlba = cur_nlba; info(bttc, "found %d BTT arena%s\n", bttc->num_arenas, (bttc->num_arenas > 1) ? "s" : ""); free(btt_sb); return ret; out: free(bttc->arena); free(btt_sb); return ret; } /* * Wrap call to mmap(2) to work with btt device offsets that are not aligned * to system page boundary. It works by rounding down the requested offset * to sys_page_size when calling mmap(2) and then returning a fixed-up pointer * to the correct offset in the mmaped region. */ static void *btt_mmap(struct btt_chk *bttc, size_t length, off_t offset) { off_t page_offset; int prot_flags; uint8_t *addr; if (!bttc->opts->repair) prot_flags = PROT_READ; else prot_flags = PROT_READ|PROT_WRITE; /* Calculate the page_offset from the system page boundary */ page_offset = offset - rounddown(offset, bttc->sys_page_size); /* Update the offset and length with the page_offset calculated above */ offset -= page_offset; length += page_offset; addr = mmap(NULL, length, prot_flags, MAP_SHARED, bttc->fd, offset); /* If needed fixup the return pointer to correct offset requested */ if (addr != MAP_FAILED) addr += page_offset; dbg(bttc, "addr = %p, length = %#lx, offset = %#lx, page_offset = %#lx\n", (void *) addr, length, offset, page_offset); return addr == MAP_FAILED ? NULL : addr; } static void btt_unmap(struct btt_chk *bttc, void *ptr, size_t length) { off_t page_offset; uintptr_t addr = (uintptr_t) ptr; /* Calculate the page_offset from system page boundary */ page_offset = addr - rounddown(addr, bttc->sys_page_size); addr -= page_offset; length += page_offset; munmap((void *) addr, length); dbg(bttc, "addr = %p, length = %#lx, page_offset = %#lx\n", (void *) addr, length, page_offset); } static int btt_create_mappings(struct btt_chk *bttc) { struct arena_info *a; int i; for (i = 0; i < bttc->num_arenas; i++) { a = &bttc->arena[i]; a->map.info_len = BTT_INFO_SIZE; a->map.info = btt_mmap(bttc, a->map.info_len, a->infooff); if (!a->map.info) { err(bttc, "mmap arena[%d].info [sz = %#lx, off = %#lx] failed: %s\n", i, a->map.info_len, a->infooff, strerror(errno)); return -errno; } a->map.data_len = a->mapoff - a->dataoff; a->map.data = btt_mmap(bttc, a->map.data_len, a->dataoff); if (!a->map.data) { err(bttc, "mmap arena[%d].data [sz = %#lx, off = %#lx] failed: %s\n", i, a->map.data_len, a->dataoff, strerror(errno)); return -errno; } a->map.map_len = a->logoff - a->mapoff; a->map.map = btt_mmap(bttc, a->map.map_len, a->mapoff); if (!a->map.map) { err(bttc, "mmap arena[%d].map [sz = %#lx, off = %#lx] failed: %s\n", i, a->map.map_len, a->mapoff, strerror(errno)); return -errno; } a->map.log_len = a->info2off - a->logoff; a->map.log = btt_mmap(bttc, a->map.log_len, a->logoff); if (!a->map.log) { err(bttc, "mmap arena[%d].log [sz = %#lx, off = %#lx] failed: %s\n", i, a->map.log_len, a->logoff, strerror(errno)); return -errno; } a->map.info2_len = BTT_INFO_SIZE; a->map.info2 = btt_mmap(bttc, a->map.info2_len, a->info2off); if (!a->map.info2) { err(bttc, "mmap arena[%d].info2 [sz = %#lx, off = %#lx] failed: %s\n", i, a->map.info2_len, a->info2off, strerror(errno)); return -errno; } } return 0; } static void btt_remove_mappings(struct btt_chk *bttc) { struct arena_info *a; int i; for (i = 0; i < bttc->num_arenas; i++) { a = &bttc->arena[i]; if (a->map.info) btt_unmap(bttc, a->map.info, a->map.info_len); if (a->map.data) btt_unmap(bttc, a->map.data, a->map.data_len); if (a->map.map) btt_unmap(bttc, a->map.map, a->map.map_len); if (a->map.log) btt_unmap(bttc, a->map.log, a->map.log_len); if (a->map.info2) btt_unmap(bttc, a->map.info2, a->map.info2_len); } } static int btt_sb_get_expected_offset(struct btt_sb *btt_sb) { u16 version_major, version_minor; version_major = le16_to_cpu(btt_sb->version_major); version_minor = le16_to_cpu(btt_sb->version_minor); if (version_major == 1 && version_minor == 1) return BTT1_START_OFFSET; else if (version_major == 2 && version_minor == 0) return BTT2_START_OFFSET; else return -ENXIO; } static int __btt_recover_first_sb(struct btt_chk *bttc, int off) { int rc, est_arenas = 0; u64 offset, remaining; struct btt_sb *btt_sb; /* Estimate the number of arenas */ remaining = bttc->rawsize - off; while (remaining) { if (remaining < ARENA_MIN_SIZE && est_arenas == 0) return -EINVAL; if (remaining > ARENA_MAX_SIZE) { /* full-size arena */ remaining -= ARENA_MAX_SIZE; est_arenas++; continue; } if (remaining < ARENA_MIN_SIZE) { /* 'remaining' was too small for another arena */ break; } else { /* last, short arena */ remaining = 0; est_arenas++; break; } } info(bttc, "estimated arenas: %d, remaining bytes: %#lx\n", est_arenas, remaining); btt_sb = malloc(2 * sizeof(*btt_sb)); if (btt_sb == NULL) return -ENOMEM; /* Read the original first info block into btt_sb[0] */ rc = btt_read_info(bttc, &btt_sb[0], off); if (rc) goto out; /* Attepmt 1: try recovery from expected end of the first arena */ if (est_arenas == 1) offset = rounddown(bttc->rawsize - remaining, SZ_4K) - BTT_INFO_SIZE; else offset = ARENA_MAX_SIZE - BTT_INFO_SIZE + off; info(bttc, "Attempting recover info-block from end-of-arena offset %#lx\n", offset); rc = btt_info_read_verify(bttc, &btt_sb[1], offset); if (rc == 0) { int expected_offset = btt_sb_get_expected_offset(&btt_sb[1]); /* * The fact that the btt_sb is self-consistent doesn't tell us * what BTT version it was, if restoring from the end of the * arena. (i.e. a consistent sb may be found for any valid * start offset). Use the version information in the sb to * determine what the expected start offset is. */ if ((expected_offset < 0) || (expected_offset != off)) { rc = -ENXIO; goto out; } rc = btt_write_info(bttc, &btt_sb[1], off); goto out; } /* * Attempt 2: From the very end of 'rawsize', try to copy the fields * that are constant in every arena (only valid when multiple arenas * are present) */ if (est_arenas > 1) { offset = rounddown(bttc->rawsize - remaining, SZ_4K) - BTT_INFO_SIZE; info(bttc, "Attempting to recover info-block from end offset %#lx\n", offset); rc = btt_info_read_verify(bttc, &btt_sb[1], offset); if (rc) goto out; /* copy over the arena0 specific fields from btt_sb[0] */ btt_sb[1].flags = btt_sb[0].flags; btt_sb[1].external_nlba = btt_sb[0].external_nlba; btt_sb[1].internal_nlba = btt_sb[0].internal_nlba; btt_sb[1].nextoff = btt_sb[0].nextoff; btt_sb[1].dataoff = btt_sb[0].dataoff; btt_sb[1].mapoff = btt_sb[0].mapoff; btt_sb[1].logoff = btt_sb[0].logoff; btt_sb[1].info2off = btt_sb[0].info2off; btt_sb[1].checksum = btt_sb[0].checksum; rc = btt_info_verify(bttc, &btt_sb[1]); if (rc == 0) { rc = btt_write_info(bttc, &btt_sb[1], off); goto out; } } /* * Attempt 3: use info2off as-is, and check if we find a valid info * block at that location. */ offset = le32_to_cpu(btt_sb[0].info2off); if (offset > min(bttc->rawsize - BTT_INFO_SIZE, ARENA_MAX_SIZE - BTT_INFO_SIZE + off)) { rc = -ENXIO; goto out; } if (offset) { info(bttc, "Attempting to recover info-block from info2 offset %#lx\n", offset); rc = btt_info_read_verify(bttc, &btt_sb[1], offset + off); if (rc == 0) { rc = btt_write_info(bttc, &btt_sb[1], off); goto out; } } else rc = -ENXIO; out: free(btt_sb); return rc; } static int btt_recover_first_sb(struct btt_chk *bttc) { int offsets[BTT_NUM_OFFSETS] = { BTT1_START_OFFSET, BTT2_START_OFFSET, }; int i, rc; for (i = 0; i < BTT_NUM_OFFSETS; i++) { rc = __btt_recover_first_sb(bttc, offsets[i]); if (rc == 0) { bttc->start_off = offsets[i]; return rc; } } return rc; } int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force, bool repair, bool logfix) { const char *devname = ndctl_namespace_get_devname(ndns); struct check_opts __opts = { .verbose = verbose, .force = force, .repair = repair, .logfix = logfix, }, *opts = &__opts; int raw_mode, rc, disabled_flag = 0, open_flags; struct btt_sb *btt_sb; struct btt_chk *bttc; struct sigaction act; char path[50]; int i; bttc = calloc(1, sizeof(*bttc)); if (bttc == NULL) return -ENOMEM; log_init(&bttc->ctx, devname, "NDCTL_CHECK_NAMESPACE"); if (opts->verbose) bttc->ctx.log_priority = LOG_DEBUG; memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus_hdl; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { err(bttc, "Unable to set sigaction\n"); rc = -errno; goto out_bttc; } if (opts->logfix) { if (!opts->repair) { err(bttc, "--rewrite-log also requires --repair\n"); rc = -EINVAL; goto out_bttc; } info(bttc, "WARNING: interruption may cause unrecoverable metadata corruption\n"); } bttc->opts = opts; bttc->sys_page_size = sysconf(_SC_PAGESIZE); bttc->rawsize = ndctl_namespace_get_size(ndns); ndctl_namespace_get_uuid(ndns, bttc->parent_uuid); info(bttc, "checking %s\n", devname); if (ndctl_namespace_is_active(ndns)) { if (opts->force) { rc = ndctl_namespace_disable_safe(ndns); if (rc) goto out_bttc; disabled_flag = 1; } else { err(bttc, "%s: check aborted, namespace online\n", devname); rc = -EBUSY; goto out_bttc; } } /* In typical usage, the current raw_mode should be false. */ raw_mode = ndctl_namespace_get_raw_mode(ndns); /* * Putting the namespace into raw mode will allow us to access * the btt metadata. */ rc = ndctl_namespace_set_raw_mode(ndns, 1); if (rc < 0) { err(bttc, "%s: failed to set the raw mode flag: %s (%d)\n", devname, strerror(abs(rc)), rc); goto out_ns; } /* * Now enable the namespace. This will result in a pmem device * node showing up in /dev that is in raw mode. */ rc = ndctl_namespace_enable(ndns); if (rc != 0) { err(bttc, "%s: failed to enable in raw mode: %s (%d)\n", devname, strerror(abs(rc)), rc); goto out_ns; } sprintf(path, "/dev/%s", ndctl_namespace_get_block_device(ndns)); bttc->path = path; btt_sb = malloc(sizeof(*btt_sb)); if (btt_sb == NULL) { rc = -ENOMEM; goto out_ns; } if (!bttc->opts->repair) open_flags = O_RDONLY|O_EXCL; else open_flags = O_RDWR|O_EXCL; bttc->fd = open(bttc->path, open_flags); if (bttc->fd < 0) { err(bttc, "unable to open %s: %s\n", bttc->path, strerror(errno)); rc = -errno; goto out_sb; } /* * This is where we jump to if we receive a SIGBUS, prior to doing any * mmaped reads, and can safely abort */ if (sigsetjmp(sj_env, 1)) { err(bttc, "Received a SIGBUS\n"); err(bttc, "Metadata corruption found, recovery is not possible\n"); rc = -EFAULT; goto out_close; } /* Try reading a BTT1 info block first */ rc = btt_info_read_verify(bttc, btt_sb, BTT1_START_OFFSET); if (rc == 0) bttc->start_off = BTT1_START_OFFSET; if (rc) { /* Try reading a BTT2 info block */ rc = btt_info_read_verify(bttc, btt_sb, BTT2_START_OFFSET); if (rc == 0) bttc->start_off = BTT2_START_OFFSET; if (rc) { rc = btt_recover_first_sb(bttc); if (rc) { err(bttc, "Unable to recover any BTT info blocks\n"); err(bttc, "This may not be a sector mode namespace\n"); goto out_close; } /* * btt_recover_first_sb will have set bttc->start_off * based on the version it found */ rc = btt_info_read_verify(bttc, btt_sb, bttc->start_off); if (rc) goto out_close; } } rc = btt_discover_arenas(bttc); if (rc) goto out_close; rc = btt_create_mappings(bttc); if (rc) goto out_close; for (i = 0; i < bttc->num_arenas; i++) { rc = log_set_indices(&bttc->arena[i]); if (rc) { err(bttc, "Unable to deduce log/padding indices\n"); goto out_close; } } rc = btt_check_arenas(bttc); btt_remove_mappings(bttc); out_close: close(bttc->fd); out_sb: free(btt_sb); out_ns: ndctl_namespace_set_raw_mode(ndns, raw_mode); ndctl_namespace_disable_invalidate(ndns); if (disabled_flag) if(ndctl_namespace_enable(ndns) < 0) err(bttc, "%s: failed to re-enable namespace\n", devname); out_bttc: free(bttc); return rc; } ndctl-67/ndctl/create-nfit.c000066400000000000000000000117771355562357700161240ustar00rootroot00000000000000/* * Copyright(c) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_NFIT "local_nfit.bin" static const char *nfit_file = DEFAULT_NFIT; static LIST_HEAD(spas); struct spa { struct list_node list; unsigned long long size, offset; }; static int parse_add_spa(const struct option *option, const char *__arg, int unset) { struct spa *s = calloc(1, sizeof(struct spa)); char *arg = strdup(__arg); char *size, *offset; int rc = -ENOMEM; if (!s || !arg) goto err; rc = -EINVAL; size = arg; offset = strchr(arg, ','); if (!offset) goto err; *offset++ = '\0'; s->size = parse_size64(size); if (s->size == ULLONG_MAX) goto err; s->offset = parse_size64(offset); if (s->offset == ULLONG_MAX) goto err; list_add_tail(&spas, &s->list); free(arg); return 0; err: error("failed to parse --add-spa=%s\n", __arg); free(arg); free(s); return rc; } static unsigned char nfit_checksum(void *buf, size_t size) { unsigned char sum, *data = buf; size_t i; for (sum = 0, i = 0; i < size; i++) sum += data[i]; return 0 - sum; } static void writeq(unsigned long long v, void *a) { unsigned long long *p = a; *p = htole64(v); } static void writel(unsigned long v, void *a) { unsigned long *p = a; *p = htole32(v); } static void writew(unsigned short v, void *a) { unsigned short *p = a; *p = htole16(v); } static void writeb(unsigned char v, void *a) { unsigned char *p = a; *p = v; } static struct nfit *create_nfit(struct list_head *spa_list) { struct nfit_spa *nfit_spa; struct nfit *nfit; struct spa *s; size_t size; char *buf; int i; size = sizeof(struct nfit); list_for_each(spa_list, s, list) size += sizeof(struct nfit_spa); buf = calloc(1, size); if (!buf) return NULL; /* nfit header */ nfit = (struct nfit *) buf; memcpy(nfit->signature, "NFIT", 4); writel(size, &nfit->length); writeb(1, &nfit->revision); memcpy(nfit->oemid, "LOCAL", 6); writew(1, &nfit->oem_tbl_id); writel(1, &nfit->oem_revision); writel(0x80860000, &nfit->creator_id); writel(1, &nfit->creator_revision); nfit_spa = (struct nfit_spa *) (buf + sizeof(*nfit)); i = 1; list_for_each(spa_list, s, list) { writew(NFIT_TABLE_SPA, &nfit_spa->type); writew(sizeof(*nfit_spa), &nfit_spa->length); nfit_spa_uuid_pm(&nfit_spa->type_uuid); writew(i++, &nfit_spa->range_index); writeq(s->offset, &nfit_spa->spa_base); writeq(s->size, &nfit_spa->spa_length); nfit_spa++; } writeb(nfit_checksum(buf, size), &nfit->checksum); return nfit; } static int write_nfit(struct nfit *nfit, const char *file, int force) { int fd; ssize_t rc; mode_t mode = S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP; fd = open(file, O_RDWR|O_CREAT|O_EXCL, mode); if (fd < 0 && !force && errno == EEXIST) { error("\"%s\" exists, overwrite with --force\n", file); return -EEXIST; } else if (fd < 0 && force && errno == EEXIST) { fd = open(file, O_RDWR|O_CREAT|O_TRUNC, mode); } if (fd < 0) { error("Failed to open \"%s\": %s\n", file, strerror(errno)); return -errno; } rc = write(fd, nfit, le32toh(nfit->length)); close(fd); return rc; } struct ndctl_ctx; int cmd_create_nfit(int argc, const char **argv, struct ndctl_ctx *ctx) { int i, rc = -ENXIO, force = 0; const char * const u[] = { "ndctl create-nfit []", NULL }; const struct option options[] = { OPT_CALLBACK('a', "add-spa", NULL, "size,offset", "add a system-physical-address range table entry", parse_add_spa), OPT_STRING('o', NULL, &nfit_file, "file", "output to (default: " DEFAULT_NFIT ")"), OPT_INCR('f', "force", &force, "overwrite if it already exists"), OPT_END(), }; struct spa *s, *_s; struct nfit *nfit = NULL; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (list_empty(&spas)) error("specify at least one --add-spa= option\n"); if (argc || list_empty(&spas)) usage_with_options(u, options); nfit = create_nfit(&spas); if (!nfit) goto out; rc = write_nfit(nfit, nfit_file, force); if ((unsigned int) rc == le32toh(nfit->length)) { fprintf(stderr, "wrote %d bytes to %s\n", le32toh(nfit->length), nfit_file); rc = 0; } out: free(nfit); list_for_each_safe(&spas, s, _s, list) { list_del(&s->list); free(s); } return rc; } ndctl-67/ndctl/dimm.c000066400000000000000000001032521355562357700146370ustar00rootroot00000000000000/* * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct action_context { struct json_object *jdimms; enum ndctl_namespace_version labelversion; FILE *f_out; FILE *f_in; struct update_context update; }; static struct parameters { const char *bus; const char *outfile; const char *infile; const char *labelversion; const char *kek; unsigned len; unsigned offset; bool crypto_erase; bool overwrite; bool zero_key; bool master_pass; bool human; bool force; bool index; bool json; bool verbose; } param = { .labelversion = "1.1", }; static int action_disable(struct ndctl_dimm *dimm, struct action_context *actx) { if (ndctl_dimm_is_active(dimm)) { fprintf(stderr, "%s is active, skipping...\n", ndctl_dimm_get_devname(dimm)); return -EBUSY; } return ndctl_dimm_disable(dimm); } static int action_enable(struct ndctl_dimm *dimm, struct action_context *actx) { return ndctl_dimm_enable(dimm); } static int action_zero(struct ndctl_dimm *dimm, struct action_context *actx) { return ndctl_dimm_zero_label_extent(dimm, param.len, param.offset); } static struct json_object *dump_label_json(struct ndctl_dimm *dimm, struct ndctl_cmd *cmd_read, ssize_t size, unsigned long flags) { struct json_object *jarray = json_object_new_array(); struct json_object *jlabel = NULL; struct namespace_label nslabel; unsigned int slot = -1; ssize_t offset; if (!jarray) return NULL; for (offset = NSINDEX_ALIGN * 2; offset < size; offset += ndctl_dimm_sizeof_namespace_label(dimm)) { ssize_t len = min_t(ssize_t, ndctl_dimm_sizeof_namespace_label(dimm), size - offset); struct json_object *jobj; char uuid[40]; slot++; jlabel = json_object_new_object(); if (!jlabel) break; if (len < (ssize_t) ndctl_dimm_sizeof_namespace_label(dimm)) break; len = ndctl_cmd_cfg_read_get_data(cmd_read, &nslabel, len, offset); if (len < 0) break; if (le32_to_cpu(nslabel.slot) != slot) continue; uuid_unparse((void *) nslabel.uuid, uuid); jobj = json_object_new_string(uuid); if (!jobj) break; json_object_object_add(jlabel, "uuid", jobj); nslabel.name[NSLABEL_NAME_LEN - 1] = 0; jobj = json_object_new_string(nslabel.name); if (!jobj) break; json_object_object_add(jlabel, "name", jobj); jobj = json_object_new_int(le32_to_cpu(nslabel.slot)); if (!jobj) break; json_object_object_add(jlabel, "slot", jobj); jobj = json_object_new_int(le16_to_cpu(nslabel.position)); if (!jobj) break; json_object_object_add(jlabel, "position", jobj); jobj = json_object_new_int(le16_to_cpu(nslabel.nlabel)); if (!jobj) break; json_object_object_add(jlabel, "nlabel", jobj); jobj = util_json_object_hex(le32_to_cpu(nslabel.flags), flags); if (!jobj) break; json_object_object_add(jlabel, "flags", jobj); jobj = util_json_object_hex(le64_to_cpu(nslabel.isetcookie), flags); if (!jobj) break; json_object_object_add(jlabel, "isetcookie", jobj); jobj = json_object_new_int64(le64_to_cpu(nslabel.lbasize)); if (!jobj) break; json_object_object_add(jlabel, "lbasize", jobj); jobj = util_json_object_hex(le64_to_cpu(nslabel.dpa), flags); if (!jobj) break; json_object_object_add(jlabel, "dpa", jobj); jobj = util_json_object_size(le64_to_cpu(nslabel.rawsize), flags); if (!jobj) break; json_object_object_add(jlabel, "rawsize", jobj); json_object_array_add(jarray, jlabel); if (ndctl_dimm_sizeof_namespace_label(dimm) < 256) continue; uuid_unparse((void *) nslabel.type_guid, uuid); jobj = json_object_new_string(uuid); if (!jobj) break; json_object_object_add(jlabel, "type_guid", jobj); uuid_unparse((void *) nslabel.abstraction_guid, uuid); jobj = json_object_new_string(uuid); if (!jobj) break; json_object_object_add(jlabel, "abstraction_guid", jobj); } if (json_object_array_length(jarray) < 1) { json_object_put(jarray); if (jlabel) json_object_put(jlabel); jarray = NULL; } return jarray; } static struct json_object *dump_index_json(struct ndctl_cmd *cmd_read, ssize_t size) { struct json_object *jarray = json_object_new_array(); struct json_object *jindex = NULL; struct namespace_index nsindex; ssize_t offset; if (!jarray) return NULL; for (offset = 0; offset < NSINDEX_ALIGN * 2; offset += NSINDEX_ALIGN) { ssize_t len = min_t(ssize_t, sizeof(nsindex), size - offset); struct json_object *jobj; jindex = json_object_new_object(); if (!jindex) break; if (len < (ssize_t) sizeof(nsindex)) break; len = ndctl_cmd_cfg_read_get_data(cmd_read, &nsindex, len, offset); if (len < 0) break; nsindex.sig[NSINDEX_SIG_LEN - 1] = 0; jobj = json_object_new_string(nsindex.sig); if (!jobj) break; json_object_object_add(jindex, "signature", jobj); jobj = json_object_new_int(le16_to_cpu(nsindex.major)); if (!jobj) break; json_object_object_add(jindex, "major", jobj); jobj = json_object_new_int(le16_to_cpu(nsindex.minor)); if (!jobj) break; json_object_object_add(jindex, "minor", jobj); jobj = json_object_new_int(1 << (7 + nsindex.labelsize)); if (!jobj) break; json_object_object_add(jindex, "labelsize", jobj); jobj = json_object_new_int(le32_to_cpu(nsindex.seq)); if (!jobj) break; json_object_object_add(jindex, "seq", jobj); jobj = json_object_new_int(le32_to_cpu(nsindex.nslot)); if (!jobj) break; json_object_object_add(jindex, "nslot", jobj); json_object_array_add(jarray, jindex); } if (json_object_array_length(jarray) < 1) { json_object_put(jarray); if (jindex) json_object_put(jindex); jarray = NULL; } return jarray; } static struct json_object *dump_json(struct ndctl_dimm *dimm, struct ndctl_cmd *cmd_read, ssize_t size) { unsigned long flags = param.human ? UTIL_JSON_HUMAN : 0; struct json_object *jdimm = json_object_new_object(); struct json_object *jlabel, *jobj, *jindex; if (!jdimm) return NULL; jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); if (!jobj) goto err; json_object_object_add(jdimm, "dev", jobj); jindex = dump_index_json(cmd_read, size); if (!jindex) goto err; json_object_object_add(jdimm, "index", jindex); if (param.index) return jdimm; jlabel = dump_label_json(dimm, cmd_read, size, flags); if (!jlabel) goto err; json_object_object_add(jdimm, "label", jlabel); return jdimm; err: json_object_put(jdimm); return NULL; } static int rw_bin(FILE *f, struct ndctl_cmd *cmd, ssize_t size, unsigned int start_offset, int rw) { char buf[4096]; ssize_t offset, write = 0; for (offset = start_offset; offset < start_offset + size; offset += sizeof(buf)) { ssize_t len = min_t(ssize_t, sizeof(buf), size - offset), rc; if (rw == WRITE) { len = fread(buf, 1, len, f); if (len == 0) break; rc = ndctl_cmd_cfg_write_set_data(cmd, buf, len, offset); if (rc < 0) return -ENXIO; write += len; } else { len = ndctl_cmd_cfg_read_get_data(cmd, buf, len, offset); if (len < 0) return len; rc = fwrite(buf, 1, len, f); if (rc != len) return -ENXIO; fflush(f); } } if (write) return ndctl_cmd_submit(cmd); return 0; } static int action_write(struct ndctl_dimm *dimm, struct action_context *actx) { struct ndctl_cmd *cmd_read, *cmd_write; ssize_t size; int rc = 0; if (ndctl_dimm_is_active(dimm)) { fprintf(stderr, "dimm is active, abort label write\n"); return -EBUSY; } cmd_read = ndctl_dimm_read_label_extent(dimm, param.len, param.offset); if (!cmd_read) return -EINVAL; cmd_write = ndctl_dimm_cmd_new_cfg_write(cmd_read); if (!cmd_write) { ndctl_cmd_unref(cmd_read); return -ENXIO; } size = ndctl_cmd_cfg_read_get_size(cmd_read); rc = rw_bin(actx->f_in, cmd_write, size, param.offset, WRITE); /* * If the dimm is already disabled the kernel is not holding a cached * copy of the label space. */ if (!ndctl_dimm_is_enabled(dimm)) goto out; rc = ndctl_dimm_disable(dimm); if (rc) goto out; rc = ndctl_dimm_enable(dimm); out: ndctl_cmd_unref(cmd_read); ndctl_cmd_unref(cmd_write); return rc; } static int action_read(struct ndctl_dimm *dimm, struct action_context *actx) { struct ndctl_cmd *cmd_read; ssize_t size; int rc = 0; if (param.index) cmd_read = ndctl_dimm_read_label_index(dimm); else cmd_read = ndctl_dimm_read_label_extent(dimm, param.len, param.offset); if (!cmd_read) return -EINVAL; size = ndctl_cmd_cfg_read_get_size(cmd_read); if (actx->jdimms) { struct json_object *jdimm = dump_json(dimm, cmd_read, size); if (jdimm) json_object_array_add(actx->jdimms, jdimm); else rc = -ENOMEM; } else rc = rw_bin(actx->f_out, cmd_read, size, param.offset, READ); ndctl_cmd_unref(cmd_read); return rc; } static int update_verify_input(struct action_context *actx) { int rc; struct stat st; rc = fstat(fileno(actx->f_in), &st); if (rc == -1) { rc = -errno; fprintf(stderr, "fstat failed: %s\n", strerror(errno)); return rc; } if (!S_ISREG(st.st_mode)) { fprintf(stderr, "Input not a regular file.\n"); return -EINVAL; } if (st.st_size == 0) { fprintf(stderr, "Input file size is 0.\n"); return -EINVAL; } actx->update.fw_size = st.st_size; return 0; } static int verify_fw_size(struct update_context *uctx) { struct fw_info *fw = &uctx->dimm_fw; if (uctx->fw_size > fw->store_size) { error("Firmware file size greater than DIMM store\n"); return -ENOSPC; } return 0; } static int submit_get_firmware_info(struct ndctl_dimm *dimm, struct action_context *actx) { struct update_context *uctx = &actx->update; struct fw_info *fw = &uctx->dimm_fw; struct ndctl_cmd *cmd; int rc; enum ND_FW_STATUS status; cmd = ndctl_dimm_cmd_new_fw_get_info(dimm); if (!cmd) return -ENXIO; rc = ndctl_cmd_submit(cmd); if (rc < 0) goto out; rc = -ENXIO; status = ndctl_cmd_fw_xlat_firmware_status(cmd); if (status != FW_SUCCESS) { fprintf(stderr, "GET FIRMWARE INFO on DIMM %s failed: %#x\n", ndctl_dimm_get_devname(dimm), status); goto out; } fw->store_size = ndctl_cmd_fw_info_get_storage_size(cmd); if (fw->store_size == UINT_MAX) goto out; fw->update_size = ndctl_cmd_fw_info_get_max_send_len(cmd); if (fw->update_size == UINT_MAX) goto out; fw->query_interval = ndctl_cmd_fw_info_get_query_interval(cmd); if (fw->query_interval == UINT_MAX) goto out; fw->max_query = ndctl_cmd_fw_info_get_max_query_time(cmd); if (fw->max_query == UINT_MAX) goto out; fw->run_version = ndctl_cmd_fw_info_get_run_version(cmd); if (fw->run_version == ULLONG_MAX) goto out; rc = verify_fw_size(uctx); out: ndctl_cmd_unref(cmd); return rc; } static int submit_start_firmware_upload(struct ndctl_dimm *dimm, struct action_context *actx) { struct update_context *uctx = &actx->update; struct fw_info *fw = &uctx->dimm_fw; struct ndctl_cmd *cmd; int rc; enum ND_FW_STATUS status; cmd = ndctl_dimm_cmd_new_fw_start_update(dimm); if (!cmd) return -ENXIO; rc = ndctl_cmd_submit(cmd); if (rc < 0) return rc; status = ndctl_cmd_fw_xlat_firmware_status(cmd); if (status != FW_SUCCESS) { fprintf(stderr, "START FIRMWARE UPDATE on DIMM %s failed: %#x\n", ndctl_dimm_get_devname(dimm), status); if (status == FW_EBUSY) fprintf(stderr, "Another firmware upload in progress" " or firmware already updated.\n"); return -ENXIO; } fw->context = ndctl_cmd_fw_start_get_context(cmd); if (fw->context == UINT_MAX) { fprintf(stderr, "Retrieved firmware context invalid on DIMM %s\n", ndctl_dimm_get_devname(dimm)); return -ENXIO; } uctx->start = cmd; return 0; } static int get_fw_data_from_file(FILE *file, void *buf, uint32_t len) { size_t rc; rc = fread(buf, len, 1, file); if (rc != 1) { if (feof(file)) fprintf(stderr, "Firmware file shorter than expected\n"); else if (ferror(file)) fprintf(stderr, "Firmware file read error\n"); return -EBADF; } return len; } static int send_firmware(struct ndctl_dimm *dimm, struct action_context *actx) { struct update_context *uctx = &actx->update; struct fw_info *fw = &uctx->dimm_fw; struct ndctl_cmd *cmd = NULL; ssize_t read; int rc = -ENXIO; enum ND_FW_STATUS status; uint32_t copied = 0, len, remain; void *buf; buf = malloc(fw->update_size); if (!buf) return -ENOMEM; remain = uctx->fw_size; while (remain) { len = min(fw->update_size, remain); read = get_fw_data_from_file(actx->f_in, buf, len); if (read < 0) { rc = read; goto cleanup; } cmd = ndctl_dimm_cmd_new_fw_send(uctx->start, copied, read, buf); if (!cmd) { rc = -ENXIO; goto cleanup; } rc = ndctl_cmd_submit(cmd); if (rc < 0) goto cleanup; status = ndctl_cmd_fw_xlat_firmware_status(cmd); if (status != FW_SUCCESS) { error("SEND FIRMWARE failed: %#x\n", status); rc = -ENXIO; goto cleanup; } copied += read; remain -= read; ndctl_cmd_unref(cmd); cmd = NULL; } cleanup: ndctl_cmd_unref(cmd); free(buf); return rc; } static int submit_finish_firmware(struct ndctl_dimm *dimm, struct action_context *actx) { struct update_context *uctx = &actx->update; struct ndctl_cmd *cmd; int rc; enum ND_FW_STATUS status; cmd = ndctl_dimm_cmd_new_fw_finish(uctx->start); if (!cmd) return -ENXIO; rc = ndctl_cmd_submit(cmd); if (rc < 0) goto out; status = ndctl_cmd_fw_xlat_firmware_status(cmd); if (status != FW_SUCCESS) { fprintf(stderr, "FINISH FIRMWARE UPDATE on DIMM %s failed: %#x\n", ndctl_dimm_get_devname(dimm), status); rc = -ENXIO; goto out; } out: ndctl_cmd_unref(cmd); return rc; } static int submit_abort_firmware(struct ndctl_dimm *dimm, struct action_context *actx) { struct update_context *uctx = &actx->update; struct ndctl_cmd *cmd; int rc; enum ND_FW_STATUS status; cmd = ndctl_dimm_cmd_new_fw_abort(uctx->start); if (!cmd) return -ENXIO; rc = ndctl_cmd_submit(cmd); if (rc < 0) goto out; status = ndctl_cmd_fw_xlat_firmware_status(cmd); if (!(status & ND_CMD_STATUS_FIN_ABORTED)) { fprintf(stderr, "Firmware update abort on DIMM %s failed: %#x\n", ndctl_dimm_get_devname(dimm), status); rc = -ENXIO; goto out; } out: ndctl_cmd_unref(cmd); return rc; } static int query_fw_finish_status(struct ndctl_dimm *dimm, struct action_context *actx) { struct update_context *uctx = &actx->update; struct fw_info *fw = &uctx->dimm_fw; struct ndctl_cmd *cmd; int rc; enum ND_FW_STATUS status; struct timespec now, before, after; uint64_t ver; cmd = ndctl_dimm_cmd_new_fw_finish_query(uctx->start); if (!cmd) return -ENXIO; rc = clock_gettime(CLOCK_MONOTONIC, &before); if (rc < 0) goto out; now.tv_nsec = fw->query_interval / 1000; now.tv_sec = 0; do { rc = ndctl_cmd_submit(cmd); if (rc < 0) break; status = ndctl_cmd_fw_xlat_firmware_status(cmd); switch (status) { case FW_SUCCESS: ver = ndctl_cmd_fw_fquery_get_fw_rev(cmd); if (ver == 0) { fprintf(stderr, "No firmware updated.\n"); rc = -ENXIO; goto out; } printf("Image updated successfully to DIMM %s.\n", ndctl_dimm_get_devname(dimm)); printf("Firmware version %#lx.\n", ver); printf("Cold reboot to activate.\n"); rc = 0; goto out; break; case FW_EBUSY: /* Still on going, continue */ rc = clock_gettime(CLOCK_MONOTONIC, &after); if (rc < 0) { rc = -errno; goto out; } /* * If we expire max query time, * we timed out */ if (after.tv_sec - before.tv_sec > fw->max_query / 1000000) { rc = -ETIMEDOUT; goto out; } /* * Sleep the interval dictated by firmware * before query again. */ rc = nanosleep(&now, NULL); if (rc < 0) { rc = -errno; goto out; } break; case FW_EBADFW: fprintf(stderr, "Firmware failed to verify by DIMM %s.\n", ndctl_dimm_get_devname(dimm)); case FW_EINVAL_CTX: case FW_ESEQUENCE: rc = -ENXIO; goto out; case FW_ENORES: fprintf(stderr, "Firmware update sequence timed out: %s\n", ndctl_dimm_get_devname(dimm)); rc = -ETIMEDOUT; goto out; default: fprintf(stderr, "Unknown update status: %#x on DIMM %s\n", status, ndctl_dimm_get_devname(dimm)); rc = -EINVAL; goto out; } } while (true); out: ndctl_cmd_unref(cmd); return rc; } static int update_firmware(struct ndctl_dimm *dimm, struct action_context *actx) { int rc; rc = submit_get_firmware_info(dimm, actx); if (rc < 0) return rc; rc = submit_start_firmware_upload(dimm, actx); if (rc < 0) return rc; printf("Uploading firmware to DIMM %s.\n", ndctl_dimm_get_devname(dimm)); rc = send_firmware(dimm, actx); if (rc < 0) { fprintf(stderr, "Firmware send failed. Aborting!\n"); rc = submit_abort_firmware(dimm, actx); if (rc < 0) fprintf(stderr, "Aborting update sequence failed.\n"); return rc; } /* * Done reading file, reset firmware file back to beginning for * next update. */ rewind(actx->f_in); rc = submit_finish_firmware(dimm, actx); if (rc < 0) { fprintf(stderr, "Unable to end update sequence.\n"); rc = submit_abort_firmware(dimm, actx); if (rc < 0) fprintf(stderr, "Aborting update sequence failed.\n"); return rc; } rc = query_fw_finish_status(dimm, actx); if (rc < 0) return rc; return 0; } static int action_update(struct ndctl_dimm *dimm, struct action_context *actx) { int rc; rc = ndctl_dimm_fw_update_supported(dimm); switch (rc) { case -ENOTTY: error("%s: firmware update not supported by ndctl.", ndctl_dimm_get_devname(dimm)); return rc; case -EOPNOTSUPP: error("%s: firmware update not supported by the kernel", ndctl_dimm_get_devname(dimm)); return rc; case -EIO: error("%s: firmware update not supported by either platform firmware or the kernel.", ndctl_dimm_get_devname(dimm)); return rc; } rc = update_verify_input(actx); if (rc < 0) return rc; rc = update_firmware(dimm, actx); if (rc < 0) return rc; ndctl_cmd_unref(actx->update.start); return rc; } static int action_setup_passphrase(struct ndctl_dimm *dimm, struct action_context *actx) { if (ndctl_dimm_get_security(dimm) < 0) { error("%s: security operation not supported\n", ndctl_dimm_get_devname(dimm)); return -EOPNOTSUPP; } if (!param.kek) return -EINVAL; return ndctl_dimm_setup_key(dimm, param.kek, param.master_pass ? ND_MASTER_KEY : ND_USER_KEY); } static int action_update_passphrase(struct ndctl_dimm *dimm, struct action_context *actx) { if (ndctl_dimm_get_security(dimm) < 0) { error("%s: security operation not supported\n", ndctl_dimm_get_devname(dimm)); return -EOPNOTSUPP; } return ndctl_dimm_update_key(dimm, param.kek, param.master_pass ? ND_MASTER_KEY : ND_USER_KEY); } static int action_remove_passphrase(struct ndctl_dimm *dimm, struct action_context *actx) { if (ndctl_dimm_get_security(dimm) < 0) { error("%s: security operation not supported\n", ndctl_dimm_get_devname(dimm)); return -EOPNOTSUPP; } return ndctl_dimm_remove_key(dimm); } static int action_security_freeze(struct ndctl_dimm *dimm, struct action_context *actx) { int rc; if (ndctl_dimm_get_security(dimm) < 0) { error("%s: security operation not supported\n", ndctl_dimm_get_devname(dimm)); return -EOPNOTSUPP; } rc = ndctl_dimm_freeze_security(dimm); if (rc < 0) error("Failed to freeze security for %s\n", ndctl_dimm_get_devname(dimm)); return rc; } static int action_sanitize_dimm(struct ndctl_dimm *dimm, struct action_context *actx) { int rc; enum ndctl_key_type key_type; if (ndctl_dimm_get_security(dimm) < 0) { error("%s: security operation not supported\n", ndctl_dimm_get_devname(dimm)); return -EOPNOTSUPP; } if (param.overwrite && param.master_pass) { error("%s: overwrite does not support master passphrase\n", ndctl_dimm_get_devname(dimm)); return -EINVAL; } /* * Setting crypto erase to be default. The other method will be * overwrite. */ if (!param.crypto_erase && !param.overwrite) { param.crypto_erase = true; printf("No santize method passed in, default to crypto-erase\n"); } if (param.crypto_erase) { if (param.zero_key) key_type = ND_ZERO_KEY; else if (param.master_pass) key_type = ND_MASTER_KEY; else key_type = ND_USER_KEY; rc = ndctl_dimm_secure_erase_key(dimm, key_type); if (rc < 0) return rc; } if (param.overwrite) { rc = ndctl_dimm_overwrite_key(dimm); if (rc < 0) return rc; } return 0; } static int action_wait_overwrite(struct ndctl_dimm *dimm, struct action_context *actx) { int rc; if (ndctl_dimm_get_security(dimm) < 0) { error("%s: security operation not supported\n", ndctl_dimm_get_devname(dimm)); return -EOPNOTSUPP; } rc = ndctl_dimm_wait_overwrite(dimm); if (rc == 1) printf("%s: overwrite completed.\n", ndctl_dimm_get_devname(dimm)); return rc; } static int __action_init(struct ndctl_dimm *dimm, enum ndctl_namespace_version version, int chk_only) { struct ndctl_cmd *cmd_read; int rc; cmd_read = ndctl_dimm_read_label_index(dimm); if (!cmd_read) return -ENXIO; /* * If the region goes active after this point, i.e. we're racing * another administrative action, the kernel will fail writes to * the label area. */ if (!chk_only && ndctl_dimm_is_active(dimm)) { fprintf(stderr, "%s: regions active, abort label write\n", ndctl_dimm_get_devname(dimm)); rc = -EBUSY; goto out; } rc = ndctl_dimm_validate_labels(dimm); if (chk_only) goto out; if (rc >= 0 && !param.force) { fprintf(stderr, "%s: error: labels already initialized\n", ndctl_dimm_get_devname(dimm)); rc = -EBUSY; goto out; } rc = ndctl_dimm_init_labels(dimm, version); if (rc < 0) goto out; /* * If the dimm is already disabled the kernel is not holding a cached * copy of the label space. */ if (!ndctl_dimm_is_enabled(dimm)) goto out; rc = ndctl_dimm_disable(dimm); if (rc) goto out; rc = ndctl_dimm_enable(dimm); out: ndctl_cmd_unref(cmd_read); return rc >= 0 ? 0 : rc; } static int action_init(struct ndctl_dimm *dimm, struct action_context *actx) { return __action_init(dimm, actx->labelversion, 0); } static int action_check(struct ndctl_dimm *dimm, struct action_context *actx) { return __action_init(dimm, 0, 1); } #define BASE_OPTIONS() \ OPT_STRING('b', "bus", ¶m.bus, "bus-id", \ " must be on a bus with an id/provider of "), \ OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug") #define READ_OPTIONS() \ OPT_STRING('o', "output", ¶m.outfile, "output-file", \ "filename to write label area contents"), \ OPT_BOOLEAN('j', "json", ¶m.json, "parse label data into json"), \ OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats (implies --json)"), \ OPT_BOOLEAN('I', "index", ¶m.index, "limit read to the index block area") #define WRITE_OPTIONS() \ OPT_STRING('i', "input", ¶m.infile, "input-file", \ "filename to read label area data") #define UPDATE_OPTIONS() \ OPT_STRING('f', "firmware", ¶m.infile, "firmware-file", \ "firmware filename for update") #define INIT_OPTIONS() \ OPT_BOOLEAN('f', "force", ¶m.force, \ "force initialization even if existing index-block present"), \ OPT_STRING('V', "label-version", ¶m.labelversion, "version-number", \ "namespace label specification version (default: 1.1)") #define KEY_OPTIONS() \ OPT_STRING('k', "key-handle", ¶m.kek, "key-handle", \ "master encryption key handle") #define SANITIZE_OPTIONS() \ OPT_BOOLEAN('c', "crypto-erase", ¶m.crypto_erase, \ "crypto erase a dimm"), \ OPT_BOOLEAN('o', "overwrite", ¶m.overwrite, \ "overwrite a dimm"), \ OPT_BOOLEAN('z', "zero-key", ¶m.zero_key, \ "pass in a zero key") #define MASTER_OPTIONS() \ OPT_BOOLEAN('m', "master-passphrase", ¶m.master_pass, \ "use master passphrase") #define LABEL_OPTIONS() \ OPT_UINTEGER('s', "size", ¶m.len, "number of label bytes to operate"), \ OPT_UINTEGER('O', "offset", ¶m.offset, \ "offset into the label area to start operation") static const struct option read_options[] = { BASE_OPTIONS(), LABEL_OPTIONS(), READ_OPTIONS(), OPT_END(), }; static const struct option write_options[] = { BASE_OPTIONS(), LABEL_OPTIONS(), WRITE_OPTIONS(), OPT_END(), }; static const struct option zero_options[] = { BASE_OPTIONS(), LABEL_OPTIONS(), OPT_END(), }; static const struct option update_options[] = { BASE_OPTIONS(), UPDATE_OPTIONS(), OPT_END(), }; static const struct option base_options[] = { BASE_OPTIONS(), OPT_END(), }; static const struct option init_options[] = { BASE_OPTIONS(), INIT_OPTIONS(), OPT_END(), }; static const struct option key_options[] = { BASE_OPTIONS(), KEY_OPTIONS(), MASTER_OPTIONS(), }; static const struct option sanitize_options[] = { BASE_OPTIONS(), SANITIZE_OPTIONS(), MASTER_OPTIONS(), OPT_END(), }; static int dimm_action(int argc, const char **argv, struct ndctl_ctx *ctx, int (*action)(struct ndctl_dimm *dimm, struct action_context *actx), const struct option *options, const char *usage) { struct action_context actx = { 0 }; int i, rc = 0, count = 0, err = 0; struct ndctl_dimm *single = NULL; const char * const u[] = { usage, NULL }; unsigned long id; bool json = false; argc = parse_options(argc, argv, options, u, 0); if (argc == 0) usage_with_options(u, options); for (i = 0; i < argc; i++) { if (strcmp(argv[i], "all") == 0) { argv[0] = "all"; argc = 1; break; } if (sscanf(argv[i], "nmem%lu", &id) != 1) { fprintf(stderr, "'%s' is not a valid dimm name\n", argv[i]); err++; } } if (err == argc) { usage_with_options(u, options); return -EINVAL; } json = param.json || param.human; if (action == action_read && json && (param.len || param.offset)) { fprintf(stderr, "--size and --offset are incompatible with --json\n"); usage_with_options(u, options); return -EINVAL; } if (param.index && param.len) { fprintf(stderr, "pick either --size, or --index, not both\n"); usage_with_options(u, options); return -EINVAL; } if (json) { actx.jdimms = json_object_new_array(); if (!actx.jdimms) return -ENOMEM; } if (!param.outfile) actx.f_out = stdout; else { actx.f_out = fopen(param.outfile, "w+"); if (!actx.f_out) { fprintf(stderr, "failed to open: %s: (%s)\n", param.outfile, strerror(errno)); rc = -errno; goto out; } } if (!param.infile) { if (action == action_update) { usage_with_options(u, options); return -EINVAL; } actx.f_in = stdin; } else { actx.f_in = fopen(param.infile, "r"); if (!actx.f_in) { fprintf(stderr, "failed to open: %s: (%s)\n", param.infile, strerror(errno)); rc = -errno; goto out; } } if (param.verbose) ndctl_set_log_priority(ctx, LOG_DEBUG); if (strcmp(param.labelversion, "1.1") == 0) actx.labelversion = NDCTL_NS_VERSION_1_1; else if (strcmp(param.labelversion, "v1.1") == 0) actx.labelversion = NDCTL_NS_VERSION_1_1; else if (strcmp(param.labelversion, "1.2") == 0) actx.labelversion = NDCTL_NS_VERSION_1_2; else if (strcmp(param.labelversion, "v1.2") == 0) actx.labelversion = NDCTL_NS_VERSION_1_2; else { fprintf(stderr, "'%s' is not a valid label version\n", param.labelversion); rc = -EINVAL; goto out; } rc = 0; err = 0; count = 0; for (i = 0; i < argc; i++) { struct ndctl_dimm *dimm; struct ndctl_bus *bus; if (sscanf(argv[i], "nmem%lu", &id) != 1 && strcmp(argv[i], "all") != 0) continue; ndctl_bus_foreach(ctx, bus) { if (!util_bus_filter(bus, param.bus)) continue; ndctl_dimm_foreach(bus, dimm) { if (!util_dimm_filter(dimm, argv[i])) continue; if (action == action_write) { single = dimm; rc = 0; } else rc = action(dimm, &actx); if (rc == 0) count++; else if (rc && !err) err = rc; } } } rc = err; if (action == action_write) { if (count > 1) { error("write-labels only supports writing a single dimm\n"); usage_with_options(u, options); return -EINVAL; } else if (single) rc = action(single, &actx); } if (actx.jdimms) util_display_json_array(actx.f_out, actx.jdimms, 0); if (actx.f_out != stdout) fclose(actx.f_out); if (actx.f_in != stdin) fclose(actx.f_in); out: /* * count if some actions succeeded, 0 if none were attempted, * negative error code otherwise. */ if (count > 0) return count; return rc; } int cmd_write_labels(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_write, write_options, "ndctl write-labels [-i ]"); fprintf(stderr, "wrote %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_read_labels(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_read, read_options, "ndctl read-labels [..] [-o ]"); fprintf(stderr, "read %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_zero_labels(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_zero, zero_options, "ndctl zero-labels [..] []"); fprintf(stderr, "zeroed %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_init_labels(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_init, init_options, "ndctl init-labels [..] []"); fprintf(stderr, "initialized %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_check_labels(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_check, base_options, "ndctl check-labels [..] []"); fprintf(stderr, "successfully verified %d nmem label%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_disable_dimm(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_disable, base_options, "ndctl disable-dimm [..] []"); fprintf(stderr, "disabled %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_enable_dimm(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_enable, base_options, "ndctl enable-dimm [..] []"); fprintf(stderr, "enabled %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_update_firmware(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_update, update_options, "ndctl update-firmware [..] []"); fprintf(stderr, "updated %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_update_passphrase(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_update_passphrase, key_options, "ndctl update-passphrase [..] []"); fprintf(stderr, "passphrase updated for %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_setup_passphrase(int argc, const char **argv, struct ndctl_ctx *ctx) { int count = dimm_action(argc, argv, ctx, action_setup_passphrase, key_options, "ndctl setup-passphrase [..] []"); fprintf(stderr, "passphrase enabled for %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_remove_passphrase(int argc, const char **argv, void *ctx) { int count = dimm_action(argc, argv, ctx, action_remove_passphrase, base_options, "ndctl remove-passphrase [..] []"); fprintf(stderr, "passphrase removed for %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_freeze_security(int argc, const char **argv, void *ctx) { int count = dimm_action(argc, argv, ctx, action_security_freeze, base_options, "ndctl freeze-security [..] []"); fprintf(stderr, "security froze %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_sanitize_dimm(int argc, const char **argv, void *ctx) { int count = dimm_action(argc, argv, ctx, action_sanitize_dimm, sanitize_options, "ndctl sanitize-dimm [..] []"); if (param.overwrite) fprintf(stderr, "overwrite issued for %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); else fprintf(stderr, "sanitized %d nmem%s.\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_wait_overwrite(int argc, const char **argv, void *ctx) { int count = dimm_action(argc, argv, ctx, action_wait_overwrite, base_options, "ndctl wait-overwrite [..] []"); return count >= 0 ? 0 : EXIT_FAILURE; } ndctl-67/ndctl/firmware-update.h000066400000000000000000000022551355562357700170130ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #ifndef _FIRMWARE_UPDATE_H_ #define _FIRMWARE_UPDATE_H_ #define ND_CMD_STATUS_SUCCESS 0 #define ND_CMD_STATUS_NOTSUPP 1 #define ND_CMD_STATUS_NOTEXIST 2 #define ND_CMD_STATUS_INVALPARM 3 #define ND_CMD_STATUS_HWERR 4 #define ND_CMD_STATUS_RETRY 5 #define ND_CMD_STATUS_UNKNOWN 6 #define ND_CMD_STATUS_EXTEND 7 #define ND_CMD_STATUS_NORES 8 #define ND_CMD_STATUS_NOTREADY 9 /* extended status through ND_CMD_STATUS_EXTEND */ #define ND_CMD_STATUS_START_BUSY 0x10000 #define ND_CMD_STATUS_SEND_CTXINVAL 0x10000 #define ND_CMD_STATUS_FIN_CTXINVAL 0x10000 #define ND_CMD_STATUS_FIN_DONE 0x20000 #define ND_CMD_STATUS_FIN_BAD 0x30000 #define ND_CMD_STATUS_FIN_ABORTED 0x40000 #define ND_CMD_STATUS_FQ_CTXINVAL 0x10000 #define ND_CMD_STATUS_FQ_BUSY 0x20000 #define ND_CMD_STATUS_FQ_BAD 0x30000 #define ND_CMD_STATUS_FQ_ORDER 0x40000 struct fw_info { uint32_t store_size; uint32_t update_size; uint32_t query_interval; uint32_t max_query; uint64_t run_version; uint32_t context; }; struct update_context { size_t fw_size; struct fw_info dimm_fw; struct ndctl_cmd *start; }; #endif ndctl-67/ndctl/inject-error.c000066400000000000000000000222241355562357700163130ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool verbose; static struct parameters { const char *bus; const char *region; const char *namespace; const char *block; const char *count; bool clear; bool status; bool no_notify; bool saturate; bool human; } param; static struct inject_ctx { u64 block; u64 count; unsigned int op_mask; unsigned long json_flags; unsigned int inject_flags; } ictx; #define BASE_OPTIONS() \ OPT_STRING('b', "bus", ¶m.bus, "bus-id", \ "limit namespace to a bus with an id or provider of "), \ OPT_STRING('r', "region", ¶m.region, "region-id", \ "limit namespace to a region with an id or name of "), \ OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr") #define INJECT_OPTIONS() \ OPT_STRING('B', "block", ¶m.block, "namespace block offset (512B)", \ "specify the block at which to (un)inject the error"), \ OPT_STRING('n', "count", ¶m.count, "count", \ "specify the number of blocks of errors to (un)inject"), \ OPT_BOOLEAN('d', "uninject", ¶m.clear, \ "un-inject a previously injected error"), \ OPT_BOOLEAN('t', "status", ¶m.status, "get error injection status"), \ OPT_BOOLEAN('N', "no-notify", ¶m.no_notify, "firmware should not notify OS"), \ OPT_BOOLEAN('S', "saturate", ¶m.saturate, \ "inject full sector, not just 'ars_unit' bytes"), \ OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats ") static const struct option inject_options[] = { BASE_OPTIONS(), INJECT_OPTIONS(), OPT_END(), }; enum { OP_INJECT = 0, OP_CLEAR, OP_STATUS, }; static int inject_init(void) { if (!param.clear && !param.status) { ictx.op_mask |= 1 << OP_INJECT; ictx.inject_flags |= (1 << NDCTL_NS_INJECT_NOTIFY); if (param.no_notify) ictx.inject_flags &= ~(1 << NDCTL_NS_INJECT_NOTIFY); } if (param.clear) { if (param.status) { error("status is invalid with inject or uninject\n"); return -EINVAL; } ictx.op_mask |= 1 << OP_CLEAR; } if (param.status) { if (param.block || param.count || param.saturate) { error("status is invalid with inject or uninject\n"); return -EINVAL; } ictx.op_mask |= 1 << OP_STATUS; } if (ictx.op_mask == 0) { error("Unable to determine operation\n"); return -EINVAL; } ictx.op_mask &= ( (1 << OP_INJECT) | (1 << OP_CLEAR) | (1 << OP_STATUS)); if (param.block) { ictx.block = parse_size64(param.block); if (ictx.block == ULLONG_MAX) { error("Invalid block: %s\n", param.block); return -EINVAL; } } if (param.count) { ictx.count = parse_size64(param.count); if (ictx.count == ULLONG_MAX) { error("Invalid count: %s\n", param.count); return -EINVAL; } } /* For inject or clear, an block and count are required */ if (ictx.op_mask & ((1 << OP_INJECT) | (1 << OP_CLEAR))) { if (!param.block || !param.count) { error("block and count required for inject/uninject\n"); return -EINVAL; } } if (param.human) ictx.json_flags |= UTIL_JSON_HUMAN; if (param.saturate) ictx.inject_flags |= 1 << NDCTL_NS_INJECT_SATURATE; return 0; } static int ns_errors_to_json(struct ndctl_namespace *ndns, unsigned int start_count) { unsigned long json_flags = ictx.json_flags | UTIL_JSON_MEDIA_ERRORS; struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct json_object *jndns; unsigned int count; int rc, tmo = 30; /* only wait for scrubs for the inject and notify case */ if ((ictx.op_mask & (1 << OP_INJECT)) && (ictx.inject_flags & (1 << NDCTL_NS_INJECT_NOTIFY))) { do { /* wait for a scrub to start */ count = ndctl_bus_get_scrub_count(bus); if (count == UINT_MAX) { fprintf(stderr, "Unable to get scrub count\n"); return -ENXIO; } sleep(1); } while (count <= start_count && --tmo > 0); rc = ndctl_bus_wait_for_scrub_completion(bus); if (rc) { fprintf(stderr, "Error waiting for scrub\n"); return rc; } } jndns = util_namespace_to_json(ndns, json_flags); if (jndns) printf("%s\n", json_object_to_json_string_ext(jndns, JSON_C_TO_STRING_PRETTY)); return 0; } static int inject_error(struct ndctl_namespace *ndns, u64 offset, u64 length, unsigned int flags) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); unsigned int scrub_count; int rc; scrub_count = ndctl_bus_get_scrub_count(bus); if (scrub_count == UINT_MAX) { fprintf(stderr, "Unable to get scrub count\n"); return -ENXIO; } rc = ndctl_namespace_inject_error2(ndns, offset, length, flags); if (rc) { fprintf(stderr, "Unable to inject error: %s (%d)\n", strerror(abs(rc)), rc); return rc; } return ns_errors_to_json(ndns, scrub_count); } static int uninject_error(struct ndctl_namespace *ndns, u64 offset, u64 length, unsigned int flags) { int rc; rc = ndctl_namespace_uninject_error2(ndns, offset, length, flags); if (rc) { fprintf(stderr, "Unable to uninject error: %s (%d)\n", strerror(abs(rc)), rc); return rc; } printf("Warning: Un-injecting previously injected errors here will\n"); printf("not cause the kernel to 'forget' its badblock entries. Those\n"); printf("have to be cleared through the normal process of writing\n"); printf("the affected blocks\n\n"); return ns_errors_to_json(ndns, 0); } static int injection_status(struct ndctl_namespace *ndns) { unsigned long long block, count, bbs = 0; struct json_object *jbbs, *jbb, *jobj; struct ndctl_bb *bb; int rc; rc = ndctl_namespace_injection_status(ndns); if (rc) { fprintf(stderr, "Unable to get injection status: %s (%d)\n", strerror(abs(rc)), rc); return rc; } jobj = json_object_new_object(); if (!jobj) return -ENOMEM; jbbs = json_object_new_array(); if (!jbbs) { json_object_put(jobj); return -ENOMEM; } ndctl_namespace_bb_foreach(ndns, bb) { if (!bb) break; block = ndctl_bb_get_block(bb); count = ndctl_bb_get_count(bb); jbb = util_badblock_rec_to_json(block, count, ictx.json_flags); if (!jbb) break; json_object_array_add(jbbs, jbb); bbs++; } if (bbs) { json_object_object_add(jobj, "badblocks", jbbs); printf("%s\n", json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY)); } json_object_put(jobj); return rc; } static int err_inject_ns(struct ndctl_namespace *ndns) { unsigned int op_mask; int rc; op_mask = ictx.op_mask; while (op_mask) { if (op_mask & (1 << OP_INJECT)) { rc = inject_error(ndns, ictx.block, ictx.count, ictx.inject_flags); if (rc) return rc; op_mask &= ~(1 << OP_INJECT); } if (op_mask & (1 << OP_CLEAR)) { rc = uninject_error(ndns, ictx.block, ictx.count, ictx.inject_flags); if (rc) return rc; op_mask &= ~(1 << OP_CLEAR); } if (op_mask & (1 << OP_STATUS)) { rc = injection_status(ndns); if (rc) return rc; op_mask &= ~(1 << OP_STATUS); } } return rc; } static int do_inject(const char *namespace, struct ndctl_ctx *ctx) { struct ndctl_namespace *ndns; struct ndctl_region *region; const char *ndns_name; struct ndctl_bus *bus; int rc = -ENXIO; if (namespace == NULL) return rc; if (verbose) ndctl_set_log_priority(ctx, LOG_DEBUG); ndctl_bus_foreach(ctx, bus) { if (!util_bus_filter(bus, param.bus)) continue; ndctl_region_foreach(bus, region) { if (!util_region_filter(region, param.region)) continue; ndctl_namespace_foreach(region, ndns) { ndns_name = ndctl_namespace_get_devname(ndns); if (strcmp(namespace, ndns_name) != 0) continue; if (!ndctl_bus_has_error_injection(bus)) { fprintf(stderr, "%s: error injection not supported\n", ndns_name); return -EOPNOTSUPP; } return err_inject_ns(ndns); } } } error("%s: no such namespace\n", namespace); return rc; } int cmd_inject_error(int argc, const char **argv, struct ndctl_ctx *ctx) { const char * const u[] = { "ndctl inject-error []", NULL }; int i, rc; argc = parse_options(argc, argv, inject_options, u, 0); rc = inject_init(); if (rc) return rc; if (argc == 0) error("specify a namespace to inject error to\n"); for (i = 1; i < argc; i++) error("unknown extra parameter \"%s\"\n", argv[i]); if (argc == 0 || argc > 1) { usage_with_options(u, inject_options); return -ENODEV; /* we won't return from usage_with_options() */ } return do_inject(argv[0], ctx); } ndctl-67/ndctl/inject-smart.c000066400000000000000000000327441355562357700163200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct parameters { const char *bus; const char *dimm; bool verbose; bool human; const char *media_temperature; const char *ctrl_temperature; const char *spares; const char *media_temperature_threshold; const char *ctrl_temperature_threshold; const char *spares_threshold; const char *media_temperature_alarm; const char *ctrl_temperature_alarm; const char *spares_alarm; bool fatal; bool unsafe_shutdown; bool media_temperature_uninject; bool ctrl_temperature_uninject; bool spares_uninject; bool fatal_uninject; bool unsafe_shutdown_uninject; bool uninject_all; } param; static struct smart_ctx { bool alarms_present; bool err_continue; unsigned long op_mask; unsigned long flags; unsigned int media_temperature; unsigned int ctrl_temperature; unsigned long spares; unsigned int media_temperature_threshold; unsigned int ctrl_temperature_threshold; unsigned long spares_threshold; unsigned int media_temperature_alarm; unsigned int ctrl_temperature_alarm; unsigned long spares_alarm; } sctx; #define SMART_OPTIONS() \ OPT_STRING('b', "bus", ¶m.bus, "bus-id", \ "limit dimm to a bus with an id or provider of "), \ OPT_BOOLEAN('v', "verbose", ¶m.verbose, "emit extra debug messages to stderr"), \ OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats"), \ OPT_STRING('m', "media-temperature", ¶m.media_temperature, \ "smart media temperature attribute", \ "inject a value for smart media temperature"), \ OPT_STRING('M', "media-temperature-threshold", \ ¶m.media_temperature_threshold, \ "set smart media temperature threshold", \ "set threshold value for smart media temperature"), \ OPT_STRING('\0', "media-temperature-alarm", ¶m.media_temperature_alarm, \ "smart media temperature alarm", \ "enable or disable the smart media temperature alarm"), \ OPT_BOOLEAN('\0', "media-temperature-uninject", \ ¶m.media_temperature_uninject, "uninject media temperature"), \ OPT_STRING('c', "ctrl-temperature", ¶m.ctrl_temperature, \ "smart controller temperature attribute", \ "inject a value for smart controller temperature"), \ OPT_STRING('C', "ctrl-temperature-threshold", \ ¶m.ctrl_temperature_threshold, \ "set smart controller temperature threshold", \ "set threshold value for smart controller temperature"), \ OPT_STRING('\0', "ctrl-temperature-alarm", ¶m.ctrl_temperature_alarm, \ "smart controller temperature alarm", \ "enable or disable the smart controller temperature alarm"), \ OPT_BOOLEAN('\0', "ctrl-temperature-uninject", \ ¶m.ctrl_temperature_uninject, "uninject controller temperature"), \ OPT_STRING('s', "spares", ¶m.spares, \ "smart spares attribute", \ "inject a value for smart spares"), \ OPT_STRING('S', "spares-threshold", ¶m.spares_threshold, \ "set smart spares threshold", \ "set a threshold value for smart spares"), \ OPT_STRING('\0', "spares-alarm", ¶m.spares_alarm, \ "smart spares alarm", \ "enable or disable the smart spares alarm"), \ OPT_BOOLEAN('\0', "spares-uninject", \ ¶m.spares_uninject, "uninject spare percentage"), \ OPT_BOOLEAN('f', "fatal", ¶m.fatal, "inject fatal smart health status"), \ OPT_BOOLEAN('F', "fatal-uninject", \ ¶m.fatal_uninject, "uninject fatal health condition"), \ OPT_BOOLEAN('U', "unsafe-shutdown", ¶m.unsafe_shutdown, \ "inject smart unsafe shutdown status"), \ OPT_BOOLEAN('\0', "unsafe-shutdown-uninject", \ ¶m.unsafe_shutdown_uninject, "uninject unsafe shutdown status"), \ OPT_BOOLEAN('N', "uninject-all", \ ¶m.uninject_all, "uninject all possible fields") static const struct option smart_opts[] = { SMART_OPTIONS(), OPT_END(), }; enum smart_ops { OP_SET = 0, OP_INJECT, }; enum alarms { ALARM_ON = 1, ALARM_OFF, }; static inline void enable_set(void) { sctx.op_mask |= 1 << OP_SET; } static inline void enable_inject(void) { sctx.op_mask |= 1 << OP_INJECT; } #define smart_param_setup_uint(arg) \ { \ if (param.arg) { \ sctx.arg = strtoul(param.arg, NULL, 0); \ if (sctx.arg == ULONG_MAX || sctx.arg > UINT_MAX) { \ error("Invalid argument: %s: %s\n", #arg, param.arg); \ return -EINVAL; \ } \ enable_inject(); \ } \ if (param.arg##_threshold) { \ sctx.arg##_threshold = \ strtoul(param.arg##_threshold, NULL, 0); \ if (sctx.arg##_threshold == ULONG_MAX \ || sctx.arg##_threshold > UINT_MAX) { \ error("Invalid argument: %s\n", \ param.arg##_threshold); \ return -EINVAL; \ } \ enable_set(); \ } \ } #define smart_param_setup_temps(arg) \ { \ double temp; \ if (param.arg) { \ temp = strtod(param.arg, NULL); \ if (temp == HUGE_VAL || temp == -HUGE_VAL) { \ error("Invalid argument: %s: %s\n", #arg, param.arg); \ return -EINVAL; \ } \ sctx.arg = ndctl_encode_smart_temperature(temp); \ enable_inject(); \ } \ if (param.arg##_threshold) { \ temp = strtod(param.arg##_threshold, NULL); \ if (temp == HUGE_VAL || temp == -HUGE_VAL) { \ error("Invalid argument: %s\n", \ param.arg##_threshold); \ return -EINVAL; \ } \ sctx.arg##_threshold = ndctl_encode_smart_temperature(temp); \ enable_set(); \ } \ } #define smart_param_setup_alarm(arg) \ { \ if (param.arg##_alarm) { \ if (strncmp(param.arg##_alarm, "on", 2) == 0) \ sctx.arg##_alarm = ALARM_ON; \ else if (strncmp(param.arg##_alarm, "off", 3) == 0) \ sctx.arg##_alarm = ALARM_OFF; \ sctx.alarms_present = true; \ } \ } #define smart_param_setup_uninj(arg) \ { \ if (param.arg##_uninject) { \ /* Ensure user didn't set inject and uninject together */ \ if (param.arg) { \ error("Cannot use %s inject and uninject together\n", \ #arg); \ return -EINVAL; \ } \ /* Then set the inject flag so this can be accounted for */ \ param.arg = "0"; \ enable_inject(); \ } \ } static int smart_init(void) { if (param.human) sctx.flags |= UTIL_JSON_HUMAN; sctx.err_continue = false; /* setup attributes and thresholds except alarm_control */ smart_param_setup_temps(media_temperature) smart_param_setup_temps(ctrl_temperature) smart_param_setup_uint(spares) /* set up alarm_control */ smart_param_setup_alarm(media_temperature) smart_param_setup_alarm(ctrl_temperature) smart_param_setup_alarm(spares) if (sctx.alarms_present) enable_set(); /* setup remaining injection attributes */ if (param.fatal || param.unsafe_shutdown) enable_inject(); /* setup uninjections */ if (param.uninject_all) { param.media_temperature_uninject = true; param.ctrl_temperature_uninject = true; param.spares_uninject = true; param.fatal_uninject = true; param.unsafe_shutdown_uninject = true; sctx.err_continue = true; } smart_param_setup_uninj(media_temperature) smart_param_setup_uninj(ctrl_temperature) smart_param_setup_uninj(spares) smart_param_setup_uninj(fatal) smart_param_setup_uninj(unsafe_shutdown) if (sctx.op_mask == 0) { error("No valid operation specified\n"); return -EINVAL; } return 0; } #define setup_thresh_field(arg) \ { \ if (param.arg##_threshold) \ ndctl_cmd_smart_threshold_set_##arg(sst_cmd, \ sctx.arg##_threshold); \ } static int smart_set_thresh(struct ndctl_dimm *dimm) { const char *name = ndctl_dimm_get_devname(dimm); struct ndctl_cmd *st_cmd = NULL, *sst_cmd = NULL; int rc = -EOPNOTSUPP; st_cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); if (!st_cmd) { error("%s: no smart threshold command support\n", name); goto out; } rc = ndctl_cmd_submit_xlat(st_cmd); if (rc < 0) { error("%s: smart threshold command failed: %s (%d)\n", name, strerror(abs(rc)), rc); goto out; } sst_cmd = ndctl_dimm_cmd_new_smart_set_threshold(st_cmd); if (!sst_cmd) { error("%s: no smart set threshold command support\n", name); rc = -EOPNOTSUPP; goto out; } /* setup all thresholds except alarm_control */ setup_thresh_field(media_temperature) setup_thresh_field(ctrl_temperature) setup_thresh_field(spares) /* setup alarm_control manually */ if (sctx.alarms_present) { unsigned int alarm; alarm = ndctl_cmd_smart_threshold_get_alarm_control(st_cmd); if (sctx.media_temperature_alarm == ALARM_ON) alarm |= ND_SMART_TEMP_TRIP; else if (sctx.media_temperature_alarm == ALARM_OFF) alarm &= ~ND_SMART_TEMP_TRIP; if (sctx.ctrl_temperature_alarm == ALARM_ON) alarm |= ND_SMART_CTEMP_TRIP; else if (sctx.ctrl_temperature_alarm == ALARM_OFF) alarm &= ~ND_SMART_CTEMP_TRIP; if (sctx.spares_alarm == ALARM_ON) alarm |= ND_SMART_SPARE_TRIP; else if (sctx.spares_alarm == ALARM_OFF) alarm &= ~ND_SMART_SPARE_TRIP; ndctl_cmd_smart_threshold_set_alarm_control(sst_cmd, alarm); } rc = ndctl_cmd_submit_xlat(sst_cmd); if (rc < 0) error("%s: smart set threshold command failed: %s (%d)\n", name, strerror(abs(rc)), rc); out: ndctl_cmd_unref(sst_cmd); ndctl_cmd_unref(st_cmd); return rc; } #define send_inject_val(arg) \ { \ if (param.arg) { \ bool enable = true; \ \ si_cmd = ndctl_dimm_cmd_new_smart_inject(dimm); \ if (!si_cmd) { \ error("%s: no smart inject command support\n", name); \ if (sctx.err_continue == false) \ goto out; \ } \ if (param.arg##_uninject) \ enable = false; \ rc = ndctl_cmd_smart_inject_##arg(si_cmd, enable, sctx.arg); \ if (rc) { \ error("%s: smart inject %s cmd invalid: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ if (sctx.err_continue == false) \ goto out; \ } \ rc = ndctl_cmd_submit_xlat(si_cmd); \ if (rc < 0) { \ error("%s: smart inject %s command failed: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ if (sctx.err_continue == false) \ goto out; \ } \ ndctl_cmd_unref(si_cmd); \ } \ } #define send_inject_bool(arg) \ { \ if (param.arg) { \ bool enable = true; \ \ si_cmd = ndctl_dimm_cmd_new_smart_inject(dimm); \ if (!si_cmd) { \ error("%s: no smart inject command support\n", name); \ if (sctx.err_continue == false) \ goto out; \ } \ if (param.arg##_uninject) \ enable = false; \ rc = ndctl_cmd_smart_inject_##arg(si_cmd, enable); \ if (rc) { \ error("%s: smart inject %s cmd invalid: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ if (sctx.err_continue == false) \ goto out; \ } \ rc = ndctl_cmd_submit_xlat(si_cmd); \ if (rc < 0) { \ error("%s: smart inject %s command failed: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ if (sctx.err_continue == false) \ goto out; \ } \ ndctl_cmd_unref(si_cmd); \ } \ } static int smart_inject(struct ndctl_dimm *dimm) { const char *name = ndctl_dimm_get_devname(dimm); struct ndctl_cmd *si_cmd = NULL; int rc = -EOPNOTSUPP; send_inject_val(media_temperature) send_inject_val(ctrl_temperature) send_inject_val(spares) send_inject_bool(fatal) send_inject_bool(unsafe_shutdown) out: ndctl_cmd_unref(si_cmd); return rc; } static int dimm_inject_smart(struct ndctl_dimm *dimm) { struct json_object *jhealth; struct json_object *jdimms; struct json_object *jdimm; int rc; rc = ndctl_dimm_smart_inject_supported(dimm); switch (rc) { case -ENOTTY: error("%s: smart injection not supported by ndctl.", ndctl_dimm_get_devname(dimm)); return rc; case -EOPNOTSUPP: error("%s: smart injection not supported by the kernel", ndctl_dimm_get_devname(dimm)); return rc; case -EIO: error("%s: smart injection not supported by either platform firmware or the kernel.", ndctl_dimm_get_devname(dimm)); return rc; } if (sctx.op_mask & (1 << OP_SET)) { rc = smart_set_thresh(dimm); if (rc) goto out; } if (sctx.op_mask & (1 << OP_INJECT)) { rc = smart_inject(dimm); if (rc) goto out; } if (rc == 0) { jdimms = json_object_new_array(); if (!jdimms) goto out; jdimm = util_dimm_to_json(dimm, sctx.flags); if (!jdimm) goto out; json_object_array_add(jdimms, jdimm); jhealth = util_dimm_health_to_json(dimm); if (jhealth) { json_object_object_add(jdimm, "health", jhealth); util_display_json_array(stdout, jdimms, sctx.flags); } } out: return rc; } static int do_smart(const char *dimm_arg, struct ndctl_ctx *ctx) { struct ndctl_dimm *dimm; struct ndctl_bus *bus; int rc = -ENXIO; if (dimm_arg == NULL) return rc; if (param.verbose) ndctl_set_log_priority(ctx, LOG_DEBUG); ndctl_bus_foreach(ctx, bus) { if (!util_bus_filter(bus, param.bus)) continue; ndctl_dimm_foreach(bus, dimm) { if (!util_dimm_filter(dimm, dimm_arg)) continue; return dimm_inject_smart(dimm); } } error("%s: no such dimm\n", dimm_arg); return rc; } int cmd_inject_smart(int argc, const char **argv, struct ndctl_ctx *ctx) { const char * const u[] = { "ndctl inject-smart []", NULL }; int i, rc; argc = parse_options(argc, argv, smart_opts, u, 0); rc = smart_init(); if (rc) return rc; if (argc == 0) error("specify a dimm for the smart operation\n"); for (i = 1; i < argc; i++) error("unknown extra parameter \"%s\"\n", argv[i]); if (argc == 0 || argc > 1) { usage_with_options(u, smart_opts); return -ENODEV; /* we won't return from usage_with_options() */ } return do_smart(argv[0], ctx); } ndctl-67/ndctl/keys.readme000066400000000000000000000000441355562357700156720ustar00rootroot00000000000000See 'ndctl setup-passphrase --help' ndctl-67/ndctl/lib/000077500000000000000000000000001355562357700143105ustar00rootroot00000000000000ndctl-67/ndctl/lib/.gitignore000066400000000000000000000000701355562357700162750ustar00rootroot00000000000000.dirstamp .deps/ .libs/ *.la *.lo libabc.pc test-libabc ndctl-67/ndctl/lib/Makefile.am000066400000000000000000000015531355562357700163500ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in %.pc: %.pc.in Makefile $(SED_PROCESS) pkginclude_HEADERS = ../libndctl.h ../ndctl.h lib_LTLIBRARIES = libndctl.la libndctl_la_SOURCES =\ ../libndctl.h \ private.h \ ../../util/log.c \ ../../util/log.h \ ../../util/sysfs.c \ ../../util/sysfs.h \ dimm.c \ inject.c \ nfit.c \ smart.c \ intel.c \ hpe1.c \ msft.c \ hyperv.c \ ars.c \ firmware.c \ libndctl.c libndctl_la_LIBADD =\ ../../daxctl/lib/libdaxctl.la \ $(UDEV_LIBS) \ $(UUID_LIBS) \ $(KMOD_LIBS) EXTRA_DIST += libndctl.sym libndctl_la_LDFLAGS = $(AM_LDFLAGS) \ -version-info $(LIBNDCTL_CURRENT):$(LIBNDCTL_REVISION):$(LIBNDCTL_AGE) \ -Wl,--version-script=$(top_srcdir)/ndctl/lib/libndctl.sym libndctl_la_DEPENDENCIES = libndctl.sym pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libndctl.pc EXTRA_DIST += libndctl.pc.in CLEANFILES += libndctl.pc ndctl-67/ndctl/lib/ars.c000066400000000000000000000216661355562357700152540ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include "private.h" NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, unsigned long long address, unsigned long long len) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { dbg(ctx, "unsupported cmd\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap); cmd = calloc(1, size); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_ARS_CAP; cmd->size = size; cmd->status = 1; cmd->firmware_status = &cmd->ars_cap->status; cmd->ars_cap->address = address; cmd->ars_cap->length = len; return cmd; } static bool is_power_of_2(unsigned int v) { return v && ((v & (v - 1)) == 0); } static bool validate_clear_error(struct ndctl_cmd *ars_cap) { if (!is_power_of_2(ars_cap->ars_cap->clear_err_unit)) return false; return true; } static bool __validate_ars_cap(struct ndctl_cmd *ars_cap) { if (ars_cap->type != ND_CMD_ARS_CAP || ars_cap->status != 0) return false; if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) return false; return validate_clear_error(ars_cap); } #define validate_ars_cap(ctx, ars_cap) \ ({ \ bool __valid = __validate_ars_cap(ars_cap); \ if (!__valid) \ dbg(ctx, "expected sucessfully completed ars_cap command\n"); \ __valid; \ }) NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, int type) { struct ndctl_bus *bus = ars_cap->bus; struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (!validate_ars_cap(ctx, ars_cap)) return NULL; if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) { dbg(ctx, "ars_cap does not show requested type as supported\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start); cmd = calloc(1, size); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_ARS_START; cmd->size = size; cmd->status = 1; cmd->firmware_status = &cmd->ars_start->status; cmd->ars_start->address = ars_cap->ars_cap->address; cmd->ars_start->length = ars_cap->ars_cap->length; cmd->ars_start->type = type; return cmd; } NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap) { struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap; struct ndctl_bus *bus = ars_cap->bus; struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_STATUS)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (!validate_ars_cap(ctx, ars_cap)) return NULL; if (ars_cap_cmd->max_ars_out == 0) { dbg(ctx, "invalid ars_cap\n"); return NULL; } size = sizeof(*cmd) + ars_cap_cmd->max_ars_out; /* * Older kernels have a bug that miscalculates the output length of the * ars status and will overrun the provided buffer by 4 bytes, * corrupting the memory. Add an additional 4 bytes in the allocation * size to prevent that corruption. See kernel patch for more details: * * https://patchwork.kernel.org/patch/10563103/ */ cmd = calloc(1, size + 4); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_ARS_STATUS; cmd->size = size; cmd->status = 1; cmd->firmware_status = &cmd->ars_status->status; cmd->ars_status->out_length = ars_cap_cmd->max_ars_out; return cmd; } NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { dbg(ctx, "max_ars_out: %d\n", ars_cap->ars_cap->max_ars_out); return ars_cap->ars_cap->max_ars_out; } dbg(ctx, "invalid ars_cap\n"); return 0; } NDCTL_EXPORT int ndctl_cmd_ars_cap_get_range(struct ndctl_cmd *ars_cap, struct ndctl_range *range) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); if (range && ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { struct nd_cmd_ars_cap *cap = ars_cap->ars_cap; dbg(ctx, "range: %llx - %llx\n", cap->address, cap->length); range->address = cap->address; range->length = cap->length; return 0; } dbg(ctx, "invalid ars_cap\n"); return -EINVAL; } NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_clear_unit( struct ndctl_cmd *ars_cap) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { dbg(ctx, "clear_err_unit: %d\n", ars_cap->ars_cap->clear_err_unit); return ars_cap->ars_cap->clear_err_unit; } dbg(ctx, "invalid ars_cap\n"); return 0; } static bool __validate_ars_stat(struct ndctl_cmd *ars_stat) { /* * A positive status indicates an underrun, but that is fine since * the firmware is not expected to completely fill the max_ars_out * sized buffer. */ if (ars_stat->type != ND_CMD_ARS_STATUS || ars_stat->status < 0) return false; if ((ndctl_cmd_get_firmware_status(ars_stat) & ARS_STATUS_MASK) != 0) return false; return true; } #define validate_ars_stat(ctx, ars_stat) \ ({ \ bool __valid = __validate_ars_stat(ars_stat); \ if (!__valid) \ dbg(ctx, "expected sucessfully completed ars_stat command\n"); \ __valid; \ }) NDCTL_EXPORT int ndctl_cmd_ars_in_progress(struct ndctl_cmd *cmd) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(cmd)); if (!validate_ars_stat(ctx, cmd)) return 0; return (ndctl_cmd_get_firmware_status(cmd) == 1 << 16); } NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); if (!validate_ars_stat(ctx, ars_stat)) return 0; return ars_stat->ars_status->num_records; } NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr( struct ndctl_cmd *ars_stat, unsigned int rec_index) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); if (!validate_ars_stat(ctx, ars_stat)) return 0; if (rec_index >= ars_stat->ars_status->num_records) { dbg(ctx, "invalid record index\n"); return 0; } return ars_stat->ars_status->records[rec_index].err_address; } NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len( struct ndctl_cmd *ars_stat, unsigned int rec_index) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); if (!validate_ars_stat(ctx, ars_stat)) return 0; if (rec_index >= ars_stat->ars_status->num_records) { dbg(ctx, "invalid record index\n"); return 0; } return ars_stat->ars_status->records[rec_index].length; } NDCTL_EXPORT int ndctl_cmd_ars_stat_get_flag_overflow( struct ndctl_cmd *ars_stat) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); if (!validate_ars_stat(ctx, ars_stat)) return -EINVAL; return !!(ars_stat->ars_status->flags & ND_ARS_STAT_FLAG_OVERFLOW); } NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_clear_error( unsigned long long address, unsigned long long len, struct ndctl_cmd *ars_cap) { size_t size; unsigned int mask; struct nd_cmd_ars_cap *cap; struct ndctl_cmd *clear_err; struct ndctl_bus *bus = ars_cap->bus; struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_STATUS)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (!validate_ars_cap(ctx, ars_cap)) return NULL; cap = ars_cap->ars_cap; if (address < cap->address || address > (cap->address + cap->length) || address + len > (cap->address + cap->length)) { dbg(ctx, "request out of range (relative to ars_cap)\n"); return NULL; } mask = cap->clear_err_unit - 1; if ((address | len) & mask) { dbg(ctx, "request unaligned\n"); return NULL; } size = sizeof(*clear_err) + sizeof(struct nd_cmd_clear_error); clear_err = calloc(1, size); if (!clear_err) return NULL; ndctl_cmd_ref(clear_err); clear_err->bus = bus; clear_err->type = ND_CMD_CLEAR_ERROR; clear_err->size = size; clear_err->status = 1; clear_err->firmware_status = &clear_err->clear_err->status; clear_err->clear_err->address = address; clear_err->clear_err->length = len; return clear_err; } NDCTL_EXPORT unsigned long long ndctl_cmd_clear_error_get_cleared( struct ndctl_cmd *clear_err) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(clear_err)); if (clear_err->type == ND_CMD_CLEAR_ERROR && clear_err->status == 0) return clear_err->clear_err->cleared; dbg(ctx, "invalid clear_err\n"); return 0; } ndctl-67/ndctl/lib/dimm.c000066400000000000000000000530611355562357700154070ustar00rootroot00000000000000/* * Copyright (c) 2014-2017, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include "private.h" static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0"; /* * Note, best_seq(), inc_seq(), sizeof_namespace_index() * nvdimm_num_label_slots(), label_validate(), and label_write_index() * are copied from drivers/nvdimm/label.c in the Linux kernel with the * following modifications: * 1/ s,nd_,,gc * 2/ s,ndd->nsarea.config_size,ndd->config_size,gc * 3/ s,dev_dbg(dev,dbg(ctx,gc * 4/ s,__le,le,gc * 5/ s,__cpu_to,cpu_to,gc * 6/ remove flags argument to label_write_index * 7/ dropped clear_bit_le() usage in label_write_index * 8/ s,nvdimm_drvdata,nvdimm_data,gc */ static unsigned inc_seq(unsigned seq) { static const unsigned next[] = { 0, 2, 3, 1 }; return next[seq & 3]; } static u32 best_seq(u32 a, u32 b) { a &= NSINDEX_SEQ_MASK; b &= NSINDEX_SEQ_MASK; if (a == 0 || a == b) return b; else if (b == 0) return a; else if (inc_seq(a) == b) return b; else return a; } static struct ndctl_dimm *to_dimm(struct nvdimm_data *ndd) { return container_of(ndd, struct ndctl_dimm, ndd); } static unsigned int sizeof_namespace_label(struct nvdimm_data *ndd) { return ndctl_dimm_sizeof_namespace_label(to_dimm(ndd)); } static size_t __sizeof_namespace_index(u32 nslot) { return ALIGN(sizeof(struct namespace_index) + DIV_ROUND_UP(nslot, 8), NSINDEX_ALIGN); } static int __nvdimm_num_label_slots(struct nvdimm_data *ndd, size_t index_size) { return (ndd->config_size - index_size * 2) / sizeof_namespace_label(ndd); } static int nvdimm_num_label_slots(struct nvdimm_data *ndd) { u32 tmp_nslot, n; tmp_nslot = ndd->config_size / sizeof_namespace_label(ndd); n = __sizeof_namespace_index(tmp_nslot) / NSINDEX_ALIGN; return __nvdimm_num_label_slots(ndd, NSINDEX_ALIGN * n); } static unsigned int sizeof_namespace_index(struct nvdimm_data *ndd) { u32 nslot, space, size; struct ndctl_dimm *dimm = to_dimm(ndd); struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); /* * Per UEFI 2.7, the minimum size of the Label Storage Area is * large enough to hold 2 index blocks and 2 labels. The * minimum index block size is 256 bytes, and the minimum label * size is 256 bytes. */ nslot = nvdimm_num_label_slots(ndd); space = ndd->config_size - nslot * sizeof_namespace_label(ndd); size = __sizeof_namespace_index(nslot) * 2; if (size <= space && nslot >= 2) return size / 2; err(ctx, "%s: label area (%ld) too small to host (%d byte) labels\n", ndctl_dimm_get_devname(dimm), ndd->config_size, sizeof_namespace_label(ndd)); return 0; } static struct namespace_index *to_namespace_index(struct nvdimm_data *ndd, int i) { char *index; if (i < 0) return NULL; index = (char *) ndd->data + sizeof_namespace_index(ndd) * i; return (struct namespace_index *) index; } static int __label_validate(struct nvdimm_data *ndd) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(to_dimm(ndd)); /* * On media label format consists of two index blocks followed * by an array of labels. None of these structures are ever * updated in place. A sequence number tracks the current * active index and the next one to write, while labels are * written to free slots. * * +------------+ * | | * | nsindex0 | * | | * +------------+ * | | * | nsindex1 | * | | * +------------+ * | label0 | * +------------+ * | label1 | * +------------+ * | | * ....nslot... * | | * +------------+ * | labelN | * +------------+ */ struct namespace_index *nsindex[] = { to_namespace_index(ndd, 0), to_namespace_index(ndd, 1), }; const int num_index = ARRAY_SIZE(nsindex); bool valid[2] = { 0 }; int i, num_valid = 0; u32 seq; for (i = 0; i < num_index; i++) { u32 nslot; u8 sig[NSINDEX_SIG_LEN]; u64 sum_save, sum, size; unsigned int version, labelsize; memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN); if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) { dbg(ctx, "nsindex%d signature invalid\n", i); continue; } /* label sizes larger than 128 arrived with v1.2 */ version = le16_to_cpu(nsindex[i]->major) * 100 + le16_to_cpu(nsindex[i]->minor); if (version >= 102) labelsize = 1 << (7 + nsindex[i]->labelsize); else labelsize = 128; if (labelsize != sizeof_namespace_label(ndd)) { dbg(ctx, "nsindex%d labelsize %d invalid\n", i, nsindex[i]->labelsize); continue; } sum_save = le64_to_cpu(nsindex[i]->checksum); nsindex[i]->checksum = cpu_to_le64(0); sum = fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1); nsindex[i]->checksum = cpu_to_le64(sum_save); if (sum != sum_save) { dbg(ctx, "nsindex%d checksum invalid\n", i); continue; } seq = le32_to_cpu(nsindex[i]->seq); if ((seq & NSINDEX_SEQ_MASK) == 0) { dbg(ctx, "nsindex%d sequence: %#x invalid\n", i, seq); continue; } /* sanity check the index against expected values */ if (le64_to_cpu(nsindex[i]->myoff) != i * sizeof_namespace_index(ndd)) { dbg(ctx, "nsindex%d myoff: %#llx invalid\n", i, (unsigned long long) le64_to_cpu(nsindex[i]->myoff)); continue; } if (le64_to_cpu(nsindex[i]->otheroff) != (!i) * sizeof_namespace_index(ndd)) { dbg(ctx, "nsindex%d otheroff: %#llx invalid\n", i, (unsigned long long) le64_to_cpu(nsindex[i]->otheroff)); continue; } size = le64_to_cpu(nsindex[i]->mysize); if (size > sizeof_namespace_index(ndd) || size < sizeof(struct namespace_index)) { dbg(ctx, "nsindex%d mysize: %#zx invalid\n", i, size); continue; } nslot = le32_to_cpu(nsindex[i]->nslot); if (nslot * sizeof_namespace_label(ndd) + 2 * sizeof_namespace_index(ndd) > ndd->config_size) { dbg(ctx, "nsindex%d nslot: %u invalid, config_size: %#zx\n", i, nslot, ndd->config_size); continue; } valid[i] = true; num_valid++; } switch (num_valid) { case 0: break; case 1: for (i = 0; i < num_index; i++) if (valid[i]) return i; /* can't have num_valid > 0 but valid[] = { false, false } */ err(ctx, "unexpected index-block parse error\n"); break; default: /* pick the best index... */ seq = best_seq(le32_to_cpu(nsindex[0]->seq), le32_to_cpu(nsindex[1]->seq)); if (seq == (le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK)) return 1; else return 0; break; } return -EINVAL; } /* * If the dimm labels have not been previously validated this routine * will make up a default size. Otherwise, it will pick the size based * on what version is specified in the index block. */ NDCTL_EXPORT unsigned int ndctl_dimm_sizeof_namespace_label(struct ndctl_dimm *dimm) { struct nvdimm_data *ndd = &dimm->ndd; struct namespace_index nsindex; ssize_t offset, size; int v1 = 0, v2 = 0; if (ndd->nslabel_size) return ndd->nslabel_size; for (offset = 0; offset < NSINDEX_ALIGN * 2; offset += NSINDEX_ALIGN) { ssize_t len = (ssize_t) sizeof(nsindex); len = ndctl_cmd_cfg_read_get_data(ndd->cmd_read, &nsindex, len, offset); if (len < 0) break; /* * Since we're doing a best effort parsing we don't * fully validate the index block. Instead just assume * v1.1 unless there's 2 index blocks that say v1.2. */ if (le16_to_cpu(nsindex.major) == 1) { if (le16_to_cpu(nsindex.minor) == 1) v1++; else if (le16_to_cpu(nsindex.minor) == 2) v2++; } } if (v2 > v1) size = 256; else size = 128; ndd->nslabel_size = size; return size; } static int label_validate(struct nvdimm_data *ndd) { /* * In order to probe for and validate namespace index blocks we * need to know the size of the labels, and we can't trust the * size of the labels until we validate the index blocks. * Resolve this dependency loop by probing for known label * sizes, but default to v1.2 256-byte namespace labels if * discovery fails. */ int label_size[] = { 128, 256 }; int i, rc; for (i = 0; (size_t) i < ARRAY_SIZE(label_size); i++) { ndd->nslabel_size = label_size[i]; rc = __label_validate(ndd); if (rc >= 0) return nvdimm_num_label_slots(ndd); } return -EINVAL; } static int nvdimm_set_config_data(struct nvdimm_data *ndd, size_t offset, void *buf, size_t len) { struct ndctl_cmd *cmd_write; int rc; cmd_write = ndctl_dimm_cmd_new_cfg_write(ndd->cmd_read); if (!cmd_write) return -ENXIO; rc = ndctl_cmd_cfg_write_set_data(cmd_write, buf, len, offset); if (rc < 0) goto out; rc = ndctl_cmd_submit_xlat(cmd_write); if (rc < 0) rc = -ENXIO; out: ndctl_cmd_unref(cmd_write); return rc; } static int label_next_nsindex(int index) { if (index < 0) return -1; return (index + 1) % 2; } static struct namespace_label *label_base(struct nvdimm_data *ndd) { char *base = (char *) to_namespace_index(ndd, 0); base += 2 * sizeof_namespace_index(ndd); return (struct namespace_label *) base; } static void init_ndd(struct nvdimm_data *ndd, struct ndctl_cmd *cmd_read, struct ndctl_cmd *cmd_size) { ndctl_cmd_unref(ndd->cmd_read); memset(ndd, 0, sizeof(*ndd)); ndd->cmd_read = cmd_read; ndctl_cmd_ref(cmd_read); ndd->data = cmd_read->iter.total_buf; ndd->config_size = cmd_size->get_size->config_size; ndd->ns_current = -1; ndd->ns_next = -1; } static int write_label_index(struct ndctl_dimm *dimm, enum ndctl_namespace_version ver, unsigned index, unsigned seq) { struct nvdimm_data *ndd = &dimm->ndd; struct namespace_index *nsindex; unsigned long offset; u64 checksum; u32 nslot; /* * We may have initialized ndd to whatever labelsize is * currently on the dimm during label_validate(), so we reset it * to the desired version here. */ switch (ver) { case NDCTL_NS_VERSION_1_1: ndd->nslabel_size = 128; break; case NDCTL_NS_VERSION_1_2: ndd->nslabel_size = 256; break; default: return -EINVAL; } nsindex = to_namespace_index(ndd, index); nslot = nvdimm_num_label_slots(ndd); memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN); memset(nsindex->flags, 0, 3); nsindex->labelsize = sizeof_namespace_label(ndd) >> 8; nsindex->seq = cpu_to_le32(seq); offset = (unsigned long) nsindex - (unsigned long) to_namespace_index(ndd, 0); nsindex->myoff = cpu_to_le64(offset); nsindex->mysize = cpu_to_le64(sizeof_namespace_index(ndd)); offset = (unsigned long) to_namespace_index(ndd, label_next_nsindex(index)) - (unsigned long) to_namespace_index(ndd, 0); nsindex->otheroff = cpu_to_le64(offset); offset = (unsigned long) label_base(ndd) - (unsigned long) to_namespace_index(ndd, 0); nsindex->labeloff = cpu_to_le64(offset); nsindex->nslot = cpu_to_le32(nslot); nsindex->major = cpu_to_le16(1); if (sizeof_namespace_label(ndd) < 256) nsindex->minor = cpu_to_le16(1); else nsindex->minor = cpu_to_le16(2); nsindex->checksum = cpu_to_le64(0); /* init label bitmap */ memset(nsindex->free, 0xff, ALIGN(nslot, BITS_PER_LONG) / 8); checksum = fletcher64(nsindex, sizeof_namespace_index(ndd), 1); nsindex->checksum = cpu_to_le64(checksum); return nvdimm_set_config_data(ndd, le64_to_cpu(nsindex->myoff), nsindex, sizeof_namespace_index(ndd)); } NDCTL_EXPORT int ndctl_dimm_init_labels(struct ndctl_dimm *dimm, enum ndctl_namespace_version v) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); struct nvdimm_data *ndd = &dimm->ndd; struct ndctl_region *region; int i; if (!ndd->cmd_read) { err(ctx, "%s: needs to be initialized by ndctl_dimm_read_labels\n", ndctl_dimm_get_devname(dimm)); return -EINVAL; } ndctl_region_foreach(bus, region) { struct ndctl_dimm *match; ndctl_dimm_foreach_in_region(region, match) if (match == dimm) { region_flag_refresh(region); break; } } for (i = 0; i < 2; i++) { int rc; rc = write_label_index(dimm, v, i, 3 - i); if (rc < 0) return rc; } return nvdimm_num_label_slots(ndd); } NDCTL_EXPORT int ndctl_dimm_validate_labels(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); struct nvdimm_data *ndd = &dimm->ndd; if (!ndd->cmd_read) { err(ctx, "%s: needs to be initialized by ndctl_dimm_read_labels\n", ndctl_dimm_get_devname(dimm)); return -EINVAL; } return label_validate(&dimm->ndd); } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_read_label_index(struct ndctl_dimm *dimm) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_cmd *cmd_size, *cmd_read; struct nvdimm_data *ndd = &dimm->ndd; int rc; rc = ndctl_bus_wait_probe(bus); if (rc < 0) return NULL; cmd_size = ndctl_dimm_cmd_new_cfg_size(dimm); if (!cmd_size) return NULL; rc = ndctl_cmd_submit_xlat(cmd_size); if (rc < 0) goto out_size; cmd_read = ndctl_dimm_cmd_new_cfg_read(cmd_size); if (!cmd_read) goto out_size; /* * To calc the namespace index size use the minimum label * size which corresponds to the maximum namespace index size. */ init_ndd(ndd, cmd_read, cmd_size); ndd->nslabel_size = 128; rc = ndctl_cmd_cfg_read_set_extent(cmd_read, sizeof_namespace_index(ndd) * 2, 0); if (rc < 0) goto out_read; rc = ndctl_cmd_submit_xlat(cmd_read); if (rc < 0) goto out_read; ndctl_cmd_unref(cmd_size); return cmd_read; out_read: ndctl_cmd_unref(cmd_read); out_size: ndctl_cmd_unref(cmd_size); return NULL; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_read_label_extent( struct ndctl_dimm *dimm, unsigned int len, unsigned int offset) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_cmd *cmd_size, *cmd_read; int rc; rc = ndctl_bus_wait_probe(bus); if (rc < 0) return NULL; cmd_size = ndctl_dimm_cmd_new_cfg_size(dimm); if (!cmd_size) return NULL; rc = ndctl_cmd_submit_xlat(cmd_size); if (rc < 0) goto out_size; cmd_read = ndctl_dimm_cmd_new_cfg_read(cmd_size); if (!cmd_read) goto out_size; /* * For ndctl_read_labels() compat, enable subsequent calls that * will manipulate labels */ if (len == 0 && offset == 0) init_ndd(&dimm->ndd, cmd_read, cmd_size); if (len == 0) len = cmd_size->get_size->config_size; rc = ndctl_cmd_cfg_read_set_extent(cmd_read, len, offset); if (rc < 0) goto out_read; rc = ndctl_cmd_submit_xlat(cmd_read); if (rc < 0) goto out_read; ndctl_cmd_unref(cmd_size); return cmd_read; out_read: ndctl_cmd_unref(cmd_read); out_size: ndctl_cmd_unref(cmd_size); return NULL; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_read_labels(struct ndctl_dimm *dimm) { return ndctl_dimm_read_label_extent(dimm, 0, 0); } NDCTL_EXPORT int ndctl_dimm_zero_label_extent(struct ndctl_dimm *dimm, unsigned int len, unsigned int offset) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); struct ndctl_cmd *cmd_read, *cmd_write; int rc; cmd_read = ndctl_dimm_read_label_extent(dimm, len, offset); if (!cmd_read) return -EINVAL; if (ndctl_dimm_is_active(dimm)) { dbg(ctx, "%s: regions active, abort label write\n", ndctl_dimm_get_devname(dimm)); rc = -EBUSY; goto out_read; } cmd_write = ndctl_dimm_cmd_new_cfg_write(cmd_read); if (!cmd_write) { rc = -ENOTTY; goto out_read; } if (ndctl_cmd_cfg_write_zero_data(cmd_write) < 0) { rc = -ENXIO; goto out_write; } rc = ndctl_cmd_submit_xlat(cmd_write); if (rc < 0) goto out_write; /* * If the dimm is already disabled the kernel is not holding a cached * copy of the label space. */ if (!ndctl_dimm_is_enabled(dimm)) goto out_write; rc = ndctl_dimm_disable(dimm); if (rc) goto out_write; rc = ndctl_dimm_enable(dimm); out_write: ndctl_cmd_unref(cmd_write); out_read: ndctl_cmd_unref(cmd_read); return rc; } NDCTL_EXPORT int ndctl_dimm_zero_labels(struct ndctl_dimm *dimm) { return ndctl_dimm_zero_label_extent(dimm, 0, 0); } NDCTL_EXPORT unsigned long ndctl_dimm_get_available_labels( struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); char *path = dimm->dimm_buf; int rc, len = dimm->buf_len; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/available_slots", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); errno = ENOMEM; return ULONG_MAX; } rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) { errno = -rc; return ULONG_MAX; } return strtoul(buf, NULL, 0); } NDCTL_EXPORT enum ndctl_security_state ndctl_dimm_get_security( struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); char *path = dimm->dimm_buf; char buf[SYSFS_ATTR_SIZE]; int len = dimm->buf_len; int rc; if (snprintf(path, len, "%s/security", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return NDCTL_SECURITY_INVALID; } rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) return NDCTL_SECURITY_INVALID; if (strcmp(buf, "disabled") == 0) return NDCTL_SECURITY_DISABLED; else if (strcmp(buf, "unlocked") == 0) return NDCTL_SECURITY_UNLOCKED; else if (strcmp(buf, "locked") == 0) return NDCTL_SECURITY_LOCKED; else if (strcmp(buf, "frozen") == 0) return NDCTL_SECURITY_FROZEN; else if (strcmp(buf, "overwrite") == 0) return NDCTL_SECURITY_OVERWRITE; return NDCTL_SECURITY_INVALID; } NDCTL_EXPORT bool ndctl_dimm_security_is_frozen(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); char *path = dimm->dimm_buf; char buf[SYSFS_ATTR_SIZE]; int len = dimm->buf_len; int rc; if (ndctl_dimm_get_security(dimm) == NDCTL_SECURITY_FROZEN) return true; if (snprintf(path, len, "%s/frozen", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return false; } rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) return false; return !!strtoul(buf, NULL, 0); } static int write_security(struct ndctl_dimm *dimm, const char *cmd) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); char *path = dimm->dimm_buf; int len = dimm->buf_len; if (snprintf(path, len, "%s/security", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return -ERANGE; } return sysfs_write_attr(ctx, path, cmd); } NDCTL_EXPORT int ndctl_dimm_update_passphrase(struct ndctl_dimm *dimm, long ckey, long nkey) { char buf[SYSFS_ATTR_SIZE]; sprintf(buf, "update %ld %ld\n", ckey, nkey); return write_security(dimm, buf); } NDCTL_EXPORT int ndctl_dimm_disable_passphrase(struct ndctl_dimm *dimm, long key) { char buf[SYSFS_ATTR_SIZE]; sprintf(buf, "disable %ld\n", key); return write_security(dimm, buf); } NDCTL_EXPORT int ndctl_dimm_freeze_security(struct ndctl_dimm *dimm) { return write_security(dimm, "freeze"); } NDCTL_EXPORT int ndctl_dimm_secure_erase(struct ndctl_dimm *dimm, long key) { char buf[SYSFS_ATTR_SIZE]; sprintf(buf, "erase %ld\n", key); return write_security(dimm, buf); } NDCTL_EXPORT int ndctl_dimm_overwrite(struct ndctl_dimm *dimm, long key) { char buf[SYSFS_ATTR_SIZE]; sprintf(buf, "overwrite %ld\n", key); return write_security(dimm, buf); } NDCTL_EXPORT int ndctl_dimm_wait_overwrite(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); struct pollfd fds; char buf[SYSFS_ATTR_SIZE]; int fd = 0, rc; char *path = dimm->dimm_buf; int len = dimm->buf_len; if (snprintf(path, len, "%s/security", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return -ERANGE; } fd = open(path, O_RDONLY|O_CLOEXEC); if (fd < 0) { rc = -errno; err(ctx, "open: %s\n", strerror(errno)); return rc; } memset(&fds, 0, sizeof(fds)); fds.fd = fd; rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) { rc = -EOPNOTSUPP; goto out; } /* skipping if we aren't in overwrite state */ if (strcmp(buf, "overwrite") != 0) { rc = 0; goto out; } for (;;) { rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) { rc = -EOPNOTSUPP; break; } if (strncmp(buf, "overwrite", 9) == 0) { rc = poll(&fds, 1, -1); if (rc < 0) { rc = -errno; err(ctx, "poll error: %s\n", strerror(errno)); break; } dbg(ctx, "poll wake: revents: %d\n", fds.revents); if (pread(fd, buf, 1, 0) == -1) { rc = -errno; break; } fds.revents = 0; } else { if (strncmp(buf, "disabled", 8) == 0) rc = 1; break; } } if (rc == 1) dbg(ctx, "%s: overwrite complete\n", ndctl_dimm_get_devname(dimm)); else if (rc == 0) dbg(ctx, "%s: ovewrite skipped\n", ndctl_dimm_get_devname(dimm)); else dbg(ctx, "%s: overwrite error waiting for complete\n", ndctl_dimm_get_devname(dimm)); out: close(fd); return rc; } NDCTL_EXPORT int ndctl_dimm_update_master_passphrase(struct ndctl_dimm *dimm, long ckey, long nkey) { char buf[SYSFS_ATTR_SIZE]; sprintf(buf, "master_update %ld %ld\n", ckey, nkey); return write_security(dimm, buf); } NDCTL_EXPORT int ndctl_dimm_master_secure_erase(struct ndctl_dimm *dimm, long key) { char buf[SYSFS_ATTR_SIZE]; sprintf(buf, "master_erase %ld\n", key); return write_security(dimm, buf); } ndctl-67/ndctl/lib/firmware.c000066400000000000000000000056551355562357700163030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include "private.h" /* * Define the wrappers around the ndctl_dimm_ops for firmware update: */ NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_fw_get_info(struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->new_fw_get_info) return ops->new_fw_get_info(dimm); else return NULL; } NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_fw_start_update(struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->new_fw_start_update) return ops->new_fw_start_update(dimm); else return NULL; } NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_fw_send(struct ndctl_cmd *start, unsigned int offset, unsigned int len, void *data) { struct ndctl_dimm_ops *ops = start->dimm->ops; if (ops && ops->new_fw_send) return ops->new_fw_send(start, offset, len, data); else return NULL; } NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_fw_finish(struct ndctl_cmd *start) { struct ndctl_dimm_ops *ops = start->dimm->ops; if (ops && ops->new_fw_finish) return ops->new_fw_finish(start); else return NULL; } NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_fw_abort(struct ndctl_cmd *start) { struct ndctl_dimm_ops *ops = start->dimm->ops; if (ops && ops->new_fw_finish) return ops->new_fw_abort(start); else return NULL; } NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_fw_finish_query(struct ndctl_cmd *start) { struct ndctl_dimm_ops *ops = start->dimm->ops; if (ops && ops->new_fw_finish_query) return ops->new_fw_finish_query(start); else return NULL; } #define firmware_cmd_op(op, rettype, defretvalue) \ NDCTL_EXPORT rettype ndctl_cmd_##op(struct ndctl_cmd *cmd) \ { \ if (cmd->dimm) { \ struct ndctl_dimm_ops *ops = cmd->dimm->ops; \ if (ops && ops->op) \ return ops->op(cmd); \ } \ return defretvalue; \ } firmware_cmd_op(fw_info_get_storage_size, unsigned int, 0) firmware_cmd_op(fw_info_get_max_send_len, unsigned int, 0) firmware_cmd_op(fw_info_get_query_interval, unsigned int, 0) firmware_cmd_op(fw_info_get_max_query_time, unsigned int, 0) firmware_cmd_op(fw_info_get_run_version, unsigned long long, 0) firmware_cmd_op(fw_info_get_updated_version, unsigned long long, 0) firmware_cmd_op(fw_start_get_context, unsigned int, 0) firmware_cmd_op(fw_fquery_get_fw_rev, unsigned long long, 0) NDCTL_EXPORT enum ND_FW_STATUS ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd) { struct ndctl_dimm_ops *ops = cmd->dimm->ops; if (ops && ops->fw_xlat_firmware_status) return ops->fw_xlat_firmware_status(cmd); else return FW_EUNKNOWN; } NDCTL_EXPORT int ndctl_dimm_fw_update_supported(struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->fw_update_supported) return ops->fw_update_supported(dimm); else return -ENOTTY; } ndctl-67/ndctl/lib/hpe1.c000066400000000000000000000221351355562357700153140ustar00rootroot00000000000000/* * Copyright (C) 2016 Hewlett Packard Enterprise Development LP * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include "private.h" #include "hpe1.h" #define CMD_HPE1(_c) ((_c)->hpe1) #define CMD_HPE1_SMART(_c) (CMD_HPE1(_c)->u.smart.data) #define CMD_HPE1_SMART_THRESH(_c) (CMD_HPE1(_c)->u.thresh.data) static struct ndctl_cmd *hpe1_dimm_cmd_new_smart(struct ndctl_dimm *dimm) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; struct ndn_pkg_hpe1 *hpe1; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (test_dimm_dsm(dimm, NDN_HPE1_CMD_SMART) == DIMM_DSM_UNSUPPORTED) { dbg(ctx, "unsupported function\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1); cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; hpe1 = CMD_HPE1(cmd); hpe1->gen.nd_family = NVDIMM_FAMILY_HPE1; hpe1->gen.nd_command = NDN_HPE1_CMD_SMART; hpe1->gen.nd_fw_size = 0; hpe1->gen.nd_size_in = offsetof(struct ndn_hpe1_smart, status); hpe1->gen.nd_size_out = sizeof(hpe1->u.smart); hpe1->u.smart.status = 3; hpe1->u.smart.in_valid_flags = 0; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_HEALTH_VALID; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_TEMP_VALID; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_SPARES_VALID; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_ALARM_VALID; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_USED_VALID; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_SHUTDOWN_VALID; hpe1->u.smart.in_valid_flags |= NDN_HPE1_SMART_VENDOR_VALID; cmd->firmware_status = &hpe1->u.smart.status; return cmd; } static int hpe1_smart_valid(struct ndctl_cmd *cmd) { if (cmd->type != ND_CMD_CALL || cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1) || CMD_HPE1(cmd)->gen.nd_family != NVDIMM_FAMILY_HPE1 || CMD_HPE1(cmd)->gen.nd_command != NDN_HPE1_CMD_SMART || cmd->status != 0) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } static unsigned int hpe1_cmd_smart_get_flags(struct ndctl_cmd *cmd) { unsigned int hpe1flags; unsigned int flags; int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } hpe1flags = CMD_HPE1_SMART(cmd)->out_valid_flags; flags = 0; if (hpe1flags & NDN_HPE1_SMART_HEALTH_VALID) flags |= ND_SMART_HEALTH_VALID; if (hpe1flags & NDN_HPE1_SMART_TEMP_VALID) flags |= ND_SMART_TEMP_VALID ; if (hpe1flags & NDN_HPE1_SMART_SPARES_VALID) flags |= ND_SMART_SPARES_VALID; if (hpe1flags & NDN_HPE1_SMART_ALARM_VALID) flags |= ND_SMART_ALARM_VALID; if (hpe1flags & NDN_HPE1_SMART_USED_VALID) flags |= ND_SMART_USED_VALID; if (hpe1flags & NDN_HPE1_SMART_SHUTDOWN_VALID) flags |= ND_SMART_SHUTDOWN_VALID; if (hpe1flags & NDN_HPE1_SMART_VENDOR_VALID) flags |= ND_SMART_VENDOR_VALID; return flags; } static unsigned int hpe1_cmd_smart_get_health(struct ndctl_cmd *cmd) { unsigned char hpe1health; unsigned int health; int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } hpe1health = CMD_HPE1_SMART(cmd)->stat_summary; health = 0; if (hpe1health & NDN_HPE1_SMART_NONCRIT_HEALTH) health |= ND_SMART_NON_CRITICAL_HEALTH;; if (hpe1health & NDN_HPE1_SMART_CRITICAL_HEALTH) health |= ND_SMART_CRITICAL_HEALTH; if (hpe1health & NDN_HPE1_SMART_FATAL_HEALTH) health |= ND_SMART_FATAL_HEALTH; return health; } static unsigned int hpe1_cmd_smart_get_media_temperature(struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_HPE1_SMART(cmd)->curr_temp; } static unsigned int hpe1_cmd_smart_get_spares(struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_HPE1_SMART(cmd)->spare_blocks; } static unsigned int hpe1_cmd_smart_get_alarm_flags(struct ndctl_cmd *cmd) { unsigned int hpe1flags; unsigned int flags; int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } hpe1flags = CMD_HPE1_SMART(cmd)->alarm_trips; flags = 0; if (hpe1flags & NDN_HPE1_SMART_TEMP_TRIP) flags |= ND_SMART_TEMP_TRIP; if (hpe1flags & NDN_HPE1_SMART_SPARE_TRIP) flags |= ND_SMART_SPARE_TRIP; return flags; } static unsigned int hpe1_cmd_smart_get_life_used(struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_HPE1_SMART(cmd)->device_life; } static unsigned int hpe1_cmd_smart_get_shutdown_state(struct ndctl_cmd *cmd) { unsigned int shutdown; int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } shutdown = CMD_HPE1_SMART(cmd)->last_shutdown_stat; if (shutdown == NDN_HPE1_SMART_LASTSAVEGOOD) return 0; else return 1; } static unsigned int hpe1_cmd_smart_get_vendor_size(struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_HPE1_SMART(cmd)->vndr_spec_data_size; } static unsigned char *hpe1_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_valid(cmd); if (rc < 0) { errno = -rc; return NULL; } return CMD_HPE1_SMART(cmd)->vnd_spec_data; } static struct ndctl_cmd *hpe1_dimm_cmd_new_smart_threshold(struct ndctl_dimm *dimm) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; struct ndn_pkg_hpe1 *hpe1; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (test_dimm_dsm(dimm, NDN_HPE1_CMD_SMART_THRESHOLD) == DIMM_DSM_UNSUPPORTED) { dbg(ctx, "unsupported function\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1); cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; hpe1 = CMD_HPE1(cmd); hpe1->gen.nd_family = NVDIMM_FAMILY_HPE1; hpe1->gen.nd_command = NDN_HPE1_CMD_SMART_THRESHOLD; hpe1->gen.nd_fw_size = 0; hpe1->gen.nd_size_in = offsetof(struct ndn_hpe1_smart_threshold, status); hpe1->gen.nd_size_out = sizeof(hpe1->u.smart); hpe1->u.thresh.status = 3; cmd->firmware_status = &hpe1->u.thresh.status; return cmd; } static int hpe1_smart_threshold_valid(struct ndctl_cmd *cmd) { if (cmd->type != ND_CMD_CALL || cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_hpe1) || CMD_HPE1(cmd)->gen.nd_family != NVDIMM_FAMILY_HPE1 || CMD_HPE1(cmd)->gen.nd_command != NDN_HPE1_CMD_SMART_THRESHOLD || cmd->status != 0) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } static unsigned int hpe1_cmd_smart_threshold_get_alarm_control(struct ndctl_cmd *cmd) { unsigned int hpe1flags; unsigned int flags; int rc; rc = hpe1_smart_threshold_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } hpe1flags = CMD_HPE1_SMART_THRESH(cmd)->threshold_alarm_ctl; flags = 0; if (hpe1flags & NDN_HPE1_SMART_TEMP_TRIP) flags |= ND_SMART_TEMP_TRIP; if (hpe1flags & NDN_HPE1_SMART_SPARE_TRIP) flags |= ND_SMART_SPARE_TRIP; return flags; } static unsigned int hpe1_cmd_smart_threshold_get_media_temperature( struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_threshold_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_HPE1_SMART_THRESH(cmd)->temp_threshold; } static unsigned int hpe1_cmd_smart_threshold_get_spares(struct ndctl_cmd *cmd) { int rc; rc = hpe1_smart_threshold_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_HPE1_SMART_THRESH(cmd)->spare_block_threshold; } struct ndctl_dimm_ops * const hpe1_dimm_ops = &(struct ndctl_dimm_ops) { .new_smart = hpe1_dimm_cmd_new_smart, .smart_get_flags = hpe1_cmd_smart_get_flags, .smart_get_health = hpe1_cmd_smart_get_health, .smart_get_media_temperature = hpe1_cmd_smart_get_media_temperature, .smart_get_spares = hpe1_cmd_smart_get_spares, .smart_get_alarm_flags = hpe1_cmd_smart_get_alarm_flags, .smart_get_life_used = hpe1_cmd_smart_get_life_used, .smart_get_shutdown_state = hpe1_cmd_smart_get_shutdown_state, .smart_get_vendor_size = hpe1_cmd_smart_get_vendor_size, .smart_get_vendor_data = hpe1_cmd_smart_get_vendor_data, .new_smart_threshold = hpe1_dimm_cmd_new_smart_threshold, .smart_threshold_get_alarm_control = hpe1_cmd_smart_threshold_get_alarm_control, .smart_threshold_get_media_temperature = hpe1_cmd_smart_threshold_get_media_temperature, .smart_threshold_get_spares = hpe1_cmd_smart_threshold_get_spares, }; ndctl-67/ndctl/lib/hpe1.h000066400000000000000000000222301355562357700153150ustar00rootroot00000000000000/* * Copyright (C) 2016 Hewlett Packard Enterprise Development LP * Copyright (c) 2014-2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __NDCTL_HPE1_H__ #define __NDCTL_HPE1_H__ enum { NDN_HPE1_CMD_QUERY = 0, /* non-root commands */ NDN_HPE1_CMD_SMART = 1, NDN_HPE1_CMD_SMART_THRESHOLD = 2, NDN_HPE1_CMD_GET_CONFIG_SIZE = 4, NDN_HPE1_CMD_GET_CONFIG_DATA = 5, NDN_HPE1_CMD_SET_CONFIG_DATA = 6, NDN_HPE1_CMD_GET_IDENT = 10, NDN_HPE1_CMD_GET_ES_IDENT = 11, NDN_HPE1_CMD_GET_LAST_BACKUP = 12, NDN_HPE1_CMD_SET_LIFE_THRESHOLD = 13, NDN_HPE1_CMD_ERRINJ_QUERY = 18, NDN_HPE1_CMD_ERRINJ_INJECT = 19, NDN_HPE1_CMD_ERRINJ_STATUS = 20, }; /* NDN_HPE1_CMD_SMART */ /* ndn_hpe1_smart.in_valid_flags / ndn_hpe1_smart_data.out_valid_flags */ #define NDN_HPE1_SMART_HEALTH_VALID (1 << 0) #define NDN_HPE1_SMART_TEMP_VALID (1 << 1) #define NDN_HPE1_SMART_SPARES_VALID (1 << 2) #define NDN_HPE1_SMART_ALARM_VALID (1 << 3) #define NDN_HPE1_SMART_USED_VALID (1 << 4) #define NDN_HPE1_SMART_SHUTDOWN_VALID (1 << 5) #define NDN_HPE1_SMART_STATS_VALID (1 << 6) #define NDN_HPE1_SMART_DETAIL_VALID (1 << 7) #define NDN_HPE1_SMART_ENERGY_VALID (1 << 8) #define NDN_HPE1_SMART_VENDOR_VALID (1 << 9) #define NDN_HPE1_SMART_NOTIFIED (1 << 31) /* ndn_hpe1_smart_data.stat_summary */ #define NDN_HPE1_SMART_NONCRIT_HEALTH (1 << 0) #define NDN_HPE1_SMART_CRITICAL_HEALTH (1 << 1) #define NDN_HPE1_SMART_FATAL_HEALTH (1 << 2) /* ndn_hpe1_smart_data.alarm_trips */ #define NDN_HPE1_SMART_TEMP_TRIP (1 << 0) #define NDN_HPE1_SMART_SPARE_TRIP (1 << 1) #define NDN_HPE1_SMART_LIFEWARN_TRIP (1 << 2) #define NDN_HPE1_SMART_LIFEERR_TRIP (1 << 3) #define NDN_HPE1_SMART_ESLIFEWARN_TRIP (1 << 4) #define NDN_HPE1_SMART_ESLIFEERR_TRIP (1 << 5) #define NDN_HPE1_SMART_ESTEMPWARN_TRIP (1 << 6) #define NDN_HPE1_SMART_ESTEMPERR_TRIP (1 << 7) /* ndn_hpe1_smart_data.last_shutdown_stat */ #define NDN_HPE1_SMART_LASTSAVEGOOD (1 << 1) /* ndn_hpe1_smart_data.mod_hlth_stat */ #define NDN_HPE1_SMART_ES_FAILURE (1 << 0) #define NDN_HPE1_SMART_CTLR_FAILURE (1 << 1) #define NDN_HPE1_SMART_UE_TRIP (1 << 2) #define NDN_HPE1_SMART_CE_TRIP (1 << 3) #define NDN_HPE1_SMART_SAVE_FAILED (1 << 4) #define NDN_HPE1_SMART_RESTORE_FAILED (1 << 5) #define NDN_HPE1_SMART_ARM_FAILED (1 << 6) #define NDN_HPE1_SMART_ERASE_FAILED (1 << 7) #define NDN_HPE1_SMART_CONFIG_ERROR (1 << 8) #define NDN_HPE1_SMART_FW_ERROR (1 << 9) #define NDN_HPE1_SMART_VENDOR_ERROR (1 << 10) struct ndn_hpe1_smart_data { __u32 out_valid_flags; __u8 stat_summary; __u16 curr_temp; __u8 spare_blocks; __u16 alarm_trips; __u8 device_life; __u8 last_shutdown_stat; __u16 last_save_op_dur; __u16 last_restore_op_dur; __u16 last_erase_op_dur; __u16 res1; __u32 save_ops; __u32 restore_ops; __u32 erase_ops; __u32 life_save_ops; __u32 life_restore_ops; __u32 life_erase_ops; __u32 life_mod_pwr_cycles; __u32 mod_hlth_stat; __u32 energy_src_check; __u8 energy_src_life_percent; __u16 energy_src_curr_temp; __u8 res2; __u16 energy_src_total_runtime; __u16 vndr_spec_data_size; __u8 vnd_spec_data[60]; } __attribute__((packed)); struct ndn_hpe1_smart { __u32 in_valid_flags; __u32 status; union { __u8 buf[124]; struct ndn_hpe1_smart_data data[0]; }; } __attribute__((packed)); /* NDN_HPE1_CMD_SMART_THRESHOLD */ struct ndn_hpe1_smart_threshold_data { __u16 threshold_alarm_ctl; __u16 temp_threshold; __u8 spare_block_threshold; __u8 res1[3]; __u8 dev_lifewarn_threshold; __u8 dev_lifeerr_threshold; __u8 res2[6]; __u8 es_lifewarn_threshold; __u8 es_lifeerr_threshold; __u8 es_tempwarn_threshold; __u8 es_temperr_threshold; __u8 res3[4]; __u64 res4; } __attribute__((packed)); struct ndn_hpe1_smart_threshold { __u32 status; union { __u8 buf[32]; struct ndn_hpe1_smart_threshold_data data[0]; }; } __attribute__((packed)); /* NDN_HPE1_CMD_GET_CONFIG_SIZE */ struct ndn_hpe1_get_config_size { __u32 status; __u32 config_size; __u32 max_xfer; } __attribute__((packed)); /* NDN_HPE1_CMD_GET_CONFIG_DATA */ struct ndn_hpe1_get_config_data_hdr { __u32 in_offset; __u32 in_length; __u32 status; __u8 out_buf[0]; } __attribute__((packed)); /* NDN_HPE1_CMD_SET_CONFIG_DATA */ struct ndn_hpe1_set_config_hdr { __u32 in_offset; __u32 in_length; __u8 in_buf[0]; } __attribute__((packed)); /* ndn_hpe1_get_id.sup_backup_trigger */ #define NDN_HPE1_BKUP_SUPPORT_CKE (1 << 0) #define NDN_HPE1_BKUP_SUPPORT_EXTERNAL (1 << 1) #define NDN_HPE1_BKUP_SUPPORT_12V (1 << 2) #define NDN_HPE1_BKUP_SUPPORT_I2C (1 << 3) #define NDN_HPE1_BKUP_SUPPORT_SAVEN (1 << 4) /* NDN_HPE1_CMD_GET_IDENT */ struct ndn_hpe1_get_id { __u32 status; __u8 spec_rev; __u8 num_stnd_pages; __u32 hw_rev; __u8 sup_backup_trigger; __u16 max_op_retries; __u8 __res1[3]; __u32 backup_op_timeout; __u32 restore_op_timeout; __u32 erase_op_timeout; __u32 arm_op_timeout; __u32 fw_op_timeout; __u32 region_block_size; __u16 min_op_temp; __u16 max_op_temp; __u8 curr_fw_slot; __u8 res2[1]; __u16 num_fw_slots; __u8 fw_slot_revision[0]; } __attribute__((packed)); /* ndn_hpe1_get_energy_src_id.attr */ #define NDN_HPE1_ES_ATTR_BUILTIN (1 << 0) #define NDN_HPE1_ES_ATTR_TETHERED (1 << 1) #define NDN_HPE1_ES_ATTR_SHARED (1 << 2) /* ndn_hpe1_get_energy_src_id.tech */ #define NDN_HPE1_ES_TECH_UNDEFINED (1 << 0) #define NDN_HPE1_ES_TECH_SUPERCAP (1 << 1) #define NDN_HPE1_ES_TECH_BATTERY (1 << 2) #define NDN_HPE1_ES_TECH_HYBRIDCAP (1 << 3) /* NDN_HPE1_CMD_GET_ES_IDENT */ struct ndn_hpe1_get_energy_src_id { __u32 status; __u8 energy_src_policy; __u8 attr; __u8 tech; __u8 reserved; __u16 hw_rev; __u16 fw_rev; __u32 charge_timeout; __u16 min_op_temp; __u16 max_op_temp; } __attribute__((packed)); /* ndn_hpe1_last_backup_info.last_backup_initiation */ #define NDN_HPE1_LASTBKUP_SAVEN (1 << 0) #define NDN_HPE1_LASTBKUP_EXTERNAL (1 << 1) #define NDN_HPE1_LASTBKUP_CKE (1 << 2) #define NDN_HPE1_LASTBKUP_FW (1 << 3) #define NDN_HPE1_LASTBKUP_RESETN (1 << 4) /* ndn_hpe1_last_backup_info.ctlr_backup_stat */ #define NDN_HPE1_LASTBKUP_GTG (1 << 0) #define NDN_HPE1_LASTBKUP_SDRAM_FAULT (1 << 1) #define NDN_HPE1_LASTBKUP_GEN_FAULT (1 << 2) /* NDN_HPE1_CMD_GET_LAST_BACKUP */ struct ndn_hpe1_get_last_backup { __u32 status; __u32 last_backup_info; } __attribute__((packed)); struct ndn_hpe1_last_backup_info { __u8 backup_image; __u8 backup_cmplt_stat; __u8 last_backup_initiation; __u8 ctlr_backup_stat; } __attribute__((packed)); /* NDN_HPE1_CMD_SET_LIFE_THRESHOLD */ struct ndn_hpe1_set_lifetime_threshold { __u8 in_nvm_lifetime_warn_threshold; __u32 status; } __attribute__((packed)); /* ndn_hpe1_inj_err.in_err_typ * ndn_hpe1_get_inj_err_status.err_inj_type * log2(ndn_hpe1_query_err_inj_cap.err_inj_cap) */ enum { NDN_HPE1_EINJ_DEV_NONCRIT = 1, NDN_HPE1_EINJ_DEV_CRIT = 2, NDN_HPE1_EINJ_DEV_FATAL = 3, NDN_HPE1_EINJ_UE_BACKUP = 4, NDN_HPE1_EINJ_UE_RESTORE = 5, NDN_HPE1_EINJ_UE_ERASE = 6, NDN_HPE1_EINJ_UE_ARM = 7, NDN_HPE1_EINJ_BADBLOCK = 8, NDN_HPE1_EINJ_ES_FAULT = 9, NDN_HPE1_EINJ_ES_LOWCHARGE = 10, NDN_HPE1_EINJ_ES_TEMPWARN = 11, NDN_HPE1_EINJ_ES_TEMPERR = 12, NDN_HPE1_EINJ_ES_LIFEWARN = 13, NDN_HPE1_EINJ_ES_LIFEERR = 14, NDN_HPE1_EINJ_DEV_LIFEWARN = 15, NDN_HPE1_EINJ_DEV_LIFEERR = 16, NDN_HPE1_EINJ_FWUPDATE_ERR = 17, NDN_HPE1_EINJ_CTRL_ERR = 18, }; /* ndn_hpe1_inj_err.in_options / ndn_hpe1_get_inj_err_status.err_inj_opt */ enum { NDN_HPE1_EINJ_OPT_SINGLE = 0, NDN_HPE1_EINJ_OPT_PERSISTENT = 1, NDN_HPE1_EINJ_OPT_CLEAR = 2, }; /* ndn_hpe1_get_inj_err_status.err_inj_stat_info */ enum { NDN_HPE1_EINJ_STAT_NONE = 0, NDN_HPE1_EINJ_STAT_INJECTED = 1, NDN_HPE1_EINJ_STAT_PENDING = 2, }; /* NDN_HPE1_CMD_ERRINJ_QUERY */ struct ndn_hpe1_query_err_inj_cap { __u32 status; __u8 err_inj_cap[32]; } __attribute__((packed)); /* NDN_HPE1_CMD_ERRINJ_INJECT */ struct ndn_hpe1_inj_err { __u8 in_err_typ; __u8 in_options; __u32 status; } __attribute__((packed)); /* NDN_HPE1_CMD_ERRINJ_STATUS */ struct ndn_hpe1_get_inj_err_status { __u32 status; __u8 err_inj_stat_info; __u8 err_inj_type; __u8 err_inj_opt; } __attribute__((packed)); union ndn_hpe1_cmd { __u64 query; struct ndn_hpe1_smart smart; struct ndn_hpe1_smart_threshold thresh; struct ndn_hpe1_get_config_size get_size; struct ndn_hpe1_get_config_data_hdr get_data; struct ndn_hpe1_get_id get_id; struct ndn_hpe1_get_energy_src_id get_energy_src_id; struct ndn_hpe1_get_last_backup get_last_backup; struct ndn_hpe1_last_backup_info last_backup_info; struct ndn_hpe1_set_lifetime_threshold set_life_thresh; struct ndn_hpe1_query_err_inj_cap err_cap; struct ndn_hpe1_inj_err inj_err; struct ndn_hpe1_get_inj_err_status inj_err_stat; unsigned char buf[128]; }; struct ndn_pkg_hpe1 { struct nd_cmd_pkg gen; union ndn_hpe1_cmd u; } __attribute__((packed)); #define NDN_IOCTL_HPE1_PASSTHRU _IOWR(ND_IOCTL, ND_CMD_CALL, \ struct ndn_pkg_hpe1) #endif /* __NDCTL_HPE1_H__ */ ndctl-67/ndctl/lib/hyperv.c000066400000000000000000000104471355562357700157770ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2019, Microsoft Corporation. All rights reserved. */ #include #include #include #include #include #include "private.h" #include "hyperv.h" static bool hyperv_cmd_is_supported(struct ndctl_dimm *dimm, int cmd) { /* * "ndctl monitor" requires ND_CMD_SMART, which is not really supported * by Hyper-V virtual NVDIMM. Nevertheless, ND_CMD_SMART can be emulated * by ND_HYPERV_CMD_GET_HEALTH_INFO and ND_HYPERV_CMD_GET_SHUTDOWN_INFO. */ if (cmd == ND_CMD_SMART ) return true; return !!(dimm->cmd_mask & (1ULL << cmd)); } static struct ndctl_cmd *alloc_hyperv_cmd(struct ndctl_dimm *dimm, unsigned int command) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct nd_pkg_hyperv *hyperv; struct ndctl_cmd *cmd; size_t size; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (test_dimm_dsm(dimm, command) == DIMM_DSM_UNSUPPORTED) { dbg(ctx, "unsupported function\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_pkg_hyperv); cmd = calloc(1, size); if (!cmd) return NULL; ndctl_cmd_ref(cmd); cmd->dimm = dimm; cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; hyperv = cmd->hyperv; hyperv->gen.nd_family = NVDIMM_FAMILY_HYPERV; hyperv->gen.nd_command = command; hyperv->gen.nd_size_out = sizeof(hyperv->u.health_info); cmd->firmware_status = &hyperv->u.health_info.status; return cmd; } static struct ndctl_cmd *hyperv_dimm_cmd_new_smart(struct ndctl_dimm *dimm) { return alloc_hyperv_cmd(dimm, ND_HYPERV_CMD_GET_HEALTH_INFO); } static int hyperv_cmd_valid(struct ndctl_cmd *cmd, unsigned int command) { if (cmd->type != ND_CMD_CALL || cmd->size != sizeof(*cmd) + sizeof(struct nd_pkg_hyperv) || cmd->hyperv->gen.nd_family != NVDIMM_FAMILY_HYPERV || cmd->hyperv->gen.nd_command != command || cmd->status != 0 || cmd->hyperv->u.status != 0) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } static int hyperv_valid_health_info(struct ndctl_cmd *cmd) { return hyperv_cmd_valid(cmd, ND_HYPERV_CMD_GET_HEALTH_INFO); } static int hyperv_get_shutdown_count(struct ndctl_cmd *cmd, unsigned int *count) { unsigned int command = ND_HYPERV_CMD_GET_SHUTDOWN_INFO; struct ndctl_cmd *cmd_get_shutdown_info; int rc; cmd_get_shutdown_info = alloc_hyperv_cmd(cmd->dimm, command); if (!cmd_get_shutdown_info) return -EINVAL; if (ndctl_cmd_submit_xlat(cmd_get_shutdown_info) < 0 || hyperv_cmd_valid(cmd_get_shutdown_info, command) < 0) { rc = -EINVAL; goto out; } *count = cmd_get_shutdown_info->hyperv->u.shutdown_info.count; rc = 0; out: ndctl_cmd_unref(cmd_get_shutdown_info); return rc; } static unsigned int hyperv_cmd_get_flags(struct ndctl_cmd *cmd) { unsigned int flags = 0; unsigned int count; int rc; rc = hyperv_valid_health_info(cmd); if (rc < 0) { errno = -rc; return 0; } flags |= ND_SMART_HEALTH_VALID; if (hyperv_get_shutdown_count(cmd, &count) == 0) flags |= ND_SMART_SHUTDOWN_COUNT_VALID; return flags; } static unsigned int hyperv_cmd_get_health(struct ndctl_cmd *cmd) { unsigned int health = 0; __u32 num; int rc; rc = hyperv_valid_health_info(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } num = cmd->hyperv->u.health_info.health & 0x3F; if (num & (BIT(0) | BIT(1))) health |= ND_SMART_CRITICAL_HEALTH; if (num & BIT(2)) health |= ND_SMART_FATAL_HEALTH; if (num & (BIT(3) | BIT(4) | BIT(5))) health |= ND_SMART_NON_CRITICAL_HEALTH; return health; } static unsigned int hyperv_cmd_get_shutdown_count(struct ndctl_cmd *cmd) { unsigned int count; int rc;; rc = hyperv_get_shutdown_count(cmd, &count); if (rc < 0) { errno = -rc; return UINT_MAX; } return count; } static int hyperv_cmd_xlat_firmware_status(struct ndctl_cmd *cmd) { return cmd->hyperv->u.status == 0 ? 0 : -EINVAL; } struct ndctl_dimm_ops * const hyperv_dimm_ops = &(struct ndctl_dimm_ops) { .cmd_is_supported = hyperv_cmd_is_supported, .new_smart = hyperv_dimm_cmd_new_smart, .smart_get_flags = hyperv_cmd_get_flags, .smart_get_health = hyperv_cmd_get_health, .smart_get_shutdown_count = hyperv_cmd_get_shutdown_count, .xlat_firmware_status = hyperv_cmd_xlat_firmware_status, }; ndctl-67/ndctl/lib/hyperv.h000066400000000000000000000016471355562357700160060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2019, Microsoft Corporation. All rights reserved. */ #ifndef __NDCTL_HYPERV_H__ #define __NDCTL_HYPERV_H__ /* See http://www.uefi.org/RFIC_LIST ("Virtual NVDIMM 0x1901") */ enum { ND_HYPERV_CMD_QUERY_SUPPORTED_FUNCTIONS = 0, ND_HYPERV_CMD_GET_HEALTH_INFO = 1, ND_HYPERV_CMD_GET_SHUTDOWN_INFO = 2, }; /* Get Health Information (Function Index 1) */ struct nd_hyperv_health_info { __u32 status; __u32 health; } __attribute__((packed)); /* Get Unsafe Shutdown Count (Function Index 2) */ struct nd_hyperv_shutdown_info { __u32 status; __u32 count; } __attribute__((packed)); union nd_hyperv_cmd { __u32 status; struct nd_hyperv_health_info health_info; struct nd_hyperv_shutdown_info shutdown_info; } __attribute__((packed)); struct nd_pkg_hyperv { struct nd_cmd_pkg gen; union nd_hyperv_cmd u; } __attribute__((packed)); #endif /* __NDCTL_HYPERV_H__ */ ndctl-67/ndctl/lib/inject.c000066400000000000000000000321031355562357700157270ustar00rootroot00000000000000/* * Copyright (c) 2014-2017, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include "private.h" NDCTL_EXPORT int ndctl_bus_has_error_injection(struct ndctl_bus *bus) { /* Currently, only nfit buses have error injection */ if (!bus || !ndctl_bus_has_nfit(bus)) return 0; if (ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_SET) && ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_GET) && ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_CLEAR)) return 1; return 0; } static int ndctl_namespace_get_injection_bounds( struct ndctl_namespace *ndns, unsigned long long *ns_offset, unsigned long long *ns_size) { struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); if (!ns_offset || !ns_size) return -ENXIO; if (pfn) { *ns_offset = ndctl_pfn_get_resource(pfn); *ns_size = ndctl_pfn_get_size(pfn); return 0; } if (dax) { *ns_offset = ndctl_dax_get_resource(dax); *ns_size = ndctl_dax_get_size(dax); return 0; } if (btt) return -EOPNOTSUPP; /* raw */ *ns_offset = ndctl_namespace_get_resource(ndns); *ns_size = ndctl_namespace_get_size(ndns); return 0; } static int block_to_spa_offset(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, u64 *offset, u64 *length) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); unsigned long long ns_offset, ns_size; int rc; rc = ndctl_namespace_get_injection_bounds(ndns, &ns_offset, &ns_size); if (rc) return rc; *offset = ns_offset + block * 512; *length = count * 512; /* check bounds */ if (*offset + *length > ns_offset + ns_size) { dbg(ctx, "Error: block %#llx, count %#llx are out of bounds\n", block, count); return -EINVAL; } return 0; } static int translate_status(u32 status) { switch (status) { case ND_ARS_ERR_INJ_STATUS_NOT_SUPP: return -EOPNOTSUPP; case ND_ARS_ERR_INJ_STATUS_INVALID_PARAM: return -EINVAL; } return 0; } static int ndctl_namespace_get_clear_unit(struct ndctl_namespace *ndns) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); unsigned long long ns_offset, ns_size; unsigned int clear_unit; struct ndctl_cmd *cmd; int rc; rc = ndctl_namespace_get_injection_bounds(ndns, &ns_offset, &ns_size); if (rc) return rc; cmd = ndctl_bus_cmd_new_ars_cap(bus, ns_offset, ns_size); rc = ndctl_cmd_submit(cmd); if (rc < 0) { dbg(ctx, "Error submitting ars_cap: %d\n", rc); goto out; } clear_unit = ndctl_cmd_ars_cap_get_clear_unit(cmd); if (clear_unit == 0) { dbg(ctx, "Got an invalid clear_err_unit from ars_cap\n"); rc = -EINVAL; goto out; } rc = clear_unit; out: ndctl_cmd_unref(cmd); return rc; } static int ndctl_namespace_inject_one_error(struct ndctl_namespace *ndns, unsigned long long block, unsigned int flags) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct nd_cmd_ars_err_inj *err_inj; struct nd_cmd_pkg *pkg; struct ndctl_cmd *cmd; u64 offset, length; int rc, clear_unit; rc = block_to_spa_offset(ndns, block, 1, &offset, &length); if (rc) return rc; clear_unit = ndctl_namespace_get_clear_unit(ndns); if (clear_unit < 0) return clear_unit; if (!(flags & (1 << NDCTL_NS_INJECT_SATURATE))) { /* clamp injection length per block to the clear_unit */ if (length > (unsigned int)clear_unit) length = clear_unit; } cmd = ndctl_bus_cmd_new_err_inj(bus); if (!cmd) return -ENOMEM; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; err_inj = (struct nd_cmd_ars_err_inj *)&pkg->nd_payload[0]; err_inj->err_inj_spa_range_base = offset; err_inj->err_inj_spa_range_length = length; if (flags & (1 << NDCTL_NS_INJECT_NOTIFY)) err_inj->err_inj_options |= (1 << ND_ARS_ERR_INJ_OPT_NOTIFY); rc = ndctl_cmd_submit(cmd); if (rc < 0) { dbg(ctx, "Error submitting command: %d\n", rc); goto out; } rc = translate_status(err_inj->status); out: ndctl_cmd_unref(cmd); return rc; } NDCTL_EXPORT int ndctl_namespace_inject_error2(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, unsigned int flags) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); unsigned long long i; int rc = -EINVAL; if (!ndctl_bus_has_error_injection(bus)) return -EOPNOTSUPP; if (!ndctl_bus_has_nfit(bus)) return -EOPNOTSUPP; for (i = 0; i < count; i++) { rc = ndctl_namespace_inject_one_error(ndns, block + i, flags); if (rc) { err(ctx, "Injection failed at block %llx\n", block + i); return rc; } } return rc; } NDCTL_EXPORT int ndctl_namespace_inject_error(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, bool notify) { return ndctl_namespace_inject_error2(ndns, block, count, notify ? (1 << NDCTL_NS_INJECT_NOTIFY) : 0); } static int ndctl_namespace_uninject_one_error(struct ndctl_namespace *ndns, unsigned long long block, unsigned int flags) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct nd_cmd_ars_err_inj_clr *err_inj_clr; struct nd_cmd_pkg *pkg; struct ndctl_cmd *cmd; u64 offset, length; int rc, clear_unit; rc = block_to_spa_offset(ndns, block, 1, &offset, &length); if (rc) return rc; clear_unit = ndctl_namespace_get_clear_unit(ndns); if (clear_unit < 0) return clear_unit; if (!(flags & (1 << NDCTL_NS_INJECT_SATURATE))) { /* clamp injection length per block to the clear_unit */ if (length > (unsigned int)clear_unit) length = clear_unit; } cmd = ndctl_bus_cmd_new_err_inj_clr(bus); if (!cmd) return -ENOMEM; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; err_inj_clr = (struct nd_cmd_ars_err_inj_clr *)&pkg->nd_payload[0]; err_inj_clr->err_inj_clr_spa_range_base = offset; err_inj_clr->err_inj_clr_spa_range_length = length; rc = ndctl_cmd_submit(cmd); if (rc < 0) { dbg(ctx, "Error submitting command: %d\n", rc); goto out; } rc = translate_status(err_inj_clr->status); out: ndctl_cmd_unref(cmd); return rc; } NDCTL_EXPORT int ndctl_namespace_uninject_error2(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, unsigned int flags) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); unsigned long long i; int rc = -EINVAL; if (!ndctl_bus_has_error_injection(bus)) return -EOPNOTSUPP; if (!ndctl_bus_has_nfit(bus)) return -EOPNOTSUPP; for (i = 0; i < count; i++) { rc = ndctl_namespace_uninject_one_error(ndns, block + i, flags); if (rc) { err(ctx, "Un-injection failed at block %llx\n", block + i); return rc; } } return rc; } NDCTL_EXPORT int ndctl_namespace_uninject_error(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count) { return ndctl_namespace_uninject_error2(ndns, block, count, 0); } static int bb_add_record(struct list_head *h, u64 block, u64 count) { struct ndctl_bb *bb, *bb_iter, *bb_next, *bb_prev; int merged = 0; bb = calloc(1, sizeof(*bb)); if (bb == NULL) return -ENOMEM; bb->block = block; bb->count = count; if (list_empty(h)) { list_add(h, &bb->list); return 0; } /* add 'bb' to the list such that it remains sorted */ list_for_each(h, bb_iter, list) { /* Find insertion point */ bb_prev = list_prev(h, bb_iter, list); bb_next = list_next(h, bb_iter, list); if (bb_prev == NULL) { /* bb_iter is the first entry */ if (bb->block < bb_iter->block) { list_add(h, &bb->list); bb = NULL; break; } } if (bb_next == NULL) { /* * bb_iter is the last entry. If we've reached here, * the only option is to add to the tail as the case * for "tail - 1" should have been covered by the * following checks for the previous iteration. */ list_add_tail(h, &bb->list); bb = NULL; break; } /* Add to the left of bb_iter */ if (bb->block <= bb_iter->block) { if (bb_prev && (bb_prev->block <= bb->block)) { list_add_after(h, &bb_prev->list, &bb->list); bb = NULL; break; } } /* Add to the right of bb_iter */ if (bb_iter->block <= bb->block) { if (bb_next && (bb->block <= bb_next->block)) { list_add_after(h, &bb_iter->list, &bb->list); bb = NULL; break; } } } /* ensure bb has actually been consumed (set to NULL earlier) */ if (bb != NULL) { free(bb); return -ENXIO; } /* second pass over the list looking for mergeable entries */ list_for_each(h, bb_iter, list) { u64 cur_end, next_end, cur_start, next_start; /* * test for merges in a loop here because one addition can * potentially have a cascading merge effect on multiple * remaining entries */ do { /* reset the merged flag */ merged = 0; bb_next = list_next(h, bb_iter, list); if (bb_next == NULL) break; cur_start = bb_iter->block; next_start = bb_next->block; cur_end = bb_iter->block + bb_iter->count - 1; next_end = bb_next->block + bb_next->count - 1; if (cur_end >= next_start) { /* overlapping records that can be merged */ if (next_end > cur_end) { /* next extends cur */ bb_iter->count = next_end - cur_start + 1; } else { /* next is contained in cur */ ; } /* next is now redundant */ list_del_from(h, &bb_next->list); free(bb_next); merged = 1; continue; } if (next_start == cur_end + 1) { /* adjoining records that can be merged */ bb_iter->count = next_end - cur_start + 1; list_del_from(h, &bb_next->list); free(bb_next); merged = 1; continue; } } while (merged); } return 0; } static int injection_status_to_bb(struct ndctl_namespace *ndns, struct nd_cmd_ars_err_inj_stat *stat, u64 ns_spa, u64 ns_size) { unsigned int i; int rc = 0; for (i = 0; i < stat->inj_err_rec_count; i++) { u64 ns_off, rec_off, rec_len; u64 block, count, start_pad; rec_off = stat->record[i].err_inj_stat_spa_range_base; rec_len = stat->record[i].err_inj_stat_spa_range_length; /* discard ranges outside the provided namespace */ if (rec_off < ns_spa) continue; if (rec_off >= ns_spa + ns_size) continue; /* translate spa offset to namespace offset */ ns_off = rec_off - ns_spa; block = ALIGN_DOWN(ns_off, 512)/512; start_pad = ns_off - (block * 512); count = ALIGN(start_pad + rec_len, 512)/512; rc = bb_add_record(&ndns->injected_bb, block, count); if (rc) break; } return rc; } NDCTL_EXPORT int ndctl_namespace_injection_status(struct ndctl_namespace *ndns) { struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct nd_cmd_ars_err_inj_stat *err_inj_stat; unsigned long long ns_offset, ns_size; int rc = -EOPNOTSUPP, buf_size; struct ndctl_cmd *cmd = NULL; struct nd_cmd_pkg *pkg; if (!ndctl_bus_has_error_injection(bus)) return -EOPNOTSUPP; if (ndctl_bus_has_nfit(bus)) { rc = ndctl_namespace_get_injection_bounds(ndns, &ns_offset, &ns_size); if (rc) return rc; cmd = ndctl_bus_cmd_new_ars_cap(bus, ns_offset, ns_size); rc = ndctl_cmd_submit(cmd); if (rc < 0) { dbg(ctx, "Error submitting ars_cap: %d\n", rc); goto out; } buf_size = ndctl_cmd_ars_cap_get_size(cmd); if (buf_size == 0) { dbg(ctx, "Got an invalid max_ars_out from ars_cap\n"); rc = -EINVAL; goto out; } ndctl_cmd_unref(cmd); cmd = ndctl_bus_cmd_new_err_inj_stat(bus, buf_size); if (!cmd) return -ENOMEM; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; err_inj_stat = (struct nd_cmd_ars_err_inj_stat *)&pkg->nd_payload[0]; rc = ndctl_cmd_submit(cmd); if (rc < 0) { dbg(ctx, "Error submitting command: %d\n", rc); goto out; } rc = injection_status_to_bb(ndns, err_inj_stat, ns_offset, ns_size); if (rc) { dbg(ctx, "Error converting status to badblocks: %d\n", rc); goto out; } } out: ndctl_cmd_unref(cmd); return rc; } NDCTL_EXPORT struct ndctl_bb *ndctl_namespace_injection_get_first_bb( struct ndctl_namespace *ndns) { return list_top(&ndns->injected_bb, struct ndctl_bb, list); } NDCTL_EXPORT struct ndctl_bb *ndctl_namespace_injection_get_next_bb( struct ndctl_namespace *ndns, struct ndctl_bb *bb) { return list_next(&ndns->injected_bb, bb, list); } NDCTL_EXPORT unsigned long long ndctl_bb_get_block(struct ndctl_bb *bb) { if (bb) return bb->block; errno = EINVAL; return ULLONG_MAX; } NDCTL_EXPORT unsigned long long ndctl_bb_get_count(struct ndctl_bb *bb) { if (bb) return bb->count; errno = EINVAL; return ULLONG_MAX; } ndctl-67/ndctl/lib/intel.c000066400000000000000000000555371355562357700156060ustar00rootroot00000000000000/* * Copyright (c) 2016-2017, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include "private.h" static int intel_cmd_xlat_firmware_status(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; unsigned int status, ext_status; status = (*cmd->firmware_status) & ND_INTEL_STATUS_MASK; ext_status = (*cmd->firmware_status) & ND_INTEL_STATUS_EXTEND_MASK; /* Common statuses */ switch (status) { case ND_INTEL_STATUS_SUCCESS: return 0; case ND_INTEL_STATUS_NOTSUPP: return -EOPNOTSUPP; case ND_INTEL_STATUS_NOTEXIST: return -ENXIO; case ND_INTEL_STATUS_INVALPARM: return -EINVAL; case ND_INTEL_STATUS_HWERR: return -EIO; case ND_INTEL_STATUS_RETRY: return -EAGAIN; case ND_INTEL_STATUS_EXTEND: /* refer to extended status, break out of this */ break; case ND_INTEL_STATUS_NORES: return -EAGAIN; case ND_INTEL_STATUS_NOTREADY: return -EBUSY; } /* Extended status is command specific */ switch (pkg->gen.nd_command) { case ND_INTEL_SMART: case ND_INTEL_SMART_THRESHOLD: case ND_INTEL_SMART_SET_THRESHOLD: /* ext status not specified */ break; case ND_INTEL_SMART_INJECT: /* smart injection not enabled */ if (ext_status == ND_INTEL_STATUS_INJ_DISABLED) return -ENXIO; break; } return -ENOMSG; } static struct ndctl_cmd *alloc_intel_cmd(struct ndctl_dimm *dimm, unsigned func, size_t in_size, size_t out_size) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); struct ndctl_cmd *cmd; size_t size; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd: %d\n", ND_CMD_CALL); return NULL; } if (test_dimm_dsm(dimm, func) == DIMM_DSM_UNSUPPORTED) { dbg(ctx, "unsupported function: %d\n", func); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_pkg_intel) + in_size + out_size; cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; *(cmd->intel) = (struct nd_pkg_intel) { .gen = { .nd_family = NVDIMM_FAMILY_INTEL, .nd_command = func, .nd_size_in = in_size, .nd_size_out = out_size, }, }; return cmd; } static struct ndctl_cmd *intel_dimm_cmd_new_smart(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_smart) == 132); cmd = alloc_intel_cmd(dimm, ND_INTEL_SMART, 0, sizeof(cmd->intel->smart)); if (!cmd) return NULL; cmd->firmware_status = &cmd->intel->smart.status; return cmd; } static int intel_smart_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 0 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_SMART) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } #define intel_smart_get_field(cmd, field) \ static unsigned int intel_cmd_smart_get_##field(struct ndctl_cmd *cmd) \ { \ int rc; \ rc = intel_smart_valid(cmd); \ if (rc < 0) { \ errno = -rc; \ return UINT_MAX; \ } \ return cmd->intel->smart.field; \ } static unsigned int intel_cmd_smart_get_flags(struct ndctl_cmd *cmd) { unsigned int flags = 0; unsigned int intel_flags; if (intel_smart_valid(cmd) < 0) return 0; /* translate intel specific flags to libndctl api smart flags */ intel_flags = cmd->intel->smart.flags; if (intel_flags & ND_INTEL_SMART_HEALTH_VALID) flags |= ND_SMART_HEALTH_VALID; if (intel_flags & ND_INTEL_SMART_SPARES_VALID) flags |= ND_SMART_SPARES_VALID; if (intel_flags & ND_INTEL_SMART_USED_VALID) flags |= ND_SMART_USED_VALID; if (intel_flags & ND_INTEL_SMART_MTEMP_VALID) flags |= ND_SMART_MTEMP_VALID; if (intel_flags & ND_INTEL_SMART_CTEMP_VALID) flags |= ND_SMART_CTEMP_VALID; if (intel_flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) flags |= ND_SMART_SHUTDOWN_COUNT_VALID; if (intel_flags & ND_INTEL_SMART_AIT_STATUS_VALID) flags |= ND_SMART_AIT_STATUS_VALID; if (intel_flags & ND_INTEL_SMART_PTEMP_VALID) flags |= ND_SMART_PTEMP_VALID; if (intel_flags & ND_INTEL_SMART_ALARM_VALID) flags |= ND_SMART_ALARM_VALID; if (intel_flags & ND_INTEL_SMART_SHUTDOWN_VALID) flags |= ND_SMART_SHUTDOWN_VALID; if (intel_flags & ND_INTEL_SMART_VENDOR_VALID) flags |= ND_SMART_VENDOR_VALID; return flags; } static unsigned int intel_cmd_smart_get_health(struct ndctl_cmd *cmd) { unsigned int health = 0; unsigned int intel_health; if (intel_smart_valid(cmd) < 0) return 0; intel_health = cmd->intel->smart.health; if (intel_health & ND_INTEL_SMART_NON_CRITICAL_HEALTH) health |= ND_SMART_NON_CRITICAL_HEALTH; if (intel_health & ND_INTEL_SMART_CRITICAL_HEALTH) health |= ND_SMART_CRITICAL_HEALTH; if (intel_health & ND_INTEL_SMART_FATAL_HEALTH) health |= ND_SMART_FATAL_HEALTH; return health; } intel_smart_get_field(cmd, media_temperature) intel_smart_get_field(cmd, ctrl_temperature) intel_smart_get_field(cmd, spares) intel_smart_get_field(cmd, alarm_flags) intel_smart_get_field(cmd, life_used) intel_smart_get_field(cmd, shutdown_state) intel_smart_get_field(cmd, shutdown_count) intel_smart_get_field(cmd, vendor_size) static unsigned char *intel_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd) { if (intel_smart_valid(cmd) < 0) return NULL; return cmd->intel->smart.vendor_data; } static int intel_smart_threshold_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 0 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_SMART_THRESHOLD) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } #define intel_smart_threshold_get_field(cmd, field) \ static unsigned int intel_cmd_smart_threshold_get_##field( \ struct ndctl_cmd *cmd) \ { \ int rc; \ rc = intel_smart_threshold_valid(cmd); \ if (rc < 0) { \ errno = -rc; \ return UINT_MAX; \ } \ return cmd->intel->thresh.field; \ } static unsigned int intel_cmd_smart_threshold_get_alarm_control( struct ndctl_cmd *cmd) { struct nd_intel_smart_threshold *thresh; unsigned int flags = 0; if (intel_smart_threshold_valid(cmd) < 0) return 0; thresh = &cmd->intel->thresh; if (thresh->alarm_control & ND_INTEL_SMART_SPARE_TRIP) flags |= ND_SMART_SPARE_TRIP; if (thresh->alarm_control & ND_INTEL_SMART_TEMP_TRIP) flags |= ND_SMART_TEMP_TRIP; if (thresh->alarm_control & ND_INTEL_SMART_CTEMP_TRIP) flags |= ND_SMART_CTEMP_TRIP; return flags; } intel_smart_threshold_get_field(cmd, media_temperature) intel_smart_threshold_get_field(cmd, ctrl_temperature) intel_smart_threshold_get_field(cmd, spares) static struct ndctl_cmd *intel_dimm_cmd_new_smart_threshold( struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_smart_threshold) == 12); cmd = alloc_intel_cmd(dimm, ND_INTEL_SMART_THRESHOLD, 0, sizeof(cmd->intel->thresh)); if (!cmd) return NULL; cmd->firmware_status = &cmd->intel->thresh.status; return cmd; } static struct ndctl_cmd *intel_dimm_cmd_new_smart_set_threshold( struct ndctl_cmd *cmd_thresh) { struct ndctl_cmd *cmd; struct nd_intel_smart_threshold *thresh; struct nd_intel_smart_set_threshold *set_thresh; BUILD_ASSERT(sizeof(struct nd_intel_smart_set_threshold) == 11); if (intel_smart_threshold_valid(cmd_thresh) < 0) return NULL; cmd = alloc_intel_cmd(cmd_thresh->dimm, ND_INTEL_SMART_SET_THRESHOLD, offsetof(typeof(*set_thresh), status), 4); if (!cmd) return NULL; cmd->source = cmd_thresh; ndctl_cmd_ref(cmd_thresh); set_thresh = &cmd->intel->set_thresh; thresh = &cmd_thresh->intel->thresh; set_thresh->alarm_control = thresh->alarm_control; set_thresh->spares = thresh->spares; set_thresh->media_temperature = thresh->media_temperature; set_thresh->ctrl_temperature = thresh->ctrl_temperature; cmd->firmware_status = &set_thresh->status; return cmd; } static int intel_smart_set_threshold_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 1 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_SMART_SET_THRESHOLD) return -EINVAL; return 0; } #define intel_smart_set_threshold_field(field) \ static int intel_cmd_smart_threshold_set_##field( \ struct ndctl_cmd *cmd, unsigned int val) \ { \ if (intel_smart_set_threshold_valid(cmd) < 0) \ return -EINVAL; \ cmd->intel->set_thresh.field = val; \ return 0; \ } static unsigned int intel_cmd_smart_threshold_get_supported_alarms( struct ndctl_cmd *cmd) { if (intel_smart_set_threshold_valid(cmd) < 0) return 0; return ND_SMART_SPARE_TRIP | ND_SMART_MTEMP_TRIP | ND_SMART_CTEMP_TRIP; } intel_smart_set_threshold_field(alarm_control) intel_smart_set_threshold_field(spares) intel_smart_set_threshold_field(media_temperature) intel_smart_set_threshold_field(ctrl_temperature) static int intel_smart_inject_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 1 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_SMART_INJECT) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } static struct ndctl_cmd *intel_new_smart_inject(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_smart_inject) == 19); cmd = alloc_intel_cmd(dimm, ND_INTEL_SMART_INJECT, offsetof(struct nd_intel_smart_inject, status), 4); if (!cmd) return NULL; cmd->firmware_status = &cmd->intel->inject.status; return cmd; } static int intel_cmd_smart_inject_media_temperature(struct ndctl_cmd *cmd, bool enable, unsigned int mtemp) { struct nd_intel_smart_inject *inj; if (intel_smart_inject_valid(cmd) < 0) return -EINVAL; inj = &cmd->intel->inject; inj->flags |= ND_INTEL_SMART_INJECT_MTEMP; inj->mtemp_enable = enable == true; inj->media_temperature = mtemp; return 0; } static int intel_cmd_smart_inject_spares(struct ndctl_cmd *cmd, bool enable, unsigned int spares) { struct nd_intel_smart_inject *inj; if (intel_smart_inject_valid(cmd) < 0) return -EINVAL; inj = &cmd->intel->inject; inj->flags |= ND_INTEL_SMART_INJECT_SPARE; inj->spare_enable = enable == true; inj->spares = spares; return 0; } static int intel_cmd_smart_inject_fatal(struct ndctl_cmd *cmd, bool enable) { struct nd_intel_smart_inject *inj; if (intel_smart_inject_valid(cmd) < 0) return -EINVAL; inj = &cmd->intel->inject; inj->flags |= ND_INTEL_SMART_INJECT_FATAL; inj->fatal_enable = enable == true; return 0; } static int intel_cmd_smart_inject_unsafe_shutdown(struct ndctl_cmd *cmd, bool enable) { struct nd_intel_smart_inject *inj; if (intel_smart_inject_valid(cmd) < 0) return -EINVAL; inj = &cmd->intel->inject; inj->flags |= ND_INTEL_SMART_INJECT_SHUTDOWN; inj->unsafe_shutdown_enable = enable == true; return 0; } static int intel_dimm_smart_inject_supported(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd: %d\n", ND_CMD_CALL); return -EOPNOTSUPP; } if (!test_dimm_dsm(dimm, ND_INTEL_SMART_INJECT)) { dbg(ctx, "smart injection functions unsupported\n"); return -EIO; } return 0; } static const char *intel_cmd_desc(int fn) { static const char *descs[] = { [ND_INTEL_SMART] = "smart", [ND_INTEL_SMART_INJECT] = "smart_inject", [ND_INTEL_SMART_THRESHOLD] = "smart_thresh", [ND_INTEL_FW_GET_INFO] = "firmware_get_info", [ND_INTEL_FW_START_UPDATE] = "firmware_start_update", [ND_INTEL_FW_SEND_DATA] = "firmware_send_data", [ND_INTEL_FW_FINISH_UPDATE] = "firmware_finish_update", [ND_INTEL_FW_FINISH_STATUS_QUERY] = "firmware_finish_query", [ND_INTEL_SMART_SET_THRESHOLD] = "smart_set_thresh", }; const char *desc = descs[fn]; if (fn >= (int) ARRAY_SIZE(descs)) return "unknown"; if (!desc) return "unknown"; return desc; } static struct ndctl_cmd *intel_dimm_cmd_new_fw_get_info(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_fw_info) == 44); cmd = alloc_intel_cmd(dimm, ND_INTEL_FW_GET_INFO, 0, sizeof(cmd->intel->info)); if (!cmd) return NULL; cmd->firmware_status = &cmd->intel->info.status; return cmd; } static int intel_fw_get_info_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 0 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_FW_GET_INFO) return -EINVAL; return 0; } #define intel_fw_info_get_field32(cmd, field) \ static unsigned int intel_cmd_fw_info_get_##field( \ struct ndctl_cmd *cmd) \ { \ int rc; \ rc = intel_fw_get_info_valid(cmd); \ if (rc < 0) { \ errno = -rc; \ return UINT_MAX; \ } \ return cmd->intel->info.field; \ } #define intel_fw_info_get_field64(cmd, field) \ static unsigned long long intel_cmd_fw_info_get_##field( \ struct ndctl_cmd *cmd) \ { \ int rc; \ rc = intel_fw_get_info_valid(cmd); \ if (rc < 0) { \ errno = -rc; \ return ULLONG_MAX; \ } \ return cmd->intel->info.field; \ } intel_fw_info_get_field32(cmd, storage_size) intel_fw_info_get_field32(cmd, max_send_len) intel_fw_info_get_field32(cmd, query_interval) intel_fw_info_get_field32(cmd, max_query_time); intel_fw_info_get_field64(cmd, run_version); static unsigned long long intel_cmd_fw_info_get_updated_version( struct ndctl_cmd *cmd) { int rc; rc = intel_fw_get_info_valid(cmd); if (rc < 0) { errno = -rc; return ULLONG_MAX; } return cmd->intel->info.updated_version; } static struct ndctl_cmd *intel_dimm_cmd_new_fw_start(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_fw_start) == 8); cmd = alloc_intel_cmd(dimm, ND_INTEL_FW_START_UPDATE, 0, sizeof(cmd->intel->start)); if (!cmd) return NULL; cmd->firmware_status = &cmd->intel->start.status; return cmd; } static int intel_fw_start_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 0 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_FW_START_UPDATE) return -EINVAL; return 0; } static unsigned int intel_cmd_fw_start_get_context(struct ndctl_cmd *cmd) { int rc; rc = intel_fw_start_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return cmd->intel->start.context; } static struct ndctl_cmd *intel_dimm_cmd_new_fw_send(struct ndctl_cmd *start, unsigned int offset, unsigned int len, void *data) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_fw_send_data) == 12); cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_SEND_DATA, sizeof(cmd->intel->send) + len, 4); if (!cmd) return NULL; cmd->intel->send.context = start->intel->start.context; cmd->intel->send.offset = offset; cmd->intel->send.length = len; memcpy(cmd->intel->send.data, data, len); /* the last dword is reserved for status */ cmd->firmware_status = (unsigned int *)(&cmd->intel->send.data[0] + len); return cmd; } static struct ndctl_cmd *intel_dimm_cmd_new_fw_finish(struct ndctl_cmd *start) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_fw_finish_update) == 12); cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_FINISH_UPDATE, offsetof(struct nd_intel_fw_finish_update, status), 4); if (!cmd) return NULL; cmd->intel->finish.context = start->intel->start.context; cmd->intel->finish.ctrl_flags = 0; cmd->firmware_status = &cmd->intel->finish.status; return cmd; } static struct ndctl_cmd *intel_dimm_cmd_new_fw_abort(struct ndctl_cmd *start) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_fw_finish_update) == 12); cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_FINISH_UPDATE, sizeof(cmd->intel->finish) - 4, 4); if (!cmd) return NULL; cmd->intel->finish.context = start->intel->start.context; cmd->intel->finish.ctrl_flags = 1; cmd->firmware_status = &cmd->intel->finish.status; return cmd; } static struct ndctl_cmd * intel_dimm_cmd_new_fw_finish_query(struct ndctl_cmd *start) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_fw_finish_query) == 16); cmd = alloc_intel_cmd(start->dimm, ND_INTEL_FW_FINISH_STATUS_QUERY, 4, sizeof(cmd->intel->fquery) - 4); if (!cmd) return NULL; cmd->intel->fquery.context = start->intel->start.context; cmd->firmware_status = &cmd->intel->fquery.status; return cmd; } static int intel_fw_fquery_valid(struct ndctl_cmd *cmd) { struct nd_pkg_intel *pkg = cmd->intel; if (cmd->type != ND_CMD_CALL || cmd->status != 0 || pkg->gen.nd_family != NVDIMM_FAMILY_INTEL || pkg->gen.nd_command != ND_INTEL_FW_FINISH_STATUS_QUERY) return -EINVAL; return 0; } static unsigned long long intel_cmd_fw_fquery_get_fw_rev(struct ndctl_cmd *cmd) { int rc; rc = intel_fw_fquery_valid(cmd); if (rc < 0) { errno = -rc; return ULLONG_MAX; } return cmd->intel->fquery.updated_fw_rev; } static enum ND_FW_STATUS intel_cmd_fw_xlat_extend_firmware_status(struct ndctl_cmd *cmd, unsigned int status) { /* * Note: the cases commented out are identical to the ones that are * not. They are there for reference. */ switch (status & ND_INTEL_STATUS_EXTEND_MASK) { case ND_INTEL_STATUS_START_BUSY: /* case ND_INTEL_STATUS_SEND_CTXINVAL: */ /* case ND_INTEL_STATUS_FIN_CTXINVAL: */ /* case ND_INTEL_STATUS_FQ_CTXINVAL: */ if (cmd->intel->gen.nd_command == ND_INTEL_FW_START_UPDATE) return FW_EBUSY; else return FW_EINVAL_CTX; case ND_INTEL_STATUS_FIN_DONE: /* case ND_INTEL_STATUS_FQ_BUSY: */ if (cmd->intel->gen.nd_command == ND_INTEL_FW_FINISH_UPDATE) return FW_ALREADY_DONE; else return FW_EBUSY; case ND_INTEL_STATUS_FIN_BAD: /* case ND_INTEL_STATUS_FQ_BAD: */ return FW_EBADFW; case ND_INTEL_STATUS_FIN_ABORTED: /* case ND_INTEL_STATUS_FQ_ORDER: */ if (cmd->intel->gen.nd_command == ND_INTEL_FW_FINISH_UPDATE) return FW_ABORTED; else return FW_ESEQUENCE; } return FW_EUNKNOWN; } static enum ND_FW_STATUS intel_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd) { unsigned int status = *cmd->firmware_status; switch (status & ND_INTEL_STATUS_MASK) { case ND_INTEL_STATUS_SUCCESS: return FW_SUCCESS; case ND_INTEL_STATUS_NOTSUPP: return FW_ENOTSUPP; case ND_INTEL_STATUS_NOTEXIST: return FW_ENOTEXIST; case ND_INTEL_STATUS_INVALPARM: return FW_EINVAL; case ND_INTEL_STATUS_HWERR: return FW_EHWERR; case ND_INTEL_STATUS_RETRY: return FW_ERETRY; case ND_INTEL_STATUS_EXTEND: return intel_cmd_fw_xlat_extend_firmware_status(cmd, status); case ND_INTEL_STATUS_NORES: return FW_ENORES; case ND_INTEL_STATUS_NOTREADY: return FW_ENOTREADY; } return FW_EUNKNOWN; } static struct ndctl_cmd * intel_dimm_cmd_new_lss(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; BUILD_ASSERT(sizeof(struct nd_intel_lss) == 5); cmd = alloc_intel_cmd(dimm, ND_INTEL_ENABLE_LSS_STATUS, 1, 4); if (!cmd) return NULL; cmd->intel->lss.enable = 1; cmd->firmware_status = &cmd->intel->lss.status; return cmd; } static int intel_dimm_fw_update_supported(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd: %d\n", ND_CMD_CALL); return -EOPNOTSUPP; } if (test_dimm_dsm(dimm, ND_INTEL_FW_GET_INFO) == DIMM_DSM_UNSUPPORTED || test_dimm_dsm(dimm, ND_INTEL_FW_START_UPDATE) == DIMM_DSM_UNSUPPORTED || test_dimm_dsm(dimm, ND_INTEL_FW_SEND_DATA) == DIMM_DSM_UNSUPPORTED || test_dimm_dsm(dimm, ND_INTEL_FW_FINISH_UPDATE) == DIMM_DSM_UNSUPPORTED || test_dimm_dsm(dimm, ND_INTEL_FW_FINISH_STATUS_QUERY) == DIMM_DSM_UNSUPPORTED) { dbg(ctx, "unsupported function: %d\n", ND_INTEL_FW_GET_INFO); return -EIO; } return 0; } struct ndctl_dimm_ops * const intel_dimm_ops = &(struct ndctl_dimm_ops) { .cmd_desc = intel_cmd_desc, .new_smart = intel_dimm_cmd_new_smart, .smart_get_flags = intel_cmd_smart_get_flags, .smart_get_health = intel_cmd_smart_get_health, .smart_get_media_temperature = intel_cmd_smart_get_media_temperature, .smart_get_ctrl_temperature = intel_cmd_smart_get_ctrl_temperature, .smart_get_spares = intel_cmd_smart_get_spares, .smart_get_alarm_flags = intel_cmd_smart_get_alarm_flags, .smart_get_life_used = intel_cmd_smart_get_life_used, .smart_get_shutdown_state = intel_cmd_smart_get_shutdown_state, .smart_get_shutdown_count = intel_cmd_smart_get_shutdown_count, .smart_get_vendor_size = intel_cmd_smart_get_vendor_size, .smart_get_vendor_data = intel_cmd_smart_get_vendor_data, .new_smart_threshold = intel_dimm_cmd_new_smart_threshold, .smart_threshold_get_alarm_control = intel_cmd_smart_threshold_get_alarm_control, .smart_threshold_get_media_temperature = intel_cmd_smart_threshold_get_media_temperature, .smart_threshold_get_ctrl_temperature = intel_cmd_smart_threshold_get_ctrl_temperature, .smart_threshold_get_spares = intel_cmd_smart_threshold_get_spares, .new_smart_set_threshold = intel_dimm_cmd_new_smart_set_threshold, .smart_threshold_get_supported_alarms = intel_cmd_smart_threshold_get_supported_alarms, .smart_threshold_set_alarm_control = intel_cmd_smart_threshold_set_alarm_control, .smart_threshold_set_media_temperature = intel_cmd_smart_threshold_set_media_temperature, .smart_threshold_set_ctrl_temperature = intel_cmd_smart_threshold_set_ctrl_temperature, .smart_threshold_set_spares = intel_cmd_smart_threshold_set_spares, .new_smart_inject = intel_new_smart_inject, .smart_inject_media_temperature = intel_cmd_smart_inject_media_temperature, .smart_inject_spares = intel_cmd_smart_inject_spares, .smart_inject_fatal = intel_cmd_smart_inject_fatal, .smart_inject_unsafe_shutdown = intel_cmd_smart_inject_unsafe_shutdown, .smart_inject_supported = intel_dimm_smart_inject_supported, .new_fw_get_info = intel_dimm_cmd_new_fw_get_info, .fw_info_get_storage_size = intel_cmd_fw_info_get_storage_size, .fw_info_get_max_send_len = intel_cmd_fw_info_get_max_send_len, .fw_info_get_query_interval = intel_cmd_fw_info_get_query_interval, .fw_info_get_max_query_time = intel_cmd_fw_info_get_max_query_time, .fw_info_get_run_version = intel_cmd_fw_info_get_run_version, .fw_info_get_updated_version = intel_cmd_fw_info_get_updated_version, .new_fw_start_update = intel_dimm_cmd_new_fw_start, .fw_start_get_context = intel_cmd_fw_start_get_context, .new_fw_send = intel_dimm_cmd_new_fw_send, .new_fw_finish = intel_dimm_cmd_new_fw_finish, .new_fw_abort = intel_dimm_cmd_new_fw_abort, .new_fw_finish_query = intel_dimm_cmd_new_fw_finish_query, .fw_fquery_get_fw_rev = intel_cmd_fw_fquery_get_fw_rev, .fw_xlat_firmware_status = intel_cmd_fw_xlat_firmware_status, .new_ack_shutdown_count = intel_dimm_cmd_new_lss, .fw_update_supported = intel_dimm_fw_update_supported, .xlat_firmware_status = intel_cmd_xlat_firmware_status, }; ndctl-67/ndctl/lib/intel.h000066400000000000000000000117261355562357700156030ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* Copyright (c) 2017, Intel Corporation. All rights reserved. */ #ifndef __INTEL_H__ #define __INTEL_H__ #define ND_INTEL_SMART 1 #define ND_INTEL_SMART_THRESHOLD 2 #define ND_INTEL_ENABLE_LSS_STATUS 10 #define ND_INTEL_FW_GET_INFO 12 #define ND_INTEL_FW_START_UPDATE 13 #define ND_INTEL_FW_SEND_DATA 14 #define ND_INTEL_FW_FINISH_UPDATE 15 #define ND_INTEL_FW_FINISH_STATUS_QUERY 16 #define ND_INTEL_SMART_SET_THRESHOLD 17 #define ND_INTEL_SMART_INJECT 18 #define ND_INTEL_SMART_HEALTH_VALID (1 << 0) #define ND_INTEL_SMART_SPARES_VALID (1 << 1) #define ND_INTEL_SMART_USED_VALID (1 << 2) #define ND_INTEL_SMART_MTEMP_VALID (1 << 3) #define ND_INTEL_SMART_CTEMP_VALID (1 << 4) #define ND_INTEL_SMART_SHUTDOWN_COUNT_VALID (1 << 5) #define ND_INTEL_SMART_AIT_STATUS_VALID (1 << 6) #define ND_INTEL_SMART_PTEMP_VALID (1 << 7) #define ND_INTEL_SMART_ALARM_VALID (1 << 9) #define ND_INTEL_SMART_SHUTDOWN_VALID (1 << 10) #define ND_INTEL_SMART_VENDOR_VALID (1 << 11) #define ND_INTEL_SMART_SPARE_TRIP (1 << 0) #define ND_INTEL_SMART_TEMP_TRIP (1 << 1) #define ND_INTEL_SMART_CTEMP_TRIP (1 << 2) #define ND_INTEL_SMART_NON_CRITICAL_HEALTH (1 << 0) #define ND_INTEL_SMART_CRITICAL_HEALTH (1 << 1) #define ND_INTEL_SMART_FATAL_HEALTH (1 << 2) #define ND_INTEL_SMART_INJECT_MTEMP (1 << 0) #define ND_INTEL_SMART_INJECT_SPARE (1 << 1) #define ND_INTEL_SMART_INJECT_FATAL (1 << 2) #define ND_INTEL_SMART_INJECT_SHUTDOWN (1 << 3) struct nd_intel_smart { __u32 status; union { struct { __u32 flags; __u8 reserved0[4]; __u8 health; __u8 spares; __u8 life_used; __u8 alarm_flags; __u16 media_temperature; __u16 ctrl_temperature; __u32 shutdown_count; __u8 ait_status; __u16 pmic_temperature; __u8 reserved1[8]; __u8 shutdown_state; __u32 vendor_size; __u8 vendor_data[92]; } __attribute__((packed)); __u8 data[128]; }; } __attribute__((packed)); struct nd_intel_smart_threshold { __u32 status; union { struct { __u16 alarm_control; __u8 spares; __u16 media_temperature; __u16 ctrl_temperature; __u8 reserved[1]; } __attribute__((packed)); __u8 data[8]; }; } __attribute__((packed)); struct nd_intel_smart_set_threshold { __u16 alarm_control; __u8 spares; __u16 media_temperature; __u16 ctrl_temperature; __u32 status; } __attribute__((packed)); struct nd_intel_smart_inject { __u64 flags; __u8 mtemp_enable; __u16 media_temperature; __u8 spare_enable; __u8 spares; __u8 fatal_enable; __u8 unsafe_shutdown_enable; __u32 status; } __attribute__((packed)); struct nd_intel_fw_info { __u32 status; __u32 storage_size; __u32 max_send_len; __u32 query_interval; __u32 max_query_time; __u8 update_cap; __u8 reserved[3]; __u32 fis_version; __u64 run_version; __u64 updated_version; } __attribute__((packed)); struct nd_intel_fw_start { __u32 status; __u32 context; } __attribute__((packed)); /* this one has the output first because the variable input data size */ struct nd_intel_fw_send_data { __u32 context; __u32 offset; __u32 length; __u8 data[0]; /* reserving last 4 bytes as status */ /* __u32 status; */ } __attribute__((packed)); struct nd_intel_fw_finish_update { __u8 ctrl_flags; __u8 reserved[3]; __u32 context; __u32 status; } __attribute__((packed)); struct nd_intel_fw_finish_query { __u32 context; __u32 status; __u64 updated_fw_rev; } __attribute__((packed)); struct nd_intel_lss { __u8 enable; __u32 status; } __attribute__((packed)); struct nd_pkg_intel { struct nd_cmd_pkg gen; union { struct nd_intel_smart smart; struct nd_intel_smart_inject inject; struct nd_intel_smart_threshold thresh; struct nd_intel_smart_set_threshold set_thresh; struct nd_intel_fw_info info; struct nd_intel_fw_start start; struct nd_intel_fw_send_data send; struct nd_intel_fw_finish_update finish; struct nd_intel_fw_finish_query fquery; struct nd_intel_lss lss; }; }; #define ND_INTEL_STATUS_MASK 0xffff #define ND_INTEL_STATUS_SUCCESS 0 #define ND_INTEL_STATUS_NOTSUPP 1 #define ND_INTEL_STATUS_NOTEXIST 2 #define ND_INTEL_STATUS_INVALPARM 3 #define ND_INTEL_STATUS_HWERR 4 #define ND_INTEL_STATUS_RETRY 5 #define ND_INTEL_STATUS_UNKNOWN 6 #define ND_INTEL_STATUS_EXTEND 7 #define ND_INTEL_STATUS_NORES 8 #define ND_INTEL_STATUS_NOTREADY 9 #define ND_INTEL_STATUS_EXTEND_MASK 0xffff0000 #define ND_INTEL_STATUS_START_BUSY 0x10000 #define ND_INTEL_STATUS_SEND_CTXINVAL 0x10000 #define ND_INTEL_STATUS_FIN_CTXINVAL 0x10000 #define ND_INTEL_STATUS_FIN_DONE 0x20000 #define ND_INTEL_STATUS_FIN_BAD 0x30000 #define ND_INTEL_STATUS_FIN_ABORTED 0x40000 #define ND_INTEL_STATUS_FQ_CTXINVAL 0x10000 #define ND_INTEL_STATUS_FQ_BUSY 0x20000 #define ND_INTEL_STATUS_FQ_BAD 0x30000 #define ND_INTEL_STATUS_FQ_ORDER 0x40000 #define ND_INTEL_STATUS_INJ_DISABLED 0x10000 #endif /* __INTEL_H__ */ ndctl-67/ndctl/lib/libndctl.c000066400000000000000000004112341355562357700162540ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "private.h" static uuid_t null_uuid; /** * DOC: General note, the structure layouts are privately defined. * Access struct member fields with ndctl__get_. This * library is multithread-aware in that it supports multiple * simultaneous reference-counted contexts, but it is not multithread * safe. Also note that there is no coordination between contexts, * changes made in one context instance may not be reflected in another. */ /** * ndctl_sizeof_namespace_index - min size of a namespace index block plus padding */ NDCTL_EXPORT size_t ndctl_sizeof_namespace_index(void) { return ALIGN(sizeof(struct namespace_index), NSINDEX_ALIGN); } /** * ndctl_min_namespace_size - minimum namespace size that btt suports */ NDCTL_EXPORT size_t ndctl_min_namespace_size(void) { return NSLABEL_NAMESPACE_MIN_SIZE; } /** * ndctl_sizeof_namespace_label - single entry size in a dimm label set */ NDCTL_EXPORT size_t ndctl_sizeof_namespace_label(void) { /* TODO: v1.2 label support */ return offsetof(struct namespace_label, type_guid); } NDCTL_EXPORT double ndctl_decode_smart_temperature(unsigned int temp) { bool negative = !!(temp & (1 << 15)); double t; temp &= ~(1 << 15); t = temp; t /= 16; if (negative) t *= -1; return t; } NDCTL_EXPORT unsigned int ndctl_encode_smart_temperature(double temp) { bool negative = false; unsigned int t; if (temp < 0) { negative = true; temp *= -1; } t = temp; t *= 16; if (negative) t |= (1 << 15); return t; } struct ndctl_ctx; /** * struct ndctl_mapping - dimm extent relative to a region * @dimm: backing dimm for the mapping * @offset: dimm relative offset * @length: span of the extent * @position: interleave-order of the extent * * This data can be used to identify the dimm ranges contributing to a * region / interleave-set and identify how regions alias each other. */ struct ndctl_mapping { struct ndctl_region *region; struct ndctl_dimm *dimm; unsigned long long offset, length; int position; struct list_node list; }; /** * struct ndctl_region - container for 'pmem' or 'block' capacity * @module: kernel module * @mappings: number of extent ranges contributing to the region * @size: total capacity of the region before resolving aliasing * @type: integer nd-bus device-type * @type_name: 'pmem' or 'block' * @generation: incremented everytime the region is disabled * @nstype: the resulting type of namespace this region produces * @numa_node: numa node attribute * * A region may alias between pmem and block-window access methods. The * region driver is tasked with parsing the label (if their is one) and * coordinating configuration with peer regions. * * When a region is disabled a client may have pending references to * namespaces and btts. After a disable event the client can * ndctl_region_cleanup() to clean up invalid objects, or it can * specify the cleanup flag to ndctl_region_disable(). */ struct ndctl_region { struct kmod_module *module; struct ndctl_bus *bus; int id, num_mappings, nstype, range_index, ro; int mappings_init; int namespaces_init; int btts_init; int pfns_init; int daxs_init; int refresh_type; unsigned long long size; char *region_path; char *region_buf; int buf_len; int generation; int numa_node; struct list_head btts; struct list_head pfns; struct list_head daxs; struct list_head mappings; struct list_head namespaces; struct list_head stale_namespaces; struct list_head stale_btts; struct list_head stale_pfns; struct list_head stale_daxs; struct list_node list; /** * struct ndctl_interleave_set - extra info for interleave sets * @state: are any interleave set members active or all idle * @cookie: summary cookie identifying the NFIT config for the set */ struct ndctl_interleave_set { int state; unsigned long long cookie; } iset; struct badblocks_iter bb_iter; enum ndctl_persistence_domain persistence_domain; /* file descriptor for deep flush sysfs entry */ int flush_fd; }; /** * struct ndctl_btt - stacked block device provided sector atomicity * @module: kernel module (nd_btt) * @lbasize: sector size info * @size: usable size of the btt after removing metadata etc * @ndns: host namespace for the btt instance * @region: parent region * @btt_path: btt devpath * @uuid: unique identifier for a btt instance * @btt_buf: space to print paths for bind/unbind operations * @bdev: block device associated with a btt */ struct ndctl_btt { struct kmod_module *module; struct ndctl_region *region; struct ndctl_namespace *ndns; struct list_node list; struct ndctl_lbasize lbasize; unsigned long long size; char *btt_path; char *btt_buf; char *bdev; int buf_len; uuid_t uuid; int id, generation; }; /** * struct ndctl_pfn - reservation for per-page-frame metadata * @module: kernel module (nd_pfn) * @ndns: host namespace for the pfn instance * @loc: host metadata location (ram or pmem (default)) * @align: data offset alignment * @region: parent region * @pfn_path: pfn devpath * @uuid: unique identifier for a pfn instance * @pfn_buf: space to print paths for bind/unbind operations * @bdev: block device associated with a pfn */ struct ndctl_pfn { struct kmod_module *module; struct ndctl_region *region; struct ndctl_namespace *ndns; struct list_node list; enum ndctl_pfn_loc loc; unsigned long align; unsigned long long resource, size; char *pfn_path; char *pfn_buf; char *bdev; int buf_len; uuid_t uuid; int id, generation; struct ndctl_lbasize alignments; }; struct ndctl_dax { struct ndctl_pfn pfn; struct daxctl_region *region; }; /** * ndctl_get_userdata - retrieve stored data pointer from library context * @ctx: ndctl library context * * This might be useful to access from callbacks like a custom logging * function. */ NDCTL_EXPORT void *ndctl_get_userdata(struct ndctl_ctx *ctx) { if (ctx == NULL) return NULL; return ctx->userdata; } /** * ndctl_set_userdata - store custom @userdata in the library context * @ctx: ndctl library context * @userdata: data pointer */ NDCTL_EXPORT void ndctl_set_userdata(struct ndctl_ctx *ctx, void *userdata) { if (ctx == NULL) return; ctx->userdata = userdata; } /** * ndctl_new - instantiate a new library context * @ctx: context to establish * * Returns zero on success and stores an opaque pointer in ctx. The * context is freed by ndctl_unref(), i.e. ndctl_new() implies an * internal ndctl_ref(). */ NDCTL_EXPORT int ndctl_new(struct ndctl_ctx **ctx) { struct daxctl_ctx *daxctl_ctx; struct kmod_ctx *kmod_ctx; struct ndctl_ctx *c; struct udev *udev; const char *env; int rc = 0; udev = udev_new(); if (check_udev(udev) != 0) return -ENXIO; kmod_ctx = kmod_new(NULL, NULL); if (check_kmod(kmod_ctx) != 0) { rc = -ENXIO; goto err_kmod; } rc = daxctl_new(&daxctl_ctx); if (rc) goto err_daxctl; c = calloc(1, sizeof(struct ndctl_ctx)); if (!c) { rc = -ENOMEM; goto err_ctx; } c->refcount = 1; log_init(&c->ctx, "libndctl", "NDCTL_LOG"); c->udev = udev; c->timeout = 5000; list_head_init(&c->busses); info(c, "ctx %p created\n", c); dbg(c, "log_priority=%d\n", c->ctx.log_priority); *ctx = c; env = secure_getenv("NDCTL_TIMEOUT"); if (env != NULL) { unsigned long tmo; char *end; tmo = strtoul(env, &end, 0); if (tmo < ULONG_MAX && !end) c->timeout = tmo; dbg(c, "timeout = %ld\n", tmo); } if (udev) { c->udev = udev; c->udev_queue = udev_queue_new(udev); if (!c->udev_queue) err(c, "failed to retrieve udev queue\n"); } c->kmod_ctx = kmod_ctx; c->daxctl_ctx = daxctl_ctx; return 0; err_ctx: daxctl_unref(daxctl_ctx); err_daxctl: kmod_unref(kmod_ctx); err_kmod: udev_unref(udev); return rc; } NDCTL_EXPORT void ndctl_set_private_data(struct ndctl_ctx *ctx, void *data) { ctx->private_data = data; } NDCTL_EXPORT void *ndctl_get_private_data(struct ndctl_ctx *ctx) { return ctx->private_data; } NDCTL_EXPORT struct daxctl_ctx *ndctl_get_daxctl_ctx(struct ndctl_ctx *ctx) { return ctx->daxctl_ctx; } /** * ndctl_ref - take an additional reference on the context * @ctx: context established by ndctl_new() */ NDCTL_EXPORT struct ndctl_ctx *ndctl_ref(struct ndctl_ctx *ctx) { if (ctx == NULL) return NULL; ctx->refcount++; return ctx; } static void badblocks_iter_free(struct badblocks_iter *bb_iter) { if (bb_iter->file) fclose(bb_iter->file); } static int badblocks_iter_init(struct badblocks_iter *bb_iter, const char *path) { char *bb_path; int rc = 0; /* if the file is already open */ if (bb_iter->file) { fclose(bb_iter->file); bb_iter->file = NULL; } if (asprintf(&bb_path, "%s/badblocks", path) < 0) return -errno; bb_iter->file = fopen(bb_path, "re"); if (!bb_iter->file) { rc = -errno; free(bb_path); return rc; } free(bb_path); return rc; } static struct badblock *badblocks_iter_next(struct badblocks_iter *bb_iter) { int rc; char *buf = NULL; size_t rlen = 0; if (!bb_iter->file) return NULL; rc = getline(&buf, &rlen, bb_iter->file); if (rc == -1) { free(buf); return NULL; } rc = sscanf(buf, "%llu %u", &bb_iter->bb.offset, &bb_iter->bb.len); free(buf); if (rc != 2) { fclose(bb_iter->file); bb_iter->file = NULL; bb_iter->bb.offset = 0; bb_iter->bb.len = 0; return NULL; } return &bb_iter->bb; } static struct badblock *badblocks_iter_first(struct badblocks_iter *bb_iter, struct ndctl_ctx *ctx, const char *path) { int rc; rc = badblocks_iter_init(bb_iter, path); if (rc < 0) return NULL; return badblocks_iter_next(bb_iter); } static void free_namespace(struct ndctl_namespace *ndns, struct list_head *head) { struct ndctl_bb *bb, *next; if (head) list_del_from(head, &ndns->list); list_for_each_safe(&ndns->injected_bb, bb, next, list) free(bb); free(ndns->lbasize.supported); free(ndns->ndns_path); free(ndns->ndns_buf); free(ndns->bdev); free(ndns->alt_name); badblocks_iter_free(&ndns->bb_iter); kmod_module_unref(ndns->module); free(ndns); } static void free_namespaces(struct ndctl_region *region) { struct ndctl_namespace *ndns, *_n; list_for_each_safe(®ion->namespaces, ndns, _n, list) free_namespace(ndns, ®ion->namespaces); } static void free_stale_namespaces(struct ndctl_region *region) { struct ndctl_namespace *ndns, *_n; list_for_each_safe(®ion->stale_namespaces, ndns, _n, list) free_namespace(ndns, ®ion->stale_namespaces); } static void free_btt(struct ndctl_btt *btt, struct list_head *head) { if (head) list_del_from(head, &btt->list); kmod_module_unref(btt->module); free(btt->lbasize.supported); free(btt->btt_path); free(btt->btt_buf); free(btt->bdev); free(btt); } static void free_btts(struct ndctl_region *region) { struct ndctl_btt *btt, *_b; list_for_each_safe(®ion->btts, btt, _b, list) free_btt(btt, ®ion->btts); } static void free_stale_btts(struct ndctl_region *region) { struct ndctl_btt *btt, *_b; list_for_each_safe(®ion->stale_btts, btt, _b, list) free_btt(btt, ®ion->stale_btts); } static void __free_pfn(struct ndctl_pfn *pfn, struct list_head *head, void *to_free) { if (head) list_del_from(head, &pfn->list); kmod_module_unref(pfn->module); free(pfn->pfn_path); free(pfn->pfn_buf); free(pfn->bdev); free(pfn->alignments.supported); free(to_free); } static void free_pfn(struct ndctl_pfn *pfn, struct list_head *head) { __free_pfn(pfn, head, pfn); } static void free_dax(struct ndctl_dax *dax, struct list_head *head) { __free_pfn(&dax->pfn, head, dax); } static void free_pfns(struct ndctl_region *region) { struct ndctl_pfn *pfn, *_b; list_for_each_safe(®ion->pfns, pfn, _b, list) free_pfn(pfn, ®ion->pfns); } static void free_daxs(struct ndctl_region *region) { struct ndctl_dax *dax, *_b; list_for_each_safe(®ion->daxs, dax, _b, pfn.list) free_dax(dax, ®ion->daxs); } static void free_stale_pfns(struct ndctl_region *region) { struct ndctl_pfn *pfn, *_b; list_for_each_safe(®ion->stale_pfns, pfn, _b, list) free_pfn(pfn, ®ion->stale_pfns); } static void free_stale_daxs(struct ndctl_region *region) { struct ndctl_dax *dax, *_b; list_for_each_safe(®ion->stale_daxs, dax, _b, pfn.list) free_dax(dax, ®ion->stale_daxs); } static void free_region(struct ndctl_region *region) { struct ndctl_bus *bus = region->bus; struct ndctl_mapping *mapping, *_m; list_for_each_safe(®ion->mappings, mapping, _m, list) { list_del_from(®ion->mappings, &mapping->list); free(mapping); } free_btts(region); free_stale_btts(region); free_pfns(region); free_stale_pfns(region); free_daxs(region); free_stale_daxs(region); free_namespaces(region); free_stale_namespaces(region); list_del_from(&bus->regions, ®ion->list); kmod_module_unref(region->module); free(region->region_buf); free(region->region_path); badblocks_iter_free(®ion->bb_iter); if (region->flush_fd > 0) close(region->flush_fd); free(region); } static void free_dimm(struct ndctl_dimm *dimm) { if (!dimm) return; free(dimm->unique_id); free(dimm->dimm_buf); free(dimm->dimm_path); if (dimm->module) kmod_module_unref(dimm->module); if (dimm->health_eventfd > -1) close(dimm->health_eventfd); ndctl_cmd_unref(dimm->ndd.cmd_read); free(dimm); } static void free_bus(struct ndctl_bus *bus, struct list_head *head) { struct ndctl_dimm *dimm, *_d; struct ndctl_region *region, *_r; list_for_each_safe(&bus->dimms, dimm, _d, list) { list_del_from(&bus->dimms, &dimm->list); free_dimm(dimm); } list_for_each_safe(&bus->regions, region, _r, list) free_region(region); if (head) list_del_from(head, &bus->list); free(bus->provider); free(bus->bus_path); free(bus->bus_buf); free(bus->wait_probe_path); free(bus->scrub_path); free(bus); } static void free_context(struct ndctl_ctx *ctx) { struct ndctl_bus *bus, *_b; list_for_each_safe(&ctx->busses, bus, _b, list) free_bus(bus, &ctx->busses); free(ctx); } /** * ndctl_unref - drop a context reference count * @ctx: context established by ndctl_new() * * Drop a reference and if the resulting reference count is 0 destroy * the context. */ NDCTL_EXPORT struct ndctl_ctx *ndctl_unref(struct ndctl_ctx *ctx) { if (ctx == NULL) return NULL; ctx->refcount--; if (ctx->refcount > 0) return NULL; udev_queue_unref(ctx->udev_queue); udev_unref(ctx->udev); kmod_unref(ctx->kmod_ctx); daxctl_unref(ctx->daxctl_ctx); info(ctx, "context %p released\n", ctx); free_context(ctx); return NULL; } /** * ndctl_set_log_fn - override default log routine * @ctx: ndctl library context * @log_fn: function to be called for logging messages * * The built-in logging writes to stderr. It can be overridden by a * custom function, to plug log messages into the user's logging * functionality. */ NDCTL_EXPORT void ndctl_set_log_fn(struct ndctl_ctx *ctx, void (*ndctl_log_fn)(struct ndctl_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args)) { ctx->ctx.log_fn = (log_fn) ndctl_log_fn; info(ctx, "custom logging function %p registered\n", ndctl_log_fn); } /** * ndctl_get_log_priority - retrieve current library loglevel (syslog) * @ctx: ndctl library context */ NDCTL_EXPORT int ndctl_get_log_priority(struct ndctl_ctx *ctx) { return ctx->ctx.log_priority; } /** * ndctl_set_log_priority - set log verbosity * @priority: from syslog.h, LOG_ERR, LOG_INFO, LOG_DEBUG * * Note: LOG_DEBUG requires library be built with "configure --enable-debug" */ NDCTL_EXPORT void ndctl_set_log_priority(struct ndctl_ctx *ctx, int priority) { ctx->ctx.log_priority = priority; /* forward the debug level to our internal libdaxctl instance */ daxctl_set_log_priority(ctx->daxctl_ctx, priority); } static char *__dev_path(char *type, int major, int minor, int parent) { char *path, *dev_path; if (asprintf(&path, "/sys/dev/%s/%d:%d%s", type, major, minor, parent ? "/device" : "") < 0) return NULL; dev_path = realpath(path, NULL); free(path); return dev_path; } static char *parent_dev_path(char *type, int major, int minor) { return __dev_path(type, major, minor, 1); } static int device_parse(struct ndctl_ctx *ctx, struct ndctl_bus *bus, const char *base_path, const char *dev_name, void *parent, add_dev_fn add_dev) { if (bus) ndctl_bus_wait_probe(bus); return sysfs_device_parse(ctx, base_path, dev_name, parent, add_dev); } static int to_cmd_index(const char *name, int dimm) { const char *(*cmd_name_fn)(unsigned cmd); int i, end_cmd; if (dimm) { end_cmd = ND_CMD_CALL; cmd_name_fn = nvdimm_cmd_name; } else { end_cmd = ND_CMD_CLEAR_ERROR; cmd_name_fn = nvdimm_bus_cmd_name; } for (i = 1; i <= end_cmd; i++) { const char *cmd_name = cmd_name_fn(i); if (!cmd_name) continue; if (strcmp(name, cmd_name) == 0) return i; } return 0; } static unsigned long parse_commands(char *commands, int dimm) { unsigned long cmd_mask = 0; char *start, *end; start = commands; while ((end = strchr(start, ' '))) { int cmd; *end = '\0'; cmd = to_cmd_index(start, dimm); if (cmd) cmd_mask |= 1 << cmd; start = end + 1; } return cmd_mask; } static void parse_nfit_mem_flags(struct ndctl_dimm *dimm, char *flags) { char *start, *end; start = flags; while ((end = strchr(start, ' '))) { *end = '\0'; if (strcmp(start, "not_armed") == 0) dimm->flags.f_arm = 1; else if (strcmp(start, "save_fail") == 0) dimm->flags.f_save = 1; else if (strcmp(start, "flush_fail") == 0) dimm->flags.f_flush = 1; else if (strcmp(start, "smart_event") == 0) dimm->flags.f_smart = 1; else if (strcmp(start, "restore_fail") == 0) dimm->flags.f_restore = 1; else if (strcmp(start, "map_fail") == 0) dimm->flags.f_map = 1; else if (strcmp(start, "smart_notify") == 0) dimm->flags.f_notify = 1; start = end + 1; } if (end != start) dbg(ndctl_dimm_get_ctx(dimm), "%s: %s\n", ndctl_dimm_get_devname(dimm), flags); } static void parse_dimm_flags(struct ndctl_dimm *dimm, char *flags) { char *start, *end; dimm->locked = 0; dimm->aliased = 0; start = flags; while ((end = strchr(start, ' '))) { *end = '\0'; if (strcmp(start, "lock") == 0) dimm->locked = 1; else if (strcmp(start, "alias") == 0) dimm->aliased = 1; start = end + 1; } if (end != start) dbg(ndctl_dimm_get_ctx(dimm), "%s: %s\n", ndctl_dimm_get_devname(dimm), flags); } static void *add_bus(void *parent, int id, const char *ctl_base) { char buf[SYSFS_ATTR_SIZE]; struct ndctl_ctx *ctx = parent; struct ndctl_bus *bus, *bus_dup; char *path = calloc(1, strlen(ctl_base) + 100); if (!path) return NULL; bus = calloc(1, sizeof(*bus)); if (!bus) goto err_bus; list_head_init(&bus->dimms); list_head_init(&bus->regions); bus->ctx = ctx; bus->id = id; sprintf(path, "%s/dev", ctl_base); if (sysfs_read_attr(ctx, path, buf) < 0 || sscanf(buf, "%d:%d", &bus->major, &bus->minor) != 2) goto err_read; sprintf(path, "%s/device/commands", ctl_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; bus->cmd_mask = parse_commands(buf, 0); sprintf(path, "%s/device/nfit/revision", ctl_base); if (sysfs_read_attr(ctx, path, buf) < 0) { bus->has_nfit = 0; bus->revision = -1; } else { bus->has_nfit = 1; bus->revision = strtoul(buf, NULL, 0); } sprintf(path, "%s/device/nfit/dsm_mask", ctl_base); if (sysfs_read_attr(ctx, path, buf) < 0) bus->nfit_dsm_mask = 0; else bus->nfit_dsm_mask = strtoul(buf, NULL, 0); sprintf(path, "%s/device/provider", ctl_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; bus->provider = strdup(buf); if (!bus->provider) goto err_read; sprintf(path, "%s/device/wait_probe", ctl_base); bus->wait_probe_path = strdup(path); if (!bus->wait_probe_path) goto err_read; sprintf(path, "%s/device/nfit/scrub", ctl_base); bus->scrub_path = strdup(path); if (!bus->scrub_path) goto err_read; bus->bus_path = parent_dev_path("char", bus->major, bus->minor); if (!bus->bus_path) goto err_dev_path; bus->bus_buf = calloc(1, strlen(bus->bus_path) + 50); if (!bus->bus_buf) goto err_read; bus->buf_len = strlen(bus->bus_path) + 50; ndctl_bus_foreach(ctx, bus_dup) if (strcmp(ndctl_bus_get_provider(bus_dup), ndctl_bus_get_provider(bus)) == 0 && strcmp(ndctl_bus_get_devname(bus_dup), ndctl_bus_get_devname(bus)) == 0) { free_bus(bus, NULL); free(path); return bus_dup; } list_add(&ctx->busses, &bus->list); free(path); return bus; err_dev_path: err_read: free(bus->wait_probe_path); free(bus->scrub_path); free(bus->provider); free(bus->bus_buf); free(bus); err_bus: free(path); return NULL; } static void busses_init(struct ndctl_ctx *ctx) { if (ctx->busses_init) return; ctx->busses_init = 1; device_parse(ctx, NULL, "/sys/class/nd", "ndctl", ctx, add_bus); } NDCTL_EXPORT void ndctl_invalidate(struct ndctl_ctx *ctx) { ctx->busses_init = 0; } /** * ndctl_bus_get_first - retrieve first "nd bus" in the system * @ctx: context established by ndctl_new * * Returns an ndctl_bus if an nd bus exists in the system. This return * value can be used to iterate to the next available bus in the system * ia ndctl_bus_get_next() */ NDCTL_EXPORT struct ndctl_bus *ndctl_bus_get_first(struct ndctl_ctx *ctx) { busses_init(ctx); return list_top(&ctx->busses, struct ndctl_bus, list); } /** * ndctl_bus_get_next - retrieve the "next" nd bus in the system * @bus: ndctl_bus instance returned from ndctl_bus_get_{first|next} * * Returns NULL if @bus was the "last" bus available in the system */ NDCTL_EXPORT struct ndctl_bus *ndctl_bus_get_next(struct ndctl_bus *bus) { struct ndctl_ctx *ctx = bus->ctx; return list_next(&ctx->busses, bus, list); } NDCTL_EXPORT int ndctl_bus_has_nfit(struct ndctl_bus *bus) { return bus->has_nfit; } /** * ndctl_bus_get_major - nd bus character device major number * @bus: ndctl_bus instance returned from ndctl_bus_get_{first|next} */ NDCTL_EXPORT unsigned int ndctl_bus_get_major(struct ndctl_bus *bus) { return bus->major; } /** * ndctl_bus_get_minor - nd bus character device minor number * @bus: ndctl_bus instance returned from ndctl_bus_get_{first|next} */ NDCTL_EXPORT unsigned int ndctl_bus_get_minor(struct ndctl_bus *bus) { return bus->minor; } NDCTL_EXPORT const char *ndctl_bus_get_devname(struct ndctl_bus *bus) { return devpath_to_devname(bus->bus_path); } NDCTL_EXPORT struct ndctl_bus *ndctl_bus_get_by_provider(struct ndctl_ctx *ctx, const char *provider) { struct ndctl_bus *bus; ndctl_bus_foreach(ctx, bus) if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0) return bus; return NULL; } NDCTL_EXPORT enum ndctl_persistence_domain ndctl_bus_get_persistence_domain(struct ndctl_bus *bus) { struct ndctl_region *region; enum ndctl_persistence_domain pd = -1; /* iterate through region to get the region persistence domain */ ndctl_region_foreach(bus, region) { /* we are looking for the least persistence domain */ if (pd < region->persistence_domain) pd = region->persistence_domain; } return pd < 0 ? PERSISTENCE_UNKNOWN : pd; } NDCTL_EXPORT struct ndctl_btt *ndctl_region_get_btt_seed(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len; struct ndctl_btt *btt; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/btt_seed", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_btt_foreach(region, btt) if (strcmp(buf, ndctl_btt_get_devname(btt)) == 0) return btt; return NULL; } NDCTL_EXPORT struct ndctl_pfn *ndctl_region_get_pfn_seed(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len; struct ndctl_pfn *pfn; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/pfn_seed", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_pfn_foreach(region, pfn) if (strcmp(buf, ndctl_pfn_get_devname(pfn)) == 0) return pfn; return NULL; } NDCTL_EXPORT struct ndctl_dax *ndctl_region_get_dax_seed(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len; struct ndctl_dax *dax; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/dax_seed", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_dax_foreach(region, dax) if (strcmp(buf, ndctl_dax_get_devname(dax)) == 0) return dax; return NULL; } NDCTL_EXPORT int ndctl_region_get_ro(struct ndctl_region *region) { return region->ro; } NDCTL_EXPORT int ndctl_region_set_ro(struct ndctl_region *region, int ro) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len, rc; if (snprintf(path, len, "%s/read_only", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); return -ENXIO; } ro = !!ro; rc = sysfs_write_attr(ctx, path, ro ? "1\n" : "0\n"); if (rc < 0) return rc; region->ro = ro; return ro; } NDCTL_EXPORT unsigned long long ndctl_region_get_resource(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; int rc; if (snprintf(path, len, "%s/resource", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); errno = ENOMEM; return ULLONG_MAX; } rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) { errno = -rc; return ULLONG_MAX; } return strtoull(buf, NULL, 0); } NDCTL_EXPORT int ndctl_region_deep_flush(struct ndctl_region *region) { int rc = pwrite(region->flush_fd, "1\n", 1, 0); return (rc == -1) ? -errno : 0; } NDCTL_EXPORT const char *ndctl_bus_get_cmd_name(struct ndctl_bus *bus, int cmd) { return nvdimm_bus_cmd_name(cmd); } NDCTL_EXPORT int ndctl_bus_is_cmd_supported(struct ndctl_bus *bus, int cmd) { return !!(bus->cmd_mask & (1ULL << cmd)); } NDCTL_EXPORT unsigned int ndctl_bus_get_revision(struct ndctl_bus *bus) { return bus->revision; } NDCTL_EXPORT unsigned int ndctl_bus_get_id(struct ndctl_bus *bus) { return bus->id; } NDCTL_EXPORT const char *ndctl_bus_get_provider(struct ndctl_bus *bus) { return bus->provider; } NDCTL_EXPORT struct ndctl_ctx *ndctl_bus_get_ctx(struct ndctl_bus *bus) { return bus->ctx; } /** * ndctl_bus_wait_probe - flush bus async probing * @bus: bus to sync * * Upon return this bus's dimm and region devices are probed, the region * child namespace devices are registered, and drivers for namespaces * and btts are loaded (if module policy allows) */ NDCTL_EXPORT int ndctl_bus_wait_probe(struct ndctl_bus *bus) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); unsigned long tmo = ctx->timeout; char buf[SYSFS_ATTR_SIZE]; int rc, sleep = 0; do { rc = sysfs_read_attr(bus->ctx, bus->wait_probe_path, buf); if (rc < 0) break; if (!ctx->udev_queue) break; if (udev_queue_get_queue_is_empty(ctx->udev_queue)) break; sleep++; usleep(1000); } while (ctx->timeout == 0 || tmo-- != 0); if (sleep) dbg(ctx, "waited %d millisecond%s for bus%d...\n", sleep, sleep == 1 ? "" : "s", ndctl_bus_get_id(bus)); return rc < 0 ? -ENXIO : 0; } static int __ndctl_bus_get_scrub_state(struct ndctl_bus *bus, unsigned int *scrub_count, bool *active) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); char buf[SYSFS_ATTR_SIZE]; char in_progress = '\0'; int rc; rc = sysfs_read_attr(ctx, bus->scrub_path, buf); if (rc < 0) return -EOPNOTSUPP; rc = sscanf(buf, "%u%c", scrub_count, &in_progress); if (rc < 0) return -ENXIO; switch (rc) { case 1: *active = false; return 0; case 2: if (in_progress == '+') { *active = true; return 0; } /* fall through */ default: /* unable to read scrub count */ return -ENXIO; } } NDCTL_EXPORT int ndctl_bus_start_scrub(struct ndctl_bus *bus) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); int rc; rc = sysfs_write_attr(ctx, bus->scrub_path, "1\n"); if (rc == -EBUSY) return rc; else if (rc < 0) return -EOPNOTSUPP; return 0; } NDCTL_EXPORT int ndctl_bus_get_scrub_state(struct ndctl_bus *bus) { unsigned int scrub_count = 0; bool active = false; int rc; rc = __ndctl_bus_get_scrub_state(bus, &scrub_count, &active); if (rc < 0) return rc; return active; } NDCTL_EXPORT unsigned int ndctl_bus_get_scrub_count(struct ndctl_bus *bus) { unsigned int scrub_count = 0; bool active = false; int rc; rc = __ndctl_bus_get_scrub_state(bus, &scrub_count, &active); if (rc) { errno = -rc; return UINT_MAX; } return scrub_count; } /** * ndctl_bus_poll_scrub_completion - wait for a scrub to complete * @bus: bus for which to check whether a scrub is in progress * @poll_interval: nr seconds between wake up and re-read the status * @timeout: total number of seconds to wait * * Upon return this bus has completed any in-progress scrubs if @timeout * is 0 otherwise -ETIMEDOUT when @timeout seconds have expired. This * is different from ndctl_cmd_ars_in_progress in that the latter checks * the output of an ars_status command to see if the in-progress flag is * set, i.e. provides the firmware's view of whether a scrub is in * progress. ndctl_bus_wait_for_scrub_completion() instead checks the * kernel's view of whether a scrub is in progress by looking at the * 'scrub' file in sysfs. * * The @poll_interval option changes the frequency at which the kernel * status is polled, but it requires a supporting kernel for that poll * interval to be reflected to the kernel's polling of the ARS * interface. Kernel's with poll interval support limit that polling to * root (CAP_SYS_RAWIO) processes. */ NDCTL_EXPORT int ndctl_bus_poll_scrub_completion(struct ndctl_bus *bus, unsigned int poll_interval, unsigned int timeout) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); const char *provider = ndctl_bus_get_provider(bus); char buf[SYSFS_ATTR_SIZE] = { 0 }; unsigned int scrub_count; struct pollfd fds; char in_progress; int fd = 0, rc; fd = open(bus->scrub_path, O_RDONLY|O_CLOEXEC); if (fd < 0) return -errno; memset(&fds, 0, sizeof(fds)); fds.fd = fd; for (;;) { rc = sysfs_read_attr(ctx, bus->scrub_path, buf); if (rc < 0) { rc = -EOPNOTSUPP; break; } rc = sscanf(buf, "%u%c", &scrub_count, &in_progress); if (rc < 0) { rc = -EOPNOTSUPP; break; } if (rc == 1) { /* scrub complete, break successfully */ rc = 0; break; } else if (rc == 2 && in_progress == '+') { long tmo; if (!timeout) tmo = poll_interval; else if (!poll_interval) tmo = timeout; else tmo = min(poll_interval, timeout); tmo *= 1000; if (tmo == 0) tmo = -1; /* scrub in progress, wait */ rc = poll(&fds, 1, tmo); dbg(ctx, "%s: poll wake: rc: %d status: \'%s\'\n", provider, rc, buf); if (rc > 0) fds.revents = 0; if (pread(fd, buf, 1, 0) == -1) { rc = -errno; break; } if (rc < 0) { rc = -errno; dbg(ctx, "%s: poll error: %s\n", provider, strerror(errno)); break; } else if (rc == 0) { dbg(ctx, "%s: poll timeout: interval: %d timeout: %d\n", provider, poll_interval, timeout); if (!timeout) continue; if (!poll_interval || poll_interval > timeout) { rc = -ETIMEDOUT; break; } if (timeout > poll_interval) timeout -= poll_interval; else if (timeout == poll_interval) { timeout = 1; poll_interval = 0; } } } } if (rc == 0) dbg(ctx, "%s: scrub complete, status: \'%s\'\n", provider, buf); else dbg(ctx, "%s: error waiting for scrub completion: %s\n", provider, strerror(-rc)); if (fd) close (fd); return rc; } NDCTL_EXPORT int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus) { return ndctl_bus_poll_scrub_completion(bus, 0, 0); } static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module, const char *devname); static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath); static struct kmod_module *to_module(struct ndctl_ctx *ctx, const char *alias); static void *add_dimm(void *parent, int id, const char *dimm_base) { int formats, i; struct ndctl_dimm *dimm; char buf[SYSFS_ATTR_SIZE]; struct ndctl_bus *bus = parent; struct ndctl_ctx *ctx = bus->ctx; char *path = calloc(1, strlen(dimm_base) + 100); if (!path) return NULL; sprintf(path, "%s/nfit/formats", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) formats = 1; else formats = clamp(strtoul(buf, NULL, 0), 1UL, 2UL); dimm = calloc(1, sizeof(*dimm) + sizeof(int) * formats); if (!dimm) goto err_dimm; dimm->bus = bus; dimm->id = id; sprintf(path, "%s/dev", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; if (sscanf(buf, "%d:%d", &dimm->major, &dimm->minor) != 2) goto err_read; sprintf(path, "%s/commands", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; dimm->cmd_mask = parse_commands(buf, 1); dimm->dimm_buf = calloc(1, strlen(dimm_base) + 50); if (!dimm->dimm_buf) goto err_read; dimm->buf_len = strlen(dimm_base) + 50; dimm->dimm_path = strdup(dimm_base); if (!dimm->dimm_path) goto err_read; sprintf(path, "%s/modalias", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; dimm->module = to_module(ctx, buf); dimm->handle = -1; dimm->phys_id = -1; dimm->serial = -1; dimm->vendor_id = -1; dimm->device_id = -1; dimm->revision_id = -1; dimm->health_eventfd = -1; dimm->dirty_shutdown = -ENOENT; dimm->subsystem_vendor_id = -1; dimm->subsystem_device_id = -1; dimm->subsystem_revision_id = -1; dimm->manufacturing_date = -1; dimm->manufacturing_location = -1; dimm->cmd_family = -1; dimm->nfit_dsm_mask = ULONG_MAX; for (i = 0; i < formats; i++) dimm->format[i] = -1; sprintf(path, "%s/flags", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) { dimm->locked = -1; dimm->aliased = -1; } else parse_dimm_flags(dimm, buf); if (!ndctl_bus_has_nfit(bus)) goto out; /* * 'unique_id' may not be available on older kernels, so don't * fail if the read fails. */ sprintf(path, "%s/nfit/id", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) { unsigned int b[9]; dimm->unique_id = strdup(buf); if (!dimm->unique_id) goto err_read; if (sscanf(dimm->unique_id, "%02x%02x-%02x-%02x%02x-%02x%02x%02x%02x", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7], &b[8]) == 9) { dimm->manufacturing_date = b[3] << 8 | b[4]; dimm->manufacturing_location = b[2]; } } sprintf(path, "%s/nfit/handle", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; dimm->handle = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/phys_id", dimm_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; dimm->phys_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/serial", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->serial = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/vendor", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->vendor_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/device", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->device_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/rev_id", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->revision_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/dirty_shutdown", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->dirty_shutdown = strtoll(buf, NULL, 0); sprintf(path, "%s/nfit/subsystem_vendor", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->subsystem_vendor_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/subsystem_device", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->subsystem_device_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/subsystem_rev_id", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->subsystem_revision_id = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/family", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->cmd_family = strtoul(buf, NULL, 0); if (dimm->cmd_family == NVDIMM_FAMILY_INTEL) dimm->ops = intel_dimm_ops; if (dimm->cmd_family == NVDIMM_FAMILY_HPE1) dimm->ops = hpe1_dimm_ops; if (dimm->cmd_family == NVDIMM_FAMILY_MSFT) dimm->ops = msft_dimm_ops; if (dimm->cmd_family == NVDIMM_FAMILY_HYPERV) dimm->ops = hyperv_dimm_ops; sprintf(path, "%s/nfit/dsm_mask", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->nfit_dsm_mask = strtoul(buf, NULL, 0); dimm->formats = formats; sprintf(path, "%s/nfit/format", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->format[0] = strtoul(buf, NULL, 0); for (i = 1; i < formats; i++) { sprintf(path, "%s/nfit/format%d", dimm_base, i); if (sysfs_read_attr(ctx, path, buf) == 0) dimm->format[i] = strtoul(buf, NULL, 0); } sprintf(path, "%s/nfit/flags", dimm_base); if (sysfs_read_attr(ctx, path, buf) == 0) parse_nfit_mem_flags(dimm, buf); dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC); out: list_add(&bus->dimms, &dimm->list); free(path); return dimm; err_read: free_dimm(dimm); err_dimm: free(path); return NULL; } static void dimms_init(struct ndctl_bus *bus) { if (bus->dimms_init) return; bus->dimms_init = 1; device_parse(bus->ctx, bus, bus->bus_path, "nmem", bus, add_dimm); } NDCTL_EXPORT struct ndctl_dimm *ndctl_dimm_get_first(struct ndctl_bus *bus) { dimms_init(bus); return list_top(&bus->dimms, struct ndctl_dimm, list); } NDCTL_EXPORT struct ndctl_dimm *ndctl_dimm_get_next(struct ndctl_dimm *dimm) { struct ndctl_bus *bus = dimm->bus; return list_next(&bus->dimms, dimm, list); } NDCTL_EXPORT unsigned int ndctl_dimm_get_handle(struct ndctl_dimm *dimm) { return dimm->handle; } NDCTL_EXPORT unsigned short ndctl_dimm_get_phys_id(struct ndctl_dimm *dimm) { return dimm->phys_id; } NDCTL_EXPORT unsigned short ndctl_dimm_get_vendor(struct ndctl_dimm *dimm) { return dimm->vendor_id; } NDCTL_EXPORT unsigned short ndctl_dimm_get_device(struct ndctl_dimm *dimm) { return dimm->device_id; } NDCTL_EXPORT unsigned short ndctl_dimm_get_revision(struct ndctl_dimm *dimm) { return dimm->revision_id; } NDCTL_EXPORT long long ndctl_dimm_get_dirty_shutdown(struct ndctl_dimm *dimm) { return dimm->dirty_shutdown; } NDCTL_EXPORT unsigned short ndctl_dimm_get_subsystem_vendor( struct ndctl_dimm *dimm) { return dimm->subsystem_vendor_id; } NDCTL_EXPORT unsigned short ndctl_dimm_get_subsystem_device( struct ndctl_dimm *dimm) { return dimm->subsystem_device_id; } NDCTL_EXPORT unsigned short ndctl_dimm_get_subsystem_revision( struct ndctl_dimm *dimm) { return dimm->subsystem_revision_id; } NDCTL_EXPORT unsigned short ndctl_dimm_get_manufacturing_date( struct ndctl_dimm *dimm) { return dimm->manufacturing_date; } NDCTL_EXPORT unsigned char ndctl_dimm_get_manufacturing_location( struct ndctl_dimm *dimm) { return dimm->manufacturing_location; } NDCTL_EXPORT unsigned short ndctl_dimm_get_format(struct ndctl_dimm *dimm) { return dimm->format[0]; } NDCTL_EXPORT int ndctl_dimm_get_formats(struct ndctl_dimm *dimm) { return dimm->formats; } NDCTL_EXPORT int ndctl_dimm_get_formatN(struct ndctl_dimm *dimm, int i) { if (i < dimm->formats && i >= 0) return dimm->format[i]; return -EINVAL; } NDCTL_EXPORT unsigned int ndctl_dimm_get_major(struct ndctl_dimm *dimm) { return dimm->major; } NDCTL_EXPORT unsigned int ndctl_dimm_get_minor(struct ndctl_dimm *dimm) { return dimm->minor; } NDCTL_EXPORT unsigned int ndctl_dimm_get_id(struct ndctl_dimm *dimm) { return dimm->id; } NDCTL_EXPORT const char *ndctl_dimm_get_unique_id(struct ndctl_dimm *dimm) { return dimm->unique_id; } NDCTL_EXPORT unsigned int ndctl_dimm_get_serial(struct ndctl_dimm *dimm) { return dimm->serial; } NDCTL_EXPORT const char *ndctl_dimm_get_devname(struct ndctl_dimm *dimm) { return devpath_to_devname(dimm->dimm_path); } NDCTL_EXPORT const char *ndctl_dimm_get_cmd_name(struct ndctl_dimm *dimm, int cmd) { return nvdimm_cmd_name(cmd); } NDCTL_EXPORT int ndctl_dimm_has_errors(struct ndctl_dimm *dimm) { union dimm_flags flags = dimm->flags; flags.f_notify = 0; return flags.flags != 0; } NDCTL_EXPORT int ndctl_dimm_locked(struct ndctl_dimm *dimm) { return dimm->locked; } NDCTL_EXPORT int ndctl_dimm_aliased(struct ndctl_dimm *dimm) { return dimm->aliased; } NDCTL_EXPORT int ndctl_dimm_has_notifications(struct ndctl_dimm *dimm) { return dimm->flags.f_notify; } NDCTL_EXPORT int ndctl_dimm_failed_save(struct ndctl_dimm *dimm) { return dimm->flags.f_save; } NDCTL_EXPORT int ndctl_dimm_failed_arm(struct ndctl_dimm *dimm) { return dimm->flags.f_arm; } NDCTL_EXPORT int ndctl_dimm_failed_restore(struct ndctl_dimm *dimm) { return dimm->flags.f_restore; } NDCTL_EXPORT int ndctl_dimm_smart_pending(struct ndctl_dimm *dimm) { return dimm->flags.f_smart; } NDCTL_EXPORT int ndctl_dimm_failed_flush(struct ndctl_dimm *dimm) { return dimm->flags.f_flush; } NDCTL_EXPORT int ndctl_dimm_failed_map(struct ndctl_dimm *dimm) { return dimm->flags.f_map; } NDCTL_EXPORT int ndctl_dimm_is_cmd_supported(struct ndctl_dimm *dimm, int cmd) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->cmd_is_supported) return ops->cmd_is_supported(dimm, cmd); return !!(dimm->cmd_mask & (1ULL << cmd)); } NDCTL_EXPORT int ndctl_dimm_get_health_eventfd(struct ndctl_dimm *dimm) { return dimm->health_eventfd; } NDCTL_EXPORT unsigned int ndctl_dimm_get_health(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd = NULL; unsigned int health; struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); const char *devname = ndctl_dimm_get_devname(dimm); int rc; cmd = ndctl_dimm_cmd_new_smart(dimm); if (!cmd) { err(ctx, "%s: no smart command support\n", devname); return UINT_MAX; } rc = ndctl_cmd_submit(cmd); if (rc) { err(ctx, "%s: smart command failed\n", devname); ndctl_cmd_unref(cmd); if (rc < 0) errno = -rc; return UINT_MAX; } health = ndctl_cmd_smart_get_health(cmd); ndctl_cmd_unref(cmd); return health; } NDCTL_EXPORT unsigned int ndctl_dimm_get_flags(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd = NULL; unsigned int flags; struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); const char *devname = ndctl_dimm_get_devname(dimm); int rc; cmd = ndctl_dimm_cmd_new_smart(dimm); if (!cmd) { dbg(ctx, "%s: no smart command support\n", devname); return UINT_MAX; } rc = ndctl_cmd_submit(cmd); if (rc) { dbg(ctx, "%s: smart command failed\n", devname); ndctl_cmd_unref(cmd); if (rc < 0) errno = -rc; return UINT_MAX; } flags = ndctl_cmd_smart_get_flags(cmd); ndctl_cmd_unref(cmd); return flags; } NDCTL_EXPORT int ndctl_dimm_is_flag_supported(struct ndctl_dimm *dimm, unsigned int flag) { unsigned int flags = ndctl_dimm_get_flags(dimm); return (flags == UINT_MAX) ? 0 : !!(flags & flag); } NDCTL_EXPORT unsigned int ndctl_dimm_get_event_flags(struct ndctl_dimm *dimm) { int rc; struct ndctl_cmd *cmd = NULL; unsigned int alarm_flags, event_flags = 0; struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); const char *devname = ndctl_dimm_get_devname(dimm); cmd = ndctl_dimm_cmd_new_smart(dimm); if (!cmd) { err(ctx, "%s: no smart command support\n", devname); return UINT_MAX; } rc = ndctl_cmd_submit(cmd); if (rc) { err(ctx, "%s: smart command failed\n", devname); ndctl_cmd_unref(cmd); if (rc < 0) errno = -rc; return UINT_MAX; } alarm_flags = ndctl_cmd_smart_get_alarm_flags(cmd); if (alarm_flags & ND_SMART_SPARE_TRIP) event_flags |= ND_EVENT_SPARES_REMAINING; if (alarm_flags & ND_SMART_MTEMP_TRIP) event_flags |= ND_EVENT_MEDIA_TEMPERATURE; if (alarm_flags & ND_SMART_CTEMP_TRIP) event_flags |= ND_EVENT_CTRL_TEMPERATURE; if (ndctl_cmd_smart_get_shutdown_state(cmd)) event_flags |= ND_EVENT_UNCLEAN_SHUTDOWN; ndctl_cmd_unref(cmd); return event_flags; } NDCTL_EXPORT unsigned int ndctl_dimm_handle_get_node(struct ndctl_dimm *dimm) { return dimm->handle >> 16 & 0xfff; } NDCTL_EXPORT unsigned int ndctl_dimm_handle_get_socket(struct ndctl_dimm *dimm) { return dimm->handle >> 12 & 0xf; } NDCTL_EXPORT unsigned int ndctl_dimm_handle_get_imc(struct ndctl_dimm *dimm) { return dimm->handle >> 8 & 0xf; } NDCTL_EXPORT unsigned int ndctl_dimm_handle_get_channel(struct ndctl_dimm *dimm) { return dimm->handle >> 4 & 0xf; } NDCTL_EXPORT unsigned int ndctl_dimm_handle_get_dimm(struct ndctl_dimm *dimm) { return dimm->handle & 0xf; } NDCTL_EXPORT struct ndctl_bus *ndctl_dimm_get_bus(struct ndctl_dimm *dimm) { return dimm->bus; } NDCTL_EXPORT struct ndctl_ctx *ndctl_dimm_get_ctx(struct ndctl_dimm *dimm) { return dimm->bus->ctx; } NDCTL_EXPORT int ndctl_dimm_disable(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); const char *devname = ndctl_dimm_get_devname(dimm); if (!ndctl_dimm_is_enabled(dimm)) return 0; ndctl_unbind(ctx, dimm->dimm_path); if (ndctl_dimm_is_enabled(dimm)) { err(ctx, "%s: failed to disable\n", devname); return -EBUSY; } dbg(ctx, "%s: disabled\n", devname); return 0; } NDCTL_EXPORT int ndctl_dimm_enable(struct ndctl_dimm *dimm) { const char *devname = ndctl_dimm_get_devname(dimm); struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); if (ndctl_dimm_is_enabled(dimm)) return 0; ndctl_bind(ctx, dimm->module, devname); if (!ndctl_dimm_is_enabled(dimm)) { err(ctx, "%s: failed to enable\n", devname); return -ENXIO; } dbg(ctx, "%s: enabled\n", devname); return 0; } NDCTL_EXPORT struct ndctl_dimm *ndctl_dimm_get_by_handle(struct ndctl_bus *bus, unsigned int handle) { struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) if (dimm->handle == handle) return dimm; return NULL; } static struct ndctl_dimm *ndctl_dimm_get_by_id(struct ndctl_bus *bus, unsigned int id) { struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) if (ndctl_dimm_get_id(dimm) == id) return dimm; return NULL; } /** * ndctl_bus_get_region_by_physical_address - get region by physical address * @bus: ndctl_bus instance * @address: (System) Physical Address * * If @bus and @address is valid, returns a region address, which * physical address belongs to. */ NDCTL_EXPORT struct ndctl_region *ndctl_bus_get_region_by_physical_address( struct ndctl_bus *bus, unsigned long long address) { unsigned long long region_start, region_end; struct ndctl_region *region; ndctl_region_foreach(bus, region) { region_start = ndctl_region_get_resource(region); region_end = region_start + ndctl_region_get_size(region); if (region_start <= address && address < region_end) return region; } return NULL; } /** * ndctl_bus_get_dimm_by_physical_address - get ndctl_dimm pointer by physical address * @bus: ndctl_bus instance * @address: (System) Physical Address * * Returns address of ndctl_dimm on success. */ NDCTL_EXPORT struct ndctl_dimm *ndctl_bus_get_dimm_by_physical_address( struct ndctl_bus *bus, unsigned long long address) { unsigned int handle; unsigned long long dpa; struct ndctl_region *region; if (!bus) return NULL; region = ndctl_bus_get_region_by_physical_address(bus, address); if (!region) return NULL; if (ndctl_region_get_interleave_ways(region) == 1) { struct ndctl_mapping *mapping = ndctl_mapping_get_first(region); /* No need to ask firmware, there's only one dimm */ if (!mapping) return NULL; return ndctl_mapping_get_dimm(mapping); } /* * Since the region is interleaved, we need to ask firmware about it. * If it supports Translate SPA, the dimm is returned. */ if (ndctl_bus_has_nfit(bus)) { int rc; rc = ndctl_bus_nfit_translate_spa(bus, address, &handle, &dpa); if (rc) return NULL; return ndctl_dimm_get_by_handle(bus, handle); } /* No way to get dimm info */ return NULL; } static int region_set_type(struct ndctl_region *region, char *path) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char buf[SYSFS_ATTR_SIZE]; int rc; sprintf(path, "%s/nstype", region->region_path); rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) return rc; region->nstype = strtoul(buf, NULL, 0); sprintf(path, "%s/set_cookie", region->region_path); if (region->nstype == ND_DEVICE_NAMESPACE_PMEM) { rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) return rc; region->iset.cookie = strtoull(buf, NULL, 0); dbg(ctx, "%s: iset-%#.16llx added\n", ndctl_region_get_devname(region), region->iset.cookie); } return 0; } static enum ndctl_persistence_domain region_get_pd_type(char *name) { if (strncmp("cpu_cache", name, 9) == 0) return PERSISTENCE_CPU_CACHE; else if (strncmp("memory_controller", name, 17) == 0) return PERSISTENCE_MEM_CTRL; else if (strncmp("none", name, 4) == 0) return PERSISTENCE_NONE; else return PERSISTENCE_UNKNOWN; } static void *add_region(void *parent, int id, const char *region_base) { char buf[SYSFS_ATTR_SIZE]; struct ndctl_region *region; struct ndctl_bus *bus = parent; struct ndctl_ctx *ctx = bus->ctx; char *path = calloc(1, strlen(region_base) + 100); int perm; if (!path) return NULL; region = calloc(1, sizeof(*region)); if (!region) goto err_region; list_head_init(®ion->btts); list_head_init(®ion->pfns); list_head_init(®ion->daxs); list_head_init(®ion->stale_btts); list_head_init(®ion->stale_pfns); list_head_init(®ion->stale_daxs); list_head_init(®ion->mappings); list_head_init(®ion->namespaces); list_head_init(®ion->stale_namespaces); region->region_path = (char *) region_base; region->bus = bus; region->id = id; sprintf(path, "%s/size", region_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; region->size = strtoull(buf, NULL, 0); sprintf(path, "%s/mappings", region_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; region->num_mappings = strtoul(buf, NULL, 0); sprintf(path, "%s/nfit/range_index", region_base); if (ndctl_bus_has_nfit(bus)) { if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; region->range_index = strtoul(buf, NULL, 0); } else region->range_index = -1; sprintf(path, "%s/read_only", region_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; region->ro = strtoul(buf, NULL, 0); sprintf(path, "%s/modalias", region_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; region->module = to_module(ctx, buf); sprintf(path, "%s/numa_node", region_base); if (sysfs_read_attr(ctx, path, buf) == 0) region->numa_node = strtol(buf, NULL, 0); else region->numa_node = -1; if (region_set_type(region, path) < 0) goto err_read; region->region_buf = calloc(1, strlen(region_base) + 50); if (!region->region_buf) goto err_read; region->buf_len = strlen(region_base) + 50; region->region_path = strdup(region_base); if (!region->region_path) goto err_read; list_add(&bus->regions, ®ion->list); /* get the persistence domain attrib */ sprintf(path, "%s/persistence_domain", region_base); if (sysfs_read_attr(ctx, path, buf) < 0) region->persistence_domain = PERSISTENCE_UNKNOWN; else region->persistence_domain = region_get_pd_type(buf); sprintf(path, "%s/deep_flush", region_base); region->flush_fd = open(path, O_RDWR | O_CLOEXEC); if (region->flush_fd == -1) goto out; if (pread(region->flush_fd, buf, 1, 0) == -1) { close(region->flush_fd); region->flush_fd = -1; goto out; } perm = strtol(buf, NULL, 0); if (perm == 0) { close(region->flush_fd); region->flush_fd = -1; } out: free(path); return region; err_read: free(region->region_buf); free(region); err_region: free(path); return NULL; } static void regions_init(struct ndctl_bus *bus) { if (bus->regions_init) return; bus->regions_init = 1; device_parse(bus->ctx, bus, bus->bus_path, "region", bus, add_region); } NDCTL_EXPORT struct ndctl_region *ndctl_region_get_first(struct ndctl_bus *bus) { regions_init(bus); return list_top(&bus->regions, struct ndctl_region, list); } NDCTL_EXPORT struct ndctl_region *ndctl_region_get_next(struct ndctl_region *region) { struct ndctl_bus *bus = region->bus; return list_next(&bus->regions, region, list); } NDCTL_EXPORT unsigned int ndctl_region_get_id(struct ndctl_region *region) { return region->id; } NDCTL_EXPORT unsigned int ndctl_region_get_interleave_ways(struct ndctl_region *region) { return max(1U, ndctl_region_get_mappings(region)); } NDCTL_EXPORT unsigned int ndctl_region_get_mappings(struct ndctl_region *region) { return region->num_mappings; } NDCTL_EXPORT unsigned long long ndctl_region_get_size(struct ndctl_region *region) { return region->size; } NDCTL_EXPORT unsigned long long ndctl_region_get_available_size( struct ndctl_region *region) { unsigned int nstype = ndctl_region_get_nstype(region); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int rc, len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; switch (nstype) { case ND_DEVICE_NAMESPACE_PMEM: case ND_DEVICE_NAMESPACE_BLK: break; default: return 0; } if (snprintf(path, len, "%s/available_size", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); errno = ENOMEM; return ULLONG_MAX; } rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) { errno = -rc; return ULLONG_MAX; } return strtoull(buf, NULL, 0); } NDCTL_EXPORT unsigned long long ndctl_region_get_max_available_extent( struct ndctl_region *region) { unsigned int nstype = ndctl_region_get_nstype(region); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int rc, len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; switch (nstype) { case ND_DEVICE_NAMESPACE_PMEM: case ND_DEVICE_NAMESPACE_BLK: break; default: return 0; } if (snprintf(path, len, "%s/max_available_extent", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); errno = ENOMEM; return ULLONG_MAX; } /* fall back to legacy behavior if max extents is not exported */ rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) { dbg(ctx, "max extents attribute not exported on older kernels\n"); errno = -rc; return ULLONG_MAX; } return strtoull(buf, NULL, 0); } NDCTL_EXPORT unsigned int ndctl_region_get_range_index(struct ndctl_region *region) { return region->range_index; } NDCTL_EXPORT unsigned int ndctl_region_get_nstype(struct ndctl_region *region) { return region->nstype; } NDCTL_EXPORT unsigned int ndctl_region_get_type(struct ndctl_region *region) { switch (region->nstype) { case ND_DEVICE_NAMESPACE_IO: case ND_DEVICE_NAMESPACE_PMEM: return ND_DEVICE_REGION_PMEM; default: return ND_DEVICE_REGION_BLK; } } NDCTL_EXPORT struct ndctl_namespace *ndctl_region_get_namespace_seed( struct ndctl_region *region) { struct ndctl_bus *bus = ndctl_region_get_bus(region); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); char *path = region->region_buf; struct ndctl_namespace *ndns; int len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/namespace_seed", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_namespace_foreach(region, ndns) if (strcmp(buf, ndctl_namespace_get_devname(ndns)) == 0) return ndns; return NULL; } static const char *ndctl_device_type_name(int type) { switch (type) { case ND_DEVICE_DIMM: return "dimm"; case ND_DEVICE_REGION_PMEM: return "pmem"; case ND_DEVICE_REGION_BLK: return "blk"; case ND_DEVICE_NAMESPACE_IO: return "namespace_io"; case ND_DEVICE_NAMESPACE_PMEM: return "namespace_pmem"; case ND_DEVICE_NAMESPACE_BLK: return "namespace_blk"; case ND_DEVICE_DAX_PMEM: return "dax_pmem"; default: return "unknown"; } } NDCTL_EXPORT const char *ndctl_region_get_type_name(struct ndctl_region *region) { return ndctl_device_type_name(ndctl_region_get_type(region)); } NDCTL_EXPORT struct ndctl_bus *ndctl_region_get_bus(struct ndctl_region *region) { return region->bus; } NDCTL_EXPORT struct ndctl_ctx *ndctl_region_get_ctx(struct ndctl_region *region) { return region->bus->ctx; } NDCTL_EXPORT struct ndctl_dimm *ndctl_region_get_first_dimm(struct ndctl_region *region) { struct ndctl_bus *bus = region->bus; struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) { struct ndctl_mapping *mapping; ndctl_mapping_foreach(region, mapping) if (mapping->dimm == dimm) return dimm; } return NULL; } NDCTL_EXPORT struct ndctl_dimm *ndctl_region_get_next_dimm(struct ndctl_region *region, struct ndctl_dimm *dimm) { while ((dimm = ndctl_dimm_get_next(dimm))) { struct ndctl_mapping *mapping; ndctl_mapping_foreach(region, mapping) if (mapping->dimm == dimm) return dimm; } return NULL; } NDCTL_EXPORT int ndctl_region_get_numa_node(struct ndctl_region *region) { return region->numa_node; } NDCTL_EXPORT struct badblock *ndctl_region_get_next_badblock(struct ndctl_region *region) { return badblocks_iter_next(®ion->bb_iter); } NDCTL_EXPORT struct badblock *ndctl_region_get_first_badblock(struct ndctl_region *region) { return badblocks_iter_first(®ion->bb_iter, ndctl_region_get_ctx(region), region->region_path); } NDCTL_EXPORT enum ndctl_persistence_domain ndctl_region_get_persistence_domain(struct ndctl_region *region) { return region->persistence_domain; } static struct nd_cmd_vendor_tail *to_vendor_tail(struct ndctl_cmd *cmd) { struct nd_cmd_vendor_tail *tail = (struct nd_cmd_vendor_tail *) (cmd->cmd_buf + sizeof(struct nd_cmd_vendor_hdr) + cmd->vendor->in_length); return tail; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_vendor_specific( struct ndctl_dimm *dimm, unsigned int opcode, size_t input_size, size_t output_size) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_VENDOR)) { dbg(ctx, "unsupported cmd\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_cmd_vendor_hdr) + sizeof(struct nd_cmd_vendor_tail) + input_size + output_size; cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_VENDOR; cmd->size = size; cmd->status = 1; cmd->vendor->opcode = opcode; cmd->vendor->in_length = input_size; cmd->firmware_status = &to_vendor_tail(cmd)->status; to_vendor_tail(cmd)->out_length = output_size; return cmd; } NDCTL_EXPORT ssize_t ndctl_cmd_vendor_set_input(struct ndctl_cmd *cmd, void *buf, unsigned int len) { if (cmd->type != ND_CMD_VENDOR) return -EINVAL; len = min(len, cmd->vendor->in_length); memcpy(cmd->vendor->in_buf, buf, len); return len; } NDCTL_EXPORT ssize_t ndctl_cmd_vendor_get_output_size(struct ndctl_cmd *cmd) { if (cmd->type != ND_CMD_VENDOR) return -EINVAL; /* * When cmd->status is non-zero it contains either a negative * error code, or the number of bytes that are available in the * output buffer. */ if (cmd->status) return cmd->status; return to_vendor_tail(cmd)->out_length; } NDCTL_EXPORT ssize_t ndctl_cmd_vendor_get_output(struct ndctl_cmd *cmd, void *buf, unsigned int len) { ssize_t out_length = ndctl_cmd_vendor_get_output_size(cmd); if (out_length < 0) return out_length; len = min(len, out_length); memcpy(buf, to_vendor_tail(cmd)->out_buf, len); return len; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_size(struct ndctl_dimm *dimm) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_GET_CONFIG_SIZE)) { dbg(ctx, "unsupported cmd\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_cmd_get_config_size); cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_GET_CONFIG_SIZE; cmd->size = size; cmd->status = 1; cmd->firmware_status = &cmd->get_size->status; return cmd; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_read(struct ndctl_cmd *cfg_size) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(cfg_size)); struct ndctl_dimm *dimm = cfg_size->dimm; struct ndctl_cmd *cmd; size_t size; if (cfg_size->type != ND_CMD_GET_CONFIG_SIZE || cfg_size->status != 0) { dbg(ctx, "expected sucessfully completed cfg_size command\n"); return NULL; } if (!dimm || cfg_size->get_size->config_size == 0) { dbg(ctx, "invalid cfg_size\n"); return NULL; } if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_GET_CONFIG_DATA)) { dbg(ctx, "unsupported cmd\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_cmd_get_config_data_hdr) + cfg_size->get_size->max_xfer; cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; cmd->refcount = 1; cmd->type = ND_CMD_GET_CONFIG_DATA; cmd->size = size; cmd->status = 1; cmd->get_data->in_offset = 0; cmd->get_data->in_length = cfg_size->get_size->max_xfer; cmd->firmware_status = &cmd->get_data->status; cmd->iter.init_offset = 0; cmd->iter.offset = &cmd->get_data->in_offset; cmd->iter.xfer = &cmd->get_data->in_length; cmd->iter.max_xfer = cfg_size->get_size->max_xfer; cmd->iter.data = cmd->get_data->out_buf; cmd->iter.total_xfer = cfg_size->get_size->config_size; cmd->iter.total_buf = calloc(1, cmd->iter.total_xfer); cmd->iter.dir = READ; if (!cmd->iter.total_buf) { free(cmd); return NULL; } cmd->source = cfg_size; ndctl_cmd_ref(cfg_size); return cmd; } static void iter_set_extent(struct ndctl_cmd_iter *iter, unsigned int len, unsigned int offset) { iter->init_offset = offset; *iter->offset = offset; *iter->xfer = min(iter->max_xfer, len); iter->total_xfer = len; } NDCTL_EXPORT int ndctl_cmd_cfg_read_set_extent(struct ndctl_cmd *cfg_read, unsigned int len, unsigned int offset) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(cfg_read)); struct ndctl_cmd *cfg_size = cfg_read->source; if (cfg_read->type != ND_CMD_GET_CONFIG_DATA || cfg_read->status <= 0) { dbg(ctx, "expected unsubmitted cfg_read command\n"); return -EINVAL; } if (offset + len > cfg_size->get_size->config_size) { dbg(ctx, "read %d from %d exceeds %d\n", len, offset, cfg_size->get_size->config_size); return -EINVAL; } iter_set_extent(&cfg_read->iter, len, offset); return 0; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_write(struct ndctl_cmd *cfg_read) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(cfg_read)); struct ndctl_dimm *dimm = cfg_read->dimm; struct ndctl_cmd *cmd; size_t size; /* enforce rmw */ if (cfg_read->type != ND_CMD_GET_CONFIG_DATA || cfg_read->status != 0) { dbg(ctx, "expected sucessfully completed cfg_read command\n"); return NULL; } if (!dimm || cfg_read->get_data->in_length == 0) { dbg(ctx, "invalid cfg_read\n"); return NULL; } if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SET_CONFIG_DATA)) { dbg(ctx, "unsupported cmd\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct nd_cmd_set_config_hdr) + cfg_read->iter.max_xfer + 4; cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_SET_CONFIG_DATA; cmd->size = size; cmd->status = 1; cmd->set_data->in_offset = cfg_read->iter.init_offset; cmd->set_data->in_length = cfg_read->iter.max_xfer; cmd->firmware_status = (u32 *) (cmd->cmd_buf + sizeof(struct nd_cmd_set_config_hdr) + cfg_read->iter.max_xfer); cmd->iter.init_offset = cfg_read->iter.init_offset; cmd->iter.offset = &cmd->set_data->in_offset; cmd->iter.xfer = &cmd->set_data->in_length; cmd->iter.max_xfer = cfg_read->iter.max_xfer; cmd->iter.data = cmd->set_data->in_buf; cmd->iter.total_xfer = cfg_read->iter.total_xfer; cmd->iter.total_buf = cfg_read->iter.total_buf; cmd->iter.dir = WRITE; cmd->source = cfg_read; ndctl_cmd_ref(cfg_read); return cmd; } NDCTL_EXPORT unsigned int ndctl_cmd_cfg_size_get_size(struct ndctl_cmd *cfg_size) { if (cfg_size->type == ND_CMD_GET_CONFIG_SIZE && cfg_size->status == 0) return cfg_size->get_size->config_size; return 0; } static ssize_t iter_access(struct ndctl_cmd_iter *iter, unsigned int len, unsigned int offset) { if (offset < iter->init_offset || offset > iter->init_offset + iter->total_xfer || len + offset < len) return -EINVAL; if (len + offset > iter->init_offset + iter->total_xfer) len = iter->total_xfer - offset; return len; } NDCTL_EXPORT ssize_t ndctl_cmd_cfg_read_get_data(struct ndctl_cmd *cfg_read, void *buf, unsigned int _len, unsigned int offset) { struct ndctl_cmd_iter *iter; ssize_t len; if (cfg_read->type != ND_CMD_GET_CONFIG_DATA || cfg_read->status > 0) return -EINVAL; if (cfg_read->status < 0) return cfg_read->status; iter = &cfg_read->iter; len = iter_access(&cfg_read->iter, _len, offset); if (len >= 0) memcpy(buf, iter->total_buf + offset, len); return len; } NDCTL_EXPORT ssize_t ndctl_cmd_cfg_read_get_size(struct ndctl_cmd *cfg_read) { if (cfg_read->type != ND_CMD_GET_CONFIG_DATA || cfg_read->status > 0) return -EINVAL; if (cfg_read->status < 0) return cfg_read->status; return cfg_read->iter.total_xfer; } NDCTL_EXPORT int ndctl_cmd_cfg_write_set_extent(struct ndctl_cmd *cfg_write, unsigned int len, unsigned int offset) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(cfg_write)); struct ndctl_cmd *cfg_size, *cfg_read; if (cfg_write->type != ND_CMD_SET_CONFIG_DATA || cfg_write->status <= 0) { dbg(ctx, "expected unsubmitted cfg_write command\n"); return -EINVAL; } cfg_read = cfg_write->source; cfg_size = cfg_read->source; if (offset + len > cfg_size->get_size->config_size) { dbg(ctx, "write %d from %d exceeds %d\n", len, offset, cfg_size->get_size->config_size); return -EINVAL; } iter_set_extent(&cfg_write->iter, len, offset); return 0; } NDCTL_EXPORT ssize_t ndctl_cmd_cfg_write_set_data(struct ndctl_cmd *cfg_write, void *buf, unsigned int _len, unsigned int offset) { ssize_t len; if (cfg_write->type != ND_CMD_SET_CONFIG_DATA || cfg_write->status < 1) return -EINVAL; if (cfg_write->status < 0) return cfg_write->status; len = iter_access(&cfg_write->iter, _len, offset); if (len >= 0) memcpy(cfg_write->iter.total_buf + offset, buf, len); return len; } NDCTL_EXPORT ssize_t ndctl_cmd_cfg_write_zero_data(struct ndctl_cmd *cfg_write) { struct ndctl_cmd_iter *iter = &cfg_write->iter; if (cfg_write->type != ND_CMD_SET_CONFIG_DATA || cfg_write->status < 1) return -EINVAL; if (cfg_write->status < 0) return cfg_write->status; memset(iter->total_buf + iter->init_offset, 0, iter->total_xfer); return iter->total_xfer; } NDCTL_EXPORT void ndctl_cmd_unref(struct ndctl_cmd *cmd) { if (!cmd) return; if (--cmd->refcount == 0) { if (cmd->source) ndctl_cmd_unref(cmd->source); else free(cmd->iter.total_buf); free(cmd); } } NDCTL_EXPORT void ndctl_cmd_ref(struct ndctl_cmd *cmd) { cmd->refcount++; } NDCTL_EXPORT int ndctl_cmd_get_type(struct ndctl_cmd *cmd) { return cmd->type; } static int to_ioctl_cmd(int cmd, int dimm) { if (!dimm) { switch (cmd) { case ND_CMD_ARS_CAP: return ND_IOCTL_ARS_CAP; case ND_CMD_ARS_START: return ND_IOCTL_ARS_START; case ND_CMD_ARS_STATUS: return ND_IOCTL_ARS_STATUS; case ND_CMD_CLEAR_ERROR: return ND_IOCTL_CLEAR_ERROR; case ND_CMD_CALL: return ND_IOCTL_CALL; default: return 0; }; } switch (cmd) { case ND_CMD_DIMM_FLAGS: return ND_IOCTL_DIMM_FLAGS; case ND_CMD_GET_CONFIG_SIZE: return ND_IOCTL_GET_CONFIG_SIZE; case ND_CMD_GET_CONFIG_DATA: return ND_IOCTL_GET_CONFIG_DATA; case ND_CMD_SET_CONFIG_DATA: return ND_IOCTL_SET_CONFIG_DATA; case ND_CMD_VENDOR: return ND_IOCTL_VENDOR; case ND_CMD_CALL: return ND_IOCTL_CALL; case ND_CMD_VENDOR_EFFECT_LOG_SIZE: case ND_CMD_VENDOR_EFFECT_LOG: default: return 0; } } static const char *ndctl_dimm_get_cmd_subname(struct ndctl_cmd *cmd) { struct ndctl_dimm *dimm = cmd->dimm; struct ndctl_dimm_ops *ops = dimm ? dimm->ops : NULL; if (!dimm || cmd->type != ND_CMD_CALL || !ops || !ops->cmd_desc) return NULL; return ops->cmd_desc(cmd->pkg->nd_command); } NDCTL_EXPORT int ndctl_cmd_xlat_firmware_status(struct ndctl_cmd *cmd) { struct ndctl_dimm *dimm = cmd->dimm; struct ndctl_dimm_ops *ops = dimm ? dimm->ops : NULL; if (!dimm || !ops || !ops->xlat_firmware_status) return 0; return ops->xlat_firmware_status(cmd); } static int do_cmd(int fd, int ioctl_cmd, struct ndctl_cmd *cmd) { int rc; u32 offset; const char *name, *sub_name = NULL; struct ndctl_dimm *dimm = cmd->dimm; struct ndctl_bus *bus = cmd_to_bus(cmd); struct ndctl_cmd_iter *iter = &cmd->iter; struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); if (dimm) { name = ndctl_dimm_get_cmd_name(dimm, cmd->type); sub_name = ndctl_dimm_get_cmd_subname(cmd); } else name = ndctl_bus_get_cmd_name(cmd->bus, cmd->type); if (iter->total_xfer == 0) { rc = ioctl(fd, ioctl_cmd, cmd->cmd_buf); dbg(ctx, "bus: %d dimm: %#x cmd: %s%s%s status: %d fw: %d (%s)\n", bus->id, dimm ? ndctl_dimm_get_handle(dimm) : 0, name, sub_name ? ":" : "", sub_name ? sub_name : "", rc, *(cmd->firmware_status), rc < 0 ? strerror(errno) : "success"); if (rc < 0) return -errno; else return rc; } for (offset = 0; offset < iter->total_xfer; offset += iter->max_xfer) { *(cmd->iter.xfer) = min(iter->total_xfer - offset, iter->max_xfer); *(cmd->iter.offset) = offset; if (iter->dir == WRITE) memcpy(iter->data, iter->total_buf + offset, *(cmd->iter.xfer)); rc = ioctl(fd, ioctl_cmd, cmd->cmd_buf); if (rc < 0) { rc = -errno; break; } if (iter->dir == READ) memcpy(iter->total_buf + offset, iter->data, *(cmd->iter.xfer) - rc); if (*(cmd->firmware_status) || rc) { rc = offset + *(cmd->iter.xfer) - rc; break; } } dbg(ctx, "bus: %d dimm: %#x cmd: %s%s%s total: %d max_xfer: %d status: %d fw: %d (%s)\n", bus->id, dimm ? ndctl_dimm_get_handle(dimm) : 0, name, sub_name ? ":" : "", sub_name ? sub_name : "", iter->total_xfer, iter->max_xfer, rc, *(cmd->firmware_status), rc < 0 ? strerror(errno) : "success"); return rc; } NDCTL_EXPORT int ndctl_cmd_submit(struct ndctl_cmd *cmd) { struct stat st; char path[20], *prefix; unsigned int major, minor, id; int rc = 0, fd, len = sizeof(path); int ioctl_cmd = to_ioctl_cmd(cmd->type, !!cmd->dimm); struct ndctl_bus *bus = cmd_to_bus(cmd); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); if (ioctl_cmd == 0) { rc = -EINVAL; goto out; } if (cmd->dimm) { prefix = "nmem"; id = ndctl_dimm_get_id(cmd->dimm); major = ndctl_dimm_get_major(cmd->dimm); minor = ndctl_dimm_get_minor(cmd->dimm); } else { prefix = "ndctl"; id = ndctl_bus_get_id(cmd->bus); major = ndctl_bus_get_major(cmd->bus); minor = ndctl_bus_get_minor(cmd->bus); } if (snprintf(path, len, "/dev/%s%u", prefix, id) >= len) { rc = -EINVAL; goto out; } fd = open(path, O_RDWR); if (fd < 0) { err(ctx, "failed to open %s: %s\n", path, strerror(errno)); rc = -errno; goto out; } if (fstat(fd, &st) >= 0 && S_ISCHR(st.st_mode) && major(st.st_rdev) == major && minor(st.st_rdev) == minor) { rc = do_cmd(fd, ioctl_cmd, cmd); } else { err(ctx, "failed to validate %s as a control node\n", path); rc = -ENXIO; } close(fd); out: cmd->status = rc; return rc; } NDCTL_EXPORT int ndctl_cmd_submit_xlat(struct ndctl_cmd *cmd) { int rc, xlat_rc; rc = ndctl_cmd_submit(cmd); if (rc < 0) return rc; /* * NOTE: This can lose a positive rc when xlat_rc is non-zero. The * positive rc indicates a buffer underrun from the original command * submission. If the caller cares about that (generally not very * useful), then the xlat function is available separately as well. */ xlat_rc = ndctl_cmd_xlat_firmware_status(cmd); return (xlat_rc == 0) ? rc : xlat_rc; } NDCTL_EXPORT int ndctl_cmd_get_status(struct ndctl_cmd *cmd) { return cmd->status; } NDCTL_EXPORT unsigned int ndctl_cmd_get_firmware_status(struct ndctl_cmd *cmd) { return *(cmd->firmware_status); } NDCTL_EXPORT const char *ndctl_region_get_devname(struct ndctl_region *region) { return devpath_to_devname(region->region_path); } static int is_enabled(struct ndctl_bus *bus, const char *drvpath) { struct stat st; ndctl_bus_wait_probe(bus); if (lstat(drvpath, &st) < 0 || !S_ISLNK(st.st_mode)) return 0; else return 1; } NDCTL_EXPORT int ndctl_region_is_enabled(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *path = region->region_buf; int len = region->buf_len; if (snprintf(path, len, "%s/driver", region->region_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_region_get_devname(region)); return 0; } return is_enabled(ndctl_region_get_bus(region), path); } NDCTL_EXPORT int ndctl_region_enable(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); const char *devname = ndctl_region_get_devname(region); if (ndctl_region_is_enabled(region)) return 0; ndctl_bind(ctx, region->module, devname); if (!ndctl_region_is_enabled(region)) { err(ctx, "%s: failed to enable\n", devname); return -ENXIO; } if (region->refresh_type) { region->refresh_type = 0; region_set_type(region, region->region_buf); } dbg(ctx, "%s: enabled\n", devname); return 0; } void region_flag_refresh(struct ndctl_region *region) { region->refresh_type = 1; } NDCTL_EXPORT void ndctl_region_cleanup(struct ndctl_region *region) { free_stale_namespaces(region); free_stale_btts(region); free_stale_pfns(region); free_stale_daxs(region); } static int ndctl_region_disable(struct ndctl_region *region, int cleanup) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); const char *devname = ndctl_region_get_devname(region); if (!ndctl_region_is_enabled(region)) return 0; ndctl_unbind(ctx, region->region_path); if (ndctl_region_is_enabled(region)) { err(ctx, "%s: failed to disable\n", devname); return -EBUSY; } region->namespaces_init = 0; region->btts_init = 0; region->pfns_init = 0; region->daxs_init = 0; list_append_list(®ion->stale_namespaces, ®ion->namespaces); list_append_list(®ion->stale_btts, ®ion->btts); list_append_list(®ion->stale_pfns, ®ion->pfns); list_append_list(®ion->stale_daxs, ®ion->daxs); region->generation++; if (cleanup) ndctl_region_cleanup(region); dbg(ctx, "%s: disabled\n", devname); return 0; } NDCTL_EXPORT int ndctl_region_disable_invalidate(struct ndctl_region *region) { return ndctl_region_disable(region, 1); } NDCTL_EXPORT int ndctl_region_disable_preserve(struct ndctl_region *region) { return ndctl_region_disable(region, 0); } NDCTL_EXPORT struct ndctl_interleave_set *ndctl_region_get_interleave_set( struct ndctl_region *region) { unsigned int nstype = ndctl_region_get_nstype(region); if (nstype == ND_DEVICE_NAMESPACE_PMEM) return ®ion->iset; return NULL; } NDCTL_EXPORT struct ndctl_region *ndctl_interleave_set_get_region( struct ndctl_interleave_set *iset) { return container_of(iset, struct ndctl_region, iset); } NDCTL_EXPORT struct ndctl_interleave_set *ndctl_interleave_set_get_first( struct ndctl_bus *bus) { struct ndctl_region *region; ndctl_region_foreach(bus, region) { struct ndctl_interleave_set *iset; iset = ndctl_region_get_interleave_set(region); if (iset) return iset; } return NULL; } NDCTL_EXPORT struct ndctl_interleave_set *ndctl_interleave_set_get_next( struct ndctl_interleave_set *iset) { struct ndctl_region *region = ndctl_interleave_set_get_region(iset); iset = NULL; do { region = ndctl_region_get_next(region); if (!region) break; iset = ndctl_region_get_interleave_set(region); if (iset) break; } while (1); return iset; } NDCTL_EXPORT int ndctl_dimm_is_enabled(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); char *path = dimm->dimm_buf; int len = dimm->buf_len; if (snprintf(path, len, "%s/driver", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return 0; } return is_enabled(ndctl_dimm_get_bus(dimm), path); } NDCTL_EXPORT int ndctl_dimm_is_active(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); char *path = dimm->dimm_buf; int len = dimm->buf_len; char buf[20]; if (snprintf(path, len, "%s/state", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return -ENOMEM; } if (sysfs_read_attr(ctx, path, buf) < 0) return -ENXIO; if (strcmp(buf, "active") == 0) return 1; return 0; } NDCTL_EXPORT int ndctl_interleave_set_is_active( struct ndctl_interleave_set *iset) { struct ndctl_dimm *dimm; ndctl_dimm_foreach_in_interleave_set(iset, dimm) { int active = ndctl_dimm_is_active(dimm); if (active) return active; } return 0; } NDCTL_EXPORT unsigned long long ndctl_interleave_set_get_cookie( struct ndctl_interleave_set *iset) { return iset->cookie; } NDCTL_EXPORT struct ndctl_dimm *ndctl_interleave_set_get_first_dimm( struct ndctl_interleave_set *iset) { struct ndctl_region *region = ndctl_interleave_set_get_region(iset); return ndctl_region_get_first_dimm(region); } NDCTL_EXPORT struct ndctl_dimm *ndctl_interleave_set_get_next_dimm( struct ndctl_interleave_set *iset, struct ndctl_dimm *dimm) { struct ndctl_region *region = ndctl_interleave_set_get_region(iset); return ndctl_region_get_next_dimm(region, dimm); } static void mappings_init(struct ndctl_region *region) { char *mapping_path, buf[SYSFS_ATTR_SIZE]; struct ndctl_bus *bus = region->bus; struct ndctl_ctx *ctx = bus->ctx; int i; if (region->mappings_init) return; region->mappings_init = 1; mapping_path = calloc(1, strlen(region->region_path) + 100); if (!mapping_path) { err(ctx, "bus%d region%d: allocation failure\n", bus->id, region->id); return; } for (i = 0; i < region->num_mappings; i++) { struct ndctl_mapping *mapping; unsigned long long offset, length; struct ndctl_dimm *dimm; unsigned int dimm_id; int position, match; sprintf(mapping_path, "%s/mapping%d", region->region_path, i); if (sysfs_read_attr(ctx, mapping_path, buf) < 0) { err(ctx, "bus%d region%d: failed to read mapping%d\n", bus->id, region->id, i); continue; } match = sscanf(buf, "nmem%u,%llu,%llu,%d", &dimm_id, &offset, &length, &position); if (match < 4) position = -1; if (match < 3) { err(ctx, "bus%d mapping parse failure\n", ndctl_bus_get_id(bus)); continue; } dimm = ndctl_dimm_get_by_id(bus, dimm_id); if (!dimm) { err(ctx, "bus%d region%d mapping%d: nmem%d lookup failure\n", bus->id, region->id, i, dimm_id); continue; } mapping = calloc(1, sizeof(*mapping)); if (!mapping) { err(ctx, "bus%d region%d mapping%d: allocation failure\n", bus->id, region->id, i); continue; } mapping->region = region; mapping->offset = offset; mapping->length = length; mapping->dimm = dimm; mapping->position = position; list_add(®ion->mappings, &mapping->list); } free(mapping_path); } NDCTL_EXPORT struct ndctl_mapping *ndctl_mapping_get_first(struct ndctl_region *region) { mappings_init(region); return list_top(®ion->mappings, struct ndctl_mapping, list); } NDCTL_EXPORT struct ndctl_mapping *ndctl_mapping_get_next(struct ndctl_mapping *mapping) { struct ndctl_region *region = mapping->region; return list_next(®ion->mappings, mapping, list); } NDCTL_EXPORT struct ndctl_dimm *ndctl_mapping_get_dimm(struct ndctl_mapping *mapping) { return mapping->dimm; } NDCTL_EXPORT unsigned long long ndctl_mapping_get_offset(struct ndctl_mapping *mapping) { return mapping->offset; } NDCTL_EXPORT unsigned long long ndctl_mapping_get_length(struct ndctl_mapping *mapping) { return mapping->length; } NDCTL_EXPORT int ndctl_mapping_get_position(struct ndctl_mapping *mapping) { return mapping->position; } NDCTL_EXPORT struct ndctl_region *ndctl_mapping_get_region( struct ndctl_mapping *mapping) { return mapping->region; } NDCTL_EXPORT struct ndctl_bus *ndctl_mapping_get_bus( struct ndctl_mapping *mapping) { return ndctl_mapping_get_region(mapping)->bus; } NDCTL_EXPORT struct ndctl_ctx *ndctl_mapping_get_ctx( struct ndctl_mapping *mapping) { return ndctl_mapping_get_bus(mapping)->ctx; } static struct kmod_module *to_module(struct ndctl_ctx *ctx, const char *alias) { struct kmod_list *list = NULL; struct kmod_module *mod; int rc; if (!ctx->kmod_ctx) return NULL; rc = kmod_module_new_from_lookup(ctx->kmod_ctx, alias, &list); if (rc < 0 || !list) { dbg(ctx, "failed to find module for alias: %s %d list: %s\n", alias, rc, list ? "populated" : "empty"); return NULL; } mod = kmod_module_get_module(list); dbg(ctx, "alias: %s module: %s\n", alias, kmod_module_get_name(mod)); kmod_module_unref_list(list); return mod; } static char *get_block_device(struct ndctl_ctx *ctx, const char *block_path) { char *bdev_name = NULL; struct dirent *de; DIR *dir; dir = opendir(block_path); if (!dir) { dbg(ctx, "no block device found: %s\n", block_path); return NULL; } while ((de = readdir(dir)) != NULL) { if (de->d_ino == 0 || de->d_name[0] == '.') continue; if (bdev_name) { dbg(ctx, "invalid block_path format: %s\n", block_path); free(bdev_name); bdev_name = NULL; break; } bdev_name = strdup(de->d_name); } closedir(dir); return bdev_name; } static int parse_lbasize_supported(struct ndctl_ctx *ctx, const char *devname, const char *buf, struct ndctl_lbasize *lba); static const char *enforce_id_to_name(enum ndctl_namespace_mode mode) { static const char *id_to_name[] = { [NDCTL_NS_MODE_MEMORY] = "pfn", [NDCTL_NS_MODE_SAFE] = "btt", /* TODO: convert to btt2 */ [NDCTL_NS_MODE_RAW] = "", [NDCTL_NS_MODE_DAX] = "dax", [NDCTL_NS_MODE_UNKNOWN] = "", }; if (mode < NDCTL_NS_MODE_UNKNOWN && mode >= 0) return id_to_name[mode]; return id_to_name[NDCTL_NS_MODE_UNKNOWN]; } static enum ndctl_namespace_mode enforce_name_to_id(const char *name) { int i; for (i = 0; i < NDCTL_NS_MODE_UNKNOWN; i++) if (strcmp(enforce_id_to_name(i), name) == 0) return i; return NDCTL_NS_MODE_UNKNOWN; } static void *add_namespace(void *parent, int id, const char *ndns_base) { const char *devname = devpath_to_devname(ndns_base); char *path = calloc(1, strlen(ndns_base) + 100); struct ndctl_namespace *ndns, *ndns_dup; struct ndctl_region *region = parent; struct ndctl_bus *bus = region->bus; struct ndctl_ctx *ctx = bus->ctx; char buf[SYSFS_ATTR_SIZE]; if (!path) return NULL; ndns = calloc(1, sizeof(*ndns)); if (!ndns) goto err_namespace; ndns->id = id; ndns->region = region; ndns->generation = region->generation; list_head_init(&ndns->injected_bb); sprintf(path, "%s/nstype", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; ndns->type = strtoul(buf, NULL, 0); sprintf(path, "%s/size", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; ndns->size = strtoull(buf, NULL, 0); sprintf(path, "%s/resource", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) ndns->resource = ULLONG_MAX; else ndns->resource = strtoull(buf, NULL, 0); sprintf(path, "%s/force_raw", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; ndns->raw_mode = strtoul(buf, NULL, 0); sprintf(path, "%s/numa_node", ndns_base); if (sysfs_read_attr(ctx, path, buf) == 0) ndns->numa_node = strtol(buf, NULL, 0); else ndns->numa_node = -1; sprintf(path, "%s/holder_class", ndns_base); if (sysfs_read_attr(ctx, path, buf) == 0) ndns->enforce_mode = enforce_name_to_id(buf); switch (ndns->type) { case ND_DEVICE_NAMESPACE_BLK: case ND_DEVICE_NAMESPACE_PMEM: sprintf(path, "%s/sector_size", ndns_base); if (sysfs_read_attr(ctx, path, buf) == 0) parse_lbasize_supported(ctx, devname, buf, &ndns->lbasize); else if (ndns->type == ND_DEVICE_NAMESPACE_BLK) { /* * sector_size support is mandatory for blk, * optional for pmem. */ goto err_read; } else parse_lbasize_supported(ctx, devname, "", &ndns->lbasize); sprintf(path, "%s/alt_name", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; ndns->alt_name = strdup(buf); if (!ndns->alt_name) goto err_read; sprintf(path, "%s/uuid", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; if (strlen(buf) && uuid_parse(buf, ndns->uuid) < 0) { dbg(ctx, "%s:%s\n", path, buf); goto err_read; } break; default: break; } ndns->ndns_path = strdup(ndns_base); if (!ndns->ndns_path) goto err_read; ndns->ndns_buf = calloc(1, strlen(ndns_base) + 50); if (!ndns->ndns_buf) goto err_read; ndns->buf_len = strlen(ndns_base) + 50; sprintf(path, "%s/modalias", ndns_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; ndns->module = to_module(ctx, buf); ndctl_namespace_foreach(region, ndns_dup) if (ndns_dup->id == ndns->id) { free_namespace(ndns, NULL); free(path); return ndns_dup; } list_add(®ion->namespaces, &ndns->list); free(path); return ndns; err_read: free(ndns->ndns_buf); free(ndns->ndns_path); free(ndns->alt_name); free(ndns); err_namespace: free(path); return NULL; } static void namespaces_init(struct ndctl_region *region) { struct ndctl_bus *bus = region->bus; struct ndctl_ctx *ctx = bus->ctx; char ndns_fmt[20]; if (region->namespaces_init) return; region->namespaces_init = 1; sprintf(ndns_fmt, "namespace%d.", region->id); device_parse(ctx, bus, region->region_path, ndns_fmt, region, add_namespace); } NDCTL_EXPORT struct ndctl_namespace *ndctl_namespace_get_first(struct ndctl_region *region) { namespaces_init(region); return list_top(®ion->namespaces, struct ndctl_namespace, list); } NDCTL_EXPORT struct ndctl_namespace *ndctl_namespace_get_next(struct ndctl_namespace *ndns) { struct ndctl_region *region = ndns->region; return list_next(®ion->namespaces, ndns, list); } NDCTL_EXPORT unsigned int ndctl_namespace_get_id(struct ndctl_namespace *ndns) { return ndns->id; } NDCTL_EXPORT unsigned int ndctl_namespace_get_type(struct ndctl_namespace *ndns) { return ndns->type; } NDCTL_EXPORT const char *ndctl_namespace_get_type_name(struct ndctl_namespace *ndns) { return ndctl_device_type_name(ndns->type); } NDCTL_EXPORT struct ndctl_region *ndctl_namespace_get_region(struct ndctl_namespace *ndns) { return ndns->region; } NDCTL_EXPORT struct ndctl_bus *ndctl_namespace_get_bus(struct ndctl_namespace *ndns) { return ndns->region->bus; } NDCTL_EXPORT struct ndctl_ctx *ndctl_namespace_get_ctx(struct ndctl_namespace *ndns) { return ndns->region->bus->ctx; } NDCTL_EXPORT const char *ndctl_namespace_get_devname(struct ndctl_namespace *ndns) { return devpath_to_devname(ndns->ndns_path); } NDCTL_EXPORT struct ndctl_btt *ndctl_namespace_get_btt(struct ndctl_namespace *ndns) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; struct ndctl_btt *btt; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/holder", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_btt_foreach(region, btt) if (strcmp(buf, ndctl_btt_get_devname(btt)) == 0) return btt; return NULL; } NDCTL_EXPORT struct ndctl_pfn *ndctl_namespace_get_pfn(struct ndctl_namespace *ndns) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; struct ndctl_pfn *pfn; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/holder", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_pfn_foreach(region, pfn) if (strcmp(buf, ndctl_pfn_get_devname(pfn)) == 0) return pfn; return NULL; } NDCTL_EXPORT struct ndctl_dax *ndctl_namespace_get_dax(struct ndctl_namespace *ndns) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; struct ndctl_dax *dax; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/holder", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_dax_foreach(region, dax) if (strcmp(buf, ndctl_dax_get_devname(dax)) == 0) return dax; return NULL; } NDCTL_EXPORT const char *ndctl_namespace_get_block_device(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; if (ndns->bdev) return ndns->bdev; if (snprintf(path, len, "%s/block", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return ""; } ndctl_bus_wait_probe(bus); ndns->bdev = get_block_device(ctx, path); return ndns->bdev ? ndns->bdev : ""; } NDCTL_EXPORT enum ndctl_namespace_mode ndctl_namespace_get_mode( struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/mode", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENOMEM; } if (sysfs_read_attr(ctx, path, buf) < 0) return -ENXIO; if (strcmp("memory", buf) == 0) return NDCTL_NS_MODE_MEMORY; if (strcmp("dax", buf) == 0) return NDCTL_NS_MODE_DAX; if (strcmp("raw", buf) == 0) return NDCTL_NS_MODE_RAW; if (strcmp("safe", buf) == 0) return NDCTL_NS_MODE_SAFE; return -ENXIO; } NDCTL_EXPORT enum ndctl_namespace_mode ndctl_namespace_get_enforce_mode( struct ndctl_namespace *ndns) { return ndns->enforce_mode; } NDCTL_EXPORT int ndctl_namespace_set_enforce_mode(struct ndctl_namespace *ndns, enum ndctl_namespace_mode mode) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; int rc; if (mode < 0 || mode >= NDCTL_NS_MODE_UNKNOWN) return -EINVAL; if (snprintf(path, len, "%s/holder_class", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENOMEM; } rc = sysfs_write_attr(ctx, path, enforce_id_to_name(mode)); if (rc >= 0) ndns->enforce_mode = mode; return rc; } NDCTL_EXPORT int ndctl_namespace_is_valid(struct ndctl_namespace *ndns) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); return ndns->generation == region->generation; } NDCTL_EXPORT int ndctl_namespace_get_raw_mode(struct ndctl_namespace *ndns) { return ndns->raw_mode; } NDCTL_EXPORT int ndctl_namespace_set_raw_mode(struct ndctl_namespace *ndns, int raw_mode) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len, rc; if (snprintf(path, len, "%s/force_raw", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } raw_mode = !!raw_mode; rc = sysfs_write_attr(ctx, path, raw_mode ? "1\n" : "0\n"); if (rc < 0) return rc; ndns->raw_mode = raw_mode; return raw_mode; } NDCTL_EXPORT int ndctl_namespace_is_enabled(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len; if (snprintf(path, len, "%s/driver", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return 0; } return is_enabled(ndctl_namespace_get_bus(ndns), path); } NDCTL_EXPORT struct badblock *ndctl_namespace_get_next_badblock( struct ndctl_namespace *ndns) { return badblocks_iter_next(&ndns->bb_iter); } NDCTL_EXPORT struct badblock *ndctl_namespace_get_first_badblock( struct ndctl_namespace *ndns) { struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); const char *dev = ndctl_namespace_get_devname(ndns); char path[SYSFS_ATTR_SIZE]; ssize_t len = sizeof(path); const char *bdev; if (btt || dax) { dbg(ctx, "%s: badblocks not supported for %s\n", dev, btt ? "btt" : "device-dax"); return NULL; } if (pfn) bdev = ndctl_pfn_get_block_device(pfn); else bdev = ndctl_namespace_get_block_device(ndns); if (!bdev) { dbg(ctx, "%s: failed to determine block device\n", dev); return NULL; } if (snprintf(path, len, "/sys/block/%s", bdev) >= len) { err(ctx, "%s: buffer too small!\n", dev); return NULL; } return badblocks_iter_first(&ndns->bb_iter, ctx, path); } static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module, const char *devname) { DIR *dir; int rc = 0; char path[200]; struct dirent *de; const int len = sizeof(path); if (!devname) { err(ctx, "missing devname\n"); return -EINVAL; } if (module) { rc = kmod_module_probe_insert_module(module, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); if (rc < 0) { err(ctx, "%s: insert failure: %d\n", __func__, rc); return rc; } } if (snprintf(path, len, "/sys/bus/nd/drivers") >= len) { err(ctx, "%s: buffer too small!\n", devname); return -ENXIO; } dir = opendir(path); if (!dir) { err(ctx, "%s: opendir(\"%s\") failed\n", devname, path); return -ENXIO; } while ((de = readdir(dir)) != NULL) { char *drv_path; if (de->d_ino == 0) continue; if (de->d_name[0] == '.') continue; if (asprintf(&drv_path, "%s/%s/bind", path, de->d_name) < 0) { err(ctx, "%s: path allocation failure\n", devname); continue; } rc = sysfs_write_attr_quiet(ctx, drv_path, devname); free(drv_path); if (rc == 0) break; } closedir(dir); if (rc) { dbg(ctx, "%s: bind failed\n", devname); return -ENXIO; } return 0; } static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath) { const char *devname = devpath_to_devname(devpath); char path[200]; const int len = sizeof(path); if (snprintf(path, len, "%s/driver/unbind", devpath) >= len) { err(ctx, "%s: buffer too small!\n", devname); return -ENXIO; } return sysfs_write_attr(ctx, path, devname); } static void *add_btt(void *parent, int id, const char *btt_base); static void *add_pfn(void *parent, int id, const char *pfn_base); static void *add_dax(void *parent, int id, const char *dax_base); static void btts_init(struct ndctl_region *region) { struct ndctl_bus *bus = ndctl_region_get_bus(region); char btt_fmt[20]; if (region->btts_init) return; region->btts_init = 1; sprintf(btt_fmt, "btt%d.", region->id); device_parse(bus->ctx, bus, region->region_path, btt_fmt, region, add_btt); } static void pfns_init(struct ndctl_region *region) { struct ndctl_bus *bus = ndctl_region_get_bus(region); char pfn_fmt[20]; if (region->pfns_init) return; region->pfns_init = 1; sprintf(pfn_fmt, "pfn%d.", region->id); device_parse(bus->ctx, bus, region->region_path, pfn_fmt, region, add_pfn); } static void daxs_init(struct ndctl_region *region) { struct ndctl_bus *bus = ndctl_region_get_bus(region); char dax_fmt[20]; if (region->daxs_init) return; region->daxs_init = 1; sprintf(dax_fmt, "dax%d.", region->id); device_parse(bus->ctx, bus, region->region_path, dax_fmt, region, add_dax); } static void region_refresh_children(struct ndctl_region *region) { region->namespaces_init = 0; region->btts_init = 0; region->pfns_init = 0; region->daxs_init = 0; namespaces_init(region); btts_init(region); pfns_init(region); daxs_init(region); } NDCTL_EXPORT bool ndctl_namespace_is_active(struct ndctl_namespace *ndns) { struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); if ((btt && ndctl_btt_is_enabled(btt)) || (pfn && ndctl_pfn_is_enabled(pfn)) || (dax && ndctl_dax_is_enabled(dax)) || (!btt && !pfn && !dax && ndctl_namespace_is_enabled(ndns))) return true; return false; } /* * Return 0 if enabled, < 0 if failed to enable, and > 0 if claimed by * another device and that device is enabled. In the > 0 case a * subsequent call to ndctl_namespace_is_enabled() will return 'false'. */ NDCTL_EXPORT int ndctl_namespace_enable(struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); struct ndctl_region *region = ndns->region; int rc; if (ndctl_namespace_is_enabled(ndns)) return 0; rc = ndctl_bind(ctx, ndns->module, devname); /* * Rescan now as successfully enabling a namespace device leads * to a new one being created, and potentially btts, pfns, or * daxs being attached */ region_refresh_children(region); if (!ndctl_namespace_is_enabled(ndns)) { struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); if (btt && ndctl_btt_is_enabled(btt)) { dbg(ctx, "%s: enabled via %s\n", devname, ndctl_btt_get_devname(btt)); return 1; } if (pfn && ndctl_pfn_is_enabled(pfn)) { dbg(ctx, "%s: enabled via %s\n", devname, ndctl_pfn_get_devname(pfn)); return 1; } if (dax && ndctl_dax_is_enabled(dax)) { dbg(ctx, "%s: enabled via %s\n", devname, ndctl_dax_get_devname(dax)); return 1; } err(ctx, "%s: failed to enable\n", devname); return rc ? rc : -ENXIO; } rc = 0; dbg(ctx, "%s: enabled\n", devname); return rc; } NDCTL_EXPORT int ndctl_namespace_disable(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); const char *devname = ndctl_namespace_get_devname(ndns); if (!ndctl_namespace_is_enabled(ndns)) return 0; ndctl_unbind(ctx, ndns->ndns_path); if (ndctl_namespace_is_enabled(ndns)) { err(ctx, "%s: failed to disable\n", devname); return -EBUSY; } free(ndns->bdev); ndns->bdev = NULL; dbg(ctx, "%s: disabled\n", devname); return 0; } NDCTL_EXPORT int ndctl_namespace_disable_invalidate(struct ndctl_namespace *ndns) { struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); int rc = 0; if (btt) rc = ndctl_btt_delete(btt); if (pfn) rc = ndctl_pfn_delete(pfn); if (dax) rc = ndctl_dax_delete(dax); if (rc) return rc; return ndctl_namespace_disable(ndns); } NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); const char *bdev = NULL; char path[50]; int fd; if (pfn && ndctl_pfn_is_enabled(pfn)) bdev = ndctl_pfn_get_block_device(pfn); else if (btt && ndctl_btt_is_enabled(btt)) bdev = ndctl_btt_get_block_device(btt); else if (ndctl_namespace_is_enabled(ndns)) bdev = ndctl_namespace_get_block_device(ndns); if (bdev) { sprintf(path, "/dev/%s", bdev); fd = open(path, O_RDWR|O_EXCL); if (fd >= 0) { /* * Got it, now block new mounts while we have it * pinned. */ ndctl_namespace_disable_invalidate(ndns); close(fd); } else { /* * Yes, TOCTOU hole, but if you're racing namespace * creation you have other problems, and there's nothing * stopping the !bdev case from racing to mount an fs or * re-enabling the namepace. */ dbg(ctx, "%s: %s failed exclusive open: %s\n", devname, bdev, strerror(errno)); return -errno; } } else ndctl_namespace_disable_invalidate(ndns); return 0; } static int pmem_namespace_is_configured(struct ndctl_namespace *ndns) { if (ndctl_namespace_get_size(ndns) < ND_MIN_NAMESPACE_SIZE) return 0; if (memcmp(&ndns->uuid, null_uuid, sizeof(null_uuid)) == 0) return 0; return 1; } static int blk_namespace_is_configured(struct ndctl_namespace *ndns) { if (pmem_namespace_is_configured(ndns) == 0) return 0; if (ndctl_namespace_get_sector_size(ndns) == 0) return 0; return 1; } NDCTL_EXPORT int ndctl_namespace_is_configured(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); switch (ndctl_namespace_get_type(ndns)) { case ND_DEVICE_NAMESPACE_PMEM: return pmem_namespace_is_configured(ndns); case ND_DEVICE_NAMESPACE_IO: return 1; case ND_DEVICE_NAMESPACE_BLK: return blk_namespace_is_configured(ndns); default: dbg(ctx, "%s: nstype: %d is_configured() not implemented\n", ndctl_namespace_get_devname(ndns), ndctl_namespace_get_type(ndns)); return -ENXIO; } } NDCTL_EXPORT void ndctl_namespace_get_uuid(struct ndctl_namespace *ndns, uuid_t uu) { memcpy(uu, ndns->uuid, sizeof(uuid_t)); } NDCTL_EXPORT int ndctl_namespace_set_uuid(struct ndctl_namespace *ndns, uuid_t uu) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len, rc; char uuid[40]; if (snprintf(path, len, "%s/uuid", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } uuid_unparse(uu, uuid); rc = sysfs_write_attr(ctx, path, uuid); if (rc != 0) return rc; memcpy(ndns->uuid, uu, sizeof(uuid_t)); return 0; } NDCTL_EXPORT unsigned int ndctl_namespace_get_supported_sector_size( struct ndctl_namespace *ndns, int i) { if (ndns->lbasize.num == 0) return 0; if (i < 0 || i > ndns->lbasize.num) { errno = EINVAL; return UINT_MAX; } else return ndns->lbasize.supported[i]; } NDCTL_EXPORT unsigned int ndctl_namespace_get_sector_size(struct ndctl_namespace *ndns) { return ndctl_namespace_get_supported_sector_size(ndns, ndns->lbasize.select); } NDCTL_EXPORT int ndctl_namespace_get_num_sector_sizes(struct ndctl_namespace *ndns) { return ndns->lbasize.num; } NDCTL_EXPORT int ndctl_namespace_set_sector_size(struct ndctl_namespace *ndns, unsigned int sector_size) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len, rc; char sector_str[40]; int i; for (i = 0; i < ndns->lbasize.num; i++) if (ndns->lbasize.supported[i] == sector_size) break; if (i > ndns->lbasize.num) { err(ctx, "%s: unsupported sector size %d\n", ndctl_namespace_get_devname(ndns), sector_size); return -EOPNOTSUPP; } if (snprintf(path, len, "%s/sector_size", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } sprintf(sector_str, "%d\n", sector_size); rc = sysfs_write_attr(ctx, path, sector_str); if (rc != 0) return rc; ndns->lbasize.select = i; return 0; } NDCTL_EXPORT const char *ndctl_namespace_get_alt_name(struct ndctl_namespace *ndns) { if (ndns->alt_name) return ndns->alt_name; return ""; } NDCTL_EXPORT int ndctl_namespace_set_alt_name(struct ndctl_namespace *ndns, const char *alt_name) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len, rc; char *buf; if (!ndns->alt_name) return 0; if (strlen(alt_name) >= (size_t) NSLABEL_NAME_LEN) return -EINVAL; if (snprintf(path, len, "%s/alt_name", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } buf = strdup(alt_name); if (!buf) return -ENOMEM; rc = sysfs_write_attr(ctx, path, buf); if (rc < 0) { free(buf); return rc; } free(ndns->alt_name); ndns->alt_name = buf; return 0; } NDCTL_EXPORT unsigned long long ndctl_namespace_get_size(struct ndctl_namespace *ndns) { return ndns->size; } NDCTL_EXPORT unsigned long long ndctl_namespace_get_resource(struct ndctl_namespace *ndns) { return ndns->resource; } static int namespace_set_size(struct ndctl_namespace *ndns, unsigned long long size) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char *path = ndns->ndns_buf; int len = ndns->buf_len, rc; char buf[SYSFS_ATTR_SIZE]; if (snprintf(path, len, "%s/size", ndns->ndns_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } sprintf(buf, "%#llx\n", size); rc = sysfs_write_attr(ctx, path, buf); if (rc < 0) return rc; ndns->size = size; return 0; } NDCTL_EXPORT int ndctl_namespace_set_size(struct ndctl_namespace *ndns, unsigned long long size) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); if (size == 0) { dbg(ctx, "%s: use ndctl_namespace_delete() instead\n", ndctl_namespace_get_devname(ndns)); return -EINVAL; } if (ndctl_namespace_is_enabled(ndns)) return -EBUSY; switch (ndctl_namespace_get_type(ndns)) { case ND_DEVICE_NAMESPACE_PMEM: case ND_DEVICE_NAMESPACE_BLK: return namespace_set_size(ndns, size); default: dbg(ctx, "%s: nstype: %d set size failed\n", ndctl_namespace_get_devname(ndns), ndctl_namespace_get_type(ndns)); return -ENXIO; } } NDCTL_EXPORT int ndctl_namespace_get_numa_node(struct ndctl_namespace *ndns) { return ndns->numa_node; } static int __ndctl_namespace_set_write_cache(struct ndctl_namespace *ndns, int state) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); char *path = ndns->ndns_buf; char buf[SYSFS_ATTR_SIZE]; int len = ndns->buf_len; const char *bdev; if (state != 1 && state != 0) return -ENXIO; if (pfn) bdev = ndctl_pfn_get_block_device(pfn); else bdev = ndctl_namespace_get_block_device(ndns); if (!bdev) return -ENXIO; if (snprintf(path, len, "/sys/block/%s/dax/write_cache", bdev) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } sprintf(buf, "%d\n", state); return sysfs_write_attr(ctx, path, buf); } NDCTL_EXPORT int ndctl_namespace_enable_write_cache( struct ndctl_namespace *ndns) { return __ndctl_namespace_set_write_cache(ndns, 1); } NDCTL_EXPORT int ndctl_namespace_disable_write_cache( struct ndctl_namespace *ndns) { return __ndctl_namespace_set_write_cache(ndns, 0); } NDCTL_EXPORT int ndctl_namespace_write_cache_is_enabled( struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); int len = ndns->buf_len, wc; char *path = ndns->ndns_buf; char buf[SYSFS_ATTR_SIZE]; const char *bdev; if (pfn) bdev = ndctl_pfn_get_block_device(pfn); else bdev = ndctl_namespace_get_block_device(ndns); if (!bdev) return -ENXIO; if (snprintf(path, len, "/sys/block/%s/dax/write_cache", bdev) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } if (sysfs_read_attr(ctx, path, buf) < 0) return -ENXIO; if (sscanf(buf, "%d", &wc) == 1) if (wc) return 1; return 0; } NDCTL_EXPORT int ndctl_namespace_delete(struct ndctl_namespace *ndns) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); int rc; if (!ndctl_namespace_is_valid(ndns)) { free_namespace(ndns, ®ion->stale_namespaces); return 0; } if (ndctl_namespace_is_enabled(ndns)) return -EBUSY; switch (ndctl_namespace_get_type(ndns)) { case ND_DEVICE_NAMESPACE_PMEM: case ND_DEVICE_NAMESPACE_BLK: break; default: dbg(ctx, "%s: nstype: %d not deletable\n", ndctl_namespace_get_devname(ndns), ndctl_namespace_get_type(ndns)); return 0; } rc = namespace_set_size(ndns, 0); /* * if the namespace has already been deleted, this will return * -ENXIO due to the uuid check in __size_store. We can safely * ignore it in the case of writing a zero. */ if (rc && (rc != -ENXIO)) return rc; region->namespaces_init = 0; free_namespace(ndns, ®ion->namespaces); return 0; } static int parse_lbasize_supported(struct ndctl_ctx *ctx, const char *devname, const char *buf, struct ndctl_lbasize *lba) { char *s = strdup(buf), *end, *field; void *temp; if (!s) return -ENOMEM; field = s; lba->num = 0; end = strchr(s, ' '); lba->select = -1; lba->supported = NULL; while (end) { unsigned int val; *end = '\0'; if (sscanf(field, "[%d]", &val) == 1) { if (lba->select >= 0) goto err; lba->select = lba->num; } else if (sscanf(field, "%d", &val) == 1) { /* pass */; } else { break; } temp = realloc(lba->supported, sizeof(unsigned int) * ++lba->num); if (temp != NULL) lba->supported = temp; else goto err; lba->supported[lba->num - 1] = val; field = end + 1; end = strchr(field, ' '); } free(s); dbg(ctx, "%s: %s\n", devname, buf); return 0; err: free(s); free(lba->supported); lba->supported = NULL; lba->select = -1; return -ENXIO; } static void *add_btt(void *parent, int id, const char *btt_base) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(parent); const char *devname = devpath_to_devname(btt_base); char *path = calloc(1, strlen(btt_base) + 100); struct ndctl_region *region = parent; struct ndctl_btt *btt, *btt_dup; char buf[SYSFS_ATTR_SIZE]; if (!path) return NULL; btt = calloc(1, sizeof(*btt)); if (!btt) goto err_btt; btt->id = id; btt->region = region; btt->generation = region->generation; btt->btt_path = strdup(btt_base); if (!btt->btt_path) goto err_read; btt->btt_buf = calloc(1, strlen(btt_base) + 50); if (!btt->btt_buf) goto err_read; btt->buf_len = strlen(btt_base) + 50; sprintf(path, "%s/modalias", btt_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; btt->module = to_module(ctx, buf); sprintf(path, "%s/uuid", btt_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; if (strlen(buf) && uuid_parse(buf, btt->uuid) < 0) goto err_read; sprintf(path, "%s/sector_size", btt_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; if (parse_lbasize_supported(ctx, devname, buf, &btt->lbasize) < 0) goto err_read; sprintf(path, "%s/size", btt_base); if (sysfs_read_attr(ctx, path, buf) < 0) btt->size = ULLONG_MAX; else btt->size = strtoull(buf, NULL, 0); free(path); ndctl_btt_foreach(region, btt_dup) if (btt->id == btt_dup->id) { btt_dup->size = btt->size; free_btt(btt, NULL); return btt_dup; } list_add(®ion->btts, &btt->list); return btt; err_read: free(btt->lbasize.supported); free(btt->btt_buf); free(btt->btt_path); free(btt); err_btt: free(path); return NULL; } NDCTL_EXPORT struct ndctl_btt *ndctl_btt_get_first(struct ndctl_region *region) { btts_init(region); return list_top(®ion->btts, struct ndctl_btt, list); } NDCTL_EXPORT struct ndctl_btt *ndctl_btt_get_next(struct ndctl_btt *btt) { struct ndctl_region *region = btt->region; return list_next(®ion->btts, btt, list); } NDCTL_EXPORT unsigned int ndctl_btt_get_id(struct ndctl_btt *btt) { return btt->id; } NDCTL_EXPORT unsigned int ndctl_btt_get_supported_sector_size( struct ndctl_btt *btt, int i) { if (i < 0 || i > btt->lbasize.num) { errno = EINVAL; return UINT_MAX; } else return btt->lbasize.supported[i]; } NDCTL_EXPORT unsigned int ndctl_btt_get_sector_size(struct ndctl_btt *btt) { return ndctl_btt_get_supported_sector_size(btt, btt->lbasize.select); } NDCTL_EXPORT int ndctl_btt_get_num_sector_sizes(struct ndctl_btt *btt) { return btt->lbasize.num; } NDCTL_EXPORT struct ndctl_namespace *ndctl_btt_get_namespace(struct ndctl_btt *btt) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); struct ndctl_namespace *ndns, *found = NULL; struct ndctl_region *region = btt->region; char *path = region->region_buf; int len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; if (btt->ndns) return btt->ndns; if (snprintf(path, len, "%s/namespace", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_btt_get_devname(btt)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_namespace_foreach(region, ndns) if (strcmp(buf, ndctl_namespace_get_devname(ndns)) == 0) found = ndns; btt->ndns = found; return found; } NDCTL_EXPORT void ndctl_btt_get_uuid(struct ndctl_btt *btt, uuid_t uu) { memcpy(uu, btt->uuid, sizeof(uuid_t)); } NDCTL_EXPORT unsigned long long ndctl_btt_get_size(struct ndctl_btt *btt) { return btt->size; } NDCTL_EXPORT int ndctl_btt_set_uuid(struct ndctl_btt *btt, uuid_t uu) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); char *path = btt->btt_buf; int len = btt->buf_len, rc; char uuid[40]; if (snprintf(path, len, "%s/uuid", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_btt_get_devname(btt)); return -ENXIO; } uuid_unparse(uu, uuid); rc = sysfs_write_attr(ctx, path, uuid); if (rc != 0) return rc; memcpy(btt->uuid, uu, sizeof(uuid_t)); return 0; } NDCTL_EXPORT int ndctl_btt_set_sector_size(struct ndctl_btt *btt, unsigned int sector_size) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); char *path = btt->btt_buf; int len = btt->buf_len, rc; char sector_str[40]; int i; if (snprintf(path, len, "%s/sector_size", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_btt_get_devname(btt)); return -ENXIO; } sprintf(sector_str, "%d\n", sector_size); rc = sysfs_write_attr(ctx, path, sector_str); if (rc != 0) return rc; for (i = 0; i < btt->lbasize.num; i++) if (btt->lbasize.supported[i] == sector_size) btt->lbasize.select = i; return 0; } NDCTL_EXPORT int ndctl_btt_set_namespace(struct ndctl_btt *btt, struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); int len = btt->buf_len, rc; char *path = btt->btt_buf; if (snprintf(path, len, "%s/namespace", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_btt_get_devname(btt)); return -ENXIO; } rc = sysfs_write_attr(ctx, path, ndns ? ndctl_namespace_get_devname(ndns) : "\n"); if (rc != 0) return rc; btt->ndns = ndns; return 0; } NDCTL_EXPORT struct ndctl_bus *ndctl_btt_get_bus(struct ndctl_btt *btt) { return btt->region->bus; } NDCTL_EXPORT struct ndctl_ctx *ndctl_btt_get_ctx(struct ndctl_btt *btt) { return ndctl_bus_get_ctx(ndctl_btt_get_bus(btt)); } NDCTL_EXPORT const char *ndctl_btt_get_devname(struct ndctl_btt *btt) { return devpath_to_devname(btt->btt_path); } NDCTL_EXPORT const char *ndctl_btt_get_block_device(struct ndctl_btt *btt) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); struct ndctl_bus *bus = ndctl_btt_get_bus(btt); char *path = btt->btt_buf; int len = btt->buf_len; if (btt->bdev) return btt->bdev; if (snprintf(path, len, "%s/block", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_btt_get_devname(btt)); return ""; } ndctl_bus_wait_probe(bus); btt->bdev = get_block_device(ctx, path); return btt->bdev ? btt->bdev : ""; } NDCTL_EXPORT int ndctl_btt_is_valid(struct ndctl_btt *btt) { struct ndctl_region *region = ndctl_btt_get_region(btt); return btt->generation == region->generation; } NDCTL_EXPORT int ndctl_btt_is_enabled(struct ndctl_btt *btt) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); char *path = btt->btt_buf; int len = btt->buf_len; if (snprintf(path, len, "%s/driver", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_btt_get_devname(btt)); return 0; } return is_enabled(ndctl_btt_get_bus(btt), path); } NDCTL_EXPORT struct ndctl_region *ndctl_btt_get_region(struct ndctl_btt *btt) { return btt->region; } NDCTL_EXPORT int ndctl_btt_enable(struct ndctl_btt *btt) { struct ndctl_region *region = ndctl_btt_get_region(btt); const char *devname = ndctl_btt_get_devname(btt); struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); char *path = btt->btt_buf; int len = btt->buf_len; if (ndctl_btt_is_enabled(btt)) return 0; ndctl_bind(ctx, btt->module, devname); if (!ndctl_btt_is_enabled(btt)) { err(ctx, "%s: failed to enable\n", devname); return -ENXIO; } dbg(ctx, "%s: enabled\n", devname); if (snprintf(path, len, "%s/block", btt->btt_path) >= len) { err(ctx, "%s: buffer too small!\n", devname); } else { btt->bdev = get_block_device(ctx, path); } /* * Rescan now as successfully enabling a btt device leads to a * new one being created, and potentially the backing namespace * as well. */ region_refresh_children(region); return 0; } NDCTL_EXPORT int ndctl_btt_delete(struct ndctl_btt *btt) { struct ndctl_region *region = ndctl_btt_get_region(btt); struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); int rc; if (!ndctl_btt_is_valid(btt)) { free_btt(btt, ®ion->stale_btts); return 0; } ndctl_unbind(ctx, btt->btt_path); rc = ndctl_btt_set_namespace(btt, NULL); if (rc) { dbg(ctx, "%s: failed to clear namespace: %d\n", ndctl_btt_get_devname(btt), rc); return rc; } free_btt(btt, ®ion->btts); region->btts_init = 0; return 0; } NDCTL_EXPORT int ndctl_btt_is_configured(struct ndctl_btt *btt) { if (ndctl_btt_get_namespace(btt)) return 1; if (ndctl_btt_get_sector_size(btt) != UINT_MAX) return 1; if (memcmp(&btt->uuid, null_uuid, sizeof(null_uuid)) != 0) return 1; return 0; } static void *__add_pfn(struct ndctl_pfn *pfn, const char *pfn_base) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(pfn->region); char *path = calloc(1, strlen(pfn_base) + 100); struct ndctl_region *region = pfn->region; char buf[SYSFS_ATTR_SIZE]; if (!path) return NULL; pfn->generation = region->generation; pfn->pfn_path = strdup(pfn_base); if (!pfn->pfn_path) goto err_read; pfn->pfn_buf = calloc(1, strlen(pfn_base) + 50); if (!pfn->pfn_buf) goto err_read; pfn->buf_len = strlen(pfn_base) + 50; sprintf(path, "%s/modalias", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; pfn->module = to_module(ctx, buf); sprintf(path, "%s/uuid", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; if (strlen(buf) && uuid_parse(buf, pfn->uuid) < 0) goto err_read; sprintf(path, "%s/mode", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) goto err_read; if (strcmp(buf, "none") == 0) pfn->loc = NDCTL_PFN_LOC_NONE; else if (strcmp(buf, "ram") == 0) pfn->loc = NDCTL_PFN_LOC_RAM; else if (strcmp(buf, "pmem") == 0) pfn->loc = NDCTL_PFN_LOC_PMEM; else goto err_read; sprintf(path, "%s/align", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) pfn->align = 0; else pfn->align = strtoul(buf, NULL, 0); sprintf(path, "%s/resource", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) pfn->resource = ULLONG_MAX; else pfn->resource = strtoull(buf, NULL, 0); sprintf(path, "%s/size", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) pfn->size = ULLONG_MAX; else pfn->size = strtoull(buf, NULL, 0); /* * The supported_alignments attribute was added before arches other * than x86 had pmem support. If the kernel doesn't provide the * attribute then it's safe to assume that we running on x86 where * 4KiB and 2MiB have always been supported. */ sprintf(path, "%s/supported_alignments", pfn_base); if (sysfs_read_attr(ctx, path, buf) < 0) sprintf(buf, "%d %d", SZ_4K, SZ_2M); if (parse_lbasize_supported(ctx, pfn_base, buf, &pfn->alignments) < 0) goto err_read; free(path); return pfn; err_read: free(pfn->pfn_buf); free(pfn->pfn_path); free(path); return NULL; } static void *add_pfn(void *parent, int id, const char *pfn_base) { struct ndctl_pfn *pfn = calloc(1, sizeof(*pfn)), *pfn_dup; struct ndctl_region *region = parent; if (!pfn) return NULL; pfn->id = id; pfn->region = region; if (!__add_pfn(pfn, pfn_base)) { free(pfn); return NULL; } ndctl_pfn_foreach(region, pfn_dup) if (pfn->id == pfn_dup->id) { pfn_dup->resource = pfn->resource; pfn_dup->size = pfn->size; free_pfn(pfn, NULL); return pfn_dup; } list_add(®ion->pfns, &pfn->list); return pfn; } static void *add_dax(void *parent, int id, const char *dax_base) { struct ndctl_dax *dax = calloc(1, sizeof(*dax)), *dax_dup; struct ndctl_region *region = parent; struct ndctl_pfn *pfn = &dax->pfn; if (!dax) return NULL; pfn->id = id; pfn->region = region; if (!__add_pfn(pfn, dax_base)) { free(dax); return NULL; } ndctl_dax_foreach(region, dax_dup) { struct ndctl_pfn *pfn_dup = &dax_dup->pfn; if (pfn->id == pfn_dup->id) { pfn_dup->resource = pfn->resource; pfn_dup->size = pfn->size; free_dax(dax, NULL); return dax_dup; } } list_add(®ion->daxs, &dax->pfn.list); return dax; } NDCTL_EXPORT struct ndctl_pfn *ndctl_pfn_get_first(struct ndctl_region *region) { pfns_init(region); return list_top(®ion->pfns, struct ndctl_pfn, list); } NDCTL_EXPORT struct ndctl_pfn *ndctl_pfn_get_next(struct ndctl_pfn *pfn) { struct ndctl_region *region = pfn->region; return list_next(®ion->pfns, pfn, list); } NDCTL_EXPORT unsigned int ndctl_pfn_get_id(struct ndctl_pfn *pfn) { return pfn->id; } NDCTL_EXPORT struct ndctl_namespace *ndctl_pfn_get_namespace(struct ndctl_pfn *pfn) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); struct ndctl_namespace *ndns, *found = NULL; struct ndctl_region *region = pfn->region; char *path = region->region_buf; int len = region->buf_len; char buf[SYSFS_ATTR_SIZE]; if (pfn->ndns) return pfn->ndns; if (snprintf(path, len, "%s/namespace", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return NULL; } if (sysfs_read_attr(ctx, path, buf) < 0) return NULL; ndctl_namespace_foreach(region, ndns) if (strcmp(buf, ndctl_namespace_get_devname(ndns)) == 0) found = ndns; pfn->ndns = found; return found; } NDCTL_EXPORT void ndctl_pfn_get_uuid(struct ndctl_pfn *pfn, uuid_t uu) { memcpy(uu, pfn->uuid, sizeof(uuid_t)); } NDCTL_EXPORT unsigned long long ndctl_pfn_get_size(struct ndctl_pfn *pfn) { return pfn->size; } NDCTL_EXPORT unsigned long long ndctl_pfn_get_resource(struct ndctl_pfn *pfn) { return pfn->resource; } NDCTL_EXPORT int ndctl_pfn_set_uuid(struct ndctl_pfn *pfn, uuid_t uu) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); int len = pfn->buf_len, rc; char *path = pfn->pfn_buf; char uuid[40]; if (snprintf(path, len, "%s/uuid", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return -ENXIO; } uuid_unparse(uu, uuid); rc = sysfs_write_attr(ctx, path, uuid); if (rc != 0) return rc; memcpy(pfn->uuid, uu, sizeof(uuid_t)); return 0; } NDCTL_EXPORT enum ndctl_pfn_loc ndctl_pfn_get_location(struct ndctl_pfn *pfn) { return pfn->loc; } NDCTL_EXPORT int ndctl_pfn_set_location(struct ndctl_pfn *pfn, enum ndctl_pfn_loc loc) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); int len = pfn->buf_len, rc; char *path = pfn->pfn_buf; const char *locations[] = { [NDCTL_PFN_LOC_NONE] = "none", [NDCTL_PFN_LOC_RAM] = "ram", [NDCTL_PFN_LOC_PMEM] = "pmem", }; switch (loc) { case NDCTL_PFN_LOC_NONE: case NDCTL_PFN_LOC_RAM: case NDCTL_PFN_LOC_PMEM: break; default: return -EINVAL; } if (snprintf(path, len, "%s/mode", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return -ENXIO; } rc = sysfs_write_attr(ctx, path, locations[loc]); if (rc != 0) return rc; pfn->loc = loc; return 0; } NDCTL_EXPORT unsigned long ndctl_pfn_get_align(struct ndctl_pfn *pfn) { return pfn->align; } NDCTL_EXPORT int ndctl_pfn_has_align(struct ndctl_pfn *pfn) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); char *path = pfn->pfn_buf; int len = pfn->buf_len; struct stat st; if (snprintf(path, len, "%s/align", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return 0; } return stat(path, &st) == 0; } NDCTL_EXPORT int ndctl_pfn_set_align(struct ndctl_pfn *pfn, unsigned long align) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); int len = pfn->buf_len, rc; char *path = pfn->pfn_buf; char align_str[40]; if (snprintf(path, len, "%s/align", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return -ENXIO; } sprintf(align_str, "%lu\n", align); rc = sysfs_write_attr(ctx, path, align_str); if (rc != 0) return rc; pfn->align = align; return 0; } NDCTL_EXPORT int ndctl_pfn_get_num_alignments(struct ndctl_pfn *pfn) { return pfn->alignments.num; } NDCTL_EXPORT unsigned long ndctl_pfn_get_supported_alignment( struct ndctl_pfn *pfn, int i) { if (pfn->alignments.num == 0) return 0; if (i < 0 || i > pfn->alignments.num) return -EINVAL; else return pfn->alignments.supported[i]; } NDCTL_EXPORT int ndctl_pfn_set_namespace(struct ndctl_pfn *pfn, struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); int len = pfn->buf_len, rc; char *path = pfn->pfn_buf; if (snprintf(path, len, "%s/namespace", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return -ENXIO; } rc = sysfs_write_attr(ctx, path, ndns ? ndctl_namespace_get_devname(ndns) : "\n"); if (rc != 0) return rc; pfn->ndns = ndns; return 0; } NDCTL_EXPORT struct ndctl_bus *ndctl_pfn_get_bus(struct ndctl_pfn *pfn) { return pfn->region->bus; } NDCTL_EXPORT struct ndctl_ctx *ndctl_pfn_get_ctx(struct ndctl_pfn *pfn) { return ndctl_bus_get_ctx(ndctl_pfn_get_bus(pfn)); } NDCTL_EXPORT const char *ndctl_pfn_get_devname(struct ndctl_pfn *pfn) { return devpath_to_devname(pfn->pfn_path); } NDCTL_EXPORT const char *ndctl_pfn_get_block_device(struct ndctl_pfn *pfn) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); struct ndctl_bus *bus = ndctl_pfn_get_bus(pfn); char *path = pfn->pfn_buf; int len = pfn->buf_len; if (pfn->bdev) return pfn->bdev; if (snprintf(path, len, "%s/block", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return ""; } ndctl_bus_wait_probe(bus); pfn->bdev = get_block_device(ctx, path); return pfn->bdev ? pfn->bdev : ""; } NDCTL_EXPORT int ndctl_pfn_is_valid(struct ndctl_pfn *pfn) { struct ndctl_region *region = ndctl_pfn_get_region(pfn); return pfn->generation == region->generation; } NDCTL_EXPORT int ndctl_pfn_is_enabled(struct ndctl_pfn *pfn) { struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); char *path = pfn->pfn_buf; int len = pfn->buf_len; if (snprintf(path, len, "%s/driver", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_pfn_get_devname(pfn)); return 0; } return is_enabled(ndctl_pfn_get_bus(pfn), path); } NDCTL_EXPORT struct ndctl_region *ndctl_pfn_get_region(struct ndctl_pfn *pfn) { return pfn->region; } NDCTL_EXPORT int ndctl_pfn_enable(struct ndctl_pfn *pfn) { struct ndctl_region *region = ndctl_pfn_get_region(pfn); const char *devname = ndctl_pfn_get_devname(pfn); struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); char *path = pfn->pfn_buf; int len = pfn->buf_len; if (ndctl_pfn_is_enabled(pfn)) return 0; ndctl_bind(ctx, pfn->module, devname); if (!ndctl_pfn_is_enabled(pfn)) { err(ctx, "%s: failed to enable\n", devname); return -ENXIO; } dbg(ctx, "%s: enabled\n", devname); if (snprintf(path, len, "%s/block", pfn->pfn_path) >= len) { err(ctx, "%s: buffer too small!\n", devname); } else { pfn->bdev = get_block_device(ctx, path); } /* * Rescan now as successfully enabling a pfn device leads to a * new one being created, and potentially the backing namespace * as well. */ region_refresh_children(region); return 0; } NDCTL_EXPORT int ndctl_pfn_delete(struct ndctl_pfn *pfn) { struct ndctl_region *region = ndctl_pfn_get_region(pfn); struct ndctl_ctx *ctx = ndctl_pfn_get_ctx(pfn); int rc; if (!ndctl_pfn_is_valid(pfn)) { free_pfn(pfn, ®ion->stale_pfns); return 0; } ndctl_unbind(ctx, pfn->pfn_path); rc = ndctl_pfn_set_namespace(pfn, NULL); if (rc) { dbg(ctx, "%s: failed to clear namespace: %d\n", ndctl_pfn_get_devname(pfn), rc); return rc; } free_pfn(pfn, ®ion->pfns); region->pfns_init = 0; return 0; } NDCTL_EXPORT int ndctl_pfn_is_configured(struct ndctl_pfn *pfn) { if (ndctl_pfn_get_namespace(pfn)) return 1; if (ndctl_pfn_get_location(pfn) != NDCTL_PFN_LOC_NONE) return 1; if (memcmp(&pfn->uuid, null_uuid, sizeof(null_uuid)) != 0) return 1; return 0; } NDCTL_EXPORT struct ndctl_dax *ndctl_dax_get_first(struct ndctl_region *region) { daxs_init(region); return list_top(®ion->daxs, struct ndctl_dax, pfn.list); } NDCTL_EXPORT struct ndctl_dax *ndctl_dax_get_next(struct ndctl_dax *dax) { struct ndctl_region *region = dax->pfn.region; return list_next(®ion->daxs, dax, pfn.list); } NDCTL_EXPORT unsigned int ndctl_dax_get_id(struct ndctl_dax *dax) { return ndctl_pfn_get_id(&dax->pfn); } NDCTL_EXPORT struct ndctl_namespace *ndctl_dax_get_namespace(struct ndctl_dax *dax) { return ndctl_pfn_get_namespace(&dax->pfn); } NDCTL_EXPORT void ndctl_dax_get_uuid(struct ndctl_dax *dax, uuid_t uu) { ndctl_pfn_get_uuid(&dax->pfn, uu); } NDCTL_EXPORT unsigned long long ndctl_dax_get_size(struct ndctl_dax *dax) { return ndctl_pfn_get_size(&dax->pfn); } NDCTL_EXPORT unsigned long long ndctl_dax_get_resource(struct ndctl_dax *dax) { return ndctl_pfn_get_resource(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_set_uuid(struct ndctl_dax *dax, uuid_t uu) { return ndctl_pfn_set_uuid(&dax->pfn, uu); } NDCTL_EXPORT enum ndctl_pfn_loc ndctl_dax_get_location(struct ndctl_dax *dax) { return ndctl_pfn_get_location(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_set_location(struct ndctl_dax *dax, enum ndctl_pfn_loc loc) { return ndctl_pfn_set_location(&dax->pfn, loc); } NDCTL_EXPORT unsigned long ndctl_dax_get_align(struct ndctl_dax *dax) { return ndctl_pfn_get_align(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_get_num_alignments(struct ndctl_dax *dax) { return ndctl_pfn_get_num_alignments(&dax->pfn); } NDCTL_EXPORT unsigned long ndctl_dax_get_supported_alignment( struct ndctl_dax *dax, int i) { return ndctl_pfn_get_supported_alignment(&dax->pfn, i); } NDCTL_EXPORT int ndctl_dax_has_align(struct ndctl_dax *dax) { return ndctl_pfn_has_align(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_set_align(struct ndctl_dax *dax, unsigned long align) { return ndctl_pfn_set_align(&dax->pfn, align); } NDCTL_EXPORT int ndctl_dax_set_namespace(struct ndctl_dax *dax, struct ndctl_namespace *ndns) { return ndctl_pfn_set_namespace(&dax->pfn, ndns); } NDCTL_EXPORT struct ndctl_bus *ndctl_dax_get_bus(struct ndctl_dax *dax) { return ndctl_pfn_get_bus(&dax->pfn); } NDCTL_EXPORT struct ndctl_ctx *ndctl_dax_get_ctx(struct ndctl_dax *dax) { return ndctl_pfn_get_ctx(&dax->pfn); } NDCTL_EXPORT const char *ndctl_dax_get_devname(struct ndctl_dax *dax) { return ndctl_pfn_get_devname(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_is_valid(struct ndctl_dax *dax) { return ndctl_pfn_is_valid(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_is_enabled(struct ndctl_dax *dax) { return ndctl_pfn_is_enabled(&dax->pfn); } NDCTL_EXPORT struct ndctl_region *ndctl_dax_get_region(struct ndctl_dax *dax) { return ndctl_pfn_get_region(&dax->pfn); } NDCTL_EXPORT int ndctl_dax_enable(struct ndctl_dax *dax) { struct ndctl_region *region = ndctl_dax_get_region(dax); const char *devname = ndctl_dax_get_devname(dax); struct ndctl_ctx *ctx = ndctl_dax_get_ctx(dax); struct ndctl_pfn *pfn = &dax->pfn; if (ndctl_dax_is_enabled(dax)) return 0; ndctl_bind(ctx, pfn->module, devname); if (!ndctl_dax_is_enabled(dax)) { err(ctx, "%s: failed to enable\n", devname); return -ENXIO; } dbg(ctx, "%s: enabled\n", devname); /* * Rescan now as successfully enabling a dax device leads to a * new one being created, and potentially the backing namespace * as well. */ region_refresh_children(region); return 0; } NDCTL_EXPORT int ndctl_dax_delete(struct ndctl_dax *dax) { struct ndctl_region *region = ndctl_dax_get_region(dax); struct ndctl_ctx *ctx = ndctl_dax_get_ctx(dax); struct ndctl_pfn *pfn = &dax->pfn; int rc; if (!ndctl_dax_is_valid(dax)) { free_dax(dax, ®ion->stale_daxs); return 0; } ndctl_unbind(ctx, pfn->pfn_path); rc = ndctl_dax_set_namespace(dax, NULL); if (rc) { dbg(ctx, "%s: failed to clear namespace: %d\n", ndctl_dax_get_devname(dax), rc); return rc; } free_dax(dax, ®ion->daxs); region->daxs_init = 0; return 0; } NDCTL_EXPORT int ndctl_dax_is_configured(struct ndctl_dax *dax) { return ndctl_pfn_is_configured(&dax->pfn); } NDCTL_EXPORT struct daxctl_region *ndctl_dax_get_daxctl_region( struct ndctl_dax *dax) { struct ndctl_ctx *ctx = ndctl_dax_get_ctx(dax); struct ndctl_region *region; uuid_t uuid; int id; if (dax->region) return dax->region; region = ndctl_dax_get_region(dax); id = ndctl_region_get_id(region); ndctl_dax_get_uuid(dax, uuid); dax->region = daxctl_new_region(ctx->daxctl_ctx, id, uuid, dax->pfn.pfn_path); return dax->region; } ndctl-67/ndctl/lib/libndctl.pc.in000066400000000000000000000003751355562357700170410ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libndctl Description: Manage "libnvdimm" subsystem devices (Non-volatile Memory) Version: @VERSION@ Libs: -L${libdir} -lndctl Libs.private: Cflags: -I${includedir} ndctl-67/ndctl/lib/libndctl.sym000066400000000000000000000257361355562357700166520ustar00rootroot00000000000000LIBNDCTL_1 { global: ndctl_min_namespace_size; ndctl_sizeof_namespace_index; ndctl_sizeof_namespace_label; ndctl_get_userdata; ndctl_set_userdata; ndctl_ref; ndctl_get_log_priority; ndctl_set_log_fn; ndctl_unref; ndctl_set_log_priority; ndctl_new; ndctl_get_daxctl_ctx; ndctl_set_private_data; ndctl_get_private_data; ndctl_invalidate; local: *; }; LIBNDCTL_3 { global: ndctl_bus_get_first; ndctl_bus_get_next; ndctl_bus_get_ctx; ndctl_bus_get_major; ndctl_bus_get_minor; ndctl_bus_get_devname; ndctl_bus_get_by_provider; ndctl_bus_get_cmd_name; ndctl_bus_is_cmd_supported; ndctl_bus_has_nfit; ndctl_bus_get_revision; ndctl_bus_get_id; ndctl_bus_get_provider; ndctl_bus_get_ctx; ndctl_bus_wait_probe; ndctl_dimm_get_first; ndctl_dimm_get_next; ndctl_dimm_get_handle; ndctl_dimm_get_phys_id; ndctl_dimm_get_vendor; ndctl_dimm_get_device; ndctl_dimm_get_revision; ndctl_dimm_get_manufacturing_date; ndctl_dimm_get_manufacturing_location; ndctl_dimm_get_subsystem_vendor; ndctl_dimm_get_subsystem_device; ndctl_dimm_get_subsystem_revision; ndctl_dimm_get_format; ndctl_dimm_get_formats; ndctl_dimm_get_formatN; ndctl_dimm_get_major; ndctl_dimm_get_minor; ndctl_dimm_get_serial; ndctl_dimm_get_id; ndctl_dimm_get_unique_id; ndctl_dimm_get_devname; ndctl_dimm_get_cmd_name; ndctl_dimm_has_errors; ndctl_dimm_has_notifications; ndctl_dimm_failed_save; ndctl_dimm_failed_map; ndctl_dimm_failed_arm; ndctl_dimm_failed_restore; ndctl_dimm_smart_pending; ndctl_dimm_failed_flush; ndctl_dimm_get_health_eventfd; ndctl_dimm_is_cmd_supported; ndctl_dimm_handle_get_node; ndctl_dimm_handle_get_socket; ndctl_dimm_handle_get_imc; ndctl_dimm_handle_get_channel; ndctl_dimm_handle_get_dimm; ndctl_dimm_get_bus; ndctl_dimm_get_ctx; ndctl_dimm_get_by_handle; ndctl_dimm_is_active; ndctl_dimm_is_enabled; ndctl_dimm_disable; ndctl_dimm_enable; ndctl_bus_cmd_new_ars_cap; ndctl_bus_cmd_new_ars_start; ndctl_bus_cmd_new_ars_status; ndctl_cmd_ars_cap_get_size; ndctl_cmd_ars_cap_get_range; ndctl_cmd_ars_in_progress; ndctl_cmd_ars_num_records; ndctl_cmd_ars_get_record_addr; ndctl_cmd_ars_get_record_len; ndctl_bus_cmd_new_clear_error; ndctl_cmd_clear_error_get_cleared; ndctl_dimm_cmd_new_vendor_specific; ndctl_cmd_vendor_set_input; ndctl_cmd_vendor_get_output_size; ndctl_cmd_vendor_get_output; ndctl_dimm_cmd_new_cfg_size; ndctl_dimm_cmd_new_cfg_read; ndctl_dimm_cmd_new_cfg_write; ndctl_dimm_cmd_new_smart; ndctl_cmd_smart_get_flags; ndctl_cmd_smart_get_health; ndctl_cmd_smart_get_temperature; ndctl_cmd_smart_get_spares; ndctl_cmd_smart_get_alarm_flags; ndctl_cmd_smart_get_life_used; ndctl_cmd_smart_get_shutdown_state; ndctl_cmd_smart_get_vendor_size; ndctl_cmd_smart_get_vendor_data; ndctl_dimm_cmd_new_smart_threshold; ndctl_cmd_smart_threshold_get_alarm_control; ndctl_cmd_smart_threshold_get_temperature; ndctl_cmd_smart_threshold_get_spares; ndctl_dimm_zero_labels; ndctl_dimm_get_available_labels; ndctl_region_get_first; ndctl_region_get_next; ndctl_region_get_id; ndctl_region_get_devname; ndctl_region_get_interleave_ways; ndctl_region_get_range_index; ndctl_region_get_mappings; ndctl_region_get_size; ndctl_region_get_available_size; ndctl_region_get_type; ndctl_region_get_namespace_seed; ndctl_region_get_btt_seed; ndctl_region_get_type_name; ndctl_region_get_bus; ndctl_region_get_ctx; ndctl_region_get_first_dimm; ndctl_region_get_next_dimm; ndctl_region_is_enabled; ndctl_region_enable; ndctl_region_disable_invalidate; ndctl_region_disable_preserve; ndctl_region_cleanup; ndctl_region_get_interleave_set; ndctl_region_get_ro; ndctl_region_set_ro; ndctl_region_get_resource; ndctl_region_get_first_badblock; ndctl_region_get_next_badblock; ndctl_interleave_set_get_first; ndctl_interleave_set_get_next; ndctl_interleave_set_is_active; ndctl_interleave_set_get_cookie; ndctl_interleave_set_get_region; ndctl_interleave_set_get_first_dimm; ndctl_interleave_set_get_next_dimm; ndctl_mapping_get_first; ndctl_mapping_get_next; ndctl_mapping_get_dimm; ndctl_mapping_get_ctx; ndctl_mapping_get_bus; ndctl_mapping_get_region; ndctl_mapping_get_offset; ndctl_mapping_get_length; ndctl_namespace_get_first; ndctl_namespace_get_next; ndctl_namespace_get_ctx; ndctl_namespace_get_bus; ndctl_namespace_get_btt; ndctl_namespace_get_pfn; ndctl_namespace_get_dax; ndctl_namespace_get_region; ndctl_namespace_get_id; ndctl_namespace_get_devname; ndctl_namespace_get_block_device; ndctl_namespace_get_mode; ndctl_region_get_nstype; ndctl_namespace_get_type; ndctl_namespace_get_type_name; ndctl_namespace_is_enabled; ndctl_namespace_enable; ndctl_namespace_disable; ndctl_namespace_disable_invalidate; ndctl_namespace_disable_safe; ndctl_namespace_is_active; ndctl_namespace_is_valid; ndctl_namespace_is_configured; ndctl_namespace_delete; ndctl_namespace_set_uuid; ndctl_namespace_get_uuid; ndctl_namespace_get_alt_name; ndctl_namespace_set_alt_name; ndctl_namespace_get_size; ndctl_namespace_set_size; ndctl_namespace_get_resource; ndctl_namespace_get_supported_sector_size; ndctl_namespace_get_sector_size; ndctl_namespace_get_num_sector_sizes; ndctl_namespace_set_sector_size; ndctl_namespace_get_raw_mode; ndctl_namespace_set_raw_mode; ndctl_namespace_get_numa_node; ndctl_btt_get_first; ndctl_btt_get_next; ndctl_btt_get_ctx; ndctl_btt_get_bus; ndctl_btt_get_region; ndctl_btt_get_id; ndctl_btt_get_supported_sector_size; ndctl_btt_get_sector_size; ndctl_btt_get_num_sector_sizes; ndctl_btt_get_namespace; ndctl_btt_get_uuid; ndctl_btt_get_size; ndctl_btt_is_enabled; ndctl_btt_is_valid; ndctl_btt_get_devname; ndctl_btt_get_block_device; ndctl_btt_set_uuid; ndctl_btt_set_sector_size; ndctl_btt_set_namespace; ndctl_btt_enable; ndctl_btt_delete; ndctl_btt_is_configured; ndctl_cmd_cfg_size_get_size; ndctl_cmd_cfg_read_get_data; ndctl_cmd_cfg_read_get_size; ndctl_cmd_cfg_write_set_data; ndctl_cmd_cfg_write_zero_data; ndctl_cmd_unref; ndctl_cmd_ref; ndctl_cmd_get_type; ndctl_cmd_get_status; ndctl_cmd_get_firmware_status; ndctl_cmd_submit; ndctl_region_get_pfn_seed; ndctl_pfn_get_first; ndctl_pfn_get_next; ndctl_pfn_get_id; ndctl_pfn_get_namespace; ndctl_pfn_get_uuid; ndctl_pfn_set_uuid; ndctl_pfn_get_location; ndctl_pfn_set_location; ndctl_pfn_get_align; ndctl_pfn_get_size; ndctl_pfn_get_resource; ndctl_pfn_has_align; ndctl_pfn_set_align; ndctl_pfn_set_namespace; ndctl_pfn_get_bus; ndctl_pfn_get_ctx; ndctl_pfn_get_devname; ndctl_pfn_get_block_device; ndctl_pfn_is_valid; ndctl_pfn_is_enabled; ndctl_pfn_get_region; ndctl_pfn_enable; ndctl_pfn_delete; ndctl_pfn_is_configured; ndctl_region_get_dax_seed; ndctl_namespace_get_dax; ndctl_dax_get_first; ndctl_dax_get_next; ndctl_dax_get_id; ndctl_dax_get_namespace; ndctl_dax_get_uuid; ndctl_dax_get_size; ndctl_dax_get_resource; ndctl_dax_set_uuid; ndctl_dax_get_location; ndctl_dax_set_location; ndctl_dax_get_align; ndctl_dax_has_align; ndctl_dax_set_align; ndctl_dax_set_namespace; ndctl_dax_get_bus; ndctl_dax_get_ctx; ndctl_dax_get_devname; ndctl_dax_is_valid; ndctl_dax_is_enabled; ndctl_dax_get_region; ndctl_dax_enable; ndctl_dax_delete; ndctl_dax_is_configured; ndctl_dax_get_daxctl_region; } LIBNDCTL_1; LIBNDCTL_13 { global: ndctl_bus_get_region_by_physical_address; ndctl_bus_get_dimm_by_physical_address; ndctl_bus_is_nfit_cmd_supported; ndctl_dimm_read_labels; ndctl_dimm_validate_labels; ndctl_dimm_init_labels; ndctl_dimm_sizeof_namespace_label; ndctl_mapping_get_position; ndctl_namespace_set_enforce_mode; ndctl_namespace_get_enforce_mode; } LIBNDCTL_3; LIBNDCTL_14 { global: ndctl_dimm_locked; ndctl_dimm_aliased; ndctl_cmd_smart_get_shutdown_count; ndctl_bus_wait_for_scrub_completion; ndctl_bus_get_scrub_count; ndctl_bus_has_error_injection; ndctl_namespace_inject_error; ndctl_namespace_uninject_error; ndctl_namespace_injection_status; ndctl_namespace_injection_get_first_bb; ndctl_namespace_injection_get_next_bb; ndctl_bb_get_block; ndctl_bb_get_count; ndctl_cmd_smart_get_media_temperature; ndctl_cmd_smart_threshold_get_media_temperature; ndctl_cmd_smart_get_ctrl_temperature; ndctl_cmd_smart_threshold_get_ctrl_temperature; ndctl_dimm_cmd_new_smart_set_threshold; ndctl_cmd_smart_threshold_get_supported_alarms; ndctl_cmd_smart_threshold_set_alarm_control; ndctl_cmd_smart_threshold_set_temperature; ndctl_cmd_smart_threshold_set_media_temperature; ndctl_cmd_smart_threshold_set_ctrl_temperature; ndctl_cmd_smart_threshold_set_spares; ndctl_decode_smart_temperature; ndctl_encode_smart_temperature; ndctl_dimm_cmd_new_smart_inject; ndctl_cmd_smart_inject_media_temperature; ndctl_cmd_smart_inject_spares; ndctl_cmd_smart_inject_fatal; ndctl_cmd_smart_inject_unsafe_shutdown; ndctl_dimm_cmd_new_fw_get_info; ndctl_dimm_cmd_new_fw_start_update; ndctl_dimm_cmd_new_fw_send; ndctl_dimm_cmd_new_fw_finish; ndctl_dimm_cmd_new_fw_abort; ndctl_dimm_cmd_new_fw_finish_query; ndctl_cmd_fw_info_get_storage_size; ndctl_cmd_fw_info_get_max_send_len; ndctl_cmd_fw_info_get_query_interval; ndctl_cmd_fw_info_get_max_query_time; ndctl_cmd_fw_info_get_run_version; ndctl_cmd_fw_info_get_updated_version; ndctl_cmd_fw_start_get_context; ndctl_cmd_fw_fquery_get_fw_rev; ndctl_cmd_fw_xlat_firmware_status; } LIBNDCTL_13; LIBNDCTL_15 { global: ndctl_dimm_cmd_new_ack_shutdown_count; ndctl_region_get_numa_node; ndctl_dimm_fw_update_supported; ndctl_region_get_persistence_domain; ndctl_bus_get_persistence_domain; ndctl_namespace_write_cache_is_enabled; ndctl_namespace_enable_write_cache; ndctl_namespace_disable_write_cache; ndctl_bus_get_scrub_state; ndctl_bus_start_scrub; ndctl_region_deep_flush; } LIBNDCTL_14; LIBNDCTL_16 { global: ndctl_cmd_ars_cap_get_clear_unit; ndctl_namespace_inject_error2; ndctl_namespace_uninject_error2; ndctl_cmd_ars_stat_get_flag_overflow; } LIBNDCTL_15; LIBNDCTL_17 { global: ndctl_dimm_smart_inject_supported; ndctl_dimm_get_health; ndctl_dimm_get_flags; ndctl_dimm_get_event_flags; ndctl_dimm_is_flag_supported; ndctl_region_get_max_available_extent; ndctl_cmd_smart_inject_ctrl_temperature; } LIBNDCTL_16; LIBNDCTL_18 { global: ndctl_namespace_get_first_badblock; ndctl_namespace_get_next_badblock; ndctl_dimm_get_dirty_shutdown; } LIBNDCTL_17; LIBNDCTL_19 { global: ndctl_cmd_xlat_firmware_status; ndctl_cmd_submit_xlat; ndctl_pfn_get_supported_alignment; ndctl_pfn_get_num_alignments; ndctl_dax_get_supported_alignment; ndctl_dax_get_num_alignments; ndctl_dimm_get_security; ndctl_dimm_update_passphrase; ndctl_dimm_disable_passphrase; ndctl_dimm_freeze_security; ndctl_dimm_secure_erase; ndctl_dimm_overwrite; ndctl_dimm_wait_overwrite; ndctl_dimm_update_master_passphrase; ndctl_dimm_master_secure_erase; } LIBNDCTL_18; LIBNDCTL_20 { global: ndctl_bus_poll_scrub_completion; } LIBNDCTL_19; LIBNDCTL_21 { ndctl_cmd_cfg_read_set_extent; ndctl_cmd_cfg_write_set_extent; ndctl_dimm_read_label_index; ndctl_dimm_read_label_extent; ndctl_dimm_zero_label_extent; } LIBNDCTL_20; LIBNDCTL_22 { ndctl_dimm_security_is_frozen; } LIBNDCTL_21; ndctl-67/ndctl/lib/msft.c000066400000000000000000000076131355562357700154340ustar00rootroot00000000000000/* * Copyright (C) 2016-2017 Dell, Inc. * Copyright (C) 2016 Hewlett Packard Enterprise Development LP * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include "private.h" #include "msft.h" #define CMD_MSFT(_c) ((_c)->msft) #define CMD_MSFT_SMART(_c) (CMD_MSFT(_c)->u.smart.data) static struct ndctl_cmd *msft_dimm_cmd_new_smart(struct ndctl_dimm *dimm) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); struct ndctl_cmd *cmd; size_t size; struct ndn_pkg_msft *msft; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) { dbg(ctx, "unsupported cmd\n"); return NULL; } if (test_dimm_dsm(dimm, NDN_MSFT_CMD_SMART) == DIMM_DSM_UNSUPPORTED) { dbg(ctx, "unsupported function\n"); return NULL; } size = sizeof(*cmd) + sizeof(struct ndn_pkg_msft); cmd = calloc(1, size); if (!cmd) return NULL; cmd->dimm = dimm; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; msft = CMD_MSFT(cmd); msft->gen.nd_family = NVDIMM_FAMILY_MSFT; msft->gen.nd_command = NDN_MSFT_CMD_SMART; msft->gen.nd_fw_size = 0; msft->gen.nd_size_in = offsetof(struct ndn_msft_smart, status); msft->gen.nd_size_out = sizeof(msft->u.smart); msft->u.smart.status = 0; cmd->firmware_status = &msft->u.smart.status; return cmd; } static int msft_smart_valid(struct ndctl_cmd *cmd) { if (cmd->type != ND_CMD_CALL || cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_msft) || CMD_MSFT(cmd)->gen.nd_family != NVDIMM_FAMILY_MSFT || CMD_MSFT(cmd)->gen.nd_command != NDN_MSFT_CMD_SMART || cmd->status != 0) return cmd->status < 0 ? cmd->status : -EINVAL; return 0; } static unsigned int msft_cmd_smart_get_flags(struct ndctl_cmd *cmd) { int rc; rc = msft_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } /* below health data can be retrieved via MSFT _DSM function 11 */ return NDN_MSFT_SMART_HEALTH_VALID | NDN_MSFT_SMART_TEMP_VALID | NDN_MSFT_SMART_USED_VALID; } static unsigned int num_set_bit_health(__u16 num) { int i; __u16 n = num & 0x7FFF; unsigned int count = 0; for (i = 0; i < 15; i++) if (!!(n & (1 << i))) count++; return count; } static unsigned int msft_cmd_smart_get_health(struct ndctl_cmd *cmd) { unsigned int health; unsigned int num; int rc; rc = msft_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } num = num_set_bit_health(CMD_MSFT_SMART(cmd)->health); if (num == 0) health = 0; else if (num < 2) health = ND_SMART_NON_CRITICAL_HEALTH; else if (num < 3) health = ND_SMART_CRITICAL_HEALTH; else health = ND_SMART_FATAL_HEALTH; return health; } static unsigned int msft_cmd_smart_get_media_temperature(struct ndctl_cmd *cmd) { int rc; rc = msft_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return CMD_MSFT_SMART(cmd)->temp * 16; } static unsigned int msft_cmd_smart_get_life_used(struct ndctl_cmd *cmd) { int rc; rc = msft_smart_valid(cmd); if (rc < 0) { errno = -rc; return UINT_MAX; } return 100 - CMD_MSFT_SMART(cmd)->nvm_lifetime; } struct ndctl_dimm_ops * const msft_dimm_ops = &(struct ndctl_dimm_ops) { .new_smart = msft_dimm_cmd_new_smart, .smart_get_flags = msft_cmd_smart_get_flags, .smart_get_health = msft_cmd_smart_get_health, .smart_get_media_temperature = msft_cmd_smart_get_media_temperature, .smart_get_life_used = msft_cmd_smart_get_life_used, }; ndctl-67/ndctl/lib/msft.h000066400000000000000000000031721355562357700154350ustar00rootroot00000000000000/* * Copyright (C) 2016-2017 Dell, Inc. * Copyright (C) 2016 Hewlett Packard Enterprise Development LP * Copyright (c) 2014-2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __NDCTL_MSFT_H__ #define __NDCTL_MSFT_H__ enum { NDN_MSFT_CMD_QUERY = 0, /* non-root commands */ NDN_MSFT_CMD_SMART = 11, }; /* NDN_MSFT_CMD_SMART */ #define NDN_MSFT_SMART_HEALTH_VALID ND_SMART_HEALTH_VALID #define NDN_MSFT_SMART_TEMP_VALID ND_SMART_TEMP_VALID #define NDN_MSFT_SMART_USED_VALID ND_SMART_USED_VALID /* * This is actually function 11 data, * This is the closest I can find to match smart * Microsoft _DSM does not have smart function */ struct ndn_msft_smart_data { __u16 health; __u16 temp; __u8 err_thresh_stat; __u8 warn_thresh_stat; __u8 nvm_lifetime; __u8 count_dram_uncorr_err; __u8 count_dram_corr_err; } __attribute__((packed)); struct ndn_msft_smart { __u32 status; union { __u8 buf[9]; struct ndn_msft_smart_data data[0]; }; } __attribute__((packed)); union ndn_msft_cmd { __u32 query; struct ndn_msft_smart smart; } __attribute__((packed)); struct ndn_pkg_msft { struct nd_cmd_pkg gen; union ndn_msft_cmd u; } __attribute__((packed)); #endif /* __NDCTL_MSFT_H__ */ ndctl-67/ndctl/lib/nfit.c000066400000000000000000000144431355562357700154220ustar00rootroot00000000000000/* * Copyright (c) 2017, FUJITSU LIMITED. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include "private.h" #include /** * ndctl_bus_is_nfit_cmd_supported - ask nfit command is supported on @bus. * @bus: ndctl_bus instance * @cmd: nfit command number (defined as NFIT_CMD_XXX in libndctl-nfit.h) * * Return 1: command is supported. Return 0: command is not supported. * */ NDCTL_EXPORT int ndctl_bus_is_nfit_cmd_supported(struct ndctl_bus *bus, int cmd) { return !!(bus->nfit_dsm_mask & (1ULL << cmd)); } static int bus_has_translate_spa(struct ndctl_bus *bus) { if (!ndctl_bus_has_nfit(bus)) return 0; return ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_TRANSLATE_SPA); } static struct ndctl_cmd *ndctl_bus_cmd_new_translate_spa(struct ndctl_bus *bus) { struct ndctl_cmd *cmd; struct nd_cmd_pkg *pkg; struct nd_cmd_translate_spa *translate_spa; size_t size, spa_length; spa_length = sizeof(struct nd_cmd_translate_spa) + sizeof(struct nd_nvdimm_device); size = sizeof(*cmd) + sizeof(*pkg) + spa_length; cmd = calloc(1, size); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; pkg->nd_command = NFIT_CMD_TRANSLATE_SPA; pkg->nd_size_in = sizeof(unsigned long long); pkg->nd_size_out = spa_length; pkg->nd_fw_size = spa_length; translate_spa = (struct nd_cmd_translate_spa *)&pkg->nd_payload[0]; cmd->firmware_status = &translate_spa->status; translate_spa->translate_length = spa_length; return cmd; } static int ndctl_bus_cmd_get_translate_spa(struct ndctl_cmd *cmd, unsigned int *handle, unsigned long long *dpa) { struct nd_cmd_pkg *pkg; struct nd_cmd_translate_spa *translate_spa; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; translate_spa = (struct nd_cmd_translate_spa *)&pkg->nd_payload[0]; if (translate_spa->status == ND_TRANSLATE_SPA_STATUS_INVALID_SPA) return -EINVAL; /* * XXX: Currently NVDIMM mirroring is not supported. * Even if ACPI returned plural dimms due to mirroring, * this function returns just the first dimm. */ *handle = translate_spa->devices[0].nfit_device_handle; *dpa = translate_spa->devices[0].dpa; return 0; } static int is_valid_spa(struct ndctl_bus *bus, unsigned long long spa) { return !!ndctl_bus_get_region_by_physical_address(bus, spa); } /** * ndctl_bus_nfit_translate_spa - call translate spa. * @bus: bus which belongs to. * @address: address (System Physical Address) * @handle: pointer to return dimm handle * @dpa: pointer to return Dimm Physical address * * If success, returns zero, store dimm's @handle, and @dpa. */ int ndctl_bus_nfit_translate_spa(struct ndctl_bus *bus, unsigned long long address, unsigned int *handle, unsigned long long *dpa) { struct ndctl_cmd *cmd; struct nd_cmd_pkg *pkg; struct nd_cmd_translate_spa *translate_spa; int rc; if (!bus || !handle || !dpa) return -EINVAL; if (!bus_has_translate_spa(bus)) return -ENOTTY; if (!is_valid_spa(bus, address)) return -EINVAL; cmd = ndctl_bus_cmd_new_translate_spa(bus); if (!cmd) return -ENOMEM; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; translate_spa = (struct nd_cmd_translate_spa *)&pkg->nd_payload[0]; translate_spa->spa = address; rc = ndctl_cmd_submit(cmd); if (rc < 0) { ndctl_cmd_unref(cmd); return rc; } rc = ndctl_bus_cmd_get_translate_spa(cmd, handle, dpa); ndctl_cmd_unref(cmd); return rc; } struct ndctl_cmd *ndctl_bus_cmd_new_err_inj(struct ndctl_bus *bus) { struct nd_cmd_ars_err_inj *err_inj; size_t size, cmd_length; struct nd_cmd_pkg *pkg; struct ndctl_cmd *cmd; cmd_length = sizeof(struct nd_cmd_ars_err_inj); size = sizeof(*cmd) + sizeof(*pkg) + cmd_length; cmd = calloc(1, size); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; pkg->nd_command = NFIT_CMD_ARS_INJECT_SET; pkg->nd_size_in = offsetof(struct nd_cmd_ars_err_inj, status); pkg->nd_size_out = cmd_length - pkg->nd_size_in; pkg->nd_fw_size = pkg->nd_size_out; err_inj = (struct nd_cmd_ars_err_inj *)&pkg->nd_payload[0]; cmd->firmware_status = &err_inj->status; return cmd; } struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus) { struct nd_cmd_ars_err_inj_clr *err_inj_clr; size_t size, cmd_length; struct nd_cmd_pkg *pkg; struct ndctl_cmd *cmd; cmd_length = sizeof(struct nd_cmd_ars_err_inj_clr); size = sizeof(*cmd) + sizeof(*pkg) + cmd_length; cmd = calloc(1, size); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; pkg->nd_command = NFIT_CMD_ARS_INJECT_CLEAR; pkg->nd_size_in = offsetof(struct nd_cmd_ars_err_inj_clr, status); pkg->nd_size_out = cmd_length - pkg->nd_size_in; pkg->nd_fw_size = pkg->nd_size_out; err_inj_clr = (struct nd_cmd_ars_err_inj_clr *)&pkg->nd_payload[0]; cmd->firmware_status = &err_inj_clr->status; return cmd; } struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_stat(struct ndctl_bus *bus, u32 buf_size) { struct nd_cmd_ars_err_inj_stat *err_inj_stat; size_t size, cmd_length; struct nd_cmd_pkg *pkg; struct ndctl_cmd *cmd; cmd_length = sizeof(struct nd_cmd_ars_err_inj_stat); size = sizeof(*cmd) + sizeof(*pkg) + cmd_length + buf_size; cmd = calloc(1, size); if (!cmd) return NULL; cmd->bus = bus; ndctl_cmd_ref(cmd); cmd->type = ND_CMD_CALL; cmd->size = size; cmd->status = 1; pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0]; pkg->nd_command = NFIT_CMD_ARS_INJECT_GET; pkg->nd_size_in = 0; pkg->nd_size_out = cmd_length + buf_size; pkg->nd_fw_size = pkg->nd_size_out; err_inj_stat = (struct nd_cmd_ars_err_inj_stat *)&pkg->nd_payload[0]; cmd->firmware_status = &err_inj_stat->status; return cmd; } ndctl-67/ndctl/lib/private.h000066400000000000000000000276541355562357700161510ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef _LIBNDCTL_PRIVATE_H_ #define _LIBNDCTL_PRIVATE_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "intel.h" #include "hpe1.h" #include "msft.h" #include "hyperv.h" struct nvdimm_data { struct ndctl_cmd *cmd_read; void *data; unsigned long config_size; size_t nslabel_size; int ns_current, ns_next; }; /** * struct ndctl_dimm - memory device as identified by NFIT * @module: kernel module (libnvdimm) * @handle: NFIT-handle value * @major: /dev/nmemX major character device number * @minor: /dev/nmemX minor character device number * @phys_id: SMBIOS physical id * @vendor_id: hardware component vendor * @device_id: hardware device id * @revision_id: hardware revision id * @node: system node-id * @socket: socket-id in the node * @imc: memory-controller-id in the socket * @channel: channel-id in the memory-controller * @dimm: dimm-id in the channel * @formats: number of support interfaces * @format: array of format interface code numbers */ struct ndctl_dimm { struct kmod_module *module; struct ndctl_bus *bus; struct ndctl_dimm_ops *ops; struct nvdimm_data ndd; unsigned int handle, major, minor, serial; unsigned short phys_id; unsigned short vendor_id; unsigned short device_id; unsigned short revision_id; unsigned short subsystem_vendor_id; unsigned short subsystem_device_id; unsigned short subsystem_revision_id; unsigned short manufacturing_date; unsigned char manufacturing_location; unsigned long cmd_family; unsigned long cmd_mask; unsigned long nfit_dsm_mask; long long dirty_shutdown; char *unique_id; char *dimm_path; char *dimm_buf; int health_eventfd; int buf_len; int id; union dimm_flags { unsigned long flags; struct { unsigned int f_map:1; unsigned int f_arm:1; unsigned int f_save:1; unsigned int f_flush:1; unsigned int f_smart:1; unsigned int f_restore:1; unsigned int f_notify:1; }; } flags; int locked; int aliased; struct list_node list; int formats; int format[0]; }; enum dsm_support { DIMM_DSM_UNSUPPORTED, /* don't attempt command */ DIMM_DSM_SUPPORTED, /* good to go */ DIMM_DSM_UNKNOWN, /* try ND_CMD_CALL on older kernels */ }; static inline enum dsm_support test_dimm_dsm(struct ndctl_dimm *dimm, int fn) { if (dimm->nfit_dsm_mask == ULONG_MAX) { return DIMM_DSM_UNKNOWN; } else if (dimm->nfit_dsm_mask & (1 << fn)) return DIMM_DSM_SUPPORTED; return DIMM_DSM_UNSUPPORTED; } void region_flag_refresh(struct ndctl_region *region); /** * struct ndctl_ctx - library user context to find "nd" instances * * Instantiate with ndctl_new(), which takes an initial reference. Free * the context by dropping the reference count to zero with * ndctrl_unref(), or take additional references with ndctl_ref() * @timeout: default library timeout in milliseconds */ struct ndctl_ctx { /* log_ctx must be first member for ndctl_set_log_fn compat */ struct log_ctx ctx; int refcount; int regions_init; void *userdata; struct list_head busses; int busses_init; struct udev *udev; struct udev_queue *udev_queue; struct kmod_ctx *kmod_ctx; struct daxctl_ctx *daxctl_ctx; unsigned long timeout; void *private_data; }; /** * struct ndctl_bus - a nfit table instance * @major: control character device major number * @minor: control character device minor number * @revision: NFIT table revision number * @provider: identifier for the source of the NFIT table * * The expectation is one NFIT/nd bus per system provided by platform * firmware (for example @provider == "ACPI.NFIT"). However, the * nfit_test module provides multiple test busses with provider names of * the format "nfit_test.N" */ struct ndctl_bus { struct ndctl_ctx *ctx; unsigned int id, major, minor, revision; char *provider; struct list_head dimms; struct list_head regions; struct list_node list; int dimms_init; int regions_init; int has_nfit; char *bus_path; char *bus_buf; size_t buf_len; char *wait_probe_path; char *scrub_path; unsigned long cmd_mask; unsigned long nfit_dsm_mask; }; /** * struct ndctl_lbasize - lbasize info for btt and blk-namespace devices * @select: currently selected sector_size * @supported: possible sector_size options * @num: number of entries in @supported */ struct ndctl_lbasize { int select; unsigned int *supported; int num; }; struct badblocks_iter { struct badblock bb; FILE *file; }; /** * struct ndctl_namespace - device claimed by the nd_blk or nd_pmem driver * @module: kernel module * @type: integer nd-bus device-type * @type_name: 'namespace_io', 'namespace_pmem', or 'namespace_block' * @namespace_path: devpath for namespace device * @bdev: associated block_device of a namespace * @size: unsigned * @numa_node: numa node attribute * * A 'namespace' is the resulting device after region-aliasing and * label-parsing is resolved. */ struct ndctl_namespace { struct kmod_module *module; struct ndctl_region *region; struct list_node list; char *ndns_path; char *ndns_buf; char *bdev; int type, id, buf_len, raw_mode; int generation; unsigned long long resource, size; enum ndctl_namespace_mode enforce_mode; struct badblocks_iter bb_iter; char *alt_name; uuid_t uuid; struct ndctl_lbasize lbasize; int numa_node; struct list_head injected_bb; }; /** * struct ndctl_cmd - device-specific-method (_DSM ioctl) container * @dimm: set if the command is relative to a dimm, NULL otherwise * @bus: set if the command is relative to a bus (like ARS), NULL otherwise * @refcount: reference for passing command buffer around * @type: cmd number * @size: total size of the ndctl_cmd allocation * @status: negative if failed, 0 if success, > 0 if never submitted * @firmware_status: NFIT command output status code * @iter: iterator for multi-xfer commands * @source: source cmd of an inherited iter.total_buf * * For dynamically sized commands like 'get_config', 'set_config', or * 'vendor', @size encompasses the entire buffer for the command input * and response output data. * * A command may only specify one of @source, or @iter.total_buf, not both. */ struct ndctl_cmd { struct ndctl_dimm *dimm; struct ndctl_bus *bus; int refcount; int type; int size; int status; u32 *firmware_status; struct ndctl_cmd_iter { u32 init_offset; u32 *offset; u32 *xfer; /* pointer to xfer length in cmd */ u8 *data; /* pointer to the data buffer location in cmd */ u32 max_xfer; char *total_buf; u32 total_xfer; int dir; } iter; struct ndctl_cmd *source; union { struct nd_cmd_ars_cap ars_cap[0]; struct nd_cmd_ars_start ars_start[0]; struct nd_cmd_ars_status ars_status[0]; struct nd_cmd_clear_error clear_err[0]; struct nd_cmd_pkg pkg[0]; struct ndn_pkg_hpe1 hpe1[0]; struct ndn_pkg_msft msft[0]; struct nd_pkg_hyperv hyperv[0]; struct nd_pkg_intel intel[0]; struct nd_cmd_get_config_size get_size[0]; struct nd_cmd_get_config_data_hdr get_data[0]; struct nd_cmd_set_config_hdr set_data[0]; struct nd_cmd_vendor_hdr vendor[0]; char cmd_buf[0]; }; }; struct ndctl_bb { u64 block; u64 count; struct list_node list; }; /* ars_status flags */ #define ND_ARS_STAT_FLAG_OVERFLOW (1 << 0) struct ndctl_dimm_ops { const char *(*cmd_desc)(int); bool (*cmd_is_supported)(struct ndctl_dimm *, int); struct ndctl_cmd *(*new_smart)(struct ndctl_dimm *); unsigned int (*smart_get_flags)(struct ndctl_cmd *); unsigned int (*smart_get_health)(struct ndctl_cmd *); unsigned int (*smart_get_media_temperature)(struct ndctl_cmd *); unsigned int (*smart_get_ctrl_temperature)(struct ndctl_cmd *); unsigned int (*smart_get_spares)(struct ndctl_cmd *); unsigned int (*smart_get_alarm_flags)(struct ndctl_cmd *); unsigned int (*smart_get_life_used)(struct ndctl_cmd *); unsigned int (*smart_get_shutdown_state)(struct ndctl_cmd *); unsigned int (*smart_get_shutdown_count)(struct ndctl_cmd *); unsigned int (*smart_get_vendor_size)(struct ndctl_cmd *); unsigned char *(*smart_get_vendor_data)(struct ndctl_cmd *); struct ndctl_cmd *(*new_smart_threshold)(struct ndctl_dimm *); unsigned int (*smart_threshold_get_alarm_control)(struct ndctl_cmd *); unsigned int (*smart_threshold_get_media_temperature)(struct ndctl_cmd *); unsigned int (*smart_threshold_get_ctrl_temperature)(struct ndctl_cmd *); unsigned int (*smart_threshold_get_spares)(struct ndctl_cmd *); struct ndctl_cmd *(*new_smart_set_threshold)(struct ndctl_cmd *); unsigned int (*smart_threshold_get_supported_alarms)(struct ndctl_cmd *); int (*smart_threshold_set_alarm_control)(struct ndctl_cmd *, unsigned int); int (*smart_threshold_set_media_temperature)(struct ndctl_cmd *, unsigned int); int (*smart_threshold_set_ctrl_temperature)(struct ndctl_cmd *, unsigned int); int (*smart_threshold_set_spares)(struct ndctl_cmd *, unsigned int); struct ndctl_cmd *(*new_smart_inject)(struct ndctl_dimm *); int (*smart_inject_media_temperature)(struct ndctl_cmd *, bool, unsigned int); int (*smart_inject_ctrl_temperature)(struct ndctl_cmd *, bool, unsigned int); int (*smart_inject_spares)(struct ndctl_cmd *, bool, unsigned int); int (*smart_inject_fatal)(struct ndctl_cmd *, bool); int (*smart_inject_unsafe_shutdown)(struct ndctl_cmd *, bool); int (*smart_inject_supported)(struct ndctl_dimm *); struct ndctl_cmd *(*new_fw_get_info)(struct ndctl_dimm *); unsigned int (*fw_info_get_storage_size)(struct ndctl_cmd *); unsigned int (*fw_info_get_max_send_len)(struct ndctl_cmd *); unsigned int (*fw_info_get_query_interval)(struct ndctl_cmd *); unsigned int (*fw_info_get_max_query_time)(struct ndctl_cmd *); unsigned long long (*fw_info_get_run_version)(struct ndctl_cmd *); unsigned long long (*fw_info_get_updated_version)(struct ndctl_cmd *); struct ndctl_cmd *(*new_fw_start_update)(struct ndctl_dimm *); unsigned int (*fw_start_get_context)(struct ndctl_cmd *); struct ndctl_cmd *(*new_fw_send)(struct ndctl_cmd *, unsigned int, unsigned int, void *); struct ndctl_cmd *(*new_fw_finish)(struct ndctl_cmd *); struct ndctl_cmd *(*new_fw_abort)(struct ndctl_cmd *); struct ndctl_cmd *(*new_fw_finish_query)(struct ndctl_cmd *); unsigned long long (*fw_fquery_get_fw_rev)(struct ndctl_cmd *); enum ND_FW_STATUS (*fw_xlat_firmware_status)(struct ndctl_cmd *); struct ndctl_cmd *(*new_ack_shutdown_count)(struct ndctl_dimm *); int (*fw_update_supported)(struct ndctl_dimm *); int (*xlat_firmware_status)(struct ndctl_cmd *); }; struct ndctl_dimm_ops * const intel_dimm_ops; struct ndctl_dimm_ops * const hpe1_dimm_ops; struct ndctl_dimm_ops * const msft_dimm_ops; struct ndctl_dimm_ops * const hyperv_dimm_ops; static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd) { if (cmd->dimm) return ndctl_dimm_get_bus(cmd->dimm); return cmd->bus; } #define NDCTL_EXPORT __attribute__ ((visibility("default"))) static inline int check_udev(struct udev *udev) { return udev ? 0 : -ENXIO; } static inline int check_kmod(struct kmod_ctx *kmod_ctx) { return kmod_ctx ? 0 : -ENXIO; } int ndctl_bus_nfit_translate_spa(struct ndctl_bus *bus, unsigned long long addr, unsigned int *handle, unsigned long long *dpa); struct ndctl_cmd *ndctl_bus_cmd_new_err_inj(struct ndctl_bus *bus); struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus); struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_stat(struct ndctl_bus *bus, u32 buf_size); #endif /* _LIBNDCTL_PRIVATE_H_ */ ndctl-67/ndctl/lib/smart.c000066400000000000000000000121161355562357700156030ustar00rootroot00000000000000/* * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include "private.h" /* * Define the wrappers around the ndctl_dimm_ops: */ NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart( struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->new_smart) return ops->new_smart(dimm); else return NULL; } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart_threshold( struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->new_smart_threshold) return ops->new_smart_threshold(dimm); else return NULL; } /* * smart_set_threshold is a read-modify-write command it depends on a * successfully completed smart_threshold command for its defaults. */ NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart_set_threshold( struct ndctl_cmd *cmd) { struct ndctl_dimm_ops *ops; if (!cmd || !cmd->dimm) return NULL; ops = cmd->dimm->ops; if (ops && ops->new_smart_set_threshold) return ops->new_smart_set_threshold(cmd); else return NULL; } #define smart_cmd_op(op, rettype, defretvalue) \ NDCTL_EXPORT rettype ndctl_cmd_##op(struct ndctl_cmd *cmd) \ { \ if (cmd->dimm) { \ struct ndctl_dimm_ops *ops = cmd->dimm->ops; \ if (ops && ops->op) \ return ops->op(cmd); \ } \ return defretvalue; \ } smart_cmd_op(smart_get_flags, unsigned int, 0) smart_cmd_op(smart_get_health, unsigned int, 0) smart_cmd_op(smart_get_media_temperature, unsigned int, 0) smart_cmd_op(smart_get_ctrl_temperature, unsigned int, 0) smart_cmd_op(smart_get_spares, unsigned int, 0) smart_cmd_op(smart_get_alarm_flags, unsigned int, 0) smart_cmd_op(smart_get_life_used, unsigned int, 0) smart_cmd_op(smart_get_shutdown_state, unsigned int, 0) smart_cmd_op(smart_get_shutdown_count, unsigned int, 0) smart_cmd_op(smart_get_vendor_size, unsigned int, 0) smart_cmd_op(smart_get_vendor_data, unsigned char *, NULL) smart_cmd_op(smart_threshold_get_alarm_control, unsigned int, 0) smart_cmd_op(smart_threshold_get_media_temperature, unsigned int, 0) smart_cmd_op(smart_threshold_get_ctrl_temperature, unsigned int, 0) smart_cmd_op(smart_threshold_get_spares, unsigned int, 0) NDCTL_EXPORT unsigned int ndctl_cmd_smart_get_temperature(struct ndctl_cmd *cmd) { return ndctl_cmd_smart_get_media_temperature(cmd); } NDCTL_EXPORT unsigned int ndctl_cmd_smart_threshold_get_temperature( struct ndctl_cmd *cmd) { return ndctl_cmd_smart_threshold_get_media_temperature(cmd); } smart_cmd_op(smart_threshold_get_supported_alarms, unsigned int, 0); #define smart_cmd_set_op(op) \ NDCTL_EXPORT int ndctl_cmd_##op(struct ndctl_cmd *cmd, unsigned int val) \ { \ if (cmd->dimm) { \ struct ndctl_dimm_ops *ops = cmd->dimm->ops; \ if (ops && ops->op) \ return ops->op(cmd, val); \ } \ return -ENXIO; \ } smart_cmd_set_op(smart_threshold_set_alarm_control) smart_cmd_set_op(smart_threshold_set_media_temperature) smart_cmd_set_op(smart_threshold_set_ctrl_temperature) smart_cmd_set_op(smart_threshold_set_spares) NDCTL_EXPORT int ndctl_cmd_smart_threshold_set_temperature( struct ndctl_cmd *cmd, unsigned int val) { return ndctl_cmd_smart_threshold_set_media_temperature(cmd, val); } NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_smart_inject( struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->new_smart_inject) return ops->new_smart_inject(dimm); else return NULL; } #define smart_cmd_inject_val(op) \ NDCTL_EXPORT int ndctl_cmd_##op(struct ndctl_cmd *cmd, bool enable, unsigned int val) \ { \ if (cmd->dimm) { \ struct ndctl_dimm_ops *ops = cmd->dimm->ops; \ if (ops && ops->op) \ return ops->op(cmd, enable, val); \ } \ return -ENXIO; \ } smart_cmd_inject_val(smart_inject_media_temperature) smart_cmd_inject_val(smart_inject_ctrl_temperature) smart_cmd_inject_val(smart_inject_spares) #define smart_cmd_inject(op) \ NDCTL_EXPORT int ndctl_cmd_##op(struct ndctl_cmd *cmd, bool enable) \ { \ if (cmd->dimm) { \ struct ndctl_dimm_ops *ops = cmd->dimm->ops; \ if (ops && ops->op) \ return ops->op(cmd, enable); \ } \ return -ENXIO; \ } smart_cmd_inject(smart_inject_fatal) smart_cmd_inject(smart_inject_unsafe_shutdown) NDCTL_EXPORT struct ndctl_cmd * ndctl_dimm_cmd_new_ack_shutdown_count(struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->new_ack_shutdown_count) return ops->new_ack_shutdown_count(dimm); else return NULL; } NDCTL_EXPORT int ndctl_dimm_smart_inject_supported(struct ndctl_dimm *dimm) { struct ndctl_dimm_ops *ops = dimm->ops; if (ops && ops->smart_inject_supported) return ops->smart_inject_supported(dimm); else return -ENOTTY; } ndctl-67/ndctl/libndctl-nfit.h000066400000000000000000000045721355562357700164540ustar00rootroot00000000000000/* * * Copyright (c) 2017 Hewlett Packard Enterprise Development LP * Copyright (c) 2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __LIBNDCTL_NFIT_H__ #define __LIBNDCTL_NFIT_H__ #include /* * libndctl-nfit.h : definitions for NFIT related commands/functions. */ /* nfit command numbers which are called via ND_CMD_CALL */ enum { NFIT_CMD_TRANSLATE_SPA = 5, NFIT_CMD_ARS_INJECT_SET = 7, NFIT_CMD_ARS_INJECT_CLEAR = 8, NFIT_CMD_ARS_INJECT_GET = 9, }; /* error number of Translate SPA by firmware */ #define ND_TRANSLATE_SPA_STATUS_INVALID_SPA 2 /* status definitions for error injection */ #define ND_ARS_ERR_INJ_STATUS_NOT_SUPP 1 #define ND_ARS_ERR_INJ_STATUS_INVALID_PARAM 2 enum err_inj_options { ND_ARS_ERR_INJ_OPT_NOTIFY = 0, }; /* * The following structures are command packages which are * defined by ACPI 6.2 (or later). */ /* For Translate SPA */ struct nd_cmd_translate_spa { __u64 spa; __u32 status; __u8 flags; __u8 _reserved[3]; __u64 translate_length; __u32 num_nvdimms; struct nd_nvdimm_device { __u32 nfit_device_handle; __u32 _reserved; __u64 dpa; } __attribute__((packed)) devices[0]; } __attribute__((packed)); /* For ARS Error Inject */ struct nd_cmd_ars_err_inj { __u64 err_inj_spa_range_base; __u64 err_inj_spa_range_length; __u8 err_inj_options; __u32 status; } __attribute__((packed)); /* For ARS Error Inject Clear */ struct nd_cmd_ars_err_inj_clr { __u64 err_inj_clr_spa_range_base; __u64 err_inj_clr_spa_range_length; __u32 status; } __attribute__((packed)); /* For ARS Error Inject Status Query */ struct nd_cmd_ars_err_inj_stat { __u32 status; __u32 inj_err_rec_count; struct nd_error_stat_query_record { __u64 err_inj_stat_spa_range_base; __u64 err_inj_stat_spa_range_length; } __attribute__((packed)) record[0]; } __attribute__((packed)); int ndctl_bus_is_nfit_cmd_supported(struct ndctl_bus *bus, int cmd); #endif /* __LIBNDCTL_NFIT_H__ */ ndctl-67/ndctl/libndctl.h000066400000000000000000001075471355562357700155240ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef _LIBNDCTL_H_ #define _LIBNDCTL_H_ #include #include #include #include #include #include #ifdef HAVE_UUID #include #else typedef unsigned char uuid_t[16]; #endif /* * "nd/ndctl" device/object hierarchy and kernel modules * * +-----------+-----------+-----------+------------------+-----------+ * | DEVICE | BUS | REGION | NAMESPACE | BLOCK | * | CLASSES: | PROVIDERS | DEVICES | DEVICES | DEVICES | * +-----------+-----------+-----------+------------------+-----------+ * | MODULES: | nd_core | nd_core | nd_region | nd_pmem | * | | nd_acpi | nd_region | | nd_blk | * | | nfit_test | | | btt | * +-----------v-----------v-----------v------------------v-----------v * +-----+ * | CTX | * +--+--+ +---------+ +--------------+ +-------+ * | +-> REGION0 +---> NAMESPACE0.0 +---> PMEM3 | * +-------+ +--+---+ | +---------+ +--------------+ +-------+ * | DIMM0 <-----+ BUS0 +---> REGION1 +---> NAMESPACE1.0 +---> PMEM2 | * +-------+ +--+---+ | +---------+ +--------------+ +-------+ * | +-> REGION2 +---> NAMESPACE2.0 +---> PMEM1 | * | +---------+ + ------------ + +-------+ * | * +-------+ | +---------+ +--------------+ +-------+ * | DIMM1 <---+ +--+---+ +-> REGION3 +---> NAMESPACE3.0 +---> PMEM0 | * +-------+ +-+ BUS1 +-+ +---------+ +--------------+ +-------+ * | DIMM2 <---+ +--+---+ +-> REGION4 +---> NAMESPACE4.0 +---> ND0 | * +-------+ | + ------- + +--------------+ +-------+ * | * +-------+ | +--------------+ +-------+ * | DIMM3 <---+ | +-> NAMESPACE5.0 +---> ND2 | * +-------+ | +--+---+ +---------+ | +--------------+ +---------------+ * | DIMM4 <-----+ BUS2 +---> REGION5 +---> NAMESPACE5.1 +---> BTT1 | ND1 | * +-------+ | +------+ +---------+ | +--------------+ +---------------+ * | DIMM5 <---+ +-> NAMESPACE5.2 +---> BTT0 | ND0 | * +-------+ +--------------+ +-------+-------+ * * Notes: * 1/ The object ids are not guaranteed to be stable from boot to boot * 2/ While regions and busses are numbered in sequential/bus-discovery * order, the resulting block devices may appear to have random ids. * Use static attributes of the devices/device-path to generate a * persistent name. */ #ifdef __cplusplus extern "C" { #endif #define ND_EVENT_SPARES_REMAINING (1 << 0) #define ND_EVENT_MEDIA_TEMPERATURE (1 << 1) #define ND_EVENT_CTRL_TEMPERATURE (1 << 2) #define ND_EVENT_HEALTH_STATE (1 << 3) #define ND_EVENT_UNCLEAN_SHUTDOWN (1 << 4) size_t ndctl_min_namespace_size(void); size_t ndctl_sizeof_namespace_index(void); size_t ndctl_sizeof_namespace_label(void); double ndctl_decode_smart_temperature(unsigned int temp); unsigned int ndctl_encode_smart_temperature(double temp); struct ndctl_ctx; struct ndctl_ctx *ndctl_ref(struct ndctl_ctx *ctx); struct ndctl_ctx *ndctl_unref(struct ndctl_ctx *ctx); int ndctl_new(struct ndctl_ctx **ctx); void ndctl_set_private_data(struct ndctl_ctx *ctx, void *data); void *ndctl_get_private_data(struct ndctl_ctx *ctx); struct daxctl_ctx; struct daxctl_ctx *ndctl_get_daxctl_ctx(struct ndctl_ctx *ctx); void ndctl_invalidate(struct ndctl_ctx *ctx); void ndctl_set_log_fn(struct ndctl_ctx *ctx, void (*log_fn)(struct ndctl_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args)); int ndctl_get_log_priority(struct ndctl_ctx *ctx); void ndctl_set_log_priority(struct ndctl_ctx *ctx, int priority); void ndctl_set_userdata(struct ndctl_ctx *ctx, void *userdata); void *ndctl_get_userdata(struct ndctl_ctx *ctx); enum ndctl_persistence_domain { PERSISTENCE_NONE = 0, PERSISTENCE_MEM_CTRL = 10, PERSISTENCE_CPU_CACHE = 20, PERSISTENCE_UNKNOWN = INT_MAX, }; struct ndctl_bus; struct ndctl_bus *ndctl_bus_get_first(struct ndctl_ctx *ctx); struct ndctl_bus *ndctl_bus_get_next(struct ndctl_bus *bus); #define ndctl_bus_foreach(ctx, bus) \ for (bus = ndctl_bus_get_first(ctx); \ bus != NULL; \ bus = ndctl_bus_get_next(bus)) struct ndctl_ctx *ndctl_bus_get_ctx(struct ndctl_bus *bus); int ndctl_bus_has_nfit(struct ndctl_bus *bus); unsigned int ndctl_bus_get_major(struct ndctl_bus *bus); unsigned int ndctl_bus_get_minor(struct ndctl_bus *bus); const char *ndctl_bus_get_devname(struct ndctl_bus *bus); struct ndctl_bus *ndctl_bus_get_by_provider(struct ndctl_ctx *ctx, const char *provider); const char *ndctl_bus_get_cmd_name(struct ndctl_bus *bus, int cmd); int ndctl_bus_is_cmd_supported(struct ndctl_bus *bus, int cmd); unsigned int ndctl_bus_get_revision(struct ndctl_bus *bus); unsigned int ndctl_bus_get_id(struct ndctl_bus *bus); const char *ndctl_bus_get_provider(struct ndctl_bus *bus); enum ndctl_persistence_domain ndctl_bus_get_persistence_domain( struct ndctl_bus *bus); int ndctl_bus_wait_probe(struct ndctl_bus *bus); int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus); int ndctl_bus_poll_scrub_completion(struct ndctl_bus *bus, unsigned int poll_interval, unsigned int timeout); unsigned int ndctl_bus_get_scrub_count(struct ndctl_bus *bus); int ndctl_bus_get_scrub_state(struct ndctl_bus *bus); int ndctl_bus_start_scrub(struct ndctl_bus *bus); int ndctl_bus_has_error_injection(struct ndctl_bus *bus); struct ndctl_dimm; struct ndctl_dimm *ndctl_dimm_get_first(struct ndctl_bus *bus); struct ndctl_dimm *ndctl_dimm_get_next(struct ndctl_dimm *dimm); #define ndctl_dimm_foreach(bus, dimm) \ for (dimm = ndctl_dimm_get_first(bus); \ dimm != NULL; \ dimm = ndctl_dimm_get_next(dimm)) unsigned int ndctl_dimm_get_handle(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_phys_id(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_vendor(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_device(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_revision(struct ndctl_dimm *dimm); long long ndctl_dimm_get_dirty_shutdown(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_subsystem_vendor(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_subsystem_device(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_manufacturing_date(struct ndctl_dimm *dimm); unsigned char ndctl_dimm_get_manufacturing_location(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_subsystem_revision(struct ndctl_dimm *dimm); unsigned short ndctl_dimm_get_format(struct ndctl_dimm *dimm); int ndctl_dimm_get_formats(struct ndctl_dimm *dimm); int ndctl_dimm_get_formatN(struct ndctl_dimm *dimm, int i); unsigned int ndctl_dimm_get_major(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_get_minor(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_get_id(struct ndctl_dimm *dimm); const char *ndctl_dimm_get_unique_id(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_get_serial(struct ndctl_dimm *dimm); const char *ndctl_dimm_get_cmd_name(struct ndctl_dimm *dimm, int cmd); int ndctl_dimm_is_cmd_supported(struct ndctl_dimm *dimm, int cmd); int ndctl_dimm_locked(struct ndctl_dimm *dimm); int ndctl_dimm_aliased(struct ndctl_dimm *dimm); int ndctl_dimm_has_errors(struct ndctl_dimm *dimm); int ndctl_dimm_has_notifications(struct ndctl_dimm *dimm); int ndctl_dimm_failed_save(struct ndctl_dimm *dimm); int ndctl_dimm_failed_arm(struct ndctl_dimm *dimm); int ndctl_dimm_failed_restore(struct ndctl_dimm *dimm); int ndctl_dimm_failed_map(struct ndctl_dimm *dimm); int ndctl_dimm_smart_pending(struct ndctl_dimm *dimm); int ndctl_dimm_failed_flush(struct ndctl_dimm *dimm); int ndctl_dimm_get_health_eventfd(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_get_health(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_get_flags(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_get_event_flags(struct ndctl_dimm *dimm); int ndctl_dimm_is_flag_supported(struct ndctl_dimm *dimm, unsigned int flag); unsigned int ndctl_dimm_handle_get_node(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_handle_get_socket(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_handle_get_imc(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_handle_get_channel(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_handle_get_dimm(struct ndctl_dimm *dimm); const char *ndctl_dimm_get_devname(struct ndctl_dimm *dimm); struct ndctl_bus *ndctl_dimm_get_bus(struct ndctl_dimm *dimm); struct ndctl_ctx *ndctl_dimm_get_ctx(struct ndctl_dimm *dimm); struct ndctl_dimm *ndctl_dimm_get_by_handle(struct ndctl_bus *bus, unsigned int handle); struct ndctl_dimm *ndctl_bus_get_dimm_by_physical_address(struct ndctl_bus *bus, unsigned long long address); int ndctl_dimm_is_active(struct ndctl_dimm *dimm); int ndctl_dimm_is_enabled(struct ndctl_dimm *dimm); int ndctl_dimm_disable(struct ndctl_dimm *dimm); int ndctl_dimm_enable(struct ndctl_dimm *dimm); struct ndctl_cmd; struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, unsigned long long address, unsigned long long len); struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, int type); struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap); struct ndctl_range { unsigned long long address; unsigned long long length; }; unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap); int ndctl_cmd_ars_cap_get_range(struct ndctl_cmd *ars_cap, struct ndctl_range *range); int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_status); unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat); unsigned long long ndctl_cmd_ars_get_record_addr(struct ndctl_cmd *ars_stat, unsigned int rec_index); unsigned long long ndctl_cmd_ars_get_record_len(struct ndctl_cmd *ars_stat, unsigned int rec_index); struct ndctl_cmd *ndctl_bus_cmd_new_clear_error(unsigned long long address, unsigned long long len, struct ndctl_cmd *ars_cap); unsigned long long ndctl_cmd_clear_error_get_cleared( struct ndctl_cmd *clear_err); unsigned int ndctl_cmd_ars_cap_get_clear_unit(struct ndctl_cmd *ars_cap); int ndctl_cmd_ars_stat_get_flag_overflow(struct ndctl_cmd *ars_stat); /* * Note: ndctl_cmd_smart_get_temperature is an alias for * ndctl_cmd_smart_get_temperature */ /* * the ndctl.h definition of these are deprecated, libndctl.h is the * authoritative defintion. */ #define ND_SMART_HEALTH_VALID (1 << 0) #define ND_SMART_SPARES_VALID (1 << 1) #define ND_SMART_USED_VALID (1 << 2) #define ND_SMART_MTEMP_VALID (1 << 3) #define ND_SMART_TEMP_VALID ND_SMART_MTEMP_VALID #define ND_SMART_CTEMP_VALID (1 << 4) #define ND_SMART_SHUTDOWN_COUNT_VALID (1 << 5) #define ND_SMART_AIT_STATUS_VALID (1 << 6) #define ND_SMART_PTEMP_VALID (1 << 7) #define ND_SMART_ALARM_VALID (1 << 9) #define ND_SMART_SHUTDOWN_VALID (1 << 10) #define ND_SMART_VENDOR_VALID (1 << 11) #define ND_SMART_SPARE_TRIP (1 << 0) #define ND_SMART_MTEMP_TRIP (1 << 1) #define ND_SMART_TEMP_TRIP ND_SMART_MTEMP_TRIP #define ND_SMART_CTEMP_TRIP (1 << 2) #define ND_SMART_NON_CRITICAL_HEALTH (1 << 0) #define ND_SMART_CRITICAL_HEALTH (1 << 1) #define ND_SMART_FATAL_HEALTH (1 << 2) struct ndctl_cmd *ndctl_dimm_cmd_new_smart(struct ndctl_dimm *dimm); unsigned int ndctl_cmd_smart_get_flags(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_health(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_temperature(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_media_temperature(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_ctrl_temperature(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_spares(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_alarm_flags(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_life_used(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_shutdown_state(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_shutdown_count(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_get_vendor_size(struct ndctl_cmd *cmd); unsigned char *ndctl_cmd_smart_get_vendor_data(struct ndctl_cmd *cmd); struct ndctl_cmd *ndctl_dimm_cmd_new_smart_threshold(struct ndctl_dimm *dimm); unsigned int ndctl_cmd_smart_threshold_get_alarm_control(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_threshold_get_temperature(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_threshold_get_media_temperature(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_threshold_get_ctrl_temperature(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_threshold_get_spares(struct ndctl_cmd *cmd); struct ndctl_cmd *ndctl_dimm_cmd_new_smart_set_threshold(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_smart_threshold_get_supported_alarms(struct ndctl_cmd *cmd); int ndctl_cmd_smart_threshold_set_alarm_control(struct ndctl_cmd *cmd, unsigned int val); int ndctl_cmd_smart_threshold_set_temperature(struct ndctl_cmd *cmd, unsigned int val); int ndctl_cmd_smart_threshold_set_media_temperature(struct ndctl_cmd *cmd, unsigned int val); int ndctl_cmd_smart_threshold_set_ctrl_temperature(struct ndctl_cmd *cmd, unsigned int val); int ndctl_cmd_smart_threshold_set_spares(struct ndctl_cmd *cmd, unsigned int val); struct ndctl_cmd *ndctl_dimm_cmd_new_smart_inject(struct ndctl_dimm *dimm); int ndctl_cmd_smart_inject_media_temperature(struct ndctl_cmd *cmd, bool enable, unsigned int mtemp); int ndctl_cmd_smart_inject_ctrl_temperature(struct ndctl_cmd *cmd, bool enable, unsigned int ctemp); int ndctl_cmd_smart_inject_spares(struct ndctl_cmd *cmd, bool enable, unsigned int spares); int ndctl_cmd_smart_inject_fatal(struct ndctl_cmd *cmd, bool enable); int ndctl_cmd_smart_inject_unsafe_shutdown(struct ndctl_cmd *cmd, bool enable); int ndctl_dimm_smart_inject_supported(struct ndctl_dimm *dimm); struct ndctl_cmd *ndctl_dimm_cmd_new_vendor_specific(struct ndctl_dimm *dimm, unsigned int opcode, size_t input_size, size_t output_size); ssize_t ndctl_cmd_vendor_set_input(struct ndctl_cmd *cmd, void *buf, unsigned int len); ssize_t ndctl_cmd_vendor_get_output_size(struct ndctl_cmd *cmd); ssize_t ndctl_cmd_vendor_get_output(struct ndctl_cmd *cmd, void *buf, unsigned int len); struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_size(struct ndctl_dimm *dimm); struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_read(struct ndctl_cmd *cfg_size); struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_write(struct ndctl_cmd *cfg_read); int ndctl_dimm_zero_labels(struct ndctl_dimm *dimm); int ndctl_dimm_zero_label_extent(struct ndctl_dimm *dimm, unsigned int len, unsigned int offset); struct ndctl_cmd *ndctl_dimm_read_labels(struct ndctl_dimm *dimm); struct ndctl_cmd *ndctl_dimm_read_label_index(struct ndctl_dimm *dimm); struct ndctl_cmd *ndctl_dimm_read_label_extent(struct ndctl_dimm *dimm, unsigned int len, unsigned int offset); int ndctl_dimm_validate_labels(struct ndctl_dimm *dimm); enum ndctl_namespace_version { NDCTL_NS_VERSION_1_1, NDCTL_NS_VERSION_1_2, }; int ndctl_dimm_init_labels(struct ndctl_dimm *dimm, enum ndctl_namespace_version v); unsigned long ndctl_dimm_get_available_labels(struct ndctl_dimm *dimm); unsigned int ndctl_dimm_sizeof_namespace_label(struct ndctl_dimm *dimm); unsigned int ndctl_cmd_cfg_size_get_size(struct ndctl_cmd *cfg_size); ssize_t ndctl_cmd_cfg_read_get_data(struct ndctl_cmd *cfg_read, void *buf, unsigned int len, unsigned int offset); ssize_t ndctl_cmd_cfg_read_get_size(struct ndctl_cmd *cfg_read); int ndctl_cmd_cfg_read_set_extent(struct ndctl_cmd *cfg_read, unsigned int len, unsigned int offset); int ndctl_cmd_cfg_write_set_extent(struct ndctl_cmd *cfg_write, unsigned int len, unsigned int offset); ssize_t ndctl_cmd_cfg_write_set_data(struct ndctl_cmd *cfg_write, void *buf, unsigned int len, unsigned int offset); ssize_t ndctl_cmd_cfg_write_zero_data(struct ndctl_cmd *cfg_write); void ndctl_cmd_unref(struct ndctl_cmd *cmd); void ndctl_cmd_ref(struct ndctl_cmd *cmd); int ndctl_cmd_get_type(struct ndctl_cmd *cmd); int ndctl_cmd_get_status(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_get_firmware_status(struct ndctl_cmd *cmd); int ndctl_cmd_submit(struct ndctl_cmd *cmd); struct badblock { unsigned long long offset; unsigned int len; }; struct ndctl_region; struct ndctl_region *ndctl_region_get_first(struct ndctl_bus *bus); struct ndctl_region *ndctl_region_get_next(struct ndctl_region *region); #define ndctl_region_foreach(bus, region) \ for (region = ndctl_region_get_first(bus); \ region != NULL; \ region = ndctl_region_get_next(region)) struct badblock *ndctl_region_get_first_badblock(struct ndctl_region *region); struct badblock *ndctl_region_get_next_badblock(struct ndctl_region *region); #define ndctl_region_badblock_foreach(region, badblock) \ for (badblock = ndctl_region_get_first_badblock(region); \ badblock != NULL; \ badblock = ndctl_region_get_next_badblock(region)) unsigned int ndctl_region_get_id(struct ndctl_region *region); const char *ndctl_region_get_devname(struct ndctl_region *region); unsigned int ndctl_region_get_interleave_ways(struct ndctl_region *region); unsigned int ndctl_region_get_mappings(struct ndctl_region *region); unsigned long long ndctl_region_get_size(struct ndctl_region *region); unsigned long long ndctl_region_get_available_size(struct ndctl_region *region); unsigned long long ndctl_region_get_max_available_extent( struct ndctl_region *region); unsigned int ndctl_region_get_range_index(struct ndctl_region *region); unsigned int ndctl_region_get_type(struct ndctl_region *region); struct ndctl_namespace *ndctl_region_get_namespace_seed( struct ndctl_region *region); int ndctl_region_get_ro(struct ndctl_region *region); int ndctl_region_set_ro(struct ndctl_region *region, int ro); unsigned long long ndctl_region_get_resource(struct ndctl_region *region); struct ndctl_btt *ndctl_region_get_btt_seed(struct ndctl_region *region); struct ndctl_pfn *ndctl_region_get_pfn_seed(struct ndctl_region *region); unsigned int ndctl_region_get_nstype(struct ndctl_region *region); const char *ndctl_region_get_type_name(struct ndctl_region *region); struct ndctl_bus *ndctl_region_get_bus(struct ndctl_region *region); struct ndctl_ctx *ndctl_region_get_ctx(struct ndctl_region *region); struct ndctl_dimm *ndctl_region_get_first_dimm(struct ndctl_region *region); struct ndctl_dimm *ndctl_region_get_next_dimm(struct ndctl_region *region, struct ndctl_dimm *dimm); int ndctl_region_get_numa_node(struct ndctl_region *region); struct ndctl_region *ndctl_bus_get_region_by_physical_address(struct ndctl_bus *bus, unsigned long long address); #define ndctl_dimm_foreach_in_region(region, dimm) \ for (dimm = ndctl_region_get_first_dimm(region); \ dimm != NULL; \ dimm = ndctl_region_get_next_dimm(region, dimm)) enum ndctl_persistence_domain ndctl_region_get_persistence_domain( struct ndctl_region *region); int ndctl_region_is_enabled(struct ndctl_region *region); int ndctl_region_enable(struct ndctl_region *region); int ndctl_region_disable_invalidate(struct ndctl_region *region); int ndctl_region_disable_preserve(struct ndctl_region *region); void ndctl_region_cleanup(struct ndctl_region *region); int ndctl_region_deep_flush(struct ndctl_region *region); struct ndctl_interleave_set; struct ndctl_interleave_set *ndctl_region_get_interleave_set( struct ndctl_region *region); struct ndctl_interleave_set *ndctl_interleave_set_get_first( struct ndctl_bus *bus); struct ndctl_interleave_set *ndctl_interleave_set_get_next( struct ndctl_interleave_set *iset); #define ndctl_interleave_set_foreach(bus, iset) \ for (iset = ndctl_interleave_set_get_first(bus); \ iset != NULL; \ iset = ndctl_interleave_set_get_next(iset)) #define ndctl_dimm_foreach_in_interleave_set(iset, dimm) \ for (dimm = ndctl_interleave_set_get_first_dimm(iset); \ dimm != NULL; \ dimm = ndctl_interleave_set_get_next_dimm(iset, dimm)) int ndctl_interleave_set_is_active(struct ndctl_interleave_set *iset); unsigned long long ndctl_interleave_set_get_cookie( struct ndctl_interleave_set *iset); struct ndctl_region *ndctl_interleave_set_get_region( struct ndctl_interleave_set *iset); struct ndctl_dimm *ndctl_interleave_set_get_first_dimm( struct ndctl_interleave_set *iset); struct ndctl_dimm *ndctl_interleave_set_get_next_dimm( struct ndctl_interleave_set *iset, struct ndctl_dimm *dimm); struct ndctl_mapping; struct ndctl_mapping *ndctl_mapping_get_first(struct ndctl_region *region); struct ndctl_mapping *ndctl_mapping_get_next(struct ndctl_mapping *mapping); #define ndctl_mapping_foreach(region, mapping) \ for (mapping = ndctl_mapping_get_first(region); \ mapping != NULL; \ mapping = ndctl_mapping_get_next(mapping)) struct ndctl_dimm *ndctl_mapping_get_dimm(struct ndctl_mapping *mapping); struct ndctl_ctx *ndctl_mapping_get_ctx(struct ndctl_mapping *mapping); struct ndctl_bus *ndctl_mapping_get_bus(struct ndctl_mapping *mapping); struct ndctl_region *ndctl_mapping_get_region(struct ndctl_mapping *mapping); unsigned long long ndctl_mapping_get_offset(struct ndctl_mapping *mapping); unsigned long long ndctl_mapping_get_length(struct ndctl_mapping *mapping); int ndctl_mapping_get_position(struct ndctl_mapping *mapping); struct ndctl_namespace; struct ndctl_namespace *ndctl_namespace_get_first(struct ndctl_region *region); struct ndctl_namespace *ndctl_namespace_get_next(struct ndctl_namespace *ndns); #define ndctl_namespace_foreach(region, ndns) \ for (ndns = ndctl_namespace_get_first(region); \ ndns != NULL; \ ndns = ndctl_namespace_get_next(ndns)) #define ndctl_namespace_foreach_safe(region, ndns, _ndns) \ for (ndns = ndctl_namespace_get_first(region), \ _ndns = ndns ? ndctl_namespace_get_next(ndns) : NULL; \ ndns != NULL; \ ndns = _ndns, \ _ndns = _ndns ? ndctl_namespace_get_next(_ndns) : NULL) struct badblock *ndctl_namespace_get_first_badblock(struct ndctl_namespace *ndns); struct badblock *ndctl_namespace_get_next_badblock(struct ndctl_namespace *ndns); #define ndctl_namespace_badblock_foreach(ndns, badblock) \ for (badblock = ndctl_namespace_get_first_badblock(ndns); \ badblock != NULL; \ badblock = ndctl_namespace_get_next_badblock(ndns)) struct ndctl_ctx *ndctl_namespace_get_ctx(struct ndctl_namespace *ndns); struct ndctl_bus *ndctl_namespace_get_bus(struct ndctl_namespace *ndns); struct ndctl_region *ndctl_namespace_get_region(struct ndctl_namespace *ndns); struct ndctl_btt *ndctl_namespace_get_btt(struct ndctl_namespace *ndns); struct ndctl_pfn *ndctl_namespace_get_pfn(struct ndctl_namespace *ndns); struct ndctl_dax *ndctl_namespace_get_dax(struct ndctl_namespace *ndns); unsigned int ndctl_namespace_get_id(struct ndctl_namespace *ndns); const char *ndctl_namespace_get_devname(struct ndctl_namespace *ndns); unsigned int ndctl_namespace_get_type(struct ndctl_namespace *ndns); const char *ndctl_namespace_get_type_name(struct ndctl_namespace *ndns); const char *ndctl_namespace_get_block_device(struct ndctl_namespace *ndns); enum ndctl_namespace_mode { NDCTL_NS_MODE_MEMORY, NDCTL_NS_MODE_FSDAX = NDCTL_NS_MODE_MEMORY, NDCTL_NS_MODE_SAFE, NDCTL_NS_MODE_RAW, NDCTL_NS_MODE_DAX, NDCTL_NS_MODE_DEVDAX = NDCTL_NS_MODE_DAX, NDCTL_NS_MODE_UNKNOWN, /* must be last entry */ }; enum ndctl_namespace_mode ndctl_namespace_get_mode( struct ndctl_namespace *ndns); enum ndctl_namespace_mode ndctl_namespace_get_enforce_mode( struct ndctl_namespace *ndns); int ndctl_namespace_set_enforce_mode(struct ndctl_namespace *ndns, enum ndctl_namespace_mode mode); int ndctl_namespace_is_enabled(struct ndctl_namespace *ndns); int ndctl_namespace_enable(struct ndctl_namespace *ndns); int ndctl_namespace_disable(struct ndctl_namespace *ndns); int ndctl_namespace_disable_invalidate(struct ndctl_namespace *ndns); int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns); bool ndctl_namespace_is_active(struct ndctl_namespace *ndns); int ndctl_namespace_is_valid(struct ndctl_namespace *ndns); int ndctl_namespace_is_configured(struct ndctl_namespace *ndns); int ndctl_namespace_delete(struct ndctl_namespace *ndns); int ndctl_namespace_set_uuid(struct ndctl_namespace *ndns, uuid_t uu); void ndctl_namespace_get_uuid(struct ndctl_namespace *ndns, uuid_t uu); const char *ndctl_namespace_get_alt_name(struct ndctl_namespace *ndns); int ndctl_namespace_set_alt_name(struct ndctl_namespace *ndns, const char *alt_name); unsigned long long ndctl_namespace_get_size(struct ndctl_namespace *ndns); unsigned long long ndctl_namespace_get_resource(struct ndctl_namespace *ndns); int ndctl_namespace_set_size(struct ndctl_namespace *ndns, unsigned long long size); unsigned int ndctl_namespace_get_supported_sector_size( struct ndctl_namespace *ndns, int i); unsigned int ndctl_namespace_get_sector_size(struct ndctl_namespace *ndns); int ndctl_namespace_get_num_sector_sizes(struct ndctl_namespace *ndns); int ndctl_namespace_set_sector_size(struct ndctl_namespace *ndns, unsigned int sector_size); int ndctl_namespace_get_raw_mode(struct ndctl_namespace *ndns); int ndctl_namespace_set_raw_mode(struct ndctl_namespace *ndns, int raw_mode); int ndctl_namespace_get_numa_node(struct ndctl_namespace *ndns); int ndctl_namespace_inject_error(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, bool notify); int ndctl_namespace_inject_error2(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, unsigned int flags); int ndctl_namespace_uninject_error(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count); int ndctl_namespace_uninject_error2(struct ndctl_namespace *ndns, unsigned long long block, unsigned long long count, unsigned int flags); int ndctl_namespace_injection_status(struct ndctl_namespace *ndns); enum ndctl_namespace_inject_flags { NDCTL_NS_INJECT_NOTIFY = 0, NDCTL_NS_INJECT_SATURATE, }; struct ndctl_bb; unsigned long long ndctl_bb_get_block(struct ndctl_bb *bb); unsigned long long ndctl_bb_get_count(struct ndctl_bb *bb); struct ndctl_bb *ndctl_namespace_injection_get_first_bb( struct ndctl_namespace *ndns); struct ndctl_bb *ndctl_namespace_injection_get_next_bb( struct ndctl_namespace *ndns, struct ndctl_bb *bb); #define ndctl_namespace_bb_foreach(ndns, bb) \ for (bb = ndctl_namespace_injection_get_first_bb(ndns); \ bb != NULL; \ bb = ndctl_namespace_injection_get_next_bb(ndns, bb)) int ndctl_namespace_write_cache_is_enabled(struct ndctl_namespace *ndns); int ndctl_namespace_enable_write_cache(struct ndctl_namespace *ndns); int ndctl_namespace_disable_write_cache(struct ndctl_namespace *ndns); struct ndctl_btt; struct ndctl_btt *ndctl_btt_get_first(struct ndctl_region *region); struct ndctl_btt *ndctl_btt_get_next(struct ndctl_btt *btt); #define ndctl_btt_foreach(region, btt) \ for (btt = ndctl_btt_get_first(region); \ btt != NULL; \ btt = ndctl_btt_get_next(btt)) #define ndctl_btt_foreach_safe(region, btt, _btt) \ for (btt = ndctl_btt_get_first(region), \ _btt = btt ? ndctl_btt_get_next(btt) : NULL; \ btt != NULL; \ btt = _btt, \ _btt = _btt ? ndctl_btt_get_next(_btt) : NULL) struct ndctl_ctx *ndctl_btt_get_ctx(struct ndctl_btt *btt); struct ndctl_bus *ndctl_btt_get_bus(struct ndctl_btt *btt); struct ndctl_region *ndctl_btt_get_region(struct ndctl_btt *btt); unsigned int ndctl_btt_get_id(struct ndctl_btt *btt); unsigned int ndctl_btt_get_supported_sector_size(struct ndctl_btt *btt, int i); unsigned int ndctl_btt_get_sector_size(struct ndctl_btt *btt); int ndctl_btt_get_num_sector_sizes(struct ndctl_btt *btt); struct ndctl_namespace *ndctl_btt_get_namespace(struct ndctl_btt *btt); void ndctl_btt_get_uuid(struct ndctl_btt *btt, uuid_t uu); unsigned long long ndctl_btt_get_size(struct ndctl_btt *btt); int ndctl_btt_is_enabled(struct ndctl_btt *btt); int ndctl_btt_is_valid(struct ndctl_btt *btt); const char *ndctl_btt_get_devname(struct ndctl_btt *btt); const char *ndctl_btt_get_block_device(struct ndctl_btt *btt); int ndctl_btt_set_uuid(struct ndctl_btt *btt, uuid_t uu); int ndctl_btt_set_sector_size(struct ndctl_btt *btt, unsigned int sector_size); int ndctl_btt_set_namespace(struct ndctl_btt *btt, struct ndctl_namespace *ndns); int ndctl_btt_enable(struct ndctl_btt *btt); int ndctl_btt_delete(struct ndctl_btt *btt); int ndctl_btt_is_configured(struct ndctl_btt *btt); struct ndctl_pfn; struct ndctl_pfn *ndctl_pfn_get_first(struct ndctl_region *region); struct ndctl_pfn *ndctl_pfn_get_next(struct ndctl_pfn *pfn); #define ndctl_pfn_foreach(region, pfn) \ for (pfn = ndctl_pfn_get_first(region); \ pfn != NULL; \ pfn = ndctl_pfn_get_next(pfn)) #define ndctl_pfn_foreach_safe(region, pfn, _pfn) \ for (pfn = ndctl_pfn_get_first(region), \ _pfn = ndctl_pfn_get_next(pfn); \ pfn != NULL; \ pfn = _pfn, \ _pfn = _pfn ? ndctl_pfn_get_next(_pfn) : NULL) struct ndctl_ctx *ndctl_pfn_get_ctx(struct ndctl_pfn *pfn); struct ndctl_bus *ndctl_pfn_get_bus(struct ndctl_pfn *pfn); struct ndctl_region *ndctl_pfn_get_region(struct ndctl_pfn *pfn); unsigned int ndctl_pfn_get_id(struct ndctl_pfn *pfn); int ndctl_pfn_is_enabled(struct ndctl_pfn *pfn); int ndctl_pfn_is_valid(struct ndctl_pfn *pfn); const char *ndctl_pfn_get_devname(struct ndctl_pfn *pfn); const char *ndctl_pfn_get_block_device(struct ndctl_pfn *pfn); enum ndctl_pfn_loc { NDCTL_PFN_LOC_NONE, NDCTL_PFN_LOC_RAM, NDCTL_PFN_LOC_PMEM, }; int ndctl_pfn_set_location(struct ndctl_pfn *pfn, enum ndctl_pfn_loc loc); enum ndctl_pfn_loc ndctl_pfn_get_location(struct ndctl_pfn *pfn); int ndctl_pfn_set_uuid(struct ndctl_pfn *pfn, uuid_t uu); void ndctl_pfn_get_uuid(struct ndctl_pfn *pfn, uuid_t uu); int ndctl_pfn_has_align(struct ndctl_pfn *pfn); int ndctl_pfn_set_align(struct ndctl_pfn *pfn, unsigned long align); int ndctl_pfn_get_num_alignments(struct ndctl_pfn *pfn); unsigned long ndctl_pfn_get_align(struct ndctl_pfn *pfn); unsigned long ndctl_pfn_get_supported_alignment(struct ndctl_pfn *pfn, int i); unsigned long long ndctl_pfn_get_resource(struct ndctl_pfn *pfn); unsigned long long ndctl_pfn_get_size(struct ndctl_pfn *pfn); int ndctl_pfn_set_namespace(struct ndctl_pfn *pfn, struct ndctl_namespace *ndns); struct ndctl_namespace *ndctl_pfn_get_namespace(struct ndctl_pfn *pfn); int ndctl_pfn_enable(struct ndctl_pfn *pfn); int ndctl_pfn_delete(struct ndctl_pfn *pfn); int ndctl_pfn_is_configured(struct ndctl_pfn *pfn); #define ndctl_dax_foreach(region, dax) \ for (dax = ndctl_dax_get_first(region); \ dax != NULL; \ dax = ndctl_dax_get_next(dax)) #define ndctl_dax_foreach_safe(region, dax, _dax) \ for (dax = ndctl_dax_get_first(region), \ _dax = ndctl_dax_get_next(dax); \ dax != NULL; \ dax = _dax, \ _dax = _dax ? ndctl_dax_get_next(_dax) : NULL) struct ndctl_dax *ndctl_region_get_dax_seed(struct ndctl_region *region); struct ndctl_dax *ndctl_namespace_get_dax(struct ndctl_namespace *ndns); struct ndctl_dax *ndctl_dax_get_first(struct ndctl_region *region); struct ndctl_dax *ndctl_dax_get_next(struct ndctl_dax *dax); unsigned int ndctl_dax_get_id(struct ndctl_dax *dax); struct ndctl_namespace *ndctl_dax_get_namespace(struct ndctl_dax *dax); void ndctl_dax_get_uuid(struct ndctl_dax *dax, uuid_t uu); unsigned long long ndctl_dax_get_size(struct ndctl_dax *dax); unsigned long long ndctl_dax_get_resource(struct ndctl_dax *dax); int ndctl_dax_set_uuid(struct ndctl_dax *dax, uuid_t uu); enum ndctl_pfn_loc ndctl_dax_get_location(struct ndctl_dax *dax); int ndctl_dax_set_location(struct ndctl_dax *dax, enum ndctl_pfn_loc loc); int ndctl_dax_get_num_alignments(struct ndctl_dax *dax); unsigned long ndctl_dax_get_align(struct ndctl_dax *dax); unsigned long ndctl_dax_get_supported_alignment(struct ndctl_dax *dax, int i); int ndctl_dax_has_align(struct ndctl_dax *dax); int ndctl_dax_set_align(struct ndctl_dax *dax, unsigned long align); int ndctl_dax_set_namespace(struct ndctl_dax *dax, struct ndctl_namespace *ndns); struct ndctl_bus *ndctl_dax_get_bus(struct ndctl_dax *dax); struct ndctl_ctx *ndctl_dax_get_ctx(struct ndctl_dax *dax); const char *ndctl_dax_get_devname(struct ndctl_dax *dax); int ndctl_dax_is_valid(struct ndctl_dax *dax); int ndctl_dax_is_enabled(struct ndctl_dax *dax); struct ndctl_region *ndctl_dax_get_region(struct ndctl_dax *dax); int ndctl_dax_enable(struct ndctl_dax *dax); int ndctl_dax_delete(struct ndctl_dax *dax); int ndctl_dax_is_configured(struct ndctl_dax *dax); struct daxctl_region *ndctl_dax_get_daxctl_region(struct ndctl_dax *dax); enum ND_FW_STATUS { FW_SUCCESS = 0, /* success */ FW_ENOTSUPP, /* not supported */ FW_ENOTEXIST, /* device not exist */ FW_EINVAL, /* invalid input */ FW_EHWERR, /* hardware error */ FW_ERETRY, /* try again */ FW_EUNKNOWN, /* unknown reason */ FW_ENORES, /* out of resource */ FW_ENOTREADY, /* hardware not ready */ FW_EBUSY, /* firmware inprogress */ FW_EINVAL_CTX, /* invalid context passed in */ FW_ALREADY_DONE, /* firmware already updated */ FW_EBADFW, /* firmware failed verification */ FW_ABORTED, /* update sequence aborted success */ FW_ESEQUENCE, /* update sequence incorrect */ }; struct ndctl_cmd *ndctl_dimm_cmd_new_fw_get_info(struct ndctl_dimm *dimm); struct ndctl_cmd *ndctl_dimm_cmd_new_fw_start_update(struct ndctl_dimm *dimm); struct ndctl_cmd *ndctl_dimm_cmd_new_fw_send(struct ndctl_cmd *start, unsigned int offset, unsigned int len, void *data); struct ndctl_cmd *ndctl_dimm_cmd_new_fw_finish(struct ndctl_cmd *start); struct ndctl_cmd *ndctl_dimm_cmd_new_fw_abort(struct ndctl_cmd *start); struct ndctl_cmd *ndctl_dimm_cmd_new_fw_finish_query(struct ndctl_cmd *start); unsigned int ndctl_cmd_fw_info_get_storage_size(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_fw_info_get_max_send_len(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_fw_info_get_query_interval(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_fw_info_get_max_query_time(struct ndctl_cmd *cmd); unsigned long long ndctl_cmd_fw_info_get_run_version(struct ndctl_cmd *cmd); unsigned long long ndctl_cmd_fw_info_get_updated_version(struct ndctl_cmd *cmd); unsigned int ndctl_cmd_fw_start_get_context(struct ndctl_cmd *cmd); unsigned long long ndctl_cmd_fw_fquery_get_fw_rev(struct ndctl_cmd *cmd); enum ND_FW_STATUS ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd); struct ndctl_cmd *ndctl_dimm_cmd_new_ack_shutdown_count(struct ndctl_dimm *dimm); int ndctl_dimm_fw_update_supported(struct ndctl_dimm *dimm); int ndctl_cmd_xlat_firmware_status(struct ndctl_cmd *cmd); int ndctl_cmd_submit_xlat(struct ndctl_cmd *cmd); #define ND_PASSPHRASE_SIZE 32 #define ND_KEY_DESC_LEN 22 #define ND_KEY_DESC_PREFIX 7 enum ndctl_security_state { NDCTL_SECURITY_INVALID = -1, NDCTL_SECURITY_DISABLED = 0, NDCTL_SECURITY_UNLOCKED, NDCTL_SECURITY_LOCKED, NDCTL_SECURITY_FROZEN, NDCTL_SECURITY_OVERWRITE, }; enum ndctl_security_state ndctl_dimm_get_security(struct ndctl_dimm *dimm); bool ndctl_dimm_security_is_frozen(struct ndctl_dimm *dimm); int ndctl_dimm_update_passphrase(struct ndctl_dimm *dimm, long ckey, long nkey); int ndctl_dimm_disable_passphrase(struct ndctl_dimm *dimm, long key); int ndctl_dimm_freeze_security(struct ndctl_dimm *dimm); int ndctl_dimm_secure_erase(struct ndctl_dimm *dimm, long key); int ndctl_dimm_overwrite(struct ndctl_dimm *dimm, long key); int ndctl_dimm_wait_overwrite(struct ndctl_dimm *dimm); int ndctl_dimm_update_master_passphrase(struct ndctl_dimm *dimm, long ckey, long nkey); int ndctl_dimm_master_secure_erase(struct ndctl_dimm *dimm, long key); #define ND_KEY_DESC_SIZE 128 #define ND_KEY_CMD_SIZE 128 #ifdef __cplusplus } /* extern "C" */ #endif #endif ndctl-67/ndctl/list.c000066400000000000000000000312071355562357700146640ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include static struct { bool buses; bool dimms; bool regions; bool namespaces; bool idle; bool health; bool dax; bool media_errors; bool human; bool firmware; bool capabilities; int verbose; } list; static unsigned long listopts_to_flags(void) { unsigned long flags = 0; if (list.idle) flags |= UTIL_JSON_IDLE; if (list.media_errors) flags |= UTIL_JSON_MEDIA_ERRORS; if (list.dax) flags |= UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS; if (list.human) flags |= UTIL_JSON_HUMAN; if (list.verbose) flags |= UTIL_JSON_VERBOSE; if (list.capabilities) flags |= UTIL_JSON_CAPABILITIES; return flags; } struct util_filter_params param; static int did_fail; #define fail(fmt, ...) \ do { \ did_fail = 1; \ fprintf(stderr, "ndctl-%s:%s:%d: " fmt, \ VERSION, __func__, __LINE__, ##__VA_ARGS__); \ } while (0) static struct json_object *region_to_json(struct ndctl_region *region, unsigned long flags) { struct json_object *jregion = json_object_new_object(); struct json_object *jobj, *jbbs, *jmappings = NULL; struct ndctl_interleave_set *iset; struct ndctl_mapping *mapping; unsigned int bb_count = 0; unsigned long long extent; enum ndctl_persistence_domain pd; int numa; if (!jregion) return NULL; jobj = json_object_new_string(ndctl_region_get_devname(region)); if (!jobj) goto err; json_object_object_add(jregion, "dev", jobj); jobj = util_json_object_size(ndctl_region_get_size(region), flags); if (!jobj) goto err; json_object_object_add(jregion, "size", jobj); jobj = util_json_object_size(ndctl_region_get_available_size(region), flags); if (!jobj) goto err; json_object_object_add(jregion, "available_size", jobj); extent = ndctl_region_get_max_available_extent(region); if (extent != ULLONG_MAX) { jobj = util_json_object_size(extent, flags); if (!jobj) goto err; json_object_object_add(jregion, "max_available_extent", jobj); } switch (ndctl_region_get_type(region)) { case ND_DEVICE_REGION_PMEM: jobj = json_object_new_string("pmem"); break; case ND_DEVICE_REGION_BLK: jobj = json_object_new_string("blk"); break; default: jobj = NULL; } if (!jobj) goto err; json_object_object_add(jregion, "type", jobj); numa = ndctl_region_get_numa_node(region); if (numa >= 0 && flags & UTIL_JSON_VERBOSE) { jobj = json_object_new_int(numa); if (jobj) json_object_object_add(jregion, "numa_node", jobj); } iset = ndctl_region_get_interleave_set(region); if (iset) { jobj = util_json_object_hex( ndctl_interleave_set_get_cookie(iset), flags); if (!jobj) fail("\n"); else json_object_object_add(jregion, "iset_id", jobj); } ndctl_mapping_foreach(region, mapping) { struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping); struct json_object *jmapping; if (!list.dimms) break; if (!util_dimm_filter(dimm, param.dimm)) continue; if (!list.idle && !ndctl_dimm_is_enabled(dimm)) continue; if (!jmappings) { jmappings = json_object_new_array(); if (!jmappings) { fail("\n"); continue; } json_object_object_add(jregion, "mappings", jmappings); } jmapping = util_mapping_to_json(mapping, listopts_to_flags()); if (!jmapping) { fail("\n"); continue; } json_object_array_add(jmappings, jmapping); } if (!ndctl_region_is_enabled(region)) { jobj = json_object_new_string("disabled"); if (!jobj) goto err; json_object_object_add(jregion, "state", jobj); } jbbs = util_region_badblocks_to_json(region, &bb_count, flags); if (bb_count) { jobj = json_object_new_int(bb_count); if (!jobj) { json_object_put(jbbs); goto err; } json_object_object_add(jregion, "badblock_count", jobj); } if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs) json_object_object_add(jregion, "badblocks", jbbs); if (flags & UTIL_JSON_CAPABILITIES) { jobj = util_region_capabilities_to_json(region); if (jobj) json_object_object_add(jregion, "capabilities", jobj); } pd = ndctl_region_get_persistence_domain(region); switch (pd) { case PERSISTENCE_CPU_CACHE: jobj = json_object_new_string("cpu_cache"); break; case PERSISTENCE_MEM_CTRL: jobj = json_object_new_string("memory_controller"); break; case PERSISTENCE_NONE: jobj = json_object_new_string("none"); break; default: jobj = json_object_new_string("unknown"); break; } if (jobj) json_object_object_add(jregion, "persistence_domain", jobj); return jregion; err: fail("\n"); json_object_put(jregion); return NULL; } static void filter_namespace(struct ndctl_namespace *ndns, struct util_filter_ctx *ctx) { struct json_object *jndns; struct list_filter_arg *lfa = ctx->list; struct json_object *container = lfa->jregion ? lfa->jregion : lfa->jbus; if (!list.idle && !ndctl_namespace_is_active(ndns)) return; if (!lfa->jnamespaces) { lfa->jnamespaces = json_object_new_array(); if (!lfa->jnamespaces) { fail("\n"); return; } if (container) json_object_object_add(container, "namespaces", lfa->jnamespaces); } jndns = util_namespace_to_json(ndns, lfa->flags); if (!jndns) { fail("\n"); return; } json_object_array_add(lfa->jnamespaces, jndns); } static bool filter_region(struct ndctl_region *region, struct util_filter_ctx *ctx) { struct list_filter_arg *lfa = ctx->list; struct json_object *jbus = lfa->jbus; struct json_object *jregion; if (!list.regions) return true; if (!list.idle && !ndctl_region_is_enabled(region)) return true; if (!lfa->jregions) { lfa->jregions = json_object_new_array(); if (!lfa->jregions) { fail("\n"); return false; } if (jbus) json_object_object_add(jbus, "regions", lfa->jregions); } jregion = region_to_json(region, lfa->flags); if (!jregion) { fail("\n"); return false; } lfa->jregion = jregion; /* * We've started a new region, any previous jnamespaces will * have been parented to the last region. Clear out jnamespaces * so we start a new array per region. */ lfa->jnamespaces = NULL; /* * Without a bus we are collecting regions anonymously across * the platform. */ json_object_array_add(lfa->jregions, jregion); return true; } static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx) { struct list_filter_arg *lfa = ctx->list; struct json_object *jdimm; if (!list.idle && !ndctl_dimm_is_enabled(dimm)) return; if (!lfa->jdimms) { lfa->jdimms = json_object_new_array(); if (!lfa->jdimms) { fail("\n"); return; } if (lfa->jbus) json_object_object_add(lfa->jbus, "dimms", lfa->jdimms); } jdimm = util_dimm_to_json(dimm, lfa->flags); if (!jdimm) { fail("\n"); return; } if (list.health) { struct json_object *jhealth; jhealth = util_dimm_health_to_json(dimm); if (jhealth) json_object_object_add(jdimm, "health", jhealth); else if (ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SMART)) { /* * Failed to retrieve health data from a dimm * that otherwise supports smart data retrieval * commands. */ fail("\n"); return; } } if (list.firmware) { struct json_object *jfirmware; jfirmware = util_dimm_firmware_to_json(dimm, lfa->flags); if (jfirmware) json_object_object_add(jdimm, "firmware", jfirmware); } /* * Without a bus we are collecting dimms anonymously across the * platform. */ json_object_array_add(lfa->jdimms, jdimm); } static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *ctx) { struct list_filter_arg *lfa = ctx->list; /* * These sub-objects are local to a bus and, if present, have * been added as a child of a parent object on the last * iteration. */ if (lfa->jbuses) { lfa->jdimms = NULL; lfa->jregion = NULL; lfa->jregions = NULL; lfa->jnamespaces = NULL; } if (!list.buses) return true; if (!lfa->jbuses) { lfa->jbuses = json_object_new_array(); if (!lfa->jbuses) { fail("\n"); return false; } } lfa->jbus = util_bus_to_json(bus); if (!lfa->jbus) { fail("\n"); return false; } json_object_array_add(lfa->jbuses, lfa->jbus); return true; } static int list_display(struct list_filter_arg *lfa) { struct json_object *jnamespaces = lfa->jnamespaces; struct json_object *jregions = lfa->jregions; struct json_object *jdimms = lfa->jdimms; struct json_object *jbuses = lfa->jbuses; if (jbuses) util_display_json_array(stdout, jbuses, lfa->flags); else if ((!!jdimms + !!jregions + !!jnamespaces) > 1) { struct json_object *jplatform = json_object_new_object(); if (!jplatform) { fail("\n"); return -ENOMEM; } if (jdimms) json_object_object_add(jplatform, "dimms", jdimms); if (jregions) json_object_object_add(jplatform, "regions", jregions); if (jnamespaces && !jregions) json_object_object_add(jplatform, "namespaces", jnamespaces); printf("%s\n", json_object_to_json_string_ext(jplatform, JSON_C_TO_STRING_PRETTY)); json_object_put(jplatform); } else if (jdimms) util_display_json_array(stdout, jdimms, lfa->flags); else if (jregions) util_display_json_array(stdout, jregions, lfa->flags); else if (jnamespaces) util_display_json_array(stdout, jnamespaces, lfa->flags); return 0; } static int num_list_flags(void) { return list.buses + list.dimms + list.regions + list.namespaces; } int cmd_list(int argc, const char **argv, struct ndctl_ctx *ctx) { const struct option options[] = { OPT_STRING('b', "bus", ¶m.bus, "bus-id", "filter by bus"), OPT_STRING('r', "region", ¶m.region, "region-id", "filter by region"), OPT_STRING('d', "dimm", ¶m.dimm, "dimm-id", "filter by dimm"), OPT_STRING('n', "namespace", ¶m.namespace, "namespace-id", "filter by namespace id"), OPT_STRING('m', "mode", ¶m.mode, "namespace-mode", "filter by namespace mode"), OPT_STRING('t', "type", ¶m.type, "region-type", "filter by region-type"), OPT_STRING('U', "numa-node", ¶m.numa_node, "numa node", "filter by numa node"), OPT_BOOLEAN('B', "buses", &list.buses, "include bus info"), OPT_BOOLEAN('D', "dimms", &list.dimms, "include dimm info"), OPT_BOOLEAN('F', "firmware", &list.firmware, "include firmware info"), OPT_BOOLEAN('H', "health", &list.health, "include dimm health"), OPT_BOOLEAN('R', "regions", &list.regions, "include region info"), OPT_BOOLEAN('N', "namespaces", &list.namespaces, "include namespace info (default)"), OPT_BOOLEAN('X', "device-dax", &list.dax, "include device-dax info"), OPT_BOOLEAN('C', "capabilities", &list.capabilities, "include region capability info"), OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"), OPT_BOOLEAN('M', "media-errors", &list.media_errors, "include media errors"), OPT_BOOLEAN('u', "human", &list.human, "use human friendly number formats "), OPT_INCR('v', "verbose", &list.verbose, "increase output detail"), OPT_END(), }; const char * const u[] = { "ndctl list []", NULL }; struct util_filter_ctx fctx = { 0 }; struct list_filter_arg lfa = { 0 }; int i, rc; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); if (num_list_flags() == 0) { list.buses = !!param.bus; list.regions = !!param.region; list.dimms = !!param.dimm; if (list.dax && !param.mode) param.mode = "dax"; } switch (list.verbose) { default: case 3: list.idle = true; list.firmware = true; list.health = true; list.capabilities = true; case 2: list.dimms = true; list.buses = true; list.regions = true; case 1: list.media_errors = true; list.namespaces = true; list.dax = true; case 0: break; } if (num_list_flags() == 0) list.namespaces = true; fctx.filter_bus = filter_bus; fctx.filter_dimm = list.dimms ? filter_dimm : NULL; fctx.filter_region = filter_region; fctx.filter_namespace = list.namespaces ? filter_namespace : NULL; fctx.list = &lfa; lfa.flags = listopts_to_flags(); rc = util_filter_walk(ctx, &fctx, ¶m); if (rc) return rc; if (list_display(&lfa) || did_fail) return -ENOMEM; return 0; } ndctl-67/ndctl/load-keys.c000066400000000000000000000121711355562357700156000ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2019 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct parameters { const char *key_path; const char *tpm_handle; } param; static const char *key_names[] = {"user", "trusted", "encrypted"}; static struct loadkeys { enum key_type key_type; DIR *dir; int dirfd; } loadkey_ctx; static int load_master_key(struct loadkeys *lk_ctx, const char *keypath) { key_serial_t key; char *blob; int size, rc; char path[PATH_MAX]; enum key_type; rc = sprintf(path, "%s/nvdimm-master.blob", keypath); if (rc < 0) return -errno; if (param.tpm_handle) lk_ctx->key_type = KEY_TRUSTED; else lk_ctx->key_type = KEY_USER; key = keyctl_search(KEY_SPEC_USER_KEYRING, key_names[lk_ctx->key_type], "nvdimm-master", 0); if (key > 0) /* check to see if key already loaded */ return 0; if (key < 0 && errno != ENOKEY) { fprintf(stderr, "keyctl_search() failed: %s\n", strerror(errno)); return -errno; } blob = ndctl_load_key_blob(path, &size, param.tpm_handle, -1, lk_ctx->key_type); if (!blob) return -ENOMEM; key = add_key(key_names[lk_ctx->key_type], "nvdimm-master", blob, size, KEY_SPEC_USER_KEYRING); free(blob); if (key < 0) { fprintf(stderr, "add_key failed: %s\n", strerror(errno)); return -errno; } printf("nvdimm master key loaded.\n"); return 0; } static int load_dimm_keys(struct loadkeys *lk_ctx) { int rc; struct dirent *dent; char *fname = NULL, *id, *blob = NULL; char desc[ND_KEY_DESC_SIZE]; int size, count = 0; key_serial_t key; while ((dent = readdir(lk_ctx->dir)) != NULL) { if (dent->d_type != DT_REG) continue; fname = strdup(dent->d_name); if (!fname) { fprintf(stderr, "Unable to strdup %s\n", dent->d_name); return -ENOMEM; } /* * We want to pick up the second member of the file name * as the nvdimm id. */ id = strtok(fname, "_"); if (!id) { free(fname); continue; } if (strcmp(id, "nvdimm") != 0) { free(fname); continue; } id = strtok(NULL, "_"); if (!id) { free(fname); continue; } blob = ndctl_load_key_blob(dent->d_name, &size, NULL, lk_ctx->dirfd, KEY_ENCRYPTED); if (!blob) { free(fname); continue; } rc = sprintf(desc, "nvdimm:%s", id); if (rc < 0) { free(fname); free(blob); continue; } key = add_key("encrypted", desc, blob, size, KEY_SPEC_USER_KEYRING); if (key < 0) fprintf(stderr, "add_key failed: %s\n", strerror(errno)); else count++; free(fname); free(blob); } printf("%d nvdimm keys loaded\n", count); return 0; } static int check_tpm_handle(struct loadkeys *lk_ctx) { int fd, rc; FILE *fs; char *buf; fd = openat(lk_ctx->dirfd, "tpm.handle", O_RDONLY); if (fd < 0) return -errno; fs = fdopen(fd, "r"); if (!fs) { fprintf(stderr, "Failed to open file stream: %s\n", strerror(errno)); return -errno; } rc = fscanf(fs, "%ms", &buf); if (rc < 0) { rc = -errno; fprintf(stderr, "Failed to read file: %s\n", strerror(errno)); fclose(fs); return rc; } param.tpm_handle = buf; fclose(fs); return 0; } static int load_keys(struct loadkeys *lk_ctx, const char *keypath, const char *tpmhandle) { int rc; rc = chdir(keypath); if (rc < 0) { fprintf(stderr, "Change current work dir to %s failed: %s\n", param.key_path, strerror(errno)); rc = -errno; goto erropen; } lk_ctx->dir = opendir(param.key_path); if (!lk_ctx->dir) { fprintf(stderr, "Unable to open dir %s: %s\n", param.key_path, strerror(errno)); rc = -errno; goto erropen; } lk_ctx->dirfd = open(param.key_path, O_DIRECTORY); if (lk_ctx->dirfd < 0) { fprintf(stderr, "Unable to open dir %s: %s\n", param.key_path, strerror(errno)); rc = -errno; goto erropen; } if (!tpmhandle) { rc = check_tpm_handle(lk_ctx); if (rc < 0) fprintf(stderr, "No TPM handle discovered.\n"); } rc = load_master_key(lk_ctx, param.key_path); if (rc < 0) goto out; rc = load_dimm_keys(lk_ctx); if (rc < 0) goto out; out: close(lk_ctx->dirfd); erropen: closedir(lk_ctx->dir); return rc; } int cmd_load_keys(int argc, const char **argv, struct ndctl_ctx *ctx) { const struct option options[] = { OPT_FILENAME('p', "key-path", ¶m.key_path, "key-path", "override the default key path"), OPT_STRING('t', "tpm-handle", ¶m.tpm_handle, "tpm-handle", "TPM handle for trusted key"), OPT_END(), }; const char *const u[] = { "ndctl load-keys []", NULL }; int i; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); if (!param.key_path) param.key_path = strdup(NDCTL_KEYS_DIR); return load_keys(&loadkey_ctx, param.key_path, param.tpm_handle); } ndctl-67/ndctl/monitor.c000066400000000000000000000400111355562357700153710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018, FUJITSU LIMITED. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUF_SIZE 2048 /* reuse the core log helpers for the monitor logger */ #ifndef ENABLE_LOGGING #define ENABLE_LOGGING #endif #ifndef ENABLE_DEBUG #define ENABLE_DEBUG #endif #include static struct monitor { const char *log; const char *config_file; const char *dimm_event; FILE *log_file; bool daemon; bool human; bool verbose; unsigned int event_flags; struct log_ctx ctx; } monitor; struct monitor_dimm { struct ndctl_dimm *dimm; int health_eventfd; unsigned int health; unsigned int event_flags; struct list_node list; }; struct util_filter_params param; static int did_fail; #define fail(fmt, ...) \ do { \ did_fail = 1; \ dbg(&monitor, "ndctl-%s:%s:%d: " fmt, \ VERSION, __func__, __LINE__, ##__VA_ARGS__); \ } while (0) static void log_syslog(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { vsyslog(priority, format, args); } static void log_standard(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { if (priority == 6) vfprintf(stdout, format, args); else vfprintf(stderr, format, args); } static void log_file(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { FILE *f = monitor.log_file; if (priority != LOG_NOTICE) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); fprintf(f, "[%10ld.%09ld] [%d] ", ts.tv_sec, ts.tv_nsec, getpid()); vfprintf(f, format, args); } else vfprintf(f, format, args); fflush(f); } static struct json_object *dimm_event_to_json(struct monitor_dimm *mdimm) { struct json_object *jevent, *jobj; bool spares_flag, media_temp_flag, ctrl_temp_flag, health_state_flag, unclean_shutdown_flag; jevent = json_object_new_object(); if (!jevent) { fail("\n"); return NULL; } if (monitor.event_flags & ND_EVENT_SPARES_REMAINING) { spares_flag = !!(mdimm->event_flags & ND_EVENT_SPARES_REMAINING); jobj = json_object_new_boolean(spares_flag); if (jobj) json_object_object_add(jevent, "dimm-spares-remaining", jobj); } if (monitor.event_flags & ND_EVENT_MEDIA_TEMPERATURE) { media_temp_flag = !!(mdimm->event_flags & ND_EVENT_MEDIA_TEMPERATURE); jobj = json_object_new_boolean(media_temp_flag); if (jobj) json_object_object_add(jevent, "dimm-media-temperature", jobj); } if (monitor.event_flags & ND_EVENT_CTRL_TEMPERATURE) { ctrl_temp_flag = !!(mdimm->event_flags & ND_EVENT_CTRL_TEMPERATURE); jobj = json_object_new_boolean(ctrl_temp_flag); if (jobj) json_object_object_add(jevent, "dimm-controller-temperature", jobj); } if (monitor.event_flags & ND_EVENT_HEALTH_STATE) { health_state_flag = !!(mdimm->event_flags & ND_EVENT_HEALTH_STATE); jobj = json_object_new_boolean(health_state_flag); if (jobj) json_object_object_add(jevent, "dimm-health-state", jobj); } if (monitor.event_flags & ND_EVENT_UNCLEAN_SHUTDOWN) { unclean_shutdown_flag = !!(mdimm->event_flags & ND_EVENT_UNCLEAN_SHUTDOWN); jobj = json_object_new_boolean(unclean_shutdown_flag); if (jobj) json_object_object_add(jevent, "dimm-unclean-shutdown", jobj); } return jevent; } static int notify_dimm_event(struct monitor_dimm *mdimm) { struct json_object *jmsg, *jdimm, *jobj; struct timespec ts; char timestamp[32]; jmsg = json_object_new_object(); if (!jmsg) { fail("\n"); return -ENOMEM; } clock_gettime(CLOCK_REALTIME, &ts); sprintf(timestamp, "%10ld.%09ld", ts.tv_sec, ts.tv_nsec); jobj = json_object_new_string(timestamp); if (jobj) json_object_object_add(jmsg, "timestamp", jobj); jobj = json_object_new_int(getpid()); if (jobj) json_object_object_add(jmsg, "pid", jobj); jobj = dimm_event_to_json(mdimm); if (jobj) json_object_object_add(jmsg, "event", jobj); jdimm = util_dimm_to_json(mdimm->dimm, 0); if (jdimm) json_object_object_add(jmsg, "dimm", jdimm); jobj = util_dimm_health_to_json(mdimm->dimm); if (jobj) json_object_object_add(jdimm, "health", jobj); if (monitor.human) notice(&monitor, "%s\n", json_object_to_json_string_ext(jmsg, JSON_C_TO_STRING_PRETTY)); else notice(&monitor, "%s\n", json_object_to_json_string_ext(jmsg, JSON_C_TO_STRING_PLAIN)); free(jobj); free(jdimm); free(jmsg); return 0; } static struct monitor_dimm *util_dimm_event_filter(struct monitor_dimm *mdimm, unsigned int event_flags) { unsigned int health; mdimm->event_flags = ndctl_dimm_get_event_flags(mdimm->dimm); if (mdimm->event_flags == UINT_MAX) return NULL; health = ndctl_dimm_get_health(mdimm->dimm); if (health == UINT_MAX) return NULL; if (mdimm->health != health) mdimm->event_flags |= ND_EVENT_HEALTH_STATE; if (mdimm->event_flags & event_flags) return mdimm; return NULL; } static int enable_dimm_supported_threshold_alarms(struct ndctl_dimm *dimm) { unsigned int alarm; int rc = -EOPNOTSUPP; struct ndctl_cmd *st_cmd = NULL, *sst_cmd = NULL; const char *name = ndctl_dimm_get_devname(dimm); st_cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); if (!st_cmd) { err(&monitor, "%s: no smart threshold command support\n", name); goto out; } if (ndctl_cmd_submit_xlat(st_cmd) < 0) { err(&monitor, "%s: smart threshold command failed\n", name); goto out; } sst_cmd = ndctl_dimm_cmd_new_smart_set_threshold(st_cmd); if (!sst_cmd) { err(&monitor, "%s: no smart set threshold command support\n", name); goto out; } alarm = ndctl_cmd_smart_threshold_get_alarm_control(st_cmd); if (monitor.event_flags & ND_EVENT_SPARES_REMAINING) alarm |= ND_SMART_SPARE_TRIP; if (monitor.event_flags & ND_EVENT_MEDIA_TEMPERATURE) alarm |= ND_SMART_TEMP_TRIP; if (monitor.event_flags & ND_EVENT_CTRL_TEMPERATURE) alarm |= ND_SMART_CTEMP_TRIP; ndctl_cmd_smart_threshold_set_alarm_control(sst_cmd, alarm); rc = ndctl_cmd_submit_xlat(sst_cmd); if (rc < 0) { err(&monitor, "%s: smart set threshold command failed\n", name); goto out; } out: ndctl_cmd_unref(sst_cmd); ndctl_cmd_unref(st_cmd); return rc; } static bool filter_region(struct ndctl_region *region, struct util_filter_ctx *fctx) { return true; } static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *fctx) { struct monitor_dimm *mdimm; struct monitor_filter_arg *mfa = fctx->monitor; const char *name = ndctl_dimm_get_devname(dimm); if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SMART)) { err(&monitor, "%s: no smart support\n", name); return; } if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SMART_THRESHOLD)) { dbg(&monitor, "%s: no smart threshold support\n", name); } else if (!ndctl_dimm_is_flag_supported(dimm, ND_SMART_ALARM_VALID)) { err(&monitor, "%s: smart alarm invalid\n", name); return; } else if (enable_dimm_supported_threshold_alarms(dimm)) { err(&monitor, "%s: enable supported threshold alarms failed\n", name); return; } mdimm = calloc(1, sizeof(struct monitor_dimm)); if (!mdimm) { err(&monitor, "%s: calloc for monitor dimm failed\n", name); return; } mdimm->dimm = dimm; mdimm->health_eventfd = ndctl_dimm_get_health_eventfd(dimm); mdimm->health = ndctl_dimm_get_health(dimm); mdimm->event_flags = ndctl_dimm_get_event_flags(dimm); if (mdimm->event_flags && util_dimm_event_filter(mdimm, monitor.event_flags)) { if (notify_dimm_event(mdimm)) { err(&monitor, "%s: notify dimm event failed\n", name); free(mdimm); return; } } list_add_tail(&mfa->dimms, &mdimm->list); if (mdimm->health_eventfd > mfa->maxfd_dimm) mfa->maxfd_dimm = mdimm->health_eventfd; mfa->num_dimm++; return; } static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *fctx) { return true; } static int monitor_event(struct ndctl_ctx *ctx, struct monitor_filter_arg *mfa) { struct epoll_event ev, *events; int nfds, epollfd, i, rc = 0; struct monitor_dimm *mdimm; char buf; events = calloc(mfa->num_dimm, sizeof(struct epoll_event)); if (!events) { err(&monitor, "malloc for events error\n"); return -ENOMEM; } epollfd = epoll_create1(0); if (epollfd == -1) { err(&monitor, "epoll_create1 error\n"); rc = -errno; goto out; } list_for_each(&mfa->dimms, mdimm, list) { memset(&ev, 0, sizeof(ev)); rc = pread(mdimm->health_eventfd, &buf, sizeof(buf), 0); if (rc < 0) { err(&monitor, "pread error\n"); rc = -errno; goto out; } ev.data.ptr = mdimm; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, mdimm->health_eventfd, &ev) != 0) { err(&monitor, "epoll_ctl error\n"); rc = -errno; goto out; } } while (1) { did_fail = 0; nfds = epoll_wait(epollfd, events, mfa->num_dimm, -1); if (nfds <= 0 && errno != EINTR) { err(&monitor, "epoll_wait error: (%s)\n", strerror(errno)); rc = -errno; goto out; } for (i = 0; i < nfds; i++) { mdimm = events[i].data.ptr; if (util_dimm_event_filter(mdimm, monitor.event_flags)) { rc = notify_dimm_event(mdimm); if (rc) { err(&monitor, "%s: notify dimm event failed\n", ndctl_dimm_get_devname(mdimm->dimm)); did_fail = 1; goto out; } } rc = pread(mdimm->health_eventfd, &buf, sizeof(buf), 0); if (rc < 0) { err(&monitor, "pread error\n"); rc = -errno; goto out; } } if (did_fail) return 1; } out: free(events); return rc; } static void monitor_enable_all_events(struct monitor *_monitor) { _monitor->event_flags = ND_EVENT_SPARES_REMAINING | ND_EVENT_MEDIA_TEMPERATURE | ND_EVENT_CTRL_TEMPERATURE | ND_EVENT_HEALTH_STATE | ND_EVENT_UNCLEAN_SHUTDOWN; } static int parse_monitor_event(struct monitor *_monitor, struct ndctl_ctx *ctx) { char *dimm_event, *save; const char *event; int rc = 0; if (!_monitor->dimm_event) { monitor_enable_all_events(_monitor); return 0;; } dimm_event = strdup(_monitor->dimm_event); if (!dimm_event) return -ENOMEM; for (event = strtok_r(dimm_event, " ", &save); event; event = strtok_r(NULL, " ", &save)) { if (strcmp(event, "all") == 0) { monitor_enable_all_events(_monitor); goto out; } if (strcmp(event, "dimm-spares-remaining") == 0) _monitor->event_flags |= ND_EVENT_SPARES_REMAINING; else if (strcmp(event, "dimm-media-temperature") == 0) _monitor->event_flags |= ND_EVENT_MEDIA_TEMPERATURE; else if (strcmp(event, "dimm-controller-temperature") == 0) _monitor->event_flags |= ND_EVENT_CTRL_TEMPERATURE; else if (strcmp(event, "dimm-health-state") == 0) _monitor->event_flags |= ND_EVENT_HEALTH_STATE; else if (strcmp(event, "dimm-unclean-shutdown") == 0) _monitor->event_flags |= ND_EVENT_UNCLEAN_SHUTDOWN; else { err(&monitor, "no dimm-event named %s\n", event); rc = -EINVAL; goto out; } } out: free(dimm_event); return rc; } static void parse_config(const char **arg, char *key, char *val, char *ident) { struct strbuf value = STRBUF_INIT; size_t arg_len = *arg ? strlen(*arg) : 0; if (!ident || !key || (strcmp(ident, key) != 0)) return; if (arg_len) { strbuf_add(&value, *arg, arg_len); strbuf_addstr(&value, " "); } strbuf_addstr(&value, val); *arg = strbuf_detach(&value, NULL); } static int read_config_file(struct ndctl_ctx *ctx, struct monitor *_monitor, struct util_filter_params *_param) { FILE *f; size_t len = 0; int line = 0, rc = 0; char *buf = NULL, *seek, *value, *config_file; if (_monitor->config_file) config_file = strdup(_monitor->config_file); else config_file = strdup(NDCTL_CONF_FILE); if (!config_file) { fail("strdup default config file failed\n"); rc = -ENOMEM; goto out; } buf = malloc(BUF_SIZE); if (!buf) { fail("malloc read config-file buf error\n"); rc = -ENOMEM; goto out; } seek = buf; f = fopen(config_file, "r"); if (!f) { if (_monitor->config_file) { err(&monitor, "config-file: %s cannot be opened\n", config_file); rc = -errno; } goto out; } while (fgets(seek, BUF_SIZE, f)) { value = NULL; line++; while (isspace(*seek)) seek++; if (*seek == '#' || *seek == '\0') continue; value = strchr(seek, '='); if (!value) { fail("config-file syntax error, skip line[%i]\n", line); continue; } value[0] = '\0'; value++; while (isspace(value[0])) value++; len = strlen(seek); if (len == 0) continue; while (isspace(seek[len-1])) len--; seek[len] = '\0'; len = strlen(value); if (len == 0) continue; while (isspace(value[len-1])) len--; value[len] = '\0'; if (len == 0) continue; parse_config(&_param->bus, "bus", value, seek); parse_config(&_param->dimm, "dimm", value, seek); parse_config(&_param->region, "region", value, seek); parse_config(&_param->namespace, "namespace", value, seek); parse_config(&_monitor->dimm_event, "dimm-event", value, seek); if (!_monitor->log) parse_config(&_monitor->log, "log", value, seek); } fclose(f); out: free(buf); free(config_file); return rc; } int cmd_monitor(int argc, const char **argv, struct ndctl_ctx *ctx) { const struct option options[] = { OPT_STRING('b', "bus", ¶m.bus, "bus-id", "filter by bus"), OPT_STRING('r', "region", ¶m.region, "region-id", "filter by region"), OPT_STRING('d', "dimm", ¶m.dimm, "dimm-id", "filter by dimm"), OPT_STRING('n', "namespace", ¶m.namespace, "namespace-id", "filter by namespace id"), OPT_STRING('D', "dimm-event", &monitor.dimm_event, "name of event type", "filter by DIMM event type"), OPT_FILENAME('l', "log", &monitor.log, " | syslog | standard", "where to output the monitor's notification"), OPT_FILENAME('c', "config-file", &monitor.config_file, "config-file", "override the default config"), OPT_BOOLEAN('\0', "daemon", &monitor.daemon, "run ndctl monitor as a daemon"), OPT_BOOLEAN('u', "human", &monitor.human, "use human friendly output formats"), OPT_BOOLEAN('v', "verbose", &monitor.verbose, "emit extra debug messages to log"), OPT_END(), }; const char * const u[] = { "ndctl monitor []", NULL }; const char *prefix = "./"; struct util_filter_ctx fctx = { 0 }; struct monitor_filter_arg mfa = { 0 }; int i, rc; argc = parse_options_prefix(argc, argv, prefix, options, u, 0); for (i = 0; i < argc; i++) { error("unknown parameter \"%s\"\n", argv[i]); } if (argc) usage_with_options(u, options); log_init(&monitor.ctx, "ndctl/monitor", "NDCTL_MONITOR_LOG"); monitor.ctx.log_fn = log_standard; if (monitor.verbose) monitor.ctx.log_priority = LOG_DEBUG; else monitor.ctx.log_priority = LOG_INFO; rc = read_config_file(ctx, &monitor, ¶m); if (rc) goto out; if (monitor.log) { if (strncmp(monitor.log, "./", 2) != 0) fix_filename(prefix, (const char **)&monitor.log); if (strncmp(monitor.log, "./syslog", 8) == 0) monitor.ctx.log_fn = log_syslog; else if (strncmp(monitor.log, "./standard", 10) == 0) monitor.ctx.log_fn = log_standard; else { monitor.log_file = fopen(monitor.log, "a+"); if (!monitor.log_file) { error("open %s failed\n", monitor.log); rc = -errno; goto out; } monitor.ctx.log_fn = log_file; } } if (monitor.daemon) { if (!monitor.log || strncmp(monitor.log, "./", 2) == 0) monitor.ctx.log_fn = log_syslog; if (daemon(0, 0) != 0) { err(&monitor, "daemon start failed\n"); goto out; } info(&monitor, "ndctl monitor daemon started\n"); } if (parse_monitor_event(&monitor, ctx)) goto out; fctx.filter_bus = filter_bus; fctx.filter_dimm = filter_dimm; fctx.filter_region = filter_region; fctx.filter_namespace = NULL; fctx.arg = &mfa; list_head_init(&mfa.dimms); mfa.num_dimm = 0; mfa.maxfd_dimm = -1; mfa.flags = 0; rc = util_filter_walk(ctx, &fctx, ¶m); if (rc) goto out; if (!mfa.num_dimm) { info(&monitor, "no dimms to monitor, exiting\n"); if (!monitor.daemon) rc = -ENXIO; goto out; } rc = monitor_event(ctx, &mfa); out: if (monitor.log_file) fclose(monitor.log_file); return rc; } ndctl-67/ndctl/monitor.conf000066400000000000000000000037461355562357700161120ustar00rootroot00000000000000# This is the main ndctl monitor configuration file. It contains the # configuration directives that give ndctl monitor instructions. # You can change the configuration of ndctl monitor by editing this # file or using [--config-file=] option to override this one. # The changed value will work after restart ndctl monitor service. # In this file, lines starting with a hash (#) are comments. # The configurations should follow = style. # Multiple space-separated values are allowed, but except the following # characters: : ? / \ % " ' $ & ! * { } [ ] ( ) = < > @ # The objects to monitor are filtered via dimm's name by setting key "dimm". # If this value is different from the value of [--dimm=] option, # both of the values will work. # dimm = all # The objects to monitor are filtered via its parent bus by setting key "bus". # If this value is different from the value of [--bus=] option, # both of the values will work. # bus = all # The objects to monitor are filtered via region by setting key "region". # If this value is different from the value of [--region=] option, # both of the values will work. # region = all # The objects to monitor are filtered via namespace by setting key "namespace". # If this value is different from the value of [--namespace=] option, # both of the values will work. # namespace = all # The DIMM events to monitor are filtered via event type by setting key # "dimm-event". If this value is different from the value of # [--dimm-event=] option, both of the values will work. # dimm-event = all # Users can choose to output the notifications to syslog (log=syslog), # to standard output (log=standard) or to write into a special file (log=) # by setting key "log". If this value is in conflict with the value of # [--log=] option, this value will be ignored. # Note: Setting value to "standard" or relative path for will not work # when running moniotr as a daemon. # log = /var/log/ndctl/monitor.log ndctl-67/ndctl/namespace.c000066400000000000000000001212011355562357700156370ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include "action.h" #include #include #include #include #include #include #include #include #include #include static bool verbose; static bool force; static bool repair; static bool logfix; static bool scrub; static struct parameters { bool do_scan; bool mode_default; bool autolabel; bool greedy; const char *bus; const char *map; const char *type; const char *uuid; const char *name; const char *size; const char *mode; const char *region; const char *reconfig; const char *sector_size; const char *align; } param = { .autolabel = true, }; void builtin_xaction_namespace_reset(void) { /* * Initialize parameter data for the unit test case where * multiple calls to cmd__namespace() are made without * an intervening exit(). */ verbose = false; force = false; memset(¶m, 0, sizeof(param)); } #define NSLABEL_NAME_LEN 64 struct parsed_parameters { enum ndctl_pfn_loc loc; uuid_t uuid; char name[NSLABEL_NAME_LEN]; enum ndctl_namespace_mode mode; unsigned long long size; unsigned long sector_size; unsigned long align; bool autolabel; }; #define debug(fmt, ...) \ ({if (verbose) { \ fprintf(stderr, "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); \ } else { \ do { } while (0); \ }}) #define BASE_OPTIONS() \ OPT_STRING('b', "bus", ¶m.bus, "bus-id", \ "limit namespace to a bus with an id or provider of "), \ OPT_STRING('r', "region", ¶m.region, "region-id", \ "limit namespace to a region with an id or name of "), \ OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr") #define CREATE_OPTIONS() \ OPT_STRING('e', "reconfig", ¶m.reconfig, "reconfig namespace", \ "reconfigure existing namespace"), \ OPT_STRING('u', "uuid", ¶m.uuid, "uuid", \ "specify the uuid for the namespace (default: autogenerate)"), \ OPT_STRING('n', "name", ¶m.name, "name", \ "specify an optional free form name for the namespace"), \ OPT_STRING('s', "size", ¶m.size, "size", \ "specify the namespace size in bytes (default: available capacity)"), \ OPT_STRING('m', "mode", ¶m.mode, "operation-mode", \ "specify a mode for the namespace, 'sector', 'fsdax', 'devdax' or 'raw'"), \ OPT_STRING('M', "map", ¶m.map, "memmap-location", \ "specify 'mem' or 'dev' for the location of the memmap"), \ OPT_STRING('l', "sector-size", ¶m.sector_size, "lba-size", \ "specify the logical sector size in bytes"), \ OPT_STRING('t', "type", ¶m.type, "type", \ "specify the type of namespace to create 'pmem' or 'blk'"), \ OPT_STRING('a', "align", ¶m.align, "align", \ "specify the namespace alignment in bytes (default: 2M)"), \ OPT_BOOLEAN('f', "force", &force, "reconfigure namespace even if currently active"), \ OPT_BOOLEAN('L', "autolabel", ¶m.autolabel, "automatically initialize labels"), \ OPT_BOOLEAN('c', "continue", ¶m.greedy, \ "continue creating namespaces as long as the filter criteria are met") #define CHECK_OPTIONS() \ OPT_BOOLEAN('R', "repair", &repair, "perform metadata repairs"), \ OPT_BOOLEAN('L', "rewrite-log", &logfix, "regenerate the log"), \ OPT_BOOLEAN('f', "force", &force, "check namespace even if currently active") #define CLEAR_OPTIONS() \ OPT_BOOLEAN('s', "scrub", &scrub, "run a scrub to find latent errors") static const struct option base_options[] = { BASE_OPTIONS(), OPT_END(), }; static const struct option destroy_options[] = { BASE_OPTIONS(), OPT_BOOLEAN('f', "force", &force, "destroy namespace even if currently active"), OPT_END(), }; static const struct option create_options[] = { BASE_OPTIONS(), CREATE_OPTIONS(), OPT_END(), }; static const struct option check_options[] = { BASE_OPTIONS(), CHECK_OPTIONS(), OPT_END(), }; static const struct option clear_options[] = { BASE_OPTIONS(), CLEAR_OPTIONS(), OPT_END(), }; static int set_defaults(enum device_action mode) { int rc = 0; if (param.type) { if (strcmp(param.type, "pmem") == 0) /* pass */; else if (strcmp(param.type, "blk") == 0) /* pass */; else { error("invalid type '%s', must be either 'pmem' or 'blk'\n", param.type); rc = -EINVAL; } } else if (!param.reconfig && mode == ACTION_CREATE) param.type = "pmem"; if (param.mode) { if (strcmp(param.mode, "safe") == 0) /* pass */; else if (strcmp(param.mode, "sector") == 0) param.mode = "safe"; /* pass */ else if (strcmp(param.mode, "memory") == 0) /* pass */; else if (strcmp(param.mode, "fsdax") == 0) param.mode = "memory"; /* pass */ else if (strcmp(param.mode, "raw") == 0) /* pass */; else if (strcmp(param.mode, "dax") == 0) /* pass */; else if (strcmp(param.mode, "devdax") == 0) param.mode = "dax"; /* pass */ else { error("invalid mode '%s'\n", param.mode); rc = -EINVAL; } } else if (!param.reconfig && param.type) { if (strcmp(param.type, "pmem") == 0) param.mode = "memory"; else param.mode = "safe"; param.mode_default = true; } if (param.map) { if (strcmp(param.map, "mem") == 0) /* pass */; else if (strcmp(param.map, "dev") == 0) /* pass */; else { error("invalid map location '%s'\n", param.map); rc = -EINVAL; } if (!param.reconfig && param.mode && strcmp(param.mode, "memory") != 0 && strcmp(param.mode, "dax") != 0) { error("--map only valid for an dax mode pmem namespace\n"); rc = -EINVAL; } } else if (!param.reconfig) param.map = "dev"; /* check for incompatible mode and type combinations */ if (param.type && param.mode && strcmp(param.type, "blk") == 0 && (strcmp(param.mode, "memory") == 0 || strcmp(param.mode, "dax") == 0)) { error("only 'pmem' namespaces support dax operation\n"); rc = -ENXIO; } if (param.size && parse_size64(param.size) == ULLONG_MAX) { error("failed to parse namespace size '%s'\n", param.size); rc = -EINVAL; } if (param.align && parse_size64(param.align) == ULLONG_MAX) { error("failed to parse namespace alignment '%s'\n", param.align); rc = -EINVAL; } if (param.uuid) { uuid_t uuid; if (uuid_parse(param.uuid, uuid)) { error("failed to parse uuid: '%s'\n", param.uuid); rc = -EINVAL; } } if (param.sector_size) { if (parse_size64(param.sector_size) == ULLONG_MAX) { error("invalid sector size: %s\n", param.sector_size); rc = -EINVAL; } } else if (((param.type && strcmp(param.type, "blk") == 0) || (param.mode && strcmp(param.mode, "safe") == 0))) { /* default sector size for blk-type or safe-mode */ param.sector_size = "4096"; } return rc; } /* * parse_namespace_options - basic parsing sanity checks before we start * looking at actual namespace devices and available resources. */ static const char *parse_namespace_options(int argc, const char **argv, enum device_action mode, const struct option *options, char *xable_usage) { const char * const u[] = { xable_usage, NULL }; int i, rc = 0; param.do_scan = argc == 1; argc = parse_options(argc, argv, options, u, 0); rc = set_defaults(mode); if (argc == 0 && mode != ACTION_CREATE) { char *action_string; switch (mode) { case ACTION_ENABLE: action_string = "enable"; break; case ACTION_DISABLE: action_string = "disable"; break; case ACTION_DESTROY: action_string = "destroy"; break; case ACTION_CHECK: action_string = "check"; break; case ACTION_CLEAR: action_string = "clear errors for"; break; default: action_string = "<>"; break; } error("specify a namespace to %s, or \"all\"\n", action_string); rc = -EINVAL; } for (i = mode == ACTION_CREATE ? 0 : 1; i < argc; i++) { error("unknown extra parameter \"%s\"\n", argv[i]); rc = -EINVAL; } if (rc) { usage_with_options(u, options); return NULL; /* we won't return from usage_with_options() */ } return mode == ACTION_CREATE ? param.reconfig : argv[0]; } #define try(prefix, op, dev, p) \ do { \ int __rc = prefix##_##op(dev, p); \ if (__rc) { \ debug("%s: " #op " failed: %s\n", \ prefix##_get_devname(dev), \ strerror(abs(__rc))); \ return __rc; \ } \ } while (0) static bool do_setup_pfn(struct ndctl_namespace *ndns, struct parsed_parameters *p) { if (p->mode != NDCTL_NS_MODE_MEMORY) return false; /* * Dynamically allocated namespaces always require a pfn * instance, and a pfn device is required to place the memmap * array in device memory. */ if (!ndns || ndctl_namespace_get_mode(ndns) != NDCTL_NS_MODE_MEMORY || p->loc == NDCTL_PFN_LOC_PMEM) return true; return false; } static int setup_namespace(struct ndctl_region *region, struct ndctl_namespace *ndns, struct parsed_parameters *p) { uuid_t uuid; int rc; if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { try(ndctl_namespace, set_uuid, ndns, p->uuid); try(ndctl_namespace, set_alt_name, ndns, p->name); try(ndctl_namespace, set_size, ndns, p->size); } if (p->sector_size && p->sector_size < UINT_MAX) { int i, num = ndctl_namespace_get_num_sector_sizes(ndns); /* * With autolabel support we need to recheck if the * namespace gained sector_size support late in * namespace_reconfig(). */ for (i = 0; i < num; i++) if (ndctl_namespace_get_supported_sector_size(ndns, i) == p->sector_size) break; if (i < num) try(ndctl_namespace, set_sector_size, ndns, p->sector_size); else if (p->mode == NDCTL_NS_MODE_SAFE) /* pass, the btt sector_size will override */; else if (p->sector_size != 512) { error("%s: sector_size: %ld not supported\n", ndctl_namespace_get_devname(ndns), p->sector_size); return -EINVAL; } } uuid_generate(uuid); /* * Note, this call to ndctl_namespace_set_mode() is not error * checked since kernels older than 4.13 do not support this * property of namespaces and it is an opportunistic enforcement * mechanism. */ ndctl_namespace_set_enforce_mode(ndns, p->mode); if (do_setup_pfn(ndns, p)) { struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); try(ndctl_pfn, set_uuid, pfn, uuid); try(ndctl_pfn, set_location, pfn, p->loc); if (ndctl_pfn_has_align(pfn)) try(ndctl_pfn, set_align, pfn, p->align); try(ndctl_pfn, set_namespace, pfn, ndns); rc = ndctl_pfn_enable(pfn); if (rc) ndctl_pfn_set_namespace(pfn, NULL); } else if (p->mode == NDCTL_NS_MODE_DAX) { struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); try(ndctl_dax, set_uuid, dax, uuid); try(ndctl_dax, set_location, dax, p->loc); /* device-dax assumes 'align' attribute present */ try(ndctl_dax, set_align, dax, p->align); try(ndctl_dax, set_namespace, dax, ndns); rc = ndctl_dax_enable(dax); if (rc) ndctl_dax_set_namespace(dax, NULL); } else if (p->mode == NDCTL_NS_MODE_SAFE) { struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); /* * Handle the case of btt on a pmem namespace where the * pmem kernel support is pre-v1.2 namespace labels * support (does not support sector size settings). */ if (p->sector_size == UINT_MAX) p->sector_size = 4096; try(ndctl_btt, set_uuid, btt, uuid); try(ndctl_btt, set_sector_size, btt, p->sector_size); try(ndctl_btt, set_namespace, btt, ndns); rc = ndctl_btt_enable(btt); } else rc = ndctl_namespace_enable(ndns); if (rc) { error("%s: failed to enable\n", ndctl_namespace_get_devname(ndns)); } else { unsigned long flags = UTIL_JSON_DAX | UTIL_JSON_DAX_DEVS; struct json_object *jndns; if (isatty(1)) flags |= UTIL_JSON_HUMAN; jndns = util_namespace_to_json(ndns, flags); if (jndns) printf("%s\n", json_object_to_json_string_ext(jndns, JSON_C_TO_STRING_PRETTY)); } return rc; } static int is_namespace_active(struct ndctl_namespace *ndns) { return ndns && (ndctl_namespace_is_enabled(ndns) || ndctl_namespace_get_pfn(ndns) || ndctl_namespace_get_dax(ndns) || ndctl_namespace_get_btt(ndns)); } /* * validate_namespace_options - init parameters for setup_namespace * @region: parent of the namespace to create / reconfigure * @ndns: specified when we are reconfiguring, NULL otherwise * @p: parameters to fill * * parse_namespace_options() will have already done basic verification * of the parameters and set defaults in the !reconfigure case. When * reconfiguring fill in any unset options with defaults from the * namespace itself. * * Given that parse_namespace_options() runs before we have identified * the target namespace we need to do basic sanity checks here for * pmem-only attributes specified for blk namespace and vice versa. */ static int validate_namespace_options(struct ndctl_region *region, struct ndctl_namespace *ndns, struct parsed_parameters *p) { const char *region_name = ndctl_region_get_devname(region); unsigned long long size_align, units = 1, resource; struct ndctl_pfn *pfn = NULL; struct ndctl_dax *dax = NULL; unsigned int ways; int rc = 0; memset(p, 0, sizeof(*p)); if (!ndctl_region_is_enabled(region)) { debug("%s: disabled, skipping...\n", region_name); return -EAGAIN; } if (param.size) p->size = __parse_size64(param.size, &units); else if (ndns) p->size = ndctl_namespace_get_size(ndns); if (param.uuid) { if (uuid_parse(param.uuid, p->uuid) != 0) { debug("%s: invalid uuid\n", __func__); return -EINVAL; } } else uuid_generate(p->uuid); if (param.name) rc = snprintf(p->name, sizeof(p->name), "%s", param.name); else if (ndns) rc = snprintf(p->name, sizeof(p->name), "%s", ndctl_namespace_get_alt_name(ndns)); if (rc >= (int) sizeof(p->name)) { debug("%s: alt name overflow\n", __func__); return -EINVAL; } if (param.mode) { if (strcmp(param.mode, "memory") == 0) p->mode = NDCTL_NS_MODE_MEMORY; else if (strcmp(param.mode, "sector") == 0) p->mode = NDCTL_NS_MODE_SAFE; else if (strcmp(param.mode, "safe") == 0) p->mode = NDCTL_NS_MODE_SAFE; else if (strcmp(param.mode, "dax") == 0) p->mode = NDCTL_NS_MODE_DAX; else p->mode = NDCTL_NS_MODE_RAW; if (ndctl_region_get_type(region) != ND_DEVICE_REGION_PMEM && (p->mode == NDCTL_NS_MODE_MEMORY || p->mode == NDCTL_NS_MODE_DAX)) { debug("blk %s does not support %s mode\n", region_name, p->mode == NDCTL_NS_MODE_MEMORY ? "fsdax" : "devdax"); return -EAGAIN; } } else if (ndns) p->mode = ndctl_namespace_get_mode(ndns); if (p->mode == NDCTL_NS_MODE_MEMORY) { pfn = ndctl_region_get_pfn_seed(region); if (!pfn && param.mode_default) { debug("%s fsdax mode not available\n", region_name); p->mode = NDCTL_NS_MODE_RAW; } /* * NB: We only fail validation if a pfn-specific option is used */ } else if (p->mode == NDCTL_NS_MODE_DAX) { dax = ndctl_region_get_dax_seed(region); if (!dax) { error("Kernel does not support devdax mode\n"); return -ENODEV; } } if (param.align) { int i, alignments; switch (p->mode) { case NDCTL_NS_MODE_MEMORY: if (!pfn) { error("Kernel does not support setting an alignment in fsdax mode\n"); return -EINVAL; } alignments = ndctl_pfn_get_num_alignments(pfn); break; case NDCTL_NS_MODE_DAX: alignments = ndctl_dax_get_num_alignments(dax); break; default: error("%s mode does not support setting an alignment\n", p->mode == NDCTL_NS_MODE_SAFE ? "sector" : "raw"); return -ENXIO; } p->align = parse_size64(param.align); for (i = 0; i < alignments; i++) { uint64_t a; if (p->mode == NDCTL_NS_MODE_MEMORY) a = ndctl_pfn_get_supported_alignment(pfn, i); else a = ndctl_dax_get_supported_alignment(dax, i); if (p->align == a) break; } if (i >= alignments) { error("unsupported align: %s\n", param.align); return -ENXIO; } } else { /* * If we are trying to reconfigure with the same namespace mode, * use the align details from the original namespace. Otherwise * pick the align details from seed namespace */ if (ndns && p->mode == ndctl_namespace_get_mode(ndns)) { struct ndctl_pfn *ns_pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *ns_dax = ndctl_namespace_get_dax(ndns); if (ns_pfn) p->align = ndctl_pfn_get_align(ns_pfn); else if (ns_dax) p->align = ndctl_dax_get_align(ns_dax); else p->align = sysconf(_SC_PAGE_SIZE); } else /* * Use the seed namespace alignment as the default if we need * one. If we don't then use PAGE_SIZE so the size_align * checking works. */ if (p->mode == NDCTL_NS_MODE_MEMORY) { /* * The initial pfn device support in the kernel didn't * have the 'align' sysfs attribute and assumed a 2MB * alignment. Fall back to that if we don't have the * attribute. */ if (pfn && ndctl_pfn_has_align(pfn)) p->align = ndctl_pfn_get_align(pfn); else p->align = SZ_2M; } else if (p->mode == NDCTL_NS_MODE_DAX) { /* * device dax mode was added after the align attribute * so checking for it is unnecessary. */ p->align = ndctl_dax_get_align(dax); } else { p->align = sysconf(_SC_PAGE_SIZE); } /* * Fallback to a page alignment if the region is not aligned * to the default. This is mainly useful for the nfit_test * use case where it is backed by vmalloc memory. */ resource = ndctl_region_get_resource(region); if (resource < ULLONG_MAX && (resource & (p->align - 1))) { debug("%s: falling back to a page alignment\n", region_name); p->align = sysconf(_SC_PAGE_SIZE); } } size_align = p->align; /* (re-)validate that the size satisfies the alignment */ ways = ndctl_region_get_interleave_ways(region); if (p->size % (size_align * ways)) { char *suffix = ""; if (units == SZ_1K) suffix = "K"; else if (units == SZ_1M) suffix = "M"; else if (units == SZ_1G) suffix = "G"; else if (units == SZ_1T) suffix = "T"; /* * Make the recommendation in the units of the '--size' * option */ size_align = max(units, size_align) * ways; p->size /= size_align; p->size++; p->size *= size_align; p->size /= units; error("'--size=' must align to interleave-width: %d and alignment: %ld\n" "did you intend --size=%lld%s?\n", ways, p->align, p->size, suffix); return -EINVAL; } if (param.sector_size) { struct ndctl_btt *btt; int num, i; p->sector_size = parse_size64(param.sector_size); btt = ndctl_region_get_btt_seed(region); if (p->mode == NDCTL_NS_MODE_SAFE) { if (!btt) { debug("%s: does not support 'sector' mode\n", region_name); return -EINVAL; } num = ndctl_btt_get_num_sector_sizes(btt); for (i = 0; i < num; i++) if (ndctl_btt_get_supported_sector_size(btt, i) == p->sector_size) break; if (i >= num) { debug("%s: does not support btt sector_size %lu\n", region_name, p->sector_size); return -EINVAL; } } else { struct ndctl_namespace *seed = ndns; if (!seed) seed = ndctl_region_get_namespace_seed(region); num = ndctl_namespace_get_num_sector_sizes(seed); for (i = 0; i < num; i++) if (ndctl_namespace_get_supported_sector_size(seed, i) == p->sector_size) break; if (i >= num) { debug("%s: does not support namespace sector_size %lu\n", region_name, p->sector_size); return -EINVAL; } } } else if (ndns) { struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); /* * If the target mode is still 'safe' carry forward the * sector size, otherwise fall back to what the * namespace supports. */ if (btt && p->mode == NDCTL_NS_MODE_SAFE) p->sector_size = ndctl_btt_get_sector_size(btt); else p->sector_size = ndctl_namespace_get_sector_size(ndns); } else { struct ndctl_namespace *seed; seed = ndctl_region_get_namespace_seed(region); if (ndctl_namespace_get_type(seed) == ND_DEVICE_NAMESPACE_BLK) debug("%s: set_defaults() should preclude this?\n", ndctl_region_get_devname(region)); /* * Pick a default sector size for a pmem namespace based * on what the kernel supports. */ if (ndctl_namespace_get_num_sector_sizes(seed) == 0) p->sector_size = UINT_MAX; else p->sector_size = 512; } if (param.map) { if (!strcmp(param.map, "mem")) p->loc = NDCTL_PFN_LOC_RAM; else p->loc = NDCTL_PFN_LOC_PMEM; if (ndns && p->mode != NDCTL_NS_MODE_MEMORY && p->mode != NDCTL_NS_MODE_DAX) { debug("%s: --map= only valid for fsdax mode namespace\n", ndctl_namespace_get_devname(ndns)); return -EINVAL; } } else if (p->mode == NDCTL_NS_MODE_MEMORY || p->mode == NDCTL_NS_MODE_DAX) p->loc = NDCTL_PFN_LOC_PMEM; if (!pfn && do_setup_pfn(ndns, p)) { error("operation failed, %s cannot support requested mode\n", region_name); return -EINVAL; } p->autolabel = param.autolabel; return 0; } static struct ndctl_namespace *region_get_namespace(struct ndctl_region *region) { struct ndctl_namespace *ndns; /* prefer the 0th namespace if it is idle */ ndctl_namespace_foreach(region, ndns) if (ndctl_namespace_get_id(ndns) == 0 && !is_namespace_active(ndns)) return ndns; return ndctl_region_get_namespace_seed(region); } static int namespace_create(struct ndctl_region *region) { const char *devname = ndctl_region_get_devname(region); unsigned long long available; struct ndctl_namespace *ndns; struct parsed_parameters p; int rc; rc = validate_namespace_options(region, NULL, &p); if (rc) return rc; if (ndctl_region_get_ro(region)) { debug("%s: read-only, ineligible for namespace creation\n", devname); return -EAGAIN; } if (ndctl_region_get_nstype(region) == ND_DEVICE_NAMESPACE_IO) available = ndctl_region_get_size(region); else { available = ndctl_region_get_max_available_extent(region); if (available == ULLONG_MAX) available = ndctl_region_get_available_size(region); } if (!available || p.size > available) { debug("%s: insufficient capacity size: %llx avail: %llx\n", devname, p.size, available); return -EAGAIN; } if (p.size == 0) p.size = available; ndns = region_get_namespace(region); if (!ndns || is_namespace_active(ndns)) { debug("%s: no %s namespace seed\n", devname, ndns ? "idle" : "available"); return -EAGAIN; } rc = setup_namespace(region, ndns, &p); if (rc) { ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_RAW); ndctl_namespace_delete(ndns); } return rc; } /* * Return convention: * rc < 0 : Error while zeroing, propagate forward * rc == 0 : Successfully cleared the info block, report as destroyed * rc > 0 : skipped, do not count */ static int zero_info_block(struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); int fd, rc = -ENXIO, info_size = 8192; void *buf = NULL, *read_buf = NULL; char path[50]; ndctl_namespace_set_raw_mode(ndns, 1); rc = ndctl_namespace_enable(ndns); if (rc < 0) { debug("%s failed to enable for zeroing, continuing\n", devname); rc = 1; goto out; } if (posix_memalign(&buf, 4096, info_size) != 0) { rc = -ENOMEM; goto out; } if (posix_memalign(&read_buf, 4096, info_size) != 0) { rc = -ENOMEM; goto out; } sprintf(path, "/dev/%s", ndctl_namespace_get_block_device(ndns)); fd = open(path, O_RDWR|O_DIRECT|O_EXCL); if (fd < 0) { debug("%s: failed to open %s to zero info block\n", devname, path); goto out; } memset(buf, 0, info_size); rc = pread(fd, read_buf, info_size, 0); if (rc < info_size) { debug("%s: failed to read info block, continuing\n", devname); } if (memcmp(buf, read_buf, info_size) == 0) { rc = 1; goto out_close; } rc = pwrite(fd, buf, info_size, 0); if (rc < info_size) { debug("%s: failed to zero info block %s\n", devname, path); rc = -ENXIO; } else rc = 0; out_close: close(fd); out: ndctl_namespace_set_raw_mode(ndns, 0); ndctl_namespace_disable_invalidate(ndns); free(read_buf); free(buf); return rc; } static int namespace_destroy(struct ndctl_region *region, struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); bool did_zero = false; int rc; if (ndctl_region_get_ro(region)) { error("%s: read-only, re-configuration disabled\n", devname); return -ENXIO; } if (ndctl_namespace_is_active(ndns) && !force) { error("%s is active, specify --force for re-configuration\n", devname); return -EBUSY; } else { rc = ndctl_namespace_disable_safe(ndns); if (rc) return rc; } ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_RAW); if (pfn || btt || dax) { rc = zero_info_block(ndns); if (rc < 0) return rc; if (rc == 0) did_zero = true; } switch (ndctl_namespace_get_type(ndns)) { case ND_DEVICE_NAMESPACE_PMEM: case ND_DEVICE_NAMESPACE_BLK: break; default: /* * for legacy namespaces, we we did any info block * zeroing, we need "processed" to be incremented * but otherwise we are skipping in the count */ if (did_zero) rc = 0; else rc = 1; goto out; } rc = ndctl_namespace_delete(ndns); if (rc) debug("%s: failed to reclaim\n", devname); out: return rc; } static int enable_labels(struct ndctl_region *region) { int mappings = ndctl_region_get_mappings(region); struct ndctl_cmd *cmd_read = NULL; enum ndctl_namespace_version v; struct ndctl_dimm *dimm; int count; /* no dimms => no labels */ if (!mappings) return 0; count = 0; ndctl_dimm_foreach_in_region(region, dimm) { if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_GET_CONFIG_SIZE)) break; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_GET_CONFIG_DATA)) break; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SET_CONFIG_DATA)) break; count++; } /* all the dimms must support labeling */ if (count != mappings) return 0; ndctl_region_disable_invalidate(region); count = 0; ndctl_dimm_foreach_in_region(region, dimm) if (ndctl_dimm_is_active(dimm)) { warning("%s is active in %s, failing autolabel\n", ndctl_dimm_get_devname(dimm), ndctl_region_get_devname(region)); count++; } /* some of the dimms belong to multiple regions?? */ if (count) goto out; v = NDCTL_NS_VERSION_1_2; retry: ndctl_dimm_foreach_in_region(region, dimm) { int num_labels, avail; ndctl_cmd_unref(cmd_read); cmd_read = ndctl_dimm_read_label_index(dimm); if (!cmd_read) continue; num_labels = ndctl_dimm_init_labels(dimm, v); if (num_labels < 0) continue; ndctl_dimm_disable(dimm); ndctl_dimm_enable(dimm); /* * If the kernel appears to not understand v1.2 labels, * try v1.1. Note, we increment avail by 1 to account * for the one free label that the kernel always * maintains for ongoing updates. */ avail = ndctl_dimm_get_available_labels(dimm) + 1; if (num_labels != avail && v == NDCTL_NS_VERSION_1_2) { v = NDCTL_NS_VERSION_1_1; goto retry; } } ndctl_cmd_unref(cmd_read); out: ndctl_region_enable(region); if (ndctl_region_get_nstype(region) != ND_DEVICE_NAMESPACE_PMEM) { debug("%s: failed to initialize labels\n", ndctl_region_get_devname(region)); return -EBUSY; } return 0; } static int namespace_reconfig(struct ndctl_region *region, struct ndctl_namespace *ndns) { struct parsed_parameters p; int rc; rc = validate_namespace_options(region, ndns, &p); if (rc) return rc; rc = namespace_destroy(region, ndns); if (rc < 0) return rc; /* check if we can enable labels on this region */ if (ndctl_region_get_nstype(region) == ND_DEVICE_NAMESPACE_IO && p.autolabel) { /* if this fails, try to continue label-less */ enable_labels(region); } ndns = region_get_namespace(region); if (!ndns || is_namespace_active(ndns)) { debug("%s: no %s namespace seed\n", ndctl_region_get_devname(region), ndns ? "idle" : "available"); return -ENODEV; } return setup_namespace(region, ndns, &p); } int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force, bool repair, bool logfix); static int bus_send_clear(struct ndctl_bus *bus, unsigned long long start, unsigned long long size) { const char *busname = ndctl_bus_get_provider(bus); struct ndctl_cmd *cmd_cap, *cmd_clear; unsigned long long cleared; struct ndctl_range range; int rc; /* get ars_cap */ cmd_cap = ndctl_bus_cmd_new_ars_cap(bus, start, size); if (!cmd_cap) { debug("bus: %s failed to create cmd\n", busname); return -ENOTTY; } rc = ndctl_cmd_submit_xlat(cmd_cap); if (rc < 0) { debug("bus: %s failed to submit cmd: %d\n", busname, rc); goto out_cap; } /* send clear_error */ if (ndctl_cmd_ars_cap_get_range(cmd_cap, &range)) { debug("bus: %s failed to get ars_cap range\n", busname); rc = -ENXIO; goto out_cap; } cmd_clear = ndctl_bus_cmd_new_clear_error(range.address, range.length, cmd_cap); if (!cmd_clear) { debug("bus: %s failed to create cmd\n", busname); rc = -ENOTTY; goto out_cap; } rc = ndctl_cmd_submit_xlat(cmd_clear); if (rc < 0) { debug("bus: %s failed to submit cmd: %d\n", busname, rc); goto out_clr; } cleared = ndctl_cmd_clear_error_get_cleared(cmd_clear); if (cleared != range.length) { debug("bus: %s expected to clear: %lld actual: %lld\n", busname, range.length, cleared); rc = -ENXIO; } out_clr: ndctl_cmd_unref(cmd_clear); out_cap: ndctl_cmd_unref(cmd_cap); return rc; } static int nstype_clear_badblocks(struct ndctl_namespace *ndns, const char *devname, unsigned long long dev_begin, unsigned long long dev_size) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_bus *bus = ndctl_region_get_bus(region); unsigned long long region_begin, dev_end; unsigned int cleared = 0; struct badblock *bb; int rc = 0; region_begin = ndctl_region_get_resource(region); if (region_begin == ULLONG_MAX) { rc = -errno; if (ndctl_namespace_enable(ndns) < 0) error("%s: failed to reenable namespace\n", devname); return rc; } dev_end = dev_begin + dev_size - 1; ndctl_region_badblock_foreach(region, bb) { unsigned long long bb_begin, bb_end, bb_len; bb_begin = region_begin + (bb->offset << 9); bb_len = (unsigned long long)bb->len << 9; bb_end = bb_begin + bb_len - 1; /* bb is not fully contained in the usable area */ if (bb_begin < dev_begin || bb_end > dev_end) continue; rc = bus_send_clear(bus, bb_begin, bb_len); if (rc) { error("%s: failed to clear badblock at {%lld, %u}\n", devname, bb->offset, bb->len); break; } cleared += bb->len; } debug("%s: cleared %u badblocks\n", devname, cleared); rc = ndctl_namespace_enable(ndns); if (rc < 0) return rc; return 0; } static int dax_clear_badblocks(struct ndctl_dax *dax) { struct ndctl_namespace *ndns = ndctl_dax_get_namespace(dax); const char *devname = ndctl_dax_get_devname(dax); unsigned long long begin, size; int rc; begin = ndctl_dax_get_resource(dax); if (begin == ULLONG_MAX) return -ENXIO; size = ndctl_dax_get_size(dax); if (size == ULLONG_MAX) return -ENXIO; rc = ndctl_namespace_disable_safe(ndns); if (rc) { error("%s: unable to disable namespace: %s\n", devname, strerror(-rc)); return rc; } return nstype_clear_badblocks(ndns, devname, begin, size); } static int pfn_clear_badblocks(struct ndctl_pfn *pfn) { struct ndctl_namespace *ndns = ndctl_pfn_get_namespace(pfn); const char *devname = ndctl_pfn_get_devname(pfn); unsigned long long begin, size; int rc; begin = ndctl_pfn_get_resource(pfn); if (begin == ULLONG_MAX) return -ENXIO; size = ndctl_pfn_get_size(pfn); if (size == ULLONG_MAX) return -ENXIO; rc = ndctl_namespace_disable_safe(ndns); if (rc) { error("%s: unable to disable namespace: %s\n", devname, strerror(-rc)); return rc; } return nstype_clear_badblocks(ndns, devname, begin, size); } static int raw_clear_badblocks(struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); unsigned long long begin, size; int rc; begin = ndctl_namespace_get_resource(ndns); if (begin == ULLONG_MAX) return -ENXIO; size = ndctl_namespace_get_size(ndns); if (size == ULLONG_MAX) return -ENXIO; rc = ndctl_namespace_disable_safe(ndns); if (rc) { error("%s: unable to disable namespace: %s\n", devname, strerror(-rc)); return rc; } return nstype_clear_badblocks(ndns, devname, begin, size); } static int namespace_wait_scrub(struct ndctl_namespace *ndns, bool do_scrub) { const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns); int in_progress, rc; in_progress = ndctl_bus_get_scrub_state(bus); if (in_progress < 0) { error("%s: Unable to determine scrub state: %s\n", devname, strerror(-in_progress)); return in_progress; } /* start a scrub if asked and if one isn't in progress */ if (do_scrub && (!in_progress)) { rc = ndctl_bus_start_scrub(bus); if (rc) { error("%s: Unable to start scrub: %s\n", devname, strerror(-rc)); return rc; } } /* * wait for any in-progress scrub, whether started above, or * started automatically at boot time */ rc = ndctl_bus_wait_for_scrub_completion(bus); if (rc) { error("%s: Error waiting for scrub: %s\n", devname, strerror(-rc)); return rc; } return 0; } static int namespace_clear_bb(struct ndctl_namespace *ndns, bool do_scrub) { struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); struct json_object *jndns; int rc; if (btt) { /* skip btt error clearing for now */ debug("%s: skip error clearing for btt\n", ndctl_btt_get_devname(btt)); return 1; } rc = namespace_wait_scrub(ndns, do_scrub); if (rc) return rc; if (dax) rc = dax_clear_badblocks(dax); else if (pfn) rc = pfn_clear_badblocks(pfn); else rc = raw_clear_badblocks(ndns); if (rc) return rc; jndns = util_namespace_to_json(ndns, UTIL_JSON_MEDIA_ERRORS); if (jndns) printf("%s\n", json_object_to_json_string_ext(jndns, JSON_C_TO_STRING_PRETTY)); return 0; } static int do_xaction_namespace(const char *namespace, enum device_action action, struct ndctl_ctx *ctx, int *processed) { struct ndctl_namespace *ndns, *_n; int rc = -ENXIO, saved_rc = 0; struct ndctl_region *region; const char *ndns_name; struct ndctl_bus *bus; *processed = 0; if (!namespace && action != ACTION_CREATE) return rc; if (verbose) ndctl_set_log_priority(ctx, LOG_DEBUG); ndctl_bus_foreach(ctx, bus) { bool do_scrub; if (!util_bus_filter(bus, param.bus)) continue; /* only request scrubbing once per bus */ do_scrub = scrub; ndctl_region_foreach(bus, region) { if (!util_region_filter(region, param.region)) continue; if (param.type) { if (strcmp(param.type, "pmem") == 0 && ndctl_region_get_type(region) == ND_DEVICE_REGION_PMEM) /* pass */; else if (strcmp(param.type, "blk") == 0 && ndctl_region_get_type(region) == ND_DEVICE_REGION_BLK) /* pass */; else continue; } if (action == ACTION_CREATE && !namespace) { rc = namespace_create(region); if (rc == -EAGAIN) continue; if (rc == 0) { (*processed)++; if (param.greedy) continue; } if (force) { if (rc) saved_rc = rc; continue; } return rc; } ndctl_namespace_foreach_safe(region, ndns, _n) { ndns_name = ndctl_namespace_get_devname(ndns); if (strcmp(namespace, "all") != 0 && strcmp(namespace, ndns_name) != 0) continue; switch (action) { case ACTION_DISABLE: rc = ndctl_namespace_disable_safe(ndns); if (rc == 0) (*processed)++; break; case ACTION_ENABLE: rc = ndctl_namespace_enable(ndns); if (rc >= 0) { (*processed)++; rc = 0; } break; case ACTION_DESTROY: rc = namespace_destroy(region, ndns); if (rc == 0) (*processed)++; /* return success if skipped */ if (rc > 0) rc = 0; break; case ACTION_CHECK: rc = namespace_check(ndns, verbose, force, repair, logfix); if (rc == 0) (*processed)++; break; case ACTION_CLEAR: rc = namespace_clear_bb(ndns, do_scrub); /* one scrub per bus is sufficient */ do_scrub = false; if (rc == 0) (*processed)++; break; case ACTION_CREATE: rc = namespace_reconfig(region, ndns); if (rc == 0) *processed = 1; return rc; default: rc = -EINVAL; break; } } } } if (action == ACTION_CREATE && rc == -EAGAIN) { /* * Namespace creation searched through all candidate * regions and all of them said "nope, I don't have * enough capacity", so report -ENOSPC. Except during * greedy namespace creation using --continue as we * may have created some namespaces already, and the * last one in the region search may preexist. */ if (param.greedy && (*processed) > 0) rc = 0; else rc = -ENOSPC; } if (saved_rc) rc = saved_rc; return rc; } int cmd_disable_namespace(int argc, const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl disable-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_DISABLE, base_options, xable_usage); int disabled, rc; rc = do_xaction_namespace(namespace, ACTION_DISABLE, ctx, &disabled); if (rc < 0) fprintf(stderr, "error disabling namespaces: %s\n", strerror(-rc)); fprintf(stderr, "disabled %d namespace%s\n", disabled, disabled == 1 ? "" : "s"); return rc; } int cmd_enable_namespace(int argc, const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl enable-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_ENABLE, base_options, xable_usage); int enabled, rc; rc = do_xaction_namespace(namespace, ACTION_ENABLE, ctx, &enabled); if (rc < 0) fprintf(stderr, "error enabling namespaces: %s\n", strerror(-rc)); fprintf(stderr, "enabled %d namespace%s\n", enabled, enabled == 1 ? "" : "s"); return rc; } int cmd_create_namespace(int argc, const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl create-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_CREATE, create_options, xable_usage); int created, rc; rc = do_xaction_namespace(namespace, ACTION_CREATE, ctx, &created); if (rc < 0 && created < 1 && param.do_scan) { /* * In the default scan case we try pmem first and then * fallback to blk before giving up. */ memset(¶m, 0, sizeof(param)); param.type = "blk"; set_defaults(ACTION_CREATE); rc = do_xaction_namespace(NULL, ACTION_CREATE, ctx, &created); } if (param.greedy) fprintf(stderr, "created %d namespace%s\n", created, created == 1 ? "" : "s"); if (rc < 0 || (!namespace && created < 1)) { fprintf(stderr, "failed to %s namespace: %s\n", namespace ? "reconfigure" : "create", strerror(-rc)); if (!namespace) rc = -ENODEV; } return rc; } int cmd_destroy_namespace(int argc , const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl destroy-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_DESTROY, destroy_options, xable_usage); int destroyed, rc; rc = do_xaction_namespace(namespace, ACTION_DESTROY, ctx, &destroyed); if (rc < 0) fprintf(stderr, "error destroying namespaces: %s\n", strerror(-rc)); fprintf(stderr, "destroyed %d namespace%s\n", destroyed, destroyed == 1 ? "" : "s"); return rc; } int cmd_check_namespace(int argc , const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl check-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_CHECK, check_options, xable_usage); int checked, rc; rc = do_xaction_namespace(namespace, ACTION_CHECK, ctx, &checked); if (rc < 0) fprintf(stderr, "error checking namespaces: %s\n", strerror(-rc)); fprintf(stderr, "checked %d namespace%s\n", checked, checked == 1 ? "" : "s"); return rc; } int cmd_clear_errors(int argc , const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl clear_errors []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_CLEAR, clear_options, xable_usage); int cleared, rc; rc = do_xaction_namespace(namespace, ACTION_CLEAR, ctx, &cleared); if (rc < 0) fprintf(stderr, "error clearing namespaces: %s\n", strerror(-rc)); fprintf(stderr, "cleared %d namespace%s\n", cleared, cleared == 1 ? "" : "s"); return rc; } ndctl-67/ndctl/namespace.h000066400000000000000000000124511355562357700156520ustar00rootroot00000000000000/* * Copyright (c) 2014-2017, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __NDCTL_NAMESPACE_H__ #define __NDCTL_NAMESPACE_H__ #include #include #include #include enum { NSINDEX_SIG_LEN = 16, NSINDEX_ALIGN = 256, NSINDEX_SEQ_MASK = 0x3, NSLABEL_UUID_LEN = 16, NSLABEL_NAMESPACE_MIN_SIZE = SZ_16M, NSLABEL_NAME_LEN = 64, }; /** * struct namespace_index - label set superblock * @sig: NAMESPACE_INDEX\0 * @flags: placeholder * @seq: sequence number for this index * @myoff: offset of this index in label area * @mysize: size of this index struct * @otheroff: offset of other index * @labeloff: offset of first label slot * @nslot: total number of label slots * @major: label area major version * @minor: label area minor version * @checksum: fletcher64 of all fields * @free[0]: bitmap, nlabel bits * * The size of free[] is rounded up so the total struct size is a * multiple of NSINDEX_ALIGN bytes. Any bits this allocates beyond * nlabel bits must be zero. */ struct namespace_index { char sig[NSINDEX_SIG_LEN]; u8 flags[3]; u8 labelsize; le32 seq; le64 myoff; le64 mysize; le64 otheroff; le64 labeloff; le32 nslot; le16 major; le16 minor; le64 checksum; char free[0]; }; /** * struct namespace_label - namespace superblock * @uuid: UUID per RFC 4122 * @name: optional name (NULL-terminated) * @flags: see NSLABEL_FLAG_* * @nlabel: num labels to describe this ns * @position: labels position in set * @isetcookie: interleave set cookie * @lbasize: LBA size in bytes or 0 for pmem * @dpa: DPA of NVM range on this DIMM * @rawsize: size of namespace * @slot: slot of this label in label area */ struct namespace_label { char uuid[NSLABEL_UUID_LEN]; char name[NSLABEL_NAME_LEN]; le32 flags; le16 nlabel; le16 position; le64 isetcookie; le64 lbasize; le64 dpa; le64 rawsize; le32 slot; /* * Accessing fields past this point should be gated by a * namespace_label_has() check. */ u8 align; u8 reserved[3]; char type_guid[NSLABEL_UUID_LEN]; char abstraction_guid[NSLABEL_UUID_LEN]; u8 reserved2[88]; le64 checksum; }; #define BTT_SIG_LEN 16 #define BTT_SIG "BTT_ARENA_INFO\0" #define MAP_TRIM_SHIFT 31 #define MAP_ERR_SHIFT 30 #define MAP_LBA_MASK (~((1 << MAP_TRIM_SHIFT) | (1 << MAP_ERR_SHIFT))) #define MAP_ENT_NORMAL 0xC0000000 #define ARENA_MIN_SIZE (1UL << 24) /* 16 MB */ #define ARENA_MAX_SIZE (1ULL << 39) /* 512 GB */ #define BTT_INFO_SIZE 4096 #define IB_FLAG_ERROR_MASK 0x00000001 #define LOG_GRP_SIZE sizeof(struct log_group) #define LOG_ENT_SIZE sizeof(struct log_entry) #define BTT_NUM_OFFSETS 2 #define BTT1_START_OFFSET 4096 #define BTT2_START_OFFSET 0 struct log_entry { le32 lba; le32 old_map; le32 new_map; le32 seq; }; /* * A log group represents one log 'lane', and consists of four log entries. * Two of the four entries are valid entries, and the remaining two are * padding. Due to an old bug in the padding location, we need to perform a * test to determine the padding scheme being used, and use that scheme * thereafter. * * In kernels prior to 4.15, 'log group' would have actual log entries at * indices (0, 2) and padding at indices (1, 3), where as the correct/updated * format has log entries at indices (0, 1) and padding at indices (2, 3). * * Old (pre 4.15) format: * +-----------------+-----------------+ * | ent[0] | ent[1] | * | 16B | 16B | * | lba/old/new/seq | pad | * +-----------------------------------+ * | ent[2] | ent[3] | * | 16B | 16B | * | lba/old/new/seq | pad | * +-----------------+-----------------+ * * New format: * +-----------------+-----------------+ * | ent[0] | ent[1] | * | 16B | 16B | * | lba/old/new/seq | lba/old/new/seq | * +-----------------------------------+ * | ent[2] | ent[3] | * | 16B | 16B | * | pad | pad | * +-----------------+-----------------+ * * We detect during start-up which format is in use, and set * arena->log_index[(0, 1)] with the detected format. */ struct log_group { struct log_entry ent[4]; }; struct btt_sb { u8 signature[BTT_SIG_LEN]; u8 uuid[16]; u8 parent_uuid[16]; le32 flags; le16 version_major; le16 version_minor; le32 external_lbasize; le32 external_nlba; le32 internal_lbasize; le32 internal_nlba; le32 nfree; le32 infosize; le64 nextoff; le64 dataoff; le64 mapoff; le64 logoff; le64 info2off; u8 padding[3968]; le64 checksum; }; struct free_entry { u32 block; u8 sub; u8 seq; }; struct arena_map { struct btt_sb *info; size_t info_len; void *data; size_t data_len; u32 *map; size_t map_len; struct log_group *log; size_t log_len; struct btt_sb *info2; size_t info2_len; }; #endif /* __NDCTL_NAMESPACE_H__ */ ndctl-67/ndctl/ndctl-monitor.service000066400000000000000000000002061355562357700177130ustar00rootroot00000000000000[Unit] Description=Ndctl Monitor Daemon [Service] Type=simple ExecStart=/usr/bin/ndctl monitor [Install] WantedBy=multi-user.target ndctl-67/ndctl/ndctl.c000066400000000000000000000103041355562357700150100ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * Copyright(c) 2005 Andreas Ericsson. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char ndctl_usage_string[] = "ndctl [--version] [--help] COMMAND [ARGS]"; static const char ndctl_more_info_string[] = "See 'ndctl help COMMAND' for more information on a specific command.\n" " ndctl --list-cmds to see all available commands"; static int cmd_version(int argc, const char **argv, struct ndctl_ctx *ctx) { printf("%s\n", VERSION); return 0; } static int cmd_help(int argc, const char **argv, struct ndctl_ctx *ctx) { const char * const builtin_help_subcommands[] = { "enable-region", "disable-region", "zero-labels", "enable-namespace", "disable-namespace", NULL }; struct option builtin_help_options[] = { OPT_END(), }; const char *builtin_help_usage[] = { "ndctl help [command]", NULL }; argc = parse_options_subcommand(argc, argv, builtin_help_options, builtin_help_subcommands, builtin_help_usage, 0); if (!argv[0]) { printf("\n usage: %s\n\n", ndctl_usage_string); printf("\n %s\n\n", ndctl_more_info_string); return 0; } return help_show_man_page(argv[0], "ndctl", "NDCTL_MAN_VIEWER"); } static struct cmd_struct commands[] = { { "version", { cmd_version } }, { "create-nfit", { cmd_create_nfit } }, { "enable-namespace", { cmd_enable_namespace } }, { "disable-namespace", { cmd_disable_namespace } }, { "create-namespace", { cmd_create_namespace } }, { "destroy-namespace", { cmd_destroy_namespace } }, { "check-namespace", { cmd_check_namespace } }, { "clear-errors", { cmd_clear_errors } }, { "enable-region", { cmd_enable_region } }, { "disable-region", { cmd_disable_region } }, { "enable-dimm", { cmd_enable_dimm } }, { "disable-dimm", { cmd_disable_dimm } }, { "zero-labels", { cmd_zero_labels } }, { "read-labels", { cmd_read_labels } }, { "write-labels", { cmd_write_labels } }, { "init-labels", { cmd_init_labels } }, { "check-labels", { cmd_check_labels } }, { "inject-error", { cmd_inject_error } }, { "update-firmware", { cmd_update_firmware } }, { "inject-smart", { cmd_inject_smart } }, { "wait-scrub", { cmd_wait_scrub } }, { "start-scrub", { cmd_start_scrub } }, { "setup-passphrase", { cmd_setup_passphrase } }, { "update-passphrase", { cmd_update_passphrase } }, { "remove-passphrase", { cmd_remove_passphrase } }, { "freeze-security", { cmd_freeze_security } }, { "sanitize-dimm", { cmd_sanitize_dimm } }, #ifdef ENABLE_KEYUTILS { "load-keys", { cmd_load_keys } }, #endif { "wait-overwrite", { cmd_wait_overwrite } }, { "list", { cmd_list } }, { "monitor", { cmd_monitor } }, { "help", { cmd_help } }, #ifdef ENABLE_TEST { "test", { cmd_test } }, #endif #ifdef ENABLE_DESTRUCTIVE { "bat", { cmd_bat } }, #endif }; int main(int argc, const char **argv) { struct ndctl_ctx *ctx; int rc; /* Look for flags.. */ argv++; argc--; main_handle_options(&argv, &argc, ndctl_usage_string, commands, ARRAY_SIZE(commands)); if (argc > 0) { if (!prefixcmp(argv[0], "--")) argv[0] += 2; } else { /* The user didn't specify a command; give them help */ printf("\n usage: %s\n\n", ndctl_usage_string); printf("\n %s\n\n", ndctl_more_info_string); goto out; } rc = ndctl_new(&ctx); if (rc) goto out; main_handle_internal_command(argc, argv, ctx, commands, ARRAY_SIZE(commands), PROG_NDCTL); ndctl_unref(ctx); fprintf(stderr, "Unknown command: '%s'\n", argv[0]); out: return 1; } ndctl-67/ndctl/ndctl.h000066400000000000000000000156001355562357700150210ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __NDCTL_H__ #define __NDCTL_H__ #ifndef ARRAY_SIZE #include #endif #include #include #include struct nd_cmd_dimm_flags { __u32 status; __u32 flags; } __attribute__((packed)); struct nd_cmd_get_config_size { __u32 status; __u32 config_size; __u32 max_xfer; } __attribute__((packed)); struct nd_cmd_get_config_data_hdr { __u32 in_offset; __u32 in_length; __u32 status; __u8 out_buf[0]; } __attribute__((packed)); struct nd_cmd_set_config_hdr { __u32 in_offset; __u32 in_length; __u8 in_buf[0]; } __attribute__((packed)); struct nd_cmd_vendor_hdr { __u32 opcode; __u32 in_length; __u8 in_buf[0]; } __attribute__((packed)); struct nd_cmd_vendor_tail { __u32 status; __u32 out_length; __u8 out_buf[0]; } __attribute__((packed)); struct nd_cmd_ars_cap { __u64 address; __u64 length; __u32 status; __u32 max_ars_out; __u32 clear_err_unit; __u16 flags; __u16 reserved; } __attribute__((packed)); struct nd_cmd_ars_start { __u64 address; __u64 length; __u16 type; __u8 flags; __u8 reserved[5]; __u32 status; __u32 scrub_time; } __attribute__((packed)); struct nd_cmd_ars_status { __u32 status; __u32 out_length; __u64 address; __u64 length; __u64 restart_address; __u64 restart_length; __u16 type; __u16 flags; __u32 num_records; struct nd_ars_record { __u32 handle; __u32 reserved; __u64 err_address; __u64 length; } __attribute__((packed)) records[0]; } __attribute__((packed)); struct nd_cmd_clear_error { __u64 address; __u64 length; __u32 status; __u8 reserved[4]; __u64 cleared; } __attribute__((packed)); enum { ND_CMD_IMPLEMENTED = 0, /* bus commands */ ND_CMD_ARS_CAP = 1, ND_CMD_ARS_START = 2, ND_CMD_ARS_STATUS = 3, ND_CMD_CLEAR_ERROR = 4, /* per-dimm commands */ ND_CMD_SMART = 1, ND_CMD_SMART_THRESHOLD = 2, ND_CMD_DIMM_FLAGS = 3, ND_CMD_GET_CONFIG_SIZE = 4, ND_CMD_GET_CONFIG_DATA = 5, ND_CMD_SET_CONFIG_DATA = 6, ND_CMD_VENDOR_EFFECT_LOG_SIZE = 7, ND_CMD_VENDOR_EFFECT_LOG = 8, ND_CMD_VENDOR = 9, ND_CMD_CALL = 10, }; enum { ND_ARS_VOLATILE = 1, ND_ARS_PERSISTENT = 2, ND_ARS_RETURN_PREV_DATA = 1 << 1, ND_CONFIG_LOCKED = 1, }; static __inline__ const char *nvdimm_bus_cmd_name(unsigned cmd) { static const char * const names[] = { [ND_CMD_ARS_CAP] = "ars_cap", [ND_CMD_ARS_START] = "ars_start", [ND_CMD_ARS_STATUS] = "ars_status", [ND_CMD_CLEAR_ERROR] = "clear_error", [ND_CMD_CALL] = "cmd_call", }; if (cmd < ARRAY_SIZE(names) && names[cmd]) return names[cmd]; return "unknown"; } static __inline__ const char *nvdimm_cmd_name(unsigned cmd) { static const char * const names[] = { [ND_CMD_SMART] = "smart", [ND_CMD_SMART_THRESHOLD] = "smart_thresh", [ND_CMD_DIMM_FLAGS] = "flags", [ND_CMD_GET_CONFIG_SIZE] = "get_size", [ND_CMD_GET_CONFIG_DATA] = "get_data", [ND_CMD_SET_CONFIG_DATA] = "set_data", [ND_CMD_VENDOR_EFFECT_LOG_SIZE] = "effect_size", [ND_CMD_VENDOR_EFFECT_LOG] = "effect_log", [ND_CMD_VENDOR] = "vendor", [ND_CMD_CALL] = "cmd_call", }; if (cmd < ARRAY_SIZE(names) && names[cmd]) return names[cmd]; return "unknown"; } #define ND_IOCTL 'N' #define ND_IOCTL_DIMM_FLAGS _IOWR(ND_IOCTL, ND_CMD_DIMM_FLAGS,\ struct nd_cmd_dimm_flags) #define ND_IOCTL_GET_CONFIG_SIZE _IOWR(ND_IOCTL, ND_CMD_GET_CONFIG_SIZE,\ struct nd_cmd_get_config_size) #define ND_IOCTL_GET_CONFIG_DATA _IOWR(ND_IOCTL, ND_CMD_GET_CONFIG_DATA,\ struct nd_cmd_get_config_data_hdr) #define ND_IOCTL_SET_CONFIG_DATA _IOWR(ND_IOCTL, ND_CMD_SET_CONFIG_DATA,\ struct nd_cmd_set_config_hdr) #define ND_IOCTL_VENDOR _IOWR(ND_IOCTL, ND_CMD_VENDOR,\ struct nd_cmd_vendor_hdr) #define ND_IOCTL_ARS_CAP _IOWR(ND_IOCTL, ND_CMD_ARS_CAP,\ struct nd_cmd_ars_cap) #define ND_IOCTL_ARS_START _IOWR(ND_IOCTL, ND_CMD_ARS_START,\ struct nd_cmd_ars_start) #define ND_IOCTL_ARS_STATUS _IOWR(ND_IOCTL, ND_CMD_ARS_STATUS,\ struct nd_cmd_ars_status) #define ND_IOCTL_CLEAR_ERROR _IOWR(ND_IOCTL, ND_CMD_CLEAR_ERROR,\ struct nd_cmd_clear_error) #define ND_DEVICE_DIMM 1 /* nd_dimm: container for "config data" */ #define ND_DEVICE_REGION_PMEM 2 /* nd_region: (parent of PMEM namespaces) */ #define ND_DEVICE_REGION_BLK 3 /* nd_region: (parent of BLK namespaces) */ #define ND_DEVICE_NAMESPACE_IO 4 /* legacy persistent memory */ #define ND_DEVICE_NAMESPACE_PMEM 5 /* PMEM namespace (may alias with BLK) */ #define ND_DEVICE_NAMESPACE_BLK 6 /* BLK namespace (may alias with PMEM) */ #define ND_DEVICE_DAX_PMEM 7 /* Device DAX interface to pmem */ enum nd_driver_flags { ND_DRIVER_DIMM = 1 << ND_DEVICE_DIMM, ND_DRIVER_REGION_PMEM = 1 << ND_DEVICE_REGION_PMEM, ND_DRIVER_REGION_BLK = 1 << ND_DEVICE_REGION_BLK, ND_DRIVER_NAMESPACE_IO = 1 << ND_DEVICE_NAMESPACE_IO, ND_DRIVER_NAMESPACE_PMEM = 1 << ND_DEVICE_NAMESPACE_PMEM, ND_DRIVER_NAMESPACE_BLK = 1 << ND_DEVICE_NAMESPACE_BLK, ND_DRIVER_DAX_PMEM = 1 << ND_DEVICE_DAX_PMEM, }; #ifdef PAGE_SIZE enum { ND_MIN_NAMESPACE_SIZE = PAGE_SIZE, }; #else #define ND_MIN_NAMESPACE_SIZE ((unsigned) sysconf(_SC_PAGESIZE)) #endif enum ars_masks { ARS_STATUS_MASK = 0x0000FFFF, ARS_EXT_STATUS_SHIFT = 16, }; /* * struct nd_cmd_pkg * * is a wrapper to a quasi pass thru interface for invoking firmware * associated with nvdimms. * * INPUT PARAMETERS * * nd_family corresponds to the firmware (e.g. DSM) interface. * * nd_command are the function index advertised by the firmware. * * nd_size_in is the size of the input parameters being passed to firmware * * OUTPUT PARAMETERS * * nd_fw_size is the size of the data firmware wants to return for * the call. If nd_fw_size is greater than size of nd_size_out, only * the first nd_size_out bytes are returned. */ struct nd_cmd_pkg { __u64 nd_family; /* family of commands */ __u64 nd_command; __u32 nd_size_in; /* INPUT: size of input args */ __u32 nd_size_out; /* INPUT: size of payload */ __u32 nd_reserved2[9]; /* reserved must be zero */ __u32 nd_fw_size; /* OUTPUT: size fw wants to return */ unsigned char nd_payload[]; /* Contents of call */ }; /* These NVDIMM families represent pre-standardization command sets */ #define NVDIMM_FAMILY_INTEL 0 #define NVDIMM_FAMILY_HPE1 1 #define NVDIMM_FAMILY_HPE2 2 #define NVDIMM_FAMILY_MSFT 3 #define NVDIMM_FAMILY_HYPERV 4 #define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\ struct nd_cmd_pkg) #endif /* __NDCTL_H__ */ ndctl-67/ndctl/region.c000066400000000000000000000100371355562357700151720ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include "action.h" #include #include #include static struct { const char *bus; const char *type; } param; static const struct option region_options[] = { OPT_STRING('b', "bus", ¶m.bus, "bus-id", " must be on a bus with an id/provider of "), OPT_STRING('t', "type", ¶m.type, "region-type", " must be of the specified type"), OPT_END(), }; static const char *parse_region_options(int argc, const char **argv, char *xable_usage) { const char * const u[] = { xable_usage, NULL }; int i; argc = parse_options(argc, argv, region_options, u, 0); if (argc == 0) error("specify a specific region id to act on, or \"all\"\n"); for (i = 1; i < argc; i++) error("unknown extra parameter \"%s\"\n", argv[i]); if (argc == 0 || argc > 1) { usage_with_options(u, region_options); return NULL; /* we won't return from usage_with_options() */ } if (param.type) { if (strcmp(param.type, "pmem") == 0) /* pass */; else if (strcmp(param.type, "blk") == 0) /* pass */; else { error("unknown region type '%s', should be 'pmem' or 'blk'\n", param.type); usage_with_options(u, region_options); return NULL; } } return argv[0]; } static int region_action(struct ndctl_region *region, enum device_action mode) { struct ndctl_namespace *ndns; int rc = 0; switch (mode) { case ACTION_ENABLE: rc = ndctl_region_enable(region); break; case ACTION_DISABLE: ndctl_namespace_foreach(region, ndns) { rc = ndctl_namespace_disable_safe(ndns); if (rc) return rc; } rc = ndctl_region_disable_invalidate(region); break; default: break; } return rc; } static int do_xable_region(const char *region_arg, enum device_action mode, struct ndctl_ctx *ctx) { int rc = -ENXIO, success = 0; struct ndctl_region *region; struct ndctl_bus *bus; if (!region_arg) goto out; ndctl_bus_foreach(ctx, bus) { if (!util_bus_filter(bus, param.bus)) continue; ndctl_region_foreach(bus, region) { const char *type = ndctl_region_get_type_name(region); if (param.type && strcmp(param.type, type) != 0) continue; if (!util_region_filter(region, region_arg)) continue; if (region_action(region, mode) == 0) success++; } } rc = success; out: param.bus = NULL; return rc; } int cmd_disable_region(int argc, const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl disable-region []"; const char *region = parse_region_options(argc, argv, xable_usage); int disabled = do_xable_region(region, ACTION_DISABLE, ctx); if (disabled < 0) { fprintf(stderr, "error disabling regions: %s\n", strerror(-disabled)); return disabled; } else if (disabled == 0) { fprintf(stderr, "disabled 0 regions\n"); return 0; } else { fprintf(stderr, "disabled %d region%s\n", disabled, disabled > 1 ? "s" : ""); return 0; } } int cmd_enable_region(int argc, const char **argv, struct ndctl_ctx *ctx) { char *xable_usage = "ndctl enable-region []"; const char *region = parse_region_options(argc, argv, xable_usage); int enabled = do_xable_region(region, ACTION_ENABLE, ctx); if (enabled < 0) { fprintf(stderr, "error enabling regions: %s\n", strerror(-enabled)); return enabled; } else if (enabled == 0) { fprintf(stderr, "enabled 0 regions\n"); return 0; } else { fprintf(stderr, "enabled %d region%s\n", enabled, enabled > 1 ? "s" : ""); return 0; } } ndctl-67/ndctl/test.c000066400000000000000000000041061355562357700146660ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include static char *result(int rc) { if (rc == 77) return "SKIP"; else if (rc) return "FAIL"; else return "PASS"; } int cmd_test(int argc, const char **argv, struct ndctl_ctx *ctx) { struct ndctl_test *test; int loglevel = LOG_DEBUG, i, rc; const char * const u[] = { "ndctl test []", NULL }; bool force = false; const struct option options[] = { OPT_INTEGER('l', "loglevel", &loglevel, "set the log level (default LOG_DEBUG)"), OPT_BOOLEAN('f', "force", &force, "force run all tests regardless of required kernel"), OPT_END(), }; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); if (force) test = ndctl_test_new(UINT_MAX); else test = ndctl_test_new(0); if (!test) return EXIT_FAILURE; rc = test_libndctl(loglevel, test, ctx); fprintf(stderr, "test-libndctl: %s\n", result(rc)); if (rc && rc != 77) return rc; rc = test_dsm_fail(loglevel, test, ctx); fprintf(stderr, "test-dsm-fail: %s\n", result(rc)); if (rc && rc != 77) return rc; rc = test_dpa_alloc(loglevel, test, ctx); fprintf(stderr, "test-dpa-alloc: %s\n", result(rc)); if (rc && rc != 77) return rc; rc = test_parent_uuid(loglevel, test, ctx); fprintf(stderr, "test-parent-uuid: %s\n", result(rc)); rc = test_multi_pmem(loglevel, test, ctx); fprintf(stderr, "test-multi-pmem: %s\n", result(rc)); return ndctl_test_result(test, rc); } ndctl-67/ndctl/util/000077500000000000000000000000001355562357700145175ustar00rootroot00000000000000ndctl-67/ndctl/util/json-firmware.c000066400000000000000000000034371355562357700174550ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, unsigned long flags) { struct json_object *jfirmware = json_object_new_object(); struct json_object *jobj; struct ndctl_cmd *cmd; int rc; uint64_t run, next; if (!jfirmware) return NULL; cmd = ndctl_dimm_cmd_new_fw_get_info(dimm); if (!cmd) goto err; rc = ndctl_cmd_submit(cmd); if ((rc < 0) || ndctl_cmd_fw_xlat_firmware_status(cmd) != FW_SUCCESS) { jobj = util_json_object_hex(-1, flags); if (jobj) json_object_object_add(jfirmware, "current_version", jobj); goto out; } run = ndctl_cmd_fw_info_get_run_version(cmd); if (run == ULLONG_MAX) { jobj = util_json_object_hex(-1, flags); if (jobj) json_object_object_add(jfirmware, "current_version", jobj); goto out; } jobj = util_json_object_hex(run, flags); if (jobj) json_object_object_add(jfirmware, "current_version", jobj); next = ndctl_cmd_fw_info_get_updated_version(cmd); if (next == ULLONG_MAX) { jobj = util_json_object_hex(-1, flags); if (jobj) json_object_object_add(jfirmware, "next_version", jobj); goto out; } if (next != 0) { jobj = util_json_object_hex(next, flags); if (jobj) json_object_object_add(jfirmware, "next_version", jobj); jobj = json_object_new_boolean(true); if (jobj) json_object_object_add(jfirmware, "need_powercycle", jobj); } ndctl_cmd_unref(cmd); return jfirmware; err: json_object_put(jfirmware); jfirmware = NULL; out: if (cmd) ndctl_cmd_unref(cmd); return jfirmware; } ndctl-67/ndctl/util/json-smart.c000066400000000000000000000137371355562357700167730ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include static void smart_threshold_to_json(struct ndctl_dimm *dimm, struct json_object *jhealth) { unsigned int alarm_control; struct json_object *jobj; struct ndctl_cmd *cmd; int rc; cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); if (!cmd) return; rc = ndctl_cmd_submit_xlat(cmd); if (rc < 0) goto out; alarm_control = ndctl_cmd_smart_threshold_get_alarm_control(cmd); if (alarm_control & ND_SMART_TEMP_TRIP) { unsigned int temp; double t; jobj = json_object_new_boolean(true); if (jobj) json_object_object_add(jhealth, "alarm_enabled_media_temperature", jobj); temp = ndctl_cmd_smart_threshold_get_temperature(cmd); t = ndctl_decode_smart_temperature(temp); jobj = json_object_new_double(t); if (jobj) json_object_object_add(jhealth, "temperature_threshold", jobj); } else { jobj = json_object_new_boolean(false); if (jobj) json_object_object_add(jhealth, "alarm_enabled_media_temperature", jobj); } if (alarm_control & ND_SMART_CTEMP_TRIP) { unsigned int temp; double t; jobj = json_object_new_boolean(true); if (jobj) json_object_object_add(jhealth, "alarm_enabled_ctrl_temperature", jobj); temp = ndctl_cmd_smart_threshold_get_ctrl_temperature(cmd); t = ndctl_decode_smart_temperature(temp); jobj = json_object_new_double(t); if (jobj) json_object_object_add(jhealth, "controller_temperature_threshold", jobj); } else { jobj = json_object_new_boolean(false); if (jobj) json_object_object_add(jhealth, "alarm_enabled_ctrl_temperature", jobj); } if (alarm_control & ND_SMART_SPARE_TRIP) { unsigned int spares; jobj = json_object_new_boolean(true); if (jobj) json_object_object_add(jhealth, "alarm_enabled_spares", jobj); spares = ndctl_cmd_smart_threshold_get_spares(cmd); jobj = json_object_new_int(spares); if (jobj) json_object_object_add(jhealth, "spares_threshold", jobj); } else { jobj = json_object_new_boolean(false); if (jobj) json_object_object_add(jhealth, "alarm_enabled_spares", jobj); } out: ndctl_cmd_unref(cmd); } struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm) { struct json_object *jhealth = json_object_new_object(); struct json_object *jobj; struct ndctl_cmd *cmd; unsigned int flags; int rc; if (!jhealth) return NULL; cmd = ndctl_dimm_cmd_new_smart(dimm); if (!cmd) goto err; rc = ndctl_cmd_submit_xlat(cmd); if (rc < 0) { jobj = json_object_new_string("unknown"); if (jobj) json_object_object_add(jhealth, "health_state", jobj); goto out; } flags = ndctl_cmd_smart_get_flags(cmd); if (flags & ND_SMART_HEALTH_VALID) { unsigned int health = ndctl_cmd_smart_get_health(cmd); if (health & ND_SMART_FATAL_HEALTH) jobj = json_object_new_string("fatal"); else if (health & ND_SMART_CRITICAL_HEALTH) jobj = json_object_new_string("critical"); else if (health & ND_SMART_NON_CRITICAL_HEALTH) jobj = json_object_new_string("non-critical"); else jobj = json_object_new_string("ok"); if (jobj) json_object_object_add(jhealth, "health_state", jobj); } if (flags & ND_SMART_TEMP_VALID) { unsigned int temp = ndctl_cmd_smart_get_temperature(cmd); double t = ndctl_decode_smart_temperature(temp); jobj = json_object_new_double(t); if (jobj) json_object_object_add(jhealth, "temperature_celsius", jobj); } if (flags & ND_SMART_CTEMP_VALID) { unsigned int temp = ndctl_cmd_smart_get_ctrl_temperature(cmd); double t = ndctl_decode_smart_temperature(temp); jobj = json_object_new_double(t); if (jobj) json_object_object_add(jhealth, "controller_temperature_celsius", jobj); } if (flags & ND_SMART_SPARES_VALID) { unsigned int spares = ndctl_cmd_smart_get_spares(cmd); jobj = json_object_new_int(spares); if (jobj) json_object_object_add(jhealth, "spares_percentage", jobj); } if (flags & ND_SMART_ALARM_VALID) { unsigned int alarm_flags = ndctl_cmd_smart_get_alarm_flags(cmd); bool temp_flag = !!(alarm_flags & ND_SMART_TEMP_TRIP); bool ctrl_temp_flag = !!(alarm_flags & ND_SMART_CTEMP_TRIP); bool spares_flag = !!(alarm_flags & ND_SMART_SPARE_TRIP); jobj = json_object_new_boolean(temp_flag); if (jobj) json_object_object_add(jhealth, "alarm_temperature", jobj); jobj = json_object_new_boolean(ctrl_temp_flag); if (jobj) json_object_object_add(jhealth, "alarm_controller_temperature", jobj); jobj = json_object_new_boolean(spares_flag); if (jobj) json_object_object_add(jhealth, "alarm_spares", jobj); } smart_threshold_to_json(dimm, jhealth); if (flags & ND_SMART_USED_VALID) { unsigned int life_used = ndctl_cmd_smart_get_life_used(cmd); jobj = json_object_new_int(life_used); if (jobj) json_object_object_add(jhealth, "life_used_percentage", jobj); } if (flags & ND_SMART_SHUTDOWN_VALID) { unsigned int shutdown = ndctl_cmd_smart_get_shutdown_state(cmd); jobj = json_object_new_string(shutdown ? "dirty" : "clean"); if (jobj) json_object_object_add(jhealth, "shutdown_state", jobj); } if (flags & ND_SMART_SHUTDOWN_COUNT_VALID) { unsigned int shutdown = ndctl_cmd_smart_get_shutdown_count(cmd); jobj = json_object_new_int(shutdown); if (jobj) json_object_object_add(jhealth, "shutdown_count", jobj); } ndctl_cmd_unref(cmd); return jhealth; err: json_object_put(jhealth); jhealth = NULL; out: if (cmd) ndctl_cmd_unref(cmd); return jhealth; } ndctl-67/ndctl/util/keys.c000066400000000000000000000324011355562357700156360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int get_key_path(struct ndctl_dimm *dimm, char *path, enum ndctl_key_type key_type) { char hostname[HOST_NAME_MAX]; int rc; rc = gethostname(hostname, HOST_NAME_MAX); if (rc < 0) { fprintf(stderr, "gethostname: %s\n", strerror(errno)); return -errno; } switch (key_type) { case ND_USER_OLD_KEY: rc = sprintf(path, "%s/nvdimm-old_%s_%s.blob", NDCTL_KEYS_DIR, ndctl_dimm_get_unique_id(dimm), hostname); break; case ND_USER_KEY: rc = sprintf(path, "%s/nvdimm_%s_%s.blob", NDCTL_KEYS_DIR, ndctl_dimm_get_unique_id(dimm), hostname); break; case ND_MASTER_OLD_KEY: rc = sprintf(path, "%s/nvdimm-master-old_%s_%s.blob", NDCTL_KEYS_DIR, ndctl_dimm_get_unique_id(dimm), hostname); break; case ND_MASTER_KEY: rc = sprintf(path, "%s/nvdimm-master_%s_%s.blob", NDCTL_KEYS_DIR, ndctl_dimm_get_unique_id(dimm), hostname); break; default: return -EINVAL; } if (rc < 0) { fprintf(stderr, "error setting path: %s\n", strerror(errno)); return -errno; } return 0; } static int get_key_desc(struct ndctl_dimm *dimm, char *desc, enum ndctl_key_type key_type) { int rc; switch (key_type) { case ND_USER_OLD_KEY: rc = sprintf(desc, "nvdimm-old:%s", ndctl_dimm_get_unique_id(dimm)); break; case ND_USER_KEY: rc = sprintf(desc, "nvdimm:%s", ndctl_dimm_get_unique_id(dimm)); break; case ND_MASTER_OLD_KEY: rc = sprintf(desc, "nvdimm-master-old:%s", ndctl_dimm_get_unique_id(dimm)); break; case ND_MASTER_KEY: rc = sprintf(desc, "nvdimm-master:%s", ndctl_dimm_get_unique_id(dimm)); break; default: return -EINVAL; } if (rc < 0) { fprintf(stderr, "error setting key description: %s\n", strerror(errno)); return -errno; } return 0; } char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, int dirfd, enum key_type key_type) { struct stat st; ssize_t read_bytes = 0; int rc, fd; char *blob, *pl, *rdptr; char prefix[] = "load "; bool need_prefix = false; if (key_type == KEY_ENCRYPTED || key_type == KEY_TRUSTED) need_prefix = true; fd = openat(dirfd, path, O_RDONLY); if (fd < 0) { fprintf(stderr, "failed to open file %s: %s\n", path, strerror(errno)); return NULL; } rc = fstat(fd, &st); if (rc < 0) { fprintf(stderr, "stat: %s\n", strerror(errno)); return NULL; } if ((st.st_mode & S_IFMT) != S_IFREG) { fprintf(stderr, "%s not a regular file\n", path); return NULL; } if (st.st_size == 0 || st.st_size > 4096) { fprintf(stderr, "Invalid blob file size\n"); return NULL; } *size = st.st_size; if (need_prefix) *size += strlen(prefix); /* * We need to increment postfix and space. * "keyhandle=" is 10 bytes, plus null termination. */ if (postfix) *size += strlen(postfix) + 10 + 1; blob = malloc(*size); if (!blob) { fprintf(stderr, "Unable to allocate memory for blob\n"); return NULL; } if (need_prefix) { memcpy(blob, prefix, strlen(prefix)); pl = blob + strlen(prefix); } else pl = blob; rdptr = pl; do { rc = read(fd, rdptr, st.st_size - read_bytes); if (rc < 0) { fprintf(stderr, "Failed to read from blob file: %s\n", strerror(errno)); free(blob); close(fd); return NULL; } read_bytes += rc; rdptr += rc; } while (read_bytes != st.st_size); close(fd); if (postfix) { pl += read_bytes; *pl = ' '; pl++; rc = sprintf(pl, "keyhandle=%s", postfix); } return blob; } static key_serial_t dimm_check_key(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { char desc[ND_KEY_DESC_SIZE]; int rc; rc = get_key_desc(dimm, desc, key_type); if (rc < 0) return rc; return keyctl_search(KEY_SPEC_USER_KEYRING, "encrypted", desc, 0); } static key_serial_t dimm_create_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type) { char desc[ND_KEY_DESC_SIZE]; char path[PATH_MAX]; char cmd[ND_KEY_CMD_SIZE]; key_serial_t key; void *buffer; int rc; ssize_t size; FILE *fp; ssize_t wrote; struct stat st; if (ndctl_dimm_is_active(dimm)) { fprintf(stderr, "regions active on %s, op failed\n", ndctl_dimm_get_devname(dimm)); return -EBUSY; } rc = get_key_desc(dimm, desc, key_type); if (rc < 0) return rc; /* make sure it's not already in the key ring */ key = keyctl_search(KEY_SPEC_USER_KEYRING, "encrypted", desc, 0); if (key > 0) { fprintf(stderr, "Error: key already present in user keyring\n"); return -EEXIST; } rc = get_key_path(dimm, path, key_type); if (rc < 0) return rc; rc = stat(path, &st); if (rc == 0) { fprintf(stderr, "%s already exists!\n", path); return -EEXIST; } rc = sprintf(cmd, "new enc32 %s 32", kek); if (rc < 0) { fprintf(stderr, "sprintf: %s\n", strerror(errno)); return -errno; } key = add_key("encrypted", desc, cmd, strlen(cmd), KEY_SPEC_USER_KEYRING); if (key < 0) { fprintf(stderr, "add_key failed: %s\n", strerror(errno)); return -errno; } size = keyctl_read_alloc(key, &buffer); if (size < 0) { fprintf(stderr, "keyctl_read_alloc failed: %s\n", strerror(errno)); keyctl_unlink(key, KEY_SPEC_USER_KEYRING); return rc; } fp = fopen(path, "w"); if (!fp) { rc = -errno; fprintf(stderr, "Unable to open file %s: %s\n", path, strerror(errno)); free(buffer); return rc; } wrote = fwrite(buffer, 1, size, fp); if (wrote != size) { if (wrote == -1) rc = -errno; else rc = -EIO; fprintf(stderr, "Failed to write to %s: %s\n", path, strerror(-rc)); fclose(fp); free(buffer); return rc; } fclose(fp); free(buffer); return key; } static key_serial_t dimm_load_key(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { key_serial_t key; char desc[ND_KEY_DESC_SIZE]; char path[PATH_MAX]; int rc; char *blob; int size; if (ndctl_dimm_is_active(dimm)) { fprintf(stderr, "regions active on %s, op failed\n", ndctl_dimm_get_devname(dimm)); return -EBUSY; } rc = get_key_desc(dimm, desc, key_type); if (rc < 0) return rc; rc = get_key_path(dimm, path, key_type); if (rc < 0) return rc; blob = ndctl_load_key_blob(path, &size, NULL, -1, KEY_ENCRYPTED); if (!blob) return -ENOMEM; key = add_key("encrypted", desc, blob, size, KEY_SPEC_USER_KEYRING); free(blob); if (key < 0) { fprintf(stderr, "add_key failed: %s\n", strerror(errno)); return -errno; } return key; } /* * The function will check to see if the existing key is there and remove * from user key ring if it is. Rename the existing key blob to old key * blob, and then attempt to inject the key as old key into the user key * ring. */ static key_serial_t move_key_to_old(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { int rc; key_serial_t key; char old_path[PATH_MAX]; char new_path[PATH_MAX]; enum ndctl_key_type okey_type; if (ndctl_dimm_is_active(dimm)) { fprintf(stderr, "regions active on %s, op failed\n", ndctl_dimm_get_devname(dimm)); return -EBUSY; } key = dimm_check_key(dimm, key_type); if (key > 0) keyctl_unlink(key, KEY_SPEC_USER_KEYRING); if (key_type == ND_USER_KEY) okey_type = ND_USER_OLD_KEY; else if (key_type == ND_MASTER_KEY) okey_type = ND_MASTER_OLD_KEY; else return -EINVAL; rc = get_key_path(dimm, old_path, key_type); if (rc < 0) return rc; rc = get_key_path(dimm, new_path, okey_type); if (rc < 0) return rc; rc = rename(old_path, new_path); if (rc < 0) { fprintf(stderr, "rename failed from %s to %s: %s\n", old_path, new_path, strerror(errno)); return -errno; } return dimm_load_key(dimm, okey_type); } static int dimm_remove_key(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { key_serial_t key; char path[PATH_MAX]; int rc; key = dimm_check_key(dimm, key_type); if (key > 0) keyctl_unlink(key, KEY_SPEC_USER_KEYRING); rc = get_key_path(dimm, path, key_type); if (rc < 0) return rc; rc = unlink(path); if (rc < 0) { fprintf(stderr, "delete file %s failed: %s\n", path, strerror(errno)); return -errno; } return 0; } static int verify_kek(struct ndctl_dimm *dimm, const char *kek) { char *type, *desc, *key_handle; key_serial_t key; int rc = 0; key_handle = strdup(kek); if (!key_handle) return -ENOMEM; type = strtok(key_handle, ":"); if (!type) { fprintf(stderr, "No key type found for kek handle\n"); rc = -EINVAL; goto out; } if (strcmp(type, "trusted") != 0 && strcmp(type, "user") != 0) { fprintf(stderr, "No such key type: %s", type); rc = -EINVAL; goto out; } desc = strtok(NULL, ":"); if (!desc) { fprintf(stderr, "No description found for kek handle\n"); rc = -EINVAL; goto out; } key = keyctl_search(KEY_SPEC_USER_KEYRING, type, desc, 0); if (key < 0) { fprintf(stderr, "No key encryption key found\n"); rc = key; goto out; } out: free(key_handle); return rc; } int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type) { key_serial_t key; int rc; rc = verify_kek(dimm, kek); if (rc < 0) return rc; key = dimm_create_key(dimm, kek, key_type); if (key < 0) return key; if (key_type == ND_MASTER_KEY) rc = ndctl_dimm_update_master_passphrase(dimm, 0, key); else rc = ndctl_dimm_update_passphrase(dimm, 0, key); if (rc < 0) { dimm_remove_key(dimm, key_type); return rc; } return 0; } static char *get_current_kek(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { key_serial_t key; char *key_buf; long rc; char *type, *desc; key = dimm_check_key(dimm, key_type); if (key < 0) return NULL; rc = keyctl_read_alloc(key, (void **)&key_buf); if (rc < 0) return NULL; rc = sscanf(key_buf, "%ms %ms", &type, &desc); if (rc < 0) return NULL; free(key_buf); free(type); return desc; } int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type) { int rc; key_serial_t old_key, new_key; char *current_kek = NULL; enum ndctl_key_type okey_type; if (kek) { rc = verify_kek(dimm, kek); if (rc < 0) return rc; } else { /* find current kek */ current_kek = get_current_kek(dimm, key_type); if (!current_kek) return -ENOKEY; } if (key_type == ND_USER_KEY) okey_type = ND_USER_OLD_KEY; else if (key_type == ND_MASTER_KEY) okey_type = ND_MASTER_OLD_KEY; else return -EINVAL; /* * 1. check if current key is loaded and remove * 2. move current key blob to old key blob * 3. load old key blob * 4. trigger change key with old and new key * 5. remove old key * 6. remove old key blob */ old_key = move_key_to_old(dimm, key_type); if (old_key < 0) return old_key; new_key = dimm_create_key(dimm, current_kek ? current_kek : kek, key_type); free(current_kek); /* need to create new key here */ if (new_key < 0) { new_key = dimm_load_key(dimm, key_type); if (new_key < 0) return new_key; } if (key_type == ND_MASTER_KEY) rc = ndctl_dimm_update_master_passphrase(dimm, old_key, new_key); else rc = ndctl_dimm_update_passphrase(dimm, old_key, new_key); if (rc < 0) return rc; rc = dimm_remove_key(dimm, okey_type); if (rc < 0) return rc; return 0; } static key_serial_t check_dimm_key(struct ndctl_dimm *dimm, bool need_key, enum ndctl_key_type key_type) { key_serial_t key; key = dimm_check_key(dimm, key_type); if (key < 0) { key = dimm_load_key(dimm, key_type); if (key < 0 && need_key) { fprintf(stderr, "Unable to load key\n"); return -ENOKEY; } else key = 0; } return key; } static int run_key_op(struct ndctl_dimm *dimm, key_serial_t key, int (*run_op)(struct ndctl_dimm *, long), const char *name) { int rc; rc = run_op(dimm, key); if (rc < 0) { fprintf(stderr, "Failed %s for %s\n", name, ndctl_dimm_get_devname(dimm)); return rc; } return 0; } static int discard_key(struct ndctl_dimm *dimm) { int rc; rc = dimm_remove_key(dimm, ND_USER_KEY); if (rc < 0) { fprintf(stderr, "Unable to cleanup key.\n"); return rc; } return 0; } int ndctl_dimm_remove_key(struct ndctl_dimm *dimm) { key_serial_t key; int rc; key = check_dimm_key(dimm, true, ND_USER_KEY); if (key < 0) return key; rc = run_key_op(dimm, key, ndctl_dimm_disable_passphrase, "remove passphrase"); if (rc < 0) return rc; return discard_key(dimm); } int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { key_serial_t key = 0; int rc; if (key_type != ND_ZERO_KEY) { key = check_dimm_key(dimm, true, key_type); if (key < 0) return key; } if (key_type == ND_MASTER_KEY) rc = run_key_op(dimm, key, ndctl_dimm_master_secure_erase, "master crypto erase"); else if (key_type == ND_USER_KEY || key_type == ND_ZERO_KEY) rc = run_key_op(dimm, key, ndctl_dimm_secure_erase, "crypto erase"); else rc = -EINVAL; if (rc < 0) return rc; if (key_type == ND_USER_KEY) return discard_key(dimm); return 0; } int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm) { key_serial_t key; int rc; key = check_dimm_key(dimm, false, ND_USER_KEY); if (key < 0) return key; rc = run_key_op(dimm, key, ndctl_dimm_overwrite, "overwrite"); if (rc < 0) return rc; return 0; } ndctl-67/ndctl/util/keys.h000066400000000000000000000031441355562357700156450ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2019 Intel Corporation. All rights reserved. */ #ifndef _NDCTL_UTIL_KEYS_H_ #define _NDCTL_UTIL_KEYS_H_ enum ndctl_key_type { ND_USER_KEY, ND_USER_OLD_KEY, ND_MASTER_KEY, ND_MASTER_OLD_KEY, ND_ZERO_KEY, }; enum key_type { KEY_USER = 0, KEY_TRUSTED, KEY_ENCRYPTED, }; #ifdef ENABLE_KEYUTILS char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, int dirfd, enum key_type key_type); int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type); int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type); int ndctl_dimm_remove_key(struct ndctl_dimm *dimm); int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, enum ndctl_key_type key_type); int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm); #else char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, int dirfd, enum key_type key_type) { return NULL; } static inline int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type) { return -EOPNOTSUPP; } static inline int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, enum ndctl_key_type key_type) { return -EOPNOTSUPP; } static inline int ndctl_dimm_remove_key(struct ndctl_dimm *dimm) { return -EOPNOTSUPP; } static inline int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, enum ndctl_key_type key_type) { return -EOPNOTSUPP; } static inline int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm) { return -EOPNOTSUPP; } #endif /* ENABLE_KEYUTILS */ #endif ndctl-67/nfit.h000066400000000000000000000031231355562357700135460ustar00rootroot00000000000000/* * NVDIMM Firmware Interface Table - NFIT * * Copyright(c) 2013-2016 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License 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 __NFIT_H__ #define __NFIT_H__ #include #include static inline void nfit_spa_uuid_pm(void *uuid) { uuid_le uuid_pm = UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, 0x18, 0xb7, 0x8c, 0xdb); memcpy(uuid, &uuid_pm, 16); } enum { NFIT_TABLE_SPA = 0, }; /** * struct nfit - Nvdimm Firmware Interface Table * @signature: "NFIT" * @length: sum of size of this table plus all appended subtables */ struct nfit { uint8_t signature[4]; uint32_t length; uint8_t revision; uint8_t checksum; uint8_t oemid[6]; uint64_t oem_tbl_id; uint32_t oem_revision; uint32_t creator_id; uint32_t creator_revision; uint32_t reserved; } __attribute__((packed)); /** * struct nfit_spa - System Physical Address Range Descriptor Table */ struct nfit_spa { uint16_t type; uint16_t length; uint16_t range_index; uint16_t flags; uint32_t reserved; uint32_t proximity_domain; uint8_t type_uuid[16]; uint64_t spa_base; uint64_t spa_length; uint64_t mem_attr; } __attribute__((packed)); #endif /* __NFIT_H__ */ ndctl-67/rpmbuild.sh000077500000000000000000000003041355562357700146100ustar00rootroot00000000000000#!/bin/bash pushd $(dirname $0) >/dev/null [ ! -d ~/rpmbuild/SOURCES ] && echo "rpmdev tree not found" && exit 1 ./make-git-snapshot.sh popd > /dev/null rpmbuild -ba $(dirname $0)/rhel/ndctl.spec ndctl-67/sles/000077500000000000000000000000001355562357700134045ustar00rootroot00000000000000ndctl-67/sles/header000066400000000000000000000013651355562357700145640ustar00rootroot00000000000000# # spec file for package ndctl # # Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2015 Intel Corporation # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ # ndctl-67/test.h000066400000000000000000000046461355562357700136000ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 __TEST_H__ #define __TEST_H__ #include struct ndctl_test; struct ndctl_ctx; struct ndctl_test *ndctl_test_new(unsigned int kver); int ndctl_test_result(struct ndctl_test *test, int rc); int ndctl_test_get_skipped(struct ndctl_test *test); int ndctl_test_get_attempted(struct ndctl_test *test); int __ndctl_test_attempt(struct ndctl_test *test, unsigned int kver, const char *caller, int line); #define ndctl_test_attempt(t, v) __ndctl_test_attempt(t, v, __func__, __LINE__) void __ndctl_test_skip(struct ndctl_test *test, const char *caller, int line); #define ndctl_test_skip(t) __ndctl_test_skip(t, __func__, __LINE__) struct ndctl_namespace *ndctl_get_test_dev(struct ndctl_ctx *ctx); void builtin_xaction_namespace_reset(void); struct kmod_ctx; struct kmod_module; int nfit_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, struct ndctl_ctx *nd_ctx, int log_level, struct ndctl_test *test); struct ndctl_ctx; int test_parent_uuid(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); int test_multi_pmem(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); int test_dax_directio(int dax_fd, unsigned long align, void *dax_addr, off_t offset); #ifdef ENABLE_POISON int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, void *dax_addr, off_t offset, bool fsdax); #else static inline int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, void *dax_addr, off_t offset, bool fsdax) { return 0; } #endif int test_dpa_alloc(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); int test_dsm_fail(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); int test_libndctl(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); int test_blk_namespaces(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); int test_pmem_namespaces(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); #endif /* __TEST_H__ */ ndctl-67/test/000077500000000000000000000000001355562357700134155ustar00rootroot00000000000000ndctl-67/test/Makefile.am000066400000000000000000000062361355562357700154600ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in TESTS =\ libndctl \ dsm-fail \ dpa-alloc \ parent-uuid \ multi-pmem \ create.sh \ clear.sh \ pmem-errors.sh \ daxdev-errors.sh \ multi-dax.sh \ btt-check.sh \ label-compat.sh \ blk-exhaust.sh \ sector-mode.sh \ inject-error.sh \ btt-errors.sh \ hugetlb \ btt-pad-compat.sh \ firmware-update.sh \ ack-shutdown-count-set \ rescan-partitions.sh \ inject-smart.sh \ monitor.sh \ max_available_extent_ns.sh \ pfn-meta-errors.sh check_PROGRAMS =\ libndctl \ dsm-fail \ dpa-alloc \ parent-uuid \ multi-pmem \ dax-errors \ smart-notify \ smart-listen \ hugetlb \ daxdev-errors \ ack-shutdown-count-set \ list-smart-dimm if ENABLE_DESTRUCTIVE TESTS +=\ blk-ns \ pmem-ns \ dax-dev \ dax-ext4.sh \ dax-xfs.sh \ device-dax \ device-dax-fio.sh \ mmap.sh \ daxctl-devices.sh if ENABLE_KEYUTILS TESTS += security.sh endif check_PROGRAMS +=\ blk-ns \ pmem-ns \ dax-dev \ dax-pmd \ device-dax \ mmap endif LIBNDCTL_LIB =\ ../ndctl/lib/libndctl.la \ ../daxctl/lib/libdaxctl.la testcore =\ core.c \ ../util/log.c \ ../util/sysfs.c libndctl_SOURCES = libndctl.c $(testcore) libndctl_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) dsm_fail_SOURCES =\ dsm-fail.c \ $(testcore) \ ../ndctl/namespace.c \ ../ndctl/check.c \ ../util/json.c dsm_fail_LDADD = $(LIBNDCTL_LIB) \ $(KMOD_LIBS) \ $(JSON_LIBS) \ $(UUID_LIBS) \ ../libutil.a ack_shutdown_count_set_SOURCES =\ ack-shutdown-count-set.c \ $(testcore) ack_shutdown_count_set_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) blk_ns_SOURCES = blk_namespaces.c $(testcore) blk_ns_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) pmem_ns_SOURCES = pmem_namespaces.c $(testcore) pmem_ns_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) dpa_alloc_SOURCES = dpa-alloc.c $(testcore) dpa_alloc_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) parent_uuid_SOURCES = parent-uuid.c $(testcore) parent_uuid_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) dax_dev_SOURCES = dax-dev.c $(testcore) dax_dev_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) dax_pmd_SOURCES = dax-pmd.c \ $(testcore) hugetlb_SOURCES = hugetlb.c \ dax-pmd.c mmap_SOURCES = mmap.c dax_errors_SOURCES = dax-errors.c daxdev_errors_SOURCES = daxdev-errors.c \ ../util/log.c \ ../util/sysfs.c daxdev_errors_LDADD = $(LIBNDCTL_LIB) device_dax_SOURCES = \ device-dax.c \ dax-dev.c \ dax-pmd.c \ $(testcore) \ ../ndctl/namespace.c \ ../ndctl/check.c \ ../util/json.c if ENABLE_POISON dax_pmd_SOURCES += dax-poison.c hugetlb_SOURCES += dax-poison.c device_dax_SOURCES += dax-poison.c endif device_dax_LDADD = \ $(LIBNDCTL_LIB) \ $(KMOD_LIBS) \ $(JSON_LIBS) \ ../libutil.a smart_notify_SOURCES = smart-notify.c smart_notify_LDADD = $(LIBNDCTL_LIB) smart_listen_SOURCES = smart-listen.c smart_listen_LDADD = $(LIBNDCTL_LIB) multi_pmem_SOURCES = \ multi-pmem.c \ $(testcore) \ ../ndctl/namespace.c \ ../ndctl/check.c \ ../util/json.c multi_pmem_LDADD = \ $(LIBNDCTL_LIB) \ $(JSON_LIBS) \ $(UUID_LIBS) \ $(KMOD_LIBS) \ ../libutil.a list_smart_dimm_SOURCES = \ list-smart-dimm.c \ ../util/json.c list_smart_dimm_LDADD = \ $(LIBNDCTL_LIB) \ $(JSON_LIBS) \ $(UUID_LIBS) \ ../libutil.a ndctl-67/test/ack-shutdown-count-set.c000066400000000000000000000055651355562357700201220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int test_dimm(struct ndctl_dimm *dimm) { struct ndctl_cmd *cmd; int rc = 0; cmd = ndctl_dimm_cmd_new_ack_shutdown_count(dimm); if (!cmd) return -ENOMEM; rc = ndctl_cmd_submit_xlat(cmd); if (rc < 0) { fprintf(stderr, "dimm %s LSS enable set failed\n", ndctl_dimm_get_devname(dimm)); goto out; } printf("DIMM %s LSS enable set\n", ndctl_dimm_get_devname(dimm)); out: ndctl_cmd_unref(cmd); return rc; } static void reset_bus(struct ndctl_bus *bus) { struct ndctl_region *region; struct ndctl_dimm *dimm; /* disable all regions so that set_config_data commands are permitted */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) ndctl_dimm_zero_labels(dimm); } static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) { struct ndctl_bus *bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); struct ndctl_dimm *dimm; struct ndctl_region *region; struct log_ctx log_ctx; int rc = 0; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 15, 0))) return 77; if (!bus) return -ENXIO; log_init(&log_ctx, "test/ack-shutdown-count-set", "NDCTL_TEST"); ndctl_bus_wait_probe(bus); ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) { fprintf(stderr, "Testing dimm: %s\n", ndctl_dimm_get_devname(dimm)); rc = test_dimm(dimm); if (rc < 0) { fprintf(stderr, "dimm %s failed\n", ndctl_dimm_get_devname(dimm)); goto out; } } out: reset_bus(bus); return rc; } static int test_ack_shutdown_count_set(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { struct kmod_module *mod; struct kmod_ctx *kmod_ctx; int result = EXIT_FAILURE, err; ndctl_set_log_priority(ctx, loglevel); err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); if (err < 0) { result = 77; ndctl_test_skip(test); fprintf(stderr, "%s unavailable skipping tests\n", "nfit_test"); return result; } result = do_test(ctx, test); kmod_module_remove_module(mod, 0); kmod_unref(kmod_ctx); return result; } int main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_ack_shutdown_count_set(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/blk-exhaust.sh000077500000000000000000000022451355562357700162060ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. set -e rc=77 . ./common check_min_kver "4.11" || do_skip "may lack blk-exhaustion fix" trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all # if the kernel accounting is correct we should be able to create two # pmem and two blk namespaces on nfit_test.0 rc=1 $NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem $NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem $NDCTL create-namespace -b $NFIT_TEST_BUS0 -t blk -m raw $NDCTL create-namespace -b $NFIT_TEST_BUS0 -t blk -m raw # clearnup and exit _cleanup exit 0 ndctl-67/test/blk_namespaces.c000066400000000000000000000216141355562357700165340ustar00rootroot00000000000000/* * blk_namespaces: tests functionality of multiple block namespaces * * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* The purpose of this test is to verify that we can successfully do I/O to * multiple nd_blk namespaces that have discontiguous segments. It first * sets up two namespaces, each 1/2 the total size of the NVDIMM and each with * two discontiguous segments, arranged like this: * * +-------+-------+-------+-------+ * | nd0 | nd1 | nd0 | nd1 | * +-------+-------+-------+-------+ * * It then runs some I/O to the beginning, middle and end of each of these * namespaces, checking data integrity. The I/O to the middle of the * namespace will hit two pages, one on either side of the segment boundary. */ #define err(msg)\ fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno)) static struct ndctl_namespace *create_blk_namespace(int region_fraction, struct ndctl_region *region) { struct ndctl_namespace *ndns, *seed_ns = NULL; unsigned long long size; uuid_t uuid; ndctl_namespace_foreach(region, ndns) if (ndctl_namespace_get_size(ndns) == 0) { seed_ns = ndns; break; } if (!seed_ns) return NULL; uuid_generate(uuid); size = ndctl_region_get_size(region)/region_fraction; if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0) return NULL; if (ndctl_namespace_set_size(seed_ns, size) < 0) return NULL; if (ndctl_namespace_set_sector_size(seed_ns, 512) < 0) return NULL; if (ndctl_namespace_enable(seed_ns) < 0) return NULL; return seed_ns; } static int disable_blk_namespace(struct ndctl_namespace *ndns) { if (ndctl_namespace_disable_invalidate(ndns) < 0) return -ENODEV; if (ndctl_namespace_delete(ndns) < 0) return -ENODEV; return 0; } static int ns_do_io(const char *bdev) { int fd, i; int rc = 0; const int page_size = 4096; const int num_pages = 4; unsigned long num_dev_pages, num_blocks; off_t addr; void *random_page[num_pages]; void *blk_page[num_pages]; rc = posix_memalign(random_page, page_size, page_size * num_pages); if (rc) { fprintf(stderr, "posix_memalign failure\n"); return rc; } rc = posix_memalign(blk_page, page_size, page_size * num_pages); if (rc) { fprintf(stderr, "posix_memalign failure\n"); goto err_free_blk; } for (i = 1; i < num_pages; i++) { random_page[i] = (char*)random_page[0] + page_size * i; blk_page[i] = (char*)blk_page[0] + page_size * i; } /* read random data into random_page */ if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { err("open"); rc = -ENODEV; goto err_free_all; } rc = read(fd, random_page[0], page_size * num_pages); if (rc < 0) { err("read"); close(fd); goto err_free_all; } close(fd); if ((fd = open(bdev, O_RDWR|O_DIRECT)) < 0) { err("open"); rc = -ENODEV; goto err_free_all; } ioctl(fd, BLKGETSIZE, &num_blocks); num_dev_pages = num_blocks / 8; /* write the random data out to each of the segments */ rc = pwrite(fd, random_page[0], page_size, 0); if (rc < 0) { err("write"); goto err_close; } /* two pages that span the region discontinuity */ addr = page_size * (num_dev_pages/2 - 1); rc = pwrite(fd, random_page[1], page_size*2, addr); if (rc < 0) { err("write"); goto err_close; } addr = page_size * (num_dev_pages - 1); rc = pwrite(fd, random_page[3], page_size, addr); if (rc < 0) { err("write"); goto err_close; } /* read back the random data into blk_page */ rc = pread(fd, blk_page[0], page_size, 0); if (rc < 0) { err("read"); goto err_close; } /* two pages that span the region discontinuity */ addr = page_size * (num_dev_pages/2 - 1); rc = pread(fd, blk_page[1], page_size*2, addr); if (rc < 0) { err("read"); goto err_close; } addr = page_size * (num_dev_pages - 1); rc = pread(fd, blk_page[3], page_size, addr); if (rc < 0) { err("read"); goto err_close; } /* verify the data */ if (memcmp(random_page[0], blk_page[0], page_size * num_pages)) { fprintf(stderr, "Block data miscompare\n"); rc = -EIO; goto err_close; } rc = 0; err_close: close(fd); err_free_all: free(random_page[0]); err_free_blk: free(blk_page[0]); return rc; } static const char *comm = "test-blk-namespaces"; int test_blk_namespaces(int log_level, struct ndctl_test *test, struct ndctl_ctx *ctx) { char bdev[50]; int rc = -ENXIO; struct ndctl_bus *bus; struct ndctl_dimm *dimm; struct kmod_module *mod = NULL; struct kmod_ctx *kmod_ctx = NULL; struct ndctl_namespace *ndns[2]; struct ndctl_region *region, *blk_region = NULL; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) return 77; ndctl_set_log_priority(ctx, log_level); bus = ndctl_bus_get_by_provider(ctx, "ACPI.NFIT"); if (bus) { /* skip this bus if no BLK regions */ ndctl_region_foreach(bus, region) if (ndctl_region_get_nstype(region) == ND_DEVICE_NAMESPACE_BLK) break; if (!region) bus = NULL; } if (!bus) { fprintf(stderr, "ACPI.NFIT unavailable falling back to nfit_test\n"); rc = nfit_test_init(&kmod_ctx, &mod, NULL, log_level, test); ndctl_invalidate(ctx); bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); if (rc < 0 || !bus) { ndctl_test_skip(test); fprintf(stderr, "nfit_test unavailable skipping tests\n"); return 77; } } fprintf(stderr, "%s: found provider: %s\n", comm, ndctl_bus_get_provider(bus)); /* get the system to a clean state */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) { rc = ndctl_dimm_zero_labels(dimm); if (rc < 0) { fprintf(stderr, "failed to zero %s\n", ndctl_dimm_get_devname(dimm)); goto err_module; } } /* create our config */ ndctl_region_foreach(bus, region) if (strcmp(ndctl_region_get_type_name(region), "blk") == 0) { blk_region = region; break; } if (!blk_region || ndctl_region_enable(blk_region) < 0) { fprintf(stderr, "%s: failed to find block region\n", comm); rc = -ENODEV; goto err_cleanup; } rc = -ENODEV; ndns[0] = create_blk_namespace(4, blk_region); if (!ndns[0]) { fprintf(stderr, "%s: failed to create block namespace\n", comm); goto err_cleanup; } ndns[1] = create_blk_namespace(4, blk_region); if (!ndns[1]) { fprintf(stderr, "%s: failed to create block namespace\n", comm); goto err_cleanup; } rc = disable_blk_namespace(ndns[0]); if (rc < 0) { fprintf(stderr, "%s: failed to disable block namespace\n", comm); goto err_cleanup; } ndns[0] = create_blk_namespace(2, blk_region); if (!ndns[0]) { fprintf(stderr, "%s: failed to create block namespace\n", comm); rc = -ENODEV; goto err_cleanup; } rc = disable_blk_namespace(ndns[1]); if (rc < 0) { fprintf(stderr, "%s: failed to disable block namespace\n", comm); goto err_cleanup; } rc = -ENODEV; ndns[1] = create_blk_namespace(2, blk_region); if (!ndns[1]) { fprintf(stderr, "%s: failed to create block namespace\n", comm); goto err_cleanup; } /* okay, all set up, do some I/O */ rc = -EIO; sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns[0])); if (ns_do_io(bdev)) goto err_cleanup; sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns[1])); if (ns_do_io(bdev)) goto err_cleanup; rc = 0; err_cleanup: /* unload nfit_test */ bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); if (bus) ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); bus = ndctl_bus_get_by_provider(ctx, "nfit_test.1"); if (bus) ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); if (mod) kmod_module_remove_module(mod, 0); err_module: if (kmod_ctx) kmod_unref(kmod_ctx); return rc; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; comm = argv[0]; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_blk_namespaces(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/btt-check.sh000077500000000000000000000101451355562357700156210ustar00rootroot00000000000000#!/bin/bash -E # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. dev="" mode="" size="" sector_size="" blockdev="" bs=4096 rc=77 . ./common trap 'err $LINENO' ERR # sample json: # { # "dev":"namespace5.0", # "mode":"sector", # "size":32440320, # "uuid":"51805176-e124-4635-ae17-0e6a4a16671a", # "sector_size":4096, # "blockdev":"pmem5s" # } check_min_kver "4.14" || do_skip "may not support badblocks clearing on pmem via btt" create() { json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m sector) rc=2 eval "$(echo "$json" | json2var)" [ -n "$dev" ] || err "$LINENO" [ "$mode" = "sector" ] || err "$LINENO" [ -n "$size" ] || err "$LINENO" [ -n "$sector_size" ] || err "$LINENO" [ -n "$blockdev" ] || err "$LINENO" [ $size -gt 0 ] || err "$LINENO" } reset() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all } # re-enable the BTT namespace, and do IO to it in an attempt to # verify it still comes up ok, and functions as expected post_repair_test() { echo "${FUNCNAME[0]}: I/O to BTT namespace" test -b /dev/$blockdev dd if=/dev/urandom of=test-bin bs=$sector_size count=$((size/sector_size)) > /dev/null 2>&1 dd if=test-bin of=/dev/$blockdev bs=$sector_size count=$((size/sector_size)) > /dev/null 2>&1 dd if=/dev/$blockdev of=test-bin-read bs=$sector_size count=$((size/sector_size)) > /dev/null 2>&1 diff test-bin test-bin-read rm -f test-bin* echo "done" } test_normal() { echo "=== ${FUNCNAME[0]} ===" # disable the namespace $NDCTL disable-namespace $dev $NDCTL check-namespace $dev $NDCTL enable-namespace $dev post_repair_test } test_force() { echo "=== ${FUNCNAME[0]} ===" $NDCTL check-namespace --force $dev post_repair_test } set_raw() { $NDCTL disable-namespace $dev echo -n "set raw_mode: " echo 1 | tee /sys/bus/nd/devices/$dev/force_raw $NDCTL enable-namespace $dev raw_bdev="${blockdev%%s}" test -b /dev/$raw_bdev raw_size="$(cat /sys/bus/nd/devices/$dev/size)" } unset_raw() { $NDCTL disable-namespace $dev echo -n "set raw_mode: " echo 0 | tee /sys/bus/nd/devices/$dev/force_raw $NDCTL enable-namespace $dev raw_bdev="" } test_bad_info2() { echo "=== ${FUNCNAME[0]} ===" set_raw seek="$((raw_size/bs - 1))" echo "wiping info2 block (offset = $seek blocks)" dd if=/dev/zero of=/dev/$raw_bdev bs=$bs count=1 seek=$seek unset_raw $NDCTL disable-namespace $dev $NDCTL check-namespace $dev 2>&1 | grep "info2 needs to be restored" $NDCTL check-namespace --repair $dev $NDCTL enable-namespace $dev post_repair_test } test_bad_info() { echo "=== ${FUNCNAME[0]} ===" set_raw echo "wiping info block" dd if=/dev/zero of=/dev/$raw_bdev bs=$bs count=2 seek=0 unset_raw $NDCTL disable-namespace $dev $NDCTL check-namespace $dev 2>&1 | grep -E "info block at offset .* needs to be restored" $NDCTL check-namespace --repair $dev $NDCTL enable-namespace $dev post_repair_test } test_bitmap() { echo "=== ${FUNCNAME[0]} ===" reset && create set_raw # scribble over the last 4K of the map rm -f /tmp/scribble for (( i=0 ; i<512 ; i++ )); do echo -n -e \\x1e\\x1e\\x00\\xc0\\x1e\\x1e\\x00\\xc0 >> /tmp/scribble done seek="$((raw_size/bs - (256*64/bs) - 2))" echo "scribbling over map entries (offset = $seek blocks)" dd if=/tmp/scribble of=/dev/$raw_bdev bs=$bs seek=$seek rm -f /tmp/scribble unset_raw $NDCTL disable-namespace $dev $NDCTL check-namespace $dev 2>&1 | grep "bitmap error" # This is not repairable reset && create } do_tests() { test_normal test_force test_bad_info2 test_bad_info test_bitmap } # setup (reset nfit_test dimms, create the BTT namespace) modprobe nfit_test rc=1 reset && create do_tests reset _cleanup exit 0 ndctl-67/test/btt-errors.sh000077500000000000000000000111441355562357700160600ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. MNT=test_btt_mnt FILE=image blockdev="" rc=77 . ./common cleanup() { rm -f $FILE rm -f $MNT/$FILE if [ -n "$blockdev" ]; then umount "/dev/$blockdev" else rc=77 fi rmdir $MNT } force_raw() { raw="$1" if grep -q "$MNT" /proc/mounts; then umount $MNT; fi $NDCTL disable-namespace "$dev" echo "$raw" > "/sys/bus/nd/devices/$dev/force_raw" $NDCTL enable-namespace "$dev" echo "Set $dev to raw mode: $raw" if [[ "$raw" == "1" ]]; then raw_bdev=${blockdev%s} test -b "/dev/$raw_bdev" else raw_bdev="" fi } check_min_kver "4.15" || do_skip "may lack BTT error handling" set -e mkdir -p $MNT trap 'err $LINENO cleanup' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 # create a btt namespace and clear errors (if any) dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m sector) eval "$(echo "$json" | json2var)" [ $dev = "x" ] && echo "fail: $LINENO" && exit 1 force_raw 1 if read -r sector len < "/sys/block/$raw_bdev/badblocks"; then dd of=/dev/$raw_bdev if=/dev/zero oflag=direct bs=512 seek="$sector" count="$len" fi force_raw 0 mkfs.ext4 "/dev/$blockdev" -b 4096 mount -o nodelalloc "/dev/$blockdev" $MNT # prepare an image file with random data dd if=/dev/urandom of=$FILE bs=4096 count=1 test -s $FILE # copy it to the file system cp $FILE $MNT/$FILE # Get the start sector for the file start_sect=$(filefrag -v -b512 $MNT/$FILE | grep -E "^[ ]+[0-9]+.*" | head -1 | awk '{ print $4 }' | cut -d. -f1) start_4k=$((start_sect/8)) test -n "$start_sect" echo "start sector of the file is: $start_sect (512B) or $start_4k (4096B)" # figure out the btt offset force_raw 1 # calculate start of the map map=$(hexdump -s 96 -n 4 "/dev/$raw_bdev" | head -1 | cut -d' ' -f2-) map=$(tr -d ' ' <<< "0x${map#* }${map%% *}") printf "btt map starts at: %x\n" "$map" # calculate map entry byte offset for the file's block map_idx=$((map + (4 * start_4k))) printf "btt map entry location for sector %x: %x\n" "$start_4k" "$map_idx" # read the map entry map_ent=$(hexdump -s $map_idx -n 4 "/dev/$raw_bdev" | head -1 | cut -d' ' -f2-) map_ent=$(tr -d ' ' <<< "0x${map_ent#* }${map_ent%% *}") map_ent=$((map_ent & 0x3fffffff)) printf "btt map entry: 0x%x\n" "$map_ent" # calculate the data offset dataoff=$(((map_ent * 4096) + 4096)) printf "dataoff: 0x%x\n" "$dataoff" bb_inj=$((dataoff/512)) # inject badblocks for one page at the start of the file $NDCTL inject-error --block="$bb_inj" --count=8 $dev $NDCTL start-scrub && $NDCTL wait-scrub force_raw 0 mount -o nodelalloc "/dev/$blockdev" $MNT # make sure reading the first block of the file fails as expected : The following 'dd' is expected to hit an I/O Error dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true # write via btt to clear the error dd if=/dev/zero of=$MNT/$FILE oflag=direct bs=4096 count=1 # read again and that should succeed dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 ## ensure we get an EIO for errors in namespace metadata # reset everything to get a clean log if grep -q "$MNT" /proc/mounts; then umount $MNT; fi $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m sector) eval "$(echo "$json" | json2var)" [ $dev = "x" ] && echo "fail: $LINENO" && exit 1 # insert error at an arbitrary offset in the map (sector 0) force_raw 1 map=$(hexdump -s 96 -n 4 "/dev/$raw_bdev" | head -1 | cut -d' ' -f2-) map=$(tr -d ' ' <<< "0x${map#* }${map%% *}") bb_inj=$((map/512)) $NDCTL inject-error --block="$bb_inj" --count=1 $dev $NDCTL start-scrub && $NDCTL wait-scrub force_raw 0 # make sure reading the first block of the namespace fails : The following 'dd' is expected to hit an I/O Error dd if=/dev/$blockdev of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true # done, exit $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all _cleanup exit 0 ndctl-67/test/btt-pad-compat.sh000077500000000000000000000112241355562357700165700ustar00rootroot00000000000000#!/bin/bash -Ex # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. dev="" size="" blockdev="" rc=77 . ./common trap 'err $LINENO' ERR # sample json: #{ # "dev":"namespace7.0", # "mode":"fsdax", # "size":"60.00 MiB (62.92 MB)", # "uuid":"f1baa71a-d165-4da4-bb6a-083a2b0e6469", # "blockdev":"pmem7", #} create() { json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m sector) rc=2 eval "$(echo "$json" | json2var)" [ -n "$dev" ] || err "$LINENO" [ -n "$size" ] || err "$LINENO" [ -n "$blockdev" ] || err "$LINENO" [ $size -gt 0 ] || err "$LINENO" bttdev=$(cat /sys/bus/nd/devices/$dev/holder) [ -n "$bttdev" ] || err "$LINENO" if [ ! -e /sys/kernel/debug/btt/$bttdev/arena0/log_index_0 ]; then do_skip "seems to be missing the BTT compatibility fixes, skipping." fi } reset() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all } verify_idx() { idx0="$1" idx1="$2" # check debugfs is mounted if ! grep -qE "debugfs" /proc/mounts; then mount -t debugfs none /sys/kernel/debug fi test $(cat /sys/kernel/debug/btt/$bttdev/arena0/log_index_0) -eq "$idx0" test $(cat /sys/kernel/debug/btt/$bttdev/arena0/log_index_1) -eq "$idx1" } do_random_io() { local bdev="$1" dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=0 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=32 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=64 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=128 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=256 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=512 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=1024 & dd if=/dev/urandom of="$bdev" bs=4096 count=32 seek=2048 & wait } cycle_ns() { local ns="$1" $NDCTL disable-namespace $ns $NDCTL enable-namespace $ns } force_raw() { raw="$1" $NDCTL disable-namespace "$dev" echo "$raw" > "/sys/bus/nd/devices/$dev/force_raw" $NDCTL enable-namespace "$dev" echo "Set $dev to raw mode: $raw" if [[ "$raw" == "1" ]]; then raw_bdev=${blockdev%s} test -b "/dev/$raw_bdev" else raw_bdev="" fi } copy_xxd_img() { local bdev="$1" local xxd_patch="btt-pad-compat.xxd" test -s "$xxd_patch" test -b "$bdev" xxd -r "$xxd_patch" "$bdev" } create_oldfmt_ns() { # create null-uuid namespace, note that this requires a kernel # that supports a raw namespace with a 4K sector size, prior to # v4.13 raw namespaces are limited to 512-byte sector size. rc=77 json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -s 64M -t pmem -m raw -l 4096 -u 00000000-0000-0000-0000-000000000000) rc=2 eval "$(echo "$json" | json2var)" [ -n "$dev" ] || err "$LINENO" [ -n "$size" ] || err "$LINENO" [ $size -gt 0 ] || err "$LINENO" # reconfig it to sector mode json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -e $dev -m sector --force) eval "$(echo "$json" | json2var)" [ -n "$dev" ] || err "$LINENO" [ -n "$size" ] || err "$LINENO" [ -n "$blockdev" ] || err "$LINENO" [ $size -gt 0 ] || err "$LINENO" bttdev=$(cat /sys/bus/nd/devices/$dev/holder) [ -n "$bttdev" ] || err "$LINENO" rc=1 # copy old-padding-format btt image, and try to re-enable the resulting btt force_raw 1 copy_xxd_img "/dev/$raw_bdev" force_raw 0 test -b "/dev/$blockdev" } ns_info_wipe() { force_raw 1 dd if=/dev/zero of=/dev/$raw_bdev bs=4096 count=2 } do_tests() { # regular btt create verify_idx 0 1 # do io, and cycle namespace, verify indices do_random_io "/dev/$blockdev" cycle_ns "$dev" verify_idx 0 1 # do the same with an old format namespace reset create_oldfmt_ns verify_idx 0 2 # do io, and cycle namespace, verify indices do_random_io "/dev/$blockdev" cycle_ns "$dev" verify_idx 0 2 # rewrite log using ndctl, verify conversion to new format $NDCTL check-namespace --rewrite-log --repair --force --verbose $dev do_random_io "/dev/$blockdev" cycle_ns "$dev" verify_idx 0 1 # check-namespace again to make sure everything is ok $NDCTL check-namespace --force --verbose $dev # the old format btt metadata was created with a null parent uuid, # making it 'stickier' than a normally created btt. Be sure to clean # it up by wiping the info block ns_info_wipe } modprobe nfit_test check_prereq xxd rc=1 reset do_tests reset _cleanup exit 0 ndctl-67/test/btt-pad-compat.xxd000066400000000000000000001074311355562357700167640ustar00rootroot000000000000000000000: 4254 545f 4152 454e 415f 494e 464f 0000 BTT_ARENA_INFO.. 0000010: f0d6 4d51 7ad4 44e2 8757 6b8e 8e4f 7500 ..MQz.D..Wk..Ou. 0000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000030: 0000 0000 0200 0000 0010 0000 e93e 0000 .............>.. 0000040: 0010 0000 e93f 0000 0001 0000 0010 0000 .....?.......... 0000050: 0000 0000 0000 0000 0010 0000 0000 0000 ................ 0000060: 00b0 fe03 0000 0000 00b0 ff03 0000 0000 ................ 0000070: 00f0 ff03 0000 0000 0000 0000 0000 0000 ................ 0000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 0000ff0: 0000 0000 0000 0000 5db7 55ca 03a9 d829 ........].U....) 0001000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ef0000: cbe4 21e6 d01b d67b c5f7 7bf5 d943 abff ..!....{..{..C.. 3ef0010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3feb000: ef3e 00c0 0000 0000 0000 0000 0000 0000 .>.............. 3feb010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb000: 0000 0000 e93e 0000 e93e 0000 0100 0000 .....>...>...... 3ffb010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb040: 0100 0000 ea3e 0000 ea3e 0000 0100 0000 .....>...>...... 3ffb050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb080: 0200 0000 eb3e 0000 eb3e 0000 0100 0000 .....>...>...... 3ffb090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb0c0: 0300 0000 ec3e 0000 ec3e 0000 0100 0000 .....>...>...... 3ffb0d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb100: 0400 0000 ed3e 0000 ed3e 0000 0100 0000 .....>...>...... 3ffb110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb140: 0500 0000 ee3e 0000 ee3e 0000 0100 0000 .....>...>...... 3ffb150: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb180: 0600 0000 ef3e 0000 ef3e 0000 0100 0000 .....>...>...... 3ffb190: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 3ffb1a0: 0000 0000 0000 0000 ef3e 0000 0200 0000 .........>...... 3ffb1b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 3ffb1c0: 0700 0000 f03e 0000 f03e 0000 0100 0000 .....>...>...... 3ffb1d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb200: 0800 0000 f13e 0000 f13e 0000 0100 0000 .....>...>...... 3ffb210: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb240: 0900 0000 f23e 0000 f23e 0000 0100 0000 .....>...>...... 3ffb250: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb280: 0a00 0000 f33e 0000 f33e 0000 0100 0000 .....>...>...... 3ffb290: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb2c0: 0b00 0000 f43e 0000 f43e 0000 0100 0000 .....>...>...... 3ffb2d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb300: 0c00 0000 f53e 0000 f53e 0000 0100 0000 .....>...>...... 3ffb310: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb340: 0d00 0000 f63e 0000 f63e 0000 0100 0000 .....>...>...... 3ffb350: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb380: 0e00 0000 f73e 0000 f73e 0000 0100 0000 .....>...>...... 3ffb390: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb3c0: 0f00 0000 f83e 0000 f83e 0000 0100 0000 .....>...>...... 3ffb3d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb400: 1000 0000 f93e 0000 f93e 0000 0100 0000 .....>...>...... 3ffb410: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb440: 1100 0000 fa3e 0000 fa3e 0000 0100 0000 .....>...>...... 3ffb450: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb480: 1200 0000 fb3e 0000 fb3e 0000 0100 0000 .....>...>...... 3ffb490: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb4c0: 1300 0000 fc3e 0000 fc3e 0000 0100 0000 .....>...>...... 3ffb4d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb500: 1400 0000 fd3e 0000 fd3e 0000 0100 0000 .....>...>...... 3ffb510: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb540: 1500 0000 fe3e 0000 fe3e 0000 0100 0000 .....>...>...... 3ffb550: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb580: 1600 0000 ff3e 0000 ff3e 0000 0100 0000 .....>...>...... 3ffb590: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb5c0: 1700 0000 003f 0000 003f 0000 0100 0000 .....?...?...... 3ffb5d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb600: 1800 0000 013f 0000 013f 0000 0100 0000 .....?...?...... 3ffb610: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb640: 1900 0000 023f 0000 023f 0000 0100 0000 .....?...?...... 3ffb650: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb680: 1a00 0000 033f 0000 033f 0000 0100 0000 .....?...?...... 3ffb690: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb6c0: 1b00 0000 043f 0000 043f 0000 0100 0000 .....?...?...... 3ffb6d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb700: 1c00 0000 053f 0000 053f 0000 0100 0000 .....?...?...... 3ffb710: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb740: 1d00 0000 063f 0000 063f 0000 0100 0000 .....?...?...... 3ffb750: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb780: 1e00 0000 073f 0000 073f 0000 0100 0000 .....?...?...... 3ffb790: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb7c0: 1f00 0000 083f 0000 083f 0000 0100 0000 .....?...?...... 3ffb7d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb800: 2000 0000 093f 0000 093f 0000 0100 0000 ....?...?...... 3ffb810: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb840: 2100 0000 0a3f 0000 0a3f 0000 0100 0000 !....?...?...... 3ffb850: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb880: 2200 0000 0b3f 0000 0b3f 0000 0100 0000 "....?...?...... 3ffb890: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb8c0: 2300 0000 0c3f 0000 0c3f 0000 0100 0000 #....?...?...... 3ffb8d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb900: 2400 0000 0d3f 0000 0d3f 0000 0100 0000 $....?...?...... 3ffb910: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb940: 2500 0000 0e3f 0000 0e3f 0000 0100 0000 %....?...?...... 3ffb950: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb980: 2600 0000 0f3f 0000 0f3f 0000 0100 0000 &....?...?...... 3ffb990: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffb9c0: 2700 0000 103f 0000 103f 0000 0100 0000 '....?...?...... 3ffb9d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffba00: 2800 0000 113f 0000 113f 0000 0100 0000 (....?...?...... 3ffba10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffba40: 2900 0000 123f 0000 123f 0000 0100 0000 )....?...?...... 3ffba50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffba80: 2a00 0000 133f 0000 133f 0000 0100 0000 *....?...?...... 3ffba90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbac0: 2b00 0000 143f 0000 143f 0000 0100 0000 +....?...?...... 3ffbad0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbb00: 2c00 0000 153f 0000 153f 0000 0100 0000 ,....?...?...... 3ffbb10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbb40: 2d00 0000 163f 0000 163f 0000 0100 0000 -....?...?...... 3ffbb50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbb80: 2e00 0000 173f 0000 173f 0000 0100 0000 .....?...?...... 3ffbb90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbbc0: 2f00 0000 183f 0000 183f 0000 0100 0000 /....?...?...... 3ffbbd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbc00: 3000 0000 193f 0000 193f 0000 0100 0000 0....?...?...... 3ffbc10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbc40: 3100 0000 1a3f 0000 1a3f 0000 0100 0000 1....?...?...... 3ffbc50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbc80: 3200 0000 1b3f 0000 1b3f 0000 0100 0000 2....?...?...... 3ffbc90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbcc0: 3300 0000 1c3f 0000 1c3f 0000 0100 0000 3....?...?...... 3ffbcd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbd00: 3400 0000 1d3f 0000 1d3f 0000 0100 0000 4....?...?...... 3ffbd10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbd40: 3500 0000 1e3f 0000 1e3f 0000 0100 0000 5....?...?...... 3ffbd50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbd80: 3600 0000 1f3f 0000 1f3f 0000 0100 0000 6....?...?...... 3ffbd90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbdc0: 3700 0000 203f 0000 203f 0000 0100 0000 7... ?.. ?...... 3ffbdd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbe00: 3800 0000 213f 0000 213f 0000 0100 0000 8...!?..!?...... 3ffbe10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbe40: 3900 0000 223f 0000 223f 0000 0100 0000 9..."?.."?...... 3ffbe50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbe80: 3a00 0000 233f 0000 233f 0000 0100 0000 :...#?..#?...... 3ffbe90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbec0: 3b00 0000 243f 0000 243f 0000 0100 0000 ;...$?..$?...... 3ffbed0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbf00: 3c00 0000 253f 0000 253f 0000 0100 0000 <...%?..%?...... 3ffbf10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbf40: 3d00 0000 263f 0000 263f 0000 0100 0000 =...&?..&?...... 3ffbf50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbf80: 3e00 0000 273f 0000 273f 0000 0100 0000 >...'?..'?...... 3ffbf90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffbfc0: 3f00 0000 283f 0000 283f 0000 0100 0000 ?...(?..(?...... 3ffbfd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc000: 4000 0000 293f 0000 293f 0000 0100 0000 @...)?..)?...... 3ffc010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc040: 4100 0000 2a3f 0000 2a3f 0000 0100 0000 A...*?..*?...... 3ffc050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc080: 4200 0000 2b3f 0000 2b3f 0000 0100 0000 B...+?..+?...... 3ffc090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc0c0: 4300 0000 2c3f 0000 2c3f 0000 0100 0000 C...,?..,?...... 3ffc0d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc100: 4400 0000 2d3f 0000 2d3f 0000 0100 0000 D...-?..-?...... 3ffc110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc140: 4500 0000 2e3f 0000 2e3f 0000 0100 0000 E....?...?...... 3ffc150: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc180: 4600 0000 2f3f 0000 2f3f 0000 0100 0000 F.../?../?...... 3ffc190: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc1c0: 4700 0000 303f 0000 303f 0000 0100 0000 G...0?..0?...... 3ffc1d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc200: 4800 0000 313f 0000 313f 0000 0100 0000 H...1?..1?...... 3ffc210: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc240: 4900 0000 323f 0000 323f 0000 0100 0000 I...2?..2?...... 3ffc250: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc280: 4a00 0000 333f 0000 333f 0000 0100 0000 J...3?..3?...... 3ffc290: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc2c0: 4b00 0000 343f 0000 343f 0000 0100 0000 K...4?..4?...... 3ffc2d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc300: 4c00 0000 353f 0000 353f 0000 0100 0000 L...5?..5?...... 3ffc310: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc340: 4d00 0000 363f 0000 363f 0000 0100 0000 M...6?..6?...... 3ffc350: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc380: 4e00 0000 373f 0000 373f 0000 0100 0000 N...7?..7?...... 3ffc390: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc3c0: 4f00 0000 383f 0000 383f 0000 0100 0000 O...8?..8?...... 3ffc3d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc400: 5000 0000 393f 0000 393f 0000 0100 0000 P...9?..9?...... 3ffc410: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc440: 5100 0000 3a3f 0000 3a3f 0000 0100 0000 Q...:?..:?...... 3ffc450: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc480: 5200 0000 3b3f 0000 3b3f 0000 0100 0000 R...;?..;?...... 3ffc490: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc4c0: 5300 0000 3c3f 0000 3c3f 0000 0100 0000 S...?..>?...... 3ffc550: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc580: 5600 0000 3f3f 0000 3f3f 0000 0100 0000 V...??..??...... 3ffc590: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc5c0: 5700 0000 403f 0000 403f 0000 0100 0000 W...@?..@?...... 3ffc5d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc600: 5800 0000 413f 0000 413f 0000 0100 0000 X...A?..A?...... 3ffc610: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc640: 5900 0000 423f 0000 423f 0000 0100 0000 Y...B?..B?...... 3ffc650: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc680: 5a00 0000 433f 0000 433f 0000 0100 0000 Z...C?..C?...... 3ffc690: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc6c0: 5b00 0000 443f 0000 443f 0000 0100 0000 [...D?..D?...... 3ffc6d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc700: 5c00 0000 453f 0000 453f 0000 0100 0000 \...E?..E?...... 3ffc710: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc740: 5d00 0000 463f 0000 463f 0000 0100 0000 ]...F?..F?...... 3ffc750: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc780: 5e00 0000 473f 0000 473f 0000 0100 0000 ^...G?..G?...... 3ffc790: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc7c0: 5f00 0000 483f 0000 483f 0000 0100 0000 _...H?..H?...... 3ffc7d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc800: 6000 0000 493f 0000 493f 0000 0100 0000 `...I?..I?...... 3ffc810: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc840: 6100 0000 4a3f 0000 4a3f 0000 0100 0000 a...J?..J?...... 3ffc850: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc880: 6200 0000 4b3f 0000 4b3f 0000 0100 0000 b...K?..K?...... 3ffc890: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc8c0: 6300 0000 4c3f 0000 4c3f 0000 0100 0000 c...L?..L?...... 3ffc8d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc900: 6400 0000 4d3f 0000 4d3f 0000 0100 0000 d...M?..M?...... 3ffc910: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc940: 6500 0000 4e3f 0000 4e3f 0000 0100 0000 e...N?..N?...... 3ffc950: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc980: 6600 0000 4f3f 0000 4f3f 0000 0100 0000 f...O?..O?...... 3ffc990: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffc9c0: 6700 0000 503f 0000 503f 0000 0100 0000 g...P?..P?...... 3ffc9d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffca00: 6800 0000 513f 0000 513f 0000 0100 0000 h...Q?..Q?...... 3ffca10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffca40: 6900 0000 523f 0000 523f 0000 0100 0000 i...R?..R?...... 3ffca50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffca80: 6a00 0000 533f 0000 533f 0000 0100 0000 j...S?..S?...... 3ffca90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcac0: 6b00 0000 543f 0000 543f 0000 0100 0000 k...T?..T?...... 3ffcad0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcb00: 6c00 0000 553f 0000 553f 0000 0100 0000 l...U?..U?...... 3ffcb10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcb40: 6d00 0000 563f 0000 563f 0000 0100 0000 m...V?..V?...... 3ffcb50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcb80: 6e00 0000 573f 0000 573f 0000 0100 0000 n...W?..W?...... 3ffcb90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcbc0: 6f00 0000 583f 0000 583f 0000 0100 0000 o...X?..X?...... 3ffcbd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcc00: 7000 0000 593f 0000 593f 0000 0100 0000 p...Y?..Y?...... 3ffcc10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcc40: 7100 0000 5a3f 0000 5a3f 0000 0100 0000 q...Z?..Z?...... 3ffcc50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcc80: 7200 0000 5b3f 0000 5b3f 0000 0100 0000 r...[?..[?...... 3ffcc90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffccc0: 7300 0000 5c3f 0000 5c3f 0000 0100 0000 s...\?..\?...... 3ffccd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcd00: 7400 0000 5d3f 0000 5d3f 0000 0100 0000 t...]?..]?...... 3ffcd10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcd40: 7500 0000 5e3f 0000 5e3f 0000 0100 0000 u...^?..^?...... 3ffcd50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcd80: 7600 0000 5f3f 0000 5f3f 0000 0100 0000 v..._?.._?...... 3ffcd90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcdc0: 7700 0000 603f 0000 603f 0000 0100 0000 w...`?..`?...... 3ffcdd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffce00: 7800 0000 613f 0000 613f 0000 0100 0000 x...a?..a?...... 3ffce10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffce40: 7900 0000 623f 0000 623f 0000 0100 0000 y...b?..b?...... 3ffce50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffce80: 7a00 0000 633f 0000 633f 0000 0100 0000 z...c?..c?...... 3ffce90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcec0: 7b00 0000 643f 0000 643f 0000 0100 0000 {...d?..d?...... 3ffced0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcf00: 7c00 0000 653f 0000 653f 0000 0100 0000 |...e?..e?...... 3ffcf10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcf40: 7d00 0000 663f 0000 663f 0000 0100 0000 }...f?..f?...... 3ffcf50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcf80: 7e00 0000 673f 0000 673f 0000 0100 0000 ~...g?..g?...... 3ffcf90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffcfc0: 7f00 0000 683f 0000 683f 0000 0100 0000 ....h?..h?...... 3ffcfd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd000: 8000 0000 693f 0000 693f 0000 0100 0000 ....i?..i?...... 3ffd010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd040: 8100 0000 6a3f 0000 6a3f 0000 0100 0000 ....j?..j?...... 3ffd050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd080: 8200 0000 6b3f 0000 6b3f 0000 0100 0000 ....k?..k?...... 3ffd090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd0c0: 8300 0000 6c3f 0000 6c3f 0000 0100 0000 ....l?..l?...... 3ffd0d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd100: 8400 0000 6d3f 0000 6d3f 0000 0100 0000 ....m?..m?...... 3ffd110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd140: 8500 0000 6e3f 0000 6e3f 0000 0100 0000 ....n?..n?...... 3ffd150: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd180: 8600 0000 6f3f 0000 6f3f 0000 0100 0000 ....o?..o?...... 3ffd190: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd1c0: 8700 0000 703f 0000 703f 0000 0100 0000 ....p?..p?...... 3ffd1d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd200: 8800 0000 713f 0000 713f 0000 0100 0000 ....q?..q?...... 3ffd210: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd240: 8900 0000 723f 0000 723f 0000 0100 0000 ....r?..r?...... 3ffd250: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd280: 8a00 0000 733f 0000 733f 0000 0100 0000 ....s?..s?...... 3ffd290: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd2c0: 8b00 0000 743f 0000 743f 0000 0100 0000 ....t?..t?...... 3ffd2d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd300: 8c00 0000 753f 0000 753f 0000 0100 0000 ....u?..u?...... 3ffd310: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd340: 8d00 0000 763f 0000 763f 0000 0100 0000 ....v?..v?...... 3ffd350: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd380: 8e00 0000 773f 0000 773f 0000 0100 0000 ....w?..w?...... 3ffd390: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd3c0: 8f00 0000 783f 0000 783f 0000 0100 0000 ....x?..x?...... 3ffd3d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd400: 9000 0000 793f 0000 793f 0000 0100 0000 ....y?..y?...... 3ffd410: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd440: 9100 0000 7a3f 0000 7a3f 0000 0100 0000 ....z?..z?...... 3ffd450: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd480: 9200 0000 7b3f 0000 7b3f 0000 0100 0000 ....{?..{?...... 3ffd490: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd4c0: 9300 0000 7c3f 0000 7c3f 0000 0100 0000 ....|?..|?...... 3ffd4d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd500: 9400 0000 7d3f 0000 7d3f 0000 0100 0000 ....}?..}?...... 3ffd510: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd540: 9500 0000 7e3f 0000 7e3f 0000 0100 0000 ....~?..~?...... 3ffd550: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd580: 9600 0000 7f3f 0000 7f3f 0000 0100 0000 .....?...?...... 3ffd590: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd5c0: 9700 0000 803f 0000 803f 0000 0100 0000 .....?...?...... 3ffd5d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd600: 9800 0000 813f 0000 813f 0000 0100 0000 .....?...?...... 3ffd610: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd640: 9900 0000 823f 0000 823f 0000 0100 0000 .....?...?...... 3ffd650: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd680: 9a00 0000 833f 0000 833f 0000 0100 0000 .....?...?...... 3ffd690: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd6c0: 9b00 0000 843f 0000 843f 0000 0100 0000 .....?...?...... 3ffd6d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd700: 9c00 0000 853f 0000 853f 0000 0100 0000 .....?...?...... 3ffd710: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd740: 9d00 0000 863f 0000 863f 0000 0100 0000 .....?...?...... 3ffd750: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd780: 9e00 0000 873f 0000 873f 0000 0100 0000 .....?...?...... 3ffd790: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd7c0: 9f00 0000 883f 0000 883f 0000 0100 0000 .....?...?...... 3ffd7d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd800: a000 0000 893f 0000 893f 0000 0100 0000 .....?...?...... 3ffd810: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd840: a100 0000 8a3f 0000 8a3f 0000 0100 0000 .....?...?...... 3ffd850: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd880: a200 0000 8b3f 0000 8b3f 0000 0100 0000 .....?...?...... 3ffd890: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd8c0: a300 0000 8c3f 0000 8c3f 0000 0100 0000 .....?...?...... 3ffd8d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd900: a400 0000 8d3f 0000 8d3f 0000 0100 0000 .....?...?...... 3ffd910: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd940: a500 0000 8e3f 0000 8e3f 0000 0100 0000 .....?...?...... 3ffd950: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd980: a600 0000 8f3f 0000 8f3f 0000 0100 0000 .....?...?...... 3ffd990: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffd9c0: a700 0000 903f 0000 903f 0000 0100 0000 .....?...?...... 3ffd9d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffda00: a800 0000 913f 0000 913f 0000 0100 0000 .....?...?...... 3ffda10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffda40: a900 0000 923f 0000 923f 0000 0100 0000 .....?...?...... 3ffda50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffda80: aa00 0000 933f 0000 933f 0000 0100 0000 .....?...?...... 3ffda90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdac0: ab00 0000 943f 0000 943f 0000 0100 0000 .....?...?...... 3ffdad0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdb00: ac00 0000 953f 0000 953f 0000 0100 0000 .....?...?...... 3ffdb10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdb40: ad00 0000 963f 0000 963f 0000 0100 0000 .....?...?...... 3ffdb50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdb80: ae00 0000 973f 0000 973f 0000 0100 0000 .....?...?...... 3ffdb90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdbc0: af00 0000 983f 0000 983f 0000 0100 0000 .....?...?...... 3ffdbd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdc00: b000 0000 993f 0000 993f 0000 0100 0000 .....?...?...... 3ffdc10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdc40: b100 0000 9a3f 0000 9a3f 0000 0100 0000 .....?...?...... 3ffdc50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdc80: b200 0000 9b3f 0000 9b3f 0000 0100 0000 .....?...?...... 3ffdc90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdcc0: b300 0000 9c3f 0000 9c3f 0000 0100 0000 .....?...?...... 3ffdcd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdd00: b400 0000 9d3f 0000 9d3f 0000 0100 0000 .....?...?...... 3ffdd10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdd40: b500 0000 9e3f 0000 9e3f 0000 0100 0000 .....?...?...... 3ffdd50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdd80: b600 0000 9f3f 0000 9f3f 0000 0100 0000 .....?...?...... 3ffdd90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffddc0: b700 0000 a03f 0000 a03f 0000 0100 0000 .....?...?...... 3ffddd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffde00: b800 0000 a13f 0000 a13f 0000 0100 0000 .....?...?...... 3ffde10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffde40: b900 0000 a23f 0000 a23f 0000 0100 0000 .....?...?...... 3ffde50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffde80: ba00 0000 a33f 0000 a33f 0000 0100 0000 .....?...?...... 3ffde90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdec0: bb00 0000 a43f 0000 a43f 0000 0100 0000 .....?...?...... 3ffded0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdf00: bc00 0000 a53f 0000 a53f 0000 0100 0000 .....?...?...... 3ffdf10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdf40: bd00 0000 a63f 0000 a63f 0000 0100 0000 .....?...?...... 3ffdf50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdf80: be00 0000 a73f 0000 a73f 0000 0100 0000 .....?...?...... 3ffdf90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffdfc0: bf00 0000 a83f 0000 a83f 0000 0100 0000 .....?...?...... 3ffdfd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe000: c000 0000 a93f 0000 a93f 0000 0100 0000 .....?...?...... 3ffe010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe040: c100 0000 aa3f 0000 aa3f 0000 0100 0000 .....?...?...... 3ffe050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe080: c200 0000 ab3f 0000 ab3f 0000 0100 0000 .....?...?...... 3ffe090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe0c0: c300 0000 ac3f 0000 ac3f 0000 0100 0000 .....?...?...... 3ffe0d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe100: c400 0000 ad3f 0000 ad3f 0000 0100 0000 .....?...?...... 3ffe110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe140: c500 0000 ae3f 0000 ae3f 0000 0100 0000 .....?...?...... 3ffe150: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe180: c600 0000 af3f 0000 af3f 0000 0100 0000 .....?...?...... 3ffe190: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe1c0: c700 0000 b03f 0000 b03f 0000 0100 0000 .....?...?...... 3ffe1d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe200: c800 0000 b13f 0000 b13f 0000 0100 0000 .....?...?...... 3ffe210: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe240: c900 0000 b23f 0000 b23f 0000 0100 0000 .....?...?...... 3ffe250: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe280: ca00 0000 b33f 0000 b33f 0000 0100 0000 .....?...?...... 3ffe290: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe2c0: cb00 0000 b43f 0000 b43f 0000 0100 0000 .....?...?...... 3ffe2d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe300: cc00 0000 b53f 0000 b53f 0000 0100 0000 .....?...?...... 3ffe310: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe340: cd00 0000 b63f 0000 b63f 0000 0100 0000 .....?...?...... 3ffe350: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe380: ce00 0000 b73f 0000 b73f 0000 0100 0000 .....?...?...... 3ffe390: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe3c0: cf00 0000 b83f 0000 b83f 0000 0100 0000 .....?...?...... 3ffe3d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe400: d000 0000 b93f 0000 b93f 0000 0100 0000 .....?...?...... 3ffe410: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe440: d100 0000 ba3f 0000 ba3f 0000 0100 0000 .....?...?...... 3ffe450: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe480: d200 0000 bb3f 0000 bb3f 0000 0100 0000 .....?...?...... 3ffe490: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe4c0: d300 0000 bc3f 0000 bc3f 0000 0100 0000 .....?...?...... 3ffe4d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe500: d400 0000 bd3f 0000 bd3f 0000 0100 0000 .....?...?...... 3ffe510: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe540: d500 0000 be3f 0000 be3f 0000 0100 0000 .....?...?...... 3ffe550: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe580: d600 0000 bf3f 0000 bf3f 0000 0100 0000 .....?...?...... 3ffe590: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe5c0: d700 0000 c03f 0000 c03f 0000 0100 0000 .....?...?...... 3ffe5d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe600: d800 0000 c13f 0000 c13f 0000 0100 0000 .....?...?...... 3ffe610: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe640: d900 0000 c23f 0000 c23f 0000 0100 0000 .....?...?...... 3ffe650: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe680: da00 0000 c33f 0000 c33f 0000 0100 0000 .....?...?...... 3ffe690: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe6c0: db00 0000 c43f 0000 c43f 0000 0100 0000 .....?...?...... 3ffe6d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe700: dc00 0000 c53f 0000 c53f 0000 0100 0000 .....?...?...... 3ffe710: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe740: dd00 0000 c63f 0000 c63f 0000 0100 0000 .....?...?...... 3ffe750: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe780: de00 0000 c73f 0000 c73f 0000 0100 0000 .....?...?...... 3ffe790: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe7c0: df00 0000 c83f 0000 c83f 0000 0100 0000 .....?...?...... 3ffe7d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe800: e000 0000 c93f 0000 c93f 0000 0100 0000 .....?...?...... 3ffe810: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe840: e100 0000 ca3f 0000 ca3f 0000 0100 0000 .....?...?...... 3ffe850: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe880: e200 0000 cb3f 0000 cb3f 0000 0100 0000 .....?...?...... 3ffe890: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe8c0: e300 0000 cc3f 0000 cc3f 0000 0100 0000 .....?...?...... 3ffe8d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe900: e400 0000 cd3f 0000 cd3f 0000 0100 0000 .....?...?...... 3ffe910: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe940: e500 0000 ce3f 0000 ce3f 0000 0100 0000 .....?...?...... 3ffe950: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe980: e600 0000 cf3f 0000 cf3f 0000 0100 0000 .....?...?...... 3ffe990: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffe9c0: e700 0000 d03f 0000 d03f 0000 0100 0000 .....?...?...... 3ffe9d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffea00: e800 0000 d13f 0000 d13f 0000 0100 0000 .....?...?...... 3ffea10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffea40: e900 0000 d23f 0000 d23f 0000 0100 0000 .....?...?...... 3ffea50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffea80: ea00 0000 d33f 0000 d33f 0000 0100 0000 .....?...?...... 3ffea90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffeac0: eb00 0000 d43f 0000 d43f 0000 0100 0000 .....?...?...... 3ffead0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffeb00: ec00 0000 d53f 0000 d53f 0000 0100 0000 .....?...?...... 3ffeb10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffeb40: ed00 0000 d63f 0000 d63f 0000 0100 0000 .....?...?...... 3ffeb50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffeb80: ee00 0000 d73f 0000 d73f 0000 0100 0000 .....?...?...... 3ffeb90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffebc0: ef00 0000 d83f 0000 d83f 0000 0100 0000 .....?...?...... 3ffebd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffec00: f000 0000 d93f 0000 d93f 0000 0100 0000 .....?...?...... 3ffec10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffec40: f100 0000 da3f 0000 da3f 0000 0100 0000 .....?...?...... 3ffec50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffec80: f200 0000 db3f 0000 db3f 0000 0100 0000 .....?...?...... 3ffec90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffecc0: f300 0000 dc3f 0000 dc3f 0000 0100 0000 .....?...?...... 3ffecd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffed00: f400 0000 dd3f 0000 dd3f 0000 0100 0000 .....?...?...... 3ffed10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffed40: f500 0000 de3f 0000 de3f 0000 0100 0000 .....?...?...... 3ffed50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffed80: f600 0000 df3f 0000 df3f 0000 0100 0000 .....?...?...... 3ffed90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffedc0: f700 0000 e03f 0000 e03f 0000 0100 0000 .....?...?...... 3ffedd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffee00: f800 0000 e13f 0000 e13f 0000 0100 0000 .....?...?...... 3ffee10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffee40: f900 0000 e23f 0000 e23f 0000 0100 0000 .....?...?...... 3ffee50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffee80: fa00 0000 e33f 0000 e33f 0000 0100 0000 .....?...?...... 3ffee90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffeec0: fb00 0000 e43f 0000 e43f 0000 0100 0000 .....?...?...... 3ffeed0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffef00: fc00 0000 e53f 0000 e53f 0000 0100 0000 .....?...?...... 3ffef10: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffef40: fd00 0000 e63f 0000 e63f 0000 0100 0000 .....?...?...... 3ffef50: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffef80: fe00 0000 e73f 0000 e73f 0000 0100 0000 .....?...?...... 3ffef90: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3ffefc0: ff00 0000 e83f 0000 e83f 0000 0100 0000 .....?...?...... 3ffefd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3fff000: 4254 545f 4152 454e 415f 494e 464f 0000 BTT_ARENA_INFO.. 3fff010: f0d6 4d51 7ad4 44e2 8757 6b8e 8e4f 7500 ..MQz.D..Wk..Ou. 3fff020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 3fff030: 0000 0000 0200 0000 0010 0000 e93e 0000 .............>.. 3fff040: 0010 0000 e93f 0000 0001 0000 0010 0000 .....?.......... 3fff050: 0000 0000 0000 0000 0010 0000 0000 0000 ................ 3fff060: 00b0 fe03 0000 0000 00b0 ff03 0000 0000 ................ 3fff070: 00f0 ff03 0000 0000 0000 0000 0000 0000 ................ 3fff080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ * 3fffff0: 0000 0000 0000 0000 5db7 55ca 03a9 d829 ........].U....) ndctl-67/test/clear.sh000077500000000000000000000052371355562357700150510ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. set -e rc=77 . ./common check_min_kver "4.6" || do_skip "lacks clear poison support" trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 # create pmem dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m raw) eval $(echo $json | json2var) [ $dev = "x" ] && echo "fail: $LINENO" && exit 1 [ $mode != "raw" ] && echo "fail: $LINENO" && exit 1 # inject errors in the middle of the namespace, verify that reading fails err_sector="$(((size/512) / 2))" err_count=8 if ! read sector len < /sys/block/$blockdev/badblocks; then $NDCTL inject-error --block="$err_sector" --count=$err_count $dev $NDCTL start-scrub && $NDCTL wait-scrub fi read sector len < /sys/block/$blockdev/badblocks [ $((sector * 2)) -ne $((size /512)) ] && echo "fail: $LINENO" && exit 1 if dd if=/dev/$blockdev of=/dev/null iflag=direct bs=512 skip=$sector count=$len; then echo "fail: $LINENO" && exit 1 fi size_raw=$size sector_raw=$sector # convert pmem to fsdax mode json=$($NDCTL create-namespace -m fsdax -f -e $dev) eval $(echo $json | json2var) [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 # check for errors relative to the offset injected by the pfn device read sector len < /sys/block/$blockdev/badblocks [ $((sector_raw - sector)) -ne $(((size_raw - size) / 512)) ] && echo "fail: $LINENO" && exit 1 # check that writing clears the errors if ! dd of=/dev/$blockdev if=/dev/zero oflag=direct bs=512 seek=$sector count=$len; then echo "fail: $LINENO" && exit 1 fi if read sector len < /sys/block/$blockdev/badblocks; then # fail if reading badblocks returns data echo "fail: $LINENO" && exit 1 fi if check_min_kver "4.9"; then # check for re-appearance of stale badblocks from poison_list $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all # since we have cleared the errors, a disable/reenable shouldn't bring them back if read sector len < /sys/block/$blockdev/badblocks; then # fail if reading badblocks returns data echo "fail: $LINENO" && exit 1 fi fi _cleanup exit 0 ndctl-67/test/common000066400000000000000000000030721355562357700146320ustar00rootroot00000000000000 # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018, FUJITSU LIMITED. All rights reserved. # Global variables # NDCTL # if [ -f "../ndctl/ndctl" ] && [ -x "../ndctl/ndctl" ]; then export NDCTL=../ndctl/ndctl elif [ -f "./ndctl/ndctl" ] && [ -x "./ndctl/ndctl" ]; then export NDCTL=./ndctl/ndctl else echo "Couldn't find an ndctl binary" exit 1 fi # DAXCTL # if [ -f "../daxctl/daxctl" ] && [ -x "../daxctl/daxctl" ]; then export DAXCTL=../daxctl/daxctl elif [ -f "./daxctl/daxctl" ] && [ -x "./daxctl/daxctl" ]; then export DAXCTL=./daxctl/daxctl else echo "Couldn't find an daxctl binary" exit 1 fi # NFIT_TEST_BUS[01] # NFIT_TEST_BUS0="nfit_test.0" NFIT_TEST_BUS1="nfit_test.1" ACPI_BUS="ACPI.NFIT" E820_BUS="e820" # Functions # err # $1: line number which error detected # $2: cleanup function (optional) # err() { echo test/$(basename $0): failed at line $1 [ -n "$2" ] && "$2" exit $rc } # check_min_kver # $1: Supported kernel version. format: X.Y # check_min_kver() { local ver="$1" : "${KVER:=$(uname -r)}" [ -n "$ver" ] || return 1 [[ "$ver" == "$(echo -e "$ver\n$KVER" | sort -V | head -1)" ]] } # do_skip # $1: Skip message # do_skip() { echo kernel $(uname -r): $1 exit 77 } # check_prereq # $1: command to check # check_prereq() { if ! command -v "$1" >/dev/null; then do_skip "missing $1, skipping..." fi } # _cleanup # _cleanup() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL disable-region -b $NFIT_TEST_BUS1 all modprobe -r nfit_test } # json2var # stdin: json # json2var() { sed -e "s/[{}\",]//g; s/\[//g; s/\]//g; s/:/=/g" } ndctl-67/test/core.c000066400000000000000000000134631355562357700145200ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #define KVER_STRLEN 20 struct ndctl_test { unsigned int kver; int attempt; int skip; }; static unsigned int get_system_kver(void) { const char *kver = getenv("KVER"); struct utsname utsname; int a, b, c; if (!kver) { uname(&utsname); kver = utsname.release; } if (sscanf(kver, "%d.%d.%d", &a, &b, &c) != 3) return LINUX_VERSION_CODE; return KERNEL_VERSION(a,b,c); } struct ndctl_test *ndctl_test_new(unsigned int kver) { struct ndctl_test *test = calloc(1, sizeof(*test)); if (!test) return NULL; if (!kver) test->kver = get_system_kver(); else test->kver = kver; return test; } int ndctl_test_result(struct ndctl_test *test, int rc) { if (ndctl_test_get_skipped(test)) fprintf(stderr, "attempted: %d skipped: %d\n", ndctl_test_get_attempted(test), ndctl_test_get_skipped(test)); if (rc && rc != 77) return rc; if (ndctl_test_get_skipped(test) >= ndctl_test_get_attempted(test)) return 77; /* return success if no failures and at least one test not skipped */ return 0; } static char *kver_str(char *buf, unsigned int kver) { snprintf(buf, KVER_STRLEN, "%d.%d.%d", (kver >> 16) & 0xffff, (kver >> 8) & 0xff, kver & 0xff); return buf; } int __ndctl_test_attempt(struct ndctl_test *test, unsigned int kver, const char *caller, int line) { char requires[KVER_STRLEN], current[KVER_STRLEN]; test->attempt++; if (kver <= test->kver) return 1; fprintf(stderr, "%s: skip %s:%d requires: %s current: %s\n", __func__, caller, line, kver_str(requires, kver), kver_str(current, test->kver)); test->skip++; return 0; } void __ndctl_test_skip(struct ndctl_test *test, const char *caller, int line) { test->skip++; test->attempt = test->skip; fprintf(stderr, "%s: explicit skip %s:%d\n", __func__, caller, line); } int ndctl_test_get_attempted(struct ndctl_test *test) { return test->attempt; } int ndctl_test_get_skipped(struct ndctl_test *test) { return test->skip; } int nfit_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, struct ndctl_ctx *nd_ctx, int log_level, struct ndctl_test *test) { int rc; unsigned int i; const char *name; struct ndctl_bus *bus; struct log_ctx log_ctx; const char *list[] = { "nfit", "device_dax", "dax_pmem", "dax_pmem_core", "dax_pmem_compat", "libnvdimm", "nd_blk", "nd_btt", "nd_e820", "nd_pmem", }; log_init(&log_ctx, "test/init", "NDCTL_TEST"); log_ctx.log_priority = log_level; *ctx = kmod_new(NULL, NULL); if (!*ctx) return -ENXIO; kmod_set_log_priority(*ctx, log_level); /* * Check that all nfit, libnvdimm, and device-dax modules are * the mocked versions. If they are loaded, check that they have * the "out-of-tree" kernel taint, otherwise check that they * come from the "/lib/modules//extra" directory. */ for (i = 0; i < ARRAY_SIZE(list); i++) { char attr[SYSFS_ATTR_SIZE]; const char *path; char buf[100]; int state; name = list[i]; /* * Don't check for device-dax modules on kernels older * than 4.7. */ if (strstr(name, "dax") && !ndctl_test_attempt(test, KERNEL_VERSION(4, 7, 0))) continue; /* * Skip device-dax bus-model modules on pre-v5.1 */ if ((strstr(name, "dax_pmem_core") || strstr(name, "dax_pmem_compat")) && !ndctl_test_attempt(test, KERNEL_VERSION(5, 1, 0))) continue; retry: rc = kmod_module_new_from_name(*ctx, name, mod); if (rc) { log_err(&log_ctx, "%s.ko: missing\n", name); break; } path = kmod_module_get_path(*mod); if (!path) { log_err(&log_ctx, "%s.ko: failed to get path\n", name); break; } if (!strstr(path, "/extra/")) { log_err(&log_ctx, "%s.ko: appears to be production version: %s\n", name, path); break; } state = kmod_module_get_initstate(*mod); if (state == KMOD_MODULE_LIVE) { sprintf(buf, "/sys/module/%s/taint", name); rc = __sysfs_read_attr(&log_ctx, buf, attr); if (rc < 0) { log_err(&log_ctx, "%s.ko: failed to read %s\n", name, buf); break; } if (!strchr(attr, 'O')) { log_err(&log_ctx, "%s.ko: expected taint: O got: %s\n", name, attr); break; } } else if (state == KMOD_MODULE_BUILTIN) { log_err(&log_ctx, "%s: must be built as a module\n", name); break; } } if (i < ARRAY_SIZE(list)) { /* device-dax changed module names in 4.12 */ if (strcmp(name, "device_dax") == 0) { name = "dax"; goto retry; } kmod_unref(*ctx); return -ENXIO; } rc = kmod_module_new_from_name(*ctx, "nfit_test", mod); if (rc < 0) { kmod_unref(*ctx); return rc; } if (nd_ctx) { /* caller wants a full nfit_test reset */ ndctl_bus_foreach(nd_ctx, bus) { struct ndctl_region *region; if (strncmp(ndctl_bus_get_provider(bus), "nfit_test", 9) != 0) continue; ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); } rc = kmod_module_remove_module(*mod, 0); if (rc < 0 && rc != -ENOENT) { kmod_unref(*ctx); return rc; } ndctl_invalidate(nd_ctx); } rc = kmod_module_probe_insert_module(*mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); if (rc) kmod_unref(*ctx); return rc; } ndctl-67/test/create.sh000077500000000000000000000040041355562357700152150ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. set -e SECTOR_SIZE="4096" rc=77 . ./common check_min_kver "4.5" || do_skip "may lack namespace mode attribute" trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 # create pmem dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m raw) eval $(echo $json | json2var ) [ $dev = "x" ] && echo "fail: $LINENO" && exit 1 [ $mode != "raw" ] && echo "fail: $LINENO" && exit 1 # convert pmem to fsdax mode json=$($NDCTL create-namespace -m fsdax -f -e $dev) eval $(echo $json | json2var) [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 # convert pmem to sector mode json=$($NDCTL create-namespace -m sector -l $SECTOR_SIZE -f -e $dev) eval $(echo $json | json2var) [ $sector_size != $SECTOR_SIZE ] && echo "fail: $LINENO" && exit 1 [ $mode != "sector" ] && echo "fail: $LINENO" && exit 1 # free capacity for blk creation $NDCTL destroy-namespace -f $dev # create blk dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t blk -m raw -v) eval $(echo $json | json2var) [ $dev = "x" ] && echo "fail: $LINENO" && exit 1 [ $mode != "raw" ] && echo "fail: $LINENO" && exit 1 # convert blk to sector mode json=$($NDCTL create-namespace -m sector -l $SECTOR_SIZE -f -e $dev) eval $(echo $json | json2var) [ $sector_size != $SECTOR_SIZE ] && echo "fail: $LINENO" && exit 1 [ $mode != "sector" ] && echo "fail: $LINENO" && exit 1 _cleanup exit 0 ndctl-67/test/dax-dev.c000066400000000000000000000055571355562357700151250ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct ndctl_namespace *ndctl_get_test_dev(struct ndctl_ctx *ctx) { char path[256]; const char *bdev; int fd, rc = -ENXIO; struct ndctl_bus *bus; struct ndctl_dax *dax; struct ndctl_pfn *pfn; struct ndctl_region *region; struct ndctl_namespace *ndns; enum ndctl_namespace_mode mode; bus = ndctl_bus_get_by_provider(ctx, "e820"); if (!bus) goto out; region = ndctl_region_get_first(bus); if (!region) goto out; ndns = ndctl_namespace_get_first(region); if (!ndns) goto out; mode = ndctl_namespace_get_mode(ndns); if (mode >= 0 && mode != NDCTL_NS_MODE_MEMORY) goto out; /* if device-dax mode already established it might contain user data */ pfn = ndctl_namespace_get_pfn(ndns); dax = ndctl_namespace_get_dax(ndns); if (dax || pfn) goto out; /* device is unconfigured, assume that was on purpose */ bdev = ndctl_namespace_get_block_device(ndns); if (!bdev) goto out; if (snprintf(path, sizeof(path), "/dev/%s", bdev) >= (int) sizeof(path)) goto out; /* * Note, if the bdev goes active after this check we'll still * clobber it in the following tests, see test/dax.sh and * test/device-dax.sh. */ fd = open(path, O_RDWR | O_EXCL); if (fd < 0) goto out; close(fd); rc = 0; out: return rc ? NULL : ndns; } static int emit_e820_device(int loglevel, struct ndctl_test *test) { int err; struct ndctl_ctx *ctx; struct ndctl_namespace *ndns; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 3, 0))) return 77; err = ndctl_new(&ctx); if (err < 0) return err; ndctl_set_log_priority(ctx, loglevel); ndns = ndctl_get_test_dev(ctx); if (!ndns) { fprintf(stderr, "%s: failed to find usable victim device\n", __func__); ndctl_test_skip(test); err = 77; } else { fprintf(stdout, "%s\n", ndctl_namespace_get_devname(ndns)); err = 0; } ndctl_unref(ctx); return err; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = emit_e820_device(LOG_DEBUG, test); return ndctl_test_result(test, rc); } ndctl-67/test/dax-errors.c000066400000000000000000000063341355562357700156550ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define fail() fprintf(stderr, "%s: failed at: %d\n", __func__, __LINE__) static sigjmp_buf sj_env; static int sig_count; /* buf is global in order to avoid gcc memcpy optimization */ static void *buf; static void sigbus_hdl(int sig, siginfo_t *siginfo, void *ptr) { fprintf(stderr, "** Received a SIGBUS **\n"); sig_count++; siglongjmp(sj_env, 1); } static int test_dax_read_err(int fd) { void *base; int rc = 0; if (fd < 0) { fail(); return -ENXIO; } if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; base = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { perror("mmap"); rc = -ENXIO; goto err_mmap; } if (sigsetjmp(sj_env, 1)) { if (sig_count == 1) { fprintf(stderr, "Failed to read from mapped file\n"); free(buf); if (base) { if (munmap(base, 4096) < 0) { fail(); return 1; } } return 1; } return sig_count; } /* read a page through DAX (should fail due to a bad block) */ memcpy(buf, base, 4096); err_mmap: free(buf); return rc; } /* TODO: disabled till we get clear-on-write in the kernel */ #if 0 static int test_dax_write_clear(int fd) { void *buf; int rc = 0; if (fd < 0) { fail(); return -ENXIO; } if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; memset(buf, 0, 4096); /* * Attempt to write zeroes to the first page of the file using write() * This should clear the pmem errors/bad blocks */ printf("Attempting to write\n"); if (write(fd, buf, 4096) < 0) rc = errno; free(buf); return rc; } #endif int main(int argc, char *argv[]) { int fd, rc; struct sigaction act; if (argc < 1) return -EINVAL; memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus_hdl; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { fail(); return 1; } fd = open(argv[1], O_RDWR | O_DIRECT); /* Start the test. First, we do an mmap-read, and expect it to fail */ rc = test_dax_read_err(fd); if (rc == 0) { fprintf(stderr, "Expected read to fail, but it succeeded\n"); rc = -ENXIO; goto out; } if (rc > 1) { fprintf(stderr, "Received a second SIGBUS, exiting.\n"); rc = -ENXIO; goto out; } printf(" mmap-read failed as expected\n"); rc = 0; /* Next, do a regular (O_DIRECT) write() */ /* TODO: Disable this till we have clear-on-write in the kernel * rc = test_dax_write_clear(fd); * * if (rc) * perror("write"); */ out: if (fd >= 0) close(fd); return rc; } ndctl-67/test/dax-ext4.sh000077700000000000000000000000001355562357700165122dax.shustar00rootroot00000000000000ndctl-67/test/dax-pmd.c000066400000000000000000000143771355562357700151270ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_EXTENTS 5 #define fail() fprintf(stderr, "%s: failed at: %d (%s)\n", \ __func__, __LINE__, strerror(errno)) #define faili(i) fprintf(stderr, "%s: failed at: %d: %d (%s)\n", \ __func__, __LINE__, i, strerror(errno)) #define TEST_FILE "test_dax_data" int test_dax_directio(int dax_fd, unsigned long align, void *dax_addr, off_t offset) { int i, rc = -ENXIO; void *buf; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; for (i = 0; i < 5; i++) { unsigned long flags; void *addr; int fd2; if (dax_fd >= 0) flags = MAP_SHARED; else { /* hugetlbfs instead of device-dax */ const char *base = "/sys/kernel/mm/hugepages"; FILE *f_nrhuge; char path[256]; flags = MAP_SHARED | MAP_ANONYMOUS; if (align >= SZ_2M) { char setting[] = { "2\n" }; sprintf(path, "%s/hugepages-%ldkB/nr_hugepages", base, align / 1024); f_nrhuge = fopen(path, "r+"); if (!f_nrhuge) { rc = -errno; faili(i); return rc; } if (fwrite(setting, sizeof(setting), 1, f_nrhuge) != 1) { rc = -errno; faili(i); fclose(f_nrhuge); return rc; } fclose(f_nrhuge); /* FIXME: support non-x86 page sizes */ if (align > SZ_2M) flags |= MAP_HUGETLB | MAP_HUGE_1GB; else flags |= MAP_HUGETLB | MAP_HUGE_2MB; } } addr = mmap(dax_addr, 2*align, PROT_READ|PROT_WRITE, flags, dax_fd, offset); if (addr == MAP_FAILED) { rc = -errno; faili(i); break; } rc = -ENXIO; fd2 = open(TEST_FILE, O_CREAT|O_TRUNC|O_DIRECT|O_RDWR, DEFFILEMODE); if (fd2 < 0) { faili(i); munmap(addr, 2*align); break; } fprintf(stderr, "%s: test: %d\n", __func__, i); rc = 0; switch (i) { case 0: /* test O_DIRECT read of unfaulted address */ if (write(fd2, addr, 4096) != 4096) { faili(i); rc = -ENXIO; } /* * test O_DIRECT write of pre-faulted read-only * address */ if (pread(fd2, addr, 4096, 0) != 4096) { faili(i); rc = -ENXIO; } break; case 1: /* test O_DIRECT of pre-faulted address */ sprintf(addr, "odirect data"); if (pwrite(fd2, addr, 4096, 0) != 4096) { faili(i); rc = -ENXIO; } ((char *) buf)[0] = 0; if (pread(fd2, buf, 4096, 0) != 4096) { faili(i); rc = -ENXIO; } if (strcmp(buf, "odirect data") != 0) { faili(i); rc = -ENXIO; } break; case 2: /* fork with pre-faulted pmd */ sprintf(addr, "fork data"); rc = fork(); if (rc == 0) { /* child */ if (strcmp(addr, "fork data") == 0) exit(EXIT_SUCCESS); else exit(EXIT_FAILURE); } else if (rc > 0) { /* parent */ wait(&rc); rc = WEXITSTATUS(rc); if (rc != EXIT_SUCCESS) { faili(i); } } else faili(i); break; case 3: /* convert ro mapping to rw */ rc = *(volatile int *) addr; *(volatile int *) addr = rc; rc = 0; break; case 4: /* test O_DIRECT write of unfaulted address */ sprintf(buf, "O_DIRECT write of unfaulted address\n"); if (pwrite(fd2, buf, 4096, 0) < 4096) { faili(i); rc = -ENXIO; break; } if (pread(fd2, addr, 4096, 0) < 4096) { faili(i); rc = -ENXIO; break; } rc = 0; break; default: faili(i); rc = -ENXIO; break; } munmap(addr, 2*align); addr = MAP_FAILED; unlink(TEST_FILE); close(fd2); fd2 = -1; if (rc) break; } free(buf); return rc; } /* test_pmd assumes that fd references a pre-allocated + dax-capable file */ static int test_pmd(struct ndctl_test *test, int fd) { unsigned long long m_align, p_align, pmd_off; static const bool fsdax = true; struct fiemap_extent *ext; void *base, *pmd_addr; struct fiemap *map; int rc = -ENXIO; unsigned long i; if (fd < 0) { fail(); return -ENXIO; } map = calloc(1, sizeof(struct fiemap) + sizeof(struct fiemap_extent) * NUM_EXTENTS); if (!map) { fail(); return -ENXIO; } base = mmap(NULL, 4*HPAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { fail(); goto err_mmap; } munmap(base, 4*HPAGE_SIZE); map->fm_start = 0; map->fm_length = -1; map->fm_extent_count = NUM_EXTENTS; rc = ioctl(fd, FS_IOC_FIEMAP, map); if (rc < 0) { fail(); goto err_extent; } for (i = 0; i < map->fm_mapped_extents; i++) { ext = &map->fm_extents[i]; p_align = ALIGN(ext->fe_physical, HPAGE_SIZE) - ext->fe_physical; fprintf(stderr, "[%ld]: l: %llx p: %llx len: %llx flags: %x\n", i, ext->fe_logical, ext->fe_physical, ext->fe_length, ext->fe_flags); if (ext->fe_length > 2 * HPAGE_SIZE && p_align == 0) { fprintf(stderr, "found potential huge extent\n"); break; } } if (i >= map->fm_mapped_extents) { fail(); goto err_extent; } m_align = ALIGN(base, HPAGE_SIZE) - ((unsigned long) base); p_align = ALIGN(ext->fe_physical, HPAGE_SIZE) - ext->fe_physical; pmd_addr = (char *) base + m_align; pmd_off = ext->fe_logical + p_align; rc = test_dax_directio(fd, HPAGE_SIZE, pmd_addr, pmd_off); if (rc) goto err_directio; rc = test_dax_poison(test, fd, HPAGE_SIZE, pmd_addr, pmd_off, fsdax); err_directio: err_extent: err_mmap: free(map); return rc; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); int fd, rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } if (argc < 1) return -EINVAL; fd = open(argv[1], O_RDWR); rc = test_pmd(test, fd); if (fd >= 0) close(fd); return ndctl_test_result(test, rc); } ndctl-67/test/dax-poison.c000066400000000000000000000057111355562357700156460ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define fail() fprintf(stderr, "%s: failed at: %d (%s)\n", \ __func__, __LINE__, strerror(errno)) static sigjmp_buf sj_env; static int sig_mcerr_ao, sig_mcerr_ar, sig_count; static void sigbus_hdl(int sig, siginfo_t *si, void *ptr) { switch (si->si_code) { case BUS_MCEERR_AO: fprintf(stderr, "%s: BUS_MCEERR_AO addr: %p len: %d\n", __func__, si->si_addr, 1 << si->si_addr_lsb); sig_mcerr_ao++; break; case BUS_MCEERR_AR: fprintf(stderr, "%s: BUS_MCEERR_AR addr: %p len: %d\n", __func__, si->si_addr, 1 << si->si_addr_lsb); sig_mcerr_ar++; break; default: sig_count++; break; } siglongjmp(sj_env, 1); } int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, void *dax_addr, off_t offset, bool fsdax) { unsigned char *addr = MAP_FAILED; struct sigaction act; unsigned x = x; void *buf; int rc; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 19, 0))) return 77; /* * MADV_HWPOISON must be page aligned, and this routine assumes * align is >= 8K */ if (align < SZ_2M) return 0; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus_hdl; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { fail(); rc = -errno; goto out; } /* dirty the block on disk to bypass the default zero page */ if (fsdax) { rc = pwrite(dax_fd, buf, 4096, offset + align / 2); if (rc < 4096) { fail(); rc = -ENXIO; goto out; } fsync(dax_fd); } addr = mmap(dax_addr, 2*align, PROT_READ|PROT_WRITE, MAP_SHARED_VALIDATE|MAP_POPULATE|MAP_SYNC, dax_fd, offset); if (addr == MAP_FAILED) { fail(); rc = -errno; goto out; } if (sigsetjmp(sj_env, 1)) { if (sig_mcerr_ar) { fprintf(stderr, "madvise triggered 'action required' sigbus\n"); goto clear_error; } else if (sig_count) { fail(); return -ENXIO; } } rc = madvise(addr + align / 2, 4096, MADV_HWPOISON); if (rc) { fail(); rc = -errno; goto out; } /* clear the error */ clear_error: if (!sig_mcerr_ar) { fail(); rc = -ENXIO; goto out; } if (!fsdax) { rc = 0; goto out; } rc = fallocate(dax_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset + align / 2, 4096); if (rc) { fail(); rc = -errno; goto out; } rc = pwrite(dax_fd, buf, 4096, offset + align / 2); if (rc < 4096) { fail(); rc = -ENXIO; goto out; } fsync(dax_fd); /* check that we can fault in the poison page */ x = *(volatile unsigned *) addr + align / 2; rc = 0; out: if (addr != MAP_FAILED) munmap(addr, 2 * align); free(buf); return rc; } ndctl-67/test/dax-xfs.sh000077700000000000000000000000001355562357700164262dax.shustar00rootroot00000000000000ndctl-67/test/dax.sh000077500000000000000000000061211355562357700145300ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. . ./common MNT=test_dax_mnt FILE=image blockdev="" cleanup() { echo "test-dax: failed at line $1" if [ -n "$blockdev" ]; then umount /dev/$blockdev else rc=77 fi rmdir $MNT exit $rc } run_test() { rc=0 if ! trace-cmd record -e fs_dax:dax_pmd_fault_done ./dax-pmd $MNT/$FILE; then rc=$? if [ "$rc" -ne 77 ] && [ "$rc" -ne 0 ]; then cleanup "$1" fi fi # Fragile hack to double check the kernel services this test # with successful pmd faults. If dax-pmd.c ever changes the # number of times the dax_pmd_fault_done trace point fires the # hack needs to be updated from 10 expected firings and the # result of success (NOPAGE). count=0 rc=1 while read -r p; do [[ $p ]] || continue if [ "$count" -lt 10 ]; then if [ "$p" != "0x100" ] && [ "$p" != "NOPAGE" ]; then cleanup "$1" fi fi count=$((count + 1)) done < <(trace-cmd report | awk '{ print $21 }') if [ $count -lt 10 ]; then cleanup "$1" fi } run_ext4() { mkfs.ext4 -b 4096 /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test $LINENO umount $MNT # convert pmem to put the memmap on the device json=$($NDCTL create-namespace -m fsdax -M dev -f -e $dev) eval $(json2var <<< "$json") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 #note the blockdev returned from ndctl create-namespace lacks the /dev prefix mkfs.ext4 -b 4096 /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test $LINENO umount $MNT json=$($NDCTL create-namespace -m raw -f -e $dev) eval $(json2var <<< "$json") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 true } run_xfs() { mkfs.xfs -f -d su=2m,sw=1,agcount=2 -m reflink=0 /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test $LINENO umount $MNT # convert pmem to put the memmap on the device json=$($NDCTL create-namespace -m fsdax -M dev -f -e $dev) eval $(json2var <<< "$json") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 mkfs.xfs -f -d su=2m,sw=1,agcount=2 -m reflink=0 /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test $LINENO umount $MNT # revert namespace to raw mode json=$($NDCTL create-namespace -m raw -f -e $dev) eval $(json2var <<< "$json") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 true } set -e mkdir -p $MNT trap 'err $LINENO cleanup' ERR dev=$(./dax-dev) json=$($NDCTL list -N -n $dev) eval $(json2var <<< "$json") rc=1 if [ $(basename $0) = "dax-ext4.sh" ]; then run_ext4 elif [ $(basename $0) = "dax-xfs.sh" ]; then run_xfs else run_ext4 run_xfs fi exit 0 ndctl-67/test/daxctl-devices.sh000077500000000000000000000036531355562357700166620ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2019 Intel Corporation. All rights reserved. rc=77 . ./common trap 'cleanup $LINENO' ERR cleanup() { printf "Error at line %d\n" "$1" [[ $testdev ]] && reset_dev exit $rc } find_testdev() { local rc=77 # The kmem driver is needed to change the device mode, only # kernels >= v5.1 might have it available. Skip if not. if ! modinfo kmem; then # check if kmem is builtin if ! grep -qF "kmem" "/lib/modules/$(uname -r)/modules.builtin"; then printf "Unable to find kmem module\n" exit $rc fi fi # find a victim device testbus="$ACPI_BUS" testdev=$("$NDCTL" list -b "$testbus" -Ni | jq -er '.[0].dev | .//""') if [[ ! $testdev ]]; then printf "Unable to find a victim device\n" exit "$rc" fi printf "Found victim dev: %s on bus: %s\n" "$testdev" "$testbus" } setup_dev() { test -n "$testbus" test -n "$testdev" "$NDCTL" destroy-namespace -f -b "$testbus" "$testdev" testdev=$("$NDCTL" create-namespace -b "$testbus" -m devdax -fe "$testdev" -s 256M | \ jq -er '.dev') test -n "$testdev" } reset_dev() { "$NDCTL" destroy-namespace -f -b "$testbus" "$testdev" } daxctl_get_dev() { "$NDCTL" list -n "$1" -X | jq -er '.[].daxregion.devices[0].chardev' } daxctl_get_mode() { "$DAXCTL" list -d "$1" | jq -er '.[].mode' } daxctl_test() { local daxdev daxdev=$(daxctl_get_dev "$testdev") test -n "$daxdev" "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev" [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] "$DAXCTL" online-memory "$daxdev" "$DAXCTL" offline-memory "$daxdev" "$DAXCTL" reconfigure-device -m devdax "$daxdev" [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] "$DAXCTL" reconfigure-device -m system-ram "$daxdev" [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] } find_testdev setup_dev rc=1 daxctl_test reset_dev exit 0 ndctl-67/test/daxdev-errors.c000066400000000000000000000206431355562357700163530ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define fail() fprintf(stderr, "%s: failed at: %d\n", __func__, __LINE__) struct check_cmd { struct ndctl_cmd *cmd; struct ndctl_test *test; }; static sigjmp_buf sj_env; static int sig_count; static const char *NFIT_PROVIDER0 = "nfit_test.0"; static struct check_cmd *check_cmds; static void sigbus_hdl(int sig, siginfo_t *siginfo, void *ptr) { fprintf(stderr, "** Received a SIGBUS **\n"); sig_count++; siglongjmp(sj_env, 1); } static int check_ars_cap(struct ndctl_bus *bus, uint64_t start, size_t size, struct check_cmd *check) { struct ndctl_cmd *cmd; int rc; if (check->cmd != NULL) { fprintf(stderr, "%s: expected a NULL command, by default\n", __func__); return -EINVAL; } cmd = ndctl_bus_cmd_new_ars_cap(bus, start, size); if (!cmd) { fprintf(stderr, "%s: bus: %s failed to create cmd\n", __func__, ndctl_bus_get_provider(bus)); return -ENOTTY; } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n", __func__, ndctl_bus_get_provider(bus), rc); ndctl_cmd_unref(cmd); return rc; } if (ndctl_cmd_ars_cap_get_size(cmd) < sizeof(struct nd_cmd_ars_status)){ fprintf(stderr, "%s: bus: %s expected size >= %zd got: %d\n", __func__, ndctl_bus_get_provider(bus), sizeof(struct nd_cmd_ars_status), ndctl_cmd_ars_cap_get_size(cmd)); ndctl_cmd_unref(cmd); return -ENXIO; } check->cmd = cmd; return 0; } static int check_ars_start(struct ndctl_bus *bus, struct check_cmd *check) { struct ndctl_cmd *cmd_ars_cap = check_cmds[ND_CMD_ARS_CAP].cmd; struct ndctl_cmd *cmd; int rc; if (check->cmd != NULL) { fprintf(stderr, "%s: expected a NULL command, by default\n", __func__); return -ENXIO; } cmd = ndctl_bus_cmd_new_ars_start(cmd_ars_cap, ND_ARS_PERSISTENT); if (!cmd) { fprintf(stderr, "%s: bus: %s failed to create cmd\n", __func__, ndctl_bus_get_provider(bus)); return -ENOTTY; } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n", __func__, ndctl_bus_get_provider(bus), rc); ndctl_cmd_unref(cmd); return rc; } check->cmd = cmd; return 0; } static int check_ars_status(struct ndctl_bus *bus, struct check_cmd *check) { struct ndctl_cmd *cmd_ars_cap = check_cmds[ND_CMD_ARS_CAP].cmd; struct ndctl_cmd *cmd; unsigned long tmo = 5; unsigned int i; int rc; if (check->cmd != NULL) { fprintf(stderr, "%s: expected a NULL command, by default\n", __func__); return -ENXIO; } retry: cmd = ndctl_bus_cmd_new_ars_status(cmd_ars_cap); if (!cmd) { fprintf(stderr, "%s: bus: %s failed to create cmd\n", __func__, ndctl_bus_get_provider(bus)); return -ENOTTY; } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n", __func__, ndctl_bus_get_provider(bus), rc); ndctl_cmd_unref(cmd); return rc; } if (!tmo) { fprintf(stderr, "%s: bus: %s ars timeout\n", __func__, ndctl_bus_get_provider(bus)); return -EIO; } if (ndctl_cmd_ars_in_progress(cmd)) { tmo--; sleep(1); goto retry; } for (i = 0; i < ndctl_cmd_ars_num_records(cmd); i++) { fprintf(stderr, "%s: record[%d].addr: 0x%llx\n", __func__, i, ndctl_cmd_ars_get_record_addr(cmd, i)); fprintf(stderr, "%s: record[%d].length: 0x%llx\n", __func__, i, ndctl_cmd_ars_get_record_len(cmd, i)); } check->cmd = cmd; return 0; } static int check_clear_error(struct ndctl_bus *bus, struct check_cmd *check) { struct ndctl_cmd *ars_cap = check_cmds[ND_CMD_ARS_CAP].cmd; struct ndctl_cmd *clear_err; unsigned long long cleared; struct ndctl_range range; int rc; if (check->cmd != NULL) { fprintf(stderr, "%s: expected a NULL command, by default\n", __func__); return -ENXIO; } if (ndctl_cmd_ars_cap_get_range(ars_cap, &range)) { fprintf(stderr, "failed to get ars_cap range\n"); return -ENXIO; } fprintf(stderr, "%s: clearing at %#llx for %llu bytes\n", __func__, range.address, range.length); clear_err = ndctl_bus_cmd_new_clear_error(range.address, range.length, ars_cap); if (!clear_err) { fprintf(stderr, "%s: bus: %s failed to create cmd\n", __func__, ndctl_bus_get_provider(bus)); return -ENOTTY; } rc = ndctl_cmd_submit(clear_err); if (rc < 0) { fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n", __func__, ndctl_bus_get_provider(bus), rc); ndctl_cmd_unref(clear_err); return rc; } cleared = ndctl_cmd_clear_error_get_cleared(clear_err); if (cleared != range.length) { fprintf(stderr, "%s: bus: %s expected to clear: %lld actual: %lld\ n", __func__, ndctl_bus_get_provider(bus), range.length, cleared); return -ENXIO; } check->cmd = clear_err; return 0; } static struct ndctl_dax * get_dax_region(struct ndctl_region *region) { struct ndctl_dax *dax; ndctl_dax_foreach(region, dax) if (ndctl_dax_is_enabled(dax) && ndctl_dax_is_configured(dax)) return dax; return NULL; } static int test_daxdev_clear_error(const char *bus_name, const char *region_name) { int rc = 0, i; struct ndctl_ctx *ctx; struct ndctl_bus *bus; struct ndctl_region *region; struct ndctl_dax *dax = NULL; uint64_t base, start, offset, blocks, size; struct check_cmd __bus_cmds[] = { [ND_CMD_ARS_CAP] = {}, [ND_CMD_ARS_START] = {}, [ND_CMD_ARS_STATUS] = {}, [ND_CMD_CLEAR_ERROR] = {}, }; char path[256]; char buf[SYSFS_ATTR_SIZE]; struct log_ctx log_ctx; log_init(&log_ctx, "test/init", "NDCTL_DAXDEV_TEST"); rc = ndctl_new(&ctx); if (rc) return rc; bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER0); if (!bus) { rc = -ENODEV; goto cleanup; } ndctl_region_foreach(bus, region) { if (strncmp(region_name, ndctl_region_get_devname(region), 256) == 0) { /* find the dax region */ dax = get_dax_region(region); break; } } if (!dax) { rc = -ENODEV; goto cleanup; } /* get badblocks */ if (snprintf(path, 256, "/sys/devices/platform/%s/%s/%s/badblocks", NFIT_PROVIDER0, bus_name, ndctl_region_get_devname(region)) >= 256) { fprintf(stderr, "%s: buffer too small!\n", ndctl_region_get_devname(region)); rc = -ENXIO; goto cleanup; } if (__sysfs_read_attr(&log_ctx, path, buf) < 0) { rc = -ENXIO; goto cleanup; } /* retrieve badblocks from buf */ rc = sscanf(buf, "%lu %lu", &offset, &blocks); if (rc == EOF) { rc = -errno; goto cleanup; } /* get resource base */ base = ndctl_region_get_resource(region); if (base == ULLONG_MAX) { rc = -ERANGE; goto cleanup; } check_cmds = __bus_cmds; start = base + offset * 512; size = 512 * blocks; rc = check_ars_cap(bus, start, size, &check_cmds[ND_CMD_ARS_CAP]); if (rc < 0) goto cleanup; rc = check_ars_start(bus, &check_cmds[ND_CMD_ARS_START]); if (rc < 0) goto cleanup; rc = check_ars_status(bus, &check_cmds[ND_CMD_ARS_STATUS]); if (rc < 0) goto cleanup; rc = check_clear_error(bus, &check_cmds[ND_CMD_CLEAR_ERROR]); if (rc < 0) goto cleanup; for (i = 1; i < (int)ARRAY_SIZE(__bus_cmds); i++) { if (__bus_cmds[i].cmd) { ndctl_cmd_unref(__bus_cmds[i].cmd); __bus_cmds[i].cmd = NULL; } } cleanup: ndctl_unref(ctx); return rc; } int main(int argc, char *argv[]) { int rc; struct sigaction act; if (argc < 1 || argc > 4) return -EINVAL; memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus_hdl; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { fail(); return 1; } rc = test_daxdev_clear_error(argv[1], argv[2]); return rc; } ndctl-67/test/daxdev-errors.sh000077500000000000000000000043251355562357700165450ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. set -e rc=77 . ./common check_min_kver "4.12" || do_skip "lacks dax dev error handling" trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 query=". | sort_by(.available_size) | reverse | .[0].dev" region=$($NDCTL list -b $NFIT_TEST_BUS0 -t pmem -Ri | jq -r "$query") json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a 4096) chardev=$(echo $json | jq ". | select(.mode == \"devdax\") | .daxregion.devices[0].chardev") #{ # "dev":"namespace6.0", # "mode":"devdax", # "size":64004096, # "uuid":"83a925dd-42b5-4ac6-8588-6a50bfc0c001", # "daxregion":{ # "id":6, # "size":64004096, # "align":4096, # "devices":[ # { # "chardev":"dax6.0", # "size":64004096 # } # ] # } #} json1=$($NDCTL list -b $NFIT_TEST_BUS0 --mode=devdax --namespaces) eval $(echo $json1 | json2var) nsdev=$dev json1=$($NDCTL list -b $NFIT_TEST_BUS0) eval $(echo $json1 | json2var) busdev=$dev # inject errors in the middle of the namespace err_sector="$(((size/512) / 2))" err_count=8 if ! read sector len < /sys/bus/nd/devices/$region/badblocks; then $NDCTL inject-error --block="$err_sector" --count=$err_count $nsdev fi read sector len < /sys/bus/nd/devices/$region/badblocks echo "sector: $sector len: $len" # run the daxdev-errors test test -x ./daxdev-errors ./daxdev-errors $busdev $region # check badblocks, should be empty if read sector len < /sys/bus/platform/devices/nfit_test.0/$busdev/$region/badblocks; then echo "badblocks empty, expected" fi [ -n "$sector" ] && echo "fail: $LINENO" && exit 1 _cleanup exit 0 ndctl-67/test/device-dax-fio.sh000077500000000000000000000030111355562357700165330ustar00rootroot00000000000000#!/bin/bash # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. . ./common rc=77 set -e check_min_kver "4.11" || do_skip "kernel may lack device-dax fixes" trap 'err $LINENO' ERR check_prereq "fio" if ! fio --enghelp | grep -q "dev-dax"; then echo "fio lacks dev-dax engine" exit 77 fi dev=$(./dax-dev) for align in 4k 2m 1g do json=$($NDCTL create-namespace -m devdax -a $align -f -e $dev) chardev=$(echo $json | jq -r ". | select(.mode == \"devdax\") | .daxregion.devices[0].chardev") if [ align = "1g" ]; then bs="1g" else bs="2m" fi cat > fio.job <<- EOF [global] ioengine=dev-dax direct=0 filename=/dev/${chardev} verify=crc32c bs=${bs} [write] rw=write runtime=5 [read] stonewall rw=read runtime=5 EOF rc=1 fio fio.job 2>&1 | tee fio.log if grep -q "fio.*got signal" fio.log; then echo "test/device-dax-fio.sh: failed with align: $align" exit 1 fi # revert namespace to raw mode json=$($NDCTL create-namespace -m raw -f -e $dev) eval $(json2var <<< "$json") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 done exit 0 ndctl-67/test/device-dax.c000066400000000000000000000235171355562357700156020ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static sigjmp_buf sj_env; static int create_namespace(int argc, const char **argv, void *ctx) { builtin_xaction_namespace_reset(); return cmd_create_namespace(argc, argv, ctx); } static int reset_device_dax(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); const char *argv[] = { "__func__", "-v", "-m", "raw", "-f", "-e", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); return create_namespace(argc, argv, ctx); } static int setup_device_dax(struct ndctl_namespace *ndns, unsigned long __align) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char align[32]; const char *argv[] = { "__func__", "-v", "-m", "devdax", "-M", "dev", "-f", "-a", align, "-e", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); sprintf(align, "%ld", __align); return create_namespace(argc, argv, ctx); } static int setup_pmem_fsdax_mode(struct ndctl_namespace *ndns, unsigned long __align) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); char align[32]; const char *argv[] = { "__func__", "-v", "-m", "fsdax", "-M", "dev", "-f", "-a", align, "-e", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); sprintf(align, "%ld", __align); return create_namespace(argc, argv, ctx); } static void sigbus(int sig, siginfo_t *siginfo, void *d) { siglongjmp(sj_env, 1); } #define VERIFY_SIZE(x) (x * 2) #define VERIFY_BUF_SIZE 4096 /* * This timeout value derived from an Intel(R) Xeon(R) CPU E5-2690 v2 @ * 3.00GHz where the loop, for the align == 2M case, completes in 7500us * when cached and 200ms when uncached. */ #define VERIFY_TIME(x) (suseconds_t) ((ALIGN(x, SZ_2M) / SZ_4K) * 60) static int verify_data(struct daxctl_dev *dev, char *dax_buf, unsigned long align, int salt, struct ndctl_test *test) { struct timeval tv1, tv2, tv_diff; unsigned long i; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) return 0; /* verify data and cache mode */ gettimeofday(&tv1, NULL); for (i = 0; i < VERIFY_SIZE(align); i += VERIFY_BUF_SIZE) { unsigned int *verify = (unsigned int *) (dax_buf + i), j; for (j = 0; j < VERIFY_BUF_SIZE / sizeof(int); j++) if (verify[j] != salt + i + j) break; if (j < VERIFY_BUF_SIZE / sizeof(int)) { fprintf(stderr, "%s: @ %#lx expected %#x got %#lx\n", daxctl_dev_get_devname(dev), i, verify[j], salt + i + j); return -ENXIO; } } gettimeofday(&tv2, NULL); timersub(&tv2, &tv1, &tv_diff); tv_diff.tv_usec += tv_diff.tv_sec * 1000000; if (tv_diff.tv_usec > VERIFY_TIME(align)) { /* * Checks whether the kernel correctly mapped the * device-dax range as cacheable. */ fprintf(stderr, "%s: verify loop took too long usecs: %ld\n", daxctl_dev_get_devname(dev), tv_diff.tv_usec); return -ENXIO; } return 0; } static int __test_device_dax(unsigned long align, int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { unsigned long i; struct sigaction act; struct ndctl_dax *dax; struct ndctl_pfn *pfn; struct daxctl_dev *dev; int fd, rc, *p, salt; struct ndctl_namespace *ndns; struct daxctl_region *dax_region; char *buf, path[100], data[VERIFY_BUF_SIZE]; ndctl_set_log_priority(ctx, loglevel); ndns = ndctl_get_test_dev(ctx); if (!ndns) { fprintf(stderr, "%s: failed to find suitable namespace\n", __func__); return 77; } if (align > SZ_2M && !ndctl_test_attempt(test, KERNEL_VERSION(4, 11, 0))) return 77; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 7, 0))) return 77; /* setup up fsdax mode pmem device and seed with verification data */ rc = setup_pmem_fsdax_mode(ndns, align); if (rc < 0 || !(pfn = ndctl_namespace_get_pfn(ndns))) { fprintf(stderr, "%s: failed device-dax setup\n", ndctl_namespace_get_devname(ndns)); goto out; } sprintf(path, "/dev/%s", ndctl_pfn_get_block_device(pfn)); fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open pmem device\n", path); rc = -ENXIO; goto out; } srand(getpid()); salt = rand(); for (i = 0; i < VERIFY_SIZE(align); i += VERIFY_BUF_SIZE) { unsigned int *verify = (unsigned int *) data, j; for (j = 0; j < VERIFY_BUF_SIZE / sizeof(int); j++) verify[j] = salt + i + j; if (write(fd, data, sizeof(data)) != sizeof(data)) { fprintf(stderr, "%s: failed data setup\n", path); rc = -ENXIO; goto out; } } fsync(fd); close(fd); /* switch the namespace to device-dax mode and verify data via mmap */ rc = setup_device_dax(ndns, align); if (rc < 0) { fprintf(stderr, "%s: failed device-dax setup\n", ndctl_namespace_get_devname(ndns)); goto out; } dax = ndctl_namespace_get_dax(ndns); dax_region = ndctl_dax_get_daxctl_region(dax); dev = daxctl_dev_get_first(dax_region); if (!dev) { fprintf(stderr, "%s: failed to find device-dax instance\n", ndctl_namespace_get_devname(ndns)); rc = -ENXIO; goto out; } sprintf(path, "/dev/%s", daxctl_dev_get_devname(dev)); fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: failed to open(O_RDONLY) device-dax instance\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } buf = mmap(NULL, VERIFY_SIZE(align), PROT_READ, MAP_PRIVATE, fd, 0); if (buf != MAP_FAILED) { fprintf(stderr, "%s: expected MAP_PRIVATE failure\n", path); rc = -ENXIO; goto out; } buf = mmap(NULL, VERIFY_SIZE(align), PROT_READ, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { fprintf(stderr, "%s: expected MAP_SHARED success\n", path); return -ENXIO; } rc = verify_data(dev, buf, align, salt, test); if (rc) goto out; close(fd); munmap(buf, VERIFY_SIZE(align)); /* * Prior to 4.8-final these tests cause crashes, or are * otherwise not supported. */ if (ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { static const bool devdax = false; int fd2; fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open for direct-io test\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } rc = test_dax_directio(fd, align, NULL, 0); if (rc) { fprintf(stderr, "%s: failed dax direct-i/o\n", ndctl_namespace_get_devname(ndns)); goto out; } close(fd); fprintf(stderr, "%s: test dax poison\n", ndctl_namespace_get_devname(ndns)); fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open for poison test\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } rc = test_dax_poison(test, fd, align, NULL, 0, devdax); if (rc) { fprintf(stderr, "%s: failed dax poison\n", ndctl_namespace_get_devname(ndns)); goto out; } close(fd); fd2 = open("/proc/self/smaps", O_RDONLY); if (fd2 < 0) { fprintf(stderr, "%s: failed smaps open\n", ndctl_namespace_get_devname(ndns)); rc = -ENXIO; goto out; } do { rc = read(fd2, data, sizeof(data)); } while (rc > 0); if (rc) { fprintf(stderr, "%s: failed smaps retrieval\n", ndctl_namespace_get_devname(ndns)); rc = -ENXIO; goto out; } } /* establish a writable mapping */ fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open(O_RDWR) device-dax instance\n", daxctl_dev_get_devname(dev)); rc = -ENXIO; goto out; } buf = mmap(NULL, VERIFY_SIZE(align), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { fprintf(stderr, "%s: expected PROT_WRITE + MAP_SHARED success\n", path); return -ENXIO; } rc = reset_device_dax(ndns); if (rc < 0) { fprintf(stderr, "%s: failed to reset device-dax instance\n", ndctl_namespace_get_devname(ndns)); goto out; } memset(&act, 0, sizeof(act)); act.sa_sigaction = sigbus; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0)) { perror("sigaction"); rc = EXIT_FAILURE; goto out; } /* test fault after device-dax instance disabled */ if (sigsetjmp(sj_env, 1)) { /* got sigbus, success */ close(fd); rc = 0; goto out; } rc = EXIT_SUCCESS; p = (int *) (buf + align); *p = 0xff; if (ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { /* after 4.9 this test will properly get sigbus above */ rc = EXIT_FAILURE; fprintf(stderr, "%s: failed to unmap after reset\n", daxctl_dev_get_devname(dev)); } close(fd); out: reset_device_dax(ndns); return rc; } static int test_device_dax(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { unsigned long i, aligns[] = { SZ_4K, SZ_2M, SZ_1G }; int rc; for (i = 0; i < ARRAY_SIZE(aligns); i++) { rc = __test_device_dax(aligns[i], loglevel, test, ctx); if (rc && rc != 77) break; } return rc; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc < 0) return ndctl_test_result(test, rc); rc = test_device_dax(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/dpa-alloc.c000066400000000000000000000205341355562357700154210ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *NFIT_PROVIDER0 = "nfit_test.0"; static const char *NFIT_PROVIDER1 = "nfit_test.1"; #define NUM_NAMESPACES 4 struct test_dpa_namespace { struct ndctl_namespace *ndns; unsigned long long size; uuid_t uuid; } namespaces[NUM_NAMESPACES]; #define MIN_SIZE SZ_4M static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) { unsigned int default_available_slots, available_slots, i; struct ndctl_region *region, *blk_region = NULL; struct ndctl_namespace *ndns; struct ndctl_dimm *dimm; unsigned long size; struct ndctl_bus *bus; char uuid_str[40]; int round; int rc; /* disable nfit_test.1, not used in this test */ bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER1); if (!bus) return -ENXIO; ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); /* init nfit_test.0 */ bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER0); if (!bus) return -ENXIO; ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) { rc = ndctl_dimm_zero_labels(dimm); if (rc < 0) { fprintf(stderr, "failed to zero %s\n", ndctl_dimm_get_devname(dimm)); return rc; } } /* * Find a guineapig BLK region, we know that the dimm with * handle==0 from nfit_test.0 always allocates from highest DPA * to lowest with no excursions into BLK only ranges. */ ndctl_region_foreach(bus, region) { if (ndctl_region_get_type(region) != ND_DEVICE_REGION_BLK) continue; dimm = ndctl_region_get_first_dimm(region); if (!dimm) continue; if (ndctl_dimm_get_handle(dimm) == 0) { blk_region = region; break; } } if (!blk_region || ndctl_region_enable(blk_region) < 0) { fprintf(stderr, "failed to find a usable BLK region\n"); return -ENXIO; } region = blk_region; if (ndctl_region_get_available_size(region) / MIN_SIZE < NUM_NAMESPACES) { fprintf(stderr, "%s insufficient available_size\n", ndctl_region_get_devname(region)); return -ENXIO; } default_available_slots = ndctl_dimm_get_available_labels(dimm); /* grow namespaces */ for (i = 0; i < ARRAY_SIZE(namespaces); i++) { uuid_t uuid; ndns = ndctl_region_get_namespace_seed(region); if (!ndns) { fprintf(stderr, "%s: failed to get seed: %d\n", ndctl_region_get_devname(region), i); return -ENXIO; } uuid_generate_random(uuid); ndctl_namespace_set_uuid(ndns, uuid); ndctl_namespace_set_sector_size(ndns, 512); ndctl_namespace_set_size(ndns, MIN_SIZE); rc = ndctl_namespace_enable(ndns); if (rc) { fprintf(stderr, "failed to enable %s: %d\n", ndctl_namespace_get_devname(ndns), rc); return rc; } ndctl_namespace_disable_invalidate(ndns); rc = ndctl_namespace_set_size(ndns, SZ_4K); if (rc) { fprintf(stderr, "failed to init %s to size: %d\n", ndctl_namespace_get_devname(ndns), SZ_4K); return rc; } namespaces[i].ndns = ndns; ndctl_namespace_get_uuid(ndns, namespaces[i].uuid); } available_slots = ndctl_dimm_get_available_labels(dimm); if (available_slots != default_available_slots - ARRAY_SIZE(namespaces)) { fprintf(stderr, "expected %ld slots available\n", default_available_slots - ARRAY_SIZE(namespaces)); return -ENOSPC; } /* exhaust label space, by round-robin allocating 4K */ round = 1; for (i = 0; i < available_slots; i++) { ndns = namespaces[i % ARRAY_SIZE(namespaces)].ndns; if (i % ARRAY_SIZE(namespaces) == 0) round++; size = SZ_4K * round; rc = ndctl_namespace_set_size(ndns, size); if (rc) { fprintf(stderr, "%s: set_size: %lx failed: %d\n", ndctl_namespace_get_devname(ndns), size, rc); return rc; } } /* * The last namespace we updated should still be modifiable via * the kernel's reserve label */ i--; round++; ndns = namespaces[i % ARRAY_SIZE(namespaces)].ndns; size = SZ_4K * round; rc = ndctl_namespace_set_size(ndns, size); if (rc) { fprintf(stderr, "%s failed to update while labels full\n", ndctl_namespace_get_devname(ndns)); return rc; } round--; size = SZ_4K * round; rc = ndctl_namespace_set_size(ndns, size); if (rc) { fprintf(stderr, "%s failed to reduce size while labels full\n", ndctl_namespace_get_devname(ndns)); return rc; } /* do the allocations survive a region cycle? */ for (i = 0; i < ARRAY_SIZE(namespaces); i++) { ndns = namespaces[i].ndns; namespaces[i].size = ndctl_namespace_get_size(ndns); namespaces[i].ndns = NULL; } ndctl_region_disable_invalidate(region); rc = ndctl_region_enable(region); if (rc) { fprintf(stderr, "failed to re-enable %s: %d\n", ndctl_region_get_devname(region), rc); return rc; } ndctl_namespace_foreach(region, ndns) { uuid_t uuid; ndctl_namespace_get_uuid(ndns, uuid); for (i = 0; i < ARRAY_SIZE(namespaces); i++) { if (uuid_compare(uuid, namespaces[i].uuid) == 0) { namespaces[i].ndns = ndns; break; } } } /* validate that they all came back */ for (i = 0; i < ARRAY_SIZE(namespaces); i++) { ndns = namespaces[i].ndns; size = ndns ? ndctl_namespace_get_size(ndns) : 0; if (ndns && size == namespaces[i].size) continue; uuid_unparse(namespaces[i].uuid, uuid_str); fprintf(stderr, "failed to recover %s\n", uuid_str); return -ENODEV; } /* test deletion and merging */ ndns = namespaces[0].ndns; for (i = 1; i < ARRAY_SIZE(namespaces); i++) { struct ndctl_namespace *victim = namespaces[i].ndns; uuid_unparse(namespaces[i].uuid, uuid_str); size = ndctl_namespace_get_size(victim); rc = ndctl_namespace_disable(victim); if (rc) { fprintf(stderr, "failed to disable %s\n", uuid_str); return rc; } rc = ndctl_namespace_delete(victim); if (rc) { fprintf(stderr, "failed to delete %s\n", uuid_str); return rc; } size += ndctl_namespace_get_size(ndns); rc = ndctl_namespace_set_size(ndns, size); if (rc) { fprintf(stderr, "failed to merge %s\n", uuid_str); return rc; } } /* there can be only one */ i = 0; ndctl_namespace_foreach(region, ndns) { unsigned long long sz = ndctl_namespace_get_size(ndns); if (sz) { i++; if (sz == size) continue; fprintf(stderr, "%s size: %llx expected %lx\n", ndctl_namespace_get_devname(ndns), sz, size); return -ENXIO; } } if (i != 1) { fprintf(stderr, "failed to delete namespaces\n"); return -ENXIO; } available_slots = ndctl_dimm_get_available_labels(dimm); if (available_slots != default_available_slots - 1) { fprintf(stderr, "mishandled slot count\n"); return -ENXIO; } ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); return 0; } int test_dpa_alloc(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { struct kmod_module *mod; struct kmod_ctx *kmod_ctx; int err, result = EXIT_FAILURE; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) return 77; ndctl_set_log_priority(ctx, loglevel); err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); if (err < 0) { ndctl_test_skip(test); fprintf(stderr, "nfit_test unavailable skipping tests\n"); return 77; } err = do_test(ctx, test); if (err == 0) result = EXIT_SUCCESS; kmod_module_remove_module(mod, 0); kmod_unref(kmod_ctx); return result; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_dpa_alloc(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/dsm-fail.c000066400000000000000000000225631355562357700152650ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DIMM_PATH "/sys/devices/platform/nfit_test.0/nfit_test_dimm/test_dimm0" static int reset_bus(struct ndctl_bus *bus) { struct ndctl_region *region; struct ndctl_dimm *dimm; /* disable all regions so that set_config_data commands are permitted */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) { if (!ndctl_dimm_read_labels(dimm)) return -ENXIO; ndctl_dimm_disable(dimm); ndctl_dimm_init_labels(dimm, NDCTL_NS_VERSION_1_2); ndctl_dimm_enable(dimm); } /* set regions back to their default state */ ndctl_region_foreach(bus, region) ndctl_region_enable(region); return 0; } static int set_dimm_response(const char *dimm_path, int cmd, int error_code, struct log_ctx *log_ctx) { char path[1024], buf[SYSFS_ATTR_SIZE]; int rc; if (error_code) { sprintf(path, "%s/fail_cmd", dimm_path); sprintf(buf, "%#x\n", 1 << cmd); rc = __sysfs_write_attr(log_ctx, path, buf); if (rc) goto out; sprintf(path, "%s/fail_cmd_code", dimm_path); sprintf(buf, "%d\n", error_code); rc = __sysfs_write_attr(log_ctx, path, buf); if (rc) goto out; } else { sprintf(path, "%s/fail_cmd", dimm_path); sprintf(buf, "0\n"); rc = __sysfs_write_attr(log_ctx, path, buf); if (rc) goto out; } out: if (rc < 0) fprintf(stderr, "%s failed, cmd: %d code: %d\n", __func__, cmd, error_code); return 0; } static int dimms_disable(struct ndctl_bus *bus) { struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) { int rc = ndctl_dimm_disable(dimm); if (rc) { fprintf(stderr, "dimm: %s failed to disable: %d\n", ndctl_dimm_get_devname(dimm), rc); return rc; } } return 0; } static int test_dimms_enable(struct ndctl_bus *bus, struct ndctl_dimm *victim, bool expect) { struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) { int rc = ndctl_dimm_enable(dimm); if (((expect != (rc == 0)) && (dimm == victim)) || (rc && dimm != victim)) { bool __expect = true; if (dimm == victim) __expect = expect; fprintf(stderr, "fail expected %s enable %s victim: %s rc: %d\n", ndctl_dimm_get_devname(dimm), __expect ? "success" : "failure", ndctl_dimm_get_devname(victim), rc); return -ENXIO; } } return 0; } static int test_regions_enable(struct ndctl_bus *bus, struct ndctl_dimm *victim, struct ndctl_region *victim_region, bool region_expect, int namespace_count) { struct ndctl_region *region; ndctl_region_foreach(bus, region) { struct ndctl_namespace *ndns; struct ndctl_dimm *dimm; bool has_victim = false; int rc, count = 0; ndctl_dimm_foreach_in_region(region, dimm) { if (dimm == victim) { has_victim = true; break; } } rc = ndctl_region_enable(region); fprintf(stderr, "region: %s enable: %d has_victim: %d\n", ndctl_region_get_devname(region), rc, has_victim); if (((region_expect != (rc == 0)) && has_victim) || (rc && !has_victim)) { bool __expect = true; if (has_victim) __expect = region_expect; fprintf(stderr, "%s: fail expected enable: %s with %s\n", ndctl_region_get_devname(region), __expect ? "success" : "failure", ndctl_dimm_get_devname(victim)); return -ENXIO; } if (region != victim_region) continue; ndctl_namespace_foreach(region, ndns) { if (ndctl_namespace_is_enabled(ndns)) { fprintf(stderr, "%s: enabled, expected disabled\n", ndctl_namespace_get_devname(ndns)); return -ENXIO; } fprintf(stderr, "%s: %s: size: %lld\n", __func__, ndctl_namespace_get_devname(ndns), ndctl_namespace_get_size(ndns)); count++; } if (count != namespace_count) { fprintf(stderr, "%s: fail expected %d namespaces got %d\n", ndctl_region_get_devname(region), namespace_count, count); return -ENXIO; } } return 0; } static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) { struct ndctl_bus *bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); struct ndctl_region *region, *victim_region = NULL; struct ndctl_dimm *dimm, *victim = NULL; char path[1024], buf[SYSFS_ATTR_SIZE]; struct log_ctx log_ctx; unsigned int handle; int rc, err = 0; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) return 77; if (!bus) return -ENXIO; log_init(&log_ctx, "test/dsm-fail", "NDCTL_TEST"); if (reset_bus(bus)) { fprintf(stderr, "failed to read labels\n"); return -ENXIO; } sprintf(path, "%s/handle", DIMM_PATH); rc = __sysfs_read_attr(&log_ctx, path, buf); if (rc) { fprintf(stderr, "failed to retrieve test dimm handle\n"); return -ENXIO; } handle = strtoul(buf, NULL, 0); ndctl_dimm_foreach(bus, dimm) if (ndctl_dimm_get_handle(dimm) == handle) { victim = dimm; break; } if (!victim) { fprintf(stderr, "failed to find victim dimm\n"); return -ENXIO; } fprintf(stderr, "victim: %s\n", ndctl_dimm_get_devname(victim)); ndctl_region_foreach(bus, region) { if (ndctl_region_get_type(region) != ND_DEVICE_REGION_PMEM) continue; ndctl_dimm_foreach_in_region(region, dimm) { const char *argv[] = { "__func__", "-v", "-r", ndctl_region_get_devname(region), "-s", "4M", "-m", "raw", }; struct ndctl_namespace *ndns; int count, i; if (dimm != victim) continue; /* * Validate that we only have the one seed * namespace, and then create one so that we can * verify namespace enumeration while locked. */ count = 0; ndctl_namespace_foreach(region, ndns) count++; if (count != 1) { fprintf(stderr, "%s: found %d namespaces expected 1\n", ndctl_region_get_devname(region), count); rc = -ENXIO; goto out; } if (ndctl_region_get_size(region) != ndctl_region_get_available_size(region)) { fprintf(stderr, "%s: expected empty region\n", ndctl_region_get_devname(region)); rc = -ENXIO; goto out; } for (i = 0; i < 2; i++) { builtin_xaction_namespace_reset(); rc = cmd_create_namespace(ARRAY_SIZE(argv), argv, ndctl_region_get_ctx(region)); if (rc) { fprintf(stderr, "%s: failed to create namespace\n", ndctl_region_get_devname(region)); rc = -ENXIO; goto out; } } victim_region = region; } if (victim_region) break; } /* disable all regions so that we can disable a dimm */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); rc = dimms_disable(bus); if (rc) goto out; rc = set_dimm_response(DIMM_PATH, ND_CMD_GET_CONFIG_SIZE, -EACCES, &log_ctx); if (rc) goto out; rc = test_dimms_enable(bus, victim, true); if (rc) goto out; rc = test_regions_enable(bus, victim, victim_region, true, 2); if (rc) goto out; rc = set_dimm_response(DIMM_PATH, ND_CMD_GET_CONFIG_SIZE, 0, &log_ctx); if (rc) goto out; ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); rc = dimms_disable(bus); if (rc) goto out; rc = set_dimm_response(DIMM_PATH, ND_CMD_GET_CONFIG_DATA, -EACCES, &log_ctx); if (rc) goto out; rc = test_dimms_enable(bus, victim, false); if (rc) goto out; rc = test_regions_enable(bus, victim, victim_region, false, 0); if (rc) goto out; rc = set_dimm_response(DIMM_PATH, ND_CMD_GET_CONFIG_DATA, 0, &log_ctx); if (rc) goto out; rc = dimms_disable(bus); if (rc) goto out; out: err = rc; sprintf(path, "%s/fail_cmd", DIMM_PATH); sprintf(buf, "0\n"); rc = __sysfs_write_attr(&log_ctx, path, buf); if (rc) fprintf(stderr, "%s: failed to clear fail_cmd mask\n", ndctl_dimm_get_devname(victim)); rc = ndctl_dimm_enable(victim); if (rc) { fprintf(stderr, "failed to enable victim: %s after clearing error\n", ndctl_dimm_get_devname(victim)); rc = -ENXIO; } reset_bus(bus); if (rc) err = rc; return err; } int test_dsm_fail(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { struct kmod_module *mod; struct kmod_ctx *kmod_ctx; int result = EXIT_FAILURE, err; ndctl_set_log_priority(ctx, loglevel); err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); if (err < 0) { result = 77; ndctl_test_skip(test); fprintf(stderr, "%s unavailable skipping tests\n", "nfit_test"); return result; } result = do_test(ctx, test); kmod_module_remove_module(mod, 0); kmod_unref(kmod_ctx); return result; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_dsm_fail(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/firmware-update.sh000077500000000000000000000013251355562357700170510ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018 Intel Corporation. All rights reserved. rc=77 dev="" image="update-fw.img" . ./common trap 'err $LINENO' ERR reset() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all if [ -f $image ]; then rm -f $image fi } detect() { dev=$($NDCTL list -b $NFIT_TEST_BUS0 -D | jq .[0].dev | tr -d '"') [ -n "$dev" ] || err "$LINENO" } do_tests() { truncate -s 196608 $image $NDCTL update-firmware -f $image $dev } check_min_kver "4.16" || do_skip "may lack firmware update test handling" modprobe nfit_test rc=1 reset rc=2 detect do_tests rm -f $image _cleanup exit 0 ndctl-67/test/hugetlb.c000066400000000000000000000011401355562357700152070ustar00rootroot00000000000000#include #include #include #include #include static int test_hugetlb(void) { int rc, i; unsigned long aligns[] = { SZ_4K, SZ_2M, SZ_1G }; for (i = 0; i < (int) ARRAY_SIZE(aligns); i++) { fprintf(stderr, "%s: page_size: %#lx\n", __func__, aligns[i]); rc = test_dax_directio(-1, aligns[i], NULL, 0); if (rc == -ENOENT && aligns[i] == SZ_1G) continue; /* system not configured for 1G pages */ else if (rc) return 77; } return 0; } int __attribute__((weak)) main(int argc, char *argv[]) { return test_hugetlb(); } ndctl-67/test/inject-error.sh000077500000000000000000000047711355562357700163700ustar00rootroot00000000000000#!/bin/bash -Ex # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. dev="" size="" blockdev="" rc=77 err_block=42 err_count=8 . ./common trap 'err $LINENO' ERR # sample json: #{ # "dev":"namespace7.0", # "mode":"fsdax", # "size":"60.00 MiB (62.92 MB)", # "uuid":"f1baa71a-d165-4da4-bb6a-083a2b0e6469", # "blockdev":"pmem7", #} check_min_kver "4.15" || do_skip "kernel $KVER may not support error injection" create() { json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem --align=4k) rc=2 eval "$(echo "$json" | json2var)" [ -n "$dev" ] || err "$LINENO" [ -n "$size" ] || err "$LINENO" [ -n "$blockdev" ] || err "$LINENO" [ $size -gt 0 ] || err "$LINENO" } reset() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all } check_status() { local sector="$1" local count="$2" json="$($NDCTL inject-error --status $dev)" [[ "$sector" == "$(jq ".badblocks[0].block" <<< "$json")" ]] [[ "$count" == "$(jq ".badblocks[0].count" <<< "$json")" ]] } do_tests() { # inject without notification $NDCTL inject-error --block=$err_block --count=$err_count --no-notify $dev check_status "$err_block" "$err_count" if read -r sector len < /sys/block/$blockdev/badblocks; then # fail if reading badblocks returns data echo "fail: $LINENO" && exit 1 fi # clear via err-inj-clear $NDCTL inject-error --block=$err_block --count=$err_count --uninject $dev check_status # inject normally $NDCTL inject-error --block=$err_block --count=$err_count $dev $NDCTL start-scrub && $NDCTL wait-scrub check_status "$err_block" "$err_count" if read -r sector len < /sys/block/$blockdev/badblocks; then test "$sector" -eq "$err_block" test "$len" -eq "$err_count" fi # clear via write dd if=/dev/zero of=/dev/$blockdev bs=512 count=$err_count seek=$err_block oflag=direct if read -r sector len < /sys/block/$blockdev/badblocks; then # fail if reading badblocks returns data echo "fail: $LINENO" && exit 1 fi check_status } modprobe nfit_test rc=1 reset && create do_tests reset _cleanup exit 0 ndctl-67/test/inject-smart.sh000077500000000000000000000056001355562357700163550ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018 Intel Corporation. All rights reserved. rc=77 . ./common bus="$NFIT_TEST_BUS0" inj_val="42" trap 'err $LINENO' ERR # sample json: # { # "dev":"nmem0", # "id":"cdab-0a-07e0-ffffffff", # "handle":0, # "phys_id":0, # "health":{ # "health_state":"non-critical", # "temperature_celsius":23, # "spares_percentage":75, # "alarm_temperature":true, # "alarm_spares":true, # "temperature_threshold":40, # "spares_threshold":5, # "life_used_percentage":5, # "shutdown_state":"clean" # } #} translate_field() { local in="$1" case $in in media-temperature) echo "temperature_celsius" ;; ctrl-temperature) echo "controller_temperature_celsius" ;; spares) echo "spares_percentage" ;; media-temperature-alarm) echo "alarm_temperature" ;; ctrl-temperature-alarm) echo "alarm_controller_temperature" ;; spares-alarm) echo "alarm_spares" ;; media-temperature-threshold) echo "temperature_threshold" ;; spares-threshold) echo "spares_threshold" ;; unsafe-shutdown) echo "shutdown_state" ;; fatal) echo "health_state" ;; *) # passthrough echo "$in" return ;; esac } translate_val() { local in="$1" case $in in dirty) ;& fatal) ;& true) echo "1" ;; non-critical) ;& clean) ;& false) echo "0" ;; *) # passthrough echo "$in" ;; esac } get_field() { local field="$1" local smart_listing="$(translate_field $field)" json="$($NDCTL list -b $bus -d $dimm -H)" val="$(jq -r ".[].dimms[].health.$smart_listing" <<< $json)" val="$(translate_val $val)" echo $val } verify() { local field="$1" local val="$2" [[ "$val" == "$(get_field $field)" ]] } test_field() { local field="$1" local val="$2" local op="$3" local old_val="" if [ -n "$val" ]; then inj_opt="--${field}=${val}" else inj_opt="--${field}" fi old_val=$(get_field $field) if [[ "$old_val" == "0" || "$old_val" == "1" ]]; then val=$(((old_val + 1) % 2)) fi $NDCTL inject-smart -b $bus $dimm $inj_opt verify $field $val if [[ "$op" != "thresh" ]]; then $NDCTL inject-smart -b $bus --${field}-uninject $dimm verify $field $old_val fi } do_tests() { local fields_val=(media-temperature spares) local fields_bool=(unsafe-shutdown fatal) local fields_thresh=(media-temperature-threshold spares-threshold) local field="" $NDCTL inject-smart -b $bus --uninject-all $dimm # start tests for field in "${fields_val[@]}"; do test_field $field $inj_val done for field in "${fields_bool[@]}"; do test_field $field done for field in "${fields_thresh[@]}"; do test_field $field $inj_val "thresh" done } check_min_kver "4.19" || do_skip "kernel $KVER may not support smart (un)injection" modprobe nfit_test rc=1 jlist=$(./list-smart-dimm -b $bus) dimm="$(jq '.[]."dev"?, ."dev"?' <<< $jlist | sort | head -1 | xargs)" test -n "$dimm" do_tests _cleanup exit 0 ndctl-67/test/label-compat.sh000077500000000000000000000026201355562357700163140ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. set -e rc=77 . ./common check_min_kver "4.11" || do_skip "may not provide reliable isetcookie values" trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all # grab the largest pmem region on -b $NFIT_TEST_BUS0 query=". | sort_by(.available_size) | reverse | .[0].dev" region=$($NDCTL list -b $NFIT_TEST_BUS0 -t pmem -Ri | jq -r "$query") # we assume that $region is comprised of 4 dimms query=". | .regions[0].mappings | sort_by(.dimm) | .[].dimm" dimms=$($NDCTL list -DRi -r $region | jq -r "$query" | xargs) i=1 for d in $dimms do $NDCTL write-labels $d -i nmem${i}.bin i=$((i+1)) done $NDCTL enable-region -b $NFIT_TEST_BUS0 all len=$($NDCTL list -r $region -N | jq -r "length") if [ -z $len ]; then rc=1 echo "failed to find legacy isetcookie namespace" exit 1 fi _cleanup exit 0 ndctl-67/test/libndctl.c000066400000000000000000002142051355562357700153600ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */ #define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */ /* * Kernel provider "nfit_test.0" produces an NFIT with the following attributes: * * (a) (b) DIMM BLK-REGION * +-------------------+--------+--------+--------+ * +------+ | pm0.0 | blk2.0 | pm1.0 | blk2.1 | 0 region2 * | imc0 +--+- - - region0- - - +--------+ +--------+ * +--+---+ | pm0.0 | blk3.0 | pm1.0 | blk3.1 | 1 region3 * | +-------------------+--------v v--------+ * +--+---+ | | * | cpu0 | region1 * +--+---+ | | * | +----------------------------^ ^--------+ * +--+---+ | blk4.0 | pm1.0 | blk4.0 | 2 region4 * | imc1 +--+----------------------------| +--------+ * +------+ | blk5.0 | pm1.0 | blk5.0 | 3 region5 * +----------------------------+--------+--------+ * * *) In this layout we have four dimms and two memory controllers in one * socket. Each unique interface ("blk" or "pmem") to DPA space * is identified by a region device with a dynamically assigned id. * * *) The first portion of dimm0 and dimm1 are interleaved as REGION0. * A single "pmem" namespace is created in the REGION0-"spa"-range * that spans dimm0 and dimm1 with a user-specified name of "pm0.0". * Some of that interleaved "spa" range is reclaimed as "bdw" * accessed space starting at offset (a) into each dimm. In that * reclaimed space we create two "bdw" "namespaces" from REGION2 and * REGION3 where "blk2.0" and "blk3.0" are just human readable names * that could be set to any user-desired name in the label. * * *) In the last portion of dimm0 and dimm1 we have an interleaved * "spa" range, REGION1, that spans those two dimms as well as dimm2 * and dimm3. Some of REGION1 allocated to a "pmem" namespace named * "pm1.0" the rest is reclaimed in 4 "bdw" namespaces (for each * dimm in the interleave set), "blk2.1", "blk3.1", "blk4.0", and * "blk5.0". * * *) The portion of dimm2 and dimm3 that do not participate in the * REGION1 interleaved "spa" range (i.e. the DPA address below * offset (b) are also included in the "blk4.0" and "blk5.0" * namespaces. Note, that this example shows that "bdw" namespaces * don't need to be contiguous in DPA-space. * * Kernel provider "nfit_test.1" produces an NFIT with the following attributes: * * region2 * +---------------------+ * |---------------------| * || pm2.0 || * |---------------------| * +---------------------+ * * *) Describes a simple system-physical-address range with a non-aliasing backing * dimm. */ static const char *NFIT_PROVIDER0 = "nfit_test.0"; static const char *NFIT_PROVIDER1 = "nfit_test.1"; #define SZ_4K 0x00001000 #define SZ_128K 0x00020000 #define SZ_7M 0x00700000 #define SZ_2M 0x00200000 #define SZ_8M 0x00800000 #define SZ_11M 0x00b00000 #define SZ_12M 0x00c00000 #define SZ_16M 0x01000000 #define SZ_18M 0x01200000 #define SZ_20M 0x01400000 #define SZ_27M 0x01b00000 #define SZ_28M 0x01c00000 #define SZ_32M 0x02000000 #define SZ_64M 0x04000000 #define SZ_1G 0x40000000 struct dimm { unsigned int handle; unsigned int phys_id; unsigned int subsystem_vendor; unsigned short manufacturing_date; unsigned char manufacturing_location; long long dirty_shutdown; union { unsigned long flags; struct { unsigned int f_arm:1; unsigned int f_save:1; unsigned int f_flush:1; unsigned int f_smart:1; unsigned int f_restore:1; }; }; int formats; int format[2]; }; #define DIMM_HANDLE(n, s, i, c, d) \ (((n & 0xfff) << 16) | ((s & 0xf) << 12) | ((i & 0xf) << 8) \ | ((c & 0xf) << 4) | (d & 0xf)) static struct dimm dimms0[] = { { DIMM_HANDLE(0, 0, 0, 0, 0), 0, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, { DIMM_HANDLE(0, 0, 0, 0, 1), 1, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, { DIMM_HANDLE(0, 0, 1, 0, 0), 2, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, { DIMM_HANDLE(0, 0, 1, 0, 1), 3, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, }; static struct dimm dimms1[] = { { DIMM_HANDLE(0, 0, 0, 0, 0), 0, 0, 2016, 10, 42, { .f_arm = 1, .f_save = 1, .f_flush = 1, .f_smart = 1, .f_restore = 1, }, 1, { 0x101, }, }, }; static struct btt { int enabled; uuid_t uuid; int num_sector_sizes; unsigned int sector_sizes[7]; } default_btt = { 0, { 0, }, 7, { 512, 520, 528, 4096, 4104, 4160, 4224, }, }; struct pfn { int enabled; uuid_t uuid; enum ndctl_pfn_loc locs[2]; unsigned long aligns[4]; }; struct dax { int enabled; uuid_t uuid; enum ndctl_pfn_loc locs[2]; unsigned long aligns[4]; }; static struct pfn_default { int enabled; uuid_t uuid; enum ndctl_pfn_loc loc; unsigned long align; } default_pfn = { .enabled = 0, .uuid = { 0, }, .loc = NDCTL_PFN_LOC_NONE, .align = SZ_2M, }; struct region { union { unsigned int range_index; unsigned int handle; }; unsigned int interleave_ways; int enabled; char *type; unsigned long long available_size; unsigned long long size; struct set { int active; } iset; struct btt *btts[2]; struct pfn_default *pfns[2]; struct namespace *namespaces[4]; }; static struct btt btt_settings = { .enabled = 1, .uuid = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, .num_sector_sizes = 7, .sector_sizes = { 512, 520, 528, 4096, 4104, 4160, 4224, }, }; static struct pfn pfn_settings = { .enabled = 1, .uuid = { 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15 }, .locs = { NDCTL_PFN_LOC_RAM, NDCTL_PFN_LOC_PMEM }, }; static struct dax dax_settings = { .enabled = 1, .uuid = { 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15 }, .locs = { NDCTL_PFN_LOC_RAM, NDCTL_PFN_LOC_PMEM }, }; struct namespace { unsigned int id; char *type; struct btt *btt_settings; struct pfn *pfn_settings; struct dax *dax_settings; unsigned long long size; uuid_t uuid; int do_configure; int check_alt_name; int ro; int num_sector_sizes; unsigned long *sector_sizes; }; static uuid_t null_uuid; static unsigned long blk_sector_sizes[] = { 512, 520, 528, 4096, 4104, 4160, 4224, }; static unsigned long pmem_sector_sizes[] = { 512, 4096 }; static unsigned long io_sector_sizes[] = { 0 }; static struct namespace namespace0_pmem0 = { 0, "namespace_pmem", &btt_settings, &pfn_settings, &dax_settings, SZ_18M, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 1, 1, 0, ARRAY_SIZE(pmem_sector_sizes), pmem_sector_sizes, }; static struct namespace namespace1_pmem0 = { 0, "namespace_pmem", &btt_settings, &pfn_settings, &dax_settings, SZ_20M, { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }, 1, 1, 0, ARRAY_SIZE(pmem_sector_sizes), pmem_sector_sizes, }; static struct namespace namespace2_blk0 = { 0, "namespace_blk", NULL, NULL, NULL, SZ_7M, { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }, 1, 1, 0, ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, }; static struct namespace namespace2_blk1 = { 1, "namespace_blk", NULL, NULL, NULL, SZ_11M, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, }, 1, 1, 0, ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, }; static struct namespace namespace3_blk0 = { 0, "namespace_blk", NULL, NULL, NULL, SZ_7M, { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, }, 1, 1, 0, ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, }; static struct namespace namespace3_blk1 = { 1, "namespace_blk", NULL, NULL, NULL, SZ_11M, { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, }, 1, 1, 0, ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, }; static struct namespace namespace4_blk0 = { 0, "namespace_blk", &btt_settings, NULL, NULL, SZ_27M, { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, }, 1, 1, 0, ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, }; static struct namespace namespace5_blk0 = { 0, "namespace_blk", &btt_settings, NULL, NULL, SZ_27M, { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }, 1, 1, 0, ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, }; static struct region regions0[] = { { { 1 }, 2, 1, "pmem", SZ_32M, SZ_32M, { 1 }, .namespaces = { [0] = &namespace0_pmem0, }, .btts = { [0] = &default_btt, }, .pfns = { [0] = &default_pfn, }, }, { { 2 }, 4, 1, "pmem", SZ_64M, SZ_64M, { 1 }, .namespaces = { [0] = &namespace1_pmem0, }, .btts = { [0] = &default_btt, }, .pfns = { [0] = &default_pfn, }, }, { { DIMM_HANDLE(0, 0, 0, 0, 0) }, 1, 1, "blk", SZ_18M, SZ_32M, .namespaces = { [0] = &namespace2_blk0, [1] = &namespace2_blk1, }, .btts = { [0] = &default_btt, }, }, { { DIMM_HANDLE(0, 0, 0, 0, 1) }, 1, 1, "blk", SZ_18M, SZ_32M, .namespaces = { [0] = &namespace3_blk0, [1] = &namespace3_blk1, }, .btts = { [0] = &default_btt, }, }, { { DIMM_HANDLE(0, 0, 1, 0, 0) }, 1, 1, "blk", SZ_27M, SZ_32M, .namespaces = { [0] = &namespace4_blk0, }, .btts = { [0] = &default_btt, }, }, { { DIMM_HANDLE(0, 0, 1, 0, 1) }, 1, 1, "blk", SZ_27M, SZ_32M, .namespaces = { [0] = &namespace5_blk0, }, .btts = { [0] = &default_btt, }, }, }; static struct namespace namespace1 = { 0, "namespace_io", &btt_settings, &pfn_settings, &dax_settings, SZ_32M, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, -1, 0, 1, ARRAY_SIZE(io_sector_sizes), io_sector_sizes, }; static struct region regions1[] = { { { 1 }, 1, 1, "pmem", 0, SZ_32M, .namespaces = { [0] = &namespace1, }, }, }; static unsigned long dimm_commands0 = 1UL << ND_CMD_GET_CONFIG_SIZE | 1UL << ND_CMD_GET_CONFIG_DATA | 1UL << ND_CMD_SET_CONFIG_DATA | 1UL << ND_CMD_SMART | 1UL << ND_CMD_SMART_THRESHOLD; #define CLEAR_ERROR_CMDS (1UL << ND_CMD_CLEAR_ERROR) #define ARS_CMDS (1UL << ND_CMD_ARS_CAP | 1UL << ND_CMD_ARS_START \ | 1UL << ND_CMD_ARS_STATUS) static unsigned long bus_commands0 = CLEAR_ERROR_CMDS | ARS_CMDS; static struct ndctl_dimm *get_dimm_by_handle(struct ndctl_bus *bus, unsigned int handle) { struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) if (ndctl_dimm_get_handle(dimm) == handle) return dimm; return NULL; } static struct ndctl_btt *get_idle_btt(struct ndctl_region *region) { struct ndctl_btt *btt; ndctl_btt_foreach(region, btt) if (!ndctl_btt_is_enabled(btt) && !ndctl_btt_is_configured(btt)) return btt; return NULL; } static struct ndctl_pfn *get_idle_pfn(struct ndctl_region *region) { struct ndctl_pfn *pfn; ndctl_pfn_foreach(region, pfn) if (!ndctl_pfn_is_enabled(pfn) && !ndctl_pfn_is_configured(pfn)) return pfn; return NULL; } static struct ndctl_dax *get_idle_dax(struct ndctl_region *region) { struct ndctl_dax *dax; ndctl_dax_foreach(region, dax) if (!ndctl_dax_is_enabled(dax) && !ndctl_dax_is_configured(dax)) return dax; return NULL; } static struct ndctl_namespace *get_namespace_by_id(struct ndctl_region *region, struct namespace *namespace) { struct ndctl_namespace *ndns; if (memcmp(namespace->uuid, null_uuid, sizeof(uuid_t)) != 0) ndctl_namespace_foreach(region, ndns) { uuid_t ndns_uuid; int cmp; ndctl_namespace_get_uuid(ndns, ndns_uuid); cmp = memcmp(ndns_uuid, namespace->uuid, sizeof(uuid_t)); if (cmp == 0) return ndns; } /* fall back to nominal id if uuid is not configured yet */ ndctl_namespace_foreach(region, ndns) if (ndctl_namespace_get_id(ndns) == namespace->id) return ndns; return NULL; } static struct ndctl_region *get_pmem_region_by_range_index(struct ndctl_bus *bus, unsigned int range_index) { struct ndctl_region *region; ndctl_region_foreach(bus, region) { if (ndctl_region_get_type(region) != ND_DEVICE_REGION_PMEM) continue; if (ndctl_region_get_range_index(region) == range_index) return region; } return NULL; } static struct ndctl_region *get_blk_region_by_dimm_handle(struct ndctl_bus *bus, unsigned int handle) { struct ndctl_region *region; ndctl_region_foreach(bus, region) { struct ndctl_mapping *map; if (ndctl_region_get_type(region) != ND_DEVICE_REGION_BLK) continue; ndctl_mapping_foreach(region, map) { struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(map); if (ndctl_dimm_get_handle(dimm) == handle) return region; } } return NULL; } enum ns_mode { BTT, PFN, DAX, }; static int check_namespaces(struct ndctl_region *region, struct namespace **namespaces, enum ns_mode mode); static int check_btts(struct ndctl_region *region, struct btt **btts); static int check_regions(struct ndctl_bus *bus, struct region *regions, int n, enum ns_mode mode) { struct ndctl_region *region; int i, rc = 0; for (i = 0; i < n; i++) { struct ndctl_interleave_set *iset; char devname[50]; if (strcmp(regions[i].type, "pmem") == 0) region = get_pmem_region_by_range_index(bus, regions[i].range_index); else region = get_blk_region_by_dimm_handle(bus, regions[i].handle); if (!region) { fprintf(stderr, "failed to find region type: %s ident: %x\n", regions[i].type, regions[i].handle); return -ENXIO; } snprintf(devname, sizeof(devname), "region%d", ndctl_region_get_id(region)); if (strcmp(ndctl_region_get_type_name(region), regions[i].type) != 0) { fprintf(stderr, "%s: expected type: %s got: %s\n", devname, regions[i].type, ndctl_region_get_type_name(region)); return -ENXIO; } if (ndctl_region_get_interleave_ways(region) != regions[i].interleave_ways) { fprintf(stderr, "%s: expected interleave_ways: %d got: %d\n", devname, regions[i].interleave_ways, ndctl_region_get_interleave_ways(region)); return -ENXIO; } if (regions[i].enabled && !ndctl_region_is_enabled(region)) { fprintf(stderr, "%s: expected enabled by default\n", devname); return -ENXIO; } if (regions[i].available_size != ndctl_region_get_available_size(region)) { fprintf(stderr, "%s: expected available_size: %#llx got: %#llx\n", devname, regions[i].available_size, ndctl_region_get_available_size(region)); return -ENXIO; } if (regions[i].size != ndctl_region_get_size(region)) { fprintf(stderr, "%s: expected size: %#llx got: %#llx\n", devname, regions[i].size, ndctl_region_get_size(region)); return -ENXIO; } iset = ndctl_region_get_interleave_set(region); if (regions[i].iset.active && !(iset && ndctl_interleave_set_is_active(iset) > 0)) { fprintf(stderr, "%s: expected interleave set active by default\n", devname); return -ENXIO; } else if (regions[i].iset.active == 0 && iset) { fprintf(stderr, "%s: expected no interleave set\n", devname); return -ENXIO; } if (ndctl_region_disable_invalidate(region) < 0) { fprintf(stderr, "%s: failed to disable\n", devname); return -ENXIO; } if (regions[i].enabled && ndctl_region_enable(region) < 0) { fprintf(stderr, "%s: failed to enable\n", devname); return -ENXIO; } rc = check_btts(region, regions[i].btts); if (rc) return rc; if (regions[i].namespaces[0]) rc = check_namespaces(region, regions[i].namespaces, mode); if (rc) break; } if (rc == 0) ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); return rc; } static int validate_dax(struct ndctl_dax *dax) { /* TODO: make nfit_test namespaces dax capable */ struct ndctl_namespace *ndns = ndctl_dax_get_namespace(dax); const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_region *region = ndctl_dax_get_region(dax); struct ndctl_ctx *ctx = ndctl_dax_get_ctx(dax); struct ndctl_test *test = ndctl_get_private_data(ctx); struct daxctl_region *dax_region = NULL, *found; int rc = -ENXIO, fd, count, dax_expect; struct daxctl_dev *dax_dev, *seed; struct daxctl_ctx *dax_ctx; uuid_t uuid, region_uuid; char devpath[50]; dax_region = ndctl_dax_get_daxctl_region(dax); if (!dax_region) { fprintf(stderr, "%s: failed to retrieve daxctl_region\n", devname); return -ENXIO; } dax_ctx = ndctl_get_daxctl_ctx(ctx); count = 0; daxctl_region_foreach(dax_ctx, found) if (found == dax_region) count++; if (count != 1) { fprintf(stderr, "%s: failed to iterate to single region instance\n", devname); return -ENXIO; } if (ndctl_test_attempt(test, KERNEL_VERSION(4, 10, 0))) { if (daxctl_region_get_size(dax_region) != ndctl_dax_get_size(dax)) { fprintf(stderr, "%s: expect size: %llu != %llu\n", devname, ndctl_dax_get_size(dax), daxctl_region_get_size(dax_region)); return -ENXIO; } if (daxctl_region_get_align(dax_region) != ndctl_dax_get_align(dax)) { fprintf(stderr, "%s: expect align: %lu != %lu\n", devname, ndctl_dax_get_align(dax), daxctl_region_get_align(dax_region)); return -ENXIO; } } rc = -ENXIO; ndctl_dax_get_uuid(dax, uuid); daxctl_region_get_uuid(dax_region, region_uuid); if (uuid_compare(uuid, region_uuid) != 0) { char expect[40], actual[40]; uuid_unparse(region_uuid, actual); uuid_unparse(uuid, expect); fprintf(stderr, "%s: expected uuid: %s got: %s\n", devname, expect, actual); goto out; } if ((int) ndctl_region_get_id(region) != daxctl_region_get_id(dax_region)) { fprintf(stderr, "%s: expected region id: %d got: %d\n", devname, ndctl_region_get_id(region), daxctl_region_get_id(dax_region)); goto out; } dax_dev = daxctl_dev_get_first(dax_region); if (!dax_dev) { fprintf(stderr, "%s: failed to find daxctl_dev\n", devname); goto out; } seed = daxctl_region_get_dev_seed(dax_region); if (dax_dev != seed && daxctl_dev_get_size(dax_dev) <= 0) { fprintf(stderr, "%s: expected non-zero sized dax device\n", devname); goto out; } sprintf(devpath, "/dev/%s", daxctl_dev_get_devname(dax_dev)); fd = open(devpath, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: failed to open %s\n", devname, devpath); goto out; } close(fd); count = 0; daxctl_dev_foreach(dax_region, dax_dev) count++; dax_expect = seed ? 2 : 1; if (count != dax_expect) { fprintf(stderr, "%s: expected %d dax device%s, got %d\n", devname, dax_expect, dax_expect == 1 ? "" : "s", count); rc = -ENXIO; goto out; } rc = 0; out: daxctl_region_unref(dax_region); return rc; } static int __check_dax_create(struct ndctl_region *region, struct ndctl_namespace *ndns, struct namespace *namespace, enum ndctl_pfn_loc loc, uuid_t uuid) { struct ndctl_dax *dax_seed = ndctl_region_get_dax_seed(region); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); struct ndctl_test *test = ndctl_get_private_data(ctx); enum ndctl_namespace_mode mode; struct ndctl_dax *dax; const char *devname; ssize_t rc; dax = get_idle_dax(region); if (!dax) return -ENXIO; devname = ndctl_dax_get_devname(dax); ndctl_dax_set_uuid(dax, uuid); ndctl_dax_set_location(dax, loc); /* * nfit_test uses vmalloc()'d resources so the only feasible * alignment is PAGE_SIZE */ ndctl_dax_set_align(dax, SZ_4K); rc = ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_DAX); if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0)) && rc < 0) { fprintf(stderr, "%s: failed to enforce dax mode\n", devname); return rc; } ndctl_dax_set_namespace(dax, ndns); rc = ndctl_dax_enable(dax); if (rc) { fprintf(stderr, "%s: failed to enable dax\n", devname); return rc; } mode = ndctl_namespace_get_mode(ndns); if (mode >= 0 && mode != NDCTL_NS_MODE_DAX) fprintf(stderr, "%s: expected dax mode got: %d\n", devname, mode); if (namespace->ro == (rc == 0)) { fprintf(stderr, "%s: expected dax enable %s, %s read-%s\n", devname, namespace->ro ? "failure" : "success", ndctl_region_get_devname(region), namespace->ro ? "only" : "write"); return -ENXIO; } if (dax_seed == ndctl_region_get_dax_seed(region) && dax == dax_seed) { fprintf(stderr, "%s: failed to advance dax seed\n", ndctl_region_get_devname(region)); return -ENXIO; } if (namespace->ro) { ndctl_region_set_ro(region, 0); rc = ndctl_dax_enable(dax); fprintf(stderr, "%s: failed to enable after setting rw\n", devname); ndctl_region_set_ro(region, 1); return -ENXIO; } rc = validate_dax(dax); if (rc) { fprintf(stderr, "%s: %s validate_dax failed\n", __func__, devname); return rc; } if (namespace->ro) ndctl_region_set_ro(region, 1); rc = ndctl_dax_delete(dax); if (rc) fprintf(stderr, "%s: failed to delete dax (%zd)\n", devname, rc); return rc; } static int check_dax_create(struct ndctl_region *region, struct ndctl_namespace *ndns, struct namespace *namespace) { struct dax *dax_s = namespace->dax_settings; void *buf = NULL; unsigned int i; int rc = 0; if (!dax_s) return 0; for (i = 0; i < ARRAY_SIZE(dax_s->locs); i++) { /* * The kernel enforces invalidating the previous info * block when the current uuid is does not validate with * the contents of the info block. */ dax_s->uuid[0]++; rc = __check_dax_create(region, ndns, namespace, dax_s->locs[i], dax_s->uuid); if (rc) break; } free(buf); return rc; } static int __check_pfn_create(struct ndctl_region *region, struct ndctl_namespace *ndns, struct namespace *namespace, void *buf, enum ndctl_pfn_loc loc, uuid_t uuid) { struct ndctl_pfn *pfn_seed = ndctl_region_get_pfn_seed(region); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); struct ndctl_test *test = ndctl_get_private_data(ctx); enum ndctl_namespace_mode mode; struct ndctl_pfn *pfn; const char *devname; int fd, retry = 10; char bdevpath[50]; ssize_t rc; pfn = get_idle_pfn(region); if (!pfn) return -ENXIO; devname = ndctl_pfn_get_devname(pfn); ndctl_pfn_set_uuid(pfn, uuid); ndctl_pfn_set_location(pfn, loc); /* * nfit_test uses vmalloc()'d resources so the only feasible * alignment is PAGE_SIZE */ ndctl_pfn_set_align(pfn, SZ_4K); rc = ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_MEMORY); if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0)) && rc < 0) { fprintf(stderr, "%s: failed to enforce pfn mode\n", devname); return rc; } ndctl_pfn_set_namespace(pfn, ndns); rc = ndctl_pfn_enable(pfn); if (rc) { fprintf(stderr, "%s: failed to enable pfn\n", devname); return rc; } mode = ndctl_namespace_get_mode(ndns); if (mode >= 0 && mode != NDCTL_NS_MODE_MEMORY) fprintf(stderr, "%s: expected fsdax mode got: %d\n", devname, mode); if (namespace->ro == (rc == 0)) { fprintf(stderr, "%s: expected pfn enable %s, %s read-%s\n", devname, namespace->ro ? "failure" : "success", ndctl_region_get_devname(region), namespace->ro ? "only" : "write"); return -ENXIO; } if (pfn_seed == ndctl_region_get_pfn_seed(region) && pfn == pfn_seed) { fprintf(stderr, "%s: failed to advance pfn seed\n", ndctl_region_get_devname(region)); return -ENXIO; } if (namespace->ro) { ndctl_region_set_ro(region, 0); rc = ndctl_pfn_enable(pfn); fprintf(stderr, "%s: failed to enable after setting rw\n", devname); ndctl_region_set_ro(region, 1); return -ENXIO; } sprintf(bdevpath, "/dev/%s", ndctl_pfn_get_block_device(pfn)); rc = -ENXIO; fd = open(bdevpath, O_RDWR|O_DIRECT); if (fd < 0) fprintf(stderr, "%s: failed to open %s\n", devname, bdevpath); while (fd >= 0) { rc = pread(fd, buf, 4096, 0); if (rc < 4096) { /* TODO: track down how this happens! */ if (errno == ENOENT && retry--) { usleep(5000); continue; } fprintf(stderr, "%s: failed to read %s: %d %zd (%s)\n", devname, bdevpath, -errno, rc, strerror(errno)); rc = -ENXIO; break; } if (write(fd, buf, 4096) < 4096) { fprintf(stderr, "%s: failed to write %s\n", devname, bdevpath); rc = -ENXIO; break; } rc = 0; break; } if (namespace->ro) ndctl_region_set_ro(region, 1); if (fd >= 0) close(fd); if (rc) return rc; rc = ndctl_pfn_delete(pfn); if (rc) fprintf(stderr, "%s: failed to delete pfn (%zd)\n", devname, rc); return rc; } static int check_pfn_create(struct ndctl_region *region, struct ndctl_namespace *ndns, struct namespace *namespace) { struct pfn *pfn_s = namespace->pfn_settings; void *buf = NULL; unsigned int i; int rc = 0; if (!pfn_s) return 0; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENXIO; for (i = 0; i < ARRAY_SIZE(pfn_s->locs); i++) { /* * The kernel enforces invalidating the previous info * block when the current uuid is does not validate with * the contents of the info block. */ pfn_s->uuid[0]++; rc = __check_pfn_create(region, ndns, namespace, buf, pfn_s->locs[i], pfn_s->uuid); if (rc) break; } free(buf); return rc; } static int check_btt_size(struct ndctl_btt *btt) { struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); struct ndctl_test *test = ndctl_get_private_data(ctx); struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); unsigned long long ns_size = ndctl_namespace_get_size(ndns); unsigned long sect_size = ndctl_btt_get_sector_size(btt); unsigned long long actual, expect; int size_select, sect_select; unsigned long long expect_table[][2] = { [0] = { [0] = 0x11b5400, [1] = 0x8daa000, }, [1] = { [0] = 0x13b1400, [1] = 0x9d8a000, }, [2] = { [0] = 0x1aa3600, [1] = 0xd51b000, }, }; if (sect_size >= SZ_4K) sect_select = 1; else if (sect_size >= 512) sect_select = 0; else { fprintf(stderr, "%s: %s unexpected sector size: %lx\n", __func__, ndctl_btt_get_devname(btt), sect_size); return -ENXIO; } switch (ns_size) { case SZ_18M: size_select = 0; break; case SZ_20M: size_select = 1; break; case SZ_27M: size_select = 2; break; default: fprintf(stderr, "%s: %s unexpected namespace size: %llx\n", __func__, ndctl_namespace_get_devname(ndns), ns_size); return -ENXIO; } /* prior to 4.8 btt devices did not have a size attribute */ if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 8, 0))) return 0; expect = expect_table[size_select][sect_select]; actual = ndctl_btt_get_size(btt); if (expect != actual) { fprintf(stderr, "%s: namespace: %s unexpected size: %llx (expected: %llx)\n", ndctl_btt_get_devname(btt), ndctl_namespace_get_devname(ndns), actual, expect); return -ENXIO; } return 0; } static int check_btt_create(struct ndctl_region *region, struct ndctl_namespace *ndns, struct namespace *namespace) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); struct ndctl_test *test = ndctl_get_private_data(ctx); struct btt *btt_s = namespace->btt_settings; int i, fd, retry = 10; struct ndctl_btt *btt; const char *devname; char bdevpath[50]; void *buf = NULL; ssize_t rc = 0; if (!namespace->btt_settings) return 0; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENXIO; for (i = 0; i < btt_s->num_sector_sizes; i++) { struct ndctl_namespace *ns_seed = ndctl_region_get_namespace_seed(region); struct ndctl_btt *btt_seed = ndctl_region_get_btt_seed(region); enum ndctl_namespace_mode mode; btt = get_idle_btt(region); if (!btt) goto err; devname = ndctl_btt_get_devname(btt); ndctl_btt_set_uuid(btt, btt_s->uuid); ndctl_btt_set_sector_size(btt, btt_s->sector_sizes[i]); rc = ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_SAFE); if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0)) && rc < 0) { fprintf(stderr, "%s: failed to enforce btt mode\n", devname); goto err; } ndctl_btt_set_namespace(btt, ndns); rc = ndctl_btt_enable(btt); if (namespace->ro == (rc == 0)) { fprintf(stderr, "%s: expected btt enable %s, %s read-%s\n", devname, namespace->ro ? "failure" : "success", ndctl_region_get_devname(region), namespace->ro ? "only" : "write"); goto err; } /* prior to v4.5 the mode attribute did not exist */ if (ndctl_test_attempt(test, KERNEL_VERSION(4, 5, 0))) { mode = ndctl_namespace_get_mode(ndns); if (mode >= 0 && mode != NDCTL_NS_MODE_SAFE) fprintf(stderr, "%s: expected safe mode got: %d\n", devname, mode); } /* prior to v4.13 the expected sizes were different due to BTT1.1 */ if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0))) { rc = check_btt_size(btt); if (rc) goto err; } if (btt_seed == ndctl_region_get_btt_seed(region) && btt == btt_seed) { fprintf(stderr, "%s: failed to advance btt seed\n", ndctl_region_get_devname(region)); goto err; } /* check new seed creation for BLK regions */ if (ndctl_region_get_type(region) == ND_DEVICE_REGION_BLK) { if (ns_seed == ndctl_region_get_namespace_seed(region) && ndns == ns_seed) { fprintf(stderr, "%s: failed to advance namespace seed\n", ndctl_region_get_devname(region)); goto err; } } if (namespace->ro) { ndctl_region_set_ro(region, 0); rc = ndctl_btt_enable(btt); fprintf(stderr, "%s: failed to enable after setting rw\n", devname); ndctl_region_set_ro(region, 1); goto err; } sprintf(bdevpath, "/dev/%s", ndctl_btt_get_block_device(btt)); rc = -ENXIO; fd = open(bdevpath, O_RDWR|O_DIRECT); if (fd < 0) fprintf(stderr, "%s: failed to open %s\n", devname, bdevpath); while (fd >= 0) { rc = pread(fd, buf, 4096, 0); if (rc < 4096) { /* TODO: track down how this happens! */ if (errno == ENOENT && retry--) { usleep(5000); continue; } fprintf(stderr, "%s: failed to read %s: %d %zd (%s)\n", devname, bdevpath, -errno, rc, strerror(errno)); rc = -ENXIO; break; } if (write(fd, buf, 4096) < 4096) { fprintf(stderr, "%s: failed to write %s\n", devname, bdevpath); rc = -ENXIO; break; } rc = 0; break; } if (namespace->ro) ndctl_region_set_ro(region, 1); if (fd >= 0) close(fd); if (rc) break; rc = ndctl_btt_delete(btt); if (rc) fprintf(stderr, "%s: failed to delete btt (%zd)\n", devname, rc); } free(buf); return rc; err: free(buf); return -ENXIO; } static int configure_namespace(struct ndctl_region *region, struct ndctl_namespace *ndns, struct namespace *namespace, unsigned long lbasize, enum ns_mode mode) { char devname[50]; int rc; if (namespace->do_configure <= 0) return 0; snprintf(devname, sizeof(devname), "namespace%d.%d", ndctl_region_get_id(region), namespace->id); if (!ndctl_namespace_is_configured(ndns)) { rc = ndctl_namespace_set_uuid(ndns, namespace->uuid); if (rc) fprintf(stderr, "%s: set_uuid failed: %d\n", devname, rc); rc = ndctl_namespace_set_alt_name(ndns, devname); if (rc) fprintf(stderr, "%s: set_alt_name failed: %d\n", devname, rc); rc = ndctl_namespace_set_size(ndns, namespace->size); if (rc) fprintf(stderr, "%s: set_size failed: %d\n", devname, rc); } if (lbasize) { rc = ndctl_namespace_set_sector_size(ndns, lbasize); if (rc) fprintf(stderr, "%s: set_sector_size (%lu) failed: %d\n", devname, lbasize, rc); } rc = ndctl_namespace_is_configured(ndns); if (rc < 1) fprintf(stderr, "%s: is_configured: %d\n", devname, rc); if (mode == BTT) { rc = check_btt_create(region, ndns, namespace); if (rc < 0) { fprintf(stderr, "%s: failed to create btt\n", devname); return rc; } } if (mode == PFN) { rc = check_pfn_create(region, ndns, namespace); if (rc < 0) { fprintf(stderr, "%s: failed to create pfn\n", devname); return rc; } } if (mode == DAX) { rc = check_dax_create(region, ndns, namespace); if (rc < 0) { fprintf(stderr, "%s: failed to create dax\n", devname); return rc; } } rc = ndctl_namespace_enable(ndns); if (rc < 0) fprintf(stderr, "%s: enable: %d\n", devname, rc); return rc; } static int check_pfn_autodetect(struct ndctl_bus *bus, struct ndctl_namespace *ndns, void *buf, struct namespace *namespace) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_test *test = ndctl_get_private_data(ctx); struct pfn *auto_pfn = namespace->pfn_settings; struct ndctl_pfn *pfn, *found = NULL; enum ndctl_namespace_mode mode; ssize_t rc = -ENXIO; char bdev[50]; int fd, ro; ndctl_pfn_foreach(region, pfn) { struct ndctl_namespace *pfn_ndns; uuid_t uu; ndctl_pfn_get_uuid(pfn, uu); if (uuid_compare(uu, auto_pfn->uuid) != 0) continue; if (!ndctl_pfn_is_enabled(pfn)) continue; pfn_ndns = ndctl_pfn_get_namespace(pfn); if (strcmp(ndctl_namespace_get_devname(pfn_ndns), devname) != 0) continue; fprintf(stderr, "%s: pfn_ndns: %p ndns: %p\n", __func__, pfn_ndns, ndns); found = pfn; break; } if (!found) return -ENXIO; mode = ndctl_namespace_get_enforce_mode(ndns); if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0)) && mode != NDCTL_NS_MODE_MEMORY) { fprintf(stderr, "%s expected enforce_mode pfn\n", devname); return -ENXIO; } sprintf(bdev, "/dev/%s", ndctl_pfn_get_block_device(pfn)); fd = open(bdev, O_RDONLY); if (fd < 0) return -ENXIO; rc = ioctl(fd, BLKROGET, &ro); if (rc < 0) { fprintf(stderr, "%s: failed to open %s\n", __func__, bdev); rc = -ENXIO; goto out; } close(fd); fd = -1; rc = -ENXIO; if (ro != namespace->ro) { fprintf(stderr, "%s: read-%s expected read-%s by default\n", bdev, ro ? "only" : "write", namespace->ro ? "only" : "write"); goto out; } /* destroy pfn device */ ndctl_pfn_delete(found); /* clear read-write, and enable raw mode */ ndctl_region_set_ro(region, 0); ndctl_namespace_set_raw_mode(ndns, 1); ndctl_namespace_enable(ndns); /* destroy pfn metadata */ sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns)); fd = open(bdev, O_RDWR|O_DIRECT|O_EXCL); if (fd < 0) { fprintf(stderr, "%s: failed to open %s to destroy pfn\n", devname, bdev); goto out; } memset(buf, 0, 4096); rc = pwrite(fd, buf, 4096, 4096); if (rc < 4096) { rc = -ENXIO; fprintf(stderr, "%s: failed to overwrite pfn on %s\n", devname, bdev); } out: ndctl_region_set_ro(region, namespace->ro); ndctl_namespace_set_raw_mode(ndns, 0); if (fd >= 0) close(fd); return rc; } static int check_dax_autodetect(struct ndctl_bus *bus, struct ndctl_namespace *ndns, void *buf, struct namespace *namespace) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_test *test = ndctl_get_private_data(ctx); struct dax *auto_dax = namespace->dax_settings; struct ndctl_dax *dax, *found = NULL; enum ndctl_namespace_mode mode; ssize_t rc = -ENXIO; char bdev[50]; int fd; ndctl_dax_foreach(region, dax) { struct ndctl_namespace *dax_ndns; uuid_t uu; ndctl_dax_get_uuid(dax, uu); if (uuid_compare(uu, auto_dax->uuid) != 0) continue; if (!ndctl_dax_is_enabled(dax)) continue; dax_ndns = ndctl_dax_get_namespace(dax); if (strcmp(ndctl_namespace_get_devname(dax_ndns), devname) != 0) continue; fprintf(stderr, "%s: dax_ndns: %p ndns: %p\n", __func__, dax_ndns, ndns); found = dax; break; } if (!found) return -ENXIO; mode = ndctl_namespace_get_enforce_mode(ndns); if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0)) && mode != NDCTL_NS_MODE_DAX) { fprintf(stderr, "%s expected enforce_mode dax\n", devname); return -ENXIO; } rc = validate_dax(dax); if (rc) { fprintf(stderr, "%s: %s validate_dax failed\n", __func__, devname); return rc; } rc = -ENXIO; /* destroy dax device */ ndctl_dax_delete(found); /* clear read-write, and enable raw mode */ ndctl_region_set_ro(region, 0); ndctl_namespace_set_raw_mode(ndns, 1); ndctl_namespace_enable(ndns); /* destroy dax metadata */ sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns)); fd = open(bdev, O_RDWR|O_DIRECT|O_EXCL); if (fd < 0) { fprintf(stderr, "%s: failed to open %s to destroy dax\n", devname, bdev); goto out; } memset(buf, 0, 4096); rc = pwrite(fd, buf, 4096, 4096); if (rc < 4096) { rc = -ENXIO; fprintf(stderr, "%s: failed to overwrite dax on %s\n", devname, bdev); } out: ndctl_region_set_ro(region, namespace->ro); ndctl_namespace_set_raw_mode(ndns, 0); if (fd >= 0) close(fd); return rc; } static int check_btt_autodetect(struct ndctl_bus *bus, struct ndctl_namespace *ndns, void *buf, struct namespace *namespace) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); const char *devname = ndctl_namespace_get_devname(ndns); struct ndctl_test *test = ndctl_get_private_data(ctx); struct btt *auto_btt = namespace->btt_settings; struct ndctl_btt *btt, *found = NULL; enum ndctl_namespace_mode mode; ssize_t rc = -ENXIO; char bdev[50]; int fd, ro; ndctl_btt_foreach(region, btt) { struct ndctl_namespace *btt_ndns; uuid_t uu; ndctl_btt_get_uuid(btt, uu); if (uuid_compare(uu, auto_btt->uuid) != 0) continue; if (!ndctl_btt_is_enabled(btt)) continue; btt_ndns = ndctl_btt_get_namespace(btt); if (strcmp(ndctl_namespace_get_devname(btt_ndns), devname) != 0) continue; fprintf(stderr, "%s: btt_ndns: %p ndns: %p\n", __func__, btt_ndns, ndns); found = btt; break; } if (!found) return -ENXIO; mode = ndctl_namespace_get_enforce_mode(ndns); if (ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0)) && mode != NDCTL_NS_MODE_SAFE) { fprintf(stderr, "%s expected enforce_mode btt\n", devname); return -ENXIO; } sprintf(bdev, "/dev/%s", ndctl_btt_get_block_device(btt)); fd = open(bdev, O_RDONLY); if (fd < 0) return -ENXIO; rc = ioctl(fd, BLKROGET, &ro); if (rc < 0) { fprintf(stderr, "%s: failed to open %s\n", __func__, bdev); rc = -ENXIO; goto out; } close(fd); fd = -1; rc = -ENXIO; if (ro != namespace->ro) { fprintf(stderr, "%s: read-%s expected read-%s by default\n", bdev, ro ? "only" : "write", namespace->ro ? "only" : "write"); goto out; } /* destroy btt device */ ndctl_btt_delete(found); /* clear read-write, and enable raw mode */ ndctl_region_set_ro(region, 0); ndctl_namespace_set_raw_mode(ndns, 1); ndctl_namespace_enable(ndns); /* destroy btt metadata */ sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns)); fd = open(bdev, O_RDWR|O_DIRECT|O_EXCL); if (fd < 0) { fprintf(stderr, "%s: failed to open %s to destroy btt\n", devname, bdev); goto out; } memset(buf, 0, 4096); /* Delete both the first and second 4K pages */ rc = pwrite(fd, buf, 4096, 4096); if (rc < 4096) { rc = -ENXIO; fprintf(stderr, "%s: failed to overwrite btt on %s\n", devname, bdev); goto out; } rc = pwrite(fd, buf, 4096, 0); if (rc < 4096) { rc = -ENXIO; fprintf(stderr, "%s: failed to overwrite btt on %s\n", devname, bdev); } out: ndctl_region_set_ro(region, namespace->ro); ndctl_namespace_set_raw_mode(ndns, 0); if (fd >= 0) close(fd); return rc; } static int validate_bdev(const char *devname, struct ndctl_btt *btt, struct ndctl_pfn *pfn, struct ndctl_namespace *ndns, struct namespace *namespace, void *buf) { char bdevpath[50]; int fd, rc, ro; if (btt) sprintf(bdevpath, "/dev/%s", ndctl_btt_get_block_device(btt)); else if (pfn) sprintf(bdevpath, "/dev/%s", ndctl_pfn_get_block_device(pfn)); else sprintf(bdevpath, "/dev/%s", ndctl_namespace_get_block_device(ndns)); fd = open(bdevpath, O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: failed to open(%s, O_RDONLY)\n", devname, bdevpath); return -ENXIO; } rc = ioctl(fd, BLKROGET, &ro); if (rc < 0) { fprintf(stderr, "%s: BLKROGET failed\n", devname); rc = -errno; goto out; } if (namespace->ro != ro) { fprintf(stderr, "%s: read-%s expected: read-%s\n", devname, ro ? "only" : "write", namespace->ro ? "only" : "write"); rc = -ENXIO; goto out; } ro = 0; rc = ioctl(fd, BLKROSET, &ro); if (rc < 0) { fprintf(stderr, "%s: BLKROSET failed\n", devname); rc = -errno; goto out; } close(fd); fd = open(bdevpath, O_RDWR|O_DIRECT); if (fd < 0) { fprintf(stderr, "%s: failed to open(%s, O_RDWR|O_DIRECT)\n", devname, bdevpath); return -ENXIO; } if (read(fd, buf, 4096) < 4096) { fprintf(stderr, "%s: failed to read %s\n", devname, bdevpath); rc = -ENXIO; goto out; } if (write(fd, buf, 4096) < 4096) { fprintf(stderr, "%s: failed to write %s\n", devname, bdevpath); rc = -ENXIO; goto out; } rc = 0; out: close(fd); return rc; } static int validate_write_cache(struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); int wc, mode, type, rc; type = ndctl_namespace_get_type(ndns); mode = ndctl_namespace_get_mode(ndns); wc = ndctl_namespace_write_cache_is_enabled(ndns); if ((type == ND_DEVICE_NAMESPACE_PMEM || type == ND_DEVICE_NAMESPACE_IO) && (mode == NDCTL_NS_MODE_FSDAX || mode == NDCTL_NS_MODE_RAW)) { if (wc != 1) { fprintf(stderr, "%s: expected write_cache enabled\n", devname); return -ENXIO; } rc = ndctl_namespace_disable_write_cache(ndns); if (rc) { fprintf(stderr, "%s: failed to disable write_cache\n", devname); return rc; } rc = ndctl_namespace_write_cache_is_enabled(ndns); if (rc != 0) { fprintf(stderr, "%s: write_cache could not be disabled\n", devname); return rc; } rc = ndctl_namespace_enable_write_cache(ndns); if (rc) { fprintf(stderr, "%s: failed to re-enable write_cache\n", devname); return rc; } rc = ndctl_namespace_write_cache_is_enabled(ndns); if (rc != 1) { fprintf(stderr, "%s: write_cache could not be re-enabled\n", devname); return rc; } } else { if (wc == 0 || wc == 1) { fprintf(stderr, "%s: expected write_cache to be absent\n", devname); return -ENXIO; } } return 0; } static int check_namespaces(struct ndctl_region *region, struct namespace **namespaces, enum ns_mode mode) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); struct ndctl_test *test = ndctl_get_private_data(ctx); struct ndctl_bus *bus = ndctl_region_get_bus(region); struct ndctl_namespace **ndns_save; struct namespace *namespace; int i, j, rc, retry_cnt = 1; void *buf = NULL, *__ndns_save; char devname[50]; if (posix_memalign(&buf, 4096, 4096) != 0) return -ENOMEM; for (i = 0; (namespace = namespaces[i]); i++) if (namespace->do_configure >= 0) namespace->do_configure = 1; retry: ndns_save = NULL; for (i = 0; (namespace = namespaces[i]); i++) { uuid_t uu; struct ndctl_namespace *ndns; unsigned long _sizes[] = { 0 }, *sector_sizes = _sizes; int num_sector_sizes = (int) ARRAY_SIZE(_sizes); snprintf(devname, sizeof(devname), "namespace%d.%d", ndctl_region_get_id(region), namespace->id); ndns = get_namespace_by_id(region, namespace); if (!ndns) { fprintf(stderr, "%s: failed to find namespace\n", devname); break; } if (ndctl_region_get_type(region) == ND_DEVICE_REGION_PMEM && !ndctl_test_attempt(test, KERNEL_VERSION(4, 13, 0))) /* pass, no sector_size support for pmem prior to 4.13 */; else { num_sector_sizes = namespace->num_sector_sizes; sector_sizes = namespace->sector_sizes; } for (j = 0; j < num_sector_sizes; j++) { struct btt *btt_s = NULL; struct pfn *pfn_s = NULL; struct dax *dax_s = NULL; struct ndctl_btt *btt = NULL; struct ndctl_pfn *pfn = NULL; struct ndctl_dax *dax = NULL; rc = configure_namespace(region, ndns, namespace, sector_sizes[j], mode); if (rc < 0) { fprintf(stderr, "%s: failed to configure namespace\n", devname); break; } if (strcmp(ndctl_namespace_get_type_name(ndns), namespace->type) != 0) { fprintf(stderr, "%s: expected type: %s got: %s\n", devname, ndctl_namespace_get_type_name(ndns), namespace->type); rc = -ENXIO; break; } /* * On the second time through this loop we skip * establishing btt|pfn since * check_{btt|pfn}_autodetect() destroyed the * inital instance. */ if (mode == BTT) { btt_s = namespace->do_configure > 0 ? namespace->btt_settings : NULL; btt = ndctl_namespace_get_btt(ndns); if (!!btt_s != !!btt) { fprintf(stderr, "%s expected btt %s by default\n", devname, namespace->btt_settings ? "enabled" : "disabled"); rc = -ENXIO; break; } } if (mode == PFN) { pfn_s = namespace->do_configure > 0 ? namespace->pfn_settings : NULL; pfn = ndctl_namespace_get_pfn(ndns); if (!!pfn_s != !!pfn) { fprintf(stderr, "%s expected pfn %s by default\n", devname, namespace->pfn_settings ? "enabled" : "disabled"); rc = -ENXIO; break; } } if (mode == DAX) { dax_s = namespace->do_configure > 0 ? namespace->dax_settings : NULL; dax = ndctl_namespace_get_dax(ndns); if (!!dax_s != !!dax) { fprintf(stderr, "%s expected dax %s by default\n", devname, namespace->dax_settings ? "enabled" : "disabled"); rc = -ENXIO; break; } } if (!btt_s && !pfn_s && !dax_s && !ndctl_namespace_is_enabled(ndns)) { fprintf(stderr, "%s: expected enabled by default\n", devname); rc = -ENXIO; break; } if (namespace->size != ndctl_namespace_get_size(ndns)) { fprintf(stderr, "%s: expected size: %#llx got: %#llx\n", devname, namespace->size, ndctl_namespace_get_size(ndns)); rc = -ENXIO; break; } if (sector_sizes[j] && sector_sizes[j] != ndctl_namespace_get_sector_size(ndns)) { fprintf(stderr, "%s: expected lbasize: %#lx got: %#x\n", devname, sector_sizes[j], ndctl_namespace_get_sector_size(ndns)); rc = -ENXIO; break; } ndctl_namespace_get_uuid(ndns, uu); if (uuid_compare(uu, namespace->uuid) != 0) { char expect[40], actual[40]; uuid_unparse(uu, actual); uuid_unparse(namespace->uuid, expect); fprintf(stderr, "%s: expected uuid: %s got: %s\n", devname, expect, actual); rc = -ENXIO; break; } if (namespace->check_alt_name && strcmp(ndctl_namespace_get_alt_name(ndns), devname) != 0) { fprintf(stderr, "%s: expected alt_name: %s got: %s\n", devname, devname, ndctl_namespace_get_alt_name(ndns)); rc = -ENXIO; break; } if (dax) rc = validate_dax(dax); else rc = validate_bdev(devname, btt, pfn, ndns, namespace, buf); if (rc) { fprintf(stderr, "%s: %s validate_%s failed\n", __func__, devname, dax ? "dax" : "bdev"); break; } rc = validate_write_cache(ndns); if (rc) { fprintf(stderr, "%s: %s validate_write_cache failed\n", __func__, devname); break; } if (ndctl_namespace_disable_invalidate(ndns) < 0) { fprintf(stderr, "%s: failed to disable\n", devname); rc = -ENXIO; break; } if (ndctl_namespace_enable(ndns) < 0) { fprintf(stderr, "%s: failed to enable\n", devname); rc = -ENXIO; break; } if (btt_s && check_btt_autodetect(bus, ndns, buf, namespace) < 0) { fprintf(stderr, "%s, failed btt autodetect\n", devname); rc = -ENXIO; break; } if (pfn_s && check_pfn_autodetect(bus, ndns, buf, namespace) < 0) { fprintf(stderr, "%s, failed pfn autodetect\n", devname); rc = -ENXIO; break; } if (dax_s && check_dax_autodetect(bus, ndns, buf, namespace) < 0) { fprintf(stderr, "%s, failed dax autodetect\n", devname); rc = -ENXIO; break; } /* * if the namespace is being tested with a btt, there is no * point testing different sector sizes for the namespace itself */ if (btt_s || pfn_s || dax_s) break; /* * If this is the last sector size being tested, don't disable * the namespace */ if (j == num_sector_sizes - 1) break; /* * If we're in the second time through this, don't loop for * different sector sizes as ->do_configure is disabled */ if (!retry_cnt) break; if (ndctl_namespace_disable_invalidate(ndns) < 0) { fprintf(stderr, "%s: failed to disable\n", devname); break; } } namespace->do_configure = 0; __ndns_save = realloc(ndns_save, sizeof(struct ndctl_namespace *) * (i + 1)); if (!__ndns_save) { fprintf(stderr, "%s: %s() -ENOMEM\n", devname, __func__); rc = -ENOMEM; break; } else { ndns_save = __ndns_save; ndns_save[i] = ndns; } if (rc) break; } if (namespace || ndctl_region_disable_preserve(region) != 0) { rc = -ENXIO; if (!namespace) fprintf(stderr, "failed to disable region%d\n", ndctl_region_get_id(region)); goto out; } /* * On the second time through configure_namespace() is skipped * to test assembling namespace(s) from an existing label set */ if (retry_cnt--) { ndctl_region_enable(region); free(ndns_save); goto retry; } rc = 0; for (i--; i >= 0; i--) { struct ndctl_namespace *ndns = ndns_save[i]; snprintf(devname, sizeof(devname), "namespace%d.%d", ndctl_region_get_id(region), ndctl_namespace_get_id(ndns)); if (ndctl_namespace_is_valid(ndns)) { fprintf(stderr, "%s: failed to invalidate\n", devname); rc = -ENXIO; break; } } ndctl_region_cleanup(region); out: free(ndns_save); free(buf); return rc; } static int check_btt_supported_sectors(struct ndctl_btt *btt, struct btt *expect_btt) { int s, t; char devname[50]; snprintf(devname, sizeof(devname), "btt%d", ndctl_btt_get_id(btt)); for (s = 0; s < expect_btt->num_sector_sizes; s++) { for (t = 0; t < expect_btt->num_sector_sizes; t++) { if (ndctl_btt_get_supported_sector_size(btt, t) == expect_btt->sector_sizes[s]) break; } if (t >= expect_btt->num_sector_sizes) { fprintf(stderr, "%s: expected sector_size: %d to be supported\n", devname, expect_btt->sector_sizes[s]); return -ENXIO; } } return 0; } static int check_btts(struct ndctl_region *region, struct btt **btts) { struct btt *btt_s; int i; for (i = 0; (btt_s = btts[i]); i++) { struct ndctl_btt *btt; char devname[50]; uuid_t btt_uuid; int rc; btt = get_idle_btt(region); if (!btt) { fprintf(stderr, "failed to find idle btt\n"); return -ENXIO; } snprintf(devname, sizeof(devname), "btt%d", ndctl_btt_get_id(btt)); ndctl_btt_get_uuid(btt, btt_uuid); if (uuid_compare(btt_uuid, btt_s->uuid) != 0) { char expect[40], actual[40]; uuid_unparse(btt_uuid, actual); uuid_unparse(btt_s->uuid, expect); fprintf(stderr, "%s: expected uuid: %s got: %s\n", devname, expect, actual); return -ENXIO; } if (ndctl_btt_get_num_sector_sizes(btt) != btt_s->num_sector_sizes) { fprintf(stderr, "%s: expected num_sector_sizes: %d got: %d\n", devname, btt_s->num_sector_sizes, ndctl_btt_get_num_sector_sizes(btt)); } rc = check_btt_supported_sectors(btt, btt_s); if (rc) return rc; if (btt_s->enabled && ndctl_btt_is_enabled(btt)) { fprintf(stderr, "%s: expected disabled by default\n", devname); return -ENXIO; } } return 0; } struct check_cmd { int (*check_fn)(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check); struct ndctl_cmd *cmd; struct ndctl_test *test; }; static struct check_cmd *check_cmds; static int check_get_config_size(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check) { struct ndctl_cmd *cmd; int rc; if (check->cmd != NULL) { fprintf(stderr, "%s: dimm: %#x expected a NULL command, by default\n", __func__, ndctl_dimm_get_handle(dimm)); return -ENXIO; } cmd = ndctl_dimm_cmd_new_cfg_size(dimm); if (!cmd) { fprintf(stderr, "%s: dimm: %#x failed to create cmd\n", __func__, ndctl_dimm_get_handle(dimm)); return -ENOTTY; } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } if (ndctl_cmd_cfg_size_get_size(cmd) != SZ_128K) { fprintf(stderr, "%s: dimm: %#x expect size: %d got: %d\n", __func__, ndctl_dimm_get_handle(dimm), SZ_128K, ndctl_cmd_cfg_size_get_size(cmd)); ndctl_cmd_unref(cmd); return -ENXIO; } check->cmd = cmd; return 0; } static int check_get_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check) { struct ndctl_cmd *cmd_size = check_cmds[ND_CMD_GET_CONFIG_SIZE].cmd; struct ndctl_cmd *cmd = ndctl_dimm_cmd_new_cfg_read(cmd_size); static char buf[SZ_128K]; ssize_t rc; if (!cmd) { fprintf(stderr, "%s: dimm: %#x failed to create cmd\n", __func__, ndctl_dimm_get_handle(dimm)); return -ENOTTY; } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd: %zd\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } rc = ndctl_cmd_cfg_read_get_data(cmd, buf, SZ_128K, 0); if (rc != SZ_128K) { fprintf(stderr, "%s: dimm: %#x expected read %d bytes, got: %zd\n", __func__, ndctl_dimm_get_handle(dimm), SZ_128K, rc); ndctl_cmd_unref(cmd); return -ENXIO; } check->cmd = cmd; return 0; } static int check_set_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check) { struct ndctl_cmd *cmd_read = check_cmds[ND_CMD_GET_CONFIG_DATA].cmd; struct ndctl_cmd *cmd = ndctl_dimm_cmd_new_cfg_write(cmd_read); char buf[20], result[sizeof(buf)]; int rc; if (!cmd) { fprintf(stderr, "%s: dimm: %#x failed to create cmd\n", __func__, ndctl_dimm_get_handle(dimm)); return -ENOTTY; } memset(buf, 0, sizeof(buf)); ndctl_cmd_cfg_write_set_data(cmd, buf, sizeof(buf), 0); rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } rc = ndctl_cmd_submit(cmd_read); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit read1: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } ndctl_cmd_cfg_read_get_data(cmd_read, result, sizeof(result), 0); if (memcmp(result, buf, sizeof(result)) != 0) { fprintf(stderr, "%s: dimm: %#x read1 data miscompare: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return -ENXIO; } sprintf(buf, "dimm-%#x", ndctl_dimm_get_handle(dimm)); ndctl_cmd_cfg_write_set_data(cmd, buf, sizeof(buf), 0); rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } rc = ndctl_cmd_submit(cmd_read); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit read2: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } ndctl_cmd_cfg_read_get_data(cmd_read, result, sizeof(result), 0); if (memcmp(result, buf, sizeof(result)) != 0) { fprintf(stderr, "%s: dimm: %#x read2 data miscompare: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } check->cmd = cmd; return 0; } #define __check_smart(dimm, cmd, field, mask) ({ \ if ((ndctl_cmd_smart_get_##field(cmd) & mask) != smart_data.field) { \ fprintf(stderr, "%s dimm: %#x expected \'" #field \ "\' %#x got: %#x\n", __func__, \ ndctl_dimm_get_handle(dimm), \ smart_data.field, \ ndctl_cmd_smart_get_##field(cmd)); \ ndctl_cmd_unref(cmd); \ return -ENXIO; \ } \ }) /* * Note, this is not a command payload, this is just a namespace for * smart parameters. */ struct smart { unsigned int flags, health, temperature, spares, alarm_flags, life_used, shutdown_state, shutdown_count, vendor_size; }; static int check_smart(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check) { static const struct smart smart_data = { .flags = ND_SMART_HEALTH_VALID | ND_SMART_TEMP_VALID | ND_SMART_SPARES_VALID | ND_SMART_ALARM_VALID | ND_SMART_USED_VALID | ND_SMART_SHUTDOWN_VALID, .health = ND_SMART_NON_CRITICAL_HEALTH, .temperature = 23 * 16, .spares = 75, .alarm_flags = ND_SMART_SPARE_TRIP | ND_SMART_TEMP_TRIP, .life_used = 5, .shutdown_state = 0, .shutdown_count = 42, .vendor_size = 0, }; struct ndctl_cmd *cmd = ndctl_dimm_cmd_new_smart(dimm); int rc; if (!cmd) { fprintf(stderr, "%s: dimm: %#x failed to create cmd\n", __func__, ndctl_dimm_get_handle(dimm)); return -ENXIO; } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } __check_smart(dimm, cmd, flags, ~(ND_SMART_CTEMP_VALID | ND_SMART_SHUTDOWN_COUNT_VALID)); __check_smart(dimm, cmd, health, -1); __check_smart(dimm, cmd, temperature, -1); __check_smart(dimm, cmd, spares, -1); __check_smart(dimm, cmd, alarm_flags, -1); __check_smart(dimm, cmd, life_used, -1); __check_smart(dimm, cmd, shutdown_state, -1); __check_smart(dimm, cmd, vendor_size, -1); if (ndctl_cmd_smart_get_flags(cmd) & ND_SMART_SHUTDOWN_COUNT_VALID) __check_smart(dimm, cmd, shutdown_count, -1); check->cmd = cmd; return 0; } #define __check_smart_threshold(dimm, cmd, field) ({ \ if (ndctl_cmd_smart_threshold_get_##field(cmd) != smart_t_data.field) { \ fprintf(stderr, "%s dimm: %#x expected \'" #field \ "\' %#x got: %#x\n", __func__, \ ndctl_dimm_get_handle(dimm), \ smart_t_data.field, \ ndctl_cmd_smart_threshold_get_##field(cmd)); \ ndctl_cmd_unref(cmd_set); \ ndctl_cmd_unref(cmd); \ return -ENXIO; \ } \ }) /* * Note, this is not a command payload, this is just a namespace for * smart_threshold parameters. */ struct smart_threshold { unsigned int alarm_control, media_temperature, ctrl_temperature, spares; }; static int check_smart_threshold(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check) { static const struct smart_threshold smart_t_data = { .alarm_control = ND_SMART_SPARE_TRIP | ND_SMART_TEMP_TRIP, .media_temperature = 40 * 16, .ctrl_temperature = 30 * 16, .spares = 5, }; struct ndctl_cmd *cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); struct ndctl_cmd *cmd_smart = check_cmds[ND_CMD_SMART].cmd; struct ndctl_cmd *cmd_set; struct timeval tm; char buf[4096]; fd_set fds; int rc, fd; if (!cmd) { fprintf(stderr, "%s: dimm: %#x failed to create cmd\n", __func__, ndctl_dimm_get_handle(dimm)); return -ENXIO; } fd = ndctl_dimm_get_health_eventfd(dimm); FD_ZERO(&fds); FD_SET(fd, &fds); rc = pread(fd, buf, sizeof(buf), 0); tm.tv_sec = 0; tm.tv_usec = 500; rc = select(fd + 1, NULL, NULL, &fds, &tm); if (rc) { fprintf(stderr, "%s: expected health event timeout\n", ndctl_dimm_get_devname(dimm)); return -ENXIO; } /* * Starting with v4.9 smart threshold requests trigger the file * descriptor returned by ndctl_dimm_get_health_eventfd(). */ if (ndctl_test_attempt(check->test, KERNEL_VERSION(4, 9, 0))) { int pid = fork(); if (pid == 0) { FD_ZERO(&fds); FD_SET(fd, &fds); tm.tv_sec = 5; tm.tv_usec = 0; rc = select(fd + 1, NULL, NULL, &fds, &tm); if (rc != 1 || !FD_ISSET(fd, &fds)) exit(EXIT_FAILURE); rc = pread(fd, buf, sizeof(buf), 0); exit(EXIT_SUCCESS); } } rc = ndctl_cmd_submit(cmd); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd); return rc; } /* * The same kernel change that adds nfit_test support for this * command is the same change that moves notifications to * require set_threshold. If we fail to get a command, but the * notification fires then we are on an old kernel, otherwise * whether old kernel or new kernel the notification should * fire. */ cmd_set = ndctl_dimm_cmd_new_smart_set_threshold(cmd); if (cmd_set) { /* * These values got reworked when nfit_test gained * set_threshold support */ __check_smart_threshold(dimm, cmd, media_temperature); __check_smart_threshold(dimm, cmd, ctrl_temperature); __check_smart_threshold(dimm, cmd, spares); __check_smart_threshold(dimm, cmd, alarm_control); /* * Set all thresholds to match current values and set * all alarms. */ rc = ndctl_cmd_smart_threshold_set_alarm_control(cmd_set, ndctl_cmd_smart_threshold_get_supported_alarms(cmd_set)); /* 'set_temperature' and 'set_media_temperature' are aliases */ rc |= ndctl_cmd_smart_threshold_set_temperature(cmd_set, ndctl_cmd_smart_get_media_temperature(cmd_smart)); rc |= ndctl_cmd_smart_threshold_set_ctrl_temperature(cmd_set, ndctl_cmd_smart_get_ctrl_temperature(cmd_smart)); rc |= ndctl_cmd_smart_threshold_set_spares(cmd_set, ndctl_cmd_smart_get_spares(cmd_smart)); if (rc) { fprintf(stderr, "%s: failed set threshold parameters\n", __func__); ndctl_cmd_unref(cmd_set); return -ENXIO; } rc = ndctl_cmd_submit(cmd_set); if (rc < 0) { fprintf(stderr, "%s: dimm: %#x failed to submit cmd_set: %d\n", __func__, ndctl_dimm_get_handle(dimm), rc); ndctl_cmd_unref(cmd_set); return rc; } ndctl_cmd_unref(cmd_set); } if (ndctl_test_attempt(check->test, KERNEL_VERSION(4, 9, 0))) { wait(&rc); if (WEXITSTATUS(rc) == EXIT_FAILURE) { fprintf(stderr, "%s: expect health event trigger\n", ndctl_dimm_get_devname(dimm)); return -ENXIO; } } ndctl_cmd_unref(cmd); return 0; } #define BITS_PER_LONG 32 static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm, unsigned long bus_commands, unsigned long dimm_commands, struct ndctl_test *test) { /* * For now, by coincidence, these are indexed in test execution * order such that check_get_config_data can assume that * check_get_config_size has updated * check_cmd[ND_CMD_GET_CONFIG_SIZE].cmd and * check_set_config_data can assume that both * check_get_config_size and check_get_config_data have run */ struct check_cmd __check_dimm_cmds[] = { [ND_CMD_GET_CONFIG_SIZE] = { check_get_config_size }, [ND_CMD_GET_CONFIG_DATA] = { check_get_config_data }, [ND_CMD_SET_CONFIG_DATA] = { check_set_config_data }, [ND_CMD_SMART] = { check_smart }, [ND_CMD_SMART_THRESHOLD] = { .check_fn = check_smart_threshold, .test = test, }, }; unsigned int i, rc = 0; /* * The kernel did not start emulating v1.2 namespace spec smart data * until 4.9. */ if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) dimm_commands &= ~((1 << ND_CMD_SMART) | (1 << ND_CMD_SMART_THRESHOLD)); /* Check DIMM commands */ check_cmds = __check_dimm_cmds; for (i = 0; i < BITS_PER_LONG; i++) { struct check_cmd *check = &check_cmds[i]; if ((dimm_commands & (1UL << i)) == 0) continue; if (!ndctl_dimm_is_cmd_supported(dimm, i)) { fprintf(stderr, "%s: bus: %s dimm%d expected cmd: %s supported\n", __func__, ndctl_bus_get_provider(bus), ndctl_dimm_get_id(dimm), ndctl_dimm_get_cmd_name(dimm, i)); return -ENXIO; } if (!check->check_fn) continue; rc = check->check_fn(bus, dimm, check); if (rc) break; } for (i = 0; i < ARRAY_SIZE(__check_dimm_cmds); i++) { if (__check_dimm_cmds[i].cmd) ndctl_cmd_unref(__check_dimm_cmds[i].cmd); __check_dimm_cmds[i].cmd = NULL; } if (rc) goto out; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 6, 0))) goto out; out: return rc; } static int check_dimms(struct ndctl_bus *bus, struct dimm *dimms, int n, unsigned long bus_commands, unsigned long dimm_commands, struct ndctl_test *test) { long long dsc; int i, j, rc; for (i = 0; i < n; i++) { struct ndctl_dimm *dimm = get_dimm_by_handle(bus, dimms[i].handle); if (!dimm) { fprintf(stderr, "failed to find dimm: %d\n", dimms[i].phys_id); return -ENXIO; } if (ndctl_dimm_get_phys_id(dimm) != dimms[i].phys_id) { fprintf(stderr, "dimm%d expected phys_id: %d got: %d\n", i, dimms[i].phys_id, ndctl_dimm_get_phys_id(dimm)); return -ENXIO; } if (ndctl_dimm_has_errors(dimm) != !!dimms[i].flags) { fprintf(stderr, "bus: %s dimm%d %s expected%s errors\n", ndctl_bus_get_provider(bus), i, ndctl_dimm_get_devname(dimm), dimms[i].flags ? "" : " no"); return -ENXIO; } if (ndctl_dimm_failed_save(dimm) != dimms[i].f_save || ndctl_dimm_failed_arm(dimm) != dimms[i].f_arm || ndctl_dimm_failed_restore(dimm) != dimms[i].f_restore || ndctl_dimm_smart_pending(dimm) != dimms[i].f_smart || ndctl_dimm_failed_flush(dimm) != dimms[i].f_flush) { fprintf(stderr, "expected: %s%s%s%s%sgot: %s%s%s%s%s\n", dimms[i].f_save ? "save_fail " : "", dimms[i].f_arm ? "not_armed " : "", dimms[i].f_restore ? "restore_fail " : "", dimms[i].f_smart ? "smart_event " : "", dimms[i].f_flush ? "flush_fail " : "", ndctl_dimm_failed_save(dimm) ? "save_fail " : "", ndctl_dimm_failed_arm(dimm) ? "not_armed " : "", ndctl_dimm_failed_restore(dimm) ? "restore_fail " : "", ndctl_dimm_smart_pending(dimm) ? "smart_event " : "", ndctl_dimm_failed_flush(dimm) ? "flush_fail " : ""); return -ENXIO; } if (ndctl_test_attempt(test, KERNEL_VERSION(4, 7, 0))) { if (ndctl_dimm_get_formats(dimm) != dimms[i].formats) { fprintf(stderr, "dimm%d expected formats: %d got: %d\n", i, dimms[i].formats, ndctl_dimm_get_formats(dimm)); return -ENXIO; } for (j = 0; j < dimms[i].formats; j++) { if (ndctl_dimm_get_formatN(dimm, j) != dimms[i].format[j]) { fprintf(stderr, "dimm%d expected format[%d]: %d got: %d\n", i, j, dimms[i].format[j], ndctl_dimm_get_formatN(dimm, j)); return -ENXIO; } } } if (ndctl_test_attempt(test, KERNEL_VERSION(4, 7, 0))) { if (ndctl_dimm_get_subsystem_vendor(dimm) != dimms[i].subsystem_vendor) { fprintf(stderr, "dimm%d expected subsystem vendor: %d got: %d\n", i, dimms[i].subsystem_vendor, ndctl_dimm_get_subsystem_vendor(dimm)); return -ENXIO; } } if (ndctl_test_attempt(test, KERNEL_VERSION(4, 8, 0))) { if (ndctl_dimm_get_manufacturing_date(dimm) != dimms[i].manufacturing_date) { fprintf(stderr, "dimm%d expected manufacturing date: %d got: %d\n", i, dimms[i].manufacturing_date, ndctl_dimm_get_manufacturing_date(dimm)); return -ENXIO; } } dsc = ndctl_dimm_get_dirty_shutdown(dimm); if (dsc != -ENOENT && dsc != dimms[i].dirty_shutdown) { fprintf(stderr, "dimm%d expected dirty shutdown: %lld got: %lld\n", i, dimms[i].dirty_shutdown, ndctl_dimm_get_dirty_shutdown(dimm)); return -ENXIO; } rc = check_commands(bus, dimm, bus_commands, dimm_commands, test); if (rc) return rc; } return 0; } static void reset_bus(struct ndctl_bus *bus) { struct ndctl_region *region; struct ndctl_dimm *dimm; /* disable all regions so that set_config_data commands are permitted */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) ndctl_dimm_zero_labels(dimm); /* set regions back to their default state */ ndctl_region_foreach(bus, region) ndctl_region_enable(region); } static int do_test0(struct ndctl_ctx *ctx, struct ndctl_test *test) { struct ndctl_bus *bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER0); struct ndctl_region *region; struct ndctl_dimm *dimm; int rc; if (!bus) return -ENXIO; ndctl_bus_wait_probe(bus); /* disable all regions so that set_config_data commands are permitted */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); rc = check_dimms(bus, dimms0, ARRAY_SIZE(dimms0), bus_commands0, dimm_commands0, test); if (rc) return rc; ndctl_dimm_foreach(bus, dimm) { rc = ndctl_dimm_zero_labels(dimm); if (rc < 0) { fprintf(stderr, "failed to zero %s\n", ndctl_dimm_get_devname(dimm)); return rc; } } /* set regions back to their default state */ ndctl_region_foreach(bus, region) ndctl_region_enable(region); /* pfn and dax tests require vmalloc-enabled nfit_test */ if (ndctl_test_attempt(test, KERNEL_VERSION(4, 8, 0))) { rc = check_regions(bus, regions0, ARRAY_SIZE(regions0), DAX); if (rc) return rc; reset_bus(bus); } if (ndctl_test_attempt(test, KERNEL_VERSION(4, 8, 0))) { rc = check_regions(bus, regions0, ARRAY_SIZE(regions0), PFN); if (rc) return rc; reset_bus(bus); } return check_regions(bus, regions0, ARRAY_SIZE(regions0), BTT); } static int do_test1(struct ndctl_ctx *ctx, struct ndctl_test *test) { struct ndctl_bus *bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER1); int rc; if (!bus) return -ENXIO; ndctl_bus_wait_probe(bus); /* * Starting with v4.10 the dimm on nfit_test.1 gets a unique * handle. */ if (ndctl_test_attempt(test, KERNEL_VERSION(4, 10, 0))) dimms1[0].handle = DIMM_HANDLE(1, 0, 0, 0, 0); rc = check_dimms(bus, dimms1, ARRAY_SIZE(dimms1), 0, 0, test); if (rc) return rc; return check_regions(bus, regions1, ARRAY_SIZE(regions1), BTT); } typedef int (*do_test_fn)(struct ndctl_ctx *ctx, struct ndctl_test *test); static do_test_fn do_test[] = { do_test0, do_test1, }; int test_libndctl(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { unsigned int i; struct kmod_module *mod; struct kmod_ctx *kmod_ctx; struct daxctl_ctx *daxctl_ctx; int err, result = EXIT_FAILURE; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) return 77; ndctl_set_log_priority(ctx, loglevel); daxctl_ctx = ndctl_get_daxctl_ctx(ctx); daxctl_set_log_priority(daxctl_ctx, loglevel); ndctl_set_private_data(ctx, test); err = nfit_test_init(&kmod_ctx, &mod, ctx, loglevel, test); if (err < 0) { ndctl_test_skip(test); fprintf(stderr, "nfit_test unavailable skipping tests\n"); return 77; } for (i = 0; i < ARRAY_SIZE(do_test); i++) { err = do_test[i](ctx, test); if (err < 0) { fprintf(stderr, "ndctl-test%d failed: %d\n", i, err); break; } } if (i >= ARRAY_SIZE(do_test)) result = EXIT_SUCCESS; kmod_module_remove_module(mod, 0); kmod_unref(kmod_ctx); return result; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_libndctl(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/list-smart-dimm.c000066400000000000000000000051641355562357700166120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2018, FUJITSU LIMITED. All rights reserved. */ #include #include #include #include #include #include #include #include struct util_filter_params param; static int did_fail; static int jflag = JSON_C_TO_STRING_PRETTY; #define fail(fmt, ...) \ do { \ did_fail = 1; \ fprintf(stderr, "ndctl-%s:%s:%d: " fmt, \ VERSION, __func__, __LINE__, ##__VA_ARGS__); \ } while (0) static bool filter_region(struct ndctl_region *region, struct util_filter_ctx *ctx) { return true; } static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx) { struct list_filter_arg *lfa = ctx->list; struct json_object *jdimm; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SMART)) return; if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SMART_THRESHOLD)) return; if (!ndctl_dimm_is_flag_supported(dimm, ND_SMART_ALARM_VALID)) return; if (!lfa->jdimms) { lfa->jdimms = json_object_new_array(); if (!lfa->jdimms) { fail("\n"); return; } } jdimm = util_dimm_to_json(dimm, lfa->flags); if (!jdimm) { fail("\n"); return; } json_object_array_add(lfa->jdimms, jdimm); } static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *ctx) { return true; } static int list_display(struct list_filter_arg *lfa) { struct json_object *jdimms = lfa->jdimms; if (jdimms) util_display_json_array(stdout, jdimms, jflag); return 0; } int main(int argc, const char *argv[]) { struct ndctl_ctx *ctx; int i, rc; const struct option options[] = { OPT_STRING('b', "bus", ¶m.bus, "bus-id", "filter by bus"), OPT_STRING('r', "region", ¶m.region, "region-id", "filter by region"), OPT_STRING('d', "dimm", ¶m.dimm, "dimm-id", "filter by dimm"), OPT_STRING('n', "namespace", ¶m.namespace, "namespace-id", "filter by namespace id"), OPT_END(), }; const char * const u[] = { "list-smart-dimm []", NULL }; struct util_filter_ctx fctx = { 0 }; struct list_filter_arg lfa = { 0 }; rc = ndctl_new(&ctx); if (rc < 0) return EXIT_FAILURE; argc = parse_options(argc, argv, options, u, 0); for (i = 0; i < argc; i++) error("unknown parameter \"%s\"\n", argv[i]); if (argc) usage_with_options(u, options); fctx.filter_bus = filter_bus; fctx.filter_dimm = filter_dimm; fctx.filter_region = filter_region; fctx.filter_namespace = NULL; fctx.list = &lfa; lfa.flags = 0; rc = util_filter_walk(ctx, &fctx, ¶m); if (rc) return rc; if (list_display(&lfa) || did_fail) return -ENOMEM; return 0; } ndctl-67/test/max_available_extent_ns.sh000077500000000000000000000016071355562357700206340ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018, FUJITSU LIMITED. All rights reserved. rc=77 . ./common trap 'err $LINENO' ERR check_min_kver "4.19" || do_skip "kernel $KVER may not support max_available_size" init() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all } do_test() { region=$($NDCTL list -b $NFIT_TEST_BUS0 -R -t pmem | jq -r 'sort_by(-.size) | .[].dev' | head -1) available_sz=$($NDCTL list -r $region | jq -r .[].available_size) size=$(( available_sz/4 )) NS=() for ((i=0; i<3; i++)) do NS[$i]=$($NDCTL create-namespace -r $region -t pmem -s $size | jq -r .dev) [[ -n ${NS[$i]} ]] done $NDCTL disable-namespace ${NS[1]} $NDCTL destroy-namespace ${NS[1]} $NDCTL create-namespace -r $region -t pmem } modprobe nfit_test rc=1 init do_test _cleanup exit 0 ndctl-67/test/mmap.c000066400000000000000000000116141355562357700145160ustar00rootroot00000000000000/* * Copyright(c) 2015 Toshi Kani, Hewlett Packard Enterprise. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #define MiB(a) ((a) * 1024UL * 1024UL) static struct timeval start_tv, stop_tv; // Calculate the difference between two time values. static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0) { tdiff->tv_sec = t1->tv_sec - t0->tv_sec; tdiff->tv_usec = t1->tv_usec - t0->tv_usec; if (tdiff->tv_usec < 0) tdiff->tv_sec--, tdiff->tv_usec += 1000000; } // Start timing now. static void start(void) { (void) gettimeofday(&start_tv, (struct timezone *) 0); } // Stop timing and return real time in microseconds. static unsigned long long stop(void) { struct timeval tdiff; (void) gettimeofday(&stop_tv, (struct timezone *) 0); tvsub(&tdiff, &stop_tv, &start_tv); return (tdiff.tv_sec * 1000000 + tdiff.tv_usec); } static void test_write(unsigned long *p, size_t size) { size_t i; unsigned long *wp; unsigned long long timeval; start(); for (i=0, wp=p; i<(size/sizeof(*wp)); i++) *wp++ = 1; timeval = stop(); printf("Write: %10llu usec\n", timeval); } static void test_read(unsigned long *p, size_t size) { size_t i; volatile unsigned long *wp, tmp; unsigned long long timeval; start(); for (i=0, wp=p; i<(size/sizeof(*wp)); i++) tmp = *wp++; tmp = tmp; timeval = stop(); printf("Read : %10llu usec\n", timeval); } int main(int argc, char **argv) { int fd, i, opt, ret; int oflags, mprot, mflags = 0; int is_read_only = 0, is_mlock = 0, is_mlockall = 0; int mlock_skip = 0, read_test = 0, write_test = 0; void *mptr = NULL; unsigned long *p; struct stat stat; size_t size, cpy_size; const char *file_name = NULL; while ((opt = getopt(argc, argv, "RMSApsrw")) != -1) { switch (opt) { case 'R': printf("> mmap: read-only\n"); is_read_only = 1; break; case 'M': printf("> mlock\n"); is_mlock = 1; break; case 'S': printf("> mlock - skip first iteration\n"); mlock_skip = 1; break; case 'A': printf("> mlockall\n"); is_mlockall = 1; break; case 'p': printf("> MAP_POPULATE\n"); mflags |= MAP_POPULATE; break; case 's': printf("> MAP_SHARED\n"); mflags |= MAP_SHARED; break; case 'r': printf("> read-test\n"); read_test = 1; break; case 'w': printf("> write-test\n"); write_test = 1; break; } } if (optind == argc) { printf("missing file name\n"); return EXIT_FAILURE; } file_name = argv[optind]; if (!(mflags & MAP_SHARED)) { printf("> MAP_PRIVATE\n"); mflags |= MAP_PRIVATE; } if (is_read_only) { oflags = O_RDONLY; mprot = PROT_READ; } else { oflags = O_RDWR; mprot = PROT_READ|PROT_WRITE; } fd = open(file_name, oflags); if (fd == -1) { perror("open failed"); return EXIT_FAILURE; } ret = fstat(fd, &stat); if (ret < 0) { perror("fstat failed"); return EXIT_FAILURE; } size = stat.st_size; printf("> open %s size %#zx flags %#x\n", file_name, size, oflags); ret = posix_memalign(&mptr, MiB(2), size); if (ret ==0) free(mptr); printf("> mmap mprot 0x%x flags 0x%x\n", mprot, mflags); p = mmap(mptr, size, mprot, mflags, fd, 0x0); if (!p) { perror("mmap failed"); return EXIT_FAILURE; } if ((long unsigned)p & (MiB(2)-1)) printf("> mmap: NOT 2MiB aligned: 0x%p\n", p); else printf("> mmap: 2MiB aligned: 0x%p\n", p); cpy_size = size; for (i=0; i<3; i++) { if (is_mlock && !mlock_skip) { printf("> mlock 0x%p\n", p); ret = mlock(p, size); if (ret < 0) { perror("mlock failed"); return EXIT_FAILURE; } } else if (is_mlockall) { printf("> mlockall\n"); ret = mlockall(MCL_CURRENT|MCL_FUTURE); if (ret < 0) { perror("mlockall failed"); return EXIT_FAILURE; } } printf("===== %d =====\n", i+1); if (write_test) test_write(p, cpy_size); if (read_test) test_read(p, cpy_size); if (is_mlock && !mlock_skip) { printf("> munlock 0x%p\n", p); ret = munlock(p, size); if (ret < 0) { perror("munlock failed"); return EXIT_FAILURE; } } else if (is_mlockall) { printf("> munlockall\n"); ret = munlockall(); if (ret < 0) { perror("munlockall failed"); return EXIT_FAILURE; } } /* skip, if requested, only the first iteration */ mlock_skip = 0; } printf("> munmap 0x%p\n", p); munmap(p, size); return EXIT_SUCCESS; } ndctl-67/test/mmap.sh000077500000000000000000000043101355562357700147040ustar00rootroot00000000000000#!/bin/bash # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. . ./common MNT=test_mmap_mnt FILE=image DEV="" TEST=./mmap rc=77 cleanup() { echo "test-mmap: failed at line $1" if [ -n "$DEV" ]; then umount $DEV else rc=77 fi rmdir $MNT exit $rc } test_mmap() { # SHARED $TEST -Mrwps $MNT/$FILE # mlock, populate, shared (mlock fail) $TEST -Arwps $MNT/$FILE # mlockall, populate, shared $TEST -RMrps $MNT/$FILE # read-only, mlock, populate, shared (mlock fail) $TEST -rwps $MNT/$FILE # populate, shared (populate no effect) $TEST -Rrps $MNT/$FILE # read-only populate, shared (populate no effect) $TEST -Mrws $MNT/$FILE # mlock, shared (mlock fail) $TEST -RMrs $MNT/$FILE # read-only, mlock, shared (mlock fail) $TEST -rws $MNT/$FILE # shared (ok) $TEST -Rrs $MNT/$FILE # read-only, shared (ok) # PRIVATE $TEST -Mrwp $MNT/$FILE # mlock, populate, private (ok) $TEST -RMrp $MNT/$FILE # read-only, mlock, populate, private (mlock fail) $TEST -rwp $MNT/$FILE # populate, private (ok) $TEST -Rrp $MNT/$FILE # read-only, populate, private (populate no effect) $TEST -Mrw $MNT/$FILE # mlock, private (ok) $TEST -RMr $MNT/$FILE # read-only, mlock, private (mlock fail) $TEST -MSr $MNT/$FILE # private, read before mlock (ok) $TEST -rw $MNT/$FILE # private (ok) $TEST -Rr $MNT/$FILE # read-only, private (ok) } set -e mkdir -p $MNT trap 'err $LINENO cleanup' ERR dev=$(./dax-dev) json=$($NDCTL list -N -n $dev) eval $(json2var <<< "$json") DEV="/dev/${blockdev}" rc=1 mkfs.ext4 $DEV mount $DEV $MNT -o dax fallocate -l 1GiB $MNT/$FILE test_mmap umount $MNT mkfs.xfs -f $DEV -m reflink=0 mount $DEV $MNT -o dax fallocate -l 1GiB $MNT/$FILE test_mmap umount $MNT ndctl-67/test/monitor.sh000077500000000000000000000070561355562357700154530ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018, FUJITSU LIMITED. All rights reserved. rc=77 monitor_pid=65536 logfile="" conf_file="" monitor_dimms="" monitor_regions="" monitor_namespace="" smart_supported_bus="" . ./common trap 'err $LINENO' ERR check_min_kver "4.15" || do_skip "kernel $KVER may not support monitor service" init() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all } start_monitor() { logfile=$(mktemp) $NDCTL monitor -l $logfile $1 & monitor_pid=$! sync; sleep 3 truncate --size 0 $logfile #remove startup log } set_smart_supported_bus() { smart_supported_bus=$NFIT_TEST_BUS0 monitor_dimms=$(./list-smart-dimm -b $smart_supported_bus | jq -r .[0].dev) if [ -z $monitor_dimms ]; then smart_supported_bus=$NFIT_TEST_BUS1 fi } get_monitor_dimm() { jlist=$(./list-smart-dimm -b $smart_supported_bus $1) monitor_dimms=$(jq '.[]."dev"?, ."dev"?' <<<$jlist | sort | uniq | xargs) echo $monitor_dimms } call_notify() { ./smart-notify $smart_supported_bus sync; sleep 3 } inject_smart() { $NDCTL inject-smart $monitor_dimms $1 sync; sleep 3 } check_result() { jlog=$(cat $logfile) notify_dimms=$(jq ."dimm"."dev" <<<$jlog | sort | uniq | xargs) [[ $1 == $notify_dimms ]] } stop_monitor() { kill $monitor_pid rm $logfile } test_filter_dimm() { monitor_dimms=$(get_monitor_dimm | awk '{print $1}') start_monitor "-d $monitor_dimms" call_notify check_result "$monitor_dimms" stop_monitor } test_filter_bus() { monitor_dimms=$(get_monitor_dimm) start_monitor "-b $smart_supported_bus" call_notify check_result "$monitor_dimms" stop_monitor } test_filter_region() { count=$($NDCTL list -R -b $smart_supported_bus | jq -r .[].dev | wc -l) i=0 while [ $i -lt $count ]; do monitor_region=$($NDCTL list -R -b $smart_supported_bus | jq -r .[$i].dev) monitor_dimms=$(get_monitor_dimm "-r $monitor_region") [ ! -z $monitor_dimms ] && break i=$((i + 1)) done start_monitor "-r $monitor_region" call_notify check_result "$monitor_dimms" stop_monitor } test_filter_namespace() { init monitor_namespace=$($NDCTL create-namespace -b $smart_supported_bus | jq -r .dev) monitor_dimms=$(get_monitor_dimm "-n $monitor_namespace") start_monitor "-n $monitor_namespace" call_notify check_result "$monitor_dimms" stop_monitor $NDCTL destroy-namespace $monitor_namespace -f } test_conf_file() { monitor_dimms=$(get_monitor_dimm) conf_file=$(mktemp) echo "dimm = $monitor_dimms" > $conf_file start_monitor "-c $conf_file" call_notify check_result "$monitor_dimms" stop_monitor rm $conf_file } test_filter_dimmevent() { monitor_dimms="$(get_monitor_dimm | awk '{print $1}')" start_monitor "-d $monitor_dimms -D dimm-unclean-shutdown" inject_smart "-U" check_result "$monitor_dimms" stop_monitor inject_value=$($NDCTL list -H -d $monitor_dimms | jq -r .[]."health"."spares_threshold") inject_value=$((inject_value - 1)) start_monitor "-d $monitor_dimms -D dimm-spares-remaining" inject_smart "-s $inject_value" check_result "$monitor_dimms" stop_monitor inject_value=$($NDCTL list -H -d $monitor_dimms | jq -r .[]."health"."temperature_threshold") inject_value=$((inject_value + 1)) start_monitor "-d $monitor_dimms -D dimm-media-temperature" inject_smart "-m $inject_value" check_result "$monitor_dimms" stop_monitor } do_tests() { test_filter_dimm test_filter_bus test_filter_region test_filter_namespace test_conf_file test_filter_dimmevent } modprobe nfit_test rc=1 init set_smart_supported_bus do_tests _cleanup exit 0 ndctl-67/test/multi-dax.sh000077500000000000000000000024761355562357700156710ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. set -e rc=77 . ./common check_min_kver "4.13" || do_skip "may lack multi-dax support" trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 query=". | sort_by(.available_size) | reverse | .[0].dev" region=$($NDCTL list -b $NFIT_TEST_BUS0 -t pmem -Ri | jq -r "$query") json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a 4096 -s 16M) chardev1=$(echo $json | jq ". | select(.mode == \"devdax\") | .daxregion.devices[0].chardev") json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a 4096 -s 16M) chardev2=$(echo $json | jq ". | select(.mode == \"devdax\") | .daxregion.devices[0].chardev") _cleanup exit 0 ndctl-67/test/multi-pmem.c000066400000000000000000000173441355562357700156600ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_NAMESPACES 4 #define SZ_NAMESPACE SZ_16M static int setup_namespace(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); const char *argv[] = { "__func__", "-v", "-m", "raw", "-s", "16M", "-r", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_region_get_devname(region); builtin_xaction_namespace_reset(); return cmd_create_namespace(argc, argv, ctx); } static void destroy_namespace(struct ndctl_namespace *ndns) { struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); const char *argv[] = { "__func__", "-v", "-f", "", }; int argc = ARRAY_SIZE(argv); argv[argc - 1] = ndctl_namespace_get_devname(ndns); builtin_xaction_namespace_reset(); cmd_destroy_namespace(argc, argv, ctx); } /* Check that the namespace device is gone (if it wasn't the seed) */ static int check_deleted(struct ndctl_region *region, const char *devname, struct ndctl_test *test) { struct ndctl_namespace *ndns; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 10, 0))) return 0; ndctl_namespace_foreach(region, ndns) { if (strcmp(devname, ndctl_namespace_get_devname(ndns))) continue; if (ndns == ndctl_region_get_namespace_seed(region)) continue; fprintf(stderr, "multi-pmem: expected %s to be deleted\n", devname); return -ENXIO; } return 0; } static int do_multi_pmem(struct ndctl_ctx *ctx, struct ndctl_test *test) { int i; char devname[100]; struct ndctl_bus *bus; uuid_t uuid[NUM_NAMESPACES]; struct ndctl_namespace *ndns; struct ndctl_dimm *dimm_target, *dimm; struct ndctl_region *region, *target = NULL; struct ndctl_namespace *namespaces[NUM_NAMESPACES]; unsigned long long blk_avail, blk_avail_orig, expect; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { ndctl_test_skip(test); return 77; } bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); if (!bus) return -ENXIO; /* disable all regions so that set_config_data commands are permitted */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) { int rc = ndctl_dimm_zero_labels(dimm); if (rc < 0) { fprintf(stderr, "failed to zero %s\n", ndctl_dimm_get_devname(dimm)); return rc; } } /* * Set regions back to their default state and find our target * region. */ ndctl_region_foreach(bus, region) { ndctl_region_enable(region); if (ndctl_region_get_available_size(region) == SZ_NAMESPACE * NUM_NAMESPACES) target = region; } if (!target) { fprintf(stderr, "multi-pmem: failed to find target region\n"); return -ENXIO; } region = target; for (i = 0; i < (int) ARRAY_SIZE(uuid); i++) { if (setup_namespace(region) != 0) { fprintf(stderr, "multi-pmem: failed to setup namespace: %d\n", i); return -ENXIO; } sprintf(devname, "namespace%d.%d", ndctl_region_get_id(region), i); ndctl_namespace_foreach(region, ndns) if (strcmp(ndctl_namespace_get_devname(ndns), devname) == 0 && ndctl_namespace_is_enabled(ndns)) break; if (!ndns) { fprintf(stderr, "multi-pmem: failed to find namespace: %s\n", devname); return -ENXIO; } ndctl_namespace_get_uuid(ndns, uuid[i]); } /* bounce the region and verify everything came back as expected */ ndctl_region_disable_invalidate(region); ndctl_region_enable(region); for (i = 0; i < (int) ARRAY_SIZE(uuid); i++) { char uuid_str1[40], uuid_str2[40]; uuid_t uuid_check; ndctl_namespace_foreach(region, ndns) sprintf(devname, "namespace%d.%d", ndctl_region_get_id(region), i); ndctl_namespace_foreach(region, ndns) if (strcmp(ndctl_namespace_get_devname(ndns), devname) == 0 && ndctl_namespace_is_enabled(ndns)) break; if (!ndns) { fprintf(stderr, "multi-pmem: failed to restore namespace: %s\n", devname); return -ENXIO; } ndctl_namespace_get_uuid(ndns, uuid_check); uuid_unparse(uuid_check, uuid_str2); uuid_unparse(uuid[i], uuid_str1); if (uuid_compare(uuid_check, uuid[i]) != 0) { fprintf(stderr, "multi-pmem: expected uuid[%d]: %s, got %s\n", i, uuid_str1, uuid_str2); return -ENXIO; } namespaces[i] = ndns; } /* * Check that aliased blk capacity does not increase until the * highest dpa pmem-namespace is deleted. */ dimm_target = ndctl_region_get_first_dimm(region); if (!dimm_target) { fprintf(stderr, "multi-pmem: failed to retrieve dimm from %s\n", ndctl_region_get_devname(region)); return -ENXIO; } dimm = NULL; ndctl_region_foreach(bus, region) { if (ndctl_region_get_type(region) != ND_DEVICE_REGION_BLK) continue; ndctl_dimm_foreach_in_region(region, dimm) if (dimm == dimm_target) break; if (dimm) break; } blk_avail_orig = ndctl_region_get_available_size(region); for (i = 1; i < NUM_NAMESPACES - 1; i++) { ndns = namespaces[i]; sprintf(devname, "%s", ndctl_namespace_get_devname(ndns)); destroy_namespace(ndns); blk_avail = ndctl_region_get_available_size(region); if (blk_avail != blk_avail_orig) { fprintf(stderr, "multi-pmem: destroy %s %llx avail, expect %llx\n", devname, blk_avail, blk_avail_orig); return -ENXIO; } if (check_deleted(target, devname, test) != 0) return -ENXIO; } ndns = namespaces[NUM_NAMESPACES - 1]; sprintf(devname, "%s", ndctl_namespace_get_devname(ndns)); destroy_namespace(ndns); blk_avail = ndctl_region_get_available_size(region); expect = (SZ_NAMESPACE / ndctl_region_get_interleave_ways(target)) * (NUM_NAMESPACES - 1) + blk_avail_orig; if (blk_avail != expect) { fprintf(stderr, "multi-pmem: destroy %s %llx avail, expect %llx\n", devname, blk_avail, expect); return -ENXIO; } if (check_deleted(target, devname, test) != 0) return -ENXIO; ndctl_bus_foreach(ctx, bus) { if (strncmp(ndctl_bus_get_provider(bus), "nfit_test", 9) != 0) continue; ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); } return 0; } int test_multi_pmem(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { struct kmod_module *mod; struct kmod_ctx *kmod_ctx; int err, result = EXIT_FAILURE; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) return 77; ndctl_set_log_priority(ctx, loglevel); err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); if (err < 0) { result = 77; ndctl_test_skip(test); fprintf(stderr, "%s unavailable skipping tests\n", "nfit_test"); return result; } result = do_multi_pmem(ctx, test); kmod_module_remove_module(mod, 0); kmod_unref(kmod_ctx); return result; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_multi_pmem(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/nmem1.bin000066400000000000000000004000001355562357700151160ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-67/test/nmem2.bin000066400000000000000000004000001355562357700151170ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-67/test/nmem3.bin000066400000000000000000004000001355562357700151200ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-67/test/nmem4.bin000066400000000000000000004000001355562357700151210ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-67/test/parent-uuid.c000066400000000000000000000144161355562357700160240ustar00rootroot00000000000000/* * blk_namespaces: tests functionality of multiple block namespaces * * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *PROVIDER = "nfit_test.0"; static struct ndctl_bus *get_bus_by_provider(struct ndctl_ctx *ctx, const char *provider) { struct ndctl_bus *bus; ndctl_bus_foreach(ctx, bus) if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0) return bus; return NULL; } static struct ndctl_btt *get_idle_btt(struct ndctl_region *region) { struct ndctl_btt *btt; ndctl_btt_foreach(region, btt) if (!ndctl_btt_is_enabled(btt) && !ndctl_btt_is_configured(btt)) return btt; return NULL; } static struct ndctl_namespace *create_blk_namespace(int region_fraction, struct ndctl_region *region, unsigned long long req_size, uuid_t uuid) { struct ndctl_namespace *ndns, *seed_ns = NULL; unsigned long long size; ndctl_namespace_foreach(region, ndns) if (ndctl_namespace_get_size(ndns) == 0) { seed_ns = ndns; break; } if (!seed_ns) return NULL; size = ndctl_region_get_size(region)/region_fraction; if (req_size) size = req_size; if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0) return NULL; if (ndctl_namespace_set_size(seed_ns, size) < 0) return NULL; if (ndctl_namespace_set_sector_size(seed_ns, 512) < 0) return NULL; if (ndctl_namespace_enable(seed_ns) < 0) return NULL; return seed_ns; } static int disable_blk_namespace(struct ndctl_namespace *ndns) { if (ndctl_namespace_disable_invalidate(ndns) < 0) return -ENODEV; if (ndctl_namespace_delete(ndns) < 0) return -ENODEV; return 0; } static struct ndctl_btt *check_valid_btt(struct ndctl_region *region, struct ndctl_namespace *ndns, uuid_t btt_uuid) { struct ndctl_btt *btt = NULL; ndctl_btt_foreach(region, btt) { struct ndctl_namespace *btt_ndns; uuid_t uu; ndctl_btt_get_uuid(btt, uu); if (uuid_compare(uu, btt_uuid) != 0) continue; if (!ndctl_btt_is_enabled(btt)) continue; btt_ndns = ndctl_btt_get_namespace(btt); if (strcmp(ndctl_namespace_get_devname(btt_ndns), ndctl_namespace_get_devname(ndns)) != 0) continue; return btt; } return NULL; } static int do_test(struct ndctl_ctx *ctx) { int rc; struct ndctl_bus *bus; struct ndctl_btt *btt, *found = NULL, *_btt; struct ndctl_region *region, *blk_region = NULL; struct ndctl_namespace *ndns, *_ndns; unsigned long long ns_size = 18874368; uuid_t uuid = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}; uuid_t btt_uuid; bus = get_bus_by_provider(ctx, PROVIDER); if (!bus) { fprintf(stderr, "failed to find NFIT-provider: %s\n", PROVIDER); return -ENODEV; } ndctl_region_foreach(bus, region) if (strcmp(ndctl_region_get_type_name(region), "blk") == 0) { blk_region = region; break; } if (!blk_region) { fprintf(stderr, "failed to find block region\n"); return -ENODEV; } /* create a blk namespace */ ndns = create_blk_namespace(1, blk_region, ns_size, uuid); if (!ndns) { fprintf(stderr, "failed to create block namespace\n"); return -ENXIO; } /* create a btt for this namespace */ uuid_generate(btt_uuid); btt = get_idle_btt(region); if (!btt) return -ENXIO; ndctl_namespace_disable_invalidate(ndns); ndctl_btt_set_uuid(btt, btt_uuid); ndctl_btt_set_sector_size(btt, 512); ndctl_btt_set_namespace(btt, ndns); rc = ndctl_btt_enable(btt); if (rc) { fprintf(stderr, "failed to create btt 0\n"); return rc; } /* re-create the namespace - this should auto-enable the btt */ disable_blk_namespace(ndns); ndns = create_blk_namespace(1, blk_region, ns_size, uuid); if (!ndns) { fprintf(stderr, "failed to re-create block namespace\n"); return -ENXIO; } /* Verify btt was auto-created */ found = check_valid_btt(blk_region, ndns, btt_uuid); if (!found) return -ENXIO; btt = found; /*disable the btt and namespace again */ ndctl_btt_delete(btt); disable_blk_namespace(ndns); /* recreate the namespace with a different uuid */ uuid_generate(uuid); ndns = create_blk_namespace(1, blk_region, ns_size, uuid); if (!ndns) { fprintf(stderr, "failed to re-create block namespace\n"); return -ENXIO; } /* make sure there is no btt on this namespace */ found = check_valid_btt(blk_region, ndns, btt_uuid); if (found) { fprintf(stderr, "found a stale btt\n"); return -ENXIO; } ndctl_btt_foreach_safe(blk_region, btt, _btt) ndctl_btt_delete(btt); ndctl_namespace_foreach_safe(blk_region, ndns, _ndns) if (ndctl_namespace_get_size(ndns) != 0) disable_blk_namespace(ndns); ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); return 0; } int test_parent_uuid(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) { struct kmod_module *mod; struct kmod_ctx *kmod_ctx; int err, result = EXIT_FAILURE; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 3, 0))) return 77; ndctl_set_log_priority(ctx, loglevel); err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); if (err < 0) { ndctl_test_skip(test); fprintf(stderr, "nfit_test unavailable skipping tests\n"); return 77; } err = do_test(ctx); if (err == 0) result = EXIT_SUCCESS; kmod_module_remove_module(mod, 0); kmod_unref(kmod_ctx); return result; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_parent_uuid(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/pfn-meta-errors.sh000077500000000000000000000033021355562357700167730ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018 Intel Corporation. All rights reserved. blockdev="" rc=77 . ./common force_raw() { raw="$1" $NDCTL disable-namespace "$dev" echo "$raw" > "/sys/bus/nd/devices/$dev/force_raw" $NDCTL enable-namespace "$dev" echo "Set $dev to raw mode: $raw" if [[ "$raw" == "1" ]]; then raw_bdev=${blockdev} test -b "/dev/$raw_bdev" else raw_bdev="" fi } check_min_kver "4.20" || do_skip "may lack PFN metadata error handling" set -e trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 # create a fsdax namespace and clear errors (if any) dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m fsdax) eval "$(echo "$json" | json2var)" [ $dev = "x" ] && echo "fail: $LINENO" && exit 1 force_raw 1 if read -r sector len < "/sys/block/$raw_bdev/badblocks"; then dd of=/dev/$raw_bdev if=/dev/zero oflag=direct bs=512 seek="$sector" count="$len" fi force_raw 0 # find dataoff from sb force_raw 1 doff=$(hexdump -s $((4096 + 56)) -n 4 "/dev/$raw_bdev" | head -1 | cut -d' ' -f2-) doff=$(tr -d ' ' <<< "0x${doff#* }${doff%% *}") printf "pfn dataoff: %x\n" "$doff" dblk="$((doff/512))" metaoff="0x2000" mblk="$((metaoff/512))" # inject in the middle of the struct page area bb_inj=$(((dblk - mblk)/2)) $NDCTL inject-error --block="$bb_inj" --count=32 $dev $NDCTL start-scrub && $NDCTL wait-scrub # after probe from the enable-namespace, the error should've been cleared force_raw 0 force_raw 1 if read -r sector len < "/sys/block/$raw_bdev/badblocks"; then false fi _cleanup exit 0 ndctl-67/test/pmem-errors.sh000077500000000000000000000070461355562357700162330ustar00rootroot00000000000000#!/bin/bash -x # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. MNT=test_dax_mnt FILE=image rc=77 . ./common cleanup() { rm -f $FILE rm -f $MNT/$FILE if [ -n "$blockdev" ]; then umount /dev/$blockdev else rc=77 fi rmdir $MNT } check_min_kver "4.7" || do_skip "may lack dax error handling" set -e mkdir -p $MNT trap 'err $LINENO cleanup' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all rc=1 # create pmem dev="x" json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m raw) eval $(echo $json | json2var) [ $dev = "x" ] && echo "fail: $LINENO" && false [ $mode != "raw" ] && echo "fail: $LINENO" && false # inject errors in the middle of the namespace, verify that reading fails err_sector="$(((size/512) / 2))" err_count=8 if ! read sector len < /sys/block/$blockdev/badblocks; then $NDCTL inject-error --block="$err_sector" --count=$err_count $dev $NDCTL start-scrub; $NDCTL wait-scrub fi read sector len < /sys/block/$blockdev/badblocks [ $((sector * 2)) -ne $((size /512)) ] && echo "fail: $LINENO" && false if dd if=/dev/$blockdev of=/dev/null iflag=direct bs=512 skip=$sector count=$len; then echo "fail: $LINENO" && false fi # check that writing clears the errors if ! dd of=/dev/$blockdev if=/dev/zero oflag=direct bs=512 seek=$sector count=$len; then echo "fail: $LINENO" && false fi if read sector len < /sys/block/$blockdev/badblocks; then # fail if reading badblocks returns data echo "fail: $LINENO" && false fi #mkfs.xfs /dev/$blockdev -b size=4096 -f mkfs.ext4 /dev/$blockdev -b 4096 mount /dev/$blockdev $MNT # prepare an image file with random data dd if=/dev/urandom of=$MNT/$FILE bs=4096 count=4 oflag=direct # Get the start sector for the file start_sect=$(filefrag -v -b512 $MNT/$FILE | grep -E "^[ ]+[0-9]+.*" | head -1 | awk '{ print $4 }' | cut -d. -f1) test -n "$start_sect" echo "start sector of the file is $start_sect" # inject badblocks for one page at the start of the file echo $start_sect 8 > /sys/block/$blockdev/badblocks # make sure reading the first block of the file fails as expected : The following 'dd' is expected to hit an I/O Error dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true # run the dax-errors test test -x ./dax-errors ./dax-errors $MNT/$FILE # TODO: disable this check till we have clear-on-write in the kernel #if read sector len < /sys/block/$blockdev/badblocks; then # # fail if reading badblocks returns data # echo "fail: $LINENO" && false #fi # TODO Due to the above, we have to clear the existing badblock manually read sector len < /sys/block/$blockdev/badblocks if ! dd of=/dev/$blockdev if=/dev/zero oflag=direct bs=512 seek=$sector count=$len; then echo "fail: $LINENO" && false fi # test that a hole punch to a dax file also clears errors dd if=/dev/urandom of=$MNT/$FILE oflag=direct bs=4096 count=4 start_sect=$(filefrag -v -b512 $MNT/$FILE | grep -E "^[ ]+[0-9]+.*" | head -1 | awk '{ print $4 }' | cut -d. -f1) test -n "$start_sect" echo "holepunch test: start sector: $start_sect" # inject a badblock at the second sector of the first page echo $((start_sect + 1)) 1 > /sys/block/$blockdev/badblocks # verify badblock by reading : The following 'dd' is expected to hit an I/O Error dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true # cleanup rm -f $FILE rm -f $MNT/$FILE if [ -n "$blockdev" ]; then umount /dev/$blockdev fi rmdir $MNT _cleanup exit 0 ndctl-67/test/pmem_namespaces.c000066400000000000000000000152541355562357700167250ustar00rootroot00000000000000/* * pmem_namespaces: test functionality of PMEM namespaces * * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define err(msg)\ fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno)) static struct ndctl_namespace *create_pmem_namespace(struct ndctl_region *region) { struct ndctl_namespace *seed_ns = NULL; unsigned long long size; uuid_t uuid; seed_ns = ndctl_region_get_namespace_seed(region); if (!seed_ns) return NULL; uuid_generate(uuid); size = ndctl_region_get_size(region); if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0) return NULL; if (ndctl_namespace_set_size(seed_ns, size) < 0) return NULL; if (ndctl_namespace_enable(seed_ns) < 0) return NULL; return seed_ns; } static int disable_pmem_namespace(struct ndctl_namespace *ndns) { if (ndctl_namespace_disable_invalidate(ndns) < 0) return -ENODEV; if (ndctl_namespace_delete(ndns) < 0) return -ENODEV; return 0; } static int ns_do_io(const char *bdev) { unsigned long num_dev_pages, num_blocks; const int page_size = 4096; const int num_pages = 2; off_t addr; int rc = 0; int fd, i; void *random_page[num_pages]; void *pmem_page[num_pages]; rc = posix_memalign(random_page, page_size, page_size * num_pages); if (rc) { fprintf(stderr, "posix_memalign failure\n"); return rc; } rc = posix_memalign(pmem_page, page_size, page_size * num_pages); if (rc) { fprintf(stderr, "posix_memalign failure\n"); goto err_free_pmem; } for (i = 1; i < num_pages; i++) { random_page[i] = (char*)random_page[0] + page_size * i; pmem_page[i] = (char*)pmem_page[0] + page_size * i; } /* read random data into random_page */ if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { err("open"); rc = -ENODEV; goto err_free_all; } rc = read(fd, random_page[0], page_size * num_pages); if (rc < 0) { err("read"); close(fd); goto err_free_all; } close(fd); /* figure out our dev size */ if ((fd = open(bdev, O_RDWR|O_DIRECT)) < 0) { err("open"); rc = -ENODEV; goto err_free_all; } ioctl(fd, BLKGETSIZE, &num_blocks); num_dev_pages = num_blocks / 8; /* write the random data out to each of the segments */ rc = pwrite(fd, random_page[0], page_size, 0); if (rc < 0) { err("write"); goto err_close; } addr = page_size * (num_dev_pages - 1); rc = pwrite(fd, random_page[1], page_size, addr); if (rc < 0) { err("write"); goto err_close; } /* read back the random data into pmem_page */ rc = pread(fd, pmem_page[0], page_size, 0); if (rc < 0) { err("read"); goto err_close; } addr = page_size * (num_dev_pages - 1); rc = pread(fd, pmem_page[1], page_size, addr); if (rc < 0) { err("read"); goto err_close; } /* verify the data */ if (memcmp(random_page[0], pmem_page[0], page_size * num_pages)) { fprintf(stderr, "PMEM data miscompare\n"); rc = -EIO; goto err_close; } rc = 0; err_close: close(fd); err_free_all: free(random_page[0]); err_free_pmem: free(pmem_page[0]); return rc; } static const char *comm = "test-pmem-namespaces"; int test_pmem_namespaces(int log_level, struct ndctl_test *test, struct ndctl_ctx *ctx) { struct ndctl_region *region, *pmem_region = NULL; struct kmod_ctx *kmod_ctx = NULL; struct kmod_module *mod = NULL; struct ndctl_namespace *ndns; struct ndctl_dimm *dimm; struct ndctl_bus *bus; int rc = -ENXIO; char bdev[50]; if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) return 77; ndctl_set_log_priority(ctx, log_level); bus = ndctl_bus_get_by_provider(ctx, "ACPI.NFIT"); if (bus) { /* skip this bus if no label-enabled PMEM regions */ ndctl_region_foreach(bus, region) if (ndctl_region_get_nstype(region) == ND_DEVICE_NAMESPACE_PMEM) break; if (!region) bus = NULL; } if (!bus) { fprintf(stderr, "ACPI.NFIT unavailable falling back to nfit_test\n"); rc = nfit_test_init(&kmod_ctx, &mod, NULL, log_level, test); ndctl_invalidate(ctx); bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); if (rc < 0 || !bus) { rc = 77; ndctl_test_skip(test); fprintf(stderr, "nfit_test unavailable skipping tests\n"); goto err_module; } } fprintf(stderr, "%s: found provider: %s\n", comm, ndctl_bus_get_provider(bus)); /* get the system to a clean state */ ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); ndctl_dimm_foreach(bus, dimm) { rc = ndctl_dimm_zero_labels(dimm); if (rc < 0) { fprintf(stderr, "failed to zero %s\n", ndctl_dimm_get_devname(dimm)); goto err; } } /* create our config */ ndctl_region_foreach(bus, region) if (strcmp(ndctl_region_get_type_name(region), "pmem") == 0) { pmem_region = region; break; } if (!pmem_region || ndctl_region_enable(pmem_region) < 0) { fprintf(stderr, "%s: failed to find PMEM region\n", comm); rc = -ENODEV; goto err; } rc = -ENODEV; ndns = create_pmem_namespace(pmem_region); if (!ndns) { fprintf(stderr, "%s: failed to create PMEM namespace\n", comm); goto err; } sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns)); rc = ns_do_io(bdev); disable_pmem_namespace(ndns); err: /* unload nfit_test */ bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); if (bus) ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); bus = ndctl_bus_get_by_provider(ctx, "nfit_test.1"); if (bus) ndctl_region_foreach(bus, region) ndctl_region_disable_invalidate(region); kmod_module_remove_module(mod, 0); err_module: kmod_unref(kmod_ctx); return rc; } int __attribute__((weak)) main(int argc, char *argv[]) { struct ndctl_test *test = ndctl_test_new(0); struct ndctl_ctx *ctx; int rc; comm = argv[0]; if (!test) { fprintf(stderr, "failed to initialize test\n"); return EXIT_FAILURE; } rc = ndctl_new(&ctx); if (rc) return ndctl_test_result(test, rc); rc = test_pmem_namespaces(LOG_DEBUG, test, ctx); ndctl_unref(ctx); return ndctl_test_result(test, rc); } ndctl-67/test/rescan-partitions.sh000077500000000000000000000032161355562357700174230ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018 Intel Corporation. All rights reserved. dev="" size="" blockdev="" rc=77 . ./common trap 'err $LINENO' ERR # sample json: #{ # "dev":"namespace5.0", # "mode":"sector", # "size":"60.00 MiB (62.92 MB)", # "uuid":"f1baa71a-d165-4da4-bb6a-083a2b0e6469", # "blockdev":"pmem5s", #} check_min_kver "4.16" || do_skip "may not contain fixes for partition rescanning" check_prereq "parted" check_prereq "blockdev" reset() { $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all } test_mode() { local mode="$1" # create namespace json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m "$mode") rc=2 eval "$(echo "$json" | json2var)" [ -n "$dev" ] || err "$LINENO" [ -n "$size" ] || err "$LINENO" [ -n "$blockdev" ] || err "$LINENO" [ $size -gt 0 ] || err "$LINENO" rc=1 # create partition parted --script /dev/$blockdev mklabel gpt mkpart primary 1MiB 10MiB # verify it is created sleep 1 blockdev --rereadpt /dev/$blockdev sleep 1 partdev="$(grep -Eo "${blockdev}.+" /proc/partitions)" test -b /dev/$partdev # cycle the namespace, and verify the partition is read # without needing to do a blockdev --rereadpt $NDCTL disable-namespace $dev $NDCTL enable-namespace $dev if [ -b /dev/$partdev ]; then echo "mode: $mode - partition read successful" else echo "mode: $mode - partition read failed" rc=1 err "$LINENO" fi $NDCTL disable-namespace $dev $NDCTL destroy-namespace $dev } modprobe nfit_test rc=1 reset test_mode "raw" test_mode "fsdax" test_mode "sector" _cleanup exit 0 ndctl-67/test/sector-mode.sh000077500000000000000000000025021355562357700161740ustar00rootroot00000000000000#!/bin/bash -x # Copyright(c) 2015-2017 Intel Corporation. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License 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. rc=77 . ./common set -e trap 'err $LINENO' ERR # setup (reset nfit_test dimms) modprobe nfit_test $NDCTL disable-region -b $NFIT_TEST_BUS0 all $NDCTL zero-labels -b $NFIT_TEST_BUS0 all $NDCTL enable-region -b $NFIT_TEST_BUS0 all $NDCTL disable-region -b $NFIT_TEST_BUS1 all $NDCTL zero-labels -b $NFIT_TEST_BUS1 all $NDCTL enable-region -b $NFIT_TEST_BUS1 all rc=1 query=". | sort_by(.size) | reverse | .[0].dev" NAMESPACE=$($NDCTL list -b $NFIT_TEST_BUS1 -N | jq -r "$query") REGION=$($NDCTL list -R --namespace=$NAMESPACE | jq -r "(.[]) | .dev") echo 0 > /sys/bus/nd/devices/$REGION/read_only $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m sector -f -l 4K $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m dax -f -a 4K $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m sector -f -l 4K _cleanup exit 0 ndctl-67/test/security.sh000077500000000000000000000142741355562357700156330ustar00rootroot00000000000000#!/bin/bash -Ex # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2018 Intel Corporation. All rights reserved. rc=77 dev="" id="" keypath="/etc/ndctl/keys" masterkey="nvdimm-master" masterpath="$keypath/$masterkey.blob" backup_key=0 backup_handle=0 . ./common trap 'err $LINENO' ERR setup() { $NDCTL disable-region -b "$NFIT_TEST_BUS0" all } detect() { dev="$($NDCTL list -b "$NFIT_TEST_BUS0" -D | jq -r .[0].dev)" [ -n "$dev" ] || err "$LINENO" id="$($NDCTL list -b "$NFIT_TEST_BUS0" -D | jq -r .[0].id)" [ -n "$id" ] || err "$LINENO" } setup_keys() { if [ ! -d "$keypath" ]; then mkdir -p "$keypath" fi if [ -f "$masterpath" ]; then mv "$masterpath" "$masterpath.bak" backup_key=1 fi if [ -f "$keypath/tpm.handle" ]; then mv "$keypath/tpm.handle" "$keypath/tmp.handle.bak" backup_handle=1 fi dd if=/dev/urandom bs=1 count=32 2>/dev/null | keyctl padd user "$masterkey" @u keyctl pipe "$(keyctl search @u user $masterkey)" > "$masterpath" } test_cleanup() { if keyctl search @u encrypted nvdimm:"$id"; then keyctl unlink "$(keyctl search @u encrypted nvdimm:"$id")" fi if keyctl search @u user "$masterkey"; then keyctl unlink "$(keyctl search @u user "$masterkey")" fi if [ -f "$keypath"/nvdimm_"$id"_"$(hostname)".blob ]; then rm -f "$keypath"/nvdimm_"$id"_"$(hostname)".blob fi } post_cleanup() { if [ -f $masterpath ]; then rm -f "$masterpath" fi if [ "$backup_key" -eq 1 ]; then mv "$masterpath.bak" "$masterpath" fi if [ "$backup_handle" -eq 1 ]; then mv "$keypath/tpm.handle.bak" "$keypath/tmp.handle" fi } lock_dimm() { $NDCTL disable-dimm "$dev" # convert nmemX --> test_dimmY # For now this is the only user of such a conversion so we can leave it # inline. Once a subsequent user arrives we can refactor this to a # helper in test/common: # get_test_dimm_path "nfit_test.0" "nmem3" handle="$($NDCTL list -b "$NFIT_TEST_BUS0" -d "$dev" -i | jq -r .[].dimms[0].handle)" test_dimm_path="" for test_dimm in /sys/devices/platform/"$NFIT_TEST_BUS0"/nfit_test_dimm/test_dimm*; do td_handle_file="$test_dimm/handle" test -e "$td_handle_file" || continue td_handle="$(cat "$td_handle_file")" if [[ "$td_handle" -eq "$handle" ]]; then test_dimm_path="$test_dimm" break fi done test -d "$test_dimm_path" # now lock the dimm echo 1 > "${test_dimm_path}/lock_dimm" sstate="$(get_security_state)" if [ "$sstate" != "locked" ]; then echo "Incorrect security state: $sstate expected: locked" err "$LINENO" fi } get_frozen_state() { $NDCTL list -i -b "$NFIT_TEST_BUS0" -d "$dev" | jq -r .[].dimms[0].security_frozen } get_security_state() { $NDCTL list -i -b "$NFIT_TEST_BUS0" -d "$dev" | jq -r .[].dimms[0].security } setup_passphrase() { $NDCTL setup-passphrase "$dev" -k user:"$masterkey" sstate="$(get_security_state)" if [ "$sstate" != "unlocked" ]; then echo "Incorrect security state: $sstate expected: unlocked" err "$LINENO" fi } remove_passphrase() { $NDCTL remove-passphrase "$dev" sstate="$(get_security_state)" if [ "$sstate" != "disabled" ]; then echo "Incorrect security state: $sstate expected: disabled" err "$LINENO" fi } erase_security() { $NDCTL sanitize-dimm -c "$dev" sstate="$(get_security_state)" if [ "$sstate" != "disabled" ]; then echo "Incorrect security state: $sstate expected: disabled" err "$LINENO" fi } update_security() { $NDCTL update-passphrase "$dev" sstate="$(get_security_state)" if [ "$sstate" != "unlocked" ]; then echo "Incorrect security state: $sstate expected: unlocked" err "$LINENO" fi } freeze_security() { $NDCTL freeze-security "$dev" } test_1_security_setup_and_remove() { setup_passphrase remove_passphrase } test_2_security_setup_and_update() { setup_passphrase update_security remove_passphrase } test_3_security_setup_and_erase() { setup_passphrase erase_security } test_4_security_unlock() { setup_passphrase lock_dimm $NDCTL enable-dimm "$dev" sstate="$(get_security_state)" if [ "$sstate" != "unlocked" ]; then echo "Incorrect security state: $sstate expected: unlocked" err "$LINENO" fi $NDCTL disable-region -b "$NFIT_TEST_BUS0" all remove_passphrase } # This should always be the last nvdimm security test. # with security frozen, nfit_test must be removed and is no longer usable test_5_security_freeze() { setup_passphrase freeze_security sstate="$(get_security_state)" fstate="$(get_frozen_state)" if [ "$fstate" != "true" ]; then echo "Incorrect security state: expected: frozen" err "$LINENO" fi $NDCTL remove-passphrase "$dev" && { echo "remove succeed after frozen"; } sstate2="$(get_security_state)" if [ "$sstate" != "$sstate2" ]; then echo "Incorrect security state: $sstate2 expected: $sstate" err "$LINENO" fi } test_6_load_keys() { if keyctl search @u encrypted nvdimm:"$id"; then keyctl unlink "$(keyctl search @u encrypted nvdimm:"$id")" fi if keyctl search @u user "$masterkey"; then keyctl unlink "$(keyctl search @u user "$masterkey")" fi $NDCTL load-keys if keyctl search @u user "$masterkey"; then echo "master key loaded" else echo "master key failed to loaded" err "$LINENO" fi if keyctl search @u encrypted nvdimm:"$id"; then echo "dimm key loaded" else echo "dimm key failed to load" err "$LINENO" fi } check_min_kver "5.0" || do_skip "may lack security handling" uid="$(keyctl show | grep -Eo "_uid.[0-9]+" | head -1 | cut -d. -f2-)" if [ "$uid" -ne 0 ]; then do_skip "run as root or with a sudo login shell for test to work" fi modprobe nfit_test setup check_prereq "keyctl" rc=1 detect test_cleanup setup_keys echo "Test 1, security setup and remove" test_1_security_setup_and_remove echo "Test 2, security setup, update, and remove" test_2_security_setup_and_update echo "Test 3, security setup and erase" test_3_security_setup_and_erase echo "Test 4, unlock dimm" test_4_security_unlock # Freeze should always be the last nvdimm security test because it locks # security state and require nfit_test module unload. However, this does # not impact any key management testing via libkeyctl. echo "Test 5, freeze security" test_5_security_freeze # Load-keys is independent of actual nvdimm security and is part of key # mangement testing. echo "Test 6, test load-keys" test_6_load_keys test_cleanup post_cleanup _cleanup exit 0 ndctl-67/test/smart-listen.c000066400000000000000000000040371355562357700162070ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2017 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include static void dimm_listen(struct ndctl_bus *bus) { struct ndctl_dimm *dimm, **dimms; int count = 0, maxfd = -1, i, rc; struct pollfd *poll_ents, *p; char buf; ndctl_dimm_foreach(bus, dimm) { int fd = ndctl_dimm_get_health_eventfd(dimm); if (fd > maxfd) maxfd = fd; count++; } if (!count) { fprintf(stderr, "no dimms on bus: %s\n", ndctl_bus_get_provider(bus)); return; } poll_ents = calloc(count, sizeof(struct pollfd)); dimms = calloc(maxfd + 1, sizeof(struct ndctl_dimm *)); if (!poll_ents) goto out; if (!dimms) goto out; i = 0; ndctl_dimm_foreach(bus, dimm) { int fd = ndctl_dimm_get_health_eventfd(dimm); p = &poll_ents[i++]; p->fd = fd; dimms[fd] = dimm; if (i > count) { fprintf(stderr, "dimm count changed!?\n"); goto out; } } retry: for (i = 0; i < count; i++) { p = &poll_ents[i]; dimm = dimms[p->fd]; if (pread(p->fd, &buf, 1, 0) != 1) { fprintf(stderr, "%s: failed to read\n", ndctl_dimm_get_devname(dimm)); goto out; } if (p->revents) fprintf(stderr, "%s: smart event: %d\n", ndctl_dimm_get_devname(dimm), p->revents); p->revents = 0; } rc = poll(poll_ents, count, -1); if (rc <= 0) { fprintf(stderr, "failed to poll\n"); goto out; } goto retry; out: free(poll_ents); free(dimms); } int main(int argc, char *argv[]) { struct ndctl_ctx *ctx; struct ndctl_bus *bus; int rc = EXIT_FAILURE; const char *provider; rc = ndctl_new(&ctx); if (rc < 0) return EXIT_FAILURE; if (argc != 2) { fprintf(stderr, "usage: smart-notify \n"); goto out; } provider = argv[1]; bus = ndctl_bus_get_by_provider(ctx, provider); if (!bus) { fprintf(stderr, "smart-notify: unable to find bus (%s)\n", provider); goto out; } rc = EXIT_SUCCESS; dimm_listen(bus); out: ndctl_unref(ctx); return rc; } ndctl-67/test/smart-notify.c000066400000000000000000000134051355562357700162200ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2017 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include static void do_notify(struct ndctl_dimm *dimm) { struct ndctl_cmd *s_cmd = ndctl_dimm_cmd_new_smart(dimm); struct ndctl_cmd *st_cmd = NULL, *sst_cmd = NULL; unsigned int orig_mtemp, orig_ctemp, orig_spare; const char *name = ndctl_dimm_get_devname(dimm); unsigned int alarm, mtemp, ctemp, spare, valid; int rc, i; if (!s_cmd) { fprintf(stderr, "%s: no smart command support\n", name); goto out; } rc = ndctl_cmd_submit(s_cmd); if (rc < 0) { fprintf(stderr, "%s: smart command failed: %d %s\n", name, rc, strerror(errno)); goto out; } valid = ndctl_cmd_smart_get_flags(s_cmd); alarm = ndctl_cmd_smart_get_alarm_flags(s_cmd); mtemp = ndctl_cmd_smart_get_media_temperature(s_cmd); ctemp = ndctl_cmd_smart_get_ctrl_temperature(s_cmd); spare = ndctl_cmd_smart_get_spares(s_cmd); fprintf(stderr, "%s: (smart) alarm%s: %#x mtemp%s: %.2f ctemp%s: %.2f spares%s: %d\n", name, valid & ND_SMART_ALARM_VALID ? "" : "(invalid)", alarm, valid & ND_SMART_MTEMP_VALID ? "" : "(invalid)", ndctl_decode_smart_temperature(mtemp), valid & ND_SMART_CTEMP_VALID ? "" : "(invalid)", ndctl_decode_smart_temperature(ctemp), valid & ND_SMART_SPARES_VALID ? "" : "(invalid)", spare); st_cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); if (!st_cmd) { fprintf(stderr, "%s: no smart threshold command support\n", name); goto out; } rc = ndctl_cmd_submit(st_cmd); if (rc < 0) { fprintf(stderr, "%s: smart threshold command failed: %d %s\n", name, rc, strerror(errno)); goto out; } alarm = ndctl_cmd_smart_threshold_get_alarm_control(st_cmd); mtemp = ndctl_cmd_smart_threshold_get_media_temperature(st_cmd); ctemp = ndctl_cmd_smart_threshold_get_ctrl_temperature(st_cmd); spare = ndctl_cmd_smart_threshold_get_spares(st_cmd); fprintf(stderr, "%s: (smart thresh) alarm: %#x mtemp: %.2f ctemp: %.2f spares: %d\n", name, alarm, ndctl_decode_smart_temperature(mtemp), ndctl_decode_smart_temperature(ctemp), spare); orig_mtemp = mtemp; orig_ctemp = ctemp; orig_spare = spare; sst_cmd = ndctl_dimm_cmd_new_smart_set_threshold(st_cmd); if (!sst_cmd) { fprintf(stderr, "%s: no smart set threshold command support\n", name); goto out; } alarm = ndctl_cmd_smart_threshold_get_supported_alarms(sst_cmd); if (!alarm) { fprintf(stderr, "%s: no smart set threshold command support\n", name); goto out; } fprintf(stderr, "%s: supported alarms: %#x\n", name, alarm); /* * free the cmd now since we only needed the alarms and will * create + issue a set_threshold test for each alarm */ ndctl_cmd_unref(sst_cmd); for (i = 0; i < 3; i++) { unsigned int set_alarm = 1 << i; if (!(alarm & set_alarm)) continue; sst_cmd = ndctl_dimm_cmd_new_smart_set_threshold(st_cmd); if (!sst_cmd) { fprintf(stderr, "%s: alloc failed: smart set threshold\n", name); goto out; } switch (set_alarm) { case ND_SMART_SPARE_TRIP: fprintf(stderr, "%s: set spare threshold: 99\n", name); ndctl_cmd_smart_threshold_set_spares(sst_cmd, 99); ndctl_cmd_smart_threshold_set_media_temperature( sst_cmd, orig_mtemp); ndctl_cmd_smart_threshold_set_ctrl_temperature( sst_cmd, orig_ctemp); break; case ND_SMART_MTEMP_TRIP: mtemp = ndctl_cmd_smart_get_media_temperature(s_cmd); if (mtemp & (1 << 15)) mtemp *= 2; else mtemp /= 2; fprintf(stderr, "%s: set mtemp threshold: %.2f\n", name, ndctl_decode_smart_temperature(mtemp)); ndctl_cmd_smart_threshold_set_spares( sst_cmd, orig_spare); ndctl_cmd_smart_threshold_set_media_temperature( sst_cmd, mtemp); ndctl_cmd_smart_threshold_set_ctrl_temperature( sst_cmd, orig_ctemp); break; case ND_SMART_CTEMP_TRIP: ctemp = ndctl_cmd_smart_get_ctrl_temperature(s_cmd); if (ctemp & (1 << 15)) ctemp *= 2; else ctemp /= 2; fprintf(stderr, "%s: set ctemp threshold: %.2f\n", name, ndctl_decode_smart_temperature(ctemp)); ndctl_cmd_smart_threshold_set_spares( sst_cmd, orig_spare); ndctl_cmd_smart_threshold_set_media_temperature( sst_cmd, orig_mtemp); ndctl_cmd_smart_threshold_set_ctrl_temperature( sst_cmd, ctemp); break; default: break; } ndctl_cmd_smart_threshold_set_alarm_control(sst_cmd, set_alarm); rc = ndctl_cmd_submit(sst_cmd); if (rc < 0) { fprintf(stderr, "%s: smart set threshold command failed: %d %s\n", name, rc, strerror(errno)); goto out; } ndctl_cmd_unref(sst_cmd); } fprintf(stderr, "%s: set thresholds back to defaults\n", name); sst_cmd = ndctl_dimm_cmd_new_smart_set_threshold(st_cmd); if (!sst_cmd) { fprintf(stderr, "%s: alloc failed: smart set threshold\n", name); goto out; } rc = ndctl_cmd_submit(sst_cmd); if (rc < 0) { fprintf(stderr, "%s: smart set threshold defaults failed: %d %s\n", name, rc, strerror(errno)); goto out; } out: ndctl_cmd_unref(sst_cmd); ndctl_cmd_unref(st_cmd); ndctl_cmd_unref(s_cmd); } static void test_dimm_notify(struct ndctl_bus *bus) { struct ndctl_dimm *dimm; ndctl_dimm_foreach(bus, dimm) do_notify(dimm); } int main(int argc, char *argv[]) { struct ndctl_ctx *ctx; struct ndctl_bus *bus; int rc = EXIT_FAILURE; const char *provider; rc = ndctl_new(&ctx); if (rc < 0) return EXIT_FAILURE; if (argc != 2) { fprintf(stderr, "usage: smart-notify \n"); goto out; } ndctl_set_log_priority(ctx, LOG_DEBUG); provider = argv[1]; bus = ndctl_bus_get_by_provider(ctx, provider); if (!bus) { fprintf(stderr, "smart-notify: unable to find bus (%s)\n", provider); goto out; } rc = EXIT_SUCCESS; test_dimm_notify(bus); out: ndctl_unref(ctx); return rc; } ndctl-67/util/000077500000000000000000000000001355562357700134135ustar00rootroot00000000000000ndctl-67/util/COPYING000066400000000000000000000431331355562357700144520ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ndctl-67/util/abspath.c000066400000000000000000000011401355562357700151750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* originally copied from git/abspath.c */ #include #include char *prefix_filename(const char *pfx, const char *arg) { struct strbuf path = STRBUF_INIT; size_t pfx_len = pfx ? strlen(pfx) : 0; if (pfx_len && !is_absolute_path(arg)) strbuf_add(&path, pfx, pfx_len); strbuf_addstr(&path, arg); return strbuf_detach(&path, NULL); } void fix_filename(const char *prefix, const char **file) { if (!file || !*file || !prefix || is_absolute_path(*file) || !strcmp("-", *file)) return; *file = prefix_filename(prefix, *file); } ndctl-67/util/bitmap.c000066400000000000000000000067221355562357700150420ustar00rootroot00000000000000/* * Copyright(c) 2017 Intel Corporation. All rights reserved. * Copyright(c) 2009 Akinobu Mita. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from the Linux kernel bitmap implementation */ #include #include #include #include #include #include #include unsigned long *bitmap_alloc(unsigned long nbits) { return calloc(BITS_TO_LONGS(nbits), sizeof(unsigned long)); } void bitmap_set(unsigned long *map, unsigned int start, int len) { unsigned long *p = map + BIT_WORD(start); const unsigned int size = start + len; int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); while (len - bits_to_set >= 0) { *p |= mask_to_set; len -= bits_to_set; bits_to_set = BITS_PER_LONG; mask_to_set = ~0UL; p++; } if (len) { mask_to_set &= BITMAP_LAST_WORD_MASK(size); *p |= mask_to_set; } } void bitmap_clear(unsigned long *map, unsigned int start, int len) { unsigned long *p = map + BIT_WORD(start); const unsigned int size = start + len; int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); while (len - bits_to_clear >= 0) { *p &= ~mask_to_clear; len -= bits_to_clear; bits_to_clear = BITS_PER_LONG; mask_to_clear = ~0UL; p++; } if (len) { mask_to_clear &= BITMAP_LAST_WORD_MASK(size); *p &= ~mask_to_clear; } } /** * test_bit - Determine whether a bit is set * @nr: bit number to test * @addr: Address to start counting from */ int test_bit(unsigned int nr, const volatile unsigned long *addr) { return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); } /* * This is a common helper function for find_next_bit and * find_next_zero_bit. The difference is the "invert" argument, which * is XORed with each fetched word before searching it for one bits. */ static unsigned long _find_next_bit(const unsigned long *addr, unsigned long nbits, unsigned long start, unsigned long invert) { unsigned long tmp; if (!nbits || start >= nbits) return nbits; tmp = addr[start / BITS_PER_LONG] ^ invert; /* Handle 1st word. */ tmp &= BITMAP_FIRST_WORD_MASK(start); start = round_down(start, BITS_PER_LONG); while (!tmp) { start += BITS_PER_LONG; if (start >= nbits) return nbits; tmp = addr[start / BITS_PER_LONG] ^ invert; } return min(start + __builtin_ffsl(tmp), nbits); } /* * Find the next set bit in a memory region. */ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { return _find_next_bit(addr, size, offset, 0UL); } unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { return _find_next_bit(addr, size, offset, ~0UL); } int bitmap_full(const unsigned long *src, unsigned int nbits) { if (small_const_nbits(nbits)) return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); return find_next_zero_bit(src, nbits, 0UL) == nbits; } ndctl-67/util/bitmap.h000066400000000000000000000033011355562357700150350ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 _NDCTL_BITMAP_H_ #define _NDCTL_BITMAP_H_ #include #include #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define BIT(nr) (1UL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_PER_BYTE 8 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) #define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1))) #define small_const_nbits(nbits) \ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) unsigned long *bitmap_alloc(unsigned long nbits); void bitmap_set(unsigned long *map, unsigned int start, int len); void bitmap_clear(unsigned long *map, unsigned int start, int len); int test_bit(unsigned int nr, const volatile unsigned long *addr); unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset); int bitmap_full(const unsigned long *src, unsigned int nbits); #endif /* _NDCTL_BITMAP_H_ */ ndctl-67/util/filter.c000066400000000000000000000246761355562357700150630ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include #define NUMA_NO_NODE (-1) struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *__ident) { char *end = NULL, *ident, *save; unsigned long bus_id, id; const char *provider, *devname, *name; if (!__ident) return bus; ident = strdup(__ident); if (!ident) return NULL; for (name = strtok_r(ident, " ", &save); name; name = strtok_r(NULL, " ", &save)) { if (strcmp(name, "all") == 0) break; bus_id = strtoul(ident, &end, 0); if (end == ident || end[0]) bus_id = ULONG_MAX; provider = ndctl_bus_get_provider(bus); devname = ndctl_bus_get_devname(bus); id = ndctl_bus_get_id(bus); if (bus_id < ULONG_MAX && bus_id == id) break; if (bus_id == ULONG_MAX && (strcmp(provider, name) == 0 || strcmp(devname, name) == 0)) break; } free(ident); if (name) return bus; return NULL; } struct ndctl_region *util_region_filter(struct ndctl_region *region, const char *__ident) { char *end = NULL, *ident, *save; const char *name, *region_name; unsigned long region_id, id; if (!__ident) return region; ident = strdup(__ident); if (!ident) return NULL; for (name = strtok_r(ident, " ", &save); name; name = strtok_r(NULL, " ", &save)) { if (strcmp(name, "all") == 0) break; region_id = strtoul(ident, &end, 0); if (end == ident || end[0]) region_id = ULONG_MAX; region_name = ndctl_region_get_devname(region); id = ndctl_region_get_id(region); if (region_id < ULONG_MAX && region_id == id) break; if (region_id == ULONG_MAX && strcmp(region_name, name) == 0) break; } free(ident); if (name) return region; return NULL; } struct ndctl_namespace *util_namespace_filter(struct ndctl_namespace *ndns, const char *__ident) { struct ndctl_region *region = ndctl_namespace_get_region(ndns); unsigned long region_id, ndns_id; const char *name; char *ident, *save; if (!__ident) return ndns; ident = strdup(__ident); if (!ident) return NULL; for (name = strtok_r(ident, " ", &save); name; name = strtok_r(NULL, " ", &save)) { if (strcmp(name, "all") == 0) break; if (strcmp(name, ndctl_namespace_get_devname(ndns)) == 0) break; if (sscanf(name, "%ld.%ld", ®ion_id, &ndns_id) == 2 && ndctl_region_get_id(region) == region_id && ndctl_namespace_get_id(ndns) == ndns_id) break; } free(ident); if (name) return ndns; return NULL; } struct ndctl_dimm *util_dimm_filter(struct ndctl_dimm *dimm, const char *__ident) { char *end = NULL, *ident, *save; const char *name, *dimm_name; unsigned long dimm_id, id; if (!__ident) return dimm; ident = strdup(__ident); if (!ident) return NULL; for (name = strtok_r(ident, " ", &save); name; name = strtok_r(NULL, " ", &save)) { if (strcmp(name, "all") == 0) break; dimm_id = strtoul(ident, &end, 0); if (end == ident || end[0]) dimm_id = ULONG_MAX; dimm_name = ndctl_dimm_get_devname(dimm); id = ndctl_dimm_get_id(dimm); if (dimm_id < ULONG_MAX && dimm_id == id) break; if (dimm_id == ULONG_MAX && strcmp(dimm_name, name) == 0) break; } free(ident); if (name) return dimm; return NULL; } struct ndctl_bus *util_bus_filter_by_dimm(struct ndctl_bus *bus, const char *ident) { struct ndctl_dimm *dimm; if (!ident || strcmp(ident, "all") == 0) return bus; ndctl_dimm_foreach(bus, dimm) if (util_dimm_filter(dimm, ident)) return bus; return NULL; } struct ndctl_bus *util_bus_filter_by_region(struct ndctl_bus *bus, const char *ident) { struct ndctl_region *region; if (!ident || strcmp(ident, "all") == 0) return bus; ndctl_region_foreach(bus, region) if (util_region_filter(region, ident)) return bus; return NULL; } struct ndctl_bus *util_bus_filter_by_namespace(struct ndctl_bus *bus, const char *ident) { struct ndctl_region *region; struct ndctl_namespace *ndns; if (!ident || strcmp(ident, "all") == 0) return bus; ndctl_region_foreach(bus, region) ndctl_namespace_foreach(region, ndns) if (util_namespace_filter(ndns, ident)) return bus; return NULL; } struct ndctl_region *util_region_filter_by_dimm(struct ndctl_region *region, const char *ident) { struct ndctl_dimm *dimm; if (!ident || strcmp(ident, "all") == 0) return region; ndctl_dimm_foreach_in_region(region, dimm) if (util_dimm_filter(dimm, ident)) return region; return NULL; } struct ndctl_dimm *util_dimm_filter_by_region(struct ndctl_dimm *dimm, const char *ident) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_region *region; struct ndctl_dimm *check; if (!ident || strcmp(ident, "all") == 0) return dimm; ndctl_region_foreach(bus, region) { if (!util_region_filter(region, ident)) continue; ndctl_dimm_foreach_in_region(region, check) if (check == dimm) return dimm; } return NULL; } struct ndctl_dimm *util_dimm_filter_by_namespace(struct ndctl_dimm *dimm, const char *ident) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_namespace *ndns; struct ndctl_region *region; struct ndctl_dimm *check; if (!ident || strcmp(ident, "all") == 0) return dimm; ndctl_region_foreach(bus, region) { ndctl_namespace_foreach(region, ndns) { if (!util_namespace_filter(ndns, ident)) continue; ndctl_dimm_foreach_in_region(region, check) if (check == dimm) return dimm; } } return NULL; } struct ndctl_dimm *util_dimm_filter_by_numa_node(struct ndctl_dimm *dimm, int numa_node) { struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); struct ndctl_region *region; struct ndctl_dimm *check; if (numa_node == NUMA_NO_NODE) return dimm; ndctl_region_foreach(bus, region) ndctl_dimm_foreach_in_region(region, check) if (check == dimm && ndctl_region_get_numa_node(region) == numa_node) return dimm; return NULL; } struct ndctl_region *util_region_filter_by_namespace(struct ndctl_region *region, const char *ident) { struct ndctl_namespace *ndns; if (!ident || strcmp(ident, "all") == 0) return region; ndctl_namespace_foreach(region, ndns) if (util_namespace_filter(ndns, ident)) return region; return NULL; } struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, const char *ident) { struct daxctl_region *region = daxctl_dev_get_region(dev); int region_id, dev_id; if (!ident || strcmp(ident, "all") == 0) return dev; if (strcmp(ident, daxctl_dev_get_devname(dev)) == 0) return dev; if (sscanf(ident, "%d.%d", ®ion_id, &dev_id) == 2 && daxctl_region_get_id(region) == region_id && daxctl_dev_get_id(dev) == dev_id) return dev; return NULL; } static enum ndctl_namespace_mode mode_to_type(const char *mode) { if (!mode) return -ENXIO; if (strcasecmp(mode, "memory") == 0) return NDCTL_NS_MODE_MEMORY; else if (strcasecmp(mode, "fsdax") == 0) return NDCTL_NS_MODE_MEMORY; else if (strcasecmp(mode, "sector") == 0) return NDCTL_NS_MODE_SAFE; else if (strcasecmp(mode, "safe") == 0) return NDCTL_NS_MODE_SAFE; else if (strcasecmp(mode, "dax") == 0) return NDCTL_NS_MODE_DAX; else if (strcasecmp(mode, "devdax") == 0) return NDCTL_NS_MODE_DAX; else if (strcasecmp(mode, "raw") == 0) return NDCTL_NS_MODE_RAW; return NDCTL_NS_MODE_UNKNOWN; } int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, struct util_filter_params *param) { struct ndctl_bus *bus; unsigned int type = 0; int numa_node = NUMA_NO_NODE; char *end = NULL; if (param->type && (strcmp(param->type, "pmem") != 0 && strcmp(param->type, "blk") != 0)) { error("unknown type \"%s\" must be \"pmem\" or \"blk\"\n", param->type); return -EINVAL; } if (param->type) { if (strcmp(param->type, "pmem") == 0) type = ND_DEVICE_REGION_PMEM; else type = ND_DEVICE_REGION_BLK; } if (mode_to_type(param->mode) == NDCTL_NS_MODE_UNKNOWN) { error("invalid mode: '%s'\n", param->mode); return -EINVAL; } if (param->numa_node && strcmp(param->numa_node, "all") != 0) { struct stat st; if (stat("/sys/devices/system/node", &st) != 0) { error("This system does not support NUMA"); return -EINVAL; } numa_node = strtol(param->numa_node, &end, 0); if (end == param->numa_node || end[0]) { error("invalid numa_node: '%s'\n", param->numa_node); return -EINVAL; } } ndctl_bus_foreach(ctx, bus) { struct ndctl_region *region; struct ndctl_dimm *dimm; if (!util_bus_filter(bus, param->bus) || !util_bus_filter_by_dimm(bus, param->dimm) || !util_bus_filter_by_region(bus, param->region) || !util_bus_filter_by_namespace(bus, param->namespace)) continue; if (!fctx->filter_bus(bus, fctx)) continue; ndctl_dimm_foreach(bus, dimm) { if (!fctx->filter_dimm) break; if (!util_dimm_filter(dimm, param->dimm) || !util_dimm_filter_by_region(dimm, param->region) || !util_dimm_filter_by_namespace(dimm, param->namespace) || !util_dimm_filter_by_numa_node(dimm, numa_node)) continue; fctx->filter_dimm(dimm, fctx); } ndctl_region_foreach(bus, region) { struct ndctl_namespace *ndns; if (!util_region_filter(region, param->region) || !util_region_filter_by_dimm(region, param->dimm) || !util_region_filter_by_namespace(region, param->namespace)) continue; if (numa_node != NUMA_NO_NODE && ndctl_region_get_numa_node(region) != numa_node) continue; if (type && ndctl_region_get_type(region) != type) continue; if (!fctx->filter_region(region, fctx)) continue; ndctl_namespace_foreach(region, ndns) { enum ndctl_namespace_mode mode; if (!fctx->filter_namespace) break; if (!util_namespace_filter(ndns, param->namespace)) continue; mode = ndctl_namespace_get_mode(ndns); if (param->mode && mode_to_type(param->mode) != mode) continue; fctx->filter_namespace(ndns, fctx); } } } return 0; } ndctl-67/util/filter.h000066400000000000000000000062571355562357700150630ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 _UTIL_FILTER_H_ #define _UTIL_FILTER_H_ #include #include struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *ident); struct ndctl_region *util_region_filter(struct ndctl_region *region, const char *ident); struct ndctl_namespace *util_namespace_filter(struct ndctl_namespace *ndns, const char *ident); struct ndctl_dimm *util_dimm_filter(struct ndctl_dimm *dimm, const char *ident); struct ndctl_bus *util_bus_filter_by_dimm(struct ndctl_bus *bus, const char *ident); struct ndctl_bus *util_bus_filter_by_region(struct ndctl_bus *bus, const char *ident); struct ndctl_bus *util_bus_filter_by_namespace(struct ndctl_bus *bus, const char *ident); struct ndctl_region *util_region_filter_by_dimm(struct ndctl_region *region, const char *ident); struct ndctl_dimm *util_dimm_filter_by_region(struct ndctl_dimm *dimm, const char *ident); struct ndctl_dimm *util_dimm_filter_by_namespace(struct ndctl_dimm *dimm, const char *ident); struct ndctl_region *util_region_filter_by_namespace(struct ndctl_region *region, const char *ident); struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, const char *ident); struct json_object; /* json object hierarchy for the util_filter_walk() performed by cmd_list() */ struct list_filter_arg { struct json_object *jnamespaces; struct json_object *jregions; struct json_object *jdimms; struct json_object *jbuses; struct json_object *jregion; struct json_object *jbus; unsigned long flags; }; struct monitor_filter_arg { struct list_head dimms; int maxfd_dimm; int num_dimm; unsigned long flags; }; /* * struct util_filter_ctx - control and callbacks for util_filter_walk() * ->filter_bus() and ->filter_region() return bool because the * child-object filter routines can not be called if the parent context * is not established. ->filter_dimm() and ->filter_namespace() are leaf * objects, so no child dependencies to check. */ struct util_filter_ctx { bool (*filter_bus)(struct ndctl_bus *bus, struct util_filter_ctx *ctx); void (*filter_dimm)(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx); bool (*filter_region)(struct ndctl_region *region, struct util_filter_ctx *ctx); void (*filter_namespace)(struct ndctl_namespace *ndns, struct util_filter_ctx *ctx); union { void *arg; struct list_filter_arg *list; struct monitor_filter_arg *monitor; }; }; struct util_filter_params { const char *bus; const char *region; const char *type; const char *dimm; const char *mode; const char *namespace; const char *numa_node; }; struct ndctl_ctx; int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, struct util_filter_params *param); #endif ndctl-67/util/fletcher.h000066400000000000000000000010061355562357700153550ustar00rootroot00000000000000#ifndef _NDCTL_FLETCHER_H_ #define _NDCTL_FLETCHER_H_ #include #include /* * Note, fletcher64() is copied from drivers/nvdimm/label.c in the Linux kernel */ static inline u64 fletcher64(void *addr, size_t len, bool le) { u32 *buf = addr; u32 lo32 = 0; u64 hi32 = 0; size_t i; for (i = 0; i < len / sizeof(u32); i++) { lo32 += le ? le32_to_cpu((le32) buf[i]) : buf[i]; hi32 += lo32; } return hi32 << 32 | lo32; } #endif /* _NDCTL_FLETCHER_H_ */ ndctl-67/util/help.c000066400000000000000000000075061355562357700145170ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * Copyright(c) 2008 Miklos Vajna. All rights reserved. * Copyright(c) 2006 Linus Torvalds. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ /* * builtin-help.c * * Builtin help command */ #include #include #include #include #include #include #include #define pr_err(x, ...) fprintf(stderr, x, ##__VA_ARGS__) #define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */ static void exec_man_konqueror(const char *path, const char *page) { const char *display = getenv("DISPLAY"); if (display && *display) { struct strbuf man_page = STRBUF_INIT; const char *filename = "kfmclient"; char sbuf[STRERR_BUFSIZE]; /* It's simpler to launch konqueror using kfmclient. */ if (path) { const char *file = strrchr(path, '/'); if (file && !strcmp(file + 1, "konqueror")) { char *new = strdup(path); char *dest = strrchr(new, '/'); /* strlen("konqueror") == strlen("kfmclient") */ strcpy(dest + 1, "kfmclient"); path = new; } if (file) filename = file; } else path = "kfmclient"; strbuf_addf(&man_page, "man:%s(1)", page); execlp(path, filename, "newTab", man_page.buf, NULL); warning("failed to exec '%s': %s", path, strerror_r(errno, sbuf, sizeof(sbuf))); } } static void exec_man_man(const char *path, const char *page) { char sbuf[STRERR_BUFSIZE]; if (!path) path = "man"; execlp(path, "man", page, NULL); warning("failed to exec '%s': %s", path, strerror_r(errno, sbuf, sizeof(sbuf))); } static char *cmd_to_page(const char *cmd, char **page, const char *util_name) { int rc; if (!cmd) rc = asprintf(page, "%s", util_name); else if (!prefixcmp(cmd, util_name)) rc = asprintf(page, "%s", cmd); else rc = asprintf(page, "%s-%s", util_name, cmd); if (rc < 0) return NULL; return *page; } static const char *system_path(const char *path) { static const char *prefix = PREFIX; struct strbuf d = STRBUF_INIT; if (is_absolute_path(path)) return path; strbuf_addf(&d, "%s/%s", prefix, path); path = strbuf_detach(&d, NULL); return path; } static void setup_man_path(void) { struct strbuf new_path = STRBUF_INIT; const char *old_path = getenv("MANPATH"); /* We should always put ':' after our path. If there is no * old_path, the ':' at the end will let 'man' to try * system-wide paths after ours to find the manual page. If * there is old_path, we need ':' as delimiter. */ strbuf_addstr(&new_path, system_path(NDCTL_MAN_PATH)); strbuf_addch(&new_path, ':'); if (old_path) strbuf_addstr(&new_path, old_path); setenv("MANPATH", new_path.buf, 1); strbuf_release(&new_path); } static void exec_viewer(const char *name, const char *page) { if (!strcasecmp(name, "man")) exec_man_man(NULL, page); else if (!strcasecmp(name, "konqueror")) exec_man_konqueror(NULL, page); else warning("'%s': unknown man viewer.", name); } int help_show_man_page(const char *cmd, const char *util_name, const char *viewer) { const char *fallback = getenv(viewer); char *page; page = cmd_to_page(cmd, &page, util_name); if (!page) return -1; setup_man_path(); if (fallback) exec_viewer(fallback, page); exec_viewer("man", page); pr_err("no man viewer handled the request"); free(page); return -1; } ndctl-67/util/iomem.c000066400000000000000000000015341355562357700146700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2019 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include unsigned long long __iomem_get_dev_resource(struct log_ctx *ctx, const char *devpath) { const char *devname = devpath_to_devname(devpath); FILE *fp = fopen("/proc/iomem", "r"); unsigned long long res; char name[256]; if (fp == NULL) { log_err(ctx, "%s: open /proc/iomem: %s\n", devname, strerror(errno)); return 0; } while (fscanf(fp, "%llx-%*x : %254[^\n]\n", &res, name) == 2) { if (strcmp(name, devname) == 0) { log_dbg(ctx, "%s: got resource via iomem: %#llx\n", devname, res); fclose(fp); return res; } } log_dbg(ctx, "%s: not found in iomem\n", devname); fclose(fp); return 0; } ndctl-67/util/iomem.h000066400000000000000000000005531355562357700146750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* Copyright(c) 2019 Intel Corporation. All rights reserved. */ #ifndef _NDCTL_IOMEM_H_ #define _NDCTL_IOMEM_H_ struct log_ctx; unsigned long long __iomem_get_dev_resource(struct log_ctx *ctx, const char *path); #define iomem_get_dev_resource(c, p) __iomem_get_dev_resource(&(c)->ctx, (p)) #endif /* _NDCTL_IOMEM_H_ */ ndctl-67/util/json.c000066400000000000000000000710341355562357700145350ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include #include #include #include #include #include #include #include #include #include /* adapted from mdadm::human_size_brief() */ static int display_size(struct json_object *jobj, struct printbuf *pbuf, int level, int flags) { unsigned long long bytes = json_object_get_int64(jobj); static char buf[128]; int c; /* * We convert bytes to either centi-M{ega,ibi}bytes or * centi-G{igi,ibi}bytes, with appropriate rounding, and then print * 1/100th of those as a decimal. We allow upto 2048Megabytes before * converting to gigabytes, as that shows more precision and isn't too * large a number. Terabytes are not yet handled. * * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc. * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc. */ if (bytes < 5000*1024) snprintf(buf, sizeof(buf), "%lld", bytes); else { /* IEC */ if (bytes < 2*1024LL*1024LL*1024LL) { long cMiB = (bytes * 200LL / (1LL<<20) +1) /2; c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB", cMiB/100 , cMiB % 100); } else { long cGiB = (bytes * 200LL / (1LL<<30) +1) /2; c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB", cGiB/100 , cGiB % 100); } /* JEDEC */ if (bytes < 2*1024LL*1024LL*1024LL) { long cMB = (bytes / (1000000LL / 200LL) + 1) / 2; snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld MB)\"", cMB/100, cMB % 100); } else { long cGB = (bytes / (1000000000LL/200LL) + 1) / 2; snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld GB)\"", cGB/100 , cGB % 100); } } return printbuf_memappend(pbuf, buf, strlen(buf)); } static int display_hex(struct json_object *jobj, struct printbuf *pbuf, int level, int flags) { unsigned long long val = json_object_get_int64(jobj); static char buf[32]; snprintf(buf, sizeof(buf), "\"%#llx\"", val); return printbuf_memappend(pbuf, buf, strlen(buf)); } struct json_object *util_json_object_size(unsigned long long size, unsigned long flags) { struct json_object *jobj = json_object_new_int64(size); if (jobj && (flags & UTIL_JSON_HUMAN)) json_object_set_serializer(jobj, display_size, NULL, NULL); return jobj; } struct json_object *util_json_object_hex(unsigned long long val, unsigned long flags) { struct json_object *jobj = json_object_new_int64(val); if (jobj && (flags & UTIL_JSON_HUMAN)) json_object_set_serializer(jobj, display_hex, NULL, NULL); return jobj; } void util_display_json_array(FILE *f_out, struct json_object *jarray, unsigned long flags) { int len = json_object_array_length(jarray); int jflag = JSON_C_TO_STRING_PRETTY; if (json_object_array_length(jarray) > 1 || !(flags & UTIL_JSON_HUMAN)) fprintf(f_out, "%s\n", json_object_to_json_string_ext(jarray, jflag)); else if (len) { struct json_object *jobj; jobj = json_object_array_get_idx(jarray, 0); fprintf(f_out, "%s\n", json_object_to_json_string_ext(jobj, jflag)); } json_object_put(jarray); } struct json_object *util_bus_to_json(struct ndctl_bus *bus) { struct json_object *jbus = json_object_new_object(); struct json_object *jobj; int scrub; if (!jbus) return NULL; jobj = json_object_new_string(ndctl_bus_get_provider(bus)); if (!jobj) goto err; json_object_object_add(jbus, "provider", jobj); jobj = json_object_new_string(ndctl_bus_get_devname(bus)); if (!jobj) goto err; json_object_object_add(jbus, "dev", jobj); scrub = ndctl_bus_get_scrub_state(bus); if (scrub < 0) return jbus; jobj = json_object_new_string(scrub ? "active" : "idle"); if (!jobj) goto err; json_object_object_add(jbus, "scrub_state", jobj); return jbus; err: json_object_put(jbus); return NULL; } struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, unsigned long flags) { struct json_object *jdimm = json_object_new_object(); const char *id = ndctl_dimm_get_unique_id(dimm); unsigned int handle = ndctl_dimm_get_handle(dimm); unsigned short phys_id = ndctl_dimm_get_phys_id(dimm); struct json_object *jobj; enum ndctl_security_state sstate; if (!jdimm) return NULL; jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); if (!jobj) goto err; json_object_object_add(jdimm, "dev", jobj); if (id) { jobj = json_object_new_string(id); if (!jobj) goto err; json_object_object_add(jdimm, "id", jobj); } if (handle < UINT_MAX) { jobj = util_json_object_hex(handle, flags); if (!jobj) goto err; json_object_object_add(jdimm, "handle", jobj); } if (phys_id < USHRT_MAX) { jobj = util_json_object_hex(phys_id, flags); if (!jobj) goto err; json_object_object_add(jdimm, "phys_id", jobj); } if (!ndctl_dimm_is_enabled(dimm)) { jobj = json_object_new_string("disabled"); if (!jobj) goto err; json_object_object_add(jdimm, "state", jobj); } if (ndctl_dimm_failed_map(dimm)) { jobj = json_object_new_boolean(true); if (!jobj) goto err; json_object_object_add(jdimm, "flag_failed_map", jobj); } if (ndctl_dimm_failed_save(dimm)) { jobj = json_object_new_boolean(true); if (!jobj) goto err; json_object_object_add(jdimm, "flag_failed_save", jobj); } if (ndctl_dimm_failed_arm(dimm)) { jobj = json_object_new_boolean(true); if (!jobj) goto err; json_object_object_add(jdimm, "flag_failed_arm", jobj); } if (ndctl_dimm_failed_restore(dimm)) { jobj = json_object_new_boolean(true); if (!jobj) goto err; json_object_object_add(jdimm, "flag_failed_restore", jobj); } if (ndctl_dimm_failed_flush(dimm)) { jobj = json_object_new_boolean(true); if (!jobj) goto err; json_object_object_add(jdimm, "flag_failed_flush", jobj); } if (ndctl_dimm_smart_pending(dimm)) { jobj = json_object_new_boolean(true); if (!jobj) goto err; json_object_object_add(jdimm, "flag_smart_event", jobj); } sstate = ndctl_dimm_get_security(dimm); if (sstate == NDCTL_SECURITY_DISABLED) jobj = json_object_new_string("disabled"); else if (sstate == NDCTL_SECURITY_UNLOCKED) jobj = json_object_new_string("unlocked"); else if (sstate == NDCTL_SECURITY_LOCKED) jobj = json_object_new_string("locked"); else if (sstate == NDCTL_SECURITY_FROZEN) jobj = json_object_new_string("frozen"); else if (sstate == NDCTL_SECURITY_OVERWRITE) jobj = json_object_new_string("overwrite"); else jobj = NULL; if (jobj) json_object_object_add(jdimm, "security", jobj); if (ndctl_dimm_security_is_frozen(dimm)) { jobj = json_object_new_boolean(true); if (jobj) json_object_object_add(jdimm, "security_frozen", jobj); } return jdimm; err: json_object_put(jdimm); return NULL; } struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, unsigned long flags) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); struct json_object *jdev, *jobj; int node, movable; jdev = json_object_new_object(); if (!devname || !jdev) return NULL; jobj = json_object_new_string(devname); if (jobj) json_object_object_add(jdev, "chardev", jobj); jobj = util_json_object_size(daxctl_dev_get_size(dev), flags); if (jobj) json_object_object_add(jdev, "size", jobj); node = daxctl_dev_get_target_node(dev); if (node >= 0) { jobj = json_object_new_int(node); if (jobj) json_object_object_add(jdev, "target_node", jobj); } if (mem) jobj = json_object_new_string("system-ram"); else jobj = json_object_new_string("devdax"); if (jobj) json_object_object_add(jdev, "mode", jobj); if (mem) { movable = daxctl_memory_is_movable(mem); if (movable == 1) jobj = json_object_new_boolean(true); else if (movable == 0) jobj = json_object_new_boolean(false); else jobj = NULL; if (jobj) json_object_object_add(jdev, "movable", jobj); } if (!daxctl_dev_is_enabled(dev)) { jobj = json_object_new_string("disabled"); if (jobj) json_object_object_add(jdev, "state", jobj); } return jdev; } struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, struct json_object *jdevs, const char *ident, unsigned long flags) { struct daxctl_dev *dev; daxctl_dev_foreach(region, dev) { struct json_object *jdev; if (!util_daxctl_dev_filter(dev, ident)) continue; if (!(flags & UTIL_JSON_IDLE) && !daxctl_dev_get_size(dev)) continue; if (!jdevs) { jdevs = json_object_new_array(); if (!jdevs) return NULL; } jdev = util_daxctl_dev_to_json(dev, flags); if (!jdev) { json_object_put(jdevs); return NULL; } json_object_array_add(jdevs, jdev); } return jdevs; } #define _SZ(get_max, get_elem, type) \ static struct json_object *util_##type##_build_size_array(struct ndctl_##type *arg) \ { \ struct json_object *arr = json_object_new_array(); \ int i; \ \ if (!arr) \ return NULL; \ \ for (i = 0; i < get_max(arg); i++) { \ struct json_object *jobj; \ int64_t align; \ \ align = get_elem(arg, i); \ jobj = json_object_new_int64(align); \ if (!jobj) \ goto err; \ json_object_array_add(arr, jobj); \ } \ \ return arr; \ err: \ json_object_put(arr); \ return NULL; \ } #define SZ(type, kind) _SZ(ndctl_##type##_get_num_##kind##s, \ ndctl_##type##_get_supported_##kind, type) SZ(pfn, alignment) SZ(dax, alignment) SZ(btt, sector_size) struct json_object *util_region_capabilities_to_json(struct ndctl_region *region) { struct json_object *jcaps, *jcap, *jobj; struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); if (!btt || !pfn || !dax) return NULL; jcaps = json_object_new_array(); if (!jcaps) return NULL; if (btt) { jcap = json_object_new_object(); if (!jcap) goto err; json_object_array_add(jcaps, jcap); jobj = json_object_new_string("sector"); if (!jobj) goto err; json_object_object_add(jcap, "mode", jobj); jobj = util_btt_build_size_array(btt); if (!jobj) goto err; json_object_object_add(jcap, "sector_sizes", jobj); } if (pfn) { jcap = json_object_new_object(); if (!jcap) goto err; json_object_array_add(jcaps, jcap); jobj = json_object_new_string("fsdax"); if (!jobj) goto err; json_object_object_add(jcap, "mode", jobj); jobj = util_pfn_build_size_array(pfn); if (!jobj) goto err; json_object_object_add(jcap, "alignments", jobj); } if (dax) { jcap = json_object_new_object(); if (!jcap) goto err; json_object_array_add(jcaps, jcap); jobj = json_object_new_string("devdax"); if (!jobj) goto err; json_object_object_add(jcap, "mode", jobj); jobj = util_dax_build_size_array(dax); if (!jobj) goto err; json_object_object_add(jcap, "alignments", jobj); } return jcaps; err: json_object_put(jcaps); return NULL; } struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, const char *ident, unsigned long flags) { unsigned long align; struct json_object *jregion, *jobj; unsigned long long available_size, size; jregion = json_object_new_object(); if (!jregion) return NULL; /* * The flag indicates when we are being called by an agent that * already knows about the parent device information. */ if (!(flags & UTIL_JSON_DAX)) { /* trim off the redundant /sys/devices prefix */ const char *path = daxctl_region_get_path(region); int len = strlen("/sys/devices"); const char *trim = &path[len]; if (strncmp(path, "/sys/devices", len) != 0) goto err; jobj = json_object_new_string(trim); if (!jobj) goto err; json_object_object_add(jregion, "path", jobj); } jobj = json_object_new_int(daxctl_region_get_id(region)); if (!jobj) goto err; json_object_object_add(jregion, "id", jobj); size = daxctl_region_get_size(region); if (size < ULLONG_MAX) { jobj = util_json_object_size(size, flags); if (!jobj) goto err; json_object_object_add(jregion, "size", jobj); } available_size = daxctl_region_get_available_size(region); if (available_size) { jobj = util_json_object_size(available_size, flags); if (!jobj) goto err; json_object_object_add(jregion, "available_size", jobj); } align = daxctl_region_get_align(region); if (align < ULONG_MAX) { jobj = json_object_new_int64(align); if (!jobj) goto err; json_object_object_add(jregion, "align", jobj); } if (!(flags & UTIL_JSON_DAX_DEVS)) return jregion; jobj = util_daxctl_devs_to_list(region, NULL, ident, flags); if (jobj) json_object_object_add(jregion, "devices", jobj); return jregion; err: json_object_put(jregion); return NULL; } static int compare_dimm_number(const void *p1, const void *p2) { struct ndctl_dimm *dimm1 = *(struct ndctl_dimm **)p1; struct ndctl_dimm *dimm2 = *(struct ndctl_dimm **)p2; const char *dimm1_name = ndctl_dimm_get_devname(dimm1); const char *dimm2_name = ndctl_dimm_get_devname(dimm2); int num1, num2; if (sscanf(dimm1_name, "nmem%d", &num1) != 1) num1 = 0; if (sscanf(dimm2_name, "nmem%d", &num2) != 1) num2 = 0; return num1 - num2; } static struct json_object *badblocks_to_jdimms(struct ndctl_region *region, unsigned long long addr, unsigned long len) { struct ndctl_bus *bus = ndctl_region_get_bus(region); int count = ndctl_region_get_interleave_ways(region); unsigned long long end = addr + len; struct json_object *jdimms, *jobj; struct ndctl_dimm **dimms, *dimm; int found, i; jdimms = json_object_new_array(); if (!jdimms) return NULL; dimms = calloc(count, sizeof(struct ndctl_dimm *)); if (!dimms) goto err_dimms; for (found = 0; found < count && addr < end; addr += 512) { dimm = ndctl_bus_get_dimm_by_physical_address(bus, addr); if (!dimm) continue; for (i = 0; i < count; i++) if (dimms[i] == dimm) break; if (i >= count) dimms[found++] = dimm; } if (!found) goto err_found; qsort(dimms, found, sizeof(dimm), compare_dimm_number); for (i = 0; i < found; i++) { const char *devname = ndctl_dimm_get_devname(dimms[i]); jobj = json_object_new_string(devname); if (!jobj) break; json_object_array_add(jdimms, jobj); } if (!i) goto err_found; free(dimms); return jdimms; err_found: free(dimms); err_dimms: json_object_put(jdimms); return NULL; } struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, unsigned int *bb_count, unsigned long flags) { struct json_object *jbb = NULL, *jbbs = NULL, *jobj; struct badblock *bb; int bbs = 0; if (flags & UTIL_JSON_MEDIA_ERRORS) { jbbs = json_object_new_array(); if (!jbbs) return NULL; } ndctl_region_badblock_foreach(region, bb) { struct json_object *jdimms; unsigned long long addr; bbs += bb->len; /* recheck so we can still get the badblocks_count from above */ if (!(flags & UTIL_JSON_MEDIA_ERRORS)) continue; /* get start address of region */ addr = ndctl_region_get_resource(region); if (addr == ULLONG_MAX) goto err_array; /* get address of bad block */ addr += bb->offset << 9; jbb = json_object_new_object(); if (!jbb) goto err_array; jobj = json_object_new_int64(bb->offset); if (!jobj) goto err; json_object_object_add(jbb, "offset", jobj); jobj = json_object_new_int(bb->len); if (!jobj) goto err; json_object_object_add(jbb, "length", jobj); jdimms = badblocks_to_jdimms(region, addr, bb->len << 9); if (jdimms) json_object_object_add(jbb, "dimms", jdimms); json_object_array_add(jbbs, jbb); } *bb_count = bbs; if (bbs) return jbbs; err: json_object_put(jbb); err_array: json_object_put(jbbs); return NULL; } static struct json_object *util_namespace_badblocks_to_json( struct ndctl_namespace *ndns, unsigned int *bb_count, unsigned long flags) { struct json_object *jbb = NULL, *jbbs = NULL, *jobj; struct badblock *bb; int bbs = 0; if (flags & UTIL_JSON_MEDIA_ERRORS) { jbbs = json_object_new_array(); if (!jbbs) return NULL; } else return NULL; ndctl_namespace_badblock_foreach(ndns, bb) { bbs += bb->len; /* recheck so we can still get the badblocks_count from above */ if (!(flags & UTIL_JSON_MEDIA_ERRORS)) continue; jbb = json_object_new_object(); if (!jbb) goto err_array; jobj = json_object_new_int64(bb->offset); if (!jobj) goto err; json_object_object_add(jbb, "offset", jobj); jobj = json_object_new_int(bb->len); if (!jobj) goto err; json_object_object_add(jbb, "length", jobj); json_object_array_add(jbbs, jbb); } *bb_count = bbs; if (bbs) return jbbs; err: json_object_put(jbb); err_array: json_object_put(jbbs); return NULL; } static struct json_object *dev_badblocks_to_json(struct ndctl_region *region, unsigned long long dev_begin, unsigned long long dev_size, unsigned int *bb_count, unsigned long flags) { struct json_object *jbb = NULL, *jbbs = NULL, *jobj; unsigned long long region_begin, dev_end, offset; unsigned int len, bbs = 0; struct badblock *bb; region_begin = ndctl_region_get_resource(region); if (region_begin == ULLONG_MAX) return NULL; dev_end = dev_begin + dev_size - 1; if (flags & UTIL_JSON_MEDIA_ERRORS) { jbbs = json_object_new_array(); if (!jbbs) return NULL; } ndctl_region_badblock_foreach(region, bb) { unsigned long long bb_begin, bb_end, begin, end; struct json_object *jdimms; bb_begin = region_begin + (bb->offset << 9); bb_end = bb_begin + (bb->len << 9) - 1; if (bb_end <= dev_begin || bb_begin >= dev_end) continue; if (bb_begin < dev_begin) begin = dev_begin; else begin = bb_begin; if (bb_end > dev_end) end = dev_end; else end = bb_end; offset = (begin - dev_begin) >> 9; len = (end - begin + 1) >> 9; bbs += len; /* recheck so we can still get the badblocks_count from above */ if (!(flags & UTIL_JSON_MEDIA_ERRORS)) continue; jbb = json_object_new_object(); if (!jbb) goto err_array; jobj = json_object_new_int64(offset); if (!jobj) goto err; json_object_object_add(jbb, "offset", jobj); jobj = json_object_new_int(len); if (!jobj) goto err; json_object_object_add(jbb, "length", jobj); jdimms = badblocks_to_jdimms(region, begin, len << 9); if (jdimms) json_object_object_add(jbb, "dimms", jdimms); json_object_array_add(jbbs, jbb); } *bb_count = bbs; if (bbs) return jbbs; err: json_object_put(jbb); err_array: json_object_put(jbbs); return NULL; } static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn, unsigned int *bb_count, unsigned long flags) { struct ndctl_region *region = ndctl_pfn_get_region(pfn); unsigned long long pfn_begin, pfn_size; pfn_begin = ndctl_pfn_get_resource(pfn); if (pfn_begin == ULLONG_MAX) { struct ndctl_namespace *ndns = ndctl_pfn_get_namespace(pfn); return util_namespace_badblocks_to_json(ndns, bb_count, flags); } pfn_size = ndctl_pfn_get_size(pfn); if (pfn_size == ULLONG_MAX) return NULL; return dev_badblocks_to_json(region, pfn_begin, pfn_size, bb_count, flags); } static void util_btt_badblocks_to_json(struct ndctl_btt *btt, unsigned int *bb_count) { struct ndctl_region *region = ndctl_btt_get_region(btt); struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); unsigned long long begin, size; begin = ndctl_namespace_get_resource(ndns); if (begin == ULLONG_MAX) return; size = ndctl_namespace_get_size(ndns); if (size == ULLONG_MAX) return; /* * The dev_badblocks_to_json() for BTT is not accurate with * respect to data vs metadata badblocks, and is only useful for * a potential bb_count. * * FIXME: switch to native BTT badblocks representation * when / if the kernel provides it. */ dev_badblocks_to_json(region, begin, size, bb_count, 0); } static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax, unsigned int *bb_count, unsigned long flags) { struct ndctl_region *region = ndctl_dax_get_region(dax); unsigned long long dax_begin, dax_size; dax_begin = ndctl_dax_get_resource(dax); if (dax_begin == ULLONG_MAX) return NULL; dax_size = ndctl_dax_get_size(dax); if (dax_size == ULLONG_MAX) return NULL; return dev_badblocks_to_json(region, dax_begin, dax_size, bb_count, flags); } static struct json_object *util_raw_uuid(struct ndctl_namespace *ndns) { char buf[40]; uuid_t raw_uuid; ndctl_namespace_get_uuid(ndns, raw_uuid); if (uuid_is_null(raw_uuid)) return NULL; uuid_unparse(raw_uuid, buf); return json_object_new_string(buf); } static void util_raw_uuid_to_json(struct ndctl_namespace *ndns, unsigned long flags, struct json_object *jndns) { struct json_object *jobj; if (!(flags & UTIL_JSON_VERBOSE)) return; jobj = util_raw_uuid(ndns); if (!jobj) return; json_object_object_add(jndns, "raw_uuid", jobj); } struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, unsigned long flags) { struct json_object *jndns = json_object_new_object(); enum ndctl_pfn_loc loc = NDCTL_PFN_LOC_NONE; struct json_object *jobj, *jbbs = NULL; const char *locations[] = { [NDCTL_PFN_LOC_NONE] = "none", [NDCTL_PFN_LOC_RAM] = "mem", [NDCTL_PFN_LOC_PMEM] = "dev", }; unsigned long long size = ULLONG_MAX; unsigned int sector_size = UINT_MAX; enum ndctl_namespace_mode mode; const char *bdev = NULL, *name; unsigned int bb_count = 0; struct ndctl_btt *btt; struct ndctl_pfn *pfn; struct ndctl_dax *dax; unsigned long align = 0; char buf[40]; uuid_t uuid; int numa; if (!jndns) return NULL; jobj = json_object_new_string(ndctl_namespace_get_devname(ndns)); if (!jobj) goto err; json_object_object_add(jndns, "dev", jobj); btt = ndctl_namespace_get_btt(ndns); dax = ndctl_namespace_get_dax(ndns); pfn = ndctl_namespace_get_pfn(ndns); mode = ndctl_namespace_get_mode(ndns); switch (mode) { case NDCTL_NS_MODE_MEMORY: if (pfn) { /* dynamic memory mode */ size = ndctl_pfn_get_size(pfn); loc = ndctl_pfn_get_location(pfn); } else { /* native/static memory mode */ size = ndctl_namespace_get_size(ndns); loc = NDCTL_PFN_LOC_RAM; } jobj = json_object_new_string("fsdax"); break; case NDCTL_NS_MODE_DAX: if (!dax) goto err; size = ndctl_dax_get_size(dax); jobj = json_object_new_string("devdax"); loc = ndctl_dax_get_location(dax); break; case NDCTL_NS_MODE_SAFE: if (!btt) goto err; jobj = json_object_new_string("sector"); size = ndctl_btt_get_size(btt); break; case NDCTL_NS_MODE_RAW: size = ndctl_namespace_get_size(ndns); jobj = json_object_new_string("raw"); break; default: jobj = NULL; } if (jobj) json_object_object_add(jndns, "mode", jobj); if ((mode != NDCTL_NS_MODE_SAFE) && (mode != NDCTL_NS_MODE_RAW)) { jobj = json_object_new_string(locations[loc]); if (jobj) json_object_object_add(jndns, "map", jobj); } if (size < ULLONG_MAX) { jobj = util_json_object_size(size, flags); if (jobj) json_object_object_add(jndns, "size", jobj); } if (btt) { ndctl_btt_get_uuid(btt, uuid); uuid_unparse(uuid, buf); jobj = json_object_new_string(buf); if (!jobj) goto err; json_object_object_add(jndns, "uuid", jobj); util_raw_uuid_to_json(ndns, flags, jndns); bdev = ndctl_btt_get_block_device(btt); } else if (pfn) { align = ndctl_pfn_get_align(pfn); ndctl_pfn_get_uuid(pfn, uuid); uuid_unparse(uuid, buf); jobj = json_object_new_string(buf); if (!jobj) goto err; json_object_object_add(jndns, "uuid", jobj); util_raw_uuid_to_json(ndns, flags, jndns); bdev = ndctl_pfn_get_block_device(pfn); } else if (dax) { struct daxctl_region *dax_region; dax_region = ndctl_dax_get_daxctl_region(dax); align = ndctl_dax_get_align(dax); ndctl_dax_get_uuid(dax, uuid); uuid_unparse(uuid, buf); jobj = json_object_new_string(buf); if (!jobj) goto err; json_object_object_add(jndns, "uuid", jobj); util_raw_uuid_to_json(ndns, flags, jndns); if ((flags & UTIL_JSON_DAX) && dax_region) { jobj = util_daxctl_region_to_json(dax_region, NULL, flags); if (jobj) json_object_object_add(jndns, "daxregion", jobj); } else if (dax_region) { struct daxctl_dev *dev; /* * We can only find/list these device-dax * details when the instance is enabled. */ dev = daxctl_dev_get_first(dax_region); if (dev) { name = daxctl_dev_get_devname(dev); jobj = json_object_new_string(name); if (!jobj) goto err; json_object_object_add(jndns, "chardev", jobj); } } } else if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { ndctl_namespace_get_uuid(ndns, uuid); uuid_unparse(uuid, buf); jobj = json_object_new_string(buf); if (!jobj) goto err; json_object_object_add(jndns, "uuid", jobj); bdev = ndctl_namespace_get_block_device(ndns); } else bdev = ndctl_namespace_get_block_device(ndns); if (btt) sector_size = ndctl_btt_get_sector_size(btt); else if (!dax) { sector_size = ndctl_namespace_get_sector_size(ndns); if (!sector_size || sector_size == UINT_MAX) sector_size = 512; } /* * The kernel will default to a 512 byte sector size on PMEM * namespaces that don't explicitly have a sector size. This * happens because they use pre-v1.2 labels or because they * don't have a label space (devtype=nd_namespace_io). */ if (sector_size < UINT_MAX) { jobj = json_object_new_int(sector_size); if (!jobj) goto err; json_object_object_add(jndns, "sector_size", jobj); } if (align) { jobj = json_object_new_int64(align); if (!jobj) goto err; json_object_object_add(jndns, "align", jobj); } if (bdev && bdev[0]) { jobj = json_object_new_string(bdev); if (!jobj) goto err; json_object_object_add(jndns, "blockdev", jobj); } if (!ndctl_namespace_is_active(ndns)) { jobj = json_object_new_string("disabled"); if (!jobj) goto err; json_object_object_add(jndns, "state", jobj); } name = ndctl_namespace_get_alt_name(ndns); if (name && name[0]) { jobj = json_object_new_string(name); if (!jobj) goto err; json_object_object_add(jndns, "name", jobj); } numa = ndctl_namespace_get_numa_node(ndns); if (numa >= 0 && flags & UTIL_JSON_VERBOSE) { jobj = json_object_new_int(numa); if (jobj) json_object_object_add(jndns, "numa_node", jobj); } if (pfn) jbbs = util_pfn_badblocks_to_json(pfn, &bb_count, flags); else if (dax) jbbs = util_dax_badblocks_to_json(dax, &bb_count, flags); else if (btt) util_btt_badblocks_to_json(btt, &bb_count); else { jbbs = util_region_badblocks_to_json( ndctl_namespace_get_region(ndns), &bb_count, flags); if (!jbbs) jbbs = util_namespace_badblocks_to_json(ndns, &bb_count, flags); } if (bb_count) { jobj = json_object_new_int(bb_count); if (!jobj) { json_object_put(jbbs); goto err; } json_object_object_add(jndns, "badblock_count", jobj); } if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs) json_object_object_add(jndns, "badblocks", jbbs); return jndns; err: json_object_put(jndns); return NULL; } struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, unsigned long flags) { struct json_object *jmapping = json_object_new_object(); struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping); struct json_object *jobj; int position; if (!jmapping) return NULL; jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); if (!jobj) goto err; json_object_object_add(jmapping, "dimm", jobj); jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags); if (!jobj) goto err; json_object_object_add(jmapping, "offset", jobj); jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags); if (!jobj) goto err; json_object_object_add(jmapping, "length", jobj); position = ndctl_mapping_get_position(mapping); if (position >= 0) { jobj = json_object_new_int(position); if (!jobj) goto err; json_object_object_add(jmapping, "position", jobj); } return jmapping; err: json_object_put(jmapping); return NULL; } struct json_object *util_badblock_rec_to_json(u64 block, u64 count, unsigned long flags) { struct json_object *jerr = json_object_new_object(); struct json_object *jobj; if (!jerr) return NULL; jobj = util_json_object_hex(block, flags); if (!jobj) goto err; json_object_object_add(jerr, "block", jobj); jobj = util_json_object_hex(count, flags); if (!jobj) goto err; json_object_object_add(jerr, "count", jobj); return jerr; err: json_object_put(jerr); return NULL; } ndctl-67/util/json.h000066400000000000000000000046301355562357700145400ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 __NDCTL_JSON_H__ #define __NDCTL_JSON_H__ #include #include #include #include enum util_json_flags { UTIL_JSON_IDLE = (1 << 0), UTIL_JSON_MEDIA_ERRORS = (1 << 1), UTIL_JSON_DAX = (1 << 2), UTIL_JSON_DAX_DEVS = (1 << 3), UTIL_JSON_HUMAN = (1 << 4), UTIL_JSON_VERBOSE = (1 << 5), UTIL_JSON_CAPABILITIES = (1 << 6), }; struct json_object; void util_display_json_array(FILE *f_out, struct json_object *jarray, unsigned long flags); struct json_object *util_bus_to_json(struct ndctl_bus *bus); struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, unsigned long flags); struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, unsigned long flags); struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, unsigned long flags); struct json_object *util_badblock_rec_to_json(u64 block, u64 count, unsigned long flags); struct daxctl_region; struct daxctl_dev; struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, unsigned int *bb_count, unsigned long flags); struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, const char *ident, unsigned long flags); struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, unsigned long flags); struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, struct json_object *jdevs, const char *ident, unsigned long flags); struct json_object *util_json_object_size(unsigned long long size, unsigned long flags); struct json_object *util_json_object_hex(unsigned long long val, unsigned long flags); struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm); struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, unsigned long flags); struct json_object *util_region_capabilities_to_json(struct ndctl_region *region); #endif /* __NDCTL_JSON_H__ */ ndctl-67/util/list.h000066400000000000000000000027631355562357700145470ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 _NDCTL_LIST_H_ #define _NDCTL_LIST_H_ #include /** * list_add_after - add an entry after the given node in the linked list. * @h: the list_head to add the node to * @l: the list_node after which to add to * @n: the list_node to add to the list. * * The list_node does not need to be initialized; it will be overwritten. * Example: * struct child *child = malloc(sizeof(*child)); * * child->name = "geoffrey"; * list_add_after(&parent->children, &child1->list, &child->list); * parent->num_children++; */ #define list_add_after(h, l, n) list_add_after_(h, l, n, LIST_LOC) static inline void list_add_after_(struct list_head *h, struct list_node *l, struct list_node *n, const char *abortstr) { if (l->next == &h->n) { /* l is the last element, this becomes a list_add_tail */ list_add_tail(h, n); return; } n->next = l->next; n->prev = l; l->next->prev = n; l->next = n; (void)list_debug(h, abortstr); } #endif /* _NDCTL_LIST_H_ */ ndctl-67/util/log.c000066400000000000000000000035241355562357700143440ustar00rootroot00000000000000/* * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include void do_log(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, ...) { va_list args; int errno_save = errno; va_start(args, format); ctx->log_fn(ctx, priority, file, line, fn, format, args); va_end(args); errno = errno_save; } static void log_stderr(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { fprintf(stderr, "%s: %s: ", ctx->owner, fn); vfprintf(stderr, format, args); } static int log_priority(const char *priority) { char *endptr; int prio; prio = strtol(priority, &endptr, 10); if (endptr[0] == '\0' || isspace(endptr[0])) return prio; if (strncmp(priority, "err", 3) == 0) return LOG_ERR; if (strncmp(priority, "info", 4) == 0) return LOG_INFO; if (strncmp(priority, "debug", 5) == 0) return LOG_DEBUG; if (strncmp(priority, "notice", 6) == 0) return LOG_NOTICE; return 0; } void log_init(struct log_ctx *ctx, const char *owner, const char *log_env) { const char *env; ctx->owner = owner; ctx->log_fn = log_stderr; ctx->log_priority = LOG_ERR; /* environment overwrites config */ env = secure_getenv(log_env); if (env != NULL) ctx->log_priority = log_priority(env); } ndctl-67/util/log.h000066400000000000000000000046211355562357700143500ustar00rootroot00000000000000/* * Copyright (c) 2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __UTIL_LOG_H__ #define __UTIL_LOG_H__ #include #include #include struct log_ctx; typedef void (*log_fn)(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, va_list args); struct log_ctx { log_fn log_fn; const char *owner; int log_priority; }; void do_log(struct log_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, ...) __attribute__((format(printf, 6, 7))); void log_init(struct log_ctx *ctx, const char *owner, const char *log_env); static inline void __attribute__((always_inline, format(printf, 2, 3))) log_null(struct log_ctx *ctx, const char *format, ...) {} #define log_cond(ctx, prio, arg...) \ do { \ if ((ctx)->log_priority >= prio) \ do_log(ctx, prio, __FILE__, __LINE__, __FUNCTION__, ## arg); \ } while (0) #ifdef ENABLE_LOGGING # ifdef ENABLE_DEBUG # define log_dbg(ctx, arg...) log_cond(ctx, LOG_DEBUG, ## arg) # else # define log_dbg(ctx, arg...) log_null(ctx, ## arg) # endif # define log_info(ctx, arg...) log_cond(ctx, LOG_INFO, ## arg) # define log_err(ctx, arg...) log_cond(ctx, LOG_ERR, ## arg) # define log_notice(ctx, arg...) log_cond(ctx, LOG_NOTICE, ## arg) #else # define log_dbg(ctx, arg...) log_null(ctx, ## arg) # define log_info(ctx, arg...) log_null(ctx, ## arg) # define log_err(ctx, arg...) log_null(ctx, ## arg) # define log_notice(ctx, arg...) log_null(ctx, ## arg) #endif #define dbg(x, arg...) log_dbg(&(x)->ctx, ## arg) #define info(x, arg...) log_info(&(x)->ctx, ## arg) #define err(x, arg...) log_err(&(x)->ctx, ## arg) #define notice(x, arg...) log_notice(&(x)->ctx, ## arg) #ifndef HAVE_SECURE_GETENV # ifdef HAVE___SECURE_GETENV # define secure_getenv __secure_getenv # else # warning neither secure_getenv nor __secure_getenv is available. # define secure_getenv getenv # endif #endif #endif /* __UTIL_LOG_H__ */ ndctl-67/util/main.c000066400000000000000000000063031355562357700145050ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * Copyright(c) 2006 Linus Torvalds. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #include #include #include #include #include #include #include #include #include #include int main_handle_options(const char ***argv, int *argc, const char *usage_msg, struct cmd_struct *cmds, int num_cmds) { int handled = 0; while (*argc > 0) { const char *cmd = (*argv)[0]; if (cmd[0] != '-') break; if (!strcmp(cmd, "--version") || !strcmp(cmd, "--help")) break; /* * Shortcut for '-h' and '-v' options to invoke help * and version command. */ if (!strcmp(cmd, "-h")) { (*argv)[0] = "--help"; break; } if (!strcmp(cmd, "-v")) { (*argv)[0] = "--version"; break; } if (!strcmp(cmd, "--list-cmds")) { int i; for (i = 0; i < num_cmds; i++) { struct cmd_struct *p = cmds+i; /* filter out commands from auto-complete */ if (strcmp(p->cmd, "create-nfit") == 0) continue; if (strcmp(p->cmd, "test") == 0) continue; if (strcmp(p->cmd, "bat") == 0) continue; printf("%s\n", p->cmd); } exit(0); } else { fprintf(stderr, "Unknown option: %s\n", cmd); usage(usage_msg); } (*argv)++; (*argc)--; handled++; } return handled; } static int run_builtin(struct cmd_struct *p, int argc, const char **argv, void *ctx, enum program prog) { int status; struct stat st; if (prog == PROG_NDCTL) status = p->n_fn(argc, argv, ctx); else status = p->d_fn(argc, argv, ctx); if (status) return status & 0xff; /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) return 0; /* Ignore write errors for pipes and sockets.. */ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) return 0; status = 1; /* Check for ENOSPC and EIO errors.. */ if (fflush(stdout)) { fprintf(stderr, "write failure on standard output: %s", strerror(errno)); goto out; } if (ferror(stdout)) { fprintf(stderr, "unknown write failure on standard output"); goto out; } if (fclose(stdout)) { fprintf(stderr, "close failed on standard output: %s", strerror(errno)); goto out; } status = 0; out: return status; } void main_handle_internal_command(int argc, const char **argv, void *ctx, struct cmd_struct *cmds, int num_cmds, enum program prog) { const char *cmd = argv[0]; int i; /* Turn " cmd --help" into " help cmd" */ if (argc > 1 && !strcmp(argv[1], "--help")) { argv[1] = argv[0]; argv[0] = cmd = "help"; } for (i = 0; i < num_cmds; i++) { struct cmd_struct *p = cmds+i; if (strcmp(p->cmd, cmd)) continue; exit(run_builtin(p, argc, argv, ctx, prog)); } } ndctl-67/util/main.h000066400000000000000000000023751355562357700145170ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * Copyright(c) 2006 Linus Torvalds. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #ifndef __MAIN_H__ #define __MAIN_H__ enum program { PROG_NDCTL, PROG_DAXCTL, }; struct ndctl_ctx; struct daxctl_ctx; struct cmd_struct { const char *cmd; union { int (*n_fn)(int, const char **, struct ndctl_ctx *ctx); int (*d_fn)(int, const char **, struct daxctl_ctx *ctx); }; }; int main_handle_options(const char ***argv, int *argc, const char *usage_msg, struct cmd_struct *cmds, int num_cmds); void main_handle_internal_command(int argc, const char **argv, void *ctx, struct cmd_struct *cmds, int num_cmds, enum program prog); int help_show_man_page(const char *cmd, const char *util_name, const char *viewer); #endif /* __MAIN_H__ */ ndctl-67/util/parse-options.c000066400000000000000000000431761355562357700163750ustar00rootroot00000000000000/* * Copyright(c) 2007 Pierre Habouzit. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #include #include #include #include #include #include #define OPT_SHORT 1 #define OPT_UNSET 2 static int opterror(const struct option *opt, const char *reason, int flags) { if (flags & OPT_SHORT) return error("switch `%c' %s", opt->short_name, reason); if (flags & OPT_UNSET) return error("option `no-%s' %s", opt->long_name, reason); return error("option `%s' %s", opt->long_name, reason); } static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, int flags, const char **arg) { if (p->opt) { *arg = p->opt; p->opt = NULL; } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || **(p->argv + 1) == '-')) { *arg = (const char *)opt->defval; } else if (p->argc > 1) { p->argc--; *arg = *++p->argv; } else return opterror(opt, "requires a value", flags); return 0; } static int get_value(struct parse_opt_ctx_t *p, const struct option *opt, int flags) { const char *s, *arg = NULL; const int unset = flags & OPT_UNSET; int err; if (unset && p->opt) return opterror(opt, "takes no value", flags); if (unset && (opt->flags & PARSE_OPT_NONEG)) return opterror(opt, "isn't available", flags); if (!(flags & OPT_SHORT) && p->opt) { switch (opt->type) { case OPTION_CALLBACK: if (!(opt->flags & PARSE_OPT_NOARG)) break; /* FALLTHROUGH */ case OPTION_BOOLEAN: case OPTION_INCR: case OPTION_BIT: case OPTION_SET_UINT: case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); case OPTION_END: case OPTION_ARGUMENT: case OPTION_GROUP: case OPTION_STRING: case OPTION_FILENAME: case OPTION_INTEGER: case OPTION_UINTEGER: case OPTION_LONG: case OPTION_U64: default: break; } } switch (opt->type) { case OPTION_BIT: if (unset) *(int *)opt->value &= ~opt->defval; else *(int *)opt->value |= opt->defval; return 0; case OPTION_BOOLEAN: *(bool *)opt->value = unset ? false : true; if (opt->set) *(bool *)opt->set = true; return 0; case OPTION_INCR: *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; case OPTION_SET_UINT: *(unsigned int *)opt->value = unset ? 0 : opt->defval; return 0; case OPTION_SET_PTR: *(void **)opt->value = unset ? NULL : (void *)opt->defval; return 0; case OPTION_STRING: if (unset) *(const char **)opt->value = NULL; else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) *(const char **)opt->value = (const char *)opt->defval; else return get_arg(p, opt, flags, (const char **)opt->value); return 0; case OPTION_FILENAME: err = 0; if (unset) *(const char **)opt->value = NULL; else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) *(const char **)opt->value = (const char *)opt->defval; else err = get_arg(p, opt, flags, (const char **)opt->value); if (!err) fix_filename(p->prefix, (const char **)opt->value); return err; case OPTION_CALLBACK: if (unset) return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; if (opt->flags & PARSE_OPT_NOARG) return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; if (opt->flags & PARSE_OPT_OPTARG && !p->opt) return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; if (get_arg(p, opt, flags, &arg)) return -1; return (*opt->callback)(opt, arg, 0) ? (-1) : 0; case OPTION_INTEGER: if (unset) { *(int *)opt->value = 0; return 0; } if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { *(int *)opt->value = opt->defval; return 0; } if (get_arg(p, opt, flags, &arg)) return -1; *(int *)opt->value = strtol(arg, (char **)&s, 10); if (*s) return opterror(opt, "expects a numerical value", flags); return 0; case OPTION_UINTEGER: if (unset) { *(unsigned int *)opt->value = 0; return 0; } if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { *(unsigned int *)opt->value = opt->defval; return 0; } if (get_arg(p, opt, flags, &arg)) return -1; *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); if (*s) return opterror(opt, "expects a numerical value", flags); return 0; case OPTION_LONG: if (unset) { *(long *)opt->value = 0; return 0; } if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { *(long *)opt->value = opt->defval; return 0; } if (get_arg(p, opt, flags, &arg)) return -1; *(long *)opt->value = strtol(arg, (char **)&s, 10); if (*s) return opterror(opt, "expects a numerical value", flags); return 0; case OPTION_U64: if (unset) { *(uint64_t *)opt->value = 0; return 0; } if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { *(uint64_t *)opt->value = opt->defval; return 0; } if (get_arg(p, opt, flags, &arg)) return -1; *(uint64_t *)opt->value = strtoull(arg, (char **)&s, 10); if (*s) return opterror(opt, "expects a numerical value", flags); return 0; case OPTION_END: case OPTION_ARGUMENT: case OPTION_GROUP: default: die("should not happen, someone must be hit on the forehead"); } } static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) { for (; options->type != OPTION_END; options++) { if (options->short_name == *p->opt) { p->opt = p->opt[1] ? p->opt + 1 : NULL; return get_value(p, options, OPT_SHORT); } } return -2; } static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, const struct option *options) { const char *arg_end = strchr(arg, '='); const struct option *abbrev_option = NULL, *ambiguous_option = NULL; int abbrev_flags = 0, ambiguous_flags = 0; if (!arg_end) arg_end = arg + strlen(arg); for (; options->type != OPTION_END; options++) { const char *rest; int flags = 0; if (!options->long_name) continue; rest = skip_prefix(arg, options->long_name); if (options->type == OPTION_ARGUMENT) { if (!rest) continue; if (*rest == '=') return opterror(options, "takes no value", flags); if (*rest) continue; p->out[p->cpidx++] = arg - 2; return 0; } if (!rest) { if (!prefixcmp(options->long_name, "no-")) { /* * The long name itself starts with "no-", so * accept the option without "no-" so that users * do not have to enter "no-no-" to get the * negation. */ rest = skip_prefix(arg, options->long_name + 3); if (rest) { flags |= OPT_UNSET; goto match; } /* Abbreviated case */ if (!prefixcmp(options->long_name + 3, arg)) { flags |= OPT_UNSET; goto is_abbreviated; } } /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: if (abbrev_option) { /* * If this is abbreviated, it is * ambiguous. So when there is no * exact match later, we need to * error out. */ ambiguous_option = abbrev_option; ambiguous_flags = abbrev_flags; } if (!(flags & OPT_UNSET) && *arg_end) p->opt = arg_end + 1; abbrev_option = options; abbrev_flags = flags; continue; } /* negated and abbreviated very much? */ if (!prefixcmp("no-", arg)) { flags |= OPT_UNSET; goto is_abbreviated; } /* negated? */ if (strncmp(arg, "no-", 3)) continue; flags |= OPT_UNSET; rest = skip_prefix(arg + 3, options->long_name); /* abbreviated and negated? */ if (!rest && !prefixcmp(options->long_name, arg + 3)) goto is_abbreviated; if (!rest) continue; } match: if (*rest) { if (*rest != '=') continue; p->opt = rest + 1; } return get_value(p, options, flags); } if (ambiguous_option) return error("Ambiguous option: %s " "(could be --%s%s or --%s%s)", arg, (ambiguous_flags & OPT_UNSET) ? "no-" : "", ambiguous_option->long_name, (abbrev_flags & OPT_UNSET) ? "no-" : "", abbrev_option->long_name); if (abbrev_option) return get_value(p, abbrev_option, abbrev_flags); return -2; } static void check_typos(const char *arg, const struct option *options) { if (strlen(arg) < 3) return; if (!prefixcmp(arg, "no-")) { error ("did you mean `--%s` (with two dashes ?)", arg); exit(129); } for (; options->type != OPTION_END; options++) { if (!options->long_name) continue; if (!prefixcmp(options->long_name, arg)) { error ("did you mean `--%s` (with two dashes ?)", arg); exit(129); } } } void parse_options_start(struct parse_opt_ctx_t *ctx, int argc, const char **argv, const char *prefix, int flags) { memset(ctx, 0, sizeof(*ctx)); ctx->argc = argc - 1; ctx->argv = argv + 1; ctx->out = argv; ctx->prefix = prefix; ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); ctx->flags = flags; if ((flags & PARSE_OPT_KEEP_UNKNOWN) && (flags & PARSE_OPT_STOP_AT_NON_OPTION)) die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); } static int usage_with_options_internal(const char * const *, const struct option *, int); int parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, const char * const usagestr[]) { int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); /* we must reset ->opt, unknown short option leave it dangling */ ctx->opt = NULL; for (; ctx->argc; ctx->argc--, ctx->argv++) { const char *arg = ctx->argv[0]; if (*arg != '-' || !arg[1]) { if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) break; ctx->out[ctx->cpidx++] = ctx->argv[0]; continue; } if (arg[1] != '-') { ctx->opt = arg + 1; if (internal_help && *ctx->opt == 'h') return usage_with_options_internal(usagestr, options, 0); switch (parse_short_opt(ctx, options)) { case -1: return parse_options_usage(usagestr, options, arg + 1, 1); case -2: goto unknown; default: break; } if (ctx->opt) check_typos(arg + 1, options); while (ctx->opt) { if (internal_help && *ctx->opt == 'h') return usage_with_options_internal(usagestr, options, 0); arg = ctx->opt; switch (parse_short_opt(ctx, options)) { case -1: return parse_options_usage(usagestr, options, arg, 1); case -2: /* fake a short option thing to hide the fact that we may have * started to parse aggregated stuff * * This is leaky, too bad. */ ctx->argv[0] = strdup(ctx->opt - 1); *(char *)ctx->argv[0] = '-'; goto unknown; default: break; } } continue; } if (!arg[2]) { /* "--" */ if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { ctx->argc--; ctx->argv++; } break; } if (internal_help && !strcmp(arg + 2, "help-all")) return usage_with_options_internal(usagestr, options, 1); if (internal_help && !strcmp(arg + 2, "help")) return usage_with_options_internal(usagestr, options, 0); if (!strcmp(arg + 2, "list-opts")) return PARSE_OPT_LIST_OPTS; if (!strcmp(arg + 2, "list-cmds")) return PARSE_OPT_LIST_SUBCMDS; switch (parse_long_opt(ctx, arg + 2, options)) { case -1: return parse_options_usage(usagestr, options, arg + 2, 0); case -2: goto unknown; default: break; } continue; unknown: if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) return PARSE_OPT_UNKNOWN; ctx->out[ctx->cpidx++] = ctx->argv[0]; ctx->opt = NULL; } return PARSE_OPT_DONE; } int parse_options_end(struct parse_opt_ctx_t *ctx) { memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); ctx->out[ctx->cpidx + ctx->argc] = NULL; return ctx->cpidx + ctx->argc; } static int parse_options_subcommand_prefix(int argc, const char **argv, const char *prefix, const struct option *options, const char *const subcommands[], const char *usagestr[], int flags) { struct parse_opt_ctx_t ctx; /* build usage string if it's not provided */ if (subcommands && !usagestr[0]) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "ndctl %s [] {", argv[0]); for (int i = 0; subcommands[i]; i++) { if (i) strbuf_addstr(&buf, "|"); strbuf_addstr(&buf, subcommands[i]); } strbuf_addstr(&buf, "}"); usagestr[0] = strdup(buf.buf); strbuf_release(&buf); } parse_options_start(&ctx, argc, argv, prefix, flags); switch (parse_options_step(&ctx, options, usagestr)) { case PARSE_OPT_HELP: exit(129); case PARSE_OPT_DONE: break; case PARSE_OPT_LIST_OPTS: while (options->type != OPTION_END) { printf("--%s ", options->long_name); options++; } exit(130); case PARSE_OPT_LIST_SUBCMDS: if (subcommands) for (int i = 0; subcommands[i]; i++) printf("%s ", subcommands[i]); exit(130); default: /* PARSE_OPT_UNKNOWN */ if (ctx.argv[0][1] == '-') { error("unknown option `%s'", ctx.argv[0] + 2); } else { error("unknown switch `%c'", *ctx.opt); } usage_with_options(usagestr, options); } return parse_options_end(&ctx); } int parse_options_subcommand(int argc, const char **argv, const struct option *options, const char *const subcommands[], const char *usagestr[], int flags) { return parse_options_subcommand_prefix(argc, argv, NULL, options, subcommands, usagestr, flags); } int parse_options_prefix(int argc, const char **argv, const char *prefix, const struct option *options, const char * const usagestr[], int flags) { return parse_options_subcommand_prefix(argc, argv, prefix, options, NULL, (const char **) usagestr, flags); } int parse_options(int argc, const char **argv, const struct option *options, const char * const usagestr[], int flags) { return parse_options_subcommand_prefix(argc, argv, NULL, options, NULL, (const char **) usagestr, flags); } #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 static void print_option_help(const struct option *opts, int full) { size_t pos; int pad; if (opts->type == OPTION_GROUP) { fputc('\n', stderr); if (*opts->help) fprintf(stderr, "%s\n", opts->help); return; } if (!full && (opts->flags & PARSE_OPT_HIDDEN)) return; pos = fprintf(stderr, " "); if (opts->short_name) pos += fprintf(stderr, "-%c", opts->short_name); else pos += fprintf(stderr, " "); if (opts->long_name && opts->short_name) pos += fprintf(stderr, ", "); if (opts->long_name) pos += fprintf(stderr, "--%s", opts->long_name); switch (opts->type) { case OPTION_ARGUMENT: break; case OPTION_LONG: case OPTION_U64: case OPTION_INTEGER: case OPTION_UINTEGER: if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) pos += fprintf(stderr, "[=]"); else pos += fprintf(stderr, "[]"); else pos += fprintf(stderr, " "); break; case OPTION_CALLBACK: if (opts->flags & PARSE_OPT_NOARG) break; /* FALLTHROUGH */ case OPTION_FILENAME: case OPTION_STRING: if (opts->argh) { if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) pos += fprintf(stderr, "[=<%s>]", opts->argh); else pos += fprintf(stderr, "[<%s>]", opts->argh); else pos += fprintf(stderr, " <%s>", opts->argh); } else { if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) pos += fprintf(stderr, "[=...]"); else pos += fprintf(stderr, "[...]"); else pos += fprintf(stderr, " ..."); } break; default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ case OPTION_END: case OPTION_GROUP: case OPTION_BIT: case OPTION_BOOLEAN: case OPTION_INCR: case OPTION_SET_UINT: case OPTION_SET_PTR: break; } if (pos <= USAGE_OPTS_WIDTH) pad = USAGE_OPTS_WIDTH - pos; else { fputc('\n', stderr); pad = USAGE_OPTS_WIDTH; } fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); } int usage_with_options_internal(const char * const *usagestr, const struct option *opts, int full) { if (!usagestr) return PARSE_OPT_HELP; fprintf(stderr, "\n usage: %s\n", *usagestr++); while (*usagestr && **usagestr) fprintf(stderr, " or: %s\n", *usagestr++); while (*usagestr) { fprintf(stderr, "%s%s\n", **usagestr ? " " : "", *usagestr); usagestr++; } if (opts->type != OPTION_GROUP) fputc('\n', stderr); for ( ; opts->type != OPTION_END; opts++) print_option_help(opts, full); fputc('\n', stderr); return PARSE_OPT_HELP; } void usage_with_options(const char * const *usagestr, const struct option *opts) { usage_with_options_internal(usagestr, opts, 0); exit(129); } int parse_options_usage(const char * const *usagestr, const struct option *opts, const char *optstr, bool short_opt) { if (!usagestr) goto opt; fprintf(stderr, "\n usage: %s\n", *usagestr++); while (*usagestr && **usagestr) fprintf(stderr, " or: %s\n", *usagestr++); while (*usagestr) { fprintf(stderr, "%s%s\n", **usagestr ? " " : "", *usagestr); usagestr++; } fputc('\n', stderr); opt: for ( ; opts->type != OPTION_END; opts++) { if (short_opt) { if (opts->short_name == *optstr) break; continue; } if (opts->long_name == NULL) continue; if (!prefixcmp(optstr, opts->long_name)) break; if (!prefixcmp(optstr, "no-") && !prefixcmp(optstr + 3, opts->long_name)) break; } if (opts->type != OPTION_END) print_option_help(opts, 0); return PARSE_OPT_HELP; } int parse_opt_verbosity_cb(const struct option *opt, const char *arg __maybe_unused, int unset) { int *target = opt->value; if (unset) /* --no-quiet, --no-verbose */ *target = 0; else if (opt->short_name == 'v') { if (*target >= 0) (*target)++; else *target = 1; } else { if (*target <= 0) (*target)--; else *target = -1; } return 0; } ndctl-67/util/parse-options.h000066400000000000000000000216021355562357700163700ustar00rootroot00000000000000/* * Copyright(c) 2007 Pierre Habouzit. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #ifndef __NDCTL_PARSE_OPTIONS_H #define __NDCTL_PARSE_OPTIONS_H #include #include #include enum parse_opt_type { /* special types */ OPTION_END, OPTION_ARGUMENT, OPTION_GROUP, /* options with no arguments */ OPTION_BIT, OPTION_BOOLEAN, OPTION_INCR, OPTION_SET_UINT, OPTION_SET_PTR, /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, OPTION_LONG, OPTION_CALLBACK, OPTION_U64, OPTION_UINTEGER, OPTION_FILENAME, }; enum parse_opt_flags { PARSE_OPT_KEEP_DASHDASH = 1, PARSE_OPT_STOP_AT_NON_OPTION = 2, PARSE_OPT_KEEP_ARGV0 = 4, PARSE_OPT_KEEP_UNKNOWN = 8, PARSE_OPT_NO_INTERNAL_HELP = 16, }; enum parse_opt_option_flags { PARSE_OPT_OPTARG = 1, PARSE_OPT_NOARG = 2, PARSE_OPT_NONEG = 4, PARSE_OPT_HIDDEN = 8, PARSE_OPT_LASTARG_DEFAULT = 16, }; struct option; typedef int parse_opt_cb(const struct option *, const char *arg, int unset); /* * `type`:: * holds the type of the option, you must have an OPTION_END last in your * array. * * `short_name`:: * the character to use as a short option name, '\0' if none. * * `long_name`:: * the long option name, without the leading dashes, NULL if none. * * `value`:: * stores pointers to the values to be filled. * * `argh`:: * token to explain the kind of argument this option wants. Keep it * homogenous across the repository. * * `help`:: * the short help associated to what the option does. * Must never be NULL (except for OPTION_END). * OPTION_GROUP uses this pointer to store the group header. * * `flags`:: * mask of parse_opt_option_flags. * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs * PARSE_OPT_NONEG: says that this option cannot be negated * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in * the long one. * * `callback`:: * pointer to the callback to use for OPTION_CALLBACK. * * `defval`:: * default value to fill (*->value) with for PARSE_OPT_OPTARG. * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. * * `set`:: * whether an option was set by the user */ struct option { enum parse_opt_type type; int short_name; const char *long_name; void *value; const char *argh; const char *help; int flags; parse_opt_cb *callback; intptr_t defval; bool *set; }; #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) #define OPT_END() { .type = OPTION_END } #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } #define OPT_BOOLEAN_SET(s, l, v, os, h) \ { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ .value = check_vtype(v, bool *), .help = (h), \ .set = check_vtype(os, bool *)} #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } #define OPT_FILENAME(s, l, v, a, h) { .type = OPTION_FILENAME, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } #define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } #define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} /* parse_options() will filter out the processed options and leave the * non-option argments in argv[]. * Returns the number of arguments left in argv[]. */ extern int parse_options(int argc, const char **argv, const struct option *options, const char * const usagestr[], int flags); extern int parse_options_prefix(int argc, const char **argv, const char *prefix, const struct option *options, const char * const usagestr[], int flags); extern int parse_options_subcommand(int argc, const char **argv, const struct option *options, const char *const subcommands[], const char *usagestr[], int flags); extern NORETURN void usage_with_options(const char * const *usagestr, const struct option *options); /*----- incremantal advanced APIs -----*/ enum { PARSE_OPT_HELP = -1, PARSE_OPT_DONE, PARSE_OPT_LIST_OPTS, PARSE_OPT_LIST_SUBCMDS, PARSE_OPT_UNKNOWN, }; /* * It's okay for the caller to consume argv/argc in the usual way. * Other fields of that structure are private to parse-options and should not * be modified in any way. */ struct parse_opt_ctx_t { const char **argv; const char **out; int argc, cpidx; const char *opt; int flags; const char *prefix; }; extern int parse_options_usage(const char * const *usagestr, const struct option *opts, const char *optstr, bool short_opt); extern void parse_options_start(struct parse_opt_ctx_t *ctx, int argc, const char **argv, const char *prefix, int flags); extern int parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, const char * const usagestr[]); extern int parse_options_end(struct parse_opt_ctx_t *ctx); /*----- some often used options -----*/ extern int parse_opt_abbrev_cb(const struct option *, const char *, int); extern int parse_opt_approxidate_cb(const struct option *, const char *, int); extern int parse_opt_verbosity_cb(const struct option *, const char *, int); #define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") #define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") #define OPT__VERBOSITY(var) \ { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } #define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") #define OPT__ABBREV(var) \ { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ "use digits to display SHA-1s", \ PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } extern const char *parse_options_fix_filename(const char *prefix, const char *file); #endif /* __NDCTL_PARSE_OPTIONS_H */ ndctl-67/util/size.c000066400000000000000000000025651355562357700145410ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 #include #include unsigned long long __parse_size64(const char *str, unsigned long long *units) { unsigned long long val, check; char *end; val = strtoull(str, &end, 0); if (val == ULLONG_MAX) return val; check = val; switch (*end) { case 'k': case 'K': if (units) *units = SZ_1K; val *= SZ_1K; end++; break; case 'm': case 'M': if (units) *units = SZ_1M; val *= SZ_1M; end++; break; case 'g': case 'G': if (units) *units = SZ_1G; val *= SZ_1G; end++; break; case 't': case 'T': if (units) *units = SZ_1T; val *= SZ_1T; end++; break; default: if (units) *units = 1; break; } if (val < check || *end != '\0') val = ULLONG_MAX; return val; } unsigned long long parse_size64(const char *str) { return __parse_size64(str, NULL); } ndctl-67/util/size.h000066400000000000000000000022721355562357700145410ustar00rootroot00000000000000/* * Copyright(c) 2015-2017 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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 _NDCTL_SIZE_H_ #define _NDCTL_SIZE_H_ #define SZ_1K 0x00000400 #define SZ_4K 0x00001000 #define SZ_1M 0x00100000 #define SZ_2M 0x00200000 #define SZ_4M 0x00400000 #define SZ_16M 0x01000000 #define SZ_64M 0x04000000 #define SZ_1G 0x40000000 #define SZ_1T 0x10000000000ULL unsigned long long parse_size64(const char *str); unsigned long long __parse_size64(const char *str, unsigned long long *units); #define ALIGN(x, a) ((((unsigned long long) x) + (a - 1)) & ~(a - 1)) #define ALIGN_DOWN(x, a) (((((unsigned long long) x) + a) & ~(a - 1)) - a) #define BITS_PER_LONG (sizeof(unsigned long) * 8) #define HPAGE_SIZE (2 << 20) #endif /* _NDCTL_SIZE_H_ */ ndctl-67/util/strbuf.c000066400000000000000000000067641355562357700151010ustar00rootroot00000000000000/* * Copyright(c) 2005 Junio C Hamano. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #include #include #include #include #include int prefixcmp(const char *str, const char *prefix) { for (; ; str++, prefix++) if (!*prefix) return 0; else if (*str != *prefix) return (unsigned char)*prefix - (unsigned char)*str; } /* * Used as the default ->buf value, so that people can always assume * buf is non NULL and ->buf is NUL terminated even for a freshly * initialized strbuf. */ char strbuf_slopbuf[1]; void strbuf_init(struct strbuf *sb, ssize_t hint) { sb->alloc = sb->len = 0; sb->buf = strbuf_slopbuf; if (hint) strbuf_grow(sb, hint); } void strbuf_release(struct strbuf *sb) { if (sb->alloc) { zfree(&sb->buf); strbuf_init(sb, 0); } } char *strbuf_detach(struct strbuf *sb, size_t *sz) { char *res = sb->alloc ? sb->buf : NULL; if (sz) *sz = sb->len; strbuf_init(sb, 0); return res; } void strbuf_grow(struct strbuf *sb, size_t extra) { if (sb->len + extra + 1 <= sb->len) die("you want to use way too much memory"); if (!sb->alloc) sb->buf = NULL; ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); } static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, const void *data, size_t dlen) { if (pos + len < pos) die("you want to use way too much memory"); if (pos > sb->len) die("`pos' is too far after the end of the buffer"); if (pos + len > sb->len) die("`pos + len' is too far after the end of the buffer"); if (dlen >= len) strbuf_grow(sb, dlen - len); memmove(sb->buf + pos + dlen, sb->buf + pos + len, sb->len - pos - len); memcpy(sb->buf + pos, data, dlen); strbuf_setlen(sb, sb->len + dlen - len); } void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) { strbuf_splice(sb, pos, len, NULL, 0); } void strbuf_add(struct strbuf *sb, const void *data, size_t len) { strbuf_grow(sb, len); memcpy(sb->buf + sb->len, data, len); strbuf_setlen(sb, sb->len + len); } void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { int len; va_list ap; if (!strbuf_avail(sb)) strbuf_grow(sb, 64); va_start(ap, fmt); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); if (len < 0) die("your vsnprintf is broken"); if (len > strbuf_avail(sb)) { strbuf_grow(sb, len); va_start(ap, fmt); len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); va_end(ap); if (len > strbuf_avail(sb)) { die("this should not happen, your vsnprintf is broken"); } } strbuf_setlen(sb, sb->len + len); } ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint) { size_t oldlen = sb->len; size_t oldalloc = sb->alloc; strbuf_grow(sb, hint ? hint : 8192); for (;;) { ssize_t cnt; cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); if (cnt < 0) { if (oldalloc == 0) strbuf_release(sb); else strbuf_setlen(sb, oldlen); return -1; } if (!cnt) break; sb->len += cnt; strbuf_grow(sb, 8192); } sb->buf[sb->len] = '\0'; return sb->len - oldlen; } ndctl-67/util/strbuf.h000066400000000000000000000071001355562357700150670ustar00rootroot00000000000000/* * Copyright(c) 2005 Junio C Hamano. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #ifndef __NDCTL_STRBUF_H #define __NDCTL_STRBUF_H #include #include #include /* for ssize_t */ /* * Strbuf's can be use in many ways: as a byte array, or to store arbitrary * long, overflow safe strings. * * Strbufs has some invariants that are very important to keep in mind: * * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to * build complex strings/buffers whose final size isn't easily known. * * It is NOT legal to copy the ->buf pointer away. * `strbuf_detach' is the operation that detachs a buffer from its shell * while keeping the shell valid wrt its invariants. * * 2. the ->buf member is a byte array that has at least ->len + 1 bytes * allocated. The extra byte is used to store a '\0', allowing the ->buf * member to be a valid C-string. Every strbuf function ensure this * invariant is preserved. * * Note that it is OK to "play" with the buffer directly if you work it * that way: * * strbuf_grow(sb, SOME_SIZE); * ... Here, the memory array starting at sb->buf, and of length * ... strbuf_avail(sb) is all yours, and you are sure that * ... strbuf_avail(sb) is at least SOME_SIZE. * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); * * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb). * * Doing so is safe, though if it has to be done in many places, adding the * missing API to the strbuf module is the way to go. * * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1 * even if it's true in the current implementation. Alloc is somehow a * "private" member that should not be messed with. */ extern char strbuf_slopbuf[]; struct strbuf { size_t alloc; size_t len; char *buf; }; #define STRBUF_INIT { 0, 0, strbuf_slopbuf } /*----- strbuf life cycle -----*/ extern void strbuf_init(struct strbuf *buf, ssize_t hint); extern void strbuf_release(struct strbuf *); extern char *strbuf_detach(struct strbuf *, size_t *); /*----- strbuf size related -----*/ static inline ssize_t strbuf_avail(const struct strbuf *sb) { return sb->alloc ? sb->alloc - sb->len - 1 : 0; } extern void strbuf_grow(struct strbuf *, size_t); static inline void strbuf_setlen(struct strbuf *sb, size_t len) { if (!sb->alloc) strbuf_grow(sb, 0); assert(len < sb->alloc); sb->len = len; sb->buf[len] = '\0'; } /*----- add data in your buffer -----*/ static inline void strbuf_addch(struct strbuf *sb, int c) { strbuf_grow(sb, 1); sb->buf[sb->len++] = c; sb->buf[sb->len] = '\0'; } extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); extern void strbuf_add(struct strbuf *, const void *, size_t); static inline void strbuf_addstr(struct strbuf *sb, const char *s) { strbuf_add(sb, s, strlen(s)); } __attribute__((format(printf,2,3))) extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); /* XXX: if read fails, any partial read is undone */ extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint); #endif /* __NDCTL_STRBUF_H */ ndctl-67/util/sysfs.c000066400000000000000000000057431355562357700147370ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int __sysfs_read_attr(struct log_ctx *ctx, const char *path, char *buf) { int fd = open(path, O_RDONLY|O_CLOEXEC); int n; if (fd < 0) { log_dbg(ctx, "failed to open %s: %s\n", path, strerror(errno)); return -errno; } n = read(fd, buf, SYSFS_ATTR_SIZE); close(fd); if (n < 0 || n >= SYSFS_ATTR_SIZE) { buf[0] = 0; log_dbg(ctx, "failed to read %s: %s\n", path, strerror(errno)); return -errno; } buf[n] = 0; if (n && buf[n-1] == '\n') buf[n-1] = 0; return 0; } static int write_attr(struct log_ctx *ctx, const char *path, const char *buf, int quiet) { int fd = open(path, O_WRONLY|O_CLOEXEC); int n, len = strlen(buf) + 1, rc; if (fd < 0) { rc = -errno; log_dbg(ctx, "failed to open %s: %s\n", path, strerror(errno)); return rc; } n = write(fd, buf, len); rc = -errno; close(fd); if (n < len) { if (!quiet) log_dbg(ctx, "failed to write %s to %s: %s\n", buf, path, strerror(errno)); return rc; } return 0; } int __sysfs_write_attr(struct log_ctx *ctx, const char *path, const char *buf) { return write_attr(ctx, path, buf, 0); } int __sysfs_write_attr_quiet(struct log_ctx *ctx, const char *path, const char *buf) { return write_attr(ctx, path, buf, 1); } int __sysfs_device_parse(struct log_ctx *ctx, const char *base_path, const char *dev_name, void *parent, add_dev_fn add_dev) { int add_errors = 0; struct dirent *de; DIR *dir; log_dbg(ctx, "base: '%s' dev: '%s'\n", base_path, dev_name); dir = opendir(base_path); if (!dir) { log_dbg(ctx, "no \"%s\" devices found\n", dev_name); return -ENODEV; } while ((de = readdir(dir)) != NULL) { char *dev_path; char fmt[20]; void *dev; int id; sprintf(fmt, "%s%%d", dev_name); if (de->d_ino == 0) continue; if (sscanf(de->d_name, fmt, &id) != 1) continue; if (asprintf(&dev_path, "%s/%s", base_path, de->d_name) < 0) { log_err(ctx, "%s%d: path allocation failure\n", dev_name, id); continue; } dev = add_dev(parent, id, dev_path); free(dev_path); if (!dev) { add_errors++; log_err(ctx, "%s%d: add_dev() failed\n", dev_name, id); } else log_dbg(ctx, "%s%d: processed\n", dev_name, id); } closedir(dir); return add_errors; } ndctl-67/util/sysfs.h000066400000000000000000000030171355562357700147340ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope 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. */ #ifndef __UTIL_SYSFS_H__ #define __UTIL_SYSFS_H__ #include typedef void *(*add_dev_fn)(void *parent, int id, const char *dev_path); #define SYSFS_ATTR_SIZE 1024 struct log_ctx; int __sysfs_read_attr(struct log_ctx *ctx, const char *path, char *buf); int __sysfs_write_attr(struct log_ctx *ctx, const char *path, const char *buf); int __sysfs_write_attr_quiet(struct log_ctx *ctx, const char *path, const char *buf); int __sysfs_device_parse(struct log_ctx *ctx, const char *base_path, const char *dev_name, void *parent, add_dev_fn add_dev); #define sysfs_read_attr(c, p, b) __sysfs_read_attr(&(c)->ctx, (p), (b)) #define sysfs_write_attr(c, p, b) __sysfs_write_attr(&(c)->ctx, (p), (b)) #define sysfs_write_attr_quiet(c, p, b) __sysfs_write_attr_quiet(&(c)->ctx, (p), (b)) #define sysfs_device_parse(c, b, d, p, fn) __sysfs_device_parse(&(c)->ctx, \ (b), (d), (p), (fn)) static inline const char *devpath_to_devname(const char *devpath) { return strrchr(devpath, '/') + 1; } #endif /* __UTIL_SYSFS_H__ */ ndctl-67/util/usage.c000066400000000000000000000042461355562357700146710ustar00rootroot00000000000000/* * Copyright(c) 2005 Linus Torvalds. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #include #include #include #include static void report(const char *prefix, const char *err, va_list params) { char msg[1024]; vsnprintf(msg, sizeof(msg), err, params); fprintf(stderr, " %s%s\n", prefix, msg); } static NORETURN void usage_builtin(const char *err) { fprintf(stderr, "\n Usage: %s\n", err); exit(129); } static NORETURN void die_builtin(const char *err, va_list params) { report(" Fatal: ", err, params); exit(128); } static void error_builtin(const char *err, va_list params) { report(" Error: ", err, params); } static void warn_builtin(const char *warn, va_list params) { report(" Warning: ", warn, params); } /* If we are in a dlopen()ed .so write to a global variable would segfault * (ugh), so keep things static. */ static void (*usage_routine)(const char *err) NORETURN = usage_builtin; static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin; static void (*error_routine)(const char *err, va_list params) = error_builtin; static void (*warn_routine)(const char *err, va_list params) = warn_builtin; void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN) { die_routine = routine; } void usage(const char *err) { usage_routine(err); } void die(const char *err, ...) { va_list params; va_start(params, err); die_routine(err, params); va_end(params); } int error(const char *err, ...) { va_list params; va_start(params, err); error_routine(err, params); va_end(params); return -1; } void warning(const char *warn, ...) { va_list params; va_start(params, warn); warn_routine(warn, params); va_end(params); } ndctl-67/util/util.h000066400000000000000000000060171355562357700145450ustar00rootroot00000000000000/* * Copyright(c) 2005 Junio C Hamano. All rights reserved. * Copyright(c) 2005 Linus Torvalds. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ #ifndef __UTIL_H__ #define __UTIL_H__ #include #include #include #pragma GCC diagnostic ignored "-Wmissing-prototypes" #ifdef __GNUC__ #define NORETURN __attribute__((__noreturn__)) #else #define NORETURN #ifndef __attribute__ #define __attribute__(x) #endif #endif #ifndef __maybe_unused # define __maybe_unused /* unimplemented */ #endif #define is_dir_sep(c) ((c) == '/') #define alloc_nr(x) (((x)+16)*3/2) #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) #define round_down(x, y) ((x) & ~__round_mask(x, y)) #define rounddown(x, y) ( \ { \ typeof(x) __x = (x); \ __x - (__x % (y)); \ } \ ) /* * Realloc the buffer pointed at by variable 'x' so that it can hold * at least 'nr' entries; the number of entries currently allocated * is 'alloc', using the standard growing factor alloc_nr() macro. * * DO NOT USE any expression with side-effect for 'x' or 'alloc'. */ #define ALLOC_GROW(x, nr, alloc) \ do { \ if ((nr) > alloc) { \ if (alloc_nr(alloc) < (nr)) \ alloc = (nr); \ else \ alloc = alloc_nr(alloc); \ x = xrealloc((x), alloc * sizeof(*(x))); \ } \ } while(0) #define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) enum { READ, WRITE, }; static inline const char *skip_prefix(const char *str, const char *prefix) { size_t len = strlen(prefix); return strncmp(str, prefix, len) ? NULL : str + len; } static inline int is_absolute_path(const char *path) { return path[0] == '/'; } void usage(const char *err) NORETURN; void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); int error(const char *err, ...) __attribute__((format (printf, 1, 2))); void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); char *xstrdup(const char *str); void *xrealloc(void *ptr, size_t size); int prefixcmp(const char *str, const char *prefix); char *prefix_filename(const char *pfx, const char *arg); void fix_filename(const char *prefix, const char **file); #endif /* __UTIL_H__ */ ndctl-67/util/wrapper.c000066400000000000000000000022401355562357700152350ustar00rootroot00000000000000/* * Copyright(c) 2005 Junio C Hamano. All rights reserved. * Copyright(c) 2005 Linus Torvalds. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License 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. */ /* originally copied from perf and git */ /* * Various trivial helper wrappers around standard functions */ #include #include #include char *xstrdup(const char *str) { char *ret = strdup(str); if (!ret) { ret = strdup(str); if (!ret) die("Out of memory, strdup failed"); } return ret; } void *xrealloc(void *ptr, size_t size) { void *ret = realloc(ptr, size); if (!ret && !size) ret = realloc(ptr, 1); if (!ret) { ret = realloc(ptr, size); if (!ret && !size) ret = realloc(ptr, 1); if (!ret) die("Out of memory, realloc failed"); } return ret; }