pax_global_header00006660000000000000000000000064133177760720014527gustar00rootroot0000000000000052 comment=39ef3d07dd7f7337780884659008e5a26e9f76cf ndctl-61.2/000077500000000000000000000000001331777607200125635ustar00rootroot00000000000000ndctl-61.2/.gitignore000066400000000000000000000015601331777607200145550ustar00rootroot00000000000000*.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/daxctl daxctl/lib/libdaxctl.la daxctl/lib/libdaxctl.lo daxctl/lib/libdaxctl.pc *.a 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 ndctl-61.2/COPYING000066400000000000000000000636251331777607200136320ustar00rootroot00000000000000 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-61.2/Documentation/000077500000000000000000000000001331777607200153745ustar00rootroot00000000000000ndctl-61.2/Documentation/COPYING000066400000000000000000000433341331777607200164360ustar00rootroot00000000000000All 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-61.2/Documentation/asciidoc.conf.in000066400000000000000000000046551331777607200204400ustar00rootroot00000000000000## 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-61.2/Documentation/asciidoctor-extensions.rb.in000066400000000000000000000016341331777607200230320ustar00rootroot00000000000000require '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-61.2/Documentation/copyright.txt000066400000000000000000000004471331777607200201520ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 COPYRIGHT --------- Copyright (c) 2016 - 2018, 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-61.2/Documentation/daxctl/000077500000000000000000000000001331777607200166535ustar00rootroot00000000000000ndctl-61.2/Documentation/daxctl/Makefile.am000066400000000000000000000030261331777607200207100ustar00rootroot00000000000000# 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 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-61.2/Documentation/daxctl/daxctl-list.txt000066400000000000000000000035631331777607200216530ustar00rootroot00000000000000// 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-61.2/Documentation/daxctl/daxctl.txt000066400000000000000000000012671331777607200207010ustar00rootroot00000000000000// 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-61.2/Documentation/manpage-base.xsl000066400000000000000000000026721331777607200204530ustar00rootroot00000000000000 sp br ndctl-61.2/Documentation/manpage-normal.xsl000066400000000000000000000013551331777607200210260ustar00rootroot00000000000000 \ . ndctl-61.2/Documentation/ndctl/000077500000000000000000000000001331777607200165005ustar00rootroot00000000000000ndctl-61.2/Documentation/ndctl/Makefile.am000066400000000000000000000042701331777607200205370ustar00rootroot00000000000000# 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-inject-error.1 \ ndctl-inject-smart.1 \ ndctl-update-firmware.1 \ ndctl-list.1 CLEANFILES = $(man1_MANS) 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 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-61.2/Documentation/ndctl/ars-description.txt000066400000000000000000000005731331777607200223540ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/dimm-description.txt000066400000000000000000000015141331777607200225110ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/human-option.txt000066400000000000000000000004031331777607200216540ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/labels-description.txt000066400000000000000000000004271331777607200230270ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/labels-options.txt000066400000000000000000000007341331777607200222000ustar00rootroot00000000000000// 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). -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-61.2/Documentation/ndctl/namespace-description.txt000066400000000000000000000053411331777607200235210ustar00rootroot00000000000000// 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. 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-61.2/Documentation/ndctl/ndctl-check-labels.txt000066400000000000000000000012051331777607200226560ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-check-namespace.txt000066400000000000000000000041011331777607200233460ustar00rootroot00000000000000// 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[] 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-61.2/Documentation/ndctl/ndctl-create-namespace.txt000066400000000000000000000203221331777607200235370ustar00rootroot00000000000000// 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 that a filesystem is required for dax operation, the resulting raw block device (/dev/pmemX) 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. -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. -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[] 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-61.2/Documentation/ndctl/ndctl-destroy-namespace.txt000066400000000000000000000014561331777607200237740ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-disable-dimm.txt000066400000000000000000000005771331777607200227030ustar00rootroot00000000000000// 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[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-enable-dimm[1] ndctl-61.2/Documentation/ndctl/ndctl-disable-namespace.txt000066400000000000000000000006361331777607200237050ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-disable-region.txt000066400000000000000000000006541331777607200232340ustar00rootroot00000000000000// 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[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-enable-region[1] ndctl-61.2/Documentation/ndctl/ndctl-enable-dimm.txt000066400000000000000000000005631331777607200225210ustar00rootroot00000000000000// 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[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-dimm[1] ndctl-61.2/Documentation/ndctl/ndctl-enable-namespace.txt000066400000000000000000000006321331777607200235240ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-enable-region.txt000066400000000000000000000006501331777607200230530ustar00rootroot00000000000000// 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[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-disable-region[1] ndctl-61.2/Documentation/ndctl/ndctl-init-labels.txt000066400000000000000000000036411331777607200225520ustar00rootroot00000000000000// 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. 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-61.2/Documentation/ndctl/ndctl-inject-error.txt000066400000000000000000000076601331777607200227570ustar00rootroot00000000000000// 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[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-list[1], ndctl-61.2/Documentation/ndctl/ndctl-inject-smart.txt000066400000000000000000000044171331777607200227510ustar00rootroot00000000000000// 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'. -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'. -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'. -H:: --health=:: Smart attribute for health status. Provide either 'fatal' or 'nominal' to set the state of the attribute. -U:: --unsafe-shutdown=:: Set the flag to spoof an unsafe shutdown on the next power down. -v:: --verbose:: Emit debug messages for the error injection process include::human-option.txt[] include::../copyright.txt[] SEE ALSO -------- linkndctl:ndctl-list[1], ndctl-61.2/Documentation/ndctl/ndctl-list.txt000066400000000000000000000104231331777607200213160ustar00rootroot00000000000000// 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[] -d:: --dimm=:: An 'nmemX' device name, or dimm id number. 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 -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 -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 } ] } 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-61.2/Documentation/ndctl/ndctl-read-labels.txt000066400000000000000000000013371331777607200225220ustar00rootroot00000000000000// 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[] -o:: --output:: output file -j:: --json:: parse the label data into json assuming the 'NVDIMM Namespace Specification' format. include::../copyright.txt[] SEE ALSO -------- http://www.uefi.org/sites/default/files/resources/UEFI_Spec_2_7.pdf[UEFI NVDIMM Label Protocol] ndctl-61.2/Documentation/ndctl/ndctl-start-scrub.txt000066400000000000000000000026531331777607200226220ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-update-firmware.txt000066400000000000000000000012221331777607200234340ustar00rootroot00000000000000// 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[] -f:: --firmware:: firmware file used to perform the update -v:: --verbose:: Emit debug messages for the namespace check process. include::../copyright.txt[] ndctl-61.2/Documentation/ndctl/ndctl-wait-scrub.txt000066400000000000000000000030441331777607200224240ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-write-labels.txt000066400000000000000000000012361331777607200227370ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl-zero-labels.txt000066400000000000000000000010441331777607200225610ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/ndctl.txt000066400000000000000000000026421331777607200203510ustar00rootroot00000000000000// 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, namspaces) 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-61.2/Documentation/ndctl/region-description.txt000066400000000000000000000007071331777607200230510ustar00rootroot00000000000000// 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-61.2/Documentation/ndctl/xable-dimm-options.txt000066400000000000000000000006051331777607200227520ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 A 'nmemX' device name, or a dimm id number. The keyword 'all' can be specified to carry out the operation on every dimm in the system, optionally filtered by bus id (see --bus= option). -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. ndctl-61.2/Documentation/ndctl/xable-namespace-options.txt000066400000000000000000000005361331777607200237630ustar00rootroot00000000000000// 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[] -v:: --verbose:: Emit debug messages for the namespace operation ndctl-61.2/Documentation/ndctl/xable-region-options.txt000066400000000000000000000006131331777607200233060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 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). -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. ndctl-61.2/Makefile.am000066400000000000000000000040051331777607200146160ustar00rootroot00000000000000include 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 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 nobase_include_HEADERS = daxctl/libdaxctl.h ndctl-61.2/Makefile.am.in000066400000000000000000000016271331777607200152320ustar00rootroot00000000000000EXTRA_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=16 LIBNDCTL_REVISION=1 LIBNDCTL_AGE=10 LIBDAXCTL_CURRENT=3 LIBDAXCTL_REVISION=0 LIBDAXCTL_AGE=2 ndctl-61.2/README.md000066400000000000000000000071001331777607200140400ustar00rootroot00000000000000# 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 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-61.2/autogen.sh000077500000000000000000000014261331777607200145670ustar00rootroot00000000000000#!/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-61.2/builtin.h000066400000000000000000000043071331777607200144060ustar00rootroot00000000000000/* * 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_BUILTIN_H_ #define _NDCTL_BUILTIN_H_ extern const char ndctl_usage_string[]; extern const char ndctl_more_info_string[]; struct cmd_struct { const char *cmd; int (*fn)(int, const char **, void *ctx); }; int cmd_create_nfit(int argc, const char **argv, void *ctx); int cmd_enable_namespace(int argc, const char **argv, void *ctx); int cmd_create_namespace(int argc, const char **argv, void *ctx); int cmd_destroy_namespace(int argc, const char **argv, void *ctx); int cmd_disable_namespace(int argc, const char **argv, void *ctx); int cmd_check_namespace(int argc, const char **argv, void *ctx); int cmd_enable_region(int argc, const char **argv, void *ctx); int cmd_disable_region(int argc, const char **argv, void *ctx); int cmd_enable_dimm(int argc, const char **argv, void *ctx); int cmd_disable_dimm(int argc, const char **argv, void *ctx); int cmd_zero_labels(int argc, const char **argv, void *ctx); int cmd_read_labels(int argc, const char **argv, void *ctx); int cmd_write_labels(int argc, const char **argv, void *ctx); int cmd_init_labels(int argc, const char **argv, void *ctx); int cmd_check_labels(int argc, const char **argv, void *ctx); int cmd_inject_error(int argc, const char **argv, void *ctx); int cmd_wait_scrub(int argc, const char **argv, void *ctx); int cmd_start_scrub(int argc, const char **argv, void *ctx); int cmd_list(int argc, const char **argv, void *ctx); #ifdef ENABLE_TEST int cmd_test(int argc, const char **argv, void *ctx); #endif #ifdef ENABLE_DESTRUCTIVE int cmd_bat(int argc, const char **argv, void *ctx); #endif int cmd_update_firmware(int argc, const char **argv, void *ctx); int cmd_inject_smart(int argc, const char **argv, void *ctx); #endif /* _NDCTL_BUILTIN_H_ */ ndctl-61.2/ccan/000077500000000000000000000000001331777607200134675ustar00rootroot00000000000000ndctl-61.2/ccan/array_size/000077500000000000000000000000001331777607200156375ustar00rootroot00000000000000ndctl-61.2/ccan/array_size/LICENSE000077700000000000000000000000001331777607200213622../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/array_size/array_size.h000066400000000000000000000015711331777607200201640ustar00rootroot00000000000000/* 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-61.2/ccan/build_assert/000077500000000000000000000000001331777607200161475ustar00rootroot00000000000000ndctl-61.2/ccan/build_assert/LICENSE000077700000000000000000000000001331777607200216722../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/build_assert/build_assert.h000066400000000000000000000023141331777607200210000ustar00rootroot00000000000000/* 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-61.2/ccan/check_type/000077500000000000000000000000001331777607200156055ustar00rootroot00000000000000ndctl-61.2/ccan/check_type/LICENSE000077700000000000000000000000001331777607200213302../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/check_type/check_type.h000066400000000000000000000045051331777607200201000ustar00rootroot00000000000000/* 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-61.2/ccan/container_of/000077500000000000000000000000001331777607200161355ustar00rootroot00000000000000ndctl-61.2/ccan/container_of/LICENSE000077700000000000000000000000001331777607200216602../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/container_of/container_of.h000066400000000000000000000061341331777607200207600ustar00rootroot00000000000000/* 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-61.2/ccan/endian/000077500000000000000000000000001331777607200147255ustar00rootroot00000000000000ndctl-61.2/ccan/endian/LICENSE000077700000000000000000000000001331777607200204502../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/endian/endian.h000066400000000000000000000220031331777607200163310ustar00rootroot00000000000000/* 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-61.2/ccan/list/000077500000000000000000000000001331777607200144425ustar00rootroot00000000000000ndctl-61.2/ccan/list/LICENSE000077700000000000000000000000001331777607200206572../../licenses/BSD-MITustar00rootroot00000000000000ndctl-61.2/ccan/list/list.c000066400000000000000000000017501331777607200155640ustar00rootroot00000000000000/* 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-61.2/ccan/list/list.h000066400000000000000000000447171331777607200156030ustar00rootroot00000000000000/* 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-61.2/ccan/minmax/000077500000000000000000000000001331777607200147605ustar00rootroot00000000000000ndctl-61.2/ccan/minmax/LICENSE000077700000000000000000000000001331777607200205032../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/minmax/minmax.h000066400000000000000000000023721331777607200164260ustar00rootroot00000000000000/* 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-61.2/ccan/short_types/000077500000000000000000000000001331777607200160525ustar00rootroot00000000000000ndctl-61.2/ccan/short_types/LICENSE000077700000000000000000000000001331777607200215752../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/short_types/short_types.h000066400000000000000000000014311331777607200206050ustar00rootroot00000000000000/* 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-61.2/ccan/str/000077500000000000000000000000001331777607200142775ustar00rootroot00000000000000ndctl-61.2/ccan/str/LICENSE000077700000000000000000000000001331777607200200222../../licenses/CC0ustar00rootroot00000000000000ndctl-61.2/ccan/str/debug.c000066400000000000000000000031021331777607200155250ustar00rootroot00000000000000/* 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-61.2/ccan/str/str.c000066400000000000000000000004331331777607200152530ustar00rootroot00000000000000/* 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-61.2/ccan/str/str.h000066400000000000000000000135031331777607200152620ustar00rootroot00000000000000/* 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-61.2/ccan/str/str_debug.h000066400000000000000000000014061331777607200164270ustar00rootroot00000000000000/* 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-61.2/configure.ac000066400000000000000000000131051331777607200150510ustar00rootroot00000000000000AC_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=no) 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_SYNC], [enable_map_sync=yes], [], [[#include ]]) AS_IF([test "x$enable_bus_mc_err" = "xyes" -a "x$enable_map_sync" = "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"]) PKG_CHECK_MODULES([KMOD], [libkmod]) PKG_CHECK_MODULES([UDEV], [libudev]) PKG_CHECK_MODULES([UUID], [uuid]) PKG_CHECK_MODULES([JSON], [json-c]) AC_ARG_WITH([bash-completion-dir], AS_HELP_STRING([--with-bash-completion-dir[=PATH]], [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]), [], [with_bash_completion_dir=yes]) if test "x$with_bash_completion_dir" = "xyes"; then PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"], [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"]) else BASH_COMPLETION_DIR="$with_bash_completion_dir" fi AC_SUBST([BASH_COMPLETION_DIR]) AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"]) 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\ ]) my_CFLAGS="\ -Wall \ -Wchar-subscripts \ -Wformat-security \ -Wmissing-declarations \ -Wmissing-prototypes \ -Wnested-externs \ -Wpointer-arith \ -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-61.2/contrib/000077500000000000000000000000001331777607200142235ustar00rootroot00000000000000ndctl-61.2/contrib/do_abidiff000077500000000000000000000040701331777607200162200ustar00rootroot00000000000000#!/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)" git archive --format=tar --prefix="ndctl-${version}/" HEAD | gzip > ndctl-${version}.tar.gz fedpkg --release master --module-name ndctl mockbuild 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)" 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-61.2/contrib/ndctl000077500000000000000000000230761331777607200152650ustar00rootroot00000000000000# ndctl bash and zsh completion # Taken from perf's completion script. __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 # __ndctl_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" ] || __ndctl_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 } __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) ]]; 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" } __ndctl_file_comp() { local cur="$1" _filedir } __ndctl_comp_options() { local cur=$1 local opts if [[ "$cur" == *=* ]]; then local cur_subopt=${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##*=}" 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) ;; *) return ;; esac __ndctlcomp "$opts" "${cur##*=}" 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" ;; 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" ;; *) return ;; esac __ndctlcomp "$opts" "$cur" } __ndctl_add_special_opts() { local subcmd=$1 case $subcmd in create-namespace) opts="$opts --no-autolabel" ;; esac } __ndctl_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_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 __ndctl_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 __ndctl_get_comp_words_by_ref -n =: cur words cword prev fi __ndctl_main } && complete -o nospace -F _ndctl ndctl 2>/dev/null ndctl-61.2/contrib/prepare-release.sh000077500000000000000000000131501331777607200176360ustar00rootroot00000000000000#!/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}" local symfile="${libdir}/lib/${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-61.2/daxctl/000077500000000000000000000000001331777607200140425ustar00rootroot00000000000000ndctl-61.2/daxctl/Makefile.am000066400000000000000000000003131331777607200160730ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in bin_PROGRAMS = daxctl daxctl_SOURCES =\ daxctl.c \ list.c \ ../util/json.c daxctl_LDADD =\ lib/libdaxctl.la \ ../libutil.a \ $(UUID_LIBS) \ $(JSON_LIBS) ndctl-61.2/daxctl/daxctl.c000066400000000000000000000053101331777607200154640ustar00rootroot00000000000000/* * 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, void *ctx) { printf("%s\n", VERSION); return 0; } static int cmd_help(int argc, const char **argv, void *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"); } int cmd_list(int argc, const char **argv, void *ctx); static struct cmd_struct commands[] = { { "version", cmd_version }, { "list", cmd_list }, { "help", cmd_help }, }; 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)); daxctl_unref(ctx); fprintf(stderr, "Unknown command: '%s'\n", argv[0]); out: return 1; } ndctl-61.2/daxctl/lib/000077500000000000000000000000001331777607200146105ustar00rootroot00000000000000ndctl-61.2/daxctl/lib/Makefile.am000066400000000000000000000013161331777607200166450ustar00rootroot00000000000000include $(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/sysfs.c \ ../../util/sysfs.h \ ../../util/log.c \ ../../util/log.h \ libdaxctl.c libdaxctl_la_LIBADD =\ $(UUID_LIBS) 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-61.2/daxctl/lib/libdaxctl-private.h000066400000000000000000000023101331777607200203730ustar00rootroot00000000000000/* * 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_ #define DAXCTL_EXPORT __attribute__ ((visibility("default"))) /** * 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 size; struct daxctl_region *region; }; #endif /* _LIBDAXCTL_PRIVATE_H_ */ ndctl-61.2/daxctl/lib/libdaxctl.c000066400000000000000000000330331331777607200167240ustar00rootroot00000000000000/* * 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 "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; }; /** * 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 daxctl_ctx *c; c = calloc(1, sizeof(struct daxctl_ctx)); if (!c) return -ENOMEM; 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); return 0; } /** * 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); 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_dev(struct daxctl_dev *dev, struct list_head *head) { if (head) list_del_from(head, &dev->list); free(dev->dev_buf); free(dev->dev_path); 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 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/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; 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]; char *region_path; if (region->devices_init) return; region->devices_init = 1; sprintf(daxdev_fmt, "dax%d.", region->id); if (asprintf(®ion_path, "%s/dax", region->region_path) < 0) { dbg(ctx, "region path alloc fail\n"); return; } sysfs_device_parse(ctx, region_path, daxdev_fmt, region, add_dax_dev); free(region_path); } static char *dax_region_path(const char *base, const char *device) { char *path, *region_path, *c; if (asprintf(&path, "%s/%s", base, 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', trim back to regionX */ c = strrchr(region_path, '/'); if (!c) { free(region_path); return NULL; } *c = '\0'; 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) { const char *base = "/sys/class/dax"; struct dirent *de; DIR *dir; if (ctx->regions_init) return; ctx->regions_init = 1; dir = opendir(base); if (!dir) { dbg(ctx, "no dax regions found\n"); 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(base, de->d_name); 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); } 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_size(struct daxctl_dev *dev) { return dev->size; } ndctl-61.2/daxctl/lib/libdaxctl.pc.in000066400000000000000000000003401331777607200175040ustar00rootroot00000000000000prefix=@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-61.2/daxctl/lib/libdaxctl.sym000066400000000000000000000016151331777607200173130ustar00rootroot00000000000000LIBDAXCTL_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; ndctl-61.2/daxctl/libdaxctl.h000066400000000000000000000062541331777607200161700ustar00rootroot00000000000000/* * 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_LIBUUID #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_size(struct daxctl_dev *dev); #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-61.2/daxctl/list.c000066400000000000000000000064131331777607200151650ustar00rootroot00000000000000/* * 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; static int jflag = JSON_C_TO_STRING_PRETTY; #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, void *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; 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; 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, listopts_to_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, listopts_to_flags()); } if (jregions) util_display_json_array(stdout, jregions, jflag); else if (jdevs) util_display_json_array(stdout, jdevs, jflag); if (did_fail) return -ENOMEM; return 0; } ndctl-61.2/git-version000077500000000000000000000016511331777607200147620ustar00rootroot00000000000000#!/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=61.2 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-61.2/git-version-gen000077500000000000000000000004421331777607200155260ustar00rootroot00000000000000#!/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-61.2/licenses/000077500000000000000000000000001331777607200143705ustar00rootroot00000000000000ndctl-61.2/licenses/BSD-MIT000066400000000000000000000017771331777607200153660ustar00rootroot00000000000000Permission 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-61.2/licenses/CC0000066400000000000000000000143571331777607200146720ustar00rootroot00000000000000Statement 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-61.2/m4/000077500000000000000000000000001331777607200131035ustar00rootroot00000000000000ndctl-61.2/m4/.gitignore000066400000000000000000000001001331777607200150620ustar00rootroot00000000000000libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 ndctl-61.2/make-git-snapshot.sh000077500000000000000000000011501331777607200164520ustar00rootroot00000000000000#!/bin/bash set -e NAME=ndctl REFDIR="$HOME/git/ndctl" # for faster cloning, if available UPSTREAM=$REFDIR #TODO update once we have a public upstream OUTDIR=$HOME/rpmbuild/SOURCES [ -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-61.2/ndctl.spec.in000066400000000000000000000101621331777607200151500ustar00rootroot00000000000000Name: ndctl Version: VERSION Release: 1%{?dist} Summary: Manage "libnvdimm" subsystem devices (Non-volatile Memory) License: GPLv2 Group: System Environment/Base 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: asciidoc 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) %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 Group: Development/Libraries 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 Group: System Environment/Base 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 Group: Development/Libraries 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 Group: System Environment/Libraries 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 Group: System Environment/Libraries %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 %post -n LNAME -p /sbin/ldconfig %postun -n LNAME -p /sbin/ldconfig %post -n DAX_LNAME -p /sbin/ldconfig %postun -n DAX_LNAME -p /sbin/ldconfig %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}/ %files -n daxctl %defattr(-,root,root) %license util/COPYING licenses/BSD-MIT licenses/CC0 %{_bindir}/daxctl %{_mandir}/man1/daxctl* %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-61.2/ndctl/000077500000000000000000000000001331777607200136675ustar00rootroot00000000000000ndctl-61.2/ndctl/Makefile.am000066400000000000000000000013721331777607200157260ustar00rootroot00000000000000include $(top_srcdir)/Makefile.am.in bin_PROGRAMS = ndctl ndctl_SOURCES = ndctl.c \ bus.c \ create-nfit.c \ namespace.c \ check.c \ region.c \ dimm.c \ ../util/log.c \ list.c \ test.c \ ../util/json.c \ util/json-smart.c \ util/json-firmware.c \ inject-error.c \ inject-smart.c 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_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 endif ndctl-61.2/ndctl/action.h000066400000000000000000000005141331777607200153150ustar00rootroot00000000000000/* * 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, }; #endif /* __NDCTL_ACTION_H__ */ ndctl-61.2/ndctl/bat.c000066400000000000000000000032611331777607200146030ustar00rootroot00000000000000/* * 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, void *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-61.2/ndctl/bus.c000066400000000000000000000054571331777607200146370ustar00rootroot00000000000000/* 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; } param; static const struct option bus_options[] = { OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug"), OPT_END(), }; static int scrub_action(struct ndctl_bus *bus, enum device_action action) { switch (action) { case ACTION_WAIT: return ndctl_bus_wait_for_scrub_completion(bus); 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, JSON_C_TO_STRING_PRETTY); else json_object_put(jbuses); if (success) return success; return fail ? fail : -ENXIO; } int cmd_start_scrub(int argc, const char **argv, void *ctx) { char *usage = "ndctl start-scrub [ ... ] []"; int start = bus_action(argc, argv, usage, bus_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, void *ctx) { char *usage = "ndctl wait-scrub [ ... ] []"; int wait = bus_action(argc, argv, usage, bus_options, ACTION_WAIT, ctx); if (wait <= 0) { fprintf(stderr, "error waiting for scrub completion: %s\n", strerror(-wait)); return wait; } else { return 0; } } ndctl-61.2/ndctl/check.c000066400000000000000000001013721331777607200151140ustar00rootroot00000000000000/* * 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; } static int btt_create_mappings(struct btt_chk *bttc) { struct arena_info *a; int mmap_flags; int i; if (!bttc->opts->repair) mmap_flags = PROT_READ; else mmap_flags = PROT_READ|PROT_WRITE; for (i = 0; i < bttc->num_arenas; i++) { a = &bttc->arena[i]; a->map.info_len = BTT_INFO_SIZE; a->map.info = mmap(NULL, a->map.info_len, mmap_flags, MAP_SHARED, bttc->fd, a->infooff); if (a->map.info == MAP_FAILED) { 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 = mmap(NULL, a->map.data_len, mmap_flags, MAP_SHARED, bttc->fd, a->dataoff); if (a->map.data == MAP_FAILED) { 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 = mmap(NULL, a->map.map_len, mmap_flags, MAP_SHARED, bttc->fd, a->mapoff); if (a->map.map == MAP_FAILED) { 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 = mmap(NULL, a->map.log_len, mmap_flags, MAP_SHARED, bttc->fd, a->logoff); if (a->map.log == MAP_FAILED) { 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 = mmap(NULL, a->map.info2_len, mmap_flags, MAP_SHARED, bttc->fd, a->info2off); if (a->map.info2 == MAP_FAILED) { 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) munmap(a->map.info, a->map.info_len); if (a->map.data) munmap(a->map.data, a->map.data_len); if (a->map.map) munmap(a->map.map, a->map.map_len); if (a->map.log) munmap(a->map.log, a->map.log_len); if (a->map.info2) munmap(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"); 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-61.2/ndctl/create-nfit.c000066400000000000000000000117631331777607200162440ustar00rootroot00000000000000/* * 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, void *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-61.2/ndctl/dimm.c000066400000000000000000000647241331777607200147760ustar00rootroot00000000000000/* * 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 struct action_context { struct json_object *jdimms; enum ndctl_namespace_version labelversion; FILE *f_out; FILE *f_in; struct update_context update; }; 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_labels(dimm); } static struct json_object *dump_label_json(struct ndctl_dimm *dimm, struct ndctl_cmd *cmd_read, ssize_t size) { 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 = json_object_new_int64(le64_to_cpu(nslabel.isetcookie)); 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 = json_object_new_int64(le64_to_cpu(nslabel.dpa)); if (!jobj) break; json_object_object_add(jlabel, "dpa", jobj); jobj = json_object_new_int64(le64_to_cpu(nslabel.rawsize)); 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) { struct json_object *jdimm = json_object_new_object(); struct json_object *jlabel, *jobj, *jindex; if (!jdimm) return NULL; jindex = dump_index_json(cmd_read, size); if (!jindex) goto err_jindex; jlabel = dump_label_json(dimm, cmd_read, size); if (!jlabel) goto err_jlabel; jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); if (!jobj) goto err_jobj; json_object_object_add(jdimm, "dev", jobj); json_object_object_add(jdimm, "index", jindex); json_object_object_add(jdimm, "label", jlabel); return jdimm; err_jobj: json_object_put(jlabel); err_jlabel: json_object_put(jindex); err_jindex: json_object_put(jdimm); return NULL; } static int rw_bin(FILE *f, struct ndctl_cmd *cmd, ssize_t size, int rw) { char buf[4096]; ssize_t offset, write = 0; for (offset = 0; offset < size; offset += sizeof(buf)) { ssize_t len = min_t(ssize_t, sizeof(buf), size - offset), rc; if (rw) { 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_labels(dimm); if (!cmd_read) return -ENXIO; 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, 1); /* * 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; cmd_read = ndctl_dimm_read_labels(dimm); if (!cmd_read) return -ENXIO; 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, 0); 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) return rc; 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); return -ENXIO; } fw->store_size = ndctl_cmd_fw_info_get_storage_size(cmd); if (fw->store_size == UINT_MAX) return -ENXIO; fw->update_size = ndctl_cmd_fw_info_get_max_send_len(cmd); if (fw->update_size == UINT_MAX) return -ENXIO; fw->query_interval = ndctl_cmd_fw_info_get_query_interval(cmd); if (fw->query_interval == UINT_MAX) return -ENXIO; fw->max_query = ndctl_cmd_fw_info_get_max_query_time(cmd); if (fw->max_query == UINT_MAX) return -ENXIO; fw->run_version = ndctl_cmd_fw_info_get_run_version(cmd); if (fw->run_version == ULLONG_MAX) return -ENXIO; rc = verify_fw_size(uctx); 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; bool done = false; 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"); done = true; rc = 0; 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: done = true; rc = -ENXIO; goto out; case FW_ENORES: fprintf(stderr, "Firmware update sequence timed out: %s\n", ndctl_dimm_get_devname(dimm)); rc = -ETIMEDOUT; done = true; goto out; default: fprintf(stderr, "Unknown update status: %#x on DIMM %s\n", status, ndctl_dimm_get_devname(dimm)); rc = -EINVAL; done = true; goto out; } } while (!done); 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 struct parameters { const char *bus; const char *outfile; const char *infile; const char *labelversion; bool force; bool json; bool verbose; } param = { .labelversion = "1.1", }; 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_labels(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; } 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") #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)") static const struct option read_options[] = { BASE_OPTIONS(), READ_OPTIONS(), OPT_END(), }; static const struct option write_options[] = { BASE_OPTIONS(), WRITE_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 int dimm_action(int argc, const char **argv, void *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; 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; } if (param.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, JSON_C_TO_STRING_PRETTY); 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, void *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, void *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, void *ctx) { int count = dimm_action(argc, argv, ctx, action_zero, base_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, void *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, void *ctx) { int count = dimm_action(argc, argv, ctx, action_check, base_options, "ndctl check-labels [..] []"); fprintf(stderr, "successfully verified %d nmem%s\n", count >= 0 ? count : 0, count > 1 ? "s" : ""); return count >= 0 ? 0 : EXIT_FAILURE; } int cmd_disable_dimm(int argc, const char **argv, void *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, void *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, void *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; } ndctl-61.2/ndctl/firmware-update.h000066400000000000000000000022551331777607200171400ustar00rootroot00000000000000/* 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-61.2/ndctl/inject-error.c000066400000000000000000000222351331777607200164420ustar00rootroot00000000000000/* * 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 "private.h" #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, void *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-61.2/ndctl/inject-smart.c000066400000000000000000000257041331777607200164430ustar00rootroot00000000000000/* 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 "private.h" #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; } param; static struct smart_ctx { bool alarms_present; 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('x', "media-temperature-alarm", ¶m.media_temperature_alarm, \ "smart media temperature alarm", \ "enable or disable the smart media temperature alarm"), \ 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('y', "ctrl-temperature-alarm", ¶m.ctrl_temperature_alarm, \ "smart controller temperature alarm", \ "enable or disable the smart controller temperature alarm"), \ 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('z', "spares-alarm", ¶m.spares_alarm, \ "smart spares alarm", \ "enable or disable the smart spares alarm"), \ OPT_BOOLEAN('f', "fatal", ¶m.fatal, "inject fatal smart health status"), \ OPT_BOOLEAN('U', "unsafe-shutdown", ¶m.unsafe_shutdown, \ "inject smart unsafe shutdown status") 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; \ } \ } static int smart_init(void) { if (param.human) sctx.flags |= UTIL_JSON_HUMAN; /* 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(); 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(st_cmd); if (rc) { 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(sst_cmd); if (rc) 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) { \ si_cmd = ndctl_dimm_cmd_new_smart_inject(dimm); \ if (!si_cmd) { \ error("%s: no smart inject command support\n", name); \ goto out; \ } \ rc = ndctl_cmd_smart_inject_##arg(si_cmd, true, sctx.arg); \ if (rc) { \ error("%s: smart inject %s cmd invalid: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ goto out; \ } \ rc = ndctl_cmd_submit(si_cmd); \ if (rc) { \ error("%s: smart inject %s command failed: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ goto out; \ } \ ndctl_cmd_unref(si_cmd); \ } \ } #define send_inject_bool(arg) \ { \ if (param.arg) { \ si_cmd = ndctl_dimm_cmd_new_smart_inject(dimm); \ if (!si_cmd) { \ error("%s: no smart inject command support\n", name); \ goto out; \ } \ rc = ndctl_cmd_smart_inject_##arg(si_cmd, true); \ if (rc) { \ error("%s: smart inject %s cmd invalid: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ goto out; \ } \ rc = ndctl_cmd_submit(si_cmd); \ if (rc) { \ error("%s: smart inject %s command failed: %s (%d)\n", \ name, #arg, strerror(abs(rc)), rc); \ 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(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; 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, JSON_C_TO_STRING_PRETTY); } } 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, void *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-61.2/ndctl/lib/000077500000000000000000000000001331777607200144355ustar00rootroot00000000000000ndctl-61.2/ndctl/lib/.gitignore000066400000000000000000000000701331777607200164220ustar00rootroot00000000000000.dirstamp .deps/ .libs/ *.la *.lo libabc.pc test-libabc ndctl-61.2/ndctl/lib/Makefile.am000066400000000000000000000015341331777607200164740ustar00rootroot00000000000000include $(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 \ 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-61.2/ndctl/lib/ars.c000066400000000000000000000211311331777607200153640ustar00rootroot00000000000000/* * 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; cmd = calloc(1, size); 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-61.2/ndctl/lib/dimm.c000066400000000000000000000355521331777607200155410ustar00rootroot00000000000000/* * 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 "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 int nvdimm_num_label_slots(struct nvdimm_data *ndd) { return ndd->config_size / (sizeof_namespace_label(ndd) + 1); } 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); /* * The minimum index space is 512 bytes, with that amount of * index we can describe ~1400 labels which is less than a byte * of overhead per label. Round up to a byte of overhead per * label and determine the size of the index region. Yes, this * starts to waste space at larger config_sizes, but it's * unlikely we'll ever see anything but 128K. */ nslot = nvdimm_num_label_slots(ndd); space = ndd->config_size - nslot * sizeof_namespace_label(ndd); size = ALIGN(sizeof(struct namespace_index) + DIV_ROUND_UP(nslot, 8), NSINDEX_ALIGN) * 2; if (size <= space) 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(cmd_write); if (rc || ndctl_cmd_get_firmware_status(cmd_write)) 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) { 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_read->iter.total_xfer; 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_labels(struct ndctl_dimm *dimm) { 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(cmd_size); if (rc || ndctl_cmd_get_firmware_status(cmd_size)) goto out_size; cmd_read = ndctl_dimm_cmd_new_cfg_read(cmd_size); if (!cmd_read) goto out_size; rc = ndctl_cmd_submit(cmd_read); if (rc || ndctl_cmd_get_firmware_status(cmd_read)) goto out_read; ndctl_cmd_unref(cmd_size); init_ndd(&dimm->ndd, cmd_read); return cmd_read; out_read: ndctl_cmd_unref(cmd_read); out_size: ndctl_cmd_unref(cmd_size); return NULL; } NDCTL_EXPORT int ndctl_dimm_zero_labels(struct ndctl_dimm *dimm) { struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm); struct ndctl_cmd *cmd_read, *cmd_write; int rc; cmd_read = ndctl_dimm_read_labels(dimm); if (!cmd_read) return -ENXIO; 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(cmd_write); if (rc || ndctl_cmd_get_firmware_status(cmd_write)) 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 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 len = dimm->buf_len; char buf[20]; if (snprintf(path, len, "%s/available_slots", dimm->dimm_path) >= len) { err(ctx, "%s: buffer too small!\n", ndctl_dimm_get_devname(dimm)); return ULONG_MAX; } if (sysfs_read_attr(ctx, path, buf) < 0) return ULONG_MAX; return strtoul(buf, NULL, 0); } ndctl-61.2/ndctl/lib/firmware.c000066400000000000000000000056551331777607200164300ustar00rootroot00000000000000/* 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-61.2/ndctl/lib/hpe1.c000066400000000000000000000212021331777607200154330ustar00rootroot00000000000000/* * 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; if (hpe1_smart_valid(cmd) < 0) 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; if (hpe1_smart_valid(cmd) < 0) 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) { if (hpe1_smart_valid(cmd) < 0) return UINT_MAX; return CMD_HPE1_SMART(cmd)->curr_temp; } static unsigned int hpe1_cmd_smart_get_spares(struct ndctl_cmd *cmd) { if (hpe1_smart_valid(cmd) < 0) 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; if (hpe1_smart_valid(cmd) < 0) 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) { if (hpe1_smart_valid(cmd) < 0) 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; if (hpe1_smart_valid(cmd) < 0) 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) { if (hpe1_smart_valid(cmd) < 0) 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) { if (hpe1_smart_valid(cmd) < 0) 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; if (hpe1_smart_threshold_valid(cmd) < 0) 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) { if (hpe1_smart_threshold_valid(cmd) < 0) return UINT_MAX; return CMD_HPE1_SMART_THRESH(cmd)->temp_threshold; } static unsigned int hpe1_cmd_smart_threshold_get_spares(struct ndctl_cmd *cmd) { if (hpe1_smart_threshold_valid(cmd) < 0) 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-61.2/ndctl/lib/hpe1.h000066400000000000000000000222301331777607200154420ustar00rootroot00000000000000/* * 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-61.2/ndctl/lib/inject.c000066400000000000000000000314651331777607200160660ustar00rootroot00000000000000/* * 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 void 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); if (!ns_offset || !ns_size) return; if (pfn) { *ns_offset = ndctl_pfn_get_resource(pfn); *ns_size = ndctl_pfn_get_size(pfn); return; } else if (dax) { *ns_offset = ndctl_dax_get_resource(dax); *ns_size = ndctl_dax_get_size(dax); return; } /* raw or btt */ *ns_offset = ndctl_namespace_get_resource(ndns); *ns_size = ndctl_namespace_get_size(ndns); } 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; ndctl_namespace_get_injection_bounds(ndns, &ns_offset, &ns_size); *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; ndctl_namespace_get_injection_bounds(ndns, &ns_offset, &ns_size); cmd = ndctl_bus_cmd_new_ars_cap(bus, ns_offset, ns_size); rc = ndctl_cmd_submit(cmd); if (rc) { dbg(ctx, "Error submitting ars_cap: %d\n", rc); return rc; } 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"); return -EINVAL; } ndctl_cmd_unref(cmd); return clear_unit; } 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) { 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) { 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)) { ndctl_namespace_get_injection_bounds(ndns, &ns_offset, &ns_size); cmd = ndctl_bus_cmd_new_ars_cap(bus, ns_offset, ns_size); rc = ndctl_cmd_submit(cmd); if (rc) { 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) { 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; return ULLONG_MAX; } NDCTL_EXPORT unsigned long long ndctl_bb_get_count(struct ndctl_bb *bb) { if (bb) return bb->count; return ULLONG_MAX; } ndctl-61.2/ndctl/lib/intel.c000066400000000000000000000516351331777607200157260ustar00rootroot00000000000000/* * 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 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) \ { \ if (intel_smart_valid(cmd) < 0) \ 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) \ { \ if (intel_smart_threshold_valid(cmd) < 0) \ 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 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) \ { \ if (intel_fw_get_info_valid(cmd) < 0) \ 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) \ { \ if (intel_fw_get_info_valid(cmd) < 0) \ 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) { if (intel_fw_get_info_valid(cmd) < 0) 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) { if (intel_fw_start_valid(cmd) < 0) 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) { if (intel_fw_fquery_valid(cmd) < 0) 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, .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, }; ndctl-61.2/ndctl/lib/intel.h000066400000000000000000000116511331777607200157250ustar00rootroot00000000000000/* 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 #endif /* __INTEL_H__ */ ndctl-61.2/ndctl/lib/libndctl.c000066400000000000000000003634521331777607200164110ustar00rootroot00000000000000/* * 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 "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; FILE *badblocks; struct badblock bb; 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_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 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); 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(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); if (region->badblocks) fclose(region->badblocks); 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) { 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)); return ULLONG_MAX; } rc = sysfs_read_attr(ctx, path, buf); if (rc < 0) 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; if (__ndctl_bus_get_scrub_state(bus, &scrub_count, &active)) return UINT_MAX; return scrub_count; } /** * ndctl_bus_wait_for_scrub - wait for a scrub to complete * @bus: bus for which to check whether a scrub is in progress * * Upon return this bus has completed any in-progress scrubs. 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 instead checks the kernel's view * of whether a scrub is in progress by looking at the 'scrub' file in * sysfs. */ NDCTL_EXPORT int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus) { struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); unsigned int scrub_count; char buf[SYSFS_ATTR_SIZE]; struct pollfd fds; char in_progress; int fd = 0, rc; fd = open(bus->scrub_path, O_RDONLY|O_CLOEXEC); 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 == '+') { /* scrub in progress, wait */ rc = poll(&fds, 1, -1); if (rc < 0) { rc = -errno; dbg(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; } } if (rc == 0) dbg(ctx, "bus%d: scrub complete\n", ndctl_bus_get_id(bus)); else dbg(ctx, "bus%d: error waiting for scrub completion: %s\n", ndctl_bus_get_id(bus), strerror(-rc)); if (fd) close (fd); return rc; } 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->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/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; 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 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) { 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_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 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)); return ULLONG_MAX; } if (sysfs_read_attr(ctx, path, buf) < 0) 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; } static int regions_badblocks_init(struct ndctl_region *region) { struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); char *bb_path; int rc = 0; /* if the file is already open */ if (region->badblocks) { fclose(region->badblocks); region->badblocks = NULL; } if (asprintf(&bb_path, "%s/badblocks", region->region_path) < 0) { rc = -errno; err(ctx, "region badblocks path allocation failure\n"); return rc; } region->badblocks = fopen(bb_path, "re"); if (!region->badblocks) { rc = -errno; free(bb_path); return rc; } free(bb_path); return rc; } NDCTL_EXPORT struct badblock *ndctl_region_get_next_badblock(struct ndctl_region *region) { int rc; char *buf = NULL; size_t rlen = 0; if (!region->badblocks) return NULL; rc = getline(&buf, &rlen, region->badblocks); if (rc == -1) { free(buf); return NULL; } rc = sscanf(buf, "%llu %u", ®ion->bb.offset, ®ion->bb.len); free(buf); if (rc != 2) { fclose(region->badblocks); region->badblocks = NULL; region->bb.offset = 0; region->bb.len = 0; return NULL; } return ®ion->bb; } NDCTL_EXPORT struct badblock *ndctl_region_get_first_badblock(struct ndctl_region *region) { int rc; rc = regions_badblocks_init(region); if (rc < 0) return NULL; return ndctl_region_get_next_badblock(region); } 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.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; } return cmd; } 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 = 0; 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.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; } NDCTL_EXPORT ssize_t ndctl_cmd_cfg_read_get_data(struct ndctl_cmd *cfg_read, void *buf, unsigned int len, unsigned int offset) { if (cfg_read->type != ND_CMD_GET_CONFIG_DATA || cfg_read->status > 0) return -EINVAL; if (cfg_read->status < 0) return cfg_read->status; if (offset > cfg_read->iter.total_xfer || len + offset < len) return -EINVAL; if (len + offset > cfg_read->iter.total_xfer) len = cfg_read->iter.total_xfer - offset; memcpy(buf, cfg_read->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 ssize_t ndctl_cmd_cfg_write_set_data(struct ndctl_cmd *cfg_write, void *buf, unsigned int len, unsigned int offset) { if (cfg_write->type != ND_CMD_SET_CONFIG_DATA || cfg_write->status < 1) return -EINVAL; if (cfg_write->status < 0) return cfg_write->status; if (offset > cfg_write->iter.total_xfer || len + offset < len) return -EINVAL; if (len + offset > cfg_write->iter.total_xfer) len = cfg_write->iter.total_xfer - offset; 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) { 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(cfg_write->iter.total_buf, 0, cfg_write->iter.total_xfer); return cfg_write->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); } 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_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); } 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) 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 (rc) 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) 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); 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_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_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-61.2/ndctl/lib/libndctl.pc.in000066400000000000000000000003751331777607200171660ustar00rootroot00000000000000prefix=@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-61.2/ndctl/lib/libndctl.sym000066400000000000000000000234261331777607200167710ustar00rootroot00000000000000LIBNDCTL_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; ndctl-61.2/ndctl/lib/msft.c000066400000000000000000000073541331777607200155630ustar00rootroot00000000000000/* * 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) { if (msft_smart_valid(cmd) < 0) 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; if (msft_smart_valid(cmd) < 0) 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) { if (msft_smart_valid(cmd) < 0) return UINT_MAX; return CMD_MSFT_SMART(cmd)->temp * 16; } static unsigned int msft_cmd_smart_get_life_used(struct ndctl_cmd *cmd) { if (msft_smart_valid(cmd) < 0) 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-61.2/ndctl/lib/msft.h000066400000000000000000000031721331777607200155620ustar00rootroot00000000000000/* * 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-61.2/ndctl/lib/nfit.c000066400000000000000000000144371331777607200155520ustar00rootroot00000000000000/* * 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) { 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-61.2/ndctl/lib/private.h000066400000000000000000000267501331777607200162720ustar00rootroot00000000000000/* * 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" 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; 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 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; 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. */ enum { READ, WRITE, }; 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 *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_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); 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_spares)(struct ndctl_cmd *, bool, unsigned int); int (*smart_inject_fatal)(struct ndctl_cmd *, bool); int (*smart_inject_unsafe_shutdown)(struct ndctl_cmd *, bool); 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 *); }; struct ndctl_dimm_ops * const intel_dimm_ops; struct ndctl_dimm_ops * const hpe1_dimm_ops; struct ndctl_dimm_ops * const msft_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-61.2/ndctl/lib/smart.c000066400000000000000000000114621331777607200157330ustar00rootroot00000000000000/* * 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_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-61.2/ndctl/libndctl-nfit.h000066400000000000000000000045721331777607200166010ustar00rootroot00000000000000/* * * 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-61.2/ndctl/libndctl.h000066400000000000000000001013361331777607200156370ustar00rootroot00000000000000/* * 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_LIBUUID #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 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); 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); 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_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_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); 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); struct ndctl_cmd *ndctl_dimm_read_labels(struct ndctl_dimm *dimm); 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); 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 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 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); unsigned long ndctl_pfn_get_align(struct ndctl_pfn *pfn); 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); unsigned long ndctl_dax_get_align(struct ndctl_dax *dax); 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); #ifdef __cplusplus } /* extern "C" */ #endif #endif ndctl-61.2/ndctl/list.c000066400000000000000000000271171331777607200150160ustar00rootroot00000000000000/* * 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; } 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; return flags; } 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 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; 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); 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) { 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); 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, jflag); 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, jflag)); json_object_put(jplatform); } else if (jdimms) util_display_json_array(stdout, jdimms, jflag); else if (jregions) util_display_json_array(stdout, jregions, jflag); else if (jnamespaces) util_display_json_array(stdout, jnamespaces, jflag); 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, void *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('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_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"; } 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-61.2/ndctl/namespace.c000066400000000000000000000760601331777607200160000ustar00rootroot00000000000000/* * 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 "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 struct parameters { bool do_scan; bool mode_default; bool align_default; bool autolabel; 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") #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") 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 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; } else if (!param.align) { param.align = "2M"; param.align_default = true; } 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; 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); } 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); } 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 = SZ_4K, units = 1, resource; 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 (param.align) { struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); p->align = parse_size64(param.align); if (p->mode == NDCTL_NS_MODE_MEMORY && p->align != SZ_2M && (!pfn || !ndctl_pfn_has_align(pfn))) { /* * Initial pfn device support in the kernel * supported a 2M default alignment when * ndctl_pfn_has_align() returns false. */ debug("%s not support 'align' for fsdax mode\n", region_name); return -EAGAIN; } else if (p->mode == NDCTL_NS_MODE_DAX && (!dax || !ndctl_dax_has_align(dax))) { /* * Unlike the pfn case, we require the kernel to * have 'align' support for device-dax. */ debug("%s not support 'align' for devdax mode\n", region_name); return -EAGAIN; } else if (!param.align_default && (p->mode == NDCTL_NS_MODE_SAFE || p->mode == NDCTL_NS_MODE_RAW)) { /* * Specifying an alignment has no effect for * raw, or btt mode namespaces. */ error("%s mode does not support setting an alignment\n", p->mode == NDCTL_NS_MODE_SAFE ? "sector" : "raw"); return -ENXIO; } /* * Fallback to a 4K default alignment if the region is * not 2MB (typical default) aligned. This mainly helps * the nfit_test use case where it is backed by vmalloc * memory. */ resource = ndctl_region_get_resource(region); if (param.align_default && resource < ULLONG_MAX && (resource & (SZ_2M - 1))) { debug("%s: falling back to a 4K alignment\n", region_name); p->align = SZ_4K; } switch (p->align) { case SZ_4K: case SZ_2M: case SZ_1G: break; default: error("unsupported align: %s\n", param.align); return -ENXIO; } /* * 'raw' and 'sector' mode namespaces don't support an * alignment attribute. */ if (p->mode == NDCTL_NS_MODE_MEMORY || p->mode == NDCTL_NS_MODE_DAX) 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, param.align ? p->align : SZ_4K, 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; /* check if we need, and whether the kernel supports, pfn devices */ if (do_setup_pfn(ndns, p)) { struct ndctl_pfn *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; } else if (!pfn) { error("operation failed, %s fsdax mode not available\n", region_name); return -EINVAL; } } /* check if we need, and whether the kernel supports, dax devices */ if (p->mode == NDCTL_NS_MODE_DAX) { struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); if (!dax) { error("operation failed, %s devdax mode not available\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; } 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 -ENODEV; } return setup_namespace(region, ndns, &p); } static int zero_info_block(struct ndctl_namespace *ndns) { const char *devname = ndctl_namespace_get_devname(ndns); int fd, rc = -ENXIO; void *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 = 0; goto out; } if (posix_memalign(&buf, 4096, 4096) != 0) return -ENXIO; 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, 4096); rc = pwrite(fd, buf, 4096, 4096); if (rc < 4096) { debug("%s: failed to zero info block %s\n", devname, path); rc = -ENXIO; } else rc = 0; close(fd); out: ndctl_namespace_set_raw_mode(ndns, 0); ndctl_namespace_disable_invalidate(ndns); 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); 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) return rc; } rc = ndctl_namespace_delete(ndns); if (rc) debug("%s: failed to reclaim\n", devname); return 0; } 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_labels(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) 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 do_xaction_namespace(const char *namespace, enum device_action action, struct ndctl_ctx *ctx) { struct ndctl_namespace *ndns, *_n; int rc = -ENXIO, success = 0; struct ndctl_region *region; const char *ndns_name; struct ndctl_bus *bus; if (!namespace && action != ACTION_CREATE) 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; 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) rc = 1; 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); break; case ACTION_ENABLE: rc = ndctl_namespace_enable(ndns); break; case ACTION_DESTROY: rc = namespace_destroy(region, ndns); break; case ACTION_CHECK: rc = namespace_check(ndns, verbose, force, repair, logfix); if (rc < 0) return rc; break; case ACTION_CREATE: rc = namespace_reconfig(region, ndns); if (rc < 0) return rc; return 1; default: rc = -EINVAL; break; } if (rc >= 0) success++; } } } if (success) return success; return rc; } int cmd_disable_namespace(int argc, const char **argv, void *ctx) { char *xable_usage = "ndctl disable-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_DISABLE, base_options, xable_usage); int disabled = do_xaction_namespace(namespace, ACTION_DISABLE, ctx); if (disabled < 0) { fprintf(stderr, "error disabling namespaces: %s\n", strerror(-disabled)); return disabled; } else if (disabled == 0) { fprintf(stderr, "disabled 0 namespaces\n"); return -ENXIO; } else { fprintf(stderr, "disabled %d namespace%s\n", disabled, disabled > 1 ? "s" : ""); return 0; } } int cmd_enable_namespace(int argc, const char **argv, void *ctx) { char *xable_usage = "ndctl enable-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_ENABLE, base_options, xable_usage); int enabled = do_xaction_namespace(namespace, ACTION_ENABLE, ctx); if (enabled < 0) { fprintf(stderr, "error enabling namespaces: %s\n", strerror(-enabled)); return enabled; } else if (enabled == 0) { fprintf(stderr, "enabled 0 namespaces\n"); return 0; } else { fprintf(stderr, "enabled %d namespace%s\n", enabled, enabled > 1 ? "s" : ""); return 0; } } int cmd_create_namespace(int argc, const char **argv, void *ctx) { char *xable_usage = "ndctl create-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_CREATE, create_options, xable_usage); int created = do_xaction_namespace(namespace, ACTION_CREATE, ctx); if (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); created = do_xaction_namespace(NULL, ACTION_CREATE, ctx); } if (created < 0 || (!namespace && created < 1)) { fprintf(stderr, "failed to %s namespace: %s\n", namespace ? "reconfigure" : "create", strerror(-created)); if (!namespace) created = -ENODEV; } if (created < 0) return created; return 0; } int cmd_destroy_namespace(int argc , const char **argv, void *ctx) { char *xable_usage = "ndctl destroy-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_DESTROY, destroy_options, xable_usage); int destroyed = do_xaction_namespace(namespace, ACTION_DESTROY, ctx); if (destroyed < 0) { fprintf(stderr, "error destroying namespaces: %s\n", strerror(-destroyed)); return destroyed; } else if (destroyed == 0) { fprintf(stderr, "destroyed 0 namespaces\n"); return 0; } else { fprintf(stderr, "destroyed %d namespace%s\n", destroyed, destroyed > 1 ? "s" : ""); return 0; } } int cmd_check_namespace(int argc , const char **argv, void *ctx) { char *xable_usage = "ndctl check-namespace []"; const char *namespace = parse_namespace_options(argc, argv, ACTION_CHECK, check_options, xable_usage); int checked; checked = do_xaction_namespace(namespace, ACTION_CHECK, ctx); if (checked < 0) { fprintf(stderr, "error checking namespaces: %s\n", strerror(-checked)); return checked; } else if (checked == 0) { fprintf(stderr, "checked 0 namespaces\n"); return 0; } else { fprintf(stderr, "checked %d namespace%s\n", checked, checked > 1 ? "s" : ""); return 0; } } ndctl-61.2/ndctl/namespace.h000066400000000000000000000124511331777607200157770ustar00rootroot00000000000000/* * 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-61.2/ndctl/ndctl.c000066400000000000000000000071571331777607200151510ustar00rootroot00000000000000/* * 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 ndctl_usage_string[] = "ndctl [--version] [--help] COMMAND [ARGS]"; 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, void *ctx) { printf("%s\n", VERSION); return 0; } static int cmd_help(int argc, const char **argv, void *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 }, { "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 }, { "list", cmd_list }, { "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)); ndctl_unref(ctx); fprintf(stderr, "Unknown command: '%s'\n", argv[0]); out: return 1; } ndctl-61.2/ndctl/ndctl.h000066400000000000000000000155411331777607200151520ustar00rootroot00000000000000/* * 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 ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\ struct nd_cmd_pkg) #endif /* __NDCTL_H__ */ ndctl-61.2/ndctl/region.c000066400000000000000000000100061331777607200153130ustar00rootroot00000000000000/* * 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 0; } 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, void *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, void *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-61.2/ndctl/test.c000066400000000000000000000040271331777607200150150ustar00rootroot00000000000000/* * 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, void *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); 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-61.2/ndctl/util/000077500000000000000000000000001331777607200146445ustar00rootroot00000000000000ndctl-61.2/ndctl/util/json-firmware.c000066400000000000000000000034311331777607200175740ustar00rootroot00000000000000/* 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 || 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-61.2/ndctl/util/json-smart.c000066400000000000000000000123641331777607200171130ustar00rootroot00000000000000/* * 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(cmd); if (rc || ndctl_cmd_get_firmware_status(cmd)) goto out; alarm_control = ndctl_cmd_smart_threshold_get_alarm_control(cmd); if (alarm_control & ND_SMART_TEMP_TRIP) { unsigned int temp; double t; 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); } if (alarm_control & ND_SMART_CTEMP_TRIP) { unsigned int temp; double t; 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); } if (alarm_control & ND_SMART_SPARE_TRIP) { unsigned int spares; spares = ndctl_cmd_smart_threshold_get_spares(cmd); jobj = json_object_new_int(spares); if (jobj) json_object_object_add(jhealth, "spares_threshold", 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(cmd); if (rc || ndctl_cmd_get_firmware_status(cmd)) { 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-61.2/nfit.h000066400000000000000000000031231331777607200136730ustar00rootroot00000000000000/* * 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-61.2/rpmbuild.sh000077500000000000000000000003041331777607200147350ustar00rootroot00000000000000#!/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-61.2/sles/000077500000000000000000000000001331777607200135315ustar00rootroot00000000000000ndctl-61.2/sles/header000066400000000000000000000013651331777607200147110ustar00rootroot00000000000000# # 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-61.2/test.h000066400000000000000000000046461331777607200137250ustar00rootroot00000000000000/* * 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-61.2/test/000077500000000000000000000000001331777607200135425ustar00rootroot00000000000000ndctl-61.2/test/Makefile.am000066400000000000000000000054741331777607200156100ustar00rootroot00000000000000include $(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 check_PROGRAMS =\ libndctl \ dsm-fail \ dpa-alloc \ parent-uuid \ multi-pmem \ dax-errors \ smart-notify \ smart-listen \ hugetlb \ daxdev-errors \ ack-shutdown-count-set if ENABLE_DESTRUCTIVE TESTS +=\ blk-ns \ pmem-ns \ dax-dev \ dax.sh \ device-dax \ device-dax-fio.sh \ mmap.sh 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 ndctl-61.2/test/ack-shutdown-count-set.c000066400000000000000000000056651331777607200202500ustar00rootroot00000000000000/* 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(cmd); if (rc < 0) goto out; rc = ndctl_cmd_get_firmware_status(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-61.2/test/blk-exhaust.sh000077500000000000000000000022451331777607200163330ustar00rootroot00000000000000#!/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-61.2/test/blk_namespaces.c000066400000000000000000000216141331777607200166610ustar00rootroot00000000000000/* * 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-61.2/test/btt-check.sh000077500000000000000000000101451331777607200157460ustar00rootroot00000000000000#!/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-61.2/test/btt-errors.sh000077500000000000000000000110241331777607200162020ustar00rootroot00000000000000#!/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 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 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-61.2/test/btt-pad-compat.sh000077500000000000000000000112241331777607200167150ustar00rootroot00000000000000#!/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-61.2/test/btt-pad-compat.xxd000066400000000000000000001074311331777607200171110ustar00rootroot000000000000000000000: 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-61.2/test/clear.sh000077500000000000000000000051661331777607200151770ustar00rootroot00000000000000#!/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 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-61.2/test/common000066400000000000000000000023751331777607200147640ustar00rootroot00000000000000 # 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 # NFIT_TEST_BUS[01] # NFIT_TEST_BUS0=nfit_test.0 NFIT_TEST_BUS1=nfit_test.1 # 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" } ndctl-61.2/test/core.c000066400000000000000000000130611331777607200146370ustar00rootroot00000000000000/* * 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", "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; 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-61.2/test/create.sh000077500000000000000000000040041331777607200153420ustar00rootroot00000000000000#!/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-61.2/test/dax-dev.c000066400000000000000000000055571331777607200152520ustar00rootroot00000000000000/* * 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-61.2/test/dax-errors.c000066400000000000000000000063341331777607200160020ustar00rootroot00000000000000/* * 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-61.2/test/dax-pmd.c000066400000000000000000000142531331777607200152450ustar00rootroot00000000000000/* * 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]; 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) { 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-61.2/test/dax-poison.c000066400000000000000000000057411331777607200157760ustar00rootroot00000000000000/* 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 #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-61.2/test/dax.sh000077500000000000000000000043241331777607200146600ustar00rootroot00000000000000#!/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. MNT=test_dax_mnt FILE=image NDCTL="../ndctl/ndctl" json2var="s/[{}\",]//g; s/:/=/g" blockdev="" err() { echo "test-dax: failed at line $1" if [ -n "$blockdev" ]; then umount /dev/$blockdev else rc=77 fi rmdir $MNT exit $rc } run_test() { if ! ./dax-pmd $MNT/$FILE; then rc=$? if [ $rc -ne 77 -a $rc -ne 0 ]; then err fi fi } set -e mkdir -p $MNT trap 'err $LINENO' ERR dev=$(./dax-dev) json=$($NDCTL list -N -n $dev) eval $(echo $json | sed -e "$json2var") rc=1 mkfs.ext4 /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test umount $MNT # convert pmem to put the memmap on the device json=$($NDCTL create-namespace -m fsdax -M dev -f -e $dev) eval $(echo $json | sed -e "$json2var") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 #note the blockdev returned from ndctl create-namespace lacks the /dev prefix mkfs.ext4 /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test umount $MNT json=$($NDCTL create-namespace -m raw -f -e $dev) eval $(echo $json | sed -e "$json2var") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 mkfs.xfs -f /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test umount $MNT # convert pmem to put the memmap on the device json=$($NDCTL create-namespace -m fsdax -M dev -f -e $dev) eval $(echo $json | sed -e "$json2var") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 mkfs.xfs -f /dev/$blockdev mount /dev/$blockdev $MNT -o dax fallocate -l 1GiB $MNT/$FILE run_test umount $MNT # revert namespace to raw mode json=$($NDCTL create-namespace -m raw -f -e $dev) eval $(echo $json | sed -e "$json2var") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 exit $rc ndctl-61.2/test/daxdev-errors.c000066400000000000000000000206231331777607200164760ustar00rootroot00000000000000/* * 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) { 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) { 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) { 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) { 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-61.2/test/daxdev-errors.sh000077500000000000000000000043251331777607200166720ustar00rootroot00000000000000#!/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-61.2/test/device-dax-fio.sh000077500000000000000000000034121331777607200166650ustar00rootroot00000000000000#!/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. NDCTL="../ndctl/ndctl" rc=77 set -e err() { echo "test/device-dax-fio.sh: failed at line $1" exit $rc } check_min_kver() { local ver="$1" : "${KVER:=$(uname -r)}" [ -n "$ver" ] || return 1 [[ "$ver" == "$(echo -e "$ver\n$KVER" | sort -V | head -1)" ]] } check_min_kver "4.11" || { echo "kernel $KVER may lack latest device-dax fixes"; exit $rc; } set -e trap 'err $LINENO' ERR 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) mode=$(echo $json | jq -r ".mode") [ $mode != "fsdax" ] && echo "fail: $LINENO" && exit 1 done exit 0 ndctl-61.2/test/device-dax.c000066400000000000000000000227341331777607200157270ustar00rootroot00000000000000/* * 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) * 30) 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; /* upgrade to a writable mapping */ close(fd); munmap(buf, VERIFY_SIZE(align)); 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; } /* * 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; 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; } fprintf(stderr, "%s: test dax poison\n", ndctl_namespace_get_devname(ndns)); 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; } 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; } } 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-61.2/test/dpa-alloc.c000066400000000000000000000205341331777607200155460ustar00rootroot00000000000000/* * 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-61.2/test/dsm-fail.c000066400000000000000000000235341331777607200154110ustar00rootroot00000000000000/* * 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; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = test_dimms_enable(bus, victim, true); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = test_regions_enable(bus, victim, victim_region, true, 2); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); 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); fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = dimms_disable(bus); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = set_dimm_response(DIMM_PATH, ND_CMD_GET_CONFIG_DATA, -EACCES, &log_ctx); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = test_dimms_enable(bus, victim, false); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = test_regions_enable(bus, victim, victim_region, false, 0); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = set_dimm_response(DIMM_PATH, ND_CMD_GET_CONFIG_DATA, 0, &log_ctx); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); rc = dimms_disable(bus); if (rc) goto out; fprintf(stderr, "%s:%d\n", __func__, __LINE__); 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-61.2/test/firmware-update.sh000077500000000000000000000013101331777607200171700ustar00rootroot00000000000000#!/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 _cleanup exit 0 ndctl-61.2/test/hugetlb.c000066400000000000000000000011401331777607200153340ustar00rootroot00000000000000#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-61.2/test/inject-error.sh000077500000000000000000000047201331777607200165070ustar00rootroot00000000000000#!/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 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-61.2/test/label-compat.sh000077500000000000000000000026201331777607200164410ustar00rootroot00000000000000#!/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-61.2/test/libndctl.c000066400000000000000000002130701331777607200155040ustar00rootroot00000000000000/* * 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; 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, { 0 }, 2, { 0x201, 0x301, }, }, { DIMM_HANDLE(0, 0, 0, 0, 1), 1, 0, 2016, 10, { 0 }, 2, { 0x201, 0x301, }, }, { DIMM_HANDLE(0, 0, 1, 0, 0), 2, 0, 2016, 10, { 0 }, 2, { 0x201, 0x301, }, }, { DIMM_HANDLE(0, 0, 1, 0, 1), 3, 0, 2016, 10, { 0 }, 2, { 0x201, 0x301, }, }, }; static struct dimm dimms1[] = { { DIMM_HANDLE(0, 0, 0, 0, 0), 0, 0, 2016, 10, { .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) { 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) { 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)]; size_t 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) { 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_submit(cmd_read); if (rc) { fprintf(stderr, "%s: dimm: %#x failed to submit read1: %zd\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: %zd\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) { 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_submit(cmd_read); if (rc) { fprintf(stderr, "%s: dimm: %#x failed to submit read2: %zd\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: %zd\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, 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, .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) { 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); __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); 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); \ 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) { 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) { 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) { 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; } } 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-61.2/test/mmap.c000066400000000000000000000116141331777607200146430ustar00rootroot00000000000000/* * 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-61.2/test/mmap.sh000077500000000000000000000043771331777607200150460ustar00rootroot00000000000000#!/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. MNT=test_mmap_mnt FILE=image DEV="" TEST=./mmap NDCTL="../ndctl/ndctl" json2var="s/[{}\",]//g; s/:/=/g" rc=77 err() { echo "test-mmap: failed at line $1" if [ -n "$DEV" ]; then umount $DEV else rc=77 fi rmdir $MNT exit $rc } test_mmap() { trap 'err $LINENO' ERR # 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' ERR dev=$(./dax-dev) json=$($NDCTL list -N -n $dev) eval $(echo $json | sed -e "$json2var") 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 mount $DEV $MNT -o dax fallocate -l 1GiB $MNT/$FILE test_mmap umount $MNT ndctl-61.2/test/multi-dax.sh000077500000000000000000000024761331777607200160160ustar00rootroot00000000000000#!/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-61.2/test/multi-pmem.c000066400000000000000000000173441331777607200160050ustar00rootroot00000000000000/* * 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-61.2/test/nmem1.bin000066400000000000000000004000001331777607200152430ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-61.2/test/nmem2.bin000066400000000000000000004000001331777607200152440ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-61.2/test/nmem3.bin000066400000000000000000004000001331777607200152450ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-61.2/test/nmem4.bin000066400000000000000000004000001331777607200152460ustar00rootroot00000000000000NAMESPACE_INDEXø>1بDcúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNAMESPACE_INDEXø>1Ø+Icúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh0ð9yB>ºÖ"½¨ýèÖûþ×öndctl-61.2/test/parent-uuid.c000066400000000000000000000144161331777607200161510ustar00rootroot00000000000000/* * 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-61.2/test/pmem-errors.sh000077500000000000000000000070461331777607200163600ustar00rootroot00000000000000#!/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-61.2/test/pmem_namespaces.c000066400000000000000000000152541331777607200170520ustar00rootroot00000000000000/* * 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-61.2/test/rescan-partitions.sh000077500000000000000000000032161331777607200175500ustar00rootroot00000000000000#!/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-61.2/test/sector-mode.sh000077500000000000000000000024721331777607200163270ustar00rootroot00000000000000#!/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-61.2/test/smart-listen.c000066400000000000000000000040371331777607200163340ustar00rootroot00000000000000/* 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-61.2/test/smart-notify.c000066400000000000000000000133651331777607200163520ustar00rootroot00000000000000/* 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) { 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) { 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) { 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) { 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-61.2/util/000077500000000000000000000000001331777607200135405ustar00rootroot00000000000000ndctl-61.2/util/COPYING000066400000000000000000000431331331777607200145770ustar00rootroot00000000000000 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-61.2/util/abspath.c000066400000000000000000000012011331777607200153200ustar00rootroot00000000000000/* 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) ; else if (is_absolute_path(arg)) pfx_len = 0; else 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-61.2/util/bitmap.c000066400000000000000000000067221331777607200151670ustar00rootroot00000000000000/* * 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-61.2/util/bitmap.h000066400000000000000000000033011331777607200151620ustar00rootroot00000000000000/* * 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-61.2/util/filter.c000066400000000000000000000246761331777607200152100ustar00rootroot00000000000000/* * 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-61.2/util/filter.h000066400000000000000000000057761331777607200152150ustar00rootroot00000000000000/* * 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 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 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 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-61.2/util/fletcher.h000066400000000000000000000010061331777607200155020ustar00rootroot00000000000000#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-61.2/util/help.c000066400000000000000000000075061331777607200146440ustar00rootroot00000000000000/* * 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-61.2/util/json.c000066400000000000000000000556351331777607200146730ustar00rootroot00000000000000/* * 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, int jflag) { int len = json_object_array_length(jarray); if (json_object_array_length(jarray) > 1) 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; 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); } return jdimm; err: json_object_put(jdimm); return NULL; } struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, unsigned long flags) { const char *devname = daxctl_dev_get_devname(dev); struct json_object *jdev, *jobj; 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); 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; } 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; if (!(flags & UTIL_JSON_MEDIA_ERRORS)) continue; /* get start address of region */ addr = ndctl_region_get_resource(region); if (addr == ULONG_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 *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; 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) return NULL; 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); } 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; 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); jobj = util_raw_uuid(ndns); if (jobj) json_object_object_add(jndns, "raw_uuid", jobj); bdev = ndctl_btt_get_block_device(btt); } else if (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); jobj = util_raw_uuid(ndns); if (jobj) json_object_object_add(jndns, "raw_uuid", jobj); bdev = ndctl_pfn_get_block_device(pfn); } else if (dax) { struct daxctl_region *dax_region; dax_region = ndctl_dax_get_daxctl_region(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); jobj = util_raw_uuid(ndns); if (jobj) json_object_object_add(jndns, "raw_uuid", jobj); 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 (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) { 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 (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-61.2/util/json.h000066400000000000000000000043661331777607200146730ustar00rootroot00000000000000/* * 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), }; struct json_object; void util_display_json_array(FILE *f_out, struct json_object *jarray, int jflag); 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); #endif /* __NDCTL_JSON_H__ */ ndctl-61.2/util/list.h000066400000000000000000000027631331777607200146740ustar00rootroot00000000000000/* * 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-61.2/util/log.c000066400000000000000000000035241331777607200144710ustar00rootroot00000000000000/* * 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-61.2/util/log.h000066400000000000000000000046211331777607200144750ustar00rootroot00000000000000/* * 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-61.2/util/main.c000066400000000000000000000061161331777607200146340ustar00rootroot00000000000000/* * 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) { int status; struct stat st; status = p->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) { 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)); } } ndctl-61.2/util/main.h000066400000000000000000000017741331777607200146460ustar00rootroot00000000000000/* * 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__ struct cmd_struct; 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); int help_show_man_page(const char *cmd, const char *util_name, const char *viewer); #endif /* __MAIN_H__ */ ndctl-61.2/util/parse-options.c000066400000000000000000000431761331777607200165220ustar00rootroot00000000000000/* * 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-61.2/util/parse-options.h000066400000000000000000000216021331777607200165150ustar00rootroot00000000000000/* * 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-61.2/util/size.c000066400000000000000000000025651331777607200146660ustar00rootroot00000000000000/* * 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-61.2/util/size.h000066400000000000000000000022721331777607200146660ustar00rootroot00000000000000/* * 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-61.2/util/strbuf.c000066400000000000000000000067641331777607200152260ustar00rootroot00000000000000/* * 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-61.2/util/strbuf.h000066400000000000000000000070271331777607200152240ustar00rootroot00000000000000/* * 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 /* * 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-61.2/util/sysfs.c000066400000000000000000000057371331777607200150670ustar00rootroot00000000000000/* * 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-61.2/util/sysfs.h000066400000000000000000000030171331777607200150610ustar00rootroot00000000000000/* * 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-61.2/util/usage.c000066400000000000000000000042461331777607200150160ustar00rootroot00000000000000/* * 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-61.2/util/util.h000066400000000000000000000057661331777607200147040ustar00rootroot00000000000000/* * 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)])) 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-61.2/util/wrapper.c000066400000000000000000000022401331777607200153620ustar00rootroot00000000000000/* * 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; }